summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJulius Werner <jwerner@chromium.org>2014-04-08 13:34:11 -0700
committerMarc Jones <marc.jones@se-eng.com>2014-10-22 23:53:54 +0200
commit3b9795bb2dc213c4fd73689bee7a073c19d0edf7 (patch)
treefcb7e6e89c9d9d526dca66bfb7f84023cf086288
parentbedd6aff10675f77f31431adecb9dab2970ab61f (diff)
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@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/193730 Reviewed-by: Stefan Reinauer <reinauer@chromium.org> (cherry picked from commit e46b6ebc439e86a00e13bf656d60cf6c186a3777) Signed-off-by: Isaac Christensen <isaac.christensen@se-eng.com> Reviewed-on: http://review.coreboot.org/7010 Tested-by: build bot (Jenkins) Reviewed-by: Patrick Georgi <pgeorgi@google.com>
-rw-r--r--payloads/libpayload/drivers/usb/ohci.c83
-rw-r--r--payloads/libpayload/drivers/usb/ohci_private.h2
2 files changed, 62 insertions, 23 deletions
diff --git a/payloads/libpayload/drivers/usb/ohci.c b/payloads/libpayload/drivers/usb/ohci.c
index 723b0dbadf..e1664cca9b 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 b6eaf6b449..e912837413 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;