From 270b0b60acba4802b8a9272d2727ee576733ad47 Mon Sep 17 00:00:00 2001 From: Jianjun Wang Date: Wed, 14 Jul 2021 15:38:19 +0800 Subject: [PATCH] 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 Change-Id: Ib9b6adaafa20aeee136372ec9564273f86776da0 Reviewed-on: https://review.coreboot.org/c/coreboot/+/56791 Tested-by: build bot (Jenkins) Reviewed-by: Yu-Ping Wu --- src/soc/mediatek/common/Kconfig | 1 + .../mediatek/common/include/soc/pcie_common.h | 28 ++ src/soc/mediatek/common/pcie.c | 249 ++++++++++++++++++ 3 files changed, 278 insertions(+) create mode 100644 src/soc/mediatek/common/include/soc/pcie_common.h create mode 100644 src/soc/mediatek/common/pcie.c diff --git a/src/soc/mediatek/common/Kconfig b/src/soc/mediatek/common/Kconfig index 27f6cec125..cd3b2a223b 100644 --- a/src/soc/mediatek/common/Kconfig +++ b/src/soc/mediatek/common/Kconfig @@ -1,5 +1,6 @@ config SOC_MEDIATEK_COMMON bool + select NO_ECAM_MMCONF_SUPPORT if PCI help common code blocks for Mediatek SOCs diff --git a/src/soc/mediatek/common/include/soc/pcie_common.h b/src/soc/mediatek/common/include/soc/pcie_common.h new file mode 100644 index 0000000000..d737c9dd9b --- /dev/null +++ b/src/soc/mediatek/common/include/soc/pcie_common.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef SOC_MEDIATEK_PCIE_COMMON_H +#define SOC_MEDIATEK_PCIE_COMMON_H + +#include +#include + +struct mtk_pcie_mmio_res { + uint32_t cpu_addr; + uint32_t pci_addr; + uint32_t size; + unsigned long type; +}; + +struct mtk_pcie_controller { + uintptr_t base; /* MAC physical address */ + int (*phy_init)(void); + void (*reset)(uintptr_t base, bool enable); + const struct mtk_pcie_mmio_res *mmio_res_io; + const struct mtk_pcie_mmio_res *mmio_res_mem; +}; + +void mtk_pcie_domain_read_resources(struct device *dev); +void mtk_pcie_domain_set_resources(struct device *dev); +void mtk_pcie_domain_enable(struct device *dev); + +#endif diff --git a/src/soc/mediatek/common/pcie.c b/src/soc/mediatek/common/pcie.c new file mode 100644 index 0000000000..c05db8cca9 --- /dev/null +++ b/src/soc/mediatek/common/pcie.c @@ -0,0 +1,249 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; +}