summaryrefslogtreecommitdiff
path: root/drivers/usb/host/ehci-hcd.c
diff options
context:
space:
mode:
authorMarek Vasut <marek.vasut@gmail.com>2020-04-06 14:29:44 +0200
committerTom Rini <trini@konsulko.com>2020-04-09 15:26:59 -0400
commit31232de07ef2bd97ff67625976eecd97eeb1bd3d (patch)
treea2c2b13d0321e29a09435d664c8a8c960a935e62 /drivers/usb/host/ehci-hcd.c
parent0db0ba6141f402b1d496ef53d9fa69978f75ec61 (diff)
usb: Keep async schedule running only across mass storage xfers
Rather than keeping the asynchronous schedule running always, keep it running only across USB mass storage transfers for now, as it seems that keeping it running all the time interferes with certain control transfers during device enumeration. Note that running the async schedule all the time should not be an issue, especially on EHCI HCD, as that one implements most of the transfers using async schedule. Note that we have usb_disable_asynch(), which however is utterly broken. The usb_disable_asynch() blocks the USB core from doing async transfers by setting a global flag. The async schedule should however be disabled per USB controller. Moreover, setting a global flag does not prevent the controller from using the async schedule, which e.g. the EHCI HCD does. This patch implements additional callback to the controller, which permits it to lock the async schedule and keep it running across multiple transfers. Once the schedule is unlocked, it must also be disabled. This thus prevents the async schedule from running outside of the USB mass storage transfers. Signed-off-by: Marek Vasut <marek.vasut+renesas@gmail.com> Cc: Lukasz Majewski <lukma@denx.de> Cc: Tom Rini <trini@konsulko.com> Tested-by: Tom Rini <trini@konsulko.com> [omap3_beagle, previously failing]
Diffstat (limited to 'drivers/usb/host/ehci-hcd.c')
-rw-r--r--drivers/usb/host/ehci-hcd.c91
1 files changed, 77 insertions, 14 deletions
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 1cc02052f5..1edb344d0f 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -298,6 +298,51 @@ static void ehci_update_endpt2_dev_n_port(struct usb_device *udev,
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)
@@ -311,7 +356,6 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
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);
@@ -556,19 +600,9 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
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);
@@ -611,6 +645,10 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
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) &
@@ -1512,6 +1550,16 @@ static int _ehci_submit_int_msg(struct usb_device *dev, unsigned long pipe,
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)
@@ -1549,6 +1597,13 @@ int destroy_int_queue(struct usb_device *dev, struct int_queue *queue)
{
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)
@@ -1612,6 +1667,13 @@ static int ehci_get_max_xfer_size(struct udevice *dev, size_t *size)
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)
@@ -1678,6 +1740,7 @@ struct dm_usb_ops ehci_usb_ops = {
.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