openwrt/target/linux/cns3xxx/patches-3.10/310-pci_isolated_interrupts.patch
Felix Fietkau 1839d1b912 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
2014-09-02 16:44:40 +00:00

197 lines
5.9 KiB
Diff

--- 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");