#include <linux/pm.h>
 #include <linux/platform_device.h>
 #include <linux/irq.h>
+#include <linux/io.h>
 
 #include <asm/hardware.h>
 #include <asm/arch/pxa3xx-regs.h>
        PXA3xx_CKEN("MMCCLK", MMC3, 19500000, 0, &pxa3xx_device_mci3.dev),
 };
 
+#ifdef CONFIG_PM
+#define SLEEP_SAVE_SIZE        4
+
+#define ISRAM_START    0x5c000000
+#define ISRAM_SIZE     SZ_256K
+
+static void __iomem *sram;
+static unsigned long wakeup_src;
+
+static void pxa3xx_cpu_pm_save(unsigned long *sleep_save)
+{
+       pr_debug("PM: CKENA=%08x CKENB=%08x\n", CKENA, CKENB);
+
+       if (CKENA & (1 << CKEN_USBH)) {
+               printk(KERN_ERR "PM: USB host clock not stopped?\n");
+               CKENA &= ~(1 << CKEN_USBH);
+       }
+//     CKENA |= 1 << (CKEN_ISC & 31);
+
+       /*
+        * Low power modes require the HSIO2 clock to be enabled.
+        */
+       CKENB |= 1 << (CKEN_HSIO2 & 31);
+}
+
+static void pxa3xx_cpu_pm_restore(unsigned long *sleep_save)
+{
+       CKENB &= ~(1 << (CKEN_HSIO2 & 31));
+}
+
+/*
+ * Enter a standby mode (S0D1C2 or S0D2C2).  Upon wakeup, the dynamic
+ * memory controller has to be reinitialised, so we place some code
+ * in the SRAM to perform this function.
+ *
+ * We disable FIQs across the standby - otherwise, we might receive a
+ * FIQ while the SDRAM is unavailable.
+ */
+static void pxa3xx_cpu_standby(unsigned int pwrmode)
+{
+       extern const char pm_enter_standby_start[], pm_enter_standby_end[];
+       void (*fn)(unsigned int) = (void __force *)(sram + 0x8000);
+
+       memcpy_toio(sram + 0x8000, pm_enter_standby_start,
+                   pm_enter_standby_end - pm_enter_standby_start);
+
+       AD2D0SR = ~0;
+       AD2D1SR = ~0;
+       AD2D0ER = wakeup_src;
+       AD2D1ER = 0;
+       ASCR = ASCR;
+       ARSR = ARSR;
+
+       local_fiq_disable();
+       fn(pwrmode);
+       local_fiq_enable();
+
+       AD2D0ER = 0;
+       AD2D1ER = 0;
+
+       printk("PM: AD2D0SR=%08x ASCR=%08x\n", AD2D0SR, ASCR);
+}
+
+static void pxa3xx_cpu_pm_enter(suspend_state_t state)
+{
+       /*
+        * Don't sleep if no wakeup sources are defined
+        */
+       if (wakeup_src == 0)
+               return;
+
+       switch (state) {
+       case PM_SUSPEND_STANDBY:
+               pxa3xx_cpu_standby(PXA3xx_PM_S0D2C2);
+               break;
+
+       case PM_SUSPEND_MEM:
+               break;
+       }
+}
+
+static int pxa3xx_cpu_pm_valid(suspend_state_t state)
+{
+       return state == PM_SUSPEND_MEM || state == PM_SUSPEND_STANDBY;
+}
+
+static struct pxa_cpu_pm_fns pxa3xx_cpu_pm_fns = {
+       .save_size      = SLEEP_SAVE_SIZE,
+       .save           = pxa3xx_cpu_pm_save,
+       .restore        = pxa3xx_cpu_pm_restore,
+       .valid          = pxa3xx_cpu_pm_valid,
+       .enter          = pxa3xx_cpu_pm_enter,
+};
+
+static void __init pxa3xx_init_pm(void)
+{
+       sram = ioremap(ISRAM_START, ISRAM_SIZE);
+       if (!sram) {
+               printk(KERN_ERR "Unable to map ISRAM: disabling standby/suspend\n");
+               return;
+       }
+
+       /*
+        * Since we copy wakeup code into the SRAM, we need to ensure
+        * that it is preserved over the low power modes.  Note: bit 8
+        * is undocumented in the developer manual, but must be set.
+        */
+       AD1R |= ADXR_L2 | ADXR_R0;
+       AD2R |= ADXR_L2 | ADXR_R0;
+       AD3R |= ADXR_L2 | ADXR_R0;
+
+       /*
+        * Clear the resume enable registers.
+        */
+       AD1D0ER = 0;
+       AD2D0ER = 0;
+       AD2D1ER = 0;
+       AD3ER = 0;
+
+       pxa_cpu_pm_fns = &pxa3xx_cpu_pm_fns;
+}
+
+static int pxa3xx_set_wake(unsigned int irq, unsigned int on)
+{
+       unsigned long flags, mask = 0;
+
+       switch (irq) {
+       case IRQ_SSP3:
+               mask = ADXER_MFP_WSSP3;
+               break;
+       case IRQ_MSL:
+               mask = ADXER_WMSL0;
+               break;
+       case IRQ_USBH2:
+       case IRQ_USBH1:
+               mask = ADXER_WUSBH;
+               break;
+       case IRQ_KEYPAD:
+               mask = ADXER_WKP;
+               break;
+       case IRQ_AC97:
+               mask = ADXER_MFP_WAC97;
+               break;
+       case IRQ_USIM:
+               mask = ADXER_WUSIM0;
+               break;
+       case IRQ_SSP2:
+               mask = ADXER_MFP_WSSP2;
+               break;
+       case IRQ_I2C:
+               mask = ADXER_MFP_WI2C;
+               break;
+       case IRQ_STUART:
+               mask = ADXER_MFP_WUART3;
+               break;
+       case IRQ_BTUART:
+               mask = ADXER_MFP_WUART2;
+               break;
+       case IRQ_FFUART:
+               mask = ADXER_MFP_WUART1;
+               break;
+       case IRQ_MMC:
+               mask = ADXER_MFP_WMMC1;
+               break;
+       case IRQ_SSP:
+               mask = ADXER_MFP_WSSP1;
+               break;
+       case IRQ_RTCAlrm:
+               mask = ADXER_WRTC;
+               break;
+       case IRQ_SSP4:
+               mask = ADXER_MFP_WSSP4;
+               break;
+       case IRQ_TSI:
+               mask = ADXER_WTSI;
+               break;
+       case IRQ_USIM2:
+               mask = ADXER_WUSIM1;
+               break;
+       case IRQ_MMC2:
+               mask = ADXER_MFP_WMMC2;
+               break;
+       case IRQ_NAND:
+               mask = ADXER_MFP_WFLASH;
+               break;
+       case IRQ_USB2:
+               mask = ADXER_WUSB2;
+               break;
+       case IRQ_WAKEUP0:
+               mask = ADXER_WEXTWAKE0;
+               break;
+       case IRQ_WAKEUP1:
+               mask = ADXER_WEXTWAKE1;
+               break;
+       case IRQ_MMC3:
+               mask = ADXER_MFP_GEN12;
+               break;
+       }
+
+       local_irq_save(flags);
+       if (on)
+               wakeup_src |= mask;
+       else
+               wakeup_src &= ~mask;
+       local_irq_restore(flags);
+
+       return 0;
+}
+
+static void pxa3xx_init_irq_pm(void)
+{
+       pxa_init_irq_set_wake(pxa3xx_set_wake);
+}
+
+#else
+static inline void pxa3xx_init_pm(void) {}
+static inline void pxa3xx_init_irq_pm(void) {}
+#endif
+
 void __init pxa3xx_init_irq(void)
 {
        /* enable CP6 access */
        pxa_init_irq_low();
        pxa_init_irq_high();
        pxa_init_irq_gpio(128);
+       pxa3xx_init_irq_pm();
 }
 
 /*
                if ((ret = pxa_init_dma(32)))
                        return ret;
 
+               pxa3xx_init_pm();
+
                return platform_add_devices(devices, ARRAY_SIZE(devices));
        }
        return 0;
 
        mov     pc, lr
 
 #endif
+
+#ifdef CONFIG_PXA3xx
+
+#define MDCNFG         0x0000
+#define MDCNFG_DMCEN   (1 << 30)
+#define DDR_HCAL       0x0060
+#define DDR_HCAL_HCRNG 0x1f
+#define DDR_HCAL_HCPROG        (1 << 28)
+#define DDR_HCAL_HCEN  (1 << 31)
+#define DMCIER         0x0070
+#define DMCIER_EDLP    (1 << 29)
+#define DMCISR         0x0078
+#define RCOMP          0x0100
+#define RCOMP_SWEVAL   (1 << 31)
+
+ENTRY(pm_enter_standby_start)
+       mov     r1, #0xf6000000         @ DMEMC_REG_BASE (MDCNFG)
+       add     r1, r1, #0x00100000
+
+       /*
+        * Preload the TLB entry for accessing the dynamic memory
+        * controller registers.  Note that page table lookups will
+        * fail until the dynamic memory controller has been
+        * reinitialised - and that includes MMU page table walks.
+        * This also means that only the dynamic memory controller
+        * can be reliably accessed in the code following standby.
+        */
+       ldr     r2, [r1]                @ Dummy read MDCNFG
+
+       mcr     p14, 0, r0, c7, c0, 0
+       .rept   8
+       nop
+       .endr
+
+       ldr     r0, [r1, #DDR_HCAL]     @ Clear (and wait for) HCEN
+       bic     r0, r0, #DDR_HCAL_HCEN
+       str     r0, [r1, #DDR_HCAL]
+1:     ldr     r0, [r1, #DDR_HCAL]
+       tst     r0, #DDR_HCAL_HCEN
+       bne     1b
+
+       ldr     r0, [r1, #RCOMP]        @ Initiate RCOMP
+       orr     r0, r0, #RCOMP_SWEVAL
+       str     r0, [r1, #RCOMP]
+
+       mov     r0, #~0                 @ Clear interrupts
+       str     r0, [r1, #DMCISR]
+
+       ldr     r0, [r1, #DMCIER]       @ set DMIER[EDLP]
+       orr     r0, r0, #DMCIER_EDLP
+       str     r0, [r1, #DMCIER]
+
+       ldr     r0, [r1, #DDR_HCAL]     @ clear HCRNG, set HCPROG, HCEN
+       bic     r0, r0, #DDR_HCAL_HCRNG
+       orr     r0, r0, #DDR_HCAL_HCEN | DDR_HCAL_HCPROG
+       str     r0, [r1, #DDR_HCAL]
+
+1:     ldr     r0, [r1, #DMCISR]
+       tst     r0, #DMCIER_EDLP
+       beq     1b
+
+       ldr     r0, [r1, #MDCNFG]       @ set MDCNFG[DMCEN]
+       orr     r0, r0, #MDCNFG_DMCEN
+       str     r0, [r1, #MDCNFG]
+1:     ldr     r0, [r1, #MDCNFG]
+       tst     r0, #MDCNFG_DMCEN
+       beq     1b
+
+       ldr     r0, [r1, #DDR_HCAL]     @ set DDR_HCAL[HCRNG]
+       orr     r0, r0, #2 @ HCRNG
+       str     r0, [r1, #DDR_HCAL]
+
+       ldr     r0, [r1, #DMCIER]       @ Clear the interrupt
+       bic     r0, r0, #0x20000000
+       str     r0, [r1, #DMCIER]
+
+       mov     pc, lr
+ENTRY(pm_enter_standby_end)
+
+#endif
 
 #ifndef __ASM_ARCH_PXA3XX_REGS_H
 #define __ASM_ARCH_PXA3XX_REGS_H
 
+/*
+ * Slave Power Managment Unit
+ */
+#define ASCR           __REG(0x40f40000)       /* Application Subsystem Power Status/Configuration */
+#define ARSR           __REG(0x40f40004)       /* Application Subsystem Reset Status */
+#define AD3ER          __REG(0x40f40008)       /* Application Subsystem Wake-Up from D3 Enable */
+#define AD3SR          __REG(0x40f4000c)       /* Application Subsystem Wake-Up from D3 Status */
+#define AD2D0ER                __REG(0x40f40010)       /* Application Subsystem Wake-Up from D2 to D0 Enable */
+#define AD2D0SR                __REG(0x40f40014)       /* Application Subsystem Wake-Up from D2 to D0 Status */
+#define AD2D1ER                __REG(0x40f40018)       /* Application Subsystem Wake-Up from D2 to D1 Enable */
+#define AD2D1SR                __REG(0x40f4001c)       /* Application Subsystem Wake-Up from D2 to D1 Status */
+#define AD1D0ER                __REG(0x40f40020)       /* Application Subsystem Wake-Up from D1 to D0 Enable */
+#define AD1D0SR                __REG(0x40f40024)       /* Application Subsystem Wake-Up from D1 to D0 Status */
+#define AGENP          __REG(0x40f4002c)       /* Application Subsystem General Purpose */
+#define AD3R           __REG(0x40f40030)       /* Application Subsystem D3 Configuration */
+#define AD2R           __REG(0x40f40034)       /* Application Subsystem D2 Configuration */
+#define AD1R           __REG(0x40f40038)       /* Application Subsystem D1 Configuration */
+
+/*
+ * Application Subsystem Configuration bits.
+ */
+#define ASCR_RDH               (1 << 31)
+#define ASCR_D1S               (1 << 2)
+#define ASCR_D2S               (1 << 1)
+#define ASCR_D3S               (1 << 0)
+
+/*
+ * Application Reset Status bits.
+ */
+#define ARSR_GPR               (1 << 3)
+#define ARSR_LPMR              (1 << 2)
+#define ARSR_WDT               (1 << 1)
+#define ARSR_HWR               (1 << 0)
+
+/*
+ * Application Subsystem Wake-Up bits.
+ */
+#define ADXER_WRTC             (1 << 31)       /* RTC */
+#define ADXER_WOST             (1 << 30)       /* OS Timer */
+#define ADXER_WTSI             (1 << 29)       /* Touchscreen */
+#define ADXER_WUSBH            (1 << 28)       /* USB host */
+#define ADXER_WUSB2            (1 << 26)       /* USB client 2.0 */
+#define ADXER_WMSL0            (1 << 24)       /* MSL port 0*/
+#define ADXER_WDMUX3           (1 << 23)       /* USB EDMUX3 */
+#define ADXER_WDMUX2           (1 << 22)       /* USB EDMUX2 */
+#define ADXER_WKP              (1 << 21)       /* Keypad */
+#define ADXER_WUSIM1           (1 << 20)       /* USIM Port 1 */
+#define ADXER_WUSIM0           (1 << 19)       /* USIM Port 0 */
+#define ADXER_WOTG             (1 << 16)       /* USBOTG input */
+#define ADXER_MFP_WFLASH       (1 << 15)       /* MFP: Data flash busy */
+#define ADXER_MFP_GEN12                (1 << 14)       /* MFP: MMC3/GPIO/OST inputs */
+#define ADXER_MFP_WMMC2                (1 << 13)       /* MFP: MMC2 */
+#define ADXER_MFP_WMMC1                (1 << 12)       /* MFP: MMC1 */
+#define ADXER_MFP_WI2C         (1 << 11)       /* MFP: I2C */
+#define ADXER_MFP_WSSP4                (1 << 10)       /* MFP: SSP4 */
+#define ADXER_MFP_WSSP3                (1 << 9)        /* MFP: SSP3 */
+#define ADXER_MFP_WMAXTRIX     (1 << 8)        /* MFP: matrix keypad */
+#define ADXER_MFP_WUART3       (1 << 7)        /* MFP: UART3 */
+#define ADXER_MFP_WUART2       (1 << 6)        /* MFP: UART2 */
+#define ADXER_MFP_WUART1       (1 << 5)        /* MFP: UART1 */
+#define ADXER_MFP_WSSP2                (1 << 4)        /* MFP: SSP2 */
+#define ADXER_MFP_WSSP1                (1 << 3)        /* MFP: SSP1 */
+#define ADXER_MFP_WAC97                (1 << 2)        /* MFP: AC97 */
+#define ADXER_WEXTWAKE1                (1 << 1)        /* External Wake 1 */
+#define ADXER_WEXTWAKE0                (1 << 0)        /* External Wake 0 */
+
+/*
+ * AD3R/AD2R/AD1R bits.  R2-R5 are only defined for PXA320.
+ */
+#define ADXR_L2                        (1 << 8)
+#define ADXR_R5                        (1 << 5)
+#define ADXR_R4                        (1 << 4)
+#define ADXR_R3                        (1 << 3)
+#define ADXR_R2                        (1 << 2)
+#define ADXR_R1                        (1 << 1)
+#define ADXR_R0                        (1 << 0)
+
+/*
+ * Values for PWRMODE CP15 register
+ */
+#define PXA3xx_PM_S3D4C4       0x07    /* aka deep sleep */
+#define PXA3xx_PM_S2D3C4       0x06    /* aka sleep */
+#define PXA3xx_PM_S0D2C2       0x03    /* aka standby */
+#define PXA3xx_PM_S0D1C2       0x02    /* aka LCD refresh */
+#define PXA3xx_PM_S0D0C1       0x01
+
 /*
  * Application Subsystem Clock
  */