xref: /haiku/src/add-ons/kernel/drivers/timer/hpet.cpp (revision 3828ba8a3fa427681ab57eb534fcbc3d04e2d3dc)
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  */
6 
7 #include "hpet.h"
8 #include "hpet_interface.h"
9 #include "int.h"
10 #include "msi.h"
11 
12 #include <Drivers.h>
13 #include <KernelExport.h>
14 #include <ACPI.h>
15 #include <PCI.h>
16 
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 
21 
22 #define TRACE_HPET
23 #ifdef TRACE_HPET
24 	#define TRACE(x) dprintf x
25 #else
26 	#define TRACE(x) ;
27 #endif
28 
29 #define TEST_HPET
30 #define HPET64 0
31 
32 static struct hpet_regs *sHPETRegs;
33 static uint64 sHPETPeriod;
34 
35 static area_id sHPETArea;
36 
37 
38 struct hpet_timer_cookie {
39 	int number;
40 	int32 irq;
41 	sem_id sem;
42 	hpet_timer* timer;
43 };
44 
45 ////////////////////////////////////////////////////////////////////////////////
46 
47 static status_t hpet_open(const char*, uint32, void**);
48 static status_t hpet_close(void*);
49 static status_t hpet_free(void*);
50 static status_t hpet_control(void*, uint32, void*, size_t);
51 static ssize_t hpet_read(void*, off_t, void*, size_t*);
52 static ssize_t hpet_write(void*, off_t, const void*, size_t*);
53 
54 ////////////////////////////////////////////////////////////////////////////////
55 
56 static const char* hpet_name[] = {
57     "misc/hpet",
58     NULL
59 };
60 
61 
62 device_hooks hpet_hooks = {
63 	hpet_open,
64 	hpet_close,
65 	hpet_free,
66 	hpet_control,
67 	hpet_read,
68 	hpet_write,
69 };
70 
71 int32 api_version = B_CUR_DRIVER_API_VERSION;
72 
73 static acpi_module_info* sAcpi;
74 static int32 sOpenCount;
75 
76 
77 static inline bigtime_t
hpet_convert_timeout(const bigtime_t & relativeTimeout)78 hpet_convert_timeout(const bigtime_t &relativeTimeout)
79 {
80 #if HPET64
81 	bigtime_t counter = sHPETRegs->u0.counter64;
82 #else
83 	bigtime_t counter = sHPETRegs->u0.counter32;
84 #endif
85 	bigtime_t converted = (1000000000ULL / sHPETPeriod) * relativeTimeout;
86 
87 	dprintf("counter: %lld, relativeTimeout: %lld, converted: %lld\n",
88 		counter, relativeTimeout, converted);
89 
90 	return converted + counter;
91 }
92 
93 
94 #define MIN_TIMEOUT 1
95 
96 static status_t
hpet_set_hardware_timer(bigtime_t relativeTimeout,hpet_timer * timer)97 hpet_set_hardware_timer(bigtime_t relativeTimeout, hpet_timer *timer)
98 {
99 	// TODO:
100 	if (relativeTimeout < MIN_TIMEOUT)
101 		relativeTimeout = MIN_TIMEOUT;
102 
103 	bigtime_t timerValue = hpet_convert_timeout(relativeTimeout);
104 
105 	//dprintf("comparator: %lld, new value: %lld\n", timer->u0.comparator64, timerValue);
106 
107 #if HPET64
108 	timer->u0.comparator64 = timerValue;
109 #else
110 	timer->u0.comparator32 = timerValue;
111 #endif
112 
113 	// enable timer interrupt
114 	timer->config |= HPET_CONF_TIMER_INT_ENABLE;
115 
116 	return B_OK;
117 }
118 
119 
120 static status_t
hpet_clear_hardware_timer(hpet_timer * timer)121 hpet_clear_hardware_timer(hpet_timer *timer)
122 {
123 	// Disable timer interrupt
124 	timer->config &= ~HPET_CONF_TIMER_INT_ENABLE;
125 	return B_OK;
126 }
127 
128 
129 static int32
hpet_timer_interrupt(void * arg)130 hpet_timer_interrupt(void *arg)
131 {
132 	//dprintf("HPET timer_interrupt!!!!\n");
133 	hpet_timer_cookie* hpetCookie = (hpet_timer_cookie*)arg;
134 	hpet_timer* timer = &sHPETRegs->timer[hpetCookie->number];
135 
136 	int32 intStatus = 1 << hpetCookie->number;
137 	if (!HPET_GET_CONF_TIMER_INT_IS_LEVEL(timer)
138 			|| (sHPETRegs->interrupt_status & intStatus)) {
139 		// clear interrupt status
140 		sHPETRegs->interrupt_status |= intStatus;
141 		hpet_clear_hardware_timer(timer);
142 
143 		release_sem_etc(hpetCookie->sem, 1, B_DO_NOT_RESCHEDULE);
144 		return B_HANDLED_INTERRUPT;
145 	}
146 
147 	return B_UNHANDLED_INTERRUPT;
148 }
149 
150 
151 static status_t
hpet_set_enabled(bool enabled)152 hpet_set_enabled(bool enabled)
153 {
154 	if (enabled)
155 		sHPETRegs->config |= HPET_CONF_MASK_ENABLED;
156 	else
157 		sHPETRegs->config &= ~HPET_CONF_MASK_ENABLED;
158 	return B_OK;
159 }
160 
161 
162 static status_t
hpet_set_legacy(bool enabled)163 hpet_set_legacy(bool enabled)
164 {
165 	if (!HPET_IS_LEGACY_CAPABLE(sHPETRegs)) {
166 		dprintf("hpet_init: HPET doesn't support legacy mode.\n");
167 		return B_NOT_SUPPORTED;
168 	}
169 
170 	if (enabled)
171 		sHPETRegs->config |= HPET_CONF_MASK_LEGACY;
172 	else
173 		sHPETRegs->config &= ~HPET_CONF_MASK_LEGACY;
174 
175 	return B_OK;
176 }
177 
178 
179 #ifdef TRACE_HPET
180 static void
hpet_dump_timer(volatile struct hpet_timer * timer)181 hpet_dump_timer(volatile struct hpet_timer *timer)
182 {
183 	dprintf("HPET Timer %ld:\n", (timer - sHPETRegs->timer));
184 	dprintf("CAP/CONFIG register: 0x%llx\n", timer->config);
185 	dprintf("Capabilities:\n");
186 	dprintf("\troutable IRQs: ");
187 	uint32 interrupts = (uint32)HPET_GET_CAP_TIMER_ROUTE(timer);
188 	for (int i = 0; i < 32; i++) {
189 		if (interrupts & (1 << i))
190 			dprintf("%d ", i);
191 	}
192 
193 	dprintf("\n\tsupports FSB delivery: %s\n",
194 			timer->config & HPET_CAP_TIMER_FSB_INT_DEL ? "Yes" : "No");
195 
196 	dprintf("Configuration:\n");
197 	dprintf("\tFSB Enabled: %s\n",
198 		timer->config & HPET_CONF_TIMER_FSB_ENABLE ? "Yes" : "No");
199 	dprintf("\tInterrupt Enabled: %s\n",
200 		timer->config & HPET_CONF_TIMER_INT_ENABLE ? "Yes" : "No");
201 	dprintf("\tTimer type: %s\n",
202 		timer->config & HPET_CONF_TIMER_TYPE ? "Periodic" : "OneShot");
203 	dprintf("\tInterrupt Type: %s\n",
204 		HPET_GET_CONF_TIMER_INT_IS_LEVEL(timer) ? "Level" : "Edge");
205 
206 	dprintf("\tconfigured IRQ: %lld\n",
207 		HPET_GET_CONF_TIMER_INT_ROUTE(timer));
208 
209 	if (timer->config & HPET_CONF_TIMER_FSB_ENABLE) {
210 		dprintf("\tfsb_route[0]: 0x%llx\n", timer->fsb_route[0]);
211 		dprintf("\tfsb_route[1]: 0x%llx\n", timer->fsb_route[1]);
212 	}
213 }
214 #endif
215 
216 
217 static status_t
hpet_init_timer(hpet_timer_cookie * cookie)218 hpet_init_timer(hpet_timer_cookie* cookie)
219 {
220 	struct hpet_timer *timer = cookie->timer;
221 
222 	uint32 interrupts = (uint32)HPET_GET_CAP_TIMER_ROUTE(timer);
223 
224 	// TODO: Check if the interrupt is already used, and try another
225 	int32 interrupt = -1;
226 	for (int i = 0; i < 32; i++) {
227 		if (interrupts & (1 << i)) {
228 			interrupt = i;
229 			break;
230 		}
231 	}
232 
233 	if (interrupt == -1) {
234 		dprintf("hpet_init_timer(): timer can't be routed to any interrupt!");
235 		return B_ERROR;
236 	}
237 	// Non-periodic mode
238 	timer->config &= ~HPET_CONF_TIMER_TYPE;
239 
240 	// level triggered
241 	timer->config |= HPET_CONF_TIMER_INT_TYPE;
242 
243 	// Disable FSB/MSI
244 	timer->config &= ~HPET_CONF_TIMER_FSB_ENABLE;
245 
246 #if HPET64
247 	//disable 32 bit mode
248 	timer->config &= ~HPET_CONF_TIMER_32MODE;
249 #else
250 	//enable 32 bit mode
251 	timer->config |= HPET_CONF_TIMER_32MODE;
252 #endif
253 
254 	timer->config |= (interrupt << HPET_CONF_TIMER_INT_ROUTE_SHIFT)
255 		& HPET_CONF_TIMER_INT_ROUTE_MASK;
256 
257 	cookie->irq = interrupt = HPET_GET_CONF_TIMER_INT_ROUTE(timer);
258 	status_t status = install_io_interrupt_handler(interrupt, &hpet_timer_interrupt, cookie, 0);
259 	if (status != B_OK) {
260 		dprintf("hpet_init_timer(): cannot install interrupt handler: %s\n", strerror(status));
261 		return status;
262 	}
263 #ifdef TRACE_HPET
264 	hpet_dump_timer(timer);
265 #endif
266 	return B_OK;
267 }
268 
269 
270 static status_t
hpet_test()271 hpet_test()
272 {
273 	uint64 initialValue = sHPETRegs->u0.counter32;
274 	spin(10);
275 	uint64 finalValue = sHPETRegs->u0.counter32;
276 
277 	if (initialValue == finalValue) {
278 		dprintf("hpet_test: counter does not increment\n");
279 		return B_ERROR;
280 	}
281 
282 	return B_OK;
283 }
284 
285 
286 static status_t
hpet_init()287 hpet_init()
288 {
289 	if (sHPETRegs == NULL)
290 		return B_NO_INIT;
291 
292 	sHPETPeriod = HPET_GET_PERIOD(sHPETRegs);
293 
294 	TRACE(("hpet_init: HPET is at %p.\n"
295 			"\tVendor ID: %llx, rev: %llx, period: %lld\n",
296 		sHPETRegs, HPET_GET_VENDOR_ID(sHPETRegs), HPET_GET_REVID(sHPETRegs),
297 		sHPETPeriod));
298 
299 	status_t status = hpet_set_enabled(false);
300 	if (status != B_OK)
301 		return status;
302 
303 	status = hpet_set_legacy(false);
304 	if (status != B_OK)
305 		return status;
306 
307 	uint32 numTimers = HPET_GET_NUM_TIMERS(sHPETRegs) + 1;
308 
309 	TRACE(("hpet_init: HPET supports %lu timers, is %s bits wide, "
310 			"and is %sin legacy mode.\n",
311 			numTimers, HPET_IS_64BIT(sHPETRegs) ? "64" : "32",
312 			sHPETRegs->config & HPET_CONF_MASK_LEGACY ? "" : "not "));
313 
314 	TRACE(("hpet_init: configuration: 0x%llx, timer_interrupts: 0x%llx\n",
315 		sHPETRegs->config, sHPETRegs->interrupt_status));
316 
317 	if (numTimers < 3) {
318 		dprintf("hpet_init: HPET does not have at least 3 timers. Skipping.\n");
319 		return B_ERROR;
320 	}
321 
322 
323 #ifdef TRACE_HPET
324 	for (uint32 c = 0; c < numTimers; c++)
325 		hpet_dump_timer(&sHPETRegs->timer[c]);
326 #endif
327 
328 	sHPETRegs->interrupt_status = 0;
329 
330 	status = hpet_set_enabled(true);
331 	if (status != B_OK)
332 		return status;
333 
334 #ifdef TEST_HPET
335 	status = hpet_test();
336 	if (status != B_OK)
337 		return status;
338 #endif
339 
340 	return status;
341 }
342 
343 
344 ////////////////////////////////////////////////////////////////////////////////
345 
346 
347 status_t
init_hardware(void)348 init_hardware(void)
349 {
350 	return B_OK;
351 }
352 
353 
354 status_t
init_driver(void)355 init_driver(void)
356 {
357 	sOpenCount = 0;
358 
359 	status_t status = get_module(B_ACPI_MODULE_NAME, (module_info**)&sAcpi);
360 	if (status < B_OK)
361 		return status;
362 
363 	acpi_hpet *hpetTable;
364 	status = sAcpi->get_table(ACPI_HPET_SIGNATURE, 0,
365 		(void**)&hpetTable);
366 
367 	if (status != B_OK) {
368 		put_module(B_ACPI_MODULE_NAME);
369 		return status;
370 	}
371 
372 	sHPETArea = map_physical_memory("HPET registries",
373 					hpetTable->hpet_address.address,
374 					B_PAGE_SIZE,
375 					B_ANY_KERNEL_ADDRESS,
376 					B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA,
377 					(void**)&sHPETRegs);
378 
379 	if (sHPETArea < 0) {
380 		put_module(B_ACPI_MODULE_NAME);
381 		return sHPETArea;
382 	}
383 
384 	status = hpet_init();
385 	if (status != B_OK) {
386 		delete_area(sHPETArea);
387 		put_module(B_ACPI_MODULE_NAME);
388 	}
389 
390 	return status;
391 }
392 
393 
394 void
uninit_driver(void)395 uninit_driver(void)
396 {
397 	hpet_set_enabled(false);
398 
399 	if (sHPETArea > 0)
400 		delete_area(sHPETArea);
401 
402 	put_module(B_ACPI_MODULE_NAME);
403 }
404 
405 
406 const char**
publish_devices(void)407 publish_devices(void)
408 {
409 	return hpet_name;
410 }
411 
412 
413 device_hooks*
find_device(const char * name)414 find_device(const char* name)
415 {
416 	return &hpet_hooks;
417 }
418 
419 
420 ////////////////////////////////////////////////////////////////////////////////
421 //	#pragma mark -
422 
423 
424 status_t
hpet_open(const char * name,uint32 flags,void ** cookie)425 hpet_open(const char* name, uint32 flags, void** cookie)
426 {
427 	*cookie = NULL;
428 
429 	if (sHPETRegs == NULL)
430 		return B_NO_INIT;
431 
432 	if (atomic_add(&sOpenCount, 1) != 0) {
433 		atomic_add(&sOpenCount, -1);
434 		return B_BUSY;
435 	}
436 
437 	int timerNumber = 2;
438 		// TODO
439 
440 	char semName[B_OS_NAME_LENGTH];
441 	snprintf(semName, B_OS_NAME_LENGTH, "hpet_timer %d sem", timerNumber);
442 	sem_id sem = create_sem(0, semName);
443 	if (sem < 0) {
444 		atomic_add(&sOpenCount, -1);
445 		return sem;
446 	}
447 
448 	hpet_timer_cookie* hpetCookie = (hpet_timer_cookie*)malloc(sizeof(hpet_timer_cookie));
449 	if (hpetCookie == NULL) {
450 		delete_sem(sem);
451 		atomic_add(&sOpenCount, -1);
452 		return B_NO_MEMORY;
453 	}
454 
455 	hpetCookie->number = timerNumber;
456 	hpetCookie->timer = &sHPETRegs->timer[timerNumber];
457 	hpetCookie->sem = sem;
458 	set_sem_owner(hpetCookie->sem, B_SYSTEM_TEAM);
459 
460 	hpet_set_enabled(false);
461 
462 	status_t status = hpet_init_timer(hpetCookie);
463 	if (status != B_OK)
464 		dprintf("hpet_open: initializing timer failed: %s\n", strerror(status));
465 
466 	hpet_set_enabled(true);
467 
468 	*cookie = hpetCookie;
469 
470 	if (status != B_OK) {
471 		delete_sem(sem);
472 		free(hpetCookie);
473 		atomic_add(&sOpenCount, -1);
474 	}
475 	return status;
476 }
477 
478 
479 status_t
hpet_close(void * cookie)480 hpet_close(void* cookie)
481 {
482 	if (sHPETRegs == NULL)
483 		return B_NO_INIT;
484 
485 	atomic_add(&sOpenCount, -1);
486 
487 	hpet_timer_cookie* hpetCookie = (hpet_timer_cookie*)cookie;
488 
489 	dprintf("hpet_close (%d)\n", hpetCookie->number);
490 	hpet_clear_hardware_timer(&sHPETRegs->timer[hpetCookie->number]);
491 	remove_io_interrupt_handler(hpetCookie->irq, &hpet_timer_interrupt, hpetCookie);
492 
493 	return B_OK;
494 }
495 
496 
497 status_t
hpet_free(void * cookie)498 hpet_free(void* cookie)
499 {
500 	if (sHPETRegs == NULL)
501 		return B_NO_INIT;
502 
503 	hpet_timer_cookie* hpetCookie = (hpet_timer_cookie*)cookie;
504 
505 	delete_sem(hpetCookie->sem);
506 
507 	free(cookie);
508 
509 	return B_OK;
510 }
511 
512 
513 status_t
hpet_control(void * cookie,uint32 op,void * arg,size_t length)514 hpet_control(void* cookie, uint32 op, void* arg, size_t length)
515 {
516 	hpet_timer_cookie* hpetCookie = (hpet_timer_cookie*)cookie;
517 	status_t status = B_BAD_VALUE;
518 
519 	switch (op) {
520 		case HPET_WAIT_TIMER:
521 		{
522 			bigtime_t value = *(bigtime_t*)arg;
523 			dprintf("hpet: wait timer (%d) for %lld...\n", hpetCookie->number, value);
524 			hpet_set_hardware_timer(value, &sHPETRegs->timer[hpetCookie->number]);
525 			status = acquire_sem_etc(hpetCookie->sem, 1, B_CAN_INTERRUPT, B_INFINITE_TIMEOUT);
526 			break;
527 		}
528 		default:
529 			break;
530 
531 	}
532 
533 	return status;
534 }
535 
536 
537 ssize_t
hpet_read(void * cookie,off_t position,void * buffer,size_t * numBytes)538 hpet_read(void* cookie, off_t position, void* buffer, size_t* numBytes)
539 {
540 	//hpet_timer_cookie* hpetCookie = (hpet_timer_cookie*)cookie;
541 	*(uint64*)buffer = sHPETRegs->u0.counter64;
542 
543 	return sizeof(uint64);
544 }
545 
546 
547 ssize_t
hpet_write(void * cookie,off_t position,const void * buffer,size_t * numBytes)548 hpet_write(void* cookie, off_t position, const void* buffer, size_t* numBytes)
549 {
550 	*numBytes = 0;
551 	return B_NOT_ALLOWED;
552 }
553 
554