linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v8 1/8] pwm: pca9685: Switch to atomic API
@ 2021-04-12 13:27 Clemens Gruber
  2021-04-12 13:27 ` [PATCH v8 2/8] pwm: pca9685: Support hardware readout Clemens Gruber
                   ` (8 more replies)
  0 siblings, 9 replies; 42+ messages in thread
From: Clemens Gruber @ 2021-04-12 13:27 UTC (permalink / raw)
  To: linux-pwm
  Cc: Thierry Reding, Sven Van Asbroeck, Uwe Kleine-König,
	devicetree, 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
- Previously, only the full ON bit was toggled in GPIO mode (and full
  OFF cleared if set to high), which could result in both full OFF and
  full ON not being set and on=0, off=0, which is not allowed according
  to the datasheet
- The OFF registers were reset to 0 in probe, which could lead to the
  forbidden on=0, off=0. Fixed by resetting to POR default (full OFF)

Signed-off-by: Clemens Gruber <clemens.gruber@pqgruber.com>
---
Changes since v7:
- Moved check for !state->enabled before prescaler configuration
- Removed unnecessary cast
- Use DIV_ROUND_DOWN in .apply

Changes since v6:
- Order of a comparison switched for improved readability

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 | 259 +++++++++++++-------------------------
 1 file changed, 89 insertions(+), 170 deletions(-)

diff --git a/drivers/pwm/pwm-pca9685.c b/drivers/pwm/pwm-pca9685.c
index 4a55dc18656c..827b57ced3c2 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,52 @@ 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);
-
+	if (!state->enabled) {
+		pca9685_pwm_set_duty(pca, pwm->hwpwm, 0);
 		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, prescale);
 
-	regmap_update_bits(pca->regmap, reg, LED_FULL, 0x0);
+		/* Wake the chip up */
+		pca9685_set_sleep_mode(pca, false);
+	}
 
+	duty = PCA9685_COUNTER_RANGE * state->duty_cycle;
+	duty = DIV_ROUND_DOWN_ULL(duty, state->period);
+	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 +344,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 +381,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 +403,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] 42+ messages in thread

* [PATCH v8 2/8] pwm: pca9685: Support hardware readout
  2021-04-12 13:27 [PATCH v8 1/8] pwm: pca9685: Switch to atomic API Clemens Gruber
@ 2021-04-12 13:27 ` Clemens Gruber
  2021-04-12 16:21   ` Uwe Kleine-König
  2021-04-12 13:27 ` [PATCH v8 3/8] pwm: pca9685: Improve runtime PM behavior Clemens Gruber
                   ` (7 subsequent siblings)
  8 siblings, 1 reply; 42+ messages in thread
From: Clemens Gruber @ 2021-04-12 13:27 UTC (permalink / raw)
  To: linux-pwm
  Cc: Thierry Reding, Sven Van Asbroeck, Uwe Kleine-König,
	devicetree, linux-kernel, Clemens Gruber

Implement .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>
---
Changes since v7:
- Always return enabled=true for channels except "all LEDs" channel
- Use DIV_ROUND_UP in .get_state

Changes since v6:
- Added a comment regarding the division (Suggested by Uwe)
- Rebased

 drivers/pwm/pwm-pca9685.c | 36 ++++++++++++++++++++++++++++++++++++
 1 file changed, 36 insertions(+)

diff --git a/drivers/pwm/pwm-pca9685.c b/drivers/pwm/pwm-pca9685.c
index 827b57ced3c2..b39c0ba701ab 100644
--- a/drivers/pwm/pwm-pca9685.c
+++ b/drivers/pwm/pwm-pca9685.c
@@ -329,6 +329,41 @@ 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);
+	/*
+	 * PCA9685_OSC_CLOCK_MHZ is 25, i.e. an integer divider of 1000.
+	 * The following calculation is therefore only a multiplication
+	 * and we are not losing precision.
+	 */
+	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;
+	}
+
+	state->enabled = true;
+	duty = pca9685_pwm_get_duty(pca, pwm->hwpwm);
+	state->duty_cycle = DIV_ROUND_UP_ULL(duty * state->period, PCA9685_COUNTER_RANGE);
+}
+
 static int pca9685_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
 {
 	struct pca9685 *pca = to_pca(chip);
@@ -351,6 +386,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] 42+ messages in thread

* [PATCH v8 3/8] pwm: pca9685: Improve runtime PM behavior
  2021-04-12 13:27 [PATCH v8 1/8] pwm: pca9685: Switch to atomic API Clemens Gruber
  2021-04-12 13:27 ` [PATCH v8 2/8] pwm: pca9685: Support hardware readout Clemens Gruber
@ 2021-04-12 13:27 ` Clemens Gruber
  2021-04-12 13:27 ` [PATCH v8 4/8] dt-bindings: pwm: Support new PWM_USAGE_POWER flag Clemens Gruber
                   ` (6 subsequent siblings)
  8 siblings, 0 replies; 42+ messages in thread
From: Clemens Gruber @ 2021-04-12 13:27 UTC (permalink / raw)
  To: linux-pwm
  Cc: Thierry Reding, Sven Van Asbroeck, Uwe Kleine-König,
	devicetree, 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.

If runtime PM is disabled, we instead wake the chip in .probe and put it
to sleep in .remove.

Signed-off-by: Clemens Gruber <clemens.gruber@pqgruber.com>
---
Changes since v7:
- Handle sysfs power control as well and not just CONFIG_PM

Changes since v6:
- Improved !CONFIG_PM handling (wake it up without putting it to sleep
  first)

 drivers/pwm/pwm-pca9685.c | 25 +++++++++++++++++++------
 1 file changed, 19 insertions(+), 6 deletions(-)

diff --git a/drivers/pwm/pwm-pca9685.c b/drivers/pwm/pwm-pca9685.c
index b39c0ba701ab..7f97965033e7 100644
--- a/drivers/pwm/pwm-pca9685.c
+++ b/drivers/pwm/pwm-pca9685.c
@@ -460,14 +460,20 @@ 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
-	 */
 	pm_runtime_enable(&client->dev);
 
+	if (pm_runtime_enabled(&client->dev)) {
+		/*
+		 * Although the chip comes out of power-up in the sleep state,
+		 * we force it to sleep in case it was woken up before
+		 */
+		pca9685_set_sleep_mode(pca, true);
+		pm_runtime_set_suspended(&client->dev);
+	} else {
+		/* Wake the chip up if runtime PM is disabled */
+		pca9685_set_sleep_mode(pca, false);
+	}
+
 	return 0;
 }
 
@@ -479,7 +485,14 @@ static int pca9685_pwm_remove(struct i2c_client *client)
 	ret = pwmchip_remove(&pca->chip);
 	if (ret)
 		return ret;
+
+	if (!pm_runtime_enabled(&client->dev)) {
+		/* Put chip in sleep state if runtime PM is disabled */
+		pca9685_set_sleep_mode(pca, true);
+	}
+
 	pm_runtime_disable(&client->dev);
+
 	return 0;
 }
 
-- 
2.31.1


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

* [PATCH v8 4/8] dt-bindings: pwm: Support new PWM_USAGE_POWER flag
  2021-04-12 13:27 [PATCH v8 1/8] pwm: pca9685: Switch to atomic API Clemens Gruber
  2021-04-12 13:27 ` [PATCH v8 2/8] pwm: pca9685: Support hardware readout Clemens Gruber
  2021-04-12 13:27 ` [PATCH v8 3/8] pwm: pca9685: Improve runtime PM behavior Clemens Gruber
@ 2021-04-12 13:27 ` Clemens Gruber
  2021-04-12 16:27   ` Uwe Kleine-König
  2021-04-16 13:55   ` Thierry Reding
  2021-04-12 13:27 ` [PATCH v8 5/8] pwm: core: " Clemens Gruber
                   ` (5 subsequent siblings)
  8 siblings, 2 replies; 42+ messages in thread
From: Clemens Gruber @ 2021-04-12 13:27 UTC (permalink / raw)
  To: linux-pwm
  Cc: Thierry Reding, Sven Van Asbroeck, Uwe Kleine-König,
	devicetree, linux-kernel, Clemens Gruber, Rob Herring

Add the flag and corresponding documentation for PWM_USAGE_POWER.

Cc: Rob Herring <robh+dt@kernel.org>
Signed-off-by: Clemens Gruber <clemens.gruber@pqgruber.com>
---
 Documentation/devicetree/bindings/pwm/pwm.txt | 3 +++
 include/dt-bindings/pwm/pwm.h                 | 1 +
 2 files changed, 4 insertions(+)

diff --git a/Documentation/devicetree/bindings/pwm/pwm.txt b/Documentation/devicetree/bindings/pwm/pwm.txt
index 084886bd721e..fe3a28f887c0 100644
--- a/Documentation/devicetree/bindings/pwm/pwm.txt
+++ b/Documentation/devicetree/bindings/pwm/pwm.txt
@@ -46,6 +46,9 @@ period in nanoseconds.
 Optionally, the pwm-specifier can encode a number of flags (defined in
 <dt-bindings/pwm/pwm.h>) in a third cell:
 - PWM_POLARITY_INVERTED: invert the PWM signal polarity
+- PWM_USAGE_POWER: Only care about the power output of the signal. This
+  allows drivers (if supported) to optimize the signals, for example to
+  improve EMI and reduce current spikes.
 
 Example with optional PWM specifier for inverse polarity
 
diff --git a/include/dt-bindings/pwm/pwm.h b/include/dt-bindings/pwm/pwm.h
index ab9a077e3c7d..0d5a8f0c0035 100644
--- a/include/dt-bindings/pwm/pwm.h
+++ b/include/dt-bindings/pwm/pwm.h
@@ -11,5 +11,6 @@
 #define _DT_BINDINGS_PWM_PWM_H
 
 #define PWM_POLARITY_INVERTED			(1 << 0)
+#define PWM_USAGE_POWER				(1 << 1)
 
 #endif
-- 
2.31.1


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

* [PATCH v8 5/8] pwm: core: Support new PWM_USAGE_POWER flag
  2021-04-12 13:27 [PATCH v8 1/8] pwm: pca9685: Switch to atomic API Clemens Gruber
                   ` (2 preceding siblings ...)
  2021-04-12 13:27 ` [PATCH v8 4/8] dt-bindings: pwm: Support new PWM_USAGE_POWER flag Clemens Gruber
@ 2021-04-12 13:27 ` Clemens Gruber
  2021-04-12 13:27 ` [PATCH v8 6/8] pwm: pca9685: " Clemens Gruber
                   ` (4 subsequent siblings)
  8 siblings, 0 replies; 42+ messages in thread
From: Clemens Gruber @ 2021-04-12 13:27 UTC (permalink / raw)
  To: linux-pwm
  Cc: Thierry Reding, Sven Van Asbroeck, Uwe Kleine-König,
	devicetree, linux-kernel, Clemens Gruber

If the flag PWM_USAGE_POWER is set on a channel, the PWM driver may
optimize the signal as long as the power output is not changed.

Depending on the specific driver, the optimization could for example
improve EMI (if supported) by phase-shifting the individual channels.

Signed-off-by: Clemens Gruber <clemens.gruber@pqgruber.com>
---
 drivers/pwm/core.c  | 9 +++++++--
 include/linux/pwm.h | 1 +
 2 files changed, 8 insertions(+), 2 deletions(-)

diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
index a8eff4b3ee36..56a9c739e1b2 100644
--- a/drivers/pwm/core.c
+++ b/drivers/pwm/core.c
@@ -153,9 +153,14 @@ of_pwm_xlate_with_flags(struct pwm_chip *pc, const struct of_phandle_args *args)
 
 	pwm->args.period = args->args[1];
 	pwm->args.polarity = PWM_POLARITY_NORMAL;
+	pwm->args.usage_power = false;
 
-	if (args->args_count > 2 && args->args[2] & PWM_POLARITY_INVERTED)
-		pwm->args.polarity = PWM_POLARITY_INVERSED;
+	if (args->args_count > 2) {
+		if (args->args[2] & PWM_POLARITY_INVERTED)
+			pwm->args.polarity = PWM_POLARITY_INVERSED;
+		if (args->args[2] & PWM_USAGE_POWER)
+			pwm->args.usage_power = true;
+	}
 
 	return pwm;
 }
diff --git a/include/linux/pwm.h b/include/linux/pwm.h
index e4d84d4db293..555e050e8bec 100644
--- a/include/linux/pwm.h
+++ b/include/linux/pwm.h
@@ -41,6 +41,7 @@ enum pwm_polarity {
 struct pwm_args {
 	u64 period;
 	enum pwm_polarity polarity;
+	bool usage_power;
 };
 
 enum {
-- 
2.31.1


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

* [PATCH v8 6/8] pwm: pca9685: Support new PWM_USAGE_POWER flag
  2021-04-12 13:27 [PATCH v8 1/8] pwm: pca9685: Switch to atomic API Clemens Gruber
                   ` (3 preceding siblings ...)
  2021-04-12 13:27 ` [PATCH v8 5/8] pwm: core: " Clemens Gruber
@ 2021-04-12 13:27 ` Clemens Gruber
  2021-04-12 16:30   ` Uwe Kleine-König
  2021-04-12 13:27 ` [PATCH v8 7/8] pwm: pca9685: Restrict period change for enabled PWMs Clemens Gruber
                   ` (3 subsequent siblings)
  8 siblings, 1 reply; 42+ messages in thread
From: Clemens Gruber @ 2021-04-12 13:27 UTC (permalink / raw)
  To: linux-pwm
  Cc: Thierry Reding, Sven Van Asbroeck, Uwe Kleine-König,
	devicetree, linux-kernel, Clemens Gruber

If PWM_USAGE_POWER is set on a PWM, the pca9685 driver will phase shift
the individual channels relative to their channel number. This improves
EMI because the enabled channels no longer turn on at the same time,
while still maintaining the configured duty cycle / power output.

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

diff --git a/drivers/pwm/pwm-pca9685.c b/drivers/pwm/pwm-pca9685.c
index 7f97965033e7..410b93b115dc 100644
--- a/drivers/pwm/pwm-pca9685.c
+++ b/drivers/pwm/pwm-pca9685.c
@@ -93,46 +93,76 @@ 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)
 {
+	struct pwm_device *pwm = &pca->chip.pwms[channel];
+	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 (pwm->args.usage_power && channel < PCA9685_MAXCHAN) {
+		/*
+		 * If PWM_USAGE_POWER is set on a PWM, the pca9685
+		 * driver will phase shift the individual channels
+		 * relative to their channel number.
+		 * This improves EMI because the enabled channels no
+		 * longer turn on at the same time, while still
+		 * maintaining the configured duty cycle / power output.
+		 */
+		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;
+	struct pwm_device *pwm = &pca->chip.pwms[channel];
+	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 (!pwm->args.usage_power)
+		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)
@@ -439,9 +469,11 @@ 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);
 
-	/* 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 */
@@ -450,6 +482,9 @@ static int pca9685_pwm_probe(struct i2c_client *client,
 	pca->chip.dev = &client->dev;
 	pca->chip.base = -1;
 
+	pca->chip.of_xlate = of_pwm_xlate_with_flags;
+	pca->chip.of_pwm_n_cells = 3;
+
 	ret = pwmchip_add(&pca->chip);
 	if (ret < 0)
 		return ret;
-- 
2.31.1


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

* [PATCH v8 7/8] pwm: pca9685: Restrict period change for enabled PWMs
  2021-04-12 13:27 [PATCH v8 1/8] pwm: pca9685: Switch to atomic API Clemens Gruber
                   ` (4 preceding siblings ...)
  2021-04-12 13:27 ` [PATCH v8 6/8] pwm: pca9685: " Clemens Gruber
@ 2021-04-12 13:27 ` Clemens Gruber
  2021-04-17 15:46   ` Uwe Kleine-König
  2021-04-12 13:27 ` [PATCH v8 8/8] pwm: pca9685: Add error messages for failed regmap calls Clemens Gruber
                   ` (2 subsequent siblings)
  8 siblings, 1 reply; 42+ messages in thread
From: Clemens Gruber @ 2021-04-12 13:27 UTC (permalink / raw)
  To: linux-pwm
  Cc: Thierry Reding, Sven Van Asbroeck, Uwe Kleine-König,
	devicetree, 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 enabled PWM 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.

GPIOs do not count as enabled PWMs as they are not using the prescaler
and can't change it.

Signed-off-by: Clemens Gruber <clemens.gruber@pqgruber.com>
---
Changes since v7:
- As the HW readout always returns enabled, also set the pwms_enabled
  bit in request (except for the "all LEDs" channel)

Changes since v6:
- Only allow the first PWM that is enabled to change the prescaler, not
  the first one that uses the prescaler

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

diff --git a/drivers/pwm/pwm-pca9685.c b/drivers/pwm/pwm-pca9685.c
index 410b93b115dc..4583edd5e477 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 is enabled 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.
+ * GPIOs do not count as enabled PWMs as they are not using the prescaler.
  */
 
 #define PCA9685_MODE1		0x00
@@ -78,8 +78,9 @@
 struct pca9685 {
 	struct pwm_chip chip;
 	struct regmap *regmap;
-#if IS_ENABLED(CONFIG_GPIOLIB)
 	struct mutex lock;
+	DECLARE_BITMAP(pwms_enabled, PCA9685_MAXCHAN + 1);
+#if IS_ENABLED(CONFIG_GPIOLIB)
 	struct gpio_chip gpio;
 	DECLARE_BITMAP(pwms_inuse, PCA9685_MAXCHAN + 1);
 #endif
@@ -90,6 +91,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)
+{
+	/* No PWM enabled: Change allowed */
+	if (bitmap_empty(pca->pwms_enabled, PCA9685_MAXCHAN + 1))
+		return true;
+	/* More than one PWM enabled: Change not allowed */
+	if (bitmap_weight(pca->pwms_enabled, PCA9685_MAXCHAN + 1) > 1)
+		return false;
+	/*
+	 * Only one PWM enabled: Change allowed if the PWM about to
+	 * be changed is the one that is already enabled
+	 */
+	return test_bit(channel, pca->pwms_enabled);
+}
+
 /* 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)
 {
@@ -268,8 +285,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;
@@ -313,8 +328,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;
@@ -337,6 +352,12 @@ static int pca9685_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
 
 	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: periods of enabled pwms must match!\n");
+			return -EBUSY;
+		}
+
 		/*
 		 * Putting the chip briefly into SLEEP mode
 		 * at this point won't interfere with the
@@ -359,6 +380,25 @@ static int pca9685_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
 	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);
+	if (ret == 0) {
+		if (state->enabled)
+			set_bit(pwm->hwpwm, pca->pwms_enabled);
+		else
+			clear_bit(pwm->hwpwm, pca->pwms_enabled);
+	}
+	mutex_unlock(&pca->lock);
+
+	return ret;
+}
+
 static void pca9685_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
 				  struct pwm_state *state)
 {
@@ -400,6 +440,14 @@ static int pca9685_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
 
 	if (pca9685_pwm_test_and_set_inuse(pca, pwm->hwpwm))
 		return -EBUSY;
+
+	if (pwm->hwpwm < PCA9685_MAXCHAN) {
+		/* PWMs - except the "all LEDs" channel - default to enabled */
+		mutex_lock(&pca->lock);
+		set_bit(pwm->hwpwm, pca->pwms_enabled);
+		mutex_unlock(&pca->lock);
+	}
+
 	pm_runtime_get_sync(chip->dev);
 
 	return 0;
@@ -409,7 +457,11 @@ static void pca9685_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
 {
 	struct pca9685 *pca = to_pca(chip);
 
+	mutex_lock(&pca->lock);
 	pca9685_pwm_set_duty(pca, pwm->hwpwm, 0);
+	clear_bit(pwm->hwpwm, pca->pwms_enabled);
+	mutex_unlock(&pca->lock);
+
 	pm_runtime_put(chip->dev);
 	pca9685_pwm_clear_inuse(pca, pwm->hwpwm);
 }
@@ -450,6 +502,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] 42+ messages in thread

* [PATCH v8 8/8] pwm: pca9685: Add error messages for failed regmap calls
  2021-04-12 13:27 [PATCH v8 1/8] pwm: pca9685: Switch to atomic API Clemens Gruber
                   ` (5 preceding siblings ...)
  2021-04-12 13:27 ` [PATCH v8 7/8] pwm: pca9685: Restrict period change for enabled PWMs Clemens Gruber
@ 2021-04-12 13:27 ` Clemens Gruber
  2021-04-17 15:47   ` Uwe Kleine-König
  2021-04-12 16:18 ` [PATCH v8 1/8] pwm: pca9685: Switch to atomic API Uwe Kleine-König
  2021-04-17 15:37 ` Uwe Kleine-König
  8 siblings, 1 reply; 42+ messages in thread
From: Clemens Gruber @ 2021-04-12 13:27 UTC (permalink / raw)
  To: linux-pwm
  Cc: Thierry Reding, Sven Van Asbroeck, Uwe Kleine-König,
	devicetree, 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>
---
Changes since v7:
- Use %pe instead of %d for error codes (Suggested by Uwe)

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 4583edd5e477..b0b321203947 100644
--- a/drivers/pwm/pwm-pca9685.c
+++ b/drivers/pwm/pwm-pca9685.c
@@ -107,6 +107,30 @@ static bool pca9685_prescaler_can_change(struct pca9685 *pca, int channel)
 	return test_bit(channel, pca->pwms_enabled);
 }
 
+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: %pe\n", reg, ERR_PTR(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: %pe\n", reg, ERR_PTR(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;
 	}
 
@@ -141,11 +165,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)
@@ -158,26 +182,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 (!pwm->args.usage_power)
 		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);
 }
@@ -320,8 +344,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: %pe\n",
+			PCA9685_MODE1, ERR_PTR(err));
+		return;
+	}
+
 	if (!enable) {
 		/* Wait 500us for the oscillator to be back up */
 		udelay(500);
@@ -350,7 +381,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,
@@ -368,7 +399,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, prescale);
+		pca9685_write_reg(pca, PCA9685_PRESCALE, prescale);
 
 		/* Wake the chip up */
 		pca9685_set_sleep_mode(pca, false);
@@ -407,7 +438,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);
 	/*
 	 * PCA9685_OSC_CLOCK_MHZ is 25, i.e. an integer divider of 1000.
 	 * The following calculation is therefore only a multiplication
@@ -504,7 +535,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;
@@ -516,18 +549,20 @@ 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;
 
 	/* 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] 42+ messages in thread

* Re: [PATCH v8 1/8] pwm: pca9685: Switch to atomic API
  2021-04-12 13:27 [PATCH v8 1/8] pwm: pca9685: Switch to atomic API Clemens Gruber
                   ` (6 preceding siblings ...)
  2021-04-12 13:27 ` [PATCH v8 8/8] pwm: pca9685: Add error messages for failed regmap calls Clemens Gruber
@ 2021-04-12 16:18 ` Uwe Kleine-König
  2021-04-12 16:39   ` Clemens Gruber
  2021-04-17 15:37 ` Uwe Kleine-König
  8 siblings, 1 reply; 42+ messages in thread
From: Uwe Kleine-König @ 2021-04-12 16:18 UTC (permalink / raw)
  To: Clemens Gruber
  Cc: linux-pwm, Thierry Reding, Sven Van Asbroeck, devicetree, linux-kernel

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

Hello Clemens,

On Mon, Apr 12, 2021 at 03:27:38PM +0200, Clemens Gruber wrote:
> 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
> - Previously, only the full ON bit was toggled in GPIO mode (and full
>   OFF cleared if set to high), which could result in both full OFF and
>   full ON not being set and on=0, off=0, which is not allowed according
>   to the datasheet
> - The OFF registers were reset to 0 in probe, which could lead to the
>   forbidden on=0, off=0. Fixed by resetting to POR default (full OFF)
> 
> Signed-off-by: Clemens Gruber <clemens.gruber@pqgruber.com>
> ---
> Changes since v7:
> - Moved check for !state->enabled before prescaler configuration
> - Removed unnecessary cast
> - Use DIV_ROUND_DOWN in .apply
> 
> Changes since v6:
> - Order of a comparison switched for improved readability
> 
> 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 | 259 +++++++++++++-------------------------
>  1 file changed, 89 insertions(+), 170 deletions(-)
> 
> diff --git a/drivers/pwm/pwm-pca9685.c b/drivers/pwm/pwm-pca9685.c
> index 4a55dc18656c..827b57ced3c2 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)))

I'd like to see these named PCA9685_REG_ON_H etc.

>  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);

I asked in the last round why you initialize val. You answered "just to
have it set to 0 in case regmap_read fails / val was not set." I wonder
if

	ret = regmap_read(pca->regmap, LED_N_OFF_L(channel), &val);
	if (!ret)
		/* 
		val = 0

would be better then and also make the intention obvious.

> +	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,52 @@ 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);
> -
> +	if (!state->enabled) {
> +		pca9685_pwm_set_duty(pca, pwm->hwpwm, 0);
>  		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, prescale);
>  
> -	regmap_update_bits(pca->regmap, reg, LED_FULL, 0x0);
> +		/* Wake the chip up */
> +		pca9685_set_sleep_mode(pca, false);
> +	}
>  
> +	duty = PCA9685_COUNTER_RANGE * state->duty_cycle;
> +	duty = DIV_ROUND_DOWN_ULL(duty, state->period);

If you round down here you should probably also round down in the
calculation of prescale. Also note that you're losing precision by using
state->period.

Consider the following request: state->period = 4177921 [ns] +
state->duty_cycle = 100000 [ns], then we get
PRESCALE = round(25 * state->period / 4096000) - 1 = 25 and so an actual
period of 4096000 / 25 * (25 + 1) = 4259840 [ns]. If you now calculate
the duty using 4096 * 100000 / 4177920 = 98, this corresponds to an
actual duty cycle of 98 * 4259840 / 4096 = 101920 ns while you should
actually configure 96 to get 99840 ns.

So in the end I'd like to have the following:

	PRESCALE = round-down(25 * state->period / 4096000) - 1

(to get the biggest period not bigger than state->period) and

	duty = round-down(state->duty_cycle * 25 / ((PRESCALE + 1) * 1000))

(to get the biggest duty cycle not bigger than state->duty_cycle). With
the example above this yields

	PRESCALE = 24
	duty = 100

which results in

	.period = 4096000 / 25 * 25 = 4096000 [ns]
	.duty_cycle = 100000 [ns]
	
Now you have a mixture of old and new with no consistent behaviour. So
please either stick to the old behaviour or do it right immediately.

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] 42+ messages in thread

* Re: [PATCH v8 2/8] pwm: pca9685: Support hardware readout
  2021-04-12 13:27 ` [PATCH v8 2/8] pwm: pca9685: Support hardware readout Clemens Gruber
@ 2021-04-12 16:21   ` Uwe Kleine-König
  0 siblings, 0 replies; 42+ messages in thread
From: Uwe Kleine-König @ 2021-04-12 16:21 UTC (permalink / raw)
  To: Clemens Gruber
  Cc: linux-pwm, Thierry Reding, Sven Van Asbroeck, devicetree, linux-kernel

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

Hello Clemens,

On Mon, Apr 12, 2021 at 03:27:39PM +0200, Clemens Gruber wrote:
> Implement .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>

Reviewed-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>

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] 42+ messages in thread

* Re: [PATCH v8 4/8] dt-bindings: pwm: Support new PWM_USAGE_POWER flag
  2021-04-12 13:27 ` [PATCH v8 4/8] dt-bindings: pwm: Support new PWM_USAGE_POWER flag Clemens Gruber
@ 2021-04-12 16:27   ` Uwe Kleine-König
  2021-04-12 16:46     ` Clemens Gruber
  2021-04-13 11:51     ` Thierry Reding
  2021-04-16 13:55   ` Thierry Reding
  1 sibling, 2 replies; 42+ messages in thread
From: Uwe Kleine-König @ 2021-04-12 16:27 UTC (permalink / raw)
  To: Clemens Gruber
  Cc: linux-pwm, Thierry Reding, Sven Van Asbroeck, devicetree,
	linux-kernel, Rob Herring

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

On Mon, Apr 12, 2021 at 03:27:41PM +0200, Clemens Gruber wrote:
> Add the flag and corresponding documentation for PWM_USAGE_POWER.

My concern here in the previous round was that PWM_USAGE_POWER isn't a
name that intuitively suggests its semantic. Do you disagree?

> Cc: Rob Herring <robh+dt@kernel.org>
> Signed-off-by: Clemens Gruber <clemens.gruber@pqgruber.com>
> ---
>  Documentation/devicetree/bindings/pwm/pwm.txt | 3 +++
>  include/dt-bindings/pwm/pwm.h                 | 1 +
>  2 files changed, 4 insertions(+)
> 
> diff --git a/Documentation/devicetree/bindings/pwm/pwm.txt b/Documentation/devicetree/bindings/pwm/pwm.txt
> index 084886bd721e..fe3a28f887c0 100644
> --- a/Documentation/devicetree/bindings/pwm/pwm.txt
> +++ b/Documentation/devicetree/bindings/pwm/pwm.txt
> @@ -46,6 +46,9 @@ period in nanoseconds.
>  Optionally, the pwm-specifier can encode a number of flags (defined in
>  <dt-bindings/pwm/pwm.h>) in a third cell:
>  - PWM_POLARITY_INVERTED: invert the PWM signal polarity
> +- PWM_USAGE_POWER: Only care about the power output of the signal. This
> +  allows drivers (if supported) to optimize the signals, for example to
> +  improve EMI and reduce current spikes.

IMHO there are too many open questions about which freedom this gives to
the lowlevel driver. If the consumer requests .duty_cycle = 25ns +
.period = 100ns, can the driver provide .duty_cycle = 25s + .period =
100s which nominally has the same power output? Let's not introduce more
ambiguity than there already is.

This is a NAck.

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] 42+ messages in thread

* Re: [PATCH v8 6/8] pwm: pca9685: Support new PWM_USAGE_POWER flag
  2021-04-12 13:27 ` [PATCH v8 6/8] pwm: pca9685: " Clemens Gruber
@ 2021-04-12 16:30   ` Uwe Kleine-König
  2021-04-12 17:11     ` Clemens Gruber
  0 siblings, 1 reply; 42+ messages in thread
From: Uwe Kleine-König @ 2021-04-12 16:30 UTC (permalink / raw)
  To: Clemens Gruber
  Cc: linux-pwm, Thierry Reding, Sven Van Asbroeck, devicetree, linux-kernel

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

On Mon, Apr 12, 2021 at 03:27:43PM +0200, Clemens Gruber wrote:
> If PWM_USAGE_POWER is set on a PWM, the pca9685 driver will phase shift
> the individual channels relative to their channel number. This improves
> EMI because the enabled channels no longer turn on at the same time,
> while still maintaining the configured duty cycle / power output.
> 
> Signed-off-by: Clemens Gruber <clemens.gruber@pqgruber.com>
> ---
>  drivers/pwm/pwm-pca9685.c | 63 ++++++++++++++++++++++++++++++---------
>  1 file changed, 49 insertions(+), 14 deletions(-)
> 
> diff --git a/drivers/pwm/pwm-pca9685.c b/drivers/pwm/pwm-pca9685.c
> index 7f97965033e7..410b93b115dc 100644
> --- a/drivers/pwm/pwm-pca9685.c
> +++ b/drivers/pwm/pwm-pca9685.c
> @@ -93,46 +93,76 @@ 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)
>  {
> +	struct pwm_device *pwm = &pca->chip.pwms[channel];
> +	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 (pwm->args.usage_power && channel < PCA9685_MAXCHAN) {
> +		/*
> +		 * If PWM_USAGE_POWER is set on a PWM, the pca9685
> +		 * driver will phase shift the individual channels
> +		 * relative to their channel number.
> +		 * This improves EMI because the enabled channels no
> +		 * longer turn on at the same time, while still
> +		 * maintaining the configured duty cycle / power output.
> +		 */
> +		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;
> +	struct pwm_device *pwm = &pca->chip.pwms[channel];
> +	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 (!pwm->args.usage_power)
> +		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 LED_N_ON is != 0 but usage_power is false, the returned state is
bogus.

>  }
>  
>  #if IS_ENABLED(CONFIG_GPIOLIB)
> @@ -439,9 +469,11 @@ 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);
>  
> -	/* 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 */
> @@ -450,6 +482,9 @@ static int pca9685_pwm_probe(struct i2c_client *client,
>  	pca->chip.dev = &client->dev;
>  	pca->chip.base = -1;
>  
> +	pca->chip.of_xlate = of_pwm_xlate_with_flags;
> +	pca->chip.of_pwm_n_cells = 3;
> +

Huh, you're incompatibly changing the device tree binding here.

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] 42+ messages in thread

* Re: [PATCH v8 1/8] pwm: pca9685: Switch to atomic API
  2021-04-12 16:18 ` [PATCH v8 1/8] pwm: pca9685: Switch to atomic API Uwe Kleine-König
@ 2021-04-12 16:39   ` Clemens Gruber
  2021-04-12 20:10     ` Uwe Kleine-König
  0 siblings, 1 reply; 42+ messages in thread
From: Clemens Gruber @ 2021-04-12 16:39 UTC (permalink / raw)
  To: Uwe Kleine-König
  Cc: linux-pwm, Thierry Reding, Sven Van Asbroeck, devicetree, linux-kernel

Hi Uwe,

On Mon, Apr 12, 2021 at 06:18:08PM +0200, Uwe Kleine-König wrote:
> Hello Clemens,
> 
> On Mon, Apr 12, 2021 at 03:27:38PM +0200, Clemens Gruber wrote:
> > 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
> > - Previously, only the full ON bit was toggled in GPIO mode (and full
> >   OFF cleared if set to high), which could result in both full OFF and
> >   full ON not being set and on=0, off=0, which is not allowed according
> >   to the datasheet
> > - The OFF registers were reset to 0 in probe, which could lead to the
> >   forbidden on=0, off=0. Fixed by resetting to POR default (full OFF)
> > 
> > Signed-off-by: Clemens Gruber <clemens.gruber@pqgruber.com>
> > ---
> > Changes since v7:
> > - Moved check for !state->enabled before prescaler configuration
> > - Removed unnecessary cast
> > - Use DIV_ROUND_DOWN in .apply
> > 
> > Changes since v6:
> > - Order of a comparison switched for improved readability
> > 
> > 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 | 259 +++++++++++++-------------------------
> >  1 file changed, 89 insertions(+), 170 deletions(-)
> > 
> > diff --git a/drivers/pwm/pwm-pca9685.c b/drivers/pwm/pwm-pca9685.c
> > index 4a55dc18656c..827b57ced3c2 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)))
> 
> I'd like to see these named PCA9685_REG_ON_H etc.

I did not use the prefix because the existing LED_N_ON/OFF_H/L also do
not have a prefix. If the prefix is mandatory, I think LED_N_.. should
also be prefixed, right?

> 
> >  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);
> 
> I asked in the last round why you initialize val. You answered "just to
> have it set to 0 in case regmap_read fails / val was not set." I wonder
> if
> 
> 	ret = regmap_read(pca->regmap, LED_N_OFF_L(channel), &val);
> 	if (!ret)
> 		/* 
> 		val = 0
> 
> would be better then and also make the intention obvious.

I am not sure if that's more clear, but if others find it more obvious
like this, I can change it.

> 
> > +	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,52 @@ 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);
> > -
> > +	if (!state->enabled) {
> > +		pca9685_pwm_set_duty(pca, pwm->hwpwm, 0);
> >  		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, prescale);
> >  
> > -	regmap_update_bits(pca->regmap, reg, LED_FULL, 0x0);
> > +		/* Wake the chip up */
> > +		pca9685_set_sleep_mode(pca, false);
> > +	}
> >  
> > +	duty = PCA9685_COUNTER_RANGE * state->duty_cycle;
> > +	duty = DIV_ROUND_DOWN_ULL(duty, state->period);
> 
> If you round down here you should probably also round down in the
> calculation of prescale. Also note that you're losing precision by using
> state->period.
> 
> Consider the following request: state->period = 4177921 [ns] +
> state->duty_cycle = 100000 [ns], then we get
> PRESCALE = round(25 * state->period / 4096000) - 1 = 25 and so an actual
> period of 4096000 / 25 * (25 + 1) = 4259840 [ns]. If you now calculate
> the duty using 4096 * 100000 / 4177920 = 98, this corresponds to an
> actual duty cycle of 98 * 4259840 / 4096 = 101920 ns while you should
> actually configure 96 to get 99840 ns.
> 
> So in the end I'd like to have the following:
> 
> 	PRESCALE = round-down(25 * state->period / 4096000) - 1
> 
> (to get the biggest period not bigger than state->period) and
> 
> 	duty = round-down(state->duty_cycle * 25 / ((PRESCALE + 1) * 1000))
> 
> (to get the biggest duty cycle not bigger than state->duty_cycle). With
> the example above this yields
> 
> 	PRESCALE = 24
> 	duty = 100
> 
> which results in
> 
> 	.period = 4096000 / 25 * 25 = 4096000 [ns]
> 	.duty_cycle = 100000 [ns]
> 	
> Now you have a mixture of old and new with no consistent behaviour. So
> please either stick to the old behaviour or do it right immediately.

I avoided rounding down the prescale value because the datasheet has an
example where a round-closest is used, see page 25.

With your suggested round-down, the example with frequency of 200 Hz
would no longer result in 30 but 29 and that contradicts the datasheet.

So would you rather have me keep the old duty rounding behaviour?
Meaning: Keep rounding up the duty calculation in apply and use
round-down in the new .get_state function?

Thanks,
Clemens

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

* Re: [PATCH v8 4/8] dt-bindings: pwm: Support new PWM_USAGE_POWER flag
  2021-04-12 16:27   ` Uwe Kleine-König
@ 2021-04-12 16:46     ` Clemens Gruber
  2021-04-13 11:38       ` Uwe Kleine-König
  2021-04-13 11:51     ` Thierry Reding
  1 sibling, 1 reply; 42+ messages in thread
From: Clemens Gruber @ 2021-04-12 16:46 UTC (permalink / raw)
  To: Uwe Kleine-König
  Cc: linux-pwm, Thierry Reding, Sven Van Asbroeck, devicetree,
	linux-kernel, Rob Herring

On Mon, Apr 12, 2021 at 06:27:23PM +0200, Uwe Kleine-König wrote:
> On Mon, Apr 12, 2021 at 03:27:41PM +0200, Clemens Gruber wrote:
> > Add the flag and corresponding documentation for PWM_USAGE_POWER.
> 
> My concern here in the previous round was that PWM_USAGE_POWER isn't a
> name that intuitively suggests its semantic. Do you disagree?

No. It is more abstract and requires documentation. But I also didn't
want to waste too much time on discussing names, so I used Thierry's
suggestion.

I guess we need his input on this subject. I can live both with
PWM_ALLOW_PHASE_SHIFTING and PWM_USAGE_POWER, as long as it is
documented well enough.

> 
> > Cc: Rob Herring <robh+dt@kernel.org>
> > Signed-off-by: Clemens Gruber <clemens.gruber@pqgruber.com>
> > ---
> >  Documentation/devicetree/bindings/pwm/pwm.txt | 3 +++
> >  include/dt-bindings/pwm/pwm.h                 | 1 +
> >  2 files changed, 4 insertions(+)
> > 
> > diff --git a/Documentation/devicetree/bindings/pwm/pwm.txt b/Documentation/devicetree/bindings/pwm/pwm.txt
> > index 084886bd721e..fe3a28f887c0 100644
> > --- a/Documentation/devicetree/bindings/pwm/pwm.txt
> > +++ b/Documentation/devicetree/bindings/pwm/pwm.txt
> > @@ -46,6 +46,9 @@ period in nanoseconds.
> >  Optionally, the pwm-specifier can encode a number of flags (defined in
> >  <dt-bindings/pwm/pwm.h>) in a third cell:
> >  - PWM_POLARITY_INVERTED: invert the PWM signal polarity
> > +- PWM_USAGE_POWER: Only care about the power output of the signal. This
> > +  allows drivers (if supported) to optimize the signals, for example to
> > +  improve EMI and reduce current spikes.
> 
> IMHO there are too many open questions about which freedom this gives to
> the lowlevel driver. If the consumer requests .duty_cycle = 25ns +
> .period = 100ns, can the driver provide .duty_cycle = 25s + .period =
> 100s which nominally has the same power output? Let's not introduce more
> ambiguity than there already is.
> 
> This is a NAck.
> 
> Best regards
> Uwe
> 
> -- 
> Pengutronix e.K.                           | Uwe Kleine-König            |
> Industrial Linux Solutions                 | https://www.pengutronix.de/ |



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

* Re: [PATCH v8 6/8] pwm: pca9685: Support new PWM_USAGE_POWER flag
  2021-04-12 16:30   ` Uwe Kleine-König
@ 2021-04-12 17:11     ` Clemens Gruber
  2021-04-13 10:34       ` Uwe Kleine-König
  0 siblings, 1 reply; 42+ messages in thread
From: Clemens Gruber @ 2021-04-12 17:11 UTC (permalink / raw)
  To: Uwe Kleine-König
  Cc: linux-pwm, Thierry Reding, Sven Van Asbroeck, devicetree, linux-kernel

Hi,

On Mon, Apr 12, 2021 at 06:30:45PM +0200, Uwe Kleine-König wrote:
> On Mon, Apr 12, 2021 at 03:27:43PM +0200, Clemens Gruber wrote:
> > If PWM_USAGE_POWER is set on a PWM, the pca9685 driver will phase shift
> > the individual channels relative to their channel number. This improves
> > EMI because the enabled channels no longer turn on at the same time,
> > while still maintaining the configured duty cycle / power output.
> > 
> > Signed-off-by: Clemens Gruber <clemens.gruber@pqgruber.com>
> > ---
> >  drivers/pwm/pwm-pca9685.c | 63 ++++++++++++++++++++++++++++++---------
> >  1 file changed, 49 insertions(+), 14 deletions(-)
> > 
> > diff --git a/drivers/pwm/pwm-pca9685.c b/drivers/pwm/pwm-pca9685.c
> > index 7f97965033e7..410b93b115dc 100644
> > --- a/drivers/pwm/pwm-pca9685.c
> > +++ b/drivers/pwm/pwm-pca9685.c
> > @@ -93,46 +93,76 @@ 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)
> >  {
> > +	struct pwm_device *pwm = &pca->chip.pwms[channel];
> > +	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 (pwm->args.usage_power && channel < PCA9685_MAXCHAN) {
> > +		/*
> > +		 * If PWM_USAGE_POWER is set on a PWM, the pca9685
> > +		 * driver will phase shift the individual channels
> > +		 * relative to their channel number.
> > +		 * This improves EMI because the enabled channels no
> > +		 * longer turn on at the same time, while still
> > +		 * maintaining the configured duty cycle / power output.
> > +		 */
> > +		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;
> > +	struct pwm_device *pwm = &pca->chip.pwms[channel];
> > +	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 (!pwm->args.usage_power)
> > +		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 LED_N_ON is != 0 but usage_power is false, the returned state is
> bogus.

If usage_power is false, LED_N_ON is guaranteed to be 0. It is reset to
0 in probe and never changed. Or did I miss something?

> 
> >  }
> >  
> >  #if IS_ENABLED(CONFIG_GPIOLIB)
> > @@ -439,9 +469,11 @@ 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);
> >  
> > -	/* 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 */
> > @@ -450,6 +482,9 @@ static int pca9685_pwm_probe(struct i2c_client *client,
> >  	pca->chip.dev = &client->dev;
> >  	pca->chip.base = -1;
> >  
> > +	pca->chip.of_xlate = of_pwm_xlate_with_flags;
> > +	pca->chip.of_pwm_n_cells = 3;
> > +
> 
> Huh, you're incompatibly changing the device tree binding here.

No, I don't think so:

The third cell is optional with of_pwm_xlate_with_flags.
So previous DTs with pwm-cells = <2> will still work.
If you want to use the new flag for some PWMs you have to set pwm-cells
to <3> and PWM_USAGE_POWER (or 0) in the third field at the consumer.

This should not break backwards compatibility. Let me know if I missed
something.

Thanks for your review,
Clemens

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

* Re: [PATCH v8 1/8] pwm: pca9685: Switch to atomic API
  2021-04-12 16:39   ` Clemens Gruber
@ 2021-04-12 20:10     ` Uwe Kleine-König
  2021-04-13 12:11       ` Clemens Gruber
  0 siblings, 1 reply; 42+ messages in thread
From: Uwe Kleine-König @ 2021-04-12 20:10 UTC (permalink / raw)
  To: Clemens Gruber
  Cc: linux-pwm, Thierry Reding, Sven Van Asbroeck, devicetree, linux-kernel

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

Hello Clemens,

On Mon, Apr 12, 2021 at 06:39:28PM +0200, Clemens Gruber wrote:
> On Mon, Apr 12, 2021 at 06:18:08PM +0200, Uwe Kleine-König wrote:
> > On Mon, Apr 12, 2021 at 03:27:38PM +0200, Clemens Gruber wrote:
> > > 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
> > > - Previously, only the full ON bit was toggled in GPIO mode (and full
> > >   OFF cleared if set to high), which could result in both full OFF and
> > >   full ON not being set and on=0, off=0, which is not allowed according
> > >   to the datasheet
> > > - The OFF registers were reset to 0 in probe, which could lead to the
> > >   forbidden on=0, off=0. Fixed by resetting to POR default (full OFF)
> > > 
> > > Signed-off-by: Clemens Gruber <clemens.gruber@pqgruber.com>
> > > ---
> > > Changes since v7:
> > > - Moved check for !state->enabled before prescaler configuration
> > > - Removed unnecessary cast
> > > - Use DIV_ROUND_DOWN in .apply
> > > 
> > > Changes since v6:
> > > - Order of a comparison switched for improved readability
> > > 
> > > 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 | 259 +++++++++++++-------------------------
> > >  1 file changed, 89 insertions(+), 170 deletions(-)
> > > 
> > > diff --git a/drivers/pwm/pwm-pca9685.c b/drivers/pwm/pwm-pca9685.c
> > > index 4a55dc18656c..827b57ced3c2 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)))
> > 
> > I'd like to see these named PCA9685_REG_ON_H etc.
> 
> I did not use the prefix because the existing LED_N_ON/OFF_H/L also do
> not have a prefix. If the prefix is mandatory, I think LED_N_.. should
> also be prefixed, right?

I'd like to seem the prefixed (and assume that Thierry doesn't agree).
IMHO it's good style and even though it yields longer name usually it
yields easier understandable code. (But this seems to be subjective.)

> > > +	val = 0;
> > > +	regmap_read(pca->regmap, LED_N_OFF_L(channel), &val);
> > 
> > I asked in the last round why you initialize val. You answered "just to
> > have it set to 0 in case regmap_read fails / val was not set." I wonder
> > if
> > 
> > 	ret = regmap_read(pca->regmap, LED_N_OFF_L(channel), &val);
> > 	if (!ret)
> > 		/* 

I intended to write something like

		/* initialize val in case reading LED_N_OFF failed */

> > 		val = 0
> > 
> > would be better then and also make the intention obvious.
> 
> I am not sure if that's more clear, but if others find it more obvious
> like this, I can change it.
> 
> > 
> > > +	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,52 @@ 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)
> > >  {
> > > [...]
> > > +	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 (!state->enabled) {
> > > +		pca9685_pwm_set_duty(pca, pwm->hwpwm, 0);
> > >  		return 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);
> > >  
> > > [...]
> > > +		/* Change the chip-wide output frequency */
> > > +		regmap_write(pca->regmap, PCA9685_PRESCALE, prescale);
> > >  
> > > -	regmap_update_bits(pca->regmap, reg, LED_FULL, 0x0);
> > > +		/* Wake the chip up */
> > > +		pca9685_set_sleep_mode(pca, false);
> > > +	}
> > >  
> > > +	duty = PCA9685_COUNTER_RANGE * state->duty_cycle;
> > > +	duty = DIV_ROUND_DOWN_ULL(duty, state->period);
> > 
> > If you round down here you should probably also round down in the
> > calculation of prescale. Also note that you're losing precision by using
> > state->period.
> > 
> > Consider the following request: state->period = 4177921 [ns] +
> > state->duty_cycle = 100000 [ns], then we get
> > PRESCALE = round(25 * state->period / 4096000) - 1 = 25 and so an actual
> > period of 4096000 / 25 * (25 + 1) = 4259840 [ns]. If you now calculate
> > the duty using 4096 * 100000 / 4177920 = 98, this corresponds to an
> > actual duty cycle of 98 * 4259840 / 4096 = 101920 ns while you should
> > actually configure 96 to get 99840 ns.
> > 
> > So in the end I'd like to have the following:
> > 
> > 	PRESCALE = round-down(25 * state->period / 4096000) - 1
> > 
> > (to get the biggest period not bigger than state->period) and
> > 
> > 	duty = round-down(state->duty_cycle * 25 / ((PRESCALE + 1) * 1000))
> > 
> > (to get the biggest duty cycle not bigger than state->duty_cycle). With
> > the example above this yields
> > 
> > 	PRESCALE = 24
> > 	duty = 100
> > 
> > which results in
> > 
> > 	.period = 4096000 / 25 * 25 = 4096000 [ns]
> > 	.duty_cycle = 100000 [ns]
> > 	
> > Now you have a mixture of old and new with no consistent behaviour. So
> > please either stick to the old behaviour or do it right immediately.
> 
> I avoided rounding down the prescale value because the datasheet has an
> example where a round-closest is used, see page 25.

The hardware guy who wrote this data sheet wasn't aware of the rounding
rules for Linux PWM drivers :-)

> With your suggested round-down, the example with frequency of 200 Hz
> would no longer result in 30 but 29 and that contradicts the datasheet.

Well, with PRESCALE = 30 we get a frequency of 196.88 Hz and with
PRESCALE = 29 we get a frequency of 203.45 Hz. So no matter if you pick
29 or 30, you don't get 200 Hz. And which of the two possible values is
the better one depends on the consumer, no matter what rounding
algorithm the data sheet suggests. Also note that the math here contains
surprises you don't expect at first. For example, what PRESCALE value
would you pick to get 284 Hz? [If my mail was a video, I'd suggest to
press Space now to pause and let you think first :-)] The data sheet's
formula suggests:

	round(25 MHz / (4096 * 284)) - 1 = 20

The resulting frequency when picking PRESCALE = 20 is 290.644 Hz (so an
error of 6.644 Hz). If instead you pick PRESCALE = 21 you get 277.433 Hz
(error = 6.567 Hz), so 21 is the better choice.

Exercise for the reader:
 What is the correct formula to really determine the PRESCALE value that
 yields the best approximation (i.e. minimizing
 abs(real_freq - target_freq)) for a given target_freq?

These things don't happen when you round down only.

> So would you rather have me keep the old duty rounding behaviour?
> 
> Meaning: Keep rounding up the duty calculation in apply and use
> round-down in the new .get_state function?

There are two things I want:

 a) To improve consistency among the PWM drivers (and to keep the math
    simple and unsurprising), the pca9685 driver should use round-down
    instead of round-nearest (or whatever mix it is currently using).

 b) .get_state should be the inverse to .apply in the sense that
    applying the result of .get_state is idempotent.

I don't care much how you get there, so it's up to you if you do that in
this patch that converts to .apply, or if you keep the math as is and
then adapt the rounding behaviour in a separate change. But changing the
algorithm in this patch and not getting to the "good" one is ugly, so
please don't do that.

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] 42+ messages in thread

* Re: [PATCH v8 6/8] pwm: pca9685: Support new PWM_USAGE_POWER flag
  2021-04-12 17:11     ` Clemens Gruber
@ 2021-04-13 10:34       ` Uwe Kleine-König
  0 siblings, 0 replies; 42+ messages in thread
From: Uwe Kleine-König @ 2021-04-13 10:34 UTC (permalink / raw)
  To: Clemens Gruber
  Cc: linux-pwm, Thierry Reding, Sven Van Asbroeck, devicetree, linux-kernel

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

Hi Clemens,

On Mon, Apr 12, 2021 at 07:11:58PM +0200, Clemens Gruber wrote:
> On Mon, Apr 12, 2021 at 06:30:45PM +0200, Uwe Kleine-König wrote:
> > On Mon, Apr 12, 2021 at 03:27:43PM +0200, Clemens Gruber wrote:
> > >  static unsigned int pca9685_pwm_get_duty(struct pca9685 *pca, int channel)
> > >  {
> > > -	unsigned int off_h = 0, val = 0;
> > > +	struct pwm_device *pwm = &pca->chip.pwms[channel];
> > > +	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 (!pwm->args.usage_power)
> > > +		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 LED_N_ON is != 0 but usage_power is false, the returned state is
> > bogus.
> 
> If usage_power is false, LED_N_ON is guaranteed to be 0. It is reset to
> 0 in probe and never changed. Or did I miss something?

Ah right, so my concern is only a challenge once the reset in probe goes
away.

> > >  }
> > >  
> > >  #if IS_ENABLED(CONFIG_GPIOLIB)
> > > @@ -439,9 +469,11 @@ 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);
> > >  
> > > -	/* 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 */
> > > @@ -450,6 +482,9 @@ static int pca9685_pwm_probe(struct i2c_client *client,
> > >  	pca->chip.dev = &client->dev;
> > >  	pca->chip.base = -1;
> > >  
> > > +	pca->chip.of_xlate = of_pwm_xlate_with_flags;
> > > +	pca->chip.of_pwm_n_cells = 3;
> > > +
> > 
> > Huh, you're incompatibly changing the device tree binding here.
> 
> No, I don't think so:
> 
> The third cell is optional with of_pwm_xlate_with_flags.
> So previous DTs with pwm-cells = <2> will still work.

I thought that .of_pwm_n_cells was enforced, let me check the code ... I
had in mind that of_pwm_get() enforced that, but I cannot find it, so I
guess you're right and my concern is unjustified.

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] 42+ messages in thread

* Re: [PATCH v8 4/8] dt-bindings: pwm: Support new PWM_USAGE_POWER flag
  2021-04-12 16:46     ` Clemens Gruber
@ 2021-04-13 11:38       ` Uwe Kleine-König
  2021-04-13 11:41         ` Thierry Reding
  0 siblings, 1 reply; 42+ messages in thread
From: Uwe Kleine-König @ 2021-04-13 11:38 UTC (permalink / raw)
  To: Clemens Gruber
  Cc: linux-pwm, Thierry Reding, Sven Van Asbroeck, devicetree,
	linux-kernel, Rob Herring

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

Hello,

On Mon, Apr 12, 2021 at 06:46:51PM +0200, Clemens Gruber wrote:
> On Mon, Apr 12, 2021 at 06:27:23PM +0200, Uwe Kleine-König wrote:
> > On Mon, Apr 12, 2021 at 03:27:41PM +0200, Clemens Gruber wrote:
> > > Add the flag and corresponding documentation for PWM_USAGE_POWER.
> > 
> > My concern here in the previous round was that PWM_USAGE_POWER isn't a
> > name that intuitively suggests its semantic. Do you disagree?
> 
> No. It is more abstract and requires documentation. But I also didn't
> want to waste too much time on discussing names, so I used Thierry's
> suggestion.

If you introduce API thinking about the name before actually introducing
it is a good idea in general. (OK, the name doesn't become part of the
(binary) dt API, but we don't even agree about its semantic here.)

And IMHO a bad name with a good documentation isn't good enough.
Otherwise we can better just agree on using plain numbers in the .dts
files.

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] 42+ messages in thread

* Re: [PATCH v8 4/8] dt-bindings: pwm: Support new PWM_USAGE_POWER flag
  2021-04-13 11:38       ` Uwe Kleine-König
@ 2021-04-13 11:41         ` Thierry Reding
  0 siblings, 0 replies; 42+ messages in thread
From: Thierry Reding @ 2021-04-13 11:41 UTC (permalink / raw)
  To: Uwe Kleine-König
  Cc: Clemens Gruber, linux-pwm, Sven Van Asbroeck, devicetree,
	linux-kernel, Rob Herring

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

On Tue, Apr 13, 2021 at 01:38:05PM +0200, Uwe Kleine-König wrote:
> Hello,
> 
> On Mon, Apr 12, 2021 at 06:46:51PM +0200, Clemens Gruber wrote:
> > On Mon, Apr 12, 2021 at 06:27:23PM +0200, Uwe Kleine-König wrote:
> > > On Mon, Apr 12, 2021 at 03:27:41PM +0200, Clemens Gruber wrote:
> > > > Add the flag and corresponding documentation for PWM_USAGE_POWER.
> > > 
> > > My concern here in the previous round was that PWM_USAGE_POWER isn't a
> > > name that intuitively suggests its semantic. Do you disagree?
> > 
> > No. It is more abstract and requires documentation. But I also didn't
> > want to waste too much time on discussing names, so I used Thierry's
> > suggestion.
> 
> If you introduce API thinking about the name before actually introducing
> it is a good idea in general. (OK, the name doesn't become part of the
> (binary) dt API, but we don't even agree about its semantic here.)
> 
> And IMHO a bad name with a good documentation isn't good enough.
> Otherwise we can better just agree on using plain numbers in the .dts
> files.

Plain numbers or not doesn't change anything. The meaning of the bit has
to be defined. This has nothing to do with the symbolic name at all.

Thierry

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

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

* Re: [PATCH v8 4/8] dt-bindings: pwm: Support new PWM_USAGE_POWER flag
  2021-04-12 16:27   ` Uwe Kleine-König
  2021-04-12 16:46     ` Clemens Gruber
@ 2021-04-13 11:51     ` Thierry Reding
  2021-04-13 17:56       ` Uwe Kleine-König
  1 sibling, 1 reply; 42+ messages in thread
From: Thierry Reding @ 2021-04-13 11:51 UTC (permalink / raw)
  To: Uwe Kleine-König
  Cc: Clemens Gruber, linux-pwm, Sven Van Asbroeck, devicetree,
	linux-kernel, Rob Herring

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

On Mon, Apr 12, 2021 at 06:27:23PM +0200, Uwe Kleine-König wrote:
> On Mon, Apr 12, 2021 at 03:27:41PM +0200, Clemens Gruber wrote:
> > Add the flag and corresponding documentation for PWM_USAGE_POWER.
> 
> My concern here in the previous round was that PWM_USAGE_POWER isn't a
> name that intuitively suggests its semantic. Do you disagree?

I suggested PWM_USAGE_POWER because I think it accurately captures what
we want here.

> > Cc: Rob Herring <robh+dt@kernel.org>
> > Signed-off-by: Clemens Gruber <clemens.gruber@pqgruber.com>
> > ---
> >  Documentation/devicetree/bindings/pwm/pwm.txt | 3 +++
> >  include/dt-bindings/pwm/pwm.h                 | 1 +
> >  2 files changed, 4 insertions(+)
> > 
> > diff --git a/Documentation/devicetree/bindings/pwm/pwm.txt b/Documentation/devicetree/bindings/pwm/pwm.txt
> > index 084886bd721e..fe3a28f887c0 100644
> > --- a/Documentation/devicetree/bindings/pwm/pwm.txt
> > +++ b/Documentation/devicetree/bindings/pwm/pwm.txt
> > @@ -46,6 +46,9 @@ period in nanoseconds.
> >  Optionally, the pwm-specifier can encode a number of flags (defined in
> >  <dt-bindings/pwm/pwm.h>) in a third cell:
> >  - PWM_POLARITY_INVERTED: invert the PWM signal polarity
> > +- PWM_USAGE_POWER: Only care about the power output of the signal. This
> > +  allows drivers (if supported) to optimize the signals, for example to
> > +  improve EMI and reduce current spikes.
> 
> IMHO there are too many open questions about which freedom this gives to
> the lowlevel driver. If the consumer requests .duty_cycle = 25ns +
> .period = 100ns, can the driver provide .duty_cycle = 25s + .period =
> 100s which nominally has the same power output? Let's not introduce more
> ambiguity than there already is.

The freedom given to the driver should be to adjust the signal within
reasonable bounds. Changing the time unit by a factor of 1000000000 is
not within reason, and I doubt anyone would interpret it that way, even
if we didn't document this at all.

To be frank I think that quest of yours to try and rid the PWM API of
all ambiguity is futile. I've been trying to be lenient because you seem
motivated, but I think you're taking this too far. There are always
going to be cases that aren't completely clear-cut and where drivers
need the flexibility to cheat in order to be useful at all. If we get to
a point where everything needs to be 100% accurate, the majority of the
PWM controllers won't be usable at all.

Don't let perfect be the enemy of good.

Thierry

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

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

* Re: [PATCH v8 1/8] pwm: pca9685: Switch to atomic API
  2021-04-12 20:10     ` Uwe Kleine-König
@ 2021-04-13 12:11       ` Clemens Gruber
  2021-04-13 12:17         ` Clemens Gruber
                           ` (2 more replies)
  0 siblings, 3 replies; 42+ messages in thread
From: Clemens Gruber @ 2021-04-13 12:11 UTC (permalink / raw)
  To: Uwe Kleine-König
  Cc: linux-pwm, Thierry Reding, Sven Van Asbroeck, devicetree, linux-kernel

Hi Uwe,

On Mon, Apr 12, 2021 at 10:10:19PM +0200, Uwe Kleine-König wrote:
> Hello Clemens,
> 
> On Mon, Apr 12, 2021 at 06:39:28PM +0200, Clemens Gruber wrote:
> > On Mon, Apr 12, 2021 at 06:18:08PM +0200, Uwe Kleine-König wrote:
> > > On Mon, Apr 12, 2021 at 03:27:38PM +0200, Clemens Gruber wrote:
> > > > 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
> > > > - Previously, only the full ON bit was toggled in GPIO mode (and full
> > > >   OFF cleared if set to high), which could result in both full OFF and
> > > >   full ON not being set and on=0, off=0, which is not allowed according
> > > >   to the datasheet
> > > > - The OFF registers were reset to 0 in probe, which could lead to the
> > > >   forbidden on=0, off=0. Fixed by resetting to POR default (full OFF)
> > > > 
> > > > Signed-off-by: Clemens Gruber <clemens.gruber@pqgruber.com>
> > > > ---
> > > > Changes since v7:
> > > > - Moved check for !state->enabled before prescaler configuration
> > > > - Removed unnecessary cast
> > > > - Use DIV_ROUND_DOWN in .apply
> > > > 
> > > > Changes since v6:
> > > > - Order of a comparison switched for improved readability
> > > > 
> > > > 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 | 259 +++++++++++++-------------------------
> > > >  1 file changed, 89 insertions(+), 170 deletions(-)
> > > > 
> > > > diff --git a/drivers/pwm/pwm-pca9685.c b/drivers/pwm/pwm-pca9685.c
> > > > index 4a55dc18656c..827b57ced3c2 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)))
> > > 
> > > I'd like to see these named PCA9685_REG_ON_H etc.
> > 
> > I did not use the prefix because the existing LED_N_ON/OFF_H/L also do
> > not have a prefix. If the prefix is mandatory, I think LED_N_.. should
> > also be prefixed, right?
> 
> I'd like to seem the prefixed (and assume that Thierry doesn't agree).
> IMHO it's good style and even though it yields longer name usually it
> yields easier understandable code. (But this seems to be subjective.)

I am not sure I want to also rename the existing LED_N_OFF stuff in this
patch. Maybe we can discuss unifying the macros (either with or without
prefix) in a later patch and I keep the REG_ON_ stuff for now without to
match the LED_N_ stuff?

> 
> > > > +	val = 0;
> > > > +	regmap_read(pca->regmap, LED_N_OFF_L(channel), &val);
> > > 
> > > I asked in the last round why you initialize val. You answered "just to
> > > have it set to 0 in case regmap_read fails / val was not set." I wonder
> > > if
> > > 
> > > 	ret = regmap_read(pca->regmap, LED_N_OFF_L(channel), &val);
> > > 	if (!ret)
> > > 		/* 
> 
> I intended to write something like
> 
> 		/* initialize val in case reading LED_N_OFF failed */
> 
> > > 		val = 0
> > > 
> > > would be better then and also make the intention obvious.

Maybe a little bit better.. I can change it.

> > 
> > I am not sure if that's more clear, but if others find it more obvious
> > like this, I can change it.
> > 
> > > 
> > > > +	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,52 @@ 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)
> > > >  {
> > > > [...]
> > > > +	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 (!state->enabled) {
> > > > +		pca9685_pwm_set_duty(pca, pwm->hwpwm, 0);
> > > >  		return 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);
> > > >  
> > > > [...]
> > > > +		/* Change the chip-wide output frequency */
> > > > +		regmap_write(pca->regmap, PCA9685_PRESCALE, prescale);
> > > >  
> > > > -	regmap_update_bits(pca->regmap, reg, LED_FULL, 0x0);
> > > > +		/* Wake the chip up */
> > > > +		pca9685_set_sleep_mode(pca, false);
> > > > +	}
> > > >  
> > > > +	duty = PCA9685_COUNTER_RANGE * state->duty_cycle;
> > > > +	duty = DIV_ROUND_DOWN_ULL(duty, state->period);
> > > 
> > > If you round down here you should probably also round down in the
> > > calculation of prescale. Also note that you're losing precision by using
> > > state->period.
> > > 
> > > Consider the following request: state->period = 4177921 [ns] +
> > > state->duty_cycle = 100000 [ns], then we get
> > > PRESCALE = round(25 * state->period / 4096000) - 1 = 25 and so an actual
> > > period of 4096000 / 25 * (25 + 1) = 4259840 [ns]. If you now calculate
> > > the duty using 4096 * 100000 / 4177920 = 98, this corresponds to an
> > > actual duty cycle of 98 * 4259840 / 4096 = 101920 ns while you should
> > > actually configure 96 to get 99840 ns.
> > > 
> > > So in the end I'd like to have the following:
> > > 
> > > 	PRESCALE = round-down(25 * state->period / 4096000) - 1
> > > 
> > > (to get the biggest period not bigger than state->period) and
> > > 
> > > 	duty = round-down(state->duty_cycle * 25 / ((PRESCALE + 1) * 1000))
> > > 
> > > (to get the biggest duty cycle not bigger than state->duty_cycle). With
> > > the example above this yields
> > > 
> > > 	PRESCALE = 24
> > > 	duty = 100
> > > 
> > > which results in
> > > 
> > > 	.period = 4096000 / 25 * 25 = 4096000 [ns]
> > > 	.duty_cycle = 100000 [ns]
> > > 	
> > > Now you have a mixture of old and new with no consistent behaviour. So
> > > please either stick to the old behaviour or do it right immediately.
> > 
> > I avoided rounding down the prescale value because the datasheet has an
> > example where a round-closest is used, see page 25.
> 
> The hardware guy who wrote this data sheet wasn't aware of the rounding
> rules for Linux PWM drivers :-)
> 
> > With your suggested round-down, the example with frequency of 200 Hz
> > would no longer result in 30 but 29 and that contradicts the datasheet.
> 
> Well, with PRESCALE = 30 we get a frequency of 196.88 Hz and with
> PRESCALE = 29 we get a frequency of 203.45 Hz. So no matter if you pick
> 29 or 30, you don't get 200 Hz. And which of the two possible values is
> the better one depends on the consumer, no matter what rounding
> algorithm the data sheet suggests. Also note that the math here contains
> surprises you don't expect at first. For example, what PRESCALE value
> would you pick to get 284 Hz? [If my mail was a video, I'd suggest to
> press Space now to pause and let you think first :-)] The data sheet's
> formula suggests:
> 
> 	round(25 MHz / (4096 * 284)) - 1 = 20
> 
> The resulting frequency when picking PRESCALE = 20 is 290.644 Hz (so an
> error of 6.644 Hz). If instead you pick PRESCALE = 21 you get 277.433 Hz
> (error = 6.567 Hz), so 21 is the better choice.
> 
> Exercise for the reader:
>  What is the correct formula to really determine the PRESCALE value that
>  yields the best approximation (i.e. minimizing
>  abs(real_freq - target_freq)) for a given target_freq?
> 
> These things don't happen when you round down only.

Sure, but it might also be counterintuitive that the Linux driver does
not use the same formula as the datasheet. And when using 200 Hz, 29 is
a little closer than 30.
I once measured the actual frequency and the internal oscillator is not
very accurate, so even if you think you should get 196.88 Hz, the actual
frequency measured with a decent scope is about 206 Hz and varies from
chip to chip (~ 205-207 Hz).

> 
> > So would you rather have me keep the old duty rounding behaviour?
> > 
> > Meaning: Keep rounding up the duty calculation in apply and use
> > round-down in the new .get_state function?
> 
> There are two things I want:
> 
>  a) To improve consistency among the PWM drivers (and to keep the math
>     simple and unsurprising), the pca9685 driver should use round-down
>     instead of round-nearest (or whatever mix it is currently using).
> 
>  b) .get_state should be the inverse to .apply in the sense that
>     applying the result of .get_state is idempotent.
> 
> I don't care much how you get there, so it's up to you if you do that in
> this patch that converts to .apply, or if you keep the math as is and
> then adapt the rounding behaviour in a separate change. But changing the
> algorithm in this patch and not getting to the "good" one is ugly, so
> please don't do that.

OK, then I think the best option is to keep the math as it was before
and if we want to change the rounding behaviour we do this in a separate
patch in the future. Then we can continue the discussion wether changing
the prescaler formula to round-down even though the datasheet does it
otherwise is the way to go.

Thanks,
Clemens

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

* Re: [PATCH v8 1/8] pwm: pca9685: Switch to atomic API
  2021-04-13 12:11       ` Clemens Gruber
@ 2021-04-13 12:17         ` Clemens Gruber
  2021-04-13 12:37         ` Thierry Reding
  2021-04-13 19:38         ` Uwe Kleine-König
  2 siblings, 0 replies; 42+ messages in thread
From: Clemens Gruber @ 2021-04-13 12:17 UTC (permalink / raw)
  To: Uwe Kleine-König
  Cc: linux-pwm, Thierry Reding, Sven Van Asbroeck, devicetree, linux-kernel

On Tue, Apr 13, 2021 at 02:11:38PM +0200, Clemens Gruber wrote:
> Hi Uwe,
> 
> On Mon, Apr 12, 2021 at 10:10:19PM +0200, Uwe Kleine-König wrote:
> > Hello Clemens,
> > 
> > On Mon, Apr 12, 2021 at 06:39:28PM +0200, Clemens Gruber wrote:
> > > On Mon, Apr 12, 2021 at 06:18:08PM +0200, Uwe Kleine-König wrote:
> > > > On Mon, Apr 12, 2021 at 03:27:38PM +0200, Clemens Gruber wrote:
> > > > > 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
> > > > > - Previously, only the full ON bit was toggled in GPIO mode (and full
> > > > >   OFF cleared if set to high), which could result in both full OFF and
> > > > >   full ON not being set and on=0, off=0, which is not allowed according
> > > > >   to the datasheet
> > > > > - The OFF registers were reset to 0 in probe, which could lead to the
> > > > >   forbidden on=0, off=0. Fixed by resetting to POR default (full OFF)
> > > > > 
> > > > > Signed-off-by: Clemens Gruber <clemens.gruber@pqgruber.com>
> > > > > ---
> > > > > Changes since v7:
> > > > > - Moved check for !state->enabled before prescaler configuration
> > > > > - Removed unnecessary cast
> > > > > - Use DIV_ROUND_DOWN in .apply
> > > > > 
> > > > > Changes since v6:
> > > > > - Order of a comparison switched for improved readability
> > > > > 
> > > > > 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 | 259 +++++++++++++-------------------------
> > > > >  1 file changed, 89 insertions(+), 170 deletions(-)
> > > > > 
> > > > > diff --git a/drivers/pwm/pwm-pca9685.c b/drivers/pwm/pwm-pca9685.c
> > > > > index 4a55dc18656c..827b57ced3c2 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)))
> > > > 
> > > > I'd like to see these named PCA9685_REG_ON_H etc.
> > > 
> > > I did not use the prefix because the existing LED_N_ON/OFF_H/L also do
> > > not have a prefix. If the prefix is mandatory, I think LED_N_.. should
> > > also be prefixed, right?
> > 
> > I'd like to seem the prefixed (and assume that Thierry doesn't agree).
> > IMHO it's good style and even though it yields longer name usually it
> > yields easier understandable code. (But this seems to be subjective.)
> 
> I am not sure I want to also rename the existing LED_N_OFF stuff in this
> patch. Maybe we can discuss unifying the macros (either with or without
> prefix) in a later patch and I keep the REG_ON_ stuff for now without to
> match the LED_N_ stuff?
> 
> > 
> > > > > +	val = 0;
> > > > > +	regmap_read(pca->regmap, LED_N_OFF_L(channel), &val);
> > > > 
> > > > I asked in the last round why you initialize val. You answered "just to
> > > > have it set to 0 in case regmap_read fails / val was not set." I wonder
> > > > if
> > > > 
> > > > 	ret = regmap_read(pca->regmap, LED_N_OFF_L(channel), &val);
> > > > 	if (!ret)
> > > > 		/* 
> > 
> > I intended to write something like
> > 
> > 		/* initialize val in case reading LED_N_OFF failed */
> > 
> > > > 		val = 0
> > > > 
> > > > would be better then and also make the intention obvious.
> 
> Maybe a little bit better.. I can change it.
> 
> > > 
> > > I am not sure if that's more clear, but if others find it more obvious
> > > like this, I can change it.
> > > 
> > > > 
> > > > > +	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,52 @@ 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)
> > > > >  {
> > > > > [...]
> > > > > +	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 (!state->enabled) {
> > > > > +		pca9685_pwm_set_duty(pca, pwm->hwpwm, 0);
> > > > >  		return 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);
> > > > >  
> > > > > [...]
> > > > > +		/* Change the chip-wide output frequency */
> > > > > +		regmap_write(pca->regmap, PCA9685_PRESCALE, prescale);
> > > > >  
> > > > > -	regmap_update_bits(pca->regmap, reg, LED_FULL, 0x0);
> > > > > +		/* Wake the chip up */
> > > > > +		pca9685_set_sleep_mode(pca, false);
> > > > > +	}
> > > > >  
> > > > > +	duty = PCA9685_COUNTER_RANGE * state->duty_cycle;
> > > > > +	duty = DIV_ROUND_DOWN_ULL(duty, state->period);
> > > > 
> > > > If you round down here you should probably also round down in the
> > > > calculation of prescale. Also note that you're losing precision by using
> > > > state->period.
> > > > 
> > > > Consider the following request: state->period = 4177921 [ns] +
> > > > state->duty_cycle = 100000 [ns], then we get
> > > > PRESCALE = round(25 * state->period / 4096000) - 1 = 25 and so an actual
> > > > period of 4096000 / 25 * (25 + 1) = 4259840 [ns]. If you now calculate
> > > > the duty using 4096 * 100000 / 4177920 = 98, this corresponds to an
> > > > actual duty cycle of 98 * 4259840 / 4096 = 101920 ns while you should
> > > > actually configure 96 to get 99840 ns.
> > > > 
> > > > So in the end I'd like to have the following:
> > > > 
> > > > 	PRESCALE = round-down(25 * state->period / 4096000) - 1
> > > > 
> > > > (to get the biggest period not bigger than state->period) and
> > > > 
> > > > 	duty = round-down(state->duty_cycle * 25 / ((PRESCALE + 1) * 1000))
> > > > 
> > > > (to get the biggest duty cycle not bigger than state->duty_cycle). With
> > > > the example above this yields
> > > > 
> > > > 	PRESCALE = 24
> > > > 	duty = 100
> > > > 
> > > > which results in
> > > > 
> > > > 	.period = 4096000 / 25 * 25 = 4096000 [ns]
> > > > 	.duty_cycle = 100000 [ns]
> > > > 	
> > > > Now you have a mixture of old and new with no consistent behaviour. So
> > > > please either stick to the old behaviour or do it right immediately.
> > > 
> > > I avoided rounding down the prescale value because the datasheet has an
> > > example where a round-closest is used, see page 25.
> > 
> > The hardware guy who wrote this data sheet wasn't aware of the rounding
> > rules for Linux PWM drivers :-)
> > 
> > > With your suggested round-down, the example with frequency of 200 Hz
> > > would no longer result in 30 but 29 and that contradicts the datasheet.
> > 
> > Well, with PRESCALE = 30 we get a frequency of 196.88 Hz and with
> > PRESCALE = 29 we get a frequency of 203.45 Hz. So no matter if you pick
> > 29 or 30, you don't get 200 Hz. And which of the two possible values is
> > the better one depends on the consumer, no matter what rounding
> > algorithm the data sheet suggests. Also note that the math here contains
> > surprises you don't expect at first. For example, what PRESCALE value
> > would you pick to get 284 Hz? [If my mail was a video, I'd suggest to
> > press Space now to pause and let you think first :-)] The data sheet's
> > formula suggests:
> > 
> > 	round(25 MHz / (4096 * 284)) - 1 = 20
> > 
> > The resulting frequency when picking PRESCALE = 20 is 290.644 Hz (so an
> > error of 6.644 Hz). If instead you pick PRESCALE = 21 you get 277.433 Hz
> > (error = 6.567 Hz), so 21 is the better choice.
> > 
> > Exercise for the reader:
> >  What is the correct formula to really determine the PRESCALE value that
> >  yields the best approximation (i.e. minimizing
> >  abs(real_freq - target_freq)) for a given target_freq?
> > 
> > These things don't happen when you round down only.
> 
> Sure, but it might also be counterintuitive that the Linux driver does
> not use the same formula as the datasheet. And when using 200 Hz, 29 is
> a little closer than 30.

Sorry, I meant: 30 (196.88 Hz) is a little closer than 29 (203.45 Hz).

> I once measured the actual frequency and the internal oscillator is not
> very accurate, so even if you think you should get 196.88 Hz, the actual
> frequency measured with a decent scope is about 206 Hz and varies from
> chip to chip (~ 205-207 Hz).
> 
> > 
> > > So would you rather have me keep the old duty rounding behaviour?
> > > 
> > > Meaning: Keep rounding up the duty calculation in apply and use
> > > round-down in the new .get_state function?
> > 
> > There are two things I want:
> > 
> >  a) To improve consistency among the PWM drivers (and to keep the math
> >     simple and unsurprising), the pca9685 driver should use round-down
> >     instead of round-nearest (or whatever mix it is currently using).
> > 
> >  b) .get_state should be the inverse to .apply in the sense that
> >     applying the result of .get_state is idempotent.
> > 
> > I don't care much how you get there, so it's up to you if you do that in
> > this patch that converts to .apply, or if you keep the math as is and
> > then adapt the rounding behaviour in a separate change. But changing the
> > algorithm in this patch and not getting to the "good" one is ugly, so
> > please don't do that.
> 
> OK, then I think the best option is to keep the math as it was before
> and if we want to change the rounding behaviour we do this in a separate
> patch in the future. Then we can continue the discussion wether changing
> the prescaler formula to round-down even though the datasheet does it
> otherwise is the way to go.
> 
> Thanks,
> Clemens

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

* Re: [PATCH v8 1/8] pwm: pca9685: Switch to atomic API
  2021-04-13 12:11       ` Clemens Gruber
  2021-04-13 12:17         ` Clemens Gruber
@ 2021-04-13 12:37         ` Thierry Reding
  2021-04-13 13:06           ` Clemens Gruber
  2021-04-13 19:38         ` Uwe Kleine-König
  2 siblings, 1 reply; 42+ messages in thread
From: Thierry Reding @ 2021-04-13 12:37 UTC (permalink / raw)
  To: Clemens Gruber
  Cc: Uwe Kleine-König, linux-pwm, Sven Van Asbroeck, devicetree,
	linux-kernel

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

On Tue, Apr 13, 2021 at 02:11:38PM +0200, Clemens Gruber wrote:
> Hi Uwe,
> 
> On Mon, Apr 12, 2021 at 10:10:19PM +0200, Uwe Kleine-König wrote:
> > Hello Clemens,
> > 
> > On Mon, Apr 12, 2021 at 06:39:28PM +0200, Clemens Gruber wrote:
> > > On Mon, Apr 12, 2021 at 06:18:08PM +0200, Uwe Kleine-König wrote:
> > > > On Mon, Apr 12, 2021 at 03:27:38PM +0200, Clemens Gruber wrote:
> > > > > 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
> > > > > - Previously, only the full ON bit was toggled in GPIO mode (and full
> > > > >   OFF cleared if set to high), which could result in both full OFF and
> > > > >   full ON not being set and on=0, off=0, which is not allowed according
> > > > >   to the datasheet
> > > > > - The OFF registers were reset to 0 in probe, which could lead to the
> > > > >   forbidden on=0, off=0. Fixed by resetting to POR default (full OFF)
> > > > > 
> > > > > Signed-off-by: Clemens Gruber <clemens.gruber@pqgruber.com>
> > > > > ---
> > > > > Changes since v7:
> > > > > - Moved check for !state->enabled before prescaler configuration
> > > > > - Removed unnecessary cast
> > > > > - Use DIV_ROUND_DOWN in .apply
> > > > > 
> > > > > Changes since v6:
> > > > > - Order of a comparison switched for improved readability
> > > > > 
> > > > > 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 | 259 +++++++++++++-------------------------
> > > > >  1 file changed, 89 insertions(+), 170 deletions(-)
> > > > > 
> > > > > diff --git a/drivers/pwm/pwm-pca9685.c b/drivers/pwm/pwm-pca9685.c
> > > > > index 4a55dc18656c..827b57ced3c2 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)))
> > > > 
> > > > I'd like to see these named PCA9685_REG_ON_H etc.
> > > 
> > > I did not use the prefix because the existing LED_N_ON/OFF_H/L also do
> > > not have a prefix. If the prefix is mandatory, I think LED_N_.. should
> > > also be prefixed, right?
> > 
> > I'd like to seem the prefixed (and assume that Thierry doesn't agree).
> > IMHO it's good style and even though it yields longer name usually it
> > yields easier understandable code. (But this seems to be subjective.)
> 
> I am not sure I want to also rename the existing LED_N_OFF stuff in this
> patch. Maybe we can discuss unifying the macros (either with or without
> prefix) in a later patch and I keep the REG_ON_ stuff for now without to
> match the LED_N_ stuff?

Uwe guessed right, I don't think these need the prefix. There's very
little potential for name clashes and this driver is already called
PCA9685, so adding the prefix everywhere can be annoying, especially
if that then causes you to need line breaks, etc.

For things like function names I usually prefer having a consistent
prefix because it makes it much easier to decipher stack traces. Not so
with the defines here.

But I also don't feel strongly, so either way is fine. If you decide to
go with the prefix, making it consistent throughout the file in a
separate patch would be preferable, though.

> 
> > 
> > > > > +	val = 0;
> > > > > +	regmap_read(pca->regmap, LED_N_OFF_L(channel), &val);
> > > > 
> > > > I asked in the last round why you initialize val. You answered "just to
> > > > have it set to 0 in case regmap_read fails / val was not set." I wonder
> > > > if
> > > > 
> > > > 	ret = regmap_read(pca->regmap, LED_N_OFF_L(channel), &val);
> > > > 	if (!ret)
> > > > 		/* 
> > 
> > I intended to write something like
> > 
> > 		/* initialize val in case reading LED_N_OFF failed */
> > 
> > > > 		val = 0
> > > > 
> > > > would be better then and also make the intention obvious.
> 
> Maybe a little bit better.. I can change it.
> 
> > > 
> > > I am not sure if that's more clear, but if others find it more obvious
> > > like this, I can change it.
> > > 
> > > > 
> > > > > +	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,52 @@ 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)
> > > > >  {
> > > > > [...]
> > > > > +	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 (!state->enabled) {
> > > > > +		pca9685_pwm_set_duty(pca, pwm->hwpwm, 0);
> > > > >  		return 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);
> > > > >  
> > > > > [...]
> > > > > +		/* Change the chip-wide output frequency */
> > > > > +		regmap_write(pca->regmap, PCA9685_PRESCALE, prescale);
> > > > >  
> > > > > -	regmap_update_bits(pca->regmap, reg, LED_FULL, 0x0);
> > > > > +		/* Wake the chip up */
> > > > > +		pca9685_set_sleep_mode(pca, false);
> > > > > +	}
> > > > >  
> > > > > +	duty = PCA9685_COUNTER_RANGE * state->duty_cycle;
> > > > > +	duty = DIV_ROUND_DOWN_ULL(duty, state->period);
> > > > 
> > > > If you round down here you should probably also round down in the
> > > > calculation of prescale. Also note that you're losing precision by using
> > > > state->period.
> > > > 
> > > > Consider the following request: state->period = 4177921 [ns] +
> > > > state->duty_cycle = 100000 [ns], then we get
> > > > PRESCALE = round(25 * state->period / 4096000) - 1 = 25 and so an actual
> > > > period of 4096000 / 25 * (25 + 1) = 4259840 [ns]. If you now calculate
> > > > the duty using 4096 * 100000 / 4177920 = 98, this corresponds to an
> > > > actual duty cycle of 98 * 4259840 / 4096 = 101920 ns while you should
> > > > actually configure 96 to get 99840 ns.
> > > > 
> > > > So in the end I'd like to have the following:
> > > > 
> > > > 	PRESCALE = round-down(25 * state->period / 4096000) - 1
> > > > 
> > > > (to get the biggest period not bigger than state->period) and
> > > > 
> > > > 	duty = round-down(state->duty_cycle * 25 / ((PRESCALE + 1) * 1000))
> > > > 
> > > > (to get the biggest duty cycle not bigger than state->duty_cycle). With
> > > > the example above this yields
> > > > 
> > > > 	PRESCALE = 24
> > > > 	duty = 100
> > > > 
> > > > which results in
> > > > 
> > > > 	.period = 4096000 / 25 * 25 = 4096000 [ns]
> > > > 	.duty_cycle = 100000 [ns]
> > > > 	
> > > > Now you have a mixture of old and new with no consistent behaviour. So
> > > > please either stick to the old behaviour or do it right immediately.
> > > 
> > > I avoided rounding down the prescale value because the datasheet has an
> > > example where a round-closest is used, see page 25.
> > 
> > The hardware guy who wrote this data sheet wasn't aware of the rounding
> > rules for Linux PWM drivers :-)
> > 
> > > With your suggested round-down, the example with frequency of 200 Hz
> > > would no longer result in 30 but 29 and that contradicts the datasheet.
> > 
> > Well, with PRESCALE = 30 we get a frequency of 196.88 Hz and with
> > PRESCALE = 29 we get a frequency of 203.45 Hz. So no matter if you pick
> > 29 or 30, you don't get 200 Hz. And which of the two possible values is
> > the better one depends on the consumer, no matter what rounding
> > algorithm the data sheet suggests. Also note that the math here contains
> > surprises you don't expect at first. For example, what PRESCALE value
> > would you pick to get 284 Hz? [If my mail was a video, I'd suggest to
> > press Space now to pause and let you think first :-)] The data sheet's
> > formula suggests:
> > 
> > 	round(25 MHz / (4096 * 284)) - 1 = 20
> > 
> > The resulting frequency when picking PRESCALE = 20 is 290.644 Hz (so an
> > error of 6.644 Hz). If instead you pick PRESCALE = 21 you get 277.433 Hz
> > (error = 6.567 Hz), so 21 is the better choice.
> > 
> > Exercise for the reader:
> >  What is the correct formula to really determine the PRESCALE value that
> >  yields the best approximation (i.e. minimizing
> >  abs(real_freq - target_freq)) for a given target_freq?
> > 
> > These things don't happen when you round down only.
> 
> Sure, but it might also be counterintuitive that the Linux driver does
> not use the same formula as the datasheet. And when using 200 Hz, 29 is
> a little closer than 30.
> I once measured the actual frequency and the internal oscillator is not
> very accurate, so even if you think you should get 196.88 Hz, the actual
> frequency measured with a decent scope is about 206 Hz and varies from
> chip to chip (~ 205-207 Hz).
> 
> > 
> > > So would you rather have me keep the old duty rounding behaviour?
> > > 
> > > Meaning: Keep rounding up the duty calculation in apply and use
> > > round-down in the new .get_state function?
> > 
> > There are two things I want:
> > 
> >  a) To improve consistency among the PWM drivers (and to keep the math
> >     simple and unsurprising), the pca9685 driver should use round-down
> >     instead of round-nearest (or whatever mix it is currently using).
> > 
> >  b) .get_state should be the inverse to .apply in the sense that
> >     applying the result of .get_state is idempotent.
> > 
> > I don't care much how you get there, so it's up to you if you do that in
> > this patch that converts to .apply, or if you keep the math as is and
> > then adapt the rounding behaviour in a separate change. But changing the
> > algorithm in this patch and not getting to the "good" one is ugly, so
> > please don't do that.
> 
> OK, then I think the best option is to keep the math as it was before
> and if we want to change the rounding behaviour we do this in a separate
> patch in the future. Then we can continue the discussion wether changing
> the prescaler formula to round-down even though the datasheet does it
> otherwise is the way to go.

I agree, keeping the existing behaviour is preferable. The series
already changes plenty as-is, and rolling the rounding change into it
may make it more difficult to revert/fix later on.

We can always consider a follow-up patch to change it.

Thierry

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

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

* Re: [PATCH v8 1/8] pwm: pca9685: Switch to atomic API
  2021-04-13 12:37         ` Thierry Reding
@ 2021-04-13 13:06           ` Clemens Gruber
  0 siblings, 0 replies; 42+ messages in thread
From: Clemens Gruber @ 2021-04-13 13:06 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Uwe Kleine-König, linux-pwm, Sven Van Asbroeck, devicetree,
	linux-kernel

On Tue, Apr 13, 2021 at 02:37:50PM +0200, Thierry Reding wrote:
> On Tue, Apr 13, 2021 at 02:11:38PM +0200, Clemens Gruber wrote:
> > Hi Uwe,
> > 
> > On Mon, Apr 12, 2021 at 10:10:19PM +0200, Uwe Kleine-König wrote:
> > > Hello Clemens,
> > > 
> > > On Mon, Apr 12, 2021 at 06:39:28PM +0200, Clemens Gruber wrote:
> > > > On Mon, Apr 12, 2021 at 06:18:08PM +0200, Uwe Kleine-König wrote:
> > > > > On Mon, Apr 12, 2021 at 03:27:38PM +0200, Clemens Gruber wrote:
> > > > > > 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
> > > > > > - Previously, only the full ON bit was toggled in GPIO mode (and full
> > > > > >   OFF cleared if set to high), which could result in both full OFF and
> > > > > >   full ON not being set and on=0, off=0, which is not allowed according
> > > > > >   to the datasheet
> > > > > > - The OFF registers were reset to 0 in probe, which could lead to the
> > > > > >   forbidden on=0, off=0. Fixed by resetting to POR default (full OFF)
> > > > > > 
> > > > > > Signed-off-by: Clemens Gruber <clemens.gruber@pqgruber.com>
> > > > > > ---
> > > > > > Changes since v7:
> > > > > > - Moved check for !state->enabled before prescaler configuration
> > > > > > - Removed unnecessary cast
> > > > > > - Use DIV_ROUND_DOWN in .apply
> > > > > > 
> > > > > > Changes since v6:
> > > > > > - Order of a comparison switched for improved readability
> > > > > > 
> > > > > > 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 | 259 +++++++++++++-------------------------
> > > > > >  1 file changed, 89 insertions(+), 170 deletions(-)
> > > > > > 
> > > > > > diff --git a/drivers/pwm/pwm-pca9685.c b/drivers/pwm/pwm-pca9685.c
> > > > > > index 4a55dc18656c..827b57ced3c2 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)))
> > > > > 
> > > > > I'd like to see these named PCA9685_REG_ON_H etc.
> > > > 
> > > > I did not use the prefix because the existing LED_N_ON/OFF_H/L also do
> > > > not have a prefix. If the prefix is mandatory, I think LED_N_.. should
> > > > also be prefixed, right?
> > > 
> > > I'd like to seem the prefixed (and assume that Thierry doesn't agree).
> > > IMHO it's good style and even though it yields longer name usually it
> > > yields easier understandable code. (But this seems to be subjective.)
> > 
> > I am not sure I want to also rename the existing LED_N_OFF stuff in this
> > patch. Maybe we can discuss unifying the macros (either with or without
> > prefix) in a later patch and I keep the REG_ON_ stuff for now without to
> > match the LED_N_ stuff?
> 
> Uwe guessed right, I don't think these need the prefix. There's very
> little potential for name clashes and this driver is already called
> PCA9685, so adding the prefix everywhere can be annoying, especially
> if that then causes you to need line breaks, etc.
> 
> For things like function names I usually prefer having a consistent
> prefix because it makes it much easier to decipher stack traces. Not so
> with the defines here.
> 
> But I also don't feel strongly, so either way is fine. If you decide to
> go with the prefix, making it consistent throughout the file in a
> separate patch would be preferable, though.

OK. I will keep them without a prefix for now.

> 
> > 
> > > 
> > > > > > +	val = 0;
> > > > > > +	regmap_read(pca->regmap, LED_N_OFF_L(channel), &val);
> > > > > 
> > > > > I asked in the last round why you initialize val. You answered "just to
> > > > > have it set to 0 in case regmap_read fails / val was not set." I wonder
> > > > > if
> > > > > 
> > > > > 	ret = regmap_read(pca->regmap, LED_N_OFF_L(channel), &val);
> > > > > 	if (!ret)
> > > > > 		/* 
> > > 
> > > I intended to write something like
> > > 
> > > 		/* initialize val in case reading LED_N_OFF failed */
> > > 
> > > > > 		val = 0
> > > > > 
> > > > > would be better then and also make the intention obvious.
> > 
> > Maybe a little bit better.. I can change it.
> > 
> > > > 
> > > > I am not sure if that's more clear, but if others find it more obvious
> > > > like this, I can change it.
> > > > 
> > > > > 
> > > > > > +	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,52 @@ 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)
> > > > > >  {
> > > > > > [...]
> > > > > > +	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 (!state->enabled) {
> > > > > > +		pca9685_pwm_set_duty(pca, pwm->hwpwm, 0);
> > > > > >  		return 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);
> > > > > >  
> > > > > > [...]
> > > > > > +		/* Change the chip-wide output frequency */
> > > > > > +		regmap_write(pca->regmap, PCA9685_PRESCALE, prescale);
> > > > > >  
> > > > > > -	regmap_update_bits(pca->regmap, reg, LED_FULL, 0x0);
> > > > > > +		/* Wake the chip up */
> > > > > > +		pca9685_set_sleep_mode(pca, false);
> > > > > > +	}
> > > > > >  
> > > > > > +	duty = PCA9685_COUNTER_RANGE * state->duty_cycle;
> > > > > > +	duty = DIV_ROUND_DOWN_ULL(duty, state->period);
> > > > > 
> > > > > If you round down here you should probably also round down in the
> > > > > calculation of prescale. Also note that you're losing precision by using
> > > > > state->period.
> > > > > 
> > > > > Consider the following request: state->period = 4177921 [ns] +
> > > > > state->duty_cycle = 100000 [ns], then we get
> > > > > PRESCALE = round(25 * state->period / 4096000) - 1 = 25 and so an actual
> > > > > period of 4096000 / 25 * (25 + 1) = 4259840 [ns]. If you now calculate
> > > > > the duty using 4096 * 100000 / 4177920 = 98, this corresponds to an
> > > > > actual duty cycle of 98 * 4259840 / 4096 = 101920 ns while you should
> > > > > actually configure 96 to get 99840 ns.
> > > > > 
> > > > > So in the end I'd like to have the following:
> > > > > 
> > > > > 	PRESCALE = round-down(25 * state->period / 4096000) - 1
> > > > > 
> > > > > (to get the biggest period not bigger than state->period) and
> > > > > 
> > > > > 	duty = round-down(state->duty_cycle * 25 / ((PRESCALE + 1) * 1000))
> > > > > 
> > > > > (to get the biggest duty cycle not bigger than state->duty_cycle). With
> > > > > the example above this yields
> > > > > 
> > > > > 	PRESCALE = 24
> > > > > 	duty = 100
> > > > > 
> > > > > which results in
> > > > > 
> > > > > 	.period = 4096000 / 25 * 25 = 4096000 [ns]
> > > > > 	.duty_cycle = 100000 [ns]
> > > > > 	
> > > > > Now you have a mixture of old and new with no consistent behaviour. So
> > > > > please either stick to the old behaviour or do it right immediately.
> > > > 
> > > > I avoided rounding down the prescale value because the datasheet has an
> > > > example where a round-closest is used, see page 25.
> > > 
> > > The hardware guy who wrote this data sheet wasn't aware of the rounding
> > > rules for Linux PWM drivers :-)
> > > 
> > > > With your suggested round-down, the example with frequency of 200 Hz
> > > > would no longer result in 30 but 29 and that contradicts the datasheet.
> > > 
> > > Well, with PRESCALE = 30 we get a frequency of 196.88 Hz and with
> > > PRESCALE = 29 we get a frequency of 203.45 Hz. So no matter if you pick
> > > 29 or 30, you don't get 200 Hz. And which of the two possible values is
> > > the better one depends on the consumer, no matter what rounding
> > > algorithm the data sheet suggests. Also note that the math here contains
> > > surprises you don't expect at first. For example, what PRESCALE value
> > > would you pick to get 284 Hz? [If my mail was a video, I'd suggest to
> > > press Space now to pause and let you think first :-)] The data sheet's
> > > formula suggests:
> > > 
> > > 	round(25 MHz / (4096 * 284)) - 1 = 20
> > > 
> > > The resulting frequency when picking PRESCALE = 20 is 290.644 Hz (so an
> > > error of 6.644 Hz). If instead you pick PRESCALE = 21 you get 277.433 Hz
> > > (error = 6.567 Hz), so 21 is the better choice.
> > > 
> > > Exercise for the reader:
> > >  What is the correct formula to really determine the PRESCALE value that
> > >  yields the best approximation (i.e. minimizing
> > >  abs(real_freq - target_freq)) for a given target_freq?
> > > 
> > > These things don't happen when you round down only.
> > 
> > Sure, but it might also be counterintuitive that the Linux driver does
> > not use the same formula as the datasheet. And when using 200 Hz, 29 is
> > a little closer than 30.
> > I once measured the actual frequency and the internal oscillator is not
> > very accurate, so even if you think you should get 196.88 Hz, the actual
> > frequency measured with a decent scope is about 206 Hz and varies from
> > chip to chip (~ 205-207 Hz).
> > 
> > > 
> > > > So would you rather have me keep the old duty rounding behaviour?
> > > > 
> > > > Meaning: Keep rounding up the duty calculation in apply and use
> > > > round-down in the new .get_state function?
> > > 
> > > There are two things I want:
> > > 
> > >  a) To improve consistency among the PWM drivers (and to keep the math
> > >     simple and unsurprising), the pca9685 driver should use round-down
> > >     instead of round-nearest (or whatever mix it is currently using).
> > > 
> > >  b) .get_state should be the inverse to .apply in the sense that
> > >     applying the result of .get_state is idempotent.
> > > 
> > > I don't care much how you get there, so it's up to you if you do that in
> > > this patch that converts to .apply, or if you keep the math as is and
> > > then adapt the rounding behaviour in a separate change. But changing the
> > > algorithm in this patch and not getting to the "good" one is ugly, so
> > > please don't do that.
> > 
> > OK, then I think the best option is to keep the math as it was before
> > and if we want to change the rounding behaviour we do this in a separate
> > patch in the future. Then we can continue the discussion wether changing
> > the prescaler formula to round-down even though the datasheet does it
> > otherwise is the way to go.
> 
> I agree, keeping the existing behaviour is preferable. The series
> already changes plenty as-is, and rolling the rounding change into it
> may make it more difficult to revert/fix later on.
> 
> We can always consider a follow-up patch to change it.

Sounds good.

I will keep the math as-is and we can improve the rounding and avoid the
loss of precision in a future series.

Thanks,
Clemens

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

* Re: [PATCH v8 4/8] dt-bindings: pwm: Support new PWM_USAGE_POWER flag
  2021-04-13 11:51     ` Thierry Reding
@ 2021-04-13 17:56       ` Uwe Kleine-König
  2021-04-15 16:27         ` Thierry Reding
  0 siblings, 1 reply; 42+ messages in thread
From: Uwe Kleine-König @ 2021-04-13 17:56 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Clemens Gruber, linux-pwm, Sven Van Asbroeck, devicetree,
	linux-kernel, Rob Herring

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

On Tue, Apr 13, 2021 at 01:51:15PM +0200, Thierry Reding wrote:
> On Mon, Apr 12, 2021 at 06:27:23PM +0200, Uwe Kleine-König wrote:
> > On Mon, Apr 12, 2021 at 03:27:41PM +0200, Clemens Gruber wrote:
> > > Add the flag and corresponding documentation for PWM_USAGE_POWER.
> > 
> > My concern here in the previous round was that PWM_USAGE_POWER isn't a
> > name that intuitively suggests its semantic. Do you disagree?
> 
> I suggested PWM_USAGE_POWER because I think it accurately captures what
> we want here.
> 
> > > Cc: Rob Herring <robh+dt@kernel.org>
> > > Signed-off-by: Clemens Gruber <clemens.gruber@pqgruber.com>
> > > ---
> > >  Documentation/devicetree/bindings/pwm/pwm.txt | 3 +++
> > >  include/dt-bindings/pwm/pwm.h                 | 1 +
> > >  2 files changed, 4 insertions(+)
> > > 
> > > diff --git a/Documentation/devicetree/bindings/pwm/pwm.txt b/Documentation/devicetree/bindings/pwm/pwm.txt
> > > index 084886bd721e..fe3a28f887c0 100644
> > > --- a/Documentation/devicetree/bindings/pwm/pwm.txt
> > > +++ b/Documentation/devicetree/bindings/pwm/pwm.txt
> > > @@ -46,6 +46,9 @@ period in nanoseconds.
> > >  Optionally, the pwm-specifier can encode a number of flags (defined in
> > >  <dt-bindings/pwm/pwm.h>) in a third cell:
> > >  - PWM_POLARITY_INVERTED: invert the PWM signal polarity
> > > +- PWM_USAGE_POWER: Only care about the power output of the signal. This
> > > +  allows drivers (if supported) to optimize the signals, for example to
> > > +  improve EMI and reduce current spikes.
> > 
> > IMHO there are too many open questions about which freedom this gives to
> > the lowlevel driver. If the consumer requests .duty_cycle = 25ns +
> > .period = 100ns, can the driver provide .duty_cycle = 25s + .period =
> > 100s which nominally has the same power output? Let's not introduce more
> > ambiguity than there already is.
> 
> The freedom given to the driver should be to adjust the signal within
> reasonable bounds. Changing the time unit by a factor of 1000000000 is
> not within reason, and I doubt anyone would interpret it that way, even
> if we didn't document this at all.

Please define a rule that allows to judge if any given implementation is
correct or not. For the record neither "within reasonable bounds" nor "a
factor of 1000000000 is not within reason" is good enough.

This is not only important to be able to review drivers that implement
it, but also for consumers, because they should know what to expect.

> To be frank I think that quest of yours to try and rid the PWM API of
> all ambiguity is futile.

I consider my quest about rounding reasonable. And I think this is
painful because when the PWM framework was introduced it was too much ad
hoc and the APIs were not thought through enough. And because I don't
want to have that repeated, I express my concerns here.

> I've been trying to be lenient because you seem
> motivated, but I think you're taking this too far. There are always
> going to be cases that aren't completely clear-cut and where drivers
> need the flexibility to cheat in order to be useful at all. If we get to
> a point where everything needs to be 100% accurate, the majority of the
> PWM controllers won't be usable at all.
> 
> Don't let perfect be the enemy of good.

I admit here I don't have a constructive idea how to define what is
needed.

For example if we only care about the relative duty cycle, a consumer
requests

	.period = 1045
	.duty_cyle = 680

and the driver can provide multiples of 100 ns for both .period and
.duty_cycle, the candidates that might be sensible to chose from are
(IMHO):

 - exact relative duty:

	.period = 104500
	.duty_cycle = 68000

 - round both values in the same direction, minimizing error

 	.period = 1100
	.duty_cycle = 700

   (requested relative duty = 65.07%, implemented = 63.64%; when
   rounding both down we get 60%)

 - round both values mathematically: 

 	.period = 1000
	.duty_cycle = 700

   (yielding a relative duty of 70% instead of the requested 65.07%)

 - Maybe

 	.period = 1000
	.duty_cycle = 600

   might also be preferable for some consumers?! (60%)

 - Maybe

 	.period = 2000
	.duty_cycle = 1300

   is a good compromise because the relative duty is nearly exactly
   matched and the period is only stretched by a factor < 2.

In my eyes a driver author should be told which of these options should
be picked. Do you consider it obvious which of these options is the
objective best? If so why? Do you agree that we should tell driver
authors how to implement this before we have several drivers that all
implement their own ideas and getting this in a consistent state is
another pain?

(My bet is you are lax and don't consider consistency among drivers soo
important. In this case we don't agree. I think it's important for
consumer driver authors to be able to rely on some expectations
independently which lowlevel driver is in use.)

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] 42+ messages in thread

* Re: [PATCH v8 1/8] pwm: pca9685: Switch to atomic API
  2021-04-13 12:11       ` Clemens Gruber
  2021-04-13 12:17         ` Clemens Gruber
  2021-04-13 12:37         ` Thierry Reding
@ 2021-04-13 19:38         ` Uwe Kleine-König
  2021-04-14 12:09           ` Clemens Gruber
  2 siblings, 1 reply; 42+ messages in thread
From: Uwe Kleine-König @ 2021-04-13 19:38 UTC (permalink / raw)
  To: Clemens Gruber
  Cc: linux-pwm, Thierry Reding, Sven Van Asbroeck, devicetree, linux-kernel

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

Hello Clemens,

On Tue, Apr 13, 2021 at 02:11:38PM +0200, Clemens Gruber wrote:
> On Mon, Apr 12, 2021 at 10:10:19PM +0200, Uwe Kleine-König wrote:
> > On Mon, Apr 12, 2021 at 06:39:28PM +0200, Clemens Gruber wrote:
> > > On Mon, Apr 12, 2021 at 06:18:08PM +0200, Uwe Kleine-König wrote:
> > > > On Mon, Apr 12, 2021 at 03:27:38PM +0200, Clemens Gruber wrote:
> > > > > 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
> > > > > - Previously, only the full ON bit was toggled in GPIO mode (and full
> > > > >   OFF cleared if set to high), which could result in both full OFF and
> > > > >   full ON not being set and on=0, off=0, which is not allowed according
> > > > >   to the datasheet
> > > > > - The OFF registers were reset to 0 in probe, which could lead to the
> > > > >   forbidden on=0, off=0. Fixed by resetting to POR default (full OFF)
> > > > > 
> > > > > Signed-off-by: Clemens Gruber <clemens.gruber@pqgruber.com>
> > > > > ---
> > > > > Changes since v7:
> > > > > - Moved check for !state->enabled before prescaler configuration
> > > > > - Removed unnecessary cast
> > > > > - Use DIV_ROUND_DOWN in .apply
> > > > > 
> > > > > Changes since v6:
> > > > > - Order of a comparison switched for improved readability
> > > > > 
> > > > > 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 | 259 +++++++++++++-------------------------
> > > > >  1 file changed, 89 insertions(+), 170 deletions(-)
> > > > > 
> > > > > diff --git a/drivers/pwm/pwm-pca9685.c b/drivers/pwm/pwm-pca9685.c
> > > > > index 4a55dc18656c..827b57ced3c2 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)))
> > > > 
> > > > I'd like to see these named PCA9685_REG_ON_H etc.
> > > 
> > > I did not use the prefix because the existing LED_N_ON/OFF_H/L also do
> > > not have a prefix. If the prefix is mandatory, I think LED_N_.. should
> > > also be prefixed, right?
> > 
> > I'd like to seem the prefixed (and assume that Thierry doesn't agree).
> > IMHO it's good style and even though it yields longer name usually it
> > yields easier understandable code. (But this seems to be subjective.)
> 
> I am not sure I want to also rename the existing LED_N_OFF stuff in this
> patch. Maybe we can discuss unifying the macros (either with or without
> prefix) in a later patch and I keep the REG_ON_ stuff for now without to
> match the LED_N_ stuff?

While consistency is fine I agree that this patch is already big and
letting it do the things similar to other stuff in this driver is ok.

> > > > Consider the following request: state->period = 4177921 [ns] +
> > > > state->duty_cycle = 100000 [ns], then we get
> > > > PRESCALE = round(25 * state->period / 4096000) - 1 = 25 and so an actual
> > > > period of 4096000 / 25 * (25 + 1) = 4259840 [ns]. If you now calculate
> > > > the duty using 4096 * 100000 / 4177920 = 98, this corresponds to an
> > > > actual duty cycle of 98 * 4259840 / 4096 = 101920 ns while you should
> > > > actually configure 96 to get 99840 ns.
> > > > 
> > > > So in the end I'd like to have the following:
> > > > 
> > > > 	PRESCALE = round-down(25 * state->period / 4096000) - 1
> > > > 
> > > > (to get the biggest period not bigger than state->period) and
> > > > 
> > > > 	duty = round-down(state->duty_cycle * 25 / ((PRESCALE + 1) * 1000))
> > > > 
> > > > (to get the biggest duty cycle not bigger than state->duty_cycle). With
> > > > the example above this yields
> > > > 
> > > > 	PRESCALE = 24
> > > > 	duty = 100
> > > > 
> > > > which results in
> > > > 
> > > > 	.period = 4096000 / 25 * 25 = 4096000 [ns]
> > > > 	.duty_cycle = 100000 [ns]
> > > > 	
> > > > Now you have a mixture of old and new with no consistent behaviour. So
> > > > please either stick to the old behaviour or do it right immediately.
> > > 
> > > I avoided rounding down the prescale value because the datasheet has an
> > > example where a round-closest is used, see page 25.
> > 
> > The hardware guy who wrote this data sheet wasn't aware of the rounding
> > rules for Linux PWM drivers :-)
> > 
> > > With your suggested round-down, the example with frequency of 200 Hz
> > > would no longer result in 30 but 29 and that contradicts the datasheet.
> > 
> > Well, with PRESCALE = 30 we get a frequency of 196.88 Hz and with
> > PRESCALE = 29 we get a frequency of 203.45 Hz. So no matter if you pick
> > 29 or 30, you don't get 200 Hz. And which of the two possible values is
> > the better one depends on the consumer, no matter what rounding
> > algorithm the data sheet suggests. Also note that the math here contains
> > surprises you don't expect at first. For example, what PRESCALE value
> > would you pick to get 284 Hz? [If my mail was a video, I'd suggest to
> > press Space now to pause and let you think first :-)] The data sheet's
> > formula suggests:
> > 
> > 	round(25 MHz / (4096 * 284)) - 1 = 20
> > 
> > The resulting frequency when picking PRESCALE = 20 is 290.644 Hz (so an
> > error of 6.644 Hz). If instead you pick PRESCALE = 21 you get 277.433 Hz
> > (error = 6.567 Hz), so 21 is the better choice.
> > 
> > Exercise for the reader:
> >  What is the correct formula to really determine the PRESCALE value that
> >  yields the best approximation (i.e. minimizing
> >  abs(real_freq - target_freq)) for a given target_freq?

I wonder if you tried this.

> > These things don't happen when you round down only.
> 
> Sure, but it might also be counterintuitive that the Linux driver does
> not use the same formula as the datasheet. And when using 200 Hz, 29 is
> a little closer than 30.

First let me state that I consider keeping the math as is in this patch
a good idea. So to argue already for the future:

I value consistency among the various pwm lowlevel drivers higher than
what an individual hardware engineer happened to write in a data sheet.
That engineer was successful in describing the functionality of the chip
and that's where her/his job ends. How a driver should behave is to be
decided by us.

> I once measured the actual frequency and the internal oscillator is not
> very accurate, so even if you think you should get 196.88 Hz, the actual
> frequency measured with a decent scope is about 206 Hz and varies from
> chip to chip (~ 205-207 Hz).

Huh. Did you do further measurements that suggest that the oscillator
doesn't run at 25 MHz but maybe at 26 MHz (which would yield 204.7552
Hz)? (Assume this is true, what do you think: Should be use the formula
that matches reality, or should we stick to the formula defined in the
data sheet?)
 
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] 42+ messages in thread

* Re: [PATCH v8 1/8] pwm: pca9685: Switch to atomic API
  2021-04-13 19:38         ` Uwe Kleine-König
@ 2021-04-14 12:09           ` Clemens Gruber
  2021-04-14 19:21             ` Uwe Kleine-König
  0 siblings, 1 reply; 42+ messages in thread
From: Clemens Gruber @ 2021-04-14 12:09 UTC (permalink / raw)
  To: Uwe Kleine-König
  Cc: linux-pwm, Thierry Reding, Sven Van Asbroeck, devicetree, linux-kernel

Hi Uwe,

On Tue, Apr 13, 2021 at 09:38:18PM +0200, Uwe Kleine-König wrote:
> Hello Clemens,
> 
> On Tue, Apr 13, 2021 at 02:11:38PM +0200, Clemens Gruber wrote:
> > On Mon, Apr 12, 2021 at 10:10:19PM +0200, Uwe Kleine-König wrote:
> > > On Mon, Apr 12, 2021 at 06:39:28PM +0200, Clemens Gruber wrote:
> > > > On Mon, Apr 12, 2021 at 06:18:08PM +0200, Uwe Kleine-König wrote:
> > > > > On Mon, Apr 12, 2021 at 03:27:38PM +0200, Clemens Gruber wrote:
> > > > > > 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
> > > > > > - Previously, only the full ON bit was toggled in GPIO mode (and full
> > > > > >   OFF cleared if set to high), which could result in both full OFF and
> > > > > >   full ON not being set and on=0, off=0, which is not allowed according
> > > > > >   to the datasheet
> > > > > > - The OFF registers were reset to 0 in probe, which could lead to the
> > > > > >   forbidden on=0, off=0. Fixed by resetting to POR default (full OFF)
> > > > > > 
> > > > > > Signed-off-by: Clemens Gruber <clemens.gruber@pqgruber.com>
> > > > > > ---
> > > > > > Changes since v7:
> > > > > > - Moved check for !state->enabled before prescaler configuration
> > > > > > - Removed unnecessary cast
> > > > > > - Use DIV_ROUND_DOWN in .apply
> > > > > > 
> > > > > > Changes since v6:
> > > > > > - Order of a comparison switched for improved readability
> > > > > > 
> > > > > > 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 | 259 +++++++++++++-------------------------
> > > > > >  1 file changed, 89 insertions(+), 170 deletions(-)
> > > > > > 
> > > > > > diff --git a/drivers/pwm/pwm-pca9685.c b/drivers/pwm/pwm-pca9685.c
> > > > > > index 4a55dc18656c..827b57ced3c2 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)))
> > > > > 
> > > > > I'd like to see these named PCA9685_REG_ON_H etc.
> > > > 
> > > > I did not use the prefix because the existing LED_N_ON/OFF_H/L also do
> > > > not have a prefix. If the prefix is mandatory, I think LED_N_.. should
> > > > also be prefixed, right?
> > > 
> > > I'd like to seem the prefixed (and assume that Thierry doesn't agree).
> > > IMHO it's good style and even though it yields longer name usually it
> > > yields easier understandable code. (But this seems to be subjective.)
> > 
> > I am not sure I want to also rename the existing LED_N_OFF stuff in this
> > patch. Maybe we can discuss unifying the macros (either with or without
> > prefix) in a later patch and I keep the REG_ON_ stuff for now without to
> > match the LED_N_ stuff?
> 
> While consistency is fine I agree that this patch is already big and
> letting it do the things similar to other stuff in this driver is ok.
> 
> > > > > Consider the following request: state->period = 4177921 [ns] +
> > > > > state->duty_cycle = 100000 [ns], then we get
> > > > > PRESCALE = round(25 * state->period / 4096000) - 1 = 25 and so an actual
> > > > > period of 4096000 / 25 * (25 + 1) = 4259840 [ns]. If you now calculate
> > > > > the duty using 4096 * 100000 / 4177920 = 98, this corresponds to an
> > > > > actual duty cycle of 98 * 4259840 / 4096 = 101920 ns while you should
> > > > > actually configure 96 to get 99840 ns.
> > > > > 
> > > > > So in the end I'd like to have the following:
> > > > > 
> > > > > 	PRESCALE = round-down(25 * state->period / 4096000) - 1
> > > > > 
> > > > > (to get the biggest period not bigger than state->period) and
> > > > > 
> > > > > 	duty = round-down(state->duty_cycle * 25 / ((PRESCALE + 1) * 1000))
> > > > > 
> > > > > (to get the biggest duty cycle not bigger than state->duty_cycle). With
> > > > > the example above this yields
> > > > > 
> > > > > 	PRESCALE = 24
> > > > > 	duty = 100
> > > > > 
> > > > > which results in
> > > > > 
> > > > > 	.period = 4096000 / 25 * 25 = 4096000 [ns]
> > > > > 	.duty_cycle = 100000 [ns]
> > > > > 	
> > > > > Now you have a mixture of old and new with no consistent behaviour. So
> > > > > please either stick to the old behaviour or do it right immediately.
> > > > 
> > > > I avoided rounding down the prescale value because the datasheet has an
> > > > example where a round-closest is used, see page 25.
> > > 
> > > The hardware guy who wrote this data sheet wasn't aware of the rounding
> > > rules for Linux PWM drivers :-)
> > > 
> > > > With your suggested round-down, the example with frequency of 200 Hz
> > > > would no longer result in 30 but 29 and that contradicts the datasheet.
> > > 
> > > Well, with PRESCALE = 30 we get a frequency of 196.88 Hz and with
> > > PRESCALE = 29 we get a frequency of 203.45 Hz. So no matter if you pick
> > > 29 or 30, you don't get 200 Hz. And which of the two possible values is
> > > the better one depends on the consumer, no matter what rounding
> > > algorithm the data sheet suggests. Also note that the math here contains
> > > surprises you don't expect at first. For example, what PRESCALE value
> > > would you pick to get 284 Hz? [If my mail was a video, I'd suggest to
> > > press Space now to pause and let you think first :-)] The data sheet's
> > > formula suggests:
> > > 
> > > 	round(25 MHz / (4096 * 284)) - 1 = 20
> > > 
> > > The resulting frequency when picking PRESCALE = 20 is 290.644 Hz (so an
> > > error of 6.644 Hz). If instead you pick PRESCALE = 21 you get 277.433 Hz
> > > (error = 6.567 Hz), so 21 is the better choice.
> > > 
> > > Exercise for the reader:
> > >  What is the correct formula to really determine the PRESCALE value that
> > >  yields the best approximation (i.e. minimizing
> > >  abs(real_freq - target_freq)) for a given target_freq?
> 
> I wonder if you tried this.

We could calculate both round-up and round-down and decide which one is
closer to "real freq" (even though that is not the actual frequency but
just our backwards-calculated frequency).

But I can't give you a formula with minimized abs(real_freq-target_freq)
Is it a different round point than 0.5 and maybe relative to f ?

Please enlighten us :-)

> 
> > > These things don't happen when you round down only.
> > 
> > Sure, but it might also be counterintuitive that the Linux driver does
> > not use the same formula as the datasheet. And when using 200 Hz, 29 is
> > a little closer than 30.
> 
> First let me state that I consider keeping the math as is in this patch
> a good idea. So to argue already for the future:
> 
> I value consistency among the various pwm lowlevel drivers higher than
> what an individual hardware engineer happened to write in a data sheet.
> That engineer was successful in describing the functionality of the chip
> and that's where her/his job ends. How a driver should behave is to be
> decided by us.
> 
> > I once measured the actual frequency and the internal oscillator is not
> > very accurate, so even if you think you should get 196.88 Hz, the actual
> > frequency measured with a decent scope is about 206 Hz and varies from
> > chip to chip (~ 205-207 Hz).
> 
> Huh. Did you do further measurements that suggest that the oscillator
> doesn't run at 25 MHz but maybe at 26 MHz (which would yield 204.7552
> Hz)? (Assume this is true, what do you think: Should be use the formula
> that matches reality, or should we stick to the formula defined in the
> data sheet?)

No, I just tested this for 3-4 chips and for them I measured these
errors, but that's just a small sample.
Maybe the next few measurements would indicate that it runs at 24 MHz
and it evens out at 25 MHz for all produced chips, but we just have
enough information imho.

Clemens

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

* Re: [PATCH v8 1/8] pwm: pca9685: Switch to atomic API
  2021-04-14 12:09           ` Clemens Gruber
@ 2021-04-14 19:21             ` Uwe Kleine-König
  2021-04-14 19:45               ` Clemens Gruber
  0 siblings, 1 reply; 42+ messages in thread
From: Uwe Kleine-König @ 2021-04-14 19:21 UTC (permalink / raw)
  To: Clemens Gruber
  Cc: linux-pwm, Thierry Reding, Sven Van Asbroeck, devicetree, linux-kernel

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

On Wed, Apr 14, 2021 at 02:09:14PM +0200, Clemens Gruber wrote:
> Hi Uwe,
> 
> On Tue, Apr 13, 2021 at 09:38:18PM +0200, Uwe Kleine-König wrote:
> > Hello Clemens,
> > 
> > On Tue, Apr 13, 2021 at 02:11:38PM +0200, Clemens Gruber wrote:
> > > On Mon, Apr 12, 2021 at 10:10:19PM +0200, Uwe Kleine-König wrote:
> > > > On Mon, Apr 12, 2021 at 06:39:28PM +0200, Clemens Gruber wrote:
> > > > > With your suggested round-down, the example with frequency of 200 Hz
> > > > > would no longer result in 30 but 29 and that contradicts the datasheet.
> > > > 
> > > > Well, with PRESCALE = 30 we get a frequency of 196.88 Hz and with
> > > > PRESCALE = 29 we get a frequency of 203.45 Hz. So no matter if you pick
> > > > 29 or 30, you don't get 200 Hz. And which of the two possible values is
> > > > the better one depends on the consumer, no matter what rounding
> > > > algorithm the data sheet suggests. Also note that the math here contains
> > > > surprises you don't expect at first. For example, what PRESCALE value
> > > > would you pick to get 284 Hz? [If my mail was a video, I'd suggest to
> > > > press Space now to pause and let you think first :-)] The data sheet's
> > > > formula suggests:
> > > > 
> > > > 	round(25 MHz / (4096 * 284)) - 1 = 20
> > > > 
> > > > The resulting frequency when picking PRESCALE = 20 is 290.644 Hz (so an
> > > > error of 6.644 Hz). If instead you pick PRESCALE = 21 you get 277.433 Hz
> > > > (error = 6.567 Hz), so 21 is the better choice.
> > > > 
> > > > Exercise for the reader:
> > > >  What is the correct formula to really determine the PRESCALE value that
> > > >  yields the best approximation (i.e. minimizing
> > > >  abs(real_freq - target_freq)) for a given target_freq?
> > 
> > I wonder if you tried this.
> 
> We could calculate both round-up and round-down and decide which one is
> closer to "real freq" (even though that is not the actual frequency but
> just our backwards-calculated frequency).

Yeah, the backwards-calculated frequency is the best assumption we
have.

> But I can't give you a formula with minimized abs(real_freq-target_freq)
> Is it a different round point than 0.5 and maybe relative to f ?
> 
> Please enlighten us :-)

Sorry, I cannot. I spend ~20 min today after lunch with pencil and
paper, but without success. I was aware that it isn't trivial and this
is the main reason I established round-down as default for new drivers
instead of round-nearest.

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] 42+ messages in thread

* Re: [PATCH v8 1/8] pwm: pca9685: Switch to atomic API
  2021-04-14 19:21             ` Uwe Kleine-König
@ 2021-04-14 19:45               ` Clemens Gruber
  2021-04-15  6:48                 ` Uwe Kleine-König
  0 siblings, 1 reply; 42+ messages in thread
From: Clemens Gruber @ 2021-04-14 19:45 UTC (permalink / raw)
  To: Uwe Kleine-König
  Cc: linux-pwm, Thierry Reding, Sven Van Asbroeck, devicetree, linux-kernel

On Wed, Apr 14, 2021 at 09:21:31PM +0200, Uwe Kleine-König wrote:
> On Wed, Apr 14, 2021 at 02:09:14PM +0200, Clemens Gruber wrote:
> > Hi Uwe,
> > 
> > On Tue, Apr 13, 2021 at 09:38:18PM +0200, Uwe Kleine-König wrote:
> > > Hello Clemens,
> > > 
> > > On Tue, Apr 13, 2021 at 02:11:38PM +0200, Clemens Gruber wrote:
> > > > On Mon, Apr 12, 2021 at 10:10:19PM +0200, Uwe Kleine-König wrote:
> > > > > On Mon, Apr 12, 2021 at 06:39:28PM +0200, Clemens Gruber wrote:
> > > > > > With your suggested round-down, the example with frequency of 200 Hz
> > > > > > would no longer result in 30 but 29 and that contradicts the datasheet.
> > > > > 
> > > > > Well, with PRESCALE = 30 we get a frequency of 196.88 Hz and with
> > > > > PRESCALE = 29 we get a frequency of 203.45 Hz. So no matter if you pick
> > > > > 29 or 30, you don't get 200 Hz. And which of the two possible values is
> > > > > the better one depends on the consumer, no matter what rounding
> > > > > algorithm the data sheet suggests. Also note that the math here contains
> > > > > surprises you don't expect at first. For example, what PRESCALE value
> > > > > would you pick to get 284 Hz? [If my mail was a video, I'd suggest to
> > > > > press Space now to pause and let you think first :-)] The data sheet's
> > > > > formula suggests:
> > > > > 
> > > > > 	round(25 MHz / (4096 * 284)) - 1 = 20
> > > > > 
> > > > > The resulting frequency when picking PRESCALE = 20 is 290.644 Hz (so an
> > > > > error of 6.644 Hz). If instead you pick PRESCALE = 21 you get 277.433 Hz
> > > > > (error = 6.567 Hz), so 21 is the better choice.
> > > > > 
> > > > > Exercise for the reader:
> > > > >  What is the correct formula to really determine the PRESCALE value that
> > > > >  yields the best approximation (i.e. minimizing
> > > > >  abs(real_freq - target_freq)) for a given target_freq?
> > > 
> > > I wonder if you tried this.
> > 
> > We could calculate both round-up and round-down and decide which one is
> > closer to "real freq" (even though that is not the actual frequency but
> > just our backwards-calculated frequency).
> 
> Yeah, the backwards-calculated frequency is the best assumption we
> have.
> 
> > But I can't give you a formula with minimized abs(real_freq-target_freq)
> > Is it a different round point than 0.5 and maybe relative to f ?
> > 
> > Please enlighten us :-)
> 
> Sorry, I cannot. I spend ~20 min today after lunch with pencil and
> paper, but without success. I was aware that it isn't trivial and this
> is the main reason I established round-down as default for new drivers
> instead of round-nearest.

Oh, I thought you already solved it. I tried too for a while but was
unsuccessful. Not trivial indeed!

But regarding you establishing round-down: Wouldn't it be even better if
the driver did what I suggested above, namely calculate backwards from
both the rounded-up as well as the rounded-down prescale value and then
write the one with the smallest abs(f_target - f_real) to the register?

Clemens

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

* Re: [PATCH v8 1/8] pwm: pca9685: Switch to atomic API
  2021-04-14 19:45               ` Clemens Gruber
@ 2021-04-15  6:48                 ` Uwe Kleine-König
  0 siblings, 0 replies; 42+ messages in thread
From: Uwe Kleine-König @ 2021-04-15  6:48 UTC (permalink / raw)
  To: Clemens Gruber
  Cc: linux-pwm, Thierry Reding, Sven Van Asbroeck, devicetree, linux-kernel

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

On Wed, Apr 14, 2021 at 09:45:32PM +0200, Clemens Gruber wrote:
> On Wed, Apr 14, 2021 at 09:21:31PM +0200, Uwe Kleine-König wrote:
> > On Wed, Apr 14, 2021 at 02:09:14PM +0200, Clemens Gruber wrote:
> > > Hi Uwe,
> > > 
> > > On Tue, Apr 13, 2021 at 09:38:18PM +0200, Uwe Kleine-König wrote:
> > > > Hello Clemens,
> > > > 
> > > > On Tue, Apr 13, 2021 at 02:11:38PM +0200, Clemens Gruber wrote:
> > > > > On Mon, Apr 12, 2021 at 10:10:19PM +0200, Uwe Kleine-König wrote:
> > > > > > On Mon, Apr 12, 2021 at 06:39:28PM +0200, Clemens Gruber wrote:
> > > > > > > With your suggested round-down, the example with frequency of 200 Hz
> > > > > > > would no longer result in 30 but 29 and that contradicts the datasheet.
> > > > > > 
> > > > > > Well, with PRESCALE = 30 we get a frequency of 196.88 Hz and with
> > > > > > PRESCALE = 29 we get a frequency of 203.45 Hz. So no matter if you pick
> > > > > > 29 or 30, you don't get 200 Hz. And which of the two possible values is
> > > > > > the better one depends on the consumer, no matter what rounding
> > > > > > algorithm the data sheet suggests. Also note that the math here contains
> > > > > > surprises you don't expect at first. For example, what PRESCALE value
> > > > > > would you pick to get 284 Hz? [If my mail was a video, I'd suggest to
> > > > > > press Space now to pause and let you think first :-)] The data sheet's
> > > > > > formula suggests:
> > > > > > 
> > > > > > 	round(25 MHz / (4096 * 284)) - 1 = 20
> > > > > > 
> > > > > > The resulting frequency when picking PRESCALE = 20 is 290.644 Hz (so an
> > > > > > error of 6.644 Hz). If instead you pick PRESCALE = 21 you get 277.433 Hz
> > > > > > (error = 6.567 Hz), so 21 is the better choice.
> > > > > > 
> > > > > > Exercise for the reader:
> > > > > >  What is the correct formula to really determine the PRESCALE value that
> > > > > >  yields the best approximation (i.e. minimizing
> > > > > >  abs(real_freq - target_freq)) for a given target_freq?
> > > > 
> > > > I wonder if you tried this.
> > > 
> > > We could calculate both round-up and round-down and decide which one is
> > > closer to "real freq" (even though that is not the actual frequency but
> > > just our backwards-calculated frequency).
> > 
> > Yeah, the backwards-calculated frequency is the best assumption we
> > have.
> > 
> > > But I can't give you a formula with minimized abs(real_freq-target_freq)
> > > Is it a different round point than 0.5 and maybe relative to f ?
> > > 
> > > Please enlighten us :-)
> > 
> > Sorry, I cannot. I spend ~20 min today after lunch with pencil and
> > paper, but without success. I was aware that it isn't trivial and this
> > is the main reason I established round-down as default for new drivers
> > instead of round-nearest.
> 
> Oh, I thought you already solved it. I tried too for a while but was
> unsuccessful. Not trivial indeed!
> 
> But regarding you establishing round-down: Wouldn't it be even better if
> the driver did what I suggested above, namely calculate backwards from
> both the rounded-up as well as the rounded-down prescale value and then
> write the one with the smallest abs(f_target - f_real) to the register?

No, I don't think so for several reasons. First, just rounding down is
easier (and keeping lowlevel drivers rules and implementation easy is
IMHO a good goal). The second reason is that round-nearest is a bit
ambigous because round to the nearest frequency is slightly different to
round to the nearest period length. So to actually implement (or use)
it correctly, people have to grasp that difference. Compared to that
rounding down the period length corresponds 1:1 to rounding up
frequency. That's easy.

For the third reason I have to backup a bit: I intend to introduce a
function pwm_round_rate that predicts what pwm_apply_rate will actually
implement. Of course it must have the same rounding rules. This allows
to implement efficient search for consumers that e.g. prefer
round-nearest time, or round-nearest frequency. I'm convinced that
searching the optimal request to make is easier if round_rate uses
round-down and not round-nearest.

All three reasons boil down to "the math for round-down is just simpler
(for implementers and for users) than with round-nearest".

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] 42+ messages in thread

* Re: [PATCH v8 4/8] dt-bindings: pwm: Support new PWM_USAGE_POWER flag
  2021-04-13 17:56       ` Uwe Kleine-König
@ 2021-04-15 16:27         ` Thierry Reding
  2021-04-16  9:32           ` Uwe Kleine-König
  0 siblings, 1 reply; 42+ messages in thread
From: Thierry Reding @ 2021-04-15 16:27 UTC (permalink / raw)
  To: Uwe Kleine-König
  Cc: Clemens Gruber, linux-pwm, Sven Van Asbroeck, devicetree,
	linux-kernel, Rob Herring

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

On Tue, Apr 13, 2021 at 07:56:31PM +0200, Uwe Kleine-König wrote:
> On Tue, Apr 13, 2021 at 01:51:15PM +0200, Thierry Reding wrote:
> > On Mon, Apr 12, 2021 at 06:27:23PM +0200, Uwe Kleine-König wrote:
> > > On Mon, Apr 12, 2021 at 03:27:41PM +0200, Clemens Gruber wrote:
> > > > Add the flag and corresponding documentation for PWM_USAGE_POWER.
> > > 
> > > My concern here in the previous round was that PWM_USAGE_POWER isn't a
> > > name that intuitively suggests its semantic. Do you disagree?
> > 
> > I suggested PWM_USAGE_POWER because I think it accurately captures what
> > we want here.
> > 
> > > > Cc: Rob Herring <robh+dt@kernel.org>
> > > > Signed-off-by: Clemens Gruber <clemens.gruber@pqgruber.com>
> > > > ---
> > > >  Documentation/devicetree/bindings/pwm/pwm.txt | 3 +++
> > > >  include/dt-bindings/pwm/pwm.h                 | 1 +
> > > >  2 files changed, 4 insertions(+)
> > > > 
> > > > diff --git a/Documentation/devicetree/bindings/pwm/pwm.txt b/Documentation/devicetree/bindings/pwm/pwm.txt
> > > > index 084886bd721e..fe3a28f887c0 100644
> > > > --- a/Documentation/devicetree/bindings/pwm/pwm.txt
> > > > +++ b/Documentation/devicetree/bindings/pwm/pwm.txt
> > > > @@ -46,6 +46,9 @@ period in nanoseconds.
> > > >  Optionally, the pwm-specifier can encode a number of flags (defined in
> > > >  <dt-bindings/pwm/pwm.h>) in a third cell:
> > > >  - PWM_POLARITY_INVERTED: invert the PWM signal polarity
> > > > +- PWM_USAGE_POWER: Only care about the power output of the signal. This
> > > > +  allows drivers (if supported) to optimize the signals, for example to
> > > > +  improve EMI and reduce current spikes.
> > > 
> > > IMHO there are too many open questions about which freedom this gives to
> > > the lowlevel driver. If the consumer requests .duty_cycle = 25ns +
> > > .period = 100ns, can the driver provide .duty_cycle = 25s + .period =
> > > 100s which nominally has the same power output? Let's not introduce more
> > > ambiguity than there already is.
> > 
> > The freedom given to the driver should be to adjust the signal within
> > reasonable bounds. Changing the time unit by a factor of 1000000000 is
> > not within reason, and I doubt anyone would interpret it that way, even
> > if we didn't document this at all.
> 
> Please define a rule that allows to judge if any given implementation is
> correct or not. For the record neither "within reasonable bounds" nor "a
> factor of 1000000000 is not within reason" is good enough.

We haven't had any rules thus far and I have yet to see a single report
that drivers get this completely wrong. So "within reason", which I
think is what driver authors will do by default, is good enough in
practice.

> This is not only important to be able to review drivers that implement
> it, but also for consumers, because they should know what to expect.

Again, consumers should expect that the PWM driver will do something
that is within reasonable margins. If that ever ends up being wrong for
a given use-case we may need to change that.

But I don't think it's necessary to take out all flexibility if we don't
have to. As long as things work fine there's no reason to make the rules
any more strict.

> > To be frank I think that quest of yours to try and rid the PWM API of
> > all ambiguity is futile.
> 
> I consider my quest about rounding reasonable. And I think this is
> painful because when the PWM framework was introduced it was too much ad
> hoc and the APIs were not thought through enough. And because I don't
> want to have that repeated, I express my concerns here.

Maybe try to look at this from another perspective. Maybe what you call
adhoc API was actually deliberately designed this way. To be honest I
don't know what the intentions were when the original PWM API was
created, that was way before I took on maintenance of the PWM subsystem.
The PWM framework adopted the existing API and there was no reason to
change it because it worked just fine.

And I still don't see a reason for the API to change. Like I said, if we
ever run into a case where the current flexibility gets in the way and
yields unpredictable or unusable results, then that's something we have
to improve. But I don't think we should make any such changes if they're
not necessary, because then we may end up making matters worse.

Also, I think this actually corroborates the need for something like the
usage flags in the PWM specifier. Currently drivers will do their best
to generate a PWM signal that's as close as possible to the requested
parameters. If that's not enough for a specific use-case, then that's
something that the new use-case has to describe somehow. They could do
that using a usage flag (perhaps something like PWM_USAGE_STRICT, which
may tell the driver to return an error if the requested parameters
cannot be applied exactly). Another possibility is to give consumers a
way of running a given state through the driver but not applying just
yet so that they can inspect what the driver would have programmed and
then make adjustments (that's along the lines of what you had in mind
with the "round state" concept, I suppose).

> > I've been trying to be lenient because you seem
> > motivated, but I think you're taking this too far. There are always
> > going to be cases that aren't completely clear-cut and where drivers
> > need the flexibility to cheat in order to be useful at all. If we get to
> > a point where everything needs to be 100% accurate, the majority of the
> > PWM controllers won't be usable at all.
> > 
> > Don't let perfect be the enemy of good.
> 
> I admit here I don't have a constructive idea how to define what is
> needed.
> 
> For example if we only care about the relative duty cycle, a consumer
> requests
> 
> 	.period = 1045
> 	.duty_cyle = 680
> 
> and the driver can provide multiples of 100 ns for both .period and
> .duty_cycle, the candidates that might be sensible to chose from are
> (IMHO):
> 
>  - exact relative duty:
> 
> 	.period = 104500
> 	.duty_cycle = 68000
> 
>  - round both values in the same direction, minimizing error
> 
>  	.period = 1100
> 	.duty_cycle = 700
> 
>    (requested relative duty = 65.07%, implemented = 63.64%; when
>    rounding both down we get 60%)
> 
>  - round both values mathematically: 
> 
>  	.period = 1000
> 	.duty_cycle = 700
> 
>    (yielding a relative duty of 70% instead of the requested 65.07%)
> 
>  - Maybe
> 
>  	.period = 1000
> 	.duty_cycle = 600
> 
>    might also be preferable for some consumers?! (60%)
> 
>  - Maybe
> 
>  	.period = 2000
> 	.duty_cycle = 1300
> 
>    is a good compromise because the relative duty is nearly exactly
>    matched and the period is only stretched by a factor < 2.
> 
> In my eyes a driver author should be told which of these options should
> be picked. Do you consider it obvious which of these options is the
> objective best? If so why? Do you agree that we should tell driver
> authors how to implement this before we have several drivers that all
> implement their own ideas and getting this in a consistent state is
> another pain?

We already have several drivers implementing things inconsistently. And
again, I don't see how that's a problem. Most of the time, values for
period will be hand-picked to match the requirements of the use-case on
a given platform (for backlights or LEDs, for example, you don't want a
period that's too long, because then you'll get flicker). The duty cycle
is then simply used as a way of getting a power output of the desired
percentage. For something like PWM backlight, if interpolation doesn't
work, you have the option of specifying discrete levels with hand-picked
values.

Backlight and LEDs are the vast majority of applications for PWMs used
in the kernel today. Another category would be regulators and they end
up being pretty much the same in where the values come from.

The one use-case that's perhaps a bit more tricky is the sysfs interface
because people can throw whatever they want at it. But even that is not
likely to be problematic in practice because users will either be
satisfied with the result that they get when computationally getting the
numbers, or end up hand-picking values for those as well, with the only
difference being that they are programmed from userspace.

For the particular case of PWM_USAGE_POWER, I think it really only says
that the power output of the signal should be as requested. It does not
mean that the driver can pick whatever values it wants. Drivers should
still try to match period and duty cycle as closely as possible because
there's not enough other information to know if, for example, stretching
the clock by a factor of 2 is reasonable for the use-case.

> (My bet is you are lax and don't consider consistency among drivers soo
> important. In this case we don't agree. I think it's important for
> consumer driver authors to be able to rely on some expectations
> independently which lowlevel driver is in use.)

Well, yeah. Consumers should be able to rely on the expectation that the
provider will try to best match the given parameters. Something like
PWM_USAGE_POWER can be used to give the driver a bit more freedom, but
it doesn't mean it should switch into crazy mode.

Again, most of the time the values that we're dealing with here will be
hand-picked for a given use-case, which means a given PWM channel and
what it will be used for. So the values that the API and driver get are
going to be something that the driver can set to within a reasonable
margin, otherwise users will go and pick a better value.

So in practice these problems just don't exist, and we're spending a
huge amount of time tryng to solve a non-existent problem. And that's
the reason why we're not coming up with a good solution. You can't come
up with a good solution to a problem that doesn't exist because you
don't know any of the parameters.

Thierry

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

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

* Re: [PATCH v8 4/8] dt-bindings: pwm: Support new PWM_USAGE_POWER flag
  2021-04-15 16:27         ` Thierry Reding
@ 2021-04-16  9:32           ` Uwe Kleine-König
  2021-04-16 10:45             ` Thierry Reding
  0 siblings, 1 reply; 42+ messages in thread
From: Uwe Kleine-König @ 2021-04-16  9:32 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Clemens Gruber, linux-pwm, Sven Van Asbroeck, devicetree,
	linux-kernel, Rob Herring

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

Hello Thierry,

On Thu, Apr 15, 2021 at 06:27:02PM +0200, Thierry Reding wrote:
> On Tue, Apr 13, 2021 at 07:56:31PM +0200, Uwe Kleine-König wrote:
> > On Tue, Apr 13, 2021 at 01:51:15PM +0200, Thierry Reding wrote:
> > > On Mon, Apr 12, 2021 at 06:27:23PM +0200, Uwe Kleine-König wrote:
> > > > On Mon, Apr 12, 2021 at 03:27:41PM +0200, Clemens Gruber wrote:
> > > > > Cc: Rob Herring <robh+dt@kernel.org>
> > > > > Signed-off-by: Clemens Gruber <clemens.gruber@pqgruber.com>
> > > > > ---
> > > > >  Documentation/devicetree/bindings/pwm/pwm.txt | 3 +++
> > > > >  include/dt-bindings/pwm/pwm.h                 | 1 +
> > > > >  2 files changed, 4 insertions(+)
> > > > > 
> > > > > diff --git a/Documentation/devicetree/bindings/pwm/pwm.txt b/Documentation/devicetree/bindings/pwm/pwm.txt
> > > > > index 084886bd721e..fe3a28f887c0 100644
> > > > > --- a/Documentation/devicetree/bindings/pwm/pwm.txt
> > > > > +++ b/Documentation/devicetree/bindings/pwm/pwm.txt
> > > > > @@ -46,6 +46,9 @@ period in nanoseconds.
> > > > >  Optionally, the pwm-specifier can encode a number of flags (defined in
> > > > >  <dt-bindings/pwm/pwm.h>) in a third cell:
> > > > >  - PWM_POLARITY_INVERTED: invert the PWM signal polarity
> > > > > +- PWM_USAGE_POWER: Only care about the power output of the signal. This
> > > > > +  allows drivers (if supported) to optimize the signals, for example to
> > > > > +  improve EMI and reduce current spikes.
> > > > 
> > > > IMHO there are too many open questions about which freedom this gives to
> > > > the lowlevel driver. If the consumer requests .duty_cycle = 25ns +
> > > > .period = 100ns, can the driver provide .duty_cycle = 25s + .period =
> > > > 100s which nominally has the same power output? Let's not introduce more
> > > > ambiguity than there already is.
> > > 
> > > The freedom given to the driver should be to adjust the signal within
> > > reasonable bounds. Changing the time unit by a factor of 1000000000 is
> > > not within reason, and I doubt anyone would interpret it that way, even
> > > if we didn't document this at all.
> > 
> > Please define a rule that allows to judge if any given implementation is
> > correct or not. For the record neither "within reasonable bounds" nor "a
> > factor of 1000000000 is not within reason" is good enough.
> 
> We haven't had any rules thus far and I have yet to see a single report
> that drivers get this completely wrong. So "within reason", which I
> think is what driver authors will do by default, is good enough in
> practice.

For me commit 11fc4edc483b ("pwm: bcm2835: Improve precision of PWM")
indicates that there is a problem. Someone used the pwm-ir-tx driver on
top of pwm-bcm2835. The former driver's expectation is that

	period = DIV_ROUND_CLOSEST(NSEC_PER_SEC, pwm_ir->carrier);
	duty = DIV_ROUND_CLOSEST(pwm_ir->duty_cycle * period, 100);
	pwm_config(pwm, duty, period);

yields a frequency near pwm_ir->carrier (minimizing

	abs(1 / implemented_freq - 1 / pwm_ir->carrier)

(or should it minimize abs(implemented_freq - pwm_ir->carrier) instead?
Not entirely sure.). The result was that the pwm-bcm2835 driver was
changed to implement a different rounding. I took the time to look at
the drivers in 11fc4edc483b^, with the following result:

 - pwm-ab8500.c: ignores period_ns in .config
 - pwm-atmel.c: rounds down period
 - pwm-atmel-hlcdc.c: rounds up period (unsure)
 - pwm-atmel-tcb.c: rounds down period (unsure)
 - pwm-bcm2835.c: rounds down period
 - pwm-bcm-iproc.c: rounds down period
 - pwm-bcm-kona.c: rounds down period
 - pwm-berlin.c: rounds down period
 - pwm-brcmstb.c: rounds down period
 - pwm-clps711x.c: doesn't support changing period (IMHO in a buggy way
   because the period in the dts is just overwritten)
 - pwm-crc: rounds down period
 - pwm-cros-ec.c: doesn't support changing period
 - pwm-ep93xx.c: rounds down period
 - pwm-fsl-ftm.c: rounds down period
 - pwm-hibvt.c: rounds down period
 o pwm-img.c: confusing rounding behaviour
 - pwm-imx1.c: just implements relative duty cycle
 - pwm-imx27.c: rounds down period
 + pwm-imx-tpm.c: rounds to nearest period (unsure)
 - pwm-jz4740.c: rounds down period
 - pwm-lp3943.c: rounds down period (apart from corner cases)
 - pwm-lpc18xx-sct.c: rounds down period
 - pwm-lpc32xx.c: rounds down period
 - pwm-lpss.c: rounds up period
 + pwm-mediatek.c: tries to implement round-nearest
 - pwm-meson.c: tries to round down period
 o pwm-mtk-disp.c: confusing rounding behaviour
 - pwm-mxs.c: rounds down period
 + pwm-omap-dmtimer.c: rounds to closest period
 + pwm-pca9685.c: rounds to closest period
 - pwm-puv3.c: rounds down period
 - pwm-pxa.c: rounds down period
 - pwm-rcar.c: rounds down period
 o pwm-renesas-tpu.c: confusing rounding behaviour
 + pwm-rockchip.c: rounds closest period
 - pwm-samsung.c: rounds down period (unsure)
 - pwm-sifive.c: rounds down period
 - pwm-spear.c: rounds down period
 - pwm-sti.c: rounds down period
 - pwm-stm32.c: rounds down period
 - pwm-stm32-lp.c: rounds down period
 - pwm-stmpe.c: just implements relative duty cycle
 + pwm-sun4i.c: rounds closest period
 + pwm-tegra.c: tries to round nearest period
 - pwm-tiecap.c: rounds down period
 - pwm-tiehrpwm.c: rounds down period
 - pwm-twl.c: just implements relative duty cycle
 - pwm-twl-led.c: just implements relative duty cycle
 - pwm-vt8500.c: rounds down period
 - pwm-zx.c: rounds down period

(- = doesn't behave "reasonable" to be used by pwm-ir-tx, + = behaves
"reasonable" for pwm-ir-tx, o = don't know, too complicated for me to
understand quickly (should we count that as -?))

So among the 51 drivers 7 were suitable to be used by the pwm-ir-tx
driver and pwm-bcm2835 was "fixed" to become the eighth. IMHO this is
ridiculous.

The right way is to improve the capabilities for consumers to make
informed requests.

> > This is not only important to be able to review drivers that implement
> > it, but also for consumers, because they should know what to expect.
> 
> Again, consumers should expect that the PWM driver will do something
> that is within reasonable margins. If that ever ends up being wrong for
> a given use-case we may need to change that.

Would you say that having 40 of 50 drivers being unsuitable to be used
by pwm-ir-tx is a problem? If soon someone appears who reports that
using pwm-ir-tx with pwm-mxs.c is broken, should we adapt how pwm-mxs.c
rounds? If someone else appears who reports that pwm-sun4i.c doesn't
work with the motor control driver (that assumes round-down because
that's safer and is correct for most drivers), should we change
pwm-sun4i.c to break the pwm-sun4i.c/pwm-ir-tx combination?

IMHO this isn't a sane long-term plan. And in my eyes this is a good
enough reason to work on improving the framework.

> But I don't think it's necessary to take out all flexibility if we don't
> have to. As long as things work fine there's no reason to make the rules
> any more strict.
> 
> > > To be frank I think that quest of yours to try and rid the PWM API of
> > > all ambiguity is futile.
> > 
> > I consider my quest about rounding reasonable. And I think this is
> > painful because when the PWM framework was introduced it was too much ad
> > hoc and the APIs were not thought through enough. And because I don't
> > want to have that repeated, I express my concerns here.
> 
> Maybe try to look at this from another perspective. Maybe what you call
> adhoc API was actually deliberately designed this way.

In my eyes everyone who designs an API deliberately lax is a fool.
(There are some valid reasons to not fix an API's behaviour. "Oh,
drivers might round in one or the other direction, they can pick their
way themselves" isn't such a reason. Something like: "Either way to fix
rounding behaviour results in computationally complexity for some
drivers and consumers should never care which behaviour is picked" can
be valid.)

> To be honest I don't know what the intentions were when the original
> PWM API was created, that was way before I took on maintenance of the
> PWM subsystem.  The PWM framework adopted the existing API and there
> was no reason to change it because it worked just fine.
> 
> And I still don't see a reason for the API to change. Like I said, if we
> ever run into a case where the current flexibility gets in the way and
> yields unpredictable or unusable results, then that's something we have
> to improve. But I don't think we should make any such changes if they're
> not necessary, because then we may end up making matters worse.
> 
> Also, I think this actually corroborates the need for something like the
> usage flags in the PWM specifier. Currently drivers will do their best
> to generate a PWM signal that's as close as possible to the requested
> parameters.

No, see above, most driver round down, not closest. (One problem is also
that "as close as possible" isn't well defined.)

> If that's not enough for a specific use-case, then that's
> something that the new use-case has to describe somehow. They could do
> that using a usage flag (perhaps something like PWM_USAGE_STRICT, which
> may tell the driver to return an error if the requested parameters
> cannot be applied exactly).

I think this will result in too much problems in practise.

> Another possibility is to give consumers a way of running a given
> state through the driver but not applying just yet so that they can
> inspect what the driver would have programmed and then make
> adjustments (that's along the lines of what you had in mind with the
> "round state" concept, I suppose).

Yes. And this function can only be efficiently worked with if the
rounding behaviour is known.

> > > I've been trying to be lenient because you seem
> > > motivated, but I think you're taking this too far. There are always
> > > going to be cases that aren't completely clear-cut and where drivers
> > > need the flexibility to cheat in order to be useful at all. If we get to
> > > a point where everything needs to be 100% accurate, the majority of the
> > > PWM controllers won't be usable at all.
> > > 
> > > Don't let perfect be the enemy of good.
> > 
> > I admit here I don't have a constructive idea how to define what is
> > needed.
> > 
> > For example if we only care about the relative duty cycle, a consumer
> > requests
> > 
> > 	.period = 1045
> > 	.duty_cyle = 680
> > 
> > and the driver can provide multiples of 100 ns for both .period and
> > .duty_cycle, the candidates that might be sensible to chose from are
> > (IMHO):
> > 
> >  - exact relative duty:
> > 
> > 	.period = 104500
> > 	.duty_cycle = 68000
> > 
> >  - round both values in the same direction, minimizing error
> > 
> >  	.period = 1100
> > 	.duty_cycle = 700
> > 
> >    (requested relative duty = 65.07%, implemented = 63.64%; when
> >    rounding both down we get 60%)
> > 
> >  - round both values mathematically: 
> > 
> >  	.period = 1000
> > 	.duty_cycle = 700
> > 
> >    (yielding a relative duty of 70% instead of the requested 65.07%)
> > 
> >  - Maybe
> > 
> >  	.period = 1000
> > 	.duty_cycle = 600
> > 
> >    might also be preferable for some consumers?! (60%)
> > 
> >  - Maybe
> > 
> >  	.period = 2000
> > 	.duty_cycle = 1300
> > 
> >    is a good compromise because the relative duty is nearly exactly
> >    matched and the period is only stretched by a factor < 2.
> > 
> > In my eyes a driver author should be told which of these options should
> > be picked. Do you consider it obvious which of these options is the
> > objective best? If so why? Do you agree that we should tell driver
> > authors how to implement this before we have several drivers that all
> > implement their own ideas and getting this in a consistent state is
> > another pain?
> 
> We already have several drivers implementing things inconsistently.

I'm more ambitious here. I think inconstancies should be purged.

> And again, I don't see how that's a problem.

So what should consumer driver authors request that wants "50% relative
duty cycle with a period between 60 and 250 Hz" if they don't know how
the underlying PWM driver behaves? Sure, you can say, a fitting value
should be picked that yields a good enough setting for the machine that
is currently worked with. But there is an algorithm that allows (given a
consistent behaviour of the lowlevel drivers) to automatically pick a
suitable setting. And this is what I want target for because I consider
this is right behaviour for a good OS.

Do you remember the times when you had to specify mode lines for your
monitor in your X-server's configuration? Do you think that today's
status quo where needing to hand-tune an xorg.conf is a big exception
is an improvement? I think yes, and my efforts for PWM are similar.

If you're happy with the PWM framework that it only solves the problems
it already solved 10 years ago there is no need to work on it. If
however you want to improve the solutions saying "oh, there are
inconsistencies, but I don't see how that's problem" isn't good enough.

> Most of the time, values for
> period will be hand-picked to match the requirements of the use-case on
> a given platform (for backlights or LEDs, for example, you don't want a
> period that's too long, because then you'll get flicker). The duty cycle
> is then simply used as a way of getting a power output of the desired
> percentage. For something like PWM backlight, if interpolation doesn't
> work, you have the option of specifying discrete levels with hand-picked
> values.
> 
> Backlight and LEDs are the vast majority of applications for PWMs used
> in the kernel today. Another category would be regulators and they end
> up being pretty much the same in where the values come from.
> 
> The one use-case that's perhaps a bit more tricky is the sysfs interface
> because people can throw whatever they want at it. But even that is not
> likely to be problematic in practice because users will either be
> satisfied with the result that they get when computationally getting the
> numbers, or end up hand-picking values for those as well, with the only
> difference being that they are programmed from userspace.

So the PWM API doesn't provide what it should be there for: Abstracting
PWM hardware. Consumers seem to have to know exactly what hardware is
used and modify their usage of the API accordingly. (Or even adapt the
driver to do what matches their use-case break another one.) :-\

> For the particular case of PWM_USAGE_POWER, I think it really only says
> that the power output of the signal should be as requested. It does not
> mean that the driver can pick whatever values it wants. Drivers should
> still try to match period and duty cycle as closely as possible because
> there's not enough other information to know if, for example, stretching
> the clock by a factor of 2 is reasonable for the use-case.

So if this factor of 2 is the best some given driver can provide, the
request should be declined, right? Where do you draw the line? Is 1.5
the biggest factor that should be allowed? IMHO this is a question we
should be ready to answer before driver authors start proposing patches.

> > (My bet is you are lax and don't consider consistency among drivers soo
> > important. In this case we don't agree. I think it's important for
> > consumer driver authors to be able to rely on some expectations
> > independently which lowlevel driver is in use.)
> 
> Well, yeah. Consumers should be able to rely on the expectation that the
> provider will try to best match the given parameters. Something like
> PWM_USAGE_POWER can be used to give the driver a bit more freedom, but
> it doesn't mean it should switch into crazy mode.

I didn't suggest doing something deliberately crazy. I only want a to
know how to judge a patch that adds support for this concept. Because
then we will have to decide if you take this patch or not.
 
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] 42+ messages in thread

* Re: [PATCH v8 4/8] dt-bindings: pwm: Support new PWM_USAGE_POWER flag
  2021-04-16  9:32           ` Uwe Kleine-König
@ 2021-04-16 10:45             ` Thierry Reding
  2021-04-18 13:30               ` Uwe Kleine-König
  0 siblings, 1 reply; 42+ messages in thread
From: Thierry Reding @ 2021-04-16 10:45 UTC (permalink / raw)
  To: Uwe Kleine-König
  Cc: Clemens Gruber, linux-pwm, Sven Van Asbroeck, devicetree,
	linux-kernel, Rob Herring

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

On Fri, Apr 16, 2021 at 11:32:12AM +0200, Uwe Kleine-König wrote:
> Hello Thierry,
> 
> On Thu, Apr 15, 2021 at 06:27:02PM +0200, Thierry Reding wrote:
> > On Tue, Apr 13, 2021 at 07:56:31PM +0200, Uwe Kleine-König wrote:
> > > On Tue, Apr 13, 2021 at 01:51:15PM +0200, Thierry Reding wrote:
> > > > On Mon, Apr 12, 2021 at 06:27:23PM +0200, Uwe Kleine-König wrote:
> > > > > On Mon, Apr 12, 2021 at 03:27:41PM +0200, Clemens Gruber wrote:
> > > > > > Cc: Rob Herring <robh+dt@kernel.org>
> > > > > > Signed-off-by: Clemens Gruber <clemens.gruber@pqgruber.com>
> > > > > > ---
> > > > > >  Documentation/devicetree/bindings/pwm/pwm.txt | 3 +++
> > > > > >  include/dt-bindings/pwm/pwm.h                 | 1 +
> > > > > >  2 files changed, 4 insertions(+)
> > > > > > 
> > > > > > diff --git a/Documentation/devicetree/bindings/pwm/pwm.txt b/Documentation/devicetree/bindings/pwm/pwm.txt
> > > > > > index 084886bd721e..fe3a28f887c0 100644
> > > > > > --- a/Documentation/devicetree/bindings/pwm/pwm.txt
> > > > > > +++ b/Documentation/devicetree/bindings/pwm/pwm.txt
> > > > > > @@ -46,6 +46,9 @@ period in nanoseconds.
> > > > > >  Optionally, the pwm-specifier can encode a number of flags (defined in
> > > > > >  <dt-bindings/pwm/pwm.h>) in a third cell:
> > > > > >  - PWM_POLARITY_INVERTED: invert the PWM signal polarity
> > > > > > +- PWM_USAGE_POWER: Only care about the power output of the signal. This
> > > > > > +  allows drivers (if supported) to optimize the signals, for example to
> > > > > > +  improve EMI and reduce current spikes.
> > > > > 
> > > > > IMHO there are too many open questions about which freedom this gives to
> > > > > the lowlevel driver. If the consumer requests .duty_cycle = 25ns +
> > > > > .period = 100ns, can the driver provide .duty_cycle = 25s + .period =
> > > > > 100s which nominally has the same power output? Let's not introduce more
> > > > > ambiguity than there already is.
> > > > 
> > > > The freedom given to the driver should be to adjust the signal within
> > > > reasonable bounds. Changing the time unit by a factor of 1000000000 is
> > > > not within reason, and I doubt anyone would interpret it that way, even
> > > > if we didn't document this at all.
> > > 
> > > Please define a rule that allows to judge if any given implementation is
> > > correct or not. For the record neither "within reasonable bounds" nor "a
> > > factor of 1000000000 is not within reason" is good enough.
> > 
> > We haven't had any rules thus far and I have yet to see a single report
> > that drivers get this completely wrong. So "within reason", which I
> > think is what driver authors will do by default, is good enough in
> > practice.
> 
> For me commit 11fc4edc483b ("pwm: bcm2835: Improve precision of PWM")
> indicates that there is a problem. Someone used the pwm-ir-tx driver on
> top of pwm-bcm2835. The former driver's expectation is that
> 
> 	period = DIV_ROUND_CLOSEST(NSEC_PER_SEC, pwm_ir->carrier);
> 	duty = DIV_ROUND_CLOSEST(pwm_ir->duty_cycle * period, 100);
> 	pwm_config(pwm, duty, period);
> 
> yields a frequency near pwm_ir->carrier (minimizing
> 
> 	abs(1 / implemented_freq - 1 / pwm_ir->carrier)
> 
> (or should it minimize abs(implemented_freq - pwm_ir->carrier) instead?
> Not entirely sure.). The result was that the pwm-bcm2835 driver was
> changed to implement a different rounding. I took the time to look at
> the drivers in 11fc4edc483b^, with the following result:
> 
>  - pwm-ab8500.c: ignores period_ns in .config
>  - pwm-atmel.c: rounds down period
>  - pwm-atmel-hlcdc.c: rounds up period (unsure)
>  - pwm-atmel-tcb.c: rounds down period (unsure)
>  - pwm-bcm2835.c: rounds down period
>  - pwm-bcm-iproc.c: rounds down period
>  - pwm-bcm-kona.c: rounds down period
>  - pwm-berlin.c: rounds down period
>  - pwm-brcmstb.c: rounds down period
>  - pwm-clps711x.c: doesn't support changing period (IMHO in a buggy way
>    because the period in the dts is just overwritten)
>  - pwm-crc: rounds down period
>  - pwm-cros-ec.c: doesn't support changing period
>  - pwm-ep93xx.c: rounds down period
>  - pwm-fsl-ftm.c: rounds down period
>  - pwm-hibvt.c: rounds down period
>  o pwm-img.c: confusing rounding behaviour
>  - pwm-imx1.c: just implements relative duty cycle
>  - pwm-imx27.c: rounds down period
>  + pwm-imx-tpm.c: rounds to nearest period (unsure)
>  - pwm-jz4740.c: rounds down period
>  - pwm-lp3943.c: rounds down period (apart from corner cases)
>  - pwm-lpc18xx-sct.c: rounds down period
>  - pwm-lpc32xx.c: rounds down period
>  - pwm-lpss.c: rounds up period
>  + pwm-mediatek.c: tries to implement round-nearest
>  - pwm-meson.c: tries to round down period
>  o pwm-mtk-disp.c: confusing rounding behaviour
>  - pwm-mxs.c: rounds down period
>  + pwm-omap-dmtimer.c: rounds to closest period
>  + pwm-pca9685.c: rounds to closest period
>  - pwm-puv3.c: rounds down period
>  - pwm-pxa.c: rounds down period
>  - pwm-rcar.c: rounds down period
>  o pwm-renesas-tpu.c: confusing rounding behaviour
>  + pwm-rockchip.c: rounds closest period
>  - pwm-samsung.c: rounds down period (unsure)
>  - pwm-sifive.c: rounds down period
>  - pwm-spear.c: rounds down period
>  - pwm-sti.c: rounds down period
>  - pwm-stm32.c: rounds down period
>  - pwm-stm32-lp.c: rounds down period
>  - pwm-stmpe.c: just implements relative duty cycle
>  + pwm-sun4i.c: rounds closest period
>  + pwm-tegra.c: tries to round nearest period
>  - pwm-tiecap.c: rounds down period
>  - pwm-tiehrpwm.c: rounds down period
>  - pwm-twl.c: just implements relative duty cycle
>  - pwm-twl-led.c: just implements relative duty cycle
>  - pwm-vt8500.c: rounds down period
>  - pwm-zx.c: rounds down period
> 
> (- = doesn't behave "reasonable" to be used by pwm-ir-tx, + = behaves
> "reasonable" for pwm-ir-tx, o = don't know, too complicated for me to
> understand quickly (should we count that as -?))

I'm not sure I understand correctly, but aren't you actually making a
point against always using round-down now?

> So among the 51 drivers 7 were suitable to be used by the pwm-ir-tx
> driver and pwm-bcm2835 was "fixed" to become the eighth. IMHO this is
> ridiculous.

Just because a driver rounds down doesn't automatically make it
unsuitable.

> The right way is to improve the capabilities for consumers to make
> informed requests.

Agreed. But that's a different problem from forcing everyone to round
down. It's also different from adding something like PWM_USAGE_POWER. I
never said that the API was perfect for every possible use-case. What I
said was that we should be solving problems that actually exist.

> > > This is not only important to be able to review drivers that implement
> > > it, but also for consumers, because they should know what to expect.
> > 
> > Again, consumers should expect that the PWM driver will do something
> > that is within reasonable margins. If that ever ends up being wrong for
> > a given use-case we may need to change that.
> 
> Would you say that having 40 of 50 drivers being unsuitable to be used
> by pwm-ir-tx is a problem? If soon someone appears who reports that
> using pwm-ir-tx with pwm-mxs.c is broken, should we adapt how pwm-mxs.c
> rounds? If someone else appears who reports that pwm-sun4i.c doesn't
> work with the motor control driver (that assumes round-down because
> that's safer and is correct for most drivers), should we change
> pwm-sun4i.c to break the pwm-sun4i.c/pwm-ir-tx combination?

Clearly if there are two conflicting use-cases we need to find a way to
distinguish between them. But again, I'm not opposing improving things
in general. All I'm saying is that we shouldn't start making any
improvements that we don't even know are necessary.

The bottom line is that if we don't have a real use-case or an actual
bug, chances are we're just going to guess what is right, but we don't
really know, nor do we have a way of validating that our guess is any
good.

Also, if you go around and change existing behaviour, you're likely to
cause regressions. And you're going to cause them for no reason.

> IMHO this isn't a sane long-term plan. And in my eyes this is a good
> enough reason to work on improving the framework.

Again, yes, no problem improving the framework if there's an actual
problem. And whether you consider this sane or not isn't really
relevant. It works. That's what counts.

So if you do want to propose any changes, please make sure that they are
really necessary by a) showing how the current framework fails in a
specific case and b) showing how the proposed changes are going to fix
the issue. That's what we did back when the atomic API was introduced
and that's going to be my benchmark for any new proposals.

> > But I don't think it's necessary to take out all flexibility if we don't
> > have to. As long as things work fine there's no reason to make the rules
> > any more strict.
> > 
> > > > To be frank I think that quest of yours to try and rid the PWM API of
> > > > all ambiguity is futile.
> > > 
> > > I consider my quest about rounding reasonable. And I think this is
> > > painful because when the PWM framework was introduced it was too much ad
> > > hoc and the APIs were not thought through enough. And because I don't
> > > want to have that repeated, I express my concerns here.
> > 
> > Maybe try to look at this from another perspective. Maybe what you call
> > adhoc API was actually deliberately designed this way.
> 
> In my eyes everyone who designs an API deliberately lax is a fool.

You might want to bring that up with RMK then, who initially wrote the
PWM API. But I already said that this argument goes both ways. If you
make the API deliberately strict you risk making it completely useless.

> (There are some valid reasons to not fix an API's behaviour. "Oh,
> drivers might round in one or the other direction, they can pick their
> way themselves" isn't such a reason. Something like: "Either way to fix
> rounding behaviour results in computationally complexity for some
> drivers and consumers should never care which behaviour is picked" can
> be valid.)

Also note that this doesn't have anything to do with the API itself.
There is no ambiguity in the API as to what period or duty cycle is
specified. There's no rounding or anything like that in the API.

How drivers interpret the parameters is necessarily device-specific
and PWMs just so happen to not need to be 100% accurate, which has the
nice benefit of allowing a wide range of controllers to work with a wide
range of use-cases.

I mean we've been over this. If you want to make the API very strict you
will ultimately end up making it useless because only the occasional
driver will be able to exactly program the parameters that were
requested.

And I don't know how many times I have to say this, but for the vast
majority it simply doesn't matter. Why should we prevent one PWM
controller from being usable if the use-case couldn't care less if the
signal ends up being slightly off?

> > To be honest I don't know what the intentions were when the original
> > PWM API was created, that was way before I took on maintenance of the
> > PWM subsystem.  The PWM framework adopted the existing API and there
> > was no reason to change it because it worked just fine.
> > 
> > And I still don't see a reason for the API to change. Like I said, if we
> > ever run into a case where the current flexibility gets in the way and
> > yields unpredictable or unusable results, then that's something we have
> > to improve. But I don't think we should make any such changes if they're
> > not necessary, because then we may end up making matters worse.
> > 
> > Also, I think this actually corroborates the need for something like the
> > usage flags in the PWM specifier. Currently drivers will do their best
> > to generate a PWM signal that's as close as possible to the requested
> > parameters.
> 
> No, see above, most driver round down, not closest. (One problem is also
> that "as close as possible" isn't well defined.)

The probably round down because that's just what division does by
default. Again, this is just because it doesn't actually matter in
practice for the cases that people care about.

> > If that's not enough for a specific use-case, then that's
> > something that the new use-case has to describe somehow. They could do
> > that using a usage flag (perhaps something like PWM_USAGE_STRICT, which
> > may tell the driver to return an error if the requested parameters
> > cannot be applied exactly).
> 
> I think this will result in too much problems in practise.

What I think or you think is irrelevant. This is pure speculation until
we have an actual use-case that requires this.

> > Another possibility is to give consumers a way of running a given
> > state through the driver but not applying just yet so that they can
> > inspect what the driver would have programmed and then make
> > adjustments (that's along the lines of what you had in mind with the
> > "round state" concept, I suppose).
> 
> Yes. And this function can only be efficiently worked with if the
> rounding behaviour is known.
> 
> > > > I've been trying to be lenient because you seem
> > > > motivated, but I think you're taking this too far. There are always
> > > > going to be cases that aren't completely clear-cut and where drivers
> > > > need the flexibility to cheat in order to be useful at all. If we get to
> > > > a point where everything needs to be 100% accurate, the majority of the
> > > > PWM controllers won't be usable at all.
> > > > 
> > > > Don't let perfect be the enemy of good.
> > > 
> > > I admit here I don't have a constructive idea how to define what is
> > > needed.
> > > 
> > > For example if we only care about the relative duty cycle, a consumer
> > > requests
> > > 
> > > 	.period = 1045
> > > 	.duty_cyle = 680
> > > 
> > > and the driver can provide multiples of 100 ns for both .period and
> > > .duty_cycle, the candidates that might be sensible to chose from are
> > > (IMHO):
> > > 
> > >  - exact relative duty:
> > > 
> > > 	.period = 104500
> > > 	.duty_cycle = 68000
> > > 
> > >  - round both values in the same direction, minimizing error
> > > 
> > >  	.period = 1100
> > > 	.duty_cycle = 700
> > > 
> > >    (requested relative duty = 65.07%, implemented = 63.64%; when
> > >    rounding both down we get 60%)
> > > 
> > >  - round both values mathematically: 
> > > 
> > >  	.period = 1000
> > > 	.duty_cycle = 700
> > > 
> > >    (yielding a relative duty of 70% instead of the requested 65.07%)
> > > 
> > >  - Maybe
> > > 
> > >  	.period = 1000
> > > 	.duty_cycle = 600
> > > 
> > >    might also be preferable for some consumers?! (60%)
> > > 
> > >  - Maybe
> > > 
> > >  	.period = 2000
> > > 	.duty_cycle = 1300
> > > 
> > >    is a good compromise because the relative duty is nearly exactly
> > >    matched and the period is only stretched by a factor < 2.
> > > 
> > > In my eyes a driver author should be told which of these options should
> > > be picked. Do you consider it obvious which of these options is the
> > > objective best? If so why? Do you agree that we should tell driver
> > > authors how to implement this before we have several drivers that all
> > > implement their own ideas and getting this in a consistent state is
> > > another pain?
> > 
> > We already have several drivers implementing things inconsistently.
> 
> I'm more ambitious here. I think inconstancies should be purged.

And how are you planning on doing that, exactly? There are factors in
this that can't be controller because the hardware was designed in a
specific way. Not all hardware is equal, so not all controllers will be
able to generate exactly the same signal as other controllers. There's
no way to get rid of these inconsistencies, so why bother trying?

> > And again, I don't see how that's a problem.
> 
> So what should consumer driver authors request that wants "50% relative
> duty cycle with a period between 60 and 250 Hz" if they don't know how
> the underlying PWM driver behaves? Sure, you can say, a fitting value
> should be picked that yields a good enough setting for the machine that
> is currently worked with. But there is an algorithm that allows (given a
> consistent behaviour of the lowlevel drivers) to automatically pick a
> suitable setting. And this is what I want target for because I consider
> this is right behaviour for a good OS.

Like I said, in most cases there's no need for the consumer driver
author to pick that value because it will come from device tree. If the
period needs to be between 60 and 250 Hz, then the board designer can
pick any value that can be most accurately be programmed in hardware. If
there are additional restrictions on the duty cycle then they may take
that into account as well.

> Do you remember the times when you had to specify mode lines for your
> monitor in your X-server's configuration? Do you think that today's
> status quo where needing to hand-tune an xorg.conf is a big exception
> is an improvement? I think yes, and my efforts for PWM are similar.

You're comparing apples to oranges. The reason why you no longer have to
configure modelines for X to work is because drivers can probe the
monitor's EDID for the equivalent of those modelines.

For PWM the equivalent of EDID would be DT or PWM lookup tables. So PWM
isn't the Neanderthal that you make it out to be.

But if you want to stick with the example of monitors: display drivers
are not always able to exactly generate the required pixel clock for a
given mode. The end result is that sometimes your vertical refresh rate
of a monitor is not exactly the 60 Hz that you've asked for but perhaps
60.03 Hz or 59.27 Hz. And just like for PWM this doesn't matter in the
majority of cases. In the cases where it does matter and you need
perfect synchronization the system designers will have made sure that
the target refresh rate can be met. This may mean, though, that you
cannot support arbitrary monitors. Instead you may have to select one
specific monitor with exactly the pixel clock that you can match.

> If you're happy with the PWM framework that it only solves the problems
> it already solved 10 years ago there is no need to work on it. If
> however you want to improve the solutions saying "oh, there are
> inconsistencies, but I don't see how that's problem" isn't good enough.

Again, this isn't about me trying to oppose progress. If there's a real
problem that needs a different solution, then I agree we should improve
things. But so far none of these proposals are because there are new
problems. You're just trying to change how things work because it bugs
you that they are not completely consistent. And please keep in mind
that if you go around changing behaviour just for the sake of
consistency you might accidentally end up breaking existing use-cases.
And that's just going to annoy other people.

So again, if you want to change something, please go and find a real
problem that needs that change.

That said, PWM_USAGE_POWER is a solution for a new problem. So far you
are the one objecting to this improvement and the only reasons for
objecting have been along the lines of "I don't like it", or "I think
it's going to cause problems".

> > Most of the time, values for
> > period will be hand-picked to match the requirements of the use-case on
> > a given platform (for backlights or LEDs, for example, you don't want a
> > period that's too long, because then you'll get flicker). The duty cycle
> > is then simply used as a way of getting a power output of the desired
> > percentage. For something like PWM backlight, if interpolation doesn't
> > work, you have the option of specifying discrete levels with hand-picked
> > values.
> > 
> > Backlight and LEDs are the vast majority of applications for PWMs used
> > in the kernel today. Another category would be regulators and they end
> > up being pretty much the same in where the values come from.
> > 
> > The one use-case that's perhaps a bit more tricky is the sysfs interface
> > because people can throw whatever they want at it. But even that is not
> > likely to be problematic in practice because users will either be
> > satisfied with the result that they get when computationally getting the
> > numbers, or end up hand-picking values for those as well, with the only
> > difference being that they are programmed from userspace.
> 
> So the PWM API doesn't provide what it should be there for: Abstracting
> PWM hardware. Consumers seem to have to know exactly what hardware is
> used and modify their usage of the API accordingly. (Or even adapt the
> driver to do what matches their use-case break another one.) :-\

You seem to have a strange definition of what "abstraction" means. I
don't even know what to reply to this.

> > For the particular case of PWM_USAGE_POWER, I think it really only says
> > that the power output of the signal should be as requested. It does not
> > mean that the driver can pick whatever values it wants. Drivers should
> > still try to match period and duty cycle as closely as possible because
> > there's not enough other information to know if, for example, stretching
> > the clock by a factor of 2 is reasonable for the use-case.
> 
> So if this factor of 2 is the best some given driver can provide, the
> request should be declined, right? Where do you draw the line? Is 1.5
> the biggest factor that should be allowed? IMHO this is a question we
> should be ready to answer before driver authors start proposing patches.

I said the driver should still try to match period and duty cycle as
closely as possible, but if stretching by a factor of 2 is the best it
can do, then that's tough luck, but not a reason to reject the request.
A potentially unusable result is better than no result at all.

But this is again pure speculation. In practice this isn't going to
happen because nobody is going to design a system to perform some task
using components that are not capable of performing those tasks. So if
you need to set a period of 60 Hz for a PWM signal for a given use-case
and you need it to be exactly 60 Hz, then you're going to have to use
components that can achieve these 60 Hz. If you choose hardware that can
only do 30 Hz, there's just no way you can make it work.

> > > (My bet is you are lax and don't consider consistency among drivers soo
> > > important. In this case we don't agree. I think it's important for
> > > consumer driver authors to be able to rely on some expectations
> > > independently which lowlevel driver is in use.)
> > 
> > Well, yeah. Consumers should be able to rely on the expectation that the
> > provider will try to best match the given parameters. Something like
> > PWM_USAGE_POWER can be used to give the driver a bit more freedom, but
> > it doesn't mean it should switch into crazy mode.
> 
> I didn't suggest doing something deliberately crazy. I only want a to
> know how to judge a patch that adds support for this concept. Because
> then we will have to decide if you take this patch or not.

Sorry, but I'm not a psychic. I don't know what exactly people will want
to do with this, so I can't give you a checklist for what I consider to
be okay and what not. We'll have to decide that as these cases come up.
Right now it means that the drivers will be able to modify the offset in
order to optimize for EMI.

Thierry

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

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

* Re: [PATCH v8 4/8] dt-bindings: pwm: Support new PWM_USAGE_POWER flag
  2021-04-12 13:27 ` [PATCH v8 4/8] dt-bindings: pwm: Support new PWM_USAGE_POWER flag Clemens Gruber
  2021-04-12 16:27   ` Uwe Kleine-König
@ 2021-04-16 13:55   ` Thierry Reding
  2021-04-16 15:39     ` Rob Herring
  2021-04-16 15:54     ` Clemens Gruber
  1 sibling, 2 replies; 42+ messages in thread
From: Thierry Reding @ 2021-04-16 13:55 UTC (permalink / raw)
  To: Rob Herring
  Cc: Clemens Gruber, linux-pwm, Sven Van Asbroeck,
	Uwe Kleine-König, devicetree, linux-kernel

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

On Mon, Apr 12, 2021 at 03:27:41PM +0200, Clemens Gruber wrote:
> Add the flag and corresponding documentation for PWM_USAGE_POWER.
> 
> Cc: Rob Herring <robh+dt@kernel.org>
> Signed-off-by: Clemens Gruber <clemens.gruber@pqgruber.com>
> ---
>  Documentation/devicetree/bindings/pwm/pwm.txt | 3 +++
>  include/dt-bindings/pwm/pwm.h                 | 1 +
>  2 files changed, 4 insertions(+)

Rob, what are your thoughts on this? I've been thinking about this some
more and I'm having second thoughts about putting this into device tree
because it doesn't actually describe a property of the PWM hardware but
rather a use-case specific hint. It's a bit of a gray area because this
is just part of the PWM specifier which already has use-case specific
"configuration", such as the period and the polarity.

Perhaps a better place for this is within the PWM API? We could add the
same information into struct pwm_state and then consumers that don't
care about specifics of the signal (such as pwm-backlight) can set that
flag when they request a state to be applied.

Thierry

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

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

* Re: [PATCH v8 4/8] dt-bindings: pwm: Support new PWM_USAGE_POWER flag
  2021-04-16 13:55   ` Thierry Reding
@ 2021-04-16 15:39     ` Rob Herring
  2021-04-16 15:54     ` Clemens Gruber
  1 sibling, 0 replies; 42+ messages in thread
From: Rob Herring @ 2021-04-16 15:39 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Clemens Gruber, Linux PWM List, Sven Van Asbroeck,
	Uwe Kleine-König, devicetree, linux-kernel

On Fri, Apr 16, 2021 at 8:54 AM Thierry Reding <thierry.reding@gmail.com> wrote:
>
> On Mon, Apr 12, 2021 at 03:27:41PM +0200, Clemens Gruber wrote:
> > Add the flag and corresponding documentation for PWM_USAGE_POWER.
> >
> > Cc: Rob Herring <robh+dt@kernel.org>
> > Signed-off-by: Clemens Gruber <clemens.gruber@pqgruber.com>
> > ---
> >  Documentation/devicetree/bindings/pwm/pwm.txt | 3 +++
> >  include/dt-bindings/pwm/pwm.h                 | 1 +
> >  2 files changed, 4 insertions(+)
>
> Rob, what are your thoughts on this? I've been thinking about this some
> more and I'm having second thoughts about putting this into device tree
> because it doesn't actually describe a property of the PWM hardware but
> rather a use-case specific hint. It's a bit of a gray area because this
> is just part of the PWM specifier which already has use-case specific
> "configuration", such as the period and the polarity.

I'm pretty neutral. My main hesitation from what I've followed is
'power' seems a bit indirect. A PWM signal doesn't have a 'power' any
more than a GPIO signal does.

> Perhaps a better place for this is within the PWM API? We could add the
> same information into struct pwm_state and then consumers that don't
> care about specifics of the signal (such as pwm-backlight) can set that
> flag when they request a state to be applied.

Yeah, seems like this is fairly well tied to the class of consumer.

Rob

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

* Re: [PATCH v8 4/8] dt-bindings: pwm: Support new PWM_USAGE_POWER flag
  2021-04-16 13:55   ` Thierry Reding
  2021-04-16 15:39     ` Rob Herring
@ 2021-04-16 15:54     ` Clemens Gruber
  2021-04-17 15:44       ` Uwe Kleine-König
  1 sibling, 1 reply; 42+ messages in thread
From: Clemens Gruber @ 2021-04-16 15:54 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Rob Herring, linux-pwm, Sven Van Asbroeck, Uwe Kleine-König,
	devicetree, linux-kernel

On Fri, Apr 16, 2021 at 03:55:11PM +0200, Thierry Reding wrote:
> On Mon, Apr 12, 2021 at 03:27:41PM +0200, Clemens Gruber wrote:
> > Add the flag and corresponding documentation for PWM_USAGE_POWER.
> > 
> > Cc: Rob Herring <robh+dt@kernel.org>
> > Signed-off-by: Clemens Gruber <clemens.gruber@pqgruber.com>
> > ---
> >  Documentation/devicetree/bindings/pwm/pwm.txt | 3 +++
> >  include/dt-bindings/pwm/pwm.h                 | 1 +
> >  2 files changed, 4 insertions(+)
> 
> Rob, what are your thoughts on this? I've been thinking about this some
> more and I'm having second thoughts about putting this into device tree
> because it doesn't actually describe a property of the PWM hardware but
> rather a use-case specific hint. It's a bit of a gray area because this
> is just part of the PWM specifier which already has use-case specific
> "configuration", such as the period and the polarity.
> 
> Perhaps a better place for this is within the PWM API? We could add the
> same information into struct pwm_state and then consumers that don't
> care about specifics of the signal (such as pwm-backlight) can set that
> flag when they request a state to be applied.

I just want to note that in my opinion, this is not a flag that is
changed often, so is it really a good idea to require setting this
wherever PWM state is applied? Also, this can't be read-out in
.get_state.

Thierry: If this discussion carries on and a v10 is required: Could you
maybe merge the uncontroversial patches 1 to 3 of v9 separately and
maybe get those in 5.12 ? Patches 4 to 8 can probably wait for 5.13 and
have some time in linux-next.

Thanks,
Clemens

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

* Re: [PATCH v8 1/8] pwm: pca9685: Switch to atomic API
  2021-04-12 13:27 [PATCH v8 1/8] pwm: pca9685: Switch to atomic API Clemens Gruber
                   ` (7 preceding siblings ...)
  2021-04-12 16:18 ` [PATCH v8 1/8] pwm: pca9685: Switch to atomic API Uwe Kleine-König
@ 2021-04-17 15:37 ` Uwe Kleine-König
  2021-04-17 16:40   ` Clemens Gruber
  8 siblings, 1 reply; 42+ messages in thread
From: Uwe Kleine-König @ 2021-04-17 15:37 UTC (permalink / raw)
  To: Clemens Gruber
  Cc: linux-pwm, Thierry Reding, Sven Van Asbroeck, devicetree, linux-kernel

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

On Mon, Apr 12, 2021 at 03:27:38PM +0200, Clemens Gruber wrote:
> 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
> - Previously, only the full ON bit was toggled in GPIO mode (and full
>   OFF cleared if set to high), which could result in both full OFF and
>   full ON not being set and on=0, off=0, which is not allowed according
>   to the datasheet
> - The OFF registers were reset to 0 in probe, which could lead to the
>   forbidden on=0, off=0. Fixed by resetting to POR default (full OFF)

I didn't recheck all details, but the patch is definitively an
improvement, so:

Acked-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>

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] 42+ messages in thread

* Re: [PATCH v8 4/8] dt-bindings: pwm: Support new PWM_USAGE_POWER flag
  2021-04-16 15:54     ` Clemens Gruber
@ 2021-04-17 15:44       ` Uwe Kleine-König
  0 siblings, 0 replies; 42+ messages in thread
From: Uwe Kleine-König @ 2021-04-17 15:44 UTC (permalink / raw)
  To: Clemens Gruber
  Cc: Thierry Reding, Rob Herring, linux-pwm, Sven Van Asbroeck,
	devicetree, linux-kernel

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

On Fri, Apr 16, 2021 at 05:54:29PM +0200, Clemens Gruber wrote:
> On Fri, Apr 16, 2021 at 03:55:11PM +0200, Thierry Reding wrote:
> > On Mon, Apr 12, 2021 at 03:27:41PM +0200, Clemens Gruber wrote:
> > > Add the flag and corresponding documentation for PWM_USAGE_POWER.
> > > 
> > > Cc: Rob Herring <robh+dt@kernel.org>
> > > Signed-off-by: Clemens Gruber <clemens.gruber@pqgruber.com>
> > > ---
> > >  Documentation/devicetree/bindings/pwm/pwm.txt | 3 +++
> > >  include/dt-bindings/pwm/pwm.h                 | 1 +
> > >  2 files changed, 4 insertions(+)
> > 
> > Rob, what are your thoughts on this? I've been thinking about this some
> > more and I'm having second thoughts about putting this into device tree
> > because it doesn't actually describe a property of the PWM hardware but
> > rather a use-case specific hint. It's a bit of a gray area because this
> > is just part of the PWM specifier which already has use-case specific
> > "configuration", such as the period and the polarity.

This is something I'd prefer over making it part of the device tree API.
I still don't think it's a good idea but when we keep it in-kernel we
can at least easier modify it in the future.

> > Perhaps a better place for this is within the PWM API? We could add the
> > same information into struct pwm_state and then consumers that don't
> > care about specifics of the signal (such as pwm-backlight) can set that
> > flag when they request a state to be applied.
> 
> I just want to note that in my opinion, this is not a flag that is
> changed often, so is it really a good idea to require setting this
> wherever PWM state is applied? Also, this can't be read-out in
> .get_state.

Not being able to read it out isn't a problem in my eyes.

> Thierry: If this discussion carries on and a v10 is required: Could you
> maybe merge the uncontroversial patches 1 to 3 of v9 separately and
> maybe get those in 5.12 ? Patches 4 to 8 can probably wait for 5.13 and
> have some time in linux-next.

I'm ok in getting those into next now and than into the upcoming merge
window. That won't make them part of 5.12 however, but 5.13-rc1. IMHO
patches 7 and 8 can go in, 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] 42+ messages in thread

* Re: [PATCH v8 7/8] pwm: pca9685: Restrict period change for enabled PWMs
  2021-04-12 13:27 ` [PATCH v8 7/8] pwm: pca9685: Restrict period change for enabled PWMs Clemens Gruber
@ 2021-04-17 15:46   ` Uwe Kleine-König
  0 siblings, 0 replies; 42+ messages in thread
From: Uwe Kleine-König @ 2021-04-17 15:46 UTC (permalink / raw)
  To: Clemens Gruber
  Cc: linux-pwm, Thierry Reding, Sven Van Asbroeck, devicetree, linux-kernel

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

On Mon, Apr 12, 2021 at 03:27:44PM +0200, Clemens Gruber wrote:
> 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 enabled PWM 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.
> 
> GPIOs do not count as enabled PWMs as they are not using the prescaler
> and can't change it.
> 
> Signed-off-by: Clemens Gruber <clemens.gruber@pqgruber.com>

I think this patch could be a tad simpler (by just counting the number
of enabled channels instead of maintaining a bitmap). Still this is
beneficial, so:

Acked-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>

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] 42+ messages in thread

* Re: [PATCH v8 8/8] pwm: pca9685: Add error messages for failed regmap calls
  2021-04-12 13:27 ` [PATCH v8 8/8] pwm: pca9685: Add error messages for failed regmap calls Clemens Gruber
@ 2021-04-17 15:47   ` Uwe Kleine-König
  0 siblings, 0 replies; 42+ messages in thread
From: Uwe Kleine-König @ 2021-04-17 15:47 UTC (permalink / raw)
  To: Clemens Gruber
  Cc: linux-pwm, Thierry Reding, Sven Van Asbroeck, devicetree, linux-kernel

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

On Mon, Apr 12, 2021 at 03:27:45PM +0200, Clemens Gruber wrote:
> 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>

Acked-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>

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] 42+ messages in thread

* Re: [PATCH v8 1/8] pwm: pca9685: Switch to atomic API
  2021-04-17 15:37 ` Uwe Kleine-König
@ 2021-04-17 16:40   ` Clemens Gruber
  0 siblings, 0 replies; 42+ messages in thread
From: Clemens Gruber @ 2021-04-17 16:40 UTC (permalink / raw)
  To: Uwe Kleine-König
  Cc: linux-pwm, Thierry Reding, Sven Van Asbroeck, devicetree, linux-kernel

Hi,

On Sat, Apr 17, 2021 at 05:37:28PM +0200, Uwe Kleine-König wrote:
> On Mon, Apr 12, 2021 at 03:27:38PM +0200, Clemens Gruber wrote:
> > 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
> > - Previously, only the full ON bit was toggled in GPIO mode (and full
> >   OFF cleared if set to high), which could result in both full OFF and
> >   full ON not being set and on=0, off=0, which is not allowed according
> >   to the datasheet
> > - The OFF registers were reset to 0 in probe, which could lead to the
> >   forbidden on=0, off=0. Fixed by resetting to POR default (full OFF)
> 
> I didn't recheck all details, but the patch is definitively an
> improvement, so:
> 
> Acked-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>

Thanks, but there is a newer version v9, I assume your acks are meant
for the newer one?

Clemens

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

* Re: [PATCH v8 4/8] dt-bindings: pwm: Support new PWM_USAGE_POWER flag
  2021-04-16 10:45             ` Thierry Reding
@ 2021-04-18 13:30               ` Uwe Kleine-König
  0 siblings, 0 replies; 42+ messages in thread
From: Uwe Kleine-König @ 2021-04-18 13:30 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Clemens Gruber, linux-pwm, Sven Van Asbroeck, devicetree,
	linux-kernel, Rob Herring

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

Hello Thierry,

On Fri, Apr 16, 2021 at 12:45:10PM +0200, Thierry Reding wrote:
> On Fri, Apr 16, 2021 at 11:32:12AM +0200, Uwe Kleine-König wrote:
> > On Thu, Apr 15, 2021 at 06:27:02PM +0200, Thierry Reding wrote:
> > > On Tue, Apr 13, 2021 at 07:56:31PM +0200, Uwe Kleine-König wrote:
> > > > Please define a rule that allows to judge if any given implementation is
> > > > correct or not. For the record neither "within reasonable bounds" nor "a
> > > > factor of 1000000000 is not within reason" is good enough.
> > > 
> > > We haven't had any rules thus far and I have yet to see a single report
> > > that drivers get this completely wrong. So "within reason", which I
> > > think is what driver authors will do by default, is good enough in
> > > practice.
> > 
> > For me commit 11fc4edc483b ("pwm: bcm2835: Improve precision of PWM")
> > indicates that there is a problem. Someone used the pwm-ir-tx driver on
> > top of pwm-bcm2835. The former driver's expectation is that
> > 
> > 	period = DIV_ROUND_CLOSEST(NSEC_PER_SEC, pwm_ir->carrier);
> > 	duty = DIV_ROUND_CLOSEST(pwm_ir->duty_cycle * period, 100);
> > 	pwm_config(pwm, duty, period);
> > 
> > yields a frequency near pwm_ir->carrier (minimizing
> > 
> > 	abs(1 / implemented_freq - 1 / pwm_ir->carrier)
> > 
> > (or should it minimize abs(implemented_freq - pwm_ir->carrier) instead?
> > Not entirely sure.). The result was that the pwm-bcm2835 driver was
> > changed to implement a different rounding. I took the time to look at
> > the drivers in 11fc4edc483b^, with the following result:
> > 
> >  - pwm-ab8500.c: ignores period_ns in .config
> >  - pwm-atmel.c: rounds down period
> >  - pwm-atmel-hlcdc.c: rounds up period (unsure)
> >  - pwm-atmel-tcb.c: rounds down period (unsure)
> >  - pwm-bcm2835.c: rounds down period
> >  - pwm-bcm-iproc.c: rounds down period
> >  - pwm-bcm-kona.c: rounds down period
> >  - pwm-berlin.c: rounds down period
> >  - pwm-brcmstb.c: rounds down period
> >  - pwm-clps711x.c: doesn't support changing period (IMHO in a buggy way
> >    because the period in the dts is just overwritten)
> >  - pwm-crc: rounds down period
> >  - pwm-cros-ec.c: doesn't support changing period
> >  - pwm-ep93xx.c: rounds down period
> >  - pwm-fsl-ftm.c: rounds down period
> >  - pwm-hibvt.c: rounds down period
> >  o pwm-img.c: confusing rounding behaviour
> >  - pwm-imx1.c: just implements relative duty cycle
> >  - pwm-imx27.c: rounds down period
> >  + pwm-imx-tpm.c: rounds to nearest period (unsure)
> >  - pwm-jz4740.c: rounds down period
> >  - pwm-lp3943.c: rounds down period (apart from corner cases)
> >  - pwm-lpc18xx-sct.c: rounds down period
> >  - pwm-lpc32xx.c: rounds down period
> >  - pwm-lpss.c: rounds up period
> >  + pwm-mediatek.c: tries to implement round-nearest
> >  - pwm-meson.c: tries to round down period
> >  o pwm-mtk-disp.c: confusing rounding behaviour
> >  - pwm-mxs.c: rounds down period
> >  + pwm-omap-dmtimer.c: rounds to closest period
> >  + pwm-pca9685.c: rounds to closest period
> >  - pwm-puv3.c: rounds down period
> >  - pwm-pxa.c: rounds down period
> >  - pwm-rcar.c: rounds down period
> >  o pwm-renesas-tpu.c: confusing rounding behaviour
> >  + pwm-rockchip.c: rounds closest period
> >  - pwm-samsung.c: rounds down period (unsure)
> >  - pwm-sifive.c: rounds down period
> >  - pwm-spear.c: rounds down period
> >  - pwm-sti.c: rounds down period
> >  - pwm-stm32.c: rounds down period
> >  - pwm-stm32-lp.c: rounds down period
> >  - pwm-stmpe.c: just implements relative duty cycle
> >  + pwm-sun4i.c: rounds closest period
> >  + pwm-tegra.c: tries to round nearest period
> >  - pwm-tiecap.c: rounds down period
> >  - pwm-tiehrpwm.c: rounds down period
> >  - pwm-twl.c: just implements relative duty cycle
> >  - pwm-twl-led.c: just implements relative duty cycle
> >  - pwm-vt8500.c: rounds down period
> >  - pwm-zx.c: rounds down period
> > 
> > (- = doesn't behave "reasonable" to be used by pwm-ir-tx, + = behaves
> > "reasonable" for pwm-ir-tx, o = don't know, too complicated for me to
> > understand quickly (should we count that as -?))
> 
> I'm not sure I understand correctly, but aren't you actually making a
> point against always using round-down now?

Not really. This is indeed a case where round-down isn't the right thing
for a consumer. As there are also use cases where round-down is
right, the conclusion has to be: Currently the PWM API isn't powerful
enough to map the different needs of different consumers. IMHO we need
pwm_round_nearest_state() here.

And the reason I mentioned this was to contradict your position that
there isn't any problem. (You said "I have yet to see a single report
that drivers get this completely wrong." It depends on what you consider
"completely wrong", but I'd say
https://lore.kernel.org/linux-pwm/20190603090058.qd3tbiffmdgqm34d@gofer.mess.org/
was a report about a driver that gets it wrong enough to actually hurt
and in reply to that report the driver was changed possibly breaking
other use cases and ignoring the fact that pwm-bcm2835.c was just one of
roughly 40 lowlevel drivers (so ~80%) that are "broken" in the same
way.)

> > So among the 51 drivers 7 were suitable to be used by the pwm-ir-tx
> > driver and pwm-bcm2835 was "fixed" to become the eighth. IMHO this is
> > ridiculous.
> 
> Just because a driver rounds down doesn't automatically make it
> unsuitable.

A driver that rounds down yields a suboptimal setting in ~50% of the
cases. So if you are lucky, you get a setting that is good enough
(either because round-down is round-nearest here, or because round-down
while not being the optimum is still good enough).

Additionally given that the pwm-ir-tx driver cannot inspect the
resulting setting, even nearest possible setting might be too different
and the data sent is wrong. With pwm_round_nearest_state() this could be
determined before actually sending wrong data.

> > The right way is to improve the capabilities for consumers to make
> > informed requests.
> 
> Agreed. But that's a different problem from forcing everyone to round
> down.

It's intermangled. With rounding nearest the maths involved to implement
pwm_round_nearest_state() and pwm_round_down_state() is more complex
compared to round down. (Round up would also work, but this isn't
sensible IMHO.) If you don't know if the driver rounds down or up or
nearest it is hardly possible to implement these functions. Together
with round_nearest being harder to implement than round-down in some
cases (and round-down being never harder than round_nearest) for a
lowlevel driver this convinces me that round-down is the way to go.

> It's also different from adding something like PWM_USAGE_POWER. I
> never said that the API was perfect for every possible use-case. What I
> said was that we should be solving problems that actually exist.

Do you consider yielding a non-optimal setting with a probablility of
~50% an actually existing problem?

> > In my eyes everyone who designs an API deliberately lax is a fool.
> 
> You might want to bring that up with RMK then, who initially wrote the
> PWM API.

Yes this was defined lax, but I guess not deliberately lax. This isn't
optimal but ok.

> But I already said that this argument goes both ways. If you
> make the API deliberately strict you risk making it completely useless.

I believe my proposal to be well thought out and so the API should
be more useful in the end.

> > (There are some valid reasons to not fix an API's behaviour. "Oh,
> > drivers might round in one or the other direction, they can pick their
> > way themselves" isn't such a reason. Something like: "Either way to fix
> > rounding behaviour results in computationally complexity for some
> > drivers and consumers should never care which behaviour is picked" can
> > be valid.)
> 
> Also note that this doesn't have anything to do with the API itself.
> There is no ambiguity in the API as to what period or duty cycle is
> specified. There's no rounding or anything like that in the API.

<sarcastic>Oh yes, it's not a problem in the API, only with every single
driver implementing it.</sarcastic>

> How drivers interpret the parameters is necessarily device-specific
> and PWMs just so happen to not need to be 100% accurate, which has the
> nice benefit of allowing a wide range of controllers to work with a wide
> range of use-cases.
> 
> I mean we've been over this. If you want to make the API very strict you
> will ultimately end up making it useless because only the occasional
> driver will be able to exactly program the parameters that were
> requested.

Note, I don't want to make .apply strict in this sense. I only want to
dictate how to deviate from the requested state.

> And I don't know how many times I have to say this, but for the vast
> majority it simply doesn't matter. Why should we prevent one PWM
> controller from being usable if the use-case couldn't care less if the
> signal ends up being slightly off?

I completely agree, so no need to repeat. But your statement doesn't
apply to my improvements because no controller is prevented from being
useable. If you think that my approach will make a single driver useless
for a certain use case either you didn't understand it or you identified
a problem in it that I missed up to now and you failed to communicate.

> > > We already have several drivers implementing things inconsistently.
> > 
> > I'm more ambitious here. I think inconstancies should be purged.
> 
> And how are you planning on doing that, exactly? There are factors in
> this that can't be controller because the hardware was designed in a
> specific way. Not all hardware is equal, so not all controllers will be
> able to generate exactly the same signal as other controllers. There's
> no way to get rid of these inconsistencies, so why bother trying?

There is. I will work on my idea and propose it as a patch.

> > > And again, I don't see how that's a problem.
> > 
> > So what should consumer driver authors request that wants "50% relative
> > duty cycle with a period between 60 and 250 Hz" if they don't know how
> > the underlying PWM driver behaves? Sure, you can say, a fitting value
> > should be picked that yields a good enough setting for the machine that
> > is currently worked with. But there is an algorithm that allows (given a
> > consistent behaviour of the lowlevel drivers) to automatically pick a
> > suitable setting. And this is what I want target for because I consider
> > this is right behaviour for a good OS.
> 
> Like I said, in most cases there's no need for the consumer driver
> author to pick that value because it will come from device tree. If the
> period needs to be between 60 and 250 Hz, then the board designer can
> pick any value that can be most accurately be programmed in hardware. If
> there are additional restrictions on the duty cycle then they may take
> that into account as well.
> 
> > Do you remember the times when you had to specify mode lines for your
> > monitor in your X-server's configuration? Do you think that today's
> > status quo where needing to hand-tune an xorg.conf is a big exception
> > is an improvement? I think yes, and my efforts for PWM are similar.
> 
> You're comparing apples to oranges.

Of course monitor mode lines are different from pwm_states. The
comparable thing is: Back then you had to specify the input for some
algorithm by hand. For the X-server a way was found to make that hand
picking unnecessary. This is possible for PWM states, too.

> The reason why you no longer have to configure modelines for X to work
> is because drivers can probe the monitor's EDID for the equivalent of
> those modelines.

With your reasoning it was not necessary to implement reading EDID data
and autotuning graphics driver. The system integrator of the monitor
could have just written an X-server configuration with the needed
values.

> For PWM the equivalent of EDID would be DT or PWM lookup tables. So PWM
> isn't the Neanderthal that you make it out to be.

Let's keep discussing the pwm-ir-tx driver. It has to pick a period
depending on the signal to transmit. So do you want to put the period to
pick for all relevant carrier frequencies in the device tree or a lookup
table?

Having pwm_round_{down,nearest}_state() is the more universal approach
here.

> But if you want to stick with the example of monitors: display drivers
> are not always able to exactly generate the required pixel clock for a
> given mode. The end result is that sometimes your vertical refresh rate
> of a monitor is not exactly the 60 Hz that you've asked for but perhaps
> 60.03 Hz or 59.27 Hz. And just like for PWM this doesn't matter in the
> majority of cases. In the cases where it does matter and you need
> perfect synchronization the system designers will have made sure that
> the target refresh rate can be met.

The system designers can provide a hand picked value to the driver in
this case. The better solution however is to teach the driver to pick
the right value alone and so save future system designers from having to
repeat the analysis by hand again and again for each new machine.

> That said, PWM_USAGE_POWER is a solution for a new problem. So far you
> are the one objecting to this improvement and the only reasons for
> objecting have been along the lines of "I don't like it", or "I think
> it's going to cause problems".

If that is what you understood, then you didn't get what I want.

The problem I see is that the concept of PWM_USAGE_POWER (apart from
having a bad name) is not well defined. Consumers don't know what they
can expect when requesting a USAGE_POWER state and lowlevel driver
authors don't know what to provide. It all depends on what you consider
reasonable when you consider applying a patch.

> > > Most of the time, values for
> > > period will be hand-picked to match the requirements of the use-case on
> > > a given platform (for backlights or LEDs, for example, you don't want a
> > > period that's too long, because then you'll get flicker). The duty cycle
> > > is then simply used as a way of getting a power output of the desired
> > > percentage. For something like PWM backlight, if interpolation doesn't
> > > work, you have the option of specifying discrete levels with hand-picked
> > > values.
> > > 
> > > Backlight and LEDs are the vast majority of applications for PWMs used
> > > in the kernel today. Another category would be regulators and they end
> > > up being pretty much the same in where the values come from.
> > > 
> > > The one use-case that's perhaps a bit more tricky is the sysfs interface
> > > because people can throw whatever they want at it. But even that is not
> > > likely to be problematic in practice because users will either be
> > > satisfied with the result that they get when computationally getting the
> > > numbers, or end up hand-picking values for those as well, with the only
> > > difference being that they are programmed from userspace.
> > 
> > So the PWM API doesn't provide what it should be there for: Abstracting
> > PWM hardware. Consumers seem to have to know exactly what hardware is
> > used and modify their usage of the API accordingly. (Or even adapt the
> > driver to do what matches their use-case break another one.) :-\
> 
> You seem to have a strange definition of what "abstraction" means. I
> don't even know what to reply to this.

I thought this is a common definition of abstraction. My idea of a good
abstraction is to be able to tell the consumer "here is a PWM" and they
can make use of it independently of the underlaying hardware in the
bounds of what the actual hardware permits. That is, the consumer should
be able to ask: What is the setting nearest to {.period = 1ms,
.duty_cycle = 500ns} that is actually implementable and then judge if
this is good enough or not for their use case. (Note this is different
from being able to say: Implement exactly {.period = 1ms, .duty_cycle =
500ns} what I have the impression is your view on my goal.)

What is your definition of abstraction?

> > > For the particular case of PWM_USAGE_POWER, I think it really only says
> > > that the power output of the signal should be as requested. It does not
> > > mean that the driver can pick whatever values it wants. Drivers should
> > > still try to match period and duty cycle as closely as possible because
> > > there's not enough other information to know if, for example, stretching
> > > the clock by a factor of 2 is reasonable for the use-case.
> > 
> > So if this factor of 2 is the best some given driver can provide, the
> > request should be declined, right? Where do you draw the line? Is 1.5
> > the biggest factor that should be allowed? IMHO this is a question we
> > should be ready to answer before driver authors start proposing patches.
> 
> I said the driver should still try to match period and duty cycle as
> closely as possible, but if stretching by a factor of 2 is the best it
> can do, then that's tough luck, but not a reason to reject the request.

I don't understand how a driver should pick this "closest possible"
setting because I don't consider it obvious what "closest possible"
means for you. For a PWM_USAGE_POWER request of { .period = 1045,
.duty_cyle = 680 } which of the following candidates is the closest
possible:

  - { .period = 104500, .duty_cycle = 68000 }

  - { .period = 1100, .duty_cycle = 700 }

  - { .period = 1000, .duty_cycle = 700 }

  - { .period = 1000, .duty_cycle = 600 }

  - { .period = 2000, .duty_cycle = 1300 }

? Or which of these would be acceptable? Or asked differently: If you
added support for PWM_USAGE_POWER to a driver for hardware where
.duty_cycle and .period must be integer factors of 100 ns, which state
would you configure in reply to the above request and why do you
consider it the best choice? Does it depend on the usecase you have in
mind while implementing, or is there an objective metric?

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] 42+ messages in thread

end of thread, other threads:[~2021-04-18 13:31 UTC | newest]

Thread overview: 42+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-04-12 13:27 [PATCH v8 1/8] pwm: pca9685: Switch to atomic API Clemens Gruber
2021-04-12 13:27 ` [PATCH v8 2/8] pwm: pca9685: Support hardware readout Clemens Gruber
2021-04-12 16:21   ` Uwe Kleine-König
2021-04-12 13:27 ` [PATCH v8 3/8] pwm: pca9685: Improve runtime PM behavior Clemens Gruber
2021-04-12 13:27 ` [PATCH v8 4/8] dt-bindings: pwm: Support new PWM_USAGE_POWER flag Clemens Gruber
2021-04-12 16:27   ` Uwe Kleine-König
2021-04-12 16:46     ` Clemens Gruber
2021-04-13 11:38       ` Uwe Kleine-König
2021-04-13 11:41         ` Thierry Reding
2021-04-13 11:51     ` Thierry Reding
2021-04-13 17:56       ` Uwe Kleine-König
2021-04-15 16:27         ` Thierry Reding
2021-04-16  9:32           ` Uwe Kleine-König
2021-04-16 10:45             ` Thierry Reding
2021-04-18 13:30               ` Uwe Kleine-König
2021-04-16 13:55   ` Thierry Reding
2021-04-16 15:39     ` Rob Herring
2021-04-16 15:54     ` Clemens Gruber
2021-04-17 15:44       ` Uwe Kleine-König
2021-04-12 13:27 ` [PATCH v8 5/8] pwm: core: " Clemens Gruber
2021-04-12 13:27 ` [PATCH v8 6/8] pwm: pca9685: " Clemens Gruber
2021-04-12 16:30   ` Uwe Kleine-König
2021-04-12 17:11     ` Clemens Gruber
2021-04-13 10:34       ` Uwe Kleine-König
2021-04-12 13:27 ` [PATCH v8 7/8] pwm: pca9685: Restrict period change for enabled PWMs Clemens Gruber
2021-04-17 15:46   ` Uwe Kleine-König
2021-04-12 13:27 ` [PATCH v8 8/8] pwm: pca9685: Add error messages for failed regmap calls Clemens Gruber
2021-04-17 15:47   ` Uwe Kleine-König
2021-04-12 16:18 ` [PATCH v8 1/8] pwm: pca9685: Switch to atomic API Uwe Kleine-König
2021-04-12 16:39   ` Clemens Gruber
2021-04-12 20:10     ` Uwe Kleine-König
2021-04-13 12:11       ` Clemens Gruber
2021-04-13 12:17         ` Clemens Gruber
2021-04-13 12:37         ` Thierry Reding
2021-04-13 13:06           ` Clemens Gruber
2021-04-13 19:38         ` Uwe Kleine-König
2021-04-14 12:09           ` Clemens Gruber
2021-04-14 19:21             ` Uwe Kleine-König
2021-04-14 19:45               ` Clemens Gruber
2021-04-15  6:48                 ` Uwe Kleine-König
2021-04-17 15:37 ` Uwe Kleine-König
2021-04-17 16:40   ` 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).