summaryrefslogtreecommitdiffstats
path: root/drivers/usb/host
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/host')
-rw-r--r--drivers/usb/host/ehci-hcd.c38
1 files changed, 38 insertions, 0 deletions
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index fdad739724..3ae04c0253 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -36,6 +36,12 @@
#define CONFIG_USB_MAX_CONTROLLER_COUNT 1
#endif
+/*
+ * EHCI spec page 20 says that the HC may take up to 16 uFrames (= 4ms) to halt.
+ * Let's time out after 8 to have a little safety margin on top of that.
+ */
+#define HCHALT_TIMEOUT (8 * 1000)
+
static struct ehci_ctrl ehcic[CONFIG_USB_MAX_CONTROLLER_COUNT];
#define ALIGN_END_ADDR(type, ptr, size) \
@@ -190,6 +196,36 @@ out:
return ret;
}
+static int ehci_shutdown(struct ehci_ctrl *ctrl)
+{
+ int i, ret = 0;
+ uint32_t cmd, reg;
+
+ cmd = ehci_readl(&ctrl->hcor->or_usbcmd);
+ cmd &= ~(CMD_PSE | CMD_ASE);
+ ehci_writel(&ctrl->hcor->or_usbcmd, cmd);
+ ret = handshake(&ctrl->hcor->or_usbsts, STS_ASS | STS_PSS, 0,
+ 100 * 1000);
+
+ if (!ret) {
+ for (i = 0; i < CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS; i++) {
+ reg = ehci_readl(&ctrl->hcor->or_portsc[i]);
+ reg |= EHCI_PS_SUSP;
+ ehci_writel(&ctrl->hcor->or_portsc[i], reg);
+ }
+
+ cmd &= ~CMD_RUN;
+ ehci_writel(&ctrl->hcor->or_usbcmd, cmd);
+ ret = handshake(&ctrl->hcor->or_usbsts, STS_HALT, STS_HALT,
+ HCHALT_TIMEOUT);
+ }
+
+ if (ret)
+ puts("EHCI failed to shut down host controller.\n");
+
+ return ret;
+}
+
static int ehci_td_buffer(struct qTD *td, void *buf, size_t sz)
{
uint32_t delta, next;
@@ -808,6 +844,7 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer,
}
break;
case USB_PORT_FEAT_TEST:
+ ehci_shutdown(ctrl);
reg &= ~(0xf << 16);
reg |= ((le16_to_cpu(req->index) >> 8) & 0xf) << 16;
ehci_writel(status_reg, reg);
@@ -878,6 +915,7 @@ unknown:
int usb_lowlevel_stop(int index)
{
+ ehci_shutdown(&ehcic[index]);
return ehci_hcd_stop(index);
}
OpenPOWER on IntegriCloud