MLK-16704-1: watchdog: imx8_wdt: add watchdog driver for i.mx8QM/QXP
authorRobin Gong <yibin.gong@nxp.com>
Fri, 27 Oct 2017 01:40:30 +0000 (09:40 +0800)
committerNitin Garg <nitin.garg@nxp.com>
Mon, 19 Mar 2018 20:39:02 +0000 (15:39 -0500)
This watchdog driver is a virtual driver in Linux and call ATF interface
where call SCFW eventually. In SCFW, it's done by SCU timer tick instead
of hardware watchdog.This is why we have to call ATF because such system
resource owned by secure patition.Currently, booard reset happen if not
ping this software watchdog in time in linux side, may change to partition
reboot once SCFW support this feature in the future.
 BuildInfo:
   - SCFW 93c142a9, IMX-MKIMAGE 2522fd70, ATF f2547fb
   - U-Boot 2017.03-00097-gd7599cf

Signed-off-by: Robin Gong <yibin.gong@nxp.com>
Reviewed-by: Anson Huang <Anson.Huang@nxp.com>
Documentation/devicetree/bindings/watchdog/fsl-imx-wdt.txt
drivers/watchdog/Kconfig
drivers/watchdog/Makefile
drivers/watchdog/imx8_wdt.c [new file with mode: 0644]
include/soc/imx/fsl_sip.h

index 107280e..10771fb 100644 (file)
@@ -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
index 48fd9be..b8b2051 100644 (file)
@@ -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
index 1954250..3d2a0f8 100644 (file)
@@ -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 (file)
index 0000000..1083377
--- /dev/null
@@ -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");
index 75459b1..1f4e319 100644 (file)
 
 #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