retu_wdt.c 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. // SPDX-License-Identifier: GPL-2.0+
  2. /*
  3. * Retu watchdog driver
  4. *
  5. * Copyright (C) 2004, 2005 Nokia Corporation
  6. *
  7. * Based on code written by Amit Kucheria and Michael Buesch.
  8. * Rewritten by Aaro Koskinen.
  9. */
  10. #include <linux/devm-helpers.h>
  11. #include <linux/slab.h>
  12. #include <linux/errno.h>
  13. #include <linux/device.h>
  14. #include <linux/kernel.h>
  15. #include <linux/module.h>
  16. #include <linux/mfd/retu.h>
  17. #include <linux/watchdog.h>
  18. #include <linux/platform_device.h>
  19. /* Watchdog timer values in seconds */
  20. #define RETU_WDT_MAX_TIMER 63
  21. struct retu_wdt_dev {
  22. struct retu_dev *rdev;
  23. struct device *dev;
  24. struct delayed_work ping_work;
  25. };
  26. /*
  27. * Since Retu watchdog cannot be disabled in hardware, we must kick it
  28. * with a timer until userspace watchdog software takes over. If
  29. * CONFIG_WATCHDOG_NOWAYOUT is set, we never start the feeding.
  30. */
  31. static void retu_wdt_ping_enable(struct retu_wdt_dev *wdev)
  32. {
  33. retu_write(wdev->rdev, RETU_REG_WATCHDOG, RETU_WDT_MAX_TIMER);
  34. schedule_delayed_work(&wdev->ping_work,
  35. round_jiffies_relative(RETU_WDT_MAX_TIMER * HZ / 2));
  36. }
  37. static void retu_wdt_ping_disable(struct retu_wdt_dev *wdev)
  38. {
  39. retu_write(wdev->rdev, RETU_REG_WATCHDOG, RETU_WDT_MAX_TIMER);
  40. cancel_delayed_work_sync(&wdev->ping_work);
  41. }
  42. static void retu_wdt_ping_work(struct work_struct *work)
  43. {
  44. struct retu_wdt_dev *wdev = container_of(to_delayed_work(work),
  45. struct retu_wdt_dev, ping_work);
  46. retu_wdt_ping_enable(wdev);
  47. }
  48. static int retu_wdt_start(struct watchdog_device *wdog)
  49. {
  50. struct retu_wdt_dev *wdev = watchdog_get_drvdata(wdog);
  51. retu_wdt_ping_disable(wdev);
  52. return retu_write(wdev->rdev, RETU_REG_WATCHDOG, wdog->timeout);
  53. }
  54. static int retu_wdt_stop(struct watchdog_device *wdog)
  55. {
  56. struct retu_wdt_dev *wdev = watchdog_get_drvdata(wdog);
  57. retu_wdt_ping_enable(wdev);
  58. return 0;
  59. }
  60. static int retu_wdt_ping(struct watchdog_device *wdog)
  61. {
  62. struct retu_wdt_dev *wdev = watchdog_get_drvdata(wdog);
  63. return retu_write(wdev->rdev, RETU_REG_WATCHDOG, wdog->timeout);
  64. }
  65. static int retu_wdt_set_timeout(struct watchdog_device *wdog,
  66. unsigned int timeout)
  67. {
  68. struct retu_wdt_dev *wdev = watchdog_get_drvdata(wdog);
  69. wdog->timeout = timeout;
  70. return retu_write(wdev->rdev, RETU_REG_WATCHDOG, wdog->timeout);
  71. }
  72. static const struct watchdog_info retu_wdt_info = {
  73. .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING,
  74. .identity = "Retu watchdog",
  75. };
  76. static const struct watchdog_ops retu_wdt_ops = {
  77. .owner = THIS_MODULE,
  78. .start = retu_wdt_start,
  79. .stop = retu_wdt_stop,
  80. .ping = retu_wdt_ping,
  81. .set_timeout = retu_wdt_set_timeout,
  82. };
  83. static int retu_wdt_probe(struct platform_device *pdev)
  84. {
  85. struct retu_dev *rdev = dev_get_drvdata(pdev->dev.parent);
  86. bool nowayout = WATCHDOG_NOWAYOUT;
  87. struct watchdog_device *retu_wdt;
  88. struct retu_wdt_dev *wdev;
  89. int ret;
  90. retu_wdt = devm_kzalloc(&pdev->dev, sizeof(*retu_wdt), GFP_KERNEL);
  91. if (!retu_wdt)
  92. return -ENOMEM;
  93. wdev = devm_kzalloc(&pdev->dev, sizeof(*wdev), GFP_KERNEL);
  94. if (!wdev)
  95. return -ENOMEM;
  96. retu_wdt->info = &retu_wdt_info;
  97. retu_wdt->ops = &retu_wdt_ops;
  98. retu_wdt->timeout = RETU_WDT_MAX_TIMER;
  99. retu_wdt->min_timeout = 0;
  100. retu_wdt->max_timeout = RETU_WDT_MAX_TIMER;
  101. retu_wdt->parent = &pdev->dev;
  102. watchdog_set_drvdata(retu_wdt, wdev);
  103. watchdog_set_nowayout(retu_wdt, nowayout);
  104. wdev->rdev = rdev;
  105. wdev->dev = &pdev->dev;
  106. ret = devm_delayed_work_autocancel(&pdev->dev, &wdev->ping_work,
  107. retu_wdt_ping_work);
  108. if (ret)
  109. return ret;
  110. ret = devm_watchdog_register_device(&pdev->dev, retu_wdt);
  111. if (ret < 0)
  112. return ret;
  113. if (nowayout)
  114. retu_wdt_ping(retu_wdt);
  115. else
  116. retu_wdt_ping_enable(wdev);
  117. return 0;
  118. }
  119. static struct platform_driver retu_wdt_driver = {
  120. .probe = retu_wdt_probe,
  121. .driver = {
  122. .name = "retu-wdt",
  123. },
  124. };
  125. module_platform_driver(retu_wdt_driver);
  126. MODULE_ALIAS("platform:retu-wdt");
  127. MODULE_DESCRIPTION("Retu watchdog");
  128. MODULE_AUTHOR("Amit Kucheria");
  129. MODULE_AUTHOR("Aaro Koskinen <[email protected]>");
  130. MODULE_LICENSE("GPL");