openwrt/target/linux/cns3xxx/patches-3.10/310-pci_isolated_interrupts.patch
Felix Fietkau 736419452b cns3xxx: fix shared PCI interrupt mapping
This patch originally failed to combine INTA/B/C/D onto a single ARM CPU
interrupt. Instead, it mapped INTA/B/C and excluded D. This patch
corrects the issue by mapping all four interrupts to the single ARM CPU
interrupt. The original intent of the patch still holds as the newer PCB
take advantage of isolated interrupts. This fix only applies to older
PCB's that do not route INTA/B/C/D to unique external ARM CPU
interrupts.

Signed-off-by: Pushpal Sidhu <psidhu@gateworks.com>

SVN-Revision: 42830
2014-10-07 10:37:48 +00:00

194 lines
5.8 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,42 @@ 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);
+ return cns3xxx_pcie_init(NULL, 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");