From e621e106a139c1f9309aff383f92a94fce59352f Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Tue, 4 Jan 2022 16:35:19 +0100 Subject: MAINTAINERS: Add Pali Rohár as pci-mvebu.c maintainer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Link: https://lore.kernel.org/r/20220104153529.31647-2-pali@kernel.org Signed-off-by: Pali Rohár Signed-off-by: Lorenzo Pieralisi Reviewed-by: Rob Herring Acked-by: Thomas Petazzoni --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index ea3e6c914384..4e1a698599f1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -14784,6 +14784,7 @@ F: drivers/pci/controller/mobiveil/pcie-mobiveil* PCI DRIVER FOR MVEBU (Marvell Armada 370 and Armada XP SOC support) M: Thomas Petazzoni +M: Pali Rohár L: linux-pci@vger.kernel.org L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained -- cgit v1.2.3 From 64a70f521e1355d44280abaaf04aacdf8cd77723 Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Tue, 4 Jan 2022 16:35:20 +0100 Subject: PCI: pci-bridge-emul: Make struct pci_bridge_emul_ops as const MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It is read-only constant structure, so properly mark it with const keyword. Link: https://lore.kernel.org/r/20220104153529.31647-3-pali@kernel.org Signed-off-by: Pali Rohár Signed-off-by: Lorenzo Pieralisi Reviewed-by: Rob Herring Acked-by: Thomas Petazzoni --- drivers/pci/controller/pci-aardvark.c | 2 +- drivers/pci/controller/pci-mvebu.c | 2 +- drivers/pci/pci-bridge-emul.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c index 4f5b44827d21..87c36e12c246 100644 --- a/drivers/pci/controller/pci-aardvark.c +++ b/drivers/pci/controller/pci-aardvark.c @@ -928,7 +928,7 @@ advk_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge, } } -static struct pci_bridge_emul_ops advk_pci_bridge_emul_ops = { +static const struct pci_bridge_emul_ops advk_pci_bridge_emul_ops = { .read_base = advk_pci_bridge_emul_base_conf_read, .write_base = advk_pci_bridge_emul_base_conf_write, .read_pcie = advk_pci_bridge_emul_pcie_conf_read, diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c index 71258ea3d35f..a62b80ffd675 100644 --- a/drivers/pci/controller/pci-mvebu.c +++ b/drivers/pci/controller/pci-mvebu.c @@ -709,7 +709,7 @@ mvebu_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge, } } -static struct pci_bridge_emul_ops mvebu_pci_bridge_emul_ops = { +static const 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, diff --git a/drivers/pci/pci-bridge-emul.h b/drivers/pci/pci-bridge-emul.h index 49bbd37ee318..0690b6369755 100644 --- a/drivers/pci/pci-bridge-emul.h +++ b/drivers/pci/pci-bridge-emul.h @@ -112,7 +112,7 @@ struct pci_bridge_reg_behavior; struct pci_bridge_emul { struct pci_bridge_emul_conf conf; struct pci_bridge_emul_pcie_conf pcie_conf; - struct pci_bridge_emul_ops *ops; + const struct pci_bridge_emul_ops *ops; struct pci_bridge_reg_behavior *pci_regs_behavior; struct pci_bridge_reg_behavior *pcie_cap_regs_behavior; void *data; -- cgit v1.2.3 From d3f332b568dcf876f1d15f0abecececc1fe14987 Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Tue, 4 Jan 2022 16:35:21 +0100 Subject: PCI: pci-bridge-emul: Rename PCI_BRIDGE_EMUL_NO_PREFETCHABLE_BAR to PCI_BRIDGE_EMUL_NO_PREFMEM_FORWARD MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This flag describe whether PCI bridge supports forwarding of prefetchable memory requests in given range between primary and secondary buses. It does not specify if bridge has support for prefetchable memory BAR (moreover this pci-bridge-emul.c driver does not provide support for BARs). So change name of this flag to be less misleading and add comment. Link: https://lore.kernel.org/r/20220104153529.31647-4-pali@kernel.org Signed-off-by: Pali Rohár Signed-off-by: Lorenzo Pieralisi Reviewed-by: Rob Herring --- drivers/pci/controller/pci-mvebu.c | 2 +- drivers/pci/pci-bridge-emul.c | 2 +- drivers/pci/pci-bridge-emul.h | 6 +++++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c index a62b80ffd675..7156ddcfb1e0 100644 --- a/drivers/pci/controller/pci-mvebu.c +++ b/drivers/pci/controller/pci-mvebu.c @@ -747,7 +747,7 @@ static int mvebu_pci_bridge_emul_init(struct mvebu_pcie_port *port) bridge->data = port; bridge->ops = &mvebu_pci_bridge_emul_ops; - return pci_bridge_emul_init(bridge, PCI_BRIDGE_EMUL_NO_PREFETCHABLE_BAR); + return pci_bridge_emul_init(bridge, PCI_BRIDGE_EMUL_NO_PREFMEM_FORWARD); } static inline struct mvebu_pcie *sys_to_pcie(struct pci_sys_data *sys) diff --git a/drivers/pci/pci-bridge-emul.c b/drivers/pci/pci-bridge-emul.c index c994ebec2360..ceacba03e714 100644 --- a/drivers/pci/pci-bridge-emul.c +++ b/drivers/pci/pci-bridge-emul.c @@ -377,7 +377,7 @@ int pci_bridge_emul_init(struct pci_bridge_emul *bridge, ~(BIT(10) << 16); } - if (flags & PCI_BRIDGE_EMUL_NO_PREFETCHABLE_BAR) { + if (flags & PCI_BRIDGE_EMUL_NO_PREFMEM_FORWARD) { bridge->pci_regs_behavior[PCI_PREF_MEMORY_BASE / 4].ro = ~0; bridge->pci_regs_behavior[PCI_PREF_MEMORY_BASE / 4].rw = 0; } diff --git a/drivers/pci/pci-bridge-emul.h b/drivers/pci/pci-bridge-emul.h index 0690b6369755..087b5fa01bcf 100644 --- a/drivers/pci/pci-bridge-emul.h +++ b/drivers/pci/pci-bridge-emul.h @@ -120,7 +120,11 @@ struct pci_bridge_emul { }; enum { - PCI_BRIDGE_EMUL_NO_PREFETCHABLE_BAR = BIT(0), + /* + * PCI bridge does not support forwarding of prefetchable memory + * requests between primary and secondary buses. + */ + PCI_BRIDGE_EMUL_NO_PREFMEM_FORWARD = BIT(0), }; int pci_bridge_emul_init(struct pci_bridge_emul *bridge, -- cgit v1.2.3 From 05241c13655c43116ea7322a6edffc47d7218550 Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Tue, 4 Jan 2022 16:35:22 +0100 Subject: PCI: pci-bridge-emul: Add support for new flag PCI_BRIDGE_EMUL_NO_IO_FORWARD MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Like PCI_BRIDGE_EMUL_NO_PREFMEM_FORWARD, this new flag specifies that emulated PCI bridge does not support forwarding of IO requests in given range between primary and secondary buses. This flag should be used as argument for pci_bridge_emul_init() for hardware setup without IO support. Setting this flag cause that IO base and limit registers are read-only. Link: https://lore.kernel.org/r/20220104153529.31647-5-pali@kernel.org Signed-off-by: Pali Rohár Signed-off-by: Lorenzo Pieralisi Reviewed-by: Rob Herring --- drivers/pci/pci-bridge-emul.c | 9 +++++++++ drivers/pci/pci-bridge-emul.h | 6 ++++++ 2 files changed, 15 insertions(+) diff --git a/drivers/pci/pci-bridge-emul.c b/drivers/pci/pci-bridge-emul.c index ceacba03e714..a16f9e30099e 100644 --- a/drivers/pci/pci-bridge-emul.c +++ b/drivers/pci/pci-bridge-emul.c @@ -382,6 +382,15 @@ int pci_bridge_emul_init(struct pci_bridge_emul *bridge, bridge->pci_regs_behavior[PCI_PREF_MEMORY_BASE / 4].rw = 0; } + if (flags & PCI_BRIDGE_EMUL_NO_IO_FORWARD) { + bridge->pci_regs_behavior[PCI_COMMAND / 4].ro |= PCI_COMMAND_IO; + bridge->pci_regs_behavior[PCI_COMMAND / 4].rw &= ~PCI_COMMAND_IO; + bridge->pci_regs_behavior[PCI_IO_BASE / 4].ro |= GENMASK(15, 0); + bridge->pci_regs_behavior[PCI_IO_BASE / 4].rw &= ~GENMASK(15, 0); + bridge->pci_regs_behavior[PCI_IO_BASE_UPPER16 / 4].ro = ~0; + bridge->pci_regs_behavior[PCI_IO_BASE_UPPER16 / 4].rw = 0; + } + return 0; } EXPORT_SYMBOL_GPL(pci_bridge_emul_init); diff --git a/drivers/pci/pci-bridge-emul.h b/drivers/pci/pci-bridge-emul.h index 087b5fa01bcf..4953274cac18 100644 --- a/drivers/pci/pci-bridge-emul.h +++ b/drivers/pci/pci-bridge-emul.h @@ -125,6 +125,12 @@ enum { * requests between primary and secondary buses. */ PCI_BRIDGE_EMUL_NO_PREFMEM_FORWARD = BIT(0), + + /* + * PCI bridge does not support forwarding of IO requests between + * primary and secondary buses. + */ + PCI_BRIDGE_EMUL_NO_IO_FORWARD = BIT(1), }; int pci_bridge_emul_init(struct pci_bridge_emul *bridge, -- cgit v1.2.3 From 7f09a4b195a94cda747ff87b56834907844b326c Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Tue, 4 Jan 2022 16:35:23 +0100 Subject: PCI: mvebu: Add help string for CONFIG_PCI_MVEBU option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is no description for CONFIG_PCI_MVEBU option. Add it. Link: https://lore.kernel.org/r/20220104153529.31647-6-pali@kernel.org Signed-off-by: Pali Rohár Signed-off-by: Lorenzo Pieralisi Reviewed-by: Rob Herring Acked-by: Thomas Petazzoni --- drivers/pci/controller/Kconfig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig index 601f2531ee91..b8d96d38064d 100644 --- a/drivers/pci/controller/Kconfig +++ b/drivers/pci/controller/Kconfig @@ -10,6 +10,10 @@ config PCI_MVEBU depends on ARM depends on OF select PCI_BRIDGE_EMUL + help + Add support for Marvell EBU PCIe controller. This PCIe controller + is used on 32-bit Marvell ARM SoCs: Dove, Kirkwood, Armada 370, + Armada XP, Armada 375, Armada 38x and Armada 39x. config PCI_AARDVARK tristate "Aardvark PCIe controller" -- cgit v1.2.3 From 7a02acdb5debff8cbee4a91efbba084f7b26a680 Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Tue, 4 Jan 2022 16:35:24 +0100 Subject: PCI: mvebu: Remove duplicate nports assignment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Member pcie->nports is initialized to correct value before the previous for-loop. There is not need to initialize it more times. Link: https://lore.kernel.org/r/20220104153529.31647-7-pali@kernel.org Signed-off-by: Pali Rohár Signed-off-by: Lorenzo Pieralisi Reviewed-by: Rob Herring Acked-by: Thomas Petazzoni --- drivers/pci/controller/pci-mvebu.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c index 7156ddcfb1e0..a7eaa58483dd 100644 --- a/drivers/pci/controller/pci-mvebu.c +++ b/drivers/pci/controller/pci-mvebu.c @@ -1332,8 +1332,6 @@ static int mvebu_pcie_probe(struct platform_device *pdev) mvebu_pcie_set_local_dev_nr(port, 0); } - pcie->nports = i; - bridge->sysdata = pcie; bridge->ops = &mvebu_pcie_ops; bridge->align_resource = mvebu_pcie_align_resource; -- cgit v1.2.3 From 5c88ed798522c59c7656c1e5704ca0062f4d92dc Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Tue, 4 Jan 2022 16:35:25 +0100 Subject: PCI: mvebu: Set PCI_BRIDGE_EMUL_NO_IO_FORWARD when IO is unsupported MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This will make PCI bridge to return zeros when accessing IO base and limit registers, as required by PCIe base specification. This allows to remove adhoc checks around mvebu_pcie_handle_iobase_change() function for unsupported IO ranges. PCI_BRIDGE_EMUL_NO_IO_FORWARD ensures that there will be no non-zeros write to IO registers when IO is not supported. Link: https://lore.kernel.org/r/20220104153529.31647-8-pali@kernel.org Signed-off-by: Pali Rohár Signed-off-by: Lorenzo Pieralisi Reviewed-by: Rob Herring --- drivers/pci/controller/pci-mvebu.c | 29 ++++++++++------------------- 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c index a7eaa58483dd..806cfcdb6b44 100644 --- a/drivers/pci/controller/pci-mvebu.c +++ b/drivers/pci/controller/pci-mvebu.c @@ -438,12 +438,6 @@ static int mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port) 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 -EOPNOTSUPP; - } - /* * We read the PCI-to-PCI bridge emulated registers, and * calculate the base address and size of the address decoding @@ -599,24 +593,18 @@ mvebu_pci_bridge_emul_base_conf_write(struct pci_bridge_emul *bridge, switch (reg) { case PCI_COMMAND: - 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: - if ((mask & 0xffff) && mvebu_pcie_handle_iobase_change(port)) { + if ((mask & 0xffff) && mvebu_has_ioport(port) && + mvebu_pcie_handle_iobase_change(port)) { /* On error disable IO range */ conf->iobase &= ~0xf0; conf->iolimit &= ~0xf0; + conf->iobase |= 0xf0; conf->iobaseupper = cpu_to_le16(0x0000); conf->iolimitupper = cpu_to_le16(0x0000); - if (mvebu_has_ioport(port)) - conf->iobase |= 0xf0; } break; @@ -630,14 +618,14 @@ mvebu_pci_bridge_emul_base_conf_write(struct pci_bridge_emul *bridge, break; case PCI_IO_BASE_UPPER16: - if (mvebu_pcie_handle_iobase_change(port)) { + if (mvebu_has_ioport(port) && + mvebu_pcie_handle_iobase_change(port)) { /* On error disable IO range */ conf->iobase &= ~0xf0; conf->iolimit &= ~0xf0; + conf->iobase |= 0xf0; conf->iobaseupper = cpu_to_le16(0x0000); conf->iolimitupper = cpu_to_le16(0x0000); - if (mvebu_has_ioport(port)) - conf->iobase |= 0xf0; } break; @@ -722,6 +710,7 @@ static const struct pci_bridge_emul_ops mvebu_pci_bridge_emul_ops = { */ static int mvebu_pci_bridge_emul_init(struct mvebu_pcie_port *port) { + unsigned int bridge_flags = PCI_BRIDGE_EMUL_NO_PREFMEM_FORWARD; struct pci_bridge_emul *bridge = &port->bridge; u32 pcie_cap = mvebu_readl(port, PCIE_CAP_PCIEXP); u8 pcie_cap_ver = ((pcie_cap >> 16) & PCI_EXP_FLAGS_VERS); @@ -735,6 +724,8 @@ static int mvebu_pci_bridge_emul_init(struct mvebu_pcie_port *port) /* We support 32 bits I/O addressing */ bridge->conf.iobase = PCI_IO_RANGE_TYPE_32; bridge->conf.iolimit = PCI_IO_RANGE_TYPE_32; + } else { + bridge_flags |= PCI_BRIDGE_EMUL_NO_IO_FORWARD; } /* @@ -747,7 +738,7 @@ static int mvebu_pci_bridge_emul_init(struct mvebu_pcie_port *port) bridge->data = port; bridge->ops = &mvebu_pci_bridge_emul_ops; - return pci_bridge_emul_init(bridge, PCI_BRIDGE_EMUL_NO_PREFMEM_FORWARD); + return pci_bridge_emul_init(bridge, bridge_flags); } static inline struct mvebu_pcie *sys_to_pcie(struct pci_sys_data *sys) -- cgit v1.2.3 From 16038ebb0f9f7daa685a439b4ee973c9dbd1416f Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Tue, 4 Jan 2022 16:35:26 +0100 Subject: PCI: mvebu: Properly initialize vendor, device and revision of emulated bridge MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With this change also PCI vendor id is read from mvebu registers. Link: https://lore.kernel.org/r/20220104153529.31647-9-pali@kernel.org Signed-off-by: Pali Rohár Signed-off-by: Lorenzo Pieralisi Reviewed-by: Rob Herring --- drivers/pci/controller/pci-mvebu.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c index 806cfcdb6b44..1a03fb353084 100644 --- a/drivers/pci/controller/pci-mvebu.c +++ b/drivers/pci/controller/pci-mvebu.c @@ -712,13 +712,14 @@ static int mvebu_pci_bridge_emul_init(struct mvebu_pcie_port *port) { unsigned int bridge_flags = PCI_BRIDGE_EMUL_NO_PREFMEM_FORWARD; struct pci_bridge_emul *bridge = &port->bridge; + u32 dev_id = mvebu_readl(port, PCIE_DEV_ID_OFF); + u32 dev_rev = mvebu_readl(port, PCIE_DEV_REV_OFF); u32 pcie_cap = mvebu_readl(port, PCIE_CAP_PCIEXP); u8 pcie_cap_ver = ((pcie_cap >> 16) & PCI_EXP_FLAGS_VERS); - bridge->conf.vendor = PCI_VENDOR_ID_MARVELL; - bridge->conf.device = mvebu_readl(port, PCIE_DEV_ID_OFF) >> 16; - bridge->conf.class_revision = - mvebu_readl(port, PCIE_DEV_REV_OFF) & 0xff; + bridge->conf.vendor = cpu_to_le16(dev_id & 0xffff); + bridge->conf.device = cpu_to_le16(dev_id >> 16); + bridge->conf.class_revision = cpu_to_le32(dev_rev & 0xff); if (mvebu_has_ioport(port)) { /* We support 32 bits I/O addressing */ -- cgit v1.2.3 From d76a6ed0964af14327068408da095a2355a5b8dc Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Tue, 4 Jan 2022 16:35:27 +0100 Subject: PCI: mvebu: Update comment for PCI_EXP_LNKCAP register on emulated bridge MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reason for clearing this bit is because mvebu hw returns incorrectly this bit set to 1. Link: https://lore.kernel.org/r/20220104153529.31647-10-pali@kernel.org Signed-off-by: Pali Rohár Signed-off-by: Lorenzo Pieralisi Reviewed-by: Rob Herring --- drivers/pci/controller/pci-mvebu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c index 1a03fb353084..216da126a06d 100644 --- a/drivers/pci/controller/pci-mvebu.c +++ b/drivers/pci/controller/pci-mvebu.c @@ -546,8 +546,8 @@ mvebu_pci_bridge_emul_pcie_conf_read(struct pci_bridge_emul *bridge, case PCI_EXP_LNKCAP: /* - * PCIe requires the clock power management capability to be - * hard-wired to zero for downstream ports + * PCIe requires that the Clock Power Management capability bit + * is hard-wired to zero for downstream ports but HW returns 1. */ *value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_LNKCAP) & ~PCI_EXP_LNKCAP_CLKPM; -- cgit v1.2.3 From c94ea32c0d3db6ce7ca4fe771051b5f8818b77f0 Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Tue, 4 Jan 2022 16:35:28 +0100 Subject: PCI: mvebu: Update comment for PCI_EXP_LNKCTL register on emulated bridge MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Logic and code for clearing PCI_EXP_LNKCTL_CLKREQ_EN bit is correct, but comment describing it is misleading. PCI_EXP_LNKCTL_CLKREQ_EN bit should be hardwired to zero but mvebu hw allows to change it. Link: https://lore.kernel.org/r/20220104153529.31647-11-pali@kernel.org Signed-off-by: Pali Rohár Signed-off-by: Lorenzo Pieralisi Reviewed-by: Rob Herring --- drivers/pci/controller/pci-mvebu.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c index 216da126a06d..48098deb9b7b 100644 --- a/drivers/pci/controller/pci-mvebu.c +++ b/drivers/pci/controller/pci-mvebu.c @@ -663,10 +663,9 @@ mvebu_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge, case PCI_EXP_LNKCTL: /* - * If we don't support CLKREQ, we must ensure that the - * CLKREQ enable bit always reads zero. Since we haven't - * had this capability, and it's dependent on board wiring, - * disable it for the time being. + * PCIe requires that the Enable Clock Power Management bit + * is hard-wired to zero for downstream ports but HW allows + * to change it. */ new &= ~PCI_EXP_LNKCTL_CLKREQ_EN; -- cgit v1.2.3 From c3bd7dc553eea5a3595ca3aa0adee9bf83622a1f Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Tue, 4 Jan 2022 16:35:29 +0100 Subject: PCI: mvebu: Fix reporting Data Link Layer Link Active on emulated bridge MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for reporting PCI_EXP_LNKSTA_DLLLA bit in Link Control register on emulated bridge via PCIE_STAT_OFF reg. Function mvebu_pcie_link_up() already parses this register and returns if Data Link is Active or not. Also correctly indicate DLLLA capability via PCI_EXP_LNKCAP_DLLLARC bit in Link Control Capability register which is required for reporting DLLLA bit. Link: https://lore.kernel.org/r/20220104153529.31647-12-pali@kernel.org Signed-off-by: Pali Rohár Signed-off-by: Lorenzo Pieralisi Reviewed-by: Rob Herring --- drivers/pci/controller/pci-mvebu.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c index 48098deb9b7b..357f0f41f68e 100644 --- a/drivers/pci/controller/pci-mvebu.c +++ b/drivers/pci/controller/pci-mvebu.c @@ -548,13 +548,18 @@ mvebu_pci_bridge_emul_pcie_conf_read(struct pci_bridge_emul *bridge, /* * PCIe requires that the Clock Power Management capability bit * is hard-wired to zero for downstream ports but HW returns 1. + * Additionally enable Data Link Layer Link Active Reporting + * Capable bit as DL_Active indication is provided too. */ - *value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_LNKCAP) & - ~PCI_EXP_LNKCAP_CLKPM; + *value = (mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_LNKCAP) & + ~PCI_EXP_LNKCAP_CLKPM) | PCI_EXP_LNKCAP_DLLLARC; break; case PCI_EXP_LNKCTL: - *value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_LNKCTL); + /* DL_Active indication is provided via PCIE_STAT_OFF */ + *value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_LNKCTL) | + (mvebu_pcie_link_up(port) ? + (PCI_EXP_LNKSTA_DLLLA << 16) : 0); break; case PCI_EXP_SLTCTL: -- cgit v1.2.3 From c453bf6f9b3279dc483878fb4f81944844ca75ae Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 22 Feb 2022 16:50:19 +0100 Subject: PCI: pci-bridge-emul: Re-arrange register tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Re-arrange the tests for which sets of registers are being accessed so that it is easier to add further regions later. No functional change. [pali: Fix reading old value in pci_bridge_emul_conf_write] Link: https://lore.kernel.org/r/20220222155030.988-2-pali@kernel.org Signed-off-by: Russell King Signed-off-by: Pali Rohár Signed-off-by: Marek Behún Signed-off-by: Lorenzo Pieralisi --- drivers/pci/pci-bridge-emul.c | 61 ++++++++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 30 deletions(-) diff --git a/drivers/pci/pci-bridge-emul.c b/drivers/pci/pci-bridge-emul.c index a16f9e30099e..a956408834d6 100644 --- a/drivers/pci/pci-bridge-emul.c +++ b/drivers/pci/pci-bridge-emul.c @@ -422,25 +422,25 @@ int pci_bridge_emul_conf_read(struct pci_bridge_emul *bridge, int where, __le32 *cfgspace; const struct pci_bridge_reg_behavior *behavior; - if (bridge->has_pcie && reg >= PCI_CAP_PCIE_END) { - *value = 0; - return PCIBIOS_SUCCESSFUL; - } - - if (!bridge->has_pcie && reg >= PCI_BRIDGE_CONF_END) { + if (reg < PCI_BRIDGE_CONF_END) { + /* Emulated PCI space */ + read_op = bridge->ops->read_base; + cfgspace = (__le32 *) &bridge->conf; + behavior = bridge->pci_regs_behavior; + } else if (!bridge->has_pcie) { + /* PCIe space is not implemented, and no PCI capabilities */ *value = 0; return PCIBIOS_SUCCESSFUL; - } - - if (bridge->has_pcie && reg >= PCI_CAP_PCIE_START) { + } else if (reg < PCI_CAP_PCIE_END) { + /* Our emulated PCIe capability */ reg -= PCI_CAP_PCIE_START; read_op = bridge->ops->read_pcie; cfgspace = (__le32 *) &bridge->pcie_conf; behavior = bridge->pcie_cap_regs_behavior; } else { - read_op = bridge->ops->read_base; - cfgspace = (__le32 *) &bridge->conf; - behavior = bridge->pci_regs_behavior; + /* Beyond our PCIe space */ + *value = 0; + return PCIBIOS_SUCCESSFUL; } if (read_op) @@ -484,11 +484,27 @@ int pci_bridge_emul_conf_write(struct pci_bridge_emul *bridge, int where, __le32 *cfgspace; const struct pci_bridge_reg_behavior *behavior; - if (bridge->has_pcie && reg >= PCI_CAP_PCIE_END) - return PCIBIOS_SUCCESSFUL; + ret = pci_bridge_emul_conf_read(bridge, reg, 4, &old); + if (ret != PCIBIOS_SUCCESSFUL) + return ret; - if (!bridge->has_pcie && reg >= PCI_BRIDGE_CONF_END) + if (reg < PCI_BRIDGE_CONF_END) { + /* Emulated PCI space */ + write_op = bridge->ops->write_base; + cfgspace = (__le32 *) &bridge->conf; + behavior = bridge->pci_regs_behavior; + } else if (!bridge->has_pcie) { + /* PCIe space is not implemented, and no PCI capabilities */ return PCIBIOS_SUCCESSFUL; + } else if (reg < PCI_CAP_PCIE_END) { + /* Our emulated PCIe capability */ + reg -= PCI_CAP_PCIE_START; + write_op = bridge->ops->write_pcie; + cfgspace = (__le32 *) &bridge->pcie_conf; + behavior = bridge->pcie_cap_regs_behavior; + } else { + return PCIBIOS_SUCCESSFUL; + } shift = (where & 0x3) * 8; @@ -501,21 +517,6 @@ int pci_bridge_emul_conf_write(struct pci_bridge_emul *bridge, int where, else return PCIBIOS_BAD_REGISTER_NUMBER; - ret = pci_bridge_emul_conf_read(bridge, reg, 4, &old); - if (ret != PCIBIOS_SUCCESSFUL) - return ret; - - if (bridge->has_pcie && reg >= PCI_CAP_PCIE_START) { - reg -= PCI_CAP_PCIE_START; - write_op = bridge->ops->write_pcie; - cfgspace = (__le32 *) &bridge->pcie_conf; - behavior = bridge->pcie_cap_regs_behavior; - } else { - write_op = bridge->ops->write_base; - cfgspace = (__le32 *) &bridge->conf; - behavior = bridge->pci_regs_behavior; - } - /* Keep all bits, except the RW bits */ new = old & (~mask | ~behavior[reg / 4].rw); -- cgit v1.2.3 From c0bd419732945b38268d56d2e924995fc53c8f7c Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 22 Feb 2022 16:50:20 +0100 Subject: PCI: pci-bridge-emul: Add support for PCIe extended capabilities MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for PCIe extended capabilities, which we just redirect to the emulating driver. [pali: Fix writing new value with W1C bits] Link: https://lore.kernel.org/r/20220222155030.988-3-pali@kernel.org Signed-off-by: Russell King Signed-off-by: Pali Rohár Signed-off-by: Marek Behún Signed-off-by: Lorenzo Pieralisi --- drivers/pci/pci-bridge-emul.c | 77 +++++++++++++++++++++++++++++-------------- drivers/pci/pci-bridge-emul.h | 15 +++++++++ 2 files changed, 67 insertions(+), 25 deletions(-) diff --git a/drivers/pci/pci-bridge-emul.c b/drivers/pci/pci-bridge-emul.c index a956408834d6..c4b9837006ff 100644 --- a/drivers/pci/pci-bridge-emul.c +++ b/drivers/pci/pci-bridge-emul.c @@ -437,10 +437,16 @@ int pci_bridge_emul_conf_read(struct pci_bridge_emul *bridge, int where, read_op = bridge->ops->read_pcie; cfgspace = (__le32 *) &bridge->pcie_conf; behavior = bridge->pcie_cap_regs_behavior; - } else { - /* Beyond our PCIe space */ + } else if (reg < PCI_CFG_SPACE_SIZE) { + /* Rest of PCI space not implemented */ *value = 0; return PCIBIOS_SUCCESSFUL; + } else { + /* PCIe extended capability space */ + reg -= PCI_CFG_SPACE_SIZE; + read_op = bridge->ops->read_ext; + cfgspace = NULL; + behavior = NULL; } if (read_op) @@ -448,15 +454,20 @@ int pci_bridge_emul_conf_read(struct pci_bridge_emul *bridge, int where, else ret = PCI_BRIDGE_EMUL_NOT_HANDLED; - if (ret == PCI_BRIDGE_EMUL_NOT_HANDLED) - *value = le32_to_cpu(cfgspace[reg / 4]); + if (ret == PCI_BRIDGE_EMUL_NOT_HANDLED) { + if (cfgspace) + *value = le32_to_cpu(cfgspace[reg / 4]); + else + *value = 0; + } /* * Make sure we never return any reserved bit with a value * different from 0. */ - *value &= behavior[reg / 4].ro | behavior[reg / 4].rw | - behavior[reg / 4].w1c; + if (behavior) + *value &= behavior[reg / 4].ro | behavior[reg / 4].rw | + behavior[reg / 4].w1c; if (size == 1) *value = (*value >> (8 * (where & 3))) & 0xff; @@ -502,8 +513,15 @@ int pci_bridge_emul_conf_write(struct pci_bridge_emul *bridge, int where, write_op = bridge->ops->write_pcie; cfgspace = (__le32 *) &bridge->pcie_conf; behavior = bridge->pcie_cap_regs_behavior; - } else { + } else if (reg < PCI_CFG_SPACE_SIZE) { + /* Rest of PCI space not implemented */ return PCIBIOS_SUCCESSFUL; + } else { + /* PCIe extended capability space */ + reg -= PCI_CFG_SPACE_SIZE; + write_op = bridge->ops->write_ext; + cfgspace = NULL; + behavior = NULL; } shift = (where & 0x3) * 8; @@ -517,29 +535,38 @@ int pci_bridge_emul_conf_write(struct pci_bridge_emul *bridge, int where, else return PCIBIOS_BAD_REGISTER_NUMBER; - /* Keep all bits, except the RW bits */ - new = old & (~mask | ~behavior[reg / 4].rw); + if (behavior) { + /* Keep all bits, except the RW bits */ + new = old & (~mask | ~behavior[reg / 4].rw); - /* Update the value of the RW bits */ - new |= (value << shift) & (behavior[reg / 4].rw & mask); + /* Update the value of the RW bits */ + new |= (value << shift) & (behavior[reg / 4].rw & mask); - /* Clear the W1C bits */ - new &= ~((value << shift) & (behavior[reg / 4].w1c & mask)); + /* Clear the W1C bits */ + new &= ~((value << shift) & (behavior[reg / 4].w1c & mask)); + } else { + new = old & ~mask; + new |= (value << shift) & mask; + } - /* Save the new value with the cleared W1C bits into the cfgspace */ - cfgspace[reg / 4] = cpu_to_le32(new); + if (cfgspace) { + /* Save the new value with the cleared W1C bits into the cfgspace */ + cfgspace[reg / 4] = cpu_to_le32(new); + } - /* - * Clear the W1C bits not specified by the write mask, so that the - * write_op() does not clear them. - */ - new &= ~(behavior[reg / 4].w1c & ~mask); + if (behavior) { + /* + * Clear the W1C bits not specified by the write mask, so that the + * write_op() does not clear them. + */ + new &= ~(behavior[reg / 4].w1c & ~mask); - /* - * Set the W1C bits specified by the write mask, so that write_op() - * knows about that they are to be cleared. - */ - new |= (value << shift) & (behavior[reg / 4].w1c & mask); + /* + * Set the W1C bits specified by the write mask, so that write_op() + * knows about that they are to be cleared. + */ + new |= (value << shift) & (behavior[reg / 4].w1c & mask); + } if (write_op) write_op(bridge, reg, old, new, mask); diff --git a/drivers/pci/pci-bridge-emul.h b/drivers/pci/pci-bridge-emul.h index 4953274cac18..6b5f75b2ad02 100644 --- a/drivers/pci/pci-bridge-emul.h +++ b/drivers/pci/pci-bridge-emul.h @@ -90,6 +90,14 @@ struct pci_bridge_emul_ops { */ pci_bridge_emul_read_status_t (*read_pcie)(struct pci_bridge_emul *bridge, int reg, u32 *value); + + /* + * Same as ->read_base(), except it is for reading from the + * PCIe extended capability configuration space. + */ + pci_bridge_emul_read_status_t (*read_ext)(struct pci_bridge_emul *bridge, + int reg, u32 *value); + /* * Called when writing to the regular PCI bridge configuration * space. old is the current value, new is the new value being @@ -105,6 +113,13 @@ struct pci_bridge_emul_ops { */ void (*write_pcie)(struct pci_bridge_emul *bridge, int reg, u32 old, u32 new, u32 mask); + + /* + * Same as ->write_base(), except it is for writing from the + * PCIe extended capability configuration space. + */ + void (*write_ext)(struct pci_bridge_emul *bridge, int reg, + u32 old, u32 new, u32 mask); }; struct pci_bridge_reg_behavior; -- cgit v1.2.3 From 3767a9024252095654d4688ddc323b25404c00fc Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Tue, 22 Feb 2022 16:50:21 +0100 Subject: PCI: pci-bridge-emul: Add support for PCI Bridge Subsystem Vendor ID capability MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is read-only capability in PCI config space. Put it between base PCI capability and base PCI Express capability. Driver just have to specify subsystem_vendor_id and subsystem_id fields in emulated bridge structure and pci-bridge-emul takes care of correctly compose PCI Bridge Subsystem Vendor ID capability. Link: https://lore.kernel.org/r/20220222155030.988-4-pali@kernel.org Signed-off-by: Pali Rohár Signed-off-by: Lorenzo Pieralisi --- drivers/pci/pci-bridge-emul.c | 69 ++++++++++++++++++++++++++++++------------- drivers/pci/pci-bridge-emul.h | 2 ++ 2 files changed, 51 insertions(+), 20 deletions(-) diff --git a/drivers/pci/pci-bridge-emul.c b/drivers/pci/pci-bridge-emul.c index c4b9837006ff..a5b662cc89d0 100644 --- a/drivers/pci/pci-bridge-emul.c +++ b/drivers/pci/pci-bridge-emul.c @@ -21,8 +21,11 @@ #include "pci-bridge-emul.h" #define PCI_BRIDGE_CONF_END PCI_STD_HEADER_SIZEOF +#define PCI_CAP_SSID_SIZEOF (PCI_SSVID_DEVICE_ID + 2) +#define PCI_CAP_SSID_START PCI_BRIDGE_CONF_END +#define PCI_CAP_SSID_END (PCI_CAP_SSID_START + PCI_CAP_SSID_SIZEOF) #define PCI_CAP_PCIE_SIZEOF (PCI_EXP_SLTSTA2 + 2) -#define PCI_CAP_PCIE_START PCI_BRIDGE_CONF_END +#define PCI_CAP_PCIE_START PCI_CAP_SSID_END #define PCI_CAP_PCIE_END (PCI_CAP_PCIE_START + PCI_CAP_PCIE_SIZEOF) /** @@ -315,6 +318,25 @@ struct pci_bridge_reg_behavior pcie_cap_regs_behavior[PCI_CAP_PCIE_SIZEOF / 4] = }, }; +static pci_bridge_emul_read_status_t +pci_bridge_emul_read_ssid(struct pci_bridge_emul *bridge, int reg, u32 *value) +{ + switch (reg) { + case PCI_CAP_LIST_ID: + *value = PCI_CAP_ID_SSVID | + (bridge->has_pcie ? (PCI_CAP_PCIE_START << 8) : 0); + return PCI_BRIDGE_EMUL_HANDLED; + + case PCI_SSVID_VENDOR_ID: + *value = bridge->subsystem_vendor_id | + (bridge->subsystem_id << 16); + return PCI_BRIDGE_EMUL_HANDLED; + + default: + return PCI_BRIDGE_EMUL_NOT_HANDLED; + } +} + /* * Initialize a pci_bridge_emul structure to represent a fake PCI * bridge configuration space. The caller needs to have initialized @@ -341,9 +363,17 @@ int pci_bridge_emul_init(struct pci_bridge_emul *bridge, if (!bridge->pci_regs_behavior) return -ENOMEM; - if (bridge->has_pcie) { + if (bridge->subsystem_vendor_id) + bridge->conf.capabilities_pointer = PCI_CAP_SSID_START; + else if (bridge->has_pcie) bridge->conf.capabilities_pointer = PCI_CAP_PCIE_START; + else + bridge->conf.capabilities_pointer = 0; + + if (bridge->conf.capabilities_pointer) bridge->conf.status |= cpu_to_le16(PCI_STATUS_CAP_LIST); + + if (bridge->has_pcie) { bridge->pcie_conf.cap_id = PCI_CAP_ID_EXP; bridge->pcie_conf.cap |= cpu_to_le16(PCI_EXP_TYPE_ROOT_PORT << 4); bridge->pcie_cap_regs_behavior = @@ -427,26 +457,28 @@ int pci_bridge_emul_conf_read(struct pci_bridge_emul *bridge, int where, read_op = bridge->ops->read_base; cfgspace = (__le32 *) &bridge->conf; behavior = bridge->pci_regs_behavior; - } else if (!bridge->has_pcie) { - /* PCIe space is not implemented, and no PCI capabilities */ - *value = 0; - return PCIBIOS_SUCCESSFUL; - } else if (reg < PCI_CAP_PCIE_END) { + } else if (reg >= PCI_CAP_SSID_START && reg < PCI_CAP_SSID_END && bridge->subsystem_vendor_id) { + /* Emulated PCI Bridge Subsystem Vendor ID capability */ + reg -= PCI_CAP_SSID_START; + read_op = pci_bridge_emul_read_ssid; + cfgspace = NULL; + behavior = NULL; + } else if (reg >= PCI_CAP_PCIE_START && reg < PCI_CAP_PCIE_END && bridge->has_pcie) { /* Our emulated PCIe capability */ reg -= PCI_CAP_PCIE_START; read_op = bridge->ops->read_pcie; cfgspace = (__le32 *) &bridge->pcie_conf; behavior = bridge->pcie_cap_regs_behavior; - } else if (reg < PCI_CFG_SPACE_SIZE) { - /* Rest of PCI space not implemented */ - *value = 0; - return PCIBIOS_SUCCESSFUL; - } else { + } else if (reg >= PCI_CFG_SPACE_SIZE && bridge->has_pcie) { /* PCIe extended capability space */ reg -= PCI_CFG_SPACE_SIZE; read_op = bridge->ops->read_ext; cfgspace = NULL; behavior = NULL; + } else { + /* Not implemented */ + *value = 0; + return PCIBIOS_SUCCESSFUL; } if (read_op) @@ -504,24 +536,21 @@ int pci_bridge_emul_conf_write(struct pci_bridge_emul *bridge, int where, write_op = bridge->ops->write_base; cfgspace = (__le32 *) &bridge->conf; behavior = bridge->pci_regs_behavior; - } else if (!bridge->has_pcie) { - /* PCIe space is not implemented, and no PCI capabilities */ - return PCIBIOS_SUCCESSFUL; - } else if (reg < PCI_CAP_PCIE_END) { + } else if (reg >= PCI_CAP_PCIE_START && reg < PCI_CAP_PCIE_END && bridge->has_pcie) { /* Our emulated PCIe capability */ reg -= PCI_CAP_PCIE_START; write_op = bridge->ops->write_pcie; cfgspace = (__le32 *) &bridge->pcie_conf; behavior = bridge->pcie_cap_regs_behavior; - } else if (reg < PCI_CFG_SPACE_SIZE) { - /* Rest of PCI space not implemented */ - return PCIBIOS_SUCCESSFUL; - } else { + } else if (reg >= PCI_CFG_SPACE_SIZE && bridge->has_pcie) { /* PCIe extended capability space */ reg -= PCI_CFG_SPACE_SIZE; write_op = bridge->ops->write_ext; cfgspace = NULL; behavior = NULL; + } else { + /* Not implemented */ + return PCIBIOS_SUCCESSFUL; } shift = (where & 0x3) * 8; diff --git a/drivers/pci/pci-bridge-emul.h b/drivers/pci/pci-bridge-emul.h index 6b5f75b2ad02..71392b67471d 100644 --- a/drivers/pci/pci-bridge-emul.h +++ b/drivers/pci/pci-bridge-emul.h @@ -132,6 +132,8 @@ struct pci_bridge_emul { struct pci_bridge_reg_behavior *pcie_cap_regs_behavior; void *data; bool has_pcie; + u16 subsystem_vendor_id; + u16 subsystem_id; }; enum { -- cgit v1.2.3 From 26b982ca83210684b04191b73a7bbc02cdd3164f Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Tue, 22 Feb 2022 16:50:22 +0100 Subject: dt-bindings: PCI: mvebu: Add num-lanes property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Controller driver needs to correctly configure PCIe link if it contains 1 or 4 SerDes PCIe lanes. Therefore add a new 'num-lanes' DT property for mvebu PCIe controller. Property 'num-lanes' seems to be de-facto standard way how number of lanes is specified in other PCIe controllers. Link: https://lore.kernel.org/r/20220222155030.988-5-pali@kernel.org Signed-off-by: Pali Rohár Signed-off-by: Lorenzo Pieralisi Acked-by: Rob Herring --- Documentation/devicetree/bindings/pci/mvebu-pci.txt | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Documentation/devicetree/bindings/pci/mvebu-pci.txt b/Documentation/devicetree/bindings/pci/mvebu-pci.txt index 6173af6885f8..24225852bce0 100644 --- a/Documentation/devicetree/bindings/pci/mvebu-pci.txt +++ b/Documentation/devicetree/bindings/pci/mvebu-pci.txt @@ -77,6 +77,7 @@ and the following optional properties: - marvell,pcie-lane: the physical PCIe lane number, for ports having multiple lanes. If this property is not found, we assume that the value is 0. +- num-lanes: number of SerDes PCIe lanes for this link (1 or 4) - reset-gpios: optional GPIO to PERST# - reset-delay-us: delay in us to wait after reset de-assertion, if not specified will default to 100ms, as required by the PCIe specification. @@ -141,6 +142,7 @@ pcie-controller { interrupt-map = <0 0 0 0 &mpic 58>; marvell,pcie-port = <0>; marvell,pcie-lane = <0>; + num-lanes = <1>; /* low-active PERST# reset on GPIO 25 */ reset-gpios = <&gpio0 25 1>; /* wait 20ms for device settle after reset deassertion */ @@ -161,6 +163,7 @@ pcie-controller { interrupt-map = <0 0 0 0 &mpic 59>; marvell,pcie-port = <0>; marvell,pcie-lane = <1>; + num-lanes = <1>; clocks = <&gateclk 6>; }; @@ -177,6 +180,7 @@ pcie-controller { interrupt-map = <0 0 0 0 &mpic 60>; marvell,pcie-port = <0>; marvell,pcie-lane = <2>; + num-lanes = <1>; clocks = <&gateclk 7>; }; @@ -193,6 +197,7 @@ pcie-controller { interrupt-map = <0 0 0 0 &mpic 61>; marvell,pcie-port = <0>; marvell,pcie-lane = <3>; + num-lanes = <1>; clocks = <&gateclk 8>; }; @@ -209,6 +214,7 @@ pcie-controller { interrupt-map = <0 0 0 0 &mpic 62>; marvell,pcie-port = <1>; marvell,pcie-lane = <0>; + num-lanes = <1>; clocks = <&gateclk 9>; }; @@ -225,6 +231,7 @@ pcie-controller { interrupt-map = <0 0 0 0 &mpic 63>; marvell,pcie-port = <1>; marvell,pcie-lane = <1>; + num-lanes = <1>; clocks = <&gateclk 10>; }; @@ -241,6 +248,7 @@ pcie-controller { interrupt-map = <0 0 0 0 &mpic 64>; marvell,pcie-port = <1>; marvell,pcie-lane = <2>; + num-lanes = <1>; clocks = <&gateclk 11>; }; @@ -257,6 +265,7 @@ pcie-controller { interrupt-map = <0 0 0 0 &mpic 65>; marvell,pcie-port = <1>; marvell,pcie-lane = <3>; + num-lanes = <1>; clocks = <&gateclk 12>; }; @@ -273,6 +282,7 @@ pcie-controller { interrupt-map = <0 0 0 0 &mpic 99>; marvell,pcie-port = <2>; marvell,pcie-lane = <0>; + num-lanes = <1>; clocks = <&gateclk 26>; }; @@ -289,6 +299,7 @@ pcie-controller { interrupt-map = <0 0 0 0 &mpic 103>; marvell,pcie-port = <3>; marvell,pcie-lane = <0>; + num-lanes = <1>; clocks = <&gateclk 27>; }; }; -- cgit v1.2.3 From 2a81dd9fd9b0b42b42ba7484baf6feb7b4d94d2e Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Tue, 22 Feb 2022 16:50:23 +0100 Subject: PCI: mvebu: Correctly configure x1/x4 mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If x1/x4 mode is not set correctly then link with endpoint card is not established. Use DTS property 'num-lanes' to deteriminate x1/x4 mode. Link: https://lore.kernel.org/r/20220222155030.988-6-pali@kernel.org Signed-off-by: Pali Rohár Signed-off-by: Lorenzo Pieralisi --- drivers/pci/controller/pci-mvebu.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c index 357f0f41f68e..d0a75c3b78c3 100644 --- a/drivers/pci/controller/pci-mvebu.c +++ b/drivers/pci/controller/pci-mvebu.c @@ -93,6 +93,7 @@ struct mvebu_pcie_port { void __iomem *base; u32 port; u32 lane; + bool is_x4; int devfn; unsigned int mem_target; unsigned int mem_attr; @@ -233,13 +234,25 @@ 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, dev_rev, mask; + u32 ctrl, lnkcap, cmd, dev_rev, 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); + /* + * 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. + */ + lnkcap = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_LNKCAP); + lnkcap &= ~PCI_EXP_LNKCAP_MLW; + lnkcap |= (port->is_x4 ? 4 : 1) << 4; + mvebu_writel(port, lnkcap, PCIE_CAP_PCIEXP + PCI_EXP_LNKCAP); + /* 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); @@ -982,6 +995,7 @@ static int mvebu_pcie_parse_port(struct mvebu_pcie *pcie, struct device *dev = &pcie->pdev->dev; enum of_gpio_flags flags; int reset_gpio, ret; + u32 num_lanes; port->pcie = pcie; @@ -994,6 +1008,9 @@ static int mvebu_pcie_parse_port(struct mvebu_pcie *pcie, if (of_property_read_u32(child, "marvell,pcie-lane", &port->lane)) port->lane = 0; + if (!of_property_read_u32(child, "num-lanes", &num_lanes) && num_lanes == 4) + port->is_x4 = true; + port->name = devm_kasprintf(dev, GFP_KERNEL, "pcie%d.%d", port->port, port->lane); if (!port->name) { -- cgit v1.2.3 From e3e13c9135da969cfe1666a9f24e76b589d5652b Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Tue, 22 Feb 2022 16:50:24 +0100 Subject: PCI: mvebu: Add support for PCI Bridge Subsystem Vendor ID on emulated bridge MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Register with Subsystem Device/Vendor ID is at offset 0x2c. Export is via emulated bridge. After this change Subsystem ID is visible in lspci output at line: Capabilities: [40] Subsystem Link: https://lore.kernel.org/r/20220222155030.988-7-pali@kernel.org Signed-off-by: Pali Rohár Signed-off-by: Lorenzo Pieralisi --- drivers/pci/controller/pci-mvebu.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c index d0a75c3b78c3..566d8382afe6 100644 --- a/drivers/pci/controller/pci-mvebu.c +++ b/drivers/pci/controller/pci-mvebu.c @@ -32,6 +32,7 @@ #define PCIE_DEV_REV_OFF 0x0008 #define PCIE_BAR_LO_OFF(n) (0x0010 + ((n) << 3)) #define PCIE_BAR_HI_OFF(n) (0x0014 + ((n) << 3)) +#define PCIE_SSDEV_ID_OFF 0x002c #define PCIE_CAP_PCIEXP 0x0060 #define PCIE_HEADER_LOG_4_OFF 0x0128 #define PCIE_BAR_CTRL_OFF(n) (0x1804 + (((n) - 1) * 4)) @@ -731,6 +732,7 @@ static int mvebu_pci_bridge_emul_init(struct mvebu_pcie_port *port) struct pci_bridge_emul *bridge = &port->bridge; u32 dev_id = mvebu_readl(port, PCIE_DEV_ID_OFF); u32 dev_rev = mvebu_readl(port, PCIE_DEV_REV_OFF); + u32 ssdev_id = mvebu_readl(port, PCIE_SSDEV_ID_OFF); u32 pcie_cap = mvebu_readl(port, PCIE_CAP_PCIEXP); u8 pcie_cap_ver = ((pcie_cap >> 16) & PCI_EXP_FLAGS_VERS); @@ -752,6 +754,8 @@ static int mvebu_pci_bridge_emul_init(struct mvebu_pcie_port *port) */ bridge->pcie_conf.cap = cpu_to_le16(pcie_cap_ver); + bridge->subsystem_vendor_id = ssdev_id & 0xffff; + bridge->subsystem_id = ssdev_id >> 16; bridge->has_pcie = true; bridge->data = port; bridge->ops = &mvebu_pci_bridge_emul_ops; -- cgit v1.2.3 From 2b6ee04c0aa6cfff87bdf71b47b78fa26ad54afd Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Tue, 22 Feb 2022 16:50:25 +0100 Subject: PCI: mvebu: Add support for Advanced Error Reporting registers on emulated bridge MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AER registers start at mvebu offset 0x0100. Registers PCI_ERR_ROOT_COMMAND, PCI_ERR_ROOT_STATUS and PCI_ERR_ROOT_ERR_SRC are not supported on pre-XP hardware and returns zeros. Note that AER interrupt is not supported yet as mvebu emulated bridge does not implement interrupts support at all yet. Also remove custom macro PCIE_HEADER_LOG_4_OFF as it is unused and correctly this register should be referenced via standard macros with offset, e.g. as: PCIE_CAP_PCIERR_OFF + PCI_ERR_HEADER_LOG + 4. Link: https://lore.kernel.org/r/20220222155030.988-8-pali@kernel.org Signed-off-by: Pali Rohár Signed-off-by: Lorenzo Pieralisi --- drivers/pci/controller/pci-mvebu.c | 67 +++++++++++++++++++++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c index 566d8382afe6..1cf5c02499cd 100644 --- a/drivers/pci/controller/pci-mvebu.c +++ b/drivers/pci/controller/pci-mvebu.c @@ -34,7 +34,7 @@ #define PCIE_BAR_HI_OFF(n) (0x0014 + ((n) << 3)) #define PCIE_SSDEV_ID_OFF 0x002c #define PCIE_CAP_PCIEXP 0x0060 -#define PCIE_HEADER_LOG_4_OFF 0x0128 +#define PCIE_CAP_PCIERR_OFF 0x0100 #define PCIE_BAR_CTRL_OFF(n) (0x1804 + (((n) - 1) * 4)) #define PCIE_WIN04_CTRL_OFF(n) (0x1820 + ((n) << 4)) #define PCIE_WIN04_BASE_OFF(n) (0x1824 + ((n) << 4)) @@ -603,6 +603,37 @@ mvebu_pci_bridge_emul_pcie_conf_read(struct pci_bridge_emul *bridge, return PCI_BRIDGE_EMUL_HANDLED; } +static pci_bridge_emul_read_status_t +mvebu_pci_bridge_emul_ext_conf_read(struct pci_bridge_emul *bridge, + int reg, u32 *value) +{ + struct mvebu_pcie_port *port = bridge->data; + + switch (reg) { + case 0: + case PCI_ERR_UNCOR_STATUS: + case PCI_ERR_UNCOR_MASK: + case PCI_ERR_UNCOR_SEVER: + case PCI_ERR_COR_STATUS: + case PCI_ERR_COR_MASK: + case PCI_ERR_CAP: + case PCI_ERR_HEADER_LOG+0: + case PCI_ERR_HEADER_LOG+4: + case PCI_ERR_HEADER_LOG+8: + case PCI_ERR_HEADER_LOG+12: + case PCI_ERR_ROOT_COMMAND: + case PCI_ERR_ROOT_STATUS: + case PCI_ERR_ROOT_ERR_SRC: + *value = mvebu_readl(port, PCIE_CAP_PCIERR_OFF + reg); + break; + + default: + return PCI_BRIDGE_EMUL_NOT_HANDLED; + } + + return PCI_BRIDGE_EMUL_HANDLED; +} + static void mvebu_pci_bridge_emul_base_conf_write(struct pci_bridge_emul *bridge, int reg, u32 old, u32 new, u32 mask) @@ -715,11 +746,45 @@ mvebu_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge, } } +static void +mvebu_pci_bridge_emul_ext_conf_write(struct pci_bridge_emul *bridge, + int reg, u32 old, u32 new, u32 mask) +{ + struct mvebu_pcie_port *port = bridge->data; + + switch (reg) { + /* These are W1C registers, so clear other bits */ + case PCI_ERR_UNCOR_STATUS: + case PCI_ERR_COR_STATUS: + case PCI_ERR_ROOT_STATUS: + new &= mask; + fallthrough; + + case PCI_ERR_UNCOR_MASK: + case PCI_ERR_UNCOR_SEVER: + case PCI_ERR_COR_MASK: + case PCI_ERR_CAP: + case PCI_ERR_HEADER_LOG+0: + case PCI_ERR_HEADER_LOG+4: + case PCI_ERR_HEADER_LOG+8: + case PCI_ERR_HEADER_LOG+12: + case PCI_ERR_ROOT_COMMAND: + case PCI_ERR_ROOT_ERR_SRC: + mvebu_writel(port, new, PCIE_CAP_PCIERR_OFF + reg); + break; + + default: + break; + } +} + static const 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, + .read_ext = mvebu_pci_bridge_emul_ext_conf_read, + .write_ext = mvebu_pci_bridge_emul_ext_conf_write, }; /* -- cgit v1.2.3 From c099c2a7618536edc28ab86fc171e96c4c1b22bb Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Tue, 22 Feb 2022 16:50:26 +0100 Subject: PCI: mvebu: Use child_ops API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Split struct pci_ops between ops and child_ops. Member ops is used for accessing PCIe Root Ports via pci-bridge-emul.c driver and child_ops for accessing real PCIe cards. There is no need to mix these two struct pci_ops into one as PCI core code already provides separate callbacks via bridge->ops and bridge->child_ops. Link: https://lore.kernel.org/r/20220222155030.988-9-pali@kernel.org Signed-off-by: Pali Rohár Signed-off-by: Lorenzo Pieralisi --- drivers/pci/controller/pci-mvebu.c | 77 ++++++++++++++++++++------------------ 1 file changed, 40 insertions(+), 37 deletions(-) diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c index 1cf5c02499cd..1ae7718a2e3e 100644 --- a/drivers/pci/controller/pci-mvebu.c +++ b/drivers/pci/controller/pci-mvebu.c @@ -294,11 +294,25 @@ static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port) mvebu_writel(port, mask, PCIE_MASK_OFF); } -static int mvebu_pcie_hw_rd_conf(struct mvebu_pcie_port *port, - struct pci_bus *bus, - u32 devfn, int where, int size, u32 *val) +static struct mvebu_pcie_port *mvebu_pcie_find_port(struct mvebu_pcie *pcie, + struct pci_bus *bus, + int devfn); + +static int mvebu_pcie_child_rd_conf(struct pci_bus *bus, u32 devfn, int where, + int size, u32 *val) { - void __iomem *conf_data = port->base + PCIE_CONF_DATA_OFF; + struct mvebu_pcie *pcie = bus->sysdata; + struct mvebu_pcie_port *port; + void __iomem *conf_data; + + port = mvebu_pcie_find_port(pcie, bus, devfn); + if (!port) + return PCIBIOS_DEVICE_NOT_FOUND; + + if (!mvebu_pcie_link_up(port)) + return PCIBIOS_DEVICE_NOT_FOUND; + + conf_data = port->base + PCIE_CONF_DATA_OFF; mvebu_writel(port, PCIE_CONF_ADDR(bus->number, devfn, where), PCIE_CONF_ADDR_OFF); @@ -314,18 +328,27 @@ static int mvebu_pcie_hw_rd_conf(struct mvebu_pcie_port *port, *val = readl_relaxed(conf_data); break; default: - *val = 0xffffffff; return PCIBIOS_BAD_REGISTER_NUMBER; } return PCIBIOS_SUCCESSFUL; } -static int mvebu_pcie_hw_wr_conf(struct mvebu_pcie_port *port, - struct pci_bus *bus, - u32 devfn, int where, int size, u32 val) +static int mvebu_pcie_child_wr_conf(struct pci_bus *bus, u32 devfn, + int where, int size, u32 val) { - void __iomem *conf_data = port->base + PCIE_CONF_DATA_OFF; + struct mvebu_pcie *pcie = bus->sysdata; + struct mvebu_pcie_port *port; + void __iomem *conf_data; + + port = mvebu_pcie_find_port(pcie, bus, devfn); + if (!port) + return PCIBIOS_DEVICE_NOT_FOUND; + + if (!mvebu_pcie_link_up(port)) + return PCIBIOS_DEVICE_NOT_FOUND; + + conf_data = port->base + PCIE_CONF_DATA_OFF; mvebu_writel(port, PCIE_CONF_ADDR(bus->number, devfn, where), PCIE_CONF_ADDR_OFF); @@ -347,6 +370,11 @@ static int mvebu_pcie_hw_wr_conf(struct mvebu_pcie_port *port, return PCIBIOS_SUCCESSFUL; } +static struct pci_ops mvebu_pcie_child_ops = { + .read = mvebu_pcie_child_rd_conf, + .write = mvebu_pcie_child_wr_conf, +}; + /* * Remove windows, starting from the largest ones to the smallest * ones. @@ -862,25 +890,12 @@ static int mvebu_pcie_wr_conf(struct pci_bus *bus, u32 devfn, { struct mvebu_pcie *pcie = bus->sysdata; struct mvebu_pcie_port *port; - int ret; port = mvebu_pcie_find_port(pcie, bus, devfn); if (!port) return PCIBIOS_DEVICE_NOT_FOUND; - /* Access the emulated PCI-to-PCI bridge */ - if (bus->number == 0) - return pci_bridge_emul_conf_write(&port->bridge, where, - size, val); - - if (!mvebu_pcie_link_up(port)) - return PCIBIOS_DEVICE_NOT_FOUND; - - /* Access the real PCIe interface */ - ret = mvebu_pcie_hw_wr_conf(port, bus, devfn, - where, size, val); - - return ret; + return pci_bridge_emul_conf_write(&port->bridge, where, size, val); } /* PCI configuration space read function */ @@ -889,25 +904,12 @@ static int mvebu_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where, { struct mvebu_pcie *pcie = bus->sysdata; struct mvebu_pcie_port *port; - int ret; port = mvebu_pcie_find_port(pcie, bus, devfn); if (!port) return PCIBIOS_DEVICE_NOT_FOUND; - /* Access the emulated PCI-to-PCI bridge */ - if (bus->number == 0) - return pci_bridge_emul_conf_read(&port->bridge, where, - size, val); - - if (!mvebu_pcie_link_up(port)) - return PCIBIOS_DEVICE_NOT_FOUND; - - /* Access the real PCIe interface */ - ret = mvebu_pcie_hw_rd_conf(port, bus, devfn, - where, size, val); - - return ret; + return pci_bridge_emul_conf_read(&port->bridge, where, size, val); } static struct pci_ops mvebu_pcie_ops = { @@ -1416,6 +1418,7 @@ static int mvebu_pcie_probe(struct platform_device *pdev) bridge->sysdata = pcie; bridge->ops = &mvebu_pcie_ops; + bridge->child_ops = &mvebu_pcie_child_ops; bridge->align_resource = mvebu_pcie_align_resource; bridge->map_irq = mvebu_pcie_map_irq; -- cgit v1.2.3 From 0124989220f21b5275f6bf9ad9a924c8fb278dce Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Tue, 22 Feb 2022 16:50:27 +0100 Subject: dt-bindings: PCI: mvebu: Update information about intx interrupts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Link: https://lore.kernel.org/r/20220222155030.988-10-pali@kernel.org Signed-off-by: Pali Rohár Signed-off-by: Lorenzo Pieralisi Acked-by: Rob Herring --- Documentation/devicetree/bindings/pci/mvebu-pci.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Documentation/devicetree/bindings/pci/mvebu-pci.txt b/Documentation/devicetree/bindings/pci/mvebu-pci.txt index 24225852bce0..6d022a9d36ee 100644 --- a/Documentation/devicetree/bindings/pci/mvebu-pci.txt +++ b/Documentation/devicetree/bindings/pci/mvebu-pci.txt @@ -81,6 +81,11 @@ and the following optional properties: - reset-gpios: optional GPIO to PERST# - reset-delay-us: delay in us to wait after reset de-assertion, if not specified will default to 100ms, as required by the PCIe specification. +- interrupt-names: list of interrupt names, supported are: + - "intx" - interrupt line triggered by one of the legacy interrupt +- interrupts or interrupts-extended: List of the interrupt sources which + corresponding to the "interrupt-names". If non-empty then also additional + 'interrupt-controller' subnode must be defined. Example: -- cgit v1.2.3 From d00ea94e620903059d6818c074c70a95376b7e03 Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Tue, 22 Feb 2022 16:50:28 +0100 Subject: PCI: mvebu: Fix macro names and comments about legacy interrupts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Register 0x1910 unmasks interrupts and legacy INTx interrupts are unmasked because driver does not support individual masking yet. Link: https://lore.kernel.org/r/20220222155030.988-11-pali@kernel.org Signed-off-by: Pali Rohár Signed-off-by: Lorenzo Pieralisi --- drivers/pci/controller/pci-mvebu.c | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c index 1ae7718a2e3e..d096289035bc 100644 --- a/drivers/pci/controller/pci-mvebu.c +++ b/drivers/pci/controller/pci-mvebu.c @@ -54,9 +54,10 @@ PCIE_CONF_ADDR_EN) #define PCIE_CONF_DATA_OFF 0x18fc #define PCIE_INT_CAUSE_OFF 0x1900 +#define PCIE_INT_UNMASK_OFF 0x1910 +#define PCIE_INT_INTX(i) BIT(24+i) #define PCIE_INT_PM_PME BIT(28) -#define PCIE_MASK_OFF 0x1910 -#define PCIE_MASK_ENABLE_INTS 0x0f000000 +#define PCIE_INT_ALL_MASK GENMASK(31, 0) #define PCIE_CTRL_OFF 0x1a00 #define PCIE_CTRL_X1_MODE 0x0001 #define PCIE_CTRL_RC_MODE BIT(1) @@ -235,7 +236,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, lnkcap, cmd, dev_rev, mask; + u32 ctrl, lnkcap, cmd, dev_rev, unmask; /* Setup PCIe controller to Root Complex mode. */ ctrl = mvebu_readl(port, PCIE_CTRL_OFF); @@ -288,10 +289,19 @@ static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port) /* 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; - mvebu_writel(port, mask, PCIE_MASK_OFF); + /* + * Unmask all legacy INTx interrupts as driver does not provide a way + * for masking and unmasking of individual legacy INTx interrupts. + * Legacy INTx are reported via one shared GIC source and therefore + * kernel cannot distinguish which individual legacy INTx was triggered. + * These interrupts are shared, so it should not cause any issue. Just + * performance penalty as every PCIe interrupt handler needs to be + * called when some interrupt is triggered. + */ + unmask = mvebu_readl(port, PCIE_INT_UNMASK_OFF); + unmask |= PCIE_INT_INTX(0) | PCIE_INT_INTX(1) | + PCIE_INT_INTX(2) | PCIE_INT_INTX(3); + mvebu_writel(port, unmask, PCIE_INT_UNMASK_OFF); } static struct mvebu_pcie_port *mvebu_pcie_find_port(struct mvebu_pcie *pcie, @@ -1450,7 +1460,7 @@ static int mvebu_pcie_remove(struct platform_device *pdev) mvebu_writel(port, cmd, PCIE_CMD_OFF); /* Mask all interrupt sources. */ - mvebu_writel(port, 0, PCIE_MASK_OFF); + mvebu_writel(port, ~PCIE_INT_ALL_MASK, PCIE_INT_UNMASK_OFF); /* Free config space for emulated root bridge. */ pci_bridge_emul_cleanup(&port->bridge); -- cgit v1.2.3 From ec075262648f396d65e03500b2a513ed69ba38d1 Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Tue, 22 Feb 2022 16:50:29 +0100 Subject: PCI: mvebu: Implement support for legacy INTx interrupts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds support for legacy INTx interrupts received from other PCIe devices and which are reported by a new INTx irq chip. With this change, kernel can distinguish between INTA, INTB, INTC and INTD interrupts. Note that for this support, device tree files has to be properly adjusted to provide "interrupts" or "interrupts-extended" property with intx interrupt source, "interrupt-names" property with "intx" string and also 'interrupt-controller' subnode must be defined. If device tree files do not provide these nodes then driver would work as before. Link: https://lore.kernel.org/r/20220222155030.988-12-pali@kernel.org Signed-off-by: Pali Rohár Signed-off-by: Lorenzo Pieralisi --- drivers/pci/controller/pci-mvebu.c | 159 +++++++++++++++++++++++++++++++++++++ 1 file changed, 159 insertions(+) diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c index d096289035bc..a75d2b9196f9 100644 --- a/drivers/pci/controller/pci-mvebu.c +++ b/drivers/pci/controller/pci-mvebu.c @@ -111,6 +111,9 @@ struct mvebu_pcie_port { struct mvebu_pcie_window iowin; u32 saved_pcie_stat; struct resource regs; + struct irq_domain *intx_irq_domain; + raw_spinlock_t irq_lock; + int intx_irq; }; static inline void mvebu_writel(struct mvebu_pcie_port *port, u32 val, u32 reg) @@ -289,7 +292,18 @@ static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port) /* Point PCIe unit MBUS decode windows to DRAM space. */ mvebu_pcie_setup_wins(port); + /* Mask all interrupt sources. */ + mvebu_writel(port, ~PCIE_INT_ALL_MASK, PCIE_INT_UNMASK_OFF); + + /* Clear all interrupt causes. */ + mvebu_writel(port, ~PCIE_INT_ALL_MASK, PCIE_INT_CAUSE_OFF); + + /* Check if "intx" interrupt was specified in DT. */ + if (port->intx_irq > 0) + return; + /* + * Fallback code when "intx" interrupt was not specified in DT: * Unmask all legacy INTx interrupts as driver does not provide a way * for masking and unmasking of individual legacy INTx interrupts. * Legacy INTx are reported via one shared GIC source and therefore @@ -927,6 +941,108 @@ static struct pci_ops mvebu_pcie_ops = { .write = mvebu_pcie_wr_conf, }; +static void mvebu_pcie_intx_irq_mask(struct irq_data *d) +{ + struct mvebu_pcie_port *port = d->domain->host_data; + irq_hw_number_t hwirq = irqd_to_hwirq(d); + unsigned long flags; + u32 unmask; + + raw_spin_lock_irqsave(&port->irq_lock, flags); + unmask = mvebu_readl(port, PCIE_INT_UNMASK_OFF); + unmask &= ~PCIE_INT_INTX(hwirq); + mvebu_writel(port, unmask, PCIE_INT_UNMASK_OFF); + raw_spin_unlock_irqrestore(&port->irq_lock, flags); +} + +static void mvebu_pcie_intx_irq_unmask(struct irq_data *d) +{ + struct mvebu_pcie_port *port = d->domain->host_data; + irq_hw_number_t hwirq = irqd_to_hwirq(d); + unsigned long flags; + u32 unmask; + + raw_spin_lock_irqsave(&port->irq_lock, flags); + unmask = mvebu_readl(port, PCIE_INT_UNMASK_OFF); + unmask |= PCIE_INT_INTX(hwirq); + mvebu_writel(port, unmask, PCIE_INT_UNMASK_OFF); + raw_spin_unlock_irqrestore(&port->irq_lock, flags); +} + +static struct irq_chip intx_irq_chip = { + .name = "mvebu-INTx", + .irq_mask = mvebu_pcie_intx_irq_mask, + .irq_unmask = mvebu_pcie_intx_irq_unmask, +}; + +static int mvebu_pcie_intx_irq_map(struct irq_domain *h, + unsigned int virq, irq_hw_number_t hwirq) +{ + struct mvebu_pcie_port *port = h->host_data; + + irq_set_status_flags(virq, IRQ_LEVEL); + irq_set_chip_and_handler(virq, &intx_irq_chip, handle_level_irq); + irq_set_chip_data(virq, port); + + return 0; +} + +static const struct irq_domain_ops mvebu_pcie_intx_irq_domain_ops = { + .map = mvebu_pcie_intx_irq_map, + .xlate = irq_domain_xlate_onecell, +}; + +static int mvebu_pcie_init_irq_domain(struct mvebu_pcie_port *port) +{ + struct device *dev = &port->pcie->pdev->dev; + struct device_node *pcie_intc_node; + + raw_spin_lock_init(&port->irq_lock); + + pcie_intc_node = of_get_next_child(port->dn, NULL); + if (!pcie_intc_node) { + dev_err(dev, "No PCIe Intc node found for %s\n", port->name); + return -ENODEV; + } + + port->intx_irq_domain = irq_domain_add_linear(pcie_intc_node, PCI_NUM_INTX, + &mvebu_pcie_intx_irq_domain_ops, + port); + of_node_put(pcie_intc_node); + if (!port->intx_irq_domain) { + dev_err(dev, "Failed to get INTx IRQ domain for %s\n", port->name); + return -ENOMEM; + } + + return 0; +} + +static void mvebu_pcie_irq_handler(struct irq_desc *desc) +{ + struct mvebu_pcie_port *port = irq_desc_get_handler_data(desc); + struct irq_chip *chip = irq_desc_get_chip(desc); + struct device *dev = &port->pcie->pdev->dev; + u32 cause, unmask, status; + int i; + + chained_irq_enter(chip, desc); + + cause = mvebu_readl(port, PCIE_INT_CAUSE_OFF); + unmask = mvebu_readl(port, PCIE_INT_UNMASK_OFF); + status = cause & unmask; + + /* Process legacy INTx interrupts */ + for (i = 0; i < PCI_NUM_INTX; i++) { + if (!(status & PCIE_INT_INTX(i))) + continue; + + if (generic_handle_domain_irq(port->intx_irq_domain, i) == -EINVAL) + dev_err_ratelimited(dev, "unexpected INT%c IRQ\n", (char)i+'A'); + } + + chained_irq_exit(chip, desc); +} + 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 */ @@ -1124,6 +1240,21 @@ static int mvebu_pcie_parse_port(struct mvebu_pcie *pcie, port->io_attr = -1; } + /* + * Old DT bindings do not contain "intx" interrupt + * so do not fail probing driver when interrupt does not exist. + */ + port->intx_irq = of_irq_get_byname(child, "intx"); + if (port->intx_irq == -EPROBE_DEFER) { + ret = port->intx_irq; + goto err; + } + if (port->intx_irq <= 0) { + dev_warn(dev, "%s: legacy INTx interrupts cannot be masked individually, " + "%pOF does not contain intx interrupt\n", + port->name, child); + } + reset_gpio = of_get_named_gpio_flags(child, "reset-gpios", 0, &flags); if (reset_gpio == -EPROBE_DEFER) { ret = reset_gpio; @@ -1320,6 +1451,7 @@ static int mvebu_pcie_probe(struct platform_device *pdev) for (i = 0; i < pcie->nports; i++) { struct mvebu_pcie_port *port = &pcie->ports[i]; + int irq = port->intx_irq; child = port->dn; if (!child) @@ -1347,6 +1479,22 @@ static int mvebu_pcie_probe(struct platform_device *pdev) continue; } + if (irq > 0) { + ret = mvebu_pcie_init_irq_domain(port); + if (ret) { + dev_err(dev, "%s: cannot init irq domain\n", + port->name); + pci_bridge_emul_cleanup(&port->bridge); + devm_iounmap(dev, port->base); + port->base = NULL; + mvebu_pcie_powerdown(port); + continue; + } + irq_set_chained_handler_and_data(irq, + mvebu_pcie_irq_handler, + port); + } + /* * PCIe topology exported by mvebu hw is quite complicated. In * reality has something like N fully independent host bridges @@ -1450,6 +1598,7 @@ static int mvebu_pcie_remove(struct platform_device *pdev) for (i = 0; i < pcie->nports; i++) { struct mvebu_pcie_port *port = &pcie->ports[i]; + int irq = port->intx_irq; if (!port->base) continue; @@ -1462,6 +1611,16 @@ static int mvebu_pcie_remove(struct platform_device *pdev) /* Mask all interrupt sources. */ mvebu_writel(port, ~PCIE_INT_ALL_MASK, PCIE_INT_UNMASK_OFF); + /* Clear all interrupt causes. */ + mvebu_writel(port, ~PCIE_INT_ALL_MASK, PCIE_INT_CAUSE_OFF); + + if (irq > 0) + irq_set_chained_handler_and_data(irq, NULL, NULL); + + /* Remove IRQ domains. */ + if (port->intx_irq_domain) + irq_domain_remove(port->intx_irq_domain); + /* Free config space for emulated root bridge. */ pci_bridge_emul_cleanup(&port->bridge); -- cgit v1.2.3