linux/drivers/cpufreq/e_powersaver.c
<<
>>
Prefs
   1/*
   2 *  Based on documentation provided by Dave Jones. Thanks!
   3 *
   4 *  Licensed under the terms of the GNU GPL License version 2.
   5 *
   6 *  BIG FAT DISCLAIMER: Work in progress code. Possibly *dangerous*
   7 */
   8
   9#include <linux/kernel.h>
  10#include <linux/module.h>
  11#include <linux/init.h>
  12#include <linux/cpufreq.h>
  13#include <linux/ioport.h>
  14#include <linux/slab.h>
  15#include <linux/timex.h>
  16#include <linux/io.h>
  17#include <linux/delay.h>
  18
  19#include <asm/cpu_device_id.h>
  20#include <asm/msr.h>
  21#include <asm/tsc.h>
  22
  23#if defined CONFIG_ACPI_PROCESSOR || defined CONFIG_ACPI_PROCESSOR_MODULE
  24#include <linux/acpi.h>
  25#include <acpi/processor.h>
  26#endif
  27
  28#define EPS_BRAND_C7M   0
  29#define EPS_BRAND_C7    1
  30#define EPS_BRAND_EDEN  2
  31#define EPS_BRAND_C3    3
  32#define EPS_BRAND_C7D   4
  33
  34struct eps_cpu_data {
  35        u32 fsb;
  36#if defined CONFIG_ACPI_PROCESSOR || defined CONFIG_ACPI_PROCESSOR_MODULE
  37        u32 bios_limit;
  38#endif
  39        struct cpufreq_frequency_table freq_table[];
  40};
  41
  42static struct eps_cpu_data *eps_cpu[NR_CPUS];
  43
  44/* Module parameters */
  45static int freq_failsafe_off;
  46static int voltage_failsafe_off;
  47static int set_max_voltage;
  48
  49#if defined CONFIG_ACPI_PROCESSOR || defined CONFIG_ACPI_PROCESSOR_MODULE
  50static int ignore_acpi_limit;
  51
  52static struct acpi_processor_performance *eps_acpi_cpu_perf;
  53
  54/* Minimum necessary to get acpi_processor_get_bios_limit() working */
  55static int eps_acpi_init(void)
  56{
  57        eps_acpi_cpu_perf = kzalloc(sizeof(struct acpi_processor_performance),
  58                                      GFP_KERNEL);
  59        if (!eps_acpi_cpu_perf)
  60                return -ENOMEM;
  61
  62        if (!zalloc_cpumask_var(&eps_acpi_cpu_perf->shared_cpu_map,
  63                                                                GFP_KERNEL)) {
  64                kfree(eps_acpi_cpu_perf);
  65                eps_acpi_cpu_perf = NULL;
  66                return -ENOMEM;
  67        }
  68
  69        if (acpi_processor_register_performance(eps_acpi_cpu_perf, 0)) {
  70                free_cpumask_var(eps_acpi_cpu_perf->shared_cpu_map);
  71                kfree(eps_acpi_cpu_perf);
  72                eps_acpi_cpu_perf = NULL;
  73                return -EIO;
  74        }
  75        return 0;
  76}
  77
  78static int eps_acpi_exit(struct cpufreq_policy *policy)
  79{
  80        if (eps_acpi_cpu_perf) {
  81                acpi_processor_unregister_performance(eps_acpi_cpu_perf, 0);
  82                free_cpumask_var(eps_acpi_cpu_perf->shared_cpu_map);
  83                kfree(eps_acpi_cpu_perf);
  84                eps_acpi_cpu_perf = NULL;
  85        }
  86        return 0;
  87}
  88#endif
  89
  90static unsigned int eps_get(unsigned int cpu)
  91{
  92        struct eps_cpu_data *centaur;
  93        u32 lo, hi;
  94
  95        if (cpu)
  96                return 0;
  97        centaur = eps_cpu[cpu];
  98        if (centaur == NULL)
  99                return 0;
 100
 101        /* Return current frequency */
 102        rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
 103        return centaur->fsb * ((lo >> 8) & 0xff);
 104}
 105
 106static int eps_set_state(struct eps_cpu_data *centaur,
 107                         unsigned int cpu,
 108                         u32 dest_state)
 109{
 110        struct cpufreq_freqs freqs;
 111        u32 lo, hi;
 112        int err = 0;
 113        int i;
 114
 115        freqs.old = eps_get(cpu);
 116        freqs.new = centaur->fsb * ((dest_state >> 8) & 0xff);
 117        freqs.cpu = cpu;
 118        cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
 119
 120        /* Wait while CPU is busy */
 121        rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
 122        i = 0;
 123        while (lo & ((1 << 16) | (1 << 17))) {
 124                udelay(16);
 125                rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
 126                i++;
 127                if (unlikely(i > 64)) {
 128                        err = -ENODEV;
 129                        goto postchange;
 130                }
 131        }
 132        /* Set new multiplier and voltage */
 133        wrmsr(MSR_IA32_PERF_CTL, dest_state & 0xffff, 0);
 134        /* Wait until transition end */
 135        i = 0;
 136        do {
 137                udelay(16);
 138                rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
 139                i++;
 140                if (unlikely(i > 64)) {
 141                        err = -ENODEV;
 142                        goto postchange;
 143                }
 144        } while (lo & ((1 << 16) | (1 << 17)));
 145
 146        /* Return current frequency */
 147postchange:
 148        rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
 149        freqs.new = centaur->fsb * ((lo >> 8) & 0xff);
 150
 151#ifdef DEBUG
 152        {
 153        u8 current_multiplier, current_voltage;
 154
 155        /* Print voltage and multiplier */
 156        rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
 157        current_voltage = lo & 0xff;
 158        printk(KERN_INFO "eps: Current voltage = %dmV\n",
 159                current_voltage * 16 + 700);
 160        current_multiplier = (lo >> 8) & 0xff;
 161        printk(KERN_INFO "eps: Current multiplier = %d\n",
 162                current_multiplier);
 163        }
 164#endif
 165        cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
 166        return err;
 167}
 168
 169static int eps_target(struct cpufreq_policy *policy,
 170                               unsigned int target_freq,
 171                               unsigned int relation)
 172{
 173        struct eps_cpu_data *centaur;
 174        unsigned int newstate = 0;
 175        unsigned int cpu = policy->cpu;
 176        unsigned int dest_state;
 177        int ret;
 178
 179        if (unlikely(eps_cpu[cpu] == NULL))
 180                return -ENODEV;
 181        centaur = eps_cpu[cpu];
 182
 183        if (unlikely(cpufreq_frequency_table_target(policy,
 184                        &eps_cpu[cpu]->freq_table[0],
 185                        target_freq,
 186                        relation,
 187                        &newstate))) {
 188                return -EINVAL;
 189        }
 190
 191        /* Make frequency transition */
 192        dest_state = centaur->freq_table[newstate].index & 0xffff;
 193        ret = eps_set_state(centaur, cpu, dest_state);
 194        if (ret)
 195                printk(KERN_ERR "eps: Timeout!\n");
 196        return ret;
 197}
 198
 199static int eps_verify(struct cpufreq_policy *policy)
 200{
 201        return cpufreq_frequency_table_verify(policy,
 202                        &eps_cpu[policy->cpu]->freq_table[0]);
 203}
 204
 205static int eps_cpu_init(struct cpufreq_policy *policy)
 206{
 207        unsigned int i;
 208        u32 lo, hi;
 209        u64 val;
 210        u8 current_multiplier, current_voltage;
 211        u8 max_multiplier, max_voltage;
 212        u8 min_multiplier, min_voltage;
 213        u8 brand = 0;
 214        u32 fsb;
 215        struct eps_cpu_data *centaur;
 216        struct cpuinfo_x86 *c = &cpu_data(0);
 217        struct cpufreq_frequency_table *f_table;
 218        int k, step, voltage;
 219        int ret;
 220        int states;
 221#if defined CONFIG_ACPI_PROCESSOR || defined CONFIG_ACPI_PROCESSOR_MODULE
 222        unsigned int limit;
 223#endif
 224
 225        if (policy->cpu != 0)
 226                return -ENODEV;
 227
 228        /* Check brand */
 229        printk(KERN_INFO "eps: Detected VIA ");
 230
 231        switch (c->x86_model) {
 232        case 10:
 233                rdmsr(0x1153, lo, hi);
 234                brand = (((lo >> 2) ^ lo) >> 18) & 3;
 235                printk(KERN_CONT "Model A ");
 236                break;
 237        case 13:
 238                rdmsr(0x1154, lo, hi);
 239                brand = (((lo >> 4) ^ (lo >> 2))) & 0x000000ff;
 240                printk(KERN_CONT "Model D ");
 241                break;
 242        }
 243
 244        switch (brand) {
 245        case EPS_BRAND_C7M:
 246                printk(KERN_CONT "C7-M\n");
 247                break;
 248        case EPS_BRAND_C7:
 249                printk(KERN_CONT "C7\n");
 250                break;
 251        case EPS_BRAND_EDEN:
 252                printk(KERN_CONT "Eden\n");
 253                break;
 254        case EPS_BRAND_C7D:
 255                printk(KERN_CONT "C7-D\n");
 256                break;
 257        case EPS_BRAND_C3:
 258                printk(KERN_CONT "C3\n");
 259                return -ENODEV;
 260                break;
 261        }
 262        /* Enable Enhanced PowerSaver */
 263        rdmsrl(MSR_IA32_MISC_ENABLE, val);
 264        if (!(val & MSR_IA32_MISC_ENABLE_ENHANCED_SPEEDSTEP)) {
 265                val |= MSR_IA32_MISC_ENABLE_ENHANCED_SPEEDSTEP;
 266                wrmsrl(MSR_IA32_MISC_ENABLE, val);
 267                /* Can be locked at 0 */
 268                rdmsrl(MSR_IA32_MISC_ENABLE, val);
 269                if (!(val & MSR_IA32_MISC_ENABLE_ENHANCED_SPEEDSTEP)) {
 270                        printk(KERN_INFO "eps: Can't enable Enhanced PowerSaver\n");
 271                        return -ENODEV;
 272                }
 273        }
 274
 275        /* Print voltage and multiplier */
 276        rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
 277        current_voltage = lo & 0xff;
 278        printk(KERN_INFO "eps: Current voltage = %dmV\n",
 279                        current_voltage * 16 + 700);
 280        current_multiplier = (lo >> 8) & 0xff;
 281        printk(KERN_INFO "eps: Current multiplier = %d\n", current_multiplier);
 282
 283        /* Print limits */
 284        max_voltage = hi & 0xff;
 285        printk(KERN_INFO "eps: Highest voltage = %dmV\n",
 286                        max_voltage * 16 + 700);
 287        max_multiplier = (hi >> 8) & 0xff;
 288        printk(KERN_INFO "eps: Highest multiplier = %d\n", max_multiplier);
 289        min_voltage = (hi >> 16) & 0xff;
 290        printk(KERN_INFO "eps: Lowest voltage = %dmV\n",
 291                        min_voltage * 16 + 700);
 292        min_multiplier = (hi >> 24) & 0xff;
 293        printk(KERN_INFO "eps: Lowest multiplier = %d\n", min_multiplier);
 294
 295        /* Sanity checks */
 296        if (current_multiplier == 0 || max_multiplier == 0
 297            || min_multiplier == 0)
 298                return -EINVAL;
 299        if (current_multiplier > max_multiplier
 300            || max_multiplier <= min_multiplier)
 301                return -EINVAL;
 302        if (current_voltage > 0x1f || max_voltage > 0x1f)
 303                return -EINVAL;
 304        if (max_voltage < min_voltage
 305            || current_voltage < min_voltage
 306            || current_voltage > max_voltage)
 307                return -EINVAL;
 308
 309        /* Check for systems using underclocked CPU */
 310        if (!freq_failsafe_off && max_multiplier != current_multiplier) {
 311                printk(KERN_INFO "eps: Your processor is running at different "
 312                        "frequency then its maximum. Aborting.\n");
 313                printk(KERN_INFO "eps: You can use freq_failsafe_off option "
 314                        "to disable this check.\n");
 315                return -EINVAL;
 316        }
 317        if (!voltage_failsafe_off && max_voltage != current_voltage) {
 318                printk(KERN_INFO "eps: Your processor is running at different "
 319                        "voltage then its maximum. Aborting.\n");
 320                printk(KERN_INFO "eps: You can use voltage_failsafe_off "
 321                        "option to disable this check.\n");
 322                return -EINVAL;
 323        }
 324
 325        /* Calc FSB speed */
 326        fsb = cpu_khz / current_multiplier;
 327
 328#if defined CONFIG_ACPI_PROCESSOR || defined CONFIG_ACPI_PROCESSOR_MODULE
 329        /* Check for ACPI processor speed limit */
 330        if (!ignore_acpi_limit && !eps_acpi_init()) {
 331                if (!acpi_processor_get_bios_limit(policy->cpu, &limit)) {
 332                        printk(KERN_INFO "eps: ACPI limit %u.%uGHz\n",
 333                                limit/1000000,
 334                                (limit%1000000)/10000);
 335                        eps_acpi_exit(policy);
 336                        /* Check if max_multiplier is in BIOS limits */
 337                        if (limit && max_multiplier * fsb > limit) {
 338                                printk(KERN_INFO "eps: Aborting.\n");
 339                                return -EINVAL;
 340                        }
 341                }
 342        }
 343#endif
 344
 345        /* Allow user to set lower maximum voltage then that reported
 346         * by processor */
 347        if (brand == EPS_BRAND_C7M && set_max_voltage) {
 348                u32 v;
 349
 350                /* Change mV to something hardware can use */
 351                v = (set_max_voltage - 700) / 16;
 352                /* Check if voltage is within limits */
 353                if (v >= min_voltage && v <= max_voltage) {
 354                        printk(KERN_INFO "eps: Setting %dmV as maximum.\n",
 355                                v * 16 + 700);
 356                        max_voltage = v;
 357                }
 358        }
 359
 360        /* Calc number of p-states supported */
 361        if (brand == EPS_BRAND_C7M)
 362                states = max_multiplier - min_multiplier + 1;
 363        else
 364                states = 2;
 365
 366        /* Allocate private data and frequency table for current cpu */
 367        centaur = kzalloc(sizeof(struct eps_cpu_data)
 368                    + (states + 1) * sizeof(struct cpufreq_frequency_table),
 369                    GFP_KERNEL);
 370        if (!centaur)
 371                return -ENOMEM;
 372        eps_cpu[0] = centaur;
 373
 374        /* Copy basic values */
 375        centaur->fsb = fsb;
 376#if defined CONFIG_ACPI_PROCESSOR || defined CONFIG_ACPI_PROCESSOR_MODULE
 377        centaur->bios_limit = limit;
 378#endif
 379
 380        /* Fill frequency and MSR value table */
 381        f_table = &centaur->freq_table[0];
 382        if (brand != EPS_BRAND_C7M) {
 383                f_table[0].frequency = fsb * min_multiplier;
 384                f_table[0].index = (min_multiplier << 8) | min_voltage;
 385                f_table[1].frequency = fsb * max_multiplier;
 386                f_table[1].index = (max_multiplier << 8) | max_voltage;
 387                f_table[2].frequency = CPUFREQ_TABLE_END;
 388        } else {
 389                k = 0;
 390                step = ((max_voltage - min_voltage) * 256)
 391                        / (max_multiplier - min_multiplier);
 392                for (i = min_multiplier; i <= max_multiplier; i++) {
 393                        voltage = (k * step) / 256 + min_voltage;
 394                        f_table[k].frequency = fsb * i;
 395                        f_table[k].index = (i << 8) | voltage;
 396                        k++;
 397                }
 398                f_table[k].frequency = CPUFREQ_TABLE_END;
 399        }
 400
 401        policy->cpuinfo.transition_latency = 140000; /* 844mV -> 700mV in ns */
 402        policy->cur = fsb * current_multiplier;
 403
 404        ret = cpufreq_frequency_table_cpuinfo(policy, &centaur->freq_table[0]);
 405        if (ret) {
 406                kfree(centaur);
 407                return ret;
 408        }
 409
 410        cpufreq_frequency_table_get_attr(&centaur->freq_table[0], policy->cpu);
 411        return 0;
 412}
 413
 414static int eps_cpu_exit(struct cpufreq_policy *policy)
 415{
 416        unsigned int cpu = policy->cpu;
 417
 418        /* Bye */
 419        cpufreq_frequency_table_put_attr(policy->cpu);
 420        kfree(eps_cpu[cpu]);
 421        eps_cpu[cpu] = NULL;
 422        return 0;
 423}
 424
 425static struct freq_attr *eps_attr[] = {
 426        &cpufreq_freq_attr_scaling_available_freqs,
 427        NULL,
 428};
 429
 430static struct cpufreq_driver eps_driver = {
 431        .verify         = eps_verify,
 432        .target         = eps_target,
 433        .init           = eps_cpu_init,
 434        .exit           = eps_cpu_exit,
 435        .get            = eps_get,
 436        .name           = "e_powersaver",
 437        .owner          = THIS_MODULE,
 438        .attr           = eps_attr,
 439};
 440
 441
 442/* This driver will work only on Centaur C7 processors with
 443 * Enhanced SpeedStep/PowerSaver registers */
 444static const struct x86_cpu_id eps_cpu_id[] = {
 445        { X86_VENDOR_CENTAUR, 6, X86_MODEL_ANY, X86_FEATURE_EST },
 446        {}
 447};
 448MODULE_DEVICE_TABLE(x86cpu, eps_cpu_id);
 449
 450static int __init eps_init(void)
 451{
 452        if (!x86_match_cpu(eps_cpu_id) || boot_cpu_data.x86_model < 10)
 453                return -ENODEV;
 454        if (cpufreq_register_driver(&eps_driver))
 455                return -EINVAL;
 456        return 0;
 457}
 458
 459static void __exit eps_exit(void)
 460{
 461        cpufreq_unregister_driver(&eps_driver);
 462}
 463
 464/* Allow user to overclock his machine or to change frequency to higher after
 465 * unloading module */
 466module_param(freq_failsafe_off, int, 0644);
 467MODULE_PARM_DESC(freq_failsafe_off, "Disable current vs max frequency check");
 468module_param(voltage_failsafe_off, int, 0644);
 469MODULE_PARM_DESC(voltage_failsafe_off, "Disable current vs max voltage check");
 470#if defined CONFIG_ACPI_PROCESSOR || defined CONFIG_ACPI_PROCESSOR_MODULE
 471module_param(ignore_acpi_limit, int, 0644);
 472MODULE_PARM_DESC(ignore_acpi_limit, "Don't check ACPI's processor speed limit");
 473#endif
 474module_param(set_max_voltage, int, 0644);
 475MODULE_PARM_DESC(set_max_voltage, "Set maximum CPU voltage (mV) C7-M only");
 476
 477MODULE_AUTHOR("Rafal Bilski <rafalbilski@interia.pl>");
 478MODULE_DESCRIPTION("Enhanced PowerSaver driver for VIA C7 CPU's.");
 479MODULE_LICENSE("GPL");
 480
 481module_init(eps_init);
 482module_exit(eps_exit);
 483
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.