diff options
author | Manuel Lauss <mano@roarinelk.homelinux.net> | 2008-12-21 03:26:27 -0500 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2009-01-11 04:57:27 -0500 |
commit | 61f9c58da57a80b0df1ced18a28cbbaebd4d417a (patch) | |
tree | cbeaa2353d98899e88c6d44dc8d1a16eabbb6a47 /arch/mips/alchemy/common | |
parent | ac15dad061d351281b0bafbae1ecdd84e601435a (diff) |
MIPS: Alchemy: new userspace suspend interface for development boards.
Replace the current sysctl-based suspend interface with a new sysfs-
based one which also uses the Linux-2.6 suspend model.
To configure wakeup sources, a subtree for the demoboards is created
under /sys/power/db1x:
sys/
`-- power
`-- db1x
|-- gpio0
|-- gpio1
|-- gpio2
|-- gpio3
|-- gpio4
|-- gpio5
|-- gpio6
|-- gpio7
|-- timer
|-- timer_timeout
|-- wakemsk
`-- wakesrc
The nodes 'gpio[0-7]' and 'timer' configure the GPIO0..7 and M2
bits of the SYS_WAKEMSK (wakeup source enable) register. Writing '1'
enables a wakesource, 0 disables it.
The 'timer_timeout' node holds the timeout in seconds after which the
TOYMATCH2 event should wake the system.
The 'wakesrc' node holds the SYS_WAKESRC register after wakeup (in hex),
the 'wakemsk' node can be used to get/set the wakeup mask directly.
For example, to have the timer wake the system after 10 seconds of sleep,
the following must be done in userspace:
echo 10 > /sys/power/db1x/timer_timeout
echo 1 > /sys/power/db1x/timer
echo mem > /sys/power/sleep
This patch also removes the homebrew CPU frequency switching code. I don't
understand how it could have ever worked reliably; it does not communicate
the clock changes to peripheral devices other than uarts.
Signed-off-by: Manuel Lauss <mano@roarinelk.homelinux.net>
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
create mode 100644 arch/mips/alchemy/devboards/pm.c
Diffstat (limited to 'arch/mips/alchemy/common')
-rw-r--r-- | arch/mips/alchemy/common/irq.c | 44 | ||||
-rw-r--r-- | arch/mips/alchemy/common/power.c | 309 |
2 files changed, 0 insertions, 353 deletions
diff --git a/arch/mips/alchemy/common/irq.c b/arch/mips/alchemy/common/irq.c index c54384779fb9..c88c821b4c36 100644 --- a/arch/mips/alchemy/common/irq.c +++ b/arch/mips/alchemy/common/irq.c | |||
@@ -37,8 +37,6 @@ | |||
37 | #include <asm/mach-pb1x00/pb1000.h> | 37 | #include <asm/mach-pb1x00/pb1000.h> |
38 | #endif | 38 | #endif |
39 | 39 | ||
40 | static DEFINE_SPINLOCK(irq_lock); | ||
41 | |||
42 | static int au1x_ic_settype(unsigned int irq, unsigned int flow_type); | 40 | static int au1x_ic_settype(unsigned int irq, unsigned int flow_type); |
43 | 41 | ||
44 | /* per-processor fixed function irqs */ | 42 | /* per-processor fixed function irqs */ |
@@ -611,45 +609,3 @@ void __init arch_init_irq(void) | |||
611 | 609 | ||
612 | set_c0_status(IE_IRQ0 | IE_IRQ1 | IE_IRQ2 | IE_IRQ3); | 610 | set_c0_status(IE_IRQ0 | IE_IRQ1 | IE_IRQ2 | IE_IRQ3); |
613 | } | 611 | } |
614 | |||
615 | unsigned long save_local_and_disable(int controller) | ||
616 | { | ||
617 | int i; | ||
618 | unsigned long flags, mask; | ||
619 | |||
620 | spin_lock_irqsave(&irq_lock, flags); | ||
621 | if (controller) { | ||
622 | mask = au_readl(IC1_MASKSET); | ||
623 | for (i = 0; i < 32; i++) | ||
624 | au1x_ic1_mask(i + AU1000_INTC1_INT_BASE); | ||
625 | } else { | ||
626 | mask = au_readl(IC0_MASKSET); | ||
627 | for (i = 0; i < 32; i++) | ||
628 | au1x_ic0_mask(i + AU1000_INTC0_INT_BASE); | ||
629 | } | ||
630 | spin_unlock_irqrestore(&irq_lock, flags); | ||
631 | |||
632 | return mask; | ||
633 | } | ||
634 | |||
635 | void restore_local_and_enable(int controller, unsigned long mask) | ||
636 | { | ||
637 | int i; | ||
638 | unsigned long flags, new_mask; | ||
639 | |||
640 | spin_lock_irqsave(&irq_lock, flags); | ||
641 | for (i = 0; i < 32; i++) | ||
642 | if (mask & (1 << i)) { | ||
643 | if (controller) | ||
644 | au1x_ic1_unmask(i + AU1000_INTC1_INT_BASE); | ||
645 | else | ||
646 | au1x_ic0_unmask(i + AU1000_INTC0_INT_BASE); | ||
647 | } | ||
648 | |||
649 | if (controller) | ||
650 | new_mask = au_readl(IC1_MASKSET); | ||
651 | else | ||
652 | new_mask = au_readl(IC0_MASKSET); | ||
653 | |||
654 | spin_unlock_irqrestore(&irq_lock, flags); | ||
655 | } | ||
diff --git a/arch/mips/alchemy/common/power.c b/arch/mips/alchemy/common/power.c index f58e151b38d4..6ab7b42aa1be 100644 --- a/arch/mips/alchemy/common/power.c +++ b/arch/mips/alchemy/common/power.c | |||
@@ -42,18 +42,6 @@ | |||
42 | 42 | ||
43 | #ifdef CONFIG_PM | 43 | #ifdef CONFIG_PM |
44 | 44 | ||
45 | #define DEBUG 1 | ||
46 | #ifdef DEBUG | ||
47 | #define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __func__, ## args) | ||
48 | #else | ||
49 | #define DPRINTK(fmt, args...) | ||
50 | #endif | ||
51 | |||
52 | extern unsigned long save_local_and_disable(int controller); | ||
53 | extern void restore_local_and_enable(int controller, unsigned long mask); | ||
54 | |||
55 | static DEFINE_SPINLOCK(pm_lock); | ||
56 | |||
57 | /* | 45 | /* |
58 | * We need to save/restore a bunch of core registers that are | 46 | * We need to save/restore a bunch of core registers that are |
59 | * either volatile or reset to some state across a processor sleep. | 47 | * either volatile or reset to some state across a processor sleep. |
@@ -74,21 +62,6 @@ static unsigned int sleep_sys_clocks[5]; | |||
74 | static unsigned int sleep_sys_pinfunc; | 62 | static unsigned int sleep_sys_pinfunc; |
75 | static unsigned int sleep_static_memctlr[4][3]; | 63 | static unsigned int sleep_static_memctlr[4][3]; |
76 | 64 | ||
77 | /* | ||
78 | * Define this to cause the value you write to /proc/sys/pm/sleep to | ||
79 | * set the TOY timer for the amount of time you want to sleep. | ||
80 | * This is done mainly for testing, but may be useful in other cases. | ||
81 | * The value is number of 32KHz ticks to sleep. | ||
82 | */ | ||
83 | #define SLEEP_TEST_TIMEOUT 1 | ||
84 | #ifdef SLEEP_TEST_TIMEOUT | ||
85 | static int sleep_ticks; | ||
86 | static void wakeup_counter0_set(int ticks) | ||
87 | { | ||
88 | au_writel(au_readl(SYS_TOYREAD) + ticks, SYS_TOYMATCH2); | ||
89 | au_sync(); | ||
90 | } | ||
91 | #endif | ||
92 | 65 | ||
93 | static void save_core_regs(void) | 66 | static void save_core_regs(void) |
94 | { | 67 | { |
@@ -234,13 +207,6 @@ static void restore_core_regs(void) | |||
234 | #endif | 207 | #endif |
235 | } | 208 | } |
236 | 209 | ||
237 | unsigned long suspend_mode; | ||
238 | |||
239 | void wakeup_from_suspend(void) | ||
240 | { | ||
241 | suspend_mode = 0; | ||
242 | } | ||
243 | |||
244 | void au_sleep(void) | 210 | void au_sleep(void) |
245 | { | 211 | { |
246 | save_core_regs(); | 212 | save_core_regs(); |
@@ -248,279 +214,4 @@ void au_sleep(void) | |||
248 | restore_core_regs(); | 214 | restore_core_regs(); |
249 | } | 215 | } |
250 | 216 | ||
251 | static int pm_do_sleep(ctl_table *ctl, int write, struct file *file, | ||
252 | void __user *buffer, size_t *len, loff_t *ppos) | ||
253 | { | ||
254 | unsigned long wakeup, flags; | ||
255 | int ret; | ||
256 | #ifdef SLEEP_TEST_TIMEOUT | ||
257 | #define TMPBUFLEN2 16 | ||
258 | char buf[TMPBUFLEN2], *p; | ||
259 | #endif | ||
260 | |||
261 | spin_lock_irqsave(&pm_lock, flags); | ||
262 | |||
263 | if (!write) { | ||
264 | *len = 0; | ||
265 | ret = 0; | ||
266 | goto out_unlock; | ||
267 | }; | ||
268 | |||
269 | #ifdef SLEEP_TEST_TIMEOUT | ||
270 | if (*len > TMPBUFLEN2 - 1) { | ||
271 | ret = -EFAULT; | ||
272 | goto out_unlock; | ||
273 | } | ||
274 | if (copy_from_user(buf, buffer, *len)) { | ||
275 | return -EFAULT; | ||
276 | goto out_unlock; | ||
277 | } | ||
278 | buf[*len] = 0; | ||
279 | p = buf; | ||
280 | sleep_ticks = simple_strtoul(p, &p, 0); | ||
281 | wakeup_counter0_set(sleep_ticks); | ||
282 | #endif | ||
283 | |||
284 | /** | ||
285 | ** The code below is all system dependent and we should probably | ||
286 | ** have a function call out of here to set this up. You need | ||
287 | ** to configure the GPIO or timer interrupts that will bring | ||
288 | ** you out of sleep. | ||
289 | ** For testing, the TOY counter wakeup is useful. | ||
290 | **/ | ||
291 | #if 0 | ||
292 | au_writel(au_readl(SYS_PINSTATERD) & ~(1 << 11), SYS_PINSTATERD); | ||
293 | |||
294 | /* GPIO 6 can cause a wake up event */ | ||
295 | wakeup = au_readl(SYS_WAKEMSK); | ||
296 | wakeup &= ~(1 << 8); /* turn off match20 wakeup */ | ||
297 | wakeup |= 1 << 6; /* turn on GPIO 6 wakeup */ | ||
298 | #else | ||
299 | /* For testing, allow match20 to wake us up. */ | ||
300 | wakeup = 1 << 8; /* turn on match20 wakeup */ | ||
301 | wakeup = 0; | ||
302 | #endif | ||
303 | au_writel(1, SYS_WAKESRC); /* clear cause */ | ||
304 | au_sync(); | ||
305 | au_writel(wakeup, SYS_WAKEMSK); | ||
306 | au_sync(); | ||
307 | |||
308 | au_sleep(); | ||
309 | ret = 0; | ||
310 | |||
311 | out_unlock: | ||
312 | spin_unlock_irqrestore(&pm_lock, flags); | ||
313 | return ret; | ||
314 | } | ||
315 | |||
316 | #if !defined(CONFIG_SOC_AU1200) && !defined(CONFIG_SOC_AU1550) | ||
317 | |||
318 | /* | ||
319 | * This is right out of init/main.c | ||
320 | */ | ||
321 | |||
322 | /* | ||
323 | * This is the number of bits of precision for the loops_per_jiffy. | ||
324 | * Each bit takes on average 1.5/HZ seconds. This (like the original) | ||
325 | * is a little better than 1%. | ||
326 | */ | ||
327 | #define LPS_PREC 8 | ||
328 | |||
329 | static void au1000_calibrate_delay(void) | ||
330 | { | ||
331 | unsigned long ticks, loopbit; | ||
332 | int lps_precision = LPS_PREC; | ||
333 | |||
334 | loops_per_jiffy = 1 << 12; | ||
335 | |||
336 | while (loops_per_jiffy <<= 1) { | ||
337 | /* Wait for "start of" clock tick */ | ||
338 | ticks = jiffies; | ||
339 | while (ticks == jiffies) | ||
340 | /* nothing */ ; | ||
341 | /* Go ... */ | ||
342 | ticks = jiffies; | ||
343 | __delay(loops_per_jiffy); | ||
344 | ticks = jiffies - ticks; | ||
345 | if (ticks) | ||
346 | break; | ||
347 | } | ||
348 | |||
349 | /* | ||
350 | * Do a binary approximation to get loops_per_jiffy set to be equal | ||
351 | * one clock (up to lps_precision bits) | ||
352 | */ | ||
353 | loops_per_jiffy >>= 1; | ||
354 | loopbit = loops_per_jiffy; | ||
355 | while (lps_precision-- && (loopbit >>= 1)) { | ||
356 | loops_per_jiffy |= loopbit; | ||
357 | ticks = jiffies; | ||
358 | while (ticks == jiffies); | ||
359 | ticks = jiffies; | ||
360 | __delay(loops_per_jiffy); | ||
361 | if (jiffies != ticks) /* longer than 1 tick */ | ||
362 | loops_per_jiffy &= ~loopbit; | ||
363 | } | ||
364 | } | ||
365 | |||
366 | static int pm_do_freq(ctl_table *ctl, int write, struct file *file, | ||
367 | void __user *buffer, size_t *len, loff_t *ppos) | ||
368 | { | ||
369 | int retval = 0, i; | ||
370 | unsigned long val, pll; | ||
371 | #define TMPBUFLEN 64 | ||
372 | #define MAX_CPU_FREQ 396 | ||
373 | char buf[TMPBUFLEN], *p; | ||
374 | unsigned long flags, intc0_mask, intc1_mask; | ||
375 | unsigned long old_baud_base, old_cpu_freq, old_clk, old_refresh; | ||
376 | unsigned long new_baud_base, new_cpu_freq, new_clk, new_refresh; | ||
377 | unsigned long baud_rate; | ||
378 | |||
379 | spin_lock_irqsave(&pm_lock, flags); | ||
380 | if (!write) | ||
381 | *len = 0; | ||
382 | else { | ||
383 | /* Parse the new frequency */ | ||
384 | if (*len > TMPBUFLEN - 1) { | ||
385 | spin_unlock_irqrestore(&pm_lock, flags); | ||
386 | return -EFAULT; | ||
387 | } | ||
388 | if (copy_from_user(buf, buffer, *len)) { | ||
389 | spin_unlock_irqrestore(&pm_lock, flags); | ||
390 | return -EFAULT; | ||
391 | } | ||
392 | buf[*len] = 0; | ||
393 | p = buf; | ||
394 | val = simple_strtoul(p, &p, 0); | ||
395 | if (val > MAX_CPU_FREQ) { | ||
396 | spin_unlock_irqrestore(&pm_lock, flags); | ||
397 | return -EFAULT; | ||
398 | } | ||
399 | |||
400 | pll = val / 12; | ||
401 | if ((pll > 33) || (pll < 7)) { /* 396 MHz max, 84 MHz min */ | ||
402 | /* Revisit this for higher speed CPUs */ | ||
403 | spin_unlock_irqrestore(&pm_lock, flags); | ||
404 | return -EFAULT; | ||
405 | } | ||
406 | |||
407 | old_baud_base = get_au1x00_uart_baud_base(); | ||
408 | old_cpu_freq = get_au1x00_speed(); | ||
409 | |||
410 | new_cpu_freq = pll * 12 * 1000000; | ||
411 | new_baud_base = (new_cpu_freq / (2 * ((int)(au_readl(SYS_POWERCTRL) | ||
412 | & 0x03) + 2) * 16)); | ||
413 | set_au1x00_speed(new_cpu_freq); | ||
414 | set_au1x00_uart_baud_base(new_baud_base); | ||
415 | |||
416 | old_refresh = au_readl(MEM_SDREFCFG) & 0x1ffffff; | ||
417 | new_refresh = ((old_refresh * new_cpu_freq) / old_cpu_freq) | | ||
418 | (au_readl(MEM_SDREFCFG) & ~0x1ffffff); | ||
419 | |||
420 | au_writel(pll, SYS_CPUPLL); | ||
421 | au_sync_delay(1); | ||
422 | au_writel(new_refresh, MEM_SDREFCFG); | ||
423 | au_sync_delay(1); | ||
424 | |||
425 | for (i = 0; i < 4; i++) | ||
426 | if (au_readl(UART_BASE + UART_MOD_CNTRL + | ||
427 | i * 0x00100000) == 3) { | ||
428 | old_clk = au_readl(UART_BASE + UART_CLK + | ||
429 | i * 0x00100000); | ||
430 | baud_rate = old_baud_base / old_clk; | ||
431 | /* | ||
432 | * We won't get an exact baud rate and the error | ||
433 | * could be significant enough that our new | ||
434 | * calculation will result in a clock that will | ||
435 | * give us a baud rate that's too far off from | ||
436 | * what we really want. | ||
437 | */ | ||
438 | if (baud_rate > 100000) | ||
439 | baud_rate = 115200; | ||
440 | else if (baud_rate > 50000) | ||
441 | baud_rate = 57600; | ||
442 | else if (baud_rate > 30000) | ||
443 | baud_rate = 38400; | ||
444 | else if (baud_rate > 17000) | ||
445 | baud_rate = 19200; | ||
446 | else | ||
447 | baud_rate = 9600; | ||
448 | new_clk = new_baud_base / baud_rate; | ||
449 | au_writel(new_clk, UART_BASE + UART_CLK + | ||
450 | i * 0x00100000); | ||
451 | au_sync_delay(10); | ||
452 | } | ||
453 | } | ||
454 | |||
455 | /* | ||
456 | * We don't want _any_ interrupts other than match20. Otherwise our | ||
457 | * au1000_calibrate_delay() calculation will be off, potentially a lot. | ||
458 | */ | ||
459 | intc0_mask = save_local_and_disable(0); | ||
460 | intc1_mask = save_local_and_disable(1); | ||
461 | val = 1 << (AU1000_TOY_MATCH2_INT - AU1000_INTC0_INT_BASE); | ||
462 | au_writel(val, IC0_MASKSET); /* unmask */ | ||
463 | au_writel(val, IC0_WAKESET); /* enable wake-from-sleep */ | ||
464 | au_sync(); | ||
465 | spin_unlock_irqrestore(&pm_lock, flags); | ||
466 | au1000_calibrate_delay(); | ||
467 | restore_local_and_enable(0, intc0_mask); | ||
468 | restore_local_and_enable(1, intc1_mask); | ||
469 | |||
470 | return retval; | ||
471 | } | ||
472 | #endif | ||
473 | |||
474 | static struct ctl_table pm_table[] = { | ||
475 | { | ||
476 | .ctl_name = CTL_UNNUMBERED, | ||
477 | .procname = "sleep", | ||
478 | .data = NULL, | ||
479 | .maxlen = 0, | ||
480 | .mode = 0600, | ||
481 | .proc_handler = &pm_do_sleep | ||
482 | }, | ||
483 | #if !defined(CONFIG_SOC_AU1200) && !defined(CONFIG_SOC_AU1550) | ||
484 | { | ||
485 | .ctl_name = CTL_UNNUMBERED, | ||
486 | .procname = "freq", | ||
487 | .data = NULL, | ||
488 | .maxlen = 0, | ||
489 | .mode = 0600, | ||
490 | .proc_handler = &pm_do_freq | ||
491 | }, | ||
492 | #endif | ||
493 | {} | ||
494 | }; | ||
495 | |||
496 | static struct ctl_table pm_dir_table[] = { | ||
497 | { | ||
498 | .ctl_name = CTL_UNNUMBERED, | ||
499 | .procname = "pm", | ||
500 | .mode = 0555, | ||
501 | .child = pm_table | ||
502 | }, | ||
503 | {} | ||
504 | }; | ||
505 | |||
506 | /* | ||
507 | * Initialize power interface | ||
508 | */ | ||
509 | static int __init pm_init(void) | ||
510 | { | ||
511 | /* init TOY to tick at 1Hz. No need to wait for access bits | ||
512 | * since there's plenty of time between here and the first | ||
513 | * suspend cycle. | ||
514 | */ | ||
515 | if (au_readl(SYS_TOYTRIM) != 32767) { | ||
516 | au_writel(32767, SYS_TOYTRIM); | ||
517 | au_sync(); | ||
518 | } | ||
519 | |||
520 | register_sysctl_table(pm_dir_table); | ||
521 | return 0; | ||
522 | } | ||
523 | |||
524 | __initcall(pm_init); | ||
525 | |||
526 | #endif /* CONFIG_PM */ | 217 | #endif /* CONFIG_PM */ |