blob: c96a596f08c124788bec01f25cbdebb1ffb9265b [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
6#include <common.h>
7#include <dm.h>
8#include <errno.h>
Simon Glassf11478f2019-12-28 10:45:07 -07009#include <hang.h>
Chris Packham32a234f2020-02-24 13:20:33 +130010#include <time.h>
maxims@google.comdaea6d42017-04-17 12:00:21 -070011#include <wdt.h>
12#include <dm/device-internal.h>
13#include <dm/lists.h>
14
Stefan Roese502acb02019-04-11 15:58:44 +020015DECLARE_GLOBAL_DATA_PTR;
16
Rasmus Villemoesf7c2e9f2020-03-13 17:04:57 +010017#define WATCHDOG_TIMEOUT_SECS (CONFIG_WATCHDOG_TIMEOUT_MSECS / 1000)
18
19int initr_watchdog(void)
20{
21 u32 timeout = WATCHDOG_TIMEOUT_SECS;
22
23 /*
24 * Init watchdog: This will call the probe function of the
25 * watchdog driver, enabling the use of the device
26 */
27 if (uclass_get_device_by_seq(UCLASS_WDT, 0,
28 (struct udevice **)&gd->watchdog_dev)) {
29 debug("WDT: Not found by seq!\n");
30 if (uclass_get_device(UCLASS_WDT, 0,
31 (struct udevice **)&gd->watchdog_dev)) {
32 printf("WDT: Not found!\n");
33 return 0;
34 }
35 }
36
37 if (CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)) {
38 timeout = dev_read_u32_default(gd->watchdog_dev, "timeout-sec",
39 WATCHDOG_TIMEOUT_SECS);
40 }
41
42 wdt_start(gd->watchdog_dev, timeout * 1000, 0);
43 gd->flags |= GD_FLG_WDT_READY;
44 printf("WDT: Started with%s servicing (%ds timeout)\n",
45 IS_ENABLED(CONFIG_WATCHDOG) ? "" : "out", timeout);
46
47 return 0;
48}
49
Andy Shevchenko2befb4b2017-08-04 15:48:28 -060050int wdt_start(struct udevice *dev, u64 timeout_ms, ulong flags)
maxims@google.comdaea6d42017-04-17 12:00:21 -070051{
52 const struct wdt_ops *ops = device_get_ops(dev);
53
54 if (!ops->start)
55 return -ENOSYS;
56
Andy Shevchenko2befb4b2017-08-04 15:48:28 -060057 return ops->start(dev, timeout_ms, flags);
maxims@google.comdaea6d42017-04-17 12:00:21 -070058}
59
60int wdt_stop(struct udevice *dev)
61{
62 const struct wdt_ops *ops = device_get_ops(dev);
63
64 if (!ops->stop)
65 return -ENOSYS;
66
67 return ops->stop(dev);
68}
69
70int wdt_reset(struct udevice *dev)
71{
72 const struct wdt_ops *ops = device_get_ops(dev);
73
74 if (!ops->reset)
75 return -ENOSYS;
76
77 return ops->reset(dev);
78}
79
80int wdt_expire_now(struct udevice *dev, ulong flags)
81{
82 int ret = 0;
83 const struct wdt_ops *ops;
84
Andy Shevchenkof047be42017-07-05 20:44:06 +030085 debug("WDT Resetting: %lu\n", flags);
maxims@google.comdaea6d42017-04-17 12:00:21 -070086 ops = device_get_ops(dev);
87 if (ops->expire_now) {
88 return ops->expire_now(dev, flags);
89 } else {
90 if (!ops->start)
91 return -ENOSYS;
92
93 ret = ops->start(dev, 1, flags);
94 if (ret < 0)
95 return ret;
96
97 hang();
98 }
99
100 return ret;
101}
Stefan Roese502acb02019-04-11 15:58:44 +0200102
103#if defined(CONFIG_WATCHDOG)
104/*
105 * Called by macro WATCHDOG_RESET. This function be called *very* early,
106 * so we need to make sure, that the watchdog driver is ready before using
107 * it in this function.
108 */
109void watchdog_reset(void)
110{
111 static ulong next_reset;
112 ulong now;
113
114 /* Exit if GD is not ready or watchdog is not initialized yet */
115 if (!gd || !(gd->flags & GD_FLG_WDT_READY))
116 return;
117
118 /* Do not reset the watchdog too often */
119 now = get_timer(0);
Chris Packham32a234f2020-02-24 13:20:33 +1300120 if (time_after(now, next_reset)) {
Stefan Roese502acb02019-04-11 15:58:44 +0200121 next_reset = now + 1000; /* reset every 1000ms */
122 wdt_reset(gd->watchdog_dev);
123 }
124}
125#endif
maxims@google.comdaea6d42017-04-17 12:00:21 -0700126
Michal Simek7b5eca92018-07-11 15:42:25 +0200127static int wdt_post_bind(struct udevice *dev)
128{
129#if defined(CONFIG_NEEDS_MANUAL_RELOC)
130 struct wdt_ops *ops = (struct wdt_ops *)device_get_ops(dev);
131 static int reloc_done;
132
133 if (!reloc_done) {
134 if (ops->start)
135 ops->start += gd->reloc_off;
136 if (ops->stop)
137 ops->stop += gd->reloc_off;
138 if (ops->reset)
139 ops->reset += gd->reloc_off;
140 if (ops->expire_now)
141 ops->expire_now += gd->reloc_off;
142
143 reloc_done++;
144 }
145#endif
146 return 0;
147}
148
maxims@google.comdaea6d42017-04-17 12:00:21 -0700149UCLASS_DRIVER(wdt) = {
150 .id = UCLASS_WDT,
Michal Simekfc61be32018-07-11 08:24:43 +0200151 .name = "watchdog",
152 .flags = DM_UC_FLAG_SEQ_ALIAS,
Michal Simek7b5eca92018-07-11 15:42:25 +0200153 .post_bind = wdt_post_bind,
maxims@google.comdaea6d42017-04-17 12:00:21 -0700154};