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