soc/mediatek: Add PCIe support

Add PCIe support for MediaTek platform.

Reference:
  - MT8195 Register Map V0.3-2, Chapter 3.18 PCIe controller (Page 1250)
  - linux/drivers/pci/controller/pcie-mediatek-gen3.c

This code is based on MT8195 platform, but it should be common in each
platform with the same PCIe IP in the future.

TEST=Build pass and boot up to kernel successfully via SSD on Dojo
board, here is the SSD information in boot log:
 == NVME IDENTIFY CONTROLLER DATA ==
    PCI VID   : 0x15b7
    PCI SSVID : 0x15b7
    SN        : 21517J440114
    MN        : WDC PC SN530 SDBPTPZ-256G-1006
    RAB       : 0x4
    AERL      : 0x7
    SQES      : 0x66
    CQES      : 0x44
    NN        : 0x1
Identified NVMe model WDC PC SN530 SDBPTPZ-256G-1006

BUG=b:178565024

Signed-off-by: Jianjun Wang <jianjun.wang@mediatek.com>
Change-Id: Ib9b6adaafa20aeee136372ec9564273f86776da0
Reviewed-on: https://review.coreboot.org/c/coreboot/+/56791
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Yu-Ping Wu <yupingso@google.com>
diff --git a/src/soc/mediatek/common/pcie.c b/src/soc/mediatek/common/pcie.c
new file mode 100644
index 0000000..c05db8c
--- /dev/null
+++ b/src/soc/mediatek/common/pcie.c
@@ -0,0 +1,249 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <commonlib/stdlib.h>
+#include <console/console.h>
+#include <device/device.h>
+#include <device/mmio.h>
+#include <device/pci.h>
+#include <device/pci_def.h>
+#include <device/pci_ids.h>
+#include <device/resource.h>
+#include <delay.h>
+#include <lib.h>
+#include <soc/addressmap.h>
+#include <soc/pcie.h>
+#include <soc/pcie_common.h>
+#include <stdlib.h>
+#include <types.h>
+
+#define PCIE_SETTING_REG		0x80
+#define PCIE_PCI_IDS_1			0x9c
+#define PCI_CLASS(class)		((class) << 8)
+#define PCIE_RC_MODE			BIT(0)
+
+#define PCIE_CFGNUM_REG			0x140
+#define PCIE_CFG_DEVFN(devfn)		((devfn) & GENMASK(7, 0))
+#define PCIE_CFG_BUS(bus)		(((bus) << 8) & GENMASK(15, 8))
+#define PCIE_CFG_OFFSET_ADDR		0x1000
+#define PCIE_CFG_HEADER(bus, devfn) \
+	(PCIE_CFG_BUS(bus) | PCIE_CFG_DEVFN(devfn))
+
+#define PCIE_RST_CTRL_REG		0x148
+#define PCIE_MAC_RSTB			BIT(0)
+#define PCIE_PHY_RSTB			BIT(1)
+#define PCIE_BRG_RSTB			BIT(2)
+#define PCIE_PE_RSTB			BIT(3)
+
+#define PCIE_LTSSM_STATUS_REG		0x150
+#define PCIE_LTSSM_STATE(val)		(((val) >> 24) & 0x1f)
+
+#define PCIE_LINK_STATUS_REG		0x154
+#define PCIE_CTRL_LINKUP		BIT(8)
+
+#define PCI_NUM_INTX			4
+#define PCIE_INT_ENABLE_REG		0x180
+#define PCIE_INTX_SHIFT			24
+#define PCIE_INTX_ENABLE \
+	GENMASK(PCIE_INTX_SHIFT + PCI_NUM_INTX - 1, PCIE_INTX_SHIFT)
+
+#define PCIE_TRANS_TABLE_BASE_REG	0x800
+#define PCIE_ATR_SRC_ADDR_MSB_OFFSET	0x4
+#define PCIE_ATR_TRSL_ADDR_LSB_OFFSET	0x8
+#define PCIE_ATR_TRSL_ADDR_MSB_OFFSET	0xc
+#define PCIE_ATR_TRSL_PARAM_OFFSET	0x10
+#define PCIE_ATR_TLB_SET_OFFSET		0x20
+
+#define PCIE_MAX_TRANS_TABLES		8
+#define PCIE_ATR_EN			BIT(0)
+#define PCIE_ATR_SIZE(size) \
+	(((((size) - 1) << 1) & GENMASK(6, 1)) | PCIE_ATR_EN)
+#define PCIE_ATR_ID(id)			((id) & GENMASK(3, 0))
+#define PCIE_ATR_TYPE_MEM		PCIE_ATR_ID(0)
+#define PCIE_ATR_TYPE_IO		PCIE_ATR_ID(1)
+#define PCIE_ATR_TLP_TYPE(type)		(((type) << 16) & GENMASK(18, 16))
+#define PCIE_ATR_TLP_TYPE_MEM		PCIE_ATR_TLP_TYPE(0)
+#define PCIE_ATR_TLP_TYPE_IO		PCIE_ATR_TLP_TYPE(2)
+
+static struct mtk_pcie_controller pcie_ctrl;
+
+/* LTSSM state in PCIE_LTSSM_STATUS_REG bit[28:24] */
+static const char *const ltssm_str[] = {
+	"detect.quiet",			/* 0x00 */
+	"detect.active",		/* 0x01 */
+	"polling.active",		/* 0x02 */
+	"polling.compliance",		/* 0x03 */
+	"polling.configuration",	/* 0x04 */
+	"config.linkwidthstart",	/* 0x05 */
+	"config.linkwidthaccept",	/* 0x06 */
+	"config.lanenumwait",		/* 0x07 */
+	"config.lanenumaccept",		/* 0x08 */
+	"config.complete",		/* 0x09 */
+	"config.idle",			/* 0x0A */
+	"recovery.receiverlock",	/* 0x0B */
+	"recovery.equalization",	/* 0x0C */
+	"recovery.speed",		/* 0x0D */
+	"recovery.receiverconfig",	/* 0x0E */
+	"recovery.idle",		/* 0x0F */
+	"L0",				/* 0x10 */
+	"L0s",				/* 0x11 */
+	"L1.entry",			/* 0x12 */
+	"L1.idle",			/* 0x13 */
+	"L2.idle",			/* 0x14 */
+	"L2.transmitwake",		/* 0x15 */
+	"disable",			/* 0x16 */
+	"loopback.entry",		/* 0x17 */
+	"loopback.active",		/* 0x18 */
+	"loopback.exit",		/* 0x19 */
+	"hotreset",			/* 0x1A */
+};
+
+volatile union pci_bank *pci_map_bus(pci_devfn_t dev)
+{
+	u32 val, devfn, bus;
+
+	devfn = PCI_DEV2DEVFN(dev);
+	bus = PCI_DEV2SEGBUS(dev);
+	val = PCIE_CFG_HEADER(bus, devfn);
+
+	write32p(pcie_ctrl.base + PCIE_CFGNUM_REG, val);
+
+	return (void *)(pcie_ctrl.base + PCIE_CFG_OFFSET_ADDR);
+}
+
+static int mtk_pcie_startup_port(struct mtk_pcie_controller *ctrl)
+{
+	uint32_t val;
+	size_t tries = 0;
+	const char *ltssm_state;
+
+	/* Set as RC mode */
+	val = read32p(ctrl->base + PCIE_SETTING_REG);
+	val |= PCIE_RC_MODE;
+	write32p(ctrl->base + PCIE_SETTING_REG, val);
+
+	/* Set class code */
+	val = read32p(ctrl->base + PCIE_PCI_IDS_1);
+	val &= ~GENMASK(31, 8);
+	val |= PCI_CLASS(PCI_CLASS_BRIDGE_PCI << 8);
+	write32p(ctrl->base + PCIE_PCI_IDS_1, val);
+
+	/* Mask all INTx interrupts */
+	val = read32p(ctrl->base + PCIE_INT_ENABLE_REG);
+	val &= ~PCIE_INTX_ENABLE;
+	write32p(ctrl->base + PCIE_INT_ENABLE_REG, val);
+
+	if (!ctrl->reset) {
+		printk(BIOS_ERR, "%s: Missing reset function\n", __func__);
+		return -1;
+	}
+
+	/* De-assert reset signals */
+	ctrl->reset(ctrl->base + PCIE_RST_CTRL_REG, false);
+
+	if (!retry(100,
+		   (tries++, read32p(ctrl->base + PCIE_LINK_STATUS_REG) &
+		    PCIE_CTRL_LINKUP), mdelay(1))) {
+		val = read32p(ctrl->base + PCIE_LTSSM_STATUS_REG);
+		ltssm_state = PCIE_LTSSM_STATE(val) >= ARRAY_SIZE(ltssm_str) ?
+			    "Unknown state" : ltssm_str[PCIE_LTSSM_STATE(val)];
+		printk(BIOS_ERR, "%s: PCIe link down, current ltssm state: %s\n",
+		       __func__, ltssm_state);
+		return -1;
+	}
+
+	printk(BIOS_INFO, "%s: PCIe link up success (%ld tries)\n", __func__,
+	       tries);
+
+	return 0;
+}
+
+static int mtk_pcie_set_trans_window(struct device *dev, uintptr_t table,
+				     const struct mtk_pcie_mmio_res *mmio_res)
+{
+	const char *range_type;
+	uint32_t table_attr;
+
+	if (!mmio_res)
+		return -1;
+
+	if (mmio_res->type == IORESOURCE_IO) {
+		range_type = "IO";
+		table_attr = PCIE_ATR_TYPE_IO | PCIE_ATR_TLP_TYPE_IO;
+	} else if (mmio_res->type == IORESOURCE_MEM) {
+		range_type = "MEM";
+		table_attr = PCIE_ATR_TYPE_MEM | PCIE_ATR_TLP_TYPE_MEM;
+	} else {
+		printk(BIOS_ERR, "%s: Unknown trans table type %#lx\n",
+		       __func__, mmio_res->type);
+		return -1;
+	}
+
+	write32p(table, mmio_res->cpu_addr |
+		 PCIE_ATR_SIZE(__fls(mmio_res->size)));
+	write32p(table + PCIE_ATR_SRC_ADDR_MSB_OFFSET, 0);
+	write32p(table + PCIE_ATR_TRSL_ADDR_LSB_OFFSET, mmio_res->pci_addr);
+	write32p(table + PCIE_ATR_TRSL_ADDR_MSB_OFFSET, 0);
+	write32p(table + PCIE_ATR_TRSL_PARAM_OFFSET, table_attr);
+
+	printk(BIOS_INFO,
+	       "%s: set %s trans window: cpu_addr = %#x, pci_addr = %#x, size = %#x\n",
+	       __func__, range_type, mmio_res->cpu_addr, mmio_res->pci_addr,
+	       mmio_res->size);
+
+	return 0;
+}
+
+static void mtk_pcie_domain_new_res(struct device *dev, unsigned int index,
+				    const struct mtk_pcie_mmio_res *mmio_res)
+{
+	struct resource *res;
+
+	if (!mmio_res)
+		return;
+
+	res = new_resource(dev, index);
+	res->base = mmio_res->cpu_addr;
+	res->limit = mmio_res->cpu_addr + mmio_res->size - 1;
+	res->flags = mmio_res->type | IORESOURCE_SUBTRACTIVE |
+		     IORESOURCE_ASSIGNED;
+}
+
+void mtk_pcie_domain_read_resources(struct device *dev)
+{
+	struct mtk_pcie_controller *ctrl = dev->chip_info;
+
+	mtk_pcie_domain_new_res(dev, IOINDEX_SUBTRACTIVE(0, 0),
+				ctrl->mmio_res_io);
+
+	mtk_pcie_domain_new_res(dev, IOINDEX_SUBTRACTIVE(1, 0),
+				ctrl->mmio_res_mem);
+}
+
+void mtk_pcie_domain_set_resources(struct device *dev)
+{
+	struct mtk_pcie_controller *ctrl = dev->chip_info;
+	uintptr_t table;
+
+	/* Initialize I/O space constraints. */
+	table = ctrl->base + PCIE_TRANS_TABLE_BASE_REG;
+	if (mtk_pcie_set_trans_window(dev, table, ctrl->mmio_res_io) < 0)
+		printk(BIOS_ERR, "%s: Failed to set IO window\n", __func__);
+
+	/* Initialize memory resources constraints. */
+	table = ctrl->base + PCIE_TRANS_TABLE_BASE_REG +
+		PCIE_ATR_TLB_SET_OFFSET;
+	if (mtk_pcie_set_trans_window(dev, table, ctrl->mmio_res_mem) < 0)
+		printk(BIOS_ERR, "%s: Failed to set MEM window\n", __func__);
+
+	pci_domain_set_resources(dev);
+}
+
+void mtk_pcie_domain_enable(struct device *dev)
+{
+	mtk_pcie_get_hw_info(&pcie_ctrl);
+
+	if (mtk_pcie_startup_port(&pcie_ctrl) < 0)
+		return;
+
+	dev->chip_info = &pcie_ctrl;
+}