1 // SPDX-License-Identifier: GPL-2.0
3 * Renesas RZ/V2H(P) WDT Watchdog Driver
5 * Copyright (C) 2024 Renesas Electronics Corporation.
8 #include <linux/delay.h>
10 #include <linux/kernel.h>
11 #include <linux/module.h>
13 #include <linux/platform_device.h>
14 #include <linux/pm_runtime.h>
15 #include <linux/reset.h>
16 #include <linux/units.h>
17 #include <linux/watchdog.h>
19 #define WDTRR 0x00 /* WDT Refresh Register RW, 8 */
20 #define WDTCR 0x02 /* WDT Control Register RW, 16 */
21 #define WDTSR 0x04 /* WDT Status Register RW, 16 */
22 #define WDTRCR 0x06 /* WDT Reset Control Register RW, 8 */
24 #define WDTCR_TOPS_1024 0x00
25 #define WDTCR_TOPS_16384 0x03
27 #define WDTCR_CKS_CLK_1 0x00
28 #define WDTCR_CKS_CLK_256 0x50
30 #define WDTCR_RPES_0 0x300
31 #define WDTCR_RPES_75 0x000
33 #define WDTCR_RPSS_25 0x00
34 #define WDTCR_RPSS_100 0x3000
36 #define WDTRCR_RSTIRQS BIT(7)
38 #define MAX_TIMEOUT_CYCLES 16384
39 #define CLOCK_DIV_BY_256 256
41 #define WDT_DEFAULT_TIMEOUT 60U
43 static bool nowayout
= WATCHDOG_NOWAYOUT
;
44 module_param(nowayout
, bool, 0);
45 MODULE_PARM_DESC(nowayout
, "Watchdog cannot be stopped once started (default="
46 __MODULE_STRING(WATCHDOG_NOWAYOUT
) ")");
48 struct rzv2h_wdt_priv
{
52 struct reset_control
*rstc
;
53 struct watchdog_device wdev
;
56 static int rzv2h_wdt_ping(struct watchdog_device
*wdev
)
58 struct rzv2h_wdt_priv
*priv
= watchdog_get_drvdata(wdev
);
61 * The down-counter is refreshed and starts counting operation on
62 * a write of the values 00h and FFh to the WDTRR register.
64 writeb(0x0, priv
->base
+ WDTRR
);
65 writeb(0xFF, priv
->base
+ WDTRR
);
70 static void rzv2h_wdt_setup(struct watchdog_device
*wdev
, u16 wdtcr
)
72 struct rzv2h_wdt_priv
*priv
= watchdog_get_drvdata(wdev
);
74 /* Configure the timeout, clock division ratio, and window start and end positions. */
75 writew(wdtcr
, priv
->base
+ WDTCR
);
77 /* Enable interrupt output to the ICU. */
78 writeb(0, priv
->base
+ WDTRCR
);
80 /* Clear underflow flag and refresh error flag. */
81 writew(0, priv
->base
+ WDTSR
);
84 static int rzv2h_wdt_start(struct watchdog_device
*wdev
)
86 struct rzv2h_wdt_priv
*priv
= watchdog_get_drvdata(wdev
);
89 ret
= pm_runtime_resume_and_get(wdev
->parent
);
93 ret
= reset_control_deassert(priv
->rstc
);
95 pm_runtime_put(wdev
->parent
);
99 /* delay to handle clock halt after de-assert operation */
104 * - CKS[7:4] - Clock Division Ratio Select - 0101b: oscclk/256
105 * - RPSS[13:12] - Window Start Position Select - 11b: 100%
106 * - RPES[9:8] - Window End Position Select - 11b: 0%
107 * - TOPS[1:0] - Timeout Period Select - 11b: 16384 cycles (3FFFh)
109 rzv2h_wdt_setup(wdev
, WDTCR_CKS_CLK_256
| WDTCR_RPSS_100
|
110 WDTCR_RPES_0
| WDTCR_TOPS_16384
);
113 * Down counting starts after writing the sequence 00h -> FFh to the
114 * WDTRR register. Hence, call the ping operation after loading the counter.
116 rzv2h_wdt_ping(wdev
);
121 static int rzv2h_wdt_stop(struct watchdog_device
*wdev
)
123 struct rzv2h_wdt_priv
*priv
= watchdog_get_drvdata(wdev
);
126 ret
= reset_control_assert(priv
->rstc
);
130 ret
= pm_runtime_put(wdev
->parent
);
137 static const struct watchdog_info rzv2h_wdt_ident
= {
138 .options
= WDIOF_MAGICCLOSE
| WDIOF_KEEPALIVEPING
| WDIOF_SETTIMEOUT
,
139 .identity
= "Renesas RZ/V2H WDT Watchdog",
142 static int rzv2h_wdt_restart(struct watchdog_device
*wdev
,
143 unsigned long action
, void *data
)
145 struct rzv2h_wdt_priv
*priv
= watchdog_get_drvdata(wdev
);
148 if (!watchdog_active(wdev
)) {
149 ret
= clk_enable(priv
->pclk
);
153 ret
= clk_enable(priv
->oscclk
);
155 clk_disable(priv
->pclk
);
159 ret
= reset_control_deassert(priv
->rstc
);
161 clk_disable(priv
->oscclk
);
162 clk_disable(priv
->pclk
);
167 * Writing to the WDT Control Register (WDTCR) or WDT Reset
168 * Control Register (WDTRCR) is possible once between the
169 * release from the reset state and the first refresh operation.
170 * Therefore, issue a reset if the watchdog is active.
172 ret
= reset_control_reset(priv
->rstc
);
177 /* delay to handle clock halt after de-assert operation */
182 * - CKS[7:4] - Clock Division Ratio Select - 0000b: oscclk/1
183 * - RPSS[13:12] - Window Start Position Select - 00b: 25%
184 * - RPES[9:8] - Window End Position Select - 00b: 75%
185 * - TOPS[1:0] - Timeout Period Select - 00b: 1024 cycles (03FFh)
187 rzv2h_wdt_setup(wdev
, WDTCR_CKS_CLK_1
| WDTCR_RPSS_25
|
188 WDTCR_RPES_75
| WDTCR_TOPS_1024
);
190 rzv2h_wdt_ping(wdev
);
192 /* wait for underflow to trigger... */
198 static const struct watchdog_ops rzv2h_wdt_ops
= {
199 .owner
= THIS_MODULE
,
200 .start
= rzv2h_wdt_start
,
201 .stop
= rzv2h_wdt_stop
,
202 .ping
= rzv2h_wdt_ping
,
203 .restart
= rzv2h_wdt_restart
,
206 static int rzv2h_wdt_probe(struct platform_device
*pdev
)
208 struct device
*dev
= &pdev
->dev
;
209 struct rzv2h_wdt_priv
*priv
;
212 priv
= devm_kzalloc(dev
, sizeof(*priv
), GFP_KERNEL
);
216 priv
->base
= devm_platform_ioremap_resource(pdev
, 0);
217 if (IS_ERR(priv
->base
))
218 return PTR_ERR(priv
->base
);
220 priv
->pclk
= devm_clk_get_prepared(&pdev
->dev
, "pclk");
221 if (IS_ERR(priv
->pclk
))
222 return dev_err_probe(&pdev
->dev
, PTR_ERR(priv
->pclk
), "no pclk");
224 priv
->oscclk
= devm_clk_get_prepared(&pdev
->dev
, "oscclk");
225 if (IS_ERR(priv
->oscclk
))
226 return dev_err_probe(&pdev
->dev
, PTR_ERR(priv
->oscclk
), "no oscclk");
228 priv
->rstc
= devm_reset_control_get_exclusive(&pdev
->dev
, NULL
);
229 if (IS_ERR(priv
->rstc
))
230 return dev_err_probe(&pdev
->dev
, PTR_ERR(priv
->rstc
),
231 "failed to get cpg reset");
233 priv
->wdev
.max_hw_heartbeat_ms
= (MILLI
* MAX_TIMEOUT_CYCLES
* CLOCK_DIV_BY_256
) /
234 clk_get_rate(priv
->oscclk
);
235 dev_dbg(dev
, "max hw timeout of %dms\n", priv
->wdev
.max_hw_heartbeat_ms
);
237 ret
= devm_pm_runtime_enable(&pdev
->dev
);
241 priv
->wdev
.min_timeout
= 1;
242 priv
->wdev
.timeout
= WDT_DEFAULT_TIMEOUT
;
243 priv
->wdev
.info
= &rzv2h_wdt_ident
;
244 priv
->wdev
.ops
= &rzv2h_wdt_ops
;
245 priv
->wdev
.parent
= dev
;
246 watchdog_set_drvdata(&priv
->wdev
, priv
);
247 watchdog_set_nowayout(&priv
->wdev
, nowayout
);
248 watchdog_stop_on_unregister(&priv
->wdev
);
250 ret
= watchdog_init_timeout(&priv
->wdev
, 0, dev
);
252 dev_warn(dev
, "Specified timeout invalid, using default");
254 return devm_watchdog_register_device(&pdev
->dev
, &priv
->wdev
);
257 static const struct of_device_id rzv2h_wdt_ids
[] = {
258 { .compatible
= "renesas,r9a09g057-wdt", },
261 MODULE_DEVICE_TABLE(of
, rzv2h_wdt_ids
);
263 static struct platform_driver rzv2h_wdt_driver
= {
266 .of_match_table
= rzv2h_wdt_ids
,
268 .probe
= rzv2h_wdt_probe
,
270 module_platform_driver(rzv2h_wdt_driver
);
271 MODULE_AUTHOR("Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>");
272 MODULE_DESCRIPTION("Renesas RZ/V2H(P) WDT Watchdog Driver");
273 MODULE_LICENSE("GPL");