006-arm-add-fa-watchdog-driver.patch 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456
  1. --- a/drivers/watchdog/Kconfig
  2. +++ b/drivers/watchdog/Kconfig
  3. @@ -331,6 +331,13 @@ config IMX2_WDT
  4. To compile this driver as a module, choose M here: the
  5. module will be called imx2_wdt.
  6. +config FA_WATCHDOG
  7. + tristate "Faraday watchdog"
  8. + depends on ARCH_GEMINI
  9. + help
  10. + Say Y here if you want support for the built-in watchdog timer
  11. + found in some Faraday FA526 based SoCs.
  12. +
  13. # AVR32 Architecture
  14. config AT32AP700X_WDT
  15. --- a/drivers/watchdog/Makefile
  16. +++ b/drivers/watchdog/Makefile
  17. @@ -49,6 +49,7 @@ obj-$(CONFIG_NUC900_WATCHDOG) += nuc900_
  18. obj-$(CONFIG_ADX_WATCHDOG) += adx_wdt.o
  19. obj-$(CONFIG_TS72XX_WATCHDOG) += ts72xx_wdt.o
  20. obj-$(CONFIG_IMX2_WDT) += imx2_wdt.o
  21. +obj-$(CONFIG_FA_WATCHDOG) += fa_wdt.o
  22. # AVR32 Architecture
  23. obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o
  24. --- /dev/null
  25. +++ b/drivers/watchdog/fa_wdt.c
  26. @@ -0,0 +1,411 @@
  27. +/*
  28. + * Watchdog driver for SoCs based on the Faraday FA526 core
  29. + *
  30. + * Copyright (C) 2009 Paulius Zaleckas <[email protected]>
  31. + * Copyright (C) 2010 Gabor Juhos <[email protected]>
  32. + *
  33. + * This program is free software; you can redistribute it and/or modify
  34. + * it under the terms of the GNU General Public License version 2 as
  35. + * published by the Free Software Foundation.
  36. + */
  37. +
  38. +#include <linux/kernel.h>
  39. +#include <linux/init.h>
  40. +#include <linux/io.h>
  41. +#include <linux/fs.h>
  42. +#include <linux/notifier.h>
  43. +#include <linux/reboot.h>
  44. +#include <linux/uaccess.h>
  45. +#include <linux/miscdevice.h>
  46. +#include <linux/platform_device.h>
  47. +#include <linux/watchdog.h>
  48. +#include <linux/slab.h>
  49. +#include <linux/fa_wdt.h>
  50. +
  51. +#define FA_WDCOUNTER 0x0
  52. +#define FA_WDLOAD 0x4
  53. +#define FA_WDRESTART 0x8
  54. +
  55. +#define WDRESTART_MAGIC 0x5AB9
  56. +
  57. +#define FA_WDCR 0xC
  58. +
  59. +#define WDCR_CLOCK_5MHZ (1 << 4)
  60. +#define WDCR_SYS_RST (1 << 1)
  61. +#define WDCR_ENABLE (1 << 0)
  62. +
  63. +#define WDT_DEFAULT_TIMEOUT 13
  64. +
  65. +/* status bits */
  66. +#define WDT_ACTIVE 0
  67. +#define WDT_OK_TO_CLOSE 1
  68. +
  69. +static unsigned int timeout = WDT_DEFAULT_TIMEOUT;
  70. +static int nowayout = WATCHDOG_NOWAYOUT;
  71. +
  72. +static DEFINE_SPINLOCK(fa_wdt_lock);
  73. +
  74. +static struct platform_device *fa_wdt_dev;
  75. +
  76. +struct fa_wdt_struct {
  77. + struct resource *res;
  78. + struct device *dev;
  79. + void __iomem *base;
  80. + unsigned long status;
  81. + unsigned int clock;
  82. + unsigned int max_timeout;
  83. +};
  84. +
  85. +static const struct watchdog_info fa_wdt_info = {
  86. + .identity = "Faraday watchdog",
  87. + .options = WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING |
  88. + WDIOF_SETTIMEOUT,
  89. +};
  90. +
  91. +/* Disable the watchdog. */
  92. +static void fa_wdt_stop(struct fa_wdt_struct *fa_wdt)
  93. +{
  94. + spin_lock(&fa_wdt_lock);
  95. +
  96. + __raw_writel(0, fa_wdt->base + FA_WDCR);
  97. +
  98. + clear_bit(WDT_ACTIVE, &fa_wdt->status);
  99. +
  100. + spin_unlock(&fa_wdt_lock);
  101. +}
  102. +
  103. +/* Service the watchdog */
  104. +static void fa_wdt_service(struct fa_wdt_struct *fa_wdt)
  105. +{
  106. + __raw_writel(WDRESTART_MAGIC, fa_wdt->base + FA_WDRESTART);
  107. +}
  108. +
  109. +/* Enable and reset the watchdog. */
  110. +static void fa_wdt_start(struct fa_wdt_struct *fa_wdt)
  111. +{
  112. + spin_lock(&fa_wdt_lock);
  113. +
  114. + __raw_writel(timeout * fa_wdt->clock,
  115. + fa_wdt->base + FA_WDLOAD);
  116. +
  117. + fa_wdt_service(fa_wdt);
  118. +
  119. + /* set clock before enabling */
  120. + __raw_writel(WDCR_CLOCK_5MHZ | WDCR_SYS_RST,
  121. + fa_wdt->base + FA_WDCR);
  122. +
  123. + __raw_writel(WDCR_CLOCK_5MHZ | WDCR_SYS_RST | WDCR_ENABLE,
  124. + fa_wdt->base + FA_WDCR);
  125. +
  126. + set_bit(WDT_ACTIVE, &fa_wdt->status);
  127. +
  128. + spin_unlock(&fa_wdt_lock);
  129. +}
  130. +
  131. +/* Watchdog device is opened, and watchdog starts running. */
  132. +static int fa_wdt_open(struct inode *inode, struct file *file)
  133. +{
  134. + struct fa_wdt_struct *fa_wdt = platform_get_drvdata(fa_wdt_dev);
  135. +
  136. + if (test_bit(WDT_ACTIVE, &fa_wdt->status))
  137. + return -EBUSY;
  138. +
  139. + file->private_data = fa_wdt;
  140. +
  141. + fa_wdt_start(fa_wdt);
  142. +
  143. + return nonseekable_open(inode, file);
  144. +}
  145. +
  146. +/* Close the watchdog device. */
  147. +static int fa_wdt_close(struct inode *inode, struct file *file)
  148. +{
  149. + struct fa_wdt_struct *fa_wdt = file->private_data;
  150. +
  151. + /* Disable the watchdog if possible */
  152. + if (test_bit(WDT_OK_TO_CLOSE, &fa_wdt->status))
  153. + fa_wdt_stop(fa_wdt);
  154. + else
  155. + dev_warn(fa_wdt->dev, "Device closed unexpectedly - timer will not stop\n");
  156. +
  157. + return 0;
  158. +}
  159. +
  160. +/* Handle commands from user-space. */
  161. +static long fa_wdt_ioctl(struct file *file, unsigned int cmd,
  162. + unsigned long arg)
  163. +{
  164. + struct fa_wdt_struct *fa_wdt = file->private_data;
  165. +
  166. + int value;
  167. +
  168. + switch (cmd) {
  169. + case WDIOC_KEEPALIVE:
  170. + fa_wdt_service(fa_wdt);
  171. + return 0;
  172. +
  173. + case WDIOC_GETSUPPORT:
  174. + return copy_to_user((struct watchdog_info *)arg, &fa_wdt_info,
  175. + sizeof(fa_wdt_info)) ? -EFAULT : 0;
  176. +
  177. + case WDIOC_SETTIMEOUT:
  178. + if (get_user(value, (int *)arg))
  179. + return -EFAULT;
  180. +
  181. + if ((value < 1) || (value > fa_wdt->max_timeout))
  182. + return -EINVAL;
  183. +
  184. + timeout = value;
  185. +
  186. + /* restart wdt to use new timeout */
  187. + fa_wdt_stop(fa_wdt);
  188. + fa_wdt_start(fa_wdt);
  189. +
  190. + /* Fall through */
  191. + case WDIOC_GETTIMEOUT:
  192. + return put_user(timeout, (int *)arg);
  193. +
  194. + case WDIOC_GETTIMELEFT:
  195. + value = __raw_readl(fa_wdt->base + FA_WDCOUNTER);
  196. + return put_user(value / fa_wdt->clock, (int *)arg);
  197. +
  198. + default:
  199. + return -ENOTTY;
  200. + }
  201. +}
  202. +
  203. +/* Refresh the watchdog whenever device is written to. */
  204. +static ssize_t fa_wdt_write(struct file *file, const char *data,
  205. + size_t len, loff_t *ppos)
  206. +{
  207. + struct fa_wdt_struct *fa_wdt = file->private_data;
  208. +
  209. + if (len) {
  210. + if (!nowayout) {
  211. + size_t i;
  212. +
  213. + clear_bit(WDT_OK_TO_CLOSE, &fa_wdt->status);
  214. + for (i = 0; i != len; i++) {
  215. + char c;
  216. +
  217. + if (get_user(c, data + i))
  218. + return -EFAULT;
  219. + if (c == 'V')
  220. + set_bit(WDT_OK_TO_CLOSE,
  221. + &fa_wdt->status);
  222. + }
  223. + }
  224. + fa_wdt_service(fa_wdt);
  225. + }
  226. +
  227. + return len;
  228. +}
  229. +
  230. +static int fa_wdt_notify_sys(struct notifier_block *this,
  231. + unsigned long code, void *unused)
  232. +{
  233. + struct fa_wdt_struct *fa_wdt = platform_get_drvdata(fa_wdt_dev);
  234. +
  235. + if (code == SYS_DOWN || code == SYS_HALT)
  236. + fa_wdt_stop(fa_wdt);
  237. +
  238. + return NOTIFY_DONE;
  239. +}
  240. +
  241. +static struct notifier_block fa_wdt_notifier = {
  242. + .notifier_call = fa_wdt_notify_sys,
  243. +};
  244. +
  245. +static const struct file_operations fa_wdt_fops = {
  246. + .owner = THIS_MODULE,
  247. + .llseek = no_llseek,
  248. + .unlocked_ioctl = fa_wdt_ioctl,
  249. + .open = fa_wdt_open,
  250. + .release = fa_wdt_close,
  251. + .write = fa_wdt_write,
  252. +};
  253. +
  254. +static struct miscdevice fa_wdt_miscdev = {
  255. + .minor = WATCHDOG_MINOR,
  256. + .name = "watchdog",
  257. + .fops = &fa_wdt_fops,
  258. +};
  259. +
  260. +static void fa_wdt_shutdown(struct platform_device *pdev)
  261. +{
  262. + struct fa_wdt_struct *fa_wdt = platform_get_drvdata(pdev);
  263. +
  264. + fa_wdt_stop(fa_wdt);
  265. +}
  266. +
  267. +static int __devinit fa_wdt_probe(struct platform_device *pdev)
  268. +{
  269. + int ret;
  270. + int res_size;
  271. + struct resource *res;
  272. + void __iomem *base;
  273. + struct fa_wdt_struct *fa_wdt;
  274. + struct fa_wdt_platform_data *pdata;
  275. +
  276. + pdata = pdev->dev.platform_data;
  277. + if (!pdata) {
  278. + dev_err(&pdev->dev, "no platform data specified\n");
  279. + return -EINVAL;
  280. + }
  281. +
  282. + if (!pdata->clock) {
  283. + dev_err(&pdev->dev, "invalid clock value\n");
  284. + return -EINVAL;
  285. + }
  286. +
  287. + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  288. + if (!res) {
  289. + dev_err(&pdev->dev, "can't get device resources\n");
  290. + return -ENODEV;
  291. + }
  292. +
  293. + res_size = resource_size(res);
  294. + if (!request_mem_region(res->start, res_size, res->name)) {
  295. + dev_err(&pdev->dev, "can't allocate %d bytes at %d address\n",
  296. + res_size, res->start);
  297. + return -ENOMEM;
  298. + }
  299. +
  300. + base = ioremap(res->start, res_size);
  301. + if (!base) {
  302. + dev_err(&pdev->dev, "ioremap failed\n");
  303. + ret = -EIO;
  304. + goto fail0;
  305. + }
  306. +
  307. + fa_wdt = kzalloc(sizeof(struct fa_wdt_struct), GFP_KERNEL);
  308. + if (!fa_wdt) {
  309. + dev_err(&pdev->dev, "can't allocate interface\n");
  310. + ret = -ENOMEM;
  311. + goto fail1;
  312. + }
  313. +
  314. + /* Setup fa_wdt driver structure */
  315. + fa_wdt->base = base;
  316. + fa_wdt->res = res;
  317. + fa_wdt->dev = &pdev->dev;
  318. + fa_wdt->clock = pdata->clock;
  319. + fa_wdt->max_timeout = 0xffffffffU / pdata->clock;
  320. +
  321. + /* Set up platform driver data */
  322. + platform_set_drvdata(pdev, fa_wdt);
  323. + fa_wdt_dev = pdev;
  324. +
  325. + if (fa_wdt_miscdev.parent) {
  326. + ret = -EBUSY;
  327. + goto fail2;
  328. + }
  329. +
  330. + ret = register_reboot_notifier(&fa_wdt_notifier);
  331. + if (ret)
  332. + goto fail2;
  333. +
  334. + fa_wdt_miscdev.parent = &pdev->dev;
  335. +
  336. + ret = misc_register(&fa_wdt_miscdev);
  337. + if (ret)
  338. + goto fail3;
  339. +
  340. + return 0;
  341. +
  342. +fail3:
  343. + unregister_reboot_notifier(&fa_wdt_notifier);
  344. +fail2:
  345. + platform_set_drvdata(pdev, NULL);
  346. + kfree(fa_wdt);
  347. +fail1:
  348. + iounmap(base);
  349. +fail0:
  350. + release_mem_region(res->start, res_size);
  351. +
  352. + return ret;
  353. +}
  354. +
  355. +static int __devexit fa_wdt_remove(struct platform_device *pdev)
  356. +{
  357. + struct fa_wdt_struct *fa_wdt = platform_get_drvdata(pdev);
  358. +
  359. + platform_set_drvdata(pdev, NULL);
  360. + misc_deregister(&fa_wdt_miscdev);
  361. + unregister_reboot_notifier(&fa_wdt_notifier);
  362. + fa_wdt_dev = NULL;
  363. + iounmap(fa_wdt->base);
  364. + release_mem_region(fa_wdt->res->start, resource_size(fa_wdt->res));
  365. +
  366. + kfree(fa_wdt);
  367. +
  368. + return 0;
  369. +}
  370. +
  371. +#ifdef CONFIG_PM
  372. +static int fa_wdt_suspend(struct platform_device *pdev, pm_message_t message)
  373. +{
  374. + struct fa_wdt_struct *fa_wdt = platform_get_drvdata(pdev);
  375. + unsigned int reg;
  376. +
  377. + reg = __raw_readw(fa_wdt->base + FA_WDCR);
  378. + reg &= ~(WDCR_WDENABLE);
  379. + __raw_writel(reg, fa_wdt->base + FA_WDCR);
  380. +
  381. + return 0;
  382. +}
  383. +
  384. +static int fa_wdt_resume(struct platform_device *pdev)
  385. +{
  386. + struct fa_wdt_struct *fa_wdt = platform_get_drvdata(pdev);
  387. + unsigned int reg;
  388. +
  389. + if (fa_wdt->status) {
  390. + reg = __raw_readw(fa_wdt->base + FA_WDCR);
  391. + reg |= WDCR_WDENABLE;
  392. + __raw_writel(reg, fa_wdt->base + FA_WDCR);
  393. + }
  394. +
  395. + return 0;
  396. +}
  397. +#else
  398. +#define fa_wdt_suspend NULL
  399. +#define fa_wdt_resume NULL
  400. +#endif
  401. +
  402. +static struct platform_driver fa_wdt_driver = {
  403. + .probe = fa_wdt_probe,
  404. + .remove = __devexit_p(fa_wdt_remove),
  405. + .shutdown = fa_wdt_shutdown,
  406. + .suspend = fa_wdt_suspend,
  407. + .resume = fa_wdt_resume,
  408. + .driver = {
  409. + .name = "fa-wdt",
  410. + .owner = THIS_MODULE,
  411. + },
  412. +};
  413. +
  414. +static int __init fa_wdt_init(void)
  415. +{
  416. + return platform_driver_probe(&fa_wdt_driver, fa_wdt_probe);
  417. +}
  418. +
  419. +static void __exit fa_wdt_exit(void)
  420. +{
  421. + platform_driver_unregister(&fa_wdt_driver);
  422. +}
  423. +
  424. +module_init(fa_wdt_init);
  425. +module_exit(fa_wdt_exit);
  426. +
  427. +module_param(timeout, uint, 0);
  428. +MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds");
  429. +
  430. +module_param(nowayout, int, 0);
  431. +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started");
  432. +
  433. +MODULE_AUTHOR("Paulius Zaleckas");
  434. +MODULE_DESCRIPTION("Watchdog driver for Faraday FA526 based SoCs");
  435. +MODULE_LICENSE("GPL v2");
  436. +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
  437. +MODULE_ALIAS("platform:fa-wdt");
  438. --- /dev/null
  439. +++ b/include/linux/fa_wdt.h
  440. @@ -0,0 +1,13 @@
  441. +/*
  442. + * Platform data definition for the Faraday watchdog driver
  443. + */
  444. +
  445. +#ifndef _FA_WDT_H
  446. +#define _FA_WDT_H
  447. +
  448. +struct fa_wdt_platform_data {
  449. + unsigned int clock;
  450. +};
  451. +
  452. +#endif /* _FA_WDT_H */
  453. +