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. & Institute 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
  14#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  15
  16#include <linux/cpufreq.h>
  17#include <linux/module.h>
  18#include <linux/err.h>
  19#include <linux/delay.h>
  20#include <linux/platform_device.h>
  21
  22#include <asm/idle.h>
  23
  24#include <asm/mach-loongson2ef/loongson.h>
  25
  26static uint nowait;
  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
  46/*
  47 * Here we notify other drivers of the proposed change and the final change.
  48 */
  49static int loongson2_cpufreq_target(struct cpufreq_policy *policy,
  50                                     unsigned int index)
  51{
  52        unsigned int freq;
  53
  54        freq =
  55            ((cpu_clock_freq / 1000) *
  56             loongson2_clockmod_table[index].driver_data) / 8;
  57
  58        /* setting the cpu frequency */
  59        loongson2_cpu_set_rate(freq);
  60
  61        return 0;
  62}
  63
  64static int loongson2_cpufreq_cpu_init(struct cpufreq_policy *policy)
  65{
  66        int i;
  67        unsigned long rate;
  68        int ret;
  69
  70        rate = cpu_clock_freq / 1000;
  71        if (!rate)
  72                return -EINVAL;
  73
  74        /* clock table init */
  75        for (i = 2;
  76             (loongson2_clockmod_table[i].frequency != CPUFREQ_TABLE_END);
  77             i++)
  78                loongson2_clockmod_table[i].frequency = (rate * i) / 8;
  79
  80        ret = loongson2_cpu_set_rate(rate);
  81        if (ret)
  82                return ret;
  83
  84        cpufreq_generic_init(policy, &loongson2_clockmod_table[0], 0);
  85        return 0;
  86}
  87
  88static int loongson2_cpufreq_exit(struct cpufreq_policy *policy)
  89{
  90        return 0;
  91}
  92
  93static struct cpufreq_driver loongson2_cpufreq_driver = {
  94        .name = "loongson2",
  95        .init = loongson2_cpufreq_cpu_init,
  96        .verify = cpufreq_generic_frequency_table_verify,
  97        .target_index = loongson2_cpufreq_target,
  98        .get = cpufreq_generic_get,
  99        .exit = loongson2_cpufreq_exit,
 100        .attr = cpufreq_generic_attr,
 101};
 102
 103static const struct platform_device_id platform_device_ids[] = {
 104        {
 105                .name = "loongson2_cpufreq",
 106        },
 107        {}
 108};
 109
 110MODULE_DEVICE_TABLE(platform, platform_device_ids);
 111
 112static struct platform_driver platform_driver = {
 113        .driver = {
 114                .name = "loongson2_cpufreq",
 115        },
 116        .id_table = platform_device_ids,
 117};
 118
 119/*
 120 * This is the simple version of Loongson-2 wait, Maybe we need do this in
 121 * interrupt disabled context.
 122 */
 123
 124static DEFINE_SPINLOCK(loongson2_wait_lock);
 125
 126static void loongson2_cpu_wait(void)
 127{
 128        unsigned long flags;
 129        u32 cpu_freq;
 130
 131        spin_lock_irqsave(&loongson2_wait_lock, flags);
 132        cpu_freq = readl(LOONGSON_CHIPCFG);
 133        /* Put CPU into wait mode */
 134        writel(readl(LOONGSON_CHIPCFG) & ~0x7, LOONGSON_CHIPCFG);
 135        /* Restore CPU state */
 136        writel(cpu_freq, LOONGSON_CHIPCFG);
 137        spin_unlock_irqrestore(&loongson2_wait_lock, flags);
 138        local_irq_enable();
 139}
 140
 141static int __init cpufreq_init(void)
 142{
 143        int ret;
 144
 145        /* Register platform stuff */
 146        ret = platform_driver_register(&platform_driver);
 147        if (ret)
 148                return ret;
 149
 150        pr_info("Loongson-2F CPU frequency driver\n");
 151
 152        cpufreq_register_notifier(&loongson2_cpufreq_notifier_block,
 153                                  CPUFREQ_TRANSITION_NOTIFIER);
 154
 155        ret = cpufreq_register_driver(&loongson2_cpufreq_driver);
 156
 157        if (!ret && !nowait) {
 158                saved_cpu_wait = cpu_wait;
 159                cpu_wait = loongson2_cpu_wait;
 160        }
 161
 162        return ret;
 163}
 164
 165static void __exit cpufreq_exit(void)
 166{
 167        if (!nowait && saved_cpu_wait)
 168                cpu_wait = saved_cpu_wait;
 169        cpufreq_unregister_driver(&loongson2_cpufreq_driver);
 170        cpufreq_unregister_notifier(&loongson2_cpufreq_notifier_block,
 171                                    CPUFREQ_TRANSITION_NOTIFIER);
 172
 173        platform_driver_unregister(&platform_driver);
 174}
 175
 176module_init(cpufreq_init);
 177module_exit(cpufreq_exit);
 178
 179module_param(nowait, uint, 0644);
 180MODULE_PARM_DESC(nowait, "Disable Loongson-2F specific wait");
 181
 182MODULE_AUTHOR("Yanhua <yanh@lemote.com>");
 183MODULE_DESCRIPTION("cpufreq driver for Loongson2F");
 184MODULE_LICENSE("GPL");
 185