blob: f11b9fcc4a6fdeae968b251e5a52031ce9a75549 [file] [log] [blame]
Matt Wagantall488bef32012-07-13 19:42:11 -07001/*
2 * Copyright (c) 2012, The Linux Foundation. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 and
6 * only version 2 as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 */
13
14#define pr_fmt(fmt) "%s: " fmt, __func__
15
16#include <linux/kernel.h>
17#include <linux/io.h>
18#include <linux/debugfs.h>
19#include <linux/module.h>
20#include <linux/mutex.h>
21#include <linux/err.h>
22#include <linux/errno.h>
23#include <linux/cpu.h>
24#include <linux/smp.h>
25
26#include <mach/msm_bus.h>
27#include <mach/msm-krait-l2-accessors.h>
28
29#include "acpuclock-krait.h"
30
31static struct drv_data *drv;
32static DEFINE_MUTEX(debug_lock);
33
34struct acg_action {
35 bool set;
36 bool enable;
37};
Matt Wagantallaaef70a2012-10-15 19:35:19 -070038static int l2_acg_en_val;
Matt Wagantall488bef32012-07-13 19:42:11 -070039static struct dentry *base_dir;
40static struct dentry *sc_dir[MAX_SCALABLES];
41
42static void cpu_action(void *info)
43{
44 struct acg_action *action = info;
45
46 u32 val;
47 asm volatile ("mrc p15, 7, %[cpmr0], c15, c0, 5\n\t"
48 : [cpmr0]"=r" (val));
49 if (action->set) {
50 if (action->enable)
51 val &= ~BIT(0);
52 else
53 val |= BIT(0);
Matt Wagantallaaef70a2012-10-15 19:35:19 -070054 asm volatile ("mcr p15, 7, %[cpmr0], c15, c0, 5\n\t"
55 : : [cpmr0]"r" (val));
Matt Wagantall488bef32012-07-13 19:42:11 -070056 } else {
57 action->enable = !(val & BIT(0));
58 }
59}
60
61/* Disable auto clock-gating for a scalable. */
62static void disable_acg(int sc_id)
63{
64 u32 regval;
65
66 if (sc_id == L2) {
67 regval = get_l2_indirect_reg(drv->scalable[sc_id].l2cpmr_iaddr);
Matt Wagantallaaef70a2012-10-15 19:35:19 -070068 l2_acg_en_val = regval & (0x3 << 10);
Matt Wagantall488bef32012-07-13 19:42:11 -070069 regval |= (0x3 << 10);
70 set_l2_indirect_reg(drv->scalable[sc_id].l2cpmr_iaddr, regval);
71 } else {
72 struct acg_action action = { .set = true, .enable = false };
73 smp_call_function_single(sc_id, cpu_action, &action, 1);
74 }
75}
76
77/* Enable auto clock-gating for a scalable. */
78static void enable_acg(int sc_id)
79{
80 u32 regval;
81
82 if (sc_id == L2) {
83 regval = get_l2_indirect_reg(drv->scalable[sc_id].l2cpmr_iaddr);
84 regval &= ~(0x3 << 10);
Matt Wagantallaaef70a2012-10-15 19:35:19 -070085 regval |= l2_acg_en_val;
Matt Wagantall488bef32012-07-13 19:42:11 -070086 set_l2_indirect_reg(drv->scalable[sc_id].l2cpmr_iaddr, regval);
87 } else {
88 struct acg_action action = { .set = true, .enable = true };
89 smp_call_function_single(sc_id, cpu_action, &action, 1);
90 }
91}
92
93/* Check if auto clock-gating for a scalable. */
94static bool acg_is_enabled(int sc_id)
95{
96 u32 regval;
97
98 if (sc_id == L2) {
99 regval = get_l2_indirect_reg(drv->scalable[sc_id].l2cpmr_iaddr);
100 return ((regval >> 10) & 0x3) != 0x3;
101 } else {
102 struct acg_action action = { .set = false };
103 smp_call_function_single(sc_id, cpu_action, &action, 1);
104 return action.enable;
105 }
106}
107
108/* Enable/Disable auto clock gating. */
109static int acg_set(void *data, u64 val)
110{
111 int ret = 0;
112 int sc_id = (int)data;
113
114 mutex_lock(&debug_lock);
115 get_online_cpus();
116 if (!sc_dir[sc_id]) {
117 ret = -ENODEV;
118 goto out;
119 }
120
121 if (val == 0 && acg_is_enabled(sc_id))
122 disable_acg(sc_id);
123 else if (val == 1)
124 enable_acg(sc_id);
125out:
126 put_online_cpus();
127 mutex_unlock(&debug_lock);
128
129 return ret;
130}
131
132/* Get auto clock-gating state. */
133static int acg_get(void *data, u64 *val)
134{
135 int ret = 0;
136 int sc_id = (int)data;
137
138 mutex_lock(&debug_lock);
139 get_online_cpus();
140 if (!sc_dir[sc_id]) {
141 ret = -ENODEV;
142 goto out;
143 }
144
145 *val = acg_is_enabled(sc_id);
146out:
147 put_online_cpus();
148 mutex_unlock(&debug_lock);
149
150 return ret;
151}
152DEFINE_SIMPLE_ATTRIBUTE(acgd_fops, acg_get, acg_set, "%lld\n");
153
154/* Get the rate */
155static int rate_get(void *data, u64 *val)
156{
157 int sc_id = (int)data;
158 *val = drv->scalable[sc_id].cur_speed->khz;
159 return 0;
160}
161DEFINE_SIMPLE_ATTRIBUTE(rate_fops, rate_get, NULL, "%lld\n");
162
163/* Get the HFPLL's L-value. */
164static int hfpll_l_get(void *data, u64 *val)
165{
166 int sc_id = (int)data;
167 *val = drv->scalable[sc_id].cur_speed->pll_l_val;
168 return 0;
169}
170DEFINE_SIMPLE_ATTRIBUTE(hfpll_l_fops, hfpll_l_get, NULL, "%lld\n");
171
172/* Get the L2 rate vote. */
173static int l2_vote_get(void *data, u64 *val)
174{
175 int level, sc_id = (int)data;
176
177 level = drv->scalable[sc_id].l2_vote;
178 *val = drv->l2_freq_tbl[level].speed.khz;
179
180 return 0;
181}
182DEFINE_SIMPLE_ATTRIBUTE(l2_vote_fops, l2_vote_get, NULL, "%lld\n");
183
184/* Get the bandwidth vote. */
185static int bw_vote_get(void *data, u64 *val)
186{
187 struct l2_level *l;
188
189 l = container_of(drv->scalable[L2].cur_speed,
190 struct l2_level, speed);
191 *val = drv->bus_scale->usecase[l->bw_level].vectors->ib;
192
193 return 0;
194}
195DEFINE_SIMPLE_ATTRIBUTE(bw_vote_fops, bw_vote_get, NULL, "%lld\n");
196
197/* Get the name of the currently-selected clock source. */
198static int src_name_show(struct seq_file *m, void *unused)
199{
200 const char *const src_names[NUM_SRC_ID] = {
201 [PLL_0] = "PLL0",
202 [HFPLL] = "HFPLL",
203 [PLL_8] = "PLL8",
204 };
205 int src, sc_id = (int)m->private;
206
207 src = drv->scalable[sc_id].cur_speed->src;
208 if (src > ARRAY_SIZE(src_names))
209 return -EINVAL;
210
211 seq_printf(m, "%s\n", src_names[src]);
212
213 return 0;
214}
215
216static int src_name_open(struct inode *inode, struct file *file)
217{
218 return single_open(file, src_name_show, inode->i_private);
219}
220
221static const struct file_operations src_name_fops = {
222 .open = src_name_open,
223 .read = seq_read,
224 .llseek = seq_lseek,
225 .release = seq_release,
226};
227
228/* Get speed_bin ID */
229static int speed_bin_get(void *data, u64 *val)
230{
231 *val = drv->speed_bin;
232 return 0;
233}
234DEFINE_SIMPLE_ATTRIBUTE(speed_bin_fops, speed_bin_get, NULL, "%lld\n");
235
236/* Get pvs_bin ID */
237static int pvs_bin_get(void *data, u64 *val)
238{
239 *val = drv->pvs_bin;
240 return 0;
241}
242DEFINE_SIMPLE_ATTRIBUTE(pvs_bin_fops, pvs_bin_get, NULL, "%lld\n");
243
244/* Get boost_uv */
245static int boost_get(void *data, u64 *val)
246{
247 *val = drv->boost_uv;
248 return 0;
249}
250DEFINE_SIMPLE_ATTRIBUTE(boost_fops, boost_get, NULL, "%lld\n");
251
Vikram Mulukutlae81c14d2013-03-15 17:21:23 -0700252static int acpu_table_show(struct seq_file *m, void *unused)
253{
254 const struct acpu_level *level;
255
256 seq_printf(m, "CPU_KHz PLL_L_Val L2_KHz VDD_Dig VDD_Mem ");
257 seq_printf(m, "BW_Mbps VDD_Core UA_Core AVS\n");
258
259 for (level = drv->acpu_freq_tbl; level->speed.khz != 0; level++) {
260
261 const struct l2_level *l2 =
262 &drv->l2_freq_tbl[level->l2_level];
263 u32 bw = drv->bus_scale->usecase[l2->bw_level].vectors[0].ib;
264
265 if (!level->use_for_scaling)
266 continue;
267
268 /* CPU speed information */
269 seq_printf(m, "%7lu %9u ",
270 level->speed.khz,
271 level->speed.pll_l_val);
272
273 /* L2 level information */
274 seq_printf(m, "%7lu %7d %7d %7u ",
275 l2->speed.khz,
276 l2->vdd_dig,
277 l2->vdd_mem,
278 bw / 1000000);
279
280 /* Core voltage information */
281 seq_printf(m, "%8d %7d %3d\n",
282 level->vdd_core,
283 level->ua_core,
284 level->avsdscr_setting);
285 }
286
287 return 0;
288}
289
290static int acpu_table_open(struct inode *inode, struct file *file)
291{
292 return single_open(file, acpu_table_show, inode->i_private);
293}
294
295static const struct file_operations acpu_table_fops = {
296 .open = acpu_table_open,
297 .read = seq_read,
298 .llseek = seq_lseek,
299 .release = seq_release,
300};
301
Matt Wagantall488bef32012-07-13 19:42:11 -0700302static void __cpuinit add_scalable_dir(int sc_id)
303{
304 char sc_name[8];
305
306 if (sc_id == L2)
307 snprintf(sc_name, sizeof(sc_name), "l2");
308 else
309 snprintf(sc_name, sizeof(sc_name), "cpu%d", sc_id);
310
311 sc_dir[sc_id] = debugfs_create_dir(sc_name, base_dir);
312 if (!sc_dir[sc_id])
313 return;
314
315 debugfs_create_file("auto_gating", S_IRUGO | S_IWUSR,
316 sc_dir[sc_id], (void *)sc_id, &acgd_fops);
317
318 debugfs_create_file("rate", S_IRUGO,
319 sc_dir[sc_id], (void *)sc_id, &rate_fops);
320
321 debugfs_create_file("hfpll_l", S_IRUGO,
322 sc_dir[sc_id], (void *)sc_id, &hfpll_l_fops);
323
324 debugfs_create_file("src", S_IRUGO,
325 sc_dir[sc_id], (void *)sc_id, &src_name_fops);
326
327 if (sc_id == L2)
328 debugfs_create_file("bw_ib_vote", S_IRUGO,
329 sc_dir[sc_id], (void *)sc_id, &bw_vote_fops);
330 else
331 debugfs_create_file("l2_vote", S_IRUGO,
332 sc_dir[sc_id], (void *)sc_id, &l2_vote_fops);
333}
334
335static void __cpuinit remove_scalable_dir(int sc_id)
336{
337 debugfs_remove_recursive(sc_dir[sc_id]);
338 sc_dir[sc_id] = NULL;
339}
340
341static int __cpuinit debug_cpu_callback(struct notifier_block *nfb,
342 unsigned long action, void *hcpu)
343{
344 int cpu = (int)hcpu;
345
346 switch (action & ~CPU_TASKS_FROZEN) {
347 case CPU_DOWN_FAILED:
348 case CPU_UP_PREPARE:
349 add_scalable_dir(cpu);
350 break;
351 case CPU_UP_CANCELED:
352 case CPU_DOWN_PREPARE:
353 remove_scalable_dir(cpu);
354 break;
355 default:
356 break;
357 }
358
359 return NOTIFY_OK;
360}
361
362static struct notifier_block __cpuinitdata debug_cpu_notifier = {
363 .notifier_call = debug_cpu_callback,
364};
365
366void __init acpuclk_krait_debug_init(struct drv_data *drv_data)
367{
368 int cpu;
369 drv = drv_data;
370
371 base_dir = debugfs_create_dir("acpuclk", NULL);
372 if (!base_dir)
373 return;
374
375 debugfs_create_file("speed_bin", S_IRUGO, base_dir, NULL,
376 &speed_bin_fops);
377 debugfs_create_file("pvs_bin", S_IRUGO, base_dir, NULL, &pvs_bin_fops);
378 debugfs_create_file("boost_uv", S_IRUGO, base_dir, NULL, &boost_fops);
Vikram Mulukutlae81c14d2013-03-15 17:21:23 -0700379 debugfs_create_file("acpu_table", S_IRUGO, base_dir, NULL,
380 &acpu_table_fops);
Matt Wagantall488bef32012-07-13 19:42:11 -0700381
382 for_each_online_cpu(cpu)
383 add_scalable_dir(cpu);
384 add_scalable_dir(L2);
385
386 register_hotcpu_notifier(&debug_cpu_notifier);
387}