watchdog: add pulse support to gpio watchdog driver

A common external watchdog circuit is kept alive by triggering a short
pulse on the reset pin. This patch adds support for this use case, while
making the algorithm configurable in the devicetree.

The "linux,wdt-gpio" driver being modified is based off the equivalent
driver in the Linux kernel, which provides support for this algorithm.
This patch brings parity to this driver, and is kept aligned with
the functionality and devicetree configuration in the kernel.

It should be noted that this adds a required property named 'hw_algo'
to the devicetree binding, following suit with the kernel. I'm happy to
make this backward-compatible if preferred.

Signed-off-by: Paul Doelle <paaull.git@gmail.com>
Reviewed-by: Stefan Roese <sr@denx.de>
diff --git a/drivers/watchdog/gpio_wdt.c b/drivers/watchdog/gpio_wdt.c
index 982a66b..fe06ec8 100644
--- a/drivers/watchdog/gpio_wdt.c
+++ b/drivers/watchdog/gpio_wdt.c
@@ -4,20 +4,38 @@
 #include <dm/device_compat.h>
 #include <wdt.h>
 #include <asm/gpio.h>
+#include <linux/delay.h>
+
+enum {
+	HW_ALGO_TOGGLE,
+	HW_ALGO_LEVEL,
+};
 
 struct gpio_wdt_priv {
-	struct gpio_desc gpio;
-	bool always_running;
-	int state;
+	struct		gpio_desc gpio;
+	unsigned int	hw_algo;
+	bool		always_running;
+	int		state;
 };
 
 static int gpio_wdt_reset(struct udevice *dev)
 {
 	struct gpio_wdt_priv *priv = dev_get_priv(dev);
 
-	priv->state = !priv->state;
-
-	return dm_gpio_set_value(&priv->gpio, priv->state);
+	switch (priv->hw_algo) {
+	case HW_ALGO_TOGGLE:
+		/* Toggle output pin */
+		priv->state = !priv->state;
+		dm_gpio_set_value(&priv->gpio, priv->state);
+		break;
+	case HW_ALGO_LEVEL:
+		/* Pulse */
+		dm_gpio_set_value(&priv->gpio, 1);
+		udelay(1);
+		dm_gpio_set_value(&priv->gpio, 0);
+		break;
+	}
+	return 0;
 }
 
 static int gpio_wdt_start(struct udevice *dev, u64 timeout, ulong flags)
@@ -34,6 +52,16 @@
 {
 	struct gpio_wdt_priv *priv = dev_get_priv(dev);
 	int ret;
+	const char *algo = dev_read_string(dev, "hw_algo");
+
+	if (!algo)
+		return -EINVAL;
+	if (!strcmp(algo, "toggle"))
+		priv->hw_algo = HW_ALGO_TOGGLE;
+	else if (!strcmp(algo, "level"))
+		priv->hw_algo = HW_ALGO_LEVEL;
+	else
+		return -EINVAL;
 
 	priv->always_running = dev_read_bool(dev, "always-running");
 	ret = gpio_request_by_name(dev, "gpios", 0, &priv->gpio, GPIOD_IS_OUT);