0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012 #include <linux/module.h>
0013 #include <linux/moduleparam.h>
0014 #include <linux/types.h>
0015 #include <linux/timer.h>
0016 #include <linux/watchdog.h>
0017 #include <linux/platform_device.h>
0018 #include <linux/interrupt.h>
0019 #include <linux/clk.h>
0020 #include <linux/uaccess.h>
0021 #include <linux/io.h>
0022 #include <linux/cpufreq.h>
0023 #include <linux/slab.h>
0024 #include <linux/err.h>
0025 #include <linux/of.h>
0026 #include <linux/of_device.h>
0027 #include <linux/mfd/syscon.h>
0028 #include <linux/regmap.h>
0029 #include <linux/delay.h>
0030
0031 #define S3C2410_WTCON 0x00
0032 #define S3C2410_WTDAT 0x04
0033 #define S3C2410_WTCNT 0x08
0034 #define S3C2410_WTCLRINT 0x0c
0035
0036 #define S3C2410_WTCNT_MAXCNT 0xffff
0037
0038 #define S3C2410_WTCON_RSTEN (1 << 0)
0039 #define S3C2410_WTCON_INTEN (1 << 2)
0040 #define S3C2410_WTCON_ENABLE (1 << 5)
0041
0042 #define S3C2410_WTCON_DIV16 (0 << 3)
0043 #define S3C2410_WTCON_DIV32 (1 << 3)
0044 #define S3C2410_WTCON_DIV64 (2 << 3)
0045 #define S3C2410_WTCON_DIV128 (3 << 3)
0046
0047 #define S3C2410_WTCON_MAXDIV 0x80
0048
0049 #define S3C2410_WTCON_PRESCALE(x) ((x) << 8)
0050 #define S3C2410_WTCON_PRESCALE_MASK (0xff << 8)
0051 #define S3C2410_WTCON_PRESCALE_MAX 0xff
0052
0053 #define S3C2410_WATCHDOG_ATBOOT (0)
0054 #define S3C2410_WATCHDOG_DEFAULT_TIME (15)
0055
0056 #define EXYNOS5_RST_STAT_REG_OFFSET 0x0404
0057 #define EXYNOS5_WDT_DISABLE_REG_OFFSET 0x0408
0058 #define EXYNOS5_WDT_MASK_RESET_REG_OFFSET 0x040c
0059 #define EXYNOS850_CLUSTER0_NONCPU_OUT 0x1220
0060 #define EXYNOS850_CLUSTER0_NONCPU_INT_EN 0x1244
0061 #define EXYNOS850_CLUSTER1_NONCPU_OUT 0x1620
0062 #define EXYNOS850_CLUSTER1_NONCPU_INT_EN 0x1644
0063
0064 #define EXYNOS850_CLUSTER0_WDTRESET_BIT 24
0065 #define EXYNOS850_CLUSTER1_WDTRESET_BIT 23
0066
0067
0068
0069
0070
0071
0072
0073
0074
0075
0076
0077
0078
0079
0080
0081
0082
0083
0084
0085
0086
0087
0088
0089
0090
0091
0092
0093
0094
0095
0096
0097
0098
0099
0100
0101 #define QUIRK_HAS_WTCLRINT_REG (1 << 0)
0102 #define QUIRK_HAS_PMU_MASK_RESET (1 << 1)
0103 #define QUIRK_HAS_PMU_RST_STAT (1 << 2)
0104 #define QUIRK_HAS_PMU_AUTO_DISABLE (1 << 3)
0105 #define QUIRK_HAS_PMU_CNT_EN (1 << 4)
0106
0107
0108 #define QUIRKS_HAVE_PMUREG \
0109 (QUIRK_HAS_PMU_MASK_RESET | QUIRK_HAS_PMU_RST_STAT | \
0110 QUIRK_HAS_PMU_AUTO_DISABLE | QUIRK_HAS_PMU_CNT_EN)
0111
0112 static bool nowayout = WATCHDOG_NOWAYOUT;
0113 static int tmr_margin;
0114 static int tmr_atboot = S3C2410_WATCHDOG_ATBOOT;
0115 static int soft_noboot;
0116
0117 module_param(tmr_margin, int, 0);
0118 module_param(tmr_atboot, int, 0);
0119 module_param(nowayout, bool, 0);
0120 module_param(soft_noboot, int, 0);
0121
0122 MODULE_PARM_DESC(tmr_margin, "Watchdog tmr_margin in seconds. (default="
0123 __MODULE_STRING(S3C2410_WATCHDOG_DEFAULT_TIME) ")");
0124 MODULE_PARM_DESC(tmr_atboot,
0125 "Watchdog is started at boot time if set to 1, default="
0126 __MODULE_STRING(S3C2410_WATCHDOG_ATBOOT));
0127 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
0128 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
0129 MODULE_PARM_DESC(soft_noboot, "Watchdog action, set to 1 to ignore reboots, 0 to reboot (default 0)");
0130
0131
0132
0133
0134
0135
0136
0137
0138
0139
0140
0141
0142
0143
0144
0145
0146
0147
0148
0149 struct s3c2410_wdt_variant {
0150 int disable_reg;
0151 int mask_reset_reg;
0152 bool mask_reset_inv;
0153 int mask_bit;
0154 int rst_stat_reg;
0155 int rst_stat_bit;
0156 int cnt_en_reg;
0157 int cnt_en_bit;
0158 u32 quirks;
0159 };
0160
0161 struct s3c2410_wdt {
0162 struct device *dev;
0163 struct clk *bus_clk;
0164 struct clk *src_clk;
0165 void __iomem *reg_base;
0166 unsigned int count;
0167 spinlock_t lock;
0168 unsigned long wtcon_save;
0169 unsigned long wtdat_save;
0170 struct watchdog_device wdt_device;
0171 struct notifier_block freq_transition;
0172 const struct s3c2410_wdt_variant *drv_data;
0173 struct regmap *pmureg;
0174 };
0175
0176 static const struct s3c2410_wdt_variant drv_data_s3c2410 = {
0177 .quirks = 0
0178 };
0179
0180 #ifdef CONFIG_OF
0181 static const struct s3c2410_wdt_variant drv_data_s3c6410 = {
0182 .quirks = QUIRK_HAS_WTCLRINT_REG,
0183 };
0184
0185 static const struct s3c2410_wdt_variant drv_data_exynos5250 = {
0186 .disable_reg = EXYNOS5_WDT_DISABLE_REG_OFFSET,
0187 .mask_reset_reg = EXYNOS5_WDT_MASK_RESET_REG_OFFSET,
0188 .mask_bit = 20,
0189 .rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET,
0190 .rst_stat_bit = 20,
0191 .quirks = QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_MASK_RESET | \
0192 QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_AUTO_DISABLE,
0193 };
0194
0195 static const struct s3c2410_wdt_variant drv_data_exynos5420 = {
0196 .disable_reg = EXYNOS5_WDT_DISABLE_REG_OFFSET,
0197 .mask_reset_reg = EXYNOS5_WDT_MASK_RESET_REG_OFFSET,
0198 .mask_bit = 0,
0199 .rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET,
0200 .rst_stat_bit = 9,
0201 .quirks = QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_MASK_RESET | \
0202 QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_AUTO_DISABLE,
0203 };
0204
0205 static const struct s3c2410_wdt_variant drv_data_exynos7 = {
0206 .disable_reg = EXYNOS5_WDT_DISABLE_REG_OFFSET,
0207 .mask_reset_reg = EXYNOS5_WDT_MASK_RESET_REG_OFFSET,
0208 .mask_bit = 23,
0209 .rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET,
0210 .rst_stat_bit = 23,
0211 .quirks = QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_MASK_RESET | \
0212 QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_AUTO_DISABLE,
0213 };
0214
0215 static const struct s3c2410_wdt_variant drv_data_exynos850_cl0 = {
0216 .mask_reset_reg = EXYNOS850_CLUSTER0_NONCPU_INT_EN,
0217 .mask_bit = 2,
0218 .mask_reset_inv = true,
0219 .rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET,
0220 .rst_stat_bit = EXYNOS850_CLUSTER0_WDTRESET_BIT,
0221 .cnt_en_reg = EXYNOS850_CLUSTER0_NONCPU_OUT,
0222 .cnt_en_bit = 7,
0223 .quirks = QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_MASK_RESET | \
0224 QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_CNT_EN,
0225 };
0226
0227 static const struct s3c2410_wdt_variant drv_data_exynos850_cl1 = {
0228 .mask_reset_reg = EXYNOS850_CLUSTER1_NONCPU_INT_EN,
0229 .mask_bit = 2,
0230 .mask_reset_inv = true,
0231 .rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET,
0232 .rst_stat_bit = EXYNOS850_CLUSTER1_WDTRESET_BIT,
0233 .cnt_en_reg = EXYNOS850_CLUSTER1_NONCPU_OUT,
0234 .cnt_en_bit = 7,
0235 .quirks = QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_MASK_RESET | \
0236 QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_CNT_EN,
0237 };
0238
0239 static const struct of_device_id s3c2410_wdt_match[] = {
0240 { .compatible = "samsung,s3c2410-wdt",
0241 .data = &drv_data_s3c2410 },
0242 { .compatible = "samsung,s3c6410-wdt",
0243 .data = &drv_data_s3c6410 },
0244 { .compatible = "samsung,exynos5250-wdt",
0245 .data = &drv_data_exynos5250 },
0246 { .compatible = "samsung,exynos5420-wdt",
0247 .data = &drv_data_exynos5420 },
0248 { .compatible = "samsung,exynos7-wdt",
0249 .data = &drv_data_exynos7 },
0250 { .compatible = "samsung,exynos850-wdt",
0251 .data = &drv_data_exynos850_cl0 },
0252 {},
0253 };
0254 MODULE_DEVICE_TABLE(of, s3c2410_wdt_match);
0255 #endif
0256
0257 static const struct platform_device_id s3c2410_wdt_ids[] = {
0258 {
0259 .name = "s3c2410-wdt",
0260 .driver_data = (unsigned long)&drv_data_s3c2410,
0261 },
0262 {}
0263 };
0264 MODULE_DEVICE_TABLE(platform, s3c2410_wdt_ids);
0265
0266
0267
0268 static inline unsigned long s3c2410wdt_get_freq(struct s3c2410_wdt *wdt)
0269 {
0270 return clk_get_rate(wdt->src_clk ? wdt->src_clk : wdt->bus_clk);
0271 }
0272
0273 static inline unsigned int s3c2410wdt_max_timeout(struct s3c2410_wdt *wdt)
0274 {
0275 const unsigned long freq = s3c2410wdt_get_freq(wdt);
0276
0277 return S3C2410_WTCNT_MAXCNT / (freq / (S3C2410_WTCON_PRESCALE_MAX + 1)
0278 / S3C2410_WTCON_MAXDIV);
0279 }
0280
0281 static inline struct s3c2410_wdt *freq_to_wdt(struct notifier_block *nb)
0282 {
0283 return container_of(nb, struct s3c2410_wdt, freq_transition);
0284 }
0285
0286 static int s3c2410wdt_disable_wdt_reset(struct s3c2410_wdt *wdt, bool mask)
0287 {
0288 const u32 mask_val = BIT(wdt->drv_data->mask_bit);
0289 const u32 val = mask ? mask_val : 0;
0290 int ret;
0291
0292 ret = regmap_update_bits(wdt->pmureg, wdt->drv_data->disable_reg,
0293 mask_val, val);
0294 if (ret < 0)
0295 dev_err(wdt->dev, "failed to update reg(%d)\n", ret);
0296
0297 return ret;
0298 }
0299
0300 static int s3c2410wdt_mask_wdt_reset(struct s3c2410_wdt *wdt, bool mask)
0301 {
0302 const u32 mask_val = BIT(wdt->drv_data->mask_bit);
0303 const bool val_inv = wdt->drv_data->mask_reset_inv;
0304 const u32 val = (mask ^ val_inv) ? mask_val : 0;
0305 int ret;
0306
0307 ret = regmap_update_bits(wdt->pmureg, wdt->drv_data->mask_reset_reg,
0308 mask_val, val);
0309 if (ret < 0)
0310 dev_err(wdt->dev, "failed to update reg(%d)\n", ret);
0311
0312 return ret;
0313 }
0314
0315 static int s3c2410wdt_enable_counter(struct s3c2410_wdt *wdt, bool en)
0316 {
0317 const u32 mask_val = BIT(wdt->drv_data->cnt_en_bit);
0318 const u32 val = en ? mask_val : 0;
0319 int ret;
0320
0321 ret = regmap_update_bits(wdt->pmureg, wdt->drv_data->cnt_en_reg,
0322 mask_val, val);
0323 if (ret < 0)
0324 dev_err(wdt->dev, "failed to update reg(%d)\n", ret);
0325
0326 return ret;
0327 }
0328
0329 static int s3c2410wdt_enable(struct s3c2410_wdt *wdt, bool en)
0330 {
0331 int ret;
0332
0333 if (wdt->drv_data->quirks & QUIRK_HAS_PMU_AUTO_DISABLE) {
0334 ret = s3c2410wdt_disable_wdt_reset(wdt, !en);
0335 if (ret < 0)
0336 return ret;
0337 }
0338
0339 if (wdt->drv_data->quirks & QUIRK_HAS_PMU_MASK_RESET) {
0340 ret = s3c2410wdt_mask_wdt_reset(wdt, !en);
0341 if (ret < 0)
0342 return ret;
0343 }
0344
0345 if (wdt->drv_data->quirks & QUIRK_HAS_PMU_CNT_EN) {
0346 ret = s3c2410wdt_enable_counter(wdt, en);
0347 if (ret < 0)
0348 return ret;
0349 }
0350
0351 return 0;
0352 }
0353
0354 static int s3c2410wdt_keepalive(struct watchdog_device *wdd)
0355 {
0356 struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd);
0357
0358 spin_lock(&wdt->lock);
0359 writel(wdt->count, wdt->reg_base + S3C2410_WTCNT);
0360 spin_unlock(&wdt->lock);
0361
0362 return 0;
0363 }
0364
0365 static void __s3c2410wdt_stop(struct s3c2410_wdt *wdt)
0366 {
0367 unsigned long wtcon;
0368
0369 wtcon = readl(wdt->reg_base + S3C2410_WTCON);
0370 wtcon &= ~(S3C2410_WTCON_ENABLE | S3C2410_WTCON_RSTEN);
0371 writel(wtcon, wdt->reg_base + S3C2410_WTCON);
0372 }
0373
0374 static int s3c2410wdt_stop(struct watchdog_device *wdd)
0375 {
0376 struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd);
0377
0378 spin_lock(&wdt->lock);
0379 __s3c2410wdt_stop(wdt);
0380 spin_unlock(&wdt->lock);
0381
0382 return 0;
0383 }
0384
0385 static int s3c2410wdt_start(struct watchdog_device *wdd)
0386 {
0387 unsigned long wtcon;
0388 struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd);
0389
0390 spin_lock(&wdt->lock);
0391
0392 __s3c2410wdt_stop(wdt);
0393
0394 wtcon = readl(wdt->reg_base + S3C2410_WTCON);
0395 wtcon |= S3C2410_WTCON_ENABLE | S3C2410_WTCON_DIV128;
0396
0397 if (soft_noboot) {
0398 wtcon |= S3C2410_WTCON_INTEN;
0399 wtcon &= ~S3C2410_WTCON_RSTEN;
0400 } else {
0401 wtcon &= ~S3C2410_WTCON_INTEN;
0402 wtcon |= S3C2410_WTCON_RSTEN;
0403 }
0404
0405 dev_dbg(wdt->dev, "Starting watchdog: count=0x%08x, wtcon=%08lx\n",
0406 wdt->count, wtcon);
0407
0408 writel(wdt->count, wdt->reg_base + S3C2410_WTDAT);
0409 writel(wdt->count, wdt->reg_base + S3C2410_WTCNT);
0410 writel(wtcon, wdt->reg_base + S3C2410_WTCON);
0411 spin_unlock(&wdt->lock);
0412
0413 return 0;
0414 }
0415
0416 static inline int s3c2410wdt_is_running(struct s3c2410_wdt *wdt)
0417 {
0418 return readl(wdt->reg_base + S3C2410_WTCON) & S3C2410_WTCON_ENABLE;
0419 }
0420
0421 static int s3c2410wdt_set_heartbeat(struct watchdog_device *wdd,
0422 unsigned int timeout)
0423 {
0424 struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd);
0425 unsigned long freq = s3c2410wdt_get_freq(wdt);
0426 unsigned int count;
0427 unsigned int divisor = 1;
0428 unsigned long wtcon;
0429
0430 if (timeout < 1)
0431 return -EINVAL;
0432
0433 freq = DIV_ROUND_UP(freq, 128);
0434 count = timeout * freq;
0435
0436 dev_dbg(wdt->dev, "Heartbeat: count=%d, timeout=%d, freq=%lu\n",
0437 count, timeout, freq);
0438
0439
0440
0441
0442
0443
0444 if (count >= 0x10000) {
0445 divisor = DIV_ROUND_UP(count, 0xffff);
0446
0447 if (divisor > 0x100) {
0448 dev_err(wdt->dev, "timeout %d too big\n", timeout);
0449 return -EINVAL;
0450 }
0451 }
0452
0453 dev_dbg(wdt->dev, "Heartbeat: timeout=%d, divisor=%d, count=%d (%08x)\n",
0454 timeout, divisor, count, DIV_ROUND_UP(count, divisor));
0455
0456 count = DIV_ROUND_UP(count, divisor);
0457 wdt->count = count;
0458
0459
0460 wtcon = readl(wdt->reg_base + S3C2410_WTCON);
0461 wtcon &= ~S3C2410_WTCON_PRESCALE_MASK;
0462 wtcon |= S3C2410_WTCON_PRESCALE(divisor-1);
0463
0464 writel(count, wdt->reg_base + S3C2410_WTDAT);
0465 writel(wtcon, wdt->reg_base + S3C2410_WTCON);
0466
0467 wdd->timeout = (count * divisor) / freq;
0468
0469 return 0;
0470 }
0471
0472 static int s3c2410wdt_restart(struct watchdog_device *wdd, unsigned long action,
0473 void *data)
0474 {
0475 struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd);
0476 void __iomem *wdt_base = wdt->reg_base;
0477
0478
0479 writel(0, wdt_base + S3C2410_WTCON);
0480
0481
0482 writel(0x80, wdt_base + S3C2410_WTCNT);
0483 writel(0x80, wdt_base + S3C2410_WTDAT);
0484
0485
0486 writel(S3C2410_WTCON_ENABLE | S3C2410_WTCON_DIV16 |
0487 S3C2410_WTCON_RSTEN | S3C2410_WTCON_PRESCALE(0x20),
0488 wdt_base + S3C2410_WTCON);
0489
0490
0491 mdelay(500);
0492
0493 return 0;
0494 }
0495
0496 #define OPTIONS (WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE)
0497
0498 static const struct watchdog_info s3c2410_wdt_ident = {
0499 .options = OPTIONS,
0500 .firmware_version = 0,
0501 .identity = "S3C2410 Watchdog",
0502 };
0503
0504 static const struct watchdog_ops s3c2410wdt_ops = {
0505 .owner = THIS_MODULE,
0506 .start = s3c2410wdt_start,
0507 .stop = s3c2410wdt_stop,
0508 .ping = s3c2410wdt_keepalive,
0509 .set_timeout = s3c2410wdt_set_heartbeat,
0510 .restart = s3c2410wdt_restart,
0511 };
0512
0513 static const struct watchdog_device s3c2410_wdd = {
0514 .info = &s3c2410_wdt_ident,
0515 .ops = &s3c2410wdt_ops,
0516 .timeout = S3C2410_WATCHDOG_DEFAULT_TIME,
0517 };
0518
0519
0520
0521 static irqreturn_t s3c2410wdt_irq(int irqno, void *param)
0522 {
0523 struct s3c2410_wdt *wdt = platform_get_drvdata(param);
0524
0525 dev_info(wdt->dev, "watchdog timer expired (irq)\n");
0526
0527 s3c2410wdt_keepalive(&wdt->wdt_device);
0528
0529 if (wdt->drv_data->quirks & QUIRK_HAS_WTCLRINT_REG)
0530 writel(0x1, wdt->reg_base + S3C2410_WTCLRINT);
0531
0532 return IRQ_HANDLED;
0533 }
0534
0535 #ifdef CONFIG_ARM_S3C24XX_CPUFREQ
0536
0537 static int s3c2410wdt_cpufreq_transition(struct notifier_block *nb,
0538 unsigned long val, void *data)
0539 {
0540 int ret;
0541 struct s3c2410_wdt *wdt = freq_to_wdt(nb);
0542
0543 if (!s3c2410wdt_is_running(wdt))
0544 goto done;
0545
0546 if (val == CPUFREQ_PRECHANGE) {
0547
0548
0549
0550
0551
0552 s3c2410wdt_keepalive(&wdt->wdt_device);
0553 } else if (val == CPUFREQ_POSTCHANGE) {
0554 s3c2410wdt_stop(&wdt->wdt_device);
0555
0556 ret = s3c2410wdt_set_heartbeat(&wdt->wdt_device,
0557 wdt->wdt_device.timeout);
0558
0559 if (ret >= 0)
0560 s3c2410wdt_start(&wdt->wdt_device);
0561 else
0562 goto err;
0563 }
0564
0565 done:
0566 return 0;
0567
0568 err:
0569 dev_err(wdt->dev, "cannot set new value for timeout %d\n",
0570 wdt->wdt_device.timeout);
0571 return ret;
0572 }
0573
0574 static inline int s3c2410wdt_cpufreq_register(struct s3c2410_wdt *wdt)
0575 {
0576 wdt->freq_transition.notifier_call = s3c2410wdt_cpufreq_transition;
0577
0578 return cpufreq_register_notifier(&wdt->freq_transition,
0579 CPUFREQ_TRANSITION_NOTIFIER);
0580 }
0581
0582 static inline void s3c2410wdt_cpufreq_deregister(struct s3c2410_wdt *wdt)
0583 {
0584 wdt->freq_transition.notifier_call = s3c2410wdt_cpufreq_transition;
0585
0586 cpufreq_unregister_notifier(&wdt->freq_transition,
0587 CPUFREQ_TRANSITION_NOTIFIER);
0588 }
0589
0590 #else
0591
0592 static inline int s3c2410wdt_cpufreq_register(struct s3c2410_wdt *wdt)
0593 {
0594 return 0;
0595 }
0596
0597 static inline void s3c2410wdt_cpufreq_deregister(struct s3c2410_wdt *wdt)
0598 {
0599 }
0600 #endif
0601
0602 static inline unsigned int s3c2410wdt_get_bootstatus(struct s3c2410_wdt *wdt)
0603 {
0604 unsigned int rst_stat;
0605 int ret;
0606
0607 if (!(wdt->drv_data->quirks & QUIRK_HAS_PMU_RST_STAT))
0608 return 0;
0609
0610 ret = regmap_read(wdt->pmureg, wdt->drv_data->rst_stat_reg, &rst_stat);
0611 if (ret)
0612 dev_warn(wdt->dev, "Couldn't get RST_STAT register\n");
0613 else if (rst_stat & BIT(wdt->drv_data->rst_stat_bit))
0614 return WDIOF_CARDRESET;
0615
0616 return 0;
0617 }
0618
0619 static inline const struct s3c2410_wdt_variant *
0620 s3c2410_get_wdt_drv_data(struct platform_device *pdev)
0621 {
0622 const struct s3c2410_wdt_variant *variant;
0623 struct device *dev = &pdev->dev;
0624
0625 variant = of_device_get_match_data(dev);
0626 if (!variant) {
0627
0628 variant = (struct s3c2410_wdt_variant *)
0629 platform_get_device_id(pdev)->driver_data;
0630 }
0631
0632 #ifdef CONFIG_OF
0633
0634 if (variant == &drv_data_exynos850_cl0) {
0635 u32 index;
0636 int err;
0637
0638 err = of_property_read_u32(dev->of_node,
0639 "samsung,cluster-index", &index);
0640 if (err) {
0641 dev_err(dev, "failed to get cluster index\n");
0642 return NULL;
0643 }
0644
0645 switch (index) {
0646 case 0:
0647 return &drv_data_exynos850_cl0;
0648 case 1:
0649 return &drv_data_exynos850_cl1;
0650 default:
0651 dev_err(dev, "wrong cluster index: %u\n", index);
0652 return NULL;
0653 }
0654 }
0655 #endif
0656
0657 return variant;
0658 }
0659
0660 static int s3c2410wdt_probe(struct platform_device *pdev)
0661 {
0662 struct device *dev = &pdev->dev;
0663 struct s3c2410_wdt *wdt;
0664 unsigned int wtcon;
0665 int wdt_irq;
0666 int ret;
0667
0668 wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
0669 if (!wdt)
0670 return -ENOMEM;
0671
0672 wdt->dev = dev;
0673 spin_lock_init(&wdt->lock);
0674 wdt->wdt_device = s3c2410_wdd;
0675
0676 wdt->drv_data = s3c2410_get_wdt_drv_data(pdev);
0677 if (!wdt->drv_data)
0678 return -EINVAL;
0679
0680 if (wdt->drv_data->quirks & QUIRKS_HAVE_PMUREG) {
0681 wdt->pmureg = syscon_regmap_lookup_by_phandle(dev->of_node,
0682 "samsung,syscon-phandle");
0683 if (IS_ERR(wdt->pmureg)) {
0684 dev_err(dev, "syscon regmap lookup failed.\n");
0685 return PTR_ERR(wdt->pmureg);
0686 }
0687 }
0688
0689 wdt_irq = platform_get_irq(pdev, 0);
0690 if (wdt_irq < 0)
0691 return wdt_irq;
0692
0693
0694 wdt->reg_base = devm_platform_ioremap_resource(pdev, 0);
0695 if (IS_ERR(wdt->reg_base))
0696 return PTR_ERR(wdt->reg_base);
0697
0698 wdt->bus_clk = devm_clk_get(dev, "watchdog");
0699 if (IS_ERR(wdt->bus_clk)) {
0700 dev_err(dev, "failed to find bus clock\n");
0701 return PTR_ERR(wdt->bus_clk);
0702 }
0703
0704 ret = clk_prepare_enable(wdt->bus_clk);
0705 if (ret < 0) {
0706 dev_err(dev, "failed to enable bus clock\n");
0707 return ret;
0708 }
0709
0710
0711
0712
0713
0714 wdt->src_clk = devm_clk_get_optional(dev, "watchdog_src");
0715 if (IS_ERR(wdt->src_clk)) {
0716 dev_err_probe(dev, PTR_ERR(wdt->src_clk),
0717 "failed to get source clock\n");
0718 ret = PTR_ERR(wdt->src_clk);
0719 goto err_bus_clk;
0720 }
0721
0722 ret = clk_prepare_enable(wdt->src_clk);
0723 if (ret) {
0724 dev_err(dev, "failed to enable source clock\n");
0725 goto err_bus_clk;
0726 }
0727
0728 wdt->wdt_device.min_timeout = 1;
0729 wdt->wdt_device.max_timeout = s3c2410wdt_max_timeout(wdt);
0730
0731 ret = s3c2410wdt_cpufreq_register(wdt);
0732 if (ret < 0) {
0733 dev_err(dev, "failed to register cpufreq\n");
0734 goto err_src_clk;
0735 }
0736
0737 watchdog_set_drvdata(&wdt->wdt_device, wdt);
0738
0739
0740
0741
0742 watchdog_init_timeout(&wdt->wdt_device, tmr_margin, dev);
0743 ret = s3c2410wdt_set_heartbeat(&wdt->wdt_device,
0744 wdt->wdt_device.timeout);
0745 if (ret) {
0746 ret = s3c2410wdt_set_heartbeat(&wdt->wdt_device,
0747 S3C2410_WATCHDOG_DEFAULT_TIME);
0748 if (ret == 0) {
0749 dev_warn(dev, "tmr_margin value out of range, default %d used\n",
0750 S3C2410_WATCHDOG_DEFAULT_TIME);
0751 } else {
0752 dev_err(dev, "failed to use default timeout\n");
0753 goto err_cpufreq;
0754 }
0755 }
0756
0757 ret = devm_request_irq(dev, wdt_irq, s3c2410wdt_irq, 0,
0758 pdev->name, pdev);
0759 if (ret != 0) {
0760 dev_err(dev, "failed to install irq (%d)\n", ret);
0761 goto err_cpufreq;
0762 }
0763
0764 watchdog_set_nowayout(&wdt->wdt_device, nowayout);
0765 watchdog_set_restart_priority(&wdt->wdt_device, 128);
0766
0767 wdt->wdt_device.bootstatus = s3c2410wdt_get_bootstatus(wdt);
0768 wdt->wdt_device.parent = dev;
0769
0770
0771
0772
0773
0774
0775
0776
0777 if (tmr_atboot) {
0778 dev_info(dev, "starting watchdog timer\n");
0779 s3c2410wdt_start(&wdt->wdt_device);
0780 set_bit(WDOG_HW_RUNNING, &wdt->wdt_device.status);
0781 } else {
0782 s3c2410wdt_stop(&wdt->wdt_device);
0783 }
0784
0785 ret = watchdog_register_device(&wdt->wdt_device);
0786 if (ret)
0787 goto err_cpufreq;
0788
0789 ret = s3c2410wdt_enable(wdt, true);
0790 if (ret < 0)
0791 goto err_unregister;
0792
0793 platform_set_drvdata(pdev, wdt);
0794
0795
0796
0797 wtcon = readl(wdt->reg_base + S3C2410_WTCON);
0798
0799 dev_info(dev, "watchdog %sactive, reset %sabled, irq %sabled\n",
0800 (wtcon & S3C2410_WTCON_ENABLE) ? "" : "in",
0801 (wtcon & S3C2410_WTCON_RSTEN) ? "en" : "dis",
0802 (wtcon & S3C2410_WTCON_INTEN) ? "en" : "dis");
0803
0804 return 0;
0805
0806 err_unregister:
0807 watchdog_unregister_device(&wdt->wdt_device);
0808
0809 err_cpufreq:
0810 s3c2410wdt_cpufreq_deregister(wdt);
0811
0812 err_src_clk:
0813 clk_disable_unprepare(wdt->src_clk);
0814
0815 err_bus_clk:
0816 clk_disable_unprepare(wdt->bus_clk);
0817
0818 return ret;
0819 }
0820
0821 static int s3c2410wdt_remove(struct platform_device *dev)
0822 {
0823 int ret;
0824 struct s3c2410_wdt *wdt = platform_get_drvdata(dev);
0825
0826 ret = s3c2410wdt_enable(wdt, false);
0827 if (ret < 0)
0828 return ret;
0829
0830 watchdog_unregister_device(&wdt->wdt_device);
0831
0832 s3c2410wdt_cpufreq_deregister(wdt);
0833
0834 clk_disable_unprepare(wdt->src_clk);
0835 clk_disable_unprepare(wdt->bus_clk);
0836
0837 return 0;
0838 }
0839
0840 static void s3c2410wdt_shutdown(struct platform_device *dev)
0841 {
0842 struct s3c2410_wdt *wdt = platform_get_drvdata(dev);
0843
0844 s3c2410wdt_enable(wdt, false);
0845 s3c2410wdt_stop(&wdt->wdt_device);
0846 }
0847
0848 static int s3c2410wdt_suspend(struct device *dev)
0849 {
0850 int ret;
0851 struct s3c2410_wdt *wdt = dev_get_drvdata(dev);
0852
0853
0854 wdt->wtcon_save = readl(wdt->reg_base + S3C2410_WTCON);
0855 wdt->wtdat_save = readl(wdt->reg_base + S3C2410_WTDAT);
0856
0857 ret = s3c2410wdt_enable(wdt, false);
0858 if (ret < 0)
0859 return ret;
0860
0861
0862 s3c2410wdt_stop(&wdt->wdt_device);
0863
0864 return 0;
0865 }
0866
0867 static int s3c2410wdt_resume(struct device *dev)
0868 {
0869 int ret;
0870 struct s3c2410_wdt *wdt = dev_get_drvdata(dev);
0871
0872
0873 writel(wdt->wtdat_save, wdt->reg_base + S3C2410_WTDAT);
0874 writel(wdt->wtdat_save, wdt->reg_base + S3C2410_WTCNT);
0875 writel(wdt->wtcon_save, wdt->reg_base + S3C2410_WTCON);
0876
0877 ret = s3c2410wdt_enable(wdt, true);
0878 if (ret < 0)
0879 return ret;
0880
0881 dev_info(dev, "watchdog %sabled\n",
0882 (wdt->wtcon_save & S3C2410_WTCON_ENABLE) ? "en" : "dis");
0883
0884 return 0;
0885 }
0886
0887 static DEFINE_SIMPLE_DEV_PM_OPS(s3c2410wdt_pm_ops,
0888 s3c2410wdt_suspend, s3c2410wdt_resume);
0889
0890 static struct platform_driver s3c2410wdt_driver = {
0891 .probe = s3c2410wdt_probe,
0892 .remove = s3c2410wdt_remove,
0893 .shutdown = s3c2410wdt_shutdown,
0894 .id_table = s3c2410_wdt_ids,
0895 .driver = {
0896 .name = "s3c2410-wdt",
0897 .pm = pm_sleep_ptr(&s3c2410wdt_pm_ops),
0898 .of_match_table = of_match_ptr(s3c2410_wdt_match),
0899 },
0900 };
0901
0902 module_platform_driver(s3c2410wdt_driver);
0903
0904 MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>, Dimitry Andric <dimitry.andric@tomtom.com>");
0905 MODULE_DESCRIPTION("S3C2410 Watchdog Device Driver");
0906 MODULE_LICENSE("GPL");