linux/drivers/cpufreq/elanfreq.c
<<
>>
Prefs
   1/*
   2 *      elanfreq:       cpufreq driver for the AMD ELAN family
   3 *
   4 *      (c) Copyright 2002 Robert Schwebel <r.schwebel@pengutronix.de>
   5 *
   6 *      Parts of this code are (c) Sven Geggus <sven@geggus.net>
   7 *
   8 *      All Rights Reserved.
   9 *
  10 *      This program is free software; you can redistribute it and/or
  11 *      modify it under the terms of the GNU General Public License
  12 *      as published by the Free Software Foundation; either version
  13 *      2 of the License, or (at your option) any later version.
  14 *
  15 *      2002-02-13: - initial revision for 2.4.18-pre9 by Robert Schwebel
  16 *
  17 */
  18
  19#include <linux/kernel.h>
  20#include <linux/module.h>
  21#include <linux/init.h>
  22
  23#include <linux/delay.h>
  24#include <linux/cpufreq.h>
  25
  26#include <asm/msr.h>
  27#include <linux/timex.h>
  28#include <linux/io.h>
  29
  30#define REG_CSCIR 0x22          /* Chip Setup and Control Index Register    */
  31#define REG_CSCDR 0x23          /* Chip Setup and Control Data  Register    */
  32
  33/* Module parameter */
  34static int max_freq;
  35
  36struct s_elan_multiplier {
  37        int clock;              /* frequency in kHz                         */
  38        int val40h;             /* PMU Force Mode register                  */
  39        int val80h;             /* CPU Clock Speed Register                 */
  40};
  41
  42/*
  43 * It is important that the frequencies
  44 * are listed in ascending order here!
  45 */
  46static struct s_elan_multiplier elan_multiplier[] = {
  47        {1000,  0x02,   0x18},
  48        {2000,  0x02,   0x10},
  49        {4000,  0x02,   0x08},
  50        {8000,  0x00,   0x00},
  51        {16000, 0x00,   0x02},
  52        {33000, 0x00,   0x04},
  53        {66000, 0x01,   0x04},
  54        {99000, 0x01,   0x05}
  55};
  56
  57static struct cpufreq_frequency_table elanfreq_table[] = {
  58        {0,     1000},
  59        {1,     2000},
  60        {2,     4000},
  61        {3,     8000},
  62        {4,     16000},
  63        {5,     33000},
  64        {6,     66000},
  65        {7,     99000},
  66        {0,     CPUFREQ_TABLE_END},
  67};
  68
  69
  70/**
  71 *      elanfreq_get_cpu_frequency: determine current cpu speed
  72 *
  73 *      Finds out at which frequency the CPU of the Elan SOC runs
  74 *      at the moment. Frequencies from 1 to 33 MHz are generated
  75 *      the normal way, 66 and 99 MHz are called "Hyperspeed Mode"
  76 *      and have the rest of the chip running with 33 MHz.
  77 */
  78
  79static unsigned int elanfreq_get_cpu_frequency(unsigned int cpu)
  80{
  81        u8 clockspeed_reg;    /* Clock Speed Register */
  82
  83        local_irq_disable();
  84        outb_p(0x80, REG_CSCIR);
  85        clockspeed_reg = inb_p(REG_CSCDR);
  86        local_irq_enable();
  87
  88        if ((clockspeed_reg & 0xE0) == 0xE0)
  89                return 0;
  90
  91        /* Are we in CPU clock multiplied mode (66/99 MHz)? */
  92        if ((clockspeed_reg & 0xE0) == 0xC0) {
  93                if ((clockspeed_reg & 0x01) == 0)
  94                        return 66000;
  95                else
  96                        return 99000;
  97        }
  98
  99        /* 33 MHz is not 32 MHz... */
 100        if ((clockspeed_reg & 0xE0) == 0xA0)
 101                return 33000;
 102
 103        return (1<<((clockspeed_reg & 0xE0) >> 5)) * 1000;
 104}
 105
 106
 107/**
 108 *      elanfreq_set_cpu_frequency: Change the CPU core frequency
 109 *      @cpu: cpu number
 110 *      @freq: frequency in kHz
 111 *
 112 *      This function takes a frequency value and changes the CPU frequency
 113 *      according to this. Note that the frequency has to be checked by
 114 *      elanfreq_validatespeed() for correctness!
 115 *
 116 *      There is no return value.
 117 */
 118
 119static void elanfreq_set_cpu_state(unsigned int state)
 120{
 121        struct cpufreq_freqs    freqs;
 122
 123        freqs.old = elanfreq_get_cpu_frequency(0);
 124        freqs.new = elan_multiplier[state].clock;
 125        freqs.cpu = 0; /* elanfreq.c is UP only driver */
 126
 127        cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
 128
 129        printk(KERN_INFO "elanfreq: attempting to set frequency to %i kHz\n",
 130                        elan_multiplier[state].clock);
 131
 132
 133        /*
 134         * Access to the Elan's internal registers is indexed via
 135         * 0x22: Chip Setup & Control Register Index Register (CSCI)
 136         * 0x23: Chip Setup & Control Register Data  Register (CSCD)
 137         *
 138         */
 139
 140        /*
 141         * 0x40 is the Power Management Unit's Force Mode Register.
 142         * Bit 6 enables Hyperspeed Mode (66/100 MHz core frequency)
 143         */
 144
 145        local_irq_disable();
 146        outb_p(0x40, REG_CSCIR);                /* Disable hyperspeed mode */
 147        outb_p(0x00, REG_CSCDR);
 148        local_irq_enable();             /* wait till internal pipelines and */
 149        udelay(1000);                   /* buffers have cleaned up          */
 150
 151        local_irq_disable();
 152
 153        /* now, set the CPU clock speed register (0x80) */
 154        outb_p(0x80, REG_CSCIR);
 155        outb_p(elan_multiplier[state].val80h, REG_CSCDR);
 156
 157        /* now, the hyperspeed bit in PMU Force Mode Register (0x40) */
 158        outb_p(0x40, REG_CSCIR);
 159        outb_p(elan_multiplier[state].val40h, REG_CSCDR);
 160        udelay(10000);
 161        local_irq_enable();
 162
 163        cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
 164};
 165
 166
 167/**
 168 *      elanfreq_validatespeed: test if frequency range is valid
 169 *      @policy: the policy to validate
 170 *
 171 *      This function checks if a given frequency range in kHz is valid
 172 *      for the hardware supported by the driver.
 173 */
 174
 175static int elanfreq_verify(struct cpufreq_policy *policy)
 176{
 177        return cpufreq_frequency_table_verify(policy, &elanfreq_table[0]);
 178}
 179
 180static int elanfreq_target(struct cpufreq_policy *policy,
 181                            unsigned int target_freq,
 182                            unsigned int relation)
 183{
 184        unsigned int newstate = 0;
 185
 186        if (cpufreq_frequency_table_target(policy, &elanfreq_table[0],
 187                                target_freq, relation, &newstate))
 188                return -EINVAL;
 189
 190        elanfreq_set_cpu_state(newstate);
 191
 192        return 0;
 193}
 194
 195
 196/*
 197 *      Module init and exit code
 198 */
 199
 200static int elanfreq_cpu_init(struct cpufreq_policy *policy)
 201{
 202        struct cpuinfo_x86 *c = &cpu_data(0);
 203        unsigned int i;
 204        int result;
 205
 206        /* capability check */
 207        if ((c->x86_vendor != X86_VENDOR_AMD) ||
 208            (c->x86 != 4) || (c->x86_model != 10))
 209                return -ENODEV;
 210
 211        /* max freq */
 212        if (!max_freq)
 213                max_freq = elanfreq_get_cpu_frequency(0);
 214
 215        /* table init */
 216        for (i = 0; (elanfreq_table[i].frequency != CPUFREQ_TABLE_END); i++) {
 217                if (elanfreq_table[i].frequency > max_freq)
 218                        elanfreq_table[i].frequency = CPUFREQ_ENTRY_INVALID;
 219        }
 220
 221        /* cpuinfo and default policy values */
 222        policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
 223        policy->cur = elanfreq_get_cpu_frequency(0);
 224
 225        result = cpufreq_frequency_table_cpuinfo(policy, elanfreq_table);
 226        if (result)
 227                return result;
 228
 229        cpufreq_frequency_table_get_attr(elanfreq_table, policy->cpu);
 230        return 0;
 231}
 232
 233
 234static int elanfreq_cpu_exit(struct cpufreq_policy *policy)
 235{
 236        cpufreq_frequency_table_put_attr(policy->cpu);
 237        return 0;
 238}
 239
 240
 241#ifndef MODULE
 242/**
 243 * elanfreq_setup - elanfreq command line parameter parsing
 244 *
 245 * elanfreq command line parameter.  Use:
 246 *  elanfreq=66000
 247 * to set the maximum CPU frequency to 66 MHz. Note that in
 248 * case you do not give this boot parameter, the maximum
 249 * frequency will fall back to _current_ CPU frequency which
 250 * might be lower. If you build this as a module, use the
 251 * max_freq module parameter instead.
 252 */
 253static int __init elanfreq_setup(char *str)
 254{
 255        max_freq = simple_strtoul(str, &str, 0);
 256        printk(KERN_WARNING "You're using the deprecated elanfreq command line option. Use elanfreq.max_freq instead, please!\n");
 257        return 1;
 258}
 259__setup("elanfreq=", elanfreq_setup);
 260#endif
 261
 262
 263static struct freq_attr *elanfreq_attr[] = {
 264        &cpufreq_freq_attr_scaling_available_freqs,
 265        NULL,
 266};
 267
 268
 269static struct cpufreq_driver elanfreq_driver = {
 270        .get            = elanfreq_get_cpu_frequency,
 271        .verify         = elanfreq_verify,
 272        .target         = elanfreq_target,
 273        .init           = elanfreq_cpu_init,
 274        .exit           = elanfreq_cpu_exit,
 275        .name           = "elanfreq",
 276        .owner          = THIS_MODULE,
 277        .attr           = elanfreq_attr,
 278};
 279
 280
 281static int __init elanfreq_init(void)
 282{
 283        struct cpuinfo_x86 *c = &cpu_data(0);
 284
 285        /* Test if we have the right hardware */
 286        if ((c->x86_vendor != X86_VENDOR_AMD) ||
 287                (c->x86 != 4) || (c->x86_model != 10)) {
 288                printk(KERN_INFO "elanfreq: error: no Elan processor found!\n");
 289                return -ENODEV;
 290        }
 291        return cpufreq_register_driver(&elanfreq_driver);
 292}
 293
 294
 295static void __exit elanfreq_exit(void)
 296{
 297        cpufreq_unregister_driver(&elanfreq_driver);
 298}
 299
 300
 301module_param(max_freq, int, 0444);
 302
 303MODULE_LICENSE("GPL");
 304MODULE_AUTHOR("Robert Schwebel <r.schwebel@pengutronix.de>, "
 305                "Sven Geggus <sven@geggus.net>");
 306MODULE_DESCRIPTION("cpufreq driver for AMD's Elan CPUs");
 307
 308module_init(elanfreq_init);
 309module_exit(elanfreq_exit);
 310
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.