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