nic7018_wdt.c 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. * Copyright (C) 2016 National Instruments Corp.
  4. */
  5. #include <linux/acpi.h>
  6. #include <linux/bitops.h>
  7. #include <linux/device.h>
  8. #include <linux/io.h>
  9. #include <linux/module.h>
  10. #include <linux/platform_device.h>
  11. #include <linux/watchdog.h>
  12. #define LOCK 0xA5
  13. #define UNLOCK 0x5A
  14. #define WDT_CTRL_RESET_EN BIT(7)
  15. #define WDT_RELOAD_PORT_EN BIT(7)
  16. #define WDT_CTRL 1
  17. #define WDT_RELOAD_CTRL 2
  18. #define WDT_PRESET_PRESCALE 4
  19. #define WDT_REG_LOCK 5
  20. #define WDT_COUNT 6
  21. #define WDT_RELOAD_PORT 7
  22. #define WDT_MIN_TIMEOUT 1
  23. #define WDT_MAX_TIMEOUT 464
  24. #define WDT_DEFAULT_TIMEOUT 80
  25. #define WDT_MAX_COUNTER 15
  26. static unsigned int timeout;
  27. module_param(timeout, uint, 0);
  28. MODULE_PARM_DESC(timeout,
  29. "Watchdog timeout in seconds. (default="
  30. __MODULE_STRING(WDT_DEFAULT_TIMEOUT) ")");
  31. static bool nowayout = WATCHDOG_NOWAYOUT;
  32. module_param(nowayout, bool, 0);
  33. MODULE_PARM_DESC(nowayout,
  34. "Watchdog cannot be stopped once started. (default="
  35. __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
  36. struct nic7018_wdt {
  37. u16 io_base;
  38. u32 period;
  39. struct watchdog_device wdd;
  40. };
  41. struct nic7018_config {
  42. u32 period;
  43. u8 divider;
  44. };
  45. static const struct nic7018_config nic7018_configs[] = {
  46. { 2, 4 },
  47. { 32, 5 },
  48. };
  49. static inline u32 nic7018_timeout(u32 period, u8 counter)
  50. {
  51. return period * counter - period / 2;
  52. }
  53. static const struct nic7018_config *nic7018_get_config(u32 timeout,
  54. u8 *counter)
  55. {
  56. const struct nic7018_config *config;
  57. u8 count;
  58. if (timeout < 30 && timeout != 16) {
  59. config = &nic7018_configs[0];
  60. count = timeout / 2 + 1;
  61. } else {
  62. config = &nic7018_configs[1];
  63. count = DIV_ROUND_UP(timeout + 16, 32);
  64. if (count > WDT_MAX_COUNTER)
  65. count = WDT_MAX_COUNTER;
  66. }
  67. *counter = count;
  68. return config;
  69. }
  70. static int nic7018_set_timeout(struct watchdog_device *wdd,
  71. unsigned int timeout)
  72. {
  73. struct nic7018_wdt *wdt = watchdog_get_drvdata(wdd);
  74. const struct nic7018_config *config;
  75. u8 counter;
  76. config = nic7018_get_config(timeout, &counter);
  77. outb(counter << 4 | config->divider,
  78. wdt->io_base + WDT_PRESET_PRESCALE);
  79. wdd->timeout = nic7018_timeout(config->period, counter);
  80. wdt->period = config->period;
  81. return 0;
  82. }
  83. static int nic7018_start(struct watchdog_device *wdd)
  84. {
  85. struct nic7018_wdt *wdt = watchdog_get_drvdata(wdd);
  86. u8 control;
  87. nic7018_set_timeout(wdd, wdd->timeout);
  88. control = inb(wdt->io_base + WDT_RELOAD_CTRL);
  89. outb(control | WDT_RELOAD_PORT_EN, wdt->io_base + WDT_RELOAD_CTRL);
  90. outb(1, wdt->io_base + WDT_RELOAD_PORT);
  91. control = inb(wdt->io_base + WDT_CTRL);
  92. outb(control | WDT_CTRL_RESET_EN, wdt->io_base + WDT_CTRL);
  93. return 0;
  94. }
  95. static int nic7018_stop(struct watchdog_device *wdd)
  96. {
  97. struct nic7018_wdt *wdt = watchdog_get_drvdata(wdd);
  98. outb(0, wdt->io_base + WDT_CTRL);
  99. outb(0, wdt->io_base + WDT_RELOAD_CTRL);
  100. outb(0xF0, wdt->io_base + WDT_PRESET_PRESCALE);
  101. return 0;
  102. }
  103. static int nic7018_ping(struct watchdog_device *wdd)
  104. {
  105. struct nic7018_wdt *wdt = watchdog_get_drvdata(wdd);
  106. outb(1, wdt->io_base + WDT_RELOAD_PORT);
  107. return 0;
  108. }
  109. static unsigned int nic7018_get_timeleft(struct watchdog_device *wdd)
  110. {
  111. struct nic7018_wdt *wdt = watchdog_get_drvdata(wdd);
  112. u8 count;
  113. count = inb(wdt->io_base + WDT_COUNT) & 0xF;
  114. if (!count)
  115. return 0;
  116. return nic7018_timeout(wdt->period, count);
  117. }
  118. static const struct watchdog_info nic7018_wdd_info = {
  119. .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
  120. .identity = "NIC7018 Watchdog",
  121. };
  122. static const struct watchdog_ops nic7018_wdd_ops = {
  123. .owner = THIS_MODULE,
  124. .start = nic7018_start,
  125. .stop = nic7018_stop,
  126. .ping = nic7018_ping,
  127. .set_timeout = nic7018_set_timeout,
  128. .get_timeleft = nic7018_get_timeleft,
  129. };
  130. static int nic7018_probe(struct platform_device *pdev)
  131. {
  132. struct device *dev = &pdev->dev;
  133. struct watchdog_device *wdd;
  134. struct nic7018_wdt *wdt;
  135. struct resource *io_rc;
  136. int ret;
  137. wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
  138. if (!wdt)
  139. return -ENOMEM;
  140. platform_set_drvdata(pdev, wdt);
  141. io_rc = platform_get_resource(pdev, IORESOURCE_IO, 0);
  142. if (!io_rc) {
  143. dev_err(dev, "missing IO resources\n");
  144. return -EINVAL;
  145. }
  146. if (!devm_request_region(dev, io_rc->start, resource_size(io_rc),
  147. KBUILD_MODNAME)) {
  148. dev_err(dev, "failed to get IO region\n");
  149. return -EBUSY;
  150. }
  151. wdt->io_base = io_rc->start;
  152. wdd = &wdt->wdd;
  153. wdd->info = &nic7018_wdd_info;
  154. wdd->ops = &nic7018_wdd_ops;
  155. wdd->min_timeout = WDT_MIN_TIMEOUT;
  156. wdd->max_timeout = WDT_MAX_TIMEOUT;
  157. wdd->timeout = WDT_DEFAULT_TIMEOUT;
  158. wdd->parent = dev;
  159. watchdog_set_drvdata(wdd, wdt);
  160. watchdog_set_nowayout(wdd, nowayout);
  161. watchdog_init_timeout(wdd, timeout, dev);
  162. /* Unlock WDT register */
  163. outb(UNLOCK, wdt->io_base + WDT_REG_LOCK);
  164. ret = watchdog_register_device(wdd);
  165. if (ret) {
  166. outb(LOCK, wdt->io_base + WDT_REG_LOCK);
  167. return ret;
  168. }
  169. dev_dbg(dev, "io_base=0x%04X, timeout=%d, nowayout=%d\n",
  170. wdt->io_base, timeout, nowayout);
  171. return 0;
  172. }
  173. static int nic7018_remove(struct platform_device *pdev)
  174. {
  175. struct nic7018_wdt *wdt = platform_get_drvdata(pdev);
  176. watchdog_unregister_device(&wdt->wdd);
  177. /* Lock WDT register */
  178. outb(LOCK, wdt->io_base + WDT_REG_LOCK);
  179. return 0;
  180. }
  181. static const struct acpi_device_id nic7018_device_ids[] = {
  182. {"NIC7018", 0},
  183. {"", 0},
  184. };
  185. MODULE_DEVICE_TABLE(acpi, nic7018_device_ids);
  186. static struct platform_driver watchdog_driver = {
  187. .probe = nic7018_probe,
  188. .remove = nic7018_remove,
  189. .driver = {
  190. .name = KBUILD_MODNAME,
  191. .acpi_match_table = ACPI_PTR(nic7018_device_ids),
  192. },
  193. };
  194. module_platform_driver(watchdog_driver);
  195. MODULE_DESCRIPTION("National Instruments NIC7018 Watchdog driver");
  196. MODULE_AUTHOR("Hui Chun Ong <hui.chun.ong@ni.com>");
  197. MODULE_LICENSE("GPL");