blob: 1a8ef1f533d0199a00d4234bb93ce80aaeb74ea1 [file] [log] [blame]
Rohit Gupta5e4358c2014-07-18 16:16:02 -07001/*
Saravana Kannan6f67be82017-09-07 22:22:49 -07002 * Copyright (c) 2015-2017, 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;
Saravana Kannana0ecf002017-09-07 16:11:16 -070039 bool already_zero;
Rohit Gupta5e4358c2014-07-18 16:16:02 -070040 struct list_head list;
41 void *orig_data;
42 struct memlat_hwmon *hw;
43 struct devfreq_governor *gov;
44 struct attribute_group *attr_grp;
45};
46
47static LIST_HEAD(memlat_list);
48static DEFINE_MUTEX(list_lock);
49
50static int use_cnt;
51static DEFINE_MUTEX(state_lock);
52
53#define show_attr(name) \
54static ssize_t show_##name(struct device *dev, \
55 struct device_attribute *attr, char *buf) \
56{ \
57 struct devfreq *df = to_devfreq(dev); \
58 struct memlat_node *hw = df->data; \
59 return snprintf(buf, PAGE_SIZE, "%u\n", hw->name); \
60}
61
62#define store_attr(name, _min, _max) \
63static ssize_t store_##name(struct device *dev, \
64 struct device_attribute *attr, const char *buf, \
65 size_t count) \
66{ \
67 struct devfreq *df = to_devfreq(dev); \
68 struct memlat_node *hw = df->data; \
69 int ret; \
70 unsigned int val; \
71 ret = kstrtouint(buf, 10, &val); \
72 if (ret) \
73 return ret; \
74 val = max(val, _min); \
75 val = min(val, _max); \
76 hw->name = val; \
77 return count; \
78}
79
80#define gov_attr(__attr, min, max) \
81show_attr(__attr) \
82store_attr(__attr, min, max) \
83static DEVICE_ATTR(__attr, 0644, show_##__attr, store_##__attr)
84
David Keitel724116e2016-09-13 15:41:52 -070085static ssize_t show_map(struct device *dev, struct device_attribute *attr,
86 char *buf)
87{
88 struct devfreq *df = to_devfreq(dev);
89 struct memlat_node *n = df->data;
90 struct core_dev_map *map = n->hw->freq_map;
91 unsigned int cnt = 0;
92
93 cnt += snprintf(buf, PAGE_SIZE, "Core freq (MHz)\tDevice BW\n");
94
95 while (map->core_mhz && cnt < PAGE_SIZE) {
96 cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "%15u\t%9u\n",
97 map->core_mhz, map->target_freq);
98 map++;
99 }
100 if (cnt < PAGE_SIZE)
101 cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, "\n");
102
103 return cnt;
104}
105
106static DEVICE_ATTR(freq_map, 0444, show_map, NULL);
107
Rohit Gupta870b1802016-04-13 16:55:04 -0700108static unsigned long core_to_dev_freq(struct memlat_node *node,
109 unsigned long coref)
Rohit Gupta5e4358c2014-07-18 16:16:02 -0700110{
Rohit Gupta5e4358c2014-07-18 16:16:02 -0700111 struct memlat_hwmon *hw = node->hw;
Rohit Gupta870b1802016-04-13 16:55:04 -0700112 struct core_dev_map *map = hw->freq_map;
113 unsigned long freq = 0;
Rohit Gupta5e4358c2014-07-18 16:16:02 -0700114
Rohit Gupta870b1802016-04-13 16:55:04 -0700115 if (!map)
116 goto out;
Rohit Gupta5e4358c2014-07-18 16:16:02 -0700117
Rohit Gupta870b1802016-04-13 16:55:04 -0700118 while (map->core_mhz && map->core_mhz < coref)
119 map++;
120 if (!map->core_mhz)
121 map--;
122 freq = map->target_freq;
Rohit Gupta5e4358c2014-07-18 16:16:02 -0700123
Rohit Gupta870b1802016-04-13 16:55:04 -0700124out:
125 pr_debug("freq: %lu -> dev: %lu\n", coref, freq);
126 return freq;
Rohit Gupta5e4358c2014-07-18 16:16:02 -0700127}
128
129static struct memlat_node *find_memlat_node(struct devfreq *df)
130{
131 struct memlat_node *node, *found = NULL;
132
133 mutex_lock(&list_lock);
134 list_for_each_entry(node, &memlat_list, list)
135 if (node->hw->dev == df->dev.parent ||
136 node->hw->of_node == df->dev.parent->of_node) {
137 found = node;
138 break;
139 }
140 mutex_unlock(&list_lock);
141
142 return found;
143}
144
145static int start_monitor(struct devfreq *df)
146{
147 struct memlat_node *node = df->data;
148 struct memlat_hwmon *hw = node->hw;
149 struct device *dev = df->dev.parent;
150 int ret;
151
152 ret = hw->start_hwmon(hw);
153
154 if (ret) {
155 dev_err(dev, "Unable to start HW monitor! (%d)\n", ret);
156 return ret;
157 }
158
159 devfreq_monitor_start(df);
160
161 node->mon_started = true;
162
163 return 0;
164}
165
166static void stop_monitor(struct devfreq *df)
167{
168 struct memlat_node *node = df->data;
169 struct memlat_hwmon *hw = node->hw;
170
171 node->mon_started = false;
172
173 devfreq_monitor_stop(df);
174 hw->stop_hwmon(hw);
175}
176
177static int gov_start(struct devfreq *df)
178{
179 int ret = 0;
180 struct device *dev = df->dev.parent;
181 struct memlat_node *node;
182 struct memlat_hwmon *hw;
183
184 node = find_memlat_node(df);
185 if (!node) {
186 dev_err(dev, "Unable to find HW monitor!\n");
187 return -ENODEV;
188 }
189 hw = node->hw;
190
191 hw->df = df;
192 node->orig_data = df->data;
193 df->data = node;
194
195 if (start_monitor(df))
196 goto err_start;
197
198 ret = sysfs_create_group(&df->dev.kobj, node->attr_grp);
199 if (ret)
200 goto err_sysfs;
201
202 return 0;
203
204err_sysfs:
205 stop_monitor(df);
206err_start:
207 df->data = node->orig_data;
208 node->orig_data = NULL;
209 hw->df = NULL;
210 return ret;
211}
212
213static void gov_stop(struct devfreq *df)
214{
215 struct memlat_node *node = df->data;
216 struct memlat_hwmon *hw = node->hw;
217
218 sysfs_remove_group(&df->dev.kobj, node->attr_grp);
219 stop_monitor(df);
220 df->data = node->orig_data;
221 node->orig_data = NULL;
222 hw->df = NULL;
223}
224
225static int devfreq_memlat_get_freq(struct devfreq *df,
226 unsigned long *freq)
227{
Saravana Kannana0ecf002017-09-07 16:11:16 -0700228 int i, lat_dev = 0;
Rohit Gupta5e4358c2014-07-18 16:16:02 -0700229 struct memlat_node *node = df->data;
Rohit Gupta870b1802016-04-13 16:55:04 -0700230 struct memlat_hwmon *hw = node->hw;
231 unsigned long max_freq = 0;
232 unsigned int ratio;
Rohit Gupta5e4358c2014-07-18 16:16:02 -0700233
Rohit Gupta870b1802016-04-13 16:55:04 -0700234 hw->get_cnt(hw);
Rohit Gupta5e4358c2014-07-18 16:16:02 -0700235
Rohit Gupta870b1802016-04-13 16:55:04 -0700236 for (i = 0; i < hw->num_cores; i++) {
237 ratio = hw->core_stats[i].inst_count;
238
239 if (hw->core_stats[i].mem_count)
240 ratio /= hw->core_stats[i].mem_count;
241
Saravana Kannana0ecf002017-09-07 16:11:16 -0700242 if (!hw->core_stats[i].inst_count
243 || !hw->core_stats[i].freq)
244 continue;
245
Rohit Gupta870b1802016-04-13 16:55:04 -0700246 trace_memlat_dev_meas(dev_name(df->dev.parent),
247 hw->core_stats[i].id,
248 hw->core_stats[i].inst_count,
249 hw->core_stats[i].mem_count,
250 hw->core_stats[i].freq, ratio);
251
Saravana Kannan6f67be82017-09-07 22:22:49 -0700252 if (ratio <= node->ratio_ceil
Rohit Gupta870b1802016-04-13 16:55:04 -0700253 && hw->core_stats[i].freq > max_freq) {
254 lat_dev = i;
255 max_freq = hw->core_stats[i].freq;
256 }
257 }
258
Saravana Kannana0ecf002017-09-07 16:11:16 -0700259 if (max_freq)
Rohit Gupta870b1802016-04-13 16:55:04 -0700260 max_freq = core_to_dev_freq(node, max_freq);
Saravana Kannana0ecf002017-09-07 16:11:16 -0700261
262 if (max_freq || !node->already_zero) {
Rohit Gupta870b1802016-04-13 16:55:04 -0700263 trace_memlat_dev_update(dev_name(df->dev.parent),
264 hw->core_stats[lat_dev].id,
265 hw->core_stats[lat_dev].inst_count,
266 hw->core_stats[lat_dev].mem_count,
267 hw->core_stats[lat_dev].freq,
268 max_freq);
269 }
270
Saravana Kannana0ecf002017-09-07 16:11:16 -0700271 node->already_zero = !max_freq;
272
Rohit Gupta870b1802016-04-13 16:55:04 -0700273 *freq = max_freq;
Rohit Gupta5e4358c2014-07-18 16:16:02 -0700274 return 0;
275}
276
Rohit Gupta870b1802016-04-13 16:55:04 -0700277gov_attr(ratio_ceil, 1U, 10000U);
Rohit Gupta5e4358c2014-07-18 16:16:02 -0700278
279static struct attribute *dev_attr[] = {
280 &dev_attr_ratio_ceil.attr,
David Keitel724116e2016-09-13 15:41:52 -0700281 &dev_attr_freq_map.attr,
Rohit Gupta5e4358c2014-07-18 16:16:02 -0700282 NULL,
283};
284
285static struct attribute_group dev_attr_group = {
286 .name = "mem_latency",
287 .attrs = dev_attr,
288};
289
290#define MIN_MS 10U
291#define MAX_MS 500U
292static int devfreq_memlat_ev_handler(struct devfreq *df,
293 unsigned int event, void *data)
294{
295 int ret;
296 unsigned int sample_ms;
297
298 switch (event) {
299 case DEVFREQ_GOV_START:
300 sample_ms = df->profile->polling_ms;
301 sample_ms = max(MIN_MS, sample_ms);
302 sample_ms = min(MAX_MS, sample_ms);
303 df->profile->polling_ms = sample_ms;
304
305 ret = gov_start(df);
306 if (ret)
307 return ret;
308
309 dev_dbg(df->dev.parent,
310 "Enabled Memory Latency governor\n");
311 break;
312
313 case DEVFREQ_GOV_STOP:
314 gov_stop(df);
315 dev_dbg(df->dev.parent,
316 "Disabled Memory Latency governor\n");
317 break;
318
319 case DEVFREQ_GOV_INTERVAL:
320 sample_ms = *(unsigned int *)data;
321 sample_ms = max(MIN_MS, sample_ms);
322 sample_ms = min(MAX_MS, sample_ms);
323 devfreq_interval_update(df, &sample_ms);
324 break;
325 }
326
327 return 0;
328}
329
330static struct devfreq_governor devfreq_gov_memlat = {
331 .name = "mem_latency",
332 .get_target_freq = devfreq_memlat_get_freq,
333 .event_handler = devfreq_memlat_ev_handler,
334};
335
Rohit Gupta870b1802016-04-13 16:55:04 -0700336#define NUM_COLS 2
337static struct core_dev_map *init_core_dev_map(struct device *dev,
338 char *prop_name)
339{
340 int len, nf, i, j;
341 u32 data;
342 struct core_dev_map *tbl;
343 int ret;
344
345 if (!of_find_property(dev->of_node, prop_name, &len))
346 return NULL;
347 len /= sizeof(data);
348
349 if (len % NUM_COLS || len == 0)
350 return NULL;
351 nf = len / NUM_COLS;
352
353 tbl = devm_kzalloc(dev, (nf + 1) * sizeof(struct core_dev_map),
354 GFP_KERNEL);
355 if (!tbl)
356 return NULL;
357
358 for (i = 0, j = 0; i < nf; i++, j += 2) {
359 ret = of_property_read_u32_index(dev->of_node, prop_name, j,
360 &data);
361 if (ret)
362 return NULL;
363 tbl[i].core_mhz = data / 1000;
364
365 ret = of_property_read_u32_index(dev->of_node, prop_name, j + 1,
366 &data);
367 if (ret)
368 return NULL;
369 tbl[i].target_freq = data;
370 pr_debug("Entry%d CPU:%u, Dev:%u\n", i, tbl[i].core_mhz,
371 tbl[i].target_freq);
372 }
373 tbl[i].core_mhz = 0;
374
375 return tbl;
376}
377
Rohit Gupta5e4358c2014-07-18 16:16:02 -0700378int register_memlat(struct device *dev, struct memlat_hwmon *hw)
379{
380 int ret = 0;
381 struct memlat_node *node;
382
383 if (!hw->dev && !hw->of_node)
384 return -EINVAL;
385
386 node = devm_kzalloc(dev, sizeof(*node), GFP_KERNEL);
387 if (!node)
388 return -ENOMEM;
389
390 node->gov = &devfreq_gov_memlat;
391 node->attr_grp = &dev_attr_group;
392
393 node->ratio_ceil = 10;
Rohit Gupta5e4358c2014-07-18 16:16:02 -0700394 node->hw = hw;
395
Rohit Gupta870b1802016-04-13 16:55:04 -0700396 hw->freq_map = init_core_dev_map(dev, "qcom,core-dev-table");
397 if (!hw->freq_map) {
398 dev_err(dev, "Couldn't find the core-dev freq table!\n");
399 return -EINVAL;
400 }
401
Rohit Gupta5e4358c2014-07-18 16:16:02 -0700402 mutex_lock(&list_lock);
403 list_add_tail(&node->list, &memlat_list);
404 mutex_unlock(&list_lock);
405
406 mutex_lock(&state_lock);
407 if (!use_cnt)
408 ret = devfreq_add_governor(&devfreq_gov_memlat);
409 if (!ret)
410 use_cnt++;
411 mutex_unlock(&state_lock);
412
413 if (!ret)
414 dev_info(dev, "Memory Latency governor registered.\n");
415 else
416 dev_err(dev, "Memory Latency governor registration failed!\n");
417
418 return ret;
419}
420
421MODULE_DESCRIPTION("HW monitor based dev DDR bandwidth voting driver");
422MODULE_LICENSE("GPL v2");