Blame view

drivers/watchdog/mtx-1_wdt.c 6.33 KB
04bf3b4f5   Florian Fainelli   [WATCHDOG] MTX-1 ...
1
2
3
  /*
   *      Driver for the MTX-1 Watchdog.
   *
ed78c2da1   Alan Cox   [WATCHDOG 28/57] ...
4
5
   *      (C) Copyright 2005 4G Systems <info@4g-systems.biz>,
   *							All Rights Reserved.
04bf3b4f5   Florian Fainelli   [WATCHDOG] MTX-1 ...
6
7
   *                              http://www.4g-systems.biz
   *
143a2e54b   Wim Van Sebroeck   [WATCHDOG] More c...
8
   *	(C) Copyright 2007 OpenWrt.org, Florian Fainelli <florian@openwrt.org>
04bf3b4f5   Florian Fainelli   [WATCHDOG] MTX-1 ...
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
   *
   *      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.
   *
   *      Neither Michael Stickel nor 4G Systems admit liability nor provide
   *      warranty for any of this software. This material is provided
   *      "AS-IS" and at no charge.
   *
   *      (c) Copyright 2005    4G Systems <info@4g-systems.biz>
   *
   *      Release 0.01.
   *      Author: Michael Stickel  michael.stickel@4g-systems.biz
   *
   *      Release 0.02.
   *	Author: Florian Fainelli florian@openwrt.org
   *		use the Linux watchdog/timer APIs
   *
   *      The Watchdog is configured to reset the MTX-1
   *      if it is not triggered for 100 seconds.
   *      It should not be triggered more often than 1.6 seconds.
   *
   *      A timer triggers the watchdog every 5 seconds, until
   *      it is opened for the first time. After the first open
   *      it MUST be triggered every 2..95 seconds.
   */
  
  #include <linux/module.h>
  #include <linux/moduleparam.h>
  #include <linux/types.h>
  #include <linux/errno.h>
  #include <linux/miscdevice.h>
  #include <linux/fs.h>
  #include <linux/init.h>
  #include <linux/ioport.h>
  #include <linux/timer.h>
  #include <linux/completion.h>
  #include <linux/jiffies.h>
  #include <linux/watchdog.h>
6ea8115bb   Florian Fainelli   [WATCHDOG] Conver...
49
  #include <linux/platform_device.h>
ed78c2da1   Alan Cox   [WATCHDOG 28/57] ...
50
51
52
  #include <linux/io.h>
  #include <linux/uaccess.h>
  #include <linux/gpio.h>
04bf3b4f5   Florian Fainelli   [WATCHDOG] MTX-1 ...
53
54
55
56
57
58
59
60
61
  
  #include <asm/mach-au1x00/au1000.h>
  
  #define MTX1_WDT_INTERVAL	(5 * HZ)
  
  static int ticks = 100 * HZ;
  
  static struct {
  	struct completion stop;
ed78c2da1   Alan Cox   [WATCHDOG 28/57] ...
62
  	spinlock_t lock;
996d62d44   Florian Fainelli   [WATCHDOG] Remove...
63
  	int running;
04bf3b4f5   Florian Fainelli   [WATCHDOG] MTX-1 ...
64
  	struct timer_list timer;
996d62d44   Florian Fainelli   [WATCHDOG] Remove...
65
  	int queue;
04bf3b4f5   Florian Fainelli   [WATCHDOG] MTX-1 ...
66
67
  	int default_ticks;
  	unsigned long inuse;
6ea8115bb   Florian Fainelli   [WATCHDOG] Conver...
68
  	unsigned gpio;
2ea4e76e9   Florian Fainelli   watchdog: mtx1-wd...
69
  	unsigned int gstate;
04bf3b4f5   Florian Fainelli   [WATCHDOG] MTX-1 ...
70
71
72
73
  } mtx1_wdt_device;
  
  static void mtx1_wdt_trigger(unsigned long unused)
  {
ed78c2da1   Alan Cox   [WATCHDOG 28/57] ...
74
  	spin_lock(&mtx1_wdt_device.lock);
04bf3b4f5   Florian Fainelli   [WATCHDOG] MTX-1 ...
75
76
  	if (mtx1_wdt_device.running)
  		ticks--;
b7f720d68   Manuel Lauss   MIPS: Alchemy: Cl...
77
78
  
  	/* toggle wdt gpio */
2ea4e76e9   Florian Fainelli   watchdog: mtx1-wd...
79
80
  	mtx1_wdt_device.gstate = !mtx1_wdt_device.gstate;
  	gpio_set_value(mtx1_wdt_device.gpio, mtx1_wdt_device.gstate);
04bf3b4f5   Florian Fainelli   [WATCHDOG] MTX-1 ...
81
82
83
  
  	if (mtx1_wdt_device.queue && ticks)
  		mod_timer(&mtx1_wdt_device.timer, jiffies + MTX1_WDT_INTERVAL);
ed78c2da1   Alan Cox   [WATCHDOG 28/57] ...
84
  	else
04bf3b4f5   Florian Fainelli   [WATCHDOG] MTX-1 ...
85
  		complete(&mtx1_wdt_device.stop);
ed78c2da1   Alan Cox   [WATCHDOG 28/57] ...
86
  	spin_unlock(&mtx1_wdt_device.lock);
04bf3b4f5   Florian Fainelli   [WATCHDOG] MTX-1 ...
87
88
89
90
91
92
93
94
95
96
  }
  
  static void mtx1_wdt_reset(void)
  {
  	ticks = mtx1_wdt_device.default_ticks;
  }
  
  
  static void mtx1_wdt_start(void)
  {
f80e919bb   Florian Fainelli   [WATCHDOG] fix mt...
97
  	unsigned long flags;
ed78c2da1   Alan Cox   [WATCHDOG 28/57] ...
98
  	spin_lock_irqsave(&mtx1_wdt_device.lock, flags);
04bf3b4f5   Florian Fainelli   [WATCHDOG] MTX-1 ...
99
100
  	if (!mtx1_wdt_device.queue) {
  		mtx1_wdt_device.queue = 1;
b7f720d68   Manuel Lauss   MIPS: Alchemy: Cl...
101
  		mtx1_wdt_device.gstate = 1;
2ea4e76e9   Florian Fainelli   watchdog: mtx1-wd...
102
  		gpio_set_value(mtx1_wdt_device.gpio, 1);
04bf3b4f5   Florian Fainelli   [WATCHDOG] MTX-1 ...
103
104
105
  		mod_timer(&mtx1_wdt_device.timer, jiffies + MTX1_WDT_INTERVAL);
  	}
  	mtx1_wdt_device.running++;
ed78c2da1   Alan Cox   [WATCHDOG 28/57] ...
106
  	spin_unlock_irqrestore(&mtx1_wdt_device.lock, flags);
04bf3b4f5   Florian Fainelli   [WATCHDOG] MTX-1 ...
107
108
109
110
  }
  
  static int mtx1_wdt_stop(void)
  {
f80e919bb   Florian Fainelli   [WATCHDOG] fix mt...
111
  	unsigned long flags;
ed78c2da1   Alan Cox   [WATCHDOG 28/57] ...
112
  	spin_lock_irqsave(&mtx1_wdt_device.lock, flags);
04bf3b4f5   Florian Fainelli   [WATCHDOG] MTX-1 ...
113
114
  	if (mtx1_wdt_device.queue) {
  		mtx1_wdt_device.queue = 0;
b7f720d68   Manuel Lauss   MIPS: Alchemy: Cl...
115
  		mtx1_wdt_device.gstate = 0;
2ea4e76e9   Florian Fainelli   watchdog: mtx1-wd...
116
  		gpio_set_value(mtx1_wdt_device.gpio, 0);
04bf3b4f5   Florian Fainelli   [WATCHDOG] MTX-1 ...
117
  	}
04bf3b4f5   Florian Fainelli   [WATCHDOG] MTX-1 ...
118
  	ticks = mtx1_wdt_device.default_ticks;
ed78c2da1   Alan Cox   [WATCHDOG 28/57] ...
119
  	spin_unlock_irqrestore(&mtx1_wdt_device.lock, flags);
04bf3b4f5   Florian Fainelli   [WATCHDOG] MTX-1 ...
120
121
122
123
124
125
126
127
128
  	return 0;
  }
  
  /* Filesystem functions */
  
  static int mtx1_wdt_open(struct inode *inode, struct file *file)
  {
  	if (test_and_set_bit(0, &mtx1_wdt_device.inuse))
  		return -EBUSY;
04bf3b4f5   Florian Fainelli   [WATCHDOG] MTX-1 ...
129
130
131
132
133
134
135
136
137
  	return nonseekable_open(inode, file);
  }
  
  
  static int mtx1_wdt_release(struct inode *inode, struct file *file)
  {
  	clear_bit(0, &mtx1_wdt_device.inuse);
  	return 0;
  }
ed78c2da1   Alan Cox   [WATCHDOG 28/57] ...
138
139
  static long mtx1_wdt_ioctl(struct file *file, unsigned int cmd,
  							unsigned long arg)
04bf3b4f5   Florian Fainelli   [WATCHDOG] MTX-1 ...
140
141
  {
  	void __user *argp = (void __user *)arg;
ed78c2da1   Alan Cox   [WATCHDOG 28/57] ...
142
  	int __user *p = (int __user *)argp;
04bf3b4f5   Florian Fainelli   [WATCHDOG] MTX-1 ...
143
  	unsigned int value;
ed78c2da1   Alan Cox   [WATCHDOG 28/57] ...
144
  	static const struct watchdog_info ident = {
04bf3b4f5   Florian Fainelli   [WATCHDOG] MTX-1 ...
145
146
147
  		.options = WDIOF_CARDRESET,
  		.identity = "MTX-1 WDT",
  	};
ed78c2da1   Alan Cox   [WATCHDOG 28/57] ...
148
  	switch (cmd) {
0c06090c9   Wim Van Sebroeck   [WATCHDOG] Coding...
149
150
151
  	case WDIOC_GETSUPPORT:
  		if (copy_to_user(argp, &ident, sizeof(ident)))
  			return -EFAULT;
ed78c2da1   Alan Cox   [WATCHDOG 28/57] ...
152
153
154
155
156
  		break;
  	case WDIOC_GETSTATUS:
  	case WDIOC_GETBOOTSTATUS:
  		put_user(0, p);
  		break;
ed78c2da1   Alan Cox   [WATCHDOG 28/57] ...
157
158
159
160
161
162
163
164
165
166
  	case WDIOC_SETOPTIONS:
  		if (get_user(value, p))
  			return -EFAULT;
  		if (value & WDIOS_ENABLECARD)
  			mtx1_wdt_start();
  		else if (value & WDIOS_DISABLECARD)
  			mtx1_wdt_stop();
  		else
  			return -EINVAL;
  		return 0;
0c06090c9   Wim Van Sebroeck   [WATCHDOG] Coding...
167
168
169
  	case WDIOC_KEEPALIVE:
  		mtx1_wdt_reset();
  		break;
ed78c2da1   Alan Cox   [WATCHDOG 28/57] ...
170
171
  	default:
  		return -ENOTTY;
04bf3b4f5   Florian Fainelli   [WATCHDOG] MTX-1 ...
172
173
174
  	}
  	return 0;
  }
ed78c2da1   Alan Cox   [WATCHDOG 28/57] ...
175
176
  static ssize_t mtx1_wdt_write(struct file *file, const char *buf,
  						size_t count, loff_t *ppos)
04bf3b4f5   Florian Fainelli   [WATCHDOG] MTX-1 ...
177
178
179
  {
  	if (!count)
  		return -EIO;
04bf3b4f5   Florian Fainelli   [WATCHDOG] MTX-1 ...
180
181
182
  	mtx1_wdt_reset();
  	return count;
  }
b47a166ed   Jan Engelhardt   [WATCHDOG] consti...
183
  static const struct file_operations mtx1_wdt_fops = {
5f3b27569   Wim Van Sebroeck   watchdog: cleanup...
184
  	.owner		= THIS_MODULE,
04bf3b4f5   Florian Fainelli   [WATCHDOG] MTX-1 ...
185
  	.llseek		= no_llseek,
ed78c2da1   Alan Cox   [WATCHDOG 28/57] ...
186
  	.unlocked_ioctl	= mtx1_wdt_ioctl,
5f3b27569   Wim Van Sebroeck   watchdog: cleanup...
187
188
189
  	.open		= mtx1_wdt_open,
  	.write		= mtx1_wdt_write,
  	.release	= mtx1_wdt_release,
04bf3b4f5   Florian Fainelli   [WATCHDOG] MTX-1 ...
190
191
192
193
  };
  
  
  static struct miscdevice mtx1_wdt_misc = {
5f3b27569   Wim Van Sebroeck   watchdog: cleanup...
194
195
196
  	.minor	= WATCHDOG_MINOR,
  	.name	= "watchdog",
  	.fops	= &mtx1_wdt_fops,
04bf3b4f5   Florian Fainelli   [WATCHDOG] MTX-1 ...
197
  };
b6bf291f1   Wim Van Sebroeck   [WATCHDOG] move p...
198
  static int __devinit mtx1_wdt_probe(struct platform_device *pdev)
04bf3b4f5   Florian Fainelli   [WATCHDOG] MTX-1 ...
199
200
  {
  	int ret;
6ea8115bb   Florian Fainelli   [WATCHDOG] Conver...
201
  	mtx1_wdt_device.gpio = pdev->resource[0].start;
9b19d40aa   Florian Fainelli   watchdog: mtx1-wd...
202
203
204
205
206
207
  	ret = gpio_request_one(mtx1_wdt_device.gpio,
  				GPIOF_OUT_INIT_HIGH, "mtx1-wdt");
  	if (ret < 0) {
  		dev_err(&pdev->dev, "failed to request gpio");
  		return ret;
  	}
6ea8115bb   Florian Fainelli   [WATCHDOG] Conver...
208

ed78c2da1   Alan Cox   [WATCHDOG 28/57] ...
209
  	spin_lock_init(&mtx1_wdt_device.lock);
04bf3b4f5   Florian Fainelli   [WATCHDOG] MTX-1 ...
210
211
  	init_completion(&mtx1_wdt_device.stop);
  	mtx1_wdt_device.queue = 0;
04bf3b4f5   Florian Fainelli   [WATCHDOG] MTX-1 ...
212
  	clear_bit(0, &mtx1_wdt_device.inuse);
04bf3b4f5   Florian Fainelli   [WATCHDOG] MTX-1 ...
213
  	setup_timer(&mtx1_wdt_device.timer, mtx1_wdt_trigger, 0L);
04bf3b4f5   Florian Fainelli   [WATCHDOG] MTX-1 ...
214
  	mtx1_wdt_device.default_ticks = ticks;
ed78c2da1   Alan Cox   [WATCHDOG 28/57] ...
215
216
  	ret = misc_register(&mtx1_wdt_misc);
  	if (ret < 0) {
fad0a9dd0   Florian Fainelli   watchdog: mtx1-wd...
217
218
  		dev_err(&pdev->dev, "failed to register
  ");
ed78c2da1   Alan Cox   [WATCHDOG 28/57] ...
219
220
  		return ret;
  	}
04bf3b4f5   Florian Fainelli   [WATCHDOG] MTX-1 ...
221
  	mtx1_wdt_start();
fad0a9dd0   Florian Fainelli   watchdog: mtx1-wd...
222
223
  	dev_info(&pdev->dev, "MTX-1 Watchdog driver
  ");
04bf3b4f5   Florian Fainelli   [WATCHDOG] MTX-1 ...
224
225
  	return 0;
  }
b6bf291f1   Wim Van Sebroeck   [WATCHDOG] move p...
226
  static int __devexit mtx1_wdt_remove(struct platform_device *pdev)
04bf3b4f5   Florian Fainelli   [WATCHDOG] MTX-1 ...
227
  {
ed78c2da1   Alan Cox   [WATCHDOG 28/57] ...
228
  	/* FIXME: do we need to lock this test ? */
04bf3b4f5   Florian Fainelli   [WATCHDOG] MTX-1 ...
229
230
231
232
  	if (mtx1_wdt_device.queue) {
  		mtx1_wdt_device.queue = 0;
  		wait_for_completion(&mtx1_wdt_device.stop);
  	}
9b19d40aa   Florian Fainelli   watchdog: mtx1-wd...
233
234
  
  	gpio_free(mtx1_wdt_device.gpio);
04bf3b4f5   Florian Fainelli   [WATCHDOG] MTX-1 ...
235
  	misc_deregister(&mtx1_wdt_misc);
6ea8115bb   Florian Fainelli   [WATCHDOG] Conver...
236
237
  	return 0;
  }
db98f89a2   Florian Fainelli   watchdog: mtx1-wd...
238
  static struct platform_driver mtx1_wdt_driver = {
6ea8115bb   Florian Fainelli   [WATCHDOG] Conver...
239
  	.probe = mtx1_wdt_probe,
b6bf291f1   Wim Van Sebroeck   [WATCHDOG] move p...
240
  	.remove = __devexit_p(mtx1_wdt_remove),
6ea8115bb   Florian Fainelli   [WATCHDOG] Conver...
241
  	.driver.name = "mtx1-wdt",
f37d193c7   Kay Sievers   watchdog: fix pla...
242
  	.driver.owner = THIS_MODULE,
6ea8115bb   Florian Fainelli   [WATCHDOG] Conver...
243
  };
b8ec61189   Axel Lin   watchdog: convert...
244
  module_platform_driver(mtx1_wdt_driver);
04bf3b4f5   Florian Fainelli   [WATCHDOG] MTX-1 ...
245
246
247
248
  
  MODULE_AUTHOR("Michael Stickel, Florian Fainelli");
  MODULE_DESCRIPTION("Driver for the MTX-1 watchdog");
  MODULE_LICENSE("GPL");
6ea8115bb   Florian Fainelli   [WATCHDOG] Conver...
249
  MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
f37d193c7   Kay Sievers   watchdog: fix pla...
250
  MODULE_ALIAS("platform:mtx1-wdt");