Changeset 4069f5c in mainline for uspace/drv/bus/usb/ohci/hc.c
- Timestamp:
- 2011-07-17T09:52:42Z (14 years ago)
- Branches:
- lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
- Children:
- 64639256, ca1f1ec
- Parents:
- 27eddb52 (diff), 4118f5f (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the(diff)
links above to see all the changes relative to each parent. - File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
uspace/drv/bus/usb/ohci/hc.c
r27eddb52 r4069f5c 46 46 #define OHCI_USED_INTERRUPTS \ 47 47 (I_SO | I_WDH | I_UE | I_RHSC) 48 static int interrupt_emulator(hc_t *instance); 48 49 static const irq_cmd_t ohci_irq_commands[] = 50 { 51 { .cmd = CMD_MEM_READ_32, .dstarg = 1, .addr = NULL /*filled later*/ }, 52 { .cmd = CMD_BTEST, .srcarg = 1, .dstarg = 2, .value = OHCI_USED_INTERRUPTS }, 53 { .cmd = CMD_PREDICATE, .srcarg = 2, .value = 2 }, 54 { .cmd = CMD_MEM_WRITE_A_32, .srcarg = 1, .addr = NULL /*filled later*/ }, 55 { .cmd = CMD_ACCEPT }, 56 }; 57 49 58 static void hc_gain_control(hc_t *instance); 59 static void hc_start(hc_t *instance); 50 60 static int hc_init_transfer_lists(hc_t *instance); 51 61 static int hc_init_memory(hc_t *instance); 62 static int interrupt_emulator(hc_t *instance); 63 64 /*----------------------------------------------------------------------------*/ 65 /** Get number of commands used in IRQ code. 66 * @return Number of commands. 67 */ 68 size_t hc_irq_cmd_count(void) 69 { 70 return sizeof(ohci_irq_commands) / sizeof(irq_cmd_t); 71 } 72 /*----------------------------------------------------------------------------*/ 73 /** Generate IRQ code commands. 74 * @param[out] cmds Place to store the commands. 75 * @param[in] cmd_size Size of the place (bytes). 76 * @param[in] regs Physical address of device's registers. 77 * @param[in] reg_size Size of the register area (bytes). 78 * 79 * @return Error code. 80 */ 81 int hc_get_irq_commands( 82 irq_cmd_t cmds[], size_t cmd_size, uintptr_t regs, size_t reg_size) 83 { 84 if (cmd_size < sizeof(ohci_irq_commands) 85 || reg_size < sizeof(ohci_regs_t)) 86 return EOVERFLOW; 87 88 /* Create register mapping to use in IRQ handler. 89 * This mapping should be present in kernel only. 90 * Remove it from here when kernel knows how to create mappings 91 * and accepts physical addresses in IRQ code. 92 * TODO: remove */ 93 ohci_regs_t *registers; 94 const int ret = pio_enable((void*)regs, reg_size, (void**)®isters); 95 if (ret != EOK) 96 return ret; 97 98 /* Some bogus access to force create mapping. DO NOT remove, 99 * unless whole virtual addresses in irq is replaced 100 * NOTE: Compiler won't remove this as ohci_regs_t members 101 * are declared volatile. 102 * 103 * Introducing CMD_MEM set of IRQ code commands broke 104 * assumption that IRQ code does not cause page faults. 105 * If this happens during idling (THREAD == NULL) 106 * it causes kernel panic. 107 */ 108 registers->revision; 109 110 memcpy(cmds, ohci_irq_commands, sizeof(ohci_irq_commands)); 111 112 void *address = (void*)®isters->interrupt_status; 113 cmds[0].addr = address; 114 cmds[3].addr = address; 115 return EOK; 116 } 52 117 /*----------------------------------------------------------------------------*/ 53 118 /** Announce OHCI root hub to the DDF … … 83 148 int ret = hc_add_endpoint(instance, hub_address, 0, USB_SPEED_FULL, 84 149 USB_TRANSFER_CONTROL, USB_DIRECTION_BOTH, 64, 0, 0); 85 CHECK_RET_RELEASE(ret, "Failed(%d) to add OHCI rh endpoint 0.\n", ret); 86 87 char *match_str = NULL; 88 /* DDF needs heap allocated string */ 89 ret = asprintf(&match_str, "usb&class=hub"); 90 ret = ret > 0 ? 0 : ret; 91 CHECK_RET_RELEASE(ret, "Failed(%d) to create match-id string.\n", ret); 92 93 ret = ddf_fun_add_match_id(hub_fun, match_str, 100); 94 CHECK_RET_RELEASE(ret, "Failed(%d) add root hub match-id.\n", ret); 150 CHECK_RET_RELEASE(ret, 151 "Failed to add OHCI root hub endpoint 0: %s.\n", str_error(ret)); 152 153 ret = ddf_fun_add_match_id(hub_fun, "usb&class=hub", 100); 154 CHECK_RET_RELEASE(ret, 155 "Failed to add root hub match-id: %s.\n", str_error(ret)); 95 156 96 157 ret = ddf_fun_bind(hub_fun); 97 CHECK_RET_RELEASE(ret, "Failed(%d) to bind root hub function.\n", ret); 158 CHECK_RET_RELEASE(ret, 159 "Failed to bind root hub function: %s.\n", str_error(ret)); 98 160 99 161 return EOK; … … 112 174 { 113 175 assert(instance); 114 int ret = EOK; 176 115 177 #define CHECK_RET_RETURN(ret, message...) \ 116 178 if (ret != EOK) { \ … … 119 181 } else (void)0 120 182 121 ret = pio_enable((void*)regs, reg_size, (void**)&instance->registers); 183 int ret = 184 pio_enable((void*)regs, reg_size, (void**)&instance->registers); 122 185 CHECK_RET_RETURN(ret, 123 "Failed(%d) to gain access to device registers: %s.\n", 124 ret, str_error(ret)); 186 "Failed to gain access to device registers: %s.\n", str_error(ret)); 125 187 126 188 list_initialize(&instance->pending_batches); 127 189 usb_device_keeper_init(&instance->manager); 190 128 191 ret = usb_endpoint_manager_init(&instance->ep_manager, 129 192 BANDWIDTH_AVAILABLE_USB11); … … 137 200 138 201 fibril_mutex_initialize(&instance->guard); 202 139 203 hc_gain_control(instance); 140 141 rh_init(&instance->rh, instance->registers);142 204 143 205 if (!interrupts) { … … 147 209 } 148 210 149 return EOK; 150 } 151 /*----------------------------------------------------------------------------*/ 152 /** Create end register endpoint structures 211 rh_init(&instance->rh, instance->registers); 212 hc_start(instance); 213 214 return EOK; 215 } 216 /*----------------------------------------------------------------------------*/ 217 /** Create and register endpoint structures. 153 218 * 154 219 * @param[in] instance OHCI driver structure. … … 168 233 size_t mps, size_t size, unsigned interval) 169 234 { 170 endpoint_t *ep = malloc(sizeof(endpoint_t)); 235 endpoint_t *ep = 236 endpoint_get(address, endpoint, direction, type, speed, mps); 171 237 if (ep == NULL) 172 238 return ENOMEM; 173 int ret =174 endpoint_init(ep, address, endpoint, direction, type, speed, mps);175 if (ret != EOK) {176 free(ep);177 return ret;178 }179 239 180 240 hcd_endpoint_t *hcd_ep = hcd_endpoint_assign(ep); … … 184 244 } 185 245 186 ret = usb_endpoint_manager_register_ep(&instance->ep_manager, ep, size); 246 int ret = 247 usb_endpoint_manager_register_ep(&instance->ep_manager, ep, size); 187 248 if (ret != EOK) { 188 249 hcd_endpoint_clear(ep); … … 212 273 &instance->lists[ep->transfer_type], hcd_ep); 213 274 instance->registers->control |= C_PLE | C_IE; 214 break;215 default:216 275 break; 217 276 } … … 312 371 /* Check for root hub communication */ 313 372 if (batch->ep->address == instance->rh.address) { 314 return rh_request(&instance->rh, batch); 373 rh_request(&instance->rh, batch); 374 return EOK; 315 375 } 316 376 … … 374 434 375 435 if (status & I_UE) { 376 hc_start _hw(instance);436 hc_start(instance); 377 437 } 378 438 … … 399 459 /** Turn off any (BIOS)driver that might be in control of the device. 400 460 * 461 * This function implements routines described in chapter 5.1.1.3 of the OHCI 462 * specification (page 40, pdf page 54). 463 * 401 464 * @param[in] instance OHCI hc driver structure. 402 465 */ … … 404 467 { 405 468 assert(instance); 469 406 470 usb_log_debug("Requesting OHCI control.\n"); 407 /* Turn off legacy emulation */ 408 volatile uint32_t *ohci_emulation_reg = 409 (uint32_t*)((char*)instance->registers + 0x100); 410 usb_log_debug("OHCI legacy register %p: %x.\n", 411 ohci_emulation_reg, *ohci_emulation_reg); 412 /* Do not change A20 state */ 413 *ohci_emulation_reg &= 0x100; 414 usb_log_debug("OHCI legacy register %p: %x.\n", 415 ohci_emulation_reg, *ohci_emulation_reg); 471 if (instance->registers->revision & R_LEGACY_FLAG) { 472 /* Turn off legacy emulation, it should be enough to zero 473 * the lowest bit, but it caused problems. Thus clear all 474 * except GateA20 (causes restart on some hw). 475 * See page 145 of the specs for details. 476 */ 477 volatile uint32_t *ohci_emulation_reg = 478 (uint32_t*)((char*)instance->registers + LEGACY_REGS_OFFSET); 479 usb_log_debug("OHCI legacy register %p: %x.\n", 480 ohci_emulation_reg, *ohci_emulation_reg); 481 /* Zero everything but A20State */ 482 *ohci_emulation_reg &= 0x100; 483 usb_log_debug( 484 "OHCI legacy register (should be 0 or 0x100) %p: %x.\n", 485 ohci_emulation_reg, *ohci_emulation_reg); 486 } 416 487 417 488 /* Interrupt routing enabled => smm driver is active */ … … 419 490 usb_log_debug("SMM driver: request ownership change.\n"); 420 491 instance->registers->command_status |= CS_OCR; 492 /* Hope that SMM actually knows its stuff or we can hang here */ 421 493 while (instance->registers->control & C_IR) { 422 494 async_usleep(1000); 423 495 } 424 496 usb_log_info("SMM driver: Ownership taken.\n"); 425 instance->registers->control &= (C_HCFS_RESET << C_HCFS_SHIFT);497 C_HCFS_SET(instance->registers->control, C_HCFS_RESET); 426 498 async_usleep(50000); 427 499 return; 428 500 } 429 501 430 const unsigned hc_status = 431 (instance->registers->control >> C_HCFS_SHIFT) & C_HCFS_MASK; 502 const unsigned hc_status = C_HCFS_GET(instance->registers->control); 432 503 /* Interrupt routing disabled && status != USB_RESET => BIOS active */ 433 504 if (hc_status != C_HCFS_RESET) { … … 437 508 return; 438 509 } 439 /* HC is suspended assert resume for 20ms */440 instance->registers->control &= (C_HCFS_RESUME << C_HCFS_SHIFT);510 /* HC is suspended assert resume for 20ms, */ 511 C_HCFS_SET(instance->registers->control, C_HCFS_RESUME); 441 512 async_usleep(20000); 442 513 usb_log_info("BIOS driver: HC resumed.\n"); … … 454 525 * @param[in] instance OHCI hc driver structure. 455 526 */ 456 void hc_start _hw(hc_t *instance)527 void hc_start(hc_t *instance) 457 528 { 458 529 /* OHCI guide page 42 */ … … 516 587 instance->registers->periodic_start, frame_length); 517 588 518 instance->registers->control &= (C_HCFS_OPERATIONAL << C_HCFS_SHIFT);589 C_HCFS_SET(instance->registers->control, C_HCFS_OPERATIONAL); 519 590 usb_log_debug("OHCI HC up and running (ctl_reg=0x%x).\n", 520 591 instance->registers->control); … … 534 605 int ret = endpoint_list_init(&instance->lists[type], name); \ 535 606 if (ret != EOK) { \ 536 usb_log_error("Failed (%d) to setup %s endpoint list.\n", \537 ret, name); \607 usb_log_error("Failed to setup %s endpoint list: %s.\n", \ 608 name, str_error(ret)); \ 538 609 endpoint_list_fini(&instance->lists[USB_TRANSFER_ISOCHRONOUS]);\ 539 610 endpoint_list_fini(&instance->lists[USB_TRANSFER_INTERRUPT]); \ … … 587 658 instance->lists[USB_TRANSFER_INTERRUPT].list_head_pa); 588 659 589 /* Init interrupt code */ 590 instance->interrupt_code.cmds = instance->interrupt_commands; 591 instance->interrupt_code.cmdcount = OHCI_NEEDED_IRQ_COMMANDS; 592 { 593 /* Read status register */ 594 instance->interrupt_commands[0].cmd = CMD_MEM_READ_32; 595 instance->interrupt_commands[0].dstarg = 1; 596 instance->interrupt_commands[0].addr = 597 (void*)&instance->registers->interrupt_status; 598 599 /* Test whether we are the interrupt cause */ 600 instance->interrupt_commands[1].cmd = CMD_BTEST; 601 instance->interrupt_commands[1].value = 602 OHCI_USED_INTERRUPTS; 603 instance->interrupt_commands[1].srcarg = 1; 604 instance->interrupt_commands[1].dstarg = 2; 605 606 /* Predicate cleaning and accepting */ 607 instance->interrupt_commands[2].cmd = CMD_PREDICATE; 608 instance->interrupt_commands[2].value = 2; 609 instance->interrupt_commands[2].srcarg = 2; 610 611 /* Write-clean status register */ 612 instance->interrupt_commands[3].cmd = CMD_MEM_WRITE_A_32; 613 instance->interrupt_commands[3].srcarg = 1; 614 instance->interrupt_commands[3].addr = 615 (void*)&instance->registers->interrupt_status; 616 617 /* Accept interrupt */ 618 instance->interrupt_commands[4].cmd = CMD_ACCEPT; 619 } 620 621 return EOK; 622 } 660 return EOK; 661 } 662 623 663 /** 624 664 * @}
Note:
See TracChangeset
for help on using the changeset viewer.