1 /* 2 * Watchdog driver for the wm8350 3 * 4 * Copyright (C) 2007, 2008 Wolfson Microelectronics <linux@wolfsonmicro.com> 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 9 */ 10 11 #include <linux/module.h> 12 #include <linux/moduleparam.h> 13 #include <linux/types.h> 14 #include <linux/kernel.h> 15 #include <linux/fs.h> 16 #include <linux/miscdevice.h> 17 #include <linux/platform_device.h> 18 #include <linux/watchdog.h> 19 #include <linux/uaccess.h> 20 #include <linux/mfd/wm8350/core.h> 21 22 static int nowayout = WATCHDOG_NOWAYOUT; 23 module_param(nowayout, int, 0); 24 MODULE_PARM_DESC(nowayout, 25 "Watchdog cannot be stopped once started (default=" 26 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 27 28 static unsigned long wm8350_wdt_users; 29 static struct miscdevice wm8350_wdt_miscdev; 30 static int wm8350_wdt_expect_close; 31 static DEFINE_MUTEX(wdt_mutex); 32 33 static struct { 34 int time; /* Seconds */ 35 u16 val; /* To be set in WM8350_SYSTEM_CONTROL_2 */ 36 } wm8350_wdt_cfgs[] = { 37 { 1, 0x02 }, 38 { 2, 0x04 }, 39 { 4, 0x05 }, 40 }; 41 42 static struct wm8350 *get_wm8350(void) 43 { 44 return dev_get_drvdata(wm8350_wdt_miscdev.parent); 45 } 46 47 static int wm8350_wdt_set_timeout(struct wm8350 *wm8350, u16 value) 48 { 49 int ret; 50 u16 reg; 51 52 mutex_lock(&wdt_mutex); 53 wm8350_reg_unlock(wm8350); 54 55 reg = wm8350_reg_read(wm8350, WM8350_SYSTEM_CONTROL_2); 56 reg &= ~WM8350_WDOG_TO_MASK; 57 reg |= value; 58 ret = wm8350_reg_write(wm8350, WM8350_SYSTEM_CONTROL_2, reg); 59 60 wm8350_reg_lock(wm8350); 61 mutex_unlock(&wdt_mutex); 62 63 return ret; 64 } 65 66 static int wm8350_wdt_start(struct wm8350 *wm8350) 67 { 68 int ret; 69 u16 reg; 70 71 mutex_lock(&wdt_mutex); 72 wm8350_reg_unlock(wm8350); 73 74 reg = wm8350_reg_read(wm8350, WM8350_SYSTEM_CONTROL_2); 75 reg &= ~WM8350_WDOG_MODE_MASK; 76 reg |= 0x20; 77 ret = wm8350_reg_write(wm8350, WM8350_SYSTEM_CONTROL_2, reg); 78 79 wm8350_reg_lock(wm8350); 80 mutex_unlock(&wdt_mutex); 81 82 return ret; 83 } 84 85 static int wm8350_wdt_stop(struct wm8350 *wm8350) 86 { 87 int ret; 88 u16 reg; 89 90 mutex_lock(&wdt_mutex); 91 wm8350_reg_unlock(wm8350); 92 93 reg = wm8350_reg_read(wm8350, WM8350_SYSTEM_CONTROL_2); 94 reg &= ~WM8350_WDOG_MODE_MASK; 95 ret = wm8350_reg_write(wm8350, WM8350_SYSTEM_CONTROL_2, reg); 96 97 wm8350_reg_lock(wm8350); 98 mutex_unlock(&wdt_mutex); 99 100 return ret; 101 } 102 103 static int wm8350_wdt_kick(struct wm8350 *wm8350) 104 { 105 int ret; 106 u16 reg; 107 108 mutex_lock(&wdt_mutex); 109 110 reg = wm8350_reg_read(wm8350, WM8350_SYSTEM_CONTROL_2); 111 ret = wm8350_reg_write(wm8350, WM8350_SYSTEM_CONTROL_2, reg); 112 113 mutex_unlock(&wdt_mutex); 114 115 return ret; 116 } 117 118 static int wm8350_wdt_open(struct inode *inode, struct file *file) 119 { 120 struct wm8350 *wm8350 = get_wm8350(); 121 int ret; 122 123 if (!wm8350) 124 return -ENODEV; 125 126 if (test_and_set_bit(0, &wm8350_wdt_users)) 127 return -EBUSY; 128 129 ret = wm8350_wdt_start(wm8350); 130 if (ret != 0) 131 return ret; 132 133 return nonseekable_open(inode, file); 134 } 135 136 static int wm8350_wdt_release(struct inode *inode, struct file *file) 137 { 138 struct wm8350 *wm8350 = get_wm8350(); 139 140 if (wm8350_wdt_expect_close) 141 wm8350_wdt_stop(wm8350); 142 else { 143 dev_warn(wm8350->dev, "Watchdog device closed uncleanly\n"); 144 wm8350_wdt_kick(wm8350); 145 } 146 147 clear_bit(0, &wm8350_wdt_users); 148 149 return 0; 150 } 151 152 static ssize_t wm8350_wdt_write(struct file *file, 153 const char __user *data, size_t count, 154 loff_t *ppos) 155 { 156 struct wm8350 *wm8350 = get_wm8350(); 157 size_t i; 158 159 if (count) { 160 wm8350_wdt_kick(wm8350); 161 162 if (!nowayout) { 163 /* In case it was set long ago */ 164 wm8350_wdt_expect_close = 0; 165 166 /* scan to see whether or not we got the magic 167 character */ 168 for (i = 0; i != count; i++) { 169 char c; 170 if (get_user(c, data + i)) 171 return -EFAULT; 172 if (c == 'V') 173 wm8350_wdt_expect_close = 42; 174 } 175 } 176 } 177 return count; 178 } 179 180 static struct watchdog_info ident = { 181 .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, 182 .identity = "WM8350 Watchdog", 183 }; 184 185 static long wm8350_wdt_ioctl(struct file *file, unsigned int cmd, 186 unsigned long arg) 187 { 188 struct wm8350 *wm8350 = get_wm8350(); 189 int ret = -ENOTTY, time, i; 190 void __user *argp = (void __user *)arg; 191 int __user *p = argp; 192 u16 reg; 193 194 switch (cmd) { 195 case WDIOC_GETSUPPORT: 196 ret = copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0; 197 break; 198 199 case WDIOC_GETSTATUS: 200 case WDIOC_GETBOOTSTATUS: 201 ret = put_user(0, p); 202 break; 203 204 case WDIOC_SETOPTIONS: 205 { 206 int options; 207 208 if (get_user(options, p)) 209 return -EFAULT; 210 211 ret = -EINVAL; 212 213 /* Setting both simultaneously means at least one must fail */ 214 if (options == WDIOS_DISABLECARD) 215 ret = wm8350_wdt_start(wm8350); 216 217 if (options == WDIOS_ENABLECARD) 218 ret = wm8350_wdt_stop(wm8350); 219 break; 220 } 221 222 case WDIOC_KEEPALIVE: 223 ret = wm8350_wdt_kick(wm8350); 224 break; 225 226 case WDIOC_SETTIMEOUT: 227 ret = get_user(time, p); 228 if (ret) 229 break; 230 231 if (time == 0) { 232 if (nowayout) 233 ret = -EINVAL; 234 else 235 wm8350_wdt_stop(wm8350); 236 break; 237 } 238 239 for (i = 0; i < ARRAY_SIZE(wm8350_wdt_cfgs); i++) 240 if (wm8350_wdt_cfgs[i].time == time) 241 break; 242 if (i == ARRAY_SIZE(wm8350_wdt_cfgs)) 243 ret = -EINVAL; 244 else 245 ret = wm8350_wdt_set_timeout(wm8350, 246 wm8350_wdt_cfgs[i].val); 247 break; 248 249 case WDIOC_GETTIMEOUT: 250 reg = wm8350_reg_read(wm8350, WM8350_SYSTEM_CONTROL_2); 251 reg &= WM8350_WDOG_TO_MASK; 252 for (i = 0; i < ARRAY_SIZE(wm8350_wdt_cfgs); i++) 253 if (wm8350_wdt_cfgs[i].val == reg) 254 break; 255 if (i == ARRAY_SIZE(wm8350_wdt_cfgs)) { 256 dev_warn(wm8350->dev, 257 "Unknown watchdog configuration: %x\n", reg); 258 ret = -EINVAL; 259 } else 260 ret = put_user(wm8350_wdt_cfgs[i].time, p); 261 262 } 263 264 return ret; 265 } 266 267 static const struct file_operations wm8350_wdt_fops = { 268 .owner = THIS_MODULE, 269 .llseek = no_llseek, 270 .write = wm8350_wdt_write, 271 .unlocked_ioctl = wm8350_wdt_ioctl, 272 .open = wm8350_wdt_open, 273 .release = wm8350_wdt_release, 274 }; 275 276 static struct miscdevice wm8350_wdt_miscdev = { 277 .minor = WATCHDOG_MINOR, 278 .name = "watchdog", 279 .fops = &wm8350_wdt_fops, 280 }; 281 282 static int __devinit wm8350_wdt_probe(struct platform_device *pdev) 283 { 284 struct wm8350 *wm8350 = platform_get_drvdata(pdev); 285 286 if (!wm8350) { 287 dev_err(wm8350->dev, "No driver data supplied\n"); 288 return -ENODEV; 289 } 290 291 /* Default to 4s timeout */ 292 wm8350_wdt_set_timeout(wm8350, 0x05); 293 294 wm8350_wdt_miscdev.parent = &pdev->dev; 295 296 return misc_register(&wm8350_wdt_miscdev); 297 } 298 299 static int __devexit wm8350_wdt_remove(struct platform_device *pdev) 300 { 301 misc_deregister(&wm8350_wdt_miscdev); 302 303 return 0; 304 } 305 306 static struct platform_driver wm8350_wdt_driver = { 307 .probe = wm8350_wdt_probe, 308 .remove = __devexit_p(wm8350_wdt_remove), 309 .driver = { 310 .name = "wm8350-wdt", 311 }, 312 }; 313 314 static int __init wm8350_wdt_init(void) 315 { 316 return platform_driver_register(&wm8350_wdt_driver); 317 } 318 module_init(wm8350_wdt_init); 319 320 static void __exit wm8350_wdt_exit(void) 321 { 322 platform_driver_unregister(&wm8350_wdt_driver); 323 } 324 module_exit(wm8350_wdt_exit); 325 326 MODULE_AUTHOR("Mark Brown"); 327 MODULE_DESCRIPTION("WM8350 Watchdog"); 328 MODULE_LICENSE("GPL"); 329 MODULE_ALIAS("platform:wm8350-wdt"); 330