Date: Thu, 19 Dec 2013 07:12:40 +0000 (UTC) From: Hans Petter Selasky <hselasky@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-9@freebsd.org Subject: svn commit: r259602 - in stable/9/sys/dev/usb: . controller Message-ID: <201312190712.rBJ7Ce2R095609@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: hselasky Date: Thu Dec 19 07:12:40 2013 New Revision: 259602 URL: http://svnweb.freebsd.org/changeset/base/259602 Log: MFC r259023 and r259095: Improve the XHCI command timeout recovery handling code. Fix some typos while at it. Modified: stable/9/sys/dev/usb/controller/usb_controller.c stable/9/sys/dev/usb/controller/xhci.c stable/9/sys/dev/usb/usb_bus.h stable/9/sys/dev/usb/usb_controller.h Directory Properties: stable/9/sys/ (props changed) stable/9/sys/dev/ (props changed) Modified: stable/9/sys/dev/usb/controller/usb_controller.c ============================================================================== --- stable/9/sys/dev/usb/controller/usb_controller.c Thu Dec 19 07:12:34 2013 (r259601) +++ stable/9/sys/dev/usb/controller/usb_controller.c Thu Dec 19 07:12:40 2013 (r259602) @@ -278,6 +278,28 @@ usb_resume(device_t dev) } /*------------------------------------------------------------------------* + * usb_bus_reset_async_locked + *------------------------------------------------------------------------*/ +void +usb_bus_reset_async_locked(struct usb_bus *bus) +{ + USB_BUS_LOCK_ASSERT(bus, MA_OWNED); + + DPRINTF("\n"); + + if (bus->reset_msg[0].hdr.pm_qentry.tqe_prev != NULL || + bus->reset_msg[1].hdr.pm_qentry.tqe_prev != NULL) { + DPRINTF("Reset already pending\n"); + return; + } + + device_printf(bus->parent, "Resetting controller\n"); + + usb_proc_msignal(&bus->explore_proc, + &bus->reset_msg[0], &bus->reset_msg[1]); +} + +/*------------------------------------------------------------------------* * usb_shutdown *------------------------------------------------------------------------*/ static int @@ -399,7 +421,7 @@ usb_bus_detach(struct usb_proc_msg *pm) /*------------------------------------------------------------------------* * usb_bus_suspend * - * This function is used to suspend the USB contoller. + * This function is used to suspend the USB controller. *------------------------------------------------------------------------*/ static void usb_bus_suspend(struct usb_proc_msg *pm) @@ -409,6 +431,8 @@ usb_bus_suspend(struct usb_proc_msg *pm) usb_error_t err; uint8_t do_unlock; + DPRINTF("\n"); + bus = ((struct usb_bus_msg *)pm)->bus; udev = bus->devices[USB_ROOT_HUB_ADDR]; @@ -454,7 +478,7 @@ usb_bus_suspend(struct usb_proc_msg *pm) /*------------------------------------------------------------------------* * usb_bus_resume * - * This function is used to resume the USB contoller. + * This function is used to resume the USB controller. *------------------------------------------------------------------------*/ static void usb_bus_resume(struct usb_proc_msg *pm) @@ -464,6 +488,8 @@ usb_bus_resume(struct usb_proc_msg *pm) usb_error_t err; uint8_t do_unlock; + DPRINTF("\n"); + bus = ((struct usb_bus_msg *)pm)->bus; udev = bus->devices[USB_ROOT_HUB_ADDR]; @@ -513,9 +539,31 @@ usb_bus_resume(struct usb_proc_msg *pm) } /*------------------------------------------------------------------------* + * usb_bus_reset + * + * This function is used to reset the USB controller. + *------------------------------------------------------------------------*/ +static void +usb_bus_reset(struct usb_proc_msg *pm) +{ + struct usb_bus *bus; + + DPRINTF("\n"); + + bus = ((struct usb_bus_msg *)pm)->bus; + + if (bus->bdev == NULL || bus->no_explore != 0) + return; + + /* a suspend and resume will reset the USB controller */ + usb_bus_suspend(pm); + usb_bus_resume(pm); +} + +/*------------------------------------------------------------------------* * usb_bus_shutdown * - * This function is used to shutdown the USB contoller. + * This function is used to shutdown the USB controller. *------------------------------------------------------------------------*/ static void usb_bus_shutdown(struct usb_proc_msg *pm) @@ -728,6 +776,11 @@ usb_attach_sub(device_t dev, struct usb_ bus->resume_msg[1].hdr.pm_callback = &usb_bus_resume; bus->resume_msg[1].bus = bus; + bus->reset_msg[0].hdr.pm_callback = &usb_bus_reset; + bus->reset_msg[0].bus = bus; + bus->reset_msg[1].hdr.pm_callback = &usb_bus_reset; + bus->reset_msg[1].bus = bus; + bus->shutdown_msg[0].hdr.pm_callback = &usb_bus_shutdown; bus->shutdown_msg[0].bus = bus; bus->shutdown_msg[1].hdr.pm_callback = &usb_bus_shutdown; Modified: stable/9/sys/dev/usb/controller/xhci.c ============================================================================== --- stable/9/sys/dev/usb/controller/xhci.c Thu Dec 19 07:12:34 2013 (r259601) +++ stable/9/sys/dev/usb/controller/xhci.c Thu Dec 19 07:12:40 2013 (r259602) @@ -253,6 +253,69 @@ xhci_ctx_get_le64(struct xhci_softc *sc, } #endif +static int +xhci_reset_command_queue_locked(struct xhci_softc *sc) +{ + struct usb_page_search buf_res; + struct xhci_hw_root *phwr; + uint64_t addr; + uint32_t temp; + + DPRINTF("\n"); + + temp = XREAD4(sc, oper, XHCI_CRCR_LO); + if (temp & XHCI_CRCR_LO_CRR) { + DPRINTF("Command ring running\n"); + temp &= ~(XHCI_CRCR_LO_CS | XHCI_CRCR_LO_CA); + + /* + * Try to abort the last command as per section + * 4.6.1.2 "Aborting a Command" of the XHCI + * specification: + */ + + /* stop and cancel */ + XWRITE4(sc, oper, XHCI_CRCR_LO, temp | XHCI_CRCR_LO_CS); + XWRITE4(sc, oper, XHCI_CRCR_HI, 0); + + XWRITE4(sc, oper, XHCI_CRCR_LO, temp | XHCI_CRCR_LO_CA); + XWRITE4(sc, oper, XHCI_CRCR_HI, 0); + + /* wait 250ms */ + usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 4); + + /* check if command ring is still running */ + temp = XREAD4(sc, oper, XHCI_CRCR_LO); + if (temp & XHCI_CRCR_LO_CRR) { + DPRINTF("Comand ring still running\n"); + return (USB_ERR_IOERROR); + } + } + + /* reset command ring */ + sc->sc_command_ccs = 1; + sc->sc_command_idx = 0; + + usbd_get_page(&sc->sc_hw.root_pc, 0, &buf_res); + + /* setup command ring control base address */ + addr = buf_res.physaddr; + phwr = buf_res.buffer; + addr += (uintptr_t)&((struct xhci_hw_root *)0)->hwr_commands[0]; + + DPRINTF("CRCR=0x%016llx\n", (unsigned long long)addr); + + memset(phwr->hwr_commands, 0, sizeof(phwr->hwr_commands)); + phwr->hwr_commands[XHCI_MAX_COMMANDS - 1].qwTrb0 = htole64(addr); + + usb_pc_cpu_flush(&sc->sc_hw.root_pc); + + XWRITE4(sc, oper, XHCI_CRCR_LO, ((uint32_t)addr) | XHCI_CRCR_LO_RCS); + XWRITE4(sc, oper, XHCI_CRCR_HI, (uint32_t)(addr >> 32)); + + return (0); +} + usb_error_t xhci_start_controller(struct xhci_softc *sc) { @@ -1025,6 +1088,7 @@ xhci_do_command(struct xhci_softc *sc, s uint32_t temp; uint8_t i; uint8_t j; + uint8_t timeout = 0; int err; XHCI_CMD_ASSERT_LOCKED(sc); @@ -1038,7 +1102,7 @@ xhci_do_command(struct xhci_softc *sc, s /* Queue command */ USB_BUS_LOCK(&sc->sc_bus); - +retry: i = sc->sc_command_idx; j = sc->sc_command_ccs; @@ -1109,25 +1173,22 @@ xhci_do_command(struct xhci_softc *sc, s err = 0; } if (err != 0) { - DPRINTFN(0, "Command timeout!\n"); - + DPRINTF("Command timeout!\n"); /* - * Try to abort the last command as per section - * 4.6.1.2 "Aborting a Command" of the XHCI - * specification: + * After some weeks of continuous operation, it has + * been observed that the ASMedia Technology, ASM1042 + * SuperSpeed USB Host Controller can suddenly stop + * accepting commands via the command queue. Try to + * first reset the command queue. If that fails do a + * host controller reset. */ - temp = XREAD4(sc, oper, XHCI_CRCR_LO); - XWRITE4(sc, oper, XHCI_CRCR_LO, temp | XHCI_CRCR_LO_CA); - - /* wait for abort event, if any */ - err = cv_timedwait(&sc->sc_cmd_cv, &sc->sc_bus.bus_mtx, hz / 16); - - if (err != 0 && xhci_interrupt_poll(sc) != 0) { - DPRINTF("Command was completed when polling\n"); - err = 0; - } - if (err != 0) { - DPRINTF("Command abort timeout!\n"); + if (timeout == 0 && + xhci_reset_command_queue_locked(sc) == 0) { + timeout = 1; + goto retry; + } else { + DPRINTF("Controller reset!\n"); + usb_bus_reset_async_locked(&sc->sc_bus); } err = USB_ERR_TIMEOUT; trb->dwTrb2 = 0; Modified: stable/9/sys/dev/usb/usb_bus.h ============================================================================== --- stable/9/sys/dev/usb/usb_bus.h Thu Dec 19 07:12:34 2013 (r259601) +++ stable/9/sys/dev/usb/usb_bus.h Thu Dec 19 07:12:40 2013 (r259602) @@ -71,6 +71,7 @@ struct usb_bus { struct usb_bus_msg attach_msg[2]; struct usb_bus_msg suspend_msg[2]; struct usb_bus_msg resume_msg[2]; + struct usb_bus_msg reset_msg[2]; struct usb_bus_msg shutdown_msg[2]; /* * This mutex protects the USB hardware: Modified: stable/9/sys/dev/usb/usb_controller.h ============================================================================== --- stable/9/sys/dev/usb/usb_controller.h Thu Dec 19 07:12:34 2013 (r259601) +++ stable/9/sys/dev/usb/usb_controller.h Thu Dec 19 07:12:40 2013 (r259602) @@ -186,6 +186,7 @@ void usb_bus_mem_flush_all(struct usb_bu uint8_t usb_bus_mem_alloc_all(struct usb_bus *bus, bus_dma_tag_t dmat, usb_bus_mem_cb_t *cb); void usb_bus_mem_free_all(struct usb_bus *bus, usb_bus_mem_cb_t *cb); uint16_t usb_isoc_time_expand(struct usb_bus *bus, uint16_t isoc_time_curr); +void usb_bus_reset_async_locked(struct usb_bus *bus); #if USB_HAVE_TT_SUPPORT uint8_t usbd_fs_isoc_schedule_alloc_slot(struct usb_xfer *isoc_xfer, uint16_t isoc_time); #endif
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201312190712.rBJ7Ce2R095609>