1 /* 2 * GE watchdog userspace interface 3 * 4 * Author: Martyn Welch <martyn.welch@ge.com> 5 * 6 * Copyright 2008 GE Intelligent Platforms Embedded Systems, Inc. 7 * 8 * This program is free software; you can redistribute it and/or modify it 9 * under the terms of the GNU General Public License as published by the 10 * Free Software Foundation; either version 2 of the License, or (at your 11 * option) any later version. 12 * 13 * Based on: mv64x60_wdt.c (MV64X60 watchdog userspace interface) 14 * Author: James Chapman <jchapman@katalix.com> 15 */ 16 17 /* TODO: 18 * This driver does not provide support for the hardwares capability of sending 19 * an interrupt at a programmable threshold. 20 * 21 * This driver currently can only support 1 watchdog - there are 2 in the 22 * hardware that this driver supports. Thus one could be configured as a 23 * process-based watchdog (via /dev/watchdog), the second (using the interrupt 24 * capabilities) a kernel-based watchdog. 25 */ 26 27 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 28 29 #include <linux/kernel.h> 30 #include <linux/compiler.h> 31 #include <linux/init.h> 32 #include <linux/module.h> 33 #include <linux/miscdevice.h> 34 #include <linux/watchdog.h> 35 #include <linux/fs.h> 36 #include <linux/of.h> 37 #include <linux/of_platform.h> 38 #include <linux/io.h> 39 #include <linux/uaccess.h> 40 41 #include <sysdev/fsl_soc.h> 42 43 /* 44 * The watchdog configuration register contains a pair of 2-bit fields, 45 * 1. a reload field, bits 27-26, which triggers a reload of 46 * the countdown register, and 47 * 2. an enable field, bits 25-24, which toggles between 48 * enabling and disabling the watchdog timer. 49 * Bit 31 is a read-only field which indicates whether the 50 * watchdog timer is currently enabled. 51 * 52 * The low 24 bits contain the timer reload value. 53 */ 54 #define GEF_WDC_ENABLE_SHIFT 24 55 #define GEF_WDC_SERVICE_SHIFT 26 56 #define GEF_WDC_ENABLED_SHIFT 31 57 58 #define GEF_WDC_ENABLED_TRUE 1 59 #define GEF_WDC_ENABLED_FALSE 0 60 61 /* Flags bits */ 62 #define GEF_WDOG_FLAG_OPENED 0 63 64 static unsigned long wdt_flags; 65 static int wdt_status; 66 static void __iomem *gef_wdt_regs; 67 static int gef_wdt_timeout; 68 static int gef_wdt_count; 69 static unsigned int bus_clk; 70 static char expect_close; 71 static DEFINE_SPINLOCK(gef_wdt_spinlock); 72 73 static bool nowayout = WATCHDOG_NOWAYOUT; 74 module_param(nowayout, bool, 0); 75 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" 76 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 77 78 79 static int gef_wdt_toggle_wdc(int enabled_predicate, int field_shift) 80 { 81 u32 data; 82 u32 enabled; 83 int ret = 0; 84 85 spin_lock(&gef_wdt_spinlock); 86 data = ioread32be(gef_wdt_regs); 87 enabled = (data >> GEF_WDC_ENABLED_SHIFT) & 1; 88 89 /* only toggle the requested field if enabled state matches predicate */ 90 if ((enabled ^ enabled_predicate) == 0) { 91 /* We write a 1, then a 2 -- to the appropriate field */ 92 data = (1 << field_shift) | gef_wdt_count; 93 iowrite32be(data, gef_wdt_regs); 94 95 data = (2 << field_shift) | gef_wdt_count; 96 iowrite32be(data, gef_wdt_regs); 97 ret = 1; 98 } 99 spin_unlock(&gef_wdt_spinlock); 100 101 return ret; 102 } 103 104 static void gef_wdt_service(void) 105 { 106 gef_wdt_toggle_wdc(GEF_WDC_ENABLED_TRUE, 107 GEF_WDC_SERVICE_SHIFT); 108 } 109 110 static void gef_wdt_handler_enable(void) 111 { 112 if (gef_wdt_toggle_wdc(GEF_WDC_ENABLED_FALSE, 113 GEF_WDC_ENABLE_SHIFT)) { 114 gef_wdt_service(); 115 pr_notice("watchdog activated\n"); 116 } 117 } 118 119 static void gef_wdt_handler_disable(void) 120 { 121 if (gef_wdt_toggle_wdc(GEF_WDC_ENABLED_TRUE, 122 GEF_WDC_ENABLE_SHIFT)) 123 pr_notice("watchdog deactivated\n"); 124 } 125 126 static void gef_wdt_set_timeout(unsigned int timeout) 127 { 128 /* maximum bus cycle count is 0xFFFFFFFF */ 129 if (timeout > 0xFFFFFFFF / bus_clk) 130 timeout = 0xFFFFFFFF / bus_clk; 131 132 /* Register only holds upper 24 bits, bit shifted into lower 24 */ 133 gef_wdt_count = (timeout * bus_clk) >> 8; 134 gef_wdt_timeout = timeout; 135 } 136 137 138 static ssize_t gef_wdt_write(struct file *file, const char __user *data, 139 size_t len, loff_t *ppos) 140 { 141 if (len) { 142 if (!nowayout) { 143 size_t i; 144 145 expect_close = 0; 146 147 for (i = 0; i != len; i++) { 148 char c; 149 if (get_user(c, data + i)) 150 return -EFAULT; 151 if (c == 'V') 152 expect_close = 42; 153 } 154 } 155 gef_wdt_service(); 156 } 157 158 return len; 159 } 160 161 static long gef_wdt_ioctl(struct file *file, unsigned int cmd, 162 unsigned long arg) 163 { 164 int timeout; 165 int options; 166 void __user *argp = (void __user *)arg; 167 static const struct watchdog_info info = { 168 .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | 169 WDIOF_KEEPALIVEPING, 170 .firmware_version = 0, 171 .identity = "GE watchdog", 172 }; 173 174 switch (cmd) { 175 case WDIOC_GETSUPPORT: 176 if (copy_to_user(argp, &info, sizeof(info))) 177 return -EFAULT; 178 break; 179 180 case WDIOC_GETSTATUS: 181 case WDIOC_GETBOOTSTATUS: 182 if (put_user(wdt_status, (int __user *)argp)) 183 return -EFAULT; 184 wdt_status &= ~WDIOF_KEEPALIVEPING; 185 break; 186 187 case WDIOC_SETOPTIONS: 188 if (get_user(options, (int __user *)argp)) 189 return -EFAULT; 190 191 if (options & WDIOS_DISABLECARD) 192 gef_wdt_handler_disable(); 193 194 if (options & WDIOS_ENABLECARD) 195 gef_wdt_handler_enable(); 196 break; 197 198 case WDIOC_KEEPALIVE: 199 gef_wdt_service(); 200 wdt_status |= WDIOF_KEEPALIVEPING; 201 break; 202 203 case WDIOC_SETTIMEOUT: 204 if (get_user(timeout, (int __user *)argp)) 205 return -EFAULT; 206 gef_wdt_set_timeout(timeout); 207 /* Fall through */ 208 209 case WDIOC_GETTIMEOUT: 210 if (put_user(gef_wdt_timeout, (int __user *)argp)) 211 return -EFAULT; 212 break; 213 214 default: 215 return -ENOTTY; 216 } 217 218 return 0; 219 } 220 221 static int gef_wdt_open(struct inode *inode, struct file *file) 222 { 223 if (test_and_set_bit(GEF_WDOG_FLAG_OPENED, &wdt_flags)) 224 return -EBUSY; 225 226 if (nowayout) 227 __module_get(THIS_MODULE); 228 229 gef_wdt_handler_enable(); 230 231 return nonseekable_open(inode, file); 232 } 233 234 static int gef_wdt_release(struct inode *inode, struct file *file) 235 { 236 if (expect_close == 42) 237 gef_wdt_handler_disable(); 238 else { 239 pr_crit("unexpected close, not stopping timer!\n"); 240 gef_wdt_service(); 241 } 242 expect_close = 0; 243 244 clear_bit(GEF_WDOG_FLAG_OPENED, &wdt_flags); 245 246 return 0; 247 } 248 249 static const struct file_operations gef_wdt_fops = { 250 .owner = THIS_MODULE, 251 .llseek = no_llseek, 252 .write = gef_wdt_write, 253 .unlocked_ioctl = gef_wdt_ioctl, 254 .open = gef_wdt_open, 255 .release = gef_wdt_release, 256 }; 257 258 static struct miscdevice gef_wdt_miscdev = { 259 .minor = WATCHDOG_MINOR, 260 .name = "watchdog", 261 .fops = &gef_wdt_fops, 262 }; 263 264 265 static int gef_wdt_probe(struct platform_device *dev) 266 { 267 int timeout = 10; 268 u32 freq; 269 270 bus_clk = 133; /* in MHz */ 271 272 freq = fsl_get_sys_freq(); 273 if (freq != -1) 274 bus_clk = freq; 275 276 /* Map devices registers into memory */ 277 gef_wdt_regs = of_iomap(dev->dev.of_node, 0); 278 if (gef_wdt_regs == NULL) 279 return -ENOMEM; 280 281 gef_wdt_set_timeout(timeout); 282 283 gef_wdt_handler_disable(); /* in case timer was already running */ 284 285 return misc_register(&gef_wdt_miscdev); 286 } 287 288 static int gef_wdt_remove(struct platform_device *dev) 289 { 290 misc_deregister(&gef_wdt_miscdev); 291 292 gef_wdt_handler_disable(); 293 294 iounmap(gef_wdt_regs); 295 296 return 0; 297 } 298 299 static const struct of_device_id gef_wdt_ids[] = { 300 { 301 .compatible = "gef,fpga-wdt", 302 }, 303 {}, 304 }; 305 306 static struct platform_driver gef_wdt_driver = { 307 .driver = { 308 .name = "gef_wdt", 309 .owner = THIS_MODULE, 310 .of_match_table = gef_wdt_ids, 311 }, 312 .probe = gef_wdt_probe, 313 .remove = gef_wdt_remove, 314 }; 315 316 static int __init gef_wdt_init(void) 317 { 318 pr_info("GE watchdog driver\n"); 319 return platform_driver_register(&gef_wdt_driver); 320 } 321 322 static void __exit gef_wdt_exit(void) 323 { 324 platform_driver_unregister(&gef_wdt_driver); 325 } 326 327 module_init(gef_wdt_init); 328 module_exit(gef_wdt_exit); 329 330 MODULE_AUTHOR("Martyn Welch <martyn.welch@ge.com>"); 331 MODULE_DESCRIPTION("GE watchdog driver"); 332 MODULE_LICENSE("GPL"); 333 MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); 334 MODULE_ALIAS("platform:gef_wdt"); 335