Enable and calibrate apic timer
This commit is contained in:
parent
2e8efd4117
commit
7e3febe699
@ -26,4 +26,5 @@ void cpu_init() {
|
||||
pic_disable();
|
||||
apic_init();
|
||||
ioapic_init();
|
||||
timer_init();
|
||||
}
|
77
src/kernel/cpu/timer.c
Normal file
77
src/kernel/cpu/timer.c
Normal 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);
|
||||
}
|
@ -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();
|
||||
|
||||
// cpu/timer.c
|
||||
void timer_init();
|
Loading…
x
Reference in New Issue
Block a user