/* * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. * * Code to handle x86 style IRQs plus some generic interrupt stuff. * * Copyright (C) 1992 Linus Torvalds * Copyright (C) 1994 - 2000 Ralf Baechle
*/ #include <linux/delay.h> #include <linux/init.h> #include <linux/ioport.h> #include <linux/interrupt.h> #include <linux/irqchip.h> #include <linux/irqdomain.h> #include <linux/kernel.h> #include <linux/of_irq.h> #include <linux/spinlock.h> #include <linux/syscore_ops.h> #include <linux/irq.h>
#include <asm/i8259.h> #include <asm/io.h>
/* * This is the 'legacy' 8259A Programmable Interrupt Controller, * present in the majority of PC/AT boxes. * plus some generic x86 specific things if generic specifics makes * any sense at all. * this file should become arch/i386/kernel/irq.c when the old irq.c * moves to arch independent land
*/
/* * This function assumes to be called rarely. Switching between * 8259A registers is slow. * This has to be protected by the irq controller spinlock * before being called.
*/ staticinlineint i8259A_irq_real(unsignedint irq)
{ int value; int irqmask = 1 << irq;
if (irq < 8) {
outb(0x0B, PIC_MASTER_CMD); /* ISR register */
value = inb(PIC_MASTER_CMD) & irqmask;
outb(0x0A, PIC_MASTER_CMD); /* back to the IRR register */ return value;
}
outb(0x0B, PIC_SLAVE_CMD); /* ISR register */
value = inb(PIC_SLAVE_CMD) & (irqmask >> 8);
outb(0x0A, PIC_SLAVE_CMD); /* back to the IRR register */ return value;
}
/* * Careful! The 8259A is a fragile beast, it pretty * much _has_ to be done exactly like this (mask it * first, _then_ send the EOI, and the order of EOI * to the two 8259s is important!
*/ staticvoid mask_and_ack_8259A(struct irq_data *d)
{ unsignedint irqmask, irq = d->irq - I8259A_IRQ_BASE; unsignedlong flags;
irqmask = 1 << irq;
raw_spin_lock_irqsave(&i8259A_lock, flags); /* * Lightweight spurious IRQ detection. We do not want * to overdo spurious IRQ handling - it's usually a sign * of hardware problems, so we only do the checks we can * do without slowing down good hardware unnecessarily. * * Note that IRQ7 and IRQ15 (the two spurious IRQs * usually resulting from the 8259A-1|2 PICs) occur * even if the IRQ is masked in the 8259A. Thus we * can check spurious 8259A IRQs without doing the * quite slow i8259A_irq_real() call for every IRQ. * This does not cover 100% of spurious interrupts, * but should be enough to warn the user that there * is something bad going on ...
*/ if (cached_irq_mask & irqmask) goto spurious_8259A_irq;
cached_irq_mask |= irqmask;
handle_real_irq: if (irq & 8) {
inb(PIC_SLAVE_IMR); /* DUMMY - (do we need this?) */
outb(cached_slave_mask, PIC_SLAVE_IMR);
outb(0x60+(irq&7), PIC_SLAVE_CMD);/* 'Specific EOI' to slave */
outb(0x60+PIC_CASCADE_IR, PIC_MASTER_CMD); /* 'Specific EOI' to master-IRQ2 */
} else {
inb(PIC_MASTER_IMR); /* DUMMY - (do we need this?) */
outb(cached_master_mask, PIC_MASTER_IMR);
outb(0x60+irq, PIC_MASTER_CMD); /* 'Specific EOI to master */
}
raw_spin_unlock_irqrestore(&i8259A_lock, flags); return;
spurious_8259A_irq: /* * this is the slow path - should happen rarely.
*/ if (i8259A_irq_real(irq)) /* * oops, the IRQ _is_ in service according to the * 8259A - not spurious, go handle it.
*/ goto handle_real_irq;
{ staticint spurious_irq_mask; /* * At this point we can be sure the IRQ is spurious, * lets ACK and report it. [once per IRQ]
*/ if (!(spurious_irq_mask & irqmask)) {
printk(KERN_DEBUG "spurious 8259A interrupt: IRQ%d.\n", irq);
spurious_irq_mask |= irqmask;
}
atomic_inc(&irq_err_count); /* * Theoretically we do not have to handle this IRQ, * but in Linux this does not cause problems and is * simpler for us.
*/ goto handle_real_irq;
}
}
staticvoid i8259A_resume(void)
{ if (i8259A_auto_eoi >= 0)
init_8259A(i8259A_auto_eoi);
}
staticvoid i8259A_shutdown(void)
{ /* Put the i8259A into a quiescent state that * the kernel initialization code can get it * out of.
*/ if (i8259A_auto_eoi >= 0) {
outb(0xff, PIC_MASTER_IMR); /* mask all of 8259A-1 */
outb(0xff, PIC_SLAVE_IMR); /* mask all of 8259A-2 */
}
}
outb(0xff, PIC_MASTER_IMR); /* mask all of 8259A-1 */
outb(0xff, PIC_SLAVE_IMR); /* mask all of 8259A-2 */
/* * outb_p - this has to work on a wide range of PC hardware.
*/
outb_p(0x11, PIC_MASTER_CMD); /* ICW1: select 8259A-1 init */
outb_p(I8259A_IRQ_BASE + 0, PIC_MASTER_IMR); /* ICW2: 8259A-1 IR0 mapped to I8259A_IRQ_BASE + 0x00 */
outb_p(1U << PIC_CASCADE_IR, PIC_MASTER_IMR); /* 8259A-1 (the master) has a slave on IR2 */ if (auto_eoi) /* master does Auto EOI */
outb_p(MASTER_ICW4_DEFAULT | PIC_ICW4_AEOI, PIC_MASTER_IMR); else/* master expects normal EOI */
outb_p(MASTER_ICW4_DEFAULT, PIC_MASTER_IMR);
outb_p(0x11, PIC_SLAVE_CMD); /* ICW1: select 8259A-2 init */
outb_p(I8259A_IRQ_BASE + 8, PIC_SLAVE_IMR); /* ICW2: 8259A-2 IR0 mapped to I8259A_IRQ_BASE + 0x08 */
outb_p(PIC_CASCADE_IR, PIC_SLAVE_IMR); /* 8259A-2 is a slave on master's IR2 */
outb_p(SLAVE_ICW4_DEFAULT, PIC_SLAVE_IMR); /* (slave's support for AEOI in flat mode is to be investigated) */ if (auto_eoi) /* * In AEOI mode we just have to mask the interrupt * when acking.
*/
i8259A_chip.irq_mask_ack = disable_8259A_irq; else
i8259A_chip.irq_mask_ack = mask_and_ack_8259A;
/* * On systems with i8259-style interrupt controllers we assume for * driver compatibility reasons interrupts 0 - 15 to be the i8259 * interrupts even if the hardware uses a different interrupt numbering.
*/ struct irq_domain * __init __init_i8259_irqs(struct device_node *node)
{ /* * PIC_CASCADE_IR is cascade interrupt to second interrupt controller
*/ int irq = I8259A_IRQ_BASE + PIC_CASCADE_IR; struct irq_domain *domain;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.