]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * arch/sh/kernel/cpufreq.c | |
3 | * | |
4 | * cpufreq driver for the SuperH processors. | |
5 | * | |
6 | * Copyright (C) 2002, 2003, 2004, 2005 Paul Mundt | |
7 | * Copyright (C) 2002 M. R. Brown | |
8 | * | |
9 | * This program is free software; you can redistribute it and/or modify it | |
10 | * under the terms of the GNU General Public License as published by the | |
11 | * Free Software Foundation; either version 2 of the License, or (at your | |
12 | * option) any later version. | |
13 | */ | |
14 | #include <linux/types.h> | |
15 | #include <linux/cpufreq.h> | |
16 | #include <linux/kernel.h> | |
17 | #include <linux/module.h> | |
18 | #include <linux/slab.h> | |
19 | #include <linux/init.h> | |
20 | #include <linux/delay.h> | |
21 | #include <linux/cpumask.h> | |
22 | #include <linux/smp.h> | |
23 | ||
24 | #include <asm/processor.h> | |
25 | #include <asm/watchdog.h> | |
26 | #include <asm/freq.h> | |
27 | #include <asm/io.h> | |
28 | ||
29 | /* | |
30 | * For SuperH, each policy change requires that we change the IFC, BFC, and | |
31 | * PFC at the same time. Here we define sane values that won't trash the | |
32 | * system. | |
33 | * | |
34 | * Note the max set is computed at runtime, we use the divisors that we booted | |
35 | * with to setup our maximum operating frequencies. | |
36 | */ | |
37 | struct clock_set { | |
38 | unsigned int ifc; | |
39 | unsigned int bfc; | |
40 | unsigned int pfc; | |
41 | } clock_sets[] = { | |
42 | #if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH2) | |
43 | { 0, 0, 0 }, /* not implemented yet */ | |
44 | #elif defined(CONFIG_CPU_SH4) | |
45 | { 4, 8, 8 }, /* min - IFC: 1/4, BFC: 1/8, PFC: 1/8 */ | |
46 | { 1, 2, 2 }, /* max - IFC: 1, BFC: 1/2, PFC: 1/2 */ | |
47 | #endif | |
48 | }; | |
49 | ||
50 | #define MIN_CLOCK_SET 0 | |
51 | #define MAX_CLOCK_SET (ARRAY_SIZE(clock_sets) - 1) | |
52 | ||
53 | /* | |
54 | * For the time being, we only support two frequencies, which in turn are | |
55 | * aimed at the POWERSAVE and PERFORMANCE policies, which in turn are derived | |
56 | * directly from the respective min/max clock sets. Technically we could | |
57 | * support a wider range of frequencies, but these vary far too much for each | |
58 | * CPU subtype (and we'd have to construct a frequency table for each subtype). | |
59 | * | |
60 | * Maybe something to implement in the future.. | |
61 | */ | |
62 | #define SH_FREQ_MAX 0 | |
63 | #define SH_FREQ_MIN 1 | |
64 | ||
65 | static struct cpufreq_frequency_table sh_freqs[] = { | |
66 | { SH_FREQ_MAX, 0 }, | |
67 | { SH_FREQ_MIN, 0 }, | |
68 | { 0, CPUFREQ_TABLE_END }, | |
69 | }; | |
70 | ||
71 | static void sh_cpufreq_update_clocks(unsigned int set) | |
72 | { | |
73 | current_cpu_data.cpu_clock = current_cpu_data.master_clock / clock_sets[set].ifc; | |
74 | current_cpu_data.bus_clock = current_cpu_data.master_clock / clock_sets[set].bfc; | |
75 | current_cpu_data.module_clock = current_cpu_data.master_clock / clock_sets[set].pfc; | |
76 | current_cpu_data.loops_per_jiffy = loops_per_jiffy; | |
77 | } | |
78 | ||
79 | /* XXX: This needs to be split out per CPU and CPU subtype. */ | |
80 | /* | |
81 | * Here we notify other drivers of the proposed change and the final change. | |
82 | */ | |
83 | static int sh_cpufreq_setstate(unsigned int cpu, unsigned int set) | |
84 | { | |
85 | unsigned short frqcr = ctrl_inw(FRQCR); | |
86 | cpumask_t cpus_allowed; | |
87 | struct cpufreq_freqs freqs; | |
88 | ||
89 | if (!cpu_online(cpu)) | |
90 | return -ENODEV; | |
91 | ||
92 | cpus_allowed = current->cpus_allowed; | |
93 | set_cpus_allowed(current, cpumask_of_cpu(cpu)); | |
94 | ||
95 | BUG_ON(smp_processor_id() != cpu); | |
96 | ||
97 | freqs.cpu = cpu; | |
98 | freqs.old = current_cpu_data.cpu_clock / 1000; | |
99 | freqs.new = (current_cpu_data.master_clock / clock_sets[set].ifc) / 1000; | |
100 | ||
101 | cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); | |
102 | #if defined(CONFIG_CPU_SH3) | |
103 | frqcr |= (newstate & 0x4000) << 14; | |
104 | frqcr |= (newstate & 0x000c) << 2; | |
105 | #elif defined(CONFIG_CPU_SH4) | |
106 | /* | |
107 | * FRQCR.PLL2EN is 1, we need to allow the PLL to stabilize by | |
108 | * initializing the WDT. | |
109 | */ | |
110 | if (frqcr & (1 << 9)) { | |
111 | __u8 csr; | |
112 | ||
113 | /* | |
114 | * Set the overflow period to the highest available, | |
115 | * in this case a 1/4096 division ratio yields a 5.25ms | |
116 | * overflow period. See asm-sh/watchdog.h for more | |
117 | * information and a range of other divisors. | |
118 | */ | |
119 | csr = sh_wdt_read_csr(); | |
120 | csr |= WTCSR_CKS_4096; | |
121 | sh_wdt_write_csr(csr); | |
122 | ||
123 | sh_wdt_write_cnt(0); | |
124 | } | |
125 | frqcr &= 0x0e00; /* Clear ifc, bfc, pfc */ | |
126 | frqcr |= get_ifc_value(clock_sets[set].ifc) << 6; | |
127 | frqcr |= get_bfc_value(clock_sets[set].bfc) << 3; | |
128 | frqcr |= get_pfc_value(clock_sets[set].pfc); | |
129 | #endif | |
130 | ctrl_outw(frqcr, FRQCR); | |
131 | sh_cpufreq_update_clocks(set); | |
132 | ||
133 | set_cpus_allowed(current, cpus_allowed); | |
134 | cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); | |
135 | ||
136 | return 0; | |
137 | } | |
138 | ||
139 | static int sh_cpufreq_cpu_init(struct cpufreq_policy *policy) | |
140 | { | |
141 | unsigned int min_freq, max_freq; | |
142 | unsigned int ifc, bfc, pfc; | |
143 | ||
144 | if (!cpu_online(policy->cpu)) | |
145 | return -ENODEV; | |
146 | ||
147 | /* Update our maximum clock set */ | |
148 | get_current_frequency_divisors(&ifc, &bfc, &pfc); | |
149 | clock_sets[MAX_CLOCK_SET].ifc = ifc; | |
150 | clock_sets[MAX_CLOCK_SET].bfc = bfc; | |
151 | clock_sets[MAX_CLOCK_SET].pfc = pfc; | |
152 | ||
153 | /* Convert from Hz to kHz */ | |
154 | max_freq = current_cpu_data.cpu_clock / 1000; | |
155 | min_freq = (current_cpu_data.master_clock / clock_sets[MIN_CLOCK_SET].ifc) / 1000; | |
156 | ||
157 | sh_freqs[SH_FREQ_MAX].frequency = max_freq; | |
158 | sh_freqs[SH_FREQ_MIN].frequency = min_freq; | |
159 | ||
160 | /* cpuinfo and default policy values */ | |
161 | policy->governor = CPUFREQ_DEFAULT_GOVERNOR; | |
162 | policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL; | |
163 | policy->cur = max_freq; | |
164 | ||
165 | return cpufreq_frequency_table_cpuinfo(policy, &sh_freqs[0]); | |
166 | } | |
167 | ||
168 | static int sh_cpufreq_verify(struct cpufreq_policy *policy) | |
169 | { | |
170 | return cpufreq_frequency_table_verify(policy, &sh_freqs[0]); | |
171 | } | |
172 | ||
173 | static int sh_cpufreq_target(struct cpufreq_policy *policy, | |
174 | unsigned int target_freq, | |
175 | unsigned int relation) | |
176 | { | |
177 | unsigned int set, idx = 0; | |
178 | ||
179 | if (cpufreq_frequency_table_target(policy, &sh_freqs[0], target_freq, relation, &idx)) | |
180 | return -EINVAL; | |
181 | ||
182 | set = (idx == SH_FREQ_MIN) ? MIN_CLOCK_SET : MAX_CLOCK_SET; | |
183 | ||
184 | sh_cpufreq_setstate(policy->cpu, set); | |
185 | ||
186 | return 0; | |
187 | } | |
188 | ||
189 | static struct cpufreq_driver sh_cpufreq_driver = { | |
190 | .owner = THIS_MODULE, | |
191 | .name = "SH cpufreq", | |
192 | .init = sh_cpufreq_cpu_init, | |
193 | .verify = sh_cpufreq_verify, | |
194 | .target = sh_cpufreq_target, | |
195 | }; | |
196 | ||
197 | static int __init sh_cpufreq_init(void) | |
198 | { | |
199 | if (!current_cpu_data.cpu_clock) | |
200 | return -EINVAL; | |
201 | if (cpufreq_register_driver(&sh_cpufreq_driver)) | |
202 | return -EINVAL; | |
203 | ||
204 | return 0; | |
205 | } | |
206 | ||
207 | static void __exit sh_cpufreq_exit(void) | |
208 | { | |
209 | cpufreq_unregister_driver(&sh_cpufreq_driver); | |
210 | } | |
211 | ||
212 | module_init(sh_cpufreq_init); | |
213 | module_exit(sh_cpufreq_exit); | |
214 | ||
215 | MODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>"); | |
216 | MODULE_DESCRIPTION("cpufreq driver for SuperH"); | |
217 | MODULE_LICENSE("GPL"); | |
218 |