public inbox for linux-arm-kernel@lists.infradead.org
 help / color / mirror / Atom feed
From: William Breathitt Gray <william.gray@linaro.org>
To: Fabrice Gasnier <fabrice.gasnier@foss.st.com>
Cc: lee@kernel.org, alexandre.torgue@foss.st.com,
	linux-iio@vger.kernel.org,
	linux-stm32@st-md-mailman.stormreply.com,
	linux-arm-kernel@lists.infradead.org,
	linux-kernel@vger.kernel.org, Will Deacon <will@kernel.org>,
	Peter Zijlstra <peterz@infradead.org>
Subject: Re: [PATCH v3 09/10] counter: stm32-timer-cnt: add support for overflow events
Date: Mon, 8 Jan 2024 21:00:37 +0000	[thread overview]
Message-ID: <ZZxidRueG8H/O7pw@ubuntu-server-vm-macos> (raw)
In-Reply-To: <20231220145726.640627-10-fabrice.gasnier@foss.st.com>


[-- Attachment #1.1: Type: text/plain, Size: 7462 bytes --]

On Wed, Dec 20, 2023 at 03:57:25PM +0100, Fabrice Gasnier wrote:
> Add support overflow events. Also add the related validation and
> configuration routine. Register and enable interrupts to push events.
> STM32 Timers can have either 1 global interrupt, or 4 dedicated interrupt
> lines. Request only the necessary interrupt, e.g. either global interrupt
> that can report all event types, or update interrupt only for overflow
> event.
> 
> Acked-by: Lee Jones <lee@kernel.org>
> Signed-off-by: Fabrice Gasnier <fabrice.gasnier@foss.st.com>

Hi Fabrice,

I've CC'd Will and Peter in case they can provide some suggestions
regarding my atomic_t comment inline below.

> @@ -44,6 +45,9 @@ struct stm32_timer_cnt {
>  	bool has_encoder;
>  	u32 idx;
>  	unsigned int nchannels;
> +	unsigned int nr_irqs;
> +	u32 *irq;

Looks like we only need this 'irq' array for registering the ISR in
stm32_timer_cnt_probe(). Since we won't need it anymore after that,
let's use ddata->irq directly instead of defining priv->irq.

> +	atomic_t nb_ovf;
>  };
>  
>  static const enum counter_function stm32_count_functions[] = {
> @@ -259,6 +263,29 @@ static int stm32_count_prescaler_write(struct counter_device *counter,
>  	return regmap_write(priv->regmap, TIM_PSC, psc);
>  }
>  
> +static int stm32_count_nb_ovf_read(struct counter_device *counter,
> +				   struct counter_count *count, u64 *val)
> +{
> +	struct stm32_timer_cnt *const priv = counter_priv(counter);
> +
> +	*val = atomic_read(&priv->nb_ovf);
> +
> +	return 0;
> +}
> +
> +static int stm32_count_nb_ovf_write(struct counter_device *counter,
> +				    struct counter_count *count, u64 val)
> +{
> +	struct stm32_timer_cnt *const priv = counter_priv(counter);
> +
> +	if (val != (typeof(priv->nb_ovf.counter))val)
> +		return -ERANGE;
> +
> +	atomic_set(&priv->nb_ovf, val);

So you want to check that the atomic_t 'nb_ovf' is able hold the value
provided by the u64 'val'. My understanding is that atomic_t should be
treated as an opaque type, so I don't think we should be accessing the
'counter' member directly for this test (interrupt-cnt does this but I
believe it's wrong to do so).

I don't know if we have any existing way to check for the value range of
an atomic_t (I don't see anything under include/linux/limits.h
specifically for it). However, you do use atomic_set() which takes an
int parameter, so perhaps we should compare against INT_MAX instead.

> +static int stm32_count_events_configure(struct counter_device *counter)
> +{
> +	struct stm32_timer_cnt *const priv = counter_priv(counter);
> +	struct counter_event_node *event_node;
> +	u32 val, dier = 0;
> +
> +	list_for_each_entry(event_node, &counter->events_list, l) {
> +		switch (event_node->event) {
> +		case COUNTER_EVENT_OVERFLOW_UNDERFLOW:
> +			/* first clear possibly latched UIF before enabling */
> +			regmap_read(priv->regmap, TIM_DIER, &val);
> +			if (!(val & TIM_DIER_UIE))

You can eliminate 'val' and the regmap_read() line like this:

    if (!regmap_test_bits(priv->regmap, TIM_DIER, TIM_DIER_UIE))

> +				regmap_write(priv->regmap, TIM_SR, (u32)~TIM_SR_UIF);
> +			dier |= TIM_DIER_UIE;
> +			break;
> +		default:
> +			/* should never reach this path */
> +			return -EINVAL;
> +		}
> +	}
> +
> +	regmap_write(priv->regmap, TIM_DIER, dier);

Do you want to overwrite TIM_DIER completely, or did you mean to set
only TIM_DIER_UIE and preserve the rest of the register? If the latter,
you could redefine 'dier' as a bool and do:

    regmap_update_bits(priv->regmap, TIM_DIER, TIM_DIER_UIE, dier);

There is also a regmap_update_bits_check() available if you want to
combine the UIF latch check with the update; but I don't know if that
will work in this case because it looks like you want to clear the UIF
latch before enabling.

>  static int stm32_count_clk_get_freq(struct counter_device *counter,
> @@ -418,6 +491,35 @@ static struct counter_count stm32_counts = {
>  	.num_ext = ARRAY_SIZE(stm32_count_ext)
>  };
>  
> +static irqreturn_t stm32_timer_cnt_isr(int irq, void *ptr)
> +{
> +	struct counter_device *counter = ptr;
> +	struct stm32_timer_cnt *const priv = counter_priv(counter);
> +	u32 clr = GENMASK(31, 0); /* SR flags can be cleared by writing 0 (wr 1 has no effect) */
> +	u32 sr, dier;
> +
> +	regmap_read(priv->regmap, TIM_SR, &sr);
> +	regmap_read(priv->regmap, TIM_DIER, &dier);
> +	/*
> +	 * Some status bits in SR don't match with the enable bits in DIER. Only take care of
> +	 * the possibly enabled bits in DIER (that matches in between SR and DIER).
> +	 */
> +	dier &= TIM_DIER_UIE;
> +	sr &= dier;
> +
> +	if (sr & TIM_SR_UIF) {

Am I understanding this logic correctly? ANDing TIM_DIER_UIE with 'dier'
will result in just the state of the TIM_DIER_UIE bit. Next, we AND that
state with 'sr'; so sr is 0 when TIM_DIER_UIE state is low, but we get
the respective SR bit when TIM_DIER_UIE state is high. Finally, we check
the TIM_SR_UIF bit state in 'sr'.

If TIM_SR_UIF bit position is expected to match the TIM_DIER_UIE bit
position, then (sr & TIM_SR_UIF) will only be true when the state of
both the TIM_DIER_UIE bit and TIM_SR_UIF bit are high. That means you
can eliminate 'sr', 'dier', and the two regmap_read() operations with
this instead:

    if (regmap_test_bits(priv->regmap, TIM_SR, TIM_SR_UIF) &&
        regmap_test_bits(priv->regmap, TIM_DIER, TIM_DIER_UIE) {

> +		atomic_inc(&priv->nb_ovf);

I wonder what happens when atomic_inc() increments past the atomic_t max
value. Does atomic_read() report back a negative value? Do we need to
guard against that scenario somehow?

> +		counter_push_event(counter, COUNTER_EVENT_OVERFLOW_UNDERFLOW, 0);
> +		dev_dbg(counter->parent, "COUNTER_EVENT_OVERFLOW_UNDERFLOW\n");
> +		/* SR flags can be cleared by writing 0, only clear relevant flag */
> +		clr &= ~TIM_SR_UIF;

You can use u32p_replace_bits(&clr, 0, TIM_SR_UIF) instead after
including the include/linux/bitfield.h header.

> @@ -511,6 +615,32 @@ static int stm32_timer_cnt_probe(struct platform_device *pdev)
>  
>  	platform_set_drvdata(pdev, priv);
>  
> +	/* STM32 Timers can have either 1 global, or 4 dedicated interrupts (optional) */
> +	if (priv->nr_irqs == 1) {
> +		/* All events reported through the global interrupt */
> +		ret = devm_request_irq(&pdev->dev, priv->irq[0], stm32_timer_cnt_isr,
> +				       0, dev_name(dev), counter);
> +		if (ret) {
> +			dev_err(dev, "Failed to request irq %d (err %d)\n",
> +				priv->irq[i], ret);

This should be irq[0], right?

I would also recommend using ddata->irq instead so we can get rid of
priv->irq outside of this probe function.

> +			return ret;
> +		}
> +	} else {
> +		for (i = 0; i < priv->nr_irqs; i++) {
> +			/* Only take care of update IRQ for overflow events */
> +			if (i != STM32_TIMERS_IRQ_UP)
> +				continue;
> +
> +			ret = devm_request_irq(&pdev->dev, priv->irq[i], stm32_timer_cnt_isr,
> +					       0, dev_name(dev), counter);
> +			if (ret) {
> +				dev_err(dev, "Failed to request irq %d (err %d)\n",
> +					priv->irq[i], ret);
> +				return ret;
> +			}
> +		}

So we only execute the loop body once for this particular
STM32_TIMERS_IRQ_UP iteration? Why have the loop at all rather than
hardcode irq[STM32_TIMERS_IRQ_UP] for devm_request_irq()?

William Breathitt Gray

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

[-- Attachment #2: Type: text/plain, Size: 176 bytes --]

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

  reply	other threads:[~2024-01-08 21:01 UTC|newest]

Thread overview: 26+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-12-20 14:57 [PATCH v3 00/10] counter: Add stm32 timer events support Fabrice Gasnier
2023-12-20 14:57 ` [PATCH v3 01/10] counter: stm32-timer-cnt: rename quadrature signal Fabrice Gasnier
2024-01-08 16:33   ` William Breathitt Gray
2023-12-20 14:57 ` [PATCH v3 02/10] counter: stm32-timer-cnt: rename counter Fabrice Gasnier
2024-01-08 16:34   ` William Breathitt Gray
2023-12-20 14:57 ` [PATCH v3 03/10] counter: stm32-timer-cnt: adopt signal definitions Fabrice Gasnier
2024-01-08 16:34   ` William Breathitt Gray
2023-12-20 14:57 ` [PATCH v3 04/10] counter: stm32-timer-cnt: introduce clock signal Fabrice Gasnier
2024-01-08 16:46   ` William Breathitt Gray
2024-02-27 17:43     ` Fabrice Gasnier
2024-02-29 18:58       ` William Breathitt Gray
2023-12-20 14:57 ` [PATCH v3 05/10] counter: stm32-timer-cnt: add counter prescaler extension Fabrice Gasnier
2024-01-08 16:48   ` William Breathitt Gray
2023-12-20 14:57 ` [PATCH v3 06/10] counter: stm32-timer-cnt: add checks on quadrature encoder capability Fabrice Gasnier
2024-01-08 16:59   ` William Breathitt Gray
2023-12-20 14:57 ` [PATCH v3 07/10] counter: stm32-timer-cnt: introduce channels Fabrice Gasnier
2024-01-08 17:21   ` William Breathitt Gray
2023-12-20 14:57 ` [PATCH v3 08/10] counter: stm32-timer-cnt: probe number of channels from registers Fabrice Gasnier
2024-01-08 17:25   ` William Breathitt Gray
2023-12-20 14:57 ` [PATCH v3 09/10] counter: stm32-timer-cnt: add support for overflow events Fabrice Gasnier
2024-01-08 21:00   ` William Breathitt Gray [this message]
2024-02-27 17:43     ` Fabrice Gasnier
2024-01-08 21:06   ` William Breathitt Gray
2023-12-20 14:57 ` [PATCH v3 10/10] counter: stm32-timer-cnt: add support for capture events Fabrice Gasnier
2024-01-08 22:07   ` William Breathitt Gray
2024-02-27 17:43     ` Fabrice Gasnier

Reply instructions:

You may reply publicly 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=ZZxidRueG8H/O7pw@ubuntu-server-vm-macos \
    --to=william.gray@linaro.org \
    --cc=alexandre.torgue@foss.st.com \
    --cc=fabrice.gasnier@foss.st.com \
    --cc=lee@kernel.org \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-iio@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-stm32@st-md-mailman.stormreply.com \
    --cc=peterz@infradead.org \
    --cc=will@kernel.org \
    /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
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox