]> git.proxmox.com Git - mirror_ubuntu-hirsute-kernel.git/blame - drivers/watchdog/imx7ulp_wdt.c
Merge branch 'for-linus' into for-next
[mirror_ubuntu-hirsute-kernel.git] / drivers / watchdog / imx7ulp_wdt.c
CommitLineData
41b630f4
AH
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)
eccb7fe5
FE
20#define LPO_CLK 0x1
21#define LPO_CLK_SHIFT 8
22#define WDOG_CS_CLK (LPO_CLK << LPO_CLK_SHIFT)
41b630f4
AH
23#define WDOG_CS_EN BIT(7)
24#define WDOG_CS_UPDATE BIT(5)
25
26#define WDOG_CNT 0x4
27#define WDOG_TOVAL 0x8
28
29#define REFRESH_SEQ0 0xA602
30#define REFRESH_SEQ1 0xB480
31#define REFRESH ((REFRESH_SEQ1 << 16) | REFRESH_SEQ0)
32
33#define UNLOCK_SEQ0 0xC520
34#define UNLOCK_SEQ1 0xD928
35#define UNLOCK ((UNLOCK_SEQ1 << 16) | UNLOCK_SEQ0)
36
37#define DEFAULT_TIMEOUT 60
38#define MAX_TIMEOUT 128
39#define WDOG_CLOCK_RATE 1000
40
41static bool nowayout = WATCHDOG_NOWAYOUT;
42module_param(nowayout, bool, 0000);
43MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
44 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
45
46struct imx7ulp_wdt_device {
41b630f4
AH
47 struct watchdog_device wdd;
48 void __iomem *base;
49 struct clk *clk;
50};
51
c37e3581 52static void imx7ulp_wdt_enable(struct watchdog_device *wdog, bool enable)
41b630f4 53{
747d88a1 54 struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog);
41b630f4 55
747d88a1
FE
56 u32 val = readl(wdt->base + WDOG_CS);
57
58 writel(UNLOCK, wdt->base + WDOG_CNT);
41b630f4 59 if (enable)
747d88a1 60 writel(val | WDOG_CS_EN, wdt->base + WDOG_CS);
41b630f4 61 else
747d88a1 62 writel(val & ~WDOG_CS_EN, wdt->base + WDOG_CS);
41b630f4
AH
63}
64
c37e3581 65static bool imx7ulp_wdt_is_enabled(void __iomem *base)
41b630f4
AH
66{
67 u32 val = readl(base + WDOG_CS);
68
69 return val & WDOG_CS_EN;
70}
71
72static int imx7ulp_wdt_ping(struct watchdog_device *wdog)
73{
74 struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog);
75
76 writel(REFRESH, wdt->base + WDOG_CNT);
77
78 return 0;
79}
80
81static int imx7ulp_wdt_start(struct watchdog_device *wdog)
82{
41b630f4 83
747d88a1 84 imx7ulp_wdt_enable(wdog, true);
41b630f4
AH
85
86 return 0;
87}
88
89static int imx7ulp_wdt_stop(struct watchdog_device *wdog)
90{
747d88a1 91 imx7ulp_wdt_enable(wdog, false);
41b630f4
AH
92
93 return 0;
94}
95
96static int imx7ulp_wdt_set_timeout(struct watchdog_device *wdog,
97 unsigned int timeout)
98{
99 struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog);
100 u32 val = WDOG_CLOCK_RATE * timeout;
101
102 writel(UNLOCK, wdt->base + WDOG_CNT);
103 writel(val, wdt->base + WDOG_TOVAL);
104
105 wdog->timeout = timeout;
106
107 return 0;
108}
109
6083ab7b
FE
110static int imx7ulp_wdt_restart(struct watchdog_device *wdog,
111 unsigned long action, void *data)
112{
113 struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog);
114
115 imx7ulp_wdt_enable(wdt->base, true);
116 imx7ulp_wdt_set_timeout(&wdt->wdd, 1);
117
118 /* wait for wdog to fire */
119 while (true)
120 ;
121
122 return NOTIFY_DONE;
123}
124
41b630f4
AH
125static const struct watchdog_ops imx7ulp_wdt_ops = {
126 .owner = THIS_MODULE,
127 .start = imx7ulp_wdt_start,
128 .stop = imx7ulp_wdt_stop,
129 .ping = imx7ulp_wdt_ping,
130 .set_timeout = imx7ulp_wdt_set_timeout,
6083ab7b 131 .restart = imx7ulp_wdt_restart,
41b630f4
AH
132};
133
134static const struct watchdog_info imx7ulp_wdt_info = {
135 .identity = "i.MX7ULP watchdog timer",
136 .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING |
137 WDIOF_MAGICCLOSE,
138};
139
c37e3581 140static void imx7ulp_wdt_init(void __iomem *base, unsigned int timeout)
41b630f4
AH
141{
142 u32 val;
143
144 /* unlock the wdog for reconfiguration */
145 writel_relaxed(UNLOCK_SEQ0, base + WDOG_CNT);
146 writel_relaxed(UNLOCK_SEQ1, base + WDOG_CNT);
147
148 /* set an initial timeout value in TOVAL */
149 writel(timeout, base + WDOG_TOVAL);
150 /* enable 32bit command sequence and reconfigure */
eccb7fe5 151 val = WDOG_CS_CMD32EN | WDOG_CS_CLK | WDOG_CS_UPDATE;
41b630f4
AH
152 writel(val, base + WDOG_CS);
153}
154
155static void imx7ulp_wdt_action(void *data)
156{
157 clk_disable_unprepare(data);
158}
159
160static int imx7ulp_wdt_probe(struct platform_device *pdev)
161{
162 struct imx7ulp_wdt_device *imx7ulp_wdt;
163 struct device *dev = &pdev->dev;
164 struct watchdog_device *wdog;
165 int ret;
166
167 imx7ulp_wdt = devm_kzalloc(dev, sizeof(*imx7ulp_wdt), GFP_KERNEL);
168 if (!imx7ulp_wdt)
169 return -ENOMEM;
170
171 platform_set_drvdata(pdev, imx7ulp_wdt);
172
173 imx7ulp_wdt->base = devm_platform_ioremap_resource(pdev, 0);
174 if (IS_ERR(imx7ulp_wdt->base))
175 return PTR_ERR(imx7ulp_wdt->base);
176
177 imx7ulp_wdt->clk = devm_clk_get(dev, NULL);
178 if (IS_ERR(imx7ulp_wdt->clk)) {
179 dev_err(dev, "Failed to get watchdog clock\n");
180 return PTR_ERR(imx7ulp_wdt->clk);
181 }
182
183 ret = clk_prepare_enable(imx7ulp_wdt->clk);
184 if (ret)
185 return ret;
186
187 ret = devm_add_action_or_reset(dev, imx7ulp_wdt_action, imx7ulp_wdt->clk);
188 if (ret)
189 return ret;
190
191 wdog = &imx7ulp_wdt->wdd;
192 wdog->info = &imx7ulp_wdt_info;
193 wdog->ops = &imx7ulp_wdt_ops;
194 wdog->min_timeout = 1;
195 wdog->max_timeout = MAX_TIMEOUT;
196 wdog->parent = dev;
197 wdog->timeout = DEFAULT_TIMEOUT;
198
199 watchdog_init_timeout(wdog, 0, dev);
200 watchdog_stop_on_reboot(wdog);
201 watchdog_stop_on_unregister(wdog);
202 watchdog_set_drvdata(wdog, imx7ulp_wdt);
203 imx7ulp_wdt_init(imx7ulp_wdt->base, wdog->timeout * WDOG_CLOCK_RATE);
204
205 return devm_watchdog_register_device(dev, wdog);
206}
207
208static int __maybe_unused imx7ulp_wdt_suspend(struct device *dev)
209{
210 struct imx7ulp_wdt_device *imx7ulp_wdt = dev_get_drvdata(dev);
211
212 if (watchdog_active(&imx7ulp_wdt->wdd))
213 imx7ulp_wdt_stop(&imx7ulp_wdt->wdd);
214
215 clk_disable_unprepare(imx7ulp_wdt->clk);
216
217 return 0;
218}
219
220static int __maybe_unused imx7ulp_wdt_resume(struct device *dev)
221{
222 struct imx7ulp_wdt_device *imx7ulp_wdt = dev_get_drvdata(dev);
223 u32 timeout = imx7ulp_wdt->wdd.timeout * WDOG_CLOCK_RATE;
224 int ret;
225
226 ret = clk_prepare_enable(imx7ulp_wdt->clk);
227 if (ret)
228 return ret;
229
230 if (imx7ulp_wdt_is_enabled(imx7ulp_wdt->base))
231 imx7ulp_wdt_init(imx7ulp_wdt->base, timeout);
232
233 if (watchdog_active(&imx7ulp_wdt->wdd))
234 imx7ulp_wdt_start(&imx7ulp_wdt->wdd);
235
236 return 0;
237}
238
239static SIMPLE_DEV_PM_OPS(imx7ulp_wdt_pm_ops, imx7ulp_wdt_suspend,
240 imx7ulp_wdt_resume);
241
242static const struct of_device_id imx7ulp_wdt_dt_ids[] = {
243 { .compatible = "fsl,imx7ulp-wdt", },
244 { /* sentinel */ }
245};
246MODULE_DEVICE_TABLE(of, imx7ulp_wdt_dt_ids);
247
248static struct platform_driver imx7ulp_wdt_driver = {
249 .probe = imx7ulp_wdt_probe,
250 .driver = {
251 .name = "imx7ulp-wdt",
252 .pm = &imx7ulp_wdt_pm_ops,
253 .of_match_table = imx7ulp_wdt_dt_ids,
254 },
255};
256module_platform_driver(imx7ulp_wdt_driver);
257
258MODULE_AUTHOR("Anson Huang <Anson.Huang@nxp.com>");
259MODULE_DESCRIPTION("Freescale i.MX7ULP watchdog driver");
260MODULE_LICENSE("GPL v2");