import of upstream 2.4.34.4 from kernel.org
[linux-2.4.git] / arch / arm / mach-sa1100 / pm.c
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
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
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  */
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  */
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