Blame view

drivers/watchdog/ep93xx_wdt.c 5.51 KB
f52ac8fec   Alessandro Zummo   [PATCH] cirrus ep...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
  /*
   * Watchdog driver for Cirrus Logic EP93xx family of devices.
   *
   * Copyright (c) 2004 Ray Lehtiniemi
   * Copyright (c) 2006 Tower Technologies
   * Based on ep93xx driver, bits from alim7101_wdt.c
   *
   * Authors: Ray Lehtiniemi <rayl@mail.com>,
   *	Alessandro Zummo <a.zummo@towertech.it>
   *
   * This file is licensed under the terms of the GNU General Public
   * License version 2. This program is licensed "as is" without any
   * warranty of any kind, whether express or implied.
   *
   * This watchdog fires after 250msec, which is a too short interval
   * for us to rely on the user space daemon alone. So we ping the
   * wdt each ~200msec and eventually stop doing it if the user space
   * daemon dies.
   *
   * TODO:
   *
   *	- Test last reset from watchdog status
   *	- Add a few missing ioctls
   */
  
  #include <linux/module.h>
  #include <linux/fs.h>
  #include <linux/miscdevice.h>
  #include <linux/watchdog.h>
  #include <linux/timer.h>
f339e2ac9   Alan Cox   [WATCHDOG 09/57] ...
31
  #include <linux/uaccess.h>
2653d1d7f   Ryan Mallon   [ARM] 5606/1: Fix...
32
  #include <linux/io.h>
a09e64fbc   Russell King   [ARM] Move includ...
33
  #include <mach/hardware.h>
f52ac8fec   Alessandro Zummo   [PATCH] cirrus ep...
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
  
  #define WDT_VERSION	"0.3"
  #define PFX		"ep93xx_wdt: "
  
  /* default timeout (secs) */
  #define WDT_TIMEOUT 30
  
  static int nowayout = WATCHDOG_NOWAYOUT;
  static int timeout = WDT_TIMEOUT;
  
  static struct timer_list timer;
  static unsigned long next_heartbeat;
  static unsigned long wdt_status;
  static unsigned long boot_status;
  
  #define WDT_IN_USE		0
  #define WDT_OK_TO_CLOSE		1
  
  #define EP93XX_WDT_REG(x)	(EP93XX_WATCHDOG_BASE + (x))
  #define EP93XX_WDT_WATCHDOG	EP93XX_WDT_REG(0x00)
  #define EP93XX_WDT_WDSTATUS	EP93XX_WDT_REG(0x04)
  
  /* reset the wdt every ~200ms */
  #define WDT_INTERVAL (HZ/5)
  
  static void wdt_enable(void)
  {
  	__raw_writew(0xaaaa, EP93XX_WDT_WATCHDOG);
  }
  
  static void wdt_disable(void)
  {
  	__raw_writew(0xaa55, EP93XX_WDT_WATCHDOG);
  }
  
  static inline void wdt_ping(void)
  {
  	__raw_writew(0x5555, EP93XX_WDT_WATCHDOG);
  }
  
  static void wdt_startup(void)
  {
  	next_heartbeat = jiffies + (timeout * HZ);
  
  	wdt_enable();
  	mod_timer(&timer, jiffies + WDT_INTERVAL);
  }
  
  static void wdt_shutdown(void)
  {
  	del_timer_sync(&timer);
  	wdt_disable();
  }
  
  static void wdt_keepalive(void)
  {
  	/* user land ping */
  	next_heartbeat = jiffies + (timeout * HZ);
  }
  
  static int ep93xx_wdt_open(struct inode *inode, struct file *file)
  {
  	if (test_and_set_bit(WDT_IN_USE, &wdt_status))
  		return -EBUSY;
  
  	clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
  
  	wdt_startup();
  
  	return nonseekable_open(inode, file);
  }
  
  static ssize_t
  ep93xx_wdt_write(struct file *file, const char __user *data, size_t len,
  		 loff_t *ppos)
  {
f52ac8fec   Alessandro Zummo   [PATCH] cirrus ep...
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
  	if (len) {
  		if (!nowayout) {
  			size_t i;
  
  			clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
  
  			for (i = 0; i != len; i++) {
  				char c;
  
  				if (get_user(c, data + i))
  					return -EFAULT;
  
  				if (c == 'V')
  					set_bit(WDT_OK_TO_CLOSE, &wdt_status);
  				else
  					clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
  			}
  		}
  		wdt_keepalive();
  	}
  
  	return len;
  }
42747d712   Wim Van Sebroeck   [WATCHDOG] watchd...
133
  static const struct watchdog_info ident = {
f52ac8fec   Alessandro Zummo   [PATCH] cirrus ep...
134
135
136
  	.options = WDIOF_CARDRESET | WDIOF_MAGICCLOSE,
  	.identity = "EP93xx Watchdog",
  };
f339e2ac9   Alan Cox   [WATCHDOG 09/57] ...
137
138
  static long ep93xx_wdt_ioctl(struct file *file,
  					unsigned int cmd, unsigned long arg)
f52ac8fec   Alessandro Zummo   [PATCH] cirrus ep...
139
  {
795b89d20   Samuel Tardieu   [WATCHDOG] use EN...
140
  	int ret = -ENOTTY;
f52ac8fec   Alessandro Zummo   [PATCH] cirrus ep...
141
142
143
144
145
146
147
148
149
150
151
152
153
154
  
  	switch (cmd) {
  	case WDIOC_GETSUPPORT:
  		ret = copy_to_user((struct watchdog_info __user *)arg, &ident,
  				sizeof(ident)) ? -EFAULT : 0;
  		break;
  
  	case WDIOC_GETSTATUS:
  		ret = put_user(0, (int __user *)arg);
  		break;
  
  	case WDIOC_GETBOOTSTATUS:
  		ret = put_user(boot_status, (int __user *)arg);
  		break;
f52ac8fec   Alessandro Zummo   [PATCH] cirrus ep...
155
156
157
158
  	case WDIOC_KEEPALIVE:
  		wdt_keepalive();
  		ret = 0;
  		break;
0c06090c9   Wim Van Sebroeck   [WATCHDOG] Coding...
159
160
161
162
163
  
  	case WDIOC_GETTIMEOUT:
  		/* actually, it is 0.250 seconds.... */
  		ret = put_user(1, (int __user *)arg);
  		break;
f52ac8fec   Alessandro Zummo   [PATCH] cirrus ep...
164
165
166
167
168
169
170
171
172
  	}
  	return ret;
  }
  
  static int ep93xx_wdt_release(struct inode *inode, struct file *file)
  {
  	if (test_bit(WDT_OK_TO_CLOSE, &wdt_status))
  		wdt_shutdown();
  	else
f339e2ac9   Alan Cox   [WATCHDOG 09/57] ...
173
174
175
  		printk(KERN_CRIT PFX
  			"Device closed unexpectedly - timer will not stop
  ");
f52ac8fec   Alessandro Zummo   [PATCH] cirrus ep...
176
177
178
179
180
181
  
  	clear_bit(WDT_IN_USE, &wdt_status);
  	clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
  
  	return 0;
  }
62322d255   Arjan van de Ven   [PATCH] make more...
182
  static const struct file_operations ep93xx_wdt_fops = {
f52ac8fec   Alessandro Zummo   [PATCH] cirrus ep...
183
184
  	.owner		= THIS_MODULE,
  	.write		= ep93xx_wdt_write,
f339e2ac9   Alan Cox   [WATCHDOG 09/57] ...
185
  	.unlocked_ioctl	= ep93xx_wdt_ioctl,
f52ac8fec   Alessandro Zummo   [PATCH] cirrus ep...
186
187
  	.open		= ep93xx_wdt_open,
  	.release	= ep93xx_wdt_release,
6038f373a   Arnd Bergmann   llseek: automatic...
188
  	.llseek		= no_llseek,
f52ac8fec   Alessandro Zummo   [PATCH] cirrus ep...
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
  };
  
  static struct miscdevice ep93xx_wdt_miscdev = {
  	.minor		= WATCHDOG_MINOR,
  	.name		= "watchdog",
  	.fops		= &ep93xx_wdt_fops,
  };
  
  static void ep93xx_timer_ping(unsigned long data)
  {
  	if (time_before(jiffies, next_heartbeat))
  		wdt_ping();
  
  	/* Re-set the timer interval */
  	mod_timer(&timer, jiffies + WDT_INTERVAL);
  }
  
  static int __init ep93xx_wdt_init(void)
  {
  	int err;
  
  	err = misc_register(&ep93xx_wdt_miscdev);
  
  	boot_status = __raw_readl(EP93XX_WDT_WATCHDOG) & 0x01 ? 1 : 0;
  
  	printk(KERN_INFO PFX "EP93XX watchdog, driver version "
  		WDT_VERSION "%s
  ",
  		(__raw_readl(EP93XX_WDT_WATCHDOG) & 0x08)
  		? " (nCS1 disable detected)" : "");
  
  	if (timeout < 1 || timeout > 3600) {
  		timeout = WDT_TIMEOUT;
  		printk(KERN_INFO PFX
  			"timeout value must be 1<=x<=3600, using %d
  ",
  			timeout);
  	}
  
  	setup_timer(&timer, ep93xx_timer_ping, 1);
  	return err;
  }
  
  static void __exit ep93xx_wdt_exit(void)
  {
  	wdt_shutdown();
  	misc_deregister(&ep93xx_wdt_miscdev);
  }
  
  module_init(ep93xx_wdt_init);
  module_exit(ep93xx_wdt_exit);
  
  module_param(nowayout, int, 0);
  MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started");
  
  module_param(timeout, int, 0);
f339e2ac9   Alan Cox   [WATCHDOG 09/57] ...
245
246
  MODULE_PARM_DESC(timeout,
  	"Watchdog timeout in seconds. (1<=timeout<=3600, default="
d557f651b   Wim Van Sebroeck   watchdog: ep93xx_...
247
  				__MODULE_STRING(WDT_TIMEOUT) ")");
f52ac8fec   Alessandro Zummo   [PATCH] cirrus ep...
248
249
250
251
252
253
254
  
  MODULE_AUTHOR("Ray Lehtiniemi <rayl@mail.com>,"
  		"Alessandro Zummo <a.zummo@towertech.it>");
  MODULE_DESCRIPTION("EP93xx Watchdog");
  MODULE_LICENSE("GPL");
  MODULE_VERSION(WDT_VERSION);
  MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);