video: tegra: dc: Add quick for Vizio P series
[linux-3.10.git] / drivers / watchdog / softdog_platform.c
1 /*
2  * SoftDog-platform: A platform based Software Watchdog Device
3  *
4  * Copyright (c) 2014, NVIDIA CORPORATION.  All rights reserved.
5  *
6  * Author: Laxman Dewangan <ldewangan@nvidia.com>
7  *
8  * Based on the softdog by:
9  *      (c) Copyright 1996 Alan Cox <alan@lxorguk.ukuu.org.uk>,
10  *      All Rights Reserved.
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  */
21
22 #include <linux/module.h>
23 #include <linux/types.h>
24 #include <linux/timer.h>
25 #include <linux/watchdog.h>
26 #include <linux/notifier.h>
27 #include <linux/reboot.h>
28 #include <linux/init.h>
29 #include <linux/jiffies.h>
30 #include <linux/kernel.h>
31 #include <linux/of.h>
32 #include <linux/of_device.h>
33 #include <linux/platform_device.h>
34 #include <linux/platform_device.h>
35 #include <linux/slab.h>
36
37 #define TIMER_MARGIN    60              /* Default is 60 seconds */
38 struct softdog_platform_wdt {
39         struct watchdog_device wdt_dev;
40         struct device *dev;
41         unsigned int soft_margin;
42         bool nowayout;
43         int soft_noboot;
44         int soft_panic;
45         struct timer_list watchdog_ticktock;
46         struct notifier_block nb;
47         int is_stopped;
48 };
49
50 /* If the timer expires..  */
51 static void softdog_platform_watchdog_fire(unsigned long data)
52 {
53         struct softdog_platform_wdt *swdt = (struct softdog_platform_wdt *)data;
54
55         if (swdt->is_stopped)
56                 return;
57
58         if (swdt->soft_noboot)
59                 dev_crit(swdt->dev, "Triggered - Reboot ignored\n");
60         else if (swdt->soft_panic) {
61                 dev_crit(swdt->dev, "Initiating panic\n");
62                 panic("Software Watchdog Timer expired");
63         } else {
64                 dev_crit(swdt->dev, "Initiating system reboot\n");
65                 emergency_restart();
66                 dev_crit(swdt->dev, "Reboot didn't ?????\n");
67         }
68 }
69
70 /* Softdog operations */
71 static int softdog_platform_ping(struct watchdog_device *wdt)
72 {
73         struct softdog_platform_wdt *swdt =  watchdog_get_drvdata(wdt);
74
75         if (swdt->is_stopped)
76                 return 0;
77
78         mod_timer(&swdt->watchdog_ticktock, jiffies+(wdt->timeout*HZ));
79         return 0;
80 }
81
82 static int softdog_platform_start(struct watchdog_device *wdt)
83 {
84         struct softdog_platform_wdt *swdt =  watchdog_get_drvdata(wdt);
85
86         swdt->is_stopped = false;
87         mod_timer(&swdt->watchdog_ticktock, jiffies+(wdt->timeout*HZ));
88         return 0;
89 }
90
91 static int softdog_platform_stop(struct watchdog_device *wdt)
92 {
93         struct softdog_platform_wdt *swdt =  watchdog_get_drvdata(wdt);
94
95         swdt->is_stopped = true;
96         return 0;
97 }
98
99 static int softdog_platform_set_timeout(struct watchdog_device *wdt,
100         unsigned int t)
101 {
102         wdt->timeout = t;
103         return 0;
104 }
105
106 /* Notifier for system down */
107 static int softdog_platform_notify_sys(struct notifier_block *this,
108         unsigned long code, void *ptr)
109 {
110         struct softdog_platform_wdt *swdt = container_of(this,
111                                         struct softdog_platform_wdt, nb);
112
113         if (code == SYS_DOWN || code == SYS_HALT)
114                 /* Turn the WDT off */
115                 softdog_platform_stop(&swdt->wdt_dev);
116
117         return NOTIFY_DONE;
118 }
119
120 static struct watchdog_info softdog_platform_info = {
121         .identity = "Software Watchdog",
122         .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
123 };
124
125 static struct watchdog_ops softdog_platform_ops = {
126         .owner = THIS_MODULE,
127         .start = softdog_platform_start,
128         .stop = softdog_platform_stop,
129         .ping = softdog_platform_ping,
130         .set_timeout = softdog_platform_set_timeout,
131 };
132
133 static int softdog_platform_probe(struct platform_device *pdev)
134 {
135         struct softdog_platform_wdt *swdt;
136         struct device_node *np = pdev->dev.of_node;
137         u32 pval;
138         int ret;
139
140         if (!np) {
141                 dev_err(&pdev->dev, "Only DT registration supported\n");
142                 return -ENODEV;
143         }
144
145         swdt = devm_kzalloc(&pdev->dev, sizeof(*swdt), GFP_KERNEL);
146         if (!swdt)
147                 return -ENOMEM;
148
149         swdt->nowayout = of_property_read_bool(np, "watchdog,nowayout");
150         swdt->soft_noboot = !of_property_read_bool(np, "watchdog,reboot");
151         swdt->soft_panic = of_property_read_bool(np, "watchdog,panic");
152         ret = of_property_read_u32(np, "watchdog,margin", &pval);
153         if (!ret)
154                 swdt->soft_margin = pval;
155         else
156                 swdt->soft_margin = TIMER_MARGIN;
157         /*
158          * Check that the soft_margin value is within it's range;
159          * if not reset to the default
160          */
161         if (swdt->soft_margin < 1 || swdt->soft_margin > 65535) {
162                 dev_err(&pdev->dev,
163                         "soft_margin must be 0 to 65536, using %d\n",
164                         TIMER_MARGIN);
165                 return -EINVAL;
166         }
167
168         swdt->wdt_dev.timeout = swdt->soft_margin;
169         swdt->nb.notifier_call = softdog_platform_notify_sys;
170         swdt->dev = &pdev->dev;
171         watchdog_set_nowayout(&swdt->wdt_dev, swdt->nowayout);
172         watchdog_set_drvdata(&swdt->wdt_dev, swdt);
173         platform_set_drvdata(pdev, swdt);
174
175         swdt->wdt_dev.info = &softdog_platform_info;
176         swdt->wdt_dev.ops = &softdog_platform_ops;
177         swdt->wdt_dev.min_timeout = 1;
178         swdt->wdt_dev.max_timeout = 0xFFFF;
179
180         setup_timer(&swdt->watchdog_ticktock, softdog_platform_watchdog_fire,
181                                 (unsigned long)swdt);
182
183         ret = register_reboot_notifier(&swdt->nb);
184         if (ret) {
185                 dev_err(&pdev->dev,
186                         "cannot register reboot notifier (err=%d)\n", ret);
187                 goto timer_del;
188         }
189
190         ret = watchdog_register_device(&swdt->wdt_dev);
191         if (ret) {
192                 dev_err(&pdev->dev, "Watchdog register failed: %d\n", ret);
193                 goto reboot_unreg;
194         }
195
196         dev_info(&pdev->dev, "Software Watchdog Timer: initialized\n");
197         return 0;
198
199 reboot_unreg:
200         unregister_reboot_notifier(&swdt->nb);
201 timer_del:
202         del_timer_sync(&swdt->watchdog_ticktock);
203         return ret;
204 }
205
206 static int softdog_platform_remove(struct platform_device *pdev)
207 {
208         struct softdog_platform_wdt *swdt = platform_get_drvdata(pdev);
209
210         del_timer_sync(&swdt->watchdog_ticktock);
211         watchdog_unregister_device(&swdt->wdt_dev);
212         unregister_reboot_notifier(&swdt->nb);
213         return 0;
214 }
215
216 static void softdog_platform_shutdown(struct platform_device *pdev)
217 {
218         struct softdog_platform_wdt *swdt = platform_get_drvdata(pdev);
219
220         del_timer_sync(&swdt->watchdog_ticktock);
221 }
222
223 static int softdog_platform_suspend(struct device *dev)
224 {
225         struct softdog_platform_wdt *swdt = dev_get_drvdata(dev);
226         int ret;
227
228         ret = softdog_platform_stop(&swdt->wdt_dev);
229         if (ret < 0)
230                 dev_err(swdt->dev, "wdt stop failed: %d\n", ret);
231         return 0;
232 }
233
234 static int softdog_platform_resume(struct device *dev)
235 {
236         struct softdog_platform_wdt *swdt = dev_get_drvdata(dev);
237         int ret;
238
239         ret = softdog_platform_start(&swdt->wdt_dev);
240         if (ret < 0)
241                 dev_err(swdt->dev, "wdt start failed: %d\n", ret);
242         return 0;
243 }
244
245 static const struct dev_pm_ops softdog_platform_pm_ops = {
246         SET_SYSTEM_SLEEP_PM_OPS(softdog_platform_suspend,
247                         softdog_platform_resume)
248 };
249
250 static struct of_device_id softdog_platform_of_match[] = {
251         { .compatible = "softdog-platform", },
252         {},
253 };
254 MODULE_DEVICE_TABLE(of, softdog_platform_of_match);
255
256 static struct platform_driver softdog_platform_driver = {
257         .driver = {
258                 .name = "softdog-platform",
259                 .owner = THIS_MODULE,
260                 .of_match_table = softdog_platform_of_match,
261                 .pm = &softdog_platform_pm_ops,
262         },
263         .probe = softdog_platform_probe,
264         .remove = softdog_platform_remove,
265         .shutdown = softdog_platform_shutdown,
266 };
267
268 module_platform_driver(softdog_platform_driver);
269
270 MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
271 MODULE_DESCRIPTION("Software Watchdog Platform Device Driver");
272 MODULE_LICENSE("GPL v2");