* [PATCH 00/15] MTU3 counter fixes and improvements
@ 2026-09-14 20:35 Cosmin Tanislav
2026-09-14 20:36 ` [PATCH 01/15] counter: rz-mtu3-cnt: put runtime PM on initialization error path Cosmin Tanislav
` (15 more replies)
0 siblings, 16 replies; 20+ messages in thread
From: Cosmin Tanislav @ 2026-09-14 20:35 UTC (permalink / raw)
To: Biju Das, William Breathitt Gray, Lee Jones
Cc: linux-iio, linux-renesas-soc, linux-kernel, Cosmin Tanislav
This series does some code quality improvements to the MTU3 counter
subdriver, while also fixing bugs and adding caching support so that
registers are not touched when the counter is disabled, as there is no
request API.
Patch 3 relies on the following series to enable runtime PM on the
parent device so that the PM domain handles it automatically.
https://lore.kernel.org/lkml/20260914201456.2018403-1-cosmin-gabriel.tanislav.xa@renesas.com/
Cosmin Tanislav (15):
counter: rz-mtu3-cnt: put runtime PM on initialization error path
counter: rz-mtu3-cnt: do not assign struct rz_mtu3_channel::dev
counter: rz-mtu3-cnt: remove manual runtime PM handling
counter: rz-mtu3-cnt: use device-managed pm_runtime_enable()
counter: rz-mtu3-cnt: read enable value from cache
counter: rz-mtu3-cnt: disable channel before releasing
counter: rz-mtu3-cnt: unify ceiling values
counter: rz-mtu3-cnt: cache ceiling values
counter: rz-mtu3-cnt: cache count values
counter: rz-mtu3-cnt: cache function values
counter: rz-mtu3-cnt: cache external input phase clock value
counter: rz-mtu3-cnt: cache direction values
counter: rz-mtu3-cnt: keep cascade_counts_enable in sync with usage
counter: rz-mtu3-cnt: drop conditional locks
counter: rz-mtu3-cnt: use pm_runtime_resume_and_get()
drivers/counter/rz-mtu3-cnt.c | 443 +++++++++++-----------------------
1 file changed, 139 insertions(+), 304 deletions(-)
--
2.55.0
^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH 01/15] counter: rz-mtu3-cnt: put runtime PM on initialization error path
2026-09-14 20:35 [PATCH 00/15] MTU3 counter fixes and improvements Cosmin Tanislav
@ 2026-09-14 20:36 ` Cosmin Tanislav
2026-09-17 0:46 ` Jonathan Cameron
2026-09-14 20:36 ` [PATCH 02/15] counter: rz-mtu3-cnt: do not assign struct rz_mtu3_channel::dev Cosmin Tanislav
` (14 subsequent siblings)
15 siblings, 1 reply; 20+ messages in thread
From: Cosmin Tanislav @ 2026-09-14 20:36 UTC (permalink / raw)
To: Biju Das, William Breathitt Gray, Lee Jones
Cc: linux-iio, linux-renesas-soc, linux-kernel, Cosmin Tanislav, stable
If rz_mtu3_initialize_counter() fails, the runtime PM usage count is not
decremented.
rz_mtu3_initialize_counter() will fail if the requested channel is busy.
Call pm_runtime_put() in the error path to decrement the usage count,
and flip the check to keep the success path straightforward.
Cc: stable@vger.kernel.org
Fixes: 0be8907359df ("counter: Add Renesas RZ/G2L MTU3a counter driver")
Signed-off-by: Cosmin Tanislav <cosmin-gabriel.tanislav.xa@renesas.com>
---
drivers/counter/rz-mtu3-cnt.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/drivers/counter/rz-mtu3-cnt.c b/drivers/counter/rz-mtu3-cnt.c
index 7bfb6979193c..48f183f0b54f 100644
--- a/drivers/counter/rz-mtu3-cnt.c
+++ b/drivers/counter/rz-mtu3-cnt.c
@@ -506,8 +506,12 @@ static int rz_mtu3_count_enable_write(struct counter_device *counter,
if (enable) {
pm_runtime_get_sync(counter->parent);
ret = rz_mtu3_initialize_counter(counter, count->id);
- if (ret == 0)
- priv->count_is_enabled[count->id] = true;
+ if (ret) {
+ pm_runtime_put(counter->parent);
+ goto exit;
+ }
+
+ priv->count_is_enabled[count->id] = true;
} else {
rz_mtu3_terminate_counter(counter, count->id);
priv->count_is_enabled[count->id] = false;
--
2.55.0
^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH 02/15] counter: rz-mtu3-cnt: do not assign struct rz_mtu3_channel::dev
2026-09-14 20:35 [PATCH 00/15] MTU3 counter fixes and improvements Cosmin Tanislav
2026-09-14 20:36 ` [PATCH 01/15] counter: rz-mtu3-cnt: put runtime PM on initialization error path Cosmin Tanislav
@ 2026-09-14 20:36 ` Cosmin Tanislav
2026-09-14 20:36 ` [PATCH 03/15] counter: rz-mtu3-cnt: remove manual runtime PM handling Cosmin Tanislav
` (13 subsequent siblings)
15 siblings, 0 replies; 20+ messages in thread
From: Cosmin Tanislav @ 2026-09-14 20:36 UTC (permalink / raw)
To: Biju Das, William Breathitt Gray, Lee Jones
Cc: linux-iio, linux-renesas-soc, linux-kernel, Cosmin Tanislav
struct rz_mtu3_channel::dev is now unused. Remove the assignment to
prepare for removing it entirely.
Signed-off-by: Cosmin Tanislav <cosmin-gabriel.tanislav.xa@renesas.com>
---
drivers/counter/rz-mtu3-cnt.c | 7 +------
1 file changed, 1 insertion(+), 6 deletions(-)
diff --git a/drivers/counter/rz-mtu3-cnt.c b/drivers/counter/rz-mtu3-cnt.c
index 48f183f0b54f..4ad71ed3d1e1 100644
--- a/drivers/counter/rz-mtu3-cnt.c
+++ b/drivers/counter/rz-mtu3-cnt.c
@@ -843,7 +843,6 @@ static int rz_mtu3_cnt_probe(struct platform_device *pdev)
struct rz_mtu3 *ddata = dev_get_drvdata(pdev->dev.parent);
struct device *dev = &pdev->dev;
struct counter_device *counter;
- struct rz_mtu3_channel *ch;
struct rz_mtu3_cnt *priv;
unsigned int i;
int ret;
@@ -856,12 +855,8 @@ static int rz_mtu3_cnt_probe(struct platform_device *pdev)
priv->clk = ddata->clk;
priv->mtu_32bit_max = U32_MAX;
priv->ch = &ddata->channels[RZ_MTU3_CHAN_1];
- ch = &priv->ch[0];
- for (i = 0; i < RZ_MTU3_MAX_HW_CNTR_CHANNELS; i++) {
- ch->dev = dev;
+ for (i = 0; i < RZ_MTU3_MAX_HW_CNTR_CHANNELS; i++)
priv->mtu_16bit_max[i] = U16_MAX;
- ch++;
- }
mutex_init(&priv->lock);
platform_set_drvdata(pdev, priv->clk);
--
2.55.0
^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH 03/15] counter: rz-mtu3-cnt: remove manual runtime PM handling
2026-09-14 20:35 [PATCH 00/15] MTU3 counter fixes and improvements Cosmin Tanislav
2026-09-14 20:36 ` [PATCH 01/15] counter: rz-mtu3-cnt: put runtime PM on initialization error path Cosmin Tanislav
2026-09-14 20:36 ` [PATCH 02/15] counter: rz-mtu3-cnt: do not assign struct rz_mtu3_channel::dev Cosmin Tanislav
@ 2026-09-14 20:36 ` Cosmin Tanislav
2026-09-14 20:36 ` [PATCH 04/15] counter: rz-mtu3-cnt: use device-managed pm_runtime_enable() Cosmin Tanislav
` (12 subsequent siblings)
15 siblings, 0 replies; 20+ messages in thread
From: Cosmin Tanislav @ 2026-09-14 20:36 UTC (permalink / raw)
To: Biju Das, William Breathitt Gray, Lee Jones
Cc: linux-iio, linux-renesas-soc, linux-kernel, Cosmin Tanislav
The clock is already managed by the CPG power domain attached to the
parent device for all supported SoCs.
When runtime PM is enabled on the parent device, __device_attach()
temporarily resumes the parent for the duration of the child's probe and
drops the runtime PM reference afterwards. Runtime PM usage by the child
device also keeps the parent device active.
As a result, explicit runtime PM handling in the counter driver is not
necessary. Remove it.
Signed-off-by: Cosmin Tanislav <cosmin-gabriel.tanislav.xa@renesas.com>
---
drivers/counter/rz-mtu3-cnt.c | 42 +++--------------------------------
1 file changed, 3 insertions(+), 39 deletions(-)
diff --git a/drivers/counter/rz-mtu3-cnt.c b/drivers/counter/rz-mtu3-cnt.c
index 4ad71ed3d1e1..6c171a741cc3 100644
--- a/drivers/counter/rz-mtu3-cnt.c
+++ b/drivers/counter/rz-mtu3-cnt.c
@@ -73,7 +73,6 @@
* @mtu_32bit_max: Cache for 32-bit counters
*/
struct rz_mtu3_cnt {
- struct clk *clk;
struct mutex lock;
struct rz_mtu3_channel *ch;
bool count_is_enabled[RZ_MTU3_MAX_LOGICAL_CNTR_CHANNELS];
@@ -808,34 +807,11 @@ static struct counter_comp rz_mtu3_device_ext[] = {
rz_mtu3_ext_input_phase_clock_select_enum),
};
-static int rz_mtu3_cnt_pm_runtime_suspend(struct device *dev)
-{
- struct clk *const clk = dev_get_drvdata(dev);
-
- clk_disable_unprepare(clk);
-
- return 0;
-}
-
-static int rz_mtu3_cnt_pm_runtime_resume(struct device *dev)
-{
- struct clk *const clk = dev_get_drvdata(dev);
-
- clk_prepare_enable(clk);
-
- return 0;
-}
-
-static DEFINE_RUNTIME_DEV_PM_OPS(rz_mtu3_cnt_pm_ops,
- rz_mtu3_cnt_pm_runtime_suspend,
- rz_mtu3_cnt_pm_runtime_resume, NULL);
-
static void rz_mtu3_cnt_pm_disable(void *data)
{
struct device *dev = data;
pm_runtime_disable(dev);
- pm_runtime_set_suspended(dev);
}
static int rz_mtu3_cnt_probe(struct platform_device *pdev)
@@ -852,20 +828,16 @@ static int rz_mtu3_cnt_probe(struct platform_device *pdev)
return -ENOMEM;
priv = counter_priv(counter);
- priv->clk = ddata->clk;
priv->mtu_32bit_max = U32_MAX;
priv->ch = &ddata->channels[RZ_MTU3_CHAN_1];
for (i = 0; i < RZ_MTU3_MAX_HW_CNTR_CHANNELS; i++)
priv->mtu_16bit_max[i] = U16_MAX;
mutex_init(&priv->lock);
- platform_set_drvdata(pdev, priv->clk);
- clk_prepare_enable(priv->clk);
- pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
ret = devm_add_action_or_reset(&pdev->dev, rz_mtu3_cnt_pm_disable, dev);
if (ret < 0)
- goto disable_clock;
+ return ret;
counter->name = dev_name(dev);
counter->parent = dev;
@@ -879,24 +851,16 @@ static int rz_mtu3_cnt_probe(struct platform_device *pdev)
/* Register Counter device */
ret = devm_counter_add(dev, counter);
- if (ret < 0) {
- dev_err_probe(dev, ret, "Failed to add counter\n");
- goto disable_clock;
- }
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to add counter\n");
return 0;
-
-disable_clock:
- clk_disable_unprepare(priv->clk);
-
- return ret;
}
static struct platform_driver rz_mtu3_cnt_driver = {
.probe = rz_mtu3_cnt_probe,
.driver = {
.name = "rz-mtu3-counter",
- .pm = pm_ptr(&rz_mtu3_cnt_pm_ops),
},
};
module_platform_driver(rz_mtu3_cnt_driver);
--
2.55.0
^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH 04/15] counter: rz-mtu3-cnt: use device-managed pm_runtime_enable()
2026-09-14 20:35 [PATCH 00/15] MTU3 counter fixes and improvements Cosmin Tanislav
` (2 preceding siblings ...)
2026-09-14 20:36 ` [PATCH 03/15] counter: rz-mtu3-cnt: remove manual runtime PM handling Cosmin Tanislav
@ 2026-09-14 20:36 ` Cosmin Tanislav
2026-09-14 20:36 ` [PATCH 05/15] counter: rz-mtu3-cnt: read enable value from cache Cosmin Tanislav
` (11 subsequent siblings)
15 siblings, 0 replies; 20+ messages in thread
From: Cosmin Tanislav @ 2026-09-14 20:36 UTC (permalink / raw)
To: Biju Das, William Breathitt Gray, Lee Jones
Cc: linux-iio, linux-renesas-soc, linux-kernel, Cosmin Tanislav
devm_pm_runtime_enable() has the same semantics as the current logic,
use it.
Signed-off-by: Cosmin Tanislav <cosmin-gabriel.tanislav.xa@renesas.com>
---
drivers/counter/rz-mtu3-cnt.c | 13 +++----------
1 file changed, 3 insertions(+), 10 deletions(-)
diff --git a/drivers/counter/rz-mtu3-cnt.c b/drivers/counter/rz-mtu3-cnt.c
index 6c171a741cc3..b99dd67703df 100644
--- a/drivers/counter/rz-mtu3-cnt.c
+++ b/drivers/counter/rz-mtu3-cnt.c
@@ -807,13 +807,6 @@ static struct counter_comp rz_mtu3_device_ext[] = {
rz_mtu3_ext_input_phase_clock_select_enum),
};
-static void rz_mtu3_cnt_pm_disable(void *data)
-{
- struct device *dev = data;
-
- pm_runtime_disable(dev);
-}
-
static int rz_mtu3_cnt_probe(struct platform_device *pdev)
{
struct rz_mtu3 *ddata = dev_get_drvdata(pdev->dev.parent);
@@ -834,9 +827,9 @@ static int rz_mtu3_cnt_probe(struct platform_device *pdev)
priv->mtu_16bit_max[i] = U16_MAX;
mutex_init(&priv->lock);
- pm_runtime_enable(&pdev->dev);
- ret = devm_add_action_or_reset(&pdev->dev, rz_mtu3_cnt_pm_disable, dev);
- if (ret < 0)
+
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
return ret;
counter->name = dev_name(dev);
--
2.55.0
^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH 05/15] counter: rz-mtu3-cnt: read enable value from cache
2026-09-14 20:35 [PATCH 00/15] MTU3 counter fixes and improvements Cosmin Tanislav
` (3 preceding siblings ...)
2026-09-14 20:36 ` [PATCH 04/15] counter: rz-mtu3-cnt: use device-managed pm_runtime_enable() Cosmin Tanislav
@ 2026-09-14 20:36 ` Cosmin Tanislav
2026-09-14 20:36 ` [PATCH 06/15] counter: rz-mtu3-cnt: disable channel before releasing Cosmin Tanislav
` (10 subsequent siblings)
15 siblings, 0 replies; 20+ messages in thread
From: Cosmin Tanislav @ 2026-09-14 20:36 UTC (permalink / raw)
To: Biju Das, William Breathitt Gray, Lee Jones
Cc: linux-iio, linux-renesas-soc, linux-kernel, Cosmin Tanislav
Reading the enable value when PWM is in use returns -EINVAL because of
the checks inside rz_mtu3_lock_if_count_is_enabled(). Reading the enable
value should not be an issue even if PWM on that specific MTU channel is
enabled.
There is no need to check whether the underlying MTU3 channel is enabled
when determining whether a count is enabled, as we have a local
count_is_enabled array containing exactly that information, which cannot
be wrong.
Use the information in the local count_is_enabled array and allow
reading it even if PWM is in use.
Signed-off-by: Cosmin Tanislav <cosmin-gabriel.tanislav.xa@renesas.com>
---
drivers/counter/rz-mtu3-cnt.c | 15 ++-------------
1 file changed, 2 insertions(+), 13 deletions(-)
diff --git a/drivers/counter/rz-mtu3-cnt.c b/drivers/counter/rz-mtu3-cnt.c
index b99dd67703df..d3daf258a3b8 100644
--- a/drivers/counter/rz-mtu3-cnt.c
+++ b/drivers/counter/rz-mtu3-cnt.c
@@ -471,21 +471,10 @@ static void rz_mtu3_terminate_counter(struct counter_device *counter, int id)
static int rz_mtu3_count_enable_read(struct counter_device *counter,
struct counter_count *count, u8 *enable)
{
- struct rz_mtu3_channel *const ch = rz_mtu3_get_ch(counter, count->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);
- int ret;
-
- ret = rz_mtu3_lock_if_count_is_enabled(ch, priv, count->id);
- if (ret)
- return ret;
-
- if (count->id == RZ_MTU3_32_BIT_CH)
- *enable = rz_mtu3_is_enabled(ch1) && rz_mtu3_is_enabled(ch2);
- else
- *enable = rz_mtu3_is_enabled(ch);
+ mutex_lock(&priv->lock);
+ *enable = priv->count_is_enabled[count->id];
mutex_unlock(&priv->lock);
return 0;
--
2.55.0
^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH 06/15] counter: rz-mtu3-cnt: disable channel before releasing
2026-09-14 20:35 [PATCH 00/15] MTU3 counter fixes and improvements Cosmin Tanislav
` (4 preceding siblings ...)
2026-09-14 20:36 ` [PATCH 05/15] counter: rz-mtu3-cnt: read enable value from cache Cosmin Tanislav
@ 2026-09-14 20:36 ` Cosmin Tanislav
2026-09-14 20:36 ` [PATCH 07/15] counter: rz-mtu3-cnt: unify ceiling values Cosmin Tanislav
` (9 subsequent siblings)
15 siblings, 0 replies; 20+ messages in thread
From: Cosmin Tanislav @ 2026-09-14 20:36 UTC (permalink / raw)
To: Biju Das, William Breathitt Gray, Lee Jones
Cc: linux-iio, linux-renesas-soc, linux-kernel, Cosmin Tanislav
Releasing a channel marks it as not busy, allowing other threads to
claim it. With the current logic, it is possible for another thread to
claim the channel before disable is called, since we release the channel
before we disable it, causing the channel to possibly remain disabled
while it should have been enabled.
Disable the channel before releasing it.
Fixes: 0be8907359df ("counter: Add Renesas RZ/G2L MTU3a counter driver")
Signed-off-by: Cosmin Tanislav <cosmin-gabriel.tanislav.xa@renesas.com>
---
drivers/counter/rz-mtu3-cnt.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/counter/rz-mtu3-cnt.c b/drivers/counter/rz-mtu3-cnt.c
index d3daf258a3b8..e200a6548dfe 100644
--- a/drivers/counter/rz-mtu3-cnt.c
+++ b/drivers/counter/rz-mtu3-cnt.c
@@ -458,13 +458,13 @@ static void rz_mtu3_terminate_counter(struct counter_device *counter, int id)
struct rz_mtu3_channel *const ch2 = rz_mtu3_get_ch(counter, 1);
if (id == RZ_MTU3_32_BIT_CH) {
- rz_mtu3_release_channel(ch2);
- rz_mtu3_release_channel(ch1);
rz_mtu3_disable(ch2);
rz_mtu3_disable(ch1);
+ rz_mtu3_release_channel(ch2);
+ rz_mtu3_release_channel(ch1);
} else {
- rz_mtu3_release_channel(ch);
rz_mtu3_disable(ch);
+ rz_mtu3_release_channel(ch);
}
}
--
2.55.0
^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH 07/15] counter: rz-mtu3-cnt: unify ceiling values
2026-09-14 20:35 [PATCH 00/15] MTU3 counter fixes and improvements Cosmin Tanislav
` (5 preceding siblings ...)
2026-09-14 20:36 ` [PATCH 06/15] counter: rz-mtu3-cnt: disable channel before releasing Cosmin Tanislav
@ 2026-09-14 20:36 ` Cosmin Tanislav
2026-09-14 20:36 ` [PATCH 08/15] counter: rz-mtu3-cnt: cache " Cosmin Tanislav
` (8 subsequent siblings)
15 siblings, 0 replies; 20+ messages in thread
From: Cosmin Tanislav @ 2026-09-14 20:36 UTC (permalink / raw)
To: Biju Das, William Breathitt Gray, Lee Jones
Cc: linux-iio, linux-renesas-soc, linux-kernel, Cosmin Tanislav
Writing to the 32-bit ceiling value clobbers the 16-bit ceiling values
as both are stored in a union. This was probably done to save a few
bytes of memory, but that memory is then wasted on code to handle the
two separate variables.
Move them out of the union and use a single u32 array to hold ceiling
values for all channels to simplify the code.
Signed-off-by: Cosmin Tanislav <cosmin-gabriel.tanislav.xa@renesas.com>
---
drivers/counter/rz-mtu3-cnt.c | 32 ++++++--------------------------
1 file changed, 6 insertions(+), 26 deletions(-)
diff --git a/drivers/counter/rz-mtu3-cnt.c b/drivers/counter/rz-mtu3-cnt.c
index e200a6548dfe..f9bdfaf7bd42 100644
--- a/drivers/counter/rz-mtu3-cnt.c
+++ b/drivers/counter/rz-mtu3-cnt.c
@@ -75,11 +75,8 @@
struct rz_mtu3_cnt {
struct mutex lock;
struct rz_mtu3_channel *ch;
+ u32 ceiling[RZ_MTU3_MAX_LOGICAL_CNTR_CHANNELS];
bool count_is_enabled[RZ_MTU3_MAX_LOGICAL_CNTR_CHANNELS];
- union {
- u16 mtu_16bit_max[RZ_MTU3_MAX_HW_CNTR_CHANNELS];
- u32 mtu_32bit_max;
- };
};
static const enum counter_function rz_mtu3_count_functions[] = {
@@ -316,27 +313,13 @@ static int rz_mtu3_count_ceiling_read(struct counter_device *counter,
{
struct rz_mtu3_channel *const ch = rz_mtu3_get_ch(counter, count->id);
struct rz_mtu3_cnt *const priv = counter_priv(counter);
- const size_t ch_id = rz_mtu3_get_hw_ch(count->id);
int ret;
ret = rz_mtu3_lock_if_counter_is_valid(counter, ch, priv, count->id);
if (ret)
return ret;
- switch (count->id) {
- case RZ_MTU3_16_BIT_MTU1_CH:
- case RZ_MTU3_16_BIT_MTU2_CH:
- *ceiling = priv->mtu_16bit_max[ch_id];
- break;
- case RZ_MTU3_32_BIT_CH:
- *ceiling = priv->mtu_32bit_max;
- break;
- default:
- /* should never reach this path */
- mutex_unlock(&priv->lock);
- return -EINVAL;
- }
-
+ *ceiling = priv->ceiling[count->id];
mutex_unlock(&priv->lock);
return 0;
}
@@ -347,7 +330,6 @@ static int rz_mtu3_count_ceiling_write(struct counter_device *counter,
{
struct rz_mtu3_channel *const ch = rz_mtu3_get_ch(counter, count->id);
struct rz_mtu3_cnt *const priv = counter_priv(counter);
- const size_t ch_id = rz_mtu3_get_hw_ch(count->id);
int ret;
ret = rz_mtu3_lock_if_counter_is_valid(counter, ch, priv, count->id);
@@ -361,14 +343,12 @@ static int rz_mtu3_count_ceiling_write(struct counter_device *counter,
mutex_unlock(&priv->lock);
return -ERANGE;
}
- priv->mtu_16bit_max[ch_id] = ceiling;
break;
case RZ_MTU3_32_BIT_CH:
if (ceiling > U32_MAX) {
mutex_unlock(&priv->lock);
return -ERANGE;
}
- priv->mtu_32bit_max = ceiling;
break;
default:
/* should never reach this path */
@@ -383,6 +363,7 @@ static int rz_mtu3_count_ceiling_write(struct counter_device *counter,
rz_mtu3_16bit_ch_write(ch, RZ_MTU3_TGRA, ceiling);
rz_mtu3_8bit_ch_write(ch, RZ_MTU3_TCR, RZ_MTU3_TCR_CCLR_TGRA);
+ priv->ceiling[count->id] = ceiling;
pm_runtime_put(counter->parent);
mutex_unlock(&priv->lock);
@@ -802,7 +783,6 @@ static int rz_mtu3_cnt_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct counter_device *counter;
struct rz_mtu3_cnt *priv;
- unsigned int i;
int ret;
counter = devm_counter_alloc(dev, sizeof(*priv));
@@ -810,10 +790,10 @@ static int rz_mtu3_cnt_probe(struct platform_device *pdev)
return -ENOMEM;
priv = counter_priv(counter);
- priv->mtu_32bit_max = U32_MAX;
+ priv->ceiling[RZ_MTU3_16_BIT_MTU1_CH] = U16_MAX;
+ priv->ceiling[RZ_MTU3_16_BIT_MTU2_CH] = U16_MAX;
+ priv->ceiling[RZ_MTU3_32_BIT_CH] = U32_MAX;
priv->ch = &ddata->channels[RZ_MTU3_CHAN_1];
- for (i = 0; i < RZ_MTU3_MAX_HW_CNTR_CHANNELS; i++)
- priv->mtu_16bit_max[i] = U16_MAX;
mutex_init(&priv->lock);
--
2.55.0
^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH 08/15] counter: rz-mtu3-cnt: cache ceiling values
2026-09-14 20:35 [PATCH 00/15] MTU3 counter fixes and improvements Cosmin Tanislav
` (6 preceding siblings ...)
2026-09-14 20:36 ` [PATCH 07/15] counter: rz-mtu3-cnt: unify ceiling values Cosmin Tanislav
@ 2026-09-14 20:36 ` Cosmin Tanislav
2026-09-14 20:36 ` [PATCH 09/15] counter: rz-mtu3-cnt: cache count values Cosmin Tanislav
` (7 subsequent siblings)
15 siblings, 0 replies; 20+ messages in thread
From: Cosmin Tanislav @ 2026-09-14 20:36 UTC (permalink / raw)
To: Biju Das, William Breathitt Gray, Lee Jones
Cc: linux-iio, linux-renesas-soc, linux-kernel, Cosmin Tanislav
If an MTU3 channel is used in phase counting mode, and then in PWM mode,
when returning to counting mode the ceiling value is lost because PWM
mode clobbers the TGRA registers used to store the ceiling values.
Restore the ceiling value in the count initialization functions for both
16-bit and 32-bit channels.
To avoid writing the ceiling 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 write ceiling 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() when writing the ceiling.
Signed-off-by: Cosmin Tanislav <cosmin-gabriel.tanislav.xa@renesas.com>
---
drivers/counter/rz-mtu3-cnt.c | 30 ++++++++++++++++++++----------
1 file changed, 20 insertions(+), 10 deletions(-)
diff --git a/drivers/counter/rz-mtu3-cnt.c b/drivers/counter/rz-mtu3-cnt.c
index f9bdfaf7bd42..b76ad6cb8b6d 100644
--- a/drivers/counter/rz-mtu3-cnt.c
+++ b/drivers/counter/rz-mtu3-cnt.c
@@ -150,6 +150,17 @@ static int rz_mtu3_lock_if_count_is_enabled(struct rz_mtu3_channel *const ch,
return 0;
}
+static void rz_mtu3_set_ceiling(struct rz_mtu3_channel *const ch, int id,
+ u64 ceiling)
+{
+ if (id == RZ_MTU3_32_BIT_CH)
+ rz_mtu3_32bit_ch_write(ch, RZ_MTU3_TGRALW, ceiling);
+ else
+ rz_mtu3_16bit_ch_write(ch, RZ_MTU3_TGRA, ceiling);
+
+ rz_mtu3_8bit_ch_write(ch, RZ_MTU3_TCR, RZ_MTU3_TCR_CCLR_TGRA);
+}
+
static int rz_mtu3_count_read(struct counter_device *counter,
struct counter_count *count, u64 *val)
{
@@ -356,15 +367,9 @@ static int rz_mtu3_count_ceiling_write(struct counter_device *counter,
return -EINVAL;
}
- pm_runtime_get_sync(counter->parent);
- if (count->id == RZ_MTU3_32_BIT_CH)
- rz_mtu3_32bit_ch_write(ch, RZ_MTU3_TGRALW, ceiling);
- else
- rz_mtu3_16bit_ch_write(ch, RZ_MTU3_TGRA, ceiling);
-
- rz_mtu3_8bit_ch_write(ch, RZ_MTU3_TCR, RZ_MTU3_TCR_CCLR_TGRA);
+ if (priv->count_is_enabled[count->id])
+ rz_mtu3_set_ceiling(ch, count->id, ceiling);
priv->ceiling[count->id] = ceiling;
- pm_runtime_put(counter->parent);
mutex_unlock(&priv->lock);
return 0;
@@ -374,11 +379,14 @@ static void rz_mtu3_32bit_cnt_setting(struct counter_device *counter)
{
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);
/* Phase counting mode 1 is used as default in initialization. */
rz_mtu3_8bit_ch_write(ch1, RZ_MTU3_TMDR1, RZ_MTU3_TMDR1_PH_CNT_MODE_1);
- rz_mtu3_8bit_ch_write(ch1, RZ_MTU3_TCR, RZ_MTU3_TCR_CCLR_TGRA);
+ rz_mtu3_set_ceiling(ch1, RZ_MTU3_32_BIT_CH,
+ priv->ceiling[RZ_MTU3_32_BIT_CH]);
+
rz_mtu3_8bit_ch_write(ch1, RZ_MTU3_TIOR, RZ_MTU3_TIOR_IC_BOTH);
rz_mtu3_enable(ch1);
@@ -388,11 +396,13 @@ static void rz_mtu3_32bit_cnt_setting(struct counter_device *counter)
static void rz_mtu3_16bit_cnt_setting(struct counter_device *counter, int id)
{
struct rz_mtu3_channel *const ch = rz_mtu3_get_ch(counter, id);
+ struct rz_mtu3_cnt *const priv = counter_priv(counter);
/* Phase counting mode 1 is used as default in initialization. */
rz_mtu3_8bit_ch_write(ch, RZ_MTU3_TMDR1, RZ_MTU3_TMDR1_PH_CNT_MODE_1);
- rz_mtu3_8bit_ch_write(ch, RZ_MTU3_TCR, RZ_MTU3_TCR_CCLR_TGRA);
+ rz_mtu3_set_ceiling(ch, id, priv->ceiling[id]);
+
rz_mtu3_8bit_ch_write(ch, RZ_MTU3_TIOR, RZ_MTU3_TIOR_NO_OUTPUT);
rz_mtu3_enable(ch);
}
--
2.55.0
^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH 09/15] counter: rz-mtu3-cnt: cache count values
2026-09-14 20:35 [PATCH 00/15] MTU3 counter fixes and improvements Cosmin Tanislav
` (7 preceding siblings ...)
2026-09-14 20:36 ` [PATCH 08/15] counter: rz-mtu3-cnt: cache " Cosmin Tanislav
@ 2026-09-14 20:36 ` Cosmin Tanislav
2026-09-14 20:36 ` [PATCH 10/15] counter: rz-mtu3-cnt: cache function values Cosmin Tanislav
` (6 subsequent siblings)
15 siblings, 0 replies; 20+ messages in thread
From: Cosmin Tanislav @ 2026-09-14 20:36 UTC (permalink / raw)
To: Biju Das, William Breathitt Gray, Lee Jones
Cc: linux-iio, linux-renesas-soc, linux-kernel, Cosmin Tanislav
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@renesas.com>
---
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
^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH 10/15] counter: rz-mtu3-cnt: cache function values
2026-09-14 20:35 [PATCH 00/15] MTU3 counter fixes and improvements Cosmin Tanislav
` (8 preceding siblings ...)
2026-09-14 20:36 ` [PATCH 09/15] counter: rz-mtu3-cnt: cache count values Cosmin Tanislav
@ 2026-09-14 20:36 ` Cosmin Tanislav
2026-09-14 20:36 ` [PATCH 11/15] counter: rz-mtu3-cnt: cache external input phase clock value Cosmin Tanislav
` (5 subsequent siblings)
15 siblings, 0 replies; 20+ messages in thread
From: Cosmin Tanislav @ 2026-09-14 20:36 UTC (permalink / raw)
To: Biju Das, William Breathitt Gray, Lee Jones
Cc: linux-iio, linux-renesas-soc, linux-kernel, Cosmin Tanislav
If an MTU3 channel is used in a specific phase counting mode, when
disabling it and enabling it again, the user set mode is lost because
the timer mode register (TMDR1) is initialized to the default value.
Save the timer mode after setting it and restore it on enable.
Use the cached value to determine the function in
rz_mtu3_count_function_read_helper().
Avoid writing the TMDR1 register when the count is disabled to prevent
writing registers without actually owning the channel, as the busy check
inside ch->is_busy can race with PWM operations.
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@renesas.com>
---
drivers/counter/rz-mtu3-cnt.c | 34 +++++++++++++++-------------------
1 file changed, 15 insertions(+), 19 deletions(-)
diff --git a/drivers/counter/rz-mtu3-cnt.c b/drivers/counter/rz-mtu3-cnt.c
index 68025c6ed409..3e380f69cb45 100644
--- a/drivers/counter/rz-mtu3-cnt.c
+++ b/drivers/counter/rz-mtu3-cnt.c
@@ -77,6 +77,7 @@ struct rz_mtu3_cnt {
struct rz_mtu3_channel *ch;
u32 ceiling[RZ_MTU3_MAX_LOGICAL_CNTR_CHANNELS];
u32 count[RZ_MTU3_MAX_LOGICAL_CNTR_CHANNELS];
+ u8 timer_mode[RZ_MTU3_MAX_LOGICAL_CNTR_CHANNELS];
bool count_is_enabled[RZ_MTU3_MAX_LOGICAL_CNTR_CHANNELS];
};
@@ -217,17 +218,11 @@ static int rz_mtu3_count_write(struct counter_device *counter,
return 0;
}
-static int rz_mtu3_count_function_read_helper(struct rz_mtu3_channel *const ch,
- struct counter_device *const counter,
+static int rz_mtu3_count_function_read_helper(struct rz_mtu3_cnt *const priv,
+ int id,
enum counter_function *function)
{
- u8 timer_mode;
-
- pm_runtime_get_sync(counter->parent);
- timer_mode = rz_mtu3_8bit_ch_read(ch, RZ_MTU3_TMDR1);
- pm_runtime_put(counter->parent);
-
- switch (timer_mode & RZ_MTU3_TMDR1_PH_CNT_MODE_MASK) {
+ switch (priv->timer_mode[id]) {
case RZ_MTU3_TMDR1_PH_CNT_MODE_1:
*function = COUNTER_FUNCTION_QUADRATURE_X4;
return 0;
@@ -259,7 +254,7 @@ static int rz_mtu3_count_function_read(struct counter_device *counter,
if (ret)
return ret;
- ret = rz_mtu3_count_function_read_helper(ch, counter, function);
+ ret = rz_mtu3_count_function_read_helper(priv, count->id, function);
mutex_unlock(&priv->lock);
return ret;
@@ -298,9 +293,9 @@ static int rz_mtu3_count_function_write(struct counter_device *counter,
return -EINVAL;
}
- pm_runtime_get_sync(counter->parent);
- rz_mtu3_8bit_ch_write(ch, RZ_MTU3_TMDR1, timer_mode);
- pm_runtime_put(counter->parent);
+ if (priv->count_is_enabled[count->id])
+ rz_mtu3_8bit_ch_write(ch, RZ_MTU3_TMDR1, timer_mode);
+ priv->timer_mode[count->id] = timer_mode;
mutex_unlock(&priv->lock);
return 0;
@@ -393,8 +388,8 @@ static void rz_mtu3_32bit_cnt_setting(struct counter_device *counter)
struct rz_mtu3_channel *const ch2 = rz_mtu3_get_ch(counter, 1);
struct rz_mtu3_cnt *const priv = counter_priv(counter);
- /* Phase counting mode 1 is used as default in initialization. */
- rz_mtu3_8bit_ch_write(ch1, RZ_MTU3_TMDR1, RZ_MTU3_TMDR1_PH_CNT_MODE_1);
+ rz_mtu3_8bit_ch_write(ch1, RZ_MTU3_TMDR1,
+ priv->timer_mode[RZ_MTU3_32_BIT_CH]);
rz_mtu3_set_ceiling(ch1, RZ_MTU3_32_BIT_CH,
priv->ceiling[RZ_MTU3_32_BIT_CH]);
@@ -412,9 +407,7 @@ static void rz_mtu3_16bit_cnt_setting(struct counter_device *counter, int id)
struct rz_mtu3_channel *const ch = rz_mtu3_get_ch(counter, id);
struct rz_mtu3_cnt *const priv = counter_priv(counter);
- /* Phase counting mode 1 is used as default in initialization. */
- rz_mtu3_8bit_ch_write(ch, RZ_MTU3_TMDR1, RZ_MTU3_TMDR1_PH_CNT_MODE_1);
-
+ rz_mtu3_8bit_ch_write(ch, RZ_MTU3_TMDR1, priv->timer_mode[id]);
rz_mtu3_set_ceiling(ch, id, priv->ceiling[id]);
rz_mtu3_set_count(ch, id, priv->count[id]);
@@ -644,7 +637,7 @@ static int rz_mtu3_action_read(struct counter_device *counter,
if (ret)
return ret;
- ret = rz_mtu3_count_function_read_helper(ch, counter, &function);
+ ret = rz_mtu3_count_function_read_helper(priv, count->id, &function);
if (ret) {
mutex_unlock(&priv->lock);
return ret;
@@ -811,6 +804,7 @@ static int rz_mtu3_cnt_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct counter_device *counter;
struct rz_mtu3_cnt *priv;
+ unsigned int i;
int ret;
counter = devm_counter_alloc(dev, sizeof(*priv));
@@ -822,6 +816,8 @@ static int rz_mtu3_cnt_probe(struct platform_device *pdev)
priv->ceiling[RZ_MTU3_16_BIT_MTU2_CH] = U16_MAX;
priv->ceiling[RZ_MTU3_32_BIT_CH] = U32_MAX;
priv->ch = &ddata->channels[RZ_MTU3_CHAN_1];
+ for (i = 0; i < RZ_MTU3_MAX_LOGICAL_CNTR_CHANNELS; i++)
+ priv->timer_mode[i] = RZ_MTU3_TMDR1_PH_CNT_MODE_1;
mutex_init(&priv->lock);
--
2.55.0
^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH 11/15] counter: rz-mtu3-cnt: cache external input phase clock value
2026-09-14 20:35 [PATCH 00/15] MTU3 counter fixes and improvements Cosmin Tanislav
` (9 preceding siblings ...)
2026-09-14 20:36 ` [PATCH 10/15] counter: rz-mtu3-cnt: cache function values Cosmin Tanislav
@ 2026-09-14 20:36 ` Cosmin Tanislav
2026-09-14 20:36 ` [PATCH 12/15] counter: rz-mtu3-cnt: cache direction values Cosmin Tanislav
` (4 subsequent siblings)
15 siblings, 0 replies; 20+ messages in thread
From: Cosmin Tanislav @ 2026-09-14 20:36 UTC (permalink / raw)
To: Biju Das, William Breathitt Gray, Lee Jones
Cc: linux-iio, linux-renesas-soc, linux-kernel, Cosmin Tanislav
PHCKSEL selects between MTCLKA-MTCLKB and MTCLKC-MTCLKD pins for the
external input phase clock.
PHCKSEL only affects the 16-bit count backed by MTU2 and the 32-bit
count.
Cache the value written to external_input_phase_clock_select and restore
it on enable to bring the external_input_phase_clock_select sysfs entry
in line with the other entries.
Initialize the cached value to true, matching PHCKSEL's reset value so
behavior is unchanged for a count that gets enabled without
external_input_phase_clock_select being written explicitly.
Always use the cached PHCKSEL value to remove the need to access
registers while counts might not be enabled.
Only restore it for the count backed by MTU2 as changing PHCKSEL for
MTU1 does not affect the pins used for the external input phase clock.
Check if any of these counts are enabled before writing the new PHCKSEL
value.
This prevents writing registers without actually owning the channel,
as the busy check inside ch->is_busy can race with PWM operations.
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@renesas.com>
---
drivers/counter/rz-mtu3-cnt.c | 35 +++++++++++++++++++----------------
1 file changed, 19 insertions(+), 16 deletions(-)
diff --git a/drivers/counter/rz-mtu3-cnt.c b/drivers/counter/rz-mtu3-cnt.c
index 3e380f69cb45..a26b8b71c9a6 100644
--- a/drivers/counter/rz-mtu3-cnt.c
+++ b/drivers/counter/rz-mtu3-cnt.c
@@ -79,6 +79,7 @@ struct rz_mtu3_cnt {
u32 count[RZ_MTU3_MAX_LOGICAL_CNTR_CHANNELS];
u8 timer_mode[RZ_MTU3_MAX_LOGICAL_CNTR_CHANNELS];
bool count_is_enabled[RZ_MTU3_MAX_LOGICAL_CNTR_CHANNELS];
+ bool mtclkc_mtclkd;
};
static const enum counter_function rz_mtu3_count_functions[] = {
@@ -180,6 +181,12 @@ static void rz_mtu3_set_count(struct rz_mtu3_channel *const ch, int id,
rz_mtu3_16bit_ch_write(ch, RZ_MTU3_TCNT, count);
}
+static void rz_mtu3_set_phcksel(struct rz_mtu3_channel *const ch, bool enable)
+{
+ rz_mtu3_shared_reg_update_bit(ch, RZ_MTU3_TMDR3, RZ_MTU3_TMDR3_PHCKSEL,
+ enable);
+}
+
static int rz_mtu3_count_read(struct counter_device *counter,
struct counter_count *count, u64 *val)
{
@@ -388,6 +395,8 @@ static void rz_mtu3_32bit_cnt_setting(struct counter_device *counter)
struct rz_mtu3_channel *const ch2 = rz_mtu3_get_ch(counter, 1);
struct rz_mtu3_cnt *const priv = counter_priv(counter);
+ rz_mtu3_set_phcksel(priv->ch, priv->mtclkc_mtclkd);
+
rz_mtu3_8bit_ch_write(ch1, RZ_MTU3_TMDR1,
priv->timer_mode[RZ_MTU3_32_BIT_CH]);
@@ -407,6 +416,8 @@ static void rz_mtu3_16bit_cnt_setting(struct counter_device *counter, int id)
struct rz_mtu3_channel *const ch = rz_mtu3_get_ch(counter, id);
struct rz_mtu3_cnt *const priv = counter_priv(counter);
+ if (id == RZ_MTU3_16_BIT_MTU2_CH)
+ rz_mtu3_set_phcksel(priv->ch, priv->mtclkc_mtclkd);
rz_mtu3_8bit_ch_write(ch, RZ_MTU3_TMDR1, priv->timer_mode[id]);
rz_mtu3_set_ceiling(ch, id, priv->ceiling[id]);
rz_mtu3_set_count(ch, id, priv->count[id]);
@@ -569,17 +580,13 @@ static int rz_mtu3_ext_input_phase_clock_select_get(struct counter_device *count
u32 *ext_input_phase_clock_select)
{
struct rz_mtu3_cnt *const priv = counter_priv(counter);
- unsigned long tmdr;
int ret;
ret = rz_mtu3_lock_if_ch0_is_enabled(priv);
if (ret)
return ret;
- pm_runtime_get_sync(counter->parent);
- tmdr = rz_mtu3_shared_reg_read(priv->ch, RZ_MTU3_TMDR3);
- pm_runtime_put(counter->parent);
- *ext_input_phase_clock_select = test_bit(RZ_MTU3_TMDR3_PHCKSEL, &tmdr);
+ *ext_input_phase_clock_select = priv->mtclkc_mtclkd;
mutex_unlock(&priv->lock);
return 0;
@@ -595,11 +602,10 @@ static int rz_mtu3_ext_input_phase_clock_select_set(struct counter_device *count
if (ret)
return ret;
- pm_runtime_get_sync(counter->parent);
- rz_mtu3_shared_reg_update_bit(priv->ch, RZ_MTU3_TMDR3,
- RZ_MTU3_TMDR3_PHCKSEL,
- ext_input_phase_clock_select);
- pm_runtime_put(counter->parent);
+ if (priv->count_is_enabled[RZ_MTU3_16_BIT_MTU2_CH] ||
+ priv->count_is_enabled[RZ_MTU3_32_BIT_CH])
+ rz_mtu3_set_phcksel(priv->ch, ext_input_phase_clock_select);
+ priv->mtclkc_mtclkd = ext_input_phase_clock_select;
mutex_unlock(&priv->lock);
return 0;
@@ -629,8 +635,6 @@ static int rz_mtu3_action_read(struct counter_device *counter,
struct rz_mtu3_channel *const ch = rz_mtu3_get_ch(counter, count->id);
struct rz_mtu3_cnt *const priv = counter_priv(counter);
enum counter_function function;
- bool mtclkc_mtclkd;
- unsigned long tmdr;
int ret;
ret = rz_mtu3_lock_if_count_is_enabled(ch, priv, count->id);
@@ -647,10 +651,8 @@ static int rz_mtu3_action_read(struct counter_device *counter,
*action = COUNTER_SYNAPSE_ACTION_NONE;
if (count->id != RZ_MTU3_16_BIT_MTU1_CH) {
- tmdr = rz_mtu3_shared_reg_read(priv->ch, RZ_MTU3_TMDR3);
- mtclkc_mtclkd = test_bit(RZ_MTU3_TMDR3_PHCKSEL, &tmdr);
- if ((mtclkc_mtclkd && is_signal_ab) ||
- (!mtclkc_mtclkd && !is_signal_ab)) {
+ if ((priv->mtclkc_mtclkd && is_signal_ab) ||
+ (!priv->mtclkc_mtclkd && !is_signal_ab)) {
mutex_unlock(&priv->lock);
return 0;
}
@@ -812,6 +814,7 @@ static int rz_mtu3_cnt_probe(struct platform_device *pdev)
return -ENOMEM;
priv = counter_priv(counter);
+ priv->mtclkc_mtclkd = true;
priv->ceiling[RZ_MTU3_16_BIT_MTU1_CH] = U16_MAX;
priv->ceiling[RZ_MTU3_16_BIT_MTU2_CH] = U16_MAX;
priv->ceiling[RZ_MTU3_32_BIT_CH] = U32_MAX;
--
2.55.0
^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH 12/15] counter: rz-mtu3-cnt: cache direction values
2026-09-14 20:35 [PATCH 00/15] MTU3 counter fixes and improvements Cosmin Tanislav
` (10 preceding siblings ...)
2026-09-14 20:36 ` [PATCH 11/15] counter: rz-mtu3-cnt: cache external input phase clock value Cosmin Tanislav
@ 2026-09-14 20:36 ` Cosmin Tanislav
2026-09-14 20:36 ` [PATCH 13/15] counter: rz-mtu3-cnt: keep cascade_counts_enable in sync with usage Cosmin Tanislav
` (3 subsequent siblings)
15 siblings, 0 replies; 20+ messages in thread
From: Cosmin Tanislav @ 2026-09-14 20:36 UTC (permalink / raw)
To: Biju Das, William Breathitt Gray, Lee Jones
Cc: linux-iio, linux-renesas-soc, linux-kernel, Cosmin Tanislav
Reading registers without the count being enabled is an issue as the
ch->is_busy check inside rz_mtu3_lock_if_count_is_enabled() can race
with PWM operations.
Cache the direction value on every read and on disable, and return the
cached value if the count is disabled.
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@renesas.com>
---
drivers/counter/rz-mtu3-cnt.c | 18 +++++++++++++-----
1 file changed, 13 insertions(+), 5 deletions(-)
diff --git a/drivers/counter/rz-mtu3-cnt.c b/drivers/counter/rz-mtu3-cnt.c
index a26b8b71c9a6..5fd12941df4c 100644
--- a/drivers/counter/rz-mtu3-cnt.c
+++ b/drivers/counter/rz-mtu3-cnt.c
@@ -79,6 +79,7 @@ struct rz_mtu3_cnt {
u32 count[RZ_MTU3_MAX_LOGICAL_CNTR_CHANNELS];
u8 timer_mode[RZ_MTU3_MAX_LOGICAL_CNTR_CHANNELS];
bool count_is_enabled[RZ_MTU3_MAX_LOGICAL_CNTR_CHANNELS];
+ bool direction[RZ_MTU3_MAX_LOGICAL_CNTR_CHANNELS];
bool mtclkc_mtclkd;
};
@@ -187,6 +188,13 @@ static void rz_mtu3_set_phcksel(struct rz_mtu3_channel *const ch, bool enable)
enable);
}
+static bool rz_mtu3_get_direction(struct rz_mtu3_channel *const ch)
+{
+ u8 tsr = rz_mtu3_8bit_ch_read(ch, RZ_MTU3_TSR);
+
+ return !!(tsr & RZ_MTU3_TSR_TCFD);
+}
+
static int rz_mtu3_count_read(struct counter_device *counter,
struct counter_count *count, u64 *val)
{
@@ -315,18 +323,17 @@ static int rz_mtu3_count_direction_read(struct counter_device *counter,
struct rz_mtu3_channel *const ch = rz_mtu3_get_ch(counter, count->id);
struct rz_mtu3_cnt *const priv = counter_priv(counter);
int ret;
- u8 tsr;
ret = rz_mtu3_lock_if_count_is_enabled(ch, priv, count->id);
if (ret)
return ret;
- pm_runtime_get_sync(counter->parent);
- tsr = rz_mtu3_8bit_ch_read(ch, RZ_MTU3_TSR);
- pm_runtime_put(counter->parent);
+ if (priv->count_is_enabled[count->id])
+ priv->direction[count->id] = rz_mtu3_get_direction(ch);
- *direction = (tsr & RZ_MTU3_TSR_TCFD) ?
+ *direction = priv->direction[count->id] ?
COUNTER_COUNT_DIRECTION_FORWARD : COUNTER_COUNT_DIRECTION_BACKWARD;
+
mutex_unlock(&priv->lock);
return 0;
@@ -469,6 +476,7 @@ static void rz_mtu3_terminate_counter(struct counter_device *counter, int id)
struct rz_mtu3_cnt *const priv = counter_priv(counter);
priv->count[id] = rz_mtu3_get_count(ch, id);
+ priv->direction[id] = rz_mtu3_get_direction(ch);
if (id == RZ_MTU3_32_BIT_CH) {
rz_mtu3_disable(ch2);
--
2.55.0
^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH 13/15] counter: rz-mtu3-cnt: keep cascade_counts_enable in sync with usage
2026-09-14 20:35 [PATCH 00/15] MTU3 counter fixes and improvements Cosmin Tanislav
` (11 preceding siblings ...)
2026-09-14 20:36 ` [PATCH 12/15] counter: rz-mtu3-cnt: cache direction values Cosmin Tanislav
@ 2026-09-14 20:36 ` Cosmin Tanislav
2026-09-14 20:36 ` [PATCH 14/15] counter: rz-mtu3-cnt: drop conditional locks Cosmin Tanislav
` (2 subsequent siblings)
15 siblings, 0 replies; 20+ messages in thread
From: Cosmin Tanislav @ 2026-09-14 20:36 UTC (permalink / raw)
To: Biju Das, William Breathitt Gray, Lee Jones
Cc: linux-iio, linux-renesas-soc, linux-kernel, Cosmin Tanislav
LWA is enabled/disabled by writing 1/0 to the cascade_counts_enable
sysfs entry.
The 32-bit channel cannot function without LWA being enabled, and the
16-bit channels cannot function with LWA enabled.
PWM mode cannot function with LWA enabled.
It is possible to accidentally leave LWA enabled while trying to use PWM
mode, leading to unintended access to the TGRA or TGRB registers of
MTU1 and MTU2, which should not be accessed while LWA is enabled.
It is also possible to toggle LWA while PWM is enabled on MTU2, as the
checks inside rz_mtu3_lock_if_ch0_is_enabled() do not check if MTU2 is
busy, even if this setting affects MTU2.
To handle this, write 1 to the LWA bit when enabling the 32-bit channel,
and automatically write 0 to it when disabling the 32-bit channel, to
ensure that LWA is always in the correct state for any possible usage.
Remove rz_mtu3_is_counter_invalid() checks as it is now guaranteed that
LWA has the proper value if a count is enabled.
Keep the cascade_counts_enable sysfs entry for ABI compatibility.
Signed-off-by: Cosmin Tanislav <cosmin-gabriel.tanislav.xa@renesas.com>
---
drivers/counter/rz-mtu3-cnt.c | 94 +++++++----------------------------
1 file changed, 17 insertions(+), 77 deletions(-)
diff --git a/drivers/counter/rz-mtu3-cnt.c b/drivers/counter/rz-mtu3-cnt.c
index 5fd12941df4c..d519e6ab56ee 100644
--- a/drivers/counter/rz-mtu3-cnt.c
+++ b/drivers/counter/rz-mtu3-cnt.c
@@ -81,6 +81,7 @@ struct rz_mtu3_cnt {
bool count_is_enabled[RZ_MTU3_MAX_LOGICAL_CNTR_CHANNELS];
bool direction[RZ_MTU3_MAX_LOGICAL_CNTR_CHANNELS];
bool mtclkc_mtclkd;
+ bool cascade_enable;
};
static const enum counter_function rz_mtu3_count_functions[] = {
@@ -102,44 +103,6 @@ static inline struct rz_mtu3_channel *rz_mtu3_get_ch(struct counter_device *coun
return &priv->ch[ch_id];
}
-static bool rz_mtu3_is_counter_invalid(struct counter_device *counter, int id)
-{
- struct rz_mtu3_cnt *const priv = counter_priv(counter);
- unsigned long tmdr;
-
- pm_runtime_get_sync(counter->parent);
- tmdr = rz_mtu3_shared_reg_read(priv->ch, RZ_MTU3_TMDR3);
- pm_runtime_put(counter->parent);
-
- if (id == RZ_MTU3_32_BIT_CH && test_bit(RZ_MTU3_TMDR3_LWA, &tmdr))
- return false;
-
- if (id != RZ_MTU3_32_BIT_CH && !test_bit(RZ_MTU3_TMDR3_LWA, &tmdr))
- return false;
-
- return true;
-}
-
-static int rz_mtu3_lock_if_counter_is_valid(struct counter_device *counter,
- struct rz_mtu3_channel *const ch,
- struct rz_mtu3_cnt *const priv,
- int id)
-{
- mutex_lock(&priv->lock);
-
- if (ch->is_busy && !priv->count_is_enabled[id]) {
- mutex_unlock(&priv->lock);
- return -EINVAL;
- }
-
- if (rz_mtu3_is_counter_invalid(counter, id)) {
- mutex_unlock(&priv->lock);
- return -EBUSY;
- }
-
- return 0;
-}
-
static int rz_mtu3_lock_if_count_is_enabled(struct rz_mtu3_channel *const ch,
struct rz_mtu3_cnt *const priv,
int id)
@@ -195,17 +158,19 @@ static bool rz_mtu3_get_direction(struct rz_mtu3_channel *const ch)
return !!(tsr & RZ_MTU3_TSR_TCFD);
}
+static void rz_mtu3_set_lwa(struct rz_mtu3_channel *const ch, bool enable)
+{
+ rz_mtu3_shared_reg_update_bit(ch, RZ_MTU3_TMDR3, RZ_MTU3_TMDR3_LWA,
+ enable);
+}
+
static int rz_mtu3_count_read(struct counter_device *counter,
struct counter_count *count, u64 *val)
{
struct rz_mtu3_channel *const ch = rz_mtu3_get_ch(counter, count->id);
struct rz_mtu3_cnt *const priv = counter_priv(counter);
- int ret;
-
- ret = rz_mtu3_lock_if_counter_is_valid(counter, ch, priv, count->id);
- if (ret)
- return ret;
+ mutex_lock(&priv->lock);
if (priv->count_is_enabled[count->id])
priv->count[count->id] = rz_mtu3_get_count(ch, count->id);
*val = priv->count[count->id];
@@ -219,12 +184,8 @@ static int rz_mtu3_count_write(struct counter_device *counter,
{
struct rz_mtu3_channel *const ch = rz_mtu3_get_ch(counter, count->id);
struct rz_mtu3_cnt *const priv = counter_priv(counter);
- int ret;
-
- ret = rz_mtu3_lock_if_counter_is_valid(counter, ch, priv, count->id);
- if (ret)
- return ret;
+ mutex_lock(&priv->lock);
if (priv->count_is_enabled[count->id])
rz_mtu3_set_count(ch, count->id, val);
priv->count[count->id] = val;
@@ -343,14 +304,9 @@ static int rz_mtu3_count_ceiling_read(struct counter_device *counter,
struct counter_count *count,
u64 *ceiling)
{
- struct rz_mtu3_channel *const ch = rz_mtu3_get_ch(counter, count->id);
struct rz_mtu3_cnt *const priv = counter_priv(counter);
- int ret;
-
- ret = rz_mtu3_lock_if_counter_is_valid(counter, ch, priv, count->id);
- if (ret)
- return ret;
+ mutex_lock(&priv->lock);
*ceiling = priv->ceiling[count->id];
mutex_unlock(&priv->lock);
return 0;
@@ -362,11 +318,8 @@ static int rz_mtu3_count_ceiling_write(struct counter_device *counter,
{
struct rz_mtu3_channel *const ch = rz_mtu3_get_ch(counter, count->id);
struct rz_mtu3_cnt *const priv = counter_priv(counter);
- int ret;
- ret = rz_mtu3_lock_if_counter_is_valid(counter, ch, priv, count->id);
- if (ret)
- return ret;
+ mutex_lock(&priv->lock);
switch (count->id) {
case RZ_MTU3_16_BIT_MTU1_CH:
@@ -403,6 +356,7 @@ static void rz_mtu3_32bit_cnt_setting(struct counter_device *counter)
struct rz_mtu3_cnt *const priv = counter_priv(counter);
rz_mtu3_set_phcksel(priv->ch, priv->mtclkc_mtclkd);
+ rz_mtu3_set_lwa(ch1, true);
rz_mtu3_8bit_ch_write(ch1, RZ_MTU3_TMDR1,
priv->timer_mode[RZ_MTU3_32_BIT_CH]);
@@ -479,6 +433,7 @@ static void rz_mtu3_terminate_counter(struct counter_device *counter, int id)
priv->direction[id] = rz_mtu3_get_direction(ch);
if (id == RZ_MTU3_32_BIT_CH) {
+ rz_mtu3_set_lwa(ch1, false);
rz_mtu3_disable(ch2);
rz_mtu3_disable(ch1);
rz_mtu3_release_channel(ch2);
@@ -549,17 +504,9 @@ static int rz_mtu3_cascade_counts_enable_get(struct counter_device *counter,
u8 *cascade_enable)
{
struct rz_mtu3_cnt *const priv = counter_priv(counter);
- unsigned long tmdr;
- int ret;
- ret = rz_mtu3_lock_if_ch0_is_enabled(priv);
- if (ret)
- return ret;
-
- pm_runtime_get_sync(counter->parent);
- tmdr = rz_mtu3_shared_reg_read(priv->ch, RZ_MTU3_TMDR3);
- pm_runtime_put(counter->parent);
- *cascade_enable = test_bit(RZ_MTU3_TMDR3_LWA, &tmdr);
+ mutex_lock(&priv->lock);
+ *cascade_enable = priv->cascade_enable;
mutex_unlock(&priv->lock);
return 0;
@@ -569,16 +516,9 @@ static int rz_mtu3_cascade_counts_enable_set(struct counter_device *counter,
u8 cascade_enable)
{
struct rz_mtu3_cnt *const priv = counter_priv(counter);
- int ret;
- ret = rz_mtu3_lock_if_ch0_is_enabled(priv);
- if (ret)
- return ret;
-
- pm_runtime_get_sync(counter->parent);
- rz_mtu3_shared_reg_update_bit(priv->ch, RZ_MTU3_TMDR3,
- RZ_MTU3_TMDR3_LWA, cascade_enable);
- pm_runtime_put(counter->parent);
+ mutex_lock(&priv->lock);
+ priv->cascade_enable = cascade_enable;
mutex_unlock(&priv->lock);
return 0;
--
2.55.0
^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH 14/15] counter: rz-mtu3-cnt: drop conditional locks
2026-09-14 20:35 [PATCH 00/15] MTU3 counter fixes and improvements Cosmin Tanislav
` (12 preceding siblings ...)
2026-09-14 20:36 ` [PATCH 13/15] counter: rz-mtu3-cnt: keep cascade_counts_enable in sync with usage Cosmin Tanislav
@ 2026-09-14 20:36 ` Cosmin Tanislav
2026-09-14 20:36 ` [PATCH 15/15] counter: rz-mtu3-cnt: use pm_runtime_resume_and_get() Cosmin Tanislav
2026-09-16 10:53 ` [PATCH 00/15] MTU3 counter fixes and improvements Lee Jones
15 siblings, 0 replies; 20+ messages in thread
From: Cosmin Tanislav @ 2026-09-14 20:36 UTC (permalink / raw)
To: Biju Das, William Breathitt Gray, Lee Jones
Cc: linux-iio, linux-renesas-soc, linux-kernel, Cosmin Tanislav
All read and write operations now only access the hardware if the
respective count is enabled.
As a count cannot be enabled without the underlying MTU channel(s) being
claimed as busy by the count driver, drop conditional locks and replace
them with simple locks.
Use guard() where possible.
Remove the conditional locking functions as there are no users left.
Signed-off-by: Cosmin Tanislav <cosmin-gabriel.tanislav.xa@renesas.com>
---
drivers/counter/rz-mtu3-cnt.c | 120 ++++++++--------------------------
1 file changed, 26 insertions(+), 94 deletions(-)
diff --git a/drivers/counter/rz-mtu3-cnt.c b/drivers/counter/rz-mtu3-cnt.c
index d519e6ab56ee..f3e8fe5cb818 100644
--- a/drivers/counter/rz-mtu3-cnt.c
+++ b/drivers/counter/rz-mtu3-cnt.c
@@ -103,20 +103,6 @@ static inline struct rz_mtu3_channel *rz_mtu3_get_ch(struct counter_device *coun
return &priv->ch[ch_id];
}
-static int rz_mtu3_lock_if_count_is_enabled(struct rz_mtu3_channel *const ch,
- struct rz_mtu3_cnt *const priv,
- int id)
-{
- mutex_lock(&priv->lock);
-
- if (ch->is_busy && !priv->count_is_enabled[id]) {
- mutex_unlock(&priv->lock);
- return -EINVAL;
- }
-
- return 0;
-}
-
static void rz_mtu3_set_ceiling(struct rz_mtu3_channel *const ch, int id,
u64 ceiling)
{
@@ -170,11 +156,11 @@ static int rz_mtu3_count_read(struct counter_device *counter,
struct rz_mtu3_channel *const ch = rz_mtu3_get_ch(counter, count->id);
struct rz_mtu3_cnt *const priv = counter_priv(counter);
- mutex_lock(&priv->lock);
+ guard(mutex)(&priv->lock);
+
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;
}
@@ -185,11 +171,11 @@ static int rz_mtu3_count_write(struct counter_device *counter,
struct rz_mtu3_channel *const ch = rz_mtu3_get_ch(counter, count->id);
struct rz_mtu3_cnt *const priv = counter_priv(counter);
- mutex_lock(&priv->lock);
+ guard(mutex)(&priv->lock);
+
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;
}
@@ -222,18 +208,11 @@ static int rz_mtu3_count_function_read(struct counter_device *counter,
struct counter_count *count,
enum counter_function *function)
{
- struct rz_mtu3_channel *const ch = rz_mtu3_get_ch(counter, count->id);
struct rz_mtu3_cnt *const priv = counter_priv(counter);
- int ret;
- ret = rz_mtu3_lock_if_count_is_enabled(ch, priv, count->id);
- if (ret)
- return ret;
+ guard(mutex)(&priv->lock);
- ret = rz_mtu3_count_function_read_helper(priv, count->id, function);
- mutex_unlock(&priv->lock);
-
- return ret;
+ return rz_mtu3_count_function_read_helper(priv, count->id, function);
}
static int rz_mtu3_count_function_write(struct counter_device *counter,
@@ -243,11 +222,6 @@ static int rz_mtu3_count_function_write(struct counter_device *counter,
struct rz_mtu3_channel *const ch = rz_mtu3_get_ch(counter, count->id);
struct rz_mtu3_cnt *const priv = counter_priv(counter);
u8 timer_mode;
- int ret;
-
- ret = rz_mtu3_lock_if_count_is_enabled(ch, priv, count->id);
- if (ret)
- return ret;
switch (function) {
case COUNTER_FUNCTION_QUADRATURE_X4:
@@ -265,14 +239,14 @@ static int rz_mtu3_count_function_write(struct counter_device *counter,
* - need to add RZ_MTU3_TMDR1_PH_CNT_MODE_3
* - need to add RZ_MTU3_TMDR1_PH_CNT_MODE_5
*/
- mutex_unlock(&priv->lock);
return -EINVAL;
}
+ guard(mutex)(&priv->lock);
+
if (priv->count_is_enabled[count->id])
rz_mtu3_8bit_ch_write(ch, RZ_MTU3_TMDR1, timer_mode);
priv->timer_mode[count->id] = timer_mode;
- mutex_unlock(&priv->lock);
return 0;
}
@@ -283,11 +257,8 @@ static int rz_mtu3_count_direction_read(struct counter_device *counter,
{
struct rz_mtu3_channel *const ch = rz_mtu3_get_ch(counter, count->id);
struct rz_mtu3_cnt *const priv = counter_priv(counter);
- int ret;
- ret = rz_mtu3_lock_if_count_is_enabled(ch, priv, count->id);
- if (ret)
- return ret;
+ guard(mutex)(&priv->lock);
if (priv->count_is_enabled[count->id])
priv->direction[count->id] = rz_mtu3_get_direction(ch);
@@ -295,8 +266,6 @@ static int rz_mtu3_count_direction_read(struct counter_device *counter,
*direction = priv->direction[count->id] ?
COUNTER_COUNT_DIRECTION_FORWARD : COUNTER_COUNT_DIRECTION_BACKWARD;
- mutex_unlock(&priv->lock);
-
return 0;
}
@@ -306,9 +275,9 @@ static int rz_mtu3_count_ceiling_read(struct counter_device *counter,
{
struct rz_mtu3_cnt *const priv = counter_priv(counter);
- mutex_lock(&priv->lock);
+ guard(mutex)(&priv->lock);
*ceiling = priv->ceiling[count->id];
- mutex_unlock(&priv->lock);
+
return 0;
}
@@ -319,32 +288,26 @@ static int rz_mtu3_count_ceiling_write(struct counter_device *counter,
struct rz_mtu3_channel *const ch = rz_mtu3_get_ch(counter, count->id);
struct rz_mtu3_cnt *const priv = counter_priv(counter);
- mutex_lock(&priv->lock);
-
switch (count->id) {
case RZ_MTU3_16_BIT_MTU1_CH:
case RZ_MTU3_16_BIT_MTU2_CH:
- if (ceiling > U16_MAX) {
- mutex_unlock(&priv->lock);
+ if (ceiling > U16_MAX)
return -ERANGE;
- }
break;
case RZ_MTU3_32_BIT_CH:
- if (ceiling > U32_MAX) {
- mutex_unlock(&priv->lock);
+ if (ceiling > U32_MAX)
return -ERANGE;
- }
break;
default:
/* should never reach this path */
- mutex_unlock(&priv->lock);
return -EINVAL;
}
+ guard(mutex)(&priv->lock);
+
if (priv->count_is_enabled[count->id])
rz_mtu3_set_ceiling(ch, count->id, ceiling);
priv->ceiling[count->id] = ceiling;
- mutex_unlock(&priv->lock);
return 0;
}
@@ -449,9 +412,8 @@ static int rz_mtu3_count_enable_read(struct counter_device *counter,
{
struct rz_mtu3_cnt *const priv = counter_priv(counter);
- mutex_lock(&priv->lock);
+ guard(mutex)(&priv->lock);
*enable = priv->count_is_enabled[count->id];
- mutex_unlock(&priv->lock);
return 0;
}
@@ -462,17 +424,17 @@ 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);
+ guard(mutex)(&priv->lock);
if (priv->count_is_enabled[count->id] == enable)
- goto exit;
+ return 0;
if (enable) {
pm_runtime_get_sync(counter->parent);
ret = rz_mtu3_initialize_counter(counter, count->id);
if (ret) {
pm_runtime_put(counter->parent);
- goto exit;
+ return ret;
}
priv->count_is_enabled[count->id] = true;
@@ -482,32 +444,16 @@ static int rz_mtu3_count_enable_write(struct counter_device *counter,
pm_runtime_put(counter->parent);
}
-exit:
- mutex_unlock(&priv->lock);
-
return ret;
}
-static int rz_mtu3_lock_if_ch0_is_enabled(struct rz_mtu3_cnt *const priv)
-{
- mutex_lock(&priv->lock);
- if (priv->ch->is_busy && !(priv->count_is_enabled[RZ_MTU3_16_BIT_MTU1_CH] ||
- priv->count_is_enabled[RZ_MTU3_32_BIT_CH])) {
- mutex_unlock(&priv->lock);
- return -EINVAL;
- }
-
- return 0;
-}
-
static int rz_mtu3_cascade_counts_enable_get(struct counter_device *counter,
u8 *cascade_enable)
{
struct rz_mtu3_cnt *const priv = counter_priv(counter);
- mutex_lock(&priv->lock);
+ guard(mutex)(&priv->lock);
*cascade_enable = priv->cascade_enable;
- mutex_unlock(&priv->lock);
return 0;
}
@@ -517,9 +463,8 @@ static int rz_mtu3_cascade_counts_enable_set(struct counter_device *counter,
{
struct rz_mtu3_cnt *const priv = counter_priv(counter);
- mutex_lock(&priv->lock);
+ guard(mutex)(&priv->lock);
priv->cascade_enable = cascade_enable;
- mutex_unlock(&priv->lock);
return 0;
}
@@ -528,14 +473,9 @@ static int rz_mtu3_ext_input_phase_clock_select_get(struct counter_device *count
u32 *ext_input_phase_clock_select)
{
struct rz_mtu3_cnt *const priv = counter_priv(counter);
- int ret;
-
- ret = rz_mtu3_lock_if_ch0_is_enabled(priv);
- if (ret)
- return ret;
+ guard(mutex)(&priv->lock);
*ext_input_phase_clock_select = priv->mtclkc_mtclkd;
- mutex_unlock(&priv->lock);
return 0;
}
@@ -544,17 +484,13 @@ static int rz_mtu3_ext_input_phase_clock_select_set(struct counter_device *count
u32 ext_input_phase_clock_select)
{
struct rz_mtu3_cnt *const priv = counter_priv(counter);
- int ret;
- ret = rz_mtu3_lock_if_ch0_is_enabled(priv);
- if (ret)
- return ret;
+ guard(mutex)(&priv->lock);
if (priv->count_is_enabled[RZ_MTU3_16_BIT_MTU2_CH] ||
priv->count_is_enabled[RZ_MTU3_32_BIT_CH])
rz_mtu3_set_phcksel(priv->ch, ext_input_phase_clock_select);
priv->mtclkc_mtclkd = ext_input_phase_clock_select;
- mutex_unlock(&priv->lock);
return 0;
}
@@ -580,14 +516,11 @@ static int rz_mtu3_action_read(struct counter_device *counter,
{
const bool is_signal_ab = (synapse->signal->id == SIGNAL_A_ID) ||
(synapse->signal->id == SIGNAL_B_ID);
- struct rz_mtu3_channel *const ch = rz_mtu3_get_ch(counter, count->id);
struct rz_mtu3_cnt *const priv = counter_priv(counter);
enum counter_function function;
int ret;
- ret = rz_mtu3_lock_if_count_is_enabled(ch, priv, count->id);
- if (ret)
- return ret;
+ mutex_lock(&priv->lock);
ret = rz_mtu3_count_function_read_helper(priv, count->id, &function);
if (ret) {
@@ -606,6 +539,8 @@ static int rz_mtu3_action_read(struct counter_device *counter,
}
}
+ mutex_unlock(&priv->lock);
+
switch (function) {
case COUNTER_FUNCTION_PULSE_DIRECTION:
/*
@@ -632,12 +567,9 @@ static int rz_mtu3_action_read(struct counter_device *counter,
break;
default:
/* should never reach this path */
- mutex_unlock(&priv->lock);
return -EINVAL;
}
- mutex_unlock(&priv->lock);
-
return 0;
}
--
2.55.0
^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH 15/15] counter: rz-mtu3-cnt: use pm_runtime_resume_and_get()
2026-09-14 20:35 [PATCH 00/15] MTU3 counter fixes and improvements Cosmin Tanislav
` (13 preceding siblings ...)
2026-09-14 20:36 ` [PATCH 14/15] counter: rz-mtu3-cnt: drop conditional locks Cosmin Tanislav
@ 2026-09-14 20:36 ` Cosmin Tanislav
2026-09-16 10:53 ` [PATCH 00/15] MTU3 counter fixes and improvements Lee Jones
15 siblings, 0 replies; 20+ messages in thread
From: Cosmin Tanislav @ 2026-09-14 20:36 UTC (permalink / raw)
To: Biju Das, William Breathitt Gray, Lee Jones
Cc: linux-iio, linux-renesas-soc, linux-kernel, Cosmin Tanislav
pm_runtime_get_sync() may increment the runtime PM usage count even if
the resume fails, which requires an explicit pm_runtime_put_noidle() to
balance it.
This driver ignores the return value of pm_runtime_get_sync(), risking
accessing device registers even if resume failed.
Replace it with pm_runtime_resume_and_get() and handle errors properly.
Signed-off-by: Cosmin Tanislav <cosmin-gabriel.tanislav.xa@renesas.com>
---
drivers/counter/rz-mtu3-cnt.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/drivers/counter/rz-mtu3-cnt.c b/drivers/counter/rz-mtu3-cnt.c
index f3e8fe5cb818..c6877f988b8b 100644
--- a/drivers/counter/rz-mtu3-cnt.c
+++ b/drivers/counter/rz-mtu3-cnt.c
@@ -430,7 +430,10 @@ static int rz_mtu3_count_enable_write(struct counter_device *counter,
return 0;
if (enable) {
- pm_runtime_get_sync(counter->parent);
+ ret = pm_runtime_resume_and_get(counter->parent);
+ if (ret)
+ return ret;
+
ret = rz_mtu3_initialize_counter(counter, count->id);
if (ret) {
pm_runtime_put(counter->parent);
--
2.55.0
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 00/15] MTU3 counter fixes and improvements
2026-09-14 20:35 [PATCH 00/15] MTU3 counter fixes and improvements Cosmin Tanislav
` (14 preceding siblings ...)
2026-09-14 20:36 ` [PATCH 15/15] counter: rz-mtu3-cnt: use pm_runtime_resume_and_get() Cosmin Tanislav
@ 2026-09-16 10:53 ` Lee Jones
2026-09-16 12:14 ` Cosmin-Gabriel Tanislav
15 siblings, 1 reply; 20+ messages in thread
From: Lee Jones @ 2026-09-16 10:53 UTC (permalink / raw)
To: Cosmin Tanislav
Cc: Biju Das, William Breathitt Gray, linux-iio, linux-renesas-soc,
linux-kernel
On Mon, 14 Sep 2026, Cosmin Tanislav wrote:
> This series does some code quality improvements to the MTU3 counter
> subdriver, while also fixing bugs and adding caching support so that
> registers are not touched when the counter is disabled, as there is no
> request API.
>
> Patch 3 relies on the following series to enable runtime PM on the
> parent device so that the PM domain handles it automatically.
>
> https://lore.kernel.org/lkml/20260914201456.2018403-1-cosmin-gabriel.tanislav.xa@renesas.com/
>
> Cosmin Tanislav (15):
> counter: rz-mtu3-cnt: put runtime PM on initialization error path
> counter: rz-mtu3-cnt: do not assign struct rz_mtu3_channel::dev
> counter: rz-mtu3-cnt: remove manual runtime PM handling
> counter: rz-mtu3-cnt: use device-managed pm_runtime_enable()
> counter: rz-mtu3-cnt: read enable value from cache
> counter: rz-mtu3-cnt: disable channel before releasing
> counter: rz-mtu3-cnt: unify ceiling values
> counter: rz-mtu3-cnt: cache ceiling values
> counter: rz-mtu3-cnt: cache count values
> counter: rz-mtu3-cnt: cache function values
> counter: rz-mtu3-cnt: cache external input phase clock value
> counter: rz-mtu3-cnt: cache direction values
> counter: rz-mtu3-cnt: keep cascade_counts_enable in sync with usage
> counter: rz-mtu3-cnt: drop conditional locks
> counter: rz-mtu3-cnt: use pm_runtime_resume_and_get()
>
> drivers/counter/rz-mtu3-cnt.c | 443 +++++++++++-----------------------
> 1 file changed, 139 insertions(+), 304 deletions(-)
And these 16 emails are in my inbox because ... ?
--
Lee Jones
^ permalink raw reply [flat|nested] 20+ messages in thread
* RE: [PATCH 00/15] MTU3 counter fixes and improvements
2026-09-16 10:53 ` [PATCH 00/15] MTU3 counter fixes and improvements Lee Jones
@ 2026-09-16 12:14 ` Cosmin-Gabriel Tanislav
0 siblings, 0 replies; 20+ messages in thread
From: Cosmin-Gabriel Tanislav @ 2026-09-16 12:14 UTC (permalink / raw)
To: Lee Jones
Cc: Biju Das, William Breathitt Gray, linux-iio, linux-renesas-soc,
linux-kernel
> From: Lee Jones <lee@kernel.org>
> Sent: Wednesday, September 16, 2026 1:53 PM
>
> On Mon, 14 Sep 2026, Cosmin Tanislav wrote:
>
> > This series does some code quality improvements to the MTU3 counter
> > subdriver, while also fixing bugs and adding caching support so that
> > registers are not touched when the counter is disabled, as there is no
> > request API.
> >
> > Patch 3 relies on the following series to enable runtime PM on the
> > parent device so that the PM domain handles it automatically.
> >
> > https://lore.kernel.org/lkml/20260914201456.2018403-1-cosmin-gabriel.tanislav.xa@renesas.com/
> >
> > Cosmin Tanislav (15):
> > counter: rz-mtu3-cnt: put runtime PM on initialization error path
> > counter: rz-mtu3-cnt: do not assign struct rz_mtu3_channel::dev
> > counter: rz-mtu3-cnt: remove manual runtime PM handling
> > counter: rz-mtu3-cnt: use device-managed pm_runtime_enable()
> > counter: rz-mtu3-cnt: read enable value from cache
> > counter: rz-mtu3-cnt: disable channel before releasing
> > counter: rz-mtu3-cnt: unify ceiling values
> > counter: rz-mtu3-cnt: cache ceiling values
> > counter: rz-mtu3-cnt: cache count values
> > counter: rz-mtu3-cnt: cache function values
> > counter: rz-mtu3-cnt: cache external input phase clock value
> > counter: rz-mtu3-cnt: cache direction values
> > counter: rz-mtu3-cnt: keep cascade_counts_enable in sync with usage
> > counter: rz-mtu3-cnt: drop conditional locks
> > counter: rz-mtu3-cnt: use pm_runtime_resume_and_get()
> >
> > drivers/counter/rz-mtu3-cnt.c | 443 +++++++++++-----------------------
> > 1 file changed, 139 insertions(+), 304 deletions(-)
>
> And these 16 emails are in my inbox because ... ?
>
This series has a Fixes: 0be8907359df ("counter: Add Renesas RZ/G2L
MTU3a counter driver") patch. get_maintainer.pl's blamed_fixes picked
you up based on your Signed-off-by on that commit and I forgot to trim
the results manually.
Sorry about that. I'll trim it from now on.
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 01/15] counter: rz-mtu3-cnt: put runtime PM on initialization error path
2026-09-14 20:36 ` [PATCH 01/15] counter: rz-mtu3-cnt: put runtime PM on initialization error path Cosmin Tanislav
@ 2026-09-17 0:46 ` Jonathan Cameron
2026-09-17 6:36 ` Cosmin-Gabriel Tanislav
0 siblings, 1 reply; 20+ messages in thread
From: Jonathan Cameron @ 2026-09-17 0:46 UTC (permalink / raw)
To: Cosmin Tanislav
Cc: Biju Das, William Breathitt Gray, Lee Jones, linux-iio,
linux-renesas-soc, linux-kernel, stable
On Mon, 14 Sep 2026 23:36:00 +0300
Cosmin Tanislav <cosmin-gabriel.tanislav.xa@renesas.com> wrote:
> If rz_mtu3_initialize_counter() fails, the runtime PM usage count is not
> decremented.
>
> rz_mtu3_initialize_counter() will fail if the requested channel is busy.
>
> Call pm_runtime_put() in the error path to decrement the usage count,
> and flip the check to keep the success path straightforward.
>
> Cc: stable@vger.kernel.org
> Fixes: 0be8907359df ("counter: Add Renesas RZ/G2L MTU3a counter driver")
> Signed-off-by: Cosmin Tanislav <cosmin-gabriel.tanislav.xa@renesas.com>
> ---
> drivers/counter/rz-mtu3-cnt.c | 8 ++++++--
> 1 file changed, 6 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/counter/rz-mtu3-cnt.c b/drivers/counter/rz-mtu3-cnt.c
> index 7bfb6979193c..48f183f0b54f 100644
> --- a/drivers/counter/rz-mtu3-cnt.c
> +++ b/drivers/counter/rz-mtu3-cnt.c
> @@ -506,8 +506,12 @@ static int rz_mtu3_count_enable_write(struct counter_device *counter,
> if (enable) {
> pm_runtime_get_sync(counter->parent);
> ret = rz_mtu3_initialize_counter(counter, count->id);
> - if (ret == 0)
> - priv->count_is_enabled[count->id] = true;
> + if (ret) {
> + pm_runtime_put(counter->parent);
> + goto exit;
I'd use a guard() for the mutex and return here and one more case
above. Not quite the minimal fix, but will give a more readable result.
> + }
> +
> + priv->count_is_enabled[count->id] = true;
> } else {
> rz_mtu3_terminate_counter(counter, count->id);
> priv->count_is_enabled[count->id] = false;
^ permalink raw reply [flat|nested] 20+ messages in thread
* RE: [PATCH 01/15] counter: rz-mtu3-cnt: put runtime PM on initialization error path
2026-09-17 0:46 ` Jonathan Cameron
@ 2026-09-17 6:36 ` Cosmin-Gabriel Tanislav
0 siblings, 0 replies; 20+ messages in thread
From: Cosmin-Gabriel Tanislav @ 2026-09-17 6:36 UTC (permalink / raw)
To: Jonathan Cameron
Cc: Biju Das, William Breathitt Gray, Lee Jones, linux-iio,
linux-renesas-soc, linux-kernel, stable
> From: Jonathan Cameron <jic23@kernel.org>
> Sent: Thursday, September 17, 2026 3:46 AM
>
> On Mon, 14 Sep 2026 23:36:00 +0300
> Cosmin Tanislav <cosmin-gabriel.tanislav.xa@renesas.com> wrote:
>
> > If rz_mtu3_initialize_counter() fails, the runtime PM usage count is not
> > decremented.
> >
> > rz_mtu3_initialize_counter() will fail if the requested channel is busy.
> >
> > Call pm_runtime_put() in the error path to decrement the usage count,
> > and flip the check to keep the success path straightforward.
> >
> > Cc: stable@vger.kernel.org
> > Fixes: 0be8907359df ("counter: Add Renesas RZ/G2L MTU3a counter driver")
> > Signed-off-by: Cosmin Tanislav <cosmin-gabriel.tanislav.xa@renesas.com>
> > ---
> > drivers/counter/rz-mtu3-cnt.c | 8 ++++++--
> > 1 file changed, 6 insertions(+), 2 deletions(-)
> >
> > diff --git a/drivers/counter/rz-mtu3-cnt.c b/drivers/counter/rz-mtu3-cnt.c
> > index 7bfb6979193c..48f183f0b54f 100644
> > --- a/drivers/counter/rz-mtu3-cnt.c
> > +++ b/drivers/counter/rz-mtu3-cnt.c
> > @@ -506,8 +506,12 @@ static int rz_mtu3_count_enable_write(struct counter_device *counter,
> > if (enable) {
> > pm_runtime_get_sync(counter->parent);
> > ret = rz_mtu3_initialize_counter(counter, count->id);
> > - if (ret == 0)
> > - priv->count_is_enabled[count->id] = true;
> > + if (ret) {
> > + pm_runtime_put(counter->parent);
> > + goto exit;
>
> I'd use a guard() for the mutex and return here and one more case
> above. Not quite the minimal fix, but will give a more readable result.
>
Hi Jonathan. I did the minimal fix here so it can be backported easily.
Patch 14/15 does the guard() conversion for the mutexes. Is that okay?
^ permalink raw reply [flat|nested] 20+ messages in thread
end of thread, other threads:[~2026-09-17 6:36 UTC | newest]
Thread overview: 20+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-14 20:35 [PATCH 00/15] MTU3 counter fixes and improvements Cosmin Tanislav
2026-09-14 20:36 ` [PATCH 01/15] counter: rz-mtu3-cnt: put runtime PM on initialization error path Cosmin Tanislav
2026-09-17 0:46 ` Jonathan Cameron
2026-09-17 6:36 ` Cosmin-Gabriel Tanislav
2026-09-14 20:36 ` [PATCH 02/15] counter: rz-mtu3-cnt: do not assign struct rz_mtu3_channel::dev Cosmin Tanislav
2026-09-14 20:36 ` [PATCH 03/15] counter: rz-mtu3-cnt: remove manual runtime PM handling Cosmin Tanislav
2026-09-14 20:36 ` [PATCH 04/15] counter: rz-mtu3-cnt: use device-managed pm_runtime_enable() Cosmin Tanislav
2026-09-14 20:36 ` [PATCH 05/15] counter: rz-mtu3-cnt: read enable value from cache Cosmin Tanislav
2026-09-14 20:36 ` [PATCH 06/15] counter: rz-mtu3-cnt: disable channel before releasing Cosmin Tanislav
2026-09-14 20:36 ` [PATCH 07/15] counter: rz-mtu3-cnt: unify ceiling values Cosmin Tanislav
2026-09-14 20:36 ` [PATCH 08/15] counter: rz-mtu3-cnt: cache " Cosmin Tanislav
2026-09-14 20:36 ` [PATCH 09/15] counter: rz-mtu3-cnt: cache count values Cosmin Tanislav
2026-09-14 20:36 ` [PATCH 10/15] counter: rz-mtu3-cnt: cache function values Cosmin Tanislav
2026-09-14 20:36 ` [PATCH 11/15] counter: rz-mtu3-cnt: cache external input phase clock value Cosmin Tanislav
2026-09-14 20:36 ` [PATCH 12/15] counter: rz-mtu3-cnt: cache direction values Cosmin Tanislav
2026-09-14 20:36 ` [PATCH 13/15] counter: rz-mtu3-cnt: keep cascade_counts_enable in sync with usage Cosmin Tanislav
2026-09-14 20:36 ` [PATCH 14/15] counter: rz-mtu3-cnt: drop conditional locks Cosmin Tanislav
2026-09-14 20:36 ` [PATCH 15/15] counter: rz-mtu3-cnt: use pm_runtime_resume_and_get() Cosmin Tanislav
2026-09-16 10:53 ` [PATCH 00/15] MTU3 counter fixes and improvements Lee Jones
2026-09-16 12:14 ` Cosmin-Gabriel Tanislav
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®