diff options
author | Julius Werner <jwerner@chromium.org> | 2014-04-08 13:34:11 -0700 |
---|---|---|
committer | Marc Jones <marc.jones@se-eng.com> | 2014-10-22 23:53:54 +0200 |
commit | 3b9795bb2dc213c4fd73689bee7a073c19d0edf7 (patch) | |
tree | fcb7e6e89c9d9d526dca66bfb7f84023cf086288 | |
parent | bedd6aff10675f77f31431adecb9dab2970ab61f (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.c | 83 | ||||
-rw-r--r-- | 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 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; |