Re: [PATCH v2 1/3] watchdog: jz4740: Use WDT clock provided by TCU driver

From: Guenter Roeck
Date: Sun Nov 17 2019 - 10:51:06 EST


On 11/16/19 9:50 AM, Paul Cercueil wrote:
Hi Guenter,

I noticed you already acked all the patches in the V1 but expected them to go through the MIPS tree; could you take them into your tree instead?

Hmm, I seem to recall there was a reason for it to go through the mips tree.
Maybe that reason no longer exists. I added the series to my watchdog-next
tree for Wim to pick up. We'll see what 0-day has to say.

Guenter

Cheers,
-Paul


Le mer., oct. 23, 2019 at 19:47, Paul Cercueil <paul@xxxxxxxxxxxxxxx> a Ãcrit :
Instead of requesting the "ext" clock and handling the watchdog clock
divider and gating in the watchdog driver, we now request and use the
"wdt" clock that is supplied by the ingenic-timer "TCU" driver.

The major benefit is that the watchdog's clock rate and parent can now
be specified from within devicetree, instead of hardcoded in the driver.

Also, this driver won't poke anymore into the TCU registers to
enable/disable the clock, as this is now handled by the TCU driver.

On the bad side, we break the ABI with devicetree - as we now request a
different clock. In this very specific case it is still okay, as every
Ingenic JZ47xx-based board out there compile the devicetree within the
kernel; so it's still time to push breaking changes, in order to get a
clean devicetree that won't break once it musn't.

Signed-off-by: Paul Cercueil <paul@xxxxxxxxxxxxxxx>
Tested-by: Mathieu Malaterre <malat@xxxxxxxxxx>
Tested-by: Artur Rojek <contact@xxxxxxxxxxxxxx>
Acked-by: Guenter Roeck <linux@xxxxxxxxxxxx>
---

Notes:
ÂÂÂ v2: Rebase on top of 5.4-rc4

Âdrivers/watchdog/KconfigÂÂÂÂÂ |Â 1 +
Âdrivers/watchdog/jz4740_wdt.c | 75 ++++++++++++++---------------------
Â2 files changed, 31 insertions(+), 45 deletions(-)

diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 58e7c100b6ad..6421187769cf 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -1642,6 +1642,7 @@ config INDYDOG
Âconfig JZ4740_WDT
ÂÂÂÂ tristate "Ingenic jz4740 SoC hardware watchdog"
ÂÂÂÂ depends on MACH_JZ4740 || MACH_JZ4780
+ÂÂÂ depends on COMMON_CLK
ÂÂÂÂ select WATCHDOG_CORE
ÂÂÂÂ help
ÂÂÂÂÂÂ Hardware driver for the built-in watchdog timer on Ingenic jz4740 SoCs.
diff --git a/drivers/watchdog/jz4740_wdt.c b/drivers/watchdog/jz4740_wdt.c
index c6052ae54f32..72920f09f4a7 100644
--- a/drivers/watchdog/jz4740_wdt.c
+++ b/drivers/watchdog/jz4740_wdt.c
@@ -18,19 +18,6 @@
Â#include <linux/err.h>
Â#include <linux/of.h>

-#include <asm/mach-jz4740/timer.h>
-
-#define JZ_WDT_CLOCK_PCLK 0x1
-#define JZ_WDT_CLOCK_RTCÂ 0x2
-#define JZ_WDT_CLOCK_EXTÂ 0x4
-
-#define JZ_WDT_CLOCK_DIV_1ÂÂÂ (0 << TCU_TCSR_PRESCALE_LSB)
-#define JZ_WDT_CLOCK_DIV_4ÂÂÂ (1 << TCU_TCSR_PRESCALE_LSB)
-#define JZ_WDT_CLOCK_DIV_16ÂÂ (2 << TCU_TCSR_PRESCALE_LSB)
-#define JZ_WDT_CLOCK_DIV_64ÂÂ (3 << TCU_TCSR_PRESCALE_LSB)
-#define JZ_WDT_CLOCK_DIV_256Â (4 << TCU_TCSR_PRESCALE_LSB)
-#define JZ_WDT_CLOCK_DIV_1024 (5 << TCU_TCSR_PRESCALE_LSB)
-
Â#define DEFAULT_HEARTBEAT 5
Â#define MAX_HEARTBEATÂÂÂÂ 2048

@@ -50,7 +37,8 @@ MODULE_PARM_DESC(heartbeat,
Âstruct jz4740_wdt_drvdata {
ÂÂÂÂ struct watchdog_device wdt;
ÂÂÂÂ void __iomem *base;
-ÂÂÂ struct clk *rtc_clk;
+ÂÂÂ struct clk *clk;
+ÂÂÂ unsigned long clk_rate;
Â};

Âstatic int jz4740_wdt_ping(struct watchdog_device *wdt_dev)
@@ -65,32 +53,14 @@ static int jz4740_wdt_set_timeout(struct watchdog_device *wdt_dev,
ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ unsigned int new_timeout)
Â{
ÂÂÂÂ struct jz4740_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev);
-ÂÂÂ unsigned int rtc_clk_rate;
-ÂÂÂ unsigned int timeout_value;
-ÂÂÂ unsigned short clock_div = JZ_WDT_CLOCK_DIV_1;
+ÂÂÂ u16 timeout_value = (u16)(drvdata->clk_rate * new_timeout);
ÂÂÂÂ u8 tcer;

-ÂÂÂ rtc_clk_rate = clk_get_rate(drvdata->rtc_clk);
-
-ÂÂÂ timeout_value = rtc_clk_rate * new_timeout;
-ÂÂÂ while (timeout_value > 0xffff) {
-ÂÂÂÂÂÂÂ if (clock_div == JZ_WDT_CLOCK_DIV_1024) {
-ÂÂÂÂÂÂÂÂÂÂÂ /* Requested timeout too high;
-ÂÂÂÂÂÂÂÂÂÂÂ * use highest possible value. */
-ÂÂÂÂÂÂÂÂÂÂÂ timeout_value = 0xffff;
-ÂÂÂÂÂÂÂÂÂÂÂ break;
-ÂÂÂÂÂÂÂ }
-ÂÂÂÂÂÂÂ timeout_value >>= 2;
-ÂÂÂÂÂÂÂ clock_div += (1 << TCU_TCSR_PRESCALE_LSB);
-ÂÂÂ }
-
ÂÂÂÂ tcer = readb(drvdata->base + TCU_REG_WDT_TCER);
ÂÂÂÂ writeb(0x0, drvdata->base + TCU_REG_WDT_TCER);
-ÂÂÂ writew(clock_div, drvdata->base + TCU_REG_WDT_TCSR);

ÂÂÂÂ writew((u16)timeout_value, drvdata->base + TCU_REG_WDT_TDR);
ÂÂÂÂ writew(0x0, drvdata->base + TCU_REG_WDT_TCNT);
-ÂÂÂ writew(clock_div | JZ_WDT_CLOCK_RTC, drvdata->base + TCU_REG_WDT_TCSR);

ÂÂÂÂ if (tcer & TCU_WDT_TCER_TCEN)
ÂÂÂÂÂÂÂÂ writeb(TCU_WDT_TCER_TCEN, drvdata->base + TCU_REG_WDT_TCER);
@@ -102,11 +72,15 @@ static int jz4740_wdt_set_timeout(struct watchdog_device *wdt_dev,
Âstatic int jz4740_wdt_start(struct watchdog_device *wdt_dev)
Â{
ÂÂÂÂ struct jz4740_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev);
+ÂÂÂ int ret;
ÂÂÂÂ u8 tcer;

+ÂÂÂ ret = clk_prepare_enable(drvdata->clk);
+ÂÂÂ if (ret)
+ÂÂÂÂÂÂÂ return ret;
+
ÂÂÂÂ tcer = readb(drvdata->base + TCU_REG_WDT_TCER);

-ÂÂÂ jz4740_timer_enable_watchdog();
ÂÂÂÂ jz4740_wdt_set_timeout(wdt_dev, wdt_dev->timeout);

ÂÂÂÂ /* Start watchdog if it wasn't started already */
@@ -121,7 +95,7 @@ static int jz4740_wdt_stop(struct watchdog_device *wdt_dev)
ÂÂÂÂ struct jz4740_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev);

ÂÂÂÂ writeb(0x0, drvdata->base + TCU_REG_WDT_TCER);
-ÂÂÂ jz4740_timer_disable_watchdog();
+ÂÂÂ clk_disable_unprepare(drvdata->clk);

ÂÂÂÂ return 0;
Â}
@@ -162,21 +136,38 @@ static int jz4740_wdt_probe(struct platform_device *pdev)
ÂÂÂÂ struct device *dev = &pdev->dev;
ÂÂÂÂ struct jz4740_wdt_drvdata *drvdata;
ÂÂÂÂ struct watchdog_device *jz4740_wdt;
+ÂÂÂ long rate;
+ÂÂÂ int ret;

ÂÂÂÂ drvdata = devm_kzalloc(dev, sizeof(struct jz4740_wdt_drvdata),
ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ GFP_KERNEL);
ÂÂÂÂ if (!drvdata)
ÂÂÂÂÂÂÂÂ return -ENOMEM;

-ÂÂÂ if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT)
-ÂÂÂÂÂÂÂ heartbeat = DEFAULT_HEARTBEAT;
+ÂÂÂ drvdata->clk = devm_clk_get(&pdev->dev, "wdt");
+ÂÂÂ if (IS_ERR(drvdata->clk)) {
+ÂÂÂÂÂÂÂ dev_err(&pdev->dev, "cannot find WDT clock\n");
+ÂÂÂÂÂÂÂ return PTR_ERR(drvdata->clk);
+ÂÂÂ }
+
+ÂÂÂ /* Set smallest clock possible */
+ÂÂÂ rate = clk_round_rate(drvdata->clk, 1);
+ÂÂÂ if (rate < 0)
+ÂÂÂÂÂÂÂ return rate;
+
+ÂÂÂ ret = clk_set_rate(drvdata->clk, rate);
+ÂÂÂ if (ret)
+ÂÂÂÂÂÂÂ return ret;

+ÂÂÂ drvdata->clk_rate = rate;
ÂÂÂÂ jz4740_wdt = &drvdata->wdt;
ÂÂÂÂ jz4740_wdt->info = &jz4740_wdt_info;
ÂÂÂÂ jz4740_wdt->ops = &jz4740_wdt_ops;
-ÂÂÂ jz4740_wdt->timeout = heartbeat;
ÂÂÂÂ jz4740_wdt->min_timeout = 1;
-ÂÂÂ jz4740_wdt->max_timeout = MAX_HEARTBEAT;
+ÂÂÂ jz4740_wdt->max_timeout = 0xffff / rate;
+ÂÂÂ jz4740_wdt->timeout = clamp(heartbeat,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ jz4740_wdt->min_timeout,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ jz4740_wdt->max_timeout);
ÂÂÂÂ jz4740_wdt->parent = dev;
ÂÂÂÂ watchdog_set_nowayout(jz4740_wdt, nowayout);
ÂÂÂÂ watchdog_set_drvdata(jz4740_wdt, drvdata);
@@ -185,12 +176,6 @@ static int jz4740_wdt_probe(struct platform_device *pdev)
ÂÂÂÂ if (IS_ERR(drvdata->base))
ÂÂÂÂÂÂÂÂ return PTR_ERR(drvdata->base);

-ÂÂÂ drvdata->rtc_clk = devm_clk_get(dev, "rtc");
-ÂÂÂ if (IS_ERR(drvdata->rtc_clk)) {
-ÂÂÂÂÂÂÂ dev_err(dev, "cannot find RTC clock\n");
-ÂÂÂÂÂÂÂ return PTR_ERR(drvdata->rtc_clk);
-ÂÂÂ }
-
ÂÂÂÂ return devm_watchdog_register_device(dev, &drvdata->wdt);
Â}

--
2.23.0