diff options
author | Ingo Molnar <mingo@elte.hu> | 2009-01-18 14:15:05 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2009-01-18 14:15:05 -0500 |
commit | 4092762aebfe55c1f8e31440b80a053c2dbe519b (patch) | |
tree | 8fb9fd14131194174c12daf5d8195afd3b62bc3e /arch/mips/alchemy/devboards/pm.c | |
parent | 745b1626dd71ce9661a05ea4db57859ed5c773d2 (diff) | |
parent | 1de9e8e70f5acc441550ca75433563d91b269bbe (diff) |
Merge branch 'tracing/ftrace'; commit 'v2.6.29-rc2' into tracing/core
Diffstat (limited to 'arch/mips/alchemy/devboards/pm.c')
-rw-r--r-- | arch/mips/alchemy/devboards/pm.c | 229 |
1 files changed, 229 insertions, 0 deletions
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 | |||
22 | static unsigned long db1x_pm_sleep_secs; | ||
23 | static unsigned long db1x_pm_wakemsk; | ||
24 | static unsigned long db1x_pm_last_wakesrc; | ||
25 | |||
26 | static 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 | |||
57 | static 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 | |||
67 | static 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 | |||
80 | static 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 | |||
89 | static 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 | |||
119 | static 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 | |||
168 | ATTR(gpio0) /* GPIO-based wakeup enable */ | ||
169 | ATTR(gpio1) | ||
170 | ATTR(gpio2) | ||
171 | ATTR(gpio3) | ||
172 | ATTR(gpio4) | ||
173 | ATTR(gpio5) | ||
174 | ATTR(gpio6) | ||
175 | ATTR(gpio7) | ||
176 | ATTR(timer) /* TOYMATCH2-based wakeup enable */ | ||
177 | ATTR(timer_timeout) /* timer-based wakeup timeout value, in seconds */ | ||
178 | ATTR(wakesrc) /* contents of SYS_WAKESRC after last wakeup */ | ||
179 | ATTR(wakemsk) /* direct access to SYS_WAKEMSK */ | ||
180 | |||
181 | #define ATTR_LIST(x) & x ## _attribute.attr | ||
182 | static 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 | |||
198 | static struct attribute_group db1x_pmattr_group = { | ||
199 | .name = "db1x", | ||
200 | .attrs = db1x_pmattrs, | ||
201 | }; | ||
202 | |||
203 | /* | ||
204 | * Initialize suspend interface | ||
205 | */ | ||
206 | static 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 | |||
229 | late_initcall(pm_init); | ||