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