| // SPDX-License-Identifier: GPL-2.0 | 
 | /* | 
 |  * Copyright (c) 2019 MediaTek Inc. | 
 |  */ | 
 |  | 
 | #include <asm/page.h> | 
 | #include "interface.h" | 
 | #include "met_drv.h" | 
 |  | 
 | #if	IS_ENABLED(CONFIG_GPU_TRACEPOINTS) | 
 | #include <trace/events/gpu.h> | 
 |  | 
 | #define show_secs_from_ns(ns) \ | 
 | 	({ \ | 
 | 		u64 t = ns + (NSEC_PER_USEC / 2); \ | 
 | 		do_div(t, NSEC_PER_SEC); \ | 
 | 		t; \ | 
 | 	}) | 
 |  | 
 | #define show_usecs_from_ns(ns) \ | 
 | 	({ \ | 
 | 		u64 t = ns + (NSEC_PER_USEC / 2) ; \ | 
 | 		u32 rem; \ | 
 | 		do_div(t, NSEC_PER_USEC); \ | 
 | 		rem = do_div(t, USEC_PER_SEC); \ | 
 | 	}) | 
 |  | 
 | static int event_gpu_registered; | 
 | static int event_gpu_enabled; | 
 |  | 
 | noinline void gpu_sched_switch(const char *gpu_name, u64 timestamp, | 
 | 				      u32 next_ctx_id, s32 next_prio, u32 next_job_id) | 
 | { | 
 | 	MET_TRACE("gpu_name=%s ts=%llu.%06lu next_ctx_id=%lu next_prio=%ld next_job_id=%lu\n", | 
 | 		   gpu_name, | 
 | 		   (unsigned long long)show_secs_from_ns(timestamp), | 
 | 		   (unsigned long)show_usecs_from_ns(timestamp), | 
 | 		   (unsigned long)next_ctx_id, (long)next_prio, (unsigned long)next_job_id); | 
 | } | 
 |  | 
 | MET_DEFINE_PROBE(gpu_sched_switch, TP_PROTO(const char *gpu_name, u64 timestamp, | 
 | 		u32 next_ctx_id, s32 next_prio, u32 next_job_id)) | 
 | { | 
 | 	gpu_sched_switch(gpu_name, timestamp, next_ctx_id, next_prio, next_job_id); | 
 | } | 
 |  | 
 | noinline void gpu_job_enqueue(u32 ctx_id, u32 job_id, const char *type) | 
 | { | 
 | 	MET_TRACE("ctx_id=%lu job_id=%lu type=%s", | 
 | 		   (unsigned long)ctx_id, (unsigned long)job_id, type); | 
 | } | 
 |  | 
 | MET_DEFINE_PROBE(gpu_job_enqueue, TP_PROTO(u32 ctx_id, u32 job_id, const char *type)) | 
 | { | 
 | 	gpu_job_enqueue(ctx_id, job_id, type); | 
 | } | 
 | #endif | 
 |  | 
 |  | 
 | #ifdef  MET_EVENT_POWER_SUPPORT | 
 | #include "met_power.h" | 
 | #include "met_kernel_symbol.h" | 
 |  | 
 | static int event_power_registered; | 
 | static int event_power_enabled; | 
 |  | 
 | const char * | 
 | met_trace_print_symbols_seq(char* pclass_name, unsigned long val, | 
 | 			const struct trace_print_flags *symbol_array) | 
 | { | 
 | 	int i; | 
 |     size_t new_fsize=0; | 
 |     char _buf[32]; | 
 | 	const char *ret = pclass_name; | 
 |  | 
 | 	for (i = 0;  symbol_array[i].name; i++) { | 
 |  | 
 | 		if (val != symbol_array[i].mask) | 
 | 			continue; | 
 |  | 
 | 		new_fsize = sprintf(pclass_name, symbol_array[i].name, strlen(symbol_array[i].name)); | 
 | 		break; | 
 | 	} | 
 |  | 
 | 	if (new_fsize == 0) { | 
 | 		snprintf(_buf, 32, "0x%lx", val); | 
 | 		new_fsize = sprintf(pclass_name, _buf, strlen(_buf)); | 
 | 	} | 
 |  | 
 | 	return ret; | 
 | } | 
 |  | 
 | #define __print_symbolic(pclass_name, value, symbol_array...)			\ | 
 | 	({								\ | 
 | 		static const struct trace_print_flags symbols[] =	\ | 
 | 			{ symbol_array, { -1, NULL }};			\ | 
 | 		met_trace_print_symbols_seq(pclass_name, value, symbols);		\ | 
 | 	}) | 
 |  | 
 | #ifdef pm_qos_update_request | 
 | #undef pm_qos_update_request | 
 | #endif | 
 | void pm_qos_update_request(int pm_qos_class, s32 value) | 
 | { | 
 | 	char class_name[64]; | 
 | 	MET_TRACE("pm_qos_class=%s value=%d owner=%s\n", | 
 | 	  __print_symbolic(class_name, pm_qos_class, | 
 | 		{ _PM_QOS_CPU_DMA_LATENCY,	"CPU_DMA_LATENCY" }, | 
 | 		{ _PM_QOS_NETWORK_LATENCY,	"NETWORK_LATENCY" }, | 
 | 		{ _PM_QOS_NETWORK_THROUGHPUT,	"NETWORK_THROUGHPUT" }), | 
 | 	  value); | 
 | } | 
 | //#endif | 
 |  | 
 | #ifdef pm_qos_update_target | 
 | #undef pm_qos_update_target | 
 | #endif | 
 | void pm_qos_update_target(unsigned int action, int prev_value, int curr_value) | 
 | { | 
 | 	char class_name[64]; | 
 |  | 
 | 	MET_TRACE("action=%s prev_value=%d curr_value=%d\n", | 
 | 	  __print_symbolic(class_name, action, | 
 | 		{ _PM_QOS_ADD_REQ,	"ADD_REQ" }, | 
 | 		{ _PM_QOS_UPDATE_REQ,	"UPDATE_REQ" }, | 
 | 		{ _PM_QOS_REMOVE_REQ,	"REMOVE_REQ" }), | 
 | 	  prev_value, curr_value); | 
 | } | 
 | #endif | 
 | //#endif | 
 |  | 
 | static int reset_driver_stat(void) | 
 | { | 
 | #if	IS_ENABLED(CONFIG_GPU_TRACEPOINTS) | 
 | 	event_gpu_enabled = 0; | 
 | #endif | 
 | #ifdef MET_EVENT_POWER_SUPPORT | 
 | 	event_power_enabled = 0; | 
 | #endif | 
 |  | 
 | 	met_trace_event.mode = 0; | 
 | 	return 0; | 
 | } | 
 |  | 
 |  | 
 |  | 
 | static void met_event_start(void) | 
 | { | 
 | #if	IS_ENABLED(CONFIG_GPU_TRACEPOINTS) | 
 | 	/* register trace event for gpu */ | 
 | 	do { | 
 | 		if (!event_gpu_enabled) | 
 | 			break; | 
 | 		if (MET_REGISTER_TRACE(gpu_sched_switch)) { | 
 | 			pr_debug("can not register callback of gpu_sched_switch\n"); | 
 | 			break; | 
 | 		} | 
 | 		if (MET_REGISTER_TRACE(gpu_job_enqueue)) { | 
 | 			pr_debug("can not register callback of gpu_job_enqueue\n"); | 
 | 			MET_UNREGISTER_TRACE(gpu_sched_switch); | 
 | 			break; | 
 | 		} | 
 | 		event_gpu_registered = 1; | 
 | 	} while (0); | 
 | #endif | 
 |  | 
 | #ifdef  MET_EVENT_POWER_SUPPORT | 
 | 	/* register trace event for power */ | 
 | 	do { | 
 | 		if (!event_power_enabled) | 
 | 			break; | 
 | 		if (met_export_api_symbol->met_reg_event_power) | 
 | 			if (met_export_api_symbol->met_reg_event_power()) { | 
 | 				pr_debug("can not register callback of met_reg_event_power\n"); | 
 | 				break; | 
 | 			} | 
 | 		event_power_registered = 1; | 
 | 	} while (0); | 
 | #endif | 
 |  | 
 | } | 
 |  | 
 | static void met_event_stop(void) | 
 | { | 
 | #if	IS_ENABLED(CONFIG_GPU_TRACEPOINTS) | 
 | 	/* unregister trace event for gpu */ | 
 | 	if (event_gpu_registered) { | 
 | 		MET_UNREGISTER_TRACE(gpu_job_enqueue); | 
 | 		MET_UNREGISTER_TRACE(gpu_sched_switch); | 
 | 		event_gpu_registered = 0; | 
 | 	} | 
 | #endif | 
 |  | 
 | #ifdef  MET_EVENT_POWER_SUPPORT | 
 | 	/* unregister trace event for power */ | 
 | 	if (event_power_registered) { | 
 | 		if (met_export_api_symbol->met_unreg_event_power) | 
 | 			met_export_api_symbol->met_unreg_event_power(); | 
 | 		event_power_registered = 0; | 
 | 	} | 
 | #endif | 
 | } | 
 |  | 
 | static int met_event_process_argument(const char *arg, int len) | 
 | { | 
 | 	int	ret = -1; | 
 |  | 
 | #if	IS_ENABLED(CONFIG_GPU_TRACEPOINTS) | 
 | 	if (strcasecmp(arg, "gpu") == 0) { | 
 | 		event_gpu_enabled = 1; | 
 | 		met_trace_event.mode = 1; | 
 | 		ret = 0; | 
 | 	} | 
 | #endif | 
 | #ifdef  MET_EVENT_POWER_SUPPORT | 
 | 	if (strcasecmp(arg, "power") == 0) { | 
 | 		event_power_enabled = 1; | 
 | 		met_trace_event.mode = 1; | 
 | 		ret = 0; | 
 | 	} | 
 | #endif | 
 | 	return ret; | 
 | } | 
 |  | 
 | static const char help[] = "\b" | 
 | #if	IS_ENABLED(CONFIG_GPU_TRACEPOINTS) | 
 | 	"  --event=gpu                           output gpu trace events\n" | 
 | #endif | 
 | #ifdef  MET_EVENT_POWER_SUPPORT | 
 | 	"  --event=power						 output pmqos trace events\n" | 
 | #endif | 
 | 	; | 
 |  | 
 | static int met_event_print_help(char *buf, int len) | 
 | { | 
 | 	return snprintf(buf, PAGE_SIZE, help); | 
 | } | 
 |  | 
 | static const char header[] = | 
 | 	"met-info [000] 0.0: met_ftrace_event:" | 
 | #if	IS_ENABLED(CONFIG_GPU_TRACEPOINTS) | 
 | 	" gpu:gpu_sched_switch gpu:gpu_job_enqueue" | 
 | #endif | 
 | #ifdef  MET_EVENT_POWER_SUPPORT | 
 | 	" power:pm_qos_update_request power:pm_qos_update_target" | 
 | #endif | 
 | 	"\n"; | 
 |  | 
 | static int met_event_print_header(char *buf, int len) | 
 | { | 
 | 	int	ret; | 
 |  | 
 | 	ret = snprintf(buf, PAGE_SIZE, header); | 
 | 	return ret; | 
 | } | 
 |  | 
 | struct metdevice met_trace_event = { | 
 | 	.name			= "event", | 
 | 	.type			= MET_TYPE_PMU, | 
 | 	.start			= met_event_start, | 
 | 	.stop			= met_event_stop, | 
 | 	.reset			= reset_driver_stat, | 
 | 	.process_argument	= met_event_process_argument, | 
 | 	.print_help		= met_event_print_help, | 
 | 	.print_header		= met_event_print_header, | 
 | }; |