From ae975c08eac62ce887e8b24f911b2e97f4ebb73c Mon Sep 17 00:00:00 2001 From: nbd Date: Tue, 2 Sep 2014 17:12:34 +0000 Subject: [PATCH] cns3xxx: support isolated PCI interrupts on newer Laguna PCBs The cns3xxx uses irq61 for pcie0_intr which in the case of a PCIe-to-PCI bridge ends up combining INTA/B/C/D on a single ARM CPU interrupt. This is not optimal when you have multiple cores. To overcome this limitation an enhancement was made on newer Laguna PCB's that support miniPCI cards to route the INTA/B/C/D signals to unique external ARM CPU interrupts which can help balance CPU core utilization and in some cases increase overall system performance or responsiveness. For more details see: http://trac.gateworks.com/wiki/multicoreprocessing#PCIInterruptsteering Signed-off-by: Tim Harvey Backport of r42400 git-svn-id: svn://svn.openwrt.org/openwrt/branches/barrier_breaker@42402 3c298f89-4303-0410-b956-a3cf2f4a3e73 --- .../310-pci_isolated_interrupts.patch | 196 ++++++++++++++++++ 1 file changed, 196 insertions(+) create mode 100644 target/linux/cns3xxx/patches-3.10/310-pci_isolated_interrupts.patch diff --git a/target/linux/cns3xxx/patches-3.10/310-pci_isolated_interrupts.patch b/target/linux/cns3xxx/patches-3.10/310-pci_isolated_interrupts.patch new file mode 100644 index 0000000..03f0009 --- /dev/null +++ b/target/linux/cns3xxx/patches-3.10/310-pci_isolated_interrupts.patch @@ -0,0 +1,196 @@ +--- a/arch/arm/mach-cns3xxx/cns3420vb.c ++++ b/arch/arm/mach-cns3xxx/cns3420vb.c +@@ -274,7 +274,7 @@ static int __init cns3420vb_pcie_init(vo + if (!machine_is_cns3420vb()) + return 0; + +- return cns3xxx_pcie_init(); ++ return cns3xxx_pcie_init(NULL, NULL); + } + subsys_initcall(cns3420vb_pcie_init); + +--- a/arch/arm/mach-cns3xxx/core.h ++++ b/arch/arm/mach-cns3xxx/core.h +@@ -17,7 +17,7 @@ extern void cns3xxx_pcie_iotable_init(vo + + void __init cns3xxx_map_io(void); + void __init cns3xxx_init_irq(void); +-int __init cns3xxx_pcie_init(void); ++int __init cns3xxx_pcie_init(int *pcie0_irqs, int *pcie1_irqs); + void cns3xxx_power_off(void); + void cns3xxx_restart(char, const char *); + +--- a/arch/arm/mach-cns3xxx/laguna.c ++++ b/arch/arm/mach-cns3xxx/laguna.c +@@ -21,6 +21,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -869,12 +870,45 @@ static int laguna_register_gpio(struct g + return ret; + } + ++/* allow disabling of external isolated PCIe IRQs */ ++static int cns3xxx_pciextirq = 1; ++static int __init cns3xxx_pciextirq_disable(char *s) ++{ ++ cns3xxx_pciextirq = 0; ++ return 1; ++} ++__setup("noextirq", cns3xxx_pciextirq_disable); ++ + static int __init laguna_pcie_init(void) + { ++ u32 __iomem *mem = (void __iomem *)(CNS3XXX_GPIOB_BASE_VIRT + 0x0004); ++ u32 reg = (__raw_readl(mem) >> 26) & 0xf; ++ int irqs[] = { ++ IRQ_CNS3XXX_EXTERNAL_PIN0, ++ IRQ_CNS3XXX_EXTERNAL_PIN1, ++ IRQ_CNS3XXX_EXTERNAL_PIN2, ++ 154, ++ }; ++ + if (!machine_is_gw2388()) + return 0; + +- return cns3xxx_pcie_init(); ++ /* Verify GPIOB[26:29] == 0001b indicating support for ext irqs */ ++ if (cns3xxx_pciextirq && reg != 1) ++ cns3xxx_pciextirq = 0; ++ ++ if (cns3xxx_pciextirq) { ++ printk("laguna: using isolated PCI interrupts:" ++ " irq%d/irq%d/irq%d/irq%d\n", ++ irqs[0], irqs[1], irqs[2], irqs[3]); ++ return cns3xxx_pcie_init(irqs, NULL); ++ } ++ printk("laguna: using shared PCI interrupts: irq%d\n", ++ IRQ_CNS3XXX_PCIE0_DEVICE); ++ irqs[0] = IRQ_CNS3XXX_PCIE0_DEVICE; ++ irqs[1] = IRQ_CNS3XXX_PCIE0_DEVICE; ++ irqs[2] = IRQ_CNS3XXX_PCIE0_DEVICE; ++ return cns3xxx_pcie_init(irqs, NULL); + } + subsys_initcall(laguna_pcie_init); + +@@ -889,8 +923,33 @@ static int __init laguna_model_setup(voi + printk("Running on Gateworks Laguna %s\n", laguna_info.model); + cns3xxx_gpio_init( 0, 32, CNS3XXX_GPIOA_BASE_VIRT, IRQ_CNS3XXX_GPIOA, + NR_IRQS_CNS3XXX); +- cns3xxx_gpio_init(32, 32, CNS3XXX_GPIOB_BASE_VIRT, IRQ_CNS3XXX_GPIOB, +- NR_IRQS_CNS3XXX + 32); ++ ++ /* ++ * If pcie external interrupts are supported and desired ++ * configure IRQ types and configure pin function. ++ * Note that cns3xxx_pciextirq is enabled by default, but can be ++ * unset via the 'noextirq' kernel param or by laguna_pcie_init() if ++ * the baseboard model does not support this hardware feature. ++ */ ++ if (cns3xxx_pciextirq) { ++ mem = (void __iomem *)(CNS3XXX_MISC_BASE_VIRT + 0x0018); ++ reg = __raw_readl(mem); ++ /* GPIO26 is gpio, EXT_INT[0:2] not gpio func */ ++ reg &= ~0x3c000000; ++ reg |= 0x38000000; ++ __raw_writel(reg, mem); ++ ++ cns3xxx_gpio_init(32, 32, CNS3XXX_GPIOB_BASE_VIRT, ++ IRQ_CNS3XXX_GPIOB, NR_IRQS_CNS3XXX + 32); ++ ++ irq_set_irq_type(154, IRQ_TYPE_LEVEL_LOW); ++ irq_set_irq_type(93, IRQ_TYPE_LEVEL_HIGH); ++ irq_set_irq_type(94, IRQ_TYPE_LEVEL_HIGH); ++ irq_set_irq_type(95, IRQ_TYPE_LEVEL_HIGH); ++ } else { ++ cns3xxx_gpio_init(32, 32, CNS3XXX_GPIOB_BASE_VIRT, ++ IRQ_CNS3XXX_GPIOB, NR_IRQS_CNS3XXX + 32); ++ } + + if (strncmp(laguna_info.model, "GW", 2) == 0) { + if (laguna_info.config_bitmap & ETH0_LOAD) +--- a/arch/arm/mach-cns3xxx/pcie.c ++++ b/arch/arm/mach-cns3xxx/pcie.c +@@ -18,6 +18,7 @@ + #include + #include + #include ++#include + #include + #include + #include "cns3xxx.h" +@@ -32,7 +33,7 @@ enum cns3xxx_access_type { + + struct cns3xxx_pcie { + struct map_desc cfg_bases[CNS3XXX_NUM_ACCESS_TYPES]; +- unsigned int irqs[2]; ++ unsigned int irqs[6]; + struct resource res_io; + struct resource res_mem; + struct hw_pci hw_pci; +@@ -255,7 +256,7 @@ static struct pci_ops cns3xxx_pcie_ops = + static int cns3xxx_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) + { + struct cns3xxx_pcie *cnspci = pdev_to_cnspci(dev); +- int irq = cnspci->irqs[slot]; ++ int irq = cnspci->irqs[slot+pin-1]; + + pr_info("PCIe map irq: %04d:%02x:%02x.%02x slot %d, pin %d, irq: %d\n", + pci_domain_nr(dev->bus), dev->bus->number, PCI_SLOT(dev->devfn), +@@ -298,7 +299,12 @@ static struct cns3xxx_pcie cns3xxx_pcie[ + .end = CNS3XXX_PCIE0_MEM_BASE + SZ_16M - 1, + .flags = IORESOURCE_MEM, + }, +- .irqs = { IRQ_CNS3XXX_PCIE0_RC, IRQ_CNS3XXX_PCIE0_DEVICE, }, ++ .irqs = { IRQ_CNS3XXX_PCIE0_RC, ++ IRQ_CNS3XXX_PCIE0_DEVICE, ++ IRQ_CNS3XXX_PCIE0_DEVICE, ++ IRQ_CNS3XXX_PCIE0_DEVICE, ++ IRQ_CNS3XXX_PCIE0_DEVICE, ++ }, + .hw_pci = { + .domain = 0, + .nr_controllers = 1, +@@ -340,7 +346,13 @@ static struct cns3xxx_pcie cns3xxx_pcie[ + .end = CNS3XXX_PCIE1_MEM_BASE + SZ_16M - 1, + .flags = IORESOURCE_MEM, + }, +- .irqs = { IRQ_CNS3XXX_PCIE1_RC, IRQ_CNS3XXX_PCIE1_DEVICE, }, ++ .irqs = { ++ IRQ_CNS3XXX_PCIE1_RC, ++ IRQ_CNS3XXX_PCIE1_DEVICE, ++ IRQ_CNS3XXX_PCIE1_DEVICE, ++ IRQ_CNS3XXX_PCIE1_DEVICE, ++ IRQ_CNS3XXX_PCIE1_DEVICE, ++ }, + .hw_pci = { + .domain = 1, + .nr_controllers = 1, +@@ -460,13 +472,22 @@ void __init cns3xxx_pcie_iotable_init() + } + } + +-int __init cns3xxx_pcie_init(void) ++int __init cns3xxx_pcie_init(int *pcie0_irqs, int *pcie1_irqs) + { + int i; + + pcibios_min_io = 0; + pcibios_min_mem = 0; + ++ if (pcie0_irqs) { ++ for (i = 0; i < 4; i++) ++ cns3xxx_pcie[0].irqs[i+1] = pcie0_irqs[i]; ++ } ++ if (pcie1_irqs) { ++ for (i = 0; i < 4; i++) ++ cns3xxx_pcie[1].irqs[i+1] = pcie1_irqs[i]; ++ } ++ + hook_fault_code(16 + 6, cns3xxx_pcie_abort_handler, SIGBUS, 0, + "imprecise external abort"); +