Enable and calibrate apic timer

This commit is contained in:
Thomas Lovén 2022-01-20 15:35:06 +01:00
parent 2e8efd4117
commit 7e3febe699
3 changed files with 82 additions and 1 deletions

View File

@ -26,4 +26,5 @@ void cpu_init() {
pic_disable(); pic_disable();
apic_init(); apic_init();
ioapic_init(); ioapic_init();
timer_init();
} }

77
src/kernel/cpu/timer.c Normal file
View File

@ -0,0 +1,77 @@
#include <stdint.h>
#include <cpu.h>
#include <ports.h>
#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);
}

View File

@ -11,6 +11,7 @@
#define IRQ_INTERRUPT(irq) (IRQ_BASE + (irq)) #define IRQ_INTERRUPT(irq) (IRQ_BASE + (irq))
#define IRQ_PS2_KBD 0x1 #define IRQ_PS2_KBD 0x1
#define IRQ_TIMER 0x20
#define IRQ_SPURIOUS 0xFF #define IRQ_SPURIOUS 0xFF
#define APIC_BASE 0xFEE00000 #define APIC_BASE 0xFEE00000
@ -91,4 +92,6 @@ void irq_ack();
void irq_mask(int irq); void irq_mask(int irq);
void irq_unmask(int irq); void irq_unmask(int irq);
void ioapic_init(); void ioapic_init();
void apic_init();
// cpu/timer.c
void timer_init();