Root/
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 | |
39 | static int nowayout = WATCHDOG_NOWAYOUT; |
40 | static int heartbeat = -1; /* module parameter (seconds) */ |
41 | static unsigned int wdt_max_duration; /* (seconds) */ |
42 | static unsigned int wdt_tclk; |
43 | static unsigned long wdt_status; |
44 | static spinlock_t wdt_lock; |
45 | |
46 | static 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 | |
56 | static 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 | |
83 | static 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 | |
102 | static 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 | |
110 | static 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 | |
119 | static 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 | |
141 | static 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 | |
152 | static const struct watchdog_info ident = { |
153 | .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | |
154 | WDIOF_KEEPALIVEPING, |
155 | .identity = "Orion Watchdog", |
156 | }; |
157 | |
158 | static 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 | |
207 | static 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 | |
221 | static 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 | |
230 | static struct miscdevice orion_wdt_miscdev = { |
231 | .minor = WATCHDOG_MINOR, |
232 | .name = "watchdog", |
233 | .fops = &orion_wdt_fops, |
234 | }; |
235 | |
236 | static 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 | |
265 | static 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 | |
281 | static void orion_wdt_shutdown(struct platform_device *pdev) |
282 | { |
283 | if (test_bit(WDT_IN_USE, &wdt_status)) |
284 | orion_wdt_disable(); |
285 | } |
286 | |
287 | static 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 | |
297 | static int __init orion_wdt_init(void) |
298 | { |
299 | spin_lock_init(&wdt_lock); |
300 | return platform_driver_register(&orion_wdt_driver); |
301 | } |
302 | |
303 | static void __exit orion_wdt_exit(void) |
304 | { |
305 | platform_driver_unregister(&orion_wdt_driver); |
306 | } |
307 | |
308 | module_init(orion_wdt_init); |
309 | module_exit(orion_wdt_exit); |
310 | |
311 | MODULE_AUTHOR("Sylver Bruneau <sylver.bruneau@googlemail.com>"); |
312 | MODULE_DESCRIPTION("Orion Processor Watchdog"); |
313 | |
314 | module_param(heartbeat, int, 0); |
315 | MODULE_PARM_DESC(heartbeat, "Initial watchdog heartbeat in seconds"); |
316 | |
317 | module_param(nowayout, int, 0); |
318 | MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" |
319 | __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); |
320 | |
321 | MODULE_LICENSE("GPL"); |
322 | MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); |
323 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9