[PATCH u-boot-marvell 0/9] mvebu: Move PCIe code from serdes to PCIe driver

This patch series removes gdsys's board_pex_config() function by converting it to spl_board_init(), adds a new mvebu-reset driver for enabling / disabling PCIe ports and finally moves PCIe code from serdes driver to pci_mvebu.c driver.
After all these changes, PCIe link is not initialized in serdes code anymore, but in pci_mvebu.c driver with help of mvebu-reset driver.
I'm not sure if change for gdsys board is correct, so if somebody has this board, please test it.
I tested this change on A385 board Turris Omnia and I verified that PCIe links are really enabled by pci_mvebu.c driver and not before.
This patch series is based on u-boot-marvell/next branch.
Pali Rohár (9): arm: mvebu: Convert board_pex_config() to CONFIG_SPL_BOARD_INIT board: gdsys: a38x: Enable PCIe link 2 in spl_board_init() pci: pci_mvebu: Fix PCIe MEM and IO resources assignment and mbus mapping pci: pci_mvebu: Inline mvebu_pcie_port_parse_dt() function pci: pci_mvebu: Remove dependency on SOC_REGS_PHY_BASE macro pci: pci_mvebu: Split initialization of PCIe ports into 3 phases pci: pci_mvebu: Wait 100ms for Link Up in mvebu_pcie_probe() arm: mvebu: Implement simple mvebu-reset driver for enabling/disabling PCIe ports arm: mvebu: a38x: serdes: Move non-serdes PCIe code to pci_mvebu.c
arch/arm/dts/armada-375.dtsi | 5 +- arch/arm/dts/armada-380.dtsi | 3 + arch/arm/dts/armada-385.dtsi | 4 + arch/arm/dts/armada-38x.dtsi | 1 + arch/arm/dts/armada-xp-98dx3236.dtsi | 2 + arch/arm/dts/armada-xp-mv78230.dtsi | 5 + arch/arm/dts/armada-xp-mv78260.dtsi | 9 + arch/arm/dts/armada-xp-mv78460.dtsi | 10 + arch/arm/dts/armada-xp-synology-ds414.dts | 1 + arch/arm/dts/armada-xp-theadorable.dts | 1 + arch/arm/dts/armada-xp.dtsi | 1 + arch/arm/mach-mvebu/Makefile | 1 + arch/arm/mach-mvebu/include/mach/cpu.h | 5 +- arch/arm/mach-mvebu/serdes/a38x/Makefile | 1 - arch/arm/mach-mvebu/serdes/a38x/ctrl_pex.c | 64 ---- arch/arm/mach-mvebu/serdes/a38x/ctrl_pex.h | 28 -- .../serdes/a38x/high_speed_env_spec.c | 19 -- arch/arm/mach-mvebu/system-controller.c | 105 +++++++ board/gdsys/a38x/controlcenterdc.c | 6 +- configs/controlcenterdc_defconfig | 1 + drivers/pci/Kconfig | 1 + drivers/pci/pci_mvebu.c | 275 ++++++++++++++---- 22 files changed, 371 insertions(+), 177 deletions(-) delete mode 100644 arch/arm/mach-mvebu/serdes/a38x/ctrl_pex.c delete mode 100644 arch/arm/mach-mvebu/serdes/a38x/ctrl_pex.h create mode 100644 arch/arm/mach-mvebu/system-controller.c

The only user of board_pex_config() weak function is A385 controlcenterdc board. It looks like that code in its board_pex_config() function needs to be executed after PCIe link is up. Therefore put this code into spl_board_init() function which is called after a38x serdes initialization, and therefore it is after the serdes hws_pex_config() function finishes (which is the state before this change).
With this change completely remove board_pex_config() function as it is not used anymore.
Signed-off-by: Pali Rohár pali@kernel.org --- arch/arm/mach-mvebu/serdes/a38x/ctrl_pex.c | 7 ------- arch/arm/mach-mvebu/serdes/a38x/ctrl_pex.h | 1 - board/gdsys/a38x/controlcenterdc.c | 2 +- configs/controlcenterdc_defconfig | 1 + 4 files changed, 2 insertions(+), 9 deletions(-)
diff --git a/arch/arm/mach-mvebu/serdes/a38x/ctrl_pex.c b/arch/arm/mach-mvebu/serdes/a38x/ctrl_pex.c index 55c3f9ca390b..b3cbddf6a2f0 100644 --- a/arch/arm/mach-mvebu/serdes/a38x/ctrl_pex.c +++ b/arch/arm/mach-mvebu/serdes/a38x/ctrl_pex.c @@ -14,11 +14,6 @@ #include "ctrl_pex.h" #include "sys_env_lib.h"
-__weak void board_pex_config(void) -{ - /* nothing in this weak default implementation */ -} - int hws_pex_config(const struct serdes_map *serdes_map, u8 count) { enum serdes_type serdes_type; @@ -58,7 +53,5 @@ int hws_pex_config(const struct serdes_map *serdes_map, u8 count)
reg_write(SOC_CONTROL_REG1, tmp);
- board_pex_config(); - return MV_OK; } diff --git a/arch/arm/mach-mvebu/serdes/a38x/ctrl_pex.h b/arch/arm/mach-mvebu/serdes/a38x/ctrl_pex.h index 64193d528886..abdbe3c66045 100644 --- a/arch/arm/mach-mvebu/serdes/a38x/ctrl_pex.h +++ b/arch/arm/mach-mvebu/serdes/a38x/ctrl_pex.h @@ -23,6 +23,5 @@ #define PCIE4_ENABLE_MASK (0x1 << PCIE3_ENABLE_OFFS)
int hws_pex_config(const struct serdes_map *serdes_map, u8 count); -void board_pex_config(void);
#endif diff --git a/board/gdsys/a38x/controlcenterdc.c b/board/gdsys/a38x/controlcenterdc.c index dc424f271c5e..243d02232667 100644 --- a/board/gdsys/a38x/controlcenterdc.c +++ b/board/gdsys/a38x/controlcenterdc.c @@ -94,7 +94,7 @@ int hws_board_topology_load(struct serdes_map **serdes_map_array, u8 *count) return 0; }
-void board_pex_config(void) +void spl_board_init(void) { #ifdef CONFIG_SPL_BUILD uint k; diff --git a/configs/controlcenterdc_defconfig b/configs/controlcenterdc_defconfig index 44db3110916c..87ad273a5131 100644 --- a/configs/controlcenterdc_defconfig +++ b/configs/controlcenterdc_defconfig @@ -33,6 +33,7 @@ CONFIG_SYS_CONSOLE_INFO_QUIET=y CONFIG_DISPLAY_BOARDINFO_LATE=y CONFIG_BOARD_LATE_INIT=y CONFIG_LAST_STAGE_INIT=y +CONFIG_SPL_BOARD_INIT=y CONFIG_SPL_SYS_MALLOC_SIMPLE=y CONFIG_SPL_I2C=y CONFIG_HUSH_PARSER=y

A385 controlcenterdc board does not use PCI DM properly and touches some PCIe devices directly in its board code.
This controlcenterdc spl_board_init() function expects that PCIe link is already initialized. Link itself is initialized in a38x serdes code but this will change in future and link initialization will be postponed from U-Boot SPL to proper U-Boot.
So explicitly enable PCIe link 2 in spl_board_init() function via SoC Control Register 1 to not break this code by future changes. This board has PCIe link 2 just x1, so no additional initialization (except enabling PCIe port) is needed.
Signed-off-by: Pali Rohár pali@kernel.org --- board/gdsys/a38x/controlcenterdc.c | 4 ++++ 1 file changed, 4 insertions(+)
diff --git a/board/gdsys/a38x/controlcenterdc.c b/board/gdsys/a38x/controlcenterdc.c index 243d02232667..7d65400ccb0d 100644 --- a/board/gdsys/a38x/controlcenterdc.c +++ b/board/gdsys/a38x/controlcenterdc.c @@ -100,6 +100,10 @@ void spl_board_init(void) uint k; struct gpio_desc gpio = {};
+ /* Enable PCIe link 2 */ + setbits_32(MVEBU_REGISTER(0x18204), BIT(2)); + mdelay(10); + if (!request_gpio_by_name(&gpio, "pca9698@22", 31, "fpga-program-gpio")) { /* prepare FPGA reconfiguration */ dm_gpio_set_dir_flags(&gpio, GPIOD_IS_OUT);

Do not call pci_set_region() for resources which were not properly mapped. This prevents U-Boot to access unmapped memory space.
Update MBUS_PCI_MEM_SIZE and MBUS_PCI_IO_SIZE macros to cover all PCIe MEM and IO ranges. Previously these macros covered only address ranges for the first PCIe port. Between MBUS_PCI_IO_BASE and MBUS_PCI_MEM_BASE there is space for six 128 MB long address ranges. So set MBUS_PCI_MEM_SIZE to value of 6*128 MB. Similarly set MBUS_PCI_IO_SIZE to 6*64 KB.
Function resource_size() returns zero when start address is 0 and end address is -1. So set invalid resources to these values to indicate that resource has no mapping.
Split global PCIe MEM and IO resources (defined by MBUS_PCI_*_* macros) into PCIe ports in mvebu_pcie_bind() function which allocates per-port based struct mvebu_pcie, instead of using global state variables mvebu_pcie_membase and mvebu_pcie_iobase. This makes pci_mvebu.c driver independent of global static variables (which store the state of allocation) and allows to bind and unbind the driver more times.
Signed-off-by: Pali Rohár pali@kernel.org Signed-off-by: Marek Behún marek.behun@nic.cz --- arch/arm/mach-mvebu/include/mach/cpu.h | 5 +- drivers/pci/pci_mvebu.c | 84 ++++++++++++++++++-------- 2 files changed, 62 insertions(+), 27 deletions(-)
diff --git a/arch/arm/mach-mvebu/include/mach/cpu.h b/arch/arm/mach-mvebu/include/mach/cpu.h index a7a62c7e7d53..b99d86a87a03 100644 --- a/arch/arm/mach-mvebu/include/mach/cpu.h +++ b/arch/arm/mach-mvebu/include/mach/cpu.h @@ -74,10 +74,11 @@ enum { /* * Default Device Address MAP BAR values */ +#define MBUS_PCI_MAX_PORTS 6 #define MBUS_PCI_MEM_BASE MVEBU_SDRAM_SIZE_MAX -#define MBUS_PCI_MEM_SIZE (128 << 20) +#define MBUS_PCI_MEM_SIZE ((MBUS_PCI_MAX_PORTS * 128) << 20) #define MBUS_PCI_IO_BASE 0xF1100000 -#define MBUS_PCI_IO_SIZE (64 << 10) +#define MBUS_PCI_IO_SIZE ((MBUS_PCI_MAX_PORTS * 64) << 10) #define MBUS_SPI_BASE 0xF4000000 #define MBUS_SPI_SIZE (8 << 20) #define MBUS_DFX_BASE 0xF6000000 diff --git a/drivers/pci/pci_mvebu.c b/drivers/pci/pci_mvebu.c index 9248cbc294ce..eaaa6cbe326d 100644 --- a/drivers/pci/pci_mvebu.c +++ b/drivers/pci/pci_mvebu.c @@ -25,6 +25,7 @@ #include <linux/errno.h> #include <linux/ioport.h> #include <linux/mbus.h> +#include <linux/sizes.h>
/* PCIe unit register offsets */ #define SELECT(x, n) ((x >> n) & 1UL) @@ -89,14 +90,6 @@ struct mvebu_pcie { u32 cfgcache[(0x3c - 0x10) / 4]; };
-/* - * MVEBU PCIe controller needs MEMORY and I/O BARs to be mapped - * into SoCs address space. Each controller will map 128M of MEM - * and 64K of I/O space when registered. - */ -static void __iomem *mvebu_pcie_membase = (void __iomem *)MBUS_PCI_MEM_BASE; -static void __iomem *mvebu_pcie_iobase = (void __iomem *)MBUS_PCI_IO_BASE; - static inline bool mvebu_pcie_link_up(struct mvebu_pcie *pcie) { u32 val; @@ -442,26 +435,24 @@ static int mvebu_pcie_probe(struct udevice *dev) mvebu_pcie_set_local_bus_nr(pcie, 0); mvebu_pcie_set_local_dev_nr(pcie, 1);
- pcie->mem.start = (u32)mvebu_pcie_membase; - pcie->mem.end = pcie->mem.start + MBUS_PCI_MEM_SIZE - 1; - mvebu_pcie_membase += MBUS_PCI_MEM_SIZE; - - if (mvebu_mbus_add_window_by_id(pcie->mem_target, pcie->mem_attr, + if (resource_size(&pcie->mem) && + mvebu_mbus_add_window_by_id(pcie->mem_target, pcie->mem_attr, (phys_addr_t)pcie->mem.start, resource_size(&pcie->mem))) { printf("PCIe unable to add mbus window for mem at %08x+%08x\n", (u32)pcie->mem.start, (unsigned)resource_size(&pcie->mem)); + pcie->mem.start = 0; + pcie->mem.end = -1; }
- pcie->io.start = (u32)mvebu_pcie_iobase; - pcie->io.end = pcie->io.start + MBUS_PCI_IO_SIZE - 1; - mvebu_pcie_iobase += MBUS_PCI_IO_SIZE; - - if (mvebu_mbus_add_window_by_id(pcie->io_target, pcie->io_attr, + if (resource_size(&pcie->io) && + mvebu_mbus_add_window_by_id(pcie->io_target, pcie->io_attr, (phys_addr_t)pcie->io.start, resource_size(&pcie->io))) { printf("PCIe unable to add mbus window for IO at %08x+%08x\n", (u32)pcie->io.start, (unsigned)resource_size(&pcie->io)); + pcie->io.start = 0; + pcie->io.end = -1; }
/* Setup windows and configure host bridge */ @@ -470,13 +461,23 @@ static int mvebu_pcie_probe(struct udevice *dev) /* PCI memory space */ pci_set_region(hose->regions + 0, pcie->mem.start, pcie->mem.start, resource_size(&pcie->mem), PCI_REGION_MEM); - pci_set_region(hose->regions + 1, - 0, 0, - gd->ram_size, - PCI_REGION_MEM | PCI_REGION_SYS_MEMORY); - pci_set_region(hose->regions + 2, pcie->io.start, - pcie->io.start, resource_size(&pcie->io), PCI_REGION_IO); - hose->region_count = 3; + hose->region_count = 1; + + if (resource_size(&pcie->mem)) { + pci_set_region(hose->regions + hose->region_count, + pcie->mem.start, pcie->mem.start, + resource_size(&pcie->mem), + PCI_REGION_MEM); + hose->region_count++; + } + + if (resource_size(&pcie->io)) { + pci_set_region(hose->regions + hose->region_count, + pcie->io.start, pcie->io.start, + resource_size(&pcie->io), + PCI_REGION_IO); + hose->region_count++; + }
/* PCI Bridge support 32-bit I/O and 64-bit prefetch mem addressing */ pcie->cfgcache[(PCI_IO_BASE - 0x10) / 4] = @@ -637,6 +638,8 @@ static int mvebu_pcie_bind(struct udevice *parent) struct mvebu_pcie *pcie; struct uclass_driver *drv; struct udevice *dev; + struct resource mem; + struct resource io; ofnode subnode;
/* Lookup pci driver */ @@ -646,6 +649,11 @@ static int mvebu_pcie_bind(struct udevice *parent) return -ENOENT; }
+ mem.start = MBUS_PCI_MEM_BASE; + mem.end = MBUS_PCI_MEM_BASE + MBUS_PCI_MEM_SIZE - 1; + io.start = MBUS_PCI_IO_BASE; + io.end = MBUS_PCI_IO_BASE + MBUS_PCI_IO_SIZE - 1; + ofnode_for_each_subnode(subnode, dev_ofnode(parent)) { if (!ofnode_is_available(subnode)) continue; @@ -654,6 +662,32 @@ static int mvebu_pcie_bind(struct udevice *parent) if (!pcie) return -ENOMEM;
+ /* + * MVEBU PCIe controller needs MEMORY and I/O BARs to be mapped + * into SoCs address space. Each controller will map 128M of MEM + * and 64K of I/O space when registered. + */ + + if (resource_size(&mem) >= SZ_128M) { + pcie->mem.start = mem.start; + pcie->mem.end = mem.start + SZ_128M - 1; + mem.start += SZ_128M; + } else { + printf("PCIe unable to assign mbus window for mem\n"); + pcie->mem.start = 0; + pcie->mem.end = -1; + } + + if (resource_size(&io) >= SZ_64K) { + pcie->io.start = io.start; + pcie->io.end = io.start + SZ_64K - 1; + io.start += SZ_64K; + } else { + printf("PCIe unable to assign mbus window for io\n"); + pcie->io.start = 0; + pcie->io.end = -1; + } + /* Create child device UCLASS_PCI and bind it */ device_bind(parent, &pcie_mvebu_drv, pcie->name, pcie, subnode, &dev);

Function mvebu_pcie_port_parse_dt() is called only from mvebu_pcie_of_to_plat() function. Both these function parse DT properties required to setup mvebu pcie. So inline mvebu_pcie_port_parse_dt() function into mvebu_pcie_of_to_plat() to have all code related to parsing DT properties at one place.
Signed-off-by: Pali Rohár pali@kernel.org --- drivers/pci/pci_mvebu.c | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-)
diff --git a/drivers/pci/pci_mvebu.c b/drivers/pci/pci_mvebu.c index eaaa6cbe326d..c47551807bc2 100644 --- a/drivers/pci/pci_mvebu.c +++ b/drivers/pci/pci_mvebu.c @@ -488,22 +488,6 @@ static int mvebu_pcie_probe(struct udevice *dev) return 0; }
-static int mvebu_pcie_port_parse_dt(ofnode node, struct mvebu_pcie *pcie) -{ - const u32 *addr; - int len; - - addr = ofnode_get_property(node, "assigned-addresses", &len); - if (!addr) { - pr_err("property "assigned-addresses" not found"); - return -FDT_ERR_NOTFOUND; - } - - pcie->base = (void *)(fdt32_to_cpu(addr[2]) + SOC_REGS_PHY_BASE); - - return 0; -} - #define DT_FLAGS_TO_TYPE(flags) (((flags) >> 24) & 0x03) #define DT_TYPE_IO 0x1 #define DT_TYPE_MEM32 0x2 @@ -567,7 +551,9 @@ static int mvebu_get_tgt_attr(ofnode node, int devfn, static int mvebu_pcie_of_to_plat(struct udevice *dev) { struct mvebu_pcie *pcie = dev_get_plat(dev); + const u32 *addr; int ret = 0; + int len;
/* Get port number, lane number and memory target / attr */ if (ofnode_read_u32(dev_ofnode(dev), "marvell,pcie-port", @@ -605,9 +591,14 @@ static int mvebu_pcie_of_to_plat(struct udevice *dev) }
/* Parse PCIe controller register base from DT */ - ret = mvebu_pcie_port_parse_dt(dev_ofnode(dev), pcie); - if (ret < 0) + addr = ofnode_get_property(dev_ofnode(dev), "assigned-addresses", &len); + if (!addr) { + printf("%s: property "assigned-addresses" not found\n", pcie->name); + ret = -FDT_ERR_NOTFOUND; goto err; + } + + pcie->base = (void *)(fdt32_to_cpu(addr[2]) + SOC_REGS_PHY_BASE);
return 0;

SoC specific macro SOC_REGS_PHY_BASE is used for two things:
* calculation of base PCIe port address * filling PCIe register with address of internal registers
For calculating base PCIe port address use function ofnode_translate_address() which translates DT "assigned-addresses" to final PCIe port address.
And for calculating address of internal registers use untranslated and translated DT "assigned-addresses".
Basically this change reads SOC_REGS_PHY_BASE address indirectly from DT.
Signed-off-by: Pali Rohár pali@kernel.org --- drivers/pci/pci_mvebu.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/drivers/pci/pci_mvebu.c b/drivers/pci/pci_mvebu.c index c47551807bc2..ba776217c9e6 100644 --- a/drivers/pci/pci_mvebu.c +++ b/drivers/pci/pci_mvebu.c @@ -76,6 +76,7 @@ struct mvebu_pcie { struct resource mem; void __iomem *iobase; struct resource io; + u32 intregs; u32 port; u32 lane; int devfn; @@ -359,7 +360,7 @@ static void mvebu_pcie_setup_wins(struct mvebu_pcie *pcie) pcie->base + PCIE_BAR_CTRL_OFF(1));
/* Setup BAR[0] to internal registers. */ - writel(SOC_REGS_PHY_BASE, pcie->base + PCIE_BAR_LO_OFF(0)); + writel(pcie->intregs, pcie->base + PCIE_BAR_LO_OFF(0)); writel(0, pcie->base + PCIE_BAR_HI_OFF(0)); }
@@ -598,7 +599,8 @@ static int mvebu_pcie_of_to_plat(struct udevice *dev) goto err; }
- pcie->base = (void *)(fdt32_to_cpu(addr[2]) + SOC_REGS_PHY_BASE); + pcie->base = (void *)(u32)ofnode_translate_address(dev_ofnode(dev), addr); + pcie->intregs = (u32)pcie->base - fdt32_to_cpu(addr[2]);
return 0;

In first phase just parse DT properties and fill struct mvebu_pcie. In second phase setup all PCIe links (without enabling them). And in the last third phase enable all PCIe links and create UCLASS_PCI device for each one.
Because parsing of DT is done before UCLASS_PCI is created, we cannot use DM for this action anymore. So remove .of_to_plat callback and replace it by ad-hoc function for parsing DT properties and filling struct mvebu_pcie.
Signed-off-by: Pali Rohár pali@kernel.org --- drivers/pci/pci_mvebu.c | 106 +++++++++++++++++++++++++++++++--------- 1 file changed, 84 insertions(+), 22 deletions(-)
diff --git a/drivers/pci/pci_mvebu.c b/drivers/pci/pci_mvebu.c index ba776217c9e6..504ff501aa2e 100644 --- a/drivers/pci/pci_mvebu.c +++ b/drivers/pci/pci_mvebu.c @@ -364,17 +364,30 @@ static void mvebu_pcie_setup_wins(struct mvebu_pcie *pcie) writel(0, pcie->base + PCIE_BAR_HI_OFF(0)); }
-static int mvebu_pcie_probe(struct udevice *dev) +/* Only enable PCIe link, do not setup it */ +static int mvebu_pcie_enable_link(struct mvebu_pcie *pcie, ofnode node) +{ + /* PCIe link is currently automatically enabled in SerDes code */ + return 0; +} + +/* Setup PCIe link but do not enable it */ +static void mvebu_pcie_setup_link(struct mvebu_pcie *pcie) { - struct mvebu_pcie *pcie = dev_get_plat(dev); - struct udevice *ctlr = pci_get_controller(dev); - struct pci_controller *hose = dev_get_uclass_priv(ctlr); u32 reg;
/* Setup PCIe controller to Root Complex mode */ reg = readl(pcie->base + PCIE_CTRL_OFF); reg |= PCIE_CTRL_RC_MODE; writel(reg, pcie->base + PCIE_CTRL_OFF); +} + +static int mvebu_pcie_probe(struct udevice *dev) +{ + struct mvebu_pcie *pcie = dev_get_plat(dev); + struct udevice *ctlr = pci_get_controller(dev); + struct pci_controller *hose = dev_get_uclass_priv(ctlr); + u32 reg;
/* * Change Class Code of PCI Bridge device to PCI Bridge (0x600400) @@ -440,7 +453,8 @@ static int mvebu_pcie_probe(struct udevice *dev) mvebu_mbus_add_window_by_id(pcie->mem_target, pcie->mem_attr, (phys_addr_t)pcie->mem.start, resource_size(&pcie->mem))) { - printf("PCIe unable to add mbus window for mem at %08x+%08x\n", + printf("%s: unable to add mbus window for mem at %08x+%08x\n", + pcie->name, (u32)pcie->mem.start, (unsigned)resource_size(&pcie->mem)); pcie->mem.start = 0; pcie->mem.end = -1; @@ -450,7 +464,8 @@ static int mvebu_pcie_probe(struct udevice *dev) mvebu_mbus_add_window_by_id(pcie->io_target, pcie->io_attr, (phys_addr_t)pcie->io.start, resource_size(&pcie->io))) { - printf("PCIe unable to add mbus window for IO at %08x+%08x\n", + printf("%s: unable to add mbus window for IO at %08x+%08x\n", + pcie->name, (u32)pcie->io.start, (unsigned)resource_size(&pcie->io)); pcie->io.start = 0; pcie->io.end = -1; @@ -549,33 +564,34 @@ static int mvebu_get_tgt_attr(ofnode node, int devfn, return -ENOENT; }
-static int mvebu_pcie_of_to_plat(struct udevice *dev) +static int mvebu_pcie_port_parse_dt(ofnode node, ofnode parent, struct mvebu_pcie *pcie) { - struct mvebu_pcie *pcie = dev_get_plat(dev); + struct fdt_pci_addr pci_addr; const u32 *addr; int ret = 0; int len;
/* Get port number, lane number and memory target / attr */ - if (ofnode_read_u32(dev_ofnode(dev), "marvell,pcie-port", + if (ofnode_read_u32(node, "marvell,pcie-port", &pcie->port)) { ret = -ENODEV; goto err; }
- if (ofnode_read_u32(dev_ofnode(dev), "marvell,pcie-lane", &pcie->lane)) + if (ofnode_read_u32(node, "marvell,pcie-lane", &pcie->lane)) pcie->lane = 0;
sprintf(pcie->name, "pcie%d.%d", pcie->port, pcie->lane);
- /* pci_get_devfn() returns devfn in bits 15..8, see PCI_DEV usage */ - pcie->devfn = pci_get_devfn(dev); - if (pcie->devfn < 0) { - ret = -ENODEV; + /* devfn is in bits [15:8], see PCI_DEV usage */ + ret = ofnode_read_pci_addr(node, FDT_PCI_SPACE_CONFIG, "reg", &pci_addr); + if (ret < 0) { + printf("%s: property "reg" is invalid\n", pcie->name); goto err; } + pcie->devfn = pci_addr.phys_hi & 0xff00;
- ret = mvebu_get_tgt_attr(dev_ofnode(dev->parent), pcie->devfn, + ret = mvebu_get_tgt_attr(parent, pcie->devfn, IORESOURCE_MEM, &pcie->mem_target, &pcie->mem_attr); if (ret < 0) { @@ -583,7 +599,7 @@ static int mvebu_pcie_of_to_plat(struct udevice *dev) goto err; }
- ret = mvebu_get_tgt_attr(dev_ofnode(dev->parent), pcie->devfn, + ret = mvebu_get_tgt_attr(parent, pcie->devfn, IORESOURCE_IO, &pcie->io_target, &pcie->io_attr); if (ret < 0) { @@ -592,14 +608,14 @@ static int mvebu_pcie_of_to_plat(struct udevice *dev) }
/* Parse PCIe controller register base from DT */ - addr = ofnode_get_property(dev_ofnode(dev), "assigned-addresses", &len); + addr = ofnode_get_property(node, "assigned-addresses", &len); if (!addr) { printf("%s: property "assigned-addresses" not found\n", pcie->name); ret = -FDT_ERR_NOTFOUND; goto err; }
- pcie->base = (void *)(u32)ofnode_translate_address(dev_ofnode(dev), addr); + pcie->base = (void *)(u32)ofnode_translate_address(node, addr); pcie->intregs = (u32)pcie->base - fdt32_to_cpu(addr[2]);
return 0; @@ -618,7 +634,6 @@ static struct driver pcie_mvebu_drv = { .id = UCLASS_PCI, .ops = &mvebu_pcie_ops, .probe = mvebu_pcie_probe, - .of_to_plat = mvebu_pcie_of_to_plat, .plat_auto = sizeof(struct mvebu_pcie), };
@@ -628,11 +643,14 @@ static struct driver pcie_mvebu_drv = { */ static int mvebu_pcie_bind(struct udevice *parent) { + struct mvebu_pcie **ports_pcie; struct mvebu_pcie *pcie; struct uclass_driver *drv; struct udevice *dev; struct resource mem; struct resource io; + int ports_count, i; + ofnode *ports_nodes; ofnode subnode;
/* Lookup pci driver */ @@ -642,18 +660,34 @@ static int mvebu_pcie_bind(struct udevice *parent) return -ENOENT; }
+ ports_count = ofnode_get_child_count(dev_ofnode(parent)); + ports_pcie = calloc(ports_count, sizeof(*ports_pcie)); + ports_nodes = calloc(ports_count, sizeof(*ports_nodes)); + if (!ports_pcie || !ports_nodes) { + free(ports_pcie); + free(ports_nodes); + return -ENOMEM; + } + ports_count = 0; + mem.start = MBUS_PCI_MEM_BASE; mem.end = MBUS_PCI_MEM_BASE + MBUS_PCI_MEM_SIZE - 1; io.start = MBUS_PCI_IO_BASE; io.end = MBUS_PCI_IO_BASE + MBUS_PCI_IO_SIZE - 1;
+ /* First phase: Fill mvebu_pcie struct for each port */ ofnode_for_each_subnode(subnode, dev_ofnode(parent)) { if (!ofnode_is_available(subnode)) continue;
pcie = calloc(1, sizeof(*pcie)); if (!pcie) - return -ENOMEM; + continue; + + if (mvebu_pcie_port_parse_dt(subnode, dev_ofnode(parent), pcie) < 0) { + free(pcie); + continue; + }
/* * MVEBU PCIe controller needs MEMORY and I/O BARs to be mapped @@ -666,7 +700,7 @@ static int mvebu_pcie_bind(struct udevice *parent) pcie->mem.end = mem.start + SZ_128M - 1; mem.start += SZ_128M; } else { - printf("PCIe unable to assign mbus window for mem\n"); + printf("%s: unable to assign mbus window for mem\n", pcie->name); pcie->mem.start = 0; pcie->mem.end = -1; } @@ -676,16 +710,44 @@ static int mvebu_pcie_bind(struct udevice *parent) pcie->io.end = io.start + SZ_64K - 1; io.start += SZ_64K; } else { - printf("PCIe unable to assign mbus window for io\n"); + printf("%s: unable to assign mbus window for io\n", pcie->name); pcie->io.start = 0; pcie->io.end = -1; }
+ ports_pcie[ports_count] = pcie; + ports_nodes[ports_count] = subnode; + ports_count++; + } + + /* Second phase: Setup all PCIe links (do not enable them yet) */ + for (i = 0; i < ports_count; i++) + mvebu_pcie_setup_link(ports_pcie[i]); + + /* Third phase: Enable all PCIe links and create for each UCLASS_PCI device */ + for (i = 0; i < ports_count; i++) { + pcie = ports_pcie[i]; + subnode = ports_nodes[i]; + + /* + * PCIe link can be enabled only after all PCIe links were + * properly configured. This is because more PCIe links shares + * one enable bit and some PCIe links cannot be enabled + * individually. + */ + if (mvebu_pcie_enable_link(pcie, subnode) < 0) { + free(pcie); + continue; + } + /* Create child device UCLASS_PCI and bind it */ device_bind(parent, &pcie_mvebu_drv, pcie->name, pcie, subnode, &dev); }
+ free(ports_pcie); + free(ports_nodes); + return 0; }

After function mvebu_pcie_probe() returns U-Boot DM expects that PCIe link is already up. In followup patches link initialization will be moved from SPL to proper and therefore explicitly link up delay is required.
Delay mvebu_pcie_probe() for 100ms to ensure that PCIe link is up after function finish. In the case when no card is connected to the PCIe slot, this will delay probe time by 100ms, which should not be problematic.
This change fixes detection and initialization of some QCA98xx cards on the first serdes when configured in x1 mode. Default configuration of the first serdes on A385 is x4 mode, so it looks as if some delay is required when x4 is changed to x1 and card correctly links with A385. Other PCIe serdes ports on A385 are x1-only, and so they don't have this problem.
Signed-off-by: Pali Rohár pali@kernel.org --- drivers/pci/pci_mvebu.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+)
diff --git a/drivers/pci/pci_mvebu.c b/drivers/pci/pci_mvebu.c index 504ff501aa2e..b5b0897b2124 100644 --- a/drivers/pci/pci_mvebu.c +++ b/drivers/pci/pci_mvebu.c @@ -22,6 +22,7 @@ #include <asm/arch/cpu.h> #include <asm/arch/soc.h> #include <linux/bitops.h> +#include <linux/delay.h> #include <linux/errno.h> #include <linux/ioport.h> #include <linux/mbus.h> @@ -69,6 +70,9 @@ #define PCIE_DEBUG_CTRL 0x1a60 #define PCIE_DEBUG_SOFT_RESET BIT(20)
+#define LINK_WAIT_RETRIES 100 +#define LINK_WAIT_TIMEOUT 1000 + struct mvebu_pcie { struct pci_controller hose; void __iomem *base; @@ -98,6 +102,23 @@ static inline bool mvebu_pcie_link_up(struct mvebu_pcie *pcie) return !(val & PCIE_STAT_LINK_DOWN); }
+static void mvebu_pcie_wait_for_link(struct mvebu_pcie *pcie) +{ + int retries; + + /* check if the link is up or not */ + for (retries = 0; retries < LINK_WAIT_RETRIES; retries++) { + if (mvebu_pcie_link_up(pcie)) { + printf("%s: Link up\n", pcie->name); + return; + } + + udelay(LINK_WAIT_TIMEOUT); + } + + printf("%s: Link down\n", pcie->name); +} + static void mvebu_pcie_set_local_bus_nr(struct mvebu_pcie *pcie, int busno) { u32 stat; @@ -501,6 +522,8 @@ static int mvebu_pcie_probe(struct udevice *dev) pcie->cfgcache[(PCI_PREF_MEMORY_BASE - 0x10) / 4] = PCI_PREF_RANGE_TYPE_64 | (PCI_PREF_RANGE_TYPE_64 << 16);
+ mvebu_pcie_wait_for_link(pcie); + return 0; }

Enabling and disabling PCIe ports is done via address space of system controller. All 32-bit Armada SoCs use low 4 bits in SoC Control 1 Register for enabling and disabling some or more PCIe ports. Correct mapping needs to be set in particular DTS files.
DT API for mvebu-reset is prepared for implementing resets also for other HW blocks, but currently only PCIe is implemented via index 0.
Currently this driver is not used as PCIe ports are automatically enabled by SerDes code executed by U-Boot SPL. But this will change in followup patches.
Signed-off-by: Pali Rohár pali@kernel.org --- arch/arm/dts/armada-375.dtsi | 3 +- arch/arm/dts/armada-38x.dtsi | 1 + arch/arm/dts/armada-xp-98dx3236.dtsi | 1 + arch/arm/dts/armada-xp.dtsi | 1 + arch/arm/mach-mvebu/Makefile | 1 + arch/arm/mach-mvebu/system-controller.c | 105 ++++++++++++++++++++++++ 6 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 arch/arm/mach-mvebu/system-controller.c
diff --git a/arch/arm/dts/armada-375.dtsi b/arch/arm/dts/armada-375.dtsi index 62a548a55f3f..fdf2d6dbdc84 100644 --- a/arch/arm/dts/armada-375.dtsi +++ b/arch/arm/dts/armada-375.dtsi @@ -384,9 +384,10 @@ interrupts = <GIC_SPI 62 IRQ_TYPE_LEVEL_HIGH>; };
- system-controller@18200 { + systemc: system-controller@18200 { compatible = "marvell,armada-375-system-controller"; reg = <0x18200 0x100>; + #reset-cells = <2>; };
gateclk: clock-gating-control@18220 { diff --git a/arch/arm/dts/armada-38x.dtsi b/arch/arm/dts/armada-38x.dtsi index 72c49beb71a4..061bd7852081 100644 --- a/arch/arm/dts/armada-38x.dtsi +++ b/arch/arm/dts/armada-38x.dtsi @@ -328,6 +328,7 @@ compatible = "marvell,armada-380-system-controller", "marvell,armada-370-xp-system-controller"; reg = <0x18200 0x100>; + #reset-cells = <2>; };
gateclk: clock-gating-control@18220 { diff --git a/arch/arm/dts/armada-xp-98dx3236.dtsi b/arch/arm/dts/armada-xp-98dx3236.dtsi index 5df1d1848dbc..8369de79afa2 100644 --- a/arch/arm/dts/armada-xp-98dx3236.dtsi +++ b/arch/arm/dts/armada-xp-98dx3236.dtsi @@ -136,6 +136,7 @@ systemc: system-controller@18200 { compatible = "marvell,armada-370-xp-system-controller"; reg = <0x18200 0x500>; + #reset-cells = <2>; };
gateclk: clock-gating-control@18220 { diff --git a/arch/arm/dts/armada-xp.dtsi b/arch/arm/dts/armada-xp.dtsi index d856d9602272..fb5640bbd93c 100644 --- a/arch/arm/dts/armada-xp.dtsi +++ b/arch/arm/dts/armada-xp.dtsi @@ -78,6 +78,7 @@ systemc: system-controller@18200 { compatible = "marvell,armada-370-xp-system-controller"; reg = <0x18200 0x500>; + #reset-cells = <2>; };
gateclk: clock-gating-control@18220 { diff --git a/arch/arm/mach-mvebu/Makefile b/arch/arm/mach-mvebu/Makefile index 7e9c206ed6b8..0b2c57e57360 100644 --- a/arch/arm/mach-mvebu/Makefile +++ b/arch/arm/mach-mvebu/Makefile @@ -21,6 +21,7 @@ else # CONFIG_ARCH_KIRKWOOD
obj-y = cpu.o obj-y += dram.o +obj-$(CONFIG_DM_RESET) += system-controller.o ifndef CONFIG_SPL_BUILD obj-$(CONFIG_ARMADA_375) += ../../../drivers/ddr/marvell/axp/xor.o obj-$(CONFIG_ARMADA_38X) += ../../../drivers/ddr/marvell/a38x/xor.o diff --git a/arch/arm/mach-mvebu/system-controller.c b/arch/arm/mach-mvebu/system-controller.c new file mode 100644 index 000000000000..a59fc26e38b1 --- /dev/null +++ b/arch/arm/mach-mvebu/system-controller.c @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: GPL-2.0+ +// (C) 2021 Pali Rohár pali@kernel.org + +#include <common.h> +#include <dm.h> +#include <reset-uclass.h> +#include <asm/io.h> + +#define MVEBU_SOC_CONTROL_1_REG 0x4 + +#define MVEBU_PCIE_ID 0 + +struct mvebu_reset_data { + void *base; +}; + +static int mvebu_reset_of_xlate(struct reset_ctl *rst, + struct ofnode_phandle_args *args) +{ + if (args->args_count < 2) + return -EINVAL; + + rst->id = args->args[0]; + rst->data = args->args[1]; + + /* Currently only PCIe is implemented */ + if (rst->id != MVEBU_PCIE_ID) + return -EINVAL; + + /* Four PCIe enable bits are shared across more PCIe links */ + if (!(rst->data >= 0 && rst->data <= 3)) + return -EINVAL; + + return 0; +} + +static int mvebu_reset_request(struct reset_ctl *rst) +{ + return 0; +} + +static int mvebu_reset_free(struct reset_ctl *rst) +{ + return 0; +} + +static int mvebu_reset_assert(struct reset_ctl *rst) +{ + struct mvebu_reset_data *data = dev_get_priv(rst->dev); + + clrbits_32(data->base + MVEBU_SOC_CONTROL_1_REG, BIT(rst->data)); + return 0; +} + +static int mvebu_reset_deassert(struct reset_ctl *rst) +{ + struct mvebu_reset_data *data = dev_get_priv(rst->dev); + + setbits_32(data->base + MVEBU_SOC_CONTROL_1_REG, BIT(rst->data)); + return 0; +} + +static int mvebu_reset_status(struct reset_ctl *rst) +{ + struct mvebu_reset_data *data = dev_get_priv(rst->dev); + + return !(readl(data->base + MVEBU_SOC_CONTROL_1_REG) & BIT(rst->data)); +} + +static int mvebu_reset_of_to_plat(struct udevice *dev) +{ + struct mvebu_reset_data *data = dev_get_priv(dev); + + data->base = (void *)dev_read_addr(dev); + if ((fdt_addr_t)data->base == FDT_ADDR_T_NONE) + return -EINVAL; + + return 0; +} + +static const struct udevice_id mvebu_reset_of_match[] = { + { .compatible = "marvell,armada-370-xp-system-controller" }, + { .compatible = "marvell,armada-375-system-controller" }, + { .compatible = "marvell,armada-380-system-controller" }, + { .compatible = "marvell,armada-390-system-controller" }, + { }, +}; + +static struct reset_ops mvebu_reset_ops = { + .of_xlate = mvebu_reset_of_xlate, + .request = mvebu_reset_request, + .rfree = mvebu_reset_free, + .rst_assert = mvebu_reset_assert, + .rst_deassert = mvebu_reset_deassert, + .rst_status = mvebu_reset_status, +}; + +U_BOOT_DRIVER(mvebu_reset) = { + .name = "mvebu-reset", + .id = UCLASS_RESET, + .of_match = mvebu_reset_of_match, + .of_to_plat = mvebu_reset_of_to_plat, + .priv_auto = sizeof(struct mvebu_reset_data), + .ops = &mvebu_reset_ops, +};

As explained in commit 3bedbcc3aa18 ("arm: mvebu: a38x: serdes: Don't overwrite read-only SAR PCIe registers") it is required to set Maximum Link Width bits of PCIe Root Port Link Capabilities Register depending of number of used serdes lanes. As this register is part of PCIe address space and not serdes address space, move it into pci_mvebu.c driver.
Read number of PCIe lanes from DT property "num-lanes" which is used also by other PCIe controller drivers in Linux kernel. If this property is absent then it defaults to 1. This property needs to be set to 4 for every mvebu board which use PEX_ROOT_COMPLEX_X4 or PEX_BUS_MODE_X4.
Enabling of PCIe port needs to be done afer all registers in PCIe address space are properly configure. For this purpose use new mvebu-reset driver (part of system-controller) and remove this code from serdes code.
Because some PCIe ports cannot be enabled individually, it is required to first setup all PCIe ports and then enable them.
This change contains also all required "num-lanes" and "resets" DTS properties, to make pci_mvebu.c driver work correctly.
Signed-off-by: Pali Rohár pali@kernel.org --- arch/arm/dts/armada-375.dtsi | 2 + arch/arm/dts/armada-380.dtsi | 3 + arch/arm/dts/armada-385.dtsi | 4 ++ arch/arm/dts/armada-xp-98dx3236.dtsi | 1 + arch/arm/dts/armada-xp-mv78230.dtsi | 5 ++ arch/arm/dts/armada-xp-mv78260.dtsi | 9 +++ arch/arm/dts/armada-xp-mv78460.dtsi | 10 ++++ arch/arm/dts/armada-xp-synology-ds414.dts | 1 + arch/arm/dts/armada-xp-theadorable.dts | 1 + arch/arm/mach-mvebu/serdes/a38x/Makefile | 1 - arch/arm/mach-mvebu/serdes/a38x/ctrl_pex.c | 57 ------------------- arch/arm/mach-mvebu/serdes/a38x/ctrl_pex.h | 27 --------- .../serdes/a38x/high_speed_env_spec.c | 19 ------- drivers/pci/Kconfig | 1 + drivers/pci/pci_mvebu.c | 43 +++++++++++++- 15 files changed, 79 insertions(+), 105 deletions(-) delete mode 100644 arch/arm/mach-mvebu/serdes/a38x/ctrl_pex.c delete mode 100644 arch/arm/mach-mvebu/serdes/a38x/ctrl_pex.h
diff --git a/arch/arm/dts/armada-375.dtsi b/arch/arm/dts/armada-375.dtsi index fdf2d6dbdc84..ff0ad7a9c7fd 100644 --- a/arch/arm/dts/armada-375.dtsi +++ b/arch/arm/dts/armada-375.dtsi @@ -617,6 +617,7 @@ marvell,pcie-port = <0>; marvell,pcie-lane = <0>; clocks = <&gateclk 5>; + resets = <&systemc 0 0>; status = "disabled"; };
@@ -635,6 +636,7 @@ marvell,pcie-port = <0>; marvell,pcie-lane = <1>; clocks = <&gateclk 6>; + resets = <&systemc 0 1>; status = "disabled"; };
diff --git a/arch/arm/dts/armada-380.dtsi b/arch/arm/dts/armada-380.dtsi index cff1269f3fbf..f3d7f4b27ddb 100644 --- a/arch/arm/dts/armada-380.dtsi +++ b/arch/arm/dts/armada-380.dtsi @@ -73,6 +73,7 @@ marvell,pcie-port = <0>; marvell,pcie-lane = <0>; clocks = <&gateclk 8>; + resets = <&systemc 0 0>; status = "disabled"; };
@@ -92,6 +93,7 @@ marvell,pcie-port = <1>; marvell,pcie-lane = <0>; clocks = <&gateclk 5>; + resets = <&systemc 0 1>; status = "disabled"; };
@@ -111,6 +113,7 @@ marvell,pcie-port = <2>; marvell,pcie-lane = <0>; clocks = <&gateclk 6>; + resets = <&systemc 0 2>; status = "disabled"; }; }; diff --git a/arch/arm/dts/armada-385.dtsi b/arch/arm/dts/armada-385.dtsi index f0022d10c715..581a7d9beac3 100644 --- a/arch/arm/dts/armada-385.dtsi +++ b/arch/arm/dts/armada-385.dtsi @@ -78,6 +78,7 @@ marvell,pcie-port = <0>; marvell,pcie-lane = <0>; clocks = <&gateclk 8>; + resets = <&systemc 0 0>; status = "disabled"; };
@@ -97,6 +98,7 @@ marvell,pcie-port = <1>; marvell,pcie-lane = <0>; clocks = <&gateclk 5>; + resets = <&systemc 0 1>; status = "disabled"; };
@@ -116,6 +118,7 @@ marvell,pcie-port = <2>; marvell,pcie-lane = <0>; clocks = <&gateclk 6>; + resets = <&systemc 0 2>; status = "disabled"; };
@@ -138,6 +141,7 @@ marvell,pcie-port = <3>; marvell,pcie-lane = <0>; clocks = <&gateclk 7>; + resets = <&systemc 0 3>; status = "disabled"; }; }; diff --git a/arch/arm/dts/armada-xp-98dx3236.dtsi b/arch/arm/dts/armada-xp-98dx3236.dtsi index 8369de79afa2..1a48ff3c6162 100644 --- a/arch/arm/dts/armada-xp-98dx3236.dtsi +++ b/arch/arm/dts/armada-xp-98dx3236.dtsi @@ -85,6 +85,7 @@ marvell,pcie-port = <0>; marvell,pcie-lane = <0>; clocks = <&gateclk 5>; + resets = <&systemc 0 0>; status = "disabled"; }; }; diff --git a/arch/arm/dts/armada-xp-mv78230.dtsi b/arch/arm/dts/armada-xp-mv78230.dtsi index 8558bf6bb54c..63d7f48cf456 100644 --- a/arch/arm/dts/armada-xp-mv78230.dtsi +++ b/arch/arm/dts/armada-xp-mv78230.dtsi @@ -92,6 +92,7 @@ marvell,pcie-port = <0>; marvell,pcie-lane = <0>; clocks = <&gateclk 5>; + resets = <&systemc 0 0>; status = "disabled"; };
@@ -110,6 +111,7 @@ marvell,pcie-port = <0>; marvell,pcie-lane = <1>; clocks = <&gateclk 6>; + resets = <&systemc 0 0>; status = "disabled"; };
@@ -128,6 +130,7 @@ marvell,pcie-port = <0>; marvell,pcie-lane = <2>; clocks = <&gateclk 7>; + resets = <&systemc 0 0>; status = "disabled"; };
@@ -146,6 +149,7 @@ marvell,pcie-port = <0>; marvell,pcie-lane = <3>; clocks = <&gateclk 8>; + resets = <&systemc 0 0>; status = "disabled"; };
@@ -164,6 +168,7 @@ marvell,pcie-port = <1>; marvell,pcie-lane = <0>; clocks = <&gateclk 9>; + resets = <&systemc 0 1>; status = "disabled"; }; }; diff --git a/arch/arm/dts/armada-xp-mv78260.dtsi b/arch/arm/dts/armada-xp-mv78260.dtsi index 2d85fe8ac327..5dc413dd14be 100644 --- a/arch/arm/dts/armada-xp-mv78260.dtsi +++ b/arch/arm/dts/armada-xp-mv78260.dtsi @@ -107,6 +107,7 @@ marvell,pcie-port = <0>; marvell,pcie-lane = <0>; clocks = <&gateclk 5>; + resets = <&systemc 0 0>; status = "disabled"; };
@@ -125,6 +126,7 @@ marvell,pcie-port = <0>; marvell,pcie-lane = <1>; clocks = <&gateclk 6>; + resets = <&systemc 0 0>; status = "disabled"; };
@@ -143,6 +145,7 @@ marvell,pcie-port = <0>; marvell,pcie-lane = <2>; clocks = <&gateclk 7>; + resets = <&systemc 0 0>; status = "disabled"; };
@@ -161,6 +164,7 @@ marvell,pcie-port = <0>; marvell,pcie-lane = <3>; clocks = <&gateclk 8>; + resets = <&systemc 0 0>; status = "disabled"; };
@@ -179,6 +183,7 @@ marvell,pcie-port = <1>; marvell,pcie-lane = <0>; clocks = <&gateclk 9>; + resets = <&systemc 0 1>; status = "disabled"; };
@@ -197,6 +202,7 @@ marvell,pcie-port = <1>; marvell,pcie-lane = <1>; clocks = <&gateclk 10>; + resets = <&systemc 0 1>; status = "disabled"; };
@@ -215,6 +221,7 @@ marvell,pcie-port = <1>; marvell,pcie-lane = <2>; clocks = <&gateclk 11>; + resets = <&systemc 0 1>; status = "disabled"; };
@@ -233,6 +240,7 @@ marvell,pcie-port = <1>; marvell,pcie-lane = <3>; clocks = <&gateclk 12>; + resets = <&systemc 0 1>; status = "disabled"; };
@@ -251,6 +259,7 @@ marvell,pcie-port = <2>; marvell,pcie-lane = <0>; clocks = <&gateclk 26>; + resets = <&systemc 0 2>; status = "disabled"; }; }; diff --git a/arch/arm/dts/armada-xp-mv78460.dtsi b/arch/arm/dts/armada-xp-mv78460.dtsi index 230a3fd36b30..6fbd0ce215ff 100644 --- a/arch/arm/dts/armada-xp-mv78460.dtsi +++ b/arch/arm/dts/armada-xp-mv78460.dtsi @@ -128,6 +128,7 @@ marvell,pcie-port = <0>; marvell,pcie-lane = <0>; clocks = <&gateclk 5>; + resets = <&systemc 0 0>; status = "disabled"; };
@@ -146,6 +147,7 @@ marvell,pcie-port = <0>; marvell,pcie-lane = <1>; clocks = <&gateclk 6>; + resets = <&systemc 0 0>; status = "disabled"; };
@@ -164,6 +166,7 @@ marvell,pcie-port = <0>; marvell,pcie-lane = <2>; clocks = <&gateclk 7>; + resets = <&systemc 0 0>; status = "disabled"; };
@@ -182,6 +185,7 @@ marvell,pcie-port = <0>; marvell,pcie-lane = <3>; clocks = <&gateclk 8>; + resets = <&systemc 0 0>; status = "disabled"; };
@@ -200,6 +204,7 @@ marvell,pcie-port = <1>; marvell,pcie-lane = <0>; clocks = <&gateclk 9>; + resets = <&systemc 0 1>; status = "disabled"; };
@@ -218,6 +223,7 @@ marvell,pcie-port = <1>; marvell,pcie-lane = <1>; clocks = <&gateclk 10>; + resets = <&systemc 0 1>; status = "disabled"; };
@@ -236,6 +242,7 @@ marvell,pcie-port = <1>; marvell,pcie-lane = <2>; clocks = <&gateclk 11>; + resets = <&systemc 0 1>; status = "disabled"; };
@@ -254,6 +261,7 @@ marvell,pcie-port = <1>; marvell,pcie-lane = <3>; clocks = <&gateclk 12>; + resets = <&systemc 0 1>; status = "disabled"; };
@@ -272,6 +280,7 @@ marvell,pcie-port = <2>; marvell,pcie-lane = <0>; clocks = <&gateclk 26>; + resets = <&systemc 0 2>; status = "disabled"; };
@@ -290,6 +299,7 @@ marvell,pcie-port = <3>; marvell,pcie-lane = <0>; clocks = <&gateclk 27>; + resets = <&systemc 0 3>; status = "disabled"; }; }; diff --git a/arch/arm/dts/armada-xp-synology-ds414.dts b/arch/arm/dts/armada-xp-synology-ds414.dts index 861967cd7e87..35909e3c69c6 100644 --- a/arch/arm/dts/armada-xp-synology-ds414.dts +++ b/arch/arm/dts/armada-xp-synology-ds414.dts @@ -187,6 +187,7 @@ pcie@1,0 { /* Port 0, Lane 0 */ status = "okay"; + num-lanes = <4>; };
/* diff --git a/arch/arm/dts/armada-xp-theadorable.dts b/arch/arm/dts/armada-xp-theadorable.dts index 24cc1cc5278e..a06a65af1587 100644 --- a/arch/arm/dts/armada-xp-theadorable.dts +++ b/arch/arm/dts/armada-xp-theadorable.dts @@ -214,5 +214,6 @@ pcie@9,0 { /* Port 2, Lane 0 */ status = "okay"; + num-lanes = <4>; }; }; diff --git a/arch/arm/mach-mvebu/serdes/a38x/Makefile b/arch/arm/mach-mvebu/serdes/a38x/Makefile index 917fc1350ce5..5a70b3759667 100644 --- a/arch/arm/mach-mvebu/serdes/a38x/Makefile +++ b/arch/arm/mach-mvebu/serdes/a38x/Makefile @@ -1,6 +1,5 @@ # SPDX-License-Identifier: GPL-2.0+
-obj-$(CONFIG_SPL_BUILD) = ctrl_pex.o obj-$(CONFIG_SPL_BUILD) += high_speed_env_spec.o obj-$(CONFIG_SPL_BUILD) += high_speed_env_spec-38x.o obj-$(CONFIG_SPL_BUILD) += seq_exec.o diff --git a/arch/arm/mach-mvebu/serdes/a38x/ctrl_pex.c b/arch/arm/mach-mvebu/serdes/a38x/ctrl_pex.c deleted file mode 100644 index b3cbddf6a2f0..000000000000 --- a/arch/arm/mach-mvebu/serdes/a38x/ctrl_pex.c +++ /dev/null @@ -1,57 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (C) Marvell International Ltd. and its affiliates - */ - -#include <common.h> -#include <spl.h> -#include <asm/io.h> -#include <asm/arch/cpu.h> -#include <asm/arch/soc.h> -#include <linux/bitops.h> -#include <linux/delay.h> - -#include "ctrl_pex.h" -#include "sys_env_lib.h" - -int hws_pex_config(const struct serdes_map *serdes_map, u8 count) -{ - enum serdes_type serdes_type; - u32 idx, tmp; - - DEBUG_INIT_FULL_S("\n### hws_pex_config ###\n"); - - tmp = reg_read(SOC_CONTROL_REG1); - tmp &= ~0x03; - - for (idx = 0; idx < count; idx++) { - serdes_type = serdes_map[idx].serdes_type; - if ((serdes_type != PEX0) && - ((serdes_map[idx].serdes_mode == PEX_ROOT_COMPLEX_X4) || - (serdes_map[idx].serdes_mode == PEX_END_POINT_X4))) { - /* for PEX by4 - relevant for the first port only */ - continue; - } - - switch (serdes_type) { - case PEX0: - tmp |= 0x1 << PCIE0_ENABLE_OFFS; - break; - case PEX1: - tmp |= 0x1 << PCIE1_ENABLE_OFFS; - break; - case PEX2: - tmp |= 0x1 << PCIE2_ENABLE_OFFS; - break; - case PEX3: - tmp |= 0x1 << PCIE3_ENABLE_OFFS; - break; - default: - break; - } - } - - reg_write(SOC_CONTROL_REG1, tmp); - - return MV_OK; -} diff --git a/arch/arm/mach-mvebu/serdes/a38x/ctrl_pex.h b/arch/arm/mach-mvebu/serdes/a38x/ctrl_pex.h deleted file mode 100644 index abdbe3c66045..000000000000 --- a/arch/arm/mach-mvebu/serdes/a38x/ctrl_pex.h +++ /dev/null @@ -1,27 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Copyright (C) Marvell International Ltd. and its affiliates - */ - -#ifndef _CTRL_PEX_H -#define _CTRL_PEX_H - -#include <pci.h> -#include "high_speed_env_spec.h" - -/* Direct access to PEX0 Root Port's PCIe Capability structure */ -#define PEX0_RP_PCIE_CFG_OFFSET (0x00080000 + 0x60) - -/* SOC_CONTROL_REG1 fields */ -#define PCIE0_ENABLE_OFFS 0 -#define PCIE0_ENABLE_MASK (0x1 << PCIE0_ENABLE_OFFS) -#define PCIE1_ENABLE_OFFS 1 -#define PCIE1_ENABLE_MASK (0x1 << PCIE1_ENABLE_OFFS) -#define PCIE2_ENABLE_OFFS 2 -#define PCIE2_ENABLE_MASK (0x1 << PCIE2_ENABLE_OFFS) -#define PCIE3_ENABLE_OFFS 3 -#define PCIE4_ENABLE_MASK (0x1 << PCIE3_ENABLE_OFFS) - -int hws_pex_config(const struct serdes_map *serdes_map, u8 count); - -#endif diff --git a/arch/arm/mach-mvebu/serdes/a38x/high_speed_env_spec.c b/arch/arm/mach-mvebu/serdes/a38x/high_speed_env_spec.c index 9ba60b57aac8..2e467b546d5f 100644 --- a/arch/arm/mach-mvebu/serdes/a38x/high_speed_env_spec.c +++ b/arch/arm/mach-mvebu/serdes/a38x/high_speed_env_spec.c @@ -12,7 +12,6 @@
#include "high_speed_env_spec.h" #include "sys_env_lib.h" -#include "ctrl_pex.h"
/* * serdes_seq_db - holds all serdes sequences, their size and the @@ -1555,9 +1554,6 @@ int hws_power_up_serdes_lanes(struct serdes_map *serdes_map, u8 count) After finish the Power_up sequence for all lanes, the lanes should be released from reset state. */ CHECK_STATUS(hws_pex_tx_config_seq(serdes_map, count)); - - /* PEX configuration */ - CHECK_STATUS(hws_pex_config(serdes_map, count)); }
/* USB2 configuration */ @@ -1743,21 +1739,6 @@ int serdes_power_up_ctrl(u32 serdes_num, int serdes_power_up, else reg_data &= ~0x4000; reg_write(SOC_CONTROL_REG1, reg_data); - - /* - * Set Maximum Link Width to X1 or X4 in Root - * Port's PCIe Link Capability register. - * This register is read-only but if is not set - * correctly then access to PCI config space of - * endpoint card behind this Root Port does not - * work. - */ - reg_data = reg_read(PEX0_RP_PCIE_CFG_OFFSET + - PCI_EXP_LNKCAP); - reg_data &= ~PCI_EXP_LNKCAP_MLW; - reg_data |= (is_pex_by1 ? 1 : 4) << 4; - reg_write(PEX0_RP_PCIE_CFG_OFFSET + - PCI_EXP_LNKCAP, reg_data); }
CHECK_STATUS(mv_seq_exec(serdes_num, PEX_POWER_UP_SEQ)); diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index cc139af6cb57..ba0d334e2009 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -259,6 +259,7 @@ config PCI_MVEBU bool "Enable Armada XP/38x PCIe driver" depends on ARCH_MVEBU select MISC + select DM_RESET help Say Y here if you want to enable PCIe controller support on Armada XP/38x SoCs. diff --git a/drivers/pci/pci_mvebu.c b/drivers/pci/pci_mvebu.c index b5b0897b2124..84b8ab4fb4d6 100644 --- a/drivers/pci/pci_mvebu.c +++ b/drivers/pci/pci_mvebu.c @@ -18,6 +18,7 @@ #include <dm/lists.h> #include <dm/of_access.h> #include <pci.h> +#include <reset.h> #include <asm/io.h> #include <asm/arch/cpu.h> #include <asm/arch/soc.h> @@ -83,6 +84,7 @@ struct mvebu_pcie { u32 intregs; u32 port; u32 lane; + bool is_x4; int devfn; u32 lane_mask; int first_busno; @@ -388,7 +390,30 @@ static void mvebu_pcie_setup_wins(struct mvebu_pcie *pcie) /* Only enable PCIe link, do not setup it */ static int mvebu_pcie_enable_link(struct mvebu_pcie *pcie, ofnode node) { - /* PCIe link is currently automatically enabled in SerDes code */ + struct reset_ctl rst; + int ret; + + ret = reset_get_by_index_nodev(node, 0, &rst); + if (ret == -ENOENT) { + return 0; + } else if (ret < 0) { + printf("%s: cannot get reset controller: %d\n", pcie->name, ret); + return ret; + } + + ret = reset_request(&rst); + if (ret) { + printf("%s: cannot request reset controller: %d\n", pcie->name, ret); + return ret; + } + + ret = reset_deassert(&rst); + reset_free(&rst); + if (ret) { + printf("%s: cannot enable PCIe port: %d\n", pcie->name, ret); + return ret; + } + return 0; }
@@ -401,6 +426,18 @@ static void mvebu_pcie_setup_link(struct mvebu_pcie *pcie) reg = readl(pcie->base + PCIE_CTRL_OFF); reg |= PCIE_CTRL_RC_MODE; writel(reg, pcie->base + PCIE_CTRL_OFF); + + /* + * Set Maximum Link Width to X1 or X4 in Root Port's PCIe Link + * Capability register. This register is defined by PCIe specification + * as read-only but this mvebu controller has it as read-write and must + * be set to number of SerDes PCIe lanes (1 or 4). If this register is + * not set correctly then link with endpoint card is not established. + */ + reg = readl(pcie->base + PCIE_CAPAB_OFF + PCI_EXP_LNKCAP); + reg &= ~PCI_EXP_LNKCAP_MLW; + reg |= (pcie->is_x4 ? 4 : 1) << 4; + writel(reg, pcie->base + PCIE_CAPAB_OFF + PCI_EXP_LNKCAP); }
static int mvebu_pcie_probe(struct udevice *dev) @@ -591,6 +628,7 @@ static int mvebu_pcie_port_parse_dt(ofnode node, ofnode parent, struct mvebu_pci { struct fdt_pci_addr pci_addr; const u32 *addr; + u32 num_lanes; int ret = 0; int len;
@@ -606,6 +644,9 @@ static int mvebu_pcie_port_parse_dt(ofnode node, ofnode parent, struct mvebu_pci
sprintf(pcie->name, "pcie%d.%d", pcie->port, pcie->lane);
+ if (!ofnode_read_u32(node, "num-lanes", &num_lanes) && num_lanes == 4) + pcie->is_x4 = true; + /* devfn is in bits [15:8], see PCI_DEV usage */ ret = ofnode_read_pci_addr(node, FDT_PCI_SPACE_CONFIG, "reg", &pci_addr); if (ret < 0) {

Mario: Could you please look at this patch series? It touches gdsys board and for future gdsys board should be converted to use u-boot driver model API...
On Tuesday 21 December 2021 12:20:10 Pali Rohár wrote:
This patch series removes gdsys's board_pex_config() function by converting it to spl_board_init(), adds a new mvebu-reset driver for enabling / disabling PCIe ports and finally moves PCIe code from serdes driver to pci_mvebu.c driver.
After all these changes, PCIe link is not initialized in serdes code anymore, but in pci_mvebu.c driver with help of mvebu-reset driver.
I'm not sure if change for gdsys board is correct, so if somebody has this board, please test it.
I tested this change on A385 board Turris Omnia and I verified that PCIe links are really enabled by pci_mvebu.c driver and not before.
This patch series is based on u-boot-marvell/next branch.
Pali Rohár (9): arm: mvebu: Convert board_pex_config() to CONFIG_SPL_BOARD_INIT board: gdsys: a38x: Enable PCIe link 2 in spl_board_init() pci: pci_mvebu: Fix PCIe MEM and IO resources assignment and mbus mapping pci: pci_mvebu: Inline mvebu_pcie_port_parse_dt() function pci: pci_mvebu: Remove dependency on SOC_REGS_PHY_BASE macro pci: pci_mvebu: Split initialization of PCIe ports into 3 phases pci: pci_mvebu: Wait 100ms for Link Up in mvebu_pcie_probe() arm: mvebu: Implement simple mvebu-reset driver for enabling/disabling PCIe ports arm: mvebu: a38x: serdes: Move non-serdes PCIe code to pci_mvebu.c
arch/arm/dts/armada-375.dtsi | 5 +- arch/arm/dts/armada-380.dtsi | 3 + arch/arm/dts/armada-385.dtsi | 4 + arch/arm/dts/armada-38x.dtsi | 1 + arch/arm/dts/armada-xp-98dx3236.dtsi | 2 + arch/arm/dts/armada-xp-mv78230.dtsi | 5 + arch/arm/dts/armada-xp-mv78260.dtsi | 9 + arch/arm/dts/armada-xp-mv78460.dtsi | 10 + arch/arm/dts/armada-xp-synology-ds414.dts | 1 + arch/arm/dts/armada-xp-theadorable.dts | 1 + arch/arm/dts/armada-xp.dtsi | 1 + arch/arm/mach-mvebu/Makefile | 1 + arch/arm/mach-mvebu/include/mach/cpu.h | 5 +- arch/arm/mach-mvebu/serdes/a38x/Makefile | 1 - arch/arm/mach-mvebu/serdes/a38x/ctrl_pex.c | 64 ---- arch/arm/mach-mvebu/serdes/a38x/ctrl_pex.h | 28 -- .../serdes/a38x/high_speed_env_spec.c | 19 -- arch/arm/mach-mvebu/system-controller.c | 105 +++++++ board/gdsys/a38x/controlcenterdc.c | 6 +- configs/controlcenterdc_defconfig | 1 + drivers/pci/Kconfig | 1 + drivers/pci/pci_mvebu.c | 275 ++++++++++++++---- 22 files changed, 371 insertions(+), 177 deletions(-) delete mode 100644 arch/arm/mach-mvebu/serdes/a38x/ctrl_pex.c delete mode 100644 arch/arm/mach-mvebu/serdes/a38x/ctrl_pex.h create mode 100644 arch/arm/mach-mvebu/system-controller.c
-- 2.20.1

On 12/21/21 12:20, Pali Rohár wrote:
This patch series removes gdsys's board_pex_config() function by converting it to spl_board_init(), adds a new mvebu-reset driver for enabling / disabling PCIe ports and finally moves PCIe code from serdes driver to pci_mvebu.c driver.
After all these changes, PCIe link is not initialized in serdes code anymore, but in pci_mvebu.c driver with help of mvebu-reset driver.
I'm not sure if change for gdsys board is correct, so if somebody has this board, please test it.
I tested this change on A385 board Turris Omnia and I verified that PCIe links are really enabled by pci_mvebu.c driver and not before.
This patch series is based on u-boot-marvell/next branch.
Pali Rohár (9): arm: mvebu: Convert board_pex_config() to CONFIG_SPL_BOARD_INIT board: gdsys: a38x: Enable PCIe link 2 in spl_board_init() pci: pci_mvebu: Fix PCIe MEM and IO resources assignment and mbus mapping pci: pci_mvebu: Inline mvebu_pcie_port_parse_dt() function pci: pci_mvebu: Remove dependency on SOC_REGS_PHY_BASE macro pci: pci_mvebu: Split initialization of PCIe ports into 3 phases pci: pci_mvebu: Wait 100ms for Link Up in mvebu_pcie_probe() arm: mvebu: Implement simple mvebu-reset driver for enabling/disabling PCIe ports arm: mvebu: a38x: serdes: Move non-serdes PCIe code to pci_mvebu.c
arch/arm/dts/armada-375.dtsi | 5 +- arch/arm/dts/armada-380.dtsi | 3 + arch/arm/dts/armada-385.dtsi | 4 + arch/arm/dts/armada-38x.dtsi | 1 + arch/arm/dts/armada-xp-98dx3236.dtsi | 2 + arch/arm/dts/armada-xp-mv78230.dtsi | 5 + arch/arm/dts/armada-xp-mv78260.dtsi | 9 + arch/arm/dts/armada-xp-mv78460.dtsi | 10 + arch/arm/dts/armada-xp-synology-ds414.dts | 1 + arch/arm/dts/armada-xp-theadorable.dts | 1 + arch/arm/dts/armada-xp.dtsi | 1 + arch/arm/mach-mvebu/Makefile | 1 + arch/arm/mach-mvebu/include/mach/cpu.h | 5 +- arch/arm/mach-mvebu/serdes/a38x/Makefile | 1 - arch/arm/mach-mvebu/serdes/a38x/ctrl_pex.c | 64 ---- arch/arm/mach-mvebu/serdes/a38x/ctrl_pex.h | 28 -- .../serdes/a38x/high_speed_env_spec.c | 19 -- arch/arm/mach-mvebu/system-controller.c | 105 +++++++ board/gdsys/a38x/controlcenterdc.c | 6 +- configs/controlcenterdc_defconfig | 1 + drivers/pci/Kconfig | 1 + drivers/pci/pci_mvebu.c | 275 ++++++++++++++---- 22 files changed, 371 insertions(+), 177 deletions(-) delete mode 100644 arch/arm/mach-mvebu/serdes/a38x/ctrl_pex.c delete mode 100644 arch/arm/mach-mvebu/serdes/a38x/ctrl_pex.h create mode 100644 arch/arm/mach-mvebu/system-controller.c
Applied to u-boot-marvell/master
Thanks, Stefan
participants (2)
-
Pali Rohár
-
Stefan Roese