diff options
Diffstat (limited to 'drivers')
| -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 fdad73972..3ae04c025 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);  } |