2 * Copyright (C) ARM Limited 2011-2014. All rights reserved.
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.
12 #include <linux/module.h>
13 #include <linux/time.h>
14 #include <linux/math64.h>
15 #include <linux/slab.h>
18 #ifdef MALI_DIR_MIDGARD
19 /* New DDK Directory structure with kernel/drivers/gpu/arm/midgard*/
20 #include "mali_linux_trace.h"
22 /* Old DDK Directory structure with kernel/drivers/gpu/arm/t6xx*/
23 #include "linux/mali_linux_trace.h"
26 #include "gator_events_mali_common.h"
29 * Check that the MALI_SUPPORT define is set to one of the allowable device codes.
31 #if (MALI_SUPPORT != MALI_T6xx)
32 #error MALI_SUPPORT set to an invalid device code: expecting MALI_T6xx
35 static const char mali_name[] = "Mali-T6xx";
37 /* Counters for Mali-T6xx:
40 * They are tracepoints, but instead of reporting a number they report a START/STOP event.
41 * They are reported in Streamline as number of microseconds while that particular counter was active.
44 * They are tracepoints reporting a particular number.
45 * They are accumulated in sw_counter_data array until they are passed to Streamline, then they are zeroed.
48 * They are the same as software counters but their value is not zeroed.
51 /* Timeline (start/stop) activity */
52 static const char *timeline_event_names[] = {
87 /* The number of shader blocks in the enum above */
88 #define NUM_PM_SHADER (8)
90 /* Software Counters */
91 static const char *software_counter_names[] = {
105 /* Software Counters */
106 static const char *accumulators_names[] = {
111 TOTAL_ALLOC_PAGES = 0
114 #define FIRST_TIMELINE_EVENT (0)
115 #define NUMBER_OF_TIMELINE_EVENTS (sizeof(timeline_event_names) / sizeof(timeline_event_names[0]))
116 #define FIRST_SOFTWARE_COUNTER (FIRST_TIMELINE_EVENT + NUMBER_OF_TIMELINE_EVENTS)
117 #define NUMBER_OF_SOFTWARE_COUNTERS (sizeof(software_counter_names) / sizeof(software_counter_names[0]))
118 #define FIRST_ACCUMULATOR (FIRST_SOFTWARE_COUNTER + NUMBER_OF_SOFTWARE_COUNTERS)
119 #define NUMBER_OF_ACCUMULATORS (sizeof(accumulators_names) / sizeof(accumulators_names[0]))
120 #define FILMSTRIP (NUMBER_OF_TIMELINE_EVENTS + NUMBER_OF_SOFTWARE_COUNTERS + NUMBER_OF_ACCUMULATORS)
121 #define NUMBER_OF_EVENTS (NUMBER_OF_TIMELINE_EVENTS + NUMBER_OF_SOFTWARE_COUNTERS + NUMBER_OF_ACCUMULATORS + 1)
124 * gatorfs variables for counter enable state
126 static mali_counter counters[NUMBER_OF_EVENTS];
127 static unsigned long filmstrip_event;
129 /* An array used to return the data we recorded
130 * as key,value pairs hence the *2
132 static unsigned long counter_dump[NUMBER_OF_EVENTS * 2];
135 * Array holding counter start times (in ns) for each counter. A zero here
136 * indicates that the activity monitored by this counter is not running.
138 static struct timespec timeline_event_starttime[NUMBER_OF_TIMELINE_EVENTS];
140 /* The data we have recorded */
141 static unsigned int timeline_data[NUMBER_OF_TIMELINE_EVENTS];
142 static unsigned int sw_counter_data[NUMBER_OF_SOFTWARE_COUNTERS];
143 static unsigned int accumulators_data[NUMBER_OF_ACCUMULATORS];
145 /* Hold the previous timestamp, used to calculate the sample interval. */
146 static struct timespec prev_timestamp;
149 * Returns the timespan (in microseconds) between the two specified timestamps.
151 * @param start Ptr to the start timestamp
152 * @param end Ptr to the end timestamp
154 * @return Number of microseconds between the two timestamps (can be negative if start follows end).
156 static inline long get_duration_us(const struct timespec *start, const struct timespec *end)
158 long event_duration_us = (end->tv_nsec - start->tv_nsec) / 1000;
159 event_duration_us += (end->tv_sec - start->tv_sec) * 1000000;
161 return event_duration_us;
164 static void record_timeline_event(unsigned int timeline_index, unsigned int type)
166 struct timespec event_timestamp;
167 struct timespec *event_start = &timeline_event_starttime[timeline_index];
171 /* Get the event time... */
172 getnstimeofday(&event_timestamp);
174 /* Remember the start time if the activity is not already started */
175 if (event_start->tv_sec == 0) {
176 *event_start = event_timestamp; /* Structure copy */
181 /* if the counter was started... */
182 if (event_start->tv_sec != 0) {
183 /* Get the event time... */
184 getnstimeofday(&event_timestamp);
186 /* Accumulate the duration in us */
187 timeline_data[timeline_index] += get_duration_us(event_start, &event_timestamp);
189 /* Reset the start time to indicate the activity is stopped. */
190 event_start->tv_sec = 0;
195 /* Other activity events are ignored. */
201 * Documentation about the following tracepoints is in mali_linux_trace.h
204 GATOR_DEFINE_PROBE(mali_pm_status, TP_PROTO(unsigned int event_id, unsigned long long value))
206 #define SHADER_PRESENT_LO 0x100 /* (RO) Shader core present bitmap, low word */
207 #define TILER_PRESENT_LO 0x110 /* (RO) Tiler core present bitmap, low word */
208 #define L2_PRESENT_LO 0x120 /* (RO) Level 2 cache present bitmap, low word */
209 #define BIT_AT(value, pos) ((value >> pos) & 1)
211 static unsigned long long previous_shader_bitmask = 0;
212 static unsigned long long previous_tiler_bitmask = 0;
213 static unsigned long long previous_l2_bitmask = 0;
216 case SHADER_PRESENT_LO:
218 unsigned long long changed_bitmask = previous_shader_bitmask ^ value;
221 for (pos = 0; pos < NUM_PM_SHADER; ++pos) {
222 if (BIT_AT(changed_bitmask, pos)) {
223 record_timeline_event(PM_SHADER_0 + pos, BIT_AT(value, pos) ? ACTIVITY_START : ACTIVITY_STOP);
227 previous_shader_bitmask = value;
231 case TILER_PRESENT_LO:
233 unsigned long long changed = previous_tiler_bitmask ^ value;
235 if (BIT_AT(changed, 0)) {
236 record_timeline_event(PM_TILER_0, BIT_AT(value, 0) ? ACTIVITY_START : ACTIVITY_STOP);
239 previous_tiler_bitmask = value;
245 unsigned long long changed = previous_l2_bitmask ^ value;
247 if (BIT_AT(changed, 0)) {
248 record_timeline_event(PM_L2_0, BIT_AT(value, 0) ? ACTIVITY_START : ACTIVITY_STOP);
250 if (BIT_AT(changed, 4)) {
251 record_timeline_event(PM_L2_1, BIT_AT(value, 4) ? ACTIVITY_START : ACTIVITY_STOP);
254 previous_l2_bitmask = value;
259 /* No other blocks are supported at present */
263 #undef SHADER_PRESENT_LO
264 #undef TILER_PRESENT_LO
269 GATOR_DEFINE_PROBE(mali_page_fault_insert_pages, TP_PROTO(int event_id, unsigned long value))
271 /* We add to the previous since we may receive many tracepoints in one sample period */
272 sw_counter_data[MMU_PAGE_FAULT_0 + event_id] += value;
275 GATOR_DEFINE_PROBE(mali_mmu_as_in_use, TP_PROTO(int event_id))
277 record_timeline_event(MMU_AS_0 + event_id, ACTIVITY_START);
280 GATOR_DEFINE_PROBE(mali_mmu_as_released, TP_PROTO(int event_id))
282 record_timeline_event(MMU_AS_0 + event_id, ACTIVITY_STOP);
285 GATOR_DEFINE_PROBE(mali_total_alloc_pages_change, TP_PROTO(long long int event_id))
287 accumulators_data[TOTAL_ALLOC_PAGES] = event_id;
290 static int create_files(struct super_block *sb, struct dentry *root)
294 * Create the filesystem for all events
296 int counter_index = 0;
297 mali_profiling_control_type *mali_control;
299 for (event = FIRST_TIMELINE_EVENT; event < FIRST_TIMELINE_EVENT + NUMBER_OF_TIMELINE_EVENTS; event++) {
300 if (gator_mali_create_file_system(mali_name, timeline_event_names[counter_index], sb, root, &counters[event], NULL) != 0) {
306 for (event = FIRST_SOFTWARE_COUNTER; event < FIRST_SOFTWARE_COUNTER + NUMBER_OF_SOFTWARE_COUNTERS; event++) {
307 if (gator_mali_create_file_system(mali_name, software_counter_names[counter_index], sb, root, &counters[event], NULL) != 0) {
313 for (event = FIRST_ACCUMULATOR; event < FIRST_ACCUMULATOR + NUMBER_OF_ACCUMULATORS; event++) {
314 if (gator_mali_create_file_system(mali_name, accumulators_names[counter_index], sb, root, &counters[event], NULL) != 0) {
320 mali_control = symbol_get(_mali_profiling_control);
322 if (gator_mali_create_file_system(mali_name, "Filmstrip_cnt0", sb, root, &counters[FILMSTRIP], &filmstrip_event) != 0) {
325 symbol_put(_mali_profiling_control);
331 static int register_tracepoints(void)
333 if (GATOR_REGISTER_TRACE(mali_pm_status)) {
334 pr_debug("gator: Mali-T6xx: mali_pm_status tracepoint failed to activate\n");
338 if (GATOR_REGISTER_TRACE(mali_page_fault_insert_pages)) {
339 pr_debug("gator: Mali-T6xx: mali_page_fault_insert_pages tracepoint failed to activate\n");
343 if (GATOR_REGISTER_TRACE(mali_mmu_as_in_use)) {
344 pr_debug("gator: Mali-T6xx: mali_mmu_as_in_use tracepoint failed to activate\n");
348 if (GATOR_REGISTER_TRACE(mali_mmu_as_released)) {
349 pr_debug("gator: Mali-T6xx: mali_mmu_as_released tracepoint failed to activate\n");
353 if (GATOR_REGISTER_TRACE(mali_total_alloc_pages_change)) {
354 pr_debug("gator: Mali-T6xx: mali_total_alloc_pages_change tracepoint failed to activate\n");
358 pr_debug("gator: Mali-T6xx: start\n");
359 pr_debug("gator: Mali-T6xx: mali_pm_status probe is at %p\n", &probe_mali_pm_status);
360 pr_debug("gator: Mali-T6xx: mali_page_fault_insert_pages probe is at %p\n", &probe_mali_page_fault_insert_pages);
361 pr_debug("gator: Mali-T6xx: mali_mmu_as_in_use probe is at %p\n", &probe_mali_mmu_as_in_use);
362 pr_debug("gator: Mali-T6xx: mali_mmu_as_released probe is at %p\n", &probe_mali_mmu_as_released);
363 pr_debug("gator: Mali-T6xx: mali_total_alloc_pages_change probe is at %p\n", &probe_mali_total_alloc_pages_change);
368 static int start(void)
371 mali_profiling_control_type *mali_control;
373 /* Clean all data for the next capture */
374 for (cnt = 0; cnt < NUMBER_OF_TIMELINE_EVENTS; cnt++) {
375 timeline_event_starttime[cnt].tv_sec = timeline_event_starttime[cnt].tv_nsec = 0;
376 timeline_data[cnt] = 0;
379 for (cnt = 0; cnt < NUMBER_OF_SOFTWARE_COUNTERS; cnt++) {
380 sw_counter_data[cnt] = 0;
383 for (cnt = 0; cnt < NUMBER_OF_ACCUMULATORS; cnt++) {
384 accumulators_data[cnt] = 0;
387 /* Register tracepoints */
388 if (register_tracepoints() == 0) {
392 /* Generic control interface for Mali DDK. */
393 mali_control = symbol_get(_mali_profiling_control);
395 /* The event attribute in the XML file keeps the actual frame rate. */
396 unsigned int enabled = counters[FILMSTRIP].enabled ? 1 : 0;
397 unsigned int rate = filmstrip_event & 0xff;
398 unsigned int resize_factor = (filmstrip_event >> 8) & 0xff;
400 pr_debug("gator: mali online _mali_profiling_control symbol @ %p\n", mali_control);
402 #define FBDUMP_CONTROL_ENABLE (1)
403 #define FBDUMP_CONTROL_RATE (2)
404 #define FBDUMP_CONTROL_RESIZE_FACTOR (4)
405 mali_control(FBDUMP_CONTROL_ENABLE, enabled);
406 mali_control(FBDUMP_CONTROL_RATE, rate);
407 mali_control(FBDUMP_CONTROL_RESIZE_FACTOR, resize_factor);
409 pr_debug("gator: sent mali_control enabled=%d, rate=%d, resize_factor=%d\n", enabled, rate, resize_factor);
411 symbol_put(_mali_profiling_control);
413 printk("gator: mali online _mali_profiling_control symbol not found\n");
417 * Set the first timestamp for calculating the sample interval. The first interval could be quite long,
418 * since it will be the time between 'start' and the first 'read'.
419 * This means that timeline values will be divided by a big number for the first sample.
421 getnstimeofday(&prev_timestamp);
426 static void stop(void)
428 mali_profiling_control_type *mali_control;
430 pr_debug("gator: Mali-T6xx: stop\n");
433 * It is safe to unregister traces even if they were not successfully
434 * registered, so no need to check.
436 GATOR_UNREGISTER_TRACE(mali_pm_status);
437 pr_debug("gator: Mali-T6xx: mali_pm_status tracepoint deactivated\n");
439 GATOR_UNREGISTER_TRACE(mali_page_fault_insert_pages);
440 pr_debug("gator: Mali-T6xx: mali_page_fault_insert_pages tracepoint deactivated\n");
442 GATOR_UNREGISTER_TRACE(mali_mmu_as_in_use);
443 pr_debug("gator: Mali-T6xx: mali_mmu_as_in_use tracepoint deactivated\n");
445 GATOR_UNREGISTER_TRACE(mali_mmu_as_released);
446 pr_debug("gator: Mali-T6xx: mali_mmu_as_released tracepoint deactivated\n");
448 GATOR_UNREGISTER_TRACE(mali_total_alloc_pages_change);
449 pr_debug("gator: Mali-T6xx: mali_total_alloc_pages_change tracepoint deactivated\n");
451 /* Generic control interface for Mali DDK. */
452 mali_control = symbol_get(_mali_profiling_control);
454 pr_debug("gator: mali offline _mali_profiling_control symbol @ %p\n", mali_control);
456 mali_control(FBDUMP_CONTROL_ENABLE, 0);
458 symbol_put(_mali_profiling_control);
460 printk("gator: mali offline _mali_profiling_control symbol not found\n");
464 static int read(int **buffer)
468 long sample_interval_us = 0;
469 struct timespec read_timestamp;
471 if (!on_primary_core()) {
475 /* Get the start of this sample period. */
476 getnstimeofday(&read_timestamp);
479 * Calculate the sample interval if the previous sample time is valid.
480 * We use tv_sec since it will not be 0.
482 if (prev_timestamp.tv_sec != 0) {
483 sample_interval_us = get_duration_us(&prev_timestamp, &read_timestamp);
486 /* Structure copy. Update the previous timestamp. */
487 prev_timestamp = read_timestamp;
490 * Report the timeline counters (ACTIVITY_START/STOP)
492 for (cnt = FIRST_TIMELINE_EVENT; cnt < (FIRST_TIMELINE_EVENT + NUMBER_OF_TIMELINE_EVENTS); cnt++) {
493 mali_counter *counter = &counters[cnt];
494 if (counter->enabled) {
495 const int index = cnt - FIRST_TIMELINE_EVENT;
498 /* If the activity is still running, reset its start time to the start of this sample period
499 * to correct the count. Add the time up to the end of the sample onto the count. */
500 if (timeline_event_starttime[index].tv_sec != 0) {
501 const long event_duration = get_duration_us(&timeline_event_starttime[index], &read_timestamp);
502 timeline_data[index] += event_duration;
503 timeline_event_starttime[index] = read_timestamp; /* Activity is still running. */
506 if (sample_interval_us != 0) {
507 /* Convert the counter to a percent-of-sample value */
508 value = (timeline_data[index] * 100) / sample_interval_us;
510 pr_debug("gator: Mali-T6xx: setting value to zero\n");
514 /* Clear the counter value ready for the next sample. */
515 timeline_data[index] = 0;
517 counter_dump[len++] = counter->key;
518 counter_dump[len++] = value;
522 /* Report the software counters */
523 for (cnt = FIRST_SOFTWARE_COUNTER; cnt < (FIRST_SOFTWARE_COUNTER + NUMBER_OF_SOFTWARE_COUNTERS); cnt++) {
524 const mali_counter *counter = &counters[cnt];
525 if (counter->enabled) {
526 const int index = cnt - FIRST_SOFTWARE_COUNTER;
527 counter_dump[len++] = counter->key;
528 counter_dump[len++] = sw_counter_data[index];
529 /* Set the value to zero for the next time */
530 sw_counter_data[index] = 0;
534 /* Report the accumulators */
535 for (cnt = FIRST_ACCUMULATOR; cnt < (FIRST_ACCUMULATOR + NUMBER_OF_ACCUMULATORS); cnt++) {
536 const mali_counter *counter = &counters[cnt];
537 if (counter->enabled) {
538 const int index = cnt - FIRST_ACCUMULATOR;
539 counter_dump[len++] = counter->key;
540 counter_dump[len++] = accumulators_data[index];
541 /* Do not zero the accumulator */
545 /* Update the buffer */
547 *buffer = (int *)counter_dump;
553 static struct gator_interface gator_events_mali_t6xx_interface = {
554 .create_files = create_files,
560 extern int gator_events_mali_t6xx_init(void)
562 pr_debug("gator: Mali-T6xx: sw_counters init\n");
564 gator_mali_initialise_counters(counters, NUMBER_OF_EVENTS);
566 return gator_events_install(&gator_events_mali_t6xx_interface);