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