/* linux/arch/arm/plat-s3c/pm.c
*
* Copyright 2008 Openmoko, Inc.
- * Copyright 2004,2006,2008 Simtec Electronics
+ * Copyright 2004-2008 Simtec Electronics
* Ben Dooks <ben@simtec.co.uk>
* http://armlinux.simtec.co.uk/
*
#include <asm/cacheflush.h>
#include <mach/hardware.h>
+#include <mach/map.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>
/* Save the UART configurations if we are configured for debug. */
+unsigned char pm_uart_udivslot;
+
#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(S3C_VA_UART0),
- SAVE_UART(S3C_VA_UART1),
-#ifndef CONFIG_CPU_S3C2400
- SAVE_UART(S3C_VA_UART2),
-#endif
-};
+struct pm_uart_save uart_save[CONFIG_SERIAL_SAMSUNG_UARTS];
-static void s3c_pm_save_uart(void)
+static void s3c_pm_save_uart(unsigned int uart, struct pm_uart_save *save)
{
- s3c_pm_do_save(uart_save, ARRAY_SIZE(uart_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_restore_uart(void)
+static void s3c_pm_save_uarts(void)
{
- s3c_pm_do_restore(uart_save, ARRAY_SIZE(uart_save));
+ 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_uart(void) { }
-static void s3c_pm_restore_uart(void) { }
+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
static int s3c_pm_enter(suspend_state_t state)
{
- unsigned long regs_save[16];
+ static unsigned long regs_save[16];
/* ensure the debug is initialised (if enabled) */
return -EINVAL;
}
- /* prepare check area if configured */
-
- s3c_pm_check_prepare();
-
/* store the physical address of the register recovery block */
s3c_sleep_save_phys = virt_to_phys(regs_save);
/* save all necessary core registers not covered by the drivers */
s3c_pm_save_gpios();
- s3c_pm_save_uart();
+ s3c_pm_save_uarts();
s3c_pm_save_core();
/* set the irq configuration for wake */
s3c_pm_arch_stop_clocks();
- /* 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 */
+ /* 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. */
- if (s3c2410_cpu_save(regs_save) == 0) {
- flush_cache_all();
- pm_cpu_sleep();
- }
+ s3c_cpu_save(regs_save);
/* restore the cpu state using the kernel's cpu init code. */
/* restore the system state */
s3c_pm_restore_core();
- s3c_pm_restore_uart();
+ s3c_pm_restore_uarts();
s3c_pm_restore_gpios();
s3c_pm_debug_init();
S3C_PMDBG("%s: post sleep, preparing to return\n", __func__);
+ /* LEDs should now be 1110 */
+ s3c_pm_debug_smdkled(1 << 1, 0);
+
s3c_pm_check_restore();
/* ok, let's return from sleep */
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,
};
-/* s3c2410_pm_init
+/* s3c_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)
+int __init s3c_pm_init(void)
{
printk("S3C Power Management, Copyright 2004 Simtec Electronics\n");