diff options
author | Haitao Zhang <haitao.zhang@linaro.org> | 2011-08-31 16:36:16 +0800 |
---|---|---|
committer | Bernhard Rosenkraenzer <Bernhard.Rosenkranzer@linaro.org> | 2011-09-09 20:58:51 +0159 |
commit | 7d16334b6446f9545ff17939a56b06889bfb33a3 (patch) | |
tree | 02c6a2e9afaac71cf8ec3de361ddf38bb95f4884 | |
parent | 7d06768691473ace0e679e34d90c0555d10011f6 (diff) | |
download | imx53-7d16334b6446f9545ff17939a56b06889bfb33a3.tar.gz |
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/Makefile | 3 | ||||
-rw-r--r-- | arch/arm/mach-mx5/board-mx53_loco.c | 28 | ||||
-rw-r--r-- | arch/arm/mach-mx5/devices.c | 33 | ||||
-rw-r--r-- | arch/arm/mach-mx5/devices.h | 2 | ||||
-rw-r--r-- | arch/arm/mach-mx5/usb.h | 47 | ||||
-rw-r--r-- | arch/arm/mach-mx5/usb_h1.c | 278 | ||||
-rw-r--r-- | arch/arm/plat-mxc/Kconfig | 6 | ||||
-rw-r--r-- | arch/arm/plat-mxc/Makefile | 1 | ||||
-rw-r--r-- | arch/arm/plat-mxc/utmixc.c | 94 | ||||
-rw-r--r-- | include/linux/fsl_devices.h | 7 |
10 files changed, 495 insertions, 4 deletions
diff --git a/arch/arm/mach-mx5/Makefile b/arch/arm/mach-mx5/Makefile index 59e968c59cc..4ca8985f6fb 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 usb_h1.o obj-$(CONFIG_SOC_IMX50) += mm-mx50.o obj-$(CONFIG_SOC_IMX51) += clock.o obj-$(CONFIG_SOC_IMX53) += clock.o diff --git a/arch/arm/mach-mx5/board-mx53_loco.c b/arch/arm/mach-mx5/board-mx53_loco.c index ea78615a7b3..51b4b95de04 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 */ @@ -375,10 +379,20 @@ 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; + imx53_soc_init(); + mxc_iomux_v3_setup_multiple_pads(mx53_loco_pads, ARRAY_SIZE(mx53_loco_pads)); @@ -408,7 +422,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(); @@ -457,6 +471,18 @@ static void __init mx53_loco_board_init(void) else gpu_data.z160_revision = 0; imx53_add_mxc_gpu(&gpu_data); + + /* USB */ + /* usb host1 vbus */ + imx_otg_base = MX53_IO_ADDRESS(MX53_OTG_BASE_ADDR); + 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(); } 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 4efa1d869e8..b6874a9c180 100644 --- a/arch/arm/mach-mx5/devices.c +++ b/arch/arm/mach-mx5/devices.c @@ -97,6 +97,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, @@ -183,6 +194,24 @@ int __init imx53_register_gpios(void) } 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..67c0991a3d0 100644 --- a/arch/arm/mach-mx5/devices.h +++ b/arch/arm/mach-mx5/devices.h @@ -1,6 +1,8 @@ extern struct platform_device mxc_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 mxc_hsi2c_device; extern struct platform_device mxc_pm_device; +extern struct platform_device mx53_usbh1_wakeup_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_h1.c b/arch/arm/mach-mx5/usb_h1.c new file mode 100644 index 00000000000..b791712a287 --- /dev/null +++ b/arch/arm/mach-mx5/usb_h1.c @@ -0,0 +1,278 @@ +/* + * 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; + int ret; + struct clk *usb_clk; + + /* 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"); + clk_enable(usb_clk); + usb_oh3_clk = usb_clk; + + usb_clk = clk_get(NULL, "usb_phy2_clk"); + clk_enable(usb_clk); + usb_phy2_clk = usb_clk; + } else if (cpu_is_mx50()) { + usb_clk = clk_get(NULL, "usb_phy2_clk"); + clk_enable(usb_clk); + usb_phy2_clk = usb_clk; + } else if (cpu_is_mx51()) { + usb_clk = clk_get(NULL, "usboh3_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()) { + clk_disable(usb_oh3_clk); + clk_put(usb_oh3_clk); + + clk_disable(usb_phy2_clk); + clk_put(usb_phy2_clk); + } else if (cpu_is_mx50()) { + clk_disable(usb_phy2_clk); + clk_put(usb_phy2_clk); + } else if (cpu_is_mx51()) { + 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 351fc601ebb..e036a0f01af 100644 --- a/arch/arm/plat-mxc/Makefile +++ b/arch/arm/plat-mxc/Makefile @@ -31,3 +31,4 @@ obj-$(CONFIG_ARCH_MX5) += dvfs_core.o # DVFS-PER support obj-$(CONFIG_MXC_DVFS_PER) += dvfs_per.o +obj-$(CONFIG_UTMI_MXC) += utmixc.o 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/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; |