136 lines
3.8 KiB
C
136 lines
3.8 KiB
C
#include "arch/amd64/hw/ioapic.h"
|
|
#include "arch/amd64/hw/timer.h"
|
|
#include "arch/amd64/hw/vesa.h"
|
|
#include "arch/amd64/hw/apic.h"
|
|
#include "arch/amd64/hw/irq.h"
|
|
#include "arch/amd64/hw/con.h"
|
|
#include "arch/amd64/hw/idt.h"
|
|
#include "arch/amd64/hw/io.h"
|
|
#include "arch/amd64/cpu.h"
|
|
#include "sys/display.h"
|
|
#include "sys/console.h"
|
|
#include "user/time.h"
|
|
#include "sys/assert.h"
|
|
#include "sys/thread.h"
|
|
#include "sys/sched.h"
|
|
#include "sys/debug.h"
|
|
#include "sys/spin.h"
|
|
|
|
#define TIMER_PIT 1
|
|
|
|
#define PIT_FREQ_BASE 1193182
|
|
// Gives ~1kHz, ~1ms resolution
|
|
#define PIT_DIV 1193
|
|
#define PIT_CH0 0x40
|
|
#define PIT_CMD 0x43
|
|
|
|
uint64_t int_timer_ticks = 0;
|
|
|
|
static spin_t g_sleep_lock = 0;
|
|
static LIST_HEAD(g_sleep_head);
|
|
|
|
void timer_add_sleep(struct thread *thr) {
|
|
//if (thr->pid > 0)
|
|
//kdebug("Adding %d to sleepers\n", thr->pid);
|
|
uintptr_t irq;
|
|
struct io_notify *n = &thr->sleep_notify;
|
|
spin_lock_irqsave(&g_sleep_lock, &irq);
|
|
list_add(&n->link, &g_sleep_head);
|
|
spin_release_irqrestore(&g_sleep_lock, &irq);
|
|
}
|
|
|
|
void timer_remove_sleep(struct thread *thr) {
|
|
//if (thr->pid > 0)
|
|
uintptr_t irq;
|
|
//struct io_notify *n = &thr->sleep_notify;
|
|
//struct io_notify *it;
|
|
spin_lock_irqsave(&g_sleep_lock, &irq);
|
|
//list_for_each_entry(it, &g_sleep_head, link) {
|
|
// if (it == n) {
|
|
// list_del_init(&it->link);
|
|
// spin_release_irqrestore(&g_sleep_lock, &irq);
|
|
// return;
|
|
// }
|
|
//}
|
|
list_del_init(&thr->sleep_notify.link);
|
|
spin_release_irqrestore(&g_sleep_lock, &irq);
|
|
}
|
|
|
|
static uint32_t timer_tick(void *arg) {
|
|
switch ((uint64_t) arg) {
|
|
case TIMER_PIT:
|
|
++int_timer_ticks;
|
|
if (int_timer_ticks >= 300) {
|
|
g_display_blink_state ^= 1;
|
|
int_timer_ticks = 0;
|
|
}
|
|
if (int_timer_ticks % 2 == 0) {
|
|
console_update_cursor();
|
|
}
|
|
// Each tick is approx. 1ms, so add 1ms to system time
|
|
system_time += 1000000;
|
|
break;
|
|
}
|
|
|
|
//#if defined(DEBUG_COUNTERS)
|
|
static uint64_t last_debug_cycle = 0;
|
|
uint64_t delta = (system_time - last_debug_cycle) / 1000000ULL;
|
|
if (delta >= 1000) {
|
|
sched_debug_cycle(delta);
|
|
last_debug_cycle = system_time;
|
|
}
|
|
//#endif
|
|
|
|
struct list_head *iter, *b_iter;
|
|
uintptr_t irq;
|
|
spin_lock_irqsave(&g_sleep_lock, &irq);
|
|
list_for_each_safe(iter, b_iter, &g_sleep_head) {
|
|
struct io_notify *n = list_entry(iter, struct io_notify, link);
|
|
struct thread *t = n->owner;
|
|
|
|
if (!t) {
|
|
kdebug("Cleaning up ownerless sleep\n");
|
|
list_del_init(iter);
|
|
continue;
|
|
}
|
|
|
|
if (t->sleep_deadline <= system_time) {
|
|
list_del_init(iter);
|
|
thread_notify_io(n);
|
|
}
|
|
}
|
|
spin_release_irqrestore(&g_sleep_lock, &irq);
|
|
|
|
return IRQ_UNHANDLED;
|
|
}
|
|
|
|
void amd64_global_timer_init(void) {
|
|
// Initialize global timer
|
|
// Setup PIT
|
|
outb(PIT_CMD, (3 << 4 /* Write lo/hi */) | (2 << 1 /* Rate generator */));
|
|
outb(PIT_CH0, PIT_DIV & 0xFF);
|
|
outb(PIT_CH0, PIT_DIV >> 8);
|
|
|
|
irq_add_handler(2, timer_tick, (void *) TIMER_PIT);
|
|
amd64_ioapic_unmask(2);
|
|
}
|
|
|
|
void amd64_timer_init(void) {
|
|
if (get_cpu()->processor_id == 0) {
|
|
amd64_global_timer_init();
|
|
}
|
|
|
|
// Initialize CPU-local timer
|
|
kdebug("cpu%d: initializing timer\n", get_cpu()->processor_id);
|
|
|
|
// No need to calibrate CPU-local timers - precision timer is global
|
|
// LAPIC Timer is only used to trigger task switches
|
|
LAPIC(LAPIC_REG_TMRDIV) = 0x3;
|
|
LAPIC(LAPIC_REG_LVTT) = 32 | (1 << 17);
|
|
LAPIC(LAPIC_REG_TMRINITCNT) = 150000;
|
|
LAPIC(LAPIC_REG_TMRCURRCNT) = 0;
|
|
|
|
get_cpu()->ticks = 0;
|
|
asm volatile ("cli");
|
|
}
|