Merge tag 'for-5.19-rc7-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/kdave...
[linux-2.6-microblaze.git] / drivers / watchdog / sama5d4_wdt.c
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
272         if (of_device_is_compatible(dev->of_node, "microchip,sam9x60-wdt") ||
273             of_device_is_compatible(dev->of_node, "microchip,sama7g5-wdt"))
274                 wdt->sam9x60_support = true;
275
276         watchdog_set_drvdata(wdd, wdt);
277
278         regs = devm_platform_ioremap_resource(pdev, 0);
279         if (IS_ERR(regs))
280                 return PTR_ERR(regs);
281
282         wdt->reg_base = regs;
283
284         ret = of_sama5d4_wdt_init(dev->of_node, wdt);
285         if (ret)
286                 return ret;
287
288         if (wdt->need_irq) {
289                 irq = irq_of_parse_and_map(dev->of_node, 0);
290                 if (!irq) {
291                         dev_warn(dev, "failed to get IRQ from DT\n");
292                         wdt->need_irq = false;
293                 }
294         }
295
296         if (wdt->need_irq) {
297                 ret = devm_request_irq(dev, irq, sama5d4_wdt_irq_handler,
298                                        IRQF_SHARED | IRQF_IRQPOLL |
299                                        IRQF_NO_SUSPEND, pdev->name, pdev);
300                 if (ret) {
301                         dev_err(dev, "cannot register interrupt handler\n");
302                         return ret;
303                 }
304         }
305
306         watchdog_init_timeout(wdd, wdt_timeout, dev);
307
308         ret = sama5d4_wdt_init(wdt);
309         if (ret)
310                 return ret;
311
312         watchdog_set_nowayout(wdd, nowayout);
313
314         watchdog_stop_on_unregister(wdd);
315         ret = devm_watchdog_register_device(dev, wdd);
316         if (ret)
317                 return ret;
318
319         platform_set_drvdata(pdev, wdt);
320
321         dev_info(dev, "initialized (timeout = %d sec, nowayout = %d)\n",
322                  wdd->timeout, nowayout);
323
324         return 0;
325 }
326
327 static const struct of_device_id sama5d4_wdt_of_match[] = {
328         {
329                 .compatible = "atmel,sama5d4-wdt",
330         },
331         {
332                 .compatible = "microchip,sam9x60-wdt",
333         },
334         {
335                 .compatible = "microchip,sama7g5-wdt",
336         },
337
338         { }
339 };
340 MODULE_DEVICE_TABLE(of, sama5d4_wdt_of_match);
341
342 #ifdef CONFIG_PM_SLEEP
343 static int sama5d4_wdt_suspend_late(struct device *dev)
344 {
345         struct sama5d4_wdt *wdt = dev_get_drvdata(dev);
346
347         if (watchdog_active(&wdt->wdd))
348                 sama5d4_wdt_stop(&wdt->wdd);
349
350         return 0;
351 }
352
353 static int sama5d4_wdt_resume_early(struct device *dev)
354 {
355         struct sama5d4_wdt *wdt = dev_get_drvdata(dev);
356
357         /*
358          * FIXME: writing MR also pings the watchdog which may not be desired.
359          * This should only be done when the registers are lost on suspend but
360          * there is no way to get this information right now.
361          */
362         sama5d4_wdt_init(wdt);
363
364         if (watchdog_active(&wdt->wdd))
365                 sama5d4_wdt_start(&wdt->wdd);
366
367         return 0;
368 }
369 #endif
370
371 static const struct dev_pm_ops sama5d4_wdt_pm_ops = {
372         SET_LATE_SYSTEM_SLEEP_PM_OPS(sama5d4_wdt_suspend_late,
373                         sama5d4_wdt_resume_early)
374 };
375
376 static struct platform_driver sama5d4_wdt_driver = {
377         .probe          = sama5d4_wdt_probe,
378         .driver         = {
379                 .name   = "sama5d4_wdt",
380                 .pm     = &sama5d4_wdt_pm_ops,
381                 .of_match_table = sama5d4_wdt_of_match,
382         }
383 };
384 module_platform_driver(sama5d4_wdt_driver);
385
386 MODULE_AUTHOR("Atmel Corporation");
387 MODULE_DESCRIPTION("Atmel SAMA5D4 Watchdog Timer driver");
388 MODULE_LICENSE("GPL v2");