diff options
author | Julius Werner <jwerner@chromium.org> | 2013-09-03 17:15:31 -0700 |
---|---|---|
committer | Isaac Christensen <isaac.christensen@se-eng.com> | 2014-08-14 23:41:21 +0200 |
commit | 1f86434227beaf9806de86269f8b42eed817ae3a (patch) | |
tree | 25eb1420c05f75de4ca3c79eb4f9ed6c13b60c69 /payloads | |
parent | d96541f3fc934fa27b800a07ccf0597bd5a80dd5 (diff) |
libpayload: xhci: Make XHCI stack usable on ARM
This patch updates the libpayload XHCI stack to run on ARM CPUs (tested
with the DWC3 controller on an Exynos5420). Firstly, it adds support for
64-byte Slot/Endpoint Context sizes. Since the existing context handling
code represented the whole device context as a C struct (whose size has
to be known at compile time), it was necessary to refactor the input and
device context structures to consist of pointers to the actual contexts
instead.
Secondly, it moves all data structures that the xHC accesses through DMA
to cache-coherent memory. With a similar rationale as in the ARM patches
for EHCI, using explicit cache maintenance functions to correctly handle
the actual transfer buffers in all cases is presumably impossible.
Instead this patch also chooses to create a DMA bounce buffer in the
XHCI stack where transfer buffers which are not already cache-coherent
will be copied to/from.
Change-Id: I14e82fffb43b4d52d687b65415f2e33920e088de
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/169453
Reviewed-by: Stefan Reinauer <reinauer@google.com>
(cherry picked from commit 1fa9964063cce6cbd87ba68334806dde8aa2354c)
Signed-off-by: Isaac Christensen <isaac.christensen@se-eng.com>
Reviewed-on: http://review.coreboot.org/6643
Tested-by: build bot (Jenkins)
Reviewed-by: Paul Menzel <paulepanter@users.sourceforge.net>
Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
Diffstat (limited to 'payloads')
-rw-r--r-- | payloads/libpayload/drivers/usb/xhci.c | 126 | ||||
-rw-r--r-- | payloads/libpayload/drivers/usb/xhci_commands.c | 6 | ||||
-rw-r--r-- | payloads/libpayload/drivers/usb/xhci_debug.c | 52 | ||||
-rw-r--r-- | payloads/libpayload/drivers/usb/xhci_devconf.c | 127 | ||||
-rw-r--r-- | payloads/libpayload/drivers/usb/xhci_events.c | 4 | ||||
-rw-r--r-- | payloads/libpayload/drivers/usb/xhci_private.h | 57 | ||||
-rw-r--r-- | payloads/libpayload/drivers/usb/xhci_rh.c | 2 |
7 files changed, 216 insertions, 158 deletions
diff --git a/payloads/libpayload/drivers/usb/xhci.c b/payloads/libpayload/drivers/usb/xhci.c index 184a370936..4ab2fe3c34 100644 --- a/payloads/libpayload/drivers/usb/xhci.c +++ b/payloads/libpayload/drivers/usb/xhci.c @@ -62,7 +62,7 @@ xhci_align(const size_t min_align, const size_t size) if (align < min_align) align = min_align; xhci_spew("Aligning %zu to %zu\n", size, align); - return memalign(align, size); + return dma_memalign(align, size); } void @@ -210,12 +210,7 @@ xhci_init (unsigned long physical_bar) goto _free_xhci; } - xhci_debug("context size: %dB\n", xhci->capreg->csz ? 64 : 32); - if (xhci->capreg->csz) { - xhci_debug("Only 32B contexts are supported\n"); - goto _free_xhci; - } - + xhci_debug("context size: %dB\n", CTXSIZE(xhci)); xhci_debug("maxslots: 0x%02lx\n", xhci->capreg->MaxSlots); xhci_debug("maxports: 0x%02lx\n", xhci->capreg->MaxPorts); const unsigned pagesize = xhci->opreg->pagesize << 12; @@ -226,13 +221,15 @@ xhci_init (unsigned long physical_bar) * structures at first and can still chicken out easily if we run out * of memory. */ - const size_t dcbaa_size = (xhci->capreg->MaxSlots + 1) * sizeof(u64); - xhci->dcbaa = xhci_align(64, dcbaa_size); - if (!xhci->dcbaa) { + xhci->max_slots_en = xhci->capreg->MaxSlots & CONFIG_LP_MASK_MaxSlotsEn; + xhci->dcbaa = xhci_align(64, (xhci->max_slots_en + 1) * sizeof(u64)); + xhci->dev = malloc((xhci->max_slots_en + 1) * sizeof(*xhci->dev)); + if (!xhci->dcbaa || !xhci->dev) { xhci_debug("Out of memory\n"); goto _free_xhci; } - memset((void*)xhci->dcbaa, 0x00, dcbaa_size); + memset(xhci->dcbaa, 0x00, (xhci->max_slots_en + 1) * sizeof(u64)); + memset(xhci->dev, 0x00, (xhci->max_slots_en + 1) * sizeof(*xhci->dev)); /* * Let dcbaa[0] point to another array of pointers, sp_ptrs. @@ -261,9 +258,17 @@ xhci_init (unsigned long physical_bar) xhci->dcbaa[0] = virt_to_phys(xhci->sp_ptrs); } + if (dma_initialized()) { + xhci->dma_buffer = dma_memalign(64 * 1024, DMA_SIZE); + if (!xhci->dma_buffer) { + xhci_debug("Not enough memory for DMA bounce buffer\n"); + goto _free_xhci_structs; + } + } + /* Now start working on the hardware */ if (xhci_wait_ready(xhci)) - goto _free_xhci; + goto _free_xhci_structs; /* TODO: Check if BIOS claims ownership (and hand over) */ @@ -290,6 +295,7 @@ _free_xhci: free((void *)xhci->er.ring); free((void *)xhci->cr.ring); free(xhci->roothub); + free(xhci->dev); free(xhci); _free_controller: detach_controller(controller); @@ -344,8 +350,7 @@ xhci_reinit (hci_t *controller) return; /* Enable all available slots */ - xhci->opreg->config = xhci->capreg->MaxSlots & CONFIG_LP_MASK_MaxSlotsEn; - xhci->max_slots_en = xhci->capreg->MaxSlots & CONFIG_LP_MASK_MaxSlotsEn; + xhci->opreg->config = xhci->max_slots_en; /* Set DCBAA */ xhci->opreg->dcbaap_lo = virt_to_phys(xhci->dcbaa); @@ -426,6 +431,7 @@ xhci_shutdown(hci_t *const controller) } free(xhci->sp_ptrs); free(xhci->dcbaa); + free(xhci->dev); free((void *)xhci->ev_ring_table); free((void *)xhci->er.ring); free((void *)xhci->cr.ring); @@ -459,15 +465,15 @@ xhci_reset_endpoint(usbdev_t *const dev, endpoint_t *const ep, const int clear_halt) { xhci_t *const xhci = XHCI_INST(dev->controller); - devinfo_t *const di = DEVINFO_FROM_XHCI(xhci, dev->address); const int slot_id = dev->address; const int ep_id = ep ? xhci_ep_id(ep) : 1; + epctx_t *const epctx = xhci->dev[slot_id].ctx.ep[ep_id]; xhci_debug("Resetting ID %d EP %d (ep state: %d)\n", - slot_id, ep_id, EC_GET(STATE, di->devctx.eps[ep_id])); + slot_id, ep_id, EC_GET(STATE, epctx)); /* Run Reset Endpoint Command if the EP is in Halted state */ - if (EC_GET(STATE, di->devctx.eps[ep_id]) == 2) { + if (EC_GET(STATE, epctx) == 2) { const int cc = xhci_cmd_reset_endpoint(xhci, slot_id, ep_id); if (cc != CC_SUCCESS) { xhci_debug("Reset Endpoint Command failed: %d\n", cc); @@ -486,9 +492,10 @@ xhci_reset_endpoint(usbdev_t *const dev, endpoint_t *const ep, clear_stall(ep); /* Reset transfer ring if the endpoint is in the right state */ - const unsigned ep_state = EC_GET(STATE, di->devctx.eps[ep_id]); + const unsigned ep_state = EC_GET(STATE, epctx); if (ep_state == 3 || ep_state == 4) { - transfer_ring_t *const tr = di->transfer_rings[ep_id]; + transfer_ring_t *const tr = + xhci->dev[slot_id].transfer_rings[ep_id]; const int cc = xhci_cmd_set_tr_dq(xhci, slot_id, ep_id, tr->ring, 1); if (cc != CC_SUCCESS) { @@ -499,7 +506,7 @@ xhci_reset_endpoint(usbdev_t *const dev, endpoint_t *const ep, } xhci_debug("Finished resetting ID %d EP %d (ep state: %d)\n", - slot_id, ep_id, EC_GET(STATE, di->devctx.eps[ep_id])); + slot_id, ep_id, EC_GET(STATE, epctx)); return 0; } @@ -579,11 +586,12 @@ xhci_enqueue_td(transfer_ring_t *const tr, const int ep, const size_t mps, static int xhci_control(usbdev_t *const dev, const direction_t dir, const int drlen, void *const devreq, - const int dalen, unsigned char *const data) + const int dalen, unsigned char *const src) { + unsigned char *data = src; xhci_t *const xhci = XHCI_INST(dev->controller); - devinfo_t *const di = DEVINFO_FROM_XHCI(xhci, dev->address); - transfer_ring_t *const tr = di->transfer_rings[1]; + epctx_t *const epctx = xhci->dev[dev->address].ctx.ep0; + transfer_ring_t *const tr = xhci->dev[dev->address].transfer_rings[1]; const size_t off = (size_t)data & 0xffff; if ((off + dalen) > ((TRANSFER_RING_SIZE - 4) << 16)) { @@ -592,12 +600,22 @@ xhci_control(usbdev_t *const dev, const direction_t dir, } /* Reset endpoint if it's halted */ - const unsigned ep_state = EC_GET(STATE, di->devctx.ep0); + const unsigned ep_state = EC_GET(STATE, epctx); if (ep_state == 2 || ep_state == 4) { if (xhci_reset_endpoint(dev, NULL, 0)) return -1; } + if (dalen && !dma_coherent(src)) { + data = xhci->dma_buffer; + if (dalen > DMA_SIZE) { + xhci_debug("Control transfer too large: %d\n", dalen); + return -1; + } + if (dir == OUT) + memcpy(data, src, dalen); + } + /* Fill and enqueue setup TRB */ trb_t *const setup = tr->cur; xhci_clear_trb(setup, tr->pcs); @@ -614,7 +632,7 @@ xhci_control(usbdev_t *const dev, const direction_t dir, /* Fill and enqueue data TRBs (if any) */ if (dalen) { - const unsigned mps = EC_GET(MPS, di->devctx.ep0); + const unsigned mps = EC_GET(MPS, epctx); const unsigned dt_dir = (dir == OUT) ? TRB_DIR_OUT : TRB_DIR_IN; xhci_enqueue_td(tr, 1, mps, dalen, data, dt_dir); } @@ -650,28 +668,31 @@ xhci_control(usbdev_t *const dev, const direction_t dir, " usbsts: 0x%08"PRIx32"\n", i, n_stages, ret, tr->ring, setup, status, - ep_state, EC_GET(STATE, di->devctx.ep0), + ep_state, EC_GET(STATE, epctx), xhci->opreg->usbsts); return ret; } } + if (dir == IN && data != src) + memcpy(src, data, transferred); return transferred; } /* finalize == 1: if data is of packet aligned size, add a zero length packet */ static int -xhci_bulk(endpoint_t *const ep, - const int size, u8 *const data, +xhci_bulk(endpoint_t *const ep, const int size, u8 *const src, const int finalize) { /* finalize: Hopefully the xHCI controller always does this. We have no control over the packets. */ + u8 *data = src; xhci_t *const xhci = XHCI_INST(ep->dev->controller); + const int slot_id = ep->dev->address; const int ep_id = xhci_ep_id(ep); - devinfo_t *const di = DEVINFO_FROM_XHCI(xhci, ep->dev->address); - transfer_ring_t *const tr = di->transfer_rings[ep_id]; + epctx_t *const epctx = xhci->dev[slot_id].ctx.ep[ep_id]; + transfer_ring_t *const tr = xhci->dev[slot_id].transfer_rings[ep_id]; const size_t off = (size_t)data & 0xffff; if ((off + size) > ((TRANSFER_RING_SIZE - 2) << 16)) { @@ -679,15 +700,25 @@ xhci_bulk(endpoint_t *const ep, return -1; } + if (!dma_coherent(src)) { + data = xhci->dma_buffer; + if (size > DMA_SIZE) { + xhci_debug("Bulk transfer too large: %d\n", size); + return -1; + } + if (ep->direction == OUT) + memcpy(data, src, size); + } + /* Reset endpoint if it's halted */ - const unsigned ep_state = EC_GET(STATE, di->devctx.eps[ep_id]); + const unsigned ep_state = EC_GET(STATE, epctx); if (ep_state == 2 || ep_state == 4) { if (xhci_reset_endpoint(ep->dev, ep, 0)) return -1; } /* Enqueue transfer and ring doorbell */ - const unsigned mps = EC_GET(MPS, di->devctx.eps[ep_id]); + const unsigned mps = EC_GET(MPS, epctx); const unsigned dir = (ep->direction == OUT) ? TRB_DIR_OUT : TRB_DIR_IN; xhci_enqueue_td(tr, ep_id, mps, size, data, dir); xhci->dbreg[ep->dev->address] = ep_id; @@ -706,11 +737,13 @@ xhci_bulk(endpoint_t *const ep, " ep state: %d -> %d\n" " usbsts: 0x%08"PRIx32"\n", ret, ep_state, - EC_GET(STATE, di->devctx.eps[ep_id]), + EC_GET(STATE, epctx), xhci->opreg->usbsts); return ret; } + if (ep->direction == IN && data != src) + memcpy(src, data, ret); return ret; } @@ -736,9 +769,9 @@ xhci_create_intr_queue(endpoint_t *const ep, endpoint descriptor configured earlier. */ xhci_t *const xhci = XHCI_INST(ep->dev->controller); + const int slot_id = ep->dev->address; const int ep_id = xhci_ep_id(ep); - devinfo_t *const di = DEVINFO_FROM_XHCI(xhci, ep->dev->address); - transfer_ring_t *const tr = di->transfer_rings[ep_id]; + transfer_ring_t *const tr = xhci->dev[slot_id].transfer_rings[ep_id]; if (reqcount > (TRANSFER_RING_SIZE - 2)) { xhci_debug("reqcount is too high, at most %d supported\n", @@ -749,7 +782,7 @@ xhci_create_intr_queue(endpoint_t *const ep, xhci_debug("reqsize is too large, at most 64KiB supported\n"); return NULL; } - if (di->interrupt_queues[ep_id]) { + if (xhci->dev[slot_id].interrupt_queues[ep_id]) { xhci_debug("Only one interrupt queue per endpoint supported\n"); return NULL; } @@ -791,13 +824,13 @@ xhci_create_intr_queue(endpoint_t *const ep, intrq->next = tr->cur; intrq->ready = NULL; intrq->ep = ep; - di->interrupt_queues[ep_id] = intrq; + xhci->dev[slot_id].interrupt_queues[ep_id] = intrq; /* Now enqueue all the prepared TRBs but the last and ring the doorbell. */ for (i = 0; i < (reqcount - 1); ++i) xhci_enqueue_trb(tr); - xhci->dbreg[ep->dev->address] = ep_id; + xhci->dbreg[slot_id] = ep_id; return intrq; @@ -816,16 +849,15 @@ static void xhci_destroy_intr_queue(endpoint_t *const ep, void *const q) { xhci_t *const xhci = XHCI_INST(ep->dev->controller); + const int slot_id = ep->dev->address; const int ep_id = xhci_ep_id(ep); - devinfo_t *const di = DEVINFO_FROM_XHCI(xhci, ep->dev->address); - transfer_ring_t *const tr = di->transfer_rings[ep_id]; + transfer_ring_t *const tr = xhci->dev[slot_id].transfer_rings[ep_id]; intrq_t *const intrq = (intrq_t *)q; /* Make sure the endpoint is stopped */ - if (EC_GET(STATE, di->devctx.eps[ep_id]) == 1) { - const int cc = xhci_cmd_stop_endpoint( - xhci, ep->dev->address, ep_id); + if (EC_GET(STATE, xhci->dev[slot_id].ctx.ep[ep_id]) == 1) { + const int cc = xhci_cmd_stop_endpoint(xhci, slot_id, ep_id); if (cc != CC_SUCCESS) xhci_debug("Warning: Failed to stop endpoint\n"); } @@ -839,11 +871,11 @@ xhci_destroy_intr_queue(endpoint_t *const ep, void *const q) free(phys_to_virt(intrq->next->ptr_low)); intrq->next = xhci_next_trb(intrq->next, NULL); } - di->interrupt_queues[ep_id] = NULL; + xhci->dev[slot_id].interrupt_queues[ep_id] = NULL; free((void *)intrq); /* Reset the controller's dequeue pointer and reinitialize the ring */ - xhci_cmd_set_tr_dq(xhci, ep->dev->address, ep_id, tr->ring, 1); + xhci_cmd_set_tr_dq(xhci, slot_id, ep_id, tr->ring, 1); xhci_init_cycle_ring(tr, TRANSFER_RING_SIZE); } @@ -868,8 +900,8 @@ xhci_poll_intr_queue(void *const q) u8 *reqdata = NULL; while (!reqdata && intrq->ready) { const int ep_id = xhci_ep_id(ep); - devinfo_t *const di = DEVINFO_FROM_XHCI(xhci, ep->dev->address); - transfer_ring_t *const tr = di->transfer_rings[ep_id]; + transfer_ring_t *const tr = + xhci->dev[ep->dev->address].transfer_rings[ep_id]; /* Fetch the request's buffer */ reqdata = phys_to_virt(intrq->next->ptr_low); diff --git a/payloads/libpayload/drivers/usb/xhci_commands.c b/payloads/libpayload/drivers/usb/xhci_commands.c index 3a744b391e..009a69c812 100644 --- a/payloads/libpayload/drivers/usb/xhci_commands.c +++ b/payloads/libpayload/drivers/usb/xhci_commands.c @@ -128,7 +128,7 @@ xhci_cmd_address_device(xhci_t *const xhci, trb_t *const cmd = xhci_next_command_trb(xhci); TRB_SET(TT, cmd, TRB_CMD_ADDRESS_DEV); TRB_SET(ID, cmd, slot_id); - cmd->ptr_low = virt_to_phys(ic); + cmd->ptr_low = virt_to_phys(ic->raw); xhci_post_command(xhci); return xhci_wait_for_command(xhci, cmd, 1); @@ -143,7 +143,7 @@ xhci_cmd_configure_endpoint(xhci_t *const xhci, trb_t *const cmd = xhci_next_command_trb(xhci); TRB_SET(TT, cmd, TRB_CMD_CONFIGURE_EP); TRB_SET(ID, cmd, slot_id); - cmd->ptr_low = virt_to_phys(ic); + cmd->ptr_low = virt_to_phys(ic->raw); if (config_id == 0) TRB_SET(DC, cmd, 1); xhci_post_command(xhci); @@ -159,7 +159,7 @@ xhci_cmd_evaluate_context(xhci_t *const xhci, trb_t *const cmd = xhci_next_command_trb(xhci); TRB_SET(TT, cmd, TRB_CMD_EVAL_CTX); TRB_SET(ID, cmd, slot_id); - cmd->ptr_low = virt_to_phys(ic); + cmd->ptr_low = virt_to_phys(ic->raw); xhci_post_command(xhci); return xhci_wait_for_command(xhci, cmd, 1); diff --git a/payloads/libpayload/drivers/usb/xhci_debug.c b/payloads/libpayload/drivers/usb/xhci_debug.c index ba644c62cf..913b545bd9 100644 --- a/payloads/libpayload/drivers/usb/xhci_debug.c +++ b/payloads/libpayload/drivers/usb/xhci_debug.c @@ -40,18 +40,18 @@ xhci_dump_slotctx(const slotctx_t *const sc) usb_debug(" FIELD2\t0x%08"PRIx32"\n", sc->f2); usb_debug(" FIELD3\t0x%08"PRIx32"\n", sc->f3); usb_debug(" FIELD4\t0x%08"PRIx32"\n", sc->f4); - SC_DUMP(ROUTE, *sc); - SC_DUMP(SPEED, *sc); - SC_DUMP(MTT, *sc); - SC_DUMP(HUB, *sc); - SC_DUMP(CTXENT, *sc); - SC_DUMP(RHPORT, *sc); - SC_DUMP(NPORTS, *sc); - SC_DUMP(TTID, *sc); - SC_DUMP(TTPORT, *sc); - SC_DUMP(TTT, *sc); - SC_DUMP(UADDR, *sc); - SC_DUMP(STATE, *sc); + SC_DUMP(ROUTE, sc); + SC_DUMP(SPEED, sc); + SC_DUMP(MTT, sc); + SC_DUMP(HUB, sc); + SC_DUMP(CTXENT, sc); + SC_DUMP(RHPORT, sc); + SC_DUMP(NPORTS, sc); + SC_DUMP(TTID, sc); + SC_DUMP(TTPORT, sc); + SC_DUMP(TTT, sc); + SC_DUMP(UADDR, sc); + SC_DUMP(STATE, sc); } void @@ -63,15 +63,15 @@ xhci_dump_epctx(const epctx_t *const ec) usb_debug(" TRDQ_L\t0x%08"PRIx32"\n", ec->tr_dq_low); usb_debug(" TRDQ_H\t0x%08"PRIx32"\n", ec->tr_dq_high); usb_debug(" FIELD5\t0x%08"PRIx32"\n", ec->f5); - EC_DUMP(STATE, *ec); - EC_DUMP(INTVAL, *ec); - EC_DUMP(CERR, *ec); - EC_DUMP(TYPE, *ec); - EC_DUMP(MBS, *ec); - EC_DUMP(MPS, *ec); - EC_DUMP(DCS, *ec); - EC_DUMP(AVRTRB, *ec); - EC_DUMP(MXESIT, *ec); + EC_DUMP(STATE, ec); + EC_DUMP(INTVAL, ec); + EC_DUMP(CERR, ec); + EC_DUMP(TYPE, ec); + EC_DUMP(MBS, ec); + EC_DUMP(MPS, ec); + EC_DUMP(DCS, ec); + EC_DUMP(AVRTRB, ec); + EC_DUMP(MXESIT, ec); } void @@ -79,19 +79,19 @@ xhci_dump_devctx(const devctx_t *const dc, const u32 ctx_mask) { int i; if (ctx_mask & 1) - xhci_dump_slotctx(&dc->slot); + xhci_dump_slotctx(dc->slot); for (i = 0; i < SC_GET(CTXENT, dc->slot); ++i) { if (ctx_mask & (2 << i)) - xhci_dump_epctx(&dc->all_eps[i]); + xhci_dump_epctx(dc->ep[i]); } } void xhci_dump_inputctx(const inputctx_t *const ic) { - xhci_debug("Input Control add: 0x%08"PRIx32"\n", ic->control.add); - xhci_debug("Input Control drop: 0x%08"PRIx32"\n", ic->control.drop); - xhci_dump_devctx(&ic->dev, ic->control.add); + xhci_debug("Input Control add: 0x%08"PRIx32"\n", *ic->add); + xhci_debug("Input Control drop: 0x%08"PRIx32"\n", *ic->drop); + xhci_dump_devctx(&ic->dev, *ic->add); } void diff --git a/payloads/libpayload/drivers/usb/xhci_devconf.c b/payloads/libpayload/drivers/usb/xhci_devconf.c index 34bac8ff47..18ba4e8104 100644 --- a/payloads/libpayload/drivers/usb/xhci_devconf.c +++ b/payloads/libpayload/drivers/usb/xhci_devconf.c @@ -37,9 +37,7 @@ xhci_gen_route(xhci_t *const xhci, const int hubport, const int hubaddr) { if (!hubaddr) return 0; - volatile const devctx_t *const devctx = - phys_to_virt(xhci->dcbaa[hubaddr]); - u32 route_string = SC_GET(ROUTE, devctx->slot); + u32 route_string = SC_GET(ROUTE, xhci->dev[hubaddr].ctx.slot); int i; for (i = 0; i < 20; i += 4) { if (!(route_string & (0xf << i))) { @@ -55,9 +53,7 @@ xhci_get_rh_port(xhci_t *const xhci, const int hubport, const int hubaddr) { if (!hubaddr) return hubport; - volatile const devctx_t *const devctx = - phys_to_virt(xhci->dcbaa[hubaddr]); - return SC_GET(RHPORT, devctx->slot); + return SC_GET(RHPORT, xhci->dev[hubaddr].ctx.slot); } static int @@ -67,12 +63,11 @@ xhci_get_tt(xhci_t *const xhci, const int xhci_speed, { if (!hubaddr) return 0; - volatile const devctx_t *const devctx = - phys_to_virt(xhci->dcbaa[hubaddr]); - if ((*tt = SC_GET(TTID, devctx->slot))) { - *tt_port = SC_GET(TTPORT, devctx->slot); + const slotctx_t *const slot = xhci->dev[hubaddr].ctx.slot; + if ((*tt = SC_GET(TTID, slot))) { + *tt_port = SC_GET(TTPORT, slot); } else if (xhci_speed < XHCI_HIGH_SPEED && - SC_GET(SPEED, devctx->slot) == XHCI_HIGH_SPEED) { + SC_GET(SPEED, slot) == XHCI_HIGH_SPEED) { *tt = hubaddr; *tt_port = hubport; } @@ -130,20 +125,45 @@ xhci_get_mps0(usbdev_t *const dev, const int xhci_speed) } } +static inputctx_t * +xhci_make_inputctx(const size_t ctxsize) +{ + int i; + const size_t size = (1 + NUM_EPS) * ctxsize; + inputctx_t *const ic = malloc(sizeof(*ic)); + void *dma_buffer = dma_memalign(64, size); + + if (!ic || !dma_buffer) { + free(ic); + free(dma_buffer); + return NULL; + } + + memset(dma_buffer, 0, size); + ic->drop = dma_buffer + 0; + ic->add = dma_buffer + 4; + dma_buffer += ctxsize; + for (i = 0; i < NUM_EPS; i++, dma_buffer += ctxsize) + ic->dev.ep[i] = dma_buffer; + + return ic; +} + int xhci_set_address (hci_t *controller, int speed, int hubport, int hubaddr) { xhci_t *const xhci = XHCI_INST(controller); const int xhci_speed = speed + 1; + const size_t ctxsize = CTXSIZE(xhci); + devinfo_t *di = NULL; - int ret = -1; + int i, ret = -1; - inputctx_t *const ic = xhci_align(64, sizeof(*ic)); - devinfo_t *const di = memalign(sizeof(di->devctx), sizeof(*di)); + inputctx_t *const ic = xhci_make_inputctx(ctxsize); transfer_ring_t *const tr = malloc(sizeof(*tr)); if (tr) tr->ring = xhci_align(16, TRANSFER_RING_SIZE * sizeof(trb_t)); - if (!ic || !di || !tr || !tr->ring) { + if (!ic || !tr || !tr->ring) { xhci_debug("Out of memory\n"); goto _free_return; } @@ -157,9 +177,15 @@ xhci_set_address (hci_t *controller, int speed, int hubport, int hubaddr) xhci_debug("Enabled slot %d\n", slot_id); } - memset(ic, 0x00, sizeof(*ic)); - ic->control.add = (1 << 0) /* Slot Context */ | - (1 << 1) /* EP0 Context */ ; + di = &xhci->dev[slot_id]; + void *dma_buffer = dma_memalign(64, NUM_EPS * ctxsize); + if (!dma_buffer) + goto _free_return; + memset(dma_buffer, 0, NUM_EPS * ctxsize); + for (i = 0; i < NUM_EPS; i++, dma_buffer += ctxsize) + di->ctx.ep[i] = dma_buffer; + + *ic->add = (1 << 0) /* Slot Context */ | (1 << 1) /* EP0 Context */ ; SC_SET(ROUTE, ic->dev.slot, xhci_gen_route(xhci, hubport, hubaddr)); SC_SET(SPEED, ic->dev.slot, xhci_speed); @@ -169,27 +195,23 @@ xhci_set_address (hci_t *controller, int speed, int hubport, int hubaddr) int tt, tt_port; if (xhci_get_tt(xhci, xhci_speed, hubport, hubaddr, &tt, &tt_port)) { xhci_debug("TT for %d: %d[%d]\n", slot_id, tt, tt_port); - volatile const devctx_t *const ttctx = - phys_to_virt(xhci->dcbaa[tt]); - SC_SET(MTT, ic->dev.slot, SC_GET(MTT, ttctx->slot)); + SC_SET(MTT, ic->dev.slot, SC_GET(MTT, xhci->dev[tt].ctx.slot)); SC_SET(TTID, ic->dev.slot, tt); SC_SET(TTPORT, ic->dev.slot, tt_port); } - memset(di, 0x00, sizeof(*di)); di->transfer_rings[1] = tr; xhci_init_cycle_ring(tr, TRANSFER_RING_SIZE); - ic->dev.ep0.tr_dq_low = virt_to_phys(tr->ring); - ic->dev.ep0.tr_dq_high = 0; + ic->dev.ep0->tr_dq_low = virt_to_phys(tr->ring); + ic->dev.ep0->tr_dq_high = 0; EC_SET(TYPE, ic->dev.ep0, EP_CONTROL); EC_SET(AVRTRB, ic->dev.ep0, 8); EC_SET(MPS, ic->dev.ep0, 8); EC_SET(CERR, ic->dev.ep0, 3); EC_SET(DCS, ic->dev.ep0, 1); - volatile devctx_t *const oc = &di->devctx; - xhci->dcbaa[slot_id] = virt_to_phys(oc); + xhci->dcbaa[slot_id] = virt_to_phys(di->ctx.raw); cc = xhci_cmd_address_device(xhci, slot_id, ic); if (cc != CC_SUCCESS) { @@ -197,7 +219,7 @@ xhci_set_address (hci_t *controller, int speed, int hubport, int hubaddr) goto _disable_return; } else { xhci_debug("Addressed device %d (USB: %d)\n", - slot_id, SC_GET(UADDR, oc->slot)); + slot_id, SC_GET(UADDR, di->ctx.slot)); } mdelay(2); /* SetAddress() recovery interval (usb20 spec 9.2.6.3) */ @@ -209,9 +231,8 @@ xhci_set_address (hci_t *controller, int speed, int hubport, int hubaddr) if (mps0 < 0) { goto _disable_return; } else if (mps0 != 8) { - memset(&ic->control, 0x00, sizeof(ic->control)); - memset(&ic->dev.ep0, 0x00, sizeof(ic->dev.ep0)); - ic->control.add = (1 << 1); /* EP0 Context */ + memset((void *)ic->dev.ep0, 0x00, ctxsize); + *ic->add = (1 << 1); /* EP0 Context */ EC_SET(MPS, ic->dev.ep0, mps0); cc = xhci_cmd_evaluate_context(xhci, slot_id, ic); if (cc != CC_SUCCESS) { @@ -232,8 +253,12 @@ _free_return: if (tr) free((void *)tr->ring); free(tr); + if (di) + free(di->ctx.raw); free((void *)di); _free_ic_return: + if (ic) + free(ic->raw); free(ic); return ret; } @@ -291,8 +316,6 @@ static int xhci_finish_ep_config(const endpoint_t *const ep, inputctx_t *const ic) { xhci_t *const xhci = XHCI_INST(ep->dev->controller); - devinfo_t *const di = phys_to_virt(xhci->dcbaa[ep->dev->address] - - offsetof(devinfo_t, devctx)); const int ep_id = xhci_ep_id(ep); xhci_debug("ep_id: %d\n", ep_id); if (ep_id <= 1 || 32 <= ep_id) @@ -306,30 +329,30 @@ xhci_finish_ep_config(const endpoint_t *const ep, inputctx_t *const ic) xhci_debug("Out of memory\n"); return OUT_OF_MEMORY; } - di->transfer_rings[ep_id] = tr; + xhci->dev[ep->dev->address].transfer_rings[ep_id] = tr; xhci_init_cycle_ring(tr, TRANSFER_RING_SIZE); - ic->control.add |= (1 << ep_id); + *ic->add |= (1 << ep_id); if (SC_GET(CTXENT, ic->dev.slot) < ep_id) SC_SET(CTXENT, ic->dev.slot, ep_id); - epctx_t *const epctx = &ic->dev.eps[ep_id]; + epctx_t *const epctx = ic->dev.ep[ep_id]; xhci_debug("Filling epctx (@%p)\n", epctx); epctx->tr_dq_low = virt_to_phys(tr->ring); epctx->tr_dq_high = 0; - EC_SET(INTVAL, *epctx, xhci_bound_interval(ep)); - EC_SET(CERR, *epctx, 3); - EC_SET(TYPE, *epctx, ep->type | ((ep->direction != OUT) << 2)); - EC_SET(MPS, *epctx, ep->maxpacketsize); - EC_SET(DCS, *epctx, 1); + EC_SET(INTVAL, epctx, xhci_bound_interval(ep)); + EC_SET(CERR, epctx, 3); + EC_SET(TYPE, epctx, ep->type | ((ep->direction != OUT) << 2)); + EC_SET(MPS, epctx, ep->maxpacketsize); + EC_SET(DCS, epctx, 1); size_t avrtrb; switch (ep->type) { case BULK: case ISOCHRONOUS: avrtrb = 3 * 1024; break; case INTERRUPT: avrtrb = 1024; break; default: avrtrb = 8; break; } - EC_SET(AVRTRB, *epctx, avrtrb); - EC_SET(MXESIT, *epctx, EC_GET(MPS, *epctx) * EC_GET(MBS, *epctx)); + EC_SET(AVRTRB, epctx, avrtrb); + EC_SET(MXESIT, epctx, EC_GET(MPS, epctx) * EC_GET(MBS, epctx)); return 0; } @@ -338,24 +361,22 @@ int xhci_finish_device_config(usbdev_t *const dev) { xhci_t *const xhci = XHCI_INST(dev->controller); - devinfo_t *const di = phys_to_virt(xhci->dcbaa[dev->address] - - offsetof(devinfo_t, devctx)); + devinfo_t *const di = &xhci->dev[dev->address]; int i, ret = 0; - inputctx_t *const ic = xhci_align(64, sizeof(*ic)); + inputctx_t *const ic = xhci_make_inputctx(CTXSIZE(xhci)); if (!ic) { xhci_debug("Out of memory\n"); return OUT_OF_MEMORY; } - memset(ic, 0x00, sizeof(*ic)); - ic->control.add = (1 << 0); /* Slot Context */ + *ic->add = (1 << 0); /* Slot Context */ - xhci_dump_slotctx((const slotctx_t *)&di->devctx.slot); - ic->dev.slot.f1 = di->devctx.slot.f1; - ic->dev.slot.f2 = di->devctx.slot.f2; - ic->dev.slot.f3 = di->devctx.slot.f3; + xhci_dump_slotctx(di->ctx.slot); + ic->dev.slot->f1 = di->ctx.slot->f1; + ic->dev.slot->f2 = di->ctx.slot->f2; + ic->dev.slot->f3 = di->ctx.slot->f3; if (((device_descriptor_t *)dev->descriptor)->bDeviceClass == 0x09) { ret = xhci_finish_hub_config(dev, ic); @@ -394,6 +415,7 @@ _free_ep_ctx_return: di->transfer_rings[i] = NULL; } _free_return: + free(ic->raw); free(ic); return ret; } @@ -412,7 +434,7 @@ xhci_destroy_dev(hci_t *const controller, const int slot_id) if (cc != CC_SUCCESS) xhci_debug("Failed to disable slot %d: %d\n", slot_id, cc); - devinfo_t *const di = DEVINFO_FROM_XHCI(xhci, slot_id); + devinfo_t *const di = &xhci->dev[slot_id]; for (i = 1; i < 31; ++i) { if (di->transfer_rings[i]) free((void *)di->transfer_rings[i]->ring); @@ -420,6 +442,5 @@ xhci_destroy_dev(hci_t *const controller, const int slot_id) free(di->interrupt_queues[i]); } - free(di); xhci->dcbaa[slot_id] = 0; } diff --git a/payloads/libpayload/drivers/usb/xhci_events.c b/payloads/libpayload/drivers/usb/xhci_events.c index ab90c59fe3..391ebf6126 100644 --- a/payloads/libpayload/drivers/usb/xhci_events.c +++ b/payloads/libpayload/drivers/usb/xhci_events.c @@ -86,12 +86,10 @@ xhci_handle_transfer_event(xhci_t *const xhci) const int id = TRB_GET(ID, ev); const int ep = TRB_GET(EP, ev); - devinfo_t *di; intrq_t *intrq; if (id && id <= xhci->max_slots_en && - (di = DEVINFO_FROM_XHCI(xhci, id)) && - (intrq = di->interrupt_queues[ep])) { + (intrq = xhci->dev[id].interrupt_queues[ep])) { /* It's a running interrupt endpoint */ intrq->ready = phys_to_virt(ev->ptr_low); if (cc == CC_SUCCESS || cc == CC_SHORT_PACKET) { diff --git a/payloads/libpayload/drivers/usb/xhci_private.h b/payloads/libpayload/drivers/usb/xhci_private.h index c7048f494e..a851d8ec57 100644 --- a/payloads/libpayload/drivers/usb/xhci_private.h +++ b/payloads/libpayload/drivers/usb/xhci_private.h @@ -198,13 +198,13 @@ typedef transfer_ring_t command_ring_t; #define SC_STATE_START 27 #define SC_STATE_LEN 8 #define SC_MASK(tok) MASK(SC_##tok##_START, SC_##tok##_LEN) -#define SC_GET(tok, sc) (((sc).SC_##tok##_FIELD & SC_MASK(tok)) \ +#define SC_GET(tok, sc) (((sc)->SC_##tok##_FIELD & SC_MASK(tok)) \ >> SC_##tok##_START) -#define SC_SET(tok, sc, to) (sc).SC_##tok##_FIELD = \ - (((sc).SC_##tok##_FIELD & ~SC_MASK(tok)) | \ +#define SC_SET(tok, sc, to) (sc)->SC_##tok##_FIELD = \ + (((sc)->SC_##tok##_FIELD & ~SC_MASK(tok)) | \ (((to) << SC_##tok##_START) & SC_MASK(tok))) #define SC_DUMP(tok, sc) usb_debug(" "#tok"\t0x%04"PRIx32"\n", SC_GET(tok, sc)) -typedef struct slotctx { +typedef volatile struct slotctx { u32 f1; u32 f2; u32 f3; @@ -240,15 +240,15 @@ typedef struct slotctx { #define EC_MXESIT_START 16 #define EC_MXESIT_LEN 16 #define EC_MASK(tok) MASK(EC_##tok##_START, EC_##tok##_LEN) -#define EC_GET(tok, ec) (((ec).EC_##tok##_FIELD & EC_MASK(tok)) \ +#define EC_GET(tok, ec) (((ec)->EC_##tok##_FIELD & EC_MASK(tok)) \ >> EC_##tok##_START) -#define EC_SET(tok, ec, to) (ec).EC_##tok##_FIELD = \ - (((ec).EC_##tok##_FIELD & ~EC_MASK(tok)) | \ +#define EC_SET(tok, ec, to) (ec)->EC_##tok##_FIELD = \ + (((ec)->EC_##tok##_FIELD & ~EC_MASK(tok)) | \ (((to) << EC_##tok##_START) & EC_MASK(tok))) #define EC_DUMP(tok, ec) usb_debug(" "#tok"\t0x%04"PRIx32"\n", EC_GET(tok, ec)) enum { EP_ISOC_OUT = 1, EP_BULK_OUT = 2, EP_INTR_OUT = 3, EP_CONTROL = 4, EP_ISOC_IN = 5, EP_BULK_IN = 6, EP_INTR_IN = 7 }; -typedef struct epctx { +typedef volatile struct epctx { u32 f1; u32 f2; u32 tr_dq_low; @@ -257,23 +257,30 @@ typedef struct epctx { u32 rsvd[3]; } epctx_t; +#define NUM_EPS 32 +#define CTXSIZE(xhci) ((xhci)->capreg->csz ? 64 : 32) + typedef union devctx { + /* set of pointers, so we can dynamically adjust Slot/EP context size */ struct { - slotctx_t slot; - epctx_t ep0; - epctx_t eps1_30[30]; + union { + slotctx_t *slot; + void *raw; /* Pointer to the whole dev context. */ + }; + epctx_t *ep0; + epctx_t *eps1_30[NUM_EPS - 2]; }; - epctx_t eps[32]; /* At index 0 it's actually the slotctx, - we have it like that so we can use - the ep_id directly as index. */ + epctx_t *ep[NUM_EPS]; /* At index 0 it's actually the slotctx, + we have it like that so we can use + the ep_id directly as index. */ } devctx_t; typedef struct inputctx { - struct { - u32 drop; - u32 add; - u32 reserved[6]; - } control; + union { /* The drop flags are located at the start of the */ + u32 *drop; /* structure, so a pointer to them is equivalent */ + void *raw; /* to a pointer to the whole (raw) input context. */ + }; + u32 *add; devctx_t dev; } inputctx_t; @@ -286,14 +293,10 @@ typedef struct intrq { } intrq_t; typedef struct devinfo { - volatile devctx_t devctx; - transfer_ring_t *transfer_rings[32]; + devctx_t ctx; + transfer_ring_t *transfer_rings[NUM_EPS]; intrq_t *interrupt_queues[32]; } devinfo_t; -#define DEVINFO_FROM_XHCI(xhci, slot_id) \ - (((xhci)->dcbaa[slot_id]) \ - ? phys_to_virt((xhci)->dcbaa[slot_id] - offsetof(devinfo_t, devctx)) \ - : NULL) typedef struct erst_entry { u32 seg_base_lo; @@ -459,6 +462,10 @@ typedef struct xhci { usbdev_t *roothub; u8 max_slots_en; + devinfo_t *dev; /* array of devinfos by slot_id */ + +#define DMA_SIZE (64 * 1024) + void *dma_buffer; } xhci_t; #define XHCI_INST(controller) ((xhci_t*)((controller)->instance)) diff --git a/payloads/libpayload/drivers/usb/xhci_rh.c b/payloads/libpayload/drivers/usb/xhci_rh.c index e6052be251..ca6131f624 100644 --- a/payloads/libpayload/drivers/usb/xhci_rh.c +++ b/payloads/libpayload/drivers/usb/xhci_rh.c @@ -27,7 +27,7 @@ * SUCH DAMAGE. */ -#define USB_DEBUG +//#define USB_DEBUG #include <usb/usb.h> #include "generic_hub.h" |