[PATCH v5 1/3] clocksource/drivers/timer-tegra186: add WDIOC_GETTIMELEFT support

Robert Lin posted 3 patches 9 months, 3 weeks ago
There is a newer version of this series
[PATCH v5 1/3] clocksource/drivers/timer-tegra186: add WDIOC_GETTIMELEFT support
Posted by Robert Lin 9 months, 3 weeks ago
From: Pohsun Su <pohsuns@nvidia.com>

This change adds support for WDIOC_GETTIMELEFT so userspace
programs can get the number of seconds before system reset by
the watchdog timer via ioctl.

Signed-off-by: Pohsun Su <pohsuns@nvidia.com>
Signed-off-by: Robert Lin <robelin@nvidia.com>
---
 drivers/clocksource/timer-tegra186.c | 58 +++++++++++++++++++++++++++-
 1 file changed, 57 insertions(+), 1 deletion(-)

diff --git a/drivers/clocksource/timer-tegra186.c b/drivers/clocksource/timer-tegra186.c
index ea742889ee06..56d08bf1b6b0 100644
--- a/drivers/clocksource/timer-tegra186.c
+++ b/drivers/clocksource/timer-tegra186.c
@@ -1,8 +1,9 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
- * Copyright (c) 2019-2020 NVIDIA Corporation. All rights reserved.
+ * Copyright (c) 2019-2025 NVIDIA Corporation. All rights reserved.
  */
 
+#include <linux/bitfield.h>
 #include <linux/clocksource.h>
 #include <linux/module.h>
 #include <linux/interrupt.h>
@@ -30,6 +31,7 @@
 
 #define TMRSR 0x004
 #define  TMRSR_INTR_CLR BIT(30)
+#define  TMRSR_PCV GENMASK(28, 0)
 
 #define TMRCSSR 0x008
 #define  TMRCSSR_SRC_USEC (0 << 0)
@@ -46,6 +48,9 @@
 #define  WDTCR_TIMER_SOURCE_MASK 0xf
 #define  WDTCR_TIMER_SOURCE(x) ((x) & 0xf)
 
+#define WDTSR 0x004
+#define  WDTSR_CURRENT_EXPIRATION_COUNT GENMASK(14, 12)
+
 #define WDTCMDR 0x008
 #define  WDTCMDR_DISABLE_COUNTER BIT(1)
 #define  WDTCMDR_START_COUNTER BIT(0)
@@ -235,12 +240,63 @@ static int tegra186_wdt_set_timeout(struct watchdog_device *wdd,
 	return 0;
 }
 
+static unsigned int tegra186_wdt_get_timeleft(struct watchdog_device *wdd)
+{
+	struct tegra186_wdt *wdt = to_tegra186_wdt(wdd);
+	u32 timeleft, expiration, val;
+
+	if (!watchdog_active(&wdt->base)) {
+		/* return zero if the watchdog timer is not activated. */
+		return 0;
+	}
+
+	/*
+	 * Reset occurs on the fifth expiration of the
+	 * watchdog timer and so when the watchdog timer is configured,
+	 * the actual value programmed into the counter is 1/5 of the
+	 * timeout value. Once the counter reaches 0, expiration count
+	 * will be increased by 1 and the down counter restarts.
+	 * Hence to get the time left before system reset we must
+	 * combine 2 parts:
+	 * 1. value of the current down counter
+	 * 2. (number of counter expirations remaining) * (timeout/5)
+	 */
+
+	/* Get the current number of counter expirations. Should be a
+	 * value between 0 and 4
+	 */
+	val = readl_relaxed(wdt->regs + WDTSR);
+	expiration = FIELD_GET(WDTSR_CURRENT_EXPIRATION_COUNT, val);
+	if (WARN_ON(expiration > 4))
+             return 0;
+
+	/* Get the current counter value in microsecond.
+	 */
+	val = readl_relaxed(wdt->tmr->regs + TMRSR);
+	timeleft = FIELD_GET(TMRSR_PCV, val);
+
+	/*
+	 * Calculate the time remaining by adding the time for the
+	 * counter value to the time of the counter expirations that
+	 * remain. Do the multiplication first on purpose just to keep
+	 * the precision due to the integer division.
+	 */
+	timeleft += wdt->base.timeout * (4 - expiration) / 5;
+	/*
+	 * Convert the current counter value to seconds,
+	 * rounding up to the nearest second.
+	 */
+	timeleft = (timeleft + USEC_PER_SEC / 2) / USEC_PER_SEC;
+	return timeleft;
+}
+
 static const struct watchdog_ops tegra186_wdt_ops = {
 	.owner = THIS_MODULE,
 	.start = tegra186_wdt_start,
 	.stop = tegra186_wdt_stop,
 	.ping = tegra186_wdt_ping,
 	.set_timeout = tegra186_wdt_set_timeout,
+	.get_timeleft = tegra186_wdt_get_timeleft,
 };
 
 static struct tegra186_wdt *tegra186_wdt_create(struct tegra186_timer *tegra,
-- 
2.34.1
Re: [PATCH v5 1/3] clocksource/drivers/timer-tegra186: add WDIOC_GETTIMELEFT support
Posted by Daniel Lezcano 9 months, 2 weeks ago
On Mon, Apr 21, 2025 at 06:08:19PM +0800, Robert Lin wrote:
> From: Pohsun Su <pohsuns@nvidia.com>
> 
> This change adds support for WDIOC_GETTIMELEFT so userspace
> programs can get the number of seconds before system reset by
> the watchdog timer via ioctl.
> 
> Signed-off-by: Pohsun Su <pohsuns@nvidia.com>
> Signed-off-by: Robert Lin <robelin@nvidia.com>
> ---

Hi Robert,

I realize that this driver should be split in two and the watchdog part go
under drivers/watchdog.

>  drivers/clocksource/timer-tegra186.c | 58 +++++++++++++++++++++++++++-
>  1 file changed, 57 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/clocksource/timer-tegra186.c b/drivers/clocksource/timer-tegra186.c
> index ea742889ee06..56d08bf1b6b0 100644
> --- a/drivers/clocksource/timer-tegra186.c
> +++ b/drivers/clocksource/timer-tegra186.c
> @@ -1,8 +1,9 @@
>  // SPDX-License-Identifier: GPL-2.0-only
>  /*
> - * Copyright (c) 2019-2020 NVIDIA Corporation. All rights reserved.
> + * Copyright (c) 2019-2025 NVIDIA Corporation. All rights reserved.
>   */
>  
> +#include <linux/bitfield.h>
>  #include <linux/clocksource.h>
>  #include <linux/module.h>
>  #include <linux/interrupt.h>
> @@ -30,6 +31,7 @@
>  
>  #define TMRSR 0x004
>  #define  TMRSR_INTR_CLR BIT(30)
> +#define  TMRSR_PCV GENMASK(28, 0)
>  
>  #define TMRCSSR 0x008
>  #define  TMRCSSR_SRC_USEC (0 << 0)
> @@ -46,6 +48,9 @@
>  #define  WDTCR_TIMER_SOURCE_MASK 0xf
>  #define  WDTCR_TIMER_SOURCE(x) ((x) & 0xf)
>  
> +#define WDTSR 0x004
> +#define  WDTSR_CURRENT_EXPIRATION_COUNT GENMASK(14, 12)
> +
>  #define WDTCMDR 0x008
>  #define  WDTCMDR_DISABLE_COUNTER BIT(1)
>  #define  WDTCMDR_START_COUNTER BIT(0)
> @@ -235,12 +240,63 @@ static int tegra186_wdt_set_timeout(struct watchdog_device *wdd,
>  	return 0;
>  }
>  
> +static unsigned int tegra186_wdt_get_timeleft(struct watchdog_device *wdd)
> +{
> +	struct tegra186_wdt *wdt = to_tegra186_wdt(wdd);
> +	u32 timeleft, expiration, val;
> +
> +	if (!watchdog_active(&wdt->base)) {
> +		/* return zero if the watchdog timer is not activated. */
> +		return 0;
> +	}
> +
> +	/*
> +	 * Reset occurs on the fifth expiration of the
> +	 * watchdog timer and so when the watchdog timer is configured,
> +	 * the actual value programmed into the counter is 1/5 of the
> +	 * timeout value. Once the counter reaches 0, expiration count
> +	 * will be increased by 1 and the down counter restarts.
> +	 * Hence to get the time left before system reset we must
> +	 * combine 2 parts:
> +	 * 1. value of the current down counter
> +	 * 2. (number of counter expirations remaining) * (timeout/5)
> +	 */
> +
> +	/* Get the current number of counter expirations. Should be a
> +	 * value between 0 and 4
> +	 */
> +	val = readl_relaxed(wdt->regs + WDTSR);
> +	expiration = FIELD_GET(WDTSR_CURRENT_EXPIRATION_COUNT, val);
> +	if (WARN_ON(expiration > 4))
> +             return 0;
> +
> +	/* Get the current counter value in microsecond.
> +	 */
> +	val = readl_relaxed(wdt->tmr->regs + TMRSR);
> +	timeleft = FIELD_GET(TMRSR_PCV, val);
> +
> +	/*
> +	 * Calculate the time remaining by adding the time for the
> +	 * counter value to the time of the counter expirations that
> +	 * remain. Do the multiplication first on purpose just to keep
> +	 * the precision due to the integer division.
> +	 */
> +	timeleft += wdt->base.timeout * (4 - expiration) / 5;
> +	/*
> +	 * Convert the current counter value to seconds,
> +	 * rounding up to the nearest second.
> +	 */
> +	timeleft = (timeleft + USEC_PER_SEC / 2) / USEC_PER_SEC;
> +	return timeleft;
> +}
> +
>  static const struct watchdog_ops tegra186_wdt_ops = {
>  	.owner = THIS_MODULE,
>  	.start = tegra186_wdt_start,
>  	.stop = tegra186_wdt_stop,
>  	.ping = tegra186_wdt_ping,
>  	.set_timeout = tegra186_wdt_set_timeout,
> +	.get_timeleft = tegra186_wdt_get_timeleft,
>  };
>  
>  static struct tegra186_wdt *tegra186_wdt_create(struct tegra186_timer *tegra,
> -- 
> 2.34.1
> 

-- 

 <http://www.linaro.org/> Linaro.org │ Open source software for ARM SoCs

Follow Linaro:  <http://www.facebook.com/pages/Linaro> Facebook |
<http://twitter.com/#!/linaroorg> Twitter |
<http://www.linaro.org/linaro-blog/> Blog
Re: [PATCH v5 1/3] clocksource/drivers/timer-tegra186: add WDIOC_GETTIMELEFT support
Posted by Jon Hunter 9 months, 2 weeks ago
Hi Daniel,

On 29/04/2025 09:59, Daniel Lezcano wrote:
> On Mon, Apr 21, 2025 at 06:08:19PM +0800, Robert Lin wrote:
>> From: Pohsun Su <pohsuns@nvidia.com>
>>
>> This change adds support for WDIOC_GETTIMELEFT so userspace
>> programs can get the number of seconds before system reset by
>> the watchdog timer via ioctl.
>>
>> Signed-off-by: Pohsun Su <pohsuns@nvidia.com>
>> Signed-off-by: Robert Lin <robelin@nvidia.com>
>> ---
> 
> Hi Robert,
> 
> I realize that this driver should be split in two and the watchdog part go
> under drivers/watchdog.

Are there any other examples you know of where the timer is split in 
this way? It is not clear to me how you propose we do this?

BTW, for this series, I just want to get these updates merged. Any other 
re-factoring we can handle later.

Cheers!
Jon

-- 
nvpublic
Re: [PATCH v5 1/3] clocksource/drivers/timer-tegra186: add WDIOC_GETTIMELEFT support
Posted by Daniel Lezcano 9 months, 2 weeks ago
On 29/04/2025 11:15, Jon Hunter wrote:
> Hi Daniel,
> 
> On 29/04/2025 09:59, Daniel Lezcano wrote:
>> On Mon, Apr 21, 2025 at 06:08:19PM +0800, Robert Lin wrote:
>>> From: Pohsun Su <pohsuns@nvidia.com>
>>>
>>> This change adds support for WDIOC_GETTIMELEFT so userspace
>>> programs can get the number of seconds before system reset by
>>> the watchdog timer via ioctl.
>>>
>>> Signed-off-by: Pohsun Su <pohsuns@nvidia.com>
>>> Signed-off-by: Robert Lin <robelin@nvidia.com>
>>> ---
>>
>> Hi Robert,
>>
>> I realize that this driver should be split in two and the watchdog 
>> part go
>> under drivers/watchdog.
> 
> Are there any other examples you know of where the timer is split in 
> this way? It is not clear to me how you propose we do this?

Just keep the clocksource and move the watchdog code (everything related 
to the watchdog_ops) to a new driver under drivers/watchdog

BTW, there are three clocksources with the same rating, what is the 
point of having them supported ?

Is it not the architected clocksource enough ?

May be the clocksource can be removed and the driver remains a pure 
watchdog driver ?

> BTW, for this series, I just want to get these updates merged. Any other 
> re-factoring we can handle later.
> 
> Cheers!
> Jon
> 


-- 
<http://www.linaro.org/> Linaro.org │ Open source software for ARM SoCs

Follow Linaro:  <http://www.facebook.com/pages/Linaro> Facebook |
<http://twitter.com/#!/linaroorg> Twitter |
<http://www.linaro.org/linaro-blog/> Blog
Re: [PATCH v5 1/3] clocksource/drivers/timer-tegra186: add WDIOC_GETTIMELEFT support
Posted by Thierry Reding 9 months, 2 weeks ago
On Tue, Apr 29, 2025 at 03:19:25PM +0200, Daniel Lezcano wrote:
> On 29/04/2025 11:15, Jon Hunter wrote:
> > Hi Daniel,
> > 
> > On 29/04/2025 09:59, Daniel Lezcano wrote:
> > > On Mon, Apr 21, 2025 at 06:08:19PM +0800, Robert Lin wrote:
> > > > From: Pohsun Su <pohsuns@nvidia.com>
> > > > 
> > > > This change adds support for WDIOC_GETTIMELEFT so userspace
> > > > programs can get the number of seconds before system reset by
> > > > the watchdog timer via ioctl.
> > > > 
> > > > Signed-off-by: Pohsun Su <pohsuns@nvidia.com>
> > > > Signed-off-by: Robert Lin <robelin@nvidia.com>
> > > > ---
> > > 
> > > Hi Robert,
> > > 
> > > I realize that this driver should be split in two and the watchdog
> > > part go
> > > under drivers/watchdog.
> > 
> > Are there any other examples you know of where the timer is split in
> > this way? It is not clear to me how you propose we do this?
> 
> Just keep the clocksource and move the watchdog code (everything related to
> the watchdog_ops) to a new driver under drivers/watchdog

That's a bad idea. This is all a single register space, so we can't have
"proper" drivers (i.e. ones that exclusively request I/O memory regions)
if we split them up.

I understand that it's nice and easy to have things split up along
subsystem boundaries, but sometimes hardware designs just aren't that
cleanly separated.

> BTW, there are three clocksources with the same rating, what is the point of
> having them supported ?
> 
> Is it not the architected clocksource enough ?

The TSC clock source that this driver exposes is different from the
architected timer. It's a SoC-wide clock that is routed to various IP
blocks and used for timestamping events. This clocksource allows these
events to be properly compared, etc.

Thierry
Re: [PATCH v5 1/3] clocksource/drivers/timer-tegra186: add WDIOC_GETTIMELEFT support
Posted by Daniel Lezcano 9 months, 2 weeks ago
On Tue, Apr 29, 2025 at 04:23:22PM +0200, Thierry Reding wrote:
> On Tue, Apr 29, 2025 at 03:19:25PM +0200, Daniel Lezcano wrote:
> > On 29/04/2025 11:15, Jon Hunter wrote:
> > > Hi Daniel,
> > > 
> > > On 29/04/2025 09:59, Daniel Lezcano wrote:
> > > > On Mon, Apr 21, 2025 at 06:08:19PM +0800, Robert Lin wrote:
> > > > > From: Pohsun Su <pohsuns@nvidia.com>
> > > > > 
> > > > > This change adds support for WDIOC_GETTIMELEFT so userspace
> > > > > programs can get the number of seconds before system reset by
> > > > > the watchdog timer via ioctl.
> > > > > 
> > > > > Signed-off-by: Pohsun Su <pohsuns@nvidia.com>
> > > > > Signed-off-by: Robert Lin <robelin@nvidia.com>
> > > > > ---
> > > > 
> > > > Hi Robert,
> > > > 
> > > > I realize that this driver should be split in two and the watchdog
> > > > part go
> > > > under drivers/watchdog.
> > > 
> > > Are there any other examples you know of where the timer is split in
> > > this way? It is not clear to me how you propose we do this?
> > 
> > Just keep the clocksource and move the watchdog code (everything related to
> > the watchdog_ops) to a new driver under drivers/watchdog
> 
> That's a bad idea. This is all a single register space, so we can't have
> "proper" drivers (i.e. ones that exclusively request I/O memory regions)
> if we split them up.
> 
> I understand that it's nice and easy to have things split up along
> subsystem boundaries, but sometimes hardware designs just aren't that
> cleanly separated.

Yes, that's true.

The driver has a lot of watchdog code inside and I think it is
possible to move most part of it under drivers/watchdog. Perhaps by
exporting tegra186_wdt_disable() / tegra186_wdt_enable().

Anyway, I understand that is an important change and I don't want to
block this series for this reason. At the first glance, these changes
seem to be fine for me, I'll just do a last review and comment/pick
them.

> > BTW, there are three clocksources with the same rating, what is the point of
> > having them supported ?
> > 
> > Is it not the architected clocksource enough ?
> 
> The TSC clock source that this driver exposes is different from the
> architected timer. It's a SoC-wide clock that is routed to various IP
> blocks and used for timestamping events. This clocksource allows these
> events to be properly compared, etc.

I see, thanks for clarifying

  -- Daniel


-- 

 <http://www.linaro.org/> Linaro.org │ Open source software for ARM SoCs

Follow Linaro:  <http://www.facebook.com/pages/Linaro> Facebook |
<http://twitter.com/#!/linaroorg> Twitter |
<http://www.linaro.org/linaro-blog/> Blog
Re: [PATCH v5 1/3] clocksource/drivers/timer-tegra186: add WDIOC_GETTIMELEFT support
Posted by Jon Hunter 9 months, 2 weeks ago
Hi Robert,

On 21/04/2025 11:08, Robert Lin wrote:
> From: Pohsun Su <pohsuns@nvidia.com>
> 
> This change adds support for WDIOC_GETTIMELEFT so userspace
> programs can get the number of seconds before system reset by
> the watchdog timer via ioctl.
> 
> Signed-off-by: Pohsun Su <pohsuns@nvidia.com>
> Signed-off-by: Robert Lin <robelin@nvidia.com>
> ---
>   drivers/clocksource/timer-tegra186.c | 58 +++++++++++++++++++++++++++-
>   1 file changed, 57 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/clocksource/timer-tegra186.c b/drivers/clocksource/timer-tegra186.c
> index ea742889ee06..56d08bf1b6b0 100644
> --- a/drivers/clocksource/timer-tegra186.c
> +++ b/drivers/clocksource/timer-tegra186.c
> @@ -1,8 +1,9 @@
>   // SPDX-License-Identifier: GPL-2.0-only
>   /*
> - * Copyright (c) 2019-2020 NVIDIA Corporation. All rights reserved.
> + * Copyright (c) 2019-2025 NVIDIA Corporation. All rights reserved.
>    */
>   
> +#include <linux/bitfield.h>
>   #include <linux/clocksource.h>
>   #include <linux/module.h>
>   #include <linux/interrupt.h>
> @@ -30,6 +31,7 @@
>   
>   #define TMRSR 0x004
>   #define  TMRSR_INTR_CLR BIT(30)
> +#define  TMRSR_PCV GENMASK(28, 0)
>   
>   #define TMRCSSR 0x008
>   #define  TMRCSSR_SRC_USEC (0 << 0)
> @@ -46,6 +48,9 @@
>   #define  WDTCR_TIMER_SOURCE_MASK 0xf
>   #define  WDTCR_TIMER_SOURCE(x) ((x) & 0xf)
>   
> +#define WDTSR 0x004
> +#define  WDTSR_CURRENT_EXPIRATION_COUNT GENMASK(14, 12)
> +
>   #define WDTCMDR 0x008
>   #define  WDTCMDR_DISABLE_COUNTER BIT(1)
>   #define  WDTCMDR_START_COUNTER BIT(0)
> @@ -235,12 +240,63 @@ static int tegra186_wdt_set_timeout(struct watchdog_device *wdd,
>   	return 0;
>   }
>   
> +static unsigned int tegra186_wdt_get_timeleft(struct watchdog_device *wdd)
> +{
> +	struct tegra186_wdt *wdt = to_tegra186_wdt(wdd);
> +	u32 timeleft, expiration, val;
> +
> +	if (!watchdog_active(&wdt->base)) {
> +		/* return zero if the watchdog timer is not activated. */
> +		return 0;
> +	}
> +
> +	/*
> +	 * Reset occurs on the fifth expiration of the
> +	 * watchdog timer and so when the watchdog timer is configured,
> +	 * the actual value programmed into the counter is 1/5 of the
> +	 * timeout value. Once the counter reaches 0, expiration count
> +	 * will be increased by 1 and the down counter restarts.
> +	 * Hence to get the time left before system reset we must
> +	 * combine 2 parts:
> +	 * 1. value of the current down counter
> +	 * 2. (number of counter expirations remaining) * (timeout/5)
> +	 */
> +
> +	/* Get the current number of counter expirations. Should be a
> +	 * value between 0 and 4
> +	 */
> +	val = readl_relaxed(wdt->regs + WDTSR);
> +	expiration = FIELD_GET(WDTSR_CURRENT_EXPIRATION_COUNT, val);
> +	if (WARN_ON(expiration > 4))
> +             return 0;
> +
> +	/* Get the current counter value in microsecond.
> +	 */
> +	val = readl_relaxed(wdt->tmr->regs + TMRSR);
> +	timeleft = FIELD_GET(TMRSR_PCV, val);

So this value is in microseconds.

> +
> +	/*
> +	 * Calculate the time remaining by adding the time for the
> +	 * counter value to the time of the counter expirations that
> +	 * remain. Do the multiplication first on purpose just to keep
> +	 * the precision due to the integer division.
> +	 */
> +	timeleft += wdt->base.timeout * (4 - expiration) / 5;

However, wdt->base.timeout is in seconds. So I don't think we can simply 
add this. Don't we need to ...

timeleft += (wdt->base.timeout * USEC_PER_SEC * (4 - expiration)) / 5;

Given that this could be quite a big number, we probably want to make 
timeleft a 64-bit type too. So we may want to define a 'u64 
timeleft_usecs' and 'u32 timeleft_secs' that we return.

Jon

-- 
nvpublic