[funini.com] -> [kei@sodan] -> Kernel Reading

root/arch/x86/kernel/cpu/cpufreq/sc520_freq.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. sc520_freq_get_cpu_frequency
  2. sc520_freq_set_cpu_state
  3. sc520_freq_verify
  4. sc520_freq_target
  5. sc520_freq_cpu_init
  6. sc520_freq_cpu_exit
  7. sc520_freq_init
  8. sc520_freq_exit

/*
 *      sc520_freq.c: cpufreq driver for the AMD Elan sc520
 *
 *      Copyright (C) 2005 Sean Young <sean@mess.org>
 *
 *      This program is free software; you can redistribute it and/or
 *      modify it under the terms of the GNU General Public License
 *      as published by the Free Software Foundation; either version
 *      2 of the License, or (at your option) any later version.
 *
 *      Based on elanfreq.c
 *
 *      2005-03-30: - initial revision
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>

#include <linux/delay.h>
#include <linux/cpufreq.h>

#include <asm/msr.h>
#include <asm/timex.h>
#include <asm/io.h>

#define MMCR_BASE       0xfffef000      /* The default base address */
#define OFFS_CPUCTL     0x2   /* CPU Control Register */

static __u8 __iomem *cpuctl;

#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, "sc520_freq", msg)

static struct cpufreq_frequency_table sc520_freq_table[] = {
        {0x01,  100000},
        {0x02,  133000},
        {0,     CPUFREQ_TABLE_END},
};

static unsigned int sc520_freq_get_cpu_frequency(unsigned int cpu)
{
        u8 clockspeed_reg = *cpuctl;

        switch (clockspeed_reg & 0x03) {
        default:
                printk(KERN_ERR "sc520_freq: error: cpuctl register has unexpected value %02x\n", clockspeed_reg);
        case 0x01:
                return 100000;
        case 0x02:
                return 133000;
        }
}

static void sc520_freq_set_cpu_state (unsigned int state)
{

        struct cpufreq_freqs    freqs;
        u8 clockspeed_reg;

        freqs.old = sc520_freq_get_cpu_frequency(0);
        freqs.new = sc520_freq_table[state].frequency;
        freqs.cpu = 0; /* AMD Elan is UP */

        cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);

        dprintk("attempting to set frequency to %i kHz\n",
                        sc520_freq_table[state].frequency);

        local_irq_disable();

        clockspeed_reg = *cpuctl & ~0x03;
        *cpuctl = clockspeed_reg | sc520_freq_table[state].index;

        local_irq_enable();

        cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
};

static int sc520_freq_verify (struct cpufreq_policy *policy)
{
        return cpufreq_frequency_table_verify(policy, &sc520_freq_table[0]);
}

static int sc520_freq_target (struct cpufreq_policy *policy,
                            unsigned int target_freq,
                            unsigned int relation)
{
        unsigned int newstate = 0;

        if (cpufreq_frequency_table_target(policy, sc520_freq_table, target_freq, relation, &newstate))
                return -EINVAL;

        sc520_freq_set_cpu_state(newstate);

        return 0;
}


/*
 *      Module init and exit code
 */

static int sc520_freq_cpu_init(struct cpufreq_policy *policy)
{
        struct cpuinfo_x86 *c = &cpu_data(0);
        int result;

        /* capability check */
        if (c->x86_vendor != X86_VENDOR_AMD ||
            c->x86 != 4 || c->x86_model != 9)
                return -ENODEV;

        /* cpuinfo and default policy values */
        policy->cpuinfo.transition_latency = 1000000; /* 1ms */
        policy->cur = sc520_freq_get_cpu_frequency(0);

        result = cpufreq_frequency_table_cpuinfo(policy, sc520_freq_table);
        if (result)
                return (result);

        cpufreq_frequency_table_get_attr(sc520_freq_table, policy->cpu);

        return 0;
}


static int sc520_freq_cpu_exit(struct cpufreq_policy *policy)
{
        cpufreq_frequency_table_put_attr(policy->cpu);
        return 0;
}


static struct freq_attr* sc520_freq_attr[] = {
        &cpufreq_freq_attr_scaling_available_freqs,
        NULL,
};


static struct cpufreq_driver sc520_freq_driver = {
        .get    = sc520_freq_get_cpu_frequency,
        .verify = sc520_freq_verify,
        .target = sc520_freq_target,
        .init   = sc520_freq_cpu_init,
        .exit   = sc520_freq_cpu_exit,
        .name   = "sc520_freq",
        .owner  = THIS_MODULE,
        .attr   = sc520_freq_attr,
};


static int __init sc520_freq_init(void)
{
        struct cpuinfo_x86 *c = &cpu_data(0);
        int err;

        /* Test if we have the right hardware */
        if(c->x86_vendor != X86_VENDOR_AMD ||
                                c->x86 != 4 || c->x86_model != 9) {
                dprintk("no Elan SC520 processor found!\n");
                return -ENODEV;
        }
        cpuctl = ioremap((unsigned long)(MMCR_BASE + OFFS_CPUCTL), 1);
        if(!cpuctl) {
                printk(KERN_ERR "sc520_freq: error: failed to remap memory\n");
                return -ENOMEM;
        }

        err = cpufreq_register_driver(&sc520_freq_driver);
        if (err)
                iounmap(cpuctl);

        return err;
}


static void __exit sc520_freq_exit(void)
{
        cpufreq_unregister_driver(&sc520_freq_driver);
        iounmap(cpuctl);
}


MODULE_LICENSE("GPL");
MODULE_AUTHOR("Sean Young <sean@mess.org>");
MODULE_DESCRIPTION("cpufreq driver for AMD's Elan sc520 CPU");

module_init(sc520_freq_init);
module_exit(sc520_freq_exit);


/* [<][>][^][v][top][bottom][index][help] */

[funini.com] -> [kei@sodan] -> Kernel Reading