[PATCH 09/15] counter: rz-mtu3-cnt: cache count values

From: Cosmin Tanislav

Date: Mon Sep 14 2026 - 16:39:11 EST


If an MTU3 channel is used in counting mode, and then in PWM mode, when
returning to counting mode the count value is lost because the TCNT
register is overwritten by PWM mode.

To handle this issue, save the count value on disable and restore it on
enable, and also save it inside rz_mtu3_count_write().

To avoid writing the count value register when the count is disabled,
and to prepare for the removal of the rz_mtu3_lock_if_counter_is_valid()
function which is susceptible to TOCTOU races because of the ch->is_busy
check, only read or write the count value if the count is enabled.

Since runtime PM is ensured to be resumed if the count is enabled, drop
the pm_runtime_get_sync() and pm_runtime_put() calls.

Signed-off-by: Cosmin Tanislav <cosmin-gabriel.tanislav.xa@xxxxxxxxxxx>
---
drivers/counter/rz-mtu3-cnt.c | 42 +++++++++++++++++++++++++----------
1 file changed, 30 insertions(+), 12 deletions(-)

diff --git a/drivers/counter/rz-mtu3-cnt.c b/drivers/counter/rz-mtu3-cnt.c
index b76ad6cb8b6d..68025c6ed409 100644
--- a/drivers/counter/rz-mtu3-cnt.c
+++ b/drivers/counter/rz-mtu3-cnt.c
@@ -76,6 +76,7 @@ struct rz_mtu3_cnt {
struct mutex lock;
struct rz_mtu3_channel *ch;
u32 ceiling[RZ_MTU3_MAX_LOGICAL_CNTR_CHANNELS];
+ u32 count[RZ_MTU3_MAX_LOGICAL_CNTR_CHANNELS];
bool count_is_enabled[RZ_MTU3_MAX_LOGICAL_CNTR_CHANNELS];
};

@@ -161,6 +162,23 @@ static void rz_mtu3_set_ceiling(struct rz_mtu3_channel *const ch, int id,
rz_mtu3_8bit_ch_write(ch, RZ_MTU3_TCR, RZ_MTU3_TCR_CCLR_TGRA);
}

+static u32 rz_mtu3_get_count(struct rz_mtu3_channel *const ch, int id)
+{
+ if (id == RZ_MTU3_32_BIT_CH)
+ return rz_mtu3_32bit_ch_read(ch, RZ_MTU3_TCNTLW);
+ else
+ return rz_mtu3_16bit_ch_read(ch, RZ_MTU3_TCNT);
+}
+
+static void rz_mtu3_set_count(struct rz_mtu3_channel *const ch, int id,
+ u32 count)
+{
+ if (id == RZ_MTU3_32_BIT_CH)
+ rz_mtu3_32bit_ch_write(ch, RZ_MTU3_TCNTLW, count);
+ else
+ rz_mtu3_16bit_ch_write(ch, RZ_MTU3_TCNT, count);
+}
+
static int rz_mtu3_count_read(struct counter_device *counter,
struct counter_count *count, u64 *val)
{
@@ -172,12 +190,9 @@ static int rz_mtu3_count_read(struct counter_device *counter,
if (ret)
return ret;

- pm_runtime_get_sync(counter->parent);
- if (count->id == RZ_MTU3_32_BIT_CH)
- *val = rz_mtu3_32bit_ch_read(ch, RZ_MTU3_TCNTLW);
- else
- *val = rz_mtu3_16bit_ch_read(ch, RZ_MTU3_TCNT);
- pm_runtime_put(counter->parent);
+ if (priv->count_is_enabled[count->id])
+ priv->count[count->id] = rz_mtu3_get_count(ch, count->id);
+ *val = priv->count[count->id];
mutex_unlock(&priv->lock);

return 0;
@@ -194,12 +209,9 @@ static int rz_mtu3_count_write(struct counter_device *counter,
if (ret)
return ret;

- pm_runtime_get_sync(counter->parent);
- if (count->id == RZ_MTU3_32_BIT_CH)
- rz_mtu3_32bit_ch_write(ch, RZ_MTU3_TCNTLW, val);
- else
- rz_mtu3_16bit_ch_write(ch, RZ_MTU3_TCNT, val);
- pm_runtime_put(counter->parent);
+ if (priv->count_is_enabled[count->id])
+ rz_mtu3_set_count(ch, count->id, val);
+ priv->count[count->id] = val;
mutex_unlock(&priv->lock);

return 0;
@@ -386,6 +398,8 @@ static void rz_mtu3_32bit_cnt_setting(struct counter_device *counter)

rz_mtu3_set_ceiling(ch1, RZ_MTU3_32_BIT_CH,
priv->ceiling[RZ_MTU3_32_BIT_CH]);
+ rz_mtu3_set_count(ch1, RZ_MTU3_32_BIT_CH,
+ priv->count[RZ_MTU3_32_BIT_CH]);

rz_mtu3_8bit_ch_write(ch1, RZ_MTU3_TIOR, RZ_MTU3_TIOR_IC_BOTH);

@@ -402,6 +416,7 @@ static void rz_mtu3_16bit_cnt_setting(struct counter_device *counter, int id)
rz_mtu3_8bit_ch_write(ch, RZ_MTU3_TMDR1, RZ_MTU3_TMDR1_PH_CNT_MODE_1);

rz_mtu3_set_ceiling(ch, id, priv->ceiling[id]);
+ rz_mtu3_set_count(ch, id, priv->count[id]);

rz_mtu3_8bit_ch_write(ch, RZ_MTU3_TIOR, RZ_MTU3_TIOR_NO_OUTPUT);
rz_mtu3_enable(ch);
@@ -447,6 +462,9 @@ static void rz_mtu3_terminate_counter(struct counter_device *counter, int id)
struct rz_mtu3_channel *const ch = rz_mtu3_get_ch(counter, id);
struct rz_mtu3_channel *const ch1 = rz_mtu3_get_ch(counter, 0);
struct rz_mtu3_channel *const ch2 = rz_mtu3_get_ch(counter, 1);
+ struct rz_mtu3_cnt *const priv = counter_priv(counter);
+
+ priv->count[id] = rz_mtu3_get_count(ch, id);

if (id == RZ_MTU3_32_BIT_CH) {
rz_mtu3_disable(ch2);
--
2.55.0