summaryrefslogtreecommitdiff
path: root/src/kernel/arch/amd64/time.c
diff options
context:
space:
mode:
authordzwdz2023-09-19 15:38:34 +0200
committerdzwdz2023-09-19 15:38:34 +0200
commit8b0953d8cdbf676cf100bcb0f77a0c82755b53ae (patch)
tree9ebfdfa1bf8bd7d663b35482b622338a14c8133c /src/kernel/arch/amd64/time.c
parent14fd2aecd074fb93bb509df0c1cedd1f1055a4a6 (diff)
kernel: use HPET timer for sleeps
not strictly necessary, but this should improve: 1. sleep performance 2. power efficiency when idle
Diffstat (limited to 'src/kernel/arch/amd64/time.c')
-rw-r--r--src/kernel/arch/amd64/time.c71
1 files changed, 56 insertions, 15 deletions
diff --git a/src/kernel/arch/amd64/time.c b/src/kernel/arch/amd64/time.c
index ef76249..44bd84e 100644
--- a/src/kernel/arch/amd64/time.c
+++ b/src/kernel/arch/amd64/time.c
@@ -6,40 +6,83 @@
static Proc *scheduled = NULL;
-static void *hpet = NULL;
+static volatile void *hpet = NULL;
static uint64_t hpet_period;
+static void hpet_irq(void);
+
+enum {
+ /* bitfields */
+ HpetIs64Bit = 1<<13,
+ HpetHasLegacyMap = 1<<15,
+
+ HpetEnabled = 1<<0,
+ HpetUseLegacyMap = 1<<1,
+
+ HpetTimerTrigger = 1<<1, /* true if level triggered */
+ HpetTimerEnable = 1<<2,
+ HpetTimerPeriodic = 1<<3,
+ HpetTimer64 = 1<<5,
+};
+
void hpet_init(void *base) {
if (hpet) panic_invalid_state();
hpet = base;
- uint64_t *info = hpet;
- uint64_t *cfg = hpet + 0x10;
+ volatile uint64_t *info = hpet;
+ volatile uint64_t *cfg = hpet + 0x10;
hpet_period = *info >> 32;
- *cfg |= 1<<0; /* enable */
+ if (!(*info & HpetIs64Bit)) panic_unimplemented();
+ if (!(*info & HpetHasLegacyMap)) panic_unimplemented();
+
+ volatile uint64_t *timcfg = hpet + 0x120;
+ if (!(*timcfg & HpetTimer64)) panic_unimplemented();
+ *timcfg |= HpetTimerTrigger;
+ *timcfg &= ~HpetTimerPeriodic;
+ *cfg |= HpetEnabled | HpetUseLegacyMap;
+
+ irq_fn[IRQ_HPET] = hpet_irq;
}
-uint64_t uptime_ns(void) {
+static void hpet_sched(void) {
+ volatile uint64_t *status = hpet + 0x20;
+ volatile uint64_t *timcfg = hpet + 0x120;
+ volatile uint64_t *timval = hpet + 0x128;
+ if (scheduled) {
+ *timval = scheduled->waits4timer.goal;
+ *timcfg |= HpetTimerEnable;
+ } else {
+ *timcfg &= ~HpetTimerEnable;
+ }
+ *status = ~0;
+}
+
+static uint64_t get_counter(void) {
if (hpet == NULL) panic_invalid_state();
- uint64_t *counter = hpet + 0xF0;
- unsigned __int128 femto = *counter * hpet_period;
+ volatile uint64_t *counter = hpet + 0xF0;
+ return *counter;
+}
+
+uint64_t uptime_ns(void) {
+ unsigned __int128 femto = get_counter() * hpet_period;
uint64_t nano = femto / 1000000;
return nano;
}
-static void pit_irq(void) {
+static void hpet_irq(void) {
Proc *p = scheduled;
- if (p && p->waits4timer.goal < uptime_ns()) {
+ if (p && p->waits4timer.goal < get_counter()) {
scheduled = p->waits4timer.next;
proc_setstate(p, PS_RUNNING);
}
+ hpet_sched();
}
void timer_schedule(Proc *p, uint64_t time) {
- uint64_t now = uptime_ns();
+ uint64_t now = get_counter();
/* to prevent leaking the current uptime, saturate instead of overflowing */
- time = now + time;
+ time = now + (time * 1000000 / hpet_period);
if (time < now) {
time = ~0;
}
@@ -54,6 +97,7 @@ void timer_schedule(Proc *p, uint64_t time) {
}
p->waits4timer.next = *slot;
*slot = p;
+ hpet_sched(); // TODO can sometimes be skipped
}
void timer_deschedule(Proc *p) {
@@ -66,8 +110,5 @@ void timer_deschedule(Proc *p) {
*slot = p->waits4timer.next;
proc_setstate(p, PS_RUNNING);
-}
-
-void timer_init(void) {
- irq_fn[IRQ_PIT] = pit_irq;
+ hpet_sched(); // TODO can sometimes be skipped
}