intel_oc_wdt.c 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Intel OC Watchdog driver
  4. *
  5. * Copyright (C) 2025, Siemens
  6. * Author: Diogo Ivo <diogo.ivo@siemens.com>
  7. */
  8. #define DRV_NAME "intel_oc_wdt"
  9. #include <linux/acpi.h>
  10. #include <linux/bits.h>
  11. #include <linux/io.h>
  12. #include <linux/module.h>
  13. #include <linux/moduleparam.h>
  14. #include <linux/platform_device.h>
  15. #include <linux/watchdog.h>
  16. #define INTEL_OC_WDT_TOV GENMASK(9, 0)
  17. #define INTEL_OC_WDT_MIN_TOV 1
  18. #define INTEL_OC_WDT_MAX_TOV 1024
  19. #define INTEL_OC_WDT_DEF_TOV 60
  20. /*
  21. * One-time writable lock bit. If set forbids
  22. * modification of itself, _TOV and _EN until
  23. * next reboot.
  24. */
  25. #define INTEL_OC_WDT_CTL_LCK BIT(12)
  26. #define INTEL_OC_WDT_EN BIT(14)
  27. #define INTEL_OC_WDT_NO_ICCSURV_STS BIT(24)
  28. #define INTEL_OC_WDT_ICCSURV_STS BIT(25)
  29. #define INTEL_OC_WDT_RLD BIT(31)
  30. #define INTEL_OC_WDT_STS_BITS (INTEL_OC_WDT_NO_ICCSURV_STS | \
  31. INTEL_OC_WDT_ICCSURV_STS)
  32. #define INTEL_OC_WDT_CTRL_REG(wdt) ((wdt)->ctrl_res->start)
  33. struct intel_oc_wdt {
  34. struct watchdog_device wdd;
  35. struct resource *ctrl_res;
  36. struct watchdog_info info;
  37. bool locked;
  38. };
  39. static int heartbeat;
  40. module_param(heartbeat, uint, 0);
  41. MODULE_PARM_DESC(heartbeat, "Watchdog heartbeats in seconds. (default="
  42. __MODULE_STRING(WDT_HEARTBEAT) ")");
  43. static bool nowayout = WATCHDOG_NOWAYOUT;
  44. module_param(nowayout, bool, 0);
  45. MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
  46. __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
  47. static int intel_oc_wdt_start(struct watchdog_device *wdd)
  48. {
  49. struct intel_oc_wdt *oc_wdt = watchdog_get_drvdata(wdd);
  50. if (oc_wdt->locked)
  51. return 0;
  52. outl(inl(INTEL_OC_WDT_CTRL_REG(oc_wdt)) | INTEL_OC_WDT_EN,
  53. INTEL_OC_WDT_CTRL_REG(oc_wdt));
  54. return 0;
  55. }
  56. static int intel_oc_wdt_stop(struct watchdog_device *wdd)
  57. {
  58. struct intel_oc_wdt *oc_wdt = watchdog_get_drvdata(wdd);
  59. outl(inl(INTEL_OC_WDT_CTRL_REG(oc_wdt)) & ~INTEL_OC_WDT_EN,
  60. INTEL_OC_WDT_CTRL_REG(oc_wdt));
  61. return 0;
  62. }
  63. static int intel_oc_wdt_ping(struct watchdog_device *wdd)
  64. {
  65. struct intel_oc_wdt *oc_wdt = watchdog_get_drvdata(wdd);
  66. outl(inl(INTEL_OC_WDT_CTRL_REG(oc_wdt)) | INTEL_OC_WDT_RLD,
  67. INTEL_OC_WDT_CTRL_REG(oc_wdt));
  68. return 0;
  69. }
  70. static int intel_oc_wdt_set_timeout(struct watchdog_device *wdd,
  71. unsigned int t)
  72. {
  73. struct intel_oc_wdt *oc_wdt = watchdog_get_drvdata(wdd);
  74. outl((inl(INTEL_OC_WDT_CTRL_REG(oc_wdt)) & ~INTEL_OC_WDT_TOV) | (t - 1),
  75. INTEL_OC_WDT_CTRL_REG(oc_wdt));
  76. wdd->timeout = t;
  77. return 0;
  78. }
  79. static const struct watchdog_info intel_oc_wdt_info = {
  80. .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING,
  81. .identity = DRV_NAME,
  82. };
  83. static const struct watchdog_ops intel_oc_wdt_ops = {
  84. .owner = THIS_MODULE,
  85. .start = intel_oc_wdt_start,
  86. .stop = intel_oc_wdt_stop,
  87. .ping = intel_oc_wdt_ping,
  88. .set_timeout = intel_oc_wdt_set_timeout,
  89. };
  90. static int intel_oc_wdt_setup(struct intel_oc_wdt *oc_wdt)
  91. {
  92. unsigned long val;
  93. val = inl(INTEL_OC_WDT_CTRL_REG(oc_wdt));
  94. if (val & INTEL_OC_WDT_STS_BITS)
  95. oc_wdt->wdd.bootstatus |= WDIOF_CARDRESET;
  96. oc_wdt->locked = !!(val & INTEL_OC_WDT_CTL_LCK);
  97. if (val & INTEL_OC_WDT_EN) {
  98. /*
  99. * No need to issue a ping here to "commit" the new timeout
  100. * value to hardware as the watchdog core schedules one
  101. * immediately when registering the watchdog.
  102. */
  103. set_bit(WDOG_HW_RUNNING, &oc_wdt->wdd.status);
  104. if (oc_wdt->locked) {
  105. /*
  106. * Set nowayout unconditionally as we cannot stop
  107. * the watchdog.
  108. */
  109. nowayout = true;
  110. /*
  111. * If we are locked read the current timeout value
  112. * and inform the core we can't change it.
  113. */
  114. oc_wdt->wdd.timeout = (val & INTEL_OC_WDT_TOV) + 1;
  115. oc_wdt->info.options &= ~WDIOF_SETTIMEOUT;
  116. dev_info(oc_wdt->wdd.parent,
  117. "Register access locked, heartbeat fixed at: %u s\n",
  118. oc_wdt->wdd.timeout);
  119. }
  120. } else if (oc_wdt->locked) {
  121. /*
  122. * In case the watchdog is disabled and locked there
  123. * is nothing we can do with it so just fail probing.
  124. */
  125. return -EACCES;
  126. }
  127. val &= ~INTEL_OC_WDT_TOV;
  128. outl(val | (oc_wdt->wdd.timeout - 1), INTEL_OC_WDT_CTRL_REG(oc_wdt));
  129. return 0;
  130. }
  131. static int intel_oc_wdt_probe(struct platform_device *pdev)
  132. {
  133. struct device *dev = &pdev->dev;
  134. struct intel_oc_wdt *oc_wdt;
  135. struct watchdog_device *wdd;
  136. int ret;
  137. oc_wdt = devm_kzalloc(&pdev->dev, sizeof(*oc_wdt), GFP_KERNEL);
  138. if (!oc_wdt)
  139. return -ENOMEM;
  140. oc_wdt->ctrl_res = platform_get_resource(pdev, IORESOURCE_IO, 0);
  141. if (!oc_wdt->ctrl_res) {
  142. dev_err(&pdev->dev, "missing I/O resource\n");
  143. return -ENODEV;
  144. }
  145. if (!devm_request_region(&pdev->dev, oc_wdt->ctrl_res->start,
  146. resource_size(oc_wdt->ctrl_res), pdev->name)) {
  147. dev_err(dev, "resource %pR already in use, device disabled\n",
  148. oc_wdt->ctrl_res);
  149. return -EBUSY;
  150. }
  151. wdd = &oc_wdt->wdd;
  152. wdd->min_timeout = INTEL_OC_WDT_MIN_TOV;
  153. wdd->max_timeout = INTEL_OC_WDT_MAX_TOV;
  154. wdd->timeout = INTEL_OC_WDT_DEF_TOV;
  155. oc_wdt->info = intel_oc_wdt_info;
  156. wdd->info = &oc_wdt->info;
  157. wdd->ops = &intel_oc_wdt_ops;
  158. wdd->parent = dev;
  159. watchdog_init_timeout(wdd, heartbeat, dev);
  160. ret = intel_oc_wdt_setup(oc_wdt);
  161. if (ret)
  162. return ret;
  163. watchdog_set_drvdata(wdd, oc_wdt);
  164. watchdog_set_nowayout(wdd, nowayout);
  165. watchdog_stop_on_reboot(wdd);
  166. watchdog_stop_on_unregister(wdd);
  167. return devm_watchdog_register_device(dev, wdd);
  168. }
  169. static const struct acpi_device_id intel_oc_wdt_match[] = {
  170. { "INT3F0D" },
  171. { "INTC1099" },
  172. { },
  173. };
  174. MODULE_DEVICE_TABLE(acpi, intel_oc_wdt_match);
  175. static struct platform_driver intel_oc_wdt_platform_driver = {
  176. .driver = {
  177. .name = DRV_NAME,
  178. .acpi_match_table = intel_oc_wdt_match,
  179. },
  180. .probe = intel_oc_wdt_probe,
  181. };
  182. module_platform_driver(intel_oc_wdt_platform_driver);
  183. MODULE_AUTHOR("Diogo Ivo <diogo.ivo@siemens.com>");
  184. MODULE_LICENSE("GPL");
  185. MODULE_DESCRIPTION("Intel OC Watchdog driver");