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