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
23struct us2e_freq_percpu_info {
24	struct cpufreq_frequency_table table[6];
25};
26
27/* Indexed by cpu number. */
28static 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
49static 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
59static 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
72static 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
84static 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
116static 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
152static 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
175static 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
198static 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
225static void __us2e_freq_get(void *arg)
226{
227	unsigned long *estar = arg;
228
229	*estar = read_hbreg(HBIRD_ESTAR_MODE_ADDR);
230}
231
232static 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
243static 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
265static 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
272static 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
299static int us2e_freq_cpu_exit(struct cpufreq_policy *policy)
300{
301	us2e_freq_target(policy, 0);
302	return 0;
303}
304
305static 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
314static 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
342static void __exit us2e_freq_exit(void)
343{
344	cpufreq_unregister_driver(&cpufreq_us2e_driver);
345	kfree(us2e_freq_table);
346}
347
348MODULE_AUTHOR("David S. Miller <davem@redhat.com>");
349MODULE_DESCRIPTION("cpufreq driver for UltraSPARC-IIe");
350MODULE_LICENSE("GPL");
351
352module_init(us2e_freq_init);
353module_exit(us2e_freq_exit);
354