diff options
author | Russell King <rmk@dyn-67.arm.linux.org.uk> | 2009-03-26 22:44:43 +0000 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2009-03-26 22:44:43 +0000 |
commit | f5f0e17a8475dee913f362510e6a8ab987d27b9f (patch) | |
tree | ebe44e7774400730f6c15cd5a84187113c274e3d /arch/arm/plat-s3c | |
parent | 2f3ec501ba1e1a68ab9d413f143bdc8f46417fc1 (diff) | |
parent | fff94cd9f5527bbba13aa5ea5719d16531ca8e65 (diff) |
Merge branch 'next-s3c-pm' of git://aeryn.fluff.org.uk/bjdooks/linux into devel
Diffstat (limited to 'arch/arm/plat-s3c')
-rw-r--r-- | arch/arm/plat-s3c/Makefile | 5 | ||||
-rw-r--r-- | arch/arm/plat-s3c/include/plat/pm.h | 174 | ||||
-rw-r--r-- | arch/arm/plat-s3c/pm-check.c | 242 | ||||
-rw-r--r-- | arch/arm/plat-s3c/pm.c | 363 |
4 files changed, 784 insertions, 0 deletions
diff --git a/arch/arm/plat-s3c/Makefile b/arch/arm/plat-s3c/Makefile index 39195f972d5e..8d7815d25a51 100644 --- a/arch/arm/plat-s3c/Makefile +++ b/arch/arm/plat-s3c/Makefile @@ -18,6 +18,11 @@ obj-y += pwm-clock.o obj-y += gpio.o obj-y += gpio-config.o +# PM support + +obj-$(CONFIG_PM) += pm.o +obj-$(CONFIG_S3C2410_PM_CHECK) += pm-check.o + # devices obj-$(CONFIG_S3C_DEV_HSMMC) += dev-hsmmc.o diff --git a/arch/arm/plat-s3c/include/plat/pm.h b/arch/arm/plat-s3c/include/plat/pm.h new file mode 100644 index 000000000000..3779775133a9 --- /dev/null +++ b/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); diff --git a/arch/arm/plat-s3c/pm-check.c b/arch/arm/plat-s3c/pm-check.c new file mode 100644 index 000000000000..39f2555564da --- /dev/null +++ b/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; +} + diff --git a/arch/arm/plat-s3c/pm.c b/arch/arm/plat-s3c/pm.c new file mode 100644 index 000000000000..061182ca66e3 --- /dev/null +++ b/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; +} |