xref: /openbmc/linux/drivers/watchdog/s3c2410_wdt.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
208497f22SKrzysztof Kozlowski /*
3b7e04f8cSWim Van Sebroeck  * Copyright (c) 2004 Simtec Electronics
4b7e04f8cSWim Van Sebroeck  *	Ben Dooks <ben@simtec.co.uk>
5b7e04f8cSWim Van Sebroeck  *
6b7e04f8cSWim Van Sebroeck  * S3C2410 Watchdog Timer Support
7b7e04f8cSWim Van Sebroeck  *
8b7e04f8cSWim Van Sebroeck  * Based on, softdog.c by Alan Cox,
929fa0586SAlan Cox  *     (c) Copyright 1996 Alan Cox <alan@lxorguk.ukuu.org.uk>
10b7e04f8cSWim Van Sebroeck  */
11b7e04f8cSWim Van Sebroeck 
12b7e04f8cSWim Van Sebroeck #include <linux/module.h>
13b7e04f8cSWim Van Sebroeck #include <linux/moduleparam.h>
14b7e04f8cSWim Van Sebroeck #include <linux/types.h>
15b7e04f8cSWim Van Sebroeck #include <linux/timer.h>
16b7e04f8cSWim Van Sebroeck #include <linux/watchdog.h>
17b7e04f8cSWim Van Sebroeck #include <linux/platform_device.h>
18b7e04f8cSWim Van Sebroeck #include <linux/interrupt.h>
19b7e04f8cSWim Van Sebroeck #include <linux/clk.h>
2041dc8b72SAlan Cox #include <linux/uaccess.h>
2141dc8b72SAlan Cox #include <linux/io.h>
22e02f838eSBen Dooks #include <linux/cpufreq.h>
235a0e3ad6STejun Heo #include <linux/slab.h>
2425dc46e3SWolfram Sang #include <linux/err.h>
253016a552SWim Van Sebroeck #include <linux/of.h>
264f1f653aSLeela Krishna Amudala #include <linux/mfd/syscon.h>
274f1f653aSLeela Krishna Amudala #include <linux/regmap.h>
28f286e133SHeiko Stuebner #include <linux/delay.h>
29b7e04f8cSWim Van Sebroeck 
30a8f5401aSTomasz Figa #define S3C2410_WTCON		0x00
31a8f5401aSTomasz Figa #define S3C2410_WTDAT		0x04
32a8f5401aSTomasz Figa #define S3C2410_WTCNT		0x08
330b445549SKrzysztof Kozlowski #define S3C2410_WTCLRINT	0x0c
34b7e04f8cSWim Van Sebroeck 
35882dec1fSJavier Martinez Canillas #define S3C2410_WTCNT_MAXCNT	0xffff
36882dec1fSJavier Martinez Canillas 
37a8f5401aSTomasz Figa #define S3C2410_WTCON_RSTEN	(1 << 0)
38a8f5401aSTomasz Figa #define S3C2410_WTCON_INTEN	(1 << 2)
39a8f5401aSTomasz Figa #define S3C2410_WTCON_ENABLE	(1 << 5)
40b7e04f8cSWim Van Sebroeck 
41a8f5401aSTomasz Figa #define S3C2410_WTCON_DIV16	(0 << 3)
42a8f5401aSTomasz Figa #define S3C2410_WTCON_DIV32	(1 << 3)
43a8f5401aSTomasz Figa #define S3C2410_WTCON_DIV64	(2 << 3)
44a8f5401aSTomasz Figa #define S3C2410_WTCON_DIV128	(3 << 3)
45a8f5401aSTomasz Figa 
46882dec1fSJavier Martinez Canillas #define S3C2410_WTCON_MAXDIV	0x80
47882dec1fSJavier Martinez Canillas 
48a8f5401aSTomasz Figa #define S3C2410_WTCON_PRESCALE(x)	((x) << 8)
49a8f5401aSTomasz Figa #define S3C2410_WTCON_PRESCALE_MASK	(0xff << 8)
50882dec1fSJavier Martinez Canillas #define S3C2410_WTCON_PRESCALE_MAX	0xff
51b7e04f8cSWim Van Sebroeck 
524f21195dSKrzysztof Kozlowski #define S3C2410_WATCHDOG_ATBOOT		(0)
534f21195dSKrzysztof Kozlowski #define S3C2410_WATCHDOG_DEFAULT_TIME	(15)
54b7e04f8cSWim Van Sebroeck 
55cffc9a60SDoug Anderson #define EXYNOS5_RST_STAT_REG_OFFSET		0x0404
564f1f653aSLeela Krishna Amudala #define EXYNOS5_WDT_DISABLE_REG_OFFSET		0x0408
574f1f653aSLeela Krishna Amudala #define EXYNOS5_WDT_MASK_RESET_REG_OFFSET	0x040c
58cd4eadf2SSam Protsenko #define EXYNOS850_CLUSTER0_NONCPU_OUT		0x1220
59cd4eadf2SSam Protsenko #define EXYNOS850_CLUSTER0_NONCPU_INT_EN	0x1244
60cd4eadf2SSam Protsenko #define EXYNOS850_CLUSTER1_NONCPU_OUT		0x1620
61cd4eadf2SSam Protsenko #define EXYNOS850_CLUSTER1_NONCPU_INT_EN	0x1644
620c91aa18SChanho Park #define EXYNOSAUTOV9_CLUSTER1_NONCPU_OUT	0x1520
630c91aa18SChanho Park #define EXYNOSAUTOV9_CLUSTER1_NONCPU_INT_EN	0x1544
64cd4eadf2SSam Protsenko 
65cd4eadf2SSam Protsenko #define EXYNOS850_CLUSTER0_WDTRESET_BIT		24
66cd4eadf2SSam Protsenko #define EXYNOS850_CLUSTER1_WDTRESET_BIT		23
670c91aa18SChanho Park #define EXYNOSAUTOV9_CLUSTER0_WDTRESET_BIT	25
680c91aa18SChanho Park #define EXYNOSAUTOV9_CLUSTER1_WDTRESET_BIT	24
69cf3fad4eSSam Protsenko 
70cf3fad4eSSam Protsenko /**
71cf3fad4eSSam Protsenko  * DOC: Quirk flags for different Samsung watchdog IP-cores
72cf3fad4eSSam Protsenko  *
73cf3fad4eSSam Protsenko  * This driver supports multiple Samsung SoCs, each of which might have
74cf3fad4eSSam Protsenko  * different set of registers and features supported. As watchdog block
75cf3fad4eSSam Protsenko  * sometimes requires modifying PMU registers for proper functioning, register
76cf3fad4eSSam Protsenko  * differences in both watchdog and PMU IP-cores should be accounted for. Quirk
77cf3fad4eSSam Protsenko  * flags described below serve the purpose of telling the driver about mentioned
78cf3fad4eSSam Protsenko  * SoC traits, and can be specified in driver data for each particular supported
79cf3fad4eSSam Protsenko  * device.
80cf3fad4eSSam Protsenko  *
81cf3fad4eSSam Protsenko  * %QUIRK_HAS_WTCLRINT_REG: Watchdog block has WTCLRINT register. It's used to
82cf3fad4eSSam Protsenko  * clear the interrupt once the interrupt service routine is complete. It's
83cf3fad4eSSam Protsenko  * write-only, writing any values to this register clears the interrupt, but
84cf3fad4eSSam Protsenko  * reading is not permitted.
85cf3fad4eSSam Protsenko  *
86cf3fad4eSSam Protsenko  * %QUIRK_HAS_PMU_MASK_RESET: PMU block has the register for disabling/enabling
87cf3fad4eSSam Protsenko  * WDT reset request. On old SoCs it's usually called MASK_WDT_RESET_REQUEST,
88cf3fad4eSSam Protsenko  * new SoCs have CLUSTERx_NONCPU_INT_EN register, which 'mask_bit' value is
89cf3fad4eSSam Protsenko  * inverted compared to the former one.
90cf3fad4eSSam Protsenko  *
91cf3fad4eSSam Protsenko  * %QUIRK_HAS_PMU_RST_STAT: PMU block has RST_STAT (reset status) register,
92cf3fad4eSSam Protsenko  * which contains bits indicating the reason for most recent CPU reset. If
93cf3fad4eSSam Protsenko  * present, driver will use this register to check if previous reboot was due to
94cf3fad4eSSam Protsenko  * watchdog timer reset.
95cf3fad4eSSam Protsenko  *
96cf3fad4eSSam Protsenko  * %QUIRK_HAS_PMU_AUTO_DISABLE: PMU block has AUTOMATIC_WDT_RESET_DISABLE
97cf3fad4eSSam Protsenko  * register. If 'mask_bit' bit is set, PMU will disable WDT reset when
98cf3fad4eSSam Protsenko  * corresponding processor is in reset state.
99cf3fad4eSSam Protsenko  *
100cf3fad4eSSam Protsenko  * %QUIRK_HAS_PMU_CNT_EN: PMU block has some register (e.g. CLUSTERx_NONCPU_OUT)
101cf3fad4eSSam Protsenko  * with "watchdog counter enable" bit. That bit should be set to make watchdog
102cf3fad4eSSam Protsenko  * counter running.
103cf3fad4eSSam Protsenko  */
104cf3fad4eSSam Protsenko #define QUIRK_HAS_WTCLRINT_REG			(1 << 0)
105cf3fad4eSSam Protsenko #define QUIRK_HAS_PMU_MASK_RESET		(1 << 1)
106cf3fad4eSSam Protsenko #define QUIRK_HAS_PMU_RST_STAT			(1 << 2)
1078d9fdf60SSam Protsenko #define QUIRK_HAS_PMU_AUTO_DISABLE		(1 << 3)
108aa220bc6SSam Protsenko #define QUIRK_HAS_PMU_CNT_EN			(1 << 4)
109cffc9a60SDoug Anderson 
110cffc9a60SDoug Anderson /* These quirks require that we have a PMU register map */
111cf3fad4eSSam Protsenko #define QUIRKS_HAVE_PMUREG \
112cf3fad4eSSam Protsenko 	(QUIRK_HAS_PMU_MASK_RESET | QUIRK_HAS_PMU_RST_STAT | \
113cf3fad4eSSam Protsenko 	 QUIRK_HAS_PMU_AUTO_DISABLE | QUIRK_HAS_PMU_CNT_EN)
1144f1f653aSLeela Krishna Amudala 
11586a1e189SWim Van Sebroeck static bool nowayout	= WATCHDOG_NOWAYOUT;
116c1fd5f64SFabio Porcedda static int tmr_margin;
1174f21195dSKrzysztof Kozlowski static int tmr_atboot	= S3C2410_WATCHDOG_ATBOOT;
11841dc8b72SAlan Cox static int soft_noboot;
119b7e04f8cSWim Van Sebroeck 
120b7e04f8cSWim Van Sebroeck module_param(tmr_margin,  int, 0);
121b7e04f8cSWim Van Sebroeck module_param(tmr_atboot,  int, 0);
12286a1e189SWim Van Sebroeck module_param(nowayout,   bool, 0);
123b7e04f8cSWim Van Sebroeck module_param(soft_noboot, int, 0);
124b7e04f8cSWim Van Sebroeck 
12576550d32SRandy Dunlap MODULE_PARM_DESC(tmr_margin, "Watchdog tmr_margin in seconds. (default="
1264f21195dSKrzysztof Kozlowski 		__MODULE_STRING(S3C2410_WATCHDOG_DEFAULT_TIME) ")");
12741dc8b72SAlan Cox MODULE_PARM_DESC(tmr_atboot,
12841dc8b72SAlan Cox 		"Watchdog is started at boot time if set to 1, default="
1294f21195dSKrzysztof Kozlowski 			__MODULE_STRING(S3C2410_WATCHDOG_ATBOOT));
13041dc8b72SAlan Cox MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
13141dc8b72SAlan Cox 			__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
13208497f22SKrzysztof Kozlowski MODULE_PARM_DESC(soft_noboot, "Watchdog action, set to 1 to ignore reboots, 0 to reboot (default 0)");
133b7e04f8cSWim Van Sebroeck 
1344f1f653aSLeela Krishna Amudala /**
1354f1f653aSLeela Krishna Amudala  * struct s3c2410_wdt_variant - Per-variant config data
1364f1f653aSLeela Krishna Amudala  *
1374f1f653aSLeela Krishna Amudala  * @disable_reg: Offset in pmureg for the register that disables the watchdog
1384f1f653aSLeela Krishna Amudala  * timer reset functionality.
1394f1f653aSLeela Krishna Amudala  * @mask_reset_reg: Offset in pmureg for the register that masks the watchdog
1404f1f653aSLeela Krishna Amudala  * timer reset functionality.
141370bc7f5SSam Protsenko  * @mask_reset_inv: If set, mask_reset_reg value will have inverted meaning.
1424f1f653aSLeela Krishna Amudala  * @mask_bit: Bit number for the watchdog timer in the disable register and the
1434f1f653aSLeela Krishna Amudala  * mask reset register.
144cffc9a60SDoug Anderson  * @rst_stat_reg: Offset in pmureg for the register that has the reset status.
145cffc9a60SDoug Anderson  * @rst_stat_bit: Bit number in the rst_stat register indicating a watchdog
146cffc9a60SDoug Anderson  * reset.
147aa220bc6SSam Protsenko  * @cnt_en_reg: Offset in pmureg for the register that enables WDT counter.
148aa220bc6SSam Protsenko  * @cnt_en_bit: Bit number for "watchdog counter enable" in cnt_en register.
1494f1f653aSLeela Krishna Amudala  * @quirks: A bitfield of quirks.
1504f1f653aSLeela Krishna Amudala  */
1514f1f653aSLeela Krishna Amudala 
1524f1f653aSLeela Krishna Amudala struct s3c2410_wdt_variant {
1534f1f653aSLeela Krishna Amudala 	int disable_reg;
1544f1f653aSLeela Krishna Amudala 	int mask_reset_reg;
155370bc7f5SSam Protsenko 	bool mask_reset_inv;
1564f1f653aSLeela Krishna Amudala 	int mask_bit;
157cffc9a60SDoug Anderson 	int rst_stat_reg;
158cffc9a60SDoug Anderson 	int rst_stat_bit;
159aa220bc6SSam Protsenko 	int cnt_en_reg;
160aa220bc6SSam Protsenko 	int cnt_en_bit;
1614f1f653aSLeela Krishna Amudala 	u32 quirks;
1624f1f653aSLeela Krishna Amudala };
1634f1f653aSLeela Krishna Amudala 
164af4ea631SLeela Krishna Amudala struct s3c2410_wdt {
165af4ea631SLeela Krishna Amudala 	struct device		*dev;
166e249d01bSSam Protsenko 	struct clk		*bus_clk; /* for register interface (PCLK) */
167e249d01bSSam Protsenko 	struct clk		*src_clk; /* for WDT counter */
168af4ea631SLeela Krishna Amudala 	void __iomem		*reg_base;
169af4ea631SLeela Krishna Amudala 	unsigned int		count;
170af4ea631SLeela Krishna Amudala 	spinlock_t		lock;
171af4ea631SLeela Krishna Amudala 	unsigned long		wtcon_save;
172af4ea631SLeela Krishna Amudala 	unsigned long		wtdat_save;
173af4ea631SLeela Krishna Amudala 	struct watchdog_device	wdt_device;
174af4ea631SLeela Krishna Amudala 	struct notifier_block	freq_transition;
17558415efeSKrzysztof Kozlowski 	const struct s3c2410_wdt_variant *drv_data;
1764f1f653aSLeela Krishna Amudala 	struct regmap *pmureg;
177af4ea631SLeela Krishna Amudala };
178b7e04f8cSWim Van Sebroeck 
1794f1f653aSLeela Krishna Amudala static const struct s3c2410_wdt_variant drv_data_s3c2410 = {
1804f1f653aSLeela Krishna Amudala 	.quirks = 0
1814f1f653aSLeela Krishna Amudala };
1824f1f653aSLeela Krishna Amudala 
1834f1f653aSLeela Krishna Amudala #ifdef CONFIG_OF
1840b445549SKrzysztof Kozlowski static const struct s3c2410_wdt_variant drv_data_s3c6410 = {
1850b445549SKrzysztof Kozlowski 	.quirks = QUIRK_HAS_WTCLRINT_REG,
1860b445549SKrzysztof Kozlowski };
1870b445549SKrzysztof Kozlowski 
1884f1f653aSLeela Krishna Amudala static const struct s3c2410_wdt_variant drv_data_exynos5250  = {
1894f1f653aSLeela Krishna Amudala 	.disable_reg = EXYNOS5_WDT_DISABLE_REG_OFFSET,
1904f1f653aSLeela Krishna Amudala 	.mask_reset_reg = EXYNOS5_WDT_MASK_RESET_REG_OFFSET,
1914f1f653aSLeela Krishna Amudala 	.mask_bit = 20,
192cffc9a60SDoug Anderson 	.rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET,
193cffc9a60SDoug Anderson 	.rst_stat_bit = 20,
194cf3fad4eSSam Protsenko 	.quirks = QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_MASK_RESET | \
195cf3fad4eSSam Protsenko 		  QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_AUTO_DISABLE,
1964f1f653aSLeela Krishna Amudala };
1974f1f653aSLeela Krishna Amudala 
1984f1f653aSLeela Krishna Amudala static const struct s3c2410_wdt_variant drv_data_exynos5420 = {
1994f1f653aSLeela Krishna Amudala 	.disable_reg = EXYNOS5_WDT_DISABLE_REG_OFFSET,
2004f1f653aSLeela Krishna Amudala 	.mask_reset_reg = EXYNOS5_WDT_MASK_RESET_REG_OFFSET,
2014f1f653aSLeela Krishna Amudala 	.mask_bit = 0,
202cffc9a60SDoug Anderson 	.rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET,
203cffc9a60SDoug Anderson 	.rst_stat_bit = 9,
204cf3fad4eSSam Protsenko 	.quirks = QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_MASK_RESET | \
205cf3fad4eSSam Protsenko 		  QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_AUTO_DISABLE,
2064f1f653aSLeela Krishna Amudala };
2074f1f653aSLeela Krishna Amudala 
2082b9366b6SNaveen Krishna Chatradhi static const struct s3c2410_wdt_variant drv_data_exynos7 = {
2092b9366b6SNaveen Krishna Chatradhi 	.disable_reg = EXYNOS5_WDT_DISABLE_REG_OFFSET,
2102b9366b6SNaveen Krishna Chatradhi 	.mask_reset_reg = EXYNOS5_WDT_MASK_RESET_REG_OFFSET,
2115476b2b7SAbhilash Kesavan 	.mask_bit = 23,
2122b9366b6SNaveen Krishna Chatradhi 	.rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET,
2132b9366b6SNaveen Krishna Chatradhi 	.rst_stat_bit = 23,	/* A57 WDTRESET */
214cf3fad4eSSam Protsenko 	.quirks = QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_MASK_RESET | \
215cf3fad4eSSam Protsenko 		  QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_AUTO_DISABLE,
2162b9366b6SNaveen Krishna Chatradhi };
2172b9366b6SNaveen Krishna Chatradhi 
218cd4eadf2SSam Protsenko static const struct s3c2410_wdt_variant drv_data_exynos850_cl0 = {
219cd4eadf2SSam Protsenko 	.mask_reset_reg = EXYNOS850_CLUSTER0_NONCPU_INT_EN,
220cd4eadf2SSam Protsenko 	.mask_bit = 2,
221cd4eadf2SSam Protsenko 	.mask_reset_inv = true,
222cd4eadf2SSam Protsenko 	.rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET,
223cd4eadf2SSam Protsenko 	.rst_stat_bit = EXYNOS850_CLUSTER0_WDTRESET_BIT,
224cd4eadf2SSam Protsenko 	.cnt_en_reg = EXYNOS850_CLUSTER0_NONCPU_OUT,
225cd4eadf2SSam Protsenko 	.cnt_en_bit = 7,
226cd4eadf2SSam Protsenko 	.quirks = QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_MASK_RESET | \
227cd4eadf2SSam Protsenko 		  QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_CNT_EN,
228cd4eadf2SSam Protsenko };
229cd4eadf2SSam Protsenko 
230cd4eadf2SSam Protsenko static const struct s3c2410_wdt_variant drv_data_exynos850_cl1 = {
231cd4eadf2SSam Protsenko 	.mask_reset_reg = EXYNOS850_CLUSTER1_NONCPU_INT_EN,
232cd4eadf2SSam Protsenko 	.mask_bit = 2,
233cd4eadf2SSam Protsenko 	.mask_reset_inv = true,
234cd4eadf2SSam Protsenko 	.rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET,
235cd4eadf2SSam Protsenko 	.rst_stat_bit = EXYNOS850_CLUSTER1_WDTRESET_BIT,
236cd4eadf2SSam Protsenko 	.cnt_en_reg = EXYNOS850_CLUSTER1_NONCPU_OUT,
237cd4eadf2SSam Protsenko 	.cnt_en_bit = 7,
238cd4eadf2SSam Protsenko 	.quirks = QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_MASK_RESET | \
239cd4eadf2SSam Protsenko 		  QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_CNT_EN,
240cd4eadf2SSam Protsenko };
241cd4eadf2SSam Protsenko 
2420c91aa18SChanho Park static const struct s3c2410_wdt_variant drv_data_exynosautov9_cl0 = {
2430c91aa18SChanho Park 	.mask_reset_reg = EXYNOS850_CLUSTER0_NONCPU_INT_EN,
2440c91aa18SChanho Park 	.mask_bit = 2,
2450c91aa18SChanho Park 	.mask_reset_inv = true,
2460c91aa18SChanho Park 	.rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET,
2470c91aa18SChanho Park 	.rst_stat_bit = EXYNOSAUTOV9_CLUSTER0_WDTRESET_BIT,
2480c91aa18SChanho Park 	.cnt_en_reg = EXYNOS850_CLUSTER0_NONCPU_OUT,
2490c91aa18SChanho Park 	.cnt_en_bit = 7,
2500c91aa18SChanho Park 	.quirks = QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_MASK_RESET |
2510c91aa18SChanho Park 		  QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_CNT_EN,
2520c91aa18SChanho Park };
2530c91aa18SChanho Park 
2540c91aa18SChanho Park static const struct s3c2410_wdt_variant drv_data_exynosautov9_cl1 = {
2550c91aa18SChanho Park 	.mask_reset_reg = EXYNOSAUTOV9_CLUSTER1_NONCPU_INT_EN,
2560c91aa18SChanho Park 	.mask_bit = 2,
2570c91aa18SChanho Park 	.mask_reset_inv = true,
2580c91aa18SChanho Park 	.rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET,
2590c91aa18SChanho Park 	.rst_stat_bit = EXYNOSAUTOV9_CLUSTER1_WDTRESET_BIT,
2600c91aa18SChanho Park 	.cnt_en_reg = EXYNOSAUTOV9_CLUSTER1_NONCPU_OUT,
2610c91aa18SChanho Park 	.cnt_en_bit = 7,
2620c91aa18SChanho Park 	.quirks = QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_MASK_RESET |
2630c91aa18SChanho Park 		  QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_CNT_EN,
2640c91aa18SChanho Park };
2650c91aa18SChanho Park 
2664f1f653aSLeela Krishna Amudala static const struct of_device_id s3c2410_wdt_match[] = {
2674f1f653aSLeela Krishna Amudala 	{ .compatible = "samsung,s3c2410-wdt",
2684f1f653aSLeela Krishna Amudala 	  .data = &drv_data_s3c2410 },
2690b445549SKrzysztof Kozlowski 	{ .compatible = "samsung,s3c6410-wdt",
2700b445549SKrzysztof Kozlowski 	  .data = &drv_data_s3c6410 },
2714f1f653aSLeela Krishna Amudala 	{ .compatible = "samsung,exynos5250-wdt",
2724f1f653aSLeela Krishna Amudala 	  .data = &drv_data_exynos5250 },
2734f1f653aSLeela Krishna Amudala 	{ .compatible = "samsung,exynos5420-wdt",
2744f1f653aSLeela Krishna Amudala 	  .data = &drv_data_exynos5420 },
2752b9366b6SNaveen Krishna Chatradhi 	{ .compatible = "samsung,exynos7-wdt",
2762b9366b6SNaveen Krishna Chatradhi 	  .data = &drv_data_exynos7 },
277cd4eadf2SSam Protsenko 	{ .compatible = "samsung,exynos850-wdt",
278cd4eadf2SSam Protsenko 	  .data = &drv_data_exynos850_cl0 },
2790c91aa18SChanho Park 	{ .compatible = "samsung,exynosautov9-wdt",
2800c91aa18SChanho Park 	  .data = &drv_data_exynosautov9_cl0 },
2814f1f653aSLeela Krishna Amudala 	{},
2824f1f653aSLeela Krishna Amudala };
2834f1f653aSLeela Krishna Amudala MODULE_DEVICE_TABLE(of, s3c2410_wdt_match);
2844f1f653aSLeela Krishna Amudala #endif
2854f1f653aSLeela Krishna Amudala 
2864f1f653aSLeela Krishna Amudala static const struct platform_device_id s3c2410_wdt_ids[] = {
2874f1f653aSLeela Krishna Amudala 	{
2884f1f653aSLeela Krishna Amudala 		.name = "s3c2410-wdt",
2894f1f653aSLeela Krishna Amudala 		.driver_data = (unsigned long)&drv_data_s3c2410,
2904f1f653aSLeela Krishna Amudala 	},
2914f1f653aSLeela Krishna Amudala 	{}
2924f1f653aSLeela Krishna Amudala };
2934f1f653aSLeela Krishna Amudala MODULE_DEVICE_TABLE(platform, s3c2410_wdt_ids);
2944f1f653aSLeela Krishna Amudala 
295b7e04f8cSWim Van Sebroeck /* functions */
296b7e04f8cSWim Van Sebroeck 
s3c2410wdt_get_freq(struct s3c2410_wdt * wdt)297e249d01bSSam Protsenko static inline unsigned long s3c2410wdt_get_freq(struct s3c2410_wdt *wdt)
298882dec1fSJavier Martinez Canillas {
299e249d01bSSam Protsenko 	return clk_get_rate(wdt->src_clk ? wdt->src_clk : wdt->bus_clk);
300e249d01bSSam Protsenko }
301e249d01bSSam Protsenko 
s3c2410wdt_max_timeout(struct s3c2410_wdt * wdt)302e249d01bSSam Protsenko static inline unsigned int s3c2410wdt_max_timeout(struct s3c2410_wdt *wdt)
303e249d01bSSam Protsenko {
304e249d01bSSam Protsenko 	const unsigned long freq = s3c2410wdt_get_freq(wdt);
305882dec1fSJavier Martinez Canillas 
306882dec1fSJavier Martinez Canillas 	return S3C2410_WTCNT_MAXCNT / (freq / (S3C2410_WTCON_PRESCALE_MAX + 1)
307882dec1fSJavier Martinez Canillas 				       / S3C2410_WTCON_MAXDIV);
308882dec1fSJavier Martinez Canillas }
309882dec1fSJavier Martinez Canillas 
s3c2410wdt_disable_wdt_reset(struct s3c2410_wdt * wdt,bool mask)3102bd33bb4SSam Protsenko static int s3c2410wdt_disable_wdt_reset(struct s3c2410_wdt *wdt, bool mask)
3114f1f653aSLeela Krishna Amudala {
3122bd33bb4SSam Protsenko 	const u32 mask_val = BIT(wdt->drv_data->mask_bit);
3132bd33bb4SSam Protsenko 	const u32 val = mask ? mask_val : 0;
3144f1f653aSLeela Krishna Amudala 	int ret;
3154f1f653aSLeela Krishna Amudala 
3162bd33bb4SSam Protsenko 	ret = regmap_update_bits(wdt->pmureg, wdt->drv_data->disable_reg,
3174f1f653aSLeela Krishna Amudala 				 mask_val, val);
3184f1f653aSLeela Krishna Amudala 	if (ret < 0)
3194f1f653aSLeela Krishna Amudala 		dev_err(wdt->dev, "failed to update reg(%d)\n", ret);
3204f1f653aSLeela Krishna Amudala 
3214f1f653aSLeela Krishna Amudala 	return ret;
3224f1f653aSLeela Krishna Amudala }
3234f1f653aSLeela Krishna Amudala 
s3c2410wdt_mask_wdt_reset(struct s3c2410_wdt * wdt,bool mask)3242bd33bb4SSam Protsenko static int s3c2410wdt_mask_wdt_reset(struct s3c2410_wdt *wdt, bool mask)
3252bd33bb4SSam Protsenko {
3262bd33bb4SSam Protsenko 	const u32 mask_val = BIT(wdt->drv_data->mask_bit);
327370bc7f5SSam Protsenko 	const bool val_inv = wdt->drv_data->mask_reset_inv;
328370bc7f5SSam Protsenko 	const u32 val = (mask ^ val_inv) ? mask_val : 0;
3292bd33bb4SSam Protsenko 	int ret;
3302bd33bb4SSam Protsenko 
3312bd33bb4SSam Protsenko 	ret = regmap_update_bits(wdt->pmureg, wdt->drv_data->mask_reset_reg,
3322bd33bb4SSam Protsenko 				 mask_val, val);
3332bd33bb4SSam Protsenko 	if (ret < 0)
3342bd33bb4SSam Protsenko 		dev_err(wdt->dev, "failed to update reg(%d)\n", ret);
3352bd33bb4SSam Protsenko 
3362bd33bb4SSam Protsenko 	return ret;
3372bd33bb4SSam Protsenko }
3382bd33bb4SSam Protsenko 
s3c2410wdt_enable_counter(struct s3c2410_wdt * wdt,bool en)339aa220bc6SSam Protsenko static int s3c2410wdt_enable_counter(struct s3c2410_wdt *wdt, bool en)
340aa220bc6SSam Protsenko {
341aa220bc6SSam Protsenko 	const u32 mask_val = BIT(wdt->drv_data->cnt_en_bit);
342aa220bc6SSam Protsenko 	const u32 val = en ? mask_val : 0;
343aa220bc6SSam Protsenko 	int ret;
344aa220bc6SSam Protsenko 
345aa220bc6SSam Protsenko 	ret = regmap_update_bits(wdt->pmureg, wdt->drv_data->cnt_en_reg,
346aa220bc6SSam Protsenko 				 mask_val, val);
347aa220bc6SSam Protsenko 	if (ret < 0)
348aa220bc6SSam Protsenko 		dev_err(wdt->dev, "failed to update reg(%d)\n", ret);
349aa220bc6SSam Protsenko 
350aa220bc6SSam Protsenko 	return ret;
351aa220bc6SSam Protsenko }
352aa220bc6SSam Protsenko 
s3c2410wdt_enable(struct s3c2410_wdt * wdt,bool en)353cf3fad4eSSam Protsenko static int s3c2410wdt_enable(struct s3c2410_wdt *wdt, bool en)
3542bd33bb4SSam Protsenko {
3552bd33bb4SSam Protsenko 	int ret;
3562bd33bb4SSam Protsenko 
3572bd33bb4SSam Protsenko 	if (wdt->drv_data->quirks & QUIRK_HAS_PMU_AUTO_DISABLE) {
358cf3fad4eSSam Protsenko 		ret = s3c2410wdt_disable_wdt_reset(wdt, !en);
3592bd33bb4SSam Protsenko 		if (ret < 0)
3602bd33bb4SSam Protsenko 			return ret;
3612bd33bb4SSam Protsenko 	}
3622bd33bb4SSam Protsenko 
363cf3fad4eSSam Protsenko 	if (wdt->drv_data->quirks & QUIRK_HAS_PMU_MASK_RESET) {
364cf3fad4eSSam Protsenko 		ret = s3c2410wdt_mask_wdt_reset(wdt, !en);
3652bd33bb4SSam Protsenko 		if (ret < 0)
3662bd33bb4SSam Protsenko 			return ret;
3672bd33bb4SSam Protsenko 	}
3682bd33bb4SSam Protsenko 
369aa220bc6SSam Protsenko 	if (wdt->drv_data->quirks & QUIRK_HAS_PMU_CNT_EN) {
370cf3fad4eSSam Protsenko 		ret = s3c2410wdt_enable_counter(wdt, en);
371aa220bc6SSam Protsenko 		if (ret < 0)
372aa220bc6SSam Protsenko 			return ret;
373aa220bc6SSam Protsenko 	}
374aa220bc6SSam Protsenko 
3752bd33bb4SSam Protsenko 	return 0;
3762bd33bb4SSam Protsenko }
3772bd33bb4SSam Protsenko 
s3c2410wdt_keepalive(struct watchdog_device * wdd)37825dc46e3SWolfram Sang static int s3c2410wdt_keepalive(struct watchdog_device *wdd)
379b7e04f8cSWim Van Sebroeck {
380af4ea631SLeela Krishna Amudala 	struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd);
381*aeb3ef51SChengfeng Ye 	unsigned long flags;
382af4ea631SLeela Krishna Amudala 
383*aeb3ef51SChengfeng Ye 	spin_lock_irqsave(&wdt->lock, flags);
384af4ea631SLeela Krishna Amudala 	writel(wdt->count, wdt->reg_base + S3C2410_WTCNT);
385*aeb3ef51SChengfeng Ye 	spin_unlock_irqrestore(&wdt->lock, flags);
38625dc46e3SWolfram Sang 
38725dc46e3SWolfram Sang 	return 0;
388b7e04f8cSWim Van Sebroeck }
389b7e04f8cSWim Van Sebroeck 
__s3c2410wdt_stop(struct s3c2410_wdt * wdt)390af4ea631SLeela Krishna Amudala static void __s3c2410wdt_stop(struct s3c2410_wdt *wdt)
39141dc8b72SAlan Cox {
39241dc8b72SAlan Cox 	unsigned long wtcon;
39341dc8b72SAlan Cox 
394af4ea631SLeela Krishna Amudala 	wtcon = readl(wdt->reg_base + S3C2410_WTCON);
395b7e04f8cSWim Van Sebroeck 	wtcon &= ~(S3C2410_WTCON_ENABLE | S3C2410_WTCON_RSTEN);
396af4ea631SLeela Krishna Amudala 	writel(wtcon, wdt->reg_base + S3C2410_WTCON);
397b7e04f8cSWim Van Sebroeck }
398b7e04f8cSWim Van Sebroeck 
s3c2410wdt_stop(struct watchdog_device * wdd)39925dc46e3SWolfram Sang static int s3c2410wdt_stop(struct watchdog_device *wdd)
40041dc8b72SAlan Cox {
401af4ea631SLeela Krishna Amudala 	struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd);
402*aeb3ef51SChengfeng Ye 	unsigned long flags;
403af4ea631SLeela Krishna Amudala 
404*aeb3ef51SChengfeng Ye 	spin_lock_irqsave(&wdt->lock, flags);
405af4ea631SLeela Krishna Amudala 	__s3c2410wdt_stop(wdt);
406*aeb3ef51SChengfeng Ye 	spin_unlock_irqrestore(&wdt->lock, flags);
40725dc46e3SWolfram Sang 
40825dc46e3SWolfram Sang 	return 0;
40941dc8b72SAlan Cox }
41041dc8b72SAlan Cox 
s3c2410wdt_start(struct watchdog_device * wdd)41125dc46e3SWolfram Sang static int s3c2410wdt_start(struct watchdog_device *wdd)
412b7e04f8cSWim Van Sebroeck {
413b7e04f8cSWim Van Sebroeck 	unsigned long wtcon;
414af4ea631SLeela Krishna Amudala 	struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd);
415*aeb3ef51SChengfeng Ye 	unsigned long flags;
416b7e04f8cSWim Van Sebroeck 
417*aeb3ef51SChengfeng Ye 	spin_lock_irqsave(&wdt->lock, flags);
41841dc8b72SAlan Cox 
419af4ea631SLeela Krishna Amudala 	__s3c2410wdt_stop(wdt);
420b7e04f8cSWim Van Sebroeck 
421af4ea631SLeela Krishna Amudala 	wtcon = readl(wdt->reg_base + S3C2410_WTCON);
422b7e04f8cSWim Van Sebroeck 	wtcon |= S3C2410_WTCON_ENABLE | S3C2410_WTCON_DIV128;
423b7e04f8cSWim Van Sebroeck 
424b7e04f8cSWim Van Sebroeck 	if (soft_noboot) {
425b7e04f8cSWim Van Sebroeck 		wtcon |= S3C2410_WTCON_INTEN;
426b7e04f8cSWim Van Sebroeck 		wtcon &= ~S3C2410_WTCON_RSTEN;
427b7e04f8cSWim Van Sebroeck 	} else {
428b7e04f8cSWim Van Sebroeck 		wtcon &= ~S3C2410_WTCON_INTEN;
429b7e04f8cSWim Van Sebroeck 		wtcon |= S3C2410_WTCON_RSTEN;
430b7e04f8cSWim Van Sebroeck 	}
431b7e04f8cSWim Van Sebroeck 
432456f53d6SKrzysztof Kozlowski 	dev_dbg(wdt->dev, "Starting watchdog: count=0x%08x, wtcon=%08lx\n",
433456f53d6SKrzysztof Kozlowski 		wdt->count, wtcon);
434b7e04f8cSWim Van Sebroeck 
435af4ea631SLeela Krishna Amudala 	writel(wdt->count, wdt->reg_base + S3C2410_WTDAT);
436af4ea631SLeela Krishna Amudala 	writel(wdt->count, wdt->reg_base + S3C2410_WTCNT);
437af4ea631SLeela Krishna Amudala 	writel(wtcon, wdt->reg_base + S3C2410_WTCON);
438*aeb3ef51SChengfeng Ye 	spin_unlock_irqrestore(&wdt->lock, flags);
43925dc46e3SWolfram Sang 
44025dc46e3SWolfram Sang 	return 0;
441b7e04f8cSWim Van Sebroeck }
442b7e04f8cSWim Van Sebroeck 
s3c2410wdt_set_heartbeat(struct watchdog_device * wdd,unsigned int timeout)44308497f22SKrzysztof Kozlowski static int s3c2410wdt_set_heartbeat(struct watchdog_device *wdd,
44408497f22SKrzysztof Kozlowski 				    unsigned int timeout)
445b7e04f8cSWim Van Sebroeck {
446af4ea631SLeela Krishna Amudala 	struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd);
447e249d01bSSam Protsenko 	unsigned long freq = s3c2410wdt_get_freq(wdt);
448b7e04f8cSWim Van Sebroeck 	unsigned int count;
449b7e04f8cSWim Van Sebroeck 	unsigned int divisor = 1;
450b7e04f8cSWim Van Sebroeck 	unsigned long wtcon;
451b7e04f8cSWim Van Sebroeck 
452b7e04f8cSWim Van Sebroeck 	if (timeout < 1)
453b7e04f8cSWim Van Sebroeck 		return -EINVAL;
454b7e04f8cSWim Van Sebroeck 
45517862440SDoug Anderson 	freq = DIV_ROUND_UP(freq, 128);
456b7e04f8cSWim Van Sebroeck 	count = timeout * freq;
457b7e04f8cSWim Van Sebroeck 
458456f53d6SKrzysztof Kozlowski 	dev_dbg(wdt->dev, "Heartbeat: count=%d, timeout=%d, freq=%lu\n",
459456f53d6SKrzysztof Kozlowski 		count, timeout, freq);
460b7e04f8cSWim Van Sebroeck 
461b7e04f8cSWim Van Sebroeck 	/* if the count is bigger than the watchdog register,
462b7e04f8cSWim Van Sebroeck 	   then work out what we need to do (and if) we can
463b7e04f8cSWim Van Sebroeck 	   actually make this value
464b7e04f8cSWim Van Sebroeck 	*/
465b7e04f8cSWim Van Sebroeck 
466b7e04f8cSWim Van Sebroeck 	if (count >= 0x10000) {
46717862440SDoug Anderson 		divisor = DIV_ROUND_UP(count, 0xffff);
468b7e04f8cSWim Van Sebroeck 
46917862440SDoug Anderson 		if (divisor > 0x100) {
470af4ea631SLeela Krishna Amudala 			dev_err(wdt->dev, "timeout %d too big\n", timeout);
471b7e04f8cSWim Van Sebroeck 			return -EINVAL;
472b7e04f8cSWim Van Sebroeck 		}
473b7e04f8cSWim Van Sebroeck 	}
474b7e04f8cSWim Van Sebroeck 
475456f53d6SKrzysztof Kozlowski 	dev_dbg(wdt->dev, "Heartbeat: timeout=%d, divisor=%d, count=%d (%08x)\n",
476456f53d6SKrzysztof Kozlowski 		timeout, divisor, count, DIV_ROUND_UP(count, divisor));
477b7e04f8cSWim Van Sebroeck 
47817862440SDoug Anderson 	count = DIV_ROUND_UP(count, divisor);
479af4ea631SLeela Krishna Amudala 	wdt->count = count;
480b7e04f8cSWim Van Sebroeck 
481b7e04f8cSWim Van Sebroeck 	/* update the pre-scaler */
482af4ea631SLeela Krishna Amudala 	wtcon = readl(wdt->reg_base + S3C2410_WTCON);
483b7e04f8cSWim Van Sebroeck 	wtcon &= ~S3C2410_WTCON_PRESCALE_MASK;
484b7e04f8cSWim Van Sebroeck 	wtcon |= S3C2410_WTCON_PRESCALE(divisor-1);
485b7e04f8cSWim Van Sebroeck 
486af4ea631SLeela Krishna Amudala 	writel(count, wdt->reg_base + S3C2410_WTDAT);
487af4ea631SLeela Krishna Amudala 	writel(wtcon, wdt->reg_base + S3C2410_WTCON);
488b7e04f8cSWim Van Sebroeck 
4895f2430f5SHans de Goede 	wdd->timeout = (count * divisor) / freq;
4900197c1c4SWim Van Sebroeck 
491b7e04f8cSWim Van Sebroeck 	return 0;
492b7e04f8cSWim Van Sebroeck }
493b7e04f8cSWim Van Sebroeck 
s3c2410wdt_restart(struct watchdog_device * wdd,unsigned long action,void * data)4944d8b229dSGuenter Roeck static int s3c2410wdt_restart(struct watchdog_device *wdd, unsigned long action,
4954d8b229dSGuenter Roeck 			      void *data)
496c71f5cd2SDamien Riegel {
497c71f5cd2SDamien Riegel 	struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd);
498c71f5cd2SDamien Riegel 	void __iomem *wdt_base = wdt->reg_base;
499c71f5cd2SDamien Riegel 
500c71f5cd2SDamien Riegel 	/* disable watchdog, to be safe  */
501c71f5cd2SDamien Riegel 	writel(0, wdt_base + S3C2410_WTCON);
502c71f5cd2SDamien Riegel 
503c71f5cd2SDamien Riegel 	/* put initial values into count and data */
504c71f5cd2SDamien Riegel 	writel(0x80, wdt_base + S3C2410_WTCNT);
505c71f5cd2SDamien Riegel 	writel(0x80, wdt_base + S3C2410_WTDAT);
506c71f5cd2SDamien Riegel 
507c71f5cd2SDamien Riegel 	/* set the watchdog to go and reset... */
508c71f5cd2SDamien Riegel 	writel(S3C2410_WTCON_ENABLE | S3C2410_WTCON_DIV16 |
509c71f5cd2SDamien Riegel 		S3C2410_WTCON_RSTEN | S3C2410_WTCON_PRESCALE(0x20),
510c71f5cd2SDamien Riegel 		wdt_base + S3C2410_WTCON);
511c71f5cd2SDamien Riegel 
512c71f5cd2SDamien Riegel 	/* wait for reset to assert... */
513c71f5cd2SDamien Riegel 	mdelay(500);
514c71f5cd2SDamien Riegel 
515c71f5cd2SDamien Riegel 	return 0;
516c71f5cd2SDamien Riegel }
517c71f5cd2SDamien Riegel 
518a77dba7eSWim Van Sebroeck #define OPTIONS (WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE)
519b7e04f8cSWim Van Sebroeck 
52041dc8b72SAlan Cox static const struct watchdog_info s3c2410_wdt_ident = {
521b7e04f8cSWim Van Sebroeck 	.options          =     OPTIONS,
522b7e04f8cSWim Van Sebroeck 	.firmware_version =	0,
523b7e04f8cSWim Van Sebroeck 	.identity         =	"S3C2410 Watchdog",
524b7e04f8cSWim Van Sebroeck };
525b7e04f8cSWim Van Sebroeck 
526b893e344SBhumika Goyal static const struct watchdog_ops s3c2410wdt_ops = {
527b7e04f8cSWim Van Sebroeck 	.owner = THIS_MODULE,
52825dc46e3SWolfram Sang 	.start = s3c2410wdt_start,
52925dc46e3SWolfram Sang 	.stop = s3c2410wdt_stop,
53025dc46e3SWolfram Sang 	.ping = s3c2410wdt_keepalive,
53125dc46e3SWolfram Sang 	.set_timeout = s3c2410wdt_set_heartbeat,
532c71f5cd2SDamien Riegel 	.restart = s3c2410wdt_restart,
533b7e04f8cSWim Van Sebroeck };
534b7e04f8cSWim Van Sebroeck 
53558415efeSKrzysztof Kozlowski static const struct watchdog_device s3c2410_wdd = {
53625dc46e3SWolfram Sang 	.info = &s3c2410_wdt_ident,
53725dc46e3SWolfram Sang 	.ops = &s3c2410wdt_ops,
5384f21195dSKrzysztof Kozlowski 	.timeout = S3C2410_WATCHDOG_DEFAULT_TIME,
539b7e04f8cSWim Van Sebroeck };
540b7e04f8cSWim Van Sebroeck 
541b7e04f8cSWim Van Sebroeck /* interrupt handler code */
542b7e04f8cSWim Van Sebroeck 
s3c2410wdt_irq(int irqno,void * param)543b7e04f8cSWim Van Sebroeck static irqreturn_t s3c2410wdt_irq(int irqno, void *param)
544b7e04f8cSWim Van Sebroeck {
545af4ea631SLeela Krishna Amudala 	struct s3c2410_wdt *wdt = platform_get_drvdata(param);
546b7e04f8cSWim Van Sebroeck 
547af4ea631SLeela Krishna Amudala 	dev_info(wdt->dev, "watchdog timer expired (irq)\n");
548af4ea631SLeela Krishna Amudala 
549af4ea631SLeela Krishna Amudala 	s3c2410wdt_keepalive(&wdt->wdt_device);
5500b445549SKrzysztof Kozlowski 
5510b445549SKrzysztof Kozlowski 	if (wdt->drv_data->quirks & QUIRK_HAS_WTCLRINT_REG)
5520b445549SKrzysztof Kozlowski 		writel(0x1, wdt->reg_base + S3C2410_WTCLRINT);
5530b445549SKrzysztof Kozlowski 
554b7e04f8cSWim Van Sebroeck 	return IRQ_HANDLED;
555b7e04f8cSWim Van Sebroeck }
556e02f838eSBen Dooks 
s3c2410wdt_get_bootstatus(struct s3c2410_wdt * wdt)557cffc9a60SDoug Anderson static inline unsigned int s3c2410wdt_get_bootstatus(struct s3c2410_wdt *wdt)
558cffc9a60SDoug Anderson {
559cffc9a60SDoug Anderson 	unsigned int rst_stat;
560cffc9a60SDoug Anderson 	int ret;
561cffc9a60SDoug Anderson 
562cf3fad4eSSam Protsenko 	if (!(wdt->drv_data->quirks & QUIRK_HAS_PMU_RST_STAT))
563cffc9a60SDoug Anderson 		return 0;
564cffc9a60SDoug Anderson 
565cffc9a60SDoug Anderson 	ret = regmap_read(wdt->pmureg, wdt->drv_data->rst_stat_reg, &rst_stat);
566cffc9a60SDoug Anderson 	if (ret)
567cffc9a60SDoug Anderson 		dev_warn(wdt->dev, "Couldn't get RST_STAT register\n");
568cffc9a60SDoug Anderson 	else if (rst_stat & BIT(wdt->drv_data->rst_stat_bit))
569cffc9a60SDoug Anderson 		return WDIOF_CARDRESET;
570cffc9a60SDoug Anderson 
571cffc9a60SDoug Anderson 	return 0;
572cffc9a60SDoug Anderson }
573cffc9a60SDoug Anderson 
57416d477a1SUwe Kleine-König static inline int
s3c2410_get_wdt_drv_data(struct platform_device * pdev,struct s3c2410_wdt * wdt)57516d477a1SUwe Kleine-König s3c2410_get_wdt_drv_data(struct platform_device *pdev, struct s3c2410_wdt *wdt)
5764f1f653aSLeela Krishna Amudala {
577a9a02c46SKrzysztof Kozlowski 	const struct s3c2410_wdt_variant *variant;
578cd4eadf2SSam Protsenko 	struct device *dev = &pdev->dev;
579a9a02c46SKrzysztof Kozlowski 
580cd4eadf2SSam Protsenko 	variant = of_device_get_match_data(dev);
581a9a02c46SKrzysztof Kozlowski 	if (!variant) {
582a9a02c46SKrzysztof Kozlowski 		/* Device matched by platform_device_id */
583a9a02c46SKrzysztof Kozlowski 		variant = (struct s3c2410_wdt_variant *)
5844f1f653aSLeela Krishna Amudala 			   platform_get_device_id(pdev)->driver_data;
5854f1f653aSLeela Krishna Amudala 	}
586a9a02c46SKrzysztof Kozlowski 
587cd4eadf2SSam Protsenko #ifdef CONFIG_OF
5880c91aa18SChanho Park 	/* Choose Exynos850/ExynosAutov9 driver data w.r.t. cluster index */
5890c91aa18SChanho Park 	if (variant == &drv_data_exynos850_cl0 ||
5900c91aa18SChanho Park 	    variant == &drv_data_exynosautov9_cl0) {
591cd4eadf2SSam Protsenko 		u32 index;
592cd4eadf2SSam Protsenko 		int err;
593cd4eadf2SSam Protsenko 
594cd4eadf2SSam Protsenko 		err = of_property_read_u32(dev->of_node,
595cd4eadf2SSam Protsenko 					   "samsung,cluster-index", &index);
596e0e0ee02SUwe Kleine-König 		if (err)
597e0e0ee02SUwe Kleine-König 			return dev_err_probe(dev, -EINVAL, "failed to get cluster index\n");
598cd4eadf2SSam Protsenko 
599cd4eadf2SSam Protsenko 		switch (index) {
600cd4eadf2SSam Protsenko 		case 0:
60116d477a1SUwe Kleine-König 			break;
602cd4eadf2SSam Protsenko 		case 1:
60316d477a1SUwe Kleine-König 			variant = (variant == &drv_data_exynos850_cl0) ?
6040c91aa18SChanho Park 				&drv_data_exynos850_cl1 :
6050c91aa18SChanho Park 				&drv_data_exynosautov9_cl1;
60616d477a1SUwe Kleine-König 			break;
607cd4eadf2SSam Protsenko 		default:
608e0e0ee02SUwe Kleine-König 			return dev_err_probe(dev, -EINVAL, "wrong cluster index: %u\n", index);
609cd4eadf2SSam Protsenko 		}
610cd4eadf2SSam Protsenko 	}
611cd4eadf2SSam Protsenko #endif
612cd4eadf2SSam Protsenko 
61316d477a1SUwe Kleine-König 	wdt->drv_data = variant;
61416d477a1SUwe Kleine-König 	return 0;
6154f1f653aSLeela Krishna Amudala }
6164f1f653aSLeela Krishna Amudala 
s3c2410wdt_wdt_disable_action(void * data)61789baf252SGuenter Roeck static void s3c2410wdt_wdt_disable_action(void *data)
61889baf252SGuenter Roeck {
61989baf252SGuenter Roeck 	s3c2410wdt_enable(data, false);
62089baf252SGuenter Roeck }
62189baf252SGuenter Roeck 
s3c2410wdt_probe(struct platform_device * pdev)6222d991a16SBill Pemberton static int s3c2410wdt_probe(struct platform_device *pdev)
623b7e04f8cSWim Van Sebroeck {
62408497f22SKrzysztof Kozlowski 	struct device *dev = &pdev->dev;
625af4ea631SLeela Krishna Amudala 	struct s3c2410_wdt *wdt;
626b7e04f8cSWim Van Sebroeck 	unsigned int wtcon;
627a51f5896SLad Prabhakar 	int wdt_irq;
628b7e04f8cSWim Van Sebroeck 	int ret;
629b7e04f8cSWim Van Sebroeck 
630af4ea631SLeela Krishna Amudala 	wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
631af4ea631SLeela Krishna Amudala 	if (!wdt)
632af4ea631SLeela Krishna Amudala 		return -ENOMEM;
633af4ea631SLeela Krishna Amudala 
63408497f22SKrzysztof Kozlowski 	wdt->dev = dev;
635af4ea631SLeela Krishna Amudala 	spin_lock_init(&wdt->lock);
636af4ea631SLeela Krishna Amudala 	wdt->wdt_device = s3c2410_wdd;
637b7e04f8cSWim Van Sebroeck 
63816d477a1SUwe Kleine-König 	ret = s3c2410_get_wdt_drv_data(pdev, wdt);
63916d477a1SUwe Kleine-König 	if (ret)
64016d477a1SUwe Kleine-König 		return ret;
641cd4eadf2SSam Protsenko 
642cffc9a60SDoug Anderson 	if (wdt->drv_data->quirks & QUIRKS_HAVE_PMUREG) {
6434f1f653aSLeela Krishna Amudala 		wdt->pmureg = syscon_regmap_lookup_by_phandle(dev->of_node,
6444f1f653aSLeela Krishna Amudala 						"samsung,syscon-phandle");
645e0e0ee02SUwe Kleine-König 		if (IS_ERR(wdt->pmureg))
646e0e0ee02SUwe Kleine-König 			return dev_err_probe(dev, PTR_ERR(wdt->pmureg),
647e0e0ee02SUwe Kleine-König 					     "syscon regmap lookup failed.\n");
6484f1f653aSLeela Krishna Amudala 	}
6494f1f653aSLeela Krishna Amudala 
650a51f5896SLad Prabhakar 	wdt_irq = platform_get_irq(pdev, 0);
651a51f5896SLad Prabhakar 	if (wdt_irq < 0)
652a51f5896SLad Prabhakar 		return wdt_irq;
65378d3e00bSMyungJoo Ham 
65478d3e00bSMyungJoo Ham 	/* get the memory region for the watchdog timer */
6550f0a6a28SGuenter Roeck 	wdt->reg_base = devm_platform_ioremap_resource(pdev, 0);
6561a47cda0SSam Protsenko 	if (IS_ERR(wdt->reg_base))
6571a47cda0SSam Protsenko 		return PTR_ERR(wdt->reg_base);
658b7e04f8cSWim Van Sebroeck 
6599b31b1eaSGuenter Roeck 	wdt->bus_clk = devm_clk_get_enabled(dev, "watchdog");
6609b31b1eaSGuenter Roeck 	if (IS_ERR(wdt->bus_clk))
6619b31b1eaSGuenter Roeck 		return dev_err_probe(dev, PTR_ERR(wdt->bus_clk), "failed to get bus clock\n");
662b7e04f8cSWim Van Sebroeck 
663e249d01bSSam Protsenko 	/*
664e249d01bSSam Protsenko 	 * "watchdog_src" clock is optional; if it's not present -- just skip it
665e249d01bSSam Protsenko 	 * and use "watchdog" clock as both bus and source clock.
666e249d01bSSam Protsenko 	 */
6679b31b1eaSGuenter Roeck 	wdt->src_clk = devm_clk_get_optional_enabled(dev, "watchdog_src");
6689b31b1eaSGuenter Roeck 	if (IS_ERR(wdt->src_clk))
6699b31b1eaSGuenter Roeck 		return dev_err_probe(dev, PTR_ERR(wdt->src_clk), "failed to get source clock\n");
670e249d01bSSam Protsenko 
671882dec1fSJavier Martinez Canillas 	wdt->wdt_device.min_timeout = 1;
672e249d01bSSam Protsenko 	wdt->wdt_device.max_timeout = s3c2410wdt_max_timeout(wdt);
673882dec1fSJavier Martinez Canillas 
674af4ea631SLeela Krishna Amudala 	watchdog_set_drvdata(&wdt->wdt_device, wdt);
675af4ea631SLeela Krishna Amudala 
676b7e04f8cSWim Van Sebroeck 	/* see if we can actually set the requested timer margin, and if
677b7e04f8cSWim Van Sebroeck 	 * not, try the default value */
678b7e04f8cSWim Van Sebroeck 
67908497f22SKrzysztof Kozlowski 	watchdog_init_timeout(&wdt->wdt_device, tmr_margin, dev);
680af4ea631SLeela Krishna Amudala 	ret = s3c2410wdt_set_heartbeat(&wdt->wdt_device,
681af4ea631SLeela Krishna Amudala 					wdt->wdt_device.timeout);
682af4ea631SLeela Krishna Amudala 	if (ret) {
683f197d475SSam Protsenko 		ret = s3c2410wdt_set_heartbeat(&wdt->wdt_device,
6844f21195dSKrzysztof Kozlowski 					       S3C2410_WATCHDOG_DEFAULT_TIME);
685e0e0ee02SUwe Kleine-König 		if (ret == 0)
686f197d475SSam Protsenko 			dev_warn(dev, "tmr_margin value out of range, default %d used\n",
6874f21195dSKrzysztof Kozlowski 				 S3C2410_WATCHDOG_DEFAULT_TIME);
688e0e0ee02SUwe Kleine-König 		else
689e0e0ee02SUwe Kleine-König 			return dev_err_probe(dev, ret, "failed to use default timeout\n");
690b7e04f8cSWim Van Sebroeck 	}
691b7e04f8cSWim Van Sebroeck 
692a51f5896SLad Prabhakar 	ret = devm_request_irq(dev, wdt_irq, s3c2410wdt_irq, 0,
69304ecc7dcSJingoo Han 			       pdev->name, pdev);
694e0e0ee02SUwe Kleine-König 	if (ret != 0)
695e0e0ee02SUwe Kleine-König 		return dev_err_probe(dev, ret, "failed to install irq (%d)\n", ret);
69678d3e00bSMyungJoo Ham 
697af4ea631SLeela Krishna Amudala 	watchdog_set_nowayout(&wdt->wdt_device, nowayout);
698c71f5cd2SDamien Riegel 	watchdog_set_restart_priority(&wdt->wdt_device, 128);
699ff0b3cd4SWim Van Sebroeck 
700cffc9a60SDoug Anderson 	wdt->wdt_device.bootstatus = s3c2410wdt_get_bootstatus(wdt);
70108497f22SKrzysztof Kozlowski 	wdt->wdt_device.parent = dev;
702cffc9a60SDoug Anderson 
703a90102e3SSam Protsenko 	/*
704a90102e3SSam Protsenko 	 * If "tmr_atboot" param is non-zero, start the watchdog right now. Also
705a90102e3SSam Protsenko 	 * set WDOG_HW_RUNNING bit, so that watchdog core can kick the watchdog.
706a90102e3SSam Protsenko 	 *
707a90102e3SSam Protsenko 	 * If we're not enabling the watchdog, then ensure it is disabled if it
708a90102e3SSam Protsenko 	 * has been left running from the bootloader or other source.
709a90102e3SSam Protsenko 	 */
710a90102e3SSam Protsenko 	if (tmr_atboot) {
711a90102e3SSam Protsenko 		dev_info(dev, "starting watchdog timer\n");
712a90102e3SSam Protsenko 		s3c2410wdt_start(&wdt->wdt_device);
713a90102e3SSam Protsenko 		set_bit(WDOG_HW_RUNNING, &wdt->wdt_device.status);
714a90102e3SSam Protsenko 	} else {
715a90102e3SSam Protsenko 		s3c2410wdt_stop(&wdt->wdt_device);
716a90102e3SSam Protsenko 	}
717a90102e3SSam Protsenko 
71889baf252SGuenter Roeck 	ret = devm_watchdog_register_device(dev, &wdt->wdt_device);
719386f465aSWolfram Sang 	if (ret)
7209b31b1eaSGuenter Roeck 		return ret;
721b7e04f8cSWim Van Sebroeck 
722cf3fad4eSSam Protsenko 	ret = s3c2410wdt_enable(wdt, true);
7234f1f653aSLeela Krishna Amudala 	if (ret < 0)
72489baf252SGuenter Roeck 		return ret;
72589baf252SGuenter Roeck 
72689baf252SGuenter Roeck 	ret = devm_add_action_or_reset(dev, s3c2410wdt_wdt_disable_action, wdt);
72789baf252SGuenter Roeck 	if (ret)
72889baf252SGuenter Roeck 		return ret;
7294f1f653aSLeela Krishna Amudala 
730af4ea631SLeela Krishna Amudala 	platform_set_drvdata(pdev, wdt);
731af4ea631SLeela Krishna Amudala 
732b7e04f8cSWim Van Sebroeck 	/* print out a statement of readiness */
733b7e04f8cSWim Van Sebroeck 
734af4ea631SLeela Krishna Amudala 	wtcon = readl(wdt->reg_base + S3C2410_WTCON);
735b7e04f8cSWim Van Sebroeck 
736b7e04f8cSWim Van Sebroeck 	dev_info(dev, "watchdog %sactive, reset %sabled, irq %sabled\n",
737b7e04f8cSWim Van Sebroeck 		 (wtcon & S3C2410_WTCON_ENABLE) ?  "" : "in",
73820403e84SDmitry Artamonow 		 (wtcon & S3C2410_WTCON_RSTEN) ? "en" : "dis",
73920403e84SDmitry Artamonow 		 (wtcon & S3C2410_WTCON_INTEN) ? "en" : "dis");
740b7e04f8cSWim Van Sebroeck 
741b7e04f8cSWim Van Sebroeck 	return 0;
742b7e04f8cSWim Van Sebroeck }
743b7e04f8cSWim Van Sebroeck 
s3c2410wdt_shutdown(struct platform_device * dev)744b7e04f8cSWim Van Sebroeck static void s3c2410wdt_shutdown(struct platform_device *dev)
745b7e04f8cSWim Van Sebroeck {
746af4ea631SLeela Krishna Amudala 	struct s3c2410_wdt *wdt = platform_get_drvdata(dev);
747af4ea631SLeela Krishna Amudala 
748cf3fad4eSSam Protsenko 	s3c2410wdt_enable(wdt, false);
749af4ea631SLeela Krishna Amudala 	s3c2410wdt_stop(&wdt->wdt_device);
750b7e04f8cSWim Van Sebroeck }
751b7e04f8cSWim Van Sebroeck 
s3c2410wdt_suspend(struct device * dev)7520183984cSJingoo Han static int s3c2410wdt_suspend(struct device *dev)
753b7e04f8cSWim Van Sebroeck {
7544f1f653aSLeela Krishna Amudala 	int ret;
755af4ea631SLeela Krishna Amudala 	struct s3c2410_wdt *wdt = dev_get_drvdata(dev);
756af4ea631SLeela Krishna Amudala 
757b7e04f8cSWim Van Sebroeck 	/* Save watchdog state, and turn it off. */
758af4ea631SLeela Krishna Amudala 	wdt->wtcon_save = readl(wdt->reg_base + S3C2410_WTCON);
759af4ea631SLeela Krishna Amudala 	wdt->wtdat_save = readl(wdt->reg_base + S3C2410_WTDAT);
760b7e04f8cSWim Van Sebroeck 
761cf3fad4eSSam Protsenko 	ret = s3c2410wdt_enable(wdt, false);
7624f1f653aSLeela Krishna Amudala 	if (ret < 0)
7634f1f653aSLeela Krishna Amudala 		return ret;
7644f1f653aSLeela Krishna Amudala 
765b7e04f8cSWim Van Sebroeck 	/* Note that WTCNT doesn't need to be saved. */
766af4ea631SLeela Krishna Amudala 	s3c2410wdt_stop(&wdt->wdt_device);
767b7e04f8cSWim Van Sebroeck 
768b7e04f8cSWim Van Sebroeck 	return 0;
769b7e04f8cSWim Van Sebroeck }
770b7e04f8cSWim Van Sebroeck 
s3c2410wdt_resume(struct device * dev)7710183984cSJingoo Han static int s3c2410wdt_resume(struct device *dev)
772b7e04f8cSWim Van Sebroeck {
7734f1f653aSLeela Krishna Amudala 	int ret;
774af4ea631SLeela Krishna Amudala 	struct s3c2410_wdt *wdt = dev_get_drvdata(dev);
775b7e04f8cSWim Van Sebroeck 
776af4ea631SLeela Krishna Amudala 	/* Restore watchdog state. */
777af4ea631SLeela Krishna Amudala 	writel(wdt->wtdat_save, wdt->reg_base + S3C2410_WTDAT);
778af4ea631SLeela Krishna Amudala 	writel(wdt->wtdat_save, wdt->reg_base + S3C2410_WTCNT);/* Reset count */
779af4ea631SLeela Krishna Amudala 	writel(wdt->wtcon_save, wdt->reg_base + S3C2410_WTCON);
780b7e04f8cSWim Van Sebroeck 
781cf3fad4eSSam Protsenko 	ret = s3c2410wdt_enable(wdt, true);
7824f1f653aSLeela Krishna Amudala 	if (ret < 0)
7834f1f653aSLeela Krishna Amudala 		return ret;
7844f1f653aSLeela Krishna Amudala 
7850183984cSJingoo Han 	dev_info(dev, "watchdog %sabled\n",
786af4ea631SLeela Krishna Amudala 		(wdt->wtcon_save & S3C2410_WTCON_ENABLE) ? "en" : "dis");
787b7e04f8cSWim Van Sebroeck 
788b7e04f8cSWim Van Sebroeck 	return 0;
789b7e04f8cSWim Van Sebroeck }
790b7e04f8cSWim Van Sebroeck 
791aa55761aSPaul Cercueil static DEFINE_SIMPLE_DEV_PM_OPS(s3c2410wdt_pm_ops,
792aa55761aSPaul Cercueil 				s3c2410wdt_suspend, s3c2410wdt_resume);
793b7e04f8cSWim Van Sebroeck 
794b7e04f8cSWim Van Sebroeck static struct platform_driver s3c2410wdt_driver = {
795b7e04f8cSWim Van Sebroeck 	.probe		= s3c2410wdt_probe,
796b7e04f8cSWim Van Sebroeck 	.shutdown	= s3c2410wdt_shutdown,
7974f1f653aSLeela Krishna Amudala 	.id_table	= s3c2410_wdt_ids,
798b7e04f8cSWim Van Sebroeck 	.driver		= {
799b7e04f8cSWim Van Sebroeck 		.name	= "s3c2410-wdt",
800aa55761aSPaul Cercueil 		.pm	= pm_sleep_ptr(&s3c2410wdt_pm_ops),
8013016a552SWim Van Sebroeck 		.of_match_table	= of_match_ptr(s3c2410_wdt_match),
802b7e04f8cSWim Van Sebroeck 	},
803b7e04f8cSWim Van Sebroeck };
804b7e04f8cSWim Van Sebroeck 
8056b761b29SSachin Kamat module_platform_driver(s3c2410wdt_driver);
806b7e04f8cSWim Van Sebroeck 
80708497f22SKrzysztof Kozlowski MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>, Dimitry Andric <dimitry.andric@tomtom.com>");
808b7e04f8cSWim Van Sebroeck MODULE_DESCRIPTION("S3C2410 Watchdog Device Driver");
809b7e04f8cSWim Van Sebroeck MODULE_LICENSE("GPL");
810