Linux Audio

Check our new training course

Yocto / OpenEmbedded training

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