blob: 66eb0e72fed325c6ec8fca8af69a558e445b433a [file] [log] [blame]
Tom Rini83d290c2018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Simon Glass0e23fd82016-01-21 19:44:55 -07002/*
3 * Copyright (c) 2016 Google, Inc
4 * Written by Simon Glass <sjg@chromium.org>
Simon Glass0e23fd82016-01-21 19:44:55 -07005 */
6
7#include <common.h>
Kever Yang12406ae2016-08-12 17:57:48 +08008#include <clk.h>
Simon Glass0e23fd82016-01-21 19:44:55 -07009#include <div64.h>
10#include <dm.h>
Simon Glassf7ae49f2020-05-10 11:40:05 -060011#include <log.h>
Simon Glass0e23fd82016-01-21 19:44:55 -070012#include <pwm.h>
13#include <regmap.h>
14#include <syscon.h>
15#include <asm/io.h>
Kever Yang15f09a12019-03-28 11:01:23 +080016#include <asm/arch-rockchip/pwm.h>
Simon Glass0e23fd82016-01-21 19:44:55 -070017#include <power/regulator.h>
18
David Wu4ee6d512019-12-03 17:49:53 +080019DECLARE_GLOBAL_DATA_PTR;
20
21struct rockchip_pwm_data {
22 struct rockchip_pwm_regs regs;
23 unsigned int prescaler;
24 bool supports_polarity;
25 bool supports_lock;
26 u32 enable_conf;
27 u32 enable_conf_mask;
28};
29
Simon Glass0e23fd82016-01-21 19:44:55 -070030struct rk_pwm_priv {
David Wu4ee6d512019-12-03 17:49:53 +080031 fdt_addr_t base;
Kever Yang12406ae2016-08-12 17:57:48 +080032 ulong freq;
David Wu4ee6d512019-12-03 17:49:53 +080033 u32 conf_polarity;
34 const struct rockchip_pwm_data *data;
Simon Glass0e23fd82016-01-21 19:44:55 -070035};
36
Kever Yang874ee592017-04-24 10:27:50 +080037static int rk_pwm_set_invert(struct udevice *dev, uint channel, bool polarity)
38{
39 struct rk_pwm_priv *priv = dev_get_priv(dev);
40
David Wu4ee6d512019-12-03 17:49:53 +080041 if (!priv->data->supports_polarity) {
42 debug("%s: Do not support polarity\n", __func__);
43 return 0;
44 }
45
Kever Yang874ee592017-04-24 10:27:50 +080046 debug("%s: polarity=%u\n", __func__, polarity);
47 if (polarity)
David Wu4ee6d512019-12-03 17:49:53 +080048 priv->conf_polarity = PWM_DUTY_NEGATIVE | PWM_INACTIVE_POSTIVE;
Kever Yang874ee592017-04-24 10:27:50 +080049 else
David Wu4ee6d512019-12-03 17:49:53 +080050 priv->conf_polarity = PWM_DUTY_POSTIVE | PWM_INACTIVE_NEGATIVE;
Kever Yang874ee592017-04-24 10:27:50 +080051
52 return 0;
53}
54
Simon Glass0e23fd82016-01-21 19:44:55 -070055static int rk_pwm_set_config(struct udevice *dev, uint channel, uint period_ns,
56 uint duty_ns)
57{
58 struct rk_pwm_priv *priv = dev_get_priv(dev);
David Wu4ee6d512019-12-03 17:49:53 +080059 const struct rockchip_pwm_regs *regs = &priv->data->regs;
Simon Glass0e23fd82016-01-21 19:44:55 -070060 unsigned long period, duty;
David Wu4ee6d512019-12-03 17:49:53 +080061 u32 ctrl;
Simon Glass0e23fd82016-01-21 19:44:55 -070062
63 debug("%s: period_ns=%u, duty_ns=%u\n", __func__, period_ns, duty_ns);
Simon Glass0e23fd82016-01-21 19:44:55 -070064
David Wu4ee6d512019-12-03 17:49:53 +080065 ctrl = readl(priv->base + regs->ctrl);
66 /*
67 * Lock the period and duty of previous configuration, then
68 * change the duty and period, that would not be effective.
69 */
70 if (priv->data->supports_lock) {
71 ctrl |= PWM_LOCK;
72 writel(ctrl, priv->base + regs->ctrl);
73 }
Simon Glass0e23fd82016-01-21 19:44:55 -070074
David Wu4ee6d512019-12-03 17:49:53 +080075 period = lldiv((uint64_t)priv->freq * period_ns,
76 priv->data->prescaler * 1000000000);
77 duty = lldiv((uint64_t)priv->freq * duty_ns,
78 priv->data->prescaler * 1000000000);
79
80 writel(period, priv->base + regs->period);
81 writel(duty, priv->base + regs->duty);
82
83 if (priv->data->supports_polarity) {
84 ctrl &= ~(PWM_DUTY_MASK | PWM_INACTIVE_MASK);
85 ctrl |= priv->conf_polarity;
86 }
87
88 /*
89 * Unlock and set polarity at the same time,
90 * the configuration of duty, period and polarity
91 * would be effective together at next period.
92 */
93 if (priv->data->supports_lock)
94 ctrl &= ~PWM_LOCK;
95 writel(ctrl, priv->base + regs->ctrl);
96
Simon Glass0e23fd82016-01-21 19:44:55 -070097 debug("%s: period=%lu, duty=%lu\n", __func__, period, duty);
98
99 return 0;
100}
101
102static int rk_pwm_set_enable(struct udevice *dev, uint channel, bool enable)
103{
104 struct rk_pwm_priv *priv = dev_get_priv(dev);
David Wu4ee6d512019-12-03 17:49:53 +0800105 const struct rockchip_pwm_regs *regs = &priv->data->regs;
106 u32 ctrl;
Simon Glass0e23fd82016-01-21 19:44:55 -0700107
108 debug("%s: Enable '%s'\n", __func__, dev->name);
David Wu4ee6d512019-12-03 17:49:53 +0800109
110 ctrl = readl(priv->base + regs->ctrl);
111 ctrl &= ~priv->data->enable_conf_mask;
112
113 if (enable)
114 ctrl |= priv->data->enable_conf;
115 else
116 ctrl &= ~priv->data->enable_conf;
117
118 writel(ctrl, priv->base + regs->ctrl);
Simon Glass0e23fd82016-01-21 19:44:55 -0700119
120 return 0;
121}
122
123static int rk_pwm_ofdata_to_platdata(struct udevice *dev)
124{
125 struct rk_pwm_priv *priv = dev_get_priv(dev);
Simon Glass0e23fd82016-01-21 19:44:55 -0700126
David Wu4ee6d512019-12-03 17:49:53 +0800127 priv->base = dev_read_addr(dev);
Simon Glass0e23fd82016-01-21 19:44:55 -0700128
129 return 0;
130}
131
132static int rk_pwm_probe(struct udevice *dev)
133{
134 struct rk_pwm_priv *priv = dev_get_priv(dev);
Kever Yang12406ae2016-08-12 17:57:48 +0800135 struct clk clk;
136 int ret = 0;
Simon Glass0e23fd82016-01-21 19:44:55 -0700137
Kever Yang12406ae2016-08-12 17:57:48 +0800138 ret = clk_get_by_index(dev, 0, &clk);
139 if (ret < 0) {
140 debug("%s get clock fail!\n", __func__);
141 return -EINVAL;
142 }
David Wu4ee6d512019-12-03 17:49:53 +0800143
Kever Yang12406ae2016-08-12 17:57:48 +0800144 priv->freq = clk_get_rate(&clk);
David Wu4ee6d512019-12-03 17:49:53 +0800145 priv->data = (struct rockchip_pwm_data *)dev_get_driver_data(dev);
146
147 if (priv->data->supports_polarity)
148 priv->conf_polarity = PWM_DUTY_POSTIVE | PWM_INACTIVE_POSTIVE;
Kever Yang12406ae2016-08-12 17:57:48 +0800149
Simon Glass0e23fd82016-01-21 19:44:55 -0700150 return 0;
151}
152
153static const struct pwm_ops rk_pwm_ops = {
Kever Yang874ee592017-04-24 10:27:50 +0800154 .set_invert = rk_pwm_set_invert,
Simon Glass0e23fd82016-01-21 19:44:55 -0700155 .set_config = rk_pwm_set_config,
156 .set_enable = rk_pwm_set_enable,
157};
158
David Wu4ee6d512019-12-03 17:49:53 +0800159static const struct rockchip_pwm_data pwm_data_v1 = {
160 .regs = {
161 .duty = 0x04,
162 .period = 0x08,
163 .cntr = 0x00,
164 .ctrl = 0x0c,
165 },
166 .prescaler = 2,
167 .supports_polarity = false,
168 .supports_lock = false,
169 .enable_conf = PWM_CTRL_OUTPUT_EN | PWM_CTRL_TIMER_EN,
170 .enable_conf_mask = BIT(1) | BIT(3),
171};
172
173static const struct rockchip_pwm_data pwm_data_v2 = {
174 .regs = {
175 .duty = 0x08,
176 .period = 0x04,
177 .cntr = 0x00,
178 .ctrl = 0x0c,
179 },
180 .prescaler = 1,
181 .supports_polarity = true,
182 .supports_lock = false,
183 .enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | RK_PWM_ENABLE |
184 PWM_CONTINUOUS,
185 .enable_conf_mask = GENMASK(2, 0) | BIT(5) | BIT(8),
186};
187
188static const struct rockchip_pwm_data pwm_data_v3 = {
189 .regs = {
190 .duty = 0x08,
191 .period = 0x04,
192 .cntr = 0x00,
193 .ctrl = 0x0c,
194 },
195 .prescaler = 1,
196 .supports_polarity = true,
197 .supports_lock = true,
198 .enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | RK_PWM_ENABLE |
199 PWM_CONTINUOUS,
200 .enable_conf_mask = GENMASK(2, 0) | BIT(5) | BIT(8),
201};
202
Simon Glass0e23fd82016-01-21 19:44:55 -0700203static const struct udevice_id rk_pwm_ids[] = {
David Wu4ee6d512019-12-03 17:49:53 +0800204 { .compatible = "rockchip,rk2928-pwm", .data = (ulong)&pwm_data_v1},
205 { .compatible = "rockchip,rk3288-pwm", .data = (ulong)&pwm_data_v2},
206 { .compatible = "rockchip,rk3328-pwm", .data = (ulong)&pwm_data_v3},
Simon Glass0e23fd82016-01-21 19:44:55 -0700207 { }
208};
209
210U_BOOT_DRIVER(rk_pwm) = {
211 .name = "rk_pwm",
212 .id = UCLASS_PWM,
213 .of_match = rk_pwm_ids,
214 .ops = &rk_pwm_ops,
215 .ofdata_to_platdata = rk_pwm_ofdata_to_platdata,
216 .probe = rk_pwm_probe,
217 .priv_auto_alloc_size = sizeof(struct rk_pwm_priv),
218};