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