* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
-#undef DEBUG
-
#include <linux/init.h>
#include <linux/ioport.h>
#include <linux/interrupt.h>
-#include <linux/kernel.h>
-#include <linux/delay.h>
#include <asm/io.h>
#include <asm/i8259.h>
-#include <asm/prom.h>
static volatile void __iomem *pci_intack; /* RO, gives us the irq vector */
static DEFINE_SPINLOCK(i8259_lock);
-static struct device_node *i8259_node;
-static struct irq_host *i8259_host;
+static int i8259_pic_irq_offset;
/*
* Acknowledge the IRQ using either the PCI host bridge's interrupt
* which is called. It should be noted that polling is broken on some
* IBM and Motorola PReP boxes so we must use the int-ack feature on them.
*/
-unsigned int i8259_irq(struct pt_regs *regs)
+int i8259_irq(struct pt_regs *regs)
{
int irq;
- int lock = 0;
+
+ spin_lock(&i8259_lock);
/* Either int-ack or poll for the IRQ */
if (pci_intack)
irq = readb(pci_intack);
else {
- spin_lock(&i8259_lock);
- lock = 1;
-
/* Perform an interrupt acknowledge cycle on controller 1. */
outb(0x0C, 0x20); /* prepare for poll */
irq = inb(0x20) & 7;
if (!pci_intack)
outb(0x0B, 0x20); /* ISR register */
if(~inb(0x20) & 0x80)
- irq = NO_IRQ;
- } else if (irq == 0xff)
- irq = NO_IRQ;
+ irq = -1;
+ }
- if (lock)
- spin_unlock(&i8259_lock);
- return irq;
+ spin_unlock(&i8259_lock);
+ return irq + i8259_pic_irq_offset;
+}
+
+int i8259_irq_cascade(struct pt_regs *regs, void *unused)
+{
+ return i8259_irq(regs);
}
static void i8259_mask_and_ack_irq(unsigned int irq_nr)
unsigned long flags;
spin_lock_irqsave(&i8259_lock, flags);
+ irq_nr -= i8259_pic_irq_offset;
if (irq_nr > 7) {
cached_A1 |= 1 << (irq_nr-8);
inb(0xA1); /* DUMMY */
{
unsigned long flags;
- pr_debug("i8259_mask_irq(%d)\n", irq_nr);
-
spin_lock_irqsave(&i8259_lock, flags);
+ irq_nr -= i8259_pic_irq_offset;
if (irq_nr < 8)
cached_21 |= 1 << irq_nr;
else
{
unsigned long flags;
- pr_debug("i8259_unmask_irq(%d)\n", irq_nr);
-
spin_lock_irqsave(&i8259_lock, flags);
+ irq_nr -= i8259_pic_irq_offset;
if (irq_nr < 8)
cached_21 &= ~(1 << irq_nr);
else
spin_unlock_irqrestore(&i8259_lock, flags);
}
-static struct irq_chip i8259_pic = {
- .typename = " i8259 ",
- .mask = i8259_mask_irq,
- .unmask = i8259_unmask_irq,
- .mask_ack = i8259_mask_and_ack_irq,
+static void i8259_end_irq(unsigned int irq)
+{
+ if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))
+ && irq_desc[irq].action)
+ i8259_unmask_irq(irq);
+}
+
+struct hw_interrupt_type i8259_pic = {
+ .typename = " i8259 ",
+ .enable = i8259_unmask_irq,
+ .disable = i8259_mask_irq,
+ .ack = i8259_mask_and_ack_irq,
+ .end = i8259_end_irq,
};
static struct resource pic1_iores = {
.flags = IORESOURCE_BUSY,
};
-static int i8259_host_match(struct irq_host *h, struct device_node *node)
-{
- return i8259_node == NULL || i8259_node == node;
-}
-
-static int i8259_host_map(struct irq_host *h, unsigned int virq,
- irq_hw_number_t hw)
-{
- pr_debug("i8259_host_map(%d, 0x%lx)\n", virq, hw);
-
- /* We block the internal cascade */
- if (hw == 2)
- get_irq_desc(virq)->status |= IRQ_NOREQUEST;
-
- /* We use the level handler only for now, we might want to
- * be more cautious here but that works for now
- */
- get_irq_desc(virq)->status |= IRQ_LEVEL;
- set_irq_chip_and_handler(virq, &i8259_pic, handle_level_irq);
- return 0;
-}
-
-static void i8259_host_unmap(struct irq_host *h, unsigned int virq)
-{
- /* Make sure irq is masked in hardware */
- i8259_mask_irq(virq);
-
- /* remove chip and handler */
- set_irq_chip_and_handler(virq, NULL, NULL);
-
- /* Make sure it's completed */
- synchronize_irq(virq);
-}
-
-static int i8259_host_xlate(struct irq_host *h, struct device_node *ct,
- u32 *intspec, unsigned int intsize,
- irq_hw_number_t *out_hwirq, unsigned int *out_flags)
-{
- static unsigned char map_isa_senses[4] = {
- IRQ_TYPE_LEVEL_LOW,
- IRQ_TYPE_LEVEL_HIGH,
- IRQ_TYPE_EDGE_FALLING,
- IRQ_TYPE_EDGE_RISING,
- };
-
- *out_hwirq = intspec[0];
- if (intsize > 1 && intspec[1] < 4)
- *out_flags = map_isa_senses[intspec[1]];
- else
- *out_flags = IRQ_TYPE_NONE;
-
- return 0;
-}
-
-static struct irq_host_ops i8259_host_ops = {
- .match = i8259_host_match,
- .map = i8259_host_map,
- .unmap = i8259_host_unmap,
- .xlate = i8259_host_xlate,
+static struct irqaction i8259_irqaction = {
+ .handler = no_action,
+ .flags = SA_INTERRUPT,
+ .mask = CPU_MASK_NONE,
+ .name = "82c59 secondary cascade",
};
-/****
- * i8259_init - Initialize the legacy controller
- * @node: device node of the legacy PIC (can be NULL, but then, it will match
- * all interrupts, so beware)
- * @intack_addr: PCI interrupt acknowledge (real) address which will return
- * the active irq from the 8259
+/*
+ * i8259_init()
+ * intack_addr - PCI interrupt acknowledge (real) address which will return
+ * the active irq from the 8259
*/
-void i8259_init(struct device_node *node, unsigned long intack_addr)
+void __init i8259_init(unsigned long intack_addr, int offset)
{
unsigned long flags;
+ int i;
- /* initialize the controller */
spin_lock_irqsave(&i8259_lock, flags);
-
- /* Mask all first */
- outb(0xff, 0xA1);
- outb(0xff, 0x21);
+ i8259_pic_irq_offset = offset;
/* init master interrupt controller */
outb(0x11, 0x20); /* Start init sequence */
outb(0x02, 0xA1); /* edge triggered, Cascade (slave) on IRQ2 */
outb(0x01, 0xA1); /* Select 8086 mode */
- /* That thing is slow */
- udelay(100);
-
/* always read ISR */
outb(0x0B, 0x20);
outb(0x0B, 0xA0);
- /* Unmask the internal cascade */
- cached_21 &= ~(1 << 2);
-
- /* Set interrupt masks */
+ /* Mask all interrupts */
outb(cached_A1, 0xA1);
outb(cached_21, 0x21);
spin_unlock_irqrestore(&i8259_lock, flags);
- /* create a legacy host */
- if (node)
- i8259_node = of_node_get(node);
- i8259_host = irq_alloc_host(IRQ_HOST_MAP_LEGACY, 0, &i8259_host_ops, 0);
- if (i8259_host == NULL) {
- printk(KERN_ERR "i8259: failed to allocate irq host !\n");
- return;
- }
+ for (i = 0; i < NUM_ISA_INTERRUPTS; ++i)
+ irq_desc[offset + i].handler = &i8259_pic;
/* reserve our resources */
- /* XXX should we continue doing that ? it seems to cause problems
- * with further requesting of PCI IO resources for that range...
- * need to look into it.
- */
+ setup_irq(offset + 2, &i8259_irqaction);
request_resource(&ioport_resource, &pic1_iores);
request_resource(&ioport_resource, &pic2_iores);
request_resource(&ioport_resource, &pic_edgectrl_iores);
if (intack_addr != 0)
pci_intack = ioremap(intack_addr, 1);
- printk(KERN_INFO "i8259 legacy interrupt controller initialized\n");
}