/*
* (c) 2003-2012 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
*
* Maintainer:
* Andreas Herrmann <herrmann.der.user@googlemail.com>
*
* Based on the powernow-k7.c module written by Dave Jones.
* (C) 2003 Dave Jones on behalf of SuSE Labs
* (C) 2004 Dominik Brodowski <linux@brodo.de>
* (C) 2004 Pavel Machek <pavel@ucw.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, Jacob Shin, 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 (BKDG) for
* the AMD Athlon 64 and AMD Opteron Processors" and section "2.x
* Power Management" in BKDGs for newer AMD CPU families.
*
* Tables for specific CPUs can be inferred from AMD's processor
* power and thermal data sheets, (e.g. 30417.pdf, 30430.pdf, 43375.pdf)
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#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/io.h>
#include <linux/delay.h>
#include <asm/msr.h>
#include <asm/cpu_device_id.h>
#include <linux/acpi.h>
#include <linux/mutex.h>
#include <acpi/processor.h>
#define VERSION "version 2.20.00"
#include "powernow-k8.h"
/* serialize freq changes */
static DEFINE_MUTEX(fidvid_mutex);
static DEFINE_PER_CPU(struct powernow_k8_data *, powernow_data);
static struct cpufreq_driver cpufreq_amd64_driver;
#ifndef CONFIG_SMP
static inline const struct cpumask *cpu_core_mask(int cpu)
{
return cpumask_of(0);
}
#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 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) {
pr_debug("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 stabilization time */
static