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