From 1e664d4740009ac49c2a2f85ffc994c12ee040bd Mon Sep 17 00:00:00 2001 From: merge Date: Wed, 3 Dec 2008 11:20:14 +0000 Subject: MERGE-via-pending-tracking-hist-MERGE-via-stable-tracking-MERGE-via-mokopatches-tracking-MERGE-via-master-1228302402-1228302766-1228303138 pending-tracking-hist top was MERGE-via-stable-tracking-MERGE-via-mokopatches-tracking-MERGE-via-master-1228302402-1228302766-1228303138 / 27d86638fe294ef1d1a8f527564ec37bb20e7ef2 ... parent commitmessage: From: merge MERGE-via-stable-tracking-hist-MERGE-via-mokopatches-tracking-MERGE-via-master-1228302402-1228302766 stable-tracking-hist top was MERGE-via-mokopatches-tracking-MERGE-via-master-1228302402-1228302766 / 66e84c4be853030f1cea816a124cf76a741ecc08 ... parent commitmessage: From: merge MERGE-via-mokopatches-tracking-hist-MERGE-via-master-1228302402 mokopatches-tracking-hist top was MERGE-via-master-1228302402 / de9177f7bd127e9b6fa6213018c7c731b8ca0d0c ... parent commitmessage: From: merge MERGE-via-master- master top was / 3838a80929f91d35c6d987e518bf9ea397f3e13c ... parent commitmessage: From: Andy Green fix-wm8753-DBG.patch Signed-off-by: Andy Green --- arch/arm/plat-s3c/pm.c | 375 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 375 insertions(+) create mode 100644 arch/arm/plat-s3c/pm.c (limited to 'arch/arm/plat-s3c/pm.c') diff --git a/arch/arm/plat-s3c/pm.c b/arch/arm/plat-s3c/pm.c new file mode 100644 index 00000000000..ea03e5e75da --- /dev/null +++ b/arch/arm/plat-s3c/pm.c @@ -0,0 +1,375 @@ +/* linux/arch/arm/plat-s3c/pm.c + * + * Copyright 2008 Openmoko, Inc. + * Copyright 2004,2006,2008 Simtec Electronics + * Ben Dooks + * 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 +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +/* 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. */ + +unsigned char pm_uart_udivslot; + +#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); + + if (pm_uart_udivslot) + save->udivslot = __raw_readl(regs + S3C2443_DIVSLOT); + + S3C_PMDBG("UART[%d]: ULCON=%04x, UCON=%04x, UFCON=%04x, UBRDIV=%04x\n", + uart, save->ulcon, save->ucon, save->ufcon, save->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); + + s3c_pm_arch_update_uart(regs, save); + + __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); + + if (pm_uart_udivslot) + __raw_writel(save->udivslot, regs + S3C2443_DIVSLOT); +} + +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 simialr 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<