Improve the XHCI command timeout recovery handling code.
MFC after: 1 week
This commit is contained in:
parent
681c6ef3ec
commit
04d9725876
@ -295,6 +295,28 @@ usb_resume(device_t dev)
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*
|
||||
* 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(USB_BUS_EXPLORE_PROC(bus),
|
||||
&bus->reset_msg[0], &bus->reset_msg[1]);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*
|
||||
* usb_shutdown
|
||||
*------------------------------------------------------------------------*/
|
||||
@ -429,6 +451,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];
|
||||
|
||||
@ -484,6 +508,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];
|
||||
|
||||
@ -532,6 +558,28 @@ usb_bus_resume(struct usb_proc_msg *pm)
|
||||
USB_BUS_LOCK(bus);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*
|
||||
* usb_bus_reset
|
||||
*
|
||||
* This function is used to reset the USB contoller.
|
||||
*------------------------------------------------------------------------*/
|
||||
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
|
||||
*
|
||||
@ -750,6 +798,11 @@ usb_attach_sub(device_t dev, struct usb_bus *bus)
|
||||
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;
|
||||
|
@ -278,6 +278,69 @@ xhci_ctx_get_le64(struct xhci_softc *sc, volatile uint64_t *ptr)
|
||||
}
|
||||
#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)
|
||||
{
|
||||
@ -1059,6 +1122,7 @@ xhci_do_command(struct xhci_softc *sc, struct xhci_trb *trb,
|
||||
uint32_t temp;
|
||||
uint8_t i;
|
||||
uint8_t j;
|
||||
uint8_t timeout = 0;
|
||||
int err;
|
||||
|
||||
XHCI_CMD_ASSERT_LOCKED(sc);
|
||||
@ -1072,7 +1136,7 @@ xhci_do_command(struct xhci_softc *sc, struct xhci_trb *trb,
|
||||
/* Queue command */
|
||||
|
||||
USB_BUS_LOCK(&sc->sc_bus);
|
||||
|
||||
retry:
|
||||
i = sc->sc_command_idx;
|
||||
j = sc->sc_command_ccs;
|
||||
|
||||
@ -1143,25 +1207,22 @@ xhci_do_command(struct xhci_softc *sc, struct xhci_trb *trb,
|
||||
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;
|
||||
|
@ -81,6 +81,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:
|
||||
|
@ -191,6 +191,7 @@ void usb_bus_mem_flush_all(struct usb_bus *bus, usb_bus_mem_cb_t *cb);
|
||||
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
|
||||
|
Loading…
Reference in New Issue
Block a user