]> xenbits.xensource.com Git - seabios.git/commitdiff
usb: Add support for OHCI bulk transfers
authorKevin O'Connor <kevin@koconnor.net>
Mon, 29 Dec 2014 18:06:23 +0000 (13:06 -0500)
committerKevin O'Connor <kevin@koconnor.net>
Thu, 1 Jan 2015 17:56:35 +0000 (12:56 -0500)
Support bulk transfers (usb drives) on OHCI USB controllers.  Now that
there is support for 32bit only drive controllers, it is not that hard
to support disks on OHCI controllers, and it may be useful for some
old hardware.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
src/hw/usb-ohci.c
src/hw/usb-ohci.h
src/hw/usb.c

index 478976880d243fc58f4d70316626e88f84380d0d..ee31b8361cbb0065f603da0dc420d928fca0c2ed 100644 (file)
@@ -7,6 +7,7 @@
 #include "biosvar.h" // GET_LOWFLAT
 #include "config.h" // CONFIG_*
 #include "malloc.h" // free
+#include "memmap.h" // PAGE_SIZE
 #include "output.h" // dprintf
 #include "pci.h" // pci_bdf_to_bus
 #include "pci_ids.h" // PCI_CLASS_SERIAL_USB_OHCI
@@ -27,6 +28,7 @@ struct usb_ohci_s {
 struct ohci_pipe {
     struct ohci_ed ed;
     struct usb_pipe pipe;
+    struct ohci_regs *regs;
     void *data;
     int count;
     struct ohci_td *tds;
@@ -118,10 +120,10 @@ check_ohci_ports(struct usb_ohci_s *cntl)
 
 // Wait for next USB frame to start - for ensuring safe memory release.
 static void
-ohci_waittick(struct usb_ohci_s *cntl)
+ohci_waittick(struct ohci_regs *regs)
 {
     barrier();
-    struct ohci_hcca *hcca = (void*)cntl->regs->hcca;
+    struct ohci_hcca *hcca = (void*)regs->hcca;
     u32 startframe = hcca->frame_no;
     u32 end = timer_calc(1000 * 5);
     for (;;) {
@@ -143,7 +145,7 @@ ohci_free_pipes(struct usb_ohci_s *cntl)
     u32 creg = readl(&cntl->regs->control);
     if (creg & (OHCI_CTRL_CLE|OHCI_CTRL_BLE)) {
         writel(&cntl->regs->control, creg & ~(OHCI_CTRL_CLE|OHCI_CTRL_BLE));
-        ohci_waittick(cntl);
+        ohci_waittick(cntl->regs);
     }
 
     u32 *pos = &cntl->regs->ed_controlhead;
@@ -209,7 +211,7 @@ start_ohci(struct usb_ohci_s *cntl, struct ohci_hcca *hcca)
 
     // Go into operational state
     writel(&cntl->regs->control
-           , (OHCI_CTRL_CBSR | OHCI_CTRL_CLE | OHCI_CTRL_PLE
+           , (OHCI_CTRL_CBSR | OHCI_CTRL_CLE | OHCI_CTRL_BLE | OHCI_CTRL_PLE
               | OHCI_USB_OPER | oldrwc));
     readl(&cntl->regs->control); // flush writes
 
@@ -320,6 +322,9 @@ ohci_desc2pipe(struct ohci_pipe *pipe, struct usbdevice_s *usbdev
     pipe->ed.hwINFO = (ED_SKIP | usbdev->devaddr | (pipe->pipe.ep << 7)
                        | (epdesc->wMaxPacketSize << 16)
                        | (usbdev->speed ? ED_LOWSPEED : 0));
+    struct usb_ohci_s *cntl = container_of(
+        usbdev->hub->cntl, struct usb_ohci_s, usb);
+    pipe->regs = cntl->regs;
 }
 
 static struct usb_pipe *
@@ -398,10 +403,6 @@ ohci_realloc_pipe(struct usbdevice_s *usbdev, struct usb_pipe *upipe
     u8 eptype = epdesc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
     if (eptype == USB_ENDPOINT_XFER_INT)
         return ohci_alloc_intr_pipe(usbdev, epdesc);
-    if (eptype != USB_ENDPOINT_XFER_CONTROL) {
-        dprintf(1, "OHCI Bulk transfers not supported.\n");
-        return NULL;
-    }
     struct usb_ohci_s *cntl = container_of(
         usbdev->hub->cntl, struct usb_ohci_s, usb);
     dprintf(7, "ohci_alloc_async_pipe %p\n", &cntl->usb);
@@ -415,7 +416,11 @@ ohci_realloc_pipe(struct usbdevice_s *usbdev, struct usb_pipe *upipe
     }
 
     // Allocate a new queue head.
-    struct ohci_pipe *pipe = malloc_tmphigh(sizeof(*pipe));
+    struct ohci_pipe *pipe;
+    if (eptype == USB_ENDPOINT_XFER_CONTROL)
+        pipe = malloc_tmphigh(sizeof(*pipe));
+    else
+        pipe = malloc_low(sizeof(*pipe));
     if (!pipe) {
         warn_noalloc();
         return NULL;
@@ -424,9 +429,12 @@ ohci_realloc_pipe(struct usbdevice_s *usbdev, struct usb_pipe *upipe
     ohci_desc2pipe(pipe, usbdev, epdesc);
 
     // Add queue head to controller list.
-    pipe->ed.hwNextED = cntl->regs->ed_controlhead;
+    u32 *head = &cntl->regs->ed_controlhead;
+    if (eptype != USB_ENDPOINT_XFER_CONTROL)
+        head = &cntl->regs->ed_bulkhead;
+    pipe->ed.hwNextED = *head;
     barrier();
-    cntl->regs->ed_controlhead = (u32)&pipe->ed;
+    *head = (u32)&pipe->ed;
     return &pipe->pipe;
 }
 
@@ -435,10 +443,12 @@ wait_ed(struct ohci_ed *ed, int timeout)
 {
     u32 end = timer_calc(timeout);
     for (;;) {
-        if (ed->hwHeadP == ed->hwTailP)
+        if ((ed->hwHeadP & ~(ED_C|ED_H)) == ed->hwTailP)
             return 0;
         if (timer_check(end)) {
             warn_timeout();
+            dprintf(1, "ohci ed info=%x tail=%x head=%x next=%x\n"
+                    , ed->hwINFO, ed->hwTailP, ed->hwHeadP, ed->hwNextED);
             return -1;
         }
         yield();
@@ -458,8 +468,6 @@ ohci_send_control(struct usb_pipe *p, int dir, const void *cmd, int cmdsize
         return -1;
     }
     struct ohci_pipe *pipe = container_of(p, struct ohci_pipe, pipe);
-    struct usb_ohci_s *cntl = container_of(
-        pipe->pipe.cntl, struct usb_ohci_s, usb);
 
     // Setup transfer descriptors
     struct ohci_td *tds = malloc_tmphigh(sizeof(*tds) * 3);
@@ -491,20 +499,66 @@ ohci_send_control(struct usb_pipe *p, int dir, const void *cmd, int cmdsize
     pipe->ed.hwTailP = (u32)td;
     barrier();
     pipe->ed.hwINFO &= ~ED_SKIP;
-    writel(&cntl->regs->cmdstatus, OHCI_CLF);
+    writel(&pipe->regs->cmdstatus, OHCI_CLF);
 
     int ret = wait_ed(&pipe->ed, usb_xfer_time(p, datasize));
     pipe->ed.hwINFO |= ED_SKIP;
     if (ret)
-        ohci_waittick(cntl);
+        ohci_waittick(pipe->regs);
     free(tds);
     return ret;
 }
 
+#define STACKOTDS 16
+#define OHCI_TD_ALIGN 16
+
 int
 ohci_send_bulk(struct usb_pipe *p, int dir, void *data, int datasize)
 {
-    return -1;
+    ASSERT32FLAT();
+    if (! CONFIG_USB_OHCI)
+        return -1;
+    dprintf(7, "ohci_send_bulk %p\n", p);
+    struct ohci_pipe *pipe = container_of(p, struct ohci_pipe, pipe);
+
+    // Allocate 16 tds on stack (with required alignment)
+    u8 tdsbuf[sizeof(struct ohci_td) * STACKOTDS + OHCI_TD_ALIGN - 1];
+    struct ohci_td *tds = (void*)ALIGN((u32)tdsbuf, OHCI_TD_ALIGN), *td = tds;
+    memset(tds, 0, sizeof(*tds) * STACKOTDS);
+
+    // Setup transfer descriptors
+    u16 maxpacket = pipe->pipe.maxpacket;
+    u32 dest = (u32)data, dataend = dest + datasize;
+    while (dest < dataend) {
+        // Send data pids
+        if (td >= &tds[STACKOTDS]) {
+            warn_noalloc();
+            return -1;
+        }
+        int maxtransfer = 2*PAGE_SIZE - (dest & (PAGE_SIZE-1));
+        int transfer = dataend - dest;
+        if (transfer > maxtransfer)
+            transfer = ALIGN_DOWN(maxtransfer, maxpacket);
+        td->hwINFO = (dir ? TD_DP_IN : TD_DP_OUT) | TD_CC;
+        td->hwCBP = dest;
+        td->hwNextTD = (u32)&td[1];
+        td->hwBE = dest + transfer - 1;
+        td++;
+        dest += transfer;
+    }
+
+    // Transfer data
+    pipe->ed.hwHeadP = (u32)tds | (pipe->ed.hwHeadP & ED_C);
+    pipe->ed.hwTailP = (u32)td;
+    barrier();
+    pipe->ed.hwINFO &= ~ED_SKIP;
+    writel(&pipe->regs->cmdstatus, OHCI_BLF);
+
+    int ret = wait_ed(&pipe->ed, usb_xfer_time(p, datasize));
+    pipe->ed.hwINFO |= ED_SKIP;
+    if (ret)
+        ohci_waittick(pipe->regs);
+    return ret;
 }
 
 int
index 56995234b5e9a4685edf40ea82ba3d16e7352033..db935cab73f53aae86c4fc8dd0675a7fdf37e55e 100644 (file)
@@ -107,6 +107,7 @@ struct ohci_regs {
 
 #define OHCI_HCR        (1 << 0)
 #define OHCI_CLF        (1 << 1)
+#define OHCI_BLF        (1 << 2)
 
 #define OHCI_INTR_MIE   (1 << 31)
 
index bb646a78538a194b484e81e243f38c02911448d8..75412f91ae622707e03b22e07ddaae2e483208e9 100644 (file)
@@ -71,6 +71,8 @@ usb_send_bulk(struct usb_pipe *pipe_fl, int dir, void *data, int datasize)
     case USB_TYPE_UHCI:
         return uhci_send_bulk(pipe_fl, dir, data, datasize);
     case USB_TYPE_OHCI:
+        if (MODESEGMENT)
+            return -1;
         return ohci_send_bulk(pipe_fl, dir, data, datasize);
     case USB_TYPE_EHCI:
         return ehci_send_bulk(pipe_fl, dir, data, datasize);
@@ -102,7 +104,8 @@ usb_poll_intr(struct usb_pipe *pipe_fl, void *data)
 
 int usb_32bit_pipe(struct usb_pipe *pipe_fl)
 {
-    return CONFIG_USB_XHCI && GET_LOWFLAT(pipe_fl->type) == USB_TYPE_XHCI;
+    return (CONFIG_USB_XHCI && GET_LOWFLAT(pipe_fl->type) == USB_TYPE_XHCI)
+        || (CONFIG_USB_OHCI && GET_LOWFLAT(pipe_fl->type) == USB_TYPE_OHCI);
 }