On Wed, Apr 07, 2021 at 08:12:29AM +0200, Uwe Kleine-König wrote: > On Tue, Apr 06, 2021 at 06:41:39PM +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@xxxxxxxxxxxx> > > --- > > 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 | 66 +++++++++++++++++++++++++++++++++------ > > 1 file changed, 56 insertions(+), 10 deletions(-) > > > > diff --git a/drivers/pwm/pwm-pca9685.c b/drivers/pwm/pwm-pca9685.c > > index 24221ee7a77a..cf0c98e4ef44 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); > > Maybe this is a bit more effective?: > > DECLARE_BITMAP(blablub, PCA9685_MAXCHAN + 1); > bitmap_zero(blablub, PCA9685_MAXCHAN + 1); > bitmap_set(blablub, channel); > return bitmap_subset(pca->pwms_enabled, blablub); But if no PWM is enabled, it should return true, not false. > (but that's a minor issue, the suggested algorithm is correct.) I would prefer to keep it explicit because it is a little easier to follow and probably not worth optimizing. > > So: > > Acked-by: Uwe Kleine-König <u.kleine-koenig@xxxxxxxxxxxxxx> Thanks. > > (side-note: I wonder if the handling of the set-all channel is correct > here. But given that it is messy anyhow, (e.g. because setting some > state to this set-all channel doesn't influence pwm_get_state for the > individual channels) I don't object if there is another problem in this > corner case. IMHO just dropping this virtual channel would be nice.) As you can't request the all channel and the individual channels together, there shouldn't be any problems. I agree that it would be nice to drop the ALL channel support. Clemens