* 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 <arch/x86/pic.h>
#include <arch/cpu.h>
#include <arch/int.h>
#include <arch/x86/arch_int.h>
#include <interrupts.h>
#ifdef TRACE_PIC
# define TRACE(x) dprintf x
#else
# define TRACE(x) ;
#endif
#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
#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;
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;
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;
}
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;
if (num >= PIC_SLAVE_INT_BASE)
out8(PIC_NON_SPECIFIC_EOI, PIC_SLAVE_CONTROL);
out8(PIC_NON_SPECIFIC_EOI, PIC_MASTER_CONTROL);
return true;
}
static void
pic_enable_io_interrupt(int32 num)
{
if (num < 0 || num > PIC_NUM_INTS)
return;
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)
{
if (num < 0 || num > PIC_NUM_INTS || num == 2)
return;
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
};
out8(PIC_INIT1 | PIC_INIT1_SEND_INIT4, PIC_MASTER_INIT1);
out8(PIC_INIT1 | PIC_INIT1_SEND_INIT4, PIC_SLAVE_INIT1);
out8(ARCH_INTERRUPT_BASE, PIC_MASTER_INIT2);
out8(ARCH_INTERRUPT_BASE + PIC_SLAVE_INT_BASE, PIC_SLAVE_INIT2);
out8(PIC_INIT3_IR2_IS_SLAVE, PIC_MASTER_INIT3);
out8(PIC_INIT3_SLAVE_ID2, PIC_SLAVE_INIT3);
out8(PIC_INIT4_x86_MODE, PIC_MASTER_INIT4);
out8(PIC_INIT4_x86_MODE, PIC_SLAVE_INIT4);
out8(0xfb, PIC_MASTER_MASK);
out8(0xff, PIC_SLAVE_MASK);
#if 0
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);
arch_int_set_interrupt_controller(picController);
}
void
pic_disable(uint16& enabledInterrupts)
{
enabledInterrupts = ~(in8(PIC_MASTER_MASK) | in8(PIC_SLAVE_MASK) << 8);
enabledInterrupts &= 0xfffb;
out8(0xff, PIC_MASTER_MASK);
out8(0xff, PIC_SLAVE_MASK);
free_io_interrupt_vectors(16, 0);
}