bk://linux-dj.bkbits.net/cpufreq davej@redhat.com|ChangeSet|20040319205549|52190 davej diff -Nru a/arch/i386/kernel/cpu/cpufreq/Kconfig b/arch/i386/kernel/cpu/cpufreq/Kconfig --- a/arch/i386/kernel/cpu/cpufreq/Kconfig Sun Mar 28 13:13:56 2004 +++ b/arch/i386/kernel/cpu/cpufreq/Kconfig Sun Mar 28 13:13:56 2004 @@ -120,6 +120,21 @@ If in doubt, say N. +config X86_SPEEDSTEP_CENTRINO_TABLE + bool + depends on X86_SPEEDSTEP_CENTRINO + default y + +config X86_SPEEDSTEP_CENTRINO_ACPI + bool "Use ACPI tables to decode valid frequency/voltage pairs (EXPERIMENTAL)" + depends on EXPERIMENTAL + depends on ((X86_SPEEDSTEP_CENTRINO = "m" && ACPI_PROCESSOR) || (X86_SPEEDSTEP_CENTRINO = "y" && ACPI_PROCESSOR = "y")) + help + Use primarily the information provided in the BIOS ACPI tables + to determine valid CPU frequency and voltage pairings. + + If in doubt, say Y. + config X86_SPEEDSTEP_ICH tristate "Intel Speedstep on ICH-M chipsets (ioport interface)" depends on CPU_FREQ_TABLE @@ -160,6 +175,16 @@ tristate depends on (X86_SPEEDSTEP_ICH || X86_SPEEDSTEP_SMI || X86_P4_CLOCKMOD) default (X86_SPEEDSTEP_ICH || X86_SPEEDSTEP_SMI || X86_P4_CLOCKMOD) + +config X86_SPEEDSTEP_RELAXED_CAP_CHECK + bool "Relaxed speedstep capability checks" + depends on (X86_SPEEDSTEP_SMI || X86_SPEEDSTEP_ICH) + help + Don't perform all checks for a speedstep capable system which would + normally be done. Some ancient or strange systems, though speedstep + capable, don't always indicate that they are speedstep capable. This + option let's the probing code bypass some of those checks if the + parameter "relaxed_check=1" is passed to the module. config X86_LONGRUN tristate "Transmeta LongRun" diff -Nru a/arch/i386/kernel/cpu/cpufreq/longhaul.c b/arch/i386/kernel/cpu/cpufreq/longhaul.c --- a/arch/i386/kernel/cpu/cpufreq/longhaul.c Sun Mar 28 13:13:56 2004 +++ b/arch/i386/kernel/cpu/cpufreq/longhaul.c Sun Mar 28 13:13:56 2004 @@ -458,7 +458,7 @@ return 0; } -static int longhaul_cpu_exit(struct cpufreq_policy *policy) +static int __exit longhaul_cpu_exit(struct cpufreq_policy *policy) { cpufreq_frequency_table_put_attr(policy->cpu); return 0; diff -Nru a/arch/i386/kernel/cpu/cpufreq/longrun.c b/arch/i386/kernel/cpu/cpufreq/longrun.c --- a/arch/i386/kernel/cpu/cpufreq/longrun.c Sun Mar 28 13:13:56 2004 +++ b/arch/i386/kernel/cpu/cpufreq/longrun.c Sun Mar 28 13:13:56 2004 @@ -33,7 +33,7 @@ * Reads the current LongRun policy by access to MSR_TMTA_LONGRUN_FLAGS * and MSR_TMTA_LONGRUN_CTRL */ -static void longrun_get_policy(struct cpufreq_policy *policy) +static void __init longrun_get_policy(struct cpufreq_policy *policy) { u32 msr_lo, msr_hi; diff -Nru a/arch/i386/kernel/cpu/cpufreq/p4-clockmod.c b/arch/i386/kernel/cpu/cpufreq/p4-clockmod.c --- a/arch/i386/kernel/cpu/cpufreq/p4-clockmod.c Sun Mar 28 13:13:56 2004 +++ b/arch/i386/kernel/cpu/cpufreq/p4-clockmod.c Sun Mar 28 13:13:56 2004 @@ -181,21 +181,24 @@ { if ((c->x86 == 0x06) && (c->x86_model == 0x09)) { /* Pentium M */ - printk(KERN_DEBUG PFX "Warning: Pentium M detected. The speedstep_centrino module\n"); - printk(KERN_DEBUG PFX "offers voltage scaling in addition of frequency scaling. You\n"); - printk(KERN_DEBUG PFX "should use that instead of p4-clockmod, if possible.\n"); + printk(KERN_WARNING PFX "Warning: Pentium M detected. " + "The speedstep_centrino module offers voltage scaling" + " in addition of frequency scaling. You should use " + "that instead of p4-clockmod, if possible.\n"); return speedstep_get_processor_frequency(SPEEDSTEP_PROCESSOR_PM); } if (c->x86 != 0xF) { - printk(KERN_DEBUG PFX "Unknown p4-clockmod-capable CPU. Please send an e-mail to \n"); + printk(KERN_WARNING PFX "Unknown p4-clockmod-capable CPU. Please send an e-mail to \n"); return 0; } if (speedstep_detect_processor() == SPEEDSTEP_PROCESSOR_P4M) { - printk(KERN_DEBUG PFX "Warning: Pentium 4-M detected. The speedstep-ich or acpi cpufreq \n"); - printk(KERN_DEBUG PFX "modules offers voltage scaling in addition of frequency scaling. You\n"); - printk(KERN_DEBUG PFX "should use either one instead of p4-clockmod, if possible.\n"); + printk(KERN_WARNING PFX "Warning: Pentium 4-M detected. " + "The speedstep-ich or acpi cpufreq modules offers " + "voltage scaling in addition of frequency scaling. " + "You should use either one instead of p4-clockmod, " + "if possible.\n"); return speedstep_get_processor_frequency(SPEEDSTEP_PROCESSOR_P4M); } diff -Nru a/arch/i386/kernel/cpu/cpufreq/speedstep-centrino.c b/arch/i386/kernel/cpu/cpufreq/speedstep-centrino.c --- a/arch/i386/kernel/cpu/cpufreq/speedstep-centrino.c Sun Mar 28 13:13:56 2004 +++ b/arch/i386/kernel/cpu/cpufreq/speedstep-centrino.c Sun Mar 28 13:13:56 2004 @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -46,7 +47,9 @@ }; /* Operating points for current CPU */ -static const struct cpu_model *centrino_model; +static struct cpu_model *centrino_model; + +#ifdef CONFIG_X86_SPEEDSTEP_CENTRINO_TABLE /* Computes the correct form for IA32_PERF_CTL MSR for a particular frequency/voltage operating point; frequency in MHz, volts in mV. @@ -172,7 +175,7 @@ /* CPU models, their operating frequency range, and freq/voltage operating points */ -static const struct cpu_model models[] = +static struct cpu_model models[] = { _CPU( 900, " 900"), CPU(1000), @@ -187,6 +190,48 @@ }; #undef CPU +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; + if (model->model_name == NULL) { + printk(KERN_INFO PFX "no support for CPU model \"%s\": " + "send /proc/cpuinfo to " MAINTAINER "\n", + cpu->x86_model_id); + return -ENOENT; + } + + centrino_model = model; + + printk(KERN_INFO PFX "found \"%s\": max frequency: %dkHz\n", + model->model_name, model->max_freq); + + return 0; +} + +#else +static inline int centrino_cpu_init_table(struct cpufreq_policy *policy) { return -ENODEV; } +#endif /* CONFIG_X86_SPEEDSTEP_CENTRINO_TABLE */ + /* Extract clock in kHz from PERF_CTL value */ static unsigned extract_clock(unsigned msr) { @@ -203,13 +248,148 @@ return extract_clock(l); } + +#ifdef CONFIG_X86_SPEEDSTEP_CENTRINO_ACPI + +static struct acpi_processor_performance p; + +#include +#include + +#define ACPI_PDC_CAPABILITY_ENHANCED_SPEEDSTEP 0x1 + +/* + * centrino_cpu_init_acpi - register with ACPI P-States library + * + * Register with the ACPI P-States library (part of drivers/acpi/processor.c) + * in order to determine correct frequency and voltage pairings by reading + * the _PSS of the ACPI DSDT or SSDT tables. + */ +static int centrino_cpu_init_acpi(struct cpufreq_policy *policy) +{ + union acpi_object arg0 = {ACPI_TYPE_BUFFER}; + u32 arg0_buf[3]; + struct acpi_object_list arg_list = {1, &arg0}; + unsigned long cur_freq; + int result = 0, i; + + /* _PDC settings */ + arg0.buffer.length = 12; + arg0.buffer.pointer = (u8 *) arg0_buf; + arg0_buf[0] = ACPI_PDC_REVISION_ID; + arg0_buf[1] = 1; + arg0_buf[2] = ACPI_PDC_CAPABILITY_ENHANCED_SPEEDSTEP; + + p.pdc = &arg_list; + + /* register with ACPI core */ + if (acpi_processor_register_performance(&p, 0)) + return -EIO; + + /* verify the acpi_data */ + if (p.state_count <= 1) { + printk(KERN_DEBUG "No P-States\n"); + result = -ENODEV; + goto err_unreg; + } + + if ((p.control_register.space_id != ACPI_ADR_SPACE_FIXED_HARDWARE) || + (p.status_register.space_id != ACPI_ADR_SPACE_FIXED_HARDWARE)) { + printk(KERN_DEBUG "Invalid control/status registers\n"); + result = -EIO; + goto err_unreg; + } + + for (i=0; imodel_name=NULL; + centrino_model->max_freq = p.states[0].core_frequency * 1000; + centrino_model->op_points = kmalloc(sizeof(struct cpufreq_frequency_table) * + (p.state_count + 1), GFP_KERNEL); + if (!centrino_model->op_points) { + result = -ENOMEM; + goto err_kfree; + } + + cur_freq = get_cur_freq(); + + for (i=0; iop_points[i].index = p.states[i].control; + centrino_model->op_points[i].frequency = p.states[i].core_frequency * 1000; + if (cur_freq == centrino_model->op_points[i].frequency) + p.state = i; + } + centrino_model->op_points[p.state_count].frequency = CPUFREQ_TABLE_END; + + return 0; + + err_kfree: + kfree(centrino_model); + err_unreg: + acpi_processor_unregister_performance(&p, 0); + return (result); +} +#else +static inline int centrino_cpu_init_acpi(struct cpufreq_policy *policy) { return -ENODEV; } +#endif + static int centrino_cpu_init(struct cpufreq_policy *policy) { unsigned freq; + unsigned l, h; + int ret; - if (policy->cpu != 0 || centrino_model == NULL) + if (policy->cpu != 0) return -ENODEV; + if (centrino_cpu_init_acpi(policy)) { + if (centrino_cpu_init_table(policy)) { + return -ENODEV; + } + } + + /* Check to see if Enhanced SpeedStep is enabled, and try to + enable it if not. */ + rdmsr(MSR_IA32_MISC_ENABLE, l, h); + + if (!(l & (1<<16))) { + l |= (1<<16); + wrmsr(MSR_IA32_MISC_ENABLE, l, h); + + /* check to see if it stuck */ + rdmsr(MSR_IA32_MISC_ENABLE, l, h); + if (!(l & (1<<16))) { + printk(KERN_INFO PFX "couldn't enable Enhanced SpeedStep\n"); + return -ENODEV; + } + } + freq = get_cur_freq(); policy->governor = CPUFREQ_DEFAULT_GOVERNOR; @@ -219,7 +399,33 @@ dprintk(KERN_INFO PFX "centrino_cpu_init: policy=%d cur=%dkHz\n", policy->policy, policy->cur); - return cpufreq_frequency_table_cpuinfo(policy, centrino_model->op_points); + ret = cpufreq_frequency_table_cpuinfo(policy, centrino_model->op_points); + if (ret) + return (ret); + + cpufreq_frequency_table_get_attr(centrino_model->op_points, policy->cpu); + + return 0; +} + +static int centrino_cpu_exit(struct cpufreq_policy *policy) +{ + if (!centrino_model) + return -ENODEV; + + cpufreq_frequency_table_put_attr(policy->cpu); + +#ifdef CONFIG_X86_SPEEDSTEP_CENTRINO_ACPI + if (!centrino_model->model_name) { + acpi_processor_unregister_performance(&p, 0); + kfree(centrino_model->op_points); + kfree(centrino_model); + } +#endif + + centrino_model = NULL; + + return 0; } /** @@ -295,12 +501,19 @@ return 0; } +static struct freq_attr* centrino_attr[] = { + &cpufreq_freq_attr_scaling_available_freqs, + NULL, +}; + static struct cpufreq_driver centrino_driver = { .name = "centrino", /* should be speedstep-centrino, but there's a 16 char limit */ .init = centrino_cpu_init, + .exit = centrino_cpu_exit, .verify = centrino_verify, .target = centrino_target, + .attr = centrino_attr, .owner = THIS_MODULE, }; @@ -322,55 +535,10 @@ static int __init centrino_init(void) { struct cpuinfo_x86 *cpu = cpu_data; - const struct cpu_model *model; - unsigned l, h; 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; - } - - /* Check to see if Enhanced SpeedStep is enabled, and try to - enable it if not. */ - rdmsr(MSR_IA32_MISC_ENABLE, l, h); - - if (!(l & (1<<16))) { - l |= (1<<16); - wrmsr(MSR_IA32_MISC_ENABLE, l, h); - - /* check to see if it stuck */ - rdmsr(MSR_IA32_MISC_ENABLE, l, h); - if (!(l & (1<<16))) { - printk(KERN_INFO PFX "couldn't enable Enhanced SpeedStep\n"); - return -ENODEV; - } - } - - for(model = models; model->model_name != NULL; model++) - if (strcmp(cpu->x86_model_id, model->model_name) == 0) - break; - if (model->model_name == NULL) { - printk(KERN_INFO PFX "no support for CPU model \"%s\": " - "send /proc/cpuinfo to " MAINTAINER "\n", - cpu->x86_model_id); - return -ENOENT; - } - - centrino_model = model; - - printk(KERN_INFO PFX "found \"%s\": max frequency: %dkHz\n", - model->model_name, model->max_freq); - return cpufreq_register_driver(¢rino_driver); } @@ -383,5 +551,5 @@ MODULE_DESCRIPTION ("Enhanced SpeedStep driver for Intel Pentium M processors."); MODULE_LICENSE ("GPL"); -module_init(centrino_init); +late_initcall(centrino_init); module_exit(centrino_exit); diff -Nru a/arch/i386/kernel/cpu/cpufreq/speedstep-lib.c b/arch/i386/kernel/cpu/cpufreq/speedstep-lib.c --- a/arch/i386/kernel/cpu/cpufreq/speedstep-lib.c Sun Mar 28 13:13:56 2004 +++ b/arch/i386/kernel/cpu/cpufreq/speedstep-lib.c Sun Mar 28 13:13:56 2004 @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -30,6 +31,12 @@ #define dprintk(msg...) do { } while(0) #endif +#ifdef CONFIG_X86_SPEEDSTEP_RELAXED_CAP_CHECK +static int relaxed_check = 0; +#else +#define relaxed_check 0 +#endif + /********************************************************************* * GET PROCESSOR CORE SPEED IN KHZ * *********************************************************************/ @@ -120,7 +127,7 @@ msr_tmp = (msr_lo >> 22) & 0x1f; dprintk(KERN_DEBUG "speedstep-lib: bits 22-26 are 0x%x\n", msr_tmp); - return (msr_tmp * 100 * 10000); + return (msr_tmp * 100 * 1000); } @@ -265,6 +272,7 @@ ebx = cpuid_ebx(0x00000001); ebx &= 0x000000FF; + if (ebx != 0x06) return 0; @@ -292,7 +300,7 @@ */ rdmsr(MSR_IA32_PLATFORM_ID, msr_lo, msr_hi); dprintk(KERN_DEBUG "cpufreq: Coppermine: MSR_IA32_PLATFORM ID is 0x%x, 0x%x\n", msr_lo, msr_hi); - if ((msr_hi & (1<<18)) && (msr_hi & (3<<24))) { + if ((msr_hi & (1<<18)) && (relaxed_check ? 1 : (msr_hi & (3<<24)))) { if (c->x86_mask == 0x01) return SPEEDSTEP_PROCESSOR_PIII_C_EARLY; else @@ -361,6 +369,11 @@ return (ret); } EXPORT_SYMBOL_GPL(speedstep_get_freqs); + +#ifdef CONFIG_X86_SPEEDSTEP_RELAXED_CAP_CHECK +module_param(relaxed_check, int, 0444); +MODULE_PARM_DESC(relaxed_check, "Don't do all checks for speedstep capability."); +#endif MODULE_AUTHOR ("Dominik Brodowski "); MODULE_DESCRIPTION ("Library for Intel SpeedStep 1 or 2 cpufreq drivers."); diff -Nru a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c --- a/drivers/cpufreq/cpufreq.c Sun Mar 28 13:13:56 2004 +++ b/drivers/cpufreq/cpufreq.c Sun Mar 28 13:13:56 2004 @@ -963,6 +963,7 @@ int cpufreq_register_driver(struct cpufreq_driver *driver_data) { unsigned long flags; + int ret; if (!driver_data || !driver_data->verify || !driver_data->init || ((!driver_data->setpolicy) && (!driver_data->target))) @@ -976,7 +977,28 @@ cpufreq_driver = driver_data; spin_unlock_irqrestore(&cpufreq_driver_lock, flags); - return sysdev_driver_register(&cpu_sysdev_class,&cpufreq_sysdev_driver); + ret = sysdev_driver_register(&cpu_sysdev_class,&cpufreq_sysdev_driver); + + if ((!ret) && !(cpufreq_driver->flags & CPUFREQ_STICKY)) { + int i; + ret = -ENODEV; + + /* check for at least one working CPU */ + for (i=0; iinit() calls failed, unregister */ + if (ret) { + sysdev_driver_unregister(&cpu_sysdev_class, &cpufreq_sysdev_driver); + + spin_lock_irqsave(&cpufreq_driver_lock, flags); + cpufreq_driver = NULL; + spin_unlock_irqrestore(&cpufreq_driver_lock, flags); + } + } + + return (ret); } EXPORT_SYMBOL_GPL(cpufreq_register_driver); diff -Nru a/include/linux/cpufreq.h b/include/linux/cpufreq.h --- a/include/linux/cpufreq.h Sun Mar 28 13:13:56 2004 +++ b/include/linux/cpufreq.h Sun Mar 28 13:13:56 2004 @@ -175,6 +175,7 @@ struct cpufreq_driver { struct module *owner; char name[CPUFREQ_NAME_LEN]; + u8 flags; /* needed by all drivers */ int (*init) (struct cpufreq_policy *policy); @@ -191,6 +192,11 @@ int (*resume) (struct cpufreq_policy *policy); struct freq_attr **attr; }; + +/* flags */ + +#define CPUFREQ_STICKY 0x01 /* the driver isn't removed even if + all ->init() calls failed */ int cpufreq_register_driver(struct cpufreq_driver *driver_data); int cpufreq_unregister_driver(struct cpufreq_driver *driver_data);