In contrast to non-SPLIT transfers each transaction has to be submitted
as an individual chunk. Handling of ACK/NAk/NYET handshakes depends on
transaction (non-SPLIT/SSPLIT/CSPLIT), thus inline the HCINT flag handling.

Signed-off-by: Stefan Brüns <stefan.bru...@rwth-aachen.de>
---
 drivers/usb/host/dwc2.c | 96 ++++++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 83 insertions(+), 13 deletions(-)

diff --git a/drivers/usb/host/dwc2.c b/drivers/usb/host/dwc2.c
index 6496fcf..0bf3ee5 100644
--- a/drivers/usb/host/dwc2.c
+++ b/drivers/usb/host/dwc2.c
@@ -848,8 +848,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];
@@ -863,23 +862,50 @@ 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 hctsiz;
+       uint32_t hcint;
+       uint32_t hcint_rem;
+       uint8_t do_split = 0;
+       uint8_t complete_split = 0;
+       uint8_t start_again = 0;
+       uint8_t hub_addr = 0;
+       uint8_t hub_port = 0;
 
        debug("%s: msg: pipe %lx pid %d in %d len %d\n", __func__, pipe, *pid,
              in, len);
 
+       /* Initialize channel */
+       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) {
+               uint32_t hprt0 = readl(&regs->hprt0);
+               if ((hprt0 & DWC2_HPRT0_PRTSPD_MASK) ==
+                       DWC2_HPRT0_PRTSPD_HIGH) {
+                       do_split = 1;
+                       dwc_find_hub_address_port(dev, &hub_addr, &hub_port);
+               }
+       }
+
        do {
-               /* Initialize channel */
-               dwc_otg_hc_init(regs, DWC2_HC_CHANNEL, dev, devnum, ep, in,
-                               eptype, max);
+               /* Clear old interrupt conditions for this host channel. */
+               writel(0x3fff, &hc_regs->hcint);
+
+               if (do_split)
+                       dwc_otg_hc_init_split(regs, DWC2_HC_CHANNEL, hub_addr,
+                                             hub_port, complete_split);
 
                xfer_len = len - done;
+               if (do_split && xfer_len > max)
+                       xfer_len = max;
                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) {
                        num_packets = (xfer_len + max - 1) / max;
                        if (num_packets > CONFIG_DWC2_MAX_PACKET_COUNT) {
                                num_packets = CONFIG_DWC2_MAX_PACKET_COUNT;
@@ -918,10 +944,54 @@ 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_bit(&hc_regs->hcint, DWC2_HCINT_CHHLTD, true);
                if (ret)
                        break;
 
+               hcint = readl(&hc_regs->hcint);
+               hcint_rem = hcint & ~(DWC2_HCINT_XFERCOMP | DWC2_HCINT_CHHLTD);
+
+               hctsiz = readl(&hc_regs->hctsiz);
+               sub = (hctsiz & DWC2_HCTSIZ_XFERSIZE_MASK) >>
+                       DWC2_HCTSIZ_XFERSIZE_OFFSET;
+               *pid = (hctsiz & DWC2_HCTSIZ_PID_MASK) >>
+                       DWC2_HCTSIZ_PID_OFFSET;
+
+               start_again = 0;
+               if (complete_split) {
+                       complete_split = 0;
+                       if (hcint_rem & DWC2_HCINT_NYET) {
+                               hcint_rem &= ~DWC2_HCINT_NYET;
+                               start_again = 1;
+                       }
+               } else if (do_split) {
+                       if (hcint_rem & DWC2_HCINT_NAK) {
+                               hcint_rem &= ~DWC2_HCINT_NAK;
+                               /* should never happen, as there are no
+                                * concurrent transactions */
+                               printf("TT busy\n");
+                               ret = -EINVAL;
+                       } else if (hcint_rem & DWC2_HCINT_ACK) {
+                               complete_split = 1;
+                               start_again = 1;
+                       }
+               }
+
+               if (start_again) {
+                       sub = 0;
+                       xfer_len = 0;
+               }
+
+               if (hcint_rem & ~(DWC2_HCINT_NAK | DWC2_HCINT_FRMOVRUN |
+                       DWC2_HCINT_ACK)) {
+                       debug("%s: Error (HCINT=%08x)\n", __func__, hcint);
+                       ret = -EINVAL;
+                       break;
+               } else if (hcint_rem & (DWC2_HCINT_NAK | DWC2_HCINT_FRMOVRUN)) {
+                       ret = -EAGAIN;
+                       break;
+               }
+
                if (in) {
                        xfer_len -= sub;
 
@@ -930,13 +1000,13 @@ int chunk_msg(struct dwc2_priv *priv, struct usb_device 
*dev,
                                roundup(xfer_len, ARCH_DMA_MINALIGN)));
 
                        memcpy(buffer + done, priv->aligned_buffer, xfer_len);
-                       if (sub)
+                       if (sub) /* short transfer/ZLP */
                                stop_transfer = 1;
                }
 
                done += xfer_len;
 
-       } while ((done < len) && !stop_transfer);
+       } while (((done < len) && !stop_transfer) || start_again);
 
        writel(0, &hc_regs->hcintmsk);
        writel(0xFFFFFFFF, &hc_regs->hcint);
@@ -960,7 +1030,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,
@@ -980,7 +1050,7 @@ 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;
 
@@ -995,7 +1065,7 @@ static int _submit_control_msg(struct dwc2_priv *priv, 
struct usb_device *dev,
                                return -ETIMEDOUT;
                        }
                        ret = chunk_msg(priv, dev, pipe, &pid, usb_pipein(pipe),
-                                       buffer, len, false);
+                                       buffer, len);
                        act_len += dev->act_len;
                        buffer += dev->act_len;
                        len -= dev->act_len;
@@ -1013,7 +1083,7 @@ static int _submit_control_msg(struct dwc2_priv *priv, 
struct usb_device *dev,
        pid = DWC2_HC_PID_DATA1;
        do {
                ret = chunk_msg(priv, dev, pipe, &pid, status_direction,
-                               priv->status_buffer, 0, false);
+                               priv->status_buffer, 0);
        } while (ret == -EAGAIN);
        if (ret)
                return ret;
-- 
2.1.4

_______________________________________________
U-Boot mailing list
U-Boot@lists.denx.de
http://lists.denx.de/mailman/listinfo/u-boot

Reply via email to