• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/linux/linux-2.6/arch/sparc/kernel/
1/* us3_cpufreq.c: UltraSPARC-III 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/init.h>
17
18#include <asm/head.h>
19#include <asm/timer.h>
20
21static struct cpufreq_driver *cpufreq_us3_driver;
22
23struct us3_freq_percpu_info {
24	struct cpufreq_frequency_table table[4];
25};
26
27/* Indexed by cpu number. */
28static struct us3_freq_percpu_info *us3_freq_table;
29
30/* UltraSPARC-III has three dividers: 1, 2, and 32.  These are controlled
31 * in the Safari config register.
32 */
33#define SAFARI_CFG_DIV_1	0x0000000000000000UL
34#define SAFARI_CFG_DIV_2	0x0000000040000000UL
35#define SAFARI_CFG_DIV_32	0x0000000080000000UL
36#define SAFARI_CFG_DIV_MASK	0x00000000C0000000UL
37
38static unsigned long read_safari_cfg(void)
39{
40	unsigned long ret;
41
42	__asm__ __volatile__("ldxa	[%%g0] %1, %0"
43			     : "=&r" (ret)
44			     : "i" (ASI_SAFARI_CONFIG));
45	return ret;
46}
47
48static void write_safari_cfg(unsigned long val)
49{
50	__asm__ __volatile__("stxa	%0, [%%g0] %1\n\t"
51			     "membar	#Sync"
52			     : /* no outputs */
53			     : "r" (val), "i" (ASI_SAFARI_CONFIG)
54			     : "memory");
55}
56
57static unsigned long get_current_freq(unsigned int cpu, unsigned long safari_cfg)
58{
59	unsigned long clock_tick = sparc64_get_clock_tick(cpu) / 1000;
60	unsigned long ret;
61
62	switch (safari_cfg & SAFARI_CFG_DIV_MASK) {
63	case SAFARI_CFG_DIV_1:
64		ret = clock_tick / 1;
65		break;
66	case SAFARI_CFG_DIV_2:
67		ret = clock_tick / 2;
68		break;
69	case SAFARI_CFG_DIV_32:
70		ret = clock_tick / 32;
71		break;
72	default:
73		BUG();
74	};
75
76	return ret;
77}
78
79static unsigned int us3_freq_get(unsigned int cpu)
80{
81	cpumask_t cpus_allowed;
82	unsigned long reg;
83	unsigned int ret;
84
85	if (!cpu_online(cpu))
86		return 0;
87
88	cpus_allowed = current->cpus_allowed;
89	set_cpus_allowed_ptr(current, cpumask_of(cpu));
90
91	reg = read_safari_cfg();
92	ret = get_current_freq(cpu, reg);
93
94	set_cpus_allowed_ptr(current, &cpus_allowed);
95
96	return ret;
97}
98
99static void us3_set_cpu_divider_index(unsigned int cpu, unsigned int index)
100{
101	unsigned long new_bits, new_freq, reg;
102	cpumask_t cpus_allowed;
103	struct cpufreq_freqs freqs;
104
105	if (!cpu_online(cpu))
106		return;
107
108	cpus_allowed = current->cpus_allowed;
109	set_cpus_allowed_ptr(current, cpumask_of(cpu));
110
111	new_freq = sparc64_get_clock_tick(cpu) / 1000;
112	switch (index) {
113	case 0:
114		new_bits = SAFARI_CFG_DIV_1;
115		new_freq /= 1;
116		break;
117	case 1:
118		new_bits = SAFARI_CFG_DIV_2;
119		new_freq /= 2;
120		break;
121	case 2:
122		new_bits = SAFARI_CFG_DIV_32;
123		new_freq /= 32;
124		break;
125
126	default:
127		BUG();
128	};
129
130	reg = read_safari_cfg();
131
132	freqs.old = get_current_freq(cpu, reg);
133	freqs.new = new_freq;
134	freqs.cpu = cpu;
135	cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
136
137	reg &= ~SAFARI_CFG_DIV_MASK;
138	reg |= new_bits;
139	write_safari_cfg(reg);
140
141	cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
142
143	set_cpus_allowed_ptr(current, &cpus_allowed);
144}
145
146static int us3_freq_target(struct cpufreq_policy *policy,
147			  unsigned int target_freq,
148			  unsigned int relation)
149{
150	unsigned int new_index = 0;
151
152	if (cpufreq_frequency_table_target(policy,
153					   &us3_freq_table[policy->cpu].table[0],
154					   target_freq,
155					   relation,
156					   &new_index))
157		return -EINVAL;
158
159	us3_set_cpu_divider_index(policy->cpu, new_index);
160
161	return 0;
162}
163
164static int us3_freq_verify(struct cpufreq_policy *policy)
165{
166	return cpufreq_frequency_table_verify(policy,
167					      &us3_freq_table[policy->cpu].table[0]);
168}
169
170static int __init us3_freq_cpu_init(struct cpufreq_policy *policy)
171{
172	unsigned int cpu = policy->cpu;
173	unsigned long clock_tick = sparc64_get_clock_tick(cpu) / 1000;
174	struct cpufreq_frequency_table *table =
175		&us3_freq_table[cpu].table[0];
176
177	table[0].index = 0;
178	table[0].frequency = clock_tick / 1;
179	table[1].index = 1;
180	table[1].frequency = clock_tick / 2;
181	table[2].index = 2;
182	table[2].frequency = clock_tick / 32;
183	table[3].index = 0;
184	table[3].frequency = CPUFREQ_TABLE_END;
185
186	policy->cpuinfo.transition_latency = 0;
187	policy->cur = clock_tick;
188
189	return cpufreq_frequency_table_cpuinfo(policy, table);
190}
191
192static int us3_freq_cpu_exit(struct cpufreq_policy *policy)
193{
194	if (cpufreq_us3_driver)
195		us3_set_cpu_divider_index(policy->cpu, 0);
196
197	return 0;
198}
199
200static int __init us3_freq_init(void)
201{
202	unsigned long manuf, impl, ver;
203	int ret;
204
205	if (tlb_type != cheetah && tlb_type != cheetah_plus)
206		return -ENODEV;
207
208	__asm__("rdpr %%ver, %0" : "=r" (ver));
209	manuf = ((ver >> 48) & 0xffff);
210	impl  = ((ver >> 32) & 0xffff);
211
212	if (manuf == CHEETAH_MANUF &&
213	    (impl == CHEETAH_IMPL ||
214	     impl == CHEETAH_PLUS_IMPL ||
215	     impl == JAGUAR_IMPL ||
216	     impl == PANTHER_IMPL)) {
217		struct cpufreq_driver *driver;
218
219		ret = -ENOMEM;
220		driver = kzalloc(sizeof(struct cpufreq_driver), GFP_KERNEL);
221		if (!driver)
222			goto err_out;
223
224		us3_freq_table = kzalloc(
225			(NR_CPUS * sizeof(struct us3_freq_percpu_info)),
226			GFP_KERNEL);
227		if (!us3_freq_table)
228			goto err_out;
229
230		driver->init = us3_freq_cpu_init;
231		driver->verify = us3_freq_verify;
232		driver->target = us3_freq_target;
233		driver->get = us3_freq_get;
234		driver->exit = us3_freq_cpu_exit;
235		driver->owner = THIS_MODULE,
236		strcpy(driver->name, "UltraSPARC-III");
237
238		cpufreq_us3_driver = driver;
239		ret = cpufreq_register_driver(driver);
240		if (ret)
241			goto err_out;
242
243		return 0;
244
245err_out:
246		if (driver) {
247			kfree(driver);
248			cpufreq_us3_driver = NULL;
249		}
250		kfree(us3_freq_table);
251		us3_freq_table = NULL;
252		return ret;
253	}
254
255	return -ENODEV;
256}
257
258static void __exit us3_freq_exit(void)
259{
260	if (cpufreq_us3_driver) {
261		cpufreq_unregister_driver(cpufreq_us3_driver);
262		kfree(cpufreq_us3_driver);
263		cpufreq_us3_driver = NULL;
264		kfree(us3_freq_table);
265		us3_freq_table = NULL;
266	}
267}
268
269MODULE_AUTHOR("David S. Miller <davem@redhat.com>");
270MODULE_DESCRIPTION("cpufreq driver for UltraSPARC-III");
271MODULE_LICENSE("GPL");
272
273module_init(us3_freq_init);
274module_exit(us3_freq_exit);
275