1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
29c5320c8SJacob Shin /*
39c5320c8SJacob Shin * amd_freq_sensitivity.c: AMD frequency sensitivity feedback powersave bias
49c5320c8SJacob Shin * for the ondemand governor.
59c5320c8SJacob Shin *
69c5320c8SJacob Shin * Copyright (C) 2013 Advanced Micro Devices, Inc.
79c5320c8SJacob Shin *
89c5320c8SJacob Shin * Author: Jacob Shin <jacob.shin@amd.com>
99c5320c8SJacob Shin */
109c5320c8SJacob Shin
119c5320c8SJacob Shin #include <linux/kernel.h>
129c5320c8SJacob Shin #include <linux/module.h>
139c5320c8SJacob Shin #include <linux/types.h>
1459a3b3a8SAkshu Agrawal #include <linux/pci.h>
159c5320c8SJacob Shin #include <linux/percpu-defs.h>
169c5320c8SJacob Shin #include <linux/init.h>
179c5320c8SJacob Shin #include <linux/mod_devicetable.h>
189c5320c8SJacob Shin
199c5320c8SJacob Shin #include <asm/msr.h>
209c5320c8SJacob Shin #include <asm/cpufeature.h>
21ba5bade4SThomas Gleixner #include <asm/cpu_device_id.h>
229c5320c8SJacob Shin
237d5a9956SRafael J. Wysocki #include "cpufreq_ondemand.h"
249c5320c8SJacob Shin
259c5320c8SJacob Shin #define MSR_AMD64_FREQ_SENSITIVITY_ACTUAL 0xc0010080
269c5320c8SJacob Shin #define MSR_AMD64_FREQ_SENSITIVITY_REFERENCE 0xc0010081
279c5320c8SJacob Shin #define CLASS_CODE_SHIFT 56
289c5320c8SJacob Shin #define POWERSAVE_BIAS_MAX 1000
299c5320c8SJacob Shin #define POWERSAVE_BIAS_DEF 400
309c5320c8SJacob Shin
319c5320c8SJacob Shin struct cpu_data_t {
329c5320c8SJacob Shin u64 actual;
339c5320c8SJacob Shin u64 reference;
349c5320c8SJacob Shin unsigned int freq_prev;
359c5320c8SJacob Shin };
369c5320c8SJacob Shin
379c5320c8SJacob Shin static DEFINE_PER_CPU(struct cpu_data_t, cpu_data);
389c5320c8SJacob Shin
amd_powersave_bias_target(struct cpufreq_policy * policy,unsigned int freq_next,unsigned int relation)399c5320c8SJacob Shin static unsigned int amd_powersave_bias_target(struct cpufreq_policy *policy,
409c5320c8SJacob Shin unsigned int freq_next,
419c5320c8SJacob Shin unsigned int relation)
429c5320c8SJacob Shin {
439c5320c8SJacob Shin int sensitivity;
449c5320c8SJacob Shin long d_actual, d_reference;
459c5320c8SJacob Shin struct msr actual, reference;
469c5320c8SJacob Shin struct cpu_data_t *data = &per_cpu(cpu_data, policy->cpu);
47bc505475SRafael J. Wysocki struct policy_dbs_info *policy_dbs = policy->governor_data;
48bc505475SRafael J. Wysocki struct dbs_data *od_data = policy_dbs->dbs_data;
499c5320c8SJacob Shin struct od_dbs_tuners *od_tuners = od_data->tuners;
509c5320c8SJacob Shin
5134ac5d7aSViresh Kumar if (!policy->freq_table)
529c5320c8SJacob Shin return freq_next;
539c5320c8SJacob Shin
549c5320c8SJacob Shin rdmsr_on_cpu(policy->cpu, MSR_AMD64_FREQ_SENSITIVITY_ACTUAL,
559c5320c8SJacob Shin &actual.l, &actual.h);
569c5320c8SJacob Shin rdmsr_on_cpu(policy->cpu, MSR_AMD64_FREQ_SENSITIVITY_REFERENCE,
579c5320c8SJacob Shin &reference.l, &reference.h);
589c5320c8SJacob Shin actual.h &= 0x00ffffff;
599c5320c8SJacob Shin reference.h &= 0x00ffffff;
609c5320c8SJacob Shin
619c5320c8SJacob Shin /* counter wrapped around, so stay on current frequency */
629c5320c8SJacob Shin if (actual.q < data->actual || reference.q < data->reference) {
639c5320c8SJacob Shin freq_next = policy->cur;
649c5320c8SJacob Shin goto out;
659c5320c8SJacob Shin }
669c5320c8SJacob Shin
679c5320c8SJacob Shin d_actual = actual.q - data->actual;
689c5320c8SJacob Shin d_reference = reference.q - data->reference;
699c5320c8SJacob Shin
709c5320c8SJacob Shin /* divide by 0, so stay on current frequency as well */
719c5320c8SJacob Shin if (d_reference == 0) {
729c5320c8SJacob Shin freq_next = policy->cur;
739c5320c8SJacob Shin goto out;
749c5320c8SJacob Shin }
759c5320c8SJacob Shin
769c5320c8SJacob Shin sensitivity = POWERSAVE_BIAS_MAX -
779c5320c8SJacob Shin (POWERSAVE_BIAS_MAX * (d_reference - d_actual) / d_reference);
789c5320c8SJacob Shin
799c5320c8SJacob Shin clamp(sensitivity, 0, POWERSAVE_BIAS_MAX);
809c5320c8SJacob Shin
819c5320c8SJacob Shin /* this workload is not CPU bound, so choose a lower freq */
829c5320c8SJacob Shin if (sensitivity < od_tuners->powersave_bias) {
839c5320c8SJacob Shin if (data->freq_prev == policy->cur)
849c5320c8SJacob Shin freq_next = policy->cur;
859c5320c8SJacob Shin
869c5320c8SJacob Shin if (freq_next > policy->cur)
879c5320c8SJacob Shin freq_next = policy->cur;
889c5320c8SJacob Shin else if (freq_next < policy->cur)
899c5320c8SJacob Shin freq_next = policy->min;
909c5320c8SJacob Shin else {
919c5320c8SJacob Shin unsigned int index;
929c5320c8SJacob Shin
9382577360SViresh Kumar index = cpufreq_table_find_index_h(policy,
941f39fa0dSVincent Donnefort policy->cur - 1,
951f39fa0dSVincent Donnefort relation & CPUFREQ_RELATION_E);
9634ac5d7aSViresh Kumar freq_next = policy->freq_table[index].frequency;
979c5320c8SJacob Shin }
989c5320c8SJacob Shin
999c5320c8SJacob Shin data->freq_prev = freq_next;
1009c5320c8SJacob Shin } else
1019c5320c8SJacob Shin data->freq_prev = 0;
1029c5320c8SJacob Shin
1039c5320c8SJacob Shin out:
1049c5320c8SJacob Shin data->actual = actual.q;
1059c5320c8SJacob Shin data->reference = reference.q;
1069c5320c8SJacob Shin return freq_next;
1079c5320c8SJacob Shin }
1089c5320c8SJacob Shin
amd_freq_sensitivity_init(void)1099c5320c8SJacob Shin static int __init amd_freq_sensitivity_init(void)
1109c5320c8SJacob Shin {
1119c5320c8SJacob Shin u64 val;
11259a3b3a8SAkshu Agrawal struct pci_dev *pcidev;
113cc9690cfSPu Wen unsigned int pci_vendor;
1149c5320c8SJacob Shin
115cc9690cfSPu Wen if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD)
116cc9690cfSPu Wen pci_vendor = PCI_VENDOR_ID_AMD;
117cc9690cfSPu Wen else if (boot_cpu_data.x86_vendor == X86_VENDOR_HYGON)
118cc9690cfSPu Wen pci_vendor = PCI_VENDOR_ID_HYGON;
119cc9690cfSPu Wen else
1209c5320c8SJacob Shin return -ENODEV;
1219c5320c8SJacob Shin
122cc9690cfSPu Wen pcidev = pci_get_device(pci_vendor,
12359a3b3a8SAkshu Agrawal PCI_DEVICE_ID_AMD_KERNCZ_SMBUS, NULL);
12459a3b3a8SAkshu Agrawal
12559a3b3a8SAkshu Agrawal if (!pcidev) {
126108ec36bSBorislav Petkov if (!boot_cpu_has(X86_FEATURE_PROC_FEEDBACK))
1279c5320c8SJacob Shin return -ENODEV;
128*91fda1f8SXiongfeng Wang } else {
129*91fda1f8SXiongfeng Wang pci_dev_put(pcidev);
13059a3b3a8SAkshu Agrawal }
1319c5320c8SJacob Shin
1329c5320c8SJacob Shin if (rdmsrl_safe(MSR_AMD64_FREQ_SENSITIVITY_ACTUAL, &val))
1339c5320c8SJacob Shin return -ENODEV;
1349c5320c8SJacob Shin
1359c5320c8SJacob Shin if (!(val >> CLASS_CODE_SHIFT))
1369c5320c8SJacob Shin return -ENODEV;
1379c5320c8SJacob Shin
1389c5320c8SJacob Shin od_register_powersave_bias_handler(amd_powersave_bias_target,
1399c5320c8SJacob Shin POWERSAVE_BIAS_DEF);
1409c5320c8SJacob Shin return 0;
1419c5320c8SJacob Shin }
1429c5320c8SJacob Shin late_initcall(amd_freq_sensitivity_init);
1439c5320c8SJacob Shin
amd_freq_sensitivity_exit(void)1449c5320c8SJacob Shin static void __exit amd_freq_sensitivity_exit(void)
1459c5320c8SJacob Shin {
1469c5320c8SJacob Shin od_unregister_powersave_bias_handler();
1479c5320c8SJacob Shin }
1489c5320c8SJacob Shin module_exit(amd_freq_sensitivity_exit);
1499c5320c8SJacob Shin
15052fe0b16SLee Jones static const struct x86_cpu_id __maybe_unused amd_freq_sensitivity_ids[] = {
151b11d77faSThomas Gleixner X86_MATCH_FEATURE(X86_FEATURE_PROC_FEEDBACK, NULL),
1529c5320c8SJacob Shin {}
1539c5320c8SJacob Shin };
1549c5320c8SJacob Shin MODULE_DEVICE_TABLE(x86cpu, amd_freq_sensitivity_ids);
1559c5320c8SJacob Shin
1569c5320c8SJacob Shin MODULE_AUTHOR("Jacob Shin <jacob.shin@amd.com>");
1579c5320c8SJacob Shin MODULE_DESCRIPTION("AMD frequency sensitivity feedback powersave bias for "
1589c5320c8SJacob Shin "the ondemand governor.");
1599c5320c8SJacob Shin MODULE_LICENSE("GPL");
160