2014-06-08 10:51:01 +04:00
|
|
|
/* vim: tabstop=4 shiftwidth=4 noexpandtab
|
2014-06-08 10:58:31 +04:00
|
|
|
* This file is part of ToaruOS and is released under the terms
|
2014-06-08 10:13:29 +04:00
|
|
|
* of the NCSA / University of Illinois License - see LICENSE.md
|
|
|
|
* Copyright (C) 2011-2013 Kevin Lange
|
2011-12-11 03:34:10 +04:00
|
|
|
*
|
|
|
|
* Programmable Interrupt Timer
|
|
|
|
*/
|
2011-01-16 19:56:44 +03:00
|
|
|
#include <system.h>
|
2011-12-15 05:06:11 +04:00
|
|
|
#include <logging.h>
|
2012-12-11 08:28:31 +04:00
|
|
|
#include <process.h>
|
2011-01-16 19:56:44 +03:00
|
|
|
|
2011-10-23 04:17:16 +04:00
|
|
|
#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
|
|
|
|
|
2012-02-05 08:29:46 +04:00
|
|
|
#define TIMER_IRQ 0
|
|
|
|
|
2015-04-15 09:05:07 +03:00
|
|
|
#define SUBTICKS_PER_TICK 1000
|
2015-05-02 09:01:12 +03:00
|
|
|
#define RESYNC_TIME 1
|
2012-12-08 07:06:43 +04:00
|
|
|
|
2011-10-23 04:17:16 +04:00
|
|
|
/*
|
|
|
|
* Set the phase (in hertz) for the Programmable
|
|
|
|
* Interrupt Timer (PIT).
|
|
|
|
*/
|
2011-01-16 19:56:44 +03:00
|
|
|
void
|
|
|
|
timer_phase(
|
|
|
|
int hz
|
|
|
|
) {
|
2011-10-23 04:17:16 +04:00
|
|
|
int divisor = PIT_SCALE / hz;
|
|
|
|
outportb(PIT_CONTROL, PIT_SET);
|
|
|
|
outportb(PIT_A, divisor & PIT_MASK);
|
|
|
|
outportb(PIT_A, (divisor >> 8) & PIT_MASK);
|
2011-01-16 19:56:44 +03:00
|
|
|
}
|
|
|
|
|
2011-10-23 04:17:16 +04:00
|
|
|
/*
|
|
|
|
* Internal timer counters
|
|
|
|
*/
|
2012-12-08 07:06:43 +04:00
|
|
|
unsigned long timer_ticks = 0;
|
2015-04-15 09:05:07 +03:00
|
|
|
unsigned long timer_subticks = 0;
|
|
|
|
signed long timer_drift = 0;
|
2015-05-02 09:01:12 +03:00
|
|
|
signed long _timer_drift = 0;
|
|
|
|
|
|
|
|
static int behind = 0;
|
2011-01-16 19:56:44 +03:00
|
|
|
|
2011-10-23 04:17:16 +04:00
|
|
|
/*
|
|
|
|
* IRQ handler for when the timer fires
|
|
|
|
*/
|
2015-05-19 07:07:06 +03:00
|
|
|
int timer_handler(struct regs *r) {
|
2015-05-02 09:01:12 +03:00
|
|
|
if (++timer_subticks == SUBTICKS_PER_TICK || (behind && ++timer_subticks == SUBTICKS_PER_TICK)) {
|
2012-12-08 07:06:43 +04:00
|
|
|
timer_ticks++;
|
|
|
|
timer_subticks = 0;
|
2015-04-15 09:05:07 +03:00
|
|
|
if (timer_ticks % RESYNC_TIME == 0) {
|
|
|
|
uint32_t new_time = read_cmos();
|
2015-05-02 09:01:12 +03:00
|
|
|
_timer_drift = new_time - boot_time - timer_ticks;
|
|
|
|
if (_timer_drift > 0) behind = 1;
|
|
|
|
else behind = 0;
|
2015-04-15 09:05:07 +03:00
|
|
|
}
|
2012-12-08 07:06:43 +04:00
|
|
|
}
|
2012-02-05 08:29:46 +04:00
|
|
|
irq_ack(TIMER_IRQ);
|
2012-12-11 08:28:31 +04:00
|
|
|
|
|
|
|
wakeup_sleepers(timer_ticks, timer_subticks);
|
2012-02-01 05:27:38 +04:00
|
|
|
switch_task(1);
|
2015-05-19 07:07:06 +03:00
|
|
|
return 1;
|
2011-01-16 19:56:44 +03:00
|
|
|
}
|
|
|
|
|
2012-12-11 08:28:31 +04:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-10-23 04:17:16 +04:00
|
|
|
/*
|
|
|
|
* Device installer for the PIT
|
|
|
|
*/
|
2013-06-06 10:10:36 +04:00
|
|
|
void timer_install(void) {
|
2012-12-08 06:33:07 +04:00
|
|
|
debug_print(NOTICE,"Initializing interval timer");
|
2015-04-15 09:05:07 +03:00
|
|
|
boot_time = read_cmos();
|
2012-02-05 08:29:46 +04:00
|
|
|
irq_install_handler(TIMER_IRQ, timer_handler);
|
2015-04-15 09:05:07 +03:00
|
|
|
timer_phase(SUBTICKS_PER_TICK); /* 100Hz */
|
2011-01-16 19:56:44 +03:00
|
|
|
}
|
|
|
|
|