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