diff --git a/src/kernel/cpu/cpu.c b/src/kernel/cpu/cpu.c index d69be75..b603230 100644 --- a/src/kernel/cpu/cpu.c +++ b/src/kernel/cpu/cpu.c @@ -26,4 +26,5 @@ void cpu_init() { pic_disable(); apic_init(); ioapic_init(); + timer_init(); } \ No newline at end of file diff --git a/src/kernel/cpu/timer.c b/src/kernel/cpu/timer.c new file mode 100644 index 0000000..9e9585e --- /dev/null +++ b/src/kernel/cpu/timer.c @@ -0,0 +1,77 @@ +#include +#include +#include + +#define PC_SPKR 0x61 +#define PIT_CH2 0x42 +#define PIT_CMD 0x43 + +#define PIT_FREQ 1193182 + +#define APIC_TIMER_DIVIDER 0x3E0 +#define APIC_TIMER_INIT 0x380 +#define APIC_TIMER_CURRENT 0x390 + +volatile uint32_t apic_ticks_per_us = 1; + +static void pit_setup(uint8_t ms) { + // Disable the pc speaker output + uint8_t spkr = inb(PC_SPKR); + spkr &= ~0x1; + outb(PC_SPKR, spkr); + + // Set up PIT for a single interrupt on CH2 + outb(PIT_CMD, 0x80 | 0x30); + + // Set up CH2 for counting down from a number of ms + uint64_t count = PIT_FREQ * ms/1000; + outb(PIT_CH2, count & 0xFF); + outb(PIT_CH2, (count >> 8) & 0xFF); +} + +static void pit_wait() { + + // Enable pc speaker + uint8_t spkr = inb(PC_SPKR); + spkr |= 0x1; + outb(PC_SPKR, spkr); + + // Wait until there's a speaker output + while(!(inb(PC_SPKR) & 0x20)); +} + +static uint64_t calibrate_apic_timer() { + pit_setup(10); + + APIC_REG(APIC_TIMER_DIVIDER) = 0xB; + APIC_REG(APIC_LVT(LVT_TIMER)) = 1<<16; // Mask + APIC_REG(APIC_TIMER_INIT) = 0xFFFFFFFF; + + pit_wait(); + + uint32_t ticks = APIC_REG(APIC_TIMER_CURRENT); + return (0xFFFFFFFF-ticks)/10/1000; +} + +void reset_apic_timer(uint64_t us) { + APIC_REG(APIC_TIMER_DIVIDER) = 0xB; + APIC_REG(APIC_TIMER_INIT) = us*apic_ticks_per_us; + APIC_REG(APIC_LVT(LVT_TIMER)) = 0x0 | IRQ_INTERRUPT(IRQ_TIMER); +} + +volatile int ctr = 0; + +registers *apic_timer_handler(registers *r) { + + ctr++; + __asm__("cli"); + reset_apic_timer(1000); + irq_ack(); + return r; +} + +void timer_init() { + apic_ticks_per_us = calibrate_apic_timer(); + bind_interrupt(IRQ_INTERRUPT(IRQ_TIMER), apic_timer_handler); + reset_apic_timer(1000); +} \ No newline at end of file diff --git a/src/kernel/include/cpu.h b/src/kernel/include/cpu.h index 8986cb4..5363111 100644 --- a/src/kernel/include/cpu.h +++ b/src/kernel/include/cpu.h @@ -11,6 +11,7 @@ #define IRQ_INTERRUPT(irq) (IRQ_BASE + (irq)) #define IRQ_PS2_KBD 0x1 +#define IRQ_TIMER 0x20 #define IRQ_SPURIOUS 0xFF #define APIC_BASE 0xFEE00000 @@ -91,4 +92,6 @@ void irq_ack(); void irq_mask(int irq); void irq_unmask(int irq); void ioapic_init(); -void apic_init(); \ No newline at end of file + +// cpu/timer.c +void timer_init(); \ No newline at end of file