summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/watchdog/fsl-imx-wdt.txt3
-rw-r--r--drivers/watchdog/Kconfig11
-rw-r--r--drivers/watchdog/Makefile1
-rw-r--r--drivers/watchdog/imx8_wdt.c159
-rw-r--r--include/soc/imx/fsl_sip.h10
5 files changed, 184 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/watchdog/fsl-imx-wdt.txt b/Documentation/devicetree/bindings/watchdog/fsl-imx-wdt.txt
index 107280ef0025..10771fbf9c63 100644
--- a/Documentation/devicetree/bindings/watchdog/fsl-imx-wdt.txt
+++ b/Documentation/devicetree/bindings/watchdog/fsl-imx-wdt.txt
@@ -5,6 +5,9 @@ Required properties:
- reg : Should contain WDT registers location and length
- interrupts : Should contain WDT interrupt
+For imx8-wdt, it's a software watchdog which implemented by timer tick
+in SCFW. In this case, only compatible name required.
+
Optional properties:
- big-endian: If present the watchdog device's registers are implemented
in big endian mode, otherwise in native mode(same with CPU), for more
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 6613799bdf9f..523e6a73664e 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -565,6 +565,17 @@ config IMX7ULP_WDT
To compile this driver as a module, choose M here: the
module will be called imx7ulp_wdt.
+config IMX8_WDT
+ tristate "IMX8 Watchdog"
+ depends on OF
+ select WATCHDOG_CORE
+ help
+ This is the driver for the watchdog on i.mx8QM/QXP
+ and later processors, this virtual watch dog call
+ the interfaces which provided by SCFW.
+ If you have one of these processors and wish to have
+ watchdog support enabled, say Y, otherwise say N.
+
config UX500_WATCHDOG
tristate "ST-Ericsson Ux500 watchdog"
depends on MFD_DB8500_PRCMU
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 1954250cb07c..3d2a0f8277e5 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -65,6 +65,7 @@ obj-$(CONFIG_TS4800_WATCHDOG) += ts4800_wdt.o
obj-$(CONFIG_TS72XX_WATCHDOG) += ts72xx_wdt.o
obj-$(CONFIG_IMX2_WDT) += imx2_wdt.o
obj-$(CONFIG_IMX7ULP_WDT) += imx7ulp_wdt.o
+obj-$(CONFIG_IMX8_WDT) += imx8_wdt.o
obj-$(CONFIG_UX500_WATCHDOG) += ux500_wdt.o
obj-$(CONFIG_RETU_WATCHDOG) += retu_wdt.o
obj-$(CONFIG_BCM2835_WDT) += bcm2835_wdt.o
diff --git a/drivers/watchdog/imx8_wdt.c b/drivers/watchdog/imx8_wdt.c
new file mode 100644
index 000000000000..108337736974
--- /dev/null
+++ b/drivers/watchdog/imx8_wdt.c
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2017 NXP.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ */
+
+#include <linux/arm-smccc.h>
+#include <linux/io.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/reboot.h>
+#include <linux/watchdog.h>
+#include <soc/imx/fsl_sip.h>
+
+#define DEFAULT_TIMEOUT 10
+/*
+ * Software timer tick implemented in scfw side, support 10ms to 0xffffffff ms
+ * in theory, but for normal case, 1s~60s is enough, you can change this max
+ * value in case it's not enough.
+ */
+#define MAX_TIMEOUT 60
+
+static struct watchdog_device imx8_wdd;
+
+static int imx8_wdt_ping(struct watchdog_device *wdog)
+{
+ struct arm_smccc_res res;
+
+ arm_smccc_smc(FSL_SIP_SRTC, FSL_SIP_SRTC_PING_WDOG, 0, 0, 0, 0, 0, 0,
+ &res);
+
+ return res.a0;
+}
+
+static int imx8_wdt_start(struct watchdog_device *wdog)
+{
+ struct arm_smccc_res res;
+
+ /* no block */
+ arm_smccc_smc(FSL_SIP_SRTC, FSL_SIP_SRTC_START_WDOG, 0, 0, 0, 0, 0, 0,
+ &res);
+ if (res.a0)
+ return res.a0;
+ /* TODO: change to SC_TIMER_WDOG_ACTION_PARTITION after SCFW support */
+ arm_smccc_smc(FSL_SIP_SRTC, FSL_SIP_SRTC_SET_WDOG_ACT,
+ SC_TIMER_WDOG_ACTION_BOARD, 0, 0, 0, 0, 0, &res);
+ return res.a0;
+}
+
+static int imx8_wdt_stop(struct watchdog_device *wdog)
+{
+ struct arm_smccc_res res;
+
+ arm_smccc_smc(FSL_SIP_SRTC, FSL_SIP_SRTC_STOP_WDOG, 0, 0, 0, 0, 0, 0,
+ &res);
+
+ return res.a0;
+}
+
+static int imx8_wdt_set_timeout(struct watchdog_device *wdog,
+ unsigned int timeout)
+{
+ struct arm_smccc_res res;
+
+ arm_smccc_smc(FSL_SIP_SRTC, FSL_SIP_SRTC_SET_TIMEOUT_WDOG,
+ timeout * 1000, 0, 0, 0, 0, 0, &res);
+
+ return res.a0;
+}
+
+static const struct watchdog_ops imx8_wdt_ops = {
+ .owner = THIS_MODULE,
+ .start = imx8_wdt_start,
+ .stop = imx8_wdt_stop,
+ .ping = imx8_wdt_ping,
+ .set_timeout = imx8_wdt_set_timeout,
+};
+
+static const struct watchdog_info imx8_wdt_info = {
+ .identity = "i.MX8 watchdog timer",
+ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING |
+ WDIOF_MAGICCLOSE,
+};
+
+static int imx8_wdt_probe(struct platform_device *pdev)
+{
+ struct watchdog_device *wdt = &imx8_wdd;
+ int err;
+
+ platform_set_drvdata(pdev, wdt);
+ /* init the wdd */
+ wdt->info = &imx8_wdt_info;
+ wdt->ops = &imx8_wdt_ops;
+ wdt->min_timeout = 1;
+ wdt->max_timeout = MAX_TIMEOUT;
+ wdt->parent = &pdev->dev;
+ watchdog_set_drvdata(wdt, NULL);
+
+ err = watchdog_init_timeout(wdt, DEFAULT_TIMEOUT, &pdev->dev);
+ if (err) {
+ dev_err(&pdev->dev, "Failed to init the wdog timeout:%d\n",
+ err);
+ return err;
+ }
+
+ err = watchdog_register_device(wdt);
+ if (err) {
+ dev_err(&pdev->dev, "Failed to register watchdog device\n");
+ return err;
+ }
+
+ return 0;
+}
+
+static int imx8_wdt_remove(struct platform_device *pdev)
+{
+ struct watchdog_device *wdt = platform_get_drvdata(pdev);
+
+ imx8_wdt_stop(wdt);
+
+ watchdog_unregister_device(wdt);
+
+ return 0;
+}
+
+static void imx8_wdt_shutdown(struct platform_device *pdev)
+{
+ struct watchdog_device *wdt = platform_get_drvdata(pdev);
+
+ if (watchdog_active(wdt))
+ imx8_wdt_stop(wdt);
+}
+
+static const struct of_device_id imx8_wdt_dt_ids[] = {
+ { .compatible = "fsl,imx8-wdt", },
+ { /*sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, imx8_wdt_dt_ids);
+
+static struct platform_driver imx8_wdt_driver = {
+ .probe = imx8_wdt_probe,
+ .remove = imx8_wdt_remove,
+ .shutdown = imx8_wdt_shutdown,
+ .driver = {
+ .name = "imx8-wdt",
+ .of_match_table = imx8_wdt_dt_ids,
+ },
+};
+
+module_platform_driver(imx8_wdt_driver);
+
+MODULE_AUTHOR("Robin Gong <yibin.gong@nxp.com>");
+MODULE_DESCRIPTION("NXP i.MX8 watchdog driver");
+MODULE_LICENSE("GPL v2");
diff --git a/include/soc/imx/fsl_sip.h b/include/soc/imx/fsl_sip.h
index 75459b10dd82..1f4e31933e0e 100644
--- a/include/soc/imx/fsl_sip.h
+++ b/include/soc/imx/fsl_sip.h
@@ -22,6 +22,11 @@
#define FSL_SIP_SRTC 0xC2000002
#define FSL_SIP_SRTC_SET_TIME 0x00
+#define FSL_SIP_SRTC_START_WDOG 0x01
+#define FSL_SIP_SRTC_STOP_WDOG 0x02
+#define FSL_SIP_SRTC_SET_WDOG_ACT 0x03
+#define FSL_SIP_SRTC_PING_WDOG 0x04
+#define FSL_SIP_SRTC_SET_TIMEOUT_WDOG 0x05
#define IMX8MQ_PD_MIPI 0
#define IMX8MQ_PD_PCIE1 1
@@ -35,4 +40,9 @@
#define IMX8MQ_PD_MIPI_CSI2 9
#define IMX8MQ_PD_PCIE2 10
+#define SC_TIMER_WDOG_ACTION_PARTITION 0 /*!< Reset partition */
+#define SC_TIMER_WDOG_ACTION_WARM 1 /*!< Warm reset system */
+#define SC_TIMER_WDOG_ACTION_COLD 2 /*!< Cold reset system */
+#define SC_TIMER_WDOG_ACTION_BOARD 3 /*!< Reset board */
+
#endif