Commit | Line | Data |
---|---|---|
1da177e4 LT |
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 | { | |
2cab224d | 59 | unsigned long clock_tick = sparc64_get_clock_tick(cpu) / 1000; |
1da177e4 LT |
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 | ||
2cab224d DM |
79 | static unsigned int us3_freq_get(unsigned int cpu) |
80 | { | |
81 | cpumask_t cpus_allowed; | |
82 | unsigned long reg; | |
83 | unsigned int ret; | |
84 | ||
85 | if (!cpu_online(cpu)) | |
86 | return 0; | |
87 | ||
88 | cpus_allowed = current->cpus_allowed; | |
89 | set_cpus_allowed(current, cpumask_of_cpu(cpu)); | |
90 | ||
91 | reg = read_safari_cfg(); | |
92 | ret = get_current_freq(cpu, reg); | |
93 | ||
94 | set_cpus_allowed(current, cpus_allowed); | |
95 | ||
96 | return ret; | |
97 | } | |
98 | ||
1da177e4 LT |
99 | static void us3_set_cpu_divider_index(unsigned int cpu, unsigned int index) |
100 | { | |
101 | unsigned long new_bits, new_freq, reg; | |
102 | cpumask_t cpus_allowed; | |
103 | struct cpufreq_freqs freqs; | |
104 | ||
105 | if (!cpu_online(cpu)) | |
106 | return; | |
107 | ||
108 | cpus_allowed = current->cpus_allowed; | |
109 | set_cpus_allowed(current, cpumask_of_cpu(cpu)); | |
110 | ||
2cab224d | 111 | new_freq = sparc64_get_clock_tick(cpu) / 1000; |
1da177e4 LT |
112 | switch (index) { |
113 | case 0: | |
114 | new_bits = SAFARI_CFG_DIV_1; | |
115 | new_freq /= 1; | |
116 | break; | |
117 | case 1: | |
118 | new_bits = SAFARI_CFG_DIV_2; | |
119 | new_freq /= 2; | |
120 | break; | |
121 | case 2: | |
122 | new_bits = SAFARI_CFG_DIV_32; | |
123 | new_freq /= 32; | |
124 | break; | |
125 | ||
126 | default: | |
127 | BUG(); | |
128 | }; | |
129 | ||
130 | reg = read_safari_cfg(); | |
131 | ||
132 | freqs.old = get_current_freq(cpu, reg); | |
133 | freqs.new = new_freq; | |
134 | freqs.cpu = cpu; | |
135 | cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); | |
136 | ||
137 | reg &= ~SAFARI_CFG_DIV_MASK; | |
138 | reg |= new_bits; | |
139 | write_safari_cfg(reg); | |
140 | ||
141 | cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); | |
142 | ||
143 | set_cpus_allowed(current, cpus_allowed); | |
144 | } | |
145 | ||
146 | static int us3_freq_target(struct cpufreq_policy *policy, | |
147 | unsigned int target_freq, | |
148 | unsigned int relation) | |
149 | { | |
150 | unsigned int new_index = 0; | |
151 | ||
152 | if (cpufreq_frequency_table_target(policy, | |
153 | &us3_freq_table[policy->cpu].table[0], | |
154 | target_freq, | |
155 | relation, | |
156 | &new_index)) | |
157 | return -EINVAL; | |
158 | ||
159 | us3_set_cpu_divider_index(policy->cpu, new_index); | |
160 | ||
161 | return 0; | |
162 | } | |
163 | ||
164 | static int us3_freq_verify(struct cpufreq_policy *policy) | |
165 | { | |
166 | return cpufreq_frequency_table_verify(policy, | |
167 | &us3_freq_table[policy->cpu].table[0]); | |
168 | } | |
169 | ||
170 | static int __init us3_freq_cpu_init(struct cpufreq_policy *policy) | |
171 | { | |
172 | unsigned int cpu = policy->cpu; | |
2cab224d | 173 | unsigned long clock_tick = sparc64_get_clock_tick(cpu) / 1000; |
1da177e4 LT |
174 | struct cpufreq_frequency_table *table = |
175 | &us3_freq_table[cpu].table[0]; | |
176 | ||
177 | table[0].index = 0; | |
178 | table[0].frequency = clock_tick / 1; | |
179 | table[1].index = 1; | |
180 | table[1].frequency = clock_tick / 2; | |
181 | table[2].index = 2; | |
182 | table[2].frequency = clock_tick / 32; | |
183 | table[3].index = 0; | |
184 | table[3].frequency = CPUFREQ_TABLE_END; | |
185 | ||
186 | policy->governor = CPUFREQ_DEFAULT_GOVERNOR; | |
187 | policy->cpuinfo.transition_latency = 0; | |
188 | policy->cur = clock_tick; | |
189 | ||
190 | return cpufreq_frequency_table_cpuinfo(policy, table); | |
191 | } | |
192 | ||
193 | static int us3_freq_cpu_exit(struct cpufreq_policy *policy) | |
194 | { | |
195 | if (cpufreq_us3_driver) | |
196 | us3_set_cpu_divider_index(policy->cpu, 0); | |
197 | ||
198 | return 0; | |
199 | } | |
200 | ||
201 | static int __init us3_freq_init(void) | |
202 | { | |
203 | unsigned long manuf, impl, ver; | |
204 | int ret; | |
205 | ||
206 | __asm__("rdpr %%ver, %0" : "=r" (ver)); | |
207 | manuf = ((ver >> 48) & 0xffff); | |
208 | impl = ((ver >> 32) & 0xffff); | |
209 | ||
210 | if (manuf == CHEETAH_MANUF && | |
d2212bc7 DM |
211 | (impl == CHEETAH_IMPL || |
212 | impl == CHEETAH_PLUS_IMPL || | |
213 | impl == JAGUAR_IMPL || | |
214 | impl == PANTHER_IMPL)) { | |
1da177e4 LT |
215 | struct cpufreq_driver *driver; |
216 | ||
217 | ret = -ENOMEM; | |
218 | driver = kmalloc(sizeof(struct cpufreq_driver), GFP_KERNEL); | |
219 | if (!driver) | |
220 | goto err_out; | |
221 | memset(driver, 0, sizeof(*driver)); | |
222 | ||
223 | us3_freq_table = kmalloc( | |
224 | (NR_CPUS * sizeof(struct us3_freq_percpu_info)), | |
225 | GFP_KERNEL); | |
226 | if (!us3_freq_table) | |
227 | goto err_out; | |
228 | ||
229 | memset(us3_freq_table, 0, | |
230 | (NR_CPUS * sizeof(struct us3_freq_percpu_info))); | |
231 | ||
2cab224d | 232 | driver->init = us3_freq_cpu_init; |
1da177e4 LT |
233 | driver->verify = us3_freq_verify; |
234 | driver->target = us3_freq_target; | |
2cab224d | 235 | driver->get = us3_freq_get; |
1da177e4 LT |
236 | driver->exit = us3_freq_cpu_exit; |
237 | driver->owner = THIS_MODULE, | |
238 | strcpy(driver->name, "UltraSPARC-III"); | |
239 | ||
240 | cpufreq_us3_driver = driver; | |
241 | ret = cpufreq_register_driver(driver); | |
242 | if (ret) | |
243 | goto err_out; | |
244 | ||
245 | return 0; | |
246 | ||
247 | err_out: | |
248 | if (driver) { | |
249 | kfree(driver); | |
250 | cpufreq_us3_driver = NULL; | |
251 | } | |
b2325fe1 JJ |
252 | kfree(us3_freq_table); |
253 | us3_freq_table = NULL; | |
1da177e4 LT |
254 | return ret; |
255 | } | |
256 | ||
257 | return -ENODEV; | |
258 | } | |
259 | ||
260 | static void __exit us3_freq_exit(void) | |
261 | { | |
262 | if (cpufreq_us3_driver) { | |
263 | cpufreq_unregister_driver(cpufreq_us3_driver); | |
1da177e4 LT |
264 | kfree(cpufreq_us3_driver); |
265 | cpufreq_us3_driver = NULL; | |
266 | kfree(us3_freq_table); | |
267 | us3_freq_table = NULL; | |
268 | } | |
269 | } | |
270 | ||
271 | MODULE_AUTHOR("David S. Miller <davem@redhat.com>"); | |
272 | MODULE_DESCRIPTION("cpufreq driver for UltraSPARC-III"); | |
273 | MODULE_LICENSE("GPL"); | |
274 | ||
275 | module_init(us3_freq_init); | |
276 | module_exit(us3_freq_exit); |