Browse Source

Merge branch 'next-s3c-pm' of git://aeryn.fluff.org.uk/bjdooks/linux into devel

Russell King 16 years ago
parent
commit
f5f0e17a84

+ 4 - 4
Documentation/arm/Samsung-S3C24XX/Suspend.txt

@@ -40,13 +40,13 @@ Resuming
 Machine Support
 Machine Support
 ---------------
 ---------------
 
 
-  The machine specific functions must call the s3c2410_pm_init() function
+  The machine specific functions must call the s3c_pm_init() function
   to say that its bootloader is capable of resuming. This can be as
   to say that its bootloader is capable of resuming. This can be as
   simple as adding the following to the machine's definition:
   simple as adding the following to the machine's definition:
 
 
-  INITMACHINE(s3c2410_pm_init)
+  INITMACHINE(s3c_pm_init)
 
 
-  A board can do its own setup before calling s3c2410_pm_init, if it
+  A board can do its own setup before calling s3c_pm_init, if it
   needs to setup anything else for power management support.
   needs to setup anything else for power management support.
 
 
   There is currently no support for over-riding the default method of
   There is currently no support for over-riding the default method of
@@ -74,7 +74,7 @@ statuc void __init machine_init(void)
 
 
 	enable_irq_wake(IRQ_EINT0);
 	enable_irq_wake(IRQ_EINT0);
 
 
-	s3c2410_pm_init();
+	s3c_pm_init();
 }
 }
 
 
 
 

+ 23 - 0
arch/arm/mach-s3c2410/include/mach/gpio-nrs.h

@@ -0,0 +1,23 @@
+/* arch/arm/mach-s3c2410/include/mach/gpio-nrs.h
+ *
+ * Copyright (c) 2008 Simtec Electronics
+ *	http://armlinux.simtec.co.uk/
+ *	Ben Dooks <ben@simtec.co.uk>
+ *
+ * S3C2410 - GPIO bank numbering
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#define S3C2410_GPIONO(bank,offset) ((bank) + (offset))
+
+#define S3C2410_GPIO_BANKA   (32*0)
+#define S3C2410_GPIO_BANKB   (32*1)
+#define S3C2410_GPIO_BANKC   (32*2)
+#define S3C2410_GPIO_BANKD   (32*3)
+#define S3C2410_GPIO_BANKE   (32*4)
+#define S3C2410_GPIO_BANKF   (32*5)
+#define S3C2410_GPIO_BANKG   (32*6)
+#define S3C2410_GPIO_BANKH   (32*7)

+ 3 - 0
arch/arm/mach-s3c2410/include/mach/gpio.h

@@ -23,3 +23,6 @@
 #define ARCH_NR_GPIOS	(256 + CONFIG_S3C24XX_GPIO_EXTRA)
 #define ARCH_NR_GPIOS	(256 + CONFIG_S3C24XX_GPIO_EXTRA)
 
 
 #include <asm-generic/gpio.h>
 #include <asm-generic/gpio.h>
+#include <mach/gpio-nrs.h>
+
+#define S3C_GPIO_END	(S3C2410_GPIO_BANKH + 32)

+ 1 - 1
arch/arm/mach-s3c2410/include/mach/irqs.h

@@ -80,7 +80,7 @@
 #define IRQ_EINT22     S3C2410_IRQ(50)
 #define IRQ_EINT22     S3C2410_IRQ(50)
 #define IRQ_EINT23     S3C2410_IRQ(51)
 #define IRQ_EINT23     S3C2410_IRQ(51)
 
 
-
+#define IRQ_EINT_BIT(x)	((x) - IRQ_EINT4 + 4)
 #define IRQ_EINT(x)    (((x) >= 4) ? (IRQ_EINT4 + (x) - 4) : (IRQ_EINT0 + (x)))
 #define IRQ_EINT(x)    (((x) >= 4) ? (IRQ_EINT4 + (x) - 4) : (IRQ_EINT0 + (x)))
 
 
 #define IRQ_LCD_FIFO   S3C2410_IRQ(52)
 #define IRQ_LCD_FIFO   S3C2410_IRQ(52)

+ 1 - 10
arch/arm/mach-s3c2410/include/mach/regs-gpio.h

@@ -14,16 +14,7 @@
 #ifndef __ASM_ARCH_REGS_GPIO_H
 #ifndef __ASM_ARCH_REGS_GPIO_H
 #define __ASM_ARCH_REGS_GPIO_H
 #define __ASM_ARCH_REGS_GPIO_H
 
 
-#define S3C2410_GPIONO(bank,offset) ((bank) + (offset))
-
-#define S3C2410_GPIO_BANKA   (32*0)
-#define S3C2410_GPIO_BANKB   (32*1)
-#define S3C2410_GPIO_BANKC   (32*2)
-#define S3C2410_GPIO_BANKD   (32*3)
-#define S3C2410_GPIO_BANKE   (32*4)
-#define S3C2410_GPIO_BANKF   (32*5)
-#define S3C2410_GPIO_BANKG   (32*6)
-#define S3C2410_GPIO_BANKH   (32*7)
+#include <mach/gpio-nrs.h>
 
 
 #ifdef CONFIG_CPU_S3C2400
 #ifdef CONFIG_CPU_S3C2400
 #define S3C24XX_GPIO_BASE(x)  S3C2400_GPIO_BASE(x)
 #define S3C24XX_GPIO_BASE(x)  S3C2400_GPIO_BASE(x)

+ 1 - 1
arch/arm/mach-s3c2410/mach-h1940.c

@@ -203,7 +203,7 @@ static void __init h1940_map_io(void)
 #ifdef CONFIG_PM_H1940
 #ifdef CONFIG_PM_H1940
 	memcpy(phys_to_virt(H1940_SUSPEND_RESUMEAT), h1940_pm_return, 1024);
 	memcpy(phys_to_virt(H1940_SUSPEND_RESUMEAT), h1940_pm_return, 1024);
 #endif
 #endif
-	s3c2410_pm_init();
+	s3c_pm_init();
 }
 }
 
 
 static void __init h1940_init_irq(void)
 static void __init h1940_init_irq(void)

+ 1 - 1
arch/arm/mach-s3c2410/mach-qt2410.c

@@ -355,7 +355,7 @@ static void __init qt2410_machine_init(void)
 	s3c2410_gpio_cfgpin(S3C2410_GPB5, S3C2410_GPIO_OUTPUT);
 	s3c2410_gpio_cfgpin(S3C2410_GPB5, S3C2410_GPIO_OUTPUT);
 
 
 	platform_add_devices(qt2410_devices, ARRAY_SIZE(qt2410_devices));
 	platform_add_devices(qt2410_devices, ARRAY_SIZE(qt2410_devices));
-	s3c2410_pm_init();
+	s3c_pm_init();
 }
 }
 
 
 MACHINE_START(QT2410, "QT2410")
 MACHINE_START(QT2410, "QT2410")

+ 3 - 10
arch/arm/mach-s3c2410/pm.c

@@ -37,21 +37,14 @@
 #include <plat/cpu.h>
 #include <plat/cpu.h>
 #include <plat/pm.h>
 #include <plat/pm.h>
 
 
-#ifdef CONFIG_S3C2410_PM_DEBUG
-extern void pm_dbg(const char *fmt, ...);
-#define DBG(fmt...) pm_dbg(fmt)
-#else
-#define DBG(fmt...) printk(KERN_DEBUG fmt)
-#endif
-
 static void s3c2410_pm_prepare(void)
 static void s3c2410_pm_prepare(void)
 {
 {
 	/* ensure at least GSTATUS3 has the resume address */
 	/* ensure at least GSTATUS3 has the resume address */
 
 
-	__raw_writel(virt_to_phys(s3c2410_cpu_resume), S3C2410_GSTATUS3);
+	__raw_writel(virt_to_phys(s3c_cpu_resume), S3C2410_GSTATUS3);
 
 
-	DBG("GSTATUS3 0x%08x\n", __raw_readl(S3C2410_GSTATUS3));
-	DBG("GSTATUS4 0x%08x\n", __raw_readl(S3C2410_GSTATUS4));
+	S3C_PMDBG("GSTATUS3 0x%08x\n", __raw_readl(S3C2410_GSTATUS3));
+	S3C_PMDBG("GSTATUS4 0x%08x\n", __raw_readl(S3C2410_GSTATUS4));
 
 
 	if (machine_is_h1940()) {
 	if (machine_is_h1940()) {
 		void *base = phys_to_virt(H1940_SUSPEND_CHECK);
 		void *base = phys_to_virt(H1940_SUSPEND_CHECK);

+ 2 - 2
arch/arm/mach-s3c2412/mach-jive.c

@@ -494,7 +494,7 @@ static int jive_pm_suspend(struct sys_device *sd, pm_message_t state)
 	 * correct address to resume from. */
 	 * correct address to resume from. */
 
 
 	__raw_writel(0x2BED, S3C2412_INFORM0);
 	__raw_writel(0x2BED, S3C2412_INFORM0);
-	__raw_writel(virt_to_phys(s3c2410_cpu_resume), S3C2412_INFORM1);
+	__raw_writel(virt_to_phys(s3c_cpu_resume), S3C2412_INFORM1);
 
 
 	return 0;
 	return 0;
 }
 }
@@ -630,7 +630,7 @@ static void __init jive_machine_init(void)
 
 
 	/* initialise the power management now we've setup everything. */
 	/* initialise the power management now we've setup everything. */
 
 
-	s3c2410_pm_init();
+	s3c_pm_init();
 
 
 	s3c_device_nand.dev.platform_data = &jive_nand_info;
 	s3c_device_nand.dev.platform_data = &jive_nand_info;
 
 

+ 2 - 2
arch/arm/mach-s3c2412/pm.c

@@ -85,7 +85,7 @@ static struct sleep_save s3c2412_sleep[] = {
 
 
 static int s3c2412_pm_suspend(struct sys_device *dev, pm_message_t state)
 static int s3c2412_pm_suspend(struct sys_device *dev, pm_message_t state)
 {
 {
-	s3c2410_pm_do_save(s3c2412_sleep, ARRAY_SIZE(s3c2412_sleep));
+	s3c_pm_do_save(s3c2412_sleep, ARRAY_SIZE(s3c2412_sleep));
 	return 0;
 	return 0;
 }
 }
 
 
@@ -98,7 +98,7 @@ static int s3c2412_pm_resume(struct sys_device *dev)
 	tmp |=  S3C2412_PWRCFG_STANDBYWFI_IDLE;
 	tmp |=  S3C2412_PWRCFG_STANDBYWFI_IDLE;
 	__raw_writel(tmp, S3C2412_PWRCFG);
 	__raw_writel(tmp, S3C2412_PWRCFG);
 
 
-	s3c2410_pm_do_restore(s3c2412_sleep, ARRAY_SIZE(s3c2412_sleep));
+	s3c_pm_do_restore(s3c2412_sleep, ARRAY_SIZE(s3c2412_sleep));
 	return 0;
 	return 0;
 }
 }
 
 

+ 1 - 1
arch/arm/mach-s3c2440/mach-rx3715.c

@@ -203,7 +203,7 @@ static void __init rx3715_init_machine(void)
 #ifdef CONFIG_PM_H1940
 #ifdef CONFIG_PM_H1940
 	memcpy(phys_to_virt(H1940_SUSPEND_RESUMEAT), h1940_pm_return, 1024);
 	memcpy(phys_to_virt(H1940_SUSPEND_RESUMEAT), h1940_pm_return, 1024);
 #endif
 #endif
-	s3c2410_pm_init();
+	s3c_pm_init();
 
 
 	s3c24xx_fb_set_platdata(&rx3715_fb_info);
 	s3c24xx_fb_set_platdata(&rx3715_fb_info);
 	platform_add_devices(rx3715_devices, ARRAY_SIZE(rx3715_devices));
 	platform_add_devices(rx3715_devices, ARRAY_SIZE(rx3715_devices));

+ 2 - 0
arch/arm/mach-s3c24a0/include/mach/irqs.h

@@ -70,6 +70,8 @@
 #define IRQ_EINT17	S3C2410_IRQ(49)
 #define IRQ_EINT17	S3C2410_IRQ(49)
 #define IRQ_EINT18	S3C2410_IRQ(50)
 #define IRQ_EINT18	S3C2410_IRQ(50)
 
 
+#define IRQ_EINT_BIT(x) ((x) - IRQ_EINT00)
+
 /* SUB IRQS */
 /* SUB IRQS */
 #define IRQ_S3CUART_RX0		S3C2410_IRQ(51)	/* 67 */
 #define IRQ_S3CUART_RX0		S3C2410_IRQ(51)	/* 67 */
 #define IRQ_S3CUART_TX0		S3C2410_IRQ(52)
 #define IRQ_S3CUART_TX0		S3C2410_IRQ(52)

+ 5 - 0
arch/arm/plat-s3c/Makefile

@@ -18,6 +18,11 @@ obj-y				+= pwm-clock.o
 obj-y				+= gpio.o
 obj-y				+= gpio.o
 obj-y				+= gpio-config.o
 obj-y				+= gpio-config.o
 
 
+# PM support
+
+obj-$(CONFIG_PM)		+= pm.o
+obj-$(CONFIG_S3C2410_PM_CHECK)	+= pm-check.o
+
 # devices
 # devices
 
 
 obj-$(CONFIG_S3C_DEV_HSMMC)	+= dev-hsmmc.o
 obj-$(CONFIG_S3C_DEV_HSMMC)	+= dev-hsmmc.o

+ 174 - 0
arch/arm/plat-s3c/include/plat/pm.h

@@ -0,0 +1,174 @@
+/* linux/include/asm-arm/plat-s3c24xx/pm.h
+ *
+ * Copyright (c) 2004 Simtec Electronics
+ *	http://armlinux.simtec.co.uk/
+ *	Written by Ben Dooks, <ben@simtec.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+/* s3c_pm_init
+ *
+ * called from board at initialisation time to setup the power
+ * management
+*/
+
+#ifdef CONFIG_PM
+
+extern __init int s3c_pm_init(void);
+
+#else
+
+static inline int s3c_pm_init(void)
+{
+	return 0;
+}
+#endif
+
+/* configuration for the IRQ mask over sleep */
+extern unsigned long s3c_irqwake_intmask;
+extern unsigned long s3c_irqwake_eintmask;
+
+/* IRQ masks for IRQs allowed to go to sleep (see irq.c) */
+extern unsigned long s3c_irqwake_intallow;
+extern unsigned long s3c_irqwake_eintallow;
+
+/* per-cpu sleep functions */
+
+extern void (*pm_cpu_prep)(void);
+extern void (*pm_cpu_sleep)(void);
+
+/* Flags for PM Control */
+
+extern unsigned long s3c_pm_flags;
+
+/* from sleep.S */
+
+extern int  s3c_cpu_save(unsigned long *saveblk);
+extern void s3c_cpu_resume(void);
+
+extern void s3c2410_cpu_suspend(void);
+
+extern unsigned long s3c_sleep_save_phys;
+
+/* sleep save info */
+
+/**
+ * struct sleep_save - save information for shared peripherals.
+ * @reg: Pointer to the register to save.
+ * @val: Holder for the value saved from reg.
+ *
+ * This describes a list of registers which is used by the pm core and
+ * other subsystem to save and restore register values over suspend.
+ */
+struct sleep_save {
+	void __iomem	*reg;
+	unsigned long	val;
+};
+
+#define SAVE_ITEM(x) \
+	{ .reg = (x) }
+
+/**
+ * struct pm_uart_save - save block for core UART
+ * @ulcon: Save value for S3C2410_ULCON
+ * @ucon: Save value for S3C2410_UCON
+ * @ufcon: Save value for S3C2410_UFCON
+ * @umcon: Save value for S3C2410_UMCON
+ * @ubrdiv: Save value for S3C2410_UBRDIV
+ *
+ * Save block for UART registers to be held over sleep and restored if they
+ * are needed (say by debug).
+*/
+struct pm_uart_save {
+	u32	ulcon;
+	u32	ucon;
+	u32	ufcon;
+	u32	umcon;
+	u32	ubrdiv;
+};
+
+/* helper functions to save/restore lists of registers. */
+
+extern void s3c_pm_do_save(struct sleep_save *ptr, int count);
+extern void s3c_pm_do_restore(struct sleep_save *ptr, int count);
+extern void s3c_pm_do_restore_core(struct sleep_save *ptr, int count);
+
+#ifdef CONFIG_PM
+extern int s3c_irqext_wake(unsigned int irqno, unsigned int state);
+extern int s3c24xx_irq_suspend(struct sys_device *dev, pm_message_t state);
+extern int s3c24xx_irq_resume(struct sys_device *dev);
+#else
+#define s3c_irqext_wake NULL
+#define s3c24xx_irq_suspend NULL
+#define s3c24xx_irq_resume  NULL
+#endif
+
+/* PM debug functions */
+
+#ifdef CONFIG_S3C2410_PM_DEBUG
+/**
+ * s3c_pm_dbg() - low level debug function for use in suspend/resume.
+ * @msg: The message to print.
+ *
+ * This function is used mainly to debug the resume process before the system
+ * can rely on printk/console output. It uses the low-level debugging output
+ * routine printascii() to do its work.
+ */
+extern void s3c_pm_dbg(const char *msg, ...);
+
+#define S3C_PMDBG(fmt...) s3c_pm_dbg(fmt)
+#else
+#define S3C_PMDBG(fmt...) printk(KERN_DEBUG fmt)
+#endif
+
+/* suspend memory checking */
+
+#ifdef CONFIG_S3C2410_PM_CHECK
+extern void s3c_pm_check_prepare(void);
+extern void s3c_pm_check_restore(void);
+extern void s3c_pm_check_cleanup(void);
+extern void s3c_pm_check_store(void);
+#else
+#define s3c_pm_check_prepare() do { } while(0)
+#define s3c_pm_check_restore() do { } while(0)
+#define s3c_pm_check_cleanup() do { } while(0)
+#define s3c_pm_check_store()   do { } while(0)
+#endif
+
+/**
+ * s3c_pm_configure_extint() - ensure pins are correctly set for IRQ
+ *
+ * Setup all the necessary GPIO pins for waking the system on external
+ * interrupt.
+ */
+extern void s3c_pm_configure_extint(void);
+
+/**
+ * s3c_pm_restore_gpios() - restore the state of the gpios after sleep.
+ *
+ * Restore the state of the GPIO pins after sleep, which may involve ensuring
+ * that we do not glitch the state of the pins from that the bootloader's
+ * resume code has done.
+*/
+extern void s3c_pm_restore_gpios(void);
+
+/**
+ * s3c_pm_save_gpios() - save the state of the GPIOs for restoring after sleep.
+ *
+ * Save the GPIO states for resotration on resume. See s3c_pm_restore_gpios().
+ */
+extern void s3c_pm_save_gpios(void);
+
+/**
+ * s3c_pm_cb_flushcache - callback for assembly code
+ *
+ * Callback to issue flush_cache_all() as this call is
+ * not a directly callable object.
+ */
+extern void s3c_pm_cb_flushcache(void);
+
+extern void s3c_pm_save_core(void);
+extern void s3c_pm_restore_core(void);

+ 242 - 0
arch/arm/plat-s3c/pm-check.c

@@ -0,0 +1,242 @@
+/* linux/arch/arm/plat-s3c/pm-check.c
+ *  originally in linux/arch/arm/plat-s3c24xx/pm.c
+ *
+ * Copyright (c) 2004,2006,2008 Simtec Electronics
+ *	http://armlinux.simtec.co.uk
+ *	Ben Dooks <ben@simtec.co.uk>
+ *
+ * S3C Power Mangament - suspend/resume memory corruptiuon check.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <linux/kernel.h>
+#include <linux/suspend.h>
+#include <linux/init.h>
+#include <linux/crc32.h>
+#include <linux/ioport.h>
+
+#include <plat/pm.h>
+
+#if CONFIG_S3C2410_PM_CHECK_CHUNKSIZE < 1
+#error CONFIG_S3C2410_PM_CHECK_CHUNKSIZE must be a positive non-zero value
+#endif
+
+/* suspend checking code...
+ *
+ * this next area does a set of crc checks over all the installed
+ * memory, so the system can verify if the resume was ok.
+ *
+ * CONFIG_S3C2410_PM_CHECK_CHUNKSIZE defines the block-size for the CRC,
+ * increasing it will mean that the area corrupted will be less easy to spot,
+ * and reducing the size will cause the CRC save area to grow
+*/
+
+#define CHECK_CHUNKSIZE (CONFIG_S3C2410_PM_CHECK_CHUNKSIZE * 1024)
+
+static u32 crc_size;	/* size needed for the crc block */
+static u32 *crcs;	/* allocated over suspend/resume */
+
+typedef u32 *(run_fn_t)(struct resource *ptr, u32 *arg);
+
+/* s3c_pm_run_res
+ *
+ * go through the given resource list, and look for system ram
+*/
+
+static void s3c_pm_run_res(struct resource *ptr, run_fn_t fn, u32 *arg)
+{
+	while (ptr != NULL) {
+		if (ptr->child != NULL)
+			s3c_pm_run_res(ptr->child, fn, arg);
+
+		if ((ptr->flags & IORESOURCE_MEM) &&
+		    strcmp(ptr->name, "System RAM") == 0) {
+			S3C_PMDBG("Found system RAM at %08lx..%08lx\n",
+				  (unsigned long)ptr->start,
+				  (unsigned long)ptr->end);
+			arg = (fn)(ptr, arg);
+		}
+
+		ptr = ptr->sibling;
+	}
+}
+
+static void s3c_pm_run_sysram(run_fn_t fn, u32 *arg)
+{
+	s3c_pm_run_res(&iomem_resource, fn, arg);
+}
+
+static u32 *s3c_pm_countram(struct resource *res, u32 *val)
+{
+	u32 size = (u32)(res->end - res->start)+1;
+
+	size += CHECK_CHUNKSIZE-1;
+	size /= CHECK_CHUNKSIZE;
+
+	S3C_PMDBG("Area %08lx..%08lx, %d blocks\n",
+		  (unsigned long)res->start, (unsigned long)res->end, size);
+
+	*val += size * sizeof(u32);
+	return val;
+}
+
+/* s3c_pm_prepare_check
+ *
+ * prepare the necessary information for creating the CRCs. This
+ * must be done before the final save, as it will require memory
+ * allocating, and thus touching bits of the kernel we do not
+ * know about.
+*/
+
+void s3c_pm_check_prepare(void)
+{
+	crc_size = 0;
+
+	s3c_pm_run_sysram(s3c_pm_countram, &crc_size);
+
+	S3C_PMDBG("s3c_pm_prepare_check: %u checks needed\n", crc_size);
+
+	crcs = kmalloc(crc_size+4, GFP_KERNEL);
+	if (crcs == NULL)
+		printk(KERN_ERR "Cannot allocated CRC save area\n");
+}
+
+static u32 *s3c_pm_makecheck(struct resource *res, u32 *val)
+{
+	unsigned long addr, left;
+
+	for (addr = res->start; addr < res->end;
+	     addr += CHECK_CHUNKSIZE) {
+		left = res->end - addr;
+
+		if (left > CHECK_CHUNKSIZE)
+			left = CHECK_CHUNKSIZE;
+
+		*val = crc32_le(~0, phys_to_virt(addr), left);
+		val++;
+	}
+
+	return val;
+}
+
+/* s3c_pm_check_store
+ *
+ * compute the CRC values for the memory blocks before the final
+ * sleep.
+*/
+
+void s3c_pm_check_store(void)
+{
+	if (crcs != NULL)
+		s3c_pm_run_sysram(s3c_pm_makecheck, crcs);
+}
+
+/* in_region
+ *
+ * return TRUE if the area defined by ptr..ptr+size contains the
+ * what..what+whatsz
+*/
+
+static inline int in_region(void *ptr, int size, void *what, size_t whatsz)
+{
+	if ((what+whatsz) < ptr)
+		return 0;
+
+	if (what > (ptr+size))
+		return 0;
+
+	return 1;
+}
+
+/**
+ * s3c_pm_runcheck() - helper to check a resource on restore.
+ * @res: The resource to check
+ * @vak: Pointer to list of CRC32 values to check.
+ *
+ * Called from the s3c_pm_check_restore() via s3c_pm_run_sysram(), this
+ * function runs the given memory resource checking it against the stored
+ * CRC to ensure that memory is restored. The function tries to skip as
+ * many of the areas used during the suspend process.
+ */
+static u32 *s3c_pm_runcheck(struct resource *res, u32 *val)
+{
+	void *save_at = phys_to_virt(s3c_sleep_save_phys);
+	unsigned long addr;
+	unsigned long left;
+	void *stkpage;
+	void *ptr;
+	u32 calc;
+
+	stkpage = (void *)((u32)&calc & ~PAGE_MASK);
+
+	for (addr = res->start; addr < res->end;
+	     addr += CHECK_CHUNKSIZE) {
+		left = res->end - addr;
+
+		if (left > CHECK_CHUNKSIZE)
+			left = CHECK_CHUNKSIZE;
+
+		ptr = phys_to_virt(addr);
+
+		if (in_region(ptr, left, stkpage, 4096)) {
+			S3C_PMDBG("skipping %08lx, has stack in\n", addr);
+			goto skip_check;
+		}
+
+		if (in_region(ptr, left, crcs, crc_size)) {
+			S3C_PMDBG("skipping %08lx, has crc block in\n", addr);
+			goto skip_check;
+		}
+
+		if (in_region(ptr, left, save_at, 32*4 )) {
+			S3C_PMDBG("skipping %08lx, has save block in\n", addr);
+			goto skip_check;
+		}
+
+		/* calculate and check the checksum */
+
+		calc = crc32_le(~0, ptr, left);
+		if (calc != *val) {
+			printk(KERN_ERR "Restore CRC error at "
+			       "%08lx (%08x vs %08x)\n", addr, calc, *val);
+
+			S3C_PMDBG("Restore CRC error at %08lx (%08x vs %08x)\n",
+			    addr, calc, *val);
+		}
+
+	skip_check:
+		val++;
+	}
+
+	return val;
+}
+
+/**
+ * s3c_pm_check_restore() - memory check called on resume
+ *
+ * check the CRCs after the restore event and free the memory used
+ * to hold them
+*/
+void s3c_pm_check_restore(void)
+{
+	if (crcs != NULL)
+		s3c_pm_run_sysram(s3c_pm_runcheck, crcs);
+}
+
+/**
+ * s3c_pm_check_cleanup() - free memory resources
+ *
+ * Free the resources that where allocated by the suspend
+ * memory check code. We do this separately from the
+ * s3c_pm_check_restore() function as we cannot call any
+ * functions that might sleep during that resume.
+ */
+void s3c_pm_check_cleanup(void)
+{
+	kfree(crcs);
+	crcs = NULL;
+}
+

+ 363 - 0
arch/arm/plat-s3c/pm.c

@@ -0,0 +1,363 @@
+/* linux/arch/arm/plat-s3c/pm.c
+ *
+ * Copyright 2008 Openmoko, Inc.
+ * Copyright 2004,2006,2008 Simtec Electronics
+ *	Ben Dooks <ben@simtec.co.uk>
+ *	http://armlinux.simtec.co.uk/
+ *
+ * S3C common power management (suspend to ram) support.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <linux/init.h>
+#include <linux/suspend.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/serial_core.h>
+#include <linux/io.h>
+
+#include <asm/cacheflush.h>
+#include <mach/hardware.h>
+
+#include <plat/regs-serial.h>
+#include <mach/regs-clock.h>
+#include <mach/regs-gpio.h>
+#include <mach/regs-mem.h>
+#include <mach/regs-irq.h>
+#include <asm/irq.h>
+
+#include <plat/pm.h>
+#include <plat/pm-core.h>
+
+/* for external use */
+
+unsigned long s3c_pm_flags;
+
+/* Debug code:
+ *
+ * This code supports debug output to the low level UARTs for use on
+ * resume before the console layer is available.
+*/
+
+#ifdef CONFIG_S3C2410_PM_DEBUG
+extern void printascii(const char *);
+
+void s3c_pm_dbg(const char *fmt, ...)
+{
+	va_list va;
+	char buff[256];
+
+	va_start(va, fmt);
+	vsprintf(buff, fmt, va);
+	va_end(va);
+
+	printascii(buff);
+}
+
+static inline void s3c_pm_debug_init(void)
+{
+	/* restart uart clocks so we can use them to output */
+	s3c_pm_debug_init_uart();
+}
+
+#else
+#define s3c_pm_debug_init() do { } while(0)
+
+#endif /* CONFIG_S3C2410_PM_DEBUG */
+
+/* Save the UART configurations if we are configured for debug. */
+
+#ifdef CONFIG_S3C2410_PM_DEBUG
+
+struct pm_uart_save uart_save[CONFIG_SERIAL_SAMSUNG_UARTS];
+
+static void s3c_pm_save_uart(unsigned int uart, struct pm_uart_save *save)
+{
+	void __iomem *regs = S3C_VA_UARTx(uart);
+
+	save->ulcon = __raw_readl(regs + S3C2410_ULCON);
+	save->ucon = __raw_readl(regs + S3C2410_UCON);
+	save->ufcon = __raw_readl(regs + S3C2410_UFCON);
+	save->umcon = __raw_readl(regs + S3C2410_UMCON);
+	save->ubrdiv = __raw_readl(regs + S3C2410_UBRDIV);
+}
+
+static void s3c_pm_save_uarts(void)
+{
+	struct pm_uart_save *save = uart_save;
+	unsigned int uart;
+
+	for (uart = 0; uart < CONFIG_SERIAL_SAMSUNG_UARTS; uart++, save++)
+		s3c_pm_save_uart(uart, save);
+}
+
+static void s3c_pm_restore_uart(unsigned int uart, struct pm_uart_save *save)
+{
+	void __iomem *regs = S3C_VA_UARTx(uart);
+
+	__raw_writel(save->ulcon, regs + S3C2410_ULCON);
+	__raw_writel(save->ucon,  regs + S3C2410_UCON);
+	__raw_writel(save->ufcon, regs + S3C2410_UFCON);
+	__raw_writel(save->umcon, regs + S3C2410_UMCON);
+	__raw_writel(save->ubrdiv, regs + S3C2410_UBRDIV);
+}
+
+static void s3c_pm_restore_uarts(void)
+{
+	struct pm_uart_save *save = uart_save;
+	unsigned int uart;
+
+	for (uart = 0; uart < CONFIG_SERIAL_SAMSUNG_UARTS; uart++, save++)
+		s3c_pm_restore_uart(uart, save);
+}
+#else
+static void s3c_pm_save_uarts(void) { }
+static void s3c_pm_restore_uarts(void) { }
+#endif
+
+/* The IRQ ext-int code goes here, it is too small to currently bother
+ * with its own file. */
+
+unsigned long s3c_irqwake_intmask	= 0xffffffffL;
+unsigned long s3c_irqwake_eintmask	= 0xffffffffL;
+
+int s3c_irqext_wake(unsigned int irqno, unsigned int state)
+{
+	unsigned long bit = 1L << IRQ_EINT_BIT(irqno);
+
+	if (!(s3c_irqwake_eintallow & bit))
+		return -ENOENT;
+
+	printk(KERN_INFO "wake %s for irq %d\n",
+	       state ? "enabled" : "disabled", irqno);
+
+	if (!state)
+		s3c_irqwake_eintmask |= bit;
+	else
+		s3c_irqwake_eintmask &= ~bit;
+
+	return 0;
+}
+
+/* helper functions to save and restore register state */
+
+/**
+ * s3c_pm_do_save() - save a set of registers for restoration on resume.
+ * @ptr: Pointer to an array of registers.
+ * @count: Size of the ptr array.
+ *
+ * Run through the list of registers given, saving their contents in the
+ * array for later restoration when we wakeup.
+ */
+void s3c_pm_do_save(struct sleep_save *ptr, int count)
+{
+	for (; count > 0; count--, ptr++) {
+		ptr->val = __raw_readl(ptr->reg);
+		S3C_PMDBG("saved %p value %08lx\n", ptr->reg, ptr->val);
+	}
+}
+
+/**
+ * s3c_pm_do_restore() - restore register values from the save list.
+ * @ptr: Pointer to an array of registers.
+ * @count: Size of the ptr array.
+ *
+ * Restore the register values saved from s3c_pm_do_save().
+ *
+ * Note, we do not use S3C_PMDBG() in here, as the system may not have
+ * restore the UARTs state yet
+*/
+
+void s3c_pm_do_restore(struct sleep_save *ptr, int count)
+{
+	for (; count > 0; count--, ptr++) {
+		printk(KERN_DEBUG "restore %p (restore %08lx, was %08x)\n",
+		       ptr->reg, ptr->val, __raw_readl(ptr->reg));
+
+		__raw_writel(ptr->val, ptr->reg);
+	}
+}
+
+/**
+ * s3c_pm_do_restore_core() - early restore register values from save list.
+ *
+ * This is similar to s3c_pm_do_restore() except we try and minimise the
+ * side effects of the function in case registers that hardware might need
+ * to work has been restored.
+ *
+ * WARNING: Do not put any debug in here that may effect memory or use
+ * peripherals, as things may be changing!
+*/
+
+void s3c_pm_do_restore_core(struct sleep_save *ptr, int count)
+{
+	for (; count > 0; count--, ptr++)
+		__raw_writel(ptr->val, ptr->reg);
+}
+
+/* s3c2410_pm_show_resume_irqs
+ *
+ * print any IRQs asserted at resume time (ie, we woke from)
+*/
+static void s3c_pm_show_resume_irqs(int start, unsigned long which,
+				    unsigned long mask)
+{
+	int i;
+
+	which &= ~mask;
+
+	for (i = 0; i <= 31; i++) {
+		if (which & (1L<<i)) {
+			S3C_PMDBG("IRQ %d asserted at resume\n", start+i);
+		}
+	}
+}
+
+
+void (*pm_cpu_prep)(void);
+void (*pm_cpu_sleep)(void);
+
+#define any_allowed(mask, allow) (((mask) & (allow)) != (allow))
+
+/* s3c_pm_enter
+ *
+ * central control for sleep/resume process
+*/
+
+static int s3c_pm_enter(suspend_state_t state)
+{
+	static unsigned long regs_save[16];
+
+	/* ensure the debug is initialised (if enabled) */
+
+	s3c_pm_debug_init();
+
+	S3C_PMDBG("%s(%d)\n", __func__, state);
+
+	if (pm_cpu_prep == NULL || pm_cpu_sleep == NULL) {
+		printk(KERN_ERR "%s: error: no cpu sleep function\n", __func__);
+		return -EINVAL;
+	}
+
+	/* check if we have anything to wake-up with... bad things seem
+	 * to happen if you suspend with no wakeup (system will often
+	 * require a full power-cycle)
+	*/
+
+	if (!any_allowed(s3c_irqwake_intmask, s3c_irqwake_intallow) &&
+	    !any_allowed(s3c_irqwake_eintmask, s3c_irqwake_eintallow)) {
+		printk(KERN_ERR "%s: No wake-up sources!\n", __func__);
+		printk(KERN_ERR "%s: Aborting sleep\n", __func__);
+		return -EINVAL;
+	}
+
+	/* store the physical address of the register recovery block */
+
+	s3c_sleep_save_phys = virt_to_phys(regs_save);
+
+	S3C_PMDBG("s3c_sleep_save_phys=0x%08lx\n", s3c_sleep_save_phys);
+
+	/* save all necessary core registers not covered by the drivers */
+
+	s3c_pm_save_gpios();
+	s3c_pm_save_uarts();
+	s3c_pm_save_core();
+
+	/* set the irq configuration for wake */
+
+	s3c_pm_configure_extint();
+
+	S3C_PMDBG("sleep: irq wakeup masks: %08lx,%08lx\n",
+	    s3c_irqwake_intmask, s3c_irqwake_eintmask);
+
+	s3c_pm_arch_prepare_irqs();
+
+	/* call cpu specific preparation */
+
+	pm_cpu_prep();
+
+	/* flush cache back to ram */
+
+	flush_cache_all();
+
+	s3c_pm_check_store();
+
+	/* send the cpu to sleep... */
+
+	s3c_pm_arch_stop_clocks();
+
+	/* s3c_cpu_save will also act as our return point from when
+	 * we resume as it saves its own register state and restores it
+	 * during the resume.  */
+
+	s3c_cpu_save(regs_save);
+
+	/* restore the cpu state using the kernel's cpu init code. */
+
+	cpu_init();
+
+	/* restore the system state */
+
+	s3c_pm_restore_core();
+	s3c_pm_restore_uarts();
+	s3c_pm_restore_gpios();
+
+	s3c_pm_debug_init();
+
+	/* check what irq (if any) restored the system */
+
+	s3c_pm_arch_show_resume_irqs();
+
+	S3C_PMDBG("%s: post sleep, preparing to return\n", __func__);
+
+	s3c_pm_check_restore();
+
+	/* ok, let's return from sleep */
+
+	S3C_PMDBG("S3C PM Resume (post-restore)\n");
+	return 0;
+}
+
+/* callback from assembly code */
+void s3c_pm_cb_flushcache(void)
+{
+	flush_cache_all();
+}
+
+static int s3c_pm_prepare(void)
+{
+	/* prepare check area if configured */
+
+	s3c_pm_check_prepare();
+	return 0;
+}
+
+static void s3c_pm_finish(void)
+{
+	s3c_pm_check_cleanup();
+}
+
+static struct platform_suspend_ops s3c_pm_ops = {
+	.enter		= s3c_pm_enter,
+	.prepare	= s3c_pm_prepare,
+	.finish		= s3c_pm_finish,
+	.valid		= suspend_valid_only_mem,
+};
+
+/* s3c_pm_init
+ *
+ * Attach the power management functions. This should be called
+ * from the board specific initialisation if the board supports
+ * it.
+*/
+
+int __init s3c_pm_init(void)
+{
+	printk("S3C Power Management, Copyright 2004 Simtec Electronics\n");
+
+	suspend_set_ops(&s3c_pm_ops);
+	return 0;
+}

+ 1 - 0
arch/arm/plat-s3c24xx/Makefile

@@ -27,6 +27,7 @@ obj-$(CONFIG_CPU_S3C244X)	+= s3c244x-irq.o
 obj-$(CONFIG_CPU_S3C244X)	+= s3c244x-clock.o
 obj-$(CONFIG_CPU_S3C244X)	+= s3c244x-clock.o
 obj-$(CONFIG_PM_SIMTEC)		+= pm-simtec.o
 obj-$(CONFIG_PM_SIMTEC)		+= pm-simtec.o
 obj-$(CONFIG_PM)		+= pm.o
 obj-$(CONFIG_PM)		+= pm.o
+obj-$(CONFIG_PM)		+= irq-pm.o
 obj-$(CONFIG_PM)		+= sleep.o
 obj-$(CONFIG_PM)		+= sleep.o
 obj-$(CONFIG_HAVE_PWM)		+= pwm.o
 obj-$(CONFIG_HAVE_PWM)		+= pwm.o
 obj-$(CONFIG_S3C2410_CLOCK)	+= s3c2410-clock.o
 obj-$(CONFIG_S3C2410_CLOCK)	+= s3c2410-clock.o

+ 1 - 1
arch/arm/plat-s3c24xx/common-smdk.c

@@ -201,5 +201,5 @@ void __init smdk_machine_init(void)
 
 
 	platform_add_devices(smdk_devs, ARRAY_SIZE(smdk_devs));
 	platform_add_devices(smdk_devs, ARRAY_SIZE(smdk_devs));
 
 
-	s3c2410_pm_init();
+	s3c_pm_init();
 }
 }

+ 6 - 0
arch/arm/plat-s3c24xx/include/plat/irq.h

@@ -10,6 +10,12 @@
  * published by the Free Software Foundation.
  * published by the Free Software Foundation.
 */
 */
 
 
+#include <linux/io.h>
+
+#include <mach/hardware.h>
+#include <mach/regs-irq.h>
+#include <mach/regs-gpio.h>
+
 #define irqdbf(x...)
 #define irqdbf(x...)
 #define irqdbf2(x...)
 #define irqdbf2(x...)
 
 

+ 2 - 0
arch/arm/plat-s3c24xx/include/plat/map.h

@@ -31,6 +31,8 @@
 #define S3C24XX_SZ_UART	   SZ_1M
 #define S3C24XX_SZ_UART	   SZ_1M
 #define S3C_UART_OFFSET	   (0x4000)
 #define S3C_UART_OFFSET	   (0x4000)
 
 
+#define S3C_VA_UARTx(uart) (S3C_VA_UART + ((uart * S3C_UART_OFFSET)))
+
 /* Timers */
 /* Timers */
 #define S3C24XX_VA_TIMER   S3C_VA_TIMER
 #define S3C24XX_VA_TIMER   S3C_VA_TIMER
 #define S3C2410_PA_TIMER   (0x51000000)
 #define S3C2410_PA_TIMER   (0x51000000)

+ 59 - 0
arch/arm/plat-s3c24xx/include/plat/pm-core.h

@@ -0,0 +1,59 @@
+/* linux/arch/arm/plat-s3c24xx/include/plat/pll.h
+ *
+ * Copyright 2008 Simtec Electronics
+ *      Ben Dooks <ben@simtec.co.uk>
+ *      http://armlinux.simtec.co.uk/
+ *
+ * S3C24xx - PM core support for arch/arm/plat-s3c/pm.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+static inline void s3c_pm_debug_init_uart(void)
+{
+	unsigned long tmp = __raw_readl(S3C2410_CLKCON);
+
+	/* re-start uart clocks */
+	tmp |= S3C2410_CLKCON_UART0;
+	tmp |= S3C2410_CLKCON_UART1;
+	tmp |= S3C2410_CLKCON_UART2;
+
+	__raw_writel(tmp, S3C2410_CLKCON);
+	udelay(10);
+}
+
+static inline void s3c_pm_arch_prepare_irqs(void)
+{
+	__raw_writel(s3c_irqwake_intmask, S3C2410_INTMSK);
+	__raw_writel(s3c_irqwake_eintmask, S3C2410_EINTMASK);
+
+	/* ack any outstanding external interrupts before we go to sleep */
+
+	__raw_writel(__raw_readl(S3C2410_EINTPEND), S3C2410_EINTPEND);
+	__raw_writel(__raw_readl(S3C2410_INTPND), S3C2410_INTPND);
+	__raw_writel(__raw_readl(S3C2410_SRCPND), S3C2410_SRCPND);
+
+}
+
+static inline void s3c_pm_arch_stop_clocks(void)
+{
+	__raw_writel(0x00, S3C2410_CLKCON);  /* turn off clocks over sleep */
+}
+
+static void s3c_pm_show_resume_irqs(int start, unsigned long which,
+				    unsigned long mask);
+
+static inline void s3c_pm_arch_show_resume_irqs(void)
+{
+	S3C_PMDBG("post sleep: IRQs 0x%08x, 0x%08x\n",
+		  __raw_readl(S3C2410_SRCPND),
+		  __raw_readl(S3C2410_EINTPEND));
+
+	s3c_pm_show_resume_irqs(IRQ_EINT0, __raw_readl(S3C2410_SRCPND),
+				s3c_irqwake_intmask);
+
+	s3c_pm_show_resume_irqs(IRQ_EINT4-4, __raw_readl(S3C2410_EINTPEND),
+				s3c_irqwake_eintmask);
+}

+ 0 - 73
arch/arm/plat-s3c24xx/include/plat/pm.h

@@ -1,73 +0,0 @@
-/* linux/include/asm-arm/plat-s3c24xx/pm.h
- *
- * Copyright (c) 2004 Simtec Electronics
- *	Written by Ben Dooks, <ben@simtec.co.uk>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
-*/
-
-/* s3c2410_pm_init
- *
- * called from board at initialisation time to setup the power
- * management
-*/
-
-#ifdef CONFIG_PM
-
-extern __init int s3c2410_pm_init(void);
-
-#else
-
-static inline int s3c2410_pm_init(void)
-{
-	return 0;
-}
-#endif
-
-/* configuration for the IRQ mask over sleep */
-extern unsigned long s3c_irqwake_intmask;
-extern unsigned long s3c_irqwake_eintmask;
-
-/* IRQ masks for IRQs allowed to go to sleep (see irq.c) */
-extern unsigned long s3c_irqwake_intallow;
-extern unsigned long s3c_irqwake_eintallow;
-
-/* per-cpu sleep functions */
-
-extern void (*pm_cpu_prep)(void);
-extern void (*pm_cpu_sleep)(void);
-
-/* Flags for PM Control */
-
-extern unsigned long s3c_pm_flags;
-
-/* from sleep.S */
-
-extern int  s3c2410_cpu_save(unsigned long *saveblk);
-extern void s3c2410_cpu_suspend(void);
-extern void s3c2410_cpu_resume(void);
-
-extern unsigned long s3c2410_sleep_save_phys;
-
-/* sleep save info */
-
-struct sleep_save {
-	void __iomem	*reg;
-	unsigned long	val;
-};
-
-#define SAVE_ITEM(x) \
-	{ .reg = (x) }
-
-extern void s3c2410_pm_do_save(struct sleep_save *ptr, int count);
-extern void s3c2410_pm_do_restore(struct sleep_save *ptr, int count);
-
-#ifdef CONFIG_PM
-extern int s3c24xx_irq_suspend(struct sys_device *dev, pm_message_t state);
-extern int s3c24xx_irq_resume(struct sys_device *dev);
-#else
-#define s3c24xx_irq_suspend NULL
-#define s3c24xx_irq_resume  NULL
-#endif

+ 95 - 0
arch/arm/plat-s3c24xx/irq-pm.c

@@ -0,0 +1,95 @@
+/* linux/arch/arm/plat-s3c24xx/irq-om.c
+ *
+ * Copyright (c) 2003,2004 Simtec Electronics
+ *	Ben Dooks <ben@simtec.co.uk>
+ *	http://armlinux.simtec.co.uk/
+ *
+ * S3C24XX - IRQ PM code
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/sysdev.h>
+
+#include <plat/cpu.h>
+#include <plat/pm.h>
+#include <plat/irq.h>
+
+/* state for IRQs over sleep */
+
+/* default is to allow for EINT0..EINT15, and IRQ_RTC as wakeup sources
+ *
+ * set bit to 1 in allow bitfield to enable the wakeup settings on it
+*/
+
+unsigned long s3c_irqwake_intallow	= 1L << (IRQ_RTC - IRQ_EINT0) | 0xfL;
+unsigned long s3c_irqwake_eintallow	= 0x0000fff0L;
+
+int s3c_irq_wake(unsigned int irqno, unsigned int state)
+{
+	unsigned long irqbit = 1 << (irqno - IRQ_EINT0);
+
+	if (!(s3c_irqwake_intallow & irqbit))
+		return -ENOENT;
+
+	printk(KERN_INFO "wake %s for irq %d\n",
+	       state ? "enabled" : "disabled", irqno);
+
+	if (!state)
+		s3c_irqwake_intmask |= irqbit;
+	else
+		s3c_irqwake_intmask &= ~irqbit;
+
+	return 0;
+}
+
+static struct sleep_save irq_save[] = {
+	SAVE_ITEM(S3C2410_INTMSK),
+	SAVE_ITEM(S3C2410_INTSUBMSK),
+};
+
+/* the extint values move between the s3c2410/s3c2440 and the s3c2412
+ * so we use an array to hold them, and to calculate the address of
+ * the register at run-time
+*/
+
+static unsigned long save_extint[3];
+static unsigned long save_eintflt[4];
+static unsigned long save_eintmask;
+
+int s3c24xx_irq_suspend(struct sys_device *dev, pm_message_t state)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(save_extint); i++)
+		save_extint[i] = __raw_readl(S3C24XX_EXTINT0 + (i*4));
+
+	for (i = 0; i < ARRAY_SIZE(save_eintflt); i++)
+		save_eintflt[i] = __raw_readl(S3C24XX_EINFLT0 + (i*4));
+
+	s3c_pm_do_save(irq_save, ARRAY_SIZE(irq_save));
+	save_eintmask = __raw_readl(S3C24XX_EINTMASK);
+
+	return 0;
+}
+
+int s3c24xx_irq_resume(struct sys_device *dev)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(save_extint); i++)
+		__raw_writel(save_extint[i], S3C24XX_EXTINT0 + (i*4));
+
+	for (i = 0; i < ARRAY_SIZE(save_eintflt); i++)
+		__raw_writel(save_eintflt[i], S3C24XX_EINFLT0 + (i*4));
+
+	s3c_pm_do_restore(irq_save, ARRAY_SIZE(irq_save));
+	__raw_writel(save_eintmask, S3C24XX_EINTMASK);
+
+	return 0;
+}

+ 1 - 151
arch/arm/plat-s3c24xx/irq.c

@@ -1,6 +1,6 @@
 /* linux/arch/arm/plat-s3c24xx/irq.c
 /* linux/arch/arm/plat-s3c24xx/irq.c
  *
  *
- * Copyright (c) 2003,2004 Simtec Electronics
+ * Copyright (c) 2003,2004 Simtec Electronics 
  *	Ben Dooks <ben@simtec.co.uk>
  *	Ben Dooks <ben@simtec.co.uk>
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * This program is free software; you can redistribute it and/or modify
@@ -16,38 +16,6 @@
  * You should have received a copy of the GNU General Public License
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
- * Changelog:
- *
- *   22-Jul-2004  Ben Dooks <ben@simtec.co.uk>
- *                Fixed compile warnings
- *
- *   22-Jul-2004  Roc Wu <cooloney@yahoo.com.cn>
- *                Fixed s3c_extirq_type
- *
- *   21-Jul-2004  Arnaud Patard (Rtp) <arnaud.patard@rtp-net.org>
- *                Addition of ADC/TC demux
- *
- *   04-Oct-2004  Klaus Fetscher <k.fetscher@fetron.de>
- *		  Fix for set_irq_type() on low EINT numbers
- *
- *   05-Oct-2004  Ben Dooks <ben@simtec.co.uk>
- *		  Tidy up KF's patch and sort out new release
- *
- *   05-Oct-2004  Ben Dooks <ben@simtec.co.uk>
- *		  Add support for power management controls
- *
- *   04-Nov-2004  Ben Dooks
- *		  Fix standard IRQ wake for EINT0..4 and RTC
- *
- *   22-Feb-2005  Ben Dooks
- *		  Fixed edge-triggering on ADC IRQ
- *
- *   28-Jun-2005  Ben Dooks
- *		  Mark IRQ_LCD valid
- *
- *   25-Jul-2005  Ben Dooks
- *		  Split the S3C2440 IRQ code to separate file
 */
 */
 
 
 #include <linux/init.h>
 #include <linux/init.h>
@@ -55,81 +23,16 @@
 #include <linux/interrupt.h>
 #include <linux/interrupt.h>
 #include <linux/ioport.h>
 #include <linux/ioport.h>
 #include <linux/sysdev.h>
 #include <linux/sysdev.h>
-#include <linux/io.h>
 
 
-#include <mach/hardware.h>
 #include <asm/irq.h>
 #include <asm/irq.h>
-
 #include <asm/mach/irq.h>
 #include <asm/mach/irq.h>
 
 
 #include <plat/regs-irqtype.h>
 #include <plat/regs-irqtype.h>
-#include <mach/regs-irq.h>
-#include <mach/regs-gpio.h>
 
 
 #include <plat/cpu.h>
 #include <plat/cpu.h>
 #include <plat/pm.h>
 #include <plat/pm.h>
 #include <plat/irq.h>
 #include <plat/irq.h>
 
 
-/* wakeup irq control */
-
-#ifdef CONFIG_PM
-
-/* state for IRQs over sleep */
-
-/* default is to allow for EINT0..EINT15, and IRQ_RTC as wakeup sources
- *
- * set bit to 1 in allow bitfield to enable the wakeup settings on it
-*/
-
-unsigned long s3c_irqwake_intallow	= 1L << (IRQ_RTC - IRQ_EINT0) | 0xfL;
-unsigned long s3c_irqwake_intmask	= 0xffffffffL;
-unsigned long s3c_irqwake_eintallow	= 0x0000fff0L;
-unsigned long s3c_irqwake_eintmask	= 0xffffffffL;
-
-int
-s3c_irq_wake(unsigned int irqno, unsigned int state)
-{
-	unsigned long irqbit = 1 << (irqno - IRQ_EINT0);
-
-	if (!(s3c_irqwake_intallow & irqbit))
-		return -ENOENT;
-
-	printk(KERN_INFO "wake %s for irq %d\n",
-	       state ? "enabled" : "disabled", irqno);
-
-	if (!state)
-		s3c_irqwake_intmask |= irqbit;
-	else
-		s3c_irqwake_intmask &= ~irqbit;
-
-	return 0;
-}
-
-static int
-s3c_irqext_wake(unsigned int irqno, unsigned int state)
-{
-	unsigned long bit = 1L << (irqno - EXTINT_OFF);
-
-	if (!(s3c_irqwake_eintallow & bit))
-		return -ENOENT;
-
-	printk(KERN_INFO "wake %s for irq %d\n",
-	       state ? "enabled" : "disabled", irqno);
-
-	if (!state)
-		s3c_irqwake_eintmask |= bit;
-	else
-		s3c_irqwake_eintmask &= ~bit;
-
-	return 0;
-}
-
-#else
-#define s3c_irqext_wake NULL
-#define s3c_irq_wake NULL
-#endif
-
-
 static void
 static void
 s3c_irq_mask(unsigned int irqno)
 s3c_irq_mask(unsigned int irqno)
 {
 {
@@ -590,59 +493,6 @@ s3c_irq_demux_extint4t7(unsigned int irq,
 	}
 	}
 }
 }
 
 
-#ifdef CONFIG_PM
-
-static struct sleep_save irq_save[] = {
-	SAVE_ITEM(S3C2410_INTMSK),
-	SAVE_ITEM(S3C2410_INTSUBMSK),
-};
-
-/* the extint values move between the s3c2410/s3c2440 and the s3c2412
- * so we use an array to hold them, and to calculate the address of
- * the register at run-time
-*/
-
-static unsigned long save_extint[3];
-static unsigned long save_eintflt[4];
-static unsigned long save_eintmask;
-
-int s3c24xx_irq_suspend(struct sys_device *dev, pm_message_t state)
-{
-	unsigned int i;
-
-	for (i = 0; i < ARRAY_SIZE(save_extint); i++)
-		save_extint[i] = __raw_readl(S3C24XX_EXTINT0 + (i*4));
-
-	for (i = 0; i < ARRAY_SIZE(save_eintflt); i++)
-		save_eintflt[i] = __raw_readl(S3C24XX_EINFLT0 + (i*4));
-
-	s3c2410_pm_do_save(irq_save, ARRAY_SIZE(irq_save));
-	save_eintmask = __raw_readl(S3C24XX_EINTMASK);
-
-	return 0;
-}
-
-int s3c24xx_irq_resume(struct sys_device *dev)
-{
-	unsigned int i;
-
-	for (i = 0; i < ARRAY_SIZE(save_extint); i++)
-		__raw_writel(save_extint[i], S3C24XX_EXTINT0 + (i*4));
-
-	for (i = 0; i < ARRAY_SIZE(save_eintflt); i++)
-		__raw_writel(save_eintflt[i], S3C24XX_EINFLT0 + (i*4));
-
-	s3c2410_pm_do_restore(irq_save, ARRAY_SIZE(irq_save));
-	__raw_writel(save_eintmask, S3C24XX_EINTMASK);
-
-	return 0;
-}
-
-#else
-#define s3c24xx_irq_suspend NULL
-#define s3c24xx_irq_resume  NULL
-#endif
-
 /* s3c24xx_init_irq
 /* s3c24xx_init_irq
  *
  *
  * Initialise S3C2410 IRQ system
  * Initialise S3C2410 IRQ system

+ 1 - 1
arch/arm/plat-s3c24xx/pm-simtec.c

@@ -61,7 +61,7 @@ static __init int pm_simtec_init(void)
 
 
 	__raw_writel(gstatus4, S3C2410_GSTATUS4);
 	__raw_writel(gstatus4, S3C2410_GSTATUS4);
 
 
-	return s3c2410_pm_init();
+	return s3c_pm_init();
 }
 }
 
 
 arch_initcall(pm_simtec_init);
 arch_initcall(pm_simtec_init);

+ 24 - 479
arch/arm/plat-s3c24xx/pm.c

@@ -31,14 +31,9 @@
 #include <linux/errno.h>
 #include <linux/errno.h>
 #include <linux/time.h>
 #include <linux/time.h>
 #include <linux/interrupt.h>
 #include <linux/interrupt.h>
-#include <linux/crc32.h>
-#include <linux/ioport.h>
 #include <linux/serial_core.h>
 #include <linux/serial_core.h>
 #include <linux/io.h>
 #include <linux/io.h>
 
 
-#include <asm/cacheflush.h>
-#include <mach/hardware.h>
-
 #include <plat/regs-serial.h>
 #include <plat/regs-serial.h>
 #include <mach/regs-clock.h>
 #include <mach/regs-clock.h>
 #include <mach/regs-gpio.h>
 #include <mach/regs-gpio.h>
@@ -49,10 +44,6 @@
 
 
 #include <plat/pm.h>
 #include <plat/pm.h>
 
 
-/* for external use */
-
-unsigned long s3c_pm_flags;
-
 #define PFX "s3c24xx-pm: "
 #define PFX "s3c24xx-pm: "
 
 
 static struct sleep_save core_save[] = {
 static struct sleep_save core_save[] = {
@@ -120,328 +111,14 @@ static struct sleep_save misc_save[] = {
 	SAVE_ITEM(S3C2410_DCLKCON),
 	SAVE_ITEM(S3C2410_DCLKCON),
 };
 };
 
 
-#ifdef CONFIG_S3C2410_PM_DEBUG
-
-#define SAVE_UART(va) \
-	SAVE_ITEM((va) + S3C2410_ULCON), \
-	SAVE_ITEM((va) + S3C2410_UCON), \
-	SAVE_ITEM((va) + S3C2410_UFCON), \
-	SAVE_ITEM((va) + S3C2410_UMCON), \
-	SAVE_ITEM((va) + S3C2410_UBRDIV)
-
-static struct sleep_save uart_save[] = {
-	SAVE_UART(S3C24XX_VA_UART0),
-	SAVE_UART(S3C24XX_VA_UART1),
-#ifndef CONFIG_CPU_S3C2400
-	SAVE_UART(S3C24XX_VA_UART2),
-#endif
-};
-
-/* debug
- *
- * we send the debug to printascii() to allow it to be seen if the
- * system never wakes up from the sleep
-*/
-
-extern void printascii(const char *);
-
-void pm_dbg(const char *fmt, ...)
-{
-	va_list va;
-	char buff[256];
-
-	va_start(va, fmt);
-	vsprintf(buff, fmt, va);
-	va_end(va);
-
-	printascii(buff);
-}
-
-static void s3c2410_pm_debug_init(void)
-{
-	unsigned long tmp = __raw_readl(S3C2410_CLKCON);
-
-	/* re-start uart clocks */
-	tmp |= S3C2410_CLKCON_UART0;
-	tmp |= S3C2410_CLKCON_UART1;
-	tmp |= S3C2410_CLKCON_UART2;
-
-	__raw_writel(tmp, S3C2410_CLKCON);
-	udelay(10);
-}
-
-#define DBG(fmt...) pm_dbg(fmt)
-#else
-#define DBG(fmt...) printk(KERN_DEBUG fmt)
-
-#define s3c2410_pm_debug_init() do { } while(0)
-
-static struct sleep_save uart_save[] = {};
-#endif
-
-#if defined(CONFIG_S3C2410_PM_CHECK) && CONFIG_S3C2410_PM_CHECK_CHUNKSIZE != 0
-
-/* suspend checking code...
- *
- * this next area does a set of crc checks over all the installed
- * memory, so the system can verify if the resume was ok.
- *
- * CONFIG_S3C2410_PM_CHECK_CHUNKSIZE defines the block-size for the CRC,
- * increasing it will mean that the area corrupted will be less easy to spot,
- * and reducing the size will cause the CRC save area to grow
-*/
-
-#define CHECK_CHUNKSIZE (CONFIG_S3C2410_PM_CHECK_CHUNKSIZE * 1024)
-
-static u32 crc_size;	/* size needed for the crc block */
-static u32 *crcs;	/* allocated over suspend/resume */
-
-typedef u32 *(run_fn_t)(struct resource *ptr, u32 *arg);
-
-/* s3c2410_pm_run_res
- *
- * go thorugh the given resource list, and look for system ram
-*/
-
-static void s3c2410_pm_run_res(struct resource *ptr, run_fn_t fn, u32 *arg)
-{
-	while (ptr != NULL) {
-		if (ptr->child != NULL)
-			s3c2410_pm_run_res(ptr->child, fn, arg);
-
-		if ((ptr->flags & IORESOURCE_MEM) &&
-		    strcmp(ptr->name, "System RAM") == 0) {
-			DBG("Found system RAM at %08lx..%08lx\n",
-			    ptr->start, ptr->end);
-			arg = (fn)(ptr, arg);
-		}
-
-		ptr = ptr->sibling;
-	}
-}
-
-static void s3c2410_pm_run_sysram(run_fn_t fn, u32 *arg)
-{
-	s3c2410_pm_run_res(&iomem_resource, fn, arg);
-}
-
-static u32 *s3c2410_pm_countram(struct resource *res, u32 *val)
-{
-	u32 size = (u32)(res->end - res->start)+1;
-
-	size += CHECK_CHUNKSIZE-1;
-	size /= CHECK_CHUNKSIZE;
-
-	DBG("Area %08lx..%08lx, %d blocks\n", res->start, res->end, size);
-
-	*val += size * sizeof(u32);
-	return val;
-}
-
-/* s3c2410_pm_prepare_check
- *
- * prepare the necessary information for creating the CRCs. This
- * must be done before the final save, as it will require memory
- * allocating, and thus touching bits of the kernel we do not
- * know about.
-*/
-
-static void s3c2410_pm_check_prepare(void)
-{
-	crc_size = 0;
-
-	s3c2410_pm_run_sysram(s3c2410_pm_countram, &crc_size);
-
-	DBG("s3c2410_pm_prepare_check: %u checks needed\n", crc_size);
-
-	crcs = kmalloc(crc_size+4, GFP_KERNEL);
-	if (crcs == NULL)
-		printk(KERN_ERR "Cannot allocated CRC save area\n");
-}
-
-static u32 *s3c2410_pm_makecheck(struct resource *res, u32 *val)
-{
-	unsigned long addr, left;
-
-	for (addr = res->start; addr < res->end;
-	     addr += CHECK_CHUNKSIZE) {
-		left = res->end - addr;
-
-		if (left > CHECK_CHUNKSIZE)
-			left = CHECK_CHUNKSIZE;
-
-		*val = crc32_le(~0, phys_to_virt(addr), left);
-		val++;
-	}
-
-	return val;
-}
-
-/* s3c2410_pm_check_store
- *
- * compute the CRC values for the memory blocks before the final
- * sleep.
-*/
-
-static void s3c2410_pm_check_store(void)
-{
-	if (crcs != NULL)
-		s3c2410_pm_run_sysram(s3c2410_pm_makecheck, crcs);
-}
-
-/* in_region
- *
- * return TRUE if the area defined by ptr..ptr+size contatins the
- * what..what+whatsz
-*/
-
-static inline int in_region(void *ptr, int size, void *what, size_t whatsz)
-{
-	if ((what+whatsz) < ptr)
-		return 0;
-
-	if (what > (ptr+size))
-		return 0;
-
-	return 1;
-}
-
-static u32 *s3c2410_pm_runcheck(struct resource *res, u32 *val)
-{
-	void *save_at = phys_to_virt(s3c2410_sleep_save_phys);
-	unsigned long addr;
-	unsigned long left;
-	void *ptr;
-	u32 calc;
-
-	for (addr = res->start; addr < res->end;
-	     addr += CHECK_CHUNKSIZE) {
-		left = res->end - addr;
-
-		if (left > CHECK_CHUNKSIZE)
-			left = CHECK_CHUNKSIZE;
-
-		ptr = phys_to_virt(addr);
-
-		if (in_region(ptr, left, crcs, crc_size)) {
-			DBG("skipping %08lx, has crc block in\n", addr);
-			goto skip_check;
-		}
-
-		if (in_region(ptr, left, save_at, 32*4 )) {
-			DBG("skipping %08lx, has save block in\n", addr);
-			goto skip_check;
-		}
-
-		/* calculate and check the checksum */
-
-		calc = crc32_le(~0, ptr, left);
-		if (calc != *val) {
-			printk(KERN_ERR PFX "Restore CRC error at "
-			       "%08lx (%08x vs %08x)\n", addr, calc, *val);
-
-			DBG("Restore CRC error at %08lx (%08x vs %08x)\n",
-			    addr, calc, *val);
-		}
-
-	skip_check:
-		val++;
-	}
-
-	return val;
-}
-
-/* s3c2410_pm_check_restore
- *
- * check the CRCs after the restore event and free the memory used
- * to hold them
-*/
-
-static void s3c2410_pm_check_restore(void)
-{
-	if (crcs != NULL) {
-		s3c2410_pm_run_sysram(s3c2410_pm_runcheck, crcs);
-		kfree(crcs);
-		crcs = NULL;
-	}
-}
-
-#else
-
-#define s3c2410_pm_check_prepare() do { } while(0)
-#define s3c2410_pm_check_restore() do { } while(0)
-#define s3c2410_pm_check_store()   do { } while(0)
-#endif
-
-/* helper functions to save and restore register state */
-
-void s3c2410_pm_do_save(struct sleep_save *ptr, int count)
-{
-	for (; count > 0; count--, ptr++) {
-		ptr->val = __raw_readl(ptr->reg);
-		DBG("saved %p value %08lx\n", ptr->reg, ptr->val);
-	}
-}
-
-/* s3c2410_pm_do_restore
- *
- * restore the system from the given list of saved registers
- *
- * Note, we do not use DBG() in here, as the system may not have
- * restore the UARTs state yet
-*/
-
-void s3c2410_pm_do_restore(struct sleep_save *ptr, int count)
-{
-	for (; count > 0; count--, ptr++) {
-		printk(KERN_DEBUG "restore %p (restore %08lx, was %08x)\n",
-		       ptr->reg, ptr->val, __raw_readl(ptr->reg));
-
-		__raw_writel(ptr->val, ptr->reg);
-	}
-}
-
-/* s3c2410_pm_do_restore_core
- *
- * similar to s3c2410_pm_do_restore_core
- *
- * WARNING: Do not put any debug in here that may effect memory or use
- * peripherals, as things may be changing!
-*/
-
-static void s3c2410_pm_do_restore_core(struct sleep_save *ptr, int count)
-{
-	for (; count > 0; count--, ptr++) {
-		__raw_writel(ptr->val, ptr->reg);
-	}
-}
 
 
-/* s3c2410_pm_show_resume_irqs
- *
- * print any IRQs asserted at resume time (ie, we woke from)
-*/
-
-static void s3c2410_pm_show_resume_irqs(int start, unsigned long which,
-					unsigned long mask)
-{
-	int i;
-
-	which &= ~mask;
-
-	for (i = 0; i <= 31; i++) {
-		if ((which) & (1L<<i)) {
-			DBG("IRQ %d asserted at resume\n", start+i);
-		}
-	}
-}
-
-/* s3c2410_pm_check_resume_pin
+/* s3c_pm_check_resume_pin
  *
  *
  * check to see if the pin is configured correctly for sleep mode, and
  * check to see if the pin is configured correctly for sleep mode, and
  * make any necessary adjustments if it is not
  * make any necessary adjustments if it is not
 */
 */
 
 
-static void s3c2410_pm_check_resume_pin(unsigned int pin, unsigned int irqoffs)
+static void s3c_pm_check_resume_pin(unsigned int pin, unsigned int irqoffs)
 {
 {
 	unsigned long irqstate;
 	unsigned long irqstate;
 	unsigned long pinstate;
 	unsigned long pinstate;
@@ -456,21 +133,21 @@ static void s3c2410_pm_check_resume_pin(unsigned int pin, unsigned int irqoffs)
 
 
 	if (!irqstate) {
 	if (!irqstate) {
 		if (pinstate == S3C2410_GPIO_IRQ)
 		if (pinstate == S3C2410_GPIO_IRQ)
-			DBG("Leaving IRQ %d (pin %d) enabled\n", irq, pin);
+			S3C_PMDBG("Leaving IRQ %d (pin %d) enabled\n", irq, pin);
 	} else {
 	} else {
 		if (pinstate == S3C2410_GPIO_IRQ) {
 		if (pinstate == S3C2410_GPIO_IRQ) {
-			DBG("Disabling IRQ %d (pin %d)\n", irq, pin);
+			S3C_PMDBG("Disabling IRQ %d (pin %d)\n", irq, pin);
 			s3c2410_gpio_cfgpin(pin, S3C2410_GPIO_INPUT);
 			s3c2410_gpio_cfgpin(pin, S3C2410_GPIO_INPUT);
 		}
 		}
 	}
 	}
 }
 }
 
 
-/* s3c2410_pm_configure_extint
+/* s3c_pm_configure_extint
  *
  *
  * configure all external interrupt pins
  * configure all external interrupt pins
 */
 */
 
 
-static void s3c2410_pm_configure_extint(void)
+void s3c_pm_configure_extint(void)
 {
 {
 	int pin;
 	int pin;
 
 
@@ -480,11 +157,11 @@ static void s3c2410_pm_configure_extint(void)
 	*/
 	*/
 
 
 	for (pin = S3C2410_GPF0; pin <= S3C2410_GPF7; pin++) {
 	for (pin = S3C2410_GPF0; pin <= S3C2410_GPF7; pin++) {
-		s3c2410_pm_check_resume_pin(pin, pin - S3C2410_GPF0);
+		s3c_pm_check_resume_pin(pin, pin - S3C2410_GPF0);
 	}
 	}
 
 
 	for (pin = S3C2410_GPG0; pin <= S3C2410_GPG7; pin++) {
 	for (pin = S3C2410_GPG0; pin <= S3C2410_GPG7; pin++) {
-		s3c2410_pm_check_resume_pin(pin, (pin - S3C2410_GPG0)+8);
+		s3c_pm_check_resume_pin(pin, (pin - S3C2410_GPG0)+8);
 	}
 	}
 }
 }
 
 
@@ -494,12 +171,12 @@ static void s3c2410_pm_configure_extint(void)
 #define OFFS_DAT	(S3C2410_GPADAT - S3C2410_GPACON)
 #define OFFS_DAT	(S3C2410_GPADAT - S3C2410_GPACON)
 #define OFFS_UP		(S3C2410_GPBUP  - S3C2410_GPBCON)
 #define OFFS_UP		(S3C2410_GPBUP  - S3C2410_GPBCON)
 
 
-/* s3c2410_pm_save_gpios()
+/* s3c_pm_save_gpios()
  *
  *
  * Save the state of the GPIOs
  * Save the state of the GPIOs
  */
  */
 
 
-static void s3c2410_pm_save_gpios(void)
+void s3c_pm_save_gpios(void)
 {
 {
 	struct gpio_sleep *gps = gpio_save;
 	struct gpio_sleep *gps = gpio_save;
 	unsigned int gpio;
 	unsigned int gpio;
@@ -538,7 +215,10 @@ static inline int is_out(unsigned long con)
 	return con == 1;
 	return con == 1;
 }
 }
 
 
-/* s3c2410_pm_restore_gpio()
+/**
+ * s3c2410_pm_restore_gpio() - restore the given GPIO bank
+ * @index: The number of the GPIO bank being resumed.
+ * @gps: The sleep confgiuration for the bank.
  *
  *
  * Restore one of the GPIO banks that was saved during suspend. This is
  * Restore one of the GPIO banks that was saved during suspend. This is
  * not as simple as once thought, due to the possibility of glitches
  * not as simple as once thought, due to the possibility of glitches
@@ -646,8 +326,8 @@ static void s3c2410_pm_restore_gpio(int index, struct gpio_sleep *gps)
 		__raw_writel(gps->gpup, base + OFFS_UP);
 		__raw_writel(gps->gpup, base + OFFS_UP);
 	}
 	}
 
 
-	DBG("GPIO[%d] CON %08lx => %08lx, DAT %08lx => %08lx\n",
-	    index, old_gpcon, gps_gpcon, old_gpdat, gps_gpdat);
+	S3C_PMDBG("GPIO[%d] CON %08lx => %08lx, DAT %08lx => %08lx\n",
+		  index, old_gpcon, gps_gpcon, old_gpdat, gps_gpdat);
 }
 }
 
 
 
 
@@ -656,7 +336,7 @@ static void s3c2410_pm_restore_gpio(int index, struct gpio_sleep *gps)
  * Restore the state of the GPIOs
  * Restore the state of the GPIOs
  */
  */
 
 
-static void s3c2410_pm_restore_gpios(void)
+void s3c_pm_restore_gpios(void)
 {
 {
 	struct gpio_sleep *gps = gpio_save;
 	struct gpio_sleep *gps = gpio_save;
 	int gpio;
 	int gpio;
@@ -666,150 +346,15 @@ static void s3c2410_pm_restore_gpios(void)
 	}
 	}
 }
 }
 
 
-void (*pm_cpu_prep)(void);
-void (*pm_cpu_sleep)(void);
-
-#define any_allowed(mask, allow) (((mask) & (allow)) != (allow))
-
-/* s3c2410_pm_enter
- *
- * central control for sleep/resume process
-*/
-
-static int s3c2410_pm_enter(suspend_state_t state)
+void s3c_pm_restore_core(void)
 {
 {
-	unsigned long regs_save[16];
-
-	/* ensure the debug is initialised (if enabled) */
-
-	s3c2410_pm_debug_init();
-
-	DBG("s3c2410_pm_enter(%d)\n", state);
-
-	if (pm_cpu_prep == NULL || pm_cpu_sleep == NULL) {
-		printk(KERN_ERR PFX "error: no cpu sleep functions set\n");
-		return -EINVAL;
-	}
-
-	/* check if we have anything to wake-up with... bad things seem
-	 * to happen if you suspend with no wakeup (system will often
-	 * require a full power-cycle)
-	*/
-
-	if (!any_allowed(s3c_irqwake_intmask, s3c_irqwake_intallow) &&
-	    !any_allowed(s3c_irqwake_eintmask, s3c_irqwake_eintallow)) {
-		printk(KERN_ERR PFX "No sources enabled for wake-up!\n");
-		printk(KERN_ERR PFX "Aborting sleep\n");
-		return -EINVAL;
-	}
-
-	/* prepare check area if configured */
-
-	s3c2410_pm_check_prepare();
-
-	/* store the physical address of the register recovery block */
-
-	s3c2410_sleep_save_phys = virt_to_phys(regs_save);
-
-	DBG("s3c2410_sleep_save_phys=0x%08lx\n", s3c2410_sleep_save_phys);
-
-	/* save all necessary core registers not covered by the drivers */
-
-	s3c2410_pm_save_gpios();
-	s3c2410_pm_do_save(misc_save, ARRAY_SIZE(misc_save));
-	s3c2410_pm_do_save(core_save, ARRAY_SIZE(core_save));
-	s3c2410_pm_do_save(uart_save, ARRAY_SIZE(uart_save));
-
-	/* set the irq configuration for wake */
-
-	s3c2410_pm_configure_extint();
-
-	DBG("sleep: irq wakeup masks: %08lx,%08lx\n",
-	    s3c_irqwake_intmask, s3c_irqwake_eintmask);
-
-	__raw_writel(s3c_irqwake_intmask, S3C2410_INTMSK);
-	__raw_writel(s3c_irqwake_eintmask, S3C2410_EINTMASK);
-
-	/* ack any outstanding external interrupts before we go to sleep */
-
-	__raw_writel(__raw_readl(S3C2410_EINTPEND), S3C2410_EINTPEND);
-	__raw_writel(__raw_readl(S3C2410_INTPND), S3C2410_INTPND);
-	__raw_writel(__raw_readl(S3C2410_SRCPND), S3C2410_SRCPND);
-
-	/* call cpu specific preparation */
-
-	pm_cpu_prep();
-
-	/* flush cache back to ram */
-
-	flush_cache_all();
-
-	s3c2410_pm_check_store();
-
-	/* send the cpu to sleep... */
-
-	__raw_writel(0x00, S3C2410_CLKCON);  /* turn off clocks over sleep */
-
-	/* s3c2410_cpu_save will also act as our return point from when
-	 * we resume as it saves its own register state, so use the return
-	 * code to differentiate return from save and return from sleep */
-
-	if (s3c2410_cpu_save(regs_save) == 0) {
-		flush_cache_all();
-		pm_cpu_sleep();
-	}
-
-	/* restore the cpu state */
-
-	cpu_init();
-
-	/* restore the system state */
-
-	s3c2410_pm_do_restore_core(core_save, ARRAY_SIZE(core_save));
-	s3c2410_pm_do_restore(misc_save, ARRAY_SIZE(misc_save));
-	s3c2410_pm_do_restore(uart_save, ARRAY_SIZE(uart_save));
-	s3c2410_pm_restore_gpios();
-
-	s3c2410_pm_debug_init();
-
-	/* check what irq (if any) restored the system */
-
-	DBG("post sleep: IRQs 0x%08x, 0x%08x\n",
-	    __raw_readl(S3C2410_SRCPND),
-	    __raw_readl(S3C2410_EINTPEND));
-
-	s3c2410_pm_show_resume_irqs(IRQ_EINT0, __raw_readl(S3C2410_SRCPND),
-				    s3c_irqwake_intmask);
-
-	s3c2410_pm_show_resume_irqs(IRQ_EINT4-4, __raw_readl(S3C2410_EINTPEND),
-				    s3c_irqwake_eintmask);
-
-	DBG("post sleep, preparing to return\n");
-
-	s3c2410_pm_check_restore();
-
-	/* ok, let's return from sleep */
-
-	DBG("S3C2410 PM Resume (post-restore)\n");
-	return 0;
+	s3c_pm_do_restore_core(core_save, ARRAY_SIZE(core_save));
+	s3c_pm_do_restore(misc_save, ARRAY_SIZE(misc_save));
 }
 }
 
 
-static struct platform_suspend_ops s3c2410_pm_ops = {
-	.enter		= s3c2410_pm_enter,
-	.valid		= suspend_valid_only_mem,
-};
-
-/* s3c2410_pm_init
- *
- * Attach the power management functions. This should be called
- * from the board specific initialisation if the board supports
- * it.
-*/
-
-int __init s3c2410_pm_init(void)
+void s3c_pm_save_core(void)
 {
 {
-	printk("S3C2410 Power Management, (c) 2004 Simtec Electronics\n");
-
-	suspend_set_ops(&s3c2410_pm_ops);
-	return 0;
+	s3c_pm_do_save(misc_save, ARRAY_SIZE(misc_save));
+	s3c_pm_do_save(core_save, ARRAY_SIZE(core_save));
 }
 }
+

+ 2 - 2
arch/arm/plat-s3c24xx/s3c244x.c

@@ -145,13 +145,13 @@ static struct sleep_save s3c244x_sleep[] = {
 
 
 static int s3c244x_suspend(struct sys_device *dev, pm_message_t state)
 static int s3c244x_suspend(struct sys_device *dev, pm_message_t state)
 {
 {
-	s3c2410_pm_do_save(s3c244x_sleep, ARRAY_SIZE(s3c244x_sleep));
+	s3c_pm_do_save(s3c244x_sleep, ARRAY_SIZE(s3c244x_sleep));
 	return 0;
 	return 0;
 }
 }
 
 
 static int s3c244x_resume(struct sys_device *dev)
 static int s3c244x_resume(struct sys_device *dev)
 {
 {
-	s3c2410_pm_do_restore(s3c244x_sleep, ARRAY_SIZE(s3c244x_sleep));
+	s3c_pm_do_restore(s3c244x_sleep, ARRAY_SIZE(s3c244x_sleep));
 	return 0;
 	return 0;
 }
 }
 
 

+ 18 - 25
arch/arm/plat-s3c24xx/sleep.S

@@ -41,25 +41,13 @@
 
 
 	.text
 	.text
 
 
-	/* s3c2410_cpu_save
-	 *
-	 * save enough of the CPU state to allow us to re-start
-	 * pm.c code. as we store items like the sp/lr, we will
-	 * end up returning from this function when the cpu resumes
-	 * so the return value is set to mark this.
-	 *
-	 * This arangement means we avoid having to flush the cache
-	 * from this code.
+	/* s3c_cpu_save
 	 *
 	 *
 	 * entry:
 	 * entry:
-	 *	r0 = pointer to save block
-	 *
-	 * exit:
-	 *	r0 = 0 => we stored everything
-	 *	     1 => resumed from sleep
+	 *	r0 = save address (virtual addr of s3c_sleep_save_phys)
 	*/
 	*/
 
 
-ENTRY(s3c2410_cpu_save)
+ENTRY(s3c_cpu_save)
 	stmfd	sp!, { r4 - r12, lr }
 	stmfd	sp!, { r4 - r12, lr }
 
 
 	@@ store co-processor registers
 	@@ store co-processor registers
@@ -71,20 +59,25 @@ ENTRY(s3c2410_cpu_save)
 
 
 	stmia	r0, { r4 - r13 }
 	stmia	r0, { r4 - r13 }
 
 
-	mov	r0, #0
-	ldmfd	sp, { r4 - r12, pc }
+	@@ write our state back to RAM
+	bl	s3c_pm_cb_flushcache
 
 
+	@@ jump to final code to send system to sleep
+	ldr	r0, =pm_cpu_sleep
+	@@ldr	pc, [ r0 ]
+	ldr	r0, [ r0 ]
+	mov	pc, r0
+	
 	@@ return to the caller, after having the MMU
 	@@ return to the caller, after having the MMU
 	@@ turned on, this restores the last bits from the
 	@@ turned on, this restores the last bits from the
 	@@ stack
 	@@ stack
 resume_with_mmu:
 resume_with_mmu:
-	mov	r0, #1
 	ldmfd	sp!, { r4 - r12, pc }
 	ldmfd	sp!, { r4 - r12, pc }
 
 
 	.ltorg
 	.ltorg
 
 
 	@@ the next bits sit in the .data segment, even though they
 	@@ the next bits sit in the .data segment, even though they
-	@@ happen to be code... the s3c2410_sleep_save_phys needs to be
+	@@ happen to be code... the s3c_sleep_save_phys needs to be
 	@@ accessed by the resume code before it can restore the MMU.
 	@@ accessed by the resume code before it can restore the MMU.
 	@@ This means that the variable has to be close enough for the
 	@@ This means that the variable has to be close enough for the
 	@@ code to read it... since the .text segment needs to be RO,
 	@@ code to read it... since the .text segment needs to be RO,
@@ -92,19 +85,19 @@ resume_with_mmu:
 
 
 	.data
 	.data
 
 
-	.global	s3c2410_sleep_save_phys
-s3c2410_sleep_save_phys:
+	.global	s3c_sleep_save_phys
+s3c_sleep_save_phys:
 	.word	0
 	.word	0
 
 
 
 
 	/* sleep magic, to allow the bootloader to check for an valid
 	/* sleep magic, to allow the bootloader to check for an valid
 	 * image to resume to. Must be the first word before the
 	 * image to resume to. Must be the first word before the
-	 * s3c2410_cpu_resume entry.
+	 * s3c_cpu_resume entry.
 	*/
 	*/
 
 
 	.word	0x2bedf00d
 	.word	0x2bedf00d
 
 
-	/* s3c2410_cpu_resume
+	/* s3c_cpu_resume
 	 *
 	 *
 	 * resume code entry for bootloader to call
 	 * resume code entry for bootloader to call
 	 *
 	 *
@@ -113,7 +106,7 @@ s3c2410_sleep_save_phys:
 	 * must not write to the code segment (code is read-only)
 	 * must not write to the code segment (code is read-only)
 	*/
 	*/
 
 
-ENTRY(s3c2410_cpu_resume)
+ENTRY(s3c_cpu_resume)
 	mov	r0, #PSR_I_BIT | PSR_F_BIT | SVC_MODE
 	mov	r0, #PSR_I_BIT | PSR_F_BIT | SVC_MODE
 	msr	cpsr_c, r0
 	msr	cpsr_c, r0
 
 
@@ -145,7 +138,7 @@ ENTRY(s3c2410_cpu_resume)
 	mcr	p15, 0, r1, c8, c7, 0		@@ invalidate I & D TLBs
 	mcr	p15, 0, r1, c8, c7, 0		@@ invalidate I & D TLBs
 	mcr	p15, 0, r1, c7, c7, 0		@@ invalidate I & D caches
 	mcr	p15, 0, r1, c7, c7, 0		@@ invalidate I & D caches
 
 
-	ldr	r0, s3c2410_sleep_save_phys	@ address of restore block
+	ldr	r0, s3c_sleep_save_phys		@ address of restore block
 	ldmia	r0, { r4 - r13 }
 	ldmia	r0, { r4 - r13 }
 
 
 	mcr	p15, 0, r4, c13, c0, 0		@ PID
 	mcr	p15, 0, r4, c13, c0, 0		@ PID