1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright (c) 2004 Simtec Electronics
4 * Ben Dooks <ben@simtec.co.uk>
6 * S3C2410 Watchdog Timer Support
8 * Based on, softdog.c by Alan Cox,
9 * (c) Copyright 1996 Alan Cox <alan@lxorguk.ukuu.org.uk>
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>
23 #include <linux/cpufreq.h>
24 #include <linux/slab.h>
25 #include <linux/err.h>
27 #include <linux/regmap.h>
28 #include <linux/delay.h>
29 #include <linux/soc/samsung/exynos-pmu.h>
31 #define S3C2410_WTCON 0x00
32 #define S3C2410_WTDAT 0x04
33 #define S3C2410_WTCNT 0x08
34 #define S3C2410_WTCLRINT 0x0c
36 #define S3C2410_WTCNT_MAXCNT 0xffff
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)
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)
48 #define S3C2410_WTCON_MAXDIV 0x80
50 #define S3C2410_WTCON_PRESCALE(x) ((x) << 8)
51 #define S3C2410_WTCON_PRESCALE_MASK (0xff << 8)
52 #define S3C2410_WTCON_PRESCALE_MAX 0xff
54 #define S3C2410_WATCHDOG_ATBOOT (0)
55 #define S3C2410_WATCHDOG_DEFAULT_TIME (15)
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
67 #define EXYNOS850_CLUSTER0_WDTRESET_BIT 24
68 #define EXYNOS850_CLUSTER1_WDTRESET_BIT 23
69 #define EXYNOSAUTOV9_CLUSTER0_WDTRESET_BIT 25
70 #define EXYNOSAUTOV9_CLUSTER1_WDTRESET_BIT 24
72 #define GS_CLUSTER0_NONCPU_OUT 0x1220
73 #define GS_CLUSTER1_NONCPU_OUT 0x1420
74 #define GS_CLUSTER0_NONCPU_INT_EN 0x1244
75 #define GS_CLUSTER1_NONCPU_INT_EN 0x1444
76 #define GS_CLUSTER2_NONCPU_INT_EN 0x1644
77 #define GS_RST_STAT_REG_OFFSET 0x3B44
80 * DOC: Quirk flags for different Samsung watchdog IP-cores
82 * This driver supports multiple Samsung SoCs, each of which might have
83 * different set of registers and features supported. As watchdog block
84 * sometimes requires modifying PMU registers for proper functioning, register
85 * differences in both watchdog and PMU IP-cores should be accounted for. Quirk
86 * flags described below serve the purpose of telling the driver about mentioned
87 * SoC traits, and can be specified in driver data for each particular supported
90 * %QUIRK_HAS_WTCLRINT_REG: Watchdog block has WTCLRINT register. It's used to
91 * clear the interrupt once the interrupt service routine is complete. It's
92 * write-only, writing any values to this register clears the interrupt, but
93 * reading is not permitted.
95 * %QUIRK_HAS_PMU_MASK_RESET: PMU block has the register for disabling/enabling
96 * WDT reset request. On old SoCs it's usually called MASK_WDT_RESET_REQUEST,
97 * new SoCs have CLUSTERx_NONCPU_INT_EN register, which 'mask_bit' value is
98 * inverted compared to the former one.
100 * %QUIRK_HAS_PMU_RST_STAT: PMU block has RST_STAT (reset status) register,
101 * which contains bits indicating the reason for most recent CPU reset. If
102 * present, driver will use this register to check if previous reboot was due to
103 * watchdog timer reset.
105 * %QUIRK_HAS_PMU_AUTO_DISABLE: PMU block has AUTOMATIC_WDT_RESET_DISABLE
106 * register. If 'mask_bit' bit is set, PMU will disable WDT reset when
107 * corresponding processor is in reset state.
109 * %QUIRK_HAS_PMU_CNT_EN: PMU block has some register (e.g. CLUSTERx_NONCPU_OUT)
110 * with "watchdog counter enable" bit. That bit should be set to make watchdog
113 * %QUIRK_HAS_DBGACK_BIT: WTCON register has DBGACK_MASK bit. Setting the
114 * DBGACK_MASK bit disables the watchdog outputs when the SoC is in debug mode.
115 * Debug mode is determined by the DBGACK CPU signal.
117 #define QUIRK_HAS_WTCLRINT_REG BIT(0)
118 #define QUIRK_HAS_PMU_MASK_RESET BIT(1)
119 #define QUIRK_HAS_PMU_RST_STAT BIT(2)
120 #define QUIRK_HAS_PMU_AUTO_DISABLE BIT(3)
121 #define QUIRK_HAS_PMU_CNT_EN BIT(4)
122 #define QUIRK_HAS_DBGACK_BIT BIT(5)
124 /* These quirks require that we have a PMU register map */
125 #define QUIRKS_HAVE_PMUREG \
126 (QUIRK_HAS_PMU_MASK_RESET | QUIRK_HAS_PMU_RST_STAT | \
127 QUIRK_HAS_PMU_AUTO_DISABLE | QUIRK_HAS_PMU_CNT_EN)
129 static bool nowayout
= WATCHDOG_NOWAYOUT
;
130 static int tmr_margin
;
131 static int tmr_atboot
= S3C2410_WATCHDOG_ATBOOT
;
132 static int soft_noboot
;
134 module_param(tmr_margin
, int, 0);
135 module_param(tmr_atboot
, int, 0);
136 module_param(nowayout
, bool, 0);
137 module_param(soft_noboot
, int, 0);
139 MODULE_PARM_DESC(tmr_margin
, "Watchdog tmr_margin in seconds. (default="
140 __MODULE_STRING(S3C2410_WATCHDOG_DEFAULT_TIME
) ")");
141 MODULE_PARM_DESC(tmr_atboot
,
142 "Watchdog is started at boot time if set to 1, default="
143 __MODULE_STRING(S3C2410_WATCHDOG_ATBOOT
));
144 MODULE_PARM_DESC(nowayout
, "Watchdog cannot be stopped once started (default="
145 __MODULE_STRING(WATCHDOG_NOWAYOUT
) ")");
146 MODULE_PARM_DESC(soft_noboot
, "Watchdog action, set to 1 to ignore reboots, 0 to reboot (default 0)");
149 * struct s3c2410_wdt_variant - Per-variant config data
151 * @disable_reg: Offset in pmureg for the register that disables the watchdog
152 * timer reset functionality.
153 * @mask_reset_reg: Offset in pmureg for the register that masks the watchdog
154 * timer reset functionality.
155 * @mask_reset_inv: If set, mask_reset_reg value will have inverted meaning.
156 * @mask_bit: Bit number for the watchdog timer in the disable register and the
157 * mask reset register.
158 * @rst_stat_reg: Offset in pmureg for the register that has the reset status.
159 * @rst_stat_bit: Bit number in the rst_stat register indicating a watchdog
161 * @cnt_en_reg: Offset in pmureg for the register that enables WDT counter.
162 * @cnt_en_bit: Bit number for "watchdog counter enable" in cnt_en register.
163 * @quirks: A bitfield of quirks.
166 struct s3c2410_wdt_variant
{
180 struct clk
*bus_clk
; /* for register interface (PCLK) */
181 struct clk
*src_clk
; /* for WDT counter */
182 void __iomem
*reg_base
;
185 unsigned long wtcon_save
;
186 unsigned long wtdat_save
;
187 struct watchdog_device wdt_device
;
188 struct notifier_block freq_transition
;
189 const struct s3c2410_wdt_variant
*drv_data
;
190 struct regmap
*pmureg
;
193 static const struct s3c2410_wdt_variant drv_data_s3c2410
= {
198 static const struct s3c2410_wdt_variant drv_data_s3c6410
= {
199 .quirks
= QUIRK_HAS_WTCLRINT_REG
,
202 static const struct s3c2410_wdt_variant drv_data_exynos5250
= {
203 .disable_reg
= EXYNOS5_WDT_DISABLE_REG_OFFSET
,
204 .mask_reset_reg
= EXYNOS5_WDT_MASK_RESET_REG_OFFSET
,
206 .rst_stat_reg
= EXYNOS5_RST_STAT_REG_OFFSET
,
208 .quirks
= QUIRK_HAS_WTCLRINT_REG
| QUIRK_HAS_PMU_MASK_RESET
| \
209 QUIRK_HAS_PMU_RST_STAT
| QUIRK_HAS_PMU_AUTO_DISABLE
,
212 static const struct s3c2410_wdt_variant drv_data_exynos5420
= {
213 .disable_reg
= EXYNOS5_WDT_DISABLE_REG_OFFSET
,
214 .mask_reset_reg
= EXYNOS5_WDT_MASK_RESET_REG_OFFSET
,
216 .rst_stat_reg
= EXYNOS5_RST_STAT_REG_OFFSET
,
218 .quirks
= QUIRK_HAS_WTCLRINT_REG
| QUIRK_HAS_PMU_MASK_RESET
| \
219 QUIRK_HAS_PMU_RST_STAT
| QUIRK_HAS_PMU_AUTO_DISABLE
,
222 static const struct s3c2410_wdt_variant drv_data_exynos7
= {
223 .disable_reg
= EXYNOS5_WDT_DISABLE_REG_OFFSET
,
224 .mask_reset_reg
= EXYNOS5_WDT_MASK_RESET_REG_OFFSET
,
226 .rst_stat_reg
= EXYNOS5_RST_STAT_REG_OFFSET
,
227 .rst_stat_bit
= 23, /* A57 WDTRESET */
228 .quirks
= QUIRK_HAS_WTCLRINT_REG
| QUIRK_HAS_PMU_MASK_RESET
| \
229 QUIRK_HAS_PMU_RST_STAT
| QUIRK_HAS_PMU_AUTO_DISABLE
,
232 static const struct s3c2410_wdt_variant drv_data_exynos850_cl0
= {
233 .mask_reset_reg
= EXYNOS850_CLUSTER0_NONCPU_INT_EN
,
235 .mask_reset_inv
= true,
236 .rst_stat_reg
= EXYNOS5_RST_STAT_REG_OFFSET
,
237 .rst_stat_bit
= EXYNOS850_CLUSTER0_WDTRESET_BIT
,
238 .cnt_en_reg
= EXYNOS850_CLUSTER0_NONCPU_OUT
,
240 .quirks
= QUIRK_HAS_WTCLRINT_REG
| QUIRK_HAS_PMU_MASK_RESET
| \
241 QUIRK_HAS_PMU_RST_STAT
| QUIRK_HAS_PMU_CNT_EN
,
244 static const struct s3c2410_wdt_variant drv_data_exynos850_cl1
= {
245 .mask_reset_reg
= EXYNOS850_CLUSTER1_NONCPU_INT_EN
,
247 .mask_reset_inv
= true,
248 .rst_stat_reg
= EXYNOS5_RST_STAT_REG_OFFSET
,
249 .rst_stat_bit
= EXYNOS850_CLUSTER1_WDTRESET_BIT
,
250 .cnt_en_reg
= EXYNOS850_CLUSTER1_NONCPU_OUT
,
252 .quirks
= QUIRK_HAS_WTCLRINT_REG
| QUIRK_HAS_PMU_MASK_RESET
| \
253 QUIRK_HAS_PMU_RST_STAT
| QUIRK_HAS_PMU_CNT_EN
,
256 static const struct s3c2410_wdt_variant drv_data_exynosautov9_cl0
= {
257 .mask_reset_reg
= EXYNOS850_CLUSTER0_NONCPU_INT_EN
,
259 .mask_reset_inv
= true,
260 .rst_stat_reg
= EXYNOS5_RST_STAT_REG_OFFSET
,
261 .rst_stat_bit
= EXYNOSAUTOV9_CLUSTER0_WDTRESET_BIT
,
262 .cnt_en_reg
= EXYNOS850_CLUSTER0_NONCPU_OUT
,
264 .quirks
= QUIRK_HAS_WTCLRINT_REG
| QUIRK_HAS_PMU_MASK_RESET
|
265 QUIRK_HAS_PMU_RST_STAT
| QUIRK_HAS_PMU_CNT_EN
,
268 static const struct s3c2410_wdt_variant drv_data_exynosautov9_cl1
= {
269 .mask_reset_reg
= EXYNOSAUTOV9_CLUSTER1_NONCPU_INT_EN
,
271 .mask_reset_inv
= true,
272 .rst_stat_reg
= EXYNOS5_RST_STAT_REG_OFFSET
,
273 .rst_stat_bit
= EXYNOSAUTOV9_CLUSTER1_WDTRESET_BIT
,
274 .cnt_en_reg
= EXYNOSAUTOV9_CLUSTER1_NONCPU_OUT
,
276 .quirks
= QUIRK_HAS_WTCLRINT_REG
| QUIRK_HAS_PMU_MASK_RESET
|
277 QUIRK_HAS_PMU_RST_STAT
| QUIRK_HAS_PMU_CNT_EN
,
280 static const struct s3c2410_wdt_variant drv_data_gs101_cl0
= {
281 .mask_reset_reg
= GS_CLUSTER0_NONCPU_INT_EN
,
283 .mask_reset_inv
= true,
284 .rst_stat_reg
= GS_RST_STAT_REG_OFFSET
,
286 .cnt_en_reg
= GS_CLUSTER0_NONCPU_OUT
,
288 .quirks
= QUIRK_HAS_PMU_RST_STAT
| QUIRK_HAS_PMU_MASK_RESET
|
289 QUIRK_HAS_PMU_CNT_EN
| QUIRK_HAS_WTCLRINT_REG
|
290 QUIRK_HAS_DBGACK_BIT
,
293 static const struct s3c2410_wdt_variant drv_data_gs101_cl1
= {
294 .mask_reset_reg
= GS_CLUSTER1_NONCPU_INT_EN
,
296 .mask_reset_inv
= true,
297 .rst_stat_reg
= GS_RST_STAT_REG_OFFSET
,
299 .cnt_en_reg
= GS_CLUSTER1_NONCPU_OUT
,
301 .quirks
= QUIRK_HAS_PMU_RST_STAT
| QUIRK_HAS_PMU_MASK_RESET
|
302 QUIRK_HAS_PMU_CNT_EN
| QUIRK_HAS_WTCLRINT_REG
|
303 QUIRK_HAS_DBGACK_BIT
,
306 static const struct of_device_id s3c2410_wdt_match
[] = {
307 { .compatible
= "google,gs101-wdt",
308 .data
= &drv_data_gs101_cl0
},
309 { .compatible
= "samsung,s3c2410-wdt",
310 .data
= &drv_data_s3c2410
},
311 { .compatible
= "samsung,s3c6410-wdt",
312 .data
= &drv_data_s3c6410
},
313 { .compatible
= "samsung,exynos5250-wdt",
314 .data
= &drv_data_exynos5250
},
315 { .compatible
= "samsung,exynos5420-wdt",
316 .data
= &drv_data_exynos5420
},
317 { .compatible
= "samsung,exynos7-wdt",
318 .data
= &drv_data_exynos7
},
319 { .compatible
= "samsung,exynos850-wdt",
320 .data
= &drv_data_exynos850_cl0
},
321 { .compatible
= "samsung,exynosautov9-wdt",
322 .data
= &drv_data_exynosautov9_cl0
},
325 MODULE_DEVICE_TABLE(of
, s3c2410_wdt_match
);
328 static const struct platform_device_id s3c2410_wdt_ids
[] = {
330 .name
= "s3c2410-wdt",
331 .driver_data
= (unsigned long)&drv_data_s3c2410
,
335 MODULE_DEVICE_TABLE(platform
, s3c2410_wdt_ids
);
339 static inline unsigned long s3c2410wdt_get_freq(struct s3c2410_wdt
*wdt
)
341 return clk_get_rate(wdt
->src_clk
? wdt
->src_clk
: wdt
->bus_clk
);
344 static inline unsigned int s3c2410wdt_max_timeout(struct s3c2410_wdt
*wdt
)
346 const unsigned long freq
= s3c2410wdt_get_freq(wdt
);
348 return S3C2410_WTCNT_MAXCNT
/ (freq
/ (S3C2410_WTCON_PRESCALE_MAX
+ 1)
349 / S3C2410_WTCON_MAXDIV
);
352 static int s3c2410wdt_disable_wdt_reset(struct s3c2410_wdt
*wdt
, bool mask
)
354 const u32 mask_val
= BIT(wdt
->drv_data
->mask_bit
);
355 const u32 val
= mask
? mask_val
: 0;
358 ret
= regmap_update_bits(wdt
->pmureg
, wdt
->drv_data
->disable_reg
,
361 dev_err(wdt
->dev
, "failed to update reg(%d)\n", ret
);
366 static int s3c2410wdt_mask_wdt_reset(struct s3c2410_wdt
*wdt
, bool mask
)
368 const u32 mask_val
= BIT(wdt
->drv_data
->mask_bit
);
369 const bool val_inv
= wdt
->drv_data
->mask_reset_inv
;
370 const u32 val
= (mask
^ val_inv
) ? mask_val
: 0;
373 ret
= regmap_update_bits(wdt
->pmureg
, wdt
->drv_data
->mask_reset_reg
,
376 dev_err(wdt
->dev
, "failed to update reg(%d)\n", ret
);
381 static int s3c2410wdt_enable_counter(struct s3c2410_wdt
*wdt
, bool en
)
383 const u32 mask_val
= BIT(wdt
->drv_data
->cnt_en_bit
);
384 const u32 val
= en
? mask_val
: 0;
387 ret
= regmap_update_bits(wdt
->pmureg
, wdt
->drv_data
->cnt_en_reg
,
390 dev_err(wdt
->dev
, "failed to update reg(%d)\n", ret
);
395 static int s3c2410wdt_enable(struct s3c2410_wdt
*wdt
, bool en
)
399 if (wdt
->drv_data
->quirks
& QUIRK_HAS_PMU_AUTO_DISABLE
) {
400 ret
= s3c2410wdt_disable_wdt_reset(wdt
, !en
);
405 if (wdt
->drv_data
->quirks
& QUIRK_HAS_PMU_MASK_RESET
) {
406 ret
= s3c2410wdt_mask_wdt_reset(wdt
, !en
);
411 if (wdt
->drv_data
->quirks
& QUIRK_HAS_PMU_CNT_EN
) {
412 ret
= s3c2410wdt_enable_counter(wdt
, en
);
420 /* Disable watchdog outputs if CPU is in debug mode */
421 static void s3c2410wdt_mask_dbgack(struct s3c2410_wdt
*wdt
)
425 if (!(wdt
->drv_data
->quirks
& QUIRK_HAS_DBGACK_BIT
))
428 wtcon
= readl(wdt
->reg_base
+ S3C2410_WTCON
);
429 wtcon
|= S3C2410_WTCON_DBGACK_MASK
;
430 writel(wtcon
, wdt
->reg_base
+ S3C2410_WTCON
);
433 static int s3c2410wdt_keepalive(struct watchdog_device
*wdd
)
435 struct s3c2410_wdt
*wdt
= watchdog_get_drvdata(wdd
);
438 spin_lock_irqsave(&wdt
->lock
, flags
);
439 writel(wdt
->count
, wdt
->reg_base
+ S3C2410_WTCNT
);
440 spin_unlock_irqrestore(&wdt
->lock
, flags
);
445 static void __s3c2410wdt_stop(struct s3c2410_wdt
*wdt
)
449 wtcon
= readl(wdt
->reg_base
+ S3C2410_WTCON
);
450 wtcon
&= ~(S3C2410_WTCON_ENABLE
| S3C2410_WTCON_RSTEN
);
451 writel(wtcon
, wdt
->reg_base
+ S3C2410_WTCON
);
454 static int s3c2410wdt_stop(struct watchdog_device
*wdd
)
456 struct s3c2410_wdt
*wdt
= watchdog_get_drvdata(wdd
);
459 spin_lock_irqsave(&wdt
->lock
, flags
);
460 __s3c2410wdt_stop(wdt
);
461 spin_unlock_irqrestore(&wdt
->lock
, flags
);
466 static int s3c2410wdt_start(struct watchdog_device
*wdd
)
469 struct s3c2410_wdt
*wdt
= watchdog_get_drvdata(wdd
);
472 spin_lock_irqsave(&wdt
->lock
, flags
);
474 __s3c2410wdt_stop(wdt
);
476 wtcon
= readl(wdt
->reg_base
+ S3C2410_WTCON
);
477 wtcon
|= S3C2410_WTCON_ENABLE
| S3C2410_WTCON_DIV128
;
480 wtcon
|= S3C2410_WTCON_INTEN
;
481 wtcon
&= ~S3C2410_WTCON_RSTEN
;
483 wtcon
&= ~S3C2410_WTCON_INTEN
;
484 wtcon
|= S3C2410_WTCON_RSTEN
;
487 dev_dbg(wdt
->dev
, "Starting watchdog: count=0x%08x, wtcon=%08lx\n",
490 writel(wdt
->count
, wdt
->reg_base
+ S3C2410_WTDAT
);
491 writel(wdt
->count
, wdt
->reg_base
+ S3C2410_WTCNT
);
492 writel(wtcon
, wdt
->reg_base
+ S3C2410_WTCON
);
493 spin_unlock_irqrestore(&wdt
->lock
, flags
);
498 static int s3c2410wdt_set_heartbeat(struct watchdog_device
*wdd
,
499 unsigned int timeout
)
501 struct s3c2410_wdt
*wdt
= watchdog_get_drvdata(wdd
);
502 unsigned long freq
= s3c2410wdt_get_freq(wdt
);
504 unsigned int divisor
= 1;
510 freq
= DIV_ROUND_UP(freq
, 128);
511 count
= timeout
* freq
;
513 dev_dbg(wdt
->dev
, "Heartbeat: count=%d, timeout=%d, freq=%lu\n",
514 count
, timeout
, freq
);
516 /* if the count is bigger than the watchdog register,
517 then work out what we need to do (and if) we can
518 actually make this value
521 if (count
>= 0x10000) {
522 divisor
= DIV_ROUND_UP(count
, 0xffff);
524 if (divisor
> 0x100) {
525 dev_err(wdt
->dev
, "timeout %d too big\n", timeout
);
530 dev_dbg(wdt
->dev
, "Heartbeat: timeout=%d, divisor=%d, count=%d (%08x)\n",
531 timeout
, divisor
, count
, DIV_ROUND_UP(count
, divisor
));
533 count
= DIV_ROUND_UP(count
, divisor
);
536 /* update the pre-scaler */
537 wtcon
= readl(wdt
->reg_base
+ S3C2410_WTCON
);
538 wtcon
&= ~S3C2410_WTCON_PRESCALE_MASK
;
539 wtcon
|= S3C2410_WTCON_PRESCALE(divisor
-1);
541 writel(count
, wdt
->reg_base
+ S3C2410_WTDAT
);
542 writel(wtcon
, wdt
->reg_base
+ S3C2410_WTCON
);
544 wdd
->timeout
= (count
* divisor
) / freq
;
549 static int s3c2410wdt_restart(struct watchdog_device
*wdd
, unsigned long action
,
552 struct s3c2410_wdt
*wdt
= watchdog_get_drvdata(wdd
);
553 void __iomem
*wdt_base
= wdt
->reg_base
;
555 /* disable watchdog, to be safe */
556 writel(0, wdt_base
+ S3C2410_WTCON
);
558 /* put initial values into count and data */
559 writel(0x80, wdt_base
+ S3C2410_WTCNT
);
560 writel(0x80, wdt_base
+ S3C2410_WTDAT
);
562 /* set the watchdog to go and reset... */
563 writel(S3C2410_WTCON_ENABLE
| S3C2410_WTCON_DIV16
|
564 S3C2410_WTCON_RSTEN
| S3C2410_WTCON_PRESCALE(0x20),
565 wdt_base
+ S3C2410_WTCON
);
567 /* wait for reset to assert... */
573 #define OPTIONS (WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE)
575 static const struct watchdog_info s3c2410_wdt_ident
= {
577 .firmware_version
= 0,
578 .identity
= "S3C2410 Watchdog",
581 static const struct watchdog_ops s3c2410wdt_ops
= {
582 .owner
= THIS_MODULE
,
583 .start
= s3c2410wdt_start
,
584 .stop
= s3c2410wdt_stop
,
585 .ping
= s3c2410wdt_keepalive
,
586 .set_timeout
= s3c2410wdt_set_heartbeat
,
587 .restart
= s3c2410wdt_restart
,
590 static const struct watchdog_device s3c2410_wdd
= {
591 .info
= &s3c2410_wdt_ident
,
592 .ops
= &s3c2410wdt_ops
,
593 .timeout
= S3C2410_WATCHDOG_DEFAULT_TIME
,
596 /* interrupt handler code */
598 static irqreturn_t
s3c2410wdt_irq(int irqno
, void *param
)
600 struct s3c2410_wdt
*wdt
= platform_get_drvdata(param
);
602 dev_info(wdt
->dev
, "watchdog timer expired (irq)\n");
604 s3c2410wdt_keepalive(&wdt
->wdt_device
);
606 if (wdt
->drv_data
->quirks
& QUIRK_HAS_WTCLRINT_REG
)
607 writel(0x1, wdt
->reg_base
+ S3C2410_WTCLRINT
);
612 static inline unsigned int s3c2410wdt_get_bootstatus(struct s3c2410_wdt
*wdt
)
614 unsigned int rst_stat
;
617 if (!(wdt
->drv_data
->quirks
& QUIRK_HAS_PMU_RST_STAT
))
620 ret
= regmap_read(wdt
->pmureg
, wdt
->drv_data
->rst_stat_reg
, &rst_stat
);
622 dev_warn(wdt
->dev
, "Couldn't get RST_STAT register\n");
623 else if (rst_stat
& BIT(wdt
->drv_data
->rst_stat_bit
))
624 return WDIOF_CARDRESET
;
630 s3c2410_get_wdt_drv_data(struct platform_device
*pdev
, struct s3c2410_wdt
*wdt
)
632 const struct s3c2410_wdt_variant
*variant
;
633 struct device
*dev
= &pdev
->dev
;
635 variant
= of_device_get_match_data(dev
);
637 /* Device matched by platform_device_id */
638 variant
= (struct s3c2410_wdt_variant
*)
639 platform_get_device_id(pdev
)->driver_data
;
643 /* Choose Exynos850/ExynosAutov9 driver data w.r.t. cluster index */
644 if (variant
== &drv_data_exynos850_cl0
||
645 variant
== &drv_data_exynosautov9_cl0
||
646 variant
== &drv_data_gs101_cl0
) {
650 err
= of_property_read_u32(dev
->of_node
,
651 "samsung,cluster-index", &index
);
653 return dev_err_probe(dev
, -EINVAL
, "failed to get cluster index\n");
659 if (variant
== &drv_data_exynos850_cl0
)
660 variant
= &drv_data_exynos850_cl1
;
661 else if (variant
== &drv_data_exynosautov9_cl0
)
662 variant
= &drv_data_exynosautov9_cl1
;
663 else if (variant
== &drv_data_gs101_cl0
)
664 variant
= &drv_data_gs101_cl1
;
667 return dev_err_probe(dev
, -EINVAL
, "wrong cluster index: %u\n", index
);
672 wdt
->drv_data
= variant
;
676 static void s3c2410wdt_wdt_disable_action(void *data
)
678 s3c2410wdt_enable(data
, false);
681 static int s3c2410wdt_probe(struct platform_device
*pdev
)
683 struct device
*dev
= &pdev
->dev
;
684 struct s3c2410_wdt
*wdt
;
689 wdt
= devm_kzalloc(dev
, sizeof(*wdt
), GFP_KERNEL
);
694 spin_lock_init(&wdt
->lock
);
695 wdt
->wdt_device
= s3c2410_wdd
;
697 ret
= s3c2410_get_wdt_drv_data(pdev
, wdt
);
701 if (wdt
->drv_data
->quirks
& QUIRKS_HAVE_PMUREG
) {
702 wdt
->pmureg
= exynos_get_pmu_regmap_by_phandle(dev
->of_node
,
703 "samsung,syscon-phandle");
704 if (IS_ERR(wdt
->pmureg
))
705 return dev_err_probe(dev
, PTR_ERR(wdt
->pmureg
),
706 "PMU regmap lookup failed.\n");
709 wdt_irq
= platform_get_irq(pdev
, 0);
713 /* get the memory region for the watchdog timer */
714 wdt
->reg_base
= devm_platform_ioremap_resource(pdev
, 0);
715 if (IS_ERR(wdt
->reg_base
))
716 return PTR_ERR(wdt
->reg_base
);
718 wdt
->bus_clk
= devm_clk_get_enabled(dev
, "watchdog");
719 if (IS_ERR(wdt
->bus_clk
))
720 return dev_err_probe(dev
, PTR_ERR(wdt
->bus_clk
), "failed to get bus clock\n");
723 * "watchdog_src" clock is optional; if it's not present -- just skip it
724 * and use "watchdog" clock as both bus and source clock.
726 wdt
->src_clk
= devm_clk_get_optional_enabled(dev
, "watchdog_src");
727 if (IS_ERR(wdt
->src_clk
))
728 return dev_err_probe(dev
, PTR_ERR(wdt
->src_clk
), "failed to get source clock\n");
730 wdt
->wdt_device
.min_timeout
= 1;
731 wdt
->wdt_device
.max_timeout
= s3c2410wdt_max_timeout(wdt
);
733 watchdog_set_drvdata(&wdt
->wdt_device
, wdt
);
735 /* see if we can actually set the requested timer margin, and if
736 * not, try the default value */
738 watchdog_init_timeout(&wdt
->wdt_device
, tmr_margin
, dev
);
739 ret
= s3c2410wdt_set_heartbeat(&wdt
->wdt_device
,
740 wdt
->wdt_device
.timeout
);
742 ret
= s3c2410wdt_set_heartbeat(&wdt
->wdt_device
,
743 S3C2410_WATCHDOG_DEFAULT_TIME
);
745 dev_warn(dev
, "tmr_margin value out of range, default %d used\n",
746 S3C2410_WATCHDOG_DEFAULT_TIME
);
748 return dev_err_probe(dev
, ret
, "failed to use default timeout\n");
751 ret
= devm_request_irq(dev
, wdt_irq
, s3c2410wdt_irq
, 0,
754 return dev_err_probe(dev
, ret
, "failed to install irq (%d)\n", ret
);
756 watchdog_set_nowayout(&wdt
->wdt_device
, nowayout
);
757 watchdog_set_restart_priority(&wdt
->wdt_device
, 128);
759 wdt
->wdt_device
.bootstatus
= s3c2410wdt_get_bootstatus(wdt
);
760 wdt
->wdt_device
.parent
= dev
;
762 s3c2410wdt_mask_dbgack(wdt
);
765 * If "tmr_atboot" param is non-zero, start the watchdog right now. Also
766 * set WDOG_HW_RUNNING bit, so that watchdog core can kick the watchdog.
768 * If we're not enabling the watchdog, then ensure it is disabled if it
769 * has been left running from the bootloader or other source.
772 dev_info(dev
, "starting watchdog timer\n");
773 s3c2410wdt_start(&wdt
->wdt_device
);
774 set_bit(WDOG_HW_RUNNING
, &wdt
->wdt_device
.status
);
776 s3c2410wdt_stop(&wdt
->wdt_device
);
779 ret
= devm_watchdog_register_device(dev
, &wdt
->wdt_device
);
783 ret
= s3c2410wdt_enable(wdt
, true);
787 ret
= devm_add_action_or_reset(dev
, s3c2410wdt_wdt_disable_action
, wdt
);
791 platform_set_drvdata(pdev
, wdt
);
793 /* print out a statement of readiness */
795 wtcon
= readl(wdt
->reg_base
+ S3C2410_WTCON
);
797 dev_info(dev
, "watchdog %sactive, reset %sabled, irq %sabled\n",
798 (wtcon
& S3C2410_WTCON_ENABLE
) ? "" : "in",
799 (wtcon
& S3C2410_WTCON_RSTEN
) ? "en" : "dis",
800 (wtcon
& S3C2410_WTCON_INTEN
) ? "en" : "dis");
805 static void s3c2410wdt_shutdown(struct platform_device
*dev
)
807 struct s3c2410_wdt
*wdt
= platform_get_drvdata(dev
);
809 s3c2410wdt_enable(wdt
, false);
810 s3c2410wdt_stop(&wdt
->wdt_device
);
813 static int s3c2410wdt_suspend(struct device
*dev
)
816 struct s3c2410_wdt
*wdt
= dev_get_drvdata(dev
);
818 /* Save watchdog state, and turn it off. */
819 wdt
->wtcon_save
= readl(wdt
->reg_base
+ S3C2410_WTCON
);
820 wdt
->wtdat_save
= readl(wdt
->reg_base
+ S3C2410_WTDAT
);
822 ret
= s3c2410wdt_enable(wdt
, false);
826 /* Note that WTCNT doesn't need to be saved. */
827 s3c2410wdt_stop(&wdt
->wdt_device
);
832 static int s3c2410wdt_resume(struct device
*dev
)
835 struct s3c2410_wdt
*wdt
= dev_get_drvdata(dev
);
837 /* Restore watchdog state. */
838 writel(wdt
->wtdat_save
, wdt
->reg_base
+ S3C2410_WTDAT
);
839 writel(wdt
->wtdat_save
, wdt
->reg_base
+ S3C2410_WTCNT
);/* Reset count */
840 writel(wdt
->wtcon_save
, wdt
->reg_base
+ S3C2410_WTCON
);
842 ret
= s3c2410wdt_enable(wdt
, true);
846 dev_info(dev
, "watchdog %sabled\n",
847 (wdt
->wtcon_save
& S3C2410_WTCON_ENABLE
) ? "en" : "dis");
852 static DEFINE_SIMPLE_DEV_PM_OPS(s3c2410wdt_pm_ops
,
853 s3c2410wdt_suspend
, s3c2410wdt_resume
);
855 static struct platform_driver s3c2410wdt_driver
= {
856 .probe
= s3c2410wdt_probe
,
857 .shutdown
= s3c2410wdt_shutdown
,
858 .id_table
= s3c2410_wdt_ids
,
860 .name
= "s3c2410-wdt",
861 .pm
= pm_sleep_ptr(&s3c2410wdt_pm_ops
),
862 .of_match_table
= of_match_ptr(s3c2410_wdt_match
),
866 module_platform_driver(s3c2410wdt_driver
);
868 MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>, Dimitry Andric <dimitry.andric@tomtom.com>");
869 MODULE_DESCRIPTION("S3C2410 Watchdog Device Driver");
870 MODULE_LICENSE("GPL");