Lines Matching +full:axi +full:- +full:pwmgen +full:- +full:2

1 // SPDX-License-Identifier: GPL-2.0
3 * Analog Devices AXI PWM generator
11 * - The writes to registers for period and duty are shadowed until
14 * - Writing LOAD_CONFIG also has the effect of re-synchronizing all
18 * - Supports normal polarity. Does not support changing polarity.
19 * - On disable, the PWM output becomes low (inactive).
21 #include <linux/adi-axi-common.h>
80 if (wf->period_length_ns == 0) { in axi_pwmgen_round_waveform_tohw()
87 /* With ddata->clk_rate_hz < NSEC_PER_SEC this won't overflow. */ in axi_pwmgen_round_waveform_tohw()
88 wfhw->period_cnt = min_t(u64, in axi_pwmgen_round_waveform_tohw()
89 mul_u64_u32_div(wf->period_length_ns, ddata->clk_rate_hz, NSEC_PER_SEC), in axi_pwmgen_round_waveform_tohw()
92 if (wfhw->period_cnt == 0) { in axi_pwmgen_round_waveform_tohw()
100 wfhw->period_cnt = 1; in axi_pwmgen_round_waveform_tohw()
101 wfhw->duty_cycle_cnt = 0; in axi_pwmgen_round_waveform_tohw()
102 wfhw->duty_offset_cnt = 0; in axi_pwmgen_round_waveform_tohw()
105 wfhw->duty_cycle_cnt = min_t(u64, in axi_pwmgen_round_waveform_tohw()
106 mul_u64_u32_div(wf->duty_length_ns, ddata->clk_rate_hz, NSEC_PER_SEC), in axi_pwmgen_round_waveform_tohw()
108 wfhw->duty_offset_cnt = min_t(u64, in axi_pwmgen_round_waveform_tohw()
109 mul_u64_u32_div(wf->duty_offset_ns, ddata->clk_rate_hz, NSEC_PER_SEC), in axi_pwmgen_round_waveform_tohw()
114 dev_dbg(&chip->dev, "pwm#%u: %lld/%lld [+%lld] @%lu -> PERIOD: %08x, DUTY: %08x, OFFSET: %08x\n", in axi_pwmgen_round_waveform_tohw()
115 pwm->hwpwm, wf->duty_length_ns, wf->period_length_ns, wf->duty_offset_ns, in axi_pwmgen_round_waveform_tohw()
116 ddata->clk_rate_hz, wfhw->period_cnt, wfhw->duty_cycle_cnt, wfhw->duty_offset_cnt); in axi_pwmgen_round_waveform_tohw()
127 wf->period_length_ns = DIV64_U64_ROUND_UP((u64)wfhw->period_cnt * NSEC_PER_SEC, in axi_pwmgen_round_waveform_fromhw()
128 ddata->clk_rate_hz); in axi_pwmgen_round_waveform_fromhw()
130 wf->duty_length_ns = DIV64_U64_ROUND_UP((u64)wfhw->duty_cycle_cnt * NSEC_PER_SEC, in axi_pwmgen_round_waveform_fromhw()
131 ddata->clk_rate_hz); in axi_pwmgen_round_waveform_fromhw()
133 wf->duty_offset_ns = DIV64_U64_ROUND_UP((u64)wfhw->duty_offset_cnt * NSEC_PER_SEC, in axi_pwmgen_round_waveform_fromhw()
134 ddata->clk_rate_hz); in axi_pwmgen_round_waveform_fromhw()
145 struct regmap *regmap = ddata->regmap; in axi_pwmgen_write_waveform()
146 unsigned int ch = pwm->hwpwm; in axi_pwmgen_write_waveform()
149 ret = regmap_write(regmap, AXI_PWMGEN_CHX_PERIOD(ch), wfhw->period_cnt); in axi_pwmgen_write_waveform()
153 ret = regmap_write(regmap, AXI_PWMGEN_CHX_DUTY(ch), wfhw->duty_cycle_cnt); in axi_pwmgen_write_waveform()
157 ret = regmap_write(regmap, AXI_PWMGEN_CHX_OFFSET(ch), wfhw->duty_offset_cnt); in axi_pwmgen_write_waveform()
170 struct regmap *regmap = ddata->regmap; in axi_pwmgen_read_waveform()
171 unsigned int ch = pwm->hwpwm; in axi_pwmgen_read_waveform()
174 ret = regmap_read(regmap, AXI_PWMGEN_CHX_PERIOD(ch), &wfhw->period_cnt); in axi_pwmgen_read_waveform()
178 ret = regmap_read(regmap, AXI_PWMGEN_CHX_DUTY(ch), &wfhw->duty_cycle_cnt); in axi_pwmgen_read_waveform()
182 ret = regmap_read(regmap, AXI_PWMGEN_CHX_OFFSET(ch), &wfhw->duty_offset_cnt); in axi_pwmgen_read_waveform()
186 if (wfhw->duty_cycle_cnt > wfhw->period_cnt) in axi_pwmgen_read_waveform()
187 wfhw->duty_cycle_cnt = wfhw->period_cnt; in axi_pwmgen_read_waveform()
190 if (wfhw->duty_offset_cnt >= wfhw->period_cnt) { in axi_pwmgen_read_waveform()
191 wfhw->duty_cycle_cnt = 0; in axi_pwmgen_read_waveform()
192 wfhw->duty_offset_cnt = 0; in axi_pwmgen_read_waveform()
216 return dev_err_probe(dev, -ENODEV, in axi_pwmgen_setup()
224 if (ADI_AXI_PCORE_VER_MAJOR(val) != 2) { in axi_pwmgen_setup()
225 return dev_err_probe(dev, -ENODEV, "Unsupported peripheral version %u.%u.%u\n", in axi_pwmgen_setup()
256 struct device *dev = &pdev->dev; in axi_pwmgen_probe()
281 ddata->regmap = regmap; in axi_pwmgen_probe()
284 * Using NULL here instead of "axi" for backwards compatibility. There in axi_pwmgen_probe()
285 * are some dtbs that don't give clock-names and have the "ext" clock in axi_pwmgen_probe()
290 return dev_err_probe(dev, PTR_ERR(axi_clk), "failed to get axi clock\n"); in axi_pwmgen_probe()
298 * ASYNC_CLK_EN=0. In this case, the AXI clock is also used for the in axi_pwmgen_probe()
308 ddata->clk_rate_hz = clk_get_rate(clk); in axi_pwmgen_probe()
309 if (!ddata->clk_rate_hz || ddata->clk_rate_hz > NSEC_PER_SEC) in axi_pwmgen_probe()
310 return dev_err_probe(dev, -EINVAL, in axi_pwmgen_probe()
311 "Invalid clock rate: %lu\n", ddata->clk_rate_hz); in axi_pwmgen_probe()
313 chip->ops = &axi_pwmgen_pwm_ops; in axi_pwmgen_probe()
314 chip->atomic = true; in axi_pwmgen_probe()
324 { .compatible = "adi,axi-pwmgen-2.00.a" },
331 .name = "axi-pwmgen",
341 MODULE_DESCRIPTION("Driver for the Analog Devices AXI PWM generator");