linux/drivers/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#include <asm/idle.h>
  22
  23#include <asm/mach-loongson/loongson.h>
  24
  25static uint nowait;
  26
  27static void (*saved_cpu_wait) (void);
  28
  29static int loongson2_cpu_freq_notifier(struct notifier_block *nb,
  30                                        unsigned long val, void *data);
  31
  32static struct notifier_block loongson2_cpufreq_notifier_block = {
  33        .notifier_call = loongson2_cpu_freq_notifier
  34};
  35
  36static int loongson2_cpu_freq_notifier(struct notifier_block *nb,
  37                                        unsigned long val, void *data)
  38{
  39        if (val == CPUFREQ_POSTCHANGE)
  40                current_cpu_data.udelay_val = loops_per_jiffy;
  41
  42        return 0;
  43}
  44
  45/*
  46 * Here we notify other drivers of the proposed change and the final change.
  47 */
  48static int loongson2_cpufreq_target(struct cpufreq_policy *policy,
  49                                     unsigned int index)
  50{
  51        unsigned int cpu = policy->cpu;
  52        cpumask_t cpus_allowed;
  53        unsigned int freq;
  54
  55        cpus_allowed = current->cpus_allowed;
  56        set_cpus_allowed_ptr(current, cpumask_of(cpu));
  57
  58        freq =
  59            ((cpu_clock_freq / 1000) *
  60             loongson2_clockmod_table[index].driver_data) / 8;
  61
  62        set_cpus_allowed_ptr(current, &cpus_allowed);
  63
  64        /* setting the cpu frequency */
  65        clk_set_rate(policy->clk, freq * 1000);
  66
  67        return 0;
  68}
  69
  70static int loongson2_cpufreq_cpu_init(struct cpufreq_policy *policy)
  71{
  72        struct clk *cpuclk;
  73        int i;
  74        unsigned long rate;
  75        int ret;
  76
  77        cpuclk = clk_get(NULL, "cpu_clk");
  78        if (IS_ERR(cpuclk)) {
  79                printk(KERN_ERR "cpufreq: couldn't get CPU clk\n");
  80                return PTR_ERR(cpuclk);
  81        }
  82
  83        rate = cpu_clock_freq / 1000;
  84        if (!rate) {
  85                clk_put(cpuclk);
  86                return -EINVAL;
  87        }
  88
  89        /* clock table init */
  90        for (i = 2;
  91             (loongson2_clockmod_table[i].frequency != CPUFREQ_TABLE_END);
  92             i++)
  93                loongson2_clockmod_table[i].frequency = (rate * i) / 8;
  94
  95        ret = clk_set_rate(cpuclk, rate * 1000);
  96        if (ret) {
  97                clk_put(cpuclk);
  98                return ret;
  99        }
 100
 101        policy->clk = cpuclk;
 102        return cpufreq_generic_init(policy, &loongson2_clockmod_table[0], 0);
 103}
 104
 105static int loongson2_cpufreq_exit(struct cpufreq_policy *policy)
 106{
 107        clk_put(policy->clk);
 108        return 0;
 109}
 110
 111static struct cpufreq_driver loongson2_cpufreq_driver = {
 112        .name = "loongson2",
 113        .init = loongson2_cpufreq_cpu_init,
 114        .verify = cpufreq_generic_frequency_table_verify,
 115        .target_index = loongson2_cpufreq_target,
 116        .get = cpufreq_generic_get,
 117        .exit = loongson2_cpufreq_exit,
 118        .attr = cpufreq_generic_attr,
 119};
 120
 121static struct platform_device_id platform_device_ids[] = {
 122        {
 123                .name = "loongson2_cpufreq",
 124        },
 125        {}
 126};
 127
 128MODULE_DEVICE_TABLE(platform, platform_device_ids);
 129
 130static struct platform_driver platform_driver = {
 131        .driver = {
 132                .name = "loongson2_cpufreq",
 133                .owner = THIS_MODULE,
 134        },
 135        .id_table = platform_device_ids,
 136};
 137
 138/*
 139 * This is the simple version of Loongson-2 wait, Maybe we need do this in
 140 * interrupt disabled context.
 141 */
 142
 143static DEFINE_SPINLOCK(loongson2_wait_lock);
 144
 145static void loongson2_cpu_wait(void)
 146{
 147        unsigned long flags;
 148        u32 cpu_freq;
 149
 150        spin_lock_irqsave(&loongson2_wait_lock, flags);
 151        cpu_freq = LOONGSON_CHIPCFG0;
 152        LOONGSON_CHIPCFG0 &= ~0x7;      /* Put CPU into wait mode */
 153        LOONGSON_CHIPCFG0 = cpu_freq;   /* Restore CPU state */
 154        spin_unlock_irqrestore(&loongson2_wait_lock, flags);
 155        local_irq_enable();
 156}
 157
 158static int __init cpufreq_init(void)
 159{
 160        int ret;
 161
 162        /* Register platform stuff */
 163        ret = platform_driver_register(&platform_driver);
 164        if (ret)
 165                return ret;
 166
 167        pr_info("cpufreq: Loongson-2F CPU frequency driver.\n");
 168
 169        cpufreq_register_notifier(&loongson2_cpufreq_notifier_block,
 170                                  CPUFREQ_TRANSITION_NOTIFIER);
 171
 172        ret = cpufreq_register_driver(&loongson2_cpufreq_driver);
 173
 174        if (!ret && !nowait) {
 175                saved_cpu_wait = cpu_wait;
 176                cpu_wait = loongson2_cpu_wait;
 177        }
 178
 179        return ret;
 180}
 181
 182static void __exit cpufreq_exit(void)
 183{
 184        if (!nowait && saved_cpu_wait)
 185                cpu_wait = saved_cpu_wait;
 186        cpufreq_unregister_driver(&loongson2_cpufreq_driver);
 187        cpufreq_unregister_notifier(&loongson2_cpufreq_notifier_block,
 188                                    CPUFREQ_TRANSITION_NOTIFIER);
 189
 190        platform_driver_unregister(&platform_driver);
 191}
 192
 193module_init(cpufreq_init);
 194module_exit(cpufreq_exit);
 195
 196module_param(nowait, uint, 0644);
 197MODULE_PARM_DESC(nowait, "Disable Loongson-2F specific wait");
 198
 199MODULE_AUTHOR("Yanhua <yanh@lemote.com>");
 200MODULE_DESCRIPTION("cpufreq driver for Loongson2F");
 201MODULE_LICENSE("GPL");
 202