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