1 // SPDX-License-Identifier: GPL-2.0
3 * Starfive Watchdog driver
5 * Copyright (C) 2022 StarFive Technology Co., Ltd.
9 #include <linux/iopoll.h>
10 #include <linux/module.h>
12 #include <linux/platform_device.h>
13 #include <linux/pm_runtime.h>
14 #include <linux/reset.h>
15 #include <linux/watchdog.h>
17 /* JH7100 Watchdog register define */
18 #define STARFIVE_WDT_JH7100_INTSTAUS 0x000
19 #define STARFIVE_WDT_JH7100_CONTROL 0x104
20 #define STARFIVE_WDT_JH7100_LOAD 0x108
21 #define STARFIVE_WDT_JH7100_EN 0x110
22 #define STARFIVE_WDT_JH7100_RELOAD 0x114 /* Write 0 or 1 to reload preset value */
23 #define STARFIVE_WDT_JH7100_VALUE 0x118
24 #define STARFIVE_WDT_JH7100_INTCLR 0x120 /*
25 * [0]: Write 1 to clear interrupt
26 * [1]: 1 mean clearing and 0 mean complete
29 #define STARFIVE_WDT_JH7100_LOCK 0x13c /* write 0x378f0765 to unlock */
31 /* JH7110 Watchdog register define */
32 #define STARFIVE_WDT_JH7110_LOAD 0x000
33 #define STARFIVE_WDT_JH7110_VALUE 0x004
34 #define STARFIVE_WDT_JH7110_CONTROL 0x008 /*
36 * [1]: interrupt enable && watchdog enable
39 #define STARFIVE_WDT_JH7110_INTCLR 0x00c /* clear intterupt and reload the counter */
40 #define STARFIVE_WDT_JH7110_IMS 0x014
41 #define STARFIVE_WDT_JH7110_LOCK 0xc00 /* write 0x1ACCE551 to unlock */
44 #define STARFIVE_WDT_ENABLE 0x1
45 #define STARFIVE_WDT_EN_SHIFT 0
46 #define STARFIVE_WDT_RESET_EN 0x1
47 #define STARFIVE_WDT_JH7100_RST_EN_SHIFT 0
48 #define STARFIVE_WDT_JH7110_RST_EN_SHIFT 1
51 #define STARFIVE_WDT_JH7100_UNLOCK_KEY 0x378f0765
52 #define STARFIVE_WDT_JH7110_UNLOCK_KEY 0x1acce551
55 #define STARFIVE_WDT_INTCLR 0x1
56 #define STARFIVE_WDT_JH7100_INTCLR_AVA_SHIFT 1 /* Watchdog can clear interrupt when 0 */
58 #define STARFIVE_WDT_MAXCNT 0xffffffff
59 #define STARFIVE_WDT_DEFAULT_TIME (15)
60 #define STARFIVE_WDT_DELAY_US 0
61 #define STARFIVE_WDT_TIMEOUT_US 10000
63 /* module parameter */
64 #define STARFIVE_WDT_EARLY_ENA 0
66 static bool nowayout
= WATCHDOG_NOWAYOUT
;
68 static bool early_enable
= STARFIVE_WDT_EARLY_ENA
;
70 module_param(heartbeat
, int, 0);
71 module_param(early_enable
, bool, 0);
72 module_param(nowayout
, bool, 0);
74 MODULE_PARM_DESC(heartbeat
, "Watchdog heartbeat in seconds. (default="
75 __MODULE_STRING(STARFIVE_WDT_DEFAULT_TIME
) ")");
76 MODULE_PARM_DESC(early_enable
,
77 "Watchdog is started at boot time if set to 1, default="
78 __MODULE_STRING(STARFIVE_WDT_EARLY_ENA
));
79 MODULE_PARM_DESC(nowayout
, "Watchdog cannot be stopped once started (default="
80 __MODULE_STRING(WATCHDOG_NOWAYOUT
) ")");
82 struct starfive_wdt_variant
{
83 unsigned int control
; /* Watchdog Control Resgister for reset enable */
84 unsigned int load
; /* Watchdog Load register */
85 unsigned int reload
; /* Watchdog Reload Control register */
86 unsigned int enable
; /* Watchdog Enable Register */
87 unsigned int value
; /* Watchdog Counter Value Register */
88 unsigned int int_clr
; /* Watchdog Interrupt Clear Register */
89 unsigned int unlock
; /* Watchdog Lock Register */
90 unsigned int int_status
; /* Watchdog Interrupt Status Register */
95 bool intclr_check
; /* whether need to check it before clearing interrupt */
96 char intclr_ava_shift
;
97 bool double_timeout
; /* The watchdog need twice timeout to reboot */
100 struct starfive_wdt
{
101 struct watchdog_device wdd
;
102 spinlock_t lock
; /* spinlock for register handling */
104 struct clk
*core_clk
;
106 const struct starfive_wdt_variant
*variant
;
108 u32 count
; /* count of timeout */
109 u32 reload
; /* restore the count */
112 /* Register layout and configuration for the JH7100 */
113 static const struct starfive_wdt_variant starfive_wdt_jh7100_variant
= {
114 .control
= STARFIVE_WDT_JH7100_CONTROL
,
115 .load
= STARFIVE_WDT_JH7100_LOAD
,
116 .reload
= STARFIVE_WDT_JH7100_RELOAD
,
117 .enable
= STARFIVE_WDT_JH7100_EN
,
118 .value
= STARFIVE_WDT_JH7100_VALUE
,
119 .int_clr
= STARFIVE_WDT_JH7100_INTCLR
,
120 .unlock
= STARFIVE_WDT_JH7100_LOCK
,
121 .unlock_key
= STARFIVE_WDT_JH7100_UNLOCK_KEY
,
122 .int_status
= STARFIVE_WDT_JH7100_INTSTAUS
,
123 .enrst_shift
= STARFIVE_WDT_JH7100_RST_EN_SHIFT
,
124 .en_shift
= STARFIVE_WDT_EN_SHIFT
,
125 .intclr_check
= true,
126 .intclr_ava_shift
= STARFIVE_WDT_JH7100_INTCLR_AVA_SHIFT
,
127 .double_timeout
= false,
130 /* Register layout and configuration for the JH7110 */
131 static const struct starfive_wdt_variant starfive_wdt_jh7110_variant
= {
132 .control
= STARFIVE_WDT_JH7110_CONTROL
,
133 .load
= STARFIVE_WDT_JH7110_LOAD
,
134 .enable
= STARFIVE_WDT_JH7110_CONTROL
,
135 .value
= STARFIVE_WDT_JH7110_VALUE
,
136 .int_clr
= STARFIVE_WDT_JH7110_INTCLR
,
137 .unlock
= STARFIVE_WDT_JH7110_LOCK
,
138 .unlock_key
= STARFIVE_WDT_JH7110_UNLOCK_KEY
,
139 .int_status
= STARFIVE_WDT_JH7110_IMS
,
140 .enrst_shift
= STARFIVE_WDT_JH7110_RST_EN_SHIFT
,
141 .en_shift
= STARFIVE_WDT_EN_SHIFT
,
142 .intclr_check
= false,
143 .double_timeout
= true,
146 static int starfive_wdt_enable_clock(struct starfive_wdt
*wdt
)
150 ret
= clk_prepare_enable(wdt
->apb_clk
);
152 return dev_err_probe(wdt
->wdd
.parent
, ret
, "failed to enable apb clock\n");
154 ret
= clk_prepare_enable(wdt
->core_clk
);
156 clk_disable_unprepare(wdt
->apb_clk
);
157 return dev_err_probe(wdt
->wdd
.parent
, ret
, "failed to enable core clock\n");
163 static void starfive_wdt_disable_clock(struct starfive_wdt
*wdt
)
165 clk_disable_unprepare(wdt
->core_clk
);
166 clk_disable_unprepare(wdt
->apb_clk
);
169 static inline int starfive_wdt_get_clock(struct starfive_wdt
*wdt
)
171 struct device
*dev
= wdt
->wdd
.parent
;
173 wdt
->apb_clk
= devm_clk_get(dev
, "apb");
174 if (IS_ERR(wdt
->apb_clk
))
175 return dev_err_probe(dev
, PTR_ERR(wdt
->apb_clk
), "failed to get apb clock\n");
177 wdt
->core_clk
= devm_clk_get(dev
, "core");
178 if (IS_ERR(wdt
->core_clk
))
179 return dev_err_probe(dev
, PTR_ERR(wdt
->core_clk
), "failed to get core clock\n");
184 static inline int starfive_wdt_reset_init(struct device
*dev
)
186 struct reset_control
*rsts
;
189 rsts
= devm_reset_control_array_get_exclusive(dev
);
191 return dev_err_probe(dev
, PTR_ERR(rsts
), "failed to get resets\n");
193 ret
= reset_control_deassert(rsts
);
195 return dev_err_probe(dev
, ret
, "failed to deassert resets\n");
200 static u32
starfive_wdt_ticks_to_sec(struct starfive_wdt
*wdt
, u32 ticks
)
202 return DIV_ROUND_CLOSEST(ticks
, wdt
->freq
);
205 /* Write unlock-key to unlock. Write other value to lock. */
206 static void starfive_wdt_unlock(struct starfive_wdt
*wdt
)
207 __acquires(&wdt
->lock
)
209 spin_lock(&wdt
->lock
);
210 writel(wdt
->variant
->unlock_key
, wdt
->base
+ wdt
->variant
->unlock
);
213 static void starfive_wdt_lock(struct starfive_wdt
*wdt
)
214 __releases(&wdt
->lock
)
216 writel(~wdt
->variant
->unlock_key
, wdt
->base
+ wdt
->variant
->unlock
);
217 spin_unlock(&wdt
->lock
);
220 /* enable watchdog interrupt to reset/reboot */
221 static void starfive_wdt_enable_reset(struct starfive_wdt
*wdt
)
225 val
= readl(wdt
->base
+ wdt
->variant
->control
);
226 val
|= STARFIVE_WDT_RESET_EN
<< wdt
->variant
->enrst_shift
;
227 writel(val
, wdt
->base
+ wdt
->variant
->control
);
230 /* interrupt status whether has been raised from the counter */
231 static bool starfive_wdt_raise_irq_status(struct starfive_wdt
*wdt
)
233 return !!readl(wdt
->base
+ wdt
->variant
->int_status
);
236 /* waiting interrupt can be free to clear */
237 static int starfive_wdt_wait_int_free(struct starfive_wdt
*wdt
)
241 return readl_poll_timeout_atomic(wdt
->base
+ wdt
->variant
->int_clr
, value
,
242 !(value
& BIT(wdt
->variant
->intclr_ava_shift
)),
243 STARFIVE_WDT_DELAY_US
, STARFIVE_WDT_TIMEOUT_US
);
246 /* clear interrupt signal before initialization or reload */
247 static int starfive_wdt_int_clr(struct starfive_wdt
*wdt
)
251 if (wdt
->variant
->intclr_check
) {
252 ret
= starfive_wdt_wait_int_free(wdt
);
254 return dev_err_probe(wdt
->wdd
.parent
, ret
,
255 "watchdog is not ready to clear interrupt.\n");
257 writel(STARFIVE_WDT_INTCLR
, wdt
->base
+ wdt
->variant
->int_clr
);
262 static inline void starfive_wdt_set_count(struct starfive_wdt
*wdt
, u32 val
)
264 writel(val
, wdt
->base
+ wdt
->variant
->load
);
267 static inline u32
starfive_wdt_get_count(struct starfive_wdt
*wdt
)
269 return readl(wdt
->base
+ wdt
->variant
->value
);
272 /* enable watchdog */
273 static inline void starfive_wdt_enable(struct starfive_wdt
*wdt
)
277 val
= readl(wdt
->base
+ wdt
->variant
->enable
);
278 val
|= STARFIVE_WDT_ENABLE
<< wdt
->variant
->en_shift
;
279 writel(val
, wdt
->base
+ wdt
->variant
->enable
);
282 /* disable watchdog */
283 static inline void starfive_wdt_disable(struct starfive_wdt
*wdt
)
287 val
= readl(wdt
->base
+ wdt
->variant
->enable
);
288 val
&= ~(STARFIVE_WDT_ENABLE
<< wdt
->variant
->en_shift
);
289 writel(val
, wdt
->base
+ wdt
->variant
->enable
);
292 static inline void starfive_wdt_set_reload_count(struct starfive_wdt
*wdt
, u32 count
)
294 starfive_wdt_set_count(wdt
, count
);
296 /* 7100 need set any value to reload register and could reload value to counter */
297 if (wdt
->variant
->reload
)
298 writel(0x1, wdt
->base
+ wdt
->variant
->reload
);
301 static unsigned int starfive_wdt_max_timeout(struct starfive_wdt
*wdt
)
303 if (wdt
->variant
->double_timeout
)
304 return DIV_ROUND_UP(STARFIVE_WDT_MAXCNT
, (wdt
->freq
/ 2)) - 1;
306 return DIV_ROUND_UP(STARFIVE_WDT_MAXCNT
, wdt
->freq
) - 1;
309 static unsigned int starfive_wdt_get_timeleft(struct watchdog_device
*wdd
)
311 struct starfive_wdt
*wdt
= watchdog_get_drvdata(wdd
);
315 * If the watchdog takes twice timeout and set half count value,
316 * timeleft value should add the count value before first timeout.
318 count
= starfive_wdt_get_count(wdt
);
319 if (wdt
->variant
->double_timeout
&& !starfive_wdt_raise_irq_status(wdt
))
322 return starfive_wdt_ticks_to_sec(wdt
, count
);
325 static int starfive_wdt_keepalive(struct watchdog_device
*wdd
)
327 struct starfive_wdt
*wdt
= watchdog_get_drvdata(wdd
);
330 starfive_wdt_unlock(wdt
);
331 ret
= starfive_wdt_int_clr(wdt
);
335 starfive_wdt_set_reload_count(wdt
, wdt
->count
);
338 /* exit with releasing spinlock and locking registers */
339 starfive_wdt_lock(wdt
);
343 static int starfive_wdt_start(struct starfive_wdt
*wdt
)
347 starfive_wdt_unlock(wdt
);
348 /* disable watchdog, to be safe */
349 starfive_wdt_disable(wdt
);
351 starfive_wdt_enable_reset(wdt
);
352 ret
= starfive_wdt_int_clr(wdt
);
356 starfive_wdt_set_count(wdt
, wdt
->count
);
357 starfive_wdt_enable(wdt
);
360 starfive_wdt_lock(wdt
);
364 static void starfive_wdt_stop(struct starfive_wdt
*wdt
)
366 starfive_wdt_unlock(wdt
);
367 starfive_wdt_disable(wdt
);
368 starfive_wdt_lock(wdt
);
371 static int starfive_wdt_pm_start(struct watchdog_device
*wdd
)
373 struct starfive_wdt
*wdt
= watchdog_get_drvdata(wdd
);
374 int ret
= pm_runtime_get_sync(wdd
->parent
);
379 return starfive_wdt_start(wdt
);
382 static int starfive_wdt_pm_stop(struct watchdog_device
*wdd
)
384 struct starfive_wdt
*wdt
= watchdog_get_drvdata(wdd
);
386 starfive_wdt_stop(wdt
);
387 return pm_runtime_put_sync(wdd
->parent
);
390 static int starfive_wdt_set_timeout(struct watchdog_device
*wdd
,
391 unsigned int timeout
)
393 struct starfive_wdt
*wdt
= watchdog_get_drvdata(wdd
);
394 unsigned long count
= timeout
* wdt
->freq
;
396 /* some watchdogs take two timeouts to reset */
397 if (wdt
->variant
->double_timeout
)
401 wdd
->timeout
= timeout
;
403 starfive_wdt_unlock(wdt
);
404 starfive_wdt_disable(wdt
);
405 starfive_wdt_set_reload_count(wdt
, wdt
->count
);
406 starfive_wdt_enable(wdt
);
407 starfive_wdt_lock(wdt
);
412 #define STARFIVE_WDT_OPTIONS (WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE)
414 static const struct watchdog_info starfive_wdt_info
= {
415 .options
= STARFIVE_WDT_OPTIONS
,
416 .identity
= "StarFive Watchdog",
419 static const struct watchdog_ops starfive_wdt_ops
= {
420 .owner
= THIS_MODULE
,
421 .start
= starfive_wdt_pm_start
,
422 .stop
= starfive_wdt_pm_stop
,
423 .ping
= starfive_wdt_keepalive
,
424 .set_timeout
= starfive_wdt_set_timeout
,
425 .get_timeleft
= starfive_wdt_get_timeleft
,
428 static int starfive_wdt_probe(struct platform_device
*pdev
)
430 struct starfive_wdt
*wdt
;
433 wdt
= devm_kzalloc(&pdev
->dev
, sizeof(*wdt
), GFP_KERNEL
);
437 wdt
->base
= devm_platform_ioremap_resource(pdev
, 0);
438 if (IS_ERR(wdt
->base
))
439 return dev_err_probe(&pdev
->dev
, PTR_ERR(wdt
->base
), "error mapping registers\n");
441 wdt
->wdd
.parent
= &pdev
->dev
;
442 ret
= starfive_wdt_get_clock(wdt
);
446 platform_set_drvdata(pdev
, wdt
);
447 pm_runtime_enable(&pdev
->dev
);
448 if (pm_runtime_enabled(&pdev
->dev
)) {
449 ret
= pm_runtime_get_sync(&pdev
->dev
);
453 /* runtime PM is disabled but clocks need to be enabled */
454 ret
= starfive_wdt_enable_clock(wdt
);
459 ret
= starfive_wdt_reset_init(&pdev
->dev
);
463 watchdog_set_drvdata(&wdt
->wdd
, wdt
);
464 wdt
->wdd
.info
= &starfive_wdt_info
;
465 wdt
->wdd
.ops
= &starfive_wdt_ops
;
466 wdt
->variant
= of_device_get_match_data(&pdev
->dev
);
467 spin_lock_init(&wdt
->lock
);
469 wdt
->freq
= clk_get_rate(wdt
->core_clk
);
471 dev_err(&pdev
->dev
, "get clock rate failed.\n");
476 wdt
->wdd
.min_timeout
= 1;
477 wdt
->wdd
.max_timeout
= starfive_wdt_max_timeout(wdt
);
478 wdt
->wdd
.timeout
= STARFIVE_WDT_DEFAULT_TIME
;
479 watchdog_init_timeout(&wdt
->wdd
, heartbeat
, &pdev
->dev
);
480 starfive_wdt_set_timeout(&wdt
->wdd
, wdt
->wdd
.timeout
);
482 watchdog_set_nowayout(&wdt
->wdd
, nowayout
);
483 watchdog_stop_on_reboot(&wdt
->wdd
);
484 watchdog_stop_on_unregister(&wdt
->wdd
);
487 ret
= starfive_wdt_start(wdt
);
490 set_bit(WDOG_HW_RUNNING
, &wdt
->wdd
.status
);
492 starfive_wdt_stop(wdt
);
495 ret
= watchdog_register_device(&wdt
->wdd
);
500 if (pm_runtime_enabled(&pdev
->dev
)) {
501 ret
= pm_runtime_put_sync(&pdev
->dev
);
510 starfive_wdt_disable_clock(wdt
);
511 pm_runtime_disable(&pdev
->dev
);
516 static void starfive_wdt_remove(struct platform_device
*pdev
)
518 struct starfive_wdt
*wdt
= platform_get_drvdata(pdev
);
520 starfive_wdt_stop(wdt
);
521 watchdog_unregister_device(&wdt
->wdd
);
523 if (pm_runtime_enabled(&pdev
->dev
))
524 pm_runtime_disable(&pdev
->dev
);
526 /* disable clock without PM */
527 starfive_wdt_disable_clock(wdt
);
530 static void starfive_wdt_shutdown(struct platform_device
*pdev
)
532 struct starfive_wdt
*wdt
= platform_get_drvdata(pdev
);
534 starfive_wdt_pm_stop(&wdt
->wdd
);
537 static int starfive_wdt_suspend(struct device
*dev
)
539 struct starfive_wdt
*wdt
= dev_get_drvdata(dev
);
541 /* Save watchdog state, and turn it off. */
542 wdt
->reload
= starfive_wdt_get_count(wdt
);
544 /* Note that WTCNT doesn't need to be saved. */
545 starfive_wdt_stop(wdt
);
547 return pm_runtime_force_suspend(dev
);
550 static int starfive_wdt_resume(struct device
*dev
)
552 struct starfive_wdt
*wdt
= dev_get_drvdata(dev
);
555 ret
= pm_runtime_force_resume(dev
);
559 starfive_wdt_unlock(wdt
);
560 /* Restore watchdog state. */
561 starfive_wdt_set_reload_count(wdt
, wdt
->reload
);
562 starfive_wdt_lock(wdt
);
564 if (watchdog_active(&wdt
->wdd
))
565 return starfive_wdt_start(wdt
);
570 static int starfive_wdt_runtime_suspend(struct device
*dev
)
572 struct starfive_wdt
*wdt
= dev_get_drvdata(dev
);
574 starfive_wdt_disable_clock(wdt
);
579 static int starfive_wdt_runtime_resume(struct device
*dev
)
581 struct starfive_wdt
*wdt
= dev_get_drvdata(dev
);
583 return starfive_wdt_enable_clock(wdt
);
586 static const struct dev_pm_ops starfive_wdt_pm_ops
= {
587 RUNTIME_PM_OPS(starfive_wdt_runtime_suspend
, starfive_wdt_runtime_resume
, NULL
)
588 SYSTEM_SLEEP_PM_OPS(starfive_wdt_suspend
, starfive_wdt_resume
)
591 static const struct of_device_id starfive_wdt_match
[] = {
592 { .compatible
= "starfive,jh7100-wdt", .data
= &starfive_wdt_jh7100_variant
},
593 { .compatible
= "starfive,jh7110-wdt", .data
= &starfive_wdt_jh7110_variant
},
596 MODULE_DEVICE_TABLE(of
, starfive_wdt_match
);
598 static struct platform_driver starfive_wdt_driver
= {
599 .probe
= starfive_wdt_probe
,
600 .remove_new
= starfive_wdt_remove
,
601 .shutdown
= starfive_wdt_shutdown
,
603 .name
= "starfive-wdt",
604 .pm
= pm_ptr(&starfive_wdt_pm_ops
),
605 .of_match_table
= starfive_wdt_match
,
608 module_platform_driver(starfive_wdt_driver
);
610 MODULE_AUTHOR("Xingyu Wu <xingyu.wu@starfivetech.com>");
611 MODULE_AUTHOR("Samin Guo <samin.guo@starfivetech.com>");
612 MODULE_DESCRIPTION("StarFive Watchdog Device Driver");
613 MODULE_LICENSE("GPL");