Blame view
drivers/watchdog/at91rm9200_wdt.c
6.5 KB
853807fb5
|
1 2 3 4 5 6 7 8 9 10 |
/* * Watchdog driver for Atmel AT91RM9200 (Thunder) * * Copyright (C) 2003 SAN People (Pty) Ltd * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ |
1977f0327
|
11 |
#include <linux/bitops.h> |
853807fb5
|
12 13 14 |
#include <linux/errno.h> #include <linux/fs.h> #include <linux/init.h> |
02e0746ec
|
15 |
#include <linux/io.h> |
853807fb5
|
16 17 18 19 |
#include <linux/kernel.h> #include <linux/miscdevice.h> #include <linux/module.h> #include <linux/moduleparam.h> |
dfc7bd9c3
|
20 |
#include <linux/platform_device.h> |
853807fb5
|
21 22 |
#include <linux/types.h> #include <linux/watchdog.h> |
2760600da
|
23 |
#include <linux/uaccess.h> |
a09e64fbc
|
24 |
#include <mach/at91_st.h> |
853807fb5
|
25 |
|
dfc7bd9c3
|
26 27 |
#define WDT_DEFAULT_TIME 5 /* seconds */ #define WDT_MAX_TIME 256 /* seconds */ |
853807fb5
|
28 29 30 31 32 |
static int wdt_time = WDT_DEFAULT_TIME; static int nowayout = WATCHDOG_NOWAYOUT; module_param(wdt_time, int, 0); |
2760600da
|
33 34 |
MODULE_PARM_DESC(wdt_time, "Watchdog time in seconds. (default=" __MODULE_STRING(WDT_DEFAULT_TIME) ")"); |
853807fb5
|
35 |
|
dfc7bd9c3
|
36 |
#ifdef CONFIG_WATCHDOG_NOWAYOUT |
853807fb5
|
37 |
module_param(nowayout, int, 0); |
2760600da
|
38 39 40 |
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); |
dfc7bd9c3
|
41 |
#endif |
853807fb5
|
42 43 44 45 46 47 48 49 50 |
static unsigned long at91wdt_busy; /* ......................................................................... */ /* * Disable the watchdog. */ |
2760600da
|
51 |
static inline void at91_wdt_stop(void) |
853807fb5
|
52 53 54 55 56 57 58 |
{ at91_sys_write(AT91_ST_WDMR, AT91_ST_EXTEN); } /* * Enable and reset the watchdog. */ |
2760600da
|
59 |
static inline void at91_wdt_start(void) |
853807fb5
|
60 |
{ |
2760600da
|
61 62 |
at91_sys_write(AT91_ST_WDMR, AT91_ST_EXTEN | AT91_ST_RSTEN | (((65536 * wdt_time) >> 8) & AT91_ST_WDV)); |
853807fb5
|
63 64 65 66 67 68 |
at91_sys_write(AT91_ST_CR, AT91_ST_WDRST); } /* * Reload the watchdog timer. (ie, pat the watchdog) */ |
2760600da
|
69 |
static inline void at91_wdt_reload(void) |
853807fb5
|
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
{ at91_sys_write(AT91_ST_CR, AT91_ST_WDRST); } /* ......................................................................... */ /* * Watchdog device is opened, and watchdog starts running. */ static int at91_wdt_open(struct inode *inode, struct file *file) { if (test_and_set_bit(0, &at91wdt_busy)) return -EBUSY; at91_wdt_start(); return nonseekable_open(inode, file); } /* * Close the watchdog device. * If CONFIG_WATCHDOG_NOWAYOUT is NOT defined then the watchdog is also * disabled. */ static int at91_wdt_close(struct inode *inode, struct file *file) { |
2760600da
|
95 |
/* Disable the watchdog when file is closed */ |
853807fb5
|
96 |
if (!nowayout) |
2760600da
|
97 |
at91_wdt_stop(); |
853807fb5
|
98 99 100 101 102 103 104 105 106 107 108 |
clear_bit(0, &at91wdt_busy); return 0; } /* * Change the watchdog time interval. */ static int at91_wdt_settimeout(int new_time) { /* |
2af29b786
|
109 |
* All counting occurs at SLOW_CLOCK / 128 = 256 Hz |
853807fb5
|
110 111 |
* * Since WDV is a 16-bit counter, the maximum period is |
2af29b786
|
112 |
* 65536 / 256 = 256 seconds. |
853807fb5
|
113 114 115 |
*/ if ((new_time <= 0) || (new_time > WDT_MAX_TIME)) return -EINVAL; |
2760600da
|
116 117 |
/* Set new watchdog time. It will be used when at91_wdt_start() is called. */ |
853807fb5
|
118 119 120 |
wdt_time = new_time; return 0; } |
42747d712
|
121 |
static const struct watchdog_info at91_wdt_info = { |
853807fb5
|
122 123 124 125 126 127 128 |
.identity = "at91 watchdog", .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, }; /* * Handle commands from user-space. */ |
3c4fafd65
|
129 |
static long at91_wdt_ioctl(struct file *file, |
2760600da
|
130 |
unsigned int cmd, unsigned long arg) |
853807fb5
|
131 132 133 134 |
{ void __user *argp = (void __user *)arg; int __user *p = argp; int new_value; |
2760600da
|
135 |
switch (cmd) { |
2760600da
|
136 137 138 |
case WDIOC_GETSUPPORT: return copy_to_user(argp, &at91_wdt_info, sizeof(at91_wdt_info)) ? -EFAULT : 0; |
2760600da
|
139 140 141 142 143 144 145 146 147 |
case WDIOC_GETSTATUS: case WDIOC_GETBOOTSTATUS: return put_user(0, p); case WDIOC_SETOPTIONS: if (get_user(new_value, p)) return -EFAULT; if (new_value & WDIOS_DISABLECARD) at91_wdt_stop(); if (new_value & WDIOS_ENABLECARD) |
853807fb5
|
148 |
at91_wdt_start(); |
2760600da
|
149 |
return 0; |
0c06090c9
|
150 151 152 153 154 155 156 157 158 159 160 161 162 163 |
case WDIOC_KEEPALIVE: at91_wdt_reload(); /* pat the watchdog */ return 0; case WDIOC_SETTIMEOUT: if (get_user(new_value, p)) return -EFAULT; if (at91_wdt_settimeout(new_value)) return -EINVAL; /* Enable new time value */ at91_wdt_start(); /* Return current value */ return put_user(wdt_time, p); case WDIOC_GETTIMEOUT: return put_user(wdt_time, p); |
2760600da
|
164 165 |
default: return -ENOTTY; |
853807fb5
|
166 167 168 169 170 171 |
} } /* * Pat the watchdog whenever device is written to. */ |
2760600da
|
172 173 |
static ssize_t at91_wdt_write(struct file *file, const char *data, size_t len, loff_t *ppos) |
853807fb5
|
174 175 176 177 178 179 |
{ at91_wdt_reload(); /* pat the watchdog */ return len; } /* ......................................................................... */ |
62322d255
|
180 |
static const struct file_operations at91wdt_fops = { |
853807fb5
|
181 182 |
.owner = THIS_MODULE, .llseek = no_llseek, |
2760600da
|
183 |
.unlocked_ioctl = at91_wdt_ioctl, |
853807fb5
|
184 185 186 187 188 189 190 191 192 193 |
.open = at91_wdt_open, .release = at91_wdt_close, .write = at91_wdt_write, }; static struct miscdevice at91wdt_miscdev = { .minor = WATCHDOG_MINOR, .name = "watchdog", .fops = &at91wdt_fops, }; |
47dec7c6c
|
194 |
static int __devinit at91wdt_probe(struct platform_device *pdev) |
853807fb5
|
195 196 |
{ int res; |
e0b79e0bc
|
197 |
if (at91wdt_miscdev.parent) |
dfc7bd9c3
|
198 |
return -EBUSY; |
e0b79e0bc
|
199 |
at91wdt_miscdev.parent = &pdev->dev; |
853807fb5
|
200 201 202 203 |
res = misc_register(&at91wdt_miscdev); if (res) return res; |
2760600da
|
204 205 206 |
printk(KERN_INFO "AT91 Watchdog Timer enabled (%d seconds%s) ", wdt_time, nowayout ? ", nowayout" : ""); |
853807fb5
|
207 208 |
return 0; } |
47dec7c6c
|
209 |
static int __devexit at91wdt_remove(struct platform_device *pdev) |
dfc7bd9c3
|
210 211 212 213 214 |
{ int res; res = misc_deregister(&at91wdt_miscdev); if (!res) |
e0b79e0bc
|
215 |
at91wdt_miscdev.parent = NULL; |
dfc7bd9c3
|
216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 |
return res; } static void at91wdt_shutdown(struct platform_device *pdev) { at91_wdt_stop(); } #ifdef CONFIG_PM static int at91wdt_suspend(struct platform_device *pdev, pm_message_t message) { at91_wdt_stop(); return 0; } static int at91wdt_resume(struct platform_device *pdev) { if (at91wdt_busy) at91_wdt_start(); |
95f62bdc5
|
237 |
return 0; |
dfc7bd9c3
|
238 239 240 241 242 243 244 245 246 |
} #else #define at91wdt_suspend NULL #define at91wdt_resume NULL #endif static struct platform_driver at91wdt_driver = { .probe = at91wdt_probe, |
47dec7c6c
|
247 |
.remove = __devexit_p(at91wdt_remove), |
dfc7bd9c3
|
248 249 250 251 252 253 254 255 256 257 258 |
.shutdown = at91wdt_shutdown, .suspend = at91wdt_suspend, .resume = at91wdt_resume, .driver = { .name = "at91_wdt", .owner = THIS_MODULE, }, }; static int __init at91_wdt_init(void) { |
2760600da
|
259 260 |
/* Check that the heartbeat value is within range; if not reset to the default */ |
dfc7bd9c3
|
261 262 |
if (at91_wdt_settimeout(wdt_time)) { at91_wdt_settimeout(WDT_DEFAULT_TIME); |
a77dba7e4
|
263 264 265 |
pr_info("at91_wdt: wdt_time value must be 1 <= wdt_time <= 256" ", using %d ", wdt_time); |
dfc7bd9c3
|
266 267 268 269 |
} return platform_driver_register(&at91wdt_driver); } |
853807fb5
|
270 271 |
static void __exit at91_wdt_exit(void) { |
dfc7bd9c3
|
272 |
platform_driver_unregister(&at91wdt_driver); |
853807fb5
|
273 274 275 276 277 278 279 280 281 |
} module_init(at91_wdt_init); module_exit(at91_wdt_exit); MODULE_AUTHOR("Andrew Victor"); MODULE_DESCRIPTION("Watchdog driver for Atmel AT91RM9200"); MODULE_LICENSE("GPL"); MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); |
f37d193c7
|
282 |
MODULE_ALIAS("platform:at91_wdt"); |