From bc02973a06a6c74374edeb6d73ed4bde99b37456 Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Wed, 24 Nov 2021 16:41:12 +0100 Subject: arm: ioremap: Implement standard PCI function pci_remap_iospace() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pci_remap_iospace() is standard PCI core function. Architecture code can reimplement default core implementation if needs custom arch specific functionality. ARM needs custom implementation due to pci_ioremap_set_mem_type() hook which allows ARM platforms to change mem type for iospace. Implement this pci_remap_iospace() function for ARM architecture to correctly handle pci_ioremap_set_mem_type() hook, which allows usage of this standard PCI core function also for platforms which needs different mem type (e.g. Marvell Armada 375, 38x and 39x). Link: https://lore.kernel.org/r/20211124154116.916-2-pali@kernel.org Signed-off-by: Pali Rohár Signed-off-by: Lorenzo Pieralisi Reviewed-by: Russell King (Oracle) --- arch/arm/include/asm/io.h | 5 +++++ arch/arm/mm/ioremap.c | 15 +++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/arch/arm/include/asm/io.h b/arch/arm/include/asm/io.h index c576fa7d9bf8..12eca75bdee9 100644 --- a/arch/arm/include/asm/io.h +++ b/arch/arm/include/asm/io.h @@ -182,6 +182,11 @@ static inline void pci_ioremap_set_mem_type(int mem_type) {} extern int pci_ioremap_io(unsigned int offset, phys_addr_t phys_addr); +struct resource; + +#define pci_remap_iospace pci_remap_iospace +int pci_remap_iospace(const struct resource *res, phys_addr_t phys_addr); + /* * PCI configuration space mapping function. * diff --git a/arch/arm/mm/ioremap.c b/arch/arm/mm/ioremap.c index 6e830b9418c9..fa3bde48d6a7 100644 --- a/arch/arm/mm/ioremap.c +++ b/arch/arm/mm/ioremap.c @@ -459,6 +459,21 @@ void pci_ioremap_set_mem_type(int mem_type) pci_ioremap_mem_type = mem_type; } +int pci_remap_iospace(const struct resource *res, phys_addr_t phys_addr) +{ + unsigned long vaddr = (unsigned long)PCI_IOBASE + res->start; + + if (!(res->flags & IORESOURCE_IO)) + return -EINVAL; + + if (res->end > IO_SPACE_LIMIT) + return -EINVAL; + + return ioremap_page_range(vaddr, vaddr + resource_size(res), phys_addr, + __pgprot(get_mem_type(pci_ioremap_mem_type)->prot_pte)); +} +EXPORT_SYMBOL(pci_remap_iospace); + int pci_ioremap_io(unsigned int offset, phys_addr_t phys_addr) { BUG_ON(offset + SZ_64K - 1 > IO_SPACE_LIMIT); -- cgit From c1aa4b55aae4c283e57c07e71968504bfa7d4a13 Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Wed, 24 Nov 2021 16:41:13 +0100 Subject: PCI: mvebu: Replace pci_ioremap_io() usage by devm_pci_remap_iospace() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now when ARM architecture code also provides standard PCI core function pci_remap_iospace(), use its devm_pci_remap_iospace() variant in pci-mvebu.c driver instead of old ARM-specific pci_ioremap_io() function. Call devm_pci_remap_iospace() before adding IO resource to host bridge structure, at the place where it should be. Link: https://lore.kernel.org/r/20211124154116.916-3-pali@kernel.org Signed-off-by: Pali Rohár Signed-off-by: Lorenzo Pieralisi --- drivers/pci/controller/pci-mvebu.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c index ed13e81cd691..a55b8bd5eb62 100644 --- a/drivers/pci/controller/pci-mvebu.c +++ b/drivers/pci/controller/pci-mvebu.c @@ -992,6 +992,10 @@ static int mvebu_pcie_parse_request_resources(struct mvebu_pcie *pcie) resource_size(&pcie->io) - 1); pcie->realio.name = "PCI I/O"; + ret = devm_pci_remap_iospace(dev, &pcie->realio, pcie->io.start); + if (ret) + return ret; + pci_add_resource(&bridge->windows, &pcie->realio); ret = devm_request_resource(dev, &ioport_resource, &pcie->realio); if (ret) @@ -1010,7 +1014,6 @@ static int mvebu_pcie_parse_request_resources(struct mvebu_pcie *pcie) */ static int mvebu_pci_host_probe(struct pci_host_bridge *bridge) { - struct mvebu_pcie *pcie; struct pci_bus *bus, *child; int ret; @@ -1020,14 +1023,6 @@ static int mvebu_pci_host_probe(struct pci_host_bridge *bridge) return ret; } - pcie = pci_host_bridge_priv(bridge); - if (resource_size(&pcie->io) != 0) { - unsigned int i; - - for (i = 0; i < resource_size(&pcie->realio); i += SZ_64K) - pci_ioremap_io(i, pcie->io.start + i); - } - bus = bridge->bus; /* -- cgit From 873883f2e92e21668e6a0ab051749429a602b121 Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Wed, 24 Nov 2021 16:41:14 +0100 Subject: PCI: mvebu: Remove custom mvebu_pci_host_probe() function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now after pci_ioremap_io() usage was replaced by devm_pci_remap_iospace() function, there is no need to use custom mvebu_pci_host_probe() function. Current implementation of mvebu_pci_host_probe() is same as standard PCI core function pci_host_probe(). So replace mvebu_pci_host_probe() call by pci_host_probe() and remove custom mvebu_pci_host_probe() function. Link: https://lore.kernel.org/r/20211124154116.916-4-pali@kernel.org Signed-off-by: Pali Rohár Signed-off-by: Lorenzo Pieralisi --- drivers/pci/controller/pci-mvebu.c | 41 +------------------------------------- 1 file changed, 1 insertion(+), 40 deletions(-) diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c index a55b8bd5eb62..f2180e4630a1 100644 --- a/drivers/pci/controller/pci-mvebu.c +++ b/drivers/pci/controller/pci-mvebu.c @@ -1005,45 +1005,6 @@ static int mvebu_pcie_parse_request_resources(struct mvebu_pcie *pcie) return 0; } -/* - * This is a copy of pci_host_probe(), except that it does the I/O - * remap as the last step, once we are sure we won't fail. - * - * It should be removed once the I/O remap error handling issue has - * been sorted out. - */ -static int mvebu_pci_host_probe(struct pci_host_bridge *bridge) -{ - struct pci_bus *bus, *child; - int ret; - - ret = pci_scan_root_bus_bridge(bridge); - if (ret < 0) { - dev_err(bridge->dev.parent, "Scanning root bridge failed"); - return ret; - } - - bus = bridge->bus; - - /* - * We insert PCI resources into the iomem_resource and - * ioport_resource trees in either pci_bus_claim_resources() - * or pci_bus_assign_resources(). - */ - if (pci_has_flag(PCI_PROBE_ONLY)) { - pci_bus_claim_resources(bus); - } else { - pci_bus_size_bridges(bus); - pci_bus_assign_resources(bus); - - list_for_each_entry(child, &bus->children, node) - pcie_bus_configure_settings(child); - } - - pci_bus_add_devices(bus); - return 0; -} - static int mvebu_pcie_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -1118,7 +1079,7 @@ static int mvebu_pcie_probe(struct platform_device *pdev) bridge->ops = &mvebu_pcie_ops; bridge->align_resource = mvebu_pcie_align_resource; - return mvebu_pci_host_probe(bridge); + return pci_host_probe(bridge); } static const struct of_device_id mvebu_pcie_of_match_table[] = { -- cgit From 6198461ef509356e7f0fe5b04e88009aa698a065 Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Wed, 24 Nov 2021 16:41:15 +0100 Subject: arm: ioremap: Replace pci_ioremap_io() usage by pci_remap_iospace() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace all usage of ARM specific pci_ioremap_io() function by standard PCI core API function pci_remap_iospace() in all drivers and ARM mach code. Link: https://lore.kernel.org/r/20211124154116.916-5-pali@kernel.org Signed-off-by: Pali Rohár Signed-off-by: Lorenzo Pieralisi Reviewed-by: Russell King (Oracle) Reviewed-by: Alexandre Belloni --- arch/arm/mach-dove/pcie.c | 9 +++++---- arch/arm/mach-iop32x/pci.c | 5 ++++- arch/arm/mach-mv78xx0/pcie.c | 5 ++++- arch/arm/mach-orion5x/pci.c | 10 ++++++++-- drivers/pcmcia/at91_cf.c | 6 +++++- 5 files changed, 26 insertions(+), 9 deletions(-) diff --git a/arch/arm/mach-dove/pcie.c b/arch/arm/mach-dove/pcie.c index ee91ac6b5ebf..2a493bdfffc6 100644 --- a/arch/arm/mach-dove/pcie.c +++ b/arch/arm/mach-dove/pcie.c @@ -38,6 +38,7 @@ static int num_pcie_ports; static int __init dove_pcie_setup(int nr, struct pci_sys_data *sys) { struct pcie_port *pp; + struct resource realio; if (nr >= num_pcie_ports) return 0; @@ -53,10 +54,10 @@ static int __init dove_pcie_setup(int nr, struct pci_sys_data *sys) orion_pcie_setup(pp->base); - if (pp->index == 0) - pci_ioremap_io(sys->busnr * SZ_64K, DOVE_PCIE0_IO_PHYS_BASE); - else - pci_ioremap_io(sys->busnr * SZ_64K, DOVE_PCIE1_IO_PHYS_BASE); + realio.start = sys->busnr * SZ_64K; + realio.end = realio.start + SZ_64K - 1; + pci_remap_iospace(&realio, pp->index == 0 ? DOVE_PCIE0_IO_PHYS_BASE : + DOVE_PCIE1_IO_PHYS_BASE); /* * IORESOURCE_MEM diff --git a/arch/arm/mach-iop32x/pci.c b/arch/arm/mach-iop32x/pci.c index ab0010dc3145..7a215d2ee7e2 100644 --- a/arch/arm/mach-iop32x/pci.c +++ b/arch/arm/mach-iop32x/pci.c @@ -185,6 +185,7 @@ iop3xx_pci_abort(unsigned long addr, unsigned int fsr, struct pt_regs *regs) int iop3xx_pci_setup(int nr, struct pci_sys_data *sys) { struct resource *res; + struct resource realio; if (nr != 0) return 0; @@ -206,7 +207,9 @@ int iop3xx_pci_setup(int nr, struct pci_sys_data *sys) pci_add_resource_offset(&sys->resources, res, sys->mem_offset); - pci_ioremap_io(0, IOP3XX_PCI_LOWER_IO_PA); + realio.start = 0; + realio.end = realio.start + SZ_64K - 1; + pci_remap_iospace(&realio, IOP3XX_PCI_LOWER_IO_PA); return 1; } diff --git a/arch/arm/mach-mv78xx0/pcie.c b/arch/arm/mach-mv78xx0/pcie.c index 636d84b40466..e15646af7f26 100644 --- a/arch/arm/mach-mv78xx0/pcie.c +++ b/arch/arm/mach-mv78xx0/pcie.c @@ -101,6 +101,7 @@ static void __init mv78xx0_pcie_preinit(void) static int __init mv78xx0_pcie_setup(int nr, struct pci_sys_data *sys) { struct pcie_port *pp; + struct resource realio; if (nr >= num_pcie_ports) return 0; @@ -115,7 +116,9 @@ static int __init mv78xx0_pcie_setup(int nr, struct pci_sys_data *sys) orion_pcie_set_local_bus_nr(pp->base, sys->busnr); orion_pcie_setup(pp->base); - pci_ioremap_io(nr * SZ_64K, MV78XX0_PCIE_IO_PHYS_BASE(nr)); + realio.start = nr * SZ_64K; + realio.end = realio.start + SZ_64K - 1; + pci_remap_iospace(&realio, MV78XX0_PCIE_IO_PHYS_BASE(nr)); pci_add_resource_offset(&sys->resources, &pp->res, sys->mem_offset); diff --git a/arch/arm/mach-orion5x/pci.c b/arch/arm/mach-orion5x/pci.c index 76951bfbacf5..92e938bba20d 100644 --- a/arch/arm/mach-orion5x/pci.c +++ b/arch/arm/mach-orion5x/pci.c @@ -142,6 +142,7 @@ static struct pci_ops pcie_ops = { static int __init pcie_setup(struct pci_sys_data *sys) { struct resource *res; + struct resource realio; int dev; /* @@ -164,7 +165,9 @@ static int __init pcie_setup(struct pci_sys_data *sys) pcie_ops.read = pcie_rd_conf_wa; } - pci_ioremap_io(sys->busnr * SZ_64K, ORION5X_PCIE_IO_PHYS_BASE); + realio.start = sys->busnr * SZ_64K; + realio.end = realio.start + SZ_64K - 1; + pci_remap_iospace(&realio, ORION5X_PCIE_IO_PHYS_BASE); /* * Request resources. @@ -466,6 +469,7 @@ static void __init orion5x_setup_pci_wins(void) static int __init pci_setup(struct pci_sys_data *sys) { struct resource *res; + struct resource realio; /* * Point PCI unit MBUS decode windows to DRAM space. @@ -482,7 +486,9 @@ static int __init pci_setup(struct pci_sys_data *sys) */ orion5x_setbits(PCI_CMD, PCI_CMD_HOST_REORDER); - pci_ioremap_io(sys->busnr * SZ_64K, ORION5X_PCI_IO_PHYS_BASE); + realio.start = sys->busnr * SZ_64K; + realio.end = realio.start + SZ_64K - 1; + pci_remap_iospace(&realio, ORION5X_PCI_IO_PHYS_BASE); /* * Request resources diff --git a/drivers/pcmcia/at91_cf.c b/drivers/pcmcia/at91_cf.c index 6b1edfc890a3..92df2c2c5d07 100644 --- a/drivers/pcmcia/at91_cf.c +++ b/drivers/pcmcia/at91_cf.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -230,6 +231,7 @@ static int at91_cf_probe(struct platform_device *pdev) struct at91_cf_socket *cf; struct at91_cf_data *board; struct resource *io; + struct resource realio; int status; board = devm_kzalloc(&pdev->dev, sizeof(*board), GFP_KERNEL); @@ -307,7 +309,9 @@ static int at91_cf_probe(struct platform_device *pdev) * io_offset is set to 0x10000 to avoid the check in static_find_io(). * */ cf->socket.io_offset = 0x10000; - status = pci_ioremap_io(0x10000, cf->phys_baseaddr + CF_IO_PHYS); + realio.start = cf->socket.io_offset; + realio.end = realio.start + SZ_64K - 1; + status = pci_remap_iospace(&realio, cf->phys_baseaddr + CF_IO_PHYS); if (status) goto fail0a; -- cgit From 600b790309864fcc311b5dc701f8dc5c3b81374c Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Wed, 24 Nov 2021 16:41:16 +0100 Subject: arm: ioremap: Remove unused ARM-specific function pci_ioremap_io() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This function is not used by any driver anymore. So completely remove it. Link: https://lore.kernel.org/r/20211124154116.916-6-pali@kernel.org Signed-off-by: Pali Rohár Signed-off-by: Lorenzo Pieralisi Reviewed-by: Russell King (Oracle) --- arch/arm/include/asm/io.h | 2 -- arch/arm/mm/ioremap.c | 11 ----------- 2 files changed, 13 deletions(-) diff --git a/arch/arm/include/asm/io.h b/arch/arm/include/asm/io.h index 12eca75bdee9..0c70eb688a00 100644 --- a/arch/arm/include/asm/io.h +++ b/arch/arm/include/asm/io.h @@ -180,8 +180,6 @@ void pci_ioremap_set_mem_type(int mem_type); static inline void pci_ioremap_set_mem_type(int mem_type) {} #endif -extern int pci_ioremap_io(unsigned int offset, phys_addr_t phys_addr); - struct resource; #define pci_remap_iospace pci_remap_iospace diff --git a/arch/arm/mm/ioremap.c b/arch/arm/mm/ioremap.c index fa3bde48d6a7..197f8eb3a775 100644 --- a/arch/arm/mm/ioremap.c +++ b/arch/arm/mm/ioremap.c @@ -474,17 +474,6 @@ int pci_remap_iospace(const struct resource *res, phys_addr_t phys_addr) } EXPORT_SYMBOL(pci_remap_iospace); -int pci_ioremap_io(unsigned int offset, phys_addr_t phys_addr) -{ - BUG_ON(offset + SZ_64K - 1 > IO_SPACE_LIMIT); - - return ioremap_page_range(PCI_IO_VIRT_BASE + offset, - PCI_IO_VIRT_BASE + offset + SZ_64K, - phys_addr, - __pgprot(get_mem_type(pci_ioremap_mem_type)->prot_pte)); -} -EXPORT_SYMBOL_GPL(pci_ioremap_io); - void __iomem *pci_remap_cfgspace(resource_size_t res_cookie, size_t size) { return arch_ioremap_caller(res_cookie, size, MT_UNCACHED, -- cgit From 8cdabfdd5a22ded53ab18f50df48e04dba744ad4 Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Thu, 25 Nov 2021 13:45:51 +0100 Subject: PCI: mvebu: Check for valid ports MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some mvebu ports do not have to be initialized. So skip these uninitialized mvebu ports in every port iteration function to prevent access to unmapped memory or dereferencing NULL pointers. Uninitialized mvebu port has base address set to NULL. Link: https://lore.kernel.org/r/20211125124605.25915-2-pali@kernel.org Signed-off-by: Pali Rohár Signed-off-by: Lorenzo Pieralisi --- drivers/pci/controller/pci-mvebu.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c index f2180e4630a1..51cf3ecb4121 100644 --- a/drivers/pci/controller/pci-mvebu.c +++ b/drivers/pci/controller/pci-mvebu.c @@ -606,6 +606,9 @@ static struct mvebu_pcie_port *mvebu_pcie_find_port(struct mvebu_pcie *pcie, for (i = 0; i < pcie->nports; i++) { struct mvebu_pcie_port *port = &pcie->ports[i]; + if (!port->base) + continue; + if (bus->number == 0 && port->devfn == devfn) return port; if (bus->number != 0 && @@ -781,6 +784,8 @@ static int mvebu_pcie_suspend(struct device *dev) pcie = dev_get_drvdata(dev); for (i = 0; i < pcie->nports; i++) { struct mvebu_pcie_port *port = pcie->ports + i; + if (!port->base) + continue; port->saved_pcie_stat = mvebu_readl(port, PCIE_STAT_OFF); } @@ -795,6 +800,8 @@ static int mvebu_pcie_resume(struct device *dev) pcie = dev_get_drvdata(dev); for (i = 0; i < pcie->nports; i++) { struct mvebu_pcie_port *port = pcie->ports + i; + if (!port->base) + continue; mvebu_writel(port, port->saved_pcie_stat, PCIE_STAT_OFF); mvebu_pcie_setup_hw(port); } -- cgit From 5d18d702e5c9309f4195653475c7a7fdde4ca71f Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Thu, 25 Nov 2021 13:45:52 +0100 Subject: PCI: mvebu: Check for errors from pci_bridge_emul_init() call MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Function pci_bridge_emul_init() may fail so correctly check for errors. Link: https://lore.kernel.org/r/20211125124605.25915-3-pali@kernel.org Fixes: 1f08673eef12 ("PCI: mvebu: Convert to PCI emulated bridge config space") Signed-off-by: Pali Rohár Signed-off-by: Lorenzo Pieralisi --- drivers/pci/controller/pci-mvebu.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c index 51cf3ecb4121..e4424db808fe 100644 --- a/drivers/pci/controller/pci-mvebu.c +++ b/drivers/pci/controller/pci-mvebu.c @@ -570,7 +570,7 @@ static struct pci_bridge_emul_ops mvebu_pci_bridge_emul_ops = { * Initialize the configuration space of the PCI-to-PCI bridge * associated with the given PCIe interface. */ -static void mvebu_pci_bridge_emul_init(struct mvebu_pcie_port *port) +static int mvebu_pci_bridge_emul_init(struct mvebu_pcie_port *port) { struct pci_bridge_emul *bridge = &port->bridge; @@ -589,7 +589,7 @@ static void mvebu_pci_bridge_emul_init(struct mvebu_pcie_port *port) bridge->data = port; bridge->ops = &mvebu_pci_bridge_emul_ops; - pci_bridge_emul_init(bridge, PCI_BRIDGE_EMUL_NO_PREFETCHABLE_BAR); + return pci_bridge_emul_init(bridge, PCI_BRIDGE_EMUL_NO_PREFETCHABLE_BAR); } static inline struct mvebu_pcie *sys_to_pcie(struct pci_sys_data *sys) @@ -1075,9 +1075,18 @@ static int mvebu_pcie_probe(struct platform_device *pdev) continue; } + ret = mvebu_pci_bridge_emul_init(port); + if (ret < 0) { + dev_err(dev, "%s: cannot init emulated bridge\n", + port->name); + devm_iounmap(dev, port->base); + port->base = NULL; + mvebu_pcie_powerdown(port); + continue; + } + mvebu_pcie_setup_hw(port); mvebu_pcie_set_local_dev_nr(port, 1); - mvebu_pci_bridge_emul_init(port); } pcie->nports = i; -- cgit From 489bfc51870b96cd3ad2ef63cf443b5f5d1a8e21 Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Thu, 25 Nov 2021 13:45:53 +0100 Subject: PCI: mvebu: Check that PCI bridge specified in DT has function number zero MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Driver cannot handle PCI bridges at non-zero function address. So add appropriate check. Currently all in-tree kernel DTS files set PCI bridge function to zero. Link: https://lore.kernel.org/r/20211125124605.25915-4-pali@kernel.org Signed-off-by: Pali Rohár Signed-off-by: Lorenzo Pieralisi --- drivers/pci/controller/pci-mvebu.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c index e4424db808fe..2a2100c2e65d 100644 --- a/drivers/pci/controller/pci-mvebu.c +++ b/drivers/pci/controller/pci-mvebu.c @@ -845,6 +845,11 @@ static int mvebu_pcie_parse_port(struct mvebu_pcie *pcie, port->devfn = of_pci_get_devfn(child); if (port->devfn < 0) goto skip; + if (PCI_FUNC(port->devfn) != 0) { + dev_err(dev, "%s: invalid function number, must be zero\n", + port->name); + goto skip; + } ret = mvebu_get_tgt_attr(dev->of_node, port->devfn, IORESOURCE_MEM, &port->mem_target, &port->mem_attr); -- cgit From 11c2bf4a20c256eea258a4332244c0deb9af0da8 Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Thu, 25 Nov 2021 13:45:54 +0100 Subject: PCI: mvebu: Handle invalid size of read config request MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Function mvebu_pcie_hw_rd_conf() does not handle invalid size. So correctly set read value to all-ones and return appropriate error return value PCIBIOS_BAD_REGISTER_NUMBER like in mvebu_pcie_hw_wr_conf() function. Link: https://lore.kernel.org/r/20211125124605.25915-5-pali@kernel.org Signed-off-by: Pali Rohár Signed-off-by: Lorenzo Pieralisi --- drivers/pci/controller/pci-mvebu.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c index 2a2100c2e65d..8388a9cc911d 100644 --- a/drivers/pci/controller/pci-mvebu.c +++ b/drivers/pci/controller/pci-mvebu.c @@ -250,6 +250,9 @@ static int mvebu_pcie_hw_rd_conf(struct mvebu_pcie_port *port, case 4: *val = readl_relaxed(conf_data); break; + default: + *val = 0xffffffff; + return PCIBIOS_BAD_REGISTER_NUMBER; } return PCIBIOS_SUCCESSFUL; -- cgit From 319e6046bd5a59e09c1a08fd6f6929df4ae9a1dc Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Thu, 25 Nov 2021 13:45:55 +0100 Subject: PCI: mvebu: Disallow mapping interrupts on emulated bridges MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Interrupt support on mvebu emulated bridges is not implemented yet. So properly indicate return value to callers that they cannot request interrupts from emulated bridge. Link: https://lore.kernel.org/r/20211125124605.25915-6-pali@kernel.org Signed-off-by: Pali Rohár Signed-off-by: Lorenzo Pieralisi --- drivers/pci/controller/pci-mvebu.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c index 8388a9cc911d..d9c76780d7cf 100644 --- a/drivers/pci/controller/pci-mvebu.c +++ b/drivers/pci/controller/pci-mvebu.c @@ -686,6 +686,15 @@ static struct pci_ops mvebu_pcie_ops = { .write = mvebu_pcie_wr_conf, }; +static int mvebu_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) +{ + /* Interrupt support on mvebu emulated bridges is not implemented yet */ + if (dev->bus->number == 0) + return 0; /* Proper return code 0 == NO_IRQ */ + + return of_irq_parse_and_map_pci(dev, slot, pin); +} + static resource_size_t mvebu_pcie_align_resource(struct pci_dev *dev, const struct resource *res, resource_size_t start, @@ -1102,6 +1111,7 @@ static int mvebu_pcie_probe(struct platform_device *pdev) bridge->sysdata = pcie; bridge->ops = &mvebu_pcie_ops; bridge->align_resource = mvebu_pcie_align_resource; + bridge->map_irq = mvebu_pcie_map_irq; return pci_host_probe(bridge); } -- cgit From e42b85583719adb87ab88dc7bcd41b38011f7d11 Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Thu, 25 Nov 2021 13:45:56 +0100 Subject: PCI: mvebu: Fix support for bus mastering and PCI_COMMAND on emulated bridge MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to PCI specifications bits [0:2] of Command Register, this should be by default disabled on reset. So explicitly disable these bits at early beginning of driver initialization. Also remove code which unconditionally enables all 3 bits and let kernel code (via pci_set_master() function) to handle bus mastering of PCI Bridge via emulated PCI_COMMAND on emulated bridge. Adjust existing functions mvebu_pcie_handle_iobase_change() and mvebu_pcie_handle_membase_change() to handle PCI_IO_BASE and PCI_MEM_BASE registers correctly even when bus mastering on emulated bridge is disabled. Link: https://lore.kernel.org/r/20211125124605.25915-7-pali@kernel.org Signed-off-by: Pali Rohár Signed-off-by: Lorenzo Pieralisi --- drivers/pci/controller/pci-mvebu.c | 52 +++++++++++++++++++++++--------------- 1 file changed, 32 insertions(+), 20 deletions(-) diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c index d9c76780d7cf..c4497bb759dd 100644 --- a/drivers/pci/controller/pci-mvebu.c +++ b/drivers/pci/controller/pci-mvebu.c @@ -215,16 +215,14 @@ static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port) { u32 cmd, mask; - /* Point PCIe unit MBUS decode windows to DRAM space. */ - mvebu_pcie_setup_wins(port); - - /* Master + slave enable. */ + /* Disable Root Bridge I/O space, memory space and bus mastering. */ cmd = mvebu_readl(port, PCIE_CMD_OFF); - cmd |= PCI_COMMAND_IO; - cmd |= PCI_COMMAND_MEMORY; - cmd |= PCI_COMMAND_MASTER; + cmd &= ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); mvebu_writel(port, cmd, PCIE_CMD_OFF); + /* Point PCIe unit MBUS decode windows to DRAM space. */ + mvebu_pcie_setup_wins(port); + /* Enable interrupt lines A-D. */ mask = mvebu_readl(port, PCIE_MASK_OFF); mask |= PCIE_MASK_ENABLE_INTS; @@ -374,8 +372,7 @@ static void mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port) /* Are the new iobase/iolimit values invalid? */ if (conf->iolimit < conf->iobase || - conf->iolimitupper < conf->iobaseupper || - !(conf->command & PCI_COMMAND_IO)) { + conf->iolimitupper < conf->iobaseupper) { mvebu_pcie_set_window(port, port->io_target, port->io_attr, &desired, &port->iowin); return; @@ -412,8 +409,7 @@ static void mvebu_pcie_handle_membase_change(struct mvebu_pcie_port *port) struct pci_bridge_emul_conf *conf = &port->bridge.conf; /* Are the new membase/memlimit values invalid? */ - if (conf->memlimit < conf->membase || - !(conf->command & PCI_COMMAND_MEMORY)) { + if (conf->memlimit < conf->membase) { mvebu_pcie_set_window(port, port->mem_target, port->mem_attr, &desired, &port->memwin); return; @@ -433,6 +429,24 @@ static void mvebu_pcie_handle_membase_change(struct mvebu_pcie_port *port) &port->memwin); } +static pci_bridge_emul_read_status_t +mvebu_pci_bridge_emul_base_conf_read(struct pci_bridge_emul *bridge, + int reg, u32 *value) +{ + struct mvebu_pcie_port *port = bridge->data; + + switch (reg) { + case PCI_COMMAND: + *value = mvebu_readl(port, PCIE_CMD_OFF); + break; + + default: + return PCI_BRIDGE_EMUL_NOT_HANDLED; + } + + return PCI_BRIDGE_EMUL_HANDLED; +} + static pci_bridge_emul_read_status_t mvebu_pci_bridge_emul_pcie_conf_read(struct pci_bridge_emul *bridge, int reg, u32 *value) @@ -487,17 +501,14 @@ mvebu_pci_bridge_emul_base_conf_write(struct pci_bridge_emul *bridge, switch (reg) { case PCI_COMMAND: - { - if (!mvebu_has_ioport(port)) - conf->command &= ~PCI_COMMAND_IO; - - if ((old ^ new) & PCI_COMMAND_IO) - mvebu_pcie_handle_iobase_change(port); - if ((old ^ new) & PCI_COMMAND_MEMORY) - mvebu_pcie_handle_membase_change(port); + if (!mvebu_has_ioport(port)) { + conf->command = cpu_to_le16( + le16_to_cpu(conf->command) & ~PCI_COMMAND_IO); + new &= ~PCI_COMMAND_IO; + } + mvebu_writel(port, new, PCIE_CMD_OFF); break; - } case PCI_IO_BASE: /* @@ -564,6 +575,7 @@ mvebu_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge, } static struct pci_bridge_emul_ops mvebu_pci_bridge_emul_ops = { + .read_base = mvebu_pci_bridge_emul_base_conf_read, .write_base = mvebu_pci_bridge_emul_base_conf_write, .read_pcie = mvebu_pci_bridge_emul_pcie_conf_read, .write_pcie = mvebu_pci_bridge_emul_pcie_conf_write, -- cgit From 2cf150216e5b5619d7c25180ccf2cc8ac7bebc13 Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Thu, 25 Nov 2021 13:45:57 +0100 Subject: PCI: mvebu: Do not modify PCI IO type bits in conf_write MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PCI IO type bits are already initialized in mvebu_pci_bridge_emul_init() function and only when IO support is enabled. These type bits are read-only and pci-bridge-emul.c code already does not allow to modify them from upper layers. When IO support is disabled then all IO registers should be read-only and return zeros. Therefore do not modify PCI IO type bits in mvebu_pci_bridge_emul_base_conf_write() callback. Link: https://lore.kernel.org/r/20211125124605.25915-8-pali@kernel.org Fixes: 1f08673eef12 ("PCI: mvebu: Convert to PCI emulated bridge config space") Signed-off-by: Pali Rohár Signed-off-by: Lorenzo Pieralisi --- drivers/pci/controller/pci-mvebu.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c index c4497bb759dd..9e2a4acb963d 100644 --- a/drivers/pci/controller/pci-mvebu.c +++ b/drivers/pci/controller/pci-mvebu.c @@ -511,13 +511,6 @@ mvebu_pci_bridge_emul_base_conf_write(struct pci_bridge_emul *bridge, break; case PCI_IO_BASE: - /* - * We keep bit 1 set, it is a read-only bit that - * indicates we support 32 bits addressing for the - * I/O - */ - conf->iobase |= PCI_IO_RANGE_TYPE_32; - conf->iolimit |= PCI_IO_RANGE_TYPE_32; mvebu_pcie_handle_iobase_change(port); break; -- cgit From e7a01876729c3e650c5f6ee446b71a309d1c55ab Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Thu, 25 Nov 2021 13:45:58 +0100 Subject: PCI: mvebu: Propagate errors when updating PCI_IO_BASE and PCI_MEM_BASE registers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Properly propagate failure from mvebu_pcie_add_windows() function back to the caller mvebu_pci_bridge_emul_base_conf_write() and correctly updates PCI_IO_BASE, PCI_MEM_BASE and PCI_IO_BASE_UPPER16 registers on error. On error set base value higher than limit value which indicates that address range is disabled. When IO is unsupported then let IO registers zeroed as required by PCIe base specification. Link: https://lore.kernel.org/r/20211125124605.25915-9-pali@kernel.org Signed-off-by: Pali Rohár Signed-off-by: Lorenzo Pieralisi --- drivers/pci/controller/pci-mvebu.c | 82 +++++++++++++++++++++++++------------- 1 file changed, 55 insertions(+), 27 deletions(-) diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c index 9e2a4acb963d..edc774e8278b 100644 --- a/drivers/pci/controller/pci-mvebu.c +++ b/drivers/pci/controller/pci-mvebu.c @@ -304,7 +304,7 @@ static void mvebu_pcie_del_windows(struct mvebu_pcie_port *port, * areas each having a power of two size. We start from the largest * one (i.e highest order bit set in the size). */ -static void mvebu_pcie_add_windows(struct mvebu_pcie_port *port, +static int mvebu_pcie_add_windows(struct mvebu_pcie_port *port, unsigned int target, unsigned int attribute, phys_addr_t base, size_t size, phys_addr_t remap) @@ -325,7 +325,7 @@ static void mvebu_pcie_add_windows(struct mvebu_pcie_port *port, &base, &end, ret); mvebu_pcie_del_windows(port, base - size_mapped, size_mapped); - return; + return ret; } size -= sz; @@ -334,16 +334,20 @@ static void mvebu_pcie_add_windows(struct mvebu_pcie_port *port, if (remap != MVEBU_MBUS_NO_REMAP) remap += sz; } + + return 0; } -static void mvebu_pcie_set_window(struct mvebu_pcie_port *port, +static int mvebu_pcie_set_window(struct mvebu_pcie_port *port, unsigned int target, unsigned int attribute, const struct mvebu_pcie_window *desired, struct mvebu_pcie_window *cur) { + int ret; + if (desired->base == cur->base && desired->remap == cur->remap && desired->size == cur->size) - return; + return 0; if (cur->size != 0) { mvebu_pcie_del_windows(port, cur->base, cur->size); @@ -358,30 +362,35 @@ static void mvebu_pcie_set_window(struct mvebu_pcie_port *port, } if (desired->size == 0) - return; + return 0; + + ret = mvebu_pcie_add_windows(port, target, attribute, desired->base, + desired->size, desired->remap); + if (ret) { + cur->size = 0; + cur->base = 0; + return ret; + } - mvebu_pcie_add_windows(port, target, attribute, desired->base, - desired->size, desired->remap); *cur = *desired; + return 0; } -static void mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port) +static int mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port) { struct mvebu_pcie_window desired = {}; struct pci_bridge_emul_conf *conf = &port->bridge.conf; /* Are the new iobase/iolimit values invalid? */ if (conf->iolimit < conf->iobase || - conf->iolimitupper < conf->iobaseupper) { - mvebu_pcie_set_window(port, port->io_target, port->io_attr, - &desired, &port->iowin); - return; - } + conf->iolimitupper < conf->iobaseupper) + return mvebu_pcie_set_window(port, port->io_target, port->io_attr, + &desired, &port->iowin); if (!mvebu_has_ioport(port)) { dev_WARN(&port->pcie->pdev->dev, "Attempt to set IO when IO is disabled\n"); - return; + return -EOPNOTSUPP; } /* @@ -399,21 +408,19 @@ static void mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port) desired.remap) + 1; - mvebu_pcie_set_window(port, port->io_target, port->io_attr, &desired, - &port->iowin); + return mvebu_pcie_set_window(port, port->io_target, port->io_attr, &desired, + &port->iowin); } -static void mvebu_pcie_handle_membase_change(struct mvebu_pcie_port *port) +static int mvebu_pcie_handle_membase_change(struct mvebu_pcie_port *port) { struct mvebu_pcie_window desired = {.remap = MVEBU_MBUS_NO_REMAP}; struct pci_bridge_emul_conf *conf = &port->bridge.conf; /* Are the new membase/memlimit values invalid? */ - if (conf->memlimit < conf->membase) { - mvebu_pcie_set_window(port, port->mem_target, port->mem_attr, - &desired, &port->memwin); - return; - } + if (conf->memlimit < conf->membase) + return mvebu_pcie_set_window(port, port->mem_target, port->mem_attr, + &desired, &port->memwin); /* * We read the PCI-to-PCI bridge emulated registers, and @@ -425,8 +432,8 @@ static void mvebu_pcie_handle_membase_change(struct mvebu_pcie_port *port) desired.size = (((conf->memlimit & 0xFFF0) << 16) | 0xFFFFF) - desired.base + 1; - mvebu_pcie_set_window(port, port->mem_target, port->mem_attr, &desired, - &port->memwin); + return mvebu_pcie_set_window(port, port->mem_target, port->mem_attr, &desired, + &port->memwin); } static pci_bridge_emul_read_status_t @@ -511,15 +518,36 @@ mvebu_pci_bridge_emul_base_conf_write(struct pci_bridge_emul *bridge, break; case PCI_IO_BASE: - mvebu_pcie_handle_iobase_change(port); + if ((mask & 0xffff) && mvebu_pcie_handle_iobase_change(port)) { + /* On error disable IO range */ + conf->iobase &= ~0xf0; + conf->iolimit &= ~0xf0; + conf->iobaseupper = cpu_to_le16(0x0000); + conf->iolimitupper = cpu_to_le16(0x0000); + if (mvebu_has_ioport(port)) + conf->iobase |= 0xf0; + } break; case PCI_MEMORY_BASE: - mvebu_pcie_handle_membase_change(port); + if (mvebu_pcie_handle_membase_change(port)) { + /* On error disable mem range */ + conf->membase = cpu_to_le16(le16_to_cpu(conf->membase) & ~0xfff0); + conf->memlimit = cpu_to_le16(le16_to_cpu(conf->memlimit) & ~0xfff0); + conf->membase = cpu_to_le16(le16_to_cpu(conf->membase) | 0xfff0); + } break; case PCI_IO_BASE_UPPER16: - mvebu_pcie_handle_iobase_change(port); + if (mvebu_pcie_handle_iobase_change(port)) { + /* On error disable IO range */ + conf->iobase &= ~0xf0; + conf->iolimit &= ~0xf0; + conf->iobaseupper = cpu_to_le16(0x0000); + conf->iolimitupper = cpu_to_le16(0x0000); + if (mvebu_has_ioport(port)) + conf->iobase |= 0xf0; + } break; case PCI_PRIMARY_BUS: -- cgit From df08ac016124bd88b8598ac0599d7b89c0642774 Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Thu, 25 Nov 2021 13:45:59 +0100 Subject: PCI: mvebu: Setup PCIe controller to Root Complex mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This driver operates only in Root Complex mode, so ensure that hardware is properly configured in Root Complex mode. Link: https://lore.kernel.org/r/20211125124605.25915-10-pali@kernel.org Signed-off-by: Pali Rohár Signed-off-by: Lorenzo Pieralisi --- drivers/pci/controller/pci-mvebu.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c index edc774e8278b..506e05c3feb6 100644 --- a/drivers/pci/controller/pci-mvebu.c +++ b/drivers/pci/controller/pci-mvebu.c @@ -55,6 +55,7 @@ #define PCIE_MASK_ENABLE_INTS 0x0f000000 #define PCIE_CTRL_OFF 0x1a00 #define PCIE_CTRL_X1_MODE 0x0001 +#define PCIE_CTRL_RC_MODE BIT(1) #define PCIE_STAT_OFF 0x1a04 #define PCIE_STAT_BUS 0xff00 #define PCIE_STAT_DEV 0x1f0000 @@ -213,7 +214,12 @@ static void mvebu_pcie_setup_wins(struct mvebu_pcie_port *port) static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port) { - u32 cmd, mask; + u32 ctrl, cmd, mask; + + /* Setup PCIe controller to Root Complex mode. */ + ctrl = mvebu_readl(port, PCIE_CTRL_OFF); + ctrl |= PCIE_CTRL_RC_MODE; + mvebu_writel(port, ctrl, PCIE_CTRL_OFF); /* Disable Root Bridge I/O space, memory space and bus mastering. */ cmd = mvebu_readl(port, PCIE_CMD_OFF); -- cgit From f587775828219d4e2d7a796c7fff97e50c7f76f1 Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Thu, 25 Nov 2021 13:46:00 +0100 Subject: PCI: mvebu: Set PCI Bridge Class Code to PCI Bridge MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The default value of Class Code of this bridge corresponds to a Memory controller, though. This is probably relict from the past when old Marvell/Galileo PCI-based controllers were used as standalone PCI device for connecting SDRAM or workaround for PCs with broken BIOS. Details are in commit 36de23a4c5f0 ("MIPS: Cobalt: Explain GT64111 early PCI fixup"). Change the Class Code to correspond to a PCI Bridge. Add comment explaining this change. Link: https://lore.kernel.org/r/20211125124605.25915-11-pali@kernel.org Signed-off-by: Pali Rohár Signed-off-by: Lorenzo Pieralisi --- drivers/pci/controller/pci-mvebu.c | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c index 506e05c3feb6..70a2d983b553 100644 --- a/drivers/pci/controller/pci-mvebu.c +++ b/drivers/pci/controller/pci-mvebu.c @@ -214,7 +214,7 @@ static void mvebu_pcie_setup_wins(struct mvebu_pcie_port *port) static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port) { - u32 ctrl, cmd, mask; + u32 ctrl, cmd, dev_rev, mask; /* Setup PCIe controller to Root Complex mode. */ ctrl = mvebu_readl(port, PCIE_CTRL_OFF); @@ -226,6 +226,32 @@ static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port) cmd &= ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); mvebu_writel(port, cmd, PCIE_CMD_OFF); + /* + * Change Class Code of PCI Bridge device to PCI Bridge (0x6004) + * because default value is Memory controller (0x5080). + * + * Note that this mvebu PCI Bridge does not have compliant Type 1 + * Configuration Space. Header Type is reported as Type 0 and it + * has format of Type 0 config space. + * + * Moreover Type 0 BAR registers (ranges 0x10 - 0x28 and 0x30 - 0x34) + * have the same format in Marvell's specification as in PCIe + * specification, but their meaning is totally different and they do + * different things: they are aliased into internal mvebu registers + * (e.g. PCIE_BAR_LO_OFF) and these should not be changed or + * reconfigured by pci device drivers. + * + * Therefore driver uses emulation of PCI Bridge which emulates + * access to configuration space via internal mvebu registers or + * emulated configuration buffer. Driver access these PCI Bridge + * directly for simplification, but these registers can be accessed + * also via standard mvebu way for accessing PCI config space. + */ + dev_rev = mvebu_readl(port, PCIE_DEV_REV_OFF); + dev_rev &= ~0xffffff00; + dev_rev |= (PCI_CLASS_BRIDGE_PCI << 8) << 8; + mvebu_writel(port, dev_rev, PCIE_DEV_REV_OFF); + /* Point PCIe unit MBUS decode windows to DRAM space. */ mvebu_pcie_setup_wins(port); -- cgit From 91a8d79fc797d3486ae978beebdfc55261c7d65b Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Thu, 25 Nov 2021 13:46:01 +0100 Subject: PCI: mvebu: Fix configuring secondary bus of PCIe Root Port via emulated bridge MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It looks like that mvebu PCIe controller has for each PCIe link fully independent PCIe host bridge and so every PCIe Root Port is isolated not only on its own bus but also isolated from each others. But in past device tree structure was defined to put all PCIe Root Ports (as PCI Bridge devices) into one root bus 0 and this bus is emulated by pci-mvebu.c driver. Probably reason for this decision was incorrect understanding of PCIe topology of these Armada SoCs and also reason of misunderstanding how is PCIe controller generating Type 0 and Type 1 config requests (it is fully different compared to other drivers). Probably incorrect setup leaded to very surprised things like having PCIe Root Port (PCI Bridge device, with even incorrect Device Class set to Memory Controller) and the PCIe device behind the Root Port on the same PCI bus, which obviously was needed to somehow hack (as these two devices cannot be in reality on the same bus). Properly set mvebu local bus number and mvebu local device number based on PCI Bridge secondary bus number configuration. Also correctly report configured secondary bus number in config space. And explain in driver comment why this setup is correct. Link: https://lore.kernel.org/r/20211125124605.25915-12-pali@kernel.org Fixes: 1f08673eef12 ("PCI: mvebu: Convert to PCI emulated bridge config space") Signed-off-by: Pali Rohár Signed-off-by: Lorenzo Pieralisi --- drivers/pci/controller/pci-mvebu.c | 99 +++++++++++++++++++++++++++++++++++++- 1 file changed, 97 insertions(+), 2 deletions(-) diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c index 70a2d983b553..9dca136f3201 100644 --- a/drivers/pci/controller/pci-mvebu.c +++ b/drivers/pci/controller/pci-mvebu.c @@ -126,6 +126,11 @@ static bool mvebu_pcie_link_up(struct mvebu_pcie_port *port) return !(mvebu_readl(port, PCIE_STAT_OFF) & PCIE_STAT_LINK_DOWN); } +static u8 mvebu_pcie_get_local_bus_nr(struct mvebu_pcie_port *port) +{ + return (mvebu_readl(port, PCIE_STAT_OFF) & PCIE_STAT_BUS) >> 8; +} + static void mvebu_pcie_set_local_bus_nr(struct mvebu_pcie_port *port, int nr) { u32 stat; @@ -479,6 +484,20 @@ mvebu_pci_bridge_emul_base_conf_read(struct pci_bridge_emul *bridge, *value = mvebu_readl(port, PCIE_CMD_OFF); break; + case PCI_PRIMARY_BUS: { + /* + * From the whole 32bit register we support reading from HW only + * secondary bus number which is mvebu local bus number. + * Other bits are retrieved only from emulated config buffer. + */ + __le32 *cfgspace = (__le32 *)&bridge->conf; + u32 val = le32_to_cpu(cfgspace[PCI_PRIMARY_BUS / 4]); + val &= ~0xff00; + val |= mvebu_pcie_get_local_bus_nr(port) << 8; + *value = val; + break; + } + default: return PCI_BRIDGE_EMUL_NOT_HANDLED; } @@ -583,7 +602,8 @@ mvebu_pci_bridge_emul_base_conf_write(struct pci_bridge_emul *bridge, break; case PCI_PRIMARY_BUS: - mvebu_pcie_set_local_bus_nr(port, conf->secondary_bus); + if (mask & 0xff00) + mvebu_pcie_set_local_bus_nr(port, conf->secondary_bus); break; default: @@ -1167,8 +1187,83 @@ static int mvebu_pcie_probe(struct platform_device *pdev) continue; } + /* + * PCIe topology exported by mvebu hw is quite complicated. In + * reality has something like N fully independent host bridges + * where each host bridge has one PCIe Root Port (which acts as + * PCI Bridge device). Each host bridge has its own independent + * internal registers, independent access to PCI config space, + * independent interrupt lines, independent window and memory + * access configuration. But additionally there is some kind of + * peer-to-peer support between PCIe devices behind different + * host bridges limited just to forwarding of memory and I/O + * transactions (forwarding of error messages and config cycles + * is not supported). So we could say there are N independent + * PCIe Root Complexes. + * + * For this kind of setup DT should have been structured into + * N independent PCIe controllers / host bridges. But instead + * structure in past was defined to put PCIe Root Ports of all + * host bridges into one bus zero, like in classic multi-port + * Root Complex setup with just one host bridge. + * + * This means that pci-mvebu.c driver provides "virtual" bus 0 + * on which registers all PCIe Root Ports (PCI Bridge devices) + * specified in DT by their BDF addresses and virtually routes + * PCI config access of each PCI bridge device to specific PCIe + * host bridge. + * + * Normally PCI Bridge should choose between Type 0 and Type 1 + * config requests based on primary and secondary bus numbers + * configured on the bridge itself. But because mvebu PCI Bridge + * does not have registers for primary and secondary bus numbers + * in its config space, it determinates type of config requests + * via its own custom way. + * + * There are two options how mvebu determinate type of config + * request. + * + * 1. If Secondary Bus Number Enable bit is not set or is not + * available (applies for pre-XP PCIe controllers) then Type 0 + * is used if target bus number equals Local Bus Number (bits + * [15:8] in register 0x1a04) and target device number differs + * from Local Device Number (bits [20:16] in register 0x1a04). + * Type 1 is used if target bus number differs from Local Bus + * Number. And when target bus number equals Local Bus Number + * and target device equals Local Device Number then request is + * routed to Local PCI Bridge (PCIe Root Port). + * + * 2. If Secondary Bus Number Enable bit is set (bit 7 in + * register 0x1a2c) then mvebu hw determinate type of config + * request like compliant PCI Bridge based on primary bus number + * which is configured via Local Bus Number (bits [15:8] in + * register 0x1a04) and secondary bus number which is configured + * via Secondary Bus Number (bits [7:0] in register 0x1a2c). + * Local PCI Bridge (PCIe Root Port) is available on primary bus + * as device with Local Device Number (bits [20:16] in register + * 0x1a04). + * + * Secondary Bus Number Enable bit is disabled by default and + * option 2. is not available on pre-XP PCIe controllers. Hence + * this driver always use option 1. + * + * Basically it means that primary and secondary buses shares + * one virtual number configured via Local Bus Number bits and + * Local Device Number bits determinates if accessing primary + * or secondary bus. Set Local Device Number to 1 and redirect + * all writes of PCI Bridge Secondary Bus Number register to + * Local Bus Number (bits [15:8] in register 0x1a04). + * + * So when accessing devices on buses behind secondary bus + * number it would work correctly. And also when accessing + * device 0 at secondary bus number via config space would be + * correctly routed to secondary bus. Due to issues described + * in mvebu_pcie_setup_hw(), PCI Bridges at primary bus (zero) + * are not accessed directly via PCI config space but rarher + * indirectly via kernel emulated PCI bridge driver. + */ mvebu_pcie_setup_hw(port); - mvebu_pcie_set_local_dev_nr(port, 1); + mvebu_pcie_set_local_dev_nr(port, 0); } pcie->nports = i; -- cgit From d75404cc08832206f173668bd35391c581fea121 Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Thu, 25 Nov 2021 13:46:02 +0100 Subject: PCI: mvebu: Fix support for PCI_BRIDGE_CTL_BUS_RESET on emulated bridge MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Hardware supports PCIe Hot Reset via PCIE_CTRL_OFF register. Use it for implementing PCI_BRIDGE_CTL_BUS_RESET bit of PCI_BRIDGE_CONTROL register on emulated bridge. With this change the function pci_reset_secondary_bus() starts working and can reset connected PCIe card. Link: https://lore.kernel.org/r/20211125124605.25915-13-pali@kernel.org Fixes: 1f08673eef12 ("PCI: mvebu: Convert to PCI emulated bridge config space") Signed-off-by: Pali Rohár Signed-off-by: Lorenzo Pieralisi --- drivers/pci/controller/pci-mvebu.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c index 9dca136f3201..89e610f3c9f3 100644 --- a/drivers/pci/controller/pci-mvebu.c +++ b/drivers/pci/controller/pci-mvebu.c @@ -56,6 +56,7 @@ #define PCIE_CTRL_OFF 0x1a00 #define PCIE_CTRL_X1_MODE 0x0001 #define PCIE_CTRL_RC_MODE BIT(1) +#define PCIE_CTRL_MASTER_HOT_RESET BIT(24) #define PCIE_STAT_OFF 0x1a04 #define PCIE_STAT_BUS 0xff00 #define PCIE_STAT_DEV 0x1f0000 @@ -498,6 +499,22 @@ mvebu_pci_bridge_emul_base_conf_read(struct pci_bridge_emul *bridge, break; } + case PCI_INTERRUPT_LINE: { + /* + * From the whole 32bit register we support reading from HW only + * one bit: PCI_BRIDGE_CTL_BUS_RESET. + * Other bits are retrieved only from emulated config buffer. + */ + __le32 *cfgspace = (__le32 *)&bridge->conf; + u32 val = le32_to_cpu(cfgspace[PCI_INTERRUPT_LINE / 4]); + if (mvebu_readl(port, PCIE_CTRL_OFF) & PCIE_CTRL_MASTER_HOT_RESET) + val |= PCI_BRIDGE_CTL_BUS_RESET << 16; + else + val &= ~(PCI_BRIDGE_CTL_BUS_RESET << 16); + *value = val; + break; + } + default: return PCI_BRIDGE_EMUL_NOT_HANDLED; } @@ -606,6 +623,17 @@ mvebu_pci_bridge_emul_base_conf_write(struct pci_bridge_emul *bridge, mvebu_pcie_set_local_bus_nr(port, conf->secondary_bus); break; + case PCI_INTERRUPT_LINE: + if (mask & (PCI_BRIDGE_CTL_BUS_RESET << 16)) { + u32 ctrl = mvebu_readl(port, PCIE_CTRL_OFF); + if (new & (PCI_BRIDGE_CTL_BUS_RESET << 16)) + ctrl |= PCIE_CTRL_MASTER_HOT_RESET; + else + ctrl &= ~PCIE_CTRL_MASTER_HOT_RESET; + mvebu_writel(port, ctrl, PCIE_CTRL_OFF); + } + break; + default: break; } -- cgit From ecae073e393e65ee7be7ebf3fdd5258ab99f1636 Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Thu, 25 Nov 2021 13:46:03 +0100 Subject: PCI: mvebu: Fix support for PCI_EXP_DEVCTL on emulated bridge MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Comment in Armada 370 functional specification is misleading. PCI_EXP_DEVCTL_*RE bits are supported and configures receiving of error interrupts. Link: https://lore.kernel.org/r/20211125124605.25915-14-pali@kernel.org Fixes: 1f08673eef12 ("PCI: mvebu: Convert to PCI emulated bridge config space") Signed-off-by: Pali Rohár Signed-off-by: Lorenzo Pieralisi --- drivers/pci/controller/pci-mvebu.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c index 89e610f3c9f3..a863b26d44f4 100644 --- a/drivers/pci/controller/pci-mvebu.c +++ b/drivers/pci/controller/pci-mvebu.c @@ -534,9 +534,7 @@ mvebu_pci_bridge_emul_pcie_conf_read(struct pci_bridge_emul *bridge, break; case PCI_EXP_DEVCTL: - *value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_DEVCTL) & - ~(PCI_EXP_DEVCTL_URRE | PCI_EXP_DEVCTL_FERE | - PCI_EXP_DEVCTL_NFERE | PCI_EXP_DEVCTL_CERE); + *value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_DEVCTL); break; case PCI_EXP_LNKCAP: @@ -647,13 +645,6 @@ mvebu_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge, switch (reg) { case PCI_EXP_DEVCTL: - /* - * Armada370 data says these bits must always - * be zero when in root complex mode. - */ - new &= ~(PCI_EXP_DEVCTL_URRE | PCI_EXP_DEVCTL_FERE | - PCI_EXP_DEVCTL_NFERE | PCI_EXP_DEVCTL_CERE); - mvebu_writel(port, new, PCIE_CAP_PCIEXP + PCI_EXP_DEVCTL); break; -- cgit From 838ff44a398ff47fe9b924961d91aee325821220 Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Thu, 25 Nov 2021 13:46:04 +0100 Subject: PCI: mvebu: Fix support for PCI_EXP_RTSTA on emulated bridge MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PME Status bit in Root Status Register (PCIE_RC_RTSTA_OFF) is read-only and can be cleared only by writing 0b to the Interrupt Cause RW0C register (PCIE_INT_CAUSE_OFF). Link: https://lore.kernel.org/r/20211125124605.25915-15-pali@kernel.org Fixes: 1f08673eef12 ("PCI: mvebu: Convert to PCI emulated bridge config space") Signed-off-by: Pali Rohár Signed-off-by: Lorenzo Pieralisi --- drivers/pci/controller/pci-mvebu.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c index a863b26d44f4..bc350bd0a3e8 100644 --- a/drivers/pci/controller/pci-mvebu.c +++ b/drivers/pci/controller/pci-mvebu.c @@ -51,6 +51,8 @@ PCIE_CONF_FUNC(PCI_FUNC(devfn)) | PCIE_CONF_REG(where) | \ PCIE_CONF_ADDR_EN) #define PCIE_CONF_DATA_OFF 0x18fc +#define PCIE_INT_CAUSE_OFF 0x1900 +#define PCIE_INT_PM_PME BIT(28) #define PCIE_MASK_OFF 0x1910 #define PCIE_MASK_ENABLE_INTS 0x0f000000 #define PCIE_CTRL_OFF 0x1a00 @@ -661,7 +663,14 @@ mvebu_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge, break; case PCI_EXP_RTSTA: - mvebu_writel(port, new, PCIE_RC_RTSTA); + /* + * PME Status bit in Root Status Register (PCIE_RC_RTSTA) + * is read-only and can be cleared only by writing 0b to the + * Interrupt Cause RW0C register (PCIE_INT_CAUSE_OFF). So + * clear PME via Interrupt Cause. + */ + if (new & PCI_EXP_RTSTA_PME) + mvebu_writel(port, ~PCIE_INT_PM_PME, PCIE_INT_CAUSE_OFF); break; } } -- cgit From 4ab34548c55fbbb3898306a47dfaccd4860e1ccb Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Thu, 25 Nov 2021 13:46:05 +0100 Subject: PCI: mvebu: Fix support for DEVCAP2, DEVCTL2 and LNKCTL2 registers on emulated bridge MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Armada XP and new hardware supports access to DEVCAP2, DEVCTL2 and LNKCTL2 configuration registers of PCIe core via PCIE_CAP_PCIEXP. So export them via emulated software root bridge. Pre-XP hardware does not support these registers and returns zeros. Link: https://lore.kernel.org/r/20211125124605.25915-16-pali@kernel.org Fixes: 1f08673eef12 ("PCI: mvebu: Convert to PCI emulated bridge config space") Signed-off-by: Pali Rohár Signed-off-by: Lorenzo Pieralisi --- drivers/pci/controller/pci-mvebu.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c index bc350bd0a3e8..b859952a9c67 100644 --- a/drivers/pci/controller/pci-mvebu.c +++ b/drivers/pci/controller/pci-mvebu.c @@ -560,6 +560,18 @@ mvebu_pci_bridge_emul_pcie_conf_read(struct pci_bridge_emul *bridge, *value = mvebu_readl(port, PCIE_RC_RTSTA); break; + case PCI_EXP_DEVCAP2: + *value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_DEVCAP2); + break; + + case PCI_EXP_DEVCTL2: + *value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_DEVCTL2); + break; + + case PCI_EXP_LNKCTL2: + *value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_LNKCTL2); + break; + default: return PCI_BRIDGE_EMUL_NOT_HANDLED; } @@ -672,6 +684,17 @@ mvebu_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge, if (new & PCI_EXP_RTSTA_PME) mvebu_writel(port, ~PCIE_INT_PM_PME, PCIE_INT_CAUSE_OFF); break; + + case PCI_EXP_DEVCTL2: + mvebu_writel(port, new, PCIE_CAP_PCIEXP + PCI_EXP_DEVCTL2); + break; + + case PCI_EXP_LNKCTL2: + mvebu_writel(port, new, PCIE_CAP_PCIEXP + PCI_EXP_LNKCTL2); + break; + + default: + break; } } -- cgit From 859186e238ffb7ca980dbb7af9abd799ceedbcb3 Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Fri, 26 Nov 2021 15:43:06 +0100 Subject: bus: mvebu-mbus: Export symbols for public API window functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This would allow to compile pci-mvebu.c driver as module. Link: https://lore.kernel.org/r/20211126144307.7568-2-pali@kernel.org Signed-off-by: Pali Rohár Signed-off-by: Lorenzo Pieralisi --- drivers/bus/mvebu-mbus.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/bus/mvebu-mbus.c b/drivers/bus/mvebu-mbus.c index ea0424922de7..db612045616f 100644 --- a/drivers/bus/mvebu-mbus.c +++ b/drivers/bus/mvebu-mbus.c @@ -914,6 +914,7 @@ int mvebu_mbus_add_window_remap_by_id(unsigned int target, return mvebu_mbus_alloc_window(s, base, size, remap, target, attribute); } +EXPORT_SYMBOL_GPL(mvebu_mbus_add_window_remap_by_id); int mvebu_mbus_add_window_by_id(unsigned int target, unsigned int attribute, phys_addr_t base, size_t size) @@ -921,6 +922,7 @@ int mvebu_mbus_add_window_by_id(unsigned int target, unsigned int attribute, return mvebu_mbus_add_window_remap_by_id(target, attribute, base, size, MVEBU_MBUS_NO_REMAP); } +EXPORT_SYMBOL_GPL(mvebu_mbus_add_window_by_id); int mvebu_mbus_del_window(phys_addr_t base, size_t size) { @@ -933,6 +935,7 @@ int mvebu_mbus_del_window(phys_addr_t base, size_t size) mvebu_mbus_disable_window(&mbus_state, win); return 0; } +EXPORT_SYMBOL_GPL(mvebu_mbus_del_window); void mvebu_mbus_get_pcie_mem_aperture(struct resource *res) { @@ -940,6 +943,7 @@ void mvebu_mbus_get_pcie_mem_aperture(struct resource *res) return; *res = mbus_state.pcie_mem_aperture; } +EXPORT_SYMBOL_GPL(mvebu_mbus_get_pcie_mem_aperture); void mvebu_mbus_get_pcie_io_aperture(struct resource *res) { @@ -947,6 +951,7 @@ void mvebu_mbus_get_pcie_io_aperture(struct resource *res) return; *res = mbus_state.pcie_io_aperture; } +EXPORT_SYMBOL_GPL(mvebu_mbus_get_pcie_io_aperture); int mvebu_mbus_get_dram_win_info(phys_addr_t phyaddr, u8 *target, u8 *attr) { -- cgit From 0746ae1be12177ebda0666eefa82583cbaeeefd6 Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Fri, 26 Nov 2021 15:43:07 +0100 Subject: PCI: mvebu: Add support for compiling driver as module MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now when driver uses devm_pci_remap_iospace() function, it is possible implement ->remove() callback for unbinding device from driver. Implement mvebu_pcie_remove() callback with proper cleanup phase, drop driver's suppress_bind_attrs flag and switch type of CONFIG_PCI_MVEBU option from bool to tristate. This allows to compile pci-mvebu.c driver as loadable module pci-mvebu.ko with ability to unload it. Link: https://lore.kernel.org/r/20211126144307.7568-3-pali@kernel.org Signed-off-by: Pali Rohár Signed-off-by: Lorenzo Pieralisi --- drivers/pci/controller/Kconfig | 2 +- drivers/pci/controller/pci-mvebu.c | 91 +++++++++++++++++++++++++++++++------- 2 files changed, 77 insertions(+), 16 deletions(-) diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig index 93b141110537..67189bcd5d89 100644 --- a/drivers/pci/controller/Kconfig +++ b/drivers/pci/controller/Kconfig @@ -4,7 +4,7 @@ menu "PCI controller drivers" depends on PCI config PCI_MVEBU - bool "Marvell EBU PCIe controller" + tristate "Marvell EBU PCIe controller" depends on ARCH_MVEBU || ARCH_DOVE || COMPILE_TEST depends on MVEBU_MBUS depends on ARM diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c index b859952a9c67..d7de48c10bda 100644 --- a/drivers/pci/controller/pci-mvebu.c +++ b/drivers/pci/controller/pci-mvebu.c @@ -6,6 +6,7 @@ */ #include +#include #include #include #include @@ -154,22 +155,13 @@ static void mvebu_pcie_set_local_dev_nr(struct mvebu_pcie_port *port, int nr) mvebu_writel(port, stat, PCIE_STAT_OFF); } -/* - * Setup PCIE BARs and Address Decode Wins: - * BAR[0] -> internal registers (needed for MSI) - * BAR[1] -> covers all DRAM banks - * BAR[2] -> Disabled - * WIN[0-3] -> DRAM bank[0-3] - */ -static void mvebu_pcie_setup_wins(struct mvebu_pcie_port *port) +static void mvebu_pcie_disable_wins(struct mvebu_pcie_port *port) { - const struct mbus_dram_target_info *dram; - u32 size; int i; - dram = mv_mbus_dram_info(); + mvebu_writel(port, 0, PCIE_BAR_LO_OFF(0)); + mvebu_writel(port, 0, PCIE_BAR_HI_OFF(0)); - /* First, disable and clear BARs and windows. */ for (i = 1; i < 3; i++) { mvebu_writel(port, 0, PCIE_BAR_CTRL_OFF(i)); mvebu_writel(port, 0, PCIE_BAR_LO_OFF(i)); @@ -185,6 +177,25 @@ static void mvebu_pcie_setup_wins(struct mvebu_pcie_port *port) mvebu_writel(port, 0, PCIE_WIN5_CTRL_OFF); mvebu_writel(port, 0, PCIE_WIN5_BASE_OFF); mvebu_writel(port, 0, PCIE_WIN5_REMAP_OFF); +} + +/* + * Setup PCIE BARs and Address Decode Wins: + * BAR[0] -> internal registers (needed for MSI) + * BAR[1] -> covers all DRAM banks + * BAR[2] -> Disabled + * WIN[0-3] -> DRAM bank[0-3] + */ +static void mvebu_pcie_setup_wins(struct mvebu_pcie_port *port) +{ + const struct mbus_dram_target_info *dram; + u32 size; + int i; + + dram = mv_mbus_dram_info(); + + /* First, disable and clear BARs and windows. */ + mvebu_pcie_disable_wins(port); /* Setup windows for DDR banks. Count total DDR size on the fly. */ size = 0; @@ -1327,6 +1338,52 @@ static int mvebu_pcie_probe(struct platform_device *pdev) return pci_host_probe(bridge); } +static int mvebu_pcie_remove(struct platform_device *pdev) +{ + struct mvebu_pcie *pcie = platform_get_drvdata(pdev); + struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie); + u32 cmd; + int i; + + /* Remove PCI bus with all devices. */ + pci_lock_rescan_remove(); + pci_stop_root_bus(bridge->bus); + pci_remove_root_bus(bridge->bus); + pci_unlock_rescan_remove(); + + for (i = 0; i < pcie->nports; i++) { + struct mvebu_pcie_port *port = &pcie->ports[i]; + + if (!port->base) + continue; + + /* Disable Root Bridge I/O space, memory space and bus mastering. */ + cmd = mvebu_readl(port, PCIE_CMD_OFF); + cmd &= ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); + mvebu_writel(port, cmd, PCIE_CMD_OFF); + + /* Mask all interrupt sources. */ + mvebu_writel(port, 0, PCIE_MASK_OFF); + + /* Free config space for emulated root bridge. */ + pci_bridge_emul_cleanup(&port->bridge); + + /* Disable and clear BARs and windows. */ + mvebu_pcie_disable_wins(port); + + /* Delete PCIe IO and MEM windows. */ + if (port->iowin.size) + mvebu_pcie_del_windows(port, port->iowin.base, port->iowin.size); + if (port->memwin.size) + mvebu_pcie_del_windows(port, port->memwin.base, port->memwin.size); + + /* Power down card and disable clocks. Must be the last step. */ + mvebu_pcie_powerdown(port); + } + + return 0; +} + static const struct of_device_id mvebu_pcie_of_match_table[] = { { .compatible = "marvell,armada-xp-pcie", }, { .compatible = "marvell,armada-370-pcie", }, @@ -1343,10 +1400,14 @@ static struct platform_driver mvebu_pcie_driver = { .driver = { .name = "mvebu-pcie", .of_match_table = mvebu_pcie_of_match_table, - /* driver unloading/unbinding currently not supported */ - .suppress_bind_attrs = true, .pm = &mvebu_pcie_pm_ops, }, .probe = mvebu_pcie_probe, + .remove = mvebu_pcie_remove, }; -builtin_platform_driver(mvebu_pcie_driver); +module_platform_driver(mvebu_pcie_driver); + +MODULE_AUTHOR("Thomas Petazzoni "); +MODULE_AUTHOR("Pali Rohár "); +MODULE_DESCRIPTION("Marvell EBU PCIe controller"); +MODULE_LICENSE("GPL v2"); -- cgit