diff options
Diffstat (limited to 'arch/arm/mach-s3c2443/cc9m2443js-pm.c')
-rw-r--r-- | arch/arm/mach-s3c2443/cc9m2443js-pm.c | 221 |
1 files changed, 221 insertions, 0 deletions
diff --git a/arch/arm/mach-s3c2443/cc9m2443js-pm.c b/arch/arm/mach-s3c2443/cc9m2443js-pm.c new file mode 100644 index 000000000000..2d56de817894 --- /dev/null +++ b/arch/arm/mach-s3c2443/cc9m2443js-pm.c @@ -0,0 +1,221 @@ +/* -*- linux-c -*- + * + * linux/arch/arm/mach-s3c2443/cc9m2443js-pm.c + * + * Copyright (c) 2009 Digi International Spain + * + * http://www.digi.com/products/embeddedsolutions/connectcore9m.jsp + * + * 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. + * + * Author : Luis Galdos <luis.galdos@digi.com> + * + */ + +#include <linux/ioport.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/platform_device.h> +#include <mach/memory.h> +#include <asm/memory.h> +#include <mach/map.h> +#include <mach/regs-s3c2443-mem.h> +#include <mach/hardware.h> + +#include <mach/regs-gpio.h> +#include <mach/regs-gpioj.h> +#include <mach/regs-lcd.h> +#include <mach/regs-s3c2443-clock.h> +#include <mach/regs-power.h> + +#include <linux/interrupt.h> +#include <asm/mach/irq.h> +#include <mach/gpio.h> + +#include "cc9m2443js-pm.h" + + +#define CC9M2443_PM_ITEM_OFFSET(off) { .offset = off } +#define CC9M2443_PM_ITEM_BASE(bas) { .base = bas } + +struct cc9m2443js_pm_gpio { + int gpio; + unsigned long config; + int irq; + int state; + unsigned long flags; + irqreturn_t (* irq_handler)(int irq, void *gpio); +}; + +struct cc9m2443_pm_reg { + void __iomem *base; + unsigned long offset; + unsigned long val; +}; + +/* + * The SSMC registers are not part of the minimal IO mapping of the CPU + */ +static void __iomem *ssmc_vptr = NULL; + +/* Converts the virtual to physical address of the stack pointer */ +unsigned long sleep_phys_sp(void *sp) +{ + return virt_to_phys(sp); +} + +/* These are the register for the external Ethernet controller (CS5) */ +static struct cc9m2443_pm_reg s3c2443_regs_ssmc[] = { + CC9M2443_PM_ITEM_OFFSET(S3C2443_SSMC_SMBIDCYR5), + CC9M2443_PM_ITEM_OFFSET(S3C2443_SSMC_SMBWSTRDR5), + CC9M2443_PM_ITEM_OFFSET(S3C2443_SSMC_SMBWSTWRR5), + CC9M2443_PM_ITEM_OFFSET(S3C2443_SSMC_SMBWSTOENR5), + CC9M2443_PM_ITEM_OFFSET(S3C2443_SSMC_SMBWSTWENR5), + CC9M2443_PM_ITEM_OFFSET(S3C2443_SSMC_SMBCR5), + CC9M2443_PM_ITEM_OFFSET(S3C2443_SSMC_SMBWSTBRDR5), +}; + +/* We MUST save the reset configuration register */ +static struct cc9m2443_pm_reg s3c2443_regs_syscon[] = { + CC9M2443_PM_ITEM_BASE(S3C2443_RSTCON), + CC9M2443_PM_ITEM_BASE(S3C2443_PWRCFG), + CC9M2443_PM_ITEM_BASE(S3C2443_OSCSET), +}; + +/* If the passed register doesn't have a base register, the offset will be used */ +static void cc9m2443_pm_restore_regs(struct cc9m2443_pm_reg *regs, int count, + void __iomem *offptr) +{ + int cnt; + void __iomem *reg_ptr; + + if (!regs) { + printk(KERN_ERR "[ RESUME ] NULL pointer passed.\n"); + return; + } + + for (cnt = 0; cnt < count; cnt++, regs++) { + reg_ptr = (regs->base) ? (regs->base) : (offptr + regs->offset); + __raw_writel(regs->val, reg_ptr); + } +} + +/* If the passed register doesn't have a base register, the offset will be used */ +static void cc9m2443_pm_save_regs(struct cc9m2443_pm_reg *regs, int count, + void __iomem *offptr) +{ + int cnt; + void __iomem *reg_ptr; + + if (!regs) { + printk(KERN_ERR "[ SUSPEND ] NULL pointer passed!\n"); + return; + } + + for (cnt = 0; cnt < count; cnt++, regs++) { + reg_ptr = (regs->base) ? (regs->base) : (offptr + regs->offset); + regs->val = __raw_readl(reg_ptr); + } +} + +/* Save all the registers that are part of the CC9M2443 */ +static int cc9m2443_pm_save(void) +{ + /* First map to our virtual memory if required */ + if (!ssmc_vptr) + ssmc_vptr = ioremap_nocache(S3C2443_PA_SSMC, S3C2443_SZ_SSMC); + + if (!ssmc_vptr) + printk(KERN_ERR "Couldn't save the regs of the SSMC\n"); + else + cc9m2443_pm_save_regs(s3c2443_regs_ssmc, ARRAY_SIZE(s3c2443_regs_ssmc), + ssmc_vptr); + + cc9m2443_pm_save_regs(s3c2443_regs_syscon, ARRAY_SIZE(s3c2443_regs_syscon), + NULL); + + return 0; +} + +static int cc9m2443_pm_restore(void) +{ + if (ssmc_vptr) + cc9m2443_pm_restore_regs(s3c2443_regs_ssmc, + ARRAY_SIZE(s3c2443_regs_ssmc), ssmc_vptr); + + cc9m2443_pm_restore_regs(s3c2443_regs_syscon, + ARRAY_SIZE(s3c2443_regs_syscon), NULL); + + return 0; +} + +/* + * This is the CPU-specific prepare function (Code coming from the WinCE world) + * (Luis Galdos) + */ +static void s3c2443_pm_prepare(void) +{ + __raw_writel(0xff80, S3C2443_RSTCON); + __raw_writel(0x8000, S3C2443_OSCSET); + __raw_writel(0x8008, S3C2443_PWRCFG); +} + +static int cc9m2443js_pm_suspend(struct sys_device *sd, pm_message_t state) +{ + /* Write the magic value u-boot uses to check for resume into + * the INFORM0 register, and ensure INFORM1 is set to the + * correct address to resume from. */ + + printk(KERN_DEBUG "[ SUSPEND ] Setting resume address to %p [0x%lx]\n", + s3c2443_cpu_resume, + virt_to_phys(s3c2443_cpu_resume)); + __raw_writel(0x2BED, S3C2412_INFORM0); + __raw_writel(virt_to_phys(s3c2443_cpu_resume), S3C2412_INFORM1); + + /* + * This two function pointers are used by the platform driver (see [1]) + * for preparing and entering in the sleep mode. + * [1] arch/arm/plat-s3c24xx/pm.c + */ + pm_cpu_prep = s3c2443_pm_prepare; + pm_cpu_sleep = s3c2443_cpu_suspend; + + /* Save the registers */ + cc9m2443_pm_save(); + + return 0; +} + +static int cc9m2443js_pm_resume(struct sys_device *sd) +{ + __raw_writel(0x0, S3C2412_INFORM0); + __raw_writel(0x0, S3C2412_INFORM1); + __raw_writel(0x0, S3C2412_INFORM2); + + cc9m2443_pm_restore(); + + return 0; +} + +/* Internal system class/device for accessing to the PM-functions */ +static struct sysdev_class cc9m2443js_pm_sysclass = { + .name = "cc9m2443js-pm", + .suspend = cc9m2443js_pm_suspend, + .resume = cc9m2443js_pm_resume, +}; + +static struct sys_device cc9m2443js_pm_sysdev = { + .cls = &cc9m2443js_pm_sysclass, +}; + +/* This function generates the required sysclass for our platform */ +void __init cc9m2443js_pm_init(void) +{ + sysdev_class_register(&cc9m2443js_pm_sysclass); + sysdev_register(&cc9m2443js_pm_sysdev); + + /* This is the main function for the PM of the Samsung-platforms */ + s3c2410_pm_init(); +} |