1 /* 2 * Copyright 2007, Hugo Santos. All Rights Reserved. 3 * Copyright 2004, Marcus Overhagen. All Rights Reserved. 4 * Distributed under the terms of the MIT License. 5 */ 6 7 8 extern "C" { 9 #include "device.h" 10 } 11 12 #include <cstdlib> 13 #include <PCI_x86.h> 14 15 #include <arch/cpu.h> 16 #include <int.h> 17 18 extern "C" { 19 #include <compat/dev/pci/pcireg.h> 20 #include <compat/dev/pci/pcivar.h> 21 #include <compat/machine/resource.h> 22 #include <compat/sys/mutex.h> 23 #include <compat/machine/bus.h> 24 #include <compat/sys/rman.h> 25 #include <compat/sys/bus.h> 26 } 27 28 29 //#define DEBUG_BUS_SPACE_RW 30 #ifdef DEBUG_BUS_SPACE_RW 31 # define TRACE_BUS_SPACE_RW(x) driver_printf x 32 #else 33 # define TRACE_BUS_SPACE_RW(x) 34 #endif 35 36 37 struct internal_intr { 38 device_t dev; 39 driver_filter_t* filter; 40 driver_intr_t *handler; 41 void *arg; 42 int irq; 43 uint32 flags; 44 45 thread_id thread; 46 sem_id sem; 47 int32 handling; 48 }; 49 50 static int32 intr_wrapper(void *data); 51 52 53 static area_id 54 map_mem(void **virtualAddr, phys_addr_t _phy, size_t size, uint32 protection, 55 const char *name) 56 { 57 uint32 offset = _phy & (B_PAGE_SIZE - 1); 58 phys_addr_t physicalAddr = _phy - offset; 59 area_id area; 60 61 size = roundup(size + offset, B_PAGE_SIZE); 62 area = map_physical_memory(name, physicalAddr, size, B_ANY_KERNEL_ADDRESS, 63 protection, virtualAddr); 64 if (area < B_OK) 65 return area; 66 67 *virtualAddr = (uint8 *)(*virtualAddr) + offset; 68 69 return area; 70 } 71 72 73 static int 74 bus_alloc_irq_resource(device_t dev, struct resource *res) 75 { 76 uint8 irq = pci_read_config(dev, PCI_interrupt_line, 1); 77 if (irq == 0 || irq == 0xff) 78 return -1; 79 80 res->r_bustag = BUS_SPACE_TAG_IRQ; 81 res->r_bushandle = irq; 82 return 0; 83 } 84 85 86 static int 87 bus_alloc_mem_resource(device_t dev, struct resource *res, pci_info *info, 88 int bar_index) 89 { 90 phys_addr_t addr = info->u.h0.base_registers[bar_index]; 91 uint64 size = info->u.h0.base_register_sizes[bar_index]; 92 uchar flags = info->u.h0.base_register_flags[bar_index]; 93 94 // reject empty regions 95 if (size == 0) 96 return -1; 97 98 // reject I/O space 99 if ((flags & PCI_address_space) != 0) 100 return -1; 101 102 // TODO: check flags & PCI_address_prefetchable ? 103 104 if ((flags & PCI_address_type) == PCI_address_type_64) { 105 addr |= (uint64)info->u.h0.base_registers[bar_index + 1] << 32; 106 size |= (uint64)info->u.h0.base_register_sizes[bar_index + 1] << 32; 107 } 108 109 // enable this I/O resource 110 if (pci_enable_io(dev, SYS_RES_MEMORY) != 0) 111 return -1; 112 113 void *virtualAddr; 114 115 res->r_mapped_area = map_mem(&virtualAddr, addr, size, 116 B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, "bus_alloc_resource(MEMORY)"); 117 if (res->r_mapped_area < B_OK) 118 return -1; 119 120 res->r_bustag = BUS_SPACE_TAG_MEM; 121 res->r_bushandle = (bus_space_handle_t)virtualAddr; 122 return 0; 123 } 124 125 126 static int 127 bus_alloc_ioport_resource(device_t dev, struct resource *res, pci_info *info, 128 int bar_index) 129 { 130 uint32 size = info->u.h0.base_register_sizes[bar_index]; 131 uchar flags = info->u.h0.base_register_flags[bar_index]; 132 133 // reject empty regions 134 if (size == 0) 135 return -1; 136 137 // reject memory space 138 if ((flags & PCI_address_space) == 0) 139 return -1; 140 141 // enable this I/O resource 142 if (pci_enable_io(dev, SYS_RES_IOPORT) != 0) 143 return -1; 144 145 res->r_bustag = BUS_SPACE_TAG_IO; 146 res->r_bushandle = info->u.h0.base_registers[bar_index]; 147 return 0; 148 } 149 150 151 static int 152 bus_register_to_bar_index(pci_info *info, int regid) 153 { 154 // check the offset really is of a BAR 155 if (regid < PCI_base_registers || (regid % sizeof(uint32) != 0) 156 || (regid >= PCI_base_registers + 6 * (int)sizeof(uint32))) { 157 return -1; 158 } 159 160 // turn offset into array index 161 regid -= PCI_base_registers; 162 regid /= sizeof(uint32); 163 return regid; 164 } 165 166 167 struct resource * 168 bus_alloc_resource(device_t dev, int type, int *rid, unsigned long start, 169 unsigned long end, unsigned long count, uint32 flags) 170 { 171 struct resource *res; 172 int result = -1; 173 174 if (type != SYS_RES_IRQ && type != SYS_RES_MEMORY 175 && type != SYS_RES_IOPORT) 176 return NULL; 177 178 device_printf(dev, "bus_alloc_resource(%i, [%i], 0x%lx, 0x%lx, 0x%lx," 179 "0x%" B_PRIx32 ")\n", type, *rid, start, end, count, flags); 180 181 // maybe a local array of resources is enough 182 res = (struct resource *)malloc(sizeof(struct resource)); 183 if (res == NULL) 184 return NULL; 185 186 if (type == SYS_RES_IRQ) { 187 if (*rid == 0) { 188 // pinned interrupt 189 result = bus_alloc_irq_resource(dev, res); 190 } else { 191 // msi or msi-x interrupt at index *rid - 1 192 pci_info* info = get_device_pci_info(dev); 193 res->r_bustag = BUS_SPACE_TAG_MSI; 194 res->r_bushandle = info->u.h0.interrupt_line + *rid - 1; 195 result = 0; 196 } 197 } else if (type == SYS_RES_MEMORY || type == SYS_RES_IOPORT) { 198 pci_info* info = get_device_pci_info(dev); 199 int bar_index = bus_register_to_bar_index(info, *rid); 200 if (bar_index >= 0) { 201 if (type == SYS_RES_MEMORY) 202 result = bus_alloc_mem_resource(dev, res, info, bar_index); 203 else 204 result = bus_alloc_ioport_resource(dev, res, info, bar_index); 205 } 206 } 207 208 if (result < 0) { 209 free(res); 210 return NULL; 211 } 212 213 res->r_type = type; 214 return res; 215 } 216 217 218 int 219 bus_release_resource(device_t dev, int type, int rid, struct resource *res) 220 { 221 if (res->r_type != type) 222 panic("bus_release_resource: mismatch"); 223 224 if (type == SYS_RES_MEMORY) 225 delete_area(res->r_mapped_area); 226 227 free(res); 228 return 0; 229 } 230 231 232 int 233 bus_alloc_resources(device_t dev, struct resource_spec *resourceSpec, 234 struct resource **resources) 235 { 236 int i; 237 238 for (i = 0; resourceSpec[i].type != -1; i++) { 239 resources[i] = bus_alloc_resource_any(dev, 240 resourceSpec[i].type, &resourceSpec[i].rid, resourceSpec[i].flags); 241 if (resources[i] == NULL 242 && (resourceSpec[i].flags & RF_OPTIONAL) == 0) { 243 for (++i; resourceSpec[i].type != -1; i++) { 244 resources[i] = NULL; 245 } 246 247 bus_release_resources(dev, resourceSpec, resources); 248 return ENXIO; 249 } 250 } 251 return 0; 252 } 253 254 255 void 256 bus_release_resources(device_t dev, const struct resource_spec *resourceSpec, 257 struct resource **resources) 258 { 259 int i; 260 261 for (i = 0; resourceSpec[i].type != -1; i++) { 262 if (resources[i] == NULL) 263 continue; 264 265 bus_release_resource(dev, resourceSpec[i].type, resourceSpec[i].rid, 266 resources[i]); 267 resources[i] = NULL; 268 } 269 } 270 271 272 bus_space_handle_t 273 rman_get_bushandle(struct resource *res) 274 { 275 return res->r_bushandle; 276 } 277 278 279 bus_space_tag_t 280 rman_get_bustag(struct resource *res) 281 { 282 return res->r_bustag; 283 } 284 285 286 int 287 rman_get_rid(struct resource *res) 288 { 289 return 0; 290 } 291 292 293 void* 294 rman_get_virtual(struct resource *res) 295 { 296 return NULL; 297 } 298 299 300 bus_addr_t 301 rman_get_start(struct resource *res) 302 { 303 return res->r_bushandle; 304 } 305 306 307 bus_size_t 308 rman_get_size(struct resource *res) 309 { 310 area_info info; 311 if (get_area_info(res->r_mapped_area, &info) != B_OK) 312 return 0; 313 return info.size; 314 } 315 316 317 // #pragma mark - Interrupt handling 318 319 320 static int32 321 intr_wrapper(void *data) 322 { 323 struct internal_intr *intr = (struct internal_intr *)data; 324 325 //device_printf(intr->dev, "in interrupt handler.\n"); 326 327 if (!HAIKU_CHECK_DISABLE_INTERRUPTS(intr->dev)) 328 return B_UNHANDLED_INTERRUPT; 329 330 release_sem_etc(intr->sem, 1, B_DO_NOT_RESCHEDULE); 331 return intr->handling ? B_HANDLED_INTERRUPT : B_INVOKE_SCHEDULER; 332 } 333 334 335 static int32 336 intr_handler(void *data) 337 { 338 struct internal_intr *intr = (struct internal_intr *)data; 339 status_t status; 340 341 while (1) { 342 status = acquire_sem(intr->sem); 343 if (status < B_OK) 344 break; 345 346 //device_printf(intr->dev, "in soft interrupt handler.\n"); 347 348 atomic_or(&intr->handling, 1); 349 if ((intr->flags & INTR_MPSAFE) == 0) 350 mtx_lock(&Giant); 351 352 intr->handler(intr->arg); 353 354 if ((intr->flags & INTR_MPSAFE) == 0) 355 mtx_unlock(&Giant); 356 atomic_and(&intr->handling, 0); 357 HAIKU_REENABLE_INTERRUPTS(intr->dev); 358 } 359 360 return 0; 361 } 362 363 364 static void 365 free_internal_intr(struct internal_intr *intr) 366 { 367 if (intr->sem >= B_OK) { 368 status_t status; 369 delete_sem(intr->sem); 370 wait_for_thread(intr->thread, &status); 371 } 372 373 free(intr); 374 } 375 376 377 int 378 bus_setup_intr(device_t dev, struct resource *res, int flags, 379 driver_filter_t* filter, driver_intr_t handler, void *arg, void **_cookie) 380 { 381 struct internal_intr *intr = (struct internal_intr *)malloc( 382 sizeof(struct internal_intr)); 383 char semName[64]; 384 status_t status; 385 386 if (intr == NULL) 387 return B_NO_MEMORY; 388 389 intr->dev = dev; 390 intr->filter = filter; 391 intr->handler = handler; 392 intr->arg = arg; 393 intr->irq = res->r_bushandle; 394 intr->flags = flags; 395 intr->sem = -1; 396 intr->thread = -1; 397 398 if (filter != NULL) { 399 status = install_io_interrupt_handler(intr->irq, 400 (interrupt_handler)intr->filter, intr->arg, 0); 401 } else { 402 snprintf(semName, sizeof(semName), "%s intr", dev->device_name); 403 404 intr->sem = create_sem(0, semName); 405 if (intr->sem < B_OK) { 406 free(intr); 407 return B_NO_MEMORY; 408 } 409 410 snprintf(semName, sizeof(semName), "%s intr handler", dev->device_name); 411 412 intr->thread = spawn_kernel_thread(intr_handler, semName, 413 B_REAL_TIME_DISPLAY_PRIORITY, intr); 414 if (intr->thread < B_OK) { 415 delete_sem(intr->sem); 416 free(intr); 417 return B_NO_MEMORY; 418 } 419 420 status = install_io_interrupt_handler(intr->irq, 421 intr_wrapper, intr, 0); 422 } 423 424 if (status == B_OK && res->r_bustag == BUS_SPACE_TAG_MSI && gPCIx86 != NULL) { 425 // this is an msi, enable it 426 struct root_device_softc* root_softc = ((struct root_device_softc *)dev->root->softc); 427 if (root_softc->is_msi) { 428 if (gPCIx86->enable_msi(root_softc->pci_info.bus, root_softc->pci_info.device, 429 root_softc->pci_info.function) != B_OK) { 430 device_printf(dev, "enabling msi failed\n"); 431 bus_teardown_intr(dev, res, intr); 432 return ENODEV; 433 } 434 } else if (root_softc->is_msix) { 435 if (gPCIx86->enable_msix(root_softc->pci_info.bus, root_softc->pci_info.device, 436 root_softc->pci_info.function) != B_OK) { 437 device_printf(dev, "enabling msix failed\n"); 438 bus_teardown_intr(dev, res, intr); 439 return ENODEV; 440 } 441 } 442 } 443 444 if (status < B_OK) { 445 free_internal_intr(intr); 446 return status; 447 } 448 449 resume_thread(intr->thread); 450 451 *_cookie = intr; 452 return 0; 453 } 454 455 456 int 457 bus_teardown_intr(device_t dev, struct resource *res, void *arg) 458 { 459 struct internal_intr *intr = (struct internal_intr *)arg; 460 if (intr == NULL) 461 return -1; 462 463 struct root_device_softc *root = (struct root_device_softc *)dev->root->softc; 464 465 if ((root->is_msi || root->is_msix) && gPCIx86 != NULL) { 466 // disable msi generation 467 pci_info *info = &root->pci_info; 468 gPCIx86->disable_msi(info->bus, info->device, info->function); 469 } 470 471 if (intr->filter != NULL) { 472 remove_io_interrupt_handler(intr->irq, (interrupt_handler)intr->filter, 473 intr->arg); 474 } else { 475 remove_io_interrupt_handler(intr->irq, intr_wrapper, intr); 476 } 477 478 free_internal_intr(intr); 479 return 0; 480 } 481 482 483 int 484 bus_bind_intr(device_t dev, struct resource *res, int cpu) 485 { 486 if (dev->parent == NULL) 487 return EINVAL; 488 489 // TODO 490 return 0; 491 } 492 493 494 int bus_describe_intr(device_t dev, struct resource *irq, void *cookie, 495 const char* fmt, ...) 496 { 497 if (dev->parent == NULL) 498 return EINVAL; 499 500 // we don't really support names for interrupts 501 return 0; 502 } 503 504 505 // #pragma mark - bus functions 506 507 508 bus_dma_tag_t 509 bus_get_dma_tag(device_t dev) 510 { 511 return NULL; 512 } 513 514 515 int 516 bus_generic_suspend(device_t dev) 517 { 518 UNIMPLEMENTED(); 519 return B_ERROR; 520 } 521 522 523 int 524 bus_generic_resume(device_t dev) 525 { 526 UNIMPLEMENTED(); 527 return B_ERROR; 528 } 529 530 531 void 532 bus_generic_shutdown(device_t dev) 533 { 534 UNIMPLEMENTED(); 535 } 536 537 538 int 539 bus_print_child_header(device_t dev, device_t child) 540 { 541 UNIMPLEMENTED(); 542 return B_ERROR; 543 } 544 545 546 int 547 bus_print_child_footer(device_t dev, device_t child) 548 { 549 UNIMPLEMENTED(); 550 return B_ERROR; 551 } 552 553 554 int 555 bus_generic_print_child(device_t dev, device_t child) 556 { 557 UNIMPLEMENTED(); 558 return B_ERROR; 559 } 560 561 562 void 563 bus_generic_driver_added(device_t dev, driver_t *driver) 564 { 565 UNIMPLEMENTED(); 566 } 567 568 569 int 570 bus_child_present(device_t child) 571 { 572 device_t parent = device_get_parent(child); 573 if (parent == NULL) 574 return 0; 575 576 return bus_child_present(parent); 577 } 578 579 580 void 581 bus_enumerate_hinted_children(device_t bus) 582 { 583 #if 0 584 UNIMPLEMENTED(); 585 #endif 586 } 587