xref: /openbmc/linux/drivers/cpufreq/e_powersaver.c (revision cdd38c5f1ce4398ec58fec95904b75824daab7b5)
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 = &centaur->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 = &centaur->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