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