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