1 /* linux/drivers/char/watchdog/s3c2410_wdt.c
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  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
24 */
25 
26 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
27 
28 #include <linux/module.h>
29 #include <linux/moduleparam.h>
30 #include <linux/types.h>
31 #include <linux/timer.h>
32 #include <linux/watchdog.h>
33 #include <linux/platform_device.h>
34 #include <linux/interrupt.h>
35 #include <linux/clk.h>
36 #include <linux/uaccess.h>
37 #include <linux/io.h>
38 #include <linux/cpufreq.h>
39 #include <linux/slab.h>
40 #include <linux/err.h>
41 #include <linux/of.h>
42 #include <linux/mfd/syscon.h>
43 #include <linux/regmap.h>
44 #include <linux/delay.h>
45 
46 #define S3C2410_WTCON		0x00
47 #define S3C2410_WTDAT		0x04
48 #define S3C2410_WTCNT		0x08
49 
50 #define S3C2410_WTCNT_MAXCNT	0xffff
51 
52 #define S3C2410_WTCON_RSTEN	(1 << 0)
53 #define S3C2410_WTCON_INTEN	(1 << 2)
54 #define S3C2410_WTCON_ENABLE	(1 << 5)
55 
56 #define S3C2410_WTCON_DIV16	(0 << 3)
57 #define S3C2410_WTCON_DIV32	(1 << 3)
58 #define S3C2410_WTCON_DIV64	(2 << 3)
59 #define S3C2410_WTCON_DIV128	(3 << 3)
60 
61 #define S3C2410_WTCON_MAXDIV	0x80
62 
63 #define S3C2410_WTCON_PRESCALE(x)	((x) << 8)
64 #define S3C2410_WTCON_PRESCALE_MASK	(0xff << 8)
65 #define S3C2410_WTCON_PRESCALE_MAX	0xff
66 
67 #define CONFIG_S3C2410_WATCHDOG_ATBOOT		(0)
68 #define CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME	(15)
69 
70 #define EXYNOS5_RST_STAT_REG_OFFSET		0x0404
71 #define EXYNOS5_WDT_DISABLE_REG_OFFSET		0x0408
72 #define EXYNOS5_WDT_MASK_RESET_REG_OFFSET	0x040c
73 #define QUIRK_HAS_PMU_CONFIG			(1 << 0)
74 #define QUIRK_HAS_RST_STAT			(1 << 1)
75 
76 /* These quirks require that we have a PMU register map */
77 #define QUIRKS_HAVE_PMUREG			(QUIRK_HAS_PMU_CONFIG | \
78 						 QUIRK_HAS_RST_STAT)
79 
80 static bool nowayout	= WATCHDOG_NOWAYOUT;
81 static int tmr_margin;
82 static int tmr_atboot	= CONFIG_S3C2410_WATCHDOG_ATBOOT;
83 static int soft_noboot;
84 static int debug;
85 
86 module_param(tmr_margin,  int, 0);
87 module_param(tmr_atboot,  int, 0);
88 module_param(nowayout,   bool, 0);
89 module_param(soft_noboot, int, 0);
90 module_param(debug,	  int, 0);
91 
92 MODULE_PARM_DESC(tmr_margin, "Watchdog tmr_margin in seconds. (default="
93 		__MODULE_STRING(CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME) ")");
94 MODULE_PARM_DESC(tmr_atboot,
95 		"Watchdog is started at boot time if set to 1, default="
96 			__MODULE_STRING(CONFIG_S3C2410_WATCHDOG_ATBOOT));
97 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
98 			__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
99 MODULE_PARM_DESC(soft_noboot, "Watchdog action, set to 1 to ignore reboots, "
100 			"0 to reboot (default 0)");
101 MODULE_PARM_DESC(debug, "Watchdog debug, set to >1 for debug (default 0)");
102 
103 /**
104  * struct s3c2410_wdt_variant - Per-variant config data
105  *
106  * @disable_reg: Offset in pmureg for the register that disables the watchdog
107  * timer reset functionality.
108  * @mask_reset_reg: Offset in pmureg for the register that masks the watchdog
109  * timer reset functionality.
110  * @mask_bit: Bit number for the watchdog timer in the disable register and the
111  * mask reset register.
112  * @rst_stat_reg: Offset in pmureg for the register that has the reset status.
113  * @rst_stat_bit: Bit number in the rst_stat register indicating a watchdog
114  * reset.
115  * @quirks: A bitfield of quirks.
116  */
117 
118 struct s3c2410_wdt_variant {
119 	int disable_reg;
120 	int mask_reset_reg;
121 	int mask_bit;
122 	int rst_stat_reg;
123 	int rst_stat_bit;
124 	u32 quirks;
125 };
126 
127 struct s3c2410_wdt {
128 	struct device		*dev;
129 	struct clk		*clock;
130 	void __iomem		*reg_base;
131 	unsigned int		count;
132 	spinlock_t		lock;
133 	unsigned long		wtcon_save;
134 	unsigned long		wtdat_save;
135 	struct watchdog_device	wdt_device;
136 	struct notifier_block	freq_transition;
137 	struct s3c2410_wdt_variant *drv_data;
138 	struct regmap *pmureg;
139 };
140 
141 static const struct s3c2410_wdt_variant drv_data_s3c2410 = {
142 	.quirks = 0
143 };
144 
145 #ifdef CONFIG_OF
146 static const struct s3c2410_wdt_variant drv_data_exynos5250  = {
147 	.disable_reg = EXYNOS5_WDT_DISABLE_REG_OFFSET,
148 	.mask_reset_reg = EXYNOS5_WDT_MASK_RESET_REG_OFFSET,
149 	.mask_bit = 20,
150 	.rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET,
151 	.rst_stat_bit = 20,
152 	.quirks = QUIRK_HAS_PMU_CONFIG | QUIRK_HAS_RST_STAT,
153 };
154 
155 static const struct s3c2410_wdt_variant drv_data_exynos5420 = {
156 	.disable_reg = EXYNOS5_WDT_DISABLE_REG_OFFSET,
157 	.mask_reset_reg = EXYNOS5_WDT_MASK_RESET_REG_OFFSET,
158 	.mask_bit = 0,
159 	.rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET,
160 	.rst_stat_bit = 9,
161 	.quirks = QUIRK_HAS_PMU_CONFIG | QUIRK_HAS_RST_STAT,
162 };
163 
164 static const struct s3c2410_wdt_variant drv_data_exynos7 = {
165 	.disable_reg = EXYNOS5_WDT_DISABLE_REG_OFFSET,
166 	.mask_reset_reg = EXYNOS5_WDT_MASK_RESET_REG_OFFSET,
167 	.mask_bit = 23,
168 	.rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET,
169 	.rst_stat_bit = 23,	/* A57 WDTRESET */
170 	.quirks = QUIRK_HAS_PMU_CONFIG | QUIRK_HAS_RST_STAT,
171 };
172 
173 static const struct of_device_id s3c2410_wdt_match[] = {
174 	{ .compatible = "samsung,s3c2410-wdt",
175 	  .data = &drv_data_s3c2410 },
176 	{ .compatible = "samsung,exynos5250-wdt",
177 	  .data = &drv_data_exynos5250 },
178 	{ .compatible = "samsung,exynos5420-wdt",
179 	  .data = &drv_data_exynos5420 },
180 	{ .compatible = "samsung,exynos7-wdt",
181 	  .data = &drv_data_exynos7 },
182 	{},
183 };
184 MODULE_DEVICE_TABLE(of, s3c2410_wdt_match);
185 #endif
186 
187 static const struct platform_device_id s3c2410_wdt_ids[] = {
188 	{
189 		.name = "s3c2410-wdt",
190 		.driver_data = (unsigned long)&drv_data_s3c2410,
191 	},
192 	{}
193 };
194 MODULE_DEVICE_TABLE(platform, s3c2410_wdt_ids);
195 
196 /* watchdog control routines */
197 
198 #define DBG(fmt, ...)					\
199 do {							\
200 	if (debug)					\
201 		pr_info(fmt, ##__VA_ARGS__);		\
202 } while (0)
203 
204 /* functions */
205 
206 static inline unsigned int s3c2410wdt_max_timeout(struct clk *clock)
207 {
208 	unsigned long freq = clk_get_rate(clock);
209 
210 	return S3C2410_WTCNT_MAXCNT / (freq / (S3C2410_WTCON_PRESCALE_MAX + 1)
211 				       / S3C2410_WTCON_MAXDIV);
212 }
213 
214 static inline struct s3c2410_wdt *freq_to_wdt(struct notifier_block *nb)
215 {
216 	return container_of(nb, struct s3c2410_wdt, freq_transition);
217 }
218 
219 static int s3c2410wdt_mask_and_disable_reset(struct s3c2410_wdt *wdt, bool mask)
220 {
221 	int ret;
222 	u32 mask_val = 1 << wdt->drv_data->mask_bit;
223 	u32 val = 0;
224 
225 	/* No need to do anything if no PMU CONFIG needed */
226 	if (!(wdt->drv_data->quirks & QUIRK_HAS_PMU_CONFIG))
227 		return 0;
228 
229 	if (mask)
230 		val = mask_val;
231 
232 	ret = regmap_update_bits(wdt->pmureg,
233 			wdt->drv_data->disable_reg,
234 			mask_val, val);
235 	if (ret < 0)
236 		goto error;
237 
238 	ret = regmap_update_bits(wdt->pmureg,
239 			wdt->drv_data->mask_reset_reg,
240 			mask_val, val);
241  error:
242 	if (ret < 0)
243 		dev_err(wdt->dev, "failed to update reg(%d)\n", ret);
244 
245 	return ret;
246 }
247 
248 static int s3c2410wdt_keepalive(struct watchdog_device *wdd)
249 {
250 	struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd);
251 
252 	spin_lock(&wdt->lock);
253 	writel(wdt->count, wdt->reg_base + S3C2410_WTCNT);
254 	spin_unlock(&wdt->lock);
255 
256 	return 0;
257 }
258 
259 static void __s3c2410wdt_stop(struct s3c2410_wdt *wdt)
260 {
261 	unsigned long wtcon;
262 
263 	wtcon = readl(wdt->reg_base + S3C2410_WTCON);
264 	wtcon &= ~(S3C2410_WTCON_ENABLE | S3C2410_WTCON_RSTEN);
265 	writel(wtcon, wdt->reg_base + S3C2410_WTCON);
266 }
267 
268 static int s3c2410wdt_stop(struct watchdog_device *wdd)
269 {
270 	struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd);
271 
272 	spin_lock(&wdt->lock);
273 	__s3c2410wdt_stop(wdt);
274 	spin_unlock(&wdt->lock);
275 
276 	return 0;
277 }
278 
279 static int s3c2410wdt_start(struct watchdog_device *wdd)
280 {
281 	unsigned long wtcon;
282 	struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd);
283 
284 	spin_lock(&wdt->lock);
285 
286 	__s3c2410wdt_stop(wdt);
287 
288 	wtcon = readl(wdt->reg_base + S3C2410_WTCON);
289 	wtcon |= S3C2410_WTCON_ENABLE | S3C2410_WTCON_DIV128;
290 
291 	if (soft_noboot) {
292 		wtcon |= S3C2410_WTCON_INTEN;
293 		wtcon &= ~S3C2410_WTCON_RSTEN;
294 	} else {
295 		wtcon &= ~S3C2410_WTCON_INTEN;
296 		wtcon |= S3C2410_WTCON_RSTEN;
297 	}
298 
299 	DBG("%s: count=0x%08x, wtcon=%08lx\n",
300 	    __func__, wdt->count, wtcon);
301 
302 	writel(wdt->count, wdt->reg_base + S3C2410_WTDAT);
303 	writel(wdt->count, wdt->reg_base + S3C2410_WTCNT);
304 	writel(wtcon, wdt->reg_base + S3C2410_WTCON);
305 	spin_unlock(&wdt->lock);
306 
307 	return 0;
308 }
309 
310 static inline int s3c2410wdt_is_running(struct s3c2410_wdt *wdt)
311 {
312 	return readl(wdt->reg_base + S3C2410_WTCON) & S3C2410_WTCON_ENABLE;
313 }
314 
315 static int s3c2410wdt_set_heartbeat(struct watchdog_device *wdd, unsigned timeout)
316 {
317 	struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd);
318 	unsigned long freq = clk_get_rate(wdt->clock);
319 	unsigned int count;
320 	unsigned int divisor = 1;
321 	unsigned long wtcon;
322 
323 	if (timeout < 1)
324 		return -EINVAL;
325 
326 	freq = DIV_ROUND_UP(freq, 128);
327 	count = timeout * freq;
328 
329 	DBG("%s: count=%d, timeout=%d, freq=%lu\n",
330 	    __func__, count, timeout, freq);
331 
332 	/* if the count is bigger than the watchdog register,
333 	   then work out what we need to do (and if) we can
334 	   actually make this value
335 	*/
336 
337 	if (count >= 0x10000) {
338 		divisor = DIV_ROUND_UP(count, 0xffff);
339 
340 		if (divisor > 0x100) {
341 			dev_err(wdt->dev, "timeout %d too big\n", timeout);
342 			return -EINVAL;
343 		}
344 	}
345 
346 	DBG("%s: timeout=%d, divisor=%d, count=%d (%08x)\n",
347 	    __func__, timeout, divisor, count, DIV_ROUND_UP(count, divisor));
348 
349 	count = DIV_ROUND_UP(count, divisor);
350 	wdt->count = count;
351 
352 	/* update the pre-scaler */
353 	wtcon = readl(wdt->reg_base + S3C2410_WTCON);
354 	wtcon &= ~S3C2410_WTCON_PRESCALE_MASK;
355 	wtcon |= S3C2410_WTCON_PRESCALE(divisor-1);
356 
357 	writel(count, wdt->reg_base + S3C2410_WTDAT);
358 	writel(wtcon, wdt->reg_base + S3C2410_WTCON);
359 
360 	wdd->timeout = (count * divisor) / freq;
361 
362 	return 0;
363 }
364 
365 static int s3c2410wdt_restart(struct watchdog_device *wdd, unsigned long action,
366 			      void *data)
367 {
368 	struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd);
369 	void __iomem *wdt_base = wdt->reg_base;
370 
371 	/* disable watchdog, to be safe  */
372 	writel(0, wdt_base + S3C2410_WTCON);
373 
374 	/* put initial values into count and data */
375 	writel(0x80, wdt_base + S3C2410_WTCNT);
376 	writel(0x80, wdt_base + S3C2410_WTDAT);
377 
378 	/* set the watchdog to go and reset... */
379 	writel(S3C2410_WTCON_ENABLE | S3C2410_WTCON_DIV16 |
380 		S3C2410_WTCON_RSTEN | S3C2410_WTCON_PRESCALE(0x20),
381 		wdt_base + S3C2410_WTCON);
382 
383 	/* wait for reset to assert... */
384 	mdelay(500);
385 
386 	return 0;
387 }
388 
389 #define OPTIONS (WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE)
390 
391 static const struct watchdog_info s3c2410_wdt_ident = {
392 	.options          =     OPTIONS,
393 	.firmware_version =	0,
394 	.identity         =	"S3C2410 Watchdog",
395 };
396 
397 static struct watchdog_ops s3c2410wdt_ops = {
398 	.owner = THIS_MODULE,
399 	.start = s3c2410wdt_start,
400 	.stop = s3c2410wdt_stop,
401 	.ping = s3c2410wdt_keepalive,
402 	.set_timeout = s3c2410wdt_set_heartbeat,
403 	.restart = s3c2410wdt_restart,
404 };
405 
406 static struct watchdog_device s3c2410_wdd = {
407 	.info = &s3c2410_wdt_ident,
408 	.ops = &s3c2410wdt_ops,
409 	.timeout = CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME,
410 };
411 
412 /* interrupt handler code */
413 
414 static irqreturn_t s3c2410wdt_irq(int irqno, void *param)
415 {
416 	struct s3c2410_wdt *wdt = platform_get_drvdata(param);
417 
418 	dev_info(wdt->dev, "watchdog timer expired (irq)\n");
419 
420 	s3c2410wdt_keepalive(&wdt->wdt_device);
421 	return IRQ_HANDLED;
422 }
423 
424 #ifdef CONFIG_ARM_S3C24XX_CPUFREQ
425 
426 static int s3c2410wdt_cpufreq_transition(struct notifier_block *nb,
427 					  unsigned long val, void *data)
428 {
429 	int ret;
430 	struct s3c2410_wdt *wdt = freq_to_wdt(nb);
431 
432 	if (!s3c2410wdt_is_running(wdt))
433 		goto done;
434 
435 	if (val == CPUFREQ_PRECHANGE) {
436 		/* To ensure that over the change we don't cause the
437 		 * watchdog to trigger, we perform an keep-alive if
438 		 * the watchdog is running.
439 		 */
440 
441 		s3c2410wdt_keepalive(&wdt->wdt_device);
442 	} else if (val == CPUFREQ_POSTCHANGE) {
443 		s3c2410wdt_stop(&wdt->wdt_device);
444 
445 		ret = s3c2410wdt_set_heartbeat(&wdt->wdt_device,
446 						wdt->wdt_device.timeout);
447 
448 		if (ret >= 0)
449 			s3c2410wdt_start(&wdt->wdt_device);
450 		else
451 			goto err;
452 	}
453 
454 done:
455 	return 0;
456 
457  err:
458 	dev_err(wdt->dev, "cannot set new value for timeout %d\n",
459 				wdt->wdt_device.timeout);
460 	return ret;
461 }
462 
463 static inline int s3c2410wdt_cpufreq_register(struct s3c2410_wdt *wdt)
464 {
465 	wdt->freq_transition.notifier_call = s3c2410wdt_cpufreq_transition;
466 
467 	return cpufreq_register_notifier(&wdt->freq_transition,
468 					 CPUFREQ_TRANSITION_NOTIFIER);
469 }
470 
471 static inline void s3c2410wdt_cpufreq_deregister(struct s3c2410_wdt *wdt)
472 {
473 	wdt->freq_transition.notifier_call = s3c2410wdt_cpufreq_transition;
474 
475 	cpufreq_unregister_notifier(&wdt->freq_transition,
476 				    CPUFREQ_TRANSITION_NOTIFIER);
477 }
478 
479 #else
480 
481 static inline int s3c2410wdt_cpufreq_register(struct s3c2410_wdt *wdt)
482 {
483 	return 0;
484 }
485 
486 static inline void s3c2410wdt_cpufreq_deregister(struct s3c2410_wdt *wdt)
487 {
488 }
489 #endif
490 
491 static inline unsigned int s3c2410wdt_get_bootstatus(struct s3c2410_wdt *wdt)
492 {
493 	unsigned int rst_stat;
494 	int ret;
495 
496 	if (!(wdt->drv_data->quirks & QUIRK_HAS_RST_STAT))
497 		return 0;
498 
499 	ret = regmap_read(wdt->pmureg, wdt->drv_data->rst_stat_reg, &rst_stat);
500 	if (ret)
501 		dev_warn(wdt->dev, "Couldn't get RST_STAT register\n");
502 	else if (rst_stat & BIT(wdt->drv_data->rst_stat_bit))
503 		return WDIOF_CARDRESET;
504 
505 	return 0;
506 }
507 
508 /* s3c2410_get_wdt_driver_data */
509 static inline struct s3c2410_wdt_variant *
510 get_wdt_drv_data(struct platform_device *pdev)
511 {
512 	if (pdev->dev.of_node) {
513 		const struct of_device_id *match;
514 		match = of_match_node(s3c2410_wdt_match, pdev->dev.of_node);
515 		return (struct s3c2410_wdt_variant *)match->data;
516 	} else {
517 		return (struct s3c2410_wdt_variant *)
518 			platform_get_device_id(pdev)->driver_data;
519 	}
520 }
521 
522 static int s3c2410wdt_probe(struct platform_device *pdev)
523 {
524 	struct device *dev;
525 	struct s3c2410_wdt *wdt;
526 	struct resource *wdt_mem;
527 	struct resource *wdt_irq;
528 	unsigned int wtcon;
529 	int started = 0;
530 	int ret;
531 
532 	DBG("%s: probe=%p\n", __func__, pdev);
533 
534 	dev = &pdev->dev;
535 
536 	wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
537 	if (!wdt)
538 		return -ENOMEM;
539 
540 	wdt->dev = &pdev->dev;
541 	spin_lock_init(&wdt->lock);
542 	wdt->wdt_device = s3c2410_wdd;
543 
544 	wdt->drv_data = get_wdt_drv_data(pdev);
545 	if (wdt->drv_data->quirks & QUIRKS_HAVE_PMUREG) {
546 		wdt->pmureg = syscon_regmap_lookup_by_phandle(dev->of_node,
547 						"samsung,syscon-phandle");
548 		if (IS_ERR(wdt->pmureg)) {
549 			dev_err(dev, "syscon regmap lookup failed.\n");
550 			return PTR_ERR(wdt->pmureg);
551 		}
552 	}
553 
554 	wdt_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
555 	if (wdt_irq == NULL) {
556 		dev_err(dev, "no irq resource specified\n");
557 		ret = -ENOENT;
558 		goto err;
559 	}
560 
561 	/* get the memory region for the watchdog timer */
562 	wdt_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
563 	wdt->reg_base = devm_ioremap_resource(dev, wdt_mem);
564 	if (IS_ERR(wdt->reg_base)) {
565 		ret = PTR_ERR(wdt->reg_base);
566 		goto err;
567 	}
568 
569 	DBG("probe: mapped reg_base=%p\n", wdt->reg_base);
570 
571 	wdt->clock = devm_clk_get(dev, "watchdog");
572 	if (IS_ERR(wdt->clock)) {
573 		dev_err(dev, "failed to find watchdog clock source\n");
574 		ret = PTR_ERR(wdt->clock);
575 		goto err;
576 	}
577 
578 	ret = clk_prepare_enable(wdt->clock);
579 	if (ret < 0) {
580 		dev_err(dev, "failed to enable clock\n");
581 		return ret;
582 	}
583 
584 	wdt->wdt_device.min_timeout = 1;
585 	wdt->wdt_device.max_timeout = s3c2410wdt_max_timeout(wdt->clock);
586 
587 	ret = s3c2410wdt_cpufreq_register(wdt);
588 	if (ret < 0) {
589 		dev_err(dev, "failed to register cpufreq\n");
590 		goto err_clk;
591 	}
592 
593 	watchdog_set_drvdata(&wdt->wdt_device, wdt);
594 
595 	/* see if we can actually set the requested timer margin, and if
596 	 * not, try the default value */
597 
598 	watchdog_init_timeout(&wdt->wdt_device, tmr_margin, &pdev->dev);
599 	ret = s3c2410wdt_set_heartbeat(&wdt->wdt_device,
600 					wdt->wdt_device.timeout);
601 	if (ret) {
602 		started = s3c2410wdt_set_heartbeat(&wdt->wdt_device,
603 					CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME);
604 
605 		if (started == 0)
606 			dev_info(dev,
607 			   "tmr_margin value out of range, default %d used\n",
608 			       CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME);
609 		else
610 			dev_info(dev, "default timer value is out of range, "
611 							"cannot start\n");
612 	}
613 
614 	ret = devm_request_irq(dev, wdt_irq->start, s3c2410wdt_irq, 0,
615 				pdev->name, pdev);
616 	if (ret != 0) {
617 		dev_err(dev, "failed to install irq (%d)\n", ret);
618 		goto err_cpufreq;
619 	}
620 
621 	watchdog_set_nowayout(&wdt->wdt_device, nowayout);
622 	watchdog_set_restart_priority(&wdt->wdt_device, 128);
623 
624 	wdt->wdt_device.bootstatus = s3c2410wdt_get_bootstatus(wdt);
625 	wdt->wdt_device.parent = &pdev->dev;
626 
627 	ret = watchdog_register_device(&wdt->wdt_device);
628 	if (ret) {
629 		dev_err(dev, "cannot register watchdog (%d)\n", ret);
630 		goto err_cpufreq;
631 	}
632 
633 	ret = s3c2410wdt_mask_and_disable_reset(wdt, false);
634 	if (ret < 0)
635 		goto err_unregister;
636 
637 	if (tmr_atboot && started == 0) {
638 		dev_info(dev, "starting watchdog timer\n");
639 		s3c2410wdt_start(&wdt->wdt_device);
640 	} else if (!tmr_atboot) {
641 		/* if we're not enabling the watchdog, then ensure it is
642 		 * disabled if it has been left running from the bootloader
643 		 * or other source */
644 
645 		s3c2410wdt_stop(&wdt->wdt_device);
646 	}
647 
648 	platform_set_drvdata(pdev, wdt);
649 
650 	/* print out a statement of readiness */
651 
652 	wtcon = readl(wdt->reg_base + S3C2410_WTCON);
653 
654 	dev_info(dev, "watchdog %sactive, reset %sabled, irq %sabled\n",
655 		 (wtcon & S3C2410_WTCON_ENABLE) ?  "" : "in",
656 		 (wtcon & S3C2410_WTCON_RSTEN) ? "en" : "dis",
657 		 (wtcon & S3C2410_WTCON_INTEN) ? "en" : "dis");
658 
659 	return 0;
660 
661  err_unregister:
662 	watchdog_unregister_device(&wdt->wdt_device);
663 
664  err_cpufreq:
665 	s3c2410wdt_cpufreq_deregister(wdt);
666 
667  err_clk:
668 	clk_disable_unprepare(wdt->clock);
669 
670  err:
671 	return ret;
672 }
673 
674 static int s3c2410wdt_remove(struct platform_device *dev)
675 {
676 	int ret;
677 	struct s3c2410_wdt *wdt = platform_get_drvdata(dev);
678 
679 	ret = s3c2410wdt_mask_and_disable_reset(wdt, true);
680 	if (ret < 0)
681 		return ret;
682 
683 	watchdog_unregister_device(&wdt->wdt_device);
684 
685 	s3c2410wdt_cpufreq_deregister(wdt);
686 
687 	clk_disable_unprepare(wdt->clock);
688 
689 	return 0;
690 }
691 
692 static void s3c2410wdt_shutdown(struct platform_device *dev)
693 {
694 	struct s3c2410_wdt *wdt = platform_get_drvdata(dev);
695 
696 	s3c2410wdt_mask_and_disable_reset(wdt, true);
697 
698 	s3c2410wdt_stop(&wdt->wdt_device);
699 }
700 
701 #ifdef CONFIG_PM_SLEEP
702 
703 static int s3c2410wdt_suspend(struct device *dev)
704 {
705 	int ret;
706 	struct s3c2410_wdt *wdt = dev_get_drvdata(dev);
707 
708 	/* Save watchdog state, and turn it off. */
709 	wdt->wtcon_save = readl(wdt->reg_base + S3C2410_WTCON);
710 	wdt->wtdat_save = readl(wdt->reg_base + S3C2410_WTDAT);
711 
712 	ret = s3c2410wdt_mask_and_disable_reset(wdt, true);
713 	if (ret < 0)
714 		return ret;
715 
716 	/* Note that WTCNT doesn't need to be saved. */
717 	s3c2410wdt_stop(&wdt->wdt_device);
718 
719 	return 0;
720 }
721 
722 static int s3c2410wdt_resume(struct device *dev)
723 {
724 	int ret;
725 	struct s3c2410_wdt *wdt = dev_get_drvdata(dev);
726 
727 	/* Restore watchdog state. */
728 	writel(wdt->wtdat_save, wdt->reg_base + S3C2410_WTDAT);
729 	writel(wdt->wtdat_save, wdt->reg_base + S3C2410_WTCNT);/* Reset count */
730 	writel(wdt->wtcon_save, wdt->reg_base + S3C2410_WTCON);
731 
732 	ret = s3c2410wdt_mask_and_disable_reset(wdt, false);
733 	if (ret < 0)
734 		return ret;
735 
736 	dev_info(dev, "watchdog %sabled\n",
737 		(wdt->wtcon_save & S3C2410_WTCON_ENABLE) ? "en" : "dis");
738 
739 	return 0;
740 }
741 #endif
742 
743 static SIMPLE_DEV_PM_OPS(s3c2410wdt_pm_ops, s3c2410wdt_suspend,
744 			s3c2410wdt_resume);
745 
746 static struct platform_driver s3c2410wdt_driver = {
747 	.probe		= s3c2410wdt_probe,
748 	.remove		= s3c2410wdt_remove,
749 	.shutdown	= s3c2410wdt_shutdown,
750 	.id_table	= s3c2410_wdt_ids,
751 	.driver		= {
752 		.name	= "s3c2410-wdt",
753 		.pm	= &s3c2410wdt_pm_ops,
754 		.of_match_table	= of_match_ptr(s3c2410_wdt_match),
755 	},
756 };
757 
758 module_platform_driver(s3c2410wdt_driver);
759 
760 MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>, "
761 	      "Dimitry Andric <dimitry.andric@tomtom.com>");
762 MODULE_DESCRIPTION("S3C2410 Watchdog Device Driver");
763 MODULE_LICENSE("GPL");
764