s3c2410_wdt.c (24951B)
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 EXYNOS850_CLUSTER0_NONCPU_OUT 0x1220 60#define EXYNOS850_CLUSTER0_NONCPU_INT_EN 0x1244 61#define EXYNOS850_CLUSTER1_NONCPU_OUT 0x1620 62#define EXYNOS850_CLUSTER1_NONCPU_INT_EN 0x1644 63 64#define EXYNOS850_CLUSTER0_WDTRESET_BIT 24 65#define EXYNOS850_CLUSTER1_WDTRESET_BIT 23 66 67/** 68 * DOC: Quirk flags for different Samsung watchdog IP-cores 69 * 70 * This driver supports multiple Samsung SoCs, each of which might have 71 * different set of registers and features supported. As watchdog block 72 * sometimes requires modifying PMU registers for proper functioning, register 73 * differences in both watchdog and PMU IP-cores should be accounted for. Quirk 74 * flags described below serve the purpose of telling the driver about mentioned 75 * SoC traits, and can be specified in driver data for each particular supported 76 * device. 77 * 78 * %QUIRK_HAS_WTCLRINT_REG: Watchdog block has WTCLRINT register. It's used to 79 * clear the interrupt once the interrupt service routine is complete. It's 80 * write-only, writing any values to this register clears the interrupt, but 81 * reading is not permitted. 82 * 83 * %QUIRK_HAS_PMU_MASK_RESET: PMU block has the register for disabling/enabling 84 * WDT reset request. On old SoCs it's usually called MASK_WDT_RESET_REQUEST, 85 * new SoCs have CLUSTERx_NONCPU_INT_EN register, which 'mask_bit' value is 86 * inverted compared to the former one. 87 * 88 * %QUIRK_HAS_PMU_RST_STAT: PMU block has RST_STAT (reset status) register, 89 * which contains bits indicating the reason for most recent CPU reset. If 90 * present, driver will use this register to check if previous reboot was due to 91 * watchdog timer reset. 92 * 93 * %QUIRK_HAS_PMU_AUTO_DISABLE: PMU block has AUTOMATIC_WDT_RESET_DISABLE 94 * register. If 'mask_bit' bit is set, PMU will disable WDT reset when 95 * corresponding processor is in reset state. 96 * 97 * %QUIRK_HAS_PMU_CNT_EN: PMU block has some register (e.g. CLUSTERx_NONCPU_OUT) 98 * with "watchdog counter enable" bit. That bit should be set to make watchdog 99 * counter running. 100 */ 101#define QUIRK_HAS_WTCLRINT_REG (1 << 0) 102#define QUIRK_HAS_PMU_MASK_RESET (1 << 1) 103#define QUIRK_HAS_PMU_RST_STAT (1 << 2) 104#define QUIRK_HAS_PMU_AUTO_DISABLE (1 << 3) 105#define QUIRK_HAS_PMU_CNT_EN (1 << 4) 106 107/* These quirks require that we have a PMU register map */ 108#define QUIRKS_HAVE_PMUREG \ 109 (QUIRK_HAS_PMU_MASK_RESET | QUIRK_HAS_PMU_RST_STAT | \ 110 QUIRK_HAS_PMU_AUTO_DISABLE | QUIRK_HAS_PMU_CNT_EN) 111 112static bool nowayout = WATCHDOG_NOWAYOUT; 113static int tmr_margin; 114static int tmr_atboot = S3C2410_WATCHDOG_ATBOOT; 115static int soft_noboot; 116 117module_param(tmr_margin, int, 0); 118module_param(tmr_atboot, int, 0); 119module_param(nowayout, bool, 0); 120module_param(soft_noboot, int, 0); 121 122MODULE_PARM_DESC(tmr_margin, "Watchdog tmr_margin in seconds. (default=" 123 __MODULE_STRING(S3C2410_WATCHDOG_DEFAULT_TIME) ")"); 124MODULE_PARM_DESC(tmr_atboot, 125 "Watchdog is started at boot time if set to 1, default=" 126 __MODULE_STRING(S3C2410_WATCHDOG_ATBOOT)); 127MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" 128 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 129MODULE_PARM_DESC(soft_noboot, "Watchdog action, set to 1 to ignore reboots, 0 to reboot (default 0)"); 130 131/** 132 * struct s3c2410_wdt_variant - Per-variant config data 133 * 134 * @disable_reg: Offset in pmureg for the register that disables the watchdog 135 * timer reset functionality. 136 * @mask_reset_reg: Offset in pmureg for the register that masks the watchdog 137 * timer reset functionality. 138 * @mask_reset_inv: If set, mask_reset_reg value will have inverted meaning. 139 * @mask_bit: Bit number for the watchdog timer in the disable register and the 140 * mask reset register. 141 * @rst_stat_reg: Offset in pmureg for the register that has the reset status. 142 * @rst_stat_bit: Bit number in the rst_stat register indicating a watchdog 143 * reset. 144 * @cnt_en_reg: Offset in pmureg for the register that enables WDT counter. 145 * @cnt_en_bit: Bit number for "watchdog counter enable" in cnt_en register. 146 * @quirks: A bitfield of quirks. 147 */ 148 149struct s3c2410_wdt_variant { 150 int disable_reg; 151 int mask_reset_reg; 152 bool mask_reset_inv; 153 int mask_bit; 154 int rst_stat_reg; 155 int rst_stat_bit; 156 int cnt_en_reg; 157 int cnt_en_bit; 158 u32 quirks; 159}; 160 161struct s3c2410_wdt { 162 struct device *dev; 163 struct clk *bus_clk; /* for register interface (PCLK) */ 164 struct clk *src_clk; /* for WDT counter */ 165 void __iomem *reg_base; 166 unsigned int count; 167 spinlock_t lock; 168 unsigned long wtcon_save; 169 unsigned long wtdat_save; 170 struct watchdog_device wdt_device; 171 struct notifier_block freq_transition; 172 const struct s3c2410_wdt_variant *drv_data; 173 struct regmap *pmureg; 174}; 175 176static const struct s3c2410_wdt_variant drv_data_s3c2410 = { 177 .quirks = 0 178}; 179 180#ifdef CONFIG_OF 181static const struct s3c2410_wdt_variant drv_data_s3c6410 = { 182 .quirks = QUIRK_HAS_WTCLRINT_REG, 183}; 184 185static const struct s3c2410_wdt_variant drv_data_exynos5250 = { 186 .disable_reg = EXYNOS5_WDT_DISABLE_REG_OFFSET, 187 .mask_reset_reg = EXYNOS5_WDT_MASK_RESET_REG_OFFSET, 188 .mask_bit = 20, 189 .rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET, 190 .rst_stat_bit = 20, 191 .quirks = QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_MASK_RESET | \ 192 QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_AUTO_DISABLE, 193}; 194 195static const struct s3c2410_wdt_variant drv_data_exynos5420 = { 196 .disable_reg = EXYNOS5_WDT_DISABLE_REG_OFFSET, 197 .mask_reset_reg = EXYNOS5_WDT_MASK_RESET_REG_OFFSET, 198 .mask_bit = 0, 199 .rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET, 200 .rst_stat_bit = 9, 201 .quirks = QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_MASK_RESET | \ 202 QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_AUTO_DISABLE, 203}; 204 205static const struct s3c2410_wdt_variant drv_data_exynos7 = { 206 .disable_reg = EXYNOS5_WDT_DISABLE_REG_OFFSET, 207 .mask_reset_reg = EXYNOS5_WDT_MASK_RESET_REG_OFFSET, 208 .mask_bit = 23, 209 .rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET, 210 .rst_stat_bit = 23, /* A57 WDTRESET */ 211 .quirks = QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_MASK_RESET | \ 212 QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_AUTO_DISABLE, 213}; 214 215static const struct s3c2410_wdt_variant drv_data_exynos850_cl0 = { 216 .mask_reset_reg = EXYNOS850_CLUSTER0_NONCPU_INT_EN, 217 .mask_bit = 2, 218 .mask_reset_inv = true, 219 .rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET, 220 .rst_stat_bit = EXYNOS850_CLUSTER0_WDTRESET_BIT, 221 .cnt_en_reg = EXYNOS850_CLUSTER0_NONCPU_OUT, 222 .cnt_en_bit = 7, 223 .quirks = QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_MASK_RESET | \ 224 QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_CNT_EN, 225}; 226 227static const struct s3c2410_wdt_variant drv_data_exynos850_cl1 = { 228 .mask_reset_reg = EXYNOS850_CLUSTER1_NONCPU_INT_EN, 229 .mask_bit = 2, 230 .mask_reset_inv = true, 231 .rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET, 232 .rst_stat_bit = EXYNOS850_CLUSTER1_WDTRESET_BIT, 233 .cnt_en_reg = EXYNOS850_CLUSTER1_NONCPU_OUT, 234 .cnt_en_bit = 7, 235 .quirks = QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_MASK_RESET | \ 236 QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_CNT_EN, 237}; 238 239static const struct of_device_id s3c2410_wdt_match[] = { 240 { .compatible = "samsung,s3c2410-wdt", 241 .data = &drv_data_s3c2410 }, 242 { .compatible = "samsung,s3c6410-wdt", 243 .data = &drv_data_s3c6410 }, 244 { .compatible = "samsung,exynos5250-wdt", 245 .data = &drv_data_exynos5250 }, 246 { .compatible = "samsung,exynos5420-wdt", 247 .data = &drv_data_exynos5420 }, 248 { .compatible = "samsung,exynos7-wdt", 249 .data = &drv_data_exynos7 }, 250 { .compatible = "samsung,exynos850-wdt", 251 .data = &drv_data_exynos850_cl0 }, 252 {}, 253}; 254MODULE_DEVICE_TABLE(of, s3c2410_wdt_match); 255#endif 256 257static const struct platform_device_id s3c2410_wdt_ids[] = { 258 { 259 .name = "s3c2410-wdt", 260 .driver_data = (unsigned long)&drv_data_s3c2410, 261 }, 262 {} 263}; 264MODULE_DEVICE_TABLE(platform, s3c2410_wdt_ids); 265 266/* functions */ 267 268static inline unsigned long s3c2410wdt_get_freq(struct s3c2410_wdt *wdt) 269{ 270 return clk_get_rate(wdt->src_clk ? wdt->src_clk : wdt->bus_clk); 271} 272 273static inline unsigned int s3c2410wdt_max_timeout(struct s3c2410_wdt *wdt) 274{ 275 const unsigned long freq = s3c2410wdt_get_freq(wdt); 276 277 return S3C2410_WTCNT_MAXCNT / (freq / (S3C2410_WTCON_PRESCALE_MAX + 1) 278 / S3C2410_WTCON_MAXDIV); 279} 280 281static inline struct s3c2410_wdt *freq_to_wdt(struct notifier_block *nb) 282{ 283 return container_of(nb, struct s3c2410_wdt, freq_transition); 284} 285 286static int s3c2410wdt_disable_wdt_reset(struct s3c2410_wdt *wdt, bool mask) 287{ 288 const u32 mask_val = BIT(wdt->drv_data->mask_bit); 289 const u32 val = mask ? mask_val : 0; 290 int ret; 291 292 ret = regmap_update_bits(wdt->pmureg, wdt->drv_data->disable_reg, 293 mask_val, val); 294 if (ret < 0) 295 dev_err(wdt->dev, "failed to update reg(%d)\n", ret); 296 297 return ret; 298} 299 300static int s3c2410wdt_mask_wdt_reset(struct s3c2410_wdt *wdt, bool mask) 301{ 302 const u32 mask_val = BIT(wdt->drv_data->mask_bit); 303 const bool val_inv = wdt->drv_data->mask_reset_inv; 304 const u32 val = (mask ^ val_inv) ? mask_val : 0; 305 int ret; 306 307 ret = regmap_update_bits(wdt->pmureg, wdt->drv_data->mask_reset_reg, 308 mask_val, val); 309 if (ret < 0) 310 dev_err(wdt->dev, "failed to update reg(%d)\n", ret); 311 312 return ret; 313} 314 315static int s3c2410wdt_enable_counter(struct s3c2410_wdt *wdt, bool en) 316{ 317 const u32 mask_val = BIT(wdt->drv_data->cnt_en_bit); 318 const u32 val = en ? mask_val : 0; 319 int ret; 320 321 ret = regmap_update_bits(wdt->pmureg, wdt->drv_data->cnt_en_reg, 322 mask_val, val); 323 if (ret < 0) 324 dev_err(wdt->dev, "failed to update reg(%d)\n", ret); 325 326 return ret; 327} 328 329static int s3c2410wdt_enable(struct s3c2410_wdt *wdt, bool en) 330{ 331 int ret; 332 333 if (wdt->drv_data->quirks & QUIRK_HAS_PMU_AUTO_DISABLE) { 334 ret = s3c2410wdt_disable_wdt_reset(wdt, !en); 335 if (ret < 0) 336 return ret; 337 } 338 339 if (wdt->drv_data->quirks & QUIRK_HAS_PMU_MASK_RESET) { 340 ret = s3c2410wdt_mask_wdt_reset(wdt, !en); 341 if (ret < 0) 342 return ret; 343 } 344 345 if (wdt->drv_data->quirks & QUIRK_HAS_PMU_CNT_EN) { 346 ret = s3c2410wdt_enable_counter(wdt, en); 347 if (ret < 0) 348 return ret; 349 } 350 351 return 0; 352} 353 354static int s3c2410wdt_keepalive(struct watchdog_device *wdd) 355{ 356 struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd); 357 358 spin_lock(&wdt->lock); 359 writel(wdt->count, wdt->reg_base + S3C2410_WTCNT); 360 spin_unlock(&wdt->lock); 361 362 return 0; 363} 364 365static void __s3c2410wdt_stop(struct s3c2410_wdt *wdt) 366{ 367 unsigned long wtcon; 368 369 wtcon = readl(wdt->reg_base + S3C2410_WTCON); 370 wtcon &= ~(S3C2410_WTCON_ENABLE | S3C2410_WTCON_RSTEN); 371 writel(wtcon, wdt->reg_base + S3C2410_WTCON); 372} 373 374static int s3c2410wdt_stop(struct watchdog_device *wdd) 375{ 376 struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd); 377 378 spin_lock(&wdt->lock); 379 __s3c2410wdt_stop(wdt); 380 spin_unlock(&wdt->lock); 381 382 return 0; 383} 384 385static int s3c2410wdt_start(struct watchdog_device *wdd) 386{ 387 unsigned long wtcon; 388 struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd); 389 390 spin_lock(&wdt->lock); 391 392 __s3c2410wdt_stop(wdt); 393 394 wtcon = readl(wdt->reg_base + S3C2410_WTCON); 395 wtcon |= S3C2410_WTCON_ENABLE | S3C2410_WTCON_DIV128; 396 397 if (soft_noboot) { 398 wtcon |= S3C2410_WTCON_INTEN; 399 wtcon &= ~S3C2410_WTCON_RSTEN; 400 } else { 401 wtcon &= ~S3C2410_WTCON_INTEN; 402 wtcon |= S3C2410_WTCON_RSTEN; 403 } 404 405 dev_dbg(wdt->dev, "Starting watchdog: count=0x%08x, wtcon=%08lx\n", 406 wdt->count, wtcon); 407 408 writel(wdt->count, wdt->reg_base + S3C2410_WTDAT); 409 writel(wdt->count, wdt->reg_base + S3C2410_WTCNT); 410 writel(wtcon, wdt->reg_base + S3C2410_WTCON); 411 spin_unlock(&wdt->lock); 412 413 return 0; 414} 415 416static inline int s3c2410wdt_is_running(struct s3c2410_wdt *wdt) 417{ 418 return readl(wdt->reg_base + S3C2410_WTCON) & S3C2410_WTCON_ENABLE; 419} 420 421static int s3c2410wdt_set_heartbeat(struct watchdog_device *wdd, 422 unsigned int timeout) 423{ 424 struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd); 425 unsigned long freq = s3c2410wdt_get_freq(wdt); 426 unsigned int count; 427 unsigned int divisor = 1; 428 unsigned long wtcon; 429 430 if (timeout < 1) 431 return -EINVAL; 432 433 freq = DIV_ROUND_UP(freq, 128); 434 count = timeout * freq; 435 436 dev_dbg(wdt->dev, "Heartbeat: count=%d, timeout=%d, freq=%lu\n", 437 count, timeout, freq); 438 439 /* if the count is bigger than the watchdog register, 440 then work out what we need to do (and if) we can 441 actually make this value 442 */ 443 444 if (count >= 0x10000) { 445 divisor = DIV_ROUND_UP(count, 0xffff); 446 447 if (divisor > 0x100) { 448 dev_err(wdt->dev, "timeout %d too big\n", timeout); 449 return -EINVAL; 450 } 451 } 452 453 dev_dbg(wdt->dev, "Heartbeat: timeout=%d, divisor=%d, count=%d (%08x)\n", 454 timeout, divisor, count, DIV_ROUND_UP(count, divisor)); 455 456 count = DIV_ROUND_UP(count, divisor); 457 wdt->count = count; 458 459 /* update the pre-scaler */ 460 wtcon = readl(wdt->reg_base + S3C2410_WTCON); 461 wtcon &= ~S3C2410_WTCON_PRESCALE_MASK; 462 wtcon |= S3C2410_WTCON_PRESCALE(divisor-1); 463 464 writel(count, wdt->reg_base + S3C2410_WTDAT); 465 writel(wtcon, wdt->reg_base + S3C2410_WTCON); 466 467 wdd->timeout = (count * divisor) / freq; 468 469 return 0; 470} 471 472static int s3c2410wdt_restart(struct watchdog_device *wdd, unsigned long action, 473 void *data) 474{ 475 struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd); 476 void __iomem *wdt_base = wdt->reg_base; 477 478 /* disable watchdog, to be safe */ 479 writel(0, wdt_base + S3C2410_WTCON); 480 481 /* put initial values into count and data */ 482 writel(0x80, wdt_base + S3C2410_WTCNT); 483 writel(0x80, wdt_base + S3C2410_WTDAT); 484 485 /* set the watchdog to go and reset... */ 486 writel(S3C2410_WTCON_ENABLE | S3C2410_WTCON_DIV16 | 487 S3C2410_WTCON_RSTEN | S3C2410_WTCON_PRESCALE(0x20), 488 wdt_base + S3C2410_WTCON); 489 490 /* wait for reset to assert... */ 491 mdelay(500); 492 493 return 0; 494} 495 496#define OPTIONS (WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE) 497 498static const struct watchdog_info s3c2410_wdt_ident = { 499 .options = OPTIONS, 500 .firmware_version = 0, 501 .identity = "S3C2410 Watchdog", 502}; 503 504static const struct watchdog_ops s3c2410wdt_ops = { 505 .owner = THIS_MODULE, 506 .start = s3c2410wdt_start, 507 .stop = s3c2410wdt_stop, 508 .ping = s3c2410wdt_keepalive, 509 .set_timeout = s3c2410wdt_set_heartbeat, 510 .restart = s3c2410wdt_restart, 511}; 512 513static const struct watchdog_device s3c2410_wdd = { 514 .info = &s3c2410_wdt_ident, 515 .ops = &s3c2410wdt_ops, 516 .timeout = S3C2410_WATCHDOG_DEFAULT_TIME, 517}; 518 519/* interrupt handler code */ 520 521static irqreturn_t s3c2410wdt_irq(int irqno, void *param) 522{ 523 struct s3c2410_wdt *wdt = platform_get_drvdata(param); 524 525 dev_info(wdt->dev, "watchdog timer expired (irq)\n"); 526 527 s3c2410wdt_keepalive(&wdt->wdt_device); 528 529 if (wdt->drv_data->quirks & QUIRK_HAS_WTCLRINT_REG) 530 writel(0x1, wdt->reg_base + S3C2410_WTCLRINT); 531 532 return IRQ_HANDLED; 533} 534 535#ifdef CONFIG_ARM_S3C24XX_CPUFREQ 536 537static int s3c2410wdt_cpufreq_transition(struct notifier_block *nb, 538 unsigned long val, void *data) 539{ 540 int ret; 541 struct s3c2410_wdt *wdt = freq_to_wdt(nb); 542 543 if (!s3c2410wdt_is_running(wdt)) 544 goto done; 545 546 if (val == CPUFREQ_PRECHANGE) { 547 /* To ensure that over the change we don't cause the 548 * watchdog to trigger, we perform an keep-alive if 549 * the watchdog is running. 550 */ 551 552 s3c2410wdt_keepalive(&wdt->wdt_device); 553 } else if (val == CPUFREQ_POSTCHANGE) { 554 s3c2410wdt_stop(&wdt->wdt_device); 555 556 ret = s3c2410wdt_set_heartbeat(&wdt->wdt_device, 557 wdt->wdt_device.timeout); 558 559 if (ret >= 0) 560 s3c2410wdt_start(&wdt->wdt_device); 561 else 562 goto err; 563 } 564 565done: 566 return 0; 567 568 err: 569 dev_err(wdt->dev, "cannot set new value for timeout %d\n", 570 wdt->wdt_device.timeout); 571 return ret; 572} 573 574static inline int s3c2410wdt_cpufreq_register(struct s3c2410_wdt *wdt) 575{ 576 wdt->freq_transition.notifier_call = s3c2410wdt_cpufreq_transition; 577 578 return cpufreq_register_notifier(&wdt->freq_transition, 579 CPUFREQ_TRANSITION_NOTIFIER); 580} 581 582static inline void s3c2410wdt_cpufreq_deregister(struct s3c2410_wdt *wdt) 583{ 584 wdt->freq_transition.notifier_call = s3c2410wdt_cpufreq_transition; 585 586 cpufreq_unregister_notifier(&wdt->freq_transition, 587 CPUFREQ_TRANSITION_NOTIFIER); 588} 589 590#else 591 592static inline int s3c2410wdt_cpufreq_register(struct s3c2410_wdt *wdt) 593{ 594 return 0; 595} 596 597static inline void s3c2410wdt_cpufreq_deregister(struct s3c2410_wdt *wdt) 598{ 599} 600#endif 601 602static inline unsigned int s3c2410wdt_get_bootstatus(struct s3c2410_wdt *wdt) 603{ 604 unsigned int rst_stat; 605 int ret; 606 607 if (!(wdt->drv_data->quirks & QUIRK_HAS_PMU_RST_STAT)) 608 return 0; 609 610 ret = regmap_read(wdt->pmureg, wdt->drv_data->rst_stat_reg, &rst_stat); 611 if (ret) 612 dev_warn(wdt->dev, "Couldn't get RST_STAT register\n"); 613 else if (rst_stat & BIT(wdt->drv_data->rst_stat_bit)) 614 return WDIOF_CARDRESET; 615 616 return 0; 617} 618 619static inline const struct s3c2410_wdt_variant * 620s3c2410_get_wdt_drv_data(struct platform_device *pdev) 621{ 622 const struct s3c2410_wdt_variant *variant; 623 struct device *dev = &pdev->dev; 624 625 variant = of_device_get_match_data(dev); 626 if (!variant) { 627 /* Device matched by platform_device_id */ 628 variant = (struct s3c2410_wdt_variant *) 629 platform_get_device_id(pdev)->driver_data; 630 } 631 632#ifdef CONFIG_OF 633 /* Choose Exynos850 driver data w.r.t. cluster index */ 634 if (variant == &drv_data_exynos850_cl0) { 635 u32 index; 636 int err; 637 638 err = of_property_read_u32(dev->of_node, 639 "samsung,cluster-index", &index); 640 if (err) { 641 dev_err(dev, "failed to get cluster index\n"); 642 return NULL; 643 } 644 645 switch (index) { 646 case 0: 647 return &drv_data_exynos850_cl0; 648 case 1: 649 return &drv_data_exynos850_cl1; 650 default: 651 dev_err(dev, "wrong cluster index: %u\n", index); 652 return NULL; 653 } 654 } 655#endif 656 657 return variant; 658} 659 660static int s3c2410wdt_probe(struct platform_device *pdev) 661{ 662 struct device *dev = &pdev->dev; 663 struct s3c2410_wdt *wdt; 664 unsigned int wtcon; 665 int wdt_irq; 666 int ret; 667 668 wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL); 669 if (!wdt) 670 return -ENOMEM; 671 672 wdt->dev = dev; 673 spin_lock_init(&wdt->lock); 674 wdt->wdt_device = s3c2410_wdd; 675 676 wdt->drv_data = s3c2410_get_wdt_drv_data(pdev); 677 if (!wdt->drv_data) 678 return -EINVAL; 679 680 if (wdt->drv_data->quirks & QUIRKS_HAVE_PMUREG) { 681 wdt->pmureg = syscon_regmap_lookup_by_phandle(dev->of_node, 682 "samsung,syscon-phandle"); 683 if (IS_ERR(wdt->pmureg)) { 684 dev_err(dev, "syscon regmap lookup failed.\n"); 685 return PTR_ERR(wdt->pmureg); 686 } 687 } 688 689 wdt_irq = platform_get_irq(pdev, 0); 690 if (wdt_irq < 0) 691 return wdt_irq; 692 693 /* get the memory region for the watchdog timer */ 694 wdt->reg_base = devm_platform_ioremap_resource(pdev, 0); 695 if (IS_ERR(wdt->reg_base)) 696 return PTR_ERR(wdt->reg_base); 697 698 wdt->bus_clk = devm_clk_get(dev, "watchdog"); 699 if (IS_ERR(wdt->bus_clk)) { 700 dev_err(dev, "failed to find bus clock\n"); 701 return PTR_ERR(wdt->bus_clk); 702 } 703 704 ret = clk_prepare_enable(wdt->bus_clk); 705 if (ret < 0) { 706 dev_err(dev, "failed to enable bus clock\n"); 707 return ret; 708 } 709 710 /* 711 * "watchdog_src" clock is optional; if it's not present -- just skip it 712 * and use "watchdog" clock as both bus and source clock. 713 */ 714 wdt->src_clk = devm_clk_get_optional(dev, "watchdog_src"); 715 if (IS_ERR(wdt->src_clk)) { 716 dev_err_probe(dev, PTR_ERR(wdt->src_clk), 717 "failed to get source clock\n"); 718 ret = PTR_ERR(wdt->src_clk); 719 goto err_bus_clk; 720 } 721 722 ret = clk_prepare_enable(wdt->src_clk); 723 if (ret) { 724 dev_err(dev, "failed to enable source clock\n"); 725 goto err_bus_clk; 726 } 727 728 wdt->wdt_device.min_timeout = 1; 729 wdt->wdt_device.max_timeout = s3c2410wdt_max_timeout(wdt); 730 731 ret = s3c2410wdt_cpufreq_register(wdt); 732 if (ret < 0) { 733 dev_err(dev, "failed to register cpufreq\n"); 734 goto err_src_clk; 735 } 736 737 watchdog_set_drvdata(&wdt->wdt_device, wdt); 738 739 /* see if we can actually set the requested timer margin, and if 740 * not, try the default value */ 741 742 watchdog_init_timeout(&wdt->wdt_device, tmr_margin, dev); 743 ret = s3c2410wdt_set_heartbeat(&wdt->wdt_device, 744 wdt->wdt_device.timeout); 745 if (ret) { 746 ret = s3c2410wdt_set_heartbeat(&wdt->wdt_device, 747 S3C2410_WATCHDOG_DEFAULT_TIME); 748 if (ret == 0) { 749 dev_warn(dev, "tmr_margin value out of range, default %d used\n", 750 S3C2410_WATCHDOG_DEFAULT_TIME); 751 } else { 752 dev_err(dev, "failed to use default timeout\n"); 753 goto err_cpufreq; 754 } 755 } 756 757 ret = devm_request_irq(dev, wdt_irq, s3c2410wdt_irq, 0, 758 pdev->name, pdev); 759 if (ret != 0) { 760 dev_err(dev, "failed to install irq (%d)\n", ret); 761 goto err_cpufreq; 762 } 763 764 watchdog_set_nowayout(&wdt->wdt_device, nowayout); 765 watchdog_set_restart_priority(&wdt->wdt_device, 128); 766 767 wdt->wdt_device.bootstatus = s3c2410wdt_get_bootstatus(wdt); 768 wdt->wdt_device.parent = dev; 769 770 /* 771 * If "tmr_atboot" param is non-zero, start the watchdog right now. Also 772 * set WDOG_HW_RUNNING bit, so that watchdog core can kick the watchdog. 773 * 774 * If we're not enabling the watchdog, then ensure it is disabled if it 775 * has been left running from the bootloader or other source. 776 */ 777 if (tmr_atboot) { 778 dev_info(dev, "starting watchdog timer\n"); 779 s3c2410wdt_start(&wdt->wdt_device); 780 set_bit(WDOG_HW_RUNNING, &wdt->wdt_device.status); 781 } else { 782 s3c2410wdt_stop(&wdt->wdt_device); 783 } 784 785 ret = watchdog_register_device(&wdt->wdt_device); 786 if (ret) 787 goto err_cpufreq; 788 789 ret = s3c2410wdt_enable(wdt, true); 790 if (ret < 0) 791 goto err_unregister; 792 793 platform_set_drvdata(pdev, wdt); 794 795 /* print out a statement of readiness */ 796 797 wtcon = readl(wdt->reg_base + S3C2410_WTCON); 798 799 dev_info(dev, "watchdog %sactive, reset %sabled, irq %sabled\n", 800 (wtcon & S3C2410_WTCON_ENABLE) ? "" : "in", 801 (wtcon & S3C2410_WTCON_RSTEN) ? "en" : "dis", 802 (wtcon & S3C2410_WTCON_INTEN) ? "en" : "dis"); 803 804 return 0; 805 806 err_unregister: 807 watchdog_unregister_device(&wdt->wdt_device); 808 809 err_cpufreq: 810 s3c2410wdt_cpufreq_deregister(wdt); 811 812 err_src_clk: 813 clk_disable_unprepare(wdt->src_clk); 814 815 err_bus_clk: 816 clk_disable_unprepare(wdt->bus_clk); 817 818 return ret; 819} 820 821static int s3c2410wdt_remove(struct platform_device *dev) 822{ 823 int ret; 824 struct s3c2410_wdt *wdt = platform_get_drvdata(dev); 825 826 ret = s3c2410wdt_enable(wdt, false); 827 if (ret < 0) 828 return ret; 829 830 watchdog_unregister_device(&wdt->wdt_device); 831 832 s3c2410wdt_cpufreq_deregister(wdt); 833 834 clk_disable_unprepare(wdt->src_clk); 835 clk_disable_unprepare(wdt->bus_clk); 836 837 return 0; 838} 839 840static void s3c2410wdt_shutdown(struct platform_device *dev) 841{ 842 struct s3c2410_wdt *wdt = platform_get_drvdata(dev); 843 844 s3c2410wdt_enable(wdt, false); 845 s3c2410wdt_stop(&wdt->wdt_device); 846} 847 848#ifdef CONFIG_PM_SLEEP 849 850static int s3c2410wdt_suspend(struct device *dev) 851{ 852 int ret; 853 struct s3c2410_wdt *wdt = dev_get_drvdata(dev); 854 855 /* Save watchdog state, and turn it off. */ 856 wdt->wtcon_save = readl(wdt->reg_base + S3C2410_WTCON); 857 wdt->wtdat_save = readl(wdt->reg_base + S3C2410_WTDAT); 858 859 ret = s3c2410wdt_enable(wdt, false); 860 if (ret < 0) 861 return ret; 862 863 /* Note that WTCNT doesn't need to be saved. */ 864 s3c2410wdt_stop(&wdt->wdt_device); 865 866 return 0; 867} 868 869static int s3c2410wdt_resume(struct device *dev) 870{ 871 int ret; 872 struct s3c2410_wdt *wdt = dev_get_drvdata(dev); 873 874 /* Restore watchdog state. */ 875 writel(wdt->wtdat_save, wdt->reg_base + S3C2410_WTDAT); 876 writel(wdt->wtdat_save, wdt->reg_base + S3C2410_WTCNT);/* Reset count */ 877 writel(wdt->wtcon_save, wdt->reg_base + S3C2410_WTCON); 878 879 ret = s3c2410wdt_enable(wdt, true); 880 if (ret < 0) 881 return ret; 882 883 dev_info(dev, "watchdog %sabled\n", 884 (wdt->wtcon_save & S3C2410_WTCON_ENABLE) ? "en" : "dis"); 885 886 return 0; 887} 888#endif 889 890static SIMPLE_DEV_PM_OPS(s3c2410wdt_pm_ops, s3c2410wdt_suspend, 891 s3c2410wdt_resume); 892 893static struct platform_driver s3c2410wdt_driver = { 894 .probe = s3c2410wdt_probe, 895 .remove = s3c2410wdt_remove, 896 .shutdown = s3c2410wdt_shutdown, 897 .id_table = s3c2410_wdt_ids, 898 .driver = { 899 .name = "s3c2410-wdt", 900 .pm = &s3c2410wdt_pm_ops, 901 .of_match_table = of_match_ptr(s3c2410_wdt_match), 902 }, 903}; 904 905module_platform_driver(s3c2410wdt_driver); 906 907MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>, Dimitry Andric <dimitry.andric@tomtom.com>"); 908MODULE_DESCRIPTION("S3C2410 Watchdog Device Driver"); 909MODULE_LICENSE("GPL");