source: mainline/uspace/drv/bus/usb/xhci/hc.c@ ef1a3a8

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since ef1a3a8 was 9620a54, checked in by Petr Manek <petr.manek@…>, 8 years ago

Small changes. Temporarily fixed no device problem for endpoint logging. Added similar macro for device logging. Changed log messages to adopt these macros. TRB rings can be freed again. Made ring finalizers noexcept. Upon detach, the entire slot is disabled prior to unregistering endpoints in order to prevent invalid HC commands. Removed active endpoints count from XHCI device. Device context is freed in HC, so DCBAA is not touched from anywhere else.

  • Property mode set to 100644
File size: 21.3 KB
Line 
1/*
2 * Copyright (c) 2017 Ondrej Hlavaty
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup drvusbxhci
30 * @{
31 */
32/** @file
33 * @brief The host controller data bookkeeping.
34 */
35
36#include <errno.h>
37#include <str_error.h>
38#include <usb/debug.h>
39#include <usb/host/endpoint.h>
40#include <usb/host/utils/malloc32.h>
41#include "debug.h"
42#include "hc.h"
43#include "rh.h"
44#include "hw_struct/trb.h"
45#include "hw_struct/context.h"
46#include "endpoint.h"
47#include "commands.h"
48#include "transfers.h"
49#include "trb_ring.h"
50
51/**
52 * Default USB Speed ID mapping: Table 157
53 */
54#define PSI_TO_BPS(psie, psim) (((uint64_t) psim) << (10 * psie))
55#define PORT_SPEED(usb, mjr, psie, psim) { \
56 .name = "USB ", \
57 .major = mjr, \
58 .minor = 0, \
59 .usb_speed = USB_SPEED_##usb, \
60 .rx_bps = PSI_TO_BPS(psie, psim), \
61 .tx_bps = PSI_TO_BPS(psie, psim) \
62}
63static const xhci_port_speed_t ps_default_full = PORT_SPEED(FULL, 2, 2, 12);
64static const xhci_port_speed_t ps_default_low = PORT_SPEED(LOW, 2, 1, 1500);
65static const xhci_port_speed_t ps_default_high = PORT_SPEED(HIGH, 2, 2, 480);
66static const xhci_port_speed_t ps_default_super = PORT_SPEED(SUPER, 3, 3, 5);
67
68/**
69 * Walk the list of extended capabilities.
70 */
71static int hc_parse_ec(xhci_hc_t *hc)
72{
73 unsigned psic, major, minor;
74 xhci_sp_name_t name;
75
76 xhci_port_speed_t *speeds = hc->speeds;
77
78 for (xhci_extcap_t *ec = hc->xecp; ec; ec = xhci_extcap_next(ec)) {
79 xhci_dump_extcap(ec);
80 switch (XHCI_REG_RD(ec, XHCI_EC_CAP_ID)) {
81 case XHCI_EC_USB_LEGACY:
82 assert(hc->legsup == NULL);
83 hc->legsup = (xhci_legsup_t *) ec;
84 break;
85 case XHCI_EC_SUPPORTED_PROTOCOL:
86 psic = XHCI_REG_RD(ec, XHCI_EC_SP_PSIC);
87 major = XHCI_REG_RD(ec, XHCI_EC_SP_MAJOR);
88 minor = XHCI_REG_RD(ec, XHCI_EC_SP_MINOR);
89 name.packed = host2uint32_t_le(XHCI_REG_RD(ec, XHCI_EC_SP_NAME));
90
91 if (name.packed != xhci_name_usb.packed) {
92 /**
93 * The detection of such protocol would work,
94 * but the rest of the implementation is made
95 * for the USB protocol only.
96 */
97 usb_log_error("Unknown protocol %.4s.", name.str);
98 return ENOTSUP;
99 }
100
101 // "Implied" speed
102 if (psic == 0) {
103 assert(minor == 0);
104
105 if (major == 2) {
106 speeds[1] = ps_default_full;
107 speeds[2] = ps_default_low;
108 speeds[3] = ps_default_high;
109
110 hc->speed_to_psiv[USB_SPEED_FULL] = 1;
111 hc->speed_to_psiv[USB_SPEED_LOW] = 2;
112 hc->speed_to_psiv[USB_SPEED_HIGH] = 3;
113 } else if (major == 3) {
114 speeds[4] = ps_default_super;
115 hc->speed_to_psiv[USB_SPEED_SUPER] = 4;
116 } else {
117 return EINVAL;
118 }
119
120 usb_log_debug2("Implied speed of USB %u.0 set up.", major);
121 } else {
122 for (unsigned i = 0; i < psic; i++) {
123 xhci_psi_t *psi = xhci_extcap_psi(ec, i);
124 unsigned sim = XHCI_REG_RD(psi, XHCI_PSI_PSIM);
125 unsigned psiv = XHCI_REG_RD(psi, XHCI_PSI_PSIV);
126 unsigned psie = XHCI_REG_RD(psi, XHCI_PSI_PSIE);
127 unsigned psim = XHCI_REG_RD(psi, XHCI_PSI_PSIM);
128
129 speeds[psiv].major = major;
130 speeds[psiv].minor = minor;
131 str_ncpy(speeds[psiv].name, 4, name.str, 4);
132 speeds[psiv].usb_speed = USB_SPEED_MAX;
133
134 uint64_t bps = PSI_TO_BPS(psie, psim);
135
136 if (sim == XHCI_PSI_PLT_SYMM || sim == XHCI_PSI_PLT_RX)
137 speeds[psiv].rx_bps = bps;
138 if (sim == XHCI_PSI_PLT_SYMM || sim == XHCI_PSI_PLT_TX) {
139 speeds[psiv].tx_bps = bps;
140 usb_log_debug2("Speed %u set up for bps %" PRIu64 " / %" PRIu64 ".", psiv, speeds[psiv].rx_bps, speeds[psiv].tx_bps);
141 }
142 }
143 }
144 }
145 }
146 return EOK;
147}
148
149int hc_init_mmio(xhci_hc_t *hc, const hw_res_list_parsed_t *hw_res)
150{
151 int err;
152
153 if (hw_res->mem_ranges.count != 1) {
154 usb_log_error("Unexpected MMIO area, bailing out.");
155 return EINVAL;
156 }
157
158 hc->mmio_range = hw_res->mem_ranges.ranges[0];
159
160 usb_log_debug("MMIO area at %p (size %zu), IRQ %d.\n",
161 RNGABSPTR(hc->mmio_range), RNGSZ(hc->mmio_range), hw_res->irqs.irqs[0]);
162
163 if (RNGSZ(hc->mmio_range) < sizeof(xhci_cap_regs_t))
164 return EOVERFLOW;
165
166 void *base;
167 if ((err = pio_enable_range(&hc->mmio_range, &base)))
168 return err;
169
170 hc->reg_base = base;
171 hc->cap_regs = (xhci_cap_regs_t *) base;
172 hc->op_regs = (xhci_op_regs_t *) (base + XHCI_REG_RD(hc->cap_regs, XHCI_CAP_LENGTH));
173 hc->rt_regs = (xhci_rt_regs_t *) (base + XHCI_REG_RD(hc->cap_regs, XHCI_CAP_RTSOFF));
174 hc->db_arry = (xhci_doorbell_t *) (base + XHCI_REG_RD(hc->cap_regs, XHCI_CAP_DBOFF));
175
176 uintptr_t xec_offset = XHCI_REG_RD(hc->cap_regs, XHCI_CAP_XECP) * sizeof(xhci_dword_t);
177 if (xec_offset > 0)
178 hc->xecp = (xhci_extcap_t *) (base + xec_offset);
179
180 usb_log_debug2("Initialized MMIO reg areas:");
181 usb_log_debug2("\tCapability regs: %p", hc->cap_regs);
182 usb_log_debug2("\tOperational regs: %p", hc->op_regs);
183 usb_log_debug2("\tRuntime regs: %p", hc->rt_regs);
184 usb_log_debug2("\tDoorbell array base: %p", hc->db_arry);
185
186 xhci_dump_cap_regs(hc->cap_regs);
187
188 hc->ac64 = XHCI_REG_RD(hc->cap_regs, XHCI_CAP_AC64);
189 hc->max_slots = XHCI_REG_RD(hc->cap_regs, XHCI_CAP_MAX_SLOTS);
190
191 if ((err = hc_parse_ec(hc))) {
192 pio_disable(hc->reg_base, RNGSZ(hc->mmio_range));
193 return err;
194 }
195
196 return EOK;
197}
198
199int hc_init_memory(xhci_hc_t *hc, ddf_dev_t *device)
200{
201 int err;
202
203 hc->dcbaa = malloc32((1 + hc->max_slots) * sizeof(uint64_t));
204 if (!hc->dcbaa)
205 return ENOMEM;
206
207 if ((err = xhci_trb_ring_init(&hc->command_ring)))
208 goto err_dcbaa;
209
210 if ((err = xhci_event_ring_init(&hc->event_ring)))
211 goto err_cmd_ring;
212
213 if ((err = xhci_scratchpad_alloc(hc)))
214 goto err_event_ring;
215
216 if ((err = xhci_init_commands(hc)))
217 goto err_scratch;
218
219 if ((err = xhci_rh_init(&hc->rh, hc, device)))
220 goto err_cmd;
221
222 if ((err = xhci_bus_init(&hc->bus, hc)))
223 goto err_rh;
224
225
226 return EOK;
227
228err_rh:
229 xhci_rh_fini(&hc->rh);
230err_cmd:
231 xhci_fini_commands(hc);
232err_scratch:
233 xhci_scratchpad_free(hc);
234err_event_ring:
235 xhci_event_ring_fini(&hc->event_ring);
236err_cmd_ring:
237 xhci_trb_ring_fini(&hc->command_ring);
238err_dcbaa:
239 free32(hc->dcbaa);
240 return err;
241}
242
243/*
244 * Pseudocode:
245 * ip = read(intr[0].iman)
246 * if (ip) {
247 * status = read(usbsts)
248 * assert status
249 * assert ip
250 * accept (passing status)
251 * }
252 * decline
253 */
254static const irq_cmd_t irq_commands[] = {
255 {
256 .cmd = CMD_PIO_READ_32,
257 .dstarg = 3,
258 .addr = NULL /* intr[0].iman */
259 },
260 {
261 .cmd = CMD_AND,
262 .srcarg = 3,
263 .dstarg = 4,
264 .value = 0 /* host2xhci(32, 1) */
265 },
266 {
267 .cmd = CMD_PREDICATE,
268 .srcarg = 4,
269 .value = 5
270 },
271 {
272 .cmd = CMD_PIO_READ_32,
273 .dstarg = 1,
274 .addr = NULL /* usbsts */
275 },
276 {
277 .cmd = CMD_AND,
278 .srcarg = 1,
279 .dstarg = 2,
280 .value = 0 /* host2xhci(32, XHCI_STATUS_ACK_MASK) */
281 },
282 {
283 .cmd = CMD_PIO_WRITE_A_32,
284 .srcarg = 2,
285 .addr = NULL /* usbsts */
286 },
287 {
288 .cmd = CMD_PIO_WRITE_A_32,
289 .srcarg = 3,
290 .addr = NULL /* intr[0].iman */
291 },
292 {
293 .cmd = CMD_ACCEPT
294 },
295 {
296 .cmd = CMD_DECLINE
297 }
298};
299
300
301/**
302 * Generates code to accept interrupts. The xHCI is designed primarily for
303 * MSI/MSI-X, but we use PCI Interrupt Pin. In this mode, all the Interrupters
304 * (except 0) are disabled.
305 */
306int hc_irq_code_gen(irq_code_t *code, xhci_hc_t *hc, const hw_res_list_parsed_t *hw_res)
307{
308 assert(code);
309 assert(hw_res);
310
311 if (hw_res->irqs.count != 1) {
312 usb_log_info("Unexpected HW resources to enable interrupts.");
313 return EINVAL;
314 }
315
316 code->ranges = malloc(sizeof(irq_pio_range_t));
317 if (code->ranges == NULL)
318 return ENOMEM;
319
320 code->cmds = malloc(sizeof(irq_commands));
321 if (code->cmds == NULL) {
322 free(code->ranges);
323 return ENOMEM;
324 }
325
326 code->rangecount = 1;
327 code->ranges[0] = (irq_pio_range_t) {
328 .base = RNGABS(hc->mmio_range),
329 .size = RNGSZ(hc->mmio_range),
330 };
331
332 code->cmdcount = ARRAY_SIZE(irq_commands);
333 memcpy(code->cmds, irq_commands, sizeof(irq_commands));
334
335 void *intr0_iman = RNGABSPTR(hc->mmio_range) + XHCI_REG_RD(hc->cap_regs, XHCI_CAP_RTSOFF) + offsetof(xhci_rt_regs_t, ir[0]);
336 void *usbsts = RNGABSPTR(hc->mmio_range) + XHCI_REG_RD(hc->cap_regs, XHCI_CAP_LENGTH) + offsetof(xhci_op_regs_t, usbsts);
337 code->cmds[0].addr = intr0_iman;
338 code->cmds[1].value = host2xhci(32, 1);
339 code->cmds[3].addr = usbsts;
340 code->cmds[4].value = host2xhci(32, XHCI_STATUS_ACK_MASK);
341 code->cmds[5].addr = usbsts;
342 code->cmds[6].addr = intr0_iman;
343
344 return hw_res->irqs.irqs[0];
345}
346
347int hc_claim(xhci_hc_t *hc, ddf_dev_t *dev)
348{
349 /* No legacy support capability, the controller is solely for us */
350 if (!hc->legsup)
351 return EOK;
352
353 /* Section 4.22.1 */
354 /* TODO: Test this with USB3-aware BIOS */
355 usb_log_debug2("LEGSUP: bios: %x, os: %x", hc->legsup->sem_bios, hc->legsup->sem_os);
356 XHCI_REG_WR(hc->legsup, XHCI_LEGSUP_SEM_OS, 1);
357 for (int i = 0; i <= (XHCI_LEGSUP_BIOS_TIMEOUT_US / XHCI_LEGSUP_POLLING_DELAY_1MS); i++) {
358 usb_log_debug2("LEGSUP: elapsed: %i ms, bios: %x, os: %x", i,
359 XHCI_REG_RD(hc->legsup, XHCI_LEGSUP_SEM_BIOS),
360 XHCI_REG_RD(hc->legsup, XHCI_LEGSUP_SEM_OS));
361 if (XHCI_REG_RD(hc->legsup, XHCI_LEGSUP_SEM_BIOS) == 0) {
362 assert(XHCI_REG_RD(hc->legsup, XHCI_LEGSUP_SEM_OS) == 1);
363 return EOK;
364 }
365 async_usleep(XHCI_LEGSUP_POLLING_DELAY_1MS);
366 }
367 usb_log_error("BIOS did not release XHCI legacy hold!\n");
368
369 return ENOTSUP;
370}
371
372static int hc_reset(xhci_hc_t *hc)
373{
374 /* Stop the HC: set R/S to 0 */
375 XHCI_REG_CLR(hc->op_regs, XHCI_OP_RS, 1);
376
377 /* Wait 16 ms until the HC is halted */
378 async_usleep(16000);
379 assert(XHCI_REG_RD(hc->op_regs, XHCI_OP_HCH));
380
381 /* Reset */
382 XHCI_REG_SET(hc->op_regs, XHCI_OP_HCRST, 1);
383
384 /* Wait until the reset is complete */
385 while (XHCI_REG_RD(hc->op_regs, XHCI_OP_HCRST))
386 async_usleep(1000);
387
388 return EOK;
389}
390
391/**
392 * Initialize the HC: section 4.2
393 */
394int hc_start(xhci_hc_t *hc, bool irq)
395{
396 int err;
397
398 if ((err = hc_reset(hc)))
399 return err;
400
401 while (XHCI_REG_RD(hc->op_regs, XHCI_OP_CNR))
402 async_usleep(1000);
403
404 uint64_t dcbaaptr = addr_to_phys(hc->dcbaa);
405 XHCI_REG_WR(hc->op_regs, XHCI_OP_DCBAAP_LO, LOWER32(dcbaaptr));
406 XHCI_REG_WR(hc->op_regs, XHCI_OP_DCBAAP_HI, UPPER32(dcbaaptr));
407 XHCI_REG_WR(hc->op_regs, XHCI_OP_MAX_SLOTS_EN, 0);
408
409 uint64_t crptr = xhci_trb_ring_get_dequeue_ptr(&hc->command_ring);
410 XHCI_REG_WR(hc->op_regs, XHCI_OP_CRCR_LO, LOWER32(crptr) >> 6);
411 XHCI_REG_WR(hc->op_regs, XHCI_OP_CRCR_HI, UPPER32(crptr));
412
413 uint64_t erstptr = addr_to_phys(hc->event_ring.erst);
414 uint64_t erdp = hc->event_ring.dequeue_ptr;
415 xhci_interrupter_regs_t *intr0 = &hc->rt_regs->ir[0];
416 XHCI_REG_WR(intr0, XHCI_INTR_ERSTSZ, hc->event_ring.segment_count);
417 XHCI_REG_WR(intr0, XHCI_INTR_ERDP_LO, LOWER32(erdp));
418 XHCI_REG_WR(intr0, XHCI_INTR_ERDP_HI, UPPER32(erdp));
419 XHCI_REG_WR(intr0, XHCI_INTR_ERSTBA_LO, LOWER32(erstptr));
420 XHCI_REG_WR(intr0, XHCI_INTR_ERSTBA_HI, UPPER32(erstptr));
421
422 if (irq) {
423 XHCI_REG_SET(intr0, XHCI_INTR_IE, 1);
424 XHCI_REG_SET(hc->op_regs, XHCI_OP_INTE, 1);
425 }
426
427 XHCI_REG_SET(hc->op_regs, XHCI_OP_RS, 1);
428
429 /* The reset changed status of all ports, and SW originated reason does
430 * not cause an interrupt.
431 */
432 xhci_rh_handle_port_change(&hc->rh);
433
434 return EOK;
435}
436
437/**
438 * Used only when polling. Shall supplement the irq_commands.
439 */
440int hc_status(xhci_hc_t *hc, uint32_t *status)
441{
442 int ip = XHCI_REG_RD(hc->rt_regs->ir, XHCI_INTR_IP);
443 if (ip) {
444 *status = XHCI_REG_RD(hc->op_regs, XHCI_OP_STATUS);
445 XHCI_REG_WR(hc->op_regs, XHCI_OP_STATUS, *status & XHCI_STATUS_ACK_MASK);
446 XHCI_REG_WR(hc->rt_regs->ir, XHCI_INTR_IP, 1);
447
448 /* interrupt handler expects status from irq_commands, which is
449 * in xhci order. */
450 *status = host2xhci(32, *status);
451 }
452
453 usb_log_debug2("HC(%p): Polled status: %x", hc, *status);
454 return EOK;
455}
456
457int hc_schedule(xhci_hc_t *hc, usb_transfer_batch_t *batch)
458{
459 assert(batch);
460 assert(batch->ep);
461
462 if (!batch->target.address) {
463 usb_log_error("Attempted to schedule transfer to address 0.");
464 return EINVAL;
465 }
466
467 return xhci_transfer_schedule(hc, batch);
468}
469
470typedef int (*event_handler) (xhci_hc_t *, xhci_trb_t *trb);
471
472static event_handler event_handlers [] = {
473 [XHCI_TRB_TYPE_COMMAND_COMPLETION_EVENT] = &xhci_handle_command_completion,
474 [XHCI_TRB_TYPE_PORT_STATUS_CHANGE_EVENT] = &xhci_rh_handle_port_status_change_event,
475 [XHCI_TRB_TYPE_TRANSFER_EVENT] = &xhci_handle_transfer_event,
476};
477
478static int hc_handle_event(xhci_hc_t *hc, xhci_trb_t *trb, xhci_interrupter_regs_t *intr)
479{
480 unsigned type = TRB_TYPE(*trb);
481 if (type >= ARRAY_SIZE(event_handlers) || !event_handlers[type])
482 return ENOTSUP;
483
484 return event_handlers[type](hc, trb);
485}
486
487static void hc_run_event_ring(xhci_hc_t *hc, xhci_event_ring_t *event_ring, xhci_interrupter_regs_t *intr)
488{
489 int err;
490 ssize_t size = 16;
491 xhci_trb_t *queue = malloc(sizeof(xhci_trb_t) * size);
492 if (!queue) {
493 usb_log_error("Not enough memory to run the event ring.");
494 return;
495 }
496
497 xhci_trb_t *head = queue;
498
499 while ((err = xhci_event_ring_dequeue(event_ring, head)) != ENOENT) {
500 if (err != EOK) {
501 usb_log_warning("Error while accessing event ring: %s", str_error(err));
502 break;
503 }
504
505 usb_log_debug2("Dequeued trb from event ring: %s", xhci_trb_str_type(TRB_TYPE(*head)));
506 head++;
507
508 /* Expand the array if needed. */
509 if (head - queue >= size) {
510 size *= 2;
511 xhci_trb_t *new_queue = realloc(queue, size);
512 if (new_queue == NULL)
513 break; /* Will process only those TRBs we have memory for. */
514
515 head = new_queue + (head - queue);
516 }
517 }
518
519 /* Update the ERDP to make room in the ring. */
520 usb_log_debug2("Copying from ring finished, updating ERDP.");
521 uint64_t erdp = hc->event_ring.dequeue_ptr;
522 XHCI_REG_WR(intr, XHCI_INTR_ERDP_LO, LOWER32(erdp));
523 XHCI_REG_WR(intr, XHCI_INTR_ERDP_HI, UPPER32(erdp));
524 XHCI_REG_SET(intr, XHCI_INTR_ERDP_EHB, 1);
525
526 /* Handle all of the collected events if possible. */
527 if (head == queue)
528 usb_log_warning("No events to be handled!");
529
530 for (xhci_trb_t *tail = queue; tail != head; tail++) {
531 if ((err = hc_handle_event(hc, tail, intr)) != EOK) {
532 usb_log_error("Failed to handle event: %s", str_error(err));
533 }
534 }
535
536 free(queue);
537 usb_log_debug2("Event ring run finished.");
538}
539
540void hc_interrupt(xhci_hc_t *hc, uint32_t status)
541{
542 status = xhci2host(32, status);
543
544 if (status & XHCI_REG_MASK(XHCI_OP_PCD)) {
545 usb_log_debug2("Root hub interrupt.");
546 xhci_rh_handle_port_change(&hc->rh);
547 status &= ~XHCI_REG_MASK(XHCI_OP_PCD);
548 }
549
550 if (status & XHCI_REG_MASK(XHCI_OP_HSE)) {
551 usb_log_error("Host controller error occured. Bad things gonna happen...");
552 status &= ~XHCI_REG_MASK(XHCI_OP_HSE);
553 }
554
555 if (status & XHCI_REG_MASK(XHCI_OP_EINT)) {
556 usb_log_debug2("Event interrupt, running the event ring.");
557 hc_run_event_ring(hc, &hc->event_ring, &hc->rt_regs->ir[0]);
558 status &= ~XHCI_REG_MASK(XHCI_OP_EINT);
559 }
560
561 if (status & XHCI_REG_MASK(XHCI_OP_SRE)) {
562 usb_log_error("Save/Restore error occured. WTF, S/R mechanism not implemented!");
563 status &= ~XHCI_REG_MASK(XHCI_OP_SRE);
564 }
565
566 if (status) {
567 usb_log_error("Non-zero status after interrupt handling (%08x) - missing something?", status);
568 }
569}
570
571static void hc_dcbaa_fini(xhci_hc_t *hc)
572{
573 xhci_scratchpad_free(hc);
574 free32(hc->dcbaa);
575}
576
577void hc_fini(xhci_hc_t *hc)
578{
579 xhci_bus_fini(&hc->bus);
580 xhci_trb_ring_fini(&hc->command_ring);
581 xhci_event_ring_fini(&hc->event_ring);
582 hc_dcbaa_fini(hc);
583 xhci_fini_commands(hc);
584 xhci_rh_fini(&hc->rh);
585 pio_disable(hc->reg_base, RNGSZ(hc->mmio_range));
586 usb_log_info("HC(%p): Finalized.", hc);
587}
588
589int hc_ring_doorbell(xhci_hc_t *hc, unsigned doorbell, unsigned target)
590{
591 assert(hc);
592 uint32_t v = host2xhci(32, target & BIT_RRANGE(uint32_t, 7));
593 pio_write_32(&hc->db_arry[doorbell], v);
594 usb_log_debug2("Ringing doorbell %d (target: %d)", doorbell, target);
595 return EOK;
596}
597
598int hc_enable_slot(xhci_hc_t *hc, uint32_t *slot_id)
599{
600 assert(hc);
601
602 int err;
603 xhci_cmd_t cmd;
604 xhci_cmd_init(&cmd, XHCI_CMD_ENABLE_SLOT);
605
606 if ((err = xhci_cmd_sync(hc, &cmd))) {
607 goto end;
608 }
609
610 if (slot_id) {
611 *slot_id = cmd.slot_id;
612 }
613
614end:
615 xhci_cmd_fini(&cmd);
616 return err;
617}
618
619int hc_disable_slot(xhci_hc_t *hc, xhci_device_t *dev)
620{
621 int err;
622 assert(hc);
623
624 if ((err = xhci_cmd_sync_inline(hc, DISABLE_SLOT, .slot_id = dev->slot_id))) {
625 return err;
626 }
627
628 /* Free the device context. */
629 hc->dcbaa[dev->slot_id] = 0;
630 if (dev->dev_ctx) {
631 free32(dev->dev_ctx);
632 dev->dev_ctx = NULL;
633 }
634
635 /* Mark the slot as invalid. */
636 dev->slot_id = 0;
637
638 return EOK;
639}
640
641static int create_valid_input_ctx(xhci_input_ctx_t **out_ictx)
642{
643 xhci_input_ctx_t *ictx = malloc32(sizeof(xhci_input_ctx_t));
644 if (!ictx) {
645 return ENOMEM;
646 }
647
648 memset(ictx, 0, sizeof(xhci_input_ctx_t));
649
650 // Quoting sec. 4.6.6: A1, D0, D1 are down, A0 is up.
651 XHCI_INPUT_CTRL_CTX_ADD_CLEAR(ictx->ctrl_ctx, 1);
652 XHCI_INPUT_CTRL_CTX_DROP_CLEAR(ictx->ctrl_ctx, 0);
653 XHCI_INPUT_CTRL_CTX_DROP_CLEAR(ictx->ctrl_ctx, 1);
654 XHCI_INPUT_CTRL_CTX_ADD_SET(ictx->ctrl_ctx, 0);
655
656 if (out_ictx) {
657 *out_ictx = ictx;
658 }
659
660 return EOK;
661}
662
663// TODO: This currently assumes the device is attached to rh directly
664// -> calculate route string
665int hc_address_device(xhci_hc_t *hc, xhci_device_t *dev, xhci_endpoint_t *ep0)
666{
667 int err = ENOMEM;
668
669 /* Although we have the precise PSIV value on devices of tier 1,
670 * we have to rely on reverse mapping on others. */
671 if (!hc->speed_to_psiv[dev->base.speed]) {
672 usb_log_error("Device reported an USB speed that cannot be mapped to HC port speed.");
673 return EINVAL;
674 }
675
676 /* Setup and register device context */
677 dev->dev_ctx = malloc32(sizeof(xhci_device_ctx_t));
678 if (!dev->dev_ctx)
679 goto err;
680 memset(dev->dev_ctx, 0, sizeof(xhci_device_ctx_t));
681
682 hc->dcbaa[dev->slot_id] = addr_to_phys(dev->dev_ctx);
683
684 /* Issue configure endpoint command (sec 4.3.5). */
685 xhci_input_ctx_t *ictx;
686 if ((err = create_valid_input_ctx(&ictx))) {
687 goto err_dev_ctx;
688 }
689
690 /* Initialize slot_ctx according to section 4.3.3 point 3. */
691 XHCI_SLOT_ROOT_HUB_PORT_SET(ictx->slot_ctx, dev->rh_port);
692 XHCI_SLOT_CTX_ENTRIES_SET(ictx->slot_ctx, 1);
693 XHCI_SLOT_ROUTE_STRING_SET(ictx->slot_ctx, dev->route_str);
694 XHCI_SLOT_SPEED_SET(ictx->slot_ctx, hc->speed_to_psiv[dev->base.speed]);
695
696 /* In a very specific case, we have to set also these. But before that,
697 * we need to refactor how TT is handled in libusbhost. */
698 XHCI_SLOT_TT_HUB_SLOT_ID_SET(ictx->slot_ctx, 0);
699 XHCI_SLOT_TT_HUB_PORT_SET(ictx->slot_ctx, 0);
700 XHCI_SLOT_MTT_SET(ictx->slot_ctx, 0);
701
702 /* Copy endpoint 0 context and set A1 flag. */
703 XHCI_INPUT_CTRL_CTX_ADD_SET(ictx->ctrl_ctx, 1);
704 xhci_setup_endpoint_context(ep0, &ictx->endpoint_ctx[0]);
705
706 /* Issue Address Device command. */
707 if ((err = xhci_cmd_sync_inline(hc, ADDRESS_DEVICE, .slot_id = dev->slot_id, .input_ctx = ictx))) {
708 goto err_dev_ctx;
709 }
710
711 dev->base.address = XHCI_SLOT_DEVICE_ADDRESS(dev->dev_ctx->slot_ctx);
712 usb_log_debug2("Obtained USB address: %d.\n", dev->base.address);
713
714 /* From now on, the device is officially online, yay! */
715 fibril_mutex_lock(&dev->base.guard);
716 dev->online = true;
717 fibril_mutex_unlock(&dev->base.guard);
718
719 return EOK;
720
721err_dev_ctx:
722 free32(dev->dev_ctx);
723 hc->dcbaa[dev->slot_id] = 0;
724err:
725 return err;
726}
727
728int hc_configure_device(xhci_hc_t *hc, uint32_t slot_id)
729{
730 /* Issue configure endpoint command (sec 4.3.5). */
731 xhci_input_ctx_t *ictx;
732 const int err = create_valid_input_ctx(&ictx);
733 if (err)
734 return err;
735
736 // TODO: Set slot context and other flags. (probably forgot a lot of 'em)
737
738 return xhci_cmd_sync_inline(hc, CONFIGURE_ENDPOINT, .slot_id = slot_id, .input_ctx = ictx);
739}
740
741int hc_deconfigure_device(xhci_hc_t *hc, uint32_t slot_id)
742{
743 /* Issue configure endpoint command (sec 4.3.5) with the DC flag. */
744 return xhci_cmd_sync_inline(hc, CONFIGURE_ENDPOINT, .slot_id = slot_id, .deconfigure = true);
745}
746
747int hc_add_endpoint(xhci_hc_t *hc, uint32_t slot_id, uint8_t ep_idx, xhci_ep_ctx_t *ep_ctx)
748{
749 /* Issue configure endpoint command (sec 4.3.5). */
750 xhci_input_ctx_t *ictx;
751 const int err = create_valid_input_ctx(&ictx);
752 if (err)
753 return err;
754
755 XHCI_INPUT_CTRL_CTX_ADD_SET(ictx->ctrl_ctx, ep_idx + 1); /* Preceded by slot ctx */
756 memcpy(&ictx->endpoint_ctx[ep_idx], ep_ctx, sizeof(xhci_ep_ctx_t));
757 // TODO: Set slot context and other flags. (probably forgot a lot of 'em)
758
759 return xhci_cmd_sync_inline(hc, CONFIGURE_ENDPOINT, .slot_id = slot_id, .input_ctx = ictx);
760}
761
762int hc_drop_endpoint(xhci_hc_t *hc, uint32_t slot_id, uint8_t ep_idx)
763{
764 /* Issue configure endpoint command (sec 4.3.5). */
765 xhci_input_ctx_t *ictx;
766 const int err = create_valid_input_ctx(&ictx);
767 if (err)
768 return err;
769
770 XHCI_INPUT_CTRL_CTX_DROP_SET(ictx->ctrl_ctx, ep_idx + 1); /* Preceded by slot ctx */
771 // TODO: Set slot context and other flags. (probably forgot a lot of 'em)
772
773 return xhci_cmd_sync_inline(hc, CONFIGURE_ENDPOINT, .slot_id = slot_id, .input_ctx = ictx);
774}
775
776/**
777 * @}
778 */
Note: See TracBrowser for help on using the repository browser.