Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * amd_freq_sensitivity.c: AMD frequency sensitivity feedback powersave bias
0004  *                         for the ondemand governor.
0005  *
0006  * Copyright (C) 2013 Advanced Micro Devices, Inc.
0007  *
0008  * Author: Jacob Shin <jacob.shin@amd.com>
0009  */
0010 
0011 #include <linux/kernel.h>
0012 #include <linux/module.h>
0013 #include <linux/types.h>
0014 #include <linux/pci.h>
0015 #include <linux/percpu-defs.h>
0016 #include <linux/init.h>
0017 #include <linux/mod_devicetable.h>
0018 
0019 #include <asm/msr.h>
0020 #include <asm/cpufeature.h>
0021 #include <asm/cpu_device_id.h>
0022 
0023 #include "cpufreq_ondemand.h"
0024 
0025 #define MSR_AMD64_FREQ_SENSITIVITY_ACTUAL   0xc0010080
0026 #define MSR_AMD64_FREQ_SENSITIVITY_REFERENCE    0xc0010081
0027 #define CLASS_CODE_SHIFT            56
0028 #define POWERSAVE_BIAS_MAX          1000
0029 #define POWERSAVE_BIAS_DEF          400
0030 
0031 struct cpu_data_t {
0032     u64 actual;
0033     u64 reference;
0034     unsigned int freq_prev;
0035 };
0036 
0037 static DEFINE_PER_CPU(struct cpu_data_t, cpu_data);
0038 
0039 static unsigned int amd_powersave_bias_target(struct cpufreq_policy *policy,
0040                           unsigned int freq_next,
0041                           unsigned int relation)
0042 {
0043     int sensitivity;
0044     long d_actual, d_reference;
0045     struct msr actual, reference;
0046     struct cpu_data_t *data = &per_cpu(cpu_data, policy->cpu);
0047     struct policy_dbs_info *policy_dbs = policy->governor_data;
0048     struct dbs_data *od_data = policy_dbs->dbs_data;
0049     struct od_dbs_tuners *od_tuners = od_data->tuners;
0050 
0051     if (!policy->freq_table)
0052         return freq_next;
0053 
0054     rdmsr_on_cpu(policy->cpu, MSR_AMD64_FREQ_SENSITIVITY_ACTUAL,
0055         &actual.l, &actual.h);
0056     rdmsr_on_cpu(policy->cpu, MSR_AMD64_FREQ_SENSITIVITY_REFERENCE,
0057         &reference.l, &reference.h);
0058     actual.h &= 0x00ffffff;
0059     reference.h &= 0x00ffffff;
0060 
0061     /* counter wrapped around, so stay on current frequency */
0062     if (actual.q < data->actual || reference.q < data->reference) {
0063         freq_next = policy->cur;
0064         goto out;
0065     }
0066 
0067     d_actual = actual.q - data->actual;
0068     d_reference = reference.q - data->reference;
0069 
0070     /* divide by 0, so stay on current frequency as well */
0071     if (d_reference == 0) {
0072         freq_next = policy->cur;
0073         goto out;
0074     }
0075 
0076     sensitivity = POWERSAVE_BIAS_MAX -
0077         (POWERSAVE_BIAS_MAX * (d_reference - d_actual) / d_reference);
0078 
0079     clamp(sensitivity, 0, POWERSAVE_BIAS_MAX);
0080 
0081     /* this workload is not CPU bound, so choose a lower freq */
0082     if (sensitivity < od_tuners->powersave_bias) {
0083         if (data->freq_prev == policy->cur)
0084             freq_next = policy->cur;
0085 
0086         if (freq_next > policy->cur)
0087             freq_next = policy->cur;
0088         else if (freq_next < policy->cur)
0089             freq_next = policy->min;
0090         else {
0091             unsigned int index;
0092 
0093             index = cpufreq_table_find_index_h(policy,
0094                                policy->cur - 1,
0095                                relation & CPUFREQ_RELATION_E);
0096             freq_next = policy->freq_table[index].frequency;
0097         }
0098 
0099         data->freq_prev = freq_next;
0100     } else
0101         data->freq_prev = 0;
0102 
0103 out:
0104     data->actual = actual.q;
0105     data->reference = reference.q;
0106     return freq_next;
0107 }
0108 
0109 static int __init amd_freq_sensitivity_init(void)
0110 {
0111     u64 val;
0112     struct pci_dev *pcidev;
0113     unsigned int pci_vendor;
0114 
0115     if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD)
0116         pci_vendor = PCI_VENDOR_ID_AMD;
0117     else if (boot_cpu_data.x86_vendor == X86_VENDOR_HYGON)
0118         pci_vendor = PCI_VENDOR_ID_HYGON;
0119     else
0120         return -ENODEV;
0121 
0122     pcidev = pci_get_device(pci_vendor,
0123             PCI_DEVICE_ID_AMD_KERNCZ_SMBUS, NULL);
0124 
0125     if (!pcidev) {
0126         if (!boot_cpu_has(X86_FEATURE_PROC_FEEDBACK))
0127             return -ENODEV;
0128     }
0129 
0130     if (rdmsrl_safe(MSR_AMD64_FREQ_SENSITIVITY_ACTUAL, &val))
0131         return -ENODEV;
0132 
0133     if (!(val >> CLASS_CODE_SHIFT))
0134         return -ENODEV;
0135 
0136     od_register_powersave_bias_handler(amd_powersave_bias_target,
0137             POWERSAVE_BIAS_DEF);
0138     return 0;
0139 }
0140 late_initcall(amd_freq_sensitivity_init);
0141 
0142 static void __exit amd_freq_sensitivity_exit(void)
0143 {
0144     od_unregister_powersave_bias_handler();
0145 }
0146 module_exit(amd_freq_sensitivity_exit);
0147 
0148 static const struct x86_cpu_id __maybe_unused amd_freq_sensitivity_ids[] = {
0149     X86_MATCH_FEATURE(X86_FEATURE_PROC_FEEDBACK, NULL),
0150     {}
0151 };
0152 MODULE_DEVICE_TABLE(x86cpu, amd_freq_sensitivity_ids);
0153 
0154 MODULE_AUTHOR("Jacob Shin <jacob.shin@amd.com>");
0155 MODULE_DESCRIPTION("AMD frequency sensitivity feedback powersave bias for "
0156         "the ondemand governor.");
0157 MODULE_LICENSE("GPL");