Linux Audio

Check our new training course

Linux kernel drivers training

Mar 31-Apr 9, 2025, special US time zones
Register
Loading...
Note: File does not exist in v4.17.
  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	cpumask_copy(&cpus_allowed, tsk_cpus_allowed(current));
 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	cpumask_copy(&cpus_allowed, tsk_cpus_allowed(current));
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);