]> pd.if.org Git - zos/blobdiff - timer.c
timer interrupt handing
[zos] / timer.c
diff --git a/timer.c b/timer.c
new file mode 100644 (file)
index 0000000..e344e91
--- /dev/null
+++ b/timer.c
@@ -0,0 +1,75 @@
+#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();
+}