Hi Uwe, Am Wed, Mar 19, 2025 at 04:37:43PM +0100 schrieb Uwe Kleine-König: [...] > > +#define MC33XS2410_PWM_CTRL1 0x05 > > +/* x in { 1 ... 4 } */ > > s/x/chan/ > Will fix it. > > +#define MC33XS2410_PWM_CTRL1_POL_INV(chan) BIT((chan) + 1) > > + > > +#define MC33XS2410_PWM_CTRL3 0x07 > > +/* x in { 1 ... 4 } */ > > +#define MC33XS2410_PWM_CTRL3_EN(chan) BIT(4 + (chan) - 1) > > + > > +/* x in { 1 ... 4 } */ > > +#define MC33XS2410_PWM_FREQ(chan) (0x08 + (chan) - 1) > > +#define MC33XS2410_PWM_FREQ_STEP GENMASK(7, 6) > > +#define MC33XS2410_PWM_FREQ_COUNT GENMASK(5, 0) > > + > > +/* x in { 1 ... 4 } */ > > +#define MC33XS2410_PWM_DC(chan) (0x0c + (chan) - 1) > > + > > +#define MC33XS2410_WDT 0x14 > > + > > +#define MC33XS2410_PWM_MIN_PERIOD 488282 > > +/* x in { 0 ... 3 } */ > > s/x/step/ > Will fix it. [...] > > +static u8 mc33xs2410_pwm_get_freq(u64 period) > > +{ > > + u8 step, count; > > + > > + /* > > + * Check which step is appropriate for the given period, starting with > > + * the highest frequency(lowest period). Higher frequencies are > > + * represented with better resolution by the device. Therefore favor > > + * frequency range with the better resolution to minimize error > > + * introduced by the frequency steps. > > This is hard to understand as the argument is presented in several > steps: > algo starts with high step values (that's not in the comment, > you have to take that from the switch statement) > high step represents high freq = low period > high freq yield better resolution > better resolution is what is favoured. > > After investing some time to reunderstand the rational here > I suggest: > > /* > * Check which step [0 .. 3] is appropriate for the given > * period. The period ranges for the different step values > * overlap. Prefer big step values as these allow more > * finegrained duty cycle selection. > */ > I think this is way better then before. But did you mean: Prefer big step values as these allow more finegrained "periods" then "duty cylces" ? [...] > > + > > + /* > > + * steps: > > + * - 0 = 0.5Hz > > + * - 1 = 2Hz > > + * - 2 = 8Hz > > + * - 3 = 32Hz > > + * frequency = (code + 1) x steps. > > + * > > + * To avoid losing precision in case steps value is zero, scale the > > + * steps value for now by two and keep it in mind when calculating the > > + * period that the frequency had been doubled. > > + */ > > + doubled_steps = 1 << (FIELD_GET(MC33XS2410_PWM_FREQ_STEP, reg) * 2); > > + code = FIELD_GET(MC33XS2410_PWM_FREQ_COUNT, reg); > > + freq = (code + 1) * doubled_steps; > > doubled_freq? > Will change to doubled_freq. > > + /* Convert frequency to period, considering the doubled frequency. */ > > + return DIV_ROUND_UP(2 * NSEC_PER_SEC, freq); > > +} > > + > > +static int mc33xs2410_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, > > + const struct pwm_state *state) > > +{ > > + struct spi_device *spi = pwmchip_get_drvdata(chip); > > + u8 reg[4] = { > > + MC33XS2410_PWM_FREQ(pwm->hwpwm + 1), > > + MC33XS2410_PWM_DC(pwm->hwpwm + 1), > > + MC33XS2410_PWM_CTRL1, > > + MC33XS2410_PWM_CTRL3 > > + }; > > + u64 period, duty_cycle; > > + int ret, rel_dc; > > + u16 rd_val[2]; > > + u8 wr_val[4]; > > + u8 mask; > > + > > + period = min(state->period, MC33XS2410_PWM_MAX_PERIOD(0)); > > + if (period < MC33XS2410_PWM_MIN_PERIOD) > > + return -EINVAL; > > + > > + ret = mc33xs2410_read_regs(spi, ®[2], MC33XS2410_FRAME_IN_DATA_RD, rd_val, 2); > > + if (ret < 0) > > + return ret; > > + > > + /* frequency */ > > + wr_val[0] = mc33xs2410_pwm_get_freq(period); > > + /* Continue calculations with the possibly truncated period */ > > + period = mc33xs2410_pwm_get_period(wr_val[0]); > > + > > + /* duty cycle */ > > + duty_cycle = min(period, state->duty_cycle); > > + rel_dc = div64_u64(duty_cycle * 256, period) - 1; > > + if (rel_dc < 0) > > + wr_val[1] = 0; > > + else > > + wr_val[1] = rel_dc; > > + > > + /* polarity */ > > + mask = MC33XS2410_PWM_CTRL1_POL_INV(pwm->hwpwm + 1); > > + wr_val[2] = (state->polarity == PWM_POLARITY_INVERSED) ? > > + (rd_val[0] | mask) : (rd_val[0] & ~mask); > > + > > + /* > > + * As the hardware cannot generate a 0% relative duty cycle but emits a > > + * constant low signal when disabled, also disable in the duty_cycle = 0 > > + * case. > > + */ > > + mask = MC33XS2410_PWM_CTRL3_EN(pwm->hwpwm + 1); > > + wr_val[3] = (state->enabled && rel_dc >= 0) ? (rd_val[1] | mask) : > > + (rd_val[1] & ~mask); > > This is wrong for inversed polarity I think. Thanks for finding this. Yes, it is for the case when duty_cycle=0 and polarity=PWM_POLARITY_INVERSED. The device should then output a constant high signal. This could be done by: enabled=1 duty_cycle=100% polarity=normal Just tested it, it works. What do you think ? > > > + return mc33xs2410_write_regs(spi, reg, wr_val, 4); > > +} > > + > > [..] > > +static int mc33xs2410_probe(struct spi_device *spi) > > +{ > > + struct device *dev = &spi->dev; > > + struct pwm_chip *chip; > > + int ret; > > + > > + chip = devm_pwmchip_alloc(dev, 4, 0); > > + if (IS_ERR(chip)) > > + return PTR_ERR(chip); > > + > > + spi->bits_per_word = 16; > > + spi->mode |= SPI_CS_WORD; > > + ret = spi_setup(spi); > > + if (ret < 0) > > + return ret; > > + > > + pwmchip_set_drvdata(chip, spi); > > + chip->ops = &mc33xs2410_pwm_ops; > > + ret = mc33xs2410_reset(dev); > > What does this reset? Does it change the output signal if the bootloader > already setup the hardware? The answer to that has to go into a comment. > (And if it interupts the output, better skip the reset part.) > If reset of the device is asserted on probe it deasserts the reset and makes the device available. On the other hand if it is already setup by the bootloader the reset will clear all registers to default values which means that the output signal will change. The reset gpio is optional, so the user can decide wether to use it or not. Both usecases are supported. I will add an comment, but will keep the reset handling. Best regards, Dimitri Fedrau