summaryrefslogtreecommitdiffstats
path: root/drivers/usb/usb_ehci_core.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/usb_ehci_core.c')
-rw-r--r--drivers/usb/usb_ehci_core.c147
1 files changed, 109 insertions, 38 deletions
diff --git a/drivers/usb/usb_ehci_core.c b/drivers/usb/usb_ehci_core.c
index 869bea3e79..ec50874f71 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;
OpenPOWER on IntegriCloud