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, 0, 116 "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 // #pragma mark - Interrupt handling 301 302 303 static int32 304 intr_wrapper(void *data) 305 { 306 struct internal_intr *intr = (struct internal_intr *)data; 307 308 //device_printf(intr->dev, "in interrupt handler.\n"); 309 310 if (!HAIKU_CHECK_DISABLE_INTERRUPTS(intr->dev)) 311 return B_UNHANDLED_INTERRUPT; 312 313 release_sem_etc(intr->sem, 1, B_DO_NOT_RESCHEDULE); 314 return intr->handling ? B_HANDLED_INTERRUPT : B_INVOKE_SCHEDULER; 315 } 316 317 318 static int32 319 intr_handler(void *data) 320 { 321 struct internal_intr *intr = (struct internal_intr *)data; 322 status_t status; 323 324 while (1) { 325 status = acquire_sem(intr->sem); 326 if (status < B_OK) 327 break; 328 329 //device_printf(intr->dev, "in soft interrupt handler.\n"); 330 331 atomic_or(&intr->handling, 1); 332 intr->handler(intr->arg); 333 atomic_and(&intr->handling, 0); 334 HAIKU_REENABLE_INTERRUPTS(intr->dev); 335 } 336 337 return 0; 338 } 339 340 341 static void 342 free_internal_intr(struct internal_intr *intr) 343 { 344 if (intr->sem >= B_OK) { 345 status_t status; 346 delete_sem(intr->sem); 347 wait_for_thread(intr->thread, &status); 348 } 349 350 free(intr); 351 } 352 353 354 int 355 bus_setup_intr(device_t dev, struct resource *res, int flags, 356 driver_filter_t* filter, driver_intr_t handler, void *arg, void **_cookie) 357 { 358 /* TODO check MPSAFE etc */ 359 360 struct internal_intr *intr = (struct internal_intr *)malloc( 361 sizeof(struct internal_intr)); 362 char semName[64]; 363 status_t status; 364 365 if (intr == NULL) 366 return B_NO_MEMORY; 367 368 intr->dev = dev; 369 intr->filter = filter; 370 intr->handler = handler; 371 intr->arg = arg; 372 intr->irq = res->r_bushandle; 373 intr->flags = flags; 374 intr->sem = -1; 375 intr->thread = -1; 376 377 if (filter != NULL) { 378 status = install_io_interrupt_handler(intr->irq, 379 (interrupt_handler)intr->filter, intr->arg, 0); 380 } else { 381 snprintf(semName, sizeof(semName), "%s intr", dev->device_name); 382 383 intr->sem = create_sem(0, semName); 384 if (intr->sem < B_OK) { 385 free(intr); 386 return B_NO_MEMORY; 387 } 388 389 snprintf(semName, sizeof(semName), "%s intr handler", dev->device_name); 390 391 intr->thread = spawn_kernel_thread(intr_handler, semName, 392 B_REAL_TIME_DISPLAY_PRIORITY, intr); 393 if (intr->thread < B_OK) { 394 delete_sem(intr->sem); 395 free(intr); 396 return B_NO_MEMORY; 397 } 398 399 status = install_io_interrupt_handler(intr->irq, 400 intr_wrapper, intr, 0); 401 } 402 403 if (status == B_OK && res->r_bustag == BUS_SPACE_TAG_MSI && gPCIx86 != NULL) { 404 // this is an msi, enable it 405 struct root_device_softc* root_softc = ((struct root_device_softc *)dev->root->softc); 406 if (root_softc->is_msi) { 407 if (gPCIx86->enable_msi(root_softc->pci_info.bus, root_softc->pci_info.device, 408 root_softc->pci_info.function) != B_OK) { 409 device_printf(dev, "enabling msi failed\n"); 410 bus_teardown_intr(dev, res, intr); 411 return ENODEV; 412 } 413 } else if (root_softc->is_msix) { 414 if (gPCIx86->enable_msix(root_softc->pci_info.bus, root_softc->pci_info.device, 415 root_softc->pci_info.function) != B_OK) { 416 device_printf(dev, "enabling msix failed\n"); 417 bus_teardown_intr(dev, res, intr); 418 return ENODEV; 419 } 420 } 421 } 422 423 if (status < B_OK) { 424 free_internal_intr(intr); 425 return status; 426 } 427 428 resume_thread(intr->thread); 429 430 *_cookie = intr; 431 return 0; 432 } 433 434 435 int 436 bus_teardown_intr(device_t dev, struct resource *res, void *arg) 437 { 438 struct internal_intr *intr = (struct internal_intr *)arg; 439 if (intr == NULL) 440 return -1; 441 442 struct root_device_softc *root = (struct root_device_softc *)dev->root->softc; 443 444 if ((root->is_msi || root->is_msix) && gPCIx86 != NULL) { 445 // disable msi generation 446 pci_info *info = &root->pci_info; 447 gPCIx86->disable_msi(info->bus, info->device, info->function); 448 } 449 450 if (intr->filter != NULL) { 451 remove_io_interrupt_handler(intr->irq, (interrupt_handler)intr->filter, 452 intr->arg); 453 } else { 454 remove_io_interrupt_handler(intr->irq, intr_wrapper, intr); 455 } 456 457 free_internal_intr(intr); 458 return 0; 459 } 460 461 462 int 463 bus_bind_intr(device_t dev, struct resource *res, int cpu) 464 { 465 if (dev->parent == NULL) 466 return EINVAL; 467 468 // TODO 469 return 0; 470 } 471 472 473 int bus_describe_intr(device_t dev, struct resource *irq, void *cookie, 474 const char* fmt, ...) 475 { 476 if (dev->parent == NULL) 477 return EINVAL; 478 479 // we don't really support names for interrupts 480 return 0; 481 } 482 483 484 // #pragma mark - bus functions 485 486 487 bus_dma_tag_t 488 bus_get_dma_tag(device_t dev) 489 { 490 return NULL; 491 } 492 493 494 int 495 bus_generic_suspend(device_t dev) 496 { 497 UNIMPLEMENTED(); 498 return B_ERROR; 499 } 500 501 502 int 503 bus_generic_resume(device_t dev) 504 { 505 UNIMPLEMENTED(); 506 return B_ERROR; 507 } 508 509 510 void 511 bus_generic_shutdown(device_t dev) 512 { 513 UNIMPLEMENTED(); 514 } 515 516 517 int 518 bus_print_child_header(device_t dev, device_t child) 519 { 520 UNIMPLEMENTED(); 521 return B_ERROR; 522 } 523 524 525 int 526 bus_print_child_footer(device_t dev, device_t child) 527 { 528 UNIMPLEMENTED(); 529 return B_ERROR; 530 } 531 532 533 int 534 bus_generic_print_child(device_t dev, device_t child) 535 { 536 UNIMPLEMENTED(); 537 return B_ERROR; 538 } 539 540 541 void 542 bus_generic_driver_added(device_t dev, driver_t *driver) 543 { 544 UNIMPLEMENTED(); 545 } 546 547 548 int 549 bus_child_present(device_t child) 550 { 551 device_t parent = device_get_parent(child); 552 if (parent == NULL) 553 return 0; 554 555 return bus_child_present(parent); 556 } 557 558 559 void 560 bus_enumerate_hinted_children(device_t bus) 561 { 562 #if 0 563 UNIMPLEMENTED(); 564 #endif 565 } 566