Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
[linux-2.6.git] / arch / powerpc / sysdev / ppc4xx_cpm.c
1 /*
2  * PowerPC 4xx Clock and Power Management
3  *
4  * Copyright (C) 2010, Applied Micro Circuits Corporation
5  * Victor Gallardo (vgallardo@apm.com)
6  *
7  * Based on arch/powerpc/platforms/44x/idle.c:
8  * Jerone Young <jyoung5@us.ibm.com>
9  * Copyright 2008 IBM Corp.
10  *
11  * Based on arch/powerpc/sysdev/fsl_pmc.c:
12  * Anton Vorontsov <avorontsov@ru.mvista.com>
13  * Copyright 2009  MontaVista Software, Inc.
14  *
15  * See file CREDITS for list of people who contributed to this
16  * project.
17  *
18  * This program is free software; you can redistribute it and/or
19  * modify it under the terms of the GNU General Public License as
20  * published by the Free Software Foundation; either version 2 of
21  * the License, or (at your option) any later version.
22  *
23  * This program is distributed in the hope that it will be useful,
24  * but WITHOUT ANY WARRANTY; without even the implied warranty of
25  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26  * GNU General Public License for more details.
27  *
28  * You should have received a copy of the GNU General Public License
29  * along with this program; if not, write to the Free Software
30  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
31  * MA 02111-1307 USA
32  */
33
34 #include <linux/kernel.h>
35 #include <linux/of_platform.h>
36 #include <linux/sysfs.h>
37 #include <linux/cpu.h>
38 #include <linux/suspend.h>
39 #include <asm/dcr.h>
40 #include <asm/dcr-native.h>
41 #include <asm/machdep.h>
42
43 #define CPM_ER  0
44 #define CPM_FR  1
45 #define CPM_SR  2
46
47 #define CPM_IDLE_WAIT   0
48 #define CPM_IDLE_DOZE   1
49
50 struct cpm {
51         dcr_host_t      dcr_host;
52         unsigned int    dcr_offset[3];
53         unsigned int    powersave_off;
54         unsigned int    unused;
55         unsigned int    idle_doze;
56         unsigned int    standby;
57         unsigned int    suspend;
58 };
59
60 static struct cpm cpm;
61
62 struct cpm_idle_mode {
63         unsigned int enabled;
64         const char  *name;
65 };
66
67 static struct cpm_idle_mode idle_mode[] = {
68         [CPM_IDLE_WAIT] = { 1, "wait" }, /* default */
69         [CPM_IDLE_DOZE] = { 0, "doze" },
70 };
71
72 static unsigned int cpm_set(unsigned int cpm_reg, unsigned int mask)
73 {
74         unsigned int value;
75
76         /* CPM controller supports 3 different types of sleep interface
77          * known as class 1, 2 and 3. For class 1 units, they are
78          * unconditionally put to sleep when the corresponding CPM bit is
79          * set. For class 2 and 3 units this is not case; if they can be
80          * put to to sleep, they will. Here we do not verify, we just
81          * set them and expect them to eventually go off when they can.
82          */
83         value = dcr_read(cpm.dcr_host, cpm.dcr_offset[cpm_reg]);
84         dcr_write(cpm.dcr_host, cpm.dcr_offset[cpm_reg], value | mask);
85
86         /* return old state, to restore later if needed */
87         return value;
88 }
89
90 static void cpm_idle_wait(void)
91 {
92         unsigned long msr_save;
93
94         /* save off initial state */
95         msr_save = mfmsr();
96         /* sync required when CPM0_ER[CPU] is set */
97         mb();
98         /* set wait state MSR */
99         mtmsr(msr_save|MSR_WE|MSR_EE|MSR_CE|MSR_DE);
100         isync();
101         /* return to initial state */
102         mtmsr(msr_save);
103         isync();
104 }
105
106 static void cpm_idle_sleep(unsigned int mask)
107 {
108         unsigned int er_save;
109
110         /* update CPM_ER state */
111         er_save = cpm_set(CPM_ER, mask);
112
113         /* go to wait state so that CPM0_ER[CPU] can take effect */
114         cpm_idle_wait();
115
116         /* restore CPM_ER state */
117         dcr_write(cpm.dcr_host, cpm.dcr_offset[CPM_ER], er_save);
118 }
119
120 static void cpm_idle_doze(void)
121 {
122         cpm_idle_sleep(cpm.idle_doze);
123 }
124
125 static void cpm_idle_config(int mode)
126 {
127         int i;
128
129         if (idle_mode[mode].enabled)
130                 return;
131
132         for (i = 0; i < ARRAY_SIZE(idle_mode); i++)
133                 idle_mode[i].enabled = 0;
134
135         idle_mode[mode].enabled = 1;
136 }
137
138 static ssize_t cpm_idle_show(struct kobject *kobj,
139                              struct kobj_attribute *attr, char *buf)
140 {
141         char *s = buf;
142         int i;
143
144         for (i = 0; i < ARRAY_SIZE(idle_mode); i++) {
145                 if (idle_mode[i].enabled)
146                         s += sprintf(s, "[%s] ", idle_mode[i].name);
147                 else
148                         s += sprintf(s, "%s ", idle_mode[i].name);
149         }
150
151         *(s-1) = '\n'; /* convert the last space to a newline */
152
153         return s - buf;
154 }
155
156 static ssize_t cpm_idle_store(struct kobject *kobj,
157                               struct kobj_attribute *attr,
158                               const char *buf, size_t n)
159 {
160         int i;
161         char *p;
162         int len;
163
164         p = memchr(buf, '\n', n);
165         len = p ? p - buf : n;
166
167         for (i = 0; i < ARRAY_SIZE(idle_mode); i++) {
168                 if (strncmp(buf, idle_mode[i].name, len) == 0) {
169                         cpm_idle_config(i);
170                         return n;
171                 }
172         }
173
174         return -EINVAL;
175 }
176
177 static struct kobj_attribute cpm_idle_attr =
178         __ATTR(idle, 0644, cpm_idle_show, cpm_idle_store);
179
180 static void cpm_idle_config_sysfs(void)
181 {
182         struct device *dev;
183         unsigned long ret;
184
185         dev = get_cpu_device(0);
186
187         ret = sysfs_create_file(&dev->kobj,
188                                 &cpm_idle_attr.attr);
189         if (ret)
190                 printk(KERN_WARNING
191                        "cpm: failed to create idle sysfs entry\n");
192 }
193
194 static void cpm_idle(void)
195 {
196         if (idle_mode[CPM_IDLE_DOZE].enabled)
197                 cpm_idle_doze();
198         else
199                 cpm_idle_wait();
200 }
201
202 static int cpm_suspend_valid(suspend_state_t state)
203 {
204         switch (state) {
205         case PM_SUSPEND_STANDBY:
206                 return !!cpm.standby;
207         case PM_SUSPEND_MEM:
208                 return !!cpm.suspend;
209         default:
210                 return 0;
211         }
212 }
213
214 static void cpm_suspend_standby(unsigned int mask)
215 {
216         unsigned long tcr_save;
217
218         /* disable decrement interrupt */
219         tcr_save = mfspr(SPRN_TCR);
220         mtspr(SPRN_TCR, tcr_save & ~TCR_DIE);
221
222         /* go to sleep state */
223         cpm_idle_sleep(mask);
224
225         /* restore decrement interrupt */
226         mtspr(SPRN_TCR, tcr_save);
227 }
228
229 static int cpm_suspend_enter(suspend_state_t state)
230 {
231         switch (state) {
232         case PM_SUSPEND_STANDBY:
233                 cpm_suspend_standby(cpm.standby);
234                 break;
235         case PM_SUSPEND_MEM:
236                 cpm_suspend_standby(cpm.suspend);
237                 break;
238         }
239
240         return 0;
241 }
242
243 static struct platform_suspend_ops cpm_suspend_ops = {
244         .valid          = cpm_suspend_valid,
245         .enter          = cpm_suspend_enter,
246 };
247
248 static int cpm_get_uint_property(struct device_node *np,
249                                  const char *name)
250 {
251         int len;
252         const unsigned int *prop = of_get_property(np, name, &len);
253
254         if (prop == NULL || len < sizeof(u32))
255                 return 0;
256
257         return *prop;
258 }
259
260 static int __init cpm_init(void)
261 {
262         struct device_node *np;
263         int dcr_base, dcr_len;
264         int ret = 0;
265
266         if (!cpm.powersave_off) {
267                 cpm_idle_config(CPM_IDLE_WAIT);
268                 ppc_md.power_save = &cpm_idle;
269         }
270
271         np = of_find_compatible_node(NULL, NULL, "ibm,cpm");
272         if (!np) {
273                 ret = -EINVAL;
274                 goto out;
275         }
276
277         dcr_base = dcr_resource_start(np, 0);
278         dcr_len = dcr_resource_len(np, 0);
279
280         if (dcr_base == 0 || dcr_len == 0) {
281                 printk(KERN_ERR "cpm: could not parse dcr property for %s\n",
282                        np->full_name);
283                 ret = -EINVAL;
284                 goto out;
285         }
286
287         cpm.dcr_host = dcr_map(np, dcr_base, dcr_len);
288
289         if (!DCR_MAP_OK(cpm.dcr_host)) {
290                 printk(KERN_ERR "cpm: failed to map dcr property for %s\n",
291                        np->full_name);
292                 ret = -EINVAL;
293                 goto out;
294         }
295
296         /* All 4xx SoCs with a CPM controller have one of two
297          * different order for the CPM registers. Some have the
298          * CPM registers in the following order (ER,FR,SR). The
299          * others have them in the following order (SR,ER,FR).
300          */
301
302         if (cpm_get_uint_property(np, "er-offset") == 0) {
303                 cpm.dcr_offset[CPM_ER] = 0;
304                 cpm.dcr_offset[CPM_FR] = 1;
305                 cpm.dcr_offset[CPM_SR] = 2;
306         } else {
307                 cpm.dcr_offset[CPM_ER] = 1;
308                 cpm.dcr_offset[CPM_FR] = 2;
309                 cpm.dcr_offset[CPM_SR] = 0;
310         }
311
312         /* Now let's see what IPs to turn off for the following modes */
313
314         cpm.unused = cpm_get_uint_property(np, "unused-units");
315         cpm.idle_doze = cpm_get_uint_property(np, "idle-doze");
316         cpm.standby = cpm_get_uint_property(np, "standby");
317         cpm.suspend = cpm_get_uint_property(np, "suspend");
318
319         /* If some IPs are unused let's turn them off now */
320
321         if (cpm.unused) {
322                 cpm_set(CPM_ER, cpm.unused);
323                 cpm_set(CPM_FR, cpm.unused);
324         }
325
326         /* Now let's export interfaces */
327
328         if (!cpm.powersave_off && cpm.idle_doze)
329                 cpm_idle_config_sysfs();
330
331         if (cpm.standby || cpm.suspend)
332                 suspend_set_ops(&cpm_suspend_ops);
333 out:
334         if (np)
335                 of_node_put(np);
336         return ret;
337 }
338
339 late_initcall(cpm_init);
340
341 static int __init cpm_powersave_off(char *arg)
342 {
343         cpm.powersave_off = 1;
344         return 0;
345 }
346 __setup("powersave=off", cpm_powersave_off);