blob: 10be334e9ed3665c8da237a1129cc6fb6a4f6a54 [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
Stefan Roesecce24722022-08-18 13:22:46 +02008#include <cyclic.h>
Chanho Parkdfd30122023-12-03 17:30:40 +09009#include <div64.h>
maxims@google.comdaea6d42017-04-17 12:00:21 -070010#include <dm.h>
11#include <errno.h>
Simon Glassf11478f2019-12-28 10:45:07 -070012#include <hang.h>
Simon Glass0f2af882020-05-10 11:40:05 -060013#include <log.h>
Samuel Holland3a8713a2021-11-03 22:55:14 -050014#include <sysreset.h>
Chris Packham32a234f2020-02-24 13:20:33 +130015#include <time.h>
maxims@google.comdaea6d42017-04-17 12:00:21 -070016#include <wdt.h>
Simon Glass3ba929a2020-10-30 21:38:53 -060017#include <asm/global_data.h>
maxims@google.comdaea6d42017-04-17 12:00:21 -070018#include <dm/device-internal.h>
19#include <dm/lists.h>
Rasmus Villemoesea36ada2024-05-21 10:46:52 +020020#include <linux/kernel.h>
maxims@google.comdaea6d42017-04-17 12:00:21 -070021
Stefan Roese502acb02019-04-11 15:58:44 +020022DECLARE_GLOBAL_DATA_PTR;
23
Rasmus Villemoesf7c2e9f2020-03-13 17:04:57 +010024#define WATCHDOG_TIMEOUT_SECS (CONFIG_WATCHDOG_TIMEOUT_MSECS / 1000)
25
Rasmus Villemoes635f9242021-08-19 11:56:56 +020026struct wdt_priv {
Rasmus Villemoesea36ada2024-05-21 10:46:52 +020027 /* The udevice owning this wdt_priv. */
28 struct udevice *dev;
Rasmus Villemoes635f9242021-08-19 11:56:56 +020029 /* Timeout, in seconds, to configure this device to. */
30 u32 timeout;
31 /*
32 * Time, in milliseconds, between calling the device's ->reset()
Rasmus Villemoes4733b8d2024-05-28 13:13:20 +020033 * method from schedule().
Rasmus Villemoes635f9242021-08-19 11:56:56 +020034 */
35 ulong reset_period;
36 /*
37 * Next time (as returned by get_timer(0)) to call
38 * ->reset().
39 */
40 ulong next_reset;
Rasmus Villemoes85fb8222021-08-19 11:56:59 +020041 /* Whether watchdog_start() has been called on the device. */
42 bool running;
Rasmus Villemoesce66f192022-09-27 11:54:03 +020043 /* autostart */
44 bool autostart;
Stefan Roesecce24722022-08-18 13:22:46 +020045
Rasmus Villemoesea36ada2024-05-21 10:46:52 +020046 struct cyclic_info cyclic;
Rasmus Villemoes635f9242021-08-19 11:56:56 +020047};
Rasmus Villemoes2ba843d2020-03-13 17:04:58 +010048
Rasmus Villemoesea36ada2024-05-21 10:46:52 +020049static void wdt_cyclic(struct cyclic_info *c)
Stefan Roesecce24722022-08-18 13:22:46 +020050{
Rasmus Villemoesea36ada2024-05-21 10:46:52 +020051 struct wdt_priv *priv = container_of(c, struct wdt_priv, cyclic);
52 struct udevice *dev = priv->dev;
Stefan Roesecce24722022-08-18 13:22:46 +020053
54 if (!device_active(dev))
55 return;
56
Stefan Roesecce24722022-08-18 13:22:46 +020057 if (!priv->running)
58 return;
59
60 wdt_reset(dev);
61}
62
Rasmus Villemoesc2453182021-08-19 11:56:58 +020063static void init_watchdog_dev(struct udevice *dev)
Rasmus Villemoesf7c2e9f2020-03-13 17:04:57 +010064{
Rasmus Villemoes635f9242021-08-19 11:56:56 +020065 struct wdt_priv *priv;
Pali Rohárc80b8212021-03-09 14:26:55 +010066 int ret;
Rasmus Villemoesf7c2e9f2020-03-13 17:04:57 +010067
Rasmus Villemoesc2453182021-08-19 11:56:58 +020068 priv = dev_get_uclass_priv(dev);
69
Samuel Holland3a8713a2021-11-03 22:55:14 -050070 if (IS_ENABLED(CONFIG_SYSRESET_WATCHDOG_AUTO)) {
71 ret = sysreset_register_wdt(dev);
72 if (ret)
73 printf("WDT: Failed to register %s for sysreset\n",
74 dev->name);
75 }
76
Rasmus Villemoesce66f192022-09-27 11:54:03 +020077 if (!priv->autostart) {
Rasmus Villemoesc2453182021-08-19 11:56:58 +020078 printf("WDT: Not starting %s\n", dev->name);
79 return;
80 }
81
82 ret = wdt_start(dev, priv->timeout * 1000, 0);
83 if (ret != 0) {
84 printf("WDT: Failed to start %s\n", dev->name);
85 return;
86 }
Rasmus Villemoesc2453182021-08-19 11:56:58 +020087}
88
89int initr_watchdog(void)
90{
Rasmus Villemoes3be6a352021-08-19 11:57:03 +020091 struct udevice *dev;
92 struct uclass *uc;
93 int ret;
94
95 ret = uclass_get(UCLASS_WDT, &uc);
96 if (ret) {
97 log_debug("Error getting UCLASS_WDT: %d\n", ret);
98 return 0;
99 }
100
101 uclass_foreach_dev(dev, uc) {
102 ret = device_probe(dev);
103 if (ret) {
104 log_debug("Error probing %s: %d\n", dev->name, ret);
105 continue;
Rasmus Villemoesf7c2e9f2020-03-13 17:04:57 +0100106 }
Rasmus Villemoes3be6a352021-08-19 11:57:03 +0200107 init_watchdog_dev(dev);
Rasmus Villemoesf7c2e9f2020-03-13 17:04:57 +0100108 }
Rasmus Villemoesf7c2e9f2020-03-13 17:04:57 +0100109
110 return 0;
111}
112
Andy Shevchenko2befb4b2017-08-04 15:48:28 -0600113int wdt_start(struct udevice *dev, u64 timeout_ms, ulong flags)
maxims@google.comdaea6d42017-04-17 12:00:21 -0700114{
115 const struct wdt_ops *ops = device_get_ops(dev);
Pali Rohár8a8a5ad2021-03-09 14:26:54 +0100116 int ret;
maxims@google.comdaea6d42017-04-17 12:00:21 -0700117
118 if (!ops->start)
119 return -ENOSYS;
120
Pali Rohár8a8a5ad2021-03-09 14:26:54 +0100121 ret = ops->start(dev, timeout_ms, flags);
Rasmus Villemoes85fb8222021-08-19 11:56:59 +0200122 if (ret == 0) {
123 struct wdt_priv *priv = dev_get_uclass_priv(dev);
Stefan Roesecce24722022-08-18 13:22:46 +0200124 char str[16];
Rasmus Villemoes85fb8222021-08-19 11:56:59 +0200125
Stefan Roesecce24722022-08-18 13:22:46 +0200126 memset(str, 0, 16);
127 if (IS_ENABLED(CONFIG_WATCHDOG)) {
Rasmus Villemoes93a82ce2024-05-21 10:46:51 +0200128 if (priv->running)
Rasmus Villemoesea36ada2024-05-21 10:46:52 +0200129 cyclic_unregister(&priv->cyclic);
Rasmus Villemoes93a82ce2024-05-21 10:46:51 +0200130
Stefan Roesecce24722022-08-18 13:22:46 +0200131 /* Register the watchdog driver as a cyclic function */
Rasmus Villemoesea36ada2024-05-21 10:46:52 +0200132 cyclic_register(&priv->cyclic, wdt_cyclic,
133 priv->reset_period * 1000,
134 dev->name);
135
136 snprintf(str, 16, "every %ldms", priv->reset_period);
Stefan Roesecce24722022-08-18 13:22:46 +0200137 }
138
Rasmus Villemoes93a82ce2024-05-21 10:46:51 +0200139 priv->running = true;
Stefan Roesecce24722022-08-18 13:22:46 +0200140 printf("WDT: Started %s with%s servicing %s (%ds timeout)\n",
141 dev->name, IS_ENABLED(CONFIG_WATCHDOG) ? "" : "out",
Chanho Parkdfd30122023-12-03 17:30:40 +0900142 str, (u32)lldiv(timeout_ms, 1000));
Rasmus Villemoes85fb8222021-08-19 11:56:59 +0200143 }
Pali Rohár8a8a5ad2021-03-09 14:26:54 +0100144
145 return ret;
maxims@google.comdaea6d42017-04-17 12:00:21 -0700146}
147
148int wdt_stop(struct udevice *dev)
149{
150 const struct wdt_ops *ops = device_get_ops(dev);
Pali Rohár8a8a5ad2021-03-09 14:26:54 +0100151 int ret;
maxims@google.comdaea6d42017-04-17 12:00:21 -0700152
153 if (!ops->stop)
154 return -ENOSYS;
155
Pali Rohár8a8a5ad2021-03-09 14:26:54 +0100156 ret = ops->stop(dev);
Rasmus Villemoes85fb8222021-08-19 11:56:59 +0200157 if (ret == 0) {
158 struct wdt_priv *priv = dev_get_uclass_priv(dev);
159
Rasmus Villemoes93a82ce2024-05-21 10:46:51 +0200160 if (IS_ENABLED(CONFIG_WATCHDOG) && priv->running)
Rasmus Villemoesea36ada2024-05-21 10:46:52 +0200161 cyclic_unregister(&priv->cyclic);
Rasmus Villemoes93a82ce2024-05-21 10:46:51 +0200162
Rasmus Villemoes85fb8222021-08-19 11:56:59 +0200163 priv->running = false;
164 }
Pali Rohár8a8a5ad2021-03-09 14:26:54 +0100165
166 return ret;
maxims@google.comdaea6d42017-04-17 12:00:21 -0700167}
168
Rasmus Villemoesbeb8c2f2021-08-19 11:57:01 +0200169int wdt_stop_all(void)
170{
171 struct wdt_priv *priv;
172 struct udevice *dev;
173 struct uclass *uc;
174 int ret, err;
175
176 ret = uclass_get(UCLASS_WDT, &uc);
177 if (ret)
178 return ret;
179
180 uclass_foreach_dev(dev, uc) {
181 if (!device_active(dev))
182 continue;
183 priv = dev_get_uclass_priv(dev);
184 if (!priv->running)
185 continue;
186 err = wdt_stop(dev);
187 if (!ret)
188 ret = err;
189 }
190
191 return ret;
192}
193
maxims@google.comdaea6d42017-04-17 12:00:21 -0700194int wdt_reset(struct udevice *dev)
195{
196 const struct wdt_ops *ops = device_get_ops(dev);
197
198 if (!ops->reset)
199 return -ENOSYS;
200
201 return ops->reset(dev);
202}
203
204int wdt_expire_now(struct udevice *dev, ulong flags)
205{
206 int ret = 0;
207 const struct wdt_ops *ops;
208
Andy Shevchenkof047be42017-07-05 20:44:06 +0300209 debug("WDT Resetting: %lu\n", flags);
maxims@google.comdaea6d42017-04-17 12:00:21 -0700210 ops = device_get_ops(dev);
211 if (ops->expire_now) {
212 return ops->expire_now(dev, flags);
213 } else {
Rasmus Villemoesf008c302021-08-19 11:56:55 +0200214 ret = wdt_start(dev, 1, flags);
maxims@google.comdaea6d42017-04-17 12:00:21 -0700215
maxims@google.comdaea6d42017-04-17 12:00:21 -0700216 if (ret < 0)
217 return ret;
218
219 hang();
220 }
221
222 return ret;
223}
Stefan Roese502acb02019-04-11 15:58:44 +0200224
Rasmus Villemoes635f9242021-08-19 11:56:56 +0200225static int wdt_pre_probe(struct udevice *dev)
226{
227 u32 timeout = WATCHDOG_TIMEOUT_SECS;
228 /*
229 * Reset every 1000ms, or however often is required as
230 * indicated by a hw_margin_ms property.
231 */
232 ulong reset_period = 1000;
Rasmus Villemoesce66f192022-09-27 11:54:03 +0200233 bool autostart = IS_ENABLED(CONFIG_WATCHDOG_AUTOSTART);
Rasmus Villemoes635f9242021-08-19 11:56:56 +0200234 struct wdt_priv *priv;
235
236 if (CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)) {
237 timeout = dev_read_u32_default(dev, "timeout-sec", timeout);
238 reset_period = dev_read_u32_default(dev, "hw_margin_ms",
239 4 * reset_period) / 4;
Rasmus Villemoesce66f192022-09-27 11:54:03 +0200240 if (dev_read_bool(dev, "u-boot,noautostart"))
241 autostart = false;
242 else if (dev_read_bool(dev, "u-boot,autostart"))
243 autostart = true;
Rasmus Villemoes635f9242021-08-19 11:56:56 +0200244 }
245 priv = dev_get_uclass_priv(dev);
Rasmus Villemoesea36ada2024-05-21 10:46:52 +0200246 priv->dev = dev;
Rasmus Villemoes635f9242021-08-19 11:56:56 +0200247 priv->timeout = timeout;
248 priv->reset_period = reset_period;
Rasmus Villemoesce66f192022-09-27 11:54:03 +0200249 priv->autostart = autostart;
Rasmus Villemoes635f9242021-08-19 11:56:56 +0200250 /*
251 * Pretend this device was last reset "long" ago so the first
Rasmus Villemoes4733b8d2024-05-28 13:13:20 +0200252 * schedule() will actually call its ->reset method.
Rasmus Villemoes635f9242021-08-19 11:56:56 +0200253 */
254 priv->next_reset = get_timer(0);
255
256 return 0;
257}
258
maxims@google.comdaea6d42017-04-17 12:00:21 -0700259UCLASS_DRIVER(wdt) = {
Rasmus Villemoes1e42d372021-08-19 11:56:57 +0200260 .id = UCLASS_WDT,
261 .name = "watchdog",
262 .flags = DM_UC_FLAG_SEQ_ALIAS,
Rasmus Villemoes635f9242021-08-19 11:56:56 +0200263 .pre_probe = wdt_pre_probe,
264 .per_device_auto = sizeof(struct wdt_priv),
maxims@google.comdaea6d42017-04-17 12:00:21 -0700265};