Root/target/linux/s3c24xx/patches-2.6.30/011-s3c-pwm.patch

1--- /dev/null
2+++ b/arch/arm/plat-s3c/include/plat/pwm.h
3@@ -0,0 +1,45 @@
4+#ifndef __S3C2410_PWM_H
5+#define __S3C2410_PWM_H
6+
7+#include <linux/err.h>
8+#include <linux/platform_device.h>
9+#include <linux/clk.h>
10+
11+#include <mach/io.h>
12+#include <mach/hardware.h>
13+#include <asm/mach-types.h>
14+#include <plat/regs-timer.h>
15+
16+enum pwm_timer {
17+ PWM0,
18+ PWM1,
19+ PWM2,
20+ PWM3,
21+ PWM4
22+};
23+
24+struct s3c2410_pwm {
25+ enum pwm_timer timerid;
26+ struct clk *pclk;
27+ unsigned long pclk_rate;
28+ unsigned long prescaler;
29+ unsigned long divider;
30+ unsigned long counter;
31+ unsigned long comparer;
32+};
33+
34+struct s3c24xx_pwm_platform_data{
35+ /* callback to attach platform children (to enforce suspend / resume
36+ * ordering */
37+ void (*attach_child_devices)(struct device *parent_device);
38+};
39+
40+int s3c2410_pwm_init(struct s3c2410_pwm *s3c2410_pwm);
41+int s3c2410_pwm_enable(struct s3c2410_pwm *s3c2410_pwm);
42+int s3c2410_pwm_disable(struct s3c2410_pwm *s3c2410_pwm);
43+int s3c2410_pwm_start(struct s3c2410_pwm *s3c2410_pwm);
44+int s3c2410_pwm_stop(struct s3c2410_pwm *s3c2410_pwm);
45+int s3c2410_pwm_duty_cycle(int reg_value, struct s3c2410_pwm *s3c2410_pwm);
46+int s3c2410_pwm_dumpregs(void);
47+
48+#endif /* __S3C2410_PWM_H */
49--- a/arch/arm/plat-s3c/Kconfig
50+++ b/arch/arm/plat-s3c/Kconfig
51@@ -157,6 +157,11 @@ config S3C_DMA
52     help
53       Internal configuration for S3C DMA core
54 
55+config S3C_PWM
56+ bool
57+ help
58+ PWM timer code for the S3C2410, and similar processors
59+
60 # device definitions to compile in
61 
62 config S3C_DEV_HSMMC
63--- a/arch/arm/plat-s3c/Makefile
64+++ b/arch/arm/plat-s3c/Makefile
65@@ -35,5 +35,6 @@ obj-$(CONFIG_S3C_DEV_HSMMC1) += dev-hsmm
66 obj-y += dev-i2c0.o
67 obj-$(CONFIG_S3C_DEV_I2C1) += dev-i2c1.o
68 obj-$(CONFIG_S3C_DEV_FB) += dev-fb.o
69+obj-$(CONFIG_S3C_PWM) += pwm.o
70 obj-$(CONFIG_S3C_DMA) += dma.o
71 
72--- /dev/null
73+++ b/arch/arm/plat-s3c/pwm.c
74@@ -0,0 +1,288 @@
75+/*
76+ * arch/arm/plat-s3c/pwm.c
77+ *
78+ * Copyright (c) by Javi Roman <javiroman@kernel-labs.org>
79+ * for the Openmoko Project.
80+ *
81+ * S3C2410A SoC PWM support
82+ *
83+ * This program is free software; you can redistribute it and/or modify
84+ * it under the terms of the GNU General Public License as published by
85+ * the Free Software Foundation; either version 2 of the License, or
86+ * (at your option) any later version.
87+ *
88+ * You should have received a copy of the GNU General Public License
89+ * along with this program; if not, write to the Free Software
90+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
91+ *
92+ */
93+
94+#include <linux/kernel.h>
95+#include <linux/init.h>
96+#include <linux/clk.h>
97+#include <linux/device.h>
98+#include <mach/hardware.h>
99+#include <plat/regs-timer.h>
100+#include <plat/pwm.h>
101+#include <asm/io.h>
102+
103+#ifdef CONFIG_PM
104+ static unsigned long standby_reg_tcon;
105+ static unsigned long standby_reg_tcfg0;
106+ static unsigned long standby_reg_tcfg1;
107+#endif
108+
109+int s3c2410_pwm_disable(struct s3c2410_pwm *pwm)
110+{
111+ unsigned long tcon;
112+
113+ /* stop timer */
114+ tcon = __raw_readl(S3C2410_TCON);
115+ tcon &= 0xffffff00;
116+ __raw_writel(tcon, S3C2410_TCON);
117+
118+ clk_disable(pwm->pclk);
119+ clk_put(pwm->pclk);
120+
121+ return 0;
122+}
123+EXPORT_SYMBOL_GPL(s3c2410_pwm_disable);
124+
125+int s3c2410_pwm_init(struct s3c2410_pwm *pwm)
126+{
127+ pwm->pclk = clk_get(NULL, "timers");
128+ if (IS_ERR(pwm->pclk))
129+ return PTR_ERR(pwm->pclk);
130+
131+ clk_enable(pwm->pclk);
132+ pwm->pclk_rate = clk_get_rate(pwm->pclk);
133+ return 0;
134+}
135+EXPORT_SYMBOL_GPL(s3c2410_pwm_init);
136+
137+int s3c2410_pwm_enable(struct s3c2410_pwm *pwm)
138+{
139+ unsigned long tcfg0, tcfg1, tcnt, tcmp;
140+
141+ /* control registers bits */
142+ tcfg1 = __raw_readl(S3C2410_TCFG1);
143+ tcfg0 = __raw_readl(S3C2410_TCFG0);
144+
145+ /* divider & scaler slection */
146+ switch (pwm->timerid) {
147+ case PWM0:
148+ tcfg1 &= ~S3C2410_TCFG1_MUX0_MASK;
149+ tcfg0 &= ~S3C2410_TCFG_PRESCALER0_MASK;
150+ break;
151+ case PWM1:
152+ tcfg1 &= ~S3C2410_TCFG1_MUX1_MASK;
153+ tcfg0 &= ~S3C2410_TCFG_PRESCALER0_MASK;
154+ break;
155+ case PWM2:
156+ tcfg1 &= ~S3C2410_TCFG1_MUX2_MASK;
157+ tcfg0 &= ~S3C2410_TCFG_PRESCALER1_MASK;
158+ break;
159+ case PWM3:
160+ tcfg1 &= ~S3C2410_TCFG1_MUX3_MASK;
161+ tcfg0 &= ~S3C2410_TCFG_PRESCALER1_MASK;
162+ break;
163+ case PWM4:
164+ /* timer four is not capable of doing PWM */
165+ break;
166+ default:
167+ clk_disable(pwm->pclk);
168+ clk_put(pwm->pclk);
169+ return -1;
170+ }
171+
172+ /* divider & scaler values */
173+ tcfg1 |= pwm->divider;
174+ __raw_writel(tcfg1, S3C2410_TCFG1);
175+
176+ switch (pwm->timerid) {
177+ case PWM0:
178+ case PWM1:
179+ tcfg0 |= pwm->prescaler;
180+ __raw_writel(tcfg0, S3C2410_TCFG0);
181+ break;
182+ default:
183+ if ((tcfg0 | pwm->prescaler) != tcfg0) {
184+ printk(KERN_WARNING "not changing prescaler of PWM %u,"
185+ " since it's shared with timer4 (clock tick)\n",
186+ pwm->timerid);
187+ }
188+ break;
189+ }
190+
191+ /* timer count and compare buffer initial values */
192+ tcnt = pwm->counter;
193+ tcmp = pwm->comparer;
194+
195+ __raw_writel(tcnt, S3C2410_TCNTB(pwm->timerid));
196+ __raw_writel(tcmp, S3C2410_TCMPB(pwm->timerid));
197+
198+ /* ensure timer is stopped */
199+ s3c2410_pwm_stop(pwm);
200+
201+ return 0;
202+}
203+EXPORT_SYMBOL_GPL(s3c2410_pwm_enable);
204+
205+int s3c2410_pwm_start(struct s3c2410_pwm *pwm)
206+{
207+ unsigned long tcon;
208+
209+ tcon = __raw_readl(S3C2410_TCON);
210+
211+ switch (pwm->timerid) {
212+ case PWM0:
213+ tcon |= S3C2410_TCON_T0START;
214+ tcon &= ~S3C2410_TCON_T0MANUALUPD;
215+ break;
216+ case PWM1:
217+ tcon |= S3C2410_TCON_T1START;
218+ tcon &= ~S3C2410_TCON_T1MANUALUPD;
219+ break;
220+ case PWM2:
221+ tcon |= S3C2410_TCON_T2START;
222+ tcon &= ~S3C2410_TCON_T2MANUALUPD;
223+ break;
224+ case PWM3:
225+ tcon |= S3C2410_TCON_T3START;
226+ tcon &= ~S3C2410_TCON_T3MANUALUPD;
227+ break;
228+ case PWM4:
229+ /* timer four is not capable of doing PWM */
230+ default:
231+ return -ENODEV;
232+ }
233+
234+ __raw_writel(tcon, S3C2410_TCON);
235+
236+ return 0;
237+}
238+EXPORT_SYMBOL_GPL(s3c2410_pwm_start);
239+
240+int s3c2410_pwm_stop(struct s3c2410_pwm *pwm)
241+{
242+ unsigned long tcon;
243+
244+ tcon = __raw_readl(S3C2410_TCON);
245+
246+ switch (pwm->timerid) {
247+ case PWM0:
248+ tcon &= ~0x00000000;
249+ tcon |= S3C2410_TCON_T0RELOAD;
250+ tcon |= S3C2410_TCON_T0MANUALUPD;
251+ break;
252+ case PWM1:
253+ tcon &= ~0x00000080;
254+ tcon |= S3C2410_TCON_T1RELOAD;
255+ tcon |= S3C2410_TCON_T1MANUALUPD;
256+ break;
257+ case PWM2:
258+ tcon &= ~0x00000800;
259+ tcon |= S3C2410_TCON_T2RELOAD;
260+ tcon |= S3C2410_TCON_T2MANUALUPD;
261+ break;
262+ case PWM3:
263+ tcon &= ~0x00008000;
264+ tcon |= S3C2410_TCON_T3RELOAD;
265+ tcon |= S3C2410_TCON_T3MANUALUPD;
266+ break;
267+ case PWM4:
268+ /* timer four is not capable of doing PWM */
269+ default:
270+ return -ENODEV;
271+ }
272+
273+ __raw_writel(tcon, S3C2410_TCON);
274+
275+ return 0;
276+}
277+EXPORT_SYMBOL_GPL(s3c2410_pwm_stop);
278+
279+int s3c2410_pwm_duty_cycle(int reg_value, struct s3c2410_pwm *pwm)
280+{
281+ __raw_writel(reg_value, S3C2410_TCMPB(pwm->timerid));
282+
283+ return 0;
284+}
285+EXPORT_SYMBOL_GPL(s3c2410_pwm_duty_cycle);
286+
287+int s3c2410_pwm_dumpregs(void)
288+{
289+ printk(KERN_INFO "TCON: %08lx, TCFG0: %08lx, TCFG1: %08lx\n",
290+ (unsigned long) __raw_readl(S3C2410_TCON),
291+ (unsigned long) __raw_readl(S3C2410_TCFG0),
292+ (unsigned long) __raw_readl(S3C2410_TCFG1));
293+
294+ return 0;
295+}
296+EXPORT_SYMBOL_GPL(s3c2410_pwm_dumpregs);
297+
298+static int __init s3c24xx_pwm_probe(struct platform_device *pdev)
299+{
300+ struct s3c24xx_pwm_platform_data *pdata = pdev->dev.platform_data;
301+
302+ dev_info(&pdev->dev, "s3c24xx_pwm is registered \n");
303+
304+ /* if platform was interested, give him a chance to register
305+ * platform devices that switch power with us as the parent
306+ * at registration time -- ensures suspend / resume ordering
307+ */
308+ if (pdata)
309+ if (pdata->attach_child_devices)
310+ (pdata->attach_child_devices)(&pdev->dev);
311+
312+ return 0;
313+}
314+
315+#ifdef CONFIG_PM
316+static int s3c24xx_pwm_suspend(struct platform_device *pdev, pm_message_t state)
317+{
318+ /* PWM config should be kept in suspending */
319+ standby_reg_tcon = __raw_readl(S3C2410_TCON);
320+ standby_reg_tcfg0 = __raw_readl(S3C2410_TCFG0);
321+ standby_reg_tcfg1 = __raw_readl(S3C2410_TCFG1);
322+
323+ return 0;
324+}
325+
326+static int s3c24xx_pwm_resume(struct platform_device *pdev)
327+{
328+ __raw_writel(standby_reg_tcon, S3C2410_TCON);
329+ __raw_writel(standby_reg_tcfg0, S3C2410_TCFG0);
330+ __raw_writel(standby_reg_tcfg1, S3C2410_TCFG1);
331+
332+ return 0;
333+}
334+#else
335+#define s3c24xx_pwm_suspend NULL
336+#define s3c24xx_pwm_resume NULL
337+#endif
338+
339+static struct platform_driver s3c24xx_pwm_driver = {
340+ .driver = {
341+ .name = "s3c24xx_pwm",
342+ .owner = THIS_MODULE,
343+ },
344+ .probe = s3c24xx_pwm_probe,
345+ .suspend = s3c24xx_pwm_suspend,
346+ .resume = s3c24xx_pwm_resume,
347+};
348+
349+static int __init s3c24xx_pwm_init(void)
350+{
351+ return platform_driver_register(&s3c24xx_pwm_driver);
352+}
353+
354+static void __exit s3c24xx_pwm_exit(void)
355+{
356+}
357+
358+MODULE_AUTHOR("Javi Roman <javiroman@kernel-labs.org>");
359+MODULE_LICENSE("GPL");
360+
361+module_init(s3c24xx_pwm_init);
362+module_exit(s3c24xx_pwm_exit);
363--- /dev/null
364+++ b/arch/arm/plat-s3c24xx/pwm-clock.c
365@@ -0,0 +1,437 @@
366+/* linux/arch/arm/plat-s3c24xx/pwm-clock.c
367+ *
368+ * Copyright (c) 2007 Simtec Electronics
369+ * Copyright (c) 2007, 2008 Ben Dooks
370+ * Ben Dooks <ben-linux@fluff.org>
371+ *
372+ * This program is free software; you can redistribute it and/or modify
373+ * it under the terms of the GNU General Public License as published by
374+ * the Free Software Foundation; either version 2 of the License.
375+*/
376+
377+#include <linux/init.h>
378+#include <linux/module.h>
379+#include <linux/kernel.h>
380+#include <linux/list.h>
381+#include <linux/errno.h>
382+#include <linux/clk.h>
383+#include <linux/err.h>
384+#include <linux/io.h>
385+
386+#include <mach/hardware.h>
387+#include <asm/irq.h>
388+
389+#include <mach/regs-clock.h>
390+#include <mach/regs-gpio.h>
391+
392+#include <asm/plat-s3c24xx/clock.h>
393+#include <asm/plat-s3c24xx/cpu.h>
394+
395+#include <asm/plat-s3c/regs-timer.h>
396+
397+/* Each of the timers 0 through 5 go through the following
398+ * clock tree, with the inputs depending on the timers.
399+ *
400+ * pclk ---- [ prescaler 0 ] -+---> timer 0
401+ * +---> timer 1
402+ *
403+ * pclk ---- [ prescaler 1 ] -+---> timer 2
404+ * +---> timer 3
405+ * \---> timer 4
406+ *
407+ * Which are fed into the timers as so:
408+ *
409+ * prescaled 0 ---- [ div 2,4,8,16 ] ---\
410+ * [mux] -> timer 0
411+ * tclk 0 ------------------------------/
412+ *
413+ * prescaled 0 ---- [ div 2,4,8,16 ] ---\
414+ * [mux] -> timer 1
415+ * tclk 0 ------------------------------/
416+ *
417+ *
418+ * prescaled 1 ---- [ div 2,4,8,16 ] ---\
419+ * [mux] -> timer 2
420+ * tclk 1 ------------------------------/
421+ *
422+ * prescaled 1 ---- [ div 2,4,8,16 ] ---\
423+ * [mux] -> timer 3
424+ * tclk 1 ------------------------------/
425+ *
426+ * prescaled 1 ---- [ div 2,4,8, 16 ] --\
427+ * [mux] -> timer 4
428+ * tclk 1 ------------------------------/
429+ *
430+ * Since the mux and the divider are tied together in the
431+ * same register space, it is impossible to set the parent
432+ * and the rate at the same time. To avoid this, we add an
433+ * intermediate 'prescaled-and-divided' clock to select
434+ * as the parent for the timer input clock called tdiv.
435+ *
436+ * prescaled clk --> pwm-tdiv ---\
437+ * [ mux ] --> timer X
438+ * tclk -------------------------/
439+*/
440+
441+static unsigned long clk_pwm_scaler_getrate(struct clk *clk)
442+{
443+ unsigned long tcfg0 = __raw_readl(S3C2410_TCFG0);
444+
445+ if (clk->id == 1) {
446+ tcfg0 &= S3C2410_TCFG_PRESCALER1_MASK;
447+ tcfg0 >>= S3C2410_TCFG_PRESCALER1_SHIFT;
448+ } else {
449+ tcfg0 &= S3C2410_TCFG_PRESCALER0_MASK;
450+ }
451+
452+ return clk_get_rate(clk->parent) / (tcfg0 + 1);
453+}
454+
455+/* TODO - add set rate calls. */
456+
457+static struct clk clk_timer_scaler[] = {
458+ [0] = {
459+ .name = "pwm-scaler0",
460+ .id = -1,
461+ .get_rate = clk_pwm_scaler_getrate,
462+ },
463+ [1] = {
464+ .name = "pwm-scaler1",
465+ .id = -1,
466+ .get_rate = clk_pwm_scaler_getrate,
467+ },
468+};
469+
470+static struct clk clk_timer_tclk[] = {
471+ [0] = {
472+ .name = "pwm-tclk0",
473+ .id = -1,
474+ },
475+ [1] = {
476+ .name = "pwm-tclk1",
477+ .id = -1,
478+ },
479+};
480+
481+struct pwm_tdiv_clk {
482+ struct clk clk;
483+ unsigned int divisor;
484+};
485+
486+static inline struct pwm_tdiv_clk *to_tdiv(struct clk *clk)
487+{
488+ return container_of(clk, struct pwm_tdiv_clk, clk);
489+}
490+
491+static inline unsigned long tcfg_to_divisor(unsigned long tcfg1)
492+{
493+ return 1 << (1 + tcfg1);
494+}
495+
496+static unsigned long clk_pwm_tdiv_get_rate(struct clk *clk)
497+{
498+ unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1);
499+ unsigned int divisor;
500+
501+ tcfg1 >>= S3C2410_TCFG1_SHIFT(clk->id);
502+ tcfg1 &= S3C2410_TCFG1_MUX_MASK;
503+
504+ if (tcfg1 == S3C2410_TCFG1_MUX_TCLK)
505+ divisor = to_tdiv(clk)->divisor;
506+ else
507+ divisor = tcfg_to_divisor(tcfg1);
508+
509+ return clk_get_rate(clk->parent) / divisor;
510+}
511+
512+static unsigned long clk_pwm_tdiv_round_rate(struct clk *clk,
513+ unsigned long rate)
514+{
515+ unsigned long parent_rate;
516+ unsigned long divisor;
517+
518+ parent_rate = clk_get_rate(clk->parent);
519+ divisor = parent_rate / rate;
520+
521+ if (divisor <= 2)
522+ divisor = 2;
523+ else if (divisor <= 4)
524+ divisor = 4;
525+ else if (divisor <= 8)
526+ divisor = 8;
527+ else
528+ divisor = 16;
529+
530+ return parent_rate / divisor;
531+}
532+
533+static unsigned long clk_pwm_tdiv_bits(struct pwm_tdiv_clk *divclk)
534+{
535+ unsigned long bits;
536+
537+ switch (divclk->divisor) {
538+ case 2:
539+ bits = S3C2410_TCFG1_MUX_DIV2;
540+ break;
541+ case 4:
542+ bits = S3C2410_TCFG1_MUX_DIV4;
543+ break;
544+ case 8:
545+ bits = S3C2410_TCFG1_MUX_DIV8;
546+ break;
547+ case 16:
548+ default:
549+ bits = S3C2410_TCFG1_MUX_DIV16;
550+ break;
551+ }
552+
553+ return bits;
554+}
555+
556+static void clk_pwm_tdiv_update(struct pwm_tdiv_clk *divclk)
557+{
558+ unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1);
559+ unsigned long bits = clk_pwm_tdiv_bits(divclk);
560+ unsigned long flags;
561+ unsigned long shift = S3C2410_TCFG1_SHIFT(divclk->clk.id);
562+
563+ local_irq_save(flags);
564+
565+ tcfg1 = __raw_readl(S3C2410_TCFG1);
566+ tcfg1 &= ~(S3C2410_TCFG1_MUX_MASK << shift);
567+ tcfg1 |= bits << shift;
568+ __raw_writel(tcfg1, S3C2410_TCFG1);
569+
570+ local_irq_restore(flags);
571+}
572+
573+static int clk_pwm_tdiv_set_rate(struct clk *clk, unsigned long rate)
574+{
575+ struct pwm_tdiv_clk *divclk = to_tdiv(clk);
576+ unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1);
577+ unsigned long parent_rate = clk_get_rate(clk->parent);
578+ unsigned long divisor;
579+
580+ tcfg1 >>= S3C2410_TCFG1_SHIFT(clk->id);
581+ tcfg1 &= S3C2410_TCFG1_MUX_MASK;
582+
583+ rate = clk_round_rate(clk, rate);
584+ divisor = parent_rate / rate;
585+
586+ if (divisor > 16)
587+ return -EINVAL;
588+
589+ divclk->divisor = divisor;
590+
591+ /* Update the current MUX settings if we are currently
592+ * selected as the clock source for this clock. */
593+
594+ if (tcfg1 != S3C2410_TCFG1_MUX_TCLK)
595+ clk_pwm_tdiv_update(divclk);
596+
597+ return 0;
598+}
599+
600+static struct pwm_tdiv_clk clk_timer_tdiv[] = {
601+ [0] = {
602+ .clk = {
603+ .name = "pwm-tdiv",
604+ .parent = &clk_timer_scaler[0],
605+ .get_rate = clk_pwm_tdiv_get_rate,
606+ .set_rate = clk_pwm_tdiv_set_rate,
607+ .round_rate = clk_pwm_tdiv_round_rate,
608+ },
609+ },
610+ [1] = {
611+ .clk = {
612+ .name = "pwm-tdiv",
613+ .parent = &clk_timer_scaler[0],
614+ .get_rate = clk_pwm_tdiv_get_rate,
615+ .set_rate = clk_pwm_tdiv_set_rate,
616+ .round_rate = clk_pwm_tdiv_round_rate,
617+ }
618+ },
619+ [2] = {
620+ .clk = {
621+ .name = "pwm-tdiv",
622+ .parent = &clk_timer_scaler[1],
623+ .get_rate = clk_pwm_tdiv_get_rate,
624+ .set_rate = clk_pwm_tdiv_set_rate,
625+ .round_rate = clk_pwm_tdiv_round_rate,
626+ },
627+ },
628+ [3] = {
629+ .clk = {
630+ .name = "pwm-tdiv",
631+ .parent = &clk_timer_scaler[1],
632+ .get_rate = clk_pwm_tdiv_get_rate,
633+ .set_rate = clk_pwm_tdiv_set_rate,
634+ .round_rate = clk_pwm_tdiv_round_rate,
635+ },
636+ },
637+ [4] = {
638+ .clk = {
639+ .name = "pwm-tdiv",
640+ .parent = &clk_timer_scaler[1],
641+ .get_rate = clk_pwm_tdiv_get_rate,
642+ .set_rate = clk_pwm_tdiv_set_rate,
643+ .round_rate = clk_pwm_tdiv_round_rate,
644+ },
645+ },
646+};
647+
648+static int __init clk_pwm_tdiv_register(unsigned int id)
649+{
650+ struct pwm_tdiv_clk *divclk = &clk_timer_tdiv[id];
651+ unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1);
652+
653+ tcfg1 >>= S3C2410_TCFG1_SHIFT(id);
654+ tcfg1 &= S3C2410_TCFG1_MUX_MASK;
655+
656+ divclk->clk.id = id;
657+ divclk->divisor = tcfg_to_divisor(tcfg1);
658+
659+ return s3c24xx_register_clock(&divclk->clk);
660+}
661+
662+static inline struct clk *s3c24xx_pwmclk_tclk(unsigned int id)
663+{
664+ return (id >= 2) ? &clk_timer_tclk[1] : &clk_timer_tclk[0];
665+}
666+
667+static inline struct clk *s3c24xx_pwmclk_tdiv(unsigned int id)
668+{
669+ return &clk_timer_tdiv[id].clk;
670+}
671+
672+static int clk_pwm_tin_set_parent(struct clk *clk, struct clk *parent)
673+{
674+ unsigned int id = clk->id;
675+ unsigned long tcfg1;
676+ unsigned long flags;
677+ unsigned long bits;
678+ unsigned long shift = S3C2410_TCFG1_SHIFT(id);
679+
680+ if (parent == s3c24xx_pwmclk_tclk(id))
681+ bits = S3C2410_TCFG1_MUX_TCLK << shift;
682+ else if (parent == s3c24xx_pwmclk_tdiv(id))
683+ bits = clk_pwm_tdiv_bits(to_tdiv(parent)) << shift;
684+ else
685+ return -EINVAL;
686+
687+ clk->parent = parent;
688+
689+ local_irq_save(flags);
690+
691+ tcfg1 = __raw_readl(S3C2410_TCFG1);
692+ tcfg1 &= ~(S3C2410_TCFG1_MUX_MASK << shift);
693+ __raw_writel(tcfg1 | bits, S3C2410_TCFG1);
694+
695+ local_irq_restore(flags);
696+
697+ return 0;
698+}
699+
700+static struct clk clk_tin[] = {
701+ [0] = {
702+ .name = "pwm-tin",
703+ .id = 0,
704+ .set_parent = clk_pwm_tin_set_parent,
705+ },
706+ [1] = {
707+ .name = "pwm-tin",
708+ .id = 1,
709+ .set_parent = clk_pwm_tin_set_parent,
710+ },
711+ [2] = {
712+ .name = "pwm-tin",
713+ .id = 2,
714+ .set_parent = clk_pwm_tin_set_parent,
715+ },
716+ [3] = {
717+ .name = "pwm-tin",
718+ .id = 3,
719+ .set_parent = clk_pwm_tin_set_parent,
720+ },
721+ [4] = {
722+ .name = "pwm-tin",
723+ .id = 4,
724+ .set_parent = clk_pwm_tin_set_parent,
725+ },
726+};
727+
728+static __init int clk_pwm_tin_register(struct clk *pwm)
729+{
730+ unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1);
731+ unsigned int id = pwm->id;
732+
733+ struct clk *parent;
734+ int ret;
735+
736+ ret = s3c24xx_register_clock(pwm);
737+ if (ret < 0)
738+ return ret;
739+
740+ tcfg1 >>= S3C2410_TCFG1_SHIFT(id);
741+ tcfg1 &= S3C2410_TCFG1_MUX_MASK;
742+
743+ if (tcfg1 == S3C2410_TCFG1_MUX_TCLK)
744+ parent = s3c24xx_pwmclk_tclk(id);
745+ else
746+ parent = s3c24xx_pwmclk_tdiv(id);
747+
748+ return clk_set_parent(pwm, parent);
749+}
750+
751+static __init int s3c24xx_pwmclk_init(void)
752+{
753+ struct clk *clk_timers;
754+ unsigned int clk;
755+ int ret;
756+
757+ clk_timers = clk_get(NULL, "timers");
758+ if (IS_ERR(clk_timers)) {
759+ printk(KERN_ERR "%s: no parent clock\n", __func__);
760+ return -EINVAL;
761+ }
762+
763+ for (clk = 0; clk < ARRAY_SIZE(clk_timer_scaler); clk++) {
764+ clk_timer_scaler[clk].parent = clk_timers;
765+ ret = s3c24xx_register_clock(&clk_timer_scaler[clk]);
766+ if (ret < 0) {
767+ printk(KERN_ERR "error adding pwm scaler%d clock\n", clk);
768+ goto err;
769+ }
770+ }
771+
772+ for (clk = 0; clk < ARRAY_SIZE(clk_timer_tclk); clk++) {
773+ ret = s3c24xx_register_clock(&clk_timer_tclk[clk]);
774+ if (ret < 0) {
775+ printk(KERN_ERR "error adding pww tclk%d\n", clk);
776+ goto err;
777+ }
778+ }
779+
780+ for (clk = 0; clk < ARRAY_SIZE(clk_timer_tdiv); clk++) {
781+ ret = clk_pwm_tdiv_register(clk);
782+ if (ret < 0) {
783+ printk(KERN_ERR "error adding pwm%d tdiv clock\n", clk);
784+ goto err;
785+ }
786+ }
787+
788+ for (clk = 0; clk < ARRAY_SIZE(clk_tin); clk++) {
789+ ret = clk_pwm_tin_register(&clk_tin[clk]);
790+ if (ret < 0) {
791+ printk(KERN_ERR "error adding pwm%d tin clock\n", clk);
792+ goto err;
793+ }
794+ }
795+
796+ return 0;
797+
798+ err:
799+ return ret;
800+}
801+
802+arch_initcall(s3c24xx_pwmclk_init);
803

Archive Download this file



interactive