[PATCH 4/5] counter: rz-mtu3-cnt: prevent counter from being toggled multiple times

From: Cosmin Tanislav

Date: Fri Jan 30 2026 - 07:26:03 EST


Runtime PM counter is incremented / decremented each time the sysfs
enable file is written to.

If user writes 0 to the sysfs enable file multiple times, runtime PM
usage count underflows, generating the following message.

rz-mtu3-counter rz-mtu3-counter.0: Runtime PM usage count underflow!

At the same time, hardware registers end up being accessed with clocks
off in rz_mtu3_terminate_counter() to disable an already disabled
channel.

If user writes 1 to the sysfs enable file multiple times, runtime PM
usage count will be incremented each time, requiring the same number of
0 writes to get it back to 0.

If user writes 0 to the sysfs enable file while PWM is in progress, PWM
is stopped without counter being the owner of the underlying MTU3
channel.

Check against the cached count_is_enabled value and exit if the user
is trying to set the same enable value.

Cc: stable@xxxxxxxxxxxxxxx
Fixes: 0be8907359df ("counter: Add Renesas RZ/G2L MTU3a counter driver")
Signed-off-by: Cosmin Tanislav <cosmin-gabriel.tanislav.xa@xxxxxxxxxxx>
---
drivers/counter/rz-mtu3-cnt.c | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/drivers/counter/rz-mtu3-cnt.c b/drivers/counter/rz-mtu3-cnt.c
index e755d54dfece..a4a8ef2d88f0 100644
--- a/drivers/counter/rz-mtu3-cnt.c
+++ b/drivers/counter/rz-mtu3-cnt.c
@@ -499,21 +499,25 @@ static int rz_mtu3_count_enable_write(struct counter_device *counter,
struct rz_mtu3_cnt *const priv = counter_priv(counter);
int ret = 0;

+ mutex_lock(&priv->lock);
+
+ if (priv->count_is_enabled[count->id] == enable)
+ goto exit;
+
if (enable) {
- mutex_lock(&priv->lock);
pm_runtime_get_sync(ch->dev);
ret = rz_mtu3_initialize_counter(counter, count->id);
if (ret == 0)
priv->count_is_enabled[count->id] = true;
- mutex_unlock(&priv->lock);
} else {
- mutex_lock(&priv->lock);
rz_mtu3_terminate_counter(counter, count->id);
priv->count_is_enabled[count->id] = false;
pm_runtime_put(ch->dev);
- mutex_unlock(&priv->lock);
}

+exit:
+ mutex_unlock(&priv->lock);
+
return ret;
}

--
2.52.0