diff options
Diffstat (limited to 'drivers/pci')
| -rw-r--r-- | drivers/pci/Kconfig | 27 | ||||
| -rw-r--r-- | drivers/pci/Makefile | 1 | ||||
| -rw-r--r-- | drivers/pci/pci-rcar-gen2.c | 7 | ||||
| -rw-r--r-- | drivers/pci/pci-rcar-gen3.c | 5 | ||||
| -rw-r--r-- | drivers/pci/pci-uclass.c | 10 | ||||
| -rw-r--r-- | drivers/pci/pci_mpc85xx.c | 11 | ||||
| -rw-r--r-- | drivers/pci/pcie_brcmstb.c | 387 | ||||
| -rw-r--r-- | drivers/pci/pcie_dw_amd.c | 250 | ||||
| -rw-r--r-- | drivers/pci/pcie_dw_mvebu.c | 5 | ||||
| -rw-r--r-- | drivers/pci/pcie_dw_qcom.c | 2 | ||||
| -rw-r--r-- | drivers/pci/pcie_imx.c | 4 | ||||
| -rw-r--r-- | drivers/pci/pcie_layerscape.h | 3 | ||||
| -rw-r--r-- | drivers/pci/pcie_layerscape_ep.c | 24 |
13 files changed, 655 insertions, 81 deletions
diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index 8fc57895a78..9ffccc3a80b 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -101,11 +101,11 @@ config PCI_ENHANCED_ALLOCATION devices in place of traditional BARS for allocation of resources. config PCI_ARID - bool "Enable Alternate Routing-ID support for PCI" - help - Say Y here if you want to enable Alternate Routing-ID capability - support on PCI devices. This helps to skip some devices in BDF - scan that are not present. + bool "Enable Alternate Routing-ID support for PCI" + help + Say Y here if you want to enable Alternate Routing-ID capability + support on PCI devices. This helps to skip some devices in BDF + scan that are not present. config PCI_SCAN_SHOW bool "Show PCI devices during startup" @@ -287,7 +287,7 @@ config PCI_IOMMU_EXTRA_MAPPINGS the node describing the PCI controller. The intent is to cover SR-IOV scenarios which need mappings for VFs and PCI hot-plug scenarios. More documentation can be found under: - arch/arm/cpu/armv8/fsl-layerscape/doc/README.pci_iommu_extra + arch/arm/cpu/armv8/fsl-layerscape/doc/README.pci_iommu_extra config PCIE_LAYERSCAPE_EP bool "Layerscape PCIe Endpoint mode support" @@ -440,8 +440,8 @@ config PCIE_XILINX_NWL bool "Xilinx NWL PCIe controller" depends on ARCH_ZYNQMP help - Say 'Y' here if you want support for Xilinx / AMD NWL PCIe - controller as Root Port. + Say 'Y' here if you want support for Xilinx / AMD NWL PCIe + controller as Root Port. config PCIE_PLDA_COMMON bool @@ -456,6 +456,17 @@ config PCIE_STARFIVE_JH7110 Say Y here if you want to enable PLDA XpressRich PCIe controller support on StarFive JH7110 SoC. +config PCIE_DW_AMD + bool "AMD Versal2 DW PCIe host controller" + depends on ARCH_VERSAL2 + depends on DM_GPIO + select PCIE_DW_COMMON + select SYS_PCI_64BIT + help + Say Y here to enable support for the AMD Versal Gen 2 PCIe + host controller. This is a DesignWare-based PCIe controller + used in AMD Versal Gen 2 SoCs. + config PCIE_DW_IMX bool "i.MX DW PCIe controller support" depends on ARCH_IMX8M || ARCH_IMX9 diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index 98f3c226f63..e6d71fd172b 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -56,4 +56,5 @@ obj-$(CONFIG_PCIE_UNIPHIER) += pcie_uniphier.o obj-$(CONFIG_PCIE_XILINX_NWL) += pcie-xilinx-nwl.o obj-$(CONFIG_PCIE_PLDA_COMMON) += pcie_plda_common.o obj-$(CONFIG_PCIE_STARFIVE_JH7110) += pcie_starfive_jh7110.o +obj-$(CONFIG_PCIE_DW_AMD) += pcie_dw_amd.o obj-$(CONFIG_PCIE_DW_IMX) += pcie_dw_imx.o diff --git a/drivers/pci/pci-rcar-gen2.c b/drivers/pci/pci-rcar-gen2.c index 08d5c4fbb8b..53cb0916741 100644 --- a/drivers/pci/pci-rcar-gen2.c +++ b/drivers/pci/pci-rcar-gen2.c @@ -10,6 +10,7 @@ #include <clk.h> #include <dm.h> #include <errno.h> +#include <fdtdec.h> #include <pci.h> #include <linux/bitops.h> @@ -235,9 +236,9 @@ static int rcar_gen2_pci_of_to_plat(struct udevice *dev) { struct rcar_gen2_pci_priv *priv = dev_get_priv(dev); - priv->cfg_base = devfdt_get_addr_index(dev, 0); - priv->mem_base = devfdt_get_addr_index(dev, 1); - if (!priv->cfg_base || !priv->mem_base) + priv->cfg_base = dev_read_addr_index(dev, 0); + priv->mem_base = dev_read_addr_index(dev, 1); + if (priv->cfg_base == FDT_ADDR_T_NONE || priv->mem_base == FDT_ADDR_T_NONE) return -EINVAL; return 0; diff --git a/drivers/pci/pci-rcar-gen3.c b/drivers/pci/pci-rcar-gen3.c index d4b4037ce19..1925d968c16 100644 --- a/drivers/pci/pci-rcar-gen3.c +++ b/drivers/pci/pci-rcar-gen3.c @@ -19,6 +19,7 @@ #include <clk.h> #include <dm.h> #include <errno.h> +#include <fdtdec.h> #include <pci.h> #include <wait_bit.h> #include <linux/bitops.h> @@ -391,8 +392,8 @@ static int rcar_gen3_pcie_of_to_plat(struct udevice *dev) { struct rcar_gen3_pcie_priv *priv = dev_get_plat(dev); - priv->regs = devfdt_get_addr_index(dev, 0); - if (!priv->regs) + priv->regs = dev_read_addr_index(dev, 0); + if (priv->regs == FDT_ADDR_T_NONE) return -EINVAL; return 0; diff --git a/drivers/pci/pci-uclass.c b/drivers/pci/pci-uclass.c index 83b76d01f24..4bdd1f7477f 100644 --- a/drivers/pci/pci-uclass.c +++ b/drivers/pci/pci-uclass.c @@ -927,7 +927,7 @@ int pci_bind_bus_devices(struct udevice *bus) if (PCI_FUNC(bdf) && !found_multi) continue; - if (only_one_child(bus) && (PCI_MASK_BUS(bdf) > 0)) + if (only_one_child(bus) && (PCI_DEV(bdf) > 0)) continue; /* Check only the first access, we don't expect problems */ @@ -1126,14 +1126,14 @@ static int decode_regions(struct pci_controller *hose, ofnode parent_node, return 0; for (i = 0; i < CONFIG_NR_DRAM_BANKS; ++i) { - if (bd->bi_dram[i].size) { - phys_addr_t start = bd->bi_dram[i].start; + if (gd->dram[i].size) { + phys_addr_t start = gd->dram[i].start; if (IS_ENABLED(CONFIG_PCI_MAP_SYSTEM_MEMORY)) - start = virt_to_phys((void *)(uintptr_t)bd->bi_dram[i].start); + start = virt_to_phys((void *)(uintptr_t)gd->dram[i].start); pci_set_region(hose->regions + hose->region_count++, - start, start, bd->bi_dram[i].size, + start, start, gd->dram[i].size, PCI_REGION_MEM | PCI_REGION_SYS_MEMORY); } } diff --git a/drivers/pci/pci_mpc85xx.c b/drivers/pci/pci_mpc85xx.c index c07feba7976..96550a9ff8f 100644 --- a/drivers/pci/pci_mpc85xx.c +++ b/drivers/pci/pci_mpc85xx.c @@ -170,13 +170,14 @@ static int mpc85xx_pci_dm_remove(struct udevice *dev) static int mpc85xx_pci_of_to_plat(struct udevice *dev) { struct mpc85xx_pci_priv *priv = dev_get_priv(dev); - fdt_addr_t addr; + void __iomem *addr; - addr = devfdt_get_addr_index(dev, 0); - if (addr == FDT_ADDR_T_NONE) + addr = dev_remap_addr_index(dev, 0); + if (!addr) return -EINVAL; - priv->cfg_addr = (void __iomem *)map_physmem(addr, 0, MAP_NOCACHE); - priv->cfg_data = (void __iomem *)((ulong)priv->cfg_addr + 4); + + priv->cfg_addr = addr; + priv->cfg_data = priv->cfg_addr + 4; return 0; } diff --git a/drivers/pci/pcie_brcmstb.c b/drivers/pci/pcie_brcmstb.c index f089c48f028..1b03b0a7b05 100644 --- a/drivers/pci/pcie_brcmstb.c +++ b/drivers/pci/pcie_brcmstb.c @@ -21,6 +21,7 @@ #include <linux/bitfield.h> #include <linux/log2.h> #include <linux/iopoll.h> +#include <reset.h> /* PCIe parameters */ #define BRCM_NUM_PCIE_OUT_WINS 4 @@ -49,6 +50,47 @@ #define SSC_STATUS_PLL_LOCK_MASK 0x800 #define SSC_STATUS_PLL_LOCK_SHIFT 11 +#define PCIE_RC_PL_PHY_CTL_15 0x184c +#define PCIE_RC_PL_PHY_CTL_15_DIS_PLL_PD_MASK 0x400000 +#define PCIE_RC_PL_PHY_CTL_15_PM_CLK_PERIOD_MASK 0xff + +#define PCIE_MISC_UBUS_CTRL 0x40a4 +#define PCIE_MISC_UBUS_CTRL_UBUS_PCIE_REPLY_ERR_DIS_MASK BIT(13) +#define PCIE_MISC_UBUS_CTRL_UBUS_PCIE_REPLY_DECERR_DIS_MASK BIT(19) +#define PCIE_MISC_AXI_READ_ERROR_DATA 0x4170 +#define PCIE_MISC_UBUS_TIMEOUT 0x40A8 +#define PCIE_MISC_RC_CONFIG_RETRY_TIMEOUT 0x405c +#define PCIE_MISC_RC_BAR4_CONFIG_LO 0x40d4 +#define PCIE_MISC_RC_BAR4_CONFIG_HI 0x40d8 +#define PCIE_MISC_UBUS_BAR_CONFIG_REMAP_HI_MASK 0xff +#define PCIE_MISC_UBUS_BAR4_CONFIG_REMAP_HI 0x4110 +#define PCIE_MISC_UBUS_BAR_CONFIG_REMAP_ENABLE 0x1 +#define PCIE_MISC_UBUS_BAR_CONFIG_REMAP_LO_MASK 0xfffff000 +#define PCIE_MISC_UBUS_BAR4_CONFIG_REMAP_LO 0x410c + +#define PCIE_MISC_UBUS_BAR1_CONFIG_REMAP 0x40ac +#define PCIE_MISC_UBUS_BAR2_CONFIG_REMAP 0x40b4 +#define PCIE_MISC_UBUS_BAR2_CONFIG_REMAP_ACCESS_ENABLE_MASK BIT(0) +#define MISC_CTRL_PCIE_RCB_MPS_MODE_MASK 0x400 + +enum { + RGR1_SW_INIT_1, + PCIE_HARD_DEBUG, +}; + +enum brcm_pcie_type { + BCM2711, + BCM2712 +}; + +struct brcm_pcie; + +struct brcm_pcie_cfg_data { + const int *offsets; + const enum brcm_pcie_type type; + void (*perst_set)(struct brcm_pcie *pcie, u32 val); +}; + /** * struct brcm_pcie - the PCIe controller state * @base: Base address of memory mapped IO registers of the controller @@ -61,6 +103,9 @@ struct brcm_pcie { int gen; bool ssc; + struct reset_ctl rescal; + struct reset_ctl bridge_reset; + const struct brcm_pcie_cfg_data *pcie_cfg; }; /** @@ -79,8 +124,8 @@ static int brcm_pcie_encode_ibar_size(u64 size) if (log2_in >= 12 && log2_in <= 15) /* Covers 4KB to 32KB (inclusive) */ return (log2_in - 12) + 0x1c; - else if (log2_in >= 16 && log2_in <= 37) - /* Covers 64KB to 32GB, (inclusive) */ + else if (log2_in >= 16 && log2_in <= 36) + /* Covers 64KB to 64GB, (inclusive) */ return log2_in - 15; /* Something is awry so disable */ @@ -104,6 +149,80 @@ static bool brcm_pcie_rc_mode(struct brcm_pcie *pcie) return (val & STATUS_PCIE_PORT_MASK) >> STATUS_PCIE_PORT_SHIFT; } +static void brcm_pcie_perst_set_generic(struct brcm_pcie *pcie, u32 val) +{ + if (val) + setbits_le32(pcie->base + pcie->pcie_cfg->offsets[RGR1_SW_INIT_1], + RGR1_SW_INIT_1_PERST_MASK); + else + clrbits_le32(pcie->base + pcie->pcie_cfg->offsets[RGR1_SW_INIT_1], + RGR1_SW_INIT_1_PERST_MASK); +} + +static void brcm_pcie_perst_set_2712(struct brcm_pcie *pcie, u32 val) +{ + u32 tmp; + + /* Perst bit has moved and assert value is 0 */ + tmp = readl(pcie->base + PCIE_MISC_PCIE_CTRL); + u32p_replace_bits(&tmp, !val, RGR1_SW_INIT_1_PERSTB_MASK); + writel(tmp, pcie->base + PCIE_MISC_PCIE_CTRL); +} + +static int brcm_pcie_get_resets_dt(struct udevice *dev) +{ + struct brcm_pcie *pcie = dev_get_priv(dev); + int ret; + + ret = reset_get_by_name(dev, "rescal", &pcie->rescal); + if (ret) { + printf("Unable to get rescal reset\n"); + return ret; + } + + ret = reset_get_by_name(dev, "bridge", &pcie->bridge_reset); + if (ret) + printf("Unable to get bridge reset\n"); + + return ret; +} + +static int brcm_pcie_do_reset(struct udevice *dev) +{ + struct brcm_pcie *pcie = dev_get_priv(dev); + int ret; + + ret = reset_deassert(&pcie->rescal); + if (ret) + printf("failed to deassert 'rescal'\n"); + return ret; +} + +static int brcm_pcie_bridge_sw_init_set(struct brcm_pcie *pcie, u32 val) +{ + int ret = 0; + + if (reset_valid(&pcie->bridge_reset)) + { + if (val) + ret = reset_assert(&pcie->bridge_reset); + else + ret = reset_deassert(&pcie->bridge_reset); + if (ret) + log_err("failed to %sassert bridge reset, err=%d\n", + val ? "" : "de", ret); + return ret; + } + + if (val) + setbits_le32(pcie->base + pcie->pcie_cfg->offsets[RGR1_SW_INIT_1], + RGR1_SW_INIT_1_INIT_MASK); + else + clrbits_le32(pcie->base + pcie->pcie_cfg->offsets[RGR1_SW_INIT_1], + RGR1_SW_INIT_1_INIT_MASK); + return 0; +} + /** * brcm_pcie_link_up() - Check whether the PCIe link is up * @pcie: Pointer to the PCIe controller state @@ -125,7 +244,7 @@ static int brcm_pcie_config_address(const struct udevice *dev, pci_dev_t bdf, uint offset, void **paddress) { struct brcm_pcie *pcie = dev_get_priv(dev); - unsigned int pci_bus = PCI_BUS(bdf); + unsigned int pci_bus = PCI_BUS(bdf) - dev_seq(dev); unsigned int pci_dev = PCI_DEV(bdf); unsigned int pci_func = PCI_FUNC(bdf); int idx; @@ -345,28 +464,150 @@ static void brcm_pcie_set_outbound_win(struct brcm_pcie *pcie, writel(tmp, base + PCIE_MEM_WIN0_LIMIT_HI(win)); } +static u32 brcm_bar_reg_offset(int bar) +{ + if (bar <= 3) + return PCIE_MISC_RC_BAR1_CONFIG_LO + 8 * (bar - 1); + else + return PCIE_MISC_RC_BAR4_CONFIG_LO + 8 * (bar - 4); +} + +static u32 brcm_ubus_reg_offset(int bar) +{ + if (bar <= 3) + return PCIE_MISC_UBUS_BAR1_CONFIG_REMAP + 8 * (bar - 1); + else + return PCIE_MISC_UBUS_BAR4_CONFIG_REMAP_LO + 8 * (bar - 4); +} + +/* + * Round size up to the next power of two, as required by + * brcm_pcie_encode_ibar_size(). If size is already a power of two + * fls64(size - 1) still gives the correct result because the hardware + * encodes the exponent, not the raw value. + */ +static u64 brcm_ibar_round_size(u64 size) +{ + return 1ULL << fls64(size - 1); +} + +static void brcm_pcie_set_inbound_windows(struct udevice *dev) +{ + struct brcm_pcie *pcie = dev_get_priv(dev); + void __iomem *base = pcie->base; + bool is_2712 = (pcie->pcie_cfg->type == BCM2712); + int i, ibar_no, ret; + u32 tmp; + + ibar_no = 0; + /* pre-2712 chips leave the first entry empty */ + if (pcie->pcie_cfg->type != BCM2712) + ibar_no++; + + /* program inbound windows from OF property "dma-regions" */ + for (i = 0; i < 7; i++, ibar_no++) { + u64 bar_cpu, bar_size, bar_pci; + struct pci_region region; + int ubus_bar_offset, rc_bar_offset; + + ret = pci_get_dma_regions(dev, ®ion, i); + if (ret) /* no region #i? Then we're done. */ + break; + ubus_bar_offset = brcm_ubus_reg_offset(ibar_no + 1); + rc_bar_offset = brcm_bar_reg_offset(ibar_no + 1); + + bar_pci = region.bus_start; + bar_cpu = region.phys_start; + bar_size = region.size; + + if (is_2712) { + /* BCM2712: BAR holds raw PCI address; UBUS remap + * registers supply the CPU-side translation. */ + tmp = lower_32_bits(bar_pci); + u32p_replace_bits(&tmp, brcm_pcie_encode_ibar_size(bar_size), + RC_BAR2_CONFIG_LO_SIZE_MASK); + writel(tmp, base + rc_bar_offset); + writel(upper_32_bits(bar_pci), base + rc_bar_offset + 4); + + tmp = lower_32_bits(bar_cpu) & + PCIE_MISC_UBUS_BAR_CONFIG_REMAP_LO_MASK; + tmp |= PCIE_MISC_UBUS_BAR_CONFIG_REMAP_ENABLE; + writel(tmp, base + ubus_bar_offset); + + tmp = upper_32_bits(bar_cpu) & + PCIE_MISC_UBUS_BAR_CONFIG_REMAP_HI_MASK; + writel(tmp, base + ubus_bar_offset + 4); + } else { + /* Pre-BCM2712 (e.g. BCM2711 / RPi4): the BAR config + * register holds the offset (bus_start - phys_start), + * not the raw PCI address. The size must be rounded + * up to the next power of two before encoding. */ + u64 bar_offset = bar_pci - bar_cpu; + u64 bar_size_po2 = brcm_ibar_round_size(bar_size); + + tmp = lower_32_bits(bar_offset); + u32p_replace_bits(&tmp, brcm_pcie_encode_ibar_size(bar_size_po2), + RC_BAR2_CONFIG_LO_SIZE_MASK); + writel(tmp, base + rc_bar_offset); + writel(upper_32_bits(bar_offset), base + rc_bar_offset + 4); + /* UBUS remap registers are not used on pre-2712 hardware. */ + } + } +} + +static void brcm_pcie_munge_pll(struct brcm_pcie *pcie) +{ + u32 tmp; + int ret, i; + u8 regs[] = { 0x16, 0x17, 0x18, 0x19, 0x1b, 0x1c, 0x1e }; + u16 data[] = { 0x50b9, 0xbda1, 0x0094, 0x97b4, 0x5030, 0x5030, 0x0007 }; + + ret = brcm_pcie_mdio_write(pcie->base, MDIO_PORT0, SET_ADDR_OFFSET, + 0x1600); + for (i = 0; i < ARRAY_SIZE(regs); i++) { + brcm_pcie_mdio_read(pcie->base, MDIO_PORT0, regs[i], &tmp); + debug("PCIE MDIO pre_refclk 0x%02x = 0x%04x\n", + regs[i], tmp); + } + for (i = 0; i < ARRAY_SIZE(regs); i++) { + brcm_pcie_mdio_write(pcie->base, MDIO_PORT0, regs[i], data[i]); + brcm_pcie_mdio_read(pcie->base, MDIO_PORT0, regs[i], &tmp); + debug("PCIE MDIO post_refclk 0x%02x = 0x%04x\n", + regs[i], tmp); + } + + udelay(200); +} + static int brcm_pcie_probe(struct udevice *dev) { struct udevice *ctlr = pci_get_controller(dev); struct pci_controller *hose = dev_get_uclass_priv(ctlr); struct brcm_pcie *pcie = dev_get_priv(dev); void __iomem *base = pcie->base; - struct pci_region region; bool ssc_good = false; int num_out_wins = 0; - u64 rc_bar2_offset, rc_bar2_size; - unsigned int scb_size_val; - int i, ret; + int i, ret = 0; u16 nlw, cls, lnksta; u32 tmp; /* + * Ensure rescal reset for BCM2712 is really disabled. + */ + if (pcie->pcie_cfg->type == BCM2712) + ret = brcm_pcie_do_reset(dev); + if (ret) + return ret; + /* * Reset the bridge, assert the fundamental reset. Note for some SoCs, * e.g. BCM7278, the fundamental reset should not be asserted here. * This will need to be changed when support for other SoCs is added. */ - setbits_le32(base + PCIE_RGR1_SW_INIT_1, - PCIE_RGR1_SW_INIT_1_INIT_MASK | PCIE_RGR1_SW_INIT_1_PERST_MASK); + ret = brcm_pcie_bridge_sw_init_set(pcie, 1); + if (ret) + return ret; + if (pcie->pcie_cfg->type != BCM2712) + pcie->pcie_cfg->perst_set(pcie, 1); /* * The delay is a safety precaution to preclude the reset signal * from looking like a glitch. @@ -374,39 +615,77 @@ static int brcm_pcie_probe(struct udevice *dev) udelay(100); /* Take the bridge out of reset */ - clrbits_le32(base + PCIE_RGR1_SW_INIT_1, PCIE_RGR1_SW_INIT_1_INIT_MASK); - - clrbits_le32(base + PCIE_MISC_HARD_PCIE_HARD_DEBUG, + ret = brcm_pcie_bridge_sw_init_set(pcie, 0); + if (ret) + return ret; + clrbits_le32(base + pcie->pcie_cfg->offsets[PCIE_HARD_DEBUG], PCIE_HARD_DEBUG_SERDES_IDDQ_MASK); /* Wait for SerDes to be stable */ udelay(100); + if (pcie->pcie_cfg->type == BCM2712) { + /* Allow a 54MHz (xosc) refclk source */ + brcm_pcie_munge_pll(pcie); + /* Fix for L1SS errata */ + tmp = readl(base + PCIE_RC_PL_PHY_CTL_15); + tmp &= ~PCIE_RC_PL_PHY_CTL_15_PM_CLK_PERIOD_MASK; + /* PM clock period is 18.52ns (round down) */ + tmp |= 0x12; + writel(tmp, base + PCIE_RC_PL_PHY_CTL_15); + } + + tmp = (pcie->pcie_cfg->type == BCM2712) ? + MISC_CTRL_MAX_BURST_SIZE_128_2712 : + MISC_CTRL_MAX_BURST_SIZE_128; /* Set SCB_MAX_BURST_SIZE, CFG_READ_UR_MODE, SCB_ACCESS_EN */ clrsetbits_le32(base + PCIE_MISC_MISC_CTRL, MISC_CTRL_MAX_BURST_SIZE_MASK, MISC_CTRL_SCB_ACCESS_EN_MASK | MISC_CTRL_CFG_READ_UR_MODE_MASK | - MISC_CTRL_MAX_BURST_SIZE_128); + MISC_CTRL_PCIE_RCB_MPS_MODE_MASK | + tmp); - pci_get_dma_regions(dev, ®ion, 0); - rc_bar2_offset = region.bus_start - region.phys_start; - rc_bar2_size = 1ULL << fls64(region.size - 1); + tmp = readl(base + PCIE_MISC_MISC_CTRL); + if (pcie->pcie_cfg->type == BCM2712) { + /* BCM2712: fixed 32GB SCB0 window */ + u32p_replace_bits(&tmp, 20, MISC_CTRL_SCB0_SIZE_MASK); + } else { + /* Pre-BCM2712: size SCB0 to match the actual DMA region. + * rc_bar2_size must be a power of two; ilog2(size) - 15 + * gives the hardware encoding (e.g. 1GB -> 15). */ + struct pci_region region; + u64 rc_bar2_size; - tmp = lower_32_bits(rc_bar2_offset); - u32p_replace_bits(&tmp, brcm_pcie_encode_ibar_size(rc_bar2_size), - RC_BAR2_CONFIG_LO_SIZE_MASK); - writel(tmp, base + PCIE_MISC_RC_BAR2_CONFIG_LO); - writel(upper_32_bits(rc_bar2_offset), - base + PCIE_MISC_RC_BAR2_CONFIG_HI); + pci_get_dma_regions(dev, ®ion, 0); + rc_bar2_size = brcm_ibar_round_size(region.size); + u32p_replace_bits(&tmp, rc_bar2_size ? ilog2(rc_bar2_size) - 15 : 0xf, + MISC_CTRL_SCB0_SIZE_MASK); + } + writel(tmp, base + PCIE_MISC_MISC_CTRL); - scb_size_val = rc_bar2_size ? - ilog2(rc_bar2_size) - 15 : 0xf; /* 0xf is 1GB */ + if (pcie->pcie_cfg->type == BCM2712) { + /* Suppress AXI error responses and return 1s for read failures */ + tmp = readl(base + PCIE_MISC_UBUS_CTRL); + u32p_replace_bits(&tmp, 1, PCIE_MISC_UBUS_CTRL_UBUS_PCIE_REPLY_ERR_DIS_MASK); + u32p_replace_bits(&tmp, 1, PCIE_MISC_UBUS_CTRL_UBUS_PCIE_REPLY_DECERR_DIS_MASK); + writel(tmp, base + PCIE_MISC_UBUS_CTRL); + writel(0xffffffff, base + PCIE_MISC_AXI_READ_ERROR_DATA); - tmp = readl(base + PCIE_MISC_MISC_CTRL); - u32p_replace_bits(&tmp, scb_size_val, - MISC_CTRL_SCB0_SIZE_MASK); - writel(tmp, base + PCIE_MISC_MISC_CTRL); + /* + * Adjust timeouts. The UBUS timeout also affects CRS + * completion retries, as the request will get terminated if + * either timeout expires, so both have to be a large value + * (in clocks of 750MHz). + * Set UBUS timeout to 250ms, then set RC config retry timeout + * to be ~240ms. + * + * Setting CRSVis=1 will stop the core from blocking on a CRS + * response, but does require the device to be well-behaved... + */ + writel(0xB2D0000, base + PCIE_MISC_UBUS_TIMEOUT); + writel(0xABA0000, base + PCIE_MISC_RC_CONFIG_RETRY_TIMEOUT); + } /* Disable the PCIe->GISB memory window (RC_BAR1) */ clrbits_le32(base + PCIE_MISC_RC_BAR1_CONFIG_LO, @@ -422,12 +701,13 @@ static int brcm_pcie_probe(struct udevice *dev) /* Clear any interrupts we find on boot */ writel(0xffffffff, base + PCIE_MSI_INTR2_CLR); + brcm_pcie_set_inbound_windows(dev); + if (pcie->gen) brcm_pcie_set_gen(pcie, pcie->gen); /* Unassert the fundamental reset */ - clrbits_le32(pcie->base + PCIE_RGR1_SW_INIT_1, - PCIE_RGR1_SW_INIT_1_PERST_MASK); + pcie->pcie_cfg->perst_set(pcie, 0); /* * Wait for 100ms after PERST# deassertion; see PCIe CEM specification @@ -514,14 +794,25 @@ static int brcm_pcie_remove(struct udevice *dev) void __iomem *base = pcie->base; /* Assert fundamental reset */ - setbits_le32(base + PCIE_RGR1_SW_INIT_1, PCIE_RGR1_SW_INIT_1_PERST_MASK); + setbits_le32(base + pcie->pcie_cfg->offsets[RGR1_SW_INIT_1], + PCIE_RGR1_SW_INIT_1_PERST_MASK); /* Turn off SerDes */ - setbits_le32(base + PCIE_MISC_HARD_PCIE_HARD_DEBUG, + setbits_le32(base + pcie->pcie_cfg->offsets[PCIE_HARD_DEBUG], PCIE_HARD_DEBUG_SERDES_IDDQ_MASK); /* Shutdown bridge */ - setbits_le32(base + PCIE_RGR1_SW_INIT_1, PCIE_RGR1_SW_INIT_1_INIT_MASK); + brcm_pcie_bridge_sw_init_set(pcie, 1); + + /* + * For the controllers that are utilizing reset for bridge Sw init, + * such as BCM2712, reset should be deasserted after assertion. + * Leaving it in asserted state may lead to unexpected hangs in + * the Linux Kernel driver because it do not perform reset initialization + * and start accessing device memory. + */ + if (pcie->pcie_cfg->type == BCM2712) + brcm_pcie_bridge_sw_init_set(pcie, 0); return 0; } @@ -546,6 +837,11 @@ static int brcm_pcie_of_to_plat(struct udevice *dev) else pcie->gen = max_link_speed; + pcie->pcie_cfg = (const struct brcm_pcie_cfg_data *)dev_get_driver_data(dev); + + if (pcie->pcie_cfg->type == BCM2712) + return brcm_pcie_get_resets_dt(dev); + return 0; } @@ -554,8 +850,31 @@ static const struct dm_pci_ops brcm_pcie_ops = { .write_config = brcm_pcie_write_config, }; +static const int pcie_offsets[] = { + [RGR1_SW_INIT_1] = 0x9210, + [PCIE_HARD_DEBUG] = 0x4204, +}; + +static const struct brcm_pcie_cfg_data bcm2711_cfg = { + .offsets = pcie_offsets, + .type = BCM2711, + .perst_set = brcm_pcie_perst_set_generic, +}; + +static const int pcie_offsets_bcm2712[] = { + [RGR1_SW_INIT_1] = 0x0, + [PCIE_HARD_DEBUG] = 0x4304, +}; + +static const struct brcm_pcie_cfg_data bcm2712_cfg = { + .offsets = pcie_offsets_bcm2712, + .type = BCM2712, + .perst_set = brcm_pcie_perst_set_2712, +}; + static const struct udevice_id brcm_pcie_ids[] = { - { .compatible = "brcm,bcm2711-pcie" }, + { .compatible = "brcm,bcm2711-pcie", .data = (ulong)&bcm2711_cfg }, + { .compatible = "brcm,bcm2712-pcie", .data = (ulong)&bcm2712_cfg }, { } }; diff --git a/drivers/pci/pcie_dw_amd.c b/drivers/pci/pcie_dw_amd.c new file mode 100644 index 00000000000..81c6d8f2817 --- /dev/null +++ b/drivers/pci/pcie_dw_amd.c @@ -0,0 +1,250 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * AMD Versal2 DesignWare PCIe host controller driver + * + * Copyright (C) 2025 - 2026, Advanced Micro Devices, Inc. + * Author: Pranav Sanwal <[email protected]> + */ + +#include <dm.h> +#include <log.h> +#include <pci.h> +#include <wait_bit.h> + +#include <asm/gpio.h> +#include <asm/io.h> +#include <dm/device_compat.h> +#include <dm/ofnode.h> +#include <linux/delay.h> + +#include "pcie_dw_common.h" + +/* + * SLCR (System Level Control Register) Interrupt Register Offsets + * These are relative to the SLCR base address from device tree + */ +#define AMD_DW_TLP_IR_STATUS_MISC 0x4c0 +#define AMD_DW_TLP_IR_DISABLE_MISC 0x4cc + +/* Interrupt bit definitions */ +#define AMD_DW_PCIE_INTR_CMPL_TIMEOUT 15 +#define AMD_DW_PCIE_INTR_PM_PME_RCVD 24 +#define AMD_DW_PCIE_INTR_PME_TO_ACK_RCVD 25 +#define AMD_DW_PCIE_INTR_MISC_CORRECTABLE 26 +#define AMD_DW_PCIE_INTR_NONFATAL 27 +#define AMD_DW_PCIE_INTR_FATAL 28 + +#define AMD_DW_PCIE_INTR_INTX_MASK GENMASK(23, 16) + +#define AMD_DW_PCIE_IMR_ALL_MASK \ + (BIT(AMD_DW_PCIE_INTR_CMPL_TIMEOUT) | \ + BIT(AMD_DW_PCIE_INTR_PM_PME_RCVD) | \ + BIT(AMD_DW_PCIE_INTR_PME_TO_ACK_RCVD) | \ + BIT(AMD_DW_PCIE_INTR_MISC_CORRECTABLE) | \ + BIT(AMD_DW_PCIE_INTR_NONFATAL) | \ + BIT(AMD_DW_PCIE_INTR_FATAL) | \ + AMD_DW_PCIE_INTR_INTX_MASK) + +/* DW PCIe Debug Registers (in DBI space) */ +#define AMD_DW_PCIE_PORT_DEBUG1 0x72c +#define AMD_DW_PCIE_PORT_DEBUG1_LINK_UP BIT(4) +#define AMD_DW_PCIE_PORT_DEBUG1_LINK_IN_TRAINING BIT(29) +#define AMD_DW_PCIE_DBI_64BIT_MEM_DECODE BIT(0) + +/* Link training timeout */ +#define LINK_WAIT_MSLEEP_MAX 1000 + +/* PCIe spec timing requirements */ +#define PCIE_RESET_CONFIG_WAIT_MS 100 +#define PCIE_T_PERST_WAIT_MS 1 + +/** + * struct amd_dw_pcie - AMD DesignWare PCIe controller private data + * @dw: DesignWare PCIe common structure + * @slcr_base: System Level Control Register base (for interrupts) + */ +struct amd_dw_pcie { + struct pcie_dw dw; + void __iomem *slcr_base; +}; + +static void amd_dw_pcie_init_port(struct amd_dw_pcie *pcie) +{ + u32 val; + + if (!pcie->slcr_base) + return; + + /* Disable all TLP interrupts */ + writel(AMD_DW_PCIE_IMR_ALL_MASK, + pcie->slcr_base + AMD_DW_TLP_IR_DISABLE_MISC); + + /* Clear any pending TLP interrupts */ + val = readl(pcie->slcr_base + AMD_DW_TLP_IR_STATUS_MISC); + val &= AMD_DW_PCIE_IMR_ALL_MASK; + writel(val, pcie->slcr_base + AMD_DW_TLP_IR_STATUS_MISC); +} + +static void amd_dw_pcie_start_link(struct amd_dw_pcie *pcie) +{ + void __iomem *reg = pcie->dw.dbi_base + AMD_DW_PCIE_PORT_DEBUG1; + struct udevice *dev = pcie->dw.dev; + struct pcie_dw *pci = &pcie->dw; + int ret; + + ret = wait_for_bit_le32(reg, AMD_DW_PCIE_PORT_DEBUG1_LINK_UP, + true, LINK_WAIT_MSLEEP_MAX, + false); + if (!ret) + ret = wait_for_bit_le32(reg, + AMD_DW_PCIE_PORT_DEBUG1_LINK_IN_TRAINING, + false, LINK_WAIT_MSLEEP_MAX, false); + if (ret) + dev_warn(dev, "PCIE-%d: Link down\n", dev_seq(dev)); + else + dev_dbg(dev, "PCIE-%d: Link up (Gen%d-x%d, Bus%d)\n", + dev_seq(dev), pcie_dw_get_link_speed(pci), + pcie_dw_get_link_width(pci), pci->first_busno); +} + +static void amd_dw_pcie_host_init(struct amd_dw_pcie *pcie) +{ + struct pcie_dw *pci = &pcie->dw; + + /* + * Set 64-bit prefetchable memory decode capability. U-Boot's pci_auto.c + * reads this bit before assigning prefetchable BARs. If cleared, it skips + * PCI_PREF_BASE_UPPER32 programming, causing 64-bit BAR assignment to fail. + */ + dw_pcie_dbi_write_enable(pci, true); + setbits_le32(pci->dbi_base + PCI_PREF_MEMORY_BASE, + AMD_DW_PCIE_DBI_64BIT_MEM_DECODE); + dw_pcie_dbi_write_enable(pci, false); + + amd_dw_pcie_init_port(pcie); + pcie_dw_setup_host(pci); +} + +static void amd_dw_pcie_request_gpio(struct udevice *dev) +{ + struct gpio_desc perst_gpio; + ofnode child_node; + int ret; + + /* + * PERST# reset GPIO is optional. Child PCI endpoint nodes may carry a + * 'reset-gpios' property to toggle the endpoint reset signal during + * initialization. If absent, the endpoint is assumed to be already + * released from reset. + */ + ofnode_for_each_subnode(child_node, dev_ofnode(dev)) { + ret = gpio_request_by_name_nodev(child_node, "reset-gpios", 0, + &perst_gpio, GPIOD_IS_OUT); + if (!ret) { + dev_dbg(dev, "Found reset-gpios in child node %s\n", + ofnode_get_name(child_node)); + dm_gpio_set_value(&perst_gpio, 1); + mdelay(PCIE_T_PERST_WAIT_MS); + dm_gpio_set_value(&perst_gpio, 0); + mdelay(PCIE_RESET_CONFIG_WAIT_MS); + dm_gpio_free(dev, &perst_gpio); + } + } +} + +static int amd_dw_pcie_of_to_plat(struct udevice *dev) +{ + struct pci_region *io_region, *mem_region, *pref_region; + struct amd_dw_pcie *pcie = dev_get_priv(dev); + struct pcie_dw *pci = &pcie->dw; + int ret; + + pci->dev = dev; + + pci->dbi_base = dev_read_addr_name_ptr(dev, "dbi"); + if (!pci->dbi_base) { + dev_err(dev, "Missing 'dbi' register region\n"); + return -EINVAL; + } + + pci->cfg_base = dev_read_addr_size_name_ptr(dev, "config", &pci->cfg_size); + if (!pci->cfg_base) { + dev_err(dev, "Missing 'config' register region\n"); + return -EINVAL; + } + + pci->atu_base = dev_read_addr_name_ptr(dev, "atu"); + if (!pci->atu_base) { + dev_dbg(dev, "No 'atu' region, using default offset from DBI\n"); + pci->atu_base = pci->dbi_base + DEFAULT_DBI_ATU_OFFSET; + } + + pcie->slcr_base = dev_read_addr_name_ptr(dev, "slcr"); + if (!pcie->slcr_base) + dev_dbg(dev, "No 'slcr' region, interrupt features disabled\n"); + + ret = pci_get_regions(dev, &io_region, &mem_region, &pref_region); + if (ret < 0) { + dev_err(dev, "Failed to get PCI regions: %d\n", ret); + return ret; + } + + if (mem_region) + pci->mem = *mem_region; + + return 0; +} + +static int amd_dw_pcie_probe(struct udevice *dev) +{ + struct amd_dw_pcie *pcie = dev_get_priv(dev); + struct pcie_dw *pci = &pcie->dw; + + /* Set first bus number */ + pci->first_busno = dev_seq(dev); + + amd_dw_pcie_request_gpio(dev); + amd_dw_pcie_host_init(pcie); + amd_dw_pcie_start_link(pcie); + + if (pci->mem.size) { + dev_dbg(dev, "Programming ATU region 0 for MEM: phys=0x%llx bus=0x%llx size=0x%llx\n", + (unsigned long long)pci->mem.phys_start, + (unsigned long long)pci->mem.bus_start, + (unsigned long long)pci->mem.size); + pcie_dw_prog_outbound_atu_unroll(pci, + PCIE_ATU_REGION_INDEX0, + PCIE_ATU_TYPE_MEM, + pci->mem.phys_start, + pci->mem.bus_start, + pci->mem.size); + } else { + dev_warn(dev, "No MEM region configured!\n"); + } + + dev_dbg(dev, "dbi: 0x%lx | config: 0x%lx | atu: 0x%lx | slcr: 0x%lx\n", + (long)pci->dbi_base, (long)pci->cfg_base, + (long)pci->atu_base, (long)pcie->slcr_base); + + return 0; +} + +static const struct dm_pci_ops amd_dw_pcie_ops = { + .read_config = pcie_dw_read_config, + .write_config = pcie_dw_write_config, +}; + +static const struct udevice_id amd_dw_pcie_ids[] = { + { .compatible = "amd,versal2-mdb-host" }, + { } +}; + +U_BOOT_DRIVER(pcie_dw_amd) = { + .name = "pcie_dw_amd", + .id = UCLASS_PCI, + .of_match = amd_dw_pcie_ids, + .ops = &amd_dw_pcie_ops, + .of_to_plat = amd_dw_pcie_of_to_plat, + .probe = amd_dw_pcie_probe, + .priv_auto = sizeof(struct amd_dw_pcie), +}; diff --git a/drivers/pci/pcie_dw_mvebu.c b/drivers/pci/pcie_dw_mvebu.c index 43b919175c9..5a177478afc 100644 --- a/drivers/pci/pcie_dw_mvebu.c +++ b/drivers/pci/pcie_dw_mvebu.c @@ -565,13 +565,12 @@ static int pcie_dw_mvebu_of_to_plat(struct udevice *dev) struct pcie_dw_mvebu *pcie = dev_get_priv(dev); /* Get the controller base address */ - pcie->ctrl_base = devfdt_get_addr_index_ptr(dev, 0); + pcie->ctrl_base = dev_read_addr_index_ptr(dev, 0); if (!pcie->ctrl_base) return -EINVAL; /* Get the config space base address and size */ - pcie->cfg_base = devfdt_get_addr_size_index_ptr(dev, 1, - &pcie->cfg_size); + pcie->cfg_base = dev_read_addr_size_index_ptr(dev, 1, &pcie->cfg_size); if (!pcie->cfg_base) return -EINVAL; diff --git a/drivers/pci/pcie_dw_qcom.c b/drivers/pci/pcie_dw_qcom.c index 10c45aaba20..ce6b4d97d1d 100644 --- a/drivers/pci/pcie_dw_qcom.c +++ b/drivers/pci/pcie_dw_qcom.c @@ -22,7 +22,7 @@ struct qcom_pcie; -struct qcom_pcie_ops { +static const struct qcom_pcie_ops { int (*config_sid)(struct qcom_pcie *priv); }; diff --git a/drivers/pci/pcie_imx.c b/drivers/pci/pcie_imx.c index 8d853ecf2c2..c8b8e171e39 100644 --- a/drivers/pci/pcie_imx.c +++ b/drivers/pci/pcie_imx.c @@ -774,8 +774,8 @@ static int imx_pcie_of_to_plat(struct udevice *dev) { struct imx_pcie_priv *priv = dev_get_priv(dev); - priv->dbi_base = devfdt_get_addr_index_ptr(dev, 0); - priv->cfg_base = devfdt_get_addr_index_ptr(dev, 1); + priv->dbi_base = dev_read_addr_index_ptr(dev, 0); + priv->cfg_base = dev_read_addr_index_ptr(dev, 1); if (!priv->dbi_base || !priv->cfg_base) return -EINVAL; diff --git a/drivers/pci/pcie_layerscape.h b/drivers/pci/pcie_layerscape.h index d5f4930e181..e6d47241e71 100644 --- a/drivers/pci/pcie_layerscape.h +++ b/drivers/pci/pcie_layerscape.h @@ -10,6 +10,7 @@ #include <fdtdec.h> #include <pci.h> +#include <linux/ioport.h> #include <linux/sizes.h> #include <linux/types.h> #include <asm/arch-fsl-layerscape/svr.h> @@ -164,7 +165,7 @@ struct ls_pcie_rc { }; struct ls_pcie_ep { - struct fdt_resource addr_res; + struct resource addr_res; struct ls_pcie *pcie; struct udevice *bus; void __iomem *addr; diff --git a/drivers/pci/pcie_layerscape_ep.c b/drivers/pci/pcie_layerscape_ep.c index 3520488b345..b7809857565 100644 --- a/drivers/pci/pcie_layerscape_ep.c +++ b/drivers/pci/pcie_layerscape_ep.c @@ -7,7 +7,6 @@ #include <config.h> #include <asm/arch/fsl_serdes.h> #include <dm.h> -#include <asm/global_data.h> #include <dm/devres.h> #include <errno.h> #include <pci_ep.h> @@ -16,8 +15,6 @@ #include <linux/log2.h> #include "pcie_layerscape.h" -DECLARE_GLOBAL_DATA_PTR; - static void ls_pcie_ep_enable_cfg(struct ls_pcie_ep *pcie_ep) { struct ls_pcie *pcie = pcie_ep->pcie; @@ -250,17 +247,15 @@ static int ls_pcie_ep_probe(struct udevice *dev) pcie_ep->pcie = pcie; - pcie->dbi = devfdt_get_addr_index_ptr(dev, 0); + pcie->dbi = dev_read_addr_index_ptr(dev, 0); if (!pcie->dbi) return -EINVAL; - pcie->ctrl = devfdt_get_addr_index_ptr(dev, 1); + pcie->ctrl = dev_read_addr_index_ptr(dev, 1); if (!pcie->ctrl) return -EINVAL; - ret = fdt_get_named_resource(gd->fdt_blob, dev_of_offset(dev), - "reg", "reg-names", - "addr_space", &pcie_ep->addr_res); + ret = dev_read_resource_byname(dev, "addr_space", &pcie_ep->addr_res); if (ret) { printf("%s: resource \"addr_space\" not found\n", dev->name); return ret; @@ -273,8 +268,7 @@ static int ls_pcie_ep_probe(struct udevice *dev) if (!is_serdes_configured(PCIE_SRDS_PRTCL(pcie->idx))) return 0; - pcie->big_endian = fdtdec_get_bool(gd->fdt_blob, dev_of_offset(dev), - "big-endian"); + pcie->big_endian = dev_read_bool(dev, "big-endian"); svr = SVR_SOC_VER(get_svr()); @@ -294,13 +288,9 @@ static int ls_pcie_ep_probe(struct udevice *dev) if (pcie->mode != PCI_HEADER_TYPE_NORMAL) return 0; - pcie_ep->max_functions = fdtdec_get_int(gd->fdt_blob, - dev_of_offset(dev), - "max-functions", 1); - pcie_ep->num_ib_wins = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), - "num-ib-windows", 8); - pcie_ep->num_ob_wins = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), - "num-ob-windows", 8); + pcie_ep->max_functions = dev_read_s32_default(dev, "max-functions", 1); + pcie_ep->num_ib_wins = dev_read_s32_default(dev, "num-ib-windows", 8); + pcie_ep->num_ob_wins = dev_read_s32_default(dev, "num-ob-windows", 8); printf("PCIe%u: %s %s", PCIE_SRDS_PRTCL(pcie->idx), dev->name, "Endpoint"); |
