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