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