[WATCHDOG] at91_wdt.c - Atmel AT91RM9200 watchdog driver

Watchdog driver for the Atmel AT91RM9200 processor.

Signed-off-by: Andrew Victor <andrew@sanpeople.com>
Signed-off-by: Wim Van Sebroeck <wim@iguana.be>
Signed-off-by: Andrew Morton <akpm@osdl.org>

authored by Andrew Victor and committed by Wim Van Sebroeck 853807fb c9d1a0b8

+236
+7
drivers/char/watchdog/Kconfig
··· 60 60 61 61 # ARM Architecture 62 62 63 + config AT91_WATCHDOG 64 + tristate "AT91RM9200 watchdog" 65 + depends on WATCHDOG && ARCH_AT91RM9200 66 + help 67 + Watchdog timer embedded into AT91RM9200 chips. This will reboot your 68 + system when the timeout is reached. 69 + 63 70 config 21285_WATCHDOG 64 71 tristate "DC21285 watchdog" 65 72 depends on WATCHDOG && FOOTBRIDGE
+1
drivers/char/watchdog/Makefile
··· 23 23 obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb.o 24 24 25 25 # ARM Architecture 26 + obj-$(CONFIG_AT91_WATCHDOG) += at91_wdt.o 26 27 obj-$(CONFIG_21285_WATCHDOG) += wdt285.o 27 28 obj-$(CONFIG_977_WATCHDOG) += wdt977.o 28 29 obj-$(CONFIG_IXP2000_WATCHDOG) += ixp2000_wdt.o
+228
drivers/char/watchdog/at91_wdt.c
··· 1 + /* 2 + * Watchdog driver for Atmel AT91RM9200 (Thunder) 3 + * 4 + * Copyright (C) 2003 SAN People (Pty) Ltd 5 + * 6 + * This program is free software; you can redistribute it and/or 7 + * modify it under the terms of the GNU General Public License 8 + * as published by the Free Software Foundation; either version 9 + * 2 of the License, or (at your option) any later version. 10 + */ 11 + 12 + #include <linux/config.h> 13 + #include <linux/errno.h> 14 + #include <linux/fs.h> 15 + #include <linux/init.h> 16 + #include <linux/kernel.h> 17 + #include <linux/miscdevice.h> 18 + #include <linux/module.h> 19 + #include <linux/moduleparam.h> 20 + #include <linux/types.h> 21 + #include <linux/watchdog.h> 22 + #include <asm/bitops.h> 23 + #include <asm/uaccess.h> 24 + 25 + 26 + #define WDT_DEFAULT_TIME 5 /* 5 seconds */ 27 + #define WDT_MAX_TIME 256 /* 256 seconds */ 28 + 29 + static int wdt_time = WDT_DEFAULT_TIME; 30 + static int nowayout = WATCHDOG_NOWAYOUT; 31 + 32 + module_param(wdt_time, int, 0); 33 + MODULE_PARM_DESC(wdt_time, "Watchdog time in seconds. (default="__MODULE_STRING(WDT_DEFAULT_TIME) ")"); 34 + 35 + module_param(nowayout, int, 0); 36 + MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 37 + 38 + 39 + static unsigned long at91wdt_busy; 40 + 41 + /* ......................................................................... */ 42 + 43 + /* 44 + * Disable the watchdog. 45 + */ 46 + static void inline at91_wdt_stop(void) 47 + { 48 + at91_sys_write(AT91_ST_WDMR, AT91_ST_EXTEN); 49 + } 50 + 51 + /* 52 + * Enable and reset the watchdog. 53 + */ 54 + static void inline at91_wdt_start(void) 55 + { 56 + at91_sys_write(AT91_ST_WDMR, AT91_ST_EXTEN | AT91_ST_RSTEN | (((65536 * wdt_time) >> 8) & AT91_ST_WDV)); 57 + at91_sys_write(AT91_ST_CR, AT91_ST_WDRST); 58 + } 59 + 60 + /* 61 + * Reload the watchdog timer. (ie, pat the watchdog) 62 + */ 63 + static void inline at91_wdt_reload(void) 64 + { 65 + at91_sys_write(AT91_ST_CR, AT91_ST_WDRST); 66 + } 67 + 68 + /* ......................................................................... */ 69 + 70 + /* 71 + * Watchdog device is opened, and watchdog starts running. 72 + */ 73 + static int at91_wdt_open(struct inode *inode, struct file *file) 74 + { 75 + if (test_and_set_bit(0, &at91wdt_busy)) 76 + return -EBUSY; 77 + 78 + at91_wdt_start(); 79 + return nonseekable_open(inode, file); 80 + } 81 + 82 + /* 83 + * Close the watchdog device. 84 + * If CONFIG_WATCHDOG_NOWAYOUT is NOT defined then the watchdog is also 85 + * disabled. 86 + */ 87 + static int at91_wdt_close(struct inode *inode, struct file *file) 88 + { 89 + if (!nowayout) 90 + at91_wdt_stop(); /* Disable the watchdog when file is closed */ 91 + 92 + clear_bit(0, &at91wdt_busy); 93 + return 0; 94 + } 95 + 96 + /* 97 + * Change the watchdog time interval. 98 + */ 99 + static int at91_wdt_settimeout(int new_time) 100 + { 101 + /* 102 + * All counting occurs at SLOW_CLOCK / 128 = 0.256 Hz 103 + * 104 + * Since WDV is a 16-bit counter, the maximum period is 105 + * 65536 / 0.256 = 256 seconds. 106 + */ 107 + if ((new_time <= 0) || (new_time > WDT_MAX_TIME)) 108 + return -EINVAL; 109 + 110 + /* Set new watchdog time. It will be used when at91_wdt_start() is called. */ 111 + wdt_time = new_time; 112 + return 0; 113 + } 114 + 115 + static struct watchdog_info at91_wdt_info = { 116 + .identity = "at91 watchdog", 117 + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, 118 + }; 119 + 120 + /* 121 + * Handle commands from user-space. 122 + */ 123 + static int at91_wdt_ioctl(struct inode *inode, struct file *file, 124 + unsigned int cmd, unsigned long arg) 125 + { 126 + void __user *argp = (void __user *)arg; 127 + int __user *p = argp; 128 + int new_value; 129 + 130 + switch(cmd) { 131 + case WDIOC_KEEPALIVE: 132 + at91_wdt_reload(); /* pat the watchdog */ 133 + return 0; 134 + 135 + case WDIOC_GETSUPPORT: 136 + return copy_to_user(argp, &at91_wdt_info, sizeof(at91_wdt_info)) ? -EFAULT : 0; 137 + 138 + case WDIOC_SETTIMEOUT: 139 + if (get_user(new_value, p)) 140 + return -EFAULT; 141 + 142 + if (at91_wdt_settimeout(new_value)) 143 + return -EINVAL; 144 + 145 + /* Enable new time value */ 146 + at91_wdt_start(); 147 + 148 + /* Return current value */ 149 + return put_user(wdt_time, p); 150 + 151 + case WDIOC_GETTIMEOUT: 152 + return put_user(wdt_time, p); 153 + 154 + case WDIOC_GETSTATUS: 155 + case WDIOC_GETBOOTSTATUS: 156 + return put_user(0, p); 157 + 158 + case WDIOC_SETOPTIONS: 159 + if (get_user(new_value, p)) 160 + return -EFAULT; 161 + 162 + if (new_value & WDIOS_DISABLECARD) 163 + at91_wdt_stop(); 164 + if (new_value & WDIOS_ENABLECARD) 165 + at91_wdt_start(); 166 + return 0; 167 + 168 + default: 169 + return -ENOIOCTLCMD; 170 + } 171 + } 172 + 173 + /* 174 + * Pat the watchdog whenever device is written to. 175 + */ 176 + static ssize_t at91_wdt_write(struct file *file, const char *data, size_t len, loff_t *ppos) 177 + { 178 + at91_wdt_reload(); /* pat the watchdog */ 179 + return len; 180 + } 181 + 182 + /* ......................................................................... */ 183 + 184 + static struct file_operations at91wdt_fops = { 185 + .owner = THIS_MODULE, 186 + .llseek = no_llseek, 187 + .ioctl = at91_wdt_ioctl, 188 + .open = at91_wdt_open, 189 + .release = at91_wdt_close, 190 + .write = at91_wdt_write, 191 + }; 192 + 193 + static struct miscdevice at91wdt_miscdev = { 194 + .minor = WATCHDOG_MINOR, 195 + .name = "watchdog", 196 + .fops = &at91wdt_fops, 197 + }; 198 + 199 + static int __init at91_wdt_init(void) 200 + { 201 + int res; 202 + 203 + /* Check that the heartbeat value is within range; if not reset to the default */ 204 + if (at91_wdt_settimeout(wdt_time)) { 205 + at91_wdt_settimeout(WDT_DEFAULT_TIME); 206 + printk(KERN_INFO "at91_wdt: wdt_time value must be 1 <= wdt_time <= 256, using %d\n", wdt_time); 207 + } 208 + 209 + res = misc_register(&at91wdt_miscdev); 210 + if (res) 211 + return res; 212 + 213 + printk("AT91 Watchdog Timer enabled (%d seconds, nowayout=%d)\n", wdt_time, nowayout); 214 + return 0; 215 + } 216 + 217 + static void __exit at91_wdt_exit(void) 218 + { 219 + misc_deregister(&at91wdt_miscdev); 220 + } 221 + 222 + module_init(at91_wdt_init); 223 + module_exit(at91_wdt_exit); 224 + 225 + MODULE_AUTHOR("Andrew Victor"); 226 + MODULE_DESCRIPTION("Watchdog driver for Atmel AT91RM9200"); 227 + MODULE_LICENSE("GPL"); 228 + MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);