aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/watchdog/Kconfig10
-rw-r--r--drivers/watchdog/Makefile1
-rw-r--r--drivers/watchdog/bcm63xx_wdt.c350
3 files changed, 361 insertions, 0 deletions
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 57b8a410697b..4a291045ebac 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -917,6 +917,16 @@ config OCTEON_WDT
917 from the first interrupt, it is then only poked when the 917 from the first interrupt, it is then only poked when the
918 device is written. 918 device is written.
919 919
920config BCM63XX_WDT
921 tristate "Broadcom BCM63xx hardware watchdog"
922 depends on BCM63XX
923 help
924 Watchdog driver for the built in watchdog hardware in Broadcom
925 BCM63xx SoC.
926
927 To compile this driver as a loadable module, choose M here.
928 The module will be called bcm63xx_wdt.
929
920# PARISC Architecture 930# PARISC Architecture
921 931
922# POWERPC Architecture 932# POWERPC Architecture
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 8374503fcc6a..4b0ef386229d 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -109,6 +109,7 @@ obj-$(CONFIG_SBC_EPX_C3_WATCHDOG) += sbc_epx_c3.o
109 109
110# MIPS Architecture 110# MIPS Architecture
111obj-$(CONFIG_BCM47XX_WDT) += bcm47xx_wdt.o 111obj-$(CONFIG_BCM47XX_WDT) += bcm47xx_wdt.o
112obj-$(CONFIG_BCM63XX_WDT) += bcm63xx_wdt.o
112obj-$(CONFIG_RC32434_WDT) += rc32434_wdt.o 113obj-$(CONFIG_RC32434_WDT) += rc32434_wdt.o
113obj-$(CONFIG_INDYDOG) += indydog.o 114obj-$(CONFIG_INDYDOG) += indydog.o
114obj-$(CONFIG_WDT_MTX1) += mtx-1_wdt.o 115obj-$(CONFIG_WDT_MTX1) += mtx-1_wdt.o
diff --git a/drivers/watchdog/bcm63xx_wdt.c b/drivers/watchdog/bcm63xx_wdt.c
new file mode 100644
index 000000000000..a1debc89356b
--- /dev/null
+++ b/drivers/watchdog/bcm63xx_wdt.c
@@ -0,0 +1,350 @@
1/*
2 * Broadcom BCM63xx SoC watchdog driver
3 *
4 * Copyright (C) 2007, Miguel Gaio <miguel.gaio@efixo.com>
5 * Copyright (C) 2008, Florian Fainelli <florian@openwrt.org>
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version
10 * 2 of the License, or (at your option) any later version.
11 */
12
13#include <linux/bitops.h>
14#include <linux/errno.h>
15#include <linux/fs.h>
16#include <linux/init.h>
17#include <linux/kernel.h>
18#include <linux/miscdevice.h>
19#include <linux/module.h>
20#include <linux/moduleparam.h>
21#include <linux/reboot.h>
22#include <linux/types.h>
23#include <linux/uaccess.h>
24#include <linux/watchdog.h>
25#include <linux/timer.h>
26#include <linux/jiffies.h>
27#include <linux/interrupt.h>
28#include <linux/ptrace.h>
29#include <linux/resource.h>
30#include <linux/platform_device.h>
31
32#include <bcm63xx_cpu.h>
33#include <bcm63xx_io.h>
34#include <bcm63xx_regs.h>
35#include <bcm63xx_timer.h>
36
37#define PFX KBUILD_MODNAME
38
39#define WDT_HZ 50000000 /* Fclk */
40#define WDT_DEFAULT_TIME 30 /* seconds */
41#define WDT_MAX_TIME 256 /* seconds */
42
43static struct {
44 void __iomem *regs;
45 struct timer_list timer;
46 int default_ticks;
47 unsigned long inuse;
48 atomic_t ticks;
49} bcm63xx_wdt_device;
50
51static int expect_close;
52
53static int wdt_time = WDT_DEFAULT_TIME;
54static int nowayout = WATCHDOG_NOWAYOUT;
55module_param(nowayout, int, 0);
56MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
57 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
58
59/* HW functions */
60static void bcm63xx_wdt_hw_start(void)
61{
62 bcm_writel(0xfffffffe, bcm63xx_wdt_device.regs + WDT_DEFVAL_REG);
63 bcm_writel(WDT_START_1, bcm63xx_wdt_device.regs + WDT_CTL_REG);
64 bcm_writel(WDT_START_2, bcm63xx_wdt_device.regs + WDT_CTL_REG);
65}
66
67static void bcm63xx_wdt_hw_stop(void)
68{
69 bcm_writel(WDT_STOP_1, bcm63xx_wdt_device.regs + WDT_CTL_REG);
70 bcm_writel(WDT_STOP_2, bcm63xx_wdt_device.regs + WDT_CTL_REG);
71}
72
73static void bcm63xx_wdt_isr(void *data)
74{
75 struct pt_regs *regs = get_irq_regs();
76
77 die(PFX " fire", regs);
78}
79
80static void bcm63xx_timer_tick(unsigned long unused)
81{
82 if (!atomic_dec_and_test(&bcm63xx_wdt_device.ticks)) {
83 bcm63xx_wdt_hw_start();
84 mod_timer(&bcm63xx_wdt_device.timer, jiffies + HZ);
85 } else
86 printk(KERN_CRIT PFX ": watchdog will restart system\n");
87}
88
89static void bcm63xx_wdt_pet(void)
90{
91 atomic_set(&bcm63xx_wdt_device.ticks, wdt_time);
92}
93
94static void bcm63xx_wdt_start(void)
95{
96 bcm63xx_wdt_pet();
97 bcm63xx_timer_tick(0);
98}
99
100static void bcm63xx_wdt_pause(void)
101{
102 del_timer_sync(&bcm63xx_wdt_device.timer);
103 bcm63xx_wdt_hw_stop();
104}
105
106static int bcm63xx_wdt_settimeout(int new_time)
107{
108 if ((new_time <= 0) || (new_time > WDT_MAX_TIME))
109 return -EINVAL;
110
111 wdt_time = new_time;
112
113 return 0;
114}
115
116static int bcm63xx_wdt_open(struct inode *inode, struct file *file)
117{
118 if (test_and_set_bit(0, &bcm63xx_wdt_device.inuse))
119 return -EBUSY;
120
121 bcm63xx_wdt_start();
122 return nonseekable_open(inode, file);
123}
124
125static int bcm63xx_wdt_release(struct inode *inode, struct file *file)
126{
127 if (expect_close == 42)
128 bcm63xx_wdt_pause();
129 else {
130 printk(KERN_CRIT PFX
131 ": Unexpected close, not stopping watchdog!\n");
132 bcm63xx_wdt_start();
133 }
134 clear_bit(0, &bcm63xx_wdt_device.inuse);
135 expect_close = 0;
136 return 0;
137}
138
139static ssize_t bcm63xx_wdt_write(struct file *file, const char *data,
140 size_t len, loff_t *ppos)
141{
142 if (len) {
143 if (!nowayout) {
144 size_t i;
145
146 /* In case it was set long ago */
147 expect_close = 0;
148
149 for (i = 0; i != len; i++) {
150 char c;
151 if (get_user(c, data + i))
152 return -EFAULT;
153 if (c == 'V')
154 expect_close = 42;
155 }
156 }
157 bcm63xx_wdt_pet();
158 }
159 return len;
160}
161
162static struct watchdog_info bcm63xx_wdt_info = {
163 .identity = PFX,
164 .options = WDIOF_SETTIMEOUT |
165 WDIOF_KEEPALIVEPING |
166 WDIOF_MAGICCLOSE,
167};
168
169
170static long bcm63xx_wdt_ioctl(struct file *file, unsigned int cmd,
171 unsigned long arg)
172{
173 void __user *argp = (void __user *)arg;
174 int __user *p = argp;
175 int new_value, retval = -EINVAL;
176
177 switch (cmd) {
178 case WDIOC_GETSUPPORT:
179 return copy_to_user(argp, &bcm63xx_wdt_info,
180 sizeof(bcm63xx_wdt_info)) ? -EFAULT : 0;
181
182 case WDIOC_GETSTATUS:
183 case WDIOC_GETBOOTSTATUS:
184 return put_user(0, p);
185
186 case WDIOC_SETOPTIONS:
187 if (get_user(new_value, p))
188 return -EFAULT;
189
190 if (new_value & WDIOS_DISABLECARD) {
191 bcm63xx_wdt_pause();
192 retval = 0;
193 }
194 if (new_value & WDIOS_ENABLECARD) {
195 bcm63xx_wdt_start();
196 retval = 0;
197 }
198
199 return retval;
200
201 case WDIOC_KEEPALIVE:
202 bcm63xx_wdt_pet();
203 return 0;
204
205 case WDIOC_SETTIMEOUT:
206 if (get_user(new_value, p))
207 return -EFAULT;
208
209 if (bcm63xx_wdt_settimeout(new_value))
210 return -EINVAL;
211
212 bcm63xx_wdt_pet();
213
214 case WDIOC_GETTIMEOUT:
215 return put_user(wdt_time, p);
216
217 default:
218 return -ENOTTY;
219
220 }
221}
222
223static int bcm63xx_wdt_notify_sys(struct notifier_block *this,
224 unsigned long code, void *unused)
225{
226 if (code == SYS_DOWN || code == SYS_HALT)
227 bcm63xx_wdt_pause();
228 return NOTIFY_DONE;
229}
230
231static const struct file_operations bcm63xx_wdt_fops = {
232 .owner = THIS_MODULE,
233 .llseek = no_llseek,
234 .write = bcm63xx_wdt_write,
235 .unlocked_ioctl = bcm63xx_wdt_ioctl,
236 .open = bcm63xx_wdt_open,
237 .release = bcm63xx_wdt_release,
238};
239
240static struct miscdevice bcm63xx_wdt_miscdev = {
241 .minor = WATCHDOG_MINOR,
242 .name = "watchdog",
243 .fops = &bcm63xx_wdt_fops,
244};
245
246static struct notifier_block bcm63xx_wdt_notifier = {
247 .notifier_call = bcm63xx_wdt_notify_sys,
248};
249
250
251static int bcm63xx_wdt_probe(struct platform_device *pdev)
252{
253 int ret;
254 struct resource *r;
255
256 setup_timer(&bcm63xx_wdt_device.timer, bcm63xx_timer_tick, 0L);
257
258 r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
259 if (!r) {
260 dev_err(&pdev->dev, "failed to get resources\n");
261 return -ENODEV;
262 }
263
264 bcm63xx_wdt_device.regs = ioremap_nocache(r->start, r->end - r->start);
265 if (!bcm63xx_wdt_device.regs) {
266 dev_err(&pdev->dev, "failed to remap I/O resources\n");
267 return -ENXIO;
268 }
269
270 ret = bcm63xx_timer_register(TIMER_WDT_ID, bcm63xx_wdt_isr, NULL);
271 if (ret < 0) {
272 dev_err(&pdev->dev, "failed to register wdt timer isr\n");
273 goto unmap;
274 }
275
276 if (bcm63xx_wdt_settimeout(wdt_time)) {
277 bcm63xx_wdt_settimeout(WDT_DEFAULT_TIME);
278 dev_info(&pdev->dev,
279 ": wdt_time value must be 1 <= wdt_time <= 256, using %d\n",
280 wdt_time);
281 }
282
283 ret = register_reboot_notifier(&bcm63xx_wdt_notifier);
284 if (ret) {
285 dev_err(&pdev->dev, "failed to register reboot_notifier\n");
286 goto unregister_timer;
287 }
288
289 ret = misc_register(&bcm63xx_wdt_miscdev);
290 if (ret < 0) {
291 dev_err(&pdev->dev, "failed to register watchdog device\n");
292 goto unregister_reboot_notifier;
293 }
294
295 dev_info(&pdev->dev, " started, timer margin: %d sec\n",
296 WDT_DEFAULT_TIME);
297
298 return 0;
299
300unregister_reboot_notifier:
301 unregister_reboot_notifier(&bcm63xx_wdt_notifier);
302unregister_timer:
303 bcm63xx_timer_unregister(TIMER_WDT_ID);
304unmap:
305 iounmap(bcm63xx_wdt_device.regs);
306 return ret;
307}
308
309static int bcm63xx_wdt_remove(struct platform_device *pdev)
310{
311 if (!nowayout)
312 bcm63xx_wdt_pause();
313
314 misc_deregister(&bcm63xx_wdt_miscdev);
315
316 iounmap(bcm63xx_wdt_device.regs);
317
318 unregister_reboot_notifier(&bcm63xx_wdt_notifier);
319 bcm63xx_timer_unregister(TIMER_WDT_ID);
320
321 return 0;
322}
323
324static struct platform_driver bcm63xx_wdt = {
325 .probe = bcm63xx_wdt_probe,
326 .remove = bcm63xx_wdt_remove,
327 .driver = {
328 .name = "bcm63xx-wdt",
329 }
330};
331
332static int __init bcm63xx_wdt_init(void)
333{
334 return platform_driver_register(&bcm63xx_wdt);
335}
336
337static void __exit bcm63xx_wdt_exit(void)
338{
339 platform_driver_unregister(&bcm63xx_wdt);
340}
341
342module_init(bcm63xx_wdt_init);
343module_exit(bcm63xx_wdt_exit);
344
345MODULE_AUTHOR("Miguel Gaio <miguel.gaio@efixo.com>");
346MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>");
347MODULE_DESCRIPTION("Driver for the Broadcom BCM63xx SoC watchdog");
348MODULE_LICENSE("GPL");
349MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
350MODULE_ALIAS("platform:bcm63xx-wdt");