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