Merge branch 'master' of https://source.denx.de/u-boot/custodians/u-boot-riscv
[platform/kernel/u-boot.git] / drivers / pwm / pwm-imx.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * (C) Copyright 2014
4  * Heiko Schocher, DENX Software Engineering, hs@denx.de.
5  *
6  * Basic support for the pwm module on imx6.
7  */
8
9 #include <common.h>
10 #include <div64.h>
11 #include <dm.h>
12 #include <log.h>
13 #include <pwm.h>
14 #include <asm/arch/imx-regs.h>
15 #include <asm/io.h>
16 #include <clk.h>
17
18 int pwm_config_internal(struct pwm_regs *pwm, unsigned long period_cycles,
19                         unsigned long duty_cycles, unsigned long prescale)
20 {
21         u32 cr;
22
23         writel(0, &pwm->ir);
24         cr = PWMCR_PRESCALER(prescale) |
25                 PWMCR_DOZEEN | PWMCR_WAITEN |
26                 PWMCR_DBGEN | PWMCR_CLKSRC_IPG_HIGH;
27
28         writel(cr, &pwm->cr);
29         /* set duty cycles */
30         writel(duty_cycles, &pwm->sar);
31         /* set period cycles */
32         writel(period_cycles, &pwm->pr);
33         return 0;
34 }
35
36 #ifndef CONFIG_DM_PWM
37 /* pwm_id from 0..7 */
38 struct pwm_regs *pwm_id_to_reg(int pwm_id)
39 {
40
41         switch (pwm_id) {
42         case 0:
43                 return (struct pwm_regs *)PWM1_BASE_ADDR;
44         case 1:
45                 return (struct pwm_regs *)PWM2_BASE_ADDR;
46 #ifdef CONFIG_MX6
47         case 2:
48                 return (struct pwm_regs *)PWM3_BASE_ADDR;
49         case 3:
50                 return (struct pwm_regs *)PWM4_BASE_ADDR;
51 #endif
52 #ifdef CONFIG_MX6SX
53         case 4:
54                 return (struct pwm_regs *)PWM5_BASE_ADDR;
55         case 5:
56                 return (struct pwm_regs *)PWM6_BASE_ADDR;
57         case 6:
58                 return (struct pwm_regs *)PWM7_BASE_ADDR;
59         case 7:
60                 return (struct pwm_regs *)PWM8_BASE_ADDR;
61 #endif
62         default:
63                 printf("unknown pwm_id: %d\n", pwm_id);
64                 break;
65         }
66         return NULL;
67 }
68
69 int pwm_imx_get_parms(int period_ns, int duty_ns, unsigned long *period_c,
70                       unsigned long *duty_c, unsigned long *prescale)
71 {
72         unsigned long long c;
73
74         /*
75          * we have not yet a clock framework for imx6, so add the clock
76          * value here as a define. Replace it when we have the clock
77          * framework.
78          */
79         c = CONFIG_IMX6_PWM_PER_CLK;
80         c = c * period_ns;
81         do_div(c, 1000000000);
82         *period_c = c;
83
84         *prescale = *period_c / 0x10000 + 1;
85
86         *period_c /= *prescale;
87         c = *period_c * (unsigned long long)duty_ns;
88         do_div(c, period_ns);
89         *duty_c = c;
90
91         /*
92          * according to imx pwm RM, the real period value should be
93          * PERIOD value in PWMPR plus 2.
94          */
95         if (*period_c > 2)
96                 *period_c -= 2;
97         else
98                 *period_c = 0;
99
100         return 0;
101 }
102
103 int pwm_init(int pwm_id, int div, int invert)
104 {
105         struct pwm_regs *pwm = (struct pwm_regs *)pwm_id_to_reg(pwm_id);
106
107         if (!pwm)
108                 return -1;
109
110         writel(0, &pwm->ir);
111         return 0;
112 }
113
114 int pwm_config(int pwm_id, int duty_ns, int period_ns)
115 {
116         struct pwm_regs *pwm = (struct pwm_regs *)pwm_id_to_reg(pwm_id);
117         unsigned long period_cycles, duty_cycles, prescale;
118
119         if (!pwm)
120                 return -1;
121
122         pwm_imx_get_parms(period_ns, duty_ns, &period_cycles, &duty_cycles,
123                           &prescale);
124
125         return pwm_config_internal(pwm, period_cycles, duty_cycles, prescale);
126 }
127
128 int pwm_enable(int pwm_id)
129 {
130         struct pwm_regs *pwm = (struct pwm_regs *)pwm_id_to_reg(pwm_id);
131
132         if (!pwm)
133                 return -1;
134
135         setbits_le32(&pwm->cr, PWMCR_EN);
136         return 0;
137 }
138
139 void pwm_disable(int pwm_id)
140 {
141         struct pwm_regs *pwm = (struct pwm_regs *)pwm_id_to_reg(pwm_id);
142
143         if (!pwm)
144                 return;
145
146         clrbits_le32(&pwm->cr, PWMCR_EN);
147 }
148
149 #else
150 struct imx_pwm_priv {
151         struct pwm_regs *regs;
152         bool invert;
153         struct clk per_clk;
154         struct clk ipg_clk;
155 };
156
157 int pwm_dm_imx_get_parms(struct imx_pwm_priv *priv, int period_ns,
158                       int duty_ns, unsigned long *period_c, unsigned long *duty_c,
159                       unsigned long *prescale)
160 {
161         unsigned long long c;
162
163         c = clk_get_rate(&priv->per_clk);
164         c = c * period_ns;
165         do_div(c, 1000000000);
166         *period_c = c;
167
168         *prescale = *period_c / 0x10000 + 1;
169
170         *period_c /= *prescale;
171         c = *period_c * (unsigned long long)duty_ns;
172         do_div(c, period_ns);
173         *duty_c = c;
174
175         /*
176          * according to imx pwm RM, the real period value should be
177          * PERIOD value in PWMPR plus 2.
178          */
179         if (*period_c > 2)
180                 *period_c -= 2;
181         else
182                 *period_c = 0;
183
184         return 0;
185 }
186
187 static int imx_pwm_set_invert(struct udevice *dev, uint channel,
188                               bool polarity)
189 {
190         struct imx_pwm_priv *priv = dev_get_priv(dev);
191
192         debug("%s: polarity=%u\n", __func__, polarity);
193         priv->invert = polarity;
194
195         return 0;
196 }
197
198 static int imx_pwm_set_config(struct udevice *dev, uint channel,
199                               uint period_ns, uint duty_ns)
200 {
201         struct imx_pwm_priv *priv = dev_get_priv(dev);
202         struct pwm_regs *regs = priv->regs;
203         unsigned long period_cycles, duty_cycles, prescale;
204
205         debug("%s: Config '%s' channel: %d\n", __func__, dev->name, channel);
206
207         pwm_dm_imx_get_parms(priv, period_ns, duty_ns, &period_cycles, &duty_cycles,
208                           &prescale);
209
210         return pwm_config_internal(regs, period_cycles, duty_cycles, prescale);
211 };
212
213 static int imx_pwm_set_enable(struct udevice *dev, uint channel, bool enable)
214 {
215         struct imx_pwm_priv *priv = dev_get_priv(dev);
216         struct pwm_regs *regs = priv->regs;
217
218         debug("%s: Enable '%s' state: %d\n", __func__, dev->name, enable);
219
220         if (enable)
221                 setbits_le32(&regs->cr, PWMCR_EN);
222         else
223                 clrbits_le32(&regs->cr, PWMCR_EN);
224
225         return 0;
226 };
227
228 static int imx_pwm_of_to_plat(struct udevice *dev)
229 {
230         int ret;
231         struct imx_pwm_priv *priv = dev_get_priv(dev);
232
233         priv->regs = dev_read_addr_ptr(dev);
234
235         ret = clk_get_by_name(dev, "per", &priv->per_clk);
236         if (ret) {
237                 printf("Failed to get per_clk\n");
238                 return ret;
239         }
240
241         ret = clk_get_by_name(dev, "ipg", &priv->ipg_clk);
242         if (ret) {
243                 printf("Failed to get ipg_clk\n");
244                 return ret;
245         }
246
247         return 0;
248 }
249
250 static int imx_pwm_probe(struct udevice *dev)
251 {
252         int ret;
253         struct imx_pwm_priv *priv = dev_get_priv(dev);
254
255         ret = clk_enable(&priv->per_clk);
256         if (ret) {
257                 printf("Failed to enable per_clk\n");
258                 return ret;
259         }
260
261         ret = clk_enable(&priv->ipg_clk);
262         if (ret) {
263                 printf("Failed to enable ipg_clk\n");
264                 return ret;
265         }
266
267         return 0;
268 }
269
270 static const struct pwm_ops imx_pwm_ops = {
271         .set_invert     = imx_pwm_set_invert,
272         .set_config     = imx_pwm_set_config,
273         .set_enable     = imx_pwm_set_enable,
274 };
275
276 static const struct udevice_id imx_pwm_ids[] = {
277         { .compatible = "fsl,imx27-pwm" },
278         { }
279 };
280
281 U_BOOT_DRIVER(imx_pwm) = {
282         .name   = "imx_pwm",
283         .id     = UCLASS_PWM,
284         .of_match = imx_pwm_ids,
285         .ops    = &imx_pwm_ops,
286         .of_to_plat     = imx_pwm_of_to_plat,
287         .probe          = imx_pwm_probe,
288         .priv_auto      = sizeof(struct imx_pwm_priv),
289 };
290 #endif