110-watchdog-add-gemini_wdt-driver.patch 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410
  1. --- /dev/null
  2. +++ b/drivers/watchdog/gemini_wdt.c
  3. @@ -0,0 +1,378 @@
  4. +/*
  5. + * Watchdog driver for Cortina Systems Gemini SoC
  6. + *
  7. + * Copyright (C) 2009 Paulius Zaleckas <paulius.zaleckas@teltonika.lt>
  8. + *
  9. + * This program is free software; you can redistribute it and/or modify
  10. + * it under the terms of the GNU General Public License version 2 as
  11. + * published by the Free Software Foundation.
  12. + */
  13. +
  14. +#include <linux/kernel.h>
  15. +#include <linux/init.h>
  16. +#include <linux/module.h>
  17. +#include <linux/io.h>
  18. +#include <linux/fs.h>
  19. +#include <linux/uaccess.h>
  20. +#include <linux/miscdevice.h>
  21. +#include <linux/platform_device.h>
  22. +#include <linux/watchdog.h>
  23. +#include <linux/slab.h>
  24. +
  25. +#define GEMINI_WDCOUNTER 0x0
  26. +#define GEMINI_WDLOAD 0x4
  27. +#define GEMINI_WDRESTART 0x8
  28. +
  29. +#define WDRESTART_MAGIC 0x5AB9
  30. +
  31. +#define GEMINI_WDCR 0xC
  32. +
  33. +#define WDCR_CLOCK_5MHZ (1 << 4)
  34. +#define WDCR_SYS_RST (1 << 1)
  35. +#define WDCR_ENABLE (1 << 0)
  36. +
  37. +#define WDT_CLOCK 5000000 /* 5 MHz */
  38. +#define WDT_DEFAULT_TIMEOUT 13
  39. +#define WDT_MAX_TIMEOUT (0xFFFFFFFF / WDT_CLOCK)
  40. +
  41. +/* status bits */
  42. +#define WDT_ACTIVE 0
  43. +#define WDT_OK_TO_CLOSE 1
  44. +
  45. +static unsigned int timeout = WDT_DEFAULT_TIMEOUT;
  46. +static int nowayout = WATCHDOG_NOWAYOUT;
  47. +
  48. +static DEFINE_SPINLOCK(gemini_wdt_lock);
  49. +
  50. +static struct platform_device *gemini_wdt_dev;
  51. +
  52. +struct gemini_wdt_struct {
  53. + struct resource *res;
  54. + struct device *dev;
  55. + void __iomem *base;
  56. + unsigned long status;
  57. +};
  58. +
  59. +static struct watchdog_info gemini_wdt_info = {
  60. + .identity = "Gemini watchdog",
  61. + .options = WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING |
  62. + WDIOF_SETTIMEOUT,
  63. +};
  64. +
  65. +/* Disable the watchdog. */
  66. +static void gemini_wdt_stop(struct gemini_wdt_struct *gemini_wdt)
  67. +{
  68. + spin_lock(&gemini_wdt_lock);
  69. +
  70. + __raw_writel(0, gemini_wdt->base + GEMINI_WDCR);
  71. +
  72. + clear_bit(WDT_ACTIVE, &gemini_wdt->status);
  73. +
  74. + spin_unlock(&gemini_wdt_lock);
  75. +}
  76. +
  77. +/* Service the watchdog */
  78. +static void gemini_wdt_service(struct gemini_wdt_struct *gemini_wdt)
  79. +{
  80. + __raw_writel(WDRESTART_MAGIC, gemini_wdt->base + GEMINI_WDRESTART);
  81. +}
  82. +
  83. +/* Enable and reset the watchdog. */
  84. +static void gemini_wdt_start(struct gemini_wdt_struct *gemini_wdt)
  85. +{
  86. + spin_lock(&gemini_wdt_lock);
  87. +
  88. + __raw_writel(timeout * WDT_CLOCK, gemini_wdt->base + GEMINI_WDLOAD);
  89. +
  90. + gemini_wdt_service(gemini_wdt);
  91. +
  92. + /* set clock before enabling */
  93. + __raw_writel(WDCR_CLOCK_5MHZ | WDCR_SYS_RST,
  94. + gemini_wdt->base + GEMINI_WDCR);
  95. +
  96. + __raw_writel(WDCR_CLOCK_5MHZ | WDCR_SYS_RST | WDCR_ENABLE,
  97. + gemini_wdt->base + GEMINI_WDCR);
  98. +
  99. + set_bit(WDT_ACTIVE, &gemini_wdt->status);
  100. +
  101. + spin_unlock(&gemini_wdt_lock);
  102. +}
  103. +
  104. +/* Watchdog device is opened, and watchdog starts running. */
  105. +static int gemini_wdt_open(struct inode *inode, struct file *file)
  106. +{
  107. + struct gemini_wdt_struct *gemini_wdt = platform_get_drvdata(gemini_wdt_dev);
  108. +
  109. + if (test_bit(WDT_ACTIVE, &gemini_wdt->status))
  110. + return -EBUSY;
  111. +
  112. + file->private_data = gemini_wdt;
  113. +
  114. + gemini_wdt_start(gemini_wdt);
  115. +
  116. + return nonseekable_open(inode, file);
  117. +}
  118. +
  119. +/* Close the watchdog device. */
  120. +static int gemini_wdt_close(struct inode *inode, struct file *file)
  121. +{
  122. + struct gemini_wdt_struct *gemini_wdt = file->private_data;
  123. +
  124. + /* Disable the watchdog if possible */
  125. + if (test_bit(WDT_OK_TO_CLOSE, &gemini_wdt->status))
  126. + gemini_wdt_stop(gemini_wdt);
  127. + else
  128. + dev_warn(gemini_wdt->dev, "Device closed unexpectedly - timer will not stop\n");
  129. +
  130. + return 0;
  131. +}
  132. +
  133. +/* Handle commands from user-space. */
  134. +static long gemini_wdt_ioctl(struct file *file, unsigned int cmd,
  135. + unsigned long arg)
  136. +{
  137. + struct gemini_wdt_struct *gemini_wdt = file->private_data;
  138. +
  139. + int value;
  140. +
  141. + switch (cmd) {
  142. + case WDIOC_KEEPALIVE:
  143. + gemini_wdt_service(gemini_wdt);
  144. + return 0;
  145. +
  146. + case WDIOC_GETSUPPORT:
  147. + return copy_to_user((struct watchdog_info *)arg, &gemini_wdt_info,
  148. + sizeof(gemini_wdt_info)) ? -EFAULT : 0;
  149. +
  150. + case WDIOC_SETTIMEOUT:
  151. + if (get_user(value, (int *)arg))
  152. + return -EFAULT;
  153. +
  154. + if ((value < 1) || (value > WDT_MAX_TIMEOUT))
  155. + return -EINVAL;
  156. +
  157. + timeout = value;
  158. +
  159. + /* restart wdt to use new timeout */
  160. + gemini_wdt_stop(gemini_wdt);
  161. + gemini_wdt_start(gemini_wdt);
  162. +
  163. + /* Fall through */
  164. + case WDIOC_GETTIMEOUT:
  165. + return put_user(timeout, (int *)arg);
  166. +
  167. + case WDIOC_GETTIMELEFT:
  168. + value = __raw_readl(gemini_wdt->base + GEMINI_WDCOUNTER);
  169. + return put_user(value / WDT_CLOCK, (int *)arg);
  170. +
  171. + default:
  172. + return -ENOTTY;
  173. + }
  174. +}
  175. +
  176. +/* Refresh the watchdog whenever device is written to. */
  177. +static ssize_t gemini_wdt_write(struct file *file, const char *data,
  178. + size_t len, loff_t *ppos)
  179. +{
  180. + struct gemini_wdt_struct *gemini_wdt = file->private_data;
  181. +
  182. + if (len) {
  183. + if (!nowayout) {
  184. + size_t i;
  185. +
  186. + clear_bit(WDT_OK_TO_CLOSE, &gemini_wdt->status);
  187. + for (i = 0; i != len; i++) {
  188. + char c;
  189. +
  190. + if (get_user(c, data + i))
  191. + return -EFAULT;
  192. + if (c == 'V')
  193. + set_bit(WDT_OK_TO_CLOSE,
  194. + &gemini_wdt->status);
  195. + }
  196. + }
  197. + gemini_wdt_service(gemini_wdt);
  198. + }
  199. +
  200. + return len;
  201. +}
  202. +
  203. +static const struct file_operations gemini_wdt_fops = {
  204. + .owner = THIS_MODULE,
  205. + .llseek = no_llseek,
  206. + .unlocked_ioctl = gemini_wdt_ioctl,
  207. + .open = gemini_wdt_open,
  208. + .release = gemini_wdt_close,
  209. + .write = gemini_wdt_write,
  210. +};
  211. +
  212. +static struct miscdevice gemini_wdt_miscdev = {
  213. + .minor = WATCHDOG_MINOR,
  214. + .name = "watchdog",
  215. + .fops = &gemini_wdt_fops,
  216. +};
  217. +
  218. +static void gemini_wdt_shutdown(struct platform_device *pdev)
  219. +{
  220. + struct gemini_wdt_struct *gemini_wdt = platform_get_drvdata(pdev);
  221. +
  222. + gemini_wdt_stop(gemini_wdt);
  223. +}
  224. +
  225. +static int gemini_wdt_probe(struct platform_device *pdev)
  226. +{
  227. + int ret;
  228. + int res_size;
  229. + struct resource *res;
  230. + void __iomem *base;
  231. + struct gemini_wdt_struct *gemini_wdt;
  232. + unsigned int reg;
  233. +
  234. + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  235. + if (!res) {
  236. + dev_err(&pdev->dev, "can't get device resources\n");
  237. + return -ENODEV;
  238. + }
  239. +
  240. + res_size = resource_size(res);
  241. + if (!request_mem_region(res->start, res_size, res->name)) {
  242. + dev_err(&pdev->dev, "can't allocate %d bytes at %d address\n",
  243. + res_size, res->start);
  244. + return -ENOMEM;
  245. + }
  246. +
  247. + base = ioremap(res->start, res_size);
  248. + if (!base) {
  249. + dev_err(&pdev->dev, "ioremap failed\n");
  250. + ret = -EIO;
  251. + goto fail0;
  252. + }
  253. +
  254. + gemini_wdt = kzalloc(sizeof(struct gemini_wdt_struct), GFP_KERNEL);
  255. + if (!gemini_wdt) {
  256. + dev_err(&pdev->dev, "can't allocate interface\n");
  257. + ret = -ENOMEM;
  258. + goto fail1;
  259. + }
  260. +
  261. + /* Setup gemini_wdt driver structure */
  262. + gemini_wdt->base = base;
  263. + gemini_wdt->res = res;
  264. +
  265. + /* Set up platform driver data */
  266. + platform_set_drvdata(pdev, gemini_wdt);
  267. + gemini_wdt_dev = pdev;
  268. +
  269. + if (gemini_wdt_miscdev.parent) {
  270. + ret = -EBUSY;
  271. + goto fail2;
  272. + }
  273. +
  274. + gemini_wdt_miscdev.parent = &pdev->dev;
  275. +
  276. + reg = __raw_readw(gemini_wdt->base + GEMINI_WDCR);
  277. + if (reg & WDCR_ENABLE) {
  278. + /* Watchdog was enabled by the bootloader, disable it. */
  279. + reg &= ~(WDCR_ENABLE);
  280. + __raw_writel(reg, gemini_wdt->base + GEMINI_WDCR);
  281. + }
  282. +
  283. + ret = misc_register(&gemini_wdt_miscdev);
  284. + if (ret)
  285. + goto fail2;
  286. +
  287. + return 0;
  288. +
  289. +fail2:
  290. + platform_set_drvdata(pdev, NULL);
  291. + kfree(gemini_wdt);
  292. +fail1:
  293. + iounmap(base);
  294. +fail0:
  295. + release_mem_region(res->start, res_size);
  296. +
  297. + return ret;
  298. +}
  299. +
  300. +static int gemini_wdt_remove(struct platform_device *pdev)
  301. +{
  302. + struct gemini_wdt_struct *gemini_wdt = platform_get_drvdata(pdev);
  303. +
  304. + platform_set_drvdata(pdev, NULL);
  305. + misc_deregister(&gemini_wdt_miscdev);
  306. + gemini_wdt_dev = NULL;
  307. + iounmap(gemini_wdt->base);
  308. + release_mem_region(gemini_wdt->res->start, resource_size(gemini_wdt->res));
  309. +
  310. + kfree(gemini_wdt);
  311. +
  312. + return 0;
  313. +}
  314. +
  315. +#ifdef CONFIG_PM
  316. +static int gemini_wdt_suspend(struct platform_device *pdev, pm_message_t message)
  317. +{
  318. + struct gemini_wdt_struct *gemini_wdt = platform_get_drvdata(pdev);
  319. + unsigned int reg;
  320. +
  321. + reg = __raw_readw(gemini_wdt->base + GEMINI_WDCR);
  322. + reg &= ~(WDCR_WDENABLE);
  323. + __raw_writel(reg, gemini_wdt->base + GEMINI_WDCR);
  324. +
  325. + return 0;
  326. +}
  327. +
  328. +static int gemini_wdt_resume(struct platform_device *pdev)
  329. +{
  330. + struct gemini_wdt_struct *gemini_wdt = platform_get_drvdata(pdev);
  331. + unsigned int reg;
  332. +
  333. + if (gemini_wdt->status) {
  334. + reg = __raw_readw(gemini_wdt->base + GEMINI_WDCR);
  335. + reg |= WDCR_WDENABLE;
  336. + __raw_writel(reg, gemini_wdt->base + GEMINI_WDCR);
  337. + }
  338. +
  339. + return 0;
  340. +}
  341. +#else
  342. +#define gemini_wdt_suspend NULL
  343. +#define gemini_wdt_resume NULL
  344. +#endif
  345. +
  346. +static struct platform_driver gemini_wdt_driver = {
  347. + .probe = gemini_wdt_probe,
  348. + .remove = gemini_wdt_remove,
  349. + .shutdown = gemini_wdt_shutdown,
  350. + .suspend = gemini_wdt_suspend,
  351. + .resume = gemini_wdt_resume,
  352. + .driver = {
  353. + .name = "gemini-wdt",
  354. + .owner = THIS_MODULE,
  355. + },
  356. +};
  357. +
  358. +static int __init gemini_wdt_init(void)
  359. +{
  360. + return platform_driver_probe(&gemini_wdt_driver, gemini_wdt_probe);
  361. +}
  362. +
  363. +static void __exit gemini_wdt_exit(void)
  364. +{
  365. + platform_driver_unregister(&gemini_wdt_driver);
  366. +}
  367. +
  368. +module_init(gemini_wdt_init);
  369. +module_exit(gemini_wdt_exit);
  370. +
  371. +module_param(timeout, uint, 0);
  372. +MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds");
  373. +
  374. +module_param(nowayout, int, 0);
  375. +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started");
  376. +
  377. +MODULE_AUTHOR("Paulius Zaleckas");
  378. +MODULE_DESCRIPTION("Watchdog driver for Gemini");
  379. +MODULE_LICENSE("GPL");
  380. +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
  381. +MODULE_ALIAS("platform:gemini-wdt");
  382. --- a/drivers/watchdog/Kconfig
  383. +++ b/drivers/watchdog/Kconfig
  384. @@ -199,6 +199,16 @@ config 977_WATCHDOG
  385. Not sure? It's safe to say N.
  386. +config GEMINI_WATCHDOG
  387. + tristate "Gemini watchdog"
  388. + depends on ARCH_GEMINI
  389. + help
  390. + Say Y here if to include support for the watchdog timer
  391. + embedded in the Cortina Systems Gemini family of devices.
  392. +
  393. + To compile this driver as a module, choose M here: the
  394. + module will be called gemini_wdt.
  395. +
  396. config IXP4XX_WATCHDOG
  397. tristate "IXP4xx Watchdog"
  398. depends on ARCH_IXP4XX
  399. --- a/drivers/watchdog/Makefile
  400. +++ b/drivers/watchdog/Makefile
  401. @@ -37,6 +37,7 @@ obj-$(CONFIG_OMAP_WATCHDOG) += omap_wdt.
  402. obj-$(CONFIG_TWL4030_WATCHDOG) += twl4030_wdt.o
  403. obj-$(CONFIG_21285_WATCHDOG) += wdt285.o
  404. obj-$(CONFIG_977_WATCHDOG) += wdt977.o
  405. +obj-$(CONFIG_GEMINI_WATCHDOG) += gemini_wdt.o
  406. obj-$(CONFIG_IXP4XX_WATCHDOG) += ixp4xx_wdt.o
  407. obj-$(CONFIG_KS8695_WATCHDOG) += ks8695_wdt.o
  408. obj-$(CONFIG_S3C2410_WATCHDOG) += s3c2410_wdt.o