diff options
| author | Rasmus Villemoes <[email protected]> | 2024-10-02 21:23:23 +0200 |
|---|---|---|
| committer | Stefan Roese <[email protected]> | 2024-10-23 06:52:38 +0200 |
| commit | 7f3d53c8bf3b851837dc2f5dd85e0c6a0d0116a1 (patch) | |
| tree | 44db470acb9ffdd9d210dd7fca7d0a851ce25284 | |
| parent | 392ff1449f7d30cc48fd4d17320d05882cd68f2e (diff) | |
watchdog: gpio_wdt: add support for stoppable devices
Back when I added this driver in commit 2ac8490412c9, I wrote
The corresponding linux driver apparently has support for some
watchdog circuits which can be disabled by tri-stating the gpio, but I
have never actually encountered such a chip in the wild;
That has changed now; I have a board with just such a watchdog on my
desk currently. Add support for that.
- For a hw_algo="toggle" device, the gpio is requested as output if the
always-running flag is set, otherwise as input.
- The ->start() method is updated to change the direction to output when
required (i.e. it is not always-running).
- The ->stop() method is implemented, but of course reports failure if
always-running.
As I still haven't met any hw_algo="level" devices, I'm not entirely
sure how they fit in, but I'm borrowing logic from the corresponding
linux driver:
- In ->probe(), such devices always request the gpio as GPIOD_IS_OUT.
- In ->stop(), the linux driver has an "eternal ping" comment and sets
the gpio to (logic) high.
Stefan:
Added necessary changes in test/dm/wdt.c to fix CI build breakage, as
suggested by Rasmus.
Signed-off-by: Rasmus Villemoes <[email protected]>
Reviewed-by: Stefan Roese <[email protected]>
| -rw-r--r-- | drivers/watchdog/gpio_wdt.c | 27 | ||||
| -rw-r--r-- | test/dm/wdt.c | 4 |
2 files changed, 26 insertions, 5 deletions
diff --git a/drivers/watchdog/gpio_wdt.c b/drivers/watchdog/gpio_wdt.c index 2920c2c751f..e889861e917 100644 --- a/drivers/watchdog/gpio_wdt.c +++ b/drivers/watchdog/gpio_wdt.c @@ -45,14 +45,32 @@ static int gpio_wdt_start(struct udevice *dev, u64 timeout, ulong flags) if (priv->always_running) return 0; - return -ENOSYS; + dm_gpio_set_dir_flags(&priv->gpio, GPIOD_IS_OUT); + gpio_wdt_reset(dev); + + return 0; +} + +static int gpio_wdt_stop(struct udevice *dev) +{ + struct gpio_wdt_priv *priv = dev_get_priv(dev); + + if (priv->always_running) + return -EOPNOTSUPP; + + if (priv->hw_algo == HW_ALGO_TOGGLE) + dm_gpio_set_dir_flags(&priv->gpio, GPIOD_IS_IN); + else + dm_gpio_set_value(&priv->gpio, 1); + + return 0; } static int dm_probe(struct udevice *dev) { struct gpio_wdt_priv *priv = dev_get_priv(dev); - int ret; const char *algo = dev_read_string(dev, "hw_algo"); + int ret, flags; if (!algo) return -EINVAL; @@ -64,7 +82,9 @@ static int dm_probe(struct udevice *dev) return -EINVAL; priv->always_running = dev_read_bool(dev, "always-running"); - ret = gpio_request_by_name(dev, "gpios", 0, &priv->gpio, GPIOD_IS_OUT); + flags = priv->always_running || priv->hw_algo == HW_ALGO_LEVEL ? + GPIOD_IS_OUT : GPIOD_IS_IN; + ret = gpio_request_by_name(dev, "gpios", 0, &priv->gpio, flags); if (ret < 0) { dev_err(dev, "Request for wdt gpio failed: %d\n", ret); return ret; @@ -78,6 +98,7 @@ static int dm_probe(struct udevice *dev) static const struct wdt_ops gpio_wdt_ops = { .start = gpio_wdt_start, + .stop = gpio_wdt_stop, .reset = gpio_wdt_reset, }; diff --git a/test/dm/wdt.c b/test/dm/wdt.c index 541bcba1b53..710414881fa 100644 --- a/test/dm/wdt.c +++ b/test/dm/wdt.c @@ -71,7 +71,7 @@ static int dm_test_wdt_gpio_toggle(struct unit_test_state *uts) ut_assertok(wdt_reset(wdt)); ut_asserteq(val, sandbox_gpio_get_value(gpio, offset)); - ut_asserteq(-ENOSYS, wdt_stop(wdt)); + ut_asserteq(-EOPNOTSUPP, wdt_stop(wdt)); return 0; } @@ -103,7 +103,7 @@ static int dm_test_wdt_gpio_level(struct unit_test_state *uts) ut_assertok(wdt_reset(wdt)); ut_asserteq(val, sandbox_gpio_get_value(gpio, offset)); - ut_asserteq(-ENOSYS, wdt_stop(wdt)); + ut_asserteq(-EOPNOTSUPP, wdt_stop(wdt)); return 0; } |
