1 /*
2  * SA1100 Power Management Routines
3  *
4  * Copyright (c) 2001 Cliff Brake <cbrake@accelent.com>
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License.
8  *
9  * History:
10  *
11  * 2001-02-06:	Cliff Brake         Initial code
12  *
13  * 2001-02-25:	Sukjae Cho <sjcho@east.isi.edu> &
14  * 		Chester Kuo <chester@linux.org.tw>
15  * 			Save more value for the resume function! Support
16  * 			Bitsy/Assabet/Freebird board
17  *
18  * 2001-08-29:	Nicolas Pitre <nico@cam.org>
19  * 			Cleaned up, pushed platform dependent stuff
20  * 			in the platform specific files.
21  *
22  * 2002-05-27:	Nicolas Pitre	Killed sleep.h and the kmalloced save array.
23  * 				Storage is local on the stack now.
24  */
25 #include <linux/config.h>
26 #include <linux/init.h>
27 #include <linux/pm.h>
28 #include <linux/sched.h>
29 #include <linux/interrupt.h>
30 #include <linux/sysctl.h>
31 #include <linux/errno.h>
32 
33 #include <asm/hardware.h>
34 #include <asm/memory.h>
35 #include <asm/system.h>
36 #include <asm/leds.h>
37 
38 
39 /*
40  * Debug macros
41  */
42 #undef DEBUG
43 
44 extern void sa1100_cpu_suspend(void);
45 extern void sa1100_cpu_resume(void);
46 
47 #define SAVE(x)		sleep_save[SLEEP_SAVE_##x] = x
48 #define RESTORE(x)	x = sleep_save[SLEEP_SAVE_##x]
49 
50 /*
51  * List of global SA11x0 peripheral registers to preserve.
52  * More ones like CP and general purpose register values are preserved
53  * with the stack location in sleep.S.
54  */
55 enum {	SLEEP_SAVE_START = 0,
56 
57 	SLEEP_SAVE_OSCR, SLEEP_SAVE_OIER,
58 	SLEEP_SAVE_OSMR0, SLEEP_SAVE_OSMR1, SLEEP_SAVE_OSMR2, SLEEP_SAVE_OSMR3,
59 
60 	SLEEP_SAVE_GPDR, SLEEP_SAVE_GRER, SLEEP_SAVE_GFER, SLEEP_SAVE_GAFR,
61 	SLEEP_SAVE_PPDR, SLEEP_SAVE_PPSR, SLEEP_SAVE_PPAR, SLEEP_SAVE_PSDR,
62 
63 	SLEEP_SAVE_ICMR,
64 	SLEEP_SAVE_Ser1SDCR0,
65 
66 	SLEEP_SAVE_SIZE
67 };
68 
69 
pm_do_suspend(void)70 int pm_do_suspend(void)
71 {
72 	unsigned long sleep_save[SLEEP_SAVE_SIZE];
73 
74 	cli();
75 
76 	leds_event(led_stop);
77 
78 	/* preserve current time */
79 	RCNR = xtime.tv_sec;
80 
81 	/* save vital registers */
82 	SAVE(OSCR);
83 	SAVE(OSMR0);
84 	SAVE(OSMR1);
85 	SAVE(OSMR2);
86 	SAVE(OSMR3);
87 	SAVE(OIER);
88 
89 	SAVE(GPDR);
90 	SAVE(GRER);
91 	SAVE(GFER);
92 	SAVE(GAFR);
93 
94 	SAVE(PPDR);
95 	SAVE(PPSR);
96 	SAVE(PPAR);
97 	SAVE(PSDR);
98 
99 	SAVE(Ser1SDCR0);
100 
101 	SAVE(ICMR);
102 
103 	/* ... maybe a global variable initialized by arch code to set this? */
104 	GRER = PWER;
105 	GFER = 0;
106 	GEDR = GEDR;
107 
108 	/* Clear previous reset status */
109 	RCSR = RCSR_HWR | RCSR_SWR | RCSR_WDR | RCSR_SMR;
110 
111 	/* set resume return address */
112 	PSPR = virt_to_phys(sa1100_cpu_resume);
113 
114 	/* go zzz */
115 	sa1100_cpu_suspend();
116 
117 	/* ensure not to come back here if it wasn't intended */
118 	PSPR = 0;
119 
120 #ifdef DEBUG
121 	printk(KERN_DEBUG "*** made it back from resume\n");
122 #endif
123 
124 	/* restore registers */
125 	RESTORE(GPDR);
126 	RESTORE(GRER);
127 	RESTORE(GFER);
128 	RESTORE(GAFR);
129 
130 	/* clear any edge detect bit */
131 	GEDR = GEDR;
132 
133 	RESTORE(PPDR);
134 	RESTORE(PPSR);
135 	RESTORE(PPAR);
136 	RESTORE(PSDR);
137 
138 	RESTORE(Ser1SDCR0);
139 
140 	PSSR = PSSR_PH;
141 
142 	RESTORE(OSMR0);
143 	RESTORE(OSMR1);
144 	RESTORE(OSMR2);
145 	RESTORE(OSMR3);
146 	RESTORE(OSCR);
147 	RESTORE(OIER);
148 
149 	ICLR = 0;
150 	ICCR = 1;
151 	RESTORE(ICMR);
152 
153 	/* restore current time */
154 	xtime.tv_sec = RCNR;
155 
156 	leds_event(led_start);
157 
158 	sti();
159 
160 	/*
161 	 * Restore the CPU frequency settings.
162 	 */
163 #ifdef CONFIG_CPU_FREQ
164 	cpufreq_restore();
165 #endif
166 
167 	return 0;
168 }
169 
sleep_phys_sp(void * sp)170 unsigned long sleep_phys_sp(void *sp)
171 {
172 	return virt_to_phys(sp);
173 }
174 
175 #ifdef CONFIG_SYSCTL
176 /*
177  * ARGH!  ACPI people defined CTL_ACPI in linux/acpi.h rather than
178  * linux/sysctl.h.
179  *
180  * This means our interface here won't survive long - it needs a new
181  * interface.  Quick hack to get this working - use sysctl id 9999.
182  */
183 #warning ACPI broke the kernel, this interface needs to be fixed up.
184 #define CTL_ACPI 9999
185 #define ACPI_S1_SLP_TYP 19
186 
187 /*
188  * Send us to sleep.
189  */
sysctl_pm_do_suspend(void)190 static int sysctl_pm_do_suspend(void)
191 {
192 	int retval;
193 
194 	retval = pm_send_all(PM_SUSPEND, (void *)3);
195 
196 	if (retval == 0) {
197 		retval = pm_do_suspend();
198 
199 		pm_send_all(PM_RESUME, (void *)0);
200 	}
201 
202 	return retval;
203 }
204 
205 static struct ctl_table pm_table[] =
206 {
207 	{ACPI_S1_SLP_TYP, "suspend", NULL, 0, 0600, NULL, (proc_handler *)&sysctl_pm_do_suspend},
208 	{0}
209 };
210 
211 static struct ctl_table pm_dir_table[] =
212 {
213 	{CTL_ACPI, "pm", NULL, 0, 0555, pm_table},
214 	{0}
215 };
216 
217 /*
218  * Initialize power interface
219  */
pm_init(void)220 static int __init pm_init(void)
221 {
222 	register_sysctl_table(pm_dir_table, 1);
223 	return 0;
224 }
225 
226 __initcall(pm_init);
227 
228 #endif
229