diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2021-02-22 20:07:51 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2021-02-22 20:07:51 +0300 |
commit | deb8d159a0a69bd19a328c700bb42b802cbcf7b5 (patch) | |
tree | aee36dbc96ceaf57ac22f39cef3c8ef92cab16c2 /drivers/watchdog | |
parent | 19472481bf58f7c6c939668be885bd300afcb6c6 (diff) | |
parent | a4f3407c41605d14f09e490045d0609990cd5d94 (diff) | |
download | linux-deb8d159a0a69bd19a328c700bb42b802cbcf7b5.tar.xz |
Merge tag 'linux-watchdog-5.12-rc1' of git://www.linux-watchdog.org/linux-watchdog
Pull watchdog updates from Wim Van Sebroeck:
"New drivers:
- Intel Keembay Soc
New chip versions:
- sun4i: Add H616 compatible string
- mt8192 wdt support
- more Rockchip compatibles to snps,dw-wdt.yaml
- binding for Qcom SDX55
- r8a779a0 (V3U) support
Removed drivers:
- sirf prima watchdog driver
- sirf atlas watchdog driver
- zte zx watchdog driver
- tango watchdog driver
- coh901 watchdog driver
And several fixes and clean-ups"
* tag 'linux-watchdog-5.12-rc1' of git://www.linux-watchdog.org/linux-watchdog: (24 commits)
watchdog: qcom: Remove incorrect usage of QCOM_WDT_ENABLE_IRQ
watchdog: mei_wdt: request stop on unregister
dt-bindings: watchdog: Add binding for Qcom SDX55
watchdog: remove coh901 driver
watchdog: remove tango driver
watchdog: remove zte zx driver
watchdog: remove sirf atlas driver
watchdog: remove sirf prima driver
watchdog: mt8192: add wdt support
dt-binding: mt8192: add toprgu reset-controller head file
dt-binding: mediatek: mt8192: update mtk-wdt document
dt-binding: mediatek: watchdog: fix the description of compatible
dt-binding: watchdog: add more Rockchip compatibles to snps,dw-wdt.yaml
dt-bindings: watchdog: sun4i: Add H616 compatible string
watchdog: mtk_wdt: Remove mtk_wdt_stop() in probe() to prevent the system freeze and it doesn't reboot by watchdog problem
watchdog: renesas_wdt: add grace period before rebooting
watchdog: stop wdd when watchdog hw running in reboot_notifier
watchdog: ziirave_wdt: remove unused including <linux/version.h>
dt-bindings: watchdog: renesas,wdt: add r8a779a0 (V3U) support
watchdog: renesas_wdt: don't sleep in atomic context
...
Diffstat (limited to 'drivers/watchdog')
-rw-r--r-- | drivers/watchdog/Kconfig | 65 | ||||
-rw-r--r-- | drivers/watchdog/Makefile | 6 | ||||
-rw-r--r-- | drivers/watchdog/atlas7_wdt.c | 221 | ||||
-rw-r--r-- | drivers/watchdog/coh901327_wdt.c | 408 | ||||
-rw-r--r-- | drivers/watchdog/hpwdt.c | 6 | ||||
-rw-r--r-- | drivers/watchdog/it8712f_wdt.c | 1 | ||||
-rw-r--r-- | drivers/watchdog/keembay_wdt.c | 286 | ||||
-rw-r--r-- | drivers/watchdog/mei_wdt.c | 1 | ||||
-rw-r--r-- | drivers/watchdog/mtk_wdt.c | 23 | ||||
-rw-r--r-- | drivers/watchdog/qcom-wdt.c | 13 | ||||
-rw-r--r-- | drivers/watchdog/renesas_wdt.c | 33 | ||||
-rw-r--r-- | drivers/watchdog/sirfsoc_wdt.c | 216 | ||||
-rw-r--r-- | drivers/watchdog/tangox_wdt.c | 209 | ||||
-rw-r--r-- | drivers/watchdog/watchdog_core.c | 2 | ||||
-rw-r--r-- | drivers/watchdog/ziirave_wdt.c | 1 | ||||
-rw-r--r-- | drivers/watchdog/zx2967_wdt.c | 279 |
16 files changed, 354 insertions, 1416 deletions
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 6b9e93d8532b..1fe0042a48d2 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -254,17 +254,6 @@ config MENZ069_WATCHDOG This driver can also be built as a module. If so the module will be called menz069_wdt. -config TANGOX_WATCHDOG - tristate "Sigma Designs SMP86xx/SMP87xx watchdog" - select WATCHDOG_CORE - depends on ARCH_TANGO || COMPILE_TEST - depends on HAS_IOMEM - help - Support for the watchdog in Sigma Designs SMP86xx (tango3) - and SMP87xx (tango4) family chips. - - This driver can be built as a module. The module name is tangox_wdt. - config WDAT_WDT tristate "ACPI Watchdog Action Table (WDAT)" depends on ACPI @@ -630,17 +619,6 @@ config SUNXI_WATCHDOG To compile this driver as a module, choose M here: the module will be called sunxi_wdt. -config COH901327_WATCHDOG - bool "ST-Ericsson COH 901 327 watchdog" - depends on ARCH_U300 || (ARM && COMMON_CLK && COMPILE_TEST) - default y if MACH_U300 - select WATCHDOG_CORE - help - Say Y here to include Watchdog timer support for the - watchdog embedded into the ST-Ericsson U300 series platforms. - This watchdog is used to reset the system and thus cannot be - compiled as a module. - config NPCM7XX_WATCHDOG tristate "Nuvoton NPCM750 watchdog" depends on ARCH_NPCM || COMPILE_TEST @@ -788,16 +766,6 @@ config MOXART_WDT To compile this driver as a module, choose M here: the module will be called moxart_wdt. -config SIRFSOC_WATCHDOG - tristate "SiRFSOC watchdog" - depends on HAS_IOMEM - depends on ARCH_SIRF || COMPILE_TEST - select WATCHDOG_CORE - default y - help - Support for CSR SiRFprimaII and SiRFatlasVI watchdog. When - the watchdog triggers the system will be reset. - config ST_LPC_WATCHDOG tristate "STMicroelectronics LPC Watchdog" depends on ARCH_STI || COMPILE_TEST @@ -900,16 +868,6 @@ config LPC18XX_WATCHDOG To compile this driver as a module, choose M here: the module will be called lpc18xx_wdt. -config ATLAS7_WATCHDOG - tristate "CSRatlas7 watchdog" - depends on ARCH_ATLAS7 || COMPILE_TEST - help - Say Y here to include Watchdog timer support for the watchdog - existing on the CSRatlas7 series platforms. - - To compile this driver as a module, choose M here: the - module will be called atlas7_wdt. - config RENESAS_WDT tristate "Renesas WDT Watchdog" depends on ARCH_RENESAS || COMPILE_TEST @@ -939,16 +897,6 @@ config ASPEED_WATCHDOG To compile this driver as a module, choose M here: the module will be called aspeed_wdt. -config ZX2967_WATCHDOG - tristate "ZTE zx2967 SoCs watchdog support" - depends on ARCH_ZX - select WATCHDOG_CORE - help - Say Y here to include support for the watchdog timer - in ZTE zx2967 SoCs. - To compile this driver as a module, choose M here: the - module will be called zx2967_wdt. - config STM32_WATCHDOG tristate "STM32 Independent WatchDoG (IWDG) support" depends on ARCH_STM32 @@ -2146,4 +2094,17 @@ config USBPCWATCHDOG Most people will say N. +config KEEMBAY_WATCHDOG + tristate "Intel Keem Bay SoC non-secure watchdog" + depends on ARCH_KEEMBAY || (ARM64 && COMPILE_TEST) + select WATCHDOG_CORE + help + This option enable support for an In-secure watchdog timer driver for + Intel Keem Bay SoC. This WDT has a 32 bit timer and decrements in every + count unit. An interrupt will be triggered, when the count crosses + the thershold configured in the register. + + To compile this driver as a module, choose M here: the + module will be called keembay_wdt. + endif # WATCHDOG diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 74f61e4105d8..f3a6540e725e 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -61,7 +61,6 @@ obj-$(CONFIG_K3_RTI_WATCHDOG) += rti_wdt.o obj-$(CONFIG_ORION_WATCHDOG) += orion_wdt.o obj-$(CONFIG_SUNXI_WATCHDOG) += sunxi_wdt.o obj-$(CONFIG_RN5T618_WATCHDOG) += rn5t618_wdt.o -obj-$(CONFIG_COH901327_WATCHDOG) += coh901327_wdt.o obj-$(CONFIG_NPCM7XX_WATCHDOG) += npcm_wdt.o obj-$(CONFIG_STMP3XXX_RTC_WATCHDOG) += stmp3xxx_rtc_wdt.o obj-$(CONFIG_TS4800_WATCHDOG) += ts4800_wdt.o @@ -73,7 +72,6 @@ obj-$(CONFIG_UX500_WATCHDOG) += ux500_wdt.o obj-$(CONFIG_RETU_WATCHDOG) += retu_wdt.o obj-$(CONFIG_BCM2835_WDT) += bcm2835_wdt.o obj-$(CONFIG_MOXART_WDT) += moxart_wdt.o -obj-$(CONFIG_SIRFSOC_WATCHDOG) += sirfsoc_wdt.o obj-$(CONFIG_ST_LPC_WATCHDOG) += st_lpc_wdt.o obj-$(CONFIG_QCOM_WDT) += qcom-wdt.o obj-$(CONFIG_BCM_KONA_WDT) += bcm_kona_wdt.o @@ -84,11 +82,9 @@ obj-$(CONFIG_MEDIATEK_WATCHDOG) += mtk_wdt.o obj-$(CONFIG_DIGICOLOR_WATCHDOG) += digicolor_wdt.o obj-$(CONFIG_LPC18XX_WATCHDOG) += lpc18xx_wdt.o obj-$(CONFIG_BCM7038_WDT) += bcm7038_wdt.o -obj-$(CONFIG_ATLAS7_WATCHDOG) += atlas7_wdt.o obj-$(CONFIG_RENESAS_WDT) += renesas_wdt.o obj-$(CONFIG_RENESAS_RZAWDT) += rza_wdt.o obj-$(CONFIG_ASPEED_WATCHDOG) += aspeed_wdt.o -obj-$(CONFIG_ZX2967_WATCHDOG) += zx2967_wdt.o obj-$(CONFIG_STM32_WATCHDOG) += stm32_iwdg.o obj-$(CONFIG_UNIPHIER_WATCHDOG) += uniphier_wdt.o obj-$(CONFIG_RTD119X_WATCHDOG) += rtd119x_wdt.o @@ -145,6 +141,7 @@ obj-$(CONFIG_INTEL_MEI_WDT) += mei_wdt.o obj-$(CONFIG_NI903X_WDT) += ni903x_wdt.o obj-$(CONFIG_NIC7018_WDT) += nic7018_wdt.o obj-$(CONFIG_MLX_WDT) += mlx_wdt.o +obj-$(CONFIG_KEEMBAY_WATCHDOG) += keembay_wdt.o # M68K Architecture obj-$(CONFIG_M54xx_WATCHDOG) += m54xx_wdt.o @@ -212,7 +209,6 @@ obj-$(CONFIG_DA9055_WATCHDOG) += da9055_wdt.o obj-$(CONFIG_DA9062_WATCHDOG) += da9062_wdt.o obj-$(CONFIG_DA9063_WATCHDOG) += da9063_wdt.o obj-$(CONFIG_GPIO_WATCHDOG) += gpio_wdt.o -obj-$(CONFIG_TANGOX_WATCHDOG) += tangox_wdt.o obj-$(CONFIG_WDAT_WDT) += wdat_wdt.o obj-$(CONFIG_WM831X_WATCHDOG) += wm831x_wdt.o obj-$(CONFIG_WM8350_WATCHDOG) += wm8350_wdt.o diff --git a/drivers/watchdog/atlas7_wdt.c b/drivers/watchdog/atlas7_wdt.c deleted file mode 100644 index 9bfe650d802f..000000000000 --- a/drivers/watchdog/atlas7_wdt.c +++ /dev/null @@ -1,221 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Watchdog driver for CSR Atlas7 - * - * Copyright (c) 2015 Cambridge Silicon Radio Limited, a CSR plc group company. - */ - -#include <linux/clk.h> -#include <linux/io.h> -#include <linux/module.h> -#include <linux/moduleparam.h> -#include <linux/of.h> -#include <linux/platform_device.h> -#include <linux/watchdog.h> - -#define ATLAS7_TIMER_WDT_INDEX 5 -#define ATLAS7_WDT_DEFAULT_TIMEOUT 20 - -#define ATLAS7_WDT_CNT_CTRL (0 + 4 * ATLAS7_TIMER_WDT_INDEX) -#define ATLAS7_WDT_CNT_MATCH (0x18 + 4 * ATLAS7_TIMER_WDT_INDEX) -#define ATLAS7_WDT_CNT (0x48 + 4 * ATLAS7_TIMER_WDT_INDEX) -#define ATLAS7_WDT_CNT_EN (BIT(0) | BIT(1)) -#define ATLAS7_WDT_EN 0x64 - -static unsigned int timeout = ATLAS7_WDT_DEFAULT_TIMEOUT; -static bool nowayout = WATCHDOG_NOWAYOUT; - -module_param(timeout, uint, 0); -module_param(nowayout, bool, 0); - -MODULE_PARM_DESC(timeout, "Default watchdog timeout (in seconds)"); -MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" - __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); - -struct atlas7_wdog { - struct device *dev; - void __iomem *base; - unsigned long tick_rate; - struct clk *clk; -}; - -static unsigned int atlas7_wdt_gettimeleft(struct watchdog_device *wdd) -{ - struct atlas7_wdog *wdt = watchdog_get_drvdata(wdd); - u32 counter, match, delta; - - counter = readl(wdt->base + ATLAS7_WDT_CNT); - match = readl(wdt->base + ATLAS7_WDT_CNT_MATCH); - delta = match - counter; - - return delta / wdt->tick_rate; -} - -static int atlas7_wdt_ping(struct watchdog_device *wdd) -{ - struct atlas7_wdog *wdt = watchdog_get_drvdata(wdd); - u32 counter, match, delta; - - counter = readl(wdt->base + ATLAS7_WDT_CNT); - delta = wdd->timeout * wdt->tick_rate; - match = counter + delta; - - writel(match, wdt->base + ATLAS7_WDT_CNT_MATCH); - - return 0; -} - -static int atlas7_wdt_enable(struct watchdog_device *wdd) -{ - struct atlas7_wdog *wdt = watchdog_get_drvdata(wdd); - - atlas7_wdt_ping(wdd); - - writel(readl(wdt->base + ATLAS7_WDT_CNT_CTRL) | ATLAS7_WDT_CNT_EN, - wdt->base + ATLAS7_WDT_CNT_CTRL); - writel(1, wdt->base + ATLAS7_WDT_EN); - - return 0; -} - -static int atlas7_wdt_disable(struct watchdog_device *wdd) -{ - struct atlas7_wdog *wdt = watchdog_get_drvdata(wdd); - - writel(0, wdt->base + ATLAS7_WDT_EN); - writel(readl(wdt->base + ATLAS7_WDT_CNT_CTRL) & ~ATLAS7_WDT_CNT_EN, - wdt->base + ATLAS7_WDT_CNT_CTRL); - - return 0; -} - -static int atlas7_wdt_settimeout(struct watchdog_device *wdd, unsigned int to) -{ - wdd->timeout = to; - - return 0; -} - -#define OPTIONS (WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE) - -static const struct watchdog_info atlas7_wdt_ident = { - .options = OPTIONS, - .firmware_version = 0, - .identity = "atlas7 Watchdog", -}; - -static const struct watchdog_ops atlas7_wdt_ops = { - .owner = THIS_MODULE, - .start = atlas7_wdt_enable, - .stop = atlas7_wdt_disable, - .get_timeleft = atlas7_wdt_gettimeleft, - .ping = atlas7_wdt_ping, - .set_timeout = atlas7_wdt_settimeout, -}; - -static struct watchdog_device atlas7_wdd = { - .info = &atlas7_wdt_ident, - .ops = &atlas7_wdt_ops, - .timeout = ATLAS7_WDT_DEFAULT_TIMEOUT, -}; - -static const struct of_device_id atlas7_wdt_ids[] = { - { .compatible = "sirf,atlas7-tick"}, - {} -}; - -static void atlas7_clk_disable_unprepare(void *data) -{ - clk_disable_unprepare(data); -} - -static int atlas7_wdt_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct atlas7_wdog *wdt; - struct clk *clk; - int ret; - - wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL); - if (!wdt) - return -ENOMEM; - wdt->base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(wdt->base)) - return PTR_ERR(wdt->base); - - clk = devm_clk_get(dev, NULL); - if (IS_ERR(clk)) - return PTR_ERR(clk); - ret = clk_prepare_enable(clk); - if (ret) { - dev_err(dev, "clk enable failed\n"); - return ret; - } - ret = devm_add_action_or_reset(dev, atlas7_clk_disable_unprepare, clk); - if (ret) - return ret; - - /* disable watchdog hardware */ - writel(0, wdt->base + ATLAS7_WDT_CNT_CTRL); - - wdt->tick_rate = clk_get_rate(clk); - if (!wdt->tick_rate) - return -EINVAL; - - wdt->clk = clk; - atlas7_wdd.min_timeout = 1; - atlas7_wdd.max_timeout = UINT_MAX / wdt->tick_rate; - - watchdog_init_timeout(&atlas7_wdd, 0, dev); - watchdog_set_nowayout(&atlas7_wdd, nowayout); - - watchdog_set_drvdata(&atlas7_wdd, wdt); - platform_set_drvdata(pdev, &atlas7_wdd); - - watchdog_stop_on_reboot(&atlas7_wdd); - watchdog_stop_on_unregister(&atlas7_wdd); - return devm_watchdog_register_device(dev, &atlas7_wdd); -} - -static int __maybe_unused atlas7_wdt_suspend(struct device *dev) -{ - /* - * NOTE:timer controller registers settings are saved - * and restored back by the timer-atlas7.c - */ - return 0; -} - -static int __maybe_unused atlas7_wdt_resume(struct device *dev) -{ - struct watchdog_device *wdd = dev_get_drvdata(dev); - - /* - * NOTE: Since timer controller registers settings are saved - * and restored back by the timer-atlas7.c, so we need not - * update WD settings except refreshing timeout. - */ - atlas7_wdt_ping(wdd); - - return 0; -} - -static SIMPLE_DEV_PM_OPS(atlas7_wdt_pm_ops, - atlas7_wdt_suspend, atlas7_wdt_resume); - -MODULE_DEVICE_TABLE(of, atlas7_wdt_ids); - -static struct platform_driver atlas7_wdt_driver = { - .driver = { - .name = "atlas7-wdt", - .pm = &atlas7_wdt_pm_ops, - .of_match_table = atlas7_wdt_ids, - }, - .probe = atlas7_wdt_probe, -}; -module_platform_driver(atlas7_wdt_driver); - -MODULE_DESCRIPTION("CSRatlas7 watchdog driver"); -MODULE_AUTHOR("Guo Zeng <Guo.Zeng@csr.com>"); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:atlas7-wdt"); diff --git a/drivers/watchdog/coh901327_wdt.c b/drivers/watchdog/coh901327_wdt.c deleted file mode 100644 index 260c50b08483..000000000000 --- a/drivers/watchdog/coh901327_wdt.c +++ /dev/null @@ -1,408 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * coh901327_wdt.c - * - * Copyright (C) 2008-2009 ST-Ericsson AB - * Watchdog driver for the ST-Ericsson AB COH 901 327 IP core - * Author: Linus Walleij <linus.walleij@stericsson.com> - */ -#include <linux/moduleparam.h> -#include <linux/mod_devicetable.h> -#include <linux/types.h> -#include <linux/watchdog.h> -#include <linux/interrupt.h> -#include <linux/pm.h> -#include <linux/platform_device.h> -#include <linux/io.h> -#include <linux/bitops.h> -#include <linux/clk.h> -#include <linux/delay.h> -#include <linux/err.h> - -#define DRV_NAME "WDOG COH 901 327" - -/* - * COH 901 327 register definitions - */ - -/* WDOG_FEED Register 32bit (-/W) */ -#define U300_WDOG_FR 0x00 -#define U300_WDOG_FR_FEED_RESTART_TIMER 0xFEEDU -/* WDOG_TIMEOUT Register 32bit (R/W) */ -#define U300_WDOG_TR 0x04 -#define U300_WDOG_TR_TIMEOUT_MASK 0x7FFFU -/* WDOG_DISABLE1 Register 32bit (-/W) */ -#define U300_WDOG_D1R 0x08 -#define U300_WDOG_D1R_DISABLE1_DISABLE_TIMER 0x2BADU -/* WDOG_DISABLE2 Register 32bit (R/W) */ -#define U300_WDOG_D2R 0x0C -#define U300_WDOG_D2R_DISABLE2_DISABLE_TIMER 0xCAFEU -#define U300_WDOG_D2R_DISABLE_STATUS_DISABLED 0xDABEU -#define U300_WDOG_D2R_DISABLE_STATUS_ENABLED 0x0000U -/* WDOG_STATUS Register 32bit (R/W) */ -#define U300_WDOG_SR 0x10 -#define U300_WDOG_SR_STATUS_TIMED_OUT 0xCFE8U -#define U300_WDOG_SR_STATUS_NORMAL 0x0000U -#define U300_WDOG_SR_RESET_STATUS_RESET 0xE8B4U -/* WDOG_COUNT Register 32bit (R/-) */ -#define U300_WDOG_CR 0x14 -#define U300_WDOG_CR_VALID_IND 0x8000U -#define U300_WDOG_CR_VALID_STABLE 0x0000U -#define U300_WDOG_CR_COUNT_VALUE_MASK 0x7FFFU -/* WDOG_JTAGOVR Register 32bit (R/W) */ -#define U300_WDOG_JOR 0x18 -#define U300_WDOG_JOR_JTAG_MODE_IND 0x0002U -#define U300_WDOG_JOR_JTAG_WATCHDOG_ENABLE 0x0001U -/* WDOG_RESTART Register 32bit (-/W) */ -#define U300_WDOG_RR 0x1C -#define U300_WDOG_RR_RESTART_VALUE_RESUME 0xACEDU -/* WDOG_IRQ_EVENT Register 32bit (R/W) */ -#define U300_WDOG_IER 0x20 -#define U300_WDOG_IER_WILL_BARK_IRQ_EVENT_IND 0x0001U -#define U300_WDOG_IER_WILL_BARK_IRQ_ACK_ENABLE 0x0001U -/* WDOG_IRQ_MASK Register 32bit (R/W) */ -#define U300_WDOG_IMR 0x24 -#define U300_WDOG_IMR_WILL_BARK_IRQ_ENABLE 0x0001U -/* WDOG_IRQ_FORCE Register 32bit (R/W) */ -#define U300_WDOG_IFR 0x28 -#define U300_WDOG_IFR_WILL_BARK_IRQ_FORCE_ENABLE 0x0001U - -/* Default timeout in seconds = 1 minute */ -#define U300_WDOG_DEFAULT_TIMEOUT 60 - -static unsigned int margin; -static int irq; -static void __iomem *virtbase; -static struct device *parent; - -static struct clk *clk; - -/* - * Enabling and disabling functions. - */ -static void coh901327_enable(u16 timeout) -{ - u16 val; - unsigned long freq; - unsigned long delay_ns; - - /* Restart timer if it is disabled */ - val = readw(virtbase + U300_WDOG_D2R); - if (val == U300_WDOG_D2R_DISABLE_STATUS_DISABLED) - writew(U300_WDOG_RR_RESTART_VALUE_RESUME, - virtbase + U300_WDOG_RR); - /* Acknowledge any pending interrupt so it doesn't just fire off */ - writew(U300_WDOG_IER_WILL_BARK_IRQ_ACK_ENABLE, - virtbase + U300_WDOG_IER); - /* - * The interrupt is cleared in the 32 kHz clock domain. - * Wait 3 32 kHz cycles for it to take effect - */ - freq = clk_get_rate(clk); - delay_ns = DIV_ROUND_UP(1000000000, freq); /* Freq to ns and round up */ - delay_ns = 3 * delay_ns; /* Wait 3 cycles */ - ndelay(delay_ns); - /* Enable the watchdog interrupt */ - writew(U300_WDOG_IMR_WILL_BARK_IRQ_ENABLE, virtbase + U300_WDOG_IMR); - /* Activate the watchdog timer */ - writew(timeout, virtbase + U300_WDOG_TR); - /* Start the watchdog timer */ - writew(U300_WDOG_FR_FEED_RESTART_TIMER, virtbase + U300_WDOG_FR); - /* - * Extra read so that this change propagate in the watchdog. - */ - (void) readw(virtbase + U300_WDOG_CR); - val = readw(virtbase + U300_WDOG_D2R); - if (val != U300_WDOG_D2R_DISABLE_STATUS_ENABLED) - dev_err(parent, - "%s(): watchdog not enabled! D2R value %04x\n", - __func__, val); -} - -static void coh901327_disable(void) -{ - u16 val; - - /* Disable the watchdog interrupt if it is active */ - writew(0x0000U, virtbase + U300_WDOG_IMR); - /* If the watchdog is currently enabled, attempt to disable it */ - val = readw(virtbase + U300_WDOG_D2R); - if (val != U300_WDOG_D2R_DISABLE_STATUS_DISABLED) { - writew(U300_WDOG_D1R_DISABLE1_DISABLE_TIMER, - virtbase + U300_WDOG_D1R); - writew(U300_WDOG_D2R_DISABLE2_DISABLE_TIMER, - virtbase + U300_WDOG_D2R); - /* Write this twice (else problems occur) */ - writew(U300_WDOG_D2R_DISABLE2_DISABLE_TIMER, - virtbase + U300_WDOG_D2R); - } - val = readw(virtbase + U300_WDOG_D2R); - if (val != U300_WDOG_D2R_DISABLE_STATUS_DISABLED) - dev_err(parent, - "%s(): watchdog not disabled! D2R value %04x\n", - __func__, val); -} - -static int coh901327_start(struct watchdog_device *wdt_dev) -{ - coh901327_enable(wdt_dev->timeout * 100); - return 0; -} - -static int coh901327_stop(struct watchdog_device *wdt_dev) -{ - coh901327_disable(); - return 0; -} - -static int coh901327_ping(struct watchdog_device *wdd) -{ - /* Feed the watchdog */ - writew(U300_WDOG_FR_FEED_RESTART_TIMER, - virtbase + U300_WDOG_FR); - return 0; -} - -static int coh901327_settimeout(struct watchdog_device *wdt_dev, - unsigned int time) -{ - wdt_dev->timeout = time; - /* Set new timeout value */ - writew(time * 100, virtbase + U300_WDOG_TR); - /* Feed the dog */ - writew(U300_WDOG_FR_FEED_RESTART_TIMER, - virtbase + U300_WDOG_FR); - return 0; -} - -static unsigned int coh901327_gettimeleft(struct watchdog_device *wdt_dev) -{ - u16 val; - - /* Read repeatedly until the value is stable! */ - val = readw(virtbase + U300_WDOG_CR); - while (val & U300_WDOG_CR_VALID_IND) - val = readw(virtbase + U300_WDOG_CR); - val &= U300_WDOG_CR_COUNT_VALUE_MASK; - if (val != 0) - val /= 100; - - return val; -} - -/* - * This interrupt occurs 10 ms before the watchdog WILL bark. - */ -static irqreturn_t coh901327_interrupt(int irq, void *data) -{ - u16 val; - - /* - * Ack IRQ? If this occurs we're FUBAR anyway, so - * just acknowledge, disable the interrupt and await the imminent end. - * If you at some point need a host of callbacks to be called - * when the system is about to watchdog-reset, add them here! - * - * NOTE: on future versions of this IP-block, it will be possible - * to prevent a watchdog reset by feeding the watchdog at this - * point. - */ - val = readw(virtbase + U300_WDOG_IER); - if (val == U300_WDOG_IER_WILL_BARK_IRQ_EVENT_IND) - writew(U300_WDOG_IER_WILL_BARK_IRQ_ACK_ENABLE, - virtbase + U300_WDOG_IER); - writew(0x0000U, virtbase + U300_WDOG_IMR); - dev_crit(parent, "watchdog is barking!\n"); - return IRQ_HANDLED; -} - -static const struct watchdog_info coh901327_ident = { - .options = WDIOF_CARDRESET | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, - .identity = DRV_NAME, -}; - -static const struct watchdog_ops coh901327_ops = { - .owner = THIS_MODULE, - .start = coh901327_start, - .stop = coh901327_stop, - .ping = coh901327_ping, - .set_timeout = coh901327_settimeout, - .get_timeleft = coh901327_gettimeleft, -}; - -static struct watchdog_device coh901327_wdt = { - .info = &coh901327_ident, - .ops = &coh901327_ops, - /* - * Max timeout is 327 since the 10ms - * timeout register is max - * 0x7FFF = 327670ms ~= 327s. - */ - .min_timeout = 1, - .max_timeout = 327, - .timeout = U300_WDOG_DEFAULT_TIMEOUT, -}; - -static int __init coh901327_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - int ret; - u16 val; - - parent = dev; - - virtbase = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(virtbase)) - return PTR_ERR(virtbase); - - clk = clk_get(dev, NULL); - if (IS_ERR(clk)) { - ret = PTR_ERR(clk); - dev_err(dev, "could not get clock\n"); - return ret; - } - ret = clk_prepare_enable(clk); - if (ret) { - dev_err(dev, "could not prepare and enable clock\n"); - goto out_no_clk_enable; - } - - val = readw(virtbase + U300_WDOG_SR); - switch (val) { - case U300_WDOG_SR_STATUS_TIMED_OUT: - dev_info(dev, "watchdog timed out since last chip reset!\n"); - coh901327_wdt.bootstatus |= WDIOF_CARDRESET; - /* Status will be cleared below */ - break; - case U300_WDOG_SR_STATUS_NORMAL: - dev_info(dev, "in normal status, no timeouts have occurred.\n"); - break; - default: - dev_info(dev, "contains an illegal status code (%08x)\n", val); - break; - } - - val = readw(virtbase + U300_WDOG_D2R); - switch (val) { - case U300_WDOG_D2R_DISABLE_STATUS_DISABLED: - dev_info(dev, "currently disabled.\n"); - break; - case U300_WDOG_D2R_DISABLE_STATUS_ENABLED: - dev_info(dev, "currently enabled! (disabling it now)\n"); - coh901327_disable(); - break; - default: - dev_err(dev, "contains an illegal enable/disable code (%08x)\n", - val); - break; - } - - /* Reset the watchdog */ - writew(U300_WDOG_SR_RESET_STATUS_RESET, virtbase + U300_WDOG_SR); - - irq = platform_get_irq(pdev, 0); - if (request_irq(irq, coh901327_interrupt, 0, - DRV_NAME " Bark", pdev)) { - ret = -EIO; - goto out_no_irq; - } - - watchdog_init_timeout(&coh901327_wdt, margin, dev); - - coh901327_wdt.parent = dev; - ret = watchdog_register_device(&coh901327_wdt); - if (ret) - goto out_no_wdog; - - dev_info(dev, "initialized. (timeout=%d sec)\n", - coh901327_wdt.timeout); - return 0; - -out_no_wdog: - free_irq(irq, pdev); -out_no_irq: - clk_disable_unprepare(clk); -out_no_clk_enable: - clk_put(clk); - return ret; -} - -#ifdef CONFIG_PM - -static u16 wdogenablestore; -static u16 irqmaskstore; - -static int coh901327_suspend(struct platform_device *pdev, pm_message_t state) -{ - irqmaskstore = readw(virtbase + U300_WDOG_IMR) & 0x0001U; - wdogenablestore = readw(virtbase + U300_WDOG_D2R); - /* If watchdog is on, disable it here and now */ - if (wdogenablestore == U300_WDOG_D2R_DISABLE_STATUS_ENABLED) - coh901327_disable(); - return 0; -} - -static int coh901327_resume(struct platform_device *pdev) -{ - /* Restore the watchdog interrupt */ - writew(irqmaskstore, virtbase + U300_WDOG_IMR); - if (wdogenablestore == U300_WDOG_D2R_DISABLE_STATUS_ENABLED) { - /* Restart the watchdog timer */ - writew(U300_WDOG_RR_RESTART_VALUE_RESUME, - virtbase + U300_WDOG_RR); - writew(U300_WDOG_FR_FEED_RESTART_TIMER, - virtbase + U300_WDOG_FR); - } - return 0; -} -#else -#define coh901327_suspend NULL -#define coh901327_resume NULL -#endif - -/* - * Mistreating the watchdog is the only way to perform a software reset of the - * system on EMP platforms. So we implement this and export a symbol for it. - */ -void coh901327_watchdog_reset(void) -{ - /* Enable even if on JTAG too */ - writew(U300_WDOG_JOR_JTAG_WATCHDOG_ENABLE, - virtbase + U300_WDOG_JOR); - /* - * Timeout = 5s, we have to wait for the watchdog reset to - * actually take place: the watchdog will be reloaded with the - * default value immediately, so we HAVE to reboot and get back - * into the kernel in 30s, or the device will reboot again! - * The boot loader will typically deactivate the watchdog, so we - * need time enough for the boot loader to get to the point of - * deactivating the watchdog before it is shut down by it. - * - * NOTE: on future versions of the watchdog, this restriction is - * gone: the watchdog will be reloaded with a default value (1 min) - * instead of last value, and you can conveniently set the watchdog - * timeout to 10ms (value = 1) without any problems. - */ - coh901327_enable(500); - /* Return and await doom */ -} - -static const struct of_device_id coh901327_dt_match[] = { - { .compatible = "stericsson,coh901327" }, - {}, -}; - -static struct platform_driver coh901327_driver = { - .driver = { - .name = "coh901327_wdog", - .of_match_table = coh901327_dt_match, - .suppress_bind_attrs = true, - }, - .suspend = coh901327_suspend, - .resume = coh901327_resume, -}; -builtin_platform_driver_probe(coh901327_driver, coh901327_probe); - -/* not really modular, but ... */ -module_param(margin, uint, 0); -MODULE_PARM_DESC(margin, "Watchdog margin in seconds (default 60s)"); diff --git a/drivers/watchdog/hpwdt.c b/drivers/watchdog/hpwdt.c index cbd1498ff015..22ddba3802ef 100644 --- a/drivers/watchdog/hpwdt.c +++ b/drivers/watchdog/hpwdt.c @@ -123,7 +123,7 @@ static int hpwdt_settimeout(struct watchdog_device *wdd, unsigned int val) if (val <= wdd->pretimeout) { dev_dbg(wdd->parent, "pretimeout < timeout. Setting to zero\n"); wdd->pretimeout = 0; - pretimeout = 0; + pretimeout = false; if (watchdog_active(wdd)) hpwdt_start(wdd); } @@ -336,13 +336,13 @@ static int hpwdt_init_one(struct pci_dev *dev, watchdog_init_timeout(&hpwdt_dev, soft_margin, NULL); if (is_kdump_kernel()) { - pretimeout = 0; + pretimeout = false; kdumptimeout = 0; } if (pretimeout && hpwdt_dev.timeout <= PRETIMEOUT_SEC) { dev_warn(&dev->dev, "timeout <= pretimeout. Setting pretimeout to zero\n"); - pretimeout = 0; + pretimeout = false; } hpwdt_dev.pretimeout = pretimeout ? PRETIMEOUT_SEC : 0; kdumptimeout = min(kdumptimeout, HPWDT_MAX_TIMER); diff --git a/drivers/watchdog/it8712f_wdt.c b/drivers/watchdog/it8712f_wdt.c index 9b89d2f09568..3ce6a58bd81e 100644 --- a/drivers/watchdog/it8712f_wdt.c +++ b/drivers/watchdog/it8712f_wdt.c @@ -31,7 +31,6 @@ #include <linux/io.h> #include <linux/ioport.h> -#define DEBUG #define NAME "it8712f_wdt" MODULE_AUTHOR("Jorge Boncompte - DTI2 <jorge@dti2.net>"); diff --git a/drivers/watchdog/keembay_wdt.c b/drivers/watchdog/keembay_wdt.c new file mode 100644 index 000000000000..547d3fea33ff --- /dev/null +++ b/drivers/watchdog/keembay_wdt.c @@ -0,0 +1,286 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Watchdog driver for Intel Keem Bay non-secure watchdog. + * + * Copyright (C) 2020 Intel Corporation + */ + +#include <linux/arm-smccc.h> +#include <linux/bits.h> +#include <linux/clk.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/limits.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/platform_device.h> +#include <linux/reboot.h> +#include <linux/watchdog.h> + +/* Non-secure watchdog register offsets */ +#define TIM_WATCHDOG 0x0 +#define TIM_WATCHDOG_INT_THRES 0x4 +#define TIM_WDOG_EN 0x8 +#define TIM_SAFE 0xc + +#define WDT_ISR_MASK GENMASK(9, 8) +#define WDT_ISR_CLEAR 0x8200ff18 +#define WDT_UNLOCK 0xf1d0dead +#define WDT_LOAD_MAX U32_MAX +#define WDT_LOAD_MIN 1 +#define WDT_TIMEOUT 5 + +static unsigned int timeout = WDT_TIMEOUT; +module_param(timeout, int, 0); +MODULE_PARM_DESC(timeout, "Watchdog timeout period in seconds (default = " + __MODULE_STRING(WDT_TIMEOUT) ")"); + +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default = " + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); + +struct keembay_wdt { + struct watchdog_device wdd; + struct clk *clk; + unsigned int rate; + int to_irq; + int th_irq; + void __iomem *base; +}; + +static inline u32 keembay_wdt_readl(struct keembay_wdt *wdt, u32 offset) +{ + return readl(wdt->base + offset); +} + +static inline void keembay_wdt_writel(struct keembay_wdt *wdt, u32 offset, u32 val) +{ + writel(WDT_UNLOCK, wdt->base + TIM_SAFE); + writel(val, wdt->base + offset); +} + +static void keembay_wdt_set_timeout_reg(struct watchdog_device *wdog) +{ + struct keembay_wdt *wdt = watchdog_get_drvdata(wdog); + + keembay_wdt_writel(wdt, TIM_WATCHDOG, wdog->timeout * wdt->rate); +} + +static void keembay_wdt_set_pretimeout_reg(struct watchdog_device *wdog) +{ + struct keembay_wdt *wdt = watchdog_get_drvdata(wdog); + u32 th_val = 0; + + if (wdog->pretimeout) + th_val = wdog->timeout - wdog->pretimeout; + + keembay_wdt_writel(wdt, TIM_WATCHDOG_INT_THRES, th_val * wdt->rate); +} + +static int keembay_wdt_start(struct watchdog_device *wdog) +{ + struct keembay_wdt *wdt = watchdog_get_drvdata(wdog); + + keembay_wdt_set_timeout_reg(wdog); + keembay_wdt_writel(wdt, TIM_WDOG_EN, 1); + + return 0; +} + +static int keembay_wdt_stop(struct watchdog_device *wdog) +{ + struct keembay_wdt *wdt = watchdog_get_drvdata(wdog); + + keembay_wdt_writel(wdt, TIM_WDOG_EN, 0); + + return 0; +} + +static int keembay_wdt_ping(struct watchdog_device *wdog) +{ + keembay_wdt_set_timeout_reg(wdog); + + return 0; +} + +static int keembay_wdt_set_timeout(struct watchdog_device *wdog, u32 t) +{ + wdog->timeout = t; + keembay_wdt_set_timeout_reg(wdog); + + return 0; +} + +static int keembay_wdt_set_pretimeout(struct watchdog_device *wdog, u32 t) +{ + if (t > wdog->timeout) + return -EINVAL; + + wdog->pretimeout = t; + keembay_wdt_set_pretimeout_reg(wdog); + + return 0; +} + +static unsigned int keembay_wdt_get_timeleft(struct watchdog_device *wdog) +{ + struct keembay_wdt *wdt = watchdog_get_drvdata(wdog); + + return keembay_wdt_readl(wdt, TIM_WATCHDOG) / wdt->rate; +} + +/* + * SMC call is used to clear the interrupt bits, because the TIM_GEN_CONFIG + * register is in the secure bank. + */ +static irqreturn_t keembay_wdt_to_isr(int irq, void *dev_id) +{ + struct keembay_wdt *wdt = dev_id; + struct arm_smccc_res res; + + keembay_wdt_writel(wdt, TIM_WATCHDOG, 1); + arm_smccc_smc(WDT_ISR_CLEAR, WDT_ISR_MASK, 0, 0, 0, 0, 0, 0, &res); + dev_crit(wdt->wdd.parent, "Intel Keem Bay non-sec wdt timeout.\n"); + emergency_restart(); + + return IRQ_HANDLED; +} + +static irqreturn_t keembay_wdt_th_isr(int irq, void *dev_id) +{ + struct keembay_wdt *wdt = dev_id; + struct arm_smccc_res res; + + arm_smccc_smc(WDT_ISR_CLEAR, WDT_ISR_MASK, 0, 0, 0, 0, 0, 0, &res); + dev_crit(wdt->wdd.parent, "Intel Keem Bay non-sec wdt pre-timeout.\n"); + watchdog_notify_pretimeout(&wdt->wdd); + + return IRQ_HANDLED; +} + +static const struct watchdog_info keembay_wdt_info = { + .identity = "Intel Keem Bay Watchdog Timer", + .options = WDIOF_SETTIMEOUT | + WDIOF_PRETIMEOUT | + WDIOF_MAGICCLOSE | + WDIOF_KEEPALIVEPING, +}; + +static const struct watchdog_ops keembay_wdt_ops = { + .owner = THIS_MODULE, + .start = keembay_wdt_start, + .stop = keembay_wdt_stop, + .ping = keembay_wdt_ping, + .set_timeout = keembay_wdt_set_timeout, + .set_pretimeout = keembay_wdt_set_pretimeout, + .get_timeleft = keembay_wdt_get_timeleft, +}; + +static int keembay_wdt_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct keembay_wdt *wdt; + int ret; + + wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL); + if (!wdt) + return -ENOMEM; + + wdt->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(wdt->base)) + return PTR_ERR(wdt->base); + + /* we do not need to enable the clock as it is enabled by default */ + wdt->clk = devm_clk_get(dev, NULL); + if (IS_ERR(wdt->clk)) + return dev_err_probe(dev, PTR_ERR(wdt->clk), "Failed to get clock\n"); + + wdt->rate = clk_get_rate(wdt->clk); + if (!wdt->rate) + return dev_err_probe(dev, -EINVAL, "Failed to get clock rate\n"); + + wdt->th_irq = platform_get_irq_byname(pdev, "threshold"); + if (wdt->th_irq < 0) + return dev_err_probe(dev, wdt->th_irq, "Failed to get IRQ for threshold\n"); + + ret = devm_request_irq(dev, wdt->th_irq, keembay_wdt_th_isr, 0, + "keembay-wdt", wdt); + if (ret) + return dev_err_probe(dev, ret, "Failed to request IRQ for threshold\n"); + + wdt->to_irq = platform_get_irq_byname(pdev, "timeout"); + if (wdt->to_irq < 0) + return dev_err_probe(dev, wdt->to_irq, "Failed to get IRQ for timeout\n"); + + ret = devm_request_irq(dev, wdt->to_irq, keembay_wdt_to_isr, 0, + "keembay-wdt", wdt); + if (ret) + return dev_err_probe(dev, ret, "Failed to request IRQ for timeout\n"); + + wdt->wdd.parent = dev; + wdt->wdd.info = &keembay_wdt_info; + wdt->wdd.ops = &keembay_wdt_ops; + wdt->wdd.min_timeout = WDT_LOAD_MIN; + wdt->wdd.max_timeout = WDT_LOAD_MAX / wdt->rate; + wdt->wdd.timeout = WDT_TIMEOUT; + + watchdog_set_drvdata(&wdt->wdd, wdt); + watchdog_set_nowayout(&wdt->wdd, nowayout); + watchdog_init_timeout(&wdt->wdd, timeout, dev); + keembay_wdt_set_timeout(&wdt->wdd, wdt->wdd.timeout); + + ret = devm_watchdog_register_device(dev, &wdt->wdd); + if (ret) + return dev_err_probe(dev, ret, "Failed to register watchdog device.\n"); + + platform_set_drvdata(pdev, wdt); + dev_info(dev, "Initial timeout %d sec%s.\n", + wdt->wdd.timeout, nowayout ? ", nowayout" : ""); + + return 0; +} + +static int __maybe_unused keembay_wdt_suspend(struct device *dev) +{ + struct keembay_wdt *wdt = dev_get_drvdata(dev); + + if (watchdog_active(&wdt->wdd)) + return keembay_wdt_stop(&wdt->wdd); + + return 0; +} + +static int __maybe_unused keembay_wdt_resume(struct device *dev) +{ + struct keembay_wdt *wdt = dev_get_drvdata(dev); + + if (watchdog_active(&wdt->wdd)) + return keembay_wdt_start(&wdt->wdd); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(keembay_wdt_pm_ops, keembay_wdt_suspend, + keembay_wdt_resume); + +static const struct of_device_id keembay_wdt_match[] = { + { .compatible = "intel,keembay-wdt" }, + { } +}; +MODULE_DEVICE_TABLE(of, keembay_wdt_match); + +static struct platform_driver keembay_wdt_driver = { + .probe = keembay_wdt_probe, + .driver = { + .name = "keembay_wdt", + .of_match_table = keembay_wdt_match, + .pm = &keembay_wdt_pm_ops, + }, +}; + +module_platform_driver(keembay_wdt_driver); + +MODULE_DESCRIPTION("Intel Keem Bay SoC watchdog driver"); +MODULE_AUTHOR("Wan Ahmad Zainie <wan.ahmad.zainie.wan.mohamad@intel.com"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/watchdog/mei_wdt.c b/drivers/watchdog/mei_wdt.c index 5391bf3e6b11..c5967d8b4256 100644 --- a/drivers/watchdog/mei_wdt.c +++ b/drivers/watchdog/mei_wdt.c @@ -382,6 +382,7 @@ static int mei_wdt_register(struct mei_wdt *wdt) watchdog_set_drvdata(&wdt->wdd, wdt); watchdog_stop_on_reboot(&wdt->wdd); + watchdog_stop_on_unregister(&wdt->wdd); ret = watchdog_register_device(&wdt->wdd); if (ret) diff --git a/drivers/watchdog/mtk_wdt.c b/drivers/watchdog/mtk_wdt.c index d6a6393f609d..97ca993bd009 100644 --- a/drivers/watchdog/mtk_wdt.c +++ b/drivers/watchdog/mtk_wdt.c @@ -11,6 +11,7 @@ #include <dt-bindings/reset-controller/mt2712-resets.h> #include <dt-bindings/reset-controller/mt8183-resets.h> +#include <dt-bindings/reset-controller/mt8192-resets.h> #include <linux/delay.h> #include <linux/err.h> #include <linux/init.h> @@ -76,6 +77,10 @@ static const struct mtk_wdt_data mt8183_data = { .toprgu_sw_rst_num = MT8183_TOPRGU_SW_RST_NUM, }; +static const struct mtk_wdt_data mt8192_data = { + .toprgu_sw_rst_num = MT8192_TOPRGU_SW_RST_NUM, +}; + static int toprgu_reset_update(struct reset_controller_dev *rcdev, unsigned long id, bool assert) { @@ -195,6 +200,19 @@ static int mtk_wdt_set_timeout(struct watchdog_device *wdt_dev, return 0; } +static void mtk_wdt_init(struct watchdog_device *wdt_dev) +{ + struct mtk_wdt_dev *mtk_wdt = watchdog_get_drvdata(wdt_dev); + void __iomem *wdt_base; + + wdt_base = mtk_wdt->wdt_base; + + if (readl(wdt_base + WDT_MODE) & WDT_MODE_EN) { + set_bit(WDOG_HW_RUNNING, &wdt_dev->status); + mtk_wdt_set_timeout(wdt_dev, wdt_dev->timeout); + } +} + static int mtk_wdt_stop(struct watchdog_device *wdt_dev) { struct mtk_wdt_dev *mtk_wdt = watchdog_get_drvdata(wdt_dev); @@ -264,7 +282,7 @@ static int mtk_wdt_probe(struct platform_device *pdev) mtk_wdt->wdt_dev.info = &mtk_wdt_info; mtk_wdt->wdt_dev.ops = &mtk_wdt_ops; mtk_wdt->wdt_dev.timeout = WDT_MAX_TIMEOUT; - mtk_wdt->wdt_dev.max_timeout = WDT_MAX_TIMEOUT; + mtk_wdt->wdt_dev.max_hw_heartbeat_ms = WDT_MAX_TIMEOUT * 1000; mtk_wdt->wdt_dev.min_timeout = WDT_MIN_TIMEOUT; mtk_wdt->wdt_dev.parent = dev; @@ -274,7 +292,7 @@ static int mtk_wdt_probe(struct platform_device *pdev) watchdog_set_drvdata(&mtk_wdt->wdt_dev, mtk_wdt); - mtk_wdt_stop(&mtk_wdt->wdt_dev); + mtk_wdt_init(&mtk_wdt->wdt_dev); watchdog_stop_on_reboot(&mtk_wdt->wdt_dev); err = devm_watchdog_register_device(dev, &mtk_wdt->wdt_dev); @@ -322,6 +340,7 @@ static const struct of_device_id mtk_wdt_dt_ids[] = { { .compatible = "mediatek,mt2712-wdt", .data = &mt2712_data }, { .compatible = "mediatek,mt6589-wdt" }, { .compatible = "mediatek,mt8183-wdt", .data = &mt8183_data }, + { .compatible = "mediatek,mt8192-wdt", .data = &mt8192_data }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, mtk_wdt_dt_ids); diff --git a/drivers/watchdog/qcom-wdt.c b/drivers/watchdog/qcom-wdt.c index 7cf0f2ec649b..e38a87ffe5f5 100644 --- a/drivers/watchdog/qcom-wdt.c +++ b/drivers/watchdog/qcom-wdt.c @@ -22,7 +22,6 @@ enum wdt_reg { }; #define QCOM_WDT_ENABLE BIT(0) -#define QCOM_WDT_ENABLE_IRQ BIT(1) static const u32 reg_offset_data_apcs_tmr[] = { [WDT_RST] = 0x38, @@ -63,16 +62,6 @@ struct qcom_wdt *to_qcom_wdt(struct watchdog_device *wdd) return container_of(wdd, struct qcom_wdt, wdd); } -static inline int qcom_get_enable(struct watchdog_device *wdd) -{ - int enable = QCOM_WDT_ENABLE; - - if (wdd->pretimeout) - enable |= QCOM_WDT_ENABLE_IRQ; - - return enable; -} - static irqreturn_t qcom_wdt_isr(int irq, void *arg) { struct watchdog_device *wdd = arg; @@ -91,7 +80,7 @@ static int qcom_wdt_start(struct watchdog_device *wdd) writel(1, wdt_addr(wdt, WDT_RST)); writel(bark * wdt->rate, wdt_addr(wdt, WDT_BARK_TIME)); writel(wdd->timeout * wdt->rate, wdt_addr(wdt, WDT_BITE_TIME)); - writel(qcom_get_enable(wdd), wdt_addr(wdt, WDT_EN)); + writel(QCOM_WDT_ENABLE, wdt_addr(wdt, WDT_EN)); return 0; } diff --git a/drivers/watchdog/renesas_wdt.c b/drivers/watchdog/renesas_wdt.c index 47fce4de0110..5791198960e6 100644 --- a/drivers/watchdog/renesas_wdt.c +++ b/drivers/watchdog/renesas_wdt.c @@ -9,6 +9,7 @@ #include <linux/clk.h> #include <linux/delay.h> #include <linux/io.h> +#include <linux/iopoll.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/of.h> @@ -50,6 +51,7 @@ struct rwdt_priv { struct watchdog_device wdev; unsigned long clk_rate; u8 cks; + struct clk *clk; }; static void rwdt_write(struct rwdt_priv *priv, u32 val, unsigned int reg) @@ -125,13 +127,33 @@ static unsigned int rwdt_get_timeleft(struct watchdog_device *wdev) return DIV_BY_CLKS_PER_SEC(priv, 65536 - val); } +/* needs to be atomic - no RPM, no usleep_range, no scheduling! */ static int rwdt_restart(struct watchdog_device *wdev, unsigned long action, void *data) { struct rwdt_priv *priv = watchdog_get_drvdata(wdev); + u8 val; + + clk_prepare_enable(priv->clk); + + /* Stop the timer before we modify any register */ + val = readb_relaxed(priv->base + RWTCSRA) & ~RWTCSRA_TME; + rwdt_write(priv, val, RWTCSRA); + /* Delay 2 cycles before setting watchdog counter */ + udelay(DIV_ROUND_UP(2 * 1000000, priv->clk_rate)); - rwdt_start(wdev); rwdt_write(priv, 0xffff, RWTCNT); + /* smallest divider to reboot soon */ + rwdt_write(priv, 0, RWTCSRA); + + readb_poll_timeout_atomic(priv->base + RWTCSRA, val, + !(val & RWTCSRA_WRFLG), 1, 100); + + rwdt_write(priv, RWTCSRA_TME, RWTCSRA); + + /* wait 2 cycles, so watchdog will trigger */ + udelay(DIV_ROUND_UP(2 * 1000000, priv->clk_rate)); + return 0; } @@ -191,7 +213,6 @@ static int rwdt_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct rwdt_priv *priv; - struct clk *clk; unsigned long clks_per_sec; int ret, i; u8 csra; @@ -207,13 +228,13 @@ static int rwdt_probe(struct platform_device *pdev) if (IS_ERR(priv->base)) return PTR_ERR(priv->base); - clk = devm_clk_get(dev, NULL); - if (IS_ERR(clk)) - return PTR_ERR(clk); + priv->clk = devm_clk_get(dev, NULL); + if (IS_ERR(priv->clk)) + return PTR_ERR(priv->clk); pm_runtime_enable(dev); pm_runtime_get_sync(dev); - priv->clk_rate = clk_get_rate(clk); + priv->clk_rate = clk_get_rate(priv->clk); csra = readb_relaxed(priv->base + RWTCSRA); priv->wdev.bootstatus = csra & RWTCSRA_WOVF ? WDIOF_CARDRESET : 0; pm_runtime_put(dev); diff --git a/drivers/watchdog/sirfsoc_wdt.c b/drivers/watchdog/sirfsoc_wdt.c deleted file mode 100644 index 734cf2966ecb..000000000000 --- a/drivers/watchdog/sirfsoc_wdt.c +++ /dev/null @@ -1,216 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Watchdog driver for CSR SiRFprimaII and SiRFatlasVI - * - * Copyright (c) 2013 Cambridge Silicon Radio Limited, a CSR plc group company. - */ - -#include <linux/module.h> -#include <linux/watchdog.h> -#include <linux/platform_device.h> -#include <linux/moduleparam.h> -#include <linux/of.h> -#include <linux/io.h> -#include <linux/uaccess.h> - -#define CLOCK_FREQ 1000000 - -#define SIRFSOC_TIMER_COUNTER_LO 0x0000 -#define SIRFSOC_TIMER_MATCH_0 0x0008 -#define SIRFSOC_TIMER_INT_EN 0x0024 -#define SIRFSOC_TIMER_WATCHDOG_EN 0x0028 -#define SIRFSOC_TIMER_LATCH 0x0030 -#define SIRFSOC_TIMER_LATCHED_LO 0x0034 - -#define SIRFSOC_TIMER_WDT_INDEX 5 - -#define SIRFSOC_WDT_MIN_TIMEOUT 30 /* 30 secs */ -#define SIRFSOC_WDT_MAX_TIMEOUT (10 * 60) /* 10 mins */ -#define SIRFSOC_WDT_DEFAULT_TIMEOUT 30 /* 30 secs */ - -static unsigned int timeout; -static bool nowayout = WATCHDOG_NOWAYOUT; - -module_param(timeout, uint, 0); -module_param(nowayout, bool, 0); - -MODULE_PARM_DESC(timeout, "Default watchdog timeout (in seconds)"); -MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" - __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); - -static void __iomem *sirfsoc_wdt_base(struct watchdog_device *wdd) -{ - return (void __iomem __force *)watchdog_get_drvdata(wdd); -} - -static unsigned int sirfsoc_wdt_gettimeleft(struct watchdog_device *wdd) -{ - u32 counter, match; - void __iomem *wdt_base; - int time_left; - - wdt_base = sirfsoc_wdt_base(wdd); - counter = readl(wdt_base + SIRFSOC_TIMER_COUNTER_LO); - match = readl(wdt_base + - SIRFSOC_TIMER_MATCH_0 + (SIRFSOC_TIMER_WDT_INDEX << 2)); - - time_left = match - counter; - - return time_left / CLOCK_FREQ; -} - -static int sirfsoc_wdt_updatetimeout(struct watchdog_device *wdd) -{ - u32 counter, timeout_ticks; - void __iomem *wdt_base; - - timeout_ticks = wdd->timeout * CLOCK_FREQ; - wdt_base = sirfsoc_wdt_base(wdd); - - /* Enable the latch before reading the LATCH_LO register */ - writel(1, wdt_base + SIRFSOC_TIMER_LATCH); - - /* Set the TO value */ - counter = readl(wdt_base + SIRFSOC_TIMER_LATCHED_LO); - - counter += timeout_ticks; - - writel(counter, wdt_base + - SIRFSOC_TIMER_MATCH_0 + (SIRFSOC_TIMER_WDT_INDEX << 2)); - - return 0; -} - -static int sirfsoc_wdt_enable(struct watchdog_device *wdd) -{ - void __iomem *wdt_base = sirfsoc_wdt_base(wdd); - sirfsoc_wdt_updatetimeout(wdd); - - /* - * NOTE: If interrupt is not enabled - * then WD-Reset doesn't get generated at all. - */ - writel(readl(wdt_base + SIRFSOC_TIMER_INT_EN) - | (1 << SIRFSOC_TIMER_WDT_INDEX), - wdt_base + SIRFSOC_TIMER_INT_EN); - writel(1, wdt_base + SIRFSOC_TIMER_WATCHDOG_EN); - - return 0; -} - -static int sirfsoc_wdt_disable(struct watchdog_device *wdd) -{ - void __iomem *wdt_base = sirfsoc_wdt_base(wdd); - - writel(0, wdt_base + SIRFSOC_TIMER_WATCHDOG_EN); - writel(readl(wdt_base + SIRFSOC_TIMER_INT_EN) - & (~(1 << SIRFSOC_TIMER_WDT_INDEX)), - wdt_base + SIRFSOC_TIMER_INT_EN); - - return 0; -} - -static int sirfsoc_wdt_settimeout(struct watchdog_device *wdd, unsigned int to) -{ - wdd->timeout = to; - sirfsoc_wdt_updatetimeout(wdd); - - return 0; -} - -#define OPTIONS (WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE) - -static const struct watchdog_info sirfsoc_wdt_ident = { - .options = OPTIONS, - .firmware_version = 0, - .identity = "SiRFSOC Watchdog", -}; - -static const struct watchdog_ops sirfsoc_wdt_ops = { - .owner = THIS_MODULE, - .start = sirfsoc_wdt_enable, - .stop = sirfsoc_wdt_disable, - .get_timeleft = sirfsoc_wdt_gettimeleft, - .ping = sirfsoc_wdt_updatetimeout, - .set_timeout = sirfsoc_wdt_settimeout, -}; - -static struct watchdog_device sirfsoc_wdd = { - .info = &sirfsoc_wdt_ident, - .ops = &sirfsoc_wdt_ops, - .timeout = SIRFSOC_WDT_DEFAULT_TIMEOUT, - .min_timeout = SIRFSOC_WDT_MIN_TIMEOUT, - .max_timeout = SIRFSOC_WDT_MAX_TIMEOUT, -}; - -static int sirfsoc_wdt_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - int ret; - void __iomem *base; - - base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(base)) - return PTR_ERR(base); - - watchdog_set_drvdata(&sirfsoc_wdd, (__force void *)base); - - watchdog_init_timeout(&sirfsoc_wdd, timeout, dev); - watchdog_set_nowayout(&sirfsoc_wdd, nowayout); - sirfsoc_wdd.parent = dev; - - watchdog_stop_on_reboot(&sirfsoc_wdd); - watchdog_stop_on_unregister(&sirfsoc_wdd); - ret = devm_watchdog_register_device(dev, &sirfsoc_wdd); - if (ret) - return ret; - - platform_set_drvdata(pdev, &sirfsoc_wdd); - - return 0; -} - -#ifdef CONFIG_PM_SLEEP -static int sirfsoc_wdt_suspend(struct device *dev) -{ - return 0; -} - -static int sirfsoc_wdt_resume(struct device *dev) -{ - struct watchdog_device *wdd = dev_get_drvdata(dev); - - /* - * NOTE: Since timer controller registers settings are saved - * and restored back by the timer-prima2.c, so we need not - * update WD settings except refreshing timeout. - */ - sirfsoc_wdt_updatetimeout(wdd); - - return 0; -} -#endif - -static SIMPLE_DEV_PM_OPS(sirfsoc_wdt_pm_ops, - sirfsoc_wdt_suspend, sirfsoc_wdt_resume); - -static const struct of_device_id sirfsoc_wdt_of_match[] = { - { .compatible = "sirf,prima2-tick"}, - {}, -}; -MODULE_DEVICE_TABLE(of, sirfsoc_wdt_of_match); - -static struct platform_driver sirfsoc_wdt_driver = { - .driver = { - .name = "sirfsoc-wdt", - .pm = &sirfsoc_wdt_pm_ops, - .of_match_table = sirfsoc_wdt_of_match, - }, - .probe = sirfsoc_wdt_probe, -}; -module_platform_driver(sirfsoc_wdt_driver); - -MODULE_DESCRIPTION("SiRF SoC watchdog driver"); -MODULE_AUTHOR("Xianglong Du <Xianglong.Du@csr.com>"); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:sirfsoc-wdt"); diff --git a/drivers/watchdog/tangox_wdt.c b/drivers/watchdog/tangox_wdt.c deleted file mode 100644 index 1afb0e9d808c..000000000000 --- a/drivers/watchdog/tangox_wdt.c +++ /dev/null @@ -1,209 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (C) 2015 Mans Rullgard <mans@mansr.com> - * SMP86xx/SMP87xx Watchdog driver - */ - -#include <linux/bitops.h> -#include <linux/clk.h> -#include <linux/delay.h> -#include <linux/io.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/moduleparam.h> -#include <linux/mod_devicetable.h> -#include <linux/platform_device.h> -#include <linux/watchdog.h> - -#define DEFAULT_TIMEOUT 30 - -static bool nowayout = WATCHDOG_NOWAYOUT; -module_param(nowayout, bool, 0); -MODULE_PARM_DESC(nowayout, - "Watchdog cannot be stopped once started (default=" - __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); - -static unsigned int timeout; -module_param(timeout, int, 0); -MODULE_PARM_DESC(timeout, "Watchdog timeout"); - -/* - * Counter counts down from programmed value. Reset asserts when - * the counter reaches 1. - */ -#define WD_COUNTER 0 - -#define WD_CONFIG 4 -#define WD_CONFIG_XTAL_IN BIT(0) -#define WD_CONFIG_DISABLE BIT(31) - -struct tangox_wdt_device { - struct watchdog_device wdt; - void __iomem *base; - unsigned long clk_rate; - struct clk *clk; -}; - -static int tangox_wdt_set_timeout(struct watchdog_device *wdt, - unsigned int new_timeout) -{ - wdt->timeout = new_timeout; - - return 0; -} - -static int tangox_wdt_start(struct watchdog_device *wdt) -{ - struct tangox_wdt_device *dev = watchdog_get_drvdata(wdt); - u32 ticks; - - ticks = 1 + wdt->timeout * dev->clk_rate; - writel(ticks, dev->base + WD_COUNTER); - - return 0; -} - -static int tangox_wdt_stop(struct watchdog_device *wdt) -{ - struct tangox_wdt_device *dev = watchdog_get_drvdata(wdt); - - writel(0, dev->base + WD_COUNTER); - - return 0; -} - -static unsigned int tangox_wdt_get_timeleft(struct watchdog_device *wdt) -{ - struct tangox_wdt_device *dev = watchdog_get_drvdata(wdt); - u32 count; - - count = readl(dev->base + WD_COUNTER); - - if (!count) - return 0; - - return (count - 1) / dev->clk_rate; -} - -static const struct watchdog_info tangox_wdt_info = { - .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, - .identity = "tangox watchdog", -}; - -static int tangox_wdt_restart(struct watchdog_device *wdt, - unsigned long action, void *data) -{ - struct tangox_wdt_device *dev = watchdog_get_drvdata(wdt); - - writel(1, dev->base + WD_COUNTER); - - return 0; -} - -static const struct watchdog_ops tangox_wdt_ops = { - .start = tangox_wdt_start, - .stop = tangox_wdt_stop, - .set_timeout = tangox_wdt_set_timeout, - .get_timeleft = tangox_wdt_get_timeleft, - .restart = tangox_wdt_restart, -}; - -static void tangox_clk_disable_unprepare(void *data) -{ - clk_disable_unprepare(data); -} - -static int tangox_wdt_probe(struct platform_device *pdev) -{ - struct tangox_wdt_device *dev; - u32 config; - int err; - - dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); - if (!dev) - return -ENOMEM; - - dev->base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(dev->base)) - return PTR_ERR(dev->base); - - dev->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(dev->clk)) - return PTR_ERR(dev->clk); - - err = clk_prepare_enable(dev->clk); - if (err) - return err; - err = devm_add_action_or_reset(&pdev->dev, - tangox_clk_disable_unprepare, dev->clk); - if (err) - return err; - - dev->clk_rate = clk_get_rate(dev->clk); - if (!dev->clk_rate) - return -EINVAL; - - dev->wdt.parent = &pdev->dev; - dev->wdt.info = &tangox_wdt_info; - dev->wdt.ops = &tangox_wdt_ops; - dev->wdt.timeout = DEFAULT_TIMEOUT; - dev->wdt.min_timeout = 1; - dev->wdt.max_hw_heartbeat_ms = (U32_MAX - 1) / dev->clk_rate; - - watchdog_init_timeout(&dev->wdt, timeout, &pdev->dev); - watchdog_set_nowayout(&dev->wdt, nowayout); - watchdog_set_drvdata(&dev->wdt, dev); - - /* - * Deactivate counter if disable bit is set to avoid - * accidental reset. - */ - config = readl(dev->base + WD_CONFIG); - if (config & WD_CONFIG_DISABLE) - writel(0, dev->base + WD_COUNTER); - - writel(WD_CONFIG_XTAL_IN, dev->base + WD_CONFIG); - - /* - * Mark as active and restart with configured timeout if - * already running. - */ - if (readl(dev->base + WD_COUNTER)) { - set_bit(WDOG_HW_RUNNING, &dev->wdt.status); - tangox_wdt_start(&dev->wdt); - } - - watchdog_set_restart_priority(&dev->wdt, 128); - - watchdog_stop_on_unregister(&dev->wdt); - err = devm_watchdog_register_device(&pdev->dev, &dev->wdt); - if (err) - return err; - - platform_set_drvdata(pdev, dev); - - dev_info(&pdev->dev, "SMP86xx/SMP87xx watchdog registered\n"); - - return 0; -} - -static const struct of_device_id tangox_wdt_dt_ids[] = { - { .compatible = "sigma,smp8642-wdt" }, - { .compatible = "sigma,smp8759-wdt" }, - { } -}; -MODULE_DEVICE_TABLE(of, tangox_wdt_dt_ids); - -static struct platform_driver tangox_wdt_driver = { - .probe = tangox_wdt_probe, - .driver = { - .name = "tangox-wdt", - .of_match_table = tangox_wdt_dt_ids, - }, -}; - -module_platform_driver(tangox_wdt_driver); - -MODULE_AUTHOR("Mans Rullgard <mans@mansr.com>"); -MODULE_DESCRIPTION("SMP86xx/SMP87xx Watchdog driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/watchdog/watchdog_core.c b/drivers/watchdog/watchdog_core.c index 0e9a99559609..5df0a22e2cb4 100644 --- a/drivers/watchdog/watchdog_core.c +++ b/drivers/watchdog/watchdog_core.c @@ -158,7 +158,7 @@ static int watchdog_reboot_notifier(struct notifier_block *nb, wdd = container_of(nb, struct watchdog_device, reboot_nb); if (code == SYS_DOWN || code == SYS_HALT) { - if (watchdog_active(wdd)) { + if (watchdog_active(wdd) || watchdog_hw_running(wdd)) { int ret; ret = wdd->ops->stop(wdd); diff --git a/drivers/watchdog/ziirave_wdt.c b/drivers/watchdog/ziirave_wdt.c index cab86a08456b..4297280807ca 100644 --- a/drivers/watchdog/ziirave_wdt.c +++ b/drivers/watchdog/ziirave_wdt.c @@ -18,7 +18,6 @@ #include <linux/slab.h> #include <linux/sysfs.h> #include <linux/types.h> -#include <linux/version.h> #include <linux/watchdog.h> #include <asm/unaligned.h> diff --git a/drivers/watchdog/zx2967_wdt.c b/drivers/watchdog/zx2967_wdt.c deleted file mode 100644 index bf183e73671a..000000000000 --- a/drivers/watchdog/zx2967_wdt.c +++ /dev/null @@ -1,279 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * watchdog driver for ZTE's zx2967 family - * - * Copyright (C) 2017 ZTE Ltd. - * - * Author: Baoyou Xie <baoyou.xie@linaro.org> - */ - -#include <linux/clk.h> -#include <linux/io.h> -#include <linux/mfd/syscon.h> -#include <linux/module.h> -#include <linux/of_address.h> -#include <linux/platform_device.h> -#include <linux/regmap.h> -#include <linux/reset.h> -#include <linux/watchdog.h> - -#define ZX2967_WDT_CFG_REG 0x4 -#define ZX2967_WDT_LOAD_REG 0x8 -#define ZX2967_WDT_REFRESH_REG 0x18 -#define ZX2967_WDT_START_REG 0x1c - -#define ZX2967_WDT_REFRESH_MASK GENMASK(5, 0) - -#define ZX2967_WDT_CFG_DIV(n) ((((n) & 0xff) - 1) << 8) -#define ZX2967_WDT_START_EN 0x1 - -/* - * Hardware magic number. - * When watchdog reg is written, the lowest 16 bits are valid, but - * the highest 16 bits should be always this number. - */ -#define ZX2967_WDT_WRITEKEY (0x1234 << 16) -#define ZX2967_WDT_VAL_MASK GENMASK(15, 0) - -#define ZX2967_WDT_DIV_DEFAULT 16 -#define ZX2967_WDT_DEFAULT_TIMEOUT 32 -#define ZX2967_WDT_MIN_TIMEOUT 1 -#define ZX2967_WDT_MAX_TIMEOUT 524 -#define ZX2967_WDT_MAX_COUNT 0xffff - -#define ZX2967_WDT_CLK_FREQ 0x8000 - -#define ZX2967_WDT_FLAG_REBOOT_MON BIT(0) - -struct zx2967_wdt { - struct watchdog_device wdt_device; - void __iomem *reg_base; - struct clk *clock; -}; - -static inline u32 zx2967_wdt_readl(struct zx2967_wdt *wdt, u16 reg) -{ - return readl_relaxed(wdt->reg_base + reg); -} - -static inline void zx2967_wdt_writel(struct zx2967_wdt *wdt, u16 reg, u32 val) -{ - writel_relaxed(val | ZX2967_WDT_WRITEKEY, wdt->reg_base + reg); -} - -static void zx2967_wdt_refresh(struct zx2967_wdt *wdt) -{ - u32 val; - - val = zx2967_wdt_readl(wdt, ZX2967_WDT_REFRESH_REG); - /* - * Bit 4-5, 1 and 2: refresh config info - * Bit 2-3, 1 and 2: refresh counter - * Bit 0-1, 1 and 2: refresh int-value - * we shift each group value between 1 and 2 to refresh all data. - */ - val ^= ZX2967_WDT_REFRESH_MASK; - zx2967_wdt_writel(wdt, ZX2967_WDT_REFRESH_REG, - val & ZX2967_WDT_VAL_MASK); -} - -static int -zx2967_wdt_set_timeout(struct watchdog_device *wdd, unsigned int timeout) -{ - struct zx2967_wdt *wdt = watchdog_get_drvdata(wdd); - unsigned int divisor = ZX2967_WDT_DIV_DEFAULT; - u32 count; - - count = timeout * ZX2967_WDT_CLK_FREQ; - if (count > divisor * ZX2967_WDT_MAX_COUNT) - divisor = DIV_ROUND_UP(count, ZX2967_WDT_MAX_COUNT); - count = DIV_ROUND_UP(count, divisor); - zx2967_wdt_writel(wdt, ZX2967_WDT_CFG_REG, - ZX2967_WDT_CFG_DIV(divisor) & ZX2967_WDT_VAL_MASK); - zx2967_wdt_writel(wdt, ZX2967_WDT_LOAD_REG, - count & ZX2967_WDT_VAL_MASK); - zx2967_wdt_refresh(wdt); - wdd->timeout = (count * divisor) / ZX2967_WDT_CLK_FREQ; - - return 0; -} - -static void __zx2967_wdt_start(struct zx2967_wdt *wdt) -{ - u32 val; - - val = zx2967_wdt_readl(wdt, ZX2967_WDT_START_REG); - val |= ZX2967_WDT_START_EN; - zx2967_wdt_writel(wdt, ZX2967_WDT_START_REG, - val & ZX2967_WDT_VAL_MASK); -} - -static void __zx2967_wdt_stop(struct zx2967_wdt *wdt) -{ - u32 val; - - val = zx2967_wdt_readl(wdt, ZX2967_WDT_START_REG); - val &= ~ZX2967_WDT_START_EN; - zx2967_wdt_writel(wdt, ZX2967_WDT_START_REG, - val & ZX2967_WDT_VAL_MASK); -} - -static int zx2967_wdt_start(struct watchdog_device *wdd) -{ - struct zx2967_wdt *wdt = watchdog_get_drvdata(wdd); - - zx2967_wdt_set_timeout(wdd, wdd->timeout); - __zx2967_wdt_start(wdt); - - return 0; -} - -static int zx2967_wdt_stop(struct watchdog_device *wdd) -{ - struct zx2967_wdt *wdt = watchdog_get_drvdata(wdd); - - __zx2967_wdt_stop(wdt); - - return 0; -} - -static int zx2967_wdt_keepalive(struct watchdog_device *wdd) -{ - struct zx2967_wdt *wdt = watchdog_get_drvdata(wdd); - - zx2967_wdt_refresh(wdt); - - return 0; -} - -#define ZX2967_WDT_OPTIONS \ - (WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE) -static const struct watchdog_info zx2967_wdt_ident = { - .options = ZX2967_WDT_OPTIONS, - .identity = "zx2967 watchdog", -}; - -static const struct watchdog_ops zx2967_wdt_ops = { - .owner = THIS_MODULE, - .start = zx2967_wdt_start, - .stop = zx2967_wdt_stop, - .ping = zx2967_wdt_keepalive, - .set_timeout = zx2967_wdt_set_timeout, -}; - -static void zx2967_wdt_reset_sysctrl(struct device *dev) -{ - int ret; - void __iomem *regmap; - unsigned int offset, mask, config; - struct of_phandle_args out_args; - - ret = of_parse_phandle_with_fixed_args(dev->of_node, - "zte,wdt-reset-sysctrl", 3, 0, &out_args); - if (ret) - return; - - offset = out_args.args[0]; - config = out_args.args[1]; - mask = out_args.args[2]; - - regmap = syscon_node_to_regmap(out_args.np); - if (IS_ERR(regmap)) { - of_node_put(out_args.np); - return; - } - - regmap_update_bits(regmap, offset, mask, config); - of_node_put(out_args.np); -} - -static void zx2967_clk_disable_unprepare(void *data) -{ - clk_disable_unprepare(data); -} - -static int zx2967_wdt_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct zx2967_wdt *wdt; - int ret; - struct reset_control *rstc; - - wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL); - if (!wdt) - return -ENOMEM; - - platform_set_drvdata(pdev, wdt); - - wdt->wdt_device.info = &zx2967_wdt_ident; - wdt->wdt_device.ops = &zx2967_wdt_ops; - wdt->wdt_device.timeout = ZX2967_WDT_DEFAULT_TIMEOUT; - wdt->wdt_device.max_timeout = ZX2967_WDT_MAX_TIMEOUT; - wdt->wdt_device.min_timeout = ZX2967_WDT_MIN_TIMEOUT; - wdt->wdt_device.parent = dev; - - wdt->reg_base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(wdt->reg_base)) - return PTR_ERR(wdt->reg_base); - - zx2967_wdt_reset_sysctrl(dev); - - wdt->clock = devm_clk_get(dev, NULL); - if (IS_ERR(wdt->clock)) { - dev_err(dev, "failed to find watchdog clock source\n"); - return PTR_ERR(wdt->clock); - } - - ret = clk_prepare_enable(wdt->clock); - if (ret < 0) { - dev_err(dev, "failed to enable clock\n"); - return ret; - } - ret = devm_add_action_or_reset(dev, zx2967_clk_disable_unprepare, - wdt->clock); - if (ret) - return ret; - clk_set_rate(wdt->clock, ZX2967_WDT_CLK_FREQ); - - rstc = devm_reset_control_get_exclusive(dev, NULL); - if (IS_ERR(rstc)) { - dev_err(dev, "failed to get rstc"); - return PTR_ERR(rstc); - } - - reset_control_assert(rstc); - reset_control_deassert(rstc); - - watchdog_set_drvdata(&wdt->wdt_device, wdt); - watchdog_init_timeout(&wdt->wdt_device, - ZX2967_WDT_DEFAULT_TIMEOUT, dev); - watchdog_set_nowayout(&wdt->wdt_device, WATCHDOG_NOWAYOUT); - - ret = devm_watchdog_register_device(dev, &wdt->wdt_device); - if (ret) - return ret; - - dev_info(dev, "watchdog enabled (timeout=%d sec, nowayout=%d)", - wdt->wdt_device.timeout, WATCHDOG_NOWAYOUT); - - return 0; -} - -static const struct of_device_id zx2967_wdt_match[] = { - { .compatible = "zte,zx296718-wdt", }, - {} -}; -MODULE_DEVICE_TABLE(of, zx2967_wdt_match); - -static struct platform_driver zx2967_wdt_driver = { - .probe = zx2967_wdt_probe, - .driver = { - .name = "zx2967-wdt", - .of_match_table = of_match_ptr(zx2967_wdt_match), - }, -}; -module_platform_driver(zx2967_wdt_driver); - -MODULE_AUTHOR("Baoyou Xie <baoyou.xie@linaro.org>"); -MODULE_DESCRIPTION("ZTE zx2967 Watchdog Device Driver"); -MODULE_LICENSE("GPL v2"); |