From 5c125683fc18fa60cb26d18b26d8d622b90c7f3e Mon Sep 17 00:00:00 2001 From: Paul Burton Date: Tue, 15 Aug 2017 16:25:08 -0500 Subject: [PATCH 1/6] PCI: xilinx: Translate INTx range to hwirqs 0-3 The pcie-xilinx driver creates an IRQ domain of size 4 for legacy PCI INTx interrupts, which at first glance seems reasonable since there are 4 possible such interrupts. Unfortunately the driver then proceeds to use the range 1-4 as the hwirq numbers for INTA-INTD, causing warnings & broken interrupts when attempting to use INTD/hwirq=4 due to it being beyond the range of the IRQ domain: WARNING: CPU: 0 PID: 1 at kernel/irq/irqdomain.c:365 irq_domain_associate+0x170/0x220 error: hwirq 0x4 is too large for dummy Modules linked in: CPU: 0 PID: 1 Comm: swapper/0 Tainted: G W 4.12.0-rc5-00126-g19e1b3a10aad-dirty #427 Stack : 0000000000000000 0000000000000004 0000000000000006 ffffffff8092c78a 0000000000000061 ffffffff8018bf60 0000000000000000 0000000000000000 ffffffff8088c287 ffffffff80811d18 a8000000ffc60000 ffffffff80926678 0000000000000001 0000000000000000 ffffffff80887880 ffffffff80960000 ffffffff80920000 ffffffff801e6744 ffffffff80887880 a8000000ffc4f8f8 000000000000089c ffffffff8018d260 0000000000010000 ffffffff80811d18 0000000000000000 0000000000000001 0000000000000000 0000000000000000 0000000000000000 a8000000ffc4f840 0000000000000000 ffffffff8042cf34 0000000000000000 0000000000000000 0000000000000000 0000000000040c00 0000000000000000 ffffffff8010d1c8 0000000000000000 ffffffff8042cf34 ... Call Trace: [] show_stack+0x80/0xa0 [] dump_stack+0xd4/0x110 [] __warn+0xf0/0x108 [] warn_slowpath_fmt+0x3c/0x48 [] irq_domain_associate+0x170/0x220 [] irq_create_mapping+0x88/0x118 [] irq_create_fwspec_mapping+0xb8/0x320 [] irq_create_of_mapping+0x60/0x70 [] of_irq_parse_and_map_pci+0x20/0x38 [] pci_fixup_irqs+0x60/0xe0 [] xilinx_pcie_probe+0x28c/0x478 [] platform_drv_probe+0x50/0xd0 [] driver_probe_device+0x2c4/0x3a0 [] __driver_attach+0xc4/0xd0 [] bus_for_each_dev+0x64/0xa8 [] bus_add_driver+0x1f0/0x268 [] driver_register+0x68/0x118 [] do_one_initcall+0x4c/0x178 [] kernel_init_freeable+0x204/0x2b0 [] kernel_init+0x10/0xf8 [] ret_from_kernel_thread+0x14/0x1c Fix this by making use of the new pci_irqd_intx_xlate() helper to translate the INTx 1-4 range into the 0-3 range suitable for the IRQ domain of size 4, and stop adding 1 to the hwirq number decoded from the interrupt FIFO which is already in the range 0-3. Whilst we're here we switch to using PCI_NUM_INTX rather than the magic number 4, making it clearer what the 4 means. Signed-off-by: Paul Burton Signed-off-by: Bjorn Helgaas Cc: Bharat Kumar Gogada Cc: Michal Simek Cc: Ravikiran Gummaluri --- drivers/pci/host/pcie-xilinx.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/pci/host/pcie-xilinx.c b/drivers/pci/host/pcie-xilinx.c index f63fa5e0278c..9a8d5ad1dd70 100644 --- a/drivers/pci/host/pcie-xilinx.c +++ b/drivers/pci/host/pcie-xilinx.c @@ -369,6 +369,7 @@ static int xilinx_pcie_intx_map(struct irq_domain *domain, unsigned int irq, /* INTx IRQ Domain operations */ static const struct irq_domain_ops intx_domain_ops = { .map = xilinx_pcie_intx_map, + .xlate = pci_irqd_intx_xlate, }; /* PCIe HW Functions */ @@ -440,8 +441,8 @@ static irqreturn_t xilinx_pcie_intr_handler(int irq, void *data) XILINX_PCIE_REG_RPIFR1); /* Handle INTx Interrupt */ - val = ((val & XILINX_PCIE_RPIFR1_INTR_MASK) >> - XILINX_PCIE_RPIFR1_INTR_SHIFT) + 1; + val = (val & XILINX_PCIE_RPIFR1_INTR_MASK) >> + XILINX_PCIE_RPIFR1_INTR_SHIFT; generic_handle_irq(irq_find_mapping(port->leg_domain, val)); } @@ -524,7 +525,7 @@ static int xilinx_pcie_init_irq_domain(struct xilinx_pcie_port *port) return -ENODEV; } - port->leg_domain = irq_domain_add_linear(pcie_intc_node, 4, + port->leg_domain = irq_domain_add_linear(pcie_intc_node, PCI_NUM_INTX, &intx_domain_ops, port); if (!port->leg_domain) { From b8550f11bd40b7b6fd4622be4fd0f5aa958faee2 Mon Sep 17 00:00:00 2001 From: Paul Burton Date: Tue, 15 Aug 2017 16:25:15 -0500 Subject: [PATCH 2/6] PCI: xilinx-nwl: Translate INTx range to hwirqs 0-3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The devicetree binding documentation for the Xilinx NWL PCIe root port bridge shows an example which uses an interrupt-map property to map PCI INTx interrupts to hardware IRQ numbers 1-4. The driver creates an IRQ domain with size 4, which therefore covers the hwirq range 0-3. This means that if we attempt to make use of the INTD interrupt then we're likely to hit a WARN() in irq_domain_associate() because INTD, or hwirw=4, is outside of the range covered by the IRQ domain. irq_domain_associate() will then return -EINVAL and we'll be unable to make use of INTD. Fix this by making use of the pci_irqd_intx_xlate() helper function to translate the 1-4 range used in the DT to a 0-3 range used within the driver, and stop adding 1 to decoded hwirq numbers. Whilst cleaning up INTx handling we make use of the new PCI_NUM_INTX macro & drop the custom INTX definitions. Signed-off-by: Paul Burton Signed-off-by: Bjorn Helgaas Cc: Michal Simek Cc: "Sören Brinkmann" --- drivers/pci/host/pcie-xilinx-nwl.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/pci/host/pcie-xilinx-nwl.c b/drivers/pci/host/pcie-xilinx-nwl.c index eec641a34fc5..573847f4b9bc 100644 --- a/drivers/pci/host/pcie-xilinx-nwl.c +++ b/drivers/pci/host/pcie-xilinx-nwl.c @@ -133,7 +133,6 @@ #define CFG_DMA_REG_BAR GENMASK(2, 0) #define INT_PCI_MSI_NR (2 * 32) -#define INTX_NUM 4 /* Readin the PS_LINKUP */ #define PS_LINKUP_OFFSET 0x00000238 @@ -334,9 +333,8 @@ static void nwl_pcie_leg_handler(struct irq_desc *desc) while ((status = nwl_bridge_readl(pcie, MSGF_LEG_STATUS) & MSGF_LEG_SR_MASKALL) != 0) { - for_each_set_bit(bit, &status, INTX_NUM) { - virq = irq_find_mapping(pcie->legacy_irq_domain, - bit + 1); + for_each_set_bit(bit, &status, PCI_NUM_INTX) { + virq = irq_find_mapping(pcie->legacy_irq_domain, bit); if (virq) generic_handle_irq(virq); } @@ -436,6 +434,7 @@ static int nwl_legacy_map(struct irq_domain *domain, unsigned int irq, static const struct irq_domain_ops legacy_domain_ops = { .map = nwl_legacy_map, + .xlate = pci_irqd_intx_xlate, }; #ifdef CONFIG_PCI_MSI @@ -559,7 +558,7 @@ static int nwl_pcie_init_irq_domain(struct nwl_pcie *pcie) } pcie->legacy_irq_domain = irq_domain_add_linear(legacy_intc_node, - INTX_NUM, + PCI_NUM_INTX, &legacy_domain_ops, pcie); From d0b5dda62eb8aa535596952bffbf86b60a258672 Mon Sep 17 00:00:00 2001 From: Paul Burton Date: Tue, 15 Aug 2017 16:25:21 -0500 Subject: [PATCH 3/6] PCI: xilinx: Unify INTx & MSI interrupt decode The INTx & MSI interrupt decode paths duplicated a fair bit of common functionality. They also strictly handled interrupts in order of INTx then MSI, so if both types of interrupt were to be asserted simultaneously and the MSI interrupt were first in the FIFO then the INTx code would read it & ignore it before the MSI code then had to read it again, wasting the original FIFO read. Unify the INTx & MSI decode in order to reduce that duplication & allow a single FIFO read to be performed for each interrupt regardless of its type. Signed-off-by: Paul Burton Signed-off-by: Bjorn Helgaas Cc: Bharat Kumar Gogada Cc: Michal Simek Cc: Ravikiran Gummaluri --- drivers/pci/host/pcie-xilinx.c | 48 +++++++++++----------------------- 1 file changed, 15 insertions(+), 33 deletions(-) diff --git a/drivers/pci/host/pcie-xilinx.c b/drivers/pci/host/pcie-xilinx.c index 9a8d5ad1dd70..a7880bed56fc 100644 --- a/drivers/pci/host/pcie-xilinx.c +++ b/drivers/pci/host/pcie-xilinx.c @@ -385,7 +385,7 @@ static irqreturn_t xilinx_pcie_intr_handler(int irq, void *data) { struct xilinx_pcie_port *port = (struct xilinx_pcie_port *)data; struct device *dev = port->dev; - u32 val, mask, status, msi_data; + u32 val, mask, status; /* Read interrupt decode and mask registers */ val = pcie_read(port, XILINX_PCIE_REG_IDR); @@ -425,8 +425,7 @@ static irqreturn_t xilinx_pcie_intr_handler(int irq, void *data) xilinx_pcie_clear_err_interrupts(port); } - if (status & XILINX_PCIE_INTR_INTX) { - /* INTx interrupt received */ + if (status & (XILINX_PCIE_INTR_INTX | XILINX_PCIE_INTR_MSI)) { val = pcie_read(port, XILINX_PCIE_REG_RPIFR1); /* Check whether interrupt valid */ @@ -435,41 +434,24 @@ static irqreturn_t xilinx_pcie_intr_handler(int irq, void *data) goto error; } - if (!(val & XILINX_PCIE_RPIFR1_MSI_INTR)) { - /* Clear interrupt FIFO register 1 */ - pcie_write(port, XILINX_PCIE_RPIFR1_ALL_MASK, - XILINX_PCIE_REG_RPIFR1); - - /* Handle INTx Interrupt */ + /* Decode the IRQ number */ + if (val & XILINX_PCIE_RPIFR1_MSI_INTR) { + val = pcie_read(port, XILINX_PCIE_REG_RPIFR2) & + XILINX_PCIE_RPIFR2_MSG_DATA; + } else { val = (val & XILINX_PCIE_RPIFR1_INTR_MASK) >> XILINX_PCIE_RPIFR1_INTR_SHIFT; - generic_handle_irq(irq_find_mapping(port->leg_domain, - val)); - } - } - - if (status & XILINX_PCIE_INTR_MSI) { - /* MSI Interrupt */ - val = pcie_read(port, XILINX_PCIE_REG_RPIFR1); - - if (!(val & XILINX_PCIE_RPIFR1_INTR_VALID)) { - dev_warn(dev, "RP Intr FIFO1 read error\n"); - goto error; + val = irq_find_mapping(port->leg_domain, val); } - if (val & XILINX_PCIE_RPIFR1_MSI_INTR) { - msi_data = pcie_read(port, XILINX_PCIE_REG_RPIFR2) & - XILINX_PCIE_RPIFR2_MSG_DATA; + /* Clear interrupt FIFO register 1 */ + pcie_write(port, XILINX_PCIE_RPIFR1_ALL_MASK, + XILINX_PCIE_REG_RPIFR1); - /* Clear interrupt FIFO register 1 */ - pcie_write(port, XILINX_PCIE_RPIFR1_ALL_MASK, - XILINX_PCIE_REG_RPIFR1); - - if (IS_ENABLED(CONFIG_PCI_MSI)) { - /* Handle MSI Interrupt */ - generic_handle_irq(msi_data); - } - } + /* Handle the interrupt */ + if (IS_ENABLED(CONFIG_PCI_MSI) || + !(val & XILINX_PCIE_RPIFR1_MSI_INTR)) + generic_handle_irq(val); } if (status & XILINX_PCIE_INTR_SLV_UNSUPP) From aac2e96bf9cce2bc61c13709d361875d2dbf098d Mon Sep 17 00:00:00 2001 From: Paul Burton Date: Tue, 15 Aug 2017 16:25:25 -0500 Subject: [PATCH 4/6] PCI: xilinx: Don't enable config completion interrupts The Xilinx AXI bridge for PCI Express device provides interrupts indicating the completion of config space accesses. We have previously enabled/unmasked them but do nothing with them besides acknowledge them. Leave the interrupts masked in order to avoid servicing a large number of pointless interrupts during boot. Signed-off-by: Paul Burton Signed-off-by: Bjorn Helgaas Cc: Bharat Kumar Gogada Cc: Michal Simek Cc: Ravikiran Gummaluri --- drivers/pci/host/pcie-xilinx.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/pci/host/pcie-xilinx.c b/drivers/pci/host/pcie-xilinx.c index a7880bed56fc..f30d03309c7f 100644 --- a/drivers/pci/host/pcie-xilinx.c +++ b/drivers/pci/host/pcie-xilinx.c @@ -60,6 +60,7 @@ #define XILINX_PCIE_INTR_MST_SLVERR BIT(27) #define XILINX_PCIE_INTR_MST_ERRP BIT(28) #define XILINX_PCIE_IMR_ALL_MASK 0x1FF30FED +#define XILINX_PCIE_IMR_ENABLE_MASK 0x1FF30F0D #define XILINX_PCIE_IDR_ALL_MASK 0xFFFFFFFF /* Root Port Error FIFO Read Register definitions */ @@ -554,8 +555,8 @@ static void xilinx_pcie_init_port(struct xilinx_pcie_port *port) XILINX_PCIE_IMR_ALL_MASK, XILINX_PCIE_REG_IDR); - /* Enable all interrupts */ - pcie_write(port, XILINX_PCIE_IMR_ALL_MASK, XILINX_PCIE_REG_IMR); + /* Enable all interrupts we handle */ + pcie_write(port, XILINX_PCIE_IMR_ENABLE_MASK, XILINX_PCIE_REG_IMR); /* Enable the Bridge enable bit */ pcie_write(port, pcie_read(port, XILINX_PCIE_REG_RPSC) | From 293962d678c08364981f19c0d0ea3f877888242f Mon Sep 17 00:00:00 2001 From: Paul Burton Date: Tue, 15 Aug 2017 16:25:29 -0500 Subject: [PATCH 5/6] PCI: xilinx: Allow build on MIPS platforms Allow the xilinx-pcie driver to be built on MIPS platforms which make use of generic PCI drivers rather than legacy MIPS-specific interfaces. This is used on the MIPS Boston development board. Signed-off-by: Paul Burton Signed-off-by: Bjorn Helgaas Cc: Bharat Kumar Gogada Cc: Michal Simek Cc: Ravikiran Gummaluri --- drivers/pci/host/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig index 89d61c2cbfaa..eb8c30828011 100644 --- a/drivers/pci/host/Kconfig +++ b/drivers/pci/host/Kconfig @@ -71,7 +71,7 @@ config PCI_HOST_GENERIC config PCIE_XILINX bool "Xilinx AXI PCIe host bridge support" - depends on ARCH_ZYNQ || MICROBLAZE + depends on ARCH_ZYNQ || MICROBLAZE || (MIPS && PCI_DRIVERS_GENERIC) help Say 'Y' here if you want kernel to support the Xilinx AXI PCIe Host Bridge driver. From 5fd4bf6a659e45251a91b04485318570554651b2 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Thu, 31 Aug 2017 14:52:10 -0300 Subject: [PATCH 6/6] PCI: xilinx-nwl: Fix platform_get_irq() error handling When platform_get_irq() fails we should propagate the real error value instead of always returning -EINVAL. Signed-off-by: Fabio Estevam Signed-off-by: Bjorn Helgaas Cc: Michal Simek --- drivers/pci/host/pcie-xilinx-nwl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/host/pcie-xilinx-nwl.c b/drivers/pci/host/pcie-xilinx-nwl.c index 573847f4b9bc..65dea98b2643 100644 --- a/drivers/pci/host/pcie-xilinx-nwl.c +++ b/drivers/pci/host/pcie-xilinx-nwl.c @@ -812,7 +812,7 @@ static int nwl_pcie_parse_dt(struct nwl_pcie *pcie, pcie->irq_intx = platform_get_irq_byname(pdev, "intx"); if (pcie->irq_intx < 0) { dev_err(dev, "failed to get intx IRQ %d\n", pcie->irq_intx); - return -EINVAL; + return pcie->irq_intx; } irq_set_chained_handler_and_data(pcie->irq_intx,