Merge tag 'lsk-android-14.04' into develop-3.10
[firefly-linux-kernel-4.4.55.git] / drivers / watchdog / rk29_wdt.c
1 /* linux/drivers/watchdog/rk29_wdt.c\r
2  *\r
3  * Copyright (C) 2011 ROCKCHIP, Inc.\r
4  *      hhb@rock-chips.com\r
5  *\r
6  * This program is free software; you can redistribute it and/or modify\r
7  * it under the terms of the GNU General Public License as published by\r
8  * the Free Software Foundation; either version 2 of the License, or\r
9  * (at your option) any later version.\r
10  *\r
11  * This program is distributed in the hope that it will be useful,\r
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
14  * GNU General Public License for more details.\r
15  *\r
16  * You should have received a copy of the GNU General Public License\r
17  * along with this program; if not, write to the Free Software\r
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
19 */\r
20 \r
21 \r
22 #include <linux/module.h>\r
23 #include <linux/moduleparam.h>\r
24 #include <linux/types.h>\r
25 #include <linux/timer.h>\r
26 #include <linux/miscdevice.h>\r
27 #include <linux/watchdog.h>\r
28 #include <linux/fs.h>\r
29 #include <linux/init.h>\r
30 #include <linux/platform_device.h>\r
31 #include <linux/interrupt.h>\r
32 #include <linux/clk.h>\r
33 #include <linux/uaccess.h>\r
34 #include <linux/io.h>\r
35 #include <asm/mach/map.h>\r
36 #ifdef CONFIG_OF\r
37 #include <linux/of.h>\r
38 #endif\r
39 \r
40 \r
41 /* RK29 registers define */\r
42 #define RK29_WDT_CR     0X00\r
43 #define RK29_WDT_TORR   0X04\r
44 #define RK29_WDT_CCVR   0X08\r
45 #define RK29_WDT_CRR    0X0C\r
46 #define RK29_WDT_STAT   0X10\r
47 #define RK29_WDT_EOI    0X14\r
48 \r
49 #define RK29_WDT_EN     1\r
50 #define RK29_RESPONSE_MODE      1\r
51 #define RK29_RESET_PULSE    4\r
52 \r
53 //THAT wdt's clock is 24MHZ\r
54 #define RK29_WDT_2730US         0\r
55 #define RK29_WDT_5460US         1\r
56 #define RK29_WDT_10920US        2\r
57 #define RK29_WDT_21840US        3\r
58 #define RK29_WDT_43680US        4\r
59 #define RK29_WDT_87360US        5\r
60 #define RK29_WDT_174720US       6\r
61 #define RK29_WDT_349440US       7\r
62 #define RK29_WDT_698880US       8\r
63 #define RK29_WDT_1397760US      9\r
64 #define RK29_WDT_2795520US      10\r
65 #define RK29_WDT_5591040US      11\r
66 #define RK29_WDT_11182080US     12\r
67 #define RK29_WDT_22364160US     13\r
68 #define RK29_WDT_44728320US     14\r
69 #define RK29_WDT_89456640US     15\r
70 \r
71 /*\r
72 #define CONFIG_RK29_WATCHDOG_ATBOOT             (1)\r
73 #define CONFIG_RK29_WATCHDOG_DEFAULT_TIME       10      //unit second\r
74 #define CONFIG_RK29_WATCHDOG_DEBUG      1\r
75 */\r
76 \r
77 static int nowayout     = WATCHDOG_NOWAYOUT;\r
78 static int tmr_margin   = 100;//CONFIG_RK29_WATCHDOG_DEFAULT_TIME;\r
79 #ifdef CONFIG_RK29_WATCHDOG_ATBOOT\r
80 static int tmr_atboot   = 1;\r
81 #else\r
82 static int tmr_atboot   = 0;\r
83 #endif\r
84 \r
85 static int soft_noboot;\r
86 \r
87 #ifdef CONFIG_RK29_WATCHDOG_DEBUG\r
88 static int debug        = 1;\r
89 #else\r
90 static int debug        = 0;\r
91 #endif\r
92 \r
93 module_param(tmr_margin,  int, 0);\r
94 module_param(tmr_atboot,  int, 0);\r
95 module_param(nowayout,    int, 0);\r
96 module_param(soft_noboot, int, 0);\r
97 module_param(debug,       int, 0);\r
98 \r
99 MODULE_PARM_DESC(tmr_margin, "Watchdog tmr_margin in seconds. default="\r
100                 __MODULE_STRING(CONFIG_RK29_WATCHDOG_DEFAULT_TIME) ")");\r
101 MODULE_PARM_DESC(tmr_atboot,\r
102                 "Watchdog is started at boot time if set to 1, default="\r
103                         __MODULE_STRING(CONFIG_RK29_WATCHDOG_ATBOOT));\r
104 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="\r
105                         __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");\r
106 MODULE_PARM_DESC(soft_noboot, "Watchdog action, set to 1 to ignore reboots, "\r
107                         "0 to reboot (default depends on ONLY_TESTING)");\r
108 MODULE_PARM_DESC(debug, "Watchdog debug, set to >1 for debug, (default 0)");\r
109 \r
110 \r
111 static unsigned long open_lock;\r
112 static struct device    *wdt_dev;       /* platform device attached to */\r
113 static struct resource  *wdt_mem;\r
114 static struct resource  *wdt_irq;\r
115 static struct clk       *wdt_clock;\r
116 static void __iomem     *wdt_base;\r
117 static char              expect_close;\r
118 \r
119 \r
120 /* watchdog control routines */\r
121 \r
122 #define DBG(msg...) do { \\r
123         if (debug) \\r
124                 printk(KERN_INFO msg); \\r
125         } while (0)\r
126 \r
127 #define wdt_writel(v, offset) do { writel_relaxed(v, wdt_base + offset); dsb(); } while (0)\r
128 \r
129 /* functions */\r
130 void rk29_wdt_keepalive(void)\r
131 {\r
132         if (wdt_base)\r
133                 wdt_writel(0x76, RK29_WDT_CRR);\r
134 }\r
135 \r
136 static void __rk29_wdt_stop(void)\r
137 {\r
138         rk29_wdt_keepalive();    //feed dog\r
139         wdt_writel(0x0a, RK29_WDT_CR);\r
140 }\r
141 \r
142 void rk29_wdt_stop(void)\r
143 {\r
144         __rk29_wdt_stop();\r
145         clk_disable_unprepare(wdt_clock);\r
146 }\r
147 \r
148 /* timeout unit second */\r
149 int rk29_wdt_set_heartbeat(int timeout)\r
150 {\r
151         unsigned int count = 0;\r
152         unsigned int torr = 0, acc = 1, maxtime = 0;    \r
153         unsigned int freq = clk_get_rate(wdt_clock);\r
154 \r
155         if (timeout < 1)\r
156                 return -EINVAL;\r
157         //0x80000000 is the max count of watch dog\r
158         maxtime = 0x80000000 / freq + 1;\r
159         if(timeout > maxtime)\r
160                 timeout = maxtime;\r
161                 \r
162         count = timeout * freq;\r
163         count /= 0x10000;\r
164 \r
165         while(acc < count){\r
166                 acc *= 2;\r
167                 torr++;\r
168         }\r
169         if(torr > 15){\r
170                 torr = 15;\r
171         }\r
172         DBG("%s:torr:%d, count:%d, maxtime:%d s\n", __func__, torr, count, maxtime);\r
173         wdt_writel(torr, RK29_WDT_TORR);\r
174         return 0;\r
175 }\r
176 \r
177 void rk29_wdt_start(void)\r
178 {\r
179         unsigned long wtcon;\r
180         clk_prepare_enable(wdt_clock);\r
181         rk29_wdt_set_heartbeat(tmr_margin);\r
182         wtcon = (RK29_WDT_EN << 0) | (RK29_RESPONSE_MODE << 1) | (RK29_RESET_PULSE << 2);\r
183         wdt_writel(wtcon, RK29_WDT_CR);\r
184 }\r
185 \r
186 /*\r
187  *      /dev/watchdog handling\r
188  */\r
189 \r
190 static int rk29_wdt_open(struct inode *inode, struct file *file)\r
191 {\r
192         DBG("%s\n", __func__);\r
193         if (test_and_set_bit(0, &open_lock))\r
194                 return -EBUSY;\r
195 \r
196         if (nowayout)\r
197                 __module_get(THIS_MODULE);\r
198 \r
199         expect_close = 0;\r
200 \r
201         /* start the timer */\r
202         rk29_wdt_start();\r
203         return nonseekable_open(inode, file);\r
204 }\r
205 \r
206 static int rk29_wdt_release(struct inode *inode, struct file *file)\r
207 {\r
208         /*\r
209          *      Shut off the timer.\r
210          *      Lock it in if it's a module and we set nowayout\r
211          */\r
212         DBG("%s\n", __func__);\r
213         if (expect_close == 42)\r
214                 rk29_wdt_stop();\r
215         else {\r
216                 dev_err(wdt_dev, "Unexpected close, not stopping watchdog\n");\r
217                 rk29_wdt_keepalive();\r
218         }\r
219         expect_close = 0;\r
220         clear_bit(0, &open_lock);\r
221         return 0;\r
222 }\r
223 \r
224 static ssize_t rk29_wdt_write(struct file *file, const char __user *data,\r
225                                 size_t len, loff_t *ppos)\r
226 {\r
227         /*\r
228          *      Refresh the timer.\r
229          */\r
230         DBG("%s\n", __func__);\r
231         if (len) {\r
232                 if (!nowayout) {\r
233                         size_t i;\r
234 \r
235                         /* In case it was set long ago */\r
236                         expect_close = 0;\r
237 \r
238                         for (i = 0; i != len; i++) {\r
239                                 char c;\r
240 \r
241                                 if (get_user(c, data + i))\r
242                                         return -EFAULT;\r
243                                 if (c == 'V')\r
244                                         expect_close = 42;\r
245                         }\r
246                 }\r
247                 rk29_wdt_keepalive();\r
248         }\r
249         return len;\r
250 }\r
251 \r
252 #define OPTIONS (WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE)\r
253 \r
254 static const struct watchdog_info rk29_wdt_ident = {\r
255         .options          =     OPTIONS,\r
256         .firmware_version =     0,\r
257         .identity         =     "rk29 Watchdog",\r
258 };\r
259 \r
260 \r
261 static long rk29_wdt_ioctl(struct file *file,   unsigned int cmd,\r
262                                                         unsigned long arg)\r
263 {\r
264         void __user *argp = (void __user *)arg;\r
265         int __user *p = argp;\r
266         int new_margin;\r
267         DBG("%s\n", __func__);\r
268         switch (cmd) {\r
269         case WDIOC_GETSUPPORT:\r
270                 return copy_to_user(argp, &rk29_wdt_ident,\r
271                         sizeof(rk29_wdt_ident)) ? -EFAULT : 0;\r
272         case WDIOC_GETSTATUS:\r
273         case WDIOC_GETBOOTSTATUS:\r
274                 return put_user(0, p);\r
275         case WDIOC_KEEPALIVE:\r
276                 DBG("%s:rk29_wdt_keepalive\n", __func__);\r
277                 rk29_wdt_keepalive();\r
278                 return 0;\r
279         case WDIOC_SETTIMEOUT:\r
280                 if (get_user(new_margin, p))\r
281                         return -EFAULT;\r
282                 if (rk29_wdt_set_heartbeat(new_margin))\r
283                         return -EINVAL;\r
284                 rk29_wdt_keepalive();\r
285                 return put_user(tmr_margin, p);\r
286         case WDIOC_GETTIMEOUT:\r
287                 return put_user(tmr_margin, p);\r
288         default:\r
289                 return -ENOTTY;\r
290         }\r
291 }\r
292 \r
293 \r
294 \r
295 /* kernel interface */\r
296 \r
297 static const struct file_operations rk29_wdt_fops = {\r
298         .owner          = THIS_MODULE,\r
299         .llseek         = no_llseek,\r
300         .write          = rk29_wdt_write,\r
301         .unlocked_ioctl = rk29_wdt_ioctl,\r
302         .open           = rk29_wdt_open,\r
303         .release        = rk29_wdt_release,\r
304 };\r
305 \r
306 static struct miscdevice rk29_wdt_miscdev = {\r
307         .minor          = WATCHDOG_MINOR,\r
308         .name           = "watchdog",\r
309         .fops           = &rk29_wdt_fops,\r
310 };\r
311 \r
312 \r
313 /* interrupt handler code */\r
314 \r
315 static irqreturn_t rk29_wdt_irq_handler(int irqno, void *param)\r
316 {\r
317         DBG("RK29_wdt:watchdog timer expired (irq)\n");\r
318         rk29_wdt_keepalive();\r
319         return IRQ_HANDLED;\r
320 }\r
321 \r
322 \r
323 /* device interface */\r
324 \r
325 static int rk29_wdt_probe(struct platform_device *pdev)\r
326 {\r
327         struct resource *res;\r
328         struct device *dev;\r
329         int started = 0;\r
330         int ret, val;\r
331 \r
332         dev = &pdev->dev;\r
333         wdt_dev = &pdev->dev;\r
334 \r
335         /* get the memory region for the watchdog timer */\r
336         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);\r
337         if (res == NULL) {\r
338                 dev_err(dev, "no memory resource specified\n");\r
339                 return -ENOENT;\r
340         }\r
341 \r
342         wdt_base = devm_request_and_ioremap(&pdev->dev, res);\r
343         if (wdt_base == NULL) {\r
344                 dev_err(dev, "failed to ioremap() region\n");\r
345                 return -EINVAL;\r
346         }\r
347         \r
348 #ifdef CONFIG_OF\r
349         if(!of_property_read_u32(pdev->dev.of_node, "rockchip,atboot", &val))\r
350                 tmr_atboot = val;\r
351         else\r
352                 tmr_atboot = 0;\r
353 \r
354         if(!of_property_read_u32(pdev->dev.of_node, "rockchip,timeout", &val))\r
355                 tmr_margin = val;\r
356         else\r
357                 tmr_margin = 0;\r
358 \r
359         if(!of_property_read_u32(pdev->dev.of_node, "rockchip,debug", &val))\r
360                 debug = val;\r
361         else\r
362                 debug = 0;\r
363         DBG("probe: mapped wdt_base=%p\n", wdt_base);\r
364 \r
365         of_property_read_u32(pdev->dev.of_node, "rockchip,irq", &val);\r
366 #endif\r
367 \r
368 #ifdef CONFIG_RK29_FEED_DOG_BY_INTE\r
369         val = 1;\r
370 #endif\r
371         //printk("atboot:%d, timeout:%ds, debug:%d, irq:%d\n", tmr_atboot, tmr_margin, debug, val);\r
372         \r
373         if(val == 1) {\r
374                 wdt_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);\r
375                 if (wdt_irq == NULL) {\r
376                         dev_err(dev, "no irq resource specified\n");\r
377                         return -ENOENT;\r
378                 }\r
379 \r
380                 ret = request_irq(wdt_irq->start, rk29_wdt_irq_handler, 0, pdev->name, pdev);\r
381                 if (ret != 0) {\r
382                         dev_err(dev, "failed to install irq (%d)\n", ret);\r
383                         return ret;\r
384                 }\r
385         }\r
386 \r
387         wdt_clock = devm_clk_get(&pdev->dev, "pclk_wdt");\r
388         if (IS_ERR(wdt_clock)) {\r
389                 dev_err(dev, "failed to find watchdog clock source\n");\r
390                 ret = PTR_ERR(wdt_clock);\r
391                 goto err_irq;\r
392         }\r
393 \r
394         ret = misc_register(&rk29_wdt_miscdev);\r
395         if (ret) {\r
396                 dev_err(dev, "cannot register miscdev on minor=%d (%d)\n",\r
397                         WATCHDOG_MINOR, ret);\r
398                 goto err_irq;\r
399         }\r
400         if (tmr_atboot && started == 0) {\r
401                 dev_info(dev, "starting watchdog timer\n");\r
402                 rk29_wdt_start();\r
403         } else if (!tmr_atboot) {\r
404                 /* if we're not enabling the watchdog, then ensure it is\r
405                  * disabled if it has been left running from the bootloader\r
406                  * or other source */\r
407 \r
408                 rk29_wdt_stop();\r
409         }\r
410         return 0;\r
411 \r
412 err_irq:\r
413         free_irq(wdt_irq->start, pdev);\r
414         return ret;\r
415 }\r
416 \r
417 static int rk29_wdt_remove(struct platform_device *dev)\r
418 {\r
419         wdt_mem = NULL;\r
420         free_irq(wdt_irq->start, dev);\r
421         wdt_irq = NULL;\r
422         clk_disable_unprepare(wdt_clock);\r
423         wdt_clock = NULL;\r
424         misc_deregister(&rk29_wdt_miscdev);\r
425         return 0;\r
426 }\r
427 \r
428 static void rk29_wdt_shutdown(struct platform_device *dev)\r
429 {\r
430         rk29_wdt_stop();\r
431 }\r
432 \r
433 #ifdef CONFIG_PM\r
434 \r
435 static int rk29_wdt_suspend(struct platform_device *dev, pm_message_t state)\r
436 {\r
437         rk29_wdt_stop();\r
438         return 0;\r
439 }\r
440 \r
441 static int rk29_wdt_resume(struct platform_device *dev)\r
442 {\r
443         rk29_wdt_start();\r
444         return 0;\r
445 }\r
446 \r
447 #else\r
448 #define rk29_wdt_suspend NULL\r
449 #define rk29_wdt_resume  NULL\r
450 #endif /* CONFIG_PM */\r
451 \r
452 #ifdef CONFIG_OF\r
453 static const struct of_device_id of_rk29_wdt_match[] = {\r
454         { .compatible = "rockchip,watch dog" },\r
455         { /* Sentinel */ }\r
456 };\r
457 #endif\r
458 static struct platform_driver rk29_wdt_driver = {\r
459         .probe          = rk29_wdt_probe,\r
460         .remove         = rk29_wdt_remove,\r
461         .shutdown       = rk29_wdt_shutdown,\r
462         .suspend        = rk29_wdt_suspend,\r
463         .resume         = rk29_wdt_resume,\r
464         .driver         = {\r
465                 .owner  = THIS_MODULE,\r
466 #ifdef CONFIG_OF\r
467                 .of_match_table = of_rk29_wdt_match,\r
468 #endif\r
469                 .name   = "rk29-wdt",\r
470         },\r
471 };\r
472 \r
473 \r
474 static char banner[] __initdata =\r
475         KERN_INFO "RK29 Watchdog Timer, (c) 2011 Rockchip Electronics\n";\r
476 \r
477 static int __init watchdog_init(void)\r
478 {\r
479         printk(banner);\r
480         return platform_driver_register(&rk29_wdt_driver);\r
481 }\r
482 \r
483 static void __exit watchdog_exit(void)\r
484 {\r
485         platform_driver_unregister(&rk29_wdt_driver);\r
486 }\r
487 \r
488 subsys_initcall(watchdog_init);\r
489 module_exit(watchdog_exit);\r
490 \r
491 MODULE_AUTHOR("hhb@rock-chips.com");\r
492 MODULE_DESCRIPTION("RK29 Watchdog Device Driver");\r
493 MODULE_LICENSE("GPL");\r
494 MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);\r
495 MODULE_ALIAS("platform:rk29-wdt");\r