diff -Nru ../src.imx35/sys/dev/usb/ehci.c sys/dev/usb/ehci.c --- ../src.imx35/sys/dev/usb/ehci.c 2010-10-23 22:36:19.000000000 +0900 +++ sys/dev/usb/ehci.c 2010-10-22 23:14:10.000000000 +0900 @@ -97,6 +97,7 @@ union { ehci_soft_qtd_t *qtd; /* ehci_soft_itd_t *itd; */ + /* ehci_soft_sitd_t *sitd; */ } tail; union { /* Control pipe */ @@ -128,6 +129,7 @@ Static void ehci_check_intr(ehci_softc_t *, struct ehci_xfer *); Static void ehci_check_qh_intr(ehci_softc_t *, struct ehci_xfer *); Static void ehci_check_itd_intr(ehci_softc_t *, struct ehci_xfer *); +Static void ehci_check_sitd_intr(ehci_softc_t *, struct ehci_xfer *); Static void ehci_idone(struct ehci_xfer *); Static void ehci_timeout(void *); Static void ehci_timeout_task(void *); @@ -175,6 +177,12 @@ Static void ehci_device_isoc_close(usbd_pipe_handle); Static void ehci_device_isoc_done(usbd_xfer_handle); +Static usbd_status ehci_device_fs_isoc_transfer(usbd_xfer_handle); +Static usbd_status ehci_device_fs_isoc_start(usbd_xfer_handle); +Static void ehci_device_fs_isoc_abort(usbd_xfer_handle); +Static void ehci_device_fs_isoc_close(usbd_pipe_handle); +Static void ehci_device_fs_isoc_done(usbd_xfer_handle); + Static void ehci_device_clear_toggle(usbd_pipe_handle pipe); Static void ehci_noop(usbd_pipe_handle pipe); @@ -193,9 +201,13 @@ ehci_soft_qtd_t *); Static ehci_soft_itd_t *ehci_alloc_itd(ehci_softc_t *sc); +Static ehci_soft_sitd_t *ehci_alloc_sitd(ehci_softc_t *sc); Static void ehci_free_itd(ehci_softc_t *sc, ehci_soft_itd_t *itd); +Static void ehci_free_sitd(ehci_softc_t *sc, ehci_soft_sitd_t *); Static void ehci_rem_free_itd_chain(ehci_softc_t *sc, struct ehci_xfer *exfer); +Static void ehci_rem_free_sitd_chain(ehci_softc_t *sc, + struct ehci_xfer *exfer); Static void ehci_abort_isoc_xfer(usbd_xfer_handle xfer, usbd_status status); @@ -308,6 +320,15 @@ ehci_device_isoc_done, }; +Static const struct usbd_pipe_methods ehci_device_fs_isoc_methods = { + ehci_device_fs_isoc_transfer, + ehci_device_fs_isoc_start, + ehci_device_fs_isoc_abort, + ehci_device_fs_isoc_close, + ehci_noop, + ehci_device_fs_isoc_done, +}; + static const uint8_t revbits[EHCI_MAX_POLLRATE] = { 0x00,0x40,0x20,0x60,0x10,0x50,0x30,0x70,0x08,0x48,0x28,0x68,0x18,0x58,0x38,0x78, 0x04,0x44,0x24,0x64,0x14,0x54,0x34,0x74,0x0c,0x4c,0x2c,0x6c,0x1c,0x5c,0x3c,0x7c, @@ -333,9 +354,19 @@ theehci = sc; #endif +#if 0 sc->sc_offs = EREAD1(sc, EHCI_CAPLENGTH); +#else +/* XXX work-around for i.MX35 EHCI */ + sc->sc_offs = EREAD4(sc, EHCI_CAPLENGTH) & 0xff; +#endif +#if 0 vers = EREAD2(sc, EHCI_HCIVERSION); +#else +/* XXX work-around for i.MX35 EHCI */ + vers = EREAD4(sc, EHCI_CAPLENGTH) >> 16; +#endif aprint_verbose("%s: EHCI version %x.%x\n", device_xname(sc->sc_dev), vers >> 8, vers & 0xff); @@ -395,6 +426,11 @@ if (sc->sc_vendor_init) sc->sc_vendor_init(sc); + if (sc->sc_flags & EHCIF_FREESCALE) { + /* HOST mode. (USBMODE = 3) */ + EWRITE4(sc, 0xa8, 3); + } + /* XXX need proper intr scheduling */ sc->sc_rand = 96; @@ -423,6 +459,7 @@ if (sc->sc_softitds == NULL) return ENOMEM; LIST_INIT(&sc->sc_freeitds); + LIST_INIT(&sc->sc_freesitds); TAILQ_INIT(&sc->sc_intrhead); mutex_init(&sc->sc_intrhead_lock, MUTEX_DEFAULT, IPL_USB); @@ -711,13 +748,18 @@ ehci_check_intr(ehci_softc_t *sc, struct ehci_xfer *ex) { int attr; + usbd_device_handle dev; DPRINTFN(/*15*/2, ("ehci_check_intr: ex=%p\n", ex)); attr = ex->xfer.pipe->endpoint->edesc->bmAttributes; - if (UE_GET_XFERTYPE(attr) == UE_ISOCHRONOUS) - ehci_check_itd_intr(sc, ex); - else + dev = ex->xfer.pipe->device; + if (UE_GET_XFERTYPE(attr) == UE_ISOCHRONOUS) { + if (dev->speed == USB_SPEED_HIGH) + ehci_check_itd_intr(sc, ex); + else + ehci_check_sitd_intr(sc, ex); + } else ehci_check_qh_intr(sc, ex); return; @@ -831,6 +873,42 @@ ehci_idone(ex); } +void +ehci_check_sitd_intr(ehci_softc_t *sc, struct ehci_xfer *ex) { + ehci_soft_sitd_t *sitd; + + if (&ex->xfer != SIMPLEQ_FIRST(&ex->xfer.pipe->queue)) + return; + + if (ex->sitdstart == NULL) { + printf("ehci_check_sitd_intr: not valid sitd\n"); + return; + } + + sitd = ex->sitdend; +#ifdef DIAGNOSTIC + if (sitd == NULL) { + printf("ehci_check_sitd_intr: sitdend == 0\n"); + return; + } +#endif + + /* + * check no active transfers in last sitd, meaning we're finished + */ + + usb_syncmem(&sitd->dma, sitd->offs + offsetof(ehci_sitd_t, sitd_buffer), + sizeof(sitd->sitd.sitd_buffer), BUS_DMASYNC_POSTWRITE | + BUS_DMASYNC_POSTREAD); + + if (le32toh(sitd->sitd.sitd_trans) & EHCI_SITD_ACTIVE) + return; + + DPRINTFN(12, ("ehci_check_sitd_intr: ex=%p done\n", ex)); + callout_stop(&(ex->xfer.timeout_handle)); + ehci_idone(ex); +} + Static void ehci_idone(struct ehci_xfer *ex) { @@ -872,9 +950,12 @@ /* The transfer is done, compute actual length and status. */ - if (UE_GET_XFERTYPE(xfer->pipe->endpoint->edesc->bmAttributes) - == UE_ISOCHRONOUS) { - /* Isoc transfer */ + u_int xfertype, speed; + + xfertype = UE_GET_XFERTYPE(xfer->pipe->endpoint->edesc->bmAttributes); + speed = xfer->pipe->device->speed; + if (xfertype == UE_ISOCHRONOUS && speed == USB_SPEED_HIGH) { + /* HS isoc transfer */ struct ehci_soft_itd *itd; int i, nframes, len, uframes; @@ -917,6 +998,53 @@ goto end; } + if (xfertype == UE_ISOCHRONOUS && speed == USB_SPEED_FULL) { + /* FS isoc transfer */ + struct ehci_soft_sitd *sitd; + int nframes, len; + + nframes = 0; + actlen = 0; + + for (sitd = ex->sitdstart; sitd != NULL; sitd = sitd->xfer_next) { + usb_syncmem(&sitd->dma,sitd->offs + offsetof(ehci_sitd_t, sitd_buffer), + sizeof(sitd->sitd.sitd_buffer), BUS_DMASYNC_POSTWRITE | + BUS_DMASYNC_POSTREAD); + + /* XXX - driver didn't fill in the frame full + * of uframes. This leads to scheduling + * inefficiencies, but working around + * this doubles complexity of tracking + * an xfer. + */ + if (nframes >= xfer->nframes) + break; + + status = le32toh(sitd->sitd.sitd_trans); + len = EHCI_SITD_GET_LEN(status); + if (status & (EHCI_SITD_ERR|EHCI_SITD_BUFERR| + EHCI_SITD_BABBLE|EHCI_SITD_XACTERR|EHCI_SITD_MISS)) { + /* No valid data on error */ + len = xfer->frlengths[nframes]; + } + + /* + * frlengths[i]: # of bytes to send + * len: # of bytes host didn't send + */ + xfer->frlengths[nframes] -= len; + /* frlengths[i]: # of bytes host sent */ + actlen += xfer->frlengths[nframes++]; + + if (nframes >= xfer->nframes) + break; + } + + xfer->actlen = actlen; + xfer->status = USBD_NORMAL_COMPLETION; + goto end; + } + /* Continue processing xfers using queue heads */ lsqtd = ex->sqtdend; @@ -1119,7 +1247,7 @@ * bus glue needs to call out to it. */ bool -ehci_suspend(device_t dv, const pmf_qual_t *qual) +ehci_suspend(device_t dv, pmf_qual_t qual) { ehci_softc_t *sc = device_private(dv); int i, s; @@ -1170,7 +1298,7 @@ } bool -ehci_resume(device_t dv, const pmf_qual_t *qual) +ehci_resume(device_t dv, pmf_qual_t qual) { ehci_softc_t *sc = device_private(dv); int i; @@ -1321,9 +1449,11 @@ DPRINTF(("ehci_device_clear_toggle: epipe=%p status=0x%x\n", epipe, epipe->sqh->qh.qh_qtd.qtd_status)); #ifdef EHCI_DEBUG +#ifdef USB_DEBUG if (ehcidebug) usbd_dump_pipe(pipe); #endif +#endif epipe->nexttoggle = 0; } @@ -1542,6 +1672,12 @@ hshubport = 0; } + if (sc->sc_flags & EHCIF_FREESCALE) { + /* Set hub address to 0 if embedded TT is used. */ + if (hshubaddr == sc->sc_addr) + hshubaddr = 0; + } + if (sc->sc_dying) return (USBD_IOERROR); @@ -1570,14 +1706,7 @@ case USB_SPEED_HIGH: speed = EHCI_QH_SPEED_HIGH; break; default: panic("ehci_open: bad device speed %d", dev->speed); } - if (speed != EHCI_QH_SPEED_HIGH && xfertype == UE_ISOCHRONOUS) { - aprint_error_dev(sc->sc_dev, "error opening low/full speed " - "isoc endpoint.\n"); - aprint_normal_dev(sc->sc_dev, "a low/full speed device is " - "attached to a USB2 hub, and transaction translations are " - "not yet supported.\n"); - aprint_normal_dev(sc->sc_dev, "reattach the device to the " - "root hub instead.\n"); + if (speed == EHCI_QH_SPEED_LOW && xfertype == UE_ISOCHRONOUS) { DPRINTFN(1,("ehci_open: hshubaddr=%d hshubport=%d\n", hshubaddr, hshubport)); return USBD_INVAL; @@ -1673,7 +1802,10 @@ goto bad; break; case UE_ISOCHRONOUS: - pipe->methods = &ehci_device_isoc_methods; + if (speed == EHCI_QH_SPEED_HIGH) + pipe->methods = &ehci_device_isoc_methods; + else + pipe->methods = &ehci_device_fs_isoc_methods; if (ed->bInterval == 0 || ed->bInterval > 16) { printf("ehci: opening pipe with invalid bInterval\n"); err = USBD_INVAL; @@ -1869,6 +2001,55 @@ exfer->itdend = NULL; } +/*Call at splusb*/ +void +ehci_rem_free_sitd_chain(ehci_softc_t *sc, struct ehci_xfer *exfer) +{ + struct ehci_soft_sitd *sitd, *prev; + + prev = NULL; + + if (exfer->sitdstart == NULL || exfer->sitdend == NULL) + panic("ehci isoc xfer being freed, but with no sitd chain\n"); + + for (sitd = exfer->sitdstart; sitd != NULL; sitd = sitd->xfer_next) { + prev = sitd->u.frame_list.prev; + /* Unlink sitd from hardware chain, or frame array */ + if (prev == NULL) { /* We're at the table head */ + sc->sc_softsitds[sitd->slot] = sitd->u.frame_list.next; + sc->sc_flist[sitd->slot] = sitd->sitd.sitd_next; + usb_syncmem(&sc->sc_fldma, + sizeof(ehci_link_t) * sitd->slot, + sizeof(ehci_link_t), + BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); + + if (sitd->u.frame_list.next != NULL) + sitd->u.frame_list.next->u.frame_list.prev = NULL; + } else { + /* XXX this part is untested... */ + prev->sitd.sitd_next = sitd->sitd.sitd_next; + usb_syncmem(&sitd->dma, + sitd->offs + offsetof(ehci_sitd_t, sitd_next), + sizeof(sitd->sitd.sitd_next), BUS_DMASYNC_PREWRITE); + + prev->u.frame_list.next = sitd->u.frame_list.next; + if (sitd->u.frame_list.next != NULL) + sitd->u.frame_list.next->u.frame_list.prev = prev; + } + } + + prev = NULL; + for (sitd = exfer->sitdstart; sitd != NULL; sitd = sitd->xfer_next) { + if (prev != NULL) + ehci_free_sitd(sc, prev); + prev = sitd; + } + if (prev) + ehci_free_sitd(sc, prev); + exfer->sitdstart = NULL; + exfer->sitdend = NULL; +} + /***********/ /* @@ -2250,6 +2431,20 @@ DPRINTFN(8,("ehci_root_ctrl_start: port status=0x%04x\n", v)); i = UPS_HIGH_SPEED; +#if 1 +/* XXX work-around for i.MX35 EHCI */ +#define EHCI_PS_PSPD 0x0c000000 /* Port speed */ +#define EHCI_PS_PSPD_FS 0x00000000 /* Full speed */ +#define EHCI_PS_PSPD_LS 0x04000000 /* Low speed */ +#define EHCI_PS_PSPD_HS 0x08000000 /* High speed */ + if (sc->sc_flags & EHCIF_FREESCALE) { + switch (v & EHCI_PS_PSPD) { + case EHCI_PS_PSPD_FS: + case EHCI_PS_PSPD_LS: + i = 0; + } + } +#endif if (v & EHCI_PS_CS) i |= UPS_CURRENT_CONNECT_STATUS; if (v & EHCI_PS_PE) i |= UPS_PORT_ENABLED; if (v & EHCI_PS_SUSP) i |= UPS_SUSPEND; @@ -2308,8 +2503,23 @@ goto ret; } /* Terminate reset sequence. */ +#ifndef HEAD_20101022 +#if 1 +/* + * XXX + * Work-around "uhubN: port N reset failed" problem of Freescale EHCI. + * Patch from Tsubai-san. + */ + if (sc->sc_flags & EHCIF_FREESCALE) { + v = EOREAD4(sc, port); + v &= ~EHCI_PS_PR; + } +#endif + EOWRITE4(sc, port, v); +#else v = EOREAD4(sc, port); EOWRITE4(sc, port, v & ~EHCI_PS_PR); +#endif /* Wait for HC to complete reset. */ usb_delay_ms(&sc->sc_bus, EHCI_PORT_RESET_COMPLETE); if (sc->sc_dying) { @@ -2803,6 +3013,70 @@ return itd; } +Static ehci_soft_sitd_t * +ehci_alloc_sitd(ehci_softc_t *sc) +{ + struct ehci_soft_sitd *sitd, *freesitd; + usbd_status err; + int i, s, offs, frindex, previndex; + usb_dma_t dma; + + s = splusb(); + + /* Find an sitd that wasn't freed this frame or last frame. This can + * discard sitds that were freed before frindex wrapped around + * XXX - can this lead to thrashing? Could fix by enabling wrap-around + * interrupt and fiddling with list when that happens */ + frindex = (EOREAD4(sc, EHCI_FRINDEX) + 1) >> 3; + previndex = (frindex != 0) ? frindex - 1 : sc->sc_flsize; + + freesitd = NULL; + LIST_FOREACH(sitd, &sc->sc_freesitds, u.free_list) { + if (sitd == NULL) + break; + if (sitd->slot != frindex && sitd->slot != previndex) { + freesitd = sitd; + break; + } + } + + if (freesitd == NULL) { + DPRINTFN(2, ("ehci_alloc_sitd allocating chunk\n")); + err = usb_allocmem(&sc->sc_bus, EHCI_SITD_SIZE * EHCI_SITD_CHUNK, + EHCI_PAGE_SIZE, &dma); + + if (err) { + DPRINTF(("ehci_alloc_sitd, alloc returned %d\n", err)); + return NULL; + } + + for (i = 0; i < EHCI_SITD_CHUNK; i++) { + offs = i * EHCI_SITD_SIZE; + sitd = KERNADDR(&dma, offs); + sitd->physaddr = DMAADDR(&dma, offs); + sitd->dma = dma; + sitd->offs = offs; + LIST_INSERT_HEAD(&sc->sc_freesitds, sitd, u.free_list); + } + freesitd = LIST_FIRST(&sc->sc_freesitds); + } + + sitd = freesitd; + LIST_REMOVE(sitd, u.free_list); + memset(&sitd->sitd, 0, sizeof(ehci_sitd_t)); + usb_syncmem(&sitd->dma, sitd->offs + offsetof(ehci_sitd_t, sitd_next), + sizeof(sitd->sitd.sitd_next), BUS_DMASYNC_PREWRITE | + BUS_DMASYNC_PREREAD); + + sitd->u.frame_list.next = NULL; + sitd->u.frame_list.prev = NULL; + sitd->xfer_next = NULL; + sitd->slot = 0; + splx(s); + + return sitd; +} + Static void ehci_free_itd(ehci_softc_t *sc, ehci_soft_itd_t *itd) { @@ -2813,6 +3087,17 @@ splx(s); } +Static void +ehci_free_sitd(ehci_softc_t *sc, ehci_soft_sitd_t *sitd) +{ + int s; + + s = splusb(); + LIST_INSERT_HEAD(&sc->sc_freesitds, sitd, u.free_list); + splx(s); +} + + /****************/ /* @@ -3003,6 +3288,7 @@ struct ehci_xfer *exfer; ehci_softc_t *sc; struct ehci_soft_itd *itd; + struct ehci_soft_sitd *sitd; int s, i, wake; epipe = (struct ehci_pipe *) xfer->pipe; @@ -3058,6 +3344,21 @@ sizeof(itd->itd.itd_ctl), BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); } + for (sitd = exfer->sitdstart; sitd != NULL; sitd = sitd->xfer_next) { + usb_syncmem(&sitd->dma, + sitd->offs + offsetof(ehci_sitd_t, sitd_buffer), + sizeof(sitd->sitd.sitd_buffer), + BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); + + trans_status = le32toh(sitd->sitd.sitd_trans); + trans_status &= ~EHCI_SITD_ACTIVE; + sitd->sitd.sitd_trans = htole32(trans_status); + + usb_syncmem(&sitd->dma, + sitd->offs + offsetof(ehci_sitd_t, sitd_buffer), + sizeof(sitd->sitd.sitd_buffer), + BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); + } splx(s); s = splusb(); @@ -3091,9 +3392,11 @@ DPRINTF(("ehci_timeout: exfer=%p\n", exfer)); #ifdef EHCI_DEBUG +#ifdef USB_DEBUG if (ehcidebug > 1) usbd_dump_pipe(exfer->xfer.pipe); #endif +#endif if (sc->sc_dying) { ehci_abort_xfer(&exfer->xfer, USBD_TIMEOUT); @@ -4104,3 +4407,318 @@ BUS_DMASYNC_POSTREAD); } + +Static usbd_status +ehci_device_fs_isoc_transfer(usbd_xfer_handle xfer) +{ + usbd_status err; + + err = usb_insert_transfer(xfer); + if (err && err != USBD_IN_PROGRESS) + return err; + + return ehci_device_fs_isoc_start(xfer); +} + +Static usbd_status +ehci_device_fs_isoc_start(usbd_xfer_handle xfer) +{ + struct ehci_pipe *epipe; + usbd_device_handle dev; + ehci_softc_t *sc; + struct ehci_xfer *exfer; + ehci_soft_sitd_t *sitd, *prev, *start, *stop; + usb_dma_t *dma_buf; + int i, j, k, frames; + int s, offs, total_length; + int frindex; + u_int huba, dir; + + start = NULL; + prev = NULL; + sitd = NULL; + total_length = 0; + exfer = (struct ehci_xfer *) xfer; + sc = xfer->pipe->device->bus->hci_private; + dev = xfer->pipe->device; + epipe = (struct ehci_pipe *)xfer->pipe; + + /* + * To allow continuous transfers, above we start all transfers + * immediately. However, we're still going to get usbd_start_next call + * this when another xfer completes. So, check if this is already + * in progress or not + */ + + if (exfer->sitdstart != NULL) + return USBD_IN_PROGRESS; + + DPRINTFN(2, ("ehci_device_fs_isoc_start: xfer %p len %d flags %d\n", + xfer, xfer->length, xfer->flags)); + + if (sc->sc_dying) + return USBD_IOERROR; + + /* + * To avoid complication, don't allow a request right now that'll span + * the entire frame table. To within 4 frames, to allow some leeway + * on either side of where the hc currently is. + */ + if (epipe->pipe.endpoint->edesc->bInterval * + xfer->nframes >= sc->sc_flsize - 4) { + printf("ehci: isoc descriptor requested that spans the entire frametable, too many frames\n"); + return USBD_INVAL; + } + +#ifdef DIAGNOSTIC + if (xfer->rqflags & URQ_REQUEST) + panic("ehci_device_fs_isoc_start: request\n"); + + if (!exfer->isdone) + printf("ehci_device_fs_isoc_start: not done, ex = %p\n", exfer); + exfer->isdone = 0; +#endif + + /* + * Step 1: Allocate and initialize sitds. + */ + + i = epipe->pipe.endpoint->edesc->bInterval; + if (i > 16 || i == 0) { + /* Spec page 271 says intervals > 16 are invalid */ + DPRINTF(("ehci_device_fs_isoc_start: bInvertal %d invalid\n", i)); + return USBD_INVAL; + } + + frames = xfer->nframes; + + if (frames == 0) { + DPRINTF(("ehci_device_fs_isoc_start: frames == 0\n")); + return USBD_INVAL; + } + + dma_buf = &xfer->dmabuf; + offs = 0; + + for (i = 0; i < frames; i++) { + sitd = ehci_alloc_sitd(sc); + + if (prev) + prev->xfer_next = sitd; + else + start = sitd; + +#ifdef DIAGNOSTIC + if (xfer->frlengths[i] > 0x3ff) { + printf("ehci: invalid frame length\n"); + xfer->frlengths[i] = 0x3ff; + } +#endif + + sitd->sitd.sitd_trans = htole32(EHCI_SITD_ACTIVE | + EHCI_SITD_SET_LEN(xfer->frlengths[i])); + + /* Set page0 index and offset. */ + sitd->sitd.sitd_buffer[0] = htole32(DMAADDR(dma_buf, offs)); + + total_length += xfer->frlengths[i]; + offs += xfer->frlengths[i]; + + sitd->sitd.sitd_buffer[1] = + htole32(EHCI_SITD_SET_BPTR(DMAADDR(dma_buf, offs - 1))); + + huba = dev->myhsport->parent->address; + + if (sc->sc_flags & EHCIF_FREESCALE) { + /* Set hub address to 0 if embedded TT is used. */ + if (huba == sc->sc_addr) + huba = 0; + } + + k = epipe->pipe.endpoint->edesc->bEndpointAddress; + dir = UE_GET_DIR(k) ? 1 : 0; + sitd->sitd.sitd_endp = + htole32(EHCI_SITD_SET_ENDPT(UE_GET_ADDR(k)) | + EHCI_SITD_SET_DADDR(dev->address) | + EHCI_SITD_SET_PORT(dev->myhsport->portno) | + EHCI_SITD_SET_HUBA(huba) | + EHCI_SITD_SET_DIR(dir)); + + sitd->sitd.sitd_back = htole32(EHCI_LINK_TERMINATE); + + /* XXX */ + u_char sa, sb; + u_int temp, tlen; + sa = 0; + + if (dir == 0) { /* OUT */ + temp = 0; + tlen = xfer->frlengths[i]; + if (tlen <= 188) { + temp |= 1; /* T-count = 1, TP = ALL */ + tlen = 1; + } else { + tlen += 187; + tlen /= 188; + temp |= tlen; /* T-count = [1..6] */ + temp |= 8; /* TP = Begin */ + } + sitd->sitd.sitd_buffer[1] |= htole32(temp); + + tlen += sa; + + if (tlen >= 8) { + sb = 0; + } else { + sb = (1 << tlen); + } + + sa = (1 << sa); + sa = (sb - sa) & 0x3F; + sb = 0; + } else { + sb = (-(4 << sa)) & 0xFE; + sa = (1 << sa) & 0x3F; + sa = 0x01; + sb = 0xfc; + } + + sitd->sitd.sitd_sched = htole32(EHCI_SITD_SET_SMASK(sa) | + EHCI_SITD_SET_CMASK(sb)); + + prev = sitd; + } /* End of frame */ + + sitd->sitd.sitd_trans |= htole32(EHCI_SITD_IOC); + + stop = sitd; + stop->xfer_next = NULL; + exfer->isoc_len = total_length; + + usb_syncmem(&exfer->xfer.dmabuf, 0, total_length, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + + /* + * Part 2: Transfer descriptors have now been set up, now they must + * be scheduled into the periodic frame list. Erk. Not wanting to + * complicate matters, transfer is denied if the transfer spans + * more than the period frame list. + */ + + s = splusb(); + + /* Start inserting frames */ + if (epipe->u.isoc.cur_xfers > 0) { + frindex = epipe->u.isoc.next_frame; + } else { + frindex = EOREAD4(sc, EHCI_FRINDEX); + frindex = frindex >> 3; /* Erase microframe index */ + frindex += 2; + } + + if (frindex >= sc->sc_flsize) + frindex &= (sc->sc_flsize - 1); + + /* Whats the frame interval? */ + i = epipe->pipe.endpoint->edesc->bInterval; + + sitd = start; + for (j = 0; j < frames; j++) { + if (sitd == NULL) + panic("ehci: unexpectedly ran out of isoc sitds\n"); + + sitd->sitd.sitd_next = sc->sc_flist[frindex]; + if (sitd->sitd.sitd_next == 0) + /* FIXME: frindex table gets initialized to NULL + * or EHCI_NULL? */ + sitd->sitd.sitd_next = EHCI_NULL; + + usb_syncmem(&sitd->dma, + sitd->offs + offsetof(ehci_sitd_t, sitd_next), + sizeof(ehci_sitd_t), + BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); + + sc->sc_flist[frindex] = + htole32(EHCI_LINK_SITD | sitd->physaddr); + + usb_syncmem(&sc->sc_fldma, + sizeof(ehci_link_t) * frindex, + sizeof(ehci_link_t), + BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); + + sitd->u.frame_list.next = sc->sc_softsitds[frindex]; + sc->sc_softsitds[frindex] = sitd; + if (sitd->u.frame_list.next != NULL) + sitd->u.frame_list.next->u.frame_list.prev = sitd; + sitd->slot = frindex; + sitd->u.frame_list.prev = NULL; + + frindex += i; + if (frindex >= sc->sc_flsize) + frindex -= sc->sc_flsize; + + sitd = sitd->xfer_next; + } + + epipe->u.isoc.cur_xfers++; + epipe->u.isoc.next_frame = frindex; + + exfer->sitdstart = start; + exfer->sitdend = stop; + exfer->sqtdstart = NULL; + exfer->sqtdstart = NULL; + + mutex_enter(&sc->sc_intrhead_lock); + ehci_add_intr_list(sc, exfer); + mutex_exit(&sc->sc_intrhead_lock); + xfer->status = USBD_IN_PROGRESS; + xfer->done = 0; + splx(s); + + if (sc->sc_bus.use_polling) { + printf("Starting ehci isoc xfer with polling. Bad idea?\n"); + ehci_waitintr(sc, xfer); + } + + return USBD_IN_PROGRESS; +} + +Static void +ehci_device_fs_isoc_abort(usbd_xfer_handle xfer) +{ + DPRINTFN(1, ("ehci_device_fs_isoc_abort: xfer = %p\n", xfer)); + ehci_abort_isoc_xfer(xfer, USBD_CANCELLED); +} + +Static void +ehci_device_fs_isoc_close(usbd_pipe_handle pipe) +{ + DPRINTFN(1, ("ehci_device_fs_isoc_close: nothing in the pipe to free?\n")); +} + +Static void +ehci_device_fs_isoc_done(usbd_xfer_handle xfer) +{ + struct ehci_xfer *exfer; + ehci_softc_t *sc; + struct ehci_pipe *epipe; + int s; + + exfer = EXFER(xfer); + sc = xfer->pipe->device->bus->hci_private; + epipe = (struct ehci_pipe *) xfer->pipe; + + s = splusb(); + epipe->u.isoc.cur_xfers--; + mutex_enter(&sc->sc_intrhead_lock); + if (xfer->status != USBD_NOMEM && ehci_active_intr_list(exfer)) { + ehci_del_intr_list(sc, exfer); + ehci_rem_free_sitd_chain(sc, exfer); + } + mutex_exit(&sc->sc_intrhead_lock); + splx(s); + + usb_syncmem(&xfer->dmabuf, 0, xfer->length, BUS_DMASYNC_POSTWRITE | + BUS_DMASYNC_POSTREAD); + +} diff -Nru ../src.imx35/sys/dev/usb/ehcireg.h sys/dev/usb/ehcireg.h --- ../src.imx35/sys/dev/usb/ehcireg.h 2010-10-23 22:36:19.000000000 +0900 +++ sys/dev/usb/ehcireg.h 2010-10-22 23:14:10.000000000 +0900 @@ -231,8 +231,36 @@ /* Split Transaction Isochronous Transfer Descriptor */ typedef struct { - volatile ehci_link_t sitd_next; - /* XXX many more */ + volatile u_int32_t sitd_next; + volatile u_int32_t sitd_endp; +#define EHCI_SITD_SET_DIR(x) (((x) & 0x01) << 31) +#define EHCI_SITD_SET_PORT(x) (((x) & 0x7f) << 24) +#define EHCI_SITD_SET_HUBA(x) (((x) & 0x7f) << 16) +#define EHCI_SITD_SET_ENDPT(x) (((x) & 0x0f) << 8) +#define EHCI_SITD_SET_DADDR(x) ((x) & 0x7f) + + volatile u_int32_t sitd_sched; +#define EHCI_SITD_SET_SMASK(x) ((x) & 0xff) +#define EHCI_SITD_SET_CMASK(x) (((x) & 0xff) << 8) + + volatile u_int32_t sitd_trans; +#define EHCI_SITD_IOC 0x80000000 +#define EHCI_SITD_P 0x40000000 +#define EHCI_SITD_GET_LEN(x) (((x) & 0x03ff0000) >> 16) +#define EHCI_SITD_SET_LEN(x) (((x) & 0x3ff) << 16) +#define EHCI_SITD_ACTIVE 0x00000080 +#define EHCI_SITD_ERR 0x00000040 +#define EHCI_SITD_BUFERR 0x00000020 +#define EHCI_SITD_BABBLE 0x00000010 +#define EHCI_SITD_XACTERR 0x00000008 +#define EHCI_SITD_MISS 0x00000004 +#define EHCI_SITD_SPLITXSTATE 0x00000002 + + volatile u_int32_t sitd_buffer[2]; +#define EHCI_SITD_SET_BPTR(x) ((x) & 0xfffff000) +#define EHCI_SITD_SET_OFFS(x) ((x) & 0xfff) + + volatile u_int32_t sitd_back; } ehci_sitd_t; #define EHCI_SITD_ALIGN 32 diff -Nru ../src.imx35/sys/dev/usb/ehcivar.h sys/dev/usb/ehcivar.h --- ../src.imx35/sys/dev/usb/ehcivar.h 2010-10-23 22:36:19.000000000 +0900 +++ sys/dev/usb/ehcivar.h 2010-10-22 23:14:10.000000000 +0900 @@ -55,7 +55,10 @@ #define EHCI_SQH_CHUNK (EHCI_PAGE_SIZE / EHCI_SQH_SIZE) typedef struct ehci_soft_itd { - ehci_itd_t itd; + union { + ehci_itd_t itd; + ehci_sitd_t sitd; + }; union { struct { /* soft_itds links in a periodic frame*/ @@ -75,6 +78,12 @@ #define EHCI_ITD_SIZE ((sizeof(struct ehci_soft_itd) + EHCI_QH_ALIGN - 1) / EHCI_ITD_ALIGN * EHCI_ITD_ALIGN) #define EHCI_ITD_CHUNK (EHCI_PAGE_SIZE / EHCI_ITD_SIZE) +#define ehci_soft_sitd_t ehci_soft_itd_t +#define ehci_soft_sitd ehci_soft_itd +#define sc_softsitds sc_softitds +#define EHCI_SITD_SIZE ((sizeof(struct ehci_soft_sitd) + EHCI_QH_ALIGN - 1) / EHCI_SITD_ALIGN * EHCI_SITD_ALIGN) +#define EHCI_SITD_CHUNK (EHCI_PAGE_SIZE / EHCI_SITD_SIZE) + struct ehci_xfer { struct usbd_xfer xfer; struct usb_task abort_task; @@ -83,6 +92,8 @@ ehci_soft_qtd_t *sqtdend; ehci_soft_itd_t *itdstart; ehci_soft_itd_t *itdend; + ehci_soft_sitd_t *sitdstart; + ehci_soft_sitd_t *sitdend; u_int isoc_len; int isdone; /* used only when DIAGNOSTIC is defined */ }; @@ -116,6 +127,7 @@ u_int sc_offs; /* offset to operational regs */ int sc_flags; /* misc flags */ #define EHCIF_DROPPED_INTR_WORKAROUND 0x01 +#define EHCIF_FREESCALE 0x100 /* Freescale EHCI extension */ char sc_vendor[32]; /* vendor string for root hub */ int sc_id_vendor; /* vendor ID for root hub */ @@ -144,6 +156,7 @@ ehci_soft_qh_t *sc_freeqhs; ehci_soft_qtd_t *sc_freeqtds; LIST_HEAD(sc_freeitds, ehci_soft_itd) sc_freeitds; + LIST_HEAD(sc_freesitds, ehci_soft_sitd) sc_freesitds; int sc_noport; u_int8_t sc_hasppc; /* has Port Power Control */ @@ -190,6 +203,6 @@ int ehci_detach(ehci_softc_t *, int); int ehci_activate(device_t, enum devact); void ehci_childdet(device_t, device_t); -bool ehci_suspend(device_t, const pmf_qual_t *); -bool ehci_resume(device_t, const pmf_qual_t *); +bool ehci_suspend(device_t, pmf_qual_t); +bool ehci_resume(device_t, pmf_qual_t); bool ehci_shutdown(device_t, int);