The Linux Kernel Mailing List
 help / color / mirror / Atom feed
From: William Breathitt Gray <wbg@kernel.org>
To: Wadim Mueller <wafgo01@gmail.com>
Cc: William Breathitt Gray <wbg@kernel.org>,
	Krzysztof Kozlowski <krzk+dt@kernel.org>,
	Rob Herring <robh@kernel.org>, Conor Dooley <conor+dt@kernel.org>,
	linux-iio@vger.kernel.org, devicetree@vger.kernel.org,
	linux-kernel@vger.kernel.org
Subject: Re: [PATCH v7 2/3] counter: add GPIO-based counter driver
Date: Wed, 22 Jul 2026 20:49:34 +0900	[thread overview]
Message-ID: <20260722114936.849882-1-wbg@kernel.org> (raw)
In-Reply-To: <20260714081709.17846-3-wafgo01@gmail.com>

On Tue, Jul 14, 2026 at 10:17:08AM +0200, Wadim Mueller wrote:
> Add a platform driver that turns plain GPIOs into a counter device.
> Edge interrupts on signal-a, signal-b (and optional index) are
> decoded in software. Quadrature steps are validated with a 2-bit
> Gray-code parity check; direction comes from the same identity.
> 
> The driver exposes two Counts on the same wires:
> 
>   "Count 1" - Synapses on A, B and optional Index 1. Supports
>               QUADRATURE_X1_{A,B} / X2_{A,B} / X4, PULSE_DIRECTION,
>               INCREASE and DECREASE.
>   "Count 2" - Synapse on B and optional Index 2. Supports INCREASE
>               and DECREASE only.
> 
> Per-Count state (value, ceiling, preset, enable, function, direction)
> lives in struct gpio_counter_count_priv so new per-Count features
> (e.g. floor) can be added without touching parallel arrays.
> 
> Sleepable GPIO providers (I2C/SPI expanders) are rejected at probe
> since the ISRs run in hardirq context.
> 
> Signed-off-by: Wadim Mueller <wafgo01@gmail.com>

Hi Wadim,

The sashiko-bot picked up an issue I hadn't considered in the previous
review: 64-bit reads may be implemented as two 32-bit read operations on
32-bit systems resulting in a race condition. So it seems that we do
need to acquire priv->lock to protect access to u64 count_priv members
even when we simply read. As such, please add back the spinlock calls
from the previous version for the relevant u64 members (value, ceiling,
and preset).

A few more comments below.

> +static void gpio_counter_index_pulse(struct counter_device *counter,
> +				     enum gpio_counter_count_id count_id)
> +{
> +	struct gpio_counter_priv *priv = counter_priv(counter);
> +	struct gpio_counter_count_priv *cp = &priv->count_priv[count_id];
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&priv->lock, flags);
> +	if (cp->preset_enabled)
> +		cp->value = (cp->preset > cp->ceiling) ? cp->ceiling : cp->preset;
> +	spin_unlock_irqrestore(&priv->lock, flags);
> +
> +	counter_push_event(counter, COUNTER_EVENT_INDEX, count_id);
> +}
> +
> +static irqreturn_t gpio_counter_index1_isr(int irq, void *dev_id)
> +{
> +	gpio_counter_index_pulse(dev_id, GPIO_COUNTER_COUNT_1);
> +	return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t gpio_counter_index2_isr(int irq, void *dev_id)
> +{
> +	gpio_counter_index_pulse(dev_id, GPIO_COUNTER_COUNT_2);
> +	return IRQ_HANDLED;
> +}

The count_priv array holds the respective IRQ value in the idx_irq
member, so you can loop through the array to find the particular
count_priv element. We have only two Counts, so a simple if-else will
suffice. That allows you to avoid the two separate Index ISR and instead
repurpose the code in gpio_counter_index_pulse() as the sole Index ISR.
Maybe something like this:

	static irqreturn_t gpio_counter_index_isr(int irq, void *private)
	{
		struct counter_device *counter = private;
		struct gpio_counter_priv *priv = counter_priv(counter);
		struct gpio_counter_count_priv *cp = &priv->count_priv[0];
		unsigned long flags;
	
		if (cp->idx_irq != irq);
			cp = &priv->count_priv[1];
	
		spin_lock_irqsave(&priv->lock, flags);

		if (!cp->enabled) {
			spin_unlock_irqrestore(&priv->lock, flags);
			return IRQ_HANDLED;
		}

		if (cp->preset_enabled)
			cp->value = (cp->preset > cp->ceiling) ? cp->ceiling : cp->preset;

		spin_unlock_irqrestore(&priv->lock, flags);
	
		counter_push_event(counter, COUNTER_EVENT_INDEX, count_id);

		return IRQ_HANDLED;
	}

> +static int gpio_counter_setup_signals(struct gpio_counter_priv *priv)
> +{
> +	priv->signals[GPIO_COUNTER_SIGNAL_A].id   = GPIO_COUNTER_SIGNAL_A;
> +	priv->signals[GPIO_COUNTER_SIGNAL_A].name = "Signal A";
> +	priv->signals[GPIO_COUNTER_SIGNAL_B].id   = GPIO_COUNTER_SIGNAL_B;
> +	priv->signals[GPIO_COUNTER_SIGNAL_B].name = "Signal B";
> +
> +	if (priv->count_priv[GPIO_COUNTER_COUNT_1].has_index) {
> +		priv->signals[GPIO_COUNTER_SIGNAL_INDEX1].id =
> +			GPIO_COUNTER_SIGNAL_INDEX1;
> +		priv->signals[GPIO_COUNTER_SIGNAL_INDEX1].name = "Index 1";
> +	}
> +	if (priv->count_priv[GPIO_COUNTER_COUNT_2].has_index) {
> +		priv->signals[GPIO_COUNTER_SIGNAL_INDEX2].id =
> +			GPIO_COUNTER_SIGNAL_INDEX2;
> +		priv->signals[GPIO_COUNTER_SIGNAL_INDEX2].name = "Index 2";
> +	}
> +
> +	return 2 + priv->count_priv[GPIO_COUNTER_COUNT_1].has_index +
> +	       priv->count_priv[GPIO_COUNTER_COUNT_2].has_index;

As sashiko-bot pointed out, the Counter subsystem doesn't handle gaps in
the signals array. What you can try instead is use a local variable
num_signals to hold the current signals array offset:

	num_signals = 2;
	if (priv->count_priv[GPIO_COUNTER_COUNT_1].has_index) {
		priv->signals[i].id = GPIO_COUNTER_SIGNAL_INDEX1;
		priv->signals[i].name = "Index 1";
		i++;
	}
	if (priv->count_priv[GPIO_COUNTER_COUNT_2].has_index) {
		priv->signals[i].id = GPIO_COUNTER_SIGNAL_INDEX2;
		priv->signals[i].name = "Index 2";
		i++;
	}

	return i;

Now, to refer back to the correct signals element later you have three
options: loop through the signals array and check each id, or save the
signals offset to a member of count_priv so you can refer to it later.

I think the first option is the simplest because there are only two
Index Signals and a single if statement suffices: if priv->signals[2].id
is not GPIO_COUNTER_SIGNAL_INDEX1, then it is GPIO_COUNTER_SIGNAL_INDEX2
(and vice versa).

> +}
> +
> +static int gpio_counter_setup_synapses(struct gpio_counter_priv *priv)
> +{
> +	int n_c1 = 2, n_c2 = 1;
> +
> +	priv->synapses_count1[0].actions_list = gpio_counter_synapse_actions;
> +	priv->synapses_count1[0].num_actions =
> +		ARRAY_SIZE(gpio_counter_synapse_actions);
> +	priv->synapses_count1[0].signal = &priv->signals[GPIO_COUNTER_SIGNAL_A];
> +
> +	priv->synapses_count1[1].actions_list = gpio_counter_synapse_actions;
> +	priv->synapses_count1[1].num_actions =
> +		ARRAY_SIZE(gpio_counter_synapse_actions);
> +	priv->synapses_count1[1].signal = &priv->signals[GPIO_COUNTER_SIGNAL_B];
> +
> +	if (priv->count_priv[GPIO_COUNTER_COUNT_1].has_index) {
> +		priv->synapses_count1[2].actions_list =
> +			gpio_counter_index_synapse_actions;
> +		priv->synapses_count1[2].num_actions =
> +			ARRAY_SIZE(gpio_counter_index_synapse_actions);
> +		priv->synapses_count1[2].signal =
> +			&priv->signals[GPIO_COUNTER_SIGNAL_INDEX1];

You can use a ternary directly like this:

	priv->synapses_count1[2].signal = (priv->signals[2].id == GPIO_COUNTER_SIGNAL_INDEX1)
		? &priv->signals[2] : &priv->signals[3];

Or wrap it in a function to abstract it with a name like
gpio_counter_get_index_signal() or similar.

> +		n_c1 = 3;
> +	}
> +
> +	priv->synapses_count2[0].actions_list = gpio_counter_synapse_actions;
> +	priv->synapses_count2[0].num_actions =
> +		ARRAY_SIZE(gpio_counter_synapse_actions);
> +	priv->synapses_count2[0].signal = &priv->signals[GPIO_COUNTER_SIGNAL_B];
> +
> +	if (priv->count_priv[GPIO_COUNTER_COUNT_2].has_index) {
> +		priv->synapses_count2[1].actions_list =
> +			gpio_counter_index_synapse_actions;
> +		priv->synapses_count2[1].num_actions =
> +			ARRAY_SIZE(gpio_counter_index_synapse_actions);
> +		priv->synapses_count2[1].signal =
> +			&priv->signals[GPIO_COUNTER_SIGNAL_INDEX2];

Similar comment as above.

William Breathitt Gray

[^1]: https://lore.kernel.org/all/20260714082741.ACC001F000E9@smtp.kernel.org/

  parent reply	other threads:[~2026-07-22 11:49 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-14  8:17 [PATCH v7 0/3] counter: add GPIO-based counter driver Wadim Mueller
2026-07-14  8:17 ` [PATCH v7 1/3] dt-bindings: counter: add gpio-counter binding Wadim Mueller
2026-07-22 15:42   ` Rob Herring
2026-07-14  8:17 ` [PATCH v7 2/3] counter: add GPIO-based counter driver Wadim Mueller
2026-07-14 12:20   ` Uwe Kleine-König
2026-07-22 11:49   ` William Breathitt Gray [this message]
2026-07-22 16:00     ` William Breathitt Gray
2026-07-14  8:17 ` [PATCH v7 3/3] MAINTAINERS: add entry for GPIO " Wadim Mueller

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=20260722114936.849882-1-wbg@kernel.org \
    --to=wbg@kernel.org \
    --cc=conor+dt@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=krzk+dt@kernel.org \
    --cc=linux-iio@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=robh@kernel.org \
    --cc=wafgo01@gmail.com \
    /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