blob: 1dca479fd8ba9802ae4ff4c6394047ea182536d4 [file] [log] [blame]
Rohit Gupta5e4358c2014-07-18 16:16:02 -07001/*
Rohit Gupta3862a342017-03-07 11:47:52 -08002 * Copyright (c) 2014-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) "arm-memlat-mon: " fmt
15
16#include <linux/kernel.h>
17#include <linux/module.h>
18#include <linux/init.h>
19#include <linux/io.h>
20#include <linux/delay.h>
21#include <linux/err.h>
22#include <linux/errno.h>
23#include <linux/interrupt.h>
24#include <linux/platform_device.h>
25#include <linux/of.h>
26#include <linux/of_irq.h>
27#include <linux/slab.h>
28#include <linux/irq.h>
29#include <linux/cpu_pm.h>
30#include <linux/cpu.h>
31#include "governor.h"
32#include "governor_memlat.h"
33#include <linux/perf_event.h>
Jonathan Avilae71f95f2017-10-12 15:15:47 -070034#include <linux/of_device.h>
Rohit Gupta5e4358c2014-07-18 16:16:02 -070035
36enum ev_index {
37 INST_IDX,
Rohit Gupta3862a342017-03-07 11:47:52 -080038 CM_IDX,
Rohit Gupta5e4358c2014-07-18 16:16:02 -070039 CYC_IDX,
Saravana Kannan83f28462017-09-26 19:45:15 -070040 STALL_CYC_IDX,
Rohit Gupta5e4358c2014-07-18 16:16:02 -070041 NUM_EVENTS
42};
43#define INST_EV 0x08
44#define L2DM_EV 0x17
45#define CYC_EV 0x11
46
47struct event_data {
48 struct perf_event *pevent;
49 unsigned long prev_count;
50};
51
Rohit Guptabb3ac622017-05-03 17:36:54 -070052struct cpu_pmu_stats {
Rohit Gupta5e4358c2014-07-18 16:16:02 -070053 struct event_data events[NUM_EVENTS];
54 ktime_t prev_ts;
Rohit Gupta5e4358c2014-07-18 16:16:02 -070055};
Rohit Gupta5e4358c2014-07-18 16:16:02 -070056
57struct cpu_grp_info {
58 cpumask_t cpus;
Rohit Guptabb3ac622017-05-03 17:36:54 -070059 cpumask_t inited_cpus;
Saravana Kannan52978b92017-09-26 20:29:47 -070060 unsigned int event_ids[NUM_EVENTS];
Rohit Guptabb3ac622017-05-03 17:36:54 -070061 struct cpu_pmu_stats *cpustats;
Rohit Gupta5e4358c2014-07-18 16:16:02 -070062 struct memlat_hwmon hw;
63 struct notifier_block arm_memlat_cpu_notif;
Rohit Guptabb3ac622017-05-03 17:36:54 -070064 struct list_head mon_list;
Rohit Gupta5e4358c2014-07-18 16:16:02 -070065};
66
Jonathan Avilae71f95f2017-10-12 15:15:47 -070067struct memlat_mon_spec {
68 bool is_compute;
69};
70
Rohit Guptabb3ac622017-05-03 17:36:54 -070071#define to_cpustats(cpu_grp, cpu) \
72 (&cpu_grp->cpustats[cpu - cpumask_first(&cpu_grp->cpus)])
73#define to_devstats(cpu_grp, cpu) \
74 (&cpu_grp->hw.core_stats[cpu - cpumask_first(&cpu_grp->cpus)])
75#define to_cpu_grp(hwmon) container_of(hwmon, struct cpu_grp_info, hw)
76
77static LIST_HEAD(memlat_mon_list);
78static DEFINE_MUTEX(list_lock);
79
80static unsigned long compute_freq(struct cpu_pmu_stats *cpustats,
Rohit Gupta5e4358c2014-07-18 16:16:02 -070081 unsigned long cyc_cnt)
82{
83 ktime_t ts;
84 unsigned int diff;
85 unsigned long freq = 0;
86
87 ts = ktime_get();
Rohit Guptabb3ac622017-05-03 17:36:54 -070088 diff = ktime_to_us(ktime_sub(ts, cpustats->prev_ts));
Rohit Gupta5e4358c2014-07-18 16:16:02 -070089 if (!diff)
90 diff = 1;
Rohit Guptabb3ac622017-05-03 17:36:54 -070091 cpustats->prev_ts = ts;
Rohit Gupta5e4358c2014-07-18 16:16:02 -070092 freq = cyc_cnt;
93 do_div(freq, diff);
94
95 return freq;
96}
97
98#define MAX_COUNT_LIM 0xFFFFFFFFFFFFFFFF
99static inline unsigned long read_event(struct event_data *event)
100{
101 unsigned long ev_count;
102 u64 total, enabled, running;
103
Jonathan Avilae71f95f2017-10-12 15:15:47 -0700104 if (!event->pevent)
105 return 0;
106
Rohit Gupta5e4358c2014-07-18 16:16:02 -0700107 total = perf_event_read_value(event->pevent, &enabled, &running);
Saravana Kannan62e41b42017-09-26 19:16:22 -0700108 ev_count = total - event->prev_count;
Rohit Gupta5e4358c2014-07-18 16:16:02 -0700109 event->prev_count = total;
Rohit Gupta5e4358c2014-07-18 16:16:02 -0700110 return ev_count;
111}
112
113static void read_perf_counters(int cpu, struct cpu_grp_info *cpu_grp)
114{
Rohit Guptabb3ac622017-05-03 17:36:54 -0700115 struct cpu_pmu_stats *cpustats = to_cpustats(cpu_grp, cpu);
116 struct dev_stats *devstats = to_devstats(cpu_grp, cpu);
Saravana Kannan83f28462017-09-26 19:45:15 -0700117 unsigned long cyc_cnt, stall_cnt;
Rohit Gupta5e4358c2014-07-18 16:16:02 -0700118
Rohit Guptabb3ac622017-05-03 17:36:54 -0700119 devstats->inst_count = read_event(&cpustats->events[INST_IDX]);
120 devstats->mem_count = read_event(&cpustats->events[CM_IDX]);
121 cyc_cnt = read_event(&cpustats->events[CYC_IDX]);
122 devstats->freq = compute_freq(cpustats, cyc_cnt);
Saravana Kannan83f28462017-09-26 19:45:15 -0700123 if (cpustats->events[STALL_CYC_IDX].pevent) {
124 stall_cnt = read_event(&cpustats->events[STALL_CYC_IDX]);
125 stall_cnt = min(stall_cnt, cyc_cnt);
126 devstats->stall_pct = mult_frac(100, stall_cnt, cyc_cnt);
127 } else {
128 devstats->stall_pct = 100;
129 }
Rohit Gupta5e4358c2014-07-18 16:16:02 -0700130}
131
132static unsigned long get_cnt(struct memlat_hwmon *hw)
133{
134 int cpu;
Rohit Guptabb3ac622017-05-03 17:36:54 -0700135 struct cpu_grp_info *cpu_grp = to_cpu_grp(hw);
Rohit Gupta5e4358c2014-07-18 16:16:02 -0700136
Rohit Guptabb3ac622017-05-03 17:36:54 -0700137 for_each_cpu(cpu, &cpu_grp->inited_cpus)
Rohit Gupta5e4358c2014-07-18 16:16:02 -0700138 read_perf_counters(cpu, cpu_grp);
139
140 return 0;
141}
142
Rohit Guptabb3ac622017-05-03 17:36:54 -0700143static void delete_events(struct cpu_pmu_stats *cpustats)
Rohit Gupta5e4358c2014-07-18 16:16:02 -0700144{
145 int i;
146
Saravana Kannan52978b92017-09-26 20:29:47 -0700147 for (i = 0; i < ARRAY_SIZE(cpustats->events); i++) {
Rohit Guptabb3ac622017-05-03 17:36:54 -0700148 cpustats->events[i].prev_count = 0;
Saravana Kannan83f28462017-09-26 19:45:15 -0700149 if (cpustats->events[i].pevent) {
150 perf_event_release_kernel(cpustats->events[i].pevent);
151 cpustats->events[i].pevent = NULL;
152 }
Rohit Gupta5e4358c2014-07-18 16:16:02 -0700153 }
154}
155
156static void stop_hwmon(struct memlat_hwmon *hw)
157{
Rohit Guptabb3ac622017-05-03 17:36:54 -0700158 int cpu;
159 struct cpu_grp_info *cpu_grp = to_cpu_grp(hw);
160 struct dev_stats *devstats;
Rohit Gupta5e4358c2014-07-18 16:16:02 -0700161
162 get_online_cpus();
Rohit Guptabb3ac622017-05-03 17:36:54 -0700163 for_each_cpu(cpu, &cpu_grp->inited_cpus) {
164 delete_events(to_cpustats(cpu_grp, cpu));
Rohit Gupta5e4358c2014-07-18 16:16:02 -0700165
166 /* Clear governor data */
Rohit Guptabb3ac622017-05-03 17:36:54 -0700167 devstats = to_devstats(cpu_grp, cpu);
168 devstats->inst_count = 0;
169 devstats->mem_count = 0;
170 devstats->freq = 0;
Saravana Kannan83f28462017-09-26 19:45:15 -0700171 devstats->stall_pct = 0;
Rohit Gupta5e4358c2014-07-18 16:16:02 -0700172 }
Rohit Guptabb3ac622017-05-03 17:36:54 -0700173 mutex_lock(&list_lock);
174 if (!cpumask_equal(&cpu_grp->cpus, &cpu_grp->inited_cpus))
175 list_del(&cpu_grp->mon_list);
176 mutex_unlock(&list_lock);
177 cpumask_clear(&cpu_grp->inited_cpus);
178
Rohit Gupta5e4358c2014-07-18 16:16:02 -0700179 put_online_cpus();
180
181 unregister_cpu_notifier(&cpu_grp->arm_memlat_cpu_notif);
182}
183
184static struct perf_event_attr *alloc_attr(void)
185{
186 struct perf_event_attr *attr;
187
188 attr = kzalloc(sizeof(struct perf_event_attr), GFP_KERNEL);
189 if (!attr)
Rohit Guptabb3ac622017-05-03 17:36:54 -0700190 return attr;
Rohit Gupta5e4358c2014-07-18 16:16:02 -0700191
192 attr->type = PERF_TYPE_RAW;
193 attr->size = sizeof(struct perf_event_attr);
194 attr->pinned = 1;
195 attr->exclude_idle = 1;
196
197 return attr;
198}
199
Rohit Guptabb3ac622017-05-03 17:36:54 -0700200static int set_events(struct cpu_grp_info *cpu_grp, int cpu)
Rohit Gupta5e4358c2014-07-18 16:16:02 -0700201{
202 struct perf_event *pevent;
203 struct perf_event_attr *attr;
Saravana Kannan52978b92017-09-26 20:29:47 -0700204 int err, i;
Saravana Kannan83f28462017-09-26 19:45:15 -0700205 unsigned int event_id;
Rohit Guptabb3ac622017-05-03 17:36:54 -0700206 struct cpu_pmu_stats *cpustats = to_cpustats(cpu_grp, cpu);
Rohit Gupta5e4358c2014-07-18 16:16:02 -0700207
208 /* Allocate an attribute for event initialization */
209 attr = alloc_attr();
Rohit Guptabb3ac622017-05-03 17:36:54 -0700210 if (!attr)
211 return -ENOMEM;
Rohit Gupta5e4358c2014-07-18 16:16:02 -0700212
Saravana Kannan52978b92017-09-26 20:29:47 -0700213 for (i = 0; i < ARRAY_SIZE(cpustats->events); i++) {
Saravana Kannan83f28462017-09-26 19:45:15 -0700214 event_id = cpu_grp->event_ids[i];
215 if (!event_id)
216 continue;
217
218 attr->config = event_id;
Saravana Kannan52978b92017-09-26 20:29:47 -0700219 pevent = perf_event_create_kernel_counter(attr, cpu, NULL,
220 NULL, NULL);
221 if (IS_ERR(pevent))
222 goto err_out;
223 cpustats->events[i].pevent = pevent;
224 perf_event_enable(pevent);
225 }
Rohit Gupta5e4358c2014-07-18 16:16:02 -0700226
227 kfree(attr);
228 return 0;
229
230err_out:
231 err = PTR_ERR(pevent);
232 kfree(attr);
233 return err;
234}
235
236static int arm_memlat_cpu_callback(struct notifier_block *nb,
237 unsigned long action, void *hcpu)
238{
239 unsigned long cpu = (unsigned long)hcpu;
Rohit Guptabb3ac622017-05-03 17:36:54 -0700240 struct cpu_grp_info *cpu_grp, *tmp;
Rohit Gupta5e4358c2014-07-18 16:16:02 -0700241
Rohit Guptabb3ac622017-05-03 17:36:54 -0700242 if (action != CPU_ONLINE)
Rohit Gupta5e4358c2014-07-18 16:16:02 -0700243 return NOTIFY_OK;
244
Rohit Guptabb3ac622017-05-03 17:36:54 -0700245 mutex_lock(&list_lock);
246 list_for_each_entry_safe(cpu_grp, tmp, &memlat_mon_list, mon_list) {
247 if (!cpumask_test_cpu(cpu, &cpu_grp->cpus) ||
248 cpumask_test_cpu(cpu, &cpu_grp->inited_cpus))
249 continue;
250 if (set_events(cpu_grp, cpu))
251 pr_warn("Failed to create perf ev for CPU%lu\n", cpu);
252 else
253 cpumask_set_cpu(cpu, &cpu_grp->inited_cpus);
254 if (cpumask_equal(&cpu_grp->cpus, &cpu_grp->inited_cpus))
255 list_del(&cpu_grp->mon_list);
256 }
257 mutex_unlock(&list_lock);
Rohit Gupta5e4358c2014-07-18 16:16:02 -0700258
259 return NOTIFY_OK;
260}
261
262static int start_hwmon(struct memlat_hwmon *hw)
263{
264 int cpu, ret = 0;
Rohit Guptabb3ac622017-05-03 17:36:54 -0700265 struct cpu_grp_info *cpu_grp = to_cpu_grp(hw);
Rohit Gupta5e4358c2014-07-18 16:16:02 -0700266
267 register_cpu_notifier(&cpu_grp->arm_memlat_cpu_notif);
268
269 get_online_cpus();
270 for_each_cpu(cpu, &cpu_grp->cpus) {
Rohit Guptabb3ac622017-05-03 17:36:54 -0700271 ret = set_events(cpu_grp, cpu);
Rohit Gupta5e4358c2014-07-18 16:16:02 -0700272 if (ret) {
273 if (!cpu_online(cpu)) {
Rohit Gupta5e4358c2014-07-18 16:16:02 -0700274 ret = 0;
275 } else {
276 pr_warn("Perf event init failed on CPU%d\n",
277 cpu);
278 break;
279 }
Rohit Guptabb3ac622017-05-03 17:36:54 -0700280 } else {
281 cpumask_set_cpu(cpu, &cpu_grp->inited_cpus);
Rohit Gupta5e4358c2014-07-18 16:16:02 -0700282 }
283 }
Rohit Guptabb3ac622017-05-03 17:36:54 -0700284 mutex_lock(&list_lock);
285 if (!cpumask_equal(&cpu_grp->cpus, &cpu_grp->inited_cpus))
286 list_add_tail(&cpu_grp->mon_list, &memlat_mon_list);
287 mutex_unlock(&list_lock);
Rohit Gupta5e4358c2014-07-18 16:16:02 -0700288
289 put_online_cpus();
Rohit Guptabb3ac622017-05-03 17:36:54 -0700290
Rohit Gupta5e4358c2014-07-18 16:16:02 -0700291 return ret;
292}
293
294static int get_mask_from_dev_handle(struct platform_device *pdev,
295 cpumask_t *mask)
296{
297 struct device *dev = &pdev->dev;
298 struct device_node *dev_phandle;
299 struct device *cpu_dev;
300 int cpu, i = 0;
301 int ret = -ENOENT;
302
303 dev_phandle = of_parse_phandle(dev->of_node, "qcom,cpulist", i++);
304 while (dev_phandle) {
305 for_each_possible_cpu(cpu) {
306 cpu_dev = get_cpu_device(cpu);
307 if (cpu_dev && cpu_dev->of_node == dev_phandle) {
308 cpumask_set_cpu(cpu, mask);
309 ret = 0;
310 break;
311 }
312 }
313 dev_phandle = of_parse_phandle(dev->of_node,
314 "qcom,cpulist", i++);
315 }
316
317 return ret;
318}
319
320static int arm_memlat_mon_driver_probe(struct platform_device *pdev)
321{
322 struct device *dev = &pdev->dev;
323 struct memlat_hwmon *hw;
324 struct cpu_grp_info *cpu_grp;
Jonathan Avilae71f95f2017-10-12 15:15:47 -0700325 const struct memlat_mon_spec *spec;
Rohit Gupta5e4358c2014-07-18 16:16:02 -0700326 int cpu, ret;
Saravana Kannan52978b92017-09-26 20:29:47 -0700327 u32 event_id;
Rohit Gupta5e4358c2014-07-18 16:16:02 -0700328
329 cpu_grp = devm_kzalloc(dev, sizeof(*cpu_grp), GFP_KERNEL);
330 if (!cpu_grp)
331 return -ENOMEM;
332 cpu_grp->arm_memlat_cpu_notif.notifier_call = arm_memlat_cpu_callback;
333 hw = &cpu_grp->hw;
334
335 hw->dev = dev;
336 hw->of_node = of_parse_phandle(dev->of_node, "qcom,target-dev", 0);
337 if (!hw->of_node) {
338 dev_err(dev, "Couldn't find a target device\n");
339 return -ENODEV;
340 }
341
342 if (get_mask_from_dev_handle(pdev, &cpu_grp->cpus)) {
343 dev_err(dev, "CPU list is empty\n");
344 return -ENODEV;
345 }
346
347 hw->num_cores = cpumask_weight(&cpu_grp->cpus);
348 hw->core_stats = devm_kzalloc(dev, hw->num_cores *
349 sizeof(*(hw->core_stats)), GFP_KERNEL);
350 if (!hw->core_stats)
351 return -ENOMEM;
352
Rohit Guptabb3ac622017-05-03 17:36:54 -0700353 cpu_grp->cpustats = devm_kzalloc(dev, hw->num_cores *
354 sizeof(*(cpu_grp->cpustats)), GFP_KERNEL);
355 if (!cpu_grp->cpustats)
356 return -ENOMEM;
357
Saravana Kannan52978b92017-09-26 20:29:47 -0700358 cpu_grp->event_ids[CYC_IDX] = CYC_EV;
359
Jonathan Avilae71f95f2017-10-12 15:15:47 -0700360 for_each_cpu(cpu, &cpu_grp->cpus)
361 to_devstats(cpu_grp, cpu)->id = cpu;
362
363 hw->start_hwmon = &start_hwmon;
364 hw->stop_hwmon = &stop_hwmon;
365 hw->get_cnt = &get_cnt;
366
367 spec = of_device_get_match_data(dev);
368 if (spec && spec->is_compute) {
369 ret = register_compute(dev, hw);
370 if (ret)
371 pr_err("Compute Gov registration failed\n");
372
373 return ret;
374 }
375
Rohit Gupta3862a342017-03-07 11:47:52 -0800376 ret = of_property_read_u32(dev->of_node, "qcom,cachemiss-ev",
Saravana Kannan52978b92017-09-26 20:29:47 -0700377 &event_id);
Rohit Gupta3862a342017-03-07 11:47:52 -0800378 if (ret) {
379 dev_dbg(dev, "Cache Miss event not specified. Using def:0x%x\n",
Saravana Kannan52978b92017-09-26 20:29:47 -0700380 L2DM_EV);
381 event_id = L2DM_EV;
Rohit Gupta3862a342017-03-07 11:47:52 -0800382 }
Saravana Kannan52978b92017-09-26 20:29:47 -0700383 cpu_grp->event_ids[CM_IDX] = event_id;
Rohit Gupta3862a342017-03-07 11:47:52 -0800384
Saravana Kannan52978b92017-09-26 20:29:47 -0700385 ret = of_property_read_u32(dev->of_node, "qcom,inst-ev", &event_id);
Rohit Gupta3862a342017-03-07 11:47:52 -0800386 if (ret) {
387 dev_dbg(dev, "Inst event not specified. Using def:0x%x\n",
Saravana Kannan52978b92017-09-26 20:29:47 -0700388 INST_EV);
389 event_id = INST_EV;
Rohit Gupta3862a342017-03-07 11:47:52 -0800390 }
Saravana Kannan52978b92017-09-26 20:29:47 -0700391 cpu_grp->event_ids[INST_IDX] = event_id;
Rohit Gupta3862a342017-03-07 11:47:52 -0800392
Saravana Kannan83f28462017-09-26 19:45:15 -0700393 ret = of_property_read_u32(dev->of_node, "qcom,stall-cycle-ev",
394 &event_id);
395 if (ret)
396 dev_dbg(dev, "Stall cycle event not specified. Event ignored.\n");
397 else
398 cpu_grp->event_ids[STALL_CYC_IDX] = event_id;
399
Rohit Gupta5e4358c2014-07-18 16:16:02 -0700400 ret = register_memlat(dev, hw);
Jonathan Avilae71f95f2017-10-12 15:15:47 -0700401 if (ret)
Rohit Gupta5e4358c2014-07-18 16:16:02 -0700402 pr_err("Mem Latency Gov registration failed\n");
Rohit Gupta5e4358c2014-07-18 16:16:02 -0700403
Jonathan Avilae71f95f2017-10-12 15:15:47 -0700404 return ret;
Rohit Gupta5e4358c2014-07-18 16:16:02 -0700405}
406
Jonathan Avilae71f95f2017-10-12 15:15:47 -0700407static const struct memlat_mon_spec spec[] = {
408 [0] = { false },
409 [1] = { true },
410};
411
Rohit Gupta5e4358c2014-07-18 16:16:02 -0700412static const struct of_device_id memlat_match_table[] = {
Jonathan Avilae71f95f2017-10-12 15:15:47 -0700413 { .compatible = "qcom,arm-memlat-mon", .data = &spec[0] },
414 { .compatible = "qcom,arm-cpu-mon", .data = &spec[1] },
Rohit Gupta5e4358c2014-07-18 16:16:02 -0700415 {}
416};
417
418static struct platform_driver arm_memlat_mon_driver = {
419 .probe = arm_memlat_mon_driver_probe,
420 .driver = {
421 .name = "arm-memlat-mon",
422 .of_match_table = memlat_match_table,
423 },
424};
425
426module_platform_driver(arm_memlat_mon_driver);