]>
git.ipfire.org Git - thirdparty/u-boot.git/blob - arch/arm/cpu/armv7/s5p-common/pwm.c
1 // SPDX-License-Identifier: GPL-2.0+
3 * Copyright (C) 2011 Samsung Electronics
5 * Donghwa Lee <dh09.lee@samsung.com>
12 #include <asm/arch/pwm.h>
13 #include <asm/arch/clk.h>
15 int pwm_enable(int pwm_id
)
17 const struct s5p_timer
*pwm
=
18 (struct s5p_timer
*)samsung_get_base_timer();
21 tcon
= readl(&pwm
->tcon
);
22 tcon
|= TCON_START(pwm_id
);
24 writel(tcon
, &pwm
->tcon
);
29 void pwm_disable(int pwm_id
)
31 const struct s5p_timer
*pwm
=
32 (struct s5p_timer
*)samsung_get_base_timer();
35 tcon
= readl(&pwm
->tcon
);
36 tcon
&= ~TCON_START(pwm_id
);
38 writel(tcon
, &pwm
->tcon
);
41 static unsigned long pwm_calc_tin(int pwm_id
, unsigned long freq
)
43 unsigned long tin_parent_rate
;
46 tin_parent_rate
= get_pwm_clk();
48 for (div
= 2; div
<= 16; div
*= 2) {
49 if ((tin_parent_rate
/ (div
<< 16)) < freq
)
50 return tin_parent_rate
/ div
;
53 return tin_parent_rate
/ 16;
56 #define NS_IN_SEC 1000000000UL
58 int pwm_config(int pwm_id
, int duty_ns
, int period_ns
)
60 const struct s5p_timer
*pwm
=
61 (struct s5p_timer
*)samsung_get_base_timer();
63 unsigned long tin_rate
;
65 unsigned long frequency
;
71 * We currently avoid using 64bit arithmetic by using the
72 * fact that anything faster than 1GHz is easily representable
75 if (period_ns
> NS_IN_SEC
|| duty_ns
> NS_IN_SEC
|| period_ns
== 0)
78 if (duty_ns
> period_ns
)
81 frequency
= NS_IN_SEC
/ period_ns
;
83 /* Check to see if we are changing the clock rate of the PWM */
84 tin_rate
= pwm_calc_tin(pwm_id
, frequency
);
86 tin_ns
= NS_IN_SEC
/ tin_rate
;
87 tcnt
= period_ns
/ tin_ns
;
89 /* Note, counters count down */
90 tcmp
= duty_ns
/ tin_ns
;
93 /* Update the PWM register block. */
96 writel(tcnt
, &pwm
->tcntb0
+ offset
);
97 writel(tcmp
, &pwm
->tcmpb0
+ offset
);
100 tcon
= readl(&pwm
->tcon
);
101 tcon
|= TCON_UPDATE(pwm_id
);
103 tcon
|= TCON_AUTO_RELOAD(pwm_id
);
105 tcon
|= TCON4_AUTO_RELOAD
;
106 writel(tcon
, &pwm
->tcon
);
108 tcon
&= ~TCON_UPDATE(pwm_id
);
109 writel(tcon
, &pwm
->tcon
);
114 int pwm_init(int pwm_id
, int div
, int invert
)
117 const struct s5p_timer
*pwm
=
118 (struct s5p_timer
*)samsung_get_base_timer();
119 unsigned long ticks_per_period
;
120 unsigned int offset
, prescaler
;
124 * PWM_CLK / { (prescaler_value + 1) * (divider_value) }
127 val
= readl(&pwm
->tcfg0
);
129 prescaler
= PRESCALER_0
;
131 val
|= (prescaler
& 0xff);
133 prescaler
= PRESCALER_1
;
135 val
|= (prescaler
& 0xff) << 8;
137 writel(val
, &pwm
->tcfg0
);
138 val
= readl(&pwm
->tcfg1
);
139 val
&= ~(0xf << MUX_DIV_SHIFT(pwm_id
));
140 val
|= (div
& 0xf) << MUX_DIV_SHIFT(pwm_id
);
141 writel(val
, &pwm
->tcfg1
);
145 * TODO(sjg): Use this as a countdown timer for now. We count
146 * down from the maximum value to 0, then reset.
148 ticks_per_period
= -1UL;
150 const unsigned long pwm_hz
= 1000;
151 unsigned long timer_rate_hz
= get_pwm_clk() /
152 ((prescaler
+ 1) * (1 << div
));
154 ticks_per_period
= timer_rate_hz
/ pwm_hz
;
157 /* set count value */
160 writel(ticks_per_period
, &pwm
->tcntb0
+ offset
);
162 val
= readl(&pwm
->tcon
) & ~(0xf << TCON_OFFSET(pwm_id
));
163 if (invert
&& (pwm_id
< 4))
164 val
|= TCON_INVERTER(pwm_id
);
165 writel(val
, &pwm
->tcon
);