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 = ¢aur->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, ¢aur->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(¢aur->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