1 /* 2 * Watchdog driver for Technologic Systems TS-72xx based SBCs 3 * (TS-7200, TS-7250 and TS-7260). These boards have external 4 * glue logic CPLD chip, which includes programmable watchdog 5 * timer. 6 * 7 * Copyright (c) 2009 Mika Westerberg <mika.westerberg@iki.fi> 8 * 9 * This driver is based on ep93xx_wdt and wm831x_wdt drivers. 10 * 11 * This file is licensed under the terms of the GNU General Public 12 * License version 2. This program is licensed "as is" without any 13 * warranty of any kind, whether express or implied. 14 */ 15 16 #include <linux/fs.h> 17 #include <linux/io.h> 18 #include <linux/module.h> 19 #include <linux/moduleparam.h> 20 #include <linux/miscdevice.h> 21 #include <linux/mutex.h> 22 #include <linux/platform_device.h> 23 #include <linux/slab.h> 24 #include <linux/watchdog.h> 25 #include <linux/uaccess.h> 26 27 #define TS72XX_WDT_FEED_VAL 0x05 28 #define TS72XX_WDT_DEFAULT_TIMEOUT 8 29 30 static int timeout = TS72XX_WDT_DEFAULT_TIMEOUT; 31 module_param(timeout, int, 0); 32 MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds. " 33 "(1 <= timeout <= 8, default=" 34 __MODULE_STRING(TS72XX_WDT_DEFAULT_TIMEOUT) 35 ")"); 36 37 static bool nowayout = WATCHDOG_NOWAYOUT; 38 module_param(nowayout, bool, 0); 39 MODULE_PARM_DESC(nowayout, "Disable watchdog shutdown on close"); 40 41 /** 42 * struct ts72xx_wdt - watchdog control structure 43 * @lock: lock that protects this structure 44 * @regval: watchdog timeout value suitable for control register 45 * @flags: flags controlling watchdog device state 46 * @control_reg: watchdog control register 47 * @feed_reg: watchdog feed register 48 * @pdev: back pointer to platform dev 49 */ 50 struct ts72xx_wdt { 51 struct mutex lock; 52 int regval; 53 54 #define TS72XX_WDT_BUSY_FLAG 1 55 #define TS72XX_WDT_EXPECT_CLOSE_FLAG 2 56 int flags; 57 58 void __iomem *control_reg; 59 void __iomem *feed_reg; 60 61 struct platform_device *pdev; 62 }; 63 64 struct platform_device *ts72xx_wdt_pdev; 65 66 /* 67 * TS-72xx Watchdog supports following timeouts (value written 68 * to control register): 69 * value description 70 * ------------------------- 71 * 0x00 watchdog disabled 72 * 0x01 250ms 73 * 0x02 500ms 74 * 0x03 1s 75 * 0x04 reserved 76 * 0x05 2s 77 * 0x06 4s 78 * 0x07 8s 79 * 80 * Timeouts below 1s are not very usable so we don't 81 * allow them at all. 82 * 83 * We provide two functions that convert between these: 84 * timeout_to_regval() and regval_to_timeout(). 85 */ 86 static const struct { 87 int timeout; 88 int regval; 89 } ts72xx_wdt_map[] = { 90 { 1, 3 }, 91 { 2, 5 }, 92 { 4, 6 }, 93 { 8, 7 }, 94 }; 95 96 /** 97 * timeout_to_regval() - converts given timeout to control register value 98 * @new_timeout: timeout in seconds to be converted 99 * 100 * Function converts given @new_timeout into valid value that can 101 * be programmed into watchdog control register. When conversion is 102 * not possible, function returns %-EINVAL. 103 */ 104 static int timeout_to_regval(int new_timeout) 105 { 106 int i; 107 108 /* first limit it to 1 - 8 seconds */ 109 new_timeout = clamp_val(new_timeout, 1, 8); 110 111 for (i = 0; i < ARRAY_SIZE(ts72xx_wdt_map); i++) { 112 if (ts72xx_wdt_map[i].timeout >= new_timeout) 113 return ts72xx_wdt_map[i].regval; 114 } 115 116 return -EINVAL; 117 } 118 119 /** 120 * regval_to_timeout() - converts control register value to timeout 121 * @regval: control register value to be converted 122 * 123 * Function converts given @regval to timeout in seconds (1, 2, 4 or 8). 124 * If @regval cannot be converted, function returns %-EINVAL. 125 */ 126 static int regval_to_timeout(int regval) 127 { 128 int i; 129 130 for (i = 0; i < ARRAY_SIZE(ts72xx_wdt_map); i++) { 131 if (ts72xx_wdt_map[i].regval == regval) 132 return ts72xx_wdt_map[i].timeout; 133 } 134 135 return -EINVAL; 136 } 137 138 /** 139 * ts72xx_wdt_kick() - kick the watchdog 140 * @wdt: watchdog to be kicked 141 * 142 * Called with @wdt->lock held. 143 */ 144 static inline void ts72xx_wdt_kick(struct ts72xx_wdt *wdt) 145 { 146 __raw_writeb(TS72XX_WDT_FEED_VAL, wdt->feed_reg); 147 } 148 149 /** 150 * ts72xx_wdt_start() - starts the watchdog timer 151 * @wdt: watchdog to be started 152 * 153 * This function programs timeout to watchdog timer 154 * and starts it. 155 * 156 * Called with @wdt->lock held. 157 */ 158 static void ts72xx_wdt_start(struct ts72xx_wdt *wdt) 159 { 160 /* 161 * To program the wdt, it first must be "fed" and 162 * only after that (within 30 usecs) the configuration 163 * can be changed. 164 */ 165 ts72xx_wdt_kick(wdt); 166 __raw_writeb((u8)wdt->regval, wdt->control_reg); 167 } 168 169 /** 170 * ts72xx_wdt_stop() - stops the watchdog timer 171 * @wdt: watchdog to be stopped 172 * 173 * Called with @wdt->lock held. 174 */ 175 static void ts72xx_wdt_stop(struct ts72xx_wdt *wdt) 176 { 177 ts72xx_wdt_kick(wdt); 178 __raw_writeb(0, wdt->control_reg); 179 } 180 181 static int ts72xx_wdt_open(struct inode *inode, struct file *file) 182 { 183 struct ts72xx_wdt *wdt = platform_get_drvdata(ts72xx_wdt_pdev); 184 int regval; 185 186 /* 187 * Try to convert default timeout to valid register 188 * value first. 189 */ 190 regval = timeout_to_regval(timeout); 191 if (regval < 0) { 192 dev_err(&wdt->pdev->dev, 193 "failed to convert timeout (%d) to register value\n", 194 timeout); 195 return -EINVAL; 196 } 197 198 if (mutex_lock_interruptible(&wdt->lock)) 199 return -ERESTARTSYS; 200 201 if ((wdt->flags & TS72XX_WDT_BUSY_FLAG) != 0) { 202 mutex_unlock(&wdt->lock); 203 return -EBUSY; 204 } 205 206 wdt->flags = TS72XX_WDT_BUSY_FLAG; 207 wdt->regval = regval; 208 file->private_data = wdt; 209 210 ts72xx_wdt_start(wdt); 211 212 mutex_unlock(&wdt->lock); 213 return nonseekable_open(inode, file); 214 } 215 216 static int ts72xx_wdt_release(struct inode *inode, struct file *file) 217 { 218 struct ts72xx_wdt *wdt = file->private_data; 219 220 if (mutex_lock_interruptible(&wdt->lock)) 221 return -ERESTARTSYS; 222 223 if ((wdt->flags & TS72XX_WDT_EXPECT_CLOSE_FLAG) != 0) { 224 ts72xx_wdt_stop(wdt); 225 } else { 226 dev_warn(&wdt->pdev->dev, 227 "TS-72XX WDT device closed unexpectly. " 228 "Watchdog timer will not stop!\n"); 229 /* 230 * Kick it one more time, to give userland some time 231 * to recover (for example, respawning the kicker 232 * daemon). 233 */ 234 ts72xx_wdt_kick(wdt); 235 } 236 237 wdt->flags = 0; 238 239 mutex_unlock(&wdt->lock); 240 return 0; 241 } 242 243 static ssize_t ts72xx_wdt_write(struct file *file, 244 const char __user *data, 245 size_t len, 246 loff_t *ppos) 247 { 248 struct ts72xx_wdt *wdt = file->private_data; 249 250 if (!len) 251 return 0; 252 253 if (mutex_lock_interruptible(&wdt->lock)) 254 return -ERESTARTSYS; 255 256 ts72xx_wdt_kick(wdt); 257 258 /* 259 * Support for magic character closing. User process 260 * writes 'V' into the device, just before it is closed. 261 * This means that we know that the wdt timer can be 262 * stopped after user closes the device. 263 */ 264 if (!nowayout) { 265 int i; 266 267 for (i = 0; i < len; i++) { 268 char c; 269 270 /* In case it was set long ago */ 271 wdt->flags &= ~TS72XX_WDT_EXPECT_CLOSE_FLAG; 272 273 if (get_user(c, data + i)) { 274 mutex_unlock(&wdt->lock); 275 return -EFAULT; 276 } 277 if (c == 'V') { 278 wdt->flags |= TS72XX_WDT_EXPECT_CLOSE_FLAG; 279 break; 280 } 281 } 282 } 283 284 mutex_unlock(&wdt->lock); 285 return len; 286 } 287 288 static const struct watchdog_info winfo = { 289 .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | 290 WDIOF_MAGICCLOSE, 291 .firmware_version = 1, 292 .identity = "TS-72XX WDT", 293 }; 294 295 static long ts72xx_wdt_ioctl(struct file *file, unsigned int cmd, 296 unsigned long arg) 297 { 298 struct ts72xx_wdt *wdt = file->private_data; 299 void __user *argp = (void __user *)arg; 300 int __user *p = (int __user *)argp; 301 int error = 0; 302 303 if (mutex_lock_interruptible(&wdt->lock)) 304 return -ERESTARTSYS; 305 306 switch (cmd) { 307 case WDIOC_GETSUPPORT: 308 error = copy_to_user(argp, &winfo, sizeof(winfo)); 309 break; 310 311 case WDIOC_GETSTATUS: 312 case WDIOC_GETBOOTSTATUS: 313 error = put_user(0, p); 314 break; 315 316 case WDIOC_KEEPALIVE: 317 ts72xx_wdt_kick(wdt); 318 break; 319 320 case WDIOC_SETOPTIONS: { 321 int options; 322 323 if (get_user(options, p)) { 324 error = -EFAULT; 325 break; 326 } 327 328 error = -EINVAL; 329 330 if ((options & WDIOS_DISABLECARD) != 0) { 331 ts72xx_wdt_stop(wdt); 332 error = 0; 333 } 334 if ((options & WDIOS_ENABLECARD) != 0) { 335 ts72xx_wdt_start(wdt); 336 error = 0; 337 } 338 339 break; 340 } 341 342 case WDIOC_SETTIMEOUT: { 343 int new_timeout; 344 345 if (get_user(new_timeout, p)) { 346 error = -EFAULT; 347 } else { 348 int regval; 349 350 regval = timeout_to_regval(new_timeout); 351 if (regval < 0) { 352 error = -EINVAL; 353 } else { 354 ts72xx_wdt_stop(wdt); 355 wdt->regval = regval; 356 ts72xx_wdt_start(wdt); 357 } 358 } 359 if (error) 360 break; 361 362 /*FALLTHROUGH*/ 363 } 364 365 case WDIOC_GETTIMEOUT: 366 if (put_user(regval_to_timeout(wdt->regval), p)) 367 error = -EFAULT; 368 break; 369 370 default: 371 error = -ENOTTY; 372 break; 373 } 374 375 mutex_unlock(&wdt->lock); 376 return error; 377 } 378 379 static const struct file_operations ts72xx_wdt_fops = { 380 .owner = THIS_MODULE, 381 .llseek = no_llseek, 382 .open = ts72xx_wdt_open, 383 .release = ts72xx_wdt_release, 384 .write = ts72xx_wdt_write, 385 .unlocked_ioctl = ts72xx_wdt_ioctl, 386 }; 387 388 static struct miscdevice ts72xx_wdt_miscdev = { 389 .minor = WATCHDOG_MINOR, 390 .name = "watchdog", 391 .fops = &ts72xx_wdt_fops, 392 }; 393 394 static int ts72xx_wdt_probe(struct platform_device *pdev) 395 { 396 struct ts72xx_wdt *wdt; 397 struct resource *r1, *r2; 398 int error = 0; 399 400 wdt = devm_kzalloc(&pdev->dev, sizeof(struct ts72xx_wdt), GFP_KERNEL); 401 if (!wdt) { 402 dev_err(&pdev->dev, "failed to allocate memory\n"); 403 return -ENOMEM; 404 } 405 406 r1 = platform_get_resource(pdev, IORESOURCE_MEM, 0); 407 wdt->control_reg = devm_ioremap_resource(&pdev->dev, r1); 408 if (IS_ERR(wdt->control_reg)) 409 return PTR_ERR(wdt->control_reg); 410 411 r2 = platform_get_resource(pdev, IORESOURCE_MEM, 1); 412 wdt->feed_reg = devm_ioremap_resource(&pdev->dev, r2); 413 if (IS_ERR(wdt->feed_reg)) 414 return PTR_ERR(wdt->feed_reg); 415 416 platform_set_drvdata(pdev, wdt); 417 ts72xx_wdt_pdev = pdev; 418 wdt->pdev = pdev; 419 mutex_init(&wdt->lock); 420 421 /* make sure that the watchdog is disabled */ 422 ts72xx_wdt_stop(wdt); 423 424 error = misc_register(&ts72xx_wdt_miscdev); 425 if (error) { 426 dev_err(&pdev->dev, "failed to register miscdev\n"); 427 return error; 428 } 429 430 dev_info(&pdev->dev, "TS-72xx Watchdog driver\n"); 431 432 return 0; 433 } 434 435 static int ts72xx_wdt_remove(struct platform_device *pdev) 436 { 437 int error; 438 439 error = misc_deregister(&ts72xx_wdt_miscdev); 440 441 return error; 442 } 443 444 static struct platform_driver ts72xx_wdt_driver = { 445 .probe = ts72xx_wdt_probe, 446 .remove = ts72xx_wdt_remove, 447 .driver = { 448 .name = "ts72xx-wdt", 449 .owner = THIS_MODULE, 450 }, 451 }; 452 453 module_platform_driver(ts72xx_wdt_driver); 454 455 MODULE_AUTHOR("Mika Westerberg <mika.westerberg@iki.fi>"); 456 MODULE_DESCRIPTION("TS-72xx SBC Watchdog"); 457 MODULE_LICENSE("GPL"); 458 MODULE_ALIAS("platform:ts72xx-wdt"); 459