summaryrefslogtreecommitdiff
path: root/target/linux/storm/patches/009-watchdog.patch
blob: 3055a94822a71d570f3605d5ac53a49286b33f30 (plain)
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
31
32
33
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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
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
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
--- a/arch/arm/mach-sl2312/sl3516_device.c
+++ b/arch/arm/mach-sl2312/sl3516_device.c
@@ -76,9 +76,30 @@
 	.resource       = sl3516_sata0_resources,
 };
 
+static struct resource sl351x_wdt_resources[] = {
+	[0] = {
+		.start  = SL2312_WAQTCHDOG_BASE + 0x00,
+		.end    = SL2312_WAQTCHDOG_BASE + 0x1C,
+		.flags  = IORESOURCE_MEM,
+	},
+	[1] = {
+		.start  = IRQ_WATCHDOG,
+		.end    = IRQ_WATCHDOG,
+		.flags  = IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device sl351x_wdt = {
+	.name		= "sl351x-wdt",
+	.id		= -1,
+	.resource	= sl351x_wdt_resources,
+	.num_resources	= ARRAY_SIZE(sl351x_wdt_resources),
+};
+
 static struct platform_device *sata_devices[] __initdata = {
 	&sata_device,
 	&sata0_device,
+	&sl351x_wdt,
 };
 
 static int __init sl3516_init(void)
--- a/drivers/char/watchdog/Kconfig
+++ b/drivers/char/watchdog/Kconfig
@@ -171,6 +171,17 @@
 	  To compile this driver as a module, choose M here: the
 	  module will be called ep93xx_wdt.
 
+config WATCHDOG_SL351X
+	tristate "SL351x Watchdog"
+	depends on WATCHDOG && ARCH_SL2312
+	help
+	  This driver adds watchdog support for the integrated watchdog in the
+	  SL351x processors (Farraday core). If you have one of these processors
+	  and wish to have watchdog support enabled, say Y, otherwise say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called sl351x_wdt.
+
 config OMAP_WATCHDOG
 	tristate "OMAP Watchdog"
 	depends on ARCH_OMAP16XX || ARCH_OMAP24XX
--- a/drivers/char/watchdog/Makefile
+++ b/drivers/char/watchdog/Makefile
@@ -36,6 +36,7 @@
 obj-$(CONFIG_SA1100_WATCHDOG) += sa1100_wdt.o
 obj-$(CONFIG_MPCORE_WATCHDOG) += mpcore_wdt.o
 obj-$(CONFIG_EP93XX_WATCHDOG) += ep93xx_wdt.o
+obj-$(CONFIG_WATCHDOG_SL351X) += sl351x_wdt.o
 obj-$(CONFIG_PNX4008_WATCHDOG) += pnx4008_wdt.o
 obj-$(CONFIG_IOP_WATCHDOG) += iop_wdt.o
 obj-$(CONFIG_DAVINCI_WATCHDOG) += davinci_wdt.o
--- /dev/null
+++ b/drivers/char/watchdog/sl351x_wdt.c
@@ -0,0 +1,332 @@
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/platform_device.h>
+#include <asm/uaccess.h>
+#include <asm/arch/sl2312.h>
+#include <asm/arch/hardware.h>
+#include <asm/arch/irqs.h>
+#include <asm/arch/watchdog.h>
+#include <asm/io.h>
+#include <linux/interrupt.h>
+
+#define WATCHDOG_TEST 1
+#define PFX "sl351x-wdt: "
+
+#define _WATCHDOG_COUNTER  0x00
+#define _WATCHDOG_LOAD     0x04
+#define _WATCHDOG_RESTART  0x08
+#define _WATCHDOG_CR       0x0C
+#define _WATCHDOG_STATUS   0x10
+#define _WATCHDOG_CLEAR    0x14
+#define _WATCHDOG_INTRLEN  0x18
+
+static struct resource  *wdt_mem;
+static struct resource  *wdt_irq;
+static void __iomem     *wdt_base;
+static int 		wdt_margin = WATCHDOG_TIMEOUT_MARGIN;	/* in range of 0 .. 60s */
+
+static int open_state = WATCHDOG_DRIVER_CLOSE;
+static int wd_expire = 0;
+
+static void watchdog_enable(void)
+{
+	unsigned long wdcr;
+
+	wdcr = readl(wdt_base + _WATCHDOG_CR);
+	wdcr |= (WATCHDOG_WDENABLE_MSK|WATCHDOG_WDRST_MSK);
+#ifdef WATCHDOG_TEST
+	wdcr |= WATCHDOG_WDINTR_MSK;
+//	wdcr &= ~WATCHDOG_WDRST_MSK;
+#endif
+	wdcr &= ~WATCHDOG_WDCLOCK_MSK;
+	writel(wdcr, wdt_base + _WATCHDOG_CR);
+}
+
+static void watchdog_set_timeout(unsigned long timeout)
+{
+	timeout = WATCHDOG_TIMEOUT_SCALE * timeout;
+	writel(timeout, wdt_base + _WATCHDOG_LOAD);
+	writel(WATCHDOG_RESTART_VALUE, wdt_base + _WATCHDOG_RESTART);
+}
+
+static void watchdog_keepalive(void)
+{
+	writel(WATCHDOG_RESTART_VALUE, wdt_base + _WATCHDOG_RESTART);
+}
+
+static void watchdog_disable(void)
+{
+	unsigned long wdcr;
+
+	wdcr = readl(wdt_base + _WATCHDOG_CR);
+	wdcr &= ~WATCHDOG_WDENABLE_MSK;
+	writel(wdcr, wdt_base + _WATCHDOG_CR);
+}
+
+
+#ifdef WATCHDOG_TEST
+static irqreturn_t watchdog_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+	unsigned int clear;
+
+	writel(WATCHDOG_CLEAR_STATUS, wdt_base + _WATCHDOG_CLEAR);
+	printk(KERN_INFO PFX "Watchdog timeout, resetting system...\n");
+
+	clear = __raw_readl(IO_ADDRESS(SL2312_INTERRUPT_BASE)+0x0C);
+	clear &= 0x01;
+	__raw_writel(clear,IO_ADDRESS(SL2312_INTERRUPT_BASE)+0x08);
+	wd_expire = 1;
+	return IRQ_HANDLED;
+}
+
+#endif
+
+#define OPTIONS WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE
+static struct watchdog_info sl351x_wdt_ident = {
+	.options          =     OPTIONS,
+	.firmware_version =     0,
+	.identity         =     "sl351x Watchdog",
+};
+
+struct file_operations watchdog_fops = {
+	.write		= watchdog_write,
+	.read		= watchdog_read,
+	.open		= watchdog_open,
+	.release	= watchdog_release,
+	.ioctl		= watchdog_ioctl,
+};
+
+static int watchdog_open(struct inode *inode, struct file *filp)
+{
+	if (open_state == WATCHDOG_DRIVER_OPEN)
+		return -EBUSY;
+
+	wd_expire = 0;
+
+	watchdog_disable();
+	watchdog_set_timeout(wdt_margin);
+	watchdog_enable();
+
+	printk(KERN_INFO PFX "watchog timer enabled, margin: %ds.\n", wdt_margin);
+	open_state = WATCHDOG_DRIVER_OPEN;
+
+	return nonseekable_open(inode, filp);
+}
+
+static int watchdog_release(struct inode *inode, struct file *filp)
+{
+	watchdog_disable();
+
+	open_state = WATCHDOG_DRIVER_CLOSE;
+	wd_expire = 0;
+	printk(KERN_INFO PFX "watchog timer disabled, margin: %ds.\n", wdt_margin);
+
+	return 0;
+}
+
+static ssize_t watchdog_read(struct file *filp, char *buf, size_t count, loff_t *off)
+{
+	int i;
+	unsigned long val;
+
+
+	for(i=0;i< count;i++)
+	{
+		if ((i%4)==0)
+			val = *((unsigned long *)WATCHDOG_COUNTER);
+		buf[i] = (val & 0xFF);
+		val >>= 8;
+	}
+	return count;
+}
+
+static ssize_t watchdog_write(struct file *filp, const char *buf, size_t len, loff_t *off)
+{
+	/*  Refresh the timer. */
+	if (len) {
+		watchdog_keepalive();
+	}
+	return len;
+
+}
+
+static int watchdog_ioctl(struct inode *inode, struct file *filp,
+			  unsigned int cmd, unsigned long arg)
+{
+	void __user *argp = (void __user *)arg;
+	int margin;
+
+	switch(cmd)
+	{
+	case WDIOC_GETSUPPORT:
+		return copy_to_user(argp, &sl351x_wdt_ident,
+				    sizeof(sl351x_wdt_ident)) ? -EFAULT : 0;
+
+	case WDIOC_GETSTATUS:
+	case WDIOC_GETBOOTSTATUS:
+		return put_user(0, (int __user*)argp);
+
+	case WDIOC_KEEPALIVE:
+		watchdog_keepalive();
+		return 0;
+
+	case WDIOC_SETTIMEOUT:
+		if (get_user(margin, (int __user*)argp))
+			return -EFAULT;
+
+		/* Arbitrary, can't find the card's limits */
+		if ((margin < 0) || (margin > 60))
+			return -EINVAL;
+
+		// watchdog_disable();
+		wdt_margin = margin;
+		watchdog_set_timeout(margin);
+		watchdog_keepalive();
+		// watchdog_enable();
+
+		/* Fall through */
+
+	case WDIOC_GETTIMEOUT:
+		return put_user(wdt_margin, (int *)arg);
+
+	default:
+		return -ENOIOCTLCMD;
+	}
+}
+
+static struct miscdevice wd_dev= {
+	WATCHDOG_MINOR,
+	"watchdog",
+	&watchdog_fops
+};
+
+static char banner[] __initdata = KERN_INFO "SL351x Watchdog Timer, (c) 2007 WILIBOX\n";
+
+static int sl351x_wdt_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	int ret, size;
+	unsigned long wdcr;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (res == NULL) {
+		printk(KERN_INFO PFX "failed to get memory region resouce\n");
+		return -ENOMEM;
+	}
+
+	size = (res->end-res->start)+1;
+
+	wdt_mem = request_mem_region(res->start, size, pdev->name);
+	if (wdt_mem == NULL) {
+		printk(KERN_INFO PFX "failed to get memory region\n");
+		return -ENOENT;
+	}
+
+	wdt_base = ioremap(res->start, size);
+	if (wdt_base == NULL) {
+		printk(KERN_INFO PFX "failed to ioremap() region\n");
+		return -EINVAL;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (res == NULL) {
+		 printk(KERN_INFO PFX "failed to get irq resource\n");
+		 return -ENOENT;
+	}
+
+	wdt_irq = res;
+
+	ret = request_irq(res->start, watchdog_irq, 0, pdev->name, pdev);
+	if (ret != 0) {
+		printk(KERN_INFO PFX "failed to install irq (%d)\n", ret);
+		return ret;
+	}
+
+	wdcr = readl(wdt_base + _WATCHDOG_CR);
+	if (wdcr & WATCHDOG_WDENABLE_MSK) {
+		printk(KERN_INFO PFX "Found watchdog in enabled state, reseting ...\n");
+		wdcr &= ~WATCHDOG_WDENABLE_MSK;
+		writel(wdcr, wdt_base + _WATCHDOG_CR);
+	}
+
+	ret = misc_register(&wd_dev);
+
+	return ret;
+}
+
+static int sl351x_wdt_remove(struct platform_device *pdev)
+{
+	if (wdt_base != NULL) {
+		iounmap(wdt_base);
+		wdt_base = NULL;
+	}
+
+	if (wdt_irq != NULL) {
+		free_irq(wdt_irq->start, pdev);
+		release_resource(wdt_irq);
+		wdt_irq = NULL;
+	}
+
+	if (wdt_mem != NULL) {
+		release_resource(wdt_mem);
+		wdt_mem = NULL;
+	}
+
+	misc_deregister(&wd_dev);
+
+	return 0;
+}
+
+static void sl351x_wdt_shutdown(struct platform_device *dev)
+{
+	watchdog_disable();
+}
+
+#ifdef CONFIG_PM
+static int sl351x_wdt_suspend(struct platform_device *dev, pm_message_t state)
+{
+	watchdog_disable();
+}
+
+static int sl351x_wdt_resume(struct platform_device *dev)
+{
+	watchdog_set_timeout(wdt_margin);
+	watchdog_enable();
+}
+
+#else
+#define sl351x_wdt_suspend	NULL
+#define sl351x_wdt_resume	NULL
+#endif
+
+static struct platform_driver sl351x_wdt_driver = {
+	.probe          = sl351x_wdt_probe,
+	.remove         = sl351x_wdt_remove,
+	.shutdown       = sl351x_wdt_shutdown,
+	.suspend        = sl351x_wdt_suspend,
+	.resume         = sl351x_wdt_resume,
+	.driver         = {
+		.owner  = THIS_MODULE,
+		.name   = "sl351x-wdt",
+	},
+};
+
+static int __init watchdog_init(void)
+{
+	printk(banner);
+	return platform_driver_register(&sl351x_wdt_driver);
+}
+
+static void __exit watchdog_exit(void)
+{
+	platform_driver_unregister(&sl351x_wdt_driver);
+}
+
+module_init(watchdog_init);
+module_exit(watchdog_exit);