blob: 53c6b89a80a49fc6ccb7ee604bb9b9e76c39bf93 [file] [log] [blame]
Rohit Gupta5e4358c2014-07-18 16:16:02 -07001/*
Rohit Gupta870b1802016-04-13 16:55:04 -07002 * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
Rohit Gupta5e4358c2014-07-18 16:16:02 -07003 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 and
6 * only version 2 as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 */
13
14#define pr_fmt(fmt) "mem_lat: " fmt
15
16#include <linux/kernel.h>
17#include <linux/sizes.h>
18#include <linux/module.h>
19#include <linux/init.h>
20#include <linux/io.h>
21#include <linux/delay.h>
22#include <linux/ktime.h>
23#include <linux/time.h>
24#include <linux/err.h>
25#include <linux/errno.h>
26#include <linux/mutex.h>
27#include <linux/interrupt.h>
28#include <linux/platform_device.h>
29#include <linux/of.h>
30#include <linux/devfreq.h>
31#include "governor.h"
32#include "governor_memlat.h"
33
34#include <trace/events/power.h>
35
36struct memlat_node {
37 unsigned int ratio_ceil;
Rohit Gupta5e4358c2014-07-18 16:16:02 -070038 bool mon_started;
39 struct list_head list;
40 void *orig_data;
41 struct memlat_hwmon *hw;
42 struct devfreq_governor *gov;
43 struct attribute_group *attr_grp;
44};
45
46static LIST_HEAD(memlat_list);
47static DEFINE_MUTEX(list_lock);
48
49static int use_cnt;
50static DEFINE_MUTEX(state_lock);
51
52#define show_attr(name) \
53static ssize_t show_##name(struct device *dev, \
54 struct device_attribute *attr, char *buf) \
55{ \
56 struct devfreq *df = to_devfreq(dev); \
57 struct memlat_node *hw = df->data; \
58 return snprintf(buf, PAGE_SIZE, "%u\n", hw->name); \
59}
60
61#define store_attr(name, _min, _max) \
62static ssize_t store_##name(struct device *dev, \
63 struct device_attribute *attr, const char *buf, \
64 size_t count) \
65{ \
66 struct devfreq *df = to_devfreq(dev); \
67 struct memlat_node *hw = df->data; \
68 int ret; \
69 unsigned int val; \
70 ret = kstrtouint(buf, 10, &val); \
71 if (ret) \
72 return ret; \
73 val = max(val, _min); \
74 val = min(val, _max); \
75 hw->name = val; \
76 return count; \
77}
78
79#define gov_attr(__attr, min, max) \
80show_attr(__attr) \
81store_attr(__attr, min, max) \
82static DEVICE_ATTR(__attr, 0644, show_##__attr, store_##__attr)
83
Rohit Gupta870b1802016-04-13 16:55:04 -070084static unsigned long core_to_dev_freq(struct memlat_node *node,
85 unsigned long coref)
Rohit Gupta5e4358c2014-07-18 16:16:02 -070086{
Rohit Gupta5e4358c2014-07-18 16:16:02 -070087 struct memlat_hwmon *hw = node->hw;
Rohit Gupta870b1802016-04-13 16:55:04 -070088 struct core_dev_map *map = hw->freq_map;
89 unsigned long freq = 0;
Rohit Gupta5e4358c2014-07-18 16:16:02 -070090
Rohit Gupta870b1802016-04-13 16:55:04 -070091 if (!map)
92 goto out;
Rohit Gupta5e4358c2014-07-18 16:16:02 -070093
Rohit Gupta870b1802016-04-13 16:55:04 -070094 while (map->core_mhz && map->core_mhz < coref)
95 map++;
96 if (!map->core_mhz)
97 map--;
98 freq = map->target_freq;
Rohit Gupta5e4358c2014-07-18 16:16:02 -070099
Rohit Gupta870b1802016-04-13 16:55:04 -0700100out:
101 pr_debug("freq: %lu -> dev: %lu\n", coref, freq);
102 return freq;
Rohit Gupta5e4358c2014-07-18 16:16:02 -0700103}
104
105static struct memlat_node *find_memlat_node(struct devfreq *df)
106{
107 struct memlat_node *node, *found = NULL;
108
109 mutex_lock(&list_lock);
110 list_for_each_entry(node, &memlat_list, list)
111 if (node->hw->dev == df->dev.parent ||
112 node->hw->of_node == df->dev.parent->of_node) {
113 found = node;
114 break;
115 }
116 mutex_unlock(&list_lock);
117
118 return found;
119}
120
121static int start_monitor(struct devfreq *df)
122{
123 struct memlat_node *node = df->data;
124 struct memlat_hwmon *hw = node->hw;
125 struct device *dev = df->dev.parent;
126 int ret;
127
128 ret = hw->start_hwmon(hw);
129
130 if (ret) {
131 dev_err(dev, "Unable to start HW monitor! (%d)\n", ret);
132 return ret;
133 }
134
135 devfreq_monitor_start(df);
136
137 node->mon_started = true;
138
139 return 0;
140}
141
142static void stop_monitor(struct devfreq *df)
143{
144 struct memlat_node *node = df->data;
145 struct memlat_hwmon *hw = node->hw;
146
147 node->mon_started = false;
148
149 devfreq_monitor_stop(df);
150 hw->stop_hwmon(hw);
151}
152
153static int gov_start(struct devfreq *df)
154{
155 int ret = 0;
156 struct device *dev = df->dev.parent;
157 struct memlat_node *node;
158 struct memlat_hwmon *hw;
159
160 node = find_memlat_node(df);
161 if (!node) {
162 dev_err(dev, "Unable to find HW monitor!\n");
163 return -ENODEV;
164 }
165 hw = node->hw;
166
167 hw->df = df;
168 node->orig_data = df->data;
169 df->data = node;
170
171 if (start_monitor(df))
172 goto err_start;
173
174 ret = sysfs_create_group(&df->dev.kobj, node->attr_grp);
175 if (ret)
176 goto err_sysfs;
177
178 return 0;
179
180err_sysfs:
181 stop_monitor(df);
182err_start:
183 df->data = node->orig_data;
184 node->orig_data = NULL;
185 hw->df = NULL;
186 return ret;
187}
188
189static void gov_stop(struct devfreq *df)
190{
191 struct memlat_node *node = df->data;
192 struct memlat_hwmon *hw = node->hw;
193
194 sysfs_remove_group(&df->dev.kobj, node->attr_grp);
195 stop_monitor(df);
196 df->data = node->orig_data;
197 node->orig_data = NULL;
198 hw->df = NULL;
199}
200
201static int devfreq_memlat_get_freq(struct devfreq *df,
202 unsigned long *freq)
203{
Rohit Gupta870b1802016-04-13 16:55:04 -0700204 int i, lat_dev;
Rohit Gupta5e4358c2014-07-18 16:16:02 -0700205 struct memlat_node *node = df->data;
Rohit Gupta870b1802016-04-13 16:55:04 -0700206 struct memlat_hwmon *hw = node->hw;
207 unsigned long max_freq = 0;
208 unsigned int ratio;
Rohit Gupta5e4358c2014-07-18 16:16:02 -0700209
Rohit Gupta870b1802016-04-13 16:55:04 -0700210 hw->get_cnt(hw);
Rohit Gupta5e4358c2014-07-18 16:16:02 -0700211
Rohit Gupta870b1802016-04-13 16:55:04 -0700212 for (i = 0; i < hw->num_cores; i++) {
213 ratio = hw->core_stats[i].inst_count;
214
215 if (hw->core_stats[i].mem_count)
216 ratio /= hw->core_stats[i].mem_count;
217
218 trace_memlat_dev_meas(dev_name(df->dev.parent),
219 hw->core_stats[i].id,
220 hw->core_stats[i].inst_count,
221 hw->core_stats[i].mem_count,
222 hw->core_stats[i].freq, ratio);
223
224 if (ratio && ratio <= node->ratio_ceil
225 && hw->core_stats[i].freq > max_freq) {
226 lat_dev = i;
227 max_freq = hw->core_stats[i].freq;
228 }
229 }
230
231 if (max_freq) {
232 max_freq = core_to_dev_freq(node, max_freq);
233 trace_memlat_dev_update(dev_name(df->dev.parent),
234 hw->core_stats[lat_dev].id,
235 hw->core_stats[lat_dev].inst_count,
236 hw->core_stats[lat_dev].mem_count,
237 hw->core_stats[lat_dev].freq,
238 max_freq);
239 }
240
241 *freq = max_freq;
Rohit Gupta5e4358c2014-07-18 16:16:02 -0700242 return 0;
243}
244
Rohit Gupta870b1802016-04-13 16:55:04 -0700245gov_attr(ratio_ceil, 1U, 10000U);
Rohit Gupta5e4358c2014-07-18 16:16:02 -0700246
247static struct attribute *dev_attr[] = {
248 &dev_attr_ratio_ceil.attr,
Rohit Gupta5e4358c2014-07-18 16:16:02 -0700249 NULL,
250};
251
252static struct attribute_group dev_attr_group = {
253 .name = "mem_latency",
254 .attrs = dev_attr,
255};
256
257#define MIN_MS 10U
258#define MAX_MS 500U
259static int devfreq_memlat_ev_handler(struct devfreq *df,
260 unsigned int event, void *data)
261{
262 int ret;
263 unsigned int sample_ms;
264
265 switch (event) {
266 case DEVFREQ_GOV_START:
267 sample_ms = df->profile->polling_ms;
268 sample_ms = max(MIN_MS, sample_ms);
269 sample_ms = min(MAX_MS, sample_ms);
270 df->profile->polling_ms = sample_ms;
271
272 ret = gov_start(df);
273 if (ret)
274 return ret;
275
276 dev_dbg(df->dev.parent,
277 "Enabled Memory Latency governor\n");
278 break;
279
280 case DEVFREQ_GOV_STOP:
281 gov_stop(df);
282 dev_dbg(df->dev.parent,
283 "Disabled Memory Latency governor\n");
284 break;
285
286 case DEVFREQ_GOV_INTERVAL:
287 sample_ms = *(unsigned int *)data;
288 sample_ms = max(MIN_MS, sample_ms);
289 sample_ms = min(MAX_MS, sample_ms);
290 devfreq_interval_update(df, &sample_ms);
291 break;
292 }
293
294 return 0;
295}
296
297static struct devfreq_governor devfreq_gov_memlat = {
298 .name = "mem_latency",
299 .get_target_freq = devfreq_memlat_get_freq,
300 .event_handler = devfreq_memlat_ev_handler,
301};
302
Rohit Gupta870b1802016-04-13 16:55:04 -0700303#define NUM_COLS 2
304static struct core_dev_map *init_core_dev_map(struct device *dev,
305 char *prop_name)
306{
307 int len, nf, i, j;
308 u32 data;
309 struct core_dev_map *tbl;
310 int ret;
311
312 if (!of_find_property(dev->of_node, prop_name, &len))
313 return NULL;
314 len /= sizeof(data);
315
316 if (len % NUM_COLS || len == 0)
317 return NULL;
318 nf = len / NUM_COLS;
319
320 tbl = devm_kzalloc(dev, (nf + 1) * sizeof(struct core_dev_map),
321 GFP_KERNEL);
322 if (!tbl)
323 return NULL;
324
325 for (i = 0, j = 0; i < nf; i++, j += 2) {
326 ret = of_property_read_u32_index(dev->of_node, prop_name, j,
327 &data);
328 if (ret)
329 return NULL;
330 tbl[i].core_mhz = data / 1000;
331
332 ret = of_property_read_u32_index(dev->of_node, prop_name, j + 1,
333 &data);
334 if (ret)
335 return NULL;
336 tbl[i].target_freq = data;
337 pr_debug("Entry%d CPU:%u, Dev:%u\n", i, tbl[i].core_mhz,
338 tbl[i].target_freq);
339 }
340 tbl[i].core_mhz = 0;
341
342 return tbl;
343}
344
Rohit Gupta5e4358c2014-07-18 16:16:02 -0700345int register_memlat(struct device *dev, struct memlat_hwmon *hw)
346{
347 int ret = 0;
348 struct memlat_node *node;
349
350 if (!hw->dev && !hw->of_node)
351 return -EINVAL;
352
353 node = devm_kzalloc(dev, sizeof(*node), GFP_KERNEL);
354 if (!node)
355 return -ENOMEM;
356
357 node->gov = &devfreq_gov_memlat;
358 node->attr_grp = &dev_attr_group;
359
360 node->ratio_ceil = 10;
Rohit Gupta5e4358c2014-07-18 16:16:02 -0700361 node->hw = hw;
362
Rohit Gupta870b1802016-04-13 16:55:04 -0700363 hw->freq_map = init_core_dev_map(dev, "qcom,core-dev-table");
364 if (!hw->freq_map) {
365 dev_err(dev, "Couldn't find the core-dev freq table!\n");
366 return -EINVAL;
367 }
368
Rohit Gupta5e4358c2014-07-18 16:16:02 -0700369 mutex_lock(&list_lock);
370 list_add_tail(&node->list, &memlat_list);
371 mutex_unlock(&list_lock);
372
373 mutex_lock(&state_lock);
374 if (!use_cnt)
375 ret = devfreq_add_governor(&devfreq_gov_memlat);
376 if (!ret)
377 use_cnt++;
378 mutex_unlock(&state_lock);
379
380 if (!ret)
381 dev_info(dev, "Memory Latency governor registered.\n");
382 else
383 dev_err(dev, "Memory Latency governor registration failed!\n");
384
385 return ret;
386}
387
388MODULE_DESCRIPTION("HW monitor based dev DDR bandwidth voting driver");
389MODULE_LICENSE("GPL v2");