Commit f5f0e17a authored by Russell King's avatar Russell King Committed by Russell King

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

parents 2f3ec501 fff94cd9
...@@ -40,13 +40,13 @@ Resuming ...@@ -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) ...@@ -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();
} }
......
/* 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)
...@@ -23,3 +23,6 @@ ...@@ -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)
...@@ -80,7 +80,7 @@ ...@@ -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)
......
...@@ -14,16 +14,7 @@ ...@@ -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)) #include <mach/gpio-nrs.h>
#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)
#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)
......
...@@ -203,7 +203,7 @@ static void __init h1940_map_io(void) ...@@ -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)
......
...@@ -355,7 +355,7 @@ static void __init qt2410_machine_init(void) ...@@ -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")
......
...@@ -37,21 +37,14 @@ ...@@ -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)); S3C_PMDBG("GSTATUS3 0x%08x\n", __raw_readl(S3C2410_GSTATUS3));
DBG("GSTATUS4 0x%08x\n", __raw_readl(S3C2410_GSTATUS4)); 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);
......
...@@ -494,7 +494,7 @@ static int jive_pm_suspend(struct sys_device *sd, pm_message_t state) ...@@ -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) ...@@ -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;
......
...@@ -85,7 +85,7 @@ static struct sleep_save s3c2412_sleep[] = { ...@@ -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) ...@@ -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;
} }
......
...@@ -203,7 +203,7 @@ static void __init rx3715_init_machine(void) ...@@ -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));
......
...@@ -70,6 +70,8 @@ ...@@ -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)
......
...@@ -18,6 +18,11 @@ obj-y += pwm-clock.o ...@@ -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
......
/* 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);
/* 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;
}
/* 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;
}
...@@ -27,6 +27,7 @@ obj-$(CONFIG_CPU_S3C244X) += s3c244x-irq.o ...@@ -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
......
...@@ -201,5 +201,5 @@ void __init smdk_machine_init(void) ...@@ -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();
} }
...@@ -10,6 +10,12 @@ ...@@ -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...)
......
...@@ -31,6 +31,8 @@ ...@@ -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)
......
/* 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);
}
/* 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
/* 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;
}
/* 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 @@ ...@@ -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 @@ ...@@ -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, ...@@ -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
......
...@@ -61,7 +61,7 @@ static __init int pm_simtec_init(void) ...@@ -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);
This diff is collapsed.
...@@ -145,13 +145,13 @@ static struct sleep_save s3c244x_sleep[] = { ...@@ -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;
} }
......
...@@ -41,25 +41,13 @@ ...@@ -41,25 +41,13 @@
.text .text
/* s3c2410_cpu_save /* s3c_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.
* *
* entry: * entry:
* r0 = pointer to save block * r0 = save address (virtual addr of s3c_sleep_save_phys)
*
* exit:
* r0 = 0 => we stored everything
* 1 => resumed from sleep
*/ */
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) ...@@ -71,20 +59,25 @@ ENTRY(s3c2410_cpu_save)
stmia r0, { r4 - r13 } stmia r0, { r4 - r13 }
mov r0, #0 @@ write our state back to RAM
ldmfd sp, { r4 - r12, pc } 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: ...@@ -92,19 +85,19 @@ resume_with_mmu:
.data .data
.global s3c2410_sleep_save_phys .global s3c_sleep_save_phys
s3c2410_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: ...@@ -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) ...@@ -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
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment