diff mbox series

[v2] da9063: Add watchdog support

Message ID 20240917134127.3152578-1-festevam@gmail.com
State Superseded
Headers show
Series [v2] da9063: Add watchdog support | expand

Commit Message

Fabio Estevam Sept. 17, 2024, 1:41 p.m. UTC
From: Fabio Estevam <festevam@denx.de>

The DA9063 PMIC is a multi-function device that provides
regulator, watchdog, RTC, and ON key functionalities.

Add support for the DA9063 PMIC watchdog functionality.

Based on the 6.11 kernel drivers/watchdog/da9063_wdt.c driver.

Signed-off-by: Fabio Estevam <festevam@denx.de>
---
Changes since v1:
- Fixed a typo in commit log: RTC -> watchdog.

 drivers/power/pmic/da9063.c   |  12 ++-
 drivers/watchdog/Kconfig      |   6 ++
 drivers/watchdog/Makefile     |   1 +
 drivers/watchdog/da9063-wdt.c | 142 ++++++++++++++++++++++++++++++++++
 4 files changed, 159 insertions(+), 2 deletions(-)
 create mode 100644 drivers/watchdog/da9063-wdt.c
diff mbox series

Patch

diff --git a/drivers/power/pmic/da9063.c b/drivers/power/pmic/da9063.c
index 7bd3df39142..59c65702863 100644
--- a/drivers/power/pmic/da9063.c
+++ b/drivers/power/pmic/da9063.c
@@ -7,6 +7,9 @@ 
 #include <fdtdec.h>
 #include <errno.h>
 #include <dm.h>
+#include <dm/device-internal.h>
+#include <dm/device_compat.h>
+#include <dm/lists.h>
 #include <i2c.h>
 #include <log.h>
 #include <linux/printk.h>
@@ -86,6 +89,7 @@  static int da9063_read(struct udevice *dev, uint reg, uint8_t *buff, int len)
 static int da9063_bind(struct udevice *dev)
 {
 	ofnode regulators_node;
+	struct driver *drv;
 	int children;
 
 	regulators_node = dev_read_subnode(dev, "regulators");
@@ -101,8 +105,12 @@  static int da9063_bind(struct udevice *dev)
 	if (!children)
 		debug("%s: %s - no child found\n", __func__, dev->name);
 
-	/* Always return success for this device */
-	return 0;
+	drv = lists_driver_lookup_name("da9063-wdt");
+	if (!drv)
+		return 0;
+
+	return device_bind_with_driver_data(dev, drv, "da9063-wdt", dev->driver_data,
+					    dev_ofnode(dev), &dev);
 }
 
 static int da9063_probe(struct udevice *dev)
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 0c3e9913318..90bc5653ee3 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -169,6 +169,12 @@  config WDT_CORTINA
 	  This driver support all CPU ISAs supported by Cortina
 	  Access CAxxxx SoCs.
 
+config WDT_DA9063
+	bool "DA9063 watchdog timer support"
+	depends on WDT && DM_PMIC_DA9063
+	help
+	  Enable support for the watchdog timer in Dialog DA9063.
+
 config WDT_GPIO
 	bool "External gpio watchdog support"
 	depends on WDT
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 7b39adcf0ff..6b564b7f96d 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -29,6 +29,7 @@  obj-$(CONFIG_WDT_BOOKE) += booke_wdt.o
 obj-$(CONFIG_WDT_CORTINA) += cortina_wdt.o
 obj-$(CONFIG_WDT_ORION) += orion_wdt.o
 obj-$(CONFIG_WDT_CDNS) += cdns_wdt.o
+obj-$(CONFIG_WDT_DA9063) += da9063-wdt.o
 obj-$(CONFIG_WDT_FTWDT010) += ftwdt010_wdt.o
 obj-$(CONFIG_WDT_GPIO) += gpio_wdt.o
 obj-$(CONFIG_WDT_MAX6370) += max6370_wdt.o
diff --git a/drivers/watchdog/da9063-wdt.c b/drivers/watchdog/da9063-wdt.c
new file mode 100644
index 00000000000..c9210a106d3
--- /dev/null
+++ b/drivers/watchdog/da9063-wdt.c
@@ -0,0 +1,142 @@ 
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// DA9063 watchdog driver
+//
+// Copyright (c) 2024 Fabio Estevam <festevam@denx.de>
+//
+
+#include <dm.h>
+#include <dm/device-internal.h>
+#include <dm/device_compat.h>
+#include <dm/lists.h>
+#include <i2c.h>
+#include <linux/delay.h>
+#include <wdt.h>
+
+#define	DA9063_REG_CONTROL_D		0x11
+/* DA9063_REG_CONTROL_D (addr=0x11) */
+#define	DA9063_TWDSCALE_MASK		0x0
+#define DA9063_TWDSCALE_DISABLE		0
+#define	DA9063_REG_CONTROL_F		0x13
+/* DA9063_REG_CONTROL_F (addr=0x13) */
+#define	DA9063_WATCHDOG			0x01
+#define	DA9063_SHUTDOWN			0x02
+
+/*
+ * Watchdog selector to timeout in seconds.
+ *   0: WDT disabled;
+ *   others: timeout = 2048 ms * 2^(TWDSCALE-1).
+ */
+static const unsigned int wdt_timeout[] = { 0, 2, 4, 8, 16, 32, 65, 131 };
+
+#define DA9063_TWDSCALE_DISABLE		0
+#define DA9063_TWDSCALE_MIN		1
+#define DA9063_TWDSCALE_MAX		(ARRAY_SIZE(wdt_timeout) - 1)
+
+static unsigned int da9063_wdt_timeout_to_sel(unsigned int secs)
+{
+	unsigned int i;
+
+	for (i = DA9063_TWDSCALE_MIN; i <= DA9063_TWDSCALE_MAX; i++) {
+		if (wdt_timeout[i] >= secs)
+			return i;
+	}
+
+	return DA9063_TWDSCALE_MAX;
+}
+
+static int da9063_read(struct udevice *dev, uint reg, uint8_t *buff, int len)
+{
+	return dm_i2c_read(dev->parent, reg, buff, len);
+}
+
+static int da9063_write(struct udevice *dev, uint reg, const u8 *buff, int len)
+{
+	return dm_i2c_write(dev->parent, reg, buff, len);
+}
+
+static int da9063_wdt_disable_timer(struct udevice *dev)
+{
+	char val;
+
+	da9063_read(dev, DA9063_REG_CONTROL_D, &val, 1);
+	val &= ~DA9063_TWDSCALE_MASK;
+	val |= DA9063_TWDSCALE_DISABLE;
+	da9063_write(dev, DA9063_REG_CONTROL_D, &val, 1);
+
+	return 0;
+}
+
+static int da9063_wdt_update_timeout(struct udevice *dev, unsigned int timeout)
+{
+	unsigned int regval;
+	char val;
+	int ret;
+
+	/*
+	 * The watchdog triggers a reboot if a timeout value is already
+	 * programmed because the timeout value combines two functions
+	 * in one: indicating the counter limit and starting the watchdog.
+	 * The watchdog must be disabled to be able to change the timeout
+	 * value if the watchdog is already running. Then we can set the
+	 * new timeout value which enables the watchdog again.
+	 */
+	ret = da9063_wdt_disable_timer(dev);
+	if (ret)
+		return ret;
+
+	udelay(300);
+
+	regval = da9063_wdt_timeout_to_sel(timeout);
+
+	da9063_read(dev, DA9063_REG_CONTROL_D, &val, 1);
+	val &= ~DA9063_TWDSCALE_MASK;
+	val |= regval;
+	da9063_write(dev, DA9063_REG_CONTROL_D, &val, 1);
+
+	return 0;
+}
+
+static int da9063_wdt_start(struct udevice *dev, u64 timeout, ulong flags)
+{
+	return da9063_wdt_update_timeout(dev, timeout);
+}
+
+static int da9063_wdt_stop(struct udevice *dev)
+{
+	return da9063_wdt_disable_timer(dev);
+}
+
+static int da9063_wdt_reset(struct udevice *dev)
+{
+	char val = DA9063_WATCHDOG;
+
+	return da9063_write(dev, DA9063_REG_CONTROL_F, &val, 1);
+}
+
+static int da9063_wdt_expire_now(struct udevice *dev, ulong flags)
+{
+	char val = DA9063_SHUTDOWN;
+
+	return da9063_write(dev, DA9063_REG_CONTROL_F, &val, 1);
+}
+
+static const struct wdt_ops da9063_wdt_ops = {
+	.start = da9063_wdt_start,
+	.stop = da9063_wdt_stop,
+	.reset = da9063_wdt_reset,
+	.expire_now = da9063_wdt_expire_now,
+};
+
+static const struct udevice_id da9063_wdt_ids[] = {
+	{ .compatible = "dlg,da9063-watchdog", },
+	{}
+};
+
+U_BOOT_DRIVER(da9063_wdt) = {
+	.name = "da9063-wdt",
+	.id = UCLASS_WDT,
+	.of_match = da9063_wdt_ids,
+	.ops = &da9063_wdt_ops,
+	.flags = DM_FLAG_PROBE_AFTER_BIND,
+};