]> Git Repo - linux.git/commitdiff
Merge tag 'pwm/duty_offset-for-6.13-rc1' of https://git.kernel.org/pub/scm/linux...
authorUwe Kleine-König <[email protected]>
Fri, 25 Oct 2024 09:41:46 +0000 (11:41 +0200)
committerUwe Kleine-König <[email protected]>
Fri, 25 Oct 2024 09:41:46 +0000 (11:41 +0200)
pwm: Support for duty_offset

Support a new abstraction for pwm configuration that allows to specify
the time between start of period and the raising edge of the signal
("duty offset").

This is used in a patch series by Trevor Gamblin for triggering an ADC
conversion and afterwards read out the result. See
https://lore.kernel.org/linux-iio/20240909-ad7625_r1-v5-0-60a397768b25@baylibre.com/
for more details.

1  2 
drivers/pwm/pwm-axi-pwmgen.c

index 5cee5645fe803dc4c49932f6a66f1a1774fab3aa,39d184417c7c0830f0eb5c1a865946a9b65a7d62..6e56ceb23d188393a31e3945287dbe4bae048764
@@@ -53,73 -54,117 +54,122 @@@ static const struct regmap_config axi_p
        .max_register = 0xFC,
  };
  
- static int axi_pwmgen_apply(struct pwm_chip *chip, struct pwm_device *pwm,
-                           const struct pwm_state *state)
+ /* This represents a hardware configuration for one channel */
+ struct axi_pwmgen_waveform {
+       u32 period_cnt;
+       u32 duty_cycle_cnt;
+       u32 duty_offset_cnt;
+ };
 +static struct axi_pwmgen_ddata *axi_pwmgen_ddata_from_chip(struct pwm_chip *chip)
 +{
 +      return pwmchip_get_drvdata(chip);
 +}
 +
+ static int axi_pwmgen_round_waveform_tohw(struct pwm_chip *chip,
+                                         struct pwm_device *pwm,
+                                         const struct pwm_waveform *wf,
+                                         void *_wfhw)
  {
 -      struct axi_pwmgen_ddata *ddata = pwmchip_get_drvdata(chip);
+       struct axi_pwmgen_waveform *wfhw = _wfhw;
-       unsigned int ch = pwm->hwpwm;
-       struct regmap *regmap = ddata->regmap;
-       u64 period_cnt, duty_cnt;
-       int ret;
 +      struct axi_pwmgen_ddata *ddata = axi_pwmgen_ddata_from_chip(chip);
  
-       if (state->polarity != PWM_POLARITY_NORMAL)
-               return -EINVAL;
+       if (wf->period_length_ns == 0) {
+               *wfhw = (struct axi_pwmgen_waveform){
+                       .period_cnt = 0,
+                       .duty_cycle_cnt = 0,
+                       .duty_offset_cnt = 0,
+               };
+       } else {
+               /* With ddata->clk_rate_hz < NSEC_PER_SEC this won't overflow. */
+               wfhw->period_cnt = min_t(u64,
+                                        mul_u64_u32_div(wf->period_length_ns, ddata->clk_rate_hz, NSEC_PER_SEC),
+                                        U32_MAX);
+               if (wfhw->period_cnt == 0) {
+                       /*
+                        * The specified period is too short for the hardware.
+                        * Let's round .duty_cycle down to 0 to get a (somewhat)
+                        * valid result.
+                        */
+                       wfhw->period_cnt = 1;
+                       wfhw->duty_cycle_cnt = 0;
+                       wfhw->duty_offset_cnt = 0;
+               } else {
+                       wfhw->duty_cycle_cnt = min_t(u64,
+                                                    mul_u64_u32_div(wf->duty_length_ns, ddata->clk_rate_hz, NSEC_PER_SEC),
+                                                    U32_MAX);
+                       wfhw->duty_offset_cnt = min_t(u64,
+                                                     mul_u64_u32_div(wf->duty_offset_ns, ddata->clk_rate_hz, NSEC_PER_SEC),
+                                                     U32_MAX);
+               }
+       }
  
-       if (state->enabled) {
-               period_cnt = mul_u64_u64_div_u64(state->period, ddata->clk_rate_hz, NSEC_PER_SEC);
-               if (period_cnt > UINT_MAX)
-                       period_cnt = UINT_MAX;
+       dev_dbg(&chip->dev, "pwm#%u: %lld/%lld [+%lld] @%lu -> PERIOD: %08x, DUTY: %08x, OFFSET: %08x\n",
+               pwm->hwpwm, wf->duty_length_ns, wf->period_length_ns, wf->duty_offset_ns,
+               ddata->clk_rate_hz, wfhw->period_cnt, wfhw->duty_cycle_cnt, wfhw->duty_offset_cnt);
  
-               if (period_cnt == 0)
-                       return -EINVAL;
+       return 0;
+ }
  
-               ret = regmap_write(regmap, AXI_PWMGEN_CHX_PERIOD(ch), period_cnt);
-               if (ret)
-                       return ret;
+ static int axi_pwmgen_round_waveform_fromhw(struct pwm_chip *chip, struct pwm_device *pwm,
+                                            const void *_wfhw, struct pwm_waveform *wf)
+ {
+       const struct axi_pwmgen_waveform *wfhw = _wfhw;
 -      struct axi_pwmgen_ddata *ddata = pwmchip_get_drvdata(chip);
++      struct axi_pwmgen_ddata *ddata = axi_pwmgen_ddata_from_chip(chip);
  
-               duty_cnt = mul_u64_u64_div_u64(state->duty_cycle, ddata->clk_rate_hz, NSEC_PER_SEC);
-               if (duty_cnt > UINT_MAX)
-                       duty_cnt = UINT_MAX;
+       wf->period_length_ns = DIV64_U64_ROUND_UP((u64)wfhw->period_cnt * NSEC_PER_SEC,
+                                       ddata->clk_rate_hz);
  
-               ret = regmap_write(regmap, AXI_PWMGEN_CHX_DUTY(ch), duty_cnt);
-               if (ret)
-                       return ret;
-       } else {
-               ret = regmap_write(regmap, AXI_PWMGEN_CHX_PERIOD(ch), 0);
-               if (ret)
-                       return ret;
+       wf->duty_length_ns = DIV64_U64_ROUND_UP((u64)wfhw->duty_cycle_cnt * NSEC_PER_SEC,
+                                           ddata->clk_rate_hz);
  
-               ret = regmap_write(regmap, AXI_PWMGEN_CHX_DUTY(ch), 0);
-               if (ret)
-                       return ret;
-       }
+       wf->duty_offset_ns = DIV64_U64_ROUND_UP((u64)wfhw->duty_offset_cnt * NSEC_PER_SEC,
+                                            ddata->clk_rate_hz);
  
-       return regmap_write(regmap, AXI_PWMGEN_REG_CONFIG, AXI_PWMGEN_LOAD_CONFIG);
+       return 0;
  }
  
- static int axi_pwmgen_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
-                               struct pwm_state *state)
+ static int axi_pwmgen_write_waveform(struct pwm_chip *chip,
+                                    struct pwm_device *pwm,
+                                    const void *_wfhw)
  {
 -      struct axi_pwmgen_ddata *ddata = pwmchip_get_drvdata(chip);
+       const struct axi_pwmgen_waveform *wfhw = _wfhw;
 +      struct axi_pwmgen_ddata *ddata = axi_pwmgen_ddata_from_chip(chip);
        struct regmap *regmap = ddata->regmap;
        unsigned int ch = pwm->hwpwm;
-       u32 cnt;
        int ret;
  
-       ret = regmap_read(regmap, AXI_PWMGEN_CHX_PERIOD(ch), &cnt);
+       ret = regmap_write(regmap, AXI_PWMGEN_CHX_PERIOD(ch), wfhw->period_cnt);
        if (ret)
                return ret;
  
-       state->enabled = cnt != 0;
+       ret = regmap_write(regmap, AXI_PWMGEN_CHX_DUTY(ch), wfhw->duty_cycle_cnt);
+       if (ret)
+               return ret;
  
-       state->period = DIV_ROUND_UP_ULL((u64)cnt * NSEC_PER_SEC, ddata->clk_rate_hz);
+       ret = regmap_write(regmap, AXI_PWMGEN_CHX_OFFSET(ch), wfhw->duty_offset_cnt);
+       if (ret)
+               return ret;
  
-       ret = regmap_read(regmap, AXI_PWMGEN_CHX_DUTY(ch), &cnt);
+       return regmap_write(regmap, AXI_PWMGEN_REG_CONFIG, AXI_PWMGEN_LOAD_CONFIG);
+ }
+ static int axi_pwmgen_read_waveform(struct pwm_chip *chip,
+                                   struct pwm_device *pwm,
+                                   void *_wfhw)
+ {
+       struct axi_pwmgen_waveform *wfhw = _wfhw;
 -      struct axi_pwmgen_ddata *ddata = pwmchip_get_drvdata(chip);
++      struct axi_pwmgen_ddata *ddata = axi_pwmgen_ddata_from_chip(chip);
+       struct regmap *regmap = ddata->regmap;
+       unsigned int ch = pwm->hwpwm;
+       int ret;
+       ret = regmap_read(regmap, AXI_PWMGEN_CHX_PERIOD(ch), &wfhw->period_cnt);
+       if (ret)
+               return ret;
+       ret = regmap_read(regmap, AXI_PWMGEN_CHX_DUTY(ch), &wfhw->duty_cycle_cnt);
        if (ret)
                return ret;
  
This page took 0.071704 seconds and 4 git commands to generate.