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 static 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 regval; 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 if (copy_to_user(argp, &winfo, sizeof(winfo))) 309 error = -EFAULT; 310 break; 311 312 case WDIOC_GETSTATUS: 313 case WDIOC_GETBOOTSTATUS: 314 error = put_user(0, p); 315 break; 316 317 case WDIOC_KEEPALIVE: 318 ts72xx_wdt_kick(wdt); 319 break; 320 321 case WDIOC_SETOPTIONS: { 322 int options; 323 324 error = get_user(options, p); 325 if (error) 326 break; 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 int regval; 345 346 error = get_user(new_timeout, p); 347 if (error) 348 break; 349 350 regval = timeout_to_regval(new_timeout); 351 if (regval < 0) { 352 error = regval; 353 break; 354 } 355 ts72xx_wdt_stop(wdt); 356 wdt->regval = regval; 357 ts72xx_wdt_start(wdt); 358 359 /*FALLTHROUGH*/ 360 } 361 362 case WDIOC_GETTIMEOUT: 363 error = put_user(regval_to_timeout(wdt->regval), p); 364 break; 365 366 default: 367 error = -ENOTTY; 368 break; 369 } 370 371 mutex_unlock(&wdt->lock); 372 return error; 373 } 374 375 static const struct file_operations ts72xx_wdt_fops = { 376 .owner = THIS_MODULE, 377 .llseek = no_llseek, 378 .open = ts72xx_wdt_open, 379 .release = ts72xx_wdt_release, 380 .write = ts72xx_wdt_write, 381 .unlocked_ioctl = ts72xx_wdt_ioctl, 382 }; 383 384 static struct miscdevice ts72xx_wdt_miscdev = { 385 .minor = WATCHDOG_MINOR, 386 .name = "watchdog", 387 .fops = &ts72xx_wdt_fops, 388 }; 389 390 static int ts72xx_wdt_probe(struct platform_device *pdev) 391 { 392 struct ts72xx_wdt *wdt; 393 struct resource *r1, *r2; 394 int error = 0; 395 396 wdt = devm_kzalloc(&pdev->dev, sizeof(struct ts72xx_wdt), GFP_KERNEL); 397 if (!wdt) 398 return -ENOMEM; 399 400 r1 = platform_get_resource(pdev, IORESOURCE_MEM, 0); 401 wdt->control_reg = devm_ioremap_resource(&pdev->dev, r1); 402 if (IS_ERR(wdt->control_reg)) 403 return PTR_ERR(wdt->control_reg); 404 405 r2 = platform_get_resource(pdev, IORESOURCE_MEM, 1); 406 wdt->feed_reg = devm_ioremap_resource(&pdev->dev, r2); 407 if (IS_ERR(wdt->feed_reg)) 408 return PTR_ERR(wdt->feed_reg); 409 410 platform_set_drvdata(pdev, wdt); 411 ts72xx_wdt_pdev = pdev; 412 wdt->pdev = pdev; 413 mutex_init(&wdt->lock); 414 415 /* make sure that the watchdog is disabled */ 416 ts72xx_wdt_stop(wdt); 417 418 error = misc_register(&ts72xx_wdt_miscdev); 419 if (error) { 420 dev_err(&pdev->dev, "failed to register miscdev\n"); 421 return error; 422 } 423 424 dev_info(&pdev->dev, "TS-72xx Watchdog driver\n"); 425 426 return 0; 427 } 428 429 static int ts72xx_wdt_remove(struct platform_device *pdev) 430 { 431 return misc_deregister(&ts72xx_wdt_miscdev); 432 } 433 434 static struct platform_driver ts72xx_wdt_driver = { 435 .probe = ts72xx_wdt_probe, 436 .remove = ts72xx_wdt_remove, 437 .driver = { 438 .name = "ts72xx-wdt", 439 .owner = THIS_MODULE, 440 }, 441 }; 442 443 module_platform_driver(ts72xx_wdt_driver); 444 445 MODULE_AUTHOR("Mika Westerberg <mika.westerberg@iki.fi>"); 446 MODULE_DESCRIPTION("TS-72xx SBC Watchdog"); 447 MODULE_LICENSE("GPL"); 448 MODULE_ALIAS("platform:ts72xx-wdt"); 449