Linux Audio

Check our new training course

Loading...
v6.8
  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);
v4.6
 
  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	unsigned long flags;
122
123	local_irq_save(flags);
124
125	estar &= ~ESTAR_MODE_DIV_MASK;
126
127	/* This is based upon the state transition diagram in the IIe manual.  */
128	if (old_divisor == 2 && divisor == 1) {
129		self_refresh_ctl(0);
130		write_hbreg(HBIRD_ESTAR_MODE_ADDR, estar | new_bits);
131		frob_mem_refresh(0, clock_tick, old_divisor, divisor);
132	} else if (old_divisor == 1 && divisor == 2) {
133		frob_mem_refresh(1, clock_tick, old_divisor, divisor);
134		write_hbreg(HBIRD_ESTAR_MODE_ADDR, estar | new_bits);
135		self_refresh_ctl(1);
136	} else if (old_divisor == 1 && divisor > 2) {
137		us2e_transition(estar, ESTAR_MODE_DIV_2, clock_tick,
138				1, 2);
139		us2e_transition(estar, new_bits, clock_tick,
140				2, divisor);
141	} else if (old_divisor > 2 && divisor == 1) {
142		us2e_transition(estar, ESTAR_MODE_DIV_2, clock_tick,
143				old_divisor, 2);
144		us2e_transition(estar, new_bits, clock_tick,
145				2, divisor);
146	} else if (old_divisor < divisor) {
147		frob_mem_refresh(0, clock_tick, old_divisor, divisor);
148		write_hbreg(HBIRD_ESTAR_MODE_ADDR, estar | new_bits);
149	} else if (old_divisor > divisor) {
150		write_hbreg(HBIRD_ESTAR_MODE_ADDR, estar | new_bits);
151		frob_mem_refresh(1, clock_tick, old_divisor, divisor);
152	} else {
153		BUG();
154	}
155
156	local_irq_restore(flags);
157}
158
159static unsigned long index_to_estar_mode(unsigned int index)
160{
161	switch (index) {
162	case 0:
163		return ESTAR_MODE_DIV_1;
164
165	case 1:
166		return ESTAR_MODE_DIV_2;
167
168	case 2:
169		return ESTAR_MODE_DIV_4;
170
171	case 3:
172		return ESTAR_MODE_DIV_6;
173
174	case 4:
175		return ESTAR_MODE_DIV_8;
176
177	default:
178		BUG();
179	}
180}
181
182static unsigned long index_to_divisor(unsigned int index)
183{
184	switch (index) {
185	case 0:
186		return 1;
187
188	case 1:
189		return 2;
190
191	case 2:
192		return 4;
193
194	case 3:
195		return 6;
196
197	case 4:
198		return 8;
199
200	default:
201		BUG();
202	}
203}
204
205static unsigned long estar_to_divisor(unsigned long estar)
206{
207	unsigned long ret;
208
209	switch (estar & ESTAR_MODE_DIV_MASK) {
210	case ESTAR_MODE_DIV_1:
211		ret = 1;
212		break;
213	case ESTAR_MODE_DIV_2:
214		ret = 2;
215		break;
216	case ESTAR_MODE_DIV_4:
217		ret = 4;
218		break;
219	case ESTAR_MODE_DIV_6:
220		ret = 6;
221		break;
222	case ESTAR_MODE_DIV_8:
223		ret = 8;
224		break;
225	default:
226		BUG();
227	}
228
229	return ret;
230}
231
 
 
 
 
 
 
 
232static unsigned int us2e_freq_get(unsigned int cpu)
233{
234	cpumask_t cpus_allowed;
235	unsigned long clock_tick, estar;
236
237	cpumask_copy(&cpus_allowed, tsk_cpus_allowed(current));
238	set_cpus_allowed_ptr(current, cpumask_of(cpu));
239
240	clock_tick = sparc64_get_clock_tick(cpu) / 1000;
241	estar = read_hbreg(HBIRD_ESTAR_MODE_ADDR);
242
243	set_cpus_allowed_ptr(current, &cpus_allowed);
244
245	return clock_tick / estar_to_divisor(estar);
246}
247
248static int us2e_freq_target(struct cpufreq_policy *policy, unsigned int index)
249{
250	unsigned int cpu = policy->cpu;
 
251	unsigned long new_bits, new_freq;
252	unsigned long clock_tick, divisor, old_divisor, estar;
253	cpumask_t cpus_allowed;
254
255	cpumask_copy(&cpus_allowed, tsk_cpus_allowed(current));
256	set_cpus_allowed_ptr(current, cpumask_of(cpu));
257
258	new_freq = clock_tick = sparc64_get_clock_tick(cpu) / 1000;
259	new_bits = index_to_estar_mode(index);
260	divisor = index_to_divisor(index);
261	new_freq /= divisor;
262
263	estar = read_hbreg(HBIRD_ESTAR_MODE_ADDR);
264
265	old_divisor = estar_to_divisor(estar);
266
267	if (old_divisor != divisor)
268		us2e_transition(estar, new_bits, clock_tick * 1000,
269				old_divisor, divisor);
 
 
270
271	set_cpus_allowed_ptr(current, &cpus_allowed);
 
 
272
273	return 0;
274}
275
276static int __init us2e_freq_cpu_init(struct cpufreq_policy *policy)
277{
278	unsigned int cpu = policy->cpu;
279	unsigned long clock_tick = sparc64_get_clock_tick(cpu) / 1000;
280	struct cpufreq_frequency_table *table =
281		&us2e_freq_table[cpu].table[0];
282
283	table[0].driver_data = 0;
284	table[0].frequency = clock_tick / 1;
285	table[1].driver_data = 1;
286	table[1].frequency = clock_tick / 2;
287	table[2].driver_data = 2;
288	table[2].frequency = clock_tick / 4;
289	table[2].driver_data = 3;
290	table[2].frequency = clock_tick / 6;
291	table[2].driver_data = 4;
292	table[2].frequency = clock_tick / 8;
293	table[2].driver_data = 5;
294	table[3].frequency = CPUFREQ_TABLE_END;
295
296	policy->cpuinfo.transition_latency = 0;
297	policy->cur = clock_tick;
 
298
299	return cpufreq_table_validate_and_show(policy, table);
300}
301
302static int us2e_freq_cpu_exit(struct cpufreq_policy *policy)
303{
304	if (cpufreq_us2e_driver)
305		us2e_freq_target(policy, 0);
306
307	return 0;
308}
309
 
 
 
 
 
 
 
 
 
310static int __init us2e_freq_init(void)
311{
312	unsigned long manuf, impl, ver;
313	int ret;
314
315	if (tlb_type != spitfire)
316		return -ENODEV;
317
318	__asm__("rdpr %%ver, %0" : "=r" (ver));
319	manuf = ((ver >> 48) & 0xffff);
320	impl  = ((ver >> 32) & 0xffff);
321
322	if (manuf == 0x17 && impl == 0x13) {
323		struct cpufreq_driver *driver;
324
325		ret = -ENOMEM;
326		driver = kzalloc(sizeof(*driver), GFP_KERNEL);
327		if (!driver)
328			goto err_out;
329
330		us2e_freq_table = kzalloc((NR_CPUS * sizeof(*us2e_freq_table)),
331			GFP_KERNEL);
332		if (!us2e_freq_table)
333			goto err_out;
334
335		driver->init = us2e_freq_cpu_init;
336		driver->verify = cpufreq_generic_frequency_table_verify;
337		driver->target_index = us2e_freq_target;
338		driver->get = us2e_freq_get;
339		driver->exit = us2e_freq_cpu_exit;
340		strcpy(driver->name, "UltraSPARC-IIe");
341
342		cpufreq_us2e_driver = driver;
343		ret = cpufreq_register_driver(driver);
344		if (ret)
345			goto err_out;
346
347		return 0;
348
349err_out:
350		if (driver) {
351			kfree(driver);
352			cpufreq_us2e_driver = NULL;
353		}
354		kfree(us2e_freq_table);
355		us2e_freq_table = NULL;
356		return ret;
357	}
358
359	return -ENODEV;
360}
361
362static void __exit us2e_freq_exit(void)
363{
364	if (cpufreq_us2e_driver) {
365		cpufreq_unregister_driver(cpufreq_us2e_driver);
366		kfree(cpufreq_us2e_driver);
367		cpufreq_us2e_driver = NULL;
368		kfree(us2e_freq_table);
369		us2e_freq_table = NULL;
370	}
371}
372
373MODULE_AUTHOR("David S. Miller <davem@redhat.com>");
374MODULE_DESCRIPTION("cpufreq driver for UltraSPARC-IIe");
375MODULE_LICENSE("GPL");
376
377module_init(us2e_freq_init);
378module_exit(us2e_freq_exit);