diff options
Diffstat (limited to 'arch/powerpc/sysdev/i8259.c')
-rw-r--r-- | arch/powerpc/sysdev/i8259.c | 141 |
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 | ||
15 | static volatile void __iomem *pci_intack; /* RO, gives us the irq vector */ | 20 | static 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 | ||
21 | static DEFINE_SPINLOCK(i8259_lock); | 26 | static DEFINE_SPINLOCK(i8259_lock); |
22 | 27 | ||
23 | static int i8259_pic_irq_offset; | 28 | static struct device_node *i8259_node; |
29 | static 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 | */ |
31 | int i8259_irq(struct pt_regs *regs) | 37 | unsigned 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 | ||
72 | static void i8259_mask_and_ack_irq(unsigned int irq_nr) | 82 | static 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 | ||
155 | static struct irqaction i8259_irqaction = { | 166 | static 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 | |
171 | static 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 | |||
188 | static 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 | |||
200 | static 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 | |||
220 | static 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 | */ |
167 | void __init i8259_init(unsigned long intack_addr, int offset) | 234 | void 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 | } |