aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/watchdog
diff options
context:
space:
mode:
authorViresh Kumar <viresh.kumar@linaro.org>2013-06-19 11:08:58 -0400
committerWim Van Sebroeck <wim@iguana.be>2013-07-11 15:47:58 -0400
commit6e63a3a294fdf91eaaac1061a9c7a5f53d16ac25 (patch)
tree585330f1e8cea1675999213b01dabf7b0e9e1394 /drivers/watchdog
parent8fce9b367d672332d2d101175b10737ee5c18b59 (diff)
watchdog: delete mpcore_wdt driver
Interrupt request doesn't use the right API: The TWD watchdog uses a per-cpu interrupt (usually interrupt #30), and the GIC configuration should flag it as such. With this setup, request_irq() should fail, and the right API is request_percpu_irq(), together with enable_percpu_irq()/disable_percpu_irq(). Nothing ensures the userspace ioctl() will end-up kicking the watchdog on the right CPU. There are no users of this driver since a long time and it makes more sense to get rid of it as nobody is looking to fix it. In case somebody wakes up after this has been removed and needs it, please revert this driver and pick these updates (These were never pushed to mainline): http://comments.gmane.org/gmane.linux.ports.arm.kernel/245998 Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Acked-by: Guenter Roeck <linux@roeck-us.net> Signed-off-by: Wim Van Sebroeck <wim@iguana.be>
Diffstat (limited to 'drivers/watchdog')
-rw-r--r--drivers/watchdog/Kconfig9
-rw-r--r--drivers/watchdog/Makefile1
-rw-r--r--drivers/watchdog/mpcore_wdt.c454
3 files changed, 0 insertions, 464 deletions
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 7460d349df59..6042e9ed542c 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -221,15 +221,6 @@ config DW_WATCHDOG
221 To compile this driver as a module, choose M here: the 221 To compile this driver as a module, choose M here: the
222 module will be called dw_wdt. 222 module will be called dw_wdt.
223 223
224config MPCORE_WATCHDOG
225 tristate "MPcore watchdog"
226 depends on HAVE_ARM_TWD
227 help
228 Watchdog timer embedded into the MPcore system.
229
230 To compile this driver as a module, choose M here: the
231 module will be called mpcore_wdt.
232
233config EP93XX_WATCHDOG 224config EP93XX_WATCHDOG
234 tristate "EP93xx Watchdog" 225 tristate "EP93xx Watchdog"
235 depends on ARCH_EP93XX 226 depends on ARCH_EP93XX
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index ec268995b261..291828b356c5 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -41,7 +41,6 @@ obj-$(CONFIG_KS8695_WATCHDOG) += ks8695_wdt.o
41obj-$(CONFIG_S3C2410_WATCHDOG) += s3c2410_wdt.o 41obj-$(CONFIG_S3C2410_WATCHDOG) += s3c2410_wdt.o
42obj-$(CONFIG_SA1100_WATCHDOG) += sa1100_wdt.o 42obj-$(CONFIG_SA1100_WATCHDOG) += sa1100_wdt.o
43obj-$(CONFIG_DW_WATCHDOG) += dw_wdt.o 43obj-$(CONFIG_DW_WATCHDOG) += dw_wdt.o
44obj-$(CONFIG_MPCORE_WATCHDOG) += mpcore_wdt.o
45obj-$(CONFIG_EP93XX_WATCHDOG) += ep93xx_wdt.o 44obj-$(CONFIG_EP93XX_WATCHDOG) += ep93xx_wdt.o
46obj-$(CONFIG_PNX4008_WATCHDOG) += pnx4008_wdt.o 45obj-$(CONFIG_PNX4008_WATCHDOG) += pnx4008_wdt.o
47obj-$(CONFIG_IOP_WATCHDOG) += iop_wdt.o 46obj-$(CONFIG_IOP_WATCHDOG) += iop_wdt.o
diff --git a/drivers/watchdog/mpcore_wdt.c b/drivers/watchdog/mpcore_wdt.c
deleted file mode 100644
index 4f5ac408c7a3..000000000000
--- a/drivers/watchdog/mpcore_wdt.c
+++ /dev/null
@@ -1,454 +0,0 @@
1/*
2 * Watchdog driver for the mpcore watchdog timer
3 *
4 * (c) Copyright 2004 ARM Limited
5 *
6 * Based on the SoftDog driver:
7 * (c) Copyright 1996 Alan Cox <alan@lxorguk.ukuu.org.uk>,
8 * All Rights Reserved.
9 *
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License
12 * as published by the Free Software Foundation; either version
13 * 2 of the License, or (at your option) any later version.
14 *
15 * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide
16 * warranty for any of this software. This material is provided
17 * "AS-IS" and at no charge.
18 *
19 * (c) Copyright 1995 Alan Cox <alan@lxorguk.ukuu.org.uk>
20 *
21 */
22
23#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
24
25#include <linux/module.h>
26#include <linux/moduleparam.h>
27#include <linux/types.h>
28#include <linux/miscdevice.h>
29#include <linux/watchdog.h>
30#include <linux/fs.h>
31#include <linux/reboot.h>
32#include <linux/init.h>
33#include <linux/interrupt.h>
34#include <linux/platform_device.h>
35#include <linux/uaccess.h>
36#include <linux/slab.h>
37#include <linux/io.h>
38
39#include <asm/smp_twd.h>
40
41struct mpcore_wdt {
42 unsigned long timer_alive;
43 struct device *dev;
44 void __iomem *base;
45 int irq;
46 unsigned int perturb;
47 char expect_close;
48};
49
50static struct platform_device *mpcore_wdt_pdev;
51static DEFINE_SPINLOCK(wdt_lock);
52
53#define TIMER_MARGIN 60
54static int mpcore_margin = TIMER_MARGIN;
55module_param(mpcore_margin, int, 0);
56MODULE_PARM_DESC(mpcore_margin,
57 "MPcore timer margin in seconds. (0 < mpcore_margin < 65536, default="
58 __MODULE_STRING(TIMER_MARGIN) ")");
59
60static bool nowayout = WATCHDOG_NOWAYOUT;
61module_param(nowayout, bool, 0);
62MODULE_PARM_DESC(nowayout,
63 "Watchdog cannot be stopped once started (default="
64 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
65
66#define ONLY_TESTING 0
67static int mpcore_noboot = ONLY_TESTING;
68module_param(mpcore_noboot, int, 0);
69MODULE_PARM_DESC(mpcore_noboot, "MPcore watchdog action, "
70 "set to 1 to ignore reboots, 0 to reboot (default="
71 __MODULE_STRING(ONLY_TESTING) ")");
72
73/*
74 * This is the interrupt handler. Note that we only use this
75 * in testing mode, so don't actually do a reboot here.
76 */
77static irqreturn_t mpcore_wdt_fire(int irq, void *arg)
78{
79 struct mpcore_wdt *wdt = arg;
80
81 /* Check it really was our interrupt */
82 if (readl(wdt->base + TWD_WDOG_INTSTAT)) {
83 dev_crit(wdt->dev, "Triggered - Reboot ignored\n");
84 /* Clear the interrupt on the watchdog */
85 writel(1, wdt->base + TWD_WDOG_INTSTAT);
86 return IRQ_HANDLED;
87 }
88 return IRQ_NONE;
89}
90
91/*
92 * mpcore_wdt_keepalive - reload the timer
93 *
94 * Note that the spec says a DIFFERENT value must be written to the reload
95 * register each time. The "perturb" variable deals with this by adding 1
96 * to the count every other time the function is called.
97 */
98static void mpcore_wdt_keepalive(struct mpcore_wdt *wdt)
99{
100 unsigned long count;
101
102 spin_lock(&wdt_lock);
103 /* Assume prescale is set to 256 */
104 count = __raw_readl(wdt->base + TWD_WDOG_COUNTER);
105 count = (0xFFFFFFFFU - count) * (HZ / 5);
106 count = (count / 256) * mpcore_margin;
107
108 /* Reload the counter */
109 writel(count + wdt->perturb, wdt->base + TWD_WDOG_LOAD);
110 wdt->perturb = wdt->perturb ? 0 : 1;
111 spin_unlock(&wdt_lock);
112}
113
114static void mpcore_wdt_stop(struct mpcore_wdt *wdt)
115{
116 spin_lock(&wdt_lock);
117 writel(0x12345678, wdt->base + TWD_WDOG_DISABLE);
118 writel(0x87654321, wdt->base + TWD_WDOG_DISABLE);
119 writel(0x0, wdt->base + TWD_WDOG_CONTROL);
120 spin_unlock(&wdt_lock);
121}
122
123static void mpcore_wdt_start(struct mpcore_wdt *wdt)
124{
125 dev_info(wdt->dev, "enabling watchdog\n");
126
127 /* This loads the count register but does NOT start the count yet */
128 mpcore_wdt_keepalive(wdt);
129
130 if (mpcore_noboot) {
131 /* Enable watchdog - prescale=256, watchdog mode=0, enable=1 */
132 writel(0x0000FF01, wdt->base + TWD_WDOG_CONTROL);
133 } else {
134 /* Enable watchdog - prescale=256, watchdog mode=1, enable=1 */
135 writel(0x0000FF09, wdt->base + TWD_WDOG_CONTROL);
136 }
137}
138
139static int mpcore_wdt_set_heartbeat(int t)
140{
141 if (t < 0x0001 || t > 0xFFFF)
142 return -EINVAL;
143
144 mpcore_margin = t;
145 return 0;
146}
147
148/*
149 * /dev/watchdog handling
150 */
151static int mpcore_wdt_open(struct inode *inode, struct file *file)
152{
153 struct mpcore_wdt *wdt = platform_get_drvdata(mpcore_wdt_pdev);
154
155 if (test_and_set_bit(0, &wdt->timer_alive))
156 return -EBUSY;
157
158 if (nowayout)
159 __module_get(THIS_MODULE);
160
161 file->private_data = wdt;
162
163 /*
164 * Activate timer
165 */
166 mpcore_wdt_start(wdt);
167
168 return nonseekable_open(inode, file);
169}
170
171static int mpcore_wdt_release(struct inode *inode, struct file *file)
172{
173 struct mpcore_wdt *wdt = file->private_data;
174
175 /*
176 * Shut off the timer.
177 * Lock it in if it's a module and we set nowayout
178 */
179 if (wdt->expect_close == 42)
180 mpcore_wdt_stop(wdt);
181 else {
182 dev_crit(wdt->dev,
183 "unexpected close, not stopping watchdog!\n");
184 mpcore_wdt_keepalive(wdt);
185 }
186 clear_bit(0, &wdt->timer_alive);
187 wdt->expect_close = 0;
188 return 0;
189}
190
191static ssize_t mpcore_wdt_write(struct file *file, const char *data,
192 size_t len, loff_t *ppos)
193{
194 struct mpcore_wdt *wdt = file->private_data;
195
196 /*
197 * Refresh the timer.
198 */
199 if (len) {
200 if (!nowayout) {
201 size_t i;
202
203 /* In case it was set long ago */
204 wdt->expect_close = 0;
205
206 for (i = 0; i != len; i++) {
207 char c;
208
209 if (get_user(c, data + i))
210 return -EFAULT;
211 if (c == 'V')
212 wdt->expect_close = 42;
213 }
214 }
215 mpcore_wdt_keepalive(wdt);
216 }
217 return len;
218}
219
220static const struct watchdog_info ident = {
221 .options = WDIOF_SETTIMEOUT |
222 WDIOF_KEEPALIVEPING |
223 WDIOF_MAGICCLOSE,
224 .identity = "MPcore Watchdog",
225};
226
227static long mpcore_wdt_ioctl(struct file *file, unsigned int cmd,
228 unsigned long arg)
229{
230 struct mpcore_wdt *wdt = file->private_data;
231 int ret;
232 union {
233 struct watchdog_info ident;
234 int i;
235 } uarg;
236
237 if (_IOC_DIR(cmd) && _IOC_SIZE(cmd) > sizeof(uarg))
238 return -ENOTTY;
239
240 if (_IOC_DIR(cmd) & _IOC_WRITE) {
241 ret = copy_from_user(&uarg, (void __user *)arg, _IOC_SIZE(cmd));
242 if (ret)
243 return -EFAULT;
244 }
245
246 switch (cmd) {
247 case WDIOC_GETSUPPORT:
248 uarg.ident = ident;
249 ret = 0;
250 break;
251
252 case WDIOC_GETSTATUS:
253 case WDIOC_GETBOOTSTATUS:
254 uarg.i = 0;
255 ret = 0;
256 break;
257
258 case WDIOC_SETOPTIONS:
259 ret = -EINVAL;
260 if (uarg.i & WDIOS_DISABLECARD) {
261 mpcore_wdt_stop(wdt);
262 ret = 0;
263 }
264 if (uarg.i & WDIOS_ENABLECARD) {
265 mpcore_wdt_start(wdt);
266 ret = 0;
267 }
268 break;
269
270 case WDIOC_KEEPALIVE:
271 mpcore_wdt_keepalive(wdt);
272 ret = 0;
273 break;
274
275 case WDIOC_SETTIMEOUT:
276 ret = mpcore_wdt_set_heartbeat(uarg.i);
277 if (ret)
278 break;
279
280 mpcore_wdt_keepalive(wdt);
281 /* Fall */
282 case WDIOC_GETTIMEOUT:
283 uarg.i = mpcore_margin;
284 ret = 0;
285 break;
286
287 default:
288 return -ENOTTY;
289 }
290
291 if (ret == 0 && _IOC_DIR(cmd) & _IOC_READ) {
292 ret = copy_to_user((void __user *)arg, &uarg, _IOC_SIZE(cmd));
293 if (ret)
294 ret = -EFAULT;
295 }
296 return ret;
297}
298
299/*
300 * System shutdown handler. Turn off the watchdog if we're
301 * restarting or halting the system.
302 */
303static void mpcore_wdt_shutdown(struct platform_device *pdev)
304{
305 struct mpcore_wdt *wdt = platform_get_drvdata(pdev);
306
307 if (system_state == SYSTEM_RESTART || system_state == SYSTEM_HALT)
308 mpcore_wdt_stop(wdt);
309}
310
311/*
312 * Kernel Interfaces
313 */
314static const struct file_operations mpcore_wdt_fops = {
315 .owner = THIS_MODULE,
316 .llseek = no_llseek,
317 .write = mpcore_wdt_write,
318 .unlocked_ioctl = mpcore_wdt_ioctl,
319 .open = mpcore_wdt_open,
320 .release = mpcore_wdt_release,
321};
322
323static struct miscdevice mpcore_wdt_miscdev = {
324 .minor = WATCHDOG_MINOR,
325 .name = "watchdog",
326 .fops = &mpcore_wdt_fops,
327};
328
329static int mpcore_wdt_probe(struct platform_device *pdev)
330{
331 struct mpcore_wdt *wdt;
332 struct resource *res;
333 int ret;
334
335 /* We only accept one device, and it must have an id of -1 */
336 if (pdev->id != -1)
337 return -ENODEV;
338
339 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
340 if (!res)
341 return -ENODEV;
342
343 wdt = devm_kzalloc(&pdev->dev, sizeof(struct mpcore_wdt), GFP_KERNEL);
344 if (!wdt)
345 return -ENOMEM;
346
347 wdt->dev = &pdev->dev;
348 wdt->irq = platform_get_irq(pdev, 0);
349 if (wdt->irq >= 0) {
350 ret = devm_request_irq(wdt->dev, wdt->irq, mpcore_wdt_fire, 0,
351 "mpcore_wdt", wdt);
352 if (ret) {
353 dev_err(wdt->dev,
354 "cannot register IRQ%d for watchdog\n",
355 wdt->irq);
356 return ret;
357 }
358 }
359
360 wdt->base = devm_ioremap(wdt->dev, res->start, resource_size(res));
361 if (!wdt->base)
362 return -ENOMEM;
363
364 mpcore_wdt_miscdev.parent = &pdev->dev;
365 ret = misc_register(&mpcore_wdt_miscdev);
366 if (ret) {
367 dev_err(wdt->dev,
368 "cannot register miscdev on minor=%d (err=%d)\n",
369 WATCHDOG_MINOR, ret);
370 return ret;
371 }
372
373 mpcore_wdt_stop(wdt);
374 platform_set_drvdata(pdev, wdt);
375 mpcore_wdt_pdev = pdev;
376
377 return 0;
378}
379
380static int mpcore_wdt_remove(struct platform_device *pdev)
381{
382 misc_deregister(&mpcore_wdt_miscdev);
383
384 mpcore_wdt_pdev = NULL;
385
386 return 0;
387}
388
389#ifdef CONFIG_PM
390static int mpcore_wdt_suspend(struct platform_device *pdev, pm_message_t msg)
391{
392 struct mpcore_wdt *wdt = platform_get_drvdata(pdev);
393 mpcore_wdt_stop(wdt); /* Turn the WDT off */
394 return 0;
395}
396
397static int mpcore_wdt_resume(struct platform_device *pdev)
398{
399 struct mpcore_wdt *wdt = platform_get_drvdata(pdev);
400 /* re-activate timer */
401 if (test_bit(0, &wdt->timer_alive))
402 mpcore_wdt_start(wdt);
403 return 0;
404}
405#else
406#define mpcore_wdt_suspend NULL
407#define mpcore_wdt_resume NULL
408#endif
409
410/* work with hotplug and coldplug */
411MODULE_ALIAS("platform:mpcore_wdt");
412
413static struct platform_driver mpcore_wdt_driver = {
414 .probe = mpcore_wdt_probe,
415 .remove = mpcore_wdt_remove,
416 .suspend = mpcore_wdt_suspend,
417 .resume = mpcore_wdt_resume,
418 .shutdown = mpcore_wdt_shutdown,
419 .driver = {
420 .owner = THIS_MODULE,
421 .name = "mpcore_wdt",
422 },
423};
424
425static int __init mpcore_wdt_init(void)
426{
427 /*
428 * Check that the margin value is within it's range;
429 * if not reset to the default
430 */
431 if (mpcore_wdt_set_heartbeat(mpcore_margin)) {
432 mpcore_wdt_set_heartbeat(TIMER_MARGIN);
433 pr_info("mpcore_margin value must be 0 < mpcore_margin < 65536, using %d\n",
434 TIMER_MARGIN);
435 }
436
437 pr_info("MPcore Watchdog Timer: 0.1. mpcore_noboot=%d mpcore_margin=%d sec (nowayout= %d)\n",
438 mpcore_noboot, mpcore_margin, nowayout);
439
440 return platform_driver_register(&mpcore_wdt_driver);
441}
442
443static void __exit mpcore_wdt_exit(void)
444{
445 platform_driver_unregister(&mpcore_wdt_driver);
446}
447
448module_init(mpcore_wdt_init);
449module_exit(mpcore_wdt_exit);
450
451MODULE_AUTHOR("ARM Limited");
452MODULE_DESCRIPTION("MPcore Watchdog Device Driver");
453MODULE_LICENSE("GPL");
454MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);