msc313e_wdt.c 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * MStar WDT driver
  4. *
  5. * Copyright (C) 2019 - 2021 Daniel Palmer
  6. * Copyright (C) 2021 Romain Perier
  7. *
  8. */
  9. #include <linux/clk.h>
  10. #include <linux/io.h>
  11. #include <linux/mod_devicetable.h>
  12. #include <linux/module.h>
  13. #include <linux/platform_device.h>
  14. #include <linux/watchdog.h>
  15. #define REG_WDT_CLR 0x0
  16. #define REG_WDT_MAX_PRD_L 0x10
  17. #define REG_WDT_MAX_PRD_H 0x14
  18. #define MSC313E_WDT_MIN_TIMEOUT 1
  19. #define MSC313E_WDT_DEFAULT_TIMEOUT 30
  20. static unsigned int timeout;
  21. module_param(timeout, int, 0);
  22. MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds");
  23. struct msc313e_wdt_priv {
  24. void __iomem *base;
  25. struct watchdog_device wdev;
  26. struct clk *clk;
  27. };
  28. static int msc313e_wdt_start(struct watchdog_device *wdev)
  29. {
  30. struct msc313e_wdt_priv *priv = watchdog_get_drvdata(wdev);
  31. u32 timeout;
  32. int err;
  33. err = clk_prepare_enable(priv->clk);
  34. if (err)
  35. return err;
  36. timeout = wdev->timeout * clk_get_rate(priv->clk);
  37. writew(timeout & 0xffff, priv->base + REG_WDT_MAX_PRD_L);
  38. writew((timeout >> 16) & 0xffff, priv->base + REG_WDT_MAX_PRD_H);
  39. writew(1, priv->base + REG_WDT_CLR);
  40. return 0;
  41. }
  42. static int msc313e_wdt_ping(struct watchdog_device *wdev)
  43. {
  44. struct msc313e_wdt_priv *priv = watchdog_get_drvdata(wdev);
  45. writew(1, priv->base + REG_WDT_CLR);
  46. return 0;
  47. }
  48. static int msc313e_wdt_stop(struct watchdog_device *wdev)
  49. {
  50. struct msc313e_wdt_priv *priv = watchdog_get_drvdata(wdev);
  51. writew(0, priv->base + REG_WDT_MAX_PRD_L);
  52. writew(0, priv->base + REG_WDT_MAX_PRD_H);
  53. writew(0, priv->base + REG_WDT_CLR);
  54. clk_disable_unprepare(priv->clk);
  55. return 0;
  56. }
  57. static int msc313e_wdt_settimeout(struct watchdog_device *wdev, unsigned int new_time)
  58. {
  59. wdev->timeout = new_time;
  60. return msc313e_wdt_start(wdev);
  61. }
  62. static const struct watchdog_info msc313e_wdt_ident = {
  63. .identity = "MSC313e watchdog",
  64. .options = WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT,
  65. };
  66. static const struct watchdog_ops msc313e_wdt_ops = {
  67. .owner = THIS_MODULE,
  68. .start = msc313e_wdt_start,
  69. .stop = msc313e_wdt_stop,
  70. .ping = msc313e_wdt_ping,
  71. .set_timeout = msc313e_wdt_settimeout,
  72. };
  73. static const struct of_device_id msc313e_wdt_of_match[] = {
  74. { .compatible = "mstar,msc313e-wdt", },
  75. { /* sentinel */ }
  76. };
  77. MODULE_DEVICE_TABLE(of, msc313e_wdt_of_match);
  78. static int msc313e_wdt_probe(struct platform_device *pdev)
  79. {
  80. struct device *dev = &pdev->dev;
  81. struct msc313e_wdt_priv *priv;
  82. priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
  83. if (!priv)
  84. return -ENOMEM;
  85. priv->base = devm_platform_ioremap_resource(pdev, 0);
  86. if (IS_ERR(priv->base))
  87. return PTR_ERR(priv->base);
  88. priv->clk = devm_clk_get(dev, NULL);
  89. if (IS_ERR(priv->clk)) {
  90. dev_err(dev, "No input clock\n");
  91. return PTR_ERR(priv->clk);
  92. }
  93. priv->wdev.info = &msc313e_wdt_ident,
  94. priv->wdev.ops = &msc313e_wdt_ops,
  95. priv->wdev.parent = dev;
  96. priv->wdev.min_timeout = MSC313E_WDT_MIN_TIMEOUT;
  97. priv->wdev.max_timeout = U32_MAX / clk_get_rate(priv->clk);
  98. priv->wdev.timeout = MSC313E_WDT_DEFAULT_TIMEOUT;
  99. /* If the period is non-zero the WDT is running */
  100. if (readw(priv->base + REG_WDT_MAX_PRD_L) | (readw(priv->base + REG_WDT_MAX_PRD_H) << 16))
  101. set_bit(WDOG_HW_RUNNING, &priv->wdev.status);
  102. watchdog_set_drvdata(&priv->wdev, priv);
  103. watchdog_init_timeout(&priv->wdev, timeout, dev);
  104. watchdog_stop_on_reboot(&priv->wdev);
  105. watchdog_stop_on_unregister(&priv->wdev);
  106. return devm_watchdog_register_device(dev, &priv->wdev);
  107. }
  108. static int __maybe_unused msc313e_wdt_suspend(struct device *dev)
  109. {
  110. struct msc313e_wdt_priv *priv = dev_get_drvdata(dev);
  111. if (watchdog_active(&priv->wdev))
  112. msc313e_wdt_stop(&priv->wdev);
  113. return 0;
  114. }
  115. static int __maybe_unused msc313e_wdt_resume(struct device *dev)
  116. {
  117. struct msc313e_wdt_priv *priv = dev_get_drvdata(dev);
  118. if (watchdog_active(&priv->wdev))
  119. msc313e_wdt_start(&priv->wdev);
  120. return 0;
  121. }
  122. static SIMPLE_DEV_PM_OPS(msc313e_wdt_pm_ops, msc313e_wdt_suspend, msc313e_wdt_resume);
  123. static struct platform_driver msc313e_wdt_driver = {
  124. .driver = {
  125. .name = "msc313e-wdt",
  126. .of_match_table = msc313e_wdt_of_match,
  127. .pm = &msc313e_wdt_pm_ops,
  128. },
  129. .probe = msc313e_wdt_probe,
  130. };
  131. module_platform_driver(msc313e_wdt_driver);
  132. MODULE_AUTHOR("Daniel Palmer <[email protected]>");
  133. MODULE_DESCRIPTION("Watchdog driver for MStar MSC313e");
  134. MODULE_LICENSE("GPL v2");