xref: /OK3568_Linux_fs/kernel/drivers/watchdog/dw_wdt.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * Copyright 2010-2011 Picochip Ltd., Jamie Iles
4*4882a593Smuzhiyun  * https://www.picochip.com
5*4882a593Smuzhiyun  *
6*4882a593Smuzhiyun  * This file implements a driver for the Synopsys DesignWare watchdog device
7*4882a593Smuzhiyun  * in the many subsystems. The watchdog has 16 different timeout periods
8*4882a593Smuzhiyun  * and these are a function of the input clock frequency.
9*4882a593Smuzhiyun  *
10*4882a593Smuzhiyun  * The DesignWare watchdog cannot be stopped once it has been started so we
11*4882a593Smuzhiyun  * do not implement a stop function. The watchdog core will continue to send
12*4882a593Smuzhiyun  * heartbeat requests after the watchdog device has been closed.
13*4882a593Smuzhiyun  */
14*4882a593Smuzhiyun 
15*4882a593Smuzhiyun #include <linux/bitops.h>
16*4882a593Smuzhiyun #include <linux/limits.h>
17*4882a593Smuzhiyun #include <linux/kernel.h>
18*4882a593Smuzhiyun #include <linux/clk.h>
19*4882a593Smuzhiyun #include <linux/delay.h>
20*4882a593Smuzhiyun #include <linux/err.h>
21*4882a593Smuzhiyun #include <linux/io.h>
22*4882a593Smuzhiyun #include <linux/kernel.h>
23*4882a593Smuzhiyun #include <linux/module.h>
24*4882a593Smuzhiyun #include <linux/moduleparam.h>
25*4882a593Smuzhiyun #include <linux/interrupt.h>
26*4882a593Smuzhiyun #include <linux/of.h>
27*4882a593Smuzhiyun #include <linux/pm.h>
28*4882a593Smuzhiyun #include <linux/platform_device.h>
29*4882a593Smuzhiyun #include <linux/reset.h>
30*4882a593Smuzhiyun #include <linux/watchdog.h>
31*4882a593Smuzhiyun #include <linux/debugfs.h>
32*4882a593Smuzhiyun 
33*4882a593Smuzhiyun #define WDOG_CONTROL_REG_OFFSET		    0x00
34*4882a593Smuzhiyun #define WDOG_CONTROL_REG_WDT_EN_MASK	    0x01
35*4882a593Smuzhiyun #define WDOG_CONTROL_REG_RESP_MODE_MASK	    0x02
36*4882a593Smuzhiyun #define WDOG_TIMEOUT_RANGE_REG_OFFSET	    0x04
37*4882a593Smuzhiyun #define WDOG_TIMEOUT_RANGE_TOPINIT_SHIFT    4
38*4882a593Smuzhiyun #define WDOG_CURRENT_COUNT_REG_OFFSET	    0x08
39*4882a593Smuzhiyun #define WDOG_COUNTER_RESTART_REG_OFFSET     0x0c
40*4882a593Smuzhiyun #define WDOG_COUNTER_RESTART_KICK_VALUE	    0x76
41*4882a593Smuzhiyun #define WDOG_INTERRUPT_STATUS_REG_OFFSET    0x10
42*4882a593Smuzhiyun #define WDOG_INTERRUPT_CLEAR_REG_OFFSET     0x14
43*4882a593Smuzhiyun #define WDOG_COMP_PARAMS_5_REG_OFFSET       0xe4
44*4882a593Smuzhiyun #define WDOG_COMP_PARAMS_4_REG_OFFSET       0xe8
45*4882a593Smuzhiyun #define WDOG_COMP_PARAMS_3_REG_OFFSET       0xec
46*4882a593Smuzhiyun #define WDOG_COMP_PARAMS_2_REG_OFFSET       0xf0
47*4882a593Smuzhiyun #define WDOG_COMP_PARAMS_1_REG_OFFSET       0xf4
48*4882a593Smuzhiyun #define WDOG_COMP_PARAMS_1_USE_FIX_TOP      BIT(6)
49*4882a593Smuzhiyun #define WDOG_COMP_VERSION_REG_OFFSET        0xf8
50*4882a593Smuzhiyun #define WDOG_COMP_TYPE_REG_OFFSET           0xfc
51*4882a593Smuzhiyun 
52*4882a593Smuzhiyun /* There are sixteen TOPs (timeout periods) that can be set in the watchdog. */
53*4882a593Smuzhiyun #define DW_WDT_NUM_TOPS		16
54*4882a593Smuzhiyun #define DW_WDT_FIX_TOP(_idx)	(1U << (16 + _idx))
55*4882a593Smuzhiyun 
56*4882a593Smuzhiyun #define DW_WDT_DEFAULT_SECONDS	30
57*4882a593Smuzhiyun 
58*4882a593Smuzhiyun static const u32 dw_wdt_fix_tops[DW_WDT_NUM_TOPS] = {
59*4882a593Smuzhiyun 	DW_WDT_FIX_TOP(0), DW_WDT_FIX_TOP(1), DW_WDT_FIX_TOP(2),
60*4882a593Smuzhiyun 	DW_WDT_FIX_TOP(3), DW_WDT_FIX_TOP(4), DW_WDT_FIX_TOP(5),
61*4882a593Smuzhiyun 	DW_WDT_FIX_TOP(6), DW_WDT_FIX_TOP(7), DW_WDT_FIX_TOP(8),
62*4882a593Smuzhiyun 	DW_WDT_FIX_TOP(9), DW_WDT_FIX_TOP(10), DW_WDT_FIX_TOP(11),
63*4882a593Smuzhiyun 	DW_WDT_FIX_TOP(12), DW_WDT_FIX_TOP(13), DW_WDT_FIX_TOP(14),
64*4882a593Smuzhiyun 	DW_WDT_FIX_TOP(15)
65*4882a593Smuzhiyun };
66*4882a593Smuzhiyun 
67*4882a593Smuzhiyun static bool nowayout = WATCHDOG_NOWAYOUT;
68*4882a593Smuzhiyun module_param(nowayout, bool, 0);
69*4882a593Smuzhiyun MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
70*4882a593Smuzhiyun 		 "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
71*4882a593Smuzhiyun 
72*4882a593Smuzhiyun enum dw_wdt_rmod {
73*4882a593Smuzhiyun 	DW_WDT_RMOD_RESET = 1,
74*4882a593Smuzhiyun 	DW_WDT_RMOD_IRQ = 2
75*4882a593Smuzhiyun };
76*4882a593Smuzhiyun 
77*4882a593Smuzhiyun struct dw_wdt_timeout {
78*4882a593Smuzhiyun 	u32 top_val;
79*4882a593Smuzhiyun 	unsigned int sec;
80*4882a593Smuzhiyun 	unsigned int msec;
81*4882a593Smuzhiyun };
82*4882a593Smuzhiyun 
83*4882a593Smuzhiyun struct dw_wdt {
84*4882a593Smuzhiyun 	void __iomem		*regs;
85*4882a593Smuzhiyun 	struct clk		*clk;
86*4882a593Smuzhiyun 	struct clk		*pclk;
87*4882a593Smuzhiyun 	unsigned long		rate;
88*4882a593Smuzhiyun 	enum dw_wdt_rmod	rmod;
89*4882a593Smuzhiyun 	struct dw_wdt_timeout	timeouts[DW_WDT_NUM_TOPS];
90*4882a593Smuzhiyun 	struct watchdog_device	wdd;
91*4882a593Smuzhiyun 	struct reset_control	*rst;
92*4882a593Smuzhiyun 	/* Save/restore */
93*4882a593Smuzhiyun 	u32			control;
94*4882a593Smuzhiyun 	u32			timeout;
95*4882a593Smuzhiyun 
96*4882a593Smuzhiyun #ifdef CONFIG_DEBUG_FS
97*4882a593Smuzhiyun 	struct dentry		*dbgfs_dir;
98*4882a593Smuzhiyun #endif
99*4882a593Smuzhiyun };
100*4882a593Smuzhiyun 
101*4882a593Smuzhiyun #define to_dw_wdt(wdd)	container_of(wdd, struct dw_wdt, wdd)
102*4882a593Smuzhiyun 
dw_wdt_is_enabled(struct dw_wdt * dw_wdt)103*4882a593Smuzhiyun static inline int dw_wdt_is_enabled(struct dw_wdt *dw_wdt)
104*4882a593Smuzhiyun {
105*4882a593Smuzhiyun 	return readl(dw_wdt->regs + WDOG_CONTROL_REG_OFFSET) &
106*4882a593Smuzhiyun 		WDOG_CONTROL_REG_WDT_EN_MASK;
107*4882a593Smuzhiyun }
108*4882a593Smuzhiyun 
dw_wdt_update_mode(struct dw_wdt * dw_wdt,enum dw_wdt_rmod rmod)109*4882a593Smuzhiyun static void dw_wdt_update_mode(struct dw_wdt *dw_wdt, enum dw_wdt_rmod rmod)
110*4882a593Smuzhiyun {
111*4882a593Smuzhiyun 	u32 val;
112*4882a593Smuzhiyun 
113*4882a593Smuzhiyun 	val = readl(dw_wdt->regs + WDOG_CONTROL_REG_OFFSET);
114*4882a593Smuzhiyun 	if (rmod == DW_WDT_RMOD_IRQ)
115*4882a593Smuzhiyun 		val |= WDOG_CONTROL_REG_RESP_MODE_MASK;
116*4882a593Smuzhiyun 	else
117*4882a593Smuzhiyun 		val &= ~WDOG_CONTROL_REG_RESP_MODE_MASK;
118*4882a593Smuzhiyun 	writel(val, dw_wdt->regs + WDOG_CONTROL_REG_OFFSET);
119*4882a593Smuzhiyun 
120*4882a593Smuzhiyun 	dw_wdt->rmod = rmod;
121*4882a593Smuzhiyun }
122*4882a593Smuzhiyun 
dw_wdt_find_best_top(struct dw_wdt * dw_wdt,unsigned int timeout,u32 * top_val)123*4882a593Smuzhiyun static unsigned int dw_wdt_find_best_top(struct dw_wdt *dw_wdt,
124*4882a593Smuzhiyun 					 unsigned int timeout, u32 *top_val)
125*4882a593Smuzhiyun {
126*4882a593Smuzhiyun 	int idx;
127*4882a593Smuzhiyun 
128*4882a593Smuzhiyun 	/*
129*4882a593Smuzhiyun 	 * Find a TOP with timeout greater or equal to the requested number.
130*4882a593Smuzhiyun 	 * Note we'll select a TOP with maximum timeout if the requested
131*4882a593Smuzhiyun 	 * timeout couldn't be reached.
132*4882a593Smuzhiyun 	 */
133*4882a593Smuzhiyun 	for (idx = 0; idx < DW_WDT_NUM_TOPS; ++idx) {
134*4882a593Smuzhiyun 		if (dw_wdt->timeouts[idx].sec >= timeout)
135*4882a593Smuzhiyun 			break;
136*4882a593Smuzhiyun 	}
137*4882a593Smuzhiyun 
138*4882a593Smuzhiyun 	if (idx == DW_WDT_NUM_TOPS)
139*4882a593Smuzhiyun 		--idx;
140*4882a593Smuzhiyun 
141*4882a593Smuzhiyun 	*top_val = dw_wdt->timeouts[idx].top_val;
142*4882a593Smuzhiyun 
143*4882a593Smuzhiyun 	return dw_wdt->timeouts[idx].sec;
144*4882a593Smuzhiyun }
145*4882a593Smuzhiyun 
dw_wdt_get_min_timeout(struct dw_wdt * dw_wdt)146*4882a593Smuzhiyun static unsigned int dw_wdt_get_min_timeout(struct dw_wdt *dw_wdt)
147*4882a593Smuzhiyun {
148*4882a593Smuzhiyun 	int idx;
149*4882a593Smuzhiyun 
150*4882a593Smuzhiyun 	/*
151*4882a593Smuzhiyun 	 * We'll find a timeout greater or equal to one second anyway because
152*4882a593Smuzhiyun 	 * the driver probe would have failed if there was none.
153*4882a593Smuzhiyun 	 */
154*4882a593Smuzhiyun 	for (idx = 0; idx < DW_WDT_NUM_TOPS; ++idx) {
155*4882a593Smuzhiyun 		if (dw_wdt->timeouts[idx].sec)
156*4882a593Smuzhiyun 			break;
157*4882a593Smuzhiyun 	}
158*4882a593Smuzhiyun 
159*4882a593Smuzhiyun 	return dw_wdt->timeouts[idx].sec;
160*4882a593Smuzhiyun }
161*4882a593Smuzhiyun 
dw_wdt_get_max_timeout_ms(struct dw_wdt * dw_wdt)162*4882a593Smuzhiyun static unsigned int dw_wdt_get_max_timeout_ms(struct dw_wdt *dw_wdt)
163*4882a593Smuzhiyun {
164*4882a593Smuzhiyun 	struct dw_wdt_timeout *timeout = &dw_wdt->timeouts[DW_WDT_NUM_TOPS - 1];
165*4882a593Smuzhiyun 	u64 msec;
166*4882a593Smuzhiyun 
167*4882a593Smuzhiyun 	msec = (u64)timeout->sec * MSEC_PER_SEC + timeout->msec;
168*4882a593Smuzhiyun 
169*4882a593Smuzhiyun 	return msec < UINT_MAX ? msec : UINT_MAX;
170*4882a593Smuzhiyun }
171*4882a593Smuzhiyun 
dw_wdt_get_timeout(struct dw_wdt * dw_wdt)172*4882a593Smuzhiyun static unsigned int dw_wdt_get_timeout(struct dw_wdt *dw_wdt)
173*4882a593Smuzhiyun {
174*4882a593Smuzhiyun 	int top_val = readl(dw_wdt->regs + WDOG_TIMEOUT_RANGE_REG_OFFSET) & 0xF;
175*4882a593Smuzhiyun 	int idx;
176*4882a593Smuzhiyun 
177*4882a593Smuzhiyun 	for (idx = 0; idx < DW_WDT_NUM_TOPS; ++idx) {
178*4882a593Smuzhiyun 		if (dw_wdt->timeouts[idx].top_val == top_val)
179*4882a593Smuzhiyun 			break;
180*4882a593Smuzhiyun 	}
181*4882a593Smuzhiyun 
182*4882a593Smuzhiyun 	/*
183*4882a593Smuzhiyun 	 * In IRQ mode due to the two stages counter, the actual timeout is
184*4882a593Smuzhiyun 	 * twice greater than the TOP setting.
185*4882a593Smuzhiyun 	 */
186*4882a593Smuzhiyun 	return dw_wdt->timeouts[idx].sec * dw_wdt->rmod;
187*4882a593Smuzhiyun }
188*4882a593Smuzhiyun 
dw_wdt_ping(struct watchdog_device * wdd)189*4882a593Smuzhiyun static int dw_wdt_ping(struct watchdog_device *wdd)
190*4882a593Smuzhiyun {
191*4882a593Smuzhiyun 	struct dw_wdt *dw_wdt = to_dw_wdt(wdd);
192*4882a593Smuzhiyun 
193*4882a593Smuzhiyun 	writel(WDOG_COUNTER_RESTART_KICK_VALUE, dw_wdt->regs +
194*4882a593Smuzhiyun 	       WDOG_COUNTER_RESTART_REG_OFFSET);
195*4882a593Smuzhiyun 
196*4882a593Smuzhiyun 	return 0;
197*4882a593Smuzhiyun }
198*4882a593Smuzhiyun 
dw_wdt_set_timeout(struct watchdog_device * wdd,unsigned int top_s)199*4882a593Smuzhiyun static int dw_wdt_set_timeout(struct watchdog_device *wdd, unsigned int top_s)
200*4882a593Smuzhiyun {
201*4882a593Smuzhiyun 	struct dw_wdt *dw_wdt = to_dw_wdt(wdd);
202*4882a593Smuzhiyun 	unsigned int timeout;
203*4882a593Smuzhiyun 	u32 top_val;
204*4882a593Smuzhiyun 
205*4882a593Smuzhiyun 	/*
206*4882a593Smuzhiyun 	 * Note IRQ mode being enabled means having a non-zero pre-timeout
207*4882a593Smuzhiyun 	 * setup. In this case we try to find a TOP as close to the half of the
208*4882a593Smuzhiyun 	 * requested timeout as possible since DW Watchdog IRQ mode is designed
209*4882a593Smuzhiyun 	 * in two stages way - first timeout rises the pre-timeout interrupt,
210*4882a593Smuzhiyun 	 * second timeout performs the system reset. So basically the effective
211*4882a593Smuzhiyun 	 * watchdog-caused reset happens after two watchdog TOPs elapsed.
212*4882a593Smuzhiyun 	 */
213*4882a593Smuzhiyun 	timeout = dw_wdt_find_best_top(dw_wdt, DIV_ROUND_UP(top_s, dw_wdt->rmod),
214*4882a593Smuzhiyun 				       &top_val);
215*4882a593Smuzhiyun 	if (dw_wdt->rmod == DW_WDT_RMOD_IRQ)
216*4882a593Smuzhiyun 		wdd->pretimeout = timeout;
217*4882a593Smuzhiyun 	else
218*4882a593Smuzhiyun 		wdd->pretimeout = 0;
219*4882a593Smuzhiyun 
220*4882a593Smuzhiyun 	/*
221*4882a593Smuzhiyun 	 * Set the new value in the watchdog.  Some versions of dw_wdt
222*4882a593Smuzhiyun 	 * have have TOPINIT in the TIMEOUT_RANGE register (as per
223*4882a593Smuzhiyun 	 * CP_WDT_DUAL_TOP in WDT_COMP_PARAMS_1).  On those we
224*4882a593Smuzhiyun 	 * effectively get a pat of the watchdog right here.
225*4882a593Smuzhiyun 	 */
226*4882a593Smuzhiyun 	writel(top_val | top_val << WDOG_TIMEOUT_RANGE_TOPINIT_SHIFT,
227*4882a593Smuzhiyun 	       dw_wdt->regs + WDOG_TIMEOUT_RANGE_REG_OFFSET);
228*4882a593Smuzhiyun 
229*4882a593Smuzhiyun 	/* Kick new TOP value into the watchdog counter if activated. */
230*4882a593Smuzhiyun 	if (watchdog_active(wdd))
231*4882a593Smuzhiyun 		dw_wdt_ping(wdd);
232*4882a593Smuzhiyun 
233*4882a593Smuzhiyun 	/*
234*4882a593Smuzhiyun 	 * In case users set bigger timeout value than HW can support,
235*4882a593Smuzhiyun 	 * kernel(watchdog_dev.c) helps to feed watchdog before
236*4882a593Smuzhiyun 	 * wdd->max_hw_heartbeat_ms
237*4882a593Smuzhiyun 	 */
238*4882a593Smuzhiyun 	if (top_s * 1000 <= wdd->max_hw_heartbeat_ms)
239*4882a593Smuzhiyun 		wdd->timeout = timeout * dw_wdt->rmod;
240*4882a593Smuzhiyun 	else
241*4882a593Smuzhiyun 		wdd->timeout = top_s;
242*4882a593Smuzhiyun 
243*4882a593Smuzhiyun 	return 0;
244*4882a593Smuzhiyun }
245*4882a593Smuzhiyun 
dw_wdt_set_pretimeout(struct watchdog_device * wdd,unsigned int req)246*4882a593Smuzhiyun static int dw_wdt_set_pretimeout(struct watchdog_device *wdd, unsigned int req)
247*4882a593Smuzhiyun {
248*4882a593Smuzhiyun 	struct dw_wdt *dw_wdt = to_dw_wdt(wdd);
249*4882a593Smuzhiyun 
250*4882a593Smuzhiyun 	/*
251*4882a593Smuzhiyun 	 * We ignore actual value of the timeout passed from user-space
252*4882a593Smuzhiyun 	 * using it as a flag whether the pretimeout functionality is intended
253*4882a593Smuzhiyun 	 * to be activated.
254*4882a593Smuzhiyun 	 */
255*4882a593Smuzhiyun 	dw_wdt_update_mode(dw_wdt, req ? DW_WDT_RMOD_IRQ : DW_WDT_RMOD_RESET);
256*4882a593Smuzhiyun 	dw_wdt_set_timeout(wdd, wdd->timeout);
257*4882a593Smuzhiyun 
258*4882a593Smuzhiyun 	return 0;
259*4882a593Smuzhiyun }
260*4882a593Smuzhiyun 
dw_wdt_arm_system_reset(struct dw_wdt * dw_wdt)261*4882a593Smuzhiyun static void dw_wdt_arm_system_reset(struct dw_wdt *dw_wdt)
262*4882a593Smuzhiyun {
263*4882a593Smuzhiyun 	u32 val = readl(dw_wdt->regs + WDOG_CONTROL_REG_OFFSET);
264*4882a593Smuzhiyun 
265*4882a593Smuzhiyun 	/* Disable/enable interrupt mode depending on the RMOD flag. */
266*4882a593Smuzhiyun 	if (dw_wdt->rmod == DW_WDT_RMOD_IRQ)
267*4882a593Smuzhiyun 		val |= WDOG_CONTROL_REG_RESP_MODE_MASK;
268*4882a593Smuzhiyun 	else
269*4882a593Smuzhiyun 		val &= ~WDOG_CONTROL_REG_RESP_MODE_MASK;
270*4882a593Smuzhiyun 	/* Enable watchdog. */
271*4882a593Smuzhiyun 	val |= WDOG_CONTROL_REG_WDT_EN_MASK;
272*4882a593Smuzhiyun 	writel(val, dw_wdt->regs + WDOG_CONTROL_REG_OFFSET);
273*4882a593Smuzhiyun }
274*4882a593Smuzhiyun 
dw_wdt_start(struct watchdog_device * wdd)275*4882a593Smuzhiyun static int dw_wdt_start(struct watchdog_device *wdd)
276*4882a593Smuzhiyun {
277*4882a593Smuzhiyun 	struct dw_wdt *dw_wdt = to_dw_wdt(wdd);
278*4882a593Smuzhiyun 
279*4882a593Smuzhiyun 	dw_wdt_set_timeout(wdd, wdd->timeout);
280*4882a593Smuzhiyun 	dw_wdt_ping(&dw_wdt->wdd);
281*4882a593Smuzhiyun 	dw_wdt_arm_system_reset(dw_wdt);
282*4882a593Smuzhiyun 
283*4882a593Smuzhiyun 	return 0;
284*4882a593Smuzhiyun }
285*4882a593Smuzhiyun 
dw_wdt_stop(struct watchdog_device * wdd)286*4882a593Smuzhiyun static int dw_wdt_stop(struct watchdog_device *wdd)
287*4882a593Smuzhiyun {
288*4882a593Smuzhiyun 	struct dw_wdt *dw_wdt = to_dw_wdt(wdd);
289*4882a593Smuzhiyun 
290*4882a593Smuzhiyun 	if (!dw_wdt->rst) {
291*4882a593Smuzhiyun 		set_bit(WDOG_HW_RUNNING, &wdd->status);
292*4882a593Smuzhiyun 		return 0;
293*4882a593Smuzhiyun 	}
294*4882a593Smuzhiyun 
295*4882a593Smuzhiyun 	reset_control_assert(dw_wdt->rst);
296*4882a593Smuzhiyun 	reset_control_deassert(dw_wdt->rst);
297*4882a593Smuzhiyun 
298*4882a593Smuzhiyun 	return 0;
299*4882a593Smuzhiyun }
300*4882a593Smuzhiyun 
dw_wdt_restart(struct watchdog_device * wdd,unsigned long action,void * data)301*4882a593Smuzhiyun static int dw_wdt_restart(struct watchdog_device *wdd,
302*4882a593Smuzhiyun 			  unsigned long action, void *data)
303*4882a593Smuzhiyun {
304*4882a593Smuzhiyun 	struct dw_wdt *dw_wdt = to_dw_wdt(wdd);
305*4882a593Smuzhiyun 
306*4882a593Smuzhiyun 	writel(0, dw_wdt->regs + WDOG_TIMEOUT_RANGE_REG_OFFSET);
307*4882a593Smuzhiyun 	dw_wdt_update_mode(dw_wdt, DW_WDT_RMOD_RESET);
308*4882a593Smuzhiyun 	if (dw_wdt_is_enabled(dw_wdt))
309*4882a593Smuzhiyun 		writel(WDOG_COUNTER_RESTART_KICK_VALUE,
310*4882a593Smuzhiyun 		       dw_wdt->regs + WDOG_COUNTER_RESTART_REG_OFFSET);
311*4882a593Smuzhiyun 	else
312*4882a593Smuzhiyun 		dw_wdt_arm_system_reset(dw_wdt);
313*4882a593Smuzhiyun 
314*4882a593Smuzhiyun 	/* wait for reset to assert... */
315*4882a593Smuzhiyun 	mdelay(500);
316*4882a593Smuzhiyun 
317*4882a593Smuzhiyun 	return 0;
318*4882a593Smuzhiyun }
319*4882a593Smuzhiyun 
dw_wdt_get_timeleft(struct watchdog_device * wdd)320*4882a593Smuzhiyun static unsigned int dw_wdt_get_timeleft(struct watchdog_device *wdd)
321*4882a593Smuzhiyun {
322*4882a593Smuzhiyun 	struct dw_wdt *dw_wdt = to_dw_wdt(wdd);
323*4882a593Smuzhiyun 	unsigned int sec;
324*4882a593Smuzhiyun 	u32 val;
325*4882a593Smuzhiyun 
326*4882a593Smuzhiyun 	val = readl(dw_wdt->regs + WDOG_CURRENT_COUNT_REG_OFFSET);
327*4882a593Smuzhiyun 	sec = val / dw_wdt->rate;
328*4882a593Smuzhiyun 
329*4882a593Smuzhiyun 	if (dw_wdt->rmod == DW_WDT_RMOD_IRQ) {
330*4882a593Smuzhiyun 		val = readl(dw_wdt->regs + WDOG_INTERRUPT_STATUS_REG_OFFSET);
331*4882a593Smuzhiyun 		if (!val)
332*4882a593Smuzhiyun 			sec += wdd->pretimeout;
333*4882a593Smuzhiyun 	}
334*4882a593Smuzhiyun 
335*4882a593Smuzhiyun 	return sec;
336*4882a593Smuzhiyun }
337*4882a593Smuzhiyun 
338*4882a593Smuzhiyun static const struct watchdog_info dw_wdt_ident = {
339*4882a593Smuzhiyun 	.options	= WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT |
340*4882a593Smuzhiyun 			  WDIOF_MAGICCLOSE,
341*4882a593Smuzhiyun 	.identity	= "Synopsys DesignWare Watchdog",
342*4882a593Smuzhiyun };
343*4882a593Smuzhiyun 
344*4882a593Smuzhiyun static const struct watchdog_info dw_wdt_pt_ident = {
345*4882a593Smuzhiyun 	.options	= WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT |
346*4882a593Smuzhiyun 			  WDIOF_PRETIMEOUT | WDIOF_MAGICCLOSE,
347*4882a593Smuzhiyun 	.identity	= "Synopsys DesignWare Watchdog",
348*4882a593Smuzhiyun };
349*4882a593Smuzhiyun 
350*4882a593Smuzhiyun static const struct watchdog_ops dw_wdt_ops = {
351*4882a593Smuzhiyun 	.owner		= THIS_MODULE,
352*4882a593Smuzhiyun 	.start		= dw_wdt_start,
353*4882a593Smuzhiyun 	.stop		= dw_wdt_stop,
354*4882a593Smuzhiyun 	.ping		= dw_wdt_ping,
355*4882a593Smuzhiyun 	.set_timeout	= dw_wdt_set_timeout,
356*4882a593Smuzhiyun 	.set_pretimeout	= dw_wdt_set_pretimeout,
357*4882a593Smuzhiyun 	.get_timeleft	= dw_wdt_get_timeleft,
358*4882a593Smuzhiyun 	.restart	= dw_wdt_restart,
359*4882a593Smuzhiyun };
360*4882a593Smuzhiyun 
dw_wdt_irq(int irq,void * devid)361*4882a593Smuzhiyun static irqreturn_t dw_wdt_irq(int irq, void *devid)
362*4882a593Smuzhiyun {
363*4882a593Smuzhiyun 	struct dw_wdt *dw_wdt = devid;
364*4882a593Smuzhiyun 	u32 val;
365*4882a593Smuzhiyun 
366*4882a593Smuzhiyun 	/*
367*4882a593Smuzhiyun 	 * We don't clear the IRQ status. It's supposed to be done by the
368*4882a593Smuzhiyun 	 * following ping operations.
369*4882a593Smuzhiyun 	 */
370*4882a593Smuzhiyun 	val = readl(dw_wdt->regs + WDOG_INTERRUPT_STATUS_REG_OFFSET);
371*4882a593Smuzhiyun 	if (!val)
372*4882a593Smuzhiyun 		return IRQ_NONE;
373*4882a593Smuzhiyun 
374*4882a593Smuzhiyun 	watchdog_notify_pretimeout(&dw_wdt->wdd);
375*4882a593Smuzhiyun 
376*4882a593Smuzhiyun 	return IRQ_HANDLED;
377*4882a593Smuzhiyun }
378*4882a593Smuzhiyun 
379*4882a593Smuzhiyun #ifdef CONFIG_PM_SLEEP
dw_wdt_suspend(struct device * dev)380*4882a593Smuzhiyun static int dw_wdt_suspend(struct device *dev)
381*4882a593Smuzhiyun {
382*4882a593Smuzhiyun 	struct dw_wdt *dw_wdt = dev_get_drvdata(dev);
383*4882a593Smuzhiyun 
384*4882a593Smuzhiyun 	dw_wdt->control = readl(dw_wdt->regs + WDOG_CONTROL_REG_OFFSET);
385*4882a593Smuzhiyun 	dw_wdt->timeout = readl(dw_wdt->regs + WDOG_TIMEOUT_RANGE_REG_OFFSET);
386*4882a593Smuzhiyun 
387*4882a593Smuzhiyun 	clk_disable_unprepare(dw_wdt->pclk);
388*4882a593Smuzhiyun 	clk_disable_unprepare(dw_wdt->clk);
389*4882a593Smuzhiyun 
390*4882a593Smuzhiyun 	return 0;
391*4882a593Smuzhiyun }
392*4882a593Smuzhiyun 
dw_wdt_resume(struct device * dev)393*4882a593Smuzhiyun static int dw_wdt_resume(struct device *dev)
394*4882a593Smuzhiyun {
395*4882a593Smuzhiyun 	struct dw_wdt *dw_wdt = dev_get_drvdata(dev);
396*4882a593Smuzhiyun 	int err = clk_prepare_enable(dw_wdt->clk);
397*4882a593Smuzhiyun 
398*4882a593Smuzhiyun 	if (err)
399*4882a593Smuzhiyun 		return err;
400*4882a593Smuzhiyun 
401*4882a593Smuzhiyun 	err = clk_prepare_enable(dw_wdt->pclk);
402*4882a593Smuzhiyun 	if (err) {
403*4882a593Smuzhiyun 		clk_disable_unprepare(dw_wdt->clk);
404*4882a593Smuzhiyun 		return err;
405*4882a593Smuzhiyun 	}
406*4882a593Smuzhiyun 
407*4882a593Smuzhiyun 	writel(dw_wdt->timeout, dw_wdt->regs + WDOG_TIMEOUT_RANGE_REG_OFFSET);
408*4882a593Smuzhiyun 	writel(dw_wdt->control, dw_wdt->regs + WDOG_CONTROL_REG_OFFSET);
409*4882a593Smuzhiyun 
410*4882a593Smuzhiyun 	dw_wdt_ping(&dw_wdt->wdd);
411*4882a593Smuzhiyun 
412*4882a593Smuzhiyun 	return 0;
413*4882a593Smuzhiyun }
414*4882a593Smuzhiyun #endif /* CONFIG_PM_SLEEP */
415*4882a593Smuzhiyun 
416*4882a593Smuzhiyun static SIMPLE_DEV_PM_OPS(dw_wdt_pm_ops, dw_wdt_suspend, dw_wdt_resume);
417*4882a593Smuzhiyun 
418*4882a593Smuzhiyun /*
419*4882a593Smuzhiyun  * In case if DW WDT IP core is synthesized with fixed TOP feature disabled the
420*4882a593Smuzhiyun  * TOPs array can be arbitrary ordered with nearly any sixteen uint numbers
421*4882a593Smuzhiyun  * depending on the system engineer imagination. The next method handles the
422*4882a593Smuzhiyun  * passed TOPs array to pre-calculate the effective timeouts and to sort the
423*4882a593Smuzhiyun  * TOP items out in the ascending order with respect to the timeouts.
424*4882a593Smuzhiyun  */
425*4882a593Smuzhiyun 
dw_wdt_handle_tops(struct dw_wdt * dw_wdt,const u32 * tops)426*4882a593Smuzhiyun static void dw_wdt_handle_tops(struct dw_wdt *dw_wdt, const u32 *tops)
427*4882a593Smuzhiyun {
428*4882a593Smuzhiyun 	struct dw_wdt_timeout tout, *dst;
429*4882a593Smuzhiyun 	int val, tidx;
430*4882a593Smuzhiyun 	u64 msec;
431*4882a593Smuzhiyun 
432*4882a593Smuzhiyun 	/*
433*4882a593Smuzhiyun 	 * We walk over the passed TOPs array and calculate corresponding
434*4882a593Smuzhiyun 	 * timeouts in seconds and milliseconds. The milliseconds granularity
435*4882a593Smuzhiyun 	 * is needed to distinguish the TOPs with very close timeouts and to
436*4882a593Smuzhiyun 	 * set the watchdog max heartbeat setting further.
437*4882a593Smuzhiyun 	 */
438*4882a593Smuzhiyun 	for (val = 0; val < DW_WDT_NUM_TOPS; ++val) {
439*4882a593Smuzhiyun 		tout.top_val = val;
440*4882a593Smuzhiyun 		tout.sec = tops[val] / dw_wdt->rate;
441*4882a593Smuzhiyun 		msec = (u64)tops[val] * MSEC_PER_SEC;
442*4882a593Smuzhiyun 		do_div(msec, dw_wdt->rate);
443*4882a593Smuzhiyun 		tout.msec = msec - ((u64)tout.sec * MSEC_PER_SEC);
444*4882a593Smuzhiyun 
445*4882a593Smuzhiyun 		/*
446*4882a593Smuzhiyun 		 * Find a suitable place for the current TOP in the timeouts
447*4882a593Smuzhiyun 		 * array so that the list is remained in the ascending order.
448*4882a593Smuzhiyun 		 */
449*4882a593Smuzhiyun 		for (tidx = 0; tidx < val; ++tidx) {
450*4882a593Smuzhiyun 			dst = &dw_wdt->timeouts[tidx];
451*4882a593Smuzhiyun 			if (tout.sec > dst->sec || (tout.sec == dst->sec &&
452*4882a593Smuzhiyun 			    tout.msec >= dst->msec))
453*4882a593Smuzhiyun 				continue;
454*4882a593Smuzhiyun 			else
455*4882a593Smuzhiyun 				swap(*dst, tout);
456*4882a593Smuzhiyun 		}
457*4882a593Smuzhiyun 
458*4882a593Smuzhiyun 		dw_wdt->timeouts[val] = tout;
459*4882a593Smuzhiyun 	}
460*4882a593Smuzhiyun }
461*4882a593Smuzhiyun 
dw_wdt_init_timeouts(struct dw_wdt * dw_wdt,struct device * dev)462*4882a593Smuzhiyun static int dw_wdt_init_timeouts(struct dw_wdt *dw_wdt, struct device *dev)
463*4882a593Smuzhiyun {
464*4882a593Smuzhiyun 	u32 data, of_tops[DW_WDT_NUM_TOPS];
465*4882a593Smuzhiyun 	const u32 *tops;
466*4882a593Smuzhiyun 	int ret;
467*4882a593Smuzhiyun 
468*4882a593Smuzhiyun 	/*
469*4882a593Smuzhiyun 	 * Retrieve custom or fixed counter values depending on the
470*4882a593Smuzhiyun 	 * WDT_USE_FIX_TOP flag found in the component specific parameters
471*4882a593Smuzhiyun 	 * #1 register.
472*4882a593Smuzhiyun 	 */
473*4882a593Smuzhiyun 	data = readl(dw_wdt->regs + WDOG_COMP_PARAMS_1_REG_OFFSET);
474*4882a593Smuzhiyun 	if (data & WDOG_COMP_PARAMS_1_USE_FIX_TOP) {
475*4882a593Smuzhiyun 		tops = dw_wdt_fix_tops;
476*4882a593Smuzhiyun 	} else {
477*4882a593Smuzhiyun 		ret = of_property_read_variable_u32_array(dev_of_node(dev),
478*4882a593Smuzhiyun 			"snps,watchdog-tops", of_tops, DW_WDT_NUM_TOPS,
479*4882a593Smuzhiyun 			DW_WDT_NUM_TOPS);
480*4882a593Smuzhiyun 		if (ret < 0) {
481*4882a593Smuzhiyun 			dev_warn(dev, "No valid TOPs array specified\n");
482*4882a593Smuzhiyun 			tops = dw_wdt_fix_tops;
483*4882a593Smuzhiyun 		} else {
484*4882a593Smuzhiyun 			tops = of_tops;
485*4882a593Smuzhiyun 		}
486*4882a593Smuzhiyun 	}
487*4882a593Smuzhiyun 
488*4882a593Smuzhiyun 	/* Convert the specified TOPs into an array of watchdog timeouts. */
489*4882a593Smuzhiyun 	dw_wdt_handle_tops(dw_wdt, tops);
490*4882a593Smuzhiyun 	if (!dw_wdt->timeouts[DW_WDT_NUM_TOPS - 1].sec) {
491*4882a593Smuzhiyun 		dev_err(dev, "No any valid TOP detected\n");
492*4882a593Smuzhiyun 		return -EINVAL;
493*4882a593Smuzhiyun 	}
494*4882a593Smuzhiyun 
495*4882a593Smuzhiyun 	return 0;
496*4882a593Smuzhiyun }
497*4882a593Smuzhiyun 
498*4882a593Smuzhiyun #ifdef CONFIG_DEBUG_FS
499*4882a593Smuzhiyun 
500*4882a593Smuzhiyun #define DW_WDT_DBGFS_REG(_name, _off) \
501*4882a593Smuzhiyun {				      \
502*4882a593Smuzhiyun 	.name = _name,		      \
503*4882a593Smuzhiyun 	.offset = _off		      \
504*4882a593Smuzhiyun }
505*4882a593Smuzhiyun 
506*4882a593Smuzhiyun static const struct debugfs_reg32 dw_wdt_dbgfs_regs[] = {
507*4882a593Smuzhiyun 	DW_WDT_DBGFS_REG("cr", WDOG_CONTROL_REG_OFFSET),
508*4882a593Smuzhiyun 	DW_WDT_DBGFS_REG("torr", WDOG_TIMEOUT_RANGE_REG_OFFSET),
509*4882a593Smuzhiyun 	DW_WDT_DBGFS_REG("ccvr", WDOG_CURRENT_COUNT_REG_OFFSET),
510*4882a593Smuzhiyun 	DW_WDT_DBGFS_REG("crr", WDOG_COUNTER_RESTART_REG_OFFSET),
511*4882a593Smuzhiyun 	DW_WDT_DBGFS_REG("stat", WDOG_INTERRUPT_STATUS_REG_OFFSET),
512*4882a593Smuzhiyun 	DW_WDT_DBGFS_REG("param5", WDOG_COMP_PARAMS_5_REG_OFFSET),
513*4882a593Smuzhiyun 	DW_WDT_DBGFS_REG("param4", WDOG_COMP_PARAMS_4_REG_OFFSET),
514*4882a593Smuzhiyun 	DW_WDT_DBGFS_REG("param3", WDOG_COMP_PARAMS_3_REG_OFFSET),
515*4882a593Smuzhiyun 	DW_WDT_DBGFS_REG("param2", WDOG_COMP_PARAMS_2_REG_OFFSET),
516*4882a593Smuzhiyun 	DW_WDT_DBGFS_REG("param1", WDOG_COMP_PARAMS_1_REG_OFFSET),
517*4882a593Smuzhiyun 	DW_WDT_DBGFS_REG("version", WDOG_COMP_VERSION_REG_OFFSET),
518*4882a593Smuzhiyun 	DW_WDT_DBGFS_REG("type", WDOG_COMP_TYPE_REG_OFFSET)
519*4882a593Smuzhiyun };
520*4882a593Smuzhiyun 
dw_wdt_dbgfs_init(struct dw_wdt * dw_wdt)521*4882a593Smuzhiyun static void dw_wdt_dbgfs_init(struct dw_wdt *dw_wdt)
522*4882a593Smuzhiyun {
523*4882a593Smuzhiyun 	struct device *dev = dw_wdt->wdd.parent;
524*4882a593Smuzhiyun 	struct debugfs_regset32 *regset;
525*4882a593Smuzhiyun 
526*4882a593Smuzhiyun 	regset = devm_kzalloc(dev, sizeof(*regset), GFP_KERNEL);
527*4882a593Smuzhiyun 	if (!regset)
528*4882a593Smuzhiyun 		return;
529*4882a593Smuzhiyun 
530*4882a593Smuzhiyun 	regset->regs = dw_wdt_dbgfs_regs;
531*4882a593Smuzhiyun 	regset->nregs = ARRAY_SIZE(dw_wdt_dbgfs_regs);
532*4882a593Smuzhiyun 	regset->base = dw_wdt->regs;
533*4882a593Smuzhiyun 
534*4882a593Smuzhiyun 	dw_wdt->dbgfs_dir = debugfs_create_dir(dev_name(dev), NULL);
535*4882a593Smuzhiyun 
536*4882a593Smuzhiyun 	debugfs_create_regset32("registers", 0444, dw_wdt->dbgfs_dir, regset);
537*4882a593Smuzhiyun }
538*4882a593Smuzhiyun 
dw_wdt_dbgfs_clear(struct dw_wdt * dw_wdt)539*4882a593Smuzhiyun static void dw_wdt_dbgfs_clear(struct dw_wdt *dw_wdt)
540*4882a593Smuzhiyun {
541*4882a593Smuzhiyun 	debugfs_remove_recursive(dw_wdt->dbgfs_dir);
542*4882a593Smuzhiyun }
543*4882a593Smuzhiyun 
544*4882a593Smuzhiyun #else /* !CONFIG_DEBUG_FS */
545*4882a593Smuzhiyun 
dw_wdt_dbgfs_init(struct dw_wdt * dw_wdt)546*4882a593Smuzhiyun static void dw_wdt_dbgfs_init(struct dw_wdt *dw_wdt) {}
dw_wdt_dbgfs_clear(struct dw_wdt * dw_wdt)547*4882a593Smuzhiyun static void dw_wdt_dbgfs_clear(struct dw_wdt *dw_wdt) {}
548*4882a593Smuzhiyun 
549*4882a593Smuzhiyun #endif /* !CONFIG_DEBUG_FS */
550*4882a593Smuzhiyun 
dw_wdt_drv_probe(struct platform_device * pdev)551*4882a593Smuzhiyun static int dw_wdt_drv_probe(struct platform_device *pdev)
552*4882a593Smuzhiyun {
553*4882a593Smuzhiyun 	struct device *dev = &pdev->dev;
554*4882a593Smuzhiyun 	struct watchdog_device *wdd;
555*4882a593Smuzhiyun 	struct dw_wdt *dw_wdt;
556*4882a593Smuzhiyun 	int ret;
557*4882a593Smuzhiyun 
558*4882a593Smuzhiyun 	dw_wdt = devm_kzalloc(dev, sizeof(*dw_wdt), GFP_KERNEL);
559*4882a593Smuzhiyun 	if (!dw_wdt)
560*4882a593Smuzhiyun 		return -ENOMEM;
561*4882a593Smuzhiyun 
562*4882a593Smuzhiyun 	dw_wdt->regs = devm_platform_ioremap_resource(pdev, 0);
563*4882a593Smuzhiyun 	if (IS_ERR(dw_wdt->regs))
564*4882a593Smuzhiyun 		return PTR_ERR(dw_wdt->regs);
565*4882a593Smuzhiyun 
566*4882a593Smuzhiyun 	/*
567*4882a593Smuzhiyun 	 * Try to request the watchdog dedicated timer clock source. It must
568*4882a593Smuzhiyun 	 * be supplied if asynchronous mode is enabled. Otherwise fallback
569*4882a593Smuzhiyun 	 * to the common timer/bus clocks configuration, in which the very
570*4882a593Smuzhiyun 	 * first found clock supply both timer and APB signals.
571*4882a593Smuzhiyun 	 */
572*4882a593Smuzhiyun 	dw_wdt->clk = devm_clk_get(dev, "tclk");
573*4882a593Smuzhiyun 	if (IS_ERR(dw_wdt->clk)) {
574*4882a593Smuzhiyun 		dw_wdt->clk = devm_clk_get(dev, NULL);
575*4882a593Smuzhiyun 		if (IS_ERR(dw_wdt->clk))
576*4882a593Smuzhiyun 			return PTR_ERR(dw_wdt->clk);
577*4882a593Smuzhiyun 	}
578*4882a593Smuzhiyun 
579*4882a593Smuzhiyun 	ret = clk_prepare_enable(dw_wdt->clk);
580*4882a593Smuzhiyun 	if (ret)
581*4882a593Smuzhiyun 		return ret;
582*4882a593Smuzhiyun 
583*4882a593Smuzhiyun 	dw_wdt->rate = clk_get_rate(dw_wdt->clk);
584*4882a593Smuzhiyun 	if (dw_wdt->rate == 0) {
585*4882a593Smuzhiyun 		ret = -EINVAL;
586*4882a593Smuzhiyun 		goto out_disable_clk;
587*4882a593Smuzhiyun 	}
588*4882a593Smuzhiyun 
589*4882a593Smuzhiyun 	/*
590*4882a593Smuzhiyun 	 * Request APB clock if device is configured with async clocks mode.
591*4882a593Smuzhiyun 	 * In this case both tclk and pclk clocks are supposed to be specified.
592*4882a593Smuzhiyun 	 * Alas we can't know for sure whether async mode was really activated,
593*4882a593Smuzhiyun 	 * so the pclk phandle reference is left optional. If it couldn't be
594*4882a593Smuzhiyun 	 * found we consider the device configured in synchronous clocks mode.
595*4882a593Smuzhiyun 	 */
596*4882a593Smuzhiyun 	dw_wdt->pclk = devm_clk_get_optional(dev, "pclk");
597*4882a593Smuzhiyun 	if (IS_ERR(dw_wdt->pclk)) {
598*4882a593Smuzhiyun 		ret = PTR_ERR(dw_wdt->pclk);
599*4882a593Smuzhiyun 		goto out_disable_clk;
600*4882a593Smuzhiyun 	}
601*4882a593Smuzhiyun 
602*4882a593Smuzhiyun 	ret = clk_prepare_enable(dw_wdt->pclk);
603*4882a593Smuzhiyun 	if (ret)
604*4882a593Smuzhiyun 		goto out_disable_clk;
605*4882a593Smuzhiyun 
606*4882a593Smuzhiyun 	dw_wdt->rst = devm_reset_control_get_optional_shared(&pdev->dev, NULL);
607*4882a593Smuzhiyun 	if (IS_ERR(dw_wdt->rst)) {
608*4882a593Smuzhiyun 		ret = PTR_ERR(dw_wdt->rst);
609*4882a593Smuzhiyun 		goto out_disable_pclk;
610*4882a593Smuzhiyun 	}
611*4882a593Smuzhiyun 
612*4882a593Smuzhiyun 	/* Enable normal reset without pre-timeout by default. */
613*4882a593Smuzhiyun 	dw_wdt_update_mode(dw_wdt, DW_WDT_RMOD_RESET);
614*4882a593Smuzhiyun 
615*4882a593Smuzhiyun 	/*
616*4882a593Smuzhiyun 	 * Pre-timeout IRQ is optional, since some hardware may lack support
617*4882a593Smuzhiyun 	 * of it. Note we must request rising-edge IRQ, since the lane is left
618*4882a593Smuzhiyun 	 * pending either until the next watchdog kick event or up to the
619*4882a593Smuzhiyun 	 * system reset.
620*4882a593Smuzhiyun 	 */
621*4882a593Smuzhiyun 	ret = platform_get_irq_optional(pdev, 0);
622*4882a593Smuzhiyun 	if (ret > 0) {
623*4882a593Smuzhiyun 		ret = devm_request_irq(dev, ret, dw_wdt_irq,
624*4882a593Smuzhiyun 				       IRQF_SHARED | IRQF_TRIGGER_RISING,
625*4882a593Smuzhiyun 				       pdev->name, dw_wdt);
626*4882a593Smuzhiyun 		if (ret)
627*4882a593Smuzhiyun 			goto out_disable_pclk;
628*4882a593Smuzhiyun 
629*4882a593Smuzhiyun 		dw_wdt->wdd.info = &dw_wdt_pt_ident;
630*4882a593Smuzhiyun 	} else {
631*4882a593Smuzhiyun 		if (ret == -EPROBE_DEFER)
632*4882a593Smuzhiyun 			goto out_disable_pclk;
633*4882a593Smuzhiyun 
634*4882a593Smuzhiyun 		dw_wdt->wdd.info = &dw_wdt_ident;
635*4882a593Smuzhiyun 	}
636*4882a593Smuzhiyun 
637*4882a593Smuzhiyun 	reset_control_deassert(dw_wdt->rst);
638*4882a593Smuzhiyun 
639*4882a593Smuzhiyun 	ret = dw_wdt_init_timeouts(dw_wdt, dev);
640*4882a593Smuzhiyun 	if (ret)
641*4882a593Smuzhiyun 		goto out_disable_clk;
642*4882a593Smuzhiyun 
643*4882a593Smuzhiyun 	wdd = &dw_wdt->wdd;
644*4882a593Smuzhiyun 	wdd->ops = &dw_wdt_ops;
645*4882a593Smuzhiyun 	wdd->min_timeout = dw_wdt_get_min_timeout(dw_wdt);
646*4882a593Smuzhiyun 	wdd->max_hw_heartbeat_ms = dw_wdt_get_max_timeout_ms(dw_wdt);
647*4882a593Smuzhiyun 	wdd->parent = dev;
648*4882a593Smuzhiyun 
649*4882a593Smuzhiyun 	watchdog_set_drvdata(wdd, dw_wdt);
650*4882a593Smuzhiyun 	watchdog_set_nowayout(wdd, nowayout);
651*4882a593Smuzhiyun 	watchdog_init_timeout(wdd, 0, dev);
652*4882a593Smuzhiyun 
653*4882a593Smuzhiyun 	/*
654*4882a593Smuzhiyun 	 * If the watchdog is already running, use its already configured
655*4882a593Smuzhiyun 	 * timeout. Otherwise use the default or the value provided through
656*4882a593Smuzhiyun 	 * devicetree.
657*4882a593Smuzhiyun 	 */
658*4882a593Smuzhiyun 	if (dw_wdt_is_enabled(dw_wdt)) {
659*4882a593Smuzhiyun 		wdd->timeout = dw_wdt_get_timeout(dw_wdt);
660*4882a593Smuzhiyun 		set_bit(WDOG_HW_RUNNING, &wdd->status);
661*4882a593Smuzhiyun 	} else {
662*4882a593Smuzhiyun 		wdd->timeout = DW_WDT_DEFAULT_SECONDS;
663*4882a593Smuzhiyun 		watchdog_init_timeout(wdd, 0, dev);
664*4882a593Smuzhiyun 	}
665*4882a593Smuzhiyun 
666*4882a593Smuzhiyun 	platform_set_drvdata(pdev, dw_wdt);
667*4882a593Smuzhiyun 
668*4882a593Smuzhiyun 	watchdog_set_restart_priority(wdd, 128);
669*4882a593Smuzhiyun 
670*4882a593Smuzhiyun 	ret = watchdog_register_device(wdd);
671*4882a593Smuzhiyun 	if (ret)
672*4882a593Smuzhiyun 		goto out_disable_pclk;
673*4882a593Smuzhiyun 
674*4882a593Smuzhiyun 	dw_wdt_dbgfs_init(dw_wdt);
675*4882a593Smuzhiyun 
676*4882a593Smuzhiyun 	return 0;
677*4882a593Smuzhiyun 
678*4882a593Smuzhiyun out_disable_pclk:
679*4882a593Smuzhiyun 	clk_disable_unprepare(dw_wdt->pclk);
680*4882a593Smuzhiyun 
681*4882a593Smuzhiyun out_disable_clk:
682*4882a593Smuzhiyun 	clk_disable_unprepare(dw_wdt->clk);
683*4882a593Smuzhiyun 	return ret;
684*4882a593Smuzhiyun }
685*4882a593Smuzhiyun 
dw_wdt_drv_remove(struct platform_device * pdev)686*4882a593Smuzhiyun static int dw_wdt_drv_remove(struct platform_device *pdev)
687*4882a593Smuzhiyun {
688*4882a593Smuzhiyun 	struct dw_wdt *dw_wdt = platform_get_drvdata(pdev);
689*4882a593Smuzhiyun 
690*4882a593Smuzhiyun 	dw_wdt_dbgfs_clear(dw_wdt);
691*4882a593Smuzhiyun 
692*4882a593Smuzhiyun 	watchdog_unregister_device(&dw_wdt->wdd);
693*4882a593Smuzhiyun 	reset_control_assert(dw_wdt->rst);
694*4882a593Smuzhiyun 	clk_disable_unprepare(dw_wdt->pclk);
695*4882a593Smuzhiyun 	clk_disable_unprepare(dw_wdt->clk);
696*4882a593Smuzhiyun 
697*4882a593Smuzhiyun 	return 0;
698*4882a593Smuzhiyun }
699*4882a593Smuzhiyun 
700*4882a593Smuzhiyun #ifdef CONFIG_OF
701*4882a593Smuzhiyun static const struct of_device_id dw_wdt_of_match[] = {
702*4882a593Smuzhiyun 	{ .compatible = "snps,dw-wdt", },
703*4882a593Smuzhiyun 	{ /* sentinel */ }
704*4882a593Smuzhiyun };
705*4882a593Smuzhiyun MODULE_DEVICE_TABLE(of, dw_wdt_of_match);
706*4882a593Smuzhiyun #endif
707*4882a593Smuzhiyun 
708*4882a593Smuzhiyun static struct platform_driver dw_wdt_driver = {
709*4882a593Smuzhiyun 	.probe		= dw_wdt_drv_probe,
710*4882a593Smuzhiyun 	.remove		= dw_wdt_drv_remove,
711*4882a593Smuzhiyun 	.driver		= {
712*4882a593Smuzhiyun 		.name	= "dw_wdt",
713*4882a593Smuzhiyun 		.of_match_table = of_match_ptr(dw_wdt_of_match),
714*4882a593Smuzhiyun 		.pm	= &dw_wdt_pm_ops,
715*4882a593Smuzhiyun 	},
716*4882a593Smuzhiyun };
717*4882a593Smuzhiyun 
718*4882a593Smuzhiyun module_platform_driver(dw_wdt_driver);
719*4882a593Smuzhiyun 
720*4882a593Smuzhiyun MODULE_AUTHOR("Jamie Iles");
721*4882a593Smuzhiyun MODULE_DESCRIPTION("Synopsys DesignWare Watchdog Driver");
722*4882a593Smuzhiyun MODULE_LICENSE("GPL");
723