186acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski/*
286acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski *  Based on documentation provided by Dave Jones. Thanks!
386acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski *
486acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski *  Licensed under the terms of the GNU GPL License version 2.
586acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski *
686acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski *  BIG FAT DISCLAIMER: Work in progress code. Possibly *dangerous*
786acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski */
886acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski
986acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski#include <linux/kernel.h>
1086acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski#include <linux/module.h>
1186acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski#include <linux/init.h>
1286acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski#include <linux/cpufreq.h>
1386acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski#include <linux/ioport.h>
1486acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski#include <linux/slab.h>
15c9b8c871525aeda153494996d84a832270205d81Dave Jones#include <linux/timex.h>
16c9b8c871525aeda153494996d84a832270205d81Dave Jones#include <linux/io.h>
17c9b8c871525aeda153494996d84a832270205d81Dave Jones#include <linux/delay.h>
1886acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski
19fa8031aefec0cf7ea6c2387c93610d99d9659aa2Andi Kleen#include <asm/cpu_device_id.h>
2086acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski#include <asm/msr.h>
2186acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski#include <asm/tsc.h>
2286acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski
2327e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilski#if defined CONFIG_ACPI_PROCESSOR || defined CONFIG_ACPI_PROCESSOR_MODULE
2427e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilski#include <linux/acpi.h>
2527e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilski#include <acpi/processor.h>
2627e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilski#endif
2727e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilski
2886acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski#define EPS_BRAND_C7M	0
2986acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski#define EPS_BRAND_C7	1
3086acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski#define EPS_BRAND_EDEN	2
3186acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski#define EPS_BRAND_C3	3
32535ae38c9f4025faadedd17fac5f11e79e1cfdb2Jesse Ahrens#define EPS_BRAND_C7D	4
3386acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski
3486acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilskistruct eps_cpu_data {
3586acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	u32 fsb;
3627e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilski#if defined CONFIG_ACPI_PROCESSOR || defined CONFIG_ACPI_PROCESSOR_MODULE
3727e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilski	u32 bios_limit;
3827e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilski#endif
3986acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	struct cpufreq_frequency_table freq_table[];
4086acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski};
4186acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski
4286acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilskistatic struct eps_cpu_data *eps_cpu[NR_CPUS];
4386acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski
44ed361bf08033f165e0a004f254919e13f07df0aeRafał Bilski/* Module parameters */
45ed361bf08033f165e0a004f254919e13f07df0aeRafał Bilskistatic int freq_failsafe_off;
46ed361bf08033f165e0a004f254919e13f07df0aeRafał Bilskistatic int voltage_failsafe_off;
47826e570bb24de7671be66de7a6f036c304caad1eRafał Bilskistatic int set_max_voltage;
48ed361bf08033f165e0a004f254919e13f07df0aeRafał Bilski
4927e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilski#if defined CONFIG_ACPI_PROCESSOR || defined CONFIG_ACPI_PROCESSOR_MODULE
5027e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilskistatic int ignore_acpi_limit;
5127e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilski
5227e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilskistatic struct acpi_processor_performance *eps_acpi_cpu_perf;
5327e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilski
5427e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilski/* Minimum necessary to get acpi_processor_get_bios_limit() working */
5527e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilskistatic int eps_acpi_init(void)
5627e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilski{
5727e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilski	eps_acpi_cpu_perf = kzalloc(sizeof(struct acpi_processor_performance),
5827e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilski				      GFP_KERNEL);
5927e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilski	if (!eps_acpi_cpu_perf)
6027e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilski		return -ENOMEM;
6127e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilski
6227e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilski	if (!zalloc_cpumask_var(&eps_acpi_cpu_perf->shared_cpu_map,
6327e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilski								GFP_KERNEL)) {
6427e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilski		kfree(eps_acpi_cpu_perf);
6527e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilski		eps_acpi_cpu_perf = NULL;
6627e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilski		return -ENOMEM;
6727e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilski	}
6827e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilski
6927e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilski	if (acpi_processor_register_performance(eps_acpi_cpu_perf, 0)) {
7027e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilski		free_cpumask_var(eps_acpi_cpu_perf->shared_cpu_map);
7127e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilski		kfree(eps_acpi_cpu_perf);
7227e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilski		eps_acpi_cpu_perf = NULL;
7327e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilski		return -EIO;
7427e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilski	}
7527e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilski	return 0;
7627e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilski}
7727e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilski
7827e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilskistatic int eps_acpi_exit(struct cpufreq_policy *policy)
7927e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilski{
8027e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilski	if (eps_acpi_cpu_perf) {
8127e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilski		acpi_processor_unregister_performance(eps_acpi_cpu_perf, 0);
8227e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilski		free_cpumask_var(eps_acpi_cpu_perf->shared_cpu_map);
8327e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilski		kfree(eps_acpi_cpu_perf);
8427e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilski		eps_acpi_cpu_perf = NULL;
8527e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilski	}
8627e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilski	return 0;
8727e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilski}
8827e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilski#endif
8986acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski
9086acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilskistatic unsigned int eps_get(unsigned int cpu)
9186acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski{
9286acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	struct eps_cpu_data *centaur;
9386acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	u32 lo, hi;
9486acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski
9586acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	if (cpu)
9686acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski		return 0;
9786acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	centaur = eps_cpu[cpu];
9886acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	if (centaur == NULL)
9986acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski		return 0;
10086acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski
10186acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	/* Return current frequency */
10286acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
10386acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	return centaur->fsb * ((lo >> 8) & 0xff);
10486acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski}
10586acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski
10686acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilskistatic int eps_set_state(struct eps_cpu_data *centaur,
10786acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski			 unsigned int cpu,
10886acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski			 u32 dest_state)
10986acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski{
11086acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	struct cpufreq_freqs freqs;
11186acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	u32 lo, hi;
11286acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	int err = 0;
11386acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	int i;
11486acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski
11586acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	freqs.old = eps_get(cpu);
11686acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	freqs.new = centaur->fsb * ((dest_state >> 8) & 0xff);
11786acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	freqs.cpu = cpu;
11886acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
11986acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski
12086acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	/* Wait while CPU is busy */
12186acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
12286acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	i = 0;
12386acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	while (lo & ((1 << 16) | (1 << 17))) {
12486acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski		udelay(16);
12586acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski		rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
12686acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski		i++;
12786acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski		if (unlikely(i > 64)) {
12886acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski			err = -ENODEV;
12986acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski			goto postchange;
13086acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski		}
13186acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	}
13286acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	/* Set new multiplier and voltage */
13386acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	wrmsr(MSR_IA32_PERF_CTL, dest_state & 0xffff, 0);
13486acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	/* Wait until transition end */
13586acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	i = 0;
13686acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	do {
13786acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski		udelay(16);
13886acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski		rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
13986acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski		i++;
14086acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski		if (unlikely(i > 64)) {
14186acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski			err = -ENODEV;
14286acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski			goto postchange;
14386acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski		}
14486acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	} while (lo & ((1 << 16) | (1 << 17)));
14586acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski
14686acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	/* Return current frequency */
14786acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilskipostchange:
14886acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
14986acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	freqs.new = centaur->fsb * ((lo >> 8) & 0xff);
15086acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski
1510e5aa8d6218f9914b23e492debf653bda5598af3Dave Jones#ifdef DEBUG
1520e5aa8d6218f9914b23e492debf653bda5598af3Dave Jones	{
1530e5aa8d6218f9914b23e492debf653bda5598af3Dave Jones	u8 current_multiplier, current_voltage;
1540e5aa8d6218f9914b23e492debf653bda5598af3Dave Jones
155535ae38c9f4025faadedd17fac5f11e79e1cfdb2Jesse Ahrens	/* Print voltage and multiplier */
156535ae38c9f4025faadedd17fac5f11e79e1cfdb2Jesse Ahrens	rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
157535ae38c9f4025faadedd17fac5f11e79e1cfdb2Jesse Ahrens	current_voltage = lo & 0xff;
158535ae38c9f4025faadedd17fac5f11e79e1cfdb2Jesse Ahrens	printk(KERN_INFO "eps: Current voltage = %dmV\n",
159535ae38c9f4025faadedd17fac5f11e79e1cfdb2Jesse Ahrens		current_voltage * 16 + 700);
160535ae38c9f4025faadedd17fac5f11e79e1cfdb2Jesse Ahrens	current_multiplier = (lo >> 8) & 0xff;
161535ae38c9f4025faadedd17fac5f11e79e1cfdb2Jesse Ahrens	printk(KERN_INFO "eps: Current multiplier = %d\n",
162535ae38c9f4025faadedd17fac5f11e79e1cfdb2Jesse Ahrens		current_multiplier);
1630e5aa8d6218f9914b23e492debf653bda5598af3Dave Jones	}
1640e5aa8d6218f9914b23e492debf653bda5598af3Dave Jones#endif
16586acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
16686acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	return err;
16786acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski}
16886acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski
16986acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilskistatic int eps_target(struct cpufreq_policy *policy,
17086acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski			       unsigned int target_freq,
17186acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski			       unsigned int relation)
17286acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski{
17386acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	struct eps_cpu_data *centaur;
17486acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	unsigned int newstate = 0;
17586acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	unsigned int cpu = policy->cpu;
17686acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	unsigned int dest_state;
17786acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	int ret;
17886acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski
17986acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	if (unlikely(eps_cpu[cpu] == NULL))
18086acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski		return -ENODEV;
18186acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	centaur = eps_cpu[cpu];
18286acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski
18386acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	if (unlikely(cpufreq_frequency_table_target(policy,
18486acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski			&eps_cpu[cpu]->freq_table[0],
18586acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski			target_freq,
18686acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski			relation,
18786acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski			&newstate))) {
18886acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski		return -EINVAL;
18986acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	}
19086acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski
19186acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	/* Make frequency transition */
19286acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	dest_state = centaur->freq_table[newstate].index & 0xffff;
19386acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	ret = eps_set_state(centaur, cpu, dest_state);
19486acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	if (ret)
19586acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski		printk(KERN_ERR "eps: Timeout!\n");
19686acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	return ret;
19786acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski}
19886acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski
19986acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilskistatic int eps_verify(struct cpufreq_policy *policy)
20086acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski{
20186acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	return cpufreq_frequency_table_verify(policy,
20286acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski			&eps_cpu[policy->cpu]->freq_table[0]);
20386acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski}
20486acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski
20586acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilskistatic int eps_cpu_init(struct cpufreq_policy *policy)
20686acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski{
20786acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	unsigned int i;
20886acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	u32 lo, hi;
20986acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	u64 val;
21086acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	u8 current_multiplier, current_voltage;
21186acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	u8 max_multiplier, max_voltage;
21286acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	u8 min_multiplier, min_voltage;
213535ae38c9f4025faadedd17fac5f11e79e1cfdb2Jesse Ahrens	u8 brand = 0;
21486acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	u32 fsb;
21586acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	struct eps_cpu_data *centaur;
216535ae38c9f4025faadedd17fac5f11e79e1cfdb2Jesse Ahrens	struct cpuinfo_x86 *c = &cpu_data(0);
21786acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	struct cpufreq_frequency_table *f_table;
21886acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	int k, step, voltage;
21986acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	int ret;
22086acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	int states;
22127e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilski#if defined CONFIG_ACPI_PROCESSOR || defined CONFIG_ACPI_PROCESSOR_MODULE
22227e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilski	unsigned int limit;
22327e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilski#endif
22486acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski
22586acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	if (policy->cpu != 0)
22686acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski		return -ENODEV;
22786acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski
22886acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	/* Check brand */
229e19717fe2bb3624d8242f66d3825881d11a847ddDave Jones	printk(KERN_INFO "eps: Detected VIA ");
230535ae38c9f4025faadedd17fac5f11e79e1cfdb2Jesse Ahrens
231535ae38c9f4025faadedd17fac5f11e79e1cfdb2Jesse Ahrens	switch (c->x86_model) {
232535ae38c9f4025faadedd17fac5f11e79e1cfdb2Jesse Ahrens	case 10:
233535ae38c9f4025faadedd17fac5f11e79e1cfdb2Jesse Ahrens		rdmsr(0x1153, lo, hi);
234535ae38c9f4025faadedd17fac5f11e79e1cfdb2Jesse Ahrens		brand = (((lo >> 2) ^ lo) >> 18) & 3;
235535ae38c9f4025faadedd17fac5f11e79e1cfdb2Jesse Ahrens		printk(KERN_CONT "Model A ");
236535ae38c9f4025faadedd17fac5f11e79e1cfdb2Jesse Ahrens		break;
237535ae38c9f4025faadedd17fac5f11e79e1cfdb2Jesse Ahrens	case 13:
238535ae38c9f4025faadedd17fac5f11e79e1cfdb2Jesse Ahrens		rdmsr(0x1154, lo, hi);
239535ae38c9f4025faadedd17fac5f11e79e1cfdb2Jesse Ahrens		brand = (((lo >> 4) ^ (lo >> 2))) & 0x000000ff;
240535ae38c9f4025faadedd17fac5f11e79e1cfdb2Jesse Ahrens		printk(KERN_CONT "Model D ");
241535ae38c9f4025faadedd17fac5f11e79e1cfdb2Jesse Ahrens		break;
242535ae38c9f4025faadedd17fac5f11e79e1cfdb2Jesse Ahrens	}
243535ae38c9f4025faadedd17fac5f11e79e1cfdb2Jesse Ahrens
244c9b8c871525aeda153494996d84a832270205d81Dave Jones	switch (brand) {
24586acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	case EPS_BRAND_C7M:
246e19717fe2bb3624d8242f66d3825881d11a847ddDave Jones		printk(KERN_CONT "C7-M\n");
24786acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski		break;
24886acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	case EPS_BRAND_C7:
249e19717fe2bb3624d8242f66d3825881d11a847ddDave Jones		printk(KERN_CONT "C7\n");
25086acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski		break;
25186acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	case EPS_BRAND_EDEN:
252e19717fe2bb3624d8242f66d3825881d11a847ddDave Jones		printk(KERN_CONT "Eden\n");
25386acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski		break;
254535ae38c9f4025faadedd17fac5f11e79e1cfdb2Jesse Ahrens	case EPS_BRAND_C7D:
255535ae38c9f4025faadedd17fac5f11e79e1cfdb2Jesse Ahrens		printk(KERN_CONT "C7-D\n");
256535ae38c9f4025faadedd17fac5f11e79e1cfdb2Jesse Ahrens		break;
25786acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	case EPS_BRAND_C3:
258e19717fe2bb3624d8242f66d3825881d11a847ddDave Jones		printk(KERN_CONT "C3\n");
25986acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski		return -ENODEV;
26086acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski		break;
26186acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	}
26286acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	/* Enable Enhanced PowerSaver */
26386acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	rdmsrl(MSR_IA32_MISC_ENABLE, val);
264ecab22aa6dc9d42ca52de2cad0854b4c6bd85ac9Vegard Nossum	if (!(val & MSR_IA32_MISC_ENABLE_ENHANCED_SPEEDSTEP)) {
265ecab22aa6dc9d42ca52de2cad0854b4c6bd85ac9Vegard Nossum		val |= MSR_IA32_MISC_ENABLE_ENHANCED_SPEEDSTEP;
26686acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski		wrmsrl(MSR_IA32_MISC_ENABLE, val);
26786acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski		/* Can be locked at 0 */
26886acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski		rdmsrl(MSR_IA32_MISC_ENABLE, val);
269ecab22aa6dc9d42ca52de2cad0854b4c6bd85ac9Vegard Nossum		if (!(val & MSR_IA32_MISC_ENABLE_ENHANCED_SPEEDSTEP)) {
270e19717fe2bb3624d8242f66d3825881d11a847ddDave Jones			printk(KERN_INFO "eps: Can't enable Enhanced PowerSaver\n");
27186acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski			return -ENODEV;
27286acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski		}
27386acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	}
27486acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski
27586acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	/* Print voltage and multiplier */
27686acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
27786acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	current_voltage = lo & 0xff;
278c9b8c871525aeda153494996d84a832270205d81Dave Jones	printk(KERN_INFO "eps: Current voltage = %dmV\n",
279c9b8c871525aeda153494996d84a832270205d81Dave Jones			current_voltage * 16 + 700);
28086acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	current_multiplier = (lo >> 8) & 0xff;
281e19717fe2bb3624d8242f66d3825881d11a847ddDave Jones	printk(KERN_INFO "eps: Current multiplier = %d\n", current_multiplier);
28286acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski
28386acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	/* Print limits */
28486acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	max_voltage = hi & 0xff;
285c9b8c871525aeda153494996d84a832270205d81Dave Jones	printk(KERN_INFO "eps: Highest voltage = %dmV\n",
286c9b8c871525aeda153494996d84a832270205d81Dave Jones			max_voltage * 16 + 700);
28786acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	max_multiplier = (hi >> 8) & 0xff;
288e19717fe2bb3624d8242f66d3825881d11a847ddDave Jones	printk(KERN_INFO "eps: Highest multiplier = %d\n", max_multiplier);
28986acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	min_voltage = (hi >> 16) & 0xff;
290c9b8c871525aeda153494996d84a832270205d81Dave Jones	printk(KERN_INFO "eps: Lowest voltage = %dmV\n",
291c9b8c871525aeda153494996d84a832270205d81Dave Jones			min_voltage * 16 + 700);
29286acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	min_multiplier = (hi >> 24) & 0xff;
293e19717fe2bb3624d8242f66d3825881d11a847ddDave Jones	printk(KERN_INFO "eps: Lowest multiplier = %d\n", min_multiplier);
29486acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski
29586acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	/* Sanity checks */
29686acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	if (current_multiplier == 0 || max_multiplier == 0
29786acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	    || min_multiplier == 0)
29886acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski		return -EINVAL;
29986acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	if (current_multiplier > max_multiplier
30086acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	    || max_multiplier <= min_multiplier)
30186acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski		return -EINVAL;
302535ae38c9f4025faadedd17fac5f11e79e1cfdb2Jesse Ahrens	if (current_voltage > 0x1f || max_voltage > 0x1f)
30386acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski		return -EINVAL;
304ed361bf08033f165e0a004f254919e13f07df0aeRafał Bilski	if (max_voltage < min_voltage
305ed361bf08033f165e0a004f254919e13f07df0aeRafał Bilski	    || current_voltage < min_voltage
306ed361bf08033f165e0a004f254919e13f07df0aeRafał Bilski	    || current_voltage > max_voltage)
30786acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski		return -EINVAL;
30886acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski
309ed361bf08033f165e0a004f254919e13f07df0aeRafał Bilski	/* Check for systems using underclocked CPU */
310ed361bf08033f165e0a004f254919e13f07df0aeRafał Bilski	if (!freq_failsafe_off && max_multiplier != current_multiplier) {
311ed361bf08033f165e0a004f254919e13f07df0aeRafał Bilski		printk(KERN_INFO "eps: Your processor is running at different "
312ed361bf08033f165e0a004f254919e13f07df0aeRafał Bilski			"frequency then its maximum. Aborting.\n");
313ed361bf08033f165e0a004f254919e13f07df0aeRafał Bilski		printk(KERN_INFO "eps: You can use freq_failsafe_off option "
314ed361bf08033f165e0a004f254919e13f07df0aeRafał Bilski			"to disable this check.\n");
315ed361bf08033f165e0a004f254919e13f07df0aeRafał Bilski		return -EINVAL;
316ed361bf08033f165e0a004f254919e13f07df0aeRafał Bilski	}
317ed361bf08033f165e0a004f254919e13f07df0aeRafał Bilski	if (!voltage_failsafe_off && max_voltage != current_voltage) {
318ed361bf08033f165e0a004f254919e13f07df0aeRafał Bilski		printk(KERN_INFO "eps: Your processor is running at different "
319ed361bf08033f165e0a004f254919e13f07df0aeRafał Bilski			"voltage then its maximum. Aborting.\n");
320ed361bf08033f165e0a004f254919e13f07df0aeRafał Bilski		printk(KERN_INFO "eps: You can use voltage_failsafe_off "
321ed361bf08033f165e0a004f254919e13f07df0aeRafał Bilski			"option to disable this check.\n");
322ed361bf08033f165e0a004f254919e13f07df0aeRafał Bilski		return -EINVAL;
323ed361bf08033f165e0a004f254919e13f07df0aeRafał Bilski	}
324ed361bf08033f165e0a004f254919e13f07df0aeRafał Bilski
32586acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	/* Calc FSB speed */
32686acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	fsb = cpu_khz / current_multiplier;
32727e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilski
32827e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilski#if defined CONFIG_ACPI_PROCESSOR || defined CONFIG_ACPI_PROCESSOR_MODULE
32927e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilski	/* Check for ACPI processor speed limit */
33027e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilski	if (!ignore_acpi_limit && !eps_acpi_init()) {
33127e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilski		if (!acpi_processor_get_bios_limit(policy->cpu, &limit)) {
33227e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilski			printk(KERN_INFO "eps: ACPI limit %u.%uGHz\n",
33327e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilski				limit/1000000,
33427e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilski				(limit%1000000)/10000);
33527e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilski			eps_acpi_exit(policy);
33627e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilski			/* Check if max_multiplier is in BIOS limits */
33727e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilski			if (limit && max_multiplier * fsb > limit) {
33827e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilski				printk(KERN_INFO "eps: Aborting.\n");
33927e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilski				return -EINVAL;
34027e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilski			}
34127e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilski		}
34227e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilski	}
34327e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilski#endif
34427e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilski
345826e570bb24de7671be66de7a6f036c304caad1eRafał Bilski	/* Allow user to set lower maximum voltage then that reported
346826e570bb24de7671be66de7a6f036c304caad1eRafał Bilski	 * by processor */
347826e570bb24de7671be66de7a6f036c304caad1eRafał Bilski	if (brand == EPS_BRAND_C7M && set_max_voltage) {
348826e570bb24de7671be66de7a6f036c304caad1eRafał Bilski		u32 v;
349826e570bb24de7671be66de7a6f036c304caad1eRafał Bilski
350826e570bb24de7671be66de7a6f036c304caad1eRafał Bilski		/* Change mV to something hardware can use */
351826e570bb24de7671be66de7a6f036c304caad1eRafał Bilski		v = (set_max_voltage - 700) / 16;
352826e570bb24de7671be66de7a6f036c304caad1eRafał Bilski		/* Check if voltage is within limits */
353826e570bb24de7671be66de7a6f036c304caad1eRafał Bilski		if (v >= min_voltage && v <= max_voltage) {
354826e570bb24de7671be66de7a6f036c304caad1eRafał Bilski			printk(KERN_INFO "eps: Setting %dmV as maximum.\n",
355826e570bb24de7671be66de7a6f036c304caad1eRafał Bilski				v * 16 + 700);
356826e570bb24de7671be66de7a6f036c304caad1eRafał Bilski			max_voltage = v;
357826e570bb24de7671be66de7a6f036c304caad1eRafał Bilski		}
358826e570bb24de7671be66de7a6f036c304caad1eRafał Bilski	}
359826e570bb24de7671be66de7a6f036c304caad1eRafał Bilski
36086acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	/* Calc number of p-states supported */
36186acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	if (brand == EPS_BRAND_C7M)
36286acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski		states = max_multiplier - min_multiplier + 1;
36386acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	else
36486acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski		states = 2;
36586acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski
36686acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	/* Allocate private data and frequency table for current cpu */
36786acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	centaur = kzalloc(sizeof(struct eps_cpu_data)
36886acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski		    + (states + 1) * sizeof(struct cpufreq_frequency_table),
36986acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski		    GFP_KERNEL);
37086acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	if (!centaur)
37186acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski		return -ENOMEM;
37286acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	eps_cpu[0] = centaur;
37386acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski
37486acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	/* Copy basic values */
37586acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	centaur->fsb = fsb;
37627e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilski#if defined CONFIG_ACPI_PROCESSOR || defined CONFIG_ACPI_PROCESSOR_MODULE
37727e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilski	centaur->bios_limit = limit;
37827e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilski#endif
37986acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski
38086acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	/* Fill frequency and MSR value table */
38186acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	f_table = &centaur->freq_table[0];
382b6f45a4b071d77777d70e097d429273aeedff717Rafa� Bilski	if (brand != EPS_BRAND_C7M) {
38386acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski		f_table[0].frequency = fsb * min_multiplier;
38486acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski		f_table[0].index = (min_multiplier << 8) | min_voltage;
38586acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski		f_table[1].frequency = fsb * max_multiplier;
38686acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski		f_table[1].index = (max_multiplier << 8) | max_voltage;
38786acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski		f_table[2].frequency = CPUFREQ_TABLE_END;
38886acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	} else {
38986acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski		k = 0;
39086acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski		step = ((max_voltage - min_voltage) * 256)
39186acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski			/ (max_multiplier - min_multiplier);
39286acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski		for (i = min_multiplier; i <= max_multiplier; i++) {
39386acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski			voltage = (k * step) / 256 + min_voltage;
39486acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski			f_table[k].frequency = fsb * i;
39586acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski			f_table[k].index = (i << 8) | voltage;
39686acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski			k++;
39786acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski		}
39886acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski		f_table[k].frequency = CPUFREQ_TABLE_END;
39986acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	}
40086acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski
40186acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	policy->cpuinfo.transition_latency = 140000; /* 844mV -> 700mV in ns */
40286acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	policy->cur = fsb * current_multiplier;
40386acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski
40486acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	ret = cpufreq_frequency_table_cpuinfo(policy, &centaur->freq_table[0]);
40586acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	if (ret) {
40686acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski		kfree(centaur);
40786acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski		return ret;
40886acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	}
40986acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski
41086acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	cpufreq_frequency_table_get_attr(&centaur->freq_table[0], policy->cpu);
41186acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	return 0;
41286acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski}
41386acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski
41486acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilskistatic int eps_cpu_exit(struct cpufreq_policy *policy)
41586acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski{
41686acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	unsigned int cpu = policy->cpu;
41786acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski
41886acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	/* Bye */
41986acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	cpufreq_frequency_table_put_attr(policy->cpu);
42086acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	kfree(eps_cpu[cpu]);
42186acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	eps_cpu[cpu] = NULL;
42286acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	return 0;
42386acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski}
42486acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski
425c9b8c871525aeda153494996d84a832270205d81Dave Jonesstatic struct freq_attr *eps_attr[] = {
42686acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	&cpufreq_freq_attr_scaling_available_freqs,
42786acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	NULL,
42886acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski};
42986acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski
430221dee285ee38099b82437531bcae9fa9cb64cc4Linus Torvaldsstatic struct cpufreq_driver eps_driver = {
43186acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	.verify		= eps_verify,
43286acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	.target		= eps_target,
43386acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	.init		= eps_cpu_init,
43486acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	.exit		= eps_cpu_exit,
43586acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	.get		= eps_get,
43686acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	.name		= "e_powersaver",
43786acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	.owner		= THIS_MODULE,
43886acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	.attr		= eps_attr,
43986acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski};
44086acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski
441fa8031aefec0cf7ea6c2387c93610d99d9659aa2Andi Kleen
442fa8031aefec0cf7ea6c2387c93610d99d9659aa2Andi Kleen/* This driver will work only on Centaur C7 processors with
443fa8031aefec0cf7ea6c2387c93610d99d9659aa2Andi Kleen * Enhanced SpeedStep/PowerSaver registers */
444fa8031aefec0cf7ea6c2387c93610d99d9659aa2Andi Kleenstatic const struct x86_cpu_id eps_cpu_id[] = {
445fa8031aefec0cf7ea6c2387c93610d99d9659aa2Andi Kleen	{ X86_VENDOR_CENTAUR, 6, X86_MODEL_ANY, X86_FEATURE_EST },
446fa8031aefec0cf7ea6c2387c93610d99d9659aa2Andi Kleen	{}
447fa8031aefec0cf7ea6c2387c93610d99d9659aa2Andi Kleen};
448fa8031aefec0cf7ea6c2387c93610d99d9659aa2Andi KleenMODULE_DEVICE_TABLE(x86cpu, eps_cpu_id);
449fa8031aefec0cf7ea6c2387c93610d99d9659aa2Andi Kleen
45086acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilskistatic int __init eps_init(void)
45186acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski{
452fa8031aefec0cf7ea6c2387c93610d99d9659aa2Andi Kleen	if (!x86_match_cpu(eps_cpu_id) || boot_cpu_data.x86_model < 10)
45386acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski		return -ENODEV;
45486acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	if (cpufreq_register_driver(&eps_driver))
45586acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski		return -EINVAL;
45686acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	return 0;
45786acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski}
45886acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski
45986acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilskistatic void __exit eps_exit(void)
46086acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski{
46186acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski	cpufreq_unregister_driver(&eps_driver);
46286acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski}
46386acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski
464ed361bf08033f165e0a004f254919e13f07df0aeRafał Bilski/* Allow user to overclock his machine or to change frequency to higher after
465ed361bf08033f165e0a004f254919e13f07df0aeRafał Bilski * unloading module */
466ed361bf08033f165e0a004f254919e13f07df0aeRafał Bilskimodule_param(freq_failsafe_off, int, 0644);
467ed361bf08033f165e0a004f254919e13f07df0aeRafał BilskiMODULE_PARM_DESC(freq_failsafe_off, "Disable current vs max frequency check");
468ed361bf08033f165e0a004f254919e13f07df0aeRafał Bilskimodule_param(voltage_failsafe_off, int, 0644);
469ed361bf08033f165e0a004f254919e13f07df0aeRafał BilskiMODULE_PARM_DESC(voltage_failsafe_off, "Disable current vs max voltage check");
47027e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilski#if defined CONFIG_ACPI_PROCESSOR || defined CONFIG_ACPI_PROCESSOR_MODULE
47127e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilskimodule_param(ignore_acpi_limit, int, 0644);
47227e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał BilskiMODULE_PARM_DESC(ignore_acpi_limit, "Don't check ACPI's processor speed limit");
47327e954c241673d2437448bd8bf0eaa7cd81a4b15Rafał Bilski#endif
474826e570bb24de7671be66de7a6f036c304caad1eRafał Bilskimodule_param(set_max_voltage, int, 0644);
475826e570bb24de7671be66de7a6f036c304caad1eRafał BilskiMODULE_PARM_DESC(set_max_voltage, "Set maximum CPU voltage (mV) C7-M only");
476ed361bf08033f165e0a004f254919e13f07df0aeRafał Bilski
477c9b8c871525aeda153494996d84a832270205d81Dave JonesMODULE_AUTHOR("Rafal Bilski <rafalbilski@interia.pl>");
47886acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� BilskiMODULE_DESCRIPTION("Enhanced PowerSaver driver for VIA C7 CPU's.");
47986acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� BilskiMODULE_LICENSE("GPL");
48086acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilski
48186acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilskimodule_init(eps_init);
48286acd49aa128bd7a1d4362c256c21fbdc2d5b1a0Rafa� Bilskimodule_exit(eps_exit);
483