1 /**
2 * Copyright (C) ARM Limited 2011-2013. All rights reserved.
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 */
10 #include "gator.h"
12 #include <linux/module.h>
13 #include <linux/time.h>
14 #include <linux/math64.h>
15 #include <linux/slab.h>
16 #include <asm/io.h>
18 #include "linux/mali_linux_trace.h"
20 #include "gator_events_mali_common.h"
22 /*
23 * Check that the MALI_SUPPORT define is set to one of the allowable device codes.
24 */
25 #if (MALI_SUPPORT != MALI_T6xx)
26 #error MALI_SUPPORT set to an invalid device code: expecting MALI_T6xx
27 #endif
29 /* Counters for Mali-T6xx:
30 *
31 * - Timeline events
32 * They are tracepoints, but instead of reporting a number they report a START/STOP event.
33 * They are reported in Streamline as number of microseconds while that particular counter was active.
34 *
35 * - SW counters
36 * They are tracepoints reporting a particular number.
37 * They are accumulated in sw_counter_data array until they are passed to Streamline, then they are zeroed.
38 *
39 * - Accumulators
40 * They are the same as software counters but their value is not zeroed.
41 */
43 /* Timeline (start/stop) activity */
44 static const char *timeline_event_names[] = {
45 "PM_SHADER_0",
46 "PM_SHADER_1",
47 "PM_SHADER_2",
48 "PM_SHADER_3",
49 "PM_SHADER_4",
50 "PM_SHADER_5",
51 "PM_SHADER_6",
52 "PM_SHADER_7",
53 "PM_TILER_0",
54 "PM_L2_0",
55 "PM_L2_1",
56 "MMU_AS_0",
57 "MMU_AS_1",
58 "MMU_AS_2",
59 "MMU_AS_3"
60 };
62 enum {
63 PM_SHADER_0 = 0,
64 PM_SHADER_1,
65 PM_SHADER_2,
66 PM_SHADER_3,
67 PM_SHADER_4,
68 PM_SHADER_5,
69 PM_SHADER_6,
70 PM_SHADER_7,
71 PM_TILER_0,
72 PM_L2_0,
73 PM_L2_1,
74 MMU_AS_0,
75 MMU_AS_1,
76 MMU_AS_2,
77 MMU_AS_3
78 };
79 /* The number of shader blocks in the enum above */
80 #define NUM_PM_SHADER (8)
82 /* Software Counters */
83 static const char *software_counter_names[] = {
84 "MMU_PAGE_FAULT_0",
85 "MMU_PAGE_FAULT_1",
86 "MMU_PAGE_FAULT_2",
87 "MMU_PAGE_FAULT_3"
88 };
90 enum {
91 MMU_PAGE_FAULT_0 = 0,
92 MMU_PAGE_FAULT_1,
93 MMU_PAGE_FAULT_2,
94 MMU_PAGE_FAULT_3
95 };
97 /* Software Counters */
98 static const char *accumulators_names[] = {
99 "TOTAL_ALLOC_PAGES"
100 };
102 enum {
103 TOTAL_ALLOC_PAGES = 0
104 };
106 #define FIRST_TIMELINE_EVENT (0)
107 #define NUMBER_OF_TIMELINE_EVENTS (sizeof(timeline_event_names) / sizeof(timeline_event_names[0]))
108 #define FIRST_SOFTWARE_COUNTER (FIRST_TIMELINE_EVENT + NUMBER_OF_TIMELINE_EVENTS)
109 #define NUMBER_OF_SOFTWARE_COUNTERS (sizeof(software_counter_names) / sizeof(software_counter_names[0]))
110 #define FIRST_ACCUMULATOR (FIRST_SOFTWARE_COUNTER + NUMBER_OF_SOFTWARE_COUNTERS)
111 #define NUMBER_OF_ACCUMULATORS (sizeof(accumulators_names) / sizeof(accumulators_names[0]))
112 #define NUMBER_OF_EVENTS (NUMBER_OF_TIMELINE_EVENTS + NUMBER_OF_SOFTWARE_COUNTERS + NUMBER_OF_ACCUMULATORS)
114 /*
115 * gatorfs variables for counter enable state
116 */
117 static mali_counter counters[NUMBER_OF_EVENTS];
119 /* An array used to return the data we recorded
120 * as key,value pairs hence the *2
121 */
122 static unsigned long counter_dump[NUMBER_OF_EVENTS * 2];
124 /*
125 * Array holding counter start times (in ns) for each counter. A zero here
126 * indicates that the activity monitored by this counter is not running.
127 */
128 static struct timespec timeline_event_starttime[NUMBER_OF_TIMELINE_EVENTS];
130 /* The data we have recorded */
131 static unsigned int timeline_data[NUMBER_OF_TIMELINE_EVENTS];
132 static unsigned int sw_counter_data[NUMBER_OF_SOFTWARE_COUNTERS];
133 static unsigned int accumulators_data[NUMBER_OF_ACCUMULATORS];
135 /* Hold the previous timestamp, used to calculate the sample interval. */
136 static struct timespec prev_timestamp;
138 /**
139 * Returns the timespan (in microseconds) between the two specified timestamps.
140 *
141 * @param start Ptr to the start timestamp
142 * @param end Ptr to the end timestamp
143 *
144 * @return Number of microseconds between the two timestamps (can be negative if start follows end).
145 */
146 static inline long get_duration_us(const struct timespec *start, const struct timespec *end)
147 {
148 long event_duration_us = (end->tv_nsec - start->tv_nsec) / 1000;
149 event_duration_us += (end->tv_sec - start->tv_sec) * 1000000;
151 return event_duration_us;
152 }
154 static void record_timeline_event(unsigned int timeline_index, unsigned int type)
155 {
156 struct timespec event_timestamp;
157 struct timespec *event_start = &timeline_event_starttime[timeline_index];
159 switch (type) {
160 case ACTIVITY_START:
161 /* Get the event time... */
162 getnstimeofday(&event_timestamp);
164 /* Remember the start time if the activity is not already started */
165 if (event_start->tv_sec == 0) {
166 *event_start = event_timestamp; /* Structure copy */
167 }
168 break;
170 case ACTIVITY_STOP:
171 /* if the counter was started... */
172 if (event_start->tv_sec != 0) {
173 /* Get the event time... */
174 getnstimeofday(&event_timestamp);
176 /* Accumulate the duration in us */
177 timeline_data[timeline_index] += get_duration_us(event_start, &event_timestamp);
179 /* Reset the start time to indicate the activity is stopped. */
180 event_start->tv_sec = 0;
181 }
182 break;
184 default:
185 /* Other activity events are ignored. */
186 break;
187 }
188 }
190 /*
191 * Documentation about the following tracepoints is in mali_linux_trace.h
192 */
194 GATOR_DEFINE_PROBE(mali_pm_status, TP_PROTO(unsigned int event_id, unsigned long long value))
195 {
196 #define SHADER_PRESENT_LO 0x100 /* (RO) Shader core present bitmap, low word */
197 #define TILER_PRESENT_LO 0x110 /* (RO) Tiler core present bitmap, low word */
198 #define L2_PRESENT_LO 0x120 /* (RO) Level 2 cache present bitmap, low word */
199 #define BIT_AT(value, pos) ((value >> pos) & 1)
201 static unsigned long long previous_shader_bitmask = 0;
202 static unsigned long long previous_tiler_bitmask = 0;
203 static unsigned long long previous_l2_bitmask = 0;
205 switch (event_id) {
206 case SHADER_PRESENT_LO:
207 {
208 unsigned long long changed_bitmask = previous_shader_bitmask ^ value;
209 int pos;
211 for (pos = 0; pos < NUM_PM_SHADER; ++pos) {
212 if (BIT_AT(changed_bitmask, pos)) {
213 record_timeline_event(PM_SHADER_0 + pos, BIT_AT(value, pos) ? ACTIVITY_START : ACTIVITY_STOP);
214 }
215 }
217 previous_shader_bitmask = value;
218 break;
219 }
221 case TILER_PRESENT_LO:
222 {
223 unsigned long long changed = previous_tiler_bitmask ^ value;
225 if (BIT_AT(changed, 0)) {
226 record_timeline_event(PM_TILER_0, BIT_AT(value, 0) ? ACTIVITY_START : ACTIVITY_STOP);
227 }
229 previous_tiler_bitmask = value;
230 break;
231 }
233 case L2_PRESENT_LO:
234 {
235 unsigned long long changed = previous_l2_bitmask ^ value;
237 if (BIT_AT(changed, 0)) {
238 record_timeline_event(PM_L2_0, BIT_AT(value, 0) ? ACTIVITY_START : ACTIVITY_STOP);
239 }
240 if (BIT_AT(changed, 4)) {
241 record_timeline_event(PM_L2_1, BIT_AT(value, 4) ? ACTIVITY_START : ACTIVITY_STOP);
242 }
244 previous_l2_bitmask = value;
245 break;
246 }
248 default:
249 /* No other blocks are supported at present */
250 break;
251 }
253 #undef SHADER_PRESENT_LO
254 #undef TILER_PRESENT_LO
255 #undef L2_PRESENT_LO
256 #undef BIT_AT
257 }
259 GATOR_DEFINE_PROBE(mali_page_fault_insert_pages, TP_PROTO(int event_id, unsigned long value))
260 {
261 /* We add to the previous since we may receive many tracepoints in one sample period */
262 sw_counter_data[MMU_PAGE_FAULT_0 + event_id] += value;
263 }
265 GATOR_DEFINE_PROBE(mali_mmu_as_in_use, TP_PROTO(int event_id))
266 {
267 record_timeline_event(MMU_AS_0 + event_id, ACTIVITY_START);
268 }
270 GATOR_DEFINE_PROBE(mali_mmu_as_released, TP_PROTO(int event_id))
271 {
272 record_timeline_event(MMU_AS_0 + event_id, ACTIVITY_STOP);
273 }
275 GATOR_DEFINE_PROBE(mali_total_alloc_pages_change, TP_PROTO(long long int event_id))
276 {
277 accumulators_data[TOTAL_ALLOC_PAGES] = event_id;
278 }
280 static int create_files(struct super_block *sb, struct dentry *root)
281 {
282 int event;
283 /*
284 * Create the filesystem for all events
285 */
286 int counter_index = 0;
287 const char *mali_name = gator_mali_get_mali_name();
289 for (event = FIRST_TIMELINE_EVENT; event < FIRST_TIMELINE_EVENT + NUMBER_OF_TIMELINE_EVENTS; event++) {
290 if (gator_mali_create_file_system(mali_name, timeline_event_names[counter_index], sb, root, &counters[event]) != 0) {
291 return -1;
292 }
293 counter_index++;
294 }
295 counter_index = 0;
296 for (event = FIRST_SOFTWARE_COUNTER; event < FIRST_SOFTWARE_COUNTER + NUMBER_OF_SOFTWARE_COUNTERS; event++) {
297 if (gator_mali_create_file_system(mali_name, software_counter_names[counter_index], sb, root, &counters[event]) != 0) {
298 return -1;
299 }
300 counter_index++;
301 }
302 counter_index = 0;
303 for (event = FIRST_ACCUMULATOR; event < FIRST_ACCUMULATOR + NUMBER_OF_ACCUMULATORS; event++) {
304 if (gator_mali_create_file_system(mali_name, accumulators_names[counter_index], sb, root, &counters[event]) != 0) {
305 return -1;
306 }
307 counter_index++;
308 }
310 return 0;
311 }
313 static int register_tracepoints(void)
314 {
315 if (GATOR_REGISTER_TRACE(mali_pm_status)) {
316 pr_debug("gator: Mali-T6xx: mali_pm_status tracepoint failed to activate\n");
317 return 0;
318 }
320 if (GATOR_REGISTER_TRACE(mali_page_fault_insert_pages)) {
321 pr_debug("gator: Mali-T6xx: mali_page_fault_insert_pages tracepoint failed to activate\n");
322 return 0;
323 }
325 if (GATOR_REGISTER_TRACE(mali_mmu_as_in_use)) {
326 pr_debug("gator: Mali-T6xx: mali_mmu_as_in_use tracepoint failed to activate\n");
327 return 0;
328 }
330 if (GATOR_REGISTER_TRACE(mali_mmu_as_released)) {
331 pr_debug("gator: Mali-T6xx: mali_mmu_as_released tracepoint failed to activate\n");
332 return 0;
333 }
335 if (GATOR_REGISTER_TRACE(mali_total_alloc_pages_change)) {
336 pr_debug("gator: Mali-T6xx: mali_total_alloc_pages_change tracepoint failed to activate\n");
337 return 0;
338 }
340 pr_debug("gator: Mali-T6xx: start\n");
341 pr_debug("gator: Mali-T6xx: mali_pm_status probe is at %p\n", &probe_mali_pm_status);
342 pr_debug("gator: Mali-T6xx: mali_page_fault_insert_pages probe is at %p\n", &probe_mali_page_fault_insert_pages);
343 pr_debug("gator: Mali-T6xx: mali_mmu_as_in_use probe is at %p\n", &probe_mali_mmu_as_in_use);
344 pr_debug("gator: Mali-T6xx: mali_mmu_as_released probe is at %p\n", &probe_mali_mmu_as_released);
345 pr_debug("gator: Mali-T6xx: mali_total_alloc_pages_change probe is at %p\n", &probe_mali_total_alloc_pages_change);
347 return 1;
348 }
350 static int start(void)
351 {
352 unsigned int cnt;
354 /* Clean all data for the next capture */
355 for (cnt = 0; cnt < NUMBER_OF_TIMELINE_EVENTS; cnt++) {
356 timeline_event_starttime[cnt].tv_sec = timeline_event_starttime[cnt].tv_nsec = 0;
357 timeline_data[cnt] = 0;
358 }
360 for (cnt = 0; cnt < NUMBER_OF_SOFTWARE_COUNTERS; cnt++) {
361 sw_counter_data[cnt] = 0;
362 }
364 for (cnt = 0; cnt < NUMBER_OF_ACCUMULATORS; cnt++) {
365 accumulators_data[cnt] = 0;
366 }
368 /* Register tracepoints */
369 if (register_tracepoints() == 0) {
370 return -1;
371 }
373 /*
374 * Set the first timestamp for calculating the sample interval. The first interval could be quite long,
375 * since it will be the time between 'start' and the first 'read'.
376 * This means that timeline values will be divided by a big number for the first sample.
377 */
378 getnstimeofday(&prev_timestamp);
380 return 0;
381 }
383 static void stop(void)
384 {
385 pr_debug("gator: Mali-T6xx: stop\n");
387 /*
388 * It is safe to unregister traces even if they were not successfully
389 * registered, so no need to check.
390 */
391 GATOR_UNREGISTER_TRACE(mali_pm_status);
392 pr_debug("gator: Mali-T6xx: mali_pm_status tracepoint deactivated\n");
394 GATOR_UNREGISTER_TRACE(mali_page_fault_insert_pages);
395 pr_debug("gator: Mali-T6xx: mali_page_fault_insert_pages tracepoint deactivated\n");
397 GATOR_UNREGISTER_TRACE(mali_mmu_as_in_use);
398 pr_debug("gator: Mali-T6xx: mali_mmu_as_in_use tracepoint deactivated\n");
400 GATOR_UNREGISTER_TRACE(mali_mmu_as_released);
401 pr_debug("gator: Mali-T6xx: mali_mmu_as_released tracepoint deactivated\n");
403 GATOR_UNREGISTER_TRACE(mali_total_alloc_pages_change);
404 pr_debug("gator: Mali-T6xx: mali_total_alloc_pages_change tracepoint deactivated\n");
405 }
407 static int read(int **buffer)
408 {
409 int cnt;
410 int len = 0;
411 long sample_interval_us = 0;
412 struct timespec read_timestamp;
414 if (!on_primary_core()) {
415 return 0;
416 }
418 /* Get the start of this sample period. */
419 getnstimeofday(&read_timestamp);
421 /*
422 * Calculate the sample interval if the previous sample time is valid.
423 * We use tv_sec since it will not be 0.
424 */
425 if (prev_timestamp.tv_sec != 0) {
426 sample_interval_us = get_duration_us(&prev_timestamp, &read_timestamp);
427 }
429 /* Structure copy. Update the previous timestamp. */
430 prev_timestamp = read_timestamp;
432 /*
433 * Report the timeline counters (ACTIVITY_START/STOP)
434 */
435 for (cnt = FIRST_TIMELINE_EVENT; cnt < (FIRST_TIMELINE_EVENT + NUMBER_OF_TIMELINE_EVENTS); cnt++) {
436 mali_counter *counter = &counters[cnt];
437 if (counter->enabled) {
438 const int index = cnt - FIRST_TIMELINE_EVENT;
439 unsigned int value;
441 /* If the activity is still running, reset its start time to the start of this sample period
442 * to correct the count. Add the time up to the end of the sample onto the count. */
443 if (timeline_event_starttime[index].tv_sec != 0) {
444 const long event_duration = get_duration_us(&timeline_event_starttime[index], &read_timestamp);
445 timeline_data[index] += event_duration;
446 timeline_event_starttime[index] = read_timestamp; /* Activity is still running. */
447 }
449 if (sample_interval_us != 0) {
450 /* Convert the counter to a percent-of-sample value */
451 value = (timeline_data[index] * 100) / sample_interval_us;
452 } else {
453 pr_debug("gator: Mali-T6xx: setting value to zero\n");
454 value = 0;
455 }
457 /* Clear the counter value ready for the next sample. */
458 timeline_data[index] = 0;
460 counter_dump[len++] = counter->key;
461 counter_dump[len++] = value;
462 }
463 }
465 /* Report the software counters */
466 for (cnt = FIRST_SOFTWARE_COUNTER; cnt < (FIRST_SOFTWARE_COUNTER + NUMBER_OF_SOFTWARE_COUNTERS); cnt++) {
467 const mali_counter *counter = &counters[cnt];
468 if (counter->enabled) {
469 const int index = cnt - FIRST_SOFTWARE_COUNTER;
470 counter_dump[len++] = counter->key;
471 counter_dump[len++] = sw_counter_data[index];
472 /* Set the value to zero for the next time */
473 sw_counter_data[index] = 0;
474 }
475 }
477 /* Report the accumulators */
478 for (cnt = FIRST_ACCUMULATOR; cnt < (FIRST_ACCUMULATOR + NUMBER_OF_ACCUMULATORS); cnt++) {
479 const mali_counter *counter = &counters[cnt];
480 if (counter->enabled) {
481 const int index = cnt - FIRST_ACCUMULATOR;
482 counter_dump[len++] = counter->key;
483 counter_dump[len++] = accumulators_data[index];
484 /* Do not zero the accumulator */
485 }
486 }
488 /* Update the buffer */
489 if (buffer) {
490 *buffer = (int *)counter_dump;
491 }
493 return len;
494 }
496 static struct gator_interface gator_events_mali_t6xx_interface = {
497 .create_files = create_files,
498 .start = start,
499 .stop = stop,
500 .read = read
501 };
503 extern int gator_events_mali_t6xx_init(void)
504 {
505 pr_debug("gator: Mali-T6xx: sw_counters init\n");
507 gator_mali_initialise_counters(counters, NUMBER_OF_EVENTS);
509 return gator_events_install(&gator_events_mali_t6xx_interface);
510 }
512 gator_events_init(gator_events_mali_t6xx_init);