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