Skip to content

Commit 1d9c307

Browse files
pcercueiWim Van Sebroeck
authored andcommitted
watchdog: jz4740: Use WDT clock provided by TCU driver
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 <[email protected]> Tested-by: Mathieu Malaterre <[email protected]> Tested-by: Artur Rojek <[email protected]> Acked-by: Guenter Roeck <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Guenter Roeck <[email protected]> Signed-off-by: Wim Van Sebroeck <[email protected]>
1 parent 74f078c commit 1d9c307

File tree

2 files changed

+31
-45
lines changed

2 files changed

+31
-45
lines changed

drivers/watchdog/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1643,6 +1643,7 @@ config INDYDOG
16431643
config JZ4740_WDT
16441644
tristate "Ingenic jz4740 SoC hardware watchdog"
16451645
depends on MACH_JZ4740 || MACH_JZ4780
1646+
depends on COMMON_CLK
16461647
select WATCHDOG_CORE
16471648
help
16481649
Hardware driver for the built-in watchdog timer on Ingenic jz4740 SoCs.

drivers/watchdog/jz4740_wdt.c

Lines changed: 30 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,6 @@
1818
#include <linux/err.h>
1919
#include <linux/of.h>
2020

21-
#include <asm/mach-jz4740/timer.h>
22-
23-
#define JZ_WDT_CLOCK_PCLK 0x1
24-
#define JZ_WDT_CLOCK_RTC 0x2
25-
#define JZ_WDT_CLOCK_EXT 0x4
26-
27-
#define JZ_WDT_CLOCK_DIV_1 (0 << TCU_TCSR_PRESCALE_LSB)
28-
#define JZ_WDT_CLOCK_DIV_4 (1 << TCU_TCSR_PRESCALE_LSB)
29-
#define JZ_WDT_CLOCK_DIV_16 (2 << TCU_TCSR_PRESCALE_LSB)
30-
#define JZ_WDT_CLOCK_DIV_64 (3 << TCU_TCSR_PRESCALE_LSB)
31-
#define JZ_WDT_CLOCK_DIV_256 (4 << TCU_TCSR_PRESCALE_LSB)
32-
#define JZ_WDT_CLOCK_DIV_1024 (5 << TCU_TCSR_PRESCALE_LSB)
33-
3421
#define DEFAULT_HEARTBEAT 5
3522
#define MAX_HEARTBEAT 2048
3623

@@ -50,7 +37,8 @@ MODULE_PARM_DESC(heartbeat,
5037
struct jz4740_wdt_drvdata {
5138
struct watchdog_device wdt;
5239
void __iomem *base;
53-
struct clk *rtc_clk;
40+
struct clk *clk;
41+
unsigned long clk_rate;
5442
};
5543

5644
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,
6553
unsigned int new_timeout)
6654
{
6755
struct jz4740_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev);
68-
unsigned int rtc_clk_rate;
69-
unsigned int timeout_value;
70-
unsigned short clock_div = JZ_WDT_CLOCK_DIV_1;
56+
u16 timeout_value = (u16)(drvdata->clk_rate * new_timeout);
7157
u8 tcer;
7258

73-
rtc_clk_rate = clk_get_rate(drvdata->rtc_clk);
74-
75-
timeout_value = rtc_clk_rate * new_timeout;
76-
while (timeout_value > 0xffff) {
77-
if (clock_div == JZ_WDT_CLOCK_DIV_1024) {
78-
/* Requested timeout too high;
79-
* use highest possible value. */
80-
timeout_value = 0xffff;
81-
break;
82-
}
83-
timeout_value >>= 2;
84-
clock_div += (1 << TCU_TCSR_PRESCALE_LSB);
85-
}
86-
8759
tcer = readb(drvdata->base + TCU_REG_WDT_TCER);
8860
writeb(0x0, drvdata->base + TCU_REG_WDT_TCER);
89-
writew(clock_div, drvdata->base + TCU_REG_WDT_TCSR);
9061

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

9565
if (tcer & TCU_WDT_TCER_TCEN)
9666
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,
10272
static int jz4740_wdt_start(struct watchdog_device *wdt_dev)
10373
{
10474
struct jz4740_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev);
75+
int ret;
10576
u8 tcer;
10677

78+
ret = clk_prepare_enable(drvdata->clk);
79+
if (ret)
80+
return ret;
81+
10782
tcer = readb(drvdata->base + TCU_REG_WDT_TCER);
10883

109-
jz4740_timer_enable_watchdog();
11084
jz4740_wdt_set_timeout(wdt_dev, wdt_dev->timeout);
11185

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

12397
writeb(0x0, drvdata->base + TCU_REG_WDT_TCER);
124-
jz4740_timer_disable_watchdog();
98+
clk_disable_unprepare(drvdata->clk);
12599

126100
return 0;
127101
}
@@ -162,21 +136,38 @@ static int jz4740_wdt_probe(struct platform_device *pdev)
162136
struct device *dev = &pdev->dev;
163137
struct jz4740_wdt_drvdata *drvdata;
164138
struct watchdog_device *jz4740_wdt;
139+
long rate;
140+
int ret;
165141

166142
drvdata = devm_kzalloc(dev, sizeof(struct jz4740_wdt_drvdata),
167143
GFP_KERNEL);
168144
if (!drvdata)
169145
return -ENOMEM;
170146

171-
if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT)
172-
heartbeat = DEFAULT_HEARTBEAT;
147+
drvdata->clk = devm_clk_get(&pdev->dev, "wdt");
148+
if (IS_ERR(drvdata->clk)) {
149+
dev_err(&pdev->dev, "cannot find WDT clock\n");
150+
return PTR_ERR(drvdata->clk);
151+
}
152+
153+
/* Set smallest clock possible */
154+
rate = clk_round_rate(drvdata->clk, 1);
155+
if (rate < 0)
156+
return rate;
157+
158+
ret = clk_set_rate(drvdata->clk, rate);
159+
if (ret)
160+
return ret;
173161

162+
drvdata->clk_rate = rate;
174163
jz4740_wdt = &drvdata->wdt;
175164
jz4740_wdt->info = &jz4740_wdt_info;
176165
jz4740_wdt->ops = &jz4740_wdt_ops;
177-
jz4740_wdt->timeout = heartbeat;
178166
jz4740_wdt->min_timeout = 1;
179-
jz4740_wdt->max_timeout = MAX_HEARTBEAT;
167+
jz4740_wdt->max_timeout = 0xffff / rate;
168+
jz4740_wdt->timeout = clamp(heartbeat,
169+
jz4740_wdt->min_timeout,
170+
jz4740_wdt->max_timeout);
180171
jz4740_wdt->parent = dev;
181172
watchdog_set_nowayout(jz4740_wdt, nowayout);
182173
watchdog_set_drvdata(jz4740_wdt, drvdata);
@@ -185,12 +176,6 @@ static int jz4740_wdt_probe(struct platform_device *pdev)
185176
if (IS_ERR(drvdata->base))
186177
return PTR_ERR(drvdata->base);
187178

188-
drvdata->rtc_clk = devm_clk_get(dev, "rtc");
189-
if (IS_ERR(drvdata->rtc_clk)) {
190-
dev_err(dev, "cannot find RTC clock\n");
191-
return PTR_ERR(drvdata->rtc_clk);
192-
}
193-
194179
return devm_watchdog_register_device(dev, &drvdata->wdt);
195180
}
196181

0 commit comments

Comments
 (0)