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