blob: a831bd5a35f8abad370f15995f766d836f2edfcf [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
2 *
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#include <linux/debugfs.h>
14#include <linux/delay.h>
15#include <linux/errno.h>
16#include <linux/init.h>
17#include <linux/io.h>
18#include <linux/kernel.h>
19#include <linux/module.h>
20#include <linux/platform_device.h>
21#include <linux/sched.h>
22#include <linux/slab.h>
23#include <linux/types.h>
24#include <linux/mm.h>
25#include <asm/uaccess.h>
26
27#include <mach/msm_iomap.h>
28#include "rpm_stats.h"
29
30enum {
31 ID_COUNTER,
32 ID_ACCUM_TIME_SCLK,
33 ID_MAX,
34};
35
36static char *msm_rpmstats_id_labels[ID_MAX] = {
37 [ID_COUNTER] = "Count",
38 [ID_ACCUM_TIME_SCLK] = "Total time(uSec)",
39};
40
41#define SCLK_HZ 32768
42struct msm_rpmstats_record{
43 char name[32];
44 uint32_t id;
45 uint32_t val;
46};
47
48struct msm_rpmstats_private_data{
49 void __iomem *reg_base;
50 u32 num_records;
51 u32 read_idx;
52 u32 len;
53 char buf[128];
54 struct msm_rpmstats_platform_data *platform_data;
55};
56
57static inline unsigned long msm_rpmstats_read_register(void __iomem *regbase,
58 int index, int offset)
59{
60 return readl_relaxed(regbase + index * 12 + (offset + 1) * 4);
61}
62static void msm_rpmstats_strcpy(char *dest, char *src)
63{
64 union {
65 char ch[4];
66 unsigned long word;
67 } string;
68 int index = 0;
69
70 do {
71 int i;
72 string.word = readl_relaxed(src + 4 * index);
73 for (i = 0; i < 4; i++) {
74 *dest++ = string.ch[i];
75 if (!string.ch[i])
76 break;
77 }
78 index++;
79 } while (*(dest-1));
80
81}
82static int msm_rpmstats_copy_stats(struct msm_rpmstats_private_data *pdata)
83{
84
85 struct msm_rpmstats_record record;
86 unsigned long ptr;
87 unsigned long offset;
88 char *str;
89 uint64_t usec;
90
91 ptr = msm_rpmstats_read_register(pdata->reg_base, pdata->read_idx, 0);
92 offset = (ptr - (unsigned long)pdata->platform_data->phys_addr_base);
93
94 if (offset > pdata->platform_data->phys_size)
95 str = (char *)ioremap(ptr, SZ_256);
96 else
97 str = (char *) pdata->reg_base + offset;
98
99 msm_rpmstats_strcpy(record.name, str);
100
101 if (offset > pdata->platform_data->phys_size)
102 iounmap(str);
103
104 record.id = msm_rpmstats_read_register(pdata->reg_base,
105 pdata->read_idx, 1);
106 record.val = msm_rpmstats_read_register(pdata->reg_base,
107 pdata->read_idx, 2);
108
109 if (record.id == ID_ACCUM_TIME_SCLK) {
110 usec = record.val * USEC_PER_SEC;
111 do_div(usec, SCLK_HZ);
112 } else
113 usec = (unsigned long)record.val;
114
115 pdata->read_idx++;
116
117 return snprintf(pdata->buf, sizeof(pdata->buf),
118 "RPM Mode:%s\n\t%s:%llu\n",
119 record.name,
120 msm_rpmstats_id_labels[record.id],
121 usec);
122}
123
124static int msm_rpmstats_file_read(struct file *file, char __user *bufu,
125 size_t count, loff_t *ppos)
126{
127 struct msm_rpmstats_private_data *prvdata;
128 prvdata = file->private_data;
129
130 if (!prvdata)
131 return -EINVAL;
132
133 if (!bufu || count < 0)
134 return -EINVAL;
135
136 if (!prvdata->num_records)
137 prvdata->num_records = readl_relaxed(prvdata->reg_base);
138
139 if ((*ppos >= prvdata->len)
140 && (prvdata->read_idx < prvdata->num_records)) {
141 prvdata->len = msm_rpmstats_copy_stats(prvdata);
142 *ppos = 0;
143 }
144
145 return simple_read_from_buffer(bufu, count, ppos,
146 prvdata->buf, prvdata->len);
147}
148
149static int msm_rpmstats_file_open(struct inode *inode, struct file *file)
150{
151 struct msm_rpmstats_private_data *prvdata;
152 struct msm_rpmstats_platform_data *pdata;
153
154 pdata = inode->i_private;
155
156 file->private_data =
157 kmalloc(sizeof(struct msm_rpmstats_private_data), GFP_KERNEL);
158
159 if (!file->private_data)
160 return -ENOMEM;
161 prvdata = file->private_data;
162
163 prvdata->reg_base = ioremap(pdata->phys_addr_base, pdata->phys_size);
164 if (!prvdata->reg_base) {
165 kfree(file->private_data);
166 prvdata = NULL;
167 pr_err("%s: ERROR could not ioremap start=%p, len=%u\n",
168 __func__, (void *)pdata->phys_addr_base,
169 pdata->phys_size);
170 return -EBUSY;
171 }
172
173 prvdata->read_idx = prvdata->num_records = prvdata->len = 0;
174 prvdata->platform_data = pdata;
175 return 0;
176}
177
178static int msm_rpmstats_file_close(struct inode *inode, struct file *file)
179{
180 struct msm_rpmstats_private_data *private = file->private_data;
181
182 if (private->reg_base)
183 iounmap(private->reg_base);
184 kfree(file->private_data);
185
186 return 0;
187}
188
189static const struct file_operations msm_rpmstats_fops = {
190 .owner = THIS_MODULE,
191 .open = msm_rpmstats_file_open,
192 .read = msm_rpmstats_file_read,
193 .release = msm_rpmstats_file_close,
194 .llseek = no_llseek,
195};
196
197static int __devinit msm_rpmstats_probe(struct platform_device *pdev)
198{
199 struct dentry *dent;
200 struct msm_rpmstats_platform_data *pdata;
201
202 pdata = pdev->dev.platform_data;
203 if (!pdata)
204 return -EINVAL;
205 dent = debugfs_create_file("rpm_stats", S_IRUGO, NULL,
206 pdev->dev.platform_data, &msm_rpmstats_fops);
207
208 if (!dent) {
209 pr_err("%s: ERROR debugfs_create_file failed\n", __func__);
210 return -ENOMEM;
211 }
212 platform_set_drvdata(pdev, dent);
213 return 0;
214}
215
216static int __devexit msm_rpmstats_remove(struct platform_device *pdev)
217{
218 struct dentry *dent;
219
220 dent = platform_get_drvdata(pdev);
221 debugfs_remove(dent);
222 platform_set_drvdata(pdev, NULL);
223 return 0;
224}
225static struct platform_driver msm_rpmstats_driver = {
226 .probe = msm_rpmstats_probe,
227 .remove = __devexit_p(msm_rpmstats_remove),
228 .driver = {
229 .name = "msm_rpm_stat",
230 .owner = THIS_MODULE,
231 },
232};
233static int __init msm_rpmstats_init(void)
234{
235 return platform_driver_register(&msm_rpmstats_driver);
236}
237static void __exit msm_rpmstats_exit(void)
238{
239 platform_driver_unregister(&msm_rpmstats_driver);
240}
241module_init(msm_rpmstats_init);
242module_exit(msm_rpmstats_exit);
243
244MODULE_LICENSE("GPL v2");
245MODULE_DESCRIPTION("MSM RPM Statistics driver");
246MODULE_VERSION("1.0");
247MODULE_ALIAS("platform:msm_stat_log");