aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--dev/timer/x86_generic/x86_pit.c91
1 files changed, 25 insertions, 66 deletions
diff --git a/dev/timer/x86_generic/x86_pit.c b/dev/timer/x86_generic/x86_pit.c
index 9f4ba9c3..6c74e37a 100644
--- a/dev/timer/x86_generic/x86_pit.c
+++ b/dev/timer/x86_generic/x86_pit.c
@@ -29,38 +29,29 @@
#include <dev/timer/x86_pit.h>
#include <err.h>
#include <lib/fixed_point.h>
+#include <lk/trace.h>
#include <platform/interrupts.h>
#include <platform/timer.h>
+#define LOCAL_TRACE 0
+
static platform_timer_callback t_callback;
static struct fp_32_64 ms_per_tsc;
static struct fp_32_64 ns_per_tsc;
-static struct fp_32_64 ticks_per_ms;
+static struct fp_32_64 pit_ticks_per_ns;
/* The oscillator used by the PIT chip runs at 1.193182MHz */
#define INTERNAL_FREQ 1193182UL
-/* Maximum amount of time that can be program, in milliseconds */
-#define MAX_TIMER_INTERVAL 55
-
-/*
- * Freqency in HZ of the timer.
- * TODO: Select the frequency in platform_set_oneshot_timer based on the time
- * we actually need to sleep so we don't wake up more often than needed when
- * sleeping more than 55ms and so we get better precision when sleeping
- * significantly less than 55ms.
- */
-#define PIT_FREQUENCY 1000
-
/* Mode/Command register */
#define I8253_CONTROL_REG 0x43
/* Channel 0 data port */
#define I8253_DATA_REG 0x40
-static uint64_t lk_time_to_ticks(lk_time_t lk_time)
+static uint64_t lk_time_ns_to_pit_ticks(lk_time_ns_t lk_time_ns)
{
- return u64_mul_u32_fp32_64(lk_time, ticks_per_ms);
+ return u64_mul_u64_fp32_64(lk_time_ns, pit_ticks_per_ns);
}
static lk_time_t tsc_cnt_to_lk_time(uint64_t tsc_cnt)
@@ -127,7 +118,7 @@ lk_time_t current_time(void)
return tsc_cnt_to_lk_time(__rdtsc());
}
-static enum handler_return os_timer_tick(void* arg)
+static enum handler_return x86_pit_timer_interrupt_handler(void* arg)
{
if (t_callback) {
return t_callback(arg, current_time_ns());
@@ -136,43 +127,13 @@ static enum handler_return os_timer_tick(void* arg)
}
}
-static void set_pit_frequency(uint32_t frequency)
-{
- struct fp_32_64 result;
- uint16_t count;
-
- /* Figure out the correct divisor for the desired frequency */
- if (frequency < 1) {
- dprintf(SPEW, "invalid frequency, %u, use max count\n", frequency);
- count = 0xffff;
- } else if (frequency >= INTERNAL_FREQ) {
- dprintf(SPEW, "invalid frequency, %u, use min count\n", frequency);
- count = 1;
- } else {
- fp_32_64_div_32_32(&result, INTERNAL_FREQ, frequency);
- if (result.l0 > 0xffff) {
- dprintf(SPEW, "invalid frequency, %u, use max count\n", frequency);
- count = 0xffff;
- } else {
- count = result.l0 & 0xffff;
- }
- }
-
- /*
- * Setup the Programmable Interval Timer
- * Counter 0, mode 2, binary counter, LSB followed by MSB
- */
- outp(I8253_CONTROL_REG, 0x34);
- outp(I8253_DATA_REG, count & 0xff);
- outp(I8253_DATA_REG, count >> 8);
-}
-
static void x86_pit_init_conversion_factors(void)
{
uint64_t begin, end;
uint8_t status = 0;
+ uint16_t ticks_per_ms = INTERNAL_FREQ / 1000;
- fp_32_64_div_32_32(&ticks_per_ms, INTERNAL_FREQ, 1000);
+ fp_32_64_div_32_32(&pit_ticks_per_ns, INTERNAL_FREQ, 1000000000);
/* Set PIT mode to count down and set OUT pin high when count reaches 0 */
outp(I8253_CONTROL_REG, 0x30);
@@ -191,9 +152,9 @@ static void x86_pit_init_conversion_factors(void)
serializing_instruction();
/* Write LSB in counter 0 */
- outp(I8253_DATA_REG, ticks_per_ms.l0 & 0xff);
+ outp(I8253_DATA_REG, ticks_per_ms & 0xff);
/* Write MSB in counter 0 */
- outp(I8253_DATA_REG, ticks_per_ms.l0 >> 8);
+ outp(I8253_DATA_REG, ticks_per_ms >> 8);
do {
/* Read-back command, count MSB, counter 0 */
@@ -222,33 +183,30 @@ void x86_init_pit(void)
x86_pit_init_conversion_factors();
- /* 1ms granularity */
- set_pit_frequency(PIT_FREQUENCY);
-
- register_int_handler(INT_PIT, &os_timer_tick, NULL);
+ register_int_handler(INT_PIT, &x86_pit_timer_interrupt_handler, NULL);
unmask_interrupt(INT_PIT);
}
status_t platform_set_oneshot_timer(platform_timer_callback callback,
- lk_time_ns_t time_ns)
+ lk_time_ns_t time_ns_abs)
{
- uint32_t count;
+ uint64_t pit_ticks;
+ uint32_t pit_ticks_clamped;
uint16_t divisor;
- uint32_t time_ms;
+ lk_time_ns_t time_ns_rel = time_ns_abs - current_time_ns();
t_callback = callback;
- /* Set millisecond timer with interval */
- time_ms = (time_ns - current_time_ns()) / (1000UL * 1000);
- if (time_ms > MAX_TIMER_INTERVAL) {
- time_ms = MAX_TIMER_INTERVAL;
- } else if (time_ms < 1) {
- time_ms = 1;
- }
+ pit_ticks = lk_time_ns_to_pit_ticks(time_ns_rel);
+ /* Clamp ticks to 1 - 0x10000. 0 in the 16 bit counter means 0x10000 */
+ pit_ticks_clamped = MAX(1, MIN(pit_ticks, UINT16_MAX + 1));
+ divisor = pit_ticks_clamped & UINT16_MAX;
- count = lk_time_to_ticks(time_ms);
+ LTRACEF("time_ns_abs %" PRIu64 " -> time_ns_rel %" PRIu64
+ " -> pit_ticks %" PRIu64 " -> pit_ticks_clamped %" PRIu32
+ " -> pit_ticks %" PRIu16 "\n",
+ time_ns_abs, time_ns_rel, pit_ticks, pit_ticks_clamped, divisor);
- divisor = count & 0xffff;
/*
* Program PIT in the software strobe configuration, to send one pulse
* after the count reach 0
@@ -262,6 +220,7 @@ status_t platform_set_oneshot_timer(platform_timer_callback callback,
void platform_stop_timer(void)
{
+ LTRACE;
/* Enable interrupt mode that will stop the decreasing counter of the PIT */
outp(I8253_CONTROL_REG, 0x30);
}