[coreboot-gerrit] Patch set updated for coreboot: 44b6a68 libpayload: xhci: Make XHCI stack usable on ARM

Isaac Christensen (isaac.christensen@se-eng.com) gerrit at coreboot.org
Wed Aug 13 19:31:34 CEST 2014


Isaac Christensen (isaac.christensen at se-eng.com) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/6643

-gerrit

commit 44b6a68dbc4748d0704caa453c7958ee962e6773
Author: Julius Werner <jwerner at chromium.org>
Date:   Tue Sep 3 17:15:31 2013 -0700

    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 at chromium.org>
    Reviewed-on: https://chromium-review.googlesource.com/169453
    Reviewed-by: Stefan Reinauer <reinauer at google.com>
    (cherry picked from commit 1fa9964063cce6cbd87ba68334806dde8aa2354c)
    Signed-off-by: Isaac Christensen <isaac.christensen at se-eng.com>
---
 payloads/libpayload/drivers/usb/xhci.c          | 126 ++++++++++++++---------
 payloads/libpayload/drivers/usb/xhci_commands.c |   6 +-
 payloads/libpayload/drivers/usb/xhci_debug.c    |  52 +++++-----
 payloads/libpayload/drivers/usb/xhci_devconf.c  | 127 ++++++++++++++----------
 payloads/libpayload/drivers/usb/xhci_events.c   |   4 +-
 payloads/libpayload/drivers/usb/xhci_private.h  |  57 ++++++-----
 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 184a370..4ab2fe3 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 3a744b3..009a69c 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 ba644c6..913b545 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 34bac8f..18ba4e8 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 ab90c59..391ebf6 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 c7048f4..a851d8e 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 e6052be..ca6131f 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"



More information about the coreboot-gerrit mailing list