aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/watchdog/orion_wdt.c
diff options
context:
space:
mode:
authorNicolas Pitre <nico@cam.org>2009-06-01 13:56:02 -0400
committerNicolas Pitre <nico@cam.org>2009-06-08 13:05:00 -0400
commit3b937a7dbddbedd9457b33fcc8fa369c0c229c6e (patch)
treef02422b135326f4212585501744ab2fd2d6fb3e8 /drivers/watchdog/orion_wdt.c
parent054bd3f053de54c81b20df11f354476389826e61 (diff)
[ARM] Orion/Kirkwood: rename orion5x_wdt to orion_wdt
The Orion watchdog driver is also used on Kirkwood. Convention is to use orion5x for stuff specific to 88F5xxx Orion chips and simply "orion" for shared stuff across SoCs including Kirkwood. Signed-off-by: Nicolas Pitre <nico@marvell.com>
Diffstat (limited to 'drivers/watchdog/orion_wdt.c')
-rw-r--r--drivers/watchdog/orion_wdt.c322
1 files changed, 322 insertions, 0 deletions
diff --git a/drivers/watchdog/orion_wdt.c b/drivers/watchdog/orion_wdt.c
new file mode 100644
index 000000000000..2d9fb96a9ee9
--- /dev/null
+++ b/drivers/watchdog/orion_wdt.c
@@ -0,0 +1,322 @@
1/*
2 * drivers/watchdog/orion_wdt.c
3 *
4 * Watchdog driver for Orion/Kirkwood processors
5 *
6 * Author: Sylver Bruneau <sylver.bruneau@googlemail.com>
7 *
8 * This file is licensed under the terms of the GNU General Public
9 * License version 2. This program is licensed "as is" without any
10 * warranty of any kind, whether express or implied.
11 */
12
13#include <linux/module.h>
14#include <linux/moduleparam.h>
15#include <linux/types.h>
16#include <linux/kernel.h>
17#include <linux/fs.h>
18#include <linux/miscdevice.h>
19#include <linux/platform_device.h>
20#include <linux/watchdog.h>
21#include <linux/init.h>
22#include <linux/uaccess.h>
23#include <linux/io.h>
24#include <linux/spinlock.h>
25#include <mach/bridge-regs.h>
26#include <plat/orion_wdt.h>
27
28/*
29 * Watchdog timer block registers.
30 */
31#define TIMER_CTRL (TIMER_VIRT_BASE + 0x0000)
32#define WDT_EN 0x0010
33#define WDT_VAL (TIMER_VIRT_BASE + 0x0024)
34
35#define WDT_MAX_CYCLE_COUNT 0xffffffff
36#define WDT_IN_USE 0
37#define WDT_OK_TO_CLOSE 1
38
39static int nowayout = WATCHDOG_NOWAYOUT;
40static int heartbeat = -1; /* module parameter (seconds) */
41static unsigned int wdt_max_duration; /* (seconds) */
42static unsigned int wdt_tclk;
43static unsigned long wdt_status;
44static spinlock_t wdt_lock;
45
46static void orion_wdt_ping(void)
47{
48 spin_lock(&wdt_lock);
49
50 /* Reload watchdog duration */
51 writel(wdt_tclk * heartbeat, WDT_VAL);
52
53 spin_unlock(&wdt_lock);
54}
55
56static void orion_wdt_enable(void)
57{
58 u32 reg;
59
60 spin_lock(&wdt_lock);
61
62 /* Set watchdog duration */
63 writel(wdt_tclk * heartbeat, WDT_VAL);
64
65 /* Clear watchdog timer interrupt */
66 reg = readl(BRIDGE_CAUSE);
67 reg &= ~WDT_INT_REQ;
68 writel(reg, BRIDGE_CAUSE);
69
70 /* Enable watchdog timer */
71 reg = readl(TIMER_CTRL);
72 reg |= WDT_EN;
73 writel(reg, TIMER_CTRL);
74
75 /* Enable reset on watchdog */
76 reg = readl(RSTOUTn_MASK);
77 reg |= WDT_RESET_OUT_EN;
78 writel(reg, RSTOUTn_MASK);
79
80 spin_unlock(&wdt_lock);
81}
82
83static void orion_wdt_disable(void)
84{
85 u32 reg;
86
87 spin_lock(&wdt_lock);
88
89 /* Disable reset on watchdog */
90 reg = readl(RSTOUTn_MASK);
91 reg &= ~WDT_RESET_OUT_EN;
92 writel(reg, RSTOUTn_MASK);
93
94 /* Disable watchdog timer */
95 reg = readl(TIMER_CTRL);
96 reg &= ~WDT_EN;
97 writel(reg, TIMER_CTRL);
98
99 spin_unlock(&wdt_lock);
100}
101
102static int orion_wdt_get_timeleft(int *time_left)
103{
104 spin_lock(&wdt_lock);
105 *time_left = readl(WDT_VAL) / wdt_tclk;
106 spin_unlock(&wdt_lock);
107 return 0;
108}
109
110static int orion_wdt_open(struct inode *inode, struct file *file)
111{
112 if (test_and_set_bit(WDT_IN_USE, &wdt_status))
113 return -EBUSY;
114 clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
115 orion_wdt_enable();
116 return nonseekable_open(inode, file);
117}
118
119static ssize_t orion_wdt_write(struct file *file, const char *data,
120 size_t len, loff_t *ppos)
121{
122 if (len) {
123 if (!nowayout) {
124 size_t i;
125
126 clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
127 for (i = 0; i != len; i++) {
128 char c;
129
130 if (get_user(c, data + i))
131 return -EFAULT;
132 if (c == 'V')
133 set_bit(WDT_OK_TO_CLOSE, &wdt_status);
134 }
135 }
136 orion_wdt_ping();
137 }
138 return len;
139}
140
141static int orion_wdt_settimeout(int new_time)
142{
143 if ((new_time <= 0) || (new_time > wdt_max_duration))
144 return -EINVAL;
145
146 /* Set new watchdog time to be used when
147 * orion_wdt_enable() or orion_wdt_ping() is called. */
148 heartbeat = new_time;
149 return 0;
150}
151
152static const struct watchdog_info ident = {
153 .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT |
154 WDIOF_KEEPALIVEPING,
155 .identity = "Orion Watchdog",
156};
157
158static long orion_wdt_ioctl(struct file *file, unsigned int cmd,
159 unsigned long arg)
160{
161 int ret = -ENOTTY;
162 int time;
163
164 switch (cmd) {
165 case WDIOC_GETSUPPORT:
166 ret = copy_to_user((struct watchdog_info *)arg, &ident,
167 sizeof(ident)) ? -EFAULT : 0;
168 break;
169
170 case WDIOC_GETSTATUS:
171 case WDIOC_GETBOOTSTATUS:
172 ret = put_user(0, (int *)arg);
173 break;
174
175 case WDIOC_KEEPALIVE:
176 orion_wdt_ping();
177 ret = 0;
178 break;
179
180 case WDIOC_SETTIMEOUT:
181 ret = get_user(time, (int *)arg);
182 if (ret)
183 break;
184
185 if (orion_wdt_settimeout(time)) {
186 ret = -EINVAL;
187 break;
188 }
189 orion_wdt_ping();
190 /* Fall through */
191
192 case WDIOC_GETTIMEOUT:
193 ret = put_user(heartbeat, (int *)arg);
194 break;
195
196 case WDIOC_GETTIMELEFT:
197 if (orion_wdt_get_timeleft(&time)) {
198 ret = -EINVAL;
199 break;
200 }
201 ret = put_user(time, (int *)arg);
202 break;
203 }
204 return ret;
205}
206
207static int orion_wdt_release(struct inode *inode, struct file *file)
208{
209 if (test_bit(WDT_OK_TO_CLOSE, &wdt_status))
210 orion_wdt_disable();
211 else
212 printk(KERN_CRIT "WATCHDOG: Device closed unexpectedly - "
213 "timer will not stop\n");
214 clear_bit(WDT_IN_USE, &wdt_status);
215 clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
216
217 return 0;
218}
219
220
221static const struct file_operations orion_wdt_fops = {
222 .owner = THIS_MODULE,
223 .llseek = no_llseek,
224 .write = orion_wdt_write,
225 .unlocked_ioctl = orion_wdt_ioctl,
226 .open = orion_wdt_open,
227 .release = orion_wdt_release,
228};
229
230static struct miscdevice orion_wdt_miscdev = {
231 .minor = WATCHDOG_MINOR,
232 .name = "watchdog",
233 .fops = &orion_wdt_fops,
234};
235
236static int __devinit orion_wdt_probe(struct platform_device *pdev)
237{
238 struct orion_wdt_platform_data *pdata = pdev->dev.platform_data;
239 int ret;
240
241 if (pdata) {
242 wdt_tclk = pdata->tclk;
243 } else {
244 printk(KERN_ERR "Orion Watchdog misses platform data\n");
245 return -ENODEV;
246 }
247
248 if (orion_wdt_miscdev.parent)
249 return -EBUSY;
250 orion_wdt_miscdev.parent = &pdev->dev;
251
252 wdt_max_duration = WDT_MAX_CYCLE_COUNT / wdt_tclk;
253 if (orion_wdt_settimeout(heartbeat))
254 heartbeat = wdt_max_duration;
255
256 ret = misc_register(&orion_wdt_miscdev);
257 if (ret)
258 return ret;
259
260 printk(KERN_INFO "Orion Watchdog Timer: Initial timeout %d sec%s\n",
261 heartbeat, nowayout ? ", nowayout" : "");
262 return 0;
263}
264
265static int __devexit orion_wdt_remove(struct platform_device *pdev)
266{
267 int ret;
268
269 if (test_bit(WDT_IN_USE, &wdt_status)) {
270 orion_wdt_disable();
271 clear_bit(WDT_IN_USE, &wdt_status);
272 }
273
274 ret = misc_deregister(&orion_wdt_miscdev);
275 if (!ret)
276 orion_wdt_miscdev.parent = NULL;
277
278 return ret;
279}
280
281static void orion_wdt_shutdown(struct platform_device *pdev)
282{
283 if (test_bit(WDT_IN_USE, &wdt_status))
284 orion_wdt_disable();
285}
286
287static struct platform_driver orion_wdt_driver = {
288 .probe = orion_wdt_probe,
289 .remove = __devexit_p(orion_wdt_remove),
290 .shutdown = orion_wdt_shutdown,
291 .driver = {
292 .owner = THIS_MODULE,
293 .name = "orion_wdt",
294 },
295};
296
297static int __init orion_wdt_init(void)
298{
299 spin_lock_init(&wdt_lock);
300 return platform_driver_register(&orion_wdt_driver);
301}
302
303static void __exit orion_wdt_exit(void)
304{
305 platform_driver_unregister(&orion_wdt_driver);
306}
307
308module_init(orion_wdt_init);
309module_exit(orion_wdt_exit);
310
311MODULE_AUTHOR("Sylver Bruneau <sylver.bruneau@googlemail.com>");
312MODULE_DESCRIPTION("Orion Processor Watchdog");
313
314module_param(heartbeat, int, 0);
315MODULE_PARM_DESC(heartbeat, "Initial watchdog heartbeat in seconds");
316
317module_param(nowayout, int, 0);
318MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
319 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
320
321MODULE_LICENSE("GPL");
322MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);