]> nv-tegra.nvidia Code Review - linux-2.6.git/blob - arch/arm/mach-tegra/sysfs-cluster.c
arm: tegra: Enable Tegra3 cluster control
[linux-2.6.git] / arch / arm / mach-tegra / sysfs-cluster.c
1 /*
2  * Copyright (c) 2010 NVIDIA Corporation.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * Redistributions of source code must retain the above copyright notice,
9  * this list of conditions and the following disclaimer.
10  *
11  * Redistributions in binary form must reproduce the above copyright notice,
12  * this list of conditions and the following disclaimer in the documentation
13  * and/or other materials provided with the distribution.
14  *
15  * Neither the name of the NVIDIA Corporation nor the names of its contributors
16  * may be used to endorse or promote products derived from this software
17  * without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  *
31  */
32
33 /*
34  * This driver creates the /sys/kernel/cluster node and attributes for CPU
35  * switch testing. Node attributes:
36  *
37  * active: currently active CPU (G or LP)
38  *              write:  'g'      = switch to G CPU
39  *                      'lp'     = switch to LP CPU
40  *                      'toggle' = switch to the other CPU
41  *              read: returns the currently active CPU (g or lp)
42  *
43  * force: force switch even if already on target CPU
44  *              write:  '0' = do not perform switch if
45  *                            active CPU == target CPU (default)
46  *                      '1' = force switch regardless of
47  *                            currently active CPU
48  *              read: returns the current status of the force flag
49  *
50  * immediate: request immediate wake-up from switch request
51  *              write:  '0' = non-immediate wake-up on next interrupt (default)
52  *                      '1' = immediate wake-up
53  *              read: returns the current status of the immediate flag
54  *
55  * power_mode: power mode to use for switch (LP1 or LP2)
56  *              write:  '1' = use LP1 power mode
57  *                      '2' = use LP2 power mode (default)
58  *              read: returns the current status of the immediate flag
59  *
60  * wake_ms: wake time (in milliseconds) -- ignored if immediate==1
61  *              write:  '0' = wake up at the next non-timer interrupt
62  *                      'n' = (n > 0) wake-up after 'n' milliseconds or the
63  *                            next non-timer interrupt (whichever comes first)
64  *              read: returns the current wake_ms value
65  *
66  * Writing the force, immediate and wake_ms attributes simply updates the
67  * state of internal variables that will be used for the next switch request.
68  * Writing to the active attribute initates a switch request using the
69  * current values of the force, immediate, and wake_ms attributes.
70  *
71  * The OS tick timer is not a valid interrupt source for waking up following
72  * a switch request. This is because the kernel uses local timers that are
73  * part of the CPU complex. These get shut down when the CPU complex is
74  * placed into reset by the switch request. If you want a timed wake up
75  * from a switch, you must specify a positive wake_ms value. This will
76  * ensure that a non-local timer is programmed to fire an interrupt
77  * after the desired interval.
78  *
79  */
80
81 #include <linux/module.h>
82 #include <linux/kernel.h>
83 #include <linux/sysfs.h>
84 #include <linux/kobject.h>
85 #include <linux/smp.h>
86 #include <linux/io.h>
87
88 #include <mach/iomap.h>
89 #include "power.h"
90
91 #define SYSFS_CLUSTER_PRINTS       1    /* Nonzero: enable status prints */
92 #define SYSFS_CLUSTER_DEBUG_PRINTS 0    /* Nonzero: enable debug prints */
93 #define SYSFS_CLUSTER_POWER_MODE   0    /* Nonzero: use power modes other than LP2*/
94
95 #if SYSFS_CLUSTER_DEBUG_PRINTS
96 #define DEBUG_CLUSTER(x) printk x
97 #else
98 #define DEBUG_CLUSTER(x)
99 #endif
100
101 #if SYSFS_CLUSTER_PRINTS
102 #define PRINT_CLUSTER(x) printk x
103 #else
104 #define PRINT_CLUSTER(x)
105 #endif
106
107 #define FLOW_CTRL_CLUSTER_CONTROL \
108         (IO_ADDRESS(TEGRA_FLOW_CTRL_BASE) + 0x2c)
109
110 static struct kobject *cluster_kobj;
111 static spinlock_t cluster_lock;
112 static unsigned int flags = 0;
113 static unsigned int wake_ms = 0;
114
115 static ssize_t sysfscluster_show(struct kobject *kobj,
116                 struct kobj_attribute *attr, char *buf);
117
118 static ssize_t sysfscluster_store(struct kobject *kobj,
119                 struct kobj_attribute *attr, const char *buf, size_t count);
120
121 /* Active CPU: "G", "LP", "toggle" */
122 static struct kobj_attribute cluster_active_attr =
123                 __ATTR(active, 0640, sysfscluster_show, sysfscluster_store);
124
125 /* Immediate wake-up when performing switch: 0, 1 */
126 static struct kobj_attribute cluster_immediate_attr =
127                 __ATTR(immediate, 0640, sysfscluster_show, sysfscluster_store);
128
129 /* Force power transition even if already on the desired CPU: 0, 1 */
130 static struct kobj_attribute cluster_force_attr =
131                 __ATTR(force, 0640, sysfscluster_show, sysfscluster_store);
132
133 /* Wake time (in milliseconds) */
134 static struct kobj_attribute cluster_wake_ms_attr =
135                 __ATTR(wake_ms, 0640, sysfscluster_show, sysfscluster_store);
136
137 #if defined(CONFIG_PM) && SYSFS_CLUSTER_POWER_MODE
138 /* LPx power mode to use when switching CPUs: 1=LP1, 2=LP2 */
139 static unsigned int power_mode = 2;
140 static struct kobj_attribute cluster_powermode_attr =
141                 __ATTR(power_mode, 0640, sysfscluster_show, sysfscluster_store);
142 #endif
143
144 typedef enum
145 {
146         ClusterAttr_Invalid = 0,
147         ClusterAttr_Active,
148         ClusterAttr_Immediate,
149         ClusterAttr_Force,
150         ClusterAttr_WakeMs,
151 #if defined(CONFIG_PM) && SYSFS_CLUSTER_POWER_MODE
152         ClusterAttr_PowerMode
153 #endif
154 } ClusterAttr;
155
156 static ClusterAttr GetClusterAttr(const char *name)
157 {
158         if (!strcmp(name, "active"))
159                 return ClusterAttr_Active;
160         if (!strcmp(name, "immediate"))
161                 return ClusterAttr_Immediate;
162         if (!strcmp(name, "force"))
163                 return ClusterAttr_Force;
164         if (!strcmp(name, "wake_ms"))
165                 return ClusterAttr_WakeMs;
166 #if defined(CONFIG_PM) && SYSFS_CLUSTER_POWER_MODE
167         if (!strcmp(name, "power_mode"))
168                 return ClusterAttr_PowerMode;
169 #endif
170         DEBUG_CLUSTER(("GetClusterAttr(%s): invalid\n", name));
171         return ClusterAttr_Invalid;
172 }
173
174 static ssize_t sysfscluster_show(struct kobject *kobj,
175                 struct kobj_attribute *attr, char *buf)
176 {
177         ClusterAttr type;
178         ssize_t len;
179
180         DEBUG_CLUSTER(("+sysfscluster_show\n"));
181
182         type = GetClusterAttr(attr->attr.name);
183         switch (type) {
184         case ClusterAttr_Active:
185                 len = sprintf(buf, "%s\n", is_lp_cluster() ? "LP" : "G");
186                 break;
187
188         case ClusterAttr_Immediate:
189                 len = sprintf(buf, "%d\n",
190                               ((flags & TEGRA_POWER_CLUSTER_IMMEDIATE) != 0));
191                 break;
192
193         case ClusterAttr_Force:
194                 len = sprintf(buf, "%d\n",
195                               ((flags & TEGRA_POWER_CLUSTER_FORCE) != 0));
196                 break;
197
198         case ClusterAttr_WakeMs:
199                 len = sprintf(buf, "%d\n", wake_ms);
200                 break;
201
202 #if defined(CONFIG_PM) && SYSFS_CLUSTER_POWER_MODE
203         case ClusterAttr_PowerMode:
204                 len = sprintf(buf, "%d\n", power_mode);
205                 break;
206 #endif
207
208         default:
209                 len = sprintf(buf, "invalid\n");
210                 break;
211         }
212
213         DEBUG_CLUSTER(("-sysfscluster_show\n"));
214         return len;
215 }
216
217 static ssize_t sysfscluster_store(struct kobject *kobj,
218         struct kobj_attribute *attr, const char *buf, size_t count)
219 {
220         ClusterAttr type;
221         ssize_t ret = count--;
222         unsigned request;
223         int e;
224         int tmp;
225         int cnt;
226
227         DEBUG_CLUSTER(("+sysfscluster_store: %p, %d\n", buf, count));
228
229         /* The count includes data bytes follow by a line feed character. */
230         if (!buf || (count < 1)) {
231                 ret = -EINVAL;
232                 goto fail;
233         }
234
235         type = GetClusterAttr(attr->attr.name);
236
237         spin_lock(&cluster_lock);
238
239         switch (type) {
240         case ClusterAttr_Active:
241                 if (!strncasecmp(buf, "g", count)) {
242                         flags &= ~TEGRA_POWER_CLUSTER_MASK;
243                         flags |= TEGRA_POWER_CLUSTER_G;
244                 } else if (!strncasecmp(buf, "lp", count)) {
245                         flags &= ~TEGRA_POWER_CLUSTER_MASK;
246                         flags |= TEGRA_POWER_CLUSTER_LP;
247                 } else if (!strncasecmp(buf, "toggle", count)) {
248                         flags &= ~TEGRA_POWER_CLUSTER_MASK;
249                         if (is_lp_cluster())
250                                 flags |= TEGRA_POWER_CLUSTER_G;
251                         else
252                                 flags |= TEGRA_POWER_CLUSTER_LP;
253                 } else {
254                         PRINT_CLUSTER(("cluster/active: '%*.*s' invalid, "
255                                 " must be g, lp, or toggle\n",
256                                 count, count, buf));
257                         ret = -EINVAL;
258                         break;
259                 }
260                 PRINT_CLUSTER(("cluster/active -> %s\n",
261                         (flags & TEGRA_POWER_CLUSTER_G) ? "G" : "LP"));
262
263                 request = flags;
264 #if defined(CONFIG_PM) && SYSFS_CLUSTER_POWER_MODE
265                 if (power_mode == 1) {
266                         request |= TEGRA_POWER_SDRAM_SELFREFRESH;
267                 }
268 #endif
269                 e = tegra_cluster_control(wake_ms * 1000, request);
270                 if (e) {
271                         PRINT_CLUSTER(("cluster/active: request failed (%d)\n",
272                                        e));
273                         ret = e;
274                 }
275                 break;
276
277         case ClusterAttr_Immediate:
278                 if ((count == 1) && (*buf == '0'))
279                         flags &= ~TEGRA_POWER_CLUSTER_IMMEDIATE;
280                 else if ((count == 1) && *buf == '1')
281                         flags |= TEGRA_POWER_CLUSTER_IMMEDIATE;
282                 else {
283                         PRINT_CLUSTER(("cluster/immediate: '%*.*s' invalid, "
284                                 "must be 0 or 1\n", count, count, buf));
285                         ret = -EINVAL;
286                         break;
287                 }
288                 PRINT_CLUSTER(("cluster/immediate -> %c\n",
289                         (flags & TEGRA_POWER_CLUSTER_IMMEDIATE) ? '1' : '0'));
290                 break;
291
292         case ClusterAttr_Force:
293                 if ((count == 1) && (*buf == '0'))
294                         flags &= ~TEGRA_POWER_CLUSTER_FORCE;
295                 else if ((count == 1) && (*buf == '1'))
296                         flags |= TEGRA_POWER_CLUSTER_FORCE;
297                 else {
298                         PRINT_CLUSTER(("cluster/force: '%*.*s' invalid, "
299                                 "must be 0 or 1\n", count, count, buf));
300                         ret = -EINVAL;
301                         break;
302                 }
303                 PRINT_CLUSTER(("cluster/force -> %c\n",
304                         (flags & TEGRA_POWER_CLUSTER_FORCE) ? '1' : '0'));
305                 break;
306
307         case ClusterAttr_WakeMs:
308                 tmp = 0;
309                 cnt = sscanf(buf, "%d\n", &tmp);
310                 if ((cnt != 1) || (tmp < 0)) {
311                         PRINT_CLUSTER(("cluster/wake_ms: '%*.*s' is invalid\n",
312                                 count, count, buf));
313                         ret = -EINVAL;
314                         break;
315                 }
316                 wake_ms = tmp;
317                 PRINT_CLUSTER(("cluster/wake_ms -> %d\n", wake_ms));
318                 break;
319
320 #if defined(CONFIG_PM) && SYSFS_CLUSTER_POWER_MODE
321         case ClusterAttr_PowerMode:
322                 if ((count == 1) && (*buf == '2'))
323                         power_mode = 2;
324                 else if ((count == 1) && *buf == '1')
325                         power_mode = 1;
326                 else {
327                         PRINT_CLUSTER(("cluster/power_mode: '%*.*s' invalid, "
328                                 "must be 2 or 1\n", count, count, buf));
329                         ret = -EINVAL;
330                         break;
331                 }
332                 PRINT_CLUSTER(("cluster/power_mode -> %d\n", power_mode));
333                 break;
334 #endif
335
336         default:
337                 ret = -ENOENT;
338                 break;
339         }
340
341         spin_unlock(&cluster_lock);
342
343 fail:
344         DEBUG_CLUSTER(("-sysfscluster_store: %d\n", count));
345         return ret;
346 }
347
348 #define CREATE_FILE(x) \
349         do { \
350                 e = sysfs_create_file(cluster_kobj, &cluster_##x##_attr.attr); \
351                 if (e) { \
352                         DEBUG_CLUSTER(("cluster/" __stringify(x) \
353                                 ": sysfs_create_file failed!\n")); \
354                         goto fail; \
355                 } \
356         } while (0)
357
358 static int __init sysfscluster_init(void)
359 {
360         int e;
361
362         DEBUG_CLUSTER(("+sysfscluster_init\n"));
363
364         spin_lock_init(&cluster_lock);
365         cluster_kobj = kobject_create_and_add("cluster", kernel_kobj);
366
367         CREATE_FILE(active);
368         CREATE_FILE(immediate);
369         CREATE_FILE(force);
370         CREATE_FILE(wake_ms);
371 #if defined(CONFIG_PM) && SYSFS_CLUSTER_POWER_MODE
372         CREATE_FILE(powermode);
373 #endif
374
375         spin_lock(&cluster_lock);
376         if (is_lp_cluster())
377                 flags |= TEGRA_POWER_CLUSTER_LP;
378         else
379                 flags |= TEGRA_POWER_CLUSTER_G;
380         spin_unlock(&cluster_lock);
381
382 fail:
383         DEBUG_CLUSTER(("-sysfscluster_init\n"));
384         return e;
385 }
386
387 #define REMOVE_FILE(x) \
388                 sysfs_remove_file(cluster_kobj, &cluster_##x##_attr.attr)
389
390 static void __exit sysfscluster_exit(void)
391 {
392         DEBUG_CLUSTER(("+sysfscluster_exit\n"));
393 #if defined(CONFIG_PM) && SYSFS_CLUSTER_POWER_MODE
394         REMOVE_FILE(powermode);
395 #endif
396         REMOVE_FILE(wake_ms);
397         REMOVE_FILE(force);
398         REMOVE_FILE(immediate);
399         REMOVE_FILE(active);
400         kobject_del(cluster_kobj);
401         DEBUG_CLUSTER(("-sysfscluster_exit\n"));
402 }
403
404 module_init(sysfscluster_init);
405 module_exit(sysfscluster_exit);
406 MODULE_LICENSE("GPL");