kempld_wdt.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581
  1. /*
  2. * Kontron PLD watchdog driver
  3. *
  4. * Copyright (c) 2010-2013 Kontron Europe GmbH
  5. * Author: Michael Brunner <michael.brunner@kontron.com>
  6. *
  7. * This program is free software; you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License 2 as published
  9. * by the Free Software Foundation.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * Note: From the PLD watchdog point of view timeout and pretimeout are
  17. * defined differently than in the kernel.
  18. * First the pretimeout stage runs out before the timeout stage gets
  19. * active.
  20. *
  21. * Kernel/API: P-----| pretimeout
  22. * |-----------------------T timeout
  23. * Watchdog: |-----------------P pretimeout_stage
  24. * |-----T timeout_stage
  25. */
  26. #include <linux/module.h>
  27. #include <linux/moduleparam.h>
  28. #include <linux/uaccess.h>
  29. #include <linux/watchdog.h>
  30. #include <linux/platform_device.h>
  31. #include <linux/mfd/kempld.h>
  32. #define KEMPLD_WDT_STAGE_TIMEOUT(x) (0x1b + (x) * 4)
  33. #define KEMPLD_WDT_STAGE_CFG(x) (0x18 + (x))
  34. #define STAGE_CFG_GET_PRESCALER(x) (((x) & 0x30) >> 4)
  35. #define STAGE_CFG_SET_PRESCALER(x) (((x) & 0x3) << 4)
  36. #define STAGE_CFG_PRESCALER_MASK 0x30
  37. #define STAGE_CFG_ACTION_MASK 0x7
  38. #define STAGE_CFG_ASSERT (1 << 3)
  39. #define KEMPLD_WDT_MAX_STAGES 2
  40. #define KEMPLD_WDT_KICK 0x16
  41. #define KEMPLD_WDT_CFG 0x17
  42. #define KEMPLD_WDT_CFG_ENABLE 0x10
  43. #define KEMPLD_WDT_CFG_ENABLE_LOCK 0x8
  44. #define KEMPLD_WDT_CFG_GLOBAL_LOCK 0x80
  45. enum {
  46. ACTION_NONE = 0,
  47. ACTION_RESET,
  48. ACTION_NMI,
  49. ACTION_SMI,
  50. ACTION_SCI,
  51. ACTION_DELAY,
  52. };
  53. enum {
  54. STAGE_TIMEOUT = 0,
  55. STAGE_PRETIMEOUT,
  56. };
  57. enum {
  58. PRESCALER_21 = 0,
  59. PRESCALER_17,
  60. PRESCALER_12,
  61. };
  62. static const u32 kempld_prescaler[] = {
  63. [PRESCALER_21] = (1 << 21) - 1,
  64. [PRESCALER_17] = (1 << 17) - 1,
  65. [PRESCALER_12] = (1 << 12) - 1,
  66. 0,
  67. };
  68. struct kempld_wdt_stage {
  69. unsigned int id;
  70. u32 mask;
  71. };
  72. struct kempld_wdt_data {
  73. struct kempld_device_data *pld;
  74. struct watchdog_device wdd;
  75. unsigned int pretimeout;
  76. struct kempld_wdt_stage stage[KEMPLD_WDT_MAX_STAGES];
  77. #ifdef CONFIG_PM
  78. u8 pm_status_store;
  79. #endif
  80. };
  81. #define DEFAULT_TIMEOUT 30 /* seconds */
  82. #define DEFAULT_PRETIMEOUT 0
  83. static unsigned int timeout = DEFAULT_TIMEOUT;
  84. module_param(timeout, uint, 0);
  85. MODULE_PARM_DESC(timeout,
  86. "Watchdog timeout in seconds. (>=0, default="
  87. __MODULE_STRING(DEFAULT_TIMEOUT) ")");
  88. static unsigned int pretimeout = DEFAULT_PRETIMEOUT;
  89. module_param(pretimeout, uint, 0);
  90. MODULE_PARM_DESC(pretimeout,
  91. "Watchdog pretimeout in seconds. (>=0, default="
  92. __MODULE_STRING(DEFAULT_PRETIMEOUT) ")");
  93. static bool nowayout = WATCHDOG_NOWAYOUT;
  94. module_param(nowayout, bool, 0);
  95. MODULE_PARM_DESC(nowayout,
  96. "Watchdog cannot be stopped once started (default="
  97. __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
  98. static int kempld_wdt_set_stage_action(struct kempld_wdt_data *wdt_data,
  99. struct kempld_wdt_stage *stage,
  100. u8 action)
  101. {
  102. struct kempld_device_data *pld = wdt_data->pld;
  103. u8 stage_cfg;
  104. if (!stage || !stage->mask)
  105. return -EINVAL;
  106. kempld_get_mutex(pld);
  107. stage_cfg = kempld_read8(pld, KEMPLD_WDT_STAGE_CFG(stage->id));
  108. stage_cfg &= ~STAGE_CFG_ACTION_MASK;
  109. stage_cfg |= (action & STAGE_CFG_ACTION_MASK);
  110. if (action == ACTION_RESET)
  111. stage_cfg |= STAGE_CFG_ASSERT;
  112. else
  113. stage_cfg &= ~STAGE_CFG_ASSERT;
  114. kempld_write8(pld, KEMPLD_WDT_STAGE_CFG(stage->id), stage_cfg);
  115. kempld_release_mutex(pld);
  116. return 0;
  117. }
  118. static int kempld_wdt_set_stage_timeout(struct kempld_wdt_data *wdt_data,
  119. struct kempld_wdt_stage *stage,
  120. unsigned int timeout)
  121. {
  122. struct kempld_device_data *pld = wdt_data->pld;
  123. u32 prescaler;
  124. u64 stage_timeout64;
  125. u32 stage_timeout;
  126. u32 remainder;
  127. u8 stage_cfg;
  128. prescaler = kempld_prescaler[PRESCALER_21];
  129. if (!stage)
  130. return -EINVAL;
  131. stage_timeout64 = (u64)timeout * pld->pld_clock;
  132. remainder = do_div(stage_timeout64, prescaler);
  133. if (remainder)
  134. stage_timeout64++;
  135. if (stage_timeout64 > stage->mask)
  136. return -EINVAL;
  137. stage_timeout = stage_timeout64 & stage->mask;
  138. kempld_get_mutex(pld);
  139. stage_cfg = kempld_read8(pld, KEMPLD_WDT_STAGE_CFG(stage->id));
  140. stage_cfg &= ~STAGE_CFG_PRESCALER_MASK;
  141. stage_cfg |= STAGE_CFG_SET_PRESCALER(PRESCALER_21);
  142. kempld_write8(pld, KEMPLD_WDT_STAGE_CFG(stage->id), stage_cfg);
  143. kempld_write32(pld, KEMPLD_WDT_STAGE_TIMEOUT(stage->id),
  144. stage_timeout);
  145. kempld_release_mutex(pld);
  146. return 0;
  147. }
  148. /*
  149. * kempld_get_mutex must be called prior to calling this function.
  150. */
  151. static unsigned int kempld_wdt_get_timeout(struct kempld_wdt_data *wdt_data,
  152. struct kempld_wdt_stage *stage)
  153. {
  154. struct kempld_device_data *pld = wdt_data->pld;
  155. unsigned int timeout;
  156. u64 stage_timeout;
  157. u32 prescaler;
  158. u32 remainder;
  159. u8 stage_cfg;
  160. if (!stage->mask)
  161. return 0;
  162. stage_cfg = kempld_read8(pld, KEMPLD_WDT_STAGE_CFG(stage->id));
  163. stage_timeout = kempld_read32(pld, KEMPLD_WDT_STAGE_TIMEOUT(stage->id));
  164. prescaler = kempld_prescaler[STAGE_CFG_GET_PRESCALER(stage_cfg)];
  165. stage_timeout = (stage_timeout & stage->mask) * prescaler;
  166. remainder = do_div(stage_timeout, pld->pld_clock);
  167. if (remainder)
  168. stage_timeout++;
  169. timeout = stage_timeout;
  170. WARN_ON_ONCE(timeout != stage_timeout);
  171. return timeout;
  172. }
  173. static int kempld_wdt_set_timeout(struct watchdog_device *wdd,
  174. unsigned int timeout)
  175. {
  176. struct kempld_wdt_data *wdt_data = watchdog_get_drvdata(wdd);
  177. struct kempld_wdt_stage *pretimeout_stage;
  178. struct kempld_wdt_stage *timeout_stage;
  179. int ret;
  180. timeout_stage = &wdt_data->stage[STAGE_TIMEOUT];
  181. pretimeout_stage = &wdt_data->stage[STAGE_PRETIMEOUT];
  182. if (pretimeout_stage->mask && wdt_data->pretimeout > 0)
  183. timeout = wdt_data->pretimeout;
  184. ret = kempld_wdt_set_stage_action(wdt_data, timeout_stage,
  185. ACTION_RESET);
  186. if (ret)
  187. return ret;
  188. ret = kempld_wdt_set_stage_timeout(wdt_data, timeout_stage,
  189. timeout);
  190. if (ret)
  191. return ret;
  192. wdd->timeout = timeout;
  193. return 0;
  194. }
  195. static int kempld_wdt_set_pretimeout(struct watchdog_device *wdd,
  196. unsigned int pretimeout)
  197. {
  198. struct kempld_wdt_data *wdt_data = watchdog_get_drvdata(wdd);
  199. struct kempld_wdt_stage *pretimeout_stage;
  200. u8 action = ACTION_NONE;
  201. int ret;
  202. pretimeout_stage = &wdt_data->stage[STAGE_PRETIMEOUT];
  203. if (!pretimeout_stage->mask)
  204. return -ENXIO;
  205. if (pretimeout > wdd->timeout)
  206. return -EINVAL;
  207. if (pretimeout > 0)
  208. action = ACTION_NMI;
  209. ret = kempld_wdt_set_stage_action(wdt_data, pretimeout_stage,
  210. action);
  211. if (ret)
  212. return ret;
  213. ret = kempld_wdt_set_stage_timeout(wdt_data, pretimeout_stage,
  214. wdd->timeout - pretimeout);
  215. if (ret)
  216. return ret;
  217. wdt_data->pretimeout = pretimeout;
  218. return 0;
  219. }
  220. static void kempld_wdt_update_timeouts(struct kempld_wdt_data *wdt_data)
  221. {
  222. struct kempld_device_data *pld = wdt_data->pld;
  223. struct kempld_wdt_stage *pretimeout_stage;
  224. struct kempld_wdt_stage *timeout_stage;
  225. unsigned int pretimeout, timeout;
  226. pretimeout_stage = &wdt_data->stage[STAGE_PRETIMEOUT];
  227. timeout_stage = &wdt_data->stage[STAGE_TIMEOUT];
  228. kempld_get_mutex(pld);
  229. pretimeout = kempld_wdt_get_timeout(wdt_data, pretimeout_stage);
  230. timeout = kempld_wdt_get_timeout(wdt_data, timeout_stage);
  231. kempld_release_mutex(pld);
  232. if (pretimeout)
  233. wdt_data->pretimeout = timeout;
  234. else
  235. wdt_data->pretimeout = 0;
  236. wdt_data->wdd.timeout = pretimeout + timeout;
  237. }
  238. static int kempld_wdt_start(struct watchdog_device *wdd)
  239. {
  240. struct kempld_wdt_data *wdt_data = watchdog_get_drvdata(wdd);
  241. struct kempld_device_data *pld = wdt_data->pld;
  242. u8 status;
  243. int ret;
  244. ret = kempld_wdt_set_timeout(wdd, wdd->timeout);
  245. if (ret)
  246. return ret;
  247. kempld_get_mutex(pld);
  248. status = kempld_read8(pld, KEMPLD_WDT_CFG);
  249. status |= KEMPLD_WDT_CFG_ENABLE;
  250. kempld_write8(pld, KEMPLD_WDT_CFG, status);
  251. status = kempld_read8(pld, KEMPLD_WDT_CFG);
  252. kempld_release_mutex(pld);
  253. /* Check if the watchdog was enabled */
  254. if (!(status & KEMPLD_WDT_CFG_ENABLE))
  255. return -EACCES;
  256. return 0;
  257. }
  258. static int kempld_wdt_stop(struct watchdog_device *wdd)
  259. {
  260. struct kempld_wdt_data *wdt_data = watchdog_get_drvdata(wdd);
  261. struct kempld_device_data *pld = wdt_data->pld;
  262. u8 status;
  263. kempld_get_mutex(pld);
  264. status = kempld_read8(pld, KEMPLD_WDT_CFG);
  265. status &= ~KEMPLD_WDT_CFG_ENABLE;
  266. kempld_write8(pld, KEMPLD_WDT_CFG, status);
  267. status = kempld_read8(pld, KEMPLD_WDT_CFG);
  268. kempld_release_mutex(pld);
  269. /* Check if the watchdog was disabled */
  270. if (status & KEMPLD_WDT_CFG_ENABLE)
  271. return -EACCES;
  272. return 0;
  273. }
  274. static int kempld_wdt_keepalive(struct watchdog_device *wdd)
  275. {
  276. struct kempld_wdt_data *wdt_data = watchdog_get_drvdata(wdd);
  277. struct kempld_device_data *pld = wdt_data->pld;
  278. kempld_get_mutex(pld);
  279. kempld_write8(pld, KEMPLD_WDT_KICK, 'K');
  280. kempld_release_mutex(pld);
  281. return 0;
  282. }
  283. static long kempld_wdt_ioctl(struct watchdog_device *wdd, unsigned int cmd,
  284. unsigned long arg)
  285. {
  286. struct kempld_wdt_data *wdt_data = watchdog_get_drvdata(wdd);
  287. void __user *argp = (void __user *)arg;
  288. int ret = -ENOIOCTLCMD;
  289. int __user *p = argp;
  290. int new_value;
  291. switch (cmd) {
  292. case WDIOC_SETPRETIMEOUT:
  293. if (get_user(new_value, p))
  294. return -EFAULT;
  295. ret = kempld_wdt_set_pretimeout(wdd, new_value);
  296. if (ret)
  297. return ret;
  298. ret = kempld_wdt_keepalive(wdd);
  299. break;
  300. case WDIOC_GETPRETIMEOUT:
  301. ret = put_user(wdt_data->pretimeout, (int __user *)arg);
  302. break;
  303. }
  304. return ret;
  305. }
  306. static int kempld_wdt_probe_stages(struct watchdog_device *wdd)
  307. {
  308. struct kempld_wdt_data *wdt_data = watchdog_get_drvdata(wdd);
  309. struct kempld_device_data *pld = wdt_data->pld;
  310. struct kempld_wdt_stage *pretimeout_stage;
  311. struct kempld_wdt_stage *timeout_stage;
  312. u8 index, data, data_orig;
  313. u32 mask;
  314. int i, j;
  315. pretimeout_stage = &wdt_data->stage[STAGE_PRETIMEOUT];
  316. timeout_stage = &wdt_data->stage[STAGE_TIMEOUT];
  317. pretimeout_stage->mask = 0;
  318. timeout_stage->mask = 0;
  319. for (i = 0; i < 3; i++) {
  320. index = KEMPLD_WDT_STAGE_TIMEOUT(i);
  321. mask = 0;
  322. kempld_get_mutex(pld);
  323. /* Probe each byte individually. */
  324. for (j = 0; j < 4; j++) {
  325. data_orig = kempld_read8(pld, index + j);
  326. kempld_write8(pld, index + j, 0x00);
  327. data = kempld_read8(pld, index + j);
  328. /* A failed write means this byte is reserved */
  329. if (data != 0x00)
  330. break;
  331. kempld_write8(pld, index + j, data_orig);
  332. mask |= 0xff << (j * 8);
  333. }
  334. kempld_release_mutex(pld);
  335. /* Assign available stages to timeout and pretimeout */
  336. if (!timeout_stage->mask) {
  337. timeout_stage->mask = mask;
  338. timeout_stage->id = i;
  339. } else {
  340. if (pld->feature_mask & KEMPLD_FEATURE_BIT_NMI) {
  341. pretimeout_stage->mask = timeout_stage->mask;
  342. timeout_stage->mask = mask;
  343. pretimeout_stage->id = timeout_stage->id;
  344. timeout_stage->id = i;
  345. }
  346. break;
  347. }
  348. }
  349. if (!timeout_stage->mask)
  350. return -ENODEV;
  351. return 0;
  352. }
  353. static const struct watchdog_info kempld_wdt_info = {
  354. .identity = "KEMPLD Watchdog",
  355. .options = WDIOF_SETTIMEOUT |
  356. WDIOF_KEEPALIVEPING |
  357. WDIOF_MAGICCLOSE |
  358. WDIOF_PRETIMEOUT
  359. };
  360. static const struct watchdog_ops kempld_wdt_ops = {
  361. .owner = THIS_MODULE,
  362. .start = kempld_wdt_start,
  363. .stop = kempld_wdt_stop,
  364. .ping = kempld_wdt_keepalive,
  365. .set_timeout = kempld_wdt_set_timeout,
  366. .ioctl = kempld_wdt_ioctl,
  367. };
  368. static int kempld_wdt_probe(struct platform_device *pdev)
  369. {
  370. struct kempld_device_data *pld = dev_get_drvdata(pdev->dev.parent);
  371. struct kempld_wdt_data *wdt_data;
  372. struct device *dev = &pdev->dev;
  373. struct watchdog_device *wdd;
  374. u8 status;
  375. int ret = 0;
  376. wdt_data = devm_kzalloc(dev, sizeof(*wdt_data), GFP_KERNEL);
  377. if (!wdt_data)
  378. return -ENOMEM;
  379. wdt_data->pld = pld;
  380. wdd = &wdt_data->wdd;
  381. wdd->parent = dev;
  382. kempld_get_mutex(pld);
  383. status = kempld_read8(pld, KEMPLD_WDT_CFG);
  384. kempld_release_mutex(pld);
  385. /* Enable nowayout if watchdog is already locked */
  386. if (status & (KEMPLD_WDT_CFG_ENABLE_LOCK |
  387. KEMPLD_WDT_CFG_GLOBAL_LOCK)) {
  388. if (!nowayout)
  389. dev_warn(dev,
  390. "Forcing nowayout - watchdog lock enabled!\n");
  391. nowayout = true;
  392. }
  393. wdd->info = &kempld_wdt_info;
  394. wdd->ops = &kempld_wdt_ops;
  395. watchdog_set_drvdata(wdd, wdt_data);
  396. watchdog_set_nowayout(wdd, nowayout);
  397. ret = kempld_wdt_probe_stages(wdd);
  398. if (ret)
  399. return ret;
  400. kempld_wdt_set_timeout(wdd, timeout);
  401. kempld_wdt_set_pretimeout(wdd, pretimeout);
  402. /* Check if watchdog is already enabled */
  403. if (status & KEMPLD_WDT_CFG_ENABLE) {
  404. /* Get current watchdog settings */
  405. kempld_wdt_update_timeouts(wdt_data);
  406. dev_info(dev, "Watchdog was already enabled\n");
  407. }
  408. platform_set_drvdata(pdev, wdt_data);
  409. ret = watchdog_register_device(wdd);
  410. if (ret)
  411. return ret;
  412. dev_info(dev, "Watchdog registered with %ds timeout\n", wdd->timeout);
  413. return 0;
  414. }
  415. static void kempld_wdt_shutdown(struct platform_device *pdev)
  416. {
  417. struct kempld_wdt_data *wdt_data = platform_get_drvdata(pdev);
  418. kempld_wdt_stop(&wdt_data->wdd);
  419. }
  420. static int kempld_wdt_remove(struct platform_device *pdev)
  421. {
  422. struct kempld_wdt_data *wdt_data = platform_get_drvdata(pdev);
  423. struct watchdog_device *wdd = &wdt_data->wdd;
  424. int ret = 0;
  425. if (!nowayout)
  426. ret = kempld_wdt_stop(wdd);
  427. watchdog_unregister_device(wdd);
  428. return ret;
  429. }
  430. #ifdef CONFIG_PM
  431. /* Disable watchdog if it is active during suspend */
  432. static int kempld_wdt_suspend(struct platform_device *pdev,
  433. pm_message_t message)
  434. {
  435. struct kempld_wdt_data *wdt_data = platform_get_drvdata(pdev);
  436. struct kempld_device_data *pld = wdt_data->pld;
  437. struct watchdog_device *wdd = &wdt_data->wdd;
  438. kempld_get_mutex(pld);
  439. wdt_data->pm_status_store = kempld_read8(pld, KEMPLD_WDT_CFG);
  440. kempld_release_mutex(pld);
  441. kempld_wdt_update_timeouts(wdt_data);
  442. if (wdt_data->pm_status_store & KEMPLD_WDT_CFG_ENABLE)
  443. return kempld_wdt_stop(wdd);
  444. return 0;
  445. }
  446. /* Enable watchdog and configure it if necessary */
  447. static int kempld_wdt_resume(struct platform_device *pdev)
  448. {
  449. struct kempld_wdt_data *wdt_data = platform_get_drvdata(pdev);
  450. struct watchdog_device *wdd = &wdt_data->wdd;
  451. /*
  452. * If watchdog was stopped before suspend be sure it gets disabled
  453. * again, for the case BIOS has enabled it during resume
  454. */
  455. if (wdt_data->pm_status_store & KEMPLD_WDT_CFG_ENABLE)
  456. return kempld_wdt_start(wdd);
  457. else
  458. return kempld_wdt_stop(wdd);
  459. }
  460. #else
  461. #define kempld_wdt_suspend NULL
  462. #define kempld_wdt_resume NULL
  463. #endif
  464. static struct platform_driver kempld_wdt_driver = {
  465. .driver = {
  466. .name = "kempld-wdt",
  467. },
  468. .probe = kempld_wdt_probe,
  469. .remove = kempld_wdt_remove,
  470. .shutdown = kempld_wdt_shutdown,
  471. .suspend = kempld_wdt_suspend,
  472. .resume = kempld_wdt_resume,
  473. };
  474. module_platform_driver(kempld_wdt_driver);
  475. MODULE_DESCRIPTION("KEM PLD Watchdog Driver");
  476. MODULE_AUTHOR("Michael Brunner <michael.brunner@kontron.com>");
  477. MODULE_LICENSE("GPL");