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#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  24
  25#include <linux/kernel.h>
  26#include <linux/module.h>
  27#include <linux/init.h>
  28#include <linux/smp.h>
  29#include <linux/cpufreq.h>
  30#include <linux/cpumask.h>
  31#include <linux/timex.h>
  32
  33#include <asm/processor.h>
  34#include <asm/msr.h>
  35#include <asm/timer.h>
  36#include <asm/cpu_device_id.h>
  37
  38#include "speedstep-lib.h"
  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                        pr_warn_once("Warning: EST-capable CPU detected. The acpi-cpufreq module offers voltage scaling in addition to frequency scaling. You should use that instead of p4-clockmod, if possible.\n");
 128                switch (c->x86_model) {
 129                case 0x0E: /* Core */
 130                case 0x0F: /* Core Duo */
 131                case 0x16: /* Celeron Core */
 132                case 0x1C: /* Atom */
 133                        p4clockmod_driver.flags |= CPUFREQ_CONST_LOOPS;
 134                        return speedstep_get_frequency(SPEEDSTEP_CPU_PCORE);
 135                case 0x0D: /* Pentium M (Dothan) */
 136                        p4clockmod_driver.flags |= CPUFREQ_CONST_LOOPS;
 137                        /* fall through */
 138                case 0x09: /* Pentium M (Banias) */
 139                        return speedstep_get_frequency(SPEEDSTEP_CPU_PM);
 140                }
 141        }
 142
 143        if (c->x86 != 0xF)
 144                return 0;
 145
 146        /* on P-4s, the TSC runs with constant frequency independent whether
 147         * throttling is active or not. */
 148        p4clockmod_driver.flags |= CPUFREQ_CONST_LOOPS;
 149
 150        if (speedstep_detect_processor() == SPEEDSTEP_CPU_P4M) {
 151                pr_warn("Warning: Pentium 4-M detected. The speedstep-ich or acpi cpufreq modules offer voltage scaling in addition of frequency scaling. You should use either one instead of p4-clockmod, if possible.\n");
 152                return speedstep_get_frequency(SPEEDSTEP_CPU_P4M);
 153        }
 154
 155        return speedstep_get_frequency(SPEEDSTEP_CPU_P4D);
 156}
 157
 158
 159
 160static int cpufreq_p4_cpu_init(struct cpufreq_policy *policy)
 161{
 162        struct cpuinfo_x86 *c = &cpu_data(policy->cpu);
 163        int cpuid = 0;
 164        unsigned int i;
 165
 166#ifdef CONFIG_SMP
 167        cpumask_copy(policy->cpus, topology_sibling_cpumask(policy->cpu));
 168#endif
 169
 170        /* Errata workaround */
 171        cpuid = (c->x86 << 8) | (c->x86_model << 4) | c->x86_mask;
 172        switch (cpuid) {
 173        case 0x0f07:
 174        case 0x0f0a:
 175        case 0x0f11:
 176        case 0x0f12:
 177                has_N44_O17_errata[policy->cpu] = 1;
 178                pr_debug("has errata -- disabling low frequencies\n");
 179        }
 180
 181        if (speedstep_detect_processor() == SPEEDSTEP_CPU_P4D &&
 182            c->x86_model < 2) {
 183                /* switch to maximum frequency and measure result */
 184                cpufreq_p4_setdc(policy->cpu, DC_DISABLE);
 185                recalibrate_cpu_khz();
 186        }
 187        /* get max frequency */
 188        stock_freq = cpufreq_p4_get_frequency(c);
 189        if (!stock_freq)
 190                return -EINVAL;
 191
 192        /* table init */
 193        for (i = 1; (p4clockmod_table[i].frequency != CPUFREQ_TABLE_END); i++) {
 194                if ((i < 2) && (has_N44_O17_errata[policy->cpu]))
 195                        p4clockmod_table[i].frequency = CPUFREQ_ENTRY_INVALID;
 196                else
 197                        p4clockmod_table[i].frequency = (stock_freq * i)/8;
 198        }
 199
 200        /* cpuinfo and default policy values */
 201
 202        /* the transition latency is set to be 1 higher than the maximum
 203         * transition latency of the ondemand governor */
 204        policy->cpuinfo.transition_latency = 10000001;
 205
 206        return cpufreq_table_validate_and_show(policy, &p4clockmod_table[0]);
 207}
 208
 209
 210static unsigned int cpufreq_p4_get(unsigned int cpu)
 211{
 212        u32 l, h;
 213
 214        rdmsr_on_cpu(cpu, MSR_IA32_THERM_CONTROL, &l, &h);
 215
 216        if (l & 0x10) {
 217                l = l >> 1;
 218                l &= 0x7;
 219        } else
 220                l = DC_DISABLE;
 221
 222        if (l != DC_DISABLE)
 223                return stock_freq * l / 8;
 224
 225        return stock_freq;
 226}
 227
 228static struct cpufreq_driver p4clockmod_driver = {
 229        .verify         = cpufreq_generic_frequency_table_verify,
 230        .target_index   = cpufreq_p4_target,
 231        .init           = cpufreq_p4_cpu_init,
 232        .get            = cpufreq_p4_get,
 233        .name           = "p4-clockmod",
 234        .attr           = cpufreq_generic_attr,
 235};
 236
 237static const struct x86_cpu_id cpufreq_p4_id[] = {
 238        { X86_VENDOR_INTEL, X86_FAMILY_ANY, X86_MODEL_ANY, X86_FEATURE_ACC },
 239        {}
 240};
 241
 242/*
 243 * Intentionally no MODULE_DEVICE_TABLE here: this driver should not
 244 * be auto loaded.  Please don't add one.
 245 */
 246
 247static int __init cpufreq_p4_init(void)
 248{
 249        int ret;
 250
 251        /*
 252         * THERM_CONTROL is architectural for IA32 now, so
 253         * we can rely on the capability checks
 254         */
 255        if (!x86_match_cpu(cpufreq_p4_id) || !boot_cpu_has(X86_FEATURE_ACPI))
 256                return -ENODEV;
 257
 258        ret = cpufreq_register_driver(&p4clockmod_driver);
 259        if (!ret)
 260                pr_info("P4/Xeon(TM) CPU On-Demand Clock Modulation available\n");
 261
 262        return ret;
 263}
 264
 265
 266static void __exit cpufreq_p4_exit(void)
 267{
 268        cpufreq_unregister_driver(&p4clockmod_driver);
 269}
 270
 271
 272MODULE_AUTHOR("Zwane Mwaikambo <zwane@commfireservices.com>");
 273MODULE_DESCRIPTION("cpufreq driver for Pentium(TM) 4/Xeon(TM)");
 274MODULE_LICENSE("GPL");
 275
 276late_initcall(cpufreq_p4_init);
 277module_exit(cpufreq_p4_exit);
 278