Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

drivers: watchdog: Add StarFive Watchdog driver

Add watchdog driver for the StarFive JH7100 and JH7110 SoC.

Signed-off-by: Xingyu Wu <xingyu.wu@starfivetech.com>
Reviewed-by: Emil Renner Berthing <emil.renner.berthing@canonical.com>
Reviewed-by: Guenter Roeck <linux@roeck-us.net>
Link: https://lore.kernel.org/r/20230314132437.121534-3-xingyu.wu@starfivetech.com
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Wim Van Sebroeck <wim@linux-watchdog.org>

authored by

Xingyu Wu and committed by
Wim Van Sebroeck
db728ea9 b24e265d

+627
+7
MAINTAINERS
··· 19951 19951 F: Documentation/devicetree/bindings/rng/starfive* 19952 19952 F: drivers/char/hw_random/jh7110-trng.c 19953 19953 19954 + STARFIVE WATCHDOG DRIVER 19955 + M: Xingyu Wu <xingyu.wu@starfivetech.com> 19956 + M: Samin Guo <samin.guo@starfivetech.com> 19957 + S: Supported 19958 + F: Documentation/devicetree/bindings/watchdog/starfive* 19959 + F: drivers/watchdog/starfive-wdt.c 19960 + 19954 19961 STATIC BRANCH/CALL 19955 19962 M: Peter Zijlstra <peterz@infradead.org> 19956 19963 M: Josh Poimboeuf <jpoimboe@kernel.org>
+11
drivers/watchdog/Kconfig
··· 1999 1999 To compile this driver as a module, choose M here. The module 2000 2000 will be called wdrtas. 2001 2001 2002 + # RISC-V Architecture 2003 + 2004 + config STARFIVE_WATCHDOG 2005 + tristate "StarFive Watchdog support" 2006 + depends on ARCH_STARFIVE || COMPILE_TEST 2007 + select WATCHDOG_CORE 2008 + default ARCH_STARFIVE 2009 + help 2010 + Say Y here to support the watchdog of StarFive JH7100 and JH7110 2011 + SoC. This driver can also be built as a module if choose M. 2012 + 2002 2013 # S390 Architecture 2003 2014 2004 2015 config DIAG288_WATCHDOG
+3
drivers/watchdog/Makefile
··· 192 192 obj-$(CONFIG_PSERIES_WDT) += pseries-wdt.o 193 193 obj-$(CONFIG_WATCHDOG_RTAS) += wdrtas.o 194 194 195 + # RISC-V Architecture 196 + obj-$(CONFIG_STARFIVE_WATCHDOG) += starfive-wdt.o 197 + 195 198 # S390 Architecture 196 199 obj-$(CONFIG_DIAG288_WATCHDOG) += diag288_wdt.o 197 200
+606
drivers/watchdog/starfive-wdt.c
··· 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_device.h> 12 + #include <linux/pm_runtime.h> 13 + #include <linux/reset.h> 14 + #include <linux/watchdog.h> 15 + 16 + /* JH7100 Watchdog register define */ 17 + #define STARFIVE_WDT_JH7100_INTSTAUS 0x000 18 + #define STARFIVE_WDT_JH7100_CONTROL 0x104 19 + #define STARFIVE_WDT_JH7100_LOAD 0x108 20 + #define STARFIVE_WDT_JH7100_EN 0x110 21 + #define STARFIVE_WDT_JH7100_RELOAD 0x114 /* Write 0 or 1 to reload preset value */ 22 + #define STARFIVE_WDT_JH7100_VALUE 0x118 23 + #define STARFIVE_WDT_JH7100_INTCLR 0x120 /* 24 + * [0]: Write 1 to clear interrupt 25 + * [1]: 1 mean clearing and 0 mean complete 26 + * [31:2]: reserved. 27 + */ 28 + #define STARFIVE_WDT_JH7100_LOCK 0x13c /* write 0x378f0765 to unlock */ 29 + 30 + /* JH7110 Watchdog register define */ 31 + #define STARFIVE_WDT_JH7110_LOAD 0x000 32 + #define STARFIVE_WDT_JH7110_VALUE 0x004 33 + #define STARFIVE_WDT_JH7110_CONTROL 0x008 /* 34 + * [0]: reset enable; 35 + * [1]: interrupt enable && watchdog enable 36 + * [31:2]: reserved. 37 + */ 38 + #define STARFIVE_WDT_JH7110_INTCLR 0x00c /* clear intterupt and reload the counter */ 39 + #define STARFIVE_WDT_JH7110_IMS 0x014 40 + #define STARFIVE_WDT_JH7110_LOCK 0xc00 /* write 0x1ACCE551 to unlock */ 41 + 42 + /* WDOGCONTROL */ 43 + #define STARFIVE_WDT_ENABLE 0x1 44 + #define STARFIVE_WDT_EN_SHIFT 0 45 + #define STARFIVE_WDT_RESET_EN 0x1 46 + #define STARFIVE_WDT_JH7100_RST_EN_SHIFT 0 47 + #define STARFIVE_WDT_JH7110_RST_EN_SHIFT 1 48 + 49 + /* WDOGLOCK */ 50 + #define STARFIVE_WDT_JH7100_UNLOCK_KEY 0x378f0765 51 + #define STARFIVE_WDT_JH7110_UNLOCK_KEY 0x1acce551 52 + 53 + /* WDOGINTCLR */ 54 + #define STARFIVE_WDT_INTCLR 0x1 55 + #define STARFIVE_WDT_JH7100_INTCLR_AVA_SHIFT 1 /* Watchdog can clear interrupt when 0 */ 56 + 57 + #define STARFIVE_WDT_MAXCNT 0xffffffff 58 + #define STARFIVE_WDT_DEFAULT_TIME (15) 59 + #define STARFIVE_WDT_DELAY_US 0 60 + #define STARFIVE_WDT_TIMEOUT_US 10000 61 + 62 + /* module parameter */ 63 + #define STARFIVE_WDT_EARLY_ENA 0 64 + 65 + static bool nowayout = WATCHDOG_NOWAYOUT; 66 + static int heartbeat; 67 + static bool early_enable = STARFIVE_WDT_EARLY_ENA; 68 + 69 + module_param(heartbeat, int, 0); 70 + module_param(early_enable, bool, 0); 71 + module_param(nowayout, bool, 0); 72 + 73 + MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. (default=" 74 + __MODULE_STRING(STARFIVE_WDT_DEFAULT_TIME) ")"); 75 + MODULE_PARM_DESC(early_enable, 76 + "Watchdog is started at boot time if set to 1, default=" 77 + __MODULE_STRING(STARFIVE_WDT_EARLY_ENA)); 78 + MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" 79 + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 80 + 81 + struct starfive_wdt_variant { 82 + unsigned int control; /* Watchdog Control Resgister for reset enable */ 83 + unsigned int load; /* Watchdog Load register */ 84 + unsigned int reload; /* Watchdog Reload Control register */ 85 + unsigned int enable; /* Watchdog Enable Register */ 86 + unsigned int value; /* Watchdog Counter Value Register */ 87 + unsigned int int_clr; /* Watchdog Interrupt Clear Register */ 88 + unsigned int unlock; /* Watchdog Lock Register */ 89 + unsigned int int_status; /* Watchdog Interrupt Status Register */ 90 + 91 + u32 unlock_key; 92 + char enrst_shift; 93 + char en_shift; 94 + bool intclr_check; /* whether need to check it before clearing interrupt */ 95 + char intclr_ava_shift; 96 + bool double_timeout; /* The watchdog need twice timeout to reboot */ 97 + }; 98 + 99 + struct starfive_wdt { 100 + struct watchdog_device wdd; 101 + spinlock_t lock; /* spinlock for register handling */ 102 + void __iomem *base; 103 + struct clk *core_clk; 104 + struct clk *apb_clk; 105 + const struct starfive_wdt_variant *variant; 106 + unsigned long freq; 107 + u32 count; /* count of timeout */ 108 + u32 reload; /* restore the count */ 109 + }; 110 + 111 + /* Register layout and configuration for the JH7100 */ 112 + static const struct starfive_wdt_variant starfive_wdt_jh7100_variant = { 113 + .control = STARFIVE_WDT_JH7100_CONTROL, 114 + .load = STARFIVE_WDT_JH7100_LOAD, 115 + .reload = STARFIVE_WDT_JH7100_RELOAD, 116 + .enable = STARFIVE_WDT_JH7100_EN, 117 + .value = STARFIVE_WDT_JH7100_VALUE, 118 + .int_clr = STARFIVE_WDT_JH7100_INTCLR, 119 + .unlock = STARFIVE_WDT_JH7100_LOCK, 120 + .unlock_key = STARFIVE_WDT_JH7100_UNLOCK_KEY, 121 + .int_status = STARFIVE_WDT_JH7100_INTSTAUS, 122 + .enrst_shift = STARFIVE_WDT_JH7100_RST_EN_SHIFT, 123 + .en_shift = STARFIVE_WDT_EN_SHIFT, 124 + .intclr_check = true, 125 + .intclr_ava_shift = STARFIVE_WDT_JH7100_INTCLR_AVA_SHIFT, 126 + .double_timeout = false, 127 + }; 128 + 129 + /* Register layout and configuration for the JH7110 */ 130 + static const struct starfive_wdt_variant starfive_wdt_jh7110_variant = { 131 + .control = STARFIVE_WDT_JH7110_CONTROL, 132 + .load = STARFIVE_WDT_JH7110_LOAD, 133 + .enable = STARFIVE_WDT_JH7110_CONTROL, 134 + .value = STARFIVE_WDT_JH7110_VALUE, 135 + .int_clr = STARFIVE_WDT_JH7110_INTCLR, 136 + .unlock = STARFIVE_WDT_JH7110_LOCK, 137 + .unlock_key = STARFIVE_WDT_JH7110_UNLOCK_KEY, 138 + .int_status = STARFIVE_WDT_JH7110_IMS, 139 + .enrst_shift = STARFIVE_WDT_JH7110_RST_EN_SHIFT, 140 + .en_shift = STARFIVE_WDT_EN_SHIFT, 141 + .intclr_check = false, 142 + .double_timeout = true, 143 + }; 144 + 145 + static int starfive_wdt_enable_clock(struct starfive_wdt *wdt) 146 + { 147 + int ret; 148 + 149 + ret = clk_prepare_enable(wdt->apb_clk); 150 + if (ret) 151 + return dev_err_probe(wdt->wdd.parent, ret, "failed to enable apb clock\n"); 152 + 153 + ret = clk_prepare_enable(wdt->core_clk); 154 + if (ret) 155 + return dev_err_probe(wdt->wdd.parent, ret, "failed to enable core clock\n"); 156 + 157 + return 0; 158 + } 159 + 160 + static void starfive_wdt_disable_clock(struct starfive_wdt *wdt) 161 + { 162 + clk_disable_unprepare(wdt->core_clk); 163 + clk_disable_unprepare(wdt->apb_clk); 164 + } 165 + 166 + static inline int starfive_wdt_get_clock(struct starfive_wdt *wdt) 167 + { 168 + struct device *dev = wdt->wdd.parent; 169 + 170 + wdt->apb_clk = devm_clk_get(dev, "apb"); 171 + if (IS_ERR(wdt->apb_clk)) 172 + return dev_err_probe(dev, PTR_ERR(wdt->apb_clk), "failed to get apb clock\n"); 173 + 174 + wdt->core_clk = devm_clk_get(dev, "core"); 175 + if (IS_ERR(wdt->core_clk)) 176 + return dev_err_probe(dev, PTR_ERR(wdt->core_clk), "failed to get core clock\n"); 177 + 178 + return 0; 179 + } 180 + 181 + static inline int starfive_wdt_reset_init(struct device *dev) 182 + { 183 + struct reset_control *rsts; 184 + int ret; 185 + 186 + rsts = devm_reset_control_array_get_exclusive(dev); 187 + if (IS_ERR(rsts)) 188 + return dev_err_probe(dev, PTR_ERR(rsts), "failed to get resets\n"); 189 + 190 + ret = reset_control_deassert(rsts); 191 + if (ret) 192 + return dev_err_probe(dev, ret, "failed to deassert resets\n"); 193 + 194 + return 0; 195 + } 196 + 197 + static u32 starfive_wdt_ticks_to_sec(struct starfive_wdt *wdt, u32 ticks) 198 + { 199 + return DIV_ROUND_CLOSEST(ticks, wdt->freq); 200 + } 201 + 202 + /* Write unlock-key to unlock. Write other value to lock. */ 203 + static void starfive_wdt_unlock(struct starfive_wdt *wdt) 204 + { 205 + spin_lock(&wdt->lock); 206 + writel(wdt->variant->unlock_key, wdt->base + wdt->variant->unlock); 207 + } 208 + 209 + static void starfive_wdt_lock(struct starfive_wdt *wdt) 210 + { 211 + writel(~wdt->variant->unlock_key, wdt->base + wdt->variant->unlock); 212 + spin_unlock(&wdt->lock); 213 + } 214 + 215 + /* enable watchdog interrupt to reset/reboot */ 216 + static void starfive_wdt_enable_reset(struct starfive_wdt *wdt) 217 + { 218 + u32 val; 219 + 220 + val = readl(wdt->base + wdt->variant->control); 221 + val |= STARFIVE_WDT_RESET_EN << wdt->variant->enrst_shift; 222 + writel(val, wdt->base + wdt->variant->control); 223 + } 224 + 225 + /* interrupt status whether has been raised from the counter */ 226 + static bool starfive_wdt_raise_irq_status(struct starfive_wdt *wdt) 227 + { 228 + return !!readl(wdt->base + wdt->variant->int_status); 229 + } 230 + 231 + /* waiting interrupt can be free to clear */ 232 + static int starfive_wdt_wait_int_free(struct starfive_wdt *wdt) 233 + { 234 + u32 value; 235 + 236 + return readl_poll_timeout_atomic(wdt->base + wdt->variant->int_clr, value, 237 + !(value & BIT(wdt->variant->intclr_ava_shift)), 238 + STARFIVE_WDT_DELAY_US, STARFIVE_WDT_TIMEOUT_US); 239 + } 240 + 241 + /* clear interrupt signal before initialization or reload */ 242 + static int starfive_wdt_int_clr(struct starfive_wdt *wdt) 243 + { 244 + int ret; 245 + 246 + if (wdt->variant->intclr_check) { 247 + ret = starfive_wdt_wait_int_free(wdt); 248 + if (ret) 249 + return dev_err_probe(wdt->wdd.parent, ret, 250 + "watchdog is not ready to clear interrupt.\n"); 251 + } 252 + writel(STARFIVE_WDT_INTCLR, wdt->base + wdt->variant->int_clr); 253 + 254 + return 0; 255 + } 256 + 257 + static inline void starfive_wdt_set_count(struct starfive_wdt *wdt, u32 val) 258 + { 259 + writel(val, wdt->base + wdt->variant->load); 260 + } 261 + 262 + static inline u32 starfive_wdt_get_count(struct starfive_wdt *wdt) 263 + { 264 + return readl(wdt->base + wdt->variant->value); 265 + } 266 + 267 + /* enable watchdog */ 268 + static inline void starfive_wdt_enable(struct starfive_wdt *wdt) 269 + { 270 + u32 val; 271 + 272 + val = readl(wdt->base + wdt->variant->enable); 273 + val |= STARFIVE_WDT_ENABLE << wdt->variant->en_shift; 274 + writel(val, wdt->base + wdt->variant->enable); 275 + } 276 + 277 + /* disable watchdog */ 278 + static inline void starfive_wdt_disable(struct starfive_wdt *wdt) 279 + { 280 + u32 val; 281 + 282 + val = readl(wdt->base + wdt->variant->enable); 283 + val &= ~(STARFIVE_WDT_ENABLE << wdt->variant->en_shift); 284 + writel(val, wdt->base + wdt->variant->enable); 285 + } 286 + 287 + static inline void starfive_wdt_set_reload_count(struct starfive_wdt *wdt, u32 count) 288 + { 289 + starfive_wdt_set_count(wdt, count); 290 + 291 + /* 7100 need set any value to reload register and could reload value to counter */ 292 + if (wdt->variant->reload) 293 + writel(0x1, wdt->base + wdt->variant->reload); 294 + } 295 + 296 + static unsigned int starfive_wdt_max_timeout(struct starfive_wdt *wdt) 297 + { 298 + if (wdt->variant->double_timeout) 299 + return DIV_ROUND_UP(STARFIVE_WDT_MAXCNT, (wdt->freq / 2)) - 1; 300 + 301 + return DIV_ROUND_UP(STARFIVE_WDT_MAXCNT, wdt->freq) - 1; 302 + } 303 + 304 + static unsigned int starfive_wdt_get_timeleft(struct watchdog_device *wdd) 305 + { 306 + struct starfive_wdt *wdt = watchdog_get_drvdata(wdd); 307 + u32 count; 308 + 309 + /* 310 + * If the watchdog takes twice timeout and set half count value, 311 + * timeleft value should add the count value before first timeout. 312 + */ 313 + count = starfive_wdt_get_count(wdt); 314 + if (wdt->variant->double_timeout && !starfive_wdt_raise_irq_status(wdt)) 315 + count += wdt->count; 316 + 317 + return starfive_wdt_ticks_to_sec(wdt, count); 318 + } 319 + 320 + static int starfive_wdt_keepalive(struct watchdog_device *wdd) 321 + { 322 + struct starfive_wdt *wdt = watchdog_get_drvdata(wdd); 323 + int ret; 324 + 325 + starfive_wdt_unlock(wdt); 326 + ret = starfive_wdt_int_clr(wdt); 327 + if (ret) 328 + goto exit; 329 + 330 + starfive_wdt_set_reload_count(wdt, wdt->count); 331 + 332 + exit: 333 + /* exit with releasing spinlock and locking registers */ 334 + starfive_wdt_lock(wdt); 335 + return ret; 336 + } 337 + 338 + static int starfive_wdt_start(struct starfive_wdt *wdt) 339 + { 340 + int ret; 341 + 342 + starfive_wdt_unlock(wdt); 343 + /* disable watchdog, to be safe */ 344 + starfive_wdt_disable(wdt); 345 + 346 + starfive_wdt_enable_reset(wdt); 347 + ret = starfive_wdt_int_clr(wdt); 348 + if (ret) 349 + goto exit; 350 + 351 + starfive_wdt_set_count(wdt, wdt->count); 352 + starfive_wdt_enable(wdt); 353 + 354 + exit: 355 + starfive_wdt_lock(wdt); 356 + return ret; 357 + } 358 + 359 + static void starfive_wdt_stop(struct starfive_wdt *wdt) 360 + { 361 + starfive_wdt_unlock(wdt); 362 + starfive_wdt_disable(wdt); 363 + starfive_wdt_lock(wdt); 364 + } 365 + 366 + static int starfive_wdt_pm_start(struct watchdog_device *wdd) 367 + { 368 + struct starfive_wdt *wdt = watchdog_get_drvdata(wdd); 369 + int ret = pm_runtime_get_sync(wdd->parent); 370 + 371 + if (ret < 0) 372 + return ret; 373 + 374 + return starfive_wdt_start(wdt); 375 + } 376 + 377 + static int starfive_wdt_pm_stop(struct watchdog_device *wdd) 378 + { 379 + struct starfive_wdt *wdt = watchdog_get_drvdata(wdd); 380 + 381 + starfive_wdt_stop(wdt); 382 + return pm_runtime_put_sync(wdd->parent); 383 + } 384 + 385 + static int starfive_wdt_set_timeout(struct watchdog_device *wdd, 386 + unsigned int timeout) 387 + { 388 + struct starfive_wdt *wdt = watchdog_get_drvdata(wdd); 389 + unsigned long count = timeout * wdt->freq; 390 + 391 + /* some watchdogs take two timeouts to reset */ 392 + if (wdt->variant->double_timeout) 393 + count /= 2; 394 + 395 + wdt->count = count; 396 + wdd->timeout = timeout; 397 + 398 + starfive_wdt_unlock(wdt); 399 + starfive_wdt_disable(wdt); 400 + starfive_wdt_set_reload_count(wdt, wdt->count); 401 + starfive_wdt_enable(wdt); 402 + starfive_wdt_lock(wdt); 403 + 404 + return 0; 405 + } 406 + 407 + #define STARFIVE_WDT_OPTIONS (WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE) 408 + 409 + static const struct watchdog_info starfive_wdt_info = { 410 + .options = STARFIVE_WDT_OPTIONS, 411 + .identity = "StarFive Watchdog", 412 + }; 413 + 414 + static const struct watchdog_ops starfive_wdt_ops = { 415 + .owner = THIS_MODULE, 416 + .start = starfive_wdt_pm_start, 417 + .stop = starfive_wdt_pm_stop, 418 + .ping = starfive_wdt_keepalive, 419 + .set_timeout = starfive_wdt_set_timeout, 420 + .get_timeleft = starfive_wdt_get_timeleft, 421 + }; 422 + 423 + static int starfive_wdt_probe(struct platform_device *pdev) 424 + { 425 + struct starfive_wdt *wdt; 426 + int ret; 427 + 428 + wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); 429 + if (!wdt) 430 + return -ENOMEM; 431 + 432 + wdt->base = devm_platform_ioremap_resource(pdev, 0); 433 + if (IS_ERR(wdt->base)) 434 + return dev_err_probe(&pdev->dev, PTR_ERR(wdt->base), "error mapping registers\n"); 435 + 436 + wdt->wdd.parent = &pdev->dev; 437 + ret = starfive_wdt_get_clock(wdt); 438 + if (ret) 439 + return ret; 440 + 441 + platform_set_drvdata(pdev, wdt); 442 + pm_runtime_enable(&pdev->dev); 443 + if (pm_runtime_enabled(&pdev->dev)) { 444 + ret = pm_runtime_get_sync(&pdev->dev); 445 + if (ret < 0) 446 + return ret; 447 + } else { 448 + /* runtime PM is disabled but clocks need to be enabled */ 449 + ret = starfive_wdt_enable_clock(wdt); 450 + if (ret) 451 + return ret; 452 + } 453 + 454 + ret = starfive_wdt_reset_init(&pdev->dev); 455 + if (ret) 456 + goto err_exit; 457 + 458 + watchdog_set_drvdata(&wdt->wdd, wdt); 459 + wdt->wdd.info = &starfive_wdt_info; 460 + wdt->wdd.ops = &starfive_wdt_ops; 461 + wdt->variant = of_device_get_match_data(&pdev->dev); 462 + spin_lock_init(&wdt->lock); 463 + 464 + wdt->freq = clk_get_rate(wdt->core_clk); 465 + if (!wdt->freq) { 466 + dev_err(&pdev->dev, "get clock rate failed.\n"); 467 + ret = -EINVAL; 468 + goto err_exit; 469 + } 470 + 471 + wdt->wdd.min_timeout = 1; 472 + wdt->wdd.max_timeout = starfive_wdt_max_timeout(wdt); 473 + wdt->wdd.timeout = STARFIVE_WDT_DEFAULT_TIME; 474 + watchdog_init_timeout(&wdt->wdd, heartbeat, &pdev->dev); 475 + starfive_wdt_set_timeout(&wdt->wdd, wdt->wdd.timeout); 476 + 477 + watchdog_set_nowayout(&wdt->wdd, nowayout); 478 + watchdog_stop_on_reboot(&wdt->wdd); 479 + watchdog_stop_on_unregister(&wdt->wdd); 480 + 481 + if (early_enable) { 482 + ret = starfive_wdt_start(wdt); 483 + if (ret) 484 + goto err_exit; 485 + set_bit(WDOG_HW_RUNNING, &wdt->wdd.status); 486 + } else { 487 + starfive_wdt_stop(wdt); 488 + } 489 + 490 + ret = watchdog_register_device(&wdt->wdd); 491 + if (ret) 492 + goto err_exit; 493 + 494 + if (!early_enable) 495 + return pm_runtime_put_sync(&pdev->dev); 496 + 497 + return 0; 498 + 499 + err_exit: 500 + starfive_wdt_disable_clock(wdt); 501 + pm_runtime_disable(&pdev->dev); 502 + 503 + return ret; 504 + } 505 + 506 + static int starfive_wdt_remove(struct platform_device *pdev) 507 + { 508 + struct starfive_wdt *wdt = platform_get_drvdata(pdev); 509 + 510 + starfive_wdt_stop(wdt); 511 + watchdog_unregister_device(&wdt->wdd); 512 + 513 + if (pm_runtime_enabled(&pdev->dev)) 514 + pm_runtime_disable(&pdev->dev); 515 + else 516 + /* disable clock without PM */ 517 + starfive_wdt_disable_clock(wdt); 518 + 519 + return 0; 520 + } 521 + 522 + static void starfive_wdt_shutdown(struct platform_device *pdev) 523 + { 524 + struct starfive_wdt *wdt = platform_get_drvdata(pdev); 525 + 526 + starfive_wdt_pm_stop(&wdt->wdd); 527 + } 528 + 529 + #ifdef CONFIG_PM_SLEEP 530 + static int starfive_wdt_suspend(struct device *dev) 531 + { 532 + struct starfive_wdt *wdt = dev_get_drvdata(dev); 533 + 534 + /* Save watchdog state, and turn it off. */ 535 + wdt->reload = starfive_wdt_get_count(wdt); 536 + 537 + /* Note that WTCNT doesn't need to be saved. */ 538 + starfive_wdt_stop(wdt); 539 + 540 + return pm_runtime_force_suspend(dev); 541 + } 542 + 543 + static int starfive_wdt_resume(struct device *dev) 544 + { 545 + struct starfive_wdt *wdt = dev_get_drvdata(dev); 546 + int ret; 547 + 548 + ret = pm_runtime_force_resume(dev); 549 + if (ret) 550 + return ret; 551 + 552 + starfive_wdt_unlock(wdt); 553 + /* Restore watchdog state. */ 554 + starfive_wdt_set_reload_count(wdt, wdt->reload); 555 + starfive_wdt_lock(wdt); 556 + 557 + return starfive_wdt_start(wdt); 558 + } 559 + #endif /* CONFIG_PM_SLEEP */ 560 + 561 + #ifdef CONFIG_PM 562 + static int starfive_wdt_runtime_suspend(struct device *dev) 563 + { 564 + struct starfive_wdt *wdt = dev_get_drvdata(dev); 565 + 566 + starfive_wdt_disable_clock(wdt); 567 + 568 + return 0; 569 + } 570 + 571 + static int starfive_wdt_runtime_resume(struct device *dev) 572 + { 573 + struct starfive_wdt *wdt = dev_get_drvdata(dev); 574 + 575 + return starfive_wdt_enable_clock(wdt); 576 + } 577 + #endif /* CONFIG_PM */ 578 + 579 + static const struct dev_pm_ops starfive_wdt_pm_ops = { 580 + SET_RUNTIME_PM_OPS(starfive_wdt_runtime_suspend, starfive_wdt_runtime_resume, NULL) 581 + SET_SYSTEM_SLEEP_PM_OPS(starfive_wdt_suspend, starfive_wdt_resume) 582 + }; 583 + 584 + static const struct of_device_id starfive_wdt_match[] = { 585 + { .compatible = "starfive,jh7100-wdt", .data = &starfive_wdt_jh7100_variant }, 586 + { .compatible = "starfive,jh7110-wdt", .data = &starfive_wdt_jh7110_variant }, 587 + { /* sentinel */ } 588 + }; 589 + MODULE_DEVICE_TABLE(of, starfive_wdt_match); 590 + 591 + static struct platform_driver starfive_wdt_driver = { 592 + .probe = starfive_wdt_probe, 593 + .remove = starfive_wdt_remove, 594 + .shutdown = starfive_wdt_shutdown, 595 + .driver = { 596 + .name = "starfive-wdt", 597 + .pm = &starfive_wdt_pm_ops, 598 + .of_match_table = of_match_ptr(starfive_wdt_match), 599 + }, 600 + }; 601 + module_platform_driver(starfive_wdt_driver); 602 + 603 + MODULE_AUTHOR("Xingyu Wu <xingyu.wu@starfivetech.com>"); 604 + MODULE_AUTHOR("Samin Guo <samin.guo@starfivetech.com>"); 605 + MODULE_DESCRIPTION("StarFive Watchdog Device Driver"); 606 + MODULE_LICENSE("GPL");