diff options
Diffstat (limited to 'sys/dev/usb/controller/xhci.c')
-rw-r--r-- | sys/dev/usb/controller/xhci.c | 46 |
1 files changed, 32 insertions, 14 deletions
diff --git a/sys/dev/usb/controller/xhci.c b/sys/dev/usb/controller/xhci.c index ec8b75a..0f8470e 100644 --- a/sys/dev/usb/controller/xhci.c +++ b/sys/dev/usb/controller/xhci.c @@ -108,6 +108,8 @@ TUNABLE_INT("hw.usb.xhci.xhci_port_route", &xhciroute); SYSCTL_INT(_hw_usb_xhci, OID_AUTO, use_polling, CTLFLAG_RW | CTLFLAG_TUN, &xhcipolling, 0, "Set to enable software interrupt polling for XHCI controller"); TUNABLE_INT("hw.usb.xhci.use_polling", &xhcipolling); +#else +#define xhciroute 0 #endif #define XHCI_INTR_ENDPT 1 @@ -194,16 +196,6 @@ xhci_dump_device(struct xhci_softc *sc, struct xhci_slot_ctx *psl) } #endif -uint32_t -xhci_get_port_route(void) -{ -#ifdef USB_DEBUG - return (0xFFFFFFFFU ^ ((uint32_t)xhciroute)); -#else - return (0xFFFFFFFFU); -#endif -} - uint8_t xhci_use_polling(void) { @@ -505,6 +497,11 @@ xhci_start_controller(struct xhci_softc *sc) /* catch any lost interrupts */ xhci_do_poll(&sc->sc_bus); + if (sc->sc_port_route != NULL) { + /* Route all ports to the XHCI by default */ + sc->sc_port_route(sc->sc_bus.parent, + ~xhciroute, xhciroute); + } return (0); } @@ -951,7 +948,7 @@ xhci_check_transfer(struct xhci_softc *sc, struct xhci_trb *trb) } } -static void +static int xhci_check_command(struct xhci_softc *sc, struct xhci_trb *trb) { if (sc->sc_cmd_addr == trb->qwTrb0) { @@ -959,16 +956,19 @@ xhci_check_command(struct xhci_softc *sc, struct xhci_trb *trb) sc->sc_cmd_result[0] = trb->dwTrb2; sc->sc_cmd_result[1] = trb->dwTrb3; cv_signal(&sc->sc_cmd_cv); + return (1); /* command match */ } + return (0); } -static void +static int xhci_interrupt_poll(struct xhci_softc *sc) { struct usb_page_search buf_res; struct xhci_hw_root *phwr; uint64_t addr; uint32_t temp; + int retval = 0; uint16_t i; uint8_t event; uint8_t j; @@ -1008,7 +1008,7 @@ xhci_interrupt_poll(struct xhci_softc *sc) xhci_check_transfer(sc, &phwr->hwr_events[i]); break; case XHCI_TRB_EVENT_CMD_COMPLETE: - xhci_check_command(sc, &phwr->hwr_events[i]); + retval |= xhci_check_command(sc, &phwr->hwr_events[i]); break; default: DPRINTF("Unhandled event = %u\n", event); @@ -1045,6 +1045,8 @@ xhci_interrupt_poll(struct xhci_softc *sc) XWRITE4(sc, runt, XHCI_ERDP_LO(0), (uint32_t)addr); XWRITE4(sc, runt, XHCI_ERDP_HI(0), (uint32_t)(addr >> 32)); + + return (retval); } static usb_error_t @@ -1132,7 +1134,15 @@ xhci_do_command(struct xhci_softc *sc, struct xhci_trb *trb, err = cv_timedwait(&sc->sc_cmd_cv, &sc->sc_bus.bus_mtx, USB_MS_TO_TICKS(timeout_ms)); - if (err) { + /* + * In some error cases event interrupts are not generated. + * Poll one time to see if the command has completed. + */ + if (err != 0 && xhci_interrupt_poll(sc) != 0) { + DPRINTF("Command was completed when polling\n"); + err = 0; + } + if (err != 0) { DPRINTFN(0, "Command timeout!\n"); err = USB_ERR_TIMEOUT; trb->dwTrb2 = 0; @@ -1311,6 +1321,14 @@ xhci_set_address(struct usb_device *udev, struct mtx *mtx, uint16_t address) (address == 0), index); if (err != 0) { + temp = le32toh(sc->sc_cmd_result[0]); + if (address == 0 && sc->sc_port_route != NULL && + XHCI_TRB_2_ERROR_GET(temp) == + XHCI_TRB_ERROR_PARAMETER) { + /* LynxPoint XHCI - ports are not switchable */ + /* Un-route all ports from the XHCI */ + sc->sc_port_route(sc->sc_bus.parent, 0, ~0); + } DPRINTF("Could not set address " "for slot %u.\n", index); if (address != 0) |