aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHaitao Zhang <haitao.zhang@linaro.org>2011-08-31 16:36:40 +0800
committerEric Miao <eric.miao@linaro.org>2011-10-14 09:57:02 +0800
commita88ca92dc386164167d76737af713d1a080f9582 (patch)
tree1f269bd0267d1d8b90b78e1a58dd530c22ff249e
parenta2b3e75b9101fef2970123873539b06b94534d0b (diff)
mx53_loco: enabled USB Host1
forward ported code from 2.6.38 Signed-off-by: Haitao Zhang <haitao.zhang@linaro.org> Signed-off-by: Eric Miao <eric.miao@linaro.org>
-rw-r--r--arch/arm/mach-mx5/Makefile5
-rw-r--r--arch/arm/mach-mx5/board-mx53_loco.c27
-rw-r--r--arch/arm/mach-mx5/devices.c96
-rw-r--r--arch/arm/mach-mx5/devices.h6
-rw-r--r--arch/arm/mach-mx5/usb.h47
-rwxr-xr-xarch/arm/mach-mx5/usb_dr.c334
-rw-r--r--arch/arm/mach-mx5/usb_h1.c295
-rw-r--r--arch/arm/plat-mxc/Kconfig6
-rw-r--r--arch/arm/plat-mxc/Makefile5
-rw-r--r--arch/arm/plat-mxc/usb_common.c44
-rw-r--r--arch/arm/plat-mxc/usb_wakeup.c224
-rw-r--r--arch/arm/plat-mxc/utmixc.c94
-rw-r--r--drivers/usb/gadget/arcotg_udc.c7
-rw-r--r--drivers/usb/host/ehci-arc.c2
-rw-r--r--include/linux/fsl_devices.h7
15 files changed, 1153 insertions, 46 deletions
diff --git a/arch/arm/mach-mx5/Makefile b/arch/arm/mach-mx5/Makefile
index 6f49252d0a6..9d3c2977f65 100644
--- a/arch/arm/mach-mx5/Makefile
+++ b/arch/arm/mach-mx5/Makefile
@@ -3,7 +3,8 @@
#
# Object file lists.
-obj-y := cpu.o mm.o devices.o ehci.o system.o bus_freq.o sdram_autogating.o suspend.o
+obj-y := cpu.o mm.o devices.o ehci.o system.o bus_freq.o sdram_autogating.o \
+ suspend.o
obj-$(CONFIG_SOC_IMX50) += mm-mx50.o
obj-$(CONFIG_SOC_IMX51) += clock.o
obj-$(CONFIG_SOC_IMX53) += clock.o
@@ -24,3 +25,5 @@ obj-$(CONFIG_MX51_EFIKA_COMMON) += mx51_efika.o
obj-$(CONFIG_MACH_MX51_EFIKAMX) += board-mx51_efikamx.o
obj-$(CONFIG_MACH_MX51_EFIKASB) += board-mx51_efikasb.o
obj-$(CONFIG_MACH_MX50_RDP) += board-mx50_rdp.o
+
+obj-$(CONFIG_USB_SUPPORT) += usb_h1.o usb_dr.o
diff --git a/arch/arm/mach-mx5/board-mx53_loco.c b/arch/arm/mach-mx5/board-mx53_loco.c
index b04c13afd71..0ac537e83d9 100644
--- a/arch/arm/mach-mx5/board-mx53_loco.c
+++ b/arch/arm/mach-mx5/board-mx53_loco.c
@@ -39,6 +39,7 @@
#include "crm_regs.h"
#include "devices-imx53.h"
#include "devices.h"
+#include "usb.h"
#define MX53_LOCO_POWER IMX_GPIO_NR(1, 8)
#define MX53_LOCO_UI1 IMX_GPIO_NR(2, 14)
@@ -53,6 +54,9 @@
#define LOCO_DISP0_RESET IMX_GPIO_NR(5, 0)
extern void __iomem *ccm_base;
+extern void __iomem *imx_otg_base;
+
+#define LOCO_USBH1_VBUS IMX_GPIO_NR(7, 8)
static iomux_v3_cfg_t mx53_loco_pads[] = {
/* FEC */
@@ -390,6 +394,14 @@ static struct platform_pwm_backlight_data loco_pwm_backlight_data = {
.pwm_period_ns = 50000,
};
+static void mx53_loco_usbh1_vbus(bool on)
+{
+ if (on)
+ gpio_set_value(LOCO_USBH1_VBUS, 1);
+ else
+ gpio_set_value(LOCO_USBH1_VBUS, 0);
+}
+
static void __init mx53_loco_io_init(void)
{
int ret;
@@ -423,7 +435,7 @@ static void __init mx53_loco_io_init(void)
static void __init mx53_loco_board_init(void)
{
- int i;
+ int i, ret;
imx53_soc_init();
@@ -472,6 +484,19 @@ static void __init mx53_loco_board_init(void)
else
gpu_data.z160_revision = 0;
imx53_add_mxc_gpu(&gpu_data);
+
+ /* USB */
+ imx_otg_base = MX53_IO_ADDRESS(MX53_OTG_BASE_ADDR);
+ /* usb host1 vbus */
+ ret = gpio_request(LOCO_USBH1_VBUS, "usbh1-vbus");
+ if (ret) {
+ printk(KERN_ERR"failed to get GPIO LOCO_USBH1_VBUS: %d\n", ret);
+ return;
+ }
+ gpio_direction_output(LOCO_USBH1_VBUS, 0);
+ mx5_set_host1_vbus_func(mx53_loco_usbh1_vbus);
+ mx5_usbh1_init();
+ mx5_usb_dr_init();
}
static void __init mx53_loco_timer_init(void)
diff --git a/arch/arm/mach-mx5/devices.c b/arch/arm/mach-mx5/devices.c
index a8d94fbccf5..dd5a9515838 100644
--- a/arch/arm/mach-mx5/devices.c
+++ b/arch/arm/mach-mx5/devices.c
@@ -62,6 +62,17 @@ struct platform_device mxc_usbdr_udc_device = {
},
};
+struct platform_device mx53_usbdr_udc_device = {
+ .name = "fsl-usb2-udc",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(usbotg_resources),
+ .resource = usbotg_resources,
+ .dev = {
+ .dma_mask = &usb_dma_mask,
+ .coherent_dma_mask = DMA_BIT_MASK(32),
+ },
+};
+
struct platform_device mxc_usbdr_host_device = {
.name = "mxc-ehci",
.id = 0,
@@ -73,6 +84,58 @@ struct platform_device mxc_usbdr_host_device = {
},
};
+struct platform_device mx53_usbdr_host_device = {
+ .name = "fsl-ehci",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(usbotg_resources),
+ .resource = usbotg_resources,
+ .dev = {
+ .dma_mask = &usb_dma_mask,
+ .coherent_dma_mask = DMA_BIT_MASK(32),
+ },
+};
+
+static struct resource usbotg_wakeup_resources[] = {
+ {
+ .start = MX51_INT_USB_OTG,/* wakeup irq */
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = MX51_INT_USB_OTG,/* usb core irq */
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device mx53_usbdr_wakeup_device = {
+ .name = "usb_wakeup",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(usbotg_wakeup_resources),
+ .resource = usbotg_wakeup_resources,
+};
+
+static struct resource usbotg_xcvr_resources[] = {
+ {
+ .start = MX51_OTG_BASE_ADDR,
+ .end = MX51_OTG_BASE_ADDR + 0x1ff,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = MX51_MXC_INT_USB_OTG,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device mx53_usbdr_otg_device = {
+ .name = "fsl-usb2-otg",
+ .id = -1,
+ .dev = {
+ .dma_mask = &usb_dma_mask,
+ .coherent_dma_mask = DMA_BIT_MASK(32),
+ },
+ .resource = usbotg_xcvr_resources,
+ .num_resources = ARRAY_SIZE(usbotg_xcvr_resources),
+};
+
static struct resource usbh1_resources[] = {
{
.start = MX51_OTG_BASE_ADDR + 0x200,
@@ -96,6 +159,17 @@ struct platform_device mxc_usbh1_device = {
},
};
+struct platform_device mx53_usbh1_device = {
+ .name = "fsl-ehci",
+ .id = 1,
+ .num_resources = ARRAY_SIZE(usbh1_resources),
+ .resource = usbh1_resources,
+ .dev = {
+ .dma_mask = &usb_dma_mask,
+ .coherent_dma_mask = DMA_BIT_MASK(32),
+ },
+};
+
static struct resource usbh2_resources[] = {
{
.start = MX51_OTG_BASE_ADDR + 0x400,
@@ -120,6 +194,24 @@ struct platform_device mxc_usbh2_device = {
};
struct platform_device mxc_pm_device = {
- .name = "mx5_pm",
- .id = 0,
+ .name = "mx5_pm",
+ .id = 0,
+};
+
+static struct resource usbh1_wakeup_resources[] = {
+ {
+ .start = MX51_INT_USB_H1, /*wakeup irq*/
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = MX51_INT_USB_H1,
+ .flags = IORESOURCE_IRQ,/* usb core irq */
+ },
+};
+
+struct platform_device mx53_usbh1_wakeup_device = {
+ .name = "usb_wakeup",
+ .id = 1,
+ .num_resources = ARRAY_SIZE(usbh1_wakeup_resources),
+ .resource = usbh1_wakeup_resources,
};
diff --git a/arch/arm/mach-mx5/devices.h b/arch/arm/mach-mx5/devices.h
index 02e83ea9962..a0de07b318f 100644
--- a/arch/arm/mach-mx5/devices.h
+++ b/arch/arm/mach-mx5/devices.h
@@ -1,6 +1,12 @@
extern struct platform_device mxc_usbdr_host_device;
+extern struct platform_device mx53_usbdr_host_device;
extern struct platform_device mxc_usbh1_device;
extern struct platform_device mxc_usbh2_device;
+extern struct platform_device mx53_usbh1_device;
extern struct platform_device mxc_usbdr_udc_device;
+extern struct platform_device mx53_usbdr_udc_device;
extern struct platform_device mxc_hsi2c_device;
extern struct platform_device mxc_pm_device;
+extern struct platform_device mx53_usbdr_wakeup_device;
+extern struct platform_device mx53_usbh1_wakeup_device;
+extern struct platform_device mx53_usbdr_otg_device;
diff --git a/arch/arm/mach-mx5/usb.h b/arch/arm/mach-mx5/usb.h
new file mode 100644
index 00000000000..c059f7409e6
--- /dev/null
+++ b/arch/arm/mach-mx5/usb.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2005-2011 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <mach/common.h>
+#include "devices.h"
+
+extern int usbotg_init(struct platform_device *pdev);
+extern void usbotg_uninit(struct fsl_usb2_platform_data *pdata);
+extern struct platform_device *host_pdev_register(struct resource *res,
+ int n_res, struct fsl_usb2_platform_data *config);
+
+extern int fsl_usb_host_init(struct platform_device *pdev);
+extern void fsl_usb_host_uninit(struct fsl_usb2_platform_data *pdata);
+extern int gpio_usbotg_utmi_active(void);
+extern void gpio_usbotg_utmi_inactive(void);
+
+extern void __init mx5_usb_dr_init(void);
+extern void __init mx5_usbh1_init(void);
+extern void __init mx5_usbh2_init(void);
+
+typedef void (*driver_vbus_func)(bool);
+extern void mx5_set_host1_vbus_func(driver_vbus_func);
+extern void mx5_set_otghost_vbus_func(driver_vbus_func);
+/*
+ * Used to set pdata->operating_mode before registering the platform_device.
+ * If OTG is configured, the controller operates in OTG mode,
+ * otherwise it's either host or device.
+ */
+#ifdef CONFIG_USB_OTG
+#define DR_UDC_MODE FSL_USB2_DR_OTG
+#define DR_HOST_MODE FSL_USB2_DR_OTG
+#else
+#define DR_UDC_MODE FSL_USB2_DR_DEVICE
+#define DR_HOST_MODE FSL_USB2_DR_HOST
+#endif
+
+extern void __iomem *imx_otg_base;
diff --git a/arch/arm/mach-mx5/usb_dr.c b/arch/arm/mach-mx5/usb_dr.c
new file mode 100755
index 00000000000..099cd402774
--- /dev/null
+++ b/arch/arm/mach-mx5/usb_dr.c
@@ -0,0 +1,334 @@
+/*
+ * Copyright (C) 2005-2011 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+#define DEBUG 1
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/fsl_devices.h>
+#include <mach/arc_otg.h>
+#include <mach/hardware.h>
+#include <linux/delay.h>
+#include "usb.h"
+static int usbotg_init_ext(struct platform_device *pdev);
+static void usbotg_uninit_ext(struct platform_device *pdev);
+static void usbotg_clock_gate(bool on);
+
+static struct clk *usb_phy1_clk;
+static struct clk *usb_oh3_clk;
+static struct clk *usb_ahb_clk;
+static void usbotg_wakeup_event_clear(void);
+extern int clk_get_usecount(struct clk *clk);
+
+/* Beginning of Common operation for DR port */
+
+/*
+ * platform data structs
+ * - Which one to use is determined by CONFIG options in usb.h
+ * - operating_mode plugged at run time
+ */
+static struct fsl_usb2_platform_data dr_utmi_config = {
+ .name = "DR",
+ .init = usbotg_init_ext,
+ .exit = usbotg_uninit_ext,
+ .phy_mode = FSL_USB2_PHY_UTMI_WIDE,
+ .power_budget = 500, /* 500 mA max power */
+ .usb_clock_for_pm = usbotg_clock_gate,
+ .transceiver = "utmi",
+};
+
+/* Platform data for wakeup operation */
+static struct fsl_usb2_wakeup_platform_data dr_wakeup_config = {
+ .name = "DR wakeup",
+ .usb_clock_for_pm = usbotg_clock_gate,
+ .usb_wakeup_exhandle = usbotg_wakeup_event_clear,
+};
+/* Notes: configure USB clock*/
+static int usbotg_init_ext(struct platform_device *pdev)
+{
+ struct clk *usb_clk;
+
+ /* the usb_ahb_clk will be enabled in usb_otg_init */
+ usb_ahb_clk = clk_get(NULL, "usb_ahb_clk");
+
+ usb_clk = clk_get(NULL, "usboh3_clk");
+ clk_enable(usb_clk);
+ usb_oh3_clk = usb_clk;
+
+ usb_clk = clk_get(NULL, "usb_phy1_clk");
+ clk_enable(usb_clk);
+ usb_phy1_clk = usb_clk;
+
+ return usbotg_init(pdev);
+}
+
+static void usbotg_uninit_ext(struct platform_device *pdev)
+{
+ struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data;
+
+ clk_disable(usb_phy1_clk);
+ clk_put(usb_phy1_clk);
+
+ clk_disable(usb_oh3_clk);
+ clk_put(usb_oh3_clk);
+
+ /* usb_ahb_clk will be disabled at usb_common.c */
+ usbotg_uninit(pdata);
+ clk_put(usb_ahb_clk);
+}
+
+/* Below two macros are used at otg mode to indicate usb mode*/
+#define ENABLED_BY_HOST (0x1 << 0)
+#define ENABLED_BY_DEVICE (0x1 << 1)
+static u32 wakeup_irq_enable_src; /* only useful at otg mode */
+static void __wakeup_irq_enable(bool on, int source)
+ {
+ /* otg host and device share the OWIE bit, only when host and device
+ * all enable the wakeup irq, we can enable the OWIE bit
+ */
+ if (on) {
+#ifdef CONFIG_USB_OTG
+ wakeup_irq_enable_src |= source;
+ if (wakeup_irq_enable_src == (ENABLED_BY_HOST | ENABLED_BY_DEVICE)) {
+ USBCTRL |= UCTRL_OWIE;
+ USB_PHY_CTR_FUNC |= USB_UTMI_PHYCTRL_CONF2;
+ }
+#else
+ USBCTRL |= UCTRL_OWIE;
+ USB_PHY_CTR_FUNC |= USB_UTMI_PHYCTRL_CONF2;
+#endif
+ } else {
+ USB_PHY_CTR_FUNC &= ~USB_UTMI_PHYCTRL_CONF2;
+ USBCTRL &= ~UCTRL_OWIE;
+ wakeup_irq_enable_src &= ~source;
+ /* The interrupt must be disabled for at least 3 clock
+ * cycles of the standby clock(32k Hz) , that is 0.094 ms*/
+ udelay(100);
+ }
+}
+
+static u32 low_power_enable_src; /* only useful at otg mode */
+static void __phy_lowpower_suspend(bool enable, int source)
+{
+ if (enable) {
+ low_power_enable_src |= source;
+#ifdef CONFIG_USB_OTG
+ if (low_power_enable_src == (ENABLED_BY_HOST | ENABLED_BY_DEVICE)) {
+ pr_debug("phy lowpower enabled\n");
+ UOG_PORTSC1 |= PORTSC_PHCD;
+ }
+#else
+ UOG_PORTSC1 |= PORTSC_PHCD;
+#endif
+ } else {
+ pr_debug("phy lowpower disable\n");
+ UOG_PORTSC1 &= ~PORTSC_PHCD;
+ low_power_enable_src &= ~source;
+ }
+}
+
+static void usbotg_clock_gate(bool on)
+{
+ pr_debug("%s: on is %d\n", __func__, on);
+ if (on) {
+ clk_enable(usb_ahb_clk);
+ clk_enable(usb_oh3_clk);
+ clk_enable(usb_phy1_clk);
+ } else {
+ clk_disable(usb_phy1_clk);
+ clk_disable(usb_oh3_clk);
+ clk_disable(usb_ahb_clk);
+ }
+ pr_debug("usb_ahb_ref_count:%d, usb_phy_clk1_ref_count:%d\n", clk_get_usecount(usb_ahb_clk), clk_get_usecount(usb_phy1_clk));
+}
+
+void mx5_set_otghost_vbus_func(driver_vbus_func driver_vbus)
+{
+ dr_utmi_config.platform_driver_vbus = driver_vbus;
+}
+
+/* The wakeup operation for DR port, it will clear the wakeup irq status
+ * and re-enable the wakeup
+ */
+static void usbotg_wakeup_event_clear(void)
+{
+ int wakeup_req = USBCTRL & UCTRL_OWIR;
+
+ if (wakeup_req != 0) {
+ printk(KERN_INFO "Unknown wakeup.(OTGSC 0x%x)\n", UOG_OTGSC);
+ /* Disable OWIE to clear OWIR, wait 3 clock
+ * cycles of standly clock(32KHz)
+ */
+ USBCTRL &= ~UCTRL_OWIE;
+ udelay(100);
+ USBCTRL |= UCTRL_OWIE;
+ }
+}
+/* End of Common operation for DR port */
+
+
+#ifdef CONFIG_USB_EHCI_ARC_OTG
+extern void fsl_usb_recover_hcd(struct platform_device *pdev);
+/* Beginning of host related operation for DR port */
+static void _host_wakeup_enable(struct fsl_usb2_platform_data *pdata, bool enable)
+{
+ __wakeup_irq_enable(enable, ENABLED_BY_HOST);
+ /* host only care the ID change wakeup event */
+ if (enable) {
+ pr_debug("host wakeup enable\n");
+ USBCTRL_HOST2 |= UCTRL_H2OIDWK_EN;
+ } else {
+ pr_debug("host wakeup disable\n");
+ USBCTRL_HOST2 &= ~UCTRL_H2OIDWK_EN;
+ /* The interrupt must be disabled for at least 3 clock
+ * cycles of the standby clock(32k Hz) , that is 0.094 ms*/
+ udelay(100);
+ }
+}
+
+static void _host_phy_lowpower_suspend(struct fsl_usb2_platform_data *pdata, bool enable)
+{
+ __phy_lowpower_suspend(enable, ENABLED_BY_HOST);
+}
+
+static enum usb_wakeup_event _is_host_wakeup(struct fsl_usb2_platform_data *pdata)
+{
+ int wakeup_req = USBCTRL & UCTRL_OWIR;
+ int otgsc = UOG_OTGSC;
+
+ /* if ID change sts, it is a host wakeup event */
+ if (wakeup_req && (otgsc & OTGSC_IS_USB_ID)) {
+ printk(KERN_INFO "otg host ID wakeup\n");
+ /* if host ID wakeup, we must clear the b session change sts */
+ UOG_OTGSC = otgsc & (~OTGSC_IS_USB_ID);
+ return WAKEUP_EVENT_ID;
+ }
+ if (wakeup_req && (!(otgsc & OTGSC_STS_USB_ID))) {
+ printk(KERN_INFO "otg host Remote wakeup\n");
+ return WAKEUP_EVENT_DPDM;
+ }
+
+ return WAKEUP_EVENT_INVALID;
+}
+
+static void host_wakeup_handler(struct fsl_usb2_platform_data *pdata)
+{
+ _host_wakeup_enable(pdata, false);
+ _host_phy_lowpower_suspend(pdata, false);
+ fsl_usb_recover_hcd(&mxc_usbdr_host_device);
+}
+/* End of host related operation for DR port */
+#endif /* CONFIG_USB_EHCI_ARC_OTG */
+
+
+#ifdef CONFIG_USB_GADGET_ARC
+/* Beginning of device related operation for DR port */
+static void _device_wakeup_enable(struct fsl_usb2_platform_data *pdata, bool enable)
+{
+ __wakeup_irq_enable(enable, ENABLED_BY_DEVICE);
+ /* if udc is not used by any gadget, we can not enable the vbus wakeup */
+ if (!pdata->port_enables) {
+ USBCTRL_HOST2 &= ~UCTRL_H2OVBWK_EN;
+ return;
+ }
+ if (enable) {
+ pr_debug("device wakeup enable\n");
+ USBCTRL_HOST2 |= UCTRL_H2OVBWK_EN;
+ } else {
+ pr_debug("device wakeup disable\n");
+ USBCTRL_HOST2 &= ~UCTRL_H2OVBWK_EN;
+ }
+}
+
+static void _device_phy_lowpower_suspend(struct fsl_usb2_platform_data *pdata, bool enable)
+{
+ __phy_lowpower_suspend(enable, ENABLED_BY_DEVICE);
+}
+
+static enum usb_wakeup_event _is_device_wakeup(struct fsl_usb2_platform_data *pdata)
+{
+ int wakeup_req = USBCTRL & UCTRL_OWIR;
+ u32 otgsc = 0;
+
+ otgsc = UOG_OTGSC;
+ if (wakeup_req &&
+ (otgsc & OTGSC_STS_USB_ID) &&
+ (otgsc & OTGSC_IS_B_SESSION_VALID)) {
+ printk(KERN_INFO "otg udc wakeup\n");
+ return WAKEUP_EVENT_VBUS;
+ }
+ return WAKEUP_EVENT_INVALID;
+
+}
+
+static void device_wakeup_handler(struct fsl_usb2_platform_data *pdata)
+{
+ _device_wakeup_enable(pdata, false);
+ _device_phy_lowpower_suspend(pdata, false);
+}
+
+/* end of device related operation for DR port */
+#endif /* CONFIG_USB_GADGET_ARC */
+
+#define MX53_OFFSET 0x20000000
+
+void __init mx5_usb_dr_init(void)
+{
+ int ret = 0;
+#ifdef CONFIG_USB_OTG
+ if (cpu_is_mx53() || cpu_is_mx50()) {
+ mx53_usbdr_otg_device.resource[0].start -= MX53_OFFSET;
+ mx53_usbdr_otg_device.resource[0].end -= MX53_OFFSET;
+ }
+ /* wake_up_enalbe is useless, just for usb_register_remote_wakeup execution*/
+ dr_utmi_config.wake_up_enable = _device_wakeup_enable;
+ dr_utmi_config.operating_mode = FSL_USB2_DR_OTG;
+ dr_utmi_config.wakeup_pdata = &dr_wakeup_config;
+ ret |= platform_device_add_data(&mx53_usbdr_otg_device, &dr_utmi_config, sizeof(dr_utmi_config));
+ ret |= platform_device_register(&mx53_usbdr_otg_device);
+ dr_wakeup_config.usb_pdata[0] = mx53_usbdr_otg_device.dev.platform_data;
+#endif
+#ifdef CONFIG_USB_EHCI_ARC_OTG
+ if (cpu_is_mx53() || cpu_is_mx50()) {
+ mx53_usbdr_host_device.resource[0].start -= MX53_OFFSET;
+ mx53_usbdr_host_device.resource[0].end -= MX53_OFFSET;
+ }
+ dr_utmi_config.operating_mode = DR_HOST_MODE;
+ dr_utmi_config.wake_up_enable = _host_wakeup_enable;
+ dr_utmi_config.phy_lowpower_suspend = _host_phy_lowpower_suspend;
+ dr_utmi_config.is_wakeup_event = _is_host_wakeup;
+ dr_utmi_config.wakeup_pdata = &dr_wakeup_config;
+ dr_utmi_config.wakeup_handler = host_wakeup_handler;
+ ret |= platform_device_add_data(&mx53_usbdr_host_device, &dr_utmi_config, sizeof(dr_utmi_config));
+ ret |= platform_device_register(&mx53_usbdr_host_device);
+ dr_wakeup_config.usb_pdata[1] = mx53_usbdr_host_device.dev.platform_data;
+#endif
+#ifdef CONFIG_USB_GADGET_ARC
+ if (cpu_is_mx53() || cpu_is_mx50()) {
+ mx53_usbdr_udc_device.resource[0].start -= MX53_OFFSET;
+ mx53_usbdr_udc_device.resource[0].end -= MX53_OFFSET;
+ }
+ dr_utmi_config.operating_mode = DR_UDC_MODE;
+ dr_utmi_config.wake_up_enable = _device_wakeup_enable;
+ dr_utmi_config.phy_lowpower_suspend = _device_phy_lowpower_suspend;
+ dr_utmi_config.is_wakeup_event = _is_device_wakeup;
+ dr_utmi_config.wakeup_pdata = &dr_wakeup_config;
+ dr_utmi_config.wakeup_handler = device_wakeup_handler;
+ ret |= platform_device_add_data(&mx53_usbdr_udc_device, &dr_utmi_config, sizeof(dr_utmi_config));
+ ret |= platform_device_register(&mx53_usbdr_udc_device);
+ dr_wakeup_config.usb_pdata[2] = mx53_usbdr_udc_device.dev.platform_data;
+#endif
+ ret |= mxc_register_device(&mx53_usbdr_wakeup_device, &dr_wakeup_config);
+ if (ret)
+ printk(KERN_ERR "%s(%d): error occures while init usb dr \n", __func__, __LINE__);
+}
diff --git a/arch/arm/mach-mx5/usb_h1.c b/arch/arm/mach-mx5/usb_h1.c
new file mode 100644
index 00000000000..11058cac793
--- /dev/null
+++ b/arch/arm/mach-mx5/usb_h1.c
@@ -0,0 +1,295 @@
+/*
+ * Copyright (C) 2005-2011 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#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 <asm/delay.h>
+#include <mach/arc_otg.h>
+#include <mach/iomux-mx51.h>
+#include <mach/iomux-mx53.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include "usb.h"
+
+static struct clk *usb_phy2_clk;
+static struct clk *usb_oh3_clk;
+static struct clk *usb_ahb_clk;
+extern int clk_get_usecount(struct clk *clk);
+
+#define MX5X_USBH1_STP IMX_GPIO_NR(1, 27)
+#define MX51_3DS_PHY_RESET IMX_GPIO_NR(2, 5)
+
+#ifdef CONFIG_USB_EHCI_ARC
+extern void fsl_usb_recover_hcd(struct platform_device *pdev);
+#else
+static void fsl_usb_recover_hcd(struct platform_device *pdev)
+{; }
+#endif
+/*
+ * USB Host1 HS port
+ */
+static int gpio_usbh1_active(void)
+{
+ iomux_v3_cfg_t usbh1stp_gpio = MX51_PAD_USBH1_STP__GPIO1_27;
+ iomux_v3_cfg_t phyreset_gpio = MX51_PAD_EIM_D17__GPIO2_1;
+ int ret;
+
+ /* Set USBH1_STP to GPIO and toggle it */
+ mxc_iomux_v3_setup_pad(usbh1stp_gpio);
+ ret = gpio_request(MX5X_USBH1_STP, "usbh1_stp");
+
+ if (ret) {
+ pr_debug("failed to get MX51_PAD_USBH1_STP__GPIO_1_27: %d\n", ret);
+ return ret;
+ }
+ gpio_direction_output(MX5X_USBH1_STP, 0);
+ gpio_set_value(MX5X_USBH1_STP, 1);
+
+ /* Signal only used on MX51-3DS for reset to PHY.*/
+ if (machine_is_mx51_3ds()) {
+ mxc_iomux_v3_setup_pad(phyreset_gpio);
+ ret = gpio_request(MX51_3DS_PHY_RESET, "eim_d17");
+ if (ret) {
+ pr_debug("failed to get MX51_PAD_EIM_D17__GPIO2_1: %d\n", ret);
+ return ret;
+ }
+ gpio_direction_output(MX51_3DS_PHY_RESET, 0);
+ gpio_set_value(MX51_3DS_PHY_RESET, 1);
+ }
+
+ msleep(100);
+
+ return 0;
+}
+
+static void gpio_usbh1_inactive(void)
+{
+ /* Signal only used on MX51-3DS for reset to PHY.*/
+ if (machine_is_mx51_3ds()) {
+ gpio_free(MX51_3DS_PHY_RESET);
+ }
+ gpio_free(MX5X_USBH1_STP);
+}
+
+
+static void _wake_up_enable(struct fsl_usb2_platform_data *pdata, bool enable)
+{
+ pr_debug("host1, %s, enable is %d\n", __func__, enable);
+ if (enable)
+ USBCTRL |= UCTRL_H1WIE;
+ else {
+ USBCTRL &= ~UCTRL_H1WIE;
+ /* 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("host1, %s, enable is %d\n", __func__, enable);
+ if (enable) {
+ UH1_PORTSC1 |= PORTSC_PHCD;
+ } else {
+ UH1_PORTSC1 &= ~PORTSC_PHCD;
+ }
+}
+
+static void usbh1_clock_gate(bool on)
+{
+ pr_debug("%s: on is %d\n", __func__, on);
+ if (on) {
+ clk_enable(usb_ahb_clk);
+ clk_enable(usb_oh3_clk);
+ clk_enable(usb_phy2_clk);
+ } else {
+ clk_disable(usb_phy2_clk);
+ clk_disable(usb_oh3_clk);
+ clk_disable(usb_ahb_clk);
+ }
+}
+
+static enum usb_wakeup_event _is_usbh1_wakeup(struct fsl_usb2_platform_data *pdata)
+{
+ int wakeup_req = USBCTRL & UCTRL_H1WIR;
+
+ if (wakeup_req)
+ return !WAKEUP_EVENT_INVALID;
+
+ return WAKEUP_EVENT_INVALID;
+}
+
+static void h1_wakeup_handler(struct fsl_usb2_platform_data *pdata)
+{
+ _wake_up_enable(pdata, false);
+ _phy_lowpower_suspend(pdata, false);
+ fsl_usb_recover_hcd(&mxc_usbh1_device);
+}
+
+static void usbh1_wakeup_event_clear(void)
+{
+ int wakeup_req = USBCTRL & UCTRL_H1WIR;
+
+ if (wakeup_req != 0) {
+ printk(KERN_INFO "Unknown wakeup.(OTGSC 0x%x)\n", UOG_OTGSC);
+ /* Disable H1WIE to clear H1WIR, wait 3 clock
+ * cycles of standly clock(32KHz)
+ */
+ USBCTRL &= ~UCTRL_H1WIE;
+ udelay(100);
+ USBCTRL |= UCTRL_H1WIE;
+ }
+}
+static int fsl_usb_host_init_ext(struct platform_device *pdev)
+{
+ iomux_v3_cfg_t usbh1stp_func = MX51_PAD_USBH1_STP__USBH1_STP;
+ struct clk *usb_clk;
+ int ret;
+
+ /* the usb_ahb_clk will be enabled in usb_otg_init */
+ usb_ahb_clk = clk_get(NULL, "usb_ahb_clk");
+
+ if (cpu_is_mx53()) {
+ usb_clk = clk_get(NULL, "usboh3_clk");
+ if (usb_clk) {
+ clk_enable(usb_clk);
+ usb_oh3_clk = usb_clk;
+ }
+
+ usb_clk = clk_get(NULL, "usb_phy2_clk");
+ if (usb_clk) {
+ clk_enable(usb_clk);
+ usb_phy2_clk = usb_clk;
+ }
+ } else if (cpu_is_mx50()) {
+ usb_clk = clk_get(NULL, "usb_phy2_clk");
+ if (usb_clk) {
+ clk_enable(usb_clk);
+ usb_phy2_clk = usb_clk;
+ }
+ } else if (cpu_is_mx51()) {
+ usb_clk = clk_get(NULL, "usboh3_clk");
+ if (usb_clk) {
+ clk_enable(usb_clk);
+ usb_oh3_clk = usb_clk;
+ }
+ }
+
+ ret = fsl_usb_host_init(pdev);
+ if (ret)
+ return ret;
+
+ if (cpu_is_mx51()) {
+ /* setback USBH1_STP to be function */
+#if 0 /* Jasper: Need to do... */
+ mxc_request_iomux(MX51_PIN_USBH1_STP, IOMUX_CONFIG_ALT0);
+ mxc_iomux_set_pad(MX51_PIN_USBH1_STP, PAD_CTL_SRE_FAST |
+ PAD_CTL_DRV_HIGH | PAD_CTL_ODE_OPENDRAIN_NONE |
+ PAD_CTL_PUE_KEEPER | PAD_CTL_PKE_ENABLE |
+ PAD_CTL_HYS_ENABLE | PAD_CTL_DDR_INPUT_CMOS |
+ PAD_CTL_DRV_VOT_LOW);
+ gpio_free(IOMUX_TO_GPIO(MX51_PIN_USBH1_STP));
+#endif
+ mxc_iomux_v3_setup_pad(usbh1stp_func);
+ gpio_free(MX5X_USBH1_STP);
+ }
+
+ /* disable remote wakeup irq */
+ USBCTRL &= ~UCTRL_H1WIE;
+ return 0;
+}
+
+static void fsl_usb_host_uninit_ext(struct platform_device *pdev)
+{
+ struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data;
+
+ if (cpu_is_mx53()) {
+ if (usb_oh3_clk) {
+ clk_disable(usb_oh3_clk);
+ clk_put(usb_oh3_clk);
+ }
+
+ if (usb_phy2_clk) {
+ clk_disable(usb_phy2_clk);
+ clk_put(usb_phy2_clk);
+ }
+ } else if (cpu_is_mx50()) {
+ if (usb_phy2_clk) {
+ clk_disable(usb_phy2_clk);
+ clk_put(usb_phy2_clk);
+ }
+ } else if (cpu_is_mx51()) {
+ if (usb_oh3_clk) {
+ clk_disable(usb_oh3_clk);
+ clk_put(usb_oh3_clk);
+ }
+ }
+
+ fsl_usb_host_uninit(pdata);
+ /* usb_ahb_clk will be disabled at usb_common.c */
+ clk_put(usb_ahb_clk);
+}
+
+static struct fsl_usb2_platform_data usbh1_config = {
+ .name = "Host 1",
+ .init = fsl_usb_host_init_ext,
+ .exit = fsl_usb_host_uninit_ext,
+ .operating_mode = FSL_USB2_MPH_HOST,
+ .phy_mode = FSL_USB2_PHY_UTMI_WIDE,
+ .power_budget = 500, /* 500 mA max power */
+ .wake_up_enable = _wake_up_enable,
+ .usb_clock_for_pm = usbh1_clock_gate,
+ .phy_lowpower_suspend = _phy_lowpower_suspend,
+ .is_wakeup_event = _is_usbh1_wakeup,
+ .wakeup_handler = h1_wakeup_handler,
+ .transceiver = "utmi",
+};
+
+static struct fsl_usb2_wakeup_platform_data usbh1_wakeup_config = {
+ .name = "USBH1 wakeup",
+ .usb_clock_for_pm = usbh1_clock_gate,
+ .usb_pdata = {&usbh1_config, NULL, NULL},
+ .usb_wakeup_exhandle = usbh1_wakeup_event_clear,
+};
+
+void mx5_set_host1_vbus_func(driver_vbus_func driver_vbus)
+{
+ usbh1_config.platform_driver_vbus = driver_vbus;
+}
+
+#define MX53_OFFSET 0x20000000
+void __init mx5_usbh1_init(void)
+{
+ if (cpu_is_mx51()) {
+ usbh1_config.phy_mode = FSL_USB2_PHY_ULPI;
+ usbh1_config.transceiver = "isp1504";
+ usbh1_config.gpio_usb_active = gpio_usbh1_active;
+ usbh1_config.gpio_usb_inactive = gpio_usbh1_inactive;
+ }
+ if (cpu_is_mx53() || cpu_is_mx50()) {
+ mx53_usbh1_device.resource[0].start -= MX53_OFFSET;
+ mx53_usbh1_device.resource[0].end -= MX53_OFFSET;
+ }
+ if (cpu_is_mx53()) {
+ mxc_register_device(&mx53_usbh1_device, &usbh1_config);
+ usbh1_config.wakeup_pdata = &usbh1_wakeup_config;
+ mxc_register_device(&mx53_usbh1_wakeup_device, &usbh1_wakeup_config);
+ }
+}
+
diff --git a/arch/arm/plat-mxc/Kconfig b/arch/arm/plat-mxc/Kconfig
index 39f4d768aea..a7044c07668 100644
--- a/arch/arm/plat-mxc/Kconfig
+++ b/arch/arm/plat-mxc/Kconfig
@@ -55,6 +55,12 @@ source "arch/arm/mach-mx5/Kconfig"
endmenu
+# set if we need the UTMI transceiver
+config UTMI_MXC
+ bool
+ default y
+ depends on ARCH_MX25 || ARCH_MX35 || ARCH_MX37 || ARCH_MX503
+
config MXC_IRQ_PRIOR
bool "Use IRQ priority"
help
diff --git a/arch/arm/plat-mxc/Makefile b/arch/arm/plat-mxc/Makefile
index 366fc1f901d..54bc597102e 100644
--- a/arch/arm/plat-mxc/Makefile
+++ b/arch/arm/plat-mxc/Makefile
@@ -3,7 +3,7 @@
#
# Common support
-obj-y := clock.o time.o devices.o cpu.o system.o irq-common.o usb_common.o
+obj-y := clock.o time.o devices.o cpu.o system.o irq-common.o
# MX51 uses the TZIC interrupt controller, older platforms use AVIC
obj-$(CONFIG_MXC_TZIC) += tzic.o
@@ -31,3 +31,6 @@ obj-$(CONFIG_ARCH_MX5) += dvfs_core.o
# DVFS-PER support
obj-$(CONFIG_MXC_DVFS_PER) += dvfs_per.o
+obj-$(CONFIG_UTMI_MXC) += utmixc.o
+
+obj-$(CONFIG_USB_EHCI_ARC) += usb_common.o usb_wakeup.o
diff --git a/arch/arm/plat-mxc/usb_common.c b/arch/arm/plat-mxc/usb_common.c
index 300d2756394..716a03d212e 100644
--- a/arch/arm/plat-mxc/usb_common.c
+++ b/arch/arm/plat-mxc/usb_common.c
@@ -101,15 +101,12 @@ void fsl_usb_xcvr_register(struct fsl_xcvr_ops *xcvr_ops)
{
int i;
- pr_debug("%s\n", __func__);
for (i = 0; i < MXC_NUMBER_USB_TRANSCEIVER; i++) {
if (g_xc_ops[i] == NULL) {
g_xc_ops[i] = xcvr_ops;
return;
}
}
-
- pr_debug("Failed %s\n", __func__);
}
EXPORT_SYMBOL(fsl_usb_xcvr_register);
@@ -124,15 +121,12 @@ void fsl_usb_xcvr_unregister(struct fsl_xcvr_ops *xcvr_ops)
{
int i;
- pr_debug("%s\n", __func__);
for (i = 0; i < MXC_NUMBER_USB_TRANSCEIVER; i++) {
if (g_xc_ops[i] == xcvr_ops) {
g_xc_ops[i] = NULL;
return;
}
}
-
- pr_debug("Failed %s\n", __func__);
}
EXPORT_SYMBOL(fsl_usb_xcvr_unregister);
@@ -140,18 +134,18 @@ static struct fsl_xcvr_ops *fsl_usb_get_xcvr(char *name)
{
int i;
- pr_debug("%s\n", __func__);
if (name == NULL) {
- printk(KERN_ERR "get_xcvr(): No tranceiver name\n");
+ pr_err("get_xcvr(): No tranceiver name\n");
return NULL;
}
for (i = 0; i < MXC_NUMBER_USB_TRANSCEIVER; i++) {
+ if (g_xc_ops[i] == NULL)
+ continue;
if (strcmp(g_xc_ops[i]->name, name) == 0) {
return g_xc_ops[i];
}
}
- pr_debug("Failed %s\n", __func__);
return NULL;
}
@@ -180,8 +174,6 @@ __init struct platform_device *host_pdev_register(struct resource *res, int n_re
struct platform_device *pdev;
int rc;
- pr_debug("register host res=0x%p, size=%d\n", res, n_res);
-
pdev = platform_device_register_simple("fsl-ehci",
usb_mxc_instance_id, res, n_res);
if (IS_ERR(pdev)) {
@@ -209,8 +201,6 @@ __init struct platform_device *host_pdev_register(struct resource *res, int n_re
printk(KERN_INFO "usb: %s host (%s) registered\n", config->name,
config->transceiver);
- pr_debug("pdev=0x%p dev=0x%p resources=0x%p pdata=0x%p\n",
- pdev, &pdev->dev, pdev->resource, pdev->dev.platform_data);
usb_mxc_instance_id++;
@@ -219,7 +209,6 @@ __init struct platform_device *host_pdev_register(struct resource *res, int n_re
static void usbh1_set_serial_xcvr(void)
{
- pr_debug("%s: \n", __func__);
USBCTRL &= ~(UCTRL_H1SIC_MASK | UCTRL_BPE); /* disable bypass mode */
USBCTRL |= UCTRL_H1SIC_SU6 | /* single-ended / unidir. */
UCTRL_H1WIE | UCTRL_H1DT | /* disable H1 TLL */
@@ -228,8 +217,6 @@ static void usbh1_set_serial_xcvr(void)
static void usbh1_set_ulpi_xcvr(void)
{
- pr_debug("%s: \n", __func__);
-
/* Stop then Reset */
UH1_USBCMD &= ~UCMD_RUN_STOP;
while (UH1_USBCMD & UCMD_RUN_STOP)
@@ -333,8 +320,6 @@ static void usbh2_set_ulpi_xcvr(void)
{
u32 tmp;
- pr_debug("%s\n", __func__);
-
UH2_USBCMD &= ~UCMD_RUN_STOP;
while (UH2_USBCMD & UCMD_RUN_STOP)
;
@@ -371,8 +356,6 @@ static void usbh2_set_ulpi_xcvr(void)
static void usbh2_set_serial_xcvr(void)
{
- pr_debug("%s: \n", __func__);
-
/* Stop then Reset */
UH2_USBCMD &= ~UCMD_RUN_STOP;
while (UH2_USBCMD & UCMD_RUN_STOP)
@@ -440,7 +423,6 @@ static int usb_register_remote_wakeup(struct platform_device *pdev)
struct resource *res;
int irq;
- pr_debug("%s: pdev=0x%p \n", __func__, pdev);
if (!(pdata->wake_up_enable))
return -ECANCELED;
@@ -463,8 +445,6 @@ int fsl_usb_host_init(struct platform_device *pdev)
struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data;
struct fsl_xcvr_ops *xops;
- pr_debug("%s: pdev=0x%p pdata=0x%p\n", __func__, pdev, pdata);
-
xops = fsl_usb_get_xcvr(pdata->transceiver);
if (!xops) {
printk(KERN_ERR "%s transceiver ops missing\n", pdata->name);
@@ -477,7 +457,6 @@ int fsl_usb_host_init(struct platform_device *pdev)
if (fsl_check_usbclk() != 0)
return -EINVAL;
- pr_debug("%s: grab pins\n", __func__);
if (pdata->gpio_usb_active && pdata->gpio_usb_active())
return -EINVAL;
@@ -522,20 +501,15 @@ int fsl_usb_host_init(struct platform_device *pdev)
usbh1_set_utmi_xcvr();
}
- pr_debug("%s: %s success\n", __func__, pdata->name);
return 0;
}
EXPORT_SYMBOL(fsl_usb_host_init);
void fsl_usb_host_uninit(struct fsl_usb2_platform_data *pdata)
{
- pr_debug("%s\n", __func__);
-
if (pdata->xcvr_ops && pdata->xcvr_ops->uninit)
pdata->xcvr_ops->uninit(pdata->xcvr_ops);
- pdata->regs = NULL;
-
if (pdata->gpio_usb_inactive)
pdata->gpio_usb_inactive();
if (pdata->xcvr_type == PORTSC_PTS_SERIAL) {
@@ -558,7 +532,9 @@ void fsl_usb_host_uninit(struct fsl_usb2_platform_data *pdata)
regulator_disable(pdata->xcvr_pwr->regu2);
}
- clk_disable(usb_ahb_clk);
+ pdata->regs = NULL;
+ if (usb_ahb_clk)
+ clk_disable(usb_ahb_clk);
}
EXPORT_SYMBOL(fsl_usb_host_uninit);
@@ -569,7 +545,6 @@ static void otg_set_serial_xcvr(void)
void otg_set_serial_host(void)
{
- pr_debug("%s\n", __func__);
/* set USBCTRL for host operation
* disable: bypass mode,
* set: single-ended/unidir/6 wire, OTG wakeup intr enable,
@@ -624,7 +599,6 @@ static void otg_set_ulpi_xcvr(void)
{
u32 tmp;
- pr_debug("%s\n", __func__);
USBCTRL &= ~UCTRL_OSIC_MASK;
#if defined(CONFIG_ARCH_MX27) || defined(CONFIG_ARCH_MX3)
USBCTRL &= ~UCTRL_BPE;
@@ -775,8 +749,6 @@ int usbotg_init(struct platform_device *pdev)
struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data;
struct fsl_xcvr_ops *xops;
- pr_debug("%s: pdev=0x%p pdata=0x%p\n", __func__, pdev, pdata);
-
xops = fsl_usb_get_xcvr(pdata->transceiver);
if (!xops) {
printk(KERN_ERR "DR transceiver ops missing\n");
@@ -793,7 +765,6 @@ int usbotg_init(struct platform_device *pdev)
/* Turn on AHB CLK for OTG*/
USB_CLKONOFF_CTRL &= ~OTG_AHBCLK_OFF;
- pr_debug("%s: grab pins\n", __func__);
if (pdata->gpio_usb_active && pdata->gpio_usb_active())
return -EINVAL;
@@ -826,15 +797,12 @@ int usbotg_init(struct platform_device *pdev)
pr_debug("DR is not a wakeup source.\n");
mxc_otg_used++;
- pr_debug("%s: success\n", __func__);
return 0;
}
EXPORT_SYMBOL(usbotg_init);
void usbotg_uninit(struct fsl_usb2_platform_data *pdata)
{
- pr_debug("%s\n", __func__);
-
mxc_otg_used--;
if (!mxc_otg_used) {
if (pdata->xcvr_ops && pdata->xcvr_ops->uninit)
diff --git a/arch/arm/plat-mxc/usb_wakeup.c b/arch/arm/plat-mxc/usb_wakeup.c
new file mode 100644
index 00000000000..80bc6c9861d
--- /dev/null
+++ b/arch/arm/plat-mxc/usb_wakeup.c
@@ -0,0 +1,224 @@
+/*
+ * Copyright 2009-2011 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ * *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+*/
+
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/kthread.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <linux/fsl_devices.h>
+#include <linux/suspend.h>
+
+struct wakeup_ctrl {
+ int wakeup_irq;
+ int usb_irq;
+ struct fsl_usb2_wakeup_platform_data *pdata;
+ struct task_struct *thread;
+ struct completion event;
+};
+static struct wakeup_ctrl *g_ctrl;
+
+extern int usb_event_is_otg_wakeup(void);
+extern void usb_debounce_id_vbus(void);
+
+static void wakeup_clk_gate(struct fsl_usb2_wakeup_platform_data *pdata, bool on)
+{
+ if (pdata->usb_clock_for_pm)
+ pdata->usb_clock_for_pm(on);
+}
+
+static bool usb2_is_in_lowpower(struct wakeup_ctrl *ctrl)
+{
+ int i;
+ struct fsl_usb2_wakeup_platform_data *pdata = ctrl->pdata;
+ /* all the usb module related the wakeup is in lowpower mode */
+ for (i = 0; i < 3; i++) {
+ if (pdata->usb_pdata[i]) {
+ if (pdata->usb_pdata[i]->phy_lowpower_suspend && !pdata->usb_pdata[i]->lowpower)
+ return false;
+ }
+ }
+ return true;
+}
+
+static void delay_process_wakeup(struct wakeup_ctrl *ctrl)
+{
+ int i;
+ struct fsl_usb2_wakeup_platform_data *pdata = ctrl->pdata;
+ disable_irq_nosync(ctrl->wakeup_irq);
+ if ((ctrl->usb_irq > 0) && (ctrl->wakeup_irq != ctrl->usb_irq))
+ disable_irq_nosync(ctrl->usb_irq);
+
+ for (i = 0; i < 3; i++) {
+ if (pdata->usb_pdata[i]) {
+ pdata->usb_pdata[i]->irq_delay = 1;
+ }
+ }
+ pdata->usb_wakeup_is_pending = true;
+ complete(&ctrl->event);
+}
+
+static irqreturn_t usb_wakeup_handler(int irq, void *_dev)
+{
+ struct wakeup_ctrl *ctrl = (struct wakeup_ctrl *)_dev;
+ irqreturn_t ret = IRQ_NONE;
+
+ if (usb2_is_in_lowpower(ctrl)) {
+ printk(KERN_INFO "usb wakeup is here\n");
+ delay_process_wakeup(ctrl);
+ ret = IRQ_HANDLED;
+ }
+ return ret;
+}
+
+static enum usb_wakeup_event is_wakeup(struct fsl_usb2_platform_data *pdata)
+{
+ if (pdata->is_wakeup_event)
+ return pdata->is_wakeup_event(pdata);
+ else
+ return WAKEUP_EVENT_INVALID;
+}
+
+static void wakeup_event_handler(struct wakeup_ctrl *ctrl)
+{
+ struct fsl_usb2_wakeup_platform_data *pdata = ctrl->pdata;
+ int already_waked = 0;
+ enum usb_wakeup_event wakeup_evt;
+ int i, cnt = 0;
+
+ wakeup_clk_gate(ctrl->pdata, true);
+
+recheck:
+ /* In order to get the real id/vbus value */
+ if (usb_event_is_otg_wakeup())
+ msleep(10); /* usb_debounce_id_vbus(); */
+
+ for (i = 0; i < 3; i++) {
+ struct fsl_usb2_platform_data *usb_pdata = pdata->usb_pdata[i];
+ if (usb_pdata) {
+ usb_pdata->irq_delay = 0;
+ wakeup_evt = is_wakeup(usb_pdata);
+ if (wakeup_evt != WAKEUP_EVENT_INVALID) {
+ if (usb_pdata->usb_clock_for_pm)
+ usb_pdata->usb_clock_for_pm(true);
+ usb_pdata->lowpower = 0;
+ already_waked = 1;
+ if (usb_pdata->wakeup_handler) {
+ usb_pdata->wakeup_handler(usb_pdata);
+ }
+ }
+ }
+ }
+ /* for IC: ID/VBUS status change after wakeup interrupt */
+ if ((cnt++ < 5) && (already_waked == 0))
+ goto recheck;
+ /* If nothing to wakeup, clear wakeup event */
+ if ((already_waked == 0) && pdata->usb_wakeup_exhandle)
+ pdata->usb_wakeup_exhandle();
+
+ wakeup_clk_gate(ctrl->pdata, false);
+ pdata->usb_wakeup_is_pending = false;
+ wake_up(&pdata->wq);
+}
+
+static int wakeup_event_thread(void *param)
+{
+ struct wakeup_ctrl *ctrl = (struct wakeup_ctrl *)param;
+ struct sched_param sch_param = {.sched_priority = 1};
+
+ sched_setscheduler(current, SCHED_RR, &sch_param);
+ while (1) {
+ wait_for_completion_interruptible(&ctrl->event);
+ if (kthread_should_stop())
+ break;
+ wakeup_event_handler(ctrl);
+ enable_irq(ctrl->wakeup_irq);
+ if ((ctrl->usb_irq > 0) && (ctrl->wakeup_irq != ctrl->usb_irq))
+ enable_irq(ctrl->usb_irq);
+ }
+ return 0;
+}
+
+static int wakeup_dev_probe(struct platform_device *pdev)
+{
+ struct fsl_usb2_wakeup_platform_data *pdata;
+ struct wakeup_ctrl *ctrl = NULL;
+ int status;
+
+ printk(KERN_INFO "IMX usb wakeup probe\n");
+
+ if (!pdev || !pdev->dev.platform_data)
+ return -ENODEV;
+ ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);
+ if (!ctrl)
+ return -ENOMEM;
+ pdata = pdev->dev.platform_data;
+ init_waitqueue_head(&pdata->wq);
+ pdata->usb_wakeup_is_pending = false;
+
+ ctrl->pdata = pdata;
+ init_completion(&ctrl->event);
+ ctrl->wakeup_irq = platform_get_irq(pdev, 0);
+ status = request_irq(ctrl->wakeup_irq, usb_wakeup_handler, IRQF_SHARED, "usb_wakeup", (void *)ctrl);
+ if (status)
+ goto error1;
+ ctrl->usb_irq = platform_get_irq(pdev, 1);
+
+ ctrl->thread = kthread_run(wakeup_event_thread, (void *)ctrl, "usb_wakeup thread");
+ status = IS_ERR(ctrl->thread) ? -1 : 0;
+ if (status)
+ goto error2;
+ g_ctrl = ctrl;
+ printk(KERN_DEBUG "the wakeup pdata is 0x%p\n", pdata);
+
+ return 0;
+error2:
+ free_irq(ctrl->wakeup_irq, (void *)ctrl);
+error1:
+ kfree(ctrl);
+ return status;
+}
+
+static int wakeup_dev_exit(struct platform_device *pdev)
+{
+ if (g_ctrl->thread) {
+ complete(&g_ctrl->event);
+ kthread_stop(g_ctrl->thread);
+ }
+ free_irq(g_ctrl->wakeup_irq, (void *)g_ctrl);
+ kfree(g_ctrl);
+ return 0;
+}
+static struct platform_driver wakeup_d = {
+ .probe = wakeup_dev_probe,
+ .remove = wakeup_dev_exit,
+ .driver = {
+ .name = "usb_wakeup",
+ },
+};
+
+static int __init wakeup_dev_init(void)
+{
+ return platform_driver_register(&wakeup_d);
+}
+static void __exit wakeup_dev_uninit(void)
+{
+ platform_driver_unregister(&wakeup_d);
+}
+
+subsys_initcall(wakeup_dev_init);
+module_exit(wakeup_dev_uninit);
+
diff --git a/arch/arm/plat-mxc/utmixc.c b/arch/arm/plat-mxc/utmixc.c
new file mode 100644
index 00000000000..e03b9510c55
--- /dev/null
+++ b/arch/arm/plat-mxc/utmixc.c
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2005-2011 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/fsl_devices.h>
+#include <linux/usb/fsl_xcvr.h>
+
+#include <mach/hardware.h>
+#include <mach/arc_otg.h>
+#include <asm/mach-types.h>
+
+static struct regulator *usbotg_regux;
+
+static void usb_utmi_init(struct fsl_xcvr_ops *this)
+{
+#if defined(CONFIG_MXC_PMIC_MC13892_MODULE) || defined(CONFIG_MXC_PMIC_MC13892)
+ if (machine_is_mx51_3ds()) {
+ unsigned int value;
+
+ /* VUSBIN */
+ pmic_read_reg(REG_USB1, &value, 0xffffff);
+ value |= 0x1;
+ value |= (0x1 << 3);
+ pmic_write_reg(REG_USB1, value, 0xffffff);
+ }
+#endif
+}
+
+static void usb_utmi_uninit(struct fsl_xcvr_ops *this)
+{
+}
+
+/*!
+ * set vbus power
+ *
+ * @param view viewport register
+ * @param on power on or off
+ */
+static void set_power(struct fsl_xcvr_ops *this,
+ struct fsl_usb2_platform_data *pdata, int on)
+{
+ struct device *dev = &pdata->pdev->dev;
+
+ pr_debug("real %s(on=%d) pdata=0x%p\n", __func__, on, pdata);
+ if (pdata && pdata->platform_driver_vbus)
+ pdata->platform_driver_vbus(on);
+}
+
+static struct fsl_xcvr_ops utmi_ops = {
+ .name = "utmi",
+ .xcvr_type = PORTSC_PTS_UTMI,
+ .init = usb_utmi_init,
+ .uninit = usb_utmi_uninit,
+ .set_vbus_power = set_power,
+};
+
+extern void fsl_usb_xcvr_register(struct fsl_xcvr_ops *xcvr_ops);
+
+static int __init utmixc_init(void)
+{
+ fsl_usb_xcvr_register(&utmi_ops);
+ return 0;
+}
+
+extern void fsl_usb_xcvr_unregister(struct fsl_xcvr_ops *xcvr_ops);
+
+static void __exit utmixc_exit(void)
+{
+ fsl_usb_xcvr_unregister(&utmi_ops);
+}
+
+subsys_initcall(utmixc_init);
+module_exit(utmixc_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("utmi xcvr driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/arcotg_udc.c b/drivers/usb/gadget/arcotg_udc.c
index 309b13995b8..bf09184e83d 100644
--- a/drivers/usb/gadget/arcotg_udc.c
+++ b/drivers/usb/gadget/arcotg_udc.c
@@ -47,6 +47,7 @@
#include <asm/unaligned.h>
#include <asm/dma.h>
#include <asm/cacheflush.h>
+#include <asm/mach-types.h>
#include "arcotg_udc.h"
#include <mach/arc_otg.h>
@@ -2169,8 +2170,10 @@ bool try_wake_up_udc(struct fsl_udc *udc)
u32 tmp;
fsl_writel(irq_src, &dr_regs->otgsc);
/* only handle device interrupt event */
- if (!(fsl_readl(&dr_regs->otgsc) & OTGSC_STS_USB_ID))
- return false;
+ if (!machine_is_mx53_loco()) {
+ if (!(fsl_readl(&dr_regs->otgsc) & OTGSC_STS_USB_ID))
+ return false;
+ }
tmp = fsl_readl(&dr_regs->usbcmd);
/* check BSV bit to see if fall or rise */
diff --git a/drivers/usb/host/ehci-arc.c b/drivers/usb/host/ehci-arc.c
index a5f46fee2bc..8a0a22c3334 100644
--- a/drivers/usb/host/ehci-arc.c
+++ b/drivers/usb/host/ehci-arc.c
@@ -264,7 +264,7 @@ err2:
err1:
dev_err(&pdev->dev, "init %s fail, %d\n", dev_name(&pdev->dev), retval);
if (pdata->exit)
- pdata->exit(pdata->pdev);
+ pdata->exit(pdev);
return retval;
}
diff --git a/include/linux/fsl_devices.h b/include/linux/fsl_devices.h
index 058b39ec290..4386d3c0f97 100644
--- a/include/linux/fsl_devices.h
+++ b/include/linux/fsl_devices.h
@@ -67,6 +67,13 @@ enum fsl_usb2_phy_modes {
FSL_USB2_PHY_SERIAL,
};
+enum usb_wakeup_event {
+ WAKEUP_EVENT_INVALID,
+ WAKEUP_EVENT_VBUS,
+ WAKEUP_EVENT_ID,
+ WAKEUP_EVENT_DPDM, /* for remote wakeup */
+};
+
struct clk;
struct platform_device;