|  | /* | 
|  | * 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"); |