Commit 17d5007f authored by Linus Torvalds's avatar Linus Torvalds

Merge bk://linux-dj.bkbits.net/cpufreq

into ppc970.osdl.org:/home/torvalds/v2.6/linux
parents 4918451f ad4c196a
......@@ -238,6 +238,34 @@ acpi_cpufreq_verify (
}
static unsigned long
acpi_cpufreq_guess_freq (
struct cpufreq_acpi_io *data,
unsigned int cpu)
{
if (cpu_khz) {
/* search the closest match to cpu_khz */
unsigned int i;
unsigned long freq;
unsigned long freqn = data->acpi_data.states[0].core_frequency * 1000;
for (i=0; i < (data->acpi_data.state_count - 1); i++) {
freq = freqn;
freqn = data->acpi_data.states[i+1].core_frequency * 1000;
if ((2 * cpu_khz) > (freqn + freq)) {
data->acpi_data.state = i;
return (freq);
}
}
data->acpi_data.state = data->acpi_data.state_count - 1;
return (freqn);
} else
/* assume CPU is at P0... */
data->acpi_data.state = 0;
return data->acpi_data.states[0].core_frequency * 1000;
}
static int
acpi_cpufreq_cpu_init (
struct cpufreq_policy *policy)
......@@ -290,11 +318,8 @@ acpi_cpufreq_cpu_init (
}
policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
/*
* The current speed is unknown and not detectable by ACPI... argh! Assume
* it's P0, it will be set to this value later during initialization.
*/
policy->cur = data->acpi_data.states[0].core_frequency * 1000;
/* The current speed is unknown and not detectable by ACPI... */
policy->cur = acpi_cpufreq_guess_freq(data, policy->cpu);
/* table init */
for (i=0; i<=data->acpi_data.state_count; i++)
......@@ -306,7 +331,7 @@ acpi_cpufreq_cpu_init (
data->freq_table[i].frequency = CPUFREQ_TABLE_END;
}
result = cpufreq_frequency_table_cpuinfo(policy, &data->freq_table[0]);
result = cpufreq_frequency_table_cpuinfo(policy, data->freq_table);
if (result) {
goto err_freqfree;
}
......@@ -321,6 +346,7 @@ acpi_cpufreq_cpu_init (
(u32) data->acpi_data.states[i].power,
(u32) data->acpi_data.states[i].transition_latency);
cpufreq_frequency_table_get_attr(data->freq_table, policy->cpu);
return_VALUE(result);
err_freqfree:
......@@ -345,6 +371,7 @@ acpi_cpufreq_cpu_exit (
ACPI_FUNCTION_TRACE("acpi_cpufreq_cpu_exit");
if (data) {
cpufreq_frequency_table_put_attr(policy->cpu);
acpi_io_data[policy->cpu] = NULL;
acpi_processor_unregister_performance(&data->acpi_data, policy->cpu);
kfree(data);
......@@ -354,6 +381,11 @@ acpi_cpufreq_cpu_exit (
}
static struct freq_attr* acpi_cpufreq_attr[] = {
&cpufreq_freq_attr_scaling_available_freqs,
NULL,
};
static struct cpufreq_driver acpi_cpufreq_driver = {
.verify = acpi_cpufreq_verify,
.target = acpi_cpufreq_target,
......@@ -361,6 +393,7 @@ static struct cpufreq_driver acpi_cpufreq_driver = {
.exit = acpi_cpufreq_cpu_exit,
.name = "acpi-cpufreq",
.owner = THIS_MODULE,
.attr = acpi_cpufreq_attr,
};
......
......@@ -17,6 +17,7 @@
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/cpufreq.h>
#include <linux/slab.h>
......@@ -27,6 +28,11 @@
#include <asm/io.h>
#include <asm/system.h>
#ifdef CONFIG_ACPI_PROCESSOR
#include <linux/acpi.h>
#include <acpi/processor.h>
#endif
#include "powernow-k7.h"
#define DEBUG
......@@ -57,6 +63,17 @@ struct pst_s {
u8 numpstates;
};
#ifdef CONFIG_ACPI_PROCESSOR
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 VID. */
static int mobile_vid_table[32] = {
......@@ -74,6 +91,12 @@ static int fid_codes[32] = {
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 powernow_acpi_force;
static struct cpufreq_frequency_table *powernow_table;
static unsigned int can_scale_bus;
......@@ -85,6 +108,14 @@ 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 int check_powernow(void)
{
......@@ -140,7 +171,8 @@ static int check_powernow(void)
static int get_ranges (unsigned char *pst)
{
unsigned int j, speed;
unsigned int j;
unsigned int speed;
u8 fid, vid;
powernow_table = kmalloc((sizeof(struct cpufreq_frequency_table) * (number_scales + 1)), GFP_KERNEL);
......@@ -151,12 +183,12 @@ static int get_ranges (unsigned char *pst)
for (j=0 ; j < number_scales; j++) {
fid = *pst++;
powernow_table[j].frequency = fsb * fid_codes[fid] * 100;
powernow_table[j].frequency = (fsb * fid_codes[fid]) / 10;
powernow_table[j].index = fid; /* lower 8 bits */
speed = fsb * (fid_codes[fid]/10);
speed = powernow_table[j].frequency;
if ((fid_codes[fid] % 10)==5) {
speed += fsb/2;
#if defined(CONFIG_ACPI_PROCESSOR) || defined(CONFIG_ACPI_PROCESSOR_MODULE)
if (have_a0 == 1)
powernow_table[j].frequency = CPUFREQ_ENTRY_INVALID;
......@@ -164,7 +196,7 @@ static int get_ranges (unsigned char *pst)
}
dprintk (KERN_INFO PFX " FID: 0x%x (%d.%dx [%dMHz])\t", fid,
fid_codes[fid] / 10, fid_codes[fid] % 10, speed);
fid_codes[fid] / 10, fid_codes[fid] % 10, speed/1000);
if (speed < minimum_speed)
minimum_speed = speed;
......@@ -176,8 +208,6 @@ static int get_ranges (unsigned char *pst)
dprintk ("VID: 0x%x (%d.%03dV)\n", vid, mobile_vid_table[vid]/1000,
mobile_vid_table[vid]%1000);
}
dprintk ("\n");
powernow_table[number_scales].frequency = CPUFREQ_TABLE_END;
powernow_table[number_scales].index = 0;
......@@ -234,7 +264,8 @@ static void change_speed (unsigned int index)
rdmsrl (MSR_K7_FID_VID_STATUS, fidvidstatus.val);
cfid = fidvidstatus.bits.CFID;
freqs.old = fsb * fid_codes[cfid] * 100;
freqs.old = fsb * fid_codes[cfid] / 10;
freqs.new = powernow_table[index].frequency;
cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
......@@ -262,19 +293,136 @@ static void change_speed (unsigned int index)
}
#ifdef CONFIG_ACPI_PROCESSOR
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 = kmalloc(sizeof(struct acpi_processor_performance),
GFP_KERNEL);
if (!acpi_processor_perf) {
retval = -ENOMEM;
goto err0;
}
memset(acpi_processor_perf, 0, sizeof(struct acpi_processor_performance));
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 = kmalloc((number_scales + 1) * (sizeof(struct cpufreq_frequency_table)), GFP_KERNEL);
if (!powernow_table) {
retval = -ENOMEM;
goto err2;
}
memset(powernow_table, 0, ((number_scales + 1) * sizeof(struct cpufreq_frequency_table)));
pc.val = (unsigned long) acpi_processor_perf->states[0].control;
for (i = 0; i < number_scales; i++) {
u8 fid, vid;
unsigned int speed;
pc.val = (unsigned long) acpi_processor_perf->states[i].control;
dprintk (KERN_INFO PFX "acpi: P%d: %d MHz %d mW %d uS control %08x SGTC %d\n",
i,
(u32) acpi_processor_perf->states[i].core_frequency,
(u32) acpi_processor_perf->states[i].power,
(u32) acpi_processor_perf->states[i].transition_latency,
(u32) acpi_processor_perf->states[i].control,
pc.bits.sgtc);
vid = pc.bits.vid;
fid = pc.bits.fid;
powernow_table[i].frequency = fsb * fid_codes[fid] / 10;
powernow_table[i].index = fid; /* lower 8 bits */
powernow_table[i].index |= (vid << 8); /* upper 8 bits */
speed = powernow_table[i].frequency;
if ((fid_codes[fid] % 10)==5) {
if (have_a0 == 1)
powernow_table[i].frequency = CPUFREQ_ENTRY_INVALID;
}
dprintk (KERN_INFO PFX " FID: 0x%x (%d.%dx [%dMHz])\t", fid,
fid_codes[fid] / 10, fid_codes[fid] % 10, speed/1000);
dprintk ("VID: 0x%x (%d.%03dV)\n", vid, mobile_vid_table[vid]/1000,
mobile_vid_table[vid]%1000);
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].index = 0;
return 0;
err2:
acpi_processor_unregister_performance(acpi_processor_perf, 0);
err1:
kfree(acpi_processor_perf);
err0:
printk(KERN_WARNING PFX "ACPI perflib can not be used in this platform\n");
acpi_processor_perf = NULL;
return retval;
}
#else
static int powernow_acpi_init(void)
{
printk(KERN_INFO PFX "no support for ACPI processor found."
" Please recompile your kernel with ACPI processor\n");
return -EINVAL;
}
#endif
static int powernow_decode_bios (int maxfid, int startvid)
{
struct psb_s *psb;
struct pst_s *pst;
struct cpuinfo_x86 *c = cpu_data;
unsigned int i, j;
unsigned char *p;
unsigned int etuple;
unsigned int ret;
etuple = cpuid_eax(0x80000001);
etuple &= 0xf00;
etuple |= (c->x86_model<<4)|(c->x86_mask);
for (i=0xC0000; i < 0xffff0 ; i+=16) {
......@@ -305,7 +453,6 @@ static int powernow_decode_bios (int maxfid, int startvid)
}
dprintk (KERN_INFO PFX "Settling Time: %d microseconds.\n", psb->settlingtime);
dprintk (KERN_INFO PFX "Has %d PST tables. (Only dumping ones relevant to this CPU).\n", psb->numpst);
latency *= 100; /* SGTC needs to be in units of 10ns */
p += sizeof (struct psb_s);
......@@ -315,7 +462,8 @@ static int powernow_decode_bios (int maxfid, int startvid)
pst = (struct pst_s *) p;
number_scales = pst->numpstates;
if ((etuple == pst->cpuid) && (maxfid==pst->maxfid) && (startvid==pst->startvid))
if ((etuple == pst->cpuid) && check_fsb(pst->fsbspeed) &&
(maxfid==pst->maxfid) && (startvid==pst->startvid))
{
dprintk (KERN_INFO PFX "PST:%d (@%p)\n", i, pst);
dprintk (KERN_INFO PFX " cpuid: 0x%x\t", pst->cpuid);
......@@ -323,7 +471,6 @@ static int powernow_decode_bios (int maxfid, int startvid)
dprintk ("maxFID: 0x%x\t", pst->maxfid);
dprintk ("startvid: 0x%x\n", pst->startvid);
fsb = pst->fsbspeed;
ret = get_ranges ((char *) pst + sizeof (struct pst_s));
return ret;
......@@ -335,7 +482,7 @@ static int powernow_decode_bios (int maxfid, int startvid)
}
printk (KERN_INFO PFX "No PST tables match this cpuid (0x%x)\n", etuple);
printk (KERN_INFO PFX "This is indicative of a broken BIOS.\n");
printk (KERN_INFO PFX "See http://www.codemonkey.org.uk/projects/cpufreq/powernow-k7.shtml\n");
return -EINVAL;
}
p++;
......@@ -365,6 +512,33 @@ static int powernow_verify (struct cpufreq_policy *policy)
return cpufreq_frequency_table_verify(policy, powernow_table);
}
/*
* 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 __init 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) {
printk(KERN_WARNING PFX "SGTC too large %d\n", sgtc);
sgtc = 0xfffff;
}
return sgtc;
}
static int __init powernow_cpu_init (struct cpufreq_policy *policy)
{
......@@ -376,18 +550,45 @@ static int __init powernow_cpu_init (struct cpufreq_policy *policy)
rdmsrl (MSR_K7_FID_VID_STATUS, fidvidstatus.val);
result = powernow_decode_bios(fidvidstatus.bits.MFID, fidvidstatus.bits.SVID);
/* A K7 with powernow technology is set to max frequency by BIOS */
fsb = (10 * cpu_khz) / fid_codes[fidvidstatus.bits.CFID];
if (!fsb) {
printk(KERN_WARNING PFX "can not determine bus frequency\n");
return -EINVAL;
}
dprintk(KERN_INFO PFX "FSB: %3d.%03d MHz\n", fsb/1000, fsb%1000);
if ((dmi_broken & BROKEN_CPUFREQ) || powernow_acpi_force) {
printk (KERN_INFO PFX "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) {
printk (KERN_INFO PFX "Trying ACPI perflib\n");
maximum_speed = 0;
minimum_speed = -1;
latency = 0;
result = powernow_acpi_init();
if (result) {
printk (KERN_INFO PFX "ACPI and legacy methods failed\n");
printk (KERN_INFO PFX "See http://www.codemonkey.org.uk/projects/cpufreq/powernow-k7.shtml\n");
}
} else {
/* SGTC use the bus clock as timer */
latency = fixup_sgtc();
printk(KERN_INFO PFX "SGTC: %d\n", latency);
}
}
if (result)
return result;
printk (KERN_INFO PFX "Minimum speed %d MHz. Maximum speed %d MHz.\n",
minimum_speed, maximum_speed);
minimum_speed/1000, maximum_speed/1000);
policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
/* latency is in 10 ns (look for SGTC above) for each VID
* and FID transition, so multiply that value with 20 */
policy->cpuinfo.transition_latency = latency * 20;
policy->cpuinfo.transition_latency = 20 * latency / fsb;
policy->cur = maximum_speed;
......@@ -418,10 +619,6 @@ static struct cpufreq_driver powernow_driver = {
static int __init powernow_init (void)
{
if (dmi_broken & BROKEN_CPUFREQ) {
printk (KERN_INFO PFX "Disabled at boot time by DMI,\n");
return -ENODEV;
}
if (check_powernow()==0)
return -ENODEV;
return cpufreq_register_driver(&powernow_driver);
......@@ -430,15 +627,25 @@ static int __init powernow_init (void)
static void __exit powernow_exit (void)
{
#ifdef CONFIG_ACPI_PROCESSOR
if (acpi_processor_perf) {
acpi_processor_unregister_performance(acpi_processor_perf, 0);
kfree(acpi_processor_perf);
}
#endif
cpufreq_unregister_driver(&powernow_driver);
if (powernow_table)
kfree(powernow_table);
}
module_param(powernow_acpi_force, int, 0444);
MODULE_PARM_DESC(acpi_force, "Force ACPI to be used");
MODULE_AUTHOR ("Dave Jones <davej@codemonkey.org.uk>");
MODULE_DESCRIPTION ("Powernow driver for AMD K7 processors.");
MODULE_LICENSE ("GPL");
module_init(powernow_init);
late_initcall(powernow_init);
module_exit(powernow_exit);
......@@ -32,7 +32,7 @@
#include <asm/io.h>
#include <asm/delay.h>
#ifdef CONFIG_X86_POWERNOW_K8_ACPI
#ifdef CONFIG_ACPI_PROCESSOR
#include <linux/acpi.h>
#include <acpi/processor.h>
#endif
......@@ -269,7 +269,7 @@ static int core_voltage_pre_transition(struct powernow_k8_data *data, u32 reqvid
dprintk(KERN_DEBUG PFX
"ph1 (cpu%d): start, currfid 0x%x, currvid 0x%x, reqvid 0x%x, rvo 0x%x\n",
smp_processor_id();
smp_processor_id(),
data->currfid, data->currvid, reqvid, data->rvo);
while (data->currvid > reqvid) {
......@@ -666,7 +666,7 @@ static int find_psb_table(struct powernow_k8_data *data)
return -ENODEV;
}
#ifdef CONFIG_X86_POWERNOW_K8_ACPI
#ifdef CONFIG_ACPI_PROCESSOR
static void powernow_k8_acpi_pst_values(struct powernow_k8_data *data, unsigned int index)
{
if (!data->acpi_data.state_count)
......
......@@ -29,7 +29,7 @@ struct powernow_k8_data {
* frequency is in kHz */
struct cpufreq_frequency_table *powernow_table;
#ifdef CONFIG_X86_POWERNOW_K8_ACPI
#ifdef CONFIG_ACPI_PROCESSOR
/* the acpi table needs to be kept. it's only available if ACPI was
* used to determine valid frequency/vid/fid states */
struct acpi_processor_performance acpi_data;
......
......@@ -195,21 +195,6 @@ static int centrino_cpu_init_table(struct cpufreq_policy *policy)
struct cpuinfo_x86 *cpu = &cpu_data[policy->cpu];
struct cpu_model *model;
if (!cpu_has(cpu, X86_FEATURE_EST))
return -ENODEV;
/* Only Intel Pentium M stepping 5 for now - add new CPUs as
they appear after making sure they use PERF_CTL in the same
way. */
if (cpu->x86_vendor != X86_VENDOR_INTEL ||
cpu->x86 != 6 ||
cpu->x86_model != 9 ||
cpu->x86_mask != 5) {
printk(KERN_INFO PFX "found unsupported CPU with Enhanced SpeedStep: "
"send /proc/cpuinfo to " MAINTAINER "\n");
return -ENODEV;
}
for(model = models; model->model_name != NULL; model++)
if (strcmp(cpu->x86_model_id, model->model_name) == 0)
break;
......@@ -361,6 +346,7 @@ static inline int centrino_cpu_init_acpi(struct cpufreq_policy *policy) { return
static int centrino_cpu_init(struct cpufreq_policy *policy)
{
struct cpuinfo_x86 *cpu = &cpu_data[policy->cpu];
unsigned freq;
unsigned l, h;
int ret;
......@@ -368,6 +354,21 @@ static int centrino_cpu_init(struct cpufreq_policy *policy)
if (policy->cpu != 0)
return -ENODEV;
if (!cpu_has(cpu, X86_FEATURE_EST))
return -ENODEV;
/* Only Intel Pentium M stepping 5 for now - add new CPUs as
they appear after making sure they use PERF_CTL in the same
way. */
if (cpu->x86_vendor != X86_VENDOR_INTEL ||
cpu->x86 != 6 ||
cpu->x86_model != 9 ||
cpu->x86_mask != 5) {
printk(KERN_INFO PFX "found unsupported CPU with Enhanced SpeedStep: "
"send /proc/cpuinfo to " MAINTAINER "\n");
return -ENODEV;
}
if (centrino_cpu_init_acpi(policy)) {
if (centrino_cpu_init_table(policy)) {
return -ENODEV;
......
......@@ -360,8 +360,10 @@ static int cpufreq_add_dev (struct sys_device * sys_dev)
return -EINVAL;
policy = kmalloc(sizeof(struct cpufreq_policy), GFP_KERNEL);
if (!policy)
return -ENOMEM;
if (!policy) {
ret = -ENOMEM;
goto nomem_out;
}
memset(policy, 0, sizeof(struct cpufreq_policy));
policy->cpu = cpu;
......@@ -410,7 +412,7 @@ static int cpufreq_add_dev (struct sys_device * sys_dev)
return 0;
err_out_unregister:
err_out_unregister:
spin_lock_irqsave(&cpufreq_driver_lock, flags);
cpufreq_cpu_data[cpu] = NULL;
spin_unlock_irqrestore(&cpufreq_driver_lock, flags);
......@@ -418,8 +420,10 @@ static int cpufreq_add_dev (struct sys_device * sys_dev)
kobject_unregister(&policy->kobj);
wait_for_completion(&policy->kobj_unregister);
err_out:
err_out:
kfree(policy);
nomem_out:
module_put(cpufreq_driver->owner);
return ret;
}
......
......@@ -167,8 +167,8 @@ cpufreq_procctl(ctl_table *ctl, int write, struct file *filp,
void __user *buffer, size_t *lenp)
{
char buf[16], *p;
int cpu = (int) ctl->extra1;
int len, left = *lenp;
int cpu = (long) ctl->extra1;
unsigned int len, left = *lenp;
if (!left || (filp->f_pos && !write) || !cpu_online(cpu)) {
*lenp = 0;
......@@ -205,7 +205,7 @@ cpufreq_sysctl(ctl_table *table, int __user *name, int nlen,
void __user *oldval, size_t __user *oldlenp,
void __user *newval, size_t newlen, void **context)
{
int cpu = (int) table->extra1;
int cpu = (long) table->extra1;
if (!cpu_online(cpu))
return -EINVAL;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment