1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Driver for Atmel SAMA5D4 Watchdog Timer
4  *
5  * Copyright (C) 2015-2019 Microchip Technology Inc. and its subsidiaries
6  */
7 
8 #include <linux/delay.h>
9 #include <linux/interrupt.h>
10 #include <linux/io.h>
11 #include <linux/kernel.h>
12 #include <linux/module.h>
13 #include <linux/of.h>
14 #include <linux/of_device.h>
15 #include <linux/of_irq.h>
16 #include <linux/platform_device.h>
17 #include <linux/reboot.h>
18 #include <linux/watchdog.h>
19 
20 #include "at91sam9_wdt.h"
21 
22 /* minimum and maximum watchdog timeout, in seconds */
23 #define MIN_WDT_TIMEOUT		1
24 #define MAX_WDT_TIMEOUT		16
25 #define WDT_DEFAULT_TIMEOUT	MAX_WDT_TIMEOUT
26 
27 #define WDT_SEC2TICKS(s)	((s) ? (((s) << 8) - 1) : 0)
28 
29 struct sama5d4_wdt {
30 	struct watchdog_device	wdd;
31 	void __iomem		*reg_base;
32 	u32			mr;
33 	u32			ir;
34 	unsigned long		last_ping;
35 	bool			need_irq;
36 	bool			sam9x60_support;
37 };
38 
39 static int wdt_timeout;
40 static bool nowayout = WATCHDOG_NOWAYOUT;
41 
42 module_param(wdt_timeout, int, 0);
43 MODULE_PARM_DESC(wdt_timeout,
44 	"Watchdog timeout in seconds. (default = "
45 	__MODULE_STRING(WDT_DEFAULT_TIMEOUT) ")");
46 
47 module_param(nowayout, bool, 0);
48 MODULE_PARM_DESC(nowayout,
49 	"Watchdog cannot be stopped once started (default="
50 	__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
51 
52 #define wdt_enabled (!(wdt->mr & AT91_WDT_WDDIS))
53 
54 #define wdt_read(wdt, field) \
55 	readl_relaxed((wdt)->reg_base + (field))
56 
57 /* 4 slow clock periods is 4/32768 = 122.07µs*/
58 #define WDT_DELAY	usecs_to_jiffies(123)
59 
60 static void wdt_write(struct sama5d4_wdt *wdt, u32 field, u32 val)
61 {
62 	/*
63 	 * WDT_CR and WDT_MR must not be modified within three slow clock
64 	 * periods following a restart of the watchdog performed by a write
65 	 * access in WDT_CR.
66 	 */
67 	while (time_before(jiffies, wdt->last_ping + WDT_DELAY))
68 		usleep_range(30, 125);
69 	writel_relaxed(val, wdt->reg_base + field);
70 	wdt->last_ping = jiffies;
71 }
72 
73 static void wdt_write_nosleep(struct sama5d4_wdt *wdt, u32 field, u32 val)
74 {
75 	if (time_before(jiffies, wdt->last_ping + WDT_DELAY))
76 		udelay(123);
77 	writel_relaxed(val, wdt->reg_base + field);
78 	wdt->last_ping = jiffies;
79 }
80 
81 static int sama5d4_wdt_start(struct watchdog_device *wdd)
82 {
83 	struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd);
84 
85 	if (wdt->sam9x60_support) {
86 		writel_relaxed(wdt->ir, wdt->reg_base + AT91_SAM9X60_IER);
87 		wdt->mr &= ~AT91_SAM9X60_WDDIS;
88 	} else {
89 		wdt->mr &= ~AT91_WDT_WDDIS;
90 	}
91 	wdt_write(wdt, AT91_WDT_MR, wdt->mr);
92 
93 	return 0;
94 }
95 
96 static int sama5d4_wdt_stop(struct watchdog_device *wdd)
97 {
98 	struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd);
99 
100 	if (wdt->sam9x60_support) {
101 		writel_relaxed(wdt->ir, wdt->reg_base + AT91_SAM9X60_IDR);
102 		wdt->mr |= AT91_SAM9X60_WDDIS;
103 	} else {
104 		wdt->mr |= AT91_WDT_WDDIS;
105 	}
106 	wdt_write(wdt, AT91_WDT_MR, wdt->mr);
107 
108 	return 0;
109 }
110 
111 static int sama5d4_wdt_ping(struct watchdog_device *wdd)
112 {
113 	struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd);
114 
115 	wdt_write(wdt, AT91_WDT_CR, AT91_WDT_KEY | AT91_WDT_WDRSTT);
116 
117 	return 0;
118 }
119 
120 static int sama5d4_wdt_set_timeout(struct watchdog_device *wdd,
121 				 unsigned int timeout)
122 {
123 	struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd);
124 	u32 value = WDT_SEC2TICKS(timeout);
125 
126 	if (wdt->sam9x60_support) {
127 		wdt_write(wdt, AT91_SAM9X60_WLR,
128 			  AT91_SAM9X60_SET_COUNTER(value));
129 
130 		wdd->timeout = timeout;
131 		return 0;
132 	}
133 
134 	wdt->mr &= ~AT91_WDT_WDV;
135 	wdt->mr |= AT91_WDT_SET_WDV(value);
136 
137 	/*
138 	 * WDDIS has to be 0 when updating WDD/WDV. The datasheet states: When
139 	 * setting the WDDIS bit, and while it is set, the fields WDV and WDD
140 	 * must not be modified.
141 	 * If the watchdog is enabled, then the timeout can be updated. Else,
142 	 * wait that the user enables it.
143 	 */
144 	if (wdt_enabled)
145 		wdt_write(wdt, AT91_WDT_MR, wdt->mr & ~AT91_WDT_WDDIS);
146 
147 	wdd->timeout = timeout;
148 
149 	return 0;
150 }
151 
152 static const struct watchdog_info sama5d4_wdt_info = {
153 	.options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING,
154 	.identity = "Atmel SAMA5D4 Watchdog",
155 };
156 
157 static const struct watchdog_ops sama5d4_wdt_ops = {
158 	.owner = THIS_MODULE,
159 	.start = sama5d4_wdt_start,
160 	.stop = sama5d4_wdt_stop,
161 	.ping = sama5d4_wdt_ping,
162 	.set_timeout = sama5d4_wdt_set_timeout,
163 };
164 
165 static irqreturn_t sama5d4_wdt_irq_handler(int irq, void *dev_id)
166 {
167 	struct sama5d4_wdt *wdt = platform_get_drvdata(dev_id);
168 	u32 reg;
169 
170 	if (wdt->sam9x60_support)
171 		reg = wdt_read(wdt, AT91_SAM9X60_ISR);
172 	else
173 		reg = wdt_read(wdt, AT91_WDT_SR);
174 
175 	if (reg) {
176 		pr_crit("Atmel Watchdog Software Reset\n");
177 		emergency_restart();
178 		pr_crit("Reboot didn't succeed\n");
179 	}
180 
181 	return IRQ_HANDLED;
182 }
183 
184 static int of_sama5d4_wdt_init(struct device_node *np, struct sama5d4_wdt *wdt)
185 {
186 	const char *tmp;
187 
188 	if (wdt->sam9x60_support)
189 		wdt->mr = AT91_SAM9X60_WDDIS;
190 	else
191 		wdt->mr = AT91_WDT_WDDIS;
192 
193 	if (!of_property_read_string(np, "atmel,watchdog-type", &tmp) &&
194 	    !strcmp(tmp, "software"))
195 		wdt->need_irq = true;
196 
197 	if (of_property_read_bool(np, "atmel,idle-halt"))
198 		wdt->mr |= AT91_WDT_WDIDLEHLT;
199 
200 	if (of_property_read_bool(np, "atmel,dbg-halt"))
201 		wdt->mr |= AT91_WDT_WDDBGHLT;
202 
203 	return 0;
204 }
205 
206 static int sama5d4_wdt_init(struct sama5d4_wdt *wdt)
207 {
208 	u32 reg, val;
209 
210 	val = WDT_SEC2TICKS(WDT_DEFAULT_TIMEOUT);
211 	/*
212 	 * When booting and resuming, the bootloader may have changed the
213 	 * watchdog configuration.
214 	 * If the watchdog is already running, we can safely update it.
215 	 * Else, we have to disable it properly.
216 	 */
217 	if (!wdt_enabled) {
218 		reg = wdt_read(wdt, AT91_WDT_MR);
219 		if (wdt->sam9x60_support && (!(reg & AT91_SAM9X60_WDDIS)))
220 			wdt_write_nosleep(wdt, AT91_WDT_MR,
221 					  reg | AT91_SAM9X60_WDDIS);
222 		else if (!wdt->sam9x60_support &&
223 			 (!(reg & AT91_WDT_WDDIS)))
224 			wdt_write_nosleep(wdt, AT91_WDT_MR,
225 					  reg | AT91_WDT_WDDIS);
226 	}
227 
228 	if (wdt->sam9x60_support) {
229 		if (wdt->need_irq)
230 			wdt->ir = AT91_SAM9X60_PERINT;
231 		else
232 			wdt->mr |= AT91_SAM9X60_PERIODRST;
233 
234 		wdt_write(wdt, AT91_SAM9X60_IER, wdt->ir);
235 		wdt_write(wdt, AT91_SAM9X60_WLR, AT91_SAM9X60_SET_COUNTER(val));
236 	} else {
237 		wdt->mr |= AT91_WDT_SET_WDD(WDT_SEC2TICKS(MAX_WDT_TIMEOUT));
238 		wdt->mr |= AT91_WDT_SET_WDV(val);
239 
240 		if (wdt->need_irq)
241 			wdt->mr |= AT91_WDT_WDFIEN;
242 		else
243 			wdt->mr |= AT91_WDT_WDRSTEN;
244 	}
245 
246 	wdt_write_nosleep(wdt, AT91_WDT_MR, wdt->mr);
247 
248 	return 0;
249 }
250 
251 static int sama5d4_wdt_probe(struct platform_device *pdev)
252 {
253 	struct device *dev = &pdev->dev;
254 	struct watchdog_device *wdd;
255 	struct sama5d4_wdt *wdt;
256 	void __iomem *regs;
257 	u32 irq = 0;
258 	int ret;
259 
260 	wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
261 	if (!wdt)
262 		return -ENOMEM;
263 
264 	wdd = &wdt->wdd;
265 	wdd->timeout = WDT_DEFAULT_TIMEOUT;
266 	wdd->info = &sama5d4_wdt_info;
267 	wdd->ops = &sama5d4_wdt_ops;
268 	wdd->min_timeout = MIN_WDT_TIMEOUT;
269 	wdd->max_timeout = MAX_WDT_TIMEOUT;
270 	wdt->last_ping = jiffies;
271 	wdt->sam9x60_support = of_device_is_compatible(dev->of_node,
272 						       "microchip,sam9x60-wdt");
273 
274 	watchdog_set_drvdata(wdd, wdt);
275 
276 	regs = devm_platform_ioremap_resource(pdev, 0);
277 	if (IS_ERR(regs))
278 		return PTR_ERR(regs);
279 
280 	wdt->reg_base = regs;
281 
282 	ret = of_sama5d4_wdt_init(dev->of_node, wdt);
283 	if (ret)
284 		return ret;
285 
286 	if (wdt->need_irq) {
287 		irq = irq_of_parse_and_map(dev->of_node, 0);
288 		if (!irq) {
289 			dev_warn(dev, "failed to get IRQ from DT\n");
290 			wdt->need_irq = false;
291 		}
292 	}
293 
294 	if (wdt->need_irq) {
295 		ret = devm_request_irq(dev, irq, sama5d4_wdt_irq_handler,
296 				       IRQF_SHARED | IRQF_IRQPOLL |
297 				       IRQF_NO_SUSPEND, pdev->name, pdev);
298 		if (ret) {
299 			dev_err(dev, "cannot register interrupt handler\n");
300 			return ret;
301 		}
302 	}
303 
304 	watchdog_init_timeout(wdd, wdt_timeout, dev);
305 
306 	ret = sama5d4_wdt_init(wdt);
307 	if (ret)
308 		return ret;
309 
310 	watchdog_set_nowayout(wdd, nowayout);
311 
312 	watchdog_stop_on_unregister(wdd);
313 	ret = devm_watchdog_register_device(dev, wdd);
314 	if (ret)
315 		return ret;
316 
317 	platform_set_drvdata(pdev, wdt);
318 
319 	dev_info(dev, "initialized (timeout = %d sec, nowayout = %d)\n",
320 		 wdd->timeout, nowayout);
321 
322 	return 0;
323 }
324 
325 static const struct of_device_id sama5d4_wdt_of_match[] = {
326 	{
327 		.compatible = "atmel,sama5d4-wdt",
328 	},
329 	{
330 		.compatible = "microchip,sam9x60-wdt",
331 	},
332 	{ }
333 };
334 MODULE_DEVICE_TABLE(of, sama5d4_wdt_of_match);
335 
336 #ifdef CONFIG_PM_SLEEP
337 static int sama5d4_wdt_suspend_late(struct device *dev)
338 {
339 	struct sama5d4_wdt *wdt = dev_get_drvdata(dev);
340 
341 	if (watchdog_active(&wdt->wdd))
342 		sama5d4_wdt_stop(&wdt->wdd);
343 
344 	return 0;
345 }
346 
347 static int sama5d4_wdt_resume_early(struct device *dev)
348 {
349 	struct sama5d4_wdt *wdt = dev_get_drvdata(dev);
350 
351 	/*
352 	 * FIXME: writing MR also pings the watchdog which may not be desired.
353 	 * This should only be done when the registers are lost on suspend but
354 	 * there is no way to get this information right now.
355 	 */
356 	sama5d4_wdt_init(wdt);
357 
358 	if (watchdog_active(&wdt->wdd))
359 		sama5d4_wdt_start(&wdt->wdd);
360 
361 	return 0;
362 }
363 #endif
364 
365 static const struct dev_pm_ops sama5d4_wdt_pm_ops = {
366 	SET_LATE_SYSTEM_SLEEP_PM_OPS(sama5d4_wdt_suspend_late,
367 			sama5d4_wdt_resume_early)
368 };
369 
370 static struct platform_driver sama5d4_wdt_driver = {
371 	.probe		= sama5d4_wdt_probe,
372 	.driver		= {
373 		.name	= "sama5d4_wdt",
374 		.pm	= &sama5d4_wdt_pm_ops,
375 		.of_match_table = sama5d4_wdt_of_match,
376 	}
377 };
378 module_platform_driver(sama5d4_wdt_driver);
379 
380 MODULE_AUTHOR("Atmel Corporation");
381 MODULE_DESCRIPTION("Atmel SAMA5D4 Watchdog Timer driver");
382 MODULE_LICENSE("GPL v2");
383