From owner-p4-projects@FreeBSD.ORG Wed Dec 19 18:56:29 2007 Return-Path: Delivered-To: p4-projects@freebsd.org Received: by hub.freebsd.org (Postfix, from userid 32767) id 9FB8F16A46E; Wed, 19 Dec 2007 18:56:29 +0000 (UTC) Delivered-To: perforce@FreeBSD.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 30F0016A420 for ; Wed, 19 Dec 2007 18:56:29 +0000 (UTC) (envelope-from hselasky@FreeBSD.org) Received: from repoman.freebsd.org (repoman.freebsd.org [IPv6:2001:4f8:fff6::29]) by mx1.freebsd.org (Postfix) with ESMTP id 2906913C44B for ; Wed, 19 Dec 2007 18:56:29 +0000 (UTC) (envelope-from hselasky@FreeBSD.org) Received: from repoman.freebsd.org (localhost [127.0.0.1]) by repoman.freebsd.org (8.14.1/8.14.1) with ESMTP id lBJIuSf8002191 for ; Wed, 19 Dec 2007 18:56:29 GMT (envelope-from hselasky@FreeBSD.org) Received: (from perforce@localhost) by repoman.freebsd.org (8.14.1/8.14.1/Submit) id lBJIuSrE002188 for perforce@freebsd.org; Wed, 19 Dec 2007 18:56:28 GMT (envelope-from hselasky@FreeBSD.org) Date: Wed, 19 Dec 2007 18:56:28 GMT Message-Id: <200712191856.lBJIuSrE002188@repoman.freebsd.org> X-Authentication-Warning: repoman.freebsd.org: perforce set sender to hselasky@FreeBSD.org using -f From: Hans Petter Selasky To: Perforce Change Reviews Cc: Subject: PERFORCE change 131246 for review X-BeenThere: p4-projects@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: p4 projects tree changes List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 19 Dec 2007 18:56:30 -0000 http://perforce.freebsd.org/chv.cgi?CH=131246 Change 131246 by hselasky@hselasky_laptop001 on 2007/12/19 18:56:23 HW DMA optimization - new technique (OHCI, UHCI and EHCI) This commit gets rid of the doorbell and any other delays in the critical execution path by introducing an extra set of TDs and QHs for every USB transfer. The idea is that the USB Host Controller does not immediately know that we have removed a QH or TD from the schedule. Even if the Host Controller will not touch any data in the buffers we cannot simply start reusing the QH and TD DMA structures. Instead we use a second QH and TD set. When the second transfer is done we know that the first QH and TD set which we dequeued before we enqued the second transfer is now out the Host Controllers hands. Then we can right away start using the first set of QH and TD DMA structures and there is no need for doorbell or any other delay. If you stop, timeout or unsetup an USB transfer there will be a delay like usual. If the USB driver you are using requires a high number of interrupts per second you will see a noticable decrease in CPU usage and a minor increase in performance. Affected files ... .. //depot/projects/usb/src/sys/dev/usb/ehci.c#63 edit .. //depot/projects/usb/src/sys/dev/usb/ehci.h#26 edit .. //depot/projects/usb/src/sys/dev/usb/ohci.c#52 edit .. //depot/projects/usb/src/sys/dev/usb/uhci.c#53 edit .. //depot/projects/usb/src/sys/dev/usb/usb_subr.h#80 edit .. //depot/projects/usb/src/sys/dev/usb/usb_transfer.c#78 edit Differences ... ==== //depot/projects/usb/src/sys/dev/usb/ehci.c#63 (text+ko) ==== @@ -1287,35 +1287,48 @@ DPRINTFN(12, ("xfer=%p checking transfer\n", xfer)); if (methods == &ehci_device_isoc_fs_methods) { - ehci_sitd_t *td = xfer->td_transfer_last; + ehci_sitd_t *td; /* isochronous full speed transfer */ + td = xfer->td_transfer_last; usbd_pc_cpu_invalidate(td->page_cache); + status = le32toh(td->sitd_status); - status = le32toh(td->sitd_status); + /* also check if first is complete */ + + td = xfer->td_transfer_first; + usbd_pc_cpu_invalidate(td->page_cache); + status |= le32toh(td->sitd_status); if (!(status & EHCI_SITD_ACTIVE)) { ehci_device_done(xfer, USBD_NORMAL_COMPLETION); goto transferred; } } else if (methods == &ehci_device_isoc_hs_methods) { - ehci_itd_t *td = xfer->td_transfer_last; + ehci_itd_t *td; /* isochronous high speed transfer */ + td = xfer->td_transfer_last; usbd_pc_cpu_invalidate(td->page_cache); + status = + td->itd_status[0] | td->itd_status[1] | + td->itd_status[2] | td->itd_status[3] | + td->itd_status[4] | td->itd_status[5] | + td->itd_status[6] | td->itd_status[7]; - status = ((!(td->itd_status[0] & htole32(EHCI_ITD_ACTIVE))) && - (!(td->itd_status[1] & htole32(EHCI_ITD_ACTIVE))) && - (!(td->itd_status[2] & htole32(EHCI_ITD_ACTIVE))) && - (!(td->itd_status[3] & htole32(EHCI_ITD_ACTIVE))) && - (!(td->itd_status[4] & htole32(EHCI_ITD_ACTIVE))) && - (!(td->itd_status[5] & htole32(EHCI_ITD_ACTIVE))) && - (!(td->itd_status[6] & htole32(EHCI_ITD_ACTIVE))) && - (!(td->itd_status[7] & htole32(EHCI_ITD_ACTIVE)))); + /* also check first transfer */ + td = xfer->td_transfer_first; + usbd_pc_cpu_invalidate(td->page_cache); + status |= + td->itd_status[0] | td->itd_status[1] | + td->itd_status[2] | td->itd_status[3] | + td->itd_status[4] | td->itd_status[5] | + td->itd_status[6] | td->itd_status[7]; - if (status) { + /* if no transactions are active we continue */ + if (!(status & htole32(EHCI_ITD_ACTIVE))) { ehci_device_done(xfer, USBD_NORMAL_COMPLETION); goto transferred; } @@ -1452,10 +1465,6 @@ status &= sc->sc_eintrs; - if (status & EHCI_STS_IAA) { - DPRINTF(("door bell\n")); - wakeup(&sc->sc_async_p_last); - } if (status & EHCI_STS_HSE) { printf("%s: unrecoverable error, " "controller halted\n", __FUNCTION__); @@ -1718,11 +1727,17 @@ temp.average = xfer->max_usb_frame_size; temp.max_frame_size = xfer->max_frame_size; - xfer->td_transfer_first = xfer->td_start; - xfer->td_transfer_cache = xfer->td_start; + /* toggle the DMA set we are using */ + xfer->flags_int.curr_dma_set ^= 1; + + /* get next DMA set */ + td = xfer->td_start[xfer->flags_int.curr_dma_set]; + + xfer->td_transfer_first = td; + xfer->td_transfer_cache = td; temp.td = NULL; - temp.td_next = xfer->td_start; + temp.td_next = td; temp.qtd_status = 0; temp.setup_alt_next = xfer->flags_int.short_frames_ok; @@ -1848,13 +1863,13 @@ if (ehcidebug > 8) { DPRINTF(("nexttog=%d; data before transfer:\n", xfer->pipe->toggle_next)); - ehci_dump_sqtds(xfer->td_start); + ehci_dump_sqtds(xfer->td_transfer_first); } #endif methods = xfer->pipe->methods; - qh = xfer->qh_start; + qh = xfer->qh_start[xfer->flags_int.curr_dma_set]; /* the "qh_link" field is filled when the QH is added */ @@ -1962,14 +1977,13 @@ return; } -static uint8_t +static void ehci_isoc_fs_done(ehci_softc_t *sc, struct usbd_xfer *xfer) { uint32_t nframes = xfer->nframes; uint32_t status; uint32_t *plen = xfer->frlengths; uint16_t len = 0; - uint8_t need_delay = 0; ehci_sitd_t *td = xfer->td_transfer_first; ehci_sitd_t **pp_last = &sc->sc_isoc_fs_p_last[xfer->qh_pos]; @@ -1993,10 +2007,6 @@ usbd_pc_cpu_invalidate(td->page_cache); status = le32toh(td->sitd_status); - /* check for active transfers */ - if (status & EHCI_SITD_ACTIVE) { - need_delay = 1; - } len = EHCI_SITD_GET_LEN(status); if (*plen >= len) { @@ -2017,10 +2027,10 @@ xfer->aframes = xfer->nframes; - return (need_delay); + return; } -static uint8_t +static void ehci_isoc_hs_done(ehci_softc_t *sc, struct usbd_xfer *xfer) { uint32_t nframes = xfer->nframes; @@ -2028,7 +2038,6 @@ uint32_t *plen = xfer->frlengths; uint16_t len = 0; uint8_t td_no = 0; - uint8_t need_delay = 0; ehci_itd_t *td = xfer->td_transfer_first; ehci_itd_t **pp_last = &sc->sc_isoc_hs_p_last[xfer->qh_pos]; @@ -2053,9 +2062,6 @@ usbd_pc_cpu_invalidate(td->page_cache); status = le32toh(td->itd_status[td_no]); - if (status & EHCI_ITD_ACTIVE) { - need_delay = 1; - } len = EHCI_ITD_GET_LEN(status); if (*plen >= len) { @@ -2080,7 +2086,7 @@ } xfer->aframes = xfer->nframes; - return (need_delay); + return; } /* NOTE: "done" can be run two times in a row, @@ -2091,39 +2097,28 @@ { struct usbd_pipe_methods *methods = xfer->pipe->methods; ehci_softc_t *sc = xfer->usb_sc; - uint8_t need_delay; mtx_assert(&sc->sc_bus.mtx, MA_OWNED); - need_delay = 0; - DPRINTFN(1, ("xfer=%p, pipe=%p, error=%d\n", xfer, xfer->pipe, error)); - /* - * ... could check for not-completed transfers, instead of setting - * need_delay ... - */ - if ((error == USBD_CANCELLED) || - (error == USBD_TIMEOUT)) { - if (xfer->flags_int.transferring) { - need_delay = 1; - } - } if ((methods == &ehci_device_bulk_methods) || (methods == &ehci_device_ctrl_methods)) { #ifdef USB_DEBUG if (ehcidebug > 8) { DPRINTF(("nexttog=%d; data after transfer:\n", xfer->pipe->toggle_next)); - ehci_dump_sqtds(xfer->td_start); + ehci_dump_sqtds(xfer->td_transfer_first); } #endif - EHCI_REMOVE_QH(xfer->qh_start, sc->sc_async_p_last); + EHCI_REMOVE_QH(xfer->qh_start[xfer->flags_int.curr_dma_set], + sc->sc_async_p_last); } if (methods == &ehci_device_intr_methods) { - EHCI_REMOVE_QH(xfer->qh_start, sc->sc_intr_p_last[xfer->qh_pos]); + EHCI_REMOVE_QH(xfer->qh_start[xfer->flags_int.curr_dma_set], + sc->sc_intr_p_last[xfer->qh_pos]); } /* * Only finish isochronous transfers once which will update @@ -2132,76 +2127,14 @@ if (xfer->td_transfer_first && xfer->td_transfer_last) { if (methods == &ehci_device_isoc_fs_methods) { - if (ehci_isoc_fs_done(sc, xfer)) { - need_delay = 1; - } + ehci_isoc_fs_done(sc, xfer); } if (methods == &ehci_device_isoc_hs_methods) { - if (ehci_isoc_hs_done(sc, xfer)) { - need_delay = 1; - } + ehci_isoc_hs_done(sc, xfer); } xfer->td_transfer_first = NULL; xfer->td_transfer_last = NULL; } - if ((methods != &ehci_root_ctrl_methods) && - (methods != &ehci_root_intr_methods)) { - - if (((methods == &ehci_device_ctrl_methods) || - (methods == &ehci_device_bulk_methods)) && - (sc->sc_doorbell_disable == 0)) { - - uint32_t to = 100 * 1000; - - /* - * This implementation does not use the doorbell - * interrupt. The reason is that one gets more - * throughput by waiting for the doorbell inline, - * than by using the doorbell interrupt. A typical - * result reading from an USB mass storage disk: - * - * With doorbell interrupt: 27 seconds for 512MB - * With inline doorbell: 21 seconds for 512MB - * - * Although the inline version causes a slightly - * higher CPU usage, it does not block the system - * in any way, because this USB driver uses its - * own mutex. - * - * --hps - */ - - /* wait for doorbell ~32us */ - EOWRITE4(sc, EHCI_USBCMD, - EOREAD4(sc, EHCI_USBCMD) | EHCI_CMD_IAAD); - - while (EOREAD4(sc, EHCI_USBCMD) & EHCI_CMD_IAAD) { - if (!to--) { - printf("%s: doorbell timeout " - "(disabling)\n", __FUNCTION__); - sc->sc_doorbell_disable = 1; - break; - } - DELAY(1); - } - - /* - * acknowledge any doorbell interrupt (SiS chipsets - * require this) - */ - EOWRITE4(sc, EHCI_USBSTS, EHCI_STS_IAA); - - } else { - /* - * wait until the hardware has finished any possible - * use of the transfer descriptor and QH - * - * in case "need_delay" is set, wait until the next - * isochronous frame is started - */ - DELAY(need_delay ? ((3 * 1000) / (2 * 8)) : (5)); - } - } /* dequeue transfer and start next transfer */ usbd_transfer_dequeue(xfer, error); return; @@ -2421,6 +2354,7 @@ { ehci_sitd_t *td; uint32_t sitd_portaddr; + uint8_t ds; sitd_portaddr = EHCI_SITD_SET_ADDR(xfer->address) | @@ -2435,19 +2369,23 @@ /* initialize all TD's */ - for (td = xfer->td_start; td; td = td->obj_next) { + for (ds = 0; ds != 2; ds++) { + + for (td = xfer->td_start[ds]; td; td = td->obj_next) { - td->sitd_portaddr = sitd_portaddr; + td->sitd_portaddr = sitd_portaddr; - /* - * TODO: make some kind of automatic SMASK/CMASK selection - * based on micro-frame usage - * - * micro-frame usage (8 microframes per 1ms) - */ - td->sitd_back = htole32(EHCI_LINK_TERMINATE); + /* + * TODO: make some kind of automatic + * SMASK/CMASK selection based on micro-frame + * usage + * + * micro-frame usage (8 microframes per 1ms) + */ + td->sitd_back = htole32(EHCI_LINK_TERMINATE); - usbd_pc_cpu_flush(td->page_cache); + usbd_pc_cpu_flush(td->page_cache); + } } return; } @@ -2534,7 +2472,12 @@ plen = xfer->frlengths; - td = (xfer->td_transfer_first = xfer->td_start); + /* toggle the DMA set we are using */ + xfer->flags_int.curr_dma_set ^= 1; + + /* get next DMA set */ + td = xfer->td_start[xfer->flags_int.curr_dma_set]; + xfer->td_transfer_first = td; pp_last = &sc->sc_isoc_fs_p_last[xfer->pipe->isoc_next]; @@ -2688,39 +2631,44 @@ { ehci_itd_t *td; uint32_t temp; + uint8_t ds; /* initialize all TD's */ - for (td = xfer->td_start; td; td = td->obj_next) { + for (ds = 0; ds != 2; ds++) { + + for (td = xfer->td_start[ds]; td; td = td->obj_next) { - /* set TD inactive */ - td->itd_status[0] = 0; - td->itd_status[1] = 0; - td->itd_status[2] = 0; - td->itd_status[3] = 0; - td->itd_status[4] = 0; - td->itd_status[5] = 0; - td->itd_status[6] = 0; - td->itd_status[7] = 0; + /* set TD inactive */ + td->itd_status[0] = 0; + td->itd_status[1] = 0; + td->itd_status[2] = 0; + td->itd_status[3] = 0; + td->itd_status[4] = 0; + td->itd_status[5] = 0; + td->itd_status[6] = 0; + td->itd_status[7] = 0; - /* set endpoint and address */ - td->itd_bp[0] = htole32 - (EHCI_ITD_SET_ADDR(xfer->address) | - EHCI_ITD_SET_ENDPT(UE_GET_ADDR(xfer->endpoint))); + /* set endpoint and address */ + td->itd_bp[0] = htole32 + (EHCI_ITD_SET_ADDR(xfer->address) | + EHCI_ITD_SET_ENDPT(UE_GET_ADDR(xfer->endpoint))); - temp = EHCI_ITD_SET_MPL(xfer->max_packet_size & 0x7FF); + temp = + EHCI_ITD_SET_MPL(xfer->max_packet_size & 0x7FF); - /* set direction */ - if (UE_GET_DIR(xfer->endpoint) == UE_DIR_IN) { - temp |= EHCI_ITD_SET_DIR_IN; - } - /* set maximum packet size */ - td->itd_bp[1] = htole32(temp); + /* set direction */ + if (UE_GET_DIR(xfer->endpoint) == UE_DIR_IN) { + temp |= EHCI_ITD_SET_DIR_IN; + } + /* set maximum packet size */ + td->itd_bp[1] = htole32(temp); - /* set transfer multiplier */ - td->itd_bp[2] = htole32(xfer->max_packet_count & 3); + /* set transfer multiplier */ + td->itd_bp[2] = htole32(xfer->max_packet_count & 3); - usbd_pc_cpu_flush(td->page_cache); + usbd_pc_cpu_flush(td->page_cache); + } } return; } @@ -2806,7 +2754,12 @@ plen = xfer->frlengths; - td = (xfer->td_transfer_first = xfer->td_start); + /* toggle the DMA set we are using */ + xfer->flags_int.curr_dma_set ^= 1; + + /* get next DMA set */ + td = xfer->td_start[xfer->flags_int.curr_dma_set]; + xfer->td_transfer_first = td; pp_last = &sc->sc_isoc_hs_p_last[xfer->pipe->isoc_next]; @@ -3704,6 +3657,8 @@ usbd_transfer_setup_sub(parm); } +alloc_dma_set: + if (parm->err) { return; } @@ -3787,7 +3742,7 @@ } } - xfer->td_start = last_obj; + xfer->td_start[xfer->flags_int.curr_dma_set] = last_obj; last_obj = NULL; @@ -3816,8 +3771,12 @@ } } - xfer->qh_start = last_obj; + xfer->qh_start[xfer->flags_int.curr_dma_set] = last_obj; + if (!xfer->flags_int.curr_dma_set) { + xfer->flags_int.curr_dma_set = 1; + goto alloc_dma_set; + } return; } @@ -3891,10 +3850,22 @@ return; } +static void +ehci_do_dma_delay(struct usbd_bus *bus) +{ + /* + * Wait until the hardware has finished any possible use of + * the transfer descriptor(s) and QH + */ + DELAY(188); + return; +} + struct usbd_bus_methods ehci_bus_methods = { .pipe_init = ehci_pipe_init, .xfer_setup = ehci_xfer_setup, .xfer_unsetup = ehci_xfer_unsetup, .do_poll = ehci_do_poll, + .do_dma_delay = ehci_do_dma_delay, }; ==== //depot/projects/usb/src/sys/dev/usb/ehci.h#26 (text+ko) ==== @@ -113,7 +113,11 @@ #define EHCI_STS_INT 0x00000001 /* RWC interrupt */ #define EHCI_STS_INTRS(x) ((x) & 0x3f) -#define EHCI_NORMAL_INTRS (/* EHCI_STS_IAA | */ EHCI_STS_HSE | \ +/* + * NOTE: the doorbell interrupt is enabled, but the doorbell is never + * used! SiS chipsets require this. + */ +#define EHCI_NORMAL_INTRS (EHCI_STS_IAA | EHCI_STS_HSE | \ EHCI_STS_PCD | EHCI_STS_ERRINT | EHCI_STS_INT) #define EHCI_USBINTR 0x08 /* RW Interrupt register */ ==== //depot/projects/usb/src/sys/dev/usb/ohci.c#52 (text+ko) ==== @@ -1035,7 +1035,7 @@ if (td) { - ed = xfer->qh_start; + ed = xfer->qh_start[xfer->flags_int.curr_dma_set]; ed->ed_headp = td->td_self; usbd_pc_cpu_flush(ed->page_cache); @@ -1055,13 +1055,15 @@ static uint8_t ohci_check_transfer(struct usbd_xfer *xfer) { - ohci_ed_t *ed = xfer->qh_start; + ohci_ed_t *ed; uint32_t ed_flags; uint32_t ed_headp; uint32_t ed_tailp; DPRINTFN(12, ("xfer=%p checking transfer\n", xfer)); + ed = xfer->qh_start[xfer->flags_int.curr_dma_set]; + usbd_pc_cpu_invalidate(ed->page_cache); ed_flags = le32toh(ed->ed_flags); ed_headp = le32toh(ed->ed_headp); @@ -1441,11 +1443,17 @@ temp.average = xfer->max_usb_frame_size; temp.max_frame_size = xfer->max_frame_size; - xfer->td_transfer_first = xfer->td_start; - xfer->td_transfer_cache = xfer->td_start; + /* toggle the DMA set we are using */ + xfer->flags_int.curr_dma_set ^= 1; + + /* get next DMA set */ + td = xfer->td_start[xfer->flags_int.curr_dma_set]; + + xfer->td_transfer_first = td; + xfer->td_transfer_cache = td; temp.td = NULL; - temp.td_next = xfer->td_start; + temp.td_next = td; temp.setup_alt_next = xfer->flags_int.short_frames_ok; methods = xfer->pipe->methods; @@ -1562,11 +1570,11 @@ if (ohcidebug > 8) { DPRINTF(("nexttog=%d; data before transfer:\n", xfer->pipe->toggle_next)); - ohci_dump_tds(xfer->td_start); + ohci_dump_tds(xfer->td_transfer_first); } #endif - ed = xfer->qh_start; + ed = xfer->qh_start[xfer->flags_int.curr_dma_set]; ed_flags = (OHCI_ED_SET_FA(xfer->address) | OHCI_ED_SET_EN(UE_GET_ADDR(xfer->endpoint)) | @@ -1652,51 +1660,32 @@ struct usbd_pipe_methods *methods = xfer->pipe->methods; ohci_softc_t *sc = xfer->usb_sc; ohci_ed_t *ed; - uint8_t need_delay; mtx_assert(&sc->sc_bus.mtx, MA_OWNED); - need_delay = 0; DPRINTFN(1, ("xfer=%p, pipe=%p, error=%d\n", xfer, xfer->pipe, error)); - for (ed = xfer->qh_start; ed; ed = ed->obj_next) { + ed = xfer->qh_start[xfer->flags_int.curr_dma_set]; + if (ed) { usbd_pc_cpu_invalidate(ed->page_cache); - - if ((!(ed->ed_flags & htole32(OHCI_ED_SKIP))) && - (!(ed->ed_headp & htole32(OHCI_HALTED))) && - ((ed->ed_headp ^ ed->ed_tailp) & htole32(-0x10))) { - need_delay = 1; - } } - if (methods == &ohci_device_bulk_methods) { - OHCI_REMOVE_QH(xfer->qh_start, sc->sc_bulk_p_last); + OHCI_REMOVE_QH(ed, sc->sc_bulk_p_last); } if (methods == &ohci_device_ctrl_methods) { - OHCI_REMOVE_QH(xfer->qh_start, sc->sc_ctrl_p_last); + OHCI_REMOVE_QH(ed, sc->sc_ctrl_p_last); } if (methods == &ohci_device_intr_methods) { - OHCI_REMOVE_QH(xfer->qh_start, sc->sc_intr_p_last[xfer->qh_pos]); + OHCI_REMOVE_QH(ed, sc->sc_intr_p_last[xfer->qh_pos]); } if (methods == &ohci_device_isoc_methods) { - OHCI_REMOVE_QH(xfer->qh_start, sc->sc_isoc_p_last); + OHCI_REMOVE_QH(ed, sc->sc_isoc_p_last); } xfer->td_transfer_first = NULL; xfer->td_transfer_last = NULL; - if ((methods != &ohci_root_ctrl_methods) && - (methods != &ohci_root_intr_methods)) { - - /* - * wait until hardware has finished any possible use of the - * transfer and QH - * - * hardware finishes in 1 millisecond - */ - DELAY(need_delay ? (2 * 1000) : (5)); - } /* dequeue transfer and start next transfer */ usbd_transfer_dequeue(xfer, error); return; @@ -1961,7 +1950,11 @@ plen = xfer->frlengths; - td = xfer->td_start; + /* toggle the DMA set we are using */ + xfer->flags_int.curr_dma_set ^= 1; + + /* get next DMA set */ + td = xfer->td_start[xfer->flags_int.curr_dma_set]; xfer->td_transfer_first = td; @@ -2040,10 +2033,10 @@ #ifdef USB_DEBUG if (ohcidebug > 8) { DPRINTF(("data before transfer:\n")); - ohci_dump_itds(xfer->td_start); + ohci_dump_itds(xfer->td_transfer_first); } #endif - ed = xfer->qh_start; + ed = xfer->qh_start[xfer->flags_int.curr_dma_set]; if (UE_GET_DIR(xfer->endpoint) == UE_DIR_IN) ed_flags = (OHCI_ED_DIR_IN | OHCI_ED_FORMAT_ISO); @@ -2660,6 +2653,8 @@ nqh = 0; } +alloc_dma_set: + if (parm->err) { return; } @@ -2715,7 +2710,7 @@ } } - xfer->td_start = last_obj; + xfer->td_start[xfer->flags_int.curr_dma_set] = last_obj; last_obj = NULL; @@ -2744,8 +2739,12 @@ } } - xfer->qh_start = last_obj; + xfer->qh_start[xfer->flags_int.curr_dma_set] = last_obj; + if (!xfer->flags_int.curr_dma_set) { + xfer->flags_int.curr_dma_set = 1; + goto alloc_dma_set; + } return; } @@ -2808,10 +2807,22 @@ return; } +static void +ohci_do_dma_delay(struct usbd_bus *bus) +{ + /* + * Wait until hardware has finished any possible use of the + * transfer descriptor(s) and QH + */ + DELAY(1125); + return; +} + struct usbd_bus_methods ohci_bus_methods = { .pipe_init = ohci_pipe_init, .xfer_setup = ohci_xfer_setup, .xfer_unsetup = ohci_xfer_unsetup, .do_poll = ohci_do_poll, + .do_dma_delay = ohci_do_dma_delay, }; ==== //depot/projects/usb/src/sys/dev/usb/uhci.c#53 (text+ko) ==== @@ -1019,7 +1019,7 @@ return (last); } -static uint8_t +static void uhci_isoc_done(uhci_softc_t *sc, struct usbd_xfer *xfer) { struct usbd_page_search res; @@ -1028,7 +1028,6 @@ uint32_t offset = 0; uint32_t *plen = xfer->frlengths; uint16_t len = 0; - uint8_t need_delay = 0; uhci_td_t *td = xfer->td_transfer_first; uhci_td_t **pp_last = &sc->sc_isoc_p_last[xfer->qh_pos]; @@ -1056,11 +1055,6 @@ usbd_pc_cpu_invalidate(td->page_cache); status = le32toh(td->td_status); - /* check for active transfers */ - - if (status & UHCI_TD_ACTIVE) { - need_delay = 1; - } len = UHCI_TD_GET_ACTLEN(status); if (len > *plen) { @@ -1091,7 +1085,7 @@ xfer->aframes = xfer->nframes; - return (need_delay); + return; } static usbd_status_t @@ -1277,7 +1271,7 @@ uint32_t td_self; td = xfer->td_transfer_cache; - qh = xfer->qh_start; + qh = xfer->qh_start[xfer->flags_int.curr_dma_set]; td_token = td->obj_next->td_token; td = td->alt_next; @@ -1341,6 +1335,13 @@ usbd_pc_cpu_invalidate(td->page_cache); status = le32toh(td->td_status); + /* check also if the first is complete */ + + td = xfer->td_transfer_first; + + usbd_pc_cpu_invalidate(td->page_cache); + status |= le32toh(td->td_status); + if (!(status & UHCI_TD_ACTIVE)) { uhci_device_done(xfer, USBD_NORMAL_COMPLETION); goto transferred; @@ -1691,11 +1692,16 @@ temp.average = xfer->max_frame_size; temp.max_frame_size = xfer->max_frame_size; - xfer->td_transfer_first = xfer->td_start; - xfer->td_transfer_cache = xfer->td_start; + /* toggle the DMA set we are using */ + xfer->flags_int.curr_dma_set ^= 1; + + /* get next DMA set */ + td = xfer->td_start[xfer->flags_int.curr_dma_set]; + xfer->td_transfer_first = td; + xfer->td_transfer_cache = td; temp.td = NULL; - temp.td_next = xfer->td_start; + temp.td_next = td; temp.setup_alt_next = xfer->flags_int.short_frames_ok; uhci_mem_layout_init(&(temp.ml), xfer); @@ -1823,10 +1829,10 @@ if (uhcidebug > 8) { DPRINTF(("nexttog=%d; data before transfer:\n", xfer->pipe->toggle_next)); - uhci_dump_tds(xfer->td_start); + uhci_dump_tds(xfer->td_transfer_first); } #endif - return (xfer->td_start); + return (xfer->td_transfer_first); } /* NOTE: "done" can be run two times in a row, @@ -1838,45 +1844,38 @@ { struct usbd_pipe_methods *methods = xfer->pipe->methods; uhci_softc_t *sc = xfer->usb_sc; - uhci_td_t *td; uhci_qh_t *qh; - uint8_t need_delay; mtx_assert(&sc->sc_bus.mtx, MA_OWNED); - need_delay = 0; - DPRINTFN(1, ("xfer=%p, pipe=%p, error=%d\n", xfer, xfer->pipe, error)); - for (qh = xfer->qh_start; qh; qh = qh->obj_next) { + qh = xfer->qh_start[xfer->flags_int.curr_dma_set]; + if (qh) { usbd_pc_cpu_invalidate(qh->page_cache); - if (!(qh->qh_e_next & htole32(UHCI_PTR_T))) { - need_delay = 1; - } qh->e_next = 0; qh->qh_e_next = htole32(UHCI_PTR_T); usbd_pc_cpu_flush(qh->page_cache); } - if (xfer->flags_int.bandwidth_reclaimed) { xfer->flags_int.bandwidth_reclaimed = 0; uhci_rem_loop(sc); } if (methods == &uhci_device_bulk_methods) { - UHCI_REMOVE_QH(xfer->qh_start, sc->sc_bulk_p_last); + UHCI_REMOVE_QH(qh, sc->sc_bulk_p_last); } if (methods == &uhci_device_ctrl_methods) { if (xfer->udev->speed == USB_SPEED_LOW) { - UHCI_REMOVE_QH(xfer->qh_start, sc->sc_ls_ctl_p_last); + UHCI_REMOVE_QH(qh, sc->sc_ls_ctl_p_last); } else { - UHCI_REMOVE_QH(xfer->qh_start, sc->sc_fs_ctl_p_last); + UHCI_REMOVE_QH(qh, sc->sc_fs_ctl_p_last); } } if (methods == &uhci_device_intr_methods) { - UHCI_REMOVE_QH(xfer->qh_start, sc->sc_intr_p_last[xfer->qh_pos]); + UHCI_REMOVE_QH(qh, sc->sc_intr_p_last[xfer->qh_pos]); } /* * Only finish isochronous transfers once @@ -1885,46 +1884,11 @@ if (xfer->td_transfer_first && xfer->td_transfer_last) { if (methods == &uhci_device_isoc_methods) { - if (uhci_isoc_done(sc, xfer)) { - need_delay = 1; - } - } - if (need_delay) { - td = xfer->td_transfer_first; - - while (1) { - if (td == NULL) { - panic("%s:%d: out of TD's\n", - __FUNCTION__, __LINE__); - } - usbd_pc_cpu_invalidate(td->page_cache); - - td->td_status &= - htole32(~(UHCI_TD_ACTIVE | UHCI_TD_IOC)); - - usbd_pc_cpu_flush(td->page_cache); - - if (((void *)td) == xfer->td_transfer_last) { - td = NULL; - break; - } - td = td->obj_next; - } + uhci_isoc_done(sc, xfer); } xfer->td_transfer_first = NULL; xfer->td_transfer_last = NULL; } - if ((methods != &uhci_root_ctrl_methods) && - (methods != &uhci_root_intr_methods)) { - - /* - * wait until hardware has finished any possible use of the - * transfer and QH - * - * hardware finishes in 1 millisecond - */ - DELAY(need_delay ? ((3 * 1000) / 2) : UHCI_QH_REMOVE_DELAY); - } /* dequeue transfer and start next transfer */ usbd_transfer_dequeue(xfer, error); return; @@ -1965,7 +1929,7 @@ td = uhci_setup_standard_chain(xfer); /* setup QH */ - qh = xfer->qh_start; + qh = xfer->qh_start[xfer->flags_int.curr_dma_set]; UHCI_APPEND_QH(qh, td, sc->sc_bulk_p_last); uhci_add_loop(sc); @@ -2024,7 +1988,7 @@ td = uhci_setup_standard_chain(xfer); /* setup QH */ - qh = xfer->qh_start; + qh = xfer->qh_start[xfer->flags_int.curr_dma_set]; /* * NOTE: some devices choke on bandwidth- reclamation for control @@ -2121,7 +2085,7 @@ td = uhci_setup_standard_chain(xfer); /* setup QH */ - qh = xfer->qh_start; + qh = xfer->qh_start[xfer->flags_int.curr_dma_set]; /* enter QHs into the controller data structures */ UHCI_APPEND_QH(qh, td, sc->sc_intr_p_last[xfer->qh_pos]); @@ -2152,6 +2116,7 @@ { uhci_td_t *td; uint32_t td_token; + uint8_t ds; td_token = (UE_GET_DIR(xfer->endpoint) == UE_DIR_IN) ? @@ -2162,13 +2127,16 @@ /* initialize all TD's */ - for (td = xfer->td_start; td; td = td->obj_next) { + for (ds = 0; ds != 2; ds++) { + + for (td = xfer->td_start[ds]; td; td = td->obj_next) { - /* mark TD as inactive */ - td->td_status = htole32(UHCI_TD_IOS); - td->td_token = td_token; + /* mark TD as inactive */ >>> TRUNCATED FOR MAIL (1000 lines) <<<