ARM: tegra12: set CPU rate to 2.2GHz for sku 0x87
[linux-3.10.git] / arch / arm / mach-tegra / tegra_simon.c
1 /*
2  * arch/arm/mach-tegra/tegra_simon.c
3  *
4  * Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved.
5  *
6  * This software is licensed under the terms of the GNU General Public
7  * License version 2, as published by the Free Software Foundation, and
8  * may be copied, distributed, and modified under those terms.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  */
16
17 #include <linux/kernel.h>
18 #include <linux/init.h>
19 #include <linux/module.h>
20 #include <linux/err.h>
21 #include <linux/debugfs.h>
22 #include <linux/seq_file.h>
23 #include <linux/hrtimer.h>
24 #include <linux/thermal.h>
25 #include <linux/regulator/consumer.h>
26
27 #include "tegra_simon.h"
28
29 static DEFINE_MUTEX(simon_lock);
30 static RAW_NOTIFIER_HEAD(simon_nh);
31 static void tegra_simon_grade_update(struct work_struct *work);
32
33 static u32 grading_sec = TEGRA_SIMON_GRADING_INTERVAL_SEC;
34
35 static struct tegra_simon_grader simon_graders[TEGRA_SIMON_DOMAIN_NUM] = {
36         [TEGRA_SIMON_DOMAIN_CPU] = {
37                 .domain_name = "cpu",
38                 .domain = TEGRA_SIMON_DOMAIN_CPU,
39         },
40         [TEGRA_SIMON_DOMAIN_GPU] = {
41                 .domain_name = "gpu",
42                 .domain = TEGRA_SIMON_DOMAIN_GPU,
43         },
44 };
45
46 /*
47  * GPU grading is implemented within vdd_gpu post-change notification chain that
48  * guarantees constant voltage during grading. First grading after boot can be
49  * executed anytime set voltage is below specified threshold, next grading is
50  * always separated by the grading interval from the last successful grading.
51  */
52 static int tegra_simon_gpu_grading_cb(
53         struct notifier_block *nb, unsigned long event, void *v)
54 {
55         int mv = (int)((long)v);
56         struct tegra_simon_grader *grader = container_of(
57                 nb, struct tegra_simon_grader, grading_condition_nb);
58         ktime_t now = ktime_get();
59         unsigned long t;
60         int grade = 0;
61
62         if (!(event & REGULATOR_EVENT_OUT_POSTCHANGE))
63                 return NOTIFY_DONE;
64
65         mv = (mv > 0) ? mv / 1000 : mv;
66         if ((mv <= 0) || (mv > grader->grading_mv_limit))
67                 return NOTIFY_OK;
68
69         if (grader->last_grading.tv64 &&
70             (ktime_to_ms(ktime_sub(now, grader->last_grading)) <
71              (s64)grading_sec * 1000))
72                 return NOTIFY_OK;
73
74         if (thermal_zone_get_temp(grader->tzd, &t)) {
75                 pr_err("%s: Failed to get %s temperature\n",
76                        __func__, grader->domain_name);
77                 return NOTIFY_OK;
78         }
79
80         if (grader->grade_simon_domain) {
81                 grade = grader->grade_simon_domain(grader->domain, mv, t);
82                 if (grade < 0) {
83                         pr_err("%s: Failed to grade %s\n",
84                                __func__, grader->domain_name);
85                         return NOTIFY_OK;
86                 }
87         }
88
89         grader->last_grading = now;
90         if (grader->grade != grade) {
91                 set_mb(grader->grade, grade);
92                 schedule_work(&grader->grade_update_work);
93         }
94         pr_info("%s: graded %s: v = %d, t = %lu, grade = %d\n",
95                 __func__, grader->domain_name, mv, t, grade);
96         return NOTIFY_OK;
97 }
98
99 static int __init tegra_simon_init_gpu(void)
100 {
101         struct tegra_simon_grader *grader =
102                 &simon_graders[TEGRA_SIMON_DOMAIN_GPU];
103         struct regulator *reg;
104
105         INIT_WORK(&grader->grade_update_work, tegra_simon_grade_update);
106
107         grader->tzd = thermal_zone_device_find_by_name("GPU-therm");
108         if (!grader->tzd) {
109                 pr_err("%s: Failed to find %s thermal zone\n",
110                        __func__, grader->domain_name);
111                 return -ENOENT;
112         }
113
114         reg = regulator_get(NULL, "vdd_gpu_simon");
115         if (IS_ERR(reg)) {
116                 pr_err("%s: Failed to get vdd_%s regulator\n",
117                        __func__, grader->domain_name);
118                 return PTR_ERR(reg);
119         }
120
121         grader->grading_condition_nb.notifier_call = tegra_simon_gpu_grading_cb;
122         regulator_register_notifier(reg, &grader->grading_condition_nb);
123         regulator_put(reg);
124
125         /* FIXME: settings below are tegar12_ specific */
126         grader->grading_mv_limit = 850;
127         grader->grade_simon_domain = NULL;
128
129         return 0;
130 }
131
132 static int __init tegra_simon_init_cpu(void)
133 {
134         struct tegra_simon_grader *grader =
135                 &simon_graders[TEGRA_SIMON_DOMAIN_CPU];
136
137         INIT_WORK(&grader->grade_update_work, tegra_simon_grade_update);
138
139         return 0;
140 }
141
142 int tegra_register_simon_notifier(struct notifier_block *nb)
143 {
144         int ret;
145
146         mutex_lock(&simon_lock);
147         ret = raw_notifier_chain_register(&simon_nh, nb);
148         mutex_unlock(&simon_lock);
149         return ret;
150 }
151
152 void tegra_unregister_simon_notifier(struct notifier_block *nb)
153 {
154         mutex_lock(&simon_lock);
155         raw_notifier_chain_unregister(&simon_nh, nb);
156         mutex_unlock(&simon_lock);
157 }
158
159 static void tegra_simon_grade_update(struct work_struct *work)
160 {
161         struct tegra_simon_grader *grader = container_of(
162                 work, struct tegra_simon_grader, grade_update_work);
163
164         mutex_lock(&simon_lock);
165         raw_notifier_call_chain(&simon_nh, grader->grade,
166                                 (void *)((long)grader->domain));
167         mutex_unlock(&simon_lock);
168 }
169
170 #ifdef CONFIG_DEBUG_FS
171
172 static int grade_get(void *data, u64 *val)
173 {
174         struct tegra_simon_grader *grader = data;
175
176         if (grader->domain >= TEGRA_SIMON_DOMAIN_NUM) {
177                 *val = -EINVAL;
178                 return -EINVAL;
179         }
180
181         *val = grader->grade;
182         return 0;
183 }
184 static int grade_set(void *data, u64 val)
185 {
186         struct tegra_simon_grader *grader = data;
187
188         if (grader->domain >= TEGRA_SIMON_DOMAIN_NUM)
189                 return -EINVAL;
190
191         if (grader->grade != (int)val) {
192                 grader->grade = (int)val;
193                 schedule_work(&grader->grade_update_work);
194         }
195         return 0;
196 }
197 DEFINE_SIMPLE_ATTRIBUTE(grade_fops, grade_get, grade_set, "%llu\n");
198
199 static int __init simon_debugfs_init_domain(struct dentry *dir,
200                                             struct tegra_simon_grader *grader)
201 {
202         struct dentry *d;
203
204         d = debugfs_create_dir(grader->domain_name, dir);
205         if (!d)
206                 return -ENOMEM;
207
208         if (!debugfs_create_file("grade", S_IWUSR | S_IRUGO, d,
209                                  (void *)grader, &grade_fops))
210                 return -ENOMEM;
211
212         return 0;
213 }
214
215 static int __init simon_debugfs_init(void)
216 {
217         int i;
218         struct tegra_simon_grader *grader;
219         struct dentry *dir;
220
221         dir = debugfs_create_dir("tegra_simon", NULL);
222         if (!dir)
223                 return -ENOMEM;
224
225         if (!debugfs_create_u32("grading_sec", S_IWUSR | S_IRUGO, dir,
226                                 &grading_sec))
227                 goto err_out;
228
229         for (i = 0; i < TEGRA_SIMON_DOMAIN_NUM; i++) {
230                 grader = &simon_graders[i];
231
232                 if (!grader->domain_name)
233                         continue;
234
235                 if (simon_debugfs_init_domain(dir, grader))
236                         goto err_out;
237         }
238
239         return 0;
240
241 err_out:
242         debugfs_remove_recursive(dir);
243         return -ENOMEM;
244 }
245 #endif
246
247 static int __init tegra_simon_init(void)
248 {
249         tegra_simon_init_gpu();
250         tegra_simon_init_cpu();
251
252 #ifdef CONFIG_DEBUG_FS
253         simon_debugfs_init();
254 #endif
255         return 0;
256
257 }
258 late_initcall_sync(tegra_simon_init);
259