0105-drivers-watchdog-Add-StarFive-Watchdog-driver.patch 19 KB

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