xref: /linux/drivers/watchdog/ma35d1_wdt.c (revision 67f8bc848ee31831336bd478e57d2f993551902e)
1*80141041SZi-Yu Chen // SPDX-License-Identifier: GPL-2.0-only
2*80141041SZi-Yu Chen /*
3*80141041SZi-Yu Chen  * Copyright (c) 2026 Nuvoton technology corporation.
4*80141041SZi-Yu Chen  *
5*80141041SZi-Yu Chen  * Author: Zi-Yu Chen <zychennvt@gmail.com>
6*80141041SZi-Yu Chen  */
7*80141041SZi-Yu Chen 
8*80141041SZi-Yu Chen #include <linux/bitfield.h>
9*80141041SZi-Yu Chen #include <linux/bits.h>
10*80141041SZi-Yu Chen #include <linux/clk.h>
11*80141041SZi-Yu Chen #include <linux/err.h>
12*80141041SZi-Yu Chen #include <linux/init.h>
13*80141041SZi-Yu Chen #include <linux/interrupt.h>
14*80141041SZi-Yu Chen #include <linux/io.h>
15*80141041SZi-Yu Chen #include <linux/iopoll.h>
16*80141041SZi-Yu Chen #include <linux/kernel.h>
17*80141041SZi-Yu Chen #include <linux/module.h>
18*80141041SZi-Yu Chen #include <linux/moduleparam.h>
19*80141041SZi-Yu Chen #include <linux/of.h>
20*80141041SZi-Yu Chen #include <linux/platform_device.h>
21*80141041SZi-Yu Chen #include <linux/pm_wakeirq.h>
22*80141041SZi-Yu Chen #include <linux/property.h>
23*80141041SZi-Yu Chen #include <linux/types.h>
24*80141041SZi-Yu Chen #include <linux/watchdog.h>
25*80141041SZi-Yu Chen 
26*80141041SZi-Yu Chen #define DRV_NAME		"ma35d1-wdt"
27*80141041SZi-Yu Chen 
28*80141041SZi-Yu Chen #define REG_WDT_CTL		0x00
29*80141041SZi-Yu Chen #define REG_WDT_RSTCNT	0x08
30*80141041SZi-Yu Chen 
31*80141041SZi-Yu Chen #define TOUTSEL			GENMASK(11, 8)
32*80141041SZi-Yu Chen #define WDTEN			BIT(7)
33*80141041SZi-Yu Chen #define INTEN			BIT(6)
34*80141041SZi-Yu Chen #define WKF				BIT(5)
35*80141041SZi-Yu Chen #define WKEN			BIT(4)
36*80141041SZi-Yu Chen #define IF				BIT(3)
37*80141041SZi-Yu Chen #define RSTF			BIT(2)
38*80141041SZi-Yu Chen #define RSTEN			BIT(1)
39*80141041SZi-Yu Chen #define SYNC			BIT(30)
40*80141041SZi-Yu Chen 
41*80141041SZi-Yu Chen #define WDT_DEFAULT_TIMEOUT		32
42*80141041SZi-Yu Chen #define RESET_COUNTER			0x00005AA5
43*80141041SZi-Yu Chen 
44*80141041SZi-Yu Chen static bool nowayout = WATCHDOG_NOWAYOUT;
45*80141041SZi-Yu Chen static unsigned int timeout;
46*80141041SZi-Yu Chen 
47*80141041SZi-Yu Chen struct ma35d1_wdt_dev {
48*80141041SZi-Yu Chen 	struct watchdog_device wdt_dev;
49*80141041SZi-Yu Chen 	spinlock_t lock;
50*80141041SZi-Yu Chen 	void __iomem *wdt_base;
51*80141041SZi-Yu Chen 	struct clk *clk;
52*80141041SZi-Yu Chen 	unsigned long clk_rate;
53*80141041SZi-Yu Chen 	int irq;
54*80141041SZi-Yu Chen };
55*80141041SZi-Yu Chen 
56*80141041SZi-Yu Chen static int ma35d1_wdt_wait_sync(struct ma35d1_wdt_dev *ma35d1_wdt)
57*80141041SZi-Yu Chen {
58*80141041SZi-Yu Chen 	unsigned int val;
59*80141041SZi-Yu Chen 
60*80141041SZi-Yu Chen 	return readl_relaxed_poll_timeout_atomic(ma35d1_wdt->wdt_base +
61*80141041SZi-Yu Chen 							 REG_WDT_CTL, val, !(val & SYNC), 10, 125);
62*80141041SZi-Yu Chen }
63*80141041SZi-Yu Chen 
64*80141041SZi-Yu Chen /**
65*80141041SZi-Yu Chen  * ma35d1_wdt_stop - Stop the watchdog.
66*80141041SZi-Yu Chen  *
67*80141041SZi-Yu Chen  * @wdt_dev: watchdog device
68*80141041SZi-Yu Chen  *
69*80141041SZi-Yu Chen  * Read the contents of the CTL register, clear the WDTEN bit
70*80141041SZi-Yu Chen  * in the register and set the access key for successful write.
71*80141041SZi-Yu Chen  *
72*80141041SZi-Yu Chen  * Return: 0 on success, negative error otherwise.
73*80141041SZi-Yu Chen  */
74*80141041SZi-Yu Chen static int ma35d1_wdt_stop(struct watchdog_device *wdt_dev)
75*80141041SZi-Yu Chen {
76*80141041SZi-Yu Chen 	struct ma35d1_wdt_dev *ma35d1_wdt = watchdog_get_drvdata(wdt_dev);
77*80141041SZi-Yu Chen 	unsigned int val;
78*80141041SZi-Yu Chen 	int ret;
79*80141041SZi-Yu Chen 
80*80141041SZi-Yu Chen 	guard(spinlock_irqsave)(&ma35d1_wdt->lock);
81*80141041SZi-Yu Chen 	val = readl_relaxed(ma35d1_wdt->wdt_base + REG_WDT_CTL);
82*80141041SZi-Yu Chen 	val &= ~WDTEN;
83*80141041SZi-Yu Chen 	writel_relaxed(val, ma35d1_wdt->wdt_base + REG_WDT_CTL);
84*80141041SZi-Yu Chen 	ret = ma35d1_wdt_wait_sync(ma35d1_wdt);
85*80141041SZi-Yu Chen 	if (ret) {
86*80141041SZi-Yu Chen 		dev_err(wdt_dev->parent, "Wait for WDTEN SYNC timeout!\n");
87*80141041SZi-Yu Chen 		return ret;
88*80141041SZi-Yu Chen 	}
89*80141041SZi-Yu Chen 	return 0;
90*80141041SZi-Yu Chen }
91*80141041SZi-Yu Chen 
92*80141041SZi-Yu Chen static int ma35d1_wdt_ping(struct watchdog_device *wdt_dev)
93*80141041SZi-Yu Chen {
94*80141041SZi-Yu Chen 	struct ma35d1_wdt_dev *ma35d1_wdt = watchdog_get_drvdata(wdt_dev);
95*80141041SZi-Yu Chen 
96*80141041SZi-Yu Chen 	writel_relaxed(RESET_COUNTER, ma35d1_wdt->wdt_base + REG_WDT_RSTCNT);
97*80141041SZi-Yu Chen 
98*80141041SZi-Yu Chen 	return 0;
99*80141041SZi-Yu Chen }
100*80141041SZi-Yu Chen 
101*80141041SZi-Yu Chen static int ma35d1_wdt_set_timeout(struct watchdog_device *wdt_dev,
102*80141041SZi-Yu Chen 				  unsigned int timeout)
103*80141041SZi-Yu Chen {
104*80141041SZi-Yu Chen 	struct ma35d1_wdt_dev *ma35d1_wdt = watchdog_get_drvdata(wdt_dev);
105*80141041SZi-Yu Chen 	unsigned long target_ticks;
106*80141041SZi-Yu Chen 	unsigned int val, i;
107*80141041SZi-Yu Chen 	static const uint8_t toutsel_shifts[] = { 4,  6,  8,  10, 12,
108*80141041SZi-Yu Chen 						  14, 16, 18, 20 };
109*80141041SZi-Yu Chen 
110*80141041SZi-Yu Chen 	if (timeout < (ma35d1_wdt->wdt_dev.max_hw_heartbeat_ms / 1000)) {
111*80141041SZi-Yu Chen 		target_ticks = (unsigned long)timeout * ma35d1_wdt->clk_rate;
112*80141041SZi-Yu Chen 
113*80141041SZi-Yu Chen 		for (i = 0; i < ARRAY_SIZE(toutsel_shifts); i++) {
114*80141041SZi-Yu Chen 			if ((1UL << toutsel_shifts[i]) >= target_ticks)
115*80141041SZi-Yu Chen 				break;
116*80141041SZi-Yu Chen 		}
117*80141041SZi-Yu Chen 		/* To avoid truncation errors (0 seconds) during division. */
118*80141041SZi-Yu Chen 		wdt_dev->timeout =
119*80141041SZi-Yu Chen 			(1UL << toutsel_shifts[i]) / ma35d1_wdt->clk_rate;
120*80141041SZi-Yu Chen 		if (wdt_dev->timeout == 0)
121*80141041SZi-Yu Chen 			wdt_dev->timeout = 1;
122*80141041SZi-Yu Chen 
123*80141041SZi-Yu Chen 	} else {
124*80141041SZi-Yu Chen 		i = ARRAY_SIZE(toutsel_shifts) - 1;
125*80141041SZi-Yu Chen 		wdt_dev->timeout = timeout;
126*80141041SZi-Yu Chen 	}
127*80141041SZi-Yu Chen 
128*80141041SZi-Yu Chen 	guard(spinlock_irqsave)(&ma35d1_wdt->lock);
129*80141041SZi-Yu Chen 	val = readl_relaxed(ma35d1_wdt->wdt_base + REG_WDT_CTL);
130*80141041SZi-Yu Chen 	val &= ~TOUTSEL;
131*80141041SZi-Yu Chen 	val |= FIELD_PREP(TOUTSEL, i);
132*80141041SZi-Yu Chen 	writel_relaxed(val, ma35d1_wdt->wdt_base + REG_WDT_CTL);
133*80141041SZi-Yu Chen 
134*80141041SZi-Yu Chen 	ma35d1_wdt_ping(wdt_dev);
135*80141041SZi-Yu Chen 	return 0;
136*80141041SZi-Yu Chen }
137*80141041SZi-Yu Chen 
138*80141041SZi-Yu Chen static int ma35d1_wdt_start(struct watchdog_device *wdt_dev)
139*80141041SZi-Yu Chen {
140*80141041SZi-Yu Chen 	struct ma35d1_wdt_dev *ma35d1_wdt = watchdog_get_drvdata(wdt_dev);
141*80141041SZi-Yu Chen 	void __iomem *wdt_base = ma35d1_wdt->wdt_base;
142*80141041SZi-Yu Chen 	unsigned int val;
143*80141041SZi-Yu Chen 	int ret;
144*80141041SZi-Yu Chen 
145*80141041SZi-Yu Chen 	ret = ma35d1_wdt_set_timeout(wdt_dev, wdt_dev->timeout);
146*80141041SZi-Yu Chen 	if (ret < 0)
147*80141041SZi-Yu Chen 		return ret;
148*80141041SZi-Yu Chen 
149*80141041SZi-Yu Chen 	guard(spinlock_irqsave)(&ma35d1_wdt->lock);
150*80141041SZi-Yu Chen 	val = readl_relaxed(wdt_base + REG_WDT_CTL);
151*80141041SZi-Yu Chen 	val |= (WDTEN | RSTEN);
152*80141041SZi-Yu Chen 
153*80141041SZi-Yu Chen 	writel_relaxed(val, wdt_base + REG_WDT_CTL);
154*80141041SZi-Yu Chen 	ret = ma35d1_wdt_wait_sync(ma35d1_wdt);
155*80141041SZi-Yu Chen 	if (ret) {
156*80141041SZi-Yu Chen 		dev_err(wdt_dev->parent, "Wait for WDTEN SYNC timeout!\n");
157*80141041SZi-Yu Chen 		return ret;
158*80141041SZi-Yu Chen 	}
159*80141041SZi-Yu Chen 
160*80141041SZi-Yu Chen 	writel_relaxed(RESET_COUNTER, wdt_base + REG_WDT_RSTCNT);
161*80141041SZi-Yu Chen 
162*80141041SZi-Yu Chen 	return 0;
163*80141041SZi-Yu Chen }
164*80141041SZi-Yu Chen 
165*80141041SZi-Yu Chen static const struct watchdog_info ma35d1_wdt_info = {
166*80141041SZi-Yu Chen 	.identity = DRV_NAME,
167*80141041SZi-Yu Chen 	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE |
168*80141041SZi-Yu Chen 		   WDIOF_CARDRESET,
169*80141041SZi-Yu Chen };
170*80141041SZi-Yu Chen 
171*80141041SZi-Yu Chen static const struct watchdog_ops ma35d1_wdt_ops = {
172*80141041SZi-Yu Chen 	.owner = THIS_MODULE,
173*80141041SZi-Yu Chen 	.start = ma35d1_wdt_start,
174*80141041SZi-Yu Chen 	.stop = ma35d1_wdt_stop,
175*80141041SZi-Yu Chen 	.ping = ma35d1_wdt_ping,
176*80141041SZi-Yu Chen 	.set_timeout = ma35d1_wdt_set_timeout,
177*80141041SZi-Yu Chen };
178*80141041SZi-Yu Chen 
179*80141041SZi-Yu Chen static irqreturn_t ma35d1_wdt_isr(int irq, void *dev_id)
180*80141041SZi-Yu Chen {
181*80141041SZi-Yu Chen 	struct ma35d1_wdt_dev *ma35d1_wdt = dev_id;
182*80141041SZi-Yu Chen 	unsigned int val;
183*80141041SZi-Yu Chen 
184*80141041SZi-Yu Chen 	/* Clear the flag if set */
185*80141041SZi-Yu Chen 	guard(spinlock)(&ma35d1_wdt->lock);
186*80141041SZi-Yu Chen 	val = readl_relaxed(ma35d1_wdt->wdt_base + REG_WDT_CTL);
187*80141041SZi-Yu Chen 	writel_relaxed(val, ma35d1_wdt->wdt_base + REG_WDT_CTL);
188*80141041SZi-Yu Chen 
189*80141041SZi-Yu Chen 	return IRQ_HANDLED;
190*80141041SZi-Yu Chen }
191*80141041SZi-Yu Chen 
192*80141041SZi-Yu Chen static int ma35d1_wdt_probe(struct platform_device *pdev)
193*80141041SZi-Yu Chen {
194*80141041SZi-Yu Chen 	struct device *dev = &pdev->dev;
195*80141041SZi-Yu Chen 	struct ma35d1_wdt_dev *ma35d1_wdt;
196*80141041SZi-Yu Chen 	unsigned long clk_rate, val;
197*80141041SZi-Yu Chen 	int ret;
198*80141041SZi-Yu Chen 
199*80141041SZi-Yu Chen 	ma35d1_wdt = devm_kzalloc(dev, sizeof(*ma35d1_wdt), GFP_KERNEL);
200*80141041SZi-Yu Chen 	if (!ma35d1_wdt)
201*80141041SZi-Yu Chen 		return -ENOMEM;
202*80141041SZi-Yu Chen 
203*80141041SZi-Yu Chen 	spin_lock_init(&ma35d1_wdt->lock);
204*80141041SZi-Yu Chen 	platform_set_drvdata(pdev, ma35d1_wdt);
205*80141041SZi-Yu Chen 
206*80141041SZi-Yu Chen 	ma35d1_wdt->wdt_base = devm_platform_ioremap_resource(pdev, 0);
207*80141041SZi-Yu Chen 	if (IS_ERR(ma35d1_wdt->wdt_base))
208*80141041SZi-Yu Chen 		return PTR_ERR(ma35d1_wdt->wdt_base);
209*80141041SZi-Yu Chen 
210*80141041SZi-Yu Chen 	ma35d1_wdt->clk = devm_clk_get_enabled(dev, NULL);
211*80141041SZi-Yu Chen 	if (IS_ERR(ma35d1_wdt->clk))
212*80141041SZi-Yu Chen 		return dev_err_probe(dev, PTR_ERR(ma35d1_wdt->clk),
213*80141041SZi-Yu Chen 				     "Can't get Watchdog clock\n");
214*80141041SZi-Yu Chen 
215*80141041SZi-Yu Chen 	clk_rate = clk_get_rate(ma35d1_wdt->clk);
216*80141041SZi-Yu Chen 	if (!clk_rate)
217*80141041SZi-Yu Chen 		return -EINVAL;
218*80141041SZi-Yu Chen 	ma35d1_wdt->clk_rate = clk_rate;
219*80141041SZi-Yu Chen 
220*80141041SZi-Yu Chen 	ma35d1_wdt->irq = platform_get_irq(pdev, 0);
221*80141041SZi-Yu Chen 	if (ma35d1_wdt->irq < 0)
222*80141041SZi-Yu Chen 		return dev_err_probe(dev, ma35d1_wdt->irq,
223*80141041SZi-Yu Chen 				     "failed to get irq\n");
224*80141041SZi-Yu Chen 
225*80141041SZi-Yu Chen 	ma35d1_wdt->wdt_dev.info = &ma35d1_wdt_info;
226*80141041SZi-Yu Chen 	ma35d1_wdt->wdt_dev.ops = &ma35d1_wdt_ops;
227*80141041SZi-Yu Chen 	ma35d1_wdt->wdt_dev.timeout = WDT_DEFAULT_TIMEOUT;
228*80141041SZi-Yu Chen 	ma35d1_wdt->wdt_dev.min_timeout = 1;
229*80141041SZi-Yu Chen 	ma35d1_wdt->wdt_dev.max_hw_heartbeat_ms = (1U << 20) * 1000 / clk_rate;
230*80141041SZi-Yu Chen 	ma35d1_wdt->wdt_dev.parent = dev;
231*80141041SZi-Yu Chen 
232*80141041SZi-Yu Chen 	val = readl_relaxed(ma35d1_wdt->wdt_base + REG_WDT_CTL);
233*80141041SZi-Yu Chen 	if (val & RSTF) {
234*80141041SZi-Yu Chen 		ma35d1_wdt->wdt_dev.bootstatus = WDIOF_CARDRESET;
235*80141041SZi-Yu Chen 		writel_relaxed(val, ma35d1_wdt->wdt_base + REG_WDT_CTL);
236*80141041SZi-Yu Chen 	}
237*80141041SZi-Yu Chen 
238*80141041SZi-Yu Chen 	if (val & WDTEN)
239*80141041SZi-Yu Chen 		set_bit(WDOG_HW_RUNNING, &ma35d1_wdt->wdt_dev.status);
240*80141041SZi-Yu Chen 
241*80141041SZi-Yu Chen 	watchdog_set_drvdata(&ma35d1_wdt->wdt_dev, ma35d1_wdt);
242*80141041SZi-Yu Chen 	watchdog_set_nowayout(&ma35d1_wdt->wdt_dev, nowayout);
243*80141041SZi-Yu Chen 	watchdog_init_timeout(&ma35d1_wdt->wdt_dev, timeout, &pdev->dev);
244*80141041SZi-Yu Chen 
245*80141041SZi-Yu Chen 	ma35d1_wdt_set_timeout(&ma35d1_wdt->wdt_dev,
246*80141041SZi-Yu Chen 			       ma35d1_wdt->wdt_dev.timeout);
247*80141041SZi-Yu Chen 
248*80141041SZi-Yu Chen 	ret = devm_request_irq(dev, ma35d1_wdt->irq, ma35d1_wdt_isr, 0,
249*80141041SZi-Yu Chen 			       dev_name(dev), ma35d1_wdt);
250*80141041SZi-Yu Chen 	if (ret)
251*80141041SZi-Yu Chen 		return dev_err_probe(dev, ret, "cannot claim IRQ %d\n",
252*80141041SZi-Yu Chen 				     ma35d1_wdt->irq);
253*80141041SZi-Yu Chen 
254*80141041SZi-Yu Chen 	if (device_property_read_bool(dev, "wakeup-source")) {
255*80141041SZi-Yu Chen 		ret = devm_device_init_wakeup(dev);
256*80141041SZi-Yu Chen 		if (ret)
257*80141041SZi-Yu Chen 			return ret;
258*80141041SZi-Yu Chen 
259*80141041SZi-Yu Chen 		ret = devm_pm_set_wake_irq(dev, ma35d1_wdt->irq);
260*80141041SZi-Yu Chen 		if (ret)
261*80141041SZi-Yu Chen 			return ret;
262*80141041SZi-Yu Chen 	}
263*80141041SZi-Yu Chen 
264*80141041SZi-Yu Chen 	watchdog_stop_on_reboot(&ma35d1_wdt->wdt_dev);
265*80141041SZi-Yu Chen 	watchdog_stop_on_unregister(&ma35d1_wdt->wdt_dev);
266*80141041SZi-Yu Chen 	ret = devm_watchdog_register_device(dev, &ma35d1_wdt->wdt_dev);
267*80141041SZi-Yu Chen 	if (ret)
268*80141041SZi-Yu Chen 		return ret;
269*80141041SZi-Yu Chen 
270*80141041SZi-Yu Chen 	return 0;
271*80141041SZi-Yu Chen }
272*80141041SZi-Yu Chen 
273*80141041SZi-Yu Chen static int ma35d1_wdt_suspend(struct device *dev)
274*80141041SZi-Yu Chen {
275*80141041SZi-Yu Chen 	struct ma35d1_wdt_dev *ma35d1_wdt = dev_get_drvdata(dev);
276*80141041SZi-Yu Chen 
277*80141041SZi-Yu Chen 	if (watchdog_active(&ma35d1_wdt->wdt_dev) ||
278*80141041SZi-Yu Chen 	    watchdog_hw_running(&ma35d1_wdt->wdt_dev)) {
279*80141041SZi-Yu Chen 		u32 val;
280*80141041SZi-Yu Chen 		int ret;
281*80141041SZi-Yu Chen 
282*80141041SZi-Yu Chen 		guard(spinlock_irqsave)(&ma35d1_wdt->lock);
283*80141041SZi-Yu Chen 		val = readl_relaxed(ma35d1_wdt->wdt_base + REG_WDT_CTL);
284*80141041SZi-Yu Chen 
285*80141041SZi-Yu Chen 		if (device_may_wakeup(dev)) {
286*80141041SZi-Yu Chen 			val &= ~RSTEN;
287*80141041SZi-Yu Chen 			val |= (INTEN | WKEN);
288*80141041SZi-Yu Chen 		} else {
289*80141041SZi-Yu Chen 			val &= ~(WDTEN | RSTEN);
290*80141041SZi-Yu Chen 		}
291*80141041SZi-Yu Chen 		writel_relaxed(val, ma35d1_wdt->wdt_base + REG_WDT_CTL);
292*80141041SZi-Yu Chen 		ret = ma35d1_wdt_wait_sync(ma35d1_wdt);
293*80141041SZi-Yu Chen 		if (ret) {
294*80141041SZi-Yu Chen 			dev_err(dev, "Wait for WDTEN SYNC timeout!\n");
295*80141041SZi-Yu Chen 			return ret;
296*80141041SZi-Yu Chen 		}
297*80141041SZi-Yu Chen 	}
298*80141041SZi-Yu Chen 
299*80141041SZi-Yu Chen 	return 0;
300*80141041SZi-Yu Chen }
301*80141041SZi-Yu Chen 
302*80141041SZi-Yu Chen static int ma35d1_wdt_resume(struct device *dev)
303*80141041SZi-Yu Chen {
304*80141041SZi-Yu Chen 	struct ma35d1_wdt_dev *ma35d1_wdt = dev_get_drvdata(dev);
305*80141041SZi-Yu Chen 
306*80141041SZi-Yu Chen 	if (watchdog_active(&ma35d1_wdt->wdt_dev) ||
307*80141041SZi-Yu Chen 	    watchdog_hw_running(&ma35d1_wdt->wdt_dev)) {
308*80141041SZi-Yu Chen 		u32 val;
309*80141041SZi-Yu Chen 		int ret;
310*80141041SZi-Yu Chen 
311*80141041SZi-Yu Chen 		guard(spinlock_irqsave)(&ma35d1_wdt->lock);
312*80141041SZi-Yu Chen 		val = readl_relaxed(ma35d1_wdt->wdt_base + REG_WDT_CTL);
313*80141041SZi-Yu Chen 
314*80141041SZi-Yu Chen 		if (device_may_wakeup(dev)) {
315*80141041SZi-Yu Chen 			val |= RSTEN;
316*80141041SZi-Yu Chen 			val &= ~(INTEN | WKEN);
317*80141041SZi-Yu Chen 		} else {
318*80141041SZi-Yu Chen 			val |= (WDTEN | RSTEN);
319*80141041SZi-Yu Chen 		}
320*80141041SZi-Yu Chen 		writel_relaxed(val, ma35d1_wdt->wdt_base + REG_WDT_CTL);
321*80141041SZi-Yu Chen 		writel_relaxed(RESET_COUNTER,
322*80141041SZi-Yu Chen 			       ma35d1_wdt->wdt_base + REG_WDT_RSTCNT);
323*80141041SZi-Yu Chen 		ret = ma35d1_wdt_wait_sync(ma35d1_wdt);
324*80141041SZi-Yu Chen 		if (ret) {
325*80141041SZi-Yu Chen 			dev_err(dev, "Wait for WDTEN SYNC timeout!\n");
326*80141041SZi-Yu Chen 			return ret;
327*80141041SZi-Yu Chen 		}
328*80141041SZi-Yu Chen 	}
329*80141041SZi-Yu Chen 	return 0;
330*80141041SZi-Yu Chen }
331*80141041SZi-Yu Chen 
332*80141041SZi-Yu Chen static DEFINE_SIMPLE_DEV_PM_OPS(ma35d1_wdt_pm_ops, ma35d1_wdt_suspend,
333*80141041SZi-Yu Chen 				ma35d1_wdt_resume);
334*80141041SZi-Yu Chen 
335*80141041SZi-Yu Chen static const struct of_device_id ma35d1_wdt_dt_ids[] = {
336*80141041SZi-Yu Chen 	{ .compatible = "nuvoton,ma35d1-wdt" },
337*80141041SZi-Yu Chen 	{ /* sentinel */ },
338*80141041SZi-Yu Chen };
339*80141041SZi-Yu Chen MODULE_DEVICE_TABLE(of, ma35d1_wdt_dt_ids);
340*80141041SZi-Yu Chen 
341*80141041SZi-Yu Chen static struct platform_driver ma35d1_wdt_driver = {
342*80141041SZi-Yu Chen 	.probe		= ma35d1_wdt_probe,
343*80141041SZi-Yu Chen 	.driver		= {
344*80141041SZi-Yu Chen 		.name	= DRV_NAME,
345*80141041SZi-Yu Chen 		.pm	= pm_ptr(&ma35d1_wdt_pm_ops),
346*80141041SZi-Yu Chen 		.of_match_table = ma35d1_wdt_dt_ids,
347*80141041SZi-Yu Chen 	},
348*80141041SZi-Yu Chen };
349*80141041SZi-Yu Chen 
350*80141041SZi-Yu Chen module_platform_driver(ma35d1_wdt_driver);
351*80141041SZi-Yu Chen 
352*80141041SZi-Yu Chen module_param(timeout, uint, 0);
353*80141041SZi-Yu Chen MODULE_PARM_DESC(timeout, "Watchdog heartbeat in seconds");
354*80141041SZi-Yu Chen 
355*80141041SZi-Yu Chen module_param(nowayout, bool, 0);
356*80141041SZi-Yu Chen MODULE_PARM_DESC(
357*80141041SZi-Yu Chen 	nowayout,
358*80141041SZi-Yu Chen 	"Watchdog cannot be stopped once started (default=" __MODULE_STRING(
359*80141041SZi-Yu Chen 		WATCHDOG_NOWAYOUT) ")");
360*80141041SZi-Yu Chen 
361*80141041SZi-Yu Chen MODULE_AUTHOR("Zi-Yu Chen <zychennvt@gmail.com>");
362*80141041SZi-Yu Chen MODULE_DESCRIPTION("Nuvoton MA35D1 Watchdog Timer Driver");
363*80141041SZi-Yu Chen MODULE_LICENSE("GPL");
364