[PATCH 2.6.34-rc4 3/3] mx5: Enable USB host funcionality on Freescale MX51 Babbage HW

From: Dinh . Nguyen
Date: Thu Apr 15 2010 - 02:25:52 EST


From: Dinh Nguyen <Dinh.Nguyen@xxxxxxxxxxxxx>

This patch enables USB host functionality for Host1 and OTG port on
Freescale MX51 Babbage HW. This patch contains the board specific
HW initialization of the USB HW. Updates mx51_defconfig to enable
USB EHCI.

This patch applies to 2.6.34-rc4.

Signed-off-by: Dinh Nguyen <Dinh.Nguyen@xxxxxxxxxxxxx>
---
arch/arm/configs/mx51_defconfig | 17 ++-
arch/arm/mach-mx5/board-mx51_babbage.c | 267 ++++++++++++++++++++++++++++-
arch/arm/plat-mxc/include/mach/mxc_ehci.h | 48 +++++
3 files changed, 330 insertions(+), 2 deletions(-)

diff --git a/arch/arm/configs/mx51_defconfig b/arch/arm/configs/mx51_defconfig
index c88e952..a708fd6 100644
--- a/arch/arm/configs/mx51_defconfig
+++ b/arch/arm/configs/mx51_defconfig
@@ -809,7 +809,22 @@ CONFIG_SSB_POSSIBLE=y
CONFIG_DUMMY_CONSOLE=y
# CONFIG_SOUND is not set
# CONFIG_HID_SUPPORT is not set
-# CONFIG_USB_SUPPORT is not set
+CONFIG_USB_SUPPORT=y
+CONFIG_USB_ARCH_HAS_HCD=y
+# CONFIG_USB_ARCH_HAS_OHCI is not set
+CONFIG_USB_ARCH_HAS_EHCI=y
+CONFIG_USB=y
+
+#
+# USB Host Controller Drivers
+#
+# CONFIG_USB_C67X00_HCD is not set
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_EHCI_ROOT_HUB_TT=y
+# CONFIG_USB_EHCI_TT_NEWSCHED is not set
+CONFIG_USB_EHCI_MXC=y
+
+
CONFIG_MMC=y
# CONFIG_MMC_DEBUG is not set
# CONFIG_MMC_UNSAFE_RESUME is not set
diff --git a/arch/arm/mach-mx5/board-mx51_babbage.c b/arch/arm/mach-mx5/board-mx51_babbage.c
index ee67a71..9dc46cf 100644
--- a/arch/arm/mach-mx5/board-mx51_babbage.c
+++ b/arch/arm/mach-mx5/board-mx51_babbage.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright (C) 2009-2010 Freescale Semiconductor, Inc. All Rights Reserved.
* Copyright (C) 2009-2010 Amit Kucheria <amit.kucheria@xxxxxxxxxxxxx>
*
* The code contained herein is licensed under the GNU General Public
@@ -12,11 +12,15 @@

#include <linux/init.h>
#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/io.h>
+#include <linux/delay.h>

#include <mach/common.h>
#include <mach/hardware.h>
#include <mach/imx-uart.h>
#include <mach/iomux-mx51.h>
+#include <mach/mxc_ehci.h>

#include <asm/irq.h>
#include <asm/setup.h>
@@ -26,6 +30,8 @@

#include "devices.h"

+#define GPIO_USB_HUB_RESET 7 /* GPIO_1_7 */
+
static struct platform_device *devices[] __initdata = {
&mxc_fec_device,
};
@@ -46,6 +52,22 @@ static struct pad_desc mx51babbage_pads[] = {
MX51_PAD_EIM_D26__UART3_TXD,
MX51_PAD_EIM_D27__UART3_RTS,
MX51_PAD_EIM_D24__UART3_CTS,
+
+ /* USB HOST1 */
+ MX51_PAD_USBH1_CLK__USBH1_CLK,
+ MX51_PAD_USBH1_DIR__USBH1_DIR,
+ MX51_PAD_USBH1_NXT__USBH1_NXT,
+ MX51_PAD_USBH1_DATA0__USBH1_DATA0,
+ MX51_PAD_USBH1_DATA1__USBH1_DATA1,
+ MX51_PAD_USBH1_DATA2__USBH1_DATA2,
+ MX51_PAD_USBH1_DATA3__USBH1_DATA3,
+ MX51_PAD_USBH1_DATA4__USBH1_DATA4,
+ MX51_PAD_USBH1_DATA5__USBH1_DATA5,
+ MX51_PAD_USBH1_DATA6__USBH1_DATA6,
+ MX51_PAD_USBH1_DATA7__USBH1_DATA7,
+
+ /* USB HUB reset line*/
+ MX51_PAD_GPIO_1_7__GPIO1_7,
};

/* Serial ports */
@@ -66,15 +88,258 @@ static inline void mxc_init_imx_uart(void)
}
#endif /* SERIAL_IMX */

+static int babbage_usbotg_init(struct platform_device *pdev)
+{
+ u32 reg_value;
+ void __iomem *usb_base;
+ u32 usbotg_base;
+ u32 usbother_base;
+ int timeout;
+ int ret = 0;
+
+ usb_base = ioremap(MX51_OTG_BASE_ADDR, SZ_4K);
+ usbotg_base = (u32)usb_base + USBOTG_OFFSET;
+ usbother_base = (u32)usb_base + USBOTHER_REGS_OFFSET;
+
+ /* Stop then Reset */
+ reg_value = __raw_readl(usbotg_base + USBCMD_OFFSET);
+ reg_value &= ~UCMD_RUN_STOP;
+ __raw_writel(reg_value, usbotg_base + USBCMD_OFFSET);
+ timeout = 0x100000;
+ while (--timeout && __raw_readl(usbotg_base + USBCMD_OFFSET) & UCMD_RUN_STOP)
+ cpu_relax();
+ if (!timeout) {
+ printk(KERN_ERR "%s could not stop usb hardware\n", __func__);
+ ret = -ETIMEDOUT;
+ goto error;
+ }
+
+ reg_value = __raw_readl(usbotg_base + USBCMD_OFFSET);
+ reg_value |= UCMD_RESET;
+ __raw_writel(reg_value, usbotg_base + USBCMD_OFFSET);
+ timeout = 0x100000;
+ while (--timeout && __raw_readl(usbotg_base + USBCMD_OFFSET) & UCMD_RESET)
+ cpu_relax();
+ if (!timeout) {
+ printk(KERN_ERR "%s could not reset usb hardware\n", __func__);
+ ret = -ETIMEDOUT;
+ goto error;
+ }
+
+ reg_value = __raw_readl(usbother_base + USB_PHY_CTR_FUNC_OFFSET);
+ reg_value |= USB_UTMI_PHYCTRL_OC_DIS; /* OC is not used */
+ __raw_writel(reg_value, usbother_base + USB_PHY_CTR_FUNC_OFFSET);
+
+ reg_value = __raw_readl(usbother_base + USBCTRL_OFFSET);
+ reg_value &= ~(UCTRL_OPM | UCTRL_OWIE);/* OTG wakeup/power mask disable */
+ __raw_writel(reg_value, usbother_base + USBCTRL_OFFSET);
+
+ /* set UTMI xcvr */
+ reg_value = __raw_readl(usbotg_base + PORTSC_OFFSET);
+ reg_value &= ~MXC_EHCI_MODE_SERIAL;
+ __raw_writel(reg_value |= MXC_EHCI_MODE_UTMI, usbotg_base + PORTSC_OFFSET);
+
+ /* Set the PHY clock to 19.2MHz */
+ reg_value = __raw_readl(usbother_base + USB_PHY_CTR_FUNC2_OFFSET);
+ reg_value &= ~USB_UTMI_PHYCTRL2_PLLDIV_MASK;
+ reg_value |= 0x01;
+ __raw_writel(reg_value, usbother_base + USB_PHY_CTR_FUNC2_OFFSET);
+
+ /* Workaround an IC issue for ehci driver:
+ * when turn off root hub port power, EHCI set
+ * PORTSC reserved bits to be 0, but PTW with 0
+ * means 8 bits tranceiver width, here change
+ * it back to be 16 bits and do PHY diable and
+ * then enable.
+ */
+ reg_value = __raw_readl(usbotg_base + PORTSC_OFFSET);
+ reg_value |= MXC_EHCI_UTMI_16BIT;
+ __raw_writel(reg_value, usbotg_base + PORTSC_OFFSET);
+
+ /* need to reset the controller here so that the ID pin
+ * is correctly detected.
+ */
+ /* Stop then Reset */
+ reg_value = __raw_readl(usbotg_base + USBCMD_OFFSET);
+ reg_value &= ~UCMD_RUN_STOP;
+ __raw_writel(reg_value, usbotg_base + USBCMD_OFFSET);
+ timeout = 0x100000;
+ while (--timeout && __raw_readl(usbotg_base + USBCMD_OFFSET) & UCMD_RUN_STOP)
+ cpu_relax();
+ if (!timeout) {
+ printk(KERN_ERR "%s could not stop usb hardware\n", __func__);
+ ret = -ETIMEDOUT;
+ goto error;
+ }
+
+ reg_value = __raw_readl(usbotg_base + USBCMD_OFFSET);
+ reg_value |= UCMD_RESET;
+ __raw_writel(reg_value, usbotg_base + USBCMD_OFFSET);
+ timeout = 0x100000;
+ while (--timeout && __raw_readl(usbotg_base + USBCMD_OFFSET) & UCMD_RESET)
+ cpu_relax();
+ if (!timeout) {
+ printk(KERN_ERR "%s could not reset usb hardware\n", __func__);
+ ret = -ETIMEDOUT;
+ goto error;
+ }
+
+ /* allow controller to reset, and leave time for
+ * the ULPI transceiver to reset too.
+ */
+ msleep(100);
+error:
+ iounmap(usb_base);
+
+ pr_debug("%s: success\n", __func__);
+ return ret;
+}
+
+static struct mxc_usbh_platform_data dr_utmi_config = {
+ .init = babbage_usbotg_init,
+ .portsc = MXC_EHCI_UTMI_16BIT,
+ .flags = MXC_EHCI_INTERNAL_PHY,
+};
+
+static int babbage_usbh1_init(struct platform_device *pdev)
+{
+ u32 reg_value;
+ void __iomem *usb_base;
+ u32 usbh1_base;
+ u32 usbother_base;
+ int timeout;
+ int ret = 0;
+
+ usb_base = ioremap(MX51_OTG_BASE_ADDR, SZ_4K);
+ usbh1_base = (u32)usb_base + USBH1_OFFSET;
+ usbother_base = (u32)usb_base + USBOTHER_REGS_OFFSET;
+
+ /* Stop then Reset */
+ reg_value = __raw_readl(usbh1_base + USBCMD_OFFSET);
+ reg_value &= ~UCMD_RUN_STOP;
+ __raw_writel(reg_value, usbh1_base + USBCMD_OFFSET);
+ timeout = 0x100000;
+ while (--timeout && __raw_readl(usbh1_base + USBCMD_OFFSET) & UCMD_RUN_STOP)
+ cpu_relax();
+ if (!timeout) {
+ printk(KERN_ERR "%s could not stop usb hardware\n", __func__);
+ ret = -ETIMEDOUT;
+ goto error;
+ }
+
+ reg_value = __raw_readl(usbh1_base + USBCMD_OFFSET);
+ reg_value |= UCMD_RESET;
+ __raw_writel(reg_value, usbh1_base + USBCMD_OFFSET);
+ timeout = 0x100000;
+ while (--timeout && __raw_readl(usbh1_base + USBCMD_OFFSET) & UCMD_RESET)
+ cpu_relax();
+ if (!timeout) {
+ printk(KERN_ERR "%s could not reset usb hardware\n", __func__);
+ ret = -ETIMEDOUT;
+ goto error;
+ }
+
+ reg_value = __raw_readl(usbother_base + USB_CTRL_1_OFFSET);
+ __raw_writel(reg_value | USB_CTRL_UH1_EXT_CLK_EN, usbother_base + USB_CTRL_1_OFFSET);
+
+ /* select ULPI PHY PTS=2 */
+ reg_value = __raw_readl(usbh1_base + PORTSC_OFFSET);
+ reg_value &= ~MXC_EHCI_MODE_SERIAL;
+ __raw_writel(reg_value |= MXC_EHCI_MODE_ULPI, usbh1_base + PORTSC_OFFSET);
+
+ reg_value = __raw_readl(usbother_base + USBCTRL_OFFSET);
+ reg_value &= ~(UCTRL_H1WIE | UCTRL_H1UIE);/* HOST1 wakeup/ULPI intr disable */
+ reg_value |= UCTRL_H1PM; /* HOST1 power mask */
+ __raw_writel(reg_value, usbother_base + USBCTRL_OFFSET);
+
+ reg_value = __raw_readl(usbother_base + USB_PHY_CTR_FUNC_OFFSET);
+ reg_value |= USB_UH1_OC_DIS; /* OC is not used */
+ __raw_writel(reg_value, usbother_base + USB_PHY_CTR_FUNC_OFFSET);
+
+ reg_value = __raw_readl(usbh1_base + USBCMD_OFFSET);
+ /* Interrupt Threshold Control:Immediate (no threshold) */
+ reg_value &= UCMD_ITC_NO_THRESHOLD;
+ __raw_writel(reg_value, usbh1_base + USBCMD_OFFSET);
+
+ /* reset the controller */
+ reg_value = __raw_readl(usbh1_base + USBCMD_OFFSET);
+ reg_value |= UCMD_RESET;
+ __raw_writel(reg_value, usbh1_base + USBCMD_OFFSET);
+
+error:
+ iounmap(usb_base);
+
+ /* allow controller to reset, and leave time for
+ * the ULPI transceiver to reset too.
+ */
+ msleep(100);
+ return ret;
+}
+
+static int gpio_usbh1_active(void)
+{
+ struct pad_desc usbh1stp_gpio = MX51_PAD_USBH1_STP__GPIO_1_27;
+ int ret;
+
+ /* Set USBH1_STP to GPIO and toggle it */
+ mxc_iomux_v3_setup_pad(&usbh1stp_gpio);
+ ret = gpio_request(27, "usbh1_stp");
+
+ if (ret) {
+ pr_debug("failed to get MX51_PAD_USBH1_STP__GPIO_1_27: %d\n", ret);
+ return ret;
+ }
+ gpio_direction_output(27, 0);
+ gpio_set_value(27, 1);
+ msleep(100);
+ gpio_free(27);
+ return 0;
+}
+
+static struct mxc_usbh_platform_data usbh1_config = {
+ .init = babbage_usbh1_init,
+ .portsc = MXC_EHCI_MODE_ULPI,
+ .flags = MXC_EHCI_POWER_PINS_ENABLED,
+};
+
+static inline void babbage_usbhub_reset(void)
+{
+ int ret;
+
+ /* Bring USB hub out of reset */
+ ret = gpio_request(GPIO_USB_HUB_RESET, "GPIO1_7");
+ if (ret) {
+ printk("failed to get GPIO_USB_HUB_RESET: %d\n", ret);
+ return;
+ }
+ gpio_direction_output(GPIO_USB_HUB_RESET, 0);
+
+ /* USB HUB RESET - De-assert USB HUB RESET_N */
+ msleep(1);
+ gpio_set_value(GPIO_USB_HUB_RESET, 0);
+ msleep(1);
+ gpio_set_value(GPIO_USB_HUB_RESET, 1);
+}
+
/*
* Board specific initialization.
*/
static void __init mxc_board_init(void)
{
+ struct pad_desc usbh1stp = MX51_PAD_USBH1_STP__USBH1_STP;
+
mxc_iomux_v3_setup_multiple_pads(mx51babbage_pads,
ARRAY_SIZE(mx51babbage_pads));
mxc_init_imx_uart();
platform_add_devices(devices, ARRAY_SIZE(devices));
+
+ mxc_register_device(&mxc_usbdr_host_device, &dr_utmi_config);
+
+ gpio_usbh1_active();
+ mxc_register_device(&mxc_usbh1_device, &usbh1_config);
+ /* setback USBH1_STP to be function */
+ mxc_iomux_v3_setup_pad(&usbh1stp);
+ babbage_usbhub_reset();
}

static void __init mx51_babbage_timer_init(void)
diff --git a/arch/arm/plat-mxc/include/mach/mxc_ehci.h b/arch/arm/plat-mxc/include/mach/mxc_ehci.h
index 4b9b836..728eb36 100644
--- a/arch/arm/plat-mxc/include/mach/mxc_ehci.h
+++ b/arch/arm/plat-mxc/include/mach/mxc_ehci.h
@@ -1,6 +1,30 @@
#ifndef __INCLUDE_ASM_ARCH_MXC_EHCI_H
#define __INCLUDE_ASM_ARCH_MXC_EHCI_H

+#define USBOTG_OFFSET 0
+#define USBH1_OFFSET 0x200
+#define USBH2_OFFSET 0x400
+#define USBH3_OFFSET 0x600
+#define USBOTHER_REGS_OFFSET 0x800
+
+#define USBCMD_OFFSET 0x140
+
+#define ULPI_VIEWPORT_OFFSET 0x170
+#define PORTSC_OFFSET 0x184
+#define USBMODE_OFFSET 0x1a8
+#define USBMODE_CM_HOST 3
+
+#define USBCTRL_OFFSET 0
+#define USB_PHY_CTR_FUNC_OFFSET 0x8
+#define USB_PHY_CTR_FUNC2_OFFSET 0xc
+#define USB_CTRL_1_OFFSET 0x10
+
+
+/* USBCMD */
+#define UCMD_RUN_STOP (1 << 0) /* controller run/stop */
+#define UCMD_RESET (1 << 1) /* controller reset */
+#define UCMD_ITC_NO_THRESHOLD (~(0xff << 16)) /* Interrupt Threshold Control */
+
/* values for portsc field */
#define MXC_EHCI_PHY_LOW_POWER_SUSPEND (1 << 23)
#define MXC_EHCI_FORCE_FS (1 << 24)
@@ -26,6 +50,30 @@
#define MXC_EHCI_IPPUE_DOWN (1 << 8)
#define MXC_EHCI_IPPUE_UP (1 << 9)

+/* USB_CTRL */
+#define UCTRL_OUIE (1 << 28) /* OTG ULPI intr enable */
+#define UCTRL_OWIE (1 << 27) /* OTG wakeup intr enable */
+#define UCTRL_OBPVAL_RXDP (1 << 26) /* OTG RxDp status in bypass mode */
+#define UCTRL_OBPVAL_RXDM (1 << 25) /* OTG RxDm status in bypass mode */
+#define UCTRL_OPM (1 << 24) /* OTG power mask */
+#define UCTRL_H1UIE (1 << 12) /* Host1 ULPI interrupt enable */
+#define UCTRL_H1WIE (1 << 11) /* HOST1 wakeup intr enable */
+#define UCTRL_H1PM (1 << 8) /* HOST1 power mask */
+
+/* USB_PHY_CTRL_FUNC */
+#define USB_UTMI_PHYCTRL_OC_DIS (1 << 8) /* OTG Disable Overcurrent Event */
+#define USB_UH1_OC_DIS (1 << 5) /* UH1 Disable Overcurrent Event */
+
+/* USB_CTRL_1 */
+#define USB_CTRL_UH1_EXT_CLK_EN (1 << 25)
+#define USB_CTRL_UH2_EXT_CLK_EN (1 << 26)
+
+/* USB_PHY_CTRL_FUNC2*/
+#define USB_UTMI_PHYCTRL2_PLLDIV_MASK 0x3
+#define USB_UTMI_PHYCTRL2_PLLDIV_SHIFT 0
+#define USB_UTMI_PHYCTRL2_HSDEVSEL_MASK 0x3
+#define USB_UTMI_PHYCTRL2_HSDEVSEL_SHIFT 19
+
struct mxc_usbh_platform_data {
int (*init)(struct platform_device *pdev);
int (*exit)(struct platform_device *pdev);
--
1.6.0.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/