#include <linux/io.h>
 #include <linux/err.h>
 #include <linux/export.h>
+
+#include <linux/genalloc.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+
 #include <asm/cacheflush.h>
+#include <asm/fncpy.h>
 #include <asm/system_misc.h>
 #include <asm/tlbflush.h>
 
  */
 #define IMX5_DEFAULT_CPU_IDLE_STATE WAIT_UNCLOCKED_POWER_OFF
 
+struct imx5_suspend_io_state {
+       u32     offset;
+       u32     clear;
+       u32     set;
+       u32     saved_value;
+};
+
 struct imx5_pm_data {
        phys_addr_t ccm_addr;
        phys_addr_t cortex_addr;
        phys_addr_t gpc_addr;
+       phys_addr_t m4if_addr;
+       phys_addr_t iomuxc_addr;
+       void (*suspend_asm)(void __iomem *ocram_vbase);
+       const u32 *suspend_asm_sz;
+       const struct imx5_suspend_io_state *suspend_io_config;
+       int suspend_io_count;
+};
+
+static const struct imx5_suspend_io_state imx53_suspend_io_config[] = {
+#define MX53_DSE_HIGHZ_MASK (0x7 << 19)
+       {.offset = 0x584, .clear = MX53_DSE_HIGHZ_MASK}, /* DQM0 */
+       {.offset = 0x594, .clear = MX53_DSE_HIGHZ_MASK}, /* DQM1 */
+       {.offset = 0x560, .clear = MX53_DSE_HIGHZ_MASK}, /* DQM2 */
+       {.offset = 0x554, .clear = MX53_DSE_HIGHZ_MASK}, /* DQM3 */
+       {.offset = 0x574, .clear = MX53_DSE_HIGHZ_MASK}, /* CAS */
+       {.offset = 0x588, .clear = MX53_DSE_HIGHZ_MASK}, /* RAS */
+       {.offset = 0x578, .clear = MX53_DSE_HIGHZ_MASK}, /* SDCLK_0 */
+       {.offset = 0x570, .clear = MX53_DSE_HIGHZ_MASK}, /* SDCLK_1 */
+
+       {.offset = 0x580, .clear = MX53_DSE_HIGHZ_MASK}, /* SDODT0 */
+       {.offset = 0x564, .clear = MX53_DSE_HIGHZ_MASK}, /* SDODT1 */
+       {.offset = 0x57c, .clear = MX53_DSE_HIGHZ_MASK}, /* SDQS0 */
+       {.offset = 0x590, .clear = MX53_DSE_HIGHZ_MASK}, /* SDQS1 */
+       {.offset = 0x568, .clear = MX53_DSE_HIGHZ_MASK}, /* SDQS2 */
+       {.offset = 0x558, .clear = MX53_DSE_HIGHZ_MASK}, /* SDSQ3 */
+       {.offset = 0x6f0, .clear = MX53_DSE_HIGHZ_MASK}, /* GRP_ADDS */
+       {.offset = 0x718, .clear = MX53_DSE_HIGHZ_MASK}, /* GRP_BODS */
+       {.offset = 0x71c, .clear = MX53_DSE_HIGHZ_MASK}, /* GRP_B1DS */
+       {.offset = 0x728, .clear = MX53_DSE_HIGHZ_MASK}, /* GRP_B2DS */
+       {.offset = 0x72c, .clear = MX53_DSE_HIGHZ_MASK}, /* GRP_B3DS */
+
+       /* Controls the CKE signal which is required to leave self refresh */
+       {.offset = 0x720, .clear = MX53_DSE_HIGHZ_MASK, .set = 1 << 19}, /* CTLDS */
 };
 
 static const struct imx5_pm_data imx51_pm_data __initconst = {
        .ccm_addr = 0x53fd4000,
        .cortex_addr = 0x63fa0000,
        .gpc_addr = 0x53fd8000,
+       .m4if_addr = 0x63fd8000,
+       .iomuxc_addr = 0x53fa8000,
+       .suspend_asm = &imx53_suspend,
+       .suspend_asm_sz = &imx53_suspend_sz,
+       .suspend_io_config = imx53_suspend_io_config,
+       .suspend_io_count = ARRAY_SIZE(imx53_suspend_io_config),
 };
 
+#define MX5_MAX_SUSPEND_IOSTATE ARRAY_SIZE(imx53_suspend_io_config)
+
+/*
+ * This structure is for passing necessary data for low level ocram
+ * suspend code(arch/arm/mach-imx/suspend-imx53.S), if this struct
+ * definition is changed, the offset definition in that file
+ * must be also changed accordingly otherwise, the suspend to ocram
+ * function will be broken!
+ */
+struct imx5_cpu_suspend_info {
+       void __iomem    *m4if_base;
+       void __iomem    *iomuxc_base;
+       u32             io_count;
+       struct imx5_suspend_io_state io_state[MX5_MAX_SUSPEND_IOSTATE];
+} __aligned(8);
+
 static void __iomem *ccm_base;
 static void __iomem *cortex_base;
 static void __iomem *gpc_base;
+static void __iomem *suspend_ocram_base;
+static void (*imx5_suspend_in_ocram_fn)(void __iomem *ocram_vbase);
 
 /*
  * set cpu low power mode before WFI instruction. This function is called
                /*clear the EMPGC0/1 bits */
                __raw_writel(0, gpc_base + MXC_SRPG_EMPGC0_SRPGCR);
                __raw_writel(0, gpc_base + MXC_SRPG_EMPGC1_SRPGCR);
+
+               if (imx5_suspend_in_ocram_fn)
+                       imx5_suspend_in_ocram_fn(suspend_ocram_base);
+               else
+                       cpu_do_idle();
+
+       } else {
+               cpu_do_idle();
        }
-       cpu_do_idle();
 
        /* return registers to default idle state */
        mx5_cpu_lp_set(IMX5_DEFAULT_CPU_IDLE_STATE);
        imx5_cpu_do_idle();
 }
 
+static int __init imx_suspend_alloc_ocram(
+                               size_t size,
+                               void __iomem **virt_out,
+                               phys_addr_t *phys_out)
+{
+       struct device_node *node;
+       struct platform_device *pdev;
+       struct gen_pool *ocram_pool;
+       unsigned long ocram_base;
+       void __iomem *virt;
+       phys_addr_t phys;
+       int ret = 0;
+
+       /* Copied from imx6: TODO factorize */
+       node = of_find_compatible_node(NULL, NULL, "mmio-sram");
+       if (!node) {
+               pr_warn("%s: failed to find ocram node!\n", __func__);
+               return -ENODEV;
+       }
+
+       pdev = of_find_device_by_node(node);
+       if (!pdev) {
+               pr_warn("%s: failed to find ocram device!\n", __func__);
+               ret = -ENODEV;
+               goto put_node;
+       }
+
+       ocram_pool = dev_get_gen_pool(&pdev->dev);
+       if (!ocram_pool) {
+               pr_warn("%s: ocram pool unavailable!\n", __func__);
+               ret = -ENODEV;
+               goto put_node;
+       }
+
+       ocram_base = gen_pool_alloc(ocram_pool, size);
+       if (!ocram_base) {
+               pr_warn("%s: unable to alloc ocram!\n", __func__);
+               ret = -ENOMEM;
+               goto put_node;
+       }
+
+       phys = gen_pool_virt_to_phys(ocram_pool, ocram_base);
+       virt = __arm_ioremap_exec(phys, size, false);
+       if (phys_out)
+               *phys_out = phys;
+       if (virt_out)
+               *virt_out = virt;
+
+put_node:
+       of_node_put(node);
+
+       return ret;
+}
+
+static int __init imx5_suspend_init(const struct imx5_pm_data *soc_data)
+{
+       struct imx5_cpu_suspend_info *suspend_info;
+       int ret;
+       /* Need this to avoid compile error due to const typeof in fncpy.h */
+       void (*suspend_asm)(void __iomem *) = soc_data->suspend_asm;
+
+       if (!suspend_asm)
+               return 0;
+
+       if (!soc_data->suspend_asm_sz || !*soc_data->suspend_asm_sz)
+               return -EINVAL;
+
+       ret = imx_suspend_alloc_ocram(
+               *soc_data->suspend_asm_sz + sizeof(*suspend_info),
+               &suspend_ocram_base, NULL);
+       if (ret)
+               return ret;
+
+       suspend_info = suspend_ocram_base;
+
+       suspend_info->io_count = soc_data->suspend_io_count;
+       memcpy(suspend_info->io_state, soc_data->suspend_io_config,
+              sizeof(*suspend_info->io_state) * soc_data->suspend_io_count);
+
+       suspend_info->m4if_base = ioremap(soc_data->m4if_addr, SZ_16K);
+       if (!suspend_info->m4if_base) {
+               ret = -ENOMEM;
+               goto failed_map_m4if;
+       }
+
+       suspend_info->iomuxc_base = ioremap(soc_data->iomuxc_addr, SZ_16K);
+       if (!suspend_info->iomuxc_base) {
+               ret = -ENOMEM;
+               goto failed_map_iomuxc;
+       }
+
+       imx5_suspend_in_ocram_fn = fncpy(
+               suspend_ocram_base + sizeof(*suspend_info),
+               suspend_asm,
+               *soc_data->suspend_asm_sz);
+
+       return 0;
+
+failed_map_iomuxc:
+       iounmap(suspend_info->m4if_base);
+
+failed_map_m4if:
+       return ret;
+}
+
 static int __init imx5_pm_common_init(const struct imx5_pm_data *data)
 {
        int ret;
        if (ret)
                pr_warn("%s: cpuidle init failed %d\n", __func__, ret);
 
+       ret = imx5_suspend_init(data);
+       if (ret)
+               pr_warn("%s: No DDR LPM support with suspend %d!\n",
+                       __func__, ret);
+
        suspend_set_ops(&mx5_suspend_ops);
 
        return 0;
 
--- /dev/null
+/*
+ * Copyright (C) 2008-2011 Freescale Semiconductor, Inc.
+ */
+/*
+ * 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/linkage.h>
+
+#define M4IF_MCR0_OFFSET                       (0x008C)
+#define M4IF_MCR0_FDVFS                                (0x1 << 11)
+#define M4IF_MCR0_FDVACK                       (0x1 << 27)
+
+       .align 3
+
+/*
+ * ==================== low level suspend ====================
+ *
+ * On entry
+ * r0: pm_info structure address;
+ *
+ * suspend ocram space layout:
+ * ======================== high address ======================
+ *                              .
+ *                              .
+ *                              .
+ *                              ^
+ *                              ^
+ *                              ^
+ *                      imx53_suspend code
+ *              PM_INFO structure(imx53_suspend_info)
+ * ======================== low address =======================
+ */
+
+/* Offsets of members of struct imx53_suspend_info */
+#define SUSPEND_INFO_MX53_M4IF_V_OFFSET                0x0
+#define SUSPEND_INFO_MX53_IOMUXC_V_OFFSET      0x4
+#define SUSPEND_INFO_MX53_IO_COUNT_OFFSET      0x8
+#define SUSPEND_INFO_MX53_IO_STATE_OFFSET      0xc
+
+ENTRY(imx53_suspend)
+       stmfd   sp!, {r4,r5,r6,r7}
+
+       /* Save pad config */
+       ldr     r1, [r0, #SUSPEND_INFO_MX53_IO_COUNT_OFFSET]
+       cmp     r1, #0
+       beq     skip_pad_conf_1
+
+       add     r2, r0, #SUSPEND_INFO_MX53_IO_STATE_OFFSET
+       ldr     r3, [r0, #SUSPEND_INFO_MX53_IOMUXC_V_OFFSET]
+
+1:
+       ldr     r5, [r2], #12   /* IOMUXC register offset */
+       ldr     r6, [r3, r5]    /* current value */
+       str     r6, [r2], #4    /* save area */
+       subs    r1, r1, #1
+       bne     1b
+
+skip_pad_conf_1:
+       /* Set FDVFS bit of M4IF_MCR0 to request DDR to enter self-refresh */
+       ldr     r1, [r0, #SUSPEND_INFO_MX53_M4IF_V_OFFSET]
+       ldr     r2,[r1, #M4IF_MCR0_OFFSET]
+       orr     r2, r2, #M4IF_MCR0_FDVFS
+       str     r2,[r1, #M4IF_MCR0_OFFSET]
+
+       /* Poll FDVACK bit of M4IF_MCR to wait for DDR to enter self-refresh */
+wait_sr_ack:
+       ldr     r2,[r1, #M4IF_MCR0_OFFSET]
+       ands    r2, r2, #M4IF_MCR0_FDVACK
+       beq     wait_sr_ack
+
+       /* Set pad config */
+       ldr     r1, [r0, #SUSPEND_INFO_MX53_IO_COUNT_OFFSET]
+       cmp     r1, #0
+       beq     skip_pad_conf_2
+
+       add     r2, r0, #SUSPEND_INFO_MX53_IO_STATE_OFFSET
+       ldr     r3, [r0, #SUSPEND_INFO_MX53_IOMUXC_V_OFFSET]
+
+2:
+       ldr     r5, [r2], #4    /* IOMUXC register offset */
+       ldr     r6, [r2], #4    /* clear */
+       ldr     r7, [r3, r5]
+       bic     r7, r7, r6
+       ldr     r6, [r2], #8    /* set */
+       orr     r7, r7, r6
+       str     r7, [r3, r5]
+       subs    r1, r1, #1
+       bne     2b
+
+skip_pad_conf_2:
+       /* Zzz, enter stop mode */
+       wfi
+       nop
+       nop
+       nop
+       nop
+
+       /* Restore pad config */
+       ldr     r1, [r0, #SUSPEND_INFO_MX53_IO_COUNT_OFFSET]
+       cmp     r1, #0
+       beq     skip_pad_conf_3
+
+       add     r2, r0, #SUSPEND_INFO_MX53_IO_STATE_OFFSET
+       ldr     r3, [r0, #SUSPEND_INFO_MX53_IOMUXC_V_OFFSET]
+
+3:
+       ldr     r5, [r2], #12   /* IOMUXC register offset */
+       ldr     r6, [r2], #4    /* saved value */
+       str     r6, [r3, r5]
+       subs    r1, r1, #1
+       bne     3b
+
+skip_pad_conf_3:
+       /* Clear FDVFS bit of M4IF_MCR0 to request DDR to exit self-refresh */
+       ldr     r1, [r0, #SUSPEND_INFO_MX53_M4IF_V_OFFSET]
+       ldr     r2,[r1, #M4IF_MCR0_OFFSET]
+       bic     r2, r2, #M4IF_MCR0_FDVFS
+       str     r2,[r1, #M4IF_MCR0_OFFSET]
+
+       /* Poll FDVACK bit of M4IF_MCR to wait for DDR to exit self-refresh */
+wait_ar_ack:
+       ldr     r2,[r1, #M4IF_MCR0_OFFSET]
+       ands    r2, r2, #M4IF_MCR0_FDVACK
+       bne     wait_ar_ack
+
+       /* Restore registers */
+       ldmfd   sp!, {r4,r5,r6,r7}
+       mov     pc, lr
+
+ENDPROC(imx53_suspend)
+
+ENTRY(imx53_suspend_sz)
+        .word   . - imx53_suspend