567 lines
17 KiB
C
567 lines
17 KiB
C
/**
|
|
* Copyright (C) ARM Limited 2011-2015. All rights reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
*/
|
|
|
|
#include "gator.h"
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/time.h>
|
|
#include <linux/math64.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/io.h>
|
|
|
|
#ifdef MALI_DIR_MIDGARD
|
|
/* New DDK Directory structure with kernel/drivers/gpu/arm/midgard*/
|
|
#include "mali_linux_trace.h"
|
|
#else
|
|
/* Old DDK Directory structure with kernel/drivers/gpu/arm/t6xx*/
|
|
#include "linux/mali_linux_trace.h"
|
|
#endif
|
|
|
|
#include "gator_events_mali_common.h"
|
|
|
|
/*
|
|
* Check that the MALI_SUPPORT define is set to one of the allowable device codes.
|
|
*/
|
|
#if (MALI_SUPPORT != MALI_MIDGARD)
|
|
#error MALI_SUPPORT set to an invalid device code: expecting MALI_MIDGARD
|
|
#endif
|
|
|
|
static const char mali_name[] = "Midgard";
|
|
|
|
/* Counters for Mali-Midgard:
|
|
*
|
|
* - Timeline events
|
|
* They are tracepoints, but instead of reporting a number they report a START/STOP event.
|
|
* They are reported in Streamline as number of microseconds while that particular counter was active.
|
|
*
|
|
* - SW counters
|
|
* They are tracepoints reporting a particular number.
|
|
* They are accumulated in sw_counter_data array until they are passed to Streamline, then they are zeroed.
|
|
*
|
|
* - Accumulators
|
|
* They are the same as software counters but their value is not zeroed.
|
|
*/
|
|
|
|
/* Timeline (start/stop) activity */
|
|
static const char *const timeline_event_names[] = {
|
|
"PM_SHADER_0",
|
|
"PM_SHADER_1",
|
|
"PM_SHADER_2",
|
|
"PM_SHADER_3",
|
|
"PM_SHADER_4",
|
|
"PM_SHADER_5",
|
|
"PM_SHADER_6",
|
|
"PM_SHADER_7",
|
|
"PM_TILER_0",
|
|
"PM_L2_0",
|
|
"PM_L2_1",
|
|
"MMU_AS_0",
|
|
"MMU_AS_1",
|
|
"MMU_AS_2",
|
|
"MMU_AS_3"
|
|
};
|
|
|
|
enum {
|
|
PM_SHADER_0 = 0,
|
|
PM_SHADER_1,
|
|
PM_SHADER_2,
|
|
PM_SHADER_3,
|
|
PM_SHADER_4,
|
|
PM_SHADER_5,
|
|
PM_SHADER_6,
|
|
PM_SHADER_7,
|
|
PM_TILER_0,
|
|
PM_L2_0,
|
|
PM_L2_1,
|
|
MMU_AS_0,
|
|
MMU_AS_1,
|
|
MMU_AS_2,
|
|
MMU_AS_3
|
|
};
|
|
/* The number of shader blocks in the enum above */
|
|
#define NUM_PM_SHADER (8)
|
|
|
|
/* Software Counters */
|
|
static const char *const software_counter_names[] = {
|
|
"MMU_PAGE_FAULT_0",
|
|
"MMU_PAGE_FAULT_1",
|
|
"MMU_PAGE_FAULT_2",
|
|
"MMU_PAGE_FAULT_3"
|
|
};
|
|
|
|
enum {
|
|
MMU_PAGE_FAULT_0 = 0,
|
|
MMU_PAGE_FAULT_1,
|
|
MMU_PAGE_FAULT_2,
|
|
MMU_PAGE_FAULT_3
|
|
};
|
|
|
|
/* Software Counters */
|
|
static const char *const accumulators_names[] = {
|
|
"TOTAL_ALLOC_PAGES"
|
|
};
|
|
|
|
enum {
|
|
TOTAL_ALLOC_PAGES = 0
|
|
};
|
|
|
|
#define FIRST_TIMELINE_EVENT (0)
|
|
#define NUMBER_OF_TIMELINE_EVENTS (sizeof(timeline_event_names) / sizeof(timeline_event_names[0]))
|
|
#define FIRST_SOFTWARE_COUNTER (FIRST_TIMELINE_EVENT + NUMBER_OF_TIMELINE_EVENTS)
|
|
#define NUMBER_OF_SOFTWARE_COUNTERS (sizeof(software_counter_names) / sizeof(software_counter_names[0]))
|
|
#define FIRST_ACCUMULATOR (FIRST_SOFTWARE_COUNTER + NUMBER_OF_SOFTWARE_COUNTERS)
|
|
#define NUMBER_OF_ACCUMULATORS (sizeof(accumulators_names) / sizeof(accumulators_names[0]))
|
|
#define FILMSTRIP (NUMBER_OF_TIMELINE_EVENTS + NUMBER_OF_SOFTWARE_COUNTERS + NUMBER_OF_ACCUMULATORS)
|
|
#define NUMBER_OF_EVENTS (NUMBER_OF_TIMELINE_EVENTS + NUMBER_OF_SOFTWARE_COUNTERS + NUMBER_OF_ACCUMULATORS + 1)
|
|
|
|
/*
|
|
* gatorfs variables for counter enable state
|
|
*/
|
|
static struct mali_counter counters[NUMBER_OF_EVENTS];
|
|
static unsigned long filmstrip_event;
|
|
|
|
/* An array used to return the data we recorded
|
|
* as key,value pairs hence the *2
|
|
*/
|
|
static int counter_dump[NUMBER_OF_EVENTS * 2];
|
|
|
|
/*
|
|
* Array holding counter start times (in ns) for each counter. A zero
|
|
* here indicates that the activity monitored by this counter is not
|
|
* running.
|
|
*/
|
|
static struct timespec timeline_event_starttime[NUMBER_OF_TIMELINE_EVENTS];
|
|
|
|
/* The data we have recorded */
|
|
static unsigned int timeline_data[NUMBER_OF_TIMELINE_EVENTS];
|
|
static unsigned int sw_counter_data[NUMBER_OF_SOFTWARE_COUNTERS];
|
|
static unsigned int accumulators_data[NUMBER_OF_ACCUMULATORS];
|
|
|
|
/* Hold the previous timestamp, used to calculate the sample interval. */
|
|
static struct timespec prev_timestamp;
|
|
|
|
static unsigned long long previous_shader_bitmask;
|
|
static unsigned long long previous_tiler_bitmask;
|
|
static unsigned long long previous_l2_bitmask;
|
|
|
|
/**
|
|
* Returns the timespan (in microseconds) between the two specified timestamps.
|
|
*
|
|
* @param start Ptr to the start timestamp
|
|
* @param end Ptr to the end timestamp
|
|
*
|
|
* @return Number of microseconds between the two timestamps (can be negative if start follows end).
|
|
*/
|
|
static inline long get_duration_us(const struct timespec *start, const struct timespec *end)
|
|
{
|
|
long event_duration_us = (end->tv_nsec - start->tv_nsec) / 1000;
|
|
|
|
event_duration_us += (end->tv_sec - start->tv_sec) * 1000000;
|
|
|
|
return event_duration_us;
|
|
}
|
|
|
|
static void record_timeline_event(unsigned int timeline_index, unsigned int type)
|
|
{
|
|
struct timespec event_timestamp;
|
|
struct timespec *event_start = &timeline_event_starttime[timeline_index];
|
|
|
|
switch (type) {
|
|
case ACTIVITY_START:
|
|
/* Get the event time... */
|
|
getnstimeofday(&event_timestamp);
|
|
|
|
/* Remember the start time if the activity is not already started */
|
|
if (event_start->tv_sec == 0)
|
|
*event_start = event_timestamp; /* Structure copy */
|
|
break;
|
|
|
|
case ACTIVITY_STOP:
|
|
/* if the counter was started... */
|
|
if (event_start->tv_sec != 0) {
|
|
/* Get the event time... */
|
|
getnstimeofday(&event_timestamp);
|
|
|
|
/* Accumulate the duration in us */
|
|
timeline_data[timeline_index] += get_duration_us(event_start, &event_timestamp);
|
|
|
|
/* Reset the start time to indicate the activity is stopped. */
|
|
event_start->tv_sec = 0;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
/* Other activity events are ignored. */
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Documentation about the following tracepoints is in mali_linux_trace.h
|
|
*/
|
|
|
|
GATOR_DEFINE_PROBE(mali_pm_status, TP_PROTO(unsigned int event_id, unsigned long long value))
|
|
{
|
|
#define SHADER_PRESENT_LO 0x100 /* (RO) Shader core present bitmap, low word */
|
|
#define TILER_PRESENT_LO 0x110 /* (RO) Tiler core present bitmap, low word */
|
|
#define L2_PRESENT_LO 0x120 /* (RO) Level 2 cache present bitmap, low word */
|
|
#define BIT_AT(value, pos) ((value >> pos) & 1)
|
|
|
|
switch (event_id) {
|
|
case SHADER_PRESENT_LO:
|
|
{
|
|
unsigned long long changed_bitmask = previous_shader_bitmask ^ value;
|
|
int pos;
|
|
|
|
for (pos = 0; pos < NUM_PM_SHADER; ++pos) {
|
|
if (BIT_AT(changed_bitmask, pos))
|
|
record_timeline_event(PM_SHADER_0 + pos, BIT_AT(value, pos) ? ACTIVITY_START : ACTIVITY_STOP);
|
|
}
|
|
|
|
previous_shader_bitmask = value;
|
|
break;
|
|
}
|
|
|
|
case TILER_PRESENT_LO:
|
|
{
|
|
unsigned long long changed = previous_tiler_bitmask ^ value;
|
|
|
|
if (BIT_AT(changed, 0))
|
|
record_timeline_event(PM_TILER_0, BIT_AT(value, 0) ? ACTIVITY_START : ACTIVITY_STOP);
|
|
|
|
previous_tiler_bitmask = value;
|
|
break;
|
|
}
|
|
|
|
case L2_PRESENT_LO:
|
|
{
|
|
unsigned long long changed = previous_l2_bitmask ^ value;
|
|
|
|
if (BIT_AT(changed, 0))
|
|
record_timeline_event(PM_L2_0, BIT_AT(value, 0) ? ACTIVITY_START : ACTIVITY_STOP);
|
|
if (BIT_AT(changed, 4))
|
|
record_timeline_event(PM_L2_1, BIT_AT(value, 4) ? ACTIVITY_START : ACTIVITY_STOP);
|
|
|
|
previous_l2_bitmask = value;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
/* No other blocks are supported at present */
|
|
break;
|
|
}
|
|
|
|
#undef SHADER_PRESENT_LO
|
|
#undef TILER_PRESENT_LO
|
|
#undef L2_PRESENT_LO
|
|
#undef BIT_AT
|
|
}
|
|
|
|
GATOR_DEFINE_PROBE(mali_page_fault_insert_pages, TP_PROTO(int event_id, unsigned long value))
|
|
{
|
|
/* We add to the previous since we may receive many tracepoints in one sample period */
|
|
sw_counter_data[MMU_PAGE_FAULT_0 + event_id] += value;
|
|
}
|
|
|
|
GATOR_DEFINE_PROBE(mali_mmu_as_in_use, TP_PROTO(int event_id))
|
|
{
|
|
record_timeline_event(MMU_AS_0 + event_id, ACTIVITY_START);
|
|
}
|
|
|
|
GATOR_DEFINE_PROBE(mali_mmu_as_released, TP_PROTO(int event_id))
|
|
{
|
|
record_timeline_event(MMU_AS_0 + event_id, ACTIVITY_STOP);
|
|
}
|
|
|
|
GATOR_DEFINE_PROBE(mali_total_alloc_pages_change, TP_PROTO(long long int event_id))
|
|
{
|
|
accumulators_data[TOTAL_ALLOC_PAGES] = event_id;
|
|
}
|
|
|
|
static int create_files(struct super_block *sb, struct dentry *root)
|
|
{
|
|
int event;
|
|
/*
|
|
* Create the filesystem for all events
|
|
*/
|
|
int counter_index = 0;
|
|
mali_profiling_control_type *mali_control;
|
|
|
|
for (event = FIRST_TIMELINE_EVENT; event < FIRST_TIMELINE_EVENT + NUMBER_OF_TIMELINE_EVENTS; event++) {
|
|
if (gator_mali_create_file_system(mali_name, timeline_event_names[counter_index], sb, root, &counters[event], NULL) != 0)
|
|
return -1;
|
|
counter_index++;
|
|
}
|
|
counter_index = 0;
|
|
for (event = FIRST_SOFTWARE_COUNTER; event < FIRST_SOFTWARE_COUNTER + NUMBER_OF_SOFTWARE_COUNTERS; event++) {
|
|
if (gator_mali_create_file_system(mali_name, software_counter_names[counter_index], sb, root, &counters[event], NULL) != 0)
|
|
return -1;
|
|
counter_index++;
|
|
}
|
|
counter_index = 0;
|
|
for (event = FIRST_ACCUMULATOR; event < FIRST_ACCUMULATOR + NUMBER_OF_ACCUMULATORS; event++) {
|
|
if (gator_mali_create_file_system(mali_name, accumulators_names[counter_index], sb, root, &counters[event], NULL) != 0)
|
|
return -1;
|
|
counter_index++;
|
|
}
|
|
|
|
mali_control = symbol_get(_mali_profiling_control);
|
|
if (mali_control) {
|
|
if (gator_mali_create_file_system(mali_name, "Filmstrip_cnt0", sb, root, &counters[FILMSTRIP], &filmstrip_event) != 0)
|
|
return -1;
|
|
symbol_put(_mali_profiling_control);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int register_tracepoints(void)
|
|
{
|
|
if (GATOR_REGISTER_TRACE(mali_pm_status)) {
|
|
pr_err("gator: Mali-Midgard: mali_pm_status tracepoint failed to activate\n");
|
|
return 0;
|
|
}
|
|
|
|
if (GATOR_REGISTER_TRACE(mali_page_fault_insert_pages)) {
|
|
pr_err("gator: Mali-Midgard: mali_page_fault_insert_pages tracepoint failed to activate\n");
|
|
return 0;
|
|
}
|
|
|
|
if (GATOR_REGISTER_TRACE(mali_mmu_as_in_use)) {
|
|
pr_err("gator: Mali-Midgard: mali_mmu_as_in_use tracepoint failed to activate\n");
|
|
return 0;
|
|
}
|
|
|
|
if (GATOR_REGISTER_TRACE(mali_mmu_as_released)) {
|
|
pr_err("gator: Mali-Midgard: mali_mmu_as_released tracepoint failed to activate\n");
|
|
return 0;
|
|
}
|
|
|
|
if (GATOR_REGISTER_TRACE(mali_total_alloc_pages_change)) {
|
|
pr_err("gator: Mali-Midgard: mali_total_alloc_pages_change tracepoint failed to activate\n");
|
|
return 0;
|
|
}
|
|
|
|
pr_debug("gator: Mali-Midgard: start\n");
|
|
pr_debug("gator: Mali-Midgard: mali_pm_status probe is at %p\n", &probe_mali_pm_status);
|
|
pr_debug("gator: Mali-Midgard: mali_page_fault_insert_pages probe is at %p\n", &probe_mali_page_fault_insert_pages);
|
|
pr_debug("gator: Mali-Midgard: mali_mmu_as_in_use probe is at %p\n", &probe_mali_mmu_as_in_use);
|
|
pr_debug("gator: Mali-Midgard: mali_mmu_as_released probe is at %p\n", &probe_mali_mmu_as_released);
|
|
pr_debug("gator: Mali-Midgard: mali_total_alloc_pages_change probe is at %p\n", &probe_mali_total_alloc_pages_change);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int start(void)
|
|
{
|
|
unsigned int cnt;
|
|
mali_profiling_control_type *mali_control;
|
|
|
|
previous_shader_bitmask = 0;
|
|
previous_tiler_bitmask = 0;
|
|
previous_l2_bitmask = 0;
|
|
|
|
/* Clean all data for the next capture */
|
|
for (cnt = 0; cnt < NUMBER_OF_TIMELINE_EVENTS; cnt++) {
|
|
timeline_event_starttime[cnt].tv_sec = timeline_event_starttime[cnt].tv_nsec = 0;
|
|
timeline_data[cnt] = 0;
|
|
}
|
|
|
|
for (cnt = 0; cnt < NUMBER_OF_SOFTWARE_COUNTERS; cnt++)
|
|
sw_counter_data[cnt] = 0;
|
|
|
|
for (cnt = 0; cnt < NUMBER_OF_ACCUMULATORS; cnt++)
|
|
accumulators_data[cnt] = 0;
|
|
|
|
/* Register tracepoints */
|
|
if (register_tracepoints() == 0)
|
|
return -1;
|
|
|
|
/* Generic control interface for Mali DDK. */
|
|
mali_control = symbol_get(_mali_profiling_control);
|
|
if (mali_control) {
|
|
/* The event attribute in the XML file keeps the actual frame rate. */
|
|
unsigned int enabled = counters[FILMSTRIP].enabled ? 1 : 0;
|
|
unsigned int rate = filmstrip_event & 0xff;
|
|
unsigned int resize_factor = (filmstrip_event >> 8) & 0xff;
|
|
|
|
pr_debug("gator: mali online _mali_profiling_control symbol @ %p\n", mali_control);
|
|
|
|
#define FBDUMP_CONTROL_ENABLE (1)
|
|
#define FBDUMP_CONTROL_RATE (2)
|
|
#define FBDUMP_CONTROL_RESIZE_FACTOR (4)
|
|
mali_control(FBDUMP_CONTROL_ENABLE, enabled);
|
|
mali_control(FBDUMP_CONTROL_RATE, rate);
|
|
mali_control(FBDUMP_CONTROL_RESIZE_FACTOR, resize_factor);
|
|
|
|
pr_debug("gator: sent mali_control enabled=%d, rate=%d, resize_factor=%d\n", enabled, rate, resize_factor);
|
|
|
|
symbol_put(_mali_profiling_control);
|
|
} else {
|
|
pr_err("gator: mali online _mali_profiling_control symbol not found\n");
|
|
}
|
|
|
|
/*
|
|
* Set the first timestamp for calculating the sample interval. The first interval could be quite long,
|
|
* since it will be the time between 'start' and the first 'read'.
|
|
* This means that timeline values will be divided by a big number for the first sample.
|
|
*/
|
|
getnstimeofday(&prev_timestamp);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void stop(void)
|
|
{
|
|
mali_profiling_control_type *mali_control;
|
|
|
|
pr_debug("gator: Mali-Midgard: stop\n");
|
|
|
|
/*
|
|
* It is safe to unregister traces even if they were not successfully
|
|
* registered, so no need to check.
|
|
*/
|
|
GATOR_UNREGISTER_TRACE(mali_pm_status);
|
|
pr_debug("gator: Mali-Midgard: mali_pm_status tracepoint deactivated\n");
|
|
|
|
GATOR_UNREGISTER_TRACE(mali_page_fault_insert_pages);
|
|
pr_debug("gator: Mali-Midgard: mali_page_fault_insert_pages tracepoint deactivated\n");
|
|
|
|
GATOR_UNREGISTER_TRACE(mali_mmu_as_in_use);
|
|
pr_debug("gator: Mali-Midgard: mali_mmu_as_in_use tracepoint deactivated\n");
|
|
|
|
GATOR_UNREGISTER_TRACE(mali_mmu_as_released);
|
|
pr_debug("gator: Mali-Midgard: mali_mmu_as_released tracepoint deactivated\n");
|
|
|
|
GATOR_UNREGISTER_TRACE(mali_total_alloc_pages_change);
|
|
pr_debug("gator: Mali-Midgard: mali_total_alloc_pages_change tracepoint deactivated\n");
|
|
|
|
/* Generic control interface for Mali DDK. */
|
|
mali_control = symbol_get(_mali_profiling_control);
|
|
if (mali_control) {
|
|
pr_debug("gator: mali offline _mali_profiling_control symbol @ %p\n", mali_control);
|
|
|
|
mali_control(FBDUMP_CONTROL_ENABLE, 0);
|
|
|
|
symbol_put(_mali_profiling_control);
|
|
} else {
|
|
pr_err("gator: mali offline _mali_profiling_control symbol not found\n");
|
|
}
|
|
}
|
|
|
|
static int read(int **buffer, bool sched_switch)
|
|
{
|
|
int cnt;
|
|
int len = 0;
|
|
long sample_interval_us = 0;
|
|
struct timespec read_timestamp;
|
|
|
|
if (!on_primary_core())
|
|
return 0;
|
|
|
|
/* Get the start of this sample period. */
|
|
getnstimeofday(&read_timestamp);
|
|
|
|
/*
|
|
* Calculate the sample interval if the previous sample time is valid.
|
|
* We use tv_sec since it will not be 0.
|
|
*/
|
|
if (prev_timestamp.tv_sec != 0)
|
|
sample_interval_us = get_duration_us(&prev_timestamp, &read_timestamp);
|
|
|
|
/* Structure copy. Update the previous timestamp. */
|
|
prev_timestamp = read_timestamp;
|
|
|
|
/*
|
|
* Report the timeline counters (ACTIVITY_START/STOP)
|
|
*/
|
|
for (cnt = FIRST_TIMELINE_EVENT; cnt < (FIRST_TIMELINE_EVENT + NUMBER_OF_TIMELINE_EVENTS); cnt++) {
|
|
struct mali_counter *counter = &counters[cnt];
|
|
|
|
if (counter->enabled) {
|
|
const int index = cnt - FIRST_TIMELINE_EVENT;
|
|
unsigned int value;
|
|
|
|
/* If the activity is still running, reset its start time to the
|
|
* start of this sample period to correct the count. Add the
|
|
* time up to the end of the sample onto the count.
|
|
*/
|
|
if (timeline_event_starttime[index].tv_sec != 0) {
|
|
const long event_duration = get_duration_us(&timeline_event_starttime[index], &read_timestamp);
|
|
|
|
timeline_data[index] += event_duration;
|
|
timeline_event_starttime[index] = read_timestamp; /* Activity is still running. */
|
|
}
|
|
|
|
if (sample_interval_us != 0) {
|
|
/* Convert the counter to a percent-of-sample value */
|
|
value = (timeline_data[index] * 100) / sample_interval_us;
|
|
} else {
|
|
pr_debug("gator: Mali-Midgard: setting value to zero\n");
|
|
value = 0;
|
|
}
|
|
|
|
/* Clear the counter value ready for the next sample. */
|
|
timeline_data[index] = 0;
|
|
|
|
counter_dump[len++] = counter->key;
|
|
counter_dump[len++] = value;
|
|
}
|
|
}
|
|
|
|
/* Report the software counters */
|
|
for (cnt = FIRST_SOFTWARE_COUNTER; cnt < (FIRST_SOFTWARE_COUNTER + NUMBER_OF_SOFTWARE_COUNTERS); cnt++) {
|
|
const struct mali_counter *counter = &counters[cnt];
|
|
|
|
if (counter->enabled) {
|
|
const int index = cnt - FIRST_SOFTWARE_COUNTER;
|
|
|
|
counter_dump[len++] = counter->key;
|
|
counter_dump[len++] = sw_counter_data[index];
|
|
/* Set the value to zero for the next time */
|
|
sw_counter_data[index] = 0;
|
|
}
|
|
}
|
|
|
|
/* Report the accumulators */
|
|
for (cnt = FIRST_ACCUMULATOR; cnt < (FIRST_ACCUMULATOR + NUMBER_OF_ACCUMULATORS); cnt++) {
|
|
const struct mali_counter *counter = &counters[cnt];
|
|
|
|
if (counter->enabled) {
|
|
const int index = cnt - FIRST_ACCUMULATOR;
|
|
|
|
counter_dump[len++] = counter->key;
|
|
counter_dump[len++] = accumulators_data[index];
|
|
/* Do not zero the accumulator */
|
|
}
|
|
}
|
|
|
|
/* Update the buffer */
|
|
if (buffer)
|
|
*buffer = counter_dump;
|
|
|
|
return len;
|
|
}
|
|
|
|
static struct gator_interface gator_events_mali_midgard_interface = {
|
|
.name = "mali_midgard",
|
|
.create_files = create_files,
|
|
.start = start,
|
|
.stop = stop,
|
|
.read = read
|
|
};
|
|
|
|
extern int gator_events_mali_midgard_init(void)
|
|
{
|
|
pr_debug("gator: Mali-Midgard: sw_counters init\n");
|
|
|
|
gator_mali_initialise_counters(counters, NUMBER_OF_EVENTS);
|
|
|
|
return gator_events_install(&gator_events_mali_midgard_interface);
|
|
}
|