~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/arch/arm/plat-s3c/pwm-clock.c

Version: ~ [ linux-5.15-rc5 ] ~ [ linux-5.14.11 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.72 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.152 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.210 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.250 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.286 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.288 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.18.140 ] ~ [ linux-3.16.85 ] ~ [ linux-3.14.79 ] ~ [ linux-3.12.74 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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/log2.h>
 18 #include <linux/clk.h>
 19 #include <linux/err.h>
 20 #include <linux/io.h>
 21 
 22 #include <mach/hardware.h>
 23 #include <mach/map.h>
 24 #include <asm/irq.h>
 25 
 26 #include <plat/clock.h>
 27 #include <plat/cpu.h>
 28 
 29 #include <plat/regs-timer.h>
 30 #include <mach/pwm-clock.h>
 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 struct clk clk_timer_scaler[];
 77 
 78 static unsigned long clk_pwm_scaler_get_rate(struct clk *clk)
 79 {
 80         unsigned long tcfg0 = __raw_readl(S3C2410_TCFG0);
 81 
 82         if (clk == &clk_timer_scaler[1]) {
 83                 tcfg0 &= S3C2410_TCFG_PRESCALER1_MASK;
 84                 tcfg0 >>= S3C2410_TCFG_PRESCALER1_SHIFT;
 85         } else {
 86                 tcfg0 &= S3C2410_TCFG_PRESCALER0_MASK;
 87         }
 88 
 89         return clk_get_rate(clk->parent) / (tcfg0 + 1);
 90 }
 91 
 92 static unsigned long clk_pwm_scaler_round_rate(struct clk *clk,
 93                                                unsigned long rate)
 94 {
 95         unsigned long parent_rate = clk_get_rate(clk->parent);
 96         unsigned long divisor = parent_rate / rate;
 97 
 98         if (divisor > 256)
 99                 divisor = 256;
100         else if (divisor < 2)
101                 divisor = 2;
102 
103         return parent_rate / divisor;
104 }
105 
106 static int clk_pwm_scaler_set_rate(struct clk *clk, unsigned long rate)
107 {
108         unsigned long round = clk_pwm_scaler_round_rate(clk, rate);
109         unsigned long tcfg0;
110         unsigned long divisor;
111         unsigned long flags;
112 
113         divisor = clk_get_rate(clk->parent) / round;
114         divisor--;
115 
116         local_irq_save(flags);
117         tcfg0 = __raw_readl(S3C2410_TCFG0);
118 
119         if (clk == &clk_timer_scaler[1]) {
120                 tcfg0 &= ~S3C2410_TCFG_PRESCALER1_MASK;
121                 tcfg0 |= divisor << S3C2410_TCFG_PRESCALER1_SHIFT;
122         } else {
123                 tcfg0 &= ~S3C2410_TCFG_PRESCALER0_MASK;
124                 tcfg0 |= divisor;
125         }
126 
127         __raw_writel(tcfg0, S3C2410_TCFG0);
128         local_irq_restore(flags);
129 
130         return 0;
131 }
132 
133 static struct clk clk_timer_scaler[] = {
134         [0]     = {
135                 .name           = "pwm-scaler0",
136                 .id             = -1,
137                 .get_rate       = clk_pwm_scaler_get_rate,
138                 .set_rate       = clk_pwm_scaler_set_rate,
139                 .round_rate     = clk_pwm_scaler_round_rate,
140         },
141         [1]     = {
142                 .name           = "pwm-scaler1",
143                 .id             = -1,
144                 .get_rate       = clk_pwm_scaler_get_rate,
145                 .set_rate       = clk_pwm_scaler_set_rate,
146                 .round_rate     = clk_pwm_scaler_round_rate,
147         },
148 };
149 
150 static struct clk clk_timer_tclk[] = {
151         [0]     = {
152                 .name           = "pwm-tclk0",
153                 .id             = -1,
154         },
155         [1]     = {
156                 .name           = "pwm-tclk1",
157                 .id             = -1,
158         },
159 };
160 
161 struct pwm_tdiv_clk {
162         struct clk      clk;
163         unsigned int    divisor;
164 };
165 
166 static inline struct pwm_tdiv_clk *to_tdiv(struct clk *clk)
167 {
168         return container_of(clk, struct pwm_tdiv_clk, clk);
169 }
170 
171 static unsigned long clk_pwm_tdiv_get_rate(struct clk *clk)
172 {
173         unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1);
174         unsigned int divisor;
175 
176         tcfg1 >>= S3C2410_TCFG1_SHIFT(clk->id);
177         tcfg1 &= S3C2410_TCFG1_MUX_MASK;
178 
179         if (pwm_cfg_src_is_tclk(tcfg1))
180                 divisor = to_tdiv(clk)->divisor;
181         else
182                 divisor = tcfg_to_divisor(tcfg1);
183 
184         return clk_get_rate(clk->parent) / divisor;
185 }
186 
187 static unsigned long clk_pwm_tdiv_round_rate(struct clk *clk,
188                                              unsigned long rate)
189 {
190         unsigned long parent_rate;
191         unsigned long divisor;
192 
193         parent_rate = clk_get_rate(clk->parent);
194         divisor = parent_rate / rate;
195 
196         if (divisor <= 1 && pwm_tdiv_has_div1())
197                 divisor = 1;
198         else if (divisor <= 2)
199                 divisor = 2;
200         else if (divisor <= 4)
201                 divisor = 4;
202         else if (divisor <= 8)
203                 divisor = 8;
204         else
205                 divisor = 16;
206 
207         return parent_rate / divisor;
208 }
209 
210 static unsigned long clk_pwm_tdiv_bits(struct pwm_tdiv_clk *divclk)
211 {
212         return pwm_tdiv_div_bits(divclk->divisor);
213 }
214 
215 static void clk_pwm_tdiv_update(struct pwm_tdiv_clk *divclk)
216 {
217         unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1);
218         unsigned long bits = clk_pwm_tdiv_bits(divclk);
219         unsigned long flags;
220         unsigned long shift =  S3C2410_TCFG1_SHIFT(divclk->clk.id);
221 
222         local_irq_save(flags);
223 
224         tcfg1 = __raw_readl(S3C2410_TCFG1);
225         tcfg1 &= ~(S3C2410_TCFG1_MUX_MASK << shift);
226         tcfg1 |= bits << shift;
227         __raw_writel(tcfg1, S3C2410_TCFG1);
228 
229         local_irq_restore(flags);
230 }
231 
232 static int clk_pwm_tdiv_set_rate(struct clk *clk, unsigned long rate)
233 {
234         struct pwm_tdiv_clk *divclk = to_tdiv(clk);
235         unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1);
236         unsigned long parent_rate = clk_get_rate(clk->parent);
237         unsigned long divisor;
238 
239         tcfg1 >>= S3C2410_TCFG1_SHIFT(clk->id);
240         tcfg1 &= S3C2410_TCFG1_MUX_MASK;
241 
242         rate = clk_round_rate(clk, rate);
243         divisor = parent_rate / rate;
244 
245         if (divisor > 16)
246                 return -EINVAL;
247 
248         divclk->divisor = divisor;
249 
250         /* Update the current MUX settings if we are currently
251          * selected as the clock source for this clock. */
252 
253         if (!pwm_cfg_src_is_tclk(tcfg1))
254                 clk_pwm_tdiv_update(divclk);
255 
256         return 0;
257 }
258 
259 static struct pwm_tdiv_clk clk_timer_tdiv[] = {
260         [0]     = {
261                 .clk    = {
262                         .name           = "pwm-tdiv",
263                         .parent         = &clk_timer_scaler[0],
264                         .get_rate       = clk_pwm_tdiv_get_rate,
265                         .set_rate       = clk_pwm_tdiv_set_rate,
266                         .round_rate     = clk_pwm_tdiv_round_rate,
267                 },
268         },
269         [1]     = {
270                 .clk    = {
271                         .name           = "pwm-tdiv",
272                         .parent         = &clk_timer_scaler[0],
273                         .get_rate       = clk_pwm_tdiv_get_rate,
274                         .set_rate       = clk_pwm_tdiv_set_rate,
275                         .round_rate     = clk_pwm_tdiv_round_rate,
276                 }
277         },
278         [2]     = {
279                 .clk    = {
280                         .name           = "pwm-tdiv",
281                         .parent         = &clk_timer_scaler[1],
282                         .get_rate       = clk_pwm_tdiv_get_rate,
283                         .set_rate       = clk_pwm_tdiv_set_rate,
284                         .round_rate     = clk_pwm_tdiv_round_rate,
285                 },
286         },
287         [3]     = {
288                 .clk    = {
289                         .name           = "pwm-tdiv",
290                         .parent         = &clk_timer_scaler[1],
291                         .get_rate       = clk_pwm_tdiv_get_rate,
292                         .set_rate       = clk_pwm_tdiv_set_rate,
293                         .round_rate     = clk_pwm_tdiv_round_rate,
294                 },
295         },
296         [4]     = {
297                 .clk    = {
298                         .name           = "pwm-tdiv",
299                         .parent         = &clk_timer_scaler[1],
300                         .get_rate       = clk_pwm_tdiv_get_rate,
301                         .set_rate       = clk_pwm_tdiv_set_rate,
302                         .round_rate     = clk_pwm_tdiv_round_rate,
303                 },
304         },
305 };
306 
307 static int __init clk_pwm_tdiv_register(unsigned int id)
308 {
309         struct pwm_tdiv_clk *divclk = &clk_timer_tdiv[id];
310         unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1);
311 
312         tcfg1 >>= S3C2410_TCFG1_SHIFT(id);
313         tcfg1 &= S3C2410_TCFG1_MUX_MASK;
314 
315         divclk->clk.id = id;
316         divclk->divisor = tcfg_to_divisor(tcfg1);
317 
318         return s3c24xx_register_clock(&divclk->clk);
319 }
320 
321 static inline struct clk *s3c24xx_pwmclk_tclk(unsigned int id)
322 {
323         return (id >= 2) ? &clk_timer_tclk[1] : &clk_timer_tclk[0];
324 }
325 
326 static inline struct clk *s3c24xx_pwmclk_tdiv(unsigned int id)
327 {
328         return &clk_timer_tdiv[id].clk;
329 }
330 
331 static int clk_pwm_tin_set_parent(struct clk *clk, struct clk *parent)
332 {
333         unsigned int id = clk->id;
334         unsigned long tcfg1;
335         unsigned long flags;
336         unsigned long bits;
337         unsigned long shift = S3C2410_TCFG1_SHIFT(id);
338 
339         if (parent == s3c24xx_pwmclk_tclk(id))
340                 bits = S3C_TCFG1_MUX_TCLK << shift;
341         else if (parent == s3c24xx_pwmclk_tdiv(id))
342                 bits = clk_pwm_tdiv_bits(to_tdiv(parent)) << shift;
343         else
344                 return -EINVAL;
345 
346         clk->parent = parent;
347 
348         local_irq_save(flags);
349 
350         tcfg1 = __raw_readl(S3C2410_TCFG1);
351         tcfg1 &= ~(S3C2410_TCFG1_MUX_MASK << shift);
352         __raw_writel(tcfg1 | bits, S3C2410_TCFG1);
353 
354         local_irq_restore(flags);
355 
356         return 0;
357 }
358 
359 static struct clk clk_tin[] = {
360         [0]     = {
361                 .name           = "pwm-tin",
362                 .id             = 0,
363                 .set_parent     = clk_pwm_tin_set_parent,
364         },
365         [1]     = {
366                 .name           = "pwm-tin",
367                 .id             = 1,
368                 .set_parent     = clk_pwm_tin_set_parent,
369         },
370         [2]     = {
371                 .name           = "pwm-tin",
372                 .id             = 2,
373                 .set_parent     = clk_pwm_tin_set_parent,
374         },
375         [3]     = {
376                 .name           = "pwm-tin",
377                 .id             = 3,
378                 .set_parent     = clk_pwm_tin_set_parent,
379         },
380         [4]     = {
381                 .name           = "pwm-tin",
382                 .id             = 4,
383                 .set_parent     = clk_pwm_tin_set_parent,
384         },
385 };
386 
387 static __init int clk_pwm_tin_register(struct clk *pwm)
388 {
389         unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1);
390         unsigned int id = pwm->id;
391 
392         struct clk *parent;
393         int ret;
394 
395         ret = s3c24xx_register_clock(pwm);
396         if (ret < 0)
397                 return ret;
398 
399         tcfg1 >>= S3C2410_TCFG1_SHIFT(id);
400         tcfg1 &= S3C2410_TCFG1_MUX_MASK;
401 
402         if (pwm_cfg_src_is_tclk(tcfg1))
403                 parent = s3c24xx_pwmclk_tclk(id);
404         else
405                 parent = s3c24xx_pwmclk_tdiv(id);
406 
407         return clk_set_parent(pwm, parent);
408 }
409 
410 /**
411  * s3c_pwmclk_init() - initialise pwm clocks
412  *
413  * Initialise and register the clocks which provide the inputs for the
414  * pwm timer blocks.
415  *
416  * Note, this call is required by the time core, so must be called after
417  * the base clocks are added and before any of the initcalls are run.
418  */
419 __init void s3c_pwmclk_init(void)
420 {
421         struct clk *clk_timers;
422         unsigned int clk;
423         int ret;
424 
425         clk_timers = clk_get(NULL, "timers");
426         if (IS_ERR(clk_timers)) {
427                 printk(KERN_ERR "%s: no parent clock\n", __func__);
428                 return;
429         }
430 
431         for (clk = 0; clk < ARRAY_SIZE(clk_timer_scaler); clk++) {
432                 clk_timer_scaler[clk].parent = clk_timers;
433                 ret = s3c24xx_register_clock(&clk_timer_scaler[clk]);
434                 if (ret < 0) {
435                         printk(KERN_ERR "error adding pwm scaler%d clock\n", clk);
436                         return;
437                 }
438         }
439 
440         for (clk = 0; clk < ARRAY_SIZE(clk_timer_tclk); clk++) {
441                 ret = s3c24xx_register_clock(&clk_timer_tclk[clk]);
442                 if (ret < 0) {
443                         printk(KERN_ERR "error adding pww tclk%d\n", clk);
444                         return;
445                 }
446         }
447 
448         for (clk = 0; clk < ARRAY_SIZE(clk_timer_tdiv); clk++) {
449                 ret = clk_pwm_tdiv_register(clk);
450                 if (ret < 0) {
451                         printk(KERN_ERR "error adding pwm%d tdiv clock\n", clk);
452                         return;
453                 }
454         }
455 
456         for (clk = 0; clk < ARRAY_SIZE(clk_tin); clk++) {
457                 ret = clk_pwm_tin_register(&clk_tin[clk]);
458                 if (ret < 0) {
459                         printk(KERN_ERR "error adding pwm%d tin clock\n", clk);
460                         return;
461                 }
462         }
463 }
464 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp