1 // SPDX-License-Identifier: GPL-2.0-only
3 * amd_freq_sensitivity.c: AMD frequency sensitivity feedback powersave bias
4 * for the ondemand governor.
6 * Copyright (C) 2013 Advanced Micro Devices, Inc.
8 * Author: Jacob Shin <jacob.shin@amd.com>
11 #include <linux/kernel.h>
12 #include <linux/module.h>
13 #include <linux/types.h>
14 #include <linux/pci.h>
15 #include <linux/percpu-defs.h>
16 #include <linux/init.h>
17 #include <linux/mod_devicetable.h>
20 #include <asm/cpufeature.h>
21 #include <asm/cpu_device_id.h>
23 #include "cpufreq_ondemand.h"
25 #define MSR_AMD64_FREQ_SENSITIVITY_ACTUAL 0xc0010080
26 #define MSR_AMD64_FREQ_SENSITIVITY_REFERENCE 0xc0010081
27 #define CLASS_CODE_SHIFT 56
28 #define POWERSAVE_BIAS_MAX 1000
29 #define POWERSAVE_BIAS_DEF 400
34 unsigned int freq_prev
;
37 static DEFINE_PER_CPU(struct cpu_data_t
, cpu_data
);
39 static unsigned int amd_powersave_bias_target(struct cpufreq_policy
*policy
,
40 unsigned int freq_next
,
41 unsigned int relation
)
44 long d_actual
, d_reference
;
45 struct msr actual
, reference
;
46 struct cpu_data_t
*data
= &per_cpu(cpu_data
, policy
->cpu
);
47 struct policy_dbs_info
*policy_dbs
= policy
->governor_data
;
48 struct dbs_data
*od_data
= policy_dbs
->dbs_data
;
49 struct od_dbs_tuners
*od_tuners
= od_data
->tuners
;
51 if (!policy
->freq_table
)
54 rdmsr_on_cpu(policy
->cpu
, MSR_AMD64_FREQ_SENSITIVITY_ACTUAL
,
55 &actual
.l
, &actual
.h
);
56 rdmsr_on_cpu(policy
->cpu
, MSR_AMD64_FREQ_SENSITIVITY_REFERENCE
,
57 &reference
.l
, &reference
.h
);
58 actual
.h
&= 0x00ffffff;
59 reference
.h
&= 0x00ffffff;
61 /* counter wrapped around, so stay on current frequency */
62 if (actual
.q
< data
->actual
|| reference
.q
< data
->reference
) {
63 freq_next
= policy
->cur
;
67 d_actual
= actual
.q
- data
->actual
;
68 d_reference
= reference
.q
- data
->reference
;
70 /* divide by 0, so stay on current frequency as well */
71 if (d_reference
== 0) {
72 freq_next
= policy
->cur
;
76 sensitivity
= POWERSAVE_BIAS_MAX
-
77 (POWERSAVE_BIAS_MAX
* (d_reference
- d_actual
) / d_reference
);
79 clamp(sensitivity
, 0, POWERSAVE_BIAS_MAX
);
81 /* this workload is not CPU bound, so choose a lower freq */
82 if (sensitivity
< od_tuners
->powersave_bias
) {
83 if (data
->freq_prev
== policy
->cur
)
84 freq_next
= policy
->cur
;
86 if (freq_next
> policy
->cur
)
87 freq_next
= policy
->cur
;
88 else if (freq_next
< policy
->cur
)
89 freq_next
= policy
->min
;
93 index
= cpufreq_table_find_index_h(policy
,
95 freq_next
= policy
->freq_table
[index
].frequency
;
98 data
->freq_prev
= freq_next
;
103 data
->actual
= actual
.q
;
104 data
->reference
= reference
.q
;
108 static int __init
amd_freq_sensitivity_init(void)
111 struct pci_dev
*pcidev
;
112 unsigned int pci_vendor
;
114 if (boot_cpu_data
.x86_vendor
== X86_VENDOR_AMD
)
115 pci_vendor
= PCI_VENDOR_ID_AMD
;
116 else if (boot_cpu_data
.x86_vendor
== X86_VENDOR_HYGON
)
117 pci_vendor
= PCI_VENDOR_ID_HYGON
;
121 pcidev
= pci_get_device(pci_vendor
,
122 PCI_DEVICE_ID_AMD_KERNCZ_SMBUS
, NULL
);
125 if (!boot_cpu_has(X86_FEATURE_PROC_FEEDBACK
))
129 if (rdmsrl_safe(MSR_AMD64_FREQ_SENSITIVITY_ACTUAL
, &val
))
132 if (!(val
>> CLASS_CODE_SHIFT
))
135 od_register_powersave_bias_handler(amd_powersave_bias_target
,
139 late_initcall(amd_freq_sensitivity_init
);
141 static void __exit
amd_freq_sensitivity_exit(void)
143 od_unregister_powersave_bias_handler();
145 module_exit(amd_freq_sensitivity_exit
);
147 static const struct x86_cpu_id __maybe_unused amd_freq_sensitivity_ids
[] = {
148 X86_MATCH_FEATURE(X86_FEATURE_PROC_FEEDBACK
, NULL
),
151 MODULE_DEVICE_TABLE(x86cpu
, amd_freq_sensitivity_ids
);
153 MODULE_AUTHOR("Jacob Shin <jacob.shin@amd.com>");
154 MODULE_DESCRIPTION("AMD frequency sensitivity feedback powersave bias for "
155 "the ondemand governor.");
156 MODULE_LICENSE("GPL");