mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-25 22:20:45 +00:00
usb: dwc2: Implement SPLIT transaction support
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>
This commit is contained in:
parent
890f0ee4bb
commit
b54e447052
1 changed files with 66 additions and 15 deletions
|
@ -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(®s->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_direction = usb_pipeout(pipe);
|
||||
} else {
|
||||
/* No-data CONTROL always ends with an IN transaction */
|
||||
status_direction = 1;
|
||||
}
|
||||
|
||||
/* STATUS stage */
|
||||
if ((len == 0) || usb_pipeout(pipe))
|
||||
status_direction = 1;
|
||||
else
|
||||
status_direction = 0;
|
||||
|
||||
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;
|
||||
|
||||
|
|
Loading…
Reference in a new issue