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

Dinh.Nguyen at freescale.com Dinh.Nguyen at freescale.com
Thu Apr 15 02:25:18 EDT 2010


From: Dinh Nguyen <Dinh.Nguyen at freescale.com>

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 at freescale.com>
---
 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 at canonical.com>
  *
  * 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




More information about the linux-arm-kernel mailing list