2 * linux/arch/arm/mach-p2001/p2001_cpufreq.c
4 * Copyright (C) 2004-2005 Tobias Lorenz
6 * CPU frequency scaling support
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.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 #include <linux/module.h>
22 #include <linux/types.h>
23 #include <linux/kernel.h>
24 #include <linux/cpufreq.h>
25 #include <linux/slab.h>
26 #include <linux/sched.h>
27 #include <linux/smp.h>
28 #include <linux/init.h>
30 #include <asm/hardware.h>
33 static struct cpufreq_driver p2001_cpufreq_driver
;
35 static int p2001_cpufreq_driver_init(struct cpufreq_policy
*policy
);
36 static int p2001_cpufreq_driver_verify(struct cpufreq_policy
*policy
);
37 //static int p2001_cpufreq_driver_setpolicy(struct cpufreq_policy *policy);
38 static int p2001_cpufreq_driver_target(struct cpufreq_policy
*policy
,
39 unsigned int target_freq
,
40 unsigned int relation
);
41 static unsigned int p2001_cpufreq_driver_get(unsigned int cpu
);
43 static struct cpufreq_frequency_table p2001_cpufreq_frequency_table
[] =
45 /* index is also the scaling factor */
47 { .index
= 1, .frequency
= 12288 }, // 12.288 MHz (no network)
48 { .index
= 2, .frequency
= 24576 }, // 24.576 MHz (no network)
49 { .index
= 3, .frequency
= 36864 }, // 36.864 MHz
50 { .index
= 4, .frequency
= 49152 }, // 49.152 MHz
51 { .index
= 5, .frequency
= 61440 }, // 61.440 MHz
52 { .index
= 6, .frequency
= 73728 }, // 73.728 MHz
53 { .index
= 7, .frequency
= 86016 }, // 86.016 MHz (overclocked)
54 { .index
= 8, .frequency
= 98304 }, // 98.304 MHz (overclocked)
55 { .index
= 9, .frequency
= 110592 }, // 110.592 MHz (not working)
56 { .frequency
= CPUFREQ_TABLE_END
},
59 static int p2001_cpufreq_driver_init(struct cpufreq_policy
*policy
)
61 // printk("p2001_cpufreq_driver_init\n");
63 /* set default policy and cpuinfo */
67 policy
->cur
= policy
->min
= policy
->max
= p2001_cpufreq_driver_get(policy
->cpu
);
68 policy
->governor
= CPUFREQ_DEFAULT_GOVERNOR
;
69 policy
->cpuinfo
.max_freq
= 73728; // kHz
70 policy
->cpuinfo
.min_freq
= 36864; // kHz
71 policy
->cpuinfo
.transition_latency
= 1000000; /* 1 ms, assumed */
77 * p2001_cpufreq_driver_verify - verifies a new CPUFreq policy
80 * Limit must be within this model's frequency range at least one
83 static int p2001_cpufreq_driver_verify(struct cpufreq_policy
*policy
)
85 // printk("p2001_cpufreq_driver_verify\n");
86 return cpufreq_frequency_table_verify(policy
, p2001_cpufreq_frequency_table
);
90 * p2001_cpufreq_driver_verify - set a new CPUFreq policy
93 * Sets a new CPUFreq policy.
96 static int p2001_cpufreq_driver_setpolicy(struct cpufreq_policy *policy)
98 // printk("p2001_cpufreq_driver_setpolicy\n");
105 * p2001_cpufreq_driver_target - set a new CPUFreq policy
106 * @policy: new policy
107 * @target_freq: the target frequency
108 * @relation: how that frequency relates to achieved frequency (CPUFREQ_RELATION_L or CPUFREQ_RELATION_H)
110 * Sets a new CPUFreq policy.
112 static int p2001_cpufreq_driver_target(struct cpufreq_policy
*policy
,
113 unsigned int target_freq
,
114 unsigned int relation
)
116 unsigned int newstate
= 0;
117 struct cpufreq_freqs freqs
;
118 unsigned int M
, P
, S
, N
, PWRDN
; // PLL_12288_config
119 unsigned int M_DIV
, N_DIV
, SEL_PLL
, SEL_DIV
; // DIV_12288_config
122 // printk("p2001_cpufreq_driver_target(target_freq=%d)\n", target_freq);
123 if (cpufreq_frequency_table_target(policy
, &p2001_cpufreq_frequency_table
[0], target_freq
, relation
, &newstate
))
126 freqs
.old
= p2001_cpufreq_driver_get(policy
->cpu
);
127 freqs
.new = p2001_cpufreq_frequency_table
[newstate
].frequency
;
128 freqs
.cpu
= 0; /* p2001_cpufreq.c is UP only driver */
130 if (freqs
.new == freqs
.old
)
132 // printk("System clock change from %d kHz to %d kHz\n", freqs.old, freqs.new);
143 switch (p2001_cpufreq_frequency_table
[newstate
].index
) {
170 case 9: // 110592 kHz
176 cpufreq_notify_transition(&freqs
, CPUFREQ_PRECHANGE
);
178 /* change DIV first to bypass PLL before PWRDN */
179 config
= (M_DIV
<<0) | (N_DIV
<<8) | (SEL_PLL
<<16) | (SEL_DIV
<<17);
180 P2001_TIMER
->DIV_12288_config
= config
;
181 config
= (M
<<0) | (P
<<8) | (S
<<14) | (N
<<16) | (PWRDN
<<26);
182 P2001_TIMER
->PLL_12288_config
= config
;
185 cpufreq_notify_transition(&freqs
, CPUFREQ_POSTCHANGE
);
191 * returns current frequency in kHz
193 static unsigned int p2001_cpufreq_driver_get(unsigned int cpu
)
195 cpumask_t cpus_allowed
;
196 unsigned int current_freq
;
197 unsigned int M
, P
, S
, N
, PWRDN
; // PLL_12288_config
198 unsigned int M_DIV
, N_DIV
, SEL_PLL
, SEL_DIV
; // DIV_12288_config
200 // printk("p2001_cpufreq_driver_get\n");
202 * Save this threads cpus_allowed mask.
204 cpus_allowed
= current
->cpus_allowed
;
207 * Bind to the specified CPU. When this call returns,
208 * we should be running on the right CPU.
210 set_cpus_allowed(current
, cpumask_of_cpu(cpu
));
211 BUG_ON(cpu
!= smp_processor_id());
213 /* get current setting */
214 M
= (P2001_TIMER
->PLL_12288_config
>> 0) & 0x00ff;
215 P
= (P2001_TIMER
->PLL_12288_config
>> 8) & 0x003f;
216 S
= (P2001_TIMER
->PLL_12288_config
>> 14) & 0x0003;
217 N
= (P2001_TIMER
->PLL_12288_config
>> 16) & 0x03ff;
218 PWRDN
= (P2001_TIMER
->PLL_12288_config
>> 26) & 0x0001;
219 M_DIV
= (P2001_TIMER
->DIV_12288_config
>> 0) & 0x00ff;
220 N_DIV
= (P2001_TIMER
->DIV_12288_config
>> 8) & 0x00ff;
221 SEL_PLL
= (P2001_TIMER
->DIV_12288_config
>> 16) & 0x0001;
222 SEL_DIV
= (P2001_TIMER
->DIV_12288_config
>> 17) & 0x0001;
223 // printk("M=%d P=%d S=%d N=%d PWRDN=%d\n", M, P, S, N, PWRDN);
224 // printk("M_DIV=%d N_DIV=%d SEL_PLL=%d SEL_DIV=%d\n", M_DIV, N_DIV, SEL_PLL, SEL_DIV);
226 current_freq
= 12288; // External 12.288 MHz oscillator
227 // printk("cpufreq after OSC: %d\n", current_freq);
233 /* WARNING: 2^S=2 for S=0 (CodeSourcery ARM Q1A 2004) */
234 // current_freq *= PWRDN ? 0 : ((M+8) / ( (2^S) * (P+2) )); // correct
235 current_freq
*= PWRDN
? 0 : ((M
+8) / (P
+2)); // working
241 // printk("cpufreq after PLL: %d\n", current_freq);
247 current_freq
/= (2*(N
+1));
250 // printk("cpufreq after DIV: %d\n", current_freq);
253 * Restore the CPUs allowed mask.
255 set_cpus_allowed(current
, cpus_allowed
);
260 static struct cpufreq_driver p2001_cpufreq_driver
= {
261 .name
= "P2001 cpufreq",
262 .init
= p2001_cpufreq_driver_init
,
263 .verify
= p2001_cpufreq_driver_verify
,
264 // .setpolicy = p2001_cpufreq_driver_setpolicy,
265 .target
= p2001_cpufreq_driver_target
,
266 .get
= p2001_cpufreq_driver_get
,
269 static int __init
p2001_cpufreq_module_init(void)
271 // printk("p2001_cpufreq_module_init\n");
272 return cpufreq_register_driver(&p2001_cpufreq_driver
);
275 static void __exit
p2001_cpufreq_module_exit(void)
277 // printk("p2001_cpufreq_module_exit\n");
278 cpufreq_unregister_driver(&p2001_cpufreq_driver
);
281 module_init(p2001_cpufreq_module_init
);
282 module_exit(p2001_cpufreq_module_exit
);
284 MODULE_AUTHOR("Tobias Lorenz");
285 MODULE_DESCRIPTION("P2001 cpu frequency scaling driver");
286 MODULE_LICENSE("GPL");