bd70528_wdt.c 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. // SPDX-License-Identifier: GPL-2.0
  2. // Copyright (C) 2018 ROHM Semiconductors
  3. // ROHM BD70528MWV watchdog driver
  4. #include <linux/bcd.h>
  5. #include <linux/kernel.h>
  6. #include <linux/mfd/rohm-bd70528.h>
  7. #include <linux/module.h>
  8. #include <linux/of.h>
  9. #include <linux/platform_device.h>
  10. #include <linux/regmap.h>
  11. #include <linux/watchdog.h>
  12. /*
  13. * Max time we can set is 1 hour, 59 minutes and 59 seconds
  14. * and Minimum time is 1 second
  15. */
  16. #define WDT_MAX_MS ((2 * 60 * 60 - 1) * 1000)
  17. #define WDT_MIN_MS 1000
  18. #define DEFAULT_TIMEOUT 60
  19. #define WD_CTRL_MAGIC1 0x55
  20. #define WD_CTRL_MAGIC2 0xAA
  21. struct wdtbd70528 {
  22. struct device *dev;
  23. struct regmap *regmap;
  24. struct rohm_regmap_dev *mfd;
  25. struct watchdog_device wdt;
  26. };
  27. /**
  28. * bd70528_wdt_set - arm or disarm watchdog timer
  29. *
  30. * @data: device data for the PMIC instance we want to operate on
  31. * @enable: new state of WDT. zero to disable, non zero to enable
  32. * @old_state: previous state of WDT will be filled here
  33. *
  34. * Arm or disarm WDT on BD70528 PMIC. Expected to be called only by
  35. * BD70528 RTC and BD70528 WDT drivers. The rtc_timer_lock must be taken
  36. * by calling bd70528_wdt_lock before calling bd70528_wdt_set.
  37. */
  38. int bd70528_wdt_set(struct rohm_regmap_dev *data, int enable, int *old_state)
  39. {
  40. int ret, i;
  41. unsigned int tmp;
  42. struct bd70528_data *bd70528 = container_of(data, struct bd70528_data,
  43. chip);
  44. u8 wd_ctrl_arr[3] = { WD_CTRL_MAGIC1, WD_CTRL_MAGIC2, 0 };
  45. u8 *wd_ctrl = &wd_ctrl_arr[2];
  46. ret = regmap_read(bd70528->chip.regmap, BD70528_REG_WDT_CTRL, &tmp);
  47. if (ret)
  48. return ret;
  49. *wd_ctrl = (u8)tmp;
  50. if (old_state) {
  51. if (*wd_ctrl & BD70528_MASK_WDT_EN)
  52. *old_state |= BD70528_WDT_STATE_BIT;
  53. else
  54. *old_state &= ~BD70528_WDT_STATE_BIT;
  55. if ((!enable) == (!(*old_state & BD70528_WDT_STATE_BIT)))
  56. return 0;
  57. }
  58. if (enable) {
  59. if (*wd_ctrl & BD70528_MASK_WDT_EN)
  60. return 0;
  61. *wd_ctrl |= BD70528_MASK_WDT_EN;
  62. } else {
  63. if (*wd_ctrl & BD70528_MASK_WDT_EN)
  64. *wd_ctrl &= ~BD70528_MASK_WDT_EN;
  65. else
  66. return 0;
  67. }
  68. for (i = 0; i < 3; i++) {
  69. ret = regmap_write(bd70528->chip.regmap, BD70528_REG_WDT_CTRL,
  70. wd_ctrl_arr[i]);
  71. if (ret)
  72. return ret;
  73. }
  74. ret = regmap_read(bd70528->chip.regmap, BD70528_REG_WDT_CTRL, &tmp);
  75. if ((tmp & BD70528_MASK_WDT_EN) != (*wd_ctrl & BD70528_MASK_WDT_EN)) {
  76. dev_err(bd70528->chip.dev,
  77. "Watchdog ctrl mismatch (hw) 0x%x (set) 0x%x\n",
  78. tmp, *wd_ctrl);
  79. ret = -EIO;
  80. }
  81. return ret;
  82. }
  83. EXPORT_SYMBOL(bd70528_wdt_set);
  84. /**
  85. * bd70528_wdt_lock - take WDT lock
  86. *
  87. * @data: device data for the PMIC instance we want to operate on
  88. *
  89. * Lock WDT for arming/disarming in order to avoid race condition caused
  90. * by WDT state changes initiated by WDT and RTC drivers.
  91. */
  92. void bd70528_wdt_lock(struct rohm_regmap_dev *data)
  93. {
  94. struct bd70528_data *bd70528 = container_of(data, struct bd70528_data,
  95. chip);
  96. mutex_lock(&bd70528->rtc_timer_lock);
  97. }
  98. EXPORT_SYMBOL(bd70528_wdt_lock);
  99. /**
  100. * bd70528_wdt_unlock - unlock WDT lock
  101. *
  102. * @data: device data for the PMIC instance we want to operate on
  103. *
  104. * Unlock WDT lock which has previously been taken by call to
  105. * bd70528_wdt_lock.
  106. */
  107. void bd70528_wdt_unlock(struct rohm_regmap_dev *data)
  108. {
  109. struct bd70528_data *bd70528 = container_of(data, struct bd70528_data,
  110. chip);
  111. mutex_unlock(&bd70528->rtc_timer_lock);
  112. }
  113. EXPORT_SYMBOL(bd70528_wdt_unlock);
  114. static int bd70528_wdt_set_locked(struct wdtbd70528 *w, int enable)
  115. {
  116. return bd70528_wdt_set(w->mfd, enable, NULL);
  117. }
  118. static int bd70528_wdt_change(struct wdtbd70528 *w, int enable)
  119. {
  120. int ret;
  121. bd70528_wdt_lock(w->mfd);
  122. ret = bd70528_wdt_set_locked(w, enable);
  123. bd70528_wdt_unlock(w->mfd);
  124. return ret;
  125. }
  126. static int bd70528_wdt_start(struct watchdog_device *wdt)
  127. {
  128. struct wdtbd70528 *w = watchdog_get_drvdata(wdt);
  129. dev_dbg(w->dev, "WDT ping...\n");
  130. return bd70528_wdt_change(w, 1);
  131. }
  132. static int bd70528_wdt_stop(struct watchdog_device *wdt)
  133. {
  134. struct wdtbd70528 *w = watchdog_get_drvdata(wdt);
  135. dev_dbg(w->dev, "WDT stopping...\n");
  136. return bd70528_wdt_change(w, 0);
  137. }
  138. static int bd70528_wdt_set_timeout(struct watchdog_device *wdt,
  139. unsigned int timeout)
  140. {
  141. unsigned int hours;
  142. unsigned int minutes;
  143. unsigned int seconds;
  144. int ret;
  145. struct wdtbd70528 *w = watchdog_get_drvdata(wdt);
  146. seconds = timeout;
  147. hours = timeout / (60 * 60);
  148. /* Maximum timeout is 1h 59m 59s => hours is 1 or 0 */
  149. if (hours)
  150. seconds -= (60 * 60);
  151. minutes = seconds / 60;
  152. seconds = seconds % 60;
  153. bd70528_wdt_lock(w->mfd);
  154. ret = bd70528_wdt_set_locked(w, 0);
  155. if (ret)
  156. goto out_unlock;
  157. ret = regmap_update_bits(w->regmap, BD70528_REG_WDT_HOUR,
  158. BD70528_MASK_WDT_HOUR, hours);
  159. if (ret) {
  160. dev_err(w->dev, "Failed to set WDT hours\n");
  161. goto out_en_unlock;
  162. }
  163. ret = regmap_update_bits(w->regmap, BD70528_REG_WDT_MINUTE,
  164. BD70528_MASK_WDT_MINUTE, bin2bcd(minutes));
  165. if (ret) {
  166. dev_err(w->dev, "Failed to set WDT minutes\n");
  167. goto out_en_unlock;
  168. }
  169. ret = regmap_update_bits(w->regmap, BD70528_REG_WDT_SEC,
  170. BD70528_MASK_WDT_SEC, bin2bcd(seconds));
  171. if (ret)
  172. dev_err(w->dev, "Failed to set WDT seconds\n");
  173. else
  174. dev_dbg(w->dev, "WDT tmo set to %u\n", timeout);
  175. out_en_unlock:
  176. ret = bd70528_wdt_set_locked(w, 1);
  177. out_unlock:
  178. bd70528_wdt_unlock(w->mfd);
  179. return ret;
  180. }
  181. static const struct watchdog_info bd70528_wdt_info = {
  182. .identity = "bd70528-wdt",
  183. .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
  184. };
  185. static const struct watchdog_ops bd70528_wdt_ops = {
  186. .start = bd70528_wdt_start,
  187. .stop = bd70528_wdt_stop,
  188. .set_timeout = bd70528_wdt_set_timeout,
  189. };
  190. static int bd70528_wdt_probe(struct platform_device *pdev)
  191. {
  192. struct rohm_regmap_dev *bd70528;
  193. struct wdtbd70528 *w;
  194. int ret;
  195. unsigned int reg;
  196. bd70528 = dev_get_drvdata(pdev->dev.parent);
  197. if (!bd70528) {
  198. dev_err(&pdev->dev, "No MFD driver data\n");
  199. return -EINVAL;
  200. }
  201. w = devm_kzalloc(&pdev->dev, sizeof(*w), GFP_KERNEL);
  202. if (!w)
  203. return -ENOMEM;
  204. w->regmap = bd70528->regmap;
  205. w->mfd = bd70528;
  206. w->dev = &pdev->dev;
  207. w->wdt.info = &bd70528_wdt_info;
  208. w->wdt.ops = &bd70528_wdt_ops;
  209. w->wdt.min_hw_heartbeat_ms = WDT_MIN_MS;
  210. w->wdt.max_hw_heartbeat_ms = WDT_MAX_MS;
  211. w->wdt.parent = pdev->dev.parent;
  212. w->wdt.timeout = DEFAULT_TIMEOUT;
  213. watchdog_set_drvdata(&w->wdt, w);
  214. watchdog_init_timeout(&w->wdt, 0, pdev->dev.parent);
  215. ret = bd70528_wdt_set_timeout(&w->wdt, w->wdt.timeout);
  216. if (ret) {
  217. dev_err(&pdev->dev, "Failed to set the watchdog timeout\n");
  218. return ret;
  219. }
  220. bd70528_wdt_lock(w->mfd);
  221. ret = regmap_read(w->regmap, BD70528_REG_WDT_CTRL, &reg);
  222. bd70528_wdt_unlock(w->mfd);
  223. if (ret) {
  224. dev_err(&pdev->dev, "Failed to get the watchdog state\n");
  225. return ret;
  226. }
  227. if (reg & BD70528_MASK_WDT_EN) {
  228. dev_dbg(&pdev->dev, "watchdog was running during probe\n");
  229. set_bit(WDOG_HW_RUNNING, &w->wdt.status);
  230. }
  231. ret = devm_watchdog_register_device(&pdev->dev, &w->wdt);
  232. if (ret < 0)
  233. dev_err(&pdev->dev, "watchdog registration failed: %d\n", ret);
  234. return ret;
  235. }
  236. static struct platform_driver bd70528_wdt = {
  237. .driver = {
  238. .name = "bd70528-wdt"
  239. },
  240. .probe = bd70528_wdt_probe,
  241. };
  242. module_platform_driver(bd70528_wdt);
  243. MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>");
  244. MODULE_DESCRIPTION("BD70528 watchdog driver");
  245. MODULE_LICENSE("GPL");
  246. MODULE_ALIAS("platform:bd70528-wdt");