Merge tag 'media/v6.5-4' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab...
[linux-2.6-microblaze.git] / drivers / cpufreq / sparc-us2e-cpufreq.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /* us2e_cpufreq.c: UltraSPARC-IIe cpu frequency support
3  *
4  * Copyright (C) 2003 David S. Miller (davem@redhat.com)
5  *
6  * Many thanks to Dominik Brodowski for fixing up the cpufreq
7  * infrastructure in order to make this driver easier to implement.
8  */
9
10 #include <linux/kernel.h>
11 #include <linux/module.h>
12 #include <linux/sched.h>
13 #include <linux/smp.h>
14 #include <linux/cpufreq.h>
15 #include <linux/threads.h>
16 #include <linux/slab.h>
17 #include <linux/delay.h>
18 #include <linux/init.h>
19
20 #include <asm/asi.h>
21 #include <asm/timer.h>
22
23 struct us2e_freq_percpu_info {
24         struct cpufreq_frequency_table table[6];
25 };
26
27 /* Indexed by cpu number. */
28 static struct us2e_freq_percpu_info *us2e_freq_table;
29
30 #define HBIRD_MEM_CNTL0_ADDR    0x1fe0000f010UL
31 #define HBIRD_ESTAR_MODE_ADDR   0x1fe0000f080UL
32
33 /* UltraSPARC-IIe has five dividers: 1, 2, 4, 6, and 8.  These are controlled
34  * in the ESTAR mode control register.
35  */
36 #define ESTAR_MODE_DIV_1        0x0000000000000000UL
37 #define ESTAR_MODE_DIV_2        0x0000000000000001UL
38 #define ESTAR_MODE_DIV_4        0x0000000000000003UL
39 #define ESTAR_MODE_DIV_6        0x0000000000000002UL
40 #define ESTAR_MODE_DIV_8        0x0000000000000004UL
41 #define ESTAR_MODE_DIV_MASK     0x0000000000000007UL
42
43 #define MCTRL0_SREFRESH_ENAB    0x0000000000010000UL
44 #define MCTRL0_REFR_COUNT_MASK  0x0000000000007f00UL
45 #define MCTRL0_REFR_COUNT_SHIFT 8
46 #define MCTRL0_REFR_INTERVAL    7800
47 #define MCTRL0_REFR_CLKS_P_CNT  64
48
49 static unsigned long read_hbreg(unsigned long addr)
50 {
51         unsigned long ret;
52
53         __asm__ __volatile__("ldxa      [%1] %2, %0"
54                              : "=&r" (ret)
55                              : "r" (addr), "i" (ASI_PHYS_BYPASS_EC_E));
56         return ret;
57 }
58
59 static void write_hbreg(unsigned long addr, unsigned long val)
60 {
61         __asm__ __volatile__("stxa      %0, [%1] %2\n\t"
62                              "membar    #Sync"
63                              : /* no outputs */
64                              : "r" (val), "r" (addr), "i" (ASI_PHYS_BYPASS_EC_E)
65                              : "memory");
66         if (addr == HBIRD_ESTAR_MODE_ADDR) {
67                 /* Need to wait 16 clock cycles for the PLL to lock.  */
68                 udelay(1);
69         }
70 }
71
72 static void self_refresh_ctl(int enable)
73 {
74         unsigned long mctrl = read_hbreg(HBIRD_MEM_CNTL0_ADDR);
75
76         if (enable)
77                 mctrl |= MCTRL0_SREFRESH_ENAB;
78         else
79                 mctrl &= ~MCTRL0_SREFRESH_ENAB;
80         write_hbreg(HBIRD_MEM_CNTL0_ADDR, mctrl);
81         (void) read_hbreg(HBIRD_MEM_CNTL0_ADDR);
82 }
83
84 static void frob_mem_refresh(int cpu_slowing_down,
85                              unsigned long clock_tick,
86                              unsigned long old_divisor, unsigned long divisor)
87 {
88         unsigned long old_refr_count, refr_count, mctrl;
89
90         refr_count  = (clock_tick * MCTRL0_REFR_INTERVAL);
91         refr_count /= (MCTRL0_REFR_CLKS_P_CNT * divisor * 1000000000UL);
92
93         mctrl = read_hbreg(HBIRD_MEM_CNTL0_ADDR);
94         old_refr_count = (mctrl & MCTRL0_REFR_COUNT_MASK)
95                 >> MCTRL0_REFR_COUNT_SHIFT;
96
97         mctrl &= ~MCTRL0_REFR_COUNT_MASK;
98         mctrl |= refr_count << MCTRL0_REFR_COUNT_SHIFT;
99         write_hbreg(HBIRD_MEM_CNTL0_ADDR, mctrl);
100         mctrl = read_hbreg(HBIRD_MEM_CNTL0_ADDR);
101
102         if (cpu_slowing_down && !(mctrl & MCTRL0_SREFRESH_ENAB)) {
103                 unsigned long usecs;
104
105                 /* We have to wait for both refresh counts (old
106                  * and new) to go to zero.
107                  */
108                 usecs = (MCTRL0_REFR_CLKS_P_CNT *
109                          (refr_count + old_refr_count) *
110                          1000000UL *
111                          old_divisor) / clock_tick;
112                 udelay(usecs + 1UL);
113         }
114 }
115
116 static void us2e_transition(unsigned long estar, unsigned long new_bits,
117                             unsigned long clock_tick,
118                             unsigned long old_divisor, unsigned long divisor)
119 {
120         estar &= ~ESTAR_MODE_DIV_MASK;
121
122         /* This is based upon the state transition diagram in the IIe manual.  */
123         if (old_divisor == 2 && divisor == 1) {
124                 self_refresh_ctl(0);
125                 write_hbreg(HBIRD_ESTAR_MODE_ADDR, estar | new_bits);
126                 frob_mem_refresh(0, clock_tick, old_divisor, divisor);
127         } else if (old_divisor == 1 && divisor == 2) {
128                 frob_mem_refresh(1, clock_tick, old_divisor, divisor);
129                 write_hbreg(HBIRD_ESTAR_MODE_ADDR, estar | new_bits);
130                 self_refresh_ctl(1);
131         } else if (old_divisor == 1 && divisor > 2) {
132                 us2e_transition(estar, ESTAR_MODE_DIV_2, clock_tick,
133                                 1, 2);
134                 us2e_transition(estar, new_bits, clock_tick,
135                                 2, divisor);
136         } else if (old_divisor > 2 && divisor == 1) {
137                 us2e_transition(estar, ESTAR_MODE_DIV_2, clock_tick,
138                                 old_divisor, 2);
139                 us2e_transition(estar, new_bits, clock_tick,
140                                 2, divisor);
141         } else if (old_divisor < divisor) {
142                 frob_mem_refresh(0, clock_tick, old_divisor, divisor);
143                 write_hbreg(HBIRD_ESTAR_MODE_ADDR, estar | new_bits);
144         } else if (old_divisor > divisor) {
145                 write_hbreg(HBIRD_ESTAR_MODE_ADDR, estar | new_bits);
146                 frob_mem_refresh(1, clock_tick, old_divisor, divisor);
147         } else {
148                 BUG();
149         }
150 }
151
152 static unsigned long index_to_estar_mode(unsigned int index)
153 {
154         switch (index) {
155         case 0:
156                 return ESTAR_MODE_DIV_1;
157
158         case 1:
159                 return ESTAR_MODE_DIV_2;
160
161         case 2:
162                 return ESTAR_MODE_DIV_4;
163
164         case 3:
165                 return ESTAR_MODE_DIV_6;
166
167         case 4:
168                 return ESTAR_MODE_DIV_8;
169
170         default:
171                 BUG();
172         }
173 }
174
175 static unsigned long index_to_divisor(unsigned int index)
176 {
177         switch (index) {
178         case 0:
179                 return 1;
180
181         case 1:
182                 return 2;
183
184         case 2:
185                 return 4;
186
187         case 3:
188                 return 6;
189
190         case 4:
191                 return 8;
192
193         default:
194                 BUG();
195         }
196 }
197
198 static unsigned long estar_to_divisor(unsigned long estar)
199 {
200         unsigned long ret;
201
202         switch (estar & ESTAR_MODE_DIV_MASK) {
203         case ESTAR_MODE_DIV_1:
204                 ret = 1;
205                 break;
206         case ESTAR_MODE_DIV_2:
207                 ret = 2;
208                 break;
209         case ESTAR_MODE_DIV_4:
210                 ret = 4;
211                 break;
212         case ESTAR_MODE_DIV_6:
213                 ret = 6;
214                 break;
215         case ESTAR_MODE_DIV_8:
216                 ret = 8;
217                 break;
218         default:
219                 BUG();
220         }
221
222         return ret;
223 }
224
225 static void __us2e_freq_get(void *arg)
226 {
227         unsigned long *estar = arg;
228
229         *estar = read_hbreg(HBIRD_ESTAR_MODE_ADDR);
230 }
231
232 static unsigned int us2e_freq_get(unsigned int cpu)
233 {
234         unsigned long clock_tick, estar;
235
236         clock_tick = sparc64_get_clock_tick(cpu) / 1000;
237         if (smp_call_function_single(cpu, __us2e_freq_get, &estar, 1))
238                 return 0;
239
240         return clock_tick / estar_to_divisor(estar);
241 }
242
243 static void __us2e_freq_target(void *arg)
244 {
245         unsigned int cpu = smp_processor_id();
246         unsigned int *index = arg;
247         unsigned long new_bits, new_freq;
248         unsigned long clock_tick, divisor, old_divisor, estar;
249
250         new_freq = clock_tick = sparc64_get_clock_tick(cpu) / 1000;
251         new_bits = index_to_estar_mode(*index);
252         divisor = index_to_divisor(*index);
253         new_freq /= divisor;
254
255         estar = read_hbreg(HBIRD_ESTAR_MODE_ADDR);
256
257         old_divisor = estar_to_divisor(estar);
258
259         if (old_divisor != divisor) {
260                 us2e_transition(estar, new_bits, clock_tick * 1000,
261                                 old_divisor, divisor);
262         }
263 }
264
265 static int us2e_freq_target(struct cpufreq_policy *policy, unsigned int index)
266 {
267         unsigned int cpu = policy->cpu;
268
269         return smp_call_function_single(cpu, __us2e_freq_target, &index, 1);
270 }
271
272 static int us2e_freq_cpu_init(struct cpufreq_policy *policy)
273 {
274         unsigned int cpu = policy->cpu;
275         unsigned long clock_tick = sparc64_get_clock_tick(cpu) / 1000;
276         struct cpufreq_frequency_table *table =
277                 &us2e_freq_table[cpu].table[0];
278
279         table[0].driver_data = 0;
280         table[0].frequency = clock_tick / 1;
281         table[1].driver_data = 1;
282         table[1].frequency = clock_tick / 2;
283         table[2].driver_data = 2;
284         table[2].frequency = clock_tick / 4;
285         table[2].driver_data = 3;
286         table[2].frequency = clock_tick / 6;
287         table[2].driver_data = 4;
288         table[2].frequency = clock_tick / 8;
289         table[2].driver_data = 5;
290         table[3].frequency = CPUFREQ_TABLE_END;
291
292         policy->cpuinfo.transition_latency = 0;
293         policy->cur = clock_tick;
294         policy->freq_table = table;
295
296         return 0;
297 }
298
299 static int us2e_freq_cpu_exit(struct cpufreq_policy *policy)
300 {
301         us2e_freq_target(policy, 0);
302         return 0;
303 }
304
305 static struct cpufreq_driver cpufreq_us2e_driver = {
306         .name = "UltraSPARC-IIe",
307         .init = us2e_freq_cpu_init,
308         .verify = cpufreq_generic_frequency_table_verify,
309         .target_index = us2e_freq_target,
310         .get = us2e_freq_get,
311         .exit = us2e_freq_cpu_exit,
312 };
313
314 static int __init us2e_freq_init(void)
315 {
316         unsigned long manuf, impl, ver;
317         int ret;
318
319         if (tlb_type != spitfire)
320                 return -ENODEV;
321
322         __asm__("rdpr %%ver, %0" : "=r" (ver));
323         manuf = ((ver >> 48) & 0xffff);
324         impl  = ((ver >> 32) & 0xffff);
325
326         if (manuf == 0x17 && impl == 0x13) {
327                 us2e_freq_table = kzalloc(NR_CPUS * sizeof(*us2e_freq_table),
328                                           GFP_KERNEL);
329                 if (!us2e_freq_table)
330                         return -ENOMEM;
331
332                 ret = cpufreq_register_driver(&cpufreq_us2e_driver);
333                 if (ret)
334                         kfree(us2e_freq_table);
335
336                 return ret;
337         }
338
339         return -ENODEV;
340 }
341
342 static void __exit us2e_freq_exit(void)
343 {
344         cpufreq_unregister_driver(&cpufreq_us2e_driver);
345         kfree(us2e_freq_table);
346 }
347
348 MODULE_AUTHOR("David S. Miller <davem@redhat.com>");
349 MODULE_DESCRIPTION("cpufreq driver for UltraSPARC-IIe");
350 MODULE_LICENSE("GPL");
351
352 module_init(us2e_freq_init);
353 module_exit(us2e_freq_exit);