Merge master.kernel.org:/pub/scm/linux/kernel/git/davej/cpufreq

* master.kernel.org:/pub/scm/linux/kernel/git/davej/cpufreq:
  [CPUFREQ] Propagate acpi_processor_preregister_performance return value.
  [CPUFREQ] [2/2] demand load governor modules.
  [CPUFREQ] [1/2] add __find_governor helper and clean up some error handling.
  [CPUFREQ] Longhaul - Rename & fix multipliers table
  [CPUFREQ] Longhaul - Fix power state test to do something more useful
  [CPUFREQ] Longhaul - Readd accidentally dropped line
  [CPUFREQ] Make longhaul_walk_callback() static
  [CPUFREQ] X86_GX_SUSPMOD must depend on PCI
  [CPUFREQ] Longhaul - Initialise later.
  [CPUFREQ] Longhaul - Workaround issues with APIC.
  [CPUFREQ] Longhaul - Hook into ACPI C states.
  [CPUFREQ] return error when failing to set minfreq
This commit is contained in:
Linus Torvalds 2006-08-04 09:41:22 -07:00
commit 12952784e5
5 changed files with 190 additions and 117 deletions

View file

@ -157,6 +157,9 @@ scaling_min_freq and
scaling_max_freq show the current "policy limits" (in scaling_max_freq show the current "policy limits" (in
kHz). By echoing new values into these kHz). By echoing new values into these
files, you can change these limits. files, you can change these limits.
NOTE: when setting a policy you need to
first set scaling_max_freq, then
scaling_min_freq.
If you have selected the "userspace" governor which allows you to If you have selected the "userspace" governor which allows you to

View file

@ -96,6 +96,7 @@ config X86_POWERNOW_K8_ACPI
config X86_GX_SUSPMOD config X86_GX_SUSPMOD
tristate "Cyrix MediaGX/NatSemi Geode Suspend Modulation" tristate "Cyrix MediaGX/NatSemi Geode Suspend Modulation"
depends on PCI
help help
This add the CPUFreq driver for NatSemi Geode processors which This add the CPUFreq driver for NatSemi Geode processors which
support suspend modulation. support suspend modulation.
@ -202,7 +203,7 @@ config X86_LONGRUN
config X86_LONGHAUL config X86_LONGHAUL
tristate "VIA Cyrix III Longhaul" tristate "VIA Cyrix III Longhaul"
select CPU_FREQ_TABLE select CPU_FREQ_TABLE
depends on BROKEN depends on ACPI_PROCESSOR
help help
This adds the CPUFreq driver for VIA Samuel/CyrixIII, This adds the CPUFreq driver for VIA Samuel/CyrixIII,
VIA Cyrix Samuel/C3, VIA Cyrix Ezra and VIA Cyrix Ezra-T VIA Cyrix Samuel/C3, VIA Cyrix Ezra and VIA Cyrix Ezra-T

View file

@ -384,8 +384,7 @@ static int acpi_cpufreq_early_init_acpi(void)
} }
/* Do initialization in ACPI core */ /* Do initialization in ACPI core */
acpi_processor_preregister_performance(acpi_perf_data); return acpi_processor_preregister_performance(acpi_perf_data);
return 0;
} }
static int static int

View file

@ -29,11 +29,13 @@
#include <linux/cpufreq.h> #include <linux/cpufreq.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/pci.h>
#include <asm/msr.h> #include <asm/msr.h>
#include <asm/timex.h> #include <asm/timex.h>
#include <asm/io.h> #include <asm/io.h>
#include <asm/acpi.h>
#include <linux/acpi.h>
#include <acpi/processor.h>
#include "longhaul.h" #include "longhaul.h"
@ -56,6 +58,8 @@ static int minvid, maxvid;
static unsigned int minmult, maxmult; static unsigned int minmult, maxmult;
static int can_scale_voltage; static int can_scale_voltage;
static int vrmrev; static int vrmrev;
static struct acpi_processor *pr = NULL;
static struct acpi_processor_cx *cx = NULL;
/* Module parameters */ /* Module parameters */
static int dont_scale_voltage; static int dont_scale_voltage;
@ -118,84 +122,65 @@ static int longhaul_get_cpu_mult(void)
return eblcr_table[invalue]; return eblcr_table[invalue];
} }
/* For processor with BCR2 MSR */
static void do_powersaver(union msr_longhaul *longhaul, static void do_longhaul1(int cx_address, unsigned int clock_ratio_index)
unsigned int clock_ratio_index)
{ {
struct pci_dev *dev; union msr_bcr2 bcr2;
unsigned long flags; u32 t;
unsigned int tmp_mask;
int version;
int i;
u16 pci_cmd;
u16 cmd_state[64];
switch (cpu_model) { rdmsrl(MSR_VIA_BCR2, bcr2.val);
case CPU_EZRA_T: /* Enable software clock multiplier */
version = 3; bcr2.bits.ESOFTBF = 1;
break; bcr2.bits.CLOCKMUL = clock_ratio_index;
case CPU_NEHEMIAH:
version = 0xf;
break;
default:
return;
}
rdmsrl(MSR_VIA_LONGHAUL, longhaul->val);
longhaul->bits.SoftBusRatio = clock_ratio_index & 0xf;
longhaul->bits.SoftBusRatio4 = (clock_ratio_index & 0x10) >> 4;
longhaul->bits.EnableSoftBusRatio = 1;
longhaul->bits.RevisionKey = 0;
preempt_disable();
local_irq_save(flags);
/*
* get current pci bus master state for all devices
* and clear bus master bit
*/
dev = NULL;
i = 0;
do {
dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev);
if (dev != NULL) {
pci_read_config_word(dev, PCI_COMMAND, &pci_cmd);
cmd_state[i++] = pci_cmd;
pci_cmd &= ~PCI_COMMAND_MASTER;
pci_write_config_word(dev, PCI_COMMAND, pci_cmd);
}
} while (dev != NULL);
tmp_mask=inb(0x21); /* works on C3. save mask. */
outb(0xFE,0x21); /* TMR0 only */
outb(0xFF,0x80); /* delay */
/* Sync to timer tick */
safe_halt(); safe_halt();
wrmsrl(MSR_VIA_LONGHAUL, longhaul->val); ACPI_FLUSH_CPU_CACHE();
halt(); /* Change frequency on next halt or sleep */
wrmsrl(MSR_VIA_BCR2, bcr2.val);
/* Invoke C3 */
inb(cx_address);
/* Dummy op - must do something useless after P_LVL3 read */
t = inl(acpi_fadt.xpm_tmr_blk.address);
/* Disable software clock multiplier */
local_irq_disable(); local_irq_disable();
rdmsrl(MSR_VIA_BCR2, bcr2.val);
bcr2.bits.ESOFTBF = 0;
wrmsrl(MSR_VIA_BCR2, bcr2.val);
}
outb(tmp_mask,0x21); /* restore mask */ /* For processor with Longhaul MSR */
/* restore pci bus master state for all devices */ static void do_powersaver(int cx_address, unsigned int clock_ratio_index)
dev = NULL; {
i = 0; union msr_longhaul longhaul;
do { u32 t;
dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev);
if (dev != NULL) {
pci_cmd = cmd_state[i++];
pci_write_config_byte(dev, PCI_COMMAND, pci_cmd);
}
} while (dev != NULL);
local_irq_restore(flags);
preempt_enable();
/* disable bus ratio bit */ rdmsrl(MSR_VIA_LONGHAUL, longhaul.val);
rdmsrl(MSR_VIA_LONGHAUL, longhaul->val); longhaul.bits.RevisionKey = longhaul.bits.RevisionID;
longhaul->bits.EnableSoftBusRatio = 0; longhaul.bits.SoftBusRatio = clock_ratio_index & 0xf;
longhaul->bits.RevisionKey = version; longhaul.bits.SoftBusRatio4 = (clock_ratio_index & 0x10) >> 4;
wrmsrl(MSR_VIA_LONGHAUL, longhaul->val); longhaul.bits.EnableSoftBusRatio = 1;
/* Sync to timer tick */
safe_halt();
ACPI_FLUSH_CPU_CACHE();
/* Change frequency on next halt or sleep */
wrmsrl(MSR_VIA_LONGHAUL, longhaul.val);
/* Invoke C3 */
inb(cx_address);
/* Dummy op - must do something useless after P_LVL3 read */
t = inl(acpi_fadt.xpm_tmr_blk.address);
/* Disable bus ratio bit */
local_irq_disable();
longhaul.bits.RevisionKey = longhaul.bits.RevisionID;
longhaul.bits.EnableSoftBusRatio = 0;
longhaul.bits.EnableSoftBSEL = 0;
longhaul.bits.EnableSoftVID = 0;
wrmsrl(MSR_VIA_LONGHAUL, longhaul.val);
} }
/** /**
@ -209,9 +194,9 @@ static void longhaul_setstate(unsigned int clock_ratio_index)
{ {
int speed, mult; int speed, mult;
struct cpufreq_freqs freqs; struct cpufreq_freqs freqs;
union msr_longhaul longhaul;
union msr_bcr2 bcr2;
static unsigned int old_ratio=-1; static unsigned int old_ratio=-1;
unsigned long flags;
unsigned int pic1_mask, pic2_mask;
if (old_ratio == clock_ratio_index) if (old_ratio == clock_ratio_index)
return; return;
@ -234,6 +219,20 @@ static void longhaul_setstate(unsigned int clock_ratio_index)
dprintk ("Setting to FSB:%dMHz Mult:%d.%dx (%s)\n", dprintk ("Setting to FSB:%dMHz Mult:%d.%dx (%s)\n",
fsb, mult/10, mult%10, print_speed(speed/1000)); fsb, mult/10, mult%10, print_speed(speed/1000));
preempt_disable();
local_irq_save(flags);
pic2_mask = inb(0xA1);
pic1_mask = inb(0x21); /* works on C3. save mask. */
outb(0xFF,0xA1); /* Overkill */
outb(0xFE,0x21); /* TMR0 only */
/* Disable bus master arbitration */
if (pr->flags.bm_check) {
acpi_set_register(ACPI_BITREG_ARB_DISABLE, 1,
ACPI_MTX_DO_NOT_LOCK);
}
switch (longhaul_version) { switch (longhaul_version) {
/* /*
@ -245,20 +244,7 @@ static void longhaul_setstate(unsigned int clock_ratio_index)
*/ */
case TYPE_LONGHAUL_V1: case TYPE_LONGHAUL_V1:
case TYPE_LONGHAUL_V2: case TYPE_LONGHAUL_V2:
rdmsrl (MSR_VIA_BCR2, bcr2.val); do_longhaul1(cx->address, clock_ratio_index);
/* Enable software clock multiplier */
bcr2.bits.ESOFTBF = 1;
bcr2.bits.CLOCKMUL = clock_ratio_index;
local_irq_disable();
wrmsrl (MSR_VIA_BCR2, bcr2.val);
safe_halt();
/* Disable software clock multiplier */
rdmsrl (MSR_VIA_BCR2, bcr2.val);
bcr2.bits.ESOFTBF = 0;
local_irq_disable();
wrmsrl (MSR_VIA_BCR2, bcr2.val);
local_irq_enable();
break; break;
/* /*
@ -273,10 +259,22 @@ static void longhaul_setstate(unsigned int clock_ratio_index)
* to work in practice. * to work in practice.
*/ */
case TYPE_POWERSAVER: case TYPE_POWERSAVER:
do_powersaver(&longhaul, clock_ratio_index); do_powersaver(cx->address, clock_ratio_index);
break; break;
} }
/* Enable bus master arbitration */
if (pr->flags.bm_check) {
acpi_set_register(ACPI_BITREG_ARB_DISABLE, 0,
ACPI_MTX_DO_NOT_LOCK);
}
outb(pic2_mask,0xA1); /* restore mask */
outb(pic1_mask,0x21);
local_irq_restore(flags);
preempt_enable();
cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
} }
@ -324,9 +322,11 @@ static int guess_fsb(void)
static int __init longhaul_get_ranges(void) static int __init longhaul_get_ranges(void)
{ {
unsigned long invalue; unsigned long invalue;
unsigned int multipliers[32]= { unsigned int ezra_t_multipliers[32]= {
50,30,40,100,55,35,45,95,90,70,80,60,120,75,85,65, 90, 30, 40, 100, 55, 35, 45, 95,
-1,110,120,-1,135,115,125,105,130,150,160,140,-1,155,-1,145 }; 50, 70, 80, 60, 120, 75, 85, 65,
-1, 110, 120, -1, 135, 115, 125, 105,
130, 150, 160, 140, -1, 155, -1, 145 };
unsigned int j, k = 0; unsigned int j, k = 0;
union msr_longhaul longhaul; union msr_longhaul longhaul;
unsigned long lo, hi; unsigned long lo, hi;
@ -355,13 +355,13 @@ static int __init longhaul_get_ranges(void)
invalue = longhaul.bits.MaxMHzBR; invalue = longhaul.bits.MaxMHzBR;
if (longhaul.bits.MaxMHzBR4) if (longhaul.bits.MaxMHzBR4)
invalue += 16; invalue += 16;
maxmult=multipliers[invalue]; maxmult=ezra_t_multipliers[invalue];
invalue = longhaul.bits.MinMHzBR; invalue = longhaul.bits.MinMHzBR;
if (longhaul.bits.MinMHzBR4 == 1) if (longhaul.bits.MinMHzBR4 == 1)
minmult = 30; minmult = 30;
else else
minmult = multipliers[invalue]; minmult = ezra_t_multipliers[invalue];
fsb = eblcr_fsb_table_v2[longhaul.bits.MaxMHzFSB]; fsb = eblcr_fsb_table_v2[longhaul.bits.MaxMHzFSB];
break; break;
} }
@ -527,6 +527,18 @@ static unsigned int longhaul_get(unsigned int cpu)
return calc_speed(longhaul_get_cpu_mult()); return calc_speed(longhaul_get_cpu_mult());
} }
static acpi_status longhaul_walk_callback(acpi_handle obj_handle,
u32 nesting_level,
void *context, void **return_value)
{
struct acpi_device *d;
if ( acpi_bus_get_device(obj_handle, &d) ) {
return 0;
}
*return_value = (void *)acpi_driver_data(d);
return 1;
}
static int __init longhaul_cpu_init(struct cpufreq_policy *policy) static int __init longhaul_cpu_init(struct cpufreq_policy *policy)
{ {
@ -534,6 +546,15 @@ static int __init longhaul_cpu_init(struct cpufreq_policy *policy)
char *cpuname=NULL; char *cpuname=NULL;
int ret; int ret;
/* Check ACPI support for C3 state */
acpi_walk_namespace(ACPI_TYPE_PROCESSOR, ACPI_ROOT_OBJECT, ACPI_UINT32_MAX,
&longhaul_walk_callback, NULL, (void *)&pr);
if (pr == NULL) goto err_acpi;
cx = &pr->power.states[ACPI_STATE_C3];
if (cx->address == 0 || cx->latency > 1000) goto err_acpi;
/* Now check what we have on this motherboard */
switch (c->x86_model) { switch (c->x86_model) {
case 6: case 6:
cpu_model = CPU_SAMUEL; cpu_model = CPU_SAMUEL;
@ -634,6 +655,10 @@ static int __init longhaul_cpu_init(struct cpufreq_policy *policy)
cpufreq_frequency_table_get_attr(longhaul_table, policy->cpu); cpufreq_frequency_table_get_attr(longhaul_table, policy->cpu);
return 0; return 0;
err_acpi:
printk(KERN_ERR PFX "No ACPI support for CPU frequency changes.\n");
return -ENODEV;
} }
static int __devexit longhaul_cpu_exit(struct cpufreq_policy *policy) static int __devexit longhaul_cpu_exit(struct cpufreq_policy *policy)
@ -666,6 +691,18 @@ static int __init longhaul_init(void)
if (c->x86_vendor != X86_VENDOR_CENTAUR || c->x86 != 6) if (c->x86_vendor != X86_VENDOR_CENTAUR || c->x86 != 6)
return -ENODEV; return -ENODEV;
#ifdef CONFIG_SMP
if (num_online_cpus() > 1) {
return -ENODEV;
printk(KERN_ERR PFX "More than 1 CPU detected, longhaul disabled.\n");
}
#endif
#ifdef CONFIG_X86_IO_APIC
if (cpu_has_apic) {
printk(KERN_ERR PFX "APIC detected. Longhaul is currently broken in this configuration.\n");
return -ENODEV;
}
#endif
switch (c->x86_model) { switch (c->x86_model) {
case 6 ... 9: case 6 ... 9:
return cpufreq_register_driver(&longhaul_driver); return cpufreq_register_driver(&longhaul_driver);
@ -699,6 +736,6 @@ MODULE_AUTHOR ("Dave Jones <davej@codemonkey.org.uk>");
MODULE_DESCRIPTION ("Longhaul driver for VIA Cyrix processors."); MODULE_DESCRIPTION ("Longhaul driver for VIA Cyrix processors.");
MODULE_LICENSE ("GPL"); MODULE_LICENSE ("GPL");
module_init(longhaul_init); late_initcall(longhaul_init);
module_exit(longhaul_exit); module_exit(longhaul_exit);

View file

@ -284,39 +284,69 @@ EXPORT_SYMBOL_GPL(cpufreq_notify_transition);
* SYSFS INTERFACE * * SYSFS INTERFACE *
*********************************************************************/ *********************************************************************/
static struct cpufreq_governor *__find_governor(const char *str_governor)
{
struct cpufreq_governor *t;
list_for_each_entry(t, &cpufreq_governor_list, governor_list)
if (!strnicmp(str_governor,t->name,CPUFREQ_NAME_LEN))
return t;
return NULL;
}
/** /**
* cpufreq_parse_governor - parse a governor string * cpufreq_parse_governor - parse a governor string
*/ */
static int cpufreq_parse_governor (char *str_governor, unsigned int *policy, static int cpufreq_parse_governor (char *str_governor, unsigned int *policy,
struct cpufreq_governor **governor) struct cpufreq_governor **governor)
{ {
int err = -EINVAL;
if (!cpufreq_driver) if (!cpufreq_driver)
return -EINVAL; goto out;
if (cpufreq_driver->setpolicy) { if (cpufreq_driver->setpolicy) {
if (!strnicmp(str_governor, "performance", CPUFREQ_NAME_LEN)) { if (!strnicmp(str_governor, "performance", CPUFREQ_NAME_LEN)) {
*policy = CPUFREQ_POLICY_PERFORMANCE; *policy = CPUFREQ_POLICY_PERFORMANCE;
return 0; err = 0;
} else if (!strnicmp(str_governor, "powersave", CPUFREQ_NAME_LEN)) { } else if (!strnicmp(str_governor, "powersave", CPUFREQ_NAME_LEN)) {
*policy = CPUFREQ_POLICY_POWERSAVE; *policy = CPUFREQ_POLICY_POWERSAVE;
return 0; err = 0;
} }
return -EINVAL; } else if (cpufreq_driver->target) {
} else {
struct cpufreq_governor *t; struct cpufreq_governor *t;
mutex_lock(&cpufreq_governor_mutex); mutex_lock(&cpufreq_governor_mutex);
if (!cpufreq_driver || !cpufreq_driver->target)
goto out; t = __find_governor(str_governor);
list_for_each_entry(t, &cpufreq_governor_list, governor_list) {
if (!strnicmp(str_governor,t->name,CPUFREQ_NAME_LEN)) { if (t == NULL) {
char *name = kasprintf(GFP_KERNEL, "cpufreq_%s", str_governor);
if (name) {
int ret;
mutex_unlock(&cpufreq_governor_mutex);
ret = request_module(name);
mutex_lock(&cpufreq_governor_mutex);
if (ret == 0)
t = __find_governor(str_governor);
}
kfree(name);
}
if (t != NULL) {
*governor = t; *governor = t;
mutex_unlock(&cpufreq_governor_mutex); err = 0;
return 0;
} }
}
out:
mutex_unlock(&cpufreq_governor_mutex); mutex_unlock(&cpufreq_governor_mutex);
} }
return -EINVAL; out:
return err;
} }
@ -1265,23 +1295,21 @@ static int __cpufreq_governor(struct cpufreq_policy *policy, unsigned int event)
int cpufreq_register_governor(struct cpufreq_governor *governor) int cpufreq_register_governor(struct cpufreq_governor *governor)
{ {
struct cpufreq_governor *t; int err;
if (!governor) if (!governor)
return -EINVAL; return -EINVAL;
mutex_lock(&cpufreq_governor_mutex); mutex_lock(&cpufreq_governor_mutex);
list_for_each_entry(t, &cpufreq_governor_list, governor_list) { err = -EBUSY;
if (!strnicmp(governor->name,t->name,CPUFREQ_NAME_LEN)) { if (__find_governor(governor->name) == NULL) {
mutex_unlock(&cpufreq_governor_mutex); err = 0;
return -EBUSY;
}
}
list_add(&governor->governor_list, &cpufreq_governor_list); list_add(&governor->governor_list, &cpufreq_governor_list);
}
mutex_unlock(&cpufreq_governor_mutex); mutex_unlock(&cpufreq_governor_mutex);
return 0; return err;
} }
EXPORT_SYMBOL_GPL(cpufreq_register_governor); EXPORT_SYMBOL_GPL(cpufreq_register_governor);
@ -1343,6 +1371,11 @@ static int __cpufreq_set_policy(struct cpufreq_policy *data, struct cpufreq_poli
memcpy(&policy->cpuinfo, &data->cpuinfo, sizeof(struct cpufreq_cpuinfo)); memcpy(&policy->cpuinfo, &data->cpuinfo, sizeof(struct cpufreq_cpuinfo));
if (policy->min > data->min && policy->min > policy->max) {
ret = -EINVAL;
goto error_out;
}
/* verify the cpu speed can be set within this limit */ /* verify the cpu speed can be set within this limit */
ret = cpufreq_driver->verify(policy); ret = cpufreq_driver->verify(policy);
if (ret) if (ret)