intel-mid_wdt.c 4.9 KB

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