Signed-off-by: Sascha Hauer <s.ha...@pengutronix.de>
---
 drivers/watchdog/Kconfig      |   6 ++
 drivers/watchdog/Makefile     |   1 +
 drivers/watchdog/imxulp-wdt.c | 161 ++++++++++++++++++++++++++++++++++
 3 files changed, 168 insertions(+)
 create mode 100644 drivers/watchdog/imxulp-wdt.c

diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index e5c2036e6d..159b495acb 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -59,6 +59,12 @@ config WATCHDOG_IMX
        help
          Add support for watchdog found on Freescale i.MX SoCs.
 
+config WATCHDOG_IMXULP
+       bool "i.MX ULP watchdog"
+       depends on ARCH_IMX || COMPILE_TEST
+       help
+         Add support for watchdog found on Freescale i.MX SoCs.
+
 config WATCHDOG_JZ4740
        bool "Ingenic jz4740 SoC hardware watchdog"
        depends on MACH_MIPS_XBURST || COMPILE_TEST
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index cdd9460e34..2b0da7cea9 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -10,6 +10,7 @@ obj-$(CONFIG_WATCHDOG_DW) += dw_wdt.o
 obj-$(CONFIG_WATCHDOG_JZ4740) += jz4740.o
 obj-$(CONFIG_WATCHDOG_IMX_RESET_SOURCE) += imxwd.o
 obj-$(CONFIG_WATCHDOG_IMX) += imxwd.o
+obj-$(CONFIG_WATCHDOG_IMXULP) += imxulp-wdt.o
 obj-$(CONFIG_WATCHDOG_KVX) += kvx_wdt.o
 obj-$(CONFIG_WATCHDOG_ORION) += orion_wdt.o
 obj-$(CONFIG_ARCH_BCM283X) += bcm2835_wdt.o
diff --git a/drivers/watchdog/imxulp-wdt.c b/drivers/watchdog/imxulp-wdt.c
new file mode 100644
index 0000000000..78d1527782
--- /dev/null
+++ b/drivers/watchdog/imxulp-wdt.c
@@ -0,0 +1,161 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2016 Freescale Semiconductor, Inc.
+ */
+
+#include <common.h>
+#include <init.h>
+#include <io.h>
+#include <of.h>
+#include <errno.h>
+#include <malloc.h>
+#include <restart.h>
+#include <watchdog.h>
+#include <reset_source.h>
+#include <linux/clk.h>
+#include <asm/system.h>
+
+struct imxulp_socdata {
+       unsigned int rate;
+};
+
+struct imxulp_wd {
+       struct watchdog wd;
+       void __iomem *base;
+       unsigned int rate;
+       struct device *dev;
+};
+
+#define REFRESH_WORD0 0xA602 /* 1st refresh word */
+#define REFRESH_WORD1 0xB480 /* 2nd refresh word */
+
+#define UNLOCK_WORD0 0xC520 /* 1st unlock word */
+#define UNLOCK_WORD1 0xD928 /* 2nd unlock word */
+
+#define UNLOCK_WORD 0xD928C520 /* unlock word */
+#define REFRESH_WORD 0xB480A602 /* refresh word */
+
+#define WDOG_CS                 0x0
+#define WDOG_CS_UPDATE         BIT(5)
+#define WDOG_CS_EN             BIT(7)
+#define WDOG_CS_RCS            BIT(10)
+#define WDOG_CS_ULK            BIT(11)
+#define WDOG_CS_PRES           BIT(12)
+#define WDOG_CS_CMD32EN                BIT(13)
+#define WDOG_CS_FLG            BIT(14)
+#define WDOG_CS_INT            BIT(6)
+#define WDOG_CS_LPO_CLK                (0x1 << 8)
+
+#define WDOG_CNT               0x4
+#define WDOG_TOVAL             0x8
+
+#define CLK_RATE_1KHZ          1000
+#define CLK_RATE_32KHZ         125
+
+static int imxulp_watchdog_set_timeout(struct watchdog *wd, unsigned int 
timeout)
+{
+       struct imxulp_wd *imxwd = container_of(wd, struct imxulp_wd, wd);
+       u32 cmd32 = 0;
+
+       if (timeout == 0)
+               return -ENOSYS;
+
+       if (readl(imxwd->base + WDOG_CS) & WDOG_CS_CMD32EN) {
+               writel(UNLOCK_WORD, imxwd->base + WDOG_CNT);
+               cmd32 = WDOG_CS_CMD32EN;
+       } else {
+               writel(UNLOCK_WORD0, imxwd->base + WDOG_CNT);
+               writel(UNLOCK_WORD1, imxwd->base + WDOG_CNT);
+       }
+
+       /* Wait WDOG Unlock */
+       while (!(readl(imxwd->base + WDOG_CS) & WDOG_CS_ULK))
+               ;
+
+       writel(timeout * imxwd->rate, imxwd->base + WDOG_TOVAL);
+
+       writel(cmd32 | WDOG_CS_EN | WDOG_CS_UPDATE | WDOG_CS_LPO_CLK |
+              WDOG_CS_FLG | WDOG_CS_PRES | WDOG_CS_INT, imxwd->base + WDOG_CS);
+
+       /* Wait WDOG reconfiguration */
+       while (!(readl(imxwd->base + WDOG_CS) & WDOG_CS_RCS))
+               ;
+
+       if (readl(imxwd->base + WDOG_CS) & WDOG_CS_CMD32EN) {
+               writel(REFRESH_WORD, imxwd->base + WDOG_CNT);
+       } else {
+               writel(REFRESH_WORD0, imxwd->base + WDOG_CNT);
+               writel(REFRESH_WORD1, imxwd->base + WDOG_CNT);
+       }
+
+       return 0;
+}
+
+static enum wdog_hw_runnning imxulp_wd_running(struct imxulp_wd *imxwd)
+{
+       if (readl(imxwd->base + WDOG_CS) & WDOG_CS_EN)
+               return WDOG_HW_RUNNING;
+       else
+               return WDOG_HW_NOT_RUNNING;
+}
+
+static int imxulp_wd_probe(struct device *dev)
+{
+       struct imxulp_wd *imxwd;
+       struct resource *iores;
+       struct imxulp_socdata *socdata;
+       int ret;
+
+       ret = dev_get_drvdata(dev, (const void **)&socdata);
+       if (ret)
+               return ret;
+
+       imxwd = xzalloc(sizeof(*imxwd));
+       iores = dev_request_mem_resource(dev, 0);
+       if (IS_ERR(iores))
+               return dev_err_probe(dev, PTR_ERR(iores), "could not get memory 
region\n");
+
+       imxwd->rate = socdata->rate;
+       imxwd->base = IOMEM(iores->start);
+       imxwd->wd.set_timeout = imxulp_watchdog_set_timeout;
+       imxwd->wd.timeout_max = 0xffff / imxwd->rate;
+       imxwd->wd.hwdev = dev;
+       imxwd->wd.running = imxulp_wd_running(imxwd);
+
+       ret = watchdog_register(&imxwd->wd);
+       if (ret)
+               return dev_err_probe(dev, ret, "Failed to register watchdog 
device\n");
+
+       return 0;
+}
+
+static struct imxulp_socdata imx7ulp_wd_socdata = {
+       .rate = CLK_RATE_1KHZ,
+};
+
+static struct imxulp_socdata imx93_wd_socdata = {
+       .rate = CLK_RATE_32KHZ,
+};
+
+static __maybe_unused struct of_device_id imxulp_wdt_dt_ids[] = {
+       {
+               .compatible = "fsl,imx7ulp-wdt",
+               .data = &imx7ulp_wd_socdata,
+       }, {
+               .compatible = "fsl,imx8ulp-wdt",
+               .data = &imx7ulp_wd_socdata,
+       }, {
+               .compatible = "fsl,imx93-wdt",
+               .data = &imx93_wd_socdata,
+       }, {
+               /* sentinel */
+       }
+};
+MODULE_DEVICE_TABLE(of, imx_wdt_dt_ids);
+
+static struct driver imxulp_wd_driver = {
+       .name = "imxulp-watchdog",
+       .probe = imxulp_wd_probe,
+       .of_compatible = DRV_OF_COMPAT(imxulp_wdt_dt_ids),
+};
+device_platform_driver(imxulp_wd_driver);
-- 
2.39.2


Reply via email to