#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
struct ohci_pipe {
struct ohci_ed ed;
struct usb_pipe pipe;
+ struct ohci_regs *regs;
void *data;
int count;
struct ohci_td *tds;
// 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 (;;) {
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;
// 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
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 *
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);
}
// 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;
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;
}
{
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();
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);
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