/*
* (c) 2003, 2004, 2005 Advanced Micro Devices, Inc.
* Your use of this code is subject to the terms and conditions of the
* GNU general public license version 2. See "COPYING" or
* http://www.gnu.org/licenses/gpl.html
*
* Support : mark.langsdorf@amd.com
*
* Based on the powernow-k7.c module written by Dave Jones.
* (C) 2003 Dave Jones <davej@codemonkey.org.uk> on behalf of SuSE Labs
* (C) 2004 Dominik Brodowski <linux@brodo.de>
* (C) 2004 Pavel Machek <pavel@suse.cz>
* Licensed under the terms of the GNU GPL License version 2.
* Based upon datasheets & sample CPUs kindly provided by AMD.
*
* Valuable input gratefully received from Dave Jones, Pavel Machek,
* Dominik Brodowski, and others.
* Originally developed by Paul Devriendt.
* Processor information obtained from Chapter 9 (Power and Thermal Management)
* of the "BIOS and Kernel Developer's Guide for the AMD Athlon 64 and AMD
* Opteron Processors" available for download from www.amd.com
*
* Tables for specific CPUs can be infrerred from
* http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/30430.pdf
*/
#include <linux/kernel.h>
#include <linux/smp.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/cpufreq.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/cpumask.h>
#include <linux/sched.h> /* for current / set_cpus_allowed() */
#include <asm/msr.h>
#include <asm/io.h>
#include <asm/delay.h>
#ifdef CONFIG_X86_POWERNOW_K8_ACPI
#include <linux/acpi.h>
#include <acpi/processor.h>
#endif
#define PFX "powernow-k8: "
#define BFX PFX "BIOS error: "
#define VERSION "version 1.60.1"
#include "powernow-k8.h"
/* serialize freq changes */
static DECLARE_MUTEX(fidvid_sem);
static struct powernow_k8_data *powernow_data[NR_CPUS];
#ifndef CONFIG_SMP
static cpumask_t cpu_core_map[1] = { CPU_MASK_ALL };
#endif
/* Return a frequency in MHz, given an input fid */
static u32 find_freq_from_fid(u32 fid)
{
return 800 + (fid * 100);
}
/* Return a frequency in KHz, given an input fid */
static u32 find_khz_freq_from_fid(u32 fid)
{
return 1000 * find_freq_from_fid(fid);
}
/* Return a voltage in miliVolts, given an input vid */
static u32 find_millivolts_from_vid(struct powernow_k8_data *data, u32 vid)
{
return 1550-vid*25;
}
/* Return the vco fid for an input fid
*
* Each "low" fid has corresponding "high" fid, and you can get to "low" fids
* only from corresponding high fids. This returns "high" fid corresponding to
* "low" one.
*/
static u32 convert_fid_to_vco_fid(u32 fid)
{
if (fid < HI_FID_TABLE_BOTTOM)
return 8 + (2 * fid);
else
return fid;
}
/*
* Return 1 if the pending bit is set. Unless we just instructed the processor
* to transition to a new state, seeing this bit set is really bad news.
*/
static int pending_bit_stuck(void)
{
u32 lo, hi;
rdmsr(MSR_FIDVID_STATUS, lo, hi);
return lo & MSR_S_LO_CHANGE_PENDING ? 1 : 0;
}
/*
* Update the global current fid / vid values from the status msr.
* Returns 1 on error.
*/
static int query_current_values_with_pending_wait(struct powernow_k8_data *data)
{
u32 lo, hi;
u32 i = 0;
do {
if (i++ > 10000) {
dprintk("detected change pending stuck\n");
return 1;
}
rdmsr(MSR_FIDVID_STATUS, lo, hi);
} while (lo & MSR_S_LO_CHANGE_PENDING);
data->currvid = hi & MSR_S_HI_CURRENT_VID;
data->currfid = lo & MSR_S_LO_CURRENT_FID;
return 0;
}
/* the isochronous relief time */
static void count_off_irt(struct powernow_k8_data *data)
{
udelay((1 << data->irt) * 10);
return;
}
/* the voltage stabalization time */
static void count_off_vst(struct powernow_k8_data *data)
{
udelay(data->vstable * VST_UNITS_20US);
return;
}
/* need to init the control msr to a safe value (for each cpu) */
static void fidvid_msr_init(void)
{
u32 lo, hi;
u8 fid, vid;
rdmsr(MSR_FIDVID_STATUS, lo, hi);
vid = hi & MSR_S_HI_CURRENT_VID;
fid