14f19048fSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2bb0a56ecSDave Jones /*
3bb0a56ecSDave Jones * Based on documentation provided by Dave Jones. Thanks!
4bb0a56ecSDave Jones *
5bb0a56ecSDave Jones * BIG FAT DISCLAIMER: Work in progress code. Possibly *dangerous*
6bb0a56ecSDave Jones */
7bb0a56ecSDave Jones
81c5864e2SJoe Perches #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
91c5864e2SJoe Perches
10bb0a56ecSDave Jones #include <linux/kernel.h>
11bb0a56ecSDave Jones #include <linux/module.h>
12bb0a56ecSDave Jones #include <linux/init.h>
13bb0a56ecSDave Jones #include <linux/cpufreq.h>
14bb0a56ecSDave Jones #include <linux/ioport.h>
15bb0a56ecSDave Jones #include <linux/slab.h>
16bb0a56ecSDave Jones #include <linux/timex.h>
17bb0a56ecSDave Jones #include <linux/io.h>
18bb0a56ecSDave Jones #include <linux/delay.h>
19bb0a56ecSDave Jones
20fa8031aeSAndi Kleen #include <asm/cpu_device_id.h>
21bb0a56ecSDave Jones #include <asm/msr.h>
22bb0a56ecSDave Jones #include <asm/tsc.h>
23bb0a56ecSDave Jones
246de0dc4bSJavier Martinez Canillas #if IS_ENABLED(CONFIG_ACPI_PROCESSOR)
2527e954c2SRafał Bilski #include <linux/acpi.h>
2627e954c2SRafał Bilski #include <acpi/processor.h>
2727e954c2SRafał Bilski #endif
2827e954c2SRafał Bilski
29bb0a56ecSDave Jones #define EPS_BRAND_C7M 0
30bb0a56ecSDave Jones #define EPS_BRAND_C7 1
31bb0a56ecSDave Jones #define EPS_BRAND_EDEN 2
32bb0a56ecSDave Jones #define EPS_BRAND_C3 3
33bb0a56ecSDave Jones #define EPS_BRAND_C7D 4
34bb0a56ecSDave Jones
35bb0a56ecSDave Jones struct eps_cpu_data {
36bb0a56ecSDave Jones u32 fsb;
376de0dc4bSJavier Martinez Canillas #if IS_ENABLED(CONFIG_ACPI_PROCESSOR)
3827e954c2SRafał Bilski u32 bios_limit;
3927e954c2SRafał Bilski #endif
40bb0a56ecSDave Jones struct cpufreq_frequency_table freq_table[];
41bb0a56ecSDave Jones };
42bb0a56ecSDave Jones
43bb0a56ecSDave Jones static struct eps_cpu_data *eps_cpu[NR_CPUS];
44bb0a56ecSDave Jones
45ed361bf0SRafał Bilski /* Module parameters */
46ed361bf0SRafał Bilski static int freq_failsafe_off;
47ed361bf0SRafał Bilski static int voltage_failsafe_off;
48826e570bSRafał Bilski static int set_max_voltage;
49ed361bf0SRafał Bilski
506de0dc4bSJavier Martinez Canillas #if IS_ENABLED(CONFIG_ACPI_PROCESSOR)
5127e954c2SRafał Bilski static int ignore_acpi_limit;
5227e954c2SRafał Bilski
5327e954c2SRafał Bilski static struct acpi_processor_performance *eps_acpi_cpu_perf;
5427e954c2SRafał Bilski
5527e954c2SRafał Bilski /* Minimum necessary to get acpi_processor_get_bios_limit() working */
eps_acpi_init(void)5627e954c2SRafał Bilski static int eps_acpi_init(void)
5727e954c2SRafał Bilski {
58d5b73cd8SViresh Kumar eps_acpi_cpu_perf = kzalloc(sizeof(*eps_acpi_cpu_perf),
5927e954c2SRafał Bilski GFP_KERNEL);
6027e954c2SRafał Bilski if (!eps_acpi_cpu_perf)
6127e954c2SRafał Bilski return -ENOMEM;
6227e954c2SRafał Bilski
6327e954c2SRafał Bilski if (!zalloc_cpumask_var(&eps_acpi_cpu_perf->shared_cpu_map,
6427e954c2SRafał Bilski GFP_KERNEL)) {
6527e954c2SRafał Bilski kfree(eps_acpi_cpu_perf);
6627e954c2SRafał Bilski eps_acpi_cpu_perf = NULL;
6727e954c2SRafał Bilski return -ENOMEM;
6827e954c2SRafał Bilski }
6927e954c2SRafał Bilski
7027e954c2SRafał Bilski if (acpi_processor_register_performance(eps_acpi_cpu_perf, 0)) {
7127e954c2SRafał Bilski free_cpumask_var(eps_acpi_cpu_perf->shared_cpu_map);
7227e954c2SRafał Bilski kfree(eps_acpi_cpu_perf);
7327e954c2SRafał Bilski eps_acpi_cpu_perf = NULL;
7427e954c2SRafał Bilski return -EIO;
7527e954c2SRafał Bilski }
7627e954c2SRafał Bilski return 0;
7727e954c2SRafał Bilski }
7827e954c2SRafał Bilski
eps_acpi_exit(struct cpufreq_policy * policy)7927e954c2SRafał Bilski static int eps_acpi_exit(struct cpufreq_policy *policy)
8027e954c2SRafał Bilski {
8127e954c2SRafał Bilski if (eps_acpi_cpu_perf) {
82b2f8dc4cSRafael J. Wysocki acpi_processor_unregister_performance(0);
8327e954c2SRafał Bilski free_cpumask_var(eps_acpi_cpu_perf->shared_cpu_map);
8427e954c2SRafał Bilski kfree(eps_acpi_cpu_perf);
8527e954c2SRafał Bilski eps_acpi_cpu_perf = NULL;
8627e954c2SRafał Bilski }
8727e954c2SRafał Bilski return 0;
8827e954c2SRafał Bilski }
8927e954c2SRafał Bilski #endif
90bb0a56ecSDave Jones
eps_get(unsigned int cpu)91bb0a56ecSDave Jones static unsigned int eps_get(unsigned int cpu)
92bb0a56ecSDave Jones {
93bb0a56ecSDave Jones struct eps_cpu_data *centaur;
94bb0a56ecSDave Jones u32 lo, hi;
95bb0a56ecSDave Jones
96bb0a56ecSDave Jones if (cpu)
97bb0a56ecSDave Jones return 0;
98bb0a56ecSDave Jones centaur = eps_cpu[cpu];
99bb0a56ecSDave Jones if (centaur == NULL)
100bb0a56ecSDave Jones return 0;
101bb0a56ecSDave Jones
102bb0a56ecSDave Jones /* Return current frequency */
103bb0a56ecSDave Jones rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
104bb0a56ecSDave Jones return centaur->fsb * ((lo >> 8) & 0xff);
105bb0a56ecSDave Jones }
106bb0a56ecSDave Jones
eps_set_state(struct eps_cpu_data * centaur,struct cpufreq_policy * policy,u32 dest_state)107bb0a56ecSDave Jones static int eps_set_state(struct eps_cpu_data *centaur,
108b43a7ffbSViresh Kumar struct cpufreq_policy *policy,
109bb0a56ecSDave Jones u32 dest_state)
110bb0a56ecSDave Jones {
111bb0a56ecSDave Jones u32 lo, hi;
112bb0a56ecSDave Jones int i;
113bb0a56ecSDave Jones
114bb0a56ecSDave Jones /* Wait while CPU is busy */
115bb0a56ecSDave Jones rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
116bb0a56ecSDave Jones i = 0;
117bb0a56ecSDave Jones while (lo & ((1 << 16) | (1 << 17))) {
118bb0a56ecSDave Jones udelay(16);
119bb0a56ecSDave Jones rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
120bb0a56ecSDave Jones i++;
121bb0a56ecSDave Jones if (unlikely(i > 64)) {
122d4019f0aSViresh Kumar return -ENODEV;
123bb0a56ecSDave Jones }
124bb0a56ecSDave Jones }
125bb0a56ecSDave Jones /* Set new multiplier and voltage */
126bb0a56ecSDave Jones wrmsr(MSR_IA32_PERF_CTL, dest_state & 0xffff, 0);
127bb0a56ecSDave Jones /* Wait until transition end */
128bb0a56ecSDave Jones i = 0;
129bb0a56ecSDave Jones do {
130bb0a56ecSDave Jones udelay(16);
131bb0a56ecSDave Jones rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
132bb0a56ecSDave Jones i++;
133bb0a56ecSDave Jones if (unlikely(i > 64)) {
134d4019f0aSViresh Kumar return -ENODEV;
135bb0a56ecSDave Jones }
136bb0a56ecSDave Jones } while (lo & ((1 << 16) | (1 << 17)));
137bb0a56ecSDave Jones
138bb0a56ecSDave Jones #ifdef DEBUG
139bb0a56ecSDave Jones {
140bb0a56ecSDave Jones u8 current_multiplier, current_voltage;
141bb0a56ecSDave Jones
142bb0a56ecSDave Jones /* Print voltage and multiplier */
143bb0a56ecSDave Jones rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
144bb0a56ecSDave Jones current_voltage = lo & 0xff;
1451c5864e2SJoe Perches pr_info("Current voltage = %dmV\n", current_voltage * 16 + 700);
146bb0a56ecSDave Jones current_multiplier = (lo >> 8) & 0xff;
1471c5864e2SJoe Perches pr_info("Current multiplier = %d\n", current_multiplier);
148bb0a56ecSDave Jones }
149bb0a56ecSDave Jones #endif
150d4019f0aSViresh Kumar return 0;
151bb0a56ecSDave Jones }
152bb0a56ecSDave Jones
eps_target(struct cpufreq_policy * policy,unsigned int index)1539c0ebcf7SViresh Kumar static int eps_target(struct cpufreq_policy *policy, unsigned int index)
154bb0a56ecSDave Jones {
155bb0a56ecSDave Jones struct eps_cpu_data *centaur;
156bb0a56ecSDave Jones unsigned int cpu = policy->cpu;
157bb0a56ecSDave Jones unsigned int dest_state;
158bb0a56ecSDave Jones int ret;
159bb0a56ecSDave Jones
160bb0a56ecSDave Jones if (unlikely(eps_cpu[cpu] == NULL))
161bb0a56ecSDave Jones return -ENODEV;
162bb0a56ecSDave Jones centaur = eps_cpu[cpu];
163bb0a56ecSDave Jones
164bb0a56ecSDave Jones /* Make frequency transition */
1659c0ebcf7SViresh Kumar dest_state = centaur->freq_table[index].driver_data & 0xffff;
166b43a7ffbSViresh Kumar ret = eps_set_state(centaur, policy, dest_state);
167bb0a56ecSDave Jones if (ret)
1681c5864e2SJoe Perches pr_err("Timeout!\n");
169bb0a56ecSDave Jones return ret;
170bb0a56ecSDave Jones }
171bb0a56ecSDave Jones
eps_cpu_init(struct cpufreq_policy * policy)172bb0a56ecSDave Jones static int eps_cpu_init(struct cpufreq_policy *policy)
173bb0a56ecSDave Jones {
174bb0a56ecSDave Jones unsigned int i;
175bb0a56ecSDave Jones u32 lo, hi;
176bb0a56ecSDave Jones u64 val;
177bb0a56ecSDave Jones u8 current_multiplier, current_voltage;
178bb0a56ecSDave Jones u8 max_multiplier, max_voltage;
179bb0a56ecSDave Jones u8 min_multiplier, min_voltage;
180bb0a56ecSDave Jones u8 brand = 0;
181bb0a56ecSDave Jones u32 fsb;
182bb0a56ecSDave Jones struct eps_cpu_data *centaur;
183bb0a56ecSDave Jones struct cpuinfo_x86 *c = &cpu_data(0);
184bb0a56ecSDave Jones struct cpufreq_frequency_table *f_table;
185bb0a56ecSDave Jones int k, step, voltage;
186bb0a56ecSDave Jones int states;
1876de0dc4bSJavier Martinez Canillas #if IS_ENABLED(CONFIG_ACPI_PROCESSOR)
18827e954c2SRafał Bilski unsigned int limit;
18927e954c2SRafał Bilski #endif
190bb0a56ecSDave Jones
191bb0a56ecSDave Jones if (policy->cpu != 0)
192bb0a56ecSDave Jones return -ENODEV;
193bb0a56ecSDave Jones
194bb0a56ecSDave Jones /* Check brand */
1951c5864e2SJoe Perches pr_info("Detected VIA ");
196bb0a56ecSDave Jones
197bb0a56ecSDave Jones switch (c->x86_model) {
198bb0a56ecSDave Jones case 10:
199bb0a56ecSDave Jones rdmsr(0x1153, lo, hi);
200bb0a56ecSDave Jones brand = (((lo >> 2) ^ lo) >> 18) & 3;
201b49c22a6SJoe Perches pr_cont("Model A ");
202bb0a56ecSDave Jones break;
203bb0a56ecSDave Jones case 13:
204bb0a56ecSDave Jones rdmsr(0x1154, lo, hi);
205bb0a56ecSDave Jones brand = (((lo >> 4) ^ (lo >> 2))) & 0x000000ff;
206b49c22a6SJoe Perches pr_cont("Model D ");
207bb0a56ecSDave Jones break;
208bb0a56ecSDave Jones }
209bb0a56ecSDave Jones
210bb0a56ecSDave Jones switch (brand) {
211bb0a56ecSDave Jones case EPS_BRAND_C7M:
212b49c22a6SJoe Perches pr_cont("C7-M\n");
213bb0a56ecSDave Jones break;
214bb0a56ecSDave Jones case EPS_BRAND_C7:
215b49c22a6SJoe Perches pr_cont("C7\n");
216bb0a56ecSDave Jones break;
217bb0a56ecSDave Jones case EPS_BRAND_EDEN:
218b49c22a6SJoe Perches pr_cont("Eden\n");
219bb0a56ecSDave Jones break;
220bb0a56ecSDave Jones case EPS_BRAND_C7D:
221b49c22a6SJoe Perches pr_cont("C7-D\n");
222bb0a56ecSDave Jones break;
223bb0a56ecSDave Jones case EPS_BRAND_C3:
224b49c22a6SJoe Perches pr_cont("C3\n");
225bb0a56ecSDave Jones return -ENODEV;
226bb0a56ecSDave Jones }
227bb0a56ecSDave Jones /* Enable Enhanced PowerSaver */
228bb0a56ecSDave Jones rdmsrl(MSR_IA32_MISC_ENABLE, val);
229bb0a56ecSDave Jones if (!(val & MSR_IA32_MISC_ENABLE_ENHANCED_SPEEDSTEP)) {
230bb0a56ecSDave Jones val |= MSR_IA32_MISC_ENABLE_ENHANCED_SPEEDSTEP;
231bb0a56ecSDave Jones wrmsrl(MSR_IA32_MISC_ENABLE, val);
232bb0a56ecSDave Jones /* Can be locked at 0 */
233bb0a56ecSDave Jones rdmsrl(MSR_IA32_MISC_ENABLE, val);
234bb0a56ecSDave Jones if (!(val & MSR_IA32_MISC_ENABLE_ENHANCED_SPEEDSTEP)) {
2351c5864e2SJoe Perches pr_info("Can't enable Enhanced PowerSaver\n");
236bb0a56ecSDave Jones return -ENODEV;
237bb0a56ecSDave Jones }
238bb0a56ecSDave Jones }
239bb0a56ecSDave Jones
240bb0a56ecSDave Jones /* Print voltage and multiplier */
241bb0a56ecSDave Jones rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
242bb0a56ecSDave Jones current_voltage = lo & 0xff;
2431c5864e2SJoe Perches pr_info("Current voltage = %dmV\n", current_voltage * 16 + 700);
244bb0a56ecSDave Jones current_multiplier = (lo >> 8) & 0xff;
2451c5864e2SJoe Perches pr_info("Current multiplier = %d\n", current_multiplier);
246bb0a56ecSDave Jones
247bb0a56ecSDave Jones /* Print limits */
248bb0a56ecSDave Jones max_voltage = hi & 0xff;
2491c5864e2SJoe Perches pr_info("Highest voltage = %dmV\n", max_voltage * 16 + 700);
250bb0a56ecSDave Jones max_multiplier = (hi >> 8) & 0xff;
2511c5864e2SJoe Perches pr_info("Highest multiplier = %d\n", max_multiplier);
252bb0a56ecSDave Jones min_voltage = (hi >> 16) & 0xff;
2531c5864e2SJoe Perches pr_info("Lowest voltage = %dmV\n", min_voltage * 16 + 700);
254bb0a56ecSDave Jones min_multiplier = (hi >> 24) & 0xff;
2551c5864e2SJoe Perches pr_info("Lowest multiplier = %d\n", min_multiplier);
256bb0a56ecSDave Jones
257bb0a56ecSDave Jones /* Sanity checks */
258bb0a56ecSDave Jones if (current_multiplier == 0 || max_multiplier == 0
259bb0a56ecSDave Jones || min_multiplier == 0)
260bb0a56ecSDave Jones return -EINVAL;
261bb0a56ecSDave Jones if (current_multiplier > max_multiplier
262bb0a56ecSDave Jones || max_multiplier <= min_multiplier)
263bb0a56ecSDave Jones return -EINVAL;
264bb0a56ecSDave Jones if (current_voltage > 0x1f || max_voltage > 0x1f)
265bb0a56ecSDave Jones return -EINVAL;
266ed361bf0SRafał Bilski if (max_voltage < min_voltage
267ed361bf0SRafał Bilski || current_voltage < min_voltage
268ed361bf0SRafał Bilski || current_voltage > max_voltage)
269bb0a56ecSDave Jones return -EINVAL;
270bb0a56ecSDave Jones
271ed361bf0SRafał Bilski /* Check for systems using underclocked CPU */
272ed361bf0SRafał Bilski if (!freq_failsafe_off && max_multiplier != current_multiplier) {
2731c5864e2SJoe Perches pr_info("Your processor is running at different frequency then its maximum. Aborting.\n");
2741c5864e2SJoe Perches pr_info("You can use freq_failsafe_off option to disable this check.\n");
275ed361bf0SRafał Bilski return -EINVAL;
276ed361bf0SRafał Bilski }
277ed361bf0SRafał Bilski if (!voltage_failsafe_off && max_voltage != current_voltage) {
2781c5864e2SJoe Perches pr_info("Your processor is running at different voltage then its maximum. Aborting.\n");
2791c5864e2SJoe Perches pr_info("You can use voltage_failsafe_off option to disable this check.\n");
280ed361bf0SRafał Bilski return -EINVAL;
281ed361bf0SRafał Bilski }
282ed361bf0SRafał Bilski
283bb0a56ecSDave Jones /* Calc FSB speed */
284bb0a56ecSDave Jones fsb = cpu_khz / current_multiplier;
28527e954c2SRafał Bilski
2866de0dc4bSJavier Martinez Canillas #if IS_ENABLED(CONFIG_ACPI_PROCESSOR)
28727e954c2SRafał Bilski /* Check for ACPI processor speed limit */
28827e954c2SRafał Bilski if (!ignore_acpi_limit && !eps_acpi_init()) {
28927e954c2SRafał Bilski if (!acpi_processor_get_bios_limit(policy->cpu, &limit)) {
2901c5864e2SJoe Perches pr_info("ACPI limit %u.%uGHz\n",
29127e954c2SRafał Bilski limit/1000000,
29227e954c2SRafał Bilski (limit%1000000)/10000);
29327e954c2SRafał Bilski eps_acpi_exit(policy);
29427e954c2SRafał Bilski /* Check if max_multiplier is in BIOS limits */
29527e954c2SRafał Bilski if (limit && max_multiplier * fsb > limit) {
2961c5864e2SJoe Perches pr_info("Aborting\n");
29727e954c2SRafał Bilski return -EINVAL;
29827e954c2SRafał Bilski }
29927e954c2SRafał Bilski }
30027e954c2SRafał Bilski }
30127e954c2SRafał Bilski #endif
30227e954c2SRafał Bilski
303826e570bSRafał Bilski /* Allow user to set lower maximum voltage then that reported
304826e570bSRafał Bilski * by processor */
305826e570bSRafał Bilski if (brand == EPS_BRAND_C7M && set_max_voltage) {
306826e570bSRafał Bilski u32 v;
307826e570bSRafał Bilski
308826e570bSRafał Bilski /* Change mV to something hardware can use */
309826e570bSRafał Bilski v = (set_max_voltage - 700) / 16;
310826e570bSRafał Bilski /* Check if voltage is within limits */
311826e570bSRafał Bilski if (v >= min_voltage && v <= max_voltage) {
3121c5864e2SJoe Perches pr_info("Setting %dmV as maximum\n", v * 16 + 700);
313826e570bSRafał Bilski max_voltage = v;
314826e570bSRafał Bilski }
315826e570bSRafał Bilski }
316826e570bSRafał Bilski
317bb0a56ecSDave Jones /* Calc number of p-states supported */
318bb0a56ecSDave Jones if (brand == EPS_BRAND_C7M)
319bb0a56ecSDave Jones states = max_multiplier - min_multiplier + 1;
320bb0a56ecSDave Jones else
321bb0a56ecSDave Jones states = 2;
322bb0a56ecSDave Jones
323bb0a56ecSDave Jones /* Allocate private data and frequency table for current cpu */
3244944514eSGustavo A. R. Silva centaur = kzalloc(struct_size(centaur, freq_table, states + 1),
325bb0a56ecSDave Jones GFP_KERNEL);
326bb0a56ecSDave Jones if (!centaur)
327bb0a56ecSDave Jones return -ENOMEM;
328bb0a56ecSDave Jones eps_cpu[0] = centaur;
329bb0a56ecSDave Jones
330bb0a56ecSDave Jones /* Copy basic values */
331bb0a56ecSDave Jones centaur->fsb = fsb;
3326de0dc4bSJavier Martinez Canillas #if IS_ENABLED(CONFIG_ACPI_PROCESSOR)
33327e954c2SRafał Bilski centaur->bios_limit = limit;
33427e954c2SRafał Bilski #endif
335bb0a56ecSDave Jones
336bb0a56ecSDave Jones /* Fill frequency and MSR value table */
337bb0a56ecSDave Jones f_table = ¢aur->freq_table[0];
338bb0a56ecSDave Jones if (brand != EPS_BRAND_C7M) {
339bb0a56ecSDave Jones f_table[0].frequency = fsb * min_multiplier;
34050701588SViresh Kumar f_table[0].driver_data = (min_multiplier << 8) | min_voltage;
341bb0a56ecSDave Jones f_table[1].frequency = fsb * max_multiplier;
34250701588SViresh Kumar f_table[1].driver_data = (max_multiplier << 8) | max_voltage;
343bb0a56ecSDave Jones f_table[2].frequency = CPUFREQ_TABLE_END;
344bb0a56ecSDave Jones } else {
345bb0a56ecSDave Jones k = 0;
346bb0a56ecSDave Jones step = ((max_voltage - min_voltage) * 256)
347bb0a56ecSDave Jones / (max_multiplier - min_multiplier);
348bb0a56ecSDave Jones for (i = min_multiplier; i <= max_multiplier; i++) {
349bb0a56ecSDave Jones voltage = (k * step) / 256 + min_voltage;
350bb0a56ecSDave Jones f_table[k].frequency = fsb * i;
35150701588SViresh Kumar f_table[k].driver_data = (i << 8) | voltage;
352bb0a56ecSDave Jones k++;
353bb0a56ecSDave Jones }
354bb0a56ecSDave Jones f_table[k].frequency = CPUFREQ_TABLE_END;
355bb0a56ecSDave Jones }
356bb0a56ecSDave Jones
357bb0a56ecSDave Jones policy->cpuinfo.transition_latency = 140000; /* 844mV -> 700mV in ns */
3580d105394SViresh Kumar policy->freq_table = ¢aur->freq_table[0];
359bb0a56ecSDave Jones
360bb0a56ecSDave Jones return 0;
361bb0a56ecSDave Jones }
362bb0a56ecSDave Jones
eps_cpu_exit(struct cpufreq_policy * policy)363bb0a56ecSDave Jones static int eps_cpu_exit(struct cpufreq_policy *policy)
364bb0a56ecSDave Jones {
365bb0a56ecSDave Jones unsigned int cpu = policy->cpu;
366bb0a56ecSDave Jones
367bb0a56ecSDave Jones /* Bye */
368bb0a56ecSDave Jones kfree(eps_cpu[cpu]);
369bb0a56ecSDave Jones eps_cpu[cpu] = NULL;
370bb0a56ecSDave Jones return 0;
371bb0a56ecSDave Jones }
372bb0a56ecSDave Jones
373bb0a56ecSDave Jones static struct cpufreq_driver eps_driver = {
374f51d2ac3SViresh Kumar .verify = cpufreq_generic_frequency_table_verify,
3759c0ebcf7SViresh Kumar .target_index = eps_target,
376bb0a56ecSDave Jones .init = eps_cpu_init,
377bb0a56ecSDave Jones .exit = eps_cpu_exit,
378bb0a56ecSDave Jones .get = eps_get,
379bb0a56ecSDave Jones .name = "e_powersaver",
380f51d2ac3SViresh Kumar .attr = cpufreq_generic_attr,
381bb0a56ecSDave Jones };
382bb0a56ecSDave Jones
383bb0a56ecSDave Jones
384bb0a56ecSDave Jones /* This driver will work only on Centaur C7 processors with
385bb0a56ecSDave Jones * Enhanced SpeedStep/PowerSaver registers */
386fa8031aeSAndi Kleen static const struct x86_cpu_id eps_cpu_id[] = {
387*b11d77faSThomas Gleixner X86_MATCH_VENDOR_FAM_FEATURE(CENTAUR, 6, X86_FEATURE_EST, NULL),
388fa8031aeSAndi Kleen {}
389fa8031aeSAndi Kleen };
390fa8031aeSAndi Kleen MODULE_DEVICE_TABLE(x86cpu, eps_cpu_id);
391bb0a56ecSDave Jones
eps_init(void)392fa8031aeSAndi Kleen static int __init eps_init(void)
393fa8031aeSAndi Kleen {
394fa8031aeSAndi Kleen if (!x86_match_cpu(eps_cpu_id) || boot_cpu_data.x86_model < 10)
395fa8031aeSAndi Kleen return -ENODEV;
396bb0a56ecSDave Jones if (cpufreq_register_driver(&eps_driver))
397bb0a56ecSDave Jones return -EINVAL;
398bb0a56ecSDave Jones return 0;
399bb0a56ecSDave Jones }
400bb0a56ecSDave Jones
eps_exit(void)401bb0a56ecSDave Jones static void __exit eps_exit(void)
402bb0a56ecSDave Jones {
403bb0a56ecSDave Jones cpufreq_unregister_driver(&eps_driver);
404bb0a56ecSDave Jones }
405bb0a56ecSDave Jones
406ed361bf0SRafał Bilski /* Allow user to overclock his machine or to change frequency to higher after
407ed361bf0SRafał Bilski * unloading module */
408ed361bf0SRafał Bilski module_param(freq_failsafe_off, int, 0644);
409ed361bf0SRafał Bilski MODULE_PARM_DESC(freq_failsafe_off, "Disable current vs max frequency check");
410ed361bf0SRafał Bilski module_param(voltage_failsafe_off, int, 0644);
411ed361bf0SRafał Bilski MODULE_PARM_DESC(voltage_failsafe_off, "Disable current vs max voltage check");
4126de0dc4bSJavier Martinez Canillas #if IS_ENABLED(CONFIG_ACPI_PROCESSOR)
41327e954c2SRafał Bilski module_param(ignore_acpi_limit, int, 0644);
41427e954c2SRafał Bilski MODULE_PARM_DESC(ignore_acpi_limit, "Don't check ACPI's processor speed limit");
41527e954c2SRafał Bilski #endif
416826e570bSRafał Bilski module_param(set_max_voltage, int, 0644);
417826e570bSRafał Bilski MODULE_PARM_DESC(set_max_voltage, "Set maximum CPU voltage (mV) C7-M only");
418ed361bf0SRafał Bilski
419bb0a56ecSDave Jones MODULE_AUTHOR("Rafal Bilski <rafalbilski@interia.pl>");
420bb0a56ecSDave Jones MODULE_DESCRIPTION("Enhanced PowerSaver driver for VIA C7 CPU's.");
421bb0a56ecSDave Jones MODULE_LICENSE("GPL");
422bb0a56ecSDave Jones
423bb0a56ecSDave Jones module_init(eps_init);
424bb0a56ecSDave Jones module_exit(eps_exit);
425