1 /* linux/drivers/char/watchdog/s3c2410_wdt.c 2 * 3 * Copyright (c) 2004 Simtec Electronics 4 * Ben Dooks <ben@simtec.co.uk> 5 * 6 * S3C2410 Watchdog Timer Support 7 * 8 * Based on, softdog.c by Alan Cox, 9 * (c) Copyright 1996 Alan Cox <alan@lxorguk.ukuu.org.uk> 10 * 11 * This program is free software; you can redistribute it and/or modify 12 * it under the terms of the GNU General Public License as published by 13 * the Free Software Foundation; either version 2 of the License, or 14 * (at your option) any later version. 15 * 16 * This program is distributed in the hope that it will be useful, 17 * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 * GNU General Public License for more details. 20 * 21 * You should have received a copy of the GNU General Public License 22 * along with this program; if not, write to the Free Software 23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 24 */ 25 26 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 27 28 #include <linux/module.h> 29 #include <linux/moduleparam.h> 30 #include <linux/types.h> 31 #include <linux/timer.h> 32 #include <linux/watchdog.h> 33 #include <linux/platform_device.h> 34 #include <linux/interrupt.h> 35 #include <linux/clk.h> 36 #include <linux/uaccess.h> 37 #include <linux/io.h> 38 #include <linux/cpufreq.h> 39 #include <linux/slab.h> 40 #include <linux/err.h> 41 #include <linux/of.h> 42 #include <linux/mfd/syscon.h> 43 #include <linux/regmap.h> 44 45 #define S3C2410_WTCON 0x00 46 #define S3C2410_WTDAT 0x04 47 #define S3C2410_WTCNT 0x08 48 49 #define S3C2410_WTCON_RSTEN (1 << 0) 50 #define S3C2410_WTCON_INTEN (1 << 2) 51 #define S3C2410_WTCON_ENABLE (1 << 5) 52 53 #define S3C2410_WTCON_DIV16 (0 << 3) 54 #define S3C2410_WTCON_DIV32 (1 << 3) 55 #define S3C2410_WTCON_DIV64 (2 << 3) 56 #define S3C2410_WTCON_DIV128 (3 << 3) 57 58 #define S3C2410_WTCON_PRESCALE(x) ((x) << 8) 59 #define S3C2410_WTCON_PRESCALE_MASK (0xff << 8) 60 61 #define CONFIG_S3C2410_WATCHDOG_ATBOOT (0) 62 #define CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME (15) 63 64 #define EXYNOS5_RST_STAT_REG_OFFSET 0x0404 65 #define EXYNOS5_WDT_DISABLE_REG_OFFSET 0x0408 66 #define EXYNOS5_WDT_MASK_RESET_REG_OFFSET 0x040c 67 #define QUIRK_HAS_PMU_CONFIG (1 << 0) 68 #define QUIRK_HAS_RST_STAT (1 << 1) 69 70 /* These quirks require that we have a PMU register map */ 71 #define QUIRKS_HAVE_PMUREG (QUIRK_HAS_PMU_CONFIG | \ 72 QUIRK_HAS_RST_STAT) 73 74 static bool nowayout = WATCHDOG_NOWAYOUT; 75 static int tmr_margin; 76 static int tmr_atboot = CONFIG_S3C2410_WATCHDOG_ATBOOT; 77 static int soft_noboot; 78 static int debug; 79 80 module_param(tmr_margin, int, 0); 81 module_param(tmr_atboot, int, 0); 82 module_param(nowayout, bool, 0); 83 module_param(soft_noboot, int, 0); 84 module_param(debug, int, 0); 85 86 MODULE_PARM_DESC(tmr_margin, "Watchdog tmr_margin in seconds. (default=" 87 __MODULE_STRING(CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME) ")"); 88 MODULE_PARM_DESC(tmr_atboot, 89 "Watchdog is started at boot time if set to 1, default=" 90 __MODULE_STRING(CONFIG_S3C2410_WATCHDOG_ATBOOT)); 91 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" 92 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 93 MODULE_PARM_DESC(soft_noboot, "Watchdog action, set to 1 to ignore reboots, " 94 "0 to reboot (default 0)"); 95 MODULE_PARM_DESC(debug, "Watchdog debug, set to >1 for debug (default 0)"); 96 97 /** 98 * struct s3c2410_wdt_variant - Per-variant config data 99 * 100 * @disable_reg: Offset in pmureg for the register that disables the watchdog 101 * timer reset functionality. 102 * @mask_reset_reg: Offset in pmureg for the register that masks the watchdog 103 * timer reset functionality. 104 * @mask_bit: Bit number for the watchdog timer in the disable register and the 105 * mask reset register. 106 * @rst_stat_reg: Offset in pmureg for the register that has the reset status. 107 * @rst_stat_bit: Bit number in the rst_stat register indicating a watchdog 108 * reset. 109 * @quirks: A bitfield of quirks. 110 */ 111 112 struct s3c2410_wdt_variant { 113 int disable_reg; 114 int mask_reset_reg; 115 int mask_bit; 116 int rst_stat_reg; 117 int rst_stat_bit; 118 u32 quirks; 119 }; 120 121 struct s3c2410_wdt { 122 struct device *dev; 123 struct clk *clock; 124 void __iomem *reg_base; 125 unsigned int count; 126 spinlock_t lock; 127 unsigned long wtcon_save; 128 unsigned long wtdat_save; 129 struct watchdog_device wdt_device; 130 struct notifier_block freq_transition; 131 struct s3c2410_wdt_variant *drv_data; 132 struct regmap *pmureg; 133 }; 134 135 static const struct s3c2410_wdt_variant drv_data_s3c2410 = { 136 .quirks = 0 137 }; 138 139 #ifdef CONFIG_OF 140 static const struct s3c2410_wdt_variant drv_data_exynos5250 = { 141 .disable_reg = EXYNOS5_WDT_DISABLE_REG_OFFSET, 142 .mask_reset_reg = EXYNOS5_WDT_MASK_RESET_REG_OFFSET, 143 .mask_bit = 20, 144 .rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET, 145 .rst_stat_bit = 20, 146 .quirks = QUIRK_HAS_PMU_CONFIG | QUIRK_HAS_RST_STAT, 147 }; 148 149 static const struct s3c2410_wdt_variant drv_data_exynos5420 = { 150 .disable_reg = EXYNOS5_WDT_DISABLE_REG_OFFSET, 151 .mask_reset_reg = EXYNOS5_WDT_MASK_RESET_REG_OFFSET, 152 .mask_bit = 0, 153 .rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET, 154 .rst_stat_bit = 9, 155 .quirks = QUIRK_HAS_PMU_CONFIG | QUIRK_HAS_RST_STAT, 156 }; 157 158 static const struct of_device_id s3c2410_wdt_match[] = { 159 { .compatible = "samsung,s3c2410-wdt", 160 .data = &drv_data_s3c2410 }, 161 { .compatible = "samsung,exynos5250-wdt", 162 .data = &drv_data_exynos5250 }, 163 { .compatible = "samsung,exynos5420-wdt", 164 .data = &drv_data_exynos5420 }, 165 {}, 166 }; 167 MODULE_DEVICE_TABLE(of, s3c2410_wdt_match); 168 #endif 169 170 static const struct platform_device_id s3c2410_wdt_ids[] = { 171 { 172 .name = "s3c2410-wdt", 173 .driver_data = (unsigned long)&drv_data_s3c2410, 174 }, 175 {} 176 }; 177 MODULE_DEVICE_TABLE(platform, s3c2410_wdt_ids); 178 179 /* watchdog control routines */ 180 181 #define DBG(fmt, ...) \ 182 do { \ 183 if (debug) \ 184 pr_info(fmt, ##__VA_ARGS__); \ 185 } while (0) 186 187 /* functions */ 188 189 static inline struct s3c2410_wdt *freq_to_wdt(struct notifier_block *nb) 190 { 191 return container_of(nb, struct s3c2410_wdt, freq_transition); 192 } 193 194 static int s3c2410wdt_mask_and_disable_reset(struct s3c2410_wdt *wdt, bool mask) 195 { 196 int ret; 197 u32 mask_val = 1 << wdt->drv_data->mask_bit; 198 u32 val = 0; 199 200 /* No need to do anything if no PMU CONFIG needed */ 201 if (!(wdt->drv_data->quirks & QUIRK_HAS_PMU_CONFIG)) 202 return 0; 203 204 if (mask) 205 val = mask_val; 206 207 ret = regmap_update_bits(wdt->pmureg, 208 wdt->drv_data->disable_reg, 209 mask_val, val); 210 if (ret < 0) 211 goto error; 212 213 ret = regmap_update_bits(wdt->pmureg, 214 wdt->drv_data->mask_reset_reg, 215 mask_val, val); 216 error: 217 if (ret < 0) 218 dev_err(wdt->dev, "failed to update reg(%d)\n", ret); 219 220 return ret; 221 } 222 223 static int s3c2410wdt_keepalive(struct watchdog_device *wdd) 224 { 225 struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd); 226 227 spin_lock(&wdt->lock); 228 writel(wdt->count, wdt->reg_base + S3C2410_WTCNT); 229 spin_unlock(&wdt->lock); 230 231 return 0; 232 } 233 234 static void __s3c2410wdt_stop(struct s3c2410_wdt *wdt) 235 { 236 unsigned long wtcon; 237 238 wtcon = readl(wdt->reg_base + S3C2410_WTCON); 239 wtcon &= ~(S3C2410_WTCON_ENABLE | S3C2410_WTCON_RSTEN); 240 writel(wtcon, wdt->reg_base + S3C2410_WTCON); 241 } 242 243 static int s3c2410wdt_stop(struct watchdog_device *wdd) 244 { 245 struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd); 246 247 spin_lock(&wdt->lock); 248 __s3c2410wdt_stop(wdt); 249 spin_unlock(&wdt->lock); 250 251 return 0; 252 } 253 254 static int s3c2410wdt_start(struct watchdog_device *wdd) 255 { 256 unsigned long wtcon; 257 struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd); 258 259 spin_lock(&wdt->lock); 260 261 __s3c2410wdt_stop(wdt); 262 263 wtcon = readl(wdt->reg_base + S3C2410_WTCON); 264 wtcon |= S3C2410_WTCON_ENABLE | S3C2410_WTCON_DIV128; 265 266 if (soft_noboot) { 267 wtcon |= S3C2410_WTCON_INTEN; 268 wtcon &= ~S3C2410_WTCON_RSTEN; 269 } else { 270 wtcon &= ~S3C2410_WTCON_INTEN; 271 wtcon |= S3C2410_WTCON_RSTEN; 272 } 273 274 DBG("%s: count=0x%08x, wtcon=%08lx\n", 275 __func__, wdt->count, wtcon); 276 277 writel(wdt->count, wdt->reg_base + S3C2410_WTDAT); 278 writel(wdt->count, wdt->reg_base + S3C2410_WTCNT); 279 writel(wtcon, wdt->reg_base + S3C2410_WTCON); 280 spin_unlock(&wdt->lock); 281 282 return 0; 283 } 284 285 static inline int s3c2410wdt_is_running(struct s3c2410_wdt *wdt) 286 { 287 return readl(wdt->reg_base + S3C2410_WTCON) & S3C2410_WTCON_ENABLE; 288 } 289 290 static int s3c2410wdt_set_heartbeat(struct watchdog_device *wdd, unsigned timeout) 291 { 292 struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd); 293 unsigned long freq = clk_get_rate(wdt->clock); 294 unsigned int count; 295 unsigned int divisor = 1; 296 unsigned long wtcon; 297 298 if (timeout < 1) 299 return -EINVAL; 300 301 freq = DIV_ROUND_UP(freq, 128); 302 count = timeout * freq; 303 304 DBG("%s: count=%d, timeout=%d, freq=%lu\n", 305 __func__, count, timeout, freq); 306 307 /* if the count is bigger than the watchdog register, 308 then work out what we need to do (and if) we can 309 actually make this value 310 */ 311 312 if (count >= 0x10000) { 313 divisor = DIV_ROUND_UP(count, 0xffff); 314 315 if (divisor > 0x100) { 316 dev_err(wdt->dev, "timeout %d too big\n", timeout); 317 return -EINVAL; 318 } 319 } 320 321 DBG("%s: timeout=%d, divisor=%d, count=%d (%08x)\n", 322 __func__, timeout, divisor, count, DIV_ROUND_UP(count, divisor)); 323 324 count = DIV_ROUND_UP(count, divisor); 325 wdt->count = count; 326 327 /* update the pre-scaler */ 328 wtcon = readl(wdt->reg_base + S3C2410_WTCON); 329 wtcon &= ~S3C2410_WTCON_PRESCALE_MASK; 330 wtcon |= S3C2410_WTCON_PRESCALE(divisor-1); 331 332 writel(count, wdt->reg_base + S3C2410_WTDAT); 333 writel(wtcon, wdt->reg_base + S3C2410_WTCON); 334 335 wdd->timeout = (count * divisor) / freq; 336 337 return 0; 338 } 339 340 #define OPTIONS (WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE) 341 342 static const struct watchdog_info s3c2410_wdt_ident = { 343 .options = OPTIONS, 344 .firmware_version = 0, 345 .identity = "S3C2410 Watchdog", 346 }; 347 348 static struct watchdog_ops s3c2410wdt_ops = { 349 .owner = THIS_MODULE, 350 .start = s3c2410wdt_start, 351 .stop = s3c2410wdt_stop, 352 .ping = s3c2410wdt_keepalive, 353 .set_timeout = s3c2410wdt_set_heartbeat, 354 }; 355 356 static struct watchdog_device s3c2410_wdd = { 357 .info = &s3c2410_wdt_ident, 358 .ops = &s3c2410wdt_ops, 359 .timeout = CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME, 360 }; 361 362 /* interrupt handler code */ 363 364 static irqreturn_t s3c2410wdt_irq(int irqno, void *param) 365 { 366 struct s3c2410_wdt *wdt = platform_get_drvdata(param); 367 368 dev_info(wdt->dev, "watchdog timer expired (irq)\n"); 369 370 s3c2410wdt_keepalive(&wdt->wdt_device); 371 return IRQ_HANDLED; 372 } 373 374 #ifdef CONFIG_ARM_S3C24XX_CPUFREQ 375 376 static int s3c2410wdt_cpufreq_transition(struct notifier_block *nb, 377 unsigned long val, void *data) 378 { 379 int ret; 380 struct s3c2410_wdt *wdt = freq_to_wdt(nb); 381 382 if (!s3c2410wdt_is_running(wdt)) 383 goto done; 384 385 if (val == CPUFREQ_PRECHANGE) { 386 /* To ensure that over the change we don't cause the 387 * watchdog to trigger, we perform an keep-alive if 388 * the watchdog is running. 389 */ 390 391 s3c2410wdt_keepalive(&wdt->wdt_device); 392 } else if (val == CPUFREQ_POSTCHANGE) { 393 s3c2410wdt_stop(&wdt->wdt_device); 394 395 ret = s3c2410wdt_set_heartbeat(&wdt->wdt_device, 396 wdt->wdt_device.timeout); 397 398 if (ret >= 0) 399 s3c2410wdt_start(&wdt->wdt_device); 400 else 401 goto err; 402 } 403 404 done: 405 return 0; 406 407 err: 408 dev_err(wdt->dev, "cannot set new value for timeout %d\n", 409 wdt->wdt_device.timeout); 410 return ret; 411 } 412 413 static inline int s3c2410wdt_cpufreq_register(struct s3c2410_wdt *wdt) 414 { 415 wdt->freq_transition.notifier_call = s3c2410wdt_cpufreq_transition; 416 417 return cpufreq_register_notifier(&wdt->freq_transition, 418 CPUFREQ_TRANSITION_NOTIFIER); 419 } 420 421 static inline void s3c2410wdt_cpufreq_deregister(struct s3c2410_wdt *wdt) 422 { 423 wdt->freq_transition.notifier_call = s3c2410wdt_cpufreq_transition; 424 425 cpufreq_unregister_notifier(&wdt->freq_transition, 426 CPUFREQ_TRANSITION_NOTIFIER); 427 } 428 429 #else 430 431 static inline int s3c2410wdt_cpufreq_register(struct s3c2410_wdt *wdt) 432 { 433 return 0; 434 } 435 436 static inline void s3c2410wdt_cpufreq_deregister(struct s3c2410_wdt *wdt) 437 { 438 } 439 #endif 440 441 static inline unsigned int s3c2410wdt_get_bootstatus(struct s3c2410_wdt *wdt) 442 { 443 unsigned int rst_stat; 444 int ret; 445 446 if (!(wdt->drv_data->quirks & QUIRK_HAS_RST_STAT)) 447 return 0; 448 449 ret = regmap_read(wdt->pmureg, wdt->drv_data->rst_stat_reg, &rst_stat); 450 if (ret) 451 dev_warn(wdt->dev, "Couldn't get RST_STAT register\n"); 452 else if (rst_stat & BIT(wdt->drv_data->rst_stat_bit)) 453 return WDIOF_CARDRESET; 454 455 return 0; 456 } 457 458 /* s3c2410_get_wdt_driver_data */ 459 static inline struct s3c2410_wdt_variant * 460 get_wdt_drv_data(struct platform_device *pdev) 461 { 462 if (pdev->dev.of_node) { 463 const struct of_device_id *match; 464 match = of_match_node(s3c2410_wdt_match, pdev->dev.of_node); 465 return (struct s3c2410_wdt_variant *)match->data; 466 } else { 467 return (struct s3c2410_wdt_variant *) 468 platform_get_device_id(pdev)->driver_data; 469 } 470 } 471 472 static int s3c2410wdt_probe(struct platform_device *pdev) 473 { 474 struct device *dev; 475 struct s3c2410_wdt *wdt; 476 struct resource *wdt_mem; 477 struct resource *wdt_irq; 478 unsigned int wtcon; 479 int started = 0; 480 int ret; 481 482 DBG("%s: probe=%p\n", __func__, pdev); 483 484 dev = &pdev->dev; 485 486 wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL); 487 if (!wdt) 488 return -ENOMEM; 489 490 wdt->dev = &pdev->dev; 491 spin_lock_init(&wdt->lock); 492 wdt->wdt_device = s3c2410_wdd; 493 494 wdt->drv_data = get_wdt_drv_data(pdev); 495 if (wdt->drv_data->quirks & QUIRKS_HAVE_PMUREG) { 496 wdt->pmureg = syscon_regmap_lookup_by_phandle(dev->of_node, 497 "samsung,syscon-phandle"); 498 if (IS_ERR(wdt->pmureg)) { 499 dev_err(dev, "syscon regmap lookup failed.\n"); 500 return PTR_ERR(wdt->pmureg); 501 } 502 } 503 504 wdt_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); 505 if (wdt_irq == NULL) { 506 dev_err(dev, "no irq resource specified\n"); 507 ret = -ENOENT; 508 goto err; 509 } 510 511 /* get the memory region for the watchdog timer */ 512 wdt_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 513 wdt->reg_base = devm_ioremap_resource(dev, wdt_mem); 514 if (IS_ERR(wdt->reg_base)) { 515 ret = PTR_ERR(wdt->reg_base); 516 goto err; 517 } 518 519 DBG("probe: mapped reg_base=%p\n", wdt->reg_base); 520 521 wdt->clock = devm_clk_get(dev, "watchdog"); 522 if (IS_ERR(wdt->clock)) { 523 dev_err(dev, "failed to find watchdog clock source\n"); 524 ret = PTR_ERR(wdt->clock); 525 goto err; 526 } 527 528 ret = clk_prepare_enable(wdt->clock); 529 if (ret < 0) { 530 dev_err(dev, "failed to enable clock\n"); 531 return ret; 532 } 533 534 ret = s3c2410wdt_cpufreq_register(wdt); 535 if (ret < 0) { 536 dev_err(dev, "failed to register cpufreq\n"); 537 goto err_clk; 538 } 539 540 watchdog_set_drvdata(&wdt->wdt_device, wdt); 541 542 /* see if we can actually set the requested timer margin, and if 543 * not, try the default value */ 544 545 watchdog_init_timeout(&wdt->wdt_device, tmr_margin, &pdev->dev); 546 ret = s3c2410wdt_set_heartbeat(&wdt->wdt_device, 547 wdt->wdt_device.timeout); 548 if (ret) { 549 started = s3c2410wdt_set_heartbeat(&wdt->wdt_device, 550 CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME); 551 552 if (started == 0) 553 dev_info(dev, 554 "tmr_margin value out of range, default %d used\n", 555 CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME); 556 else 557 dev_info(dev, "default timer value is out of range, " 558 "cannot start\n"); 559 } 560 561 ret = devm_request_irq(dev, wdt_irq->start, s3c2410wdt_irq, 0, 562 pdev->name, pdev); 563 if (ret != 0) { 564 dev_err(dev, "failed to install irq (%d)\n", ret); 565 goto err_cpufreq; 566 } 567 568 watchdog_set_nowayout(&wdt->wdt_device, nowayout); 569 570 wdt->wdt_device.bootstatus = s3c2410wdt_get_bootstatus(wdt); 571 572 ret = watchdog_register_device(&wdt->wdt_device); 573 if (ret) { 574 dev_err(dev, "cannot register watchdog (%d)\n", ret); 575 goto err_cpufreq; 576 } 577 578 ret = s3c2410wdt_mask_and_disable_reset(wdt, false); 579 if (ret < 0) 580 goto err_unregister; 581 582 if (tmr_atboot && started == 0) { 583 dev_info(dev, "starting watchdog timer\n"); 584 s3c2410wdt_start(&wdt->wdt_device); 585 } else if (!tmr_atboot) { 586 /* if we're not enabling the watchdog, then ensure it is 587 * disabled if it has been left running from the bootloader 588 * or other source */ 589 590 s3c2410wdt_stop(&wdt->wdt_device); 591 } 592 593 platform_set_drvdata(pdev, wdt); 594 595 /* print out a statement of readiness */ 596 597 wtcon = readl(wdt->reg_base + S3C2410_WTCON); 598 599 dev_info(dev, "watchdog %sactive, reset %sabled, irq %sabled\n", 600 (wtcon & S3C2410_WTCON_ENABLE) ? "" : "in", 601 (wtcon & S3C2410_WTCON_RSTEN) ? "en" : "dis", 602 (wtcon & S3C2410_WTCON_INTEN) ? "en" : "dis"); 603 604 return 0; 605 606 err_unregister: 607 watchdog_unregister_device(&wdt->wdt_device); 608 609 err_cpufreq: 610 s3c2410wdt_cpufreq_deregister(wdt); 611 612 err_clk: 613 clk_disable_unprepare(wdt->clock); 614 615 err: 616 return ret; 617 } 618 619 static int s3c2410wdt_remove(struct platform_device *dev) 620 { 621 int ret; 622 struct s3c2410_wdt *wdt = platform_get_drvdata(dev); 623 624 ret = s3c2410wdt_mask_and_disable_reset(wdt, true); 625 if (ret < 0) 626 return ret; 627 628 watchdog_unregister_device(&wdt->wdt_device); 629 630 s3c2410wdt_cpufreq_deregister(wdt); 631 632 clk_disable_unprepare(wdt->clock); 633 634 return 0; 635 } 636 637 static void s3c2410wdt_shutdown(struct platform_device *dev) 638 { 639 struct s3c2410_wdt *wdt = platform_get_drvdata(dev); 640 641 s3c2410wdt_mask_and_disable_reset(wdt, true); 642 643 s3c2410wdt_stop(&wdt->wdt_device); 644 } 645 646 #ifdef CONFIG_PM_SLEEP 647 648 static int s3c2410wdt_suspend(struct device *dev) 649 { 650 int ret; 651 struct s3c2410_wdt *wdt = dev_get_drvdata(dev); 652 653 /* Save watchdog state, and turn it off. */ 654 wdt->wtcon_save = readl(wdt->reg_base + S3C2410_WTCON); 655 wdt->wtdat_save = readl(wdt->reg_base + S3C2410_WTDAT); 656 657 ret = s3c2410wdt_mask_and_disable_reset(wdt, true); 658 if (ret < 0) 659 return ret; 660 661 /* Note that WTCNT doesn't need to be saved. */ 662 s3c2410wdt_stop(&wdt->wdt_device); 663 664 return 0; 665 } 666 667 static int s3c2410wdt_resume(struct device *dev) 668 { 669 int ret; 670 struct s3c2410_wdt *wdt = dev_get_drvdata(dev); 671 672 /* Restore watchdog state. */ 673 writel(wdt->wtdat_save, wdt->reg_base + S3C2410_WTDAT); 674 writel(wdt->wtdat_save, wdt->reg_base + S3C2410_WTCNT);/* Reset count */ 675 writel(wdt->wtcon_save, wdt->reg_base + S3C2410_WTCON); 676 677 ret = s3c2410wdt_mask_and_disable_reset(wdt, false); 678 if (ret < 0) 679 return ret; 680 681 dev_info(dev, "watchdog %sabled\n", 682 (wdt->wtcon_save & S3C2410_WTCON_ENABLE) ? "en" : "dis"); 683 684 return 0; 685 } 686 #endif 687 688 static SIMPLE_DEV_PM_OPS(s3c2410wdt_pm_ops, s3c2410wdt_suspend, 689 s3c2410wdt_resume); 690 691 static struct platform_driver s3c2410wdt_driver = { 692 .probe = s3c2410wdt_probe, 693 .remove = s3c2410wdt_remove, 694 .shutdown = s3c2410wdt_shutdown, 695 .id_table = s3c2410_wdt_ids, 696 .driver = { 697 .owner = THIS_MODULE, 698 .name = "s3c2410-wdt", 699 .pm = &s3c2410wdt_pm_ops, 700 .of_match_table = of_match_ptr(s3c2410_wdt_match), 701 }, 702 }; 703 704 module_platform_driver(s3c2410wdt_driver); 705 706 MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>, " 707 "Dimitry Andric <dimitry.andric@tomtom.com>"); 708 MODULE_DESCRIPTION("S3C2410 Watchdog Device Driver"); 709 MODULE_LICENSE("GPL"); 710