blob: e60a7add6599196b456cddfeafb0b54dcbc45781 [file] [log] [blame]
Mahesh Sivasubramanian6d0cfea2016-04-12 14:46:05 -06001/* Copyright (c) 2011-2017, The Linux Foundation. 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
Mahesh Sivasubramanian6d0cfea2016-04-12 14:46:05 -060014#include <linux/init.h>
15#include <linux/io.h>
16#include <linux/kernel.h>
17#include <linux/module.h>
18#include <linux/platform_device.h>
19#include <linux/slab.h>
20#include <linux/of.h>
21#include <linux/uaccess.h>
22#include <asm/arch_timer.h>
23
24#define RPM_STATS_NUM_REC 2
25#define MSM_ARCH_TIMER_FREQ 19200000
26
27#define GET_PDATA_OF_ATTR(attr) \
28 (container_of(attr, struct msm_rpmstats_kobj_attr, ka)->pd)
29
30struct msm_rpmstats_record {
31 char name[32];
32 u32 id;
33 u32 val;
34};
35
36struct msm_rpmstats_platform_data {
37 phys_addr_t phys_addr_base;
38 u32 phys_size;
39};
40
41struct msm_rpmstats_private_data {
42 void __iomem *reg_base;
43 u32 num_records;
44 u32 read_idx;
45 u32 len;
46 char buf[320];
47 struct msm_rpmstats_platform_data *platform_data;
48};
49
50struct msm_rpm_stats_data {
51 u32 stat_type;
52 u32 count;
53 u64 last_entered_at;
54 u64 last_exited_at;
55 u64 accumulated;
56};
57
58struct msm_rpmstats_kobj_attr {
59 struct kobj_attribute ka;
60 struct msm_rpmstats_platform_data *pd;
61};
62
63static inline u64 get_time_in_sec(u64 counter)
64{
65 do_div(counter, MSM_ARCH_TIMER_FREQ);
66
67 return counter;
68}
69
70static inline u64 get_time_in_msec(u64 counter)
71{
72 do_div(counter, MSM_ARCH_TIMER_FREQ);
73 counter *= MSEC_PER_SEC;
74
75 return counter;
76}
77
78static inline int msm_rpmstats_append_data_to_buf(char *buf,
79 struct msm_rpm_stats_data *data, int buflength)
80{
81 char stat_type[5];
82 u64 time_in_last_mode;
83 u64 time_since_last_mode;
84 u64 actual_last_sleep;
85
86 stat_type[4] = 0;
87 memcpy(stat_type, &data->stat_type, sizeof(u32));
88
89 time_in_last_mode = data->last_exited_at - data->last_entered_at;
90 time_in_last_mode = get_time_in_msec(time_in_last_mode);
91 time_since_last_mode = arch_counter_get_cntvct() - data->last_exited_at;
92 time_since_last_mode = get_time_in_sec(time_since_last_mode);
93 actual_last_sleep = get_time_in_msec(data->accumulated);
94
95 return snprintf(buf, buflength,
96 "RPM Mode:%s\n\t count:%d\ntime in last mode(msec):%llu\n"
97 "time since last mode(sec):%llu\nactual last sleep(msec):%llu\n\n",
98 stat_type, data->count, time_in_last_mode,
99 time_since_last_mode, actual_last_sleep);
100}
101
102static inline u32 msm_rpmstats_read_long_register(void __iomem *regbase,
103 int index, int offset)
104{
105 return readl_relaxed(regbase + offset +
106 index * sizeof(struct msm_rpm_stats_data));
107}
108
109static inline u64 msm_rpmstats_read_quad_register(void __iomem *regbase,
110 int index, int offset)
111{
112 u64 dst;
113
114 memcpy_fromio(&dst,
115 regbase + offset + index * sizeof(struct msm_rpm_stats_data),
116 8);
117 return dst;
118}
119
120static inline int msm_rpmstats_copy_stats(
121 struct msm_rpmstats_private_data *prvdata)
122{
123 void __iomem *reg;
124 struct msm_rpm_stats_data data;
125 int i, length;
126
127 reg = prvdata->reg_base;
128
129 for (i = 0, length = 0; i < prvdata->num_records; i++) {
130 data.stat_type = msm_rpmstats_read_long_register(reg, i,
131 offsetof(struct msm_rpm_stats_data,
132 stat_type));
133 data.count = msm_rpmstats_read_long_register(reg, i,
134 offsetof(struct msm_rpm_stats_data, count));
135 data.last_entered_at = msm_rpmstats_read_quad_register(reg,
136 i, offsetof(struct msm_rpm_stats_data,
137 last_entered_at));
138 data.last_exited_at = msm_rpmstats_read_quad_register(reg,
139 i, offsetof(struct msm_rpm_stats_data,
140 last_exited_at));
141 data.accumulated = msm_rpmstats_read_quad_register(reg,
142 i, offsetof(struct msm_rpm_stats_data,
143 accumulated));
144 length += msm_rpmstats_append_data_to_buf(prvdata->buf + length,
145 &data, sizeof(prvdata->buf) - length);
146 prvdata->read_idx++;
147 }
148
149 return length;
150}
151
Mahesh Sivasubramanian6d0cfea2016-04-12 14:46:05 -0600152static ssize_t rpmstats_show(struct kobject *kobj,
153 struct kobj_attribute *attr, char *buf)
154{
Lina Iyerc998f922017-05-05 12:07:43 -0600155 struct msm_rpmstats_private_data prvdata;
Mahesh Sivasubramanian6d0cfea2016-04-12 14:46:05 -0600156 struct msm_rpmstats_platform_data *pdata = NULL;
157
158 pdata = GET_PDATA_OF_ATTR(attr);
159
Lina Iyerc998f922017-05-05 12:07:43 -0600160 prvdata.reg_base = ioremap_nocache(pdata->phys_addr_base,
Mahesh Sivasubramanian6d0cfea2016-04-12 14:46:05 -0600161 pdata->phys_size);
Lina Iyerc998f922017-05-05 12:07:43 -0600162 if (!prvdata.reg_base) {
Mahesh Sivasubramanian6d0cfea2016-04-12 14:46:05 -0600163 pr_err("%s: ERROR could not ioremap start=%pa, len=%u\n",
164 __func__, &pdata->phys_addr_base,
165 pdata->phys_size);
166 return -EBUSY;
167 }
168
Lina Iyerc998f922017-05-05 12:07:43 -0600169 prvdata.read_idx = prvdata.len = 0;
170 prvdata.platform_data = pdata;
171 prvdata.num_records = RPM_STATS_NUM_REC;
Mahesh Sivasubramanian6d0cfea2016-04-12 14:46:05 -0600172
Lina Iyerc998f922017-05-05 12:07:43 -0600173 if (prvdata.read_idx < prvdata.num_records)
174 prvdata.len = msm_rpmstats_copy_stats(&prvdata);
Mahesh Sivasubramanian6d0cfea2016-04-12 14:46:05 -0600175
Archana Sathyakumar7c9ccf92017-10-10 11:13:41 -0600176 return snprintf(buf, prvdata.len, "%s", prvdata.buf);
Mahesh Sivasubramanian6d0cfea2016-04-12 14:46:05 -0600177}
178
179static int msm_rpmstats_create_sysfs(struct msm_rpmstats_platform_data *pd)
180{
Mahesh Sivasubramanian6d0cfea2016-04-12 14:46:05 -0600181 struct kobject *rpmstats_kobj = NULL;
182 struct msm_rpmstats_kobj_attr *rpms_ka = NULL;
183 int ret = 0;
184
Lina Iyer9eb02242017-05-09 16:25:25 -0600185 rpmstats_kobj = kobject_create_and_add("system_sleep", power_kobj);
Mahesh Sivasubramanian6d0cfea2016-04-12 14:46:05 -0600186 if (!rpmstats_kobj) {
187 pr_err("%s: Cannot create rpmstats kobject\n", __func__);
188 ret = -ENOMEM;
189 goto fail;
190 }
191
192 rpms_ka = kzalloc(sizeof(*rpms_ka), GFP_KERNEL);
193 if (!rpms_ka) {
194 kobject_put(rpmstats_kobj);
195 ret = -ENOMEM;
196 goto fail;
197 }
198
199 sysfs_attr_init(&rpms_ka->ka.attr);
200 rpms_ka->pd = pd;
201 rpms_ka->ka.attr.mode = 0444;
202 rpms_ka->ka.attr.name = "stats";
203 rpms_ka->ka.show = rpmstats_show;
204 rpms_ka->ka.store = NULL;
205
206 ret = sysfs_create_file(rpmstats_kobj, &rpms_ka->ka.attr);
207
208fail:
209 return ret;
210}
211
212static int msm_rpmstats_probe(struct platform_device *pdev)
213{
Mahesh Sivasubramanian6d0cfea2016-04-12 14:46:05 -0600214 struct msm_rpmstats_platform_data *pdata;
215 struct msm_rpmstats_platform_data *pd;
216 struct resource *res = NULL, *offset = NULL;
217 u32 offset_addr = 0;
218 void __iomem *phys_ptr = NULL;
219
220 pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
221 if (!pdata)
222 return -ENOMEM;
223
224 res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
225 "phys_addr_base");
226 if (!res)
227 return -EINVAL;
228
229 offset = platform_get_resource_byname(pdev, IORESOURCE_MEM,
230 "offset_addr");
231 if (offset) {
232 /* Remap the rpm-stats pointer */
233 phys_ptr = ioremap_nocache(offset->start, SZ_4);
234 if (!phys_ptr) {
235 pr_err("%s: Failed to ioremap address: %x\n",
236 __func__, offset_addr);
237 return -ENODEV;
238 }
239 offset_addr = readl_relaxed(phys_ptr);
240 iounmap(phys_ptr);
241 }
242
243 pdata->phys_addr_base = res->start + offset_addr;
244 pdata->phys_size = resource_size(res);
245
246 if (pdev->dev.platform_data)
247 pd = pdev->dev.platform_data;
248
Mahesh Sivasubramanian6d0cfea2016-04-12 14:46:05 -0600249 msm_rpmstats_create_sysfs(pdata);
250
Mahesh Sivasubramanian6d0cfea2016-04-12 14:46:05 -0600251 return 0;
252}
253
254static const struct of_device_id rpm_stats_table[] = {
255 { .compatible = "qcom,rpm-stats" },
256 { },
257};
258
259static struct platform_driver msm_rpmstats_driver = {
260 .probe = msm_rpmstats_probe,
Mahesh Sivasubramanian6d0cfea2016-04-12 14:46:05 -0600261 .driver = {
262 .name = "msm_rpm_stat",
263 .owner = THIS_MODULE,
264 .of_match_table = rpm_stats_table,
265 },
266};
267builtin_platform_driver(msm_rpmstats_driver);