1 // SPDX-License-Identifier: GPL-2.0
8 #include <linux/kernel.h>
9 #include <linux/module.h>
11 #include <linux/platform_device.h>
12 #include <linux/reboot.h>
13 #include <linux/watchdog.h>
16 #define WDOG_CS_CMD32EN BIT(13)
17 #define WDOG_CS_ULK BIT(11)
18 #define WDOG_CS_RCS BIT(10)
20 #define LPO_CLK_SHIFT 8
21 #define WDOG_CS_CLK (LPO_CLK << LPO_CLK_SHIFT)
22 #define WDOG_CS_EN BIT(7)
23 #define WDOG_CS_UPDATE BIT(5)
26 #define WDOG_TOVAL 0x8
28 #define REFRESH_SEQ0 0xA602
29 #define REFRESH_SEQ1 0xB480
30 #define REFRESH ((REFRESH_SEQ1 << 16) | REFRESH_SEQ0)
32 #define UNLOCK_SEQ0 0xC520
33 #define UNLOCK_SEQ1 0xD928
34 #define UNLOCK ((UNLOCK_SEQ1 << 16) | UNLOCK_SEQ0)
36 #define DEFAULT_TIMEOUT 60
37 #define MAX_TIMEOUT 128
38 #define WDOG_CLOCK_RATE 1000
40 static bool nowayout
= WATCHDOG_NOWAYOUT
;
41 module_param(nowayout
, bool, 0000);
42 MODULE_PARM_DESC(nowayout
, "Watchdog cannot be stopped once started (default="
43 __MODULE_STRING(WATCHDOG_NOWAYOUT
) ")");
45 struct imx7ulp_wdt_device
{
46 struct watchdog_device wdd
;
51 static void imx7ulp_wdt_enable(struct watchdog_device
*wdog
, bool enable
)
53 struct imx7ulp_wdt_device
*wdt
= watchdog_get_drvdata(wdog
);
55 u32 val
= readl(wdt
->base
+ WDOG_CS
);
57 writel(UNLOCK
, wdt
->base
+ WDOG_CNT
);
59 writel(val
| WDOG_CS_EN
, wdt
->base
+ WDOG_CS
);
61 writel(val
& ~WDOG_CS_EN
, wdt
->base
+ WDOG_CS
);
64 static bool imx7ulp_wdt_is_enabled(void __iomem
*base
)
66 u32 val
= readl(base
+ WDOG_CS
);
68 return val
& WDOG_CS_EN
;
71 static int imx7ulp_wdt_ping(struct watchdog_device
*wdog
)
73 struct imx7ulp_wdt_device
*wdt
= watchdog_get_drvdata(wdog
);
75 writel(REFRESH
, wdt
->base
+ WDOG_CNT
);
80 static int imx7ulp_wdt_start(struct watchdog_device
*wdog
)
83 imx7ulp_wdt_enable(wdog
, true);
88 static int imx7ulp_wdt_stop(struct watchdog_device
*wdog
)
90 imx7ulp_wdt_enable(wdog
, false);
95 static int imx7ulp_wdt_set_timeout(struct watchdog_device
*wdog
,
98 struct imx7ulp_wdt_device
*wdt
= watchdog_get_drvdata(wdog
);
99 u32 val
= WDOG_CLOCK_RATE
* timeout
;
101 writel(UNLOCK
, wdt
->base
+ WDOG_CNT
);
102 writel(val
, wdt
->base
+ WDOG_TOVAL
);
104 wdog
->timeout
= timeout
;
109 static int imx7ulp_wdt_restart(struct watchdog_device
*wdog
,
110 unsigned long action
, void *data
)
112 struct imx7ulp_wdt_device
*wdt
= watchdog_get_drvdata(wdog
);
114 imx7ulp_wdt_enable(wdog
, true);
115 imx7ulp_wdt_set_timeout(&wdt
->wdd
, 1);
117 /* wait for wdog to fire */
124 static const struct watchdog_ops imx7ulp_wdt_ops
= {
125 .owner
= THIS_MODULE
,
126 .start
= imx7ulp_wdt_start
,
127 .stop
= imx7ulp_wdt_stop
,
128 .ping
= imx7ulp_wdt_ping
,
129 .set_timeout
= imx7ulp_wdt_set_timeout
,
130 .restart
= imx7ulp_wdt_restart
,
133 static const struct watchdog_info imx7ulp_wdt_info
= {
134 .identity
= "i.MX7ULP watchdog timer",
135 .options
= WDIOF_SETTIMEOUT
| WDIOF_KEEPALIVEPING
|
139 static void imx7ulp_wdt_init(void __iomem
*base
, unsigned int timeout
)
143 /* unlock the wdog for reconfiguration */
144 writel_relaxed(UNLOCK_SEQ0
, base
+ WDOG_CNT
);
145 writel_relaxed(UNLOCK_SEQ1
, base
+ WDOG_CNT
);
147 /* set an initial timeout value in TOVAL */
148 writel(timeout
, base
+ WDOG_TOVAL
);
149 /* enable 32bit command sequence and reconfigure */
150 val
= WDOG_CS_CMD32EN
| WDOG_CS_CLK
| WDOG_CS_UPDATE
;
151 writel(val
, base
+ WDOG_CS
);
154 static void imx7ulp_wdt_action(void *data
)
156 clk_disable_unprepare(data
);
159 static int imx7ulp_wdt_probe(struct platform_device
*pdev
)
161 struct imx7ulp_wdt_device
*imx7ulp_wdt
;
162 struct device
*dev
= &pdev
->dev
;
163 struct watchdog_device
*wdog
;
166 imx7ulp_wdt
= devm_kzalloc(dev
, sizeof(*imx7ulp_wdt
), GFP_KERNEL
);
170 platform_set_drvdata(pdev
, imx7ulp_wdt
);
172 imx7ulp_wdt
->base
= devm_platform_ioremap_resource(pdev
, 0);
173 if (IS_ERR(imx7ulp_wdt
->base
))
174 return PTR_ERR(imx7ulp_wdt
->base
);
176 imx7ulp_wdt
->clk
= devm_clk_get(dev
, NULL
);
177 if (IS_ERR(imx7ulp_wdt
->clk
)) {
178 dev_err(dev
, "Failed to get watchdog clock\n");
179 return PTR_ERR(imx7ulp_wdt
->clk
);
182 ret
= clk_prepare_enable(imx7ulp_wdt
->clk
);
186 ret
= devm_add_action_or_reset(dev
, imx7ulp_wdt_action
, imx7ulp_wdt
->clk
);
190 wdog
= &imx7ulp_wdt
->wdd
;
191 wdog
->info
= &imx7ulp_wdt_info
;
192 wdog
->ops
= &imx7ulp_wdt_ops
;
193 wdog
->min_timeout
= 1;
194 wdog
->max_timeout
= MAX_TIMEOUT
;
196 wdog
->timeout
= DEFAULT_TIMEOUT
;
198 watchdog_init_timeout(wdog
, 0, dev
);
199 watchdog_stop_on_reboot(wdog
);
200 watchdog_stop_on_unregister(wdog
);
201 watchdog_set_drvdata(wdog
, imx7ulp_wdt
);
202 imx7ulp_wdt_init(imx7ulp_wdt
->base
, wdog
->timeout
* WDOG_CLOCK_RATE
);
204 return devm_watchdog_register_device(dev
, wdog
);
207 static int __maybe_unused
imx7ulp_wdt_suspend(struct device
*dev
)
209 struct imx7ulp_wdt_device
*imx7ulp_wdt
= dev_get_drvdata(dev
);
211 if (watchdog_active(&imx7ulp_wdt
->wdd
))
212 imx7ulp_wdt_stop(&imx7ulp_wdt
->wdd
);
214 clk_disable_unprepare(imx7ulp_wdt
->clk
);
219 static int __maybe_unused
imx7ulp_wdt_resume(struct device
*dev
)
221 struct imx7ulp_wdt_device
*imx7ulp_wdt
= dev_get_drvdata(dev
);
222 u32 timeout
= imx7ulp_wdt
->wdd
.timeout
* WDOG_CLOCK_RATE
;
225 ret
= clk_prepare_enable(imx7ulp_wdt
->clk
);
229 if (imx7ulp_wdt_is_enabled(imx7ulp_wdt
->base
))
230 imx7ulp_wdt_init(imx7ulp_wdt
->base
, timeout
);
232 if (watchdog_active(&imx7ulp_wdt
->wdd
))
233 imx7ulp_wdt_start(&imx7ulp_wdt
->wdd
);
238 static SIMPLE_DEV_PM_OPS(imx7ulp_wdt_pm_ops
, imx7ulp_wdt_suspend
,
241 static const struct of_device_id imx7ulp_wdt_dt_ids
[] = {
242 { .compatible
= "fsl,imx7ulp-wdt", },
245 MODULE_DEVICE_TABLE(of
, imx7ulp_wdt_dt_ids
);
247 static struct platform_driver imx7ulp_wdt_driver
= {
248 .probe
= imx7ulp_wdt_probe
,
250 .name
= "imx7ulp-wdt",
251 .pm
= &imx7ulp_wdt_pm_ops
,
252 .of_match_table
= imx7ulp_wdt_dt_ids
,
255 module_platform_driver(imx7ulp_wdt_driver
);
257 MODULE_AUTHOR("Anson Huang <Anson.Huang@nxp.com>");
258 MODULE_DESCRIPTION("Freescale i.MX7ULP watchdog driver");
259 MODULE_LICENSE("GPL v2");