summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnson Huang <b20788@freescale.com>2011-07-18 12:54:13 +0800
committerJason Liu <r64343@freescale.com>2012-07-20 13:14:47 +0800
commit683b3c5bbb377e08043c814da860677b03815452 (patch)
tree11f09f5b8c6dfe6a457ec6103b456ca2b6778ff6
parente1425efa1589cb97347f3195b018163665d2fc52 (diff)
ENGR00139274-1 [MX6]Enable suspend/resume feature
Enable suspend/resume feature for MX6q echo standby > /sys/power/state -> wait mode; echo mem > /sys/power/state -> stop mode; Currentlu only support debug uart as wakeup source; Signed-off-by: Anson Huang <b20788@freescale.com>
-rw-r--r--arch/arm/mach-mx6/Kconfig1
-rw-r--r--arch/arm/mach-mx6/Makefile4
-rw-r--r--arch/arm/mach-mx6/board-mx6q_sabreauto.c15
-rw-r--r--arch/arm/mach-mx6/cpu.c1
-rw-r--r--arch/arm/mach-mx6/devices-imx6q.h4
-rw-r--r--arch/arm/mach-mx6/devices.c21
-rw-r--r--arch/arm/mach-mx6/mx6q_suspend.S40
-rw-r--r--arch/arm/mach-mx6/plat_hotplug.c29
-rw-r--r--arch/arm/mach-mx6/platsmp.c1
-rw-r--r--arch/arm/mach-mx6/pm.c247
-rw-r--r--arch/arm/mach-mx6/system.c80
-rwxr-xr-xarch/arm/plat-mxc/devices/Kconfig3
-rwxr-xr-xarch/arm/plat-mxc/devices/Makefile1
-rw-r--r--arch/arm/plat-mxc/devices/platform-imx-pm.c47
-rwxr-xr-xarch/arm/plat-mxc/include/mach/devices-common.h10
-rw-r--r--arch/arm/plat-mxc/include/mach/imx-pm.h31
16 files changed, 510 insertions, 25 deletions
diff --git a/arch/arm/mach-mx6/Kconfig b/arch/arm/mach-mx6/Kconfig
index 265439983cb0..5619c7eb1794 100644
--- a/arch/arm/mach-mx6/Kconfig
+++ b/arch/arm/mach-mx6/Kconfig
@@ -39,6 +39,7 @@ config MACH_MX6Q_SABREAUTO
select IMX_HAVE_PLATFORM_IMX_OCOTP
select IMX_HAVE_PLATFORM_IMX2_WDT
select IMX_HAVE_PLATFORM_IMX_SNVS_RTC
+ select IMX_HAVE_PLATFORM_IMX_PM
help
Include support for i.MX 6Quad SABRE Automotive Infotainment platform. This includes specific
configurations for the board and its peripherals.
diff --git a/arch/arm/mach-mx6/Makefile b/arch/arm/mach-mx6/Makefile
index 99b5623f3fad..094b2cfd7773 100644
--- a/arch/arm/mach-mx6/Makefile
+++ b/arch/arm/mach-mx6/Makefile
@@ -3,8 +3,8 @@
#
# Object file lists.
-obj-y := cpu.o mm.o system.o devices.o dummy_gpio.o irq.o bus_freq.o usb_dr.o usb_h1.o
+obj-y := cpu.o mm.o system.o devices.o dummy_gpio.o irq.o bus_freq.o usb_dr.o usb_h1.o pm.o
-obj-$(CONFIG_ARCH_MX6) += clock.o
+obj-$(CONFIG_ARCH_MX6) += clock.o mx6q_suspend.o
obj-$(CONFIG_MACH_MX6Q_SABREAUTO) += board-mx6q_sabreauto.o
obj-$(CONFIG_SMP) += plat_hotplug.o platsmp.o headsmp.o localtimer.o
diff --git a/arch/arm/mach-mx6/board-mx6q_sabreauto.c b/arch/arm/mach-mx6/board-mx6q_sabreauto.c
index 0b1e967b8802..4bba7a49b0ff 100644
--- a/arch/arm/mach-mx6/board-mx6q_sabreauto.c
+++ b/arch/arm/mach-mx6/board-mx6q_sabreauto.c
@@ -505,6 +505,20 @@ static struct imx_ipuv3_platform_data ipu_data[] = {
},
};
+static void sabreauto_suspend_enter(void)
+{
+ /* suspend preparation */
+}
+
+static void sabreauto_suspend_exit(void)
+{
+ /* resmue resore */
+}
+static const struct pm_platform_data mx6q_sabreauto_pm_data __initconst = {
+ .name = "imx_pm",
+ .suspend_enter = sabreauto_suspend_enter,
+ .suspend_exit = sabreauto_suspend_exit,
+};
/*!
* Board specific initialization.
*/
@@ -537,6 +551,7 @@ static void __init mx6_board_init(void)
imx6q_add_anatop_thermal_imx(1, &mx6q_sabreauto_anatop_thermal_data);
imx6q_init_fec();
+ imx6q_add_pm_imx(0, &mx6q_sabreauto_pm_data);
imx6q_add_sdhci_usdhc_imx(3, &mx6q_sabreauto_sd4_data);
imx6q_add_sdhci_usdhc_imx(2, &mx6q_sabreauto_sd3_data);
imx_add_viv_gpu("gc2000", &imx6_gc2000_data, &imx6q_gc2000_pdata);
diff --git a/arch/arm/mach-mx6/cpu.c b/arch/arm/mach-mx6/cpu.c
index 685646793dbc..f0e1d77d35ce 100644
--- a/arch/arm/mach-mx6/cpu.c
+++ b/arch/arm/mach-mx6/cpu.c
@@ -21,6 +21,7 @@
#include <linux/init.h>
#include <linux/clk.h>
#include <linux/module.h>
+#include <linux/iram_alloc.h>
#include <mach/hardware.h>
#include <linux/iram_alloc.h>
#include <asm/io.h>
diff --git a/arch/arm/mach-mx6/devices-imx6q.h b/arch/arm/mach-mx6/devices-imx6q.h
index fe408c60c0d4..488ae599759e 100644
--- a/arch/arm/mach-mx6/devices-imx6q.h
+++ b/arch/arm/mach-mx6/devices-imx6q.h
@@ -111,3 +111,7 @@ extern const struct imx_otp_data imx6q_otp_data __initconst;
extern const struct imx_imx2_wdt_data imx6q_imx2_wdt_data[] __initconst;
#define imx6q_add_imx2_wdt(id, pdata) \
imx_add_imx2_wdt(&imx6q_imx2_wdt_data[id])
+
+extern const struct imx_pm_imx_data imx6q_pm_imx_data __initconst;
+#define imx6q_add_pm_imx(id, pdata) \
+ imx_add_pm_imx(&imx6q_pm_imx_data, pdata)
diff --git a/arch/arm/mach-mx6/devices.c b/arch/arm/mach-mx6/devices.c
index f40b5a4d7456..996de31d4933 100644
--- a/arch/arm/mach-mx6/devices.c
+++ b/arch/arm/mach-mx6/devices.c
@@ -32,27 +32,6 @@
#include <mach/hardware.h>
#include <mach/gpio.h>
-static struct resource mxc_anatop_resources[] = {
- {
- .start = ANATOP_BASE_ADDR,
- .end = ANATOP_BASE_ADDR + SZ_4K - 1,
- .flags = IORESOURCE_MEM,
- },
- {
- .start = MXC_INT_ANATOP_TEMPSNSR,
- .end = MXC_INT_ANATOP_TEMPSNSR,
- .flags = IORESOURCE_IRQ,
- },
-};
-
-struct platform_device anatop_thermal_device = {
- .name = "anatop_thermal",
- .id = 1,
- .num_resources = ARRAY_SIZE(mxc_anatop_resources),
- .resource = mxc_anatop_resources,
-};
-
-
static struct mxc_gpio_port mxc_gpio_ports[] = {
{
.chip.label = "gpio-0",
diff --git a/arch/arm/mach-mx6/mx6q_suspend.S b/arch/arm/mach-mx6/mx6q_suspend.S
new file mode 100644
index 000000000000..d39ff7ccefbd
--- /dev/null
+++ b/arch/arm/mach-mx6/mx6q_suspend.S
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2010-2011 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/linkage.h>
+
+#define ARM_CTRL_DCACHE 1 << 2
+#define ARM_CTRL_ICACHE 1 << 12
+#define ARM_AUXCR_L2EN 1 << 1
+
+/*
+ * mx6q_suspend
+ *
+ * Suspend the processor (eg, wait for interrupt).
+ * Set the DDR into Self Refresh
+ * IRQs are already disabled.
+ */
+ENTRY(mx6q_suspend)
+ stmfd sp!, {r0-r4} @ Save registers
+ dsb
+ wfi
+ ldmfd sp!, {r0-r4}
+ .type mx6q_do_suspend, #object
+ENTRY(mx6q_do_suspend)
+ .word mx6q_suspend
+ .size mx6q_suspend, . - mx6q_suspend
diff --git a/arch/arm/mach-mx6/plat_hotplug.c b/arch/arm/mach-mx6/plat_hotplug.c
index a0d94908765c..6ef057f6865a 100644
--- a/arch/arm/mach-mx6/plat_hotplug.c
+++ b/arch/arm/mach-mx6/plat_hotplug.c
@@ -19,7 +19,11 @@
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/smp.h>
-
+#include <mach/hardware.h>
+#include <asm/mach-types.h>
+#include <linux/io.h>
+#include "src-reg.h"
+#include <linux/sched.h>
#include <asm/cacheflush.h>
@@ -34,7 +38,30 @@ int platform_cpu_kill(unsigned int cpu)
*/
void platform_cpu_die(unsigned int cpu)
{
+ void __iomem *src_base = IO_ADDRESS(SRC_BASE_ADDR);
+ unsigned int val;
+
+ if (cpu == 0) {
+ printk(KERN_ERR "CPU0 can't be disabled!\n");
+ return;
+ }
+ flush_cache_all();
+ dsb();
+
+ /*
+ * we're ready for shutdown now, so do it
+ */
+ val = readl(src_base + SRC_SCR_OFFSET);
+ val &= ~(1 << (BP_SRC_SCR_CORES_DBG_RST + cpu));
+ writel(val, src_base + SRC_SCR_OFFSET);
+ for (;;) {
+ /*
+ * Execute WFI
+ */
+ cpu_do_idle();
+ printk(KERN_INFO "CPU%u: spurious wakeup call\n", cpu);
+ }
}
int platform_cpu_disable(unsigned int cpu)
diff --git a/arch/arm/mach-mx6/platsmp.c b/arch/arm/mach-mx6/platsmp.c
index a49e3cca3cc4..66ab4e60dfe2 100644
--- a/arch/arm/mach-mx6/platsmp.c
+++ b/arch/arm/mach-mx6/platsmp.c
@@ -61,7 +61,6 @@ int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle)
{
unsigned long boot_entry;
void __iomem *src_base = IO_ADDRESS(SRC_BASE_ADDR);
- unsigned long timeout;
int val;
/*
diff --git a/arch/arm/mach-mx6/pm.c b/arch/arm/mach-mx6/pm.c
new file mode 100644
index 000000000000..0d30bdf9500d
--- /dev/null
+++ b/arch/arm/mach-mx6/pm.c
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 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/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/suspend.h>
+#include <linux/regulator/machine.h>
+#include <linux/proc_fs.h>
+#include <linux/iram_alloc.h>
+#include <linux/fsl_devices.h>
+#include <asm/mach-types.h>
+#include <asm/cacheflush.h>
+#include <asm/tlb.h>
+#include <asm/delay.h>
+#include <asm/mach/map.h>
+#include <mach/hardware.h>
+#include <mach/imx-pm.h>
+#ifdef CONFIG_ARCH_MX61
+#include <mach/iomux-mx6q.h>
+#endif
+#include "crm_regs.h"
+
+#define SCU_CTRL_OFFSET 0x00
+#define GPC_IMR1_OFFSET 0x08
+#define GPC_IMR2_OFFSET 0x0c
+#define GPC_IMR3_OFFSET 0x10
+#define GPC_IMR4_OFFSET 0x14
+#define GPC_PGC_CPU_PUPSCR_OFFSET 0x2a4
+#define GPC_PGC_CPU_PDNSCR_OFFSET 0x2a8
+#define UART_UCR3_OFFSET 0x88
+#define UART_USR1_OFFSET 0x94
+#define UART_UCR3_AWAKEN (1 << 4)
+#define UART_USR1_AWAKE (1 << 4)
+static struct clk *cpu_clk;
+static struct pm_platform_data *pm_data;
+
+#if defined(CONFIG_CPU_FREQ)
+static int org_freq;
+extern int set_cpu_freq(int wp);
+#endif
+
+extern void mx6q_suspend(u32 m4if_addr);
+static struct device *pm_dev;
+struct clk *gpc_dvfs_clk;
+void *suspend_iram_base;
+void (*suspend_in_iram)(void *sdclk_iomux_addr) = NULL;
+void __iomem *suspend_param1;
+
+static void __iomem *scu_base;
+static void __iomem *gpc_base;
+static u32 ccm_clpcr, scu_ctrl;
+static u32 gpc_imr[4], gpc_cpu_pup, gpc_cpu_pdn;
+
+static void mx6_suspend_store()
+{
+ /* save some settings before suspend */
+ ccm_clpcr = __raw_readl(MXC_CCM_CLPCR);
+ 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_cpu_pup = __raw_readl(gpc_base + GPC_PGC_CPU_PUPSCR_OFFSET);
+ gpc_cpu_pdn = __raw_readl(gpc_base + GPC_PGC_CPU_PDNSCR_OFFSET);
+}
+
+static void mx6_suspend_restore()
+{
+ /* restore settings after suspend */
+ __raw_writel(ccm_clpcr, MXC_CCM_CLPCR);
+ __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_cpu_pup, gpc_base + GPC_PGC_CPU_PUPSCR_OFFSET);
+ __raw_writel(gpc_cpu_pdn, gpc_base + GPC_PGC_CPU_PDNSCR_OFFSET);
+}
+static int mx6_suspend_enter(suspend_state_t state)
+{
+ mx6_suspend_store();
+
+ switch (state) {
+ case PM_SUSPEND_MEM:
+ mxc_cpu_lp_set(STOP_POWER_OFF);
+ break;
+ case PM_SUSPEND_STANDBY:
+ mxc_cpu_lp_set(WAIT_UNCLOCKED_POWER_OFF);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (state == PM_SUSPEND_MEM) {
+ if (pm_data && pm_data->suspend_enter)
+ pm_data->suspend_enter();
+
+ local_flush_tlb_all();
+ flush_cache_all();
+ outer_cache.flush_all();
+
+ suspend_in_iram(suspend_param1);
+
+ mx6_suspend_restore();
+ if (pm_data && pm_data->suspend_exit)
+ pm_data->suspend_exit();
+ } else {
+ cpu_do_idle();
+ }
+ return 0;
+}
+
+
+/*
+ * Called after processes are frozen, but before we shut down devices.
+ */
+static int mx6_suspend_prepare(void)
+{
+ return 0;
+}
+
+/*
+ * Called before devices are re-setup.
+ */
+static void mx6_suspend_finish(void)
+{
+#if defined(CONFIG_CPU_FREQ)
+ struct cpufreq_freqs freqs;
+
+ freqs.old = clk_get_rate(cpu_clk) / 1000;
+ freqs.new = org_freq / 1000;
+ freqs.cpu = 0;
+ freqs.flags = 0;
+
+ if (org_freq != clk_get_rate(cpu_clk)) {
+ set_cpu_freq(org_freq);
+ cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+ cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+ }
+#endif
+}
+
+/*
+ * Called after devices are re-setup, but before processes are thawed.
+ */
+static void mx6_suspend_end(void)
+{
+}
+
+static int mx6_pm_valid(suspend_state_t state)
+{
+ return (state > PM_SUSPEND_ON && state <= PM_SUSPEND_MAX);
+}
+
+struct platform_suspend_ops mx6_suspend_ops = {
+ .valid = mx6_pm_valid,
+ .prepare = mx6_suspend_prepare,
+ .enter = mx6_suspend_enter,
+ .finish = mx6_suspend_finish,
+ .end = mx6_suspend_end,
+};
+
+static int __devinit mx6_pm_probe(struct platform_device *pdev)
+{
+ pm_dev = &pdev->dev;
+ pm_data = pdev->dev.platform_data;
+
+ return 0;
+}
+
+static struct platform_driver mx6_pm_driver = {
+ .driver = {
+ .name = "imx_pm",
+ },
+ .probe = mx6_pm_probe,
+};
+
+static int __init pm_init(void)
+{
+ unsigned long iram_paddr, cpaddr;
+
+ scu_base = IO_ADDRESS(SCU_BASE_ADDR);
+ gpc_base = IO_ADDRESS(GPC_BASE_ADDR);
+
+ pr_info("Static Power Management for Freescale i.MX6\n");
+
+ if (platform_driver_register(&mx6_pm_driver) != 0) {
+ printk(KERN_ERR "mx6_pm_driver register failed\n");
+ return -ENODEV;
+ }
+
+ suspend_set_ops(&mx6_suspend_ops);
+ /* Move suspend routine into iRAM */
+ cpaddr = (unsigned long)iram_alloc(SZ_4K, &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_4K,
+ MT_HIGH_VECTORS);
+ pr_info("cpaddr = %x suspend_iram_base=%x\n",
+ (unsigned int)cpaddr, (unsigned int)suspend_iram_base);
+
+ /*
+ * Need to run the suspend code from IRAM as the DDR needs
+ * to be put into self refresh mode manually.
+ */
+ memcpy((void *)cpaddr, mx6q_suspend, SZ_4K);
+
+ 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);
+ }
+ printk(KERN_INFO "PM driver module loaded\n");
+
+ return 0;
+}
+
+
+static void __exit pm_cleanup(void)
+{
+ /* Unregister the device structure */
+ platform_driver_unregister(&mx6_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-mx6/system.c b/arch/arm/mach-mx6/system.c
index ba4091e9d285..f18389bf26da 100644
--- a/arch/arm/mach-mx6/system.c
+++ b/arch/arm/mach-mx6/system.c
@@ -26,6 +26,86 @@
#include <mach/clock.h>
#include <asm/proc-fns.h>
#include <asm/system.h>
+#include "crm_regs.h"
+
+#define SCU_CTRL 0x00
+#define SCU_CONFIG 0x04
+#define SCU_CPU_STATUS 0x08
+#define SCU_INVALIDATE 0x0c
+#define SCU_FPGA_REVISION 0x10
+
+void gpc_enable_wakeup(unsigned int irq)
+{
+ void __iomem *gpc_base = IO_ADDRESS(GPC_BASE_ADDR);
+
+ if ((irq < 32) || (irq > 158))
+ printk(KERN_ERR "Invalid irq number!\n");
+
+ /* Enable wake up source */
+ __raw_writel(~(1 << (irq % 32)),
+ gpc_base + 0x8 + (irq / 32 - 1) * 4);
+
+}
+/* set cpu low power mode before WFI instruction */
+void mxc_cpu_lp_set(enum mxc_cpu_pwr_mode mode)
+{
+ void __iomem *scu_base = IO_ADDRESS(SCU_BASE_ADDR);
+ void __iomem *gpc_base = IO_ADDRESS(GPC_BASE_ADDR);
+ u32 scu_cr, ccm_clpcr;
+ int stop_mode = 0;
+
+ scu_cr = __raw_readl(scu_base + SCU_CTRL);
+ ccm_clpcr = __raw_readl(MXC_CCM_CLPCR) & ~(MXC_CCM_CLPCR_LPM_MASK);
+
+ switch (mode) {
+ case WAIT_CLOCKED:
+ break;
+ case WAIT_UNCLOCKED:
+ ccm_clpcr |= 0x1 << MXC_CCM_CLPCR_LPM_OFFSET;
+ break;
+ case WAIT_UNCLOCKED_POWER_OFF:
+ case STOP_POWER_OFF:
+ if (mode == WAIT_UNCLOCKED_POWER_OFF) {
+ ccm_clpcr |= 0x1 << MXC_CCM_CLPCR_LPM_OFFSET;
+ ccm_clpcr &= ~MXC_CCM_CLPCR_VSTBY;
+ ccm_clpcr &= ~MXC_CCM_CLPCR_SBYOS;
+ stop_mode = 0;
+ } else {
+ ccm_clpcr |= 0x2 << MXC_CCM_CLPCR_LPM_OFFSET;
+ ccm_clpcr |= 0x3 << MXC_CCM_CLPCR_STBY_COUNT_OFFSET;
+ ccm_clpcr |= MXC_CCM_CLPCR_VSTBY;
+ ccm_clpcr |= MXC_CCM_CLPCR_SBYOS;
+ ccm_clpcr |= MXC_CCM_CLPCR_BYP_MMDC_CH1_LPM_HS;
+ stop_mode = 1;
+ }
+ /* scu standby enable, scu clk will be
+ * off after all cpu enter WFI */
+ scu_cr |= 0x20;
+ break;
+ case STOP_POWER_ON:
+ ccm_clpcr |= 0x2 << MXC_CCM_CLPCR_LPM_OFFSET;
+ break;
+ default:
+ printk(KERN_WARNING "UNKNOWN cpu power mode: %d\n", mode);
+ return;
+ }
+
+ if (stop_mode == 1) {
+ /* Mask all wake up source */
+ __raw_writel(0xFFFFFFFF, gpc_base + 0x8);
+ __raw_writel(0xFFFFFFFF, gpc_base + 0xC);
+ __raw_writel(0xFFFFFFFF, gpc_base + 0x10);
+ __raw_writel(0xFFFFFFFF, gpc_base + 0x14);
+ /* Power down and power up sequence */
+ __raw_writel(0xFFFFFFFF, gpc_base + 0x2a4);
+ __raw_writel(0xFFFFFFFF, gpc_base + 0x2a8);
+
+ gpc_enable_wakeup(MXC_INT_UART4_ANDED);
+ }
+
+ __raw_writel(scu_cr, scu_base + SCU_CTRL);
+ __raw_writel(ccm_clpcr, MXC_CCM_CLPCR);
+}
void arch_idle(void)
{
diff --git a/arch/arm/plat-mxc/devices/Kconfig b/arch/arm/plat-mxc/devices/Kconfig
index 1307a4aaf06b..7c0e691277b1 100755
--- a/arch/arm/plat-mxc/devices/Kconfig
+++ b/arch/arm/plat-mxc/devices/Kconfig
@@ -145,3 +145,6 @@ config IMX_HAVE_PLATFORM_FSL_OTG
config IMX_HAVE_PLATFORM_FSL_USB_WAKEUP
bool
+
+config IMX_HAVE_PLATFORM_IMX_PM
+ bool
diff --git a/arch/arm/plat-mxc/devices/Makefile b/arch/arm/plat-mxc/devices/Makefile
index 43db7417c1bd..ff11fa946fee 100755
--- a/arch/arm/plat-mxc/devices/Makefile
+++ b/arch/arm/plat-mxc/devices/Makefile
@@ -50,3 +50,4 @@ obj-$(CONFIG_IMX_HAVE_PLATFORM_VIV_GPU) += platform-viv_gpu.o
obj-$(CONFIG_IMX_HAVE_PLATFORM_IMX_ANATOP_THERMAL) += platform-imx-anatop-thermal.o
obj-$(CONFIG_IMX_HAVE_PLATFORM_FSL_OTG) += platform-fsl-usb2-otg.o
obj-$(CONFIG_IMX_HAVE_PLATFORM_FSL_USB_WAKEUP) += platform-fsl-usb2-wakeup.o
+obj-$(CONFIG_IMX_HAVE_PLATFORM_IMX_PM) += platform-imx-pm.o
diff --git a/arch/arm/plat-mxc/devices/platform-imx-pm.c b/arch/arm/plat-mxc/devices/platform-imx-pm.c
new file mode 100644
index 000000000000..f901e18368c2
--- /dev/null
+++ b/arch/arm/plat-mxc/devices/platform-imx-pm.c
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#include <asm/sizes.h>
+#include <mach/hardware.h>
+#include <mach/devices-common.h>
+
+#define imx_pm_imx_data_entry_single(soc) \
+ { \
+ }
+
+#ifdef CONFIG_SOC_IMX6Q
+const struct imx_pm_imx_data imx6q_pm_imx_data[] __initconst =
+ imx_pm_imx_data_entry_single(MX6Q);
+#endif
+
+struct platform_device *__init imx_add_pm_imx(
+ const struct imx_pm_imx_data *data,
+ const struct pm_platform_data *pdata)
+{
+ struct resource res[] = {
+ {
+ .start = data->iobase,
+ .end = data->iobase + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ };
+
+ return imx_add_platform_device("imx_pm", 0,
+ res, ARRAY_SIZE(res), pdata, sizeof(*pdata));
+}
diff --git a/arch/arm/plat-mxc/include/mach/devices-common.h b/arch/arm/plat-mxc/include/mach/devices-common.h
index b37e876fc8ec..213e05c446f9 100755
--- a/arch/arm/plat-mxc/include/mach/devices-common.h
+++ b/arch/arm/plat-mxc/include/mach/devices-common.h
@@ -503,6 +503,16 @@ struct imx_dcp_data {
resource_size_t irq2;
};
+
+#include <mach/imx-pm.h>
+struct imx_pm_imx_data {
+ int id;
+ resource_size_t iobase;
+ resource_size_t irq;
+};
+struct platform_device *__init imx_add_pm_imx(
+ const struct imx_pm_imx_data *data,
+ const struct pm_platform_data *pdata);
struct platform_device *__init imx_add_dcp(
struct imx_fsl_usb2_otg_data {
diff --git a/arch/arm/plat-mxc/include/mach/imx-pm.h b/arch/arm/plat-mxc/include/mach/imx-pm.h
new file mode 100644
index 000000000000..f6d375a4f575
--- /dev/null
+++ b/arch/arm/plat-mxc/include/mach/imx-pm.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#ifndef __ASM_ARCH_IMX_PM_H
+#define __ASM_ARCH_IMX_PM_H
+
+/**
+ * struct pm_platform_data - optional platform data for pm on i.MX
+ *
+ */
+
+struct pm_platform_data {
+ char *name;
+ void (*suspend_enter) (void);
+ void (*suspend_exit) (void);
+};
+#endif /* __ASM_ARCH_IMX_PM_H */