1
0
Fork 0
alistair23-linux/drivers/cpufreq/powernow-k7.c

697 lines
16 KiB
C
Raw Normal View History

// SPDX-License-Identifier: GPL-2.0-only
/*
* AMD K7 Powernow driver.
* (C) 2003 Dave Jones on behalf of SuSE Labs.
*
* Based upon datasheets & sample CPUs kindly provided by AMD.
*
* Errata 5:
* CPU may fail to execute a FID/VID change in presence of interrupt.
* - We cli/sti on stepping A0 CPUs around the FID/VID transition.
* Errata 15:
* CPU with half frequency multipliers may hang upon wakeup from disconnect.
* - We disable half multipliers if ACPI is used on A0 stepping CPUs.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/cpufreq.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/dmi.h>
#include <linux/timex.h>
#include <linux/io.h>
#include <asm/timer.h> /* Needed for recalibrate_cpu_khz() */
#include <asm/msr.h>
#include <asm/cpu_device_id.h>
#ifdef CONFIG_X86_POWERNOW_K7_ACPI
#include <linux/acpi.h>
#include <acpi/processor.h>
#endif
#include "powernow-k7.h"
struct psb_s {
u8 signature[10];
u8 tableversion;
u8 flags;
u16 settlingtime;
u8 reserved1;
u8 numpst;
};
struct pst_s {
u32 cpuid;
u8 fsbspeed;
u8 maxfid;
u8 startvid;
u8 numpstates;
};
#ifdef CONFIG_X86_POWERNOW_K7_ACPI
union powernow_acpi_control_t {
struct {
unsigned long fid:5,
vid:5,
sgtc:20,
res1:2;
} bits;
unsigned long val;
};
#endif
/* divide by 1000 to get VCore voltage in V. */
static const int mobile_vid_table[32] = {
2000, 1950, 1900, 1850, 1800, 1750, 1700, 1650,
1600, 1550, 1500, 1450, 1400, 1350, 1300, 0,
1275, 1250, 1225, 1200, 1175, 1150, 1125, 1100,
1075, 1050, 1025, 1000, 975, 950, 925, 0,
};
/* divide by 10 to get FID. */
static const int fid_codes[32] = {
110, 115, 120, 125, 50, 55, 60, 65,
70, 75, 80, 85, 90, 95, 100, 105,
30, 190, 40, 200, 130, 135, 140, 210,
150, 225, 160, 165, 170, 180, -1, -1,
};
/* This parameter is used in order to force ACPI instead of legacy method for
* configuration purpose.
*/
static int acpi_force;
static struct cpufreq_frequency_table *powernow_table;
static unsigned int can_scale_bus;
static unsigned int can_scale_vid;
static unsigned int minimum_speed = -1;
static unsigned int maximum_speed;
static unsigned int number_scales;
static unsigned int fsb;
static unsigned int latency;
static char have_a0;
static int check_fsb(unsigned int fsbspeed)
{
int delta;
unsigned int f = fsb / 1000;
delta = (fsbspeed > f) ? fsbspeed - f : f - fsbspeed;
return delta < 5;
}
static const struct x86_cpu_id powernow_k7_cpuids[] = {
{ X86_VENDOR_AMD, 6, },
{}
};
MODULE_DEVICE_TABLE(x86cpu, powernow_k7_cpuids);
static int check_powernow(void)
{
struct cpuinfo_x86 *c = &cpu_data(0);
unsigned int maxei, eax, ebx, ecx, edx;
if (!x86_match_cpu(powernow_k7_cpuids))
return 0;
/* Get maximum capabilities */
maxei = cpuid_eax(0x80000000);
if (maxei < 0x80000007) { /* Any powernow info ? */
#ifdef MODULE
pr_info("No powernow capabilities detected\n");
#endif
return 0;
}
if ((c->x86_model == 6) && (c->x86_stepping == 0)) {
pr_info("K7 660[A0] core detected, enabling errata workarounds\n");
have_a0 = 1;
}
cpuid(0x80000007, &eax, &ebx, &ecx, &edx);
/* Check we can actually do something before we say anything.*/
if (!(edx & (1 << 1 | 1 << 2)))
return 0;
pr_info("PowerNOW! Technology present. Can scale: ");
if (edx & 1 << 1) {
pr_cont("frequency");
can_scale_bus = 1;
}
if ((edx & (1 << 1 | 1 << 2)) == 0x6)
pr_cont(" and ");
if (edx & 1 << 2) {
pr_cont("voltage");
can_scale_vid = 1;
}
pr_cont("\n");
return 1;
}
#ifdef CONFIG_X86_POWERNOW_K7_ACPI
static void invalidate_entry(unsigned int entry)
{
powernow_table[entry].frequency = CPUFREQ_ENTRY_INVALID;
}
#endif
static int get_ranges(unsigned char *pst)
{
unsigned int j;
unsigned int speed;
u8 fid, vid;
powernow_table = kzalloc((sizeof(*powernow_table) *
(number_scales + 1)), GFP_KERNEL);
if (!powernow_table)
return -ENOMEM;
for (j = 0 ; j < number_scales; j++) {
fid = *pst++;
powernow_table[j].frequency = (fsb * fid_codes[fid]) / 10;
powernow_table[j].driver_data = fid; /* lower 8 bits */
speed = powernow_table[j].frequency;
if ((fid_codes[fid] % 10) == 5) {
#ifdef CONFIG_X86_POWERNOW_K7_ACPI
if (have_a0 == 1)
invalidate_entry(j);
#endif
}
if (speed < minimum_speed)
minimum_speed = speed;
if (speed > maximum_speed)
maximum_speed = speed;
vid = *pst++;
powernow_table[j].driver_data |= (vid << 8); /* upper 8 bits */
pr_debug(" FID: 0x%x (%d.%dx [%dMHz]) "
"VID: 0x%x (%d.%03dV)\n", fid, fid_codes[fid] / 10,
fid_codes[fid] % 10, speed/1000, vid,
mobile_vid_table[vid]/1000,
mobile_vid_table[vid]%1000);
}
powernow_table[number_scales].frequency = CPUFREQ_TABLE_END;
powernow_table[number_scales].driver_data = 0;
return 0;
}
static void change_FID(int fid)
{
union msr_fidvidctl fidvidctl;
rdmsrl(MSR_K7_FID_VID_CTL, fidvidctl.val);
if (fidvidctl.bits.FID != fid) {
fidvidctl.bits.SGTC = latency;
fidvidctl.bits.FID = fid;
fidvidctl.bits.VIDC = 0;
fidvidctl.bits.FIDC = 1;
wrmsrl(MSR_K7_FID_VID_CTL, fidvidctl.val);
}
}
static void change_VID(int vid)
{
union msr_fidvidctl fidvidctl;
rdmsrl(MSR_K7_FID_VID_CTL, fidvidctl.val);
if (fidvidctl.bits.VID != vid) {
fidvidctl.bits.SGTC = latency;
fidvidctl.bits.VID = vid;
fidvidctl.bits.FIDC = 0;
fidvidctl.bits.VIDC = 1;
wrmsrl(MSR_K7_FID_VID_CTL, fidvidctl.val);
}
}
cpufreq: Implement light weight ->target_index() routine Currently, the prototype of cpufreq_drivers target routines is: int target(struct cpufreq_policy *policy, unsigned int target_freq, unsigned int relation); And most of the drivers call cpufreq_frequency_table_target() to get a valid index of their frequency table which is closest to the target_freq. And they don't use target_freq and relation after that. So, it makes sense to just do this work in cpufreq core before calling cpufreq_frequency_table_target() and simply pass index instead. But this can be done only with drivers which expose their frequency table with cpufreq core. For others we need to stick with the old prototype of target() until those drivers are converted to expose frequency tables. This patch implements the new light weight prototype for target_index() routine. It looks like this: int target_index(struct cpufreq_policy *policy, unsigned int index); CPUFreq core will call cpufreq_frequency_table_target() before calling this routine and pass index to it. Because CPUFreq core now requires to call routines present in freq_table.c CONFIG_CPU_FREQ_TABLE must be enabled all the time. This also marks target() interface as deprecated. So, that new drivers avoid using it. And Documentation is updated accordingly. It also converts existing .target() to newly defined light weight .target_index() routine for many driver. Acked-by: Hans-Christian Egtvedt <egtvedt@samfundet.no> Acked-by: Jesper Nilsson <jesper.nilsson@axis.com> Acked-by: Linus Walleij <linus.walleij@linaro.org> Acked-by: Russell King <linux@arm.linux.org.uk> Acked-by: David S. Miller <davem@davemloft.net> Tested-by: Andrew Lunn <andrew@lunn.ch> Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Rafael J. Wysocki <rjw@rjwysocki.net>
2013-10-25 08:15:48 -06:00
static int powernow_target(struct cpufreq_policy *policy, unsigned int index)
{
u8 fid, vid;
struct cpufreq_freqs freqs;
union msr_fidvidstatus fidvidstatus;
int cfid;
/* fid are the lower 8 bits of the index we stored into
* the cpufreq frequency table in powernow_decode_bios,
* vid are the upper 8 bits.
*/
fid = powernow_table[index].driver_data & 0xFF;
vid = (powernow_table[index].driver_data & 0xFF00) >> 8;
rdmsrl(MSR_K7_FID_VID_STATUS, fidvidstatus.val);
cfid = fidvidstatus.bits.CFID;
freqs.old = fsb * fid_codes[cfid] / 10;
freqs.new = powernow_table[index].frequency;
/* Now do the magic poking into the MSRs. */
if (have_a0 == 1) /* A0 errata 5 */
local_irq_disable();
if (freqs.old > freqs.new) {
/* Going down, so change FID first */
change_FID(fid);
change_VID(vid);
} else {
/* Going up, so change VID first */
change_VID(vid);
change_FID(fid);
}
if (have_a0 == 1)
local_irq_enable();
cpufreq: Implement light weight ->target_index() routine Currently, the prototype of cpufreq_drivers target routines is: int target(struct cpufreq_policy *policy, unsigned int target_freq, unsigned int relation); And most of the drivers call cpufreq_frequency_table_target() to get a valid index of their frequency table which is closest to the target_freq. And they don't use target_freq and relation after that. So, it makes sense to just do this work in cpufreq core before calling cpufreq_frequency_table_target() and simply pass index instead. But this can be done only with drivers which expose their frequency table with cpufreq core. For others we need to stick with the old prototype of target() until those drivers are converted to expose frequency tables. This patch implements the new light weight prototype for target_index() routine. It looks like this: int target_index(struct cpufreq_policy *policy, unsigned int index); CPUFreq core will call cpufreq_frequency_table_target() before calling this routine and pass index to it. Because CPUFreq core now requires to call routines present in freq_table.c CONFIG_CPU_FREQ_TABLE must be enabled all the time. This also marks target() interface as deprecated. So, that new drivers avoid using it. And Documentation is updated accordingly. It also converts existing .target() to newly defined light weight .target_index() routine for many driver. Acked-by: Hans-Christian Egtvedt <egtvedt@samfundet.no> Acked-by: Jesper Nilsson <jesper.nilsson@axis.com> Acked-by: Linus Walleij <linus.walleij@linaro.org> Acked-by: Russell King <linux@arm.linux.org.uk> Acked-by: David S. Miller <davem@davemloft.net> Tested-by: Andrew Lunn <andrew@lunn.ch> Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Rafael J. Wysocki <rjw@rjwysocki.net>
2013-10-25 08:15:48 -06:00
return 0;
}
#ifdef CONFIG_X86_POWERNOW_K7_ACPI
static struct acpi_processor_performance *acpi_processor_perf;
static int powernow_acpi_init(void)
{
int i;
int retval = 0;
union powernow_acpi_control_t pc;
if (acpi_processor_perf != NULL && powernow_table != NULL) {
retval = -EINVAL;
goto err0;
}
acpi_processor_perf = kzalloc(sizeof(*acpi_processor_perf), GFP_KERNEL);
if (!acpi_processor_perf) {
retval = -ENOMEM;
goto err0;
}
if (!zalloc_cpumask_var(&acpi_processor_perf->shared_cpu_map,
GFP_KERNEL)) {
retval = -ENOMEM;
goto err05;
}
if (acpi_processor_register_performance(acpi_processor_perf, 0)) {
retval = -EIO;
goto err1;
}
if (acpi_processor_perf->control_register.space_id !=
ACPI_ADR_SPACE_FIXED_HARDWARE) {
retval = -ENODEV;
goto err2;
}
if (acpi_processor_perf->status_register.space_id !=
ACPI_ADR_SPACE_FIXED_HARDWARE) {
retval = -ENODEV;
goto err2;
}
number_scales = acpi_processor_perf->state_count;
if (number_scales < 2) {
retval = -ENODEV;
goto err2;
}
powernow_table = kzalloc((sizeof(*powernow_table) *
(number_scales + 1)), GFP_KERNEL);
if (!powernow_table) {
retval = -ENOMEM;
goto err2;
}
pc.val = (unsigned long) acpi_processor_perf->states[0].control;
for (i = 0; i < number_scales; i++) {
u8 fid, vid;
[CPUFREQ] powernow-k7: fix MHz rounding issue with perflib When the PST tables are broken, powernow-k7 uses ACPI's processor_perflib to deduce the available frequency multipliers from the _PSS tables. Upon frequency change, processor_perflib performs some verification on the frequency (checks that it's within allowable bounds). powernow-k7 deals with absolute frequencies in KHz, whereas perflib only deals with MHz values. When performing the above verification, perflib multiplies the MHz values by 1000 to obtain the KHz value. We then end up with situations like the following: - powernow-k7 multiplies the multiplier by the FSB, and obtains a value such as 1266768 KHz - perflib belives the same state has frequency of 1266 MHz - acpi_processor_ppc_notifier calls cpufreq_verify_within_limits to verify that 1266768 is in the allowable range of 0 to 1266000 (i.e. 1266 * 1000) - it's not, so that frequency is rejected - the maximum CPU frequency is not reachable This patch solves the problem by rounding up the MHz values stored in perflib's tables. Additionally it corrects a broken URL. It also fixes http://bugzilla.kernel.org/show_bug.cgi?id=8255 although this case is a bit different: the frequencies in the _PSS tables are wildly wrong, but we get better results if we force ACPI to respect the fsb * multiplier calculations (even though it seems that the multiplier values aren't entirely correct either). Signed-off-by: Daniel Drake <dsd@gentoo.org> Signed-off-by: Dave Jones <davej@redhat.com>
2007-05-02 16:19:05 -06:00
struct acpi_processor_px *state =
&acpi_processor_perf->states[i];
unsigned int speed, speed_mhz;
[CPUFREQ] powernow-k7: fix MHz rounding issue with perflib When the PST tables are broken, powernow-k7 uses ACPI's processor_perflib to deduce the available frequency multipliers from the _PSS tables. Upon frequency change, processor_perflib performs some verification on the frequency (checks that it's within allowable bounds). powernow-k7 deals with absolute frequencies in KHz, whereas perflib only deals with MHz values. When performing the above verification, perflib multiplies the MHz values by 1000 to obtain the KHz value. We then end up with situations like the following: - powernow-k7 multiplies the multiplier by the FSB, and obtains a value such as 1266768 KHz - perflib belives the same state has frequency of 1266 MHz - acpi_processor_ppc_notifier calls cpufreq_verify_within_limits to verify that 1266768 is in the allowable range of 0 to 1266000 (i.e. 1266 * 1000) - it's not, so that frequency is rejected - the maximum CPU frequency is not reachable This patch solves the problem by rounding up the MHz values stored in perflib's tables. Additionally it corrects a broken URL. It also fixes http://bugzilla.kernel.org/show_bug.cgi?id=8255 although this case is a bit different: the frequencies in the _PSS tables are wildly wrong, but we get better results if we force ACPI to respect the fsb * multiplier calculations (even though it seems that the multiplier values aren't entirely correct either). Signed-off-by: Daniel Drake <dsd@gentoo.org> Signed-off-by: Dave Jones <davej@redhat.com>
2007-05-02 16:19:05 -06:00
pc.val = (unsigned long) state->control;
pr_debug("acpi: P%d: %d MHz %d mW %d uS control %08x SGTC %d\n",
i,
[CPUFREQ] powernow-k7: fix MHz rounding issue with perflib When the PST tables are broken, powernow-k7 uses ACPI's processor_perflib to deduce the available frequency multipliers from the _PSS tables. Upon frequency change, processor_perflib performs some verification on the frequency (checks that it's within allowable bounds). powernow-k7 deals with absolute frequencies in KHz, whereas perflib only deals with MHz values. When performing the above verification, perflib multiplies the MHz values by 1000 to obtain the KHz value. We then end up with situations like the following: - powernow-k7 multiplies the multiplier by the FSB, and obtains a value such as 1266768 KHz - perflib belives the same state has frequency of 1266 MHz - acpi_processor_ppc_notifier calls cpufreq_verify_within_limits to verify that 1266768 is in the allowable range of 0 to 1266000 (i.e. 1266 * 1000) - it's not, so that frequency is rejected - the maximum CPU frequency is not reachable This patch solves the problem by rounding up the MHz values stored in perflib's tables. Additionally it corrects a broken URL. It also fixes http://bugzilla.kernel.org/show_bug.cgi?id=8255 although this case is a bit different: the frequencies in the _PSS tables are wildly wrong, but we get better results if we force ACPI to respect the fsb * multiplier calculations (even though it seems that the multiplier values aren't entirely correct either). Signed-off-by: Daniel Drake <dsd@gentoo.org> Signed-off-by: Dave Jones <davej@redhat.com>
2007-05-02 16:19:05 -06:00
(u32) state->core_frequency,
(u32) state->power,
(u32) state->transition_latency,
(u32) state->control,
pc.bits.sgtc);
vid = pc.bits.vid;
fid = pc.bits.fid;
powernow_table[i].frequency = fsb * fid_codes[fid] / 10;
powernow_table[i].driver_data = fid; /* lower 8 bits */
powernow_table[i].driver_data |= (vid << 8); /* upper 8 bits */
speed = powernow_table[i].frequency;
[CPUFREQ] powernow-k7: fix MHz rounding issue with perflib When the PST tables are broken, powernow-k7 uses ACPI's processor_perflib to deduce the available frequency multipliers from the _PSS tables. Upon frequency change, processor_perflib performs some verification on the frequency (checks that it's within allowable bounds). powernow-k7 deals with absolute frequencies in KHz, whereas perflib only deals with MHz values. When performing the above verification, perflib multiplies the MHz values by 1000 to obtain the KHz value. We then end up with situations like the following: - powernow-k7 multiplies the multiplier by the FSB, and obtains a value such as 1266768 KHz - perflib belives the same state has frequency of 1266 MHz - acpi_processor_ppc_notifier calls cpufreq_verify_within_limits to verify that 1266768 is in the allowable range of 0 to 1266000 (i.e. 1266 * 1000) - it's not, so that frequency is rejected - the maximum CPU frequency is not reachable This patch solves the problem by rounding up the MHz values stored in perflib's tables. Additionally it corrects a broken URL. It also fixes http://bugzilla.kernel.org/show_bug.cgi?id=8255 although this case is a bit different: the frequencies in the _PSS tables are wildly wrong, but we get better results if we force ACPI to respect the fsb * multiplier calculations (even though it seems that the multiplier values aren't entirely correct either). Signed-off-by: Daniel Drake <dsd@gentoo.org> Signed-off-by: Dave Jones <davej@redhat.com>
2007-05-02 16:19:05 -06:00
speed_mhz = speed / 1000;
/* processor_perflib will multiply the MHz value by 1000 to
* get a KHz value (e.g. 1266000). However, powernow-k7 works
* with true KHz values (e.g. 1266768). To ensure that all
* powernow frequencies are available, we must ensure that
* ACPI doesn't restrict them, so we round up the MHz value
* to ensure that perflib's computed KHz value is greater than
* or equal to powernow's KHz value.
*/
if (speed % 1000 > 0)
speed_mhz++;
if ((fid_codes[fid] % 10) == 5) {
if (have_a0 == 1)
invalidate_entry(i);
}
pr_debug(" FID: 0x%x (%d.%dx [%dMHz]) "
"VID: 0x%x (%d.%03dV)\n", fid, fid_codes[fid] / 10,
[CPUFREQ] powernow-k7: fix MHz rounding issue with perflib When the PST tables are broken, powernow-k7 uses ACPI's processor_perflib to deduce the available frequency multipliers from the _PSS tables. Upon frequency change, processor_perflib performs some verification on the frequency (checks that it's within allowable bounds). powernow-k7 deals with absolute frequencies in KHz, whereas perflib only deals with MHz values. When performing the above verification, perflib multiplies the MHz values by 1000 to obtain the KHz value. We then end up with situations like the following: - powernow-k7 multiplies the multiplier by the FSB, and obtains a value such as 1266768 KHz - perflib belives the same state has frequency of 1266 MHz - acpi_processor_ppc_notifier calls cpufreq_verify_within_limits to verify that 1266768 is in the allowable range of 0 to 1266000 (i.e. 1266 * 1000) - it's not, so that frequency is rejected - the maximum CPU frequency is not reachable This patch solves the problem by rounding up the MHz values stored in perflib's tables. Additionally it corrects a broken URL. It also fixes http://bugzilla.kernel.org/show_bug.cgi?id=8255 although this case is a bit different: the frequencies in the _PSS tables are wildly wrong, but we get better results if we force ACPI to respect the fsb * multiplier calculations (even though it seems that the multiplier values aren't entirely correct either). Signed-off-by: Daniel Drake <dsd@gentoo.org> Signed-off-by: Dave Jones <davej@redhat.com>
2007-05-02 16:19:05 -06:00
fid_codes[fid] % 10, speed_mhz, vid,
mobile_vid_table[vid]/1000,
mobile_vid_table[vid]%1000);
[CPUFREQ] powernow-k7: fix MHz rounding issue with perflib When the PST tables are broken, powernow-k7 uses ACPI's processor_perflib to deduce the available frequency multipliers from the _PSS tables. Upon frequency change, processor_perflib performs some verification on the frequency (checks that it's within allowable bounds). powernow-k7 deals with absolute frequencies in KHz, whereas perflib only deals with MHz values. When performing the above verification, perflib multiplies the MHz values by 1000 to obtain the KHz value. We then end up with situations like the following: - powernow-k7 multiplies the multiplier by the FSB, and obtains a value such as 1266768 KHz - perflib belives the same state has frequency of 1266 MHz - acpi_processor_ppc_notifier calls cpufreq_verify_within_limits to verify that 1266768 is in the allowable range of 0 to 1266000 (i.e. 1266 * 1000) - it's not, so that frequency is rejected - the maximum CPU frequency is not reachable This patch solves the problem by rounding up the MHz values stored in perflib's tables. Additionally it corrects a broken URL. It also fixes http://bugzilla.kernel.org/show_bug.cgi?id=8255 although this case is a bit different: the frequencies in the _PSS tables are wildly wrong, but we get better results if we force ACPI to respect the fsb * multiplier calculations (even though it seems that the multiplier values aren't entirely correct either). Signed-off-by: Daniel Drake <dsd@gentoo.org> Signed-off-by: Dave Jones <davej@redhat.com>
2007-05-02 16:19:05 -06:00
if (state->core_frequency != speed_mhz) {
state->core_frequency = speed_mhz;
pr_debug(" Corrected ACPI frequency to %d\n",
[CPUFREQ] powernow-k7: fix MHz rounding issue with perflib When the PST tables are broken, powernow-k7 uses ACPI's processor_perflib to deduce the available frequency multipliers from the _PSS tables. Upon frequency change, processor_perflib performs some verification on the frequency (checks that it's within allowable bounds). powernow-k7 deals with absolute frequencies in KHz, whereas perflib only deals with MHz values. When performing the above verification, perflib multiplies the MHz values by 1000 to obtain the KHz value. We then end up with situations like the following: - powernow-k7 multiplies the multiplier by the FSB, and obtains a value such as 1266768 KHz - perflib belives the same state has frequency of 1266 MHz - acpi_processor_ppc_notifier calls cpufreq_verify_within_limits to verify that 1266768 is in the allowable range of 0 to 1266000 (i.e. 1266 * 1000) - it's not, so that frequency is rejected - the maximum CPU frequency is not reachable This patch solves the problem by rounding up the MHz values stored in perflib's tables. Additionally it corrects a broken URL. It also fixes http://bugzilla.kernel.org/show_bug.cgi?id=8255 although this case is a bit different: the frequencies in the _PSS tables are wildly wrong, but we get better results if we force ACPI to respect the fsb * multiplier calculations (even though it seems that the multiplier values aren't entirely correct either). Signed-off-by: Daniel Drake <dsd@gentoo.org> Signed-off-by: Dave Jones <davej@redhat.com>
2007-05-02 16:19:05 -06:00
speed_mhz);
}
if (latency < pc.bits.sgtc)
latency = pc.bits.sgtc;
if (speed < minimum_speed)
minimum_speed = speed;
if (speed > maximum_speed)
maximum_speed = speed;
}
powernow_table[i].frequency = CPUFREQ_TABLE_END;
powernow_table[i].driver_data = 0;
/* notify BIOS that we exist */
acpi_processor_notify_smm(THIS_MODULE);
return 0;
err2:
acpi_processor_unregister_performance(0);
err1:
free_cpumask_var(acpi_processor_perf->shared_cpu_map);
err05:
kfree(acpi_processor_perf);
err0:
pr_warn("ACPI perflib can not be used on this platform\n");
acpi_processor_perf = NULL;
return retval;
}
#else
static int powernow_acpi_init(void)
{
pr_info("no support for ACPI processor found - please recompile your kernel with ACPI processor\n");
return -EINVAL;
}
#endif
static void print_pst_entry(struct pst_s *pst, unsigned int j)
{
pr_debug("PST:%d (@%p)\n", j, pst);
pr_debug(" cpuid: 0x%x fsb: %d maxFID: 0x%x startvid: 0x%x\n",
pst->cpuid, pst->fsbspeed, pst->maxfid, pst->startvid);
}
static int powernow_decode_bios(int maxfid, int startvid)
{
struct psb_s *psb;
struct pst_s *pst;
unsigned int i, j;
unsigned char *p;
unsigned int etuple;
unsigned int ret;
etuple = cpuid_eax(0x80000001);
for (i = 0xC0000; i < 0xffff0 ; i += 16) {
p = phys_to_virt(i);
if (memcmp(p, "AMDK7PNOW!", 10) == 0) {
pr_debug("Found PSB header at %p\n", p);
psb = (struct psb_s *) p;
pr_debug("Table version: 0x%x\n", psb->tableversion);
if (psb->tableversion != 0x12) {
pr_info("Sorry, only v1.2 tables supported right now\n");
return -ENODEV;
}
pr_debug("Flags: 0x%x\n", psb->flags);
if ((psb->flags & 1) == 0)
pr_debug("Mobile voltage regulator\n");
else
pr_debug("Desktop voltage regulator\n");
latency = psb->settlingtime;
if (latency < 100) {
pr_info("BIOS set settling time to %d microseconds. Should be at least 100. Correcting.\n",
latency);
latency = 100;
}
pr_debug("Settling Time: %d microseconds.\n",
psb->settlingtime);
pr_debug("Has %d PST tables. (Only dumping ones "
"relevant to this CPU).\n",
psb->numpst);
p += sizeof(*psb);
pst = (struct pst_s *) p;
for (j = 0; j < psb->numpst; j++) {
pst = (struct pst_s *) p;
number_scales = pst->numpstates;
if ((etuple == pst->cpuid) &&
check_fsb(pst->fsbspeed) &&
(maxfid == pst->maxfid) &&
(startvid == pst->startvid)) {
print_pst_entry(pst, j);
p = (char *)pst + sizeof(*pst);
ret = get_ranges(p);
return ret;
} else {
unsigned int k;
p = (char *)pst + sizeof(*pst);
for (k = 0; k < number_scales; k++)
p += 2;
}
}
pr_info("No PST tables match this cpuid (0x%x)\n",
etuple);
pr_info("This is indicative of a broken BIOS\n");
return -EINVAL;
}
p++;
}
return -ENODEV;
}
/*
* We use the fact that the bus frequency is somehow
* a multiple of 100000/3 khz, then we compute sgtc according
* to this multiple.
* That way, we match more how AMD thinks all of that work.
* We will then get the same kind of behaviour already tested under
* the "well-known" other OS.
*/
static int fixup_sgtc(void)
{
unsigned int sgtc;
unsigned int m;
m = fsb / 3333;
if ((m % 10) >= 5)
m += 5;
m /= 10;
sgtc = 100 * m * latency;
sgtc = sgtc / 3;
if (sgtc > 0xfffff) {
pr_warn("SGTC too large %d\n", sgtc);
sgtc = 0xfffff;
}
return sgtc;
}
static unsigned int powernow_get(unsigned int cpu)
{
union msr_fidvidstatus fidvidstatus;
unsigned int cfid;
if (cpu)
return 0;
rdmsrl(MSR_K7_FID_VID_STATUS, fidvidstatus.val);
cfid = fidvidstatus.bits.CFID;
return fsb * fid_codes[cfid] / 10;
}
static int acer_cpufreq_pst(const struct dmi_system_id *d)
{
pr_warn("%s laptop with broken PST tables in BIOS detected\n",
d->ident);
pr_warn("You need to downgrade to 3A21 (09/09/2002), or try a newer BIOS than 3A71 (01/20/2003)\n");
pr_warn("cpufreq scaling has been disabled as a result of this\n");
return 0;
}
/*
* Some Athlon laptops have really fucked PST tables.
* A BIOS update is all that can save them.
* Mention this, and disable cpufreq.
*/
static const struct dmi_system_id powernow_dmi_table[] = {
{
.callback = acer_cpufreq_pst,
.ident = "Acer Aspire",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Insyde Software"),
DMI_MATCH(DMI_BIOS_VERSION, "3A71"),
},
},
{ }
};
static int powernow_cpu_init(struct cpufreq_policy *policy)
{
union msr_fidvidstatus fidvidstatus;
int result;
if (policy->cpu != 0)
return -ENODEV;
rdmsrl(MSR_K7_FID_VID_STATUS, fidvidstatus.val);
recalibrate_cpu_khz();
fsb = (10 * cpu_khz) / fid_codes[fidvidstatus.bits.CFID];
if (!fsb) {
pr_warn("can not determine bus frequency\n");
return -EINVAL;
}
pr_debug("FSB: %3dMHz\n", fsb/1000);
if (dmi_check_system(powernow_dmi_table) || acpi_force) {
pr_info("PSB/PST known to be broken - trying ACPI instead\n");
result = powernow_acpi_init();
} else {
result = powernow_decode_bios(fidvidstatus.bits.MFID,
fidvidstatus.bits.SVID);
if (result) {
pr_info("Trying ACPI perflib\n");
maximum_speed = 0;
minimum_speed = -1;
latency = 0;
result = powernow_acpi_init();
if (result) {
pr_info("ACPI and legacy methods failed\n");
}
} else {
/* SGTC use the bus clock as timer */
latency = fixup_sgtc();
pr_info("SGTC: %d\n", latency);
}
}
if (result)
return result;
pr_info("Minimum speed %d MHz - Maximum speed %d MHz\n",
minimum_speed/1000, maximum_speed/1000);
policy->cpuinfo.transition_latency =
cpufreq_scale(2000000UL, fsb, latency);
policy->freq_table = powernow_table;
return 0;
}
static int powernow_cpu_exit(struct cpufreq_policy *policy)
{
#ifdef CONFIG_X86_POWERNOW_K7_ACPI
if (acpi_processor_perf) {
acpi_processor_unregister_performance(0);
free_cpumask_var(acpi_processor_perf->shared_cpu_map);
kfree(acpi_processor_perf);
}
#endif
kfree(powernow_table);
return 0;
}
static struct cpufreq_driver powernow_driver = {
.verify = cpufreq_generic_frequency_table_verify,
cpufreq: Implement light weight ->target_index() routine Currently, the prototype of cpufreq_drivers target routines is: int target(struct cpufreq_policy *policy, unsigned int target_freq, unsigned int relation); And most of the drivers call cpufreq_frequency_table_target() to get a valid index of their frequency table which is closest to the target_freq. And they don't use target_freq and relation after that. So, it makes sense to just do this work in cpufreq core before calling cpufreq_frequency_table_target() and simply pass index instead. But this can be done only with drivers which expose their frequency table with cpufreq core. For others we need to stick with the old prototype of target() until those drivers are converted to expose frequency tables. This patch implements the new light weight prototype for target_index() routine. It looks like this: int target_index(struct cpufreq_policy *policy, unsigned int index); CPUFreq core will call cpufreq_frequency_table_target() before calling this routine and pass index to it. Because CPUFreq core now requires to call routines present in freq_table.c CONFIG_CPU_FREQ_TABLE must be enabled all the time. This also marks target() interface as deprecated. So, that new drivers avoid using it. And Documentation is updated accordingly. It also converts existing .target() to newly defined light weight .target_index() routine for many driver. Acked-by: Hans-Christian Egtvedt <egtvedt@samfundet.no> Acked-by: Jesper Nilsson <jesper.nilsson@axis.com> Acked-by: Linus Walleij <linus.walleij@linaro.org> Acked-by: Russell King <linux@arm.linux.org.uk> Acked-by: David S. Miller <davem@davemloft.net> Tested-by: Andrew Lunn <andrew@lunn.ch> Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Rafael J. Wysocki <rjw@rjwysocki.net>
2013-10-25 08:15:48 -06:00
.target_index = powernow_target,
[ACPI/CPUFREQ] Introduce bios_limit per cpu cpufreq sysfs interface This interface is mainly intended (and implemented) for ACPI _PPC BIOS frequency limitations, but other cpufreq drivers can also use it for similar use-cases. Why is this needed: Currently it's not obvious why cpufreq got limited. People see cpufreq/scaling_max_freq reduced, but this could have happened by: - any userspace prog writing to scaling_max_freq - thermal limitations - hardware (_PPC in ACPI case) limitiations Therefore export bios_limit (in kHz) to: - Point the user that it's the BIOS (broken or intended) which limits frequency - Export it as a sysfs interface for userspace progs. While this was a rarely used feature on laptops, there will appear more and more server implemenations providing "Green IT" features like allowing the service processor to limit the frequency. People want to know about HW/BIOS frequency limitations. All ACPI P-state driven cpufreq drivers are covered with this patch: - powernow-k8 - powernow-k7 - acpi-cpufreq Tested with a patched DSDT which limits the first two cores (_PPC returns 1) via _PPC, exposed by bios_limit: # echo 2200000 >cpu2/cpufreq/scaling_max_freq # cat cpu*/cpufreq/scaling_max_freq 2600000 2600000 2200000 2200000 # #scaling_max_freq shows general user/thermal/BIOS limitations # cat cpu*/cpufreq/bios_limit 2600000 2600000 2800000 2800000 # #bios_limit only shows the HW/BIOS limitation CC: Pallipadi Venkatesh <venkatesh.pallipadi@intel.com> CC: Len Brown <lenb@kernel.org> CC: davej@codemonkey.org.uk CC: linux@dominikbrodowski.net Signed-off-by: Thomas Renninger <trenn@suse.de> Signed-off-by: Dave Jones <davej@redhat.com>
2009-11-19 04:31:01 -07:00
.get = powernow_get,
#ifdef CONFIG_X86_POWERNOW_K7_ACPI
.bios_limit = acpi_processor_get_bios_limit,
#endif
.init = powernow_cpu_init,
.exit = powernow_cpu_exit,
.name = "powernow-k7",
.attr = cpufreq_generic_attr,
};
static int __init powernow_init(void)
{
if (check_powernow() == 0)
return -ENODEV;
return cpufreq_register_driver(&powernow_driver);
}
static void __exit powernow_exit(void)
{
cpufreq_unregister_driver(&powernow_driver);
}
module_param(acpi_force, int, 0444);
MODULE_PARM_DESC(acpi_force, "Force ACPI to be used.");
MODULE_AUTHOR("Dave Jones");
MODULE_DESCRIPTION("Powernow driver for AMD K7 processors.");
MODULE_LICENSE("GPL");
late_initcall(powernow_init);
module_exit(powernow_exit);