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