aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/watchdog/Kconfig5
-rw-r--r--drivers/watchdog/Makefile1
-rw-r--r--drivers/watchdog/max63xx_wdt.c397
3 files changed, 403 insertions, 0 deletions
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 1e48d4c45f86..713b1cef37d3 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -55,6 +55,11 @@ config SOFT_WATCHDOG
55 To compile this driver as a module, choose M here: the 55 To compile this driver as a module, choose M here: the
56 module will be called softdog. 56 module will be called softdog.
57 57
58config MAX63XX_WATCHDOG
59 tristate "Max63xx watchdog"
60 help
61 Support for memory mapped max63{69,70,71,72,73,74} watchdog timer.
62
58config WM831X_WATCHDOG 63config WM831X_WATCHDOG
59 tristate "WM831x watchdog" 64 tristate "WM831x watchdog"
60 depends on MFD_WM831X 65 depends on MFD_WM831X
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 2fd15947deb7..5e3cb95bb0e9 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -143,4 +143,5 @@ obj-$(CONFIG_WATCHDOG_CP1XXX) += cpwd.o
143# Architecture Independant 143# Architecture Independant
144obj-$(CONFIG_WM831X_WATCHDOG) += wm831x_wdt.o 144obj-$(CONFIG_WM831X_WATCHDOG) += wm831x_wdt.o
145obj-$(CONFIG_WM8350_WATCHDOG) += wm8350_wdt.o 145obj-$(CONFIG_WM8350_WATCHDOG) += wm8350_wdt.o
146obj-$(CONFIG_MAX63XX_WATCHDOG) += max63xx_wdt.o
146obj-$(CONFIG_SOFT_WATCHDOG) += softdog.o 147obj-$(CONFIG_SOFT_WATCHDOG) += softdog.o
diff --git a/drivers/watchdog/max63xx_wdt.c b/drivers/watchdog/max63xx_wdt.c
new file mode 100644
index 000000000000..6eb91d757604
--- /dev/null
+++ b/drivers/watchdog/max63xx_wdt.c
@@ -0,0 +1,397 @@
1/*
2 * drivers/char/watchdog/max63xx_wdt.c
3 *
4 * Driver for max63{69,70,71,72,73,74} watchdog timers
5 *
6 * Copyright (C) 2009 Marc Zyngier <maz@misterjones.org>
7 *
8 * This file is licensed under the terms of the GNU General Public
9 * License version 2. This program is licensed "as is" without any
10 * warranty of any kind, whether express or implied.
11 *
12 * This driver assumes the watchdog pins are memory mapped (as it is
13 * the case for the Arcom Zeus). Should it be connected over GPIOs or
14 * another interface, some abstraction will have to be introduced.
15 */
16
17#include <linux/module.h>
18#include <linux/moduleparam.h>
19#include <linux/types.h>
20#include <linux/kernel.h>
21#include <linux/fs.h>
22#include <linux/miscdevice.h>
23#include <linux/watchdog.h>
24#include <linux/init.h>
25#include <linux/bitops.h>
26#include <linux/platform_device.h>
27#include <linux/spinlock.h>
28#include <linux/uaccess.h>
29#include <linux/io.h>
30#include <linux/device.h>
31
32#define DEFAULT_HEARTBEAT 60
33#define MAX_HEARTBEAT 60
34
35static int heartbeat = DEFAULT_HEARTBEAT;
36static int nowayout = WATCHDOG_NOWAYOUT;
37
38/*
39 * Memory mapping: a single byte, 3 first lower bits to select bit 3
40 * to ping the watchdog.
41 */
42#define MAX6369_WDSET (7 << 0)
43#define MAX6369_WDI (1 << 3)
44
45static DEFINE_SPINLOCK(io_lock);
46
47static unsigned long wdt_status;
48#define WDT_IN_USE 0
49#define WDT_RUNNING 1
50#define WDT_OK_TO_CLOSE 2
51
52static int nodelay;
53static struct resource *wdt_mem;
54static void __iomem *wdt_base;
55static struct platform_device *max63xx_pdev;
56
57/*
58 * The timeout values used are actually the absolute minimum the chip
59 * offers. Typical values on my board are slightly over twice as long
60 * (10s setting ends up with a 25s timeout), and can be up to 3 times
61 * the nominal setting (according to the datasheet). So please take
62 * these values with a grain of salt. Same goes for the initial delay
63 * "feature". Only max6373/74 have a few settings without this initial
64 * delay (selected with the "nodelay" parameter).
65 *
66 * I also decided to remove from the tables any timeout smaller than a
67 * second, as it looked completly overkill...
68 */
69
70/* Timeouts in second */
71struct max63xx_timeout {
72 u8 wdset;
73 u8 tdelay;
74 u8 twd;
75};
76
77static struct max63xx_timeout max6369_table[] = {
78 { 5, 1, 1 },
79 { 6, 10, 10 },
80 { 7, 60, 60 },
81 { },
82};
83
84static struct max63xx_timeout max6371_table[] = {
85 { 6, 60, 3 },
86 { 7, 60, 60 },
87 { },
88};
89
90static struct max63xx_timeout max6373_table[] = {
91 { 2, 60, 1 },
92 { 5, 0, 1 },
93 { 1, 3, 3 },
94 { 7, 60, 10 },
95 { 6, 0, 10 },
96 { },
97};
98
99static struct max63xx_timeout *current_timeout;
100
101static struct max63xx_timeout *
102max63xx_select_timeout(struct max63xx_timeout *table, int value)
103{
104 while (table->twd) {
105 if (value <= table->twd) {
106 if (nodelay && table->tdelay == 0)
107 return table;
108
109 if (!nodelay)
110 return table;
111 }
112
113 table++;
114 }
115
116 return NULL;
117}
118
119static void max63xx_wdt_ping(void)
120{
121 u8 val;
122
123 spin_lock(&io_lock);
124
125 val = __raw_readb(wdt_base);
126
127 __raw_writeb(val | MAX6369_WDI, wdt_base);
128 __raw_writeb(val & ~MAX6369_WDI, wdt_base);
129
130 spin_unlock(&io_lock);
131}
132
133static void max63xx_wdt_enable(struct max63xx_timeout *entry)
134{
135 u8 val;
136
137 if (test_and_set_bit(WDT_RUNNING, &wdt_status))
138 return;
139
140 spin_lock(&io_lock);
141
142 val = __raw_readb(wdt_base);
143 val &= ~MAX6369_WDSET;
144 val |= entry->wdset;
145 __raw_writeb(val, wdt_base);
146
147 spin_unlock(&io_lock);
148
149 /* check for a edge triggered startup */
150 if (entry->tdelay == 0)
151 max63xx_wdt_ping();
152}
153
154static void max63xx_wdt_disable(void)
155{
156 spin_lock(&io_lock);
157
158 __raw_writeb(3, wdt_base);
159
160 spin_unlock(&io_lock);
161
162 clear_bit(WDT_RUNNING, &wdt_status);
163}
164
165static int max63xx_wdt_open(struct inode *inode, struct file *file)
166{
167 if (test_and_set_bit(WDT_IN_USE, &wdt_status))
168 return -EBUSY;
169
170 max63xx_wdt_enable(current_timeout);
171 clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
172
173 return nonseekable_open(inode, file);
174}
175
176static ssize_t max63xx_wdt_write(struct file *file, const char *data,