1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Sophgo SG2042 PWM Controller Driver
4 *
5 * Copyright (C) 2024 Sophgo Technology Inc.
6 * Copyright (C) 2024 Chen Wang <unicorn_wang@outlook.com>
7 *
8 * Limitations:
9 * - After reset, the output of the PWM channel is always high.
10 * The value of HLPERIOD/PERIOD is 0.
11 * - When HLPERIOD or PERIOD is reconfigured, PWM will start to
12 * output waveforms with the new configuration after completing
13 * the running period.
14 * - When PERIOD and HLPERIOD is set to 0, the PWM wave output will
15 * be stopped and the output is pulled to high.
16 * - SG2044 supports both polarities, SG2042 only normal polarity.
17 * See the datasheet [1] for more details.
18 * [1]:https://github.com/sophgo/sophgo-doc/tree/main/SG2042/TRM
19 */
20
21 #include <linux/clk.h>
22 #include <linux/err.h>
23 #include <linux/io.h>
24 #include <linux/math64.h>
25 #include <linux/module.h>
26 #include <linux/platform_device.h>
27 #include <linux/pwm.h>
28 #include <linux/reset.h>
29
30 /*
31 * Offset RegisterName
32 * 0x0000 HLPERIOD0
33 * 0x0004 PERIOD0
34 * 0x0008 HLPERIOD1
35 * 0x000C PERIOD1
36 * 0x0010 HLPERIOD2
37 * 0x0014 PERIOD2
38 * 0x0018 HLPERIOD3
39 * 0x001C PERIOD3
40 * Four groups and every group is composed of HLPERIOD & PERIOD
41 */
42 #define SG2042_PWM_HLPERIOD(chan) ((chan) * 8 + 0)
43 #define SG2042_PWM_PERIOD(chan) ((chan) * 8 + 4)
44
45 #define SG2044_PWM_POLARITY 0x40
46 #define SG2044_PWM_PWMSTART 0x44
47 #define SG2044_PWM_OE 0xd0
48
49 #define SG2042_PWM_CHANNELNUM 4
50
51 /**
52 * struct sg2042_pwm_ddata - private driver data
53 * @base: base address of mapped PWM registers
54 * @clk_rate_hz: rate of base clock in HZ
55 */
56 struct sg2042_pwm_ddata {
57 void __iomem *base;
58 unsigned long clk_rate_hz;
59 };
60
61 struct sg2042_chip_data {
62 const struct pwm_ops ops;
63 };
64
65 /*
66 * period_ticks: PERIOD
67 * hlperiod_ticks: HLPERIOD
68 */
pwm_sg2042_config(struct sg2042_pwm_ddata * ddata,unsigned int chan,u32 period_ticks,u32 hlperiod_ticks)69 static void pwm_sg2042_config(struct sg2042_pwm_ddata *ddata, unsigned int chan,
70 u32 period_ticks, u32 hlperiod_ticks)
71 {
72 void __iomem *base = ddata->base;
73
74 writel(period_ticks, base + SG2042_PWM_PERIOD(chan));
75 writel(hlperiod_ticks, base + SG2042_PWM_HLPERIOD(chan));
76 }
77
pwm_sg2042_set_dutycycle(struct pwm_chip * chip,struct pwm_device * pwm,const struct pwm_state * state)78 static void pwm_sg2042_set_dutycycle(struct pwm_chip *chip, struct pwm_device *pwm,
79 const struct pwm_state *state)
80 {
81 struct sg2042_pwm_ddata *ddata = pwmchip_get_drvdata(chip);
82 u32 hlperiod_ticks;
83 u32 period_ticks;
84
85 /*
86 * Duration of High level (duty_cycle) = HLPERIOD x Period_of_input_clk
87 * Duration of One Cycle (period) = PERIOD x Period_of_input_clk
88 */
89 period_ticks = min(mul_u64_u64_div_u64(ddata->clk_rate_hz, state->period, NSEC_PER_SEC), U32_MAX);
90 hlperiod_ticks = min(mul_u64_u64_div_u64(ddata->clk_rate_hz, state->duty_cycle, NSEC_PER_SEC), U32_MAX);
91
92 dev_dbg(pwmchip_parent(chip), "chan[%u]: ENABLE=%u, PERIOD=%u, HLPERIOD=%u, POLARITY=%u\n",
93 pwm->hwpwm, state->enabled, period_ticks, hlperiod_ticks, state->polarity);
94
95 pwm_sg2042_config(ddata, pwm->hwpwm, period_ticks, hlperiod_ticks);
96 }
97
pwm_sg2042_apply(struct pwm_chip * chip,struct pwm_device * pwm,const struct pwm_state * state)98 static int pwm_sg2042_apply(struct pwm_chip *chip, struct pwm_device *pwm,
99 const struct pwm_state *state)
100 {
101 struct sg2042_pwm_ddata *ddata = pwmchip_get_drvdata(chip);
102
103 if (state->polarity == PWM_POLARITY_INVERSED)
104 return -EINVAL;
105
106 if (!state->enabled) {
107 pwm_sg2042_config(ddata, pwm->hwpwm, 0, 0);
108 return 0;
109 }
110
111 pwm_sg2042_set_dutycycle(chip, pwm, state);
112
113 return 0;
114 }
115
pwm_sg2042_get_state(struct pwm_chip * chip,struct pwm_device * pwm,struct pwm_state * state)116 static int pwm_sg2042_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
117 struct pwm_state *state)
118 {
119 struct sg2042_pwm_ddata *ddata = pwmchip_get_drvdata(chip);
120 unsigned int chan = pwm->hwpwm;
121 u32 hlperiod_ticks;
122 u32 period_ticks;
123
124 period_ticks = readl(ddata->base + SG2042_PWM_PERIOD(chan));
125 hlperiod_ticks = readl(ddata->base + SG2042_PWM_HLPERIOD(chan));
126
127 if (!period_ticks) {
128 state->enabled = false;
129 return 0;
130 }
131
132 if (hlperiod_ticks > period_ticks)
133 hlperiod_ticks = period_ticks;
134
135 state->enabled = true;
136 state->period = DIV_ROUND_UP_ULL((u64)period_ticks * NSEC_PER_SEC, ddata->clk_rate_hz);
137 state->duty_cycle = DIV_ROUND_UP_ULL((u64)hlperiod_ticks * NSEC_PER_SEC, ddata->clk_rate_hz);
138 state->polarity = PWM_POLARITY_NORMAL;
139
140 return 0;
141 }
142
pwm_sg2044_set_outputen(struct sg2042_pwm_ddata * ddata,struct pwm_device * pwm,bool enabled)143 static void pwm_sg2044_set_outputen(struct sg2042_pwm_ddata *ddata, struct pwm_device *pwm,
144 bool enabled)
145 {
146 u32 pwmstart;
147
148 pwmstart = readl(ddata->base + SG2044_PWM_PWMSTART);
149
150 if (enabled)
151 pwmstart |= BIT(pwm->hwpwm);
152 else
153 pwmstart &= ~BIT(pwm->hwpwm);
154
155 writel(pwmstart, ddata->base + SG2044_PWM_PWMSTART);
156 }
157
pwm_sg2044_set_outputdir(struct sg2042_pwm_ddata * ddata,struct pwm_device * pwm,bool enabled)158 static void pwm_sg2044_set_outputdir(struct sg2042_pwm_ddata *ddata, struct pwm_device *pwm,
159 bool enabled)
160 {
161 u32 pwm_oe;
162
163 pwm_oe = readl(ddata->base + SG2044_PWM_OE);
164
165 if (enabled)
166 pwm_oe |= BIT(pwm->hwpwm);
167 else
168 pwm_oe &= ~BIT(pwm->hwpwm);
169
170 writel(pwm_oe, ddata->base + SG2044_PWM_OE);
171 }
172
pwm_sg2044_set_polarity(struct sg2042_pwm_ddata * ddata,struct pwm_device * pwm,const struct pwm_state * state)173 static void pwm_sg2044_set_polarity(struct sg2042_pwm_ddata *ddata, struct pwm_device *pwm,
174 const struct pwm_state *state)
175 {
176 u32 pwm_polarity;
177
178 pwm_polarity = readl(ddata->base + SG2044_PWM_POLARITY);
179
180 if (state->polarity == PWM_POLARITY_NORMAL)
181 pwm_polarity &= ~BIT(pwm->hwpwm);
182 else
183 pwm_polarity |= BIT(pwm->hwpwm);
184
185 writel(pwm_polarity, ddata->base + SG2044_PWM_POLARITY);
186 }
187
pwm_sg2044_apply(struct pwm_chip * chip,struct pwm_device * pwm,const struct pwm_state * state)188 static int pwm_sg2044_apply(struct pwm_chip *chip, struct pwm_device *pwm,
189 const struct pwm_state *state)
190 {
191 struct sg2042_pwm_ddata *ddata = pwmchip_get_drvdata(chip);
192
193 pwm_sg2044_set_polarity(ddata, pwm, state);
194
195 pwm_sg2042_set_dutycycle(chip, pwm, state);
196
197 /*
198 * re-enable PWMSTART to refresh the register period
199 */
200 pwm_sg2044_set_outputen(ddata, pwm, false);
201
202 if (!state->enabled)
203 return 0;
204
205 pwm_sg2044_set_outputdir(ddata, pwm, true);
206 pwm_sg2044_set_outputen(ddata, pwm, true);
207
208 return 0;
209 }
210
211 static const struct sg2042_chip_data sg2042_chip_data = {
212 .ops = {
213 .apply = pwm_sg2042_apply,
214 .get_state = pwm_sg2042_get_state,
215 },
216 };
217
218 static const struct sg2042_chip_data sg2044_chip_data = {
219 .ops = {
220 .apply = pwm_sg2044_apply,
221 .get_state = pwm_sg2042_get_state,
222 },
223 };
224
225 static const struct of_device_id sg2042_pwm_ids[] = {
226 {
227 .compatible = "sophgo,sg2042-pwm",
228 .data = &sg2042_chip_data
229 },
230 {
231 .compatible = "sophgo,sg2044-pwm",
232 .data = &sg2044_chip_data
233 },
234 { }
235 };
236 MODULE_DEVICE_TABLE(of, sg2042_pwm_ids);
237
pwm_sg2042_probe(struct platform_device * pdev)238 static int pwm_sg2042_probe(struct platform_device *pdev)
239 {
240 struct device *dev = &pdev->dev;
241 const struct sg2042_chip_data *chip_data;
242 struct sg2042_pwm_ddata *ddata;
243 struct reset_control *rst;
244 struct pwm_chip *chip;
245 struct clk *clk;
246 int ret;
247
248 chip_data = device_get_match_data(dev);
249 if (!chip_data)
250 return -ENODEV;
251
252 chip = devm_pwmchip_alloc(dev, SG2042_PWM_CHANNELNUM, sizeof(*ddata));
253 if (IS_ERR(chip))
254 return PTR_ERR(chip);
255 ddata = pwmchip_get_drvdata(chip);
256
257 ddata->base = devm_platform_ioremap_resource(pdev, 0);
258 if (IS_ERR(ddata->base))
259 return PTR_ERR(ddata->base);
260
261 clk = devm_clk_get_enabled(dev, "apb");
262 if (IS_ERR(clk))
263 return dev_err_probe(dev, PTR_ERR(clk), "Failed to get base clk\n");
264
265 ret = devm_clk_rate_exclusive_get(dev, clk);
266 if (ret)
267 return dev_err_probe(dev, ret, "Failed to get exclusive rate\n");
268
269 ddata->clk_rate_hz = clk_get_rate(clk);
270 /* period = PERIOD * NSEC_PER_SEC / clk_rate_hz */
271 if (!ddata->clk_rate_hz || ddata->clk_rate_hz > NSEC_PER_SEC)
272 return dev_err_probe(dev, -EINVAL,
273 "Invalid clock rate: %lu\n", ddata->clk_rate_hz);
274
275 rst = devm_reset_control_get_optional_shared_deasserted(dev, NULL);
276 if (IS_ERR(rst))
277 return dev_err_probe(dev, PTR_ERR(rst), "Failed to get reset\n");
278
279 chip->ops = &chip_data->ops;
280 chip->atomic = true;
281
282 ret = devm_pwmchip_add(dev, chip);
283 if (ret < 0)
284 return dev_err_probe(dev, ret, "Failed to register PWM chip\n");
285
286 return 0;
287 }
288
289 static struct platform_driver pwm_sg2042_driver = {
290 .driver = {
291 .name = "sg2042-pwm",
292 .of_match_table = sg2042_pwm_ids,
293 },
294 .probe = pwm_sg2042_probe,
295 };
296 module_platform_driver(pwm_sg2042_driver);
297
298 MODULE_AUTHOR("Chen Wang");
299 MODULE_AUTHOR("Longbin Li <looong.bin@gmail.com>");
300 MODULE_DESCRIPTION("Sophgo SG2042 PWM driver");
301 MODULE_LICENSE("GPL");
302