[PATCH] This patch add varius fix to the ehci. - fix ehci_readl, ehci_writel - introduce new define in ehci.h - introduce the handshake function for waiting on a register - fix usb_ehci_fsl with the new HC_LENGTH macro

michael michael at panicking.retis
Thu Dec 11 13:43:55 CET 2008


Signed-off-by: Michael Trimarchi <trimarchimichael at yahoo.it>
---
 drivers/usb/usb_ehci.h      |   27 +++++++--
 drivers/usb/usb_ehci_core.c |  147 ++++++++++++++++++++++++++++++++-----------
 drivers/usb/usb_ehci_fsl.c  |    3 +-
 include/usb.h               |    2 +-
 4 files changed, 134 insertions(+), 45 deletions(-)

diff --git a/drivers/usb/usb_ehci.h b/drivers/usb/usb_ehci.h
index 3e7a2ab..90b137a 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 869bea3..ec50874 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);
-
-	while ((ehci_readl(hcor->or_usbsts) & STD_ASS) == 0)
-		udelay(1);
+	cmd = ehci_readl(&hcor->or_usbcmd);
+	cmd |= CMD_ASE;
+	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);
+	}
 
 	/* 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 9ca6c56..81d5d21 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 cd6f37a..b8f2fa2 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)
 
-- 
1.5.6.5



More information about the U-Boot mailing list