misc: tegra-baseband: support bypass/boost DC/DC
[linux-3.10.git] / drivers / misc / idle_test_t132.c
1 /*
2  * drivers/misc/idle_test_t132.c
3  *
4  * Copyright (C) 2014 NVIDIA Corporation. All rights reserved.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; version 2 of the License.
9  *
10  * This program is distributed in the hope that it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
13  * more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18  *
19  */
20
21 #include <linux/init.h>
22 #include <linux/module.h>
23 #include <linux/debugfs.h>
24 #include <linux/kernel.h>
25 #include <linux/fs.h>
26 #include <linux/uaccess.h>
27 #include <linux/ktime.h>
28 #include <linux/interrupt.h>
29 #include <linux/suspend.h>
30 #include <linux/tick.h>
31 #include <linux/irq.h>
32 #include <linux/interrupt.h>
33 #include <linux/tick.h>
34
35 #include "../../kernel/irq/internals.h"
36 #include "../../arch/arm64/mach-tegra/pm-tegra132.h"
37 #include "../../arch/arm64/mach-tegra/denver-knobs.h"
38
39 #define DEBUG_HOOKS     0
40
41 #if DEBUG_HOOKS
42 #define DBG_PRINT(fmt, arg...)  pr_err(fmt, ## arg)
43 #else
44 #define DBG_PRINT(fmt, arg...)
45 #endif
46
47 #define POWER_BACKDOOR_I2C_CC4_VOLTAGE_DOWN             0x14
48 #define POWER_BACKDOOR_I2C_CC4_VOLTAGE_UP               0x15
49 #define POWER_BACKDOOR_SLEEP_EN_CC4_CORE_MASK           0x16
50 #define POWER_BACKDOOR_SLEEP_EN_CC4_CLUSTER_MASK        0x17
51
52 static struct dentry *dfs_dir;
53 static long pmstate = T132_CLUSTER_C4;
54
55 static void tegra132_do_idle(ulong pmstate)
56 {
57         asm volatile(
58         "       msr actlr_el1, %0\n"
59         "       wfi\n"
60         :
61         : "r" (pmstate));
62 }
63
64 static void tegra132_backdoor(long data, ulong command)
65 {
66         asm volatile(
67         "       sys 0, c11, c0, 1, %0\n"
68         "       sys 0, c11, c0, 0, %1\n"
69         :
70         : "r" (data), "r" (command));
71 }
72
73 void suspend_all_device_irqs(void)
74 {
75         struct irq_desc *desc;
76         int irq;
77
78         for_each_irq_desc(irq, desc) {
79                 unsigned long flags;
80
81                 raw_spin_lock_irqsave(&desc->lock, flags);
82                 __disable_irq(desc, irq, false);
83                 desc->istate |= IRQS_SUSPENDED;
84                 raw_spin_unlock_irqrestore(&desc->lock, flags);
85         }
86
87         for_each_irq_desc(irq, desc)
88                 if (desc->istate & IRQS_SUSPENDED)
89                         synchronize_irq(irq);
90 }
91
92 void resume_all_device_irqs(void)
93 {
94         struct irq_desc *desc;
95         int irq;
96
97         for_each_irq_desc(irq, desc) {
98                 unsigned long flags;
99
100                 raw_spin_lock_irqsave(&desc->lock, flags);
101                 __enable_irq(desc, irq, true);
102                 raw_spin_unlock_irqrestore(&desc->lock, flags);
103         }
104 }
105
106 static int idle_write(void *data, u64 val)
107 {
108         unsigned long timer_interval_us = 0;
109         ktime_t time, interval, sleep;
110
111         timer_interval_us = (ulong)val;
112
113         preempt_disable();
114         suspend_all_device_irqs();
115         tick_nohz_idle_enter();
116         stop_critical_timings();
117         local_fiq_disable();
118         local_irq_disable();
119
120         /* Disable bgallow */
121         denver_set_bg_allowed(0, false);
122         denver_set_bg_allowed(1, false);
123
124         interval = ktime_set(0, (NSEC_PER_USEC * timer_interval_us));
125
126         time = ktime_get();
127         sleep = ktime_add(time, interval);
128         tick_program_event(sleep, true);
129
130         tegra132_do_idle(pmstate);
131
132         sleep = ktime_sub(ktime_get(), time);
133         time = ktime_sub(sleep, interval);
134         trace_printk("idle: %lld, exit latency: %lld\n", sleep.tv64, time.tv64);
135
136         /* Re-enable bgallow */
137         denver_set_bg_allowed(1, true);
138         denver_set_bg_allowed(0, true);
139
140         local_fiq_enable();
141         local_irq_enable();
142         start_critical_timings();
143         tick_nohz_idle_exit();
144         resume_all_device_irqs();
145         preempt_enable_no_resched();
146         return 0;
147 }
148
149 /* voltage write file operation */
150 static int voltage_write(void *data, u64 val)
151 {
152         long retention_vol = (long)val;
153
154         DBG_PRINT("retention_vol is %ld\n", retention_vol);
155
156         tegra132_backdoor(retention_vol,
157                         POWER_BACKDOOR_I2C_CC4_VOLTAGE_DOWN);
158
159         return 0;
160 }
161
162 static int pmstate_write(void *data, u64 val)
163 {
164         pmstate = (long)val;
165
166         DBG_PRINT("pmstate is %ld\n", pmstate);
167
168         return 0;
169 }
170
171 static int sleep_core_mask_write(void *data, u64 val)
172 {
173         long sleep_core_mask = (long)val;
174
175         DBG_PRINT("sleep_core_mask is %ld\n", sleep_core_mask);
176
177         tegra132_backdoor(sleep_core_mask,
178                         POWER_BACKDOOR_SLEEP_EN_CC4_CORE_MASK);
179
180         return 0;
181 }
182
183 static int sleep_cluster_mask_write(void *data, u64 val)
184 {
185         long sleep_cluster_mask = (long)val;
186
187         DBG_PRINT("sleep_cluster_mask is %ld\n", sleep_cluster_mask);
188
189         tegra132_backdoor(sleep_cluster_mask,
190                         POWER_BACKDOOR_SLEEP_EN_CC4_CLUSTER_MASK);
191
192         return 0;
193 }
194
195 DEFINE_SIMPLE_ATTRIBUTE(idle_fops, NULL, idle_write, "%llu\n");
196
197 DEFINE_SIMPLE_ATTRIBUTE(voltage_fops, NULL, voltage_write, "%llu\n");
198
199 DEFINE_SIMPLE_ATTRIBUTE(pmstate_fops, NULL, pmstate_write, "%llu\n");
200
201 DEFINE_SIMPLE_ATTRIBUTE(sleep_core_mask_fops, NULL,
202         sleep_core_mask_write, "%llu\n");
203
204 DEFINE_SIMPLE_ATTRIBUTE(sleep_cluster_mask_fops, NULL,
205         sleep_cluster_mask_write, "%llu\n");
206
207 static int __init init_debug(void)
208 {
209         struct dentry *dfs_file;
210         int filevalue;
211
212         dfs_dir = debugfs_create_dir("denver_idle_test", NULL);
213         dfs_file = debugfs_create_file("idlecpu", 0644, dfs_dir,
214                                         &filevalue, &idle_fops);
215         if (!dfs_file) {
216                 DBG_PRINT("error creating idlecpu file");
217                 return -ENODEV;
218         }
219
220         dfs_file = debugfs_create_file("voltage", 0644, dfs_dir,
221                                         &filevalue, &voltage_fops);
222         if (!dfs_file) {
223                 DBG_PRINT("error creating voltage file");
224                 return -ENODEV;
225         }
226
227         dfs_file = debugfs_create_file("pmstate", 0644, dfs_dir,
228                                         &filevalue, &pmstate_fops);
229         if (!dfs_file) {
230                 DBG_PRINT("error creating pmstate file");
231                 return -ENODEV;
232         }
233
234         dfs_file = debugfs_create_file("sleep_core_mask", 0644, dfs_dir,
235                                         &filevalue, &sleep_core_mask_fops);
236         if (!dfs_file) {
237                 DBG_PRINT("error creating sleep_core_mask file");
238                 return -ENODEV;
239         }
240
241         dfs_file = debugfs_create_file("sleep_cluster_mask", 0644, dfs_dir,
242                                         &filevalue, &sleep_cluster_mask_fops);
243         if (!dfs_file) {
244                 DBG_PRINT("error creating sleep_cluster_mask file");
245                 return -ENODEV;
246         }
247
248         return 0;
249 }
250 module_init(init_debug);
251
252 static void __exit exit_debug(void)
253 {
254         debugfs_remove_recursive(dfs_dir);
255 }
256 module_exit(exit_debug);
257
258 MODULE_LICENSE("GPL");
259 MODULE_DESCRIPTION("Kernel HOOKS");