[ARM] pxa: add preliminary suspend/resume code for pxa3xx

1. clear RDH bit after resuming back from D3, otherwise, the multi function
   pins will retain the low power state

2. save/restore essential system registers

Signed-off-by: eric miao <eric.miao@marvell.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
diff --git a/arch/arm/mach-pxa/pxa3xx.c b/arch/arm/mach-pxa/pxa3xx.c
index d0b2afd4..e47e67c 100644
--- a/arch/arm/mach-pxa/pxa3xx.c
+++ b/arch/arm/mach-pxa/pxa3xx.c
@@ -40,6 +40,7 @@
 #define RO_CLK		60000000
 
 #define ACCR_D0CS	(1 << 26)
+#define ACCR_PCCE	(1 << 11)
 
 /* crystal frequency to static memory controller multiplier (SMCFS) */
 static unsigned char smcfs_mult[8] = { 6, 0, 8, 0, 0, 16, };
@@ -204,7 +205,6 @@
 };
 
 #ifdef CONFIG_PM
-#define SLEEP_SAVE_SIZE	4
 
 #define ISRAM_START	0x5c000000
 #define ISRAM_SIZE	SZ_256K
@@ -212,25 +212,29 @@
 static void __iomem *sram;
 static unsigned long wakeup_src;
 
+#define SAVE(x)		sleep_save[SLEEP_SAVE_##x] = x
+#define RESTORE(x)	x = sleep_save[SLEEP_SAVE_##x]
+
+enum {	SLEEP_SAVE_START = 0,
+	SLEEP_SAVE_CKENA,
+	SLEEP_SAVE_CKENB,
+	SLEEP_SAVE_ACCR,
+
+	SLEEP_SAVE_SIZE,
+};
+
 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);
+	SAVE(CKENA);
+	SAVE(CKENB);
+	SAVE(ACCR);
 }
 
 static void pxa3xx_cpu_pm_restore(unsigned long *sleep_save)
 {
-	CKENB &= ~(1 << (CKEN_HSIO2 & 31));
+	RESTORE(ACCR);
+	RESTORE(CKENA);
+	RESTORE(CKENB);
 }
 
 /*
@@ -266,6 +270,46 @@
 	printk("PM: AD2D0SR=%08x ASCR=%08x\n", AD2D0SR, ASCR);
 }
 
+/*
+ * NOTE:  currently, the OBM (OEM Boot Module) binary comes along with
+ * PXA3xx development kits assumes that the resuming process continues
+ * with the address stored within the first 4 bytes of SDRAM. The PSPR
+ * register is used privately by BootROM and OBM, and _must_ be set to
+ * 0x5c014000 for the moment.
+ */
+static void pxa3xx_cpu_pm_suspend(void)
+{
+	volatile unsigned long *p = (volatile void *)0xc0000000;
+	unsigned long saved_data = *p;
+
+	extern void pxa3xx_cpu_suspend(void);
+	extern void pxa3xx_cpu_resume(void);
+
+	/* resuming from D2 requires the HSIO2/BOOT/TPM clocks enabled */
+	CKENA |= (1 << CKEN_BOOT) | (1 << CKEN_TPM);
+	CKENB |= 1 << (CKEN_HSIO2 & 0x1f);
+
+	/* clear and setup wakeup source */
+	AD3SR = ~0;
+	AD3ER = wakeup_src;
+	ASCR = ASCR;
+	ARSR = ARSR;
+
+	PCFR |= (1u << 13);			/* L1_DIS */
+	PCFR &= ~((1u << 12) | (1u << 1));	/* L0_EN | SL_ROD */
+
+	PSPR = 0x5c014000;
+
+	/* overwrite with the resume address */
+	*p = virt_to_phys(pxa3xx_cpu_resume);
+
+	pxa3xx_cpu_suspend();
+
+	*p = saved_data;
+
+	AD3ER = 0;
+}
+
 static void pxa3xx_cpu_pm_enter(suspend_state_t state)
 {
 	/*
@@ -280,6 +324,7 @@
 		break;
 
 	case PM_SUSPEND_MEM:
+		pxa3xx_cpu_pm_suspend();
 		break;
 	}
 }