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        {0, DC_RESV, CPUFREQ_ENTRY_INVALID},
  96        {0, DC_DFLT, 0},
  97        {0, DC_25PT, 0},
  98        {0, DC_38PT, 0},
  99        {0, DC_50PT, 0},
 100        {0, DC_64PT, 0},
 101        {0, DC_75PT, 0},
 102        {0, DC_88PT, 0},
 103        {0, DC_DISABLE, 0},
 104        {0, DC_RESV, CPUFREQ_TABLE_END},
 105};
 106
 107
 108static int cpufreq_p4_target(struct cpufreq_policy *policy, unsigned int index)
 109{
 110        int i;
 111
 112        /* run on each logical CPU,
 113         * see section 13.15.3 of IA32 Intel Architecture Software
 114         * Developer's Manual, Volume 3
 115         */
 116        for_each_cpu(i, policy->cpus)
 117                cpufreq_p4_setdc(i, p4clockmod_table[index].driver_data);
 118
 119        return 0;
 120}
 121
 122
 123static unsigned int cpufreq_p4_get_frequency(struct cpuinfo_x86 *c)
 124{
 125        if (c->x86 == 0x06) {
 126                if (cpu_has(c, X86_FEATURE_EST))
 127                        printk_once(KERN_WARNING PFX "Warning: EST-capable "
 128                               "CPU detected. The acpi-cpufreq module offers "
 129                               "voltage scaling in addition to frequency "
 130                               "scaling. You should use that instead of "
 131                               "p4-clockmod, if possible.\n");
 132                switch (c->x86_model) {
 133                case 0x0E: /* Core */
 134                case 0x0F: /* Core Duo */
 135                case 0x16: /* Celeron Core */
 136                case 0x1C: /* Atom */
 137                        p4clockmod_driver.flags |= CPUFREQ_CONST_LOOPS;
 138                        return speedstep_get_frequency(SPEEDSTEP_CPU_PCORE);
 139                case 0x0D: /* Pentium M (Dothan) */
 140                        p4clockmod_driver.flags |= CPUFREQ_CONST_LOOPS;
 141                        /* fall through */
 142                case 0x09: /* Pentium M (Banias) */
 143                        return speedstep_get_frequency(SPEEDSTEP_CPU_PM);
 144                }
 145        }
 146
 147        if (c->x86 != 0xF)
 148                return 0;
 149
 150        /* on P-4s, the TSC runs with constant frequency independent whether
 151         * throttling is active or not. */
 152        p4clockmod_driver.flags |= CPUFREQ_CONST_LOOPS;
 153
 154        if (speedstep_detect_processor() == SPEEDSTEP_CPU_P4M) {
 155                printk(KERN_WARNING PFX "Warning: Pentium 4-M detected. "
 156                       "The speedstep-ich or acpi cpufreq modules offer "
 157                       "voltage scaling in addition of frequency scaling. "
 158                       "You should use either one instead of p4-clockmod, "
 159                       "if possible.\n");
 160                return speedstep_get_frequency(SPEEDSTEP_CPU_P4M);
 161        }
 162
 163        return speedstep_get_frequency(SPEEDSTEP_CPU_P4D);
 164}
 165
 166
 167
 168static int cpufreq_p4_cpu_init(struct cpufreq_policy *policy)
 169{
 170        struct cpuinfo_x86 *c = &cpu_data(policy->cpu);
 171        int cpuid = 0;
 172        unsigned int i;
 173
 174#ifdef CONFIG_SMP
 175        cpumask_copy(policy->cpus, cpu_sibling_mask(policy->cpu));
 176#endif
 177
 178        /* Errata workaround */
 179        cpuid = (c->x86 << 8) | (c->x86_model << 4) | c->x86_mask;
 180        switch (cpuid) {
 181        case 0x0f07:
 182        case 0x0f0a:
 183        case 0x0f11:
 184        case 0x0f12:
 185                has_N44_O17_errata[policy->cpu] = 1;
 186                pr_debug("has errata -- disabling low frequencies\n");
 187        }
 188
 189        if (speedstep_detect_processor() == SPEEDSTEP_CPU_P4D &&
 190            c->x86_model < 2) {
 191                /* switch to maximum frequency and measure result */
 192                cpufreq_p4_setdc(policy->cpu, DC_DISABLE);
 193                recalibrate_cpu_khz();
 194        }
 195        /* get max frequency */
 196        stock_freq = cpufreq_p4_get_frequency(c);
 197        if (!stock_freq)
 198                return -EINVAL;
 199
 200        /* table init */
 201        for (i = 1; (p4clockmod_table[i].frequency != CPUFREQ_TABLE_END); i++) {
 202                if ((i < 2) && (has_N44_O17_errata[policy->cpu]))
 203                        p4clockmod_table[i].frequency = CPUFREQ_ENTRY_INVALID;
 204                else
 205                        p4clockmod_table[i].frequency = (stock_freq * i)/8;
 206        }
 207
 208        /* cpuinfo and default policy values */
 209
 210        /* the transition latency is set to be 1 higher than the maximum
 211         * transition latency of the ondemand governor */
 212        policy->cpuinfo.transition_latency = 10000001;
 213
 214        return cpufreq_table_validate_and_show(policy, &p4clockmod_table[0]);
 215}
 216
 217
 218static unsigned int cpufreq_p4_get(unsigned int cpu)
 219{
 220        u32 l, h;
 221
 222        rdmsr_on_cpu(cpu, MSR_IA32_THERM_CONTROL, &l, &h);
 223
 224        if (l & 0x10) {
 225                l = l >> 1;
 226                l &= 0x7;
 227        } else
 228                l = DC_DISABLE;
 229
 230        if (l != DC_DISABLE)
 231                return stock_freq * l / 8;
 232
 233        return stock_freq;
 234}
 235
 236static struct cpufreq_driver p4clockmod_driver = {
 237        .verify         = cpufreq_generic_frequency_table_verify,
 238        .target_index   = cpufreq_p4_target,
 239        .init           = cpufreq_p4_cpu_init,
 240        .get            = cpufreq_p4_get,
 241        .name           = "p4-clockmod",
 242        .attr           = cpufreq_generic_attr,
 243};
 244
 245static const struct x86_cpu_id cpufreq_p4_id[] = {
 246        { X86_VENDOR_INTEL, X86_FAMILY_ANY, X86_MODEL_ANY, X86_FEATURE_ACC },
 247        {}
 248};
 249
 250/*
 251 * Intentionally no MODULE_DEVICE_TABLE here: this driver should not
 252 * be auto loaded.  Please don't add one.
 253 */
 254
 255static int __init cpufreq_p4_init(void)
 256{
 257        int ret;
 258
 259        /*
 260         * THERM_CONTROL is architectural for IA32 now, so
 261         * we can rely on the capability checks
 262         */
 263        if (!x86_match_cpu(cpufreq_p4_id) || !boot_cpu_has(X86_FEATURE_ACPI))
 264                return -ENODEV;
 265
 266        ret = cpufreq_register_driver(&p4clockmod_driver);
 267        if (!ret)
 268                printk(KERN_INFO PFX "P4/Xeon(TM) CPU On-Demand Clock "
 269                                "Modulation available\n");
 270
 271        return ret;
 272}
 273
 274
 275static void __exit cpufreq_p4_exit(void)
 276{
 277        cpufreq_unregister_driver(&p4clockmod_driver);
 278}
 279
 280
 281MODULE_AUTHOR("Zwane Mwaikambo <zwane@commfireservices.com>");
 282MODULE_DESCRIPTION("cpufreq driver for Pentium(TM) 4/Xeon(TM)");
 283MODULE_LICENSE("GPL");
 284
 285late_initcall(cpufreq_p4_init);
 286module_exit(cpufreq_p4_exit);
 287