1
2
3
4
5
6#define LOG_CATEGORY UCLASS_WDT
7
8#include <common.h>
9#include <dm.h>
10#include <errno.h>
11#include <hang.h>
12#include <log.h>
13#include <sysreset.h>
14#include <time.h>
15#include <wdt.h>
16#include <asm/global_data.h>
17#include <dm/device-internal.h>
18#include <dm/lists.h>
19
20DECLARE_GLOBAL_DATA_PTR;
21
22#define WATCHDOG_TIMEOUT_SECS (CONFIG_WATCHDOG_TIMEOUT_MSECS / 1000)
23
24struct wdt_priv {
25
26 u32 timeout;
27
28
29
30
31 ulong reset_period;
32
33
34
35
36 ulong next_reset;
37
38 bool running;
39};
40
41static void init_watchdog_dev(struct udevice *dev)
42{
43 struct wdt_priv *priv;
44 int ret;
45
46 priv = dev_get_uclass_priv(dev);
47
48 if (IS_ENABLED(CONFIG_SYSRESET_WATCHDOG_AUTO)) {
49 ret = sysreset_register_wdt(dev);
50 if (ret)
51 printf("WDT: Failed to register %s for sysreset\n",
52 dev->name);
53 }
54
55 if (!IS_ENABLED(CONFIG_WATCHDOG_AUTOSTART)) {
56 printf("WDT: Not starting %s\n", dev->name);
57 return;
58 }
59
60 ret = wdt_start(dev, priv->timeout * 1000, 0);
61 if (ret != 0) {
62 printf("WDT: Failed to start %s\n", dev->name);
63 return;
64 }
65
66 printf("WDT: Started %s with%s servicing (%ds timeout)\n", dev->name,
67 IS_ENABLED(CONFIG_WATCHDOG) ? "" : "out", priv->timeout);
68}
69
70int initr_watchdog(void)
71{
72 struct udevice *dev;
73 struct uclass *uc;
74 int ret;
75
76 ret = uclass_get(UCLASS_WDT, &uc);
77 if (ret) {
78 log_debug("Error getting UCLASS_WDT: %d\n", ret);
79 return 0;
80 }
81
82 uclass_foreach_dev(dev, uc) {
83 ret = device_probe(dev);
84 if (ret) {
85 log_debug("Error probing %s: %d\n", dev->name, ret);
86 continue;
87 }
88 init_watchdog_dev(dev);
89 }
90
91 gd->flags |= GD_FLG_WDT_READY;
92 return 0;
93}
94
95int wdt_start(struct udevice *dev, u64 timeout_ms, ulong flags)
96{
97 const struct wdt_ops *ops = device_get_ops(dev);
98 int ret;
99
100 if (!ops->start)
101 return -ENOSYS;
102
103 ret = ops->start(dev, timeout_ms, flags);
104 if (ret == 0) {
105 struct wdt_priv *priv = dev_get_uclass_priv(dev);
106
107 priv->running = true;
108 }
109
110 return ret;
111}
112
113int wdt_stop(struct udevice *dev)
114{
115 const struct wdt_ops *ops = device_get_ops(dev);
116 int ret;
117
118 if (!ops->stop)
119 return -ENOSYS;
120
121 ret = ops->stop(dev);
122 if (ret == 0) {
123 struct wdt_priv *priv = dev_get_uclass_priv(dev);
124
125 priv->running = false;
126 }
127
128 return ret;
129}
130
131int wdt_stop_all(void)
132{
133 struct wdt_priv *priv;
134 struct udevice *dev;
135 struct uclass *uc;
136 int ret, err;
137
138 ret = uclass_get(UCLASS_WDT, &uc);
139 if (ret)
140 return ret;
141
142 uclass_foreach_dev(dev, uc) {
143 if (!device_active(dev))
144 continue;
145 priv = dev_get_uclass_priv(dev);
146 if (!priv->running)
147 continue;
148 err = wdt_stop(dev);
149 if (!ret)
150 ret = err;
151 }
152
153 return ret;
154}
155
156int wdt_reset(struct udevice *dev)
157{
158 const struct wdt_ops *ops = device_get_ops(dev);
159
160 if (!ops->reset)
161 return -ENOSYS;
162
163 return ops->reset(dev);
164}
165
166int wdt_expire_now(struct udevice *dev, ulong flags)
167{
168 int ret = 0;
169 const struct wdt_ops *ops;
170
171 debug("WDT Resetting: %lu\n", flags);
172 ops = device_get_ops(dev);
173 if (ops->expire_now) {
174 return ops->expire_now(dev, flags);
175 } else {
176 ret = wdt_start(dev, 1, flags);
177
178 if (ret < 0)
179 return ret;
180
181 hang();
182 }
183
184 return ret;
185}
186
187#if defined(CONFIG_WATCHDOG)
188
189
190
191
192
193void watchdog_reset(void)
194{
195 struct wdt_priv *priv;
196 struct udevice *dev;
197 struct uclass *uc;
198 ulong now;
199
200
201 if (!gd || !(gd->flags & GD_FLG_WDT_READY))
202 return;
203
204 if (uclass_get(UCLASS_WDT, &uc))
205 return;
206
207
208
209
210
211
212
213 uclass_foreach_dev(dev, uc) {
214 if (!device_active(dev))
215 continue;
216 priv = dev_get_uclass_priv(dev);
217 if (!priv->running)
218 continue;
219
220 now = get_timer(0);
221 if (time_after_eq(now, priv->next_reset)) {
222 priv->next_reset = now + priv->reset_period;
223 wdt_reset(dev);
224 }
225 }
226}
227#endif
228
229static int wdt_post_bind(struct udevice *dev)
230{
231#if defined(CONFIG_NEEDS_MANUAL_RELOC)
232 struct wdt_ops *ops = (struct wdt_ops *)device_get_ops(dev);
233 static int reloc_done;
234
235 if (!reloc_done) {
236 if (ops->start)
237 ops->start += gd->reloc_off;
238 if (ops->stop)
239 ops->stop += gd->reloc_off;
240 if (ops->reset)
241 ops->reset += gd->reloc_off;
242 if (ops->expire_now)
243 ops->expire_now += gd->reloc_off;
244
245 reloc_done++;
246 }
247#endif
248 return 0;
249}
250
251static int wdt_pre_probe(struct udevice *dev)
252{
253 u32 timeout = WATCHDOG_TIMEOUT_SECS;
254
255
256
257
258 ulong reset_period = 1000;
259 struct wdt_priv *priv;
260
261 if (CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)) {
262 timeout = dev_read_u32_default(dev, "timeout-sec", timeout);
263 reset_period = dev_read_u32_default(dev, "hw_margin_ms",
264 4 * reset_period) / 4;
265 }
266 priv = dev_get_uclass_priv(dev);
267 priv->timeout = timeout;
268 priv->reset_period = reset_period;
269
270
271
272
273 priv->next_reset = get_timer(0);
274
275 return 0;
276}
277
278UCLASS_DRIVER(wdt) = {
279 .id = UCLASS_WDT,
280 .name = "watchdog",
281 .flags = DM_UC_FLAG_SEQ_ALIAS,
282 .post_bind = wdt_post_bind,
283 .pre_probe = wdt_pre_probe,
284 .per_device_auto = sizeof(struct wdt_priv),
285};
286