[ARM/tegra] Add Tegra3 support
[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   1    /* 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 #define FLOW_CTRL_CPUx_CSR(cpu) \
110         (IO_ADDRESS(TEGRA_FLOW_CTRL_BASE + ((cpu)?(((cpu)-1)*8 + 0x18) : 0x8)))
111
112 static struct kobject *cluster_kobj;
113 static spinlock_t cluster_lock;
114 static unsigned int flags = 0;
115 static unsigned int power_mode = 2;
116 static unsigned int wake_ms = 0;
117
118 static ssize_t sysfscluster_show(struct kobject *kobj,
119                 struct kobj_attribute *attr, char *buf);
120
121 static ssize_t sysfscluster_store(struct kobject *kobj,
122                 struct kobj_attribute *attr, const char *buf, size_t count);
123
124 /* Active CPU: "G", "LP", "toggle" */
125 static struct kobj_attribute cluster_active_attr =
126                 __ATTR(active, 0640, sysfscluster_show, sysfscluster_store);
127
128 /* Immediate wake-up when performing switch: 0, 1 */
129 static struct kobj_attribute cluster_immediate_attr =
130                 __ATTR(immediate, 0640, sysfscluster_show, sysfscluster_store);
131
132 /* Force power transition even if already on the desired CPU: 0, 1 */
133 static struct kobj_attribute cluster_force_attr =
134                 __ATTR(force, 0640, sysfscluster_show, sysfscluster_store);
135
136 /* Wake time (in milliseconds) */
137 static struct kobj_attribute cluster_wake_ms_attr =
138                 __ATTR(wake_ms, 0640, sysfscluster_show, sysfscluster_store);
139
140 #if SYSFS_CLUSTER_POWER_MODE
141 /* LPx power mode to use when switching CPUs: 1, 2 */
142 static struct kobj_attribute cluster_powermode_attr =
143                 __ATTR(power_mode, 0640, sysfscluster_show, sysfscluster_store);
144 #endif
145
146 typedef enum
147 {
148         ClusterAttr_Invalid = 0,
149         ClusterAttr_Active,
150         ClusterAttr_Immediate,
151         ClusterAttr_Force,
152         ClusterAttr_WakeMs,
153 #if SYSFS_CLUSTER_POWER_MODE
154         ClusterAttr_PowerMode
155 #endif
156 } ClusterAttr;
157
158 static ClusterAttr GetClusterAttr(const char *name)
159 {
160         if (!strcmp(name, "active"))
161                 return ClusterAttr_Active;
162         if (!strcmp(name, "immediate"))
163                 return ClusterAttr_Immediate;
164         if (!strcmp(name, "force"))
165                 return ClusterAttr_Force;
166         if (!strcmp(name, "wake_ms"))
167                 return ClusterAttr_WakeMs;
168 #if SYSFS_CLUSTER_POWER_MODE
169         if (!strcmp(name, "power_mode"))
170                 return ClusterAttr_PowerMode;
171 #endif
172         DEBUG_CLUSTER(("GetClusterAttr(%s): invalid\n", name));
173         return ClusterAttr_Invalid;
174 }
175
176 static ssize_t sysfscluster_show(struct kobject *kobj,
177                 struct kobj_attribute *attr, char *buf)
178 {
179         ClusterAttr type;
180         ssize_t len;
181
182         DEBUG_CLUSTER(("+sysfscluster_show\n"));
183
184         type = GetClusterAttr(attr->attr.name);
185         switch (type) {
186         case ClusterAttr_Active:
187                 len = sprintf(buf, "%s\n", is_lp_cluster() ? "LP" : "G");
188                 break;
189
190         case ClusterAttr_Immediate:
191                 len = sprintf(buf, "%d\n",
192                               ((flags & TEGRA_POWER_CLUSTER_IMMEDIATE) != 0));
193                 break;
194
195         case ClusterAttr_Force:
196                 len = sprintf(buf, "%d\n",
197                               ((flags & TEGRA_POWER_CLUSTER_FORCE) != 0));
198                 break;
199
200         case ClusterAttr_WakeMs:
201                 len = sprintf(buf, "%d\n", wake_ms);
202                 break;
203
204 #if SYSFS_CLUSTER_POWER_MODE
205         case ClusterAttr_PowerMode:
206                 len = sprintf(buf, "%d\n", power_mode);
207                 break;
208 #endif
209
210         default:
211                 len = sprintf(buf, "invalid\n");
212                 break;
213         }
214
215         DEBUG_CLUSTER(("-sysfscluster_show\n"));
216         return len;
217 }
218
219 static ssize_t sysfscluster_store(struct kobject *kobj,
220         struct kobj_attribute *attr, const char *buf, size_t count)
221 {
222         ClusterAttr type;
223         ssize_t ret = count--;
224         unsigned request;
225         int e;
226         int tmp;
227         int cnt;
228
229         DEBUG_CLUSTER(("+sysfscluster_store: %p, %d\n", buf, count));
230
231         /* The count includes data bytes follow by a line feed character. */
232         if (!buf || (count < 1)) {
233                 ret = -EINVAL;
234                 goto fail;
235         }
236
237         type = GetClusterAttr(attr->attr.name);
238
239         spin_lock(&cluster_lock);
240
241         switch (type) {
242         case ClusterAttr_Active:
243                 if (!strncasecmp(buf, "g", count)) {
244                         flags &= ~TEGRA_POWER_CLUSTER_MASK;
245                         flags |= TEGRA_POWER_CLUSTER_G;
246                 } else if (!strncasecmp(buf, "lp", count)) {
247                         flags &= ~TEGRA_POWER_CLUSTER_MASK;
248                         flags |= TEGRA_POWER_CLUSTER_LP;
249                 } else if (!strncasecmp(buf, "toggle", count)) {
250                         flags &= ~TEGRA_POWER_CLUSTER_MASK;
251                         if (is_lp_cluster())
252                                 flags |= TEGRA_POWER_CLUSTER_G;
253                         else
254                                 flags |= TEGRA_POWER_CLUSTER_LP;
255                 } else {
256                         PRINT_CLUSTER(("cluster/active: '%*.*s' invalid, "
257                                 " must be g, lp, or toggle\n",
258                                 count, count, buf));
259                         ret = -EINVAL;
260                         break;
261                 }
262                 PRINT_CLUSTER(("cluster/active -> %s\n",
263                         (flags & TEGRA_POWER_CLUSTER_G) ? "G" : "LP"));
264
265                 request = flags;
266 #if SYSFS_CLUSTER_POWER_MODE
267                 if (power_mode == 1) {
268                         request |= TEGRA_POWER_SDRAM_SELFREFRESH;
269                 }
270 #endif
271                 e = tegra_cluster_control(wake_ms * 1000, request);
272                 if (e) {
273                         PRINT_CLUSTER(("cluster/active: request failed (%d)\n",
274                                        e));
275                         ret = e;
276                 }
277                 break;
278
279         case ClusterAttr_Immediate:
280                 if ((count == 1) && (*buf == '0'))
281                         flags &= ~TEGRA_POWER_CLUSTER_IMMEDIATE;
282                 else if ((count == 1) && *buf == '1')
283                         flags |= TEGRA_POWER_CLUSTER_IMMEDIATE;
284                 else {
285                         PRINT_CLUSTER(("cluster/immediate: '%*.*s' invalid, "
286                                 "must be 0 or 1\n", count, count, buf));
287                         ret = -EINVAL;
288                         break;
289                 }
290                 PRINT_CLUSTER(("cluster/immediate -> %c\n",
291                         (flags & TEGRA_POWER_CLUSTER_IMMEDIATE) ? '1' : '0'));
292                 break;
293
294         case ClusterAttr_Force:
295                 if ((count == 1) && (*buf == '0'))
296                         flags &= ~TEGRA_POWER_CLUSTER_FORCE;
297                 else if ((count == 1) && (*buf == '1'))
298                         flags |= TEGRA_POWER_CLUSTER_FORCE;
299                 else {
300                         PRINT_CLUSTER(("cluster/force: '%*.*s' invalid, "
301                                 "must be 0 or 1\n", count, count, buf));
302                         ret = -EINVAL;
303                         break;
304                 }
305                 PRINT_CLUSTER(("cluster/force -> %c\n",
306                         (flags & TEGRA_POWER_CLUSTER_FORCE) ? '1' : '0'));
307                 break;
308
309         case ClusterAttr_WakeMs:
310                 tmp = 0;
311                 cnt = sscanf(buf, "%d\n", &tmp);
312                 if ((cnt != 1) || (tmp < 0)) {
313                         PRINT_CLUSTER(("cluster/wake_ms: '%*.*s' is invalid\n",
314                                 count, count, buf));
315                         ret = -EINVAL;
316                         break;
317                 }
318                 wake_ms = tmp;
319                 PRINT_CLUSTER(("cluster/wake_ms -> %d\n", wake_ms));
320                 break;
321
322 #if SYSFS_CLUSTER_POWER_MODE
323         case ClusterAttr_PowerMode:
324                 if ((count == 1) && (*buf == '2'))
325                         power_mode = 2;
326                 else if ((count == 1) && *buf == '1')
327                         power_mode = 1;
328                 else {
329                         PRINT_CLUSTER(("cluster/power_mode: '%*.*s' invalid, "
330                                 "must be 2 or 1\n", count, count, buf));
331                         ret = -EINVAL;
332                         break;
333                 }
334                 PRINT_CLUSTER(("cluster/power_mode -> %d\n", power_mode));
335                 break;
336 #endif
337
338         default:
339                 ret = -ENOENT;
340                 break;
341         }
342
343         spin_unlock(&cluster_lock);
344
345 fail:
346         DEBUG_CLUSTER(("-sysfscluster_store: %d\n", count));
347         return ret;
348 }
349
350 #define CREATE_FILE(x) \
351         do { \
352                 e = sysfs_create_file(cluster_kobj, &cluster_##x##_attr.attr); \
353                 if (e) { \
354                         DEBUG_CLUSTER(("cluster/" __stringify(x) \
355                                 ": sysfs_create_file failed!\n")); \
356                         goto fail; \
357                 } \
358         } while (0)
359
360 static int __init sysfscluster_init(void)
361 {
362         int e;
363
364         DEBUG_CLUSTER(("+sysfscluster_init\n"));
365
366         spin_lock_init(&cluster_lock);
367         cluster_kobj = kobject_create_and_add("cluster", kernel_kobj);
368
369         CREATE_FILE(active);
370         CREATE_FILE(immediate);
371         CREATE_FILE(force);
372         CREATE_FILE(wake_ms);
373 #if SYSFS_CLUSTER_POWER_MODE
374         CREATE_FILE(powermode);
375 #endif
376
377         spin_lock(&cluster_lock);
378         if (is_lp_cluster())
379                 flags |= TEGRA_POWER_CLUSTER_LP;
380         else
381                 flags |= TEGRA_POWER_CLUSTER_G;
382         spin_unlock(&cluster_lock);
383
384 fail:
385         DEBUG_CLUSTER(("-sysfscluster_init\n"));
386         return e;
387 }
388
389 #define REMOVE_FILE(x) \
390                 sysfs_remove_file(cluster_kobj, &cluster_##x##_attr.attr)
391
392 static void __exit sysfscluster_exit(void)
393 {
394         DEBUG_CLUSTER(("+sysfscluster_exit\n"));
395 #if SYSFS_CLUSTER_POWER_MODE
396         REMOVE_FILE(powermode);
397 #endif
398         REMOVE_FILE(wake_ms);
399         REMOVE_FILE(force);
400         REMOVE_FILE(immediate);
401         REMOVE_FILE(active);
402         kobject_del(cluster_kobj);
403         DEBUG_CLUSTER(("-sysfscluster_exit\n"));
404 }
405
406 module_init(sysfscluster_init);
407 module_exit(sysfscluster_exit);
408 MODULE_LICENSE("GPL");