linux/arch/x86/kernel/cpu/cpufreq/sc520_freq.c
<<
>>
Prefs
   1/*
   2 *      sc520_freq.c: cpufreq driver for the AMD Elan sc520
   3 *
   4 *      Copyright (C) 2005 Sean Young <sean@mess.org>
   5 *
   6 *      This program is free software; you can redistribute it and/or
   7 *      modify it under the terms of the GNU General Public License
   8 *      as published by the Free Software Foundation; either version
   9 *      2 of the License, or (at your option) any later version.
  10 *
  11 *      Based on elanfreq.c
  12 *
  13 *      2005-03-30: - initial revision
  14 */
  15
  16#include <linux/kernel.h>
  17#include <linux/module.h>
  18#include <linux/init.h>
  19
  20#include <linux/delay.h>
  21#include <linux/cpufreq.h>
  22#include <linux/timex.h>
  23#include <linux/io.h>
  24
  25#include <asm/msr.h>
  26
  27#define MMCR_BASE       0xfffef000      /* The default base address */
  28#define OFFS_CPUCTL     0x2   /* CPU Control Register */
  29
  30static __u8 __iomem *cpuctl;
  31
  32#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, \
  33                "sc520_freq", msg)
  34#define PFX "sc520_freq: "
  35
  36static struct cpufreq_frequency_table sc520_freq_table[] = {
  37        {0x01,  100000},
  38        {0x02,  133000},
  39        {0,     CPUFREQ_TABLE_END},
  40};
  41
  42static unsigned int sc520_freq_get_cpu_frequency(unsigned int cpu)
  43{
  44        u8 clockspeed_reg = *cpuctl;
  45
  46        switch (clockspeed_reg & 0x03) {
  47        default:
  48                printk(KERN_ERR PFX "error: cpuctl register has unexpected "
  49                                "value %02x\n", clockspeed_reg);
  50        case 0x01:
  51                return 100000;
  52        case 0x02:
  53                return 133000;
  54        }
  55}
  56
  57static void sc520_freq_set_cpu_state(unsigned int state)
  58{
  59
  60        struct cpufreq_freqs    freqs;
  61        u8 clockspeed_reg;
  62
  63        freqs.old = sc520_freq_get_cpu_frequency(0);
  64        freqs.new = sc520_freq_table[state].frequency;
  65        freqs.cpu = 0; /* AMD Elan is UP */
  66
  67        cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
  68
  69        dprintk("attempting to set frequency to %i kHz\n",
  70                        sc520_freq_table[state].frequency);
  71
  72        local_irq_disable();
  73
  74        clockspeed_reg = *cpuctl & ~0x03;
  75        *cpuctl = clockspeed_reg | sc520_freq_table[state].index;
  76
  77        local_irq_enable();
  78
  79        cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
  80};
  81
  82static int sc520_freq_verify(struct cpufreq_policy *policy)
  83{
  84        return cpufreq_frequency_table_verify(policy, &sc520_freq_table[0]);
  85}
  86
  87static int sc520_freq_target(struct cpufreq_policy *policy,
  88                            unsigned int target_freq,
  89                            unsigned int relation)
  90{
  91        unsigned int newstate = 0;
  92
  93        if (cpufreq_frequency_table_target(policy, sc520_freq_table,
  94                                target_freq, relation, &newstate))
  95                return -EINVAL;
  96
  97        sc520_freq_set_cpu_state(newstate);
  98
  99        return 0;
 100}
 101
 102
 103/*
 104 *      Module init and exit code
 105 */
 106
 107static int sc520_freq_cpu_init(struct cpufreq_policy *policy)
 108{
 109        struct cpuinfo_x86 *c = &cpu_data(0);
 110        int result;
 111
 112        /* capability check */
 113        if (c->x86_vendor != X86_VENDOR_AMD ||
 114            c->x86 != 4 || c->x86_model != 9)
 115                return -ENODEV;
 116
 117        /* cpuinfo and default policy values */
 118        policy->cpuinfo.transition_latency = 1000000; /* 1ms */
 119        policy->cur = sc520_freq_get_cpu_frequency(0);
 120
 121        result = cpufreq_frequency_table_cpuinfo(policy, sc520_freq_table);
 122        if (result)
 123                return result;
 124
 125        cpufreq_frequency_table_get_attr(sc520_freq_table, policy->cpu);
 126
 127        return 0;
 128}
 129
 130
 131static int sc520_freq_cpu_exit(struct cpufreq_policy *policy)
 132{
 133        cpufreq_frequency_table_put_attr(policy->cpu);
 134        return 0;
 135}
 136
 137
 138static struct freq_attr *sc520_freq_attr[] = {
 139        &cpufreq_freq_attr_scaling_available_freqs,
 140        NULL,
 141};
 142
 143
 144static struct cpufreq_driver sc520_freq_driver = {
 145        .get    = sc520_freq_get_cpu_frequency,
 146        .verify = sc520_freq_verify,
 147        .target = sc520_freq_target,
 148        .init   = sc520_freq_cpu_init,
 149        .exit   = sc520_freq_cpu_exit,
 150        .name   = "sc520_freq",
 151        .owner  = THIS_MODULE,
 152        .attr   = sc520_freq_attr,
 153};
 154
 155
 156static int __init sc520_freq_init(void)
 157{
 158        struct cpuinfo_x86 *c = &cpu_data(0);
 159        int err;
 160
 161        /* Test if we have the right hardware */
 162        if (c->x86_vendor != X86_VENDOR_AMD ||
 163            c->x86 != 4 || c->x86_model != 9) {
 164                dprintk("no Elan SC520 processor found!\n");
 165                return -ENODEV;
 166        }
 167        cpuctl = ioremap((unsigned long)(MMCR_BASE + OFFS_CPUCTL), 1);
 168        if (!cpuctl) {
 169                printk(KERN_ERR "sc520_freq: error: failed to remap memory\n");
 170                return -ENOMEM;
 171        }
 172
 173        err = cpufreq_register_driver(&sc520_freq_driver);
 174        if (err)
 175                iounmap(cpuctl);
 176
 177        return err;
 178}
 179
 180
 181static void __exit sc520_freq_exit(void)
 182{
 183        cpufreq_unregister_driver(&sc520_freq_driver);
 184        iounmap(cpuctl);
 185}
 186
 187
 188MODULE_LICENSE("GPL");
 189MODULE_AUTHOR("Sean Young <sean@mess.org>");
 190MODULE_DESCRIPTION("cpufreq driver for AMD's Elan sc520 CPU");
 191
 192module_init(sc520_freq_init);
 193module_exit(sc520_freq_exit);
 194
 195