From 2cc4681014efa5785bb5088151e6e19d10f38bd7 Mon Sep 17 00:00:00 2001 From: Matthew Dillon Date: Thu, 8 Jul 2004 03:47:09 +0000 Subject: [PATCH] Bring EHCI up-to-date with NetBSD. The most serious fixes are 1.53, 1.55, 1.56, and 1.64. revision 1.67 Fix a byte order error. (note: was already previously ported to DFly) revision 1.66 Fix an endianness problem (EHCI_NULL was being double-swapped). revision 1.65 Make one message dependent on ohcidebug, so it doesn't interfere with polled operation; e.g. when entering a root device or in DDB. revision 1.64 Further cleanup of toggle handling. Now that we use EHCI_QH_DTC, we don't need to fiddle with the TOGGLE bit in the overlay descriptor, so minimize how much we fuss with it. revision 1.63 Fix an error in a debug printf(). revision 1.62 Adjust a couple of comments to make it clear WTF is going on. revision 1.61 revision 1.60 Remove comment about the data toggle being borked. revision 1.59 As the ehci_idone() now uses the variable `epipe' unconditionally, always declare it (in other words, make this file compile w/o EHCI_DEBUG). revision 1.58 Fix a stupid bug in ehci_check_intr() that caused use to try to complete a transaction that was still running. Now ehci can handle multiple devices being active at once. revision 1.57 Oops. Remove a couple of printf()s. revision 1.56 Failure to properly mask off UE_DIR_IN from the endpoint address was causing OHCI_ED_FORMAT_ISO and EHCI_QH_HRECL to get set spuriously, causing rather interesting lossage. Suddenly I get MUCH better performance with ehci... revision 1.55 Set the data toggle correctly, and use EHCI_QTD_DTC. This fixes problems with my ALi-based drive enclosure (it works now, rather than failing to attach). Also seems to work with a GL811-based enclosure and an ASUS enclosure with a CD-RW, on both Intel and NEC controllers. Note: The ALi enclosure is currently very SLOW, due to some issue with taking too long to notice that the QTD is complete. This requires more investigation. revision 1.54 branches: 1.54.2; Michael van Elst reports his USB2 disk works stable after latest Chuck's ehci.c changes, so remove the item from TODO. revision 1.53 in ehci_softintr() when looping over the active xfers, save the next pointer before calling ehci_check_intr(), since that will free the xfer structure if the xfer is complete. revision 1.52 add list of known issues, from Lennart Augustsson and Michael van Elst revision 1.51 Use the correct wValue to get hub desriptors. Also, make wValue checks of root hub codes less strict. --- sys/bus/usb/ehci.c | 135 ++++++++++++++++++++++++------------------ sys/bus/usb/ehcireg.h | 11 ++-- 2 files changed, 82 insertions(+), 64 deletions(-) diff --git a/sys/bus/usb/ehci.c b/sys/bus/usb/ehci.c index d7bdcabc12..3617e1a817 100644 --- a/sys/bus/usb/ehci.c +++ b/sys/bus/usb/ehci.c @@ -1,24 +1,15 @@ /* - * $NetBSD: ehci.c,v 1.46 2003/03/09 19:51:13 augustss Exp $ + * $NetBSD: ehci.c,v 1.67 2004/07/06 04:18:05 mycroft Exp $ * $FreeBSD: src/sys/dev/usb/ehci.c,v 1.5 2003/11/10 00:20:52 joe Exp $ - * $DragonFly: src/sys/bus/usb/ehci.c,v 1.5 2004/03/12 03:43:06 dillon Exp $ - */ - -/* Also ported from NetBSD: - * $NetBSD: ehci.c,v 1.50 2003/10/18 04:50:35 simonb Exp $ - */ - -/* - * TODO - * hold off explorations by companion controllers until ehci has started. + * $DragonFly: src/sys/bus/usb/ehci.c,v 1.6 2004/07/08 03:47:09 dillon Exp $ */ /* - * Copyright (c) 2001 The NetBSD Foundation, Inc. + * Copyright (c) 2004 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation - * by Lennart Augustsson (lennart@augustsson.net). + * by Lennart Augustsson (lennart@augustsson.net) and by Charles M. Hannum. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -59,6 +50,24 @@ * */ +/* + * TODO: + * 1) hold off explorations by companion controllers until ehci has started. + * + * 2) The EHCI driver lacks support for interrupt isochronous transfers, so + * devices using them don't work. + * Interrupt transfers are not difficult, it's just not done. + * + * 3) The meaty part to implement is the support for USB 2.0 hubs. + * They are quite compolicated since the need to be able to do + * "transaction translation", i.e., converting to/from USB 2 and USB 1. + * So the hub driver needs to handle and schedule these things, to + * assign place in frame where different devices get to go. See chapter + * on hubs in USB 2.0 for details. + * + * 4) command failures are not recovered correctly + */ + #include #include #include @@ -116,6 +125,8 @@ SYSCTL_INT(_hw_usb_ehci, OID_AUTO, debug, CTLFLAG_RW, struct ehci_pipe { struct usbd_pipe pipe; + int nexttoggle; + ehci_soft_qh_t *sqh; union { ehci_soft_qtd_t *qtd; @@ -486,7 +497,7 @@ ehci_intr(void *v) /* If we get an interrupt while polling, then just ignore it. */ if (sc->sc_bus.use_polling) { #ifdef DIAGNOSTIC - printf("ehci_intr: ignored interrupt while polling\n"); + DPRINTFN(16, ("ehci_intr: ignored interrupt while polling\n")); #endif return (0); } @@ -618,7 +629,7 @@ void ehci_softintr(void *v) { ehci_softc_t *sc = v; - struct ehci_xfer *ex; + struct ehci_xfer *ex, *nextex; DPRINTFN(10,("%s: ehci_softintr (%d)\n", USBDEVNAME(sc->sc_bus.bdev), sc->sc_bus.intr_context)); @@ -631,8 +642,10 @@ ehci_softintr(void *v) * An interrupt just tells us that something is done, we have no * clue what, so we need to scan through all active transfers. :-( */ - for (ex = LIST_FIRST(&sc->sc_intrhead); ex; ex = LIST_NEXT(ex, inext)) + for (ex = LIST_FIRST(&sc->sc_intrhead); ex; ex = nextex) { + nextex = LIST_NEXT(ex, inext); ehci_check_intr(sc, ex); + } #ifdef USB_USE_SOFTINTR if (sc->sc_softwake) { @@ -680,7 +693,7 @@ ehci_check_intr(ehci_softc_t *sc, struct ehci_xfer *ex) if (status & EHCI_QTD_HALTED) goto done; /* We want short packets, and it is short: it's done */ - if (EHCI_QTD_SET_BYTES(status) != 0) + if (EHCI_QTD_GET_BYTES(status) != 0) goto done; } DPRINTFN(12, ("ehci_check_intr: ex=%p std=%p still active\n", @@ -697,9 +710,7 @@ void ehci_idone(struct ehci_xfer *ex) { usbd_xfer_handle xfer = &ex->xfer; -#ifdef USB_DEBUG struct ehci_pipe *epipe = (struct ehci_pipe *)xfer->pipe; -#endif ehci_soft_qtd_t *sqtd; u_int32_t status = 0, nstatus; int actlen; @@ -749,11 +760,12 @@ ehci_idone(struct ehci_xfer *ex) /* If there are left over TDs we need to update the toggle. */ if (sqtd != NULL) { - if (!(xfer->rqflags & URQ_REQUEST)) - printf("ehci_idone: need toggle update\n"); + printf("ehci_idone: need toggle update status=%08x nstatus=%08x\n", status, nstatus); #if 0 - epipe->nexttoggle = EHCI_TD_GET_DT(le32toh(std->td.td_token)); + ehci_dump_sqh(epipe->sqh); + ehci_dump_sqtds(ex->sqtdstart); #endif + epipe->nexttoggle = EHCI_QTD_GET_TOGGLE(nstatus); } status &= EHCI_QTD_STATERRS; @@ -765,9 +777,8 @@ ehci_idone(struct ehci_xfer *ex) char sbuf[128]; bitmask_snprintf((u_int32_t)status, - "\20\3MISSEDMICRO\4XACT\5BABBLE\6BABBLE" - "\7HALTED", - sbuf, sizeof(sbuf)); + "\20\7HALTED\6BUFERR\5BABBLE\4XACTERR" + "\3MISSED", sbuf, sizeof(sbuf)); DPRINTFN((status == EHCI_QTD_HALTED)*/*10*/2, ("ehci_idone: error, addr=%d, endpt=0x%02x, " @@ -1065,7 +1076,7 @@ ehci_device_clear_toggle(usbd_pipe_handle pipe) if (ehcidebug) usbd_dump_pipe(pipe); #endif - epipe->sqh->qh.qh_qtd.qtd_status &= htole32(~EHCI_QTD_TOGGLE); + epipe->nexttoggle = 0; } Static void @@ -1224,6 +1235,8 @@ ehci_open(usbd_pipe_handle pipe) if (sc->sc_dying) return (USBD_IOERROR); + epipe->nexttoggle = 0; + if (addr == sc->sc_addr) { switch (ed->bEndpointAddress) { case USB_CONTROL_ENDPOINT: @@ -1252,9 +1265,9 @@ ehci_open(usbd_pipe_handle pipe) /* qh_link filled when the QH is added */ sqh->qh.qh_endp = htole32( EHCI_QH_SET_ADDR(addr) | - EHCI_QH_SET_ENDPT(ed->bEndpointAddress) | - EHCI_QH_SET_EPS(speed) | /* XXX */ - /* XXX EHCI_QH_DTC ? */ + EHCI_QH_SET_ENDPT(UE_GET_ADDR(ed->bEndpointAddress)) | + EHCI_QH_SET_EPS(speed) | + EHCI_QH_DTC | EHCI_QH_SET_MPL(UGETW(ed->wMaxPacketSize)) | (speed != EHCI_QH_SPEED_HIGH && xfertype == UE_CONTROL ? EHCI_QH_CTL : 0) | @@ -1360,8 +1373,8 @@ ehci_set_qh_qtd(ehci_soft_qh_t *sqh, ehci_soft_qtd_t *sqtd) sqh->qh.qh_curqtd = 0; sqh->qh.qh_qtd.qtd_next = htole32(sqtd->physaddr); sqh->sqtd = sqtd; - /* Keep toggle, clear the rest, including length. */ - sqh->qh.qh_qtd.qtd_status &= htole32(EHCI_QTD_TOGGLE); + /* Clear halt */ + sqh->qh.qh_qtd.qtd_status &= htole32(~EHCI_QTD_HALTED); } /* @@ -1740,7 +1753,7 @@ ehci_root_ctrl_start(usbd_xfer_handle xfer) #endif break; case C(UR_GET_DESCRIPTOR, UT_READ_CLASS_DEVICE): - if (value != 0) { + if ((value & 0xff) != 0) { err = USBD_IOERROR; goto ret; } @@ -2109,8 +2122,9 @@ ehci_alloc_sqtd_chain(struct ehci_pipe *epipe, ehci_softc_t *sc, ehci_soft_qtd_t *next, *cur; ehci_physaddr_t dataphys, dataphyspage, dataphyslastpage, nextphys; u_int32_t qtdstatus; - int len, curlen, offset; - int i; + int len, curlen, mps; + int i, tog; + int offset; usb_dma_t *dma = &xfer->dmabuf; DPRINTFN(alen<4*4096,("ehci_alloc_sqtd_chain: start len=%d\n", alen)); @@ -2119,14 +2133,19 @@ ehci_alloc_sqtd_chain(struct ehci_pipe *epipe, ehci_softc_t *sc, len = alen; dataphys = DMAADDR(dma, 0); dataphyslastpage = EHCI_PAGE(DMAADDR(dma, len - 1)); - qtdstatus = htole32( - EHCI_QTD_ACTIVE | +#if 0 +printf("status=%08x toggle=%d\n", epipe->sqh->qh.qh_qtd.qtd_status, + epipe->nexttoggle); +#endif + qtdstatus = EHCI_QTD_ACTIVE | EHCI_QTD_SET_PID(rd ? EHCI_QTD_PID_IN : EHCI_QTD_PID_OUT) | EHCI_QTD_SET_CERR(3) /* IOC set below */ /* BYTES set below */ - /* XXX Data toggle */ - ); + ; + mps = UGETW(epipe->pipe.endpoint->edesc->wMaxPacketSize); + tog = epipe->nexttoggle; + qtdstatus |= EHCI_QTD_SET_TOGGLE(tog); cur = ehci_alloc_sqtd(sc); *sp = cur; @@ -2171,10 +2190,8 @@ ehci_alloc_sqtd_chain(struct ehci_pipe *epipe, ehci_softc_t *sc, curlen = EHCI_PAGE_SIZE - EHCI_PAGE_MASK(dataphys); #endif - - /* XXX true for EHCI? */ /* the length must be a multiple of the max size */ - curlen -= curlen % UGETW(epipe->pipe.endpoint->edesc->wMaxPacketSize); + curlen -= curlen % mps; DPRINTFN(1,("ehci_alloc_sqtd_chain: multiple QTDs, " "curlen=%d\n", curlen)); #ifdef DIAGNOSTIC @@ -2192,7 +2209,7 @@ ehci_alloc_sqtd_chain(struct ehci_pipe *epipe, ehci_softc_t *sc, next = ehci_alloc_sqtd(sc); if (next == NULL) goto nomem; - nextphys = next->physaddr; + nextphys = htole32(next->physaddr); } else { next = NULL; nextphys = EHCI_NULL; @@ -2211,13 +2228,19 @@ ehci_alloc_sqtd_chain(struct ehci_pipe *epipe, ehci_softc_t *sc, #endif } cur->nextqtd = next; - cur->qtd.qtd_next = cur->qtd.qtd_altnext = htole32(nextphys); + cur->qtd.qtd_next = cur->qtd.qtd_altnext = nextphys; cur->qtd.qtd_status = - qtdstatus | htole32(EHCI_QTD_SET_BYTES(curlen)); + htole32(qtdstatus | EHCI_QTD_SET_BYTES(curlen)); cur->xfer = xfer; cur->len = curlen; DPRINTFN(10,("ehci_alloc_sqtd_chain: cbp=0x%08x end=0x%08x\n", dataphys, dataphys + curlen)); + /* adjust the toggle based on the number of packets in this + qtd */ + if (((curlen + mps - 1) / mps) & 1) { + tog ^= 1; + qtdstatus ^= EHCI_QTD_TOGGLE_MASK; + } if (len == 0) break; DPRINTFN(10,("ehci_alloc_sqtd_chain: extend chain\n")); @@ -2227,6 +2250,7 @@ ehci_alloc_sqtd_chain(struct ehci_pipe *epipe, ehci_softc_t *sc, } cur->qtd.qtd_status |= htole32(EHCI_QTD_IOC); *ep = cur; + epipe->nexttoggle = tog; DPRINTFN(10,("ehci_alloc_sqtd_chain: return sqtd=%p sqtdend=%p\n", *sp, *ep)); @@ -2541,31 +2565,23 @@ ehci_device_request(usbd_xfer_handle xfer) sqh = epipe->sqh; epipe->u.ctl.length = len; - /* XXX - * Since we're messing with the QH we must know the HC is in sync. - * This needs to go away since it slows down control transfers. - * Removing it entails: - * - fill the QH only once with addr & wMaxPacketSize - * - put the correct data toggles in the qtds and set DTC - */ - /* ehci_sync_hc(sc); */ - /* Update device address and length since they may have changed. */ + /* Update device address and length since they may have changed + during the setup of the control pipe in usbd_new_device(). */ /* XXX This only needs to be done once, but it's too early in open. */ /* XXXX Should not touch ED here! */ sqh->qh.qh_endp = - (sqh->qh.qh_endp & htole32(~(EHCI_QH_ADDRMASK | EHCI_QG_MPLMASK))) | + (sqh->qh.qh_endp & htole32(~(EHCI_QH_ADDRMASK | EHCI_QH_MPLMASK))) | htole32( EHCI_QH_SET_ADDR(addr) | - /* EHCI_QH_DTC | */ EHCI_QH_SET_MPL(UGETW(epipe->pipe.endpoint->edesc->wMaxPacketSize)) ); - /* Clear toggle */ - sqh->qh.qh_qtd.qtd_status &= htole32(~EHCI_QTD_TOGGLE); /* Set up data transaction */ if (len != 0) { ehci_soft_qtd_t *end; + /* Start toggle at 1. */ + epipe->nexttoggle = 1; err = ehci_alloc_sqtd_chain(epipe, sc, len, isread, xfer, &next, &end); if (err) @@ -2573,18 +2589,18 @@ ehci_device_request(usbd_xfer_handle xfer) end->nextqtd = stat; end->qtd.qtd_next = end->qtd.qtd_altnext = htole32(stat->physaddr); - /* Start toggle at 1. */ - /*next->qtd.td_flags |= htole32(EHCI_QTD_TOGGLE);*/ } else { next = stat; } memcpy(KERNADDR(&epipe->u.ctl.reqdma, 0), req, sizeof *req); + /* Clear toggle */ setup->qtd.qtd_status = htole32( EHCI_QTD_ACTIVE | EHCI_QTD_SET_PID(EHCI_QTD_PID_SETUP) | EHCI_QTD_SET_CERR(3) | + EHCI_QTD_SET_TOGGLE(0) | EHCI_QTD_SET_BYTES(sizeof *req) ); setup->qtd.qtd_buffer[0] = htole32(DMAADDR(&epipe->u.ctl.reqdma, 0)); @@ -2597,6 +2613,7 @@ ehci_device_request(usbd_xfer_handle xfer) EHCI_QTD_ACTIVE | EHCI_QTD_SET_PID(isread ? EHCI_QTD_PID_OUT : EHCI_QTD_PID_IN) | EHCI_QTD_SET_CERR(3) | + EHCI_QTD_SET_TOGGLE(1) | EHCI_QTD_IOC ); stat->qtd.qtd_buffer[0] = 0; /* XXX not needed? */ diff --git a/sys/bus/usb/ehcireg.h b/sys/bus/usb/ehcireg.h index 96c23e8470..c5e88a59f3 100644 --- a/sys/bus/usb/ehcireg.h +++ b/sys/bus/usb/ehcireg.h @@ -1,7 +1,7 @@ /* - * $NetBSD: ehcireg.h,v 1.13 2001/11/23 01:16:27 augustss Exp $ + * $NetBSD: ehcireg.h,v 1.17 2004/06/23 06:45:56 mycroft Exp $ * $FreeBSD: src/sys/dev/usb/ehcireg.h,v 1.1 2003/04/14 14:04:07 ticso Exp $ - * $DragonFly: src/sys/bus/usb/ehcireg.h,v 1.2 2004/02/11 15:17:26 joerg Exp $ + * $DragonFly: src/sys/bus/usb/ehcireg.h,v 1.3 2004/07/08 03:47:09 dillon Exp $ */ /* * Copyright (c) 2001 The NetBSD Foundation, Inc. @@ -235,7 +235,8 @@ typedef struct { #define EHCI_QTD_GET_BYTES(x) (((x) >> 16) & 0x7fff) #define EHCI_QTD_SET_BYTES(x) ((x) << 16) #define EHCI_QTD_GET_TOGGLE(x) (((x) >> 31) & 0x1) -#define EHCI_QTD_TOGGLE 0x80000000 +#define EHCI_QTD_SET_TOGGLE(x) ((x) << 31) +#define EHCI_QTD_TOGGLE_MASK 0x80000000 ehci_physaddr_t qtd_buffer[EHCI_QTD_NBUFFERS]; } ehci_qtd_t; #define EHCI_QTD_ALIGN 32 @@ -262,8 +263,8 @@ typedef struct { #define EHCI_QH_HRECL 0x00008000 #define EHCI_QH_GET_MPL(x) (((x) >> 16) & 0x7ff) /* max packet len */ #define EHCI_QH_SET_MPL(x) ((x) << 16) -#define EHCI_QG_MPLMASK 0x07ff0000 -#define EHCI_QH_GET_CTL(x) (((x) >> 26) & 0x01) /* control endpoint */ +#define EHCI_QH_MPLMASK 0x07ff0000 +#define EHCI_QH_GET_CTL(x) (((x) >> 27) & 0x01) /* control endpoint */ #define EHCI_QH_CTL 0x08000000 #define EHCI_QH_GET_NRL(x) (((x) >> 28) & 0x0f) /* NAK reload */ #define EHCI_QH_SET_NRL(x) ((x) << 28) -- 2.41.0