diff options
| -rw-r--r-- | drivers/usb/usb_ehci.h | 27 | ||||
| -rw-r--r-- | drivers/usb/usb_ehci_core.c | 145 | ||||
| -rw-r--r-- | drivers/usb/usb_ehci_fsl.c | 3 | ||||
| -rw-r--r-- | include/usb.h | 2 | 
4 files changed, 133 insertions, 44 deletions
| diff --git a/drivers/usb/usb_ehci.h b/drivers/usb/usb_ehci.h index 3e7a2ab00..90b137ad0 100644 --- a/drivers/usb/usb_ehci.h +++ b/drivers/usb/usb_ehci.h @@ -45,15 +45,24 @@ struct ehci_hccr {  #define HC_LENGTH(p)		(((p) >> 0) & 0x00ff)  #define HC_VERSION(p)		(((p) >> 16) & 0xffff)  	uint32_t cr_hcsparams; +#define HCS_N_PORTS(p)		(((p) >> 0) & 0xf)  	uint32_t cr_hccparams;  	uint8_t cr_hcsp_portrt[8];  };  struct ehci_hcor {  	uint32_t or_usbcmd; -#define CMD_ASE		(1 << 5) +#define CMD_PARK	(1 << 11)		/* enable "park" */ +#define CMD_PARK_CNT(c)	(((c) >> 8) & 3)	/* how many transfers to park */ +#define CMD_ASE		(1 << 5)		/* async schedule enable */ +#define CMD_LRESET	(1 << 7)		/* partial reset */ +#define CMD_IAAD	(1 << 5)		/* "doorbell" interrupt */ +#define CMD_PSE		(1 << 4)		/* periodic schedule enable */ +#define CMD_RESET	(1 << 1)		/* reset HC not bus */ +#define CMD_RUN		(1 << 0)		/* start/stop HC */  	uint32_t or_usbsts;  #define	STD_ASS		(1 << 15) +#define STS_HALT	(1 << 12)  	uint32_t or_usbintr;  	uint32_t or_frindex;  	uint32_t or_ctrldssegment; @@ -61,10 +70,17 @@ struct ehci_hcor {  	uint32_t or_asynclistaddr;  	uint32_t _reserved_[9];  	uint32_t or_configflag; +#define FLAG_CF		(1 << 0)	/* true:  we'll support "high speed" */  	uint32_t or_portsc[2];  	uint32_t or_systune;  }; +#define USBMODE		0x68		/* USB Device mode */ +#define USBMODE_SDIS	(1 << 3)	/* Stream disable */ +#define USBMODE_BE	(1 << 2)	/* BE/LE endiannes select */ +#define USBMODE_CM_HC	(3 << 0)	/* host controller mode */ +#define USBMODE_CM_IDLE	(0 << 0)	/* idle state */ +  /* Interface descriptor */  struct usb_linux_interface_descriptor {  	unsigned char	bLength; @@ -91,11 +107,12 @@ struct usb_linux_config_descriptor {  } __attribute__ ((packed));  #if defined CONFIG_EHCI_DESC_BIG_ENDIAN -#define	ehci_readl(x)		(x) -#define ehci_writel(a, b)	(a) = (b) +#define	ehci_readl(x)		(*((volatile u32 *)(x))) +#define ehci_writel(a, b)	(*((volatile u32 *)(a)) = ((volatile u32)b))  #else -#define ehci_readl(x)		cpu_to_le32((x)) -#define ehci_writel(a, b)	(a) = cpu_to_le32((b)) +#define ehci_readl(x)		cpu_to_le32((*((volatile u32 *)(x)))) +#define ehci_writel(a, b)	(*((volatile u32 *)(a)) = \ +					cpu_to_le32(((volatile u32)b)))  #endif  #if defined CONFIG_EHCI_MMIO_BIG_ENDIAN diff --git a/drivers/usb/usb_ehci_core.c b/drivers/usb/usb_ehci_core.c index 869bea3e7..ec50874f7 100644 --- a/drivers/usb/usb_ehci_core.c +++ b/drivers/usb/usb_ehci_core.c @@ -99,8 +99,55 @@ static struct descriptor {  	},  }; -static void ehci_free (void *p, size_t sz) +static int handshake(uint32_t *ptr, uint32_t mask, uint32_t done, int msec)  { +	uint32_t result; +	do { +		result = ehci_readl(ptr); +		debug("handshake read reg(%x)=%x\n", (uint32_t)ptr, result); +		if (result == ~(uint32_t)0) +			return -1; +		result &= mask; +		if (result == done) +			return 0; +		wait_ms(1); +		msec--; +	} while (msec > 0); +	return -1; +} + +static void ehci_free(void *p, size_t sz) +{ + +} + +static int ehci_reset(void) +{ +	uint32_t cmd; +	uint32_t tmp; +	uint32_t *reg_ptr; +	int ret = 0; + +	cmd = ehci_readl(&hcor->or_usbcmd); +	cmd |= CMD_RESET; +	ehci_writel(&hcor->or_usbcmd, cmd); +	ret = handshake(&hcor->or_usbcmd, CMD_RESET, 0, 250); +	if (ret < 0) { +		printf("EHCI fail to reset\n"); +		goto out; +	} + +#if defined CONFIG_EHCI_IS_TDI +	reg_ptr = (uint32_t *)((u8 *)hcor + USBMODE); +	tmp = ehci_readl(reg_ptr); +	tmp |= USBMODE_CM_HC; +#if defined CONFIG_EHCI_MMIO_BIG_ENDIAN +	tmp |= USBMODE_BE; +#endif +	ehci_writel(reg_ptr, tmp); +#endif +out: +	return ret;  }  static void *ehci_alloc(size_t sz, size_t align) @@ -170,6 +217,7 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,  	uint32_t endpt, token, usbsts;  	uint32_t c, toggle;  	uint32_t cmd; +	uint32_t sts;  	debug("dev=%p, pipe=%lx, buffer=%p, length=%d, req=%p\n", dev, pipe,  	      buffer, length, req); @@ -277,16 +325,19 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,  	qh_list.qh_link = cpu_to_hc32((uint32_t) qh | QH_LINK_TYPE_QH); -	usbsts = ehci_readl(hcor->or_usbsts); -	ehci_writel(hcor->or_usbsts, (usbsts & 0x3f)); +	usbsts = ehci_readl(&hcor->or_usbsts); +	ehci_writel(&hcor->or_usbsts, (usbsts & 0x3f));  	/* Enable async. schedule. */ -	cmd = ehci_readl(hcor->or_usbcmd); -	hcor->or_usbcmd |= CMD_ASE; -	ehci_writel(hcor->or_usbcmd, cmd); +	cmd = ehci_readl(&hcor->or_usbcmd); +	cmd |= CMD_ASE; +	ehci_writel(&hcor->or_usbcmd, cmd); -	while ((ehci_readl(hcor->or_usbsts) & STD_ASS) == 0) -		udelay(1); +	sts = ehci_readl(&hcor->or_usbsts); +	while ((sts & STD_ASS) == 0) { +		sts = ehci_readl(&hcor->or_usbsts); +		udelay(10); +	}  	/* Wait for TDs to be processed. */  	ts = get_timer(0); @@ -298,11 +349,15 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,  	} while (get_timer(ts) < CONFIG_SYS_HZ);  	/* Disable async schedule. */ -	cmd = ehci_readl(hcor->or_usbcmd); +	cmd = ehci_readl(&hcor->or_usbcmd);  	cmd &= ~CMD_ASE; -	ehci_writel(hcor->or_usbcmd, cmd); -	while ((ehci_readl(hcor->or_usbsts) & STD_ASS) != 0) -		udelay(1); +	ehci_writel(&hcor->or_usbcmd, cmd); + +	sts = ehci_readl(&hcor->or_usbsts); +	while ((sts & STD_ASS) != 0) { +		sts = ehci_readl(&hcor->or_usbsts); +		udelay(10); +	}  	qh_list.qh_link = cpu_to_hc32((uint32_t)&qh_list | QH_LINK_TYPE_QH); @@ -335,9 +390,9 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,  	} else {  		dev->act_len = 0;  		debug("dev=%u, usbsts=%#x, p[1]=%#x, p[2]=%#x\n", -		      dev->devnum, ehci_readl(hcor->or_usbsts), -		      ehci_readl(hcor->or_portsc[0]), -		      ehci_readl(hcor->or_portsc[1])); +		      dev->devnum, ehci_readl(&hcor->or_usbsts), +		      ehci_readl(&hcor->or_portsc[0]), +		      ehci_readl(&hcor->or_portsc[1]));  	}  	return (dev->status != USB_ST_NOT_PROC) ? 0 : -1; @@ -451,7 +506,7 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer,  		break;  	case USB_REQ_GET_STATUS | ((USB_RT_PORT | USB_DIR_IN) << 8):  		memset(tmpbuf, 0, 4); -		reg = ehci_readl(hcor->or_portsc[le16_to_cpu(req->index) +		reg = ehci_readl(&hcor->or_portsc[le16_to_cpu(req->index)  				   - 1]);  		if (reg & EHCI_PS_CS)  			tmpbuf[0] |= USB_PORT_STAT_CONNECTION; @@ -479,9 +534,12 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer,  		srclen = 4;  		break;  	case USB_REQ_SET_FEATURE | ((USB_DIR_OUT | USB_RT_PORT) << 8): -		reg = ehci_readl(hcor->or_portsc[le16_to_cpu(req->index) - 1]); +		reg = ehci_readl(&hcor->or_portsc[le16_to_cpu(req->index) - 1]);  		reg &= ~EHCI_PS_CLEAR;  		switch (le16_to_cpu(req->value)) { +		case USB_PORT_FEAT_ENABLE: +			reg |= EHCI_PS_PE; +			break;  		case USB_PORT_FEAT_POWER:  			reg |= EHCI_PS_PP;  			break; @@ -495,22 +553,22 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer,  			/* Start reset sequence. */  			reg &= ~EHCI_PS_PE;  			reg |= EHCI_PS_PR; -			ehci_writel(hcor->or_portsc[ +			ehci_writel(&hcor->or_portsc[  				le16_to_cpu(req->index) - 1], reg);  			/* Wait for reset to complete. */ -			udelay(500000); +			wait_ms(500);  			/* Terminate reset sequence. */  			reg &= ~EHCI_PS_PR;  			/* TODO: is it only fsl chip that requires this  			 * manual setting of port enable?  			 */  			reg |= EHCI_PS_PE; -			ehci_writel(hcor->or_portsc[ +			ehci_writel(&hcor->or_portsc[  				le16_to_cpu(req->index) - 1], reg);  			/* Wait for HC to complete reset. */ -			udelay(2000); +			wait_ms(10);  			reg = -			    ehci_readl(hcor->or_portsc[le16_to_cpu(req->index) +			    ehci_readl(&hcor->or_portsc[le16_to_cpu(req->index)  							- 1]);  			reg &= ~EHCI_PS_CLEAR;  			if ((reg & EHCI_PS_PE) == 0) { @@ -525,10 +583,10 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer,  			debug("unknown feature %x\n", le16_to_cpu(req->value));  			goto unknown;  		} -		ehci_writel(hcor->or_portsc[le16_to_cpu(req->index) - 1], reg); +		ehci_writel(&hcor->or_portsc[le16_to_cpu(req->index) - 1], reg);  		break;  	case USB_REQ_CLEAR_FEATURE | ((USB_DIR_OUT | USB_RT_PORT) << 8): -		reg = ehci_readl(hcor->or_portsc[le16_to_cpu(req->index) - 1]); +		reg = ehci_readl(&hcor->or_portsc[le16_to_cpu(req->index) - 1]);  		reg &= ~EHCI_PS_CLEAR;  		switch (le16_to_cpu(req->value)) {  		case USB_PORT_FEAT_ENABLE: @@ -537,6 +595,9 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer,  		case USB_PORT_FEAT_C_CONNECTION:  			reg |= EHCI_PS_CSC;  			break; +		case USB_PORT_FEAT_OVER_CURRENT: +			reg |= EHCI_PS_OCC; +			break;  		case USB_PORT_FEAT_C_RESET:  			portreset &= ~(1 << le16_to_cpu(req->index));  			break; @@ -544,7 +605,7 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer,  			debug("unknown feature %x\n", le16_to_cpu(req->value));  			goto unknown;  		} -		ehci_writel(hcor->or_portsc[le16_to_cpu(req->index) - 1], reg); +		ehci_writel(&hcor->or_portsc[le16_to_cpu(req->index) - 1], reg);  		break;  	default:  		debug("Unknown request\n"); @@ -585,6 +646,10 @@ int usb_lowlevel_init(void)  	if (ehci_hcd_init() != 0)  		return -1; +	/* EHCI spec section 4.1 */ +	if (ehci_reset() != 0) +		return -1; +  	/* Set head of reclaim list */  	memset(&qh_list, 0, sizeof(qh_list));  	qh_list.qh_link = cpu_to_hc32((uint32_t)&qh_list | QH_LINK_TYPE_QH); @@ -595,25 +660,31 @@ int usb_lowlevel_init(void)  	qh_list.qh_overlay.qt_token = cpu_to_hc32(0x40);  	/* Set async. queue head pointer. */ -	ehci_writel(hcor->or_asynclistaddr, (uint32_t)&qh_list); +	ehci_writel(&hcor->or_asynclistaddr, (uint32_t)&qh_list); -	reg = ehci_readl(hccr->cr_hcsparams); -	descriptor.hub.bNbrPorts = reg & 0xf; -	printf("NbrPorts %x\n", descriptor.hub.bNbrPorts); +	reg = ehci_readl(&hccr->cr_hcsparams); +	descriptor.hub.bNbrPorts = HCS_N_PORTS(reg); +	printf("Register %x NbrPorts %d\n", reg, descriptor.hub.bNbrPorts);  	if (reg & 0x10000)	/* Port Indicators */  		descriptor.hub.wHubCharacteristics |= 0x80;  	if (reg & 0x10)		/* Port Power Control */  		descriptor.hub.wHubCharacteristics |= 0x01; -	/* take control over the ports */ -	cmd = ehci_readl(hcor->or_configflag); -	cmd |= 1; -	ehci_writel(hcor->or_configflag, cmd); -  	/* Start the host controller. */ -	cmd = ehci_readl(hcor->or_configflag); -	cmd |= 1; -	ehci_writel(hcor->or_usbcmd, cmd); +	cmd = ehci_readl(&hcor->or_usbcmd); +	/* Philips, Intel, and maybe others need CMD_RUN before the +         * root hub will detect new devices (why?); NEC doesn't */ +	cmd &= ~(CMD_LRESET|CMD_IAAD|CMD_PSE|CMD_ASE|CMD_RESET); +	cmd |= CMD_RUN; +	ehci_writel(&hcor->or_usbcmd, cmd); + +	/* take control over the ports */ +	cmd = ehci_readl(&hcor->or_configflag); +	cmd |= FLAG_CF; +	ehci_writel(&hcor->or_configflag, cmd); +	/* unblock posted writes */ +	cmd = ehci_readl(&hcor->or_usbcmd); +	wait_ms(5);  	rootdev = 0; diff --git a/drivers/usb/usb_ehci_fsl.c b/drivers/usb/usb_ehci_fsl.c index 9ca6c562a..81d5d2165 100644 --- a/drivers/usb/usb_ehci_fsl.c +++ b/drivers/usb/usb_ehci_fsl.c @@ -43,7 +43,8 @@ int ehci_hcd_init(void)  	addr = (uint32_t)&(im->usb[0]);  	hccr = (struct ehci_hccr *)(addr + FSL_SKIP_PCI); -	hcor = (struct ehci_hcor *)((uint32_t) hccr + hccr->cr_caplength); +	hcor = (struct ehci_hcor *)((uint32_t) hccr + +			HC_LENGTH(ehci_readl(&hccr->cr_capbase)));  	/* Configure clock */  	clrsetbits_be32(&(im->clk.sccr), MPC83XX_SCCR_USB_MASK, diff --git a/include/usb.h b/include/usb.h index cd6f37a19..b8f2fa256 100644 --- a/include/usb.h +++ b/include/usb.h @@ -181,7 +181,7 @@ struct usb_device {   */  #if defined(CONFIG_USB_UHCI) || defined(CONFIG_USB_OHCI) || \ -	defined(CONFI_USB_EHCI) || defined(CONFIG_USB_OHCI_NEW) || \ +	defined(CONFIG_USB_EHCI) || defined(CONFIG_USB_OHCI_NEW) || \  	defined(CONFIG_USB_SL811HS) || defined(CONFIG_USB_ISP116X_HCD) || \  	defined(CONFIG_USB_R8A66597_HCD) |