linux/arch/mips/kernel/cpufreq/loongson2_cpufreq.c
<<
>>
Prefs
   1/*
   2 * Cpufreq driver for the loongson-2 processors
   3 *
   4 * The 2E revision of loongson processor not support this feature.
   5 *
   6 * Copyright (C) 2006 - 2008 Lemote Inc. & Insititute of Computing Technology
   7 * Author: Yanhua, yanh@lemote.com
   8 *
   9 * This file is subject to the terms and conditions of the GNU General Public
  10 * License.  See the file "COPYING" in the main directory of this archive
  11 * for more details.
  12 */
  13#include <linux/cpufreq.h>
  14#include <linux/module.h>
  15#include <linux/err.h>
  16#include <linux/sched.h>        /* set_cpus_allowed() */
  17#include <linux/delay.h>
  18#include <linux/platform_device.h>
  19
  20#include <asm/clock.h>
  21
  22#include <asm/mach-loongson/loongson.h>
  23
  24static uint nowait;
  25
  26static struct clk *cpuclk;
  27
  28static void (*saved_cpu_wait) (void);
  29
  30static int loongson2_cpu_freq_notifier(struct notifier_block *nb,
  31                                        unsigned long val, void *data);
  32
  33static struct notifier_block loongson2_cpufreq_notifier_block = {
  34        .notifier_call = loongson2_cpu_freq_notifier
  35};
  36
  37static int loongson2_cpu_freq_notifier(struct notifier_block *nb,
  38                                        unsigned long val, void *data)
  39{
  40        if (val == CPUFREQ_POSTCHANGE)
  41                current_cpu_data.udelay_val = loops_per_jiffy;
  42
  43        return 0;
  44}
  45
  46static unsigned int loongson2_cpufreq_get(unsigned int cpu)
  47{
  48        return clk_get_rate(cpuclk);
  49}
  50
  51/*
  52 * Here we notify other drivers of the proposed change and the final change.
  53 */
  54static int loongson2_cpufreq_target(struct cpufreq_policy *policy,
  55                                     unsigned int target_freq,
  56                                     unsigned int relation)
  57{
  58        unsigned int cpu = policy->cpu;
  59        unsigned int newstate = 0;
  60        cpumask_t cpus_allowed;
  61        struct cpufreq_freqs freqs;
  62        unsigned int freq;
  63
  64        if (!cpu_online(cpu))
  65                return -ENODEV;
  66
  67        cpus_allowed = current->cpus_allowed;
  68        set_cpus_allowed_ptr(current, cpumask_of(cpu));
  69
  70        if (cpufreq_frequency_table_target
  71            (policy, &loongson2_clockmod_table[0], target_freq, relation,
  72             &newstate))
  73                return -EINVAL;
  74
  75        freq =
  76            ((cpu_clock_freq / 1000) *
  77             loongson2_clockmod_table[newstate].index) / 8;
  78        if (freq < policy->min || freq > policy->max)
  79                return -EINVAL;
  80
  81        pr_debug("cpufreq: requested frequency %u Hz\n", target_freq * 1000);
  82
  83        freqs.cpu = cpu;
  84        freqs.old = loongson2_cpufreq_get(cpu);
  85        freqs.new = freq;
  86        freqs.flags = 0;
  87
  88        if (freqs.new == freqs.old)
  89                return 0;
  90
  91        /* notifiers */
  92        cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
  93
  94        set_cpus_allowed_ptr(current, &cpus_allowed);
  95
  96        /* setting the cpu frequency */
  97        clk_set_rate(cpuclk, freq);
  98
  99        /* notifiers */
 100        cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
 101
 102        pr_debug("cpufreq: set frequency %u kHz\n", freq);
 103
 104        return 0;
 105}
 106
 107static int loongson2_cpufreq_cpu_init(struct cpufreq_policy *policy)
 108{
 109        int i;
 110        unsigned long rate;
 111        int ret;
 112
 113        if (!cpu_online(policy->cpu))
 114                return -ENODEV;
 115
 116        cpuclk = clk_get(NULL, "cpu_clk");
 117        if (IS_ERR(cpuclk)) {
 118                printk(KERN_ERR "cpufreq: couldn't get CPU clk\n");
 119                return PTR_ERR(cpuclk);
 120        }
 121
 122        rate = cpu_clock_freq / 1000;
 123        if (!rate) {
 124                clk_put(cpuclk);
 125                return -EINVAL;
 126        }
 127        ret = clk_set_rate(cpuclk, rate);
 128        if (ret) {
 129                clk_put(cpuclk);
 130                return ret;
 131        }
 132
 133        /* clock table init */
 134        for (i = 2;
 135             (loongson2_clockmod_table[i].frequency != CPUFREQ_TABLE_END);
 136             i++)
 137                loongson2_clockmod_table[i].frequency = (rate * i) / 8;
 138
 139        policy->cur = loongson2_cpufreq_get(policy->cpu);
 140
 141        cpufreq_frequency_table_get_attr(&loongson2_clockmod_table[0],
 142                                         policy->cpu);
 143
 144        return cpufreq_frequency_table_cpuinfo(policy,
 145                                            &loongson2_clockmod_table[0]);
 146}
 147
 148static int loongson2_cpufreq_verify(struct cpufreq_policy *policy)
 149{
 150        return cpufreq_frequency_table_verify(policy,
 151                                              &loongson2_clockmod_table[0]);
 152}
 153
 154static int loongson2_cpufreq_exit(struct cpufreq_policy *policy)
 155{
 156        clk_put(cpuclk);
 157        return 0;
 158}
 159
 160static struct freq_attr *loongson2_table_attr[] = {
 161        &cpufreq_freq_attr_scaling_available_freqs,
 162        NULL,
 163};
 164
 165static struct cpufreq_driver loongson2_cpufreq_driver = {
 166        .owner = THIS_MODULE,
 167        .name = "loongson2",
 168        .init = loongson2_cpufreq_cpu_init,
 169        .verify = loongson2_cpufreq_verify,
 170        .target = loongson2_cpufreq_target,
 171        .get = loongson2_cpufreq_get,
 172        .exit = loongson2_cpufreq_exit,
 173        .attr = loongson2_table_attr,
 174};
 175
 176static struct platform_device_id platform_device_ids[] = {
 177        {
 178                .name = "loongson2_cpufreq",
 179        },
 180        {}
 181};
 182
 183MODULE_DEVICE_TABLE(platform, platform_device_ids);
 184
 185static struct platform_driver platform_driver = {
 186        .driver = {
 187                .name = "loongson2_cpufreq",
 188                .owner = THIS_MODULE,
 189        },
 190        .id_table = platform_device_ids,
 191};
 192
 193/*
 194 * This is the simple version of Loongson-2 wait, Maybe we need do this in
 195 * interrupt disabled context.
 196 */
 197
 198static DEFINE_SPINLOCK(loongson2_wait_lock);
 199
 200static void loongson2_cpu_wait(void)
 201{
 202        unsigned long flags;
 203        u32 cpu_freq;
 204
 205        spin_lock_irqsave(&loongson2_wait_lock, flags);
 206        cpu_freq = LOONGSON_CHIPCFG0;
 207        LOONGSON_CHIPCFG0 &= ~0x7;      /* Put CPU into wait mode */
 208        LOONGSON_CHIPCFG0 = cpu_freq;   /* Restore CPU state */
 209        spin_unlock_irqrestore(&loongson2_wait_lock, flags);
 210}
 211
 212static int __init cpufreq_init(void)
 213{
 214        int ret;
 215
 216        /* Register platform stuff */
 217        ret = platform_driver_register(&platform_driver);
 218        if (ret)
 219                return ret;
 220
 221        pr_info("cpufreq: Loongson-2F CPU frequency driver.\n");
 222
 223        cpufreq_register_notifier(&loongson2_cpufreq_notifier_block,
 224                                  CPUFREQ_TRANSITION_NOTIFIER);
 225
 226        ret = cpufreq_register_driver(&loongson2_cpufreq_driver);
 227
 228        if (!ret && !nowait) {
 229                saved_cpu_wait = cpu_wait;
 230                cpu_wait = loongson2_cpu_wait;
 231        }
 232
 233        return ret;
 234}
 235
 236static void __exit cpufreq_exit(void)
 237{
 238        if (!nowait && saved_cpu_wait)
 239                cpu_wait = saved_cpu_wait;
 240        cpufreq_unregister_driver(&loongson2_cpufreq_driver);
 241        cpufreq_unregister_notifier(&loongson2_cpufreq_notifier_block,
 242                                    CPUFREQ_TRANSITION_NOTIFIER);
 243
 244        platform_driver_unregister(&platform_driver);
 245}
 246
 247module_init(cpufreq_init);
 248module_exit(cpufreq_exit);
 249
 250module_param(nowait, uint, 0644);
 251MODULE_PARM_DESC(nowait, "Disable Loongson-2F specific wait");
 252
 253MODULE_AUTHOR("Yanhua <yanh@lemote.com>");
 254MODULE_DESCRIPTION("cpufreq driver for Loongson2F");
 255MODULE_LICENSE("GPL");
 256
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.