Linux Audio

Check our new training course

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