intel-mid_wdt.c 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * intel-mid_wdt: generic Intel MID SCU watchdog driver
  4. *
  5. * Platforms supported so far:
  6. * - Merrifield only
  7. *
  8. * Copyright (C) 2014 Intel Corporation. All rights reserved.
  9. * Contact: David Cohen <david.a.cohen@linux.intel.com>
  10. */
  11. #include <linux/bitops.h>
  12. #include <linux/device.h>
  13. #include <linux/errno.h>
  14. #include <linux/interrupt.h>
  15. #include <linux/math.h>
  16. #include <linux/module.h>
  17. #include <linux/panic.h>
  18. #include <linux/platform_device.h>
  19. #include <linux/types.h>
  20. #include <linux/watchdog.h>
  21. #include <linux/platform_data/x86/intel-mid_wdt.h>
  22. #include <linux/platform_data/x86/intel_scu_ipc.h>
  23. #define IPC_WATCHDOG 0xf8
  24. #define MID_WDT_PRETIMEOUT 15
  25. #define MID_WDT_TIMEOUT_MIN (1 + MID_WDT_PRETIMEOUT)
  26. #define MID_WDT_TIMEOUT_MAX 170
  27. #define MID_WDT_DEFAULT_TIMEOUT 90
  28. /* SCU watchdog messages */
  29. enum {
  30. SCU_WATCHDOG_START = 0,
  31. SCU_WATCHDOG_STOP,
  32. SCU_WATCHDOG_KEEPALIVE,
  33. };
  34. struct mid_wdt {
  35. struct watchdog_device wd;
  36. struct device *dev;
  37. struct intel_scu_ipc_dev *scu;
  38. };
  39. static inline int
  40. wdt_command(struct mid_wdt *mid, int sub, const void *in, size_t inlen, size_t size)
  41. {
  42. struct intel_scu_ipc_dev *scu = mid->scu;
  43. return intel_scu_ipc_dev_command_with_size(scu, IPC_WATCHDOG, sub, in,
  44. inlen, size, NULL, 0);
  45. }
  46. static int wdt_start(struct watchdog_device *wd)
  47. {
  48. struct mid_wdt *mid = watchdog_get_drvdata(wd);
  49. int ret, in_size;
  50. int timeout = wd->timeout;
  51. struct ipc_wd_start {
  52. u32 pretimeout;
  53. u32 timeout;
  54. } ipc_wd_start = { timeout - MID_WDT_PRETIMEOUT, timeout };
  55. /*
  56. * SCU expects the input size for watchdog IPC to be 2 which is the
  57. * size of the structure in dwords. SCU IPC normally takes bytes
  58. * but this is a special case where we specify size to be different
  59. * than inlen.
  60. */
  61. in_size = DIV_ROUND_UP(sizeof(ipc_wd_start), 4);
  62. ret = wdt_command(mid, SCU_WATCHDOG_START, &ipc_wd_start,
  63. sizeof(ipc_wd_start), in_size);
  64. if (ret)
  65. dev_crit(mid->dev, "error starting watchdog: %d\n", ret);
  66. return ret;
  67. }
  68. static int wdt_ping(struct watchdog_device *wd)
  69. {
  70. struct mid_wdt *mid = watchdog_get_drvdata(wd);
  71. int ret;
  72. ret = wdt_command(mid, SCU_WATCHDOG_KEEPALIVE, NULL, 0, 0);
  73. if (ret)
  74. dev_crit(mid->dev, "Error executing keepalive: %d\n", ret);
  75. return ret;
  76. }
  77. static int wdt_stop(struct watchdog_device *wd)
  78. {
  79. struct mid_wdt *mid = watchdog_get_drvdata(wd);
  80. int ret;
  81. ret = wdt_command(mid, SCU_WATCHDOG_STOP, NULL, 0, 0);
  82. if (ret)
  83. dev_crit(mid->dev, "Error stopping watchdog: %d\n", ret);
  84. return ret;
  85. }
  86. static irqreturn_t mid_wdt_irq(int irq, void *dev_id)
  87. {
  88. panic("Kernel Watchdog");
  89. /* This code should not be reached */
  90. return IRQ_HANDLED;
  91. }
  92. static const struct watchdog_info mid_wdt_info = {
  93. .identity = "Intel MID SCU watchdog",
  94. .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE,
  95. };
  96. static const struct watchdog_ops mid_wdt_ops = {
  97. .owner = THIS_MODULE,
  98. .start = wdt_start,
  99. .stop = wdt_stop,
  100. .ping = wdt_ping,
  101. };
  102. static int mid_wdt_probe(struct platform_device *pdev)
  103. {
  104. struct device *dev = &pdev->dev;
  105. struct watchdog_device *wdt_dev;
  106. struct intel_mid_wdt_pdata *pdata = dev_get_platdata(dev);
  107. struct mid_wdt *mid;
  108. int ret;
  109. if (!pdata) {
  110. dev_err(dev, "missing platform data\n");
  111. return -EINVAL;
  112. }
  113. if (pdata->probe) {
  114. ret = pdata->probe(pdev);
  115. if (ret)
  116. return ret;
  117. }
  118. mid = devm_kzalloc(dev, sizeof(*mid), GFP_KERNEL);
  119. if (!mid)
  120. return -ENOMEM;
  121. mid->dev = dev;
  122. wdt_dev = &mid->wd;
  123. wdt_dev->info = &mid_wdt_info;
  124. wdt_dev->ops = &mid_wdt_ops;
  125. wdt_dev->min_timeout = MID_WDT_TIMEOUT_MIN;
  126. wdt_dev->max_timeout = MID_WDT_TIMEOUT_MAX;
  127. wdt_dev->timeout = MID_WDT_DEFAULT_TIMEOUT;
  128. wdt_dev->parent = dev;
  129. watchdog_set_nowayout(wdt_dev, WATCHDOG_NOWAYOUT);
  130. watchdog_set_drvdata(wdt_dev, mid);
  131. mid->scu = devm_intel_scu_ipc_dev_get(dev);
  132. if (!mid->scu)
  133. return -EPROBE_DEFER;
  134. ret = devm_request_irq(dev, pdata->irq, mid_wdt_irq,
  135. IRQF_SHARED | IRQF_NO_SUSPEND, "watchdog",
  136. wdt_dev);
  137. if (ret) {
  138. dev_err(dev, "error requesting warning irq %d\n", pdata->irq);
  139. return ret;
  140. }
  141. /*
  142. * The firmware followed by U-Boot leaves the watchdog running
  143. * with the default threshold which may vary. When we get here
  144. * we should make a decision to prevent any side effects before
  145. * user space daemon will take care of it. The best option,
  146. * taking into consideration that there is no way to read values
  147. * back from hardware, is to enforce watchdog being run with
  148. * deterministic values.
  149. */
  150. ret = wdt_start(wdt_dev);
  151. if (ret)
  152. return ret;
  153. /* Make sure the watchdog is serviced */
  154. set_bit(WDOG_HW_RUNNING, &wdt_dev->status);
  155. ret = devm_watchdog_register_device(dev, wdt_dev);
  156. if (ret)
  157. return ret;
  158. dev_info(dev, "Intel MID watchdog device probed\n");
  159. return 0;
  160. }
  161. static struct platform_driver mid_wdt_driver = {
  162. .probe = mid_wdt_probe,
  163. .driver = {
  164. .name = "intel_mid_wdt",
  165. },
  166. };
  167. module_platform_driver(mid_wdt_driver);
  168. MODULE_AUTHOR("David Cohen <david.a.cohen@linux.intel.com>");
  169. MODULE_DESCRIPTION("Watchdog Driver for Intel MID platform");
  170. MODULE_LICENSE("GPL");
  171. MODULE_ALIAS("platform:intel_mid_wdt");