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