aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/cns3xxx/patches-4.4/110-pci_isolated_interrupts.patch
blob: f25b171938dd2e7b663be5904d81b869040d1903 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
--- 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>
@@ -872,6 +873,47 @@ 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_irq(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;
+
+	/* 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]);
+		cns3xxx_pcie_set_irqs(0, irqs);
+	} else {
+		printk("laguna: using shared PCI interrupts: irq%d\n",
+		       IRQ_CNS3XXX_PCIE0_DEVICE);
+	}
+
+	return 0;
+}
+subsys_initcall(laguna_pcie_init_irq);
+
 static int __init laguna_model_setup(void)
 {
 	u32 __iomem *mem;
@@ -883,8 +925,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"
@@ -27,7 +28,7 @@ struct cns3xxx_pcie {
 	void __iomem *host_regs; /* PCI config registers for host bridge */
 	void __iomem *cfg0_regs; /* PCI Type 0 config registers */
 	void __iomem *cfg1_regs; /* PCI Type 1 config registers */
-	unsigned int irqs[2];
+	unsigned int irqs[5];
 	struct resource res_io;
 	struct resource res_mem;
 	int port;
@@ -95,7 +96,7 @@ static inline int check_master_abort(str
 		void __iomem *host_base;
 		u32 sreg, ereg;
 
-		host_base = (void __iomem *) cnspci->cfg_bases[CNS3XXX_HOST_TYPE].virtual;
+		host_base = (void __iomem *) cnspci->host_regs;
 		sreg = __raw_readw(host_base + 0x6) & 0xF900;
 		ereg = __raw_readl(host_base + 0x104); // Uncorrectable Error Status Reg
 
@@ -209,7 +210,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[!!dev->bus->number];
+	int irq = cnspci->irqs[!!dev->bus->number + 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),
@@ -235,7 +236,13 @@ static struct cns3xxx_pcie cns3xxx_pcie[
 			.end = CNS3XXX_PCIE0_HOST_BASE - 1, /* 176 MiB */
 			.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,
+		},
 		.port = 0,
 	},
 	[1] = {
@@ -254,7 +261,13 @@ static struct cns3xxx_pcie cns3xxx_pcie[
 			.end = CNS3XXX_PCIE1_HOST_BASE - 1, /* 176 MiB */
 			.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,
+		},
 		.port = 1,
 	},
 };
@@ -346,6 +359,14 @@ static int cns3xxx_pcie_abort_handler(un
 	return 0;
 }
 
+void __init cns3xxx_pcie_set_irqs(int bus, int *irqs)
+{
+	int i;
+
+	for (i = 0; i < 4; i++)
+		cns3xxx_pcie[bus].irqs[i + 1] = irqs[i];
+}
+
 void __init cns3xxx_pcie_init_late(void)
 {
 	int i;
--- a/arch/arm/mach-cns3xxx/core.h
+++ b/arch/arm/mach-cns3xxx/core.h
@@ -18,8 +18,10 @@ extern void cns3xxx_timer_init(void);
 
 #ifdef CONFIG_PCI
 extern void __init cns3xxx_pcie_init_late(void);
+extern void __init cns3xxx_pcie_set_irqs(int bus, int *irqs);
 #else
 static inline void __init cns3xxx_pcie_init_late(void) {}
+static inline void cns3xxx_pcie_set_irqs(int bus, int *irqs) {}
 #endif
 
 void __init cns3xxx_map_io(void);