blob: ce1290aa7dfa301dfcd8a63c29863d8da3c1af61 [file] [log] [blame]
Tom Rini83d290c2018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Heiko Schocherb2f97cf2014-07-18 06:07:19 +02002/*
3 * (C) Copyright 2014
4 * Heiko Schocher, DENX Software Engineering, hs@denx.de.
5 *
Robert P. J. Day5052e812016-09-13 08:35:18 -04006 * Basic support for the pwm module on imx6.
Heiko Schocherb2f97cf2014-07-18 06:07:19 +02007 */
8
9#include <common.h>
10#include <div64.h>
Heiko Schocher46e10e92019-05-28 06:51:52 +020011#include <dm.h>
Simon Glassf7ae49f2020-05-10 11:40:05 -060012#include <log.h>
Heiko Schocherb2f97cf2014-07-18 06:07:19 +020013#include <pwm.h>
14#include <asm/arch/imx-regs.h>
15#include <asm/io.h>
16#include "pwm-imx-util.h"
17
18int pwm_init(int pwm_id, int div, int invert)
19{
20 struct pwm_regs *pwm = (struct pwm_regs *)pwm_id_to_reg(pwm_id);
21
Axel Lin16b0c0c2015-05-23 15:16:48 +080022 if (!pwm)
23 return -1;
24
Heiko Schocherb2f97cf2014-07-18 06:07:19 +020025 writel(0, &pwm->ir);
26 return 0;
27}
Tommaso Merciaibfc778c2022-03-26 12:19:06 +010028#include <clk.h>
Heiko Schocherb2f97cf2014-07-18 06:07:19 +020029
Heiko Schocher46e10e92019-05-28 06:51:52 +020030int pwm_config_internal(struct pwm_regs *pwm, unsigned long period_cycles,
31 unsigned long duty_cycles, unsigned long prescale)
Heiko Schocherb2f97cf2014-07-18 06:07:19 +020032{
Heiko Schocherb2f97cf2014-07-18 06:07:19 +020033 u32 cr;
34
Heiko Schocher46e10e92019-05-28 06:51:52 +020035 writel(0, &pwm->ir);
Heiko Schocherb2f97cf2014-07-18 06:07:19 +020036 cr = PWMCR_PRESCALER(prescale) |
37 PWMCR_DOZEEN | PWMCR_WAITEN |
38 PWMCR_DBGEN | PWMCR_CLKSRC_IPG_HIGH;
39
40 writel(cr, &pwm->cr);
41 /* set duty cycles */
42 writel(duty_cycles, &pwm->sar);
43 /* set period cycles */
44 writel(period_cycles, &pwm->pr);
45 return 0;
46}
47
Heiko Schocher46e10e92019-05-28 06:51:52 +020048int pwm_config(int pwm_id, int duty_ns, int period_ns)
49{
50 struct pwm_regs *pwm = (struct pwm_regs *)pwm_id_to_reg(pwm_id);
51 unsigned long period_cycles, duty_cycles, prescale;
52
53 if (!pwm)
54 return -1;
55
56 pwm_imx_get_parms(period_ns, duty_ns, &period_cycles, &duty_cycles,
57 &prescale);
58
59 return pwm_config_internal(pwm, period_cycles, duty_cycles, prescale);
60}
61
Heiko Schocherb2f97cf2014-07-18 06:07:19 +020062int pwm_enable(int pwm_id)
63{
64 struct pwm_regs *pwm = (struct pwm_regs *)pwm_id_to_reg(pwm_id);
65
Axel Lin16b0c0c2015-05-23 15:16:48 +080066 if (!pwm)
67 return -1;
68
Heiko Schocherb2f97cf2014-07-18 06:07:19 +020069 setbits_le32(&pwm->cr, PWMCR_EN);
70 return 0;
71}
72
73void pwm_disable(int pwm_id)
74{
75 struct pwm_regs *pwm = (struct pwm_regs *)pwm_id_to_reg(pwm_id);
76
Axel Lin16b0c0c2015-05-23 15:16:48 +080077 if (!pwm)
78 return;
79
Heiko Schocherb2f97cf2014-07-18 06:07:19 +020080 clrbits_le32(&pwm->cr, PWMCR_EN);
81}
Heiko Schocher46e10e92019-05-28 06:51:52 +020082
83#if defined(CONFIG_DM_PWM)
84struct imx_pwm_priv {
85 struct pwm_regs *regs;
86 bool invert;
Tommaso Merciaibfc778c2022-03-26 12:19:06 +010087 struct clk per_clk;
88 struct clk ipg_clk;
Heiko Schocher46e10e92019-05-28 06:51:52 +020089};
90
Tommaso Merciai6828b3e2022-03-26 12:19:07 +010091int pwm_dm_imx_get_parms(struct imx_pwm_priv *priv, int period_ns,
92 int duty_ns, unsigned long *period_c, unsigned long *duty_c,
93 unsigned long *prescale)
94{
95 unsigned long long c;
96
97 c = clk_get_rate(&priv->per_clk);
98 c = c * period_ns;
99 do_div(c, 1000000000);
100 *period_c = c;
101
102 *prescale = *period_c / 0x10000 + 1;
103
104 *period_c /= *prescale;
105 c = *period_c * (unsigned long long)duty_ns;
106 do_div(c, period_ns);
107 *duty_c = c;
108
109 /*
110 * according to imx pwm RM, the real period value should be
111 * PERIOD value in PWMPR plus 2.
112 */
113 if (*period_c > 2)
114 *period_c -= 2;
115 else
116 *period_c = 0;
117
118 return 0;
119}
120
Heiko Schocher46e10e92019-05-28 06:51:52 +0200121static int imx_pwm_set_invert(struct udevice *dev, uint channel,
122 bool polarity)
123{
124 struct imx_pwm_priv *priv = dev_get_priv(dev);
125
126 debug("%s: polarity=%u\n", __func__, polarity);
127 priv->invert = polarity;
128
129 return 0;
130}
131
132static int imx_pwm_set_config(struct udevice *dev, uint channel,
133 uint period_ns, uint duty_ns)
134{
135 struct imx_pwm_priv *priv = dev_get_priv(dev);
136 struct pwm_regs *regs = priv->regs;
137 unsigned long period_cycles, duty_cycles, prescale;
138
139 debug("%s: Config '%s' channel: %d\n", __func__, dev->name, channel);
140
Tommaso Merciai6828b3e2022-03-26 12:19:07 +0100141 pwm_dm_imx_get_parms(priv, period_ns, duty_ns, &period_cycles, &duty_cycles,
Heiko Schocher46e10e92019-05-28 06:51:52 +0200142 &prescale);
143
144 return pwm_config_internal(regs, period_cycles, duty_cycles, prescale);
145};
146
147static int imx_pwm_set_enable(struct udevice *dev, uint channel, bool enable)
148{
149 struct imx_pwm_priv *priv = dev_get_priv(dev);
150 struct pwm_regs *regs = priv->regs;
151
152 debug("%s: Enable '%s' state: %d\n", __func__, dev->name, enable);
153
154 if (enable)
155 setbits_le32(&regs->cr, PWMCR_EN);
156 else
157 clrbits_le32(&regs->cr, PWMCR_EN);
158
159 return 0;
160};
161
Simon Glassd1998a92020-12-03 16:55:21 -0700162static int imx_pwm_of_to_plat(struct udevice *dev)
Heiko Schocher46e10e92019-05-28 06:51:52 +0200163{
Tommaso Merciaibfc778c2022-03-26 12:19:06 +0100164 int ret;
Heiko Schocher46e10e92019-05-28 06:51:52 +0200165 struct imx_pwm_priv *priv = dev_get_priv(dev);
166
Masahiro Yamada8613c8d2020-07-17 14:36:46 +0900167 priv->regs = dev_read_addr_ptr(dev);
Heiko Schocher46e10e92019-05-28 06:51:52 +0200168
Tommaso Merciaibfc778c2022-03-26 12:19:06 +0100169 ret = clk_get_by_name(dev, "per", &priv->per_clk);
170 if (ret) {
171 printf("Failed to get per_clk\n");
172 return ret;
173 }
174
175 ret = clk_get_by_name(dev, "ipg", &priv->ipg_clk);
176 if (ret) {
177 printf("Failed to get ipg_clk\n");
178 return ret;
179 }
180
Heiko Schocher46e10e92019-05-28 06:51:52 +0200181 return 0;
182}
183
184static int imx_pwm_probe(struct udevice *dev)
185{
Tommaso Merciaibfc778c2022-03-26 12:19:06 +0100186 int ret;
187 struct imx_pwm_priv *priv = dev_get_priv(dev);
188
189 ret = clk_enable(&priv->per_clk);
190 if (ret) {
191 printf("Failed to enable per_clk\n");
192 return ret;
193 }
194
195 ret = clk_enable(&priv->ipg_clk);
196 if (ret) {
197 printf("Failed to enable ipg_clk\n");
198 return ret;
199 }
200
Heiko Schocher46e10e92019-05-28 06:51:52 +0200201 return 0;
202}
203
204static const struct pwm_ops imx_pwm_ops = {
205 .set_invert = imx_pwm_set_invert,
206 .set_config = imx_pwm_set_config,
207 .set_enable = imx_pwm_set_enable,
208};
209
210static const struct udevice_id imx_pwm_ids[] = {
211 { .compatible = "fsl,imx27-pwm" },
212 { }
213};
214
215U_BOOT_DRIVER(imx_pwm) = {
216 .name = "imx_pwm",
217 .id = UCLASS_PWM,
218 .of_match = imx_pwm_ids,
219 .ops = &imx_pwm_ops,
Simon Glassd1998a92020-12-03 16:55:21 -0700220 .of_to_plat = imx_pwm_of_to_plat,
Heiko Schocher46e10e92019-05-28 06:51:52 +0200221 .probe = imx_pwm_probe,
Simon Glass41575d82020-12-03 16:55:17 -0700222 .priv_auto = sizeof(struct imx_pwm_priv),
Heiko Schocher46e10e92019-05-28 06:51:52 +0200223};
224#endif