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