gh_virt_wdt.c 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
  4. * Copyright (c) 2021-2023, Qualcomm Innovation Center, Inc. All rights reserved.
  5. *
  6. */
  7. #include <soc/qcom/watchdog.h>
  8. #include <linux/arm-smccc.h>
  9. #include <linux/gunyah_rsc_mgr.h>
  10. #include <linux/kernel.h>
  11. #include <linux/mod_devicetable.h>
  12. #include <linux/module.h>
  13. #define VIRT_WDT_CONTROL \
  14. ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_32,\
  15. ARM_SMCCC_OWNER_VENDOR_HYP, 0x0005)
  16. #define VIRT_WDT_STATUS \
  17. ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_32,\
  18. ARM_SMCCC_OWNER_VENDOR_HYP, 0x0006)
  19. #define VIRT_WDT_PET \
  20. ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_32,\
  21. ARM_SMCCC_OWNER_VENDOR_HYP, 0x0007)
  22. #define VIRT_WDT_SET_TIME \
  23. ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_32,\
  24. ARM_SMCCC_OWNER_VENDOR_HYP, 0x0008)
  25. #define VIRT_WDT_NO_CHANGE 0xFFFF
  26. /**
  27. * gh_wdt_call() - Sends ARM SMCCC 1.1 Calls to the hypervisor
  28. *
  29. * @smc_id: The smc id needed to interact with the watchdog in the hypervisor
  30. * @arg1: A u32 value to be sent to the the hypervisor
  31. * @arg2: A u16 value to be sent to the the hypervisor
  32. * @arg3: A u16 value to be sent to the the hypervisor
  33. *
  34. * The hypervisor takes input via ARM SMCCC Calls. The position of
  35. * these values matter. u16 values are needed to set both the bark
  36. * and bite time. In all other cases only the u32 (arg1) value is required.
  37. *
  38. * return: 0 on success, negative errno on failure.
  39. */
  40. static struct arm_smccc_res gh_wdt_call(u32 smc_id, u32 arg1,
  41. u16 arg2, u16 arg3)
  42. {
  43. struct arm_smccc_res res;
  44. if (smc_id == VIRT_WDT_SET_TIME)
  45. /* virtual watchdog expecting u16 values for bark and bite */
  46. arm_smccc_1_1_smc(smc_id, arg2, arg3, &res);
  47. else
  48. arm_smccc_1_1_smc(smc_id, arg1, &res);
  49. return res;
  50. }
  51. /**
  52. * gh_set_wdt_bark() - Sets the bark time for the virtual watchdog
  53. *
  54. * @time: A u32 value to be converted to milliseconds (u16)
  55. * @wdog_dd: The qcom watchdog data structure
  56. *
  57. * The hypervisor requires both the bark and the bite time in the same
  58. * call. To update one and not the other, the value VIRT_WDT_NO_CHANGE
  59. * is used.
  60. *
  61. * return: 0 on success, negative errno on failure.
  62. */
  63. static int gh_set_wdt_bark(u32 time, struct msm_watchdog_data *wdog_dd)
  64. {
  65. struct arm_smccc_res res;
  66. int hret, ret;
  67. u16 bark_time;
  68. bark_time = (u16) time;
  69. res = gh_wdt_call(VIRT_WDT_SET_TIME, 0, bark_time, VIRT_WDT_NO_CHANGE);
  70. hret = res.a0;
  71. ret = gh_error_remap(hret);
  72. if (hret) {
  73. dev_err(wdog_dd->dev, "failed to set bark time for vDOG, hret = %d ret = %d\n",
  74. hret, ret);
  75. }
  76. return ret;
  77. }
  78. /**
  79. * gh_set_wdt_bite() - Sets the bite time for the virtual watchdog
  80. *
  81. * @time: A u32 value to be converted to milliseconds (u16)
  82. * @wdog_dd: The qcom watchdog data structure
  83. *
  84. * The hypervisor requires both the bark and the bite time in the same
  85. * call. To update one and not the other, the value VIRT_WDT_NO_CHANGE
  86. * is used.
  87. *
  88. * return: 0 on success, negative errno on failure.
  89. */
  90. static int gh_set_wdt_bite(u32 time, struct msm_watchdog_data *wdog_dd)
  91. {
  92. struct arm_smccc_res res;
  93. int hret, ret;
  94. u16 bite_time;
  95. bite_time = (u16) time;
  96. res = gh_wdt_call(VIRT_WDT_SET_TIME, 0, VIRT_WDT_NO_CHANGE, bite_time);
  97. hret = res.a0;
  98. ret = gh_error_remap(hret);
  99. if (hret) {
  100. dev_err(wdog_dd->dev, "failed to set bite time for vWDOG, hret = %d ret = %d\n",
  101. hret, ret);
  102. }
  103. return ret;
  104. }
  105. /**
  106. * gh_reset_wdt() - Resets the virtual watchdog timer
  107. *
  108. * @wdog_dd: The qcom watchdog data structure
  109. *
  110. * VIRT_WDT_PET is used to reset the virtual watchdog.
  111. *
  112. * return: 0 on success, negative errno on failure.
  113. */
  114. static int gh_reset_wdt(struct msm_watchdog_data *wdog_dd)
  115. {
  116. struct arm_smccc_res res;
  117. int hret, ret;
  118. res = gh_wdt_call(VIRT_WDT_PET, 0, 0, 0);
  119. hret = res.a0;
  120. ret = gh_error_remap(hret);
  121. if (hret) {
  122. dev_err(wdog_dd->dev, "failed to reset vWDOG, hret = %d ret = %d\n",
  123. hret, ret);
  124. }
  125. return ret;
  126. }
  127. /**
  128. * gh_enable_wdt() - Enables the virtual watchdog
  129. *
  130. * @wdog_dd: The qcom watchdog data structure
  131. * @state: state value to send to watchdog
  132. *
  133. * VIRT_WDT_CONTROL is used to enable the virtual watchdog.
  134. * Bit 0 is used to enable the watchdog. When this Bit is set to
  135. * 1 the watchdog is enabled. NOTE: Bit 1 must always be set to
  136. * 1 as this bit is reserved in the hypervisor and Bit 1 is
  137. * expected to be 1. If this Bit is not set, the hypervisor will
  138. * return an error. So to enable the watchdog you must use the value 3.
  139. * An error from the hypervisor is expected if you try to enable the
  140. * watchdog when its already enabled.
  141. *
  142. * return: 0 on success, negative errno on failure.
  143. */
  144. static int gh_enable_wdt(u32 state, struct msm_watchdog_data *wdog_dd)
  145. {
  146. struct arm_smccc_res res;
  147. int hret, ret;
  148. if (wdog_dd->enabled) {
  149. dev_err(wdog_dd->dev, "vWDT already enabled\n");
  150. return 0;
  151. }
  152. res = gh_wdt_call(VIRT_WDT_CONTROL, 3, 0, 0);
  153. hret = res.a0;
  154. ret = gh_error_remap(hret);
  155. if (hret) {
  156. dev_err(wdog_dd->dev, "failed enabling vWDOG, hret = %d ret = %d\n",
  157. hret, ret);
  158. }
  159. return ret;
  160. }
  161. /**
  162. * gh_disable_wdt() - Disables the virtual watchdog
  163. *
  164. * @wdog_dd: The qcom watchdog data structure
  165. *
  166. * VIRT_WDT_CONTROL is used to disable the virtual watchdog.
  167. * Bit 0 is used to disable the watchdog. When this Bit is set to
  168. * 0 the watchdog is disabled. NOTE: Bit 1 must always be set to
  169. * 1 as this bit is reserved in the hypervisor and Bit 1 is
  170. * expected to be 1. If this Bit is not set, the hypervisor will
  171. * return an error. So to disable the watchdog you must use the value 2.
  172. * An error from the hypervisor is expected if you try to disable the
  173. * watchdog when its already disabled.
  174. *
  175. * return: 0 on success, negative errno on failure.
  176. */
  177. static int gh_disable_wdt(struct msm_watchdog_data *wdog_dd)
  178. {
  179. struct arm_smccc_res res;
  180. int hret, ret;
  181. if (!wdog_dd->enabled) {
  182. dev_err(wdog_dd->dev, "vWDT already disabled\n");
  183. return 0;
  184. }
  185. res = gh_wdt_call(VIRT_WDT_CONTROL, 2, 0, 0);
  186. hret = res.a0;
  187. ret = gh_error_remap(hret);
  188. if (hret) {
  189. dev_err(wdog_dd->dev, "failed disabling VDOG, hret = %d ret = %d\n",
  190. hret, ret);
  191. }
  192. return ret;
  193. }
  194. /**
  195. * gh_get_wdt_status() - Displays the status of the virtual watchdog
  196. *
  197. * @wdog_dd: The qcom watchdog data structure
  198. *
  199. * VIRT_WDT_STATUS is used to display status of the virtual watchdog.
  200. *
  201. * return: 0 on success, negative errno on failure.
  202. */
  203. static int gh_show_wdt_status(struct msm_watchdog_data *wdog_dd)
  204. {
  205. struct arm_smccc_res res;
  206. int hret, ret;
  207. res = gh_wdt_call(VIRT_WDT_STATUS, 0, 0, 0);
  208. hret = res.a0;
  209. ret = gh_error_remap(hret);
  210. if (hret) {
  211. dev_err(wdog_dd->dev, "failed to get vWDOG status, hret = %d ret = %d\n",
  212. hret, ret);
  213. } else {
  214. dev_err(wdog_dd->dev,
  215. "vWdog-CTL: %d, vWdog-time since last pet: %d, vWdog-expired status: %d\n",
  216. res.a1 & 1, res.a2, (res.a1 >> 31) & 1);
  217. }
  218. return ret;
  219. }
  220. static struct qcom_wdt_ops gh_wdt_ops = {
  221. .set_bark_time = gh_set_wdt_bark,
  222. .set_bite_time = gh_set_wdt_bite,
  223. .reset_wdt = gh_reset_wdt,
  224. .enable_wdt = gh_enable_wdt,
  225. .disable_wdt = gh_disable_wdt,
  226. .show_wdt_status = gh_show_wdt_status
  227. };
  228. static int gh_wdt_probe(struct platform_device *pdev)
  229. {
  230. struct msm_watchdog_data *wdog_dd;
  231. wdog_dd = devm_kzalloc(&pdev->dev, sizeof(*wdog_dd), GFP_KERNEL);
  232. if (!wdog_dd)
  233. return -ENOMEM;
  234. wdog_dd->ops = &gh_wdt_ops;
  235. return qcom_wdt_register(pdev, wdog_dd, "gh-watchdog");
  236. }
  237. static const struct dev_pm_ops gh_wdt_dev_pm_ops = {
  238. #ifdef CONFIG_PM_SLEEP
  239. .suspend_late = qcom_wdt_pet_suspend,
  240. .resume_early = qcom_wdt_pet_resume,
  241. #endif
  242. .freeze_late = qcom_wdt_pet_suspend,
  243. .restore_early = qcom_wdt_pet_resume,
  244. };
  245. static const struct of_device_id gh_wdt_match_table[] = {
  246. { .compatible = "qcom,gh-watchdog" },
  247. { .compatible = "qcom,hh-watchdog" },
  248. {}
  249. };
  250. static struct platform_driver gh_wdt_driver = {
  251. .probe = gh_wdt_probe,
  252. .remove = qcom_wdt_remove,
  253. .driver = {
  254. .name = "gh-watchdog",
  255. .pm = &gh_wdt_dev_pm_ops,
  256. .of_match_table = gh_wdt_match_table,
  257. },
  258. };
  259. static int __init init_watchdog(void)
  260. {
  261. return platform_driver_register(&gh_wdt_driver);
  262. }
  263. #if IS_MODULE(CONFIG_GH_VIRT_WATCHDOG)
  264. module_init(init_watchdog);
  265. #else
  266. pure_initcall(init_watchdog);
  267. #endif
  268. static __exit void exit_watchdog(void)
  269. {
  270. platform_driver_unregister(&gh_wdt_driver);
  271. }
  272. module_exit(exit_watchdog);
  273. MODULE_DESCRIPTION("QCOM Gunyah Watchdog Driver");
  274. MODULE_LICENSE("GPL v2");