• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/linux/linux-2.6.36/arch/arm/plat-samsung/
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
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			.ops	= &clk_tdiv_ops,
272			.parent	= &clk_timer_scaler[0],
273		},
274	},
275	[1]	= {
276		.clk	= {
277			.name	= "pwm-tdiv",
278			.ops	= &clk_tdiv_ops,
279			.parent	= &clk_timer_scaler[0],
280		}
281	},
282	[2]	= {
283		.clk	= {
284			.name	= "pwm-tdiv",
285			.ops	= &clk_tdiv_ops,
286			.parent	= &clk_timer_scaler[1],
287		},
288	},
289	[3]	= {
290		.clk	= {
291			.name	= "pwm-tdiv",
292			.ops	= &clk_tdiv_ops,
293			.parent	= &clk_timer_scaler[1],
294		},
295	},
296	[4]	= {
297		.clk	= {
298			.name	= "pwm-tdiv",
299			.ops	= &clk_tdiv_ops,
300			.parent	= &clk_timer_scaler[1],
301		},
302	},
303};
304
305static int __init clk_pwm_tdiv_register(unsigned int id)
306{
307	struct pwm_tdiv_clk *divclk = &clk_timer_tdiv[id];
308	unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1);
309
310	tcfg1 >>= S3C2410_TCFG1_SHIFT(id);
311	tcfg1 &= S3C2410_TCFG1_MUX_MASK;
312
313	divclk->clk.id = id;
314	divclk->divisor = tcfg_to_divisor(tcfg1);
315
316	return s3c24xx_register_clock(&divclk->clk);
317}
318
319static inline struct clk *s3c24xx_pwmclk_tclk(unsigned int id)
320{
321	return (id >= 2) ? &clk_timer_tclk[1] : &clk_timer_tclk[0];
322}
323
324static inline struct clk *s3c24xx_pwmclk_tdiv(unsigned int id)
325{
326	return &clk_timer_tdiv[id].clk;
327}
328
329static int clk_pwm_tin_set_parent(struct clk *clk, struct clk *parent)
330{
331	unsigned int id = clk->id;
332	unsigned long tcfg1;
333	unsigned long flags;
334	unsigned long bits;
335	unsigned long shift = S3C2410_TCFG1_SHIFT(id);
336
337	if (parent == s3c24xx_pwmclk_tclk(id))
338		bits = S3C_TCFG1_MUX_TCLK << shift;
339	else if (parent == s3c24xx_pwmclk_tdiv(id))
340		bits = clk_pwm_tdiv_bits(to_tdiv(parent)) << shift;
341	else
342		return -EINVAL;
343
344	clk->parent = parent;
345
346	local_irq_save(flags);
347
348	tcfg1 = __raw_readl(S3C2410_TCFG1);
349	tcfg1 &= ~(S3C2410_TCFG1_MUX_MASK << shift);
350	__raw_writel(tcfg1 | bits, S3C2410_TCFG1);
351
352	local_irq_restore(flags);
353
354	return 0;
355}
356
357static struct clk_ops clk_tin_ops = {
358	.set_parent	= clk_pwm_tin_set_parent,
359};
360
361static struct clk clk_tin[] = {
362	[0]	= {
363		.name	= "pwm-tin",
364		.id	= 0,
365		.ops	= &clk_tin_ops,
366	},
367	[1]	= {
368		.name	= "pwm-tin",
369		.id	= 1,
370		.ops	= &clk_tin_ops,
371	},
372	[2]	= {
373		.name	= "pwm-tin",
374		.id	= 2,
375		.ops	= &clk_tin_ops,
376	},
377	[3]	= {
378		.name	= "pwm-tin",
379		.id	= 3,
380		.ops	= &clk_tin_ops,
381	},
382	[4]	= {
383		.name	= "pwm-tin",
384		.id	= 4,
385		.ops	= &clk_tin_ops,
386	},
387};
388
389static __init int clk_pwm_tin_register(struct clk *pwm)
390{
391	unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1);
392	unsigned int id = pwm->id;
393
394	struct clk *parent;
395	int ret;
396
397	ret = s3c24xx_register_clock(pwm);
398	if (ret < 0)
399		return ret;
400
401	tcfg1 >>= S3C2410_TCFG1_SHIFT(id);
402	tcfg1 &= S3C2410_TCFG1_MUX_MASK;
403
404	if (pwm_cfg_src_is_tclk(tcfg1))
405		parent = s3c24xx_pwmclk_tclk(id);
406	else
407		parent = s3c24xx_pwmclk_tdiv(id);
408
409	return clk_set_parent(pwm, parent);
410}
411
412/**
413 * s3c_pwmclk_init() - initialise pwm clocks
414 *
415 * Initialise and register the clocks which provide the inputs for the
416 * pwm timer blocks.
417 *
418 * Note, this call is required by the time core, so must be called after
419 * the base clocks are added and before any of the initcalls are run.
420 */
421__init void s3c_pwmclk_init(void)
422{
423	struct clk *clk_timers;
424	unsigned int clk;
425	int ret;
426
427	clk_timers = clk_get(NULL, "timers");
428	if (IS_ERR(clk_timers)) {
429		printk(KERN_ERR "%s: no parent clock\n", __func__);
430		return;
431	}
432
433	for (clk = 0; clk < ARRAY_SIZE(clk_timer_scaler); clk++)
434		clk_timer_scaler[clk].parent = clk_timers;
435
436	s3c_register_clocks(clk_timer_scaler, ARRAY_SIZE(clk_timer_scaler));
437	s3c_register_clocks(clk_timer_tclk, ARRAY_SIZE(clk_timer_tclk));
438
439	for (clk = 0; clk < ARRAY_SIZE(clk_timer_tdiv); clk++) {
440		ret = clk_pwm_tdiv_register(clk);
441
442		if (ret < 0) {
443			printk(KERN_ERR "error adding pwm%d tdiv clock\n", clk);
444			return;
445		}
446	}
447
448	for (clk = 0; clk < ARRAY_SIZE(clk_tin); clk++) {
449		ret = clk_pwm_tin_register(&clk_tin[clk]);
450		if (ret < 0) {
451			printk(KERN_ERR "error adding pwm%d tin clock\n", clk);
452			return;
453		}
454	}
455}
456