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
32static struct hpet_regs *sHPETRegs;
33static uint64 sHPETPeriod;
34
35static area_id sHPETArea;
36
37
38struct hpet_timer_cookie {
39	int number;
40	int32 irq;
41	sem_id sem;
42	hpet_timer* timer;
43};
44
45////////////////////////////////////////////////////////////////////////////////
46
47static status_t hpet_open(const char*, uint32, void**);
48static status_t hpet_close(void*);
49static status_t hpet_free(void*);
50static status_t hpet_control(void*, uint32, void*, size_t);
51static ssize_t hpet_read(void*, off_t, void*, size_t*);
52static ssize_t hpet_write(void*, off_t, const void*, size_t*);
53
54////////////////////////////////////////////////////////////////////////////////
55
56static const char* hpet_name[] = {
57    "misc/hpet",
58    NULL
59};
60
61
62device_hooks hpet_hooks = {
63	hpet_open,
64	hpet_close,
65	hpet_free,
66	hpet_control,
67	hpet_read,
68	hpet_write,
69};
70
71int32 api_version = B_CUR_DRIVER_API_VERSION;
72
73static acpi_module_info* sAcpi;
74static int32 sOpenCount;
75
76
77static inline bigtime_t
78hpet_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
96static status_t
97hpet_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
120static status_t
121hpet_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
129static int32
130hpet_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
151static status_t
152hpet_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
162static status_t
163hpet_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
180static void
181hpet_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
217static status_t
218hpet_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
270static status_t
271hpet_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
286static status_t
287hpet_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
347status_t
348init_hardware(void)
349{
350	return B_OK;
351}
352
353
354status_t
355init_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
394void
395uninit_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
406const char**
407publish_devices(void)
408{
409	return hpet_name;
410}
411
412
413device_hooks*
414find_device(const char* name)
415{
416	return &hpet_hooks;
417}
418
419
420////////////////////////////////////////////////////////////////////////////////
421//	#pragma mark -
422
423
424status_t
425hpet_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
479status_t
480hpet_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
497status_t
498hpet_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
513status_t
514hpet_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
537ssize_t
538hpet_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
547ssize_t
548hpet_write(void* cookie, off_t position, const void* buffer, size_t* numBytes)
549{
550	*numBytes = 0;
551	return B_NOT_ALLOWED;
552}
553
554