aboutsummaryrefslogtreecommitdiff
path: root/payloads/libpayload/drivers/usb/xhci.c
diff options
context:
space:
mode:
Diffstat (limited to 'payloads/libpayload/drivers/usb/xhci.c')
-rw-r--r--payloads/libpayload/drivers/usb/xhci.c126
1 files changed, 79 insertions, 47 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);