blob: 7570710c4d91aa400af3243a92731a64d8c3bad5 [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
maxims@google.comdaea6d42017-04-17 12:00:21 -07002/*
3 * Copyright 2017 Google, Inc
maxims@google.comdaea6d42017-04-17 12:00:21 -07004 */
5
Patrick Delaunay81313352021-04-27 11:02:19 +02006#define LOG_CATEGORY UCLASS_WDT
7
maxims@google.comdaea6d42017-04-17 12:00:21 -07008#include <common.h>
9#include <dm.h>
10#include <errno.h>
Simon Glassf11478f2019-12-28 10:45:07 -070011#include <hang.h>
Simon Glass0f2af882020-05-10 11:40:05 -060012#include <log.h>
Chris Packham32a234f2020-02-24 13:20:33 +130013#include <time.h>
maxims@google.comdaea6d42017-04-17 12:00:21 -070014#include <wdt.h>
Simon Glass3ba929a2020-10-30 21:38:53 -060015#include <asm/global_data.h>
maxims@google.comdaea6d42017-04-17 12:00:21 -070016#include <dm/device-internal.h>
17#include <dm/lists.h>
18
Stefan Roese502acb02019-04-11 15:58:44 +020019DECLARE_GLOBAL_DATA_PTR;
20
Rasmus Villemoesf7c2e9f2020-03-13 17:04:57 +010021#define WATCHDOG_TIMEOUT_SECS (CONFIG_WATCHDOG_TIMEOUT_MSECS / 1000)
22
Rasmus Villemoes635f9242021-08-19 11:56:56 +020023struct wdt_priv {
24 /* Timeout, in seconds, to configure this device to. */
25 u32 timeout;
26 /*
27 * Time, in milliseconds, between calling the device's ->reset()
28 * method from watchdog_reset().
29 */
30 ulong reset_period;
31 /*
32 * Next time (as returned by get_timer(0)) to call
33 * ->reset().
34 */
35 ulong next_reset;
Rasmus Villemoes85fb8222021-08-19 11:56:59 +020036 /* Whether watchdog_start() has been called on the device. */
37 bool running;
Rasmus Villemoes635f9242021-08-19 11:56:56 +020038};
Rasmus Villemoes2ba843d2020-03-13 17:04:58 +010039
Rasmus Villemoesc2453182021-08-19 11:56:58 +020040static void init_watchdog_dev(struct udevice *dev)
Rasmus Villemoesf7c2e9f2020-03-13 17:04:57 +010041{
Rasmus Villemoes635f9242021-08-19 11:56:56 +020042 struct wdt_priv *priv;
Pali Rohárc80b8212021-03-09 14:26:55 +010043 int ret;
Rasmus Villemoesf7c2e9f2020-03-13 17:04:57 +010044
Rasmus Villemoesc2453182021-08-19 11:56:58 +020045 priv = dev_get_uclass_priv(dev);
46
47 if (!IS_ENABLED(CONFIG_WATCHDOG_AUTOSTART)) {
48 printf("WDT: Not starting %s\n", dev->name);
49 return;
50 }
51
52 ret = wdt_start(dev, priv->timeout * 1000, 0);
53 if (ret != 0) {
54 printf("WDT: Failed to start %s\n", dev->name);
55 return;
56 }
57
58 printf("WDT: Started %s with%s servicing (%ds timeout)\n", dev->name,
59 IS_ENABLED(CONFIG_WATCHDOG) ? "" : "out", priv->timeout);
60}
61
62int initr_watchdog(void)
63{
Rasmus Villemoes3be6a352021-08-19 11:57:03 +020064 struct udevice *dev;
65 struct uclass *uc;
66 int ret;
67
68 ret = uclass_get(UCLASS_WDT, &uc);
69 if (ret) {
70 log_debug("Error getting UCLASS_WDT: %d\n", ret);
71 return 0;
72 }
73
74 uclass_foreach_dev(dev, uc) {
75 ret = device_probe(dev);
76 if (ret) {
77 log_debug("Error probing %s: %d\n", dev->name, ret);
78 continue;
Rasmus Villemoesf7c2e9f2020-03-13 17:04:57 +010079 }
Rasmus Villemoes3be6a352021-08-19 11:57:03 +020080 init_watchdog_dev(dev);
Rasmus Villemoesf7c2e9f2020-03-13 17:04:57 +010081 }
Rasmus Villemoesf7c2e9f2020-03-13 17:04:57 +010082
Rasmus Villemoes85fb8222021-08-19 11:56:59 +020083 gd->flags |= GD_FLG_WDT_READY;
Rasmus Villemoesf7c2e9f2020-03-13 17:04:57 +010084 return 0;
85}
86
Andy Shevchenko2befb4b2017-08-04 15:48:28 -060087int wdt_start(struct udevice *dev, u64 timeout_ms, ulong flags)
maxims@google.comdaea6d42017-04-17 12:00:21 -070088{
89 const struct wdt_ops *ops = device_get_ops(dev);
Pali Rohár8a8a5ad2021-03-09 14:26:54 +010090 int ret;
maxims@google.comdaea6d42017-04-17 12:00:21 -070091
92 if (!ops->start)
93 return -ENOSYS;
94
Pali Rohár8a8a5ad2021-03-09 14:26:54 +010095 ret = ops->start(dev, timeout_ms, flags);
Rasmus Villemoes85fb8222021-08-19 11:56:59 +020096 if (ret == 0) {
97 struct wdt_priv *priv = dev_get_uclass_priv(dev);
98
99 priv->running = true;
100 }
Pali Rohár8a8a5ad2021-03-09 14:26:54 +0100101
102 return ret;
maxims@google.comdaea6d42017-04-17 12:00:21 -0700103}
104
105int wdt_stop(struct udevice *dev)
106{
107 const struct wdt_ops *ops = device_get_ops(dev);
Pali Rohár8a8a5ad2021-03-09 14:26:54 +0100108 int ret;
maxims@google.comdaea6d42017-04-17 12:00:21 -0700109
110 if (!ops->stop)
111 return -ENOSYS;
112
Pali Rohár8a8a5ad2021-03-09 14:26:54 +0100113 ret = ops->stop(dev);
Rasmus Villemoes85fb8222021-08-19 11:56:59 +0200114 if (ret == 0) {
115 struct wdt_priv *priv = dev_get_uclass_priv(dev);
116
117 priv->running = false;
118 }
Pali Rohár8a8a5ad2021-03-09 14:26:54 +0100119
120 return ret;
maxims@google.comdaea6d42017-04-17 12:00:21 -0700121}
122
Rasmus Villemoesbeb8c2f2021-08-19 11:57:01 +0200123int wdt_stop_all(void)
124{
125 struct wdt_priv *priv;
126 struct udevice *dev;
127 struct uclass *uc;
128 int ret, err;
129
130 ret = uclass_get(UCLASS_WDT, &uc);
131 if (ret)
132 return ret;
133
134 uclass_foreach_dev(dev, uc) {
135 if (!device_active(dev))
136 continue;
137 priv = dev_get_uclass_priv(dev);
138 if (!priv->running)
139 continue;
140 err = wdt_stop(dev);
141 if (!ret)
142 ret = err;
143 }
144
145 return ret;
146}
147
maxims@google.comdaea6d42017-04-17 12:00:21 -0700148int wdt_reset(struct udevice *dev)
149{
150 const struct wdt_ops *ops = device_get_ops(dev);
151
152 if (!ops->reset)
153 return -ENOSYS;
154
155 return ops->reset(dev);
156}
157
158int wdt_expire_now(struct udevice *dev, ulong flags)
159{
160 int ret = 0;
161 const struct wdt_ops *ops;
162
Andy Shevchenkof047be42017-07-05 20:44:06 +0300163 debug("WDT Resetting: %lu\n", flags);
maxims@google.comdaea6d42017-04-17 12:00:21 -0700164 ops = device_get_ops(dev);
165 if (ops->expire_now) {
166 return ops->expire_now(dev, flags);
167 } else {
Rasmus Villemoesf008c302021-08-19 11:56:55 +0200168 ret = wdt_start(dev, 1, flags);
maxims@google.comdaea6d42017-04-17 12:00:21 -0700169
maxims@google.comdaea6d42017-04-17 12:00:21 -0700170 if (ret < 0)
171 return ret;
172
173 hang();
174 }
175
176 return ret;
177}
Stefan Roese502acb02019-04-11 15:58:44 +0200178
179#if defined(CONFIG_WATCHDOG)
180/*
181 * Called by macro WATCHDOG_RESET. This function be called *very* early,
182 * so we need to make sure, that the watchdog driver is ready before using
183 * it in this function.
184 */
185void watchdog_reset(void)
186{
Rasmus Villemoes635f9242021-08-19 11:56:56 +0200187 struct wdt_priv *priv;
188 struct udevice *dev;
Rasmus Villemoes3be6a352021-08-19 11:57:03 +0200189 struct uclass *uc;
Stefan Roese502acb02019-04-11 15:58:44 +0200190 ulong now;
191
192 /* Exit if GD is not ready or watchdog is not initialized yet */
193 if (!gd || !(gd->flags & GD_FLG_WDT_READY))
194 return;
195
Rasmus Villemoes3be6a352021-08-19 11:57:03 +0200196 if (uclass_get(UCLASS_WDT, &uc))
Rasmus Villemoes85fb8222021-08-19 11:56:59 +0200197 return;
198
Rasmus Villemoes3be6a352021-08-19 11:57:03 +0200199 /*
200 * All devices bound to the wdt uclass should have been probed
201 * in initr_watchdog(). But just in case something went wrong,
202 * check device_active() before accessing the uclass private
203 * data.
204 */
205 uclass_foreach_dev(dev, uc) {
206 if (!device_active(dev))
207 continue;
208 priv = dev_get_uclass_priv(dev);
209 if (!priv->running)
210 continue;
211 /* Do not reset the watchdog too often */
212 now = get_timer(0);
213 if (time_after_eq(now, priv->next_reset)) {
214 priv->next_reset = now + priv->reset_period;
215 wdt_reset(dev);
216 }
Stefan Roese502acb02019-04-11 15:58:44 +0200217 }
218}
219#endif
maxims@google.comdaea6d42017-04-17 12:00:21 -0700220
Michal Simek7b5eca92018-07-11 15:42:25 +0200221static int wdt_post_bind(struct udevice *dev)
222{
223#if defined(CONFIG_NEEDS_MANUAL_RELOC)
224 struct wdt_ops *ops = (struct wdt_ops *)device_get_ops(dev);
225 static int reloc_done;
226
227 if (!reloc_done) {
228 if (ops->start)
229 ops->start += gd->reloc_off;
230 if (ops->stop)
231 ops->stop += gd->reloc_off;
232 if (ops->reset)
233 ops->reset += gd->reloc_off;
234 if (ops->expire_now)
235 ops->expire_now += gd->reloc_off;
236
237 reloc_done++;
238 }
239#endif
240 return 0;
241}
242
Rasmus Villemoes635f9242021-08-19 11:56:56 +0200243static int wdt_pre_probe(struct udevice *dev)
244{
245 u32 timeout = WATCHDOG_TIMEOUT_SECS;
246 /*
247 * Reset every 1000ms, or however often is required as
248 * indicated by a hw_margin_ms property.
249 */
250 ulong reset_period = 1000;
251 struct wdt_priv *priv;
252
253 if (CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)) {
254 timeout = dev_read_u32_default(dev, "timeout-sec", timeout);
255 reset_period = dev_read_u32_default(dev, "hw_margin_ms",
256 4 * reset_period) / 4;
257 }
258 priv = dev_get_uclass_priv(dev);
259 priv->timeout = timeout;
260 priv->reset_period = reset_period;
261 /*
262 * Pretend this device was last reset "long" ago so the first
263 * watchdog_reset will actually call its ->reset method.
264 */
265 priv->next_reset = get_timer(0);
266
267 return 0;
268}
269
maxims@google.comdaea6d42017-04-17 12:00:21 -0700270UCLASS_DRIVER(wdt) = {
Rasmus Villemoes1e42d372021-08-19 11:56:57 +0200271 .id = UCLASS_WDT,
272 .name = "watchdog",
273 .flags = DM_UC_FLAG_SEQ_ALIAS,
274 .post_bind = wdt_post_bind,
Rasmus Villemoes635f9242021-08-19 11:56:56 +0200275 .pre_probe = wdt_pre_probe,
276 .per_device_auto = sizeof(struct wdt_priv),
maxims@google.comdaea6d42017-04-17 12:00:21 -0700277};