linux/drivers/cpufreq/p4-clockmod.c
<<
>>
Prefs
   1/*
   2 *      Pentium 4/Xeon CPU on demand clock modulation/speed scaling
   3 *      (C) 2002 - 2003 Dominik Brodowski <linux@brodo.de>
   4 *      (C) 2002 Zwane Mwaikambo <zwane@commfireservices.com>
   5 *      (C) 2002 Arjan van de Ven <arjanv@redhat.com>
   6 *      (C) 2002 Tora T. Engstad
   7 *      All Rights Reserved
   8 *
   9 *      This program is free software; you can redistribute it and/or
  10 *      modify it under the terms of the GNU General Public License
  11 *      as published by the Free Software Foundation; either version
  12 *      2 of the License, or (at your option) any later version.
  13 *
  14 *      The author(s) of this software shall not be held liable for damages
  15 *      of any nature resulting due to the use of this software. This
  16 *      software is provided AS-IS with no warranties.
  17 *
  18 *      Date            Errata                  Description
  19 *      20020525        N44, O17        12.5% or 25% DC causes lockup
  20 *
  21 */
  22
  23#include <linux/kernel.h>
  24#include <linux/module.h>
  25#include <linux/init.h>
  26#include <linux/smp.h>
  27#include <linux/cpufreq.h>
  28#include <linux/cpumask.h>
  29#include <linux/timex.h>
  30
  31#include <asm/processor.h>
  32#include <asm/msr.h>
  33#include <asm/timer.h>
  34#include <asm/cpu_device_id.h>
  35
  36#include "speedstep-lib.h"
  37
  38#define PFX     "p4-clockmod: "
  39
  40/*
  41 * Duty Cycle (3bits), note DC_DISABLE is not specified in
  42 * intel docs i just use it to mean disable
  43 */
  44enum {
  45        DC_RESV, DC_DFLT, DC_25PT, DC_38PT, DC_50PT,
  46        DC_64PT, DC_75PT, DC_88PT, DC_DISABLE
  47};
  48
  49#define DC_ENTRIES      8
  50
  51
  52static int has_N44_O17_errata[NR_CPUS];
  53static unsigned int stock_freq;
  54static struct cpufreq_driver p4clockmod_driver;
  55static unsigned int cpufreq_p4_get(unsigned int cpu);
  56
  57static int cpufreq_p4_setdc(unsigned int cpu, unsigned int newstate)
  58{
  59        u32 l, h;
  60
  61        if ((newstate > DC_DISABLE) || (newstate == DC_RESV))
  62                return -EINVAL;
  63
  64        rdmsr_on_cpu(cpu, MSR_IA32_THERM_STATUS, &l, &h);
  65
  66        if (l & 0x01)
  67                pr_debug("CPU#%d currently thermal throttled\n", cpu);
  68
  69        if (has_N44_O17_errata[cpu] &&
  70            (newstate == DC_25PT || newstate == DC_DFLT))
  71                newstate = DC_38PT;
  72
  73        rdmsr_on_cpu(cpu, MSR_IA32_THERM_CONTROL, &l, &h);
  74        if (newstate == DC_DISABLE) {
  75                pr_debug("CPU#%d disabling modulation\n", cpu);
  76                wrmsr_on_cpu(cpu, MSR_IA32_THERM_CONTROL, l & ~(1<<4), h);
  77        } else {
  78                pr_debug("CPU#%d setting duty cycle to %d%%\n",
  79                        cpu, ((125 * newstate) / 10));
  80                /* bits 63 - 5  : reserved
  81                 * bit  4       : enable/disable
  82                 * bits 3-1     : duty cycle
  83                 * bit  0       : reserved
  84                 */
  85                l = (l & ~14);
  86                l = l | (1<<4) | ((newstate & 0x7)<<1);
  87                wrmsr_on_cpu(cpu, MSR_IA32_THERM_CONTROL, l, h);
  88        }
  89
  90        return 0;
  91}
  92
  93
  94static struct cpufreq_frequency_table p4clockmod_table[] = {
  95        {DC_RESV, CPUFREQ_ENTRY_INVALID},
  96        {DC_DFLT, 0},
  97        {DC_25PT, 0},
  98        {DC_38PT, 0},
  99        {DC_50PT, 0},
 100        {DC_64PT, 0},
 101        {DC_75PT, 0},
 102        {DC_88PT, 0},
 103        {DC_DISABLE, 0},
 104        {DC_RESV, CPUFREQ_TABLE_END},
 105};
 106
 107
 108static int cpufreq_p4_target(struct cpufreq_policy *policy,
 109                             unsigned int target_freq,
 110                             unsigned int relation)
 111{
 112        unsigned int    newstate = DC_RESV;
 113        struct cpufreq_freqs freqs;
 114        int i;
 115
 116        if (cpufreq_frequency_table_target(policy, &p4clockmod_table[0],
 117                                target_freq, relation, &newstate))
 118                return -EINVAL;
 119
 120        freqs.old = cpufreq_p4_get(policy->cpu);
 121        freqs.new = stock_freq * p4clockmod_table[newstate].driver_data / 8;
 122
 123        if (freqs.new == freqs.old)
 124                return 0;
 125
 126        /* notifiers */
 127        cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE);
 128
 129        /* run on each logical CPU,
 130         * see section 13.15.3 of IA32 Intel Architecture Software
 131         * Developer's Manual, Volume 3
 132         */
 133        for_each_cpu(i, policy->cpus)
 134                cpufreq_p4_setdc(i, p4clockmod_table[newstate].driver_data);
 135
 136        /* notifiers */
 137        cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE);
 138
 139        return 0;
 140}
 141
 142
 143static int cpufreq_p4_verify(struct cpufreq_policy *policy)
 144{
 145        return cpufreq_frequency_table_verify(policy, &p4clockmod_table[0]);
 146}
 147
 148
 149static unsigned int cpufreq_p4_get_frequency(struct cpuinfo_x86 *c)
 150{
 151        if (c->x86 == 0x06) {
 152                if (cpu_has(c, X86_FEATURE_EST))
 153                        printk_once(KERN_WARNING PFX "Warning: EST-capable "
 154                               "CPU detected. The acpi-cpufreq module offers "
 155                               "voltage scaling in addition to frequency "
 156                               "scaling. You should use that instead of "
 157                               "p4-clockmod, if possible.\n");
 158                switch (c->x86_model) {
 159                case 0x0E: /* Core */
 160                case 0x0F: /* Core Duo */
 161                case 0x16: /* Celeron Core */
 162                case 0x1C: /* Atom */
 163                        p4clockmod_driver.flags |= CPUFREQ_CONST_LOOPS;
 164                        return speedstep_get_frequency(SPEEDSTEP_CPU_PCORE);
 165                case 0x0D: /* Pentium M (Dothan) */
 166                        p4clockmod_driver.flags |= CPUFREQ_CONST_LOOPS;
 167                        /* fall through */
 168                case 0x09: /* Pentium M (Banias) */
 169                        return speedstep_get_frequency(SPEEDSTEP_CPU_PM);
 170                }
 171        }
 172
 173        if (c->x86 != 0xF)
 174                return 0;
 175
 176        /* on P-4s, the TSC runs with constant frequency independent whether
 177         * throttling is active or not. */
 178        p4clockmod_driver.flags |= CPUFREQ_CONST_LOOPS;
 179
 180        if (speedstep_detect_processor() == SPEEDSTEP_CPU_P4M) {
 181                printk(KERN_WARNING PFX "Warning: Pentium 4-M detected. "
 182                       "The speedstep-ich or acpi cpufreq modules offer "
 183                       "voltage scaling in addition of frequency scaling. "
 184                       "You should use either one instead of p4-clockmod, "
 185                       "if possible.\n");
 186                return speedstep_get_frequency(SPEEDSTEP_CPU_P4M);
 187        }
 188
 189        return speedstep_get_frequency(SPEEDSTEP_CPU_P4D);
 190}
 191
 192
 193
 194static int cpufreq_p4_cpu_init(struct cpufreq_policy *policy)
 195{
 196        struct cpuinfo_x86 *c = &cpu_data(policy->cpu);
 197        int cpuid = 0;
 198        unsigned int i;
 199
 200#ifdef CONFIG_SMP
 201        cpumask_copy(policy->cpus, cpu_sibling_mask(policy->cpu));
 202#endif
 203
 204        /* Errata workaround */
 205        cpuid = (c->x86 << 8) | (c->x86_model << 4) | c->x86_mask;
 206        switch (cpuid) {
 207        case 0x0f07:
 208        case 0x0f0a:
 209        case 0x0f11:
 210        case 0x0f12:
 211                has_N44_O17_errata[policy->cpu] = 1;
 212                pr_debug("has errata -- disabling low frequencies\n");
 213        }
 214
 215        if (speedstep_detect_processor() == SPEEDSTEP_CPU_P4D &&
 216            c->x86_model < 2) {
 217                /* switch to maximum frequency and measure result */
 218                cpufreq_p4_setdc(policy->cpu, DC_DISABLE);
 219                recalibrate_cpu_khz();
 220        }
 221        /* get max frequency */
 222        stock_freq = cpufreq_p4_get_frequency(c);
 223        if (!stock_freq)
 224                return -EINVAL;
 225
 226        /* table init */
 227        for (i = 1; (p4clockmod_table[i].frequency != CPUFREQ_TABLE_END); i++) {
 228                if ((i < 2) && (has_N44_O17_errata[policy->cpu]))
 229                        p4clockmod_table[i].frequency = CPUFREQ_ENTRY_INVALID;
 230                else
 231                        p4clockmod_table[i].frequency = (stock_freq * i)/8;
 232        }
 233        cpufreq_frequency_table_get_attr(p4clockmod_table, policy->cpu);
 234
 235        /* cpuinfo and default policy values */
 236
 237        /* the transition latency is set to be 1 higher than the maximum
 238         * transition latency of the ondemand governor */
 239        policy->cpuinfo.transition_latency = 10000001;
 240        policy->cur = stock_freq;
 241
 242        return cpufreq_frequency_table_cpuinfo(policy, &p4clockmod_table[0]);
 243}
 244
 245
 246static int cpufreq_p4_cpu_exit(struct cpufreq_policy *policy)
 247{
 248        cpufreq_frequency_table_put_attr(policy->cpu);
 249        return 0;
 250}
 251
 252static unsigned int cpufreq_p4_get(unsigned int cpu)
 253{
 254        u32 l, h;
 255
 256        rdmsr_on_cpu(cpu, MSR_IA32_THERM_CONTROL, &l, &h);
 257
 258        if (l & 0x10) {
 259                l = l >> 1;
 260                l &= 0x7;
 261        } else
 262                l = DC_DISABLE;
 263
 264        if (l != DC_DISABLE)
 265                return stock_freq * l / 8;
 266
 267        return stock_freq;
 268}
 269
 270static struct freq_attr *p4clockmod_attr[] = {
 271        &cpufreq_freq_attr_scaling_available_freqs,
 272        NULL,
 273};
 274
 275static struct cpufreq_driver p4clockmod_driver = {
 276        .verify         = cpufreq_p4_verify,
 277        .target         = cpufreq_p4_target,
 278        .init           = cpufreq_p4_cpu_init,
 279        .exit           = cpufreq_p4_cpu_exit,
 280        .get            = cpufreq_p4_get,
 281        .name           = "p4-clockmod",
 282        .attr           = p4clockmod_attr,
 283};
 284
 285static const struct x86_cpu_id cpufreq_p4_id[] = {
 286        { X86_VENDOR_INTEL, X86_FAMILY_ANY, X86_MODEL_ANY, X86_FEATURE_ACC },
 287        {}
 288};
 289
 290/*
 291 * Intentionally no MODULE_DEVICE_TABLE here: this driver should not
 292 * be auto loaded.  Please don't add one.
 293 */
 294
 295static int __init cpufreq_p4_init(void)
 296{
 297        int ret;
 298
 299        /*
 300         * THERM_CONTROL is architectural for IA32 now, so
 301         * we can rely on the capability checks
 302         */
 303        if (!x86_match_cpu(cpufreq_p4_id) || !boot_cpu_has(X86_FEATURE_ACPI))
 304                return -ENODEV;
 305
 306        ret = cpufreq_register_driver(&p4clockmod_driver);
 307        if (!ret)
 308                printk(KERN_INFO PFX "P4/Xeon(TM) CPU On-Demand Clock "
 309                                "Modulation available\n");
 310
 311        return ret;
 312}
 313
 314
 315static void __exit cpufreq_p4_exit(void)
 316{
 317        cpufreq_unregister_driver(&p4clockmod_driver);
 318}
 319
 320
 321MODULE_AUTHOR("Zwane Mwaikambo <zwane@commfireservices.com>");
 322MODULE_DESCRIPTION("cpufreq driver for Pentium(TM) 4/Xeon(TM)");
 323MODULE_LICENSE("GPL");
 324
 325late_initcall(cpufreq_p4_init);
 326module_exit(cpufreq_p4_exit);
 327
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.