summaryrefslogtreecommitdiffstats
path: root/sys/dev/usb/controller/xhci.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/usb/controller/xhci.c')
-rw-r--r--sys/dev/usb/controller/xhci.c46
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)
OpenPOWER on IntegriCloud