ss = (struct us_data *)udev->privptr;
usb_disable_asynch(1); /* asynch transfer not allowed */
+ usb_lock_async(udev, 1);
srb->lun = block_dev->lun;
buf_addr = (uintptr_t)buffer;
start = blknr;
debug("usb_read: end startblk " LBAF ", blccnt %x buffer %lx\n",
start, smallblks, buf_addr);
+ usb_lock_async(udev, 0);
usb_disable_asynch(0); /* asynch transfer allowed */
if (blkcnt >= ss->max_xfer_blk)
debug("\n");
ss = (struct us_data *)udev->privptr;
usb_disable_asynch(1); /* asynch transfer not allowed */
+ usb_lock_async(udev, 1);
srb->lun = block_dev->lun;
buf_addr = (uintptr_t)buffer;
debug("usb_write: end startblk " LBAF ", blccnt %x buffer %lx\n",
start, smallblks, buf_addr);
+ usb_lock_async(udev, 0);
usb_disable_asynch(0); /* asynch transfer allowed */
if (blkcnt >= ss->max_xfer_blk)
debug("\n");
QH_ENDPT2_HUBADDR(hubaddr));
}
+static int ehci_enable_async(struct ehci_ctrl *ctrl)
+{
+ u32 cmd;
+ int ret;
+
+ /* Enable async. schedule. */
+ cmd = ehci_readl(&ctrl->hcor->or_usbcmd);
+ if (cmd & CMD_ASE)
+ return 0;
+
+ cmd |= CMD_ASE;
+ ehci_writel(&ctrl->hcor->or_usbcmd, cmd);
+
+ ret = handshake((uint32_t *)&ctrl->hcor->or_usbsts, STS_ASS, STS_ASS,
+ 100 * 1000);
+ if (ret < 0)
+ printf("EHCI fail timeout STS_ASS set\n");
+
+ return ret;
+}
+
+static int ehci_disable_async(struct ehci_ctrl *ctrl)
+{
+ u32 cmd;
+ int ret;
+
+ if (ctrl->async_locked)
+ return 0;
+
+ /* Disable async schedule. */
+ cmd = ehci_readl(&ctrl->hcor->or_usbcmd);
+ if (!(cmd & CMD_ASE))
+ return 0;
+
+ cmd &= ~CMD_ASE;
+ ehci_writel(&ctrl->hcor->or_usbcmd, cmd);
+
+ ret = handshake((uint32_t *)&ctrl->hcor->or_usbsts, STS_ASS, 0,
+ 100 * 1000);
+ if (ret < 0)
+ printf("EHCI fail timeout STS_ASS reset\n");
+
+ return ret;
+}
+
static int
ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
int length, struct devrequest *req)
uint32_t *tdp;
uint32_t endpt, maxpacket, token, usbsts, qhtoken;
uint32_t c, toggle;
- uint32_t cmd;
int timeout;
int ret = 0;
struct ehci_ctrl *ctrl = ehci_get_ctrl(dev);
usbsts = ehci_readl(&ctrl->hcor->or_usbsts);
ehci_writel(&ctrl->hcor->or_usbsts, (usbsts & 0x3f));
- /* Enable async. schedule. */
- cmd = ehci_readl(&ctrl->hcor->or_usbcmd);
- if (!(cmd & CMD_ASE)) {
- cmd |= CMD_ASE;
- ehci_writel(&ctrl->hcor->or_usbcmd, cmd);
-
- ret = handshake((uint32_t *)&ctrl->hcor->or_usbsts, STS_ASS, STS_ASS,
- 100 * 1000);
- if (ret < 0) {
- printf("EHCI fail timeout STS_ASS set\n");
- goto fail;
- }
- }
+ ret = ehci_enable_async(ctrl);
+ if (ret)
+ goto fail;
/* Wait for TDs to be processed. */
ts = get_timer(0);
if (QT_TOKEN_GET_STATUS(token) & QT_TOKEN_STATUS_ACTIVE)
printf("EHCI timed out on TD - token=%#x\n", token);
+ ret = ehci_disable_async(ctrl);
+ if (ret)
+ goto fail;
+
if (!(QT_TOKEN_GET_STATUS(qhtoken) & QT_TOKEN_STATUS_ACTIVE)) {
debug("TOKEN=%#x\n", qhtoken);
switch (QT_TOKEN_GET_STATUS(qhtoken) &
return result;
}
+static int _ehci_lock_async(struct ehci_ctrl *ctrl, int lock)
+{
+ ctrl->async_locked = lock;
+
+ if (lock)
+ return 0;
+
+ return ehci_disable_async(ctrl);
+}
+
#if !CONFIG_IS_ENABLED(DM_USB)
int submit_bulk_msg(struct usb_device *dev, unsigned long pipe,
void *buffer, int length)
{
return _ehci_destroy_int_queue(dev, queue);
}
+
+int usb_lock_async(struct usb_device *dev, int lock)
+{
+ struct ehci_ctrl *ctrl = ehci_get_ctrl(dev);
+
+ return _ehci_lock_async(ctrl, lock);
+}
#endif
#if CONFIG_IS_ENABLED(DM_USB)
return 0;
}
+static int ehci_lock_async(struct udevice *dev, int lock)
+{
+ struct ehci_ctrl *ctrl = dev_get_priv(dev);
+
+ return _ehci_lock_async(ctrl, lock);
+}
+
int ehci_register(struct udevice *dev, struct ehci_hccr *hccr,
struct ehci_hcor *hcor, const struct ehci_ops *ops,
uint tweaks, enum usb_init_type init)
.poll_int_queue = ehci_poll_int_queue,
.destroy_int_queue = ehci_destroy_int_queue,
.get_max_xfer_size = ehci_get_max_xfer_size,
+ .lock_async = ehci_lock_async,
};
#endif
void *data, int len, int *actual_length, int timeout);
int usb_int_msg(struct usb_device *dev, unsigned long pipe,
void *buffer, int transfer_len, int interval, bool nonblock);
+int usb_lock_async(struct usb_device *dev, int lock);
int usb_disable_asynch(int disable);
int usb_maxpacket(struct usb_device *dev, unsigned long pipe);
int usb_get_configuration_no(struct usb_device *dev, int cfgno,
* in a USB transfer. USB class driver needs to be aware of this.
*/
int (*get_max_xfer_size)(struct udevice *bus, size_t *size);
+
+ /**
+ * lock_async() - Keep async schedule after a transfer
+ *
+ * It may be desired to keep the asynchronous schedule running even
+ * after a transfer finishes, usually when doing multiple transfers
+ * back-to-back. This callback allows signalling the USB controller
+ * driver to do just that.
+ */
+ int (*lock_async)(struct udevice *udev, int lock);
};
#define usb_get_ops(dev) ((struct dm_usb_ops *)(dev)->driver->ops)