Skip to content

Commit bd3f54e

Browse files
dlezcanoWim Van Sebroeck
authored andcommitted
watchdog: Add the Watchdog Timer for the NXP S32 platform
The S32 platform has several Watchdog Timer available and tied with a CPU. The SWT0 is the only one which directly asserts the reset line, other SWT require an external setup to configure the reset behavior which is not part of this change. As a side note, in the NXP documentation, the s32g2 and s32g3 reference manuals refer the watchdog as the 'Software Timer Watchdog' where the name can be misleading as it is actually a hardware watchdog. The maximum watchdog timeout value depends on the clock feeding the SWT counter which is 32bits wide. On the s32g274-rb2, the clock has a rate of 51MHz which result on 83 seconds maximum timeout. The timeout can be specified via the device tree with the usual existing bindings 'timeout-sec' or via the module param timeout. The watchdog can be loaded with the 'nowayout' option, preventing the watchdog to be stopped. The watchdog can be started at boot time with the 'early-enable' option, thus letting the watchdog framework to service the watchdog counter. The watchdog support the magic character to stop when the userspace releases the device. Cc: Ghennadi Procopciuc <[email protected]> Cc: Thomas Fossati <[email protected]> Signed-off-by: Daniel Lezcano <[email protected]> Tested-by: Alexandru-Catalin Ionita <[email protected]> Reviewed-by: Guenter Roeck <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Guenter Roeck <[email protected]> Signed-off-by: Wim Van Sebroeck <[email protected]>
1 parent 45421ff commit bd3f54e

File tree

3 files changed

+325
-0
lines changed

3 files changed

+325
-0
lines changed

drivers/watchdog/Kconfig

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -804,6 +804,15 @@ config IMX7ULP_WDT
804804
To compile this driver as a module, choose M here: the
805805
module will be called imx7ulp_wdt.
806806

807+
config S32G_WDT
808+
tristate "S32G Watchdog"
809+
depends on ARCH_S32 || COMPILE_TEST
810+
select WATCHDOG_CORE
811+
help
812+
This is the driver for the hardware watchdog on the NXP
813+
S32G platforms. If you wish to have watchdog support
814+
enabled, say Y, otherwise say N.
815+
807816
config DB500_WATCHDOG
808817
tristate "ST-Ericsson DB800 watchdog"
809818
depends on MFD_DB8500_PRCMU

drivers/watchdog/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ obj-$(CONFIG_TS72XX_WATCHDOG) += ts72xx_wdt.o
6969
obj-$(CONFIG_IMX2_WDT) += imx2_wdt.o
7070
obj-$(CONFIG_IMX_SC_WDT) += imx_sc_wdt.o
7171
obj-$(CONFIG_IMX7ULP_WDT) += imx7ulp_wdt.o
72+
obj-$(CONFIG_S32G_WDT) += s32g_wdt.o
7273
obj-$(CONFIG_DB500_WATCHDOG) += db8500_wdt.o
7374
obj-$(CONFIG_RETU_WATCHDOG) += retu_wdt.o
7475
obj-$(CONFIG_BCM2835_WDT) += bcm2835_wdt.o

drivers/watchdog/s32g_wdt.c

Lines changed: 315 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,315 @@
1+
// SPDX-License-Identifier: GPL-2.0-or-later
2+
/*
3+
* Watchdog driver for S32G SoC
4+
*
5+
* Copyright 2017-2019, 2021-2025 NXP.
6+
*
7+
*/
8+
#include <linux/clk.h>
9+
#include <linux/debugfs.h>
10+
#include <linux/io.h>
11+
#include <linux/kernel.h>
12+
#include <linux/module.h>
13+
#include <linux/moduleparam.h>
14+
#include <linux/of.h>
15+
#include <linux/platform_device.h>
16+
#include <linux/watchdog.h>
17+
18+
#define DRIVER_NAME "s32g-swt"
19+
20+
#define S32G_SWT_CR(__base) ((__base) + 0x00) /* Control Register offset */
21+
#define S32G_SWT_CR_SM (BIT(9) | BIT(10)) /* -> Service Mode */
22+
#define S32G_SWT_CR_STP BIT(2) /* -> Stop Mode Control */
23+
#define S32G_SWT_CR_FRZ BIT(1) /* -> Debug Mode Control */
24+
#define S32G_SWT_CR_WEN BIT(0) /* -> Watchdog Enable */
25+
26+
#define S32G_SWT_TO(__base) ((__base) + 0x08) /* Timeout Register offset */
27+
28+
#define S32G_SWT_SR(__base) ((__base) + 0x10) /* Service Register offset */
29+
#define S32G_WDT_SEQ1 0xA602 /* -> service sequence number 1 */
30+
#define S32G_WDT_SEQ2 0xB480 /* -> service sequence number 2 */
31+
32+
#define S32G_SWT_CO(__base) ((__base) + 0x14) /* Counter output register */
33+
34+
#define S32G_WDT_DEFAULT_TIMEOUT 30
35+
36+
struct s32g_wdt_device {
37+
int rate;
38+
void __iomem *base;
39+
struct watchdog_device wdog;
40+
};
41+
42+
static bool nowayout = WATCHDOG_NOWAYOUT;
43+
module_param(nowayout, bool, 0);
44+
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
45+
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
46+
47+
static unsigned int timeout_param = S32G_WDT_DEFAULT_TIMEOUT;
48+
module_param(timeout_param, uint, 0);
49+
MODULE_PARM_DESC(timeout_param, "Watchdog timeout in seconds (default="
50+
__MODULE_STRING(S32G_WDT_DEFAULT_TIMEOUT) ")");
51+
52+
static bool early_enable;
53+
module_param(early_enable, bool, 0);
54+
MODULE_PARM_DESC(early_enable,
55+
"Watchdog is started on module insertion (default=false)");
56+
57+
static const struct watchdog_info s32g_wdt_info = {
58+
.identity = "s32g watchdog",
59+
.options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE |
60+
WDIOC_GETTIMEOUT | WDIOC_GETTIMELEFT,
61+
};
62+
63+
static struct s32g_wdt_device *wdd_to_s32g_wdt(struct watchdog_device *wdd)
64+
{
65+
return container_of(wdd, struct s32g_wdt_device, wdog);
66+
}
67+
68+
static unsigned int wdog_sec_to_count(struct s32g_wdt_device *wdev, unsigned int timeout)
69+
{
70+
return wdev->rate * timeout;
71+
}
72+
73+
static int s32g_wdt_ping(struct watchdog_device *wdog)
74+
{
75+
struct s32g_wdt_device *wdev = wdd_to_s32g_wdt(wdog);
76+
77+
writel(S32G_WDT_SEQ1, S32G_SWT_SR(wdev->base));
78+
writel(S32G_WDT_SEQ2, S32G_SWT_SR(wdev->base));
79+
80+
return 0;
81+
}
82+
83+
static int s32g_wdt_start(struct watchdog_device *wdog)
84+
{
85+
struct s32g_wdt_device *wdev = wdd_to_s32g_wdt(wdog);
86+
unsigned long val;
87+
88+
val = readl(S32G_SWT_CR(wdev->base));
89+
90+
val |= S32G_SWT_CR_WEN;
91+
92+
writel(val, S32G_SWT_CR(wdev->base));
93+
94+
return 0;
95+
}
96+
97+
static int s32g_wdt_stop(struct watchdog_device *wdog)
98+
{
99+
struct s32g_wdt_device *wdev = wdd_to_s32g_wdt(wdog);
100+
unsigned long val;
101+
102+
val = readl(S32G_SWT_CR(wdev->base));
103+
104+
val &= ~S32G_SWT_CR_WEN;
105+
106+
writel(val, S32G_SWT_CR(wdev->base));
107+
108+
return 0;
109+
}
110+
111+
static int s32g_wdt_set_timeout(struct watchdog_device *wdog, unsigned int timeout)
112+
{
113+
struct s32g_wdt_device *wdev = wdd_to_s32g_wdt(wdog);
114+
115+
writel(wdog_sec_to_count(wdev, timeout), S32G_SWT_TO(wdev->base));
116+
117+
wdog->timeout = timeout;
118+
119+
/*
120+
* Conforming to the documentation, the timeout counter is
121+
* loaded when servicing is operated (aka ping) or when the
122+
* counter is enabled. In case the watchdog is already started
123+
* it must be stopped and started again to update the timeout
124+
* register or a ping can be sent to refresh the counter. Here
125+
* we choose to send a ping to the watchdog which is harmless
126+
* if the watchdog is stopped.
127+
*/
128+
return s32g_wdt_ping(wdog);
129+
}
130+
131+
static unsigned int s32g_wdt_get_timeleft(struct watchdog_device *wdog)
132+
{
133+
struct s32g_wdt_device *wdev = wdd_to_s32g_wdt(wdog);
134+
unsigned long counter;
135+
bool is_running;
136+
137+
/*
138+
* The counter output can be read only if the SWT is
139+
* disabled. Given the latency between the internal counter
140+
* and the counter output update, there can be very small
141+
* difference. However, we can accept this matter of fact
142+
* given the resolution is a second based unit for the output.
143+
*/
144+
is_running = watchdog_hw_running(wdog);
145+
146+
if (is_running)
147+
s32g_wdt_stop(wdog);
148+
149+
counter = readl(S32G_SWT_CO(wdev->base));
150+
151+
if (is_running)
152+
s32g_wdt_start(wdog);
153+
154+
return counter / wdev->rate;
155+
}
156+
157+
static const struct watchdog_ops s32g_wdt_ops = {
158+
.owner = THIS_MODULE,
159+
.start = s32g_wdt_start,
160+
.stop = s32g_wdt_stop,
161+
.ping = s32g_wdt_ping,
162+
.set_timeout = s32g_wdt_set_timeout,
163+
.get_timeleft = s32g_wdt_get_timeleft,
164+
};
165+
166+
static void s32g_wdt_init(struct s32g_wdt_device *wdev)
167+
{
168+
unsigned long val;
169+
170+
/* Set the watchdog's Time-Out value */
171+
val = wdog_sec_to_count(wdev, wdev->wdog.timeout);
172+
173+
writel(val, S32G_SWT_TO(wdev->base));
174+
175+
/*
176+
* Get the control register content. We are at init time, the
177+
* watchdog should not be started.
178+
*/
179+
val = readl(S32G_SWT_CR(wdev->base));
180+
181+
/*
182+
* We want to allow the watchdog timer to be stopped when
183+
* device enters debug mode.
184+
*/
185+
val |= S32G_SWT_CR_FRZ;
186+
187+
/*
188+
* However, when the CPU is in WFI or suspend mode, the
189+
* watchdog must continue. The documentation refers it as the
190+
* stopped mode.
191+
*/
192+
val &= ~S32G_SWT_CR_STP;
193+
194+
/*
195+
* Use Fixed Service Sequence to ping the watchdog which is
196+
* 0x00 configuration value for the service mode. It should be
197+
* already set because it is the default value but we reset it
198+
* in case.
199+
*/
200+
val &= ~S32G_SWT_CR_SM;
201+
202+
writel(val, S32G_SWT_CR(wdev->base));
203+
204+
/*
205+
* When the 'early_enable' option is set, we start the
206+
* watchdog from the kernel.
207+
*/
208+
if (early_enable) {
209+
s32g_wdt_start(&wdev->wdog);
210+
set_bit(WDOG_HW_RUNNING, &wdev->wdog.status);
211+
}
212+
}
213+
214+
static int s32g_wdt_probe(struct platform_device *pdev)
215+
{
216+
struct device *dev = &pdev->dev;
217+
struct resource *res;
218+
struct clk *clk;
219+
struct s32g_wdt_device *wdev;
220+
struct watchdog_device *wdog;
221+
int ret;
222+
223+
wdev = devm_kzalloc(dev, sizeof(*wdev), GFP_KERNEL);
224+
if (!wdev)
225+
return -ENOMEM;
226+
227+
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
228+
wdev->base = devm_ioremap_resource(dev, res);
229+
if (IS_ERR(wdev->base))
230+
return dev_err_probe(&pdev->dev, PTR_ERR(wdev->base), "Can not get resource\n");
231+
232+
clk = devm_clk_get_enabled(dev, "counter");
233+
if (IS_ERR(clk))
234+
return dev_err_probe(dev, PTR_ERR(clk), "Can't get Watchdog clock\n");
235+
236+
wdev->rate = clk_get_rate(clk);
237+
if (!wdev->rate) {
238+
dev_err(dev, "Input clock rate is not valid\n");
239+
return -EINVAL;
240+
}
241+
242+
wdog = &wdev->wdog;
243+
wdog->info = &s32g_wdt_info;
244+
wdog->ops = &s32g_wdt_ops;
245+
246+
/*
247+
* The code converts the timeout into a counter a value, if
248+
* the value is less than 0x100, then it is clamped by the SWT
249+
* module, so it is safe to specify a zero value as the
250+
* minimum timeout.
251+
*/
252+
wdog->min_timeout = 0;
253+
254+
/*
255+
* The counter register is a 32 bits long, so the maximum
256+
* counter value is UINT_MAX and the timeout in second is the
257+
* value divided by the rate.
258+
*
259+
* For instance, a rate of 51MHz lead to 84 seconds maximum
260+
* timeout.
261+
*/
262+
wdog->max_timeout = UINT_MAX / wdev->rate;
263+
264+
/*
265+
* The module param and the DT 'timeout-sec' property will
266+
* override the default value if they are specified.
267+
*/
268+
ret = watchdog_init_timeout(wdog, timeout_param, dev);
269+
if (ret)
270+
return ret;
271+
272+
/*
273+
* As soon as the watchdog is started, there is no way to stop
274+
* it if the 'nowayout' option is set at boot time
275+
*/
276+
watchdog_set_nowayout(wdog, nowayout);
277+
278+
/*
279+
* The devm_ version of the watchdog_register_device()
280+
* function will call watchdog_unregister_device() when the
281+
* device is removed.
282+
*/
283+
watchdog_stop_on_unregister(wdog);
284+
285+
s32g_wdt_init(wdev);
286+
287+
ret = devm_watchdog_register_device(dev, wdog);
288+
if (ret)
289+
return dev_err_probe(dev, ret, "Cannot register watchdog device\n");
290+
291+
dev_info(dev, "S32G Watchdog Timer Registered, timeout=%ds, nowayout=%d, early_enable=%d\n",
292+
wdog->timeout, nowayout, early_enable);
293+
294+
return 0;
295+
}
296+
297+
static const struct of_device_id s32g_wdt_dt_ids[] = {
298+
{ .compatible = "nxp,s32g2-swt" },
299+
{ /* sentinel */ }
300+
};
301+
MODULE_DEVICE_TABLE(of, s32g_wdt_dt_ids);
302+
303+
static struct platform_driver s32g_wdt_driver = {
304+
.probe = s32g_wdt_probe,
305+
.driver = {
306+
.name = DRIVER_NAME,
307+
.of_match_table = s32g_wdt_dt_ids,
308+
},
309+
};
310+
311+
module_platform_driver(s32g_wdt_driver);
312+
313+
MODULE_AUTHOR("Daniel Lezcano <[email protected]>");
314+
MODULE_DESCRIPTION("Watchdog driver for S32G SoC");
315+
MODULE_LICENSE("GPL");

0 commit comments

Comments
 (0)