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 return put_user(0, p); 314 315 case WDIOC_KEEPALIVE: 316 ts72xx_wdt_kick(wdt); 317 break; 318 319 case WDIOC_SETOPTIONS: { 320 int options; 321 322 if (get_user(options, p)) { 323 error = -EFAULT; 324 break; 325 } 326 327 error = -EINVAL; 328 329 if ((options & WDIOS_DISABLECARD) != 0) { 330 ts72xx_wdt_stop(wdt); 331 error = 0; 332 } 333 if ((options & WDIOS_ENABLECARD) != 0) { 334 ts72xx_wdt_start(wdt); 335 error = 0; 336 } 337 338 break; 339 } 340 341 case WDIOC_SETTIMEOUT: { 342 int new_timeout; 343 344 if (get_user(new_timeout, p)) { 345 error = -EFAULT; 346 } else { 347 int regval; 348 349 regval = timeout_to_regval(new_timeout); 350 if (regval < 0) { 351 error = -EINVAL; 352 } else { 353 ts72xx_wdt_stop(wdt); 354 wdt->regval = regval; 355 ts72xx_wdt_start(wdt); 356 } 357 } 358 if (error) 359 break; 360 361 /*FALLTHROUGH*/ 362 } 363 364 case WDIOC_GETTIMEOUT: 365 if (put_user(regval_to_timeout(wdt->regval), p)) 366 error = -EFAULT; 367 break; 368 369 default: 370 error = -ENOTTY; 371 break; 372 } 373 374 mutex_unlock(&wdt->lock); 375 return error; 376 } 377 378 static const struct file_operations ts72xx_wdt_fops = { 379 .owner = THIS_MODULE, 380 .llseek = no_llseek, 381 .open = ts72xx_wdt_open, 382 .release = ts72xx_wdt_release, 383 .write = ts72xx_wdt_write, 384 .unlocked_ioctl = ts72xx_wdt_ioctl, 385 }; 386 387 static struct miscdevice ts72xx_wdt_miscdev = { 388 .minor = WATCHDOG_MINOR, 389 .name = "watchdog", 390 .fops = &ts72xx_wdt_fops, 391 }; 392 393 static int ts72xx_wdt_probe(struct platform_device *pdev) 394 { 395 struct ts72xx_wdt *wdt; 396 struct resource *r1, *r2; 397 int error = 0; 398 399 wdt = kzalloc(sizeof(struct ts72xx_wdt), GFP_KERNEL); 400 if (!wdt) { 401 dev_err(&pdev->dev, "failed to allocate memory\n"); 402 return -ENOMEM; 403 } 404 405 r1 = platform_get_resource(pdev, IORESOURCE_MEM, 0); 406 if (!r1) { 407 dev_err(&pdev->dev, "failed to get memory resource\n"); 408 error = -ENODEV; 409 goto fail; 410 } 411 412 r1 = request_mem_region(r1->start, resource_size(r1), pdev->name); 413 if (!r1) { 414 dev_err(&pdev->dev, "cannot request memory region\n"); 415 error = -EBUSY; 416 goto fail; 417 } 418 419 wdt->control_reg = ioremap(r1->start, resource_size(r1)); 420 if (!wdt->control_reg) { 421 dev_err(&pdev->dev, "failed to map memory\n"); 422 error = -ENODEV; 423 goto fail_free_control; 424 } 425 426 r2 = platform_get_resource(pdev, IORESOURCE_MEM, 1); 427 if (!r2) { 428 dev_err(&pdev->dev, "failed to get memory resource\n"); 429 error = -ENODEV; 430 goto fail_unmap_control; 431 } 432 433 r2 = request_mem_region(r2->start, resource_size(r2), pdev->name); 434 if (!r2) { 435 dev_err(&pdev->dev, "cannot request memory region\n"); 436 error = -EBUSY; 437 goto fail_unmap_control; 438 } 439 440 wdt->feed_reg = ioremap(r2->start, resource_size(r2)); 441 if (!wdt->feed_reg) { 442 dev_err(&pdev->dev, "failed to map memory\n"); 443 error = -ENODEV; 444 goto fail_free_feed; 445 } 446 447 platform_set_drvdata(pdev, wdt); 448 ts72xx_wdt_pdev = pdev; 449 wdt->pdev = pdev; 450 mutex_init(&wdt->lock); 451 452 /* make sure that the watchdog is disabled */ 453 ts72xx_wdt_stop(wdt); 454 455 error = misc_register(&ts72xx_wdt_miscdev); 456 if (error) { 457 dev_err(&pdev->dev, "failed to register miscdev\n"); 458 goto fail_unmap_feed; 459 } 460 461 dev_info(&pdev->dev, "TS-72xx Watchdog driver\n"); 462 463 return 0; 464 465 fail_unmap_feed: 466 platform_set_drvdata(pdev, NULL); 467 iounmap(wdt->feed_reg); 468 fail_free_feed: 469 release_mem_region(r2->start, resource_size(r2)); 470 fail_unmap_control: 471 iounmap(wdt->control_reg); 472 fail_free_control: 473 release_mem_region(r1->start, resource_size(r1)); 474 fail: 475 kfree(wdt); 476 return error; 477 } 478 479 static int ts72xx_wdt_remove(struct platform_device *pdev) 480 { 481 struct ts72xx_wdt *wdt = platform_get_drvdata(pdev); 482 struct resource *res; 483 int error; 484 485 error = misc_deregister(&ts72xx_wdt_miscdev); 486 platform_set_drvdata(pdev, NULL); 487 488 iounmap(wdt->feed_reg); 489 res = platform_get_resource(pdev, IORESOURCE_MEM, 1); 490 release_mem_region(res->start, resource_size(res)); 491 492 iounmap(wdt->control_reg); 493 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 494 release_mem_region(res->start, resource_size(res)); 495 496 kfree(wdt); 497 return error; 498 } 499 500 static struct platform_driver ts72xx_wdt_driver = { 501 .probe = ts72xx_wdt_probe, 502 .remove = ts72xx_wdt_remove, 503 .driver = { 504 .name = "ts72xx-wdt", 505 .owner = THIS_MODULE, 506 }, 507 }; 508 509 module_platform_driver(ts72xx_wdt_driver); 510 511 MODULE_AUTHOR("Mika Westerberg <mika.westerberg@iki.fi>"); 512 MODULE_DESCRIPTION("TS-72xx SBC Watchdog"); 513 MODULE_LICENSE("GPL"); 514 MODULE_ALIAS("platform:ts72xx-wdt"); 515