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