Changeset cb89430 in mainline for uspace/drv/bus/usb/xhci/hc.c
- Timestamp:
- 2017-06-22T13:59:15Z (7 years ago)
- Branches:
- lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
- Children:
- e4d7363
- Parents:
- 62ba2cbe
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
uspace/drv/bus/usb/xhci/hc.c
r62ba2cbe rcb89430 35 35 36 36 #include <errno.h> 37 #include <str_error.h> 37 38 #include <usb/debug.h> 38 39 #include <usb/host/ddf_helpers.h> 40 #include <usb/host/utils/malloc32.h> 39 41 #include "debug.h" 40 42 #include "hc.h" 41 42 const ddf_hc_driver_t xhci_ddf_hc_driver = { 43 .hc_speed = USB_SPEED_SUPER, 44 .init = xhci_hc_init, 45 .fini = xhci_hc_fini, 46 .name = "XHCI-PCI", 47 .ops = { 48 .schedule = xhci_hc_schedule, 49 .irq_hook = xhci_hc_interrupt, 50 .status_hook = xhci_hc_status, 43 #include "hw_struct/trb.h" 44 45 static const irq_cmd_t irq_commands[] = { 46 { 47 .cmd = CMD_PIO_READ_32, 48 .dstarg = 1, 49 .addr = NULL 50 }, 51 { 52 .cmd = CMD_AND, 53 .srcarg = 1, 54 .dstarg = 2, 55 .value = 0 56 }, 57 { 58 .cmd = CMD_PREDICATE, 59 .srcarg = 2, 60 .value = 2 61 }, 62 { 63 .cmd = CMD_PIO_WRITE_A_32, 64 .srcarg = 1, 65 .addr = NULL 66 }, 67 { 68 .cmd = CMD_ACCEPT 51 69 } 52 70 }; 53 71 54 int xhci_hc_init(hcd_t *hcd, const hw_res_list_parsed_t *hw_res, bool irq) 72 /** 73 * Generates code to accept interrupts. The xHCI is designed primarily for 74 * MSI/MSI-X, but we use PCI Interrupt Pin. In this mode, all the Interrupters 75 * (except 0) are disabled. 76 */ 77 static int hc_gen_irq_code(irq_code_t *code, const hw_res_list_parsed_t *hw_res) 55 78 { 56 79 int err; 57 80 81 assert(code); 82 assert(hw_res); 83 84 if (hw_res->irqs.count != 1 || hw_res->mem_ranges.count != 1) { 85 usb_log_info("Unexpected HW resources to enable interrupts."); 86 return EINVAL; 87 } 88 89 addr_range_t mmio_range = hw_res->mem_ranges.ranges[0]; 90 91 if (RNGSZ(mmio_range) < sizeof(xhci_cap_regs_t)) 92 return EOVERFLOW; 93 94 95 xhci_cap_regs_t *cap_regs = NULL; 96 if ((err = pio_enable_range(&mmio_range, (void **)&cap_regs))) 97 return EIO; 98 99 code->ranges = malloc(sizeof(irq_pio_range_t)); 100 if (code->ranges == NULL) 101 return ENOMEM; 102 103 code->cmds = malloc(sizeof(irq_commands)); 104 if (code->cmds == NULL) { 105 free(code->ranges); 106 return ENOMEM; 107 } 108 109 code->rangecount = 1; 110 code->ranges[0] = (irq_pio_range_t) { 111 .base = RNGABS(mmio_range), 112 .size = RNGSZ(mmio_range), 113 }; 114 115 code->cmdcount = ARRAY_SIZE(irq_commands); 116 memcpy(code->cmds, irq_commands, sizeof(irq_commands)); 117 118 void *intr0_iman = RNGABSPTR(mmio_range) + XHCI_REG_RD(cap_regs, XHCI_CAP_RTSOFF) + offsetof(xhci_rt_regs_t, ir[0]); 119 code->cmds[0].addr = intr0_iman; 120 code->cmds[3].addr = intr0_iman; 121 code->cmds[1].value = host2xhci(32, 1); 122 123 return hw_res->irqs.irqs[0]; 124 } 125 126 static int hc_claim(ddf_dev_t *dev) 127 { 128 // TODO: implement handoff: section 4.22.1 129 return EOK; 130 } 131 132 static int hc_reset(xhci_hc_t *hc) 133 { 134 /* Stop the HC: set R/S to 0 */ 135 XHCI_REG_CLR(hc->op_regs, XHCI_OP_RS, 1); 136 137 /* Wait 16 ms until the HC is halted */ 138 async_usleep(16000); 139 assert(XHCI_REG_RD(hc->op_regs, XHCI_OP_HCH)); 140 141 /* Reset */ 142 XHCI_REG_SET(hc->op_regs, XHCI_OP_HCRST, 1); 143 144 /* Wait until the reset is complete */ 145 while (XHCI_REG_RD(hc->op_regs, XHCI_OP_HCRST)) 146 async_usleep(1000); 147 148 return EOK; 149 } 150 151 /** 152 * Initialize the HC: section 4.2 153 */ 154 static int hc_start(xhci_hc_t *hc, bool irq) 155 { 156 int err; 157 158 if ((err = hc_reset(hc))) 159 return err; 160 161 while (XHCI_REG_RD(hc->op_regs, XHCI_OP_CNR)) 162 async_usleep(1000); 163 164 uint64_t dcbaaptr = addr_to_phys(hc->event_ring.erst); 165 XHCI_REG_WR(hc->op_regs, XHCI_OP_DCBAAP_LO, LOWER32(dcbaaptr)); 166 XHCI_REG_WR(hc->op_regs, XHCI_OP_DCBAAP_HI, UPPER32(dcbaaptr)); 167 XHCI_REG_WR(hc->op_regs, XHCI_OP_MAX_SLOTS_EN, 0); 168 169 uint64_t crptr = xhci_trb_ring_get_dequeue_ptr(&hc->command_ring); 170 XHCI_REG_WR(hc->op_regs, XHCI_OP_CRCR_LO, LOWER32(crptr) >> 6); 171 XHCI_REG_WR(hc->op_regs, XHCI_OP_CRCR_HI, UPPER32(crptr)); 172 173 uint64_t erstptr = addr_to_phys(hc->event_ring.erst); 174 xhci_interrupter_regs_t *intr0 = &hc->rt_regs->ir[0]; 175 XHCI_REG_WR(intr0, XHCI_INTR_ERSTSZ, hc->event_ring.segment_count); 176 XHCI_REG_WR(intr0, XHCI_INTR_ERDP_LO, LOWER32(erstptr)); 177 XHCI_REG_WR(intr0, XHCI_INTR_ERDP_HI, UPPER32(erstptr)); 178 XHCI_REG_WR(intr0, XHCI_INTR_ERSTBA_LO, LOWER32(erstptr)); 179 XHCI_REG_WR(intr0, XHCI_INTR_ERSTBA_HI, UPPER32(erstptr)); 180 181 // TODO: Setup scratchpad buffers 182 183 if (irq) { 184 XHCI_REG_SET(intr0, XHCI_INTR_IE, 1); 185 XHCI_REG_SET(hc->op_regs, XHCI_OP_INTE, 1); 186 } 187 188 XHCI_REG_SET(hc->op_regs, XHCI_OP_RS, 1); 189 190 return EOK; 191 } 192 193 static int hc_init(hcd_t *hcd, const hw_res_list_parsed_t *hw_res, bool irq) 194 { 195 int err; 196 58 197 assert(hcd); 198 assert(hw_res); 59 199 assert(hcd_get_driver_data(hcd) == NULL); 60 200 201 /* Initialize the MMIO ranges */ 61 202 if (hw_res->mem_ranges.count != 1) { 62 usb_log_ debug("Unexpected MMIO area, bailing out.");203 usb_log_error("Unexpected MMIO area, bailing out."); 63 204 return EINVAL; 64 205 } 206 207 addr_range_t mmio_range = hw_res->mem_ranges.ranges[0]; 208 209 usb_log_debug("MMIO area at %p (size %zu), IRQ %d.\n", 210 RNGABSPTR(mmio_range), RNGSZ(mmio_range), hw_res->irqs.irqs[0]); 211 212 if (RNGSZ(mmio_range) < sizeof(xhci_cap_regs_t)) 213 return EOVERFLOW; 214 215 void *base; 216 if ((err = pio_enable_range(&mmio_range, &base))) 217 return err; 65 218 66 219 xhci_hc_t *hc = malloc(sizeof(xhci_hc_t)); … … 68 221 return ENOMEM; 69 222 70 addr_range_t mmio_range = hw_res->mem_ranges.ranges[0]; 71 72 usb_log_debug("MMIO area at %p (size %zu), IRQ %d.\n", 73 RNGABSPTR(mmio_range), RNGSZ(mmio_range), hw_res->irqs.irqs[0]); 74 75 if ((err = pio_enable_range(&mmio_range, (void **)&hc->cap_regs))) 223 hc->cap_regs = (xhci_cap_regs_t *) base; 224 hc->op_regs = (xhci_op_regs_t *) (base + XHCI_REG_RD(hc->cap_regs, XHCI_CAP_LENGTH)); 225 hc->rt_regs = (xhci_rt_regs_t *) (base + XHCI_REG_RD(hc->cap_regs, XHCI_CAP_RTSOFF)); 226 hc->db_arry = (xhci_doorbell_t *) (base + XHCI_REG_RD(hc->cap_regs, XHCI_CAP_DBOFF)); 227 228 usb_log_debug2("Initialized MMIO reg areas:"); 229 usb_log_debug2("\tCapability regs: %p", hc->cap_regs); 230 usb_log_debug2("\tOperational regs: %p", hc->op_regs); 231 usb_log_debug2("\tRuntime regs: %p", hc->rt_regs); 232 usb_log_debug2("\tDoorbell array base: %p", hc->db_arry); 233 234 xhci_dump_cap_regs(hc->cap_regs); 235 236 hc->ac64 = XHCI_REG_RD(hc->cap_regs, XHCI_CAP_AC64); 237 hc->max_slots = XHCI_REG_RD(hc->cap_regs, XHCI_CAP_MAX_SLOTS); 238 239 hc->dcbaa = malloc32((1 + hc->max_slots) * sizeof(xhci_device_ctx_t)); 240 if (!hc->dcbaa) 76 241 goto err_hc; 77 242 78 xhci_dump_cap_regs(hc->cap_regs); 79 80 uintptr_t base = (uintptr_t) hc->cap_regs; 81 82 hc->op_regs = (xhci_op_regs_t *) (base + XHCI_REG_RD(hc->cap_regs, XHCI_CAP_LENGTH)); 83 hc->rt_regs = (xhci_rt_regs_t *) (base + XHCI_REG_RD(hc->cap_regs, XHCI_CAP_RTSOFF)); 84 hc->db_arry = (xhci_doorbell_t *) (base + XHCI_REG_RD(hc->cap_regs, XHCI_CAP_DBOFF)); 85 86 usb_log_debug("Initialized MMIO reg areas:"); 87 usb_log_debug("\tCapability regs: %p", hc->cap_regs); 88 usb_log_debug("\tOperational regs: %p", hc->op_regs); 89 usb_log_debug("\tRuntime regs: %p", hc->rt_regs); 90 usb_log_debug("\tDoorbell array base: %p", hc->db_arry); 91 92 xhci_dump_state(hc); 243 if ((err = xhci_trb_ring_init(&hc->command_ring, hc))) 244 goto err_dcbaa; 245 246 if ((err = xhci_event_ring_init(&hc->event_ring, hc))) 247 goto err_cmd_ring; 248 249 // TODO: Allocate scratchpad buffers 93 250 94 251 hcd_set_implementation(hcd, hc, &xhci_ddf_hc_driver.ops); 95 252 96 // TODO: check if everything fits into the mmio_area 97 98 return EOK; 99 253 if ((err = hc_start(hc, irq))) 254 goto err_event_ring; 255 256 return EOK; 257 258 err_event_ring: 259 xhci_event_ring_fini(&hc->event_ring); 260 err_cmd_ring: 261 xhci_trb_ring_fini(&hc->command_ring); 262 err_dcbaa: 263 free32(hc->dcbaa); 100 264 err_hc: 101 265 free(hc); 266 hcd_set_implementation(hcd, NULL, NULL); 102 267 return err; 103 268 } 104 269 105 int xhci_hc_status(hcd_t *hcd, uint32_t *status) 106 { 107 usb_log_info("status"); 108 return ENOTSUP; 109 } 110 111 int xhci_hc_schedule(hcd_t *hcd, usb_transfer_batch_t *batch) 112 { 113 usb_log_info("schedule"); 114 return ENOTSUP; 115 } 116 117 void xhci_hc_interrupt(hcd_t *hcd, uint32_t status) 118 { 119 usb_log_info("Interrupted!"); 120 } 121 122 void xhci_hc_fini(hcd_t *hcd) 123 { 124 assert(hcd); 270 static int hc_status(hcd_t *hcd, uint32_t *status) 271 { 272 xhci_hc_t *hc = hcd_get_driver_data(hcd); 273 assert(hc); 274 assert(status); 275 276 *status = 0; 277 if (hc->op_regs) { 278 *status = XHCI_REG_RD(hc->op_regs, XHCI_OP_STATUS); 279 XHCI_REG_WR(hc->op_regs, XHCI_OP_STATUS, *status & XHCI_STATUS_ACK_MASK); 280 } 281 usb_log_debug2("HC(%p): Read status: %x", hc, *status); 282 return EOK; 283 } 284 285 static int ring_doorbell(xhci_hc_t *hc, unsigned doorbell, unsigned target) 286 { 287 uint32_t v = host2xhci(32, target & BIT_RRANGE(uint32_t, 7)); 288 pio_write_32(&hc->db_arry[doorbell], v); 289 return EOK; 290 } 291 292 static int send_no_op_command(xhci_hc_t *hc) 293 { 294 xhci_trb_t trb; 295 memset(&trb, 0, sizeof(trb)); 296 297 trb.control = host2xhci(32, XHCI_TRB_TYPE_NO_OP_CMD << 10); 298 299 xhci_trb_ring_enqueue(&hc->command_ring, &trb); 300 ring_doorbell(hc, 0, 0); 301 302 xhci_dump_trb(&trb); 303 usb_log_debug2("HC(%p): Sent TRB", hc); 304 return EOK; 305 } 306 307 static int hc_schedule(hcd_t *hcd, usb_transfer_batch_t *batch) 308 { 309 xhci_hc_t *hc = hcd_get_driver_data(hcd); 310 assert(hc); 311 312 xhci_dump_state(hc); 313 send_no_op_command(hc); 314 async_usleep(1000); 315 xhci_dump_state(hc); 316 317 xhci_dump_trb(hc->event_ring.dequeue_trb); 318 return EOK; 319 } 320 321 static void hc_run_event_ring(xhci_hc_t *hc, xhci_event_ring_t *event_ring, xhci_interrupter_regs_t *intr) 322 { 323 int err; 324 xhci_trb_t trb; 325 326 err = xhci_event_ring_dequeue(event_ring, &trb);; 327 328 switch (err) { 329 case EOK: 330 usb_log_debug2("Dequeued from event ring."); 331 xhci_dump_trb(&trb); 332 break; 333 334 case ENOENT: 335 usb_log_debug2("Event ring finished."); 336 break; 337 338 default: 339 usb_log_warning("Error while accessing event ring: %s", str_error(err)); 340 } 341 342 /* Update the ERDP to make room inthe ring */ 343 uint64_t erstptr = addr_to_phys(hc->event_ring.erst); 344 XHCI_REG_WR(intr, XHCI_INTR_ERDP_LO, LOWER32(erstptr)); 345 XHCI_REG_WR(intr, XHCI_INTR_ERDP_HI, UPPER32(erstptr)); 346 } 347 348 static void hc_interrupt(hcd_t *hcd, uint32_t status) 349 { 350 xhci_hc_t *hc = hcd_get_driver_data(hcd); 351 assert(hc); 352 353 if (status & XHCI_REG_MASK(XHCI_OP_HSE)) { 354 usb_log_error("Host controller error occured. Bad things gonna happen..."); 355 } 356 357 if (status & XHCI_REG_MASK(XHCI_OP_EINT)) { 358 usb_log_debug2("Event interrupt."); 359 360 xhci_interrupter_regs_t *intr0 = &hc->rt_regs->ir[0]; 361 362 if (XHCI_REG_RD(intr0, XHCI_INTR_IP)) { 363 XHCI_REG_SET(intr0, XHCI_INTR_IP, 1); 364 hc_run_event_ring(hc, &hc->event_ring, intr0); 365 } 366 } 367 368 if (status & XHCI_REG_MASK(XHCI_OP_PCD)) { 369 usb_log_error("Port change detected. Not implemented yet!"); 370 } 371 372 if (status & XHCI_REG_MASK(XHCI_OP_SRE)) { 373 usb_log_error("Save/Restore error occured. WTF, S/R mechanism not implemented!"); 374 } 375 } 376 377 static void hc_fini(hcd_t *hcd) 378 { 379 xhci_hc_t *hc = hcd_get_driver_data(hcd); 380 assert(hc); 381 125 382 usb_log_info("Finishing"); 126 383 127 xhci_hc_t *hc = hcd_get_driver_data(hcd); 384 xhci_trb_ring_fini(&hc->command_ring); 385 xhci_event_ring_fini(&hc->event_ring); 386 128 387 free(hc); 129 388 hcd_set_implementation(hcd, NULL, NULL); 130 389 } 131 390 391 const ddf_hc_driver_t xhci_ddf_hc_driver = { 392 .hc_speed = USB_SPEED_SUPER, 393 .irq_code_gen = hc_gen_irq_code, 394 .claim = hc_claim, 395 .init = hc_init, 396 .fini = hc_fini, 397 .name = "XHCI-PCI", 398 .ops = { 399 .schedule = hc_schedule, 400 .irq_hook = hc_interrupt, 401 .status_hook = hc_status, 402 } 403 }; 404 405 132 406 133 407 /**
Note:
See TracChangeset
for help on using the changeset viewer.