1/* 2 * ADM5120_WDT 0.01: Infineon ADM5120 SoC watchdog driver 3 * Copyright (c) Ondrej Zajicek <santiago@crfreenet.org>, 2007 4 * 5 * based on 6 * 7 * RC32434_WDT 0.01: IDT Interprise 79RC32434 watchdog driver 8 * 9 * This program is free software; you can redistribute it and/or 10 * modify it under the terms of the GNU General Public License 11 * as published by the Free Software Foundation; either version 12 * 2 of the License, or (at your option) any later version. 13 * 14 */ 15#include <linux/module.h> 16#include <linux/types.h> 17#include <linux/fs.h> 18#include <linux/miscdevice.h> 19#include <linux/watchdog.h> 20#include <linux/irq.h> 21 22#include <asm/bootinfo.h> 23 24#include <asm/mach-adm5120/adm5120_info.h> 25#include <asm/mach-adm5120/adm5120_defs.h> 26#include <asm/mach-adm5120/adm5120_switch.h> 27 28#define DEFAULT_TIMEOUT 15 /* (secs) Default is 15 seconds */ 29#define MAX_TIMEOUT 327 30/* Max is 327 seconds, counter is 15-bit integer, step is 10 ms */ 31 32#define NAME "adm5120_wdt" 33#define VERSION "0.1" 34 35static int expect_close; 36static int access; 37static unsigned int timeout = DEFAULT_TIMEOUT; 38 39static int nowayout = WATCHDOG_NOWAYOUT; 40module_param(nowayout, int, 0); 41MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 42MODULE_LICENSE("GPL"); 43 44 45static inline void wdt_set_timeout(void) 46{ 47 u32 val = (1 << 31) | (((timeout * 100) & 0x7FFF) << 16); 48 SW_WRITE_REG(SWITCH_REG_WDOG0, val); 49} 50 51/* 52 It looks like WDOG0-register-write don't modify counter, 53 but WDOG0-register-read resets counter. 54*/ 55 56static inline void wdt_reset_counter(void) 57{ 58 SW_READ_REG(SWITCH_REG_WDOG0); 59} 60 61static inline void wdt_disable(void) 62{ 63 SW_WRITE_REG(SWITCH_REG_WDOG0, 0x7FFF0000); 64} 65 66 67 68static int wdt_open(struct inode *inode, struct file *file) 69{ 70 /* Allow only one person to hold it open */ 71 if (access) 72 return -EBUSY; 73 74 if (nowayout) 75 __module_get(THIS_MODULE); 76 77 /* Activate timer */ 78 wdt_reset_counter(); 79 wdt_set_timeout(); 80 printk(KERN_INFO NAME ": enabling watchdog timer\n"); 81 access = 1; 82 return 0; 83} 84 85static int wdt_release(struct inode *inode, struct file *file) 86{ 87 /* 88 * Shut off the timer. 89 * Lock it in if it's a module and we set nowayout 90 */ 91 if (expect_close && (nowayout == 0)) { 92 wdt_disable(); 93 printk(KERN_INFO NAME ": disabling watchdog timer\n"); 94 module_put(THIS_MODULE); 95 } else 96 printk(KERN_CRIT NAME ": device closed unexpectedly. WDT will not stop!\n"); 97 98 access = 0; 99 return 0; 100} 101 102static ssize_t wdt_write(struct file *file, const char *data, size_t len, loff_t *ppos) 103{ 104 /* Refresh the timer. */ 105 if (len) { 106 if (!nowayout) { 107 size_t i; 108 109 /* In case it was set long ago */ 110 expect_close = 0; 111 112 for (i = 0; i != len; i++) { 113 char c; 114 if (get_user(c, data + i)) 115 return -EFAULT; 116 if (c == 'V') 117 expect_close = 1; 118 } 119 } 120 wdt_reset_counter(); 121 return len; 122 } 123 return 0; 124} 125 126static long wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 127{ 128 int new_timeout; 129 static struct watchdog_info ident = { 130 .options = WDIOF_SETTIMEOUT | 131 WDIOF_KEEPALIVEPING | 132 WDIOF_MAGICCLOSE, 133 .firmware_version = 0, 134 .identity = "ADM5120_WDT Watchdog", 135 }; 136 switch (cmd) { 137 default: 138 return -ENOTTY; 139 case WDIOC_GETSUPPORT: 140 if (copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident))) 141 return -EFAULT; 142 return 0; 143 case WDIOC_GETSTATUS: 144 case WDIOC_GETBOOTSTATUS: 145 return put_user(0, (int *)arg); 146 case WDIOC_KEEPALIVE: 147 wdt_reset_counter(); 148 return 0; 149 case WDIOC_SETTIMEOUT: 150 if (get_user(new_timeout, (int *)arg)) 151 return -EFAULT; 152 if (new_timeout < 1) 153 return -EINVAL; 154 if (new_timeout > MAX_TIMEOUT) 155 return -EINVAL; 156 timeout = new_timeout; 157 wdt_set_timeout(); 158 /* Fall */ 159 case WDIOC_GETTIMEOUT: 160 return put_user(timeout, (int *)arg); 161 } 162} 163 164static const struct file_operations wdt_fops = { 165 .owner = THIS_MODULE, 166 .llseek = no_llseek, 167 .write = wdt_write, 168 .unlocked_ioctl = wdt_ioctl, 169 .open = wdt_open, 170 .release = wdt_release, 171}; 172 173static struct miscdevice wdt_miscdev = { 174 .minor = WATCHDOG_MINOR, 175 .name = "watchdog", 176 .fops = &wdt_fops, 177}; 178 179static char banner[] __initdata = KERN_INFO NAME ": Watchdog Timer version " VERSION "\n"; 180 181static int __init watchdog_init(void) 182{ 183 int ret; 184 185 ret = misc_register(&wdt_miscdev); 186 187 if (ret) 188 return ret; 189 190 wdt_disable(); 191 printk(banner); 192 193 return 0; 194} 195 196static void __exit watchdog_exit(void) 197{ 198 misc_deregister(&wdt_miscdev); 199} 200 201module_init(watchdog_init); 202module_exit(watchdog_exit); 203