/** * Copyright (C) ARM Limited 2010-2014. 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 #include #include #include #ifdef MALI_SUPPORT #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 #endif /* * Taken from MALI_PROFILING_EVENT_TYPE_* items in Mali DDK. */ #define EVENT_TYPE_SINGLE 0 #define EVENT_TYPE_START 1 #define EVENT_TYPE_STOP 2 #define EVENT_TYPE_SUSPEND 3 #define EVENT_TYPE_RESUME 4 /* Note whether tracepoints have been registered */ static int mali_timeline_trace_registered; static int mali_job_slots_trace_registered; enum { GPU_UNIT_NONE = 0, GPU_UNIT_VP, GPU_UNIT_FP, GPU_UNIT_CL, NUMBER_OF_GPU_UNITS }; #if defined(MALI_SUPPORT) struct mali_activity { int core; int key; int count; int last_activity; int last_pid; }; #define NUMBER_OF_GPU_CORES 16 static struct mali_activity mali_activities[NUMBER_OF_GPU_UNITS*NUMBER_OF_GPU_CORES]; static DEFINE_SPINLOCK(mali_activities_lock); /* Only one event should be running on a unit and core at a time (ie, a start * event can only be followed by a stop and vice versa), but because the kernel * only knows when a job is enqueued and not started, it is possible for a * start1, start2, stop1, stop2. Change it back into start1, stop1, start2, * stop2 by queueing up start2 and releasing it when stop1 is received. */ static int mali_activity_index(int core, int key) { int i; for (i = 0; i < ARRAY_SIZE(mali_activities); ++i) { if ((mali_activities[i].core == core) && (mali_activities[i].key == key)) { break; } if ((mali_activities[i].core == 0) && (mali_activities[i].key == 0)) { mali_activities[i].core = core; mali_activities[i].key = key; break; } } BUG_ON(i >= ARRAY_SIZE(mali_activities)); return i; } static void mali_activity_enqueue(int core, int key, int activity, int pid) { int i; int count; spin_lock(&mali_activities_lock); i = mali_activity_index(core, key); count = mali_activities[i].count; BUG_ON(count < 0); ++mali_activities[i].count; if (count) { mali_activities[i].last_activity = activity; mali_activities[i].last_pid = pid; } spin_unlock(&mali_activities_lock); if (!count) { gator_marshal_activity_switch(core, key, activity, pid); } } static void mali_activity_stop(int core, int key) { int i; int count; int last_activity = 0; int last_pid = 0; spin_lock(&mali_activities_lock); i = mali_activity_index(core, key); if (mali_activities[i].count == 0) { spin_unlock(&mali_activities_lock); return; } --mali_activities[i].count; count = mali_activities[i].count; if (count) { last_activity = mali_activities[i].last_activity; last_pid = mali_activities[i].last_pid; } spin_unlock(&mali_activities_lock); gator_marshal_activity_switch(core, key, 0, 0); if (count) { gator_marshal_activity_switch(core, key, last_activity, last_pid); } } void mali_activity_clear(mali_counter mali_activity[], size_t mali_activity_size) { int activity; int cores; int core; for (activity = 0; activity < mali_activity_size; ++activity) { cores = mali_activity[activity].cores; if (cores < 0) { cores = 1; } for (core = 0; core < cores; ++core) { if (mali_activity[activity].enabled) { gator_marshal_activity_switch(core, mali_activity[activity].key, 0, 0); } } } } #endif #if defined(MALI_SUPPORT) && (MALI_SUPPORT != MALI_T6xx) #include "gator_events_mali_4xx.h" /* * Taken from MALI_PROFILING_EVENT_CHANNEL_* in Mali DDK. */ enum { EVENT_CHANNEL_SOFTWARE = 0, EVENT_CHANNEL_VP0 = 1, EVENT_CHANNEL_FP0 = 5, EVENT_CHANNEL_FP1, EVENT_CHANNEL_FP2, EVENT_CHANNEL_FP3, EVENT_CHANNEL_FP4, EVENT_CHANNEL_FP5, EVENT_CHANNEL_FP6, EVENT_CHANNEL_FP7, EVENT_CHANNEL_GPU = 21 }; /** * These events are applicable when the type MALI_PROFILING_EVENT_TYPE_SINGLE is used from the GPU channel */ enum { EVENT_REASON_SINGLE_GPU_NONE = 0, EVENT_REASON_SINGLE_GPU_FREQ_VOLT_CHANGE = 1, }; mali_counter mali_activity[2]; GATOR_DEFINE_PROBE(mali_timeline_event, TP_PROTO(unsigned int event_id, unsigned int d0, unsigned int d1, unsigned int d2, unsigned int d3, unsigned int d4)) { unsigned int component, state; // do as much work as possible before disabling interrupts component = (event_id >> 16) & 0xFF; // component is an 8-bit field state = (event_id >> 24) & 0xF; // state is a 4-bit field switch (state) { case EVENT_TYPE_START: if (component == EVENT_CHANNEL_VP0) { /* tgid = d0; pid = d1; */ if (mali_activity[1].enabled) { mali_activity_enqueue(0, mali_activity[1].key, 1, d1); } } else if (component >= EVENT_CHANNEL_FP0 && component <= EVENT_CHANNEL_FP7) { /* tgid = d0; pid = d1; */ if (mali_activity[0].enabled) { mali_activity_enqueue(component - EVENT_CHANNEL_FP0, mali_activity[0].key, 1, d1); } } break; case EVENT_TYPE_STOP: if (component == EVENT_CHANNEL_VP0) { if (mali_activity[1].enabled) { mali_activity_stop(0, mali_activity[1].key); } } else if (component >= EVENT_CHANNEL_FP0 && component <= EVENT_CHANNEL_FP7) { if (mali_activity[0].enabled) { mali_activity_stop(component - EVENT_CHANNEL_FP0, mali_activity[0].key); } } break; case EVENT_TYPE_SINGLE: if (component == EVENT_CHANNEL_GPU) { unsigned int reason = (event_id & 0xffff); if (reason == EVENT_REASON_SINGLE_GPU_FREQ_VOLT_CHANGE) { gator_events_mali_log_dvfs_event(d0, d1); } } break; default: break; } } #endif #if defined(MALI_SUPPORT) && (MALI_SUPPORT == MALI_T6xx) mali_counter mali_activity[3]; #if defined(MALI_JOB_SLOTS_EVENT_CHANGED) GATOR_DEFINE_PROBE(mali_job_slots_event, TP_PROTO(unsigned int event_id, unsigned int tgid, unsigned int pid, unsigned char job_id)) #else GATOR_DEFINE_PROBE(mali_job_slots_event, TP_PROTO(unsigned int event_id, unsigned int tgid, unsigned int pid)) #endif { unsigned int component, state, unit; #if !defined(MALI_JOB_SLOTS_EVENT_CHANGED) unsigned char job_id = 0; #endif component = (event_id >> 16) & 0xFF; // component is an 8-bit field state = (event_id >> 24) & 0xF; // state is a 4-bit field switch (component) { case 0: unit = GPU_UNIT_FP; break; case 1: unit = GPU_UNIT_VP; break; case 2: unit = GPU_UNIT_CL; break; default: unit = GPU_UNIT_NONE; } if (unit != GPU_UNIT_NONE) { switch (state) { case EVENT_TYPE_START: if (mali_activity[component].enabled) { mali_activity_enqueue(0, mali_activity[component].key, 1, (pid != 0 ? pid : tgid)); } break; case EVENT_TYPE_STOP: default: // Some jobs can be soft-stopped, so ensure that this terminates the activity trace. if (mali_activity[component].enabled) { mali_activity_stop(0, mali_activity[component].key); } break; } } } #endif static int gator_trace_gpu_start(void) { /* * Returns nonzero for installation failed * Absence of gpu trace points is not an error */ #if defined(MALI_SUPPORT) memset(&mali_activities, 0, sizeof(mali_activities)); #endif mali_timeline_trace_registered = mali_job_slots_trace_registered = 0; #if defined(MALI_SUPPORT) && (MALI_SUPPORT != MALI_T6xx) mali_activity_clear(mali_activity, ARRAY_SIZE(mali_activity)); if (!GATOR_REGISTER_TRACE(mali_timeline_event)) { mali_timeline_trace_registered = 1; } #endif #if defined(MALI_SUPPORT) && (MALI_SUPPORT == MALI_T6xx) mali_activity_clear(mali_activity, ARRAY_SIZE(mali_activity)); if (!GATOR_REGISTER_TRACE(mali_job_slots_event)) { mali_job_slots_trace_registered = 1; } #endif return 0; } static void gator_trace_gpu_stop(void) { #if defined(MALI_SUPPORT) && (MALI_SUPPORT != MALI_T6xx) if (mali_timeline_trace_registered) { GATOR_UNREGISTER_TRACE(mali_timeline_event); } #endif #if defined(MALI_SUPPORT) && (MALI_SUPPORT == MALI_T6xx) if (mali_job_slots_trace_registered) { GATOR_UNREGISTER_TRACE(mali_job_slots_event); } #endif mali_timeline_trace_registered = mali_job_slots_trace_registered = 0; }