Blame view

drivers/watchdog/rc32434_wdt.c 8.35 KB
03ec58568   Florian Fainelli   [WATCHDOG] Add su...
1
2
3
4
5
6
7
8
9
  /*
   *  IDT Interprise 79RC32434 watchdog driver
   *
   *  Copyright (C) 2006, Ondrej Zajicek <santiago@crfreenet.org>
   *  Copyright (C) 2008, Florian Fainelli <florian@openwrt.org>
   *
   *  based on
   *  SoftDog 0.05:	A Software Watchdog Device
   *
29fa0586d   Alan Cox   [PATCH] Switch al...
10
11
   *  (c) Copyright 1996 Alan Cox <alan@lxorguk.ukuu.org.uk>,
   *					All Rights Reserved.
03ec58568   Florian Fainelli   [WATCHDOG] Add su...
12
13
14
15
16
17
18
   *
   *  This program is free software; you can redistribute it and/or
   *  modify it under the terms of the GNU General Public License
   *  as published by the Free Software Foundation; either version
   *  2 of the License, or (at your option) any later version.
   *
   */
9b655e07d   Phil Sutter   [WATCHDOG] rc3243...
19
20
21
22
23
24
25
26
27
28
29
  #include <linux/module.h>		/* For module specific items */
  #include <linux/moduleparam.h>		/* For new moduleparam's */
  #include <linux/types.h>		/* For standard types (like size_t) */
  #include <linux/errno.h>		/* For the -ENODEV/... values */
  #include <linux/kernel.h>		/* For printk/panic/... */
  #include <linux/fs.h>			/* For file operations */
  #include <linux/miscdevice.h>		/* For MODULE_ALIAS_MISCDEV
  							(WATCHDOG_MINOR) */
  #include <linux/watchdog.h>		/* For the watchdog specific items */
  #include <linux/init.h>			/* For __init/__exit/... */
  #include <linux/platform_device.h>	/* For platform_driver framework */
e455b6b4e   Wim Van Sebroeck   [WATCHDOG] rc3243...
30
  #include <linux/spinlock.h>		/* For spin_lock/spin_unlock/... */
9b655e07d   Phil Sutter   [WATCHDOG] rc3243...
31
32
33
34
35
  #include <linux/uaccess.h>		/* For copy_to_user/put_user/... */
  
  #include <asm/mach-rc32434/integ.h>	/* For the Watchdog registers */
  
  #define PFX KBUILD_MODNAME ": "
03ec58568   Florian Fainelli   [WATCHDOG] Add su...
36

f296b1435   Wim Van Sebroeck   [WATCHDOG] rc3243...
37
  #define VERSION "1.0"
03ec58568   Florian Fainelli   [WATCHDOG] Add su...
38
39
  
  static struct {
03ec58568   Florian Fainelli   [WATCHDOG] Add su...
40
  	unsigned long inuse;
e455b6b4e   Wim Van Sebroeck   [WATCHDOG] rc3243...
41
  	spinlock_t io_lock;
03ec58568   Florian Fainelli   [WATCHDOG] Add su...
42
43
44
  } rc32434_wdt_device;
  
  static struct integ __iomem *wdt_reg;
03ec58568   Florian Fainelli   [WATCHDOG] Add su...
45
46
  
  static int expect_close;
0af98d37e   Phil Sutter   [WATCHDOG] rc3243...
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
  
  /* Board internal clock speed in Hz,
   * the watchdog timer ticks at. */
  extern unsigned int idt_cpu_freq;
  
  /* translate wtcompare value to seconds and vice versa */
  #define WTCOMP2SEC(x)	(x / idt_cpu_freq)
  #define SEC2WTCOMP(x)	(x * idt_cpu_freq)
  
  /* Use a default timeout of 20s. This should be
   * safe for CPU clock speeds up to 400MHz, as
   * ((2 ^ 32) - 1) / (400MHz / 2) = 21s.  */
  #define WATCHDOG_TIMEOUT 20
  
  static int timeout = WATCHDOG_TIMEOUT;
08eb2e0c0   Phil Sutter   [WATCHDOG] rc3243...
62
63
  module_param(timeout, int, 0);
  MODULE_PARM_DESC(timeout, "Watchdog timeout value, in seconds (default="
810a90ae4   Florian Fainelli   [PATCH] rc32434_w...
64
  		__MODULE_STRING(WATCHDOG_TIMEOUT) ")");
03ec58568   Florian Fainelli   [WATCHDOG] Add su...
65
66
67
68
69
  
  static int nowayout = WATCHDOG_NOWAYOUT;
  module_param(nowayout, int, 0);
  MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
  	__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
0af98d37e   Phil Sutter   [WATCHDOG] rc3243...
70
71
72
  /* apply or and nand masks to data read from addr and write back */
  #define SET_BITS(addr, or, nand) \
  	writel((readl(&addr) | or) & ~nand, &addr)
03ec58568   Florian Fainelli   [WATCHDOG] Add su...
73

08eb2e0c0   Phil Sutter   [WATCHDOG] rc3243...
74
75
76
77
78
79
80
81
82
83
  static int rc32434_wdt_set(int new_timeout)
  {
  	int max_to = WTCOMP2SEC((u32)-1);
  
  	if (new_timeout < 0 || new_timeout > max_to) {
  		printk(KERN_ERR PFX "timeout value must be between 0 and %d",
  			max_to);
  		return -EINVAL;
  	}
  	timeout = new_timeout;
e455b6b4e   Wim Van Sebroeck   [WATCHDOG] rc3243...
84
  	spin_lock(&rc32434_wdt_device.io_lock);
08eb2e0c0   Phil Sutter   [WATCHDOG] rc3243...
85
  	writel(SEC2WTCOMP(timeout), &wdt_reg->wtcompare);
e455b6b4e   Wim Van Sebroeck   [WATCHDOG] rc3243...
86
  	spin_unlock(&rc32434_wdt_device.io_lock);
08eb2e0c0   Phil Sutter   [WATCHDOG] rc3243...
87
88
89
  
  	return 0;
  }
03ec58568   Florian Fainelli   [WATCHDOG] Add su...
90
91
  static void rc32434_wdt_start(void)
  {
0af98d37e   Phil Sutter   [WATCHDOG] rc3243...
92
  	u32 or, nand;
03ec58568   Florian Fainelli   [WATCHDOG] Add su...
93

e455b6b4e   Wim Van Sebroeck   [WATCHDOG] rc3243...
94
  	spin_lock(&rc32434_wdt_device.io_lock);
0af98d37e   Phil Sutter   [WATCHDOG] rc3243...
95
96
  	/* zero the counter before enabling */
  	writel(0, &wdt_reg->wtcount);
03ec58568   Florian Fainelli   [WATCHDOG] Add su...
97

0af98d37e   Phil Sutter   [WATCHDOG] rc3243...
98
99
100
101
  	/* don't generate a non-maskable interrupt,
  	 * do a warm reset instead */
  	nand = 1 << RC32434_ERR_WNE;
  	or = 1 << RC32434_ERR_WRE;
03ec58568   Florian Fainelli   [WATCHDOG] Add su...
102

0af98d37e   Phil Sutter   [WATCHDOG] rc3243...
103
104
  	/* reset the ERRCS timeout bit in case it's set */
  	nand |= 1 << RC32434_ERR_WTO;
03ec58568   Florian Fainelli   [WATCHDOG] Add su...
105

0af98d37e   Phil Sutter   [WATCHDOG] rc3243...
106
  	SET_BITS(wdt_reg->errcs, or, nand);
03ec58568   Florian Fainelli   [WATCHDOG] Add su...
107

08eb2e0c0   Phil Sutter   [WATCHDOG] rc3243...
108
109
  	/* set the timeout (either default or based on module param) */
  	rc32434_wdt_set(timeout);
0af98d37e   Phil Sutter   [WATCHDOG] rc3243...
110
111
112
  	/* reset WTC timeout bit and enable WDT */
  	nand = 1 << RC32434_WTC_TO;
  	or = 1 << RC32434_WTC_EN;
03ec58568   Florian Fainelli   [WATCHDOG] Add su...
113

0af98d37e   Phil Sutter   [WATCHDOG] rc3243...
114
  	SET_BITS(wdt_reg->wtc, or, nand);
9b655e07d   Phil Sutter   [WATCHDOG] rc3243...
115

e455b6b4e   Wim Van Sebroeck   [WATCHDOG] rc3243...
116
  	spin_unlock(&rc32434_wdt_device.io_lock);
9b655e07d   Phil Sutter   [WATCHDOG] rc3243...
117
118
  	printk(KERN_INFO PFX "Started watchdog timer.
  ");
0af98d37e   Phil Sutter   [WATCHDOG] rc3243...
119
  }
03ec58568   Florian Fainelli   [WATCHDOG] Add su...
120

0af98d37e   Phil Sutter   [WATCHDOG] rc3243...
121
122
  static void rc32434_wdt_stop(void)
  {
e455b6b4e   Wim Van Sebroeck   [WATCHDOG] rc3243...
123
  	spin_lock(&rc32434_wdt_device.io_lock);
0af98d37e   Phil Sutter   [WATCHDOG] rc3243...
124
125
  	/* Disable WDT */
  	SET_BITS(wdt_reg->wtc, 0, 1 << RC32434_WTC_EN);
9b655e07d   Phil Sutter   [WATCHDOG] rc3243...
126

e455b6b4e   Wim Van Sebroeck   [WATCHDOG] rc3243...
127
  	spin_unlock(&rc32434_wdt_device.io_lock);
9b655e07d   Phil Sutter   [WATCHDOG] rc3243...
128
129
  	printk(KERN_INFO PFX "Stopped watchdog timer.
  ");
03ec58568   Florian Fainelli   [WATCHDOG] Add su...
130
  }
0af98d37e   Phil Sutter   [WATCHDOG] rc3243...
131
  static void rc32434_wdt_ping(void)
03ec58568   Florian Fainelli   [WATCHDOG] Add su...
132
  {
e455b6b4e   Wim Van Sebroeck   [WATCHDOG] rc3243...
133
  	spin_lock(&rc32434_wdt_device.io_lock);
03ec58568   Florian Fainelli   [WATCHDOG] Add su...
134
  	writel(0, &wdt_reg->wtcount);
e455b6b4e   Wim Van Sebroeck   [WATCHDOG] rc3243...
135
  	spin_unlock(&rc32434_wdt_device.io_lock);
03ec58568   Florian Fainelli   [WATCHDOG] Add su...
136
137
138
139
140
141
142
143
144
  }
  
  static int rc32434_wdt_open(struct inode *inode, struct file *file)
  {
  	if (test_and_set_bit(0, &rc32434_wdt_device.inuse))
  		return -EBUSY;
  
  	if (nowayout)
  		__module_get(THIS_MODULE);
0af98d37e   Phil Sutter   [WATCHDOG] rc3243...
145
146
  	rc32434_wdt_start();
  	rc32434_wdt_ping();
03ec58568   Florian Fainelli   [WATCHDOG] Add su...
147
148
149
150
151
  	return nonseekable_open(inode, file);
  }
  
  static int rc32434_wdt_release(struct inode *inode, struct file *file)
  {
0af98d37e   Phil Sutter   [WATCHDOG] rc3243...
152
  	if (expect_close == 42) {
03ec58568   Florian Fainelli   [WATCHDOG] Add su...
153
  		rc32434_wdt_stop();
03ec58568   Florian Fainelli   [WATCHDOG] Add su...
154
  		module_put(THIS_MODULE);
0af98d37e   Phil Sutter   [WATCHDOG] rc3243...
155
  	} else {
9b655e07d   Phil Sutter   [WATCHDOG] rc3243...
156
157
158
  		printk(KERN_CRIT PFX
  			"device closed unexpectedly. WDT will not stop!
  ");
0af98d37e   Phil Sutter   [WATCHDOG] rc3243...
159
160
  		rc32434_wdt_ping();
  	}
03ec58568   Florian Fainelli   [WATCHDOG] Add su...
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
  	clear_bit(0, &rc32434_wdt_device.inuse);
  	return 0;
  }
  
  static ssize_t rc32434_wdt_write(struct file *file, const char *data,
  				size_t len, loff_t *ppos)
  {
  	if (len) {
  		if (!nowayout) {
  			size_t i;
  
  			/* In case it was set long ago */
  			expect_close = 0;
  
  			for (i = 0; i != len; i++) {
  				char c;
  				if (get_user(c, data + i))
  					return -EFAULT;
  				if (c == 'V')
0af98d37e   Phil Sutter   [WATCHDOG] rc3243...
180
  					expect_close = 42;
03ec58568   Florian Fainelli   [WATCHDOG] Add su...
181
182
  			}
  		}
0af98d37e   Phil Sutter   [WATCHDOG] rc3243...
183
  		rc32434_wdt_ping();
03ec58568   Florian Fainelli   [WATCHDOG] Add su...
184
185
186
187
  		return len;
  	}
  	return 0;
  }
7275fc8c3   Wim Van Sebroeck   [WATCHDOG] unlock...
188
189
  static long rc32434_wdt_ioctl(struct file *file, unsigned int cmd,
  				unsigned long arg)
03ec58568   Florian Fainelli   [WATCHDOG] Add su...
190
191
192
193
  {
  	void __user *argp = (void __user *)arg;
  	int new_timeout;
  	unsigned int value;
42747d712   Wim Van Sebroeck   [WATCHDOG] watchd...
194
  	static const struct watchdog_info ident = {
03ec58568   Florian Fainelli   [WATCHDOG] Add su...
195
196
197
198
199
200
  		.options =		WDIOF_SETTIMEOUT |
  					WDIOF_KEEPALIVEPING |
  					WDIOF_MAGICCLOSE,
  		.identity =		"RC32434_WDT Watchdog",
  	};
  	switch (cmd) {
9b655e07d   Phil Sutter   [WATCHDOG] rc3243...
201
202
203
  	case WDIOC_GETSUPPORT:
  		if (copy_to_user(argp, &ident, sizeof(ident)))
  			return -EFAULT;
03ec58568   Florian Fainelli   [WATCHDOG] Add su...
204
205
206
  		break;
  	case WDIOC_GETSTATUS:
  	case WDIOC_GETBOOTSTATUS:
0af98d37e   Phil Sutter   [WATCHDOG] rc3243...
207
  		value = 0;
03ec58568   Florian Fainelli   [WATCHDOG] Add su...
208
209
210
  		if (copy_to_user(argp, &value, sizeof(int)))
  			return -EFAULT;
  		break;
03ec58568   Florian Fainelli   [WATCHDOG] Add su...
211
212
213
214
215
216
217
218
219
  	case WDIOC_SETOPTIONS:
  		if (copy_from_user(&value, argp, sizeof(int)))
  			return -EFAULT;
  		switch (value) {
  		case WDIOS_ENABLECARD:
  			rc32434_wdt_start();
  			break;
  		case WDIOS_DISABLECARD:
  			rc32434_wdt_stop();
0af98d37e   Phil Sutter   [WATCHDOG] rc3243...
220
  			break;
03ec58568   Florian Fainelli   [WATCHDOG] Add su...
221
222
223
224
  		default:
  			return -EINVAL;
  		}
  		break;
9b655e07d   Phil Sutter   [WATCHDOG] rc3243...
225
226
227
  	case WDIOC_KEEPALIVE:
  		rc32434_wdt_ping();
  		break;
03ec58568   Florian Fainelli   [WATCHDOG] Add su...
228
229
230
  	case WDIOC_SETTIMEOUT:
  		if (copy_from_user(&new_timeout, argp, sizeof(int)))
  			return -EFAULT;
0af98d37e   Phil Sutter   [WATCHDOG] rc3243...
231
  		if (rc32434_wdt_set(new_timeout))
03ec58568   Florian Fainelli   [WATCHDOG] Add su...
232
  			return -EINVAL;
0af98d37e   Phil Sutter   [WATCHDOG] rc3243...
233
  		/* Fall through */
03ec58568   Florian Fainelli   [WATCHDOG] Add su...
234
235
236
237
238
239
240
241
  	case WDIOC_GETTIMEOUT:
  		return copy_to_user(argp, &timeout, sizeof(int));
  	default:
  		return -ENOTTY;
  	}
  
  	return 0;
  }
d5c26a597   Wim Van Sebroeck   [WATCHDOG] struct...
242
  static const struct file_operations rc32434_wdt_fops = {
03ec58568   Florian Fainelli   [WATCHDOG] Add su...
243
244
245
  	.owner		= THIS_MODULE,
  	.llseek		= no_llseek,
  	.write		= rc32434_wdt_write,
7275fc8c3   Wim Van Sebroeck   [WATCHDOG] unlock...
246
  	.unlocked_ioctl	= rc32434_wdt_ioctl,
03ec58568   Florian Fainelli   [WATCHDOG] Add su...
247
248
249
250
251
252
253
254
255
  	.open		= rc32434_wdt_open,
  	.release	= rc32434_wdt_release,
  };
  
  static struct miscdevice rc32434_wdt_miscdev = {
  	.minor	= WATCHDOG_MINOR,
  	.name	= "watchdog",
  	.fops	= &rc32434_wdt_fops,
  };
9b655e07d   Phil Sutter   [WATCHDOG] rc3243...
256
257
258
  static char banner[] __devinitdata = KERN_INFO PFX
  		"Watchdog Timer version " VERSION ", timer margin: %d sec
  ";
03ec58568   Florian Fainelli   [WATCHDOG] Add su...
259

d9a8798c4   Phil Sutter   [WATCHDOG] rc3243...
260
  static int __devinit rc32434_wdt_probe(struct platform_device *pdev)
03ec58568   Florian Fainelli   [WATCHDOG] Add su...
261
262
263
  {
  	int ret;
  	struct resource *r;
0af98d37e   Phil Sutter   [WATCHDOG] rc3243...
264
  	r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rb532_wdt_res");
03ec58568   Florian Fainelli   [WATCHDOG] Add su...
265
  	if (!r) {
9b655e07d   Phil Sutter   [WATCHDOG] rc3243...
266
267
  		printk(KERN_ERR PFX "failed to retrieve resources
  ");
03ec58568   Florian Fainelli   [WATCHDOG] Add su...
268
269
  		return -ENODEV;
  	}
be088b139   H Hartley Sweeten   [WATCHDOG] rc3243...
270
  	wdt_reg = ioremap_nocache(r->start, resource_size(r));
03ec58568   Florian Fainelli   [WATCHDOG] Add su...
271
  	if (!wdt_reg) {
9b655e07d   Phil Sutter   [WATCHDOG] rc3243...
272
273
  		printk(KERN_ERR PFX "failed to remap I/O resources
  ");
03ec58568   Florian Fainelli   [WATCHDOG] Add su...
274
275
  		return -ENXIO;
  	}
e455b6b4e   Wim Van Sebroeck   [WATCHDOG] rc3243...
276
  	spin_lock_init(&rc32434_wdt_device.io_lock);
f296b1435   Wim Van Sebroeck   [WATCHDOG] rc3243...
277
278
  	/* Make sure the watchdog is not running */
  	rc32434_wdt_stop();
08eb2e0c0   Phil Sutter   [WATCHDOG] rc3243...
279
280
281
282
283
284
285
286
287
  	/* Check that the heartbeat value is within it's range;
  	 * if not reset to the default */
  	if (rc32434_wdt_set(timeout)) {
  		rc32434_wdt_set(WATCHDOG_TIMEOUT);
  		printk(KERN_INFO PFX
  			"timeout value must be between 0 and %d
  ",
  			WTCOMP2SEC((u32)-1));
  	}
03ec58568   Florian Fainelli   [WATCHDOG] Add su...
288
  	ret = misc_register(&rc32434_wdt_miscdev);
03ec58568   Florian Fainelli   [WATCHDOG] Add su...
289
  	if (ret < 0) {
9b655e07d   Phil Sutter   [WATCHDOG] rc3243...
290
291
  		printk(KERN_ERR PFX "failed to register watchdog device
  ");
03ec58568   Florian Fainelli   [WATCHDOG] Add su...
292
293
  		goto unmap;
  	}
03ec58568   Florian Fainelli   [WATCHDOG] Add su...
294
295
296
297
298
299
300
301
  	printk(banner, timeout);
  
  	return 0;
  
  unmap:
  	iounmap(wdt_reg);
  	return ret;
  }
d9a8798c4   Phil Sutter   [WATCHDOG] rc3243...
302
  static int __devexit rc32434_wdt_remove(struct platform_device *pdev)
03ec58568   Florian Fainelli   [WATCHDOG] Add su...
303
  {
03ec58568   Florian Fainelli   [WATCHDOG] Add su...
304
  	misc_deregister(&rc32434_wdt_miscdev);
03ec58568   Florian Fainelli   [WATCHDOG] Add su...
305
  	iounmap(wdt_reg);
03ec58568   Florian Fainelli   [WATCHDOG] Add su...
306
307
  	return 0;
  }
0aaae6617   Wim Van Sebroeck   [WATCHDOG] rc3243...
308
309
310
311
  static void rc32434_wdt_shutdown(struct platform_device *pdev)
  {
  	rc32434_wdt_stop();
  }
9b655e07d   Phil Sutter   [WATCHDOG] rc3243...
312
  static struct platform_driver rc32434_wdt_driver = {
0aaae6617   Wim Van Sebroeck   [WATCHDOG] rc3243...
313
314
315
316
317
  	.probe		= rc32434_wdt_probe,
  	.remove		= __devexit_p(rc32434_wdt_remove),
  	.shutdown	= rc32434_wdt_shutdown,
  	.driver		= {
  			.name = "rc32434_wdt",
03ec58568   Florian Fainelli   [WATCHDOG] Add su...
318
319
  	}
  };
b8ec61189   Axel Lin   watchdog: convert...
320
  module_platform_driver(rc32434_wdt_driver);
03ec58568   Florian Fainelli   [WATCHDOG] Add su...
321
322
323
324
325
326
  
  MODULE_AUTHOR("Ondrej Zajicek <santiago@crfreenet.org>,"
  		"Florian Fainelli <florian@openwrt.org>");
  MODULE_DESCRIPTION("Driver for the IDT RC32434 SoC watchdog");
  MODULE_LICENSE("GPL");
  MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);