diff options
author | Adam Lussier <adam.lussier@timesys.com> | 2013-02-25 17:19:04 -0500 |
---|---|---|
committer | Adam Lussier <adam.lussier@timesys.com> | 2013-02-25 17:19:04 -0500 |
commit | f962cbcefdfad22ffff41e19ad4c5ff58815c05c (patch) | |
tree | e9424582f1fdc92d360ff58c831ddf801a268e3f /arch/arm/mach-mvf | |
parent | 15f55e0b22f8953b56fb5d6bdf8b770228f2f449 (diff) | |
parent | 16af5ee7d9556c47b332788e4107cbed5ee7ec10 (diff) |
Merge remote-tracking branch 'github/3.0-pcm052' into 3.0-pcm0523.0-pcm052-ts2
Release 3.0-ts2: enable USB support for the phyCORE Vybrid
Diffstat (limited to 'arch/arm/mach-mvf')
-rw-r--r-- | arch/arm/mach-mvf/Kconfig | 1 | ||||
-rw-r--r-- | arch/arm/mach-mvf/Makefile | 4 | ||||
-rw-r--r-- | arch/arm/mach-mvf/board-pcm052.c | 15 | ||||
-rw-r--r-- | arch/arm/mach-mvf/board-twr-vf700.c | 39 | ||||
-rw-r--r-- | arch/arm/mach-mvf/clock.c | 50 | ||||
-rw-r--r-- | arch/arm/mach-mvf/cpu.c | 3 | ||||
-rw-r--r-- | arch/arm/mach-mvf/crm_regs.h | 1 | ||||
-rw-r--r-- | arch/arm/mach-mvf/devices-mvf.h | 42 | ||||
-rw-r--r-- | arch/arm/mach-mvf/irq.c | 10 | ||||
-rw-r--r-- | arch/arm/mach-mvf/mvf_suspend.S | 484 | ||||
-rw-r--r-- | arch/arm/mach-mvf/pm.c | 312 | ||||
-rw-r--r-- | arch/arm/mach-mvf/regs-pm.h | 77 | ||||
-rw-r--r-- | arch/arm/mach-mvf/regs-src.h | 45 | ||||
-rw-r--r-- | arch/arm/mach-mvf/system.c | 139 | ||||
-rw-r--r-- | arch/arm/mach-mvf/usb_dr.c | 310 | ||||
-rw-r--r-- | arch/arm/mach-mvf/usb_dr2.c | 199 |
16 files changed, 1655 insertions, 76 deletions
diff --git a/arch/arm/mach-mvf/Kconfig b/arch/arm/mach-mvf/Kconfig index 23df64879243..0ef1f8a09d37 100644 --- a/arch/arm/mach-mvf/Kconfig +++ b/arch/arm/mach-mvf/Kconfig @@ -54,6 +54,7 @@ config MACH_MVFA5_TWR_VF700 select IMX_HAVE_PLATFORM_MVF_DCU select IMX_HAVE_PLATFORM_MVF_SAI select IMX_HAVE_PLATFORM_MXC_NAND + select IMX_HAVE_PLATFORM_MVF_CAAM help Include support for MVF TWR-VF700 platform. This includes specific configurations for the board and its peripherals. diff --git a/arch/arm/mach-mvf/Makefile b/arch/arm/mach-mvf/Makefile index 07f33a555dcb..fad70a4a9dc7 100644 --- a/arch/arm/mach-mvf/Makefile +++ b/arch/arm/mach-mvf/Makefile @@ -3,9 +3,9 @@ # # Object file lists. -obj-y := cpu.o mm.o system.o devices.o dummy_gpio.o irq.o bus_freq.o mvf_fec.o usb_dr.o usb_dr2.o +obj-y := cpu.o mm.o system.o devices.o dummy_gpio.o irq.o bus_freq.o mvf_fec.o usb_dr.o usb_dr2.o pm.o obj-y += l2switch.o -obj-$(CONFIG_ARCH_MVFA5) += clock.o +obj-$(CONFIG_ARCH_MVFA5) += clock.o mvf_suspend.o obj-$(CONFIG_MACH_MVFA5_TWR_VF700) += board-twr-vf700.o obj-$(CONFIG_MACH_PCM052) += board-pcm052.o diff --git a/arch/arm/mach-mvf/board-pcm052.c b/arch/arm/mach-mvf/board-pcm052.c index 08b071f1dcf7..84bd65623ebb 100644 --- a/arch/arm/mach-mvf/board-pcm052.c +++ b/arch/arm/mach-mvf/board-pcm052.c @@ -195,7 +195,7 @@ static iomux_v3_cfg_t pcm052_pads[] = { #ifdef PCM952_REV0 MVF600_PAD6_PTA16__USB0_VBUS_EN, #else - MVF600_PAD134_PTA7__USB0_VBUS_EN, + MVF600_PAD134_PTA7__USB_VBUS_EN, #endif MVF600_PAD7_PTA17__USB_OC_N, @@ -275,7 +275,7 @@ static struct switch_platform_data switch_data __initdata = { }; static int pcm052_spi_cs[] = { - 134, + 41, }; static const struct spi_mvf_master pcm052_spi_data __initconst = { @@ -479,14 +479,15 @@ static struct led_pwm_platform_data mvf_led_data __initdata = { .leds = &mvf_led, }; +#define USB_VBUS_ENABLE_PIN 134 static void __init pcm052_init_usb(void) { - imx_otg_base = MVF_IO_ADDRESS(MVF_USBC0_BASE_ADDR); - /*mvf_set_otghost_vbus_func(pcm052_usbotg_vbus);*/ -#ifdef CONFIG_USB_GADGET_ARC - mvf_usb_dr_init(); -#endif + gpio_request_one(USB_VBUS_ENABLE_PIN, GPIOF_OUT_INIT_LOW, "VBUS_EN"); + msleep(2); + gpio_set_value(USB_VBUS_ENABLE_PIN, 1); + #ifdef CONFIG_USB_EHCI_ARC + mvf_usb_dr_init(); mvf_usb_dr2_init(); #endif } diff --git a/arch/arm/mach-mvf/board-twr-vf700.c b/arch/arm/mach-mvf/board-twr-vf700.c index 03cc6ad51788..01fd4dc364cc 100644 --- a/arch/arm/mach-mvf/board-twr-vf700.c +++ b/arch/arm/mach-mvf/board-twr-vf700.c @@ -72,6 +72,7 @@ #include <mach/mipi_dsi.h> #include <mach/mipi_csi2.h> #include <mach/fsl_l2_switch.h> +#include <mach/mxc.h> #include <asm/irq.h> #include <asm/setup.h> #include <asm/mach-types.h> @@ -364,22 +365,20 @@ static void spi_device_init(void) ARRAY_SIZE(mvf_spi_board_info)); } -#if 1 -static void vf700_suspend_enter(void) +static void vf600_suspend_enter(void) { /* suspend preparation */ } -static void vf700_suspend_exit(void) +static void vf600_suspend_exit(void) { /* resmue resore */ } -static const struct pm_platform_data mvf_vf700_pm_data __initconst = { +static const struct pm_platform_data mvf_vf600_pm_data __initconst = { .name = "mvf_pm", - .suspend_enter = vf700_suspend_enter, - .suspend_exit = vf700_suspend_exit, + .suspend_enter = vf600_suspend_enter, + .suspend_exit = vf600_suspend_exit, }; -#endif static struct mvf_dcu_platform_data mvf_dcu_pdata = { .mode_str = "480x272", @@ -427,16 +426,26 @@ static struct led_pwm_platform_data mvf_led_data __initdata = { .leds = &mvf_led, }; +static struct imx_asrc_platform_data imx_asrc_data = { + .channel_bits = 4, + .clk_map_ver = 3, +}; + static void __init mvf_twr_init_usb(void) { imx_otg_base = MVF_IO_ADDRESS(MVF_USBC0_BASE_ADDR); /*mvf_set_otghost_vbus_func(mvf_twr_usbotg_vbus);*/ -#ifdef CONFIG_USB_GADGET_ARC - mvf_usb_dr_init(); -#endif #ifdef CONFIG_USB_EHCI_ARC mvf_usb_dr2_init(); #endif +#ifdef CONFIG_USB_GADGET_ARC + mvf_usb_dr_init(); +#endif +} + +static void __init mvf_init_adc(void) +{ + mvf_add_adc(0); } /*! @@ -458,6 +467,12 @@ static void __init mvf_board_init(void) mvf_add_snvs_rtc(); + mvf_init_adc(); + + mvf_add_pm_imx(0, &mvf_vf600_pm_data); + + mvf700_add_caam(); + mvf_add_sdhci_esdhc_imx(1, &mvfa5_sd1_data); mvf_add_imx_i2c(0, &mvf600_i2c_data); @@ -482,6 +497,10 @@ static void __init mvf_board_init(void) mvf_add_mxc_pwm(0); mvf_add_pwm_leds(&mvf_led_data); + imx_asrc_data.asrc_core_clk = clk_get(NULL, "asrc_clk"); + imx_asrc_data.asrc_audio_clk = clk_get(NULL, "asrc_serial_clk"); + mvf_add_asrc(&imx_asrc_data); + } static void __init mvf_timer_init(void) diff --git a/arch/arm/mach-mvf/clock.c b/arch/arm/mach-mvf/clock.c index 25e1079165dd..74ce8a9f290e 100644 --- a/arch/arm/mach-mvf/clock.c +++ b/arch/arm/mach-mvf/clock.c @@ -670,10 +670,12 @@ static int _clk_pll3_usb_otg_set_rate(struct clk *clk, unsigned long rate) else return -EINVAL; +#ifndef CONFIG_MACH_PCM052 reg = __raw_readl(PLL3_480_USB1_BASE_ADDR); reg &= ~ANADIG_PLL_480_DIV_SELECT_MASK; reg |= div; __raw_writel(reg, PLL3_480_USB1_BASE_ADDR); +#endif return 0; } @@ -1613,6 +1615,14 @@ static struct clk clko2_clk = { .get_rate = _clk_clko2_get_rate, .round_rate = _clk_clko_round_rate, }; +static struct clk caam_clk = { + __INIT_CLK_DEBUG(caam_clk) + .parent = &ipg_clk, + .enable_reg = MXC_CCM_CCGR11, + .enable_shift = MXC_CCM_CCGRx_CG0_OFFSET, + .enable = _clk_enable, + .disable = _clk_disable, +}; static struct clk pit_clk = { __INIT_CLK_DEBUG(pit_clk) @@ -1626,6 +1636,18 @@ static struct clk pit_clk = { .get_rate = _clk_uart_get_rate, }; +static struct clk adc_clk[] = { + { + __INIT_CLK_DEBUG(adc_clk) + .id = 0, + .parent = &ipg_clk, + .enable_reg = MXC_CCM_CCGR1, + .enable_shift = MXC_CCM_CCGRx_CG11_OFFSET, + .enable = _clk_enable, + .disable = _clk_disable, + }, +}; + static struct clk i2c_clk[] = { { __INIT_CLK_DEBUG(i2c_clk_0) @@ -1845,6 +1867,29 @@ static struct clk qspi1_clk = { .get_rate = _clk_qspi1_get_rate, }; +static int _clk_asrc_serial_set_rate(struct clk *clk, unsigned long rate) +{ + return 0; +} + +static struct clk asrc_clk[] = { + { + __INIT_CLK_DEBUG(asrc_clk) + .id = 0, + .parent = &ipg_clk, + .enable_reg = MXC_CCM_CCGR4, + .enable_shift = MXC_CCM_CCGRx_CG1_OFFSET, + .enable = _clk_enable, + .disable = _clk_disable, + }, + { + __INIT_CLK_DEBUG(asrc_serial_clk) + .id = 1, + .parent = &audio_external_clk, + .set_rate = _clk_asrc_serial_set_rate, + }, +}; + static struct clk dummy_clk = { .id = 0, }; @@ -1890,6 +1935,7 @@ static struct clk_lookup lookups[] = { _REGISTER_CLOCK("pit", NULL, pit_clk), _REGISTER_CLOCK("fec.0", NULL, enet_clk[0]), _REGISTER_CLOCK("fec.1", NULL, enet_clk[1]), + _REGISTER_CLOCK("mvf-adc.0", NULL, adc_clk[0]), _REGISTER_CLOCK("switch.0", NULL, enet_clk[0]), _REGISTER_CLOCK("imx2-wdt.0", NULL, dummy_clk), _REGISTER_CLOCK("sdhci-esdhc-imx.1", NULL, esdhc1_clk), @@ -1902,10 +1948,12 @@ static struct clk_lookup lookups[] = { _REGISTER_CLOCK(NULL, "mvf-usb.1", usb_phy1_clk), _REGISTER_CLOCK(NULL, "pwm", ftm_pwm_clk), _REGISTER_CLOCK("mvf-qspi.0", NULL, qspi0_clk), + _REGISTER_CLOCK(NULL, "asrc_clk", asrc_clk[0]), + _REGISTER_CLOCK(NULL, "asrc_serial_clk", asrc_clk[1]), + _REGISTER_CLOCK(NULL, "caam_clk", caam_clk), }; static void clk_tree_init(void) - { unsigned int reg = 0xffffffff; diff --git a/arch/arm/mach-mvf/cpu.c b/arch/arm/mach-mvf/cpu.c index 2254ac5974c5..4c6d018cd076 100644 --- a/arch/arm/mach-mvf/cpu.c +++ b/arch/arm/mach-mvf/cpu.c @@ -81,8 +81,7 @@ EXPORT_SYMBOL(mvf_revision); static int __init post_cpu_init(void) { - - /*iram_init(MVF_IRAM_BASE_ADDR, MVF_IRAM_SIZE);*/ + iram_init(MVF_IRAM_BASE_ADDR, MVF_IRAM_SIZE); /* Move wait routine into iRAM */ ccm_base = MVF_IO_ADDRESS(MVF_CCM_BASE_ADDR); diff --git a/arch/arm/mach-mvf/crm_regs.h b/arch/arm/mach-mvf/crm_regs.h index 78604eb96447..57fac5bbe973 100644 --- a/arch/arm/mach-mvf/crm_regs.h +++ b/arch/arm/mach-mvf/crm_regs.h @@ -50,6 +50,7 @@ #define PFD_480_BASE_ADDR (MXC_PLL_BASE + 0xF0) #define PFD_528_BASE_ADDR (MXC_PLL_BASE + 0x100) #define PFD_528SYS_BASE_ADDR (MXC_PLL_BASE + 0x2B0) +#define ANADIG_2P5_ADDR (MXC_PLL_BASE + 0x130) #define ANADIG_MISC0_REG (MXC_PLL_BASE + 0x150) #define ANADIG_MISC1_REG (MXC_PLL_BASE + 0x160) #define PLL_PFD_480_USB1 (MXC_PLL_BASE + 0xF0) diff --git a/arch/arm/mach-mvf/devices-mvf.h b/arch/arm/mach-mvf/devices-mvf.h index 61792fb34530..f4ec88b062ac 100644 --- a/arch/arm/mach-mvf/devices-mvf.h +++ b/arch/arm/mach-mvf/devices-mvf.h @@ -52,6 +52,15 @@ extern const struct imx_mxc_nand_data mvf_nand_data __initconst; #define mvf_add_nand(pdata) \ imx_add_mxc_nand(&mvf_nand_data, pdata) +#ifdef CONFIG_MACH_PCM052 +extern const struct imx_fsl_usb2_udc_data mvf_fsl_usb2_udc_data[] __initconst; +#define mvf_add_fsl_usb2_udc(id, pdata) \ + imx_add_fsl_usb2_udc(&mvf_fsl_usb2_udc_data[id], pdata) + +extern const struct imx_mxc_ehci_data mvf_mxc_ehci_otg_data[] __initconst; +#define mvf_add_fsl_ehci_otg(id, pdata) \ + imx_add_fsl_ehci(&mvf_mxc_ehci_otg_data[id], pdata) +#else extern const struct imx_fsl_usb2_udc_data mvf_fsl_usb2_udc_data __initconst; #define mvf_add_fsl_usb2_udc(pdata) \ imx_add_fsl_usb2_udc(&mvf_fsl_usb2_udc_data, pdata) @@ -59,24 +68,43 @@ extern const struct imx_fsl_usb2_udc_data mvf_fsl_usb2_udc_data __initconst; extern const struct imx_mxc_ehci_data mvf_mxc_ehci_otg_data __initconst; #define mvf_add_fsl_ehci_otg(pdata) \ imx_add_fsl_ehci(&mvf_mxc_ehci_otg_data, pdata) +#endif extern const struct imx_mxc_ehci_data mvf_mxc_ehci_hs_data[] __initconst; +#ifdef CONFIG_MACH_PCM052 +#define mvf_add_fsl_ehci_hs(id, pdata) \ + imx_add_fsl_ehci(&mvf_mxc_ehci_hs_data[id], pdata) +#else #define mvf_add_fsl_ehci_hs(id, pdata) \ imx_add_fsl_ehci(&mvf_mxc_ehci_hs_data[id - 1], pdata) +#endif extern const struct imx_fsl_usb2_otg_data mvf_fsl_usb2_otg_data __initconst; #define mvf_add_fsl_usb2_otg(pdata) \ imx_add_fsl_usb2_otg(&mvf_fsl_usb2_otg_data, pdata) extern -const struct imx_fsl_usb2_wakeup_data mvf_fsl_otg_wakeup_data __initconst; -#define mvf_add_fsl_usb2_otg_wakeup(pdata) \ - imx_add_fsl_usb2_wakeup(&mvf_fsl_otg_wakeup_data, pdata) +const struct imx_fsl_usb2_wakeup_data mvf_fsl_otg_wakeup_data[] __initconst; +#ifdef CONFIG_MACH_PCM052 +#define mvf_add_fsl_usb2_ehci_otg_wakeup(id, pdata) \ + imx_add_fsl_usb2_wakeup(&mvf_fsl_otg_wakeup_data[id], pdata) +#define mvf_add_fsl_usb2_udc_wakeup(id, pdata) \ + imx_add_fsl_usb2_wakeup(&mvf_fsl_otg_wakeup_data[id], pdata) +#else +#define mvf_add_fsl_usb2_ehci_otg_wakeup(pdata) \ + imx_add_fsl_usb2_wakeup(&mvf_fsl_otg_wakeup_data[1], pdata) +#define mvf_add_fsl_usb2_udc_wakeup(pdata) \ + imx_add_fsl_usb2_wakeup(&mvf_fsl_otg_wakeup_data[0], pdata) +#endif extern const struct imx_fsl_usb2_wakeup_data mvf_fsl_hs_wakeup_data[] __initconst; +#ifdef CONFIG_MACH_PCM052 +#define mvf_add_fsl_usb2_hs_wakeup(id, pdata) \ + imx_add_fsl_usb2_wakeup(&mvf_fsl_hs_wakeup_data[id], pdata) #define mvf_add_fsl_usb2_hs_wakeup(id, pdata) \ imx_add_fsl_usb2_wakeup(&mvf_fsl_hs_wakeup_data[id - 1], pdata) +#endif extern const struct imx_imx_esai_data mvf_imx_esai_data[] __initconst; #define mvf_add_imx_esai(id, pdata) \ @@ -142,6 +170,10 @@ extern const struct imx_imx2_wdt_data mvf_imx2_wdt_data[] __initconst; #define mvf_add_imx2_wdt(id, pdata) \ imx_add_imx2_wdt(&mvf_imx2_wdt_data[id]) +extern const struct mvf_adc_data mvfa5_adc_data[] __initconst; +#define mvf_add_adc(id) \ + mvf_add_adcdev(&mvfa5_adc_data[id]) + extern const struct imx_imx2_wdt_data fsl_imx2_wdt_data[] __initconst; #define mvf_add_wdt(id) \ imx_add_imx2_wdt(&fsl_imx2_wdt_data[id]) @@ -218,3 +250,7 @@ extern const struct imx_epdc_data mvf_epdc_data __initconst; #define mvf_add_pwm_leds(pdata) \ imx_add_platform_device("leds_pwm", 0, NULL, 0, pdata, sizeof(*pdata)) + +extern const struct mvf_caam_data mvf_caam_data __initconst; +#define mvf700_add_caam() \ + mvf_add_caam(&mvf_caam_data) diff --git a/arch/arm/mach-mvf/irq.c b/arch/arm/mach-mvf/irq.c index c3e7b9291faa..89c7d382173f 100644 --- a/arch/arm/mach-mvf/irq.c +++ b/arch/arm/mach-mvf/irq.c @@ -36,9 +36,9 @@ static int mvf_gic_irq_set_wake(struct irq_data *d, unsigned int enable) if (enable) { gpc_wake_irq[d->irq / 32 - 1] |= 1 << (d->irq % 32); - printk(KERN_INFO "add wake up source irq %d\n", d->irq); + printk(KERN_DEBUG "add wake up source irq %d\n", d->irq); } else { - printk(KERN_INFO "remove wake up source irq %d\n", d->irq); + printk(KERN_DEBUG "remove wake up source irq %d\n", d->irq); gpc_wake_irq[d->irq / 32 - 1] &= ~(1 << (d->irq % 32)); } return 0; @@ -48,6 +48,7 @@ void mvf_init_irq(void) unsigned int i; void __iomem *int_router_base = MVF_IO_ADDRESS(MVF_MSCM_INT_ROUTER_BASE); + struct irq_desc *desc; /* start offset if private timer irq id, which is 29. * ID table: @@ -60,6 +61,11 @@ void mvf_init_irq(void) gic_init(0, 27, MVF_IO_ADDRESS(MVF_INTD_BASE_ADDR), MVF_IO_ADDRESS(MVF_SCUGIC_BASE_ADDR + 0x100)); + for (i = MXC_INT_START; i <= MXC_INT_END; i++) { + desc = irq_to_desc(i); + desc->irq_data.chip->irq_set_wake = mvf_gic_irq_set_wake; + } + mvf_register_gpios(); for (i = 0; i < 112; i++) diff --git a/arch/arm/mach-mvf/mvf_suspend.S b/arch/arm/mach-mvf/mvf_suspend.S new file mode 100644 index 000000000000..ce9205162538 --- /dev/null +++ b/arch/arm/mach-mvf/mvf_suspend.S @@ -0,0 +1,484 @@ +/* + * Copyright 2012 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/linkage.h> +#include <mach/hardware.h> +#include <asm/memory.h> +#include <mach/mvf.h> +#include "regs-src.h" +#include "crm_regs.h" + +#define PERIPBASE_VIRT_OFFSET 0xb2000000 +#define TTRBIT_MASK 0xffffc000 +#define TABLE_INDEX_MASK 0xfff00000 +#define TABLE_ENTRY 0x00000c02 +#define CACHE_DISABLE_MASK 0xfffffffb +#define IRAM_SUSPEND_SIZE (1 << 15) + +/************************************************************* +mvf_suspend: + +Suspend the processor (eg, wait for interrupt). + +r1: iram_paddr +r2: suspend_iram_base +*************************************************************/ + + .macro mvf_stop_mode_enter + + ldr r3, =MVF_ANATOP_BASE_ADDR + add r3, r3, #PERIPBASE_VIRT_OFFSET + add r3, r3, #0x20000 + + /* pll7 disable */ + ldr r4, [r3, #0x20] + bic r4, r4, #0x2000 + str r4, [r3, #0x20] + + /* pll3 disable */ + ldr r4, [r3, #0x10] + bic r4, r4, #0x2000 + str r4, [r3, #0x10] + + /* pll4 disable */ + ldr r4, [r3, #0x70] + bic r4, r4, #0x2000 + str r4, [r3, #0x70] + + /* pll6 disable */ + ldr r4, [r3, #0xa0] + bic r4, r4, #0x2000 + str r4, [r3, #0xa0] + + /* pll5 disable */ + ldr r4, [r3, #0xe0] + bic r4, r4, #0x2000 + str r4, [r3, #0xe0] + + /* pll1 disable */ + ldr r4, [r3, #0x270] + bic r4, r4, #0x2000 + str r4, [r3, #0x270] + + /* stop mode is masked to Anatop */ + ldr r3, =MVF_CCM_BASE_ADDR + add r3, r3, #PERIPBASE_VIRT_OFFSET + add r3, r3, #0x20000 + + ldr r4, [r3, #0x2c] + bic r4, r4, #0x100 + str r4, [r3, #0x2c] + + ldr r3, =MVF_GPC_BASE_ADDR + add r3, r3, #PERIPBASE_VIRT_OFFSET + add r3, r3, #0x20000 + + /* ensure power domain 0 */ + ldr r4, [r3, #0x0] + bic r4, r4, #0x01 + str r4, [r3, #0x0] + + /* enable deep sleep for memories */ + ldr r4, [r3, #0x0] + orr r4, r4, #0x80 + str r4, [r3, #0x0] + + /* disable well bias */ + ldr r4, [r3, #0x0] + bic r4, r4, #0x10 + str r4, [r3, #0x0] + + /* turn off HPREG in stop mode */ + ldr r4, [r3, #0x0] + orr r4, r4, #0x08 + str r4, [r3, #0x0] + + /* gpc_lpmr set stop mode */ + ldr r4, =0x02 + str r4, [r3, #0x40] + + .endm + + .macro mvf_lpstop_mode_enter + + ldr r3, =MVF_ANATOP_BASE_ADDR + add r3, r3, #PERIPBASE_VIRT_OFFSET + add r3, r3, #0x20000 + + /* pll7 disable */ + ldr r4, [r3, #0x20] + bic r4, r4, #0x2000 + str r4, [r3, #0x20] + + /* pll3 disable */ + ldr r4, [r3, #0x10] + bic r4, r4, #0x2000 + str r4, [r3, #0x10] + + /* pll4 disable */ + ldr r4, [r3, #0x70] + bic r4, r4, #0x2000 + str r4, [r3, #0x70] + + /* pll6 disable */ + ldr r4, [r3, #0xa0] + bic r4, r4, #0x2000 + str r4, [r3, #0xa0] + + /* pll5 disable */ + ldr r4, [r3, #0xe0] + bic r4, r4, #0x2000 + str r4, [r3, #0xe0] + + /* pll1 disable */ + ldr r4, [r3, #0x270] + bic r4, r4, #0x2000 + str r4, [r3, #0x270] + + ldr r3, =MVF_CCM_BASE_ADDR + add r3, r3, #PERIPBASE_VIRT_OFFSET + add r3, r3, #0x20000 + + ldr r4, [r3, #0x2c] + bic r4, r4, #0x100 + str r4, [r3, #0x2c] + + ldr r3, =MVF_GPC_BASE_ADDR + add r3, r3, #PERIPBASE_VIRT_OFFSET + add r3, r3, #0x20000 + + /* enable deep sleep for memories */ + ldr r4, [r3, #0x0] + orr r4, r4, #0x40 + str r4, [r3, #0x0] + + /* enable LPSTOP3 */ + ldr r4, [r3, #0x0] + bic r4, r4, #0x04 + bic r4, r4, #0x02 + str r4, [r3, #0x0] + + /* ensure power domain 1 */ + ldr r4, [r3, #0x0] + orr r4, r4, #0x01 + str r4, [r3, #0x0] + + /* gpc_lpmr set low-power stop mode */ + ldr r4, =0x02 + str r4, [r3, #0x40] + + .endm + +/****************************************************************** +Invalidate l1 dcache, r0-r4, r6, r7 used +******************************************************************/ + .macro invalidate_l1_dcache + + mov r0, #0 + mcr p15, 2, r0, c0, c0, 0 + mrc p15, 1, r0, c0, c0, 0 + + ldr r1, =0x7fff + and r2, r1, r0, lsr #13 + + ldr r1, =0x3ff + + and r3, r1, r0, lsr #3 @ NumWays - 1 + add r2, r2, #1 @ NumSets + + and r0, r0, #0x7 + add r0, r0, #4 @ SetShift + + clz r1, r3 @ WayShift + add r4, r3, #1 @ NumWays +1: + sub r2, r2, #1 @ NumSets-- + mov r3, r4 @ Temp = NumWays +2: + subs r3, r3, #1 @ Temp-- + mov r7, r3, lsl r1 + mov r6, r2, lsl r0 + orr r7, r7, r6 + mcr p15, 0, r7, c7, c6, 2 + bgt 2b + cmp r2, #0 + bgt 1b + dsb + isb + + .endm + +/****************************************************************** +Flush and disable L1 dcache +******************************************************************/ + .macro flush_disable_l1_dcache + + /* + * Flush all data from the L1 data cache before disabling + * SCTLR.C bit. + */ + push {r0-r12, lr} + ldr r0, =v7_flush_dcache_all + mov lr, pc + mov pc, r0 + pop {r0-r12, lr} + + /* + * Clear the SCTLR.C bit to prevent further data cache + * allocation. Clearing SCTLR.C would make all the data accesses + * strongly ordered and would not hit the cache. + */ + mrc p15, 0, r0, c1, c0, 0 + bic r0, r0, #(1 << 2) @ Disable the C bit + mcr p15, 0, r0, c1, c0, 0 + isb + + /* + * Invalidate L1 data cache. Even though only invalidate is + * necessary exported flush API is used here. Doing clean + * on already clean cache would be almost NOP. + */ + push {r0-r12, lr} + ldr r0, =v7_flush_dcache_all + mov lr, pc + mov pc, r0 + pop {r0-r12, lr} + + /* + * Execute an ISB instruction to ensure that all of the + * CP15 register changes have been committed. + */ + isb + + /* + * Execute a barrier instruction to ensure that all cache, + * TLB and branch predictor maintenance operations issued + * by any CPU in the cluster have completed. + */ + dsb + dmb + + .endm + +ENTRY(mvf_suspend) + stmfd sp!, {r0-r12} @ Save registers +/************************************************************* +suspend mode entry +*************************************************************/ + mov r11, r0 + + cmp r0, #0x1 + bne dormant + + /* Need to flush and disable L1 dcache*/ + flush_disable_l1_dcache + + wfi + + nop + nop + nop + nop + + /* Invalidate L1 I-cache first */ + mov r1, #0x0 + mcr p15, 0, r1, c7, c5, 0 @ Invalidate I-Cache + + /* Need to invalidate L1 dcache, as the power is dropped */ + invalidate_l1_dcache + + /* Enable L1 dcache first */ + mrc p15, 0, r0, c1, c0, 0 + orr r0, r0, #(1 << 2) @ Disable the C bit + mcr p15, 0, r0, c1, c0, 0 + +/*********************************************************** +never run to here +************************************************************/ + b out /* exit standby */ + /* Place the literal pool here so that literals are + within 16KB range */ + .ltorg + +/************************************************************ +dormant entry, data save in stack +************************************************************/ +dormant: + +/************************************************************ +saved register and context as below: + sp + spsr + lr + CPACR + TTBR0 + TTBR1 + TTBCR + DACR + PRRR + NMRR + ACTLR + Context ID + User r/w thread ID + Secure or NS VBAR + CPSR + SCTLR +************************************************************/ + /* stack is from the tail of iram_suspend base */ + mov r0, r2 /* get suspend_iram_base */ + add r0, r0, #IRAM_SUSPEND_SIZE + + mov r4, r11 @ Store state entered + stmfd r0!, {r4} + + mov r4, sp @ Store sp + mrs r5, spsr @ Store spsr + mov r6, lr @ Store lr + stmfd r0!, {r4-r6} + + /* c1 and c2 registers */ + mrc p15, 0, r4, c1, c0, 2 @ CPACR + mrc p15, 0, r5, c2, c0, 0 @ TTBR0 + mrc p15, 0, r6, c2, c0, 1 @ TTBR1 + mrc p15, 0, r7, c2, c0, 2 @ TTBCR + stmfd r0!, {r4-r7} + + /* c3 and c10 registers */ + mrc p15, 0, r4, c3, c0, 0 @ DACR + mrc p15, 0, r5, c10, c2, 0 @ PRRR + mrc p15, 0, r6, c10, c2, 1 @ NMRR + mrc p15, 0, r7, c1, c0, 1 @ ACTLR + stmfd r0!,{r4-r7} + + /* c12, c13 and CPSR registers */ + mrc p15, 0, r4, c13, c0, 1 @ Context ID + mrc p15, 0, r5, c13, c0, 2 @ User r/w thread ID + mrc p15, 0, r6, c12, c0, 0 @ Secure or NS VBAR + mrs r7, cpsr @ Store CPSR + stmfd r0!, {r4-r7} + + /* c1 control register */ + mrc p15, 0, r4, c1, c0, 0 @ SCTLR + stmfd r0!, {r4} + + /* Need to flush and disable L1 dcache*/ + flush_disable_l1_dcache + + /* Make sure TLBs are primed */ + ldr r1, =MVF_IOMUXC_BASE_ADDR + add r1, r1, #PERIPBASE_VIRT_OFFSET + add r1, r1, #0x20000 + ldr r0, [r1] + ldr r1, =MVF_SRC_BASE_ADDR + add r1, r1, #PERIPBASE_VIRT_OFFSET + add r1, r1, #0x20000 + ldr r0, [r1] + ldr r1, =MVF_CCM_BASE_ADDR + add r1, r1, #PERIPBASE_VIRT_OFFSET + add r1, r1, #0x20000 + ldr r0, [r1] + ldr r1, =MVF_GPC_BASE_ADDR + add r1, r1, #PERIPBASE_VIRT_OFFSET + add r1, r1, #0x20000 + ldr r0, [r1] + ldr r1, =MVF_CCM_BASE_ADDR + add r1, r1, #PERIPBASE_VIRT_OFFSET + add r1, r1, #0x20000 + ldr r0, [r1] + + /* Do a DSB to drain the buffers */ + dsb + + mvf_stop_mode_enter + +/**************************************************************** +execute a wfi instruction to let SOC go into stop mode. +****************************************************************/ + wfi + + nop + nop + nop + nop + +/**************************************************************** +if go here, means there is a wakeup irq pending, we should resume +system immediately. +****************************************************************/ + mov r0, r2 /* get suspend_iram_base */ + add r0, r0, #IRAM_SUSPEND_SIZE + + ldmea r0!, {r11} @ standby or mem + + /* mask all the GPC interrupts */ + ldr r3, =MVF_GPC_BASE_ADDR + add r3, r3, #PERIPBASE_VIRT_OFFSET + add r3, r3, #0x20000 + ldr r4, =0xffffffff + str r4, [r3, #0x44] + str r4, [r3, #0x48] + str r4, [r3, #0x4c] + str r4, [r3, #0x50] + + /* pll2 enable */ + ldr r3, =MVF_ANATOP_BASE_ADDR + add r3, r3, #PERIPBASE_VIRT_OFFSET + add r3, r3, #0x20000 + + ldr r4, [r3, #0x30] + orr r4, r4, #0x2000 + str r4, [r3, #0x30] + + nop + nop + nop + nop + nop + + nop + nop + nop + nop + nop + + nop + nop + nop + nop + nop + + nop + nop + nop + nop + nop + + nop + nop + nop + nop + nop + + mrc p15, 0, r1, c1, c0, 0 + orr r1, r1, #(1 << 2) @ Enable the C bit + mcr p15, 0, r1, c1, c0, 0 + + b out + +/************************************************ +return back to mvf_suspend_enter for suspend +*************************************************/ +out: + ldmfd sp!, {r0-r12} + mov pc, lr + + .type mvf_do_suspend, #object +ENTRY(mvf_do_suspend) + .word mvf_suspend + .size mvf_suspend, . - mvf_suspend diff --git a/arch/arm/mach-mvf/pm.c b/arch/arm/mach-mvf/pm.c new file mode 100644 index 000000000000..0e62caf372da --- /dev/null +++ b/arch/arm/mach-mvf/pm.c @@ -0,0 +1,312 @@ +/* + * Copyright 2012 Freescale Semiconductor, Inc. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/clk.h> +#include <linux/platform_device.h> +#include <linux/suspend.h> +#include <linux/iram_alloc.h> +#include <linux/interrupt.h> +#include <asm/tlb.h> +#include <asm/mach/map.h> +#include <mach/hardware.h> +#include <asm/hardware/gic.h> +#include <mach/mxc_uart.h> +#include "crm_regs.h" +#include "regs-anadig.h" +#include "regs-pm.h" +#include "regs-src.h" + +static struct clk *cpu_clk; +static struct clk *ipg_clk; +static struct clk *periph_clk; + +extern void mvf_suspend(suspend_state_t state); + +static struct device *pm_dev; +static void __iomem *scu_base; +static void __iomem *gpc_base; +static void __iomem *src_base; +static void __iomem *anadig_base; + +static void *suspend_iram_base; +static void (*suspend_in_iram)(suspend_state_t state, + unsigned long iram_paddr, unsigned long suspend_iram_base) = NULL; +static unsigned long iram_paddr, cpaddr; + +static u32 ccm_ccr, ccm_clpcr, ccm_ccsr, scu_ctrl; +static u32 gpc_imr[4], gpc_pgcr, gpc_lpmr; +static u32 ccm_anadig_pfd528, ccm_anadig_pfd480_usb0, ccm_anadig_2p5; +static int g_state; + +static void mvf_suspend_store(void) +{ + /* save some settings before suspend */ + ccm_ccr = __raw_readl(MXC_CCM_CCR); + ccm_clpcr = __raw_readl(MXC_CCM_CLPCR); + ccm_ccsr = __raw_readl(MXC_CCM_CCSR); + ccm_anadig_pfd528 = __raw_readl(PFD_528_BASE_ADDR); + ccm_anadig_pfd480_usb0 = __raw_readl(PFD_480_BASE_ADDR); + ccm_anadig_2p5 = __raw_readl(ANADIG_2P5_ADDR); + + scu_ctrl = __raw_readl(scu_base + SCU_CTRL_OFFSET); + gpc_imr[0] = __raw_readl(gpc_base + GPC_IMR1_OFFSET); + gpc_imr[1] = __raw_readl(gpc_base + GPC_IMR2_OFFSET); + gpc_imr[2] = __raw_readl(gpc_base + GPC_IMR3_OFFSET); + gpc_imr[3] = __raw_readl(gpc_base + GPC_IMR4_OFFSET); + gpc_pgcr = __raw_readl(gpc_base + GPC_PGCR_OFFSET); + gpc_lpmr = __raw_readl(gpc_base + GPC_LPMR_OFFSET); +} + +static void mvf_suspend_restore(void) +{ + /* restore settings after suspend */ + __raw_writel(ccm_anadig_2p5, ANADIG_2P5_ADDR); + + udelay(50); + + __raw_writel(ccm_ccr, MXC_CCM_CCR); + __raw_writel(ccm_clpcr, MXC_CCM_CLPCR); + __raw_writel(ccm_anadig_pfd528, PFD_528_BASE_ADDR); + __raw_writel(ccm_anadig_pfd480_usb0, PFD_480_BASE_ADDR); + + __raw_writel(scu_ctrl, scu_base + SCU_CTRL_OFFSET); + __raw_writel(gpc_imr[0], gpc_base + GPC_IMR1_OFFSET); + __raw_writel(gpc_imr[1], gpc_base + GPC_IMR2_OFFSET); + __raw_writel(gpc_imr[2], gpc_base + GPC_IMR3_OFFSET); + __raw_writel(gpc_imr[3], gpc_base + GPC_IMR4_OFFSET); + __raw_writel(gpc_pgcr, gpc_base + GPC_PGCR_OFFSET); + __raw_writel(gpc_lpmr, gpc_base + GPC_LPMR_OFFSET); + + /* enable PLLs */ + __raw_writel(__raw_readl(PLL3_480_USB1_BASE_ADDR) | ANADIG_PLL_ENABLE, + PLL3_480_USB1_BASE_ADDR); + __raw_writel(__raw_readl(PLL3_480_USB2_BASE_ADDR) | ANADIG_PLL_ENABLE, + PLL3_480_USB2_BASE_ADDR); + __raw_writel(__raw_readl(PLL4_AUDIO_BASE_ADDR) | ANADIG_PLL_ENABLE, + PLL4_AUDIO_BASE_ADDR); + __raw_writel(__raw_readl(PLL6_VIDEO_BASE_ADDR) | ANADIG_PLL_ENABLE, + PLL6_VIDEO_BASE_ADDR); + __raw_writel(__raw_readl(PLL5_ENET_BASE_ADDR) | ANADIG_PLL_ENABLE, + PLL5_ENET_BASE_ADDR); + __raw_writel(__raw_readl(PLL1_SYS_BASE_ADDR) | ANADIG_PLL_ENABLE, + PLL1_SYS_BASE_ADDR); + + /* restore system clock */ + __raw_writel(ccm_ccsr, MXC_CCM_CCSR); +} + +static void uart_reinit(unsigned long int clkspeed, unsigned long int baud) +{ + void __iomem *membase; + u8 tmp; + u16 sbr, brfa; + + membase = MVF_IO_ADDRESS(MVF_UART1_BASE_ADDR); + + __raw_writeb(0, membase + MXC_UARTMODEM); + + /* make sure the transmitter and receiver are + * disabled while changing settings */ + tmp = __raw_readb(membase + MXC_UARTCR2); + tmp &= ~MXC_UARTCR2_RE; + tmp &= ~MXC_UARTCR2_TE; + __raw_writeb(tmp, membase + MXC_UARTCR2); + + __raw_writeb(0, membase + MXC_UARTCR1); + + sbr = (u16) ((clkspeed * 1000) / (baud * 16)); + brfa = ((clkspeed * 1000) / baud) - (sbr * 16); + + tmp = __raw_readb(membase + MXC_UARTBDH); + tmp &= ~MXC_UARTBDH_SBR_MASK; + tmp |= ((sbr & 0x1f00) >> 8); + __raw_writeb(tmp, membase + MXC_UARTBDH); + tmp = sbr & 0x00ff; + __raw_writeb(tmp, membase + MXC_UARTBDL); + + tmp = __raw_readb(membase + MXC_UARTCR4); + tmp &= ~MXC_UARTCR4_BRFA_MASK; + tmp |= (brfa & MXC_UARTCR4_BRFA_MASK); + __raw_writeb(tmp , membase + MXC_UARTCR4); + + tmp = __raw_readb(membase + MXC_UARTCR2); + tmp |= MXC_UARTCR2_RE; + tmp |= MXC_UARTCR2_TE; + __raw_writeb(tmp, membase + MXC_UARTCR2); +} + +static int mvf_suspend_enter(suspend_state_t state) +{ + struct gic_dist_state gds; + struct gic_cpu_state gcs; + bool arm_pg = false; + + mvf_suspend_store(); + + switch (state) { + case PM_SUSPEND_MEM: + mvf_cpu_lp_set(LOW_POWER_STOP); + g_state = PM_SUSPEND_MEM; + arm_pg = true; + break; + case PM_SUSPEND_STANDBY: + mvf_cpu_lp_set(LOW_POWER_RUN); + g_state = PM_SUSPEND_STANDBY; + arm_pg = true; + break; + default: + return -EINVAL; + } + + if (state == PM_SUSPEND_MEM || state == PM_SUSPEND_STANDBY) { + local_flush_tlb_all(); + flush_cache_all(); + + if (arm_pg) { + /* preserve GIC state */ + save_gic_dist_state(0, &gds); + save_gic_cpu_state(0, &gcs); + } + + suspend_in_iram(state, (unsigned long)iram_paddr, + (unsigned long)suspend_iram_base); + + /* reconfigure UART1 when using 24MHz system clock */ + uart_reinit(4000, 115200); + + printk(KERN_DEBUG "Read GPC_PGSR register: %x\n", + __raw_readl(gpc_base + GPC_PGSR_OFFSET)); + + /* mask all interrupts */ + __raw_writel(0xffffffff, gpc_base + GPC_IMR1_OFFSET); + __raw_writel(0xffffffff, gpc_base + GPC_IMR2_OFFSET); + __raw_writel(0xffffffff, gpc_base + GPC_IMR3_OFFSET); + __raw_writel(0xffffffff, gpc_base + GPC_IMR4_OFFSET); + + udelay(80); + + if (arm_pg) { + /* restore GIC registers */ + restore_gic_dist_state(0, &gds); + restore_gic_cpu_state(0, &gcs); + } + + mvf_suspend_restore(); + + /* reconfigure UART1 when using 396MHz system clock */ + uart_reinit(66000, 115200); + + __raw_writel(BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG, + ANADIG_MISC0_REG); + } else + cpu_do_idle(); + + return 0; +} + +static void mvf_suspend_finish(void) +{ + if (g_state == PM_SUSPEND_MEM) + free_irq(MVF_INT_WKPU0, NULL); +} + +static int mvf_pm_valid(suspend_state_t state) +{ + return (state > PM_SUSPEND_ON && state <= PM_SUSPEND_MAX); +} + +const struct platform_suspend_ops mvf_suspend_ops = { + .valid = mvf_pm_valid, + .enter = mvf_suspend_enter, + .finish = mvf_suspend_finish, +}; + +static int __devinit mvf_pm_probe(struct platform_device *pdev) +{ + pm_dev = &pdev->dev; + + return 0; +} + +static struct platform_driver mvf_pm_driver = { + .driver = { + .name = "imx_pm", + }, + .probe = mvf_pm_probe, +}; + +static int __init pm_init(void) +{ + scu_base = MVF_IO_ADDRESS(MVF_SCUGIC_BASE_ADDR); + gpc_base = MVF_GPC_BASE; + anadig_base = MXC_PLL_BASE; + src_base = MVF_IO_ADDRESS(MVF_SRC_BASE_ADDR); + + pr_info("Static Power Management for Freescale Vybrid\n"); + + if (platform_driver_register(&mvf_pm_driver) != 0) { + printk(KERN_ERR "mvf_pm_driver register failed\n"); + return -ENODEV; + } + + suspend_set_ops(&mvf_suspend_ops); + + /* move suspend routine into sram */ + cpaddr = (unsigned long)iram_alloc(SZ_32K, &iram_paddr); + + /* need to remap the area here since we want the memory region + * to be executable */ + suspend_iram_base = __arm_ioremap(iram_paddr, SZ_32K, + MT_MEMORY_NONCACHED); + printk(KERN_DEBUG "cpaddr = %x suspend_iram_base=%x iram_paddr %x\n", + (unsigned int)cpaddr, (unsigned int)suspend_iram_base, + (unsigned int)iram_paddr); + + /* need to run the suspend code from sram */ + memcpy((void *)cpaddr, mvf_suspend, SZ_32K); + + suspend_in_iram = (void *)suspend_iram_base; + + cpu_clk = clk_get(NULL, "cpu_clk"); + if (IS_ERR(cpu_clk)) { + printk(KERN_DEBUG "%s: failed to get cpu_clk\n", __func__); + return PTR_ERR(cpu_clk); + } + ipg_clk = clk_get(NULL, "ipg_clk"); + if (IS_ERR(ipg_clk)) { + printk(KERN_DEBUG "%s: failed to get ipg_clk\n", __func__); + return PTR_ERR(ipg_clk); + } + periph_clk = clk_get(NULL, "periph_clk"); + if (IS_ERR(periph_clk)) { + printk(KERN_DEBUG "%s: failed to get periph_clk\n", __func__); + return PTR_ERR(periph_clk); + } + + printk(KERN_INFO "PM driver module loaded\n"); + return 0; +} + +static void __exit pm_cleanup(void) +{ + platform_driver_unregister(&mvf_pm_driver); +} + +module_init(pm_init); +module_exit(pm_cleanup); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("PM driver"); +MODULE_LICENSE("GPL"); diff --git a/arch/arm/mach-mvf/regs-pm.h b/arch/arm/mach-mvf/regs-pm.h new file mode 100644 index 000000000000..c54bdd346709 --- /dev/null +++ b/arch/arm/mach-mvf/regs-pm.h @@ -0,0 +1,77 @@ +/* + * Copyright 2012 Freescale Semiconductor, Inc. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#ifndef __ARCH_ARM_MACH_MVF_REGS_PM_H__ +#define __ARCH_ARM_MACH_MVF_REGS_PM_H__ + +/* GPC registers */ +#define GPC_PGCR_OFFSET (0x00) +#define GPC_PGCR_DS_STOP (0x000000080) +#define GPC_PGCR_DS_LPSTOP (0x000000040) +#define GPC_PGCR_WB_STOP (0x000000010) +#define GPC_PGCR_HP_OFF (0x000000008) +#define GPC_PGCR_PG_48K (0x000000004) +#define GPC_PGCR_PG_16K (0x000000002) +#define GPC_PGCR_PG_PD1 (0x000000001) + +#define GPC_PGSR_OFFSET (0x0c) + +#define GPC_LPMR_OFFSET (0x40) +#define GPC_LPMR_CLPCR_RUN (0x00000000) +#define GPC_LPMR_CLPCR_WAIT (0x00000001) +#define GPC_LPMR_CLPCR_STOP (0x00000002) + +#define GPC_IMR1_OFFSET (0x44) +#define GPC_IMR2_OFFSET (0x48) +#define GPC_IMR3_OFFSET (0x4c) +#define GPC_IMR4_OFFSET (0x50) +#define GPC_ISR1_OFFSET (0x54) +#define GPC_ISR2_OFFSET (0x58) +#define GPC_ISR3_OFFSET (0x5c) +#define GPC_ISR4_OFFSET (0x60) + +/* VREG registers */ +#define VREG_CTRL_OFFSET (0x00) +#define VREG_CTRL_PORPU (0x00010000) +#define VREG_CTRL_HVDMASK (0x00000001) +#define VREG_STAT_OFFSET (0x04) + +/* WKPU registers */ +#define WKPU_NSR_OFFSET (0x00) + +#define WKPU_NCR_OFFSET (0x08) +#define WKPU_NCR_NLOCK0 (0x80000000) +#define WKPU_NCR_NWRE0 (0x10000000) +#define WKPU_NCR_NREE0 (0x04000000) +#define WKPU_NCR_NFEE0 (0x02000000) +#define WKPU_NCR_NFE0 (0x01000000) + +#define WKPU_WISR_OFFSET (0x14) + +#define WKPU_IRER_OFFSET (0x18) + +#define WKPU_WRER_OFFSET (0x1c) + +#define WKPU_WIREER_OFFSET (0x28) + +#define WKPU_WIFEER_OFFSET (0x2c) + +#define WKPU_WIFER_OFFSET (0x30) + +#define WKPU_WIPUER_OFFSET (0x34) + +#define RISING_EDGE_ENABLED (0x9e) +#define FALLING_EDGE_ENABLED (0xfe) + +/* SCU registers */ +#define SCU_CTRL_OFFSET (0x00) + + +#endif /* __ARCH_ARM_MACH_MVF_REGS_PM_H__ */ diff --git a/arch/arm/mach-mvf/regs-src.h b/arch/arm/mach-mvf/regs-src.h new file mode 100644 index 000000000000..b93e99d92da1 --- /dev/null +++ b/arch/arm/mach-mvf/regs-src.h @@ -0,0 +1,45 @@ +/* + * Copyright 2012 Freescale Semiconductor, Inc, + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef _SRC_REGISTER_HEADER_ +#define _SRC_REGISTER_HEADER_ + +#define SRC_SCR_OFFSET 0x000 +#define SRC_SBMR_OFFSET 0x004 +#define SRC_SRSR_OFFSET 0x008 +#define SRC_SECR_OFFSET 0x00c +#define SRC_SISR_OFFSET 0x014 +#define SRC_SIMR_OFFSET 0x018 +#define SRC_SBMR2_OFFSET 0x01c +#define SRC_GPR0_OFFSET 0x020 +#define SRC_GPR1_OFFSET 0x024 +#define SRC_GPR2_OFFSET 0x028 +#define SRC_GPR3_OFFSET 0x02c +#define SRC_GPR4_OFFSET 0x030 +#define SRC_HAB0_OFFSET 0x034 +#define SRC_HAB1_OFFSET 0x038 +#define SRC_HAB2_OFFSET 0x03c +#define SRC_HAB3_OFFSET 0x040 +#define SRC_HAB4_OFFSET 0x044 +#define SRC_HAB5_OFFSET 0x048 +#define SRC_MISC0_OFFSET 0x04c +#define SRC_MISC1_OFFSET 0x050 +#define SRC_MISC2_OFFSET 0x054 +#define SRC_MISC3_OFFSET 0x058 + +#endif diff --git a/arch/arm/mach-mvf/system.c b/arch/arm/mach-mvf/system.c index 764c608cbf42..989c75a02ed1 100644 --- a/arch/arm/mach-mvf/system.c +++ b/arch/arm/mach-mvf/system.c @@ -21,7 +21,8 @@ #include <linux/delay.h> #include <linux/platform_device.h> #include <linux/regulator/consumer.h> -#include <linux/pmic_external.h> +#include <linux/interrupt.h> +#include <linux/gpio.h> #include <asm/io.h> #include <mach/hardware.h> #include <mach/clock.h> @@ -29,6 +30,142 @@ #include <asm/system.h> #include "crm_regs.h" #include "regs-anadig.h" +#include "regs-pm.h" + +#define SW1_WAKEUP_PIN 38 +#define SW1_PORT1_PCR6_ADDR 0x4004a018 + +static void __iomem *gpc_base = MVF_GPC_BASE; + +void gpc_set_wakeup(void) +{ + __raw_writel(0xffffffff, gpc_base + GPC_IMR1_OFFSET); + __raw_writel(0xffffffff, gpc_base + GPC_IMR2_OFFSET); + /* unmask WKPU0 interrupt */ + __raw_writel(0xefffffff, gpc_base + GPC_IMR3_OFFSET); + /* unmask GPIO4 interrupt */ + __raw_writel(0xffff7fff, gpc_base + GPC_IMR4_OFFSET); + + return; +} + +void enable_wkpu(u32 source, u32 rise_fall) +{ + __raw_writel(1 << source, MVF_WKPU_BASE + WKPU_IRER_OFFSET); + __raw_writel(1 << source, MVF_WKPU_BASE + WKPU_WRER_OFFSET); + + if (rise_fall == RISING_EDGE_ENABLED) + __raw_writel(1 << source, MVF_WKPU_BASE + WKPU_WIREER_OFFSET); + else + __raw_writel(1 << source, MVF_WKPU_BASE + WKPU_WIFEER_OFFSET); +} + +static irqreturn_t wkpu_irq(int irq, void *dev_id) +{ + u32 wisr; + + wisr = __raw_readl(MVF_WKPU_BASE + WKPU_WISR_OFFSET); + if (wisr) + __raw_writel(wisr, MVF_WKPU_BASE + WKPU_WISR_OFFSET); + + return IRQ_NONE; +} + +/* set cpu multiple modes before WFI instruction */ +void mvf_cpu_lp_set(enum mvf_cpu_pwr_mode mode) +{ + u32 ccm_ccsr, ccm_clpcr, ccm_ccr; + u32 tmp; + int ret; + + if ((mode == LOW_POWER_STOP) || (mode == STOP_MODE)) { + /* config SW1 for waking up system */ + gpio_request_one(SW1_WAKEUP_PIN, GPIOF_IN, "SW1 wakeup"); + gpio_set_value(SW1_WAKEUP_PIN, 0); + /* PORT1_PCR6 IRQC interrupt/dma request disabled */ + tmp = __raw_readl(MVF_IO_ADDRESS(SW1_PORT1_PCR6_ADDR)); + tmp &= ~0x000f0000; + __raw_writel(tmp, MVF_IO_ADDRESS(SW1_PORT1_PCR6_ADDR)); + + ret = request_irq(MVF_INT_WKPU0, wkpu_irq, + IRQF_DISABLED, "wkpu irq", NULL); + if (ret) + printk(KERN_ERR "Request wkpu IRQ failed\n"); + + /* enable WKPU interrupt */ + enable_wkpu(11, FALLING_EDGE_ENABLED); + } + + ccm_ccr = __raw_readl(MXC_CCM_CCR); + ccm_ccsr = __raw_readl(MXC_CCM_CCSR); + + switch (mode) { + case WAIT_MODE: + break; + case LOW_POWER_RUN: + ccm_ccr |= MXC_CCM_CCR_FIRC_EN; + __raw_writel(ccm_ccr, MXC_CCM_CCR); + ccm_ccsr &= ~MXC_CCM_CCSR_FAST_CLK_SEL_MASK; + ccm_ccsr &= ~MXC_CCM_CCSR_SLOW_CLK_SEL_MASK; + __raw_writel(ccm_ccsr, MXC_CCM_CCSR); + + /* switch system clock to FIRC 24MHz */ + ccm_ccsr &= ~MXC_CCM_CCSR_SYS_CLK_SEL_MASK; + __raw_writel(ccm_ccsr, MXC_CCM_CCSR); + + __raw_writel(__raw_readl(PLL3_480_USB1_BASE_ADDR) & + ~ANADIG_PLL_ENABLE, PLL3_480_USB1_BASE_ADDR); + __raw_writel(__raw_readl(PLL3_480_USB2_BASE_ADDR) & + ~ANADIG_PLL_ENABLE, PLL3_480_USB2_BASE_ADDR); + __raw_writel(__raw_readl(PLL4_AUDIO_BASE_ADDR) & + ~ANADIG_PLL_ENABLE, PLL4_AUDIO_BASE_ADDR); + __raw_writel(__raw_readl(PLL6_VIDEO_BASE_ADDR) & + ~ANADIG_PLL_ENABLE, PLL6_VIDEO_BASE_ADDR); + __raw_writel(__raw_readl(PLL5_ENET_BASE_ADDR) & + ~ANADIG_PLL_ENABLE, PLL5_ENET_BASE_ADDR); + __raw_writel(__raw_readl(PLL1_SYS_BASE_ADDR) & + ~ANADIG_PLL_ENABLE, PLL1_SYS_BASE_ADDR); + break; + case STOP_MODE: + case LOW_POWER_STOP: + gpc_set_wakeup(); + + /* unmask UART1 in low-power mode */ + __raw_writel(0xfffeffff, MXC_CCM_CCGR0); + /* unmask WKUP and GPC in low-power mode */ + __raw_writel(0xfeefffff, MXC_CCM_CCGR4); + /* unmask DDRC in low-power mode */ + __raw_writel(0xefffffff, MXC_CCM_CCGR6); + + ccm_ccr |= MXC_CCM_CCR_FIRC_EN; + __raw_writel(ccm_ccr, MXC_CCM_CCR); + ccm_ccsr &= ~MXC_CCM_CCSR_FAST_CLK_SEL_MASK; + ccm_ccsr &= ~MXC_CCM_CCSR_SLOW_CLK_SEL_MASK; + __raw_writel(ccm_ccsr, MXC_CCM_CCSR); + + /* switch system clock to FIRC 24MHz */ + ccm_ccsr &= ~MXC_CCM_CCSR_SYS_CLK_SEL_MASK; + __raw_writel(ccm_ccsr, MXC_CCM_CCSR); + + /* on-chip oscillator will not be + * powered down in stop mode */ + ccm_clpcr = __raw_readl(MXC_CCM_CLPCR); + ccm_clpcr &= ~MXC_CCM_CLPCR_SBYOS; + ccm_clpcr &= ~MXC_CCM_CLPCR_M_CORE1_WFI; + ccm_clpcr &= ~MXC_CCM_CLPCR_M_CORE0_WFI; + ccm_clpcr &= ~MXC_CCM_CLPCR_ARM_CLK_LPM; + __raw_writel(ccm_clpcr, MXC_CCM_CLPCR); + + /* mask stop mode to Anatop */ + __raw_writel(ccm_clpcr & ~MXC_CCM_CLPCR_ANATOP_STOP_MODE, + MXC_CCM_CLPCR); + + break; + default: + printk(KERN_WARNING "UNKNOW cpu power mode: %d\n", mode); + return; + } +} void arch_idle(void) { diff --git a/arch/arm/mach-mvf/usb_dr.c b/arch/arm/mach-mvf/usb_dr.c index 2467acc1bf28..34377c4c19b3 100644 --- a/arch/arm/mach-mvf/usb_dr.c +++ b/arch/arm/mach-mvf/usb_dr.c @@ -20,6 +20,7 @@ #include <linux/types.h> #include <linux/clk.h> #include <linux/platform_device.h> +#include <linux/pm_wakeup.h> #include <linux/fsl_devices.h> #include <linux/delay.h> #include <linux/io.h> @@ -35,11 +36,8 @@ 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 void _dr_discharge_line(bool enable); +static void usbotg_wakeup_event_clear(void); -/* The usb_phy0_clk do not have enable/disable function at clock.c - * and PLL output for usb0's phy should be always enabled. - * usb_phy0_clk only stands for usb uses pll3 as its parent. - */ static struct clk *usb_phy0_clk; static u8 otg_used; @@ -62,19 +60,24 @@ static struct fsl_usb2_platform_data dr_utmi_config = { .dr_discharge_line = _dr_discharge_line, }; +/* Platform data for wakeup operation */ +static struct fsl_usb2_wakeup_platform_data dr_wakeup_config = { + .name = "Host wakeup", + .usb_clock_for_pm = usbotg_clock_gate, + .usb_wakeup_exhandle = usbotg_wakeup_event_clear, +}; + static void usbotg_internal_phy_clock_gate(bool on) { - u32 reg; - void __iomem *phy_reg = MVF_IO_ADDRESS(MVF_USBPHY0_BASE_ADDR); - reg = __raw_readl(phy_reg + HW_USBPHY_CTRL); if (on) - reg &= ~BM_USBPHY_CTRL_CLKGATE; + __raw_writel(BM_USBPHY_CTRL_CLKGATE, + phy_reg + HW_USBPHY_CTRL_CLR); else - reg |= BM_USBPHY_CTRL_CLKGATE; + __raw_writel(BM_USBPHY_CTRL_CLKGATE, + phy_reg + HW_USBPHY_CTRL_SET); - __raw_writel(reg, phy_reg + HW_USBPHY_CTRL); } static int usb_phy_enable(struct fsl_usb2_platform_data *pdata) @@ -106,6 +109,13 @@ static int usb_phy_enable(struct fsl_usb2_platform_data *pdata) /* Power up the PHY */ __raw_writel(0, phy_reg + HW_USBPHY_PWD); + if ((pdata->operating_mode == FSL_USB2_DR_HOST) || + (pdata->operating_mode == FSL_USB2_DR_OTG)) { + /* enable FS/LS device */ + __raw_writel( + BM_USBPHY_CTRL_ENUTMILEVEL2 | BM_USBPHY_CTRL_ENUTMILEVEL3, + phy_reg + HW_USBPHY_CTRL_SET); + } return 0; } @@ -128,8 +138,8 @@ static int usbotg_init_ext(struct platform_device *pdev) usbotg_internal_phy_clock_gate(true); usb_phy_enable(pdev->dev.platform_data); /* - * after the phy reset,can not read the value for id/vbus at - * the register of otgsc ,cannot read at once ,need delay 3 ms + * after the phy reset,can't read the value for id/vbus at + * the register of otgsc at once ,need delay 3 ms */ mdelay(3); } @@ -151,21 +161,18 @@ static void usbotg_uninit_ext(struct platform_device *pdev) static void usbotg_clock_gate(bool on) { - pr_debug("%s: on is %d\n", __func__, on); + if (on) clk_enable(usb_phy0_clk); else clk_disable(usb_phy0_clk); + } -/* -void mvf_set_otghost_vbus_func(driver_vbus_func driver_vbus) -{ - dr_utmi_config.platform_driver_vbus = driver_vbus; -} -*/ + static void _dr_discharge_line(bool enable) { void __iomem *phy_reg = MVF_IO_ADDRESS(MVF_USBPHY0_BASE_ADDR); + if (enable) { __raw_writel(BF_USBPHY_DEBUG_ENHSTPULLDOWN(0x3), phy_reg + HW_USBPHY_DEBUG_SET); @@ -183,48 +190,275 @@ static void _dr_discharge_line(bool enable) /* 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 void enter_phy_lowpower_suspend(struct fsl_usb2_platform_data *pdata, + bool enable) +{ + void __iomem *phy_reg = MVF_IO_ADDRESS(MVF_USBPHY0_BASE_ADDR); + u32 tmp; + pr_debug("DR: %s begins, enable is %d\n", __func__, enable); + + if (enable) { + UOG_PORTSC1 |= PORTSC_PHCD; + tmp = (BM_USBPHY_PWD_TXPWDFS + | BM_USBPHY_PWD_TXPWDIBIAS + | BM_USBPHY_PWD_TXPWDV2I + | BM_USBPHY_PWD_RXPWDENV + | BM_USBPHY_PWD_RXPWD1PT1 + | BM_USBPHY_PWD_RXPWDDIFF + | BM_USBPHY_PWD_RXPWDRX); + __raw_writel(tmp, phy_reg + HW_USBPHY_PWD_SET); + usbotg_internal_phy_clock_gate(false); + + } else { + if (UOG_PORTSC1 & PORTSC_PHCD) { + UOG_PORTSC1 &= ~PORTSC_PHCD; + mdelay(1); + } + usbotg_internal_phy_clock_gate(true); + tmp = (BM_USBPHY_PWD_TXPWDFS + | BM_USBPHY_PWD_TXPWDIBIAS + | BM_USBPHY_PWD_TXPWDV2I + | BM_USBPHY_PWD_RXPWDENV + | BM_USBPHY_PWD_RXPWD1PT1 + | BM_USBPHY_PWD_RXPWDDIFF + | BM_USBPHY_PWD_RXPWDRX); + __raw_writel(tmp, phy_reg + HW_USBPHY_PWD_CLR); + + } + pr_debug("DR: %s ends, enable is %d\n", __func__, enable); +} + +static void __phy_lowpower_suspend(struct fsl_usb2_platform_data *pdata, + bool enable, int source) +{ + if (enable) + enter_phy_lowpower_suspend(pdata, enable); + else { + pr_debug("phy lowpower disable\n"); + enter_phy_lowpower_suspend(pdata, enable); + } +} + +static void otg_wake_up_enable(struct fsl_usb2_platform_data *pdata, + bool enable) +{ + void __iomem *phy_reg = MVF_IO_ADDRESS(MVF_USBPHY0_BASE_ADDR); + + pr_debug("%s, enable is %d\n", __func__, enable); + if (enable) { + __raw_writel(BM_USBPHY_CTRL_ENIDCHG_WKUP + | BM_USBPHY_CTRL_ENVBUSCHG_WKUP + | BM_USBPHY_CTRL_ENDPDMCHG_WKUP + | BM_USBPHY_CTRL_ENAUTOSET_USBCLKS + | BM_USBPHY_CTRL_ENAUTOCLR_PHY_PWD + | BM_USBPHY_CTRL_ENAUTOCLR_CLKGATE + | BM_USBPHY_CTRL_ENAUTOCLR_USBCLKGATE + | BM_USBPHY_CTRL_ENAUTO_PWRON_PLL, + phy_reg + HW_USBPHY_CTRL_SET); + USBC0_CTRL |= UCTRL_OWIE; + } else { + USBC0_CTRL &= ~UCTRL_OWIE; + /* 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 __wakeup_irq_enable(struct fsl_usb2_platform_data *pdata, + bool on, int source) +{ + pr_debug("USB Host %s,on=%d\n", __func__, on); + mdelay(3); + if (on) { + otg_wake_up_enable(pdata, on); + } else { + otg_wake_up_enable(pdata, on); + /* The interrupt must be disabled for at least 3 clock + * cycles of the standby clock(32k Hz) , that is 0.094 ms*/ + udelay(100); + } +} + +/* 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 = USBC0_CTRL & UCTRL_OWIR; + + if (wakeup_req != 0) { + pr_debug("Unknown wakeup.(OTGSC 0x%x)\n", UOG_OTGSC); + /* Disable OWIE to clear OWIR, wait 3 clock + * cycles of standly clock(32KHz) + */ + USBC0_CTRL &= ~UCTRL_OWIE; + udelay(100); + USBC0_CTRL |= UCTRL_OWIE; + } +} + +/* End of Common operation for DR port */ #ifdef CONFIG_USB_EHCI_ARC_OTG -#endif /* CONFIG_USB_EHCI_ARC_OTG */ +/* Beginning of host related operation for DR port */ +static void _host_platform_suspend(struct fsl_usb2_platform_data *pdata) +{ + void __iomem *phy_reg = MVF_IO_ADDRESS(MVF_USBPHY0_BASE_ADDR); + u32 tmp; + tmp = (BM_USBPHY_PWD_TXPWDFS + | BM_USBPHY_PWD_TXPWDIBIAS + | BM_USBPHY_PWD_TXPWDV2I + | BM_USBPHY_PWD_RXPWDENV + | BM_USBPHY_PWD_RXPWD1PT1 + | BM_USBPHY_PWD_RXPWDDIFF + | BM_USBPHY_PWD_RXPWDRX); + __raw_writel(tmp, phy_reg + HW_USBPHY_PWD_SET); +} -#ifdef CONFIG_USB_GADGET_ARC -#endif /* CONFIG_USB_GADGET_ARC */ +static void _host_platform_resume(struct fsl_usb2_platform_data *pdata) +{ + void __iomem *phy_reg = MVF_IO_ADDRESS(MVF_USBPHY0_BASE_ADDR); + u32 tmp; -void __init mvf_usb_dr_init(void) + tmp = (BM_USBPHY_PWD_TXPWDFS + | BM_USBPHY_PWD_TXPWDIBIAS + | BM_USBPHY_PWD_TXPWDV2I + | BM_USBPHY_PWD_RXPWDENV + | BM_USBPHY_PWD_RXPWD1PT1 + | BM_USBPHY_PWD_RXPWDDIFF + | BM_USBPHY_PWD_RXPWDRX); + __raw_writel(tmp, phy_reg + HW_USBPHY_PWD_CLR); +} + +static void _host_phy_lowpower_suspend(struct fsl_usb2_platform_data *pdata, + bool enable) { - struct platform_device *pdev; - u32 reg; -#ifdef CONFIG_USB_GADGET_ARC - dr_utmi_config.operating_mode = DR_UDC_MODE; - dr_utmi_config.platform_suspend = NULL; - dr_utmi_config.platform_resume = NULL; + __phy_lowpower_suspend(pdata, enable, ENABLED_BY_HOST); +} - pdev = mvf_add_fsl_usb2_udc(&dr_utmi_config); +static void _host_wakeup_enable(struct fsl_usb2_platform_data *pdata, + bool enable) +{ + __wakeup_irq_enable(pdata, enable, ENABLED_BY_HOST); - __raw_writel(((0x01 << 30) | 0x2), ANADIG_USB1_MISC); + if (enable) + device_wakeup_enable(&pdata->pdev->dev); + else + device_wakeup_disable(&pdata->pdev->dev); +} - __raw_writel(0x00000894, USBPHY1_CTRL); - udelay(10); +static enum usb_wakeup_event +_is_host_wakeup(struct fsl_usb2_platform_data *pdata) +{ + u32 wakeup_req = USBC0_CTRL & UCTRL_OWIR; + u32 otgsc = UOG_OTGSC; - __raw_writel(0x0, USBPHY1_PWD); - __raw_writel(0x1, USBPHY1_IP); + if (wakeup_req) { + pr_debug("the otgsc is 0x%x, usbsts is 0x%x," + " portsc is 0x%x, wakeup_irq is 0x%x\n", + UOG_OTGSC, UOG_USBSTS, UOG_PORTSC1, wakeup_req); + } + /* if ID change sts, it is a host wakeup event */ + if (wakeup_req && (otgsc & OTGSC_IS_USB_ID)) { + pr_debug("otg host ID wakeup\n"); + /* if host ID wakeup, we must clear the b session change sts */ + otgsc &= (~OTGSC_IS_USB_ID); + return WAKEUP_EVENT_ID; + } + if (wakeup_req && (!(otgsc & OTGSC_STS_USB_ID))) { + pr_debug("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_phy_lowpower_suspend(pdata, false); + _host_wakeup_enable(pdata, false); +} + +/* End of host related operation for DR port */ +#endif /* CONFIG_USB_EHCI_ARC_OTG */ + + +void __init mvf_usb_dr_init(void) +{ + struct platform_device *pdev, *pdev_wakeup; + u32 reg; + +#ifdef CONFIG_USB_EHCI_ARC_OTG + dr_utmi_config.operating_mode = DR_HOST_MODE; + dr_utmi_config.wake_up_enable = _host_wakeup_enable; + dr_utmi_config.platform_suspend = _host_platform_suspend; + dr_utmi_config.platform_resume = _host_platform_resume; + 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; + + pdev = mvf_add_fsl_ehci_otg(0, &dr_utmi_config); + + dr_wakeup_config.usb_pdata[0] = pdev->dev.platform_data; + + /* register wakeup device */ + pdev_wakeup = mvf_add_fsl_usb2_ehci_otg_wakeup(0, &dr_wakeup_config); + if (pdev != NULL) + ((struct fsl_usb2_platform_data *) + (pdev->dev.platform_data))->wakeup_pdata = + (struct fsl_usb2_wakeup_platform_data *) + (pdev_wakeup->dev.platform_data); + + __raw_writel(0x0220C802, USBPHY1_CTRL); udelay(20); - __raw_writel(0x7, USBPHY1_IP); + + __raw_writel(0x0, USBPHY1_PWD); reg = __raw_readl(USBPHY1_DEBUG); - reg &= ~0x40000000; /* clear gate clock */ + reg &= ~0x40000000; /* clear clock gate */ __raw_writel(reg, USBPHY1_DEBUG); + reg = __raw_readl(ANADIG_USB1_MISC); + reg |= (0x01 << 30); /* Enable CLK_TO_UTMI */ + __raw_writel(reg, ANADIG_USB1_MISC); + __raw_writel(0x10000007, USBPHY1_TX); + /*__raw_writel(0x100F0F07, USBPHY1_TX);*/ + + /* Enable disconnect detect */ reg = __raw_readl(USBPHY1_CTRL); - reg |= ((0xD1 << 11) | 0x6); + reg &= ~0x040; /* clear OTG ID change IRQ */ + reg |= (0x1 << 14); /* Enable UTMI+ level2 */ + reg |= (0x1 << 9); + reg |= (0x1 << 15); /* Enable UTMI+ level3 */ + reg &= ~((0xD1 << 11) | 0x6); __raw_writel(reg, USBPHY1_CTRL); + __raw_writel(0x1 << 3, USBPHY1_CTRL + 0x8); + /* Disable VBUS and CHARGER detect */ reg = __raw_readl(ANADIG_USB1_VBUS_DETECT); reg |= 0xE8; __raw_writel(reg, ANADIG_USB1_VBUS_DETECT); __raw_writel(0x001c0000, ANADIG_USB1_CHRG_DETECT); + #endif +} + +/* USB HIGH_SPEED disconnect detect on/off */ +void fsl_platform_set_usb0_phy_dis(struct fsl_usb2_platform_data *pdata, + bool enable) +{ + u32 reg; + reg = __raw_readl(USBPHY1_CTRL); + + if (enable) + reg |= ((0xD1 << 11) | 0x6); + else + reg &= ~((0xD1 << 11) | 0x6); + + __raw_writel(reg, USBPHY1_CTRL); + + __raw_writel(0x1 << 3, USBPHY1_CTRL + 0x8); } diff --git a/arch/arm/mach-mvf/usb_dr2.c b/arch/arm/mach-mvf/usb_dr2.c index d26cfde125e0..c1d6ebf51791 100644 --- a/arch/arm/mach-mvf/usb_dr2.c +++ b/arch/arm/mach-mvf/usb_dr2.c @@ -20,6 +20,7 @@ #include <linux/types.h> #include <linux/clk.h> #include <linux/platform_device.h> +#include <linux/pm_wakeup.h> #include <linux/fsl_devices.h> #include <linux/delay.h> #include <linux/io.h> @@ -31,10 +32,11 @@ #include "regs-anadig.h" #include "usb.h" -static int usbotg2_init_ext(struct platform_device *pdev); -static void usbotg2_uninit_ext(struct platform_device *pdev); +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 void _dr_discharge_line(bool enable); +static void usbotg_wakeup_event_clear(void); static struct clk *usb_phy1_clk; static u8 otg2_used; @@ -47,9 +49,9 @@ static u8 otg2_used; * - operating_mode plugged at run time */ static struct fsl_usb2_platform_data dr_utmi_config = { - .name = "DR", - .init = usbotg2_init_ext, - .exit = usbotg2_uninit_ext, + .name = "DR2", + .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, @@ -58,6 +60,13 @@ static struct fsl_usb2_platform_data dr_utmi_config = { .dr_discharge_line = _dr_discharge_line, }; +/* Platform data for wakeup operation */ +static struct fsl_usb2_wakeup_platform_data dr_wakeup_config = { + .name = "Host wakeup", + .usb_clock_for_pm = usbotg_clock_gate, + .usb_wakeup_exhandle = usbotg_wakeup_event_clear, +}; + static void usbotg_internal_phy_clock_gate(bool on) { void __iomem *phy_reg = MVF_IO_ADDRESS(MVF_USBPHY1_BASE_ADDR); @@ -111,7 +120,7 @@ static int usb_phy_enable(struct fsl_usb2_platform_data *pdata) return 0; } /* Notes: configure USB clock*/ -static int usbotg2_init_ext(struct platform_device *pdev) +static int usbotg_init_ext(struct platform_device *pdev) { struct clk *usb_clk; u32 ret; @@ -139,7 +148,7 @@ static int usbotg2_init_ext(struct platform_device *pdev) return ret; } -static void usbotg2_uninit_ext(struct platform_device *pdev) +static void usbotg_uninit_ext(struct platform_device *pdev) { struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; @@ -181,6 +190,113 @@ static void _dr_discharge_line(bool enable) /* 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 void enter_phy_lowpower_suspend(struct fsl_usb2_platform_data *pdata, + bool enable) +{ + void __iomem *phy_reg = MVF_IO_ADDRESS(MVF_USBPHY1_BASE_ADDR); + u32 tmp; + pr_debug("DR: %s begins, enable is %d\n", __func__, enable); + + if (enable) { + UOG2_PORTSC1 |= PORTSC_PHCD; + tmp = (BM_USBPHY_PWD_TXPWDFS + | BM_USBPHY_PWD_TXPWDIBIAS + | BM_USBPHY_PWD_TXPWDV2I + | BM_USBPHY_PWD_RXPWDENV + | BM_USBPHY_PWD_RXPWD1PT1 + | BM_USBPHY_PWD_RXPWDDIFF + | BM_USBPHY_PWD_RXPWDRX); + __raw_writel(tmp, phy_reg + HW_USBPHY_PWD_SET); + usbotg_internal_phy_clock_gate(false); + + } else { + if (UOG2_PORTSC1 & PORTSC_PHCD) { + UOG2_PORTSC1 &= ~PORTSC_PHCD; + mdelay(1); + } + usbotg_internal_phy_clock_gate(true); + tmp = (BM_USBPHY_PWD_TXPWDFS + | BM_USBPHY_PWD_TXPWDIBIAS + | BM_USBPHY_PWD_TXPWDV2I + | BM_USBPHY_PWD_RXPWDENV + | BM_USBPHY_PWD_RXPWD1PT1 + | BM_USBPHY_PWD_RXPWDDIFF + | BM_USBPHY_PWD_RXPWDRX); + __raw_writel(tmp, phy_reg + HW_USBPHY_PWD_CLR); + + } + pr_debug("DR: %s ends, enable is %d\n", __func__, enable); +} + +static void __phy_lowpower_suspend(struct fsl_usb2_platform_data *pdata, + bool enable, int source) +{ + if (enable) + enter_phy_lowpower_suspend(pdata, enable); + else { + pr_debug("phy lowpower disable\n"); + enter_phy_lowpower_suspend(pdata, enable); + } +} + +static void otg_wake_up_enable(struct fsl_usb2_platform_data *pdata, + bool enable) +{ + void __iomem *phy_reg = MVF_IO_ADDRESS(MVF_USBPHY1_BASE_ADDR); + + pr_debug("%s, enable is %d\n", __func__, enable); + if (enable) { + __raw_writel(BM_USBPHY_CTRL_ENIDCHG_WKUP + | BM_USBPHY_CTRL_ENVBUSCHG_WKUP + | BM_USBPHY_CTRL_ENDPDMCHG_WKUP + | BM_USBPHY_CTRL_ENAUTOSET_USBCLKS + | BM_USBPHY_CTRL_ENAUTOCLR_PHY_PWD + | BM_USBPHY_CTRL_ENAUTOCLR_CLKGATE + | BM_USBPHY_CTRL_ENAUTOCLR_USBCLKGATE + | BM_USBPHY_CTRL_ENAUTO_PWRON_PLL, + phy_reg + HW_USBPHY_CTRL_SET); + USBC1_CTRL |= UCTRL_OWIE; + } else { + USBC1_CTRL &= ~UCTRL_OWIE; + /* 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 __wakeup_irq_enable(struct fsl_usb2_platform_data *pdata, + bool on, int source) +{ + pr_debug("USB Host %s,on=%d\n", __func__, on); + mdelay(3); + if (on) { + otg_wake_up_enable(pdata, on); + } else { + otg_wake_up_enable(pdata, on); + /* The interrupt must be disabled for at least 3 clock + * cycles of the standby clock(32k Hz) , that is 0.094 ms*/ + udelay(100); + } +} + +/* 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 = USBC1_CTRL & UCTRL_OWIR; + + if (wakeup_req != 0) { + pr_debug("Unknown wakeup.(OTGSC 0x%x)\n", UOG2_OTGSC); + /* Disable OWIE to clear OWIR, wait 3 clock + * cycles of standly clock(32KHz) + */ + USBC1_CTRL &= ~UCTRL_OWIE; + udelay(100); + USBC1_CTRL |= UCTRL_OWIE; + } +} + /* End of Common operation for DR port */ #ifdef CONFIG_USB_EHCI_ARC_OTG @@ -215,21 +331,84 @@ static void _host_platform_resume(struct fsl_usb2_platform_data *pdata) __raw_writel(tmp, phy_reg + HW_USBPHY_PWD_CLR); } +static void _host_phy_lowpower_suspend(struct fsl_usb2_platform_data *pdata, + bool enable) +{ + __phy_lowpower_suspend(pdata, enable, ENABLED_BY_HOST); +} + +static void _host_wakeup_enable(struct fsl_usb2_platform_data *pdata, + bool enable) +{ + __wakeup_irq_enable(pdata, enable, ENABLED_BY_HOST); + + if (enable) + device_wakeup_enable(&pdata->pdev->dev); + else + device_wakeup_disable(&pdata->pdev->dev); +} + +static enum usb_wakeup_event +_is_host_wakeup(struct fsl_usb2_platform_data *pdata) +{ + u32 wakeup_req = USBC1_CTRL & UCTRL_OWIR; + u32 otgsc = UOG2_OTGSC; + + if (wakeup_req) { + pr_debug("the otgsc is 0x%x, usbsts is 0x%x," + " portsc is 0x%x, wakeup_irq is 0x%x\n", + UOG2_OTGSC, UOG2_USBSTS, UOG2_PORTSC1, wakeup_req); + } + /* if ID change sts, it is a host wakeup event */ + if (wakeup_req && (otgsc & OTGSC_IS_USB_ID)) { + pr_debug("otg host ID wakeup\n"); + /* if host ID wakeup, we must clear the b session change sts */ + otgsc &= (~OTGSC_IS_USB_ID); + return WAKEUP_EVENT_ID; + } + if (wakeup_req && (!(otgsc & OTGSC_STS_USB_ID))) { + pr_debug("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_phy_lowpower_suspend(pdata, false); + _host_wakeup_enable(pdata, false); +} + /* End of host related operation for DR port */ #endif /* CONFIG_USB_EHCI_ARC_OTG */ void __init mvf_usb_dr2_init(void) { - struct platform_device *pdev; + struct platform_device *pdev, *pdev_wakeup; u32 reg; #ifdef CONFIG_USB_EHCI_ARC_OTG dr_utmi_config.operating_mode = DR_HOST_MODE; + dr_utmi_config.wake_up_enable = _host_wakeup_enable; dr_utmi_config.platform_suspend = _host_platform_suspend; dr_utmi_config.platform_resume = _host_platform_resume; + 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; + + pdev = mvf_add_fsl_ehci_otg(1, &dr_utmi_config); + + dr_wakeup_config.usb_pdata[1] = pdev->dev.platform_data; - pdev = mvf_add_fsl_ehci_otg(&dr_utmi_config); + /* register wakeup device */ + pdev_wakeup = mvf_add_fsl_usb2_ehci_otg_wakeup(1, &dr_wakeup_config); + if (pdev != NULL) + ((struct fsl_usb2_platform_data *) + (pdev->dev.platform_data))->wakeup_pdata = + (struct fsl_usb2_wakeup_platform_data *) + (pdev_wakeup->dev.platform_data); __raw_writel(0x0220C802, USBPHY2_CTRL); udelay(20); @@ -267,7 +446,7 @@ void __init mvf_usb_dr2_init(void) } /* USB HIGH_SPEED disconnect detect on/off */ -void fsl_platform_set_usb_phy_dis(struct fsl_usb2_platform_data *pdata, +void fsl_platform_set_usb1_phy_dis(struct fsl_usb2_platform_data *pdata, bool enable) { u32 reg; |