]> Git Repo - J-linux.git/blob - drivers/pwm/pwm-axi-pwmgen.c
Merge tag 'vfs-6.13-rc7.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
[J-linux.git] / drivers / pwm / pwm-axi-pwmgen.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Analog Devices AXI PWM generator
4  *
5  * Copyright 2024 Analog Devices Inc.
6  * Copyright 2024 Baylibre SAS
7  *
8  * Device docs: https://analogdevicesinc.github.io/hdl/library/axi_pwm_gen/index.html
9  *
10  * Limitations:
11  * - The writes to registers for period and duty are shadowed until
12  *   LOAD_CONFIG is written to AXI_PWMGEN_REG_RSTN, at which point
13  *   they take effect.
14  * - Writing LOAD_CONFIG also has the effect of re-synchronizing all
15  *   enabled channels, which could cause glitching on other channels. It
16  *   is therefore expected that channels are assigned harmonic periods
17  *   and all have a single user coordinating this.
18  * - Supports normal polarity. Does not support changing polarity.
19  * - On disable, the PWM output becomes low (inactive).
20  */
21 #include <linux/bits.h>
22 #include <linux/clk.h>
23 #include <linux/err.h>
24 #include <linux/fpga/adi-axi-common.h>
25 #include <linux/io.h>
26 #include <linux/minmax.h>
27 #include <linux/module.h>
28 #include <linux/platform_device.h>
29 #include <linux/pwm.h>
30 #include <linux/regmap.h>
31 #include <linux/slab.h>
32
33 #define AXI_PWMGEN_REG_ID               0x04
34 #define AXI_PWMGEN_REG_SCRATCHPAD       0x08
35 #define AXI_PWMGEN_REG_CORE_MAGIC       0x0C
36 #define AXI_PWMGEN_REG_RSTN             0x10
37 #define   AXI_PWMGEN_REG_RSTN_LOAD_CONFIG       BIT(1)
38 #define   AXI_PWMGEN_REG_RSTN_RESET             BIT(0)
39 #define AXI_PWMGEN_REG_NPWM             0x14
40 #define AXI_PWMGEN_REG_CONFIG           0x18
41 #define   AXI_PWMGEN_REG_CONFIG_FORCE_ALIGN     BIT(1)
42 #define AXI_PWMGEN_CHX_PERIOD(ch)       (0x40 + (4 * (ch)))
43 #define AXI_PWMGEN_CHX_DUTY(ch)         (0x80 + (4 * (ch)))
44 #define AXI_PWMGEN_CHX_OFFSET(ch)       (0xC0 + (4 * (ch)))
45 #define AXI_PWMGEN_REG_CORE_MAGIC_VAL   0x601A3471 /* Identification number to test during setup */
46
47 struct axi_pwmgen_ddata {
48         struct regmap *regmap;
49         unsigned long clk_rate_hz;
50 };
51
52 static const struct regmap_config axi_pwmgen_regmap_config = {
53         .reg_bits = 32,
54         .reg_stride = 4,
55         .val_bits = 32,
56         .max_register = 0xFC,
57 };
58
59 /* This represents a hardware configuration for one channel */
60 struct axi_pwmgen_waveform {
61         u32 period_cnt;
62         u32 duty_cycle_cnt;
63         u32 duty_offset_cnt;
64 };
65
66 static struct axi_pwmgen_ddata *axi_pwmgen_ddata_from_chip(struct pwm_chip *chip)
67 {
68         return pwmchip_get_drvdata(chip);
69 }
70
71 static int axi_pwmgen_round_waveform_tohw(struct pwm_chip *chip,
72                                           struct pwm_device *pwm,
73                                           const struct pwm_waveform *wf,
74                                           void *_wfhw)
75 {
76         struct axi_pwmgen_waveform *wfhw = _wfhw;
77         struct axi_pwmgen_ddata *ddata = axi_pwmgen_ddata_from_chip(chip);
78
79         if (wf->period_length_ns == 0) {
80                 *wfhw = (struct axi_pwmgen_waveform){
81                         .period_cnt = 0,
82                         .duty_cycle_cnt = 0,
83                         .duty_offset_cnt = 0,
84                 };
85         } else {
86                 /* With ddata->clk_rate_hz < NSEC_PER_SEC this won't overflow. */
87                 wfhw->period_cnt = min_t(u64,
88                                          mul_u64_u32_div(wf->period_length_ns, ddata->clk_rate_hz, NSEC_PER_SEC),
89                                          U32_MAX);
90
91                 if (wfhw->period_cnt == 0) {
92                         /*
93                          * The specified period is too short for the hardware.
94                          * Let's round .duty_cycle down to 0 to get a (somewhat)
95                          * valid result.
96                          */
97                         wfhw->period_cnt = 1;
98                         wfhw->duty_cycle_cnt = 0;
99                         wfhw->duty_offset_cnt = 0;
100                 } else {
101                         wfhw->duty_cycle_cnt = min_t(u64,
102                                                      mul_u64_u32_div(wf->duty_length_ns, ddata->clk_rate_hz, NSEC_PER_SEC),
103                                                      U32_MAX);
104                         wfhw->duty_offset_cnt = min_t(u64,
105                                                       mul_u64_u32_div(wf->duty_offset_ns, ddata->clk_rate_hz, NSEC_PER_SEC),
106                                                       U32_MAX);
107                 }
108         }
109
110         dev_dbg(&chip->dev, "pwm#%u: %lld/%lld [+%lld] @%lu -> PERIOD: %08x, DUTY: %08x, OFFSET: %08x\n",
111                 pwm->hwpwm, wf->duty_length_ns, wf->period_length_ns, wf->duty_offset_ns,
112                 ddata->clk_rate_hz, wfhw->period_cnt, wfhw->duty_cycle_cnt, wfhw->duty_offset_cnt);
113
114         return 0;
115 }
116
117 static int axi_pwmgen_round_waveform_fromhw(struct pwm_chip *chip, struct pwm_device *pwm,
118                                              const void *_wfhw, struct pwm_waveform *wf)
119 {
120         const struct axi_pwmgen_waveform *wfhw = _wfhw;
121         struct axi_pwmgen_ddata *ddata = axi_pwmgen_ddata_from_chip(chip);
122
123         wf->period_length_ns = DIV64_U64_ROUND_UP((u64)wfhw->period_cnt * NSEC_PER_SEC,
124                                         ddata->clk_rate_hz);
125
126         wf->duty_length_ns = DIV64_U64_ROUND_UP((u64)wfhw->duty_cycle_cnt * NSEC_PER_SEC,
127                                             ddata->clk_rate_hz);
128
129         wf->duty_offset_ns = DIV64_U64_ROUND_UP((u64)wfhw->duty_offset_cnt * NSEC_PER_SEC,
130                                              ddata->clk_rate_hz);
131
132         return 0;
133 }
134
135 static int axi_pwmgen_write_waveform(struct pwm_chip *chip,
136                                      struct pwm_device *pwm,
137                                      const void *_wfhw)
138 {
139         const struct axi_pwmgen_waveform *wfhw = _wfhw;
140         struct axi_pwmgen_ddata *ddata = axi_pwmgen_ddata_from_chip(chip);
141         struct regmap *regmap = ddata->regmap;
142         unsigned int ch = pwm->hwpwm;
143         int ret;
144
145         ret = regmap_write(regmap, AXI_PWMGEN_CHX_PERIOD(ch), wfhw->period_cnt);
146         if (ret)
147                 return ret;
148
149         ret = regmap_write(regmap, AXI_PWMGEN_CHX_DUTY(ch), wfhw->duty_cycle_cnt);
150         if (ret)
151                 return ret;
152
153         ret = regmap_write(regmap, AXI_PWMGEN_CHX_OFFSET(ch), wfhw->duty_offset_cnt);
154         if (ret)
155                 return ret;
156
157         return regmap_write(regmap, AXI_PWMGEN_REG_RSTN, AXI_PWMGEN_REG_RSTN_LOAD_CONFIG);
158 }
159
160 static int axi_pwmgen_read_waveform(struct pwm_chip *chip,
161                                     struct pwm_device *pwm,
162                                     void *_wfhw)
163 {
164         struct axi_pwmgen_waveform *wfhw = _wfhw;
165         struct axi_pwmgen_ddata *ddata = axi_pwmgen_ddata_from_chip(chip);
166         struct regmap *regmap = ddata->regmap;
167         unsigned int ch = pwm->hwpwm;
168         int ret;
169
170         ret = regmap_read(regmap, AXI_PWMGEN_CHX_PERIOD(ch), &wfhw->period_cnt);
171         if (ret)
172                 return ret;
173
174         ret = regmap_read(regmap, AXI_PWMGEN_CHX_DUTY(ch), &wfhw->duty_cycle_cnt);
175         if (ret)
176                 return ret;
177
178         ret = regmap_read(regmap, AXI_PWMGEN_CHX_OFFSET(ch), &wfhw->duty_offset_cnt);
179         if (ret)
180                 return ret;
181
182         if (wfhw->duty_cycle_cnt > wfhw->period_cnt)
183                 wfhw->duty_cycle_cnt = wfhw->period_cnt;
184
185         /* XXX: is this the actual behaviour of the hardware? */
186         if (wfhw->duty_offset_cnt >= wfhw->period_cnt) {
187                 wfhw->duty_cycle_cnt = 0;
188                 wfhw->duty_offset_cnt = 0;
189         }
190
191         return 0;
192 }
193
194 static const struct pwm_ops axi_pwmgen_pwm_ops = {
195         .sizeof_wfhw = sizeof(struct axi_pwmgen_waveform),
196         .round_waveform_tohw = axi_pwmgen_round_waveform_tohw,
197         .round_waveform_fromhw = axi_pwmgen_round_waveform_fromhw,
198         .read_waveform = axi_pwmgen_read_waveform,
199         .write_waveform = axi_pwmgen_write_waveform,
200 };
201
202 static int axi_pwmgen_setup(struct regmap *regmap, struct device *dev)
203 {
204         int ret;
205         u32 val;
206
207         ret = regmap_read(regmap, AXI_PWMGEN_REG_CORE_MAGIC, &val);
208         if (ret)
209                 return ret;
210
211         if (val != AXI_PWMGEN_REG_CORE_MAGIC_VAL)
212                 return dev_err_probe(dev, -ENODEV,
213                         "failed to read expected value from register: got %08x, expected %08x\n",
214                         val, AXI_PWMGEN_REG_CORE_MAGIC_VAL);
215
216         ret = regmap_read(regmap, ADI_AXI_REG_VERSION, &val);
217         if (ret)
218                 return ret;
219
220         if (ADI_AXI_PCORE_VER_MAJOR(val) != 2) {
221                 return dev_err_probe(dev, -ENODEV, "Unsupported peripheral version %u.%u.%u\n",
222                         ADI_AXI_PCORE_VER_MAJOR(val),
223                         ADI_AXI_PCORE_VER_MINOR(val),
224                         ADI_AXI_PCORE_VER_PATCH(val));
225         }
226
227         /* Enable the core */
228         ret = regmap_clear_bits(regmap, AXI_PWMGEN_REG_RSTN, AXI_PWMGEN_REG_RSTN_RESET);
229         if (ret)
230                 return ret;
231
232         /*
233          * Enable force align so that changes to PWM period and duty cycle take
234          * effect immediately. Otherwise, the effect of the change is delayed
235          * until the period of all channels run out, which can be long after the
236          * apply function returns.
237          */
238         ret = regmap_set_bits(regmap, AXI_PWMGEN_REG_CONFIG, AXI_PWMGEN_REG_CONFIG_FORCE_ALIGN);
239         if (ret)
240                 return ret;
241
242         ret = regmap_read(regmap, AXI_PWMGEN_REG_NPWM, &val);
243         if (ret)
244                 return ret;
245
246         /* Return the number of PWMs */
247         return val;
248 }
249
250 static int axi_pwmgen_probe(struct platform_device *pdev)
251 {
252         struct device *dev = &pdev->dev;
253         struct regmap *regmap;
254         struct pwm_chip *chip;
255         struct axi_pwmgen_ddata *ddata;
256         struct clk *clk;
257         void __iomem *io_base;
258         int ret;
259
260         io_base = devm_platform_ioremap_resource(pdev, 0);
261         if (IS_ERR(io_base))
262                 return PTR_ERR(io_base);
263
264         regmap = devm_regmap_init_mmio(dev, io_base, &axi_pwmgen_regmap_config);
265         if (IS_ERR(regmap))
266                 return dev_err_probe(dev, PTR_ERR(regmap),
267                                      "failed to init register map\n");
268
269         ret = axi_pwmgen_setup(regmap, dev);
270         if (ret < 0)
271                 return ret;
272
273         chip = devm_pwmchip_alloc(dev, ret, sizeof(*ddata));
274         if (IS_ERR(chip))
275                 return PTR_ERR(chip);
276         ddata = pwmchip_get_drvdata(chip);
277         ddata->regmap = regmap;
278
279         clk = devm_clk_get_enabled(dev, NULL);
280         if (IS_ERR(clk))
281                 return dev_err_probe(dev, PTR_ERR(clk), "failed to get clock\n");
282
283         ret = devm_clk_rate_exclusive_get(dev, clk);
284         if (ret)
285                 return dev_err_probe(dev, ret, "failed to get exclusive rate\n");
286
287         ddata->clk_rate_hz = clk_get_rate(clk);
288         if (!ddata->clk_rate_hz || ddata->clk_rate_hz > NSEC_PER_SEC)
289                 return dev_err_probe(dev, -EINVAL,
290                                      "Invalid clock rate: %lu\n", ddata->clk_rate_hz);
291
292         chip->ops = &axi_pwmgen_pwm_ops;
293         chip->atomic = true;
294
295         ret = devm_pwmchip_add(dev, chip);
296         if (ret)
297                 return dev_err_probe(dev, ret, "could not add PWM chip\n");
298
299         return 0;
300 }
301
302 static const struct of_device_id axi_pwmgen_ids[] = {
303         { .compatible = "adi,axi-pwmgen-2.00.a" },
304         { }
305 };
306 MODULE_DEVICE_TABLE(of, axi_pwmgen_ids);
307
308 static struct platform_driver axi_pwmgen_driver = {
309         .driver = {
310                 .name = "axi-pwmgen",
311                 .of_match_table = axi_pwmgen_ids,
312         },
313         .probe = axi_pwmgen_probe,
314 };
315 module_platform_driver(axi_pwmgen_driver);
316
317 MODULE_LICENSE("GPL");
318 MODULE_AUTHOR("Sergiu Cuciurean <[email protected]>");
319 MODULE_AUTHOR("Trevor Gamblin <[email protected]>");
320 MODULE_DESCRIPTION("Driver for the Analog Devices AXI PWM generator");
This page took 0.044134 seconds and 4 git commands to generate.