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>
diff --git a/arch/arm/mach-mx6/Kconfig b/arch/arm/mach-mx6/Kconfig
index 2654399..5619c7e 100644
--- a/arch/arm/mach-mx6/Kconfig
+++ b/arch/arm/mach-mx6/Kconfig
@@ -39,6 +39,7 @@
 	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 99b5623..094b2cf 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 0b1e967..4bba7a4 100644
--- a/arch/arm/mach-mx6/board-mx6q_sabreauto.c
+++ b/arch/arm/mach-mx6/board-mx6q_sabreauto.c
@@ -505,6 +505,20 @@
 	},
 };
 
+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 @@
 	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 6856467..f0e1d77 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 fe408c6..488ae59 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_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 f40b5a4..996de31 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 0000000..d39ff7c
--- /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 a0d9490..6ef057f 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 @@
  */
 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 a49e3cc..66ab4e6 100644
--- a/arch/arm/mach-mx6/platsmp.c
+++ b/arch/arm/mach-mx6/platsmp.c
@@ -61,7 +61,6 @@
 {
 	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 0000000..0d30bdf
--- /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 ba4091e..f18389b 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 e8a1c02..e14793a 100644
--- a/arch/arm/plat-mxc/devices/Kconfig
+++ b/arch/arm/plat-mxc/devices/Kconfig
@@ -139,3 +139,6 @@
 
 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 74e7b0b..1ec1503 100644
--- a/arch/arm/plat-mxc/devices/Makefile
+++ b/arch/arm/plat-mxc/devices/Makefile
@@ -48,3 +48,4 @@
 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 0000000..f901e18
--- /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 652fd84..323ed42 100644
--- a/arch/arm/plat-mxc/include/mach/devices-common.h
+++ b/arch/arm/plat-mxc/include/mach/devices-common.h
@@ -503,3 +503,13 @@
 struct platform_device *__init imx_add_fsl_usb2_wakeup(
 		const struct imx_fsl_usb2_wakeup_data *data,
 		const struct fsl_usb2_wakeup_platform_data *pdata);
+
+#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);
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 0000000..f6d375a4
--- /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 */