Linux Audio

Check our new training course

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