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(-) (limited to 'drivers/pci') 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 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(-) (limited to 'drivers/pci') 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 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(+) (limited to 'drivers/pci') 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 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(+) (limited to 'drivers/pci') 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 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(-) (limited to 'drivers/pci') 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 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(-) (limited to 'drivers/pci') 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 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(-) (limited to 'drivers/pci') 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 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(-) (limited to 'drivers/pci') 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 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(-) (limited to 'drivers/pci') 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 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(-) (limited to 'drivers/pci') 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 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(-) (limited to 'drivers/pci') 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 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(-) (limited to 'drivers/pci') 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 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(-) (limited to 'drivers/pci') 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 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(-) (limited to 'drivers/pci') 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 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(+) (limited to 'drivers/pci') 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 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(-) (limited to 'drivers/pci') 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 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(-) (limited to 'drivers/pci') 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 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(-) (limited to 'drivers/pci') 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 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(+) (limited to 'drivers/pci') 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