btrfs: Attempt to fix GCC2 build.
[haiku.git] / src / system / kernel / arch / x86 / timers / x86_hpet.cpp
blobe25785cd58f8628308770945685acbb04846f3bd
1 /*
2 * Copyright 2009-2010, Stefano Ceccherini (stefano.ceccherini@gmail.com)
3 * Copyright 2008, Dustin Howett, dustin.howett@gmail.com. All rights reserved.
4 * Distributed under the terms of the MIT License.
5 */
7 #include <debug.h>
8 #include <int.h>
9 #include <smp.h>
10 #include <timer.h>
12 #include <arch/int.h>
13 #include <arch/cpu.h>
14 #include <arch/x86/timer.h>
15 #include <arch/x86/arch_hpet.h>
17 #include <boot/kernel_args.h>
18 #include <vm/vm.h>
21 //#define TRACE_HPET
22 #ifdef TRACE_HPET
23 #define TRACE(x) dprintf x
24 #else
25 #define TRACE(x) ;
26 #endif
28 #define TEST_HPET
30 static struct hpet_regs *sHPETRegs;
31 static volatile struct hpet_timer *sTimer;
32 static uint64 sHPETPeriod;
34 static int hpet_get_priority();
35 static status_t hpet_set_hardware_timer(bigtime_t relativeTimeout);
36 static status_t hpet_clear_hardware_timer();
37 static status_t hpet_init(struct kernel_args *args);
40 struct timer_info gHPETTimer = {
41 "HPET",
42 &hpet_get_priority,
43 &hpet_set_hardware_timer,
44 &hpet_clear_hardware_timer,
45 &hpet_init
49 static int
50 hpet_get_priority()
52 // TODO: Fix HPET in SMP mode.
53 if (smp_get_num_cpus() > 1)
54 return 0;
56 // HPET timers, being off-chip, are more expensive to setup
57 // than the LAPIC.
58 return 0;
62 static int32
63 hpet_timer_interrupt(void *arg)
65 //dprintf_no_syslog("HPET timer_interrupt!!!!\n");
66 return timer_interrupt();
70 static inline bigtime_t
71 hpet_convert_timeout(const bigtime_t &relativeTimeout)
73 return ((relativeTimeout * 1000000000ULL)
74 / sHPETPeriod) + sHPETRegs->u0.counter64;
78 #define MIN_TIMEOUT 3000
80 static status_t
81 hpet_set_hardware_timer(bigtime_t relativeTimeout)
83 cpu_status state = disable_interrupts();
85 // enable timer interrupt
86 sTimer->config |= HPET_CONF_TIMER_INT_ENABLE;
88 // TODO:
89 if (relativeTimeout < MIN_TIMEOUT)
90 relativeTimeout = MIN_TIMEOUT;
92 bigtime_t timerValue = hpet_convert_timeout(relativeTimeout);
94 sTimer->u0.comparator64 = timerValue;
96 restore_interrupts(state);
98 return B_OK;
102 static status_t
103 hpet_clear_hardware_timer()
105 // Disable timer interrupt
106 sTimer->config &= ~HPET_CONF_TIMER_INT_ENABLE;
107 return B_OK;
111 static status_t
112 hpet_set_enabled(bool enabled)
114 if (enabled)
115 sHPETRegs->config |= HPET_CONF_MASK_ENABLED;
116 else
117 sHPETRegs->config &= ~HPET_CONF_MASK_ENABLED;
118 return B_OK;
122 static status_t
123 hpet_set_legacy(bool enabled)
125 if (!HPET_IS_LEGACY_CAPABLE(sHPETRegs)) {
126 dprintf("hpet_init: HPET doesn't support legacy mode. Skipping.\n");
127 return B_NOT_SUPPORTED;
130 if (enabled)
131 sHPETRegs->config |= HPET_CONF_MASK_LEGACY;
132 else
133 sHPETRegs->config &= ~HPET_CONF_MASK_LEGACY;
135 return B_OK;
139 #ifdef TRACE_HPET
140 static void
141 hpet_dump_timer(volatile struct hpet_timer *timer)
143 dprintf("HPET Timer %ld:\n", (timer - sHPETRegs->timer));
145 dprintf("\troutable IRQs: ");
146 uint32 interrupts = (uint32)HPET_GET_CAP_TIMER_ROUTE(timer);
147 for (int i = 0; i < 32; i++) {
148 if (interrupts & (1 << i))
149 dprintf("%d ", i);
151 dprintf("\n");
152 dprintf("\tconfiguration: 0x%llx\n", timer->config);
153 dprintf("\tFSB Enabled: %s\n",
154 timer->config & HPET_CONF_TIMER_FSB_ENABLE ? "Yes" : "No");
155 dprintf("\tInterrupt Enabled: %s\n",
156 timer->config & HPET_CONF_TIMER_INT_ENABLE ? "Yes" : "No");
157 dprintf("\tTimer type: %s\n",
158 timer->config & HPET_CONF_TIMER_TYPE ? "Periodic" : "OneShot");
159 dprintf("\tInterrupt Type: %s\n",
160 timer->config & HPET_CONF_TIMER_INT_TYPE ? "Level" : "Edge");
162 dprintf("\tconfigured IRQ: %lld\n",
163 HPET_GET_CONF_TIMER_INT_ROUTE(timer));
165 if (timer->config & HPET_CONF_TIMER_FSB_ENABLE) {
166 dprintf("\tfsb_route[0]: 0x%llx\n", timer->fsb_route[0]);
167 dprintf("\tfsb_route[1]: 0x%llx\n", timer->fsb_route[1]);
170 #endif
173 static void
174 hpet_init_timer(volatile struct hpet_timer *timer)
176 sTimer = timer;
178 uint32 interrupt = 0;
180 sTimer->config |= (interrupt << HPET_CONF_TIMER_INT_ROUTE_SHIFT)
181 & HPET_CONF_TIMER_INT_ROUTE_MASK;
183 // Non-periodic mode, edge triggered
184 sTimer->config &= ~(HPET_CONF_TIMER_TYPE | HPET_CONF_TIMER_INT_TYPE);
186 sTimer->config &= ~HPET_CONF_TIMER_FSB_ENABLE;
187 sTimer->config &= ~HPET_CONF_TIMER_32MODE;
189 // Enable timer
190 sTimer->config |= HPET_CONF_TIMER_INT_ENABLE;
192 #ifdef TRACE_HPET
193 hpet_dump_timer(sTimer);
194 #endif
198 static status_t
199 hpet_test()
201 uint64 initialValue = sHPETRegs->u0.counter64;
202 spin(10);
203 uint64 finalValue = sHPETRegs->u0.counter64;
205 if (initialValue == finalValue) {
206 dprintf("hpet_test: counter does not increment\n");
207 return B_ERROR;
210 return B_OK;
214 static status_t
215 hpet_init(struct kernel_args *args)
217 /* hpet_acpi_probe() through a similar "scan spots" table
218 to that of smp.cpp.
219 Seems to be the most elegant solution right now. */
220 if (args->arch_args.hpet == NULL)
221 return B_ERROR;
223 if (sHPETRegs == NULL) {
224 sHPETRegs = (struct hpet_regs *)args->arch_args.hpet.Pointer();
225 if (vm_map_physical_memory(B_SYSTEM_TEAM, "hpet",
226 (void **)&sHPETRegs, B_EXACT_ADDRESS, B_PAGE_SIZE,
227 B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA,
228 (phys_addr_t)args->arch_args.hpet_phys, true) < B_OK) {
229 // Would it be better to panic here?
230 dprintf("hpet_init: Failed to map memory for the HPET registers.");
231 return B_ERROR;
235 sHPETPeriod = HPET_GET_PERIOD(sHPETRegs);
237 TRACE(("hpet_init: HPET is at %p.\n\tVendor ID: %llx, rev: %llx, period: %lld\n",
238 sHPETRegs, HPET_GET_VENDOR_ID(sHPETRegs), HPET_GET_REVID(sHPETRegs),
239 sHPETPeriod));
241 status_t status = hpet_set_enabled(false);
242 if (status != B_OK)
243 return status;
245 status = hpet_set_legacy(true);
246 if (status != B_OK)
247 return status;
249 uint32 numTimers = HPET_GET_NUM_TIMERS(sHPETRegs) + 1;
251 TRACE(("hpet_init: HPET supports %lu timers, and is %s bits wide.\n",
252 numTimers, HPET_IS_64BIT(sHPETRegs) ? "64" : "32"));
254 TRACE(("hpet_init: configuration: 0x%llx, timer_interrupts: 0x%llx\n",
255 sHPETRegs->config, sHPETRegs->interrupt_status));
257 if (numTimers < 3) {
258 dprintf("hpet_init: HPET does not have at least 3 timers. Skipping.\n");
259 return B_ERROR;
262 #ifdef TRACE_HPET
263 for (uint32 c = 0; c < numTimers; c++)
264 hpet_dump_timer(&sHPETRegs->timer[c]);
265 #endif
267 hpet_init_timer(&sHPETRegs->timer[0]);
269 status = hpet_set_enabled(true);
270 if (status != B_OK)
271 return status;
273 #ifdef TEST_HPET
274 status = hpet_test();
275 if (status != B_OK)
276 return status;
277 #endif
279 int32 configuredIRQ = HPET_GET_CONF_TIMER_INT_ROUTE(sTimer);
281 install_io_interrupt_handler(configuredIRQ, &hpet_timer_interrupt,
282 NULL, B_NO_LOCK_VECTOR);
284 return status;