2 * drivers/watchdog/orion5x_wdt.c
4 * Watchdog driver for Orion5x processors
6 * Author: Sylver Bruneau <sylver.bruneau@googlemail.com>
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.
13 #include <linux/module.h>
14 #include <linux/moduleparam.h>
15 #include <linux/types.h>
16 #include <linux/kernel.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>
24 #include <linux/spinlock.h>
25 #include <plat/orion5x_wdt.h>
28 * Watchdog timer block registers.
30 #define TIMER_CTRL (TIMER_VIRT_BASE + 0x0000)
32 #define WDT_VAL (TIMER_VIRT_BASE + 0x0024)
34 #define WDT_MAX_CYCLE_COUNT 0xffffffff
36 #define WDT_OK_TO_CLOSE 1
38 static int nowayout = WATCHDOG_NOWAYOUT;
39 static int heartbeat = -1; /* module parameter (seconds) */
40 static unsigned int wdt_max_duration; /* (seconds) */
41 static unsigned int wdt_tclk;
42 static unsigned long wdt_status;
43 static spinlock_t wdt_lock;
45 static void wdt_enable(void)
51 /* Set watchdog duration */
52 writel(wdt_tclk * heartbeat, WDT_VAL);
54 /* Clear watchdog timer interrupt */
55 reg = readl(BRIDGE_CAUSE);
57 writel(reg, BRIDGE_CAUSE);
59 /* Enable watchdog timer */
60 reg = readl(TIMER_CTRL);
62 writel(reg, TIMER_CTRL);
64 /* Enable reset on watchdog */
65 reg = readl(CPU_RESET_MASK);
67 writel(reg, CPU_RESET_MASK);
69 spin_unlock(&wdt_lock);
72 static void wdt_disable(void)
78 /* Disable reset on watchdog */
79 reg = readl(CPU_RESET_MASK);
81 writel(reg, CPU_RESET_MASK);
83 /* Disable watchdog timer */
84 reg = readl(TIMER_CTRL);
86 writel(reg, TIMER_CTRL);
88 spin_unlock(&wdt_lock);
91 static int orion5x_wdt_get_timeleft(int *time_left)
94 *time_left = readl(WDT_VAL) / wdt_tclk;
95 spin_unlock(&wdt_lock);
99 static int orion5x_wdt_open(struct inode *inode, struct file *file)
101 if (test_and_set_bit(WDT_IN_USE, &wdt_status))
103 clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
105 return nonseekable_open(inode, file);
108 static ssize_t orion5x_wdt_write(struct file *file, const char *data,
109 size_t len, loff_t *ppos)
115 clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
116 for (i = 0; i != len; i++) {
119 if (get_user(c, data + i))
122 set_bit(WDT_OK_TO_CLOSE, &wdt_status);
130 static struct watchdog_info ident = {
131 .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT |
133 .identity = "Orion5x Watchdog",
137 static long orion5x_wdt_ioctl(struct file *file, unsigned int cmd,
144 case WDIOC_GETSUPPORT:
145 ret = copy_to_user((struct watchdog_info *)arg, &ident,
146 sizeof(ident)) ? -EFAULT : 0;
149 case WDIOC_GETSTATUS:
150 case WDIOC_GETBOOTSTATUS:
151 ret = put_user(0, (int *)arg);
154 case WDIOC_KEEPALIVE:
159 case WDIOC_SETTIMEOUT:
160 ret = get_user(time, (int *)arg);
164 if (time <= 0 || time > wdt_max_duration) {
172 case WDIOC_GETTIMEOUT:
173 ret = put_user(heartbeat, (int *)arg);
176 case WDIOC_GETTIMELEFT:
177 if (orion5x_wdt_get_timeleft(&time)) {
181 ret = put_user(time, (int *)arg);
187 static int orion5x_wdt_release(struct inode *inode, struct file *file)
189 if (test_bit(WDT_OK_TO_CLOSE, &wdt_status))
192 printk(KERN_CRIT "WATCHDOG: Device closed unexpectedly - "
193 "timer will not stop\n");
194 clear_bit(WDT_IN_USE, &wdt_status);
195 clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
201 static const struct file_operations orion5x_wdt_fops = {
202 .owner = THIS_MODULE,
204 .write = orion5x_wdt_write,
205 .unlocked_ioctl = orion5x_wdt_ioctl,
206 .open = orion5x_wdt_open,
207 .release = orion5x_wdt_release,
210 static struct miscdevice orion5x_wdt_miscdev = {
211 .minor = WATCHDOG_MINOR,
213 .fops = &orion5x_wdt_fops,
216 static int __devinit orion5x_wdt_probe(struct platform_device *pdev)
218 struct orion5x_wdt_platform_data *pdata = pdev->dev.platform_data;
222 wdt_tclk = pdata->tclk;
224 printk(KERN_ERR "Orion5x Watchdog misses platform data\n");
228 if (orion5x_wdt_miscdev.parent)
230 orion5x_wdt_miscdev.parent = &pdev->dev;
232 wdt_max_duration = WDT_MAX_CYCLE_COUNT / wdt_tclk;
233 if (heartbeat <= 0 || heartbeat > wdt_max_duration)
234 heartbeat = wdt_max_duration;
236 ret = misc_register(&orion5x_wdt_miscdev);
240 printk(KERN_INFO "Orion5x Watchdog Timer: Initial timeout %d sec%s\n",
241 heartbeat, nowayout ? ", nowayout" : "");
245 static int __devexit orion5x_wdt_remove(struct platform_device *pdev)
249 if (test_bit(WDT_IN_USE, &wdt_status)) {
251 clear_bit(WDT_IN_USE, &wdt_status);
254 ret = misc_deregister(&orion5x_wdt_miscdev);
256 orion5x_wdt_miscdev.parent = NULL;
261 static struct platform_driver orion5x_wdt_driver = {
262 .probe = orion5x_wdt_probe,
263 .remove = __devexit_p(orion5x_wdt_remove),
265 .owner = THIS_MODULE,
266 .name = "orion5x_wdt",
270 static int __init orion5x_wdt_init(void)
272 spin_lock_init(&wdt_lock);
273 return platform_driver_register(&orion5x_wdt_driver);
276 static void __exit orion5x_wdt_exit(void)
278 platform_driver_unregister(&orion5x_wdt_driver);
281 module_init(orion5x_wdt_init);
282 module_exit(orion5x_wdt_exit);
284 MODULE_AUTHOR("Sylver Bruneau <sylver.bruneau@googlemail.com>");
285 MODULE_DESCRIPTION("Orion5x Processor Watchdog");
287 module_param(heartbeat, int, 0);
288 MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds");
290 module_param(nowayout, int, 0);
291 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started");
293 MODULE_LICENSE("GPL");
294 MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);