Linux Audio

Check our new training course

Embedded Linux training

Mar 10-20, 2025, special US time zones
Register
Loading...
v6.13.7
  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 void us2e_freq_cpu_exit(struct cpufreq_policy *policy)
300{
301	us2e_freq_target(policy, 0);
302}
303
304static struct cpufreq_driver cpufreq_us2e_driver = {
305	.name = "UltraSPARC-IIe",
306	.init = us2e_freq_cpu_init,
307	.verify = cpufreq_generic_frequency_table_verify,
308	.target_index = us2e_freq_target,
309	.get = us2e_freq_get,
310	.exit = us2e_freq_cpu_exit,
311};
312
313static int __init us2e_freq_init(void)
314{
315	unsigned long manuf, impl, ver;
316	int ret;
317
318	if (tlb_type != spitfire)
319		return -ENODEV;
320
321	__asm__("rdpr %%ver, %0" : "=r" (ver));
322	manuf = ((ver >> 48) & 0xffff);
323	impl  = ((ver >> 32) & 0xffff);
324
325	if (manuf == 0x17 && impl == 0x13) {
326		us2e_freq_table = kzalloc(NR_CPUS * sizeof(*us2e_freq_table),
327					  GFP_KERNEL);
 
 
 
 
 
 
 
328		if (!us2e_freq_table)
329			return -ENOMEM;
 
 
 
 
 
 
 
330
331		ret = cpufreq_register_driver(&cpufreq_us2e_driver);
 
332		if (ret)
333			kfree(us2e_freq_table);
334
 
 
 
 
 
 
 
 
 
335		return ret;
336	}
337
338	return -ENODEV;
339}
340
341static void __exit us2e_freq_exit(void)
342{
343	cpufreq_unregister_driver(&cpufreq_us2e_driver);
344	kfree(us2e_freq_table);
 
 
 
 
 
345}
346
347MODULE_AUTHOR("David S. Miller <davem@redhat.com>");
348MODULE_DESCRIPTION("cpufreq driver for UltraSPARC-IIe");
349MODULE_LICENSE("GPL");
350
351module_init(us2e_freq_init);
352module_exit(us2e_freq_exit);
v5.4
  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);