1 // SPDX-License-Identifier: GPL-2.0
8 #include <linux/iopoll.h>
9 #include <linux/kernel.h>
10 #include <linux/module.h>
12 #include <linux/platform_device.h>
13 #include <linux/reboot.h>
14 #include <linux/watchdog.h>
17 #define WDOG_CS_FLG BIT(14)
18 #define WDOG_CS_CMD32EN BIT(13)
19 #define WDOG_CS_PRES BIT(12)
20 #define WDOG_CS_ULK BIT(11)
21 #define WDOG_CS_RCS BIT(10)
23 #define LPO_CLK_SHIFT 8
24 #define WDOG_CS_CLK (LPO_CLK << LPO_CLK_SHIFT)
25 #define WDOG_CS_EN BIT(7)
26 #define WDOG_CS_INT_EN BIT(6)
27 #define WDOG_CS_UPDATE BIT(5)
28 #define WDOG_CS_WAIT BIT(1)
29 #define WDOG_CS_STOP BIT(0)
32 #define WDOG_TOVAL 0x8
34 #define REFRESH_SEQ0 0xA602
35 #define REFRESH_SEQ1 0xB480
36 #define REFRESH ((REFRESH_SEQ1 << 16) | REFRESH_SEQ0)
38 #define UNLOCK_SEQ0 0xC520
39 #define UNLOCK_SEQ1 0xD928
40 #define UNLOCK ((UNLOCK_SEQ1 << 16) | UNLOCK_SEQ0)
42 #define DEFAULT_TIMEOUT 60
43 #define MAX_TIMEOUT 128
44 #define WDOG_CLOCK_RATE 1000
45 #define WDOG_ULK_WAIT_TIMEOUT 1000
46 #define WDOG_RCS_WAIT_TIMEOUT 10000
47 #define WDOG_RCS_POST_WAIT 3000
51 static bool nowayout
= WATCHDOG_NOWAYOUT
;
52 module_param(nowayout
, bool, 0000);
53 MODULE_PARM_DESC(nowayout
, "Watchdog cannot be stopped once started (default="
54 __MODULE_STRING(WATCHDOG_NOWAYOUT
) ")");
56 struct imx_wdt_hw_feature
{
57 bool prescaler_enable
;
62 struct imx7ulp_wdt_device
{
63 struct watchdog_device wdd
;
67 const struct imx_wdt_hw_feature
*hw
;
70 static int imx7ulp_wdt_wait_ulk(void __iomem
*base
)
72 u32 val
= readl(base
+ WDOG_CS
);
74 if (!(val
& WDOG_CS_ULK
) &&
75 readl_poll_timeout_atomic(base
+ WDOG_CS
, val
,
77 WDOG_ULK_WAIT_TIMEOUT
))
83 static int imx7ulp_wdt_wait_rcs(struct imx7ulp_wdt_device
*wdt
)
86 u32 val
= readl(wdt
->base
+ WDOG_CS
);
87 u64 timeout
= (val
& WDOG_CS_PRES
) ?
88 WDOG_RCS_WAIT_TIMEOUT
* 256 : WDOG_RCS_WAIT_TIMEOUT
;
89 unsigned long wait_min
= (val
& WDOG_CS_PRES
) ?
90 WDOG_RCS_POST_WAIT
* 256 : WDOG_RCS_POST_WAIT
;
92 if (!(val
& WDOG_CS_RCS
) &&
93 readl_poll_timeout(wdt
->base
+ WDOG_CS
, val
, val
& WDOG_CS_RCS
, 100,
97 /* Wait 2.5 clocks after RCS done */
98 if (wdt
->hw
->post_rcs_wait
)
99 usleep_range(wait_min
, wait_min
+ 2000);
104 static int _imx7ulp_wdt_enable(struct imx7ulp_wdt_device
*wdt
, bool enable
)
106 u32 val
= readl(wdt
->base
+ WDOG_CS
);
110 writel(UNLOCK
, wdt
->base
+ WDOG_CNT
);
111 ret
= imx7ulp_wdt_wait_ulk(wdt
->base
);
115 writel(val
| WDOG_CS_EN
, wdt
->base
+ WDOG_CS
);
117 writel(val
& ~WDOG_CS_EN
, wdt
->base
+ WDOG_CS
);
120 ret
= imx7ulp_wdt_wait_rcs(wdt
);
129 static int imx7ulp_wdt_enable(struct watchdog_device
*wdog
, bool enable
)
131 struct imx7ulp_wdt_device
*wdt
= watchdog_get_drvdata(wdog
);
134 u32 loop
= RETRY_MAX
;
137 ret
= _imx7ulp_wdt_enable(wdt
, enable
);
138 val
= readl(wdt
->base
+ WDOG_CS
);
139 } while (--loop
> 0 && ((!!(val
& WDOG_CS_EN
)) != enable
|| ret
));
147 static int imx7ulp_wdt_ping(struct watchdog_device
*wdog
)
149 struct imx7ulp_wdt_device
*wdt
= watchdog_get_drvdata(wdog
);
151 writel(REFRESH
, wdt
->base
+ WDOG_CNT
);
156 static int imx7ulp_wdt_start(struct watchdog_device
*wdog
)
158 return imx7ulp_wdt_enable(wdog
, true);
161 static int imx7ulp_wdt_stop(struct watchdog_device
*wdog
)
163 return imx7ulp_wdt_enable(wdog
, false);
166 static int _imx7ulp_wdt_set_timeout(struct imx7ulp_wdt_device
*wdt
,
172 writel(UNLOCK
, wdt
->base
+ WDOG_CNT
);
173 ret
= imx7ulp_wdt_wait_ulk(wdt
->base
);
176 writel(toval
, wdt
->base
+ WDOG_TOVAL
);
178 ret
= imx7ulp_wdt_wait_rcs(wdt
);
186 static int imx7ulp_wdt_set_timeout(struct watchdog_device
*wdog
,
187 unsigned int timeout
)
189 struct imx7ulp_wdt_device
*wdt
= watchdog_get_drvdata(wdog
);
190 u32 toval
= wdt
->hw
->wdog_clock_rate
* timeout
;
193 u32 loop
= RETRY_MAX
;
196 ret
= _imx7ulp_wdt_set_timeout(wdt
, toval
);
197 val
= readl(wdt
->base
+ WDOG_TOVAL
);
198 } while (--loop
> 0 && (val
!= toval
|| ret
));
203 wdog
->timeout
= timeout
;
207 static int imx7ulp_wdt_restart(struct watchdog_device
*wdog
,
208 unsigned long action
, void *data
)
210 struct imx7ulp_wdt_device
*wdt
= watchdog_get_drvdata(wdog
);
213 ret
= imx7ulp_wdt_enable(wdog
, true);
217 ret
= imx7ulp_wdt_set_timeout(&wdt
->wdd
, 1);
221 /* wait for wdog to fire */
228 static const struct watchdog_ops imx7ulp_wdt_ops
= {
229 .owner
= THIS_MODULE
,
230 .start
= imx7ulp_wdt_start
,
231 .stop
= imx7ulp_wdt_stop
,
232 .ping
= imx7ulp_wdt_ping
,
233 .set_timeout
= imx7ulp_wdt_set_timeout
,
234 .restart
= imx7ulp_wdt_restart
,
237 static const struct watchdog_info imx7ulp_wdt_info
= {
238 .identity
= "i.MX7ULP watchdog timer",
239 .options
= WDIOF_SETTIMEOUT
| WDIOF_KEEPALIVEPING
|
243 static int _imx7ulp_wdt_init(struct imx7ulp_wdt_device
*wdt
, unsigned int timeout
, unsigned int cs
)
250 val
= readl(wdt
->base
+ WDOG_CS
);
251 if (val
& WDOG_CS_CMD32EN
) {
252 writel(UNLOCK
, wdt
->base
+ WDOG_CNT
);
255 /* unlock the wdog for reconfiguration */
256 writel_relaxed(UNLOCK_SEQ0
, wdt
->base
+ WDOG_CNT
);
257 writel_relaxed(UNLOCK_SEQ1
, wdt
->base
+ WDOG_CNT
);
261 ret
= imx7ulp_wdt_wait_ulk(wdt
->base
);
265 /* set an initial timeout value in TOVAL */
266 writel(timeout
, wdt
->base
+ WDOG_TOVAL
);
267 writel(cs
, wdt
->base
+ WDOG_CS
);
269 ret
= imx7ulp_wdt_wait_rcs(wdt
);
278 static int imx7ulp_wdt_init(struct imx7ulp_wdt_device
*wdt
, unsigned int timeout
)
280 /* enable 32bit command sequence and reconfigure */
281 u32 val
= WDOG_CS_CMD32EN
| WDOG_CS_CLK
| WDOG_CS_UPDATE
|
282 WDOG_CS_WAIT
| WDOG_CS_STOP
;
285 u32 loop
= RETRY_MAX
;
287 if (wdt
->hw
->prescaler_enable
)
291 val
|= WDOG_CS_INT_EN
;
293 if (readl(wdt
->base
+ WDOG_CS
) & WDOG_CS_EN
) {
294 set_bit(WDOG_HW_RUNNING
, &wdt
->wdd
.status
);
299 ret
= _imx7ulp_wdt_init(wdt
, timeout
, val
);
300 toval
= readl(wdt
->base
+ WDOG_TOVAL
);
301 cs
= readl(wdt
->base
+ WDOG_CS
);
302 cs
&= ~(WDOG_CS_FLG
| WDOG_CS_ULK
| WDOG_CS_RCS
);
303 } while (--loop
> 0 && (cs
!= val
|| toval
!= timeout
|| ret
));
311 static int imx7ulp_wdt_probe(struct platform_device
*pdev
)
313 struct imx7ulp_wdt_device
*imx7ulp_wdt
;
314 struct device
*dev
= &pdev
->dev
;
315 struct watchdog_device
*wdog
;
318 imx7ulp_wdt
= devm_kzalloc(dev
, sizeof(*imx7ulp_wdt
), GFP_KERNEL
);
322 platform_set_drvdata(pdev
, imx7ulp_wdt
);
324 imx7ulp_wdt
->base
= devm_platform_ioremap_resource(pdev
, 0);
325 if (IS_ERR(imx7ulp_wdt
->base
))
326 return PTR_ERR(imx7ulp_wdt
->base
);
328 imx7ulp_wdt
->clk
= devm_clk_get_enabled(dev
, NULL
);
329 if (IS_ERR(imx7ulp_wdt
->clk
)) {
330 dev_err(dev
, "Failed to get watchdog clock\n");
331 return PTR_ERR(imx7ulp_wdt
->clk
);
334 /* The WDOG may need to do external reset through dedicated pin */
335 imx7ulp_wdt
->ext_reset
= of_property_read_bool(dev
->of_node
, "fsl,ext-reset-output");
337 wdog
= &imx7ulp_wdt
->wdd
;
338 wdog
->info
= &imx7ulp_wdt_info
;
339 wdog
->ops
= &imx7ulp_wdt_ops
;
340 wdog
->min_timeout
= 1;
341 wdog
->max_timeout
= MAX_TIMEOUT
;
343 wdog
->timeout
= DEFAULT_TIMEOUT
;
345 watchdog_init_timeout(wdog
, 0, dev
);
346 watchdog_stop_on_reboot(wdog
);
347 watchdog_stop_on_unregister(wdog
);
348 watchdog_set_drvdata(wdog
, imx7ulp_wdt
);
350 imx7ulp_wdt
->hw
= of_device_get_match_data(dev
);
351 ret
= imx7ulp_wdt_init(imx7ulp_wdt
, wdog
->timeout
* imx7ulp_wdt
->hw
->wdog_clock_rate
);
355 return devm_watchdog_register_device(dev
, wdog
);
358 static int __maybe_unused
imx7ulp_wdt_suspend_noirq(struct device
*dev
)
360 struct imx7ulp_wdt_device
*imx7ulp_wdt
= dev_get_drvdata(dev
);
362 if (watchdog_active(&imx7ulp_wdt
->wdd
))
363 imx7ulp_wdt_stop(&imx7ulp_wdt
->wdd
);
365 clk_disable_unprepare(imx7ulp_wdt
->clk
);
370 static int __maybe_unused
imx7ulp_wdt_resume_noirq(struct device
*dev
)
372 struct imx7ulp_wdt_device
*imx7ulp_wdt
= dev_get_drvdata(dev
);
373 u32 timeout
= imx7ulp_wdt
->wdd
.timeout
* imx7ulp_wdt
->hw
->wdog_clock_rate
;
376 ret
= clk_prepare_enable(imx7ulp_wdt
->clk
);
380 if (watchdog_active(&imx7ulp_wdt
->wdd
)) {
381 imx7ulp_wdt_init(imx7ulp_wdt
, timeout
);
382 imx7ulp_wdt_start(&imx7ulp_wdt
->wdd
);
383 imx7ulp_wdt_ping(&imx7ulp_wdt
->wdd
);
389 static const struct dev_pm_ops imx7ulp_wdt_pm_ops
= {
390 SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(imx7ulp_wdt_suspend_noirq
,
391 imx7ulp_wdt_resume_noirq
)
394 static const struct imx_wdt_hw_feature imx7ulp_wdt_hw
= {
395 .prescaler_enable
= false,
396 .wdog_clock_rate
= 1000,
397 .post_rcs_wait
= true,
400 static const struct imx_wdt_hw_feature imx8ulp_wdt_hw
= {
401 .prescaler_enable
= false,
402 .wdog_clock_rate
= 1000,
405 static const struct imx_wdt_hw_feature imx93_wdt_hw
= {
406 .prescaler_enable
= true,
407 .wdog_clock_rate
= 125,
410 static const struct of_device_id imx7ulp_wdt_dt_ids
[] = {
411 { .compatible
= "fsl,imx7ulp-wdt", .data
= &imx7ulp_wdt_hw
, },
412 { .compatible
= "fsl,imx8ulp-wdt", .data
= &imx8ulp_wdt_hw
, },
413 { .compatible
= "fsl,imx93-wdt", .data
= &imx93_wdt_hw
, },
416 MODULE_DEVICE_TABLE(of
, imx7ulp_wdt_dt_ids
);
418 static struct platform_driver imx7ulp_wdt_driver
= {
419 .probe
= imx7ulp_wdt_probe
,
421 .name
= "imx7ulp-wdt",
422 .pm
= &imx7ulp_wdt_pm_ops
,
423 .of_match_table
= imx7ulp_wdt_dt_ids
,
426 module_platform_driver(imx7ulp_wdt_driver
);
428 MODULE_AUTHOR("Anson Huang <Anson.Huang@nxp.com>");
429 MODULE_DESCRIPTION("Freescale i.MX7ULP watchdog driver");
430 MODULE_LICENSE("GPL v2");