blob: 3433356c1effc17dfbf5a17ded6756b452685f5c [file] [log] [blame]
Nicholas Flintham1e3d3112013-04-10 10:48:38 +01001/*
2 * Copyright (C) 2007 Google, Inc.
3 * Copyright (c) 2007-2012, Code Aurora Forum. All rights reserved.
4 *
5 * This software is licensed under the terms of the GNU General Public
6 * License version 2, as published by the Free Software Foundation, and
7 * may be copied, distributed, and modified under those terms.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 */
15
16#include <linux/kernel.h>
17#include <linux/module.h>
18#include <linux/ctype.h>
19#include <linux/debugfs.h>
20#include <linux/seq_file.h>
21#include <linux/clk.h>
22#include <linux/list.h>
23#include <linux/clkdev.h>
24
25#include "clock.h"
26
27static int clock_debug_rate_set(void *data, u64 val)
28{
29 struct clk *clock = data;
30 int ret;
31
32 if (clock->flags & CLKFLAG_MAX)
33 clk_set_max_rate(clock, val);
34 ret = clk_set_rate(clock, val);
35 if (ret)
36 pr_err("clk_set_rate failed (%d)\n", ret);
37
38 return ret;
39}
40
41static int clock_debug_rate_get(void *data, u64 *val)
42{
43 struct clk *clock = data;
44 *val = clk_get_rate(clock);
45 return 0;
46}
47
48DEFINE_SIMPLE_ATTRIBUTE(clock_rate_fops, clock_debug_rate_get,
49 clock_debug_rate_set, "%llu\n");
50
51static struct clk *measure;
52
53static int clock_debug_measure_get(void *data, u64 *val)
54{
55 struct clk *clock = data;
56 int ret, is_hw_gated;
57
58
59 if (clock->flags & CLKFLAG_HWCG)
60 is_hw_gated = clock->ops->in_hwcg_mode(clock);
61 else
62 is_hw_gated = 0;
63
64 ret = clk_set_parent(measure, clock);
65 if (!ret) {
66 if (is_hw_gated && clock->count)
67 clock->ops->disable_hwcg(clock);
68 *val = clk_get_rate(measure);
69
70 if (is_hw_gated && clock->count)
71 clock->ops->enable_hwcg(clock);
72 }
73
74 return ret;
75}
76
77DEFINE_SIMPLE_ATTRIBUTE(clock_measure_fops, clock_debug_measure_get,
78 NULL, "%lld\n");
79
80static int clock_debug_enable_set(void *data, u64 val)
81{
82 struct clk *clock = data;
83 int rc = 0;
84
85 if (val)
86 rc = clk_prepare_enable(clock);
87 else
88 clk_disable_unprepare(clock);
89
90 return rc;
91}
92
93static int clock_debug_enable_get(void *data, u64 *val)
94{
95 struct clk *clock = data;
96 int enabled;
97
98 if (clock->ops->is_enabled)
99 enabled = clock->ops->is_enabled(clock);
100 else
101 enabled = !!(clock->count);
102
103 *val = enabled;
104 return 0;
105}
106
107DEFINE_SIMPLE_ATTRIBUTE(clock_enable_fops, clock_debug_enable_get,
108 clock_debug_enable_set, "%lld\n");
109
110static int clock_debug_local_get(void *data, u64 *val)
111{
112 struct clk *clock = data;
113
114 if (!clock->ops->is_local)
115 *val = true;
116 else
117 *val = clock->ops->is_local(clock);
118
119 return 0;
120}
121
122DEFINE_SIMPLE_ATTRIBUTE(clock_local_fops, clock_debug_local_get,
123 NULL, "%llu\n");
124
125static int clock_debug_hwcg_get(void *data, u64 *val)
126{
127 struct clk *clock = data;
128 *val = !!(clock->flags & CLKFLAG_HWCG);
129 return 0;
130}
131
132DEFINE_SIMPLE_ATTRIBUTE(clock_hwcg_fops, clock_debug_hwcg_get,
133 NULL, "%llu\n");
134
135static struct dentry *debugfs_base;
136static u32 debug_suspend;
137static struct clk_lookup *msm_clocks;
138static size_t num_msm_clocks;
139
140struct clk *clock_debug_parent_get(void *data)
141{
142 struct clk *clock = data;
143
144 if (clock->ops->get_parent)
145 return clock->ops->get_parent(clock);
146
147 return NULL;
148}
149
150int htc_clock_dump(struct clk *clock, struct seq_file *m)
151{
152 int len = 0;
153 u64 value = 0;
154 struct clk *parent;
155 char nam_buf[20], en_buf[20], hz_buf[20], loc_buf[20], par_buf[20];
156
157 if (!clock)
158 return 0;
159
160 memset(nam_buf, ' ', sizeof(nam_buf));
161 nam_buf[19] = 0;
162 memset(en_buf, 0, sizeof(en_buf));
163 memset(hz_buf, 0, sizeof(hz_buf));
164 memset(loc_buf, 0, sizeof(loc_buf));
165 memset(par_buf, ' ', sizeof(par_buf));
166 par_buf[19] = 0;
167
168 len = strlen(clock->dbg_name);
169 if (len > 19)
170 len = 19;
171 memcpy(nam_buf, clock->dbg_name, len);
172
173 clock_debug_enable_get(clock, &value);
174 if (value)
175 sprintf(en_buf, "Y");
176 else
177 sprintf(en_buf, "N");
178
179 clock_debug_rate_get(clock, &value);
180 sprintf(hz_buf, "%llu", value);
181
182 clock_debug_local_get(clock, &value);
183 if (value)
184 sprintf(loc_buf, "Y");
185 else
186 sprintf(loc_buf, "N");
187
188 parent = clock_debug_parent_get(clock);
189 if (parent) {
190 len = strlen(parent->dbg_name);
191 if (len > 19)
192 len = 19;
193 memcpy(par_buf, parent->dbg_name, len);
194 } else
195 memcpy(par_buf, "NULL", 4);
196
197 if (m)
198 seq_printf(m, "%s: [EN]%s, [LOC]%s, [SRC]%s, [FREQ]%s\n", nam_buf, en_buf, loc_buf, par_buf, hz_buf);
199 else
200 pr_info("%s: [EN]%s, [LOC]%s, [SRC]%s, [FREQ]%s\n", nam_buf, en_buf, loc_buf, par_buf, hz_buf);
201
202 return 0;
203}
204
205static int list_clocks_show(struct seq_file *m, void *unused)
206{
207 int index;
208 char *title_msg = "------------ HTC Clock -------------\n";
209
210 seq_printf(m, title_msg);
211 for (index = 0; index < num_msm_clocks; index++)
212 htc_clock_dump(msm_clocks[index].clk, m);
213 return 0;
214}
215
216static int list_clocks_open(struct inode *inode, struct file *file)
217{
218 return single_open(file, list_clocks_show, inode->i_private);
219}
220
221static const struct file_operations list_clocks_fops = {
222 .open = list_clocks_open,
223 .read = seq_read,
224 .llseek = seq_lseek,
225 .release = seq_release,
226};
227
228static struct dentry *debugfs_clock_base;
229
230int htc_clock_status_debug_init(void)
231{
232 debugfs_clock_base = debugfs_create_dir("htc_clock", NULL);
233 if (!debugfs_clock_base)
234 return -ENOMEM;
235
236 if (!debugfs_create_file("list_clocks", S_IRUGO, debugfs_clock_base,
237 &msm_clocks, &list_clocks_fops))
238 return -ENOMEM;
239
240 return 0;
241}
242
243int __init clock_debug_init(struct clock_init_data *data)
244{
245 debugfs_base = debugfs_create_dir("clk", NULL);
246 if (!debugfs_base)
247 return -ENOMEM;
248 if (!debugfs_create_u32("debug_suspend", S_IRUGO | S_IWUSR,
249 debugfs_base, &debug_suspend)) {
250 debugfs_remove_recursive(debugfs_base);
251 return -ENOMEM;
252 }
253 msm_clocks = data->table;
254 num_msm_clocks = data->size;
255
256 measure = clk_get_sys("debug", "measure");
257 if (IS_ERR(measure))
258 measure = NULL;
259
260 htc_clock_status_debug_init();
261 return 0;
262}
263
264
265static int clock_debug_print_clock(struct clk *c)
266{
267 char *start = "";
268
269 if (!c || !c->count)
270 return 0;
271
272 pr_info("\t");
273 do {
274 if (c->vdd_class)
275 pr_cont("%s%s [%ld, %lu]", start, c->dbg_name, c->rate,
276 c->vdd_class->cur_level);
277 else
278 pr_cont("%s%s [%ld]", start, c->dbg_name, c->rate);
279 start = " -> ";
280 } while ((c = clk_get_parent(c)));
281
282 pr_cont("\n");
283
284 return 1;
285}
286
287void clock_debug_print_enabled(void)
288{
289 unsigned i;
290 int cnt = 0;
291
292 if (likely(!debug_suspend))
293 return;
294
295 pr_info("Enabled clocks:\n");
296 for (i = 0; i < num_msm_clocks; i++)
297 cnt += clock_debug_print_clock(msm_clocks[i].clk);
298
299 if (cnt)
300 pr_info("Enabled clock count: %d\n", cnt);
301 else
302 pr_info("No clocks enabled.\n");
303
304}
305
306static int list_rates_show(struct seq_file *m, void *unused)
307{
308 struct clk *clock = m->private;
309 int rate, level, fmax = 0, i = 0;
310
311
312 if (!clock->vdd_class) {
313 fmax = INT_MAX;
314 } else {
315 for (level = 0; level < ARRAY_SIZE(clock->fmax); level++)
316 if (clock->fmax[level])
317 fmax = clock->fmax[level];
318 }
319
320 while ((rate = clock->ops->list_rate(clock, i++)) >= 0) {
321 if (rate <= fmax)
322 seq_printf(m, "%u\n", rate);
323 }
324
325 return 0;
326}
327
328static int list_rates_open(struct inode *inode, struct file *file)
329{
330 return single_open(file, list_rates_show, inode->i_private);
331}
332
333static const struct file_operations list_rates_fops = {
334 .open = list_rates_open,
335 .read = seq_read,
336 .llseek = seq_lseek,
337 .release = seq_release,
338};
339
340int __init clock_debug_add(struct clk *clock)
341{
342 char temp[50], *ptr;
343 struct dentry *clk_dir;
344
345 if (!debugfs_base)
346 return -ENOMEM;
347
348 strlcpy(temp, clock->dbg_name, ARRAY_SIZE(temp));
349 for (ptr = temp; *ptr; ptr++)
350 *ptr = tolower(*ptr);
351
352 clk_dir = debugfs_create_dir(temp, debugfs_base);
353 if (!clk_dir)
354 return -ENOMEM;
355
356 if (!debugfs_create_file("rate", S_IRUGO | S_IWUSR, clk_dir,
357 clock, &clock_rate_fops))
358 goto error;
359
360 if (!debugfs_create_file("enable", S_IRUGO | S_IWUSR, clk_dir,
361 clock, &clock_enable_fops))
362 goto error;
363
364 if (!debugfs_create_file("is_local", S_IRUGO, clk_dir, clock,
365 &clock_local_fops))
366 goto error;
367
368 if (!debugfs_create_file("has_hw_gating", S_IRUGO, clk_dir, clock,
369 &clock_hwcg_fops))
370 goto error;
371
372 if (measure &&
373 !clk_set_parent(measure, clock) &&
374 !debugfs_create_file("measure", S_IRUGO, clk_dir, clock,
375 &clock_measure_fops))
376 goto error;
377
378 if (clock->ops->list_rate)
379 if (!debugfs_create_file("list_rates",
380 S_IRUGO, clk_dir, clock, &list_rates_fops))
381 goto error;
382
383 return 0;
384error:
385 debugfs_remove_recursive(clk_dir);
386 return -ENOMEM;
387}