[PATCH 10/25] watchdog: Add ULP wdog support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Signed-off-by: Sascha Hauer <s.hauer@xxxxxxxxxxxxxx>
---
 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





[Index of Archives]     [Linux Embedded]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [XFree86]

  Powered by Linux