ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / arch / sparc64 / kernel / us3_cpufreq.c
1 /* us3_cpufreq.c: UltraSPARC-III cpu frequency support
2  *
3  * Copyright (C) 2003 David S. Miller (davem@redhat.com)
4  *
5  * Many thanks to Dominik Brodowski for fixing up the cpufreq
6  * infrastructure in order to make this driver easier to implement.
7  */
8
9 #include <linux/kernel.h>
10 #include <linux/module.h>
11 #include <linux/sched.h>
12 #include <linux/smp.h>
13 #include <linux/cpufreq.h>
14 #include <linux/threads.h>
15 #include <linux/slab.h>
16 #include <linux/init.h>
17
18 #include <asm/head.h>
19 #include <asm/timer.h>
20
21 static struct cpufreq_driver *cpufreq_us3_driver;
22
23 struct us3_freq_percpu_info {
24         struct cpufreq_frequency_table table[4];
25 };
26
27 /* Indexed by cpu number. */
28 static struct us3_freq_percpu_info *us3_freq_table;
29
30 /* UltraSPARC-III has three dividers: 1, 2, and 32.  These are controlled
31  * in the Safari config register.
32  */
33 #define SAFARI_CFG_DIV_1        0x0000000000000000UL
34 #define SAFARI_CFG_DIV_2        0x0000000040000000UL
35 #define SAFARI_CFG_DIV_32       0x0000000080000000UL
36 #define SAFARI_CFG_DIV_MASK     0x00000000C0000000UL
37
38 static unsigned long read_safari_cfg(void)
39 {
40         unsigned long ret;
41
42         __asm__ __volatile__("ldxa      [%%g0] %1, %0"
43                              : "=&r" (ret)
44                              : "i" (ASI_SAFARI_CONFIG));
45         return ret;
46 }
47
48 static void write_safari_cfg(unsigned long val)
49 {
50         __asm__ __volatile__("stxa      %0, [%%g0] %1\n\t"
51                              "membar    #Sync"
52                              : /* no outputs */
53                              : "r" (val), "i" (ASI_SAFARI_CONFIG)
54                              : "memory");
55 }
56
57 static unsigned long get_current_freq(unsigned int cpu, unsigned long safari_cfg)
58 {
59         unsigned long clock_tick = sparc64_get_clock_tick(cpu);
60         unsigned long ret;
61
62         switch (safari_cfg & SAFARI_CFG_DIV_MASK) {
63         case SAFARI_CFG_DIV_1:
64                 ret = clock_tick / 1;
65                 break;
66         case SAFARI_CFG_DIV_2:
67                 ret = clock_tick / 2;
68                 break;
69         case SAFARI_CFG_DIV_32:
70                 ret = clock_tick / 32;
71                 break;
72         default:
73                 BUG();
74         };
75
76         return ret;
77 }
78
79 static void us3_set_cpu_divider_index(unsigned int cpu, unsigned int index)
80 {
81         unsigned long new_bits, new_freq, reg;
82         cpumask_t cpus_allowed;
83         struct cpufreq_freqs freqs;
84
85         if (!cpu_online(cpu))
86                 return;
87
88         cpus_allowed = current->cpus_allowed;
89         set_cpus_allowed(current, cpumask_of_cpu(cpu));
90
91         new_freq = sparc64_get_clock_tick(cpu);
92         switch (index) {
93         case 0:
94                 new_bits = SAFARI_CFG_DIV_1;
95                 new_freq /= 1;
96                 break;
97         case 1:
98                 new_bits = SAFARI_CFG_DIV_2;
99                 new_freq /= 2;
100                 break;
101         case 2:
102                 new_bits = SAFARI_CFG_DIV_32;
103                 new_freq /= 32;
104                 break;
105
106         default:
107                 BUG();
108         };
109
110         reg = read_safari_cfg();
111
112         freqs.old = get_current_freq(cpu, reg);
113         freqs.new = new_freq;
114         freqs.cpu = cpu;
115         cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
116
117         reg &= ~SAFARI_CFG_DIV_MASK;
118         reg |= new_bits;
119         write_safari_cfg(reg);
120
121         cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
122
123         set_cpus_allowed(current, cpus_allowed);
124 }
125
126 static int us3_freq_target(struct cpufreq_policy *policy,
127                           unsigned int target_freq,
128                           unsigned int relation)
129 {
130         unsigned int new_index = 0;
131
132         if (cpufreq_frequency_table_target(policy,
133                                            &us3_freq_table[policy->cpu].table[0],
134                                            target_freq,
135                                            relation,
136                                            &new_index))
137                 return -EINVAL;
138
139         us3_set_cpu_divider_index(policy->cpu, new_index);
140
141         return 0;
142 }
143
144 static int us3_freq_verify(struct cpufreq_policy *policy)
145 {
146         return cpufreq_frequency_table_verify(policy,
147                                               &us3_freq_table[policy->cpu].table[0]);
148 }
149
150 static int __init us3_freq_cpu_init(struct cpufreq_policy *policy)
151 {
152         unsigned int cpu = policy->cpu;
153         unsigned long clock_tick = sparc64_get_clock_tick(cpu);
154         struct cpufreq_frequency_table *table =
155                 &us3_freq_table[cpu].table[0];
156
157         table[0].index = 0;
158         table[0].frequency = clock_tick / 1;
159         table[1].index = 1;
160         table[1].frequency = clock_tick / 2;
161         table[2].index = 2;
162         table[2].frequency = clock_tick / 32;
163         table[3].index = 0;
164         table[3].frequency = CPUFREQ_TABLE_END;
165
166         policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
167         policy->cpuinfo.transition_latency = 0;
168         policy->cur = clock_tick;
169
170         return cpufreq_frequency_table_cpuinfo(policy, table);
171 }
172
173 static int us3_freq_cpu_exit(struct cpufreq_policy *policy)
174 {
175         if (cpufreq_us3_driver)
176                 us3_set_cpu_divider_index(policy->cpu, 0);
177
178         return 0;
179 }
180
181 static int __init us3_freq_init(void)
182 {
183         unsigned long manuf, impl, ver;
184         int ret;
185
186         __asm__("rdpr %%ver, %0" : "=r" (ver));
187         manuf = ((ver >> 48) & 0xffff);
188         impl  = ((ver >> 32) & 0xffff);
189
190         if (manuf == CHEETAH_MANUF &&
191             (impl == CHEETAH_IMPL || impl == CHEETAH_PLUS_IMPL)) {
192                 struct cpufreq_driver *driver;
193
194                 ret = -ENOMEM;
195                 driver = kmalloc(sizeof(struct cpufreq_driver), GFP_KERNEL);
196                 if (!driver)
197                         goto err_out;
198                 memset(driver, 0, sizeof(*driver));
199
200                 us3_freq_table = kmalloc(
201                         (NR_CPUS * sizeof(struct us3_freq_percpu_info)),
202                         GFP_KERNEL);
203                 if (!us3_freq_table)
204                         goto err_out;
205
206                 memset(us3_freq_table, 0,
207                        (NR_CPUS * sizeof(struct us3_freq_percpu_info)));
208
209                 driver->verify = us3_freq_verify;
210                 driver->target = us3_freq_target;
211                 driver->init = us3_freq_cpu_init;
212                 driver->exit = us3_freq_cpu_exit;
213                 driver->owner = THIS_MODULE,
214                 strcpy(driver->name, "UltraSPARC-III");
215
216                 cpufreq_us3_driver = driver;
217                 ret = cpufreq_register_driver(driver);
218                 if (ret)
219                         goto err_out;
220
221                 return 0;
222
223 err_out:
224                 if (driver) {
225                         kfree(driver);
226                         cpufreq_us3_driver = NULL;
227                 }
228                 if (us3_freq_table) {
229                         kfree(us3_freq_table);
230                         us3_freq_table = NULL;
231                 }
232                 return ret;
233         }
234
235         return -ENODEV;
236 }
237
238 static void __exit us3_freq_exit(void)
239 {
240         if (cpufreq_us3_driver) {
241                 cpufreq_unregister_driver(cpufreq_us3_driver);
242
243                 kfree(cpufreq_us3_driver);
244                 cpufreq_us3_driver = NULL;
245                 kfree(us3_freq_table);
246                 us3_freq_table = NULL;
247         }
248 }
249
250 MODULE_AUTHOR("David S. Miller <davem@redhat.com>");
251 MODULE_DESCRIPTION("cpufreq driver for UltraSPARC-III");
252 MODULE_LICENSE("GPL");
253
254 module_init(us3_freq_init);
255 module_exit(us3_freq_exit);