/* * Copyright 2002-2005, Axel Dörfler, axeld@pinc-software.de. * Distributed under the terms of the MIT License. * * Copyright 2001, Travis Geiselbrecht. All rights reserved. * Distributed under the terms of the NewOS License. */ #include #include #include #include #include //#define TRACE_PIC #ifdef TRACE_PIC # define TRACE(x) dprintf x #else # define TRACE(x) ; #endif // Definitions for the PIC 8259 controller // (this is not a complete list, only what we're actually using) #define PIC_MASTER_CONTROL 0x20 #define PIC_MASTER_MASK 0x21 #define PIC_SLAVE_CONTROL 0xa0 #define PIC_SLAVE_MASK 0xa1 #define PIC_MASTER_INIT1 PIC_MASTER_CONTROL #define PIC_MASTER_INIT2 PIC_MASTER_MASK #define PIC_MASTER_INIT3 PIC_MASTER_MASK #define PIC_MASTER_INIT4 PIC_MASTER_MASK #define PIC_SLAVE_INIT1 PIC_SLAVE_CONTROL #define PIC_SLAVE_INIT2 PIC_SLAVE_MASK #define PIC_SLAVE_INIT3 PIC_SLAVE_MASK #define PIC_SLAVE_INIT4 PIC_SLAVE_MASK // the edge/level trigger control registers #define PIC_MASTER_TRIGGER_MODE 0x4d0 #define PIC_SLAVE_TRIGGER_MODE 0x4d1 #define PIC_INIT1 0x10 #define PIC_INIT1_SEND_INIT4 0x01 #define PIC_INIT3_IR2_IS_SLAVE 0x04 #define PIC_INIT3_SLAVE_ID2 0x02 #define PIC_INIT4_x86_MODE 0x01 #define PIC_CONTROL3 0x08 #define PIC_CONTROL3_READ_ISR 0x03 #define PIC_CONTROL3_READ_IRR 0x02 #define PIC_NON_SPECIFIC_EOI 0x20 #define PIC_SLAVE_INT_BASE 8 #define PIC_NUM_INTS 0x0f static uint16 sLevelTriggeredInterrupts = 0; // binary mask: 1 level, 0 edge /*! Tests if the interrupt in-service register of the responsible PIC is set for interrupts 7 and 15, and if that's not the case, it must assume it's a spurious interrupt. */ static bool pic_is_spurious_interrupt(int32 num) { if (num != 7) return false; // Note, detecting spurious interrupts on line 15 obviously doesn't // work correctly - and since those are extremely rare, anyway, we // just ignore them out8(PIC_CONTROL3 | PIC_CONTROL3_READ_ISR, PIC_MASTER_CONTROL); int32 isr = in8(PIC_MASTER_CONTROL); out8(PIC_CONTROL3 | PIC_CONTROL3_READ_IRR, PIC_MASTER_CONTROL); return (isr & 0x80) == 0; } static bool pic_is_level_triggered_interrupt(int32 num) { if (num < 0 || num > PIC_NUM_INTS) return false; return (sLevelTriggeredInterrupts & (1 << num)) != 0; } /*! Sends a non-specified EOI (end of interrupt) notice to the PIC in question (or both of them). This clears the PIC interrupt in-service bit. */ static bool pic_end_of_interrupt(int32 num) { if (num < 0 || num > PIC_NUM_INTS) return false; // PIC 8259 controlled interrupt if (num >= PIC_SLAVE_INT_BASE) out8(PIC_NON_SPECIFIC_EOI, PIC_SLAVE_CONTROL); // we always need to acknowledge the master PIC out8(PIC_NON_SPECIFIC_EOI, PIC_MASTER_CONTROL); return true; } static void pic_enable_io_interrupt(int32 num) { // interrupt is specified "normalized" if (num < 0 || num > PIC_NUM_INTS) return; // enable PIC 8259 controlled interrupt TRACE(("pic_enable_io_interrupt: irq %ld\n", num)); if (num < PIC_SLAVE_INT_BASE) out8(in8(PIC_MASTER_MASK) & ~(1 << num), PIC_MASTER_MASK); else out8(in8(PIC_SLAVE_MASK) & ~(1 << (num - PIC_SLAVE_INT_BASE)), PIC_SLAVE_MASK); } static void pic_disable_io_interrupt(int32 num) { // interrupt is specified "normalized" // never disable slave pic line IRQ 2 if (num < 0 || num > PIC_NUM_INTS || num == 2) return; // disable PIC 8259 controlled interrupt TRACE(("pic_disable_io_interrupt: irq %ld\n", num)); if (num < PIC_SLAVE_INT_BASE) out8(in8(PIC_MASTER_MASK) | (1 << num), PIC_MASTER_MASK); else out8(in8(PIC_SLAVE_MASK) | (1 << (num - PIC_SLAVE_INT_BASE)), PIC_SLAVE_MASK); } static void pic_configure_io_interrupt(int32 num, uint32 config) { uint8 value; int32 localBit; if (num < 0 || num > PIC_NUM_INTS || num == 2) return; TRACE(("pic_configure_io_interrupt: irq %ld; config 0x%08lx\n", num, config)); if (num < PIC_SLAVE_INT_BASE) { value = in8(PIC_MASTER_TRIGGER_MODE); localBit = num; } else { value = in8(PIC_SLAVE_TRIGGER_MODE); localBit = num - PIC_SLAVE_INT_BASE; } if (config & B_LEVEL_TRIGGERED) value |= 1 << localBit; else value &= ~(1 << localBit); if (num < PIC_SLAVE_INT_BASE) out8(value, PIC_MASTER_TRIGGER_MODE); else out8(value, PIC_SLAVE_TRIGGER_MODE); sLevelTriggeredInterrupts = in8(PIC_MASTER_TRIGGER_MODE) | (in8(PIC_SLAVE_TRIGGER_MODE) << 8); } void pic_init() { static const interrupt_controller picController = { "8259 PIC", &pic_enable_io_interrupt, &pic_disable_io_interrupt, &pic_configure_io_interrupt, &pic_is_spurious_interrupt, &pic_is_level_triggered_interrupt, &pic_end_of_interrupt, NULL }; // Start initialization sequence for the master and slave PICs out8(PIC_INIT1 | PIC_INIT1_SEND_INIT4, PIC_MASTER_INIT1); out8(PIC_INIT1 | PIC_INIT1_SEND_INIT4, PIC_SLAVE_INIT1); // Set start of interrupts to 0x20 for master, 0x28 for slave out8(ARCH_INTERRUPT_BASE, PIC_MASTER_INIT2); out8(ARCH_INTERRUPT_BASE + PIC_SLAVE_INT_BASE, PIC_SLAVE_INIT2); // Specify cascading through interrupt 2 out8(PIC_INIT3_IR2_IS_SLAVE, PIC_MASTER_INIT3); out8(PIC_INIT3_SLAVE_ID2, PIC_SLAVE_INIT3); // Set both to operate in 8086 mode out8(PIC_INIT4_x86_MODE, PIC_MASTER_INIT4); out8(PIC_INIT4_x86_MODE, PIC_SLAVE_INIT4); out8(0xfb, PIC_MASTER_MASK); // Mask off all interrupts (except slave pic line IRQ 2). out8(0xff, PIC_SLAVE_MASK); // Mask off interrupts on the slave. // determine which interrupts are level or edge triggered #if 0 // should set everything possible to level triggered out8(0xf8, PIC_MASTER_TRIGGER_MODE); out8(0xde, PIC_SLAVE_TRIGGER_MODE); #endif sLevelTriggeredInterrupts = in8(PIC_MASTER_TRIGGER_MODE) | (in8(PIC_SLAVE_TRIGGER_MODE) << 8); TRACE(("PIC level trigger mode: 0x%08lx\n", sLevelTriggeredInterrupts)); reserve_io_interrupt_vectors(16, 0, INTERRUPT_TYPE_EXCEPTION); // make the pic controller the current one arch_int_set_interrupt_controller(picController); } void pic_disable(uint16& enabledInterrupts) { enabledInterrupts = ~(in8(PIC_MASTER_MASK) | in8(PIC_SLAVE_MASK) << 8); enabledInterrupts &= 0xfffb; // remove slave PIC from the mask // Mask off all interrupts on master and slave out8(0xff, PIC_MASTER_MASK); out8(0xff, PIC_SLAVE_MASK); free_io_interrupt_vectors(16, 0); }