1#include <acpi/hpet.h>
2#include <asm.h>
3#include <console/printf.h>
4#include <drivers/mmio.h>
5#include <log.h>
6#include <math/bit.h>
7#include <mem/page.h>
8#include <mem/vmm.h>
9#include <smp/core.h>
10#include <uacpi/tables.h>
11
12#include "uacpi/acpi.h"
13#include "uacpi/status.h"
14
15LOG_HANDLE_DEFINE_PRINT(hpet);
16
17uint64_t cc_mem_io *hpet_base;
18uint64_t hpet_timer_count;
19uint64_t hpet_fs_per_tick;
20
21void hpet_enable(void) {
22 uint64_t conf = hpet_read64(HPET_GEN_CONF_OFFSET);
23 conf |= 1; // enable bit
24 hpet_write64(HPET_GEN_CONF_OFFSET, value: conf);
25}
26
27void hpet_disable(void) {
28 uint64_t conf = hpet_read64(HPET_GEN_CONF_OFFSET);
29 conf &= ~1ULL;
30 hpet_write64(HPET_GEN_CONF_OFFSET, value: conf);
31}
32
33void hpet_clear_interrupt_status(void) {
34 hpet_write64(HPET_GEN_INT_STAT_OFFSET, value: 1);
35}
36
37static inline uint64_t hpet_us_to_ticks(uint64_t us) {
38 return (us * 1000000000ULL) / hpet_fs_per_tick;
39}
40
41void hpet_setup_periodic_interrupt_us(uint64_t microseconds_period) {
42 hpet_disable();
43
44 uint64_t ticks = hpet_us_to_ticks(us: microseconds_period);
45
46 hpet_write64(HPET_MAIN_COUNTER_OFFSET, HPET_CURRENT);
47 union hpet_timer_config timer_cfg;
48 timer_cfg.raw = hpet_read64(HPET_TIMER_CONF_OFFSET(HPET_CURRENT));
49 timer_cfg.interrupt_enable = 1;
50 timer_cfg.interrupt_type = 1;
51 timer_cfg.type = 1; // periodic
52
53 hpet_write64(HPET_TIMER_CONF_OFFSET(HPET_CURRENT), value: timer_cfg.raw);
54 hpet_write64(HPET_TIMER_COMPARATOR_OFFSET(HPET_CURRENT), value: ticks);
55 hpet_enable();
56}
57
58void hpet_program_oneshot(uint64_t future_ms) {
59
60 union hpet_timer_config conf;
61 conf.raw = hpet_read64(HPET_TIMER_CONF_OFFSET(HPET_CURRENT));
62 conf.type = 0;
63 conf.interrupt_type = 0;
64 conf.interrupt_enable = 1;
65 hpet_disable();
66 hpet_write64(HPET_TIMER_CONF_OFFSET(HPET_CURRENT), value: conf.raw);
67
68 uint64_t now = hpet_timestamp_ms();
69 uint64_t delta_us = (future_ms > now) ? (future_ms - now) * 1000 : 1000;
70 uint64_t ticks = hpet_us_to_ticks(us: delta_us);
71
72 uint64_t fire_time = hpet_read64(HPET_MAIN_COUNTER_OFFSET) + ticks;
73
74 hpet_write64(HPET_TIMER_COMPARATOR_OFFSET(HPET_CURRENT), value: fire_time);
75 hpet_enable();
76}
77
78static inline void pit_disable(void) {
79 outb(port: 0x43, value: 0x38);
80 outb(port: 0x40, value: 0xFF);
81 outb(port: 0x40, value: 0xFF);
82}
83
84void hpet_setup_timer(uint8_t timer_index, irq_t irq_line, bool periodic,
85 bool edge_triggered) {
86 uint64_t cfg_addr = HPET_TIMER_CONF_OFFSET(timer_index);
87
88 union hpet_timer_config cfg;
89 uint64_t old_config = hpet_read64(offset: cfg_addr);
90 cfg.raw = hpet_read64(offset: cfg_addr);
91
92 cfg.ioapic_route = 0;
93 cfg.ioapic_route = irq_line;
94
95 hpet_write64(offset: cfg_addr, value: cfg.raw);
96
97 union hpet_timer_config confirmed_config;
98 confirmed_config.raw = hpet_read64(offset: cfg_addr);
99 uint8_t confirmed_irq = confirmed_config.ioapic_route;
100
101 if (confirmed_irq != irq_line) {
102 log_warn_global(LOG_HANDLE(hpet),
103 "Timer %u: IRQ %u not accepted (got %u)", timer_index,
104 irq_line, confirmed_irq);
105 hpet_write64(offset: cfg_addr, value: old_config);
106 return;
107 }
108
109 cfg.ioapic_route = 0;
110 cfg.ioapic_route = irq_line;
111
112 cfg.interrupt_enable = true;
113
114 if (periodic) {
115 cfg.type = 1;
116 } else {
117 cfg.type = 0;
118 }
119
120 if (edge_triggered) {
121 cfg.interrupt_type = 0;
122 } else {
123 cfg.interrupt_type = 1;
124 }
125
126 hpet_write64(offset: cfg_addr, value: cfg.raw);
127}
128
129void hpet_init(void) {
130 struct uacpi_table hpet_table;
131 if (uacpi_table_find_by_signature(signature: "HPET", out_table: &hpet_table) != UACPI_STATUS_OK) {
132 log_global(LOG_HANDLE(hpet), LOG_ERROR, "Did not find HPET ACPI entry");
133 }
134
135 struct acpi_hpet *hpet = hpet_table.ptr;
136 uint64_t hpet_addr = hpet->address.address;
137
138 hpet_base = mmio_map(phys: hpet_addr, PAGE_SIZE);
139
140 hpet_disable();
141 hpet_write64(HPET_MAIN_COUNTER_OFFSET, value: 0);
142 uint64_t config = hpet_read64(HPET_GEN_CONF_OFFSET);
143 config = BIT_CLEAR(config, 1); // legacy replacement mode off
144 hpet_write64(HPET_GEN_CONF_OFFSET, value: config);
145
146 union hpet_timer_general_capabilities cap = {0};
147 cap.raw = hpet_read64(HPET_GEN_CAP_ID_OFFSET);
148 hpet_timer_count = cap.num_timers;
149 hpet_fs_per_tick = hpet_read64(HPET_GEN_CAP_ID_OFFSET) >> 32;
150
151 pit_disable();
152 hpet_enable();
153 hpet_timestamp_us();
154
155 log_info_global(LOG_HANDLE(hpet), "HPET initialized - %lu timers",
156 hpet_timer_count);
157}
158
159uint64_t hpet_timestamp_ns(void) {
160 uint64_t ticks = hpet_read64(HPET_MAIN_COUNTER_OFFSET);
161
162 uint64_t fs_total = ticks * hpet_fs_per_tick;
163 return fs_total / 1000000ULL;
164}
165
166uint64_t hpet_timestamp_us(void) {
167 uint64_t ticks = hpet_read64(HPET_MAIN_COUNTER_OFFSET);
168
169 uint64_t fs_total = ticks * hpet_fs_per_tick;
170 return fs_total / 1000000000ULL;
171}
172
173uint64_t hpet_timestamp_ms(void) {
174 return hpet_timestamp_us() / 1000;
175}
176