pic32-dmt.c 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. /*
  2. * PIC32 deadman timer driver
  3. *
  4. * Purna Chandra Mandal <purna.mandal@microchip.com>
  5. * Copyright (c) 2016, Microchip Technology Inc.
  6. *
  7. * This program is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU General Public License
  9. * as published by the Free Software Foundation; either version
  10. * 2 of the License, or (at your option) any later version.
  11. */
  12. #include <linux/clk.h>
  13. #include <linux/device.h>
  14. #include <linux/err.h>
  15. #include <linux/io.h>
  16. #include <linux/kernel.h>
  17. #include <linux/module.h>
  18. #include <linux/of.h>
  19. #include <linux/of_device.h>
  20. #include <linux/platform_device.h>
  21. #include <linux/pm.h>
  22. #include <linux/watchdog.h>
  23. #include <asm/mach-pic32/pic32.h>
  24. /* Deadman Timer Regs */
  25. #define DMTCON_REG 0x00
  26. #define DMTPRECLR_REG 0x10
  27. #define DMTCLR_REG 0x20
  28. #define DMTSTAT_REG 0x30
  29. #define DMTCNT_REG 0x40
  30. #define DMTPSCNT_REG 0x60
  31. #define DMTPSINTV_REG 0x70
  32. /* Deadman Timer Regs fields */
  33. #define DMT_ON BIT(15)
  34. #define DMT_STEP1_KEY BIT(6)
  35. #define DMT_STEP2_KEY BIT(3)
  36. #define DMTSTAT_WINOPN BIT(0)
  37. #define DMTSTAT_EVENT BIT(5)
  38. #define DMTSTAT_BAD2 BIT(6)
  39. #define DMTSTAT_BAD1 BIT(7)
  40. /* Reset Control Register fields for watchdog */
  41. #define RESETCON_DMT_TIMEOUT BIT(5)
  42. struct pic32_dmt {
  43. void __iomem *regs;
  44. struct clk *clk;
  45. };
  46. static inline void dmt_enable(struct pic32_dmt *dmt)
  47. {
  48. writel(DMT_ON, PIC32_SET(dmt->regs + DMTCON_REG));
  49. }
  50. static inline void dmt_disable(struct pic32_dmt *dmt)
  51. {
  52. writel(DMT_ON, PIC32_CLR(dmt->regs + DMTCON_REG));
  53. /*
  54. * Cannot touch registers in the CPU cycle following clearing the
  55. * ON bit.
  56. */
  57. nop();
  58. }
  59. static inline int dmt_bad_status(struct pic32_dmt *dmt)
  60. {
  61. u32 val;
  62. val = readl(dmt->regs + DMTSTAT_REG);
  63. val &= (DMTSTAT_BAD1 | DMTSTAT_BAD2 | DMTSTAT_EVENT);
  64. if (val)
  65. return -EAGAIN;
  66. return 0;
  67. }
  68. static inline int dmt_keepalive(struct pic32_dmt *dmt)
  69. {
  70. u32 v;
  71. u32 timeout = 500;
  72. /* set pre-clear key */
  73. writel(DMT_STEP1_KEY << 8, dmt->regs + DMTPRECLR_REG);
  74. /* wait for DMT window to open */
  75. while (--timeout) {
  76. v = readl(dmt->regs + DMTSTAT_REG) & DMTSTAT_WINOPN;
  77. if (v == DMTSTAT_WINOPN)
  78. break;
  79. }
  80. /* apply key2 */
  81. writel(DMT_STEP2_KEY, dmt->regs + DMTCLR_REG);
  82. /* check whether keys are latched correctly */
  83. return dmt_bad_status(dmt);
  84. }
  85. static inline u32 pic32_dmt_get_timeout_secs(struct pic32_dmt *dmt)
  86. {
  87. unsigned long rate;
  88. rate = clk_get_rate(dmt->clk);
  89. if (rate)
  90. return readl(dmt->regs + DMTPSCNT_REG) / rate;
  91. return 0;
  92. }
  93. static inline u32 pic32_dmt_bootstatus(struct pic32_dmt *dmt)
  94. {
  95. u32 v;
  96. void __iomem *rst_base;
  97. rst_base = ioremap(PIC32_BASE_RESET, 0x10);
  98. if (!rst_base)
  99. return 0;
  100. v = readl(rst_base);
  101. writel(RESETCON_DMT_TIMEOUT, PIC32_CLR(rst_base));
  102. iounmap(rst_base);
  103. return v & RESETCON_DMT_TIMEOUT;
  104. }
  105. static int pic32_dmt_start(struct watchdog_device *wdd)
  106. {
  107. struct pic32_dmt *dmt = watchdog_get_drvdata(wdd);
  108. dmt_enable(dmt);
  109. return dmt_keepalive(dmt);
  110. }
  111. static int pic32_dmt_stop(struct watchdog_device *wdd)
  112. {
  113. struct pic32_dmt *dmt = watchdog_get_drvdata(wdd);
  114. dmt_disable(dmt);
  115. return 0;
  116. }
  117. static int pic32_dmt_ping(struct watchdog_device *wdd)
  118. {
  119. struct pic32_dmt *dmt = watchdog_get_drvdata(wdd);
  120. return dmt_keepalive(dmt);
  121. }
  122. static const struct watchdog_ops pic32_dmt_fops = {
  123. .owner = THIS_MODULE,
  124. .start = pic32_dmt_start,
  125. .stop = pic32_dmt_stop,
  126. .ping = pic32_dmt_ping,
  127. };
  128. static const struct watchdog_info pic32_dmt_ident = {
  129. .options = WDIOF_KEEPALIVEPING |
  130. WDIOF_MAGICCLOSE,
  131. .identity = "PIC32 Deadman Timer",
  132. };
  133. static struct watchdog_device pic32_dmt_wdd = {
  134. .info = &pic32_dmt_ident,
  135. .ops = &pic32_dmt_fops,
  136. };
  137. static int pic32_dmt_probe(struct platform_device *pdev)
  138. {
  139. int ret;
  140. struct pic32_dmt *dmt;
  141. struct resource *mem;
  142. struct watchdog_device *wdd = &pic32_dmt_wdd;
  143. dmt = devm_kzalloc(&pdev->dev, sizeof(*dmt), GFP_KERNEL);
  144. if (!dmt)
  145. return -ENOMEM;
  146. mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  147. dmt->regs = devm_ioremap_resource(&pdev->dev, mem);
  148. if (IS_ERR(dmt->regs))
  149. return PTR_ERR(dmt->regs);
  150. dmt->clk = devm_clk_get(&pdev->dev, NULL);
  151. if (IS_ERR(dmt->clk)) {
  152. dev_err(&pdev->dev, "clk not found\n");
  153. return PTR_ERR(dmt->clk);
  154. }
  155. ret = clk_prepare_enable(dmt->clk);
  156. if (ret)
  157. return ret;
  158. wdd->timeout = pic32_dmt_get_timeout_secs(dmt);
  159. if (!wdd->timeout) {
  160. dev_err(&pdev->dev,
  161. "failed to read watchdog register timeout\n");
  162. ret = -EINVAL;
  163. goto out_disable_clk;
  164. }
  165. dev_info(&pdev->dev, "timeout %d\n", wdd->timeout);
  166. wdd->bootstatus = pic32_dmt_bootstatus(dmt) ? WDIOF_CARDRESET : 0;
  167. watchdog_set_nowayout(wdd, WATCHDOG_NOWAYOUT);
  168. watchdog_set_drvdata(wdd, dmt);
  169. ret = watchdog_register_device(wdd);
  170. if (ret) {
  171. dev_err(&pdev->dev, "watchdog register failed, err %d\n", ret);
  172. goto out_disable_clk;
  173. }
  174. platform_set_drvdata(pdev, wdd);
  175. return 0;
  176. out_disable_clk:
  177. clk_disable_unprepare(dmt->clk);
  178. return ret;
  179. }
  180. static int pic32_dmt_remove(struct platform_device *pdev)
  181. {
  182. struct watchdog_device *wdd = platform_get_drvdata(pdev);
  183. struct pic32_dmt *dmt = watchdog_get_drvdata(wdd);
  184. watchdog_unregister_device(wdd);
  185. clk_disable_unprepare(dmt->clk);
  186. return 0;
  187. }
  188. static const struct of_device_id pic32_dmt_of_ids[] = {
  189. { .compatible = "microchip,pic32mzda-dmt",},
  190. { /* sentinel */ }
  191. };
  192. MODULE_DEVICE_TABLE(of, pic32_dmt_of_ids);
  193. static struct platform_driver pic32_dmt_driver = {
  194. .probe = pic32_dmt_probe,
  195. .remove = pic32_dmt_remove,
  196. .driver = {
  197. .name = "pic32-dmt",
  198. .of_match_table = of_match_ptr(pic32_dmt_of_ids),
  199. }
  200. };
  201. module_platform_driver(pic32_dmt_driver);
  202. MODULE_AUTHOR("Purna Chandra Mandal <purna.mandal@microchip.com>");
  203. MODULE_DESCRIPTION("Microchip PIC32 DMT Driver");
  204. MODULE_LICENSE("GPL");