diff options
Diffstat (limited to 'drivers/char/watchdog')
-rw-r--r-- | drivers/char/watchdog/at32ap700x_wdt.c | 64 |
1 files changed, 53 insertions, 11 deletions
diff --git a/drivers/char/watchdog/at32ap700x_wdt.c b/drivers/char/watchdog/at32ap700x_wdt.c index 6e7c9588b1b9..fcd55c600b87 100644 --- a/drivers/char/watchdog/at32ap700x_wdt.c +++ b/drivers/char/watchdog/at32ap700x_wdt.c | |||
@@ -30,6 +30,11 @@ MODULE_PARM_DESC(timeout, | |||
30 | "Timeout value. Limited to be 1 or 2 seconds. (default=" | 30 | "Timeout value. Limited to be 1 or 2 seconds. (default=" |
31 | __MODULE_STRING(TIMEOUT_DEFAULT) ")"); | 31 | __MODULE_STRING(TIMEOUT_DEFAULT) ")"); |
32 | 32 | ||
33 | static int nowayout = WATCHDOG_NOWAYOUT; | ||
34 | module_param(nowayout, int, 0); | ||
35 | MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" | ||
36 | __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); | ||
37 | |||
33 | /* Watchdog registers and write/read macro */ | 38 | /* Watchdog registers and write/read macro */ |
34 | #define WDT_CTRL 0x00 | 39 | #define WDT_CTRL 0x00 |
35 | #define WDT_CTRL_EN 0 | 40 | #define WDT_CTRL_EN 0 |
@@ -54,6 +59,7 @@ struct wdt_at32ap700x { | |||
54 | }; | 59 | }; |
55 | 60 | ||
56 | static struct wdt_at32ap700x *wdt; | 61 | static struct wdt_at32ap700x *wdt; |
62 | static char expect_release; | ||
57 | 63 | ||
58 | /* | 64 | /* |
59 | * Disable the watchdog. | 65 | * Disable the watchdog. |
@@ -102,15 +108,19 @@ static int at32_wdt_open(struct inode *inode, struct file *file) | |||
102 | } | 108 | } |
103 | 109 | ||
104 | /* | 110 | /* |
105 | * Close the watchdog device. If CONFIG_WATCHDOG_NOWAYOUT is _not_ defined then | 111 | * Close the watchdog device. |
106 | * the watchdog is also disabled. | ||
107 | */ | 112 | */ |
108 | static int at32_wdt_close(struct inode *inode, struct file *file) | 113 | static int at32_wdt_close(struct inode *inode, struct file *file) |
109 | { | 114 | { |
110 | #ifndef CONFIG_WATCHDOG_NOWAYOUT | 115 | if (expect_release == 42) { |
111 | at32_wdt_stop(); | 116 | at32_wdt_stop(); |
112 | #endif | 117 | } else { |
118 | dev_dbg(wdt->miscdev.parent, | ||
119 | "Unexpected close, not stopping watchdog!\n"); | ||
120 | at32_wdt_pat(); | ||
121 | } | ||
113 | clear_bit(1, &wdt->users); | 122 | clear_bit(1, &wdt->users); |
123 | expect_release = 0; | ||
114 | return 0; | 124 | return 0; |
115 | } | 125 | } |
116 | 126 | ||
@@ -136,7 +146,9 @@ static int at32_wdt_settimeout(int time) | |||
136 | 146 | ||
137 | static struct watchdog_info at32_wdt_info = { | 147 | static struct watchdog_info at32_wdt_info = { |
138 | .identity = "at32ap700x watchdog", | 148 | .identity = "at32ap700x watchdog", |
139 | .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, | 149 | .options = WDIOF_SETTIMEOUT | |
150 | WDIOF_KEEPALIVEPING | | ||
151 | WDIOF_MAGICCLOSE, | ||
140 | }; | 152 | }; |
141 | 153 | ||
142 | /* | 154 | /* |
@@ -191,10 +203,35 @@ static int at32_wdt_ioctl(struct inode *inode, struct file *file, | |||
191 | return ret; | 203 | return ret; |
192 | } | 204 | } |
193 | 205 | ||
194 | static ssize_t at32_wdt_write(struct file *file, const char *data, size_t len, | 206 | static ssize_t at32_wdt_write(struct file *file, const char __user *data, |
195 | loff_t *ppos) | 207 | size_t len, loff_t *ppos) |
196 | { | 208 | { |
197 | at32_wdt_pat(); | 209 | /* See if we got the magic character 'V' and reload the timer */ |
210 | if (len) { | ||
211 | if (!nowayout) { | ||
212 | size_t i; | ||
213 | |||
214 | /* | ||
215 | * note: just in case someone wrote the magic | ||
216 | * character five months ago... | ||
217 | */ | ||
218 | expect_release = 0; | ||
219 | |||
220 | /* | ||
221 | * scan to see whether or not we got the magic | ||
222 | * character | ||
223 | */ | ||
224 | for (i = 0; i != len; i++) { | ||
225 | char c; | ||
226 | if (get_user(c, data+i)) | ||
227 | return -EFAULT; | ||
228 | if (c == 'V') | ||
229 | expect_release = 42; | ||
230 | } | ||
231 | } | ||
232 | /* someone wrote to us, we should pat the watchdog */ | ||
233 | at32_wdt_pat(); | ||
234 | } | ||
198 | return len; | 235 | return len; |
199 | } | 236 | } |
200 | 237 | ||
@@ -255,8 +292,9 @@ static int __init at32_wdt_probe(struct platform_device *pdev) | |||
255 | 292 | ||
256 | platform_set_drvdata(pdev, wdt); | 293 | platform_set_drvdata(pdev, wdt); |
257 | wdt->miscdev.parent = &pdev->dev; | 294 | wdt->miscdev.parent = &pdev->dev; |
258 | dev_info(&pdev->dev, "AT32AP700X WDT at 0x%p, timeout %d sec\n", | 295 | dev_info(&pdev->dev, |
259 | wdt->regs, wdt->timeout); | 296 | "AT32AP700X WDT at 0x%p, timeout %d sec (nowayout=%d)\n", |
297 | wdt->regs, wdt->timeout, nowayout); | ||
260 | 298 | ||
261 | return 0; | 299 | return 0; |
262 | 300 | ||
@@ -271,6 +309,10 @@ err_free: | |||
271 | static int __exit at32_wdt_remove(struct platform_device *pdev) | 309 | static int __exit at32_wdt_remove(struct platform_device *pdev) |
272 | { | 310 | { |
273 | if (wdt && platform_get_drvdata(pdev) == wdt) { | 311 | if (wdt && platform_get_drvdata(pdev) == wdt) { |
312 | /* Stop the timer before we leave */ | ||
313 | if (!nowayout) | ||
314 | at32_wdt_stop(); | ||
315 | |||
274 | misc_deregister(&wdt->miscdev); | 316 | misc_deregister(&wdt->miscdev); |
275 | iounmap(wdt->regs); | 317 | iounmap(wdt->regs); |
276 | kfree(wdt); | 318 | kfree(wdt); |