diff options
author | Ingo Molnar <mingo@elte.hu> | 2008-10-12 09:05:39 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2008-10-12 09:05:39 -0400 |
commit | a9b9e81c915e4a57ac3b21d1a7fa7ff184639780 (patch) | |
tree | 98304395fbb5b9c74fca35b196cd414c1949f280 /arch/mips/alchemy/common/power.c | |
parent | a8b71a2810386a5ac8f43d2095fe3355f0d8db37 (diff) | |
parent | fd048088306656824958e7783ffcee27e241b361 (diff) |
Merge branch 'linus' into x86/memory-corruption-check
Diffstat (limited to 'arch/mips/alchemy/common/power.c')
-rw-r--r-- | arch/mips/alchemy/common/power.c | 465 |
1 files changed, 465 insertions, 0 deletions
diff --git a/arch/mips/alchemy/common/power.c b/arch/mips/alchemy/common/power.c new file mode 100644 index 000000000000..bd854a6d1d89 --- /dev/null +++ b/arch/mips/alchemy/common/power.c | |||
@@ -0,0 +1,465 @@ | |||
1 | /* | ||
2 | * BRIEF MODULE DESCRIPTION | ||
3 | * Au1xx0 Power Management routines. | ||
4 | * | ||
5 | * Copyright 2001, 2008 MontaVista Software Inc. | ||
6 | * Author: MontaVista Software, Inc. <source@mvista.com> | ||
7 | * | ||
8 | * Some of the routines are right out of init/main.c, whose | ||
9 | * copyrights apply here. | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify it | ||
12 | * under the terms of the GNU General Public License as published by the | ||
13 | * Free Software Foundation; either version 2 of the License, or (at your | ||
14 | * option) any later version. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED | ||
17 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
18 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN | ||
19 | * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||
20 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||
21 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | ||
22 | * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | ||
23 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||
25 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | * | ||
27 | * You should have received a copy of the GNU General Public License along | ||
28 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
29 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
30 | */ | ||
31 | |||
32 | #include <linux/init.h> | ||
33 | #include <linux/pm.h> | ||
34 | #include <linux/sysctl.h> | ||
35 | #include <linux/jiffies.h> | ||
36 | |||
37 | #include <asm/uaccess.h> | ||
38 | #include <asm/cacheflush.h> | ||
39 | #include <asm/mach-au1x00/au1000.h> | ||
40 | |||
41 | #ifdef CONFIG_PM | ||
42 | |||
43 | #define DEBUG 1 | ||
44 | #ifdef DEBUG | ||
45 | #define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __func__, ## args) | ||
46 | #else | ||
47 | #define DPRINTK(fmt, args...) | ||
48 | #endif | ||
49 | |||
50 | static void au1000_calibrate_delay(void); | ||
51 | |||
52 | extern unsigned long save_local_and_disable(int controller); | ||
53 | extern void restore_local_and_enable(int controller, unsigned long mask); | ||
54 | extern void local_enable_irq(unsigned int irq_nr); | ||
55 | |||
56 | static DEFINE_SPINLOCK(pm_lock); | ||
57 | |||
58 | /* | ||
59 | * We need to save/restore a bunch of core registers that are | ||
60 | * either volatile or reset to some state across a processor sleep. | ||
61 | * If reading a register doesn't provide a proper result for a | ||
62 | * later restore, we have to provide a function for loading that | ||
63 | * register and save a copy. | ||
64 | * | ||
65 | * We only have to save/restore registers that aren't otherwise | ||
66 | * done as part of a driver pm_* function. | ||
67 | */ | ||
68 | static unsigned int sleep_aux_pll_cntrl; | ||
69 | static unsigned int sleep_cpu_pll_cntrl; | ||
70 | static unsigned int sleep_pin_function; | ||
71 | static unsigned int sleep_uart0_inten; | ||
72 | static unsigned int sleep_uart0_fifoctl; | ||
73 | static unsigned int sleep_uart0_linectl; | ||
74 | static unsigned int sleep_uart0_clkdiv; | ||
75 | static unsigned int sleep_uart0_enable; | ||
76 | static unsigned int sleep_usbhost_enable; | ||
77 | static unsigned int sleep_usbdev_enable; | ||
78 | static unsigned int sleep_static_memctlr[4][3]; | ||
79 | |||
80 | /* | ||
81 | * Define this to cause the value you write to /proc/sys/pm/sleep to | ||
82 | * set the TOY timer for the amount of time you want to sleep. | ||
83 | * This is done mainly for testing, but may be useful in other cases. | ||
84 | * The value is number of 32KHz ticks to sleep. | ||
85 | */ | ||
86 | #define SLEEP_TEST_TIMEOUT 1 | ||
87 | #ifdef SLEEP_TEST_TIMEOUT | ||
88 | static int sleep_ticks; | ||
89 | void wakeup_counter0_set(int ticks); | ||
90 | #endif | ||
91 | |||
92 | static void save_core_regs(void) | ||
93 | { | ||
94 | extern void save_au1xxx_intctl(void); | ||
95 | extern void pm_eth0_shutdown(void); | ||
96 | |||
97 | /* | ||
98 | * Do the serial ports.....these really should be a pm_* | ||
99 | * registered function by the driver......but of course the | ||
100 | * standard serial driver doesn't understand our Au1xxx | ||
101 | * unique registers. | ||
102 | */ | ||
103 | sleep_uart0_inten = au_readl(UART0_ADDR + UART_IER); | ||
104 | sleep_uart0_fifoctl = au_readl(UART0_ADDR + UART_FCR); | ||
105 | sleep_uart0_linectl = au_readl(UART0_ADDR + UART_LCR); | ||
106 | sleep_uart0_clkdiv = au_readl(UART0_ADDR + UART_CLK); | ||
107 | sleep_uart0_enable = au_readl(UART0_ADDR + UART_MOD_CNTRL); | ||
108 | |||
109 | /* Shutdown USB host/device. */ | ||
110 | sleep_usbhost_enable = au_readl(USB_HOST_CONFIG); | ||
111 | |||
112 | /* There appears to be some undocumented reset register.... */ | ||
113 | au_writel(0, 0xb0100004); au_sync(); | ||
114 | au_writel(0, USB_HOST_CONFIG); au_sync(); | ||
115 | |||
116 | sleep_usbdev_enable = au_readl(USBD_ENABLE); | ||
117 | au_writel(0, USBD_ENABLE); au_sync(); | ||
118 | |||
119 | /* Save interrupt controller state. */ | ||
120 | save_au1xxx_intctl(); | ||
121 | |||
122 | /* Clocks and PLLs. */ | ||
123 | sleep_aux_pll_cntrl = au_readl(SYS_AUXPLL); | ||
124 | |||
125 | /* | ||
126 | * We don't really need to do this one, but unless we | ||
127 | * write it again it won't have a valid value if we | ||
128 | * happen to read it. | ||
129 | */ | ||
130 | sleep_cpu_pll_cntrl = au_readl(SYS_CPUPLL); | ||
131 | |||
132 | sleep_pin_function = au_readl(SYS_PINFUNC); | ||
133 | |||
134 | /* Save the static memory controller configuration. */ | ||
135 | sleep_static_memctlr[0][0] = au_readl(MEM_STCFG0); | ||
136 | sleep_static_memctlr[0][1] = au_readl(MEM_STTIME0); | ||
137 | sleep_static_memctlr[0][2] = au_readl(MEM_STADDR0); | ||
138 | sleep_static_memctlr[1][0] = au_readl(MEM_STCFG1); | ||
139 | sleep_static_memctlr[1][1] = au_readl(MEM_STTIME1); | ||
140 | sleep_static_memctlr[1][2] = au_readl(MEM_STADDR1); | ||
141 | sleep_static_memctlr[2][0] = au_readl(MEM_STCFG2); | ||
142 | sleep_static_memctlr[2][1] = au_readl(MEM_STTIME2); | ||
143 | sleep_static_memctlr[2][2] = au_readl(MEM_STADDR2); | ||
144 | sleep_static_memctlr[3][0] = au_readl(MEM_STCFG3); | ||
145 | sleep_static_memctlr[3][1] = au_readl(MEM_STTIME3); | ||
146 | sleep_static_memctlr[3][2] = au_readl(MEM_STADDR3); | ||
147 | } | ||
148 | |||
149 | static void restore_core_regs(void) | ||
150 | { | ||
151 | extern void restore_au1xxx_intctl(void); | ||
152 | extern void wakeup_counter0_adjust(void); | ||
153 | |||
154 | au_writel(sleep_aux_pll_cntrl, SYS_AUXPLL); au_sync(); | ||
155 | au_writel(sleep_cpu_pll_cntrl, SYS_CPUPLL); au_sync(); | ||
156 | au_writel(sleep_pin_function, SYS_PINFUNC); au_sync(); | ||
157 | |||
158 | /* Restore the static memory controller configuration. */ | ||
159 | au_writel(sleep_static_memctlr[0][0], MEM_STCFG0); | ||
160 | au_writel(sleep_static_memctlr[0][1], MEM_STTIME0); | ||
161 | au_writel(sleep_static_memctlr[0][2], MEM_STADDR0); | ||
162 | au_writel(sleep_static_memctlr[1][0], MEM_STCFG1); | ||
163 | au_writel(sleep_static_memctlr[1][1], MEM_STTIME1); | ||
164 | au_writel(sleep_static_memctlr[1][2], MEM_STADDR1); | ||
165 | au_writel(sleep_static_memctlr[2][0], MEM_STCFG2); | ||
166 | au_writel(sleep_static_memctlr[2][1], MEM_STTIME2); | ||
167 | au_writel(sleep_static_memctlr[2][2], MEM_STADDR2); | ||
168 | au_writel(sleep_static_memctlr[3][0], MEM_STCFG3); | ||
169 | au_writel(sleep_static_memctlr[3][1], MEM_STTIME3); | ||
170 | au_writel(sleep_static_memctlr[3][2], MEM_STADDR3); | ||
171 | |||
172 | /* | ||
173 | * Enable the UART if it was enabled before sleep. | ||
174 | * I guess I should define module control bits........ | ||
175 | */ | ||
176 | if (sleep_uart0_enable & 0x02) { | ||
177 | au_writel(0, UART0_ADDR + UART_MOD_CNTRL); au_sync(); | ||
178 | au_writel(1, UART0_ADDR + UART_MOD_CNTRL); au_sync(); | ||
179 | au_writel(3, UART0_ADDR + UART_MOD_CNTRL); au_sync(); | ||
180 | au_writel(sleep_uart0_inten, UART0_ADDR + UART_IER); au_sync(); | ||
181 | au_writel(sleep_uart0_fifoctl, UART0_ADDR + UART_FCR); au_sync(); | ||
182 | au_writel(sleep_uart0_linectl, UART0_ADDR + UART_LCR); au_sync(); | ||
183 | au_writel(sleep_uart0_clkdiv, UART0_ADDR + UART_CLK); au_sync(); | ||
184 | } | ||
185 | |||
186 | restore_au1xxx_intctl(); | ||
187 | wakeup_counter0_adjust(); | ||
188 | } | ||
189 | |||
190 | unsigned long suspend_mode; | ||
191 | |||
192 | void wakeup_from_suspend(void) | ||
193 | { | ||
194 | suspend_mode = 0; | ||
195 | } | ||
196 | |||
197 | int au_sleep(void) | ||
198 | { | ||
199 | unsigned long wakeup, flags; | ||
200 | extern void save_and_sleep(void); | ||
201 | |||
202 | spin_lock_irqsave(&pm_lock, flags); | ||
203 | |||
204 | save_core_regs(); | ||
205 | |||
206 | flush_cache_all(); | ||
207 | |||
208 | /** | ||
209 | ** The code below is all system dependent and we should probably | ||
210 | ** have a function call out of here to set this up. You need | ||
211 | ** to configure the GPIO or timer interrupts that will bring | ||
212 | ** you out of sleep. | ||
213 | ** For testing, the TOY counter wakeup is useful. | ||
214 | **/ | ||
215 | #if 0 | ||
216 | au_writel(au_readl(SYS_PINSTATERD) & ~(1 << 11), SYS_PINSTATERD); | ||
217 | |||
218 | /* GPIO 6 can cause a wake up event */ | ||
219 | wakeup = au_readl(SYS_WAKEMSK); | ||
220 | wakeup &= ~(1 << 8); /* turn off match20 wakeup */ | ||
221 | wakeup |= 1 << 6; /* turn on GPIO 6 wakeup */ | ||
222 | #else | ||
223 | /* For testing, allow match20 to wake us up. */ | ||
224 | #ifdef SLEEP_TEST_TIMEOUT | ||
225 | wakeup_counter0_set(sleep_ticks); | ||
226 | #endif | ||
227 | wakeup = 1 << 8; /* turn on match20 wakeup */ | ||
228 | wakeup = 0; | ||
229 | #endif | ||
230 | au_writel(1, SYS_WAKESRC); /* clear cause */ | ||
231 | au_sync(); | ||
232 | au_writel(wakeup, SYS_WAKEMSK); | ||
233 | au_sync(); | ||
234 | |||
235 | save_and_sleep(); | ||
236 | |||
237 | /* | ||
238 | * After a wakeup, the cpu vectors back to 0x1fc00000, so | ||
239 | * it's up to the boot code to get us back here. | ||
240 | */ | ||
241 | restore_core_regs(); | ||
242 | spin_unlock_irqrestore(&pm_lock, flags); | ||
243 | return 0; | ||
244 | } | ||
245 | |||
246 | static int pm_do_sleep(ctl_table *ctl, int write, struct file *file, | ||
247 | void __user *buffer, size_t *len, loff_t *ppos) | ||
248 | { | ||
249 | #ifdef SLEEP_TEST_TIMEOUT | ||
250 | #define TMPBUFLEN2 16 | ||
251 | char buf[TMPBUFLEN2], *p; | ||
252 | #endif | ||
253 | |||
254 | if (!write) | ||
255 | *len = 0; | ||
256 | else { | ||
257 | #ifdef SLEEP_TEST_TIMEOUT | ||
258 | if (*len > TMPBUFLEN2 - 1) | ||
259 | return -EFAULT; | ||
260 | if (copy_from_user(buf, buffer, *len)) | ||
261 | return -EFAULT; | ||
262 | buf[*len] = 0; | ||
263 | p = buf; | ||
264 | sleep_ticks = simple_strtoul(p, &p, 0); | ||
265 | #endif | ||
266 | |||
267 | au_sleep(); | ||
268 | } | ||
269 | return 0; | ||
270 | } | ||
271 | |||
272 | static int pm_do_freq(ctl_table *ctl, int write, struct file *file, | ||
273 | void __user *buffer, size_t *len, loff_t *ppos) | ||
274 | { | ||
275 | int retval = 0, i; | ||
276 | unsigned long val, pll; | ||
277 | #define TMPBUFLEN 64 | ||
278 | #define MAX_CPU_FREQ 396 | ||
279 | char buf[TMPBUFLEN], *p; | ||
280 | unsigned long flags, intc0_mask, intc1_mask; | ||
281 | unsigned long old_baud_base, old_cpu_freq, old_clk, old_refresh; | ||
282 | unsigned long new_baud_base, new_cpu_freq, new_clk, new_refresh; | ||
283 | unsigned long baud_rate; | ||
284 | |||
285 | spin_lock_irqsave(&pm_lock, flags); | ||
286 | if (!write) | ||
287 | *len = 0; | ||
288 | else { | ||
289 | /* Parse the new frequency */ | ||
290 | if (*len > TMPBUFLEN - 1) { | ||
291 | spin_unlock_irqrestore(&pm_lock, flags); | ||
292 | return -EFAULT; | ||
293 | } | ||
294 | if (copy_from_user(buf, buffer, *len)) { | ||
295 | spin_unlock_irqrestore(&pm_lock, flags); | ||
296 | return -EFAULT; | ||
297 | } | ||
298 | buf[*len] = 0; | ||
299 | p = buf; | ||
300 | val = simple_strtoul(p, &p, 0); | ||
301 | if (val > MAX_CPU_FREQ) { | ||
302 | spin_unlock_irqrestore(&pm_lock, flags); | ||
303 | return -EFAULT; | ||
304 | } | ||
305 | |||
306 | pll = val / 12; | ||
307 | if ((pll > 33) || (pll < 7)) { /* 396 MHz max, 84 MHz min */ | ||
308 | /* Revisit this for higher speed CPUs */ | ||
309 | spin_unlock_irqrestore(&pm_lock, flags); | ||
310 | return -EFAULT; | ||
311 | } | ||
312 | |||
313 | old_baud_base = get_au1x00_uart_baud_base(); | ||
314 | old_cpu_freq = get_au1x00_speed(); | ||
315 | |||
316 | new_cpu_freq = pll * 12 * 1000000; | ||
317 | new_baud_base = (new_cpu_freq / (2 * ((int)(au_readl(SYS_POWERCTRL) | ||
318 | & 0x03) + 2) * 16)); | ||
319 | set_au1x00_speed(new_cpu_freq); | ||
320 | set_au1x00_uart_baud_base(new_baud_base); | ||
321 | |||
322 | old_refresh = au_readl(MEM_SDREFCFG) & 0x1ffffff; | ||
323 | new_refresh = ((old_refresh * new_cpu_freq) / old_cpu_freq) | | ||
324 | (au_readl(MEM_SDREFCFG) & ~0x1ffffff); | ||
325 | |||
326 | au_writel(pll, SYS_CPUPLL); | ||
327 | au_sync_delay(1); | ||
328 | au_writel(new_refresh, MEM_SDREFCFG); | ||
329 | au_sync_delay(1); | ||
330 | |||
331 | for (i = 0; i < 4; i++) | ||
332 | if (au_readl(UART_BASE + UART_MOD_CNTRL + | ||
333 | i * 0x00100000) == 3) { | ||
334 | old_clk = au_readl(UART_BASE + UART_CLK + | ||
335 | i * 0x00100000); | ||
336 | baud_rate = old_baud_base / old_clk; | ||
337 | /* | ||
338 | * We won't get an exact baud rate and the error | ||
339 | * could be significant enough that our new | ||
340 | * calculation will result in a clock that will | ||
341 | * give us a baud rate that's too far off from | ||
342 | * what we really want. | ||
343 | */ | ||
344 | if (baud_rate > 100000) | ||
345 | baud_rate = 115200; | ||
346 | else if (baud_rate > 50000) | ||
347 | baud_rate = 57600; | ||
348 | else if (baud_rate > 30000) | ||
349 | baud_rate = 38400; | ||
350 | else if (baud_rate > 17000) | ||
351 | baud_rate = 19200; | ||
352 | else | ||
353 | baud_rate = 9600; | ||
354 | new_clk = new_baud_base / baud_rate; | ||
355 | au_writel(new_clk, UART_BASE + UART_CLK + | ||
356 | i * 0x00100000); | ||
357 | au_sync_delay(10); | ||
358 | } | ||
359 | } | ||
360 | |||
361 | /* | ||
362 | * We don't want _any_ interrupts other than match20. Otherwise our | ||
363 | * au1000_calibrate_delay() calculation will be off, potentially a lot. | ||
364 | */ | ||
365 | intc0_mask = save_local_and_disable(0); | ||
366 | intc1_mask = save_local_and_disable(1); | ||
367 | local_enable_irq(AU1000_TOY_MATCH2_INT); | ||
368 | spin_unlock_irqrestore(&pm_lock, flags); | ||
369 | au1000_calibrate_delay(); | ||
370 | restore_local_and_enable(0, intc0_mask); | ||
371 | restore_local_and_enable(1, intc1_mask); | ||
372 | |||
373 | return retval; | ||
374 | } | ||
375 | |||
376 | |||
377 | static struct ctl_table pm_table[] = { | ||
378 | { | ||
379 | .ctl_name = CTL_UNNUMBERED, | ||
380 | .procname = "sleep", | ||
381 | .data = NULL, | ||
382 | .maxlen = 0, | ||
383 | .mode = 0600, | ||
384 | .proc_handler = &pm_do_sleep | ||
385 | }, | ||
386 | { | ||
387 | .ctl_name = CTL_UNNUMBERED, | ||
388 | .procname = "freq", | ||
389 | .data = NULL, | ||
390 | .maxlen = 0, | ||
391 | .mode = 0600, | ||
392 | .proc_handler = &pm_do_freq | ||
393 | }, | ||
394 | {} | ||
395 | }; | ||
396 | |||
397 | static struct ctl_table pm_dir_table[] = { | ||
398 | { | ||
399 | .ctl_name = CTL_UNNUMBERED, | ||
400 | .procname = "pm", | ||
401 | .mode = 0555, | ||
402 | .child = pm_table | ||
403 | }, | ||
404 | {} | ||
405 | }; | ||
406 | |||
407 | /* | ||
408 | * Initialize power interface | ||
409 | */ | ||
410 | static int __init pm_init(void) | ||
411 | { | ||
412 | register_sysctl_table(pm_dir_table); | ||
413 | return 0; | ||
414 | } | ||
415 | |||
416 | __initcall(pm_init); | ||
417 | |||
418 | /* | ||
419 | * This is right out of init/main.c | ||
420 | */ | ||
421 | |||
422 | /* | ||
423 | * This is the number of bits of precision for the loops_per_jiffy. | ||
424 | * Each bit takes on average 1.5/HZ seconds. This (like the original) | ||
425 | * is a little better than 1%. | ||
426 | */ | ||
427 | #define LPS_PREC 8 | ||
428 | |||
429 | static void au1000_calibrate_delay(void) | ||
430 | { | ||
431 | unsigned long ticks, loopbit; | ||
432 | int lps_precision = LPS_PREC; | ||
433 | |||
434 | loops_per_jiffy = 1 << 12; | ||
435 | |||
436 | while (loops_per_jiffy <<= 1) { | ||
437 | /* Wait for "start of" clock tick */ | ||
438 | ticks = jiffies; | ||
439 | while (ticks == jiffies) | ||
440 | /* nothing */ ; | ||
441 | /* Go ... */ | ||
442 | ticks = jiffies; | ||
443 | __delay(loops_per_jiffy); | ||
444 | ticks = jiffies - ticks; | ||
445 | if (ticks) | ||
446 | break; | ||
447 | } | ||
448 | |||
449 | /* | ||
450 | * Do a binary approximation to get loops_per_jiffy set to be equal | ||
451 | * one clock (up to lps_precision bits) | ||
452 | */ | ||
453 | loops_per_jiffy >>= 1; | ||
454 | loopbit = loops_per_jiffy; | ||
455 | while (lps_precision-- && (loopbit >>= 1)) { | ||
456 | loops_per_jiffy |= loopbit; | ||
457 | ticks = jiffies; | ||
458 | while (ticks == jiffies); | ||
459 | ticks = jiffies; | ||
460 | __delay(loops_per_jiffy); | ||
461 | if (jiffies != ticks) /* longer than 1 tick */ | ||
462 | loops_per_jiffy &= ~loopbit; | ||
463 | } | ||
464 | } | ||
465 | #endif /* CONFIG_PM */ | ||