aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/watchdog/Kconfig1
-rw-r--r--drivers/watchdog/shwdt.c198
2 files changed, 45 insertions, 154 deletions
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 37096246c937..67957393393f 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -1138,6 +1138,7 @@ config ZVM_WATCHDOG
1138config SH_WDT 1138config SH_WDT
1139 tristate "SuperH Watchdog" 1139 tristate "SuperH Watchdog"
1140 depends on SUPERH && (CPU_SH3 || CPU_SH4) 1140 depends on SUPERH && (CPU_SH3 || CPU_SH4)
1141 select WATCHDOG_CORE
1141 help 1142 help
1142 This driver adds watchdog support for the integrated watchdog in the 1143 This driver adds watchdog support for the integrated watchdog in the
1143 SuperH processors. If you have one of these processors and wish 1144 SuperH processors. If you have one of these processors and wish
diff --git a/drivers/watchdog/shwdt.c b/drivers/watchdog/shwdt.c
index 74a261f36702..0beabf238d4f 100644
--- a/drivers/watchdog/shwdt.c
+++ b/drivers/watchdog/shwdt.c
@@ -27,12 +27,10 @@
27#include <linux/types.h> 27#include <linux/types.h>
28#include <linux/miscdevice.h> 28#include <linux/miscdevice.h>
29#include <linux/watchdog.h> 29#include <linux/watchdog.h>
30#include <linux/ioport.h>
31#include <linux/fs.h> 30#include <linux/fs.h>
32#include <linux/mm.h> 31#include <linux/mm.h>
33#include <linux/slab.h> 32#include <linux/slab.h>
34#include <linux/io.h> 33#include <linux/io.h>
35#include <linux/uaccess.h>
36#include <asm/watchdog.h> 34#include <asm/watchdog.h>
37 35
38#define DRV_NAME "sh-wdt" 36#define DRV_NAME "sh-wdt"
@@ -67,8 +65,6 @@
67static int clock_division_ratio = WTCSR_CKS_4096; 65static int clock_division_ratio = WTCSR_CKS_4096;
68#define next_ping_period(cks) (jiffies + msecs_to_jiffies(cks - 4)) 66#define next_ping_period(cks) (jiffies + msecs_to_jiffies(cks - 4))
69 67
70static const struct watchdog_info sh_wdt_info;
71static struct platform_device *sh_wdt_dev;
72static DEFINE_SPINLOCK(shwdt_lock); 68static DEFINE_SPINLOCK(shwdt_lock);
73 69
74#define WATCHDOG_HEARTBEAT 30 /* 30 sec default heartbeat */ 70#define WATCHDOG_HEARTBEAT 30 /* 30 sec default heartbeat */
@@ -86,8 +82,9 @@ struct sh_wdt {
86 char expect_close; 82 char expect_close;
87}; 83};
88 84
89static void sh_wdt_start(struct sh_wdt *wdt) 85static int sh_wdt_start(struct watchdog_device *wdt_dev)
90{ 86{
87 struct sh_wdt *wdt = watchdog_get_drvdata(wdt_dev);
91 unsigned long flags; 88 unsigned long flags;
92 u8 csr; 89 u8 csr;
93 90
@@ -121,10 +118,13 @@ static void sh_wdt_start(struct sh_wdt *wdt)
121 sh_wdt_write_rstcsr(csr); 118 sh_wdt_write_rstcsr(csr);
122#endif 119#endif
123 spin_unlock_irqrestore(&shwdt_lock, flags); 120 spin_unlock_irqrestore(&shwdt_lock, flags);
121
122 return 0;
124} 123}
125 124
126static void sh_wdt_stop(struct sh_wdt *wdt) 125static int sh_wdt_stop(struct watchdog_device *wdt_dev)
127{ 126{
127 struct sh_wdt *wdt = watchdog_get_drvdata(wdt_dev);
128 unsigned long flags; 128 unsigned long flags;
129 u8 csr; 129 u8 csr;
130 130
@@ -137,18 +137,22 @@ static void sh_wdt_stop(struct sh_wdt *wdt)
137 sh_wdt_write_csr(csr); 137 sh_wdt_write_csr(csr);
138 138
139 spin_unlock_irqrestore(&shwdt_lock, flags); 139 spin_unlock_irqrestore(&shwdt_lock, flags);
140
141 return 0;
140} 142}
141 143
142static inline void sh_wdt_keepalive(struct sh_wdt *wdt) 144static int sh_wdt_keepalive(struct watchdog_device *wdt_dev)
143{ 145{
144 unsigned long flags; 146 unsigned long flags;
145 147
146 spin_lock_irqsave(&shwdt_lock, flags); 148 spin_lock_irqsave(&shwdt_lock, flags);
147 next_heartbeat = jiffies + (heartbeat * HZ); 149 next_heartbeat = jiffies + (heartbeat * HZ);
148 spin_unlock_irqrestore(&shwdt_lock, flags); 150 spin_unlock_irqrestore(&shwdt_lock, flags);
151
152 return 0;
149} 153}
150 154
151static int sh_wdt_set_heartbeat(int t) 155static int sh_wdt_set_heartbeat(struct watchdog_device *wdt_dev, unsigned t)
152{ 156{
153 unsigned long flags; 157 unsigned long flags;
154 158
@@ -157,7 +161,9 @@ static int sh_wdt_set_heartbeat(int t)
157 161
158 spin_lock_irqsave(&shwdt_lock, flags); 162 spin_lock_irqsave(&shwdt_lock, flags);
159 heartbeat = t; 163 heartbeat = t;
164 wdt_dev->timeout = t;
160 spin_unlock_irqrestore(&shwdt_lock, flags); 165 spin_unlock_irqrestore(&shwdt_lock, flags);
166
161 return 0; 167 return 0;
162} 168}
163 169
@@ -183,123 +189,6 @@ static void sh_wdt_ping(unsigned long data)
183 spin_unlock_irqrestore(&shwdt_lock, flags); 189 spin_unlock_irqrestore(&shwdt_lock, flags);
184} 190}
185 191
186static int sh_wdt_open(struct inode *inode, struct file *file)
187{
188 struct sh_wdt *wdt = platform_get_drvdata(sh_wdt_dev);
189
190 if (test_and_set_bit(0, &wdt->enabled))
191 return -EBUSY;
192 if (nowayout)
193 __module_get(THIS_MODULE);
194
195 file->private_data = wdt;
196
197 sh_wdt_start(wdt);
198
199 return nonseekable_open(inode, file);
200}
201
202static int sh_wdt_close(struct inode *inode, struct file *file)
203{
204 struct sh_wdt *wdt = file->private_data;
205
206 if (wdt->expect_close == 42) {
207 sh_wdt_stop(wdt);
208 } else {
209 dev_crit(wdt->dev, "Unexpected close, not "
210 "stopping watchdog!\n");
211 sh_wdt_keepalive(wdt);
212 }
213
214 clear_bit(0, &wdt->enabled);
215 wdt->expect_close = 0;
216
217 return 0;
218}
219
220static ssize_t sh_wdt_write(struct file *file, const char *buf,
221 size_t count, loff_t *ppos)
222{
223 struct sh_wdt *wdt = file->private_data;
224
225 if (count) {
226 if (!nowayout) {
227 size_t i;
228
229 wdt->expect_close = 0;
230
231 for (i = 0; i != count; i++) {
232 char c;
233 if (get_user(c, buf + i))
234 return -EFAULT;
235 if (c == 'V')
236 wdt->expect_close = 42;
237 }
238 }
239 sh_wdt_keepalive(wdt);
240 }
241
242 return count;
243}
244
245static long sh_wdt_ioctl(struct file *file, unsigned int cmd,
246 unsigned long arg)
247{
248 struct sh_wdt *wdt = file->private_data;
249 int new_heartbeat;
250 int options, retval = -EINVAL;
251
252 switch (cmd) {
253 case WDIOC_GETSUPPORT:
254 return copy_to_user((struct watchdog_info *)arg,
255 &sh_wdt_info, sizeof(sh_wdt_info)) ? -EFAULT : 0;
256 case WDIOC_GETSTATUS:
257 case WDIOC_GETBOOTSTATUS:
258 return put_user(0, (int *)arg);
259 case WDIOC_SETOPTIONS:
260 if (get_user(options, (int *)arg))
261 return -EFAULT;
262
263 if (options & WDIOS_DISABLECARD) {
264 sh_wdt_stop(wdt);
265 retval = 0;
266 }
267
268 if (options & WDIOS_ENABLECARD) {
269 sh_wdt_start(wdt);
270 retval = 0;
271 }
272
273 return retval;
274 case WDIOC_KEEPALIVE:
275 sh_wdt_keepalive(wdt);
276 return 0;
277 case WDIOC_SETTIMEOUT:
278 if (get_user(new_heartbeat, (int *)arg))
279 return -EFAULT;
280
281 if (sh_wdt_set_heartbeat(new_heartbeat))
282 return -EINVAL;
283
284 sh_wdt_keepalive(wdt);
285 /* Fall */
286 case WDIOC_GETTIMEOUT:
287 return put_user(heartbeat, (int *)arg);
288 default:
289 return -ENOTTY;
290 }
291 return 0;
292}
293
294static const struct file_operations sh_wdt_fops = {
295 .owner = THIS_MODULE,
296 .llseek = no_llseek,
297 .write = sh_wdt_write,
298 .unlocked_ioctl = sh_wdt_ioctl,
299 .open = sh_wdt_open,
300 .release = sh_wdt_close,
301};
302
303static const struct watchdog_info sh_wdt_info = { 192static const struct watchdog_info sh_wdt_info = {
304 .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | 193 .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT |
305 WDIOF_MAGICCLOSE, 194 WDIOF_MAGICCLOSE,
@@ -307,10 +196,17 @@ static const struct watchdog_info sh_wdt_info = {
307 .identity = "SH WDT", 196 .identity = "SH WDT",
308}; 197};
309 198
310static struct miscdevice sh_wdt_miscdev = { 199static const struct watchdog_ops sh_wdt_ops = {
311 .minor = WATCHDOG_MINOR, 200 .owner = THIS_MODULE,
312 .name = "watchdog", 201 .start = sh_wdt_start,
313 .fops = &sh_wdt_fops, 202 .stop = sh_wdt_stop,
203 .ping = sh_wdt_keepalive,
204 .set_timeout = sh_wdt_set_heartbeat,
205};
206
207static struct watchdog_device sh_wdt_dev = {
208 .info = &sh_wdt_info,
209 .ops = &sh_wdt_ops,
314}; 210};
315 211
316static int __devinit sh_wdt_probe(struct platform_device *pdev) 212static int __devinit sh_wdt_probe(struct platform_device *pdev)
@@ -348,13 +244,25 @@ static int __devinit sh_wdt_probe(struct platform_device *pdev)
348 goto out_err; 244 goto out_err;
349 } 245 }
350 246
351 sh_wdt_miscdev.parent = wdt->dev; 247 rc = sh_wdt_set_heartbeat(&sh_wdt_dev, heartbeat);
248 if (unlikely(rc)) {
249 /* Default timeout if invalid */
250 sh_wdt_set_heartbeat(&sh_wdt_dev, WATCHDOG_HEARTBEAT);
251
252 dev_warn(&pdev->dev,
253 "heartbeat value must be 1<=x<=3600, using %d\n",
254 sh_wdt_dev.timeout);
255 }
256
257 dev_info(&pdev->dev, "configured with heartbeat=%d sec (nowayout=%d)\n",
258 sh_wdt_dev.timeout, nowayout);
352 259
353 rc = misc_register(&sh_wdt_miscdev); 260 watchdog_set_nowayout(&sh_wdt_dev, nowayout);
261 watchdog_set_drvdata(&sh_wdt_dev, wdt);
262
263 rc = watchdog_register_device(&sh_wdt_dev);
354 if (unlikely(rc)) { 264 if (unlikely(rc)) {
355 dev_err(&pdev->dev, 265 dev_err(&pdev->dev, "Can't register watchdog (err=%d)\n", rc);
356 "Can't register miscdev on minor=%d (err=%d)\n",
357 sh_wdt_miscdev.minor, rc);
358 goto out_unmap; 266 goto out_unmap;
359 } 267 }
360 268
@@ -364,7 +272,6 @@ static int __devinit sh_wdt_probe(struct platform_device *pdev)
364 wdt->timer.expires = next_ping_period(clock_division_ratio); 272 wdt->timer.expires = next_ping_period(clock_division_ratio);
365 273
366 platform_set_drvdata(pdev, wdt); 274 platform_set_drvdata(pdev, wdt);
367 sh_wdt_dev = pdev;
368 275
369 dev_info(&pdev->dev, "initialized.\n"); 276 dev_info(&pdev->dev, "initialized.\n");
370 277
@@ -387,9 +294,7 @@ static int __devexit sh_wdt_remove(struct platform_device *pdev)
387 294
388 platform_set_drvdata(pdev, NULL); 295 platform_set_drvdata(pdev, NULL);
389 296
390 misc_deregister(&sh_wdt_miscdev); 297 watchdog_unregister_device(&sh_wdt_dev);
391
392 sh_wdt_dev = NULL;
393 298
394 devm_release_mem_region(&pdev->dev, res->start, resource_size(res)); 299 devm_release_mem_region(&pdev->dev, res->start, resource_size(res));
395 devm_iounmap(&pdev->dev, wdt->base); 300 devm_iounmap(&pdev->dev, wdt->base);
@@ -400,9 +305,7 @@ static int __devexit sh_wdt_remove(struct platform_device *pdev)
400 305
401static void sh_wdt_shutdown(struct platform_device *pdev) 306static void sh_wdt_shutdown(struct platform_device *pdev)
402{ 307{
403 struct sh_wdt *wdt = platform_get_drvdata(pdev); 308 sh_wdt_stop(&sh_wdt_dev);
404
405 sh_wdt_stop(wdt);
406} 309}
407 310
408static struct platform_driver sh_wdt_driver = { 311static struct platform_driver sh_wdt_driver = {
@@ -418,8 +321,6 @@ static struct platform_driver sh_wdt_driver = {
418 321
419static int __init sh_wdt_init(void) 322static int __init sh_wdt_init(void)
420{ 323{
421 int rc;
422
423 if (unlikely(clock_division_ratio < 0x5 || 324 if (unlikely(clock_division_ratio < 0x5 ||
424 clock_division_ratio > 0x7)) { 325 clock_division_ratio > 0x7)) {
425 clock_division_ratio = WTCSR_CKS_4096; 326 clock_division_ratio = WTCSR_CKS_4096;
@@ -428,17 +329,6 @@ static int __init sh_wdt_init(void)
428 clock_division_ratio); 329 clock_division_ratio);
429 } 330 }
430 331
431 rc = sh_wdt_set_heartbeat(heartbeat);
432 if (unlikely(rc)) {
433 heartbeat = WATCHDOG_HEARTBEAT;
434
435 pr_info("heartbeat value must be 1<=x<=3600, using %d\n",
436 heartbeat);
437 }
438
439 pr_info("configured with heartbeat=%d sec (nowayout=%d)\n",
440 heartbeat, nowayout);
441
442 return platform_driver_register(&sh_wdt_driver); 332 return platform_driver_register(&sh_wdt_driver);
443} 333}
444 334