aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/sysdev/i8259.c
diff options
context:
space:
mode:
authorBenjamin Herrenschmidt <benh@kernel.crashing.org>2006-07-03 07:36:01 -0400
committerPaul Mackerras <paulus@samba.org>2006-07-03 07:36:01 -0400
commit0ebfff1491ef85d41ddf9c633834838be144f69f (patch)
tree5b469a6d61a9fcfbf94e7b6d411e544dbdec8dec /arch/powerpc/sysdev/i8259.c
parentf63e115fb50db39706b955b81e3375ef6bab2268 (diff)
[POWERPC] Add new interrupt mapping core and change platforms to use it
This adds the new irq remapper core and removes the old one. Because there are some fundamental conflicts with the old code, like the value of NO_IRQ which I'm now setting to 0 (as per discussions with Linus), etc..., this commit also changes the relevant platform and driver code over to use the new remapper (so as not to cause difficulties later in bisecting). This patch removes the old pre-parsing of the open firmware interrupt tree along with all the bogus assumptions it made to try to renumber interrupts according to the platform. This is all to be handled by the new code now. For the pSeries XICS interrupt controller, a single remapper host is created for the whole machine regardless of how many interrupt presentation and source controllers are found, and it's set to match any device node that isn't a 8259. That works fine on pSeries and avoids having to deal with some of the complexities of split source controllers vs. presentation controllers in the pSeries device trees. The powerpc i8259 PIC driver now always requests the legacy interrupt range. It also has the feature of being able to match any device node (including NULL) if passed no device node as an input. That will help porting over platforms with broken device-trees like Pegasos who don't have a proper interrupt tree. Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org> Signed-off-by: Paul Mackerras <paulus@samba.org>
Diffstat (limited to 'arch/powerpc/sysdev/i8259.c')
-rw-r--r--arch/powerpc/sysdev/i8259.c141
1 files changed, 112 insertions, 29 deletions
diff --git a/arch/powerpc/sysdev/i8259.c b/arch/powerpc/sysdev/i8259.c
index c2e9465871aa..72c73a6105cd 100644
--- a/arch/powerpc/sysdev/i8259.c
+++ b/arch/powerpc/sysdev/i8259.c
@@ -6,11 +6,16 @@
6 * as published by the Free Software Foundation; either version 6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version. 7 * 2 of the License, or (at your option) any later version.
8 */ 8 */
9#undef DEBUG
10
9#include <linux/init.h> 11#include <linux/init.h>
10#include <linux/ioport.h> 12#include <linux/ioport.h>
11#include <linux/interrupt.h> 13#include <linux/interrupt.h>
14#include <linux/kernel.h>
15#include <linux/delay.h>
12#include <asm/io.h> 16#include <asm/io.h>
13#include <asm/i8259.h> 17#include <asm/i8259.h>
18#include <asm/prom.h>
14 19
15static volatile void __iomem *pci_intack; /* RO, gives us the irq vector */ 20static volatile void __iomem *pci_intack; /* RO, gives us the irq vector */
16 21
@@ -20,7 +25,8 @@ static unsigned char cached_8259[2] = { 0xff, 0xff };
20 25
21static DEFINE_SPINLOCK(i8259_lock); 26static DEFINE_SPINLOCK(i8259_lock);
22 27
23static int i8259_pic_irq_offset; 28static struct device_node *i8259_node;
29static struct irq_host *i8259_host;
24 30
25/* 31/*
26 * Acknowledge the IRQ using either the PCI host bridge's interrupt 32 * Acknowledge the IRQ using either the PCI host bridge's interrupt
@@ -28,16 +34,18 @@ static int i8259_pic_irq_offset;
28 * which is called. It should be noted that polling is broken on some 34 * which is called. It should be noted that polling is broken on some
29 * IBM and Motorola PReP boxes so we must use the int-ack feature on them. 35 * IBM and Motorola PReP boxes so we must use the int-ack feature on them.
30 */ 36 */
31int i8259_irq(struct pt_regs *regs) 37unsigned int i8259_irq(struct pt_regs *regs)
32{ 38{
33 int irq; 39 int irq;
34 40 int lock = 0;
35 spin_lock(&i8259_lock);
36 41
37 /* Either int-ack or poll for the IRQ */ 42 /* Either int-ack or poll for the IRQ */
38 if (pci_intack) 43 if (pci_intack)
39 irq = readb(pci_intack); 44 irq = readb(pci_intack);
40 else { 45 else {
46 spin_lock(&i8259_lock);
47 lock = 1;
48
41 /* Perform an interrupt acknowledge cycle on controller 1. */ 49 /* Perform an interrupt acknowledge cycle on controller 1. */
42 outb(0x0C, 0x20); /* prepare for poll */ 50 outb(0x0C, 0x20); /* prepare for poll */
43 irq = inb(0x20) & 7; 51 irq = inb(0x20) & 7;
@@ -62,11 +70,13 @@ int i8259_irq(struct pt_regs *regs)
62 if (!pci_intack) 70 if (!pci_intack)
63 outb(0x0B, 0x20); /* ISR register */ 71 outb(0x0B, 0x20); /* ISR register */
64 if(~inb(0x20) & 0x80) 72 if(~inb(0x20) & 0x80)
65 irq = -1; 73 irq = NO_IRQ;
66 } 74 } else if (irq == 0xff)
75 irq = NO_IRQ;
67 76
68 spin_unlock(&i8259_lock); 77 if (lock)
69 return irq + i8259_pic_irq_offset; 78 spin_unlock(&i8259_lock);
79 return irq;
70} 80}
71 81
72static void i8259_mask_and_ack_irq(unsigned int irq_nr) 82static void i8259_mask_and_ack_irq(unsigned int irq_nr)
@@ -74,7 +84,6 @@ static void i8259_mask_and_ack_irq(unsigned int irq_nr)
74 unsigned long flags; 84 unsigned long flags;
75 85
76 spin_lock_irqsave(&i8259_lock, flags); 86 spin_lock_irqsave(&i8259_lock, flags);
77 irq_nr -= i8259_pic_irq_offset;
78 if (irq_nr > 7) { 87 if (irq_nr > 7) {
79 cached_A1 |= 1 << (irq_nr-8); 88 cached_A1 |= 1 << (irq_nr-8);
80 inb(0xA1); /* DUMMY */ 89 inb(0xA1); /* DUMMY */
@@ -100,8 +109,9 @@ static void i8259_mask_irq(unsigned int irq_nr)
100{ 109{
101 unsigned long flags; 110 unsigned long flags;
102 111
112 pr_debug("i8259_mask_irq(%d)\n", irq_nr);
113
103 spin_lock_irqsave(&i8259_lock, flags); 114 spin_lock_irqsave(&i8259_lock, flags);
104 irq_nr -= i8259_pic_irq_offset;
105 if (irq_nr < 8) 115 if (irq_nr < 8)
106 cached_21 |= 1 << irq_nr; 116 cached_21 |= 1 << irq_nr;
107 else 117 else
@@ -114,8 +124,9 @@ static void i8259_unmask_irq(unsigned int irq_nr)
114{ 124{
115 unsigned long flags; 125 unsigned long flags;
116 126
127 pr_debug("i8259_unmask_irq(%d)\n", irq_nr);
128
117 spin_lock_irqsave(&i8259_lock, flags); 129 spin_lock_irqsave(&i8259_lock, flags);
118 irq_nr -= i8259_pic_irq_offset;
119 if (irq_nr < 8) 130 if (irq_nr < 8)
120 cached_21 &= ~(1 << irq_nr); 131 cached_21 &= ~(1 << irq_nr);
121 else 132 else
@@ -152,25 +163,84 @@ static struct resource pic_edgectrl_iores = {
152 .flags = IORESOURCE_BUSY, 163 .flags = IORESOURCE_BUSY,
153}; 164};
154 165
155static struct irqaction i8259_irqaction = { 166static int i8259_host_match(struct irq_host *h, struct device_node *node)
156 .handler = no_action, 167{
157 .flags = IRQF_DISABLED, 168 return i8259_node == NULL || i8259_node == node;
158 .mask = CPU_MASK_NONE, 169}
159 .name = "82c59 secondary cascade", 170
171static int i8259_host_map(struct irq_host *h, unsigned int virq,
172 irq_hw_number_t hw, unsigned int flags)
173{
174 pr_debug("i8259_host_map(%d, 0x%lx)\n", virq, hw);
175
176 /* We block the internal cascade */
177 if (hw == 2)
178 get_irq_desc(virq)->status |= IRQ_NOREQUEST;
179
180 /* We use the level stuff only for now, we might want to
181 * be more cautious here but that works for now
182 */
183 get_irq_desc(virq)->status |= IRQ_LEVEL;
184 set_irq_chip_and_handler(virq, &i8259_pic, handle_level_irq);
185 return 0;
186}
187
188static void i8259_host_unmap(struct irq_host *h, unsigned int virq)
189{
190 /* Make sure irq is masked in hardware */
191 i8259_mask_irq(virq);
192
193 /* remove chip and handler */
194 set_irq_chip_and_handler(virq, NULL, NULL);
195
196 /* Make sure it's completed */
197 synchronize_irq(virq);
198}
199
200static int i8259_host_xlate(struct irq_host *h, struct device_node *ct,
201 u32 *intspec, unsigned int intsize,
202 irq_hw_number_t *out_hwirq, unsigned int *out_flags)
203{
204 static unsigned char map_isa_senses[4] = {
205 IRQ_TYPE_LEVEL_LOW,
206 IRQ_TYPE_LEVEL_HIGH,
207 IRQ_TYPE_EDGE_FALLING,
208 IRQ_TYPE_EDGE_RISING,
209 };
210
211 *out_hwirq = intspec[0];
212 if (intsize > 1 && intspec[1] < 4)
213 *out_flags = map_isa_senses[intspec[1]];
214 else
215 *out_flags = IRQ_TYPE_NONE;
216
217 return 0;
218}
219
220static struct irq_host_ops i8259_host_ops = {
221 .match = i8259_host_match,
222 .map = i8259_host_map,
223 .unmap = i8259_host_unmap,
224 .xlate = i8259_host_xlate,
160}; 225};
161 226
162/* 227/****
163 * i8259_init() 228 * i8259_init - Initialize the legacy controller
164 * intack_addr - PCI interrupt acknowledge (real) address which will return 229 * @node: device node of the legacy PIC (can be NULL, but then, it will match
165 * the active irq from the 8259 230 * all interrupts, so beware)
231 * @intack_addr: PCI interrupt acknowledge (real) address which will return
232 * the active irq from the 8259
166 */ 233 */
167void __init i8259_init(unsigned long intack_addr, int offset) 234void i8259_init(struct device_node *node, unsigned long intack_addr)
168{ 235{
169 unsigned long flags; 236 unsigned long flags;
170 int i;
171 237
238 /* initialize the controller */
172 spin_lock_irqsave(&i8259_lock, flags); 239 spin_lock_irqsave(&i8259_lock, flags);
173 i8259_pic_irq_offset = offset; 240
241 /* Mask all first */
242 outb(0xff, 0xA1);
243 outb(0xff, 0x21);
174 244
175 /* init master interrupt controller */ 245 /* init master interrupt controller */
176 outb(0x11, 0x20); /* Start init sequence */ 246 outb(0x11, 0x20); /* Start init sequence */
@@ -184,24 +254,36 @@ void __init i8259_init(unsigned long intack_addr, int offset)
184 outb(0x02, 0xA1); /* edge triggered, Cascade (slave) on IRQ2 */ 254 outb(0x02, 0xA1); /* edge triggered, Cascade (slave) on IRQ2 */
185 outb(0x01, 0xA1); /* Select 8086 mode */ 255 outb(0x01, 0xA1); /* Select 8086 mode */
186 256
257 /* That thing is slow */
258 udelay(100);
259
187 /* always read ISR */ 260 /* always read ISR */
188 outb(0x0B, 0x20); 261 outb(0x0B, 0x20);
189 outb(0x0B, 0xA0); 262 outb(0x0B, 0xA0);
190 263
191 /* Mask all interrupts */ 264 /* Unmask the internal cascade */
265 cached_21 &= ~(1 << 2);
266
267 /* Set interrupt masks */
192 outb(cached_A1, 0xA1); 268 outb(cached_A1, 0xA1);
193 outb(cached_21, 0x21); 269 outb(cached_21, 0x21);
194 270
195 spin_unlock_irqrestore(&i8259_lock, flags); 271 spin_unlock_irqrestore(&i8259_lock, flags);
196 272
197 for (i = 0; i < NUM_ISA_INTERRUPTS; ++i) { 273 /* create a legacy host */
198 set_irq_chip_and_handler(offset + i, &i8259_pic, 274 if (node)
199 handle_level_irq); 275 i8259_node = of_node_get(node);
200 irq_desc[offset + i].status |= IRQ_LEVEL; 276 i8259_host = irq_alloc_host(IRQ_HOST_MAP_LEGACY, 0, &i8259_host_ops, 0);
277 if (i8259_host == NULL) {
278 printk(KERN_ERR "i8259: failed to allocate irq host !\n");
279 return;
201 } 280 }
202 281
203 /* reserve our resources */ 282 /* reserve our resources */
204 setup_irq(offset + 2, &i8259_irqaction); 283 /* XXX should we continue doing that ? it seems to cause problems
284 * with further requesting of PCI IO resources for that range...
285 * need to look into it.
286 */
205 request_resource(&ioport_resource, &pic1_iores); 287 request_resource(&ioport_resource, &pic1_iores);
206 request_resource(&ioport_resource, &pic2_iores); 288 request_resource(&ioport_resource, &pic2_iores);
207 request_resource(&ioport_resource, &pic_edgectrl_iores); 289 request_resource(&ioport_resource, &pic_edgectrl_iores);
@@ -209,4 +291,5 @@ void __init i8259_init(unsigned long intack_addr, int offset)
209 if (intack_addr != 0) 291 if (intack_addr != 0)
210 pci_intack = ioremap(intack_addr, 1); 292 pci_intack = ioremap(intack_addr, 1);
211 293
294 printk(KERN_INFO "i8259 legacy interrupt controller initialized\n");
212} 295}