| /** |
| * Copyright (C) ARM Limited 2010-2013. 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/mali_linux_trace.h" |
| |
| #include "gator_events_mali_common.h" |
| #include "gator_events_mali_4xx.h" |
| |
| /* |
| * There are (currently) four different variants of the comms between gator and Mali: |
| * 1 (deprecated): No software counter support |
| * 2 (deprecated): Tracepoint called for each separate s/w counter value as it appears |
| * 3 (default): Single tracepoint for all s/w counters in a bundle. |
| * Interface style 3 is the default if no other is specified. 1 and 2 will be eliminated when |
| * existing Mali DDKs are upgraded. |
| * 4. As above, but for the Utgard (Mali-450) driver. |
| */ |
| |
| #if !defined(GATOR_MALI_INTERFACE_STYLE) |
| #define GATOR_MALI_INTERFACE_STYLE (3) |
| #endif |
| |
| #if GATOR_MALI_INTERFACE_STYLE < 4 |
| #include "mali/mali_mjollnir_profiling_gator_api.h" |
| #else |
| #include "mali/mali_utgard_profiling_gator_api.h" |
| #endif |
| |
| /* |
| * Check that the MALI_SUPPORT define is set to one of the allowable device codes. |
| */ |
| #if (MALI_SUPPORT != MALI_4xx) |
| #error MALI_SUPPORT set to an invalid device code: expecting MALI_4xx |
| #endif |
| |
| /* gatorfs variables for counter enable state, |
| * the event the counter should count and the |
| * 'key' (a unique id set by gatord and returned |
| * by gator.ko) |
| */ |
| static unsigned long counter_enabled[NUMBER_OF_EVENTS]; |
| static unsigned long counter_event[NUMBER_OF_EVENTS]; |
| static unsigned long counter_key[NUMBER_OF_EVENTS]; |
| |
| /* The data we have recorded */ |
| static u32 counter_data[NUMBER_OF_EVENTS]; |
| /* The address to sample (or 0 if samples are sent to us) */ |
| static u32 *counter_address[NUMBER_OF_EVENTS]; |
| |
| /* An array used to return the data we recorded |
| * as key,value pairs hence the *2 |
| */ |
| static unsigned long counter_dump[NUMBER_OF_EVENTS * 2]; |
| static unsigned long counter_prev[NUMBER_OF_EVENTS]; |
| |
| /* Note whether tracepoints have been registered */ |
| static int trace_registered; |
| |
| /* |
| * These numbers define the actual numbers of each block type that exist in the system. Initially |
| * these are set to the maxima defined above; if the driver is capable of being queried (newer |
| * drivers only) then the values may be revised. |
| */ |
| static unsigned int n_vp_cores = MAX_NUM_VP_CORES; |
| static unsigned int n_l2_cores = MAX_NUM_L2_CACHE_CORES; |
| static unsigned int n_fp_cores = MAX_NUM_FP_CORES; |
| |
| /** |
| * Calculate the difference and handle the overflow. |
| */ |
| static u32 get_difference(u32 start, u32 end) |
| { |
| if (start - end >= 0) { |
| return start - end; |
| } |
| |
| // Mali counters are unsigned 32 bit values that wrap. |
| return (4294967295u - end) + start; |
| } |
| |
| /** |
| * Returns non-zero if the given counter ID is an activity counter. |
| */ |
| static inline int is_activity_counter(unsigned int event_id) |
| { |
| return (event_id >= FIRST_ACTIVITY_EVENT && |
| event_id <= LAST_ACTIVITY_EVENT); |
| } |
| |
| /** |
| * Returns non-zero if the given counter ID is a hardware counter. |
| */ |
| static inline int is_hw_counter(unsigned int event_id) |
| { |
| return (event_id >= FIRST_HW_COUNTER && event_id <= LAST_HW_COUNTER); |
| } |
| |
| /* |
| * These are provided for utgard compatibility. |
| */ |
| typedef void _mali_profiling_get_mali_version_type(struct _mali_profiling_mali_version *values); |
| typedef u32 _mali_profiling_get_l2_counters_type(_mali_profiling_l2_counter_values *values); |
| |
| #if GATOR_MALI_INTERFACE_STYLE == 2 |
| /** |
| * Returns non-zero if the given counter ID is a software counter. |
| */ |
| static inline int is_sw_counter(unsigned int event_id) |
| { |
| return (event_id >= FIRST_SW_COUNTER && event_id <= LAST_SW_COUNTER); |
| } |
| #endif |
| |
| #if GATOR_MALI_INTERFACE_STYLE == 2 |
| /* |
| * The Mali DDK uses s64 types to contain software counter values, but gator |
| * can only use a maximum of 32 bits. This function scales a software counter |
| * to an appropriate range. |
| */ |
| static u32 scale_sw_counter_value(unsigned int event_id, signed long long value) |
| { |
| u32 scaled_value; |
| |
| switch (event_id) { |
| case COUNTER_GLES_UPLOAD_TEXTURE_TIME: |
| case COUNTER_GLES_UPLOAD_VBO_TIME: |
| scaled_value = (u32)div_s64(value, 1000000); |
| break; |
| default: |
| scaled_value = (u32)value; |
| break; |
| } |
| |
| return scaled_value; |
| } |
| #endif |
| |
| /* Probe for continuously sampled counter */ |
| #if 0 //WE_DONT_CURRENTLY_USE_THIS_SO_SUPPRESS_WARNING |
| GATOR_DEFINE_PROBE(mali_sample_address, TP_PROTO(unsigned int event_id, u32 *addr)) |
| { |
| /* Turning on too many pr_debug statements in frequently called functions |
| * can cause stability and/or performance problems |
| */ |
| //pr_debug("gator: mali_sample_address %d %d\n", event_id, addr); |
| if (event_id >= ACTIVITY_VP && event_id <= COUNTER_FP3_C1) { |
| counter_address[event_id] = addr; |
| } |
| } |
| #endif |
| |
| /* Probe for hardware counter events */ |
| GATOR_DEFINE_PROBE(mali_hw_counter, TP_PROTO(unsigned int event_id, unsigned int value)) |
| { |
| /* Turning on too many pr_debug statements in frequently called functions |
| * can cause stability and/or performance problems |
| */ |
| //pr_debug("gator: mali_hw_counter %d %d\n", event_id, value); |
| if (is_hw_counter(event_id)) { |
| counter_data[event_id] = value; |
| } |
| } |
| |
| #if GATOR_MALI_INTERFACE_STYLE == 2 |
| GATOR_DEFINE_PROBE(mali_sw_counter, TP_PROTO(unsigned int event_id, signed long long value)) |
| { |
| if (is_sw_counter(event_id)) { |
| counter_data[event_id] = scale_sw_counter_value(event_id, value); |
| } |
| } |
| #endif /* GATOR_MALI_INTERFACE_STYLE == 2 */ |
| |
| #if GATOR_MALI_INTERFACE_STYLE >= 3 |
| GATOR_DEFINE_PROBE(mali_sw_counters, TP_PROTO(pid_t pid, pid_t tid, void *surface_id, unsigned int *counters)) |
| { |
| u32 i; |
| |
| /* Copy over the values for those counters which are enabled. */ |
| for (i = FIRST_SW_COUNTER; i <= LAST_SW_COUNTER; i++) { |
| if (counter_enabled[i]) { |
| counter_data[i] = (u32)(counters[i - FIRST_SW_COUNTER]); |
| } |
| } |
| } |
| #endif /* GATOR_MALI_INTERFACE_STYLE >= 3 */ |
| |
| /** |
| * Create a single filesystem entry for a specified event. |
| * @param sb the superblock |
| * @param root Filesystem root |
| * @param name The name of the entry to create |
| * @param event The ID of the event |
| * @param create_event_item boolean indicating whether to create an 'event' filesystem entry. True to create. |
| * |
| * @return 0 if ok, non-zero if the create failed. |
| */ |
| static int create_fs_entry(struct super_block *sb, struct dentry *root, const char *name, int event, int create_event_item) |
| { |
| struct dentry *dir; |
| |
| dir = gatorfs_mkdir(sb, root, name); |
| |
| if (!dir) { |
| return -1; |
| } |
| |
| if (create_event_item) { |
| gatorfs_create_ulong(sb, dir, "event", &counter_event[event]); |
| } |
| |
| gatorfs_create_ulong(sb, dir, "enabled", &counter_enabled[event]); |
| gatorfs_create_ro_ulong(sb, dir, "key", &counter_key[event]); |
| |
| return 0; |
| } |
| |
| #if GATOR_MALI_INTERFACE_STYLE > 3 |
| /* |
| * Read the version info structure if available |
| */ |
| static void initialise_version_info(void) |
| { |
| _mali_profiling_get_mali_version_type *mali_profiling_get_mali_version_symbol; |
| |
| mali_profiling_get_mali_version_symbol = symbol_get(_mali_profiling_get_mali_version); |
| |
| if (mali_profiling_get_mali_version_symbol) { |
| struct _mali_profiling_mali_version version_info; |
| |
| pr_debug("gator: mali online _mali_profiling_get_mali_version symbol @ %p\n", |
| mali_profiling_get_mali_version_symbol); |
| |
| /* |
| * Revise the number of each different core type using information derived from the DDK. |
| */ |
| mali_profiling_get_mali_version_symbol(&version_info); |
| |
| n_fp_cores = version_info.num_of_fp_cores; |
| n_vp_cores = version_info.num_of_vp_cores; |
| n_l2_cores = version_info.num_of_l2_cores; |
| |
| /* Release the function - we're done with it. */ |
| symbol_put(_mali_profiling_get_mali_version); |
| } else { |
| printk("gator: mali online _mali_profiling_get_mali_version symbol not found\n"); |
| } |
| } |
| #endif |
| |
| static int create_files(struct super_block *sb, struct dentry *root) |
| { |
| int event; |
| const char *mali_name = gator_mali_get_mali_name(); |
| |
| char buf[40]; |
| int core_id; |
| int counter_number; |
| |
| pr_debug("gator: Initialising counters with style = %d\n", GATOR_MALI_INTERFACE_STYLE); |
| |
| #if GATOR_MALI_INTERFACE_STYLE > 3 |
| /* |
| * Initialise first: this sets up the number of cores available (on compatible DDK versions). |
| * Ideally this would not need guarding but other parts of the code depend on the interface style being set |
| * correctly; if it is not then the system can enter an inconsistent state. |
| */ |
| initialise_version_info(); |
| #endif |
| |
| /* Vertex processor counters */ |
| for (core_id = 0; core_id < n_vp_cores; core_id++) { |
| int activity_counter_id = ACTIVITY_VP_0; |
| snprintf(buf, sizeof buf, "ARM_%s_VP_%d_active", mali_name, core_id); |
| if (create_fs_entry(sb, root, buf, activity_counter_id, 0) != 0) { |
| return -1; |
| } |
| |
| for (counter_number = 0; counter_number < 2; counter_number++) { |
| int counter_id = COUNTER_VP_0_C0 + (2 * core_id) + counter_number; |
| |
| snprintf(buf, sizeof buf, "ARM_%s_VP_%d_cnt%d", mali_name, core_id, counter_number); |
| if (create_fs_entry(sb, root, buf, counter_id, 1) != 0) { |
| return -1; |
| } |
| } |
| } |
| |
| /* Fragment processors' counters */ |
| for (core_id = 0; core_id < n_fp_cores; core_id++) { |
| int activity_counter_id = ACTIVITY_FP_0 + core_id; |
| |
| snprintf(buf, sizeof buf, "ARM_%s_FP_%d_active", mali_name, core_id); |
| if (create_fs_entry(sb, root, buf, activity_counter_id, 0) != 0) { |
| return -1; |
| } |
| |
| for (counter_number = 0; counter_number < 2; counter_number++) { |
| int counter_id = COUNTER_FP_0_C0 + (2 * core_id) + counter_number; |
| |
| snprintf(buf, sizeof buf, "ARM_%s_FP_%d_cnt%d", mali_name, core_id, counter_number); |
| if (create_fs_entry(sb, root, buf, counter_id, 1) != 0) { |
| return -1; |
| } |
| } |
| } |
| |
| /* L2 Cache counters */ |
| for (core_id = 0; core_id < n_l2_cores; core_id++) { |
| for (counter_number = 0; counter_number < 2; counter_number++) { |
| int counter_id = COUNTER_L2_0_C0 + (2 * core_id) + counter_number; |
| |
| snprintf(buf, sizeof buf, "ARM_%s_L2_%d_cnt%d", mali_name, core_id, counter_number); |
| if (create_fs_entry(sb, root, buf, counter_id, 1) != 0) { |
| return -1; |
| } |
| } |
| } |
| |
| /* Now set up the software counter entries */ |
| for (event = FIRST_SW_COUNTER; event <= LAST_SW_COUNTER; event++) { |
| snprintf(buf, sizeof(buf), "ARM_%s_SW_%d", mali_name, event - FIRST_SW_COUNTER); |
| |
| if (create_fs_entry(sb, root, buf, event, 0) != 0) { |
| return -1; |
| } |
| } |
| |
| /* Now set up the special counter entries */ |
| snprintf(buf, sizeof(buf), "ARM_%s_Filmstrip_cnt0", mali_name); |
| if (create_fs_entry(sb, root, buf, COUNTER_FILMSTRIP, 1) != 0) { |
| return -1; |
| } |
| |
| #ifdef DVFS_REPORTED_BY_DDK |
| snprintf(buf, sizeof(buf), "ARM_%s_Frequency", mali_name); |
| if (create_fs_entry(sb, root, buf, COUNTER_FREQUENCY, 1) != 0) { |
| return -1; |
| } |
| |
| snprintf(buf, sizeof(buf), "ARM_%s_Voltage", mali_name); |
| if (create_fs_entry(sb, root, buf, COUNTER_VOLTAGE, 1) != 0) { |
| return -1; |
| } |
| #endif |
| |
| return 0; |
| } |
| |
| /* |
| * Local store for the get_counters entry point into the DDK. |
| * This is stored here since it is used very regularly. |
| */ |
| static mali_profiling_get_counters_type *mali_get_counters = NULL; |
| static _mali_profiling_get_l2_counters_type *mali_get_l2_counters = NULL; |
| |
| /* |
| * Examine list of counters between two index limits and determine if any one is enabled. |
| * Returns 1 if any counter is enabled, 0 if none is. |
| */ |
| static int is_any_counter_enabled(unsigned int first_counter, unsigned int last_counter) |
| { |
| unsigned int i; |
| |
| for (i = first_counter; i <= last_counter; i++) { |
| if (counter_enabled[i]) { |
| return 1; /* At least one counter is enabled */ |
| } |
| } |
| |
| return 0; /* No s/w counters enabled */ |
| } |
| |
| static void init_counters(unsigned int from_counter, unsigned int to_counter) |
| { |
| unsigned int counter_id; |
| |
| /* If a Mali driver is present and exporting the appropriate symbol |
| * then we can request the HW counters (of which there are only 2) |
| * be configured to count the desired events |
| */ |
| mali_profiling_set_event_type *mali_set_hw_event; |
| |
| mali_set_hw_event = symbol_get(_mali_profiling_set_event); |
| |
| if (mali_set_hw_event) { |
| pr_debug("gator: mali online _mali_profiling_set_event symbol @ %p\n", mali_set_hw_event); |
| |
| for (counter_id = from_counter; counter_id <= to_counter; counter_id++) { |
| if (counter_enabled[counter_id]) { |
| mali_set_hw_event(counter_id, counter_event[counter_id]); |
| } else { |
| mali_set_hw_event(counter_id, 0xFFFFFFFF); |
| } |
| } |
| |
| symbol_put(_mali_profiling_set_event); |
| } else { |
| printk("gator: mali online _mali_profiling_set_event symbol not found\n"); |
| } |
| } |
| |
| static void mali_counter_initialize(void) |
| { |
| int i; |
| int core_id; |
| |
| mali_osk_fb_control_set_type *mali_set_fb_event; |
| mali_profiling_control_type *mali_control; |
| |
| init_counters(COUNTER_L2_0_C0, COUNTER_L2_0_C0 + (2 * n_l2_cores) - 1); |
| init_counters(COUNTER_VP_0_C0, COUNTER_VP_0_C0 + (2 * n_vp_cores) - 1); |
| init_counters(COUNTER_FP_0_C0, COUNTER_FP_0_C0 + (2 * n_fp_cores) - 1); |
| |
| mali_set_fb_event = symbol_get(_mali_osk_fb_control_set); |
| |
| if (mali_set_fb_event) { |
| pr_debug("gator: mali online _mali_osk_fb_control_set symbol @ %p\n", mali_set_fb_event); |
| |
| mali_set_fb_event(0, (counter_enabled[COUNTER_FILMSTRIP] ? 1 : 0)); |
| |
| symbol_put(_mali_osk_fb_control_set); |
| } else { |
| printk("gator: mali online _mali_osk_fb_control_set symbol not found\n"); |
| } |
| |
| /* 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 rate = counter_event[COUNTER_FILMSTRIP] & 0xff; |
| unsigned int resize_factor = (counter_event[COUNTER_FILMSTRIP] >> 8) & 0xff; |
| |
| pr_debug("gator: mali online _mali_profiling_control symbol @ %p\n", mali_control); |
| |
| mali_control(SW_COUNTER_ENABLE, (is_any_counter_enabled(FIRST_SW_COUNTER, LAST_SW_COUNTER) ? 1 : 0)); |
| mali_control(FBDUMP_CONTROL_ENABLE, (counter_enabled[COUNTER_FILMSTRIP] ? 1 : 0)); |
| mali_control(FBDUMP_CONTROL_RATE, rate); |
| mali_control(FBDUMP_CONTROL_RESIZE_FACTOR, resize_factor); |
| |
| pr_debug("gator: sent mali_control enabled=%d, rate=%d\n", (counter_enabled[COUNTER_FILMSTRIP] ? 1 : 0), rate); |
| |
| symbol_put(_mali_profiling_control); |
| } else { |
| printk("gator: mali online _mali_profiling_control symbol not found\n"); |
| } |
| |
| mali_get_counters = symbol_get(_mali_profiling_get_counters); |
| if (mali_get_counters) { |
| pr_debug("gator: mali online _mali_profiling_get_counters symbol @ %p\n", mali_get_counters); |
| |
| } else { |
| pr_debug("gator WARNING: mali _mali_profiling_get_counters symbol not defined"); |
| } |
| |
| mali_get_l2_counters = symbol_get(_mali_profiling_get_l2_counters); |
| if (mali_get_l2_counters) { |
| pr_debug("gator: mali online _mali_profiling_get_l2_counters symbol @ %p\n", mali_get_l2_counters); |
| |
| } else { |
| pr_debug("gator WARNING: mali _mali_profiling_get_l2_counters symbol not defined"); |
| } |
| |
| if (!mali_get_counters && !mali_get_l2_counters) { |
| pr_debug("gator: WARNING: no L2 counters available"); |
| n_l2_cores = 0; |
| } |
| |
| for (core_id = 0; core_id < n_l2_cores; core_id++) { |
| int counter_id = COUNTER_L2_0_C0 + (2 * core_id); |
| counter_prev[counter_id] = 0; |
| counter_prev[counter_id + 1] = 0; |
| } |
| |
| /* Clear counters in the start */ |
| for (i = 0; i < NUMBER_OF_EVENTS; i++) { |
| counter_data[i] = 0; |
| } |
| } |
| |
| static void mali_counter_deinitialize(void) |
| { |
| mali_profiling_set_event_type *mali_set_hw_event; |
| mali_osk_fb_control_set_type *mali_set_fb_event; |
| mali_profiling_control_type *mali_control; |
| |
| mali_set_hw_event = symbol_get(_mali_profiling_set_event); |
| |
| if (mali_set_hw_event) { |
| int i; |
| |
| pr_debug("gator: mali offline _mali_profiling_set_event symbol @ %p\n", mali_set_hw_event); |
| for (i = FIRST_HW_COUNTER; i <= LAST_HW_COUNTER; i++) { |
| mali_set_hw_event(i, 0xFFFFFFFF); |
| } |
| |
| symbol_put(_mali_profiling_set_event); |
| } else { |
| printk("gator: mali offline _mali_profiling_set_event symbol not found\n"); |
| } |
| |
| mali_set_fb_event = symbol_get(_mali_osk_fb_control_set); |
| |
| if (mali_set_fb_event) { |
| pr_debug("gator: mali offline _mali_osk_fb_control_set symbol @ %p\n", mali_set_fb_event); |
| |
| mali_set_fb_event(0, 0); |
| |
| symbol_put(_mali_osk_fb_control_set); |
| } else { |
| printk("gator: mali offline _mali_osk_fb_control_set symbol not found\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_set_fb_event); |
| |
| /* Reset the DDK state - disable counter collection */ |
| mali_control(SW_COUNTER_ENABLE, 0); |
| |
| mali_control(FBDUMP_CONTROL_ENABLE, 0); |
| |
| symbol_put(_mali_profiling_control); |
| } else { |
| printk("gator: mali offline _mali_profiling_control symbol not found\n"); |
| } |
| |
| if (mali_get_counters) { |
| symbol_put(_mali_profiling_get_counters); |
| } |
| |
| if (mali_get_l2_counters) { |
| symbol_put(_mali_profiling_get_l2_counters); |
| } |
| } |
| |
| static int start(void) |
| { |
| // register tracepoints |
| if (GATOR_REGISTER_TRACE(mali_hw_counter)) { |
| printk("gator: mali_hw_counter tracepoint failed to activate\n"); |
| return -1; |
| } |
| |
| #if GATOR_MALI_INTERFACE_STYLE == 1 |
| /* None. */ |
| #elif GATOR_MALI_INTERFACE_STYLE == 2 |
| /* For patched Mali driver. */ |
| if (GATOR_REGISTER_TRACE(mali_sw_counter)) { |
| printk("gator: mali_sw_counter tracepoint failed to activate\n"); |
| return -1; |
| } |
| #elif GATOR_MALI_INTERFACE_STYLE >= 3 |
| /* For Mali drivers with built-in support. */ |
| if (GATOR_REGISTER_TRACE(mali_sw_counters)) { |
| printk("gator: mali_sw_counters tracepoint failed to activate\n"); |
| return -1; |
| } |
| #else |
| #error Unknown GATOR_MALI_INTERFACE_STYLE option. |
| #endif |
| |
| trace_registered = 1; |
| |
| mali_counter_initialize(); |
| return 0; |
| } |
| |
| static void stop(void) |
| { |
| unsigned int cnt; |
| |
| pr_debug("gator: mali stop\n"); |
| |
| if (trace_registered) { |
| GATOR_UNREGISTER_TRACE(mali_hw_counter); |
| |
| #if GATOR_MALI_INTERFACE_STYLE == 1 |
| /* None. */ |
| #elif GATOR_MALI_INTERFACE_STYLE == 2 |
| /* For patched Mali driver. */ |
| GATOR_UNREGISTER_TRACE(mali_sw_counter); |
| #elif GATOR_MALI_INTERFACE_STYLE >= 3 |
| /* For Mali drivers with built-in support. */ |
| GATOR_UNREGISTER_TRACE(mali_sw_counters); |
| #else |
| #error Unknown GATOR_MALI_INTERFACE_STYLE option. |
| #endif |
| |
| pr_debug("gator: mali timeline tracepoint deactivated\n"); |
| |
| trace_registered = 0; |
| } |
| |
| for (cnt = 0; cnt < NUMBER_OF_EVENTS; cnt++) { |
| counter_enabled[cnt] = 0; |
| counter_event[cnt] = 0; |
| counter_address[cnt] = NULL; |
| } |
| |
| mali_counter_deinitialize(); |
| } |
| |
| static void dump_counters(unsigned int from_counter, unsigned int to_counter, unsigned int *len) |
| { |
| unsigned int counter_id; |
| |
| for (counter_id = from_counter; counter_id <= to_counter; counter_id++) { |
| if (counter_enabled[counter_id]) { |
| counter_dump[(*len)++] = counter_key[counter_id]; |
| counter_dump[(*len)++] = counter_data[counter_id]; |
| |
| counter_data[counter_id] = 0; |
| } |
| } |
| } |
| |
| static int read(int **buffer) |
| { |
| int len = 0; |
| |
| if (!on_primary_core()) |
| return 0; |
| |
| // Read the L2 C0 and C1 here. |
| if (n_l2_cores > 0 && is_any_counter_enabled(COUNTER_L2_0_C0, COUNTER_L2_0_C0 + (2 * n_l2_cores))) { |
| unsigned int unavailable_l2_caches = 0; |
| _mali_profiling_l2_counter_values cache_values; |
| unsigned int cache_id; |
| struct _mali_profiling_core_counters *per_core; |
| |
| /* Poke the driver to get the counter values - older style; only one L2 cache */ |
| if (mali_get_l2_counters) { |
| unavailable_l2_caches = mali_get_l2_counters(&cache_values); |
| } else if (mali_get_counters) { |
| per_core = &cache_values.cores[0]; |
| mali_get_counters(&per_core->source0, &per_core->value0, &per_core->source1, &per_core->value1); |
| } else { |
| /* This should never happen, as n_l2_caches is only set > 0 if one of the above functions is found. */ |
| } |
| |
| /* Fill in the two cache counter values for each cache block. */ |
| for (cache_id = 0; cache_id < n_l2_cores; cache_id++) { |
| unsigned int counter_id_0 = COUNTER_L2_0_C0 + (2 * cache_id); |
| unsigned int counter_id_1 = counter_id_0 + 1; |
| |
| if ((1 << cache_id) & unavailable_l2_caches) { |
| continue; /* This cache is unavailable (powered-off, possibly). */ |
| } |
| |
| per_core = &cache_values.cores[cache_id]; |
| |
| if (counter_enabled[counter_id_0]) { |
| // Calculate and save src0's counter val0 |
| counter_dump[len++] = counter_key[counter_id_0]; |
| counter_dump[len++] = get_difference(per_core->value0, counter_prev[counter_id_0]); |
| } |
| |
| if (counter_enabled[counter_id_1]) { |
| // Calculate and save src1's counter val1 |
| counter_dump[len++] = counter_key[counter_id_1]; |
| counter_dump[len++] = get_difference(per_core->value1, counter_prev[counter_id_1]); |
| } |
| |
| // Save the previous values for the counters. |
| counter_prev[counter_id_0] = per_core->value0; |
| counter_prev[counter_id_1] = per_core->value1; |
| } |
| } |
| |
| /* Process other (non-timeline) counters. */ |
| dump_counters(COUNTER_VP_0_C0, COUNTER_VP_0_C0 + (2 * n_vp_cores) - 1, &len); |
| dump_counters(COUNTER_FP_0_C0, COUNTER_FP_0_C0 + (2 * n_fp_cores) - 1, &len); |
| |
| dump_counters(FIRST_SW_COUNTER, LAST_SW_COUNTER, &len); |
| |
| #ifdef DVFS_REPORTED_BY_DDK |
| { |
| int cnt; |
| /* |
| * Add in the voltage and frequency counters if enabled. Note that, since these are |
| * actually passed as events, the counter value should not be cleared. |
| */ |
| cnt = COUNTER_FREQUENCY; |
| if (counter_enabled[cnt]) { |
| counter_dump[len++] = counter_key[cnt]; |
| counter_dump[len++] = counter_data[cnt]; |
| } |
| |
| cnt = COUNTER_VOLTAGE; |
| if (counter_enabled[cnt]) { |
| counter_dump[len++] = counter_key[cnt]; |
| counter_dump[len++] = counter_data[cnt]; |
| } |
| } |
| #endif |
| |
| if (buffer) { |
| *buffer = (int *)counter_dump; |
| } |
| |
| return len; |
| } |
| |
| static struct gator_interface gator_events_mali_interface = { |
| .create_files = create_files, |
| .start = start, |
| .stop = stop, |
| .read = read, |
| }; |
| |
| extern void gator_events_mali_log_dvfs_event(unsigned int frequency_mhz, unsigned int voltage_mv) |
| { |
| #ifdef DVFS_REPORTED_BY_DDK |
| counter_data[COUNTER_FREQUENCY] = frequency_mhz; |
| counter_data[COUNTER_VOLTAGE] = voltage_mv; |
| #endif |
| } |
| |
| int gator_events_mali_init(void) |
| { |
| unsigned int cnt; |
| |
| pr_debug("gator: mali init\n"); |
| |
| for (cnt = 0; cnt < NUMBER_OF_EVENTS; cnt++) { |
| counter_enabled[cnt] = 0; |
| counter_event[cnt] = 0; |
| counter_key[cnt] = gator_events_get_key(); |
| counter_address[cnt] = NULL; |
| counter_data[cnt] = 0; |
| } |
| |
| trace_registered = 0; |
| |
| return gator_events_install(&gator_events_mali_interface); |
| } |
| |
| gator_events_init(gator_events_mali_init); |