[PATCH 2/2 v2] pwm: axi-pwmgen: add duty offset support

From: Trevor Gamblin
Date: Tue Apr 09 2024 - 13:41:41 EST


Enable duty_offset feature now that it is supported in the pwm
subsystem. Related macros and struct fields related to duty_offset are
renamed to be consistent.

Signed-off-by: Trevor Gamblin <tgamblin@xxxxxxxxxxxx>
---
v2 changes:
* Address feedback for driver in v1:
* Remove line setting supports_offset flag in pwm_chip, since that has
been removed from the struct in core.c.

---
drivers/pwm/pwm-axi-pwmgen.c | 34 ++++++++++++++++++++++++++--------
1 file changed, 26 insertions(+), 8 deletions(-)

diff --git a/drivers/pwm/pwm-axi-pwmgen.c b/drivers/pwm/pwm-axi-pwmgen.c
index 539625c404ac..25a083003432 100644
--- a/drivers/pwm/pwm-axi-pwmgen.c
+++ b/drivers/pwm/pwm-axi-pwmgen.c
@@ -6,9 +6,9 @@
* Copyright 2024 Baylibre SAS
*
* Limitations:
- * - The writes to registers for period and duty are shadowed until
- * LOAD_CONFIG is written to AXI_PWMGEN_REG_CONFIG at the end of the
- * current period.
+ * - The writes to registers for period, duty, and duty_offset are
+ * shadowed until LOAD_CONFIG is written to AXI_PWMGEN_REG_CONFIG at
+ * the end of the current period.
* - Writing LOAD_CONFIG also has the effect of re-synchronizing all
* enabled channels, which could cause glitching on other channels. It
* is therefore expected that channels are assigned harmonic periods
@@ -34,7 +34,7 @@
#define AXI_PWMGEN_REG_NPWM 0x14
#define AXI_PWMGEN_CHX_PERIOD(v, ch) ((v)->period_base + (v)->ch_step * (ch))
#define AXI_PWMGEN_CHX_DUTY(v, ch) ((v)->duty_base + (v)->ch_step * (ch))
-#define AXI_PWMGEN_CHX_OFFSET(v, ch) ((v)->offset_base + (v)->ch_step * (ch))
+#define AXI_PWMGEN_CHX_DUTY_OFFSET(v, ch) ((v)->duty_offset_base + (v)->ch_step * (ch))
#define AXI_PWMGEN_REG_CORE_MAGIC_VAL 0x601A3471 /* Identification number to test during setup */
#define AXI_PWMGEN_LOAD_CONFIG BIT(1)
#define AXI_PWMGEN_RESET BIT(0)
@@ -42,7 +42,7 @@
struct axi_pwm_variant {
u8 period_base;
u8 duty_base;
- u8 offset_base;
+ u8 duty_offset_base;
u8 major_version;
u8 ch_step;
};
@@ -62,7 +62,7 @@ static const struct regmap_config axi_pwmgen_regmap_config = {
static const struct axi_pwm_variant pwmgen_1_00_variant = {
.period_base = 0x40,
.duty_base = 0x44,
- .offset_base = 0x48,
+ .duty_offset_base = 0x48,
.major_version = 1,
.ch_step = 12,
};
@@ -70,7 +70,7 @@ static const struct axi_pwm_variant pwmgen_1_00_variant = {
static const struct axi_pwm_variant pwmgen_2_00_variant = {
.period_base = 0x40,
.duty_base = 0x80,
- .offset_base = 0xC0,
+ .duty_offset_base = 0xC0,
.major_version = 2,
.ch_step = 4,
};
@@ -83,7 +83,7 @@ static int axi_pwmgen_apply(struct pwm_chip *chip, struct pwm_device *pwm,
unsigned int ch = pwm->hwpwm;
struct regmap *regmap = ddata->regmap;
const struct axi_pwm_variant *variant = ddata->variant;
- u64 period_cnt, duty_cnt;
+ u64 period_cnt, duty_cnt, duty_offset_cnt;
int ret;

if (state->polarity != PWM_POLARITY_NORMAL)
@@ -108,6 +108,14 @@ static int axi_pwmgen_apply(struct pwm_chip *chip, struct pwm_device *pwm,
ret = regmap_write(regmap, AXI_PWMGEN_CHX_DUTY(variant, ch), duty_cnt);
if (ret)
return ret;
+
+ duty_offset_cnt = mul_u64_u64_div_u64(state->duty_offset, ddata->clk_rate_hz, NSEC_PER_SEC);
+ if (duty_offset_cnt > UINT_MAX)
+ duty_offset_cnt = UINT_MAX;
+
+ ret = regmap_write(regmap, AXI_PWMGEN_CHX_DUTY_OFFSET(variant, ch), duty_offset_cnt);
+ if (ret)
+ return ret;
} else {
ret = regmap_write(regmap, AXI_PWMGEN_CHX_PERIOD(variant, ch), 0);
if (ret)
@@ -116,6 +124,10 @@ static int axi_pwmgen_apply(struct pwm_chip *chip, struct pwm_device *pwm,
ret = regmap_write(regmap, AXI_PWMGEN_CHX_DUTY(variant, ch), 0);
if (ret)
return ret;
+
+ ret = regmap_write(regmap, AXI_PWMGEN_CHX_DUTY_OFFSET(variant, ch), 0);
+ if (ret)
+ return ret;
}

return regmap_write(regmap, AXI_PWMGEN_REG_CONFIG, AXI_PWMGEN_LOAD_CONFIG);
@@ -145,6 +157,12 @@ static int axi_pwmgen_get_state(struct pwm_chip *chip, struct pwm_device *pwm,

state->duty_cycle = DIV_ROUND_UP_ULL((u64)cnt * NSEC_PER_SEC, ddata->clk_rate_hz);

+ ret = regmap_read(regmap, AXI_PWMGEN_CHX_DUTY_OFFSET(variant, ch), &cnt);
+ if (ret)
+ return ret;
+
+ state->duty_offset = DIV_ROUND_UP_ULL((u64)cnt * NSEC_PER_SEC, ddata->clk_rate_hz);
+
state->polarity = PWM_POLARITY_NORMAL;

return 0;
--
2.44.0