bcm47xx_wdt.c 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. /*
  2. * Watchdog driver for Broadcom BCM47XX
  3. *
  4. * Copyright (C) 2008 Aleksandar Radovanovic <biblbroks@sezampro.rs>
  5. * Copyright (C) 2009 Matthieu CASTET <castet.matthieu@free.fr>
  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. #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  13. #include <linux/bitops.h>
  14. #include <linux/errno.h>
  15. #include <linux/init.h>
  16. #include <linux/kernel.h>
  17. #include <linux/module.h>
  18. #include <linux/moduleparam.h>
  19. #include <linux/reboot.h>
  20. #include <linux/types.h>
  21. #include <linux/watchdog.h>
  22. #include <linux/timer.h>
  23. #include <linux/jiffies.h>
  24. #include <linux/ssb/ssb_embedded.h>
  25. #include <asm/mach-bcm47xx/bcm47xx.h>
  26. #define DRV_NAME "bcm47xx_wdt"
  27. #define WDT_DEFAULT_TIME 30 /* seconds */
  28. #define WDT_MAX_TIME 255 /* seconds */
  29. static int wdt_time = WDT_DEFAULT_TIME;
  30. static bool nowayout = WATCHDOG_NOWAYOUT;
  31. module_param(wdt_time, int, 0);
  32. MODULE_PARM_DESC(wdt_time, "Watchdog time in seconds. (default="
  33. __MODULE_STRING(WDT_DEFAULT_TIME) ")");
  34. module_param(nowayout, bool, 0);
  35. MODULE_PARM_DESC(nowayout,
  36. "Watchdog cannot be stopped once started (default="
  37. __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
  38. static struct timer_list wdt_timer;
  39. static atomic_t ticks;
  40. static inline void bcm47xx_wdt_hw_start(void)
  41. {
  42. /* this is 2,5s on 100Mhz clock and 2s on 133 Mhz */
  43. switch (bcm47xx_bus_type) {
  44. #ifdef CONFIG_BCM47XX_SSB
  45. case BCM47XX_BUS_TYPE_SSB:
  46. ssb_watchdog_timer_set(&bcm47xx_bus.ssb, 0xfffffff);
  47. break;
  48. #endif
  49. #ifdef CONFIG_BCM47XX_BCMA
  50. case BCM47XX_BUS_TYPE_BCMA:
  51. bcma_chipco_watchdog_timer_set(&bcm47xx_bus.bcma.bus.drv_cc,
  52. 0xfffffff);
  53. break;
  54. #endif
  55. }
  56. }
  57. static inline int bcm47xx_wdt_hw_stop(void)
  58. {
  59. switch (bcm47xx_bus_type) {
  60. #ifdef CONFIG_BCM47XX_SSB
  61. case BCM47XX_BUS_TYPE_SSB:
  62. return ssb_watchdog_timer_set(&bcm47xx_bus.ssb, 0);
  63. #endif
  64. #ifdef CONFIG_BCM47XX_BCMA
  65. case BCM47XX_BUS_TYPE_BCMA:
  66. bcma_chipco_watchdog_timer_set(&bcm47xx_bus.bcma.bus.drv_cc, 0);
  67. return 0;
  68. #endif
  69. }
  70. return -EINVAL;
  71. }
  72. static void bcm47xx_timer_tick(unsigned long unused)
  73. {
  74. if (!atomic_dec_and_test(&ticks)) {
  75. bcm47xx_wdt_hw_start();
  76. mod_timer(&wdt_timer, jiffies + HZ);
  77. } else {
  78. pr_crit("Watchdog will fire soon!!!\n");
  79. }
  80. }
  81. static int bcm47xx_wdt_keepalive(struct watchdog_device *wdd)
  82. {
  83. atomic_set(&ticks, wdt_time);
  84. return 0;
  85. }
  86. static int bcm47xx_wdt_start(struct watchdog_device *wdd)
  87. {
  88. bcm47xx_wdt_pet();
  89. bcm47xx_timer_tick(0);
  90. return 0;
  91. }
  92. static int bcm47xx_wdt_stop(struct watchdog_device *wdd)
  93. {
  94. del_timer_sync(&wdt_timer);
  95. bcm47xx_wdt_hw_stop();
  96. return 0;
  97. }
  98. static int bcm47xx_wdt_set_timeout(struct watchdog_device *wdd,
  99. unsigned int new_time)
  100. {
  101. if ((new_time <= 0) || (new_time > WDT_MAX_TIME))
  102. return -EINVAL;
  103. wdt_time = new_time;
  104. return 0;
  105. }
  106. static const struct watchdog_info bcm47xx_wdt_info = {
  107. .identity = DRV_NAME,
  108. .options = WDIOF_SETTIMEOUT |
  109. WDIOF_KEEPALIVEPING |
  110. WDIOF_MAGICCLOSE,
  111. };
  112. static int bcm47xx_wdt_notify_sys(struct notifier_block *this,
  113. unsigned long code, void *unused)
  114. {
  115. if (code == SYS_DOWN || code == SYS_HALT)
  116. bcm47xx_wdt_stop();
  117. return NOTIFY_DONE;
  118. }
  119. static struct watchdog_ops bcm47xx_wdt_ops = {
  120. .owner = THIS_MODULE,
  121. .start = bcm47xx_wdt_start,
  122. .stop = bcm47xx_wdt_stop,
  123. .ping = bcm47xx_wdt_keepalive,
  124. .set_timeout = bcm47xx_wdt_set_timeout,
  125. };
  126. static struct watchdog_device bcm47xx_wdt_wdd = {
  127. .info = &bcm47xx_wdt_info,
  128. .ops = &bcm47xx_wdt_ops,
  129. };
  130. static struct notifier_block bcm47xx_wdt_notifier = {
  131. .notifier_call = bcm47xx_wdt_notify_sys,
  132. };
  133. static int __init bcm47xx_wdt_init(void)
  134. {
  135. int ret;
  136. if (bcm47xx_wdt_hw_stop() < 0)
  137. return -ENODEV;
  138. setup_timer(&wdt_timer, bcm47xx_timer_tick, 0L);
  139. if (bcm47xx_wdt_settimeout(wdt_time)) {
  140. bcm47xx_wdt_settimeout(WDT_DEFAULT_TIME);
  141. pr_info("wdt_time value must be 0 < wdt_time < %d, using %d\n",
  142. (WDT_MAX_TIME + 1), wdt_time);
  143. }
  144. watchdog_set_nowayout(&bcm47xx_wdt_wdd, nowayout);
  145. ret = register_reboot_notifier(&bcm47xx_wdt_notifier);
  146. if (ret)
  147. return ret;
  148. ret = watchdog_register_device(&bcm47xx_wdt_wdd);
  149. if (ret) {
  150. unregister_reboot_notifier(&bcm47xx_wdt_notifier);
  151. return ret;
  152. }
  153. pr_info("BCM47xx Watchdog Timer enabled (%d seconds%s)\n",
  154. wdt_time, nowayout ? ", nowayout" : "");
  155. return 0;
  156. }
  157. static void __exit bcm47xx_wdt_exit(void)
  158. {
  159. watchdog_unregister_device(&bcm47xx_wdt_wdd);
  160. unregister_reboot_notifier(&bcm47xx_wdt_notifier);
  161. }
  162. module_init(bcm47xx_wdt_init);
  163. module_exit(bcm47xx_wdt_exit);
  164. MODULE_AUTHOR("Aleksandar Radovanovic");
  165. MODULE_DESCRIPTION("Watchdog driver for Broadcom BCM47xx");
  166. MODULE_LICENSE("GPL");