diff options
Diffstat (limited to 'arch/powerpc/sysdev/i8259.c')
| -rw-r--r-- | arch/powerpc/sysdev/i8259.c | 163 |
1 files changed, 118 insertions, 45 deletions
diff --git a/arch/powerpc/sysdev/i8259.c b/arch/powerpc/sysdev/i8259.c index 1a3ef1ab9d6e..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,16 +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); |
| 70 | } | 79 | return irq; |
| 71 | |||
| 72 | int i8259_irq_cascade(struct pt_regs *regs, void *unused) | ||
| 73 | { | ||
| 74 | return i8259_irq(regs); | ||
| 75 | } | 80 | } |
| 76 | 81 | ||
| 77 | static void i8259_mask_and_ack_irq(unsigned int irq_nr) | 82 | static void i8259_mask_and_ack_irq(unsigned int irq_nr) |
| @@ -79,7 +84,6 @@ static void i8259_mask_and_ack_irq(unsigned int irq_nr) | |||
| 79 | unsigned long flags; | 84 | unsigned long flags; |
| 80 | 85 | ||
| 81 | spin_lock_irqsave(&i8259_lock, flags); | 86 | spin_lock_irqsave(&i8259_lock, flags); |
| 82 | irq_nr -= i8259_pic_irq_offset; | ||
| 83 | if (irq_nr > 7) { | 87 | if (irq_nr > 7) { |
| 84 | cached_A1 |= 1 << (irq_nr-8); | 88 | cached_A1 |= 1 << (irq_nr-8); |
| 85 | inb(0xA1); /* DUMMY */ | 89 | inb(0xA1); /* DUMMY */ |
| @@ -105,8 +109,9 @@ static void i8259_mask_irq(unsigned int irq_nr) | |||
| 105 | { | 109 | { |
| 106 | unsigned long flags; | 110 | unsigned long flags; |
| 107 | 111 | ||
| 112 | pr_debug("i8259_mask_irq(%d)\n", irq_nr); | ||
| 113 | |||
| 108 | spin_lock_irqsave(&i8259_lock, flags); | 114 | spin_lock_irqsave(&i8259_lock, flags); |
| 109 | irq_nr -= i8259_pic_irq_offset; | ||
| 110 | if (irq_nr < 8) | 115 | if (irq_nr < 8) |
| 111 | cached_21 |= 1 << irq_nr; | 116 | cached_21 |= 1 << irq_nr; |
| 112 | else | 117 | else |
| @@ -119,8 +124,9 @@ static void i8259_unmask_irq(unsigned int irq_nr) | |||
| 119 | { | 124 | { |
| 120 | unsigned long flags; | 125 | unsigned long flags; |
| 121 | 126 | ||
| 127 | pr_debug("i8259_unmask_irq(%d)\n", irq_nr); | ||
| 128 | |||
| 122 | spin_lock_irqsave(&i8259_lock, flags); | 129 | spin_lock_irqsave(&i8259_lock, flags); |
| 123 | irq_nr -= i8259_pic_irq_offset; | ||
| 124 | if (irq_nr < 8) | 130 | if (irq_nr < 8) |
| 125 | cached_21 &= ~(1 << irq_nr); | 131 | cached_21 &= ~(1 << irq_nr); |
| 126 | else | 132 | else |
| @@ -129,19 +135,11 @@ static void i8259_unmask_irq(unsigned int irq_nr) | |||
| 129 | spin_unlock_irqrestore(&i8259_lock, flags); | 135 | spin_unlock_irqrestore(&i8259_lock, flags); |
| 130 | } | 136 | } |
| 131 | 137 | ||
| 132 | static void i8259_end_irq(unsigned int irq) | 138 | static struct irq_chip i8259_pic = { |
| 133 | { | 139 | .typename = " i8259 ", |
| 134 | if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)) | 140 | .mask = i8259_mask_irq, |
| 135 | && irq_desc[irq].action) | 141 | .unmask = i8259_unmask_irq, |
| 136 | i8259_unmask_irq(irq); | 142 | .mask_ack = i8259_mask_and_ack_irq, |
| 137 | } | ||
| 138 | |||
| 139 | struct hw_interrupt_type i8259_pic = { | ||
| 140 | .typename = " i8259 ", | ||
| 141 | .enable = i8259_unmask_irq, | ||
| 142 | .disable = i8259_mask_irq, | ||
| 143 | .ack = i8259_mask_and_ack_irq, | ||
| 144 | .end = i8259_end_irq, | ||
| 145 | }; | 143 | }; |
| 146 | 144 | ||
| 147 | static struct resource pic1_iores = { | 145 | static struct resource pic1_iores = { |
| @@ -165,25 +163,84 @@ static struct resource pic_edgectrl_iores = { | |||
| 165 | .flags = IORESOURCE_BUSY, | 163 | .flags = IORESOURCE_BUSY, |
| 166 | }; | 164 | }; |
| 167 | 165 | ||
| 168 | static struct irqaction i8259_irqaction = { | 166 | static int i8259_host_match(struct irq_host *h, struct device_node *node) |
| 169 | .handler = no_action, | 167 | { |
| 170 | .flags = IRQF_DISABLED, | 168 | return i8259_node == NULL || i8259_node == node; |
| 171 | .mask = CPU_MASK_NONE, | 169 | } |
| 172 | .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, | ||
| 173 | }; | 225 | }; |
| 174 | 226 | ||
| 175 | /* | 227 | /**** |
| 176 | * i8259_init() | 228 | * i8259_init - Initialize the legacy controller |
| 177 | * 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 |
| 178 | * 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 | ||
| 179 | */ | 233 | */ |
| 180 | void __init i8259_init(unsigned long intack_addr, int offset) | 234 | void i8259_init(struct device_node *node, unsigned long intack_addr) |
| 181 | { | 235 | { |
| 182 | unsigned long flags; | 236 | unsigned long flags; |
| 183 | int i; | ||
| 184 | 237 | ||
| 238 | /* initialize the controller */ | ||
| 185 | spin_lock_irqsave(&i8259_lock, flags); | 239 | spin_lock_irqsave(&i8259_lock, flags); |
| 186 | i8259_pic_irq_offset = offset; | 240 | |
| 241 | /* Mask all first */ | ||
| 242 | outb(0xff, 0xA1); | ||
| 243 | outb(0xff, 0x21); | ||
| 187 | 244 | ||
| 188 | /* init master interrupt controller */ | 245 | /* init master interrupt controller */ |
| 189 | outb(0x11, 0x20); /* Start init sequence */ | 246 | outb(0x11, 0x20); /* Start init sequence */ |
| @@ -197,21 +254,36 @@ void __init i8259_init(unsigned long intack_addr, int offset) | |||
| 197 | outb(0x02, 0xA1); /* edge triggered, Cascade (slave) on IRQ2 */ | 254 | outb(0x02, 0xA1); /* edge triggered, Cascade (slave) on IRQ2 */ |
| 198 | outb(0x01, 0xA1); /* Select 8086 mode */ | 255 | outb(0x01, 0xA1); /* Select 8086 mode */ |
| 199 | 256 | ||
| 257 | /* That thing is slow */ | ||
| 258 | udelay(100); | ||
| 259 | |||
| 200 | /* always read ISR */ | 260 | /* always read ISR */ |
| 201 | outb(0x0B, 0x20); | 261 | outb(0x0B, 0x20); |
| 202 | outb(0x0B, 0xA0); | 262 | outb(0x0B, 0xA0); |
| 203 | 263 | ||
| 204 | /* Mask all interrupts */ | 264 | /* Unmask the internal cascade */ |
| 265 | cached_21 &= ~(1 << 2); | ||
| 266 | |||
| 267 | /* Set interrupt masks */ | ||
| 205 | outb(cached_A1, 0xA1); | 268 | outb(cached_A1, 0xA1); |
| 206 | outb(cached_21, 0x21); | 269 | outb(cached_21, 0x21); |
| 207 | 270 | ||
| 208 | spin_unlock_irqrestore(&i8259_lock, flags); | 271 | spin_unlock_irqrestore(&i8259_lock, flags); |
| 209 | 272 | ||
| 210 | for (i = 0; i < NUM_ISA_INTERRUPTS; ++i) | 273 | /* create a legacy host */ |
| 211 | irq_desc[offset + i].chip = &i8259_pic; | 274 | if (node) |
| 275 | i8259_node = of_node_get(node); | ||
| 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; | ||
| 280 | } | ||
| 212 | 281 | ||
| 213 | /* reserve our resources */ | 282 | /* reserve our resources */ |
| 214 | 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 | */ | ||
| 215 | request_resource(&ioport_resource, &pic1_iores); | 287 | request_resource(&ioport_resource, &pic1_iores); |
| 216 | request_resource(&ioport_resource, &pic2_iores); | 288 | request_resource(&ioport_resource, &pic2_iores); |
| 217 | request_resource(&ioport_resource, &pic_edgectrl_iores); | 289 | request_resource(&ioport_resource, &pic_edgectrl_iores); |
| @@ -219,4 +291,5 @@ void __init i8259_init(unsigned long intack_addr, int offset) | |||
| 219 | if (intack_addr != 0) | 291 | if (intack_addr != 0) |
| 220 | pci_intack = ioremap(intack_addr, 1); | 292 | pci_intack = ioremap(intack_addr, 1); |
| 221 | 293 | ||
| 294 | printk(KERN_INFO "i8259 legacy interrupt controller initialized\n"); | ||
| 222 | } | 295 | } |
