diff options
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/host/ehci-hcd.c | 38 |
1 files changed, 38 insertions, 0 deletions
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index fdad739..3ae04c0 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); } |