diff options
-rw-r--r-- | Documentation/devicetree/bindings/watchdog/fsl-imx-wdt.txt | 3 | ||||
-rw-r--r-- | drivers/watchdog/Kconfig | 11 | ||||
-rw-r--r-- | drivers/watchdog/Makefile | 1 | ||||
-rw-r--r-- | drivers/watchdog/imx8_wdt.c | 159 | ||||
-rw-r--r-- | include/soc/imx/fsl_sip.h | 10 |
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 6bdcc08d7cf8..1fc695ace26a 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -596,6 +596,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 991084b6050f..428cd57f9ffb 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -67,6 +67,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 |