From eafa475d18391f7930a46e0e0c7ccf35c3949c2f Mon Sep 17 00:00:00 2001 From: Haitao Zhang Date: Wed, 31 Aug 2011 16:36:40 +0800 Subject: mx53_loco: enabled USB Host1 forward ported code from 2.6.38 Signed-off-by: Haitao Zhang Signed-off-by: Eric Miao --- arch/arm/mach-mx5/Makefile | 4 +- arch/arm/mach-mx5/board-mx53_loco.c | 27 ++- arch/arm/mach-mx5/devices.c | 96 ++++++++++- arch/arm/mach-mx5/devices.h | 6 + arch/arm/mach-mx5/usb.h | 47 +++++ arch/arm/mach-mx5/usb_dr.c | 334 ++++++++++++++++++++++++++++++++++++ arch/arm/mach-mx5/usb_h1.c | 295 +++++++++++++++++++++++++++++++ arch/arm/plat-mxc/Kconfig | 6 + arch/arm/plat-mxc/Makefile | 5 +- arch/arm/plat-mxc/usb_common.c | 44 +---- arch/arm/plat-mxc/usb_wakeup.c | 224 ++++++++++++++++++++++++ arch/arm/plat-mxc/utmixc.c | 94 ++++++++++ drivers/usb/gadget/arcotg_udc.c | 7 +- drivers/usb/host/ehci-arc.c | 2 +- include/linux/fsl_devices.h | 7 + 15 files changed, 1152 insertions(+), 46 deletions(-) create mode 100644 arch/arm/mach-mx5/usb.h create mode 100755 arch/arm/mach-mx5/usb_dr.c create mode 100644 arch/arm/mach-mx5/usb_h1.c create mode 100644 arch/arm/plat-mxc/usb_wakeup.c create mode 100644 arch/arm/plat-mxc/utmixc.c diff --git a/arch/arm/mach-mx5/Makefile b/arch/arm/mach-mx5/Makefile index 6e315fd19af..5411507ce16 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 @@ -27,3 +28,4 @@ obj-$(CONFIG_MACH_MX50_RDP) += board-mx50_rdp.o obj-$(CONFIG_MACH_IMX51_DT) += imx51-dt.o obj-$(CONFIG_MACH_IMX53_DT) += imx53-dt.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 9f7a84feb7f..8ec8ef87fc3 100644 --- a/arch/arm/mach-mx5/board-mx53_loco.c +++ b/arch/arm/mach-mx5/board-mx53_loco.c @@ -40,6 +40,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) @@ -55,6 +56,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 */ @@ -408,6 +412,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; @@ -443,7 +455,7 @@ static void __init mx53_loco_io_init(void) static void __init mx53_loco_board_init(void) { - int i; + int i, ret; mx53_loco_io_init(); @@ -497,6 +509,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 +#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 +#include +#include +#include +#include +#include +#include +#include +#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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#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 f58e94c27bc..1c920b2b881 100644 --- a/arch/arm/plat-mxc/Kconfig +++ b/arch/arm/plat-mxc/Kconfig @@ -39,6 +39,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 6419e31d5f1..bc2a8237dae 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 obj-$(CONFIG_ARM_GIC) += gic.o obj-$(CONFIG_MXC_TZIC) += tzic.o @@ -30,3 +30,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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +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 #include #include +#include #include "arcotg_udc.h" #include @@ -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; -- cgit v1.2.3