diff options
author | Linus Walleij <linus.walleij@linaro.org> | 2012-03-16 04:14:12 -0400 |
---|---|---|
committer | Wim Van Sebroeck <wim@iguana.be> | 2012-03-27 14:15:47 -0400 |
commit | 15b25701b282bd761a82c6508529b2145744075f (patch) | |
tree | 413baca371a55476280860a56bd80f5b53e03991 /drivers/watchdog/coh901327_wdt.c | |
parent | fd7b673c92731fc6c0b1e999adfd87b6762ee797 (diff) |
watchdog: coh901327: convert to use watchdog core
This converts the COH901327 watchdog to use the watchdog core.
I followed Wolframs document, looked at some other drivers and
tested it on the U300.
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Cc: Wolfram Sang <w.sang@pengutronix.de>
Signed-off-by: Wim Van Sebroeck <wim@iguana.be>
Diffstat (limited to 'drivers/watchdog/coh901327_wdt.c')
-rw-r--r-- | drivers/watchdog/coh901327_wdt.c | 192 |
1 files changed, 55 insertions, 137 deletions
diff --git a/drivers/watchdog/coh901327_wdt.c b/drivers/watchdog/coh901327_wdt.c index 5b89f7d6cd0f..7f0cbeb58770 100644 --- a/drivers/watchdog/coh901327_wdt.c +++ b/drivers/watchdog/coh901327_wdt.c | |||
@@ -8,17 +8,15 @@ | |||
8 | */ | 8 | */ |
9 | #include <linux/module.h> | 9 | #include <linux/module.h> |
10 | #include <linux/types.h> | 10 | #include <linux/types.h> |
11 | #include <linux/fs.h> | ||
12 | #include <linux/miscdevice.h> | ||
13 | #include <linux/watchdog.h> | 11 | #include <linux/watchdog.h> |
14 | #include <linux/interrupt.h> | 12 | #include <linux/interrupt.h> |
15 | #include <linux/pm.h> | 13 | #include <linux/pm.h> |
16 | #include <linux/platform_device.h> | 14 | #include <linux/platform_device.h> |
17 | #include <linux/io.h> | 15 | #include <linux/io.h> |
18 | #include <linux/bitops.h> | 16 | #include <linux/bitops.h> |
19 | #include <linux/uaccess.h> | ||
20 | #include <linux/clk.h> | 17 | #include <linux/clk.h> |
21 | #include <linux/delay.h> | 18 | #include <linux/delay.h> |
19 | #include <linux/err.h> | ||
22 | 20 | ||
23 | #define DRV_NAME "WDOG COH 901 327" | 21 | #define DRV_NAME "WDOG COH 901 327" |
24 | 22 | ||
@@ -74,8 +72,6 @@ static resource_size_t phybase; | |||
74 | static resource_size_t physize; | 72 | static resource_size_t physize; |
75 | static int irq; | 73 | static int irq; |
76 | static void __iomem *virtbase; | 74 | static void __iomem *virtbase; |
77 | static unsigned long coh901327_users; | ||
78 | static unsigned long boot_status; | ||
79 | static struct device *parent; | 75 | static struct device *parent; |
80 | 76 | ||
81 | /* | 77 | /* |
@@ -155,30 +151,31 @@ static void coh901327_disable(void) | |||
155 | __func__, val); | 151 | __func__, val); |
156 | } | 152 | } |
157 | 153 | ||
158 | static void coh901327_start(void) | 154 | static int coh901327_start(struct watchdog_device *wdt_dev) |
159 | { | 155 | { |
160 | coh901327_enable(margin * 100); | 156 | coh901327_enable(margin * 100); |
157 | return 0; | ||
158 | } | ||
159 | |||
160 | static int coh901327_stop(struct watchdog_device *wdt_dev) | ||
161 | { | ||
162 | coh901327_disable(); | ||
163 | return 0; | ||
161 | } | 164 | } |
162 | 165 | ||
163 | static void coh901327_keepalive(void) | 166 | static int coh901327_ping(struct watchdog_device *wdd) |
164 | { | 167 | { |
165 | clk_enable(clk); | 168 | clk_enable(clk); |
166 | /* Feed the watchdog */ | 169 | /* Feed the watchdog */ |
167 | writew(U300_WDOG_FR_FEED_RESTART_TIMER, | 170 | writew(U300_WDOG_FR_FEED_RESTART_TIMER, |
168 | virtbase + U300_WDOG_FR); | 171 | virtbase + U300_WDOG_FR); |
169 | clk_disable(clk); | 172 | clk_disable(clk); |
173 | return 0; | ||
170 | } | 174 | } |
171 | 175 | ||
172 | static int coh901327_settimeout(int time) | 176 | static int coh901327_settimeout(struct watchdog_device *wdt_dev, |
177 | unsigned int time) | ||
173 | { | 178 | { |
174 | /* | ||
175 | * Max margin is 327 since the 10ms | ||
176 | * timeout register is max | ||
177 | * 0x7FFF = 327670ms ~= 327s. | ||
178 | */ | ||
179 | if (time <= 0 || time > 327) | ||
180 | return -EINVAL; | ||
181 | |||
182 | margin = time; | 179 | margin = time; |
183 | clk_enable(clk); | 180 | clk_enable(clk); |
184 | /* Set new timeout value */ | 181 | /* Set new timeout value */ |
@@ -190,6 +187,23 @@ static int coh901327_settimeout(int time) | |||
190 | return 0; | 187 | return 0; |
191 | } | 188 | } |
192 | 189 | ||
190 | static unsigned int coh901327_gettimeleft(struct watchdog_device *wdt_dev) | ||
191 | { | ||
192 | u16 val; | ||
193 | |||
194 | clk_enable(clk); | ||
195 | /* Read repeatedly until the value is stable! */ | ||
196 | val = readw(virtbase + U300_WDOG_CR); | ||
197 | while (val & U300_WDOG_CR_VALID_IND) | ||
198 | val = readw(virtbase + U300_WDOG_CR); | ||
199 | val &= U300_WDOG_CR_COUNT_VALUE_MASK; | ||
200 | clk_disable(clk); | ||
201 | if (val != 0) | ||
202 | val /= 100; | ||
203 | |||
204 | return val; | ||
205 | } | ||
206 | |||
193 | /* | 207 | /* |
194 | * This interrupt occurs 10 ms before the watchdog WILL bark. | 208 | * This interrupt occurs 10 ms before the watchdog WILL bark. |
195 | */ | 209 | */ |
@@ -218,130 +232,35 @@ static irqreturn_t coh901327_interrupt(int irq, void *data) | |||
218 | return IRQ_HANDLED; | 232 | return IRQ_HANDLED; |
219 | } | 233 | } |
220 | 234 | ||
221 | /* | 235 | static const struct watchdog_info coh901327_ident = { |
222 | * Allow only one user (daemon) to open the watchdog | 236 | .options = WDIOF_CARDRESET | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, |
223 | */ | 237 | .identity = DRV_NAME, |
224 | static int coh901327_open(struct inode *inode, struct file *file) | 238 | }; |
225 | { | ||
226 | if (test_and_set_bit(1, &coh901327_users)) | ||
227 | return -EBUSY; | ||
228 | coh901327_start(); | ||
229 | return nonseekable_open(inode, file); | ||
230 | } | ||
231 | |||
232 | static int coh901327_release(struct inode *inode, struct file *file) | ||
233 | { | ||
234 | clear_bit(1, &coh901327_users); | ||
235 | coh901327_disable(); | ||
236 | return 0; | ||
237 | } | ||
238 | |||
239 | static ssize_t coh901327_write(struct file *file, const char __user *data, | ||
240 | size_t len, loff_t *ppos) | ||
241 | { | ||
242 | if (len) | ||
243 | coh901327_keepalive(); | ||
244 | return len; | ||
245 | } | ||
246 | |||
247 | static long coh901327_ioctl(struct file *file, unsigned int cmd, | ||
248 | unsigned long arg) | ||
249 | { | ||
250 | int ret = -ENOTTY; | ||
251 | u16 val; | ||
252 | int time; | ||
253 | int new_options; | ||
254 | union { | ||
255 | struct watchdog_info __user *ident; | ||
256 | int __user *i; | ||
257 | } uarg; | ||
258 | static const struct watchdog_info ident = { | ||
259 | .options = WDIOF_CARDRESET | | ||
260 | WDIOF_SETTIMEOUT | | ||
261 | WDIOF_KEEPALIVEPING, | ||
262 | .identity = "COH 901 327 Watchdog", | ||
263 | .firmware_version = 1, | ||
264 | }; | ||
265 | uarg.i = (int __user *)arg; | ||
266 | |||
267 | switch (cmd) { | ||
268 | case WDIOC_GETSUPPORT: | ||
269 | ret = copy_to_user(uarg.ident, &ident, | ||
270 | sizeof(ident)) ? -EFAULT : 0; | ||
271 | break; | ||
272 | |||
273 | case WDIOC_GETSTATUS: | ||
274 | ret = put_user(0, uarg.i); | ||
275 | break; | ||
276 | |||
277 | case WDIOC_GETBOOTSTATUS: | ||
278 | ret = put_user(boot_status, uarg.i); | ||
279 | break; | ||
280 | |||
281 | case WDIOC_SETOPTIONS: | ||
282 | ret = get_user(new_options, uarg.i); | ||
283 | if (ret) | ||
284 | break; | ||
285 | if (new_options & WDIOS_DISABLECARD) | ||
286 | coh901327_disable(); | ||
287 | if (new_options & WDIOS_ENABLECARD) | ||
288 | coh901327_start(); | ||
289 | ret = 0; | ||
290 | break; | ||
291 | |||
292 | case WDIOC_KEEPALIVE: | ||
293 | coh901327_keepalive(); | ||
294 | ret = 0; | ||
295 | break; | ||
296 | |||
297 | case WDIOC_SETTIMEOUT: | ||
298 | ret = get_user(time, uarg.i); | ||
299 | if (ret) | ||
300 | break; | ||
301 | |||
302 | ret = coh901327_settimeout(time); | ||
303 | if (ret) | ||
304 | break; | ||
305 | /* Then fall through to return set value */ | ||
306 | |||
307 | case WDIOC_GETTIMEOUT: | ||
308 | ret = put_user(margin, uarg.i); | ||
309 | break; | ||
310 | |||
311 | case WDIOC_GETTIMELEFT: | ||
312 | clk_enable(clk); | ||
313 | /* Read repeatedly until the value is stable! */ | ||
314 | val = readw(virtbase + U300_WDOG_CR); | ||
315 | while (val & U300_WDOG_CR_VALID_IND) | ||
316 | val = readw(virtbase + U300_WDOG_CR); | ||
317 | val &= U300_WDOG_CR_COUNT_VALUE_MASK; | ||
318 | clk_disable(clk); | ||
319 | if (val != 0) | ||
320 | val /= 100; | ||
321 | ret = put_user(val, uarg.i); | ||
322 | break; | ||
323 | } | ||
324 | return ret; | ||
325 | } | ||
326 | 239 | ||
327 | static const struct file_operations coh901327_fops = { | 240 | static struct watchdog_ops coh901327_ops = { |
328 | .owner = THIS_MODULE, | 241 | .owner = THIS_MODULE, |
329 | .llseek = no_llseek, | 242 | .start = coh901327_start, |
330 | .write = coh901327_write, | 243 | .stop = coh901327_stop, |
331 | .unlocked_ioctl = coh901327_ioctl, | 244 | .ping = coh901327_ping, |
332 | .open = coh901327_open, | 245 | .set_timeout = coh901327_settimeout, |
333 | .release = coh901327_release, | 246 | .get_timeleft = coh901327_gettimeleft, |
334 | }; | 247 | }; |
335 | 248 | ||
336 | static struct miscdevice coh901327_miscdev = { | 249 | static struct watchdog_device coh901327_wdt = { |
337 | .minor = WATCHDOG_MINOR, | 250 | .info = &coh901327_ident, |
338 | .name = "watchdog", | 251 | .ops = &coh901327_ops, |
339 | .fops = &coh901327_fops, | 252 | /* |
253 | * Max margin is 327 since the 10ms | ||
254 | * timeout register is max | ||
255 | * 0x7FFF = 327670ms ~= 327s. | ||
256 | */ | ||
257 | .min_timeout = 0, | ||
258 | .max_timeout = 327, | ||
340 | }; | 259 | }; |
341 | 260 | ||
342 | static int __exit coh901327_remove(struct platform_device *pdev) | 261 | static int __exit coh901327_remove(struct platform_device *pdev) |
343 | { | 262 | { |
344 | misc_deregister(&coh901327_miscdev); | 263 | watchdog_unregister_device(&coh901327_wdt); |
345 | coh901327_disable(); | 264 | coh901327_disable(); |
346 | free_irq(irq, pdev); | 265 | free_irq(irq, pdev); |
347 | clk_put(clk); | 266 | clk_put(clk); |
@@ -350,7 +269,6 @@ static int __exit coh901327_remove(struct platform_device *pdev) | |||
350 | return 0; | 269 | return 0; |
351 | } | 270 | } |
352 | 271 | ||
353 | |||
354 | static int __init coh901327_probe(struct platform_device *pdev) | 272 | static int __init coh901327_probe(struct platform_device *pdev) |
355 | { | 273 | { |
356 | int ret; | 274 | int ret; |
@@ -393,7 +311,7 @@ static int __init coh901327_probe(struct platform_device *pdev) | |||
393 | case U300_WDOG_SR_STATUS_TIMED_OUT: | 311 | case U300_WDOG_SR_STATUS_TIMED_OUT: |
394 | dev_info(&pdev->dev, | 312 | dev_info(&pdev->dev, |
395 | "watchdog timed out since last chip reset!\n"); | 313 | "watchdog timed out since last chip reset!\n"); |
396 | boot_status = WDIOF_CARDRESET; | 314 | coh901327_wdt.bootstatus |= WDIOF_CARDRESET; |
397 | /* Status will be cleared below */ | 315 | /* Status will be cleared below */ |
398 | break; | 316 | break; |
399 | case U300_WDOG_SR_STATUS_NORMAL: | 317 | case U300_WDOG_SR_STATUS_NORMAL: |
@@ -435,7 +353,7 @@ static int __init coh901327_probe(struct platform_device *pdev) | |||
435 | 353 | ||
436 | clk_disable(clk); | 354 | clk_disable(clk); |
437 | 355 | ||
438 | ret = misc_register(&coh901327_miscdev); | 356 | ret = watchdog_register_device(&coh901327_wdt); |
439 | if (ret == 0) | 357 | if (ret == 0) |
440 | dev_info(&pdev->dev, | 358 | dev_info(&pdev->dev, |
441 | "initialized. timer margin=%d sec\n", margin); | 359 | "initialized. timer margin=%d sec\n", margin); |
@@ -547,4 +465,4 @@ module_param(margin, int, 0); | |||
547 | MODULE_PARM_DESC(margin, "Watchdog margin in seconds (default 60s)"); | 465 | MODULE_PARM_DESC(margin, "Watchdog margin in seconds (default 60s)"); |
548 | 466 | ||
549 | MODULE_LICENSE("GPL"); | 467 | MODULE_LICENSE("GPL"); |
550 | MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); | 468 | MODULE_ALIAS("platform:coh901327-watchdog"); |