diff options
Diffstat (limited to 'arch/sh')
-rw-r--r-- | arch/sh/Kconfig | 2 | ||||
-rw-r--r-- | arch/sh/kernel/cpu/sh4a/clock-sh7722.c | 796 |
2 files changed, 138 insertions, 660 deletions
diff --git a/arch/sh/Kconfig b/arch/sh/Kconfig index dbc10ff495a9..b5629cdc37a0 100644 --- a/arch/sh/Kconfig +++ b/arch/sh/Kconfig | |||
@@ -516,7 +516,7 @@ config SH_CLK_CPG | |||
516 | 516 | ||
517 | config SH_CLK_CPG_LEGACY | 517 | config SH_CLK_CPG_LEGACY |
518 | depends on SH_CLK_CPG | 518 | depends on SH_CLK_CPG |
519 | def_bool y if !CPU_SUBTYPE_SH7785 && !CPU_SUBTYPE_SH7723 && !CPU_SUBTYPE_SH7724 && !CPU_SUBTYPE_SH7343 && !CPU_SUBTYPE_SH7366 | 519 | def_bool y if !CPU_SUBTYPE_SH7785 && !ARCH_SHMOBILE |
520 | 520 | ||
521 | config SH_CLK_MD | 521 | config SH_CLK_MD |
522 | int "CPU Mode Pin Setting" | 522 | int "CPU Mode Pin Setting" |
diff --git a/arch/sh/kernel/cpu/sh4a/clock-sh7722.c b/arch/sh/kernel/cpu/sh4a/clock-sh7722.c index 6ca7c745580d..40f859354f79 100644 --- a/arch/sh/kernel/cpu/sh4a/clock-sh7722.c +++ b/arch/sh/kernel/cpu/sh4a/clock-sh7722.c | |||
@@ -1,719 +1,197 @@ | |||
1 | /* | 1 | /* |
2 | * arch/sh/kernel/cpu/sh4a/clock-sh7722.c | 2 | * arch/sh/kernel/cpu/sh4a/clock-sh7722.c |
3 | * | 3 | * |
4 | * SH7722 support for the clock framework | 4 | * SH7722 clock framework support |
5 | * | 5 | * |
6 | * Copyright (c) 2006-2007 Nomad Global Solutions Inc | 6 | * Copyright (C) 2009 Magnus Damm |
7 | * Based on code for sh7343 by Paul Mundt | ||
8 | * | 7 | * |
9 | * This file is subject to the terms and conditions of the GNU General Public | 8 | * This program is free software; you can redistribute it and/or modify |
10 | * License. See the file "COPYING" in the main directory of this archive | 9 | * it under the terms of the GNU General Public License as published by |
11 | * for more details. | 10 | * the Free Software Foundation; either version 2 of the License |
11 | * | ||
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. | ||
16 | * | ||
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 | ||
12 | */ | 20 | */ |
13 | #include <linux/init.h> | 21 | #include <linux/init.h> |
14 | #include <linux/kernel.h> | 22 | #include <linux/kernel.h> |
15 | #include <linux/io.h> | 23 | #include <linux/io.h> |
16 | #include <linux/errno.h> | ||
17 | #include <linux/stringify.h> | ||
18 | #include <asm/clock.h> | 24 | #include <asm/clock.h> |
19 | #include <asm/freq.h> | ||
20 | |||
21 | #define N (-1) | ||
22 | #define NM (-2) | ||
23 | #define ROUND_NEAREST 0 | ||
24 | #define ROUND_DOWN -1 | ||
25 | #define ROUND_UP +1 | ||
26 | |||
27 | static int adjust_algos[][3] = { | ||
28 | {}, /* NO_CHANGE */ | ||
29 | { NM, N, 1 }, /* N:1, N:1 */ | ||
30 | { 3, 2, 2 }, /* 3:2:2 */ | ||
31 | { 5, 2, 2 }, /* 5:2:2 */ | ||
32 | { N, 1, 1 }, /* N:1:1 */ | ||
33 | |||
34 | { N, 1 }, /* N:1 */ | ||
35 | 25 | ||
36 | { N, 1 }, /* N:1 */ | 26 | /* SH7722 registers */ |
37 | { 3, 2 }, | 27 | #define FRQCR 0xa4150000 |
38 | { 4, 3 }, | 28 | #define VCLKCR 0xa4150004 |
39 | { 5, 4 }, | 29 | #define SCLKACR 0xa4150008 |
40 | 30 | #define SCLKBCR 0xa415000c | |
41 | { N, 1 } | 31 | #define IRDACLKCR 0xa4150018 |
32 | #define PLLCR 0xa4150024 | ||
33 | #define MSTPCR0 0xa4150030 | ||
34 | #define MSTPCR1 0xa4150034 | ||
35 | #define MSTPCR2 0xa4150038 | ||
36 | #define DLLFRQ 0xa4150050 | ||
37 | |||
38 | /* Fixed 32 KHz root clock for RTC and Power Management purposes */ | ||
39 | static struct clk r_clk = { | ||
40 | .name = "rclk", | ||
41 | .id = -1, | ||
42 | .rate = 32768, | ||
42 | }; | 43 | }; |
43 | 44 | ||
44 | static unsigned long adjust_pair_of_clocks(unsigned long r1, unsigned long r2, | ||
45 | int m1, int m2, int round_flag) | ||
46 | { | ||
47 | unsigned long rem, div; | ||
48 | int the_one = 0; | ||
49 | |||
50 | pr_debug( "Actual values: r1 = %ld\n", r1); | ||
51 | pr_debug( "...............r2 = %ld\n", r2); | ||
52 | |||
53 | if (m1 == m2) { | ||
54 | r2 = r1; | ||
55 | pr_debug( "setting equal rates: r2 now %ld\n", r2); | ||
56 | } else if ((m2 == N && m1 == 1) || | ||
57 | (m2 == NM && m1 == N)) { /* N:1 or NM:N */ | ||
58 | pr_debug( "Setting rates as 1:N (N:N*M)\n"); | ||
59 | rem = r2 % r1; | ||
60 | pr_debug( "...remainder = %ld\n", rem); | ||
61 | if (rem) { | ||
62 | div = r2 / r1; | ||
63 | pr_debug( "...div = %ld\n", div); | ||
64 | switch (round_flag) { | ||
65 | case ROUND_NEAREST: | ||
66 | the_one = rem >= r1/2 ? 1 : 0; break; | ||
67 | case ROUND_UP: | ||
68 | the_one = 1; break; | ||
69 | case ROUND_DOWN: | ||
70 | the_one = 0; break; | ||
71 | } | ||
72 | |||
73 | r2 = r1 * (div + the_one); | ||
74 | pr_debug( "...setting r2 to %ld\n", r2); | ||
75 | } | ||
76 | } else if ((m2 == 1 && m1 == N) || | ||
77 | (m2 == N && m1 == NM)) { /* 1:N or N:NM */ | ||
78 | pr_debug( "Setting rates as N:1 (N*M:N)\n"); | ||
79 | rem = r1 % r2; | ||
80 | pr_debug( "...remainder = %ld\n", rem); | ||
81 | if (rem) { | ||
82 | div = r1 / r2; | ||
83 | pr_debug( "...div = %ld\n", div); | ||
84 | switch (round_flag) { | ||
85 | case ROUND_NEAREST: | ||
86 | the_one = rem > r2/2 ? 1 : 0; break; | ||
87 | case ROUND_UP: | ||
88 | the_one = 0; break; | ||
89 | case ROUND_DOWN: | ||
90 | the_one = 1; break; | ||
91 | } | ||
92 | |||
93 | r2 = r1 / (div + the_one); | ||
94 | pr_debug( "...setting r2 to %ld\n", r2); | ||
95 | } | ||
96 | } else { /* value:value */ | ||
97 | pr_debug( "Setting rates as %d:%d\n", m1, m2); | ||
98 | div = r1 / m1; | ||
99 | r2 = div * m2; | ||
100 | pr_debug( "...div = %ld\n", div); | ||
101 | pr_debug( "...setting r2 to %ld\n", r2); | ||
102 | } | ||
103 | |||
104 | return r2; | ||
105 | } | ||
106 | |||
107 | static void adjust_clocks(int originate, int *l, unsigned long v[], | ||
108 | int n_in_line) | ||
109 | { | ||
110 | int x; | ||
111 | |||
112 | pr_debug( "Go down from %d...\n", originate); | ||
113 | /* go up recalculation clocks */ | ||
114 | for (x = originate; x>0; x -- ) | ||
115 | v[x-1] = adjust_pair_of_clocks(v[x], v[x-1], | ||
116 | l[x], l[x-1], | ||
117 | ROUND_UP); | ||
118 | |||
119 | pr_debug( "Go up from %d...\n", originate); | ||
120 | /* go down recalculation clocks */ | ||
121 | for (x = originate; x<n_in_line - 1; x ++ ) | ||
122 | v[x+1] = adjust_pair_of_clocks(v[x], v[x+1], | ||
123 | l[x], l[x+1], | ||
124 | ROUND_UP); | ||
125 | } | ||
126 | |||
127 | |||
128 | /* | 45 | /* |
129 | * SH7722 uses a common set of multipliers and divisors, so this | 46 | * Default rate for the root input clock, reset this with clk_set_rate() |
130 | * is quite simple.. | 47 | * from the platform code. |
131 | */ | 48 | */ |
132 | 49 | struct clk extal_clk = { | |
133 | #define STCPLL(frqcr) (((frqcr >> 24) & 0x1f) + 1) | 50 | .name = "extal", |
134 | 51 | .id = -1, | |
135 | /* | 52 | .rate = 33333333, |
136 | * Instead of having two separate multipliers/divisors set, like this: | ||
137 | * | ||
138 | * static int multipliers[] = { 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; | ||
139 | * static int divisors[] = { 1, 3, 2, 5, 3, 4, 5, 6, 8, 10, 12, 16, 20 }; | ||
140 | * | ||
141 | * I created the divisors2 array, which is used to calculate rate like | ||
142 | * rate = parent * 2 / divisors2[ divisor ]; | ||
143 | */ | ||
144 | static int divisors2[] = { 2, 3, 4, 5, 6, 8, 10, 12, 16, 20, 24, 32, 40 }; | ||
145 | |||
146 | static unsigned long master_clk_recalc(struct clk *clk) | ||
147 | { | ||
148 | unsigned frqcr = ctrl_inl(FRQCR); | ||
149 | |||
150 | return CONFIG_SH_PCLK_FREQ * STCPLL(frqcr); | ||
151 | } | ||
152 | |||
153 | static void master_clk_init(struct clk *clk) | ||
154 | { | ||
155 | clk->parent = NULL; | ||
156 | clk->rate = master_clk_recalc(clk); | ||
157 | } | ||
158 | |||
159 | static unsigned long module_clk_recalc(struct clk *clk) | ||
160 | { | ||
161 | unsigned long frqcr = ctrl_inl(FRQCR); | ||
162 | |||
163 | return clk->parent->rate / STCPLL(frqcr); | ||
164 | } | ||
165 | |||
166 | #define MASTERDIVS { 2, 3, 4, 6, 8, 16 } | ||
167 | #define STCMASK 0x1f | ||
168 | #define DIVCALC(div) (div-1) | ||
169 | #define FRQCRKICK 0x00000000 | ||
170 | |||
171 | static int master_clk_setrate(struct clk *clk, unsigned long rate, int id) | ||
172 | { | ||
173 | int div = rate / clk->rate; | ||
174 | int master_divs[] = MASTERDIVS; | ||
175 | int index; | ||
176 | unsigned long frqcr; | ||
177 | |||
178 | for (index = 1; index < ARRAY_SIZE(master_divs); index++) | ||
179 | if (div >= master_divs[index - 1] && div < master_divs[index]) | ||
180 | break; | ||
181 | |||
182 | if (index >= ARRAY_SIZE(master_divs)) | ||
183 | index = ARRAY_SIZE(master_divs); | ||
184 | div = master_divs[index - 1]; | ||
185 | |||
186 | frqcr = ctrl_inl(FRQCR); | ||
187 | frqcr &= ~(STCMASK << 24); | ||
188 | frqcr |= (DIVCALC(div) << 24); | ||
189 | frqcr |= FRQCRKICK; | ||
190 | ctrl_outl(frqcr, FRQCR); | ||
191 | |||
192 | return 0; | ||
193 | } | ||
194 | |||
195 | static struct clk_ops sh7722_master_clk_ops = { | ||
196 | .init = master_clk_init, | ||
197 | .recalc = master_clk_recalc, | ||
198 | .set_rate = master_clk_setrate, | ||
199 | }; | ||
200 | |||
201 | static struct clk_ops sh7722_module_clk_ops = { | ||
202 | .recalc = module_clk_recalc, | ||
203 | }; | ||
204 | |||
205 | struct frqcr_context { | ||
206 | unsigned mask; | ||
207 | unsigned shift; | ||
208 | }; | ||
209 | |||
210 | struct frqcr_context sh7722_get_clk_context(const char *name) | ||
211 | { | ||
212 | struct frqcr_context ctx = { 0, }; | ||
213 | |||
214 | if (!strcmp(name, "peripheral_clk")) { | ||
215 | ctx.shift = 0; | ||
216 | ctx.mask = 0xF; | ||
217 | } else if (!strcmp(name, "sdram_clk")) { | ||
218 | ctx.shift = 4; | ||
219 | ctx.mask = 0xF; | ||
220 | } else if (!strcmp(name, "bus_clk")) { | ||
221 | ctx.shift = 8; | ||
222 | ctx.mask = 0xF; | ||
223 | } else if (!strcmp(name, "sh_clk")) { | ||
224 | ctx.shift = 12; | ||
225 | ctx.mask = 0xF; | ||
226 | } else if (!strcmp(name, "umem_clk")) { | ||
227 | ctx.shift = 16; | ||
228 | ctx.mask = 0xF; | ||
229 | } else if (!strcmp(name, "cpu_clk")) { | ||
230 | ctx.shift = 20; | ||
231 | ctx.mask = 7; | ||
232 | } | ||
233 | return ctx; | ||
234 | } | ||
235 | |||
236 | /** | ||
237 | * sh7722_find_div_index - find divisor for setting rate | ||
238 | * | ||
239 | * All sh7722 clocks use the same set of multipliers/divisors. This function | ||
240 | * chooses correct divisor to set the rate of clock with parent clock that | ||
241 | * generates frequency of 'parent_rate' | ||
242 | * | ||
243 | * @parent_rate: rate of parent clock | ||
244 | * @rate: requested rate to be set | ||
245 | */ | ||
246 | static int sh7722_find_div_index(unsigned long parent_rate, unsigned rate) | ||
247 | { | ||
248 | unsigned div2 = parent_rate * 2 / rate; | ||
249 | int index; | ||
250 | |||
251 | if (rate > parent_rate) | ||
252 | return -EINVAL; | ||
253 | |||
254 | for (index = 1; index < ARRAY_SIZE(divisors2); index++) { | ||
255 | if (div2 > divisors2[index - 1] && div2 <= divisors2[index]) | ||
256 | break; | ||
257 | } | ||
258 | if (index >= ARRAY_SIZE(divisors2)) | ||
259 | index = ARRAY_SIZE(divisors2) - 1; | ||
260 | return index; | ||
261 | } | ||
262 | |||
263 | static unsigned long sh7722_frqcr_recalc(struct clk *clk) | ||
264 | { | ||
265 | struct frqcr_context ctx = sh7722_get_clk_context(clk->name); | ||
266 | unsigned long frqcr = ctrl_inl(FRQCR); | ||
267 | int index; | ||
268 | |||
269 | index = (frqcr >> ctx.shift) & ctx.mask; | ||
270 | return clk->parent->rate * 2 / divisors2[index]; | ||
271 | } | ||
272 | |||
273 | static int sh7722_frqcr_set_rate(struct clk *clk, unsigned long rate, | ||
274 | int algo_id) | ||
275 | { | ||
276 | struct frqcr_context ctx = sh7722_get_clk_context(clk->name); | ||
277 | unsigned long parent_rate = clk->parent->rate; | ||
278 | int div; | ||
279 | unsigned long frqcr; | ||
280 | int err = 0; | ||
281 | |||
282 | /* pretty invalid */ | ||
283 | if (parent_rate < rate) | ||
284 | return -EINVAL; | ||
285 | |||
286 | /* look for multiplier/divisor pair */ | ||
287 | div = sh7722_find_div_index(parent_rate, rate); | ||
288 | if (div<0) | ||
289 | return div; | ||
290 | |||
291 | /* calculate new value of clock rate */ | ||
292 | clk->rate = parent_rate * 2 / divisors2[div]; | ||
293 | frqcr = ctrl_inl(FRQCR); | ||
294 | |||
295 | /* FIXME: adjust as algo_id specifies */ | ||
296 | if (algo_id != NO_CHANGE) { | ||
297 | int originator; | ||
298 | char *algo_group_1[] = { "cpu_clk", "umem_clk", "sh_clk" }; | ||
299 | char *algo_group_2[] = { "sh_clk", "bus_clk" }; | ||
300 | char *algo_group_3[] = { "sh_clk", "sdram_clk" }; | ||
301 | char *algo_group_4[] = { "bus_clk", "peripheral_clk" }; | ||
302 | char *algo_group_5[] = { "cpu_clk", "peripheral_clk" }; | ||
303 | char **algo_current = NULL; | ||
304 | /* 3 is the maximum number of clocks in relation */ | ||
305 | struct clk *ck[3]; | ||
306 | unsigned long values[3]; /* the same comment as above */ | ||
307 | int part_length = -1; | ||
308 | int i; | ||
309 | |||
310 | /* | ||
311 | * all the steps below only required if adjustion was | ||
312 | * requested | ||
313 | */ | ||
314 | if (algo_id == IUS_N1_N1 || | ||
315 | algo_id == IUS_322 || | ||
316 | algo_id == IUS_522 || | ||
317 | algo_id == IUS_N11) { | ||
318 | algo_current = algo_group_1; | ||
319 | part_length = 3; | ||
320 | } | ||
321 | if (algo_id == SB_N1) { | ||
322 | algo_current = algo_group_2; | ||
323 | part_length = 2; | ||
324 | } | ||
325 | if (algo_id == SB3_N1 || | ||
326 | algo_id == SB3_32 || | ||
327 | algo_id == SB3_43 || | ||
328 | algo_id == SB3_54) { | ||
329 | algo_current = algo_group_3; | ||
330 | part_length = 2; | ||
331 | } | ||
332 | if (algo_id == BP_N1) { | ||
333 | algo_current = algo_group_4; | ||
334 | part_length = 2; | ||
335 | } | ||
336 | if (algo_id == IP_N1) { | ||
337 | algo_current = algo_group_5; | ||
338 | part_length = 2; | ||
339 | } | ||
340 | if (!algo_current) | ||
341 | goto incorrect_algo_id; | ||
342 | |||
343 | originator = -1; | ||
344 | for (i = 0; i < part_length; i ++ ) { | ||
345 | if (originator >= 0 && !strcmp(clk->name, | ||
346 | algo_current[i])) | ||
347 | originator = i; | ||
348 | ck[i] = clk_get(NULL, algo_current[i]); | ||
349 | values[i] = clk_get_rate(ck[i]); | ||
350 | } | ||
351 | |||
352 | if (originator >= 0) | ||
353 | adjust_clocks(originator, adjust_algos[algo_id], | ||
354 | values, part_length); | ||
355 | |||
356 | for (i = 0; i < part_length; i ++ ) { | ||
357 | struct frqcr_context part_ctx; | ||
358 | int part_div; | ||
359 | |||
360 | if (likely(!err)) { | ||
361 | part_div = sh7722_find_div_index(parent_rate, | ||
362 | rate); | ||
363 | if (part_div > 0) { | ||
364 | part_ctx = sh7722_get_clk_context( | ||
365 | ck[i]->name); | ||
366 | frqcr &= ~(part_ctx.mask << | ||
367 | part_ctx.shift); | ||
368 | frqcr |= part_div << part_ctx.shift; | ||
369 | } else | ||
370 | err = part_div; | ||
371 | } | ||
372 | |||
373 | ck[i]->ops->recalc(ck[i]); | ||
374 | clk_put(ck[i]); | ||
375 | } | ||
376 | } | ||
377 | |||
378 | /* was there any error during recalculation ? If so, bail out.. */ | ||
379 | if (unlikely(err!=0)) | ||
380 | goto out_err; | ||
381 | |||
382 | /* clear FRQCR bits */ | ||
383 | frqcr &= ~(ctx.mask << ctx.shift); | ||
384 | frqcr |= div << ctx.shift; | ||
385 | frqcr |= FRQCRKICK; | ||
386 | |||
387 | /* ...and perform actual change */ | ||
388 | ctrl_outl(frqcr, FRQCR); | ||
389 | return 0; | ||
390 | |||
391 | incorrect_algo_id: | ||
392 | return -EINVAL; | ||
393 | out_err: | ||
394 | return err; | ||
395 | } | ||
396 | |||
397 | static long sh7722_frqcr_round_rate(struct clk *clk, unsigned long rate) | ||
398 | { | ||
399 | unsigned long parent_rate = clk->parent->rate; | ||
400 | int div; | ||
401 | |||
402 | /* look for multiplier/divisor pair */ | ||
403 | div = sh7722_find_div_index(parent_rate, rate); | ||
404 | if (div < 0) | ||
405 | return clk->rate; | ||
406 | |||
407 | /* calculate new value of clock rate */ | ||
408 | return parent_rate * 2 / divisors2[div]; | ||
409 | } | ||
410 | |||
411 | static struct clk_ops sh7722_frqcr_clk_ops = { | ||
412 | .recalc = sh7722_frqcr_recalc, | ||
413 | .set_rate = sh7722_frqcr_set_rate, | ||
414 | .round_rate = sh7722_frqcr_round_rate, | ||
415 | }; | 53 | }; |
416 | 54 | ||
417 | /* | 55 | /* The dll block multiplies the 32khz r_clk, may be used instead of extal */ |
418 | * clock ops methods for SIU A/B and IrDA clock | 56 | static unsigned long dll_recalc(struct clk *clk) |
419 | */ | ||
420 | static int sh7722_siu_set_rate(struct clk *clk, unsigned long rate, int algo_id) | ||
421 | { | ||
422 | unsigned long r; | ||
423 | int div; | ||
424 | |||
425 | r = ctrl_inl(clk->arch_flags); | ||
426 | div = sh7722_find_div_index(clk->parent->rate, rate); | ||
427 | if (div < 0) | ||
428 | return div; | ||
429 | r = (r & ~0xF) | div; | ||
430 | ctrl_outl(r, clk->arch_flags); | ||
431 | return 0; | ||
432 | } | ||
433 | |||
434 | static unsigned long sh7722_siu_recalc(struct clk *clk) | ||
435 | { | ||
436 | unsigned long r; | ||
437 | |||
438 | r = ctrl_inl(clk->arch_flags); | ||
439 | return clk->parent->rate * 2 / divisors2[r & 0xF]; | ||
440 | } | ||
441 | |||
442 | static int sh7722_siu_start_stop(struct clk *clk, int enable) | ||
443 | { | 57 | { |
444 | unsigned long r; | 58 | unsigned long mult; |
445 | 59 | ||
446 | r = ctrl_inl(clk->arch_flags); | 60 | if (__raw_readl(PLLCR) & 0x1000) |
447 | if (enable) | 61 | mult = __raw_readl(DLLFRQ); |
448 | ctrl_outl(r & ~(1 << 8), clk->arch_flags); | ||
449 | else | 62 | else |
450 | ctrl_outl(r | (1 << 8), clk->arch_flags); | 63 | mult = 0; |
451 | return 0; | ||
452 | } | ||
453 | 64 | ||
454 | static int sh7722_siu_enable(struct clk *clk) | 65 | return clk->parent->rate * mult; |
455 | { | ||
456 | return sh7722_siu_start_stop(clk, 1); | ||
457 | } | ||
458 | |||
459 | static void sh7722_siu_disable(struct clk *clk) | ||
460 | { | ||
461 | sh7722_siu_start_stop(clk, 0); | ||
462 | } | 66 | } |
463 | 67 | ||
464 | static struct clk_ops sh7722_siu_clk_ops = { | 68 | static struct clk_ops dll_clk_ops = { |
465 | .recalc = sh7722_siu_recalc, | 69 | .recalc = dll_recalc, |
466 | .set_rate = sh7722_siu_set_rate, | ||
467 | .enable = sh7722_siu_enable, | ||
468 | .disable = sh7722_siu_disable, | ||
469 | }; | 70 | }; |
470 | 71 | ||
471 | static int sh7722_video_enable(struct clk *clk) | 72 | static struct clk dll_clk = { |
472 | { | 73 | .name = "dll_clk", |
473 | unsigned long r; | 74 | .id = -1, |
474 | 75 | .ops = &dll_clk_ops, | |
475 | r = ctrl_inl(VCLKCR); | 76 | .parent = &r_clk, |
476 | ctrl_outl( r & ~(1<<8), VCLKCR); | 77 | .flags = CLK_ENABLE_ON_INIT, |
477 | return 0; | 78 | }; |
478 | } | ||
479 | |||
480 | static void sh7722_video_disable(struct clk *clk) | ||
481 | { | ||
482 | unsigned long r; | ||
483 | |||
484 | r = ctrl_inl(VCLKCR); | ||
485 | ctrl_outl( r | (1<<8), VCLKCR); | ||
486 | } | ||
487 | 79 | ||
488 | static int sh7722_video_set_rate(struct clk *clk, unsigned long rate, | 80 | static unsigned long pll_recalc(struct clk *clk) |
489 | int algo_id) | ||
490 | { | 81 | { |
491 | unsigned long r; | 82 | unsigned long mult = 1; |
83 | unsigned long div = 1; | ||
492 | 84 | ||
493 | r = ctrl_inl(VCLKCR); | 85 | if (__raw_readl(PLLCR) & 0x4000) |
494 | r &= ~0x3F; | 86 | mult = (((__raw_readl(FRQCR) >> 24) & 0x1f) + 1); |
495 | r |= ((clk->parent->rate / rate - 1) & 0x3F); | 87 | else |
496 | ctrl_outl(r, VCLKCR); | 88 | div = 2; |
497 | return 0; | ||
498 | } | ||
499 | |||
500 | static unsigned long sh7722_video_recalc(struct clk *clk) | ||
501 | { | ||
502 | unsigned long r; | ||
503 | 89 | ||
504 | r = ctrl_inl(VCLKCR); | 90 | return (clk->parent->rate * mult) / div; |
505 | return clk->parent->rate / ((r & 0x3F) + 1); | ||
506 | } | 91 | } |
507 | 92 | ||
508 | static struct clk_ops sh7722_video_clk_ops = { | 93 | static struct clk_ops pll_clk_ops = { |
509 | .recalc = sh7722_video_recalc, | 94 | .recalc = pll_recalc, |
510 | .set_rate = sh7722_video_set_rate, | ||
511 | .enable = sh7722_video_enable, | ||
512 | .disable = sh7722_video_disable, | ||
513 | }; | ||
514 | /* | ||
515 | * and at last, clock definitions themselves | ||
516 | */ | ||
517 | static struct clk sh7722_umem_clock = { | ||
518 | .name = "umem_clk", | ||
519 | .ops = &sh7722_frqcr_clk_ops, | ||
520 | }; | 95 | }; |
521 | 96 | ||
522 | static struct clk sh7722_sh_clock = { | 97 | static struct clk pll_clk = { |
523 | .name = "sh_clk", | 98 | .name = "pll_clk", |
524 | .ops = &sh7722_frqcr_clk_ops, | 99 | .id = -1, |
100 | .ops = &pll_clk_ops, | ||
101 | .flags = CLK_ENABLE_ON_INIT, | ||
525 | }; | 102 | }; |
526 | 103 | ||
527 | static struct clk sh7722_peripheral_clock = { | 104 | struct clk *main_clks[] = { |
528 | .name = "peripheral_clk", | 105 | &r_clk, |
529 | .ops = &sh7722_frqcr_clk_ops, | 106 | &extal_clk, |
107 | &dll_clk, | ||
108 | &pll_clk, | ||
530 | }; | 109 | }; |
531 | 110 | ||
532 | static struct clk sh7722_sdram_clock = { | 111 | static int multipliers[] = { 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; |
533 | .name = "sdram_clk", | 112 | static int divisors[] = { 1, 3, 2, 5, 3, 4, 5, 6, 8, 10, 12, 16, 20 }; |
534 | .ops = &sh7722_frqcr_clk_ops, | ||
535 | }; | ||
536 | 113 | ||
537 | static struct clk sh7722_r_clock = { | 114 | static struct clk_div_mult_table div4_table = { |
538 | .name = "r_clk", | 115 | .divisors = divisors, |
539 | .rate = 32768, | 116 | .nr_divisors = ARRAY_SIZE(divisors), |
117 | .multipliers = multipliers, | ||
118 | .nr_multipliers = ARRAY_SIZE(multipliers), | ||
540 | }; | 119 | }; |
541 | 120 | ||
542 | /* | 121 | enum { DIV4_I, DIV4_U, DIV4_SH, DIV4_B, DIV4_B3, DIV4_P, |
543 | * these three clocks - SIU A, SIU B, IrDA - share the same clk_ops | 122 | DIV4_SIUA, DIV4_SIUB, DIV4_IRDA, DIV4_NR }; |
544 | * methods of clk_ops determine which register they should access by | ||
545 | * examining clk->name field | ||
546 | */ | ||
547 | static struct clk sh7722_siu_a_clock = { | ||
548 | .name = "siu_a_clk", | ||
549 | .arch_flags = SCLKACR, | ||
550 | .ops = &sh7722_siu_clk_ops, | ||
551 | }; | ||
552 | 123 | ||
553 | static struct clk sh7722_siu_b_clock = { | 124 | #define DIV4(_str, _reg, _bit, _mask, _flags) \ |
554 | .name = "siu_b_clk", | 125 | SH_CLK_DIV4(_str, &pll_clk, _reg, _bit, _mask, _flags) |
555 | .arch_flags = SCLKBCR, | ||
556 | .ops = &sh7722_siu_clk_ops, | ||
557 | }; | ||
558 | 126 | ||
559 | #if defined(CONFIG_CPU_SUBTYPE_SH7722) | 127 | struct clk div4_clks[DIV4_NR] = { |
560 | static struct clk sh7722_irda_clock = { | 128 | [DIV4_I] = DIV4("cpu_clk", FRQCR, 20, 0x1fef, CLK_ENABLE_ON_INIT), |
561 | .name = "irda_clk", | 129 | [DIV4_U] = DIV4("umem_clk", FRQCR, 16, 0x1fff, CLK_ENABLE_ON_INIT), |
562 | .arch_flags = IrDACLKCR, | 130 | [DIV4_SH] = DIV4("shyway_clk", FRQCR, 12, 0x1fff, CLK_ENABLE_ON_INIT), |
563 | .ops = &sh7722_siu_clk_ops, | 131 | [DIV4_B] = DIV4("bus_clk", FRQCR, 8, 0x1fff, CLK_ENABLE_ON_INIT), |
132 | [DIV4_B3] = DIV4("b3_clk", FRQCR, 4, 0x1fff, CLK_ENABLE_ON_INIT), | ||
133 | [DIV4_P] = DIV4("peripheral_clk", FRQCR, 0, 0x1fff, 0), | ||
134 | [DIV4_SIUA] = DIV4("siua_clk", SCLKACR, 0, 0x1fff, 0), | ||
135 | [DIV4_SIUB] = DIV4("siub_clk", SCLKBCR, 0, 0x1fff, 0), | ||
136 | [DIV4_IRDA] = DIV4("irda_clk", IRDACLKCR, 0, 0x1fff, 0), | ||
564 | }; | 137 | }; |
565 | #endif | ||
566 | 138 | ||
567 | static struct clk sh7722_video_clock = { | 139 | struct clk div6_clks[] = { |
568 | .name = "video_clk", | 140 | SH_CLK_DIV6("video_clk", &pll_clk, VCLKCR, 0), |
569 | .ops = &sh7722_video_clk_ops, | ||
570 | }; | 141 | }; |
571 | 142 | ||
572 | #define MSTPCR_ARCH_FLAGS(reg, bit) (((reg) << 8) | (bit)) | 143 | #define MSTP(_str, _parent, _reg, _bit, _flags) \ |
573 | #define MSTPCR_ARCH_FLAGS_REG(value) ((value) >> 8) | 144 | SH_CLK_MSTP32(_str, -1, _parent, _reg, _bit, _flags) |
574 | #define MSTPCR_ARCH_FLAGS_BIT(value) ((value) & 0xff) | ||
575 | |||
576 | static int sh7722_mstpcr_start_stop(struct clk *clk, int enable) | ||
577 | { | ||
578 | unsigned long bit = MSTPCR_ARCH_FLAGS_BIT(clk->arch_flags); | ||
579 | unsigned long reg; | ||
580 | unsigned long r; | ||
581 | 145 | ||
582 | switch(MSTPCR_ARCH_FLAGS_REG(clk->arch_flags)) { | 146 | static struct clk mstp_clks[] = { |
583 | case 0: | 147 | MSTP("uram0", &div4_clks[DIV4_U], MSTPCR0, 28, CLK_ENABLE_ON_INIT), |
584 | reg = MSTPCR0; | 148 | MSTP("xymem0", &div4_clks[DIV4_B], MSTPCR0, 26, CLK_ENABLE_ON_INIT), |
585 | break; | 149 | MSTP("tmu0", &div4_clks[DIV4_P], MSTPCR0, 15, 0), |
586 | case 1: | 150 | MSTP("cmt0", &r_clk, MSTPCR0, 14, 0), |
587 | reg = MSTPCR1; | 151 | MSTP("rwdt0", &r_clk, MSTPCR0, 13, 0), |
588 | break; | 152 | MSTP("flctl0", &div4_clks[DIV4_P], MSTPCR0, 10, 0), |
589 | case 2: | 153 | MSTP("scif0", &div4_clks[DIV4_P], MSTPCR0, 7, 0), |
590 | reg = MSTPCR2; | 154 | MSTP("scif1", &div4_clks[DIV4_P], MSTPCR0, 6, 0), |
591 | break; | 155 | MSTP("scif2", &div4_clks[DIV4_P], MSTPCR0, 5, 0), |
592 | default: | ||
593 | return -EINVAL; | ||
594 | } | ||
595 | 156 | ||
596 | r = ctrl_inl(reg); | 157 | MSTP("i2c0", &div4_clks[DIV4_P], MSTPCR1, 9, 0), |
597 | 158 | MSTP("rtc0", &r_clk, MSTPCR1, 8, 0), | |
598 | if (enable) | ||
599 | r &= ~(1 << bit); | ||
600 | else | ||
601 | r |= (1 << bit); | ||
602 | 159 | ||
603 | ctrl_outl(r, reg); | 160 | MSTP("sdhi0", &div4_clks[DIV4_P], MSTPCR2, 18, 0), |
604 | return 0; | 161 | MSTP("keysc0", &r_clk, MSTPCR2, 14, 0), |
605 | } | 162 | MSTP("usbf0", &div4_clks[DIV4_P], MSTPCR2, 11, 0), |
606 | 163 | MSTP("2dg0", &div4_clks[DIV4_B], MSTPCR2, 9, 0), | |
607 | static int sh7722_mstpcr_enable(struct clk *clk) | 164 | MSTP("siu0", &div4_clks[DIV4_B], MSTPCR2, 8, 0), |
608 | { | 165 | MSTP("vou0", &div4_clks[DIV4_B], MSTPCR2, 5, 0), |
609 | return sh7722_mstpcr_start_stop(clk, 1); | 166 | MSTP("jpu0", &div4_clks[DIV4_B], MSTPCR2, 6, CLK_ENABLE_ON_INIT), |
610 | } | 167 | MSTP("beu0", &div4_clks[DIV4_B], MSTPCR2, 4, 0), |
611 | 168 | MSTP("ceu0", &div4_clks[DIV4_B], MSTPCR2, 3, 0), | |
612 | static void sh7722_mstpcr_disable(struct clk *clk) | 169 | MSTP("veu0", &div4_clks[DIV4_B], MSTPCR2, 2, CLK_ENABLE_ON_INIT), |
613 | { | 170 | MSTP("vpu0", &div4_clks[DIV4_B], MSTPCR2, 1, CLK_ENABLE_ON_INIT), |
614 | sh7722_mstpcr_start_stop(clk, 0); | 171 | MSTP("lcdc0", &div4_clks[DIV4_B], MSTPCR2, 0, 0), |
615 | } | ||
616 | |||
617 | static struct clk_ops sh7722_mstpcr_clk_ops = { | ||
618 | .enable = sh7722_mstpcr_enable, | ||
619 | .disable = sh7722_mstpcr_disable, | ||
620 | .recalc = followparent_recalc, | ||
621 | }; | ||
622 | |||
623 | #define MSTPCR(_name, _parent, regnr, bitnr, _flags) \ | ||
624 | { \ | ||
625 | .name = _name, \ | ||
626 | .flags = _flags, \ | ||
627 | .arch_flags = MSTPCR_ARCH_FLAGS(regnr, bitnr), \ | ||
628 | .ops = (void *)_parent, \ | ||
629 | } | ||
630 | |||
631 | static struct clk sh7722_mstpcr_clocks[] = { | ||
632 | #if defined(CONFIG_CPU_SUBTYPE_SH7722) | ||
633 | MSTPCR("uram0", "umem_clk", 0, 28, CLK_ENABLE_ON_INIT), | ||
634 | MSTPCR("xymem0", "bus_clk", 0, 26, CLK_ENABLE_ON_INIT), | ||
635 | MSTPCR("tmu0", "peripheral_clk", 0, 15, 0), | ||
636 | MSTPCR("cmt0", "r_clk", 0, 14, 0), | ||
637 | MSTPCR("rwdt0", "r_clk", 0, 13, 0), | ||
638 | MSTPCR("flctl0", "peripheral_clk", 0, 10, 0), | ||
639 | MSTPCR("scif0", "peripheral_clk", 0, 7, 0), | ||
640 | MSTPCR("scif1", "peripheral_clk", 0, 6, 0), | ||
641 | MSTPCR("scif2", "peripheral_clk", 0, 5, 0), | ||
642 | MSTPCR("i2c0", "peripheral_clk", 1, 9, 0), | ||
643 | MSTPCR("rtc0", "r_clk", 1, 8, 0), | ||
644 | MSTPCR("sdhi0", "peripheral_clk", 2, 18, 0), | ||
645 | MSTPCR("keysc0", "r_clk", 2, 14, 0), | ||
646 | MSTPCR("usbf0", "peripheral_clk", 2, 11, 0), | ||
647 | MSTPCR("2dg0", "bus_clk", 2, 9, 0), | ||
648 | MSTPCR("siu0", "bus_clk", 2, 8, 0), | ||
649 | MSTPCR("vou0", "bus_clk", 2, 5, 0), | ||
650 | MSTPCR("jpu0", "bus_clk", 2, 6, CLK_ENABLE_ON_INIT), | ||
651 | MSTPCR("beu0", "bus_clk", 2, 4, 0), | ||
652 | MSTPCR("ceu0", "bus_clk", 2, 3, 0), | ||
653 | MSTPCR("veu0", "bus_clk", 2, 2, CLK_ENABLE_ON_INIT), | ||
654 | MSTPCR("vpu0", "bus_clk", 2, 1, CLK_ENABLE_ON_INIT), | ||
655 | MSTPCR("lcdc0", "bus_clk", 2, 0, 0), | ||
656 | #endif | ||
657 | }; | ||
658 | |||
659 | static struct clk *sh7722_clocks[] = { | ||
660 | &sh7722_umem_clock, | ||
661 | &sh7722_sh_clock, | ||
662 | &sh7722_peripheral_clock, | ||
663 | &sh7722_sdram_clock, | ||
664 | &sh7722_siu_a_clock, | ||
665 | &sh7722_siu_b_clock, | ||
666 | #if defined(CONFIG_CPU_SUBTYPE_SH7722) | ||
667 | &sh7722_irda_clock, | ||
668 | #endif | ||
669 | &sh7722_video_clock, | ||
670 | }; | ||
671 | |||
672 | /* | ||
673 | * init in order: master, module, bus, cpu | ||
674 | */ | ||
675 | struct clk_ops *onchip_ops[] = { | ||
676 | &sh7722_master_clk_ops, | ||
677 | &sh7722_module_clk_ops, | ||
678 | &sh7722_frqcr_clk_ops, | ||
679 | &sh7722_frqcr_clk_ops, | ||
680 | }; | 172 | }; |
681 | 173 | ||
682 | void __init | ||
683 | arch_init_clk_ops(struct clk_ops **ops, int type) | ||
684 | { | ||
685 | BUG_ON(type < 0 || type >= ARRAY_SIZE(onchip_ops)); | ||
686 | *ops = onchip_ops[type]; | ||
687 | } | ||
688 | |||
689 | int __init arch_clk_init(void) | 174 | int __init arch_clk_init(void) |
690 | { | 175 | { |
691 | struct clk *clk; | 176 | int k, ret = 0; |
692 | int i; | ||
693 | 177 | ||
694 | cpg_clk_init(); | 178 | /* autodetect extal or dll configuration */ |
179 | if (__raw_readl(PLLCR) & 0x1000) | ||
180 | pll_clk.parent = &dll_clk; | ||
181 | else | ||
182 | pll_clk.parent = &extal_clk; | ||
695 | 183 | ||
696 | clk = clk_get(NULL, "master_clk"); | 184 | for (k = 0; !ret && (k < ARRAY_SIZE(main_clks)); k++) |
697 | for (i = 0; i < ARRAY_SIZE(sh7722_clocks); i++) { | 185 | ret = clk_register(main_clks[k]); |
698 | pr_debug( "Registering clock '%s'\n", sh7722_clocks[i]->name); | ||
699 | sh7722_clocks[i]->parent = clk; | ||
700 | clk_register(sh7722_clocks[i]); | ||
701 | } | ||
702 | clk_put(clk); | ||
703 | 186 | ||
704 | clk_register(&sh7722_r_clock); | 187 | if (!ret) |
188 | ret = sh_clk_div4_register(div4_clks, DIV4_NR, &div4_table); | ||
705 | 189 | ||
706 | for (i = 0; i < ARRAY_SIZE(sh7722_mstpcr_clocks); i++) { | 190 | if (!ret) |
707 | pr_debug( "Registering mstpcr clock '%s'\n", | 191 | ret = sh_clk_div6_register(div6_clks, ARRAY_SIZE(div6_clks)); |
708 | sh7722_mstpcr_clocks[i].name); | ||
709 | clk = clk_get(NULL, (void *) sh7722_mstpcr_clocks[i].ops); | ||
710 | sh7722_mstpcr_clocks[i].parent = clk; | ||
711 | sh7722_mstpcr_clocks[i].ops = &sh7722_mstpcr_clk_ops; | ||
712 | clk_register(&sh7722_mstpcr_clocks[i]); | ||
713 | clk_put(clk); | ||
714 | } | ||
715 | 192 | ||
716 | propagate_rate(&sh7722_r_clock); /* make sure rate gets propagated */ | 193 | if (!ret) |
194 | ret = sh_clk_mstp32_register(mstp_clks, ARRAY_SIZE(mstp_clks)); | ||
717 | 195 | ||
718 | return 0; | 196 | return ret; |
719 | } | 197 | } |