1 /*
2 * Copyright (c) 2016 Google, Inc
3 * Written by Simon Glass <sjg@chromium.org>
4 *
5 * SPDX-License-Identifier: GPL-2.0+
6 */
7
8 #include <common.h>
9 #include <clk.h>
10 #include <div64.h>
11 #include <dm.h>
12 #include <dm/pinctrl.h>
13 #include <pwm.h>
14 #include <regmap.h>
15 #include <syscon.h>
16 #include <asm/io.h>
17 #include <asm/arch/pwm.h>
18 #include <power/regulator.h>
19
20 DECLARE_GLOBAL_DATA_PTR;
21
22 struct rockchip_pwm_data {
23 struct rockchip_pwm_regs regs;
24 unsigned int prescaler;
25 bool supports_polarity;
26 bool supports_lock;
27 bool vop_pwm;
28 u32 enable_conf;
29 u32 enable_conf_mask;
30 };
31
32 struct rk_pwm_priv {
33 fdt_addr_t base;
34 ulong freq;
35 u32 conf_polarity;
36 bool vop_pwm_en; /* indicate voppwm mirror register state */
37 const struct rockchip_pwm_data *data;
38 };
39
rk_pwm_set_invert(struct udevice * dev,uint channel,bool polarity)40 static int rk_pwm_set_invert(struct udevice *dev, uint channel, bool polarity)
41 {
42 struct rk_pwm_priv *priv = dev_get_priv(dev);
43
44 if (!priv->data->supports_polarity) {
45 debug("%s: Do not support polarity\n", __func__);
46 return 0;
47 }
48
49 debug("%s: polarity=%u\n", __func__, polarity);
50 if (polarity)
51 priv->conf_polarity = PWM_DUTY_NEGATIVE | PWM_INACTIVE_POSTIVE;
52 else
53 priv->conf_polarity = PWM_DUTY_POSTIVE | PWM_INACTIVE_NEGATIVE;
54
55 return 0;
56 }
57
rk_pwm_set_config(struct udevice * dev,uint channel,uint period_ns,uint duty_ns)58 static int rk_pwm_set_config(struct udevice *dev, uint channel, uint period_ns,
59 uint duty_ns)
60 {
61 struct rk_pwm_priv *priv = dev_get_priv(dev);
62 const struct rockchip_pwm_regs *regs = &priv->data->regs;
63 unsigned long period, duty;
64 u32 ctrl;
65
66 debug("%s: period_ns=%u, duty_ns=%u\n", __func__, period_ns, duty_ns);
67
68 ctrl = readl(priv->base + regs->ctrl);
69 if (priv->data->vop_pwm) {
70 if (priv->vop_pwm_en)
71 ctrl |= RK_PWM_ENABLE;
72 else
73 ctrl &= ~RK_PWM_ENABLE;
74 }
75
76 /*
77 * Lock the period and duty of previous configuration, then
78 * change the duty and period, that would not be effective.
79 */
80 if (priv->data->supports_lock) {
81 ctrl |= PWM_LOCK;
82 writel(ctrl, priv->base + regs->ctrl);
83 }
84
85 period = lldiv((uint64_t)(priv->freq / 1000) * period_ns,
86 priv->data->prescaler * 1000000);
87 duty = lldiv((uint64_t)(priv->freq / 1000) * duty_ns,
88 priv->data->prescaler * 1000000);
89
90 writel(period, priv->base + regs->period);
91 writel(duty, priv->base + regs->duty);
92
93 if (priv->data->supports_polarity) {
94 ctrl &= ~(PWM_DUTY_MASK | PWM_INACTIVE_MASK);
95 ctrl |= priv->conf_polarity;
96 }
97
98 /*
99 * Unlock and set polarity at the same time,
100 * the configuration of duty, period and polarity
101 * would be effective together at next period.
102 */
103 if (priv->data->supports_lock)
104 ctrl &= ~PWM_LOCK;
105 writel(ctrl, priv->base + regs->ctrl);
106
107 debug("%s: period=%lu, duty=%lu\n", __func__, period, duty);
108
109 return 0;
110 }
111
rk_pwm_set_enable(struct udevice * dev,uint channel,bool enable)112 static int rk_pwm_set_enable(struct udevice *dev, uint channel, bool enable)
113 {
114 struct rk_pwm_priv *priv = dev_get_priv(dev);
115 const struct rockchip_pwm_regs *regs = &priv->data->regs;
116 u32 ctrl;
117
118 debug("%s: Enable '%s'\n", __func__, dev->name);
119
120 ctrl = readl(priv->base + regs->ctrl);
121 ctrl &= ~priv->data->enable_conf_mask;
122
123 if (enable)
124 ctrl |= priv->data->enable_conf;
125 else
126 ctrl &= ~priv->data->enable_conf;
127
128 writel(ctrl, priv->base + regs->ctrl);
129 if (priv->data->vop_pwm)
130 priv->vop_pwm_en = enable;
131
132 if (enable)
133 pinctrl_select_state(dev, "active");
134
135 return 0;
136 }
137
rk_pwm_ofdata_to_platdata(struct udevice * dev)138 static int rk_pwm_ofdata_to_platdata(struct udevice *dev)
139 {
140 struct rk_pwm_priv *priv = dev_get_priv(dev);
141
142 priv->base = dev_read_addr(dev);
143
144 return 0;
145 }
146
rk_pwm_probe(struct udevice * dev)147 static int rk_pwm_probe(struct udevice *dev)
148 {
149 struct rk_pwm_priv *priv = dev_get_priv(dev);
150 struct clk clk;
151 int ret = 0;
152
153 ret = clk_get_by_index(dev, 0, &clk);
154 if (ret < 0) {
155 debug("%s get clock fail!\n", __func__);
156 return -EINVAL;
157 }
158
159 ret = clk_get_rate(&clk);
160 if (ret < 0) {
161 debug("%s pwm get clock rate fail!\n", __func__);
162 return -EINVAL;
163 }
164 priv->freq = ret;
165 priv->data = (struct rockchip_pwm_data *)dev_get_driver_data(dev);
166
167 if (priv->data->supports_polarity)
168 priv->conf_polarity = PWM_DUTY_POSTIVE | PWM_INACTIVE_POSTIVE;
169
170 return 0;
171 }
172
173 static const struct pwm_ops rk_pwm_ops = {
174 .set_invert = rk_pwm_set_invert,
175 .set_config = rk_pwm_set_config,
176 .set_enable = rk_pwm_set_enable,
177 };
178
179 static const struct rockchip_pwm_data pwm_data_v1 = {
180 .regs = {
181 .duty = 0x04,
182 .period = 0x08,
183 .cntr = 0x00,
184 .ctrl = 0x0c,
185 },
186 .prescaler = 2,
187 .supports_polarity = false,
188 .supports_lock = false,
189 .vop_pwm = false,
190 .enable_conf = PWM_CTRL_OUTPUT_EN | PWM_CTRL_TIMER_EN,
191 .enable_conf_mask = BIT(1) | BIT(3),
192 };
193
194 static const struct rockchip_pwm_data pwm_data_v2 = {
195 .regs = {
196 .duty = 0x08,
197 .period = 0x04,
198 .cntr = 0x00,
199 .ctrl = 0x0c,
200 },
201 .prescaler = 1,
202 .supports_polarity = true,
203 .supports_lock = false,
204 .vop_pwm = false,
205 .enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | RK_PWM_ENABLE |
206 PWM_CONTINUOUS,
207 .enable_conf_mask = GENMASK(2, 0) | BIT(5) | BIT(8),
208 };
209
210 static const struct rockchip_pwm_data pwm_data_vop = {
211 .regs = {
212 .duty = 0x08,
213 .period = 0x04,
214 .cntr = 0x0c,
215 .ctrl = 0x00,
216 },
217 .prescaler = 1,
218 .supports_polarity = true,
219 .supports_lock = false,
220 .vop_pwm = true,
221 .enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | RK_PWM_ENABLE |
222 PWM_CONTINUOUS,
223 .enable_conf_mask = GENMASK(2, 0) | BIT(5) | BIT(8),
224 };
225
226 static const struct rockchip_pwm_data pwm_data_v3 = {
227 .regs = {
228 .duty = 0x08,
229 .period = 0x04,
230 .cntr = 0x00,
231 .ctrl = 0x0c,
232 },
233 .prescaler = 1,
234 .supports_polarity = true,
235 .supports_lock = true,
236 .vop_pwm = false,
237 .enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | RK_PWM_ENABLE |
238 PWM_CONTINUOUS,
239 .enable_conf_mask = GENMASK(2, 0) | BIT(5) | BIT(8),
240 };
241
242 static const struct udevice_id rk_pwm_ids[] = {
243 { .compatible = "rockchip,rk2928-pwm", .data = (ulong)&pwm_data_v1},
244 { .compatible = "rockchip,rk3288-pwm", .data = (ulong)&pwm_data_v2},
245 { .compatible = "rockchip,rk3328-pwm", .data = (ulong)&pwm_data_v3},
246 { .compatible = "rockchip,vop-pwm", .data = (ulong)&pwm_data_vop},
247 { .compatible = "rockchip,rk3399-pwm", .data = (ulong)&pwm_data_v2},
248 { }
249 };
250
251 U_BOOT_DRIVER(rk_pwm) = {
252 .name = "rk_pwm",
253 .id = UCLASS_PWM,
254 .of_match = rk_pwm_ids,
255 .ops = &rk_pwm_ops,
256 .ofdata_to_platdata = rk_pwm_ofdata_to_platdata,
257 .probe = rk_pwm_probe,
258 .priv_auto_alloc_size = sizeof(struct rk_pwm_priv),
259 };
260