[PATCH 1/3] watchdog: Add a driver for the sunxi watchdog

Samuel Holland samuel at sholland.org
Sun Aug 22 20:53:27 CEST 2021


This driver supports the sun4i/sun6i/sun20i watchdog timers.
They have a maximum timeout of 16 seconds.

Signed-off-by: Samuel Holland <samuel at sholland.org>
---

 drivers/watchdog/Kconfig     |   8 ++
 drivers/watchdog/Makefile    |   1 +
 drivers/watchdog/sunxi_wdt.c | 188 +++++++++++++++++++++++++++++++++++
 3 files changed, 197 insertions(+)
 create mode 100644 drivers/watchdog/sunxi_wdt.c

diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index f0ff2612a6b..59112147343 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -27,6 +27,7 @@ config WATCHDOG_TIMEOUT_MSECS
 	default 128000 if ARCH_MX25 || ARCH_MX31 || ARCH_MX5 || ARCH_MX6
 	default 128000 if ARCH_MX7 || ARCH_VF610
 	default 30000 if ARCH_SOCFPGA
+	default 16000 if ARCH_SUNXI
 	default 60000
 	help
 	  Watchdog timeout in msec
@@ -241,6 +242,13 @@ config WDT_STM32MP
 	  Enable the STM32 watchdog (IWDG) driver. Enable support to
 	  configure STM32's on-SoC watchdog.
 
+config WDT_SUNXI
+	bool "Allwinner sunxi watchdog timer support"
+	depends on WDT && ARCH_SUNXI
+	default y
+	help
+	  Enable support for the watchdog timer in Allwinner sunxi SoCs.
+
 config XILINX_TB_WATCHDOG
 	bool "Xilinx Axi watchdog timer support"
 	depends on WDT
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 5c7ef593fe5..20cf4280db0 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -35,5 +35,6 @@ obj-$(CONFIG_WDT_SBSA) += sbsa_gwdt.o
 obj-$(CONFIG_WDT_K3_RTI) += rti_wdt.o
 obj-$(CONFIG_WDT_SP805) += sp805_wdt.o
 obj-$(CONFIG_WDT_STM32MP) += stm32mp_wdt.o
+obj-$(CONFIG_WDT_SUNXI) += sunxi_wdt.o
 obj-$(CONFIG_WDT_TANGIER) += tangier_wdt.o
 obj-$(CONFIG_WDT_XILINX) += xilinx_wwdt.o
diff --git a/drivers/watchdog/sunxi_wdt.c b/drivers/watchdog/sunxi_wdt.c
new file mode 100644
index 00000000000..b40a1d29caa
--- /dev/null
+++ b/drivers/watchdog/sunxi_wdt.c
@@ -0,0 +1,188 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Derived from linux/drivers/watchdog/sunxi_wdt.c:
+ *	Copyright (C) 2013 Carlo Caione
+ *	Copyright (C) 2012 Henrik Nordstrom
+ */
+
+#include <dm.h>
+#include <wdt.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+
+#define MSEC_PER_SEC		1000
+
+#define WDT_MAX_TIMEOUT		16
+#define WDT_TIMEOUT_MASK	0xf
+
+#define WDT_CTRL_RELOAD		((1 << 0) | (0x0a57 << 1))
+
+#define WDT_MODE_EN		BIT(0)
+
+struct sunxi_wdt_reg {
+	u8 wdt_ctrl;
+	u8 wdt_cfg;
+	u8 wdt_mode;
+	u8 wdt_timeout_shift;
+	u8 wdt_reset_mask;
+	u8 wdt_reset_val;
+	u32 wdt_key_val;
+};
+
+struct sunxi_wdt_priv {
+	void __iomem			*base;
+	const struct sunxi_wdt_reg	*regs;
+};
+
+/* Map of timeout in seconds to register value */
+static const u8 wdt_timeout_map[1 + WDT_MAX_TIMEOUT] = {
+	[0]	= 0x0,
+	[1]	= 0x1,
+	[2]	= 0x2,
+	[3]	= 0x3,
+	[4]	= 0x4,
+	[5]	= 0x5,
+	[6]	= 0x6,
+	[7]	= 0x7,
+	[8]	= 0x7,
+	[9]	= 0x8,
+	[10]	= 0x8,
+	[11]	= 0x9,
+	[12]	= 0x9,
+	[13]	= 0xa,
+	[14]	= 0xa,
+	[15]	= 0xb,
+	[16]	= 0xb,
+};
+
+static int sunxi_wdt_reset(struct udevice *dev)
+{
+	struct sunxi_wdt_priv *priv = dev_get_priv(dev);
+	const struct sunxi_wdt_reg *regs = priv->regs;
+	void __iomem *base = priv->base;
+
+	writel(WDT_CTRL_RELOAD, base + regs->wdt_ctrl);
+
+	return 0;
+}
+
+static int sunxi_wdt_start(struct udevice *dev, u64 timeout, ulong flags)
+{
+	struct sunxi_wdt_priv *priv = dev_get_priv(dev);
+	const struct sunxi_wdt_reg *regs = priv->regs;
+	void __iomem *base = priv->base;
+	u32 val;
+
+	timeout /= MSEC_PER_SEC;
+	if (timeout > WDT_MAX_TIMEOUT)
+		timeout = WDT_MAX_TIMEOUT;
+
+	/* Set system reset function */
+	val = readl(base + regs->wdt_cfg);
+	val &= ~regs->wdt_reset_mask;
+	val |= regs->wdt_reset_val;
+	val |= regs->wdt_key_val;
+	writel(val, base + regs->wdt_cfg);
+
+	/* Set timeout and enable watchdog */
+	val = readl(base + regs->wdt_mode);
+	val &= ~(WDT_TIMEOUT_MASK << regs->wdt_timeout_shift);
+	val |= wdt_timeout_map[timeout] << regs->wdt_timeout_shift;
+	val |= WDT_MODE_EN;
+	val |= regs->wdt_key_val;
+	writel(val, base + regs->wdt_mode);
+
+	return sunxi_wdt_reset(dev);
+}
+
+static int sunxi_wdt_stop(struct udevice *dev)
+{
+	struct sunxi_wdt_priv *priv = dev_get_priv(dev);
+	const struct sunxi_wdt_reg *regs = priv->regs;
+	void __iomem *base = priv->base;
+
+	writel(regs->wdt_key_val, base + regs->wdt_mode);
+
+	return 0;
+}
+
+static int sunxi_wdt_expire_now(struct udevice *dev, ulong flags)
+{
+	int ret;
+
+	ret = sunxi_wdt_start(dev, 0, flags);
+	if (ret)
+		return ret;
+
+	mdelay(500);
+
+	return 0;
+}
+
+static const struct wdt_ops sunxi_wdt_ops = {
+	.reset		= sunxi_wdt_reset,
+	.start		= sunxi_wdt_start,
+	.stop		= sunxi_wdt_stop,
+	.expire_now	= sunxi_wdt_expire_now,
+};
+
+static const struct sunxi_wdt_reg sun4i_wdt_reg = {
+	.wdt_ctrl		= 0x00,
+	.wdt_cfg		= 0x04,
+	.wdt_mode		= 0x04,
+	.wdt_timeout_shift	= 3,
+	.wdt_reset_mask		= 0x2,
+	.wdt_reset_val		= 0x2,
+};
+
+static const struct sunxi_wdt_reg sun6i_wdt_reg = {
+	.wdt_ctrl		= 0x10,
+	.wdt_cfg		= 0x14,
+	.wdt_mode		= 0x18,
+	.wdt_timeout_shift	= 4,
+	.wdt_reset_mask		= 0x3,
+	.wdt_reset_val		= 0x1,
+};
+
+static const struct sunxi_wdt_reg sun20i_wdt_reg = {
+	.wdt_ctrl		= 0x10,
+	.wdt_cfg		= 0x14,
+	.wdt_mode		= 0x18,
+	.wdt_timeout_shift	= 4,
+	.wdt_reset_mask		= 0x03,
+	.wdt_reset_val		= 0x01,
+	.wdt_key_val		= 0x16aa0000,
+};
+
+static const struct udevice_id sunxi_wdt_ids[] = {
+	{ .compatible = "allwinner,sun4i-a10-wdt", .data = (ulong)&sun4i_wdt_reg },
+	{ .compatible = "allwinner,sun6i-a31-wdt", .data = (ulong)&sun6i_wdt_reg },
+	{ .compatible = "allwinner,sun20i-d1-wdt", .data = (ulong)&sun20i_wdt_reg },
+	{ /* sentinel */ }
+};
+
+static int sunxi_wdt_probe(struct udevice *dev)
+{
+	struct sunxi_wdt_priv *priv = dev_get_priv(dev);
+
+	priv->base = dev_remap_addr(dev);
+	if (!priv->base)
+		return -EINVAL;
+
+	priv->regs = (void *)dev_get_driver_data(dev);
+	if (!priv->regs)
+		return -EINVAL;
+
+	sunxi_wdt_stop(dev);
+
+	return 0;
+}
+
+U_BOOT_DRIVER(sunxi_wdt) = {
+	.name		= "sunxi_wdt",
+	.id		= UCLASS_WDT,
+	.of_match	= sunxi_wdt_ids,
+	.probe		= sunxi_wdt_probe,
+	.priv_auto	= sizeof(struct sunxi_wdt_priv),
+	.ops		= &sunxi_wdt_ops,
+};
-- 
2.31.1



More information about the U-Boot mailing list