blob: b30ab0dfcbbdba7e5d857b2ae39f75ac123ff569 [file] [log] [blame]
rjw1f884582022-01-06 17:20:42 +08001/*
2 * Copyright (C) 2018 MediaTek Inc.
3 *
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 as
6 * 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#include <linux/perf_event.h>
14#include "met_drv.h"
15#include "met_kernel_symbol.h"
16#include "interface.h"
17#include "trace.h"
18#include "cpu_dsu.h"
19#include "core_plf_init.h"
20
21
22struct cpu_dsu_hw *cpu_dsu;
23static int counter_cnt;
24static struct kobject *kobj_dsu;
25static int nr_arg;
26static unsigned long long perfCurr[MXNR_DSU_EVENTS];
27static unsigned long long perfPrev[MXNR_DSU_EVENTS];
28static int perfCntFirst[MXNR_DSU_EVENTS];
29static struct perf_event * pevent[MXNR_DSU_EVENTS];
30static struct perf_event_attr pevent_attr[MXNR_DSU_EVENTS];
31static unsigned int perf_device_type = 7;
32static ssize_t perf_type_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
33{
34 return snprintf(buf, PAGE_SIZE, "%d\n", perf_device_type);
35}
36
37static ssize_t perf_type_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t n)
38{
39 if (kstrtouint(buf, 0, &perf_device_type) != 0)
40 return -EINVAL;
41
42 return n;
43}
44static struct kobj_attribute perf_type_attr = __ATTR(perf_type, 0664, perf_type_show, perf_type_store);
45
46noinline void mp_dsu(unsigned char cnt, unsigned int *value)
47{
48 MET_GENERAL_PRINT(MET_TRACE, cnt, value);
49}
50
51static void dummy_handler(struct perf_event *event, struct perf_sample_data *data,
52 struct pt_regs *regs)
53{
54 /*
55 * Required as perf_event_create_kernel_counter() requires an overflow handler,
56 * even though all we do is poll.
57 */
58}
59
60static void perf_cpudsu_polling(unsigned long long stamp, int cpu)
61{
62 int event_count = cpu_dsu->event_count;
63 struct met_dsu *pmu = cpu_dsu->pmu;
64 int i, count;
65 unsigned long long delta;
66 struct perf_event *ev;
67 unsigned int pmu_value[MXNR_DSU_EVENTS];
68
69 count = 0;
70 for (i = 0; i < event_count; i++) {
71 if (pmu[i].mode == 0)
72 continue;
73
74 ev = pevent[i];
75 if ((ev != NULL) && (ev->state == PERF_EVENT_STATE_ACTIVE)) {
76 perfCurr[i] = met_perf_event_read_local_symbol(ev);
77 delta = (perfCurr[i] - perfPrev[i]);
78 perfPrev[i] = perfCurr[i];
79 if (perfCntFirst[i] == 1) {
80 /* we shall omit delta counter when we get first counter */
81 perfCntFirst[i] = 0;
82 continue;
83 }
84 pmu_value[count] = (unsigned int)delta;
85 count++;
86 }
87 }
88
89 if (count == counter_cnt)
90 mp_dsu(count, pmu_value);
91}
92
93static int perf_thread_set_perf_events(unsigned int cpu)
94{
95 int i, size;
96 struct perf_event *ev;
97 struct perf_event_attr *ev_attr;
98 int event_count = cpu_dsu->event_count;
99 struct met_dsu *pmu = cpu_dsu->pmu;
100
101 size = sizeof(struct perf_event_attr);
102
103 for (i = 0; i < event_count; i++) {
104 pevent[i] = NULL;
105 if (!pmu[i].mode)
106 continue; /* Skip disabled counters */
107 perfPrev[i] = 0;
108 perfCurr[i] = 0;
109 ev_attr = pevent_attr+i;
110 memset(ev_attr, 0, size);
111 ev_attr->config = pmu[i].event;
112 ev_attr->type = perf_device_type;
113 ev_attr->size = size;
114 ev_attr->sample_period = 0;
115 ev_attr->pinned = 1;
116
117 ev = perf_event_create_kernel_counter(ev_attr, cpu, NULL, dummy_handler, NULL);
118 if (IS_ERR(ev))
119 continue;
120 if (ev->state != PERF_EVENT_STATE_ACTIVE) {
121 perf_event_release_kernel(ev);
122 continue;
123 }
124 pevent[i] = ev;
125 if (ev != NULL)
126 perf_event_enable(ev);
127 } /* for all PMU counter */
128 return 0;
129}
130
131void met_perf_cpudsu_down(void)
132{
133 int i;
134 struct perf_event *ev;
135 int event_count;
136 struct met_dsu *pmu;
137
138 if (met_cpudsu.mode == 0)
139 return;
140 event_count = cpu_dsu->event_count;
141 pmu = cpu_dsu->pmu;
142 for (i = 0; i < event_count; i++) {
143 if (!pmu[i].mode)
144 continue;
145 ev = pevent[i];
146 if ((ev != NULL) && (ev->state == PERF_EVENT_STATE_ACTIVE)) {
147 perf_event_disable(ev);
148 perf_event_release_kernel(ev);
149 }
150 pevent[i] = NULL;
151 }
152 //perf_delayed_work_setup = NULL;
153}
154
155inline static void met_perf_cpudsu_start(int cpu)
156{
157 if (met_cpudsu.mode == 0)
158 return;
159 if (cpu != 0)
160 return;
161 perf_thread_set_perf_events(cpu);
162}
163
164static int cpudsu_create_subfs(struct kobject *parent)
165{
166 int ret = 0;
167 cpu_dsu = cpu_dsu_hw_init();
168 if (cpu_dsu == NULL) {
169 PR_BOOTMSG("Failed to init CPU PMU HW!!\n");
170 return -ENODEV;
171 }
172 kobj_dsu = parent;
173 ret = sysfs_create_file(kobj_dsu, &perf_type_attr.attr);
174 if (ret != 0) {
175 PR_BOOTMSG("Failed to create perf_type in sysfs\n");
176 goto out;
177 }
178 out:
179 return ret;
180}
181
182static void cpudsu_delete_subfs(void)
183{
184}
185
186void met_perf_cpudsu_polling(unsigned long long stamp, int cpu)
187{
188 perf_cpudsu_polling(stamp, cpu);
189}
190
191static void cpudsu_start(void)
192{
193 int cpu = raw_smp_processor_id();
194 for_each_online_cpu(cpu)
195 met_perf_cpudsu_start(cpu);
196}
197
198static void cpudsu_stop(void)
199{
200 met_perf_cpudsu_down();
201}
202
203static const char header[] =
204 "met-info [000] 0.0: met_dsu_pmu_header: DSU";
205
206static const char help[] =
207 " --dsu=EVENT select DSU-PMU events.\n"
208 " you can enable at most \"%d general purpose events\"\n";
209
210static int cpudsu_print_help(char *buf, int len)
211{
212 return snprintf(buf, PAGE_SIZE, help, cpu_dsu->event_count);
213}
214
215static int reset_driver_stat(void)
216{
217 int i;
218 int event_count;
219 struct met_dsu *pmu;
220
221 met_cpudsu.mode = 0;
222 event_count = cpu_dsu->event_count;
223 pmu = cpu_dsu->pmu;
224 counter_cnt = 0;
225 nr_arg = 0;
226 for (i = 0; i < event_count; i++) {
227 pmu[i].mode = MODE_DISABLED;
228 pmu[i].event = 0;
229 pmu[i].freq = 0;
230 }
231 return 0;
232}
233
234static int cpudsu_print_header(char *buf, int len)
235{
236 int first;
237 int i, ret;
238 int event_count;
239 struct met_dsu *pmu;
240 ret = 0;
241
242 ret += snprintf(buf + ret, PAGE_SIZE - ret, "# mp_dsu: pmu_value1, ...\n");
243 event_count = cpu_dsu->event_count;
244 pmu = cpu_dsu->pmu;
245 first = 1;
246 for (i = 0; i < event_count; i++) {
247 if (pmu[i].mode == 0)
248 continue;
249 if (first) {
250 ret += snprintf(buf + ret, PAGE_SIZE - ret, header);
251 first = 0;
252 }
253 ret += snprintf(buf + ret, PAGE_SIZE - ret, ",0x%x", pmu[i].event);
254 pmu[i].mode = 0;
255 }
256 ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n");
257 reset_driver_stat();
258 return ret;
259}
260
261static int met_parse_num_list(char *arg, int len, int *list, int list_cnt)
262{
263 int nr_num = 0;
264 char *num;
265 int num_len;
266
267 /* search ',' as the splitter */
268 while (len) {
269 num = arg;
270 num_len = 0;
271 if (list_cnt <= 0)
272 return -1;
273 while (len) {
274 len--;
275 if (*arg == ',') {
276 *(arg++) = '\0';
277 break;
278 }
279 arg++;
280 num_len++;
281 }
282 if (met_parse_num(num, list, num_len) < 0)
283 return -1;
284 list++;
285 list_cnt--;
286 nr_num++;
287 }
288 return nr_num;
289}
290
291static int cpudsu_process_argument(const char *arg, int len)
292{
293 int nr_events, event_list[MXNR_DSU_EVENTS];
294 int i;
295 int nr_counters;
296 struct met_dsu *pmu;
297 int arg_nr;
298 int counters;
299 int event_no;
300
301 /* get event_list */
302 if ((nr_events = met_parse_num_list((char*)arg, len, event_list, ARRAY_SIZE(event_list))) <= 0)
303 goto arg_out;
304
305 /* for each cpu in cpu_list, add all the events in event_list */
306 nr_counters = cpu_dsu->event_count;
307 pmu = cpu_dsu->pmu;
308 arg_nr = nr_arg;
309
310 /*
311 * setup nr_counters for linux native perf mode.
312 * because the selected events are stored in pmu,
313 * so nr_counters can't large then event count in pmu.
314 */
315 counters = perf_num_counters();
316 if (counters < nr_counters)
317 nr_counters = counters;
318
319 if (nr_counters == 0)
320 goto arg_out;
321
322 for (i = 0; i < nr_events; i++) {
323 event_no = event_list[i];
324 /*
325 * check if event is duplicate,
326 * but may not include 0xff when met_cpu_dsu_method == 0.
327 */
328 if (cpu_dsu->check_event(pmu, arg_nr, event_no) < 0)
329 goto arg_out;
330 if (arg_nr >= nr_counters)
331 goto arg_out;
332 pmu[arg_nr].mode = MODE_POLLING;
333 pmu[arg_nr].event = event_no;
334 pmu[arg_nr].freq = 0;
335 arg_nr++;
336 counter_cnt++;
337 }
338 nr_arg = arg_nr;
339 met_cpudsu.mode = 1;
340 return 0;
341
342arg_out:
343 reset_driver_stat();
344 return -EINVAL;
345}
346
347struct metdevice met_cpudsu = {
348 .name = "dsu",
349 .type = MET_TYPE_PMU,
350 .cpu_related = 0,
351 .create_subfs = cpudsu_create_subfs,
352 .delete_subfs = cpudsu_delete_subfs,
353 .start = cpudsu_start,
354 .stop = cpudsu_stop,
355 .polling_interval = 1,
356 .timed_polling = met_perf_cpudsu_polling,
357 .print_help = cpudsu_print_help,
358 .print_header = cpudsu_print_header,
359 .process_argument = cpudsu_process_argument
360};