--- /dev/null
+#include <stdint.h>
+
+#include "interrupt.h"
+#include "kernel.h"
+#include "timer.h"
+
+#include "ioport.h"
+#include "cpu.h"
+
+#include <time.h>
+
+static struct interrupt_handler timer;
+
+/* This will keep track of how many ticks that the system
+* has been running for */
+volatile uint64_t timer_ticks = 0;
+static int timer_hz = 18;
+
+static uint64_t timer_ns; /* nanoseconds per time tick */
+
+struct timespec system_time; /* global system time */
+
+#define ONE_BILLION 1000000000ULL
+
+/* See http://wiki.osdev.org/Programmable_Interval_Timer
+ * for a possibly better algorithm
+ */
+static void setfreq(int hz) {
+ int enable;
+ unsigned int divisor;
+
+ if (hz < 18) hz = 18;
+ divisor = 1193182/hz;
+
+ timer_hz = 1193182 / divisor;
+
+#if 0
+ printk("timer hz %llu\n", (uint64_t)hz);
+ printk("timer divisor %u\n", divisor);
+ printk("timer hz actual %llu\n", (uint64_t)timer_hz);
+#endif
+
+ enable = interrupts_enabled();
+ if (enable) disable_interrupts();
+ outport8(0x43, 0x34);
+ outport8(0x40, (uint8_t)(divisor & 0xFF)); /* Set low byte of divisor */
+ outport8(0x40, (uint8_t)(divisor >> 8 & 0xFF)); /* Set high byte of divisor */
+
+ timer_ns = (uint64_t)1000000000ULL / timer_hz;
+ /* now, let's disable the pit and use the RTC */
+ if (enable) enable_interrupts();
+}
+
+static void timer_handler(struct interrupt_context *c, void *unused) {
+ timer_ticks++;
+ system_time.tv_nsec += timer_ns;
+ while (system_time.tv_nsec >= ONE_BILLION) {
+ system_time.tv_sec++;
+ system_time.tv_nsec -= ONE_BILLION;
+ }
+}
+
+void init_timer(int hz) {
+ int enable;
+ timer.handler = timer_handler;
+ timer.context = 0;
+ system_time.tv_sec = 0;
+ system_time.tv_nsec = 0;
+
+ enable = interrupts_enabled();
+ if (enable) disable_interrupts();
+ interrupt_add_handler(IRQ0, &timer);
+ setfreq(hz);
+ if (enable) enable_interrupts();
+}