xref: /rk3399_rockchip-uboot/drivers/pwm/rk_pwm.c (revision d5f538dc02e53c7267fcd4a914104071fca889b5)
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 
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 
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 
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 
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 
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 	priv->freq = clk_get_rate(&clk);
160 	priv->data = (struct rockchip_pwm_data *)dev_get_driver_data(dev);
161 
162 	if (priv->data->supports_polarity)
163 		priv->conf_polarity = PWM_DUTY_POSTIVE | PWM_INACTIVE_POSTIVE;
164 
165 	return 0;
166 }
167 
168 static const struct pwm_ops rk_pwm_ops = {
169 	.set_invert	= rk_pwm_set_invert,
170 	.set_config	= rk_pwm_set_config,
171 	.set_enable	= rk_pwm_set_enable,
172 };
173 
174 static const struct rockchip_pwm_data pwm_data_v1 = {
175 	.regs = {
176 		.duty = 0x04,
177 		.period = 0x08,
178 		.cntr = 0x00,
179 		.ctrl = 0x0c,
180 	},
181 	.prescaler = 2,
182 	.supports_polarity = false,
183 	.supports_lock = false,
184 	.vop_pwm = false,
185 	.enable_conf = PWM_CTRL_OUTPUT_EN | PWM_CTRL_TIMER_EN,
186 	.enable_conf_mask = BIT(1) | BIT(3),
187 };
188 
189 static const struct rockchip_pwm_data pwm_data_v2 = {
190 	.regs = {
191 		.duty = 0x08,
192 		.period = 0x04,
193 		.cntr = 0x00,
194 		.ctrl = 0x0c,
195 	},
196 	.prescaler = 1,
197 	.supports_polarity = true,
198 	.supports_lock = false,
199 	.vop_pwm = false,
200 	.enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | RK_PWM_ENABLE |
201 		       PWM_CONTINUOUS,
202 	.enable_conf_mask = GENMASK(2, 0) | BIT(5) | BIT(8),
203 };
204 
205 static const struct rockchip_pwm_data pwm_data_vop = {
206 	.regs = {
207 		.duty = 0x08,
208 		.period = 0x04,
209 		.cntr = 0x0c,
210 		.ctrl = 0x00,
211 	},
212 	.prescaler = 1,
213 	.supports_polarity = true,
214 	.supports_lock = false,
215 	.vop_pwm = true,
216 	.enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | RK_PWM_ENABLE |
217 		       PWM_CONTINUOUS,
218 	.enable_conf_mask = GENMASK(2, 0) | BIT(5) | BIT(8),
219 };
220 
221 static const struct rockchip_pwm_data pwm_data_v3 = {
222 	.regs = {
223 		.duty = 0x08,
224 		.period = 0x04,
225 		.cntr = 0x00,
226 		.ctrl = 0x0c,
227 	},
228 	.prescaler = 1,
229 	.supports_polarity = true,
230 	.supports_lock = true,
231 	.vop_pwm = false,
232 	.enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | RK_PWM_ENABLE |
233 		       PWM_CONTINUOUS,
234 	.enable_conf_mask = GENMASK(2, 0) | BIT(5) | BIT(8),
235 };
236 
237 static const struct udevice_id rk_pwm_ids[] = {
238 	{ .compatible = "rockchip,rk2928-pwm", .data = (ulong)&pwm_data_v1},
239 	{ .compatible = "rockchip,rk3288-pwm", .data = (ulong)&pwm_data_v2},
240 	{ .compatible = "rockchip,rk3328-pwm", .data = (ulong)&pwm_data_v3},
241 	{ .compatible = "rockchip,vop-pwm", .data = (ulong)&pwm_data_vop},
242 	{ .compatible = "rockchip,rk3399-pwm", .data = (ulong)&pwm_data_v2},
243 	{ }
244 };
245 
246 U_BOOT_DRIVER(rk_pwm) = {
247 	.name	= "rk_pwm",
248 	.id	= UCLASS_PWM,
249 	.of_match = rk_pwm_ids,
250 	.ops	= &rk_pwm_ops,
251 	.ofdata_to_platdata	= rk_pwm_ofdata_to_platdata,
252 	.probe		= rk_pwm_probe,
253 	.priv_auto_alloc_size	= sizeof(struct rk_pwm_priv),
254 };
255