linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v6 1/7] pwm: pca9685: Switch to atomic API
@ 2021-03-29 12:57 Clemens Gruber
  2021-03-29 12:57 ` [PATCH v6 2/7] pwm: pca9685: Support hardware readout Clemens Gruber
                   ` (5 more replies)
  0 siblings, 6 replies; 33+ messages in thread
From: Clemens Gruber @ 2021-03-29 12:57 UTC (permalink / raw)
  To: linux-pwm
  Cc: Thierry Reding, Sven Van Asbroeck, u.kleine-koenig, linux-kernel,
	Clemens Gruber

The switch to the atomic API goes hand in hand with a few fixes to
previously experienced issues:
- The duty cycle is no longer lost after disable/enable (previously the
  OFF registers were cleared in disable and the user was required to
  call config to restore the duty cycle settings)
- If one sets a period resulting in the same prescale register value,
  the sleep and write to the register is now skipped

Signed-off-by: Clemens Gruber <clemens.gruber@pqgruber.com>
---
Changes since v5:
- Function documentation for set_duty
- Variable initializations
- Print warning if all LEDs channel
- Changed EOPNOTSUPP to EINVAL
- Improved error messages
- Register reset corrections moved to this patch

Changes since v4:
- Patches split up
- Use a single set_duty function
- Improve readability / new macros
- Added a patch to restrict prescale changes to the first user

Changes since v3:
- Refactoring: Extracted common functions
- Read prescale register value instead of caching it
- Return all zeros and disabled for "all LEDs" channel state
- Improved duty calculation / mapping to 0..4096

Changes since v2:
- Always set default prescale value in probe
- Simplified probe code
- Inlined functions with one callsite

Changes since v1:
- Fixed a logic error
- Impoved PM runtime handling and fixed !CONFIG_PM
- Write default prescale reg value if invalid in probe
- Reuse full_off/_on functions throughout driver
- Use cached prescale value whenever possible

 drivers/pwm/pwm-pca9685.c | 261 ++++++++++++++------------------------
 1 file changed, 92 insertions(+), 169 deletions(-)

diff --git a/drivers/pwm/pwm-pca9685.c b/drivers/pwm/pwm-pca9685.c
index 4a55dc18656c..0ed1013737e3 100644
--- a/drivers/pwm/pwm-pca9685.c
+++ b/drivers/pwm/pwm-pca9685.c
@@ -51,7 +51,6 @@
 #define PCA9685_PRESCALE_MAX	0xFF	/* => min. frequency of 24 Hz */
 
 #define PCA9685_COUNTER_RANGE	4096
-#define PCA9685_DEFAULT_PERIOD	5000000	/* Default period_ns = 1/200 Hz */
 #define PCA9685_OSC_CLOCK_MHZ	25	/* Internal oscillator with 25 MHz */
 
 #define PCA9685_NUMREGS		0xFF
@@ -71,10 +70,14 @@
 #define LED_N_OFF_H(N)	(PCA9685_LEDX_OFF_H + (4 * (N)))
 #define LED_N_OFF_L(N)	(PCA9685_LEDX_OFF_L + (4 * (N)))
 
+#define REG_ON_H(C)	((C) >= PCA9685_MAXCHAN ? PCA9685_ALL_LED_ON_H : LED_N_ON_H((C)))
+#define REG_ON_L(C)	((C) >= PCA9685_MAXCHAN ? PCA9685_ALL_LED_ON_L : LED_N_ON_L((C)))
+#define REG_OFF_H(C)	((C) >= PCA9685_MAXCHAN ? PCA9685_ALL_LED_OFF_H : LED_N_OFF_H((C)))
+#define REG_OFF_L(C)	((C) >= PCA9685_MAXCHAN ? PCA9685_ALL_LED_OFF_L : LED_N_OFF_L((C)))
+
 struct pca9685 {
 	struct pwm_chip chip;
 	struct regmap *regmap;
-	int period_ns;
 #if IS_ENABLED(CONFIG_GPIOLIB)
 	struct mutex lock;
 	struct gpio_chip gpio;
@@ -87,6 +90,51 @@ static inline struct pca9685 *to_pca(struct pwm_chip *chip)
 	return container_of(chip, struct pca9685, chip);
 }
 
+/* Helper function to set the duty cycle ratio to duty/4096 (e.g. duty=2048 -> 50%) */
+static void pca9685_pwm_set_duty(struct pca9685 *pca, int channel, unsigned int duty)
+{
+	if (duty == 0) {
+		/* Set the full OFF bit, which has the highest precedence */
+		regmap_write(pca->regmap, REG_OFF_H(channel), LED_FULL);
+	} else if (duty >= PCA9685_COUNTER_RANGE) {
+		/* Set the full ON bit and clear the full OFF bit */
+		regmap_write(pca->regmap, REG_ON_H(channel), LED_FULL);
+		regmap_write(pca->regmap, REG_OFF_H(channel), 0);
+	} else {
+		/* Set OFF time (clears the full OFF bit) */
+		regmap_write(pca->regmap, REG_OFF_L(channel), duty & 0xff);
+		regmap_write(pca->regmap, REG_OFF_H(channel), (duty >> 8) & 0xf);
+		/* Clear the full ON bit */
+		regmap_write(pca->regmap, REG_ON_H(channel), 0);
+	}
+}
+
+static unsigned int pca9685_pwm_get_duty(struct pca9685 *pca, int channel)
+{
+	unsigned int off_h = 0, val = 0;
+
+	if (WARN_ON(channel >= PCA9685_MAXCHAN)) {
+		/* HW does not support reading state of "all LEDs" channel */
+		return 0;
+	}
+
+	regmap_read(pca->regmap, LED_N_OFF_H(channel), &off_h);
+	if (off_h & LED_FULL) {
+		/* Full OFF bit is set */
+		return 0;
+	}
+
+	regmap_read(pca->regmap, LED_N_ON_H(channel), &val);
+	if (val & LED_FULL) {
+		/* Full ON bit is set */
+		return PCA9685_COUNTER_RANGE;
+	}
+
+	val = 0;
+	regmap_read(pca->regmap, LED_N_OFF_L(channel), &val);
+	return ((off_h & 0xf) << 8) | (val & 0xff);
+}
+
 #if IS_ENABLED(CONFIG_GPIOLIB)
 static bool pca9685_pwm_test_and_set_inuse(struct pca9685 *pca, int pwm_idx)
 {
@@ -138,34 +186,23 @@ static int pca9685_pwm_gpio_request(struct gpio_chip *gpio, unsigned int offset)
 static int pca9685_pwm_gpio_get(struct gpio_chip *gpio, unsigned int offset)
 {
 	struct pca9685 *pca = gpiochip_get_data(gpio);
-	struct pwm_device *pwm = &pca->chip.pwms[offset];
-	unsigned int value;
 
-	regmap_read(pca->regmap, LED_N_ON_H(pwm->hwpwm), &value);
-
-	return value & LED_FULL;
+	return pca9685_pwm_get_duty(pca, offset) != 0;
 }
 
 static void pca9685_pwm_gpio_set(struct gpio_chip *gpio, unsigned int offset,
 				 int value)
 {
 	struct pca9685 *pca = gpiochip_get_data(gpio);
-	struct pwm_device *pwm = &pca->chip.pwms[offset];
-	unsigned int on = value ? LED_FULL : 0;
-
-	/* Clear both OFF registers */
-	regmap_write(pca->regmap, LED_N_OFF_L(pwm->hwpwm), 0);
-	regmap_write(pca->regmap, LED_N_OFF_H(pwm->hwpwm), 0);
 
-	/* Set the full ON bit */
-	regmap_write(pca->regmap, LED_N_ON_H(pwm->hwpwm), on);
+	pca9685_pwm_set_duty(pca, offset, value ? PCA9685_COUNTER_RANGE : 0);
 }
 
 static void pca9685_pwm_gpio_free(struct gpio_chip *gpio, unsigned int offset)
 {
 	struct pca9685 *pca = gpiochip_get_data(gpio);
 
-	pca9685_pwm_gpio_set(gpio, offset, 0);
+	pca9685_pwm_set_duty(pca, offset, 0);
 	pm_runtime_put(pca->chip.dev);
 	pca9685_pwm_clear_inuse(pca, offset);
 }
@@ -246,167 +283,56 @@ static void pca9685_set_sleep_mode(struct pca9685 *pca, bool enable)
 	}
 }
 
-static int pca9685_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
-			      int duty_ns, int period_ns)
+static int pca9685_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+			     const struct pwm_state *state)
 {
 	struct pca9685 *pca = to_pca(chip);
-	unsigned long long duty;
-	unsigned int reg;
-	int prescale;
-
-	if (period_ns != pca->period_ns) {
-		prescale = DIV_ROUND_CLOSEST(PCA9685_OSC_CLOCK_MHZ * period_ns,
-					     PCA9685_COUNTER_RANGE * 1000) - 1;
-
-		if (prescale >= PCA9685_PRESCALE_MIN &&
-			prescale <= PCA9685_PRESCALE_MAX) {
-			/*
-			 * Putting the chip briefly into SLEEP mode
-			 * at this point won't interfere with the
-			 * pm_runtime framework, because the pm_runtime
-			 * state is guaranteed active here.
-			 */
-			/* Put chip into sleep mode */
-			pca9685_set_sleep_mode(pca, true);
-
-			/* Change the chip-wide output frequency */
-			regmap_write(pca->regmap, PCA9685_PRESCALE, prescale);
-
-			/* Wake the chip up */
-			pca9685_set_sleep_mode(pca, false);
-
-			pca->period_ns = period_ns;
-		} else {
-			dev_err(chip->dev,
-				"prescaler not set: period out of bounds!\n");
-			return -EINVAL;
-		}
-	}
+	unsigned long long duty, prescale;
+	unsigned int val = 0;
 
-	if (duty_ns < 1) {
-		if (pwm->hwpwm >= PCA9685_MAXCHAN)
-			reg = PCA9685_ALL_LED_OFF_H;
-		else
-			reg = LED_N_OFF_H(pwm->hwpwm);
+	if (state->polarity != PWM_POLARITY_NORMAL)
+		return -EINVAL;
 
-		regmap_write(pca->regmap, reg, LED_FULL);
-
-		return 0;
+	prescale = DIV_ROUND_CLOSEST_ULL(PCA9685_OSC_CLOCK_MHZ * state->period,
+					 PCA9685_COUNTER_RANGE * 1000) - 1;
+	if (prescale < PCA9685_PRESCALE_MIN || prescale > PCA9685_PRESCALE_MAX) {
+		dev_err(chip->dev, "pwm not changed: period out of bounds!\n");
+		return -EINVAL;
 	}
 
-	if (duty_ns == period_ns) {
-		/* Clear both OFF registers */
-		if (pwm->hwpwm >= PCA9685_MAXCHAN)
-			reg = PCA9685_ALL_LED_OFF_L;
-		else
-			reg = LED_N_OFF_L(pwm->hwpwm);
-
-		regmap_write(pca->regmap, reg, 0x0);
-
-		if (pwm->hwpwm >= PCA9685_MAXCHAN)
-			reg = PCA9685_ALL_LED_OFF_H;
-		else
-			reg = LED_N_OFF_H(pwm->hwpwm);
-
-		regmap_write(pca->regmap, reg, 0x0);
-
-		/* Set the full ON bit */
-		if (pwm->hwpwm >= PCA9685_MAXCHAN)
-			reg = PCA9685_ALL_LED_ON_H;
-		else
-			reg = LED_N_ON_H(pwm->hwpwm);
-
-		regmap_write(pca->regmap, reg, LED_FULL);
+	duty = PCA9685_COUNTER_RANGE * state->duty_cycle;
+	duty = DIV_ROUND_CLOSEST_ULL(duty, state->period);
 
+	if (!state->enabled || duty < 1) {
+		pca9685_pwm_set_duty(pca, pwm->hwpwm, 0);
+		return 0;
+	} else if (duty == PCA9685_COUNTER_RANGE) {
+		pca9685_pwm_set_duty(pca, pwm->hwpwm, duty);
 		return 0;
 	}
 
-	duty = PCA9685_COUNTER_RANGE * (unsigned long long)duty_ns;
-	duty = DIV_ROUND_UP_ULL(duty, period_ns);
-
-	if (pwm->hwpwm >= PCA9685_MAXCHAN)
-		reg = PCA9685_ALL_LED_OFF_L;
-	else
-		reg = LED_N_OFF_L(pwm->hwpwm);
-
-	regmap_write(pca->regmap, reg, (int)duty & 0xff);
-
-	if (pwm->hwpwm >= PCA9685_MAXCHAN)
-		reg = PCA9685_ALL_LED_OFF_H;
-	else
-		reg = LED_N_OFF_H(pwm->hwpwm);
-
-	regmap_write(pca->regmap, reg, ((int)duty >> 8) & 0xf);
-
-	/* Clear the full ON bit, otherwise the set OFF time has no effect */
-	if (pwm->hwpwm >= PCA9685_MAXCHAN)
-		reg = PCA9685_ALL_LED_ON_H;
-	else
-		reg = LED_N_ON_H(pwm->hwpwm);
-
-	regmap_write(pca->regmap, reg, 0);
-
-	return 0;
-}
-
-static int pca9685_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
-{
-	struct pca9685 *pca = to_pca(chip);
-	unsigned int reg;
-
-	/*
-	 * The PWM subsystem does not support a pre-delay.
-	 * So, set the ON-timeout to 0
-	 */
-	if (pwm->hwpwm >= PCA9685_MAXCHAN)
-		reg = PCA9685_ALL_LED_ON_L;
-	else
-		reg = LED_N_ON_L(pwm->hwpwm);
-
-	regmap_write(pca->regmap, reg, 0);
-
-	if (pwm->hwpwm >= PCA9685_MAXCHAN)
-		reg = PCA9685_ALL_LED_ON_H;
-	else
-		reg = LED_N_ON_H(pwm->hwpwm);
-
-	regmap_write(pca->regmap, reg, 0);
+	regmap_read(pca->regmap, PCA9685_PRESCALE, &val);
+	if (prescale != val) {
+		/*
+		 * Putting the chip briefly into SLEEP mode
+		 * at this point won't interfere with the
+		 * pm_runtime framework, because the pm_runtime
+		 * state is guaranteed active here.
+		 */
+		/* Put chip into sleep mode */
+		pca9685_set_sleep_mode(pca, true);
 
-	/*
-	 * Clear the full-off bit.
-	 * It has precedence over the others and must be off.
-	 */
-	if (pwm->hwpwm >= PCA9685_MAXCHAN)
-		reg = PCA9685_ALL_LED_OFF_H;
-	else
-		reg = LED_N_OFF_H(pwm->hwpwm);
+		/* Change the chip-wide output frequency */
+		regmap_write(pca->regmap, PCA9685_PRESCALE, (int)prescale);
 
-	regmap_update_bits(pca->regmap, reg, LED_FULL, 0x0);
+		/* Wake the chip up */
+		pca9685_set_sleep_mode(pca, false);
+	}
 
+	pca9685_pwm_set_duty(pca, pwm->hwpwm, duty);
 	return 0;
 }
 
-static void pca9685_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
-{
-	struct pca9685 *pca = to_pca(chip);
-	unsigned int reg;
-
-	if (pwm->hwpwm >= PCA9685_MAXCHAN)
-		reg = PCA9685_ALL_LED_OFF_H;
-	else
-		reg = LED_N_OFF_H(pwm->hwpwm);
-
-	regmap_write(pca->regmap, reg, LED_FULL);
-
-	/* Clear the LED_OFF counter. */
-	if (pwm->hwpwm >= PCA9685_MAXCHAN)
-		reg = PCA9685_ALL_LED_OFF_L;
-	else
-		reg = LED_N_OFF_L(pwm->hwpwm);
-
-	regmap_write(pca->regmap, reg, 0x0);
-}
-
 static int pca9685_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
 {
 	struct pca9685 *pca = to_pca(chip);
@@ -422,15 +348,13 @@ static void pca9685_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
 {
 	struct pca9685 *pca = to_pca(chip);
 
-	pca9685_pwm_disable(chip, pwm);
+	pca9685_pwm_set_duty(pca, pwm->hwpwm, 0);
 	pm_runtime_put(chip->dev);
 	pca9685_pwm_clear_inuse(pca, pwm->hwpwm);
 }
 
 static const struct pwm_ops pca9685_pwm_ops = {
-	.enable = pca9685_pwm_enable,
-	.disable = pca9685_pwm_disable,
-	.config = pca9685_pwm_config,
+	.apply = pca9685_pwm_apply,
 	.request = pca9685_pwm_request,
 	.free = pca9685_pwm_free,
 	.owner = THIS_MODULE,
@@ -461,7 +385,6 @@ static int pca9685_pwm_probe(struct i2c_client *client,
 			ret);
 		return ret;
 	}
-	pca->period_ns = PCA9685_DEFAULT_PERIOD;
 
 	i2c_set_clientdata(client, pca);
 
@@ -484,9 +407,9 @@ static int pca9685_pwm_probe(struct i2c_client *client,
 	reg &= ~(MODE1_ALLCALL | MODE1_SUB1 | MODE1_SUB2 | MODE1_SUB3);
 	regmap_write(pca->regmap, PCA9685_MODE1, reg);
 
-	/* Clear all "full off" bits */
-	regmap_write(pca->regmap, PCA9685_ALL_LED_OFF_L, 0);
-	regmap_write(pca->regmap, PCA9685_ALL_LED_OFF_H, 0);
+	/* Reset OFF registers to POR default */
+	regmap_write(pca->regmap, PCA9685_ALL_LED_OFF_L, LED_FULL);
+	regmap_write(pca->regmap, PCA9685_ALL_LED_OFF_H, LED_FULL);
 
 	pca->chip.ops = &pca9685_pwm_ops;
 	/* Add an extra channel for ALL_LED */
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH v6 2/7] pwm: pca9685: Support hardware readout
  2021-03-29 12:57 [PATCH v6 1/7] pwm: pca9685: Switch to atomic API Clemens Gruber
@ 2021-03-29 12:57 ` Clemens Gruber
  2021-03-29 15:51   ` Uwe Kleine-König
  2021-03-29 16:54   ` Uwe Kleine-König
  2021-03-29 12:57 ` [PATCH v6 3/7] pwm: pca9685: Improve runtime PM behavior Clemens Gruber
                   ` (4 subsequent siblings)
  5 siblings, 2 replies; 33+ messages in thread
From: Clemens Gruber @ 2021-03-29 12:57 UTC (permalink / raw)
  To: linux-pwm
  Cc: Thierry Reding, Sven Van Asbroeck, u.kleine-koenig, linux-kernel,
	Clemens Gruber

Implements .get_state to read-out the current hardware state.

The hardware readout may return slightly different values than those
that were set in apply due to the limited range of possible prescale and
counter register values.

Also note that although the datasheet mentions 200 Hz as default
frequency when using the internal 25 MHz oscillator, the calculated
period from the default prescaler register setting of 30 is 5079040ns.

Signed-off-by: Clemens Gruber <clemens.gruber@pqgruber.com>
---
 drivers/pwm/pwm-pca9685.c | 41 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 41 insertions(+)

diff --git a/drivers/pwm/pwm-pca9685.c b/drivers/pwm/pwm-pca9685.c
index 0ed1013737e3..fb026a25fb61 100644
--- a/drivers/pwm/pwm-pca9685.c
+++ b/drivers/pwm/pwm-pca9685.c
@@ -333,6 +333,46 @@ static int pca9685_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
 	return 0;
 }
 
+static void pca9685_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
+				  struct pwm_state *state)
+{
+	struct pca9685 *pca = to_pca(chip);
+	unsigned long long duty;
+	unsigned int val = 0;
+
+	/* Calculate (chip-wide) period from prescale value */
+	regmap_read(pca->regmap, PCA9685_PRESCALE, &val);
+	state->period = (PCA9685_COUNTER_RANGE * 1000 / PCA9685_OSC_CLOCK_MHZ) *
+			(val + 1);
+
+	/* The (per-channel) polarity is fixed */
+	state->polarity = PWM_POLARITY_NORMAL;
+
+	if (pwm->hwpwm >= PCA9685_MAXCHAN) {
+		/*
+		 * The "all LEDs" channel does not support HW readout
+		 * Return 0 and disabled for backwards compatibility
+		 */
+		state->duty_cycle = 0;
+		state->enabled = false;
+		return;
+	}
+
+	duty = pca9685_pwm_get_duty(pca, pwm->hwpwm);
+
+	state->enabled = !!duty;
+	if (!state->enabled) {
+		state->duty_cycle = 0;
+		return;
+	} else if (duty == PCA9685_COUNTER_RANGE) {
+		state->duty_cycle = state->period;
+		return;
+	}
+
+	duty *= state->period;
+	state->duty_cycle = duty / PCA9685_COUNTER_RANGE;
+}
+
 static int pca9685_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
 {
 	struct pca9685 *pca = to_pca(chip);
@@ -355,6 +395,7 @@ static void pca9685_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
 
 static const struct pwm_ops pca9685_pwm_ops = {
 	.apply = pca9685_pwm_apply,
+	.get_state = pca9685_pwm_get_state,
 	.request = pca9685_pwm_request,
 	.free = pca9685_pwm_free,
 	.owner = THIS_MODULE,
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH v6 3/7] pwm: pca9685: Improve runtime PM behavior
  2021-03-29 12:57 [PATCH v6 1/7] pwm: pca9685: Switch to atomic API Clemens Gruber
  2021-03-29 12:57 ` [PATCH v6 2/7] pwm: pca9685: Support hardware readout Clemens Gruber
@ 2021-03-29 12:57 ` Clemens Gruber
  2021-03-29 15:55   ` Uwe Kleine-König
  2021-03-29 12:57 ` [PATCH v6 4/7] pwm: pca9685: Support staggered output ON times Clemens Gruber
                   ` (3 subsequent siblings)
  5 siblings, 1 reply; 33+ messages in thread
From: Clemens Gruber @ 2021-03-29 12:57 UTC (permalink / raw)
  To: linux-pwm
  Cc: Thierry Reding, Sven Van Asbroeck, u.kleine-koenig, linux-kernel,
	Clemens Gruber

The chip does not come out of POR in active state but in sleep state.
To be sure (in case the bootloader woke it up) we force it to sleep in
probe.

On kernels without CONFIG_PM, we wake the chip in .probe and put it to
sleep in .remove.

Signed-off-by: Clemens Gruber <clemens.gruber@pqgruber.com>
---
 drivers/pwm/pwm-pca9685.c | 20 ++++++++++++++++----
 1 file changed, 16 insertions(+), 4 deletions(-)

diff --git a/drivers/pwm/pwm-pca9685.c b/drivers/pwm/pwm-pca9685.c
index fb026a25fb61..4d6684b90819 100644
--- a/drivers/pwm/pwm-pca9685.c
+++ b/drivers/pwm/pwm-pca9685.c
@@ -469,14 +469,19 @@ static int pca9685_pwm_probe(struct i2c_client *client,
 		return ret;
 	}
 
-	/* The chip comes out of power-up in the active state */
-	pm_runtime_set_active(&client->dev);
 	/*
-	 * Enable will put the chip into suspend, which is what we
-	 * want as all outputs are disabled at this point
+	 * The chip comes out of power-up in the sleep state,
+	 * but force it to sleep in case it was woken up before
 	 */
+	pca9685_set_sleep_mode(pca, true);
+	pm_runtime_set_suspended(&client->dev);
 	pm_runtime_enable(&client->dev);
 
+	if (!IS_ENABLED(CONFIG_PM)) {
+		/* Wake the chip up on non-PM environments */
+		pca9685_set_sleep_mode(pca, false);
+	}
+
 	return 0;
 }
 
@@ -488,7 +493,14 @@ static int pca9685_pwm_remove(struct i2c_client *client)
 	ret = pwmchip_remove(&pca->chip);
 	if (ret)
 		return ret;
+
 	pm_runtime_disable(&client->dev);
+
+	if (!IS_ENABLED(CONFIG_PM)) {
+		/* Put chip in sleep state on non-PM environments */
+		pca9685_set_sleep_mode(pca, true);
+	}
+
 	return 0;
 }
 
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH v6 4/7] pwm: pca9685: Support staggered output ON times
  2021-03-29 12:57 [PATCH v6 1/7] pwm: pca9685: Switch to atomic API Clemens Gruber
  2021-03-29 12:57 ` [PATCH v6 2/7] pwm: pca9685: Support hardware readout Clemens Gruber
  2021-03-29 12:57 ` [PATCH v6 3/7] pwm: pca9685: Improve runtime PM behavior Clemens Gruber
@ 2021-03-29 12:57 ` Clemens Gruber
  2021-03-29 17:03   ` Uwe Kleine-König
  2021-03-29 12:57 ` [PATCH v6 5/7] dt-bindings: pwm: pca9685: Add nxp,staggered-outputs property Clemens Gruber
                   ` (2 subsequent siblings)
  5 siblings, 1 reply; 33+ messages in thread
From: Clemens Gruber @ 2021-03-29 12:57 UTC (permalink / raw)
  To: linux-pwm
  Cc: Thierry Reding, Sven Van Asbroeck, u.kleine-koenig, linux-kernel,
	Clemens Gruber

The PCA9685 supports staggered LED output ON times to minimize current
surges and reduce EMI.
When this new option is enabled, the ON times of each channel are
delayed by channel number x counter range / 16, which avoids asserting
all enabled outputs at the same counter value while still maintaining
the configured duty cycle of each output.

Signed-off-by: Clemens Gruber <clemens.gruber@pqgruber.com>
---
Changes since v5:
- Simplified staggered outputs special case in set/get_duty

drivers/pwm/pwm-pca9685.c | 58 +++++++++++++++++++++++++++++----------
 1 file changed, 44 insertions(+), 14 deletions(-)

diff --git a/drivers/pwm/pwm-pca9685.c b/drivers/pwm/pwm-pca9685.c
index 4d6684b90819..a61eafdd2335 100644
--- a/drivers/pwm/pwm-pca9685.c
+++ b/drivers/pwm/pwm-pca9685.c
@@ -78,6 +78,7 @@
 struct pca9685 {
 	struct pwm_chip chip;
 	struct regmap *regmap;
+	bool staggered_outputs;
 #if IS_ENABLED(CONFIG_GPIOLIB)
 	struct mutex lock;
 	struct gpio_chip gpio;
@@ -93,46 +94,70 @@ static inline struct pca9685 *to_pca(struct pwm_chip *chip)
 /* Helper function to set the duty cycle ratio to duty/4096 (e.g. duty=2048 -> 50%) */
 static void pca9685_pwm_set_duty(struct pca9685 *pca, int channel, unsigned int duty)
 {
+	unsigned int on, off;
+
 	if (duty == 0) {
 		/* Set the full OFF bit, which has the highest precedence */
 		regmap_write(pca->regmap, REG_OFF_H(channel), LED_FULL);
+		return;
 	} else if (duty >= PCA9685_COUNTER_RANGE) {
 		/* Set the full ON bit and clear the full OFF bit */
 		regmap_write(pca->regmap, REG_ON_H(channel), LED_FULL);
 		regmap_write(pca->regmap, REG_OFF_H(channel), 0);
-	} else {
-		/* Set OFF time (clears the full OFF bit) */
-		regmap_write(pca->regmap, REG_OFF_L(channel), duty & 0xff);
-		regmap_write(pca->regmap, REG_OFF_H(channel), (duty >> 8) & 0xf);
-		/* Clear the full ON bit */
-		regmap_write(pca->regmap, REG_ON_H(channel), 0);
+		return;
 	}
+
+	if (pca->staggered_outputs && channel < PCA9685_MAXCHAN) {
+		/*
+		 * To reduce EMI, the ON times of each channel are
+		 * spread out evenly within the counter range, while
+		 * still maintaining the configured duty cycle
+		 */
+		on = channel * PCA9685_COUNTER_RANGE / PCA9685_MAXCHAN;
+	} else
+		on = 0;
+
+	off = (on + duty) % PCA9685_COUNTER_RANGE;
+
+	/* Set ON time (clears full ON bit) */
+	regmap_write(pca->regmap, REG_ON_L(channel), on & 0xff);
+	regmap_write(pca->regmap, REG_ON_H(channel), (on >> 8) & 0xf);
+	/* Set OFF time (clears full OFF bit) */
+	regmap_write(pca->regmap, REG_OFF_L(channel), off & 0xff);
+	regmap_write(pca->regmap, REG_OFF_H(channel), (off >> 8) & 0xf);
 }
 
 static unsigned int pca9685_pwm_get_duty(struct pca9685 *pca, int channel)
 {
-	unsigned int off_h = 0, val = 0;
+	unsigned int off = 0, on = 0, val = 0;
 
 	if (WARN_ON(channel >= PCA9685_MAXCHAN)) {
 		/* HW does not support reading state of "all LEDs" channel */
 		return 0;
 	}
 
-	regmap_read(pca->regmap, LED_N_OFF_H(channel), &off_h);
-	if (off_h & LED_FULL) {
+	regmap_read(pca->regmap, LED_N_OFF_H(channel), &off);
+	if (off & LED_FULL) {
 		/* Full OFF bit is set */
 		return 0;
 	}
 
-	regmap_read(pca->regmap, LED_N_ON_H(channel), &val);
-	if (val & LED_FULL) {
+	regmap_read(pca->regmap, LED_N_ON_H(channel), &on);
+	if (on & LED_FULL) {
 		/* Full ON bit is set */
 		return PCA9685_COUNTER_RANGE;
 	}
 
-	val = 0;
 	regmap_read(pca->regmap, LED_N_OFF_L(channel), &val);
-	return ((off_h & 0xf) << 8) | (val & 0xff);
+	off = ((off & 0xf) << 8) | (val & 0xff);
+	if (!pca->staggered_outputs)
+		return off;
+
+	/* Read ON register to calculate duty cycle of staggered output */
+	val = 0;
+	regmap_read(pca->regmap, LED_N_ON_L(channel), &val);
+	on = ((on & 0xf) << 8) | (val & 0xff);
+	return (off - on) & (PCA9685_COUNTER_RANGE - 1);
 }
 
 #if IS_ENABLED(CONFIG_GPIOLIB)
@@ -443,14 +468,19 @@ static int pca9685_pwm_probe(struct i2c_client *client,
 
 	regmap_write(pca->regmap, PCA9685_MODE2, reg);
 
+	pca->staggered_outputs = device_property_read_bool(
+		&client->dev, "nxp,staggered-outputs");
+
 	/* Disable all LED ALLCALL and SUBx addresses to avoid bus collisions */
 	regmap_read(pca->regmap, PCA9685_MODE1, &reg);
 	reg &= ~(MODE1_ALLCALL | MODE1_SUB1 | MODE1_SUB2 | MODE1_SUB3);
 	regmap_write(pca->regmap, PCA9685_MODE1, reg);
 
-	/* Reset OFF registers to POR default */
+	/* Reset OFF/ON registers to POR default */
 	regmap_write(pca->regmap, PCA9685_ALL_LED_OFF_L, LED_FULL);
 	regmap_write(pca->regmap, PCA9685_ALL_LED_OFF_H, LED_FULL);
+	regmap_write(pca->regmap, PCA9685_ALL_LED_ON_L, 0);
+	regmap_write(pca->regmap, PCA9685_ALL_LED_ON_H, 0);
 
 	pca->chip.ops = &pca9685_pwm_ops;
 	/* Add an extra channel for ALL_LED */
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH v6 5/7] dt-bindings: pwm: pca9685: Add nxp,staggered-outputs property
  2021-03-29 12:57 [PATCH v6 1/7] pwm: pca9685: Switch to atomic API Clemens Gruber
                   ` (2 preceding siblings ...)
  2021-03-29 12:57 ` [PATCH v6 4/7] pwm: pca9685: Support staggered output ON times Clemens Gruber
@ 2021-03-29 12:57 ` Clemens Gruber
  2021-04-02 19:52   ` Uwe Kleine-König
  2021-03-29 12:57 ` [PATCH v6 6/7] pwm: pca9685: Restrict period change for prescaler users Clemens Gruber
  2021-03-29 12:57 ` [PATCH v6 7/7] pwm: pca9685: Add error messages for failed regmap calls Clemens Gruber
  5 siblings, 1 reply; 33+ messages in thread
From: Clemens Gruber @ 2021-03-29 12:57 UTC (permalink / raw)
  To: linux-pwm
  Cc: Thierry Reding, Sven Van Asbroeck, u.kleine-koenig, linux-kernel,
	Clemens Gruber

The pca9685 driver supports a new nxp,staggered-outputs property for
reduced current surges and EMI. This adds documentation for the new DT
property.

Signed-off-by: Clemens Gruber <clemens.gruber@pqgruber.com>
---
 Documentation/devicetree/bindings/pwm/nxp,pca9685-pwm.txt | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/Documentation/devicetree/bindings/pwm/nxp,pca9685-pwm.txt b/Documentation/devicetree/bindings/pwm/nxp,pca9685-pwm.txt
index f21b55c95738..fafe954369dc 100644
--- a/Documentation/devicetree/bindings/pwm/nxp,pca9685-pwm.txt
+++ b/Documentation/devicetree/bindings/pwm/nxp,pca9685-pwm.txt
@@ -12,6 +12,8 @@ Optional properties:
   - invert (bool): boolean to enable inverted logic
   - open-drain (bool): boolean to configure outputs with open-drain structure;
 		       if omitted use totem-pole structure
+  - nxp,staggered-outputs (bool): boolean to enable staggered output ON times to
+				  minimize current surges and EMI
 
 Example:
 
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH v6 6/7] pwm: pca9685: Restrict period change for prescaler users
  2021-03-29 12:57 [PATCH v6 1/7] pwm: pca9685: Switch to atomic API Clemens Gruber
                   ` (3 preceding siblings ...)
  2021-03-29 12:57 ` [PATCH v6 5/7] dt-bindings: pwm: pca9685: Add nxp,staggered-outputs property Clemens Gruber
@ 2021-03-29 12:57 ` Clemens Gruber
  2021-03-29 17:15   ` Uwe Kleine-König
  2021-03-29 12:57 ` [PATCH v6 7/7] pwm: pca9685: Add error messages for failed regmap calls Clemens Gruber
  5 siblings, 1 reply; 33+ messages in thread
From: Clemens Gruber @ 2021-03-29 12:57 UTC (permalink / raw)
  To: linux-pwm
  Cc: Thierry Reding, Sven Van Asbroeck, u.kleine-koenig, linux-kernel,
	Clemens Gruber

Previously, the last used PWM channel could change the global prescale
setting, even if other channels are already in use.

Fix it by only allowing the first user of the prescaler to change the
global chip-wide prescale setting. If there is more than one channel in
use, the prescale settings resulting from the chosen periods must match.

PWMs that are disabled or have a duty cycle of 0% or 100% are not
considered to be using the prescaler as they have the full OFF or full
ON bits set. This also applies to channels used as GPIOs.

Signed-off-by: Clemens Gruber <clemens.gruber@pqgruber.com>
---
Changes since v5:
- Reused the existing lock
- Improved readability of can_change function
- Moved locking out of apply function
- Improved error messages

drivers/pwm/pwm-pca9685.c | 63 ++++++++++++++++++++++++++++++++-------
 1 file changed, 53 insertions(+), 10 deletions(-)

diff --git a/drivers/pwm/pwm-pca9685.c b/drivers/pwm/pwm-pca9685.c
index a61eafdd2335..418d3c5b2fa2 100644
--- a/drivers/pwm/pwm-pca9685.c
+++ b/drivers/pwm/pwm-pca9685.c
@@ -23,11 +23,11 @@
 #include <linux/bitmap.h>
 
 /*
- * Because the PCA9685 has only one prescaler per chip, changing the period of
- * one channel affects the period of all 16 PWM outputs!
- * However, the ratio between each configured duty cycle and the chip-wide
- * period remains constant, because the OFF time is set in proportion to the
- * counter range.
+ * Because the PCA9685 has only one prescaler per chip, only the first channel
+ * that uses the prescaler is allowed to change the prescale register.
+ * PWM channels requested afterwards must use a period that results in the same
+ * prescale setting as the one set by the first requested channel, unless they
+ * use duty cycles of 0% or 100% (prescaler not used for full OFF/ON).
  */
 
 #define PCA9685_MODE1		0x00
@@ -79,8 +79,9 @@ struct pca9685 {
 	struct pwm_chip chip;
 	struct regmap *regmap;
 	bool staggered_outputs;
-#if IS_ENABLED(CONFIG_GPIOLIB)
 	struct mutex lock;
+	DECLARE_BITMAP(prescaler_users, PCA9685_MAXCHAN + 1);
+#if IS_ENABLED(CONFIG_GPIOLIB)
 	struct gpio_chip gpio;
 	DECLARE_BITMAP(pwms_inuse, PCA9685_MAXCHAN + 1);
 #endif
@@ -91,6 +92,22 @@ static inline struct pca9685 *to_pca(struct pwm_chip *chip)
 	return container_of(chip, struct pca9685, chip);
 }
 
+/* This function is supposed to be called with the lock mutex held */
+static bool pca9685_prescaler_can_change(struct pca9685 *pca, int channel)
+{
+	/* Prescaler not in use: Change allowed */
+	if (bitmap_empty(pca->prescaler_users, PCA9685_MAXCHAN + 1))
+		return true;
+	/* More than one PWM using the prescaler: Change not allowed */
+	if (bitmap_weight(pca->prescaler_users, PCA9685_MAXCHAN + 1) > 1)
+		return false;
+	/*
+	 * Only one PWM using the prescaler: Change allowed if the PWM about to
+	 * be changed is the one using the prescaler
+	 */
+	return test_bit(channel, pca->prescaler_users);
+}
+
 /* Helper function to set the duty cycle ratio to duty/4096 (e.g. duty=2048 -> 50%) */
 static void pca9685_pwm_set_duty(struct pca9685 *pca, int channel, unsigned int duty)
 {
@@ -263,8 +280,6 @@ static int pca9685_pwm_gpio_probe(struct pca9685 *pca)
 {
 	struct device *dev = pca->chip.dev;
 
-	mutex_init(&pca->lock);
-
 	pca->gpio.label = dev_name(dev);
 	pca->gpio.parent = dev;
 	pca->gpio.request = pca9685_pwm_gpio_request;
@@ -308,8 +323,8 @@ static void pca9685_set_sleep_mode(struct pca9685 *pca, bool enable)
 	}
 }
 
-static int pca9685_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
-			     const struct pwm_state *state)
+static int __pca9685_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+			       const struct pwm_state *state)
 {
 	struct pca9685 *pca = to_pca(chip);
 	unsigned long long duty, prescale;
@@ -330,14 +345,22 @@ static int pca9685_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
 
 	if (!state->enabled || duty < 1) {
 		pca9685_pwm_set_duty(pca, pwm->hwpwm, 0);
+		clear_bit(pwm->hwpwm, pca->prescaler_users);
 		return 0;
 	} else if (duty == PCA9685_COUNTER_RANGE) {
 		pca9685_pwm_set_duty(pca, pwm->hwpwm, duty);
+		clear_bit(pwm->hwpwm, pca->prescaler_users);
 		return 0;
 	}
 
 	regmap_read(pca->regmap, PCA9685_PRESCALE, &val);
 	if (prescale != val) {
+		if (!pca9685_prescaler_can_change(pca, pwm->hwpwm)) {
+			dev_err(chip->dev,
+				"pwm not changed: prescaler in use with different setting!\n");
+			return -EBUSY;
+		}
+
 		/*
 		 * Putting the chip briefly into SLEEP mode
 		 * at this point won't interfere with the
@@ -355,9 +378,23 @@ static int pca9685_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
 	}
 
 	pca9685_pwm_set_duty(pca, pwm->hwpwm, duty);
+	set_bit(pwm->hwpwm, pca->prescaler_users);
 	return 0;
 }
 
+static int pca9685_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+			     const struct pwm_state *state)
+{
+	struct pca9685 *pca = to_pca(chip);
+	int ret;
+
+	mutex_lock(&pca->lock);
+	ret = __pca9685_pwm_apply(chip, pwm, state);
+	mutex_unlock(&pca->lock);
+
+	return ret;
+}
+
 static void pca9685_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
 				  struct pwm_state *state)
 {
@@ -413,7 +450,11 @@ static void pca9685_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
 {
 	struct pca9685 *pca = to_pca(chip);
 
+	mutex_lock(&pca->lock);
+	clear_bit(pwm->hwpwm, pca->prescaler_users);
 	pca9685_pwm_set_duty(pca, pwm->hwpwm, 0);
+	mutex_unlock(&pca->lock);
+
 	pm_runtime_put(chip->dev);
 	pca9685_pwm_clear_inuse(pca, pwm->hwpwm);
 }
@@ -454,6 +495,8 @@ static int pca9685_pwm_probe(struct i2c_client *client,
 
 	i2c_set_clientdata(client, pca);
 
+	mutex_init(&pca->lock);
+
 	regmap_read(pca->regmap, PCA9685_MODE2, &reg);
 
 	if (device_property_read_bool(&client->dev, "invert"))
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH v6 7/7] pwm: pca9685: Add error messages for failed regmap calls
  2021-03-29 12:57 [PATCH v6 1/7] pwm: pca9685: Switch to atomic API Clemens Gruber
                   ` (4 preceding siblings ...)
  2021-03-29 12:57 ` [PATCH v6 6/7] pwm: pca9685: Restrict period change for prescaler users Clemens Gruber
@ 2021-03-29 12:57 ` Clemens Gruber
  5 siblings, 0 replies; 33+ messages in thread
From: Clemens Gruber @ 2021-03-29 12:57 UTC (permalink / raw)
  To: linux-pwm
  Cc: Thierry Reding, Sven Van Asbroeck, u.kleine-koenig, linux-kernel,
	Clemens Gruber

Regmap operations can fail if the underlying subsystem is not working
properly (e.g. hogged I2C bus, etc.)
As this is useful information for the user, print an error message if it
happens.
Let probe fail if the first regmap_read or the first regmap_write fails.

Signed-off-by: Clemens Gruber <clemens.gruber@pqgruber.com>
---
 drivers/pwm/pwm-pca9685.c | 83 ++++++++++++++++++++++++++++-----------
 1 file changed, 59 insertions(+), 24 deletions(-)

diff --git a/drivers/pwm/pwm-pca9685.c b/drivers/pwm/pwm-pca9685.c
index 418d3c5b2fa2..065141b87fc3 100644
--- a/drivers/pwm/pwm-pca9685.c
+++ b/drivers/pwm/pwm-pca9685.c
@@ -108,6 +108,30 @@ static bool pca9685_prescaler_can_change(struct pca9685 *pca, int channel)
 	return test_bit(channel, pca->prescaler_users);
 }
 
+static int pca9685_read_reg(struct pca9685 *pca, unsigned int reg, unsigned int *val)
+{
+	struct device *dev = pca->chip.dev;
+	int err;
+
+	err = regmap_read(pca->regmap, reg, val);
+	if (err != 0)
+		dev_err(dev, "regmap_read of register 0x%x failed: %d\n", reg, err);
+
+	return err;
+}
+
+static int pca9685_write_reg(struct pca9685 *pca, unsigned int reg, unsigned int val)
+{
+	struct device *dev = pca->chip.dev;
+	int err;
+
+	err = regmap_write(pca->regmap, reg, val);
+	if (err != 0)
+		dev_err(dev, "regmap_write to register 0x%x failed: %d\n", reg, err);
+
+	return err;
+}
+
 /* Helper function to set the duty cycle ratio to duty/4096 (e.g. duty=2048 -> 50%) */
 static void pca9685_pwm_set_duty(struct pca9685 *pca, int channel, unsigned int duty)
 {
@@ -115,12 +139,12 @@ static void pca9685_pwm_set_duty(struct pca9685 *pca, int channel, unsigned int
 
 	if (duty == 0) {
 		/* Set the full OFF bit, which has the highest precedence */
-		regmap_write(pca->regmap, REG_OFF_H(channel), LED_FULL);
+		pca9685_write_reg(pca, REG_OFF_H(channel), LED_FULL);
 		return;
 	} else if (duty >= PCA9685_COUNTER_RANGE) {
 		/* Set the full ON bit and clear the full OFF bit */
-		regmap_write(pca->regmap, REG_ON_H(channel), LED_FULL);
-		regmap_write(pca->regmap, REG_OFF_H(channel), 0);
+		pca9685_write_reg(pca, REG_ON_H(channel), LED_FULL);
+		pca9685_write_reg(pca, REG_OFF_H(channel), 0);
 		return;
 	}
 
@@ -137,11 +161,11 @@ static void pca9685_pwm_set_duty(struct pca9685 *pca, int channel, unsigned int
 	off = (on + duty) % PCA9685_COUNTER_RANGE;
 
 	/* Set ON time (clears full ON bit) */
-	regmap_write(pca->regmap, REG_ON_L(channel), on & 0xff);
-	regmap_write(pca->regmap, REG_ON_H(channel), (on >> 8) & 0xf);
+	pca9685_write_reg(pca, REG_ON_L(channel), on & 0xff);
+	pca9685_write_reg(pca, REG_ON_H(channel), (on >> 8) & 0xf);
 	/* Set OFF time (clears full OFF bit) */
-	regmap_write(pca->regmap, REG_OFF_L(channel), off & 0xff);
-	regmap_write(pca->regmap, REG_OFF_H(channel), (off >> 8) & 0xf);
+	pca9685_write_reg(pca, REG_OFF_L(channel), off & 0xff);
+	pca9685_write_reg(pca, REG_OFF_H(channel), (off >> 8) & 0xf);
 }
 
 static unsigned int pca9685_pwm_get_duty(struct pca9685 *pca, int channel)
@@ -153,26 +177,26 @@ static unsigned int pca9685_pwm_get_duty(struct pca9685 *pca, int channel)
 		return 0;
 	}
 
-	regmap_read(pca->regmap, LED_N_OFF_H(channel), &off);
+	pca9685_read_reg(pca, LED_N_OFF_H(channel), &off);
 	if (off & LED_FULL) {
 		/* Full OFF bit is set */
 		return 0;
 	}
 
-	regmap_read(pca->regmap, LED_N_ON_H(channel), &on);
+	pca9685_read_reg(pca, LED_N_ON_H(channel), &on);
 	if (on & LED_FULL) {
 		/* Full ON bit is set */
 		return PCA9685_COUNTER_RANGE;
 	}
 
-	regmap_read(pca->regmap, LED_N_OFF_L(channel), &val);
+	pca9685_read_reg(pca, LED_N_OFF_L(channel), &val);
 	off = ((off & 0xf) << 8) | (val & 0xff);
 	if (!pca->staggered_outputs)
 		return off;
 
 	/* Read ON register to calculate duty cycle of staggered output */
 	val = 0;
-	regmap_read(pca->regmap, LED_N_ON_L(channel), &val);
+	pca9685_read_reg(pca, LED_N_ON_L(channel), &val);
 	on = ((on & 0xf) << 8) | (val & 0xff);
 	return (off - on) & (PCA9685_COUNTER_RANGE - 1);
 }
@@ -315,8 +339,15 @@ static inline int pca9685_pwm_gpio_probe(struct pca9685 *pca)
 
 static void pca9685_set_sleep_mode(struct pca9685 *pca, bool enable)
 {
-	regmap_update_bits(pca->regmap, PCA9685_MODE1,
-			   MODE1_SLEEP, enable ? MODE1_SLEEP : 0);
+	struct device *dev = pca->chip.dev;
+	int err = regmap_update_bits(pca->regmap, PCA9685_MODE1,
+				     MODE1_SLEEP, enable ? MODE1_SLEEP : 0);
+	if (err != 0) {
+		dev_err(dev, "regmap_update_bits of register 0x%x failed: %d\n",
+			PCA9685_MODE1, err);
+		return;
+	}
+
 	if (!enable) {
 		/* Wait 500us for the oscillator to be back up */
 		udelay(500);
@@ -353,7 +384,7 @@ static int __pca9685_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
 		return 0;
 	}
 
-	regmap_read(pca->regmap, PCA9685_PRESCALE, &val);
+	pca9685_read_reg(pca, PCA9685_PRESCALE, &val);
 	if (prescale != val) {
 		if (!pca9685_prescaler_can_change(pca, pwm->hwpwm)) {
 			dev_err(chip->dev,
@@ -371,7 +402,7 @@ static int __pca9685_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
 		pca9685_set_sleep_mode(pca, true);
 
 		/* Change the chip-wide output frequency */
-		regmap_write(pca->regmap, PCA9685_PRESCALE, (int)prescale);
+		pca9685_write_reg(pca, PCA9685_PRESCALE, (int)prescale);
 
 		/* Wake the chip up */
 		pca9685_set_sleep_mode(pca, false);
@@ -403,7 +434,7 @@ static void pca9685_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
 	unsigned int val = 0;
 
 	/* Calculate (chip-wide) period from prescale value */
-	regmap_read(pca->regmap, PCA9685_PRESCALE, &val);
+	pca9685_read_reg(pca, PCA9685_PRESCALE, &val);
 	state->period = (PCA9685_COUNTER_RANGE * 1000 / PCA9685_OSC_CLOCK_MHZ) *
 			(val + 1);
 
@@ -497,7 +528,9 @@ static int pca9685_pwm_probe(struct i2c_client *client,
 
 	mutex_init(&pca->lock);
 
-	regmap_read(pca->regmap, PCA9685_MODE2, &reg);
+	ret = pca9685_read_reg(pca, PCA9685_MODE2, &reg);
+	if (ret != 0)
+		return ret;
 
 	if (device_property_read_bool(&client->dev, "invert"))
 		reg |= MODE2_INVRT;
@@ -509,21 +542,23 @@ static int pca9685_pwm_probe(struct i2c_client *client,
 	else
 		reg |= MODE2_OUTDRV;
 
-	regmap_write(pca->regmap, PCA9685_MODE2, reg);
+	ret = pca9685_write_reg(pca, PCA9685_MODE2, reg);
+	if (ret != 0)
+		return ret;
 
 	pca->staggered_outputs = device_property_read_bool(
 		&client->dev, "nxp,staggered-outputs");
 
 	/* Disable all LED ALLCALL and SUBx addresses to avoid bus collisions */
-	regmap_read(pca->regmap, PCA9685_MODE1, &reg);
+	pca9685_read_reg(pca, PCA9685_MODE1, &reg);
 	reg &= ~(MODE1_ALLCALL | MODE1_SUB1 | MODE1_SUB2 | MODE1_SUB3);
-	regmap_write(pca->regmap, PCA9685_MODE1, reg);
+	pca9685_write_reg(pca, PCA9685_MODE1, reg);
 
 	/* Reset OFF/ON registers to POR default */
-	regmap_write(pca->regmap, PCA9685_ALL_LED_OFF_L, LED_FULL);
-	regmap_write(pca->regmap, PCA9685_ALL_LED_OFF_H, LED_FULL);
-	regmap_write(pca->regmap, PCA9685_ALL_LED_ON_L, 0);
-	regmap_write(pca->regmap, PCA9685_ALL_LED_ON_H, 0);
+	pca9685_write_reg(pca, PCA9685_ALL_LED_OFF_L, LED_FULL);
+	pca9685_write_reg(pca, PCA9685_ALL_LED_OFF_H, LED_FULL);
+	pca9685_write_reg(pca, PCA9685_ALL_LED_ON_L, 0);
+	pca9685_write_reg(pca, PCA9685_ALL_LED_ON_H, 0);
 
 	pca->chip.ops = &pca9685_pwm_ops;
 	/* Add an extra channel for ALL_LED */
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* Re: [PATCH v6 2/7] pwm: pca9685: Support hardware readout
  2021-03-29 12:57 ` [PATCH v6 2/7] pwm: pca9685: Support hardware readout Clemens Gruber
@ 2021-03-29 15:51   ` Uwe Kleine-König
  2021-03-29 16:33     ` Clemens Gruber
  2021-03-29 16:54   ` Uwe Kleine-König
  1 sibling, 1 reply; 33+ messages in thread
From: Uwe Kleine-König @ 2021-03-29 15:51 UTC (permalink / raw)
  To: Clemens Gruber
  Cc: linux-pwm, Thierry Reding, Sven Van Asbroeck, linux-kernel, kernel

[-- Attachment #1: Type: text/plain, Size: 619 bytes --]

Hello Clemens,

On Mon, Mar 29, 2021 at 02:57:02PM +0200, Clemens Gruber wrote:
> The hardware readout may return slightly different values than those
> that were set in apply due to the limited range of possible prescale and
> counter register values.

This is fine and for most hardware that's not preventable. My
requirement here is that 

	.get_state(pwm, &state);
	.apply_state(pwm, &state);

doesn't yield any changes.

Best regards
Uwe

-- 
Pengutronix e.K.                           | Uwe Kleine-König            |
Industrial Linux Solutions                 | https://www.pengutronix.de/ |

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v6 3/7] pwm: pca9685: Improve runtime PM behavior
  2021-03-29 12:57 ` [PATCH v6 3/7] pwm: pca9685: Improve runtime PM behavior Clemens Gruber
@ 2021-03-29 15:55   ` Uwe Kleine-König
  2021-03-29 16:31     ` Clemens Gruber
  0 siblings, 1 reply; 33+ messages in thread
From: Uwe Kleine-König @ 2021-03-29 15:55 UTC (permalink / raw)
  To: Clemens Gruber; +Cc: linux-pwm, Thierry Reding, Sven Van Asbroeck, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 1871 bytes --]

On Mon, Mar 29, 2021 at 02:57:03PM +0200, Clemens Gruber wrote:
> The chip does not come out of POR in active state but in sleep state.
> To be sure (in case the bootloader woke it up) we force it to sleep in
> probe.
> 
> On kernels without CONFIG_PM, we wake the chip in .probe and put it to
> sleep in .remove.

What is the effect of sleep state? Does it continue to oscilate it the
bootloader set up some configuration?


> Signed-off-by: Clemens Gruber <clemens.gruber@pqgruber.com>
> ---
>  drivers/pwm/pwm-pca9685.c | 20 ++++++++++++++++----
>  1 file changed, 16 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/pwm/pwm-pca9685.c b/drivers/pwm/pwm-pca9685.c
> index fb026a25fb61..4d6684b90819 100644
> --- a/drivers/pwm/pwm-pca9685.c
> +++ b/drivers/pwm/pwm-pca9685.c
> @@ -469,14 +469,19 @@ static int pca9685_pwm_probe(struct i2c_client *client,
>  		return ret;
>  	}
>  
> -	/* The chip comes out of power-up in the active state */
> -	pm_runtime_set_active(&client->dev);
>  	/*
> -	 * Enable will put the chip into suspend, which is what we
> -	 * want as all outputs are disabled at this point
> +	 * The chip comes out of power-up in the sleep state,
> +	 * but force it to sleep in case it was woken up before
>  	 */
> +	pca9685_set_sleep_mode(pca, true);
> +	pm_runtime_set_suspended(&client->dev);
>  	pm_runtime_enable(&client->dev);
>  
> +	if (!IS_ENABLED(CONFIG_PM)) {
> +		/* Wake the chip up on non-PM environments */
> +		pca9685_set_sleep_mode(pca, false);

I admit I didn't grasp all the PM framework details, but I wonder if
it's right to first call set_sleep_mode(true) and then in some cases to
false again.

Best regards
Uwe

-- 
Pengutronix e.K.                           | Uwe Kleine-König            |
Industrial Linux Solutions                 | https://www.pengutronix.de/ |

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v6 3/7] pwm: pca9685: Improve runtime PM behavior
  2021-03-29 15:55   ` Uwe Kleine-König
@ 2021-03-29 16:31     ` Clemens Gruber
  0 siblings, 0 replies; 33+ messages in thread
From: Clemens Gruber @ 2021-03-29 16:31 UTC (permalink / raw)
  To: Uwe Kleine-König
  Cc: linux-pwm, Thierry Reding, Sven Van Asbroeck, linux-kernel

Hi Uwe,

On Mon, Mar 29, 2021 at 05:55:27PM +0200, Uwe Kleine-König wrote:
> On Mon, Mar 29, 2021 at 02:57:03PM +0200, Clemens Gruber wrote:
> > The chip does not come out of POR in active state but in sleep state.
> > To be sure (in case the bootloader woke it up) we force it to sleep in
> > probe.
> > 
> > On kernels without CONFIG_PM, we wake the chip in .probe and put it to
> > sleep in .remove.
> 
> What is the effect of sleep state? Does it continue to oscilate it the
> bootloader set up some configuration?

The datasheet says: "When the oscillator is off (Sleep mode) the LEDn
outputs cannot be turned on, off or dimmed/blinked."

At the moment, we reset the output registers anyway, so everything is
turned off at probe time, even if the bootloader did set something up.

When removing the resets in the future, I would read out the state of
the SLEEP bit at probe time and set the pm runtime state accordingly.

> 
> 
> > Signed-off-by: Clemens Gruber <clemens.gruber@pqgruber.com>
> > ---
> >  drivers/pwm/pwm-pca9685.c | 20 ++++++++++++++++----
> >  1 file changed, 16 insertions(+), 4 deletions(-)
> > 
> > diff --git a/drivers/pwm/pwm-pca9685.c b/drivers/pwm/pwm-pca9685.c
> > index fb026a25fb61..4d6684b90819 100644
> > --- a/drivers/pwm/pwm-pca9685.c
> > +++ b/drivers/pwm/pwm-pca9685.c
> > @@ -469,14 +469,19 @@ static int pca9685_pwm_probe(struct i2c_client *client,
> >  		return ret;
> >  	}
> >  
> > -	/* The chip comes out of power-up in the active state */
> > -	pm_runtime_set_active(&client->dev);
> >  	/*
> > -	 * Enable will put the chip into suspend, which is what we
> > -	 * want as all outputs are disabled at this point
> > +	 * The chip comes out of power-up in the sleep state,
> > +	 * but force it to sleep in case it was woken up before
> >  	 */
> > +	pca9685_set_sleep_mode(pca, true);
> > +	pm_runtime_set_suspended(&client->dev);
> >  	pm_runtime_enable(&client->dev);
> >  
> > +	if (!IS_ENABLED(CONFIG_PM)) {
> > +		/* Wake the chip up on non-PM environments */
> > +		pca9685_set_sleep_mode(pca, false);
> 
> I admit I didn't grasp all the PM framework details, but I wonder if
> it's right to first call set_sleep_mode(true) and then in some cases to
> false again.

That was done for readability reasons, however, I admit that after we no
longer reset the period (deemed not necessary by me due to the planned
removal of the resets) it would probably be as readable to have:

if (IS_ENABLED(CONFIG_PM)) {
	pca9685_set_sleep_mode(pca, true);
	pm_runtime_set_suspended..
	pm_runtime_enable..
} else
	pca9685_set_sleep_mode(pca, false);

Thanks,
Clemens

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v6 2/7] pwm: pca9685: Support hardware readout
  2021-03-29 15:51   ` Uwe Kleine-König
@ 2021-03-29 16:33     ` Clemens Gruber
  0 siblings, 0 replies; 33+ messages in thread
From: Clemens Gruber @ 2021-03-29 16:33 UTC (permalink / raw)
  To: Uwe Kleine-König
  Cc: linux-pwm, Thierry Reding, Sven Van Asbroeck, linux-kernel, kernel

Hi Uwe,

On Mon, Mar 29, 2021 at 05:51:40PM +0200, Uwe Kleine-König wrote:
> Hello Clemens,
> 
> On Mon, Mar 29, 2021 at 02:57:02PM +0200, Clemens Gruber wrote:
> > The hardware readout may return slightly different values than those
> > that were set in apply due to the limited range of possible prescale and
> > counter register values.
> 
> This is fine and for most hardware that's not preventable. My
> requirement here is that 
> 
> 	.get_state(pwm, &state);
> 	.apply_state(pwm, &state);
> 
> doesn't yield any changes.

Yes, that would not change anything.

Thanks,
Clemens

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v6 2/7] pwm: pca9685: Support hardware readout
  2021-03-29 12:57 ` [PATCH v6 2/7] pwm: pca9685: Support hardware readout Clemens Gruber
  2021-03-29 15:51   ` Uwe Kleine-König
@ 2021-03-29 16:54   ` Uwe Kleine-König
  2021-03-29 17:11     ` Clemens Gruber
  1 sibling, 1 reply; 33+ messages in thread
From: Uwe Kleine-König @ 2021-03-29 16:54 UTC (permalink / raw)
  To: Clemens Gruber; +Cc: linux-pwm, Thierry Reding, Sven Van Asbroeck, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 3043 bytes --]

On Mon, Mar 29, 2021 at 02:57:02PM +0200, Clemens Gruber wrote:
> Implements .get_state to read-out the current hardware state.
> 
> The hardware readout may return slightly different values than those
> that were set in apply due to the limited range of possible prescale and
> counter register values.
> 
> Also note that although the datasheet mentions 200 Hz as default
> frequency when using the internal 25 MHz oscillator, the calculated
> period from the default prescaler register setting of 30 is 5079040ns.
> 
> Signed-off-by: Clemens Gruber <clemens.gruber@pqgruber.com>
> ---
>  drivers/pwm/pwm-pca9685.c | 41 +++++++++++++++++++++++++++++++++++++++
>  1 file changed, 41 insertions(+)
> 
> diff --git a/drivers/pwm/pwm-pca9685.c b/drivers/pwm/pwm-pca9685.c
> index 0ed1013737e3..fb026a25fb61 100644
> --- a/drivers/pwm/pwm-pca9685.c
> +++ b/drivers/pwm/pwm-pca9685.c
> @@ -333,6 +333,46 @@ static int pca9685_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
>  	return 0;
>  }
>  
> +static void pca9685_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
> +				  struct pwm_state *state)
> +{
> +	struct pca9685 *pca = to_pca(chip);
> +	unsigned long long duty;
> +	unsigned int val = 0;
> +
> +	/* Calculate (chip-wide) period from prescale value */
> +	regmap_read(pca->regmap, PCA9685_PRESCALE, &val);
> +	state->period = (PCA9685_COUNTER_RANGE * 1000 / PCA9685_OSC_CLOCK_MHZ) *
> +			(val + 1);

As we have PCA9685_OSC_CLOCK_MHZ = 25 this is an integer calculation
without loss of precision. It might be worth to point that out in a
comment. (Otherwise doing the division at the end might be more
sensible.)

> +	/* The (per-channel) polarity is fixed */
> +	state->polarity = PWM_POLARITY_NORMAL;
> +
> +	if (pwm->hwpwm >= PCA9685_MAXCHAN) {
> +		/*
> +		 * The "all LEDs" channel does not support HW readout
> +		 * Return 0 and disabled for backwards compatibility
> +		 */
> +		state->duty_cycle = 0;
> +		state->enabled = false;
> +		return;
> +	}
> +
> +	duty = pca9685_pwm_get_duty(pca, pwm->hwpwm);
> +
> +	state->enabled = !!duty;
> +	if (!state->enabled) {
> +		state->duty_cycle = 0;
> +		return;
> +	} else if (duty == PCA9685_COUNTER_RANGE) {
> +		state->duty_cycle = state->period;
> +		return;
> +	}
> +
> +	duty *= state->period;
> +	state->duty_cycle = duty / PCA9685_COUNTER_RANGE;

.apply uses ROUND_CLOSEST to calculate duty from state->duty_cycle,
still using / here (instead of ROUND_CLOSEST), but again as
PCA9685_OSC_CLOCK_MHZ is 25 this calculation doesn't suffer from
rounding errors. So if you feed the state returned here into .apply
again, there is (as I want it) no change.

The only annoyance is that if PCA9685_PRESCALE holds a value smaller
than 3, .apply() will fail. Not sure there is any saner way to handle
this.

Best regards
Uwe

-- 
Pengutronix e.K.                           | Uwe Kleine-König            |
Industrial Linux Solutions                 | https://www.pengutronix.de/ |

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v6 4/7] pwm: pca9685: Support staggered output ON times
  2021-03-29 12:57 ` [PATCH v6 4/7] pwm: pca9685: Support staggered output ON times Clemens Gruber
@ 2021-03-29 17:03   ` Uwe Kleine-König
  2021-03-29 17:16     ` Clemens Gruber
  0 siblings, 1 reply; 33+ messages in thread
From: Uwe Kleine-König @ 2021-03-29 17:03 UTC (permalink / raw)
  To: Clemens Gruber; +Cc: linux-pwm, Thierry Reding, Sven Van Asbroeck, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 802 bytes --]

On Mon, Mar 29, 2021 at 02:57:04PM +0200, Clemens Gruber wrote:
> The PCA9685 supports staggered LED output ON times to minimize current
> surges and reduce EMI.
> When this new option is enabled, the ON times of each channel are
> delayed by channel number x counter range / 16, which avoids asserting
> all enabled outputs at the same counter value while still maintaining
> the configured duty cycle of each output.
> 
> Signed-off-by: Clemens Gruber <clemens.gruber@pqgruber.com>

Is there a reason to not want this staggered output? If it never hurts I
suggest to always stagger and drop the dt property.

Best regards
Uwe

-- 
Pengutronix e.K.                           | Uwe Kleine-König            |
Industrial Linux Solutions                 | https://www.pengutronix.de/ |

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v6 2/7] pwm: pca9685: Support hardware readout
  2021-03-29 16:54   ` Uwe Kleine-König
@ 2021-03-29 17:11     ` Clemens Gruber
  2021-03-29 17:41       ` Uwe Kleine-König
  0 siblings, 1 reply; 33+ messages in thread
From: Clemens Gruber @ 2021-03-29 17:11 UTC (permalink / raw)
  To: Uwe Kleine-König
  Cc: linux-pwm, Thierry Reding, Sven Van Asbroeck, linux-kernel

On Mon, Mar 29, 2021 at 06:54:29PM +0200, Uwe Kleine-König wrote:
> On Mon, Mar 29, 2021 at 02:57:02PM +0200, Clemens Gruber wrote:
> > Implements .get_state to read-out the current hardware state.
> > 
> > The hardware readout may return slightly different values than those
> > that were set in apply due to the limited range of possible prescale and
> > counter register values.
> > 
> > Also note that although the datasheet mentions 200 Hz as default
> > frequency when using the internal 25 MHz oscillator, the calculated
> > period from the default prescaler register setting of 30 is 5079040ns.
> > 
> > Signed-off-by: Clemens Gruber <clemens.gruber@pqgruber.com>
> > ---
> >  drivers/pwm/pwm-pca9685.c | 41 +++++++++++++++++++++++++++++++++++++++
> >  1 file changed, 41 insertions(+)
> > 
> > diff --git a/drivers/pwm/pwm-pca9685.c b/drivers/pwm/pwm-pca9685.c
> > index 0ed1013737e3..fb026a25fb61 100644
> > --- a/drivers/pwm/pwm-pca9685.c
> > +++ b/drivers/pwm/pwm-pca9685.c
> > @@ -333,6 +333,46 @@ static int pca9685_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
> >  	return 0;
> >  }
> >  
> > +static void pca9685_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
> > +				  struct pwm_state *state)
> > +{
> > +	struct pca9685 *pca = to_pca(chip);
> > +	unsigned long long duty;
> > +	unsigned int val = 0;
> > +
> > +	/* Calculate (chip-wide) period from prescale value */
> > +	regmap_read(pca->regmap, PCA9685_PRESCALE, &val);
> > +	state->period = (PCA9685_COUNTER_RANGE * 1000 / PCA9685_OSC_CLOCK_MHZ) *
> > +			(val + 1);
> 
> As we have PCA9685_OSC_CLOCK_MHZ = 25 this is an integer calculation
> without loss of precision. It might be worth to point that out in a
> comment. (Otherwise doing the division at the end might be more
> sensible.)

What comment do you have in mind?
/* 1 integer multiplication (without loss of precision) at runtime */ ?

> 
> > +	/* The (per-channel) polarity is fixed */
> > +	state->polarity = PWM_POLARITY_NORMAL;
> > +
> > +	if (pwm->hwpwm >= PCA9685_MAXCHAN) {
> > +		/*
> > +		 * The "all LEDs" channel does not support HW readout
> > +		 * Return 0 and disabled for backwards compatibility
> > +		 */
> > +		state->duty_cycle = 0;
> > +		state->enabled = false;
> > +		return;
> > +	}
> > +
> > +	duty = pca9685_pwm_get_duty(pca, pwm->hwpwm);
> > +
> > +	state->enabled = !!duty;
> > +	if (!state->enabled) {
> > +		state->duty_cycle = 0;
> > +		return;
> > +	} else if (duty == PCA9685_COUNTER_RANGE) {
> > +		state->duty_cycle = state->period;
> > +		return;
> > +	}
> > +
> > +	duty *= state->period;
> > +	state->duty_cycle = duty / PCA9685_COUNTER_RANGE;
> 
> .apply uses ROUND_CLOSEST to calculate duty from state->duty_cycle,
> still using / here (instead of ROUND_CLOSEST), but again as
> PCA9685_OSC_CLOCK_MHZ is 25 this calculation doesn't suffer from
> rounding errors. So if you feed the state returned here into .apply
> again, there is (as I want it) no change.
> 
> The only annoyance is that if PCA9685_PRESCALE holds a value smaller
> than 3, .apply() will fail. Not sure there is any saner way to handle
> this.

According to the datasheet, "The hardware forces a minimum value that
can be loaded into the PRE_SCALE register at '3'", so there should never
be anything below 3 in that register.

Thanks for your review!

Clemens

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v6 6/7] pwm: pca9685: Restrict period change for prescaler users
  2021-03-29 12:57 ` [PATCH v6 6/7] pwm: pca9685: Restrict period change for prescaler users Clemens Gruber
@ 2021-03-29 17:15   ` Uwe Kleine-König
  2021-03-29 17:33     ` Clemens Gruber
  0 siblings, 1 reply; 33+ messages in thread
From: Uwe Kleine-König @ 2021-03-29 17:15 UTC (permalink / raw)
  To: Clemens Gruber
  Cc: linux-pwm, Thierry Reding, Sven Van Asbroeck, linux-kernel, kernel

[-- Attachment #1: Type: text/plain, Size: 941 bytes --]

On Mon, Mar 29, 2021 at 02:57:06PM +0200, Clemens Gruber wrote:
> @@ -330,14 +345,22 @@ static int pca9685_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
>  
>  	if (!state->enabled || duty < 1) {
>  		pca9685_pwm_set_duty(pca, pwm->hwpwm, 0);
> +		clear_bit(pwm->hwpwm, pca->prescaler_users);

Hmm, so if "my" channel runs at say

	.duty_cycle = 2539520 ns
	.period = 5079040 ns

and I call pwm_apply_state(mypwm, { .duty_cycle = 0, .period = 5079040,
enabled = true }); it might happen that another channel modifies the
period and I won't be able to return to the initial setting.

So I think it's sensible to only clear the user bit if the PWM is
disabled, but not if it is configured for duty_cycle = 0.

Does this make sense?

Best regards
Uwe

-- 
Pengutronix e.K.                           | Uwe Kleine-König            |
Industrial Linux Solutions                 | https://www.pengutronix.de/ |

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v6 4/7] pwm: pca9685: Support staggered output ON times
  2021-03-29 17:03   ` Uwe Kleine-König
@ 2021-03-29 17:16     ` Clemens Gruber
  2021-03-29 18:02       ` Uwe Kleine-König
  0 siblings, 1 reply; 33+ messages in thread
From: Clemens Gruber @ 2021-03-29 17:16 UTC (permalink / raw)
  To: Uwe Kleine-König
  Cc: linux-pwm, Thierry Reding, Sven Van Asbroeck, linux-kernel

On Mon, Mar 29, 2021 at 07:03:57PM +0200, Uwe Kleine-König wrote:
> On Mon, Mar 29, 2021 at 02:57:04PM +0200, Clemens Gruber wrote:
> > The PCA9685 supports staggered LED output ON times to minimize current
> > surges and reduce EMI.
> > When this new option is enabled, the ON times of each channel are
> > delayed by channel number x counter range / 16, which avoids asserting
> > all enabled outputs at the same counter value while still maintaining
> > the configured duty cycle of each output.
> > 
> > Signed-off-by: Clemens Gruber <clemens.gruber@pqgruber.com>
> 
> Is there a reason to not want this staggered output? If it never hurts I
> suggest to always stagger and drop the dt property.

There might be applications where you want multiple outputs to assert at
the same time / to be synchronized.
With staggered outputs mode always enabled, this would no longer be
possible as they are spread out according to their channel number.

Not sure how often that usecase is required, but just enforcing the
staggered mode by default sounds risky to me.

Thanks,
Clemens

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v6 6/7] pwm: pca9685: Restrict period change for prescaler users
  2021-03-29 17:15   ` Uwe Kleine-König
@ 2021-03-29 17:33     ` Clemens Gruber
  2021-03-29 17:49       ` Uwe Kleine-König
  0 siblings, 1 reply; 33+ messages in thread
From: Clemens Gruber @ 2021-03-29 17:33 UTC (permalink / raw)
  To: Uwe Kleine-König
  Cc: linux-pwm, Thierry Reding, Sven Van Asbroeck, linux-kernel, kernel

On Mon, Mar 29, 2021 at 07:15:59PM +0200, Uwe Kleine-König wrote:
> On Mon, Mar 29, 2021 at 02:57:06PM +0200, Clemens Gruber wrote:
> > @@ -330,14 +345,22 @@ static int pca9685_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
> >  
> >  	if (!state->enabled || duty < 1) {
> >  		pca9685_pwm_set_duty(pca, pwm->hwpwm, 0);
> > +		clear_bit(pwm->hwpwm, pca->prescaler_users);
> 
> Hmm, so if "my" channel runs at say
> 
> 	.duty_cycle = 2539520 ns
> 	.period = 5079040 ns
> 
> and I call pwm_apply_state(mypwm, { .duty_cycle = 0, .period = 5079040,
> enabled = true }); it might happen that another channel modifies the
> period and I won't be able to return to the initial setting.

Yes, that's correct.

But that also applies to PWMs set to 100%:

pwm_apply_state(mypwm, { .duty_cycle = 5079040, .period = 5079040,
enabled = true });

As this sets the full ON bit and does not use the prescaler, with the
current code, another channel could modify the period and you wouldn't
be able to return to the initial setting of 50%.

> 
> So I think it's sensible to only clear the user bit if the PWM is
> disabled, but not if it is configured for duty_cycle = 0.
> 
> Does this make sense?

With both cases in mind, you are suggesting we block modifications of
the prescaler if other PWMs are enabled and not if other PWMs are using
the prescaler?

Clemens

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v6 2/7] pwm: pca9685: Support hardware readout
  2021-03-29 17:11     ` Clemens Gruber
@ 2021-03-29 17:41       ` Uwe Kleine-König
  0 siblings, 0 replies; 33+ messages in thread
From: Uwe Kleine-König @ 2021-03-29 17:41 UTC (permalink / raw)
  To: Clemens Gruber; +Cc: linux-pwm, Thierry Reding, Sven Van Asbroeck, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 3136 bytes --]

On Mon, Mar 29, 2021 at 07:11:53PM +0200, Clemens Gruber wrote:
> On Mon, Mar 29, 2021 at 06:54:29PM +0200, Uwe Kleine-König wrote:
> > On Mon, Mar 29, 2021 at 02:57:02PM +0200, Clemens Gruber wrote:
> > > [...]
> > > +	/* Calculate (chip-wide) period from prescale value */
> > > +	regmap_read(pca->regmap, PCA9685_PRESCALE, &val);
> > > +	state->period = (PCA9685_COUNTER_RANGE * 1000 / PCA9685_OSC_CLOCK_MHZ) *
> > > +			(val + 1);
> > 
> > As we have PCA9685_OSC_CLOCK_MHZ = 25 this is an integer calculation
> > without loss of precision. It might be worth to point that out in a
> > comment. (Otherwise doing the division at the end might be more
> > sensible.)
> 
> What comment do you have in mind?
> /* 1 integer multiplication (without loss of precision) at runtime */ ?

Something like:

	/*
	 * PCA9685_OSC_CLOCK_MHZ is 25 and so an integer divider of
	 * 1000. So the calculation here is only a multiplication and
	 * we're not loosing precision.
	 */
 
> > > +	/* The (per-channel) polarity is fixed */
> > > +	state->polarity = PWM_POLARITY_NORMAL;
> > > +
> > > +	if (pwm->hwpwm >= PCA9685_MAXCHAN) {
> > > +		/*
> > > +		 * The "all LEDs" channel does not support HW readout
> > > +		 * Return 0 and disabled for backwards compatibility
> > > +		 */
> > > +		state->duty_cycle = 0;
> > > +		state->enabled = false;
> > > +		return;
> > > +	}
> > > +
> > > +	duty = pca9685_pwm_get_duty(pca, pwm->hwpwm);
> > > +
> > > +	state->enabled = !!duty;
> > > +	if (!state->enabled) {
> > > +		state->duty_cycle = 0;
> > > +		return;
> > > +	} else if (duty == PCA9685_COUNTER_RANGE) {
> > > +		state->duty_cycle = state->period;
> > > +		return;
> > > +	}
> > > +
> > > +	duty *= state->period;
> > > +	state->duty_cycle = duty / PCA9685_COUNTER_RANGE;
> > 
> > .apply uses ROUND_CLOSEST to calculate duty from state->duty_cycle,
> > still using / here (instead of ROUND_CLOSEST), but again as
> > PCA9685_OSC_CLOCK_MHZ is 25 this calculation doesn't suffer from
> > rounding errors. So if you feed the state returned here into .apply
> > again, there is (as I want it) no change.
> > 
> > The only annoyance is that if PCA9685_PRESCALE holds a value smaller
> > than 3, .apply() will fail. Not sure there is any saner way to handle
> > this.
> 
> According to the datasheet, "The hardware forces a minimum value that
> can be loaded into the PRE_SCALE register at '3'", so there should never
> be anything below 3 in that register.

Did you verify that the register reads back a 3 if you write a lower
value into the register?

Maybe the most defensive way would be:

+	regmap_read(pca->regmap, PCA9685_PRESCALE, &val);
+	/*
+	 * According to the datasheet, the hardware forces a minimum
+	 * value that can be loaded is 3, so if we read something lower
+	 * assume that the hardware actually implemented a 3.
+	 */
+	if (val < 3)
+		val = 3;
+	state->period = ...
	
Best regards
Uwe

-- 
Pengutronix e.K.                           | Uwe Kleine-König            |
Industrial Linux Solutions                 | https://www.pengutronix.de/ |

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v6 6/7] pwm: pca9685: Restrict period change for prescaler users
  2021-03-29 17:33     ` Clemens Gruber
@ 2021-03-29 17:49       ` Uwe Kleine-König
  0 siblings, 0 replies; 33+ messages in thread
From: Uwe Kleine-König @ 2021-03-29 17:49 UTC (permalink / raw)
  To: Clemens Gruber
  Cc: linux-pwm, Thierry Reding, linux-kernel, kernel, Sven Van Asbroeck

[-- Attachment #1: Type: text/plain, Size: 1748 bytes --]

Hello Clemens,

On Mon, Mar 29, 2021 at 07:33:56PM +0200, Clemens Gruber wrote:
> On Mon, Mar 29, 2021 at 07:15:59PM +0200, Uwe Kleine-König wrote:
> > On Mon, Mar 29, 2021 at 02:57:06PM +0200, Clemens Gruber wrote:
> > > @@ -330,14 +345,22 @@ static int pca9685_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
> > >  
> > >  	if (!state->enabled || duty < 1) {
> > >  		pca9685_pwm_set_duty(pca, pwm->hwpwm, 0);
> > > +		clear_bit(pwm->hwpwm, pca->prescaler_users);
> > 
> > Hmm, so if "my" channel runs at say
> > 
> > 	.duty_cycle = 2539520 ns
> > 	.period = 5079040 ns
> > 
> > and I call pwm_apply_state(mypwm, { .duty_cycle = 0, .period = 5079040,
> > enabled = true }); it might happen that another channel modifies the
> > period and I won't be able to return to the initial setting.
> 
> Yes, that's correct.
> 
> But that also applies to PWMs set to 100%:
> 
> pwm_apply_state(mypwm, { .duty_cycle = 5079040, .period = 5079040,
> enabled = true });
> 
> As this sets the full ON bit and does not use the prescaler, with the
> current code, another channel could modify the period and you wouldn't
> be able to return to the initial setting of 50%.
> 
> > So I think it's sensible to only clear the user bit if the PWM is
> > disabled, but not if it is configured for duty_cycle = 0.
> > 
> > Does this make sense?
> 
> With both cases in mind, you are suggesting we block modifications of
> the prescaler if other PWMs are enabled and not if other PWMs are using
> the prescaler?

That sounds sensible, yes.

Best regards
Uwe

-- 
Pengutronix e.K.                           | Uwe Kleine-König            |
Industrial Linux Solutions                 | https://www.pengutronix.de/ |

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v6 4/7] pwm: pca9685: Support staggered output ON times
  2021-03-29 17:16     ` Clemens Gruber
@ 2021-03-29 18:02       ` Uwe Kleine-König
  2021-03-31 12:26         ` Clemens Gruber
  0 siblings, 1 reply; 33+ messages in thread
From: Uwe Kleine-König @ 2021-03-29 18:02 UTC (permalink / raw)
  To: Clemens Gruber; +Cc: linux-pwm, Thierry Reding, Sven Van Asbroeck, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 2230 bytes --]

On Mon, Mar 29, 2021 at 07:16:38PM +0200, Clemens Gruber wrote:
> On Mon, Mar 29, 2021 at 07:03:57PM +0200, Uwe Kleine-König wrote:
> > On Mon, Mar 29, 2021 at 02:57:04PM +0200, Clemens Gruber wrote:
> > > The PCA9685 supports staggered LED output ON times to minimize current
> > > surges and reduce EMI.
> > > When this new option is enabled, the ON times of each channel are
> > > delayed by channel number x counter range / 16, which avoids asserting
> > > all enabled outputs at the same counter value while still maintaining
> > > the configured duty cycle of each output.
> > > 
> > > Signed-off-by: Clemens Gruber <clemens.gruber@pqgruber.com>
> > 
> > Is there a reason to not want this staggered output? If it never hurts I
> > suggest to always stagger and drop the dt property.
> 
> There might be applications where you want multiple outputs to assert at
> the same time / to be synchronized.
> With staggered outputs mode always enabled, this would no longer be
> possible as they are spread out according to their channel number.
> 
> Not sure how often that usecase is required, but just enforcing the
> staggered mode by default sounds risky to me.

There is no such guarantee in the PWM framework, so I don't think we
need to fear breaking setups. Thierry?

One reason we might not want staggering is if we have a consumer who
cares about config transitions. (This however is moot it the hardware
doesn't provide sane transitions even without staggering.)

Did I already ask about races in this driver? I assume there is a
free running counter and the ON and OFF registers just define where in
the period the transitions happen, right? Given that changing ON and OFF
needs two register writes probably all kind of strange things can
happen, right? (Example thought: for simplicity's sake I assume ON is
always 0. Then if you want to change from OFF = 0xaaa to OFF = 0xccc we
might see a period with 0xacc. Depending on how the hardware works we
might even see 4 edges in a single period then.)

Best regards
Uwe

-- 
Pengutronix e.K.                           | Uwe Kleine-König            |
Industrial Linux Solutions                 | https://www.pengutronix.de/ |

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v6 4/7] pwm: pca9685: Support staggered output ON times
  2021-03-29 18:02       ` Uwe Kleine-König
@ 2021-03-31 12:26         ` Clemens Gruber
  2021-03-31 13:55           ` Clemens Gruber
                             ` (3 more replies)
  0 siblings, 4 replies; 33+ messages in thread
From: Clemens Gruber @ 2021-03-31 12:26 UTC (permalink / raw)
  To: Uwe Kleine-König
  Cc: linux-pwm, Thierry Reding, Sven Van Asbroeck, linux-kernel

On Mon, Mar 29, 2021 at 08:02:06PM +0200, Uwe Kleine-König wrote:
> On Mon, Mar 29, 2021 at 07:16:38PM +0200, Clemens Gruber wrote:
> > On Mon, Mar 29, 2021 at 07:03:57PM +0200, Uwe Kleine-König wrote:
> > > On Mon, Mar 29, 2021 at 02:57:04PM +0200, Clemens Gruber wrote:
> > > > The PCA9685 supports staggered LED output ON times to minimize current
> > > > surges and reduce EMI.
> > > > When this new option is enabled, the ON times of each channel are
> > > > delayed by channel number x counter range / 16, which avoids asserting
> > > > all enabled outputs at the same counter value while still maintaining
> > > > the configured duty cycle of each output.
> > > > 
> > > > Signed-off-by: Clemens Gruber <clemens.gruber@pqgruber.com>
> > > 
> > > Is there a reason to not want this staggered output? If it never hurts I
> > > suggest to always stagger and drop the dt property.
> > 
> > There might be applications where you want multiple outputs to assert at
> > the same time / to be synchronized.
> > With staggered outputs mode always enabled, this would no longer be
> > possible as they are spread out according to their channel number.
> > 
> > Not sure how often that usecase is required, but just enforcing the
> > staggered mode by default sounds risky to me.
> 
> There is no such guarantee in the PWM framework, so I don't think we
> need to fear breaking setups. Thierry?

Still, someone might rely on it? But let's wait for Thierry's opinion.

> 
> One reason we might not want staggering is if we have a consumer who
> cares about config transitions. (This however is moot it the hardware
> doesn't provide sane transitions even without staggering.)
> 
> Did I already ask about races in this driver? I assume there is a
> free running counter and the ON and OFF registers just define where in
> the period the transitions happen, right? Given that changing ON and OFF
> needs two register writes probably all kind of strange things can
> happen, right? (Example thought: for simplicity's sake I assume ON is
> always 0. Then if you want to change from OFF = 0xaaa to OFF = 0xccc we
> might see a period with 0xacc. Depending on how the hardware works we
> might even see 4 edges in a single period then.)

Yes, there is a free running counter from 0 to 4095.
And it is probably true, that there can be short intermediate states
with our two register writes.

There is a separate mode "Update on ACK" (MODE2 register, bit 3 "OCH"),
which is 0 by default (Outputs change on STOP command) but could be set
to 1 (Outputs change on ACK):
"Update on ACK requires all 4 PWM channel registers to be loaded before
outputs will change on the last ACK."

The chip datasheet also states:
"Because the loading of the LEDn_ON and LEDn_OFF registers is via the
I2C-bus, and asynchronous to the internal oscillator, we want to ensure
that we do not see any visual artifacts of changing the ON and OFF
values. This is achieved by updating the changes at the end of the LOW
cycle."

We could look into this in a future patch series, however I would like
to keep the register updating as-is for this series (otherwise I would
have to do all the tests with the oscilloscope again and the transitions
were like this since the driver was first implemented).

Thanks,
Clemens

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v6 4/7] pwm: pca9685: Support staggered output ON times
  2021-03-31 12:26         ` Clemens Gruber
@ 2021-03-31 13:55           ` Clemens Gruber
  2021-04-01 20:59             ` Uwe Kleine-König
  2021-03-31 16:21           ` Thierry Reding
                             ` (2 subsequent siblings)
  3 siblings, 1 reply; 33+ messages in thread
From: Clemens Gruber @ 2021-03-31 13:55 UTC (permalink / raw)
  To: Uwe Kleine-König
  Cc: linux-pwm, Thierry Reding, Sven Van Asbroeck, linux-kernel

On Wed, Mar 31, 2021 at 02:26:14PM +0200, Clemens Gruber wrote:
> On Mon, Mar 29, 2021 at 08:02:06PM +0200, Uwe Kleine-König wrote:
> > On Mon, Mar 29, 2021 at 07:16:38PM +0200, Clemens Gruber wrote:
> > > On Mon, Mar 29, 2021 at 07:03:57PM +0200, Uwe Kleine-König wrote:
> > > > On Mon, Mar 29, 2021 at 02:57:04PM +0200, Clemens Gruber wrote:
> > > > > The PCA9685 supports staggered LED output ON times to minimize current
> > > > > surges and reduce EMI.
> > > > > When this new option is enabled, the ON times of each channel are
> > > > > delayed by channel number x counter range / 16, which avoids asserting
> > > > > all enabled outputs at the same counter value while still maintaining
> > > > > the configured duty cycle of each output.
> > > > > 
> > > > > Signed-off-by: Clemens Gruber <clemens.gruber@pqgruber.com>
> > > > 
> > > > Is there a reason to not want this staggered output? If it never hurts I
> > > > suggest to always stagger and drop the dt property.
> > > 
> > > There might be applications where you want multiple outputs to assert at
> > > the same time / to be synchronized.
> > > With staggered outputs mode always enabled, this would no longer be
> > > possible as they are spread out according to their channel number.
> > > 
> > > Not sure how often that usecase is required, but just enforcing the
> > > staggered mode by default sounds risky to me.
> > 
> > There is no such guarantee in the PWM framework, so I don't think we
> > need to fear breaking setups. Thierry?
> 
> Still, someone might rely on it? But let's wait for Thierry's opinion.
> 
> > 
> > One reason we might not want staggering is if we have a consumer who
> > cares about config transitions. (This however is moot it the hardware
> > doesn't provide sane transitions even without staggering.)
> > 
> > Did I already ask about races in this driver? I assume there is a
> > free running counter and the ON and OFF registers just define where in
> > the period the transitions happen, right? Given that changing ON and OFF
> > needs two register writes probably all kind of strange things can
> > happen, right? (Example thought: for simplicity's sake I assume ON is
> > always 0. Then if you want to change from OFF = 0xaaa to OFF = 0xccc we
> > might see a period with 0xacc. Depending on how the hardware works we
> > might even see 4 edges in a single period then.)
> 
> Yes, there is a free running counter from 0 to 4095.
> And it is probably true, that there can be short intermediate states
> with our two register writes.
> 
> There is a separate mode "Update on ACK" (MODE2 register, bit 3 "OCH"),
> which is 0 by default (Outputs change on STOP command) but could be set
> to 1 (Outputs change on ACK):
> "Update on ACK requires all 4 PWM channel registers to be loaded before
> outputs will change on the last ACK."

This would require the auto-increment feature to be enabled, then
multiple registers could be written before the STOP condition:
LEDn_ON_L, LEDn_ON_H, LEDn_OFF_L & LEDn_OFF_H
(With OCH=0 in MODE2)

But I think this should be done in a separate improvement patch/series
to reduce glitches.

> The chip datasheet also states:
> "Because the loading of the LEDn_ON and LEDn_OFF registers is via the
> I2C-bus, and asynchronous to the internal oscillator, we want to ensure
> that we do not see any visual artifacts of changing the ON and OFF
> values. This is achieved by updating the changes at the end of the LOW
> cycle."
> 
> We could look into this in a future patch series, however I would like
> to keep the register updating as-is for this series (otherwise I would
> have to do all the tests with the oscilloscope again and the transitions
> were like this since the driver was first implemented).
> 
> Thanks,
> Clemens

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v6 4/7] pwm: pca9685: Support staggered output ON times
  2021-03-31 12:26         ` Clemens Gruber
  2021-03-31 13:55           ` Clemens Gruber
@ 2021-03-31 16:21           ` Thierry Reding
  2021-04-01  7:50             ` Clemens Gruber
  2021-04-02 19:48             ` Uwe Kleine-König
  2021-04-01 20:58           ` Uwe Kleine-König
  2021-04-02 20:23           ` Uwe Kleine-König
  3 siblings, 2 replies; 33+ messages in thread
From: Thierry Reding @ 2021-03-31 16:21 UTC (permalink / raw)
  To: Clemens Gruber
  Cc: Uwe Kleine-König, linux-pwm, Sven Van Asbroeck, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 6848 bytes --]

On Wed, Mar 31, 2021 at 02:26:14PM +0200, Clemens Gruber wrote:
> On Mon, Mar 29, 2021 at 08:02:06PM +0200, Uwe Kleine-König wrote:
> > On Mon, Mar 29, 2021 at 07:16:38PM +0200, Clemens Gruber wrote:
> > > On Mon, Mar 29, 2021 at 07:03:57PM +0200, Uwe Kleine-König wrote:
> > > > On Mon, Mar 29, 2021 at 02:57:04PM +0200, Clemens Gruber wrote:
> > > > > The PCA9685 supports staggered LED output ON times to minimize current
> > > > > surges and reduce EMI.
> > > > > When this new option is enabled, the ON times of each channel are
> > > > > delayed by channel number x counter range / 16, which avoids asserting
> > > > > all enabled outputs at the same counter value while still maintaining
> > > > > the configured duty cycle of each output.
> > > > > 
> > > > > Signed-off-by: Clemens Gruber <clemens.gruber@pqgruber.com>
> > > > 
> > > > Is there a reason to not want this staggered output? If it never hurts I
> > > > suggest to always stagger and drop the dt property.
> > > 
> > > There might be applications where you want multiple outputs to assert at
> > > the same time / to be synchronized.
> > > With staggered outputs mode always enabled, this would no longer be
> > > possible as they are spread out according to their channel number.
> > > 
> > > Not sure how often that usecase is required, but just enforcing the
> > > staggered mode by default sounds risky to me.
> > 
> > There is no such guarantee in the PWM framework, so I don't think we
> > need to fear breaking setups. Thierry?
> 
> Still, someone might rely on it? But let's wait for Thierry's opinion.

There's currently no way to synchronize two PWM channels in the PWM
framework. And given that each PWM channel is handled separately the
programming for two channels will never happen atomically or even
concurrently, so I don't see how you could run two PWMs completely
synchronized to one another.

That said, it might be possible to implement something like this by
coupling two or more PWMs. However, I think we will only ever be able to
do this on a per-chip basis, because that's the only way we could
guarantee that multiple PWMs can be programmed at the same time, or that
they get enabled with the same write. Of course this all requires that
the chip even supports that. Even if you enable two PWM channels within
the same driver but with two consecutive register writes they will not
be guaranteed to run synchronously. There'd have to be some special chip
support to allow this to work.

However, I'm a bit hesitant about this staggering output mode. From what
I understand what's going to happen for these is basically that overall
each PWM will be running at the requested duty cycle, but the on/off
times will be evenly spread out over the whole period. In other words,
the output *power* of the PWM signal will be the same as if the signal
was a single on/off cycle. That's not technically a PWM signal as the
PWM framework defines it. See the kerneldoc for enum pwm_polarity for
what signals are expected to look like.

So I agree that this is not something that should be enabled by default
because if you've got a consumer that expects exactly one rising edge
and one falling edge per period, they will get confused if you toggle
multiple times during one period.

If that's the case,you probably want to configure this on a per-PWM
basis rather than for the entire chip because otherwise you could end up
in a scenario where one PWM does *not* work with staggered output and
the others do. I guess you could always make that decision up front, but
do you always know what people may end up using these PWMs for? What if
you have a board that breaks out one PWM on some general purpose pin
header and people end up using it via sysfs. They would need have to
recompile the DTB for the device if they wanted to enable or disable
this staggered mode.

Or did I misunderstand and it's only the start time of the rising edge
that's shifted, but the signal will remain high for a full duty cycle
after that and then go down and remain low for period - duty - offset?

That's slightly better than the above in that it likely won't trip up
any consumers. But it might still be worth to make this configurable per
PWM (perhaps by specifying a third specifier cell, in addition to the
period and flags, that defines the offset/phase of the signal).

In both cases, doing this on a per-PWM basis will allow the consumer to
specify that they're okay with staggered mode and you won't actually
force it onto anyone. This effectively makes this opt-in and there will
be no change for existing consumers.

> > One reason we might not want staggering is if we have a consumer who
> > cares about config transitions. (This however is moot it the hardware
> > doesn't provide sane transitions even without staggering.)
> > 
> > Did I already ask about races in this driver? I assume there is a
> > free running counter and the ON and OFF registers just define where in
> > the period the transitions happen, right? Given that changing ON and OFF
> > needs two register writes probably all kind of strange things can
> > happen, right? (Example thought: for simplicity's sake I assume ON is
> > always 0. Then if you want to change from OFF = 0xaaa to OFF = 0xccc we
> > might see a period with 0xacc. Depending on how the hardware works we
> > might even see 4 edges in a single period then.)
> 
> Yes, there is a free running counter from 0 to 4095.
> And it is probably true, that there can be short intermediate states
> with our two register writes.
> 
> There is a separate mode "Update on ACK" (MODE2 register, bit 3 "OCH"),
> which is 0 by default (Outputs change on STOP command) but could be set
> to 1 (Outputs change on ACK):
> "Update on ACK requires all 4 PWM channel registers to be loaded before
> outputs will change on the last ACK."

This sounds like it would allow atomic updates of the PWM settings.
That's probably something that you want to implement to avoid any
glitches.

> The chip datasheet also states:
> "Because the loading of the LEDn_ON and LEDn_OFF registers is via the
> I2C-bus, and asynchronous to the internal oscillator, we want to ensure
> that we do not see any visual artifacts of changing the ON and OFF
> values. This is achieved by updating the changes at the end of the LOW
> cycle."
> 
> We could look into this in a future patch series, however I would like
> to keep the register updating as-is for this series (otherwise I would
> have to do all the tests with the oscilloscope again and the transitions
> were like this since the driver was first implemented).

Yeah, it sounds fine to implement this at a later point in time. No need
to conflate it with the current series.

Thierry

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v6 4/7] pwm: pca9685: Support staggered output ON times
  2021-03-31 16:21           ` Thierry Reding
@ 2021-04-01  7:50             ` Clemens Gruber
  2021-04-01 13:47               ` Thierry Reding
  2021-04-02 19:48             ` Uwe Kleine-König
  1 sibling, 1 reply; 33+ messages in thread
From: Clemens Gruber @ 2021-04-01  7:50 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Uwe Kleine-König, linux-pwm, Sven Van Asbroeck, linux-kernel

Hi Thierry,

On Wed, Mar 31, 2021 at 06:21:32PM +0200, Thierry Reding wrote:
> On Wed, Mar 31, 2021 at 02:26:14PM +0200, Clemens Gruber wrote:
> > On Mon, Mar 29, 2021 at 08:02:06PM +0200, Uwe Kleine-König wrote:
> > > On Mon, Mar 29, 2021 at 07:16:38PM +0200, Clemens Gruber wrote:
> > > > On Mon, Mar 29, 2021 at 07:03:57PM +0200, Uwe Kleine-König wrote:
> > > > > On Mon, Mar 29, 2021 at 02:57:04PM +0200, Clemens Gruber wrote:
> > > > > > The PCA9685 supports staggered LED output ON times to minimize current
> > > > > > surges and reduce EMI.
> > > > > > When this new option is enabled, the ON times of each channel are
> > > > > > delayed by channel number x counter range / 16, which avoids asserting
> > > > > > all enabled outputs at the same counter value while still maintaining
> > > > > > the configured duty cycle of each output.
> > > > > > 
> > > > > > Signed-off-by: Clemens Gruber <clemens.gruber@pqgruber.com>
> > > > > 
> > > > > Is there a reason to not want this staggered output? If it never hurts I
> > > > > suggest to always stagger and drop the dt property.
> > > > 
> > > > There might be applications where you want multiple outputs to assert at
> > > > the same time / to be synchronized.
> > > > With staggered outputs mode always enabled, this would no longer be
> > > > possible as they are spread out according to their channel number.
> > > > 
> > > > Not sure how often that usecase is required, but just enforcing the
> > > > staggered mode by default sounds risky to me.
> > > 
> > > There is no such guarantee in the PWM framework, so I don't think we
> > > need to fear breaking setups. Thierry?
> > 
> > Still, someone might rely on it? But let's wait for Thierry's opinion.
> 
> There's currently no way to synchronize two PWM channels in the PWM
> framework. And given that each PWM channel is handled separately the
> programming for two channels will never happen atomically or even
> concurrently, so I don't see how you could run two PWMs completely
> synchronized to one another.

As the PCA9685 has only one prescaler and one counter per chip, by
default, all PWMs enabled will go high at the same time. If they also
have the same duty cycle configured, they also go low at the same time.

> Or did I misunderstand and it's only the start time of the rising edge
> that's shifted, but the signal will remain high for a full duty cycle
> after that and then go down and remain low for period - duty - offset?

Yes, that's how it works.

> 
> That's slightly better than the above in that it likely won't trip up
> any consumers. But it might still be worth to make this configurable per
> PWM (perhaps by specifying a third specifier cell, in addition to the
> period and flags, that defines the offset/phase of the signal).
> 
> In both cases, doing this on a per-PWM basis will allow the consumer to
> specify that they're okay with staggered mode and you won't actually
> force it onto anyone. This effectively makes this opt-in and there will
> be no change for existing consumers.

I agree that it should be opt-in, but I am not sure about doing it
per-pwm:
The reason why you'd want staggered mode is to reduce EMI or current
spikes and it is most effective if it is enabled for all PWMs.

If it is specified in the DT anyway and you have a consumer that does
not support staggered mode (probably rare but can happen), then I'd
suggest just disabling it globally by not specifying nxp,staggered-mode;

Also it would make the configuration more complicated: You have to do
the "staggering" yourself and assign offsets per channel.
It's certainly easier to just enable or disable it.

What do you think?

> > > One reason we might not want staggering is if we have a consumer who
> > > cares about config transitions. (This however is moot it the hardware
> > > doesn't provide sane transitions even without staggering.)
> > > 
> > > Did I already ask about races in this driver? I assume there is a
> > > free running counter and the ON and OFF registers just define where in
> > > the period the transitions happen, right? Given that changing ON and OFF
> > > needs two register writes probably all kind of strange things can
> > > happen, right? (Example thought: for simplicity's sake I assume ON is
> > > always 0. Then if you want to change from OFF = 0xaaa to OFF = 0xccc we
> > > might see a period with 0xacc. Depending on how the hardware works we
> > > might even see 4 edges in a single period then.)
> > 
> > Yes, there is a free running counter from 0 to 4095.
> > And it is probably true, that there can be short intermediate states
> > with our two register writes.
> > 
> > There is a separate mode "Update on ACK" (MODE2 register, bit 3 "OCH"),
> > which is 0 by default (Outputs change on STOP command) but could be set
> > to 1 (Outputs change on ACK):
> > "Update on ACK requires all 4 PWM channel registers to be loaded before
> > outputs will change on the last ACK."
> 
> This sounds like it would allow atomic updates of the PWM settings.
> That's probably something that you want to implement to avoid any
> glitches.
> 
> > The chip datasheet also states:
> > "Because the loading of the LEDn_ON and LEDn_OFF registers is via the
> > I2C-bus, and asynchronous to the internal oscillator, we want to ensure
> > that we do not see any visual artifacts of changing the ON and OFF
> > values. This is achieved by updating the changes at the end of the LOW
> > cycle."
> > 
> > We could look into this in a future patch series, however I would like
> > to keep the register updating as-is for this series (otherwise I would
> > have to do all the tests with the oscilloscope again and the transitions
> > were like this since the driver was first implemented).
> 
> Yeah, it sounds fine to implement this at a later point in time. No need
> to conflate it with the current series.

Sounds good.

Thanks,
Clemens

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v6 4/7] pwm: pca9685: Support staggered output ON times
  2021-04-01  7:50             ` Clemens Gruber
@ 2021-04-01 13:47               ` Thierry Reding
  2021-04-01 15:19                 ` Clemens Gruber
  0 siblings, 1 reply; 33+ messages in thread
From: Thierry Reding @ 2021-04-01 13:47 UTC (permalink / raw)
  To: Clemens Gruber
  Cc: Uwe Kleine-König, linux-pwm, Sven Van Asbroeck, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 5499 bytes --]

On Thu, Apr 01, 2021 at 09:50:44AM +0200, Clemens Gruber wrote:
> Hi Thierry,
> 
> On Wed, Mar 31, 2021 at 06:21:32PM +0200, Thierry Reding wrote:
> > On Wed, Mar 31, 2021 at 02:26:14PM +0200, Clemens Gruber wrote:
> > > On Mon, Mar 29, 2021 at 08:02:06PM +0200, Uwe Kleine-König wrote:
> > > > On Mon, Mar 29, 2021 at 07:16:38PM +0200, Clemens Gruber wrote:
> > > > > On Mon, Mar 29, 2021 at 07:03:57PM +0200, Uwe Kleine-König wrote:
> > > > > > On Mon, Mar 29, 2021 at 02:57:04PM +0200, Clemens Gruber wrote:
> > > > > > > The PCA9685 supports staggered LED output ON times to minimize current
> > > > > > > surges and reduce EMI.
> > > > > > > When this new option is enabled, the ON times of each channel are
> > > > > > > delayed by channel number x counter range / 16, which avoids asserting
> > > > > > > all enabled outputs at the same counter value while still maintaining
> > > > > > > the configured duty cycle of each output.
> > > > > > > 
> > > > > > > Signed-off-by: Clemens Gruber <clemens.gruber@pqgruber.com>
> > > > > > 
> > > > > > Is there a reason to not want this staggered output? If it never hurts I
> > > > > > suggest to always stagger and drop the dt property.
> > > > > 
> > > > > There might be applications where you want multiple outputs to assert at
> > > > > the same time / to be synchronized.
> > > > > With staggered outputs mode always enabled, this would no longer be
> > > > > possible as they are spread out according to their channel number.
> > > > > 
> > > > > Not sure how often that usecase is required, but just enforcing the
> > > > > staggered mode by default sounds risky to me.
> > > > 
> > > > There is no such guarantee in the PWM framework, so I don't think we
> > > > need to fear breaking setups. Thierry?
> > > 
> > > Still, someone might rely on it? But let's wait for Thierry's opinion.
> > 
> > There's currently no way to synchronize two PWM channels in the PWM
> > framework. And given that each PWM channel is handled separately the
> > programming for two channels will never happen atomically or even
> > concurrently, so I don't see how you could run two PWMs completely
> > synchronized to one another.
> 
> As the PCA9685 has only one prescaler and one counter per chip, by
> default, all PWMs enabled will go high at the same time. If they also
> have the same duty cycle configured, they also go low at the same time.

What happens if you enable one of them, it then goes high and then you
enable the next one? Is the second one going to get enabled on the next
period? Or will it start in the middle of the period?

To truly enable them atomically, you'd have to ensure they all get
enabled in basically the same write, right? Because otherwise you can
still end up with just a subset enabled and the rest getting enabled
only after the first period.

> > Or did I misunderstand and it's only the start time of the rising edge
> > that's shifted, but the signal will remain high for a full duty cycle
> > after that and then go down and remain low for period - duty - offset?
> 
> Yes, that's how it works.

That's less problematic because the signal will remain a standard PWM,
it's just shifted by some amount. Technically pwm_apply_state() must
only return when the signal has been enabled, so very strictly speaking
you'd have to wait for a given amount of time to ensure that's correct.
But again, I doubt that any use-case would require you to be that
deterministic.

> > That's slightly better than the above in that it likely won't trip up
> > any consumers. But it might still be worth to make this configurable per
> > PWM (perhaps by specifying a third specifier cell, in addition to the
> > period and flags, that defines the offset/phase of the signal).
> > 
> > In both cases, doing this on a per-PWM basis will allow the consumer to
> > specify that they're okay with staggered mode and you won't actually
> > force it onto anyone. This effectively makes this opt-in and there will
> > be no change for existing consumers.
> 
> I agree that it should be opt-in, but I am not sure about doing it
> per-pwm:
> The reason why you'd want staggered mode is to reduce EMI or current
> spikes and it is most effective if it is enabled for all PWMs.
> 
> If it is specified in the DT anyway and you have a consumer that does
> not support staggered mode (probably rare but can happen), then I'd
> suggest just disabling it globally by not specifying nxp,staggered-mode;
> 
> Also it would make the configuration more complicated: You have to do
> the "staggering" yourself and assign offsets per channel.
> It's certainly easier to just enable or disable it.
> 
> What do you think?

Yeah, if you use an offset in the PWM specifier, users would have to
manually specify the offset. An interesting "feature" of that would be
that they could configure a subset of PWM channels to run synchronized
(module the atomicity problems discussed above). Not sure if that's
something anyone would ever want to do.

Another option would be to add some new flag that specifies that a given
PWM channel may use this mode. In that case users wouldn't have to care
about specifying the exact offset and instead just use the flag and rely
on the driver to pick some offset. Within the driver you could then just
keep the same computation that offsets by channel index, or you could
have any other mechanism that you want.

Thierry

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v6 4/7] pwm: pca9685: Support staggered output ON times
  2021-04-01 13:47               ` Thierry Reding
@ 2021-04-01 15:19                 ` Clemens Gruber
  0 siblings, 0 replies; 33+ messages in thread
From: Clemens Gruber @ 2021-04-01 15:19 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Uwe Kleine-König, linux-pwm, Sven Van Asbroeck, linux-kernel

On Thu, Apr 01, 2021 at 03:47:26PM +0200, Thierry Reding wrote:
> On Thu, Apr 01, 2021 at 09:50:44AM +0200, Clemens Gruber wrote:
> > Hi Thierry,
> > 
> > On Wed, Mar 31, 2021 at 06:21:32PM +0200, Thierry Reding wrote:
> > > On Wed, Mar 31, 2021 at 02:26:14PM +0200, Clemens Gruber wrote:
> > > > On Mon, Mar 29, 2021 at 08:02:06PM +0200, Uwe Kleine-König wrote:
> > > > > On Mon, Mar 29, 2021 at 07:16:38PM +0200, Clemens Gruber wrote:
> > > > > > On Mon, Mar 29, 2021 at 07:03:57PM +0200, Uwe Kleine-König wrote:
> > > > > > > On Mon, Mar 29, 2021 at 02:57:04PM +0200, Clemens Gruber wrote:
> > > > > > > > The PCA9685 supports staggered LED output ON times to minimize current
> > > > > > > > surges and reduce EMI.
> > > > > > > > When this new option is enabled, the ON times of each channel are
> > > > > > > > delayed by channel number x counter range / 16, which avoids asserting
> > > > > > > > all enabled outputs at the same counter value while still maintaining
> > > > > > > > the configured duty cycle of each output.
> > > > > > > > 
> > > > > > > > Signed-off-by: Clemens Gruber <clemens.gruber@pqgruber.com>
> > > > > > > 
> > > > > > > Is there a reason to not want this staggered output? If it never hurts I
> > > > > > > suggest to always stagger and drop the dt property.
> > > > > > 
> > > > > > There might be applications where you want multiple outputs to assert at
> > > > > > the same time / to be synchronized.
> > > > > > With staggered outputs mode always enabled, this would no longer be
> > > > > > possible as they are spread out according to their channel number.
> > > > > > 
> > > > > > Not sure how often that usecase is required, but just enforcing the
> > > > > > staggered mode by default sounds risky to me.
> > > > > 
> > > > > There is no such guarantee in the PWM framework, so I don't think we
> > > > > need to fear breaking setups. Thierry?
> > > > 
> > > > Still, someone might rely on it? But let's wait for Thierry's opinion.
> > > 
> > > There's currently no way to synchronize two PWM channels in the PWM
> > > framework. And given that each PWM channel is handled separately the
> > > programming for two channels will never happen atomically or even
> > > concurrently, so I don't see how you could run two PWMs completely
> > > synchronized to one another.
> > 
> > As the PCA9685 has only one prescaler and one counter per chip, by
> > default, all PWMs enabled will go high at the same time. If they also
> > have the same duty cycle configured, they also go low at the same time.
> 
> What happens if you enable one of them, it then goes high and then you
> enable the next one? Is the second one going to get enabled on the next
> period? Or will it start in the middle of the period?

The channel configuration is updated at the end of the low cycle of the
channel in question and if the counter is already past the ON time, it
will start with the next period. So the second one should never start
while the first one is high (unless staggering mode is enabled).

> To truly enable them atomically, you'd have to ensure they all get
> enabled in basically the same write, right? Because otherwise you can
> still end up with just a subset enabled and the rest getting enabled
> only after the first period.

Yes. This could probably be achieved with auto-increment for consecutive
channels or with the special ALL channel for all channels.

In our usecases this is not required. I'd still like to send a follow up
patch in the future that at least implements the register writes per
channel with auto increment to not have intermediate states (due to high
and low byte being written separately)

> > > Or did I misunderstand and it's only the start time of the rising edge
> > > that's shifted, but the signal will remain high for a full duty cycle
> > > after that and then go down and remain low for period - duty - offset?
> > 
> > Yes, that's how it works.
> 
> That's less problematic because the signal will remain a standard PWM,
> it's just shifted by some amount. Technically pwm_apply_state() must
> only return when the signal has been enabled, so very strictly speaking
> you'd have to wait for a given amount of time to ensure that's correct.
> But again, I doubt that any use-case would require you to be that
> deterministic.

Yes, probably not and if we make it opt-in, it shouldn't be a problem.

> 
> > > That's slightly better than the above in that it likely won't trip up
> > > any consumers. But it might still be worth to make this configurable per
> > > PWM (perhaps by specifying a third specifier cell, in addition to the
> > > period and flags, that defines the offset/phase of the signal).
> > > 
> > > In both cases, doing this on a per-PWM basis will allow the consumer to
> > > specify that they're okay with staggered mode and you won't actually
> > > force it onto anyone. This effectively makes this opt-in and there will
> > > be no change for existing consumers.
> > 
> > I agree that it should be opt-in, but I am not sure about doing it
> > per-pwm:
> > The reason why you'd want staggered mode is to reduce EMI or current
> > spikes and it is most effective if it is enabled for all PWMs.
> > 
> > If it is specified in the DT anyway and you have a consumer that does
> > not support staggered mode (probably rare but can happen), then I'd
> > suggest just disabling it globally by not specifying nxp,staggered-mode;
> > 
> > Also it would make the configuration more complicated: You have to do
> > the "staggering" yourself and assign offsets per channel.
> > It's certainly easier to just enable or disable it.
> > 
> > What do you think?
> 
> Yeah, if you use an offset in the PWM specifier, users would have to
> manually specify the offset. An interesting "feature" of that would be
> that they could configure a subset of PWM channels to run synchronized
> (module the atomicity problems discussed above). Not sure if that's
> something anyone would ever want to do.

Yes and for the common case where you don't care it would be more work
for the user.

> 
> Another option would be to add some new flag that specifies that a given
> PWM channel may use this mode. In that case users wouldn't have to care
> about specifying the exact offset and instead just use the flag and rely
> on the driver to pick some offset. Within the driver you could then just
> keep the same computation that offsets by channel index, or you could
> have any other mechanism that you want.

OK, so maybe a PWM_STAGGERING_ALLOWED flag in dt-bindings/pwm/pwm.h,
pass it through via a new bool staggering_allowed in struct pwm_args?

Thanks,
Clemens

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v6 4/7] pwm: pca9685: Support staggered output ON times
  2021-03-31 12:26         ` Clemens Gruber
  2021-03-31 13:55           ` Clemens Gruber
  2021-03-31 16:21           ` Thierry Reding
@ 2021-04-01 20:58           ` Uwe Kleine-König
  2021-04-01 21:37             ` Clemens Gruber
  2021-04-02 20:23           ` Uwe Kleine-König
  3 siblings, 1 reply; 33+ messages in thread
From: Uwe Kleine-König @ 2021-04-01 20:58 UTC (permalink / raw)
  To: Clemens Gruber; +Cc: linux-pwm, Thierry Reding, Sven Van Asbroeck, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 3872 bytes --]

Hello Clemens,

On Wed, Mar 31, 2021 at 02:26:14PM +0200, Clemens Gruber wrote:
> On Mon, Mar 29, 2021 at 08:02:06PM +0200, Uwe Kleine-König wrote:
> > On Mon, Mar 29, 2021 at 07:16:38PM +0200, Clemens Gruber wrote:
> > > On Mon, Mar 29, 2021 at 07:03:57PM +0200, Uwe Kleine-König wrote:
> > > > On Mon, Mar 29, 2021 at 02:57:04PM +0200, Clemens Gruber wrote:
> > > > > The PCA9685 supports staggered LED output ON times to minimize current
> > > > > surges and reduce EMI.
> > > > > When this new option is enabled, the ON times of each channel are
> > > > > delayed by channel number x counter range / 16, which avoids asserting
> > > > > all enabled outputs at the same counter value while still maintaining
> > > > > the configured duty cycle of each output.
> > > > > 
> > > > > Signed-off-by: Clemens Gruber <clemens.gruber@pqgruber.com>
> > > > 
> > > > Is there a reason to not want this staggered output? If it never hurts I
> > > > suggest to always stagger and drop the dt property.
> > > 
> > > There might be applications where you want multiple outputs to assert at
> > > the same time / to be synchronized.
> > > With staggered outputs mode always enabled, this would no longer be
> > > possible as they are spread out according to their channel number.
> > > 
> > > Not sure how often that usecase is required, but just enforcing the
> > > staggered mode by default sounds risky to me.
> > 
> > There is no such guarantee in the PWM framework, so I don't think we
> > need to fear breaking setups. Thierry?
> 
> Still, someone might rely on it? But let's wait for Thierry's opinion.

Someone might rely on the pca9685 driver being as racy as a driver with
legacy bindings usually is. Should this be the reason to drop this whole
series?

> > One reason we might not want staggering is if we have a consumer who
> > cares about config transitions. (This however is moot it the hardware
> > doesn't provide sane transitions even without staggering.)
> > 
> > Did I already ask about races in this driver? I assume there is a
> > free running counter and the ON and OFF registers just define where in
> > the period the transitions happen, right? Given that changing ON and OFF
> > needs two register writes probably all kind of strange things can
> > happen, right? (Example thought: for simplicity's sake I assume ON is
> > always 0. Then if you want to change from OFF = 0xaaa to OFF = 0xccc we
> > might see a period with 0xacc. Depending on how the hardware works we
> > might even see 4 edges in a single period then.)
> 
> Yes, there is a free running counter from 0 to 4095.
> And it is probably true, that there can be short intermediate states
> with our two register writes.
> 
> There is a separate mode "Update on ACK" (MODE2 register, bit 3 "OCH"),
> which is 0 by default (Outputs change on STOP command) but could be set
> to 1 (Outputs change on ACK):
> "Update on ACK requires all 4 PWM channel registers to be loaded before
> outputs will change on the last ACK."

This is about the ACK and STOP in the i2c communication, right? I fail
to understand the relevance of this difference. I guess I have to read
the manual myself.
 
> The chip datasheet also states:
> "Because the loading of the LEDn_ON and LEDn_OFF registers is via the
> I2C-bus, and asynchronous to the internal oscillator, we want to ensure
> that we do not see any visual artifacts of changing the ON and OFF
> values. This is achieved by updating the changes at the end of the LOW
> cycle."

So we're only out of luck if the first register write happens before and
the second after the end of the LOW cycle, aren't we?

Best regards
Uwe

-- 
Pengutronix e.K.                           | Uwe Kleine-König            |
Industrial Linux Solutions                 | https://www.pengutronix.de/ |

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v6 4/7] pwm: pca9685: Support staggered output ON times
  2021-03-31 13:55           ` Clemens Gruber
@ 2021-04-01 20:59             ` Uwe Kleine-König
  2021-04-01 21:53               ` Clemens Gruber
  0 siblings, 1 reply; 33+ messages in thread
From: Uwe Kleine-König @ 2021-04-01 20:59 UTC (permalink / raw)
  To: Clemens Gruber; +Cc: linux-pwm, Thierry Reding, Sven Van Asbroeck, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 3495 bytes --]

On Wed, Mar 31, 2021 at 03:55:49PM +0200, Clemens Gruber wrote:
> On Wed, Mar 31, 2021 at 02:26:14PM +0200, Clemens Gruber wrote:
> > On Mon, Mar 29, 2021 at 08:02:06PM +0200, Uwe Kleine-König wrote:
> > > On Mon, Mar 29, 2021 at 07:16:38PM +0200, Clemens Gruber wrote:
> > > > On Mon, Mar 29, 2021 at 07:03:57PM +0200, Uwe Kleine-König wrote:
> > > > > On Mon, Mar 29, 2021 at 02:57:04PM +0200, Clemens Gruber wrote:
> > > > > > The PCA9685 supports staggered LED output ON times to minimize current
> > > > > > surges and reduce EMI.
> > > > > > When this new option is enabled, the ON times of each channel are
> > > > > > delayed by channel number x counter range / 16, which avoids asserting
> > > > > > all enabled outputs at the same counter value while still maintaining
> > > > > > the configured duty cycle of each output.
> > > > > > 
> > > > > > Signed-off-by: Clemens Gruber <clemens.gruber@pqgruber.com>
> > > > > 
> > > > > Is there a reason to not want this staggered output? If it never hurts I
> > > > > suggest to always stagger and drop the dt property.
> > > > 
> > > > There might be applications where you want multiple outputs to assert at
> > > > the same time / to be synchronized.
> > > > With staggered outputs mode always enabled, this would no longer be
> > > > possible as they are spread out according to their channel number.
> > > > 
> > > > Not sure how often that usecase is required, but just enforcing the
> > > > staggered mode by default sounds risky to me.
> > > 
> > > There is no such guarantee in the PWM framework, so I don't think we
> > > need to fear breaking setups. Thierry?
> > 
> > Still, someone might rely on it? But let's wait for Thierry's opinion.
> > 
> > > 
> > > One reason we might not want staggering is if we have a consumer who
> > > cares about config transitions. (This however is moot it the hardware
> > > doesn't provide sane transitions even without staggering.)
> > > 
> > > Did I already ask about races in this driver? I assume there is a
> > > free running counter and the ON and OFF registers just define where in
> > > the period the transitions happen, right? Given that changing ON and OFF
> > > needs two register writes probably all kind of strange things can
> > > happen, right? (Example thought: for simplicity's sake I assume ON is
> > > always 0. Then if you want to change from OFF = 0xaaa to OFF = 0xccc we
> > > might see a period with 0xacc. Depending on how the hardware works we
> > > might even see 4 edges in a single period then.)
> > 
> > Yes, there is a free running counter from 0 to 4095.
> > And it is probably true, that there can be short intermediate states
> > with our two register writes.
> > 
> > There is a separate mode "Update on ACK" (MODE2 register, bit 3 "OCH"),
> > which is 0 by default (Outputs change on STOP command) but could be set
> > to 1 (Outputs change on ACK):
> > "Update on ACK requires all 4 PWM channel registers to be loaded before
> > outputs will change on the last ACK."
> 
> This would require the auto-increment feature to be enabled, then
> multiple registers could be written before the STOP condition:
> LEDn_ON_L, LEDn_ON_H, LEDn_OFF_L & LEDn_OFF_H
> (With OCH=0 in MODE2)

Maybe a continued START would work, too?!

Best regards
Uwe

-- 
Pengutronix e.K.                           | Uwe Kleine-König            |
Industrial Linux Solutions                 | https://www.pengutronix.de/ |

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v6 4/7] pwm: pca9685: Support staggered output ON times
  2021-04-01 20:58           ` Uwe Kleine-König
@ 2021-04-01 21:37             ` Clemens Gruber
  0 siblings, 0 replies; 33+ messages in thread
From: Clemens Gruber @ 2021-04-01 21:37 UTC (permalink / raw)
  To: Uwe Kleine-König
  Cc: linux-pwm, Thierry Reding, Sven Van Asbroeck, linux-kernel

Hello Uwe,

On Thu, Apr 01, 2021 at 10:58:19PM +0200, Uwe Kleine-König wrote:
> Hello Clemens,
> 
> On Wed, Mar 31, 2021 at 02:26:14PM +0200, Clemens Gruber wrote:
> > On Mon, Mar 29, 2021 at 08:02:06PM +0200, Uwe Kleine-König wrote:
> > > On Mon, Mar 29, 2021 at 07:16:38PM +0200, Clemens Gruber wrote:
> > > > On Mon, Mar 29, 2021 at 07:03:57PM +0200, Uwe Kleine-König wrote:
> > > > > On Mon, Mar 29, 2021 at 02:57:04PM +0200, Clemens Gruber wrote:
> > > > > > The PCA9685 supports staggered LED output ON times to minimize current
> > > > > > surges and reduce EMI.
> > > > > > When this new option is enabled, the ON times of each channel are
> > > > > > delayed by channel number x counter range / 16, which avoids asserting
> > > > > > all enabled outputs at the same counter value while still maintaining
> > > > > > the configured duty cycle of each output.
> > > > > > 
> > > > > > Signed-off-by: Clemens Gruber <clemens.gruber@pqgruber.com>
> > > > > 
> > > > > Is there a reason to not want this staggered output? If it never hurts I
> > > > > suggest to always stagger and drop the dt property.
> > > > 
> > > > There might be applications where you want multiple outputs to assert at
> > > > the same time / to be synchronized.
> > > > With staggered outputs mode always enabled, this would no longer be
> > > > possible as they are spread out according to their channel number.
> > > > 
> > > > Not sure how often that usecase is required, but just enforcing the
> > > > staggered mode by default sounds risky to me.
> > > 
> > > There is no such guarantee in the PWM framework, so I don't think we
> > > need to fear breaking setups. Thierry?
> > 
> > Still, someone might rely on it? But let's wait for Thierry's opinion.
> 
> Someone might rely on the pca9685 driver being as racy as a driver with
> legacy bindings usually is. Should this be the reason to drop this whole
> series?

That's not really a fair comparison. I just don't want the whole
staggering mode patch to get reverted someday because it broke someone's
setup. If it is opt-in, something like that can't happen.

If you say that we don't have to care about usecases that are not
officialy guaranteed by the framework, then I'm fine with enabling it by
default.

But in the meantime, Thierry also argued against enabling it by default.

> 
> > > One reason we might not want staggering is if we have a consumer who
> > > cares about config transitions. (This however is moot it the hardware
> > > doesn't provide sane transitions even without staggering.)
> > > 
> > > Did I already ask about races in this driver? I assume there is a
> > > free running counter and the ON and OFF registers just define where in
> > > the period the transitions happen, right? Given that changing ON and OFF
> > > needs two register writes probably all kind of strange things can
> > > happen, right? (Example thought: for simplicity's sake I assume ON is
> > > always 0. Then if you want to change from OFF = 0xaaa to OFF = 0xccc we
> > > might see a period with 0xacc. Depending on how the hardware works we
> > > might even see 4 edges in a single period then.)
> > 
> > Yes, there is a free running counter from 0 to 4095.
> > And it is probably true, that there can be short intermediate states
> > with our two register writes.
> > 
> > There is a separate mode "Update on ACK" (MODE2 register, bit 3 "OCH"),
> > which is 0 by default (Outputs change on STOP command) but could be set
> > to 1 (Outputs change on ACK):
> > "Update on ACK requires all 4 PWM channel registers to be loaded before
> > outputs will change on the last ACK."
> 
> This is about the ACK and STOP in the i2c communication, right? I fail
> to understand the relevance of this difference. I guess I have to read
> the manual myself.

Yes. Not sure why they added the other mode myself, as you should be
able to send multiple bytes before the STOP with auto-increment as well.
But I did not try this out yet as this won't go into this series anyway.
I will look into it in more detail and do some experiments as soon as
this series is merged.

>  
> > The chip datasheet also states:
> > "Because the loading of the LEDn_ON and LEDn_OFF registers is via the
> > I2C-bus, and asynchronous to the internal oscillator, we want to ensure
> > that we do not see any visual artifacts of changing the ON and OFF
> > values. This is achieved by updating the changes at the end of the LOW
> > cycle."
> 
> So we're only out of luck if the first register write happens before and
> the second after the end of the LOW cycle, aren't we?

Yes. And this will be fixed with the auto-increment feature when we can
write all registers in one transaction.

Thanks,
Clemens

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v6 4/7] pwm: pca9685: Support staggered output ON times
  2021-04-01 20:59             ` Uwe Kleine-König
@ 2021-04-01 21:53               ` Clemens Gruber
  0 siblings, 0 replies; 33+ messages in thread
From: Clemens Gruber @ 2021-04-01 21:53 UTC (permalink / raw)
  To: Uwe Kleine-König
  Cc: linux-pwm, Thierry Reding, Sven Van Asbroeck, linux-kernel

On Thu, Apr 01, 2021 at 10:59:36PM +0200, Uwe Kleine-König wrote:
> On Wed, Mar 31, 2021 at 03:55:49PM +0200, Clemens Gruber wrote:
> > On Wed, Mar 31, 2021 at 02:26:14PM +0200, Clemens Gruber wrote:
> > > On Mon, Mar 29, 2021 at 08:02:06PM +0200, Uwe Kleine-König wrote:
> > > > On Mon, Mar 29, 2021 at 07:16:38PM +0200, Clemens Gruber wrote:
> > > > > On Mon, Mar 29, 2021 at 07:03:57PM +0200, Uwe Kleine-König wrote:
> > > > > > On Mon, Mar 29, 2021 at 02:57:04PM +0200, Clemens Gruber wrote:
> > > > > > > The PCA9685 supports staggered LED output ON times to minimize current
> > > > > > > surges and reduce EMI.
> > > > > > > When this new option is enabled, the ON times of each channel are
> > > > > > > delayed by channel number x counter range / 16, which avoids asserting
> > > > > > > all enabled outputs at the same counter value while still maintaining
> > > > > > > the configured duty cycle of each output.
> > > > > > > 
> > > > > > > Signed-off-by: Clemens Gruber <clemens.gruber@pqgruber.com>
> > > > > > 
> > > > > > Is there a reason to not want this staggered output? If it never hurts I
> > > > > > suggest to always stagger and drop the dt property.
> > > > > 
> > > > > There might be applications where you want multiple outputs to assert at
> > > > > the same time / to be synchronized.
> > > > > With staggered outputs mode always enabled, this would no longer be
> > > > > possible as they are spread out according to their channel number.
> > > > > 
> > > > > Not sure how often that usecase is required, but just enforcing the
> > > > > staggered mode by default sounds risky to me.
> > > > 
> > > > There is no such guarantee in the PWM framework, so I don't think we
> > > > need to fear breaking setups. Thierry?
> > > 
> > > Still, someone might rely on it? But let's wait for Thierry's opinion.
> > > 
> > > > 
> > > > One reason we might not want staggering is if we have a consumer who
> > > > cares about config transitions. (This however is moot it the hardware
> > > > doesn't provide sane transitions even without staggering.)
> > > > 
> > > > Did I already ask about races in this driver? I assume there is a
> > > > free running counter and the ON and OFF registers just define where in
> > > > the period the transitions happen, right? Given that changing ON and OFF
> > > > needs two register writes probably all kind of strange things can
> > > > happen, right? (Example thought: for simplicity's sake I assume ON is
> > > > always 0. Then if you want to change from OFF = 0xaaa to OFF = 0xccc we
> > > > might see a period with 0xacc. Depending on how the hardware works we
> > > > might even see 4 edges in a single period then.)
> > > 
> > > Yes, there is a free running counter from 0 to 4095.
> > > And it is probably true, that there can be short intermediate states
> > > with our two register writes.
> > > 
> > > There is a separate mode "Update on ACK" (MODE2 register, bit 3 "OCH"),
> > > which is 0 by default (Outputs change on STOP command) but could be set
> > > to 1 (Outputs change on ACK):
> > > "Update on ACK requires all 4 PWM channel registers to be loaded before
> > > outputs will change on the last ACK."
> > 
> > This would require the auto-increment feature to be enabled, then
> > multiple registers could be written before the STOP condition:
> > LEDn_ON_L, LEDn_ON_H, LEDn_OFF_L & LEDn_OFF_H
> > (With OCH=0 in MODE2)
> 
> Maybe a continued START would work, too?!

Yes, maybe. But according to the datasheet bus transaction examples,
it's enough to have one START condition and write multiple (continuous)
registers using the auto-increment feature. And repeated START does not
seem to be supported via regmap..?

Clemens

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v6 4/7] pwm: pca9685: Support staggered output ON times
  2021-03-31 16:21           ` Thierry Reding
  2021-04-01  7:50             ` Clemens Gruber
@ 2021-04-02 19:48             ` Uwe Kleine-König
  1 sibling, 0 replies; 33+ messages in thread
From: Uwe Kleine-König @ 2021-04-02 19:48 UTC (permalink / raw)
  To: Thierry Reding, Clemens Gruber; +Cc: linux-pwm, Sven Van Asbroeck, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 1993 bytes --]

Hello,

On Wed, Mar 31, 2021 at 06:21:32PM +0200, Thierry Reding wrote:
> However, I'm a bit hesitant about this staggering output mode. From what
> I understand what's going to happen for these is basically that overall
> each PWM will be running at the requested duty cycle, but the on/off
> times will be evenly spread out over the whole period. In other words,
> the output *power* of the PWM signal will be the same as if the signal
> was a single on/off cycle. That's not technically a PWM signal as the
> PWM framework defines it. See the kerneldoc for enum pwm_polarity for
> what signals are expected to look like.

After reading this thread I had the impression that there is no
(externally visible) difference between using ON = 0 plus programming a
new setting when the counter is say 70 and using ON = 30 plus
programming a new setting when the counter is 100. But that's not the
case and I agree that defaulting to staggering is a bad idea.

Having said that I doubt that adding a property to the device tree is a
good solution, because it changes behaviour without the consumer being
aware and additionally it's not really a hardware description.

The solution I'd prefer is to change struct pwm_state to include the
delay in it. (This would then make the polarity obsolete, because

	.duty_cycle = 30
	.period = 100
	.polarity = POLARITY_INVERTED
	.offset = 0

is equivalent to

	.duty_cycle = 30
	.period = 100
	.polarity = POLARITY_NORMAL
	.offset = 70

. Other inverted states can be modified similarily.) Then consumers can
be coordinated to use different offsets.

I'm aware changing this isn't trivial, and it's not thought out
completely, but I think the end result is rechnically superior to the
approach suggested in the patch under discussion.

Best regards
Uwe

-- 
Pengutronix e.K.                           | Uwe Kleine-König            |
Industrial Linux Solutions                 | https://www.pengutronix.de/ |

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v6 5/7] dt-bindings: pwm: pca9685: Add nxp,staggered-outputs property
  2021-03-29 12:57 ` [PATCH v6 5/7] dt-bindings: pwm: pca9685: Add nxp,staggered-outputs property Clemens Gruber
@ 2021-04-02 19:52   ` Uwe Kleine-König
  0 siblings, 0 replies; 33+ messages in thread
From: Uwe Kleine-König @ 2021-04-02 19:52 UTC (permalink / raw)
  To: Clemens Gruber; +Cc: linux-pwm, Thierry Reding, Sven Van Asbroeck, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 637 bytes --]

Hello Clemens,

On Mon, Mar 29, 2021 at 02:57:05PM +0200, Clemens Gruber wrote:
> The pca9685 driver supports a new nxp,staggered-outputs property for
> reduced current surges and EMI. This adds documentation for the new DT
> property.
> 
> Signed-off-by: Clemens Gruber <clemens.gruber@pqgruber.com>

This patch would need an ack by the dt guys, so you should Cc: them in
the next round if this (or a similar) patch is still part of the series.

Best regards
Uwe

-- 
Pengutronix e.K.                           | Uwe Kleine-König            |
Industrial Linux Solutions                 | https://www.pengutronix.de/ |

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v6 4/7] pwm: pca9685: Support staggered output ON times
  2021-03-31 12:26         ` Clemens Gruber
                             ` (2 preceding siblings ...)
  2021-04-01 20:58           ` Uwe Kleine-König
@ 2021-04-02 20:23           ` Uwe Kleine-König
  3 siblings, 0 replies; 33+ messages in thread
From: Uwe Kleine-König @ 2021-04-02 20:23 UTC (permalink / raw)
  To: Clemens Gruber; +Cc: linux-pwm, Thierry Reding, Sven Van Asbroeck, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 3540 bytes --]

On Wed, Mar 31, 2021 at 02:26:14PM +0200, Clemens Gruber wrote:
> On Mon, Mar 29, 2021 at 08:02:06PM +0200, Uwe Kleine-König wrote:
> > On Mon, Mar 29, 2021 at 07:16:38PM +0200, Clemens Gruber wrote:
> > > On Mon, Mar 29, 2021 at 07:03:57PM +0200, Uwe Kleine-König wrote:
> > > > On Mon, Mar 29, 2021 at 02:57:04PM +0200, Clemens Gruber wrote:
> > > > > The PCA9685 supports staggered LED output ON times to minimize current
> > > > > surges and reduce EMI.
> > > > > When this new option is enabled, the ON times of each channel are
> > > > > delayed by channel number x counter range / 16, which avoids asserting
> > > > > all enabled outputs at the same counter value while still maintaining
> > > > > the configured duty cycle of each output.
> > > > > 
> > > > > Signed-off-by: Clemens Gruber <clemens.gruber@pqgruber.com>
> > > > 
> > > > Is there a reason to not want this staggered output? If it never hurts I
> > > > suggest to always stagger and drop the dt property.
> > > 
> > > There might be applications where you want multiple outputs to assert at
> > > the same time / to be synchronized.
> > > With staggered outputs mode always enabled, this would no longer be
> > > possible as they are spread out according to their channel number.
> > > 
> > > Not sure how often that usecase is required, but just enforcing the
> > > staggered mode by default sounds risky to me.
> > 
> > There is no such guarantee in the PWM framework, so I don't think we
> > need to fear breaking setups. Thierry?
> 
> Still, someone might rely on it? But let's wait for Thierry's opinion.
> 
> > 
> > One reason we might not want staggering is if we have a consumer who
> > cares about config transitions. (This however is moot it the hardware
> > doesn't provide sane transitions even without staggering.)
> > 
> > Did I already ask about races in this driver? I assume there is a
> > free running counter and the ON and OFF registers just define where in
> > the period the transitions happen, right? Given that changing ON and OFF
> > needs two register writes probably all kind of strange things can
> > happen, right? (Example thought: for simplicity's sake I assume ON is
> > always 0. Then if you want to change from OFF = 0xaaa to OFF = 0xccc we
> > might see a period with 0xacc. Depending on how the hardware works we
> > might even see 4 edges in a single period then.)
> 
> Yes, there is a free running counter from 0 to 4095.
> And it is probably true, that there can be short intermediate states
> with our two register writes.
> 
> There is a separate mode "Update on ACK" (MODE2 register, bit 3 "OCH"),
> which is 0 by default (Outputs change on STOP command) but could be set
> to 1 (Outputs change on ACK):
> "Update on ACK requires all 4 PWM channel registers to be loaded before
> outputs will change on the last ACK."

After reading the manual I understood that at least a bit now.

The Output chang on STOP is only needed to synchronize two or more
PCA9685 chips. Also with "Update on ACK" it is possible to prevent
glitches when writing with the auto increment mode. Not sure what
happens with the way it is implemented now, as it isn't described in
manual when the registers are written in four separate transfers.

I agree that addressing this in a separater patch is sensible.

Best regards
Uwe

-- 
Pengutronix e.K.                           | Uwe Kleine-König            |
Industrial Linux Solutions                 | https://www.pengutronix.de/ |

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 33+ messages in thread

end of thread, other threads:[~2021-04-02 20:23 UTC | newest]

Thread overview: 33+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-03-29 12:57 [PATCH v6 1/7] pwm: pca9685: Switch to atomic API Clemens Gruber
2021-03-29 12:57 ` [PATCH v6 2/7] pwm: pca9685: Support hardware readout Clemens Gruber
2021-03-29 15:51   ` Uwe Kleine-König
2021-03-29 16:33     ` Clemens Gruber
2021-03-29 16:54   ` Uwe Kleine-König
2021-03-29 17:11     ` Clemens Gruber
2021-03-29 17:41       ` Uwe Kleine-König
2021-03-29 12:57 ` [PATCH v6 3/7] pwm: pca9685: Improve runtime PM behavior Clemens Gruber
2021-03-29 15:55   ` Uwe Kleine-König
2021-03-29 16:31     ` Clemens Gruber
2021-03-29 12:57 ` [PATCH v6 4/7] pwm: pca9685: Support staggered output ON times Clemens Gruber
2021-03-29 17:03   ` Uwe Kleine-König
2021-03-29 17:16     ` Clemens Gruber
2021-03-29 18:02       ` Uwe Kleine-König
2021-03-31 12:26         ` Clemens Gruber
2021-03-31 13:55           ` Clemens Gruber
2021-04-01 20:59             ` Uwe Kleine-König
2021-04-01 21:53               ` Clemens Gruber
2021-03-31 16:21           ` Thierry Reding
2021-04-01  7:50             ` Clemens Gruber
2021-04-01 13:47               ` Thierry Reding
2021-04-01 15:19                 ` Clemens Gruber
2021-04-02 19:48             ` Uwe Kleine-König
2021-04-01 20:58           ` Uwe Kleine-König
2021-04-01 21:37             ` Clemens Gruber
2021-04-02 20:23           ` Uwe Kleine-König
2021-03-29 12:57 ` [PATCH v6 5/7] dt-bindings: pwm: pca9685: Add nxp,staggered-outputs property Clemens Gruber
2021-04-02 19:52   ` Uwe Kleine-König
2021-03-29 12:57 ` [PATCH v6 6/7] pwm: pca9685: Restrict period change for prescaler users Clemens Gruber
2021-03-29 17:15   ` Uwe Kleine-König
2021-03-29 17:33     ` Clemens Gruber
2021-03-29 17:49       ` Uwe Kleine-König
2021-03-29 12:57 ` [PATCH v6 7/7] pwm: pca9685: Add error messages for failed regmap calls Clemens Gruber

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).