[U-Boot] [PATCH 04/11] usb-ehci: add Faraday USB 2.0 EHCI controller support

Kuo-Jung Su
2013-03-29T07:06:21Z

This patch add supports to both Faraday FUSBH200 and FOTG210,
these controllers slightly differ from standard EHCI specification.

Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com>
---
common/usb_hub.c | 5 ++
drivers/usb/host/Makefile | 1 +
drivers/usb/host/ehci-faraday.c | 157 +++++++++++++++++++++++++++++++++++++++
drivers/usb/host/ehci-hcd.c | 11 +++
drivers/usb/host/ehci.h | 5 ++
5 files changed, 179 insertions(+)
create mode 100644 drivers/usb/host/ehci-faraday.c

diff --git a/common/usb_hub.c b/common/usb_hub.c
index b5eeb62..099696e 100644
--- a/common/usb_hub.c
+++ b/common/usb_hub.c
@@ -375,6 +375,11 @@ static int usb_hub_configure(struct usb_device *dev)
return -1;
}

+#ifdef CONFIG_USB_EHCI_FARADAY
+ /* dante: fusbh200 requires a long long delay ... */
+ mdelay(250);
+#endif
+
if (usb_get_hub_status(dev, buffer) < 0) {
USB_HUB_PRINTF("usb_hub_configure: failed to get Status %lX\n",
dev->status);
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
index 9a6f982..d5577bd 100644
--- a/drivers/usb/host/Makefile
+++ b/drivers/usb/host/Makefile
@@ -44,6 +44,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 0000000..a5a6394
--- /dev/null
+++ b/drivers/usb/host/ehci-faraday.c
@@ -0,0 +1,157 @@
+/*
+ * 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 <usb.h>
+
+#include "ehci.h"
+
+#ifndef BIT
+#define BIT(nr) (1UL << (nr))
+#endif
+
+/* Lower Timing for FPGA Mode */
+#define CFG_LOWER_TIMING 0
+
+struct faraday_usb_hcd {
+ uint32_t iobase;
+};
+
+static struct faraday_usb_hcd faraday_usb_hcd_info[] = {
+#ifdef CONFIG_USB_EHCI_BASE
+ { .iobase = CONFIG_USB_EHCI_BASE, },
+#endif
+#ifdef CONFIG_USB_EHCI_BASE1
+ { .iobase = CONFIG_USB_EHCI_BASE1, },
+#endif
+};
+
+#define HCD_REG32(chip, off) \
+ *(volatile uint32_t *)((chip)->iobase + (off))
+
+static inline int ehci_hci_fotg2xx(struct ehci_hccr *hccr)
+{
+ uint32_t iobase = (uint32_t)hccr;
+ return !REG32(iobase + 0x34) || REG32(iobase + 0x34) == 0xffffffff;
+}
+
+/*
+ * 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 faraday_usb_hcd *hcd = &faraday_usb_hcd_info[index];
+ struct ehci_hccr *hccr;
+ struct ehci_hcor *hcor;
+
+ hccr = (struct ehci_hccr *)hcd->iobase;
+ hcor = (struct ehci_hcor *)(hcd->iobase +
+ HC_LENGTH(ehci_readl(&hccr->cr_capbase)));
+
+ if (ehci_hci_fotg2xx(hccr)) {
+
+ /* A-device bus reset */
+ HCD_REG32(hcd, 0x80) |= BIT(5);
+ HCD_REG32(hcd, 0x80) &= ~BIT(4);
+ mdelay(1);
+ HCD_REG32(hcd, 0x80) &= ~BIT(5);
+ HCD_REG32(hcd, 0x80) |= BIT(4);
+ mdelay(1);
+
+ /* Reset interrupt */
+ HCD_REG32(hcd, 0xC0) = 7;
+
+ /* Disable OTG & device interrupts */
+ HCD_REG32(hcd, 0xC4) = 3;
+
+ /* Set interrupt polarity to active high */
+ HCD_REG32(hcd, 0xC4) |= BIT(3);
+
+ /* In FPGA mode (15MHz <= AHB <= 30MHz), enter half speed mode. */
+ if (clk_get_rate("AHB") <= 30000000) {
+ HCD_REG32(hcd, 0x100) |= BIT(1);
+ printf("fotg210: AHB is too slow, enter half-speed mode.\n");
+ }
+
+#ifdef CFG_LOWER_TIMING
+ HCD_REG32(hcd, 0x40) |= 0x0d;
+#endif
+
+ } else {
+
+ /* Set interrupt polarity to active high */
+ HCD_REG32(hcd, 0x40) |= BIT(3);
+
+ /* In FPGA mode (15MHz <= AHB <= 30MHz), enter half speed mode. */
+ if (clk_get_rate("AHB") <= 30000000) {
+ HCD_REG32(hcd, 0x40) |= BIT(2);
+ printf("fusbh200: AHB is too slow, enter half-speed mode.\n");
+ }
+
+#ifdef CFG_LOWER_TIMING
+ HCD_REG32(hcd, 0x34) |= (3 << 2) | (1 << 0);
+#endif
+
+ /* Turn on VBUS */
+ HCD_REG32(hcd, 0x40) &= ~BIT(4);
+
+ /* Enable over-current & vbus error interrupts */
+ HCD_REG32(hcd, 0x44) = 0x1F;
+ HCD_REG32(hcd, 0x48) |= BIT(1) | BIT(0);
+
+ }
+
+ *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;
+}
+
+int ehci_hcd_port_speed(struct ehci_hccr *hccr)
+{
+ int rc = 0;
+ int speed;
+ uint32_t iobase = (uint32_t)hccr;
+
+ if (ehci_hci_fotg2xx(hccr))
+ speed = (REG32(iobase + 0x80) >> 22) & 0x03;
+ else
+ speed = (REG32(iobase + 0x40) >> 9) & 0x03;
+
+ switch (speed) {
+ case 0: /* full speed */
+ break;
+
+ case 1: /* low speed */
+ rc = USB_PORT_STAT_LOW_SPEED;
+ break;
+
+ case 2: /* high speed */
+ rc = USB_PORT_STAT_HIGH_SPEED;
+ break;
+
+ default:
+ printf("faraday-usb: invalid device speed\n");
+ break;
+ }
+
+ return rc;
+}
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index c816878..450d217 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -149,8 +149,10 @@ static int handshake(uint32_t *ptr, uint32_t mask, uint32_t done, int usec)
static int ehci_reset(int index)
{
uint32_t cmd;
+#ifndef CONFIG_USB_EHCI_FARADAY
uint32_t tmp;
uint32_t *reg_ptr;
+#endif
int ret = 0;

cmd = ehci_readl(&ehcic[index].hcor->or_usbcmd);
@@ -163,6 +165,7 @@ static int ehci_reset(int index)
goto out;
}

+#ifndef CONFIG_USB_EHCI_FARADAY
if (ehci_is_TDI()) {
reg_ptr = (uint32_t *)((u8 *)ehcic[index].hcor + USBMODE);
tmp = ehci_readl(reg_ptr);
@@ -172,6 +175,7 @@ static int ehci_reset(int index)
#endif
ehci_writel(reg_ptr, tmp);
}
+#endif /* !CONFIG_USB_EHCI_FARADAY */

#ifdef CONFIG_USB_EHCI_TXFIFO_THRESH
cmd = ehci_readl(&ehcic[index].hcor->or_txfilltuning);
@@ -711,6 +715,9 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer,
tmpbuf[1] |= USB_PORT_STAT_POWER >> 8;

if (ehci_is_TDI()) {
+#ifdef CONFIG_USB_EHCI_FARADAY
+ tmpbuf[1] |= ehci_hcd_port_speed(ctrl->hccr) >> 8;
+#else
switch (PORTSC_PSPD(reg)) {
case PORTSC_PSPD_FS:
break;
@@ -722,6 +729,7 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer,
tmpbuf[1] |= USB_PORT_STAT_HIGH_SPEED >> 8;
break;
}
+#endif
} else {
tmpbuf[1] |= USB_PORT_STAT_HIGH_SPEED >> 8;
}
@@ -950,10 +958,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);
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index d090f0a..9309ede 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -82,6 +82,7 @@ struct ehci_hcor {
uint32_t or_periodiclistbase;
uint32_t or_asynclistaddr;
uint32_t _reserved_0_;
+#ifndef CONFIG_USB_EHCI_FARADAY
uint32_t or_burstsize;
uint32_t or_txfilltuning;
#define TXFIFO_THRESH_MASK (0x3f << 16)
@@ -89,6 +90,7 @@ struct ehci_hcor {
uint32_t _reserved_1_[6];
uint32_t or_configflag;
#define FLAG_CF (1 << 0) /* true: we'll support "high speed" */
+#endif /* #ifndef CONFIG_USB_EHCI_FARADAY */
uint32_t or_portsc[CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS];
#define PORTSC_PSPD(x) (((x) >> 26) & 0x3)
#define PORTSC_PSPD_FS 0x0
@@ -255,5 +257,8 @@ struct QH {
/* Low level init functions */
int ehci_hcd_init(int index, struct ehci_hccr **hccr, struct ehci_hcor **hcor);
int ehci_hcd_stop(int index);
+#ifdef CONFIG_USB_EHCI_FARADAY
+int ehci_hcd_port_speed(struct ehci_hccr *hccr);
+#endif

#endif /* USB_EHCI_H */
--
1.7.9.5