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