Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v3.12-rc5 250 lines 6.0 kB view raw
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 struct clk *cpuclk; 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 47static unsigned int loongson2_cpufreq_get(unsigned int cpu) 48{ 49 return clk_get_rate(cpuclk); 50} 51 52/* 53 * Here we notify other drivers of the proposed change and the final change. 54 */ 55static int loongson2_cpufreq_target(struct cpufreq_policy *policy, 56 unsigned int target_freq, 57 unsigned int relation) 58{ 59 unsigned int cpu = policy->cpu; 60 unsigned int newstate = 0; 61 cpumask_t cpus_allowed; 62 struct cpufreq_freqs freqs; 63 unsigned int freq; 64 65 cpus_allowed = current->cpus_allowed; 66 set_cpus_allowed_ptr(current, cpumask_of(cpu)); 67 68 if (cpufreq_frequency_table_target 69 (policy, &loongson2_clockmod_table[0], target_freq, relation, 70 &newstate)) 71 return -EINVAL; 72 73 freq = 74 ((cpu_clock_freq / 1000) * 75 loongson2_clockmod_table[newstate].driver_data) / 8; 76 if (freq < policy->min || freq > policy->max) 77 return -EINVAL; 78 79 pr_debug("cpufreq: requested frequency %u Hz\n", target_freq * 1000); 80 81 freqs.old = loongson2_cpufreq_get(cpu); 82 freqs.new = freq; 83 freqs.flags = 0; 84 85 if (freqs.new == freqs.old) 86 return 0; 87 88 /* notifiers */ 89 cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE); 90 91 set_cpus_allowed_ptr(current, &cpus_allowed); 92 93 /* setting the cpu frequency */ 94 clk_set_rate(cpuclk, freq); 95 96 /* notifiers */ 97 cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE); 98 99 pr_debug("cpufreq: set frequency %u kHz\n", freq); 100 101 return 0; 102} 103 104static int loongson2_cpufreq_cpu_init(struct cpufreq_policy *policy) 105{ 106 int i; 107 unsigned long rate; 108 int ret; 109 110 cpuclk = clk_get(NULL, "cpu_clk"); 111 if (IS_ERR(cpuclk)) { 112 printk(KERN_ERR "cpufreq: couldn't get CPU clk\n"); 113 return PTR_ERR(cpuclk); 114 } 115 116 rate = cpu_clock_freq / 1000; 117 if (!rate) { 118 clk_put(cpuclk); 119 return -EINVAL; 120 } 121 122 /* clock table init */ 123 for (i = 2; 124 (loongson2_clockmod_table[i].frequency != CPUFREQ_TABLE_END); 125 i++) 126 loongson2_clockmod_table[i].frequency = (rate * i) / 8; 127 128 ret = clk_set_rate(cpuclk, rate); 129 if (ret) { 130 clk_put(cpuclk); 131 return ret; 132 } 133 134 policy->cur = loongson2_cpufreq_get(policy->cpu); 135 136 cpufreq_frequency_table_get_attr(&loongson2_clockmod_table[0], 137 policy->cpu); 138 139 return cpufreq_frequency_table_cpuinfo(policy, 140 &loongson2_clockmod_table[0]); 141} 142 143static int loongson2_cpufreq_verify(struct cpufreq_policy *policy) 144{ 145 return cpufreq_frequency_table_verify(policy, 146 &loongson2_clockmod_table[0]); 147} 148 149static int loongson2_cpufreq_exit(struct cpufreq_policy *policy) 150{ 151 clk_put(cpuclk); 152 return 0; 153} 154 155static struct freq_attr *loongson2_table_attr[] = { 156 &cpufreq_freq_attr_scaling_available_freqs, 157 NULL, 158}; 159 160static struct cpufreq_driver loongson2_cpufreq_driver = { 161 .name = "loongson2", 162 .init = loongson2_cpufreq_cpu_init, 163 .verify = loongson2_cpufreq_verify, 164 .target = loongson2_cpufreq_target, 165 .get = loongson2_cpufreq_get, 166 .exit = loongson2_cpufreq_exit, 167 .attr = loongson2_table_attr, 168}; 169 170static struct platform_device_id platform_device_ids[] = { 171 { 172 .name = "loongson2_cpufreq", 173 }, 174 {} 175}; 176 177MODULE_DEVICE_TABLE(platform, platform_device_ids); 178 179static struct platform_driver platform_driver = { 180 .driver = { 181 .name = "loongson2_cpufreq", 182 .owner = THIS_MODULE, 183 }, 184 .id_table = platform_device_ids, 185}; 186 187/* 188 * This is the simple version of Loongson-2 wait, Maybe we need do this in 189 * interrupt disabled context. 190 */ 191 192static DEFINE_SPINLOCK(loongson2_wait_lock); 193 194static void loongson2_cpu_wait(void) 195{ 196 unsigned long flags; 197 u32 cpu_freq; 198 199 spin_lock_irqsave(&loongson2_wait_lock, flags); 200 cpu_freq = LOONGSON_CHIPCFG0; 201 LOONGSON_CHIPCFG0 &= ~0x7; /* Put CPU into wait mode */ 202 LOONGSON_CHIPCFG0 = cpu_freq; /* Restore CPU state */ 203 spin_unlock_irqrestore(&loongson2_wait_lock, flags); 204 local_irq_enable(); 205} 206 207static int __init cpufreq_init(void) 208{ 209 int ret; 210 211 /* Register platform stuff */ 212 ret = platform_driver_register(&platform_driver); 213 if (ret) 214 return ret; 215 216 pr_info("cpufreq: Loongson-2F CPU frequency driver.\n"); 217 218 cpufreq_register_notifier(&loongson2_cpufreq_notifier_block, 219 CPUFREQ_TRANSITION_NOTIFIER); 220 221 ret = cpufreq_register_driver(&loongson2_cpufreq_driver); 222 223 if (!ret && !nowait) { 224 saved_cpu_wait = cpu_wait; 225 cpu_wait = loongson2_cpu_wait; 226 } 227 228 return ret; 229} 230 231static void __exit cpufreq_exit(void) 232{ 233 if (!nowait && saved_cpu_wait) 234 cpu_wait = saved_cpu_wait; 235 cpufreq_unregister_driver(&loongson2_cpufreq_driver); 236 cpufreq_unregister_notifier(&loongson2_cpufreq_notifier_block, 237 CPUFREQ_TRANSITION_NOTIFIER); 238 239 platform_driver_unregister(&platform_driver); 240} 241 242module_init(cpufreq_init); 243module_exit(cpufreq_exit); 244 245module_param(nowait, uint, 0644); 246MODULE_PARM_DESC(nowait, "Disable Loongson-2F specific wait"); 247 248MODULE_AUTHOR("Yanhua <yanh@lemote.com>"); 249MODULE_DESCRIPTION("cpufreq driver for Loongson2F"); 250MODULE_LICENSE("GPL");