Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 6 Dec 2013 08:42:42 +0000 (UTC)
From:      Hans Petter Selasky <hselasky@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r259023 - in head/sys/dev/usb: . controller
Message-ID:  <201312060842.rB68ggWj055854@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: hselasky
Date: Fri Dec  6 08:42:41 2013
New Revision: 259023
URL: http://svnweb.freebsd.org/changeset/base/259023

Log:
  Improve the XHCI command timeout recovery handling code.
  
  MFC after:	1 week

Modified:
  head/sys/dev/usb/controller/usb_controller.c
  head/sys/dev/usb/controller/xhci.c
  head/sys/dev/usb/usb_bus.h
  head/sys/dev/usb/usb_controller.h

Modified: head/sys/dev/usb/controller/usb_controller.c
==============================================================================
--- head/sys/dev/usb/controller/usb_controller.c	Fri Dec  6 02:19:58 2013	(r259022)
+++ head/sys/dev/usb/controller/usb_controller.c	Fri Dec  6 08:42:41 2013	(r259023)
@@ -296,6 +296,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(USB_BUS_EXPLORE_PROC(bus),
+	    &bus->reset_msg[0], &bus->reset_msg[1]);
+}
+
+/*------------------------------------------------------------------------*
  *	usb_shutdown
  *------------------------------------------------------------------------*/
 static int
@@ -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];
 
@@ -533,6 +559,28 @@ usb_bus_resume(struct usb_proc_msg *pm)
 }
 
 /*------------------------------------------------------------------------*
+ *	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
  *
  * This function is used to shutdown the USB contoller.
@@ -750,6 +798,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: head/sys/dev/usb/controller/xhci.c
==============================================================================
--- head/sys/dev/usb/controller/xhci.c	Fri Dec  6 02:19:58 2013	(r259022)
+++ head/sys/dev/usb/controller/xhci.c	Fri Dec  6 08:42:41 2013	(r259023)
@@ -278,6 +278,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)
 {
@@ -1059,6 +1122,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);
@@ -1072,7 +1136,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;
 
@@ -1143,25 +1207,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: head/sys/dev/usb/usb_bus.h
==============================================================================
--- head/sys/dev/usb/usb_bus.h	Fri Dec  6 02:19:58 2013	(r259022)
+++ head/sys/dev/usb/usb_bus.h	Fri Dec  6 08:42:41 2013	(r259023)
@@ -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:

Modified: head/sys/dev/usb/usb_controller.h
==============================================================================
--- head/sys/dev/usb/usb_controller.h	Fri Dec  6 02:19:58 2013	(r259022)
+++ head/sys/dev/usb/usb_controller.h	Fri Dec  6 08:42:41 2013	(r259023)
@@ -191,6 +191,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?201312060842.rB68ggWj055854>