[coreboot-gerrit] New patch to review for coreboot: cf72933 libpayload: usb: Make OHCI work with ARM systems

Isaac Christensen (isaac.christensen@se-eng.com) gerrit at coreboot.org
Fri Oct 3 00:06:09 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/7010

-gerrit

commit cf72933c426c557cfb7cfa0cc6d29ed0fda604b0
Author: Julius Werner <jwerner at chromium.org>
Date:   Tue Apr 8 13:34:11 2014 -0700

    libpayload: usb: Make OHCI work with ARM systems
    
    This patch enables the OHCI driver to use DMA memory, which is necessary
    for ARM systems where DMA devices are not cache coherent. I really only
    need this to test some later OHCI changes, but it was easy enough...
    copied almost verbatim from ehci.c.
    
    Change-Id: Ia717eef28340bd6182a6782e83bfdd0693cf0db1
    Signed-off-by: Julius Werner <jwerner at chromium.org>
    Reviewed-on: https://chromium-review.googlesource.com/193730
    Reviewed-by: Stefan Reinauer <reinauer at chromium.org>
    (cherry picked from commit e46b6ebc439e86a00e13bf656d60cf6c186a3777)
    Signed-off-by: Isaac Christensen <isaac.christensen at se-eng.com>
---
 payloads/libpayload/drivers/usb/ohci.c         | 83 +++++++++++++++++++-------
 payloads/libpayload/drivers/usb/ohci_private.h |  2 +
 2 files changed, 62 insertions(+), 23 deletions(-)

diff --git a/payloads/libpayload/drivers/usb/ohci.c b/payloads/libpayload/drivers/usb/ohci.c
index 723b0db..e1664cc 100644
--- a/payloads/libpayload/drivers/usb/ohci.c
+++ b/payloads/libpayload/drivers/usb/ohci.c
@@ -221,12 +221,18 @@ ohci_init (unsigned long physical_bar)
 	OHCI_INST (controller)->opreg->HcCommandStatus = HostControllerReset;
 	udelay (10); /* at most 10us for reset to complete. State must be set to Operational within 2ms (5.1.1.4) */
 	OHCI_INST (controller)->opreg->HcFmInterval = interval;
-	OHCI_INST (controller)->hcca = memalign(256, 256);
+	OHCI_INST (controller)->hcca = dma_memalign(256, 256);
 	memset((void*)OHCI_INST (controller)->hcca, 0, 256);
 
+	if (dma_initialized()) {
+		OHCI_INST(controller)->dma_buffer = dma_memalign(4096, DMA_SIZE);
+		if (!OHCI_INST(controller)->dma_buffer)
+			fatal("Not enough DMA memory for OHCI bounce buffer.\n");
+	}
+
 	/* Initialize interrupt table. */
 	u32 *const intr_table = OHCI_INST(controller)->hcca->HccaInterruptTable;
-	ed_t *const periodic_ed = memalign(sizeof(ed_t), sizeof(ed_t));
+	ed_t *const periodic_ed = dma_memalign(sizeof(ed_t), sizeof(ed_t));
 	memset((void *)periodic_ed, 0, sizeof(*periodic_ed));
 	for (i = 0; i < 32; ++i)
 		intr_table[i] = virt_to_phys(periodic_ed);
@@ -352,12 +358,28 @@ ohci_free_ed (ed_t *const head)
 }
 
 static int
-ohci_control (usbdev_t *dev, direction_t dir, int drlen, void *devreq, int dalen,
-	      unsigned char *data)
+ohci_control (usbdev_t *dev, direction_t dir, int drlen, void *setup, int dalen,
+	      unsigned char *src)
 {
+	u8 *data = src;
+	u8 *devreq = setup;
 	int remaining = dalen;
 	td_t *cur;
 
+	if (!dma_coherent(devreq)) {
+		devreq = OHCI_INST(dev->controller)->dma_buffer;
+		memcpy(devreq, setup, drlen);
+	}
+	if (dalen > 0 && !dma_coherent(src)) {
+		data = OHCI_INST(dev->controller)->dma_buffer + drlen;
+		if (drlen + dalen > DMA_SIZE) {
+			usb_debug("OHCI control transfer too large for DMA buffer: %d\n", drlen + dalen);
+			return -1;
+		}
+		if (dir == OUT)
+			memcpy(data, src, dalen);
+	}
+
 	// pages are specified as 4K in OHCI, so don't use getpagesize()
 	int first_page = (unsigned long)data / 4096;
 	int last_page = (unsigned long)(data+dalen-1)/4096;
@@ -365,7 +387,7 @@ ohci_control (usbdev_t *dev, direction_t dir, int drlen, void *devreq, int dalen
 	int pages = (dalen==0)?0:(last_page - first_page + 1);
 
 	/* First TD. */
-	td_t *const first_td = (td_t *)memalign(sizeof(td_t), sizeof(td_t));
+	td_t *const first_td = (td_t *)dma_memalign(sizeof(td_t), sizeof(td_t));
 	memset((void *)first_td, 0, sizeof(*first_td));
 	cur = first_td;
 
@@ -379,7 +401,7 @@ ohci_control (usbdev_t *dev, direction_t dir, int drlen, void *devreq, int dalen
 
 	while (pages > 0) {
 		/* One more TD. */
-		td_t *const next = (td_t *)memalign(sizeof(td_t), sizeof(td_t));
+		td_t *const next = (td_t *)dma_memalign(sizeof(td_t), sizeof(td_t));
 		memset((void *)next, 0, sizeof(*next));
 		/* Linked to the previous. */
 		cur->next_td = virt_to_phys(next);
@@ -413,7 +435,7 @@ ohci_control (usbdev_t *dev, direction_t dir, int drlen, void *devreq, int dalen
 	}
 
 	/* One more TD. */
-	td_t *const next_td = (td_t *)memalign(sizeof(td_t), sizeof(td_t));
+	td_t *const next_td = (td_t *)dma_memalign(sizeof(td_t), sizeof(td_t));
 	memset((void *)next_td, 0, sizeof(*next_td));
 	/* Linked to the previous. */
 	cur->next_td = virt_to_phys(next_td);
@@ -428,13 +450,13 @@ ohci_control (usbdev_t *dev, direction_t dir, int drlen, void *devreq, int dalen
 	cur->buffer_end = 0;
 
 	/* Final dummy TD. */
-	td_t *const final_td = (td_t *)memalign(sizeof(td_t), sizeof(td_t));
+	td_t *const final_td = (td_t *)dma_memalign(sizeof(td_t), sizeof(td_t));
 	memset((void *)final_td, 0, sizeof(*final_td));
 	/* Linked to the previous. */
 	cur->next_td = virt_to_phys(final_td);
 
 	/* Data structures */
-	ed_t *head = memalign(sizeof(ed_t), sizeof(ed_t));
+	ed_t *head = dma_memalign(sizeof(ed_t), sizeof(ed_t));
 	memset((void*)head, 0, sizeof(*head));
 	head->config = (dev->address << ED_FUNC_SHIFT) |
 		(0 << ED_EP_SHIFT) |
@@ -465,21 +487,34 @@ ohci_control (usbdev_t *dev, direction_t dir, int drlen, void *devreq, int dalen
 	/* free memory */
 	ohci_free_ed(head);
 
-	if (result >= 0)
+	if (result >= 0) {
 		result = dalen - result;
+		if (dir == IN && data != src)
+			memcpy(src, data, result);
+	}
 
 	return result;
 }
 
 /* finalize == 1: if data is of packet aligned size, add a zero length packet */
 static int
-ohci_bulk (endpoint_t *ep, int dalen, u8 *data, int finalize)
+ohci_bulk (endpoint_t *ep, int dalen, u8 *src, int finalize)
 {
 	int i;
-	int remaining = dalen;
-	usb_debug("bulk: %x bytes from %x, finalize: %x, maxpacketsize: %x\n", dalen, data, finalize, ep->maxpacketsize);
-
 	td_t *cur, *next;
+	int remaining = dalen;
+	u8 *data = src;
+	usb_debug("bulk: %x bytes from %x, finalize: %x, maxpacketsize: %x\n", dalen, src, finalize, ep->maxpacketsize);
+
+	if (!dma_coherent(src)) {
+		data = OHCI_INST(ep->dev->controller)->dma_buffer;
+		if (dalen > DMA_SIZE) {
+			usb_debug("OHCI bulk transfer too large for DMA buffer: %d\n", dalen);
+			return -1;
+		}
+		if (ep->direction == OUT)
+			memcpy(data, src, dalen);
+	}
 
 	// pages are specified as 4K in OHCI, so don't use getpagesize()
 	int first_page = (unsigned long)data / 4096;
@@ -493,7 +528,7 @@ ohci_bulk (endpoint_t *ep, int dalen, u8 *data, int finalize)
 	}
 
 	/* First TD. */
-	td_t *const first_td = (td_t *)memalign(sizeof(td_t), sizeof(td_t));
+	td_t *const first_td = (td_t *)dma_memalign(sizeof(td_t), sizeof(td_t));
 	memset((void *)first_td, 0, sizeof(*first_td));
 	cur = next = first_td;
 
@@ -531,7 +566,7 @@ ohci_bulk (endpoint_t *ep, int dalen, u8 *data, int finalize)
 			data += second_page_size;
 		}
 		/* One more TD. */
-		next = (td_t *)memalign(sizeof(td_t), sizeof(td_t));
+		next = (td_t *)dma_memalign(sizeof(td_t), sizeof(td_t));
 		memset((void *)next, 0, sizeof(*next));
 		/* Linked to the previous. */
 		cur->next_td = virt_to_phys(next);
@@ -543,7 +578,7 @@ ohci_bulk (endpoint_t *ep, int dalen, u8 *data, int finalize)
 	cur = next;
 
 	/* Data structures */
-	ed_t *head = memalign(sizeof(ed_t), sizeof(ed_t));
+	ed_t *head = dma_memalign(sizeof(ed_t), sizeof(ed_t));
 	memset((void*)head, 0, sizeof(*head));
 	head->config = (ep->dev->address << ED_FUNC_SHIFT) |
 		((ep->endpoint & 0xf) << ED_EP_SHIFT) |
@@ -575,9 +610,11 @@ ohci_bulk (endpoint_t *ep, int dalen, u8 *data, int finalize)
 	/* free memory */
 	ohci_free_ed(head);
 
-	if (result >= 0)
+	if (result >= 0) {
 		result = dalen - result;
-	else
+		if (ep->direction == IN && data != src)
+			memcpy(src, data, result);
+	} else
 		clear_stall(ep);
 
 	return result;
@@ -638,16 +675,16 @@ ohci_create_intr_queue(endpoint_t *const ep, const int reqsize,
 		return NULL;
 
 	intr_queue_t *const intrq =
-		(intr_queue_t *)memalign(sizeof(intrq->ed), sizeof(*intrq));
+		(intr_queue_t *)dma_memalign(sizeof(intrq->ed), sizeof(*intrq));
 	memset(intrq, 0, sizeof(*intrq));
-	intrq->data = (u8 *)malloc(reqcount * reqsize);
+	intrq->data = (u8 *)dma_malloc(reqcount * reqsize);
 	intrq->reqsize = reqsize;
 	intrq->endp = ep;
 
 	/* Create #reqcount TDs. */
 	u8 *cur_data = intrq->data;
 	for (i = 0; i < reqcount; ++i) {
-		intrq_td_t *const td = memalign(sizeof(td->td), sizeof(*td));
+		intrq_td_t *const td = dma_memalign(sizeof(td->td), sizeof(*td));
 		++intrq->remaining_tds;
 		ohci_fill_intrq_td(td, intrq, cur_data);
 		cur_data += reqsize;
@@ -659,7 +696,7 @@ ohci_create_intr_queue(endpoint_t *const ep, const int reqsize,
 	}
 
 	/* Create last, dummy TD. */
-	intrq_td_t *dummy_td = memalign(sizeof(dummy_td->td), sizeof(*dummy_td));
+	intrq_td_t *dummy_td = dma_memalign(sizeof(dummy_td->td), sizeof(*dummy_td));
 	memset(dummy_td, 0, sizeof(*dummy_td));
 	dummy_td->intrq = intrq;
 	if (last_td)
diff --git a/payloads/libpayload/drivers/usb/ohci_private.h b/payloads/libpayload/drivers/usb/ohci_private.h
index b6eaf6b..e912837 100644
--- a/payloads/libpayload/drivers/usb/ohci_private.h
+++ b/payloads/libpayload/drivers/usb/ohci_private.h
@@ -257,6 +257,8 @@
 		hcca_t *hcca;
 		usbdev_t *roothub;
 		ed_t *periodic_ed;
+#define DMA_SIZE (64 * 1024)
+		void *dma_buffer;
 	} ohci_t;
 
 	typedef enum { OHCI_SETUP=0, OHCI_OUT=1, OHCI_IN=2, OHCI_FROM_TD=3 } ohci_pid_t;



More information about the coreboot-gerrit mailing list