1 /* 2 * PIKA FPGA based Watchdog Timer 3 * 4 * Copyright (c) 2008 PIKA Technologies 5 * Sean MacLennan <smaclennan@pikatech.com> 6 */ 7 8 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 9 10 #include <linux/init.h> 11 #include <linux/errno.h> 12 #include <linux/module.h> 13 #include <linux/moduleparam.h> 14 #include <linux/types.h> 15 #include <linux/kernel.h> 16 #include <linux/fs.h> 17 #include <linux/miscdevice.h> 18 #include <linux/watchdog.h> 19 #include <linux/reboot.h> 20 #include <linux/jiffies.h> 21 #include <linux/timer.h> 22 #include <linux/bitops.h> 23 #include <linux/uaccess.h> 24 #include <linux/io.h> 25 #include <linux/of_address.h> 26 #include <linux/of_platform.h> 27 28 #define DRV_NAME "PIKA-WDT" 29 30 /* Hardware timeout in seconds */ 31 #define WDT_HW_TIMEOUT 2 32 33 /* Timer heartbeat (500ms) */ 34 #define WDT_TIMEOUT (HZ/2) 35 36 /* User land timeout */ 37 #define WDT_HEARTBEAT 15 38 static int heartbeat = WDT_HEARTBEAT; 39 module_param(heartbeat, int, 0); 40 MODULE_PARM_DESC(heartbeat, "Watchdog heartbeats in seconds. " 41 "(default = " __MODULE_STRING(WDT_HEARTBEAT) ")"); 42 43 static bool nowayout = WATCHDOG_NOWAYOUT; 44 module_param(nowayout, bool, 0); 45 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started " 46 "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 47 48 static struct { 49 void __iomem *fpga; 50 unsigned long next_heartbeat; /* the next_heartbeat for the timer */ 51 unsigned long open; 52 char expect_close; 53 int bootstatus; 54 struct timer_list timer; /* The timer that pings the watchdog */ 55 } pikawdt_private; 56 57 static struct watchdog_info ident = { 58 .identity = DRV_NAME, 59 .options = WDIOF_CARDRESET | 60 WDIOF_SETTIMEOUT | 61 WDIOF_KEEPALIVEPING | 62 WDIOF_MAGICCLOSE, 63 }; 64 65 /* 66 * Reload the watchdog timer. (ie, pat the watchdog) 67 */ 68 static inline void pikawdt_reset(void) 69 { 70 /* -- FPGA: Reset Control Register (32bit R/W) (Offset: 0x14) -- 71 * Bit 7, WTCHDG_EN: When set to 1, the watchdog timer is enabled. 72 * Once enabled, it cannot be disabled. The watchdog can be 73 * kicked by performing any write access to the reset 74 * control register (this register). 75 * Bit 8-11, WTCHDG_TIMEOUT_SEC: Sets the watchdog timeout value in 76 * seconds. Valid ranges are 1 to 15 seconds. The value can 77 * be modified dynamically. 78 */ 79 unsigned reset = in_be32(pikawdt_private.fpga + 0x14); 80 /* enable with max timeout - 15 seconds */ 81 reset |= (1 << 7) + (WDT_HW_TIMEOUT << 8); 82 out_be32(pikawdt_private.fpga + 0x14, reset); 83 } 84 85 /* 86 * Timer tick 87 */ 88 static void pikawdt_ping(unsigned long data) 89 { 90 if (time_before(jiffies, pikawdt_private.next_heartbeat) || 91 (!nowayout && !pikawdt_private.open)) { 92 pikawdt_reset(); 93 mod_timer(&pikawdt_private.timer, jiffies + WDT_TIMEOUT); 94 } else 95 pr_crit("I will reset your machine !\n"); 96 } 97 98 99 static void pikawdt_keepalive(void) 100 { 101 pikawdt_private.next_heartbeat = jiffies + heartbeat * HZ; 102 } 103 104 static void pikawdt_start(void) 105 { 106 pikawdt_keepalive(); 107 mod_timer(&pikawdt_private.timer, jiffies + WDT_TIMEOUT); 108 } 109 110 /* 111 * Watchdog device is opened, and watchdog starts running. 112 */ 113 static int pikawdt_open(struct inode *inode, struct file *file) 114 { 115 /* /dev/watchdog can only be opened once */ 116 if (test_and_set_bit(0, &pikawdt_private.open)) 117 return -EBUSY; 118 119 pikawdt_start(); 120 121 return nonseekable_open(inode, file); 122 } 123 124 /* 125 * Close the watchdog device. 126 */ 127 static int pikawdt_release(struct inode *inode, struct file *file) 128 { 129 /* stop internal ping */ 130 if (!pikawdt_private.expect_close) 131 del_timer(&pikawdt_private.timer); 132 133 clear_bit(0, &pikawdt_private.open); 134 pikawdt_private.expect_close = 0; 135 return 0; 136 } 137 138 /* 139 * Pat the watchdog whenever device is written to. 140 */ 141 static ssize_t pikawdt_write(struct file *file, const char __user *data, 142 size_t len, loff_t *ppos) 143 { 144 if (!len) 145 return 0; 146 147 /* Scan for magic character */ 148 if (!nowayout) { 149 size_t i; 150 151 pikawdt_private.expect_close = 0; 152 153 for (i = 0; i < len; i++) { 154 char c; 155 if (get_user(c, data + i)) 156 return -EFAULT; 157 if (c == 'V') { 158 pikawdt_private.expect_close = 42; 159 break; 160 } 161 } 162 } 163 164 pikawdt_keepalive(); 165 166 return len; 167 } 168 169 /* 170 * Handle commands from user-space. 171 */ 172 static long pikawdt_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, &ident, sizeof(ident)) ? -EFAULT : 0; 182 183 case WDIOC_GETSTATUS: 184 return put_user(0, p); 185 186 case WDIOC_GETBOOTSTATUS: 187 return put_user(pikawdt_private.bootstatus, p); 188 189 case WDIOC_KEEPALIVE: 190 pikawdt_keepalive(); 191 return 0; 192 193 case WDIOC_SETTIMEOUT: 194 if (get_user(new_value, p)) 195 return -EFAULT; 196 197 heartbeat = new_value; 198 pikawdt_keepalive(); 199 200 return put_user(new_value, p); /* return current value */ 201 202 case WDIOC_GETTIMEOUT: 203 return put_user(heartbeat, p); 204 } 205 return -ENOTTY; 206 } 207 208 209 static const struct file_operations pikawdt_fops = { 210 .owner = THIS_MODULE, 211 .llseek = no_llseek, 212 .open = pikawdt_open, 213 .release = pikawdt_release, 214 .write = pikawdt_write, 215 .unlocked_ioctl = pikawdt_ioctl, 216 }; 217 218 static struct miscdevice pikawdt_miscdev = { 219 .minor = WATCHDOG_MINOR, 220 .name = "watchdog", 221 .fops = &pikawdt_fops, 222 }; 223 224 static int __init pikawdt_init(void) 225 { 226 struct device_node *np; 227 void __iomem *fpga; 228 static u32 post1; 229 int ret; 230 231 np = of_find_compatible_node(NULL, NULL, "pika,fpga"); 232 if (np == NULL) { 233 pr_err("Unable to find fpga\n"); 234 return -ENOENT; 235 } 236 237 pikawdt_private.fpga = of_iomap(np, 0); 238 of_node_put(np); 239 if (pikawdt_private.fpga == NULL) { 240 pr_err("Unable to map fpga\n"); 241 return -ENOMEM; 242 } 243 244 ident.firmware_version = in_be32(pikawdt_private.fpga + 0x1c) & 0xffff; 245 246 /* POST information is in the sd area. */ 247 np = of_find_compatible_node(NULL, NULL, "pika,fpga-sd"); 248 if (np == NULL) { 249 pr_err("Unable to find fpga-sd\n"); 250 ret = -ENOENT; 251 goto out; 252 } 253 254 fpga = of_iomap(np, 0); 255 of_node_put(np); 256 if (fpga == NULL) { 257 pr_err("Unable to map fpga-sd\n"); 258 ret = -ENOMEM; 259 goto out; 260 } 261 262 /* -- FPGA: POST Test Results Register 1 (32bit R/W) (Offset: 0x4040) -- 263 * Bit 31, WDOG: Set to 1 when the last reset was caused by a watchdog 264 * timeout. 265 */ 266 post1 = in_be32(fpga + 0x40); 267 if (post1 & 0x80000000) 268 pikawdt_private.bootstatus = WDIOF_CARDRESET; 269 270 iounmap(fpga); 271 272 setup_timer(&pikawdt_private.timer, pikawdt_ping, 0); 273 274 ret = misc_register(&pikawdt_miscdev); 275 if (ret) { 276 pr_err("Unable to register miscdev\n"); 277 goto out; 278 } 279 280 pr_info("initialized. heartbeat=%d sec (nowayout=%d)\n", 281 heartbeat, nowayout); 282 return 0; 283 284 out: 285 iounmap(pikawdt_private.fpga); 286 return ret; 287 } 288 289 static void __exit pikawdt_exit(void) 290 { 291 misc_deregister(&pikawdt_miscdev); 292 293 iounmap(pikawdt_private.fpga); 294 } 295 296 module_init(pikawdt_init); 297 module_exit(pikawdt_exit); 298 299 MODULE_AUTHOR("Sean MacLennan <smaclennan@pikatech.com>"); 300 MODULE_DESCRIPTION("PIKA FPGA based Watchdog Timer"); 301 MODULE_LICENSE("GPL"); 302