summaryrefslogtreecommitdiff
path: root/drivers/usb/host
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/host')
-rw-r--r--drivers/usb/host/Makefile1
-rw-r--r--drivers/usb/host/ehci-faraday.c148
-rw-r--r--drivers/usb/host/ehci-hcd.c74
3 files changed, 214 insertions, 9 deletions
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
index 87a59704db7..98f2a104b7d 100644
--- a/drivers/usb/host/Makefile
+++ b/drivers/usb/host/Makefile
@@ -43,6 +43,7 @@ COBJS-$(CONFIG_USB_EHCI_FSL) += ehci-mpc512x.o
else
COBJS-$(CONFIG_USB_EHCI_FSL) += ehci-fsl.o
endif
+COBJS-$(CONFIG_USB_EHCI_FARADAY) += ehci-faraday.o
COBJS-$(CONFIG_USB_EHCI_EXYNOS) += ehci-exynos.o
COBJS-$(CONFIG_USB_EHCI_MXC) += ehci-mxc.o
COBJS-$(CONFIG_USB_EHCI_MXS) += ehci-mxs.o
diff --git a/drivers/usb/host/ehci-faraday.c b/drivers/usb/host/ehci-faraday.c
new file mode 100644
index 00000000000..86add36ce11
--- /dev/null
+++ b/drivers/usb/host/ehci-faraday.c
@@ -0,0 +1,148 @@
+/*
+ * Faraday USB 2.0 EHCI Controller
+ *
+ * (C) Copyright 2010 Faraday Technology
+ * Dante Su <dantesu@faraday-tech.com>
+ *
+ * This file is released under the terms of GPL v2 and any later version.
+ * See the file COPYING in the root directory of the source tree for details.
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <usb.h>
+#include <usb/fusbh200.h>
+#include <usb/fotg210.h>
+
+#include "ehci.h"
+
+#ifndef CONFIG_USB_EHCI_BASE_LIST
+#define CONFIG_USB_EHCI_BASE_LIST { CONFIG_USB_EHCI_BASE }
+#endif
+
+union ehci_faraday_regs {
+ struct fusbh200_regs usb;
+ struct fotg210_regs otg;
+};
+
+static inline int ehci_is_fotg2xx(union ehci_faraday_regs *regs)
+{
+ return !readl(&regs->usb.easstr);
+}
+
+/*
+ * Create the appropriate control structures to manage
+ * a new EHCI host controller.
+ */
+int ehci_hcd_init(int index, struct ehci_hccr **ret_hccr,
+ struct ehci_hcor **ret_hcor)
+{
+ struct ehci_hccr *hccr;
+ struct ehci_hcor *hcor;
+ union ehci_faraday_regs *regs;
+ uint32_t base_list[] = CONFIG_USB_EHCI_BASE_LIST;
+
+ if (index < 0 || index >= ARRAY_SIZE(base_list))
+ return -1;
+ regs = (void __iomem *)base_list[index];
+ hccr = (struct ehci_hccr *)&regs->usb.hccr;
+ hcor = (struct ehci_hcor *)&regs->usb.hcor;
+
+ if (ehci_is_fotg2xx(regs)) {
+ /* A-device bus reset */
+ /* ... Power off A-device */
+ setbits_le32(&regs->otg.otgcsr, OTGCSR_A_BUSDROP);
+ /* ... Drop vbus and bus traffic */
+ clrbits_le32(&regs->otg.otgcsr, OTGCSR_A_BUSREQ);
+ mdelay(1);
+ /* ... Power on A-device */
+ clrbits_le32(&regs->otg.otgcsr, OTGCSR_A_BUSDROP);
+ /* ... Drive vbus and bus traffic */
+ setbits_le32(&regs->otg.otgcsr, OTGCSR_A_BUSREQ);
+ mdelay(1);
+ /* Disable OTG & DEV interrupts, triggered at level-high */
+ writel(IMR_IRQLH | IMR_OTG | IMR_DEV, &regs->otg.imr);
+ /* Clear all interrupt status */
+ writel(ISR_HOST | ISR_OTG | ISR_DEV, &regs->otg.isr);
+ } else {
+ /* Interrupt=level-high */
+ setbits_le32(&regs->usb.bmcsr, BMCSR_IRQLH);
+ /* VBUS on */
+ clrbits_le32(&regs->usb.bmcsr, BMCSR_VBUS_OFF);
+ /* Disable all interrupts */
+ writel(0x00, &regs->usb.bmier);
+ writel(0x1f, &regs->usb.bmisr);
+ }
+
+ *ret_hccr = hccr;
+ *ret_hcor = hcor;
+
+ return 0;
+}
+
+/*
+ * Destroy the appropriate control structures corresponding
+ * the the EHCI host controller.
+ */
+int ehci_hcd_stop(int index)
+{
+ return 0;
+}
+
+/*
+ * This ehci_set_usbmode() overrides the weak function
+ * in "ehci-hcd.c".
+ */
+void ehci_set_usbmode(int index)
+{
+ /* nothing needs to be done */
+}
+
+/*
+ * This ehci_get_port_speed() overrides the weak function
+ * in "ehci-hcd.c".
+ */
+int ehci_get_port_speed(struct ehci_hcor *hcor, uint32_t reg)
+{
+ int spd, ret = PORTSC_PSPD_HS;
+ union ehci_faraday_regs *regs = (void __iomem *)((ulong)hcor - 0x10);
+
+ if (ehci_is_fotg2xx(regs))
+ spd = OTGCSR_SPD(readl(&regs->otg.otgcsr));
+ else
+ spd = BMCSR_SPD(readl(&regs->usb.bmcsr));
+
+ switch (spd) {
+ case 0: /* full speed */
+ ret = PORTSC_PSPD_FS;
+ break;
+ case 1: /* low speed */
+ ret = PORTSC_PSPD_LS;
+ break;
+ case 2: /* high speed */
+ ret = PORTSC_PSPD_HS;
+ break;
+ default:
+ printf("ehci-faraday: invalid device speed\n");
+ break;
+ }
+
+ return ret;
+}
+
+/*
+ * This ehci_get_portsc_register() overrides the weak function
+ * in "ehci-hcd.c".
+ */
+uint32_t *ehci_get_portsc_register(struct ehci_hcor *hcor, int port)
+{
+ /* Faraday EHCI has one and only one portsc register */
+ if (port) {
+ /* Printing the message would cause a scan failure! */
+ debug("The request port(%d) is not configured\n", port);
+ return NULL;
+ }
+
+ /* Faraday EHCI PORTSC register offset is 0x20 from hcor */
+ return (uint32_t *)((uint8_t *)hcor + 0x20);
+}
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index e0f3e4b6c78..706cf0cb7db 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -589,10 +589,12 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
dev->act_len = length - QT_TOKEN_GET_TOTALBYTES(token);
} else {
dev->act_len = 0;
+#ifndef CONFIG_USB_EHCI_FARADAY
debug("dev=%u, usbsts=%#x, p[1]=%#x, p[2]=%#x\n",
dev->devnum, ehci_readl(&ctrl->hcor->or_usbsts),
ehci_readl(&ctrl->hcor->or_portsc[0]),
ehci_readl(&ctrl->hcor->or_portsc[1]));
+#endif
}
free(qtd);
@@ -603,6 +605,17 @@ fail:
return -1;
}
+__weak uint32_t *ehci_get_portsc_register(struct ehci_hcor *hcor, int port)
+{
+ if (port < 0 || port >= CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS) {
+ /* Printing the message would cause a scan failure! */
+ debug("The request port(%u) is not configured\n", port);
+ return NULL;
+ }
+
+ return (uint32_t *)&hcor->or_portsc[port];
+}
+
int
ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer,
int length, struct devrequest *req)
@@ -616,11 +629,6 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer,
int port = le16_to_cpu(req->index) & 0xff;
struct ehci_ctrl *ctrl = dev->controller;
- if (port > CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS) {
- printf("The request port(%d) is not configured\n", port - 1);
- return -1;
- }
- status_reg = (uint32_t *)&ctrl->hcor->or_portsc[port - 1];
srclen = 0;
debug("req=%u (%#x), type=%u (%#x), value=%u, index=%u\n",
@@ -631,6 +639,19 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer,
typeReq = req->request | req->requesttype << 8;
switch (typeReq) {
+ case USB_REQ_GET_STATUS | ((USB_RT_PORT | USB_DIR_IN) << 8):
+ case USB_REQ_SET_FEATURE | ((USB_DIR_OUT | USB_RT_PORT) << 8):
+ case USB_REQ_CLEAR_FEATURE | ((USB_DIR_OUT | USB_RT_PORT) << 8):
+ status_reg = ehci_get_portsc_register(ctrl->hcor, port - 1);
+ if (!status_reg)
+ return -1;
+ break;
+ default:
+ status_reg = NULL;
+ break;
+ }
+
+ switch (typeReq) {
case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
switch (le16_to_cpu(req->value) >> 8) {
case USB_DT_DEVICE:
@@ -809,21 +830,23 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer,
break;
case USB_REQ_CLEAR_FEATURE | ((USB_DIR_OUT | USB_RT_PORT) << 8):
reg = ehci_readl(status_reg);
+ reg &= ~EHCI_PS_CLEAR;
switch (le16_to_cpu(req->value)) {
case USB_PORT_FEAT_ENABLE:
reg &= ~EHCI_PS_PE;
break;
case USB_PORT_FEAT_C_ENABLE:
- reg = (reg & ~EHCI_PS_CLEAR) | EHCI_PS_PE;
+ reg |= EHCI_PS_PE;
break;
case USB_PORT_FEAT_POWER:
if (HCS_PPC(ehci_readl(&ctrl->hccr->cr_hcsparams)))
- reg = reg & ~(EHCI_PS_CLEAR | EHCI_PS_PP);
+ reg &= ~EHCI_PS_PP;
+ break;
case USB_PORT_FEAT_C_CONNECTION:
- reg = (reg & ~EHCI_PS_CLEAR) | EHCI_PS_CSC;
+ reg |= EHCI_PS_CSC;
break;
case USB_PORT_FEAT_OVER_CURRENT:
- reg = (reg & ~EHCI_PS_CLEAR) | EHCI_PS_OCC;
+ reg |= EHCI_PS_OCC;
break;
case USB_PORT_FEAT_C_RESET:
ctrl->portreset &= ~(1 << port);
@@ -903,6 +926,9 @@ int usb_lowlevel_init(int index, void **controller)
qh_list->qh_overlay.qt_token =
cpu_to_hc32(QT_TOKEN_STATUS(QT_TOKEN_STATUS_HALTED));
+ flush_dcache_range((uint32_t)qh_list,
+ ALIGN_END_ADDR(struct QH, qh_list, 1));
+
/* Set async. queue head pointer. */
ehci_writel(&ehcic[index].hcor->or_asynclistaddr, (uint32_t)qh_list);
@@ -916,6 +942,9 @@ int usb_lowlevel_init(int index, void **controller)
periodic->qh_overlay.qt_next = cpu_to_hc32(QT_NEXT_TERMINATE);
periodic->qh_overlay.qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE);
+ flush_dcache_range((uint32_t)periodic,
+ ALIGN_END_ADDR(struct QH, periodic, 1));
+
/*
* Step 2: Setup frame-list: Every microframe, USB tries the same list.
* In particular, device specifications on polling frequency
@@ -933,6 +962,10 @@ int usb_lowlevel_init(int index, void **controller)
| QH_LINK_TYPE_QH;
}
+ flush_dcache_range((uint32_t)ehcic[index].periodic_list,
+ ALIGN_END_ADDR(uint32_t, ehcic[index].periodic_list,
+ 1024));
+
/* Set periodic list base address */
ehci_writel(&ehcic[index].hcor->or_periodiclistbase,
(uint32_t)ehcic[index].periodic_list);
@@ -959,10 +992,13 @@ int usb_lowlevel_init(int index, void **controller)
cmd |= CMD_RUN;
ehci_writel(&ehcic[index].hcor->or_usbcmd, cmd);
+#ifndef CONFIG_USB_EHCI_FARADAY
/* take control over the ports */
cmd = ehci_readl(&ehcic[index].hcor->or_configflag);
cmd |= FLAG_CF;
ehci_writel(&ehcic[index].hcor->or_configflag, cmd);
+#endif
+
/* unblock posted write */
cmd = ehci_readl(&ehcic[index].hcor->or_usbcmd);
mdelay(5);
@@ -1144,6 +1180,16 @@ create_int_queue(struct usb_device *dev, unsigned long pipe, int queuesize,
*buf = buffer + i * elementsize;
}
+ flush_dcache_range((uint32_t)buffer,
+ ALIGN_END_ADDR(char, buffer,
+ queuesize * elementsize));
+ flush_dcache_range((uint32_t)result->first,
+ ALIGN_END_ADDR(struct QH, result->first,
+ queuesize));
+ flush_dcache_range((uint32_t)result->tds,
+ ALIGN_END_ADDR(struct qTD, result->tds,
+ queuesize));
+
if (disable_periodic(ctrl) < 0) {
debug("FATAL: periodic should never fail, but did");
goto fail3;
@@ -1154,6 +1200,11 @@ create_int_queue(struct usb_device *dev, unsigned long pipe, int queuesize,
result->last->qh_link = list->qh_link;
list->qh_link = (uint32_t)result->first | QH_LINK_TYPE_QH;
+ flush_dcache_range((uint32_t)result->last,
+ ALIGN_END_ADDR(struct QH, result->last, 1));
+ flush_dcache_range((uint32_t)list,
+ ALIGN_END_ADDR(struct QH, list, 1));
+
if (enable_periodic(ctrl) < 0) {
debug("FATAL: periodic should never fail, but did");
goto fail3;
@@ -1184,6 +1235,8 @@ void *poll_int_queue(struct usb_device *dev, struct int_queue *queue)
return NULL;
}
/* still active */
+ invalidate_dcache_range((uint32_t)cur,
+ ALIGN_END_ADDR(struct QH, cur, 1));
if (cur->qh_overlay.qt_token & 0x80) {
debug("Exit poll_int_queue with no completed intr transfer. "
"token is %x\n", cur->qh_overlay.qt_token);
@@ -1290,6 +1343,9 @@ submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
return -EINVAL;
}
+ invalidate_dcache_range((uint32_t)buffer,
+ ALIGN_END_ADDR(char, buffer, length));
+
ret = destroy_int_queue(dev, queue);
if (ret < 0)
return ret;