summaryrefslogtreecommitdiff
path: root/arch/arm/mach-mx6/usb_h3.c
diff options
context:
space:
mode:
authorPeter Chen <peter.chen@freescale.com>2011-11-03 13:57:25 +0800
committerJason Liu <r64343@freescale.com>2012-07-20 13:17:13 +0800
commit3dc34bfa723c87cc7542f15d2ba665ad8dcb1e2d (patch)
treeebf34891215a8fc3975389708187f2bb13661a44 /arch/arm/mach-mx6/usb_h3.c
parent9baf50f313c2e3072f0889e2df44166231a75ee1 (diff)
ENGR00161314-1 mx6q usb-host: add hsic support
MSL part Add HSIC support for Host2 and Host3, for HSIC mode, there is not usb phy needed, the usb device is always at the board - Validation hardware: iMX6Q Validation Port Card and Re-worked Rev X3 board, for hardware rework detail, contact Ken Sun (b03826) - Validation device: HSIC interface SMSC HUB(USB4640) and Host 3. Host 2 is coding finishes, but not verified due to hardware limitation. - Pin Conflict with Ethernet, order to use HSIC, the user need disable ethernet function at both u-boot and linux kernel. For u-boot: please undefine CONFIG_MXC_FEC at your board config file For kernel: please define CONFIG_USB_EHCI_ARC_HSIC, the entry is: Device Drivers---> USB support---> Support HSIC Host controller for Freescale SoC - Suspend/resume and wakeup are not supported due to IC issues, these IC issues will be fixed at TO1.1 for i.mx6, software will add these support after receiving TO1.1 chip. Signed-off-by: Peter Chen <peter.chen@freescale.com>
Diffstat (limited to 'arch/arm/mach-mx6/usb_h3.c')
-rw-r--r--arch/arm/mach-mx6/usb_h3.c216
1 files changed, 216 insertions, 0 deletions
diff --git a/arch/arm/mach-mx6/usb_h3.c b/arch/arm/mach-mx6/usb_h3.c
new file mode 100644
index 000000000000..43c2b1208eed
--- /dev/null
+++ b/arch/arm/mach-mx6/usb_h3.c
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * High Speed Inter Chip code for i.MX6, this file is for HSIC port 2
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/fsl_devices.h>
+#include <linux/gpio.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <mach/arc_otg.h>
+#include <mach/hardware.h>
+#include <mach/iomux-mx6q.h>
+#include "devices-imx6q.h"
+#include "regs-anadig.h"
+#include "usb.h"
+
+static struct clk *usb_oh3_clk;
+static struct clk *usb_phy4_clk;
+
+extern int clk_get_usecount(struct clk *clk);
+static struct fsl_usb2_platform_data usbh3_config;
+
+static void usbh3_internal_phy_clock_gate(bool on)
+{
+ if (on) {
+ USB_H3_CTRL |= UCTRL_UTMI_ON_CLOCK;
+ USB_UH3_HSIC_CTRL |= HSIC_CLK_ON;
+ } else {
+ USB_UH3_HSIC_CTRL &= ~HSIC_CLK_ON;
+ USB_H3_CTRL &= ~UCTRL_UTMI_ON_CLOCK;
+ }
+}
+
+static int fsl_usb_host_init_ext(struct platform_device *pdev)
+{
+ int ret;
+ struct clk *usb_clk;
+ usb_clk = clk_get(NULL, "usboh3_clk");
+ clk_enable(usb_clk);
+ usb_oh3_clk = usb_clk;
+
+ usb_clk = clk_get(NULL, "usb_phy4_clk");
+ clk_enable(usb_clk);
+ usb_phy4_clk = usb_clk;
+
+ ret = fsl_usb_host_init(pdev);
+ if (ret) {
+ printk(KERN_ERR "host1 init fails......\n");
+ return ret;
+ }
+ usbh3_internal_phy_clock_gate(true);
+
+ /* Host3 HSIC enable */
+ USB_UH3_HSIC_CTRL |= HSIC_EN;
+
+ return 0;
+}
+
+static void fsl_usb_host_uninit_ext(struct platform_device *pdev)
+{
+ struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data;
+
+ fsl_usb_host_uninit(pdata);
+
+ usbh3_internal_phy_clock_gate(false);
+
+ clk_disable(usb_phy4_clk);
+ clk_put(usb_phy4_clk);
+
+ clk_disable(usb_oh3_clk);
+ clk_put(usb_oh3_clk);
+
+}
+
+static void usbh3_clock_gate(bool on)
+{
+ pr_debug("%s: on is %d\n", __func__, on);
+ if (on) {
+ clk_enable(usb_oh3_clk);
+ clk_enable(usb_phy4_clk);
+ usbh3_internal_phy_clock_gate(true);
+ } else {
+ usbh3_internal_phy_clock_gate(false);
+ clk_disable(usb_phy4_clk);
+ clk_disable(usb_oh3_clk);
+ }
+}
+
+void mx6_set_host3_vbus_func(driver_vbus_func driver_vbus)
+{
+ usbh3_config.platform_driver_vbus = driver_vbus;
+}
+
+static void _wake_up_enable(struct fsl_usb2_platform_data *pdata, bool enable)
+{
+ pr_debug("host3, %s, enable is %d\n", __func__, enable);
+ if (enable) {
+ USB_H3_CTRL |= (UCTRL_OWIE);
+ } else {
+ USB_H3_CTRL &= ~(UCTRL_OWIE);
+ /* The interrupt must be disabled for at least 3
+ * cycles of the standby clock(32k Hz) , that is 0.094 ms*/
+ udelay(100);
+ }
+}
+
+static void _phy_lowpower_suspend(struct fsl_usb2_platform_data *pdata, bool enable)
+{
+ pr_debug("host3, %s, enable is %d\n", __func__, enable);
+ if (enable)
+ UH3_PORTSC1 |= PORTSC_PHCD;
+ else
+ UH3_PORTSC1 &= ~PORTSC_PHCD;
+
+}
+
+static enum usb_wakeup_event _is_usbh3_wakeup(struct fsl_usb2_platform_data *pdata)
+{
+ u32 wakeup_req = USB_H3_CTRL & UCTRL_OWIR;
+
+ if (wakeup_req)
+ return !WAKEUP_EVENT_INVALID;
+ pr_err("host3, %s, invalid wake up\n", __func__);
+ return WAKEUP_EVENT_INVALID;
+}
+
+static void usbh3_wakeup_handler(struct fsl_usb2_platform_data *pdata)
+{
+ _wake_up_enable(pdata, false);
+ _phy_lowpower_suspend(pdata, false);
+ pdata->wakeup_event = 1;
+}
+
+static void usbh3_wakeup_event_clear(void)
+{
+ u32 wakeup_req = USB_H3_CTRL & UCTRL_OWIR;
+ pr_debug("%s\n", __func__);
+
+ if (wakeup_req != 0) {
+ /* Disable H3 wakeup enable to clear H3 wakeup request, wait 3 clock
+ * cycles of standly clock(32KHz)
+ */
+ USB_H3_CTRL &= ~UCTRL_OWIE;
+ udelay(100);
+ USB_H3_CTRL |= UCTRL_OWIE;
+ }
+}
+
+static void hsic_start(void)
+{
+ pr_debug("%s", __func__);
+ /* strobe 47K pull up */
+ mxc_iomux_v3_setup_pad(MX6Q_PAD_RGMII_RXC__USBOH3_H3_STROBE_START);
+}
+
+static struct fsl_usb2_platform_data usbh3_config = {
+ .name = "Host 3",
+ .init = fsl_usb_host_init_ext,
+ .exit = fsl_usb_host_uninit_ext,
+ .operating_mode = FSL_USB2_MPH_HOST,
+ .phy_mode = FSL_USB2_PHY_HSIC,
+ .power_budget = 500, /* 500 mA max power */
+ .wake_up_enable = _wake_up_enable,
+ .usb_clock_for_pm = usbh3_clock_gate,
+ .phy_lowpower_suspend = _phy_lowpower_suspend,
+ .is_wakeup_event = _is_usbh3_wakeup,
+ .wakeup_handler = usbh3_wakeup_handler,
+ .transceiver = "hsic_xcvr",
+ .hsic_post_ops = hsic_start,
+};
+
+static struct fsl_usb2_wakeup_platform_data usbh3_wakeup_config = {
+ .name = "usbh3 wakeup",
+ .usb_clock_for_pm = usbh3_clock_gate,
+ .usb_pdata = {&usbh3_config, NULL, NULL},
+ .usb_wakeup_exhandle = usbh3_wakeup_event_clear,
+};
+
+void __init mx6_usb_h3_init(void)
+{
+ struct platform_device *pdev, *pdev_wakeup;
+ static void __iomem *anatop_base_addr = MX6_IO_ADDRESS(ANATOP_BASE_ADDR);
+ usbh3_config.wakeup_pdata = &usbh3_wakeup_config;
+ pdev = imx6q_add_fsl_ehci_hs(3, &usbh3_config);
+ usbh3_wakeup_config.usb_pdata[0] = pdev->dev.platform_data;
+ pdev_wakeup = imx6q_add_fsl_usb2_hs_wakeup(3, &usbh3_wakeup_config);
+ ((struct fsl_usb2_platform_data *)(pdev->dev.platform_data))->wakeup_pdata =
+ pdev_wakeup->dev.platform_data;
+
+ /* Some phy and power's special controls for host3
+ * 1. Its 480M is from OTG's 480M
+ * 2. EN_USB_CLKS should always be opened
+ */
+ __raw_writel(BM_ANADIG_USB1_PLL_480_CTRL_EN_USB_CLKS,
+ anatop_base_addr + HW_ANADIG_USB1_PLL_480_CTRL_SET);
+}