149 lines
		
	
	
	
		
			3.8 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			149 lines
		
	
	
	
		
			3.8 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
|   | /*
 | ||
|  |  * amd_freq_sensitivity.c: AMD frequency sensitivity feedback powersave bias | ||
|  |  *                         for the ondemand governor. | ||
|  |  * | ||
|  |  * Copyright (C) 2013 Advanced Micro Devices, Inc. | ||
|  |  * | ||
|  |  * Author: Jacob Shin <jacob.shin@amd.com> | ||
|  |  * | ||
|  |  * 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 <linux/kernel.h>
 | ||
|  | #include <linux/module.h>
 | ||
|  | #include <linux/types.h>
 | ||
|  | #include <linux/percpu-defs.h>
 | ||
|  | #include <linux/init.h>
 | ||
|  | #include <linux/mod_devicetable.h>
 | ||
|  | 
 | ||
|  | #include <asm/msr.h>
 | ||
|  | #include <asm/cpufeature.h>
 | ||
|  | 
 | ||
|  | #include "cpufreq_governor.h"
 | ||
|  | 
 | ||
|  | #define MSR_AMD64_FREQ_SENSITIVITY_ACTUAL	0xc0010080
 | ||
|  | #define MSR_AMD64_FREQ_SENSITIVITY_REFERENCE	0xc0010081
 | ||
|  | #define CLASS_CODE_SHIFT			56
 | ||
|  | #define POWERSAVE_BIAS_MAX			1000
 | ||
|  | #define POWERSAVE_BIAS_DEF			400
 | ||
|  | 
 | ||
|  | struct cpu_data_t { | ||
|  | 	u64 actual; | ||
|  | 	u64 reference; | ||
|  | 	unsigned int freq_prev; | ||
|  | }; | ||
|  | 
 | ||
|  | static DEFINE_PER_CPU(struct cpu_data_t, cpu_data); | ||
|  | 
 | ||
|  | static unsigned int amd_powersave_bias_target(struct cpufreq_policy *policy, | ||
|  | 					      unsigned int freq_next, | ||
|  | 					      unsigned int relation) | ||
|  | { | ||
|  | 	int sensitivity; | ||
|  | 	long d_actual, d_reference; | ||
|  | 	struct msr actual, reference; | ||
|  | 	struct cpu_data_t *data = &per_cpu(cpu_data, policy->cpu); | ||
|  | 	struct dbs_data *od_data = policy->governor_data; | ||
|  | 	struct od_dbs_tuners *od_tuners = od_data->tuners; | ||
|  | 	struct od_cpu_dbs_info_s *od_info = | ||
|  | 		od_data->cdata->get_cpu_dbs_info_s(policy->cpu); | ||
|  | 
 | ||
|  | 	if (!od_info->freq_table) | ||
|  | 		return freq_next; | ||
|  | 
 | ||
|  | 	rdmsr_on_cpu(policy->cpu, MSR_AMD64_FREQ_SENSITIVITY_ACTUAL, | ||
|  | 		&actual.l, &actual.h); | ||
|  | 	rdmsr_on_cpu(policy->cpu, MSR_AMD64_FREQ_SENSITIVITY_REFERENCE, | ||
|  | 		&reference.l, &reference.h); | ||
|  | 	actual.h &= 0x00ffffff; | ||
|  | 	reference.h &= 0x00ffffff; | ||
|  | 
 | ||
|  | 	/* counter wrapped around, so stay on current frequency */ | ||
|  | 	if (actual.q < data->actual || reference.q < data->reference) { | ||
|  | 		freq_next = policy->cur; | ||
|  | 		goto out; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	d_actual = actual.q - data->actual; | ||
|  | 	d_reference = reference.q - data->reference; | ||
|  | 
 | ||
|  | 	/* divide by 0, so stay on current frequency as well */ | ||
|  | 	if (d_reference == 0) { | ||
|  | 		freq_next = policy->cur; | ||
|  | 		goto out; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	sensitivity = POWERSAVE_BIAS_MAX - | ||
|  | 		(POWERSAVE_BIAS_MAX * (d_reference - d_actual) / d_reference); | ||
|  | 
 | ||
|  | 	clamp(sensitivity, 0, POWERSAVE_BIAS_MAX); | ||
|  | 
 | ||
|  | 	/* this workload is not CPU bound, so choose a lower freq */ | ||
|  | 	if (sensitivity < od_tuners->powersave_bias) { | ||
|  | 		if (data->freq_prev == policy->cur) | ||
|  | 			freq_next = policy->cur; | ||
|  | 
 | ||
|  | 		if (freq_next > policy->cur) | ||
|  | 			freq_next = policy->cur; | ||
|  | 		else if (freq_next < policy->cur) | ||
|  | 			freq_next = policy->min; | ||
|  | 		else { | ||
|  | 			unsigned int index; | ||
|  | 
 | ||
|  | 			cpufreq_frequency_table_target(policy, | ||
|  | 				od_info->freq_table, policy->cur - 1, | ||
|  | 				CPUFREQ_RELATION_H, &index); | ||
|  | 			freq_next = od_info->freq_table[index].frequency; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		data->freq_prev = freq_next; | ||
|  | 	} else | ||
|  | 		data->freq_prev = 0; | ||
|  | 
 | ||
|  | out: | ||
|  | 	data->actual = actual.q; | ||
|  | 	data->reference = reference.q; | ||
|  | 	return freq_next; | ||
|  | } | ||
|  | 
 | ||
|  | static int __init amd_freq_sensitivity_init(void) | ||
|  | { | ||
|  | 	u64 val; | ||
|  | 
 | ||
|  | 	if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD) | ||
|  | 		return -ENODEV; | ||
|  | 
 | ||
|  | 	if (!static_cpu_has(X86_FEATURE_PROC_FEEDBACK)) | ||
|  | 		return -ENODEV; | ||
|  | 
 | ||
|  | 	if (rdmsrl_safe(MSR_AMD64_FREQ_SENSITIVITY_ACTUAL, &val)) | ||
|  | 		return -ENODEV; | ||
|  | 
 | ||
|  | 	if (!(val >> CLASS_CODE_SHIFT)) | ||
|  | 		return -ENODEV; | ||
|  | 
 | ||
|  | 	od_register_powersave_bias_handler(amd_powersave_bias_target, | ||
|  | 			POWERSAVE_BIAS_DEF); | ||
|  | 	return 0; | ||
|  | } | ||
|  | late_initcall(amd_freq_sensitivity_init); | ||
|  | 
 | ||
|  | static void __exit amd_freq_sensitivity_exit(void) | ||
|  | { | ||
|  | 	od_unregister_powersave_bias_handler(); | ||
|  | } | ||
|  | module_exit(amd_freq_sensitivity_exit); | ||
|  | 
 | ||
|  | static const struct x86_cpu_id amd_freq_sensitivity_ids[] = { | ||
|  | 	X86_FEATURE_MATCH(X86_FEATURE_PROC_FEEDBACK), | ||
|  | 	{} | ||
|  | }; | ||
|  | MODULE_DEVICE_TABLE(x86cpu, amd_freq_sensitivity_ids); | ||
|  | 
 | ||
|  | MODULE_AUTHOR("Jacob Shin <jacob.shin@amd.com>"); | ||
|  | MODULE_DESCRIPTION("AMD frequency sensitivity feedback powersave bias for " | ||
|  | 		"the ondemand governor."); | ||
|  | MODULE_LICENSE("GPL"); |