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