pm8916_wdt.c 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. // SPDX-License-Identifier: GPL-2.0
  2. #include <linux/bitops.h>
  3. #include <linux/interrupt.h>
  4. #include <linux/kernel.h>
  5. #include <linux/module.h>
  6. #include <linux/of.h>
  7. #include <linux/property.h>
  8. #include <linux/platform_device.h>
  9. #include <linux/regmap.h>
  10. #include <linux/watchdog.h>
  11. #define PON_INT_RT_STS 0x10
  12. #define PMIC_WD_BARK_STS_BIT BIT(6)
  13. #define PON_PMIC_WD_RESET_S1_TIMER 0x54
  14. #define PON_PMIC_WD_RESET_S2_TIMER 0x55
  15. #define PON_PMIC_WD_RESET_S2_CTL 0x56
  16. #define RESET_TYPE_WARM 0x01
  17. #define RESET_TYPE_SHUTDOWN 0x04
  18. #define RESET_TYPE_HARD 0x07
  19. #define PON_PMIC_WD_RESET_S2_CTL2 0x57
  20. #define S2_RESET_EN_BIT BIT(7)
  21. #define PON_PMIC_WD_RESET_PET 0x58
  22. #define WATCHDOG_PET_BIT BIT(0)
  23. #define PM8916_WDT_DEFAULT_TIMEOUT 32
  24. #define PM8916_WDT_MIN_TIMEOUT 1
  25. #define PM8916_WDT_MAX_TIMEOUT 127
  26. struct pm8916_wdt {
  27. struct regmap *regmap;
  28. struct watchdog_device wdev;
  29. u32 baseaddr;
  30. };
  31. static int pm8916_wdt_start(struct watchdog_device *wdev)
  32. {
  33. struct pm8916_wdt *wdt = watchdog_get_drvdata(wdev);
  34. return regmap_update_bits(wdt->regmap,
  35. wdt->baseaddr + PON_PMIC_WD_RESET_S2_CTL2,
  36. S2_RESET_EN_BIT, S2_RESET_EN_BIT);
  37. }
  38. static int pm8916_wdt_stop(struct watchdog_device *wdev)
  39. {
  40. struct pm8916_wdt *wdt = watchdog_get_drvdata(wdev);
  41. return regmap_update_bits(wdt->regmap,
  42. wdt->baseaddr + PON_PMIC_WD_RESET_S2_CTL2,
  43. S2_RESET_EN_BIT, 0);
  44. }
  45. static int pm8916_wdt_ping(struct watchdog_device *wdev)
  46. {
  47. struct pm8916_wdt *wdt = watchdog_get_drvdata(wdev);
  48. return regmap_update_bits(wdt->regmap,
  49. wdt->baseaddr + PON_PMIC_WD_RESET_PET,
  50. WATCHDOG_PET_BIT, WATCHDOG_PET_BIT);
  51. }
  52. static int pm8916_wdt_configure_timers(struct watchdog_device *wdev)
  53. {
  54. struct pm8916_wdt *wdt = watchdog_get_drvdata(wdev);
  55. int err;
  56. err = regmap_write(wdt->regmap,
  57. wdt->baseaddr + PON_PMIC_WD_RESET_S1_TIMER,
  58. wdev->timeout - wdev->pretimeout);
  59. if (err)
  60. return err;
  61. return regmap_write(wdt->regmap,
  62. wdt->baseaddr + PON_PMIC_WD_RESET_S2_TIMER,
  63. wdev->pretimeout);
  64. }
  65. static int pm8916_wdt_set_timeout(struct watchdog_device *wdev,
  66. unsigned int timeout)
  67. {
  68. wdev->timeout = timeout;
  69. return pm8916_wdt_configure_timers(wdev);
  70. }
  71. static int pm8916_wdt_set_pretimeout(struct watchdog_device *wdev,
  72. unsigned int pretimeout)
  73. {
  74. wdev->pretimeout = pretimeout;
  75. return pm8916_wdt_configure_timers(wdev);
  76. }
  77. static irqreturn_t pm8916_wdt_isr(int irq, void *arg)
  78. {
  79. struct pm8916_wdt *wdt = arg;
  80. int err, sts;
  81. err = regmap_read(wdt->regmap, wdt->baseaddr + PON_INT_RT_STS, &sts);
  82. if (err)
  83. return IRQ_HANDLED;
  84. if (sts & PMIC_WD_BARK_STS_BIT)
  85. watchdog_notify_pretimeout(&wdt->wdev);
  86. return IRQ_HANDLED;
  87. }
  88. static const struct watchdog_info pm8916_wdt_ident = {
  89. .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
  90. .identity = "QCOM PM8916 PON WDT",
  91. };
  92. static const struct watchdog_info pm8916_wdt_pt_ident = {
  93. .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE |
  94. WDIOF_PRETIMEOUT,
  95. .identity = "QCOM PM8916 PON WDT",
  96. };
  97. static const struct watchdog_ops pm8916_wdt_ops = {
  98. .owner = THIS_MODULE,
  99. .start = pm8916_wdt_start,
  100. .stop = pm8916_wdt_stop,
  101. .ping = pm8916_wdt_ping,
  102. .set_timeout = pm8916_wdt_set_timeout,
  103. .set_pretimeout = pm8916_wdt_set_pretimeout,
  104. };
  105. static int pm8916_wdt_probe(struct platform_device *pdev)
  106. {
  107. struct device *dev = &pdev->dev;
  108. struct pm8916_wdt *wdt;
  109. struct device *parent;
  110. int err, irq;
  111. wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
  112. if (!wdt)
  113. return -ENOMEM;
  114. parent = dev->parent;
  115. /*
  116. * The pm8916-pon-wdt is a child of the pon device, which is a child
  117. * of the pm8916 mfd device. We want access to the pm8916 registers.
  118. * Retrieve regmap from pm8916 (parent->parent) and base address
  119. * from pm8916-pon (pon).
  120. */
  121. wdt->regmap = dev_get_regmap(parent->parent, NULL);
  122. if (!wdt->regmap) {
  123. dev_err(dev, "failed to locate regmap\n");
  124. return -ENODEV;
  125. }
  126. err = device_property_read_u32(parent, "reg", &wdt->baseaddr);
  127. if (err) {
  128. dev_err(dev, "failed to get pm8916-pon address\n");
  129. return err;
  130. }
  131. irq = platform_get_irq(pdev, 0);
  132. if (irq > 0) {
  133. err = devm_request_irq(dev, irq, pm8916_wdt_isr, 0,
  134. "pm8916_wdt", wdt);
  135. if (err)
  136. return err;
  137. wdt->wdev.info = &pm8916_wdt_pt_ident;
  138. } else {
  139. if (irq == -EPROBE_DEFER)
  140. return -EPROBE_DEFER;
  141. wdt->wdev.info = &pm8916_wdt_ident;
  142. }
  143. /* Configure watchdog to hard-reset mode */
  144. err = regmap_write(wdt->regmap,
  145. wdt->baseaddr + PON_PMIC_WD_RESET_S2_CTL,
  146. RESET_TYPE_HARD);
  147. if (err) {
  148. dev_err(dev, "failed configure watchdog\n");
  149. return err;
  150. }
  151. wdt->wdev.ops = &pm8916_wdt_ops,
  152. wdt->wdev.parent = dev;
  153. wdt->wdev.min_timeout = PM8916_WDT_MIN_TIMEOUT;
  154. wdt->wdev.max_timeout = PM8916_WDT_MAX_TIMEOUT;
  155. wdt->wdev.timeout = PM8916_WDT_DEFAULT_TIMEOUT;
  156. wdt->wdev.pretimeout = 0;
  157. watchdog_set_drvdata(&wdt->wdev, wdt);
  158. platform_set_drvdata(pdev, wdt);
  159. watchdog_init_timeout(&wdt->wdev, 0, dev);
  160. pm8916_wdt_configure_timers(&wdt->wdev);
  161. return devm_watchdog_register_device(dev, &wdt->wdev);
  162. }
  163. static int __maybe_unused pm8916_wdt_suspend(struct device *dev)
  164. {
  165. struct pm8916_wdt *wdt = dev_get_drvdata(dev);
  166. if (watchdog_active(&wdt->wdev))
  167. return pm8916_wdt_stop(&wdt->wdev);
  168. return 0;
  169. }
  170. static int __maybe_unused pm8916_wdt_resume(struct device *dev)
  171. {
  172. struct pm8916_wdt *wdt = dev_get_drvdata(dev);
  173. if (watchdog_active(&wdt->wdev))
  174. return pm8916_wdt_start(&wdt->wdev);
  175. return 0;
  176. }
  177. static SIMPLE_DEV_PM_OPS(pm8916_wdt_pm_ops, pm8916_wdt_suspend,
  178. pm8916_wdt_resume);
  179. static const struct of_device_id pm8916_wdt_id_table[] = {
  180. { .compatible = "qcom,pm8916-wdt" },
  181. { }
  182. };
  183. MODULE_DEVICE_TABLE(of, pm8916_wdt_id_table);
  184. static struct platform_driver pm8916_wdt_driver = {
  185. .probe = pm8916_wdt_probe,
  186. .driver = {
  187. .name = "pm8916-wdt",
  188. .of_match_table = of_match_ptr(pm8916_wdt_id_table),
  189. .pm = &pm8916_wdt_pm_ops,
  190. },
  191. };
  192. module_platform_driver(pm8916_wdt_driver);
  193. MODULE_AUTHOR("Loic Poulain <loic.poulain@linaro.org>");
  194. MODULE_DESCRIPTION("Qualcomm pm8916 watchdog driver");
  195. MODULE_LICENSE("GPL v2");