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 38static int heartbeat = WDT_HEARTBEAT; 39module_param(heartbeat, int, 0); 40MODULE_PARM_DESC(heartbeat, "Watchdog heartbeats in seconds. " 41 "(default = " __MODULE_STRING(WDT_HEARTBEAT) ")"); 42 43static bool nowayout = WATCHDOG_NOWAYOUT; 44module_param(nowayout, bool, 0); 45MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started " 46 "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 47 48static 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 57static 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 */ 68static 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 */ 88static 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 99static void pikawdt_keepalive(void) 100{ 101 pikawdt_private.next_heartbeat = jiffies + heartbeat * HZ; 102} 103 104static 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 */ 113static 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 */ 127static 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 */ 141static 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 */ 172static 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 209static 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 218static struct miscdevice pikawdt_miscdev = { 219 .minor = WATCHDOG_MINOR, 220 .name = "watchdog", 221 .fops = &pikawdt_fops, 222}; 223 224static 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 284out: 285 iounmap(pikawdt_private.fpga); 286 return ret; 287} 288 289static void __exit pikawdt_exit(void) 290{ 291 misc_deregister(&pikawdt_miscdev); 292 293 iounmap(pikawdt_private.fpga); 294} 295 296module_init(pikawdt_init); 297module_exit(pikawdt_exit); 298 299MODULE_AUTHOR("Sean MacLennan <smaclennan@pikatech.com>"); 300MODULE_DESCRIPTION("PIKA FPGA based Watchdog Timer"); 301MODULE_LICENSE("GPL"); 302