Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 22 Nov 2014 17:26:44 +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: r274863 - head/sys/dev/usb/controller
Message-ID:  <201411221726.sAMHQibd086313@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: hselasky
Date: Sat Nov 22 17:26:43 2014
New Revision: 274863
URL: https://svnweb.freebsd.org/changeset/base/274863

Log:
  Fix the host mode ISOCHRONOUS transfer interval programming in the
  SAF1761 OTG driver. Currently the driver logic is very simple and
  double buffering the USB transactions is not done.  Also you need to
  use an external USB high speed USB HUB for reliable FULL speed
  outgoing ISOCHRONOUS traffic, because the internal one chokes on
  so-called split transfers above 188 bytes.

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

Modified: head/sys/dev/usb/controller/saf1761_otg.c
==============================================================================
--- head/sys/dev/usb/controller/saf1761_otg.c	Sat Nov 22 17:19:39 2014	(r274862)
+++ head/sys/dev/usb/controller/saf1761_otg.c	Sat Nov 22 17:26:43 2014	(r274863)
@@ -756,10 +756,14 @@ saf1761_host_intr_data_rx(struct saf1761
 	SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW7, 0);
 	SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW6, 0);
 
-	temp = (0xFC << td->uframe) & 0xFF;	/* complete split */
+	if (td->dw1_value & SOTG_PTD_DW1_ENABLE_SPLIT) {
+		temp = (0xFC << td->uframe) & 0xFF;	/* complete split */
+	} else {
+		temp = 0;
+	}
 	SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW5, temp);
 
-	temp = (1U << td->uframe);		/* start split */
+	temp = (1U << td->uframe);		/* start mask or start split */
 	SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW4, temp);
 
 	temp = SOTG_PTD_DW3_ACTIVE | (td->toggle << 25) | SOTG_PTD_DW3_CERR_3;
@@ -846,10 +850,14 @@ saf1761_host_intr_data_tx(struct saf1761
 	SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW7, 0);
 	SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW6, 0);
 
-	temp = (0xFC << td->uframe) & 0xFF;	/* complete split */
+	if (td->dw1_value & SOTG_PTD_DW1_ENABLE_SPLIT) {
+		temp = (0xFC << td->uframe) & 0xFF;	/* complete split */
+	} else {
+		temp = 0;
+	}
 	SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW5, temp);
 
-	temp = (1U << td->uframe);		/* start split */
+	temp = (1U << td->uframe);		/* start mask or start split */
 	SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW4, temp);
 
 	temp = SOTG_PTD_DW3_ACTIVE | (td->toggle << 25) | SOTG_PTD_DW3_CERR_3;
@@ -939,16 +947,21 @@ saf1761_host_isoc_data_rx(struct saf1761
 	SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW7, 0);
 	SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW6, 0);
 
-	temp = (0xFC << td->uframe) & 0xFF;	/* complete split */
+	if (td->dw1_value & SOTG_PTD_DW1_ENABLE_SPLIT) {
+		temp = (0xFC << (td->uframe & 7)) & 0xFF;	/* complete split */
+	} else {
+		temp = 0;
+	}
 	SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW5, temp);
 
-	temp = (1U << td->uframe);		/* start split */
+	temp = (1U << (td->uframe & 7));	/* start mask or start split */
 	SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW4, temp);
 
 	temp = SOTG_PTD_DW3_ACTIVE | SOTG_PTD_DW3_CERR_3;
 	SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW3, temp);
 
-	temp = (SOTG_HC_MEMORY_ADDR(SOTG_DATA_ADDR(td->channel)) << 8);
+	temp = (SOTG_HC_MEMORY_ADDR(SOTG_DATA_ADDR(td->channel)) << 8) |
+	    (td->uframe & 0xF8);
 	SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW2, temp);
 
 	temp = td->dw1_value | (1 << 10) /* IN-PID */ | (td->ep_index >> 1);
@@ -991,7 +1004,6 @@ saf1761_host_isoc_data_tx(struct saf1761
 		} else if (status & SOTG_PTD_DW3_HALTED) {
 			goto complete;
 		}
-
 		goto complete;
 	}
 	if (saf1761_host_channel_alloc(sc, td))
@@ -1014,13 +1026,14 @@ saf1761_host_isoc_data_tx(struct saf1761
 	SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW6, 0);
 	SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW5, 0);
 
-	temp = (1U << td->uframe);		/* start split */
+	temp = (1U << (td->uframe & 7));	/* start mask or start split */
 	SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW4, temp);
 
 	temp = SOTG_PTD_DW3_ACTIVE | SOTG_PTD_DW3_CERR_3;
 	SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW3, temp);
 
-	temp = (SOTG_HC_MEMORY_ADDR(SOTG_DATA_ADDR(td->channel)) << 8);
+	temp = (SOTG_HC_MEMORY_ADDR(SOTG_DATA_ADDR(td->channel)) << 8) |
+	    (td->uframe & 0xF8);
 	SAF1761_WRITE_LE_4(sc, pdt_addr + SOTG_PTD_DW2, temp);
 
 	temp = td->dw1_value | (0 << 10) /* OUT-PID */ | (td->ep_index >> 1);
@@ -1685,6 +1698,8 @@ saf1761_otg_setup_standard_chain(struct 
 	uint8_t ep_type;
 	uint8_t need_sync;
 	uint8_t is_host;
+	uint8_t uframe_start;
+	uint8_t uframe_interval;
 
 	DPRINTFN(9, "addr=%d endpt=%d sumlen=%d speed=%d\n",
 	    xfer->address, UE_GET_ADDR(xfer->endpointno),
@@ -1738,15 +1753,25 @@ saf1761_otg_setup_standard_chain(struct 
 		x = 0;
 	}
 
+	uframe_start = 0;
+	uframe_interval = 0;
+
 	if (x != xfer->nframes) {
 		if (xfer->endpointno & UE_DIR_IN) {
 			if (is_host) {
-				if (ep_type == UE_INTERRUPT)
+				if (ep_type == UE_INTERRUPT) {
 					temp.func = &saf1761_host_intr_data_rx;
-				else if (ep_type == UE_ISOCHRONOUS)
+				} else if (ep_type == UE_ISOCHRONOUS) {
 					temp.func = &saf1761_host_isoc_data_rx;
-				else
+					uframe_start = (SAF1761_READ_LE_4(sc, SOTG_FRINDEX) + 8) &
+					    (SOTG_FRINDEX_MASK & ~7);
+					if (xfer->xroot->udev->speed == USB_SPEED_HIGH)
+						uframe_interval = 1U << xfer->fps_shift;
+					else
+						uframe_interval = 8U;
+				} else {
 					temp.func = &saf1761_host_bulk_data_rx;
+				}
 				need_sync = 0;
 			} else {
 				temp.func = &saf1761_device_data_tx;
@@ -1754,12 +1779,19 @@ saf1761_otg_setup_standard_chain(struct 
 			}
 		} else {
 			if (is_host) {
-				if (ep_type == UE_INTERRUPT)
+				if (ep_type == UE_INTERRUPT) {
 					temp.func = &saf1761_host_intr_data_tx;
-				else if (ep_type == UE_ISOCHRONOUS)
+				} else if (ep_type == UE_ISOCHRONOUS) {
 					temp.func = &saf1761_host_isoc_data_tx;
-				else
+					uframe_start = (SAF1761_READ_LE_4(sc, SOTG_FRINDEX) + 8) &
+					    (SOTG_FRINDEX_MASK & ~7);
+					if (xfer->xroot->udev->speed == USB_SPEED_HIGH)
+						uframe_interval = 1U << xfer->fps_shift;
+					else
+						uframe_interval = 8U;
+				} else {
 					temp.func = &saf1761_host_bulk_data_tx;
+				}
 				need_sync = 0;
 			} else {
 				temp.func = &saf1761_device_data_rx;
@@ -1807,6 +1839,12 @@ saf1761_otg_setup_standard_chain(struct 
 
 		if (xfer->flags_int.isochronous_xfr) {
 			temp.offset += temp.len;
+
+			/* stamp the starting point for this transaction */
+			temp.td->uframe = uframe_start;
+
+			/* advance to next */
+			uframe_start += uframe_interval;
 		} else {
 			/* get next Page Cache pointer */
 			temp.pc = xfer->frbuffers + x;
@@ -3404,17 +3442,8 @@ saf1761_otg_xfer_setup(struct usb_setup_
 			td->ep_index = ep_no;
 			td->ep_type = ep_type;
 			td->dw1_value = dw1;
-			if (ep_type == UE_ISOCHRONOUS) {
-				if (parm->udev->speed == USB_SPEED_HIGH) {
-					uint8_t uframe_index = (ntd - 1 - n);
-					uframe_index <<= usbd_xfer_get_fps_shift(xfer);
-					td->uframe = (uframe_index & 7);
-				} else {
-					td->uframe = 0;
-				}
-			} else {
-				td->uframe = 0;
-			}
+			td->uframe = 0;
+
 			if (ep_type == UE_INTERRUPT) {
 				if (xfer->interval > 32)
 					td->interval = (32 / 2) << 3;



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