summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAnson Huang <Anson.Huang@nxp.com>2019-08-28 09:35:01 -0400
committerWim Van Sebroeck <wim@linux-watchdog.org>2019-09-17 02:59:12 -0400
commit41b630f41bf744b0eed92a53ff8c716cfc71920a (patch)
tree7366d181e8b3122158aba605d9e064c495b6c65b
parent69eb8b118631e5a6741edc90aac6d9139a43e9ec (diff)
watchdog: Add i.MX7ULP watchdog support
The i.MX7ULP Watchdog Timer (WDOG) module is an independent timer that is available for system use. It provides a safety feature to ensure that software is executing as planned and that the CPU is not stuck in an infinite loop or executing unintended code. If the WDOG module is not serviced (refreshed) within a certain period, it resets the MCU. Add driver support for i.MX7ULP watchdog. Signed-off-by: Anson Huang <Anson.Huang@nxp.com> Reviewed-by: Guenter Roeck <linux@roeck-us.net> Link: https://lore.kernel.org/r/1566999303-18795-2-git-send-email-Anson.Huang@nxp.com Signed-off-by: Guenter Roeck <linux@roeck-us.net> Signed-off-by: Wim Van Sebroeck <wim@linux-watchdog.org>
-rw-r--r--drivers/watchdog/Kconfig13
-rw-r--r--drivers/watchdog/Makefile1
-rw-r--r--drivers/watchdog/imx7ulp_wdt.c243
3 files changed, 257 insertions, 0 deletions
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index a8f5c8147d4b..d68e5b500aef 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -724,6 +724,19 @@ config IMX_SC_WDT
724 To compile this driver as a module, choose M here: the 724 To compile this driver as a module, choose M here: the
725 module will be called imx_sc_wdt. 725 module will be called imx_sc_wdt.
726 726
727config IMX7ULP_WDT
728 tristate "IMX7ULP Watchdog"
729 depends on ARCH_MXC || COMPILE_TEST
730 select WATCHDOG_CORE
731 help
732 This is the driver for the hardware watchdog on the Freescale
733 IMX7ULP and later processors. If you have one of these
734 processors and wish to have watchdog support enabled,
735 say Y, otherwise say N.
736
737 To compile this driver as a module, choose M here: the
738 module will be called imx7ulp_wdt.
739
727config UX500_WATCHDOG 740config UX500_WATCHDOG
728 tristate "ST-Ericsson Ux500 watchdog" 741 tristate "ST-Ericsson Ux500 watchdog"
729 depends on MFD_DB8500_PRCMU 742 depends on MFD_DB8500_PRCMU
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index b5a0aed537af..2ee352bf3372 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -67,6 +67,7 @@ obj-$(CONFIG_TS4800_WATCHDOG) += ts4800_wdt.o
67obj-$(CONFIG_TS72XX_WATCHDOG) += ts72xx_wdt.o 67obj-$(CONFIG_TS72XX_WATCHDOG) += ts72xx_wdt.o
68obj-$(CONFIG_IMX2_WDT) += imx2_wdt.o 68obj-$(CONFIG_IMX2_WDT) += imx2_wdt.o
69obj-$(CONFIG_IMX_SC_WDT) += imx_sc_wdt.o 69obj-$(CONFIG_IMX_SC_WDT) += imx_sc_wdt.o
70obj-$(CONFIG_IMX7ULP_WDT) += imx7ulp_wdt.o
70obj-$(CONFIG_UX500_WATCHDOG) += ux500_wdt.o 71obj-$(CONFIG_UX500_WATCHDOG) += ux500_wdt.o
71obj-$(CONFIG_RETU_WATCHDOG) += retu_wdt.o 72obj-$(CONFIG_RETU_WATCHDOG) += retu_wdt.o
72obj-$(CONFIG_BCM2835_WDT) += bcm2835_wdt.o 73obj-$(CONFIG_BCM2835_WDT) += bcm2835_wdt.o
diff --git a/drivers/watchdog/imx7ulp_wdt.c b/drivers/watchdog/imx7ulp_wdt.c
new file mode 100644
index 000000000000..5ce51026989a
--- /dev/null
+++ b/drivers/watchdog/imx7ulp_wdt.c
@@ -0,0 +1,243 @@
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright 2019 NXP.
4 */
5
6#include <linux/clk.h>
7#include <linux/init.h>
8#include <linux/io.h>
9#include <linux/kernel.h>
10#include <linux/module.h>
11#include <linux/of.h>
12#include <linux/platform_device.h>
13#include <linux/reboot.h>
14#include <linux/watchdog.h>
15
16#define WDOG_CS 0x0
17#define WDOG_CS_CMD32EN BIT(13)
18#define WDOG_CS_ULK BIT(11)
19#define WDOG_CS_RCS BIT(10)
20#define WDOG_CS_EN BIT(7)
21#define WDOG_CS_UPDATE BIT(5)
22
23#define WDOG_CNT 0x4
24#define WDOG_TOVAL 0x8
25
26#define REFRESH_SEQ0 0xA602
27#define REFRESH_SEQ1 0xB480
28#define REFRESH ((REFRESH_SEQ1 << 16) | REFRESH_SEQ0)
29
30#define UNLOCK_SEQ0 0xC520
31#define UNLOCK_SEQ1 0xD928
32#define UNLOCK ((UNLOCK_SEQ1 << 16) | UNLOCK_SEQ0)
33
34#define DEFAULT_TIMEOUT 60
35#define MAX_TIMEOUT 128
36#define WDOG_CLOCK_RATE 1000
37
38static bool nowayout = WATCHDOG_NOWAYOUT;
39module_param(nowayout, bool, 0000);
40MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
41 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
42
43struct imx7ulp_wdt_device {
44 struct notifier_block restart_handler;
45 struct watchdog_device wdd;
46 void __iomem *base;
47 struct clk *clk;
48};
49
50static inline void imx7ulp_wdt_enable(void __iomem *base, bool enable)
51{
52 u32 val = readl(base + WDOG_CS);
53
54 writel(UNLOCK, base + WDOG_CNT);
55 if (enable)
56 writel(val | WDOG_CS_EN, base + WDOG_CS);
57 else
58 writel(val & ~WDOG_CS_EN, base + WDOG_CS);
59}
60
61static inline bool imx7ulp_wdt_is_enabled(void __iomem *base)
62{
63 u32 val = readl(base + WDOG_CS);
64
65 return val & WDOG_CS_EN;
66}
67
68static int imx7ulp_wdt_ping(struct watchdog_device *wdog)
69{
70 struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog);
71
72 writel(REFRESH, wdt->base + WDOG_CNT);
73
74 return 0;
75}
76
77static int imx7ulp_wdt_start(struct watchdog_device *wdog)
78{
79 struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog);
80
81 imx7ulp_wdt_enable(wdt->base, true);
82
83 return 0;
84}
85
86static int imx7ulp_wdt_stop(struct watchdog_device *wdog)
87{
88 struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog);
89
90 imx7ulp_wdt_enable(wdt->base, false);
91
92 return 0;
93}
94
95static int imx7ulp_wdt_set_timeout(struct watchdog_device *wdog,
96 unsigned int timeout)
97{
98 struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog);
99 u32 val = WDOG_CLOCK_RATE * timeout;
100
101 writel(UNLOCK, wdt->base + WDOG_CNT);
102 writel(val, wdt->base + WDOG_TOVAL);
103
104 wdog->timeout = timeout;
105
106 return 0;
107}
108
109static const struct watchdog_ops imx7ulp_wdt_ops = {
110 .owner = THIS_MODULE,
111 .start = imx7ulp_wdt_start,
112 .stop = imx7ulp_wdt_stop,
113 .ping = imx7ulp_wdt_ping,
114 .set_timeout = imx7ulp_wdt_set_timeout,
115};
116
117static const struct watchdog_info imx7ulp_wdt_info = {
118 .identity = "i.MX7ULP watchdog timer",
119 .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING |
120 WDIOF_MAGICCLOSE,
121};
122
123static inline void imx7ulp_wdt_init(void __iomem *base, unsigned int timeout)
124{
125 u32 val;
126
127 /* unlock the wdog for reconfiguration */
128 writel_relaxed(UNLOCK_SEQ0, base + WDOG_CNT);
129 writel_relaxed(UNLOCK_SEQ1, base + WDOG_CNT);
130
131 /* set an initial timeout value in TOVAL */
132 writel(timeout, base + WDOG_TOVAL);
133 /* enable 32bit command sequence and reconfigure */
134 val = BIT(13) | BIT(8) | BIT(5);
135 writel(val, base + WDOG_CS);
136}
137
138static void imx7ulp_wdt_action(void *data)
139{
140 clk_disable_unprepare(data);
141}
142
143static int imx7ulp_wdt_probe(struct platform_device *pdev)
144{
145 struct imx7ulp_wdt_device *imx7ulp_wdt;
146 struct device *dev = &pdev->dev;
147 struct watchdog_device *wdog;
148 int ret;
149
150 imx7ulp_wdt = devm_kzalloc(dev, sizeof(*imx7ulp_wdt), GFP_KERNEL);
151 if (!imx7ulp_wdt)
152 return -ENOMEM;
153
154 platform_set_drvdata(pdev, imx7ulp_wdt);
155
156 imx7ulp_wdt->base = devm_platform_ioremap_resource(pdev, 0);
157 if (IS_ERR(imx7ulp_wdt->base))
158 return PTR_ERR(imx7ulp_wdt->base);
159
160 imx7ulp_wdt->clk = devm_clk_get(dev, NULL);
161 if (IS_ERR(imx7ulp_wdt->clk)) {
162 dev_err(dev, "Failed to get watchdog clock\n");
163 return PTR_ERR(imx7ulp_wdt->clk);
164 }
165
166 ret = clk_prepare_enable(imx7ulp_wdt->clk);
167 if (ret)
168 return ret;
169
170 ret = devm_add_action_or_reset(dev, imx7ulp_wdt_action, imx7ulp_wdt->clk);
171 if (ret)
172 return ret;
173
174 wdog = &imx7ulp_wdt->wdd;
175 wdog->info = &imx7ulp_wdt_info;
176 wdog->ops = &imx7ulp_wdt_ops;
177 wdog->min_timeout = 1;
178 wdog->max_timeout = MAX_TIMEOUT;
179 wdog->parent = dev;
180 wdog->timeout = DEFAULT_TIMEOUT;
181
182 watchdog_init_timeout(wdog, 0, dev);
183 watchdog_stop_on_reboot(wdog);
184 watchdog_stop_on_unregister(wdog);
185 watchdog_set_drvdata(wdog, imx7ulp_wdt);
186 imx7ulp_wdt_init(imx7ulp_wdt->base, wdog->timeout * WDOG_CLOCK_RATE);
187
188 return devm_watchdog_register_device(dev, wdog);
189}
190
191static int __maybe_unused imx7ulp_wdt_suspend(struct device *dev)
192{
193 struct imx7ulp_wdt_device *imx7ulp_wdt = dev_get_drvdata(dev);
194
195 if (watchdog_active(&imx7ulp_wdt->wdd))
196 imx7ulp_wdt_stop(&imx7ulp_wdt->wdd);
197
198 clk_disable_unprepare(imx7ulp_wdt->clk);
199
200 return 0;
201}
202
203static int __maybe_unused imx7ulp_wdt_resume(struct device *dev)
204{
205 struct imx7ulp_wdt_device *imx7ulp_wdt = dev_get_drvdata(dev);
206 u32 timeout = imx7ulp_wdt->wdd.timeout * WDOG_CLOCK_RATE;
207 int ret;
208
209 ret = clk_prepare_enable(imx7ulp_wdt->clk);
210 if (ret)
211 return ret;
212
213 if (imx7ulp_wdt_is_enabled(imx7ulp_wdt->base))
214 imx7ulp_wdt_init(imx7ulp_wdt->base, timeout);
215
216 if (watchdog_active(&imx7ulp_wdt->wdd))
217 imx7ulp_wdt_start(&imx7ulp_wdt->wdd);
218
219 return 0;
220}
221
222static SIMPLE_DEV_PM_OPS(imx7ulp_wdt_pm_ops, imx7ulp_wdt_suspend,
223 imx7ulp_wdt_resume);
224
225static const struct of_device_id imx7ulp_wdt_dt_ids[] = {
226 { .compatible = "fsl,imx7ulp-wdt", },
227 { /* sentinel */ }
228};
229MODULE_DEVICE_TABLE(of, imx7ulp_wdt_dt_ids);
230
231static struct platform_driver imx7ulp_wdt_driver = {
232 .probe = imx7ulp_wdt_probe,
233 .driver = {
234 .name = "imx7ulp-wdt",
235 .pm = &imx7ulp_wdt_pm_ops,
236 .of_match_table = imx7ulp_wdt_dt_ids,
237 },
238};
239module_platform_driver(imx7ulp_wdt_driver);
240
241MODULE_AUTHOR("Anson Huang <Anson.Huang@nxp.com>");
242MODULE_DESCRIPTION("Freescale i.MX7ULP watchdog driver");
243MODULE_LICENSE("GPL v2");