aboutsummaryrefslogtreecommitdiffstats
path: root/arch/mips/alchemy
diff options
context:
space:
mode:
Diffstat (limited to 'arch/mips/alchemy')
-rw-r--r--arch/mips/alchemy/common/irq.c44
-rw-r--r--arch/mips/alchemy/common/power.c309
-rw-r--r--arch/mips/alchemy/devboards/Makefile1
-rw-r--r--arch/mips/alchemy/devboards/pm.c229
4 files changed, 230 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
40static DEFINE_SPINLOCK(irq_lock);
41
42static int au1x_ic_settype(unsigned int irq, unsigned int flow_type); 40static 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
615unsigned 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
635void 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
52extern unsigned long save_local_and_disable(int controller);
53extern void restore_local_and_enable(int controller, unsigned long mask);
54
55static 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];
74static unsigned int sleep_sys_pinfunc; 62static unsigned int sleep_sys_pinfunc;
75static unsigned int sleep_static_memctlr[4][3]; 63static 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
85static int sleep_ticks;
86static 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
93static void save_core_regs(void) 66static 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
237unsigned long suspend_mode;
238
239void wakeup_from_suspend(void)
240{
241 suspend_mode = 0;
242}
243
244void au_sleep(void) 210void 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
251static 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
311out_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
329static 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
366static 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
474static 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
496static 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 */
509static 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 */
diff --git a/arch/mips/alchemy/devboards/Makefile b/arch/mips/alchemy/devboards/Makefile
index c0eb87a66fbd..730f9f2b30e8 100644
--- a/arch/mips/alchemy/devboards/Makefile
+++ b/arch/mips/alchemy/devboards/Makefile
@@ -3,6 +3,7 @@
3# 3#
4 4
5obj-y += prom.o 5obj-y += prom.o
6obj-$(CONFIG_PM) += pm.o
6obj-$(CONFIG_MIPS_PB1000) += pb1000/ 7obj-$(CONFIG_MIPS_PB1000) += pb1000/
7obj-$(CONFIG_MIPS_PB1100) += pb1100/ 8obj-$(CONFIG_MIPS_PB1100) += pb1100/
8obj-$(CONFIG_MIPS_PB1200) += pb1200/ 9obj-$(CONFIG_MIPS_PB1200) += pb1200/
diff --git a/arch/mips/alchemy/devboards/pm.c b/arch/mips/alchemy/devboards/pm.c
new file mode 100644
index 000000000000..d5eb9c325ed0
--- /dev/null
+++ b/arch/mips/alchemy/devboards/pm.c
@@ -0,0 +1,229 @@
1/*
2 * Alchemy Development Board example suspend userspace interface.
3 *
4 * (c) 2008 Manuel Lauss <mano@roarinelk.homelinux.net>
5 */
6
7#include <linux/init.h>
8#include <linux/kobject.h>
9#include <linux/suspend.h>
10#include <linux/sysfs.h>
11#include <asm/mach-au1x00/au1000.h>
12
13/*
14 * Generic suspend userspace interface for Alchemy development boards.
15 * This code exports a few sysfs nodes under /sys/power/db1x/ which
16 * can be used by userspace to en/disable all au1x-provided wakeup
17 * sources and configure the timeout after which the the TOYMATCH2 irq
18 * is to trigger a wakeup.
19 */
20
21
22static unsigned long db1x_pm_sleep_secs;
23static unsigned long db1x_pm_wakemsk;
24static unsigned long db1x_pm_last_wakesrc;
25
26static int db1x_pm_enter(suspend_state_t state)
27{
28 /* enable GPIO based wakeup */
29 au_writel(1, SYS_PININPUTEN);
30
31 /* clear and setup wake cause and source */
32 au_writel(0, SYS_WAKEMSK);
33 au_sync();
34 au_writel(0, SYS_WAKESRC);
35 au_sync();
36
37 au_writel(db1x_pm_wakemsk, SYS_WAKEMSK);
38 au_sync();
39
40 /* setup 1Hz-timer-based wakeup: wait for reg access */
41 while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_M20)
42 asm volatile ("nop");
43
44 au_writel(au_readl(SYS_TOYREAD) + db1x_pm_sleep_secs, SYS_TOYMATCH2);
45 au_sync();
46
47 /* wait for value to really hit the register */
48 while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_M20)
49 asm volatile ("nop");
50
51 /* ...and now the sandman can come! */
52 au_sleep();
53
54 return 0;
55}
56
57static int db1x_pm_begin(suspend_state_t state)
58{
59 if (!db1x_pm_wakemsk) {
60 printk(KERN_ERR "db1x: no wakeup source activated!\n");
61 return -EINVAL;
62 }
63
64 return 0;
65}
66
67static void db1x_pm_end(void)
68{
69 /* read and store wakeup source, the clear the register. To
70 * be able to clear it, WAKEMSK must be cleared first.
71 */
72 db1x_pm_last_wakesrc = au_readl(SYS_WAKESRC);
73
74 au_writel(0, SYS_WAKEMSK);
75 au_writel(0, SYS_WAKESRC);
76 au_sync();
77
78}
79
80static struct platform_suspend_ops db1x_pm_ops = {
81 .valid = suspend_valid_only_mem,
82 .begin = db1x_pm_begin,
83 .enter = db1x_pm_enter,
84 .end = db1x_pm_end,
85};
86
87#define ATTRCMP(x) (0 == strcmp(attr->attr.name, #x))
88
89static ssize_t db1x_pmattr_show(struct kobject *kobj,
90 struct kobj_attribute *attr,
91 char *buf)
92{
93 int idx;
94
95 if (ATTRCMP(timer_timeout))
96 return sprintf(buf, "%lu\n", db1x_pm_sleep_secs);
97
98 else if (ATTRCMP(timer))
99 return sprintf(buf, "%u\n",
100 !!(db1x_pm_wakemsk & SYS_WAKEMSK_M2));
101
102 else if (ATTRCMP(wakesrc))
103 return sprintf(buf, "%lu\n", db1x_pm_last_wakesrc);
104
105 else if (ATTRCMP(gpio0) || ATTRCMP(gpio1) || ATTRCMP(gpio2) ||
106 ATTRCMP(gpio3) || ATTRCMP(gpio4) || ATTRCMP(gpio5) ||
107 ATTRCMP(gpio6) || ATTRCMP(gpio7)) {
108 idx = (attr->attr.name)[4] - '0';
109 return sprintf(buf, "%d\n",
110 !!(db1x_pm_wakemsk & SYS_WAKEMSK_GPIO(idx)));
111
112 } else if (ATTRCMP(wakemsk)) {
113 return sprintf(buf, "%08lx\n", db1x_pm_wakemsk);
114 }
115
116 return -ENOENT;
117}
118
119static ssize_t db1x_pmattr_store(struct kobject *kobj,
120 struct kobj_attribute *attr,
121 const char *instr,
122 size_t bytes)
123{
124 unsigned long l;
125 int tmp;
126
127 if (ATTRCMP(timer_timeout)) {
128 tmp = strict_strtoul(instr, 0, &l);
129 if (tmp)
130 return tmp;
131
132 db1x_pm_sleep_secs = l;
133
134 } else if (ATTRCMP(timer)) {
135 if (instr[0] != '0')
136 db1x_pm_wakemsk |= SYS_WAKEMSK_M2;
137 else
138 db1x_pm_wakemsk &= ~SYS_WAKEMSK_M2;
139
140 } else if (ATTRCMP(gpio0) || ATTRCMP(gpio1) || ATTRCMP(gpio2) ||
141 ATTRCMP(gpio3) || ATTRCMP(gpio4) || ATTRCMP(gpio5) ||
142 ATTRCMP(gpio6) || ATTRCMP(gpio7)) {
143 tmp = (attr->attr.name)[4] - '0';
144 if (instr[0] != '0') {
145 db1x_pm_wakemsk |= SYS_WAKEMSK_GPIO(tmp);
146 } else {
147 db1x_pm_wakemsk &= ~SYS_WAKEMSK_GPIO(tmp);
148 }
149
150 } else if (ATTRCMP(wakemsk)) {
151 tmp = strict_strtoul(instr, 0, &l);
152 if (tmp)
153 return tmp;
154
155 db1x_pm_wakemsk = l & 0x0000003f;
156
157 } else
158 bytes = -ENOENT;
159
160 return bytes;
161}
162
163#define ATTR(x) \
164 static struct kobj_attribute x##_attribute = \
165 __ATTR(x, 0664, db1x_pmattr_show, \
166 db1x_pmattr_store);
167
168ATTR(gpio0) /* GPIO-based wakeup enable */
169ATTR(gpio1)
170ATTR(gpio2)
171ATTR(gpio3)
172ATTR(gpio4)
173ATTR(gpio5)
174ATTR(gpio6)
175ATTR(gpio7)
176ATTR(timer) /* TOYMATCH2-based wakeup enable */
177ATTR(timer_timeout) /* timer-based wakeup timeout value, in seconds */
178ATTR(wakesrc) /* contents of SYS_WAKESRC after last wakeup */
179ATTR(wakemsk) /* direct access to SYS_WAKEMSK */
180
181#define ATTR_LIST(x) & x ## _attribute.attr
182static struct attribute *db1x_pmattrs[] = {
183 ATTR_LIST(gpio0),
184 ATTR_LIST(gpio1),
185 ATTR_LIST(gpio2),
186 ATTR_LIST(gpio3),
187 ATTR_LIST(gpio4),
188 ATTR_LIST(gpio5),
189 ATTR_LIST(gpio6),
190 ATTR_LIST(gpio7),
191 ATTR_LIST(timer),
192 ATTR_LIST(timer_timeout),
193 ATTR_LIST(wakesrc),
194 ATTR_LIST(wakemsk),
195 NULL, /* terminator */
196};
197
198static struct attribute_group db1x_pmattr_group = {
199 .name = "db1x",
200 .attrs = db1x_pmattrs,
201};
202
203/*
204 * Initialize suspend interface
205 */
206static int __init pm_init(void)
207{
208 /* init TOY to tick at 1Hz if not already done. No need to wait
209 * for confirmation since there's plenty of time from here to
210 * the next suspend cycle.
211 */
212 if (au_readl(SYS_TOYTRIM) != 32767) {
213 au_writel(32767, SYS_TOYTRIM);
214 au_sync();
215 }
216
217 db1x_pm_last_wakesrc = au_readl(SYS_WAKESRC);
218
219 au_writel(0, SYS_WAKESRC);
220 au_sync();
221 au_writel(0, SYS_WAKEMSK);
222 au_sync();
223
224 suspend_set_ops(&db1x_pm_ops);
225
226 return sysfs_create_group(power_kobj, &db1x_pmattr_group);
227}
228
229late_initcall(pm_init);