aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/watchdog/Kconfig9
-rw-r--r--drivers/watchdog/Makefile1
-rw-r--r--drivers/watchdog/stmp3xxx_wdt.c296
3 files changed, 306 insertions, 0 deletions
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index c4c2206d6f3e..70d7f8cc06b7 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -257,6 +257,15 @@ config TWL4030_WATCHDOG
257 Support for TI TWL4030 watchdog. Say 'Y' here to enable the 257 Support for TI TWL4030 watchdog. Say 'Y' here to enable the
258 watchdog timer support for TWL4030 chips. 258 watchdog timer support for TWL4030 chips.
259 259
260config STMP3XXX_WATCHDOG
261 tristate "Freescale STMP3XXX watchdog"
262 depends on ARCH_STMP3XXX
263 help
264 Say Y here if to include support for the watchdog timer
265 for the Sigmatel STMP37XX/378X SoC.
266 To compile this driver as a module, choose M here: the
267 module will be called stmp3xxx_wdt.
268
260# AVR32 Architecture 269# AVR32 Architecture
261 270
262config AT32AP700X_WDT 271config AT32AP700X_WDT
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 2c54c04858b2..d437d5c2d22e 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -43,6 +43,7 @@ obj-$(CONFIG_IOP_WATCHDOG) += iop_wdt.o
43obj-$(CONFIG_DAVINCI_WATCHDOG) += davinci_wdt.o 43obj-$(CONFIG_DAVINCI_WATCHDOG) += davinci_wdt.o
44obj-$(CONFIG_ORION_WATCHDOG) += orion_wdt.o 44obj-$(CONFIG_ORION_WATCHDOG) += orion_wdt.o
45obj-$(CONFIG_COH901327_WATCHDOG) += coh901327_wdt.o 45obj-$(CONFIG_COH901327_WATCHDOG) += coh901327_wdt.o
46obj-$(CONFIG_STMP3XXX_WATCHDOG) += stmp3xxx_wdt.o
46 47
47# AVR32 Architecture 48# AVR32 Architecture
48obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o 49obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o
diff --git a/drivers/watchdog/stmp3xxx_wdt.c b/drivers/watchdog/stmp3xxx_wdt.c
new file mode 100644
index 000000000000..5dd952681f32
--- /dev/null
+++ b/drivers/watchdog/stmp3xxx_wdt.c
@@ -0,0 +1,296 @@
1/*
2 * Watchdog driver for Freescale STMP37XX/STMP378X
3 *
4 * Author: Vitaly Wool <vital@embeddedalley.com>
5 *
6 * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.
7 * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
8 */
9#include <linux/init.h>
10#include <linux/kernel.h>
11#include <linux/fs.h>
12#include <linux/miscdevice.h>
13#include <linux/watchdog.h>
14#include <linux/platform_device.h>
15#include <linux/spinlock.h>
16#include <linux/uaccess.h>
17
18#include <mach/platform.h>
19#include <mach/regs-rtc.h>
20
21#define DEFAULT_HEARTBEAT 19
22#define MAX_HEARTBEAT (0x10000000 >> 6)
23
24/* missing bitmask in headers */
25#define BV_RTC_PERSISTENT1_GENERAL__RTC_FORCE_UPDATER 0x80000000
26
27#define WDT_IN_USE 0
28#define WDT_OK_TO_CLOSE 1
29
30#define WDOG_COUNTER_RATE 1000 /* 1 kHz clock */
31
32static DEFINE_SPINLOCK(stmp3xxx_wdt_io_lock);
33static unsigned long wdt_status;
34static const int nowayout = WATCHDOG_NOWAYOUT;
35static int heartbeat = DEFAULT_HEARTBEAT;
36static unsigned long boot_status;
37
38static void wdt_enable(u32 value)
39{
40 spin_lock(&stmp3xxx_wdt_io_lock);
41 __raw_writel(value, REGS_RTC_BASE + HW_RTC_WATCHDOG);
42 stmp3xxx_setl(BM_RTC_CTRL_WATCHDOGEN, REGS_RTC_BASE + HW_RTC_CTRL);
43 stmp3xxx_setl(BV_RTC_PERSISTENT1_GENERAL__RTC_FORCE_UPDATER,
44 REGS_RTC_BASE + HW_RTC_PERSISTENT1);
45 spin_unlock(&stmp3xxx_wdt_io_lock);
46}
47
48static void wdt_disable(void)
49{
50 spin_lock(&stmp3xxx_wdt_io_lock);
51 stmp3xxx_clearl(BV_RTC_PERSISTENT1_GENERAL__RTC_FORCE_UPDATER,
52 REGS_RTC_BASE + HW_RTC_PERSISTENT1);
53 stmp3xxx_clearl(BM_RTC_CTRL_WATCHDOGEN, REGS_RTC_BASE + HW_RTC_CTRL);
54 spin_unlock(&stmp3xxx_wdt_io_lock);
55}
56
57static void wdt_ping(void)
58{
59 wdt_enable(heartbeat * WDOG_COUNTER_RATE);
60}
61
62static int stmp3xxx_wdt_open(struct inode *inode, struct file *file)
63{
64 if (test_and_set_bit(WDT_IN_USE, &wdt_status))
65 return -EBUSY;
66
67 clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
68 wdt_ping();
69
70 return nonseekable_open(inode, file);
71}
72
73static ssize_t stmp3xxx_wdt_write(struct file *file, const char __user *data,
74 size_t len, loff_t *ppos)
75{
76 if (len) {
77 if (!nowayout) {
78 size_t i;
79
80 clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
81
82 for (i = 0; i != len; i++) {
83 char c;
84
85 if (get_user(c, data + i))
86 return -EFAULT;
87 if (c == 'V')
88 set_bit(WDT_OK_TO_CLOSE, &wdt_status);
89 }
90 }
91 wdt_ping();
92 }
93
94 return len;
95}
96
97static struct watchdog_info ident = {
98 .options = WDIOF_CARDRESET |
99 WDIOF_MAGICCLOSE |
100 WDIOF_SETTIMEOUT |
101 WDIOF_KEEPALIVEPING,
102 .identity = "STMP3XXX Watchdog",
103};
104
105static long stmp3xxx_wdt_ioctl(struct file *file, unsigned int cmd,
106 unsigned long arg)
107{
108 void __user *argp = (void __user *)arg;
109 int __user *p = argp;
110 int new_heartbeat, opts;
111 int ret = -ENOTTY;
112
113 switch (cmd) {
114 case WDIOC_GETSUPPORT:
115 ret = copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
116 break;
117
118 case WDIOC_GETSTATUS:
119 ret = put_user(0, p);
120 break;
121
122 case WDIOC_GETBOOTSTATUS:
123 ret = put_user(boot_status, p);
124 break;
125
126 case WDIOC_SETOPTIONS:
127 if (get_user(opts, p)) {
128 ret = -EFAULT;
129 break;
130 }
131 if (opts & WDIOS_DISABLECARD)
132 wdt_disable();
133 else if (opts & WDIOS_ENABLECARD)
134 wdt_ping();
135 else {
136 pr_debug("%s: unknown option 0x%x\n", __func__, opts);
137 ret = -EINVAL;
138 break;
139 }
140 ret = 0;
141 break;
142
143 case WDIOC_KEEPALIVE:
144 wdt_ping();
145 ret = 0;
146 break;
147
148 case WDIOC_SETTIMEOUT:
149 if (get_user(new_heartbeat, p)) {
150 ret = -EFAULT;
151 break;
152 }
153 if (new_heartbeat <= 0 || new_heartbeat > MAX_HEARTBEAT) {
154 ret = -EINVAL;
155 break;
156 }
157
158 heartbeat = new_heartbeat;
159 wdt_ping();
160 /* Fall through */
161
162 case WDIOC_GETTIMEOUT:
163 ret = put_user(heartbeat, p);
164 break;
165 }
166 return ret;
167}
168
169static int stmp3xxx_wdt_release(struct inode *inode, struct file *file)
170{
171 int ret = 0;
172
173 if (!nowayout) {
174 if (!test_bit(WDT_OK_TO_CLOSE, &wdt_status)) {
175 wdt_ping();
176 pr_debug("%s: Device closed unexpectdly\n", __func__);
177 ret = -EINVAL;
178 } else {
179 wdt_disable();
180 clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
181 }
182 }
183 clear_bit(WDT_IN_USE, &wdt_status);
184
185 return ret;
186}
187
188static const struct file_operations stmp3xxx_wdt_fops = {
189 .owner = THIS_MODULE,
190 .llseek = no_llseek,
191 .write = stmp3xxx_wdt_write,
192 .unlocked_ioctl = stmp3xxx_wdt_ioctl,
193 .open = stmp3xxx_wdt_open,
194 .release = stmp3xxx_wdt_release,
195};
196
197static struct miscdevice stmp3xxx_wdt_miscdev = {
198 .minor = WATCHDOG_MINOR,
199 .name = "watchdog",
200 .fops = &stmp3xxx_wdt_fops,
201};
202
203static int __devinit stmp3xxx_wdt_probe(struct platform_device *pdev)
204{
205 int ret = 0;
206
207 if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT)
208 heartbeat = DEFAULT_HEARTBEAT;
209
210 boot_status = __raw_readl(REGS_RTC_BASE + HW_RTC_PERSISTENT1) &
211 BV_RTC_PERSISTENT1_GENERAL__RTC_FORCE_UPDATER;
212 boot_status = !!boot_status;
213 stmp3xxx_clearl(BV_RTC_PERSISTENT1_GENERAL__RTC_FORCE_UPDATER,
214 REGS_RTC_BASE + HW_RTC_PERSISTENT1);
215 wdt_disable(); /* disable for now */
216
217 ret = misc_register(&stmp3xxx_wdt_miscdev);
218 if (ret < 0) {
219 dev_err(&pdev->dev, "cannot register misc device\n");
220 return ret;
221 }
222
223 printk(KERN_INFO "stmp3xxx watchdog: initialized, heartbeat %d sec\n",
224 heartbeat);
225
226 return ret;
227}
228
229static int __devexit stmp3xxx_wdt_remove(struct platform_device *pdev)
230{
231 misc_deregister(&stmp3xxx_wdt_miscdev);
232 return 0;
233}
234
235#ifdef CONFIG_PM
236static int wdt_suspended;
237static u32 wdt_saved_time;
238
239static int stmp3xxx_wdt_suspend(struct platform_device *pdev,
240 pm_message_t state)
241{
242 if (__raw_readl(REGS_RTC_BASE + HW_RTC_CTRL) &
243 BM_RTC_CTRL_WATCHDOGEN) {
244 wdt_suspended = 1;
245 wdt_saved_time = __raw_readl(REGS_RTC_BASE + HW_RTC_WATCHDOG);
246 wdt_disable();
247 }
248 return 0;
249}
250
251static int stmp3xxx_wdt_resume(struct platform_device *pdev)
252{
253 if (wdt_suspended) {
254 wdt_enable(wdt_saved_time);
255 wdt_suspended = 0;
256 }
257 return 0;
258}
259#else
260#define stmp3xxx_wdt_suspend NULL
261#define stmp3xxx_wdt_resume NULL
262#endif
263
264static struct platform_driver platform_wdt_driver = {
265 .driver = {
266 .name = "stmp3xxx_wdt",
267 },
268 .probe = stmp3xxx_wdt_probe,
269 .remove = __devexit_p(stmp3xxx_wdt_remove),
270 .suspend = stmp3xxx_wdt_suspend,
271 .resume = stmp3xxx_wdt_resume,
272};
273
274static int __init stmp3xxx_wdt_init(void)
275{
276 return platform_driver_register(&platform_wdt_driver);
277}
278
279static void __exit stmp3xxx_wdt_exit(void)
280{
281 return platform_driver_unregister(&platform_wdt_driver);
282}
283
284module_init(stmp3xxx_wdt_init);
285module_exit(stmp3xxx_wdt_exit);
286
287MODULE_DESCRIPTION("STMP3XXX Watchdog Driver");
288MODULE_LICENSE("GPL");
289
290module_param(heartbeat, int, 0);
291MODULE_PARM_DESC(heartbeat,
292 "Watchdog heartbeat period in seconds from 1 to "
293 __MODULE_STRING(MAX_HEARTBEAT) ", default "
294 __MODULE_STRING(DEFAULT_HEARTBEAT));
295
296MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);