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 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 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 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 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 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 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 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 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 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 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 348 init_hardware(void) 349 { 350 return B_OK; 351 } 352 353 354 status_t 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 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** 407 publish_devices(void) 408 { 409 return hpet_name; 410 } 411 412 413 device_hooks* 414 find_device(const char* name) 415 { 416 return &hpet_hooks; 417 } 418 419 420 //////////////////////////////////////////////////////////////////////////////// 421 // #pragma mark - 422 423 424 status_t 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 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 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 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 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 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