Re: [PATCH v4 2/2] watchdog: Add Watchdog Timer driver for RZ/G2L

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 11/30/21 9:48 AM, Biju Das wrote:
Hi Guenter,

Thanks for the feedback.

Subject: Re: [PATCH v4 2/2] watchdog: Add Watchdog Timer driver for RZ/G2L

On 11/30/21 3:43 AM, Biju Das wrote:
Add Watchdog Timer driver for RZ/G2L SoC.

WDT IP block supports normal watchdog timer function and reset request
function due to CPU parity error.

This driver currently supports normal watchdog timer function and
later will add support for reset request function due to CPU parity
error.

Signed-off-by: Biju Das <biju.das.jz@xxxxxxxxxxxxxx>
Reviewed-by: Guenter Roeck <linux@xxxxxxxxxxxx>
---
V3->V4:
   * Fixed the build issue reported by kernel test robot by Replacing the
     macro WDT_CYCLE_MSEC with div64_ul for 64-bit division to fix 32-bit
     kernels.
V2->V3:
   * Added Rb tag from Guenter Roeck
V1->V2:
   * started using clk_get/put instead of devm_clk_get/put
   * Moved devm_add_action_or_reset after set_drvdata() and
   * removed redundant action on devm_add_action_or_reset() failure.
RFC->V1
   * Removed pclk_rate from priv.
   * rzg2l_wdt_write() returns void and Removed tiemout related to
register update
   * rzg2l_wdt_init_timeout() returns void and removed delays.
   * removed set_bit(WDOG_HW_RUNNING,..) as we can stop watchdog
   * renamed reset_assert_clock_disable->reset_assert_pm_disable_put
   * started using devm_reset_control_get_exclusive()
   * removed platform_set_drvdata(pdev, priv) as there is no user
   * removed watchdog_set_restart_priority(&priv->wdev, 0) as 0 is the
default.
   * removed remove callback as it is empty.
---
   drivers/watchdog/Kconfig     |   8 ++
   drivers/watchdog/Makefile    |   1 +
   drivers/watchdog/rzg2l_wdt.c | 260 +++++++++++++++++++++++++++++++++++
   3 files changed, 269 insertions(+)
   create mode 100644 drivers/watchdog/rzg2l_wdt.c

diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index
9d222ba17ec6..4760ee981263 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -881,6 +881,14 @@ config RENESAS_RZAWDT
   	  This driver adds watchdog support for the integrated watchdogs in
the
   	  Renesas RZ/A SoCs. These watchdogs can be used to reset a system.

+config RENESAS_RZG2LWDT
+	tristate "Renesas RZ/G2L WDT Watchdog"
+	depends on ARCH_RENESAS || COMPILE_TEST
+	select WATCHDOG_CORE
+	help
+	  This driver adds watchdog support for the integrated watchdogs in
the
+	  Renesas RZ/G2L SoCs. These watchdogs can be used to reset a
system.
+
   config ASPEED_WATCHDOG
   	tristate "Aspeed BMC watchdog support"
   	depends on ARCH_ASPEED || COMPILE_TEST diff --git
a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index
2ee97064145b..9a3dc0bd271b 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -84,6 +84,7 @@ obj-$(CONFIG_LPC18XX_WATCHDOG) += lpc18xx_wdt.o
   obj-$(CONFIG_BCM7038_WDT) += bcm7038_wdt.o
   obj-$(CONFIG_RENESAS_WDT) += renesas_wdt.o
   obj-$(CONFIG_RENESAS_RZAWDT) += rza_wdt.o
+obj-$(CONFIG_RENESAS_RZG2LWDT) += rzg2l_wdt.o
   obj-$(CONFIG_ASPEED_WATCHDOG) += aspeed_wdt.o
   obj-$(CONFIG_STM32_WATCHDOG) += stm32_iwdg.o
   obj-$(CONFIG_UNIPHIER_WATCHDOG) += uniphier_wdt.o diff --git
a/drivers/watchdog/rzg2l_wdt.c b/drivers/watchdog/rzg2l_wdt.c new file
mode 100644 index 000000000000..69530b92fff9
--- /dev/null
+++ b/drivers/watchdog/rzg2l_wdt.c
@@ -0,0 +1,260 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Renesas RZ/G2L WDT Watchdog Driver
+ *
+ * Copyright (C) 2021 Renesas Electronics Corporation  */ #include
+<linux/bitops.h> #include <linux/clk.h> #include <linux/delay.h>
+#include <linux/io.h> #include <linux/kernel.h> #include
+<linux/module.h> #include <linux/of.h> #include
+<linux/platform_device.h> #include <linux/pm_runtime.h> #include
+<linux/reset.h> #include <linux/watchdog.h>
+
+#define WDTCNT		0x00
+#define WDTSET		0x04
+#define WDTTIM		0x08
+#define WDTINT		0x0C
+#define WDTCNT_WDTEN	BIT(0)
+#define WDTINT_INTDISP	BIT(0)
+
+#define WDT_DEFAULT_TIMEOUT		60U
+
+/* Setting period time register only 12 bit set in WDTSET[31:20] */
+#define WDTSET_COUNTER_MASK		(0xFFF00000)
+#define WDTSET_COUNTER_VAL(f)		((f) << 20)
+
+#define F2CYCLE_NSEC(f)			(1000000000 / (f))
+
+static bool nowayout = WATCHDOG_NOWAYOUT; module_param(nowayout,
+bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once
+started (default="
+				__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+struct rzg2l_wdt_priv {
+	void __iomem *base;
+	struct watchdog_device wdev;
+	struct reset_control *rstc;
+	unsigned long osc_clk_rate;
+	unsigned long delay;
+};
+
+static void rzg2l_wdt_wait_delay(struct rzg2l_wdt_priv *priv) {
+	/* delay timer when change the setting register */
+	ndelay(priv->delay);
+}
+
+static u32 rzg2l_wdt_get_cycle_msec(unsigned long cycle, u32 wdttime)
+{
+	u64 timer_cycle_ms = 1024 * 1024 * 1000000ULL * (wdttime + 1);
+
+	return div64_ul(timer_cycle_ms, cycle); }

A description might be warranted here. The return value appears to be a
timeout in seconds, based on
	priv->wdev.max_timeout = rzg2l_wdt_get_cycle_msec(priv-
osc_clk_rate, 0xfff); but that is not what the function name suggests.

Also, the maximum timeouts seem to be excessive. Feeding the above
function into test code, I get


As per HW manual,
24MHz is our SoC input clock,

Equation is 1024 *1024 * (t +1)  /(24 * 100000) seconds.

Min value of 0 corresponds to .4369sec, if you convert it into microseconds it becomes 0.43690x1000000 = 436906 microseconds

Max value of 0xfff corresponds to 1789956.97 msec, if you convert it into microseconds it becomes 1789956970 = 0x6AB0936A , I agree it is a big value, but it is with in 32bit limits.


max_timeout is in seconds, not microseconds, and the function has _msec
in its name which doesn't reflect anything micro.

clk: 10000000 max timeout: 429496729
clk: 20000000 max timeout: 214748364
clk: 40000000 max timeout: 107374182
clk: 80000000 max timeout: 53687091
clk: 160000000 max timeout: 26843545
clk: 320000000 max timeout: 13421772
clk: 640000000 max timeout: 6710886
clk: 1280000000 max timeout: 3355443

That really doesn't look correct. Even in milli-seconds, a maximum timeout
of 429496729 ms or 429496.729 seconds at 10 MHz clock rate seems high.

+
+static void rzg2l_wdt_write(struct rzg2l_wdt_priv *priv, u32 val,
+unsigned int reg) {
+	if (reg == WDTSET)
+		val &= WDTSET_COUNTER_MASK;
+
+	writel_relaxed(val, priv->base + reg);
+	/* Registers other than the WDTINT is always synchronized with
WDT_CLK */
+	if (reg != WDTINT)
+		rzg2l_wdt_wait_delay(priv);
+}
+
+static void rzg2l_wdt_init_timeout(struct watchdog_device *wdev) {
+	struct rzg2l_wdt_priv *priv = watchdog_get_drvdata(wdev);
+	u32 time_out;
+
+	/* Clear Lapsed Time Register and clear Interrupt */
+	rzg2l_wdt_write(priv, WDTINT_INTDISP, WDTINT);
+	/* 2 consecutive overflow cycle needed to trigger reset */
+	time_out = (wdev->timeout / 2 * 1000000) /
+rzg2l_wdt_get_cycle_msec(priv->osc_clk_rate, 0);

This code effectively reduces timer granularity to 2 seconds. Is that on
purpose ?

Yes, it needs 2 consecutive overflow cycle for triggering watchdog reset

As per the above calculation,

60 secs Default timeout, the counter value = 30000000 microsec/436906 microsec = 686

And it triggers watchdog around 60 sec with the command
cat /dev/watchdog  & for i in {1..60}; do sleep 1; echo $i; devmem2 0x12800808; done

That explains the factor itself, which I did not question. I questioned
the granularity, ie why the timeout was, in practice, set to 0, 2, 4,
... seconds instead of 1, 2, 3, 4 ...

Why not something like
	time_out = (wdev->timeout * (1000000 / 2)) /
rzg2l_wdt_get_cycle_msec(priv->osc_clk_rate, 0); instead ?


Ok.


Also, feeding the maximum timeout as calculated by
rzg2l_wdt_get_cycle_msec(priv->osc_clk_rate, 0xfff) into this expression
yields really large numbers. Making things worse, those long timeouts
cause
	wdev->timeout / 2 * 1000000
to overflow easily. This calculation alone suggests that the maximum
timeout value can not be larger than ~8589 to avoid that overflow.

More test code gives me:

clk: 10000000 max timeout: 429496729
    timeout: 1s reg: 0x0
    timeout: 2s reg: 0x9
    timeout: 859s reg: 0xffb
      Overflow: t=214748364 treg: 0x3d0916df
    timeout: 214748364s reg: 0xffffffff
      Overflow: t=429496729 treg: 0x7a122dbf
    timeout: 429496729s reg: 0xffffffff

clk: 320000000 max timeout: 13421772
    timeout: 1s reg: 0x0
    timeout: 2s reg: 0x131
    timeout: 27s reg: 0xf80
      Overflow: t=6710886 treg: 0x3d0cd090
    timeout: 6710886s reg: 0xffffffff
      Overflow: t=13421772 treg: 0x7a19a120
    timeout: 13421772s reg: 0xffffffff

and similar for other clock rates. This shows both the impact of the
artificial 2s granularity and the value overflows.

Something in the calculation of max_timeout or in the calculation of the
register value or both is wrong. Whatever it is, it needs to get fixed.

I believe microsecond calculation leads to the confusion.


Again,
	priv->wdev.max_timeout = rzg2l_wdt_get_cycle_msec(priv-osc_clk_rate, 0xfff);

and max_timeout is in seconds, not milliseconds or microseconds.
Maybe it needs to be something like

	priv->wdev.max_timeout = rzg2l_wdt_get_cycle_msec(priv-osc_clk_rate, 0xfff) /
		(1000000 / 2);

instead (that would limit the range of the register value to 0..0xfff), but
even then the function name (rzg2l_wdt_get_cycle_msec) doesn't match
what it returns. Maybe it should be named rzg2l_wdt_get_cycle_usec.
It might also make sense to use USEC_PER_SEC instead of 1000000 to
clarify the context.

Guenter



[Index of Archives]     [Linux Samsung SOC]     [Linux Wireless]     [Linux Kernel]     [ATH6KL]     [Linux Bluetooth]     [Linux Netdev]     [Kernel Newbies]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Samba]     [Device Mapper]

  Powered by Linux