-/* linux/drivers/watchdog/rk29_wdt.c\r
- *\r
- * Copyright (C) 2011 ROCKCHIP, Inc.\r
- * hhb@rock-chips.com\r
- *\r
- * This program is free software; you can redistribute it and/or modify\r
- * it under the terms of the GNU General Public License as published by\r
- * the Free Software Foundation; either version 2 of the License, or\r
- * (at your option) any later version.\r
- *\r
- * This program is distributed in the hope that it will be useful,\r
- * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- * GNU General Public License for more details.\r
- *\r
- * You should have received a copy of the GNU General Public License\r
- * along with this program; if not, write to the Free Software\r
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
-*/\r
-\r
-\r
-#include <linux/module.h>\r
-#include <linux/moduleparam.h>\r
-#include <linux/types.h>\r
-#include <linux/timer.h>\r
-#include <linux/miscdevice.h>\r
-#include <linux/watchdog.h>\r
-#include <linux/fs.h>\r
-#include <linux/init.h>\r
-#include <linux/platform_device.h>\r
-#include <linux/interrupt.h>\r
-#include <linux/clk.h>\r
-#include <linux/uaccess.h>\r
-#include <linux/io.h>\r
-#include <asm/mach/map.h>\r
-#ifdef CONFIG_OF\r
-#include <linux/of.h>\r
-#endif\r
-\r
-\r
-/* RK29 registers define */\r
-#define RK29_WDT_CR 0X00\r
-#define RK29_WDT_TORR 0X04\r
-#define RK29_WDT_CCVR 0X08\r
-#define RK29_WDT_CRR 0X0C\r
-#define RK29_WDT_STAT 0X10\r
-#define RK29_WDT_EOI 0X14\r
-\r
-#define RK29_WDT_EN 1\r
-#define RK29_RESPONSE_MODE 1\r
-#define RK29_RESET_PULSE 4\r
-\r
-//THAT wdt's clock is 24MHZ\r
-#define RK29_WDT_2730US 0\r
-#define RK29_WDT_5460US 1\r
-#define RK29_WDT_10920US 2\r
-#define RK29_WDT_21840US 3\r
-#define RK29_WDT_43680US 4\r
-#define RK29_WDT_87360US 5\r
-#define RK29_WDT_174720US 6\r
-#define RK29_WDT_349440US 7\r
-#define RK29_WDT_698880US 8\r
-#define RK29_WDT_1397760US 9\r
-#define RK29_WDT_2795520US 10\r
-#define RK29_WDT_5591040US 11\r
-#define RK29_WDT_11182080US 12\r
-#define RK29_WDT_22364160US 13\r
-#define RK29_WDT_44728320US 14\r
-#define RK29_WDT_89456640US 15\r
-\r
-/*\r
-#define CONFIG_RK29_WATCHDOG_ATBOOT (1)\r
-#define CONFIG_RK29_WATCHDOG_DEFAULT_TIME 10 //unit second\r
-#define CONFIG_RK29_WATCHDOG_DEBUG 1\r
-*/\r
-\r
-static int nowayout = WATCHDOG_NOWAYOUT;\r
-static int tmr_margin = 100;//CONFIG_RK29_WATCHDOG_DEFAULT_TIME;\r
-#ifdef CONFIG_RK29_WATCHDOG_ATBOOT\r
-static int tmr_atboot = 1;\r
-#else\r
-static int tmr_atboot = 0;\r
-#endif\r
-\r
-static int soft_noboot;\r
-\r
-#ifdef CONFIG_RK29_WATCHDOG_DEBUG\r
-static int debug = 1;\r
-#else\r
-static int debug = 0;\r
-#endif\r
-\r
-module_param(tmr_margin, int, 0);\r
-module_param(tmr_atboot, int, 0);\r
-module_param(nowayout, int, 0);\r
-module_param(soft_noboot, int, 0);\r
-module_param(debug, int, 0);\r
-\r
-MODULE_PARM_DESC(tmr_margin, "Watchdog tmr_margin in seconds. default="\r
- __MODULE_STRING(CONFIG_RK29_WATCHDOG_DEFAULT_TIME) ")");\r
-MODULE_PARM_DESC(tmr_atboot,\r
- "Watchdog is started at boot time if set to 1, default="\r
- __MODULE_STRING(CONFIG_RK29_WATCHDOG_ATBOOT));\r
-MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="\r
- __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");\r
-MODULE_PARM_DESC(soft_noboot, "Watchdog action, set to 1 to ignore reboots, "\r
- "0 to reboot (default depends on ONLY_TESTING)");\r
-MODULE_PARM_DESC(debug, "Watchdog debug, set to >1 for debug, (default 0)");\r
-\r
-\r
-static unsigned long open_lock;\r
-static struct device *wdt_dev; /* platform device attached to */\r
-static struct resource *wdt_mem;\r
-static struct resource *wdt_irq;\r
-static struct clk *wdt_clock;\r
-static void __iomem *wdt_base;\r
-static char expect_close;\r
-\r
-\r
-/* watchdog control routines */\r
-\r
-#define DBG(msg...) do { \\r
- if (debug) \\r
- printk(KERN_INFO msg); \\r
- } while (0)\r
-\r
-#define wdt_writel(v, offset) do { writel_relaxed(v, wdt_base + offset); dsb(); } while (0)\r
-\r
-/* functions */\r
-void rk29_wdt_keepalive(void)\r
-{\r
- if (wdt_base)\r
- wdt_writel(0x76, RK29_WDT_CRR);\r
-}\r
-\r
-static void __rk29_wdt_stop(void)\r
-{\r
- rk29_wdt_keepalive(); //feed dog\r
- wdt_writel(0x0a, RK29_WDT_CR);\r
-}\r
-\r
-void rk29_wdt_stop(void)\r
-{\r
- __rk29_wdt_stop();\r
- clk_disable_unprepare(wdt_clock);\r
-}\r
-\r
-/* timeout unit second */\r
-int rk29_wdt_set_heartbeat(int timeout)\r
-{\r
- unsigned int count = 0;\r
- unsigned int torr = 0, acc = 1, maxtime = 0; \r
- unsigned int freq = clk_get_rate(wdt_clock);\r
-\r
- if (timeout < 1)\r
- return -EINVAL;\r
- //0x80000000 is the max count of watch dog\r
- maxtime = 0x80000000 / freq + 1;\r
- if(timeout > maxtime)\r
- timeout = maxtime;\r
- \r
- count = timeout * freq;\r
- count /= 0x10000;\r
-\r
- while(acc < count){\r
- acc *= 2;\r
- torr++;\r
- }\r
- if(torr > 15){\r
- torr = 15;\r
- }\r
- DBG("%s:torr:%d, count:%d, maxtime:%d s\n", __func__, torr, count, maxtime);\r
- wdt_writel(torr, RK29_WDT_TORR);\r
- return 0;\r
-}\r
-\r
-void rk29_wdt_start(void)\r
-{\r
- unsigned long wtcon;\r
- clk_prepare_enable(wdt_clock);\r
- rk29_wdt_set_heartbeat(tmr_margin);\r
- wtcon = (RK29_WDT_EN << 0) | (RK29_RESPONSE_MODE << 1) | (RK29_RESET_PULSE << 2);\r
- wdt_writel(wtcon, RK29_WDT_CR);\r
-}\r
-\r
-/*\r
- * /dev/watchdog handling\r
- */\r
-\r
-static int rk29_wdt_open(struct inode *inode, struct file *file)\r
-{\r
- DBG("%s\n", __func__);\r
- if (test_and_set_bit(0, &open_lock))\r
- return -EBUSY;\r
-\r
- if (nowayout)\r
- __module_get(THIS_MODULE);\r
-\r
- expect_close = 0;\r
-\r
- /* start the timer */\r
- rk29_wdt_start();\r
- return nonseekable_open(inode, file);\r
-}\r
-\r
-static int rk29_wdt_release(struct inode *inode, struct file *file)\r
-{\r
- /*\r
- * Shut off the timer.\r
- * Lock it in if it's a module and we set nowayout\r
- */\r
- DBG("%s\n", __func__);\r
- if (expect_close == 42)\r
- rk29_wdt_stop();\r
- else {\r
- dev_err(wdt_dev, "Unexpected close, not stopping watchdog\n");\r
- rk29_wdt_keepalive();\r
- }\r
- expect_close = 0;\r
- clear_bit(0, &open_lock);\r
- return 0;\r
-}\r
-\r
-static ssize_t rk29_wdt_write(struct file *file, const char __user *data,\r
- size_t len, loff_t *ppos)\r
-{\r
- /*\r
- * Refresh the timer.\r
- */\r
- DBG("%s\n", __func__);\r
- if (len) {\r
- if (!nowayout) {\r
- size_t i;\r
-\r
- /* In case it was set long ago */\r
- expect_close = 0;\r
-\r
- for (i = 0; i != len; i++) {\r
- char c;\r
-\r
- if (get_user(c, data + i))\r
- return -EFAULT;\r
- if (c == 'V')\r
- expect_close = 42;\r
- }\r
- }\r
- rk29_wdt_keepalive();\r
- }\r
- return len;\r
-}\r
-\r
-#define OPTIONS (WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE)\r
-\r
-static const struct watchdog_info rk29_wdt_ident = {\r
- .options = OPTIONS,\r
- .firmware_version = 0,\r
- .identity = "rk29 Watchdog",\r
-};\r
-\r
-\r
-static long rk29_wdt_ioctl(struct file *file, unsigned int cmd,\r
- unsigned long arg)\r
-{\r
- void __user *argp = (void __user *)arg;\r
- int __user *p = argp;\r
- int new_margin;\r
- DBG("%s\n", __func__);\r
- switch (cmd) {\r
- case WDIOC_GETSUPPORT:\r
- return copy_to_user(argp, &rk29_wdt_ident,\r
- sizeof(rk29_wdt_ident)) ? -EFAULT : 0;\r
- case WDIOC_GETSTATUS:\r
- case WDIOC_GETBOOTSTATUS:\r
- return put_user(0, p);\r
- case WDIOC_KEEPALIVE:\r
- DBG("%s:rk29_wdt_keepalive\n", __func__);\r
- rk29_wdt_keepalive();\r
- return 0;\r
- case WDIOC_SETTIMEOUT:\r
- if (get_user(new_margin, p))\r
- return -EFAULT;\r
- if (rk29_wdt_set_heartbeat(new_margin))\r
- return -EINVAL;\r
- rk29_wdt_keepalive();\r
- return put_user(tmr_margin, p);\r
- case WDIOC_GETTIMEOUT:\r
- return put_user(tmr_margin, p);\r
- default:\r
- return -ENOTTY;\r
- }\r
-}\r
-\r
-\r
-\r
-/* kernel interface */\r
-\r
-static const struct file_operations rk29_wdt_fops = {\r
- .owner = THIS_MODULE,\r
- .llseek = no_llseek,\r
- .write = rk29_wdt_write,\r
- .unlocked_ioctl = rk29_wdt_ioctl,\r
- .open = rk29_wdt_open,\r
- .release = rk29_wdt_release,\r
-};\r
-\r
-static struct miscdevice rk29_wdt_miscdev = {\r
- .minor = WATCHDOG_MINOR,\r
- .name = "watchdog",\r
- .fops = &rk29_wdt_fops,\r
-};\r
-\r
-\r
-/* interrupt handler code */\r
-\r
-static irqreturn_t rk29_wdt_irq_handler(int irqno, void *param)\r
-{\r
- DBG("RK29_wdt:watchdog timer expired (irq)\n");\r
- rk29_wdt_keepalive();\r
- return IRQ_HANDLED;\r
-}\r
-\r
-\r
-/* device interface */\r
-\r
-static int rk29_wdt_probe(struct platform_device *pdev)\r
-{\r
- struct resource *res;\r
- struct device *dev;\r
- int started = 0;\r
- int ret, val;\r
-\r
- dev = &pdev->dev;\r
- wdt_dev = &pdev->dev;\r
-\r
- /* get the memory region for the watchdog timer */\r
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);\r
- if (res == NULL) {\r
- dev_err(dev, "no memory resource specified\n");\r
- return -ENOENT;\r
- }\r
-\r
- wdt_base = devm_request_and_ioremap(&pdev->dev, res);\r
- if (wdt_base == NULL) {\r
- dev_err(dev, "failed to ioremap() region\n");\r
- return -EINVAL;\r
- }\r
- \r
-#ifdef CONFIG_OF\r
- if(!of_property_read_u32(pdev->dev.of_node, "rockchip,atboot", &val))\r
- tmr_atboot = val;\r
- else\r
- tmr_atboot = 0;\r
-\r
- if(!of_property_read_u32(pdev->dev.of_node, "rockchip,timeout", &val))\r
- tmr_margin = val;\r
- else\r
- tmr_margin = 0;\r
-\r
- if(!of_property_read_u32(pdev->dev.of_node, "rockchip,debug", &val))\r
- debug = val;\r
- else\r
- debug = 0;\r
- DBG("probe: mapped wdt_base=%p\n", wdt_base);\r
-\r
- of_property_read_u32(pdev->dev.of_node, "rockchip,irq", &val);\r
-#endif\r
-\r
-#ifdef CONFIG_RK29_FEED_DOG_BY_INTE\r
- val = 1;\r
-#endif\r
- //printk("atboot:%d, timeout:%ds, debug:%d, irq:%d\n", tmr_atboot, tmr_margin, debug, val);\r
- \r
- if(val == 1) {\r
- wdt_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);\r
- if (wdt_irq == NULL) {\r
- dev_err(dev, "no irq resource specified\n");\r
- return -ENOENT;\r
- }\r
-\r
- ret = request_irq(wdt_irq->start, rk29_wdt_irq_handler, 0, pdev->name, pdev);\r
- if (ret != 0) {\r
- dev_err(dev, "failed to install irq (%d)\n", ret);\r
- return ret;\r
- }\r
- }\r
-\r
- wdt_clock = devm_clk_get(&pdev->dev, "pclk_wdt");\r
- if (IS_ERR(wdt_clock)) {\r
- dev_err(dev, "failed to find watchdog clock source\n");\r
- ret = PTR_ERR(wdt_clock);\r
- goto err_irq;\r
- }\r
-\r
- ret = misc_register(&rk29_wdt_miscdev);\r
- if (ret) {\r
- dev_err(dev, "cannot register miscdev on minor=%d (%d)\n",\r
- WATCHDOG_MINOR, ret);\r
- goto err_irq;\r
- }\r
- if (tmr_atboot && started == 0) {\r
- dev_info(dev, "starting watchdog timer\n");\r
- rk29_wdt_start();\r
- } else if (!tmr_atboot) {\r
- /* if we're not enabling the watchdog, then ensure it is\r
- * disabled if it has been left running from the bootloader\r
- * or other source */\r
-\r
- rk29_wdt_stop();\r
- }\r
- return 0;\r
-\r
-err_irq:\r
- free_irq(wdt_irq->start, pdev);\r
- return ret;\r
-}\r
-\r
-static int rk29_wdt_remove(struct platform_device *dev)\r
-{\r
- wdt_mem = NULL;\r
- free_irq(wdt_irq->start, dev);\r
- wdt_irq = NULL;\r
- clk_disable_unprepare(wdt_clock);\r
- wdt_clock = NULL;\r
- misc_deregister(&rk29_wdt_miscdev);\r
- return 0;\r
-}\r
-\r
-static void rk29_wdt_shutdown(struct platform_device *dev)\r
-{\r
- rk29_wdt_stop();\r
-}\r
-\r
-#ifdef CONFIG_PM\r
-\r
-static int rk29_wdt_suspend(struct platform_device *dev, pm_message_t state)\r
-{\r
- rk29_wdt_stop();\r
- return 0;\r
-}\r
-\r
-static int rk29_wdt_resume(struct platform_device *dev)\r
-{\r
- rk29_wdt_start();\r
- return 0;\r
-}\r
-\r
-#else\r
-#define rk29_wdt_suspend NULL\r
-#define rk29_wdt_resume NULL\r
-#endif /* CONFIG_PM */\r
-\r
-#ifdef CONFIG_OF\r
-static const struct of_device_id of_rk29_wdt_match[] = {\r
- { .compatible = "rockchip,watch dog" },\r
- { /* Sentinel */ }\r
-};\r
-#endif\r
-static struct platform_driver rk29_wdt_driver = {\r
- .probe = rk29_wdt_probe,\r
- .remove = rk29_wdt_remove,\r
- .shutdown = rk29_wdt_shutdown,\r
- .suspend = rk29_wdt_suspend,\r
- .resume = rk29_wdt_resume,\r
- .driver = {\r
- .owner = THIS_MODULE,\r
-#ifdef CONFIG_OF\r
- .of_match_table = of_rk29_wdt_match,\r
-#endif\r
- .name = "rk29-wdt",\r
- },\r
-};\r
-\r
-\r
-static char banner[] __initdata =\r
- KERN_INFO "RK29 Watchdog Timer, (c) 2011 Rockchip Electronics\n";\r
-\r
-static int __init watchdog_init(void)\r
-{\r
- printk(banner);\r
- return platform_driver_register(&rk29_wdt_driver);\r
-}\r
-\r
-static void __exit watchdog_exit(void)\r
-{\r
- platform_driver_unregister(&rk29_wdt_driver);\r
-}\r
-\r
-subsys_initcall(watchdog_init);\r
-module_exit(watchdog_exit);\r
-\r
-MODULE_AUTHOR("hhb@rock-chips.com");\r
-MODULE_DESCRIPTION("RK29 Watchdog Device Driver");\r
-MODULE_LICENSE("GPL");\r
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);\r
-MODULE_ALIAS("platform:rk29-wdt");\r
+/* linux/drivers/watchdog/rk29_wdt.c
+ *
+ * Copyright (C) 2011 ROCKCHIP, Inc.
+ * hhb@rock-chips.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/timer.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/clk.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+#include <asm/mach/map.h>
+#ifdef CONFIG_OF
+#include <linux/of.h>
+#endif
+
+
+/* RK29 registers define */
+#define RK29_WDT_CR 0X00
+#define RK29_WDT_TORR 0X04
+#define RK29_WDT_CCVR 0X08
+#define RK29_WDT_CRR 0X0C
+#define RK29_WDT_STAT 0X10
+#define RK29_WDT_EOI 0X14
+
+#define RK29_WDT_EN 1
+#define RK29_RESPONSE_MODE 1
+#define RK29_RESET_PULSE 4
+
+//THAT wdt's clock is 24MHZ
+#define RK29_WDT_2730US 0
+#define RK29_WDT_5460US 1
+#define RK29_WDT_10920US 2
+#define RK29_WDT_21840US 3
+#define RK29_WDT_43680US 4
+#define RK29_WDT_87360US 5
+#define RK29_WDT_174720US 6
+#define RK29_WDT_349440US 7
+#define RK29_WDT_698880US 8
+#define RK29_WDT_1397760US 9
+#define RK29_WDT_2795520US 10
+#define RK29_WDT_5591040US 11
+#define RK29_WDT_11182080US 12
+#define RK29_WDT_22364160US 13
+#define RK29_WDT_44728320US 14
+#define RK29_WDT_89456640US 15
+
+/*
+#define CONFIG_RK29_WATCHDOG_ATBOOT (1)
+#define CONFIG_RK29_WATCHDOG_DEFAULT_TIME 10 //unit second
+#define CONFIG_RK29_WATCHDOG_DEBUG 1
+*/
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+static int tmr_margin = 100;//CONFIG_RK29_WATCHDOG_DEFAULT_TIME;
+#ifdef CONFIG_RK29_WATCHDOG_ATBOOT
+static int tmr_atboot = 1;
+#else
+static int tmr_atboot = 0;
+#endif
+
+static int soft_noboot;
+
+#ifdef CONFIG_RK29_WATCHDOG_DEBUG
+static int debug = 1;
+#else
+static int debug = 0;
+#endif
+
+module_param(tmr_margin, int, 0);
+module_param(tmr_atboot, int, 0);
+module_param(nowayout, int, 0);
+module_param(soft_noboot, int, 0);
+module_param(debug, int, 0);
+
+MODULE_PARM_DESC(tmr_margin, "Watchdog tmr_margin in seconds. default="
+ __MODULE_STRING(CONFIG_RK29_WATCHDOG_DEFAULT_TIME) ")");
+MODULE_PARM_DESC(tmr_atboot,
+ "Watchdog is started at boot time if set to 1, default="
+ __MODULE_STRING(CONFIG_RK29_WATCHDOG_ATBOOT));
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+MODULE_PARM_DESC(soft_noboot, "Watchdog action, set to 1 to ignore reboots, "
+ "0 to reboot (default depends on ONLY_TESTING)");
+MODULE_PARM_DESC(debug, "Watchdog debug, set to >1 for debug, (default 0)");
+
+
+static unsigned long open_lock;
+static struct device *wdt_dev; /* platform device attached to */
+static struct resource *wdt_mem;
+static struct resource *wdt_irq;
+static struct clk *wdt_clock;
+static void __iomem *wdt_base;
+static char expect_close;
+
+
+/* watchdog control routines */
+
+#define DBG(msg...) do { \
+ if (debug) \
+ printk(KERN_INFO msg); \
+ } while (0)
+
+#define wdt_writel(v, offset) do { writel_relaxed(v, wdt_base + offset); dsb(); } while (0)
+
+/* functions */
+void rk29_wdt_keepalive(void)
+{
+ if (wdt_base)
+ wdt_writel(0x76, RK29_WDT_CRR);
+}
+
+static void __rk29_wdt_stop(void)
+{
+ rk29_wdt_keepalive(); //feed dog
+ wdt_writel(0x0a, RK29_WDT_CR);
+}
+
+void rk29_wdt_stop(void)
+{
+ __rk29_wdt_stop();
+ clk_disable_unprepare(wdt_clock);
+}
+
+/* timeout unit second */
+int rk29_wdt_set_heartbeat(int timeout)
+{
+ unsigned int count = 0;
+ unsigned int torr = 0, acc = 1, maxtime = 0;
+ unsigned int freq = clk_get_rate(wdt_clock);
+
+ if (timeout < 1)
+ return -EINVAL;
+ //0x80000000 is the max count of watch dog
+ maxtime = 0x80000000 / freq + 1;
+ if(timeout > maxtime)
+ timeout = maxtime;
+
+ count = timeout * freq;
+ count /= 0x10000;
+
+ while(acc < count){
+ acc *= 2;
+ torr++;
+ }
+ if(torr > 15){
+ torr = 15;
+ }
+ DBG("%s:torr:%d, count:%d, maxtime:%d s\n", __func__, torr, count, maxtime);
+ wdt_writel(torr, RK29_WDT_TORR);
+ return 0;
+}
+
+void rk29_wdt_start(void)
+{
+ unsigned long wtcon;
+ clk_prepare_enable(wdt_clock);
+ rk29_wdt_set_heartbeat(tmr_margin);
+ wtcon = (RK29_WDT_EN << 0) | (RK29_RESPONSE_MODE << 1) | (RK29_RESET_PULSE << 2);
+ wdt_writel(wtcon, RK29_WDT_CR);
+}
+
+/*
+ * /dev/watchdog handling
+ */
+
+static int rk29_wdt_open(struct inode *inode, struct file *file)
+{
+ DBG("%s\n", __func__);
+ if (test_and_set_bit(0, &open_lock))
+ return -EBUSY;
+
+ if (nowayout)
+ __module_get(THIS_MODULE);
+
+ expect_close = 0;
+
+ /* start the timer */
+ rk29_wdt_start();
+ return nonseekable_open(inode, file);
+}
+
+static int rk29_wdt_release(struct inode *inode, struct file *file)
+{
+ /*
+ * Shut off the timer.
+ * Lock it in if it's a module and we set nowayout
+ */
+ DBG("%s\n", __func__);
+ if (expect_close == 42)
+ rk29_wdt_stop();
+ else {
+ dev_err(wdt_dev, "Unexpected close, not stopping watchdog\n");
+ rk29_wdt_keepalive();
+ }
+ expect_close = 0;
+ clear_bit(0, &open_lock);
+ return 0;
+}
+
+static ssize_t rk29_wdt_write(struct file *file, const char __user *data,
+ size_t len, loff_t *ppos)
+{
+ /*
+ * Refresh the timer.
+ */
+ DBG("%s\n", __func__);
+ if (len) {
+ if (!nowayout) {
+ size_t i;
+
+ /* In case it was set long ago */
+ expect_close = 0;
+
+ for (i = 0; i != len; i++) {
+ char c;
+
+ if (get_user(c, data + i))
+ return -EFAULT;
+ if (c == 'V')
+ expect_close = 42;
+ }
+ }
+ rk29_wdt_keepalive();
+ }
+ return len;
+}
+
+#define OPTIONS (WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE)
+
+static const struct watchdog_info rk29_wdt_ident = {
+ .options = OPTIONS,
+ .firmware_version = 0,
+ .identity = "rk29 Watchdog",
+};
+
+
+static long rk29_wdt_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ int __user *p = argp;
+ int new_margin;
+ DBG("%s\n", __func__);
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ return copy_to_user(argp, &rk29_wdt_ident,
+ sizeof(rk29_wdt_ident)) ? -EFAULT : 0;
+ case WDIOC_GETSTATUS:
+ case WDIOC_GETBOOTSTATUS:
+ return put_user(0, p);
+ case WDIOC_KEEPALIVE:
+ DBG("%s:rk29_wdt_keepalive\n", __func__);
+ rk29_wdt_keepalive();
+ return 0;
+ case WDIOC_SETTIMEOUT:
+ if (get_user(new_margin, p))
+ return -EFAULT;
+ if (rk29_wdt_set_heartbeat(new_margin))
+ return -EINVAL;
+ rk29_wdt_keepalive();
+ return put_user(tmr_margin, p);
+ case WDIOC_GETTIMEOUT:
+ return put_user(tmr_margin, p);
+ default:
+ return -ENOTTY;
+ }
+}
+
+
+
+/* kernel interface */
+
+static const struct file_operations rk29_wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = rk29_wdt_write,
+ .unlocked_ioctl = rk29_wdt_ioctl,
+ .open = rk29_wdt_open,
+ .release = rk29_wdt_release,
+};
+
+static struct miscdevice rk29_wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &rk29_wdt_fops,
+};
+
+
+/* interrupt handler code */
+
+static irqreturn_t rk29_wdt_irq_handler(int irqno, void *param)
+{
+ DBG("RK29_wdt:watchdog timer expired (irq)\n");
+ rk29_wdt_keepalive();
+ return IRQ_HANDLED;
+}
+
+
+/* device interface */
+
+static int rk29_wdt_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct device *dev;
+ int started = 0;
+ int ret, val;
+
+ dev = &pdev->dev;
+ wdt_dev = &pdev->dev;
+
+ /* get the memory region for the watchdog timer */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ dev_err(dev, "no memory resource specified\n");
+ return -ENOENT;
+ }
+
+ wdt_base = devm_request_and_ioremap(&pdev->dev, res);
+ if (wdt_base == NULL) {
+ dev_err(dev, "failed to ioremap() region\n");
+ return -EINVAL;
+ }
+
+#ifdef CONFIG_OF
+ if(!of_property_read_u32(pdev->dev.of_node, "rockchip,atboot", &val))
+ tmr_atboot = val;
+ else
+ tmr_atboot = 0;
+
+ if(!of_property_read_u32(pdev->dev.of_node, "rockchip,timeout", &val))
+ tmr_margin = val;
+ else
+ tmr_margin = 0;
+
+ if(!of_property_read_u32(pdev->dev.of_node, "rockchip,debug", &val))
+ debug = val;
+ else
+ debug = 0;
+ DBG("probe: mapped wdt_base=%p\n", wdt_base);
+
+ of_property_read_u32(pdev->dev.of_node, "rockchip,irq", &val);
+#endif
+
+#ifdef CONFIG_RK29_FEED_DOG_BY_INTE
+ val = 1;
+#endif
+ //printk("atboot:%d, timeout:%ds, debug:%d, irq:%d\n", tmr_atboot, tmr_margin, debug, val);
+
+ if(val == 1) {
+ wdt_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (wdt_irq == NULL) {
+ dev_err(dev, "no irq resource specified\n");
+ return -ENOENT;
+ }
+
+ ret = request_irq(wdt_irq->start, rk29_wdt_irq_handler, 0, pdev->name, pdev);
+ if (ret != 0) {
+ dev_err(dev, "failed to install irq (%d)\n", ret);
+ return ret;
+ }
+ }
+
+ wdt_clock = devm_clk_get(&pdev->dev, "pclk_wdt");
+ if (IS_ERR(wdt_clock)) {
+ dev_err(dev, "failed to find watchdog clock source\n");
+ ret = PTR_ERR(wdt_clock);
+ goto err_irq;
+ }
+
+ ret = misc_register(&rk29_wdt_miscdev);
+ if (ret) {
+ dev_err(dev, "cannot register miscdev on minor=%d (%d)\n",
+ WATCHDOG_MINOR, ret);
+ goto err_irq;
+ }
+ if (tmr_atboot && started == 0) {
+ dev_info(dev, "starting watchdog timer\n");
+ rk29_wdt_start();
+ } else if (!tmr_atboot) {
+ /* if we're not enabling the watchdog, then ensure it is
+ * disabled if it has been left running from the bootloader
+ * or other source */
+
+ rk29_wdt_stop();
+ }
+ return 0;
+
+err_irq:
+ free_irq(wdt_irq->start, pdev);
+ return ret;
+}
+
+static int rk29_wdt_remove(struct platform_device *dev)
+{
+ wdt_mem = NULL;
+ free_irq(wdt_irq->start, dev);
+ wdt_irq = NULL;
+ clk_disable_unprepare(wdt_clock);
+ wdt_clock = NULL;
+ misc_deregister(&rk29_wdt_miscdev);
+ return 0;
+}
+
+static void rk29_wdt_shutdown(struct platform_device *dev)
+{
+ rk29_wdt_stop();
+}
+
+#ifdef CONFIG_PM
+
+static int rk29_wdt_suspend(struct platform_device *dev, pm_message_t state)
+{
+ rk29_wdt_stop();
+ return 0;
+}
+
+static int rk29_wdt_resume(struct platform_device *dev)
+{
+ rk29_wdt_start();
+ return 0;
+}
+
+#else
+#define rk29_wdt_suspend NULL
+#define rk29_wdt_resume NULL
+#endif /* CONFIG_PM */
+
+#ifdef CONFIG_OF
+static const struct of_device_id of_rk29_wdt_match[] = {
+ { .compatible = "rockchip,watch dog" },
+ { /* Sentinel */ }
+};
+#endif
+static struct platform_driver rk29_wdt_driver = {
+ .probe = rk29_wdt_probe,
+ .remove = rk29_wdt_remove,
+ .shutdown = rk29_wdt_shutdown,
+ .suspend = rk29_wdt_suspend,
+ .resume = rk29_wdt_resume,
+ .driver = {
+ .owner = THIS_MODULE,
+#ifdef CONFIG_OF
+ .of_match_table = of_rk29_wdt_match,
+#endif
+ .name = "rk29-wdt",
+ },
+};
+
+
+static char banner[] __initdata =
+ KERN_INFO "RK29 Watchdog Timer, (c) 2011 Rockchip Electronics\n";
+
+static int __init watchdog_init(void)
+{
+ printk(banner);
+ return platform_driver_register(&rk29_wdt_driver);
+}
+
+static void __exit watchdog_exit(void)
+{
+ platform_driver_unregister(&rk29_wdt_driver);
+}
+
+subsys_initcall(watchdog_init);
+module_exit(watchdog_exit);
+
+MODULE_AUTHOR("hhb@rock-chips.com");
+MODULE_DESCRIPTION("RK29 Watchdog Device Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
+MODULE_ALIAS("platform:rk29-wdt");