Commit | Line | Data |
---|---|---|
b999f0db BD |
1 | /* linux/arch/arm/plat-s3c24xx/pwm-clock.c |
2 | * | |
3 | * Copyright (c) 2007 Simtec Electronics | |
4 | * Copyright (c) 2007, 2008 Ben Dooks | |
5 | * Ben Dooks <ben-linux@fluff.org> | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify | |
8 | * it under the terms of the GNU General Public License as published by | |
9 | * the Free Software Foundation; either version 2 of the License. | |
10 | */ | |
11 | ||
12 | #include <linux/init.h> | |
13 | #include <linux/module.h> | |
14 | #include <linux/kernel.h> | |
15 | #include <linux/list.h> | |
16 | #include <linux/errno.h> | |
17 | #include <linux/clk.h> | |
18 | #include <linux/err.h> | |
19 | #include <linux/io.h> | |
20 | ||
a09e64fb | 21 | #include <mach/hardware.h> |
b999f0db BD |
22 | #include <asm/irq.h> |
23 | ||
a09e64fb RK |
24 | #include <mach/regs-clock.h> |
25 | #include <mach/regs-gpio.h> | |
b999f0db | 26 | |
d5120ae7 | 27 | #include <plat/clock.h> |
a2b7ba9c | 28 | #include <plat/cpu.h> |
b999f0db | 29 | |
a2b7ba9c | 30 | #include <plat/regs-timer.h> |
b999f0db BD |
31 | |
32 | /* Each of the timers 0 through 5 go through the following | |
33 | * clock tree, with the inputs depending on the timers. | |
34 | * | |
35 | * pclk ---- [ prescaler 0 ] -+---> timer 0 | |
36 | * +---> timer 1 | |
37 | * | |
38 | * pclk ---- [ prescaler 1 ] -+---> timer 2 | |
39 | * +---> timer 3 | |
40 | * \---> timer 4 | |
41 | * | |
42 | * Which are fed into the timers as so: | |
43 | * | |
44 | * prescaled 0 ---- [ div 2,4,8,16 ] ---\ | |
45 | * [mux] -> timer 0 | |
46 | * tclk 0 ------------------------------/ | |
47 | * | |
48 | * prescaled 0 ---- [ div 2,4,8,16 ] ---\ | |
49 | * [mux] -> timer 1 | |
50 | * tclk 0 ------------------------------/ | |
51 | * | |
52 | * | |
53 | * prescaled 1 ---- [ div 2,4,8,16 ] ---\ | |
54 | * [mux] -> timer 2 | |
55 | * tclk 1 ------------------------------/ | |
56 | * | |
57 | * prescaled 1 ---- [ div 2,4,8,16 ] ---\ | |
58 | * [mux] -> timer 3 | |
59 | * tclk 1 ------------------------------/ | |
60 | * | |
61 | * prescaled 1 ---- [ div 2,4,8, 16 ] --\ | |
62 | * [mux] -> timer 4 | |
63 | * tclk 1 ------------------------------/ | |
64 | * | |
65 | * Since the mux and the divider are tied together in the | |
66 | * same register space, it is impossible to set the parent | |
67 | * and the rate at the same time. To avoid this, we add an | |
68 | * intermediate 'prescaled-and-divided' clock to select | |
69 | * as the parent for the timer input clock called tdiv. | |
70 | * | |
71 | * prescaled clk --> pwm-tdiv ---\ | |
72 | * [ mux ] --> timer X | |
73 | * tclk -------------------------/ | |
74 | */ | |
75 | ||
76 | static unsigned long clk_pwm_scaler_getrate(struct clk *clk) | |
77 | { | |
78 | unsigned long tcfg0 = __raw_readl(S3C2410_TCFG0); | |
79 | ||
80 | if (clk->id == 1) { | |
81 | tcfg0 &= S3C2410_TCFG_PRESCALER1_MASK; | |
82 | tcfg0 >>= S3C2410_TCFG_PRESCALER1_SHIFT; | |
83 | } else { | |
84 | tcfg0 &= S3C2410_TCFG_PRESCALER0_MASK; | |
85 | } | |
86 | ||
87 | return clk_get_rate(clk->parent) / (tcfg0 + 1); | |
88 | } | |
89 | ||
90 | /* TODO - add set rate calls. */ | |
91 | ||
1442e662 | 92 | static struct clk clk_timer_scaler[] = { |
b999f0db BD |
93 | [0] = { |
94 | .name = "pwm-scaler0", | |
95 | .id = -1, | |
96 | .get_rate = clk_pwm_scaler_getrate, | |
97 | }, | |
98 | [1] = { | |
99 | .name = "pwm-scaler1", | |
100 | .id = -1, | |
101 | .get_rate = clk_pwm_scaler_getrate, | |
102 | }, | |
103 | }; | |
104 | ||
1442e662 | 105 | static struct clk clk_timer_tclk[] = { |
b999f0db BD |
106 | [0] = { |
107 | .name = "pwm-tclk0", | |
108 | .id = -1, | |
109 | }, | |
110 | [1] = { | |
111 | .name = "pwm-tclk1", | |
112 | .id = -1, | |
113 | }, | |
114 | }; | |
115 | ||
116 | struct pwm_tdiv_clk { | |
117 | struct clk clk; | |
118 | unsigned int divisor; | |
119 | }; | |
120 | ||
121 | static inline struct pwm_tdiv_clk *to_tdiv(struct clk *clk) | |
122 | { | |
123 | return container_of(clk, struct pwm_tdiv_clk, clk); | |
124 | } | |
125 | ||
126 | static inline unsigned long tcfg_to_divisor(unsigned long tcfg1) | |
127 | { | |
128 | return 1 << (1 + tcfg1); | |
129 | } | |
130 | ||
131 | static unsigned long clk_pwm_tdiv_get_rate(struct clk *clk) | |
132 | { | |
133 | unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1); | |
134 | unsigned int divisor; | |
135 | ||
136 | tcfg1 >>= S3C2410_TCFG1_SHIFT(clk->id); | |
137 | tcfg1 &= S3C2410_TCFG1_MUX_MASK; | |
138 | ||
139 | if (tcfg1 == S3C2410_TCFG1_MUX_TCLK) | |
140 | divisor = to_tdiv(clk)->divisor; | |
141 | else | |
142 | divisor = tcfg_to_divisor(tcfg1); | |
143 | ||
144 | return clk_get_rate(clk->parent) / divisor; | |
145 | } | |
146 | ||
147 | static unsigned long clk_pwm_tdiv_round_rate(struct clk *clk, | |
148 | unsigned long rate) | |
149 | { | |
150 | unsigned long parent_rate; | |
151 | unsigned long divisor; | |
152 | ||
153 | parent_rate = clk_get_rate(clk->parent); | |
154 | divisor = parent_rate / rate; | |
155 | ||
156 | if (divisor <= 2) | |
157 | divisor = 2; | |
158 | else if (divisor <= 4) | |
159 | divisor = 4; | |
160 | else if (divisor <= 8) | |
161 | divisor = 8; | |
162 | else | |
163 | divisor = 16; | |
164 | ||
165 | return parent_rate / divisor; | |
166 | } | |
167 | ||
168 | static unsigned long clk_pwm_tdiv_bits(struct pwm_tdiv_clk *divclk) | |
169 | { | |
170 | unsigned long bits; | |
171 | ||
172 | switch (divclk->divisor) { | |
173 | case 2: | |
174 | bits = S3C2410_TCFG1_MUX_DIV2; | |
175 | break; | |
176 | case 4: | |
177 | bits = S3C2410_TCFG1_MUX_DIV4; | |
178 | break; | |
179 | case 8: | |
180 | bits = S3C2410_TCFG1_MUX_DIV8; | |
181 | break; | |
182 | case 16: | |
183 | default: | |
184 | bits = S3C2410_TCFG1_MUX_DIV16; | |
185 | break; | |
186 | } | |
187 | ||
188 | return bits; | |
189 | } | |
190 | ||
191 | static void clk_pwm_tdiv_update(struct pwm_tdiv_clk *divclk) | |
192 | { | |
193 | unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1); | |
194 | unsigned long bits = clk_pwm_tdiv_bits(divclk); | |
195 | unsigned long flags; | |
196 | unsigned long shift = S3C2410_TCFG1_SHIFT(divclk->clk.id); | |
197 | ||
198 | local_irq_save(flags); | |
199 | ||
200 | tcfg1 = __raw_readl(S3C2410_TCFG1); | |
201 | tcfg1 &= ~(S3C2410_TCFG1_MUX_MASK << shift); | |
202 | tcfg1 |= bits << shift; | |
203 | __raw_writel(tcfg1, S3C2410_TCFG1); | |
204 | ||
205 | local_irq_restore(flags); | |
206 | } | |
207 | ||
208 | static int clk_pwm_tdiv_set_rate(struct clk *clk, unsigned long rate) | |
209 | { | |
210 | struct pwm_tdiv_clk *divclk = to_tdiv(clk); | |
211 | unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1); | |
212 | unsigned long parent_rate = clk_get_rate(clk->parent); | |
213 | unsigned long divisor; | |
214 | ||
215 | tcfg1 >>= S3C2410_TCFG1_SHIFT(clk->id); | |
216 | tcfg1 &= S3C2410_TCFG1_MUX_MASK; | |
217 | ||
218 | rate = clk_round_rate(clk, rate); | |
219 | divisor = parent_rate / rate; | |
220 | ||
221 | if (divisor > 16) | |
222 | return -EINVAL; | |
223 | ||
224 | divclk->divisor = divisor; | |
225 | ||
226 | /* Update the current MUX settings if we are currently | |
227 | * selected as the clock source for this clock. */ | |
228 | ||
229 | if (tcfg1 != S3C2410_TCFG1_MUX_TCLK) | |
230 | clk_pwm_tdiv_update(divclk); | |
231 | ||
232 | return 0; | |
233 | } | |
234 | ||
1442e662 | 235 | static struct pwm_tdiv_clk clk_timer_tdiv[] = { |
b999f0db BD |
236 | [0] = { |
237 | .clk = { | |
238 | .name = "pwm-tdiv", | |
239 | .parent = &clk_timer_scaler[0], | |
240 | .get_rate = clk_pwm_tdiv_get_rate, | |
241 | .set_rate = clk_pwm_tdiv_set_rate, | |
242 | .round_rate = clk_pwm_tdiv_round_rate, | |
243 | }, | |
244 | }, | |
245 | [1] = { | |
246 | .clk = { | |
247 | .name = "pwm-tdiv", | |
248 | .parent = &clk_timer_scaler[0], | |
249 | .get_rate = clk_pwm_tdiv_get_rate, | |
250 | .set_rate = clk_pwm_tdiv_set_rate, | |
251 | .round_rate = clk_pwm_tdiv_round_rate, | |
252 | } | |
253 | }, | |
254 | [2] = { | |
255 | .clk = { | |
256 | .name = "pwm-tdiv", | |
257 | .parent = &clk_timer_scaler[1], | |
258 | .get_rate = clk_pwm_tdiv_get_rate, | |
259 | .set_rate = clk_pwm_tdiv_set_rate, | |
260 | .round_rate = clk_pwm_tdiv_round_rate, | |
261 | }, | |
262 | }, | |
263 | [3] = { | |
264 | .clk = { | |
265 | .name = "pwm-tdiv", | |
266 | .parent = &clk_timer_scaler[1], | |
267 | .get_rate = clk_pwm_tdiv_get_rate, | |
268 | .set_rate = clk_pwm_tdiv_set_rate, | |
269 | .round_rate = clk_pwm_tdiv_round_rate, | |
270 | }, | |
271 | }, | |
272 | [4] = { | |
273 | .clk = { | |
274 | .name = "pwm-tdiv", | |
275 | .parent = &clk_timer_scaler[1], | |
276 | .get_rate = clk_pwm_tdiv_get_rate, | |
277 | .set_rate = clk_pwm_tdiv_set_rate, | |
278 | .round_rate = clk_pwm_tdiv_round_rate, | |
279 | }, | |
280 | }, | |
281 | }; | |
282 | ||
283 | static int __init clk_pwm_tdiv_register(unsigned int id) | |
284 | { | |
285 | struct pwm_tdiv_clk *divclk = &clk_timer_tdiv[id]; | |
286 | unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1); | |
287 | ||
288 | tcfg1 >>= S3C2410_TCFG1_SHIFT(id); | |
289 | tcfg1 &= S3C2410_TCFG1_MUX_MASK; | |
290 | ||
291 | divclk->clk.id = id; | |
292 | divclk->divisor = tcfg_to_divisor(tcfg1); | |
293 | ||
294 | return s3c24xx_register_clock(&divclk->clk); | |
295 | } | |
296 | ||
297 | static inline struct clk *s3c24xx_pwmclk_tclk(unsigned int id) | |
298 | { | |
299 | return (id >= 2) ? &clk_timer_tclk[1] : &clk_timer_tclk[0]; | |
300 | } | |
301 | ||
302 | static inline struct clk *s3c24xx_pwmclk_tdiv(unsigned int id) | |
303 | { | |
304 | return &clk_timer_tdiv[id].clk; | |
305 | } | |
306 | ||
307 | static int clk_pwm_tin_set_parent(struct clk *clk, struct clk *parent) | |
308 | { | |
309 | unsigned int id = clk->id; | |
310 | unsigned long tcfg1; | |
311 | unsigned long flags; | |
312 | unsigned long bits; | |
313 | unsigned long shift = S3C2410_TCFG1_SHIFT(id); | |
314 | ||
315 | if (parent == s3c24xx_pwmclk_tclk(id)) | |
316 | bits = S3C2410_TCFG1_MUX_TCLK << shift; | |
317 | else if (parent == s3c24xx_pwmclk_tdiv(id)) | |
318 | bits = clk_pwm_tdiv_bits(to_tdiv(clk)) << shift; | |
319 | else | |
320 | return -EINVAL; | |
321 | ||
322 | clk->parent = parent; | |
323 | ||
324 | local_irq_save(flags); | |
325 | ||
326 | tcfg1 = __raw_readl(S3C2410_TCFG1); | |
327 | tcfg1 &= ~(S3C2410_TCFG1_MUX_MASK << shift); | |
328 | __raw_writel(tcfg1 | bits, S3C2410_TCFG1); | |
329 | ||
330 | local_irq_restore(flags); | |
331 | ||
332 | return 0; | |
333 | } | |
334 | ||
335 | static struct clk clk_tin[] = { | |
336 | [0] = { | |
337 | .name = "pwm-tin", | |
338 | .id = 0, | |
339 | .set_parent = clk_pwm_tin_set_parent, | |
340 | }, | |
341 | [1] = { | |
342 | .name = "pwm-tin", | |
343 | .id = 1, | |
344 | .set_parent = clk_pwm_tin_set_parent, | |
345 | }, | |
346 | [2] = { | |
347 | .name = "pwm-tin", | |
348 | .id = 2, | |
349 | .set_parent = clk_pwm_tin_set_parent, | |
350 | }, | |
351 | [3] = { | |
352 | .name = "pwm-tin", | |
353 | .id = 3, | |
354 | .set_parent = clk_pwm_tin_set_parent, | |
355 | }, | |
356 | [4] = { | |
357 | .name = "pwm-tin", | |
358 | .id = 4, | |
359 | .set_parent = clk_pwm_tin_set_parent, | |
360 | }, | |
361 | }; | |
362 | ||
363 | static __init int clk_pwm_tin_register(struct clk *pwm) | |
364 | { | |
365 | unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1); | |
366 | unsigned int id = pwm->id; | |
367 | ||
368 | struct clk *parent; | |
369 | int ret; | |
370 | ||
371 | ret = s3c24xx_register_clock(pwm); | |
372 | if (ret < 0) | |
373 | return ret; | |
374 | ||
375 | tcfg1 >>= S3C2410_TCFG1_SHIFT(id); | |
376 | tcfg1 &= S3C2410_TCFG1_MUX_MASK; | |
377 | ||
378 | if (tcfg1 == S3C2410_TCFG1_MUX_TCLK) | |
379 | parent = s3c24xx_pwmclk_tclk(id); | |
380 | else | |
381 | parent = s3c24xx_pwmclk_tdiv(id); | |
382 | ||
383 | return clk_set_parent(pwm, parent); | |
384 | } | |
385 | ||
386 | static __init int s3c24xx_pwmclk_init(void) | |
387 | { | |
388 | struct clk *clk_timers; | |
389 | unsigned int clk; | |
390 | int ret; | |
391 | ||
392 | clk_timers = clk_get(NULL, "timers"); | |
393 | if (IS_ERR(clk_timers)) { | |
394 | printk(KERN_ERR "%s: no parent clock\n", __func__); | |
395 | return -EINVAL; | |
396 | } | |
397 | ||
398 | for (clk = 0; clk < ARRAY_SIZE(clk_timer_scaler); clk++) { | |
399 | clk_timer_scaler[clk].parent = clk_timers; | |
400 | ret = s3c24xx_register_clock(&clk_timer_scaler[clk]); | |
401 | if (ret < 0) { | |
402 | printk(KERN_ERR "error adding pwm scaler%d clock\n", clk); | |
403 | goto err; | |
404 | } | |
405 | } | |
406 | ||
407 | for (clk = 0; clk < ARRAY_SIZE(clk_timer_tclk); clk++) { | |
408 | ret = s3c24xx_register_clock(&clk_timer_tclk[clk]); | |
409 | if (ret < 0) { | |
410 | printk(KERN_ERR "error adding pww tclk%d\n", clk); | |
411 | goto err; | |
412 | } | |
413 | } | |
414 | ||
415 | for (clk = 0; clk < ARRAY_SIZE(clk_timer_tdiv); clk++) { | |
416 | ret = clk_pwm_tdiv_register(clk); | |
417 | if (ret < 0) { | |
418 | printk(KERN_ERR "error adding pwm%d tdiv clock\n", clk); | |
419 | goto err; | |
420 | } | |
421 | } | |
422 | ||
423 | for (clk = 0; clk < ARRAY_SIZE(clk_tin); clk++) { | |
424 | ret = clk_pwm_tin_register(&clk_tin[clk]); | |
425 | if (ret < 0) { | |
426 | printk(KERN_ERR "error adding pwm%d tin clock\n", clk); | |
427 | goto err; | |
428 | } | |
429 | } | |
430 | ||
431 | return 0; | |
432 | ||
433 | err: | |
434 | return ret; | |
435 | } | |
436 | ||
437 | arch_initcall(s3c24xx_pwmclk_init); |