aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFelix Fietkau <nbd@openwrt.org>2014-09-02 16:44:40 +0000
committerFelix Fietkau <nbd@openwrt.org>2014-09-02 16:44:40 +0000
commit1839d1b912adbd416257521d8023807d047e496f (patch)
tree856bf3ce8a2231bde56a25fb7656b6fc2bf8d6df
parent734f7609b9cb41e0382187b4713113a6289ade60 (diff)
downloadupstream-1839d1b912adbd416257521d8023807d047e496f.tar.gz
upstream-1839d1b912adbd416257521d8023807d047e496f.tar.bz2
upstream-1839d1b912adbd416257521d8023807d047e496f.zip
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 <tharvey@gateworks.com> SVN-Revision: 42400
-rw-r--r--target/linux/cns3xxx/patches-3.10/310-pci_isolated_interrupts.patch196
1 files changed, 196 insertions, 0 deletions
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 0000000000..03f0009393
--- /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 <linux/kernel.h>
+ #include <linux/compiler.h>
+ #include <linux/io.h>
++#include <linux/irq.h>
+ #include <linux/gpio.h>
+ #include <linux/dma-mapping.h>
+ #include <linux/serial_core.h>
+@@ -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 <linux/io.h>
+ #include <linux/ioport.h>
+ #include <linux/interrupt.h>
++#include <linux/irq.h>
+ #include <linux/ptrace.h>
+ #include <asm/mach/map.h>
+ #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");
+