blob: 14f77a3f21d03eb8979fa4dbbd773fd9cef45032 [file] [log] [blame]
Duy Truong790f06d2013-02-13 16:38:12 -08001/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
Girish Mahadevan898c56d2012-06-05 16:09:19 -06002 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 */
13
14#include <linux/debugfs.h>
15#include <linux/delay.h>
16#include <linux/errno.h>
17#include <linux/init.h>
18#include <linux/io.h>
19#include <linux/kernel.h>
20#include <linux/module.h>
21#include <linux/platform_device.h>
22#include <linux/sched.h>
23#include <linux/slab.h>
24#include <linux/types.h>
25#include <linux/mm.h>
26#include <linux/spinlock.h>
27#include <linux/sort.h>
28#include <asm/uaccess.h>
29#include <mach/msm_iomap.h>
30#include "timer.h"
31#include "rpm_rbcpr_stats.h"
32
33#define RBCPR_USER_BUF (2000)
34#define STR(a) (#a)
35#define GETFIELD(a) ((strnstr(STR(a), "->", 80) + 2))
36#define PRINTFIELD(buf, buf_size, pos, format, ...) \
37 ((pos < buf_size) ? snprintf((buf + pos), (buf_size - pos), format,\
38 ## __VA_ARGS__) : 0)
39
40enum {
41 RBCPR_CORNER_SVS = 0,
42 RBCPR_CORNER_NOMINAL,
43 RBCPR_CORNER_TURBO,
44 RBCPR_CORNERS_COUNT,
45 RBCPR_CORNER_INVALID = 0x7FFFFFFF,
46};
47
48struct msm_rpmrbcpr_recmnd {
49 uint32_t voltage;
50 uint32_t timestamp;
51};
52
53struct msm_rpmrbcpr_corners {
54 int efuse_adjustment;
55 struct msm_rpmrbcpr_recmnd *rpm_rcmnd;
56 uint32_t programmed_voltage;
57 uint32_t isr_counter;
58 uint32_t min_counter;
59 uint32_t max_counter;
60};
61
62struct msm_rpmrbcpr_stats {
63 uint32_t status_count;
64 uint32_t num_corners;
65 uint32_t num_latest_recommends;
66 struct msm_rpmrbcpr_corners *rbcpr_corners;
67 uint32_t current_corner;
68 uint32_t railway_voltage;
69 uint32_t enable;
70};
71
72struct msm_rpmrbcpr_stats_internal {
73 void __iomem *regbase;
74 uint32_t len;
75 char buf[RBCPR_USER_BUF];
76};
77
78static DEFINE_SPINLOCK(rpm_rbcpr_lock);
79static struct msm_rpmrbcpr_design_data rbcpr_design_data;
80static struct msm_rpmrbcpr_stats rbcpr_stats;
81static struct msm_rpmrbcpr_stats_internal pvtdata;
82
83static inline unsigned long msm_rpmrbcpr_read_data(void __iomem *regbase,
84 int offset)
85{
86 return readl_relaxed(regbase + (offset * 4));
87}
88
89static int msm_rpmrbcpr_cmp_func(const void *a, const void *b)
90{
91 struct msm_rpmrbcpr_recmnd *pa = (struct msm_rpmrbcpr_recmnd *)(a);
92 struct msm_rpmrbcpr_recmnd *pb = (struct msm_rpmrbcpr_recmnd *)(b);
93 return pa->timestamp - pb->timestamp;
94}
95
96static char *msm_rpmrbcpr_corner_string(uint32_t corner)
97{
98 switch (corner) {
99 case RBCPR_CORNER_SVS:
100 return STR(RBCPR_CORNER_SVS);
101 break;
102 case RBCPR_CORNER_NOMINAL:
103 return STR(RBCPR_CORNER_NOMINAL);
104 break;
105 case RBCPR_CORNER_TURBO:
106 return STR(RBCPR_CORNER_TURBO);
107 break;
108 case RBCPR_CORNERS_COUNT:
109 case RBCPR_CORNER_INVALID:
110 default:
111 return STR(RBCPR_CORNER_INVALID);
112 break;
113 }
114}
115
116static int msm_rpmrbcpr_print_buf(struct msm_rpmrbcpr_stats *pdata,
117 struct msm_rpmrbcpr_design_data *pdesdata,
118 char *buf)
119{
120 int pos = 0;
121 struct msm_rpmrbcpr_corners *corners;
122 struct msm_rpmrbcpr_recmnd *rcmnd;
123 int i, j;
124 int current_timestamp = msm_timer_get_sclk_ticks();
125
126 if (!pdata->enable) {
127 pos += PRINTFIELD(buf, RBCPR_USER_BUF, pos,
128 "RBCPR Stats not enabled at RPM");
129 return pos;
130 }
131
132 pos += PRINTFIELD(buf, RBCPR_USER_BUF, pos,
133 ":RBCPR Platform Data");
134 pos += PRINTFIELD(buf, RBCPR_USER_BUF, pos,
135 " (%s: %u)", GETFIELD(pdesdata->upside_steps),
136 pdesdata->upside_steps);
137 pos += PRINTFIELD(buf, RBCPR_USER_BUF, pos,
138 " (%s:%u)", GETFIELD(pdesdata->downside_steps),
139 pdesdata->downside_steps);
140 pos += PRINTFIELD(buf, RBCPR_USER_BUF, pos,
141 " (%s: %d)", GETFIELD(pdesdata->svs_voltage),
142 pdesdata->svs_voltage);
143 pos += PRINTFIELD(buf, RBCPR_USER_BUF, pos,
144 " (%s: %d)", GETFIELD(pdesdata->nominal_voltage),
145 pdesdata->nominal_voltage);
146 pos += PRINTFIELD(buf, RBCPR_USER_BUF, pos,
147 " (%s: %d)\n", GETFIELD(pdesdata->turbo_voltage),
148 pdesdata->turbo_voltage);
149
150 pos += PRINTFIELD(buf, RBCPR_USER_BUF, pos,
151 ":RBCPR Stats");
152 pos += PRINTFIELD(buf, RBCPR_USER_BUF, pos,
153 " (%s: %u)", GETFIELD(pdata->status_counter),
154 pdata->status_count);
155 pos += PRINTFIELD(buf, RBCPR_USER_BUF, pos,
156 " (%s: %s)", GETFIELD(pdata->current_corner),
157 msm_rpmrbcpr_corner_string(pdata->current_corner));
158 pos += PRINTFIELD(buf, RBCPR_USER_BUF, pos,
159 " (current_timestamp: 0x%x)",
160 current_timestamp);
161 pos += PRINTFIELD(buf, RBCPR_USER_BUF, pos,
162 " (%s: %u)\n", GETFIELD(pdata->railway_voltage),
163 pdata->railway_voltage);
164
165 for (i = 0; i < pdata->num_corners; i++) {
166 corners = &pdata->rbcpr_corners[i];
167 pos += PRINTFIELD(buf, RBCPR_USER_BUF, pos,
168 ":\tRBCPR Corner Data");
169 pos += PRINTFIELD(buf, RBCPR_USER_BUF, pos,
170 " (name: %s)", msm_rpmrbcpr_corner_string(i));
171 pos += PRINTFIELD(buf, RBCPR_USER_BUF, pos,
172 " (%s: %d)", GETFIELD(corners->efuse_adjustment),
173 corners->efuse_adjustment);
174 pos += PRINTFIELD(buf, RBCPR_USER_BUF, pos,
175 " (%s: %u)", GETFIELD(corners->programmed_voltage),
176 corners->programmed_voltage);
177 pos += PRINTFIELD(buf, RBCPR_USER_BUF, pos,
178 " (%s: %u)", GETFIELD(corners->isr_counter),
179 corners->isr_counter);
180 pos += PRINTFIELD(buf, RBCPR_USER_BUF, pos,
181 "(%s: %u)", GETFIELD(corners->min_counter),
182 corners->min_counter);
183 pos += PRINTFIELD(buf, RBCPR_USER_BUF, pos,
184 "(%s:%u)\n", GETFIELD(corners->max_counter),
185 corners->max_counter);
186 for (j = 0; j < pdata->num_latest_recommends; j++) {
187 pos += PRINTFIELD(buf, RBCPR_USER_BUF, pos,
188 ":\t\tVoltage History[%d]", j);
189 rcmnd = &corners->rpm_rcmnd[j];
190 pos += PRINTFIELD(buf, RBCPR_USER_BUF, pos,
191 " (%s: %u)", GETFIELD(rcmnd->voltage),
192 rcmnd->voltage);
193 pos += PRINTFIELD(buf, RBCPR_USER_BUF, pos,
194 " (%s: 0x%x)\n", GETFIELD(rcmnd->timestamp),
195 rcmnd->timestamp);
196 }
197 }
198 return pos;
199}
200
201
202static void msm_rpmrbcpr_copy_data(struct msm_rpmrbcpr_stats_internal *pdata,
203 struct msm_rpmrbcpr_stats *prbcpr_stats)
204{
205 struct msm_rpmrbcpr_corners *corners;
206 struct msm_rpmrbcpr_recmnd *rcmnd;
207 int i, j;
208 int offset = (offsetof(struct msm_rpmrbcpr_stats, rbcpr_corners) / 4);
209
210 if (!prbcpr_stats)
211 return;
212
213 for (i = 0; i < prbcpr_stats->num_corners; i++) {
214 corners = &prbcpr_stats->rbcpr_corners[i];
215 corners->efuse_adjustment = msm_rpmrbcpr_read_data(
216 pdata->regbase, offset++);
217 for (j = 0; j < prbcpr_stats->num_latest_recommends; j++) {
218 rcmnd = &corners->rpm_rcmnd[j];
219 rcmnd->voltage = msm_rpmrbcpr_read_data(
220 pdata->regbase, offset++);
221 rcmnd->timestamp = msm_rpmrbcpr_read_data(
222 pdata->regbase, offset++);
223 }
224 sort(&corners->rpm_rcmnd[0],
225 prbcpr_stats->num_latest_recommends,
226 sizeof(struct msm_rpmrbcpr_recmnd),
227 msm_rpmrbcpr_cmp_func, NULL);
228 corners->programmed_voltage = msm_rpmrbcpr_read_data(
229 pdata->regbase, offset++);
230 corners->isr_counter = msm_rpmrbcpr_read_data(pdata->regbase,
231 offset++);
232 corners->min_counter = msm_rpmrbcpr_read_data(pdata->regbase,
233 offset++);
234 corners->max_counter = msm_rpmrbcpr_read_data(pdata->regbase,
235 offset++);
236 }
237 prbcpr_stats->current_corner = msm_rpmrbcpr_read_data(pdata->regbase,
238 offset++);
239 prbcpr_stats->railway_voltage = msm_rpmrbcpr_read_data
240 (pdata->regbase, offset++);
241 prbcpr_stats->enable = msm_rpmrbcpr_read_data(pdata->regbase, offset++);
242}
243
244static int msm_rpmrbcpr_file_read(struct file *file, char __user *bufu,
245 size_t count, loff_t *ppos)
246{
247 struct msm_rpmrbcpr_stats_internal *pdata = file->private_data;
248 int ret;
249 int status_counter;
250
251 if (!pdata) {
252 pr_info("%s pdata is null", __func__);
253 return -EINVAL;
254 }
255
256 if (!bufu || count < 0) {
257 pr_info("%s count %d ", __func__, count);
258 return -EINVAL;
259 }
260
261 if (*ppos > pdata->len || !pdata->len) {
262 /* Read RPM stats */
263 status_counter = readl_relaxed(pdata->regbase +
264 offsetof(struct msm_rpmrbcpr_stats, status_count));
265 if (status_counter != rbcpr_stats.status_count) {
266 spin_lock(&rpm_rbcpr_lock);
267 msm_rpmrbcpr_copy_data(pdata, &rbcpr_stats);
268 rbcpr_stats.status_count = status_counter;
269 spin_unlock(&rpm_rbcpr_lock);
270 }
271 pdata->len = msm_rpmrbcpr_print_buf(&rbcpr_stats,
272 &rbcpr_design_data, pdata->buf);
273 *ppos = 0;
274 }
275 /* copy to user data */
276 ret = simple_read_from_buffer(bufu, count, ppos, pdata->buf,
277 pdata->len);
278 return ret;
279}
280
281static void msm_rpmrbcpr_free_mem(struct msm_rpmrbcpr_stats_internal *pvtdata,
282 struct msm_rpmrbcpr_stats *prbcpr_stats)
283{
284 int i;
285 if (pvtdata->regbase)
286 iounmap(pvtdata->regbase);
287
288
289 if (prbcpr_stats) {
290 for (i = 0; i < prbcpr_stats->num_corners; i++) {
291 kfree(prbcpr_stats->rbcpr_corners[i].rpm_rcmnd);
292 prbcpr_stats->rbcpr_corners[i].rpm_rcmnd = NULL;
293 }
294
295 kfree(prbcpr_stats->rbcpr_corners);
296 prbcpr_stats->rbcpr_corners = NULL;
297 }
298}
299
300static int msm_rpmrbcpr_allocate_mem(struct msm_rpmrbcpr_platform_data *pdata,
301 struct resource *res)
302{
303 int i;
304
305 pvtdata.regbase = ioremap(res->start, (res->end - res->start + 1));
306 memcpy(&rbcpr_design_data, &pdata->rbcpr_data,
307 sizeof(struct msm_rpmrbcpr_design_data));
308
309
310 rbcpr_stats.num_corners = readl_relaxed(pvtdata.regbase +
311 offsetof(struct msm_rpmrbcpr_stats, num_corners));
312 rbcpr_stats.num_latest_recommends = readl_relaxed(pvtdata.regbase +
313 offsetof(struct msm_rpmrbcpr_stats,
314 num_latest_recommends));
315
316 rbcpr_stats.rbcpr_corners = kzalloc(
317 sizeof(struct msm_rpmrbcpr_corners)
318 * rbcpr_stats.num_corners, GFP_KERNEL);
319
320 if (!rbcpr_stats.rbcpr_corners) {
321 msm_rpmrbcpr_free_mem(&pvtdata, &rbcpr_stats);
322 return -ENOMEM;
323 }
324
325 for (i = 0; i < rbcpr_stats.num_corners; i++) {
326 rbcpr_stats.rbcpr_corners[i].rpm_rcmnd =
327 kzalloc(sizeof(struct msm_rpmrbcpr_corners)
328 * rbcpr_stats.num_latest_recommends,
329 GFP_KERNEL);
330
331 if (!rbcpr_stats.rbcpr_corners[i].rpm_rcmnd) {
332 msm_rpmrbcpr_free_mem(&pvtdata, &rbcpr_stats);
333 return -ENOMEM;
334 }
335 }
336 return 0;
337}
338
339static int msm_rpmrbcpr_file_open(struct inode *inode, struct file *file)
340{
341 file->private_data = &pvtdata;
342 pvtdata.len = 0;
343
344 if (!pvtdata.regbase)
345 return -EBUSY;
346
347 return 0;
348}
349
350static int msm_rpmrbcpr_file_close(struct inode *inode, struct file *file)
351{
352 return 0;
353}
354
355static const struct file_operations msm_rpmrbcpr_fops = {
356 .owner = THIS_MODULE,
357 .open = msm_rpmrbcpr_file_open,
358 .read = msm_rpmrbcpr_file_read,
359 .release = msm_rpmrbcpr_file_close,
360 .llseek = no_llseek,
361};
362
363static int __devinit msm_rpmrbcpr_probe(struct platform_device *pdev)
364{
365 struct dentry *dent;
366 struct msm_rpmrbcpr_platform_data *pdata;
367 int ret = 0;
368
369 pdata = pdev->dev.platform_data;
370 if (!pdata)
371 return -EINVAL;
372 dent = debugfs_create_file("rpm_rbcpr", S_IRUGO, NULL,
373 pdev->dev.platform_data, &msm_rpmrbcpr_fops);
374
375 if (!dent) {
376 pr_err("%s: ERROR debugfs_create_file failed\n", __func__);
377 return -ENOMEM;
378 }
379 platform_set_drvdata(pdev, dent);
380 ret = msm_rpmrbcpr_allocate_mem(pdata, pdev->resource);
381 return ret;
382}
383
384static int __devexit msm_rpmrbcpr_remove(struct platform_device *pdev)
385{
386 struct dentry *dent;
387
388 msm_rpmrbcpr_free_mem(&pvtdata, &rbcpr_stats);
389 dent = platform_get_drvdata(pdev);
390 debugfs_remove(dent);
391 platform_set_drvdata(pdev, NULL);
392 return 0;
393}
394
395static struct platform_driver msm_rpmrbcpr_driver = {
396 .probe = msm_rpmrbcpr_probe,
397 .remove = __devexit_p(msm_rpmrbcpr_remove),
398 .driver = {
399 .name = "msm_rpm_rbcpr",
400 .owner = THIS_MODULE,
401 },
402};
403
404static int __init msm_rpmrbcpr_init(void)
405{
406 return platform_driver_register(&msm_rpmrbcpr_driver);
407}
408
409static void __exit msm_rpmrbcpr_exit(void)
410{
411 platform_driver_unregister(&msm_rpmrbcpr_driver);
412}
413
414module_init(msm_rpmrbcpr_init);
415module_exit(msm_rpmrbcpr_exit);