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