ts72xx_wdt.c 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448
  1. /*
  2. * Watchdog driver for Technologic Systems TS-72xx based SBCs
  3. * (TS-7200, TS-7250 and TS-7260). These boards have external
  4. * glue logic CPLD chip, which includes programmable watchdog
  5. * timer.
  6. *
  7. * Copyright (c) 2009 Mika Westerberg <mika.westerberg@iki.fi>
  8. *
  9. * This driver is based on ep93xx_wdt and wm831x_wdt drivers.
  10. *
  11. * This file is licensed under the terms of the GNU General Public
  12. * License version 2. This program is licensed "as is" without any
  13. * warranty of any kind, whether express or implied.
  14. */
  15. #include <linux/fs.h>
  16. #include <linux/io.h>
  17. #include <linux/module.h>
  18. #include <linux/moduleparam.h>
  19. #include <linux/miscdevice.h>
  20. #include <linux/mutex.h>
  21. #include <linux/platform_device.h>
  22. #include <linux/slab.h>
  23. #include <linux/watchdog.h>
  24. #include <linux/uaccess.h>
  25. #define TS72XX_WDT_FEED_VAL 0x05
  26. #define TS72XX_WDT_DEFAULT_TIMEOUT 8
  27. static int timeout = TS72XX_WDT_DEFAULT_TIMEOUT;
  28. module_param(timeout, int, 0);
  29. MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds. "
  30. "(1 <= timeout <= 8, default="
  31. __MODULE_STRING(TS72XX_WDT_DEFAULT_TIMEOUT)
  32. ")");
  33. static bool nowayout = WATCHDOG_NOWAYOUT;
  34. module_param(nowayout, bool, 0);
  35. MODULE_PARM_DESC(nowayout, "Disable watchdog shutdown on close");
  36. /**
  37. * struct ts72xx_wdt - watchdog control structure
  38. * @lock: lock that protects this structure
  39. * @regval: watchdog timeout value suitable for control register
  40. * @flags: flags controlling watchdog device state
  41. * @control_reg: watchdog control register
  42. * @feed_reg: watchdog feed register
  43. * @pdev: back pointer to platform dev
  44. */
  45. struct ts72xx_wdt {
  46. struct mutex lock;
  47. int regval;
  48. #define TS72XX_WDT_BUSY_FLAG 1
  49. #define TS72XX_WDT_EXPECT_CLOSE_FLAG 2
  50. int flags;
  51. void __iomem *control_reg;
  52. void __iomem *feed_reg;
  53. struct platform_device *pdev;
  54. };
  55. static struct platform_device *ts72xx_wdt_pdev;
  56. /*
  57. * TS-72xx Watchdog supports following timeouts (value written
  58. * to control register):
  59. * value description
  60. * -------------------------
  61. * 0x00 watchdog disabled
  62. * 0x01 250ms
  63. * 0x02 500ms
  64. * 0x03 1s
  65. * 0x04 reserved
  66. * 0x05 2s
  67. * 0x06 4s
  68. * 0x07 8s
  69. *
  70. * Timeouts below 1s are not very usable so we don't
  71. * allow them at all.
  72. *
  73. * We provide two functions that convert between these:
  74. * timeout_to_regval() and regval_to_timeout().
  75. */
  76. static const struct {
  77. int timeout;
  78. int regval;
  79. } ts72xx_wdt_map[] = {
  80. { 1, 3 },
  81. { 2, 5 },
  82. { 4, 6 },
  83. { 8, 7 },
  84. };
  85. /**
  86. * timeout_to_regval() - converts given timeout to control register value
  87. * @new_timeout: timeout in seconds to be converted
  88. *
  89. * Function converts given @new_timeout into valid value that can
  90. * be programmed into watchdog control register. When conversion is
  91. * not possible, function returns %-EINVAL.
  92. */
  93. static int timeout_to_regval(int new_timeout)
  94. {
  95. int i;
  96. /* first limit it to 1 - 8 seconds */
  97. new_timeout = clamp_val(new_timeout, 1, 8);
  98. for (i = 0; i < ARRAY_SIZE(ts72xx_wdt_map); i++) {
  99. if (ts72xx_wdt_map[i].timeout >= new_timeout)
  100. return ts72xx_wdt_map[i].regval;
  101. }
  102. return -EINVAL;
  103. }
  104. /**
  105. * regval_to_timeout() - converts control register value to timeout
  106. * @regval: control register value to be converted
  107. *
  108. * Function converts given @regval to timeout in seconds (1, 2, 4 or 8).
  109. * If @regval cannot be converted, function returns %-EINVAL.
  110. */
  111. static int regval_to_timeout(int regval)
  112. {
  113. int i;
  114. for (i = 0; i < ARRAY_SIZE(ts72xx_wdt_map); i++) {
  115. if (ts72xx_wdt_map[i].regval == regval)
  116. return ts72xx_wdt_map[i].timeout;
  117. }
  118. return -EINVAL;
  119. }
  120. /**
  121. * ts72xx_wdt_kick() - kick the watchdog
  122. * @wdt: watchdog to be kicked
  123. *
  124. * Called with @wdt->lock held.
  125. */
  126. static inline void ts72xx_wdt_kick(struct ts72xx_wdt *wdt)
  127. {
  128. __raw_writeb(TS72XX_WDT_FEED_VAL, wdt->feed_reg);
  129. }
  130. /**
  131. * ts72xx_wdt_start() - starts the watchdog timer
  132. * @wdt: watchdog to be started
  133. *
  134. * This function programs timeout to watchdog timer
  135. * and starts it.
  136. *
  137. * Called with @wdt->lock held.
  138. */
  139. static void ts72xx_wdt_start(struct ts72xx_wdt *wdt)
  140. {
  141. /*
  142. * To program the wdt, it first must be "fed" and
  143. * only after that (within 30 usecs) the configuration
  144. * can be changed.
  145. */
  146. ts72xx_wdt_kick(wdt);
  147. __raw_writeb((u8)wdt->regval, wdt->control_reg);
  148. }
  149. /**
  150. * ts72xx_wdt_stop() - stops the watchdog timer
  151. * @wdt: watchdog to be stopped
  152. *
  153. * Called with @wdt->lock held.
  154. */
  155. static void ts72xx_wdt_stop(struct ts72xx_wdt *wdt)
  156. {
  157. ts72xx_wdt_kick(wdt);
  158. __raw_writeb(0, wdt->control_reg);
  159. }
  160. static int ts72xx_wdt_open(struct inode *inode, struct file *file)
  161. {
  162. struct ts72xx_wdt *wdt = platform_get_drvdata(ts72xx_wdt_pdev);
  163. int regval;
  164. /*
  165. * Try to convert default timeout to valid register
  166. * value first.
  167. */
  168. regval = timeout_to_regval(timeout);
  169. if (regval < 0) {
  170. dev_err(&wdt->pdev->dev,
  171. "failed to convert timeout (%d) to register value\n",
  172. timeout);
  173. return regval;
  174. }
  175. if (mutex_lock_interruptible(&wdt->lock))
  176. return -ERESTARTSYS;
  177. if ((wdt->flags & TS72XX_WDT_BUSY_FLAG) != 0) {
  178. mutex_unlock(&wdt->lock);
  179. return -EBUSY;
  180. }
  181. wdt->flags = TS72XX_WDT_BUSY_FLAG;
  182. wdt->regval = regval;
  183. file->private_data = wdt;
  184. ts72xx_wdt_start(wdt);
  185. mutex_unlock(&wdt->lock);
  186. return nonseekable_open(inode, file);
  187. }
  188. static int ts72xx_wdt_release(struct inode *inode, struct file *file)
  189. {
  190. struct ts72xx_wdt *wdt = file->private_data;
  191. if (mutex_lock_interruptible(&wdt->lock))
  192. return -ERESTARTSYS;
  193. if ((wdt->flags & TS72XX_WDT_EXPECT_CLOSE_FLAG) != 0) {
  194. ts72xx_wdt_stop(wdt);
  195. } else {
  196. dev_warn(&wdt->pdev->dev,
  197. "TS-72XX WDT device closed unexpectly. "
  198. "Watchdog timer will not stop!\n");
  199. /*
  200. * Kick it one more time, to give userland some time
  201. * to recover (for example, respawning the kicker
  202. * daemon).
  203. */
  204. ts72xx_wdt_kick(wdt);
  205. }
  206. wdt->flags = 0;
  207. mutex_unlock(&wdt->lock);
  208. return 0;
  209. }
  210. static ssize_t ts72xx_wdt_write(struct file *file,
  211. const char __user *data,
  212. size_t len,
  213. loff_t *ppos)
  214. {
  215. struct ts72xx_wdt *wdt = file->private_data;
  216. if (!len)
  217. return 0;
  218. if (mutex_lock_interruptible(&wdt->lock))
  219. return -ERESTARTSYS;
  220. ts72xx_wdt_kick(wdt);
  221. /*
  222. * Support for magic character closing. User process
  223. * writes 'V' into the device, just before it is closed.
  224. * This means that we know that the wdt timer can be
  225. * stopped after user closes the device.
  226. */
  227. if (!nowayout) {
  228. int i;
  229. for (i = 0; i < len; i++) {
  230. char c;
  231. /* In case it was set long ago */
  232. wdt->flags &= ~TS72XX_WDT_EXPECT_CLOSE_FLAG;
  233. if (get_user(c, data + i)) {
  234. mutex_unlock(&wdt->lock);
  235. return -EFAULT;
  236. }
  237. if (c == 'V') {
  238. wdt->flags |= TS72XX_WDT_EXPECT_CLOSE_FLAG;
  239. break;
  240. }
  241. }
  242. }
  243. mutex_unlock(&wdt->lock);
  244. return len;
  245. }
  246. static const struct watchdog_info winfo = {
  247. .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT |
  248. WDIOF_MAGICCLOSE,
  249. .firmware_version = 1,
  250. .identity = "TS-72XX WDT",
  251. };
  252. static long ts72xx_wdt_ioctl(struct file *file, unsigned int cmd,
  253. unsigned long arg)
  254. {
  255. struct ts72xx_wdt *wdt = file->private_data;
  256. void __user *argp = (void __user *)arg;
  257. int __user *p = (int __user *)argp;
  258. int error = 0;
  259. if (mutex_lock_interruptible(&wdt->lock))
  260. return -ERESTARTSYS;
  261. switch (cmd) {
  262. case WDIOC_GETSUPPORT:
  263. if (copy_to_user(argp, &winfo, sizeof(winfo)))
  264. error = -EFAULT;
  265. break;
  266. case WDIOC_GETSTATUS:
  267. case WDIOC_GETBOOTSTATUS:
  268. error = put_user(0, p);
  269. break;
  270. case WDIOC_KEEPALIVE:
  271. ts72xx_wdt_kick(wdt);
  272. break;
  273. case WDIOC_SETOPTIONS: {
  274. int options;
  275. error = get_user(options, p);
  276. if (error)
  277. break;
  278. error = -EINVAL;
  279. if ((options & WDIOS_DISABLECARD) != 0) {
  280. ts72xx_wdt_stop(wdt);
  281. error = 0;
  282. }
  283. if ((options & WDIOS_ENABLECARD) != 0) {
  284. ts72xx_wdt_start(wdt);
  285. error = 0;
  286. }
  287. break;
  288. }
  289. case WDIOC_SETTIMEOUT: {
  290. int new_timeout;
  291. int regval;
  292. error = get_user(new_timeout, p);
  293. if (error)
  294. break;
  295. regval = timeout_to_regval(new_timeout);
  296. if (regval < 0) {
  297. error = regval;
  298. break;
  299. }
  300. ts72xx_wdt_stop(wdt);
  301. wdt->regval = regval;
  302. ts72xx_wdt_start(wdt);
  303. /*FALLTHROUGH*/
  304. }
  305. case WDIOC_GETTIMEOUT:
  306. error = put_user(regval_to_timeout(wdt->regval), p);
  307. break;
  308. default:
  309. error = -ENOTTY;
  310. break;
  311. }
  312. mutex_unlock(&wdt->lock);
  313. return error;
  314. }
  315. static const struct file_operations ts72xx_wdt_fops = {
  316. .owner = THIS_MODULE,
  317. .llseek = no_llseek,
  318. .open = ts72xx_wdt_open,
  319. .release = ts72xx_wdt_release,
  320. .write = ts72xx_wdt_write,
  321. .unlocked_ioctl = ts72xx_wdt_ioctl,
  322. };
  323. static struct miscdevice ts72xx_wdt_miscdev = {
  324. .minor = WATCHDOG_MINOR,
  325. .name = "watchdog",
  326. .fops = &ts72xx_wdt_fops,
  327. };
  328. static int ts72xx_wdt_probe(struct platform_device *pdev)
  329. {
  330. struct ts72xx_wdt *wdt;
  331. struct resource *r1, *r2;
  332. int error = 0;
  333. wdt = devm_kzalloc(&pdev->dev, sizeof(struct ts72xx_wdt), GFP_KERNEL);
  334. if (!wdt)
  335. return -ENOMEM;
  336. r1 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  337. wdt->control_reg = devm_ioremap_resource(&pdev->dev, r1);
  338. if (IS_ERR(wdt->control_reg))
  339. return PTR_ERR(wdt->control_reg);
  340. r2 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
  341. wdt->feed_reg = devm_ioremap_resource(&pdev->dev, r2);
  342. if (IS_ERR(wdt->feed_reg))
  343. return PTR_ERR(wdt->feed_reg);
  344. platform_set_drvdata(pdev, wdt);
  345. ts72xx_wdt_pdev = pdev;
  346. wdt->pdev = pdev;
  347. mutex_init(&wdt->lock);
  348. /* make sure that the watchdog is disabled */
  349. ts72xx_wdt_stop(wdt);
  350. error = misc_register(&ts72xx_wdt_miscdev);
  351. if (error) {
  352. dev_err(&pdev->dev, "failed to register miscdev\n");
  353. return error;
  354. }
  355. dev_info(&pdev->dev, "TS-72xx Watchdog driver\n");
  356. return 0;
  357. }
  358. static int ts72xx_wdt_remove(struct platform_device *pdev)
  359. {
  360. misc_deregister(&ts72xx_wdt_miscdev);
  361. return 0;
  362. }
  363. static struct platform_driver ts72xx_wdt_driver = {
  364. .probe = ts72xx_wdt_probe,
  365. .remove = ts72xx_wdt_remove,
  366. .driver = {
  367. .name = "ts72xx-wdt",
  368. },
  369. };
  370. module_platform_driver(ts72xx_wdt_driver);
  371. MODULE_AUTHOR("Mika Westerberg <mika.westerberg@iki.fi>");
  372. MODULE_DESCRIPTION("TS-72xx SBC Watchdog");
  373. MODULE_LICENSE("GPL");
  374. MODULE_ALIAS("platform:ts72xx-wdt");