~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/arch/powerpc/sysdev/ppc4xx_cpm.c

Version: ~ [ linux-5.2-rc5 ] ~ [ linux-5.1.12 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.53 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.128 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.182 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.182 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.19.8 ] ~ [ linux-3.18.140 ] ~ [ linux-3.17.8 ] ~ [ linux-3.16.68 ] ~ [ linux-3.15.10 ] ~ [ linux-3.14.79 ] ~ [ linux-3.13.11 ] ~ [ linux-3.12.74 ] ~ [ linux-3.11.10 ] ~ [ linux-3.10.108 ] ~ [ linux-3.9.11 ] ~ [ linux-3.8.13 ] ~ [ linux-3.7.10 ] ~ [ linux-3.6.11 ] ~ [ linux-3.5.7 ] ~ [ linux-3.4.113 ] ~ [ linux-3.3.8 ] ~ [ linux-3.2.102 ] ~ [ linux-3.1.10 ] ~ [ linux-3.0.101 ] ~ [ linux-2.6.39.4 ] ~ [ linux-2.6.38.8 ] ~ [ linux-2.6.37.6 ] ~ [ linux-2.6.36.4 ] ~ [ linux-2.6.35.14 ] ~ [ linux-2.6.34.15 ] ~ [ linux-2.6.33.20 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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 node_put;
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 node_put;
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 node_put:
334         of_node_put(np);
335 out:
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);
347 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp