All of lore.kernel.org
 help / color / mirror / Atom feed
* [U-Boot] [PATCH 0/6] usb: dwc2: Add support for SPLIT transactions
@ 2016-01-17  3:09 Stefan Brüns
  2016-01-17  3:09 ` [U-Boot] [PATCH 1/6] usb: dwc2: Fix out-of-bounds access, fix chunk size Stefan Brüns
                   ` (7 more replies)
  0 siblings, 8 replies; 10+ messages in thread
From: Stefan Brüns @ 2016-01-17  3:09 UTC (permalink / raw)
  To: u-boot

The first patch fixes an out-of-bounds access, and makes the calculation of
maximum transfer size more straightforward. It also makes overriding the
maximum transfer size easier for split transactions

2nd and 3rd patch cleanup and restructure the current code in preparation
for the split support.

Patch 4 and 5 add the actual support for CONTROL and BULK transactions.

Patch 6 adds support for INTERUPT splits.

The patch series uses the same logic as the earlier patch series, but is
otherwise a rewrite.

With the patch series, all my LS/FS devices enumerate behind the RPi 1
onboard hub and an external hub.

Interrupt transfers have been tested with a Logitech K400 and a noname wired
keyboard. Both work if "stdin" includes usbkbd prior to usb start, and u-boot
has been configured with CONFIG_USB_KEYBOARD=y and CONFIG_SYS_USB_EVENT_POLL=y
(but only one at a time).

Stefan Br?ns (6):
  usb: dwc2: Fix out-of-bounds access, fix chunk size
  usb: dwc2: Simplify wait_for_chhltd(), remove ignore_ack
  usb: dwc2: split transfer core from outer loop
  usb: dwc2: add helper function for setting SPLIT HC registers
  usb: dwc2: Implement SPLIT transaction support
  usb: dwc2: Add SPLIT INTERRUPT transaction support

 drivers/usb/host/dwc2.c | 280 ++++++++++++++++++++++++++++++++----------------
 drivers/usb/host/dwc2.h |   1 +
 2 files changed, 187 insertions(+), 94 deletions(-)

-- 
2.1.4

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [U-Boot] [PATCH 1/6] usb: dwc2: Fix out-of-bounds access, fix chunk size
  2016-01-17  3:09 [U-Boot] [PATCH 0/6] usb: dwc2: Add support for SPLIT transactions Stefan Brüns
@ 2016-01-17  3:09 ` Stefan Brüns
  2016-01-17  3:09 ` [U-Boot] [PATCH 2/6] usb: dwc2: Simplify wait_for_chhltd(), remove ignore_ack Stefan Brüns
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 10+ messages in thread
From: Stefan Brüns @ 2016-01-17  3:09 UTC (permalink / raw)
  To: u-boot

Fix two errors in transfer len calculation, move loop invariant code out
of loop.

If xfer_len is equal to CONFIG_DWC2_MAX_TRANSFER_SIZE (or slightly
smaller), the xfer_len will be to large, e.g.:
  xfer_len = MAX_TRANSFER_SIZE = 65535
  max packet size = 512
    => num_packets = 128
    => IN xfer_len = 65536

For OUT transactions larger than (65536 - mps) bytes, the xfer_len
determination is quite awkward, it is only correct due to:
- max_packet_size for control/bulk/interrupt is required to be
  power-of-two.
- (CONFIG_DWC2_MAX_TRANSFER_SIZE + 1) % max-packet-size is zero
  for all allowed (2^3 ... 2^9) packet sizes

As the max xfer len is loop invariant, it can be moved out of the loop.

Signed-off-by: Stefan Br?ns <stefan.bruens@rwth-aachen.de>
---
 drivers/usb/host/dwc2.c | 30 +++++++++++++++---------------
 1 file changed, 15 insertions(+), 15 deletions(-)

diff --git a/drivers/usb/host/dwc2.c b/drivers/usb/host/dwc2.c
index 5ef6deb..d317104 100644
--- a/drivers/usb/host/dwc2.c
+++ b/drivers/usb/host/dwc2.c
@@ -786,34 +786,34 @@ int chunk_msg(struct dwc2_priv *priv, struct usb_device *dev,
 	uint32_t xfer_len;
 	uint32_t num_packets;
 	int stop_transfer = 0;
+	uint32_t max_xfer_len;
 
 	debug("%s: msg: pipe %lx pid %d in %d len %d\n", __func__, pipe, *pid,
 	      in, len);
 
+	max_xfer_len = CONFIG_DWC2_MAX_PACKET_COUNT * max;
+	if (max_xfer_len > CONFIG_DWC2_MAX_TRANSFER_SIZE)
+		max_xfer_len = CONFIG_DWC2_MAX_TRANSFER_SIZE;
+	if (max_xfer_len > DWC2_DATA_BUF_SIZE)
+		max_xfer_len = DWC2_DATA_BUF_SIZE;
+
+	/* Make sure that max_xfer_len is a multiple of max packet size. */
+	num_packets = max_xfer_len / max;
+	max_xfer_len = num_packets * max;
+
 	do {
 		/* Initialize channel */
 		dwc_otg_hc_init(regs, DWC2_HC_CHANNEL, dev, devnum, ep, in,
 				eptype, max);
 
 		xfer_len = len - done;
-		if (xfer_len > CONFIG_DWC2_MAX_TRANSFER_SIZE)
-			xfer_len = CONFIG_DWC2_MAX_TRANSFER_SIZE - max + 1;
-		if (xfer_len > DWC2_DATA_BUF_SIZE)
-			xfer_len = DWC2_DATA_BUF_SIZE - max + 1;
 
-		/* Make sure that xfer_len is a multiple of max packet size. */
-		if (xfer_len > 0) {
+		if (xfer_len > max_xfer_len)
+			xfer_len = max_xfer_len;
+		else if (xfer_len > max)
 			num_packets = (xfer_len + max - 1) / max;
-			if (num_packets > CONFIG_DWC2_MAX_PACKET_COUNT) {
-				num_packets = CONFIG_DWC2_MAX_PACKET_COUNT;
-				xfer_len = num_packets * max;
-			}
-		} else {
+		else
 			num_packets = 1;
-		}
-
-		if (in)
-			xfer_len = num_packets * max;
 
 		debug("%s: chunk: pid %d xfer_len %u pkts %u\n", __func__,
 		      *pid, xfer_len, num_packets);
-- 
2.1.4

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [U-Boot] [PATCH 2/6] usb: dwc2: Simplify wait_for_chhltd(), remove ignore_ack
  2016-01-17  3:09 [U-Boot] [PATCH 0/6] usb: dwc2: Add support for SPLIT transactions Stefan Brüns
  2016-01-17  3:09 ` [U-Boot] [PATCH 1/6] usb: dwc2: Fix out-of-bounds access, fix chunk size Stefan Brüns
@ 2016-01-17  3:09 ` Stefan Brüns
  2016-01-17  3:09 ` [U-Boot] [PATCH 3/6] usb: dwc2: split transfer core from outer loop Stefan Brüns
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 10+ messages in thread
From: Stefan Brüns @ 2016-01-17  3:09 UTC (permalink / raw)
  To: u-boot

A transfer is completed if the XFERCOMP flag is set, irrespective of the
ACK flag. BULK OUT transfers to some HS devices complete without having
the ACK flag set, which signal the devices has responded with an NYET
to the transfer (PING protocol).
The new behaviour matches the Linux kernel minus any PING protocol.

Also see 5966defabdcc (usb: dwc2: fix bulk transfers)

Signed-off-by: Stefan Br?ns <stefan.bruens@rwth-aachen.de>
---
 drivers/usb/host/dwc2.c | 40 +++++++++++++++++-----------------------
 1 file changed, 17 insertions(+), 23 deletions(-)

diff --git a/drivers/usb/host/dwc2.c b/drivers/usb/host/dwc2.c
index d317104..ad097cb 100644
--- a/drivers/usb/host/dwc2.c
+++ b/drivers/usb/host/dwc2.c
@@ -729,10 +729,8 @@ static int dwc_otg_submit_rh_msg(struct dwc2_priv *priv, struct usb_device *dev,
 	return stat;
 }
 
-int wait_for_chhltd(struct dwc2_core_regs *regs, uint32_t *sub, int *toggle,
-		    bool ignore_ack)
+int wait_for_chhltd(struct dwc2_core_regs *regs, uint32_t *sub, int *toggle)
 {
-	uint32_t hcint_comp_hlt_ack = DWC2_HCINT_XFERCOMP | DWC2_HCINT_CHHLTD;
 	struct dwc2_hc_regs *hc_regs = &regs->hc_regs[DWC2_HC_CHANNEL];
 	int ret;
 	uint32_t hcint, hctsiz;
@@ -742,25 +740,22 @@ int wait_for_chhltd(struct dwc2_core_regs *regs, uint32_t *sub, int *toggle,
 		return ret;
 
 	hcint = readl(&hc_regs->hcint);
-	if (hcint & (DWC2_HCINT_NAK | DWC2_HCINT_FRMOVRUN))
-		return -EAGAIN;
-	if (ignore_ack)
-		hcint &= ~DWC2_HCINT_ACK;
-	else
-		hcint_comp_hlt_ack |= DWC2_HCINT_ACK;
-	if (hcint != hcint_comp_hlt_ack) {
-		debug("%s: Error (HCINT=%08x)\n", __func__, hcint);
-		return -EINVAL;
-	}
-
 	hctsiz = readl(&hc_regs->hctsiz);
 	*sub = (hctsiz & DWC2_HCTSIZ_XFERSIZE_MASK) >>
 		DWC2_HCTSIZ_XFERSIZE_OFFSET;
 	*toggle = (hctsiz & DWC2_HCTSIZ_PID_MASK) >> DWC2_HCTSIZ_PID_OFFSET;
 
-	debug("%s: sub=%u toggle=%d\n", __func__, *sub, *toggle);
+	debug("%s: HCINT=%08x sub=%u toggle=%d\n", __func__, hcint, *sub,
+	      *toggle);
 
-	return 0;
+	if (hcint & DWC2_HCINT_XFERCOMP)
+		return 0;
+
+	if (hcint & (DWC2_HCINT_NAK | DWC2_HCINT_FRMOVRUN))
+		return -EAGAIN;
+
+	debug("%s: Error (HCINT=%08x)\n", __func__, hcint);
+	return -EINVAL;
 }
 
 static int dwc2_eptype[] = {
@@ -771,8 +766,7 @@ static int dwc2_eptype[] = {
 };
 
 int chunk_msg(struct dwc2_priv *priv, struct usb_device *dev,
-	      unsigned long pipe, int *pid, int in, void *buffer, int len,
-	      bool ignore_ack)
+	      unsigned long pipe, int *pid, int in, void *buffer, int len)
 {
 	struct dwc2_core_regs *regs = priv->regs;
 	struct dwc2_hc_regs *hc_regs = &regs->hc_regs[DWC2_HC_CHANNEL];
@@ -841,7 +835,7 @@ int chunk_msg(struct dwc2_priv *priv, struct usb_device *dev,
 				(1 << DWC2_HCCHAR_MULTICNT_OFFSET) |
 				DWC2_HCCHAR_CHEN);
 
-		ret = wait_for_chhltd(regs, &sub, pid, ignore_ack);
+		ret = wait_for_chhltd(regs, &sub, pid);
 		if (ret)
 			break;
 
@@ -883,7 +877,7 @@ int _submit_bulk_msg(struct dwc2_priv *priv, struct usb_device *dev,
 	}
 
 	return chunk_msg(priv, dev, pipe, &priv->bulk_data_toggle[devnum][ep],
-			 usb_pipein(pipe), buffer, len, true);
+			 usb_pipein(pipe), buffer, len);
 }
 
 static int _submit_control_msg(struct dwc2_priv *priv, struct usb_device *dev,
@@ -903,14 +897,14 @@ static int _submit_control_msg(struct dwc2_priv *priv, struct usb_device *dev,
 	}
 
 	pid = DWC2_HC_PID_SETUP;
-	ret = chunk_msg(priv, dev, pipe, &pid, 0, setup, 8, true);
+	ret = chunk_msg(priv, dev, pipe, &pid, 0, setup, 8);
 	if (ret)
 		return ret;
 
 	if (buffer) {
 		pid = DWC2_HC_PID_DATA1;
 		ret = chunk_msg(priv, dev, pipe, &pid, usb_pipein(pipe), buffer,
-				len, false);
+				len);
 		if (ret)
 			return ret;
 		act_len = dev->act_len;
@@ -926,7 +920,7 @@ static int _submit_control_msg(struct dwc2_priv *priv, struct usb_device *dev,
 
 	pid = DWC2_HC_PID_DATA1;
 	ret = chunk_msg(priv, dev, pipe, &pid, status_direction,
-			priv->status_buffer, 0, false);
+			priv->status_buffer, 0);
 	if (ret)
 		return ret;
 
-- 
2.1.4

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [U-Boot] [PATCH 3/6] usb: dwc2: split transfer core from outer loop
  2016-01-17  3:09 [U-Boot] [PATCH 0/6] usb: dwc2: Add support for SPLIT transactions Stefan Brüns
  2016-01-17  3:09 ` [U-Boot] [PATCH 1/6] usb: dwc2: Fix out-of-bounds access, fix chunk size Stefan Brüns
  2016-01-17  3:09 ` [U-Boot] [PATCH 2/6] usb: dwc2: Simplify wait_for_chhltd(), remove ignore_ack Stefan Brüns
@ 2016-01-17  3:09 ` Stefan Brüns
  2016-01-17  3:09 ` [U-Boot] [PATCH 4/6] usb: dwc2: add helper function for setting SPLIT HC registers Stefan Brüns
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 10+ messages in thread
From: Stefan Brüns @ 2016-01-17  3:09 UTC (permalink / raw)
  To: u-boot

Split the movement of data between CPU and Host Controller from the
status handling and tracking of transfer progress.
This will also simplify adding of SPLIT transaction support.

Signed-off-by: Stefan Br?ns <stefan.bruens@rwth-aachen.de>
---
 drivers/usb/host/dwc2.c | 112 +++++++++++++++++++++++++++---------------------
 1 file changed, 64 insertions(+), 48 deletions(-)

diff --git a/drivers/usb/host/dwc2.c b/drivers/usb/host/dwc2.c
index ad097cb..0e710d9 100644
--- a/drivers/usb/host/dwc2.c
+++ b/drivers/usb/host/dwc2.c
@@ -426,9 +426,6 @@ static void dwc_otg_hc_init(struct dwc2_core_regs *regs, uint8_t hc_num,
 	if (dev->speed == USB_SPEED_LOW)
 		hcchar |= DWC2_HCCHAR_LSPDDEV;
 
-	/* Clear old interrupt conditions for this host channel. */
-	writel(0x3fff, &hc_regs->hcint);
-
 	/*
 	 * Program the HCCHARn register with the endpoint characteristics
 	 * for the current transfer.
@@ -729,9 +726,8 @@ static int dwc_otg_submit_rh_msg(struct dwc2_priv *priv, struct usb_device *dev,
 	return stat;
 }
 
-int wait_for_chhltd(struct dwc2_core_regs *regs, uint32_t *sub, int *toggle)
+int wait_for_chhltd(struct dwc2_hc_regs *hc_regs, uint32_t *sub, int *toggle)
 {
-	struct dwc2_hc_regs *hc_regs = &regs->hc_regs[DWC2_HC_CHANNEL];
 	int ret;
 	uint32_t hcint, hctsiz;
 
@@ -765,6 +761,58 @@ static int dwc2_eptype[] = {
 	DWC2_HCCHAR_EPTYPE_BULK,
 };
 
+static int transfer_chunk(struct dwc2_hc_regs *hc_regs, void *aligned_buffer,
+			  int *pid, int in, void *buffer, int num_packets,
+			  int xfer_len, int *actual_len)
+{
+	int ret = 0;
+	uint32_t sub;
+
+	debug("%s: chunk: pid %d xfer_len %u pkts %u\n", __func__,
+	      *pid, xfer_len, num_packets);
+
+	writel((xfer_len << DWC2_HCTSIZ_XFERSIZE_OFFSET) |
+	       (num_packets << DWC2_HCTSIZ_PKTCNT_OFFSET) |
+	       (*pid << DWC2_HCTSIZ_PID_OFFSET),
+	       &hc_regs->hctsiz);
+
+	if (!in && xfer_len) {
+		memcpy(aligned_buffer, buffer, xfer_len);
+
+		flush_dcache_range((unsigned long)aligned_buffer,
+				   (unsigned long)aligned_buffer +
+				   roundup(xfer_len, ARCH_DMA_MINALIGN));
+	}
+
+	writel(phys_to_bus((unsigned long)aligned_buffer), &hc_regs->hcdma);
+
+	/* Clear old interrupt conditions for this host channel. */
+	writel(0x3fff, &hc_regs->hcint);
+
+	/* Set host channel enable after all other setup is complete. */
+	clrsetbits_le32(&hc_regs->hcchar, DWC2_HCCHAR_MULTICNT_MASK |
+			DWC2_HCCHAR_CHEN | DWC2_HCCHAR_CHDIS,
+			(1 << DWC2_HCCHAR_MULTICNT_OFFSET) |
+			DWC2_HCCHAR_CHEN);
+
+	ret = wait_for_chhltd(hc_regs, &sub, pid);
+	if (ret < 0)
+		return ret;
+
+	if (in) {
+		xfer_len -= sub;
+
+		invalidate_dcache_range((unsigned long)aligned_buffer,
+					(unsigned long)aligned_buffer +
+					roundup(xfer_len, ARCH_DMA_MINALIGN));
+
+		memcpy(buffer, aligned_buffer, xfer_len);
+	}
+	*actual_len = xfer_len;
+
+	return ret;
+}
+
 int chunk_msg(struct dwc2_priv *priv, struct usb_device *dev,
 	      unsigned long pipe, int *pid, int in, void *buffer, int len)
 {
@@ -776,7 +824,6 @@ int chunk_msg(struct dwc2_priv *priv, struct usb_device *dev,
 	int eptype = dwc2_eptype[usb_pipetype(pipe)];
 	int done = 0;
 	int ret = 0;
-	uint32_t sub;
 	uint32_t xfer_len;
 	uint32_t num_packets;
 	int stop_transfer = 0;
@@ -795,11 +842,12 @@ int chunk_msg(struct dwc2_priv *priv, struct usb_device *dev,
 	num_packets = max_xfer_len / max;
 	max_xfer_len = num_packets * max;
 
-	do {
-		/* Initialize channel */
-		dwc_otg_hc_init(regs, DWC2_HC_CHANNEL, dev, devnum, ep, in,
-				eptype, max);
+	/* Initialize channel */
+	dwc_otg_hc_init(regs, DWC2_HC_CHANNEL, dev, devnum, ep, in,
+			eptype, max);
 
+	do {
+		int actual_len = 0;
 		xfer_len = len - done;
 
 		if (xfer_len > max_xfer_len)
@@ -809,49 +857,17 @@ int chunk_msg(struct dwc2_priv *priv, struct usb_device *dev,
 		else
 			num_packets = 1;
 
-		debug("%s: chunk: pid %d xfer_len %u pkts %u\n", __func__,
-		      *pid, xfer_len, num_packets);
+		ret = transfer_chunk(hc_regs, priv->aligned_buffer, pid,
+				     in, (char *)buffer + done, num_packets,
+				     xfer_len, &actual_len);
 
-		writel((xfer_len << DWC2_HCTSIZ_XFERSIZE_OFFSET) |
-		       (num_packets << DWC2_HCTSIZ_PKTCNT_OFFSET) |
-		       (*pid << DWC2_HCTSIZ_PID_OFFSET),
-		       &hc_regs->hctsiz);
-
-		if (!in && xfer_len) {
-			memcpy(priv->aligned_buffer, (char *)buffer + done,
-			       xfer_len);
-
-			flush_dcache_range((unsigned long)priv->aligned_buffer,
-				(unsigned long)((void *)priv->aligned_buffer +
-				roundup(xfer_len, ARCH_DMA_MINALIGN)));
-		}
-
-		writel(phys_to_bus((unsigned long)priv->aligned_buffer),
-		       &hc_regs->hcdma);
-
-		/* Set host channel enable after all other setup is complete. */
-		clrsetbits_le32(&hc_regs->hcchar, DWC2_HCCHAR_MULTICNT_MASK |
-				DWC2_HCCHAR_CHEN | DWC2_HCCHAR_CHDIS,
-				(1 << DWC2_HCCHAR_MULTICNT_OFFSET) |
-				DWC2_HCCHAR_CHEN);
-
-		ret = wait_for_chhltd(regs, &sub, pid);
 		if (ret)
 			break;
 
-		if (in) {
-			xfer_len -= sub;
-
-			invalidate_dcache_range((unsigned long)priv->aligned_buffer,
-				(unsigned long)((void *)priv->aligned_buffer +
-				roundup(xfer_len, ARCH_DMA_MINALIGN)));
-
-			memcpy(buffer + done, priv->aligned_buffer, xfer_len);
-			if (sub)
-				stop_transfer = 1;
-		}
+		if (actual_len < xfer_len)
+			stop_transfer = 1;
 
-		done += xfer_len;
+		done += actual_len;
 
 	} while ((done < len) && !stop_transfer);
 
-- 
2.1.4

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [U-Boot] [PATCH 4/6] usb: dwc2: add helper function for setting SPLIT HC registers
  2016-01-17  3:09 [U-Boot] [PATCH 0/6] usb: dwc2: Add support for SPLIT transactions Stefan Brüns
                   ` (2 preceding siblings ...)
  2016-01-17  3:09 ` [U-Boot] [PATCH 3/6] usb: dwc2: split transfer core from outer loop Stefan Brüns
@ 2016-01-17  3:09 ` Stefan Brüns
  2016-01-17  3:09 ` [U-Boot] [PATCH 5/6] usb: dwc2: Implement SPLIT transaction support Stefan Brüns
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 10+ messages in thread
From: Stefan Brüns @ 2016-01-17  3:09 UTC (permalink / raw)
  To: u-boot

The split register setting is used for both SSPLIT and CSPLIT transactions,
the bit for CSPLIT has to be set seperately.

Signed-off-by: Stefan Br?ns <stefan.bruens@rwth-aachen.de>
---
 drivers/usb/host/dwc2.c | 15 ++++++++++++++-
 1 file changed, 14 insertions(+), 1 deletion(-)

diff --git a/drivers/usb/host/dwc2.c b/drivers/usb/host/dwc2.c
index 0e710d9..7fbbc4b 100644
--- a/drivers/usb/host/dwc2.c
+++ b/drivers/usb/host/dwc2.c
@@ -432,10 +432,23 @@ static void dwc_otg_hc_init(struct dwc2_core_regs *regs, uint8_t hc_num,
 	 */
 	writel(hcchar, &hc_regs->hcchar);
 
-	/* Program the HCSPLIT register for SPLITs */
+	/* Program the HCSPLIT register, default to no SPLIT */
 	writel(0, &hc_regs->hcsplt);
 }
 
+static void dwc_otg_hc_init_split(struct dwc2_hc_regs *hc_regs,
+				  uint8_t hub_devnum, uint8_t hub_port)
+{
+	uint32_t hcsplt = 0;
+
+	hcsplt = DWC2_HCSPLT_SPLTENA;
+	hcsplt |= hub_devnum << DWC2_HCSPLT_HUBADDR_OFFSET;
+	hcsplt |= hub_port << DWC2_HCSPLT_PRTADDR_OFFSET;
+
+	/* Program the HCSPLIT register for SPLITs */
+	writel(hcsplt, &hc_regs->hcsplt);
+}
+
 /*
  * DWC2 to USB API interface
  */
-- 
2.1.4

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [U-Boot] [PATCH 5/6] usb: dwc2: Implement SPLIT transaction support
  2016-01-17  3:09 [U-Boot] [PATCH 0/6] usb: dwc2: Add support for SPLIT transactions Stefan Brüns
                   ` (3 preceding siblings ...)
  2016-01-17  3:09 ` [U-Boot] [PATCH 4/6] usb: dwc2: add helper function for setting SPLIT HC registers Stefan Brüns
@ 2016-01-17  3:09 ` Stefan Brüns
  2016-01-17  3:09 ` [U-Boot] [PATCH 6/6] usb: dwc2: Add SPLIT INTERRUPT " Stefan Brüns
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 10+ messages in thread
From: Stefan Brüns @ 2016-01-17  3:09 UTC (permalink / raw)
  To: u-boot

In contrast to non-SPLIT transfers each transaction has to be submitted
as an individual chunk.
The transaction state machine proceeds from SSPLIT to CSPLIT if the ACK
flag is set. CSPLIT has to be repeated while NYET is set.

Signed-off-by: Stefan Br?ns <stefan.bruens@rwth-aachen.de>
---
 drivers/usb/host/dwc2.c | 81 ++++++++++++++++++++++++++++++++++++++++---------
 1 file changed, 66 insertions(+), 15 deletions(-)

diff --git a/drivers/usb/host/dwc2.c b/drivers/usb/host/dwc2.c
index 7fbbc4b..413845c 100644
--- a/drivers/usb/host/dwc2.c
+++ b/drivers/usb/host/dwc2.c
@@ -837,6 +837,8 @@ int chunk_msg(struct dwc2_priv *priv, struct usb_device *dev,
 	int eptype = dwc2_eptype[usb_pipetype(pipe)];
 	int done = 0;
 	int ret = 0;
+	int do_split = 0;
+	int complete_split = 0;
 	uint32_t xfer_len;
 	uint32_t num_packets;
 	int stop_transfer = 0;
@@ -859,8 +861,26 @@ int chunk_msg(struct dwc2_priv *priv, struct usb_device *dev,
 	dwc_otg_hc_init(regs, DWC2_HC_CHANNEL, dev, devnum, ep, in,
 			eptype, max);
 
+	/* Check if the target is a FS/LS device behind a HS hub */
+	if (dev->speed != USB_SPEED_HIGH) {
+		uint8_t hub_addr;
+		uint8_t hub_port;
+		uint32_t hprt0 = readl(&regs->hprt0);
+		if ((hprt0 & DWC2_HPRT0_PRTSPD_MASK) ==
+		     DWC2_HPRT0_PRTSPD_HIGH) {
+			usb_find_usb2_hub_address_port(dev, &hub_addr,
+						       &hub_port);
+			dwc_otg_hc_init_split(hc_regs, hub_addr, hub_port);
+
+			do_split = 1;
+			num_packets = 1;
+			max_xfer_len = max;
+		}
+	}
+
 	do {
 		int actual_len = 0;
+		uint32_t hcint;
 		xfer_len = len - done;
 
 		if (xfer_len > max_xfer_len)
@@ -870,10 +890,29 @@ int chunk_msg(struct dwc2_priv *priv, struct usb_device *dev,
 		else
 			num_packets = 1;
 
+		if (complete_split)
+			setbits_le32(&hc_regs->hcsplt, DWC2_HCSPLT_COMPSPLT);
+		else if (do_split)
+			clrbits_le32(&hc_regs->hcsplt, DWC2_HCSPLT_COMPSPLT);
+
 		ret = transfer_chunk(hc_regs, priv->aligned_buffer, pid,
 				     in, (char *)buffer + done, num_packets,
 				     xfer_len, &actual_len);
 
+		hcint = readl(&hc_regs->hcint);
+		if (complete_split) {
+			stop_transfer = 0;
+			if (hcint & DWC2_HCINT_NYET)
+				ret = 0;
+			else
+				complete_split = 0;
+		} else if (do_split) {
+			if (hcint & DWC2_HCINT_ACK) {
+				ret = 0;
+				complete_split = 1;
+			}
+		}
+
 		if (ret)
 			break;
 
@@ -882,7 +921,11 @@ int chunk_msg(struct dwc2_priv *priv, struct usb_device *dev,
 
 		done += actual_len;
 
-	} while ((done < len) && !stop_transfer);
+	/* Transactions are done when when either all data is transferred or
+	 * there is a short transfer. In case of a SPLIT make sure the CSPLIT
+	 * is executed.
+	 */
+	} while (((done < len) && !stop_transfer) || complete_split);
 
 	writel(0, &hc_regs->hcintmsk);
 	writel(0xFFFFFFFF, &hc_regs->hcint);
@@ -925,31 +968,39 @@ static int _submit_control_msg(struct dwc2_priv *priv, struct usb_device *dev,
 					     setup);
 	}
 
+	/* SETUP stage */
 	pid = DWC2_HC_PID_SETUP;
-	ret = chunk_msg(priv, dev, pipe, &pid, 0, setup, 8);
+	do {
+		ret = chunk_msg(priv, dev, pipe, &pid, 0, setup, 8);
+	} while (ret == -EAGAIN);
 	if (ret)
 		return ret;
 
+	/* DATA stage */
+	act_len = 0;
 	if (buffer) {
 		pid = DWC2_HC_PID_DATA1;
-		ret = chunk_msg(priv, dev, pipe, &pid, usb_pipein(pipe), buffer,
-				len);
+		do {
+			ret = chunk_msg(priv, dev, pipe, &pid, usb_pipein(pipe),
+					buffer, len);
+			act_len += dev->act_len;
+			buffer += dev->act_len;
+			len -= dev->act_len;
+		} while (ret == -EAGAIN);
 		if (ret)
 			return ret;
-		act_len = dev->act_len;
-	} /* End of DATA stage */
-	else
-		act_len = 0;
-
-	/* STATUS stage */
-	if ((len == 0) || usb_pipeout(pipe))
+		status_direction = usb_pipeout(pipe);
+	} else {
+		/* No-data CONTROL always ends with an IN transaction */
 		status_direction = 1;
-	else
-		status_direction = 0;
+	}
 
+	/* STATUS stage */
 	pid = DWC2_HC_PID_DATA1;
-	ret = chunk_msg(priv, dev, pipe, &pid, status_direction,
-			priv->status_buffer, 0);
+	do {
+		ret = chunk_msg(priv, dev, pipe, &pid, status_direction,
+				priv->status_buffer, 0);
+	} while (ret == -EAGAIN);
 	if (ret)
 		return ret;
 
-- 
2.1.4

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [U-Boot] [PATCH 6/6] usb: dwc2: Add SPLIT INTERRUPT transaction support
  2016-01-17  3:09 [U-Boot] [PATCH 0/6] usb: dwc2: Add support for SPLIT transactions Stefan Brüns
                   ` (4 preceding siblings ...)
  2016-01-17  3:09 ` [U-Boot] [PATCH 5/6] usb: dwc2: Implement SPLIT transaction support Stefan Brüns
@ 2016-01-17  3:09 ` Stefan Brüns
  2016-01-18 17:21 ` [U-Boot] [PATCH 0/6] usb: dwc2: Add support for SPLIT transactions Marek Vasut
  2016-01-27  3:52 ` Stephen Warren
  7 siblings, 0 replies; 10+ messages in thread
From: Stefan Brüns @ 2016-01-17  3:09 UTC (permalink / raw)
  To: u-boot

CSPLITs for INTERRUPT transactions have to be scheduled in each microframe
following the SSPLIT. INTERRUPT transfers are executed in the next even/
odd microframe depending on the HCCHAR_ODDFRM flag.

As there are no handshakes for INTERRUPT SSPLITs the SSPLIT may have
failed (transport error) without the error being detected by the host
driver. If the last CSPLIT is not received within 4 microframes after the
SSPLIT there was a transaction error and the complete transaction has
to be restarted.

Signed-off-by: Stefan Br?ns <stefan.bruens@rwth-aachen.de>
---
 drivers/usb/host/dwc2.c | 28 +++++++++++++++++++++++-----
 drivers/usb/host/dwc2.h |  1 +
 2 files changed, 24 insertions(+), 5 deletions(-)

diff --git a/drivers/usb/host/dwc2.c b/drivers/usb/host/dwc2.c
index 413845c..291e4a5 100644
--- a/drivers/usb/host/dwc2.c
+++ b/drivers/usb/host/dwc2.c
@@ -776,7 +776,7 @@ static int dwc2_eptype[] = {
 
 static int transfer_chunk(struct dwc2_hc_regs *hc_regs, void *aligned_buffer,
 			  int *pid, int in, void *buffer, int num_packets,
-			  int xfer_len, int *actual_len)
+			  int xfer_len, int *actual_len, int odd_frame)
 {
 	int ret = 0;
 	uint32_t sub;
@@ -804,8 +804,10 @@ static int transfer_chunk(struct dwc2_hc_regs *hc_regs, void *aligned_buffer,
 
 	/* Set host channel enable after all other setup is complete. */
 	clrsetbits_le32(&hc_regs->hcchar, DWC2_HCCHAR_MULTICNT_MASK |
-			DWC2_HCCHAR_CHEN | DWC2_HCCHAR_CHDIS,
+			DWC2_HCCHAR_CHEN | DWC2_HCCHAR_CHDIS |
+			DWC2_HCCHAR_ODDFRM,
 			(1 << DWC2_HCCHAR_MULTICNT_OFFSET) |
+			(odd_frame << DWC2_HCCHAR_ODDFRM_OFFSET) |
 			DWC2_HCCHAR_CHEN);
 
 	ret = wait_for_chhltd(hc_regs, &sub, pid);
@@ -831,6 +833,7 @@ int chunk_msg(struct dwc2_priv *priv, struct usb_device *dev,
 {
 	struct dwc2_core_regs *regs = priv->regs;
 	struct dwc2_hc_regs *hc_regs = &regs->hc_regs[DWC2_HC_CHANNEL];
+	struct dwc2_host_regs *host_regs = &regs->host_regs;
 	int devnum = usb_pipedevice(pipe);
 	int ep = usb_pipeendpoint(pipe);
 	int max = usb_maxpacket(dev, pipe);
@@ -843,6 +846,7 @@ int chunk_msg(struct dwc2_priv *priv, struct usb_device *dev,
 	uint32_t num_packets;
 	int stop_transfer = 0;
 	uint32_t max_xfer_len;
+	int ssplit_frame_num = 0;
 
 	debug("%s: msg: pipe %lx pid %d in %d len %d\n", __func__, pipe, *pid,
 	      in, len);
@@ -881,6 +885,7 @@ int chunk_msg(struct dwc2_priv *priv, struct usb_device *dev,
 	do {
 		int actual_len = 0;
 		uint32_t hcint;
+		int odd_frame = 0;
 		xfer_len = len - done;
 
 		if (xfer_len > max_xfer_len)
@@ -895,19 +900,32 @@ int chunk_msg(struct dwc2_priv *priv, struct usb_device *dev,
 		else if (do_split)
 			clrbits_le32(&hc_regs->hcsplt, DWC2_HCSPLT_COMPSPLT);
 
+		if (eptype == DWC2_HCCHAR_EPTYPE_INTR) {
+			int uframe_num = readl(&host_regs->hfnum);
+			if (!(uframe_num & 0x1))
+				odd_frame = 1;
+		}
+
 		ret = transfer_chunk(hc_regs, priv->aligned_buffer, pid,
 				     in, (char *)buffer + done, num_packets,
-				     xfer_len, &actual_len);
+				     xfer_len, &actual_len, odd_frame);
 
 		hcint = readl(&hc_regs->hcint);
 		if (complete_split) {
 			stop_transfer = 0;
-			if (hcint & DWC2_HCINT_NYET)
+			if (hcint & DWC2_HCINT_NYET) {
 				ret = 0;
-			else
+				int frame_num = DWC2_HFNUM_MAX_FRNUM &
+						readl(&host_regs->hfnum);
+				if (((frame_num - ssplit_frame_num) &
+				    DWC2_HFNUM_MAX_FRNUM) > 4)
+					ret = -EAGAIN;
+			} else
 				complete_split = 0;
 		} else if (do_split) {
 			if (hcint & DWC2_HCINT_ACK) {
+				ssplit_frame_num = DWC2_HFNUM_MAX_FRNUM &
+						   readl(&host_regs->hfnum);
 				ret = 0;
 				complete_split = 1;
 			}
diff --git a/drivers/usb/host/dwc2.h b/drivers/usb/host/dwc2.h
index f69372e..594757b 100644
--- a/drivers/usb/host/dwc2.h
+++ b/drivers/usb/host/dwc2.h
@@ -500,6 +500,7 @@ struct dwc2_core_regs {
 #define DWC2_HFNUM_FRNUM_OFFSET				0
 #define DWC2_HFNUM_FRREM_MASK				(0xFFFF << 16)
 #define DWC2_HFNUM_FRREM_OFFSET				16
+#define DWC2_HFNUM_MAX_FRNUM				0x3FFF
 #define DWC2_HPTXSTS_PTXFSPCAVAIL_MASK			(0xFFFF << 0)
 #define DWC2_HPTXSTS_PTXFSPCAVAIL_OFFSET		0
 #define DWC2_HPTXSTS_PTXQSPCAVAIL_MASK			(0xFF << 16)
-- 
2.1.4

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [U-Boot] [PATCH 0/6] usb: dwc2: Add support for SPLIT transactions
  2016-01-17  3:09 [U-Boot] [PATCH 0/6] usb: dwc2: Add support for SPLIT transactions Stefan Brüns
                   ` (5 preceding siblings ...)
  2016-01-17  3:09 ` [U-Boot] [PATCH 6/6] usb: dwc2: Add SPLIT INTERRUPT " Stefan Brüns
@ 2016-01-18 17:21 ` Marek Vasut
  2016-01-27  3:52 ` Stephen Warren
  7 siblings, 0 replies; 10+ messages in thread
From: Marek Vasut @ 2016-01-18 17:21 UTC (permalink / raw)
  To: u-boot

On Sunday, January 17, 2016 at 04:09:50 AM, Stefan Br?ns wrote:
> The first patch fixes an out-of-bounds access, and makes the calculation of
> maximum transfer size more straightforward. It also makes overriding the
> maximum transfer size easier for split transactions
> 
> 2nd and 3rd patch cleanup and restructure the current code in preparation
> for the split support.
> 
> Patch 4 and 5 add the actual support for CONTROL and BULK transactions.
> 
> Patch 6 adds support for INTERUPT splits.
> 
> The patch series uses the same logic as the earlier patch series, but is
> otherwise a rewrite.
> 
> With the patch series, all my LS/FS devices enumerate behind the RPi 1
> onboard hub and an external hub.
> 
> Interrupt transfers have been tested with a Logitech K400 and a noname
> wired keyboard. Both work if "stdin" includes usbkbd prior to usb start,
> and u-boot has been configured with CONFIG_USB_KEYBOARD=y and
> CONFIG_SYS_USB_EVENT_POLL=y (but only one at a time).

The series is fine with me and I was testing previous version on socfpga.
I'd like to apply this, unless there is some opposition.

Best regards,
Marek Vasut

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [U-Boot] [PATCH 0/6] usb: dwc2: Add support for SPLIT transactions
  2016-01-17  3:09 [U-Boot] [PATCH 0/6] usb: dwc2: Add support for SPLIT transactions Stefan Brüns
                   ` (6 preceding siblings ...)
  2016-01-18 17:21 ` [U-Boot] [PATCH 0/6] usb: dwc2: Add support for SPLIT transactions Marek Vasut
@ 2016-01-27  3:52 ` Stephen Warren
  2016-01-27  4:05   ` Marek Vasut
  7 siblings, 1 reply; 10+ messages in thread
From: Stephen Warren @ 2016-01-27  3:52 UTC (permalink / raw)
  To: u-boot

On 01/16/2016 08:09 PM, Stefan Br?ns wrote:
> The first patch fixes an out-of-bounds access, and makes the calculation of
> maximum transfer size more straightforward. It also makes overriding the
> maximum transfer size easier for split transactions

Sorry I haven't been keeping up on the U-Boot dwc2 patches. Is this
series still pending, or was it replaced with something else? It doesn't
apply cleanly on top of u-boot/master.

The good news is that with u-boot/master (b72ae192e39f "Merge branch
'master' of git://git.denx.de/u-boot-video") both USB keyboards I have
work perfectly on both a model A and a model B, including keyboard
repeat. That's great work; thanks very much!

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [U-Boot] [PATCH 0/6] usb: dwc2: Add support for SPLIT transactions
  2016-01-27  3:52 ` Stephen Warren
@ 2016-01-27  4:05   ` Marek Vasut
  0 siblings, 0 replies; 10+ messages in thread
From: Marek Vasut @ 2016-01-27  4:05 UTC (permalink / raw)
  To: u-boot

On Wednesday, January 27, 2016 at 04:52:03 AM, Stephen Warren wrote:
> On 01/16/2016 08:09 PM, Stefan Br?ns wrote:
> > The first patch fixes an out-of-bounds access, and makes the calculation
> > of maximum transfer size more straightforward. It also makes overriding
> > the maximum transfer size easier for split transactions
> 
> Sorry I haven't been keeping up on the U-Boot dwc2 patches. Is this
> series still pending, or was it replaced with something else? It doesn't
> apply cleanly on top of u-boot/master.
> 
> The good news is that with u-boot/master (b72ae192e39f "Merge branch
> 'master' of git://git.denx.de/u-boot-video") both USB keyboards I have
> work perfectly on both a model A and a model B, including keyboard
> repeat. That's great work; thanks very much!

V2 is applied on top of u-boot/master. Your keyboard patch is also applied:

http://git.denx.de/?p=u-boot/u-boot-usb.git;a=summary

Thank you!

Best regards,
Marek Vasut

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2016-01-27  4:05 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-01-17  3:09 [U-Boot] [PATCH 0/6] usb: dwc2: Add support for SPLIT transactions Stefan Brüns
2016-01-17  3:09 ` [U-Boot] [PATCH 1/6] usb: dwc2: Fix out-of-bounds access, fix chunk size Stefan Brüns
2016-01-17  3:09 ` [U-Boot] [PATCH 2/6] usb: dwc2: Simplify wait_for_chhltd(), remove ignore_ack Stefan Brüns
2016-01-17  3:09 ` [U-Boot] [PATCH 3/6] usb: dwc2: split transfer core from outer loop Stefan Brüns
2016-01-17  3:09 ` [U-Boot] [PATCH 4/6] usb: dwc2: add helper function for setting SPLIT HC registers Stefan Brüns
2016-01-17  3:09 ` [U-Boot] [PATCH 5/6] usb: dwc2: Implement SPLIT transaction support Stefan Brüns
2016-01-17  3:09 ` [U-Boot] [PATCH 6/6] usb: dwc2: Add SPLIT INTERRUPT " Stefan Brüns
2016-01-18 17:21 ` [U-Boot] [PATCH 0/6] usb: dwc2: Add support for SPLIT transactions Marek Vasut
2016-01-27  3:52 ` Stephen Warren
2016-01-27  4:05   ` Marek Vasut

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.