On Fri, Sep 25, 2020 at 08:30:37AM +0200, Uwe Kleine-König wrote: > Hello Jonathan, [...] > > +config PWM_NTXEC > > + tristate "Netronix embedded controller PWM support" > > + depends on MFD_NTXEC > > + help > > + Say yes here if you want to support the PWM output of the embedded > > + controller found in certain e-book readers designed by the ODM > > + Netronix. > > Is it only me who had to look up what ODM means? If not, maybe spell it > out? I'm sure other readers will have the same problem. I'll spell it out. > > +/* > > + * The maximum input value (in nanoseconds) is determined by the time base and > > + * the range of the hardware registers that hold the converted value. > > + * It fits into 32 bits, so we can do our calculations in 32 bits as well. > > + */ > > +#define MAX_PERIOD_NS (TIME_BASE_NS * 0x10000 - 1) > > The maximal configurable period length is 0xffff, so I would have > expected MAX_PERIOD_NS to be 0xffff * TIME_BASE_NS? Due to the division rounding down, TIME_BASE_NS * 0x10000 - 1 would be the highest input that results in a representable value after the division, but I'm not sure it otherwise makes sense. > > > +static int ntxec_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm_dev, > > + const struct pwm_state *state) > > +{ > > + struct ntxec_pwm *pwm = pwmchip_to_pwm(pwm_dev->chip); > > + unsigned int duty = state->duty_cycle; > > + unsigned int period = state->period; > > + int res = 0; > > + > > I assume your device only supports normal polarity? If so, please check > for it here and point out this limitation in the header (in the format > that is for example used in pwm-sifive.c to make it easy to grep for > that). I haven't seen any indication that it supports inverted polarity. I'll point it out in the header comment, and add a check. > > > + if (period > MAX_PERIOD_NS) { > > + dev_warn(pwm->dev, > > + "Period is not representable in 16 bits after division by %u: %u\n", > > + TIME_BASE_NS, period); > > No error messages in .apply() please; this might spam the kernel log. > > Also the expectation when a too big period is requested is to configure > for the biggest possible period. So just do: > > if (period > MAX_PERIOD_NS) { > period = MAX_PERIOD_NS; > > if (duty > period) > duty = period; > } > > (or something equivalent). Okay, I'll adjust it. > > + /* > > + * Writing a duty cycle of zone puts the device into a state where > > What is "zone"? A mixture of zero and one and so approximately 0.5? Oops, that's a typo. I just meant "zero". > > + * writing a higher duty cycle doesn't result in the brightness that it > > + * usually results in. This can be fixed by cycling the ENABLE register. > > + * > > + * As a workaround, write ENABLE=0 when the duty cycle is zero. > > + */ > > + if (state->enabled && duty != 0) { > > + res = regmap_write(pwm->ec->regmap, NTXEC_REG_ENABLE, ntxec_reg8(1)); > > + if (res) > > + return res; > > + > > + /* Disable the auto-off timer */ > > + res = regmap_write(pwm->ec->regmap, NTXEC_REG_AUTO_OFF_HI, ntxec_reg8(0xff)); > > + if (res) > > + return res; > > + > > + return regmap_write(pwm->ec->regmap, NTXEC_REG_AUTO_OFF_LO, ntxec_reg8(0xff)); > > + } else { > > + return regmap_write(pwm->ec->regmap, NTXEC_REG_ENABLE, ntxec_reg8(0)); > > + } > > This code is wrong for state->enabled = false. Why? > How does the PWM behave when .apply is called? Does it complete the > currently running period? Can it happen that when you switch from say > > .duty_cycle = 900 * TIME_BASE_NS (0x384) > .period = 1800 * TIME_BASE_NS (0x708) > > to > > .duty_cycle = 300 * TIME_BASE_NS (0x12c) > .period = 600 * TIME_BASE_NS (0x258) > > that a period with > > .duty_cycle = 388 * TIME_BASE_NS (0x184) > .period = 1800 * TIME_BASE_NS (0x708) > > (because only NTXEC_REG_PERIOD_HIGH was written when the new period > started) or something similar is emitted? Changes take effect after the low byte is written, so a result like 0x184 in the above example should not happen. When the period and duty cycle are both changed, it temporarily results in an inconsistent state: - period = 1800ns, duty cycle = 900ns - period = 600ns, duty cycle = 900ns (!) - period = 600ns, duty cycle = 300ns The inconsistent state of duty cycle > period is handled gracefully by the EC and it outputs a 100% duty cycle, as far as I can tell. I currently don't have a logic analyzer / oscilloscope to measure whether we get full PWM periods, or some kind of glitch when the new period starts in the middle of the last one. > > +} > > + > > +static struct pwm_ops ntxec_pwm_ops = { > > + .apply = ntxec_pwm_apply, > > Please implement a .get_state() callback. And enable PWM_DEBUG during > your tests. The device doesn't support reading back the PWM state. What should a driver do in this case? > > + .owner = THIS_MODULE, > > +}; > > + > > +static int ntxec_pwm_probe(struct platform_device *pdev) > > +{ > > + struct ntxec *ec = dev_get_drvdata(pdev->dev.parent); > > + struct ntxec_pwm *pwm; > > Please don't call this variable pwm. I would expect that a variable with > this name is of type pwm_device. I would have called it "ddata" (and the > type would be named ntxec_pwm_ddata for me); another usual name is "priv". Ok, I'll rename it. > > + chip->npwm = 1; > > + > > + res = pwmchip_add(chip); > > + if (res < 0) > > + return res; > > + > > + platform_set_drvdata(pdev, pwm); > > If you do the platform_set_drvdata earlier you can just do > > return pwmchip_add(chip); Good idea, I'll do that. Thanks, Jonathan Neuschäfer
Attachment:
signature.asc
Description: PGP signature