1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Starfive Watchdog driver |
4 | * |
5 | * Copyright (C) 2022 StarFive Technology Co., Ltd. |
6 | */ |
7 | |
8 | #include <linux/clk.h> |
9 | #include <linux/iopoll.h> |
10 | #include <linux/module.h> |
11 | #include <linux/of.h> |
12 | #include <linux/platform_device.h> |
13 | #include <linux/pm_runtime.h> |
14 | #include <linux/reset.h> |
15 | #include <linux/watchdog.h> |
16 | |
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 |
27 | * [31:2]: reserved. |
28 | */ |
29 | #define STARFIVE_WDT_JH7100_LOCK 0x13c /* write 0x378f0765 to unlock */ |
30 | |
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 /* |
35 | * [0]: reset enable; |
36 | * [1]: interrupt enable && watchdog enable |
37 | * [31:2]: reserved. |
38 | */ |
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 */ |
42 | |
43 | /* WDOGCONTROL */ |
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 |
49 | |
50 | /* WDOGLOCK */ |
51 | #define STARFIVE_WDT_JH7100_UNLOCK_KEY 0x378f0765 |
52 | #define STARFIVE_WDT_JH7110_UNLOCK_KEY 0x1acce551 |
53 | |
54 | /* WDOGINTCLR */ |
55 | #define STARFIVE_WDT_INTCLR 0x1 |
56 | #define STARFIVE_WDT_JH7100_INTCLR_AVA_SHIFT 1 /* Watchdog can clear interrupt when 0 */ |
57 | |
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 |
62 | |
63 | /* module parameter */ |
64 | #define STARFIVE_WDT_EARLY_ENA 0 |
65 | |
66 | static bool nowayout = WATCHDOG_NOWAYOUT; |
67 | static int heartbeat; |
68 | static bool early_enable = STARFIVE_WDT_EARLY_ENA; |
69 | |
70 | module_param(heartbeat, int, 0); |
71 | module_param(early_enable, bool, 0); |
72 | module_param(nowayout, bool, 0); |
73 | |
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) ")" ); |
81 | |
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 */ |
91 | |
92 | u32 unlock_key; |
93 | char enrst_shift; |
94 | char en_shift; |
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 */ |
98 | }; |
99 | |
100 | struct starfive_wdt { |
101 | struct watchdog_device wdd; |
102 | spinlock_t lock; /* spinlock for register handling */ |
103 | void __iomem *base; |
104 | struct clk *core_clk; |
105 | struct clk *apb_clk; |
106 | const struct starfive_wdt_variant *variant; |
107 | unsigned long freq; |
108 | u32 count; /* count of timeout */ |
109 | u32 reload; /* restore the count */ |
110 | }; |
111 | |
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, |
128 | }; |
129 | |
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, |
144 | }; |
145 | |
146 | static int starfive_wdt_enable_clock(struct starfive_wdt *wdt) |
147 | { |
148 | int ret; |
149 | |
150 | ret = clk_prepare_enable(clk: wdt->apb_clk); |
151 | if (ret) |
152 | return dev_err_probe(dev: wdt->wdd.parent, err: ret, fmt: "failed to enable apb clock\n" ); |
153 | |
154 | ret = clk_prepare_enable(clk: wdt->core_clk); |
155 | if (ret) |
156 | return dev_err_probe(dev: wdt->wdd.parent, err: ret, fmt: "failed to enable core clock\n" ); |
157 | |
158 | return 0; |
159 | } |
160 | |
161 | static void starfive_wdt_disable_clock(struct starfive_wdt *wdt) |
162 | { |
163 | clk_disable_unprepare(clk: wdt->core_clk); |
164 | clk_disable_unprepare(clk: wdt->apb_clk); |
165 | } |
166 | |
167 | static inline int starfive_wdt_get_clock(struct starfive_wdt *wdt) |
168 | { |
169 | struct device *dev = wdt->wdd.parent; |
170 | |
171 | wdt->apb_clk = devm_clk_get(dev, id: "apb" ); |
172 | if (IS_ERR(ptr: wdt->apb_clk)) |
173 | return dev_err_probe(dev, err: PTR_ERR(ptr: wdt->apb_clk), fmt: "failed to get apb clock\n" ); |
174 | |
175 | wdt->core_clk = devm_clk_get(dev, id: "core" ); |
176 | if (IS_ERR(ptr: wdt->core_clk)) |
177 | return dev_err_probe(dev, err: PTR_ERR(ptr: wdt->core_clk), fmt: "failed to get core clock\n" ); |
178 | |
179 | return 0; |
180 | } |
181 | |
182 | static inline int starfive_wdt_reset_init(struct device *dev) |
183 | { |
184 | struct reset_control *rsts; |
185 | int ret; |
186 | |
187 | rsts = devm_reset_control_array_get_exclusive(dev); |
188 | if (IS_ERR(ptr: rsts)) |
189 | return dev_err_probe(dev, err: PTR_ERR(ptr: rsts), fmt: "failed to get resets\n" ); |
190 | |
191 | ret = reset_control_deassert(rstc: rsts); |
192 | if (ret) |
193 | return dev_err_probe(dev, err: ret, fmt: "failed to deassert resets\n" ); |
194 | |
195 | return 0; |
196 | } |
197 | |
198 | static u32 starfive_wdt_ticks_to_sec(struct starfive_wdt *wdt, u32 ticks) |
199 | { |
200 | return DIV_ROUND_CLOSEST(ticks, wdt->freq); |
201 | } |
202 | |
203 | /* Write unlock-key to unlock. Write other value to lock. */ |
204 | static void starfive_wdt_unlock(struct starfive_wdt *wdt) |
205 | __acquires(&wdt->lock) |
206 | { |
207 | spin_lock(lock: &wdt->lock); |
208 | writel(val: wdt->variant->unlock_key, addr: wdt->base + wdt->variant->unlock); |
209 | } |
210 | |
211 | static void starfive_wdt_lock(struct starfive_wdt *wdt) |
212 | __releases(&wdt->lock) |
213 | { |
214 | writel(val: ~wdt->variant->unlock_key, addr: wdt->base + wdt->variant->unlock); |
215 | spin_unlock(lock: &wdt->lock); |
216 | } |
217 | |
218 | /* enable watchdog interrupt to reset/reboot */ |
219 | static void starfive_wdt_enable_reset(struct starfive_wdt *wdt) |
220 | { |
221 | u32 val; |
222 | |
223 | val = readl(addr: wdt->base + wdt->variant->control); |
224 | val |= STARFIVE_WDT_RESET_EN << wdt->variant->enrst_shift; |
225 | writel(val, addr: wdt->base + wdt->variant->control); |
226 | } |
227 | |
228 | /* interrupt status whether has been raised from the counter */ |
229 | static bool starfive_wdt_raise_irq_status(struct starfive_wdt *wdt) |
230 | { |
231 | return !!readl(addr: wdt->base + wdt->variant->int_status); |
232 | } |
233 | |
234 | /* waiting interrupt can be free to clear */ |
235 | static int starfive_wdt_wait_int_free(struct starfive_wdt *wdt) |
236 | { |
237 | u32 value; |
238 | |
239 | return readl_poll_timeout_atomic(wdt->base + wdt->variant->int_clr, value, |
240 | !(value & BIT(wdt->variant->intclr_ava_shift)), |
241 | STARFIVE_WDT_DELAY_US, STARFIVE_WDT_TIMEOUT_US); |
242 | } |
243 | |
244 | /* clear interrupt signal before initialization or reload */ |
245 | static int starfive_wdt_int_clr(struct starfive_wdt *wdt) |
246 | { |
247 | int ret; |
248 | |
249 | if (wdt->variant->intclr_check) { |
250 | ret = starfive_wdt_wait_int_free(wdt); |
251 | if (ret) |
252 | return dev_err_probe(dev: wdt->wdd.parent, err: ret, |
253 | fmt: "watchdog is not ready to clear interrupt.\n" ); |
254 | } |
255 | writel(STARFIVE_WDT_INTCLR, addr: wdt->base + wdt->variant->int_clr); |
256 | |
257 | return 0; |
258 | } |
259 | |
260 | static inline void starfive_wdt_set_count(struct starfive_wdt *wdt, u32 val) |
261 | { |
262 | writel(val, addr: wdt->base + wdt->variant->load); |
263 | } |
264 | |
265 | static inline u32 starfive_wdt_get_count(struct starfive_wdt *wdt) |
266 | { |
267 | return readl(addr: wdt->base + wdt->variant->value); |
268 | } |
269 | |
270 | /* enable watchdog */ |
271 | static inline void starfive_wdt_enable(struct starfive_wdt *wdt) |
272 | { |
273 | u32 val; |
274 | |
275 | val = readl(addr: wdt->base + wdt->variant->enable); |
276 | val |= STARFIVE_WDT_ENABLE << wdt->variant->en_shift; |
277 | writel(val, addr: wdt->base + wdt->variant->enable); |
278 | } |
279 | |
280 | /* disable watchdog */ |
281 | static inline void starfive_wdt_disable(struct starfive_wdt *wdt) |
282 | { |
283 | u32 val; |
284 | |
285 | val = readl(addr: wdt->base + wdt->variant->enable); |
286 | val &= ~(STARFIVE_WDT_ENABLE << wdt->variant->en_shift); |
287 | writel(val, addr: wdt->base + wdt->variant->enable); |
288 | } |
289 | |
290 | static inline void starfive_wdt_set_reload_count(struct starfive_wdt *wdt, u32 count) |
291 | { |
292 | starfive_wdt_set_count(wdt, val: count); |
293 | |
294 | /* 7100 need set any value to reload register and could reload value to counter */ |
295 | if (wdt->variant->reload) |
296 | writel(val: 0x1, addr: wdt->base + wdt->variant->reload); |
297 | } |
298 | |
299 | static unsigned int starfive_wdt_max_timeout(struct starfive_wdt *wdt) |
300 | { |
301 | if (wdt->variant->double_timeout) |
302 | return DIV_ROUND_UP(STARFIVE_WDT_MAXCNT, (wdt->freq / 2)) - 1; |
303 | |
304 | return DIV_ROUND_UP(STARFIVE_WDT_MAXCNT, wdt->freq) - 1; |
305 | } |
306 | |
307 | static unsigned int starfive_wdt_get_timeleft(struct watchdog_device *wdd) |
308 | { |
309 | struct starfive_wdt *wdt = watchdog_get_drvdata(wdd); |
310 | u32 count; |
311 | |
312 | /* |
313 | * If the watchdog takes twice timeout and set half count value, |
314 | * timeleft value should add the count value before first timeout. |
315 | */ |
316 | count = starfive_wdt_get_count(wdt); |
317 | if (wdt->variant->double_timeout && !starfive_wdt_raise_irq_status(wdt)) |
318 | count += wdt->count; |
319 | |
320 | return starfive_wdt_ticks_to_sec(wdt, ticks: count); |
321 | } |
322 | |
323 | static int starfive_wdt_keepalive(struct watchdog_device *wdd) |
324 | { |
325 | struct starfive_wdt *wdt = watchdog_get_drvdata(wdd); |
326 | int ret; |
327 | |
328 | starfive_wdt_unlock(wdt); |
329 | ret = starfive_wdt_int_clr(wdt); |
330 | if (ret) |
331 | goto exit; |
332 | |
333 | starfive_wdt_set_reload_count(wdt, count: wdt->count); |
334 | |
335 | exit: |
336 | /* exit with releasing spinlock and locking registers */ |
337 | starfive_wdt_lock(wdt); |
338 | return ret; |
339 | } |
340 | |
341 | static int starfive_wdt_start(struct starfive_wdt *wdt) |
342 | { |
343 | int ret; |
344 | |
345 | starfive_wdt_unlock(wdt); |
346 | /* disable watchdog, to be safe */ |
347 | starfive_wdt_disable(wdt); |
348 | |
349 | starfive_wdt_enable_reset(wdt); |
350 | ret = starfive_wdt_int_clr(wdt); |
351 | if (ret) |
352 | goto exit; |
353 | |
354 | starfive_wdt_set_count(wdt, val: wdt->count); |
355 | starfive_wdt_enable(wdt); |
356 | |
357 | exit: |
358 | starfive_wdt_lock(wdt); |
359 | return ret; |
360 | } |
361 | |
362 | static void starfive_wdt_stop(struct starfive_wdt *wdt) |
363 | { |
364 | starfive_wdt_unlock(wdt); |
365 | starfive_wdt_disable(wdt); |
366 | starfive_wdt_lock(wdt); |
367 | } |
368 | |
369 | static int starfive_wdt_pm_start(struct watchdog_device *wdd) |
370 | { |
371 | struct starfive_wdt *wdt = watchdog_get_drvdata(wdd); |
372 | int ret = pm_runtime_get_sync(dev: wdd->parent); |
373 | |
374 | if (ret < 0) |
375 | return ret; |
376 | |
377 | return starfive_wdt_start(wdt); |
378 | } |
379 | |
380 | static int starfive_wdt_pm_stop(struct watchdog_device *wdd) |
381 | { |
382 | struct starfive_wdt *wdt = watchdog_get_drvdata(wdd); |
383 | |
384 | starfive_wdt_stop(wdt); |
385 | return pm_runtime_put_sync(dev: wdd->parent); |
386 | } |
387 | |
388 | static int starfive_wdt_set_timeout(struct watchdog_device *wdd, |
389 | unsigned int timeout) |
390 | { |
391 | struct starfive_wdt *wdt = watchdog_get_drvdata(wdd); |
392 | unsigned long count = timeout * wdt->freq; |
393 | |
394 | /* some watchdogs take two timeouts to reset */ |
395 | if (wdt->variant->double_timeout) |
396 | count /= 2; |
397 | |
398 | wdt->count = count; |
399 | wdd->timeout = timeout; |
400 | |
401 | starfive_wdt_unlock(wdt); |
402 | starfive_wdt_disable(wdt); |
403 | starfive_wdt_set_reload_count(wdt, count: wdt->count); |
404 | starfive_wdt_enable(wdt); |
405 | starfive_wdt_lock(wdt); |
406 | |
407 | return 0; |
408 | } |
409 | |
410 | #define STARFIVE_WDT_OPTIONS (WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE) |
411 | |
412 | static const struct watchdog_info starfive_wdt_info = { |
413 | .options = STARFIVE_WDT_OPTIONS, |
414 | .identity = "StarFive Watchdog" , |
415 | }; |
416 | |
417 | static const struct watchdog_ops starfive_wdt_ops = { |
418 | .owner = THIS_MODULE, |
419 | .start = starfive_wdt_pm_start, |
420 | .stop = starfive_wdt_pm_stop, |
421 | .ping = starfive_wdt_keepalive, |
422 | .set_timeout = starfive_wdt_set_timeout, |
423 | .get_timeleft = starfive_wdt_get_timeleft, |
424 | }; |
425 | |
426 | static int starfive_wdt_probe(struct platform_device *pdev) |
427 | { |
428 | struct starfive_wdt *wdt; |
429 | int ret; |
430 | |
431 | wdt = devm_kzalloc(dev: &pdev->dev, size: sizeof(*wdt), GFP_KERNEL); |
432 | if (!wdt) |
433 | return -ENOMEM; |
434 | |
435 | wdt->base = devm_platform_ioremap_resource(pdev, index: 0); |
436 | if (IS_ERR(ptr: wdt->base)) |
437 | return dev_err_probe(dev: &pdev->dev, err: PTR_ERR(ptr: wdt->base), fmt: "error mapping registers\n" ); |
438 | |
439 | wdt->wdd.parent = &pdev->dev; |
440 | ret = starfive_wdt_get_clock(wdt); |
441 | if (ret) |
442 | return ret; |
443 | |
444 | platform_set_drvdata(pdev, data: wdt); |
445 | pm_runtime_enable(dev: &pdev->dev); |
446 | if (pm_runtime_enabled(dev: &pdev->dev)) { |
447 | ret = pm_runtime_get_sync(dev: &pdev->dev); |
448 | if (ret < 0) |
449 | return ret; |
450 | } else { |
451 | /* runtime PM is disabled but clocks need to be enabled */ |
452 | ret = starfive_wdt_enable_clock(wdt); |
453 | if (ret) |
454 | return ret; |
455 | } |
456 | |
457 | ret = starfive_wdt_reset_init(dev: &pdev->dev); |
458 | if (ret) |
459 | goto err_exit; |
460 | |
461 | watchdog_set_drvdata(wdd: &wdt->wdd, data: wdt); |
462 | wdt->wdd.info = &starfive_wdt_info; |
463 | wdt->wdd.ops = &starfive_wdt_ops; |
464 | wdt->variant = of_device_get_match_data(dev: &pdev->dev); |
465 | spin_lock_init(&wdt->lock); |
466 | |
467 | wdt->freq = clk_get_rate(clk: wdt->core_clk); |
468 | if (!wdt->freq) { |
469 | dev_err(&pdev->dev, "get clock rate failed.\n" ); |
470 | ret = -EINVAL; |
471 | goto err_exit; |
472 | } |
473 | |
474 | wdt->wdd.min_timeout = 1; |
475 | wdt->wdd.max_timeout = starfive_wdt_max_timeout(wdt); |
476 | wdt->wdd.timeout = STARFIVE_WDT_DEFAULT_TIME; |
477 | watchdog_init_timeout(wdd: &wdt->wdd, timeout_parm: heartbeat, dev: &pdev->dev); |
478 | starfive_wdt_set_timeout(wdd: &wdt->wdd, timeout: wdt->wdd.timeout); |
479 | |
480 | watchdog_set_nowayout(wdd: &wdt->wdd, nowayout); |
481 | watchdog_stop_on_reboot(wdd: &wdt->wdd); |
482 | watchdog_stop_on_unregister(wdd: &wdt->wdd); |
483 | |
484 | if (early_enable) { |
485 | ret = starfive_wdt_start(wdt); |
486 | if (ret) |
487 | goto err_exit; |
488 | set_bit(WDOG_HW_RUNNING, addr: &wdt->wdd.status); |
489 | } else { |
490 | starfive_wdt_stop(wdt); |
491 | } |
492 | |
493 | ret = watchdog_register_device(&wdt->wdd); |
494 | if (ret) |
495 | goto err_exit; |
496 | |
497 | if (!early_enable) { |
498 | if (pm_runtime_enabled(dev: &pdev->dev)) { |
499 | ret = pm_runtime_put_sync(dev: &pdev->dev); |
500 | if (ret) |
501 | goto err_exit; |
502 | } |
503 | } |
504 | |
505 | return 0; |
506 | |
507 | err_exit: |
508 | starfive_wdt_disable_clock(wdt); |
509 | pm_runtime_disable(dev: &pdev->dev); |
510 | |
511 | return ret; |
512 | } |
513 | |
514 | static void starfive_wdt_remove(struct platform_device *pdev) |
515 | { |
516 | struct starfive_wdt *wdt = platform_get_drvdata(pdev); |
517 | |
518 | starfive_wdt_stop(wdt); |
519 | watchdog_unregister_device(&wdt->wdd); |
520 | |
521 | if (pm_runtime_enabled(dev: &pdev->dev)) |
522 | pm_runtime_disable(dev: &pdev->dev); |
523 | else |
524 | /* disable clock without PM */ |
525 | starfive_wdt_disable_clock(wdt); |
526 | } |
527 | |
528 | static void starfive_wdt_shutdown(struct platform_device *pdev) |
529 | { |
530 | struct starfive_wdt *wdt = platform_get_drvdata(pdev); |
531 | |
532 | starfive_wdt_pm_stop(wdd: &wdt->wdd); |
533 | } |
534 | |
535 | static int starfive_wdt_suspend(struct device *dev) |
536 | { |
537 | struct starfive_wdt *wdt = dev_get_drvdata(dev); |
538 | |
539 | /* Save watchdog state, and turn it off. */ |
540 | wdt->reload = starfive_wdt_get_count(wdt); |
541 | |
542 | /* Note that WTCNT doesn't need to be saved. */ |
543 | starfive_wdt_stop(wdt); |
544 | |
545 | return pm_runtime_force_suspend(dev); |
546 | } |
547 | |
548 | static int starfive_wdt_resume(struct device *dev) |
549 | { |
550 | struct starfive_wdt *wdt = dev_get_drvdata(dev); |
551 | int ret; |
552 | |
553 | ret = pm_runtime_force_resume(dev); |
554 | if (ret) |
555 | return ret; |
556 | |
557 | starfive_wdt_unlock(wdt); |
558 | /* Restore watchdog state. */ |
559 | starfive_wdt_set_reload_count(wdt, count: wdt->reload); |
560 | starfive_wdt_lock(wdt); |
561 | |
562 | if (watchdog_active(wdd: &wdt->wdd)) |
563 | return starfive_wdt_start(wdt); |
564 | |
565 | return 0; |
566 | } |
567 | |
568 | static int starfive_wdt_runtime_suspend(struct device *dev) |
569 | { |
570 | struct starfive_wdt *wdt = dev_get_drvdata(dev); |
571 | |
572 | starfive_wdt_disable_clock(wdt); |
573 | |
574 | return 0; |
575 | } |
576 | |
577 | static int starfive_wdt_runtime_resume(struct device *dev) |
578 | { |
579 | struct starfive_wdt *wdt = dev_get_drvdata(dev); |
580 | |
581 | return starfive_wdt_enable_clock(wdt); |
582 | } |
583 | |
584 | static const struct dev_pm_ops starfive_wdt_pm_ops = { |
585 | RUNTIME_PM_OPS(starfive_wdt_runtime_suspend, starfive_wdt_runtime_resume, NULL) |
586 | SYSTEM_SLEEP_PM_OPS(starfive_wdt_suspend, starfive_wdt_resume) |
587 | }; |
588 | |
589 | static const struct of_device_id starfive_wdt_match[] = { |
590 | { .compatible = "starfive,jh7100-wdt" , .data = &starfive_wdt_jh7100_variant }, |
591 | { .compatible = "starfive,jh7110-wdt" , .data = &starfive_wdt_jh7110_variant }, |
592 | { /* sentinel */ } |
593 | }; |
594 | MODULE_DEVICE_TABLE(of, starfive_wdt_match); |
595 | |
596 | static struct platform_driver starfive_wdt_driver = { |
597 | .probe = starfive_wdt_probe, |
598 | .remove_new = starfive_wdt_remove, |
599 | .shutdown = starfive_wdt_shutdown, |
600 | .driver = { |
601 | .name = "starfive-wdt" , |
602 | .pm = pm_ptr(&starfive_wdt_pm_ops), |
603 | .of_match_table = starfive_wdt_match, |
604 | }, |
605 | }; |
606 | module_platform_driver(starfive_wdt_driver); |
607 | |
608 | MODULE_AUTHOR("Xingyu Wu <xingyu.wu@starfivetech.com>" ); |
609 | MODULE_AUTHOR("Samin Guo <samin.guo@starfivetech.com>" ); |
610 | MODULE_DESCRIPTION("StarFive Watchdog Device Driver" ); |
611 | MODULE_LICENSE("GPL" ); |
612 | |