1/* 2 * Watchdog driver for Atmel AT91SAM9x processors. 3 * 4 * Copyright (C) 2008 Renaud CERRATO r.cerrato@til-technologies.fr 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/* 13 * The Watchdog Timer Mode Register can be only written to once. If the 14 * timeout need to be set from Linux, be sure that the bootstrap or the 15 * bootloader doesn't write to this register. 16 */ 17 18#include <linux/errno.h> 19#include <linux/fs.h> 20#include <linux/init.h> 21#include <linux/io.h> 22#include <linux/kernel.h> 23#include <linux/miscdevice.h> 24#include <linux/module.h> 25#include <linux/moduleparam.h> 26#include <linux/platform_device.h> 27#include <linux/types.h> 28#include <linux/watchdog.h> 29#include <linux/jiffies.h> 30#include <linux/timer.h> 31#include <linux/bitops.h> 32#include <linux/uaccess.h> 33 34#include "at91sam9_wdt.h" 35 36#define DRV_NAME "AT91SAM9 Watchdog" 37 38#define wdt_read(field) \ 39 __raw_readl(at91wdt_private.base + field) 40#define wdt_write(field, val) \ 41 __raw_writel((val), at91wdt_private.base + field) 42 43/* AT91SAM9 watchdog runs a 12bit counter @ 256Hz, 44 * use this to convert a watchdog 45 * value from/to milliseconds. 46 */ 47#define ms_to_ticks(t) (((t << 8) / 1000) - 1) 48#define ticks_to_ms(t) (((t + 1) * 1000) >> 8) 49 50/* Hardware timeout in seconds */ 51#define WDT_HW_TIMEOUT 2 52 53/* Timer heartbeat (500ms) */ 54#define WDT_TIMEOUT (HZ/2) 55 56/* User land timeout */ 57#define WDT_HEARTBEAT 15 58static int heartbeat = WDT_HEARTBEAT; 59module_param(heartbeat, int, 0); 60MODULE_PARM_DESC(heartbeat, "Watchdog heartbeats in seconds. " 61 "(default = " __MODULE_STRING(WDT_HEARTBEAT) ")"); 62 63static int nowayout = WATCHDOG_NOWAYOUT; 64module_param(nowayout, int, 0); 65MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started " 66 "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 67 68static void at91_ping(unsigned long data); 69 70static struct { 71 void __iomem *base; 72 unsigned long next_heartbeat; /* the next_heartbeat for the timer */ 73 unsigned long open; 74 char expect_close; 75 struct timer_list timer; /* The timer that pings the watchdog */ 76} at91wdt_private; 77 78/* ......................................................................... */ 79 80 81/* 82 * Reload the watchdog timer. (ie, pat the watchdog) 83 */ 84static inline void at91_wdt_reset(void) 85{ 86 wdt_write(AT91_WDT_CR, AT91_WDT_KEY | AT91_WDT_WDRSTT); 87} 88 89/* 90 * Timer tick 91 */ 92static void at91_ping(unsigned long data) 93{ 94 if (time_before(jiffies, at91wdt_private.next_heartbeat) || 95 (!nowayout && !at91wdt_private.open)) { 96 at91_wdt_reset(); 97 mod_timer(&at91wdt_private.timer, jiffies + WDT_TIMEOUT); 98 } else 99 printk(KERN_CRIT DRV_NAME": I will reset your machine !\n"); 100} 101 102/* 103 * Watchdog device is opened, and watchdog starts running. 104 */ 105static int at91_wdt_open(struct inode *inode, struct file *file) 106{ 107 if (test_and_set_bit(0, &at91wdt_private.open)) 108 return -EBUSY; 109 110 at91wdt_private.next_heartbeat = jiffies + heartbeat * HZ; 111 mod_timer(&at91wdt_private.timer, jiffies + WDT_TIMEOUT); 112 113 return nonseekable_open(inode, file); 114} 115 116/* 117 * Close the watchdog device. 118 */ 119static int at91_wdt_close(struct inode *inode, struct file *file) 120{ 121 clear_bit(0, &at91wdt_private.open); 122 123 /* stop internal ping */ 124 if (!at91wdt_private.expect_close) 125 del_timer(&at91wdt_private.timer); 126 127 at91wdt_private.expect_close = 0; 128 return 0; 129} 130 131/* 132 * Set the watchdog time interval in 1/256Hz (write-once) 133 * Counter is 12 bit. 134 */ 135static int at91_wdt_settimeout(unsigned int timeout) 136{ 137 unsigned int reg; 138 unsigned int mr; 139 140 /* Check if disabled */ 141 mr = wdt_read(AT91_WDT_MR); 142 if (mr & AT91_WDT_WDDIS) { 143 printk(KERN_ERR DRV_NAME": sorry, watchdog is disabled\n"); 144 return -EIO; 145 } 146 147 /* 148 * All counting occurs at SLOW_CLOCK / 128 = 256 Hz 149 * 150 * Since WDV is a 12-bit counter, the maximum period is 151 * 4096 / 256 = 16 seconds. 152 */ 153 reg = AT91_WDT_WDRSTEN /* causes watchdog reset */ 154 /* | AT91_WDT_WDRPROC causes processor reset only */ 155 | AT91_WDT_WDDBGHLT /* disabled in debug mode */ 156 | AT91_WDT_WDD /* restart at any time */ 157 | (timeout & AT91_WDT_WDV); /* timer value */ 158 wdt_write(AT91_WDT_MR, reg); 159 160 return 0; 161} 162 163static const struct watchdog_info at91_wdt_info = { 164 .identity = DRV_NAME, 165 .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | 166 WDIOF_MAGICCLOSE, 167}; 168 169/* 170 * Handle commands from user-space. 171 */ 172static long at91_wdt_ioctl(struct file *file, 173 unsigned int cmd, unsigned long arg) 174{ 175 void __user *argp = (void __user *)arg; 176 int __user *p = argp; 177 int new_value; 178 179 switch (cmd) { 180 case WDIOC_GETSUPPORT: 181 return copy_to_user(argp, &at91_wdt_info, 182 sizeof(at91_wdt_info)) ? -EFAULT : 0; 183 184 case WDIOC_GETSTATUS: 185 case WDIOC_GETBOOTSTATUS: 186 return put_user(0, p); 187 188 case WDIOC_KEEPALIVE: 189 at91wdt_private.next_heartbeat = jiffies + heartbeat * HZ; 190 return 0; 191 192 case WDIOC_SETTIMEOUT: 193 if (get_user(new_value, p)) 194 return -EFAULT; 195 196 heartbeat = new_value; 197 at91wdt_private.next_heartbeat = jiffies + heartbeat * HZ; 198 199 return put_user(new_value, p); /* return current value */ 200 201 case WDIOC_GETTIMEOUT: 202 return put_user(heartbeat, p); 203 } 204 return -ENOTTY; 205} 206 207/* 208 * Pat the watchdog whenever device is written to. 209 */ 210static ssize_t at91_wdt_write(struct file *file, const char *data, size_t len, 211 loff_t *ppos) 212{ 213 if (!len) 214 return 0; 215 216 /* Scan for magic character */ 217 if (!nowayout) { 218 size_t i; 219 220 at91wdt_private.expect_close = 0; 221 222 for (i = 0; i < len; i++) { 223 char c; 224 if (get_user(c, data + i)) 225 return -EFAULT; 226 if (c == 'V') { 227 at91wdt_private.expect_close = 42; 228 break; 229 } 230 } 231 } 232 233 at91wdt_private.next_heartbeat = jiffies + heartbeat * HZ; 234 235 return len; 236} 237 238/* ......................................................................... */ 239 240static const struct file_operations at91wdt_fops = { 241 .owner = THIS_MODULE, 242 .llseek = no_llseek, 243 .unlocked_ioctl = at91_wdt_ioctl, 244 .open = at91_wdt_open, 245 .release = at91_wdt_close, 246 .write = at91_wdt_write, 247}; 248 249static struct miscdevice at91wdt_miscdev = { 250 .minor = WATCHDOG_MINOR, 251 .name = "watchdog", 252 .fops = &at91wdt_fops, 253}; 254 255static int __init at91wdt_probe(struct platform_device *pdev) 256{ 257 struct resource *r; 258 int res; 259 260 if (at91wdt_miscdev.parent) 261 return -EBUSY; 262 at91wdt_miscdev.parent = &pdev->dev; 263 264 r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 265 if (!r) 266 return -ENODEV; 267 at91wdt_private.base = ioremap(r->start, resource_size(r)); 268 if (!at91wdt_private.base) { 269 dev_err(&pdev->dev, "failed to map registers, aborting.\n"); 270 return -ENOMEM; 271 } 272 273 /* Set watchdog */ 274 res = at91_wdt_settimeout(ms_to_ticks(WDT_HW_TIMEOUT * 1000)); 275 if (res) 276 return res; 277 278 res = misc_register(&at91wdt_miscdev); 279 if (res) 280 return res; 281 282 at91wdt_private.next_heartbeat = jiffies + heartbeat * HZ; 283 setup_timer(&at91wdt_private.timer, at91_ping, 0); 284 mod_timer(&at91wdt_private.timer, jiffies + WDT_TIMEOUT); 285 286 printk(KERN_INFO DRV_NAME " enabled (heartbeat=%d sec, nowayout=%d)\n", 287 heartbeat, nowayout); 288 289 return 0; 290} 291 292static int __exit at91wdt_remove(struct platform_device *pdev) 293{ 294 int res; 295 296 res = misc_deregister(&at91wdt_miscdev); 297 if (!res) 298 at91wdt_miscdev.parent = NULL; 299 300 return res; 301} 302 303static struct platform_driver at91wdt_driver = { 304 .remove = __exit_p(at91wdt_remove), 305 .driver = { 306 .name = "at91_wdt", 307 .owner = THIS_MODULE, 308 }, 309}; 310 311static int __init at91sam_wdt_init(void) 312{ 313 return platform_driver_probe(&at91wdt_driver, at91wdt_probe); 314} 315 316static void __exit at91sam_wdt_exit(void) 317{ 318 platform_driver_unregister(&at91wdt_driver); 319} 320 321module_init(at91sam_wdt_init); 322module_exit(at91sam_wdt_exit); 323 324MODULE_AUTHOR("Renaud CERRATO <r.cerrato@til-technologies.fr>"); 325MODULE_DESCRIPTION("Watchdog driver for Atmel AT91SAM9x processors"); 326MODULE_LICENSE("GPL"); 327MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); 328