kempld_wdt.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579
  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 = kempld_prescaler[PRESCALER_21];
  124. u64 stage_timeout64;
  125. u32 stage_timeout;
  126. u32 remainder;
  127. u8 stage_cfg;
  128. if (!stage)
  129. return -EINVAL;
  130. stage_timeout64 = (u64)timeout * pld->pld_clock;
  131. remainder = do_div(stage_timeout64, prescaler);
  132. if (remainder)
  133. stage_timeout64++;
  134. if (stage_timeout64 > stage->mask)
  135. return -EINVAL;
  136. stage_timeout = stage_timeout64 & stage->mask;
  137. kempld_get_mutex(pld);
  138. stage_cfg = kempld_read8(pld, KEMPLD_WDT_STAGE_CFG(stage->id));
  139. stage_cfg &= ~STAGE_CFG_PRESCALER_MASK;
  140. stage_cfg |= STAGE_CFG_SET_PRESCALER(prescaler);
  141. kempld_write8(pld, KEMPLD_WDT_STAGE_CFG(stage->id), stage_cfg);
  142. kempld_write32(pld, KEMPLD_WDT_STAGE_TIMEOUT(stage->id),
  143. stage_timeout);
  144. kempld_release_mutex(pld);
  145. return 0;
  146. }
  147. /*
  148. * kempld_get_mutex must be called prior to calling this function.
  149. */
  150. static unsigned int kempld_wdt_get_timeout(struct kempld_wdt_data *wdt_data,
  151. struct kempld_wdt_stage *stage)
  152. {
  153. struct kempld_device_data *pld = wdt_data->pld;
  154. unsigned int timeout;
  155. u64 stage_timeout;
  156. u32 prescaler;
  157. u32 remainder;
  158. u8 stage_cfg;
  159. if (!stage->mask)
  160. return 0;
  161. stage_cfg = kempld_read8(pld, KEMPLD_WDT_STAGE_CFG(stage->id));
  162. stage_timeout = kempld_read32(pld, KEMPLD_WDT_STAGE_TIMEOUT(stage->id));
  163. prescaler = kempld_prescaler[STAGE_CFG_GET_PRESCALER(stage_cfg)];
  164. stage_timeout = (stage_timeout & stage->mask) * prescaler;
  165. remainder = do_div(stage_timeout, pld->pld_clock);
  166. if (remainder)
  167. stage_timeout++;
  168. timeout = stage_timeout;
  169. WARN_ON_ONCE(timeout != stage_timeout);
  170. return timeout;
  171. }
  172. static int kempld_wdt_set_timeout(struct watchdog_device *wdd,
  173. unsigned int timeout)
  174. {
  175. struct kempld_wdt_data *wdt_data = watchdog_get_drvdata(wdd);
  176. struct kempld_wdt_stage *pretimeout_stage;
  177. struct kempld_wdt_stage *timeout_stage;
  178. int ret;
  179. timeout_stage = &wdt_data->stage[STAGE_TIMEOUT];
  180. pretimeout_stage = &wdt_data->stage[STAGE_PRETIMEOUT];
  181. if (pretimeout_stage->mask && wdt_data->pretimeout > 0)
  182. timeout = wdt_data->pretimeout;
  183. ret = kempld_wdt_set_stage_action(wdt_data, timeout_stage,
  184. ACTION_RESET);
  185. if (ret)
  186. return ret;
  187. ret = kempld_wdt_set_stage_timeout(wdt_data, timeout_stage,
  188. timeout);
  189. if (ret)
  190. return ret;
  191. wdd->timeout = timeout;
  192. return 0;
  193. }
  194. static int kempld_wdt_set_pretimeout(struct watchdog_device *wdd,
  195. unsigned int pretimeout)
  196. {
  197. struct kempld_wdt_data *wdt_data = watchdog_get_drvdata(wdd);
  198. struct kempld_wdt_stage *pretimeout_stage;
  199. u8 action = ACTION_NONE;
  200. int ret;
  201. pretimeout_stage = &wdt_data->stage[STAGE_PRETIMEOUT];
  202. if (!pretimeout_stage->mask)
  203. return -ENXIO;
  204. if (pretimeout > wdd->timeout)
  205. return -EINVAL;
  206. if (pretimeout > 0)
  207. action = ACTION_NMI;
  208. ret = kempld_wdt_set_stage_action(wdt_data, pretimeout_stage,
  209. action);
  210. if (ret)
  211. return ret;
  212. ret = kempld_wdt_set_stage_timeout(wdt_data, pretimeout_stage,
  213. wdd->timeout - pretimeout);
  214. if (ret)
  215. return ret;
  216. wdt_data->pretimeout = pretimeout;
  217. return 0;
  218. }
  219. static void kempld_wdt_update_timeouts(struct kempld_wdt_data *wdt_data)
  220. {
  221. struct kempld_device_data *pld = wdt_data->pld;
  222. struct kempld_wdt_stage *pretimeout_stage;
  223. struct kempld_wdt_stage *timeout_stage;
  224. unsigned int pretimeout, timeout;
  225. pretimeout_stage = &wdt_data->stage[STAGE_PRETIMEOUT];
  226. timeout_stage = &wdt_data->stage[STAGE_TIMEOUT];
  227. kempld_get_mutex(pld);
  228. pretimeout = kempld_wdt_get_timeout(wdt_data, pretimeout_stage);
  229. timeout = kempld_wdt_get_timeout(wdt_data, timeout_stage);
  230. kempld_release_mutex(pld);
  231. if (pretimeout)
  232. wdt_data->pretimeout = timeout;
  233. else
  234. wdt_data->pretimeout = 0;
  235. wdt_data->wdd.timeout = pretimeout + timeout;
  236. }
  237. static int kempld_wdt_start(struct watchdog_device *wdd)
  238. {
  239. struct kempld_wdt_data *wdt_data = watchdog_get_drvdata(wdd);
  240. struct kempld_device_data *pld = wdt_data->pld;
  241. u8 status;
  242. int ret;
  243. ret = kempld_wdt_set_timeout(wdd, wdd->timeout);
  244. if (ret)
  245. return ret;
  246. kempld_get_mutex(pld);
  247. status = kempld_read8(pld, KEMPLD_WDT_CFG);
  248. status |= KEMPLD_WDT_CFG_ENABLE;
  249. kempld_write8(pld, KEMPLD_WDT_CFG, status);
  250. status = kempld_read8(pld, KEMPLD_WDT_CFG);
  251. kempld_release_mutex(pld);
  252. /* Check if the watchdog was enabled */
  253. if (!(status & KEMPLD_WDT_CFG_ENABLE))
  254. return -EACCES;
  255. return 0;
  256. }
  257. static int kempld_wdt_stop(struct watchdog_device *wdd)
  258. {
  259. struct kempld_wdt_data *wdt_data = watchdog_get_drvdata(wdd);
  260. struct kempld_device_data *pld = wdt_data->pld;
  261. u8 status;
  262. kempld_get_mutex(pld);
  263. status = kempld_read8(pld, KEMPLD_WDT_CFG);
  264. status &= ~KEMPLD_WDT_CFG_ENABLE;
  265. kempld_write8(pld, KEMPLD_WDT_CFG, status);
  266. status = kempld_read8(pld, KEMPLD_WDT_CFG);
  267. kempld_release_mutex(pld);
  268. /* Check if the watchdog was disabled */
  269. if (status & KEMPLD_WDT_CFG_ENABLE)
  270. return -EACCES;
  271. return 0;
  272. }
  273. static int kempld_wdt_keepalive(struct watchdog_device *wdd)
  274. {
  275. struct kempld_wdt_data *wdt_data = watchdog_get_drvdata(wdd);
  276. struct kempld_device_data *pld = wdt_data->pld;
  277. kempld_get_mutex(pld);
  278. kempld_write8(pld, KEMPLD_WDT_KICK, 'K');
  279. kempld_release_mutex(pld);
  280. return 0;
  281. }
  282. static long kempld_wdt_ioctl(struct watchdog_device *wdd, unsigned int cmd,
  283. unsigned long arg)
  284. {
  285. struct kempld_wdt_data *wdt_data = watchdog_get_drvdata(wdd);
  286. void __user *argp = (void __user *)arg;
  287. int ret = -ENOIOCTLCMD;
  288. int __user *p = argp;
  289. int new_value;
  290. switch (cmd) {
  291. case WDIOC_SETPRETIMEOUT:
  292. if (get_user(new_value, p))
  293. return -EFAULT;
  294. ret = kempld_wdt_set_pretimeout(wdd, new_value);
  295. if (ret)
  296. return ret;
  297. ret = kempld_wdt_keepalive(wdd);
  298. break;
  299. case WDIOC_GETPRETIMEOUT:
  300. ret = put_user(wdt_data->pretimeout, (int __user *)arg);
  301. break;
  302. }
  303. return ret;
  304. }
  305. static int kempld_wdt_probe_stages(struct watchdog_device *wdd)
  306. {
  307. struct kempld_wdt_data *wdt_data = watchdog_get_drvdata(wdd);
  308. struct kempld_device_data *pld = wdt_data->pld;
  309. struct kempld_wdt_stage *pretimeout_stage;
  310. struct kempld_wdt_stage *timeout_stage;
  311. u8 index, data, data_orig;
  312. u32 mask;
  313. int i, j;
  314. pretimeout_stage = &wdt_data->stage[STAGE_PRETIMEOUT];
  315. timeout_stage = &wdt_data->stage[STAGE_TIMEOUT];
  316. pretimeout_stage->mask = 0;
  317. timeout_stage->mask = 0;
  318. for (i = 0; i < 3; i++) {
  319. index = KEMPLD_WDT_STAGE_TIMEOUT(i);
  320. mask = 0;
  321. kempld_get_mutex(pld);
  322. /* Probe each byte individually. */
  323. for (j = 0; j < 4; j++) {
  324. data_orig = kempld_read8(pld, index + j);
  325. kempld_write8(pld, index + j, 0x00);
  326. data = kempld_read8(pld, index + j);
  327. /* A failed write means this byte is reserved */
  328. if (data != 0x00)
  329. break;
  330. kempld_write8(pld, index + j, data_orig);
  331. mask |= 0xff << (j * 8);
  332. }
  333. kempld_release_mutex(pld);
  334. /* Assign available stages to timeout and pretimeout */
  335. if (!timeout_stage->mask) {
  336. timeout_stage->mask = mask;
  337. timeout_stage->id = i;
  338. } else {
  339. if (pld->feature_mask & KEMPLD_FEATURE_BIT_NMI) {
  340. pretimeout_stage->mask = timeout_stage->mask;
  341. timeout_stage->mask = mask;
  342. pretimeout_stage->id = timeout_stage->id;
  343. timeout_stage->id = i;
  344. }
  345. break;
  346. }
  347. }
  348. if (!timeout_stage->mask)
  349. return -ENODEV;
  350. return 0;
  351. }
  352. static struct watchdog_info kempld_wdt_info = {
  353. .identity = "KEMPLD Watchdog",
  354. .options = WDIOF_SETTIMEOUT |
  355. WDIOF_KEEPALIVEPING |
  356. WDIOF_MAGICCLOSE |
  357. WDIOF_PRETIMEOUT
  358. };
  359. static struct watchdog_ops kempld_wdt_ops = {
  360. .owner = THIS_MODULE,
  361. .start = kempld_wdt_start,
  362. .stop = kempld_wdt_stop,
  363. .ping = kempld_wdt_keepalive,
  364. .set_timeout = kempld_wdt_set_timeout,
  365. .ioctl = kempld_wdt_ioctl,
  366. };
  367. static int kempld_wdt_probe(struct platform_device *pdev)
  368. {
  369. struct kempld_device_data *pld = dev_get_drvdata(pdev->dev.parent);
  370. struct kempld_wdt_data *wdt_data;
  371. struct device *dev = &pdev->dev;
  372. struct watchdog_device *wdd;
  373. u8 status;
  374. int ret = 0;
  375. wdt_data = devm_kzalloc(dev, sizeof(*wdt_data), GFP_KERNEL);
  376. if (!wdt_data)
  377. return -ENOMEM;
  378. wdt_data->pld = pld;
  379. wdd = &wdt_data->wdd;
  380. wdd->parent = dev;
  381. kempld_get_mutex(pld);
  382. status = kempld_read8(pld, KEMPLD_WDT_CFG);
  383. kempld_release_mutex(pld);
  384. /* Enable nowayout if watchdog is already locked */
  385. if (status & (KEMPLD_WDT_CFG_ENABLE_LOCK |
  386. KEMPLD_WDT_CFG_GLOBAL_LOCK)) {
  387. if (!nowayout)
  388. dev_warn(dev,
  389. "Forcing nowayout - watchdog lock enabled!\n");
  390. nowayout = true;
  391. }
  392. wdd->info = &kempld_wdt_info;
  393. wdd->ops = &kempld_wdt_ops;
  394. watchdog_set_drvdata(wdd, wdt_data);
  395. watchdog_set_nowayout(wdd, nowayout);
  396. ret = kempld_wdt_probe_stages(wdd);
  397. if (ret)
  398. return ret;
  399. kempld_wdt_set_timeout(wdd, timeout);
  400. kempld_wdt_set_pretimeout(wdd, pretimeout);
  401. /* Check if watchdog is already enabled */
  402. if (status & KEMPLD_WDT_CFG_ENABLE) {
  403. /* Get current watchdog settings */
  404. kempld_wdt_update_timeouts(wdt_data);
  405. dev_info(dev, "Watchdog was already enabled\n");
  406. }
  407. platform_set_drvdata(pdev, wdt_data);
  408. ret = watchdog_register_device(wdd);
  409. if (ret)
  410. return ret;
  411. dev_info(dev, "Watchdog registered with %ds timeout\n", wdd->timeout);
  412. return 0;
  413. }
  414. static void kempld_wdt_shutdown(struct platform_device *pdev)
  415. {
  416. struct kempld_wdt_data *wdt_data = platform_get_drvdata(pdev);
  417. kempld_wdt_stop(&wdt_data->wdd);
  418. }
  419. static int kempld_wdt_remove(struct platform_device *pdev)
  420. {
  421. struct kempld_wdt_data *wdt_data = platform_get_drvdata(pdev);
  422. struct watchdog_device *wdd = &wdt_data->wdd;
  423. int ret = 0;
  424. if (!nowayout)
  425. ret = kempld_wdt_stop(wdd);
  426. watchdog_unregister_device(wdd);
  427. return ret;
  428. }
  429. #ifdef CONFIG_PM
  430. /* Disable watchdog if it is active during suspend */
  431. static int kempld_wdt_suspend(struct platform_device *pdev,
  432. pm_message_t message)
  433. {
  434. struct kempld_wdt_data *wdt_data = platform_get_drvdata(pdev);
  435. struct kempld_device_data *pld = wdt_data->pld;
  436. struct watchdog_device *wdd = &wdt_data->wdd;
  437. kempld_get_mutex(pld);
  438. wdt_data->pm_status_store = kempld_read8(pld, KEMPLD_WDT_CFG);
  439. kempld_release_mutex(pld);
  440. kempld_wdt_update_timeouts(wdt_data);
  441. if (wdt_data->pm_status_store & KEMPLD_WDT_CFG_ENABLE)
  442. return kempld_wdt_stop(wdd);
  443. return 0;
  444. }
  445. /* Enable watchdog and configure it if necessary */
  446. static int kempld_wdt_resume(struct platform_device *pdev)
  447. {
  448. struct kempld_wdt_data *wdt_data = platform_get_drvdata(pdev);
  449. struct watchdog_device *wdd = &wdt_data->wdd;
  450. /*
  451. * If watchdog was stopped before suspend be sure it gets disabled
  452. * again, for the case BIOS has enabled it during resume
  453. */
  454. if (wdt_data->pm_status_store & KEMPLD_WDT_CFG_ENABLE)
  455. return kempld_wdt_start(wdd);
  456. else
  457. return kempld_wdt_stop(wdd);
  458. }
  459. #else
  460. #define kempld_wdt_suspend NULL
  461. #define kempld_wdt_resume NULL
  462. #endif
  463. static struct platform_driver kempld_wdt_driver = {
  464. .driver = {
  465. .name = "kempld-wdt",
  466. .owner = THIS_MODULE,
  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");