Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 3 Dec 2014 21:48:31 +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: r275467 - head/sys/dev/usb/controller
Message-ID:  <201412032148.sB3LmVNt038217@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: hselasky
Date: Wed Dec  3 21:48:30 2014
New Revision: 275467
URL: https://svnweb.freebsd.org/changeset/base/275467

Log:
  Workaround for possible bug in the SAF1761 chip. Wait 125us before
  re-using a hardware propritary transfer descriptor, PTD, in USB host
  mode. If the PTD's are recycled too quickly, it has been observed that
  the hardware simply fails to schedule the requested job or resets
  completely disconnecting all devices.

Modified:
  head/sys/dev/usb/controller/saf1761_otg.c
  head/sys/dev/usb/controller/saf1761_otg.h

Modified: head/sys/dev/usb/controller/saf1761_otg.c
==============================================================================
--- head/sys/dev/usb/controller/saf1761_otg.c	Wed Dec  3 19:44:24 2014	(r275466)
+++ head/sys/dev/usb/controller/saf1761_otg.c	Wed Dec  3 21:48:30 2014	(r275467)
@@ -130,6 +130,7 @@ static void saf1761_otg_do_poll(struct u
 static void saf1761_otg_standard_done(struct usb_xfer *);
 static void saf1761_otg_intr_set(struct usb_xfer *, uint8_t);
 static void saf1761_otg_root_intr(struct saf1761_otg_softc *);
+static void saf1761_otg_enable_psof(struct saf1761_otg_softc *, uint8_t);
 
 /*
  * Here is a list of what the SAF1761 chip can support. The main
@@ -214,6 +215,7 @@ saf1761_otg_wakeup_peer(struct saf1761_o
 static uint8_t
 saf1761_host_channel_alloc(struct saf1761_otg_softc *sc, struct saf1761_otg_td *td)
 {
+	uint32_t map;
 	uint32_t x;
 
 	if (td->channel < SOTG_HOST_CHANNEL_MAX)
@@ -225,8 +227,11 @@ saf1761_host_channel_alloc(struct saf176
 
 	switch (td->ep_type) {
 	case UE_INTERRUPT:
+		map = sc->sc_host_intr_map |
+		    sc->sc_host_intr_busy_map[0] |
+		    sc->sc_host_intr_busy_map[1];
 		for (x = 0; x != 32; x++) {
-			if (sc->sc_host_intr_map & (1 << x))
+			if (map & (1 << x))
 				continue;
 			sc->sc_host_intr_map |= (1 << x);
 			td->channel = 32 + x;
@@ -234,8 +239,11 @@ saf1761_host_channel_alloc(struct saf176
 		}
 		break;
 	case UE_ISOCHRONOUS:
+		map = sc->sc_host_isoc_map |
+		    sc->sc_host_isoc_busy_map[0] |
+		    sc->sc_host_isoc_busy_map[1];
 		for (x = 0; x != 32; x++) {
-			if (sc->sc_host_isoc_map & (1 << x))
+			if (map & (1 << x))
 				continue;
 			sc->sc_host_isoc_map |= (1 << x);
 			td->channel = x;
@@ -243,8 +251,11 @@ saf1761_host_channel_alloc(struct saf176
 		}
 		break;
 	default:
+		map = sc->sc_host_async_map |
+		    sc->sc_host_async_busy_map[0] |
+		    sc->sc_host_async_busy_map[1];
 		for (x = 0; x != 32; x++) {
-			if (sc->sc_host_async_map & (1 << x))
+			if (map & (1 << x))
 				continue;
 			sc->sc_host_async_map |= (1 << x);
 			td->channel = 64 + x;
@@ -269,6 +280,7 @@ saf1761_host_channel_free(struct saf1761
 		td->channel = SOTG_HOST_CHANNEL_MAX;
 		sc->sc_host_intr_map &= ~(1 << x);
 		sc->sc_host_intr_suspend_map &= ~(1 << x);
+		sc->sc_host_intr_busy_map[0] |= (1 << x);
 		SAF1761_WRITE_LE_4(sc, SOTG_INT_PTD_SKIP_PTD,
 		    (~sc->sc_host_intr_map) | sc->sc_host_intr_suspend_map);
 		break;
@@ -277,6 +289,7 @@ saf1761_host_channel_free(struct saf1761
 		td->channel = SOTG_HOST_CHANNEL_MAX;
 		sc->sc_host_isoc_map &= ~(1 << x);
 		sc->sc_host_isoc_suspend_map &= ~(1 << x);
+		sc->sc_host_isoc_busy_map[0] |= (1 << x);
 		SAF1761_WRITE_LE_4(sc, SOTG_ISO_PTD_SKIP_PTD,
 		    (~sc->sc_host_isoc_map) | sc->sc_host_isoc_suspend_map);
 		break;
@@ -285,10 +298,12 @@ saf1761_host_channel_free(struct saf1761
 		td->channel = SOTG_HOST_CHANNEL_MAX;
 		sc->sc_host_async_map &= ~(1 << x);
 		sc->sc_host_async_suspend_map &= ~(1 << x);
+		sc->sc_host_async_busy_map[0] |= (1 << x);
 		SAF1761_WRITE_LE_4(sc, SOTG_ATL_PTD_SKIP_PTD,
 		    (~sc->sc_host_async_map) | sc->sc_host_async_suspend_map);
 		break;
 	}
+	saf1761_otg_enable_psof(sc, 1);
 }
 
 static uint32_t
@@ -1484,6 +1499,17 @@ saf1761_otg_interrupt_poll_locked(struct
 }
 
 static void
+saf1761_otg_enable_psof(struct saf1761_otg_softc *sc, uint8_t on)
+{
+	if (on) {
+		sc->sc_intr_enable |= SOTG_DCINTERRUPT_IEPSOF;
+	} else {
+		sc->sc_intr_enable &= ~SOTG_DCINTERRUPT_IEPSOF;
+	}
+	SAF1761_WRITE_LE_4(sc, SOTG_DCINTERRUPT_EN, sc->sc_intr_enable);
+}
+
+static void
 saf1761_otg_wait_suspend(struct saf1761_otg_softc *sc, uint8_t on)
 {
 	if (on) {
@@ -1565,6 +1591,27 @@ saf1761_otg_filter_interrupt(void *arg)
 	(void) SAF1761_READ_LE_4(sc, SOTG_INT_PTD_DONE_PTD);
 	(void) SAF1761_READ_LE_4(sc, SOTG_ISO_PTD_DONE_PTD);
 
+	if (status & SOTG_DCINTERRUPT_IEPSOF) {
+		if ((sc->sc_host_async_busy_map[1] | sc->sc_host_async_busy_map[0] |
+		     sc->sc_host_intr_busy_map[1] | sc->sc_host_intr_busy_map[0] |
+		     sc->sc_host_isoc_busy_map[1] | sc->sc_host_isoc_busy_map[0]) != 0) {
+			/* busy waiting is active */
+			retval = FILTER_SCHEDULE_THREAD;
+
+			sc->sc_host_async_busy_map[1] = sc->sc_host_async_busy_map[0];
+			sc->sc_host_async_busy_map[0] = 0;
+
+			sc->sc_host_intr_busy_map[1] = sc->sc_host_intr_busy_map[0];
+			sc->sc_host_intr_busy_map[0] = 0;
+
+			sc->sc_host_isoc_busy_map[1] = sc->sc_host_isoc_busy_map[0];
+			sc->sc_host_isoc_busy_map[0] = 0;
+		} else {
+			/* busy waiting is not active */
+			saf1761_otg_enable_psof(sc, 0);
+		}
+	}
+
 	if (status & SAF1761_DCINTERRUPT_THREAD_IRQ)
 		retval = FILTER_SCHEDULE_THREAD;
 

Modified: head/sys/dev/usb/controller/saf1761_otg.h
==============================================================================
--- head/sys/dev/usb/controller/saf1761_otg.h	Wed Dec  3 19:44:24 2014	(r275466)
+++ head/sys/dev/usb/controller/saf1761_otg.h	Wed Dec  3 21:48:30 2014	(r275467)
@@ -139,10 +139,13 @@ struct saf1761_otg_softc {
 	bus_space_tag_t sc_io_tag;
 	bus_space_handle_t sc_io_hdl;
 
+	uint32_t sc_host_async_busy_map[2];
 	uint32_t sc_host_async_map;
 	uint32_t sc_host_async_suspend_map;
+	uint32_t sc_host_intr_busy_map[2];
 	uint32_t sc_host_intr_map;
 	uint32_t sc_host_intr_suspend_map;
+	uint32_t sc_host_isoc_busy_map[2];
 	uint32_t sc_host_isoc_map;
 	uint32_t sc_host_isoc_suspend_map;
 	uint32_t sc_intr_enable;	/* enabled interrupts */



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201412032148.sB3LmVNt038217>