aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--payloads/libpayload/drivers/usb/xhci.c126
-rw-r--r--payloads/libpayload/drivers/usb/xhci_commands.c6
-rw-r--r--payloads/libpayload/drivers/usb/xhci_debug.c52
-rw-r--r--payloads/libpayload/drivers/usb/xhci_devconf.c127
-rw-r--r--payloads/libpayload/drivers/usb/xhci_events.c4
-rw-r--r--payloads/libpayload/drivers/usb/xhci_private.h57
-rw-r--r--payloads/libpayload/drivers/usb/xhci_rh.c2
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"