blob: 5814694fb843f49cbf18fbf71e1a42cfc80c5c8b [file] [log] [blame]
/* Copyright (c) 2009, 2014 The Regents of the University of California
* Barret Rhoden <brho@cs.berkeley.edu>
* See LICENSE for details.
*
* PIC: 8259 interrupt controller */
#include <arch/pic.h>
#include <arch/x86.h>
#include <atomic.h>
#include <assert.h>
#include <stdio.h>
spinlock_t piclock = SPINLOCK_INITIALIZER_IRQSAVE;
/* Remaps the Programmable Interrupt Controller to use IRQs 32-47
* http://wiki.osdev.org/PIC
* Check osdev for a more thorough explanation/implementation.
* http://bochs.sourceforge.net/techspec/PORTS.LST */
void pic_remap(void)
{
spin_lock_irqsave(&piclock);
/* start initialization (ICW1) */
outb(PIC1_CMD, 0x11);
outb(PIC2_CMD, 0x11);
/* set new offsets (ICW2) */
outb(PIC1_DATA, PIC1_OFFSET);
outb(PIC2_DATA, PIC2_OFFSET);
/* set up cascading (ICW3) */
outb(PIC1_DATA, 0x04);
outb(PIC2_DATA, 0x02);
/* other stuff (put in 8086/88 mode, or whatever) (ICW4) */
outb(PIC1_DATA, 0x01);
outb(PIC2_DATA, 0x01);
/* Init done, further data R/W access the interrupt mask */
/* set masks, defaulting to all masked for now */
outb(PIC1_DATA, 0xff);
outb(PIC2_DATA, 0xff);
spin_unlock_irqsave(&piclock);
}
void pic_mask_irq(struct irq_handler *unused, int trap_nr)
{
int irq = trap_nr - PIC1_OFFSET;
spin_lock_irqsave(&piclock);
if (irq > 7)
outb(PIC2_DATA, inb(PIC2_DATA) | (1 << (irq - 8)));
else
outb(PIC1_DATA, inb(PIC1_DATA) | (1 << irq));
spin_unlock_irqsave(&piclock);
}
void pic_unmask_irq(struct irq_handler *unused, int trap_nr)
{
int irq = trap_nr - PIC1_OFFSET;
printd("PIC unmask for TRAP %d, IRQ %d\n", trap_nr, irq);
spin_lock_irqsave(&piclock);
if (irq > 7) {
outb(PIC2_DATA, inb(PIC2_DATA) & ~(1 << (irq - 8)));
// make sure irq2 is unmasked
outb(PIC1_DATA, inb(PIC1_DATA) & 0xfb);
} else
outb(PIC1_DATA, inb(PIC1_DATA) & ~(1 << irq));
spin_unlock_irqsave(&piclock);
}
void pic_mask_all(void)
{
for (int i = 0 + PIC1_OFFSET; i < 16 + PIC1_OFFSET; i++)
pic_mask_irq(0, i);
}
/* Aka, the IMR. Simply reading the data port are OCW1s. */
uint16_t pic_get_mask(void)
{
uint16_t ret;
spin_lock_irqsave(&piclock);
ret = (inb(PIC2_DATA) << 8) | inb(PIC1_DATA);
spin_unlock_irqsave(&piclock);
return ret;
}
static uint16_t __pic_get_irq_reg(int ocw3)
{
uint16_t ret;
spin_lock_irqsave(&piclock);
/* OCW3 to PIC CMD to get the register values. PIC2 is chained, and
* represents IRQs 8-15. PIC1 is IRQs 0-7, with 2 being the chain */
outb(PIC1_CMD, ocw3);
outb(PIC2_CMD, ocw3);
ret = (inb(PIC2_CMD) << 8) | inb(PIC1_CMD);
spin_unlock_irqsave(&piclock);
return ret;
}
/* Returns the combined value of the cascaded PICs irq request register */
uint16_t pic_get_irr(void)
{
return __pic_get_irq_reg(PIC_READ_IRR);
}
/* Returns the combined value of the cascaded PICs irq service register */
uint16_t pic_get_isr(void)
{
return __pic_get_irq_reg(PIC_READ_ISR);
}
/* Takes a raw vector/trap number (32-47), not a device IRQ (0-15) */
bool pic_check_spurious(int trap_nr)
{
/* the PIC may send spurious irqs via one of the chips irq 7. if the
* isr doesn't show that irq, then it was spurious, and we don't send an
* eoi. Check out http://wiki.osdev.org/8259_PIC#Spurious_IRQs */
if ((trap_nr == PIC1_SPURIOUS) && !(pic_get_isr() & (1 << 7))) {
/* want to know if this happens */
printd("Spurious PIC1 irq!\n");
return TRUE;
}
if ((trap_nr == PIC2_SPURIOUS) && !(pic_get_isr() & (1 << 15))) {
/* want to know if this happens */
printd("Spurious PIC2 irq!\n");
/* for the cascaded PIC, we *do* need to send an EOI to the
* master's cascade irq (2). */
pic_send_eoi(2 + PIC1_OFFSET);
return TRUE;
}
return FALSE;
}
void pic_send_eoi(int trap_nr)
{
int irq = trap_nr - PIC1_OFFSET;
spin_lock_irqsave(&piclock);
// all irqs beyond the first seven need to be chained to the slave
if (irq > 7)
outb(PIC2_CMD, PIC_EOI);
outb(PIC1_CMD, PIC_EOI);
spin_unlock_irqsave(&piclock);
}