1/*
2 *  linux/arch/arm/mach-integrator/cpu.c
3 *
4 *  Copyright (C) 2001-2002 Deep Blue Solutions Ltd.
5 *
6 *  $Id: cpu.c,v 1.1.1.1 2007/08/03 18:51:36 Exp $
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 *
12 * CPU support functions
13 */
14#include <linux/module.h>
15#include <linux/types.h>
16#include <linux/kernel.h>
17#include <linux/cpufreq.h>
18#include <linux/slab.h>
19#include <linux/sched.h>
20#include <linux/smp.h>
21#include <linux/init.h>
22
23#include <asm/hardware.h>
24#include <asm/io.h>
25#include <asm/mach-types.h>
26#include <asm/hardware/icst525.h>
27
28static struct cpufreq_driver integrator_driver;
29
30#define CM_ID  	(IO_ADDRESS(INTEGRATOR_HDR_BASE)+INTEGRATOR_HDR_ID_OFFSET)
31#define CM_OSC	(IO_ADDRESS(INTEGRATOR_HDR_BASE)+INTEGRATOR_HDR_OSC_OFFSET)
32#define CM_STAT (IO_ADDRESS(INTEGRATOR_HDR_BASE)+INTEGRATOR_HDR_STAT_OFFSET)
33#define CM_LOCK (IO_ADDRESS(INTEGRATOR_HDR_BASE)+INTEGRATOR_HDR_LOCK_OFFSET)
34
35static const struct icst525_params lclk_params = {
36	.ref		= 24000,
37	.vco_max	= 320000,
38	.vd_min		= 8,
39	.vd_max		= 132,
40	.rd_min		= 24,
41	.rd_max		= 24,
42};
43
44static const struct icst525_params cclk_params = {
45	.ref		= 24000,
46	.vco_max	= 320000,
47	.vd_min		= 12,
48	.vd_max		= 160,
49	.rd_min		= 24,
50	.rd_max		= 24,
51};
52
53/*
54 * Validate the speed policy.
55 */
56static int integrator_verify_policy(struct cpufreq_policy *policy)
57{
58	struct icst525_vco vco;
59
60	cpufreq_verify_within_limits(policy,
61				     policy->cpuinfo.min_freq,
62				     policy->cpuinfo.max_freq);
63
64	vco = icst525_khz_to_vco(&cclk_params, policy->max);
65	policy->max = icst525_khz(&cclk_params, vco);
66
67	vco = icst525_khz_to_vco(&cclk_params, policy->min);
68	policy->min = icst525_khz(&cclk_params, vco);
69
70	cpufreq_verify_within_limits(policy,
71				     policy->cpuinfo.min_freq,
72				     policy->cpuinfo.max_freq);
73
74	return 0;
75}
76
77
78static int integrator_set_target(struct cpufreq_policy *policy,
79				 unsigned int target_freq,
80				 unsigned int relation)
81{
82	cpumask_t cpus_allowed;
83	int cpu = policy->cpu;
84	struct icst525_vco vco;
85	struct cpufreq_freqs freqs;
86	u_int cm_osc;
87
88	/*
89	 * Save this threads cpus_allowed mask.
90	 */
91	cpus_allowed = current->cpus_allowed;
92
93	/*
94	 * Bind to the specified CPU.  When this call returns,
95	 * we should be running on the right CPU.
96	 */
97	set_cpus_allowed(current, cpumask_of_cpu(cpu));
98	BUG_ON(cpu != smp_processor_id());
99
100	/* get current setting */
101	cm_osc = __raw_readl(CM_OSC);
102
103	if (machine_is_integrator()) {
104		vco.s = (cm_osc >> 8) & 7;
105	} else if (machine_is_cintegrator()) {
106		vco.s = 1;
107	}
108	vco.v = cm_osc & 255;
109	vco.r = 22;
110	freqs.old = icst525_khz(&cclk_params, vco);
111
112	/* icst525_khz_to_vco rounds down -- so we need the next
113	 * larger freq in case of CPUFREQ_RELATION_L.
114	 */
115	if (relation == CPUFREQ_RELATION_L)
116		target_freq += 999;
117	if (target_freq > policy->max)
118		target_freq = policy->max;
119	vco = icst525_khz_to_vco(&cclk_params, target_freq);
120	freqs.new = icst525_khz(&cclk_params, vco);
121
122	freqs.cpu = policy->cpu;
123
124	if (freqs.old == freqs.new) {
125		set_cpus_allowed(current, cpus_allowed);
126		return 0;
127	}
128
129	cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
130
131	cm_osc = __raw_readl(CM_OSC);
132
133	if (machine_is_integrator()) {
134		cm_osc &= 0xfffff800;
135		cm_osc |= vco.s << 8;
136	} else if (machine_is_cintegrator()) {
137		cm_osc &= 0xffffff00;
138	}
139	cm_osc |= vco.v;
140
141	__raw_writel(0xa05f, CM_LOCK);
142	__raw_writel(cm_osc, CM_OSC);
143	__raw_writel(0, CM_LOCK);
144
145	/*
146	 * Restore the CPUs allowed mask.
147	 */
148	set_cpus_allowed(current, cpus_allowed);
149
150	cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
151
152	return 0;
153}
154
155static unsigned int integrator_get(unsigned int cpu)
156{
157	cpumask_t cpus_allowed;
158	unsigned int current_freq;
159	u_int cm_osc;
160	struct icst525_vco vco;
161
162	cpus_allowed = current->cpus_allowed;
163
164	set_cpus_allowed(current, cpumask_of_cpu(cpu));
165	BUG_ON(cpu != smp_processor_id());
166
167	/* detect memory etc. */
168	cm_osc = __raw_readl(CM_OSC);
169
170	if (machine_is_integrator()) {
171		vco.s = (cm_osc >> 8) & 7;
172	} else if (machine_is_cintegrator()) {
173		vco.s = 1;
174	}
175	vco.v = cm_osc & 255;
176	vco.r = 22;
177
178	current_freq = icst525_khz(&cclk_params, vco); /* current freq */
179
180	set_cpus_allowed(current, cpus_allowed);
181
182	return current_freq;
183}
184
185static int integrator_cpufreq_init(struct cpufreq_policy *policy)
186{
187
188	/* set default policy and cpuinfo */
189	policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
190	policy->cpuinfo.max_freq = 160000;
191	policy->cpuinfo.min_freq = 12000;
192	policy->cpuinfo.transition_latency = 1000000; /* 1 ms, assumed */
193	policy->cur = policy->min = policy->max = integrator_get(policy->cpu);
194
195	return 0;
196}
197
198static struct cpufreq_driver integrator_driver = {
199	.verify		= integrator_verify_policy,
200	.target		= integrator_set_target,
201	.get		= integrator_get,
202	.init		= integrator_cpufreq_init,
203	.name		= "integrator",
204};
205
206static int __init integrator_cpu_init(void)
207{
208	return cpufreq_register_driver(&integrator_driver);
209}
210
211static void __exit integrator_cpu_exit(void)
212{
213	cpufreq_unregister_driver(&integrator_driver);
214}
215
216MODULE_AUTHOR ("Russell M. King");
217MODULE_DESCRIPTION ("cpufreq driver for ARM Integrator CPUs");
218MODULE_LICENSE ("GPL");
219
220module_init(integrator_cpu_init);
221module_exit(integrator_cpu_exit);
222