summaryrefslogtreecommitdiffstats
path: root/sys/dev/usb/controller/xhci.c
diff options
context:
space:
mode:
authorhselasky <hselasky@FreeBSD.org>2013-09-21 21:40:57 +0000
committerhselasky <hselasky@FreeBSD.org>2013-09-21 21:40:57 +0000
commiteaecb3d1ae60457bac557fd719cacb819f801159 (patch)
tree7a266a6213a92c1ea75f5a60a67c666060a37f55 /sys/dev/usb/controller/xhci.c
parentcda41f674dcdd03b86d4e53cd0dbd08b534f9e7a (diff)
downloadFreeBSD-src-eaecb3d1ae60457bac557fd719cacb819f801159.zip
FreeBSD-src-eaecb3d1ae60457bac557fd719cacb819f801159.tar.gz
Stability fixes for Intel LynxPoint XHCI controllers. Disable XHCI port
routing if we get certain errors. Poll for command completion upon command timeouts. The XHCI error events might not generate interrupts. MFC after: 1 week Reported by: Daniel Gerzo <danger@rulez.sk>, Antonis Anastasiadis <anastasiadis@datalive.gr> PR: usb/181159 Approved by: re (gjb)
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