This patch add watchdog driver for Allwinner SoCs.

Initial non-dm driver has send by 'Chris Blake' So I keep his
author on the driver itself.

Signed-off-by: Chris Blake <chrisrblak...@gmail.com>
Signed-off-by: Jagan Teki <ja...@amarulasolutions.com>
---
 drivers/watchdog/Kconfig     |   7 ++
 drivers/watchdog/Makefile    |   1 +
 drivers/watchdog/sunxi_wdt.c | 207 +++++++++++++++++++++++++++++++++++
 3 files changed, 215 insertions(+)
 create mode 100644 drivers/watchdog/sunxi_wdt.c

diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 115fc4551f..79fd58048e 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -117,6 +117,13 @@ config WDT_MTK
          The watchdog timer is stopped when initialized.
          It performs full SoC reset.
 
+config WDT_SUNXI
+       bool "Allwinner SoC watchdog support"
+       depends on WDT
+       default y if ARCH_SUNXI
+       help
+         Select this to enable watchdog timer for Allwinner 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 d901240ad1..16003a8fc6 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -27,3 +27,4 @@ obj-$(CONFIG_WDT_CDNS) += cdns_wdt.o
 obj-$(CONFIG_MPC8xx_WATCHDOG) += mpc8xx_wdt.o
 obj-$(CONFIG_WDT_MT7621) += mt7621_wdt.o
 obj-$(CONFIG_WDT_MTK) += mtk_wdt.o
+obj-$(CONFIG_WDT_SUNXI) += sunxi_wdt.o
diff --git a/drivers/watchdog/sunxi_wdt.c b/drivers/watchdog/sunxi_wdt.c
new file mode 100644
index 0000000000..2e7364b7bb
--- /dev/null
+++ b/drivers/watchdog/sunxi_wdt.c
@@ -0,0 +1,207 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Watchdog driver for Allwinner SoCs
+ *
+ * Copyright (C) 2019 Chris Blake <chrisrblak...@gmail.com>
+ * Copyright (C) 2019 Jagan Teki <ja...@amarulasolutions.com>
+ *
+ * Based on the linux/drivers/watchdog/sunxi_wdt.c
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <wdt.h>
+#include <asm/io.h>
+
+#define WDT_TIMEOUT_MASK       0x0F
+#define WDT_CTRL_RELOAD                ((1 << 0) | (0x0a57 << 1))
+#define WDT_MODE_EN            BIT(0)
+
+/*
+ * This structure stores the register offsets for different variants
+ * of Allwinner's watchdog hardware.
+ */
+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;
+};
+
+struct sunxi_wdt_priv {
+       void __iomem *base;
+       const struct sunxi_wdt_reg *wdt_regs;
+};
+
+/*
+ * wdt_timeout_map maps the watchdog timer interval value in seconds to
+ * the value of the register WDT_MODE at bits .wdt_timeout_shift ~ +3
+ *
+ * [timeout seconds] = register value
+ *
+ */
+static const int wdt_timeout_map[] = {
+       [1] = 0x1,  /* 1s  */
+       [2] = 0x2,  /* 2s  */
+       [3] = 0x3,  /* 3s  */
+       [4] = 0x4,  /* 4s  */
+       [5] = 0x5,  /* 5s  */
+       [6] = 0x6,  /* 6s  */
+       [8] = 0x7,  /* 8s  */
+       [10] = 0x8, /* 10s */
+       [12] = 0x9, /* 12s */
+       [14] = 0xA, /* 14s */
+       [16] = 0xB, /* 16s */
+};
+
+static int sunxi_wdt_reset(struct udevice *dev)
+{
+       struct sunxi_wdt_priv *priv = dev_get_priv(dev);
+
+       writel(WDT_CTRL_RELOAD, priv->base + priv->wdt_regs->wdt_ctrl);
+
+       return 0;
+}
+
+static int sunxi_wdt_stop(struct udevice *dev)
+{
+       struct sunxi_wdt_priv *priv = dev_get_priv(dev);
+
+       writel(0, priv->base + priv->wdt_regs->wdt_mode);
+
+       return 0;
+}
+
+static int sunxi_wdt_expire_now(struct udevice *dev, ulong flags)
+{
+       struct sunxi_wdt_priv *priv = dev_get_priv(dev);
+       u32 val;
+
+       /* Set system reset function */
+       val = readl(priv->base + priv->wdt_regs->wdt_cfg);
+       val &= ~(priv->wdt_regs->wdt_reset_mask);
+       val |= priv->wdt_regs->wdt_reset_val;
+       writel(val, priv->base + priv->wdt_regs->wdt_cfg);
+
+       /* Set lowest timeout and enable watchdog */
+       val = readl(priv->base + priv->wdt_regs->wdt_mode);
+       val &= (WDT_TIMEOUT_MASK << priv->wdt_regs->wdt_timeout_shift);
+       val |= WDT_MODE_EN;
+       writel(val, priv->base + priv->wdt_regs->wdt_mode);
+
+       /*
+        * Restart the watchdog. The default (and lowest) interval
+        * value for the watchdog is 0.5s.
+        */
+       writel(WDT_CTRL_RELOAD, priv->base + priv->wdt_regs->wdt_ctrl);
+
+       while (1) {
+               mdelay(5);
+               val = readl(priv->base + priv->wdt_regs->wdt_mode);
+               val |= WDT_MODE_EN;
+               writel(val, priv->base + priv->wdt_regs->wdt_mode);
+       }
+
+       return 0;
+}
+
+static void sunxi_wdt_set_timeout(struct udevice *dev, unsigned int timeout)
+{
+       struct sunxi_wdt_priv *priv = dev_get_priv(dev);
+       u32 reg;
+
+       if (wdt_timeout_map[timeout] == 0)
+               timeout++;
+
+       reg = readl(priv->base + priv->wdt_regs->wdt_mode);
+       reg &= (WDT_TIMEOUT_MASK << priv->wdt_regs->wdt_timeout_shift);
+       reg |= wdt_timeout_map[timeout] << priv->wdt_regs->wdt_timeout_shift;
+       writel(reg, priv->base + priv->wdt_regs->wdt_mode);
+
+       sunxi_wdt_reset(dev);
+}
+
+static int sunxi_wdt_start(struct udevice *dev, u64 timeout, ulong flags)
+{
+       struct sunxi_wdt_priv *priv = dev_get_priv(dev);
+       u32 reg;
+
+       sunxi_wdt_set_timeout(dev, timeout);
+
+       /* Set system reset function */
+       reg = readl(priv->base + priv->wdt_regs->wdt_cfg);
+       reg &= ~(priv->wdt_regs->wdt_reset_mask);
+       reg |= priv->wdt_regs->wdt_reset_val;
+       writel(reg, priv->base + priv->wdt_regs->wdt_cfg);
+
+       /* Enable watchdog */
+       reg = readl(priv->base + priv->wdt_regs->wdt_mode);
+       reg |= WDT_MODE_EN;
+       writel(reg, priv->base + priv->wdt_regs->wdt_mode);
+
+       return 0;
+}
+
+static int sunxi_wdt_probe(struct udevice *dev)
+{
+       struct sunxi_wdt_priv *priv = dev_get_priv(dev);
+
+       priv->base = dev_read_addr_ptr(dev);
+       if (!priv->base)
+               return -ENOENT;
+
+       priv->wdt_regs = (const struct sunxi_wdt_reg *)dev_get_driver_data(dev);
+       if (!priv->wdt_regs)
+               return -EINVAL;
+
+       return sunxi_wdt_stop(dev);
+}
+
+static const struct wdt_ops sunxi_wdt_ops = {
+       .start = sunxi_wdt_start,
+       .reset = sunxi_wdt_reset,
+       .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 = 0x02,
+       .wdt_reset_val = 0x02,
+};
+
+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 = 0x03,
+       .wdt_reset_val = 0x01,
+};
+
+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
+       },
+       { /* sentinel */ }
+};
+
+U_BOOT_DRIVER(sunxi_wdt) = {
+       .name = "sunxi_wdt",
+       .id = UCLASS_WDT,
+       .of_match = sunxi_wdt_ids,
+       .priv_auto_alloc_size = sizeof(struct sunxi_wdt_priv),
+       .probe = sunxi_wdt_probe,
+       .ops = &sunxi_wdt_ops,
+       .flags = DM_FLAG_PRE_RELOC,
+};
-- 
2.18.0.321.gffc6fa0e3

_______________________________________________
U-Boot mailing list
U-Boot@lists.denx.de
https://lists.denx.de/listinfo/u-boot

Reply via email to