ARM: tegra12: set CPU rate to 2.2GHz for sku 0x87
[linux-3.10.git] / arch / arm / mach-tegra / clocks_stats.c
1 /*
2  * arch/arm/mach-tegra/clocks_stats.c
3  *
4  * Copyright (C) 2012, 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
16 #include <linux/kernel.h>
17 #include <linux/spinlock.h>
18 #include <linux/clk.h>
19 #include <linux/debugfs.h>
20 #include <linux/seq_file.h>
21 #include <linux/list.h>
22
23 #include "clock.h"
24
25 #define STATS_TABLE_MAX_SIZE 64
26
27 /*
28  * Generic stats tracking structures and functions
29  */
30 struct stats_entry {
31         int rate;
32         cputime64_t time_at_rate;
33 };
34
35 struct stats_table {
36         struct stats_entry *entry;
37         int last_rate;
38         cputime64_t last_updated;
39         spinlock_t spinlock;
40         unsigned int num_entries;
41 };
42
43 struct clock_data {
44         struct dentry *dentry;
45         struct list_head node;
46         struct stats_table table;
47         struct notifier_block rate_change_nb;
48 };
49
50 static LIST_HEAD(clock_stats);
51 static struct dentry *clock_debugfs_root;
52
53 /*
54  * Initialize a stats table to zeros
55  */
56 static void init_stats_table(struct stats_table *table)
57 {
58         table->last_rate = -1;
59         spin_lock_init(&(table->spinlock));
60         table->num_entries = 0;
61         table->last_updated = get_jiffies_64();
62 }
63
64 /*
65  * Populate table with possible rates
66  */
67 static int populate_rates(struct stats_table *table, struct clk *c)
68 {
69         unsigned long rate = 0, rounded_rate = 0;
70         unsigned int num_rates = 0;
71         int i = 0;
72
73         /* Calculate number of rates */
74         while (rate <= c->max_rate) {
75                 rounded_rate = c->ops->round_rate(c, rate);
76                 if (IS_ERR_VALUE(rounded_rate) || (rounded_rate <= rate))
77                         break;
78
79                 num_rates++;
80                 rate = rounded_rate + 2000;     /* 2kHz resolution */
81         }
82
83         /* Allocate space for a table of that size */
84         table->entry = kmalloc(num_rates * sizeof(struct stats_entry),
85                                         GFP_KERNEL);
86         if (!table->entry)
87                 return -ENOMEM;
88         rate = 0;
89         i = 0;
90
91         /* Populate table with possible rates */
92         while (rate <= c->max_rate) {
93                 rounded_rate = c->ops->round_rate(c, rate);
94                 if (IS_ERR_VALUE(rounded_rate) || (rounded_rate <= rate))
95                         break;
96
97                 table->entry[i].rate = rounded_rate;
98                 table->entry[i].time_at_rate = 0;
99                 i++;
100                 rate = rounded_rate + 2000;     /* 2kHz resolution */
101         }
102
103         table->num_entries = num_rates;
104
105         return 0;
106 }
107
108 /*
109  * Function is called whenever a rate changes. The time spent
110  * in the 'old rate' is finalized and the new rate is tracked.
111  * Entries are tracked in increasing order of rate
112  */
113 static void update_stats_table(struct stats_table *table, int new_rate)
114 {
115         int i = 0;
116         unsigned long flags;
117         u64 cur_jiffies = get_jiffies_64();
118
119         spin_lock_irqsave(&table->spinlock, flags);
120
121         if (new_rate == -1)
122                 new_rate = table->last_rate;
123
124         /* update time spent on old clock */
125         for (i = 0; i < table->num_entries; i++) {
126                 if (table->entry[i].rate == table->last_rate) {
127                         table->entry[i].time_at_rate =
128                                 table->entry[i].time_at_rate + (cur_jiffies -
129                                               table->last_updated);
130                 }
131         }
132
133         table->last_updated = cur_jiffies;
134         table->last_rate = new_rate;
135
136         spin_unlock_irqrestore(&table->spinlock, flags);
137
138 }
139
140 /*
141  * Print stats table to seq_file
142  */
143 static void dump_stats_table(struct seq_file *s, struct stats_table *table)
144 {
145         int i = 0;
146         update_stats_table(table, -1);
147
148         seq_printf(s, "%-10s %-10s\n", "rate kHz", "time");
149         for (i = 0; i < table->num_entries; i++)        {
150                 seq_printf(s, "%-10lu %-10llu\n",
151                         (long unsigned int)(table->entry[i].rate/1000),
152                         cputime64_to_clock_t(table->entry[i].time_at_rate));
153         }
154 }
155
156 static int stats_show(struct seq_file *s, void *data)
157 {
158         struct clock_data *d = (struct clock_data *)(s->private);
159         dump_stats_table(s, &d->table);
160         return 0;
161 }
162
163 static int stats_open(struct inode *inode, struct file *file)
164 {
165         return single_open(file, stats_show, inode->i_private);
166 }
167
168 static const struct file_operations clock_stats_fops = {
169         .open           = stats_open,
170         .read           = seq_read,
171         .llseek         = seq_lseek,
172         .release        = single_release,
173 };
174
175 /*
176  * Clock rate change notification callback
177  */
178 static int rate_notify_cb(struct notifier_block *nb, unsigned long rate,
179                           void *v)
180 {
181         struct clock_data *c =
182                 container_of(nb, struct clock_data, rate_change_nb);
183         update_stats_table(&c->table, rate);
184         return NOTIFY_OK;
185 }
186
187 /*
188  * Call once for each clock to track
189  */
190 static int track_clock(char *clk_name)
191 {
192         int ret = 0;
193         struct clock_data *d;
194         struct clk *c = clk_get(NULL, clk_name);
195         if (IS_ERR(c))
196                 return PTR_ERR(c);
197
198         d = kmalloc(sizeof(struct clock_data), GFP_KERNEL);
199         if (d == NULL)
200                 goto err_clk;
201
202         d->rate_change_nb.notifier_call = rate_notify_cb;
203
204         if (!clock_debugfs_root)
205                 goto err_clk;
206
207         d->dentry = debugfs_create_file(
208                 clk_name, S_IRUGO, clock_debugfs_root, d, &clock_stats_fops);
209         if (!d->dentry)
210                 goto err_clk;
211
212         init_stats_table(&d->table);
213         ret = populate_rates(&d->table, c);
214         if (ret)
215                 goto err_out;
216
217         ret = tegra_register_clk_rate_notifier(c, &d->rate_change_nb);
218         if (ret)
219                 goto err_out;
220
221         list_add(&d->node, &clock_stats);
222
223         clk_put(c);
224         return 0;
225
226 err_out:
227         kfree(d->table.entry);
228         debugfs_remove(d->dentry);
229 err_clk:
230         kfree(d);
231         clk_put(c);
232         return -ENOMEM;
233 }
234
235 static int __init tegra_clocks_debug_init(void)
236 {
237         int ret = 0;
238
239         clock_debugfs_root = debugfs_create_dir("clock_stats", NULL);
240         if (!clock_debugfs_root)
241                 return -ENOMEM;
242
243         /* Start tracking individual clocks */
244         ret = track_clock("sbus");
245         if (0 != ret)
246                 goto err_out;
247
248 #ifdef CONFIG_TEGRA_DUAL_CBUS
249         ret = track_clock("c2bus");
250         if (0 != ret)
251                 goto err_out;
252
253         ret = track_clock("c3bus");
254         if (0 != ret)
255                 goto err_out;
256 #else
257         ret = track_clock("cbus");
258         if (0 != ret)
259                 goto err_out;
260 #endif
261
262 /* FIXME: remove ifdef when track_clock gracefully handles "cannot get clock" */
263 #ifdef CONFIG_ARCH_TEGRA_12x_SOC
264         ret = track_clock("c4bus");
265         if (0 != ret)
266                 goto err_out;
267
268         ret = track_clock("gbus");
269         if (0 != ret)
270                 goto err_out;
271 #endif
272         return 0;
273
274
275 err_out:
276         pr_err("*** clock_stats: cannot get clock\n");
277         return ret;
278
279 }
280 late_initcall(tegra_clocks_debug_init);