aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/watchdog/orion5x_wdt.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/watchdog/orion5x_wdt.c')
-rw-r--r--drivers/watchdog/orion5x_wdt.c231
1 files changed, 231 insertions, 0 deletions
diff --git a/drivers/watchdog/orion5x_wdt.c b/drivers/watchdog/orion5x_wdt.c
new file mode 100644
index 000000000000..144776314989
--- /dev/null
+++ b/drivers/watchdog/orion5x_wdt.c
@@ -0,0 +1,231 @@
1/*
2 * drivers/watchdog/orion5x_wdt.c
3 *
4 * Watchdog driver for Orion5x 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/watchdog.h>
20#include <linux/init.h>
21#include <linux/uaccess.h>
22#include <linux/io.h>
23
24/*
25 * Watchdog timer block registers.
26 */
27#define TIMER_CTRL (TIMER_VIRT_BASE + 0x0000)
28#define WDT_EN 0x0010
29#define WDT_VAL (TIMER_VIRT_BASE + 0x0024)
30
31#define WDT_MAX_DURATION (0xffffffff / ORION5X_TCLK)
32#define WDT_IN_USE 0
33#define WDT_OK_TO_CLOSE 1
34
35static int nowayout = WATCHDOG_NOWAYOUT;
36static int heartbeat = WDT_MAX_DURATION; /* (seconds) */
37static unsigned long wdt_status;
38
39static void wdt_enable(void)
40{
41 u32 reg;
42
43 /* Set watchdog duration */
44 writel(ORION5X_TCLK * heartbeat, WDT_VAL);
45
46 /* Clear watchdog timer interrupt */
47 reg = readl(BRIDGE_CAUSE);
48 reg &= ~WDT_INT_REQ;
49 writel(reg, BRIDGE_CAUSE);
50
51 /* Enable watchdog timer */
52 reg = readl(TIMER_CTRL);
53 reg |= WDT_EN;
54 writel(reg, TIMER_CTRL);
55
56 /* Enable reset on watchdog */
57 reg = readl(CPU_RESET_MASK);
58 reg |= WDT_RESET;
59 writel(reg, CPU_RESET_MASK);
60}
61
62static void wdt_disable(void)
63{
64 u32 reg;
65
66 /* Disable reset on watchdog */
67 reg = readl(CPU_RESET_MASK);
68 reg &= ~WDT_RESET;
69 writel(reg, CPU_RESET_MASK);
70
71 /* Disable watchdog timer */
72 reg = readl(TIMER_CTRL);
73 reg &= ~WDT_EN;
74 writel(reg, TIMER_CTRL);
75}
76
77static int orion5x_wdt_open(struct inode *inode, struct file *file)
78{
79 if (test_and_set_bit(WDT_IN_USE, &wdt_status))
80 return -EBUSY;
81 clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
82 wdt_enable();
83 return nonseekable_open(inode, file);
84}
85
86static int orion5x_wdt_get_timeleft(int *time_left)
87{
88 *time_left = readl(WDT_VAL) / ORION5X_TCLK;
89 return 0;
90}
91
92static ssize_t orion5x_wdt_write(struct file *file, const char *data,
93 size_t len, loff_t *ppos)
94{
95 if (len) {
96 if (!nowayout) {
97 size_t i;
98
99 clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
100 for (i = 0; i != len; i++) {
101 char c;
102
103 if (get_user(c, data + i))
104 return -EFAULT;
105 if (c == 'V')
106 set_bit(WDT_OK_TO_CLOSE, &wdt_status);
107 }
108 }
109 wdt_enable();
110 }
111 return len;
112}
113
114static struct watchdog_info ident = {
115 .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT |
116 WDIOF_KEEPALIVEPING,
117 .identity = "Orion5x Watchdog",
118};
119
120
121static long orion5x_wdt_ioctl(struct file *file, unsigned int cmd,
122 unsigned long arg)
123{
124 int ret = -ENOTTY;
125 int time;
126
127 switch (cmd) {
128 case WDIOC_GETSUPPORT:
129 ret = copy_to_user((struct watchdog_info *)arg, &ident,
130 sizeof(ident)) ? -EFAULT : 0;
131 break;
132
133 case WDIOC_GETSTATUS:
134 case WDIOC_GETBOOTSTATUS:
135 ret = put_user(0, (int *)arg);
136 break;
137
138 case WDIOC_KEEPALIVE:
139 wdt_enable();
140 ret = 0;
141 break;
142
143 case WDIOC_SETTIMEOUT:
144 ret = get_user(time, (int *)arg);
145 if (ret)
146 break;
147
148 if (time <= 0 || time > WDT_MAX_DURATION) {
149 ret = -EINVAL;
150 break;
151 }
152 heartbeat = time;
153 wdt_enable();
154 /* Fall through */
155
156 case WDIOC_GETTIMEOUT:
157 ret = put_user(heartbeat, (int *)arg);
158 break;
159
160 case WDIOC_GETTIMELEFT:
161 if (orion5x_wdt_get_timeleft(&time)) {
162 ret = -EINVAL;
163 break;
164 }
165 ret = put_user(time, (int *)arg);
166 break;
167 }
168 return ret;
169}
170
171static int orion5x_wdt_release(struct inode *inode, struct file *file)
172{
173 if (test_bit(WDT_OK_TO_CLOSE, &wdt_status))
174 wdt_disable();
175 else
176 printk(KERN_CRIT "WATCHDOG: Device closed unexpectedly - "
177 "timer will not stop\n");
178 clear_bit(WDT_IN_USE, &wdt_status);
179 clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
180
181 return 0;
182}
183
184
185static const struct file_operations orion5x_wdt_fops = {
186 .owner = THIS_MODULE,
187 .llseek = no_llseek,
188 .write = orion5x_wdt_write,
189 .unlocked_ioctl = orion5x_wdt_ioctl,
190 .open = orion5x_wdt_open,
191 .release = orion5x_wdt_release,
192};
193
194static struct miscdevice orion5x_wdt_miscdev = {
195 .minor = WATCHDOG_MINOR,
196 .name = "watchdog",
197 .fops = &orion5x_wdt_fops,
198};
199
200static int __init orion5x_wdt_init(void)
201{
202 int ret;
203
204 ret = misc_register(&orion5x_wdt_miscdev);
205 if (ret == 0)
206 printk("Orion5x Watchdog Timer: heartbeat %d sec\n",
207 heartbeat);
208
209 return ret;
210}
211
212static void __exit orion5x_wdt_exit(void)
213{
214 misc_deregister(&orion5x_wdt_miscdev);
215}
216
217module_init(orion5x_wdt_init);
218module_exit(orion5x_wdt_exit);
219
220MODULE_AUTHOR("Sylver Bruneau <sylver.bruneau@googlemail.com>");
221MODULE_DESCRIPTION("Orion5x Processor Watchdog");
222
223module_param(heartbeat, int, 0);
224MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds (default is "
225 __MODULE_STRING(WDT_MAX_DURATION) ")");
226
227module_param(nowayout, int, 0);
228MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started");
229
230MODULE_LICENSE("GPL");
231MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);