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