toaruos/kernel/devices/timer.c
Kevin Lange 9a72bfc4cd Add half-assed support for IRQ chaining.
We only support 4 total chain entries at the moment, but that can be
"easily expanded"... We don't really have a lite dynamic vector, just
the big fat dynamic lists, so improving on this needs some extra effort.

This commit also drags in a bunch of random network work by necessity.
2015-05-18 21:07:06 -07:00

91 lines
2.1 KiB
C

/* vim: tabstop=4 shiftwidth=4 noexpandtab
* This file is part of ToaruOS and is released under the terms
* of the NCSA / University of Illinois License - see LICENSE.md
* Copyright (C) 2011-2013 Kevin Lange
*
* Programmable Interrupt Timer
*/
#include <system.h>
#include <logging.h>
#include <process.h>
#define PIT_A 0x40
#define PIT_B 0x41
#define PIT_C 0x42
#define PIT_CONTROL 0x43
#define PIT_MASK 0xFF
#define PIT_SCALE 1193180
#define PIT_SET 0x36
#define TIMER_IRQ 0
#define SUBTICKS_PER_TICK 1000
#define RESYNC_TIME 1
/*
* Set the phase (in hertz) for the Programmable
* Interrupt Timer (PIT).
*/
void
timer_phase(
int hz
) {
int divisor = PIT_SCALE / hz;
outportb(PIT_CONTROL, PIT_SET);
outportb(PIT_A, divisor & PIT_MASK);
outportb(PIT_A, (divisor >> 8) & PIT_MASK);
}
/*
* Internal timer counters
*/
unsigned long timer_ticks = 0;
unsigned long timer_subticks = 0;
signed long timer_drift = 0;
signed long _timer_drift = 0;
static int behind = 0;
/*
* IRQ handler for when the timer fires
*/
int timer_handler(struct regs *r) {
if (++timer_subticks == SUBTICKS_PER_TICK || (behind && ++timer_subticks == SUBTICKS_PER_TICK)) {
timer_ticks++;
timer_subticks = 0;
if (timer_ticks % RESYNC_TIME == 0) {
uint32_t new_time = read_cmos();
_timer_drift = new_time - boot_time - timer_ticks;
if (_timer_drift > 0) behind = 1;
else behind = 0;
}
}
irq_ack(TIMER_IRQ);
wakeup_sleepers(timer_ticks, timer_subticks);
switch_task(1);
return 1;
}
void relative_time(unsigned long seconds, unsigned long subseconds, unsigned long * out_seconds, unsigned long * out_subseconds) {
if (subseconds + timer_subticks > SUBTICKS_PER_TICK) {
*out_seconds = timer_ticks + seconds + 1;
*out_subseconds = (subseconds + timer_subticks) - SUBTICKS_PER_TICK;
} else {
*out_seconds = timer_ticks + seconds;
*out_subseconds = timer_subticks + subseconds;
}
}
/*
* Device installer for the PIT
*/
void timer_install(void) {
debug_print(NOTICE,"Initializing interval timer");
boot_time = read_cmos();
irq_install_handler(TIMER_IRQ, timer_handler);
timer_phase(SUBTICKS_PER_TICK); /* 100Hz */
}