arm_smc_wdt.c 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * ARM Secure Monitor Call watchdog driver
  4. *
  5. * Copyright 2020 Google LLC.
  6. * Julius Werner <jwerner@chromium.org>
  7. * Based on mtk_wdt.c
  8. */
  9. #include <linux/arm-smccc.h>
  10. #include <linux/err.h>
  11. #include <linux/module.h>
  12. #include <linux/moduleparam.h>
  13. #include <linux/of.h>
  14. #include <linux/platform_device.h>
  15. #include <linux/types.h>
  16. #include <linux/watchdog.h>
  17. #include <uapi/linux/psci.h>
  18. #define DRV_NAME "arm_smc_wdt"
  19. #define DRV_VERSION "1.0"
  20. enum smcwd_call {
  21. SMCWD_INIT = 0,
  22. SMCWD_SET_TIMEOUT = 1,
  23. SMCWD_ENABLE = 2,
  24. SMCWD_PET = 3,
  25. SMCWD_GET_TIMELEFT = 4,
  26. };
  27. static bool nowayout = WATCHDOG_NOWAYOUT;
  28. static unsigned int timeout;
  29. static int smcwd_call(struct watchdog_device *wdd, enum smcwd_call call,
  30. unsigned long arg, struct arm_smccc_res *res)
  31. {
  32. struct arm_smccc_res local_res;
  33. if (!res)
  34. res = &local_res;
  35. arm_smccc_smc((u32)(uintptr_t)watchdog_get_drvdata(wdd), call, arg, 0,
  36. 0, 0, 0, 0, res);
  37. if (res->a0 == PSCI_RET_NOT_SUPPORTED)
  38. return -ENODEV;
  39. if (res->a0 == PSCI_RET_INVALID_PARAMS)
  40. return -EINVAL;
  41. if (res->a0 != PSCI_RET_SUCCESS)
  42. return -EIO;
  43. return 0;
  44. }
  45. static int smcwd_ping(struct watchdog_device *wdd)
  46. {
  47. return smcwd_call(wdd, SMCWD_PET, 0, NULL);
  48. }
  49. static unsigned int smcwd_get_timeleft(struct watchdog_device *wdd)
  50. {
  51. struct arm_smccc_res res;
  52. smcwd_call(wdd, SMCWD_GET_TIMELEFT, 0, &res);
  53. if (res.a0)
  54. return 0;
  55. return res.a1;
  56. }
  57. static int smcwd_set_timeout(struct watchdog_device *wdd, unsigned int timeout)
  58. {
  59. int res;
  60. res = smcwd_call(wdd, SMCWD_SET_TIMEOUT, timeout, NULL);
  61. if (!res)
  62. wdd->timeout = timeout;
  63. return res;
  64. }
  65. static int smcwd_stop(struct watchdog_device *wdd)
  66. {
  67. return smcwd_call(wdd, SMCWD_ENABLE, 0, NULL);
  68. }
  69. static int smcwd_start(struct watchdog_device *wdd)
  70. {
  71. return smcwd_call(wdd, SMCWD_ENABLE, 1, NULL);
  72. }
  73. static const struct watchdog_info smcwd_info = {
  74. .identity = DRV_NAME,
  75. .options = WDIOF_SETTIMEOUT |
  76. WDIOF_KEEPALIVEPING |
  77. WDIOF_MAGICCLOSE,
  78. };
  79. static const struct watchdog_ops smcwd_ops = {
  80. .start = smcwd_start,
  81. .stop = smcwd_stop,
  82. .ping = smcwd_ping,
  83. .set_timeout = smcwd_set_timeout,
  84. };
  85. static const struct watchdog_ops smcwd_timeleft_ops = {
  86. .start = smcwd_start,
  87. .stop = smcwd_stop,
  88. .ping = smcwd_ping,
  89. .set_timeout = smcwd_set_timeout,
  90. .get_timeleft = smcwd_get_timeleft,
  91. };
  92. static int smcwd_probe(struct platform_device *pdev)
  93. {
  94. struct watchdog_device *wdd;
  95. int err;
  96. struct arm_smccc_res res;
  97. u32 smc_func_id;
  98. wdd = devm_kzalloc(&pdev->dev, sizeof(*wdd), GFP_KERNEL);
  99. if (!wdd)
  100. return -ENOMEM;
  101. platform_set_drvdata(pdev, wdd);
  102. if (of_property_read_u32(pdev->dev.of_node, "arm,smc-id",
  103. &smc_func_id))
  104. smc_func_id = 0x82003D06;
  105. watchdog_set_drvdata(wdd, (void *)(uintptr_t)smc_func_id);
  106. err = smcwd_call(wdd, SMCWD_INIT, 0, &res);
  107. if (err < 0)
  108. return err;
  109. wdd->info = &smcwd_info;
  110. /* get_timeleft is optional */
  111. if (smcwd_call(wdd, SMCWD_GET_TIMELEFT, 0, NULL))
  112. wdd->ops = &smcwd_ops;
  113. else
  114. wdd->ops = &smcwd_timeleft_ops;
  115. wdd->timeout = res.a2;
  116. wdd->max_timeout = res.a2;
  117. wdd->min_timeout = res.a1;
  118. wdd->parent = &pdev->dev;
  119. watchdog_stop_on_reboot(wdd);
  120. watchdog_stop_on_unregister(wdd);
  121. watchdog_set_nowayout(wdd, nowayout);
  122. watchdog_init_timeout(wdd, timeout, &pdev->dev);
  123. err = smcwd_set_timeout(wdd, wdd->timeout);
  124. if (err)
  125. return err;
  126. err = devm_watchdog_register_device(&pdev->dev, wdd);
  127. if (err)
  128. return err;
  129. dev_info(&pdev->dev,
  130. "Watchdog registered (timeout=%d sec, nowayout=%d)\n",
  131. wdd->timeout, nowayout);
  132. return 0;
  133. }
  134. static const struct of_device_id smcwd_dt_ids[] = {
  135. { .compatible = "arm,smc-wdt" },
  136. {}
  137. };
  138. MODULE_DEVICE_TABLE(of, smcwd_dt_ids);
  139. static struct platform_driver smcwd_driver = {
  140. .probe = smcwd_probe,
  141. .driver = {
  142. .name = DRV_NAME,
  143. .of_match_table = smcwd_dt_ids,
  144. },
  145. };
  146. module_platform_driver(smcwd_driver);
  147. module_param(timeout, uint, 0);
  148. MODULE_PARM_DESC(timeout, "Watchdog heartbeat in seconds");
  149. module_param(nowayout, bool, 0);
  150. MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
  151. __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
  152. MODULE_LICENSE("GPL");
  153. MODULE_AUTHOR("Julius Werner <jwerner@chromium.org>");
  154. MODULE_DESCRIPTION("ARM Secure Monitor Call Watchdog Driver");
  155. MODULE_VERSION(DRV_VERSION);