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 27 static uint nowait; 28 29 static void (*saved_cpu_wait) (void); 30 31 static int loongson2_cpu_freq_notifier(struct notifier_block *nb, 32 unsigned long val, void *data); 33 34 static struct notifier_block loongson2_cpufreq_notifier_block = { 35 .notifier_call = loongson2_cpu_freq_notifier 36 }; 37 38 static 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 */ 50 static 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 65 static 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 89 static int loongson2_cpufreq_exit(struct cpufreq_policy *policy) 90 { 91 return 0; 92 } 93 94 static 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 104 static const struct platform_device_id platform_device_ids[] = { 105 { 106 .name = "loongson2_cpufreq", 107 }, 108 {} 109 }; 110 111 MODULE_DEVICE_TABLE(platform, platform_device_ids); 112 113 static 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 125 static DEFINE_SPINLOCK(loongson2_wait_lock); 126 127 static 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 142 static 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 166 static 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 177 module_init(cpufreq_init); 178 module_exit(cpufreq_exit); 179 180 module_param(nowait, uint, 0644); 181 MODULE_PARM_DESC(nowait, "Disable Loongson-2F specific wait"); 182 183 MODULE_AUTHOR("Yanhua <yanh@lemote.com>"); 184 MODULE_DESCRIPTION("cpufreq driver for Loongson2F"); 185 MODULE_LICENSE("GPL"); 186