Linux-Hwmon Archive on lore.kernel.org
 help / color / Atom feed
From: Jeff LaBundy <jeff@labundy.com>
To: "Uwe Kleine-König" <u.kleine-koenig@pengutronix.de>
Cc: lee.jones@linaro.org, dmitry.torokhov@gmail.com,
	jdelvare@suse.com, linux@roeck-us.net, thierry.reding@gmail.com,
	jic23@kernel.org, devicetree@vger.kernel.org,
	linux-input@vger.kernel.org, linux-hwmon@vger.kernel.org,
	linux-pwm@vger.kernel.org, knaack.h@gmx.de, lars@metafoo.de,
	pmeerw@pmeerw.net, linux-iio@vger.kernel.org, robh+dt@kernel.org,
	mark.rutland@arm.com
Subject: Re: [PATCH 5/8] pwm: Add support for Azoteq IQS620A PWM generator
Date: Tue, 22 Oct 2019 21:45:25 -0500
Message-ID: <20191023024525.GC3233@labundy.com> (raw)
In-Reply-To: <20191022065415.2zxmpbsmogvgul7x@pengutronix.de>

Hi Uwe,

On Tue, Oct 22, 2019 at 08:54:15AM +0200, Uwe Kleine-König wrote:
> Hello Jeff,
> 
> On Mon, Oct 21, 2019 at 11:36:49PM -0500, Jeff LaBundy wrote:
> > On Mon, Oct 21, 2019 at 09:34:19AM +0200, Uwe Kleine-König wrote:
> > > > +struct iqs620_pwm_private {
> > > > +	struct iqs62x_core *iqs62x;
> > > > +	struct pwm_chip chip;
> > > > +	struct notifier_block notifier;
> > > > +	bool ready;
> > > 
> > > This is always true, so you can drop it.
> > > 
> > 
> > This is here because iqs620_pwm_notifier references chip.pwms, which is
> > not allocated until after the notifier is registered and pwmchip_add is
> > called. So it protects against this (albeit unlikely) race condition:
> > 
> > 1. iqs620_pwm_notifier is registered
> > 2. Device immediately suffers an asynchronous reset and notifier chain
> >    is called (more on that in a bit)
> > 3. iqs620_pwm_notifier evaluates chips.pwms (NULL)
> > 
> > I felt this was simpler than calling pwmchip_add before registering the
> > notifier and adding an error/tear-down path in iqs620_pwm_probe in case
> > of failure. I would be happy to add a comment or two to explain the not-
> > so-obvious purpose of this flag.
> 
> Ah, understood. A comment is definitively necessary here.
> 

Sure thing; will do.

> > > > +};
> > > > +
> > > > +static int iqs620_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
> > > > +			    struct pwm_state *state)
> > > 
> > > Since
> > > 
> > > 	71523d1812ac ("pwm: Ensure pwm_apply_state() doesn't modify the state argument")
> > > 
> > > this isn't the right prototype.
> > > 
> > 
> > Sure thing; I will add the 'const' qualifier and remove the two changes
> > to the state argument.
> > 
> > > > +{
> > > > +	struct iqs620_pwm_private *iqs620_pwm;
> > > > +	struct iqs62x_core *iqs62x;
> > > > +	int error;
> > > > +	int duty_calc = state->duty_cycle * 256 / IQS620_PWM_PERIOD_NS - 1;
> > > > +	u8 duty_clamp = clamp(duty_calc, 0, 0xFF);
> 
> Another problem that we have here is that the period is fixed to 1 ms
> and if a consumer requests for example:
> 
> 	.period = 5000000,
> 	.duty_cycle = 1000000,
> 
> the hardware is actually configured for
> 
> 	.period = 1000000,
> 	.duty_cycle = 1000000,
> 
> . I don't have a good suggestion how to fix this. We'd need to
> draw a line somewhere and decline a request that is too far from the
> result. But where this line should be is not obvious, it should
> definitively not be implemented in the driver itself IMHO.
> 
> (The only halfway sane approach would be to let lowlevel drivers
> implement a .round_state callback and then let the framework judge. But
> we're a long way from having that, so that's not a solution for today.)
> 

Agreed on all counts. For now, I will mention in the 'Limitations' heading that
the period cannot be adjusted.

> > > > +	iqs620_pwm = container_of(chip, struct iqs620_pwm_private, chip);
> > > > +	iqs62x = iqs620_pwm->iqs62x;
> > > > +
> > > > +	error = regmap_write(iqs62x->map, IQS620_PWM_DUTY_CYCLE, duty_clamp);
> > > > +	if (error)
> > > > +		return error;
> > > > +
> > > > +	state->period = IQS620_PWM_PERIOD_NS;
> > > > +	state->duty_cycle = (duty_clamp + 1) * IQS620_PWM_PERIOD_NS / 256;
> > > 
> > > This suggests that if the value in the IQS620_PWM_DUTY_CYCLE is 0 the
> > > duty cycle is 1/256 ms with a period of 1 ms and the output cannot be
> > > constant inactive. If this is right please add a paragraph in the
> > > driver's comment at the top:
> > > 
> > > 	* Limitations:
> > > 	* - The hardware cannot generate a 0% duty cycle
> > > 
> > > (Please stick to this format, other drivers use it, too.)
> > 
> > That's correct; the lowest duty cycle that can be achieved using only the
> > IQS620_PWM_DUTY_CYCLE register is 0.4%. We can, however, generate 0% duty
> > cycle by disabling the output altogether using a separate register. Would
> > that be better than flat-out saying it's impossible?
> 
> There is (maybe) a small difference between disabled and 0% duty cycle,
> at least from the framework's POV: If you do:
> 
> 	pwm_apply_state(pwm, { .enabled = true, .period = 1000000, .duty_cycle = 1000000, });
> 	pwm_apply_state(pwm, { .enabled = false, .period = $DC, .duty_cycle = $DC, });
> 	pwm_apply_state(pwm, { .enabled = true, .period = 1000000, .duty_cycle = 1000000, });
> 
> and compare it to the expected result of
> 
> 	pwm_apply_state(pwm, { .enabled = true, .period = 1000000, .duty_cycle = 1000000, });
> 	pwm_apply_state(pwm, { .enabled = true, .period = 1000000, .duty_cycle = 0, });
> 	pwm_apply_state(pwm, { .enabled = true, .period = 1000000, .duty_cycle = 1000000, });
> 
> the difference is that the duration of the inactive phase in the latter
> case is a multiple of 1 ms.
> 
> There is no policy for lowlevel drivers what to do, but disabling when
> 0% is requested is at least not unseen and probably more what consumers
> expect.
> 

With the change I am proposing, the output will be driven to zero if enabled = false
OR duty_cycle < 4000 ns. Stated another way:

enable duty_cycle IQS620_PWR_SETTINGS[7] IQS620_PWM_DUTY_CYCLE
------ ---------- ---------------------- ---------------------
  0    don't care           0                  don't care
  1    0 ... 3999           0                  don't care
  1    4000 ... x           1                      0
  1    x+1  ... y           1                      1

...and so on. For context, if IQS620_PWR_SETTINGS[7] = 0 then the output is held to
zero. If IQS620_PWR_SETTINGS[7] = 1 then the output toggles at a duty cycle between
0.4% and 100% as a function of IQS620_PWM_DUTY_CYCLE.

Based on how the device behaves in response to its two available registers, I think
your two examples will appear equal, but please let me know if I have understood.

> > > How does the hardware behave on changes? For example you're first
> > > committing the duty cycle and then on/off. Can it happen that between
> > > 
> > > 	pwm_apply_state(pwm, { .duty_cycle = 3900, .period = 1000000, .enabled = true)
> > > 	...
> > > 	pwm_apply_state(pwm, { .duty_cycle = 1000000, .period = 1000000, .enabled = false)
> > > 
> > > the output is active for longer than 4 µs because the iqs620_pwm_apply
> > > function is preempted between the two register writes and so we already
> > > have .duty_cycle = 1000000 but still .enabled = true in the hardware?
> > > 
> > 
> > My results show that it is possible to generate up to two irregular periods
> > by changing the duty cycle while the output is active.
> > 
> > Depending on the ratio of old-to-new duty cycle and the position of the I2C
> > write relative to the asynchronous output, the device may produce one pulse
> > for which the width represents neither the old nor the new duty cycle.
> > 
> > > Does a change complete the currently running period? Does disabling
> > > complete the currently running period? If so, does regmap_update_bits
> > > block until the new setting is active?
> > > 
> > 
> > A quick test reveals the following:
> > 
> > * Duty cycle changes may interrupt a running period, i.e., the output may
> >   transition in the middle of the period to accommodate the new duty cycle.
> > * Disabling the output drives it to zero immediately, i.e., the period does
> >   does not run to completion.
> > 
> > I will add a 'Limitations' section at the top as other drivers do, and call
> > these points out specifically.
> 
> Great. Thanks.
> 
> > > > +static int iqs620_pwm_notifier(struct notifier_block *notifier,
> > > > +			       unsigned long event_flags, void *context)
> > > > +{
> > > > +	struct iqs620_pwm_private *iqs620_pwm;
> > > > +	struct pwm_state state;
> > > > +	int error;
> > > > +
> > > > +	iqs620_pwm = container_of(notifier, struct iqs620_pwm_private,
> > > > +				  notifier);
> > > > +
> > > > +	if (!iqs620_pwm->ready || !(event_flags & BIT(IQS62X_EVENT_SYS_RESET)))
> > > > +		return NOTIFY_DONE;
> > > > +
> > > > +	pwm_get_state(&iqs620_pwm->chip.pwms[0], &state);
> > > > +
> > > > +	error = iqs620_pwm_apply(&iqs620_pwm->chip,
> > > > +				 &iqs620_pwm->chip.pwms[0], &state);
> > > > +	if (error) {
> > > > +		dev_err(iqs620_pwm->chip.dev,
> > > > +			"Failed to re-initialize device: %d\n", error);
> > > > +		return NOTIFY_BAD;
> > > > +	}
> > > > +
> > > > +	return NOTIFY_OK;
> > > 
> > > So the PWM can loose it's state sometimes? When does that happen?
> > 
> > That's correct. The device performs an internal soft reset in the presence
> > of what it considers to be an I2C timeout error; in this case all registers
> > are restored to their default values.
> 
> Is this a theoretic problem or does that happen from time to time?
>  

This event can occur if the I2C master stalls a transaction for 10's of ms. It's
not a theoretical problem, but it should not happen during normal circumstances.

> > The data sheet goes so far as to recommend monitoring for this interrupt and
> > restoring the device on-the-fly. I have added some comments in iqs62x_irq in
> > patch [2/8] which provides some further detail.
> 
> Monitoring that interrupt seems reasonable.
>  
> > > > +	error = devm_add_action_or_reset(&pdev->dev,
> > > > +					 iqs620_pwm_notifier_unregister,
> > > > +					 iqs620_pwm);
> > > 
> > > I wonder if this is safe. If in iqs620_pwm_notifier_unregister()
> > > unregistering of the notifier goes wrong (not sure when this can happen)
> > > the memory behind iqs620_pwm goes away. Then later iqs620_pwm_notifier
> > > might be called trying to use *iqs620_pwm ...
> > 
> > I think this is purely theoretical, as blocking_notifier_chain_unregister
> > only fails if the notifier is not found in the chain. If for some reason
> > blocking_notifier_chain_register fails (which currently cannot happen, as
> > it always returns zero), the driver will fail to probe before the action
> > could be added.
> > 
> > This of course means the error message in iqs620_pwm_notifier_unregister
> > is unnecessary; it is simply provided for debug/visibility.
> 
> I'd suggest to do the unregister call in the remove callback which you
> have for pwm unregistration anyhow. Or alternatively implement a devm_
> variant of the notifier registration that explains in the comments that
> it is safe.

Sure thing; I'll unregister the notifier in iqs620_pwm_remove.

> 
> Best regards
> Uwe
> 
> -- 
> Pengutronix e.K.                           | Uwe Kleine-König            |
> Industrial Linux Solutions                 | http://www.pengutronix.de/  |
> 

Kind regards,
Jeff LaBundy

  reply index

Thread overview: 37+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-10-21  4:11 [PATCH 0/8] Add support for Azoteq IQS620A/621/622/624/625 Jeff LaBundy
2019-10-21  4:11 ` [PATCH 1/8] dt-bindings: mfd: iqs62x: Add bindings Jeff LaBundy
2019-10-22 11:00   ` Jonathan Cameron
2019-10-23  3:36     ` Jeff LaBundy
2019-10-23  9:30       ` Lee Jones
2019-10-24  2:38         ` Jeff LaBundy
2019-10-21  4:11 ` [PATCH 2/8] mfd: Add support for Azoteq IQS620A/621/622/624/625 Jeff LaBundy
2019-10-31 13:44   ` Lee Jones
2019-10-31 18:42     ` Dmitry Torokhov
2019-11-01  4:59     ` Jeff LaBundy
2019-11-01  8:56       ` Lee Jones
2019-11-02  2:49         ` Jeff LaBundy
2019-10-21  4:11 ` [PATCH 3/8] input: keyboard: " Jeff LaBundy
2019-10-23  0:22   ` Dmitry Torokhov
2019-10-23  1:29     ` Jeff LaBundy
2019-10-23 23:08       ` Dmitry Torokhov
2019-10-21  4:11 ` [PATCH 4/8] hwmon: Add support for Azoteq IQS620AT temperature sensor Jeff LaBundy
2019-10-21 15:38   ` Guenter Roeck
2019-10-22  2:26     ` Jeff LaBundy
2019-10-22  3:22       ` Guenter Roeck
2019-10-22 11:38         ` Jonathan Cameron
2019-10-23  2:04           ` Jeff LaBundy
2019-10-21  4:11 ` [PATCH 5/8] pwm: Add support for Azoteq IQS620A PWM generator Jeff LaBundy
2019-10-21  7:34   ` Uwe Kleine-König
2019-10-22  4:36     ` Jeff LaBundy
2019-10-22  6:54       ` Uwe Kleine-König
2019-10-23  2:45         ` Jeff LaBundy [this message]
2019-10-23  7:23           ` Uwe Kleine-König
2019-10-24  3:02             ` Jeff LaBundy
2019-10-21  4:11 ` [PATCH 6/8] iio: light: Add support for Azoteq IQS621 ambient light sensor Jeff LaBundy
2019-10-22 11:23   ` Jonathan Cameron
2019-10-23  2:59     ` Jeff LaBundy
2019-10-21  4:11 ` [PATCH 7/8] iio: proximity: Add support for Azoteq IQS622 proximity sensor Jeff LaBundy
2019-10-22 11:23   ` Jonathan Cameron
2019-10-23  3:09     ` Jeff LaBundy
2019-10-21  4:11 ` [PATCH 8/8] iio: position: Add support for Azoteq IQS624/625 angle sensor Jeff LaBundy
2019-10-22 11:28   ` Jonathan Cameron

Reply instructions:

You may reply publically to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20191023024525.GC3233@labundy.com \
    --to=jeff@labundy.com \
    --cc=devicetree@vger.kernel.org \
    --cc=dmitry.torokhov@gmail.com \
    --cc=jdelvare@suse.com \
    --cc=jic23@kernel.org \
    --cc=knaack.h@gmx.de \
    --cc=lars@metafoo.de \
    --cc=lee.jones@linaro.org \
    --cc=linux-hwmon@vger.kernel.org \
    --cc=linux-iio@vger.kernel.org \
    --cc=linux-input@vger.kernel.org \
    --cc=linux-pwm@vger.kernel.org \
    --cc=linux@roeck-us.net \
    --cc=mark.rutland@arm.com \
    --cc=pmeerw@pmeerw.net \
    --cc=robh+dt@kernel.org \
    --cc=thierry.reding@gmail.com \
    --cc=u.kleine-koenig@pengutronix.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link

Linux-Hwmon Archive on lore.kernel.org

Archives are clonable:
	git clone --mirror https://lore.kernel.org/linux-hwmon/0 linux-hwmon/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 linux-hwmon linux-hwmon/ https://lore.kernel.org/linux-hwmon \
		linux-hwmon@vger.kernel.org
	public-inbox-index linux-hwmon

Example config snippet for mirrors

Newsgroup available over NNTP:
	nntp://nntp.lore.kernel.org/org.kernel.vger.linux-hwmon


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git