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