You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
115 lines
3.4 KiB
C
115 lines
3.4 KiB
C
/* Copyright (C) 2021,2022 fef <owo@fef.moe>. All rights reserved. */
|
|
|
|
/*
|
|
* Most of the PIC management stuff is taken directly from the OSDev wiki:
|
|
* <https://wiki.osdev.org/PIC>
|
|
*/
|
|
|
|
#include <arch/cpufunc.h>
|
|
#include <arch/irq.h>
|
|
#include <arch/port.h>
|
|
#include <arch/trap.h>
|
|
|
|
#define ICW1_ICW4 0x01 /* ICW4 (not) needed */
|
|
#define ICW1_SINGLE 0x02 /* Single (cascade) mode */
|
|
#define ICW1_INTERVAL4 0x04 /* Call address interval 4 (8) */
|
|
#define ICW1_LEVEL 0x08 /* Level triggered (edge) mode */
|
|
#define ICW1_INIT 0x10 /* Initialization - required! */
|
|
|
|
#define ICW4_8086 0x01 /* 8086/88 (MCS-80/85) mode */
|
|
#define ICW4_AUTO 0x02 /* Auto (normal) EOI */
|
|
#define ICW4_BUF_SLAVE 0x08 /* Buffered mode/slave */
|
|
#define ICW4_BUF_MASTER 0x0C /* Buffered mode/master */
|
|
#define ICW4_SFNM 0x10 /* Special fully nested (not) */
|
|
|
|
void arch_irq_init(void)
|
|
{
|
|
x86_set_intr_gate(X86_VECT_IRQ(0), _x86_isr_irq0);
|
|
x86_set_intr_gate(X86_VECT_IRQ(1), _x86_isr_irq1);
|
|
x86_set_intr_gate(X86_VECT_IRQ(3), _x86_isr_irq3);
|
|
x86_set_intr_gate(X86_VECT_IRQ(4), _x86_isr_irq4);
|
|
x86_set_intr_gate(X86_VECT_IRQ(5), _x86_isr_irq5);
|
|
x86_set_intr_gate(X86_VECT_IRQ(6), _x86_isr_irq6);
|
|
x86_set_intr_gate(X86_VECT_IRQ(7), _x86_isr_irq7);
|
|
x86_set_intr_gate(X86_VECT_IRQ(8), _x86_isr_irq8);
|
|
x86_set_intr_gate(X86_VECT_IRQ(9), _x86_isr_irq9);
|
|
x86_set_intr_gate(X86_VECT_IRQ(10), _x86_isr_irq10);
|
|
x86_set_intr_gate(X86_VECT_IRQ(11), _x86_isr_irq11);
|
|
x86_set_intr_gate(X86_VECT_IRQ(12), _x86_isr_irq12);
|
|
x86_set_intr_gate(X86_VECT_IRQ(13), _x86_isr_irq13);
|
|
x86_set_intr_gate(X86_VECT_IRQ(14), _x86_isr_irq14);
|
|
x86_set_intr_gate(X86_VECT_IRQ(15), _x86_isr_irq15);
|
|
|
|
/*
|
|
* disable interrupts to prevent IRQs from being fired
|
|
* immediately after the initialization sequence is complete
|
|
* (we need time to mask all IRQs again first)
|
|
*/
|
|
disable_intr();
|
|
|
|
/* begin initialization sequence in cascade mode */
|
|
x86_outb_wait(X86_PORT_PIC1_CMD, ICW1_INIT | ICW1_ICW4);
|
|
x86_outb_wait(X86_PORT_PIC2_CMD, ICW1_INIT | ICW1_ICW4);
|
|
|
|
/* tell PIC1 its offset into the vector table */
|
|
x86_outb_wait(X86_PORT_PIC1_DATA, X86_VECT_IRQ_BASE);
|
|
/* tell PIC2 its offset into the vector table */
|
|
x86_outb_wait(X86_PORT_PIC2_DATA, X86_VECT_IRQ_BASE + 8);
|
|
|
|
/* tell PIC1 that PIC2 is cascading into it on IRQ 2 */
|
|
x86_outb_wait(X86_PORT_PIC1_DATA, 1 << 2);
|
|
/* tell PIC2 that it is the second controller in the cascade chain */
|
|
x86_outb_wait(X86_PORT_PIC2_DATA, 2);
|
|
|
|
/* don't ask me what the fuck this does */
|
|
x86_outb_wait(X86_PORT_PIC1_DATA, ICW4_8086);
|
|
x86_outb_wait(X86_PORT_PIC2_DATA, ICW4_8086);
|
|
|
|
/*
|
|
* The initialization sequence is finished now, meaning all IRQ masks
|
|
* have been cleared (they are all enabled). Disable them again.
|
|
*/
|
|
x86_outb_wait(X86_PORT_PIC1_DATA, 0xff);
|
|
x86_outb_wait(X86_PORT_PIC2_DATA, 0xff);
|
|
|
|
/* it's safe to turn interrupts back on now */
|
|
enable_intr();
|
|
}
|
|
|
|
void arch_irq_enable(unsigned int number)
|
|
{
|
|
u16 port;
|
|
if (number >= 8) {
|
|
port = X86_PORT_PIC2_DATA;
|
|
number -= 8;
|
|
} else {
|
|
port = X86_PORT_PIC1_DATA;
|
|
}
|
|
|
|
u8 mask = x86_inb(port);
|
|
mask &= ~(1 << number);
|
|
x86_outb(port, mask);
|
|
}
|
|
|
|
void arch_irq_disable(unsigned int number)
|
|
{
|
|
u16 port;
|
|
if (number >= 8) {
|
|
port = X86_PORT_PIC2_DATA;
|
|
number -= 8;
|
|
} else {
|
|
port = X86_PORT_PIC1_DATA;
|
|
}
|
|
|
|
u8 mask = x86_inb(port);
|
|
mask |= (1 << number);
|
|
x86_outb(port, mask);
|
|
}
|
|
|
|
void x86_irq_ack(unsigned int number)
|
|
{
|
|
if (number >= 8)
|
|
x86_outb(X86_PORT_PIC2_CMD, 0x20);
|
|
x86_outb(X86_PORT_PIC1_CMD, 0x20);
|
|
}
|