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