1/* linux/arch/arm/mach-s3c2410/pm.c 2 * 3 * Copyright (c) 2006 Simtec Electronics 4 * Ben Dooks <ben@simtec.co.uk> 5 * 6 * S3C2410 (and compatible) Power Manager (Suspend-To-RAM) support 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2 of the License, or 11 * (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21*/ 22 23#include <linux/init.h> 24#include <linux/suspend.h> 25#include <linux/errno.h> 26#include <linux/time.h> 27#include <linux/device.h> 28#include <linux/syscore_ops.h> 29#include <linux/gpio.h> 30#include <linux/io.h> 31 32#include <asm/mach-types.h> 33 34#include <mach/hardware.h> 35#include <mach/regs-gpio.h> 36#include <mach/gpio-samsung.h> 37 38#include <plat/gpio-cfg.h> 39#include <plat/cpu.h> 40#include <plat/pm.h> 41 42#include "h1940.h" 43 44static void s3c2410_pm_prepare(void) 45{ 46 /* ensure at least GSTATUS3 has the resume address */ 47 48 __raw_writel(virt_to_phys(s3c_cpu_resume), S3C2410_GSTATUS3); 49 50 S3C_PMDBG("GSTATUS3 0x%08x\n", __raw_readl(S3C2410_GSTATUS3)); 51 S3C_PMDBG("GSTATUS4 0x%08x\n", __raw_readl(S3C2410_GSTATUS4)); 52 53 if (machine_is_h1940()) { 54 void *base = phys_to_virt(H1940_SUSPEND_CHECK); 55 unsigned long ptr; 56 unsigned long calc = 0; 57 58 /* generate check for the bootloader to check on resume */ 59 60 for (ptr = 0; ptr < 0x40000; ptr += 0x400) 61 calc += __raw_readl(base+ptr); 62 63 __raw_writel(calc, phys_to_virt(H1940_SUSPEND_CHECKSUM)); 64 } 65 66 /* RX3715 and RX1950 use similar to H1940 code and the 67 * same offsets for resume and checksum pointers */ 68 69 if (machine_is_rx3715() || machine_is_rx1950()) { 70 void *base = phys_to_virt(H1940_SUSPEND_CHECK); 71 unsigned long ptr; 72 unsigned long calc = 0; 73 74 /* generate check for the bootloader to check on resume */ 75 76 for (ptr = 0; ptr < 0x40000; ptr += 0x4) 77 calc += __raw_readl(base+ptr); 78 79 __raw_writel(calc, phys_to_virt(H1940_SUSPEND_CHECKSUM)); 80 } 81 82 if (machine_is_aml_m5900()) { 83 gpio_request_one(S3C2410_GPF(2), GPIOF_OUT_INIT_HIGH, NULL); 84 gpio_free(S3C2410_GPF(2)); 85 } 86 87 if (machine_is_rx1950()) { 88 /* According to S3C2442 user's manual, page 7-17, 89 * when the system is operating in NAND boot mode, 90 * the hardware pin configuration - EINT[23:21] – 91 * must be set as input for starting up after 92 * wakeup from sleep mode 93 */ 94 s3c_gpio_cfgpin(S3C2410_GPG(13), S3C2410_GPIO_INPUT); 95 s3c_gpio_cfgpin(S3C2410_GPG(14), S3C2410_GPIO_INPUT); 96 s3c_gpio_cfgpin(S3C2410_GPG(15), S3C2410_GPIO_INPUT); 97 } 98} 99 100static void s3c2410_pm_resume(void) 101{ 102 unsigned long tmp; 103 104 /* unset the return-from-sleep flag, to ensure reset */ 105 106 tmp = __raw_readl(S3C2410_GSTATUS2); 107 tmp &= S3C2410_GSTATUS2_OFFRESET; 108 __raw_writel(tmp, S3C2410_GSTATUS2); 109 110 if (machine_is_aml_m5900()) { 111 gpio_request_one(S3C2410_GPF(2), GPIOF_OUT_INIT_LOW, NULL); 112 gpio_free(S3C2410_GPF(2)); 113 } 114} 115 116struct syscore_ops s3c2410_pm_syscore_ops = { 117 .resume = s3c2410_pm_resume, 118}; 119 120static int s3c2410_pm_add(struct device *dev, struct subsys_interface *sif) 121{ 122 pm_cpu_prep = s3c2410_pm_prepare; 123 pm_cpu_sleep = s3c2410_cpu_suspend; 124 125 return 0; 126} 127 128#if defined(CONFIG_CPU_S3C2410) 129static struct subsys_interface s3c2410_pm_interface = { 130 .name = "s3c2410_pm", 131 .subsys = &s3c2410_subsys, 132 .add_dev = s3c2410_pm_add, 133}; 134 135/* register ourselves */ 136 137static int __init s3c2410_pm_drvinit(void) 138{ 139 return subsys_interface_register(&s3c2410_pm_interface); 140} 141 142arch_initcall(s3c2410_pm_drvinit); 143 144static struct subsys_interface s3c2410a_pm_interface = { 145 .name = "s3c2410a_pm", 146 .subsys = &s3c2410a_subsys, 147 .add_dev = s3c2410_pm_add, 148}; 149 150static int __init s3c2410a_pm_drvinit(void) 151{ 152 return subsys_interface_register(&s3c2410a_pm_interface); 153} 154 155arch_initcall(s3c2410a_pm_drvinit); 156#endif 157 158#if defined(CONFIG_CPU_S3C2440) 159static struct subsys_interface s3c2440_pm_interface = { 160 .name = "s3c2440_pm", 161 .subsys = &s3c2440_subsys, 162 .add_dev = s3c2410_pm_add, 163}; 164 165static int __init s3c2440_pm_drvinit(void) 166{ 167 return subsys_interface_register(&s3c2440_pm_interface); 168} 169 170arch_initcall(s3c2440_pm_drvinit); 171#endif 172 173#if defined(CONFIG_CPU_S3C2442) 174static struct subsys_interface s3c2442_pm_interface = { 175 .name = "s3c2442_pm", 176 .subsys = &s3c2442_subsys, 177 .add_dev = s3c2410_pm_add, 178}; 179 180static int __init s3c2442_pm_drvinit(void) 181{ 182 return subsys_interface_register(&s3c2442_pm_interface); 183} 184 185arch_initcall(s3c2442_pm_drvinit); 186#endif 187