diff options
Diffstat (limited to 'drivers/watchdog')
122 files changed, 3288 insertions, 813 deletions
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 6bee137cfbe0..0c25b2ed44eb 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -181,6 +181,19 @@ config BD957XMUF_WATCHDOG watchdog. Alternatively say M to compile the driver as a module, which will be called bd9576_wdt. +config BD96801_WATCHDOG + tristate "ROHM BD96801 PMIC Watchdog" + depends on MFD_ROHM_BD96801 + select WATCHDOG_CORE + help + Support for the watchdog in the ROHM BD96801 PMIC. Watchdog can be + configured to only generate IRQ or to trigger system reset via reset + pin. + + Say Y here to include support for the ROHM BD96801 watchdog. + Alternatively say M to compile the driver as a module, + which will be called bd96801_wdt. + config CROS_EC_WATCHDOG tristate "ChromeOS EC-based watchdog" select WATCHDOG_CORE @@ -254,6 +267,30 @@ config GPIO_WATCHDOG_ARCH_INITCALL arch_initcall. If in doubt, say N. +config LENOVO_SE10_WDT + tristate "Lenovo SE10 Watchdog" + depends on (X86 && DMI) || COMPILE_TEST + depends on HAS_IOPORT + select WATCHDOG_CORE + help + If you say yes here you get support for the watchdog + functionality for the Lenovo SE10 platform. + + This driver can also be built as a module. If so, the module + will be called lenovo-se10-wdt. + +config LENOVO_SE30_WDT + tristate "Lenovo SE30 Watchdog" + depends on (X86 && DMI) || COMPILE_TEST + depends on HAS_IOPORT + select WATCHDOG_CORE + help + If you say yes here you get support for the watchdog + functionality for the Lenovo SE30 platform. + + This driver can also be built as a module. If so, the module + will be called lenovo-se30-wdt. + config MENF21BMC_WATCHDOG tristate "MEN 14F021P00 BMC Watchdog" depends on MFD_MENF21BMC || COMPILE_TEST @@ -383,6 +420,14 @@ config SL28CPLD_WATCHDOG # ARM Architecture +config AIROHA_WATCHDOG + tristate "Airoha EN7581 Watchdog" + depends on ARCH_AIROHA || COMPILE_TEST + select WATCHDOG_CORE + help + Watchdog timer embedded into Airoha SoC. This will reboot your + system when the timeout is reached. + config ARM_SP805_WATCHDOG tristate "ARM SP805 Watchdog" depends on (ARM || ARM64 || COMPILE_TEST) && ARM_AMBA @@ -482,6 +527,7 @@ config 21285_WATCHDOG config 977_WATCHDOG tristate "NetWinder WB83C977 watchdog" depends on (FOOTBRIDGE && ARCH_NETWINDER) || (ARM && COMPILE_TEST) + depends on HAS_IOPORT help Say Y here to include support for the WB977 watchdog included in NetWinder machines. Alternatively say M to compile the driver as @@ -523,6 +569,7 @@ config S3C2410_WATCHDOG tristate "S3C6410/S5Pv210/Exynos Watchdog" depends on ARCH_S3C64XX || ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST select WATCHDOG_CORE + select MFD_SYSCON if ARCH_EXYNOS help Watchdog timer block in the Samsung S3C64xx, S5Pv210 and Exynos SoCs. This will reboot the system when the timer expires with @@ -757,6 +804,15 @@ config IMX7ULP_WDT To compile this driver as a module, choose M here: the module will be called imx7ulp_wdt. +config S32G_WDT + tristate "S32G Watchdog" + depends on ARCH_S32 || COMPILE_TEST + select WATCHDOG_CORE + help + This is the driver for the hardware watchdog on the NXP + S32G platforms. If you wish to have watchdog support + enabled, say Y, otherwise say N. + config DB500_WATCHDOG tristate "ST-Ericsson DB800 watchdog" depends on MFD_DB8500_PRCMU @@ -920,12 +976,23 @@ config RENESAS_RZN1WDT config RENESAS_RZG2LWDT tristate "Renesas RZ/G2L WDT Watchdog" - depends on ARCH_RENESAS || COMPILE_TEST + depends on ARCH_RZG2L || ARCH_R9A09G011 || COMPILE_TEST + depends on PM || COMPILE_TEST select WATCHDOG_CORE help This driver adds watchdog support for the integrated watchdogs in the Renesas RZ/G2L SoCs. These watchdogs can be used to reset a system. +config RENESAS_RZV2HWDT + tristate "Renesas RZ/{G3E,V2H(P)} WDT Watchdog" + depends on ARCH_RENESAS || COMPILE_TEST + depends on PM || COMPILE_TEST + select WATCHDOG_CORE + help + This driver adds watchdog support for the integrated watchdogs in the + Renesas RZ/{G3E,V2H(P)} SoCs. These watchdogs can be used to reset a + system. + config ASPEED_WATCHDOG tristate "Aspeed BMC watchdog support" depends on ARCH_ASPEED || COMPILE_TEST @@ -943,7 +1010,7 @@ config STM32_WATCHDOG tristate "STM32 Independent WatchDoG (IWDG) support" depends on ARCH_STM32 || COMPILE_TEST select WATCHDOG_CORE - default y + default ARCH_STM32 help Say Y here to include support for the watchdog timer in stm32 SoCs. @@ -1075,7 +1142,7 @@ config ACQUIRE_WDT config ADVANTECH_WDT tristate "Advantech SBC Watchdog Timer" - depends on X86 || COMPILE_TEST + depends on (X86 || COMPILE_TEST) && HAS_IOPORT help If you are configuring a Linux kernel for the Advantech single-board computer, say `Y' here to support its built-in watchdog timer @@ -1084,7 +1151,7 @@ config ADVANTECH_WDT config ADVANTECH_EC_WDT tristate "Advantech Embedded Controller Watchdog Timer" - depends on X86 || COMPILE_TEST + depends on (X86 || COMPILE_TEST) && HAS_IOPORT select ISA_BUS_API select WATCHDOG_CORE help @@ -1115,9 +1182,19 @@ config ALIM7101_WDT Most people will say N. +config CGBC_WDT + tristate "Congatec Board Controller Watchdog Timer" + depends on MFD_CGBC + select WATCHDOG_CORE + help + Enables watchdog timer support for the Congatec Board Controller. + + This driver can also be built as a module. If so, the module will be + called cgbc_wdt. + config EBC_C384_WDT tristate "WinSystems EBC-C384 Watchdog Timer" - depends on X86 || COMPILE_TEST + depends on (X86 || COMPILE_TEST) && HAS_IOPORT select ISA_BUS_API select WATCHDOG_CORE help @@ -1127,7 +1204,7 @@ config EBC_C384_WDT config EXAR_WDT tristate "Exar Watchdog Timer" - depends on X86 || COMPILE_TEST + depends on (X86 || COMPILE_TEST) && HAS_IOPORT select WATCHDOG_CORE help Enables watchdog timer support for the watchdog timer present @@ -1138,7 +1215,7 @@ config EXAR_WDT config F71808E_WDT tristate "Fintek F718xx, F818xx Super I/O Watchdog" - depends on X86 || COMPILE_TEST + depends on (X86 || COMPILE_TEST) && HAS_IOPORT select WATCHDOG_CORE help This is the driver for the hardware watchdog on the Fintek F71808E, @@ -1150,7 +1227,7 @@ config F71808E_WDT config SP5100_TCO tristate "AMD/ATI SP5100 TCO Timer/Watchdog" - depends on (X86 || COMPILE_TEST) && PCI + depends on (X86 || COMPILE_TEST) && PCI && HAS_IOPORT select WATCHDOG_CORE help Hardware watchdog driver for the AMD/ATI SP5100 chipset. The TCO @@ -1189,7 +1266,7 @@ config SC520_WDT config SBC_FITPC2_WATCHDOG tristate "Compulab SBC-FITPC2 watchdog" - depends on X86 || COMPILE_TEST + depends on (X86 || COMPILE_TEST) && HAS_IOPORT help This is the driver for the built-in watchdog timer on the fit-PC2, fit-PC2i, CM-iAM single-board computers made by Compulab. @@ -1214,7 +1291,7 @@ config SBC_FITPC2_WATCHDOG config EUROTECH_WDT tristate "Eurotech CPU-1220/1410 Watchdog Timer" - depends on X86 || COMPILE_TEST + depends on (X86 || COMPILE_TEST) && HAS_IOPORT help Enable support for the watchdog timer on the Eurotech CPU-1220 and CPU-1410 cards. These are PC/104 SBCs. Spec sheets and product @@ -1222,7 +1299,7 @@ config EUROTECH_WDT config IB700_WDT tristate "IB700 SBC Watchdog Timer" - depends on X86 || COMPILE_TEST + depends on (X86 || COMPILE_TEST) && HAS_IOPORT help This is the driver for the hardware watchdog on the IB700 Single Board Computer produced by TMC Technology (www.tmc-uk.com). This @@ -1239,7 +1316,7 @@ config IB700_WDT config IBMASR tristate "IBM Automatic Server Restart" - depends on X86 || COMPILE_TEST + depends on (X86 || COMPILE_TEST) && HAS_IOPORT help This is the driver for the IBM Automatic Server Restart watchdog timer built-in into some eServer xSeries machines. @@ -1249,7 +1326,7 @@ config IBMASR config WAFER_WDT tristate "ICP Single Board Computer Watchdog Timer" - depends on X86 || COMPILE_TEST + depends on (X86 || COMPILE_TEST) && HAS_IOPORT help This is a driver for the hardware watchdog on the ICP Single Board Computer. This driver is working on (at least) the following @@ -1271,7 +1348,7 @@ config I6300ESB_WDT config IE6XX_WDT tristate "Intel Atom E6xx Watchdog" - depends on (X86 || COMPILE_TEST) && PCI + depends on (X86 || COMPILE_TEST) && PCI && HAS_IOPORT select WATCHDOG_CORE select MFD_CORE select LPC_SCH @@ -1295,12 +1372,24 @@ config INTEL_MID_WATCHDOG To compile this driver as a module, choose M here. +config INTEL_OC_WATCHDOG + tristate "Intel OC Watchdog" + depends on (X86 || COMPILE_TEST) && ACPI && HAS_IOPORT + select WATCHDOG_CORE + help + Hardware driver for Intel Over-Clocking watchdog present in + Platform Controller Hub (PCH) chipsets. + + To compile this driver as a module, choose M here: the + module will be called intel_oc_wdt. + config ITCO_WDT tristate "Intel TCO Timer/Watchdog" depends on X86 && PCI select WATCHDOG_CORE depends on I2C || I2C=n depends on MFD_INTEL_PMC_BXT || !MFD_INTEL_PMC_BXT + depends on HAS_IOPORT # for I2C_I801 select LPC_ICH if !EXPERT select I2C_I801 if !EXPERT && I2C help @@ -1331,7 +1420,7 @@ config ITCO_VENDOR_SUPPORT config IT8712F_WDT tristate "IT8712F (Smart Guardian) Watchdog Timer" - depends on X86 || COMPILE_TEST + depends on (X86 || COMPILE_TEST) && HAS_IOPORT help This is the driver for the built-in watchdog timer on the IT8712F Super I/0 chipset used on many motherboards. @@ -1344,7 +1433,7 @@ config IT8712F_WDT config IT87_WDT tristate "IT87 Watchdog Timer" - depends on X86 || COMPILE_TEST + depends on (X86 || COMPILE_TEST) && HAS_IOPORT select WATCHDOG_CORE help This is the driver for the hardware watchdog on the ITE IT8607, @@ -1392,7 +1481,7 @@ config KEMPLD_WDT config SC1200_WDT tristate "National Semiconductor PC87307/PC97307 (ala SC1200) Watchdog" - depends on X86 || COMPILE_TEST + depends on (X86 || COMPILE_TEST) && HAS_IOPORT help This is a driver for National Semiconductor PC87307/PC97307 hardware watchdog cards as found on the SC1200. This watchdog is mainly used @@ -1415,7 +1504,7 @@ config SCx200_WDT config PC87413_WDT tristate "NS PC87413 watchdog" - depends on X86 || COMPILE_TEST + depends on (X86 || COMPILE_TEST) && HAS_IOPORT help This is the driver for the hardware watchdog on the PC87413 chipset This watchdog simply watches your kernel to make sure it doesn't @@ -1429,7 +1518,7 @@ config PC87413_WDT config NV_TCO tristate "nVidia TCO Timer/Watchdog" - depends on (X86 || COMPILE_TEST) && PCI + depends on (X86 || COMPILE_TEST) && PCI && HAS_IOPORT help Hardware driver for the TCO timer built into the nVidia Hub family (such as the MCP51). The TCO (Total Cost of Ownership) timer is a @@ -1458,7 +1547,7 @@ config RDC321X_WDT config 60XX_WDT tristate "SBC-60XX Watchdog Timer" - depends on X86 || COMPILE_TEST + depends on (X86 || COMPILE_TEST) && HAS_IOPORT help This driver can be used with the watchdog timer found on some single board computers, namely the 6010 PII based computer. @@ -1472,7 +1561,7 @@ config 60XX_WDT config SBC8360_WDT tristate "SBC8360 Watchdog Timer" - depends on X86_32 + depends on X86_32 && HAS_IOPORT help This is the driver for the hardware watchdog on the SBC8360 Single @@ -1485,7 +1574,7 @@ config SBC8360_WDT config SBC7240_WDT tristate "SBC Nano 7240 Watchdog Timer" - depends on X86_32 && !UML + depends on X86_32 && HAS_IOPORT help This is the driver for the hardware watchdog found on the IEI single board computers EPIC Nano 7240 (and likely others). This @@ -1496,17 +1585,9 @@ config SBC7240_WDT To compile this driver as a module, choose M here: the module will be called sbc7240_wdt. -config CPU5_WDT - tristate "SMA CPU5 Watchdog" - depends on X86 || COMPILE_TEST - help - TBD. - To compile this driver as a module, choose M here: the - module will be called cpu5wdt. - config SMSC_SCH311X_WDT tristate "SMSC SCH311X Watchdog Timer" - depends on X86 || COMPILE_TEST + depends on (X86 || COMPILE_TEST) && HAS_IOPORT help This is the driver for the hardware watchdog timer on the SMSC SCH3112, SCH3114 and SCH3116 Super IO chipset @@ -1518,7 +1599,7 @@ config SMSC_SCH311X_WDT config SMSC37B787_WDT tristate "Winbond SMsC37B787 Watchdog Timer" - depends on X86 || COMPILE_TEST + depends on (X86 || COMPILE_TEST) && HAS_IOPORT help This is the driver for the hardware watchdog component on the Winbond SMsC37B787 chipset as used on the NetRunner Mainboard @@ -1564,7 +1645,7 @@ config VIA_WDT config W83627HF_WDT tristate "Watchdog timer for W83627HF/W83627DHG and compatibles" - depends on X86 || COMPILE_TEST + depends on (X86 || COMPILE_TEST) && HAS_IOPORT select WATCHDOG_CORE help This is the driver for the hardware watchdog on the following @@ -1594,7 +1675,7 @@ config W83627HF_WDT config W83877F_WDT tristate "W83877F (EMACS) Watchdog Timer" - depends on X86 || COMPILE_TEST + depends on (X86 || COMPILE_TEST) && HAS_IOPORT help This is the driver for the hardware watchdog on the W83877F chipset as used in EMACS PC-104 motherboards (and likely others). This @@ -1609,7 +1690,7 @@ config W83877F_WDT config W83977F_WDT tristate "W83977F (PCM-5335) Watchdog Timer" - depends on X86 || COMPILE_TEST + depends on (X86 || COMPILE_TEST) && HAS_IOPORT help This is the driver for the hardware watchdog on the W83977F I/O chip as used in AAEON's PCM-5335 SBC (and likely others). This @@ -1622,7 +1703,7 @@ config W83977F_WDT config MACHZ_WDT tristate "ZF MachZ Watchdog" - depends on X86 || COMPILE_TEST + depends on (X86 || COMPILE_TEST) && HAS_IOPORT help If you are using a ZF Micro MachZ processor, say Y here, otherwise N. This is the driver for the watchdog timer built-in on that @@ -1635,7 +1716,7 @@ config MACHZ_WDT config SBC_EPX_C3_WATCHDOG tristate "Winsystems SBC EPX-C3 watchdog" - depends on X86 || COMPILE_TEST + depends on (X86 || COMPILE_TEST) && HAS_IOPORT help This is the driver for the built-in watchdog timer on the EPX-C3 Single-board computer made by Winsystems, Inc. @@ -1682,7 +1763,8 @@ config NI903X_WDT config NIC7018_WDT tristate "NIC7018 Watchdog" - depends on X86 && ACPI + depends on HAS_IOPORT + depends on ACPI || COMPILE_TEST select WATCHDOG_CORE help Support for National Instruments NIC7018 Watchdog. @@ -1807,7 +1889,7 @@ config OCTEON_WDT config MARVELL_GTI_WDT tristate "Marvell GTI Watchdog driver" depends on ARCH_THUNDER || (COMPILE_TEST && 64BIT) - default y + default ARCH_THUNDER select WATCHDOG_CORE help Marvell GTI hardware supports watchdog timer. First timeout @@ -1973,7 +2055,7 @@ config 8xxx_WDT config PIKA_WDT tristate "PIKA FPGA Watchdog" depends on WARP || (PPC64 && COMPILE_TEST) - default y + default WARP help This enables the watchdog in the PIKA FPGA. Currently used on the Warp platform. @@ -2197,7 +2279,7 @@ comment "PCI-based Watchdog Cards" config PCIPCWATCHDOG tristate "Berkshire Products PCI-PC Watchdog" - depends on PCI + depends on PCI && HAS_IOPORT help This is the driver for the Berkshire Products PCI-PC Watchdog card. This card simply watches your kernel to make sure it doesn't freeze, @@ -2212,7 +2294,7 @@ config PCIPCWATCHDOG config WDTPCI tristate "PCI-WDT500/501 Watchdog timer" - depends on PCI + depends on PCI && HAS_IOPORT help If you have a PCI-WDT500/501 watchdog board, say Y here, otherwise N. diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 3710c218f05e..bbd4d62d2cc3 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -40,6 +40,7 @@ obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb.o obj-$(CONFIG_ARM_SP805_WATCHDOG) += sp805_wdt.o obj-$(CONFIG_ARM_SBSA_WATCHDOG) += sbsa_gwdt.o obj-$(CONFIG_ARMADA_37XX_WATCHDOG) += armada_37xx_wdt.o +obj-$(CONFIG_AIROHA_WATCHDOG) += airoha_wdt.o obj-$(CONFIG_ASM9260_WATCHDOG) += asm9260_wdt.o obj-$(CONFIG_AT91RM9200_WATCHDOG) += at91rm9200_wdt.o obj-$(CONFIG_AT91SAM9X_WATCHDOG) += at91sam9_wdt.o @@ -68,6 +69,7 @@ obj-$(CONFIG_TS72XX_WATCHDOG) += ts72xx_wdt.o obj-$(CONFIG_IMX2_WDT) += imx2_wdt.o obj-$(CONFIG_IMX_SC_WDT) += imx_sc_wdt.o obj-$(CONFIG_IMX7ULP_WDT) += imx7ulp_wdt.o +obj-$(CONFIG_S32G_WDT) += s32g_wdt.o obj-$(CONFIG_DB500_WATCHDOG) += db8500_wdt.o obj-$(CONFIG_RETU_WATCHDOG) += retu_wdt.o obj-$(CONFIG_BCM2835_WDT) += bcm2835_wdt.o @@ -86,6 +88,7 @@ obj-$(CONFIG_RENESAS_WDT) += renesas_wdt.o obj-$(CONFIG_RENESAS_RZAWDT) += rza_wdt.o obj-$(CONFIG_RENESAS_RZN1WDT) += rzn1_wdt.o obj-$(CONFIG_RENESAS_RZG2LWDT) += rzg2l_wdt.o +obj-$(CONFIG_RENESAS_RZV2HWDT) += rzv2h_wdt.o obj-$(CONFIG_ASPEED_WATCHDOG) += aspeed_wdt.o obj-$(CONFIG_STM32_WATCHDOG) += stm32_iwdg.o obj-$(CONFIG_UNIPHIER_WATCHDOG) += uniphier_wdt.o @@ -106,6 +109,7 @@ obj-$(CONFIG_ADVANTECH_WDT) += advantechwdt.o obj-$(CONFIG_ADVANTECH_EC_WDT) += advantech_ec_wdt.o obj-$(CONFIG_ALIM1535_WDT) += alim1535_wdt.o obj-$(CONFIG_ALIM7101_WDT) += alim7101_wdt.o +obj-$(CONFIG_CGBC_WDT) += cgbc_wdt.o obj-$(CONFIG_EBC_C384_WDT) += ebc-c384_wdt.o obj-$(CONFIG_EXAR_WDT) += exar_wdt.o obj-$(CONFIG_F71808E_WDT) += f71808e_wdt.o @@ -120,6 +124,8 @@ obj-$(CONFIG_WAFER_WDT) += wafer5823wdt.o obj-$(CONFIG_I6300ESB_WDT) += i6300esb.o obj-$(CONFIG_IE6XX_WDT) += ie6xx_wdt.o obj-$(CONFIG_ITCO_WDT) += iTCO_wdt.o +obj-$(CONFIG_LENOVO_SE10_WDT) += lenovo_se10_wdt.o +obj-$(CONFIG_LENOVO_SE30_WDT) += lenovo_se30_wdt.o ifeq ($(CONFIG_ITCO_VENDOR_SUPPORT),y) obj-$(CONFIG_ITCO_WDT) += iTCO_vendor_support.o endif @@ -135,7 +141,6 @@ obj-$(CONFIG_RDC321X_WDT) += rdc321x_wdt.o obj-$(CONFIG_60XX_WDT) += sbc60xxwdt.o obj-$(CONFIG_SBC8360_WDT) += sbc8360.o obj-$(CONFIG_SBC7240_WDT) += sbc7240_wdt.o -obj-$(CONFIG_CPU5_WDT) += cpu5wdt.o obj-$(CONFIG_SMSC_SCH311X_WDT) += sch311x_wdt.o obj-$(CONFIG_SMSC37B787_WDT) += smsc37b787_wdt.o obj-$(CONFIG_TQMX86_WDT) += tqmx86_wdt.o @@ -146,6 +151,7 @@ obj-$(CONFIG_W83977F_WDT) += w83977f_wdt.o obj-$(CONFIG_MACHZ_WDT) += machzwd.o obj-$(CONFIG_SBC_EPX_C3_WATCHDOG) += sbc_epx_c3.o obj-$(CONFIG_INTEL_MID_WATCHDOG) += intel-mid_wdt.o +obj-$(CONFIG_INTEL_OC_WATCHDOG) += intel_oc_wdt.o obj-$(CONFIG_INTEL_MEI_WDT) += mei_wdt.o obj-$(CONFIG_NI903X_WDT) += ni903x_wdt.o obj-$(CONFIG_NIC7018_WDT) += nic7018_wdt.o @@ -217,6 +223,7 @@ obj-$(CONFIG_XEN_WDT) += xen_wdt.o # Architecture Independent obj-$(CONFIG_BD957XMUF_WATCHDOG) += bd9576_wdt.o +obj-$(CONFIG_BD96801_WATCHDOG) += bd96801_wdt.o obj-$(CONFIG_CROS_EC_WATCHDOG) += cros_ec_wdt.o obj-$(CONFIG_DA9052_WATCHDOG) += da9052_wdt.o obj-$(CONFIG_DA9055_WATCHDOG) += da9055_wdt.o diff --git a/drivers/watchdog/acquirewdt.c b/drivers/watchdog/acquirewdt.c index 53b04abd55b0..052f65c48a70 100644 --- a/drivers/watchdog/acquirewdt.c +++ b/drivers/watchdog/acquirewdt.c @@ -218,7 +218,6 @@ static int acq_close(struct inode *inode, struct file *file) static const struct file_operations acq_fops = { .owner = THIS_MODULE, - .llseek = no_llseek, .write = acq_write, .unlocked_ioctl = acq_ioctl, .compat_ioctl = compat_ptr_ioctl, @@ -286,7 +285,7 @@ static void acq_shutdown(struct platform_device *dev) } static struct platform_driver acquirewdt_driver = { - .remove_new = acq_remove, + .remove = acq_remove, .shutdown = acq_shutdown, .driver = { .name = DRV_NAME, diff --git a/drivers/watchdog/advantechwdt.c b/drivers/watchdog/advantechwdt.c index 7a0acbc3e4dd..42d3f3771781 100644 --- a/drivers/watchdog/advantechwdt.c +++ b/drivers/watchdog/advantechwdt.c @@ -217,7 +217,6 @@ static int advwdt_close(struct inode *inode, struct file *file) static const struct file_operations advwdt_fops = { .owner = THIS_MODULE, - .llseek = no_llseek, .write = advwdt_write, .unlocked_ioctl = advwdt_ioctl, .compat_ioctl = compat_ptr_ioctl, @@ -294,7 +293,7 @@ static void advwdt_shutdown(struct platform_device *dev) } static struct platform_driver advwdt_driver = { - .remove_new = advwdt_remove, + .remove = advwdt_remove, .shutdown = advwdt_shutdown, .driver = { .name = DRV_NAME, diff --git a/drivers/watchdog/airoha_wdt.c b/drivers/watchdog/airoha_wdt.c new file mode 100644 index 000000000000..dc8ca11c14d8 --- /dev/null +++ b/drivers/watchdog/airoha_wdt.c @@ -0,0 +1,216 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Airoha Watchdog Driver + * + * Copyright (c) 2024, AIROHA All rights reserved. + * + * Mayur Kumar <mayur.kumar@airoha.com> + * Christian Marangi <ansuelsmth@gmail.com> + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/types.h> +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/math.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/watchdog.h> + +/* Base address of timer and watchdog registers */ +#define TIMER_CTRL 0x0 +#define WDT_ENABLE BIT(25) +#define WDT_TIMER_INTERRUPT BIT(21) +/* Timer3 is used as Watchdog Timer */ +#define WDT_TIMER_ENABLE BIT(5) +#define WDT_TIMER_LOAD_VALUE 0x2c +#define WDT_TIMER_CUR_VALUE 0x30 +#define WDT_TIMER_VAL GENMASK(31, 0) +#define WDT_RELOAD 0x38 +#define WDT_RLD BIT(0) + +/* Airoha watchdog structure description */ +struct airoha_wdt_desc { + struct watchdog_device wdog_dev; + unsigned int wdt_freq; + void __iomem *base; +}; + +#define WDT_HEARTBEAT 24 +static int heartbeat = WDT_HEARTBEAT; +module_param(heartbeat, int, 0); +MODULE_PARM_DESC(heartbeat, "Watchdog heartbeats in seconds. (default=" + __MODULE_STRING(WDT_HEARTBEAT) ")"); + +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 int airoha_wdt_start(struct watchdog_device *wdog_dev) +{ + struct airoha_wdt_desc *airoha_wdt = watchdog_get_drvdata(wdog_dev); + u32 val; + + val = readl(airoha_wdt->base + TIMER_CTRL); + val |= (WDT_TIMER_ENABLE | WDT_ENABLE | WDT_TIMER_INTERRUPT); + writel(val, airoha_wdt->base + TIMER_CTRL); + val = wdog_dev->timeout * airoha_wdt->wdt_freq; + writel(val, airoha_wdt->base + WDT_TIMER_LOAD_VALUE); + + return 0; +} + +static int airoha_wdt_stop(struct watchdog_device *wdog_dev) +{ + struct airoha_wdt_desc *airoha_wdt = watchdog_get_drvdata(wdog_dev); + u32 val; + + val = readl(airoha_wdt->base + TIMER_CTRL); + val &= (~WDT_ENABLE & ~WDT_TIMER_ENABLE); + writel(val, airoha_wdt->base + TIMER_CTRL); + + return 0; +} + +static int airoha_wdt_ping(struct watchdog_device *wdog_dev) +{ + struct airoha_wdt_desc *airoha_wdt = watchdog_get_drvdata(wdog_dev); + u32 val; + + val = readl(airoha_wdt->base + WDT_RELOAD); + val |= WDT_RLD; + writel(val, airoha_wdt->base + WDT_RELOAD); + + return 0; +} + +static int airoha_wdt_set_timeout(struct watchdog_device *wdog_dev, unsigned int timeout) +{ + wdog_dev->timeout = timeout; + + if (watchdog_active(wdog_dev)) { + airoha_wdt_stop(wdog_dev); + return airoha_wdt_start(wdog_dev); + } + + return 0; +} + +static unsigned int airoha_wdt_get_timeleft(struct watchdog_device *wdog_dev) +{ + struct airoha_wdt_desc *airoha_wdt = watchdog_get_drvdata(wdog_dev); + u32 val; + + val = readl(airoha_wdt->base + WDT_TIMER_CUR_VALUE); + return DIV_ROUND_UP(val, airoha_wdt->wdt_freq); +} + +static const struct watchdog_info airoha_wdt_info = { + .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING, + .identity = "Airoha Watchdog", +}; + +static const struct watchdog_ops airoha_wdt_ops = { + .owner = THIS_MODULE, + .start = airoha_wdt_start, + .stop = airoha_wdt_stop, + .ping = airoha_wdt_ping, + .set_timeout = airoha_wdt_set_timeout, + .get_timeleft = airoha_wdt_get_timeleft, +}; + +static int airoha_wdt_probe(struct platform_device *pdev) +{ + struct airoha_wdt_desc *airoha_wdt; + struct watchdog_device *wdog_dev; + struct device *dev = &pdev->dev; + struct clk *bus_clk; + int ret; + + airoha_wdt = devm_kzalloc(dev, sizeof(*airoha_wdt), GFP_KERNEL); + if (!airoha_wdt) + return -ENOMEM; + + airoha_wdt->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(airoha_wdt->base)) + return PTR_ERR(airoha_wdt->base); + + bus_clk = devm_clk_get_enabled(dev, "bus"); + if (IS_ERR(bus_clk)) + return dev_err_probe(dev, PTR_ERR(bus_clk), + "failed to enable bus clock\n"); + + /* Watchdog ticks at half the bus rate */ + airoha_wdt->wdt_freq = clk_get_rate(bus_clk) / 2; + + /* Initialize struct watchdog device */ + wdog_dev = &airoha_wdt->wdog_dev; + wdog_dev->timeout = heartbeat; + wdog_dev->info = &airoha_wdt_info; + wdog_dev->ops = &airoha_wdt_ops; + /* Bus 300MHz, watchdog 150MHz, 28 seconds */ + wdog_dev->max_timeout = FIELD_MAX(WDT_TIMER_VAL) / airoha_wdt->wdt_freq; + wdog_dev->parent = dev; + + watchdog_set_drvdata(wdog_dev, airoha_wdt); + watchdog_set_nowayout(wdog_dev, nowayout); + watchdog_stop_on_unregister(wdog_dev); + + ret = devm_watchdog_register_device(dev, wdog_dev); + if (ret) + return ret; + + platform_set_drvdata(pdev, airoha_wdt); + return 0; +} + +static int airoha_wdt_suspend(struct device *dev) +{ + struct airoha_wdt_desc *airoha_wdt = dev_get_drvdata(dev); + + if (watchdog_active(&airoha_wdt->wdog_dev)) + airoha_wdt_stop(&airoha_wdt->wdog_dev); + + return 0; +} + +static int airoha_wdt_resume(struct device *dev) +{ + struct airoha_wdt_desc *airoha_wdt = dev_get_drvdata(dev); + + if (watchdog_active(&airoha_wdt->wdog_dev)) { + airoha_wdt_start(&airoha_wdt->wdog_dev); + airoha_wdt_ping(&airoha_wdt->wdog_dev); + } + return 0; +} + +static const struct of_device_id airoha_wdt_of_match[] = { + { .compatible = "airoha,en7581-wdt", }, + { }, +}; + +MODULE_DEVICE_TABLE(of, airoha_wdt_of_match); + +static DEFINE_SIMPLE_DEV_PM_OPS(airoha_wdt_pm_ops, airoha_wdt_suspend, airoha_wdt_resume); + +static struct platform_driver airoha_wdt_driver = { + .probe = airoha_wdt_probe, + .driver = { + .name = "airoha-wdt", + .pm = pm_sleep_ptr(&airoha_wdt_pm_ops), + .of_match_table = airoha_wdt_of_match, + }, +}; + +module_platform_driver(airoha_wdt_driver); + +MODULE_AUTHOR("Mayur Kumar <mayur.kumar@airoha.com>"); +MODULE_AUTHOR("Christian Marangi <ansuelsmth@gmail.com>"); +MODULE_DESCRIPTION("Airoha EN7581 Watchdog Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/watchdog/alim1535_wdt.c b/drivers/watchdog/alim1535_wdt.c index bfb9a91ca1df..1ecbd1ac5c3a 100644 --- a/drivers/watchdog/alim1535_wdt.c +++ b/drivers/watchdog/alim1535_wdt.c @@ -359,7 +359,6 @@ static int __init ali_find_watchdog(void) static const struct file_operations ali_fops = { .owner = THIS_MODULE, - .llseek = no_llseek, .write = ali_write, .unlocked_ioctl = ali_ioctl, .compat_ioctl = compat_ptr_ioctl, diff --git a/drivers/watchdog/alim7101_wdt.c b/drivers/watchdog/alim7101_wdt.c index 4ff7f5afb7aa..03a559b41f5b 100644 --- a/drivers/watchdog/alim7101_wdt.c +++ b/drivers/watchdog/alim7101_wdt.c @@ -166,7 +166,7 @@ static void wdt_startup(void) static void wdt_turnoff(void) { /* Stop the timer */ - del_timer_sync(&timer); + timer_delete_sync(&timer); wdt_change(WDT_DISABLE); pr_info("Watchdog timer is now disabled...\n"); } @@ -223,7 +223,7 @@ static int fop_close(struct inode *inode, struct file *file) if (wdt_expect_close == 42) wdt_turnoff(); else { - /* wim: shouldn't there be a: del_timer(&timer); */ + /* wim: shouldn't there be a: timer_delete(&timer); */ pr_crit("device file closed unexpectedly. Will not stop the WDT!\n"); } clear_bit(0, &wdt_is_open); @@ -289,7 +289,6 @@ static long fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg) static const struct file_operations wdt_fops = { .owner = THIS_MODULE, - .llseek = no_llseek, .write = fop_write, .open = fop_open, .release = fop_close, diff --git a/drivers/watchdog/apple_wdt.c b/drivers/watchdog/apple_wdt.c index d4f739932f0b..66a158f67a71 100644 --- a/drivers/watchdog/apple_wdt.c +++ b/drivers/watchdog/apple_wdt.c @@ -95,9 +95,12 @@ static int apple_wdt_ping(struct watchdog_device *wdd) static int apple_wdt_set_timeout(struct watchdog_device *wdd, unsigned int s) { struct apple_wdt *wdt = to_apple_wdt(wdd); + u32 actual; writel_relaxed(0, wdt->regs + APPLE_WDT_WD1_CUR_TIME); - writel_relaxed(wdt->clk_rate * s, wdt->regs + APPLE_WDT_WD1_BITE_TIME); + + actual = min(s, wdd->max_hw_heartbeat_ms / 1000); + writel_relaxed(wdt->clk_rate * actual, wdt->regs + APPLE_WDT_WD1_BITE_TIME); wdd->timeout = s; @@ -127,11 +130,11 @@ static int apple_wdt_restart(struct watchdog_device *wdd, unsigned long mode, /* * Flush writes and then wait for the SoC to reset. Even though the * reset is queued almost immediately experiments have shown that it - * can take up to ~20-25ms until the SoC is actually reset. Just wait - * 50ms here to be safe. + * can take up to ~120-125ms until the SoC is actually reset. Just + * wait 150ms here to be safe. */ - (void)readl_relaxed(wdt->regs + APPLE_WDT_WD1_CUR_TIME); - mdelay(50); + (void)readl(wdt->regs + APPLE_WDT_WD1_CUR_TIME); + mdelay(150); return 0; } @@ -177,7 +180,7 @@ static int apple_wdt_probe(struct platform_device *pdev) wdt->wdd.ops = &apple_wdt_ops; wdt->wdd.info = &apple_wdt_info; - wdt->wdd.max_timeout = U32_MAX / wdt->clk_rate; + wdt->wdd.max_hw_heartbeat_ms = U32_MAX / wdt->clk_rate * 1000; wdt->wdd.timeout = APPLE_WDT_TIMEOUT_DEFAULT; wdt_ctrl = readl_relaxed(wdt->regs + APPLE_WDT_WD1_CTRL); diff --git a/drivers/watchdog/arm_smc_wdt.c b/drivers/watchdog/arm_smc_wdt.c index 8f3d0c3a005f..bbba23ace7b8 100644 --- a/drivers/watchdog/arm_smc_wdt.c +++ b/drivers/watchdog/arm_smc_wdt.c @@ -46,6 +46,8 @@ static int smcwd_call(struct watchdog_device *wdd, enum smcwd_call call, return -ENODEV; if (res->a0 == PSCI_RET_INVALID_PARAMS) return -EINVAL; + if (res->a0 == PSCI_RET_DISABLED) + return -ENODATA; if (res->a0 != PSCI_RET_SUCCESS) return -EIO; return 0; @@ -131,10 +133,19 @@ static int smcwd_probe(struct platform_device *pdev) wdd->info = &smcwd_info; /* get_timeleft is optional */ - if (smcwd_call(wdd, SMCWD_GET_TIMELEFT, 0, NULL)) - wdd->ops = &smcwd_ops; - else + err = smcwd_call(wdd, SMCWD_GET_TIMELEFT, 0, NULL); + switch (err) { + case 0: + set_bit(WDOG_HW_RUNNING, &wdd->status); + fallthrough; + case -ENODATA: wdd->ops = &smcwd_timeleft_ops; + break; + default: + wdd->ops = &smcwd_ops; + break; + } + wdd->timeout = res.a2; wdd->max_timeout = res.a2; wdd->min_timeout = res.a1; diff --git a/drivers/watchdog/armada_37xx_wdt.c b/drivers/watchdog/armada_37xx_wdt.c index 8133a5d05647..a17a7911771a 100644 --- a/drivers/watchdog/armada_37xx_wdt.c +++ b/drivers/watchdog/armada_37xx_wdt.c @@ -248,7 +248,6 @@ static const struct watchdog_ops armada_37xx_wdt_ops = { static int armada_37xx_wdt_probe(struct platform_device *pdev) { struct armada_37xx_watchdog *dev; - struct resource *res; struct regmap *regmap; int ret; @@ -266,12 +265,9 @@ static int armada_37xx_wdt_probe(struct platform_device *pdev) return PTR_ERR(regmap); dev->cpu_misc = regmap; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -ENODEV; - dev->reg = devm_ioremap(&pdev->dev, res->start, resource_size(res)); - if (!dev->reg) - return -ENOMEM; + dev->reg = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(dev->reg)) + return PTR_ERR(dev->reg); /* init clock */ dev->clk = devm_clk_get_enabled(&pdev->dev, NULL); diff --git a/drivers/watchdog/aspeed_wdt.c b/drivers/watchdog/aspeed_wdt.c index b4773a6aaf8c..837e15701c0e 100644 --- a/drivers/watchdog/aspeed_wdt.c +++ b/drivers/watchdog/aspeed_wdt.c @@ -11,21 +11,30 @@ #include <linux/io.h> #include <linux/kernel.h> #include <linux/kstrtox.h> +#include <linux/mfd/syscon.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_irq.h> #include <linux/platform_device.h> +#include <linux/regmap.h> #include <linux/watchdog.h> 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 aspeed_wdt_scu { + const char *compatible; + u32 reset_status_reg; + u32 wdt_reset_mask; + u32 wdt_reset_mask_shift; +}; struct aspeed_wdt_config { u32 ext_pulse_width_mask; u32 irq_shift; u32 irq_mask; + struct aspeed_wdt_scu scu; }; struct aspeed_wdt { @@ -39,18 +48,36 @@ static const struct aspeed_wdt_config ast2400_config = { .ext_pulse_width_mask = 0xff, .irq_shift = 0, .irq_mask = 0, + .scu = { + .compatible = "aspeed,ast2400-scu", + .reset_status_reg = 0x3c, + .wdt_reset_mask = 0x1, + .wdt_reset_mask_shift = 1, + }, }; static const struct aspeed_wdt_config ast2500_config = { .ext_pulse_width_mask = 0xfffff, .irq_shift = 12, .irq_mask = GENMASK(31, 12), + .scu = { + .compatible = "aspeed,ast2500-scu", + .reset_status_reg = 0x3c, + .wdt_reset_mask = 0x1, + .wdt_reset_mask_shift = 2, + }, }; static const struct aspeed_wdt_config ast2600_config = { .ext_pulse_width_mask = 0xfffff, .irq_shift = 0, .irq_mask = GENMASK(31, 10), + .scu = { + .compatible = "aspeed,ast2600-scu", + .reset_status_reg = 0x74, + .wdt_reset_mask = 0xf, + .wdt_reset_mask_shift = 16, + }, }; static const struct of_device_id aspeed_wdt_of_table[] = { @@ -213,6 +240,56 @@ static int aspeed_wdt_restart(struct watchdog_device *wdd, return 0; } +static void aspeed_wdt_update_bootstatus(struct platform_device *pdev, + struct aspeed_wdt *wdt) +{ + const struct resource *res; + struct aspeed_wdt_scu scu = wdt->cfg->scu; + struct regmap *scu_base; + u32 reset_mask_width; + u32 reset_mask_shift; + u32 idx = 0; + u32 status; + int ret; + + if (!of_device_is_compatible(pdev->dev.of_node, "aspeed,ast2400-wdt")) { + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + idx = ((intptr_t)wdt->base & 0x00000fff) / (uintptr_t)resource_size(res); + } + + scu_base = syscon_regmap_lookup_by_compatible(scu.compatible); + if (IS_ERR(scu_base)) { + wdt->wdd.bootstatus = WDIOS_UNKNOWN; + return; + } + + ret = regmap_read(scu_base, scu.reset_status_reg, &status); + if (ret) { + wdt->wdd.bootstatus = WDIOS_UNKNOWN; + return; + } + + reset_mask_width = hweight32(scu.wdt_reset_mask); + reset_mask_shift = scu.wdt_reset_mask_shift + + reset_mask_width * idx; + + if (status & (scu.wdt_reset_mask << reset_mask_shift)) + wdt->wdd.bootstatus = WDIOF_CARDRESET; + + /* clear wdt reset event flag */ + if (of_device_is_compatible(pdev->dev.of_node, "aspeed,ast2400-wdt") || + of_device_is_compatible(pdev->dev.of_node, "aspeed,ast2500-wdt")) { + ret = regmap_read(scu_base, scu.reset_status_reg, &status); + if (!ret) { + status &= ~(scu.wdt_reset_mask << reset_mask_shift); + regmap_write(scu_base, scu.reset_status_reg, status); + } + } else { + regmap_write(scu_base, scu.reset_status_reg, + scu.wdt_reset_mask << reset_mask_shift); + } +} + /* access_cs0 shows if cs0 is accessible, hence the reverted bit */ static ssize_t access_cs0_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -458,10 +535,10 @@ static int aspeed_wdt_probe(struct platform_device *pdev) writel(duration - 1, wdt->base + WDT_RESET_WIDTH); } + aspeed_wdt_update_bootstatus(pdev, wdt); + status = readl(wdt->base + WDT_TIMEOUT_STATUS); if (status & WDT_TIMEOUT_STATUS_BOOT_SECONDARY) { - wdt->wdd.bootstatus = WDIOF_CARDRESET; - if (of_device_is_compatible(np, "aspeed,ast2400-wdt") || of_device_is_compatible(np, "aspeed,ast2500-wdt")) wdt->wdd.groups = bswitch_groups; diff --git a/drivers/watchdog/at91rm9200_wdt.c b/drivers/watchdog/at91rm9200_wdt.c index 558015f08c7a..1795aaf1ec45 100644 --- a/drivers/watchdog/at91rm9200_wdt.c +++ b/drivers/watchdog/at91rm9200_wdt.c @@ -210,7 +210,6 @@ static ssize_t at91_wdt_write(struct file *file, const char *data, static const struct file_operations at91wdt_fops = { .owner = THIS_MODULE, - .llseek = no_llseek, .unlocked_ioctl = at91_wdt_ioctl, .compat_ioctl = compat_ptr_ioctl, .open = at91_wdt_open, @@ -296,7 +295,7 @@ MODULE_DEVICE_TABLE(of, at91_wdt_dt_ids); static struct platform_driver at91wdt_driver = { .probe = at91wdt_probe, - .remove_new = at91wdt_remove, + .remove = at91wdt_remove, .shutdown = at91wdt_shutdown, .suspend = pm_ptr(at91wdt_suspend), .resume = pm_ptr(at91wdt_resume), diff --git a/drivers/watchdog/at91sam9_wdt.c b/drivers/watchdog/at91sam9_wdt.c index 2c6474cb858b..1b47a2fc7d17 100644 --- a/drivers/watchdog/at91sam9_wdt.c +++ b/drivers/watchdog/at91sam9_wdt.c @@ -242,7 +242,7 @@ static int at91_wdt_init(struct platform_device *pdev, struct at91wdt *wdt) return 0; out_stop_timer: - del_timer(&wdt->timer); + timer_delete(&wdt->timer); return err; } @@ -378,7 +378,7 @@ static void at91wdt_remove(struct platform_device *pdev) watchdog_unregister_device(&wdt->wdd); pr_warn("I quit now, hardware will probably reboot!\n"); - del_timer(&wdt->timer); + timer_delete(&wdt->timer); } #if defined(CONFIG_OF) @@ -392,7 +392,7 @@ MODULE_DEVICE_TABLE(of, at91_wdt_dt_ids); static struct platform_driver at91wdt_driver = { .probe = at91wdt_probe, - .remove_new = at91wdt_remove, + .remove = at91wdt_remove, .driver = { .name = "at91_wdt", .of_match_table = of_match_ptr(at91_wdt_dt_ids), diff --git a/drivers/watchdog/ath79_wdt.c b/drivers/watchdog/ath79_wdt.c index e5cc30622b12..7df703e9852a 100644 --- a/drivers/watchdog/ath79_wdt.c +++ b/drivers/watchdog/ath79_wdt.c @@ -231,7 +231,6 @@ static long ath79_wdt_ioctl(struct file *file, unsigned int cmd, static const struct file_operations ath79_wdt_fops = { .owner = THIS_MODULE, - .llseek = no_llseek, .write = ath79_wdt_write, .unlocked_ioctl = ath79_wdt_ioctl, .compat_ioctl = compat_ptr_ioctl, @@ -306,7 +305,7 @@ MODULE_DEVICE_TABLE(of, ath79_wdt_match); static struct platform_driver ath79_wdt_driver = { .probe = ath79_wdt_probe, - .remove_new = ath79_wdt_remove, + .remove = ath79_wdt_remove, .shutdown = ath79_wdt_shutdown, .driver = { .name = DRIVER_NAME, diff --git a/drivers/watchdog/bcm2835_wdt.c b/drivers/watchdog/bcm2835_wdt.c index bb001c5d7f17..9fcfee63905b 100644 --- a/drivers/watchdog/bcm2835_wdt.c +++ b/drivers/watchdog/bcm2835_wdt.c @@ -227,7 +227,7 @@ static void bcm2835_wdt_remove(struct platform_device *pdev) static struct platform_driver bcm2835_wdt_driver = { .probe = bcm2835_wdt_probe, - .remove_new = bcm2835_wdt_remove, + .remove = bcm2835_wdt_remove, .driver = { .name = "bcm2835-wdt", }, diff --git a/drivers/watchdog/bcm47xx_wdt.c b/drivers/watchdog/bcm47xx_wdt.c index 06a54c7de40b..4c0951307421 100644 --- a/drivers/watchdog/bcm47xx_wdt.c +++ b/drivers/watchdog/bcm47xx_wdt.c @@ -139,7 +139,7 @@ static int bcm47xx_wdt_soft_stop(struct watchdog_device *wdd) { struct bcm47xx_wdt *wdt = bcm47xx_wdt_get(wdd); - del_timer_sync(&wdt->soft_timer); + timer_delete_sync(&wdt->soft_timer); wdt->timer_set(wdt, 0); return 0; @@ -213,7 +213,7 @@ static int bcm47xx_wdt_probe(struct platform_device *pdev) err_timer: if (soft) - del_timer_sync(&wdt->soft_timer); + timer_delete_sync(&wdt->soft_timer); return ret; } diff --git a/drivers/watchdog/bcm_kona_wdt.c b/drivers/watchdog/bcm_kona_wdt.c index 49e12d47b073..66bd0324fd68 100644 --- a/drivers/watchdog/bcm_kona_wdt.c +++ b/drivers/watchdog/bcm_kona_wdt.c @@ -328,7 +328,7 @@ static struct platform_driver bcm_kona_wdt_driver = { .of_match_table = bcm_kona_wdt_of_match, }, .probe = bcm_kona_wdt_probe, - .remove_new = bcm_kona_wdt_remove, + .remove = bcm_kona_wdt_remove, }; module_platform_driver(bcm_kona_wdt_driver); diff --git a/drivers/watchdog/bd9576_wdt.c b/drivers/watchdog/bd9576_wdt.c index 4a20e07fbb69..f00ea1b4e40b 100644 --- a/drivers/watchdog/bd9576_wdt.c +++ b/drivers/watchdog/bd9576_wdt.c @@ -29,7 +29,6 @@ struct bd9576_wdt_priv { struct gpio_desc *gpiod_en; struct device *dev; struct regmap *regmap; - bool always_running; struct watchdog_device wdd; }; @@ -62,10 +61,7 @@ static int bd9576_wdt_stop(struct watchdog_device *wdd) { struct bd9576_wdt_priv *priv = watchdog_get_drvdata(wdd); - if (!priv->always_running) - bd9576_wdt_disable(priv); - else - set_bit(WDOG_HW_RUNNING, &wdd->status); + bd9576_wdt_disable(priv); return 0; } @@ -264,9 +260,6 @@ static int bd9576_wdt_probe(struct platform_device *pdev) if (ret) return ret; - priv->always_running = device_property_read_bool(dev->parent, - "always-running"); - watchdog_set_drvdata(&priv->wdd, priv); priv->wdd.info = &bd957x_wdt_ident; @@ -281,9 +274,6 @@ static int bd9576_wdt_probe(struct platform_device *pdev) watchdog_stop_on_reboot(&priv->wdd); - if (priv->always_running) - bd9576_wdt_start(&priv->wdd); - return devm_watchdog_register_device(dev, &priv->wdd); } diff --git a/drivers/watchdog/bd96801_wdt.c b/drivers/watchdog/bd96801_wdt.c new file mode 100644 index 000000000000..12b74fd2bc05 --- /dev/null +++ b/drivers/watchdog/bd96801_wdt.c @@ -0,0 +1,417 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2024 ROHM Semiconductors + * + * ROHM BD96801 watchdog driver + */ + +#include <linux/bitfield.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/mfd/rohm-bd96801.h> +#include <linux/mfd/rohm-generic.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/reboot.h> +#include <linux/regmap.h> +#include <linux/watchdog.h> + +static bool nowayout; +module_param(nowayout, bool, 0); +MODULE_PARM_DESC(nowayout, + "Watchdog cannot be stopped once started (default=\"false\")"); + +#define BD96801_WD_TMO_SHORT_MASK 0x70 +#define BD96801_WD_RATIO_MASK 0x3 +#define BD96801_WD_TYPE_MASK 0x4 +#define BD96801_WD_TYPE_SLOW 0x4 +#define BD96801_WD_TYPE_WIN 0x0 + +#define BD96801_WD_EN_MASK 0x3 +#define BD96801_WD_IF_EN 0x1 +#define BD96801_WD_QA_EN 0x2 +#define BD96801_WD_DISABLE 0x0 + +#define BD96801_WD_ASSERT_MASK 0x8 +#define BD96801_WD_ASSERT_RST 0x8 +#define BD96801_WD_ASSERT_IRQ 0x0 + +#define BD96801_WD_FEED_MASK 0x1 +#define BD96801_WD_FEED 0x1 + +/* 1.1 mS */ +#define FASTNG_MIN 11 +#define FASTNG_MAX_US (100 * FASTNG_MIN << 7) +#define SLOWNG_MAX_US (16 * FASTNG_MAX_US) + +#define BD96801_WDT_DEFAULT_MARGIN_MS 1843 +/* Unit is seconds */ +#define DEFAULT_TIMEOUT 30 + +/* + * BD96801 WDG supports window mode so the TMO consists of SHORT and LONG + * timeout values. SHORT time is meaningful only in window mode where feeding + * period shorter than SHORT would be an error. LONG time is used to detect if + * feeding is not occurring within given time limit (SoC SW hangs). The LONG + * timeout time is a multiple of (2, 4, 8 or 16 times) the SHORT timeout. + */ + +struct wdtbd96801 { + struct device *dev; + struct regmap *regmap; + struct watchdog_device wdt; +}; + +static int bd96801_wdt_ping(struct watchdog_device *wdt) +{ + struct wdtbd96801 *w = watchdog_get_drvdata(wdt); + + return regmap_update_bits(w->regmap, BD96801_REG_WD_FEED, + BD96801_WD_FEED_MASK, BD96801_WD_FEED); +} + +static int bd96801_wdt_start(struct watchdog_device *wdt) +{ + struct wdtbd96801 *w = watchdog_get_drvdata(wdt); + + return regmap_update_bits(w->regmap, BD96801_REG_WD_CONF, + BD96801_WD_EN_MASK, BD96801_WD_IF_EN); +} + +static int bd96801_wdt_stop(struct watchdog_device *wdt) +{ + struct wdtbd96801 *w = watchdog_get_drvdata(wdt); + + return regmap_update_bits(w->regmap, BD96801_REG_WD_CONF, + BD96801_WD_EN_MASK, BD96801_WD_DISABLE); +} + +static const struct watchdog_info bd96801_wdt_info = { + .options = WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING | + WDIOF_SETTIMEOUT, + .identity = "BD96801 Watchdog", +}; + +static const struct watchdog_ops bd96801_wdt_ops = { + .start = bd96801_wdt_start, + .stop = bd96801_wdt_stop, + .ping = bd96801_wdt_ping, +}; + +static int find_closest_fast(unsigned int target, int *sel, unsigned int *val) +{ + unsigned int window = FASTNG_MIN; + int i; + + for (i = 0; i < 8 && window < target; i++) + window <<= 1; + + if (i == 8) + return -EINVAL; + + *val = window; + *sel = i; + + return 0; +} + +static int find_closest_slow_by_fast(unsigned int fast_val, unsigned int *target, + int *slowsel) +{ + static const int multipliers[] = {2, 4, 8, 16}; + int sel; + + for (sel = 0; sel < ARRAY_SIZE(multipliers) && + multipliers[sel] * fast_val < *target; sel++) + ; + + if (sel == ARRAY_SIZE(multipliers)) + return -EINVAL; + + *slowsel = sel; + *target = multipliers[sel] * fast_val; + + return 0; +} + +static int find_closest_slow(unsigned int *target, int *slow_sel, int *fast_sel) +{ + static const int multipliers[] = {2, 4, 8, 16}; + unsigned int window = FASTNG_MIN; + unsigned int val = 0; + int i, j; + + for (i = 0; i < 8; i++) { + for (j = 0; j < ARRAY_SIZE(multipliers); j++) { + unsigned int slow; + + slow = window * multipliers[j]; + if (slow >= *target && (!val || slow < val)) { + val = slow; + *fast_sel = i; + *slow_sel = j; + } + } + window <<= 1; + } + if (!val) + return -EINVAL; + + *target = val; + + return 0; +} + +static int bd96801_set_wdt_mode(struct wdtbd96801 *w, unsigned int hw_margin, + unsigned int hw_margin_min) +{ + int fastng, slowng, type, ret, reg, mask; + struct device *dev = w->dev; + + + if (hw_margin_min * 1000 > FASTNG_MAX_US) { + dev_err(dev, "Unsupported fast timeout %u uS [max %u]\n", + hw_margin_min * 1000, FASTNG_MAX_US); + + return -EINVAL; + } + + if (hw_margin * 1000 > SLOWNG_MAX_US) { + dev_err(dev, "Unsupported slow timeout %u uS [max %u]\n", + hw_margin * 1000, SLOWNG_MAX_US); + + return -EINVAL; + } + + /* + * Convert to 100uS to guarantee reasonable timeouts fit in + * 32bit maintaining also a decent accuracy. + */ + hw_margin *= 10; + hw_margin_min *= 10; + + if (hw_margin_min) { + unsigned int min; + + type = BD96801_WD_TYPE_WIN; + dev_dbg(dev, "Setting type WINDOW 0x%x\n", type); + ret = find_closest_fast(hw_margin_min, &fastng, &min); + if (ret) + return ret; + + ret = find_closest_slow_by_fast(min, &hw_margin, &slowng); + if (ret) { + dev_err(dev, + "can't support slow timeout %u uS using fast %u uS. [max slow %u uS]\n", + hw_margin * 100, min * 100, min * 100 * 16); + + return ret; + } + w->wdt.min_hw_heartbeat_ms = min / 10; + } else { + type = BD96801_WD_TYPE_SLOW; + dev_dbg(dev, "Setting type SLOW 0x%x\n", type); + ret = find_closest_slow(&hw_margin, &slowng, &fastng); + if (ret) + return ret; + } + + w->wdt.max_hw_heartbeat_ms = hw_margin / 10; + + fastng = FIELD_PREP(BD96801_WD_TMO_SHORT_MASK, fastng); + + reg = slowng | fastng; + mask = BD96801_WD_RATIO_MASK | BD96801_WD_TMO_SHORT_MASK; + ret = regmap_update_bits(w->regmap, BD96801_REG_WD_TMO, + mask, reg); + if (ret) + return ret; + + ret = regmap_update_bits(w->regmap, BD96801_REG_WD_CONF, + BD96801_WD_TYPE_MASK, type); + + return ret; +} + +static int bd96801_set_heartbeat_from_hw(struct wdtbd96801 *w, + unsigned int conf_reg) +{ + int ret; + unsigned int val, sel, fast; + + /* + * The BD96801 supports a somewhat peculiar QA-mode, which we do not + * support in this driver. If the QA-mode is enabled then we just + * warn and bail-out. + */ + if ((conf_reg & BD96801_WD_EN_MASK) != BD96801_WD_IF_EN) { + dev_err(w->dev, "watchdog set to Q&A mode - exiting\n"); + return -EINVAL; + } + + ret = regmap_read(w->regmap, BD96801_REG_WD_TMO, &val); + if (ret) + return ret; + + sel = FIELD_GET(BD96801_WD_TMO_SHORT_MASK, val); + fast = FASTNG_MIN << sel; + + sel = (val & BD96801_WD_RATIO_MASK) + 1; + w->wdt.max_hw_heartbeat_ms = (fast << sel) / USEC_PER_MSEC; + + if ((conf_reg & BD96801_WD_TYPE_MASK) == BD96801_WD_TYPE_WIN) + w->wdt.min_hw_heartbeat_ms = fast / USEC_PER_MSEC; + + return 0; +} + +static int init_wdg_hw(struct wdtbd96801 *w) +{ + u32 hw_margin[2]; + int count, ret; + u32 hw_margin_max = BD96801_WDT_DEFAULT_MARGIN_MS, hw_margin_min = 0; + + count = device_property_count_u32(w->dev->parent, "rohm,hw-timeout-ms"); + if (count < 0 && count != -EINVAL) + return count; + + if (count > 0) { + if (count > ARRAY_SIZE(hw_margin)) + return -EINVAL; + + ret = device_property_read_u32_array(w->dev->parent, + "rohm,hw-timeout-ms", + &hw_margin[0], count); + if (ret < 0) + return ret; + + if (count == 1) + hw_margin_max = hw_margin[0]; + + if (count == 2) { + if (hw_margin[1] > hw_margin[0]) { + hw_margin_max = hw_margin[1]; + hw_margin_min = hw_margin[0]; + } else { + hw_margin_max = hw_margin[0]; + hw_margin_min = hw_margin[1]; + } + } + } + + ret = bd96801_set_wdt_mode(w, hw_margin_max, hw_margin_min); + if (ret) + return ret; + + ret = device_property_match_string(w->dev->parent, "rohm,wdg-action", + "prstb"); + if (ret >= 0) { + ret = regmap_update_bits(w->regmap, BD96801_REG_WD_CONF, + BD96801_WD_ASSERT_MASK, + BD96801_WD_ASSERT_RST); + return ret; + } + + ret = device_property_match_string(w->dev->parent, "rohm,wdg-action", + "intb-only"); + if (ret >= 0) { + ret = regmap_update_bits(w->regmap, BD96801_REG_WD_CONF, + BD96801_WD_ASSERT_MASK, + BD96801_WD_ASSERT_IRQ); + return ret; + } + + return 0; +} + +static irqreturn_t bd96801_irq_hnd(int irq, void *data) +{ + emergency_restart(); + + return IRQ_NONE; +} + +static int bd96801_wdt_probe(struct platform_device *pdev) +{ + struct wdtbd96801 *w; + int ret, irq; + unsigned int val; + + w = devm_kzalloc(&pdev->dev, sizeof(*w), GFP_KERNEL); + if (!w) + return -ENOMEM; + + w->regmap = dev_get_regmap(pdev->dev.parent, NULL); + w->dev = &pdev->dev; + + w->wdt.info = &bd96801_wdt_info; + w->wdt.ops = &bd96801_wdt_ops; + w->wdt.parent = pdev->dev.parent; + w->wdt.timeout = DEFAULT_TIMEOUT; + watchdog_set_drvdata(&w->wdt, w); + + ret = regmap_read(w->regmap, BD96801_REG_WD_CONF, &val); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "Failed to get the watchdog state\n"); + + /* + * If the WDG is already enabled we assume it is configured by boot. + * In this case we just update the hw-timeout based on values set to + * the timeout / mode registers and leave the hardware configs + * untouched. + */ + if ((val & BD96801_WD_EN_MASK) != BD96801_WD_DISABLE) { + dev_dbg(&pdev->dev, "watchdog was running during probe\n"); + ret = bd96801_set_heartbeat_from_hw(w, val); + if (ret) + return ret; + + set_bit(WDOG_HW_RUNNING, &w->wdt.status); + } else { + /* If WDG is not running so we will initializate it */ + ret = init_wdg_hw(w); + if (ret) + return ret; + } + + dev_dbg(w->dev, "heartbeat set to %u - %u\n", + w->wdt.min_hw_heartbeat_ms, w->wdt.max_hw_heartbeat_ms); + + watchdog_init_timeout(&w->wdt, 0, pdev->dev.parent); + watchdog_set_nowayout(&w->wdt, nowayout); + watchdog_stop_on_reboot(&w->wdt); + + irq = platform_get_irq_byname(pdev, "bd96801-wdg"); + if (irq > 0) { + ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, + bd96801_irq_hnd, + IRQF_ONESHOT, "bd96801-wdg", + NULL); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "Failed to register IRQ\n"); + } + + return devm_watchdog_register_device(&pdev->dev, &w->wdt); +} + +static const struct platform_device_id bd96801_wdt_id[] = { + { "bd96801-wdt", }, + { } +}; +MODULE_DEVICE_TABLE(platform, bd96801_wdt_id); + +static struct platform_driver bd96801_wdt = { + .driver = { + .name = "bd96801-wdt" + }, + .probe = bd96801_wdt_probe, + .id_table = bd96801_wdt_id, +}; +module_platform_driver(bd96801_wdt); + +MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>"); +MODULE_DESCRIPTION("BD96801 watchdog driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/watchdog/cgbc_wdt.c b/drivers/watchdog/cgbc_wdt.c new file mode 100644 index 000000000000..702b055ba6f4 --- /dev/null +++ b/drivers/watchdog/cgbc_wdt.c @@ -0,0 +1,211 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Congatec Board Controller watchdog driver + * + * Copyright (C) 2024 Bootlin + * Author: Thomas Richard <thomas.richard@bootlin.com> + */ + +#include <linux/build_bug.h> +#include <linux/device.h> +#include <linux/limits.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/watchdog.h> + +#include <linux/mfd/cgbc.h> + +#define CGBC_WDT_CMD_TRIGGER 0x27 +#define CGBC_WDT_CMD_INIT 0x28 +#define CGBC_WDT_DISABLE 0x00 + +#define CGBC_WDT_MODE_SINGLE_EVENT 0x02 + +#define CGBC_WDT_MIN_TIMEOUT 1 +#define CGBC_WDT_MAX_TIMEOUT ((U32_MAX >> 8) / 1000) + +#define CGBC_WDT_DEFAULT_TIMEOUT 30 +#define CGBC_WDT_DEFAULT_PRETIMEOUT 0 + +enum action { + ACTION_INT = 0, + ACTION_SMI, + ACTION_RESET, + ACTION_BUTTON, +}; + +static unsigned int timeout; +module_param(timeout, uint, 0); +MODULE_PARM_DESC(timeout, + "Watchdog timeout in seconds. (>=0, default=" + __MODULE_STRING(CGBC_WDT_DEFAULT_TIMEOUT) ")"); + +static unsigned int pretimeout = CGBC_WDT_DEFAULT_PRETIMEOUT; +module_param(pretimeout, uint, 0); +MODULE_PARM_DESC(pretimeout, + "Watchdog pretimeout in seconds. (>=0, default=" + __MODULE_STRING(CGBC_WDT_DEFAULT_PRETIMEOUT) ")"); + +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 cgbc_wdt_data { + struct cgbc_device_data *cgbc; + struct watchdog_device wdd; +}; + +struct cgbc_wdt_cmd_cfg { + u8 cmd; + u8 mode; + u8 action; + u8 timeout1[3]; + u8 timeout2[3]; + u8 reserved[3]; + u8 delay[3]; +} __packed; + +static_assert(sizeof(struct cgbc_wdt_cmd_cfg) == 15); + +static int cgbc_wdt_start(struct watchdog_device *wdd) +{ + struct cgbc_wdt_data *wdt_data = watchdog_get_drvdata(wdd); + struct cgbc_device_data *cgbc = wdt_data->cgbc; + unsigned int timeout1 = (wdd->timeout - wdd->pretimeout) * 1000; + unsigned int timeout2 = wdd->pretimeout * 1000; + u8 action; + + struct cgbc_wdt_cmd_cfg cmd_start = { + .cmd = CGBC_WDT_CMD_INIT, + .mode = CGBC_WDT_MODE_SINGLE_EVENT, + .timeout1[0] = (u8)timeout1, + .timeout1[1] = (u8)(timeout1 >> 8), + .timeout1[2] = (u8)(timeout1 >> 16), + .timeout2[0] = (u8)timeout2, + .timeout2[1] = (u8)(timeout2 >> 8), + .timeout2[2] = (u8)(timeout2 >> 16), + }; + + if (wdd->pretimeout) { + action = 2; + action |= ACTION_SMI << 2; + action |= ACTION_RESET << 4; + } else { + action = 1; + action |= ACTION_RESET << 2; + } + + cmd_start.action = action; + + return cgbc_command(cgbc, &cmd_start, sizeof(cmd_start), NULL, 0, NULL); +} + +static int cgbc_wdt_stop(struct watchdog_device *wdd) +{ + struct cgbc_wdt_data *wdt_data = watchdog_get_drvdata(wdd); + struct cgbc_device_data *cgbc = wdt_data->cgbc; + struct cgbc_wdt_cmd_cfg cmd_stop = { + .cmd = CGBC_WDT_CMD_INIT, + .mode = CGBC_WDT_DISABLE, + }; + + return cgbc_command(cgbc, &cmd_stop, sizeof(cmd_stop), NULL, 0, NULL); +} + +static int cgbc_wdt_keepalive(struct watchdog_device *wdd) +{ + struct cgbc_wdt_data *wdt_data = watchdog_get_drvdata(wdd); + struct cgbc_device_data *cgbc = wdt_data->cgbc; + u8 cmd_ping = CGBC_WDT_CMD_TRIGGER; + + return cgbc_command(cgbc, &cmd_ping, sizeof(cmd_ping), NULL, 0, NULL); +} + +static int cgbc_wdt_set_pretimeout(struct watchdog_device *wdd, + unsigned int pretimeout) +{ + wdd->pretimeout = pretimeout; + + if (watchdog_active(wdd)) + return cgbc_wdt_start(wdd); + + return 0; +} + +static int cgbc_wdt_set_timeout(struct watchdog_device *wdd, + unsigned int timeout) +{ + if (timeout < wdd->pretimeout) + wdd->pretimeout = 0; + + wdd->timeout = timeout; + + if (watchdog_active(wdd)) + return cgbc_wdt_start(wdd); + + return 0; +} + +static const struct watchdog_info cgbc_wdt_info = { + .identity = "CGBC Watchdog", + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | + WDIOF_MAGICCLOSE | WDIOF_PRETIMEOUT +}; + +static const struct watchdog_ops cgbc_wdt_ops = { + .owner = THIS_MODULE, + .start = cgbc_wdt_start, + .stop = cgbc_wdt_stop, + .ping = cgbc_wdt_keepalive, + .set_timeout = cgbc_wdt_set_timeout, + .set_pretimeout = cgbc_wdt_set_pretimeout, +}; + +static int cgbc_wdt_probe(struct platform_device *pdev) +{ + struct cgbc_device_data *cgbc = dev_get_drvdata(pdev->dev.parent); + struct device *dev = &pdev->dev; + struct cgbc_wdt_data *wdt_data; + struct watchdog_device *wdd; + + wdt_data = devm_kzalloc(dev, sizeof(*wdt_data), GFP_KERNEL); + if (!wdt_data) + return -ENOMEM; + + wdt_data->cgbc = cgbc; + wdd = &wdt_data->wdd; + wdd->parent = dev; + + wdd->info = &cgbc_wdt_info; + wdd->ops = &cgbc_wdt_ops; + wdd->max_timeout = CGBC_WDT_MAX_TIMEOUT; + wdd->min_timeout = CGBC_WDT_MIN_TIMEOUT; + + watchdog_set_drvdata(wdd, wdt_data); + watchdog_set_nowayout(wdd, nowayout); + + wdd->timeout = CGBC_WDT_DEFAULT_TIMEOUT; + watchdog_init_timeout(wdd, timeout, dev); + cgbc_wdt_set_pretimeout(wdd, pretimeout); + + platform_set_drvdata(pdev, wdt_data); + watchdog_stop_on_reboot(wdd); + watchdog_stop_on_unregister(wdd); + + return devm_watchdog_register_device(dev, wdd); +} + +static struct platform_driver cgbc_wdt_driver = { + .driver = { + .name = "cgbc-wdt", + }, + .probe = cgbc_wdt_probe, +}; + +module_platform_driver(cgbc_wdt_driver); + +MODULE_DESCRIPTION("Congatec Board Controller Watchdog Driver"); +MODULE_AUTHOR("Thomas Richard <thomas.richard@bootlin.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/watchdog/cpu5wdt.c b/drivers/watchdog/cpu5wdt.c deleted file mode 100644 index 688b112e712b..000000000000 --- a/drivers/watchdog/cpu5wdt.c +++ /dev/null @@ -1,285 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * sma cpu5 watchdog driver - * - * Copyright (C) 2003 Heiko Ronsdorf <hero@ihg.uni-duisburg.de> - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include <linux/module.h> -#include <linux/moduleparam.h> -#include <linux/types.h> -#include <linux/errno.h> -#include <linux/miscdevice.h> -#include <linux/fs.h> -#include <linux/ioport.h> -#include <linux/timer.h> -#include <linux/completion.h> -#include <linux/jiffies.h> -#include <linux/io.h> -#include <linux/uaccess.h> -#include <linux/watchdog.h> - -/* adjustable parameters */ - -static int verbose; -static int port = 0x91; -static int ticks = 10000; -static DEFINE_SPINLOCK(cpu5wdt_lock); - -#define PFX "cpu5wdt: " - -#define CPU5WDT_EXTENT 0x0A - -#define CPU5WDT_STATUS_REG 0x00 -#define CPU5WDT_TIME_A_REG 0x02 -#define CPU5WDT_TIME_B_REG 0x03 -#define CPU5WDT_MODE_REG 0x04 -#define CPU5WDT_TRIGGER_REG 0x07 -#define CPU5WDT_ENABLE_REG 0x08 -#define CPU5WDT_RESET_REG 0x09 - -#define CPU5WDT_INTERVAL (HZ/10+1) - -/* some device data */ - -static struct { - struct completion stop; - int running; - struct timer_list timer; - int queue; - int default_ticks; - unsigned long inuse; -} cpu5wdt_device; - -/* generic helper functions */ - -static void cpu5wdt_trigger(struct timer_list *unused) -{ - if (verbose > 2) - pr_debug("trigger at %i ticks\n", ticks); - - if (cpu5wdt_device.running) - ticks--; - - spin_lock(&cpu5wdt_lock); - /* keep watchdog alive */ - outb(1, port + CPU5WDT_TRIGGER_REG); - - /* requeue?? */ - if (cpu5wdt_device.queue && ticks) - mod_timer(&cpu5wdt_device.timer, jiffies + CPU5WDT_INTERVAL); - else { - /* ticks doesn't matter anyway */ - complete(&cpu5wdt_device.stop); - } - spin_unlock(&cpu5wdt_lock); - -} - -static void cpu5wdt_reset(void) -{ - ticks = cpu5wdt_device.default_ticks; - - if (verbose) - pr_debug("reset (%i ticks)\n", (int) ticks); - -} - -static void cpu5wdt_start(void) -{ - unsigned long flags; - - spin_lock_irqsave(&cpu5wdt_lock, flags); - if (!cpu5wdt_device.queue) { - cpu5wdt_device.queue = 1; - outb(0, port + CPU5WDT_TIME_A_REG); - outb(0, port + CPU5WDT_TIME_B_REG); - outb(1, port + CPU5WDT_MODE_REG); - outb(0, port + CPU5WDT_RESET_REG); - outb(0, port + CPU5WDT_ENABLE_REG); - mod_timer(&cpu5wdt_device.timer, jiffies + CPU5WDT_INTERVAL); - } - /* if process dies, counter is not decremented */ - cpu5wdt_device.running++; - spin_unlock_irqrestore(&cpu5wdt_lock, flags); -} - -static int cpu5wdt_stop(void) -{ - unsigned long flags; - - spin_lock_irqsave(&cpu5wdt_lock, flags); - if (cpu5wdt_device.running) - cpu5wdt_device.running = 0; - ticks = cpu5wdt_device.default_ticks; - spin_unlock_irqrestore(&cpu5wdt_lock, flags); - if (verbose) - pr_crit("stop not possible\n"); - return -EIO; -} - -/* filesystem operations */ - -static int cpu5wdt_open(struct inode *inode, struct file *file) -{ - if (test_and_set_bit(0, &cpu5wdt_device.inuse)) - return -EBUSY; - return stream_open(inode, file); -} - -static int cpu5wdt_release(struct inode *inode, struct file *file) -{ - clear_bit(0, &cpu5wdt_device.inuse); - return 0; -} - -static long cpu5wdt_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) -{ - void __user *argp = (void __user *)arg; - int __user *p = argp; - unsigned int value; - static const struct watchdog_info ident = { - .options = WDIOF_CARDRESET, - .identity = "CPU5 WDT", - }; - - switch (cmd) { - case WDIOC_GETSUPPORT: - if (copy_to_user(argp, &ident, sizeof(ident))) - return -EFAULT; - break; - case WDIOC_GETSTATUS: - value = inb(port + CPU5WDT_STATUS_REG); - value = (value >> 2) & 1; - return put_user(value, p); - case WDIOC_GETBOOTSTATUS: - return put_user(0, p); - case WDIOC_SETOPTIONS: - if (get_user(value, p)) - return -EFAULT; - if (value & WDIOS_ENABLECARD) - cpu5wdt_start(); - if (value & WDIOS_DISABLECARD) - cpu5wdt_stop(); - break; - case WDIOC_KEEPALIVE: - cpu5wdt_reset(); - break; - default: - return -ENOTTY; - } - return 0; -} - -static ssize_t cpu5wdt_write(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) -{ - if (!count) - return -EIO; - cpu5wdt_reset(); - return count; -} - -static const struct file_operations cpu5wdt_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .unlocked_ioctl = cpu5wdt_ioctl, - .compat_ioctl = compat_ptr_ioctl, - .open = cpu5wdt_open, - .write = cpu5wdt_write, - .release = cpu5wdt_release, -}; - -static struct miscdevice cpu5wdt_misc = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &cpu5wdt_fops, -}; - -/* init/exit function */ - -static int cpu5wdt_init(void) -{ - unsigned int val; - int err; - - if (verbose) - pr_debug("port=0x%x, verbose=%i\n", port, verbose); - - init_completion(&cpu5wdt_device.stop); - cpu5wdt_device.queue = 0; - timer_setup(&cpu5wdt_device.timer, cpu5wdt_trigger, 0); - cpu5wdt_device.default_ticks = ticks; - - if (!request_region(port, CPU5WDT_EXTENT, PFX)) { - pr_err("request_region failed\n"); - err = -EBUSY; - goto no_port; - } - - /* watchdog reboot? */ - val = inb(port + CPU5WDT_STATUS_REG); - val = (val >> 2) & 1; - if (!val) - pr_info("sorry, was my fault\n"); - - err = misc_register(&cpu5wdt_misc); - if (err < 0) { - pr_err("misc_register failed\n"); - goto no_misc; - } - - - pr_info("init success\n"); - return 0; - -no_misc: - release_region(port, CPU5WDT_EXTENT); -no_port: - return err; -} - -static int cpu5wdt_init_module(void) -{ - return cpu5wdt_init(); -} - -static void cpu5wdt_exit(void) -{ - if (cpu5wdt_device.queue) { - cpu5wdt_device.queue = 0; - wait_for_completion(&cpu5wdt_device.stop); - del_timer(&cpu5wdt_device.timer); - } - - misc_deregister(&cpu5wdt_misc); - - release_region(port, CPU5WDT_EXTENT); - -} - -static void cpu5wdt_exit_module(void) -{ - cpu5wdt_exit(); -} - -/* module entry points */ - -module_init(cpu5wdt_init_module); -module_exit(cpu5wdt_exit_module); - -MODULE_AUTHOR("Heiko Ronsdorf <hero@ihg.uni-duisburg.de>"); -MODULE_DESCRIPTION("sma cpu5 watchdog driver"); -MODULE_LICENSE("GPL"); - -module_param_hw(port, int, ioport, 0); -MODULE_PARM_DESC(port, "base address of watchdog card, default is 0x91"); - -module_param(verbose, int, 0); -MODULE_PARM_DESC(verbose, "be verbose, default is 0 (no)"); - -module_param(ticks, int, 0); -MODULE_PARM_DESC(ticks, "count down ticks, default is 10000"); diff --git a/drivers/watchdog/cpwd.c b/drivers/watchdog/cpwd.c index 901b94d456db..13a4d47e68cd 100644 --- a/drivers/watchdog/cpwd.c +++ b/drivers/watchdog/cpwd.c @@ -240,7 +240,7 @@ static void cpwd_brokentimer(struct timer_list *unused) * were called directly instead of by kernel timer */ if (timer_pending(&cpwd_timer)) - del_timer(&cpwd_timer); + timer_delete(&cpwd_timer); for (id = 0; id < WD_NUMDEVS; id++) { if (p->devs[id].runstatus & WD_STAT_BSTOP) { @@ -507,7 +507,6 @@ static const struct file_operations cpwd_fops = { .write = cpwd_write, .read = cpwd_read, .release = cpwd_release, - .llseek = no_llseek, }; static int cpwd_probe(struct platform_device *op) @@ -630,7 +629,7 @@ static void cpwd_remove(struct platform_device *op) } if (p->broken) - del_timer_sync(&cpwd_timer); + timer_delete_sync(&cpwd_timer); if (p->initialized) free_irq(p->irq, p); @@ -654,7 +653,7 @@ static struct platform_driver cpwd_driver = { .of_match_table = cpwd_match, }, .probe = cpwd_probe, - .remove_new = cpwd_remove, + .remove = cpwd_remove, }; module_platform_driver(cpwd_driver); diff --git a/drivers/watchdog/cros_ec_wdt.c b/drivers/watchdog/cros_ec_wdt.c index ba045e29f9a5..9ffe7f505645 100644 --- a/drivers/watchdog/cros_ec_wdt.c +++ b/drivers/watchdog/cros_ec_wdt.c @@ -25,26 +25,22 @@ static int cros_ec_wdt_send_cmd(struct cros_ec_device *cros_ec, union cros_ec_wdt_data *arg) { int ret; - struct { - struct cros_ec_command msg; - union cros_ec_wdt_data data; - } __packed buf = { - .msg = { - .version = 0, - .command = EC_CMD_HANG_DETECT, - .insize = (arg->req.command == EC_HANG_DETECT_CMD_GET_STATUS) ? - sizeof(struct ec_response_hang_detect) : - 0, - .outsize = sizeof(struct ec_params_hang_detect), - }, - .data.req = arg->req - }; - - ret = cros_ec_cmd_xfer_status(cros_ec, &buf.msg); + DEFINE_RAW_FLEX(struct cros_ec_command, msg, data, + sizeof(union cros_ec_wdt_data)); + + msg->version = 0; + msg->command = EC_CMD_HANG_DETECT; + msg->insize = (arg->req.command == EC_HANG_DETECT_CMD_GET_STATUS) ? + sizeof(struct ec_response_hang_detect) : + 0; + msg->outsize = sizeof(struct ec_params_hang_detect); + *(struct ec_params_hang_detect *)msg->data = arg->req; + + ret = cros_ec_cmd_xfer_status(cros_ec, msg); if (ret < 0) return ret; - arg->resp = buf.data.resp; + arg->resp = *(struct ec_response_hang_detect *)msg->data; return 0; } @@ -58,7 +54,7 @@ static int cros_ec_wdt_ping(struct watchdog_device *wdd) arg.req.command = EC_HANG_DETECT_CMD_RELOAD; ret = cros_ec_wdt_send_cmd(cros_ec, &arg); if (ret < 0) - dev_dbg(wdd->parent, "Failed to ping watchdog (%d)", ret); + dev_dbg(wdd->parent, "Failed to ping watchdog (%d)\n", ret); return ret; } @@ -74,7 +70,7 @@ static int cros_ec_wdt_start(struct watchdog_device *wdd) arg.req.reboot_timeout_sec = wdd->timeout; ret = cros_ec_wdt_send_cmd(cros_ec, &arg); if (ret < 0) - dev_dbg(wdd->parent, "Failed to start watchdog (%d)", ret); + dev_dbg(wdd->parent, "Failed to start watchdog (%d)\n", ret); return ret; } @@ -88,7 +84,7 @@ static int cros_ec_wdt_stop(struct watchdog_device *wdd) arg.req.command = EC_HANG_DETECT_CMD_CANCEL; ret = cros_ec_wdt_send_cmd(cros_ec, &arg); if (ret < 0) - dev_dbg(wdd->parent, "Failed to stop watchdog (%d)", ret); + dev_dbg(wdd->parent, "Failed to stop watchdog (%d)\n", ret); return ret; } @@ -136,7 +132,7 @@ static int cros_ec_wdt_probe(struct platform_device *pdev) arg.req.command = EC_HANG_DETECT_CMD_GET_STATUS; ret = cros_ec_wdt_send_cmd(cros_ec, &arg); if (ret < 0) - return dev_err_probe(dev, ret, "Failed to get watchdog bootstatus"); + return dev_err_probe(dev, ret, "Failed to get watchdog bootstatus\n"); wdd->parent = &pdev->dev; wdd->info = &cros_ec_wdt_ident; @@ -150,7 +146,7 @@ static int cros_ec_wdt_probe(struct platform_device *pdev) arg.req.command = EC_HANG_DETECT_CMD_CLEAR_STATUS; ret = cros_ec_wdt_send_cmd(cros_ec, &arg); if (ret < 0) - return dev_err_probe(dev, ret, "Failed to clear watchdog bootstatus"); + return dev_err_probe(dev, ret, "Failed to clear watchdog bootstatus\n"); watchdog_stop_on_reboot(wdd); watchdog_stop_on_unregister(wdd); diff --git a/drivers/watchdog/da9052_wdt.c b/drivers/watchdog/da9052_wdt.c index d708c091bf1b..afb7887c3a1e 100644 --- a/drivers/watchdog/da9052_wdt.c +++ b/drivers/watchdog/da9052_wdt.c @@ -30,6 +30,18 @@ struct da9052_wdt_data { unsigned long jpast; }; +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 int timeout; +module_param(timeout, int, 0); +MODULE_PARM_DESC(timeout, + "Watchdog timeout in seconds. (default = " + __MODULE_STRING(WDT_DEFAULT_TIMEOUT) ")"); + static const struct { u8 reg_val; int time; /* Seconds */ @@ -135,7 +147,11 @@ static int da9052_wdt_ping(struct watchdog_device *wdt_dev) } static const struct watchdog_info da9052_wdt_info = { - .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, + .options = WDIOF_SETTIMEOUT | + WDIOF_KEEPALIVEPING | + WDIOF_CARDRESET | + WDIOF_OVERHEAT | + WDIOF_POWERUNDER, .identity = "DA9052 Watchdog", }; @@ -164,16 +180,30 @@ static int da9052_wdt_probe(struct platform_device *pdev) da9052_wdt = &driver_data->wdt; da9052_wdt->timeout = DA9052_DEF_TIMEOUT; + da9052_wdt->min_hw_heartbeat_ms = DA9052_TWDMIN; da9052_wdt->info = &da9052_wdt_info; da9052_wdt->ops = &da9052_wdt_ops; da9052_wdt->parent = dev; watchdog_set_drvdata(da9052_wdt, driver_data); + watchdog_init_timeout(da9052_wdt, timeout, dev); + watchdog_set_nowayout(da9052_wdt, nowayout); - ret = da9052_reg_update(da9052, DA9052_CONTROL_D_REG, - DA9052_CONTROLD_TWDSCALE, 0); - if (ret < 0) { - dev_err(dev, "Failed to disable watchdog bits, %d\n", ret); + if (da9052->fault_log & DA9052_FAULTLOG_TWDERROR) + da9052_wdt->bootstatus |= WDIOF_CARDRESET; + if (da9052->fault_log & DA9052_FAULTLOG_TEMPOVER) + da9052_wdt->bootstatus |= WDIOF_OVERHEAT; + if (da9052->fault_log & DA9052_FAULTLOG_VDDFAULT) + da9052_wdt->bootstatus |= WDIOF_POWERUNDER; + + ret = da9052_reg_read(da9052, DA9052_CONTROL_D_REG); + if (ret < 0) return ret; + + /* Check if FW enabled the watchdog */ + if (ret & DA9052_CONTROLD_TWDSCALE) { + /* Ensure proper initialization */ + da9052_wdt_start(da9052_wdt); + set_bit(WDOG_HW_RUNNING, &da9052_wdt->status); } return devm_watchdog_register_device(dev, &driver_data->wdt); diff --git a/drivers/watchdog/da9055_wdt.c b/drivers/watchdog/da9055_wdt.c index 389a4bdd208c..9d5a2009466f 100644 --- a/drivers/watchdog/da9055_wdt.c +++ b/drivers/watchdog/da9055_wdt.c @@ -146,12 +146,7 @@ static int da9055_wdt_probe(struct platform_device *pdev) return ret; } - ret = devm_watchdog_register_device(dev, &driver_data->wdt); - if (ret != 0) - dev_err(da9055->dev, "watchdog_register_device() failed: %d\n", - ret); - - return ret; + return devm_watchdog_register_device(dev, &driver_data->wdt); } static struct platform_driver da9055_wdt_driver = { diff --git a/drivers/watchdog/da9063_wdt.c b/drivers/watchdog/da9063_wdt.c index 684667469b10..92e1b78ff481 100644 --- a/drivers/watchdog/da9063_wdt.c +++ b/drivers/watchdog/da9063_wdt.c @@ -27,7 +27,6 @@ * others: timeout = 2048 ms * 2^(TWDSCALE-1). */ static const unsigned int wdt_timeout[] = { 0, 2, 4, 8, 16, 32, 65, 131 }; -static bool use_sw_pm; #define DA9063_TWDSCALE_DISABLE 0 #define DA9063_TWDSCALE_MIN 1 @@ -230,7 +229,7 @@ static int da9063_wdt_probe(struct platform_device *pdev) if (!wdd) return -ENOMEM; - use_sw_pm = device_property_present(dev, "dlg,use-sw-pm"); + da9063->use_sw_pm = device_property_present(dev, "dlg,use-sw-pm"); wdd->info = &da9063_watchdog_info; wdd->ops = &da9063_watchdog_ops; @@ -264,11 +263,12 @@ static int da9063_wdt_probe(struct platform_device *pdev) return devm_watchdog_register_device(dev, wdd); } -static int __maybe_unused da9063_wdt_suspend(struct device *dev) +static int da9063_wdt_suspend(struct device *dev) { struct watchdog_device *wdd = dev_get_drvdata(dev); + struct da9063 *da9063 = watchdog_get_drvdata(wdd); - if (!use_sw_pm) + if (!da9063->use_sw_pm) return 0; if (watchdog_active(wdd)) @@ -277,11 +277,12 @@ static int __maybe_unused da9063_wdt_suspend(struct device *dev) return 0; } -static int __maybe_unused da9063_wdt_resume(struct device *dev) +static int da9063_wdt_resume(struct device *dev) { struct watchdog_device *wdd = dev_get_drvdata(dev); + struct da9063 *da9063 = watchdog_get_drvdata(wdd); - if (!use_sw_pm) + if (!da9063->use_sw_pm) return 0; if (watchdog_active(wdd)) @@ -290,14 +291,14 @@ static int __maybe_unused da9063_wdt_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(da9063_wdt_pm_ops, - da9063_wdt_suspend, da9063_wdt_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(da9063_wdt_pm_ops, da9063_wdt_suspend, + da9063_wdt_resume); static struct platform_driver da9063_wdt_driver = { .probe = da9063_wdt_probe, .driver = { .name = DA9063_DRVNAME_WATCHDOG, - .pm = &da9063_wdt_pm_ops, + .pm = pm_sleep_ptr(&da9063_wdt_pm_ops), }, }; module_platform_driver(da9063_wdt_driver); diff --git a/drivers/watchdog/diag288_wdt.c b/drivers/watchdog/diag288_wdt.c index 4631d0a3866a..887d5a6c155b 100644 --- a/drivers/watchdog/diag288_wdt.c +++ b/drivers/watchdog/diag288_wdt.c @@ -27,27 +27,15 @@ #include <linux/moduleparam.h> #include <linux/slab.h> #include <linux/watchdog.h> +#include <asm/machine.h> #include <asm/ebcdic.h> +#include <asm/diag288.h> #include <asm/diag.h> #include <linux/io.h> #define MAX_CMDLEN 240 #define DEFAULT_CMD "SYSTEM RESTART" -#define MIN_INTERVAL 15 /* Minimal time supported by diag88 */ -#define MAX_INTERVAL 3600 /* One hour should be enough - pure estimation */ - -#define WDT_DEFAULT_TIMEOUT 30 - -/* Function codes - init, change, cancel */ -#define WDT_FUNC_INIT 0 -#define WDT_FUNC_CHANGE 1 -#define WDT_FUNC_CANCEL 2 -#define WDT_FUNC_CONCEAL 0x80000000 - -/* Action codes for LPAR watchdog */ -#define LPARWDT_RESTART 0 - static char wdt_cmd[MAX_CMDLEN] = DEFAULT_CMD; static bool conceal_on; static bool nowayout_info = WATCHDOG_NOWAYOUT; @@ -74,22 +62,8 @@ static char *cmd_buf; static int diag288(unsigned int func, unsigned int timeout, unsigned long action, unsigned int len) { - union register_pair r1 = { .even = func, .odd = timeout, }; - union register_pair r3 = { .even = action, .odd = len, }; - int err; - diag_stat_inc(DIAG_STAT_X288); - - err = -EINVAL; - asm volatile( - " diag %[r1],%[r3],0x288\n" - "0: la %[err],0\n" - "1:\n" - EX_TABLE(0b, 1b) - : [err] "+d" (err) - : [r1] "d" (r1.pair), [r3] "d" (r3.pair) - : "cc", "memory"); - return err; + return __diag288(func, timeout, action, len); } static int diag288_str(unsigned int func, unsigned int timeout, char *cmd) @@ -110,7 +84,7 @@ static int wdt_start(struct watchdog_device *dev) int ret; unsigned int func; - if (MACHINE_IS_VM) { + if (machine_is_vm()) { func = conceal_on ? (WDT_FUNC_INIT | WDT_FUNC_CONCEAL) : WDT_FUNC_INIT; ret = diag288_str(func, dev->timeout, wdt_cmd); @@ -136,7 +110,7 @@ static int wdt_ping(struct watchdog_device *dev) int ret; unsigned int func; - if (MACHINE_IS_VM) { + if (machine_is_vm()) { /* * It seems to be ok to z/VM to use the init function to * retrigger the watchdog. On LPAR WDT_FUNC_CHANGE must @@ -188,34 +162,14 @@ static struct watchdog_device wdt_dev = { static int __init diag288_init(void) { - int ret; - watchdog_set_nowayout(&wdt_dev, nowayout_info); - if (MACHINE_IS_VM) { + if (machine_is_vm()) { cmd_buf = kmalloc(MAX_CMDLEN, GFP_KERNEL); if (!cmd_buf) { pr_err("The watchdog cannot be initialized\n"); return -ENOMEM; } - - ret = diag288_str(WDT_FUNC_INIT, MIN_INTERVAL, "BEGIN"); - if (ret != 0) { - pr_err("The watchdog cannot be initialized\n"); - kfree(cmd_buf); - return -EINVAL; - } - } else { - if (diag288(WDT_FUNC_INIT, WDT_DEFAULT_TIMEOUT, - LPARWDT_RESTART, 0)) { - pr_err("The watchdog cannot be initialized\n"); - return -EINVAL; - } - } - - if (diag288(WDT_FUNC_CANCEL, 0, 0, 0)) { - pr_err("The watchdog cannot be deactivated\n"); - return -EINVAL; } return watchdog_register_device(&wdt_dev); @@ -227,5 +181,5 @@ static void __exit diag288_exit(void) kfree(cmd_buf); } -module_init(diag288_init); +module_cpu_feature_match(S390_CPU_FEATURE_D288, diag288_init); module_exit(diag288_exit); diff --git a/drivers/watchdog/dw_wdt.c b/drivers/watchdog/dw_wdt.c index 84dca3695f86..26efca9ae0e7 100644 --- a/drivers/watchdog/dw_wdt.c +++ b/drivers/watchdog/dw_wdt.c @@ -684,7 +684,7 @@ MODULE_DEVICE_TABLE(of, dw_wdt_of_match); static struct platform_driver dw_wdt_driver = { .probe = dw_wdt_drv_probe, - .remove_new = dw_wdt_drv_remove, + .remove = dw_wdt_drv_remove, .driver = { .name = "dw_wdt", .of_match_table = of_match_ptr(dw_wdt_of_match), diff --git a/drivers/watchdog/eurotechwdt.c b/drivers/watchdog/eurotechwdt.c index e26609ad4c17..10c647b1226a 100644 --- a/drivers/watchdog/eurotechwdt.c +++ b/drivers/watchdog/eurotechwdt.c @@ -368,7 +368,6 @@ static int eurwdt_notify_sys(struct notifier_block *this, unsigned long code, static const struct file_operations eurwdt_fops = { .owner = THIS_MODULE, - .llseek = no_llseek, .write = eurwdt_write, .unlocked_ioctl = eurwdt_ioctl, .compat_ioctl = compat_ptr_ioctl, diff --git a/drivers/watchdog/exar_wdt.c b/drivers/watchdog/exar_wdt.c index 7c61ff343271..c2e3bb08df89 100644 --- a/drivers/watchdog/exar_wdt.c +++ b/drivers/watchdog/exar_wdt.c @@ -221,7 +221,7 @@ static const struct watchdog_info exar_wdt_info = { .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE, - .identity = "Exar/MaxLinear XR28V38x Watchdog", + .identity = "Exar XR28V38x Watchdog", }; static const struct watchdog_ops exar_wdt_ops = { diff --git a/drivers/watchdog/gef_wdt.c b/drivers/watchdog/gef_wdt.c index 6a1db1c783fa..bf6f733dfb5f 100644 --- a/drivers/watchdog/gef_wdt.c +++ b/drivers/watchdog/gef_wdt.c @@ -245,7 +245,6 @@ static int gef_wdt_release(struct inode *inode, struct file *file) static const struct file_operations gef_wdt_fops = { .owner = THIS_MODULE, - .llseek = no_llseek, .write = gef_wdt_write, .unlocked_ioctl = gef_wdt_ioctl, .compat_ioctl = compat_ptr_ioctl, @@ -306,7 +305,7 @@ static struct platform_driver gef_wdt_driver = { .of_match_table = gef_wdt_ids, }, .probe = gef_wdt_probe, - .remove_new = gef_wdt_remove, + .remove = gef_wdt_remove, }; static int __init gef_wdt_init(void) diff --git a/drivers/watchdog/geodewdt.c b/drivers/watchdog/geodewdt.c index 5186c37ad451..5b80ade1c681 100644 --- a/drivers/watchdog/geodewdt.c +++ b/drivers/watchdog/geodewdt.c @@ -196,7 +196,6 @@ static long geodewdt_ioctl(struct file *file, unsigned int cmd, static const struct file_operations geodewdt_fops = { .owner = THIS_MODULE, - .llseek = no_llseek, .write = geodewdt_write, .unlocked_ioctl = geodewdt_ioctl, .compat_ioctl = compat_ptr_ioctl, @@ -249,7 +248,7 @@ static void geodewdt_shutdown(struct platform_device *dev) } static struct platform_driver geodewdt_driver = { - .remove_new = geodewdt_remove, + .remove = geodewdt_remove, .shutdown = geodewdt_shutdown, .driver = { .name = DRV_NAME, diff --git a/drivers/watchdog/gxp-wdt.c b/drivers/watchdog/gxp-wdt.c index 2fd85be88278..f2c236160266 100644 --- a/drivers/watchdog/gxp-wdt.c +++ b/drivers/watchdog/gxp-wdt.c @@ -151,10 +151,8 @@ static int gxp_wdt_probe(struct platform_device *pdev) watchdog_stop_on_reboot(&drvdata->wdd); err = devm_watchdog_register_device(dev, &drvdata->wdd); - if (err) { - dev_err(dev, "Failed to register watchdog device"); + if (err) return err; - } dev_info(dev, "HPE GXP watchdog timer"); diff --git a/drivers/watchdog/iTCO_wdt.c b/drivers/watchdog/iTCO_wdt.c index 264857d314da..9ab769aa0244 100644 --- a/drivers/watchdog/iTCO_wdt.c +++ b/drivers/watchdog/iTCO_wdt.c @@ -58,7 +58,6 @@ #include <linux/platform_device.h> /* For platform_driver framework */ #include <linux/pci.h> /* For pci functions */ #include <linux/ioport.h> /* For io-port access */ -#include <linux/spinlock.h> /* For spin_lock/spin_unlock/... */ #include <linux/uaccess.h> /* For copy_to_user/put_user/... */ #include <linux/io.h> /* For inb/outb/... */ #include <linux/platform_data/itco_wdt.h> @@ -82,6 +81,13 @@ #define TCO2_CNT(p) (TCOBASE(p) + 0x0a) /* TCO2 Control Register */ #define TCOv2_TMR(p) (TCOBASE(p) + 0x12) /* TCOv2 Timer Initial Value*/ +/* + * NMI_NOW is bit 8 of TCO1_CNT register + * Read/Write + * This bit is implemented as RW but has no effect on HW. + */ +#define NMI_NOW BIT(8) + /* internal variables */ struct iTCO_wdt_private { struct watchdog_device wddev; @@ -95,8 +101,6 @@ struct iTCO_wdt_private { * or memory-mapped PMC register bit 4 (TCO version 3). */ unsigned long __iomem *gcs_pmc; - /* the lock for io operations */ - spinlock_t io_lock; /* the PCI-device */ struct pci_dev *pci_dev; /* whether or not the watchdog has been suspended */ @@ -219,13 +223,23 @@ static int update_no_reboot_bit_cnt(void *priv, bool set) struct iTCO_wdt_private *p = priv; u16 val, newval; - val = inw(TCO1_CNT(p)); + /* + * writing back 1b1 to NMI_NOW of TCO1_CNT register + * causes NMI_NOW bit inversion what consequently does + * not allow to perform the register's value comparison + * properly. + * + * NMI_NOW bit masking for TCO1_CNT register values + * helps to avoid possible NMI_NOW bit inversions on + * following write operation. + */ + val = inw(TCO1_CNT(p)) & ~NMI_NOW; if (set) val |= BIT(0); else val &= ~BIT(0); outw(val, TCO1_CNT(p)); - newval = inw(TCO1_CNT(p)); + newval = inw(TCO1_CNT(p)) & ~NMI_NOW; /* make sure the update is successful */ return val != newval ? -EIO : 0; @@ -269,13 +283,10 @@ static int iTCO_wdt_start(struct watchdog_device *wd_dev) struct iTCO_wdt_private *p = watchdog_get_drvdata(wd_dev); unsigned int val; - spin_lock(&p->io_lock); - iTCO_vendor_pre_start(p->smi_res, wd_dev->timeout); /* disable chipset's NO_REBOOT bit */ if (p->update_no_reboot_bit(p->no_reboot_priv, false)) { - spin_unlock(&p->io_lock); dev_err(wd_dev->parent, "failed to reset NO_REBOOT flag, reboot disabled by hardware/BIOS\n"); return -EIO; } @@ -292,7 +303,6 @@ static int iTCO_wdt_start(struct watchdog_device *wd_dev) val &= 0xf7ff; outw(val, TCO1_CNT(p)); val = inw(TCO1_CNT(p)); - spin_unlock(&p->io_lock); if (val & 0x0800) return -1; @@ -304,8 +314,6 @@ static int iTCO_wdt_stop(struct watchdog_device *wd_dev) struct iTCO_wdt_private *p = watchdog_get_drvdata(wd_dev); unsigned int val; - spin_lock(&p->io_lock); - iTCO_vendor_pre_stop(p->smi_res); /* Bit 11: TCO Timer Halt -> 1 = The TCO timer is disabled */ @@ -317,8 +325,6 @@ static int iTCO_wdt_stop(struct watchdog_device *wd_dev) /* Set the NO_REBOOT bit to prevent later reboots, just for sure */ p->update_no_reboot_bit(p->no_reboot_priv, true); - spin_unlock(&p->io_lock); - if ((val & 0x0800) == 0) return -1; return 0; @@ -328,8 +334,6 @@ static int iTCO_wdt_ping(struct watchdog_device *wd_dev) { struct iTCO_wdt_private *p = watchdog_get_drvdata(wd_dev); - spin_lock(&p->io_lock); - /* Reload the timer by writing to the TCO Timer Counter register */ if (p->iTCO_version >= 2) { outw(0x01, TCO_RLD(p)); @@ -341,7 +345,6 @@ static int iTCO_wdt_ping(struct watchdog_device *wd_dev) outb(0x01, TCO_RLD(p)); } - spin_unlock(&p->io_lock); return 0; } @@ -368,24 +371,20 @@ static int iTCO_wdt_set_timeout(struct watchdog_device *wd_dev, unsigned int t) /* Write new heartbeat to watchdog */ if (p->iTCO_version >= 2) { - spin_lock(&p->io_lock); val16 = inw(TCOv2_TMR(p)); val16 &= 0xfc00; val16 |= tmrval; outw(val16, TCOv2_TMR(p)); val16 = inw(TCOv2_TMR(p)); - spin_unlock(&p->io_lock); if ((val16 & 0x3ff) != tmrval) return -EINVAL; } else if (p->iTCO_version == 1) { - spin_lock(&p->io_lock); val8 = inb(TCOv1_TMR(p)); val8 &= 0xc0; val8 |= (tmrval & 0xff); outb(val8, TCOv1_TMR(p)); val8 = inb(TCOv1_TMR(p)); - spin_unlock(&p->io_lock); if ((val8 & 0x3f) != tmrval) return -EINVAL; @@ -404,19 +403,15 @@ static unsigned int iTCO_wdt_get_timeleft(struct watchdog_device *wd_dev) /* read the TCO Timer */ if (p->iTCO_version >= 2) { - spin_lock(&p->io_lock); val16 = inw(TCO_RLD(p)); val16 &= 0x3ff; - spin_unlock(&p->io_lock); time_left = ticks_to_seconds(p, val16); } else if (p->iTCO_version == 1) { - spin_lock(&p->io_lock); val8 = inb(TCO_RLD(p)); val8 &= 0x3f; if (!(inw(TCO1_STS(p)) & 0x0008)) val8 += (inb(TCOv1_TMR(p)) & 0x3f); - spin_unlock(&p->io_lock); time_left = ticks_to_seconds(p, val8); } @@ -476,8 +471,6 @@ static int iTCO_wdt_probe(struct platform_device *pdev) if (!p) return -ENOMEM; - spin_lock_init(&p->io_lock); - p->tco_res = platform_get_resource(pdev, IORESOURCE_IO, ICH_RES_IO_TCO); if (!p->tco_res) return -ENODEV; @@ -563,8 +556,8 @@ static int iTCO_wdt_probe(struct platform_device *pdev) } ident.firmware_version = p->iTCO_version; - p->wddev.info = &ident, - p->wddev.ops = &iTCO_wdt_ops, + p->wddev.info = &ident; + p->wddev.ops = &iTCO_wdt_ops; p->wddev.bootstatus = 0; p->wddev.timeout = WATCHDOG_TIMEOUT; watchdog_set_nowayout(&p->wddev, nowayout); @@ -587,15 +580,14 @@ static int iTCO_wdt_probe(struct platform_device *pdev) iTCO_wdt_set_timeout(&p->wddev, WATCHDOG_TIMEOUT); dev_info(dev, "timeout value out of range, using %d\n", WATCHDOG_TIMEOUT); + heartbeat = WATCHDOG_TIMEOUT; } watchdog_stop_on_reboot(&p->wddev); watchdog_stop_on_unregister(&p->wddev); ret = devm_watchdog_register_device(dev, &p->wddev); - if (ret != 0) { - dev_err(dev, "cannot register watchdog device (err=%d)\n", ret); + if (ret != 0) return ret; - } dev_info(dev, "initialized. heartbeat=%d sec (nowayout=%d)\n", heartbeat, nowayout); diff --git a/drivers/watchdog/ib700wdt.c b/drivers/watchdog/ib700wdt.c index 39ea97009abd..5ce6101d236d 100644 --- a/drivers/watchdog/ib700wdt.c +++ b/drivers/watchdog/ib700wdt.c @@ -256,7 +256,6 @@ static int ibwdt_close(struct inode *inode, struct file *file) static const struct file_operations ibwdt_fops = { .owner = THIS_MODULE, - .llseek = no_llseek, .write = ibwdt_write, .unlocked_ioctl = ibwdt_ioctl, .compat_ioctl = compat_ptr_ioctl, @@ -332,7 +331,7 @@ static void ibwdt_shutdown(struct platform_device *dev) } static struct platform_driver ibwdt_driver = { - .remove_new = ibwdt_remove, + .remove = ibwdt_remove, .shutdown = ibwdt_shutdown, .driver = { .name = DRV_NAME, diff --git a/drivers/watchdog/ibmasr.c b/drivers/watchdog/ibmasr.c index 6955c693b5fd..cf845f865945 100644 --- a/drivers/watchdog/ibmasr.c +++ b/drivers/watchdog/ibmasr.c @@ -340,7 +340,6 @@ static int asr_release(struct inode *inode, struct file *file) static const struct file_operations asr_fops = { .owner = THIS_MODULE, - .llseek = no_llseek, .write = asr_write, .unlocked_ioctl = asr_ioctl, .compat_ioctl = compat_ptr_ioctl, diff --git a/drivers/watchdog/ie6xx_wdt.c b/drivers/watchdog/ie6xx_wdt.c index e5cbb409df25..5a7bb7e84653 100644 --- a/drivers/watchdog/ie6xx_wdt.c +++ b/drivers/watchdog/ie6xx_wdt.c @@ -280,7 +280,7 @@ static void ie6xx_wdt_remove(struct platform_device *pdev) static struct platform_driver ie6xx_wdt_driver = { .probe = ie6xx_wdt_probe, - .remove_new = ie6xx_wdt_remove, + .remove = ie6xx_wdt_remove, .driver = { .name = DRIVER_NAME, }, diff --git a/drivers/watchdog/imx2_wdt.c b/drivers/watchdog/imx2_wdt.c index 42e8ffae18dd..4b3a192ee3e8 100644 --- a/drivers/watchdog/imx2_wdt.c +++ b/drivers/watchdog/imx2_wdt.c @@ -379,7 +379,7 @@ static void imx2_wdt_shutdown(struct platform_device *pdev) } /* Disable watchdog if it is active or non-active but still running */ -static int __maybe_unused imx2_wdt_suspend(struct device *dev) +static int imx2_wdt_suspend(struct device *dev) { struct watchdog_device *wdog = dev_get_drvdata(dev); struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog); @@ -404,7 +404,7 @@ static int __maybe_unused imx2_wdt_suspend(struct device *dev) } /* Enable watchdog and configure it if necessary */ -static int __maybe_unused imx2_wdt_resume(struct device *dev) +static int imx2_wdt_resume(struct device *dev) { struct watchdog_device *wdog = dev_get_drvdata(dev); struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog); @@ -435,8 +435,8 @@ static int __maybe_unused imx2_wdt_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(imx2_wdt_pm_ops, imx2_wdt_suspend, - imx2_wdt_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(imx2_wdt_pm_ops, imx2_wdt_suspend, + imx2_wdt_resume); static struct imx2_wdt_data imx_wdt = { .wdw_supported = true, @@ -476,7 +476,7 @@ static struct platform_driver imx2_wdt_driver = { .shutdown = imx2_wdt_shutdown, .driver = { .name = DRIVER_NAME, - .pm = &imx2_wdt_pm_ops, + .pm = pm_sleep_ptr(&imx2_wdt_pm_ops), .of_match_table = imx2_wdt_dt_ids, }, }; diff --git a/drivers/watchdog/imx7ulp_wdt.c b/drivers/watchdog/imx7ulp_wdt.c index b21d7a74a42d..0f13a3053357 100644 --- a/drivers/watchdog/imx7ulp_wdt.c +++ b/drivers/watchdog/imx7ulp_wdt.c @@ -55,6 +55,7 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" struct imx_wdt_hw_feature { bool prescaler_enable; + bool post_rcs_wait; u32 wdog_clock_rate; }; @@ -62,7 +63,6 @@ struct imx7ulp_wdt_device { struct watchdog_device wdd; void __iomem *base; struct clk *clk; - bool post_rcs_wait; bool ext_reset; const struct imx_wdt_hw_feature *hw; }; @@ -95,7 +95,7 @@ static int imx7ulp_wdt_wait_rcs(struct imx7ulp_wdt_device *wdt) ret = -ETIMEDOUT; /* Wait 2.5 clocks after RCS done */ - if (wdt->post_rcs_wait) + if (wdt->hw->post_rcs_wait) usleep_range(wait_min, wait_min + 2000); return ret; @@ -290,6 +290,11 @@ static int imx7ulp_wdt_init(struct imx7ulp_wdt_device *wdt, unsigned int timeout if (wdt->ext_reset) val |= WDOG_CS_INT_EN; + if (readl(wdt->base + WDOG_CS) & WDOG_CS_EN) { + set_bit(WDOG_HW_RUNNING, &wdt->wdd.status); + val |= WDOG_CS_EN; + } + do { ret = _imx7ulp_wdt_init(wdt, timeout, val); toval = readl(wdt->base + WDOG_TOVAL); @@ -329,15 +334,6 @@ static int imx7ulp_wdt_probe(struct platform_device *pdev) /* The WDOG may need to do external reset through dedicated pin */ imx7ulp_wdt->ext_reset = of_property_read_bool(dev->of_node, "fsl,ext-reset-output"); - imx7ulp_wdt->post_rcs_wait = true; - if (of_device_is_compatible(dev->of_node, - "fsl,imx8ulp-wdt")) { - dev_info(dev, "imx8ulp wdt probe\n"); - imx7ulp_wdt->post_rcs_wait = false; - } else { - dev_info(dev, "imx7ulp wdt probe\n"); - } - wdog = &imx7ulp_wdt->wdd; wdog->info = &imx7ulp_wdt_info; wdog->ops = &imx7ulp_wdt_ops; @@ -398,6 +394,12 @@ static const struct dev_pm_ops imx7ulp_wdt_pm_ops = { static const struct imx_wdt_hw_feature imx7ulp_wdt_hw = { .prescaler_enable = false, .wdog_clock_rate = 1000, + .post_rcs_wait = true, +}; + +static const struct imx_wdt_hw_feature imx8ulp_wdt_hw = { + .prescaler_enable = false, + .wdog_clock_rate = 1000, }; static const struct imx_wdt_hw_feature imx93_wdt_hw = { @@ -406,8 +408,8 @@ static const struct imx_wdt_hw_feature imx93_wdt_hw = { }; static const struct of_device_id imx7ulp_wdt_dt_ids[] = { - { .compatible = "fsl,imx8ulp-wdt", .data = &imx7ulp_wdt_hw, }, { .compatible = "fsl,imx7ulp-wdt", .data = &imx7ulp_wdt_hw, }, + { .compatible = "fsl,imx8ulp-wdt", .data = &imx8ulp_wdt_hw, }, { .compatible = "fsl,imx93-wdt", .data = &imx93_wdt_hw, }, { /* sentinel */ } }; diff --git a/drivers/watchdog/imx_sc_wdt.c b/drivers/watchdog/imx_sc_wdt.c index e51fe1b78518..1280b9b1ec2a 100644 --- a/drivers/watchdog/imx_sc_wdt.c +++ b/drivers/watchdog/imx_sc_wdt.c @@ -56,6 +56,25 @@ static int imx_sc_wdt_ping(struct watchdog_device *wdog) return 0; } +static bool imx_sc_wdt_is_running(void) +{ + struct arm_smccc_res res; + + arm_smccc_smc(IMX_SIP_TIMER, IMX_SIP_TIMER_START_WDOG, + 0, 0, 0, 0, 0, 0, &res); + + /* Already enabled (SC_TIMER_ERR_BUSY)? */ + if (res.a0 == SC_TIMER_ERR_BUSY) + return true; + + /* Undo only if that was us who has (successfully) enabled the WDT */ + if (!res.a0) + arm_smccc_smc(IMX_SIP_TIMER, IMX_SIP_TIMER_STOP_WDOG, + 0, 0, 0, 0, 0, 0, &res); + + return false; +} + static int imx_sc_wdt_start(struct watchdog_device *wdog) { struct arm_smccc_res res; @@ -183,6 +202,9 @@ static int imx_sc_wdt_probe(struct platform_device *pdev) if (ret) return ret; + if (imx_sc_wdt_is_running()) + set_bit(WDOG_HW_RUNNING, &wdog->status); + watchdog_stop_on_reboot(wdog); watchdog_stop_on_unregister(wdog); @@ -216,29 +238,6 @@ register_device: return devm_watchdog_register_device(dev, wdog); } -static int __maybe_unused imx_sc_wdt_suspend(struct device *dev) -{ - struct imx_sc_wdt_device *imx_sc_wdd = dev_get_drvdata(dev); - - if (watchdog_active(&imx_sc_wdd->wdd)) - imx_sc_wdt_stop(&imx_sc_wdd->wdd); - - return 0; -} - -static int __maybe_unused imx_sc_wdt_resume(struct device *dev) -{ - struct imx_sc_wdt_device *imx_sc_wdd = dev_get_drvdata(dev); - - if (watchdog_active(&imx_sc_wdd->wdd)) - imx_sc_wdt_start(&imx_sc_wdd->wdd); - - return 0; -} - -static SIMPLE_DEV_PM_OPS(imx_sc_wdt_pm_ops, - imx_sc_wdt_suspend, imx_sc_wdt_resume); - static const struct of_device_id imx_sc_wdt_dt_ids[] = { { .compatible = "fsl,imx-sc-wdt", }, { /* sentinel */ } @@ -250,7 +249,6 @@ static struct platform_driver imx_sc_wdt_driver = { .driver = { .name = "imx-sc-wdt", .of_match_table = imx_sc_wdt_dt_ids, - .pm = &imx_sc_wdt_pm_ops, }, }; module_platform_driver(imx_sc_wdt_driver); diff --git a/drivers/watchdog/indydog.c b/drivers/watchdog/indydog.c index 9857bb74a723..d3092d261345 100644 --- a/drivers/watchdog/indydog.c +++ b/drivers/watchdog/indydog.c @@ -149,7 +149,6 @@ static int indydog_notify_sys(struct notifier_block *this, static const struct file_operations indydog_fops = { .owner = THIS_MODULE, - .llseek = no_llseek, .write = indydog_write, .unlocked_ioctl = indydog_ioctl, .compat_ioctl = compat_ptr_ioctl, diff --git a/drivers/watchdog/intel-mid_wdt.c b/drivers/watchdog/intel-mid_wdt.c index 8d71f6a2236b..756d262dc580 100644 --- a/drivers/watchdog/intel-mid_wdt.c +++ b/drivers/watchdog/intel-mid_wdt.c @@ -20,9 +20,8 @@ #include <linux/types.h> #include <linux/watchdog.h> -#include <linux/platform_data/intel-mid_wdt.h> - -#include <asm/intel_scu_ipc.h> +#include <linux/platform_data/x86/intel-mid_wdt.h> +#include <linux/platform_data/x86/intel_scu_ipc.h> #define IPC_WATCHDOG 0xf8 diff --git a/drivers/watchdog/intel_oc_wdt.c b/drivers/watchdog/intel_oc_wdt.c new file mode 100644 index 000000000000..7c0551106981 --- /dev/null +++ b/drivers/watchdog/intel_oc_wdt.c @@ -0,0 +1,233 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Intel OC Watchdog driver + * + * Copyright (C) 2025, Siemens + * Author: Diogo Ivo <diogo.ivo@siemens.com> + */ + +#define DRV_NAME "intel_oc_wdt" + +#include <linux/acpi.h> +#include <linux/bits.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/platform_device.h> +#include <linux/watchdog.h> + +#define INTEL_OC_WDT_TOV GENMASK(9, 0) +#define INTEL_OC_WDT_MIN_TOV 1 +#define INTEL_OC_WDT_MAX_TOV 1024 +#define INTEL_OC_WDT_DEF_TOV 60 + +/* + * One-time writable lock bit. If set forbids + * modification of itself, _TOV and _EN until + * next reboot. + */ +#define INTEL_OC_WDT_CTL_LCK BIT(12) + +#define INTEL_OC_WDT_EN BIT(14) +#define INTEL_OC_WDT_NO_ICCSURV_STS BIT(24) +#define INTEL_OC_WDT_ICCSURV_STS BIT(25) +#define INTEL_OC_WDT_RLD BIT(31) + +#define INTEL_OC_WDT_STS_BITS (INTEL_OC_WDT_NO_ICCSURV_STS | \ + INTEL_OC_WDT_ICCSURV_STS) + +#define INTEL_OC_WDT_CTRL_REG(wdt) ((wdt)->ctrl_res->start) + +struct intel_oc_wdt { + struct watchdog_device wdd; + struct resource *ctrl_res; + bool locked; +}; + +static int heartbeat; +module_param(heartbeat, uint, 0); +MODULE_PARM_DESC(heartbeat, "Watchdog heartbeats in seconds. (default=" + __MODULE_STRING(WDT_HEARTBEAT) ")"); + +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 int intel_oc_wdt_start(struct watchdog_device *wdd) +{ + struct intel_oc_wdt *oc_wdt = watchdog_get_drvdata(wdd); + + if (oc_wdt->locked) + return 0; + + outl(inl(INTEL_OC_WDT_CTRL_REG(oc_wdt)) | INTEL_OC_WDT_EN, + INTEL_OC_WDT_CTRL_REG(oc_wdt)); + + return 0; +} + +static int intel_oc_wdt_stop(struct watchdog_device *wdd) +{ + struct intel_oc_wdt *oc_wdt = watchdog_get_drvdata(wdd); + + outl(inl(INTEL_OC_WDT_CTRL_REG(oc_wdt)) & ~INTEL_OC_WDT_EN, + INTEL_OC_WDT_CTRL_REG(oc_wdt)); + + return 0; +} + +static int intel_oc_wdt_ping(struct watchdog_device *wdd) +{ + struct intel_oc_wdt *oc_wdt = watchdog_get_drvdata(wdd); + + outl(inl(INTEL_OC_WDT_CTRL_REG(oc_wdt)) | INTEL_OC_WDT_RLD, + INTEL_OC_WDT_CTRL_REG(oc_wdt)); + + return 0; +} + +static int intel_oc_wdt_set_timeout(struct watchdog_device *wdd, + unsigned int t) +{ + struct intel_oc_wdt *oc_wdt = watchdog_get_drvdata(wdd); + + outl((inl(INTEL_OC_WDT_CTRL_REG(oc_wdt)) & ~INTEL_OC_WDT_TOV) | (t - 1), + INTEL_OC_WDT_CTRL_REG(oc_wdt)); + + wdd->timeout = t; + + return 0; +} + +static const struct watchdog_info intel_oc_wdt_info = { + .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING, + .identity = DRV_NAME, +}; + +static const struct watchdog_ops intel_oc_wdt_ops = { + .owner = THIS_MODULE, + .start = intel_oc_wdt_start, + .stop = intel_oc_wdt_stop, + .ping = intel_oc_wdt_ping, + .set_timeout = intel_oc_wdt_set_timeout, +}; + +static int intel_oc_wdt_setup(struct intel_oc_wdt *oc_wdt) +{ + struct watchdog_info *info; + unsigned long val; + + val = inl(INTEL_OC_WDT_CTRL_REG(oc_wdt)); + + if (val & INTEL_OC_WDT_STS_BITS) + oc_wdt->wdd.bootstatus |= WDIOF_CARDRESET; + + oc_wdt->locked = !!(val & INTEL_OC_WDT_CTL_LCK); + + if (val & INTEL_OC_WDT_EN) { + /* + * No need to issue a ping here to "commit" the new timeout + * value to hardware as the watchdog core schedules one + * immediately when registering the watchdog. + */ + set_bit(WDOG_HW_RUNNING, &oc_wdt->wdd.status); + + if (oc_wdt->locked) { + info = (struct watchdog_info *)&intel_oc_wdt_info; + /* + * Set nowayout unconditionally as we cannot stop + * the watchdog. + */ + nowayout = true; + /* + * If we are locked read the current timeout value + * and inform the core we can't change it. + */ + oc_wdt->wdd.timeout = (val & INTEL_OC_WDT_TOV) + 1; + info->options &= ~WDIOF_SETTIMEOUT; + + dev_info(oc_wdt->wdd.parent, + "Register access locked, heartbeat fixed at: %u s\n", + oc_wdt->wdd.timeout); + } + } else if (oc_wdt->locked) { + /* + * In case the watchdog is disabled and locked there + * is nothing we can do with it so just fail probing. + */ + return -EACCES; + } + + val &= ~INTEL_OC_WDT_TOV; + outl(val | (oc_wdt->wdd.timeout - 1), INTEL_OC_WDT_CTRL_REG(oc_wdt)); + + return 0; +} + +static int intel_oc_wdt_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct intel_oc_wdt *oc_wdt; + struct watchdog_device *wdd; + int ret; + + oc_wdt = devm_kzalloc(&pdev->dev, sizeof(*oc_wdt), GFP_KERNEL); + if (!oc_wdt) + return -ENOMEM; + + oc_wdt->ctrl_res = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (!oc_wdt->ctrl_res) { + dev_err(&pdev->dev, "missing I/O resource\n"); + return -ENODEV; + } + + if (!devm_request_region(&pdev->dev, oc_wdt->ctrl_res->start, + resource_size(oc_wdt->ctrl_res), pdev->name)) { + dev_err(dev, "resource %pR already in use, device disabled\n", + oc_wdt->ctrl_res); + return -EBUSY; + } + + wdd = &oc_wdt->wdd; + wdd->min_timeout = INTEL_OC_WDT_MIN_TOV; + wdd->max_timeout = INTEL_OC_WDT_MAX_TOV; + wdd->timeout = INTEL_OC_WDT_DEF_TOV; + wdd->info = &intel_oc_wdt_info; + wdd->ops = &intel_oc_wdt_ops; + wdd->parent = dev; + + watchdog_init_timeout(wdd, heartbeat, dev); + + ret = intel_oc_wdt_setup(oc_wdt); + if (ret) + return ret; + + watchdog_set_drvdata(wdd, oc_wdt); + watchdog_set_nowayout(wdd, nowayout); + watchdog_stop_on_reboot(wdd); + watchdog_stop_on_unregister(wdd); + + return devm_watchdog_register_device(dev, wdd); +} + +static const struct acpi_device_id intel_oc_wdt_match[] = { + { "INT3F0D" }, + { "INTC1099" }, + { }, +}; +MODULE_DEVICE_TABLE(acpi, intel_oc_wdt_match); + +static struct platform_driver intel_oc_wdt_platform_driver = { + .driver = { + .name = DRV_NAME, + .acpi_match_table = intel_oc_wdt_match, + }, + .probe = intel_oc_wdt_probe, +}; + +module_platform_driver(intel_oc_wdt_platform_driver); + +MODULE_AUTHOR("Diogo Ivo <diogo.ivo@siemens.com>"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Intel OC Watchdog driver"); diff --git a/drivers/watchdog/it8712f_wdt.c b/drivers/watchdog/it8712f_wdt.c index 3ce6a58bd81e..b776e6766c9d 100644 --- a/drivers/watchdog/it8712f_wdt.c +++ b/drivers/watchdog/it8712f_wdt.c @@ -341,7 +341,6 @@ static int it8712f_wdt_release(struct inode *inode, struct file *file) static const struct file_operations it8712f_wdt_fops = { .owner = THIS_MODULE, - .llseek = no_llseek, .write = it8712f_wdt_write, .unlocked_ioctl = it8712f_wdt_ioctl, .compat_ioctl = compat_ptr_ioctl, diff --git a/drivers/watchdog/it87_wdt.c b/drivers/watchdog/it87_wdt.c index 3e8c15138edd..a1e23dce8810 100644 --- a/drivers/watchdog/it87_wdt.c +++ b/drivers/watchdog/it87_wdt.c @@ -20,6 +20,8 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include <linux/bits.h> +#include <linux/dmi.h> #include <linux/init.h> #include <linux/io.h> #include <linux/kernel.h> @@ -40,6 +42,7 @@ #define VAL 0x2f /* Logical device Numbers LDN */ +#define EC 0x04 #define GPIO 0x07 /* Configuration Registers and Functions */ @@ -73,6 +76,12 @@ #define IT8784_ID 0x8784 #define IT8786_ID 0x8786 +/* Environment Controller Configuration Registers LDN=0x04 */ +#define SCR1 0xfa + +/* Environment Controller Bits SCR1 */ +#define WDT_PWRGD 0x20 + /* GPIO Configuration Registers LDN=0x07 */ #define WDTCTRL 0x71 #define WDTCFG 0x72 @@ -240,6 +249,21 @@ static int wdt_set_timeout(struct watchdog_device *wdd, unsigned int t) return ret; } +enum { + IT87_WDT_OUTPUT_THROUGH_PWRGD = BIT(0), +}; + +static const struct dmi_system_id it87_quirks[] = { + { + /* Qotom Q30900P (IT8786) */ + .matches = { + DMI_EXACT_MATCH(DMI_BOARD_NAME, "QCML04"), + }, + .driver_data = (void *)IT87_WDT_OUTPUT_THROUGH_PWRGD, + }, + {} +}; + static const struct watchdog_info ident = { .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING, .firmware_version = 1, @@ -261,8 +285,10 @@ static struct watchdog_device wdt_dev = { static int __init it87_wdt_init(void) { + const struct dmi_system_id *dmi_id; u8 chip_rev; u8 ctrl; + int quirks = 0; int rc; rc = superio_enter(); @@ -273,6 +299,10 @@ static int __init it87_wdt_init(void) chip_rev = superio_inb(CHIPREV) & 0x0f; superio_exit(); + dmi_id = dmi_first_match(it87_quirks); + if (dmi_id) + quirks = (long)dmi_id->driver_data; + switch (chip_type) { case IT8702_ID: max_units = 255; @@ -333,6 +363,15 @@ static int __init it87_wdt_init(void) superio_outb(0x00, WDTCTRL); } + if (quirks & IT87_WDT_OUTPUT_THROUGH_PWRGD) { + superio_select(EC); + ctrl = superio_inb(SCR1); + if (!(ctrl & WDT_PWRGD)) { + ctrl |= WDT_PWRGD; + superio_outb(ctrl, SCR1); + } + } + superio_exit(); if (timeout < 1 || timeout > max_units * 60) { @@ -349,10 +388,8 @@ static int __init it87_wdt_init(void) watchdog_stop_on_reboot(&wdt_dev); rc = watchdog_register_device(&wdt_dev); - if (rc) { - pr_err("Cannot register watchdog device (err=%d)\n", rc); + if (rc) return rc; - } pr_info("Chip IT%04x revision %d initialized. timeout=%d sec (nowayout=%d testmode=%d)\n", chip_type, chip_rev, timeout, nowayout, testmode); diff --git a/drivers/watchdog/lenovo_se10_wdt.c b/drivers/watchdog/lenovo_se10_wdt.c new file mode 100644 index 000000000000..cd0500e5080b --- /dev/null +++ b/drivers/watchdog/lenovo_se10_wdt.c @@ -0,0 +1,308 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * WDT driver for Lenovo SE10. + */ + +#include <linux/delay.h> +#include <linux/dmi.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/platform_device.h> +#include <linux/string.h> +#include <linux/types.h> +#include <linux/watchdog.h> + +#define STATUS_PORT 0x6C +#define CMD_PORT 0x6C +#define DATA_PORT 0x68 +#define OUTBUF_FULL 0x01 +#define INBUF_EMPTY 0x02 +#define CFG_LDN 0x07 +#define CFG_BRAM_LDN 0x10 /* for BRAM Base */ +#define CFG_PORT 0x2E +#define CFG_SIZE 2 +#define CMD_SIZE 4 +#define BRAM_SIZE 2 + +#define UNLOCK_KEY 0x87 +#define LOCK_KEY 0xAA + +#define CUS_WDT_SWI 0x1A +#define CUS_WDT_CFG 0x1B +#define CUS_WDT_FEED 0xB0 +#define CUS_WDT_CNT 0xB1 + +#define DRVNAME "lenovo-se10-wdt" + +/*The timeout range is 1-255 seconds*/ +#define MIN_TIMEOUT 1 +#define MAX_TIMEOUT 255 +#define MAX_WAIT 10 + +#define WATCHDOG_TIMEOUT 60 /* 60 sec default timeout */ +static unsigned short bram_base; +static struct platform_device *se10_pdev; + +static int timeout; /* in seconds */ +module_param(timeout, int, 0); +MODULE_PARM_DESC(timeout, + "Watchdog timeout in seconds. 1 <= timeout <= 255, default=" + __MODULE_STRING(WATCHDOG_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 se10_wdt { + struct watchdog_device wdd; +}; + +static int set_bram(unsigned char offset, unsigned char val) +{ + if (!request_muxed_region(bram_base, BRAM_SIZE, DRVNAME)) + return -EBUSY; + outb(offset, bram_base); + outb(val, bram_base + 1); + release_region(bram_base, BRAM_SIZE); + return 0; +} + +static void wait_for_buffer(int condition) +{ + int loop = 0; + + while (1) { + if (inb(STATUS_PORT) & condition || loop > MAX_WAIT) + break; + loop++; + usleep_range(10, 125); + } +} + +static void send_cmd(unsigned char cmd) +{ + wait_for_buffer(INBUF_EMPTY); + outb(cmd, CMD_PORT); + wait_for_buffer(INBUF_EMPTY); +} + +static void lpc_write(unsigned char index, unsigned char data) +{ + outb(index, CFG_PORT); + outb(data, CFG_PORT + 1); +} + +static unsigned char lpc_read(unsigned char index) +{ + outb(index, CFG_PORT); + return inb(CFG_PORT + 1); +} + +static int wdt_start(struct watchdog_device *wdog) +{ + return set_bram(CUS_WDT_SWI, 0x80); +} + +static int wdt_set_timeout(struct watchdog_device *wdog, unsigned int timeout) +{ + wdog->timeout = timeout; + return set_bram(CUS_WDT_CFG, wdog->timeout); +} + +static int wdt_stop(struct watchdog_device *wdog) +{ + return set_bram(CUS_WDT_SWI, 0); +} + +static unsigned int wdt_get_time(struct watchdog_device *wdog) +{ + unsigned char time; + + if (!request_muxed_region(CMD_PORT, CMD_SIZE, DRVNAME)) + return -EBUSY; + send_cmd(CUS_WDT_CNT); + wait_for_buffer(OUTBUF_FULL); + time = inb(DATA_PORT); + release_region(CMD_PORT, CMD_SIZE); + return time; +} + +static int wdt_ping(struct watchdog_device *wdog) +{ + if (!request_muxed_region(CMD_PORT, CMD_SIZE, DRVNAME)) + return -EBUSY; + send_cmd(CUS_WDT_FEED); + release_region(CMD_PORT, CMD_SIZE); + return 0; +} + +static const struct watchdog_info wdt_info = { + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, + .identity = "Lenovo SE10 Watchdog", +}; + +static const struct watchdog_ops se10_wdt_ops = { + .owner = THIS_MODULE, + .start = wdt_start, + .stop = wdt_stop, + .ping = wdt_ping, + .set_timeout = wdt_set_timeout, + .get_timeleft = wdt_get_time, +}; + +static unsigned int get_chipID(void) +{ + unsigned char msb, lsb; + + outb(UNLOCK_KEY, CFG_PORT); + outb(0x01, CFG_PORT); + outb(0x55, CFG_PORT); + outb(0x55, CFG_PORT); + msb = lpc_read(0x20); + lsb = lpc_read(0x21); + outb(LOCK_KEY, CFG_PORT); + return (msb * 256 + lsb); +} + +static int se10_wdt_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct se10_wdt *priv; + unsigned int chip_id; + int ret; + + if (!request_muxed_region(CFG_PORT, CFG_SIZE, DRVNAME)) + return -EBUSY; + + chip_id = get_chipID(); + if (chip_id != 0x5632) { + release_region(CFG_PORT, CFG_SIZE); + return -ENODEV; + } + + lpc_write(CFG_LDN, CFG_BRAM_LDN); + bram_base = (lpc_read(0x60) << 8) | lpc_read(0x61); + release_region(CFG_PORT, CFG_SIZE); + + dev_info(dev, "Found Lenovo SE10 0x%x\n", chip_id); + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + watchdog_set_drvdata(&priv->wdd, priv); + + priv->wdd.parent = dev; + priv->wdd.info = &wdt_info; + priv->wdd.ops = &se10_wdt_ops; + priv->wdd.timeout = WATCHDOG_TIMEOUT; /* Set default timeout */ + priv->wdd.min_timeout = MIN_TIMEOUT; + priv->wdd.max_timeout = MAX_TIMEOUT; + + set_bram(CUS_WDT_CFG, WATCHDOG_TIMEOUT); /* Set time to default */ + + watchdog_init_timeout(&priv->wdd, timeout, dev); + watchdog_set_nowayout(&priv->wdd, nowayout); + watchdog_stop_on_reboot(&priv->wdd); + watchdog_stop_on_unregister(&priv->wdd); + + ret = devm_watchdog_register_device(dev, &priv->wdd); + + dev_dbg(&pdev->dev, "initialized. timeout=%d sec (nowayout=%d)\n", + priv->wdd.timeout, nowayout); + + return ret; +} + +static struct platform_driver se10_wdt_driver = { + .driver = { + .name = DRVNAME, + }, + .probe = se10_wdt_probe, +}; + +static int se10_create_platform_device(const struct dmi_system_id *id) +{ + int err; + + se10_pdev = platform_device_alloc("lenovo-se10-wdt", -1); + if (!se10_pdev) + return -ENOMEM; + + err = platform_device_add(se10_pdev); + if (err) + platform_device_put(se10_pdev); + + return err; +} + +static const struct dmi_system_id se10_dmi_table[] __initconst = { + { + .ident = "LENOVO-SE10", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "12NH"), + }, + .callback = se10_create_platform_device, + }, + { + .ident = "LENOVO-SE10", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "12NJ"), + }, + .callback = se10_create_platform_device, + }, + { + .ident = "LENOVO-SE10", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "12NK"), + }, + .callback = se10_create_platform_device, + }, + { + .ident = "LENOVO-SE10", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "12NL"), + }, + .callback = se10_create_platform_device, + }, + { + .ident = "LENOVO-SE10", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "12NM"), + }, + .callback = se10_create_platform_device, + }, + {} +}; +MODULE_DEVICE_TABLE(dmi, se10_dmi_table); + +static int __init se10_wdt_init(void) +{ + if (!dmi_check_system(se10_dmi_table)) + return -ENODEV; + + return platform_driver_register(&se10_wdt_driver); +} + +static void __exit se10_wdt_exit(void) +{ + if (se10_pdev) + platform_device_unregister(se10_pdev); + platform_driver_unregister(&se10_wdt_driver); +} + +module_init(se10_wdt_init); +module_exit(se10_wdt_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("David Ober<dober@lenovo.com>"); +MODULE_AUTHOR("Mark Pearson <mpearson-lenovo@squebb.ca>"); +MODULE_DESCRIPTION("WDT driver for Lenovo SE10"); diff --git a/drivers/watchdog/lenovo_se30_wdt.c b/drivers/watchdog/lenovo_se30_wdt.c new file mode 100644 index 000000000000..1c73bb7eeeee --- /dev/null +++ b/drivers/watchdog/lenovo_se30_wdt.c @@ -0,0 +1,396 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * WDT driver for Lenovo SE30 device + */ + +#define dev_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/io.h> +#include <linux/dmi.h> +#include <linux/delay.h> +#include <linux/iommu.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/platform_device.h> +#include <linux/watchdog.h> + +#define IOREGION_OFFSET 4 /* Use EC port 1 */ +#define IOREGION_LENGTH 4 + +#define WATCHDOG_TIMEOUT 60 + +#define MIN_TIMEOUT 1 +#define MAX_TIMEOUT 255 +#define MAX_WAIT 10 + +static int timeout; /* in seconds */ +module_param(timeout, int, 0); +MODULE_PARM_DESC(timeout, + "Watchdog timeout in seconds. 1 <= timeout <= 255, default=" + __MODULE_STRING(WATCHDOG_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) ")"); + +#define LNV_SE30_NAME "lenovo-se30-wdt" +#define LNV_SE30_ID 0x0110 +#define CHIPID_MASK 0xFFF0 + +#define CHIPID_REG 0x20 +#define SIO_REG 0x2e +#define LDN_REG 0x07 +#define UNLOCK_KEY 0x87 +#define LOCK_KEY 0xAA +#define LD_NUM_SHM 0x0F +#define LD_BASE_ADDR 0xF8 + +#define WDT_MODULE 0x10 +#define WDT_CFG_INDEX 0x15 /* WD configuration register */ +#define WDT_CNT_INDEX 0x16 /* WD timer count register */ +#define WDT_CFG_RESET 0x2 + +/* Host Interface WIN2 offset definition */ +#define SHM_WIN_SIZE 0xFF +#define SHM_WIN_MOD_OFFSET 0x01 +#define SHM_WIN_CMD_OFFSET 0x02 +#define SHM_WIN_SEL_OFFSET 0x03 +#define SHM_WIN_CTL_OFFSET 0x04 +#define VAL_SHM_WIN_CTRL_WR 0x40 +#define VAL_SHM_WIN_CTRL_RD 0x80 +#define SHM_WIN_ID_OFFSET 0x08 +#define SHM_WIN_DAT_OFFSET 0x10 + +struct nct6692_reg { + unsigned char mod; + unsigned char cmd; + unsigned char sel; + unsigned int idx; +}; + +/* Watchdog is based on NCT6692 device */ +struct lenovo_se30_wdt { + unsigned char __iomem *shm_base_addr; + struct nct6692_reg wdt_cfg; + struct nct6692_reg wdt_cnt; + struct watchdog_device wdt; +}; + +static inline void superio_outb(int ioreg, int reg, int val) +{ + outb(reg, ioreg); + outb(val, ioreg + 1); +} + +static inline int superio_inb(int ioreg, int reg) +{ + outb(reg, ioreg); + return inb(ioreg + 1); +} + +static inline int superio_enter(int key, int addr, const char *name) +{ + if (!request_muxed_region(addr, 2, name)) { + pr_err("I/O address 0x%04x already in use\n", addr); + return -EBUSY; + } + outb(key, addr); /* Enter extended function mode */ + outb(key, addr); /* Again according to manual */ + + return 0; +} + +static inline void superio_exit(int key, int addr) +{ + outb(key, addr); /* Leave extended function mode */ + release_region(addr, 2); +} + +static int shm_get_ready(unsigned char __iomem *shm_base_addr, + const struct nct6692_reg *reg) +{ + unsigned char pre_id, new_id; + int loop = 0; + + iowrite8(reg->mod, shm_base_addr + SHM_WIN_MOD_OFFSET); + iowrite8(reg->cmd, shm_base_addr + SHM_WIN_CMD_OFFSET); + iowrite8(reg->sel, shm_base_addr + SHM_WIN_SEL_OFFSET); + + pre_id = ioread8(shm_base_addr + SHM_WIN_ID_OFFSET); + iowrite8(VAL_SHM_WIN_CTRL_RD, shm_base_addr + SHM_WIN_CTL_OFFSET); + + /* Loop checking when interface is ready */ + while (loop < MAX_WAIT) { + new_id = ioread8(shm_base_addr + SHM_WIN_ID_OFFSET); + if (new_id != pre_id) + return 0; + loop++; + usleep_range(10, 125); + } + return -ETIMEDOUT; +} + +static int read_shm_win(unsigned char __iomem *shm_base_addr, + const struct nct6692_reg *reg, + unsigned char idx_offset, + unsigned char *data) +{ + int err = shm_get_ready(shm_base_addr, reg); + + if (err) + return err; + *data = ioread8(shm_base_addr + SHM_WIN_DAT_OFFSET + reg->idx + idx_offset); + return 0; +} + +static int write_shm_win(unsigned char __iomem *shm_base_addr, + const struct nct6692_reg *reg, + unsigned char idx_offset, + unsigned char val) +{ + int err = shm_get_ready(shm_base_addr, reg); + + if (err) + return err; + iowrite8(val, shm_base_addr + SHM_WIN_DAT_OFFSET + reg->idx + idx_offset); + iowrite8(VAL_SHM_WIN_CTRL_WR, shm_base_addr + SHM_WIN_CTL_OFFSET); + err = shm_get_ready(shm_base_addr, reg); + return err; +} + +static int lenovo_se30_wdt_enable(struct lenovo_se30_wdt *data, unsigned int timeout) +{ + if (timeout) { + int err = write_shm_win(data->shm_base_addr, &data->wdt_cfg, 0, WDT_CFG_RESET); + + if (err) + return err; + } + return write_shm_win(data->shm_base_addr, &data->wdt_cnt, 0, timeout); +} + +static int lenovo_se30_wdt_start(struct watchdog_device *wdog) +{ + struct lenovo_se30_wdt *data = watchdog_get_drvdata(wdog); + + return lenovo_se30_wdt_enable(data, wdog->timeout); +} + +static int lenovo_se30_wdt_stop(struct watchdog_device *wdog) +{ + struct lenovo_se30_wdt *data = watchdog_get_drvdata(wdog); + + return lenovo_se30_wdt_enable(data, 0); +} + +static unsigned int lenovo_se30_wdt_get_timeleft(struct watchdog_device *wdog) +{ + struct lenovo_se30_wdt *data = watchdog_get_drvdata(wdog); + unsigned char timeleft; + int err; + + err = read_shm_win(data->shm_base_addr, &data->wdt_cnt, 0, &timeleft); + if (err) + return 0; + return timeleft; +} + +static int lenovo_se30_wdt_ping(struct watchdog_device *wdt) +{ + struct lenovo_se30_wdt *data = watchdog_get_drvdata(wdt); + int err = 0; + + /* + * Device does not support refreshing WDT_TIMER_REG register when + * the watchdog is active. Need to disable, feed and enable again + */ + err = lenovo_se30_wdt_enable(data, 0); + if (err) + return err; + + err = write_shm_win(data->shm_base_addr, &data->wdt_cnt, 0, wdt->timeout); + if (!err) + err = lenovo_se30_wdt_enable(data, wdt->timeout); + + return err; +} + +static const struct watchdog_info lenovo_se30_wdt_info = { + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | + WDIOF_MAGICCLOSE, + .identity = "Lenovo SE30 watchdog", +}; + +static const struct watchdog_ops lenovo_se30_wdt_ops = { + .owner = THIS_MODULE, + .start = lenovo_se30_wdt_start, + .stop = lenovo_se30_wdt_stop, + .ping = lenovo_se30_wdt_ping, + .get_timeleft = lenovo_se30_wdt_get_timeleft, +}; + +static int lenovo_se30_wdt_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct lenovo_se30_wdt *priv; + unsigned long base_phys; + unsigned short val; + int err; + + err = superio_enter(UNLOCK_KEY, SIO_REG, LNV_SE30_NAME); + if (err) + return err; + + val = superio_inb(SIO_REG, CHIPID_REG) << 8; + val |= superio_inb(SIO_REG, CHIPID_REG + 1); + + if ((val & CHIPID_MASK) != LNV_SE30_ID) { + superio_exit(LOCK_KEY, SIO_REG); + return -ENODEV; + } + + superio_outb(SIO_REG, LDN_REG, LD_NUM_SHM); + base_phys = (superio_inb(SIO_REG, LD_BASE_ADDR) | + (superio_inb(SIO_REG, LD_BASE_ADDR + 1) << 8) | + (superio_inb(SIO_REG, LD_BASE_ADDR + 2) << 16) | + (superio_inb(SIO_REG, LD_BASE_ADDR + 3) << 24)) & + 0xFFFFFFFF; + + superio_exit(LOCK_KEY, SIO_REG); + if (base_phys == 0xFFFFFFFF || base_phys == 0) + return -ENODEV; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + if (!devm_request_mem_region(dev, base_phys, SHM_WIN_SIZE, LNV_SE30_NAME)) + return -EBUSY; + + priv->shm_base_addr = devm_ioremap(dev, base_phys, SHM_WIN_SIZE); + if (!priv->shm_base_addr) + return -ENOMEM; + + priv->wdt_cfg.mod = WDT_MODULE; + priv->wdt_cfg.idx = WDT_CFG_INDEX; + priv->wdt_cnt.mod = WDT_MODULE; + priv->wdt_cnt.idx = WDT_CNT_INDEX; + + priv->wdt.ops = &lenovo_se30_wdt_ops; + priv->wdt.info = &lenovo_se30_wdt_info; + priv->wdt.timeout = WATCHDOG_TIMEOUT; /* Set default timeout */ + priv->wdt.min_timeout = MIN_TIMEOUT; + priv->wdt.max_timeout = MAX_TIMEOUT; + priv->wdt.parent = dev; + + watchdog_init_timeout(&priv->wdt, timeout, dev); + watchdog_set_drvdata(&priv->wdt, priv); + watchdog_set_nowayout(&priv->wdt, nowayout); + watchdog_stop_on_reboot(&priv->wdt); + watchdog_stop_on_unregister(&priv->wdt); + + return devm_watchdog_register_device(dev, &priv->wdt); +} + +static struct platform_device *pdev; + +static struct platform_driver lenovo_se30_wdt_driver = { + .driver = { + .name = LNV_SE30_NAME, + }, + .probe = lenovo_se30_wdt_probe, +}; + +static int lenovo_se30_create_platform_device(const struct dmi_system_id *id) +{ + int err; + + pdev = platform_device_alloc(LNV_SE30_NAME, -1); + if (!pdev) + return -ENOMEM; + + err = platform_device_add(pdev); + if (err) + platform_device_put(pdev); + + return err; +} + +static const struct dmi_system_id lenovo_se30_wdt_dmi_table[] __initconst = { + { + .ident = "LENOVO-SE30", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "11NA"), + }, + .callback = lenovo_se30_create_platform_device, + }, + { + .ident = "LENOVO-SE30", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "11NB"), + }, + .callback = lenovo_se30_create_platform_device, + }, + { + .ident = "LENOVO-SE30", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "11NC"), + }, + .callback = lenovo_se30_create_platform_device, + }, + { + .ident = "LENOVO-SE30", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "11NH"), + }, + .callback = lenovo_se30_create_platform_device, + }, + { + .ident = "LENOVO-SE30", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "11NJ"), + }, + .callback = lenovo_se30_create_platform_device, + }, + { + .ident = "LENOVO-SE30", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "11NK"), + }, + .callback = lenovo_se30_create_platform_device, + }, + {} +}; +MODULE_DEVICE_TABLE(dmi, lenovo_se30_wdt_dmi_table); + +static int __init lenovo_se30_wdt_init(void) +{ + if (!dmi_check_system(lenovo_se30_wdt_dmi_table)) + return -ENODEV; + + return platform_driver_register(&lenovo_se30_wdt_driver); +} + +static void __exit lenovo_se30_wdt_exit(void) +{ + if (pdev) + platform_device_unregister(pdev); + platform_driver_unregister(&lenovo_se30_wdt_driver); +} + +module_init(lenovo_se30_wdt_init); +module_exit(lenovo_se30_wdt_exit); + +MODULE_AUTHOR("Mark Pearson <mpearson-lenovo@squebb.ca>"); +MODULE_AUTHOR("David Ober <dober@lenovo.com>"); +MODULE_DESCRIPTION("Lenovo SE30 watchdog driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/watchdog/lpc18xx_wdt.c b/drivers/watchdog/lpc18xx_wdt.c index 19535f4a2fd2..28e3fc0df4c6 100644 --- a/drivers/watchdog/lpc18xx_wdt.c +++ b/drivers/watchdog/lpc18xx_wdt.c @@ -135,7 +135,7 @@ static int lpc18xx_wdt_start(struct watchdog_device *wdt_dev) unsigned int val; if (timer_pending(&lpc18xx_wdt->timer)) - del_timer(&lpc18xx_wdt->timer); + timer_delete(&lpc18xx_wdt->timer); val = readl(lpc18xx_wdt->base + LPC18XX_WDT_MOD); val |= LPC18XX_WDT_MOD_WDEN; @@ -266,7 +266,7 @@ static void lpc18xx_wdt_remove(struct platform_device *pdev) struct lpc18xx_wdt_dev *lpc18xx_wdt = platform_get_drvdata(pdev); dev_warn(&pdev->dev, "I quit now, hardware will probably reboot!\n"); - del_timer_sync(&lpc18xx_wdt->timer); + timer_delete_sync(&lpc18xx_wdt->timer); } static const struct of_device_id lpc18xx_wdt_match[] = { @@ -281,7 +281,7 @@ static struct platform_driver lpc18xx_wdt_driver = { .of_match_table = lpc18xx_wdt_match, }, .probe = lpc18xx_wdt_probe, - .remove_new = lpc18xx_wdt_remove, + .remove = lpc18xx_wdt_remove, }; module_platform_driver(lpc18xx_wdt_driver); diff --git a/drivers/watchdog/m54xx_wdt.c b/drivers/watchdog/m54xx_wdt.c index 062ea3e6497e..26bd073bd375 100644 --- a/drivers/watchdog/m54xx_wdt.c +++ b/drivers/watchdog/m54xx_wdt.c @@ -179,7 +179,6 @@ static int m54xx_wdt_release(struct inode *inode, struct file *file) static const struct file_operations m54xx_wdt_fops = { .owner = THIS_MODULE, - .llseek = no_llseek, .write = m54xx_wdt_write, .unlocked_ioctl = m54xx_wdt_ioctl, .compat_ioctl = compat_ptr_ioctl, diff --git a/drivers/watchdog/machzwd.c b/drivers/watchdog/machzwd.c index 73f2221f6222..0ae8e5bc10ae 100644 --- a/drivers/watchdog/machzwd.c +++ b/drivers/watchdog/machzwd.c @@ -189,7 +189,7 @@ static void zf_timer_off(void) unsigned long flags; /* stop internal ping */ - del_timer_sync(&zf_timer); + timer_delete_sync(&zf_timer); spin_lock_irqsave(&zf_port_lock, flags); /* stop watchdog timer */ @@ -337,7 +337,7 @@ static int zf_close(struct inode *inode, struct file *file) if (zf_expect_close == 42) zf_timer_off(); else { - del_timer(&zf_timer); + timer_delete(&zf_timer); pr_err("device file closed unexpectedly. Will not stop the WDT!\n"); } clear_bit(0, &zf_is_open); @@ -359,7 +359,6 @@ static int zf_notify_sys(struct notifier_block *this, unsigned long code, static const struct file_operations zf_fops = { .owner = THIS_MODULE, - .llseek = no_llseek, .write = zf_write, .unlocked_ioctl = zf_ioctl, .compat_ioctl = compat_ptr_ioctl, diff --git a/drivers/watchdog/marvell_gti_wdt.c b/drivers/watchdog/marvell_gti_wdt.c index 098bb141a521..298089d45ab8 100644 --- a/drivers/watchdog/marvell_gti_wdt.c +++ b/drivers/watchdog/marvell_gti_wdt.c @@ -285,8 +285,8 @@ static int gti_wdt_probe(struct platform_device *pdev) } wdog_dev = &priv->wdev; - wdog_dev->info = >i_wdt_ident, - wdog_dev->ops = >i_wdt_ops, + wdog_dev->info = >i_wdt_ident; + wdog_dev->ops = >i_wdt_ops; wdog_dev->parent = dev; /* * Watchdog counter is 24 bit where lower 8 bits are zeros diff --git a/drivers/watchdog/max77620_wdt.c b/drivers/watchdog/max77620_wdt.c index 33835c0b06de..d3ced783a5f4 100644 --- a/drivers/watchdog/max77620_wdt.c +++ b/drivers/watchdog/max77620_wdt.c @@ -25,7 +25,6 @@ static bool nowayout = WATCHDOG_NOWAYOUT; /** * struct max77620_variant - Data specific to a chip variant - * @wdt_info: watchdog descriptor * @reg_onoff_cnfg2: ONOFF_CNFG2 register offset * @reg_cnfg_glbl2: CNFG_GLBL2 register offset * @reg_cnfg_glbl3: CNFG_GLBL3 register offset diff --git a/drivers/watchdog/menz69_wdt.c b/drivers/watchdog/menz69_wdt.c index c7de30270043..6e5e4e5c0b56 100644 --- a/drivers/watchdog/menz69_wdt.c +++ b/drivers/watchdog/menz69_wdt.c @@ -161,6 +161,7 @@ static struct mcb_driver men_z069_driver = { module_mcb_driver(men_z069_driver); MODULE_AUTHOR("Johannes Thumshirn <jth@kernel.org>"); +MODULE_DESCRIPTION("Watchdog driver for the MEN z069 IP-Core"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("mcb:16z069"); -MODULE_IMPORT_NS(MCB); +MODULE_IMPORT_NS("MCB"); diff --git a/drivers/watchdog/mixcomwd.c b/drivers/watchdog/mixcomwd.c index d387bad377c4..1ecd5c48a005 100644 --- a/drivers/watchdog/mixcomwd.c +++ b/drivers/watchdog/mixcomwd.c @@ -141,7 +141,7 @@ static int mixcomwd_open(struct inode *inode, struct file *file) __module_get(THIS_MODULE); else { if (mixcomwd_timer_alive) { - del_timer(&mixcomwd_timer); + timer_delete(&mixcomwd_timer); mixcomwd_timer_alive = 0; } } @@ -224,7 +224,6 @@ static long mixcomwd_ioctl(struct file *file, static const struct file_operations mixcomwd_fops = { .owner = THIS_MODULE, - .llseek = no_llseek, .write = mixcomwd_write, .unlocked_ioctl = mixcomwd_ioctl, .compat_ioctl = compat_ptr_ioctl, @@ -296,7 +295,7 @@ static void __exit mixcomwd_exit(void) if (!nowayout) { if (mixcomwd_timer_alive) { pr_warn("I quit now, hardware will probably reboot!\n"); - del_timer_sync(&mixcomwd_timer); + timer_delete_sync(&mixcomwd_timer); mixcomwd_timer_alive = 0; } } diff --git a/drivers/watchdog/mtk_wdt.c b/drivers/watchdog/mtk_wdt.c index c35f85ce8d69..91d110646e16 100644 --- a/drivers/watchdog/mtk_wdt.c +++ b/drivers/watchdog/mtk_wdt.c @@ -10,6 +10,7 @@ */ #include <dt-bindings/reset/mt2712-resets.h> +#include <dt-bindings/reset/mediatek,mt6735-wdt.h> #include <dt-bindings/reset/mediatek,mt6795-resets.h> #include <dt-bindings/reset/mt7986-resets.h> #include <dt-bindings/reset/mt8183-resets.h> @@ -87,6 +88,10 @@ static const struct mtk_wdt_data mt2712_data = { .toprgu_sw_rst_num = MT2712_TOPRGU_SW_RST_NUM, }; +static const struct mtk_wdt_data mt6735_data = { + .toprgu_sw_rst_num = MT6735_TOPRGU_RST_NUM, +}; + static const struct mtk_wdt_data mt6795_data = { .toprgu_sw_rst_num = MT6795_TOPRGU_SW_RST_NUM, }; @@ -225,9 +230,15 @@ static int mtk_wdt_restart(struct watchdog_device *wdt_dev, { struct mtk_wdt_dev *mtk_wdt = watchdog_get_drvdata(wdt_dev); void __iomem *wdt_base; + u32 reg; wdt_base = mtk_wdt->wdt_base; + /* Enable reset in order to issue a system reset instead of an IRQ */ + reg = readl(wdt_base + WDT_MODE); + reg &= ~WDT_MODE_IRQ_EN; + writel(reg | WDT_MODE_KEY, wdt_base + WDT_MODE); + while (1) { writel(WDT_SWRST_KEY, wdt_base + WDT_SWRST); mdelay(5); @@ -483,6 +494,7 @@ static int mtk_wdt_resume(struct device *dev) static const struct of_device_id mtk_wdt_dt_ids[] = { { .compatible = "mediatek,mt2712-wdt", .data = &mt2712_data }, { .compatible = "mediatek,mt6589-wdt" }, + { .compatible = "mediatek,mt6735-wdt", .data = &mt6735_data }, { .compatible = "mediatek,mt6795-wdt", .data = &mt6795_data }, { .compatible = "mediatek,mt7986-wdt", .data = &mt7986_data }, { .compatible = "mediatek,mt7988-wdt", .data = &mt7988_data }, diff --git a/drivers/watchdog/mtx-1_wdt.c b/drivers/watchdog/mtx-1_wdt.c index 152e41ecbb14..f75426cfa425 100644 --- a/drivers/watchdog/mtx-1_wdt.c +++ b/drivers/watchdog/mtx-1_wdt.c @@ -177,7 +177,6 @@ static ssize_t mtx1_wdt_write(struct file *file, const char *buf, static const struct file_operations mtx1_wdt_fops = { .owner = THIS_MODULE, - .llseek = no_llseek, .unlocked_ioctl = mtx1_wdt_ioctl, .compat_ioctl = compat_ptr_ioctl, .open = mtx1_wdt_open, @@ -234,9 +233,8 @@ static void mtx1_wdt_remove(struct platform_device *pdev) static struct platform_driver mtx1_wdt_driver = { .probe = mtx1_wdt_probe, - .remove_new = mtx1_wdt_remove, + .remove = mtx1_wdt_remove, .driver.name = "mtx1-wdt", - .driver.owner = THIS_MODULE, }; module_platform_driver(mtx1_wdt_driver); diff --git a/drivers/watchdog/nic7018_wdt.c b/drivers/watchdog/nic7018_wdt.c index c3f0a4926667..44b5298f599a 100644 --- a/drivers/watchdog/nic7018_wdt.c +++ b/drivers/watchdog/nic7018_wdt.c @@ -3,12 +3,13 @@ * Copyright (C) 2016 National Instruments Corp. */ -#include <linux/acpi.h> #include <linux/bitops.h> #include <linux/device.h> #include <linux/io.h> +#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/platform_device.h> +#include <linux/types.h> #include <linux/watchdog.h> #define LOCK 0xA5 @@ -229,17 +230,17 @@ static void nic7018_remove(struct platform_device *pdev) } static const struct acpi_device_id nic7018_device_ids[] = { - {"NIC7018", 0}, - {"", 0}, + { "NIC7018" }, + { } }; MODULE_DEVICE_TABLE(acpi, nic7018_device_ids); static struct platform_driver watchdog_driver = { .probe = nic7018_probe, - .remove_new = nic7018_remove, + .remove = nic7018_remove, .driver = { .name = KBUILD_MODNAME, - .acpi_match_table = ACPI_PTR(nic7018_device_ids), + .acpi_match_table = nic7018_device_ids, }, }; diff --git a/drivers/watchdog/npcm_wdt.c b/drivers/watchdog/npcm_wdt.c index a5dd1c230137..e62ea054bc61 100644 --- a/drivers/watchdog/npcm_wdt.c +++ b/drivers/watchdog/npcm_wdt.c @@ -68,8 +68,7 @@ static int npcm_wdt_start(struct watchdog_device *wdd) struct npcm_wdt *wdt = to_npcm_wdt(wdd); u32 val; - if (wdt->clk) - clk_prepare_enable(wdt->clk); + clk_prepare_enable(wdt->clk); if (wdd->timeout < 2) val = 0x800; @@ -105,8 +104,7 @@ static int npcm_wdt_stop(struct watchdog_device *wdd) writel(0, wdt->reg); - if (wdt->clk) - clk_disable_unprepare(wdt->clk); + clk_disable_unprepare(wdt->clk); return 0; } @@ -156,8 +154,7 @@ static int npcm_wdt_restart(struct watchdog_device *wdd, struct npcm_wdt *wdt = to_npcm_wdt(wdd); /* For reset, we start the WDT clock and leave it running. */ - if (wdt->clk) - clk_prepare_enable(wdt->clk); + clk_prepare_enable(wdt->clk); writel(NPCM_WTR | NPCM_WTRE | NPCM_WTE, wdt->reg); udelay(1000); diff --git a/drivers/watchdog/nv_tco.c b/drivers/watchdog/nv_tco.c index ac4a9c16341d..f16cee5173d5 100644 --- a/drivers/watchdog/nv_tco.c +++ b/drivers/watchdog/nv_tco.c @@ -264,7 +264,6 @@ static long nv_tco_ioctl(struct file *file, unsigned int cmd, static const struct file_operations nv_tco_fops = { .owner = THIS_MODULE, - .llseek = no_llseek, .write = nv_tco_write, .unlocked_ioctl = nv_tco_ioctl, .compat_ioctl = compat_ptr_ioctl, @@ -467,7 +466,7 @@ static void nv_tco_shutdown(struct platform_device *dev) static struct platform_driver nv_tco_driver = { .probe = nv_tco_init, - .remove_new = nv_tco_remove, + .remove = nv_tco_remove, .shutdown = nv_tco_shutdown, .driver = { .name = TCO_MODULE_NAME, diff --git a/drivers/watchdog/octeon-wdt-main.c b/drivers/watchdog/octeon-wdt-main.c index 0fe71f7e66d5..0615bb816082 100644 --- a/drivers/watchdog/octeon-wdt-main.c +++ b/drivers/watchdog/octeon-wdt-main.c @@ -381,11 +381,7 @@ static int octeon_wdt_cpu_online(unsigned int cpu) /* Must set the irq affinity here */ if (octeon_has_feature(OCTEON_FEATURE_CIU3)) { - cpumask_t mask; - - cpumask_clear(&mask); - cpumask_set_cpu(cpu, &mask); - irq_set_affinity(irq, &mask); + irq_set_affinity(irq, cpumask_of(cpu)); } cpumask_set_cpu(cpu, &irq_enabled_cpus); @@ -563,10 +559,8 @@ static int __init octeon_wdt_init(void) watchdog_set_nowayout(&octeon_wdt, nowayout); ret = watchdog_register_device(&octeon_wdt); - if (ret) { - pr_err("watchdog_register_device() failed: %d\n", ret); + if (ret) return ret; - } if (disable) { pr_notice("disabled\n"); diff --git a/drivers/watchdog/omap_wdt.c b/drivers/watchdog/omap_wdt.c index a7a12f2fe9de..d523428a8d22 100644 --- a/drivers/watchdog/omap_wdt.c +++ b/drivers/watchdog/omap_wdt.c @@ -357,7 +357,7 @@ MODULE_DEVICE_TABLE(of, omap_wdt_of_match); static struct platform_driver omap_wdt_driver = { .probe = omap_wdt_probe, - .remove_new = omap_wdt_remove, + .remove = omap_wdt_remove, .shutdown = omap_wdt_shutdown, .suspend = pm_ptr(omap_wdt_suspend), .resume = pm_ptr(omap_wdt_resume), @@ -370,5 +370,6 @@ static struct platform_driver omap_wdt_driver = { module_platform_driver(omap_wdt_driver); MODULE_AUTHOR("George G. Davis"); +MODULE_DESCRIPTION("Driver for the TI OMAP 16xx/24xx/34xx 32KHz (non-secure) watchdog"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:omap_wdt"); diff --git a/drivers/watchdog/orion_wdt.c b/drivers/watchdog/orion_wdt.c index 1fe583e8a95b..0e145f762f6f 100644 --- a/drivers/watchdog/orion_wdt.c +++ b/drivers/watchdog/orion_wdt.c @@ -665,7 +665,7 @@ static void orion_wdt_shutdown(struct platform_device *pdev) static struct platform_driver orion_wdt_driver = { .probe = orion_wdt_probe, - .remove_new = orion_wdt_remove, + .remove = orion_wdt_remove, .shutdown = orion_wdt_shutdown, .driver = { .name = "orion_wdt", diff --git a/drivers/watchdog/pc87413_wdt.c b/drivers/watchdog/pc87413_wdt.c index c7f745caf203..fbf835d112b8 100644 --- a/drivers/watchdog/pc87413_wdt.c +++ b/drivers/watchdog/pc87413_wdt.c @@ -470,7 +470,6 @@ static int pc87413_notify_sys(struct notifier_block *this, static const struct file_operations pc87413_fops = { .owner = THIS_MODULE, - .llseek = no_llseek, .write = pc87413_write, .unlocked_ioctl = pc87413_ioctl, .compat_ioctl = compat_ptr_ioctl, diff --git a/drivers/watchdog/pcwd.c b/drivers/watchdog/pcwd.c index a793b03a785d..d4ea7d6ccd6a 100644 --- a/drivers/watchdog/pcwd.c +++ b/drivers/watchdog/pcwd.c @@ -432,7 +432,7 @@ static int pcwd_stop(void) int stat_reg; /* Stop the timer */ - del_timer(&pcwd_private.timer); + timer_delete(&pcwd_private.timer); /* Disable the board */ if (pcwd_private.revision == PCWD_REVISION_C) { @@ -749,7 +749,6 @@ static int pcwd_temp_close(struct inode *inode, struct file *file) static const struct file_operations pcwd_fops = { .owner = THIS_MODULE, - .llseek = no_llseek, .write = pcwd_write, .unlocked_ioctl = pcwd_ioctl, .compat_ioctl = compat_ptr_ioctl, @@ -765,7 +764,6 @@ static struct miscdevice pcwd_miscdev = { static const struct file_operations pcwd_temp_fops = { .owner = THIS_MODULE, - .llseek = no_llseek, .read = pcwd_temp_read, .open = pcwd_temp_open, .release = pcwd_temp_close, @@ -835,7 +833,7 @@ static int pcwd_isa_match(struct device *dev, unsigned int id) port0 = inb_p(base_addr); port1 = inb_p(base_addr + 1); - /* Has either hearbeat bit changed? */ + /* Has either heartbeat bit changed? */ if ((port0 ^ last_port0) & WD_HRTBT || (port1 ^ last_port1) & WD_REVC_HRBT) { retval = 1; diff --git a/drivers/watchdog/pcwd_pci.c b/drivers/watchdog/pcwd_pci.c index 54d86fcb1837..a489b426f2ba 100644 --- a/drivers/watchdog/pcwd_pci.c +++ b/drivers/watchdog/pcwd_pci.c @@ -643,7 +643,6 @@ static int pcipcwd_notify_sys(struct notifier_block *this, unsigned long code, static const struct file_operations pcipcwd_fops = { .owner = THIS_MODULE, - .llseek = no_llseek, .write = pcipcwd_write, .unlocked_ioctl = pcipcwd_ioctl, .compat_ioctl = compat_ptr_ioctl, @@ -659,7 +658,6 @@ static struct miscdevice pcipcwd_miscdev = { static const struct file_operations pcipcwd_temp_fops = { .owner = THIS_MODULE, - .llseek = no_llseek, .read = pcipcwd_temp_read, .open = pcipcwd_temp_open, .release = pcipcwd_temp_release, diff --git a/drivers/watchdog/pcwd_usb.c b/drivers/watchdog/pcwd_usb.c index 8202f0a6b093..b636650b714b 100644 --- a/drivers/watchdog/pcwd_usb.c +++ b/drivers/watchdog/pcwd_usb.c @@ -549,7 +549,6 @@ static int usb_pcwd_notify_sys(struct notifier_block *this, unsigned long code, static const struct file_operations usb_pcwd_fops = { .owner = THIS_MODULE, - .llseek = no_llseek, .write = usb_pcwd_write, .unlocked_ioctl = usb_pcwd_ioctl, .compat_ioctl = compat_ptr_ioctl, @@ -565,7 +564,6 @@ static struct miscdevice usb_pcwd_miscdev = { static const struct file_operations usb_pcwd_temperature_fops = { .owner = THIS_MODULE, - .llseek = no_llseek, .read = usb_pcwd_temperature_read, .open = usb_pcwd_temperature_open, .release = usb_pcwd_temperature_release, @@ -581,7 +579,7 @@ static struct notifier_block usb_pcwd_notifier = { .notifier_call = usb_pcwd_notify_sys, }; -/** +/* * usb_pcwd_delete */ static inline void usb_pcwd_delete(struct usb_pcwd_private *usb_pcwd) @@ -592,7 +590,7 @@ static inline void usb_pcwd_delete(struct usb_pcwd_private *usb_pcwd) kfree(usb_pcwd); } -/** +/* * usb_pcwd_probe * * Called by the usb core when a new device is connected that it thinks @@ -760,7 +758,7 @@ error: } -/** +/* * usb_pcwd_disconnect * * Called by the usb core when the device is removed from the system. diff --git a/drivers/watchdog/pika_wdt.c b/drivers/watchdog/pika_wdt.c index 782b8c23d99c..87b8988d2520 100644 --- a/drivers/watchdog/pika_wdt.c +++ b/drivers/watchdog/pika_wdt.c @@ -129,7 +129,7 @@ static int pikawdt_release(struct inode *inode, struct file *file) { /* stop internal ping */ if (!pikawdt_private.expect_close) - del_timer(&pikawdt_private.timer); + timer_delete(&pikawdt_private.timer); clear_bit(0, &pikawdt_private.open); pikawdt_private.expect_close = 0; @@ -209,7 +209,6 @@ static long pikawdt_ioctl(struct file *file, static const struct file_operations pikawdt_fops = { .owner = THIS_MODULE, - .llseek = no_llseek, .open = pikawdt_open, .release = pikawdt_release, .write = pikawdt_write, diff --git a/drivers/watchdog/pm8916_wdt.c b/drivers/watchdog/pm8916_wdt.c index f3fcbeb0852c..007ed139ab96 100644 --- a/drivers/watchdog/pm8916_wdt.c +++ b/drivers/watchdog/pm8916_wdt.c @@ -218,7 +218,7 @@ static int pm8916_wdt_probe(struct platform_device *pdev) return err; } - wdt->wdev.ops = &pm8916_wdt_ops, + wdt->wdev.ops = &pm8916_wdt_ops; wdt->wdev.parent = dev; wdt->wdev.min_timeout = PM8916_WDT_MIN_TIMEOUT; wdt->wdev.max_timeout = PM8916_WDT_MAX_TIMEOUT; diff --git a/drivers/watchdog/pretimeout_noop.c b/drivers/watchdog/pretimeout_noop.c index 4799551dd784..74ec02b9ffca 100644 --- a/drivers/watchdog/pretimeout_noop.c +++ b/drivers/watchdog/pretimeout_noop.c @@ -11,7 +11,7 @@ /** * pretimeout_noop - No operation on watchdog pretimeout event - * @wdd - watchdog_device + * @wdd: watchdog_device * * This function prints a message about pretimeout to kernel log. */ diff --git a/drivers/watchdog/pretimeout_panic.c b/drivers/watchdog/pretimeout_panic.c index 2cc3c41d2be5..8c3ac674dc45 100644 --- a/drivers/watchdog/pretimeout_panic.c +++ b/drivers/watchdog/pretimeout_panic.c @@ -11,7 +11,7 @@ /** * pretimeout_panic - Panic on watchdog pretimeout event - * @wdd - watchdog_device + * @wdd: watchdog_device * * Panic, watchdog has not been fed till pretimeout event. */ diff --git a/drivers/watchdog/qcom-wdt.c b/drivers/watchdog/qcom-wdt.c index 006f9c61aa64..dfaac5995c84 100644 --- a/drivers/watchdog/qcom-wdt.c +++ b/drivers/watchdog/qcom-wdt.c @@ -181,6 +181,12 @@ static const struct qcom_wdt_match_data match_data_apcs_tmr = { .max_tick_count = 0x10000000U, }; +static const struct qcom_wdt_match_data match_data_ipq5424 = { + .offset = reg_offset_data_kpss, + .pretimeout = true, + .max_tick_count = 0xFFFFFU, +}; + static const struct qcom_wdt_match_data match_data_kpss = { .offset = reg_offset_data_kpss, .pretimeout = true, @@ -322,6 +328,7 @@ static const struct dev_pm_ops qcom_wdt_pm_ops = { }; static const struct of_device_id qcom_wdt_of_table[] = { + { .compatible = "qcom,apss-wdt-ipq5424", .data = &match_data_ipq5424 }, { .compatible = "qcom,kpss-timer", .data = &match_data_apcs_tmr }, { .compatible = "qcom,scss-timer", .data = &match_data_apcs_tmr }, { .compatible = "qcom,kpss-wdt", .data = &match_data_kpss }, diff --git a/drivers/watchdog/rc32434_wdt.c b/drivers/watchdog/rc32434_wdt.c index 417f9b75679c..0e5c5c96af58 100644 --- a/drivers/watchdog/rc32434_wdt.c +++ b/drivers/watchdog/rc32434_wdt.c @@ -242,7 +242,6 @@ static long rc32434_wdt_ioctl(struct file *file, unsigned int cmd, static const struct file_operations rc32434_wdt_fops = { .owner = THIS_MODULE, - .llseek = no_llseek, .write = rc32434_wdt_write, .unlocked_ioctl = rc32434_wdt_ioctl, .compat_ioctl = compat_ptr_ioctl, @@ -310,7 +309,7 @@ static void rc32434_wdt_shutdown(struct platform_device *pdev) static struct platform_driver rc32434_wdt_driver = { .probe = rc32434_wdt_probe, - .remove_new = rc32434_wdt_remove, + .remove = rc32434_wdt_remove, .shutdown = rc32434_wdt_shutdown, .driver = { .name = "rc32434_wdt", diff --git a/drivers/watchdog/rdc321x_wdt.c b/drivers/watchdog/rdc321x_wdt.c index 6176f4343fc5..8955177072fa 100644 --- a/drivers/watchdog/rdc321x_wdt.c +++ b/drivers/watchdog/rdc321x_wdt.c @@ -197,7 +197,6 @@ static ssize_t rdc321x_wdt_write(struct file *file, const char __user *buf, static const struct file_operations rdc321x_wdt_fops = { .owner = THIS_MODULE, - .llseek = no_llseek, .unlocked_ioctl = rdc321x_wdt_ioctl, .compat_ioctl = compat_ptr_ioctl, .open = rdc321x_wdt_open, @@ -269,7 +268,7 @@ static void rdc321x_wdt_remove(struct platform_device *pdev) static struct platform_driver rdc321x_wdt_driver = { .probe = rdc321x_wdt_probe, - .remove_new = rdc321x_wdt_remove, + .remove = rdc321x_wdt_remove, .driver = { .name = "rdc321x-wdt", }, diff --git a/drivers/watchdog/renesas_wdt.c b/drivers/watchdog/renesas_wdt.c index 12c41d6e5cd6..c0b2a9c5250d 100644 --- a/drivers/watchdog/renesas_wdt.c +++ b/drivers/watchdog/renesas_wdt.c @@ -337,7 +337,7 @@ static struct platform_driver rwdt_driver = { .pm = &rwdt_pm_ops, }, .probe = rwdt_probe, - .remove_new = rwdt_remove, + .remove = rwdt_remove, }; module_platform_driver(rwdt_driver); diff --git a/drivers/watchdog/riowd.c b/drivers/watchdog/riowd.c index b293792a292a..83806ccf06d1 100644 --- a/drivers/watchdog/riowd.c +++ b/drivers/watchdog/riowd.c @@ -160,7 +160,6 @@ static ssize_t riowd_write(struct file *file, const char __user *buf, static const struct file_operations riowd_fops = { .owner = THIS_MODULE, - .llseek = no_llseek, .unlocked_ioctl = riowd_ioctl, .compat_ioctl = compat_ptr_ioctl, .open = riowd_open, @@ -239,7 +238,7 @@ static struct platform_driver riowd_driver = { .of_match_table = riowd_match, }, .probe = riowd_probe, - .remove_new = riowd_remove, + .remove = riowd_remove, }; module_platform_driver(riowd_driver); diff --git a/drivers/watchdog/rti_wdt.c b/drivers/watchdog/rti_wdt.c index 9215793a1c81..d1f9ce4100a8 100644 --- a/drivers/watchdog/rti_wdt.c +++ b/drivers/watchdog/rti_wdt.c @@ -59,7 +59,9 @@ #define PON_REASON_EOF_NUM 0xCCCCBBBB #define RESERVED_MEM_MIN_SIZE 12 -static int heartbeat = DEFAULT_HEARTBEAT; +#define MAX_HW_ERROR 250 + +static int heartbeat; /* * struct to hold data for each WDT device @@ -97,7 +99,7 @@ static int rti_wdt_start(struct watchdog_device *wdd) * to be 50% or less than that; we obviouly want to configure the open * window as large as possible so we select the 50% option. */ - wdd->min_hw_heartbeat_ms = 500 * wdd->timeout; + wdd->min_hw_heartbeat_ms = 520 * wdd->timeout + MAX_HW_ERROR; /* Generate NMI when wdt expires */ writel_relaxed(RTIWWDRX_NMI, wdt->base + RTIWWDRXCTRL); @@ -131,31 +133,33 @@ static int rti_wdt_setup_hw_hb(struct watchdog_device *wdd, u32 wsize) * be petted during the open window; not too early or not too late. * The HW configuration options only allow for the open window size * to be 50% or less than that. + * To avoid any glitches, we accommodate 2% + max hardware error + * safety margin. */ switch (wsize) { case RTIWWDSIZE_50P: - /* 50% open window => 50% min heartbeat */ - wdd->min_hw_heartbeat_ms = 500 * heartbeat; + /* 50% open window => 52% min heartbeat */ + wdd->min_hw_heartbeat_ms = 520 * heartbeat + MAX_HW_ERROR; break; case RTIWWDSIZE_25P: - /* 25% open window => 75% min heartbeat */ - wdd->min_hw_heartbeat_ms = 750 * heartbeat; + /* 25% open window => 77% min heartbeat */ + wdd->min_hw_heartbeat_ms = 770 * heartbeat + MAX_HW_ERROR; break; case RTIWWDSIZE_12P5: - /* 12.5% open window => 87.5% min heartbeat */ - wdd->min_hw_heartbeat_ms = 875 * heartbeat; + /* 12.5% open window => 89.5% min heartbeat */ + wdd->min_hw_heartbeat_ms = 895 * heartbeat + MAX_HW_ERROR; break; case RTIWWDSIZE_6P25: - /* 6.5% open window => 93.5% min heartbeat */ - wdd->min_hw_heartbeat_ms = 935 * heartbeat; + /* 6.5% open window => 95.5% min heartbeat */ + wdd->min_hw_heartbeat_ms = 955 * heartbeat + MAX_HW_ERROR; break; case RTIWWDSIZE_3P125: - /* 3.125% open window => 96.9% min heartbeat */ - wdd->min_hw_heartbeat_ms = 969 * heartbeat; + /* 3.125% open window => 98.9% min heartbeat */ + wdd->min_hw_heartbeat_ms = 989 * heartbeat + MAX_HW_ERROR; break; default: @@ -233,14 +237,6 @@ static int rti_wdt_probe(struct platform_device *pdev) return -EINVAL; } - /* - * If watchdog is running at 32k clock, it is not accurate. - * Adjust frequency down in this case so that we don't pet - * the watchdog too often. - */ - if (wdt->freq < 32768) - wdt->freq = wdt->freq * 9 / 10; - pm_runtime_enable(dev); ret = pm_runtime_resume_and_get(dev); if (ret < 0) { @@ -256,6 +252,7 @@ static int rti_wdt_probe(struct platform_device *pdev) wdd->min_timeout = 1; wdd->max_hw_heartbeat_ms = (WDT_PRELOAD_MAX << WDT_PRELOAD_SHIFT) / wdt->freq * 1000; + wdd->timeout = DEFAULT_HEARTBEAT; wdd->parent = dev; watchdog_set_drvdata(wdd, wdt); @@ -276,7 +273,8 @@ static int rti_wdt_probe(struct platform_device *pdev) set_bit(WDOG_HW_RUNNING, &wdd->status); time_left_ms = rti_wdt_get_timeleft_ms(wdd); - heartbeat_ms = readl(wdt->base + RTIDWDPRLD); + /* AM62x TRM: texp = (RTIDWDPRLD + 1) * (2^13) / RTICLK1 */ + heartbeat_ms = readl(wdt->base + RTIDWDPRLD) + 1; heartbeat_ms <<= WDT_PRELOAD_SHIFT; heartbeat_ms *= 1000; do_div(heartbeat_ms, wdt->freq); @@ -304,6 +302,7 @@ static int rti_wdt_probe(struct platform_device *pdev) node = of_parse_phandle(pdev->dev.of_node, "memory-region", 0); if (node) { ret = of_address_to_resource(node, 0, &res); + of_node_put(node); if (ret) { dev_err(dev, "No memory address assigned to the region.\n"); goto err_iomap; @@ -340,10 +339,8 @@ static int rti_wdt_probe(struct platform_device *pdev) watchdog_init_timeout(wdd, heartbeat, dev); ret = watchdog_register_device(wdd); - if (ret) { - dev_err(dev, "cannot register watchdog device\n"); + if (ret) goto err_iomap; - } if (last_ping) watchdog_set_last_hw_keepalive(wdd, last_ping); @@ -384,7 +381,7 @@ static struct platform_driver rti_wdt_driver = { .of_match_table = rti_wdt_of_match, }, .probe = rti_wdt_probe, - .remove_new = rti_wdt_remove, + .remove = rti_wdt_remove, }; module_platform_driver(rti_wdt_driver); diff --git a/drivers/watchdog/rza_wdt.c b/drivers/watchdog/rza_wdt.c index cb4901b3f777..9334255a37e9 100644 --- a/drivers/watchdog/rza_wdt.c +++ b/drivers/watchdog/rza_wdt.c @@ -169,7 +169,6 @@ static int rza_wdt_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct rza_wdt *priv; unsigned long rate; - int ret; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) @@ -218,11 +217,7 @@ static int rza_wdt_probe(struct platform_device *pdev) watchdog_init_timeout(&priv->wdev, 0, dev); watchdog_set_drvdata(&priv->wdev, priv); - ret = devm_watchdog_register_device(dev, &priv->wdev); - if (ret) - dev_err(dev, "Cannot register watchdog device\n"); - - return ret; + return devm_watchdog_register_device(dev, &priv->wdev); } static const struct of_device_id rza_wdt_of_match[] = { diff --git a/drivers/watchdog/rzg2l_wdt.c b/drivers/watchdog/rzg2l_wdt.c index 1741f98ca67c..11bbe48160ec 100644 --- a/drivers/watchdog/rzg2l_wdt.c +++ b/drivers/watchdog/rzg2l_wdt.c @@ -8,11 +8,11 @@ #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> #include <linux/platform_device.h> +#include <linux/pm_domain.h> #include <linux/pm_runtime.h> #include <linux/reset.h> #include <linux/units.h> @@ -54,35 +54,11 @@ struct rzg2l_wdt_priv { struct reset_control *rstc; unsigned long osc_clk_rate; unsigned long delay; - unsigned long minimum_assertion_period; struct clk *pclk; struct clk *osc_clk; enum rz_wdt_type devtype; }; -static int rzg2l_wdt_reset(struct rzg2l_wdt_priv *priv) -{ - int err, status; - - if (priv->devtype == WDT_RZV2M) { - /* WDT needs TYPE-B reset control */ - err = reset_control_assert(priv->rstc); - if (err) - return err; - ndelay(priv->minimum_assertion_period); - err = reset_control_deassert(priv->rstc); - if (err) - return err; - err = read_poll_timeout(reset_control_status, status, - status != 1, 0, 1000, false, - priv->rstc); - } else { - err = reset_control_reset(priv->rstc); - } - - return err; -} - static void rzg2l_wdt_wait_delay(struct rzg2l_wdt_priv *priv) { /* delay timer when change the setting register */ @@ -123,8 +99,17 @@ static void rzg2l_wdt_init_timeout(struct watchdog_device *wdev) static int rzg2l_wdt_start(struct watchdog_device *wdev) { struct rzg2l_wdt_priv *priv = watchdog_get_drvdata(wdev); + int ret; + + ret = pm_runtime_resume_and_get(wdev->parent); + if (ret) + return ret; - pm_runtime_get_sync(wdev->parent); + ret = reset_control_deassert(priv->rstc); + if (ret) { + pm_runtime_put(wdev->parent); + return ret; + } /* Initialize time out */ rzg2l_wdt_init_timeout(wdev); @@ -141,15 +126,23 @@ static int rzg2l_wdt_start(struct watchdog_device *wdev) static int rzg2l_wdt_stop(struct watchdog_device *wdev) { struct rzg2l_wdt_priv *priv = watchdog_get_drvdata(wdev); + int ret; + + ret = reset_control_assert(priv->rstc); + if (ret) + return ret; - rzg2l_wdt_reset(priv); - pm_runtime_put(wdev->parent); + ret = pm_runtime_put(wdev->parent); + if (ret < 0) + return ret; return 0; } static int rzg2l_wdt_set_timeout(struct watchdog_device *wdev, unsigned int timeout) { + int ret = 0; + wdev->timeout = timeout; /* @@ -158,22 +151,44 @@ static int rzg2l_wdt_set_timeout(struct watchdog_device *wdev, unsigned int time * to reset the module) so that it is updated with new timeout values. */ if (watchdog_active(wdev)) { - rzg2l_wdt_stop(wdev); - rzg2l_wdt_start(wdev); + ret = rzg2l_wdt_stop(wdev); + if (ret) + return ret; + + ret = rzg2l_wdt_start(wdev); } - return 0; + return ret; } static int rzg2l_wdt_restart(struct watchdog_device *wdev, unsigned long action, void *data) { struct rzg2l_wdt_priv *priv = watchdog_get_drvdata(wdev); + int ret; - clk_prepare_enable(priv->pclk); - clk_prepare_enable(priv->osc_clk); + /* + * In case of RZ/G3S the watchdog device may be part of an IRQ safe power + * domain that is currently powered off. In this case we need to power + * it on before accessing registers. Along with this the clocks will be + * enabled. We don't undo the pm_runtime_resume_and_get() as the device + * need to be on for the reboot to happen. + * + * For the rest of SoCs not registering a watchdog IRQ safe power + * domain it is safe to call pm_runtime_resume_and_get() as the + * irq_safe_dev_in_sleep_domain() call in genpd_runtime_resume() + * returns non zero value and the genpd_lock() is avoided, thus, there + * will be no invalid wait context reported by lockdep. + */ + ret = pm_runtime_resume_and_get(wdev->parent); + if (ret) + return ret; if (priv->devtype == WDT_RZG2L) { + ret = reset_control_deassert(priv->rstc); + if (ret) + return ret; + /* Generate Reset (WDTRSTB) Signal on parity error */ rzg2l_wdt_write(priv, 0, PECR); @@ -181,7 +196,9 @@ static int rzg2l_wdt_restart(struct watchdog_device *wdev, rzg2l_wdt_write(priv, PEEN_FORCE, PEEN); } else { /* RZ/V2M doesn't have parity error registers */ - rzg2l_wdt_reset(priv); + ret = reset_control_reset(priv->rstc); + if (ret) + return ret; wdev->timeout = 0; @@ -224,13 +241,11 @@ static const struct watchdog_ops rzg2l_wdt_ops = { .restart = rzg2l_wdt_restart, }; -static void rzg2l_wdt_reset_assert_pm_disable(void *data) +static void rzg2l_wdt_pm_disable(void *data) { struct watchdog_device *wdev = data; - struct rzg2l_wdt_priv *priv = watchdog_get_drvdata(wdev); pm_runtime_disable(wdev->parent); - reset_control_assert(priv->rstc); } static int rzg2l_wdt_probe(struct platform_device *pdev) @@ -273,19 +288,9 @@ static int rzg2l_wdt_probe(struct platform_device *pdev) return dev_err_probe(&pdev->dev, PTR_ERR(priv->rstc), "failed to get cpg reset"); - ret = reset_control_deassert(priv->rstc); - if (ret) - return dev_err_probe(dev, ret, "failed to deassert"); - priv->devtype = (uintptr_t)of_device_get_match_data(dev); - if (priv->devtype == WDT_RZV2M) { - priv->minimum_assertion_period = RZV2M_A_NSEC + - 3 * F2CYCLE_NSEC(pclk_rate) + 5 * - max(F2CYCLE_NSEC(priv->osc_clk_rate), - F2CYCLE_NSEC(pclk_rate)); - } - + pm_runtime_irq_safe(&pdev->dev); pm_runtime_enable(&pdev->dev); priv->wdev.info = &rzg2l_wdt_ident; @@ -297,10 +302,9 @@ static int rzg2l_wdt_probe(struct platform_device *pdev) priv->wdev.timeout = WDT_DEFAULT_TIMEOUT; watchdog_set_drvdata(&priv->wdev, priv); - ret = devm_add_action_or_reset(&pdev->dev, - rzg2l_wdt_reset_assert_pm_disable, - &priv->wdev); - if (ret < 0) + dev_set_drvdata(dev, priv); + ret = devm_add_action_or_reset(&pdev->dev, rzg2l_wdt_pm_disable, &priv->wdev); + if (ret) return ret; watchdog_set_nowayout(&priv->wdev, nowayout); @@ -320,10 +324,35 @@ static const struct of_device_id rzg2l_wdt_ids[] = { }; MODULE_DEVICE_TABLE(of, rzg2l_wdt_ids); +static int rzg2l_wdt_suspend_late(struct device *dev) +{ + struct rzg2l_wdt_priv *priv = dev_get_drvdata(dev); + + if (!watchdog_active(&priv->wdev)) + return 0; + + return rzg2l_wdt_stop(&priv->wdev); +} + +static int rzg2l_wdt_resume_early(struct device *dev) +{ + struct rzg2l_wdt_priv *priv = dev_get_drvdata(dev); + + if (!watchdog_active(&priv->wdev)) + return 0; + + return rzg2l_wdt_start(&priv->wdev); +} + +static const struct dev_pm_ops rzg2l_wdt_pm_ops = { + LATE_SYSTEM_SLEEP_PM_OPS(rzg2l_wdt_suspend_late, rzg2l_wdt_resume_early) +}; + static struct platform_driver rzg2l_wdt_driver = { .driver = { .name = "rzg2l_wdt", .of_match_table = rzg2l_wdt_ids, + .pm = &rzg2l_wdt_pm_ops, }, .probe = rzg2l_wdt_probe, }; diff --git a/drivers/watchdog/rzn1_wdt.c b/drivers/watchdog/rzn1_wdt.c index 980c1717adb5..96fd04fbc2a2 100644 --- a/drivers/watchdog/rzn1_wdt.c +++ b/drivers/watchdog/rzn1_wdt.c @@ -52,7 +52,7 @@ static int rzn1_wdt_ping(struct watchdog_device *w) { struct rzn1_watchdog *wdt = watchdog_get_drvdata(w); - /* Any value retrigggers the watchdog */ + /* Any value retriggers the watchdog */ writel(0, wdt->base + RZN1_WDT_RETRIGGER); return 0; @@ -140,9 +140,9 @@ static int rzn1_wdt_probe(struct platform_device *pdev) } wdt->clk_rate_khz = clk_rate / 1000; - wdt->wdtdev.info = &rzn1_wdt_info, - wdt->wdtdev.ops = &rzn1_wdt_ops, - wdt->wdtdev.status = WATCHDOG_NOWAYOUT_INIT_STATUS, + wdt->wdtdev.info = &rzn1_wdt_info; + wdt->wdtdev.ops = &rzn1_wdt_ops; + wdt->wdtdev.status = WATCHDOG_NOWAYOUT_INIT_STATUS; wdt->wdtdev.parent = dev; /* * The period of the watchdog cannot be changed once set diff --git a/drivers/watchdog/rzv2h_wdt.c b/drivers/watchdog/rzv2h_wdt.c new file mode 100644 index 000000000000..8defd0241213 --- /dev/null +++ b/drivers/watchdog/rzv2h_wdt.c @@ -0,0 +1,273 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Renesas RZ/V2H(P) WDT Watchdog Driver + * + * Copyright (C) 2024 Renesas Electronics Corporation. + */ +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/reset.h> +#include <linux/units.h> +#include <linux/watchdog.h> + +#define WDTRR 0x00 /* WDT Refresh Register RW, 8 */ +#define WDTCR 0x02 /* WDT Control Register RW, 16 */ +#define WDTSR 0x04 /* WDT Status Register RW, 16 */ +#define WDTRCR 0x06 /* WDT Reset Control Register RW, 8 */ + +#define WDTCR_TOPS_1024 0x00 +#define WDTCR_TOPS_16384 0x03 + +#define WDTCR_CKS_CLK_1 0x00 +#define WDTCR_CKS_CLK_256 0x50 + +#define WDTCR_RPES_0 0x300 +#define WDTCR_RPES_75 0x000 + +#define WDTCR_RPSS_25 0x00 +#define WDTCR_RPSS_100 0x3000 + +#define WDTRCR_RSTIRQS BIT(7) + +#define MAX_TIMEOUT_CYCLES 16384 +#define CLOCK_DIV_BY_256 256 + +#define WDT_DEFAULT_TIMEOUT 60U + +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 rzv2h_wdt_priv { + void __iomem *base; + struct clk *pclk; + struct clk *oscclk; + struct reset_control *rstc; + struct watchdog_device wdev; +}; + +static int rzv2h_wdt_ping(struct watchdog_device *wdev) +{ + struct rzv2h_wdt_priv *priv = watchdog_get_drvdata(wdev); + + /* + * The down-counter is refreshed and starts counting operation on + * a write of the values 00h and FFh to the WDTRR register. + */ + writeb(0x0, priv->base + WDTRR); + writeb(0xFF, priv->base + WDTRR); + + return 0; +} + +static void rzv2h_wdt_setup(struct watchdog_device *wdev, u16 wdtcr) +{ + struct rzv2h_wdt_priv *priv = watchdog_get_drvdata(wdev); + + /* Configure the timeout, clock division ratio, and window start and end positions. */ + writew(wdtcr, priv->base + WDTCR); + + /* Enable interrupt output to the ICU. */ + writeb(0, priv->base + WDTRCR); + + /* Clear underflow flag and refresh error flag. */ + writew(0, priv->base + WDTSR); +} + +static int rzv2h_wdt_start(struct watchdog_device *wdev) +{ + struct rzv2h_wdt_priv *priv = watchdog_get_drvdata(wdev); + int ret; + + ret = pm_runtime_resume_and_get(wdev->parent); + if (ret) + return ret; + + ret = reset_control_deassert(priv->rstc); + if (ret) { + pm_runtime_put(wdev->parent); + return ret; + } + + /* delay to handle clock halt after de-assert operation */ + udelay(3); + + /* + * WDTCR + * - CKS[7:4] - Clock Division Ratio Select - 0101b: oscclk/256 + * - RPSS[13:12] - Window Start Position Select - 11b: 100% + * - RPES[9:8] - Window End Position Select - 11b: 0% + * - TOPS[1:0] - Timeout Period Select - 11b: 16384 cycles (3FFFh) + */ + rzv2h_wdt_setup(wdev, WDTCR_CKS_CLK_256 | WDTCR_RPSS_100 | + WDTCR_RPES_0 | WDTCR_TOPS_16384); + + /* + * Down counting starts after writing the sequence 00h -> FFh to the + * WDTRR register. Hence, call the ping operation after loading the counter. + */ + rzv2h_wdt_ping(wdev); + + return 0; +} + +static int rzv2h_wdt_stop(struct watchdog_device *wdev) +{ + struct rzv2h_wdt_priv *priv = watchdog_get_drvdata(wdev); + int ret; + + ret = reset_control_assert(priv->rstc); + if (ret) + return ret; + + ret = pm_runtime_put(wdev->parent); + if (ret < 0) + return ret; + + return 0; +} + +static const struct watchdog_info rzv2h_wdt_ident = { + .options = WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT, + .identity = "Renesas RZ/V2H WDT Watchdog", +}; + +static int rzv2h_wdt_restart(struct watchdog_device *wdev, + unsigned long action, void *data) +{ + struct rzv2h_wdt_priv *priv = watchdog_get_drvdata(wdev); + int ret; + + if (!watchdog_active(wdev)) { + ret = clk_enable(priv->pclk); + if (ret) + return ret; + + ret = clk_enable(priv->oscclk); + if (ret) { + clk_disable(priv->pclk); + return ret; + } + + ret = reset_control_deassert(priv->rstc); + if (ret) { + clk_disable(priv->oscclk); + clk_disable(priv->pclk); + return ret; + } + } else { + /* + * Writing to the WDT Control Register (WDTCR) or WDT Reset + * Control Register (WDTRCR) is possible once between the + * release from the reset state and the first refresh operation. + * Therefore, issue a reset if the watchdog is active. + */ + ret = reset_control_reset(priv->rstc); + if (ret) + return ret; + } + + /* delay to handle clock halt after de-assert operation */ + udelay(3); + + /* + * WDTCR + * - CKS[7:4] - Clock Division Ratio Select - 0000b: oscclk/1 + * - RPSS[13:12] - Window Start Position Select - 00b: 25% + * - RPES[9:8] - Window End Position Select - 00b: 75% + * - TOPS[1:0] - Timeout Period Select - 00b: 1024 cycles (03FFh) + */ + rzv2h_wdt_setup(wdev, WDTCR_CKS_CLK_1 | WDTCR_RPSS_25 | + WDTCR_RPES_75 | WDTCR_TOPS_1024); + + rzv2h_wdt_ping(wdev); + + /* wait for underflow to trigger... */ + udelay(5); + + return 0; +} + +static const struct watchdog_ops rzv2h_wdt_ops = { + .owner = THIS_MODULE, + .start = rzv2h_wdt_start, + .stop = rzv2h_wdt_stop, + .ping = rzv2h_wdt_ping, + .restart = rzv2h_wdt_restart, +}; + +static int rzv2h_wdt_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct rzv2h_wdt_priv *priv; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + priv->pclk = devm_clk_get_prepared(dev, "pclk"); + if (IS_ERR(priv->pclk)) + return dev_err_probe(dev, PTR_ERR(priv->pclk), "no pclk"); + + priv->oscclk = devm_clk_get_prepared(dev, "oscclk"); + if (IS_ERR(priv->oscclk)) + return dev_err_probe(dev, PTR_ERR(priv->oscclk), "no oscclk"); + + priv->rstc = devm_reset_control_get_exclusive(dev, NULL); + if (IS_ERR(priv->rstc)) + return dev_err_probe(dev, PTR_ERR(priv->rstc), + "failed to get cpg reset"); + + priv->wdev.max_hw_heartbeat_ms = (MILLI * MAX_TIMEOUT_CYCLES * CLOCK_DIV_BY_256) / + clk_get_rate(priv->oscclk); + dev_dbg(dev, "max hw timeout of %dms\n", priv->wdev.max_hw_heartbeat_ms); + + ret = devm_pm_runtime_enable(dev); + if (ret) + return ret; + + priv->wdev.min_timeout = 1; + priv->wdev.timeout = WDT_DEFAULT_TIMEOUT; + priv->wdev.info = &rzv2h_wdt_ident; + priv->wdev.ops = &rzv2h_wdt_ops; + priv->wdev.parent = dev; + watchdog_set_drvdata(&priv->wdev, priv); + watchdog_set_nowayout(&priv->wdev, nowayout); + watchdog_stop_on_unregister(&priv->wdev); + + ret = watchdog_init_timeout(&priv->wdev, 0, dev); + if (ret) + dev_warn(dev, "Specified timeout invalid, using default"); + + return devm_watchdog_register_device(dev, &priv->wdev); +} + +static const struct of_device_id rzv2h_wdt_ids[] = { + { .compatible = "renesas,r9a09g057-wdt", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, rzv2h_wdt_ids); + +static struct platform_driver rzv2h_wdt_driver = { + .driver = { + .name = "rzv2h_wdt", + .of_match_table = rzv2h_wdt_ids, + }, + .probe = rzv2h_wdt_probe, +}; +module_platform_driver(rzv2h_wdt_driver); +MODULE_AUTHOR("Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>"); +MODULE_DESCRIPTION("Renesas RZ/V2H(P) WDT Watchdog Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/watchdog/s32g_wdt.c b/drivers/watchdog/s32g_wdt.c new file mode 100644 index 000000000000..ad55063060af --- /dev/null +++ b/drivers/watchdog/s32g_wdt.c @@ -0,0 +1,315 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Watchdog driver for S32G SoC + * + * Copyright 2017-2019, 2021-2025 NXP. + * + */ +#include <linux/clk.h> +#include <linux/debugfs.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/watchdog.h> + +#define DRIVER_NAME "s32g-swt" + +#define S32G_SWT_CR(__base) ((__base) + 0x00) /* Control Register offset */ +#define S32G_SWT_CR_SM (BIT(9) | BIT(10)) /* -> Service Mode */ +#define S32G_SWT_CR_STP BIT(2) /* -> Stop Mode Control */ +#define S32G_SWT_CR_FRZ BIT(1) /* -> Debug Mode Control */ +#define S32G_SWT_CR_WEN BIT(0) /* -> Watchdog Enable */ + +#define S32G_SWT_TO(__base) ((__base) + 0x08) /* Timeout Register offset */ + +#define S32G_SWT_SR(__base) ((__base) + 0x10) /* Service Register offset */ +#define S32G_WDT_SEQ1 0xA602 /* -> service sequence number 1 */ +#define S32G_WDT_SEQ2 0xB480 /* -> service sequence number 2 */ + +#define S32G_SWT_CO(__base) ((__base) + 0x14) /* Counter output register */ + +#define S32G_WDT_DEFAULT_TIMEOUT 30 + +struct s32g_wdt_device { + int rate; + void __iomem *base; + struct watchdog_device wdog; +}; + +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_param = S32G_WDT_DEFAULT_TIMEOUT; +module_param(timeout_param, uint, 0); +MODULE_PARM_DESC(timeout_param, "Watchdog timeout in seconds (default=" + __MODULE_STRING(S32G_WDT_DEFAULT_TIMEOUT) ")"); + +static bool early_enable; +module_param(early_enable, bool, 0); +MODULE_PARM_DESC(early_enable, + "Watchdog is started on module insertion (default=false)"); + +static const struct watchdog_info s32g_wdt_info = { + .identity = "s32g watchdog", + .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | + WDIOC_GETTIMEOUT | WDIOC_GETTIMELEFT, +}; + +static struct s32g_wdt_device *wdd_to_s32g_wdt(struct watchdog_device *wdd) +{ + return container_of(wdd, struct s32g_wdt_device, wdog); +} + +static unsigned int wdog_sec_to_count(struct s32g_wdt_device *wdev, unsigned int timeout) +{ + return wdev->rate * timeout; +} + +static int s32g_wdt_ping(struct watchdog_device *wdog) +{ + struct s32g_wdt_device *wdev = wdd_to_s32g_wdt(wdog); + + writel(S32G_WDT_SEQ1, S32G_SWT_SR(wdev->base)); + writel(S32G_WDT_SEQ2, S32G_SWT_SR(wdev->base)); + + return 0; +} + +static int s32g_wdt_start(struct watchdog_device *wdog) +{ + struct s32g_wdt_device *wdev = wdd_to_s32g_wdt(wdog); + unsigned long val; + + val = readl(S32G_SWT_CR(wdev->base)); + + val |= S32G_SWT_CR_WEN; + + writel(val, S32G_SWT_CR(wdev->base)); + + return 0; +} + +static int s32g_wdt_stop(struct watchdog_device *wdog) +{ + struct s32g_wdt_device *wdev = wdd_to_s32g_wdt(wdog); + unsigned long val; + + val = readl(S32G_SWT_CR(wdev->base)); + + val &= ~S32G_SWT_CR_WEN; + + writel(val, S32G_SWT_CR(wdev->base)); + + return 0; +} + +static int s32g_wdt_set_timeout(struct watchdog_device *wdog, unsigned int timeout) +{ + struct s32g_wdt_device *wdev = wdd_to_s32g_wdt(wdog); + + writel(wdog_sec_to_count(wdev, timeout), S32G_SWT_TO(wdev->base)); + + wdog->timeout = timeout; + + /* + * Conforming to the documentation, the timeout counter is + * loaded when servicing is operated (aka ping) or when the + * counter is enabled. In case the watchdog is already started + * it must be stopped and started again to update the timeout + * register or a ping can be sent to refresh the counter. Here + * we choose to send a ping to the watchdog which is harmless + * if the watchdog is stopped. + */ + return s32g_wdt_ping(wdog); +} + +static unsigned int s32g_wdt_get_timeleft(struct watchdog_device *wdog) +{ + struct s32g_wdt_device *wdev = wdd_to_s32g_wdt(wdog); + unsigned long counter; + bool is_running; + + /* + * The counter output can be read only if the SWT is + * disabled. Given the latency between the internal counter + * and the counter output update, there can be very small + * difference. However, we can accept this matter of fact + * given the resolution is a second based unit for the output. + */ + is_running = watchdog_hw_running(wdog); + + if (is_running) + s32g_wdt_stop(wdog); + + counter = readl(S32G_SWT_CO(wdev->base)); + + if (is_running) + s32g_wdt_start(wdog); + + return counter / wdev->rate; +} + +static const struct watchdog_ops s32g_wdt_ops = { + .owner = THIS_MODULE, + .start = s32g_wdt_start, + .stop = s32g_wdt_stop, + .ping = s32g_wdt_ping, + .set_timeout = s32g_wdt_set_timeout, + .get_timeleft = s32g_wdt_get_timeleft, +}; + +static void s32g_wdt_init(struct s32g_wdt_device *wdev) +{ + unsigned long val; + + /* Set the watchdog's Time-Out value */ + val = wdog_sec_to_count(wdev, wdev->wdog.timeout); + + writel(val, S32G_SWT_TO(wdev->base)); + + /* + * Get the control register content. We are at init time, the + * watchdog should not be started. + */ + val = readl(S32G_SWT_CR(wdev->base)); + + /* + * We want to allow the watchdog timer to be stopped when + * device enters debug mode. + */ + val |= S32G_SWT_CR_FRZ; + + /* + * However, when the CPU is in WFI or suspend mode, the + * watchdog must continue. The documentation refers it as the + * stopped mode. + */ + val &= ~S32G_SWT_CR_STP; + + /* + * Use Fixed Service Sequence to ping the watchdog which is + * 0x00 configuration value for the service mode. It should be + * already set because it is the default value but we reset it + * in case. + */ + val &= ~S32G_SWT_CR_SM; + + writel(val, S32G_SWT_CR(wdev->base)); + + /* + * When the 'early_enable' option is set, we start the + * watchdog from the kernel. + */ + if (early_enable) { + s32g_wdt_start(&wdev->wdog); + set_bit(WDOG_HW_RUNNING, &wdev->wdog.status); + } +} + +static int s32g_wdt_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct resource *res; + struct clk *clk; + struct s32g_wdt_device *wdev; + struct watchdog_device *wdog; + int ret; + + wdev = devm_kzalloc(dev, sizeof(*wdev), GFP_KERNEL); + if (!wdev) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + wdev->base = devm_ioremap_resource(dev, res); + if (IS_ERR(wdev->base)) + return dev_err_probe(&pdev->dev, PTR_ERR(wdev->base), "Can not get resource\n"); + + clk = devm_clk_get_enabled(dev, "counter"); + if (IS_ERR(clk)) + return dev_err_probe(dev, PTR_ERR(clk), "Can't get Watchdog clock\n"); + + wdev->rate = clk_get_rate(clk); + if (!wdev->rate) { + dev_err(dev, "Input clock rate is not valid\n"); + return -EINVAL; + } + + wdog = &wdev->wdog; + wdog->info = &s32g_wdt_info; + wdog->ops = &s32g_wdt_ops; + + /* + * The code converts the timeout into a counter a value, if + * the value is less than 0x100, then it is clamped by the SWT + * module, so it is safe to specify a zero value as the + * minimum timeout. + */ + wdog->min_timeout = 0; + + /* + * The counter register is a 32 bits long, so the maximum + * counter value is UINT_MAX and the timeout in second is the + * value divided by the rate. + * + * For instance, a rate of 51MHz lead to 84 seconds maximum + * timeout. + */ + wdog->max_timeout = UINT_MAX / wdev->rate; + + /* + * The module param and the DT 'timeout-sec' property will + * override the default value if they are specified. + */ + ret = watchdog_init_timeout(wdog, timeout_param, dev); + if (ret) + return ret; + + /* + * As soon as the watchdog is started, there is no way to stop + * it if the 'nowayout' option is set at boot time + */ + watchdog_set_nowayout(wdog, nowayout); + + /* + * The devm_ version of the watchdog_register_device() + * function will call watchdog_unregister_device() when the + * device is removed. + */ + watchdog_stop_on_unregister(wdog); + + s32g_wdt_init(wdev); + + ret = devm_watchdog_register_device(dev, wdog); + if (ret) + return dev_err_probe(dev, ret, "Cannot register watchdog device\n"); + + dev_info(dev, "S32G Watchdog Timer Registered, timeout=%ds, nowayout=%d, early_enable=%d\n", + wdog->timeout, nowayout, early_enable); + + return 0; +} + +static const struct of_device_id s32g_wdt_dt_ids[] = { + { .compatible = "nxp,s32g2-swt" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, s32g_wdt_dt_ids); + +static struct platform_driver s32g_wdt_driver = { + .probe = s32g_wdt_probe, + .driver = { + .name = DRIVER_NAME, + .of_match_table = s32g_wdt_dt_ids, + }, +}; + +module_platform_driver(s32g_wdt_driver); + +MODULE_AUTHOR("Daniel Lezcano <daniel.lezcano@linaro.org>"); +MODULE_DESCRIPTION("Watchdog driver for S32G SoC"); +MODULE_LICENSE("GPL"); diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c index 686cf544d0ae..40901bdac426 100644 --- a/drivers/watchdog/s3c2410_wdt.c +++ b/drivers/watchdog/s3c2410_wdt.c @@ -24,9 +24,9 @@ #include <linux/slab.h> #include <linux/err.h> #include <linux/of.h> +#include <linux/mfd/syscon.h> #include <linux/regmap.h> #include <linux/delay.h> -#include <linux/soc/samsung/exynos-pmu.h> #define S3C2410_WTCON 0x00 #define S3C2410_WTDAT 0x04 @@ -63,11 +63,17 @@ #define EXYNOS850_CLUSTER1_NONCPU_INT_EN 0x1644 #define EXYNOSAUTOV9_CLUSTER1_NONCPU_OUT 0x1520 #define EXYNOSAUTOV9_CLUSTER1_NONCPU_INT_EN 0x1544 +#define EXYNOSAUTOV920_CLUSTER0_NONCPU_OUT 0x1420 +#define EXYNOSAUTOV920_CLUSTER0_NONCPU_INT_EN 0x1444 +#define EXYNOSAUTOV920_CLUSTER1_NONCPU_OUT 0x1720 +#define EXYNOSAUTOV920_CLUSTER1_NONCPU_INT_EN 0x1744 #define EXYNOS850_CLUSTER0_WDTRESET_BIT 24 #define EXYNOS850_CLUSTER1_WDTRESET_BIT 23 #define EXYNOSAUTOV9_CLUSTER0_WDTRESET_BIT 25 #define EXYNOSAUTOV9_CLUSTER1_WDTRESET_BIT 24 +#define EXYNOSAUTOV920_CLUSTER0_WDTRESET_BIT 0 +#define EXYNOSAUTOV920_CLUSTER1_WDTRESET_BIT 1 #define GS_CLUSTER0_NONCPU_OUT 0x1220 #define GS_CLUSTER1_NONCPU_OUT 0x1420 @@ -76,6 +82,10 @@ #define GS_CLUSTER2_NONCPU_INT_EN 0x1644 #define GS_RST_STAT_REG_OFFSET 0x3B44 +#define EXYNOS990_CLUSTER2_NONCPU_OUT 0x1620 +#define EXYNOS990_CLUSTER2_NONCPU_INT_EN 0x1644 +#define EXYNOS990_CLUSTER2_WDTRESET_BIT 23 + /** * DOC: Quirk flags for different Samsung watchdog IP-cores * @@ -253,6 +263,32 @@ static const struct s3c2410_wdt_variant drv_data_exynos850_cl1 = { QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_CNT_EN, }; +static const struct s3c2410_wdt_variant drv_data_exynos990_cl0 = { + .mask_reset_reg = GS_CLUSTER0_NONCPU_INT_EN, + .mask_bit = 2, + .mask_reset_inv = true, + .rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET, + .rst_stat_bit = EXYNOS850_CLUSTER0_WDTRESET_BIT, + .cnt_en_reg = EXYNOSAUTOV920_CLUSTER0_NONCPU_OUT, + .cnt_en_bit = 7, + .quirks = QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_MASK_RESET | + QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_CNT_EN | + QUIRK_HAS_DBGACK_BIT, +}; + +static const struct s3c2410_wdt_variant drv_data_exynos990_cl2 = { + .mask_reset_reg = EXYNOS990_CLUSTER2_NONCPU_INT_EN, + .mask_bit = 2, + .mask_reset_inv = true, + .rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET, + .rst_stat_bit = EXYNOS990_CLUSTER2_WDTRESET_BIT, + .cnt_en_reg = EXYNOS990_CLUSTER2_NONCPU_OUT, + .cnt_en_bit = 7, + .quirks = QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_MASK_RESET | + QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_CNT_EN | + QUIRK_HAS_DBGACK_BIT, +}; + static const struct s3c2410_wdt_variant drv_data_exynosautov9_cl0 = { .mask_reset_reg = EXYNOS850_CLUSTER0_NONCPU_INT_EN, .mask_bit = 2, @@ -303,6 +339,32 @@ static const struct s3c2410_wdt_variant drv_data_gs101_cl1 = { QUIRK_HAS_DBGACK_BIT, }; +static const struct s3c2410_wdt_variant drv_data_exynosautov920_cl0 = { + .mask_reset_reg = EXYNOSAUTOV920_CLUSTER0_NONCPU_INT_EN, + .mask_bit = 2, + .mask_reset_inv = true, + .rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET, + .rst_stat_bit = EXYNOSAUTOV920_CLUSTER0_WDTRESET_BIT, + .cnt_en_reg = EXYNOSAUTOV920_CLUSTER0_NONCPU_OUT, + .cnt_en_bit = 8, + .quirks = QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_MASK_RESET | + QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_CNT_EN | + QUIRK_HAS_DBGACK_BIT, +}; + +static const struct s3c2410_wdt_variant drv_data_exynosautov920_cl1 = { + .mask_reset_reg = EXYNOSAUTOV920_CLUSTER1_NONCPU_INT_EN, + .mask_bit = 2, + .mask_reset_inv = true, + .rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET, + .rst_stat_bit = EXYNOSAUTOV920_CLUSTER1_WDTRESET_BIT, + .cnt_en_reg = EXYNOSAUTOV920_CLUSTER1_NONCPU_OUT, + .cnt_en_bit = 8, + .quirks = QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_MASK_RESET | + QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_CNT_EN | + QUIRK_HAS_DBGACK_BIT, +}; + static const struct of_device_id s3c2410_wdt_match[] = { { .compatible = "google,gs101-wdt", .data = &drv_data_gs101_cl0 }, @@ -318,8 +380,12 @@ static const struct of_device_id s3c2410_wdt_match[] = { .data = &drv_data_exynos7 }, { .compatible = "samsung,exynos850-wdt", .data = &drv_data_exynos850_cl0 }, + { .compatible = "samsung,exynos990-wdt", + .data = &drv_data_exynos990_cl0 }, { .compatible = "samsung,exynosautov9-wdt", .data = &drv_data_exynosautov9_cl0 }, + { .compatible = "samsung,exynosautov920-wdt", + .data = &drv_data_exynosautov920_cl0 }, {}, }; MODULE_DEVICE_TABLE(of, s3c2410_wdt_match); @@ -643,7 +709,9 @@ s3c2410_get_wdt_drv_data(struct platform_device *pdev, struct s3c2410_wdt *wdt) /* Choose Exynos850/ExynosAutov9 driver data w.r.t. cluster index */ if (variant == &drv_data_exynos850_cl0 || variant == &drv_data_exynosautov9_cl0 || - variant == &drv_data_gs101_cl0) { + variant == &drv_data_gs101_cl0 || + variant == &drv_data_exynosautov920_cl0 || + variant == &drv_data_exynos990_cl0) { u32 index; int err; @@ -662,6 +730,12 @@ s3c2410_get_wdt_drv_data(struct platform_device *pdev, struct s3c2410_wdt *wdt) variant = &drv_data_exynosautov9_cl1; else if (variant == &drv_data_gs101_cl0) variant = &drv_data_gs101_cl1; + else if (variant == &drv_data_exynosautov920_cl0) + variant = &drv_data_exynosautov920_cl1; + break; + case 2: + if (variant == &drv_data_exynos990_cl0) + variant = &drv_data_exynos990_cl2; break; default: return dev_err_probe(dev, -EINVAL, "wrong cluster index: %u\n", index); @@ -699,11 +773,11 @@ static int s3c2410wdt_probe(struct platform_device *pdev) return ret; if (wdt->drv_data->quirks & QUIRKS_HAVE_PMUREG) { - wdt->pmureg = exynos_get_pmu_regmap_by_phandle(dev->of_node, - "samsung,syscon-phandle"); + wdt->pmureg = syscon_regmap_lookup_by_phandle(dev->of_node, + "samsung,syscon-phandle"); if (IS_ERR(wdt->pmureg)) return dev_err_probe(dev, PTR_ERR(wdt->pmureg), - "PMU regmap lookup failed.\n"); + "syscon regmap lookup failed.\n"); } wdt_irq = platform_get_irq(pdev, 0); diff --git a/drivers/watchdog/sa1100_wdt.c b/drivers/watchdog/sa1100_wdt.c index 5d2df008b92a..729a8508b31d 100644 --- a/drivers/watchdog/sa1100_wdt.c +++ b/drivers/watchdog/sa1100_wdt.c @@ -164,7 +164,6 @@ static long sa1100dog_ioctl(struct file *file, unsigned int cmd, static const struct file_operations sa1100dog_fops = { .owner = THIS_MODULE, - .llseek = no_llseek, .write = sa1100dog_write, .unlocked_ioctl = sa1100dog_ioctl, .compat_ioctl = compat_ptr_ioctl, @@ -191,9 +190,8 @@ static int sa1100dog_probe(struct platform_device *pdev) if (!res) return -ENXIO; reg_base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); - ret = PTR_ERR_OR_ZERO(reg_base); - if (ret) - return ret; + if (!reg_base) + return -ENOMEM; clk = clk_get(NULL, "OSTIMER0"); if (IS_ERR(clk)) { @@ -238,8 +236,8 @@ static void sa1100dog_remove(struct platform_device *pdev) static struct platform_driver sa1100dog_driver = { .driver.name = "sa1100_wdt", - .probe = sa1100dog_probe, - .remove_new = sa1100dog_remove, + .probe = sa1100dog_probe, + .remove = sa1100dog_remove, }; module_platform_driver(sa1100dog_driver); diff --git a/drivers/watchdog/sb_wdog.c b/drivers/watchdog/sb_wdog.c index 504be461f992..eaa68b54cf56 100644 --- a/drivers/watchdog/sb_wdog.c +++ b/drivers/watchdog/sb_wdog.c @@ -234,7 +234,6 @@ static int sbwdog_notify_sys(struct notifier_block *this, unsigned long code, static const struct file_operations sbwdog_fops = { .owner = THIS_MODULE, - .llseek = no_llseek, .write = sbwdog_write, .unlocked_ioctl = sbwdog_ioctl, .compat_ioctl = compat_ptr_ioctl, diff --git a/drivers/watchdog/sbc60xxwdt.c b/drivers/watchdog/sbc60xxwdt.c index 7b974802dfc7..03eaf48c8f0f 100644 --- a/drivers/watchdog/sbc60xxwdt.c +++ b/drivers/watchdog/sbc60xxwdt.c @@ -146,7 +146,7 @@ static void wdt_startup(void) static void wdt_turnoff(void) { /* Stop the timer */ - del_timer_sync(&timer); + timer_delete_sync(&timer); inb_p(wdt_stop); pr_info("Watchdog timer is now disabled...\n"); } @@ -210,7 +210,7 @@ static int fop_close(struct inode *inode, struct file *file) if (wdt_expect_close == 42) wdt_turnoff(); else { - del_timer(&timer); + timer_delete(&timer); pr_crit("device file closed unexpectedly. Will not stop the WDT!\n"); } clear_bit(0, &wdt_is_open); @@ -275,7 +275,6 @@ static long fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg) static const struct file_operations wdt_fops = { .owner = THIS_MODULE, - .llseek = no_llseek, .write = fop_write, .open = fop_open, .release = fop_close, diff --git a/drivers/watchdog/sbc7240_wdt.c b/drivers/watchdog/sbc7240_wdt.c index d640b26e18a6..21a1f0b32070 100644 --- a/drivers/watchdog/sbc7240_wdt.c +++ b/drivers/watchdog/sbc7240_wdt.c @@ -205,7 +205,6 @@ static long fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg) static const struct file_operations wdt_fops = { .owner = THIS_MODULE, - .llseek = no_llseek, .write = fop_write, .open = fop_open, .release = fop_close, diff --git a/drivers/watchdog/sbc8360.c b/drivers/watchdog/sbc8360.c index 4f8b9912fc51..a9fd1615b4c3 100644 --- a/drivers/watchdog/sbc8360.c +++ b/drivers/watchdog/sbc8360.c @@ -301,7 +301,6 @@ static int sbc8360_notify_sys(struct notifier_block *this, unsigned long code, static const struct file_operations sbc8360_fops = { .owner = THIS_MODULE, - .llseek = no_llseek, .write = sbc8360_write, .open = sbc8360_open, .release = sbc8360_close, diff --git a/drivers/watchdog/sbc_epx_c3.c b/drivers/watchdog/sbc_epx_c3.c index 5e3a9ddb952e..1d291dc0a4a6 100644 --- a/drivers/watchdog/sbc_epx_c3.c +++ b/drivers/watchdog/sbc_epx_c3.c @@ -153,7 +153,6 @@ static int epx_c3_notify_sys(struct notifier_block *this, unsigned long code, static const struct file_operations epx_c3_fops = { .owner = THIS_MODULE, - .llseek = no_llseek, .write = epx_c3_write, .unlocked_ioctl = epx_c3_ioctl, .compat_ioctl = compat_ptr_ioctl, diff --git a/drivers/watchdog/sbc_fitpc2_wdt.c b/drivers/watchdog/sbc_fitpc2_wdt.c index b8eb8d5ca1af..ff9e44825423 100644 --- a/drivers/watchdog/sbc_fitpc2_wdt.c +++ b/drivers/watchdog/sbc_fitpc2_wdt.c @@ -181,7 +181,6 @@ static int fitpc2_wdt_release(struct inode *inode, struct file *file) static const struct file_operations fitpc2_wdt_fops = { .owner = THIS_MODULE, - .llseek = no_llseek, .write = fitpc2_wdt_write, .unlocked_ioctl = fitpc2_wdt_ioctl, .compat_ioctl = compat_ptr_ioctl, diff --git a/drivers/watchdog/sc1200wdt.c b/drivers/watchdog/sc1200wdt.c index f22ebe89fe13..76a58715f665 100644 --- a/drivers/watchdog/sc1200wdt.c +++ b/drivers/watchdog/sc1200wdt.c @@ -304,7 +304,6 @@ static struct notifier_block sc1200wdt_notifier = { static const struct file_operations sc1200wdt_fops = { .owner = THIS_MODULE, - .llseek = no_llseek, .write = sc1200wdt_write, .unlocked_ioctl = sc1200wdt_ioctl, .compat_ioctl = compat_ptr_ioctl, diff --git a/drivers/watchdog/sc520_wdt.c b/drivers/watchdog/sc520_wdt.c index ca65468f4b9c..005f62e4a4fb 100644 --- a/drivers/watchdog/sc520_wdt.c +++ b/drivers/watchdog/sc520_wdt.c @@ -186,7 +186,7 @@ static int wdt_startup(void) static int wdt_turnoff(void) { /* Stop the timer */ - del_timer_sync(&timer); + timer_delete_sync(&timer); /* Stop the watchdog */ wdt_config(0); @@ -331,7 +331,6 @@ static long fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg) static const struct file_operations wdt_fops = { .owner = THIS_MODULE, - .llseek = no_llseek, .write = fop_write, .open = fop_open, .release = fop_close, diff --git a/drivers/watchdog/sch311x_wdt.c b/drivers/watchdog/sch311x_wdt.c index 409d49880170..9670a1ea57cb 100644 --- a/drivers/watchdog/sch311x_wdt.c +++ b/drivers/watchdog/sch311x_wdt.c @@ -334,7 +334,6 @@ static int sch311x_wdt_close(struct inode *inode, struct file *file) static const struct file_operations sch311x_wdt_fops = { .owner = THIS_MODULE, - .llseek = no_llseek, .write = sch311x_wdt_write, .unlocked_ioctl = sch311x_wdt_ioctl, .compat_ioctl = compat_ptr_ioctl, @@ -446,7 +445,7 @@ static void sch311x_wdt_shutdown(struct platform_device *dev) static struct platform_driver sch311x_wdt_driver = { .probe = sch311x_wdt_probe, - .remove_new = sch311x_wdt_remove, + .remove = sch311x_wdt_remove, .shutdown = sch311x_wdt_shutdown, .driver = { .name = DRV_NAME, diff --git a/drivers/watchdog/scx200_wdt.c b/drivers/watchdog/scx200_wdt.c index 7b5e18323f3f..4dd8549e3674 100644 --- a/drivers/watchdog/scx200_wdt.c +++ b/drivers/watchdog/scx200_wdt.c @@ -198,7 +198,6 @@ static long scx200_wdt_ioctl(struct file *file, unsigned int cmd, static const struct file_operations scx200_wdt_fops = { .owner = THIS_MODULE, - .llseek = no_llseek, .write = scx200_wdt_write, .unlocked_ioctl = scx200_wdt_ioctl, .compat_ioctl = compat_ptr_ioctl, diff --git a/drivers/watchdog/shwdt.c b/drivers/watchdog/shwdt.c index 10f1fba78ec2..95af9ad94d16 100644 --- a/drivers/watchdog/shwdt.c +++ b/drivers/watchdog/shwdt.c @@ -129,7 +129,7 @@ static int sh_wdt_stop(struct watchdog_device *wdt_dev) spin_lock_irqsave(&wdt->lock, flags); - del_timer(&wdt->timer); + timer_delete(&wdt->timer); csr = sh_wdt_read_csr(); csr &= ~WTCSR_TME; @@ -297,7 +297,7 @@ static struct platform_driver sh_wdt_driver = { }, .probe = sh_wdt_probe, - .remove_new = sh_wdt_remove, + .remove = sh_wdt_remove, .shutdown = sh_wdt_shutdown, }; diff --git a/drivers/watchdog/simatic-ipc-wdt.c b/drivers/watchdog/simatic-ipc-wdt.c index cdc1a2e15180..1e91f0a560ff 100644 --- a/drivers/watchdog/simatic-ipc-wdt.c +++ b/drivers/watchdog/simatic-ipc-wdt.c @@ -227,6 +227,7 @@ static struct platform_driver simatic_ipc_wdt_driver = { module_platform_driver(simatic_ipc_wdt_driver); +MODULE_DESCRIPTION("Siemens SIMATIC IPC driver for Watchdogs"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:" KBUILD_MODNAME); MODULE_AUTHOR("Gerd Haeussler <gerd.haeussler.ext@siemens.com>"); diff --git a/drivers/watchdog/sl28cpld_wdt.c b/drivers/watchdog/sl28cpld_wdt.c index 9ce456f09f73..8630c29818f2 100644 --- a/drivers/watchdog/sl28cpld_wdt.c +++ b/drivers/watchdog/sl28cpld_wdt.c @@ -198,10 +198,8 @@ static int sl28cpld_wdt_probe(struct platform_device *pdev) } ret = devm_watchdog_register_device(&pdev->dev, wdd); - if (ret < 0) { - dev_err(&pdev->dev, "failed to register watchdog device\n"); + if (ret < 0) return ret; - } dev_info(&pdev->dev, "initial timeout %d sec%s\n", wdd->timeout, nowayout ? ", nowayout" : ""); diff --git a/drivers/watchdog/smsc37b787_wdt.c b/drivers/watchdog/smsc37b787_wdt.c index 7463df479d11..3011e1af00f9 100644 --- a/drivers/watchdog/smsc37b787_wdt.c +++ b/drivers/watchdog/smsc37b787_wdt.c @@ -485,7 +485,7 @@ static long wb_smsc_wdt_ioctl(struct file *file, } } -/* -- Notifier funtions -----------------------------------------*/ +/* -- Notifier functions -----------------------------------------*/ static int wb_smsc_wdt_notify_sys(struct notifier_block *this, unsigned long code, void *unused) @@ -502,7 +502,6 @@ static int wb_smsc_wdt_notify_sys(struct notifier_block *this, static const struct file_operations wb_smsc_wdt_fops = { .owner = THIS_MODULE, - .llseek = no_llseek, .write = wb_smsc_wdt_write, .unlocked_ioctl = wb_smsc_wdt_ioctl, .compat_ioctl = compat_ptr_ioctl, diff --git a/drivers/watchdog/softdog.c b/drivers/watchdog/softdog.c index 7a1096265f18..0820e35ad2e3 100644 --- a/drivers/watchdog/softdog.c +++ b/drivers/watchdog/softdog.c @@ -187,14 +187,12 @@ static int __init softdog_init(void) watchdog_set_nowayout(&softdog_dev, nowayout); watchdog_stop_on_reboot(&softdog_dev); - hrtimer_init(&softdog_ticktock, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - softdog_ticktock.function = softdog_fire; + hrtimer_setup(&softdog_ticktock, softdog_fire, CLOCK_MONOTONIC, HRTIMER_MODE_REL); if (IS_ENABLED(CONFIG_SOFT_WATCHDOG_PRETIMEOUT)) { softdog_info.options |= WDIOF_PRETIMEOUT; - hrtimer_init(&softdog_preticktock, CLOCK_MONOTONIC, - HRTIMER_MODE_REL); - softdog_preticktock.function = softdog_pretimeout; + hrtimer_setup(&softdog_preticktock, softdog_pretimeout, CLOCK_MONOTONIC, + HRTIMER_MODE_REL); } if (soft_active_on_boot) diff --git a/drivers/watchdog/sp805_wdt.c b/drivers/watchdog/sp805_wdt.c index 109e2e37e8f0..c2125f204a13 100644 --- a/drivers/watchdog/sp805_wdt.c +++ b/drivers/watchdog/sp805_wdt.c @@ -62,7 +62,6 @@ * @clk: (optional) clock structure of wdt * @rate: (optional) clock rate when provided via properties * @adev: amba device structure of wdt - * @status: current status of wdt * @load_val: load value to be set for current timeout */ struct sp805_wdt { @@ -128,7 +127,7 @@ static unsigned int wdt_timeleft(struct watchdog_device *wdd) /*If the interrupt is inactive then time left is WDTValue + WDTLoad. */ if (!(readl_relaxed(wdt->base + WDTRIS) & INT_MASK)) - load += wdt->load_val + 1; + load += (u64)wdt->load_val + 1; spin_unlock(&wdt->lock); return div_u64(load, wdt->rate); diff --git a/drivers/watchdog/st_lpc_wdt.c b/drivers/watchdog/st_lpc_wdt.c index 4c5b8d98a4f3..d206452072ae 100644 --- a/drivers/watchdog/st_lpc_wdt.c +++ b/drivers/watchdog/st_lpc_wdt.c @@ -286,7 +286,7 @@ static struct platform_driver st_wdog_driver = { .of_match_table = st_wdog_match, }, .probe = st_wdog_probe, - .remove_new = st_wdog_remove, + .remove = st_wdog_remove, }; module_platform_driver(st_wdog_driver); diff --git a/drivers/watchdog/starfive-wdt.c b/drivers/watchdog/starfive-wdt.c index b4b059883618..355918d62f63 100644 --- a/drivers/watchdog/starfive-wdt.c +++ b/drivers/watchdog/starfive-wdt.c @@ -80,7 +80,7 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); struct starfive_wdt_variant { - unsigned int control; /* Watchdog Control Resgister for reset enable */ + unsigned int control; /* Watchdog Control Register for reset enable */ unsigned int load; /* Watchdog Load register */ unsigned int reload; /* Watchdog Reload Control register */ unsigned int enable; /* Watchdog Enable Register */ @@ -152,8 +152,10 @@ static int starfive_wdt_enable_clock(struct starfive_wdt *wdt) return dev_err_probe(wdt->wdd.parent, ret, "failed to enable apb clock\n"); ret = clk_prepare_enable(wdt->core_clk); - if (ret) + if (ret) { + clk_disable_unprepare(wdt->apb_clk); return dev_err_probe(wdt->wdd.parent, ret, "failed to enable core clock\n"); + } return 0; } @@ -595,7 +597,7 @@ MODULE_DEVICE_TABLE(of, starfive_wdt_match); static struct platform_driver starfive_wdt_driver = { .probe = starfive_wdt_probe, - .remove_new = starfive_wdt_remove, + .remove = starfive_wdt_remove, .shutdown = starfive_wdt_shutdown, .driver = { .name = "starfive-wdt", diff --git a/drivers/watchdog/stm32_iwdg.c b/drivers/watchdog/stm32_iwdg.c index 5404e0387620..b356a272ff9a 100644 --- a/drivers/watchdog/stm32_iwdg.c +++ b/drivers/watchdog/stm32_iwdg.c @@ -18,6 +18,7 @@ #include <linux/module.h> #include <linux/of.h> #include <linux/platform_device.h> +#include <linux/pm_wakeirq.h> #include <linux/watchdog.h> #define DEFAULT_TIMEOUT 10 @@ -28,6 +29,7 @@ #define IWDG_RLR 0x08 /* ReLoad Register */ #define IWDG_SR 0x0C /* Status Register */ #define IWDG_WINR 0x10 /* Windows Register */ +#define IWDG_EWCR 0x14 /* Early Wake-up Register */ /* IWDG_KR register bit mask */ #define KR_KEY_RELOAD 0xAAAA /* reload counter enable */ @@ -47,22 +49,29 @@ #define SR_PVU BIT(0) /* Watchdog prescaler value update */ #define SR_RVU BIT(1) /* Watchdog counter reload value update */ +#define EWCR_EWIT GENMASK(11, 0) /* Watchdog counter window value */ +#define EWCR_EWIC BIT(14) /* Watchdog early interrupt acknowledge */ +#define EWCR_EWIE BIT(15) /* Watchdog early interrupt enable */ + /* set timeout to 100000 us */ #define TIMEOUT_US 100000 #define SLEEP_US 1000 struct stm32_iwdg_data { bool has_pclk; + bool has_early_wakeup; u32 max_prescaler; }; static const struct stm32_iwdg_data stm32_iwdg_data = { .has_pclk = false, + .has_early_wakeup = false, .max_prescaler = 256, }; static const struct stm32_iwdg_data stm32mp1_iwdg_data = { .has_pclk = true, + .has_early_wakeup = true, .max_prescaler = 1024, }; @@ -88,13 +97,18 @@ static inline void reg_write(void __iomem *base, u32 reg, u32 val) static int stm32_iwdg_start(struct watchdog_device *wdd) { struct stm32_iwdg *wdt = watchdog_get_drvdata(wdd); - u32 tout, presc, iwdg_rlr, iwdg_pr, iwdg_sr; + u32 tout, ptot, presc, iwdg_rlr, iwdg_ewcr, iwdg_pr, iwdg_sr; int ret; dev_dbg(wdd->parent, "%s\n", __func__); + if (!wdd->pretimeout) + wdd->pretimeout = 3 * wdd->timeout / 4; + tout = clamp_t(unsigned int, wdd->timeout, wdd->min_timeout, wdd->max_hw_heartbeat_ms / 1000); + ptot = clamp_t(unsigned int, tout - wdd->pretimeout, + wdd->min_timeout, tout); presc = DIV_ROUND_UP(tout * wdt->rate, RLR_MAX + 1); @@ -102,6 +116,7 @@ static int stm32_iwdg_start(struct watchdog_device *wdd) presc = roundup_pow_of_two(presc); iwdg_pr = presc <= 1 << PR_SHIFT ? 0 : ilog2(presc) - PR_SHIFT; iwdg_rlr = ((tout * wdt->rate) / presc) - 1; + iwdg_ewcr = ((ptot * wdt->rate) / presc) - 1; /* enable write access */ reg_write(wdt->regs, IWDG_KR, KR_KEY_EWA); @@ -109,6 +124,8 @@ static int stm32_iwdg_start(struct watchdog_device *wdd) /* set prescaler & reload registers */ reg_write(wdt->regs, IWDG_PR, iwdg_pr); reg_write(wdt->regs, IWDG_RLR, iwdg_rlr); + if (wdt->data->has_early_wakeup) + reg_write(wdt->regs, IWDG_EWCR, iwdg_ewcr | EWCR_EWIE); reg_write(wdt->regs, IWDG_KR, KR_KEY_ENABLE); /* wait for the registers to be updated (max 100ms) */ @@ -151,6 +168,34 @@ static int stm32_iwdg_set_timeout(struct watchdog_device *wdd, return 0; } +static int stm32_iwdg_set_pretimeout(struct watchdog_device *wdd, + unsigned int pretimeout) +{ + dev_dbg(wdd->parent, "%s pretimeout: %d sec\n", __func__, pretimeout); + + wdd->pretimeout = pretimeout; + + if (watchdog_active(wdd)) + return stm32_iwdg_start(wdd); + + return 0; +} + +static irqreturn_t stm32_iwdg_isr(int irq, void *wdog_arg) +{ + struct watchdog_device *wdd = wdog_arg; + struct stm32_iwdg *wdt = watchdog_get_drvdata(wdd); + u32 reg; + + reg = reg_read(wdt->regs, IWDG_EWCR); + reg |= EWCR_EWIC; + reg_write(wdt->regs, IWDG_EWCR, reg); + + watchdog_notify_pretimeout(wdd); + + return IRQ_HANDLED; +} + static void stm32_clk_disable_unprepare(void *data) { clk_disable_unprepare(data); @@ -207,11 +252,20 @@ static const struct watchdog_info stm32_iwdg_info = { .identity = "STM32 Independent Watchdog", }; +static const struct watchdog_info stm32_iwdg_preinfo = { + .options = WDIOF_SETTIMEOUT | + WDIOF_MAGICCLOSE | + WDIOF_KEEPALIVEPING | + WDIOF_PRETIMEOUT, + .identity = "STM32 Independent Watchdog", +}; + static const struct watchdog_ops stm32_iwdg_ops = { .owner = THIS_MODULE, .start = stm32_iwdg_start, .ping = stm32_iwdg_ping, .set_timeout = stm32_iwdg_set_timeout, + .set_pretimeout = stm32_iwdg_set_pretimeout, }; static const struct of_device_id stm32_iwdg_of_match[] = { @@ -221,6 +275,40 @@ static const struct of_device_id stm32_iwdg_of_match[] = { }; MODULE_DEVICE_TABLE(of, stm32_iwdg_of_match); +static int stm32_iwdg_irq_init(struct platform_device *pdev, + struct stm32_iwdg *wdt) +{ + struct device_node *np = pdev->dev.of_node; + struct watchdog_device *wdd = &wdt->wdd; + struct device *dev = &pdev->dev; + int irq, ret; + + if (!wdt->data->has_early_wakeup) + return 0; + + irq = platform_get_irq_optional(pdev, 0); + if (irq <= 0) + return 0; + + if (of_property_read_bool(np, "wakeup-source")) { + ret = devm_device_init_wakeup(dev); + if (ret) + return ret; + + ret = dev_pm_set_wake_irq(dev, irq); + if (ret) + return ret; + } + + ret = devm_request_irq(dev, irq, stm32_iwdg_isr, 0, + dev_name(dev), wdd); + if (ret) + return ret; + + wdd->info = &stm32_iwdg_preinfo; + return 0; +} + static int stm32_iwdg_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -255,6 +343,11 @@ static int stm32_iwdg_probe(struct platform_device *pdev) wdd->max_hw_heartbeat_ms = ((RLR_MAX + 1) * wdt->data->max_prescaler * 1000) / wdt->rate; + /* Initialize IRQ, this might override wdd->info, hence it is here. */ + ret = stm32_iwdg_irq_init(pdev, wdt); + if (ret) + return ret; + watchdog_set_drvdata(wdd, wdt); watchdog_set_nowayout(wdd, WATCHDOG_NOWAYOUT); watchdog_init_timeout(wdd, 0, dev); diff --git a/drivers/watchdog/stmp3xxx_rtc_wdt.c b/drivers/watchdog/stmp3xxx_rtc_wdt.c index 4b2caa9807ac..060447101f48 100644 --- a/drivers/watchdog/stmp3xxx_rtc_wdt.c +++ b/drivers/watchdog/stmp3xxx_rtc_wdt.c @@ -143,7 +143,7 @@ static struct platform_driver stmp3xxx_wdt_driver = { .pm = &stmp3xxx_wdt_pm_ops, }, .probe = stmp3xxx_wdt_probe, - .remove_new = stmp3xxx_wdt_remove, + .remove = stmp3xxx_wdt_remove, }; module_platform_driver(stmp3xxx_wdt_driver); diff --git a/drivers/watchdog/sunxi_wdt.c b/drivers/watchdog/sunxi_wdt.c index b85354a99582..b6c761acc3de 100644 --- a/drivers/watchdog/sunxi_wdt.c +++ b/drivers/watchdog/sunxi_wdt.c @@ -236,10 +236,21 @@ static const struct sunxi_wdt_reg sun20i_wdt_reg = { .wdt_key_val = 0x16aa0000, }; +static const struct sunxi_wdt_reg sun55i_wdt_reg = { + .wdt_ctrl = 0x0c, + .wdt_cfg = 0x10, + .wdt_mode = 0x14, + .wdt_timeout_shift = 4, + .wdt_reset_mask = 0x03, + .wdt_reset_val = 0x01, + .wdt_key_val = 0x16aa0000, +}; + static const struct of_device_id sunxi_wdt_dt_ids[] = { { .compatible = "allwinner,sun4i-a10-wdt", .data = &sun4i_wdt_reg }, { .compatible = "allwinner,sun6i-a31-wdt", .data = &sun6i_wdt_reg }, { .compatible = "allwinner,sun20i-d1-wdt", .data = &sun20i_wdt_reg }, + { .compatible = "allwinner,sun55i-a523-wdt", .data = &sun55i_wdt_reg }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, sunxi_wdt_dt_ids); diff --git a/drivers/watchdog/ts4800_wdt.c b/drivers/watchdog/ts4800_wdt.c index 0099403f4992..24b1ad52102e 100644 --- a/drivers/watchdog/ts4800_wdt.c +++ b/drivers/watchdog/ts4800_wdt.c @@ -200,5 +200,6 @@ static struct platform_driver ts4800_wdt_driver = { module_platform_driver(ts4800_wdt_driver); MODULE_AUTHOR("Damien Riegel <damien.riegel@savoirfairelinux.com>"); +MODULE_DESCRIPTION("Watchdog driver for TS-4800 based boards"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:ts4800_wdt"); diff --git a/drivers/watchdog/ts72xx_wdt.c b/drivers/watchdog/ts72xx_wdt.c index 3d57670befe1..ac709dc31a65 100644 --- a/drivers/watchdog/ts72xx_wdt.c +++ b/drivers/watchdog/ts72xx_wdt.c @@ -12,6 +12,7 @@ */ #include <linux/platform_device.h> +#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/watchdog.h> #include <linux/io.h> @@ -160,10 +161,17 @@ static int ts72xx_wdt_probe(struct platform_device *pdev) return 0; } +static const struct of_device_id ts72xx_wdt_of_ids[] = { + { .compatible = "technologic,ts7200-wdt" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, ts72xx_wdt_of_ids); + static struct platform_driver ts72xx_wdt_driver = { .probe = ts72xx_wdt_probe, .driver = { .name = "ts72xx-wdt", + .of_match_table = ts72xx_wdt_of_ids, }, }; diff --git a/drivers/watchdog/twl4030_wdt.c b/drivers/watchdog/twl4030_wdt.c index 09d17e20f4a7..8c80d04811e4 100644 --- a/drivers/watchdog/twl4030_wdt.c +++ b/drivers/watchdog/twl4030_wdt.c @@ -118,6 +118,7 @@ static struct platform_driver twl4030_wdt_driver = { module_platform_driver(twl4030_wdt_driver); MODULE_AUTHOR("Nokia Corporation"); +MODULE_DESCRIPTION("TWL4030 Watchdog"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:twl4030_wdt"); diff --git a/drivers/watchdog/txx9wdt.c b/drivers/watchdog/txx9wdt.c index 8d5f67acbff2..305349844b4f 100644 --- a/drivers/watchdog/txx9wdt.c +++ b/drivers/watchdog/txx9wdt.c @@ -159,7 +159,7 @@ static void txx9wdt_shutdown(struct platform_device *dev) static struct platform_driver txx9wdt_driver = { .probe = txx9wdt_probe, - .remove_new = txx9wdt_remove, + .remove = txx9wdt_remove, .shutdown = txx9wdt_shutdown, .driver = { .name = "txx9wdt", diff --git a/drivers/watchdog/via_wdt.c b/drivers/watchdog/via_wdt.c index eeb39f96e72e..d647923d68fe 100644 --- a/drivers/watchdog/via_wdt.c +++ b/drivers/watchdog/via_wdt.c @@ -233,7 +233,7 @@ err_out_disable_device: static void wdt_remove(struct pci_dev *pdev) { watchdog_unregister_device(&wdt_dev); - del_timer_sync(&timer); + timer_delete_sync(&timer); iounmap(wdt_mem); release_mem_region(mmio, VIA_WDT_MMIO_LEN); release_resource(&wdt_res); diff --git a/drivers/watchdog/w83877f_wdt.c b/drivers/watchdog/w83877f_wdt.c index f2650863fd02..53db59ef774b 100644 --- a/drivers/watchdog/w83877f_wdt.c +++ b/drivers/watchdog/w83877f_wdt.c @@ -166,7 +166,7 @@ static void wdt_startup(void) static void wdt_turnoff(void) { /* Stop the timer */ - del_timer_sync(&timer); + timer_delete_sync(&timer); wdt_change(WDT_DISABLE); @@ -228,7 +228,7 @@ static int fop_close(struct inode *inode, struct file *file) if (wdt_expect_close == 42) wdt_turnoff(); else { - del_timer(&timer); + timer_delete(&timer); pr_crit("device file closed unexpectedly. Will not stop the WDT!\n"); } clear_bit(0, &wdt_is_open); @@ -299,7 +299,6 @@ static long fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg) static const struct file_operations wdt_fops = { .owner = THIS_MODULE, - .llseek = no_llseek, .write = fop_write, .open = fop_open, .release = fop_close, diff --git a/drivers/watchdog/w83977f_wdt.c b/drivers/watchdog/w83977f_wdt.c index 31bf21ceaf48..3776030fa7c6 100644 --- a/drivers/watchdog/w83977f_wdt.c +++ b/drivers/watchdog/w83977f_wdt.c @@ -443,7 +443,6 @@ static int wdt_notify_sys(struct notifier_block *this, unsigned long code, static const struct file_operations wdt_fops = { .owner = THIS_MODULE, - .llseek = no_llseek, .write = wdt_write, .unlocked_ioctl = wdt_ioctl, .compat_ioctl = compat_ptr_ioctl, diff --git a/drivers/watchdog/wafer5823wdt.c b/drivers/watchdog/wafer5823wdt.c index a8a1ed215e1e..291109349e73 100644 --- a/drivers/watchdog/wafer5823wdt.c +++ b/drivers/watchdog/wafer5823wdt.c @@ -227,7 +227,6 @@ static int wafwdt_notify_sys(struct notifier_block *this, unsigned long code, static const struct file_operations wafwdt_fops = { .owner = THIS_MODULE, - .llseek = no_llseek, .write = wafwdt_write, .unlocked_ioctl = wafwdt_ioctl, .compat_ioctl = compat_ptr_ioctl, diff --git a/drivers/watchdog/watchdog_core.c b/drivers/watchdog/watchdog_core.c index aff2c3912ead..6152dba4b52c 100644 --- a/drivers/watchdog/watchdog_core.c +++ b/drivers/watchdog/watchdog_core.c @@ -33,7 +33,8 @@ #include <linux/init.h> /* For __init/__exit/... */ #include <linux/idr.h> /* For ida_* macros */ #include <linux/err.h> /* For IS_ERR macros */ -#include <linux/of.h> /* For of_get_timeout_sec */ +#include <linux/of.h> /* For of_alias_get_id */ +#include <linux/property.h> /* For device_property_read_u32 */ #include <linux/suspend.h> #include "watchdog_core.h" /* For watchdog_dev_register/... */ @@ -137,8 +138,7 @@ int watchdog_init_timeout(struct watchdog_device *wdd, } /* try to get the timeout_sec property */ - if (dev && dev->of_node && - of_property_read_u32(dev->of_node, "timeout-sec", &t) == 0) { + if (dev && device_property_read_u32(dev, "timeout-sec", &t) == 0) { if (t && !watchdog_timeout_invalid(wdd, t)) { wdd->timeout = t; return 0; @@ -237,7 +237,7 @@ void watchdog_set_restart_priority(struct watchdog_device *wdd, int priority) } EXPORT_SYMBOL_GPL(watchdog_set_restart_priority); -static int __watchdog_register_device(struct watchdog_device *wdd) +static int ___watchdog_register_device(struct watchdog_device *wdd) { int ret, id = -1; @@ -337,6 +337,22 @@ static int __watchdog_register_device(struct watchdog_device *wdd) return 0; } +static int __watchdog_register_device(struct watchdog_device *wdd) +{ + const char *dev_str; + int ret; + + ret = ___watchdog_register_device(wdd); + if (ret) { + dev_str = wdd->parent ? dev_name(wdd->parent) : + (const char *)wdd->info->identity; + pr_err("%s: failed to register watchdog device (err = %d)\n", + dev_str, ret); + } + + return ret; +} + /** * watchdog_register_device() - register a watchdog device * @wdd: watchdog device @@ -350,7 +366,6 @@ static int __watchdog_register_device(struct watchdog_device *wdd) int watchdog_register_device(struct watchdog_device *wdd) { - const char *dev_str; int ret = 0; mutex_lock(&wtd_deferred_reg_mutex); @@ -360,13 +375,6 @@ int watchdog_register_device(struct watchdog_device *wdd) watchdog_deferred_registration_add(wdd); mutex_unlock(&wtd_deferred_reg_mutex); - if (ret) { - dev_str = wdd->parent ? dev_name(wdd->parent) : - (const char *)wdd->info->identity; - pr_err("%s: failed to register watchdog device (err = %d)\n", - dev_str, ret); - } - return ret; } EXPORT_SYMBOL_GPL(watchdog_register_device); diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c index e2bd266b1b5b..8369fd94fc1a 100644 --- a/drivers/watchdog/watchdog_dev.c +++ b/drivers/watchdog/watchdog_dev.c @@ -1004,7 +1004,7 @@ static struct miscdevice watchdog_miscdev = { .fops = &watchdog_fops, }; -static struct class watchdog_class = { +static const struct class watchdog_class = { .name = "watchdog", .dev_groups = wdt_groups, }; @@ -1051,8 +1051,8 @@ static int watchdog_cdev_register(struct watchdog_device *wdd) } kthread_init_work(&wd_data->work, watchdog_ping_work); - hrtimer_init(&wd_data->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_HARD); - wd_data->timer.function = watchdog_timer_expired; + hrtimer_setup(&wd_data->timer, watchdog_timer_expired, CLOCK_MONOTONIC, + HRTIMER_MODE_REL_HARD); watchdog_hrtimer_pretimeout_init(wdd); if (wdd->id == 0) { @@ -1229,7 +1229,7 @@ int __init watchdog_dev_init(void) { int err; - watchdog_kworker = kthread_create_worker(0, "watchdogd"); + watchdog_kworker = kthread_run_worker(0, "watchdogd"); if (IS_ERR(watchdog_kworker)) { pr_err("Failed to create watchdog kworker\n"); return PTR_ERR(watchdog_kworker); diff --git a/drivers/watchdog/watchdog_hrtimer_pretimeout.c b/drivers/watchdog/watchdog_hrtimer_pretimeout.c index 940b53718a91..fbc7eecd8b20 100644 --- a/drivers/watchdog/watchdog_hrtimer_pretimeout.c +++ b/drivers/watchdog/watchdog_hrtimer_pretimeout.c @@ -23,8 +23,8 @@ void watchdog_hrtimer_pretimeout_init(struct watchdog_device *wdd) { struct watchdog_core_data *wd_data = wdd->wd_data; - hrtimer_init(&wd_data->pretimeout_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - wd_data->pretimeout_timer.function = watchdog_hrtimer_pretimeout; + hrtimer_setup(&wd_data->pretimeout_timer, watchdog_hrtimer_pretimeout, CLOCK_MONOTONIC, + HRTIMER_MODE_REL); } void watchdog_hrtimer_pretimeout_start(struct watchdog_device *wdd) diff --git a/drivers/watchdog/wdrtas.c b/drivers/watchdog/wdrtas.c index c00627825de8..d4fe0bc82211 100644 --- a/drivers/watchdog/wdrtas.c +++ b/drivers/watchdog/wdrtas.c @@ -469,7 +469,6 @@ static int wdrtas_reboot(struct notifier_block *this, static const struct file_operations wdrtas_fops = { .owner = THIS_MODULE, - .llseek = no_llseek, .write = wdrtas_write, .unlocked_ioctl = wdrtas_ioctl, .compat_ioctl = compat_ptr_ioctl, @@ -485,7 +484,6 @@ static struct miscdevice wdrtas_miscdev = { static const struct file_operations wdrtas_temp_fops = { .owner = THIS_MODULE, - .llseek = no_llseek, .read = wdrtas_temp_read, .open = wdrtas_temp_open, .release = wdrtas_temp_close, diff --git a/drivers/watchdog/wdt.c b/drivers/watchdog/wdt.c index 183876156243..3980d60bacd8 100644 --- a/drivers/watchdog/wdt.c +++ b/drivers/watchdog/wdt.c @@ -520,7 +520,6 @@ static int wdt_notify_sys(struct notifier_block *this, unsigned long code, static const struct file_operations wdt_fops = { .owner = THIS_MODULE, - .llseek = no_llseek, .write = wdt_write, .unlocked_ioctl = wdt_ioctl, .compat_ioctl = compat_ptr_ioctl, @@ -536,7 +535,6 @@ static struct miscdevice wdt_miscdev = { static const struct file_operations wdt_temp_fops = { .owner = THIS_MODULE, - .llseek = no_llseek, .read = wdt_temp_read, .open = wdt_temp_open, .release = wdt_temp_release, diff --git a/drivers/watchdog/wdt285.c b/drivers/watchdog/wdt285.c index 5b7be7a62d54..78681d9f7d53 100644 --- a/drivers/watchdog/wdt285.c +++ b/drivers/watchdog/wdt285.c @@ -178,7 +178,6 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd, static const struct file_operations watchdog_fops = { .owner = THIS_MODULE, - .llseek = no_llseek, .write = watchdog_write, .unlocked_ioctl = watchdog_ioctl, .compat_ioctl = compat_ptr_ioctl, diff --git a/drivers/watchdog/wdt977.c b/drivers/watchdog/wdt977.c index c9b8e863f70f..4f449ac4dda4 100644 --- a/drivers/watchdog/wdt977.c +++ b/drivers/watchdog/wdt977.c @@ -419,7 +419,6 @@ static int wdt977_notify_sys(struct notifier_block *this, unsigned long code, static const struct file_operations wdt977_fops = { .owner = THIS_MODULE, - .llseek = no_llseek, .write = wdt977_write, .unlocked_ioctl = wdt977_ioctl, .compat_ioctl = compat_ptr_ioctl, diff --git a/drivers/watchdog/wdt_pci.c b/drivers/watchdog/wdt_pci.c index d5e56b601351..3918a600f2a0 100644 --- a/drivers/watchdog/wdt_pci.c +++ b/drivers/watchdog/wdt_pci.c @@ -264,7 +264,7 @@ static int wdtpci_get_status(int *status) return 0; } -/** +/* * wdtpci_get_temperature: * * Reports the temperature in degrees Fahrenheit. The API is in @@ -563,7 +563,6 @@ static int wdtpci_notify_sys(struct notifier_block *this, unsigned long code, static const struct file_operations wdtpci_fops = { .owner = THIS_MODULE, - .llseek = no_llseek, .write = wdtpci_write, .unlocked_ioctl = wdtpci_ioctl, .compat_ioctl = compat_ptr_ioctl, @@ -579,7 +578,6 @@ static struct miscdevice wdtpci_miscdev = { static const struct file_operations wdtpci_temp_fops = { .owner = THIS_MODULE, - .llseek = no_llseek, .read = wdtpci_temp_read, .open = wdtpci_temp_open, .release = wdtpci_temp_release, diff --git a/drivers/watchdog/xilinx_wwdt.c b/drivers/watchdog/xilinx_wwdt.c index d271e2e8d6e2..3d2a156f7180 100644 --- a/drivers/watchdog/xilinx_wwdt.c +++ b/drivers/watchdog/xilinx_wwdt.c @@ -2,7 +2,7 @@ /* * Window watchdog device driver for Xilinx Versal WWDT * - * Copyright (C) 2022 - 2023, Advanced Micro Devices, Inc. + * Copyright (C) 2022 - 2024, Advanced Micro Devices, Inc. */ #include <linux/clk.h> @@ -36,6 +36,12 @@ #define XWWDT_CLOSE_WINDOW_PERCENT 50 +/* Maximum count value of each 32 bit window */ +#define XWWDT_MAX_COUNT_WINDOW GENMASK(31, 0) + +/* Maximum count value of closed and open window combined */ +#define XWWDT_MAX_COUNT_WINDOW_COMBINED GENMASK_ULL(32, 1) + static int wwdt_timeout; static int closed_window_percent; @@ -54,6 +60,8 @@ MODULE_PARM_DESC(closed_window_percent, * @xilinx_wwdt_wdd: watchdog device structure * @freq: source clock frequency of WWDT * @close_percent: Closed window percent + * @closed_timeout: Closed window timeout in ticks + * @open_timeout: Open window timeout in ticks */ struct xwwdt_device { void __iomem *base; @@ -61,27 +69,22 @@ struct xwwdt_device { struct watchdog_device xilinx_wwdt_wdd; unsigned long freq; u32 close_percent; + u64 closed_timeout; + u64 open_timeout; }; static int xilinx_wwdt_start(struct watchdog_device *wdd) { struct xwwdt_device *xdev = watchdog_get_drvdata(wdd); struct watchdog_device *xilinx_wwdt_wdd = &xdev->xilinx_wwdt_wdd; - u64 time_out, closed_timeout, open_timeout; u32 control_status_reg; - /* Calculate timeout count */ - time_out = xdev->freq * wdd->timeout; - closed_timeout = div_u64(time_out * xdev->close_percent, 100); - open_timeout = time_out - closed_timeout; - wdd->min_hw_heartbeat_ms = xdev->close_percent * 10 * wdd->timeout; - spin_lock(&xdev->spinlock); iowrite32(XWWDT_MWR_MASK, xdev->base + XWWDT_MWR_OFFSET); iowrite32(~(u32)XWWDT_ESR_WEN_MASK, xdev->base + XWWDT_ESR_OFFSET); - iowrite32((u32)closed_timeout, xdev->base + XWWDT_FWR_OFFSET); - iowrite32((u32)open_timeout, xdev->base + XWWDT_SWR_OFFSET); + iowrite32((u32)xdev->closed_timeout, xdev->base + XWWDT_FWR_OFFSET); + iowrite32((u32)xdev->open_timeout, xdev->base + XWWDT_SWR_OFFSET); /* Enable the window watchdog timer */ control_status_reg = ioread32(xdev->base + XWWDT_ESR_OFFSET); @@ -133,7 +136,12 @@ static int xwwdt_probe(struct platform_device *pdev) struct watchdog_device *xilinx_wwdt_wdd; struct device *dev = &pdev->dev; struct xwwdt_device *xdev; + u64 max_per_window_ms; + u64 min_per_window_ms; + u64 timeout_count; struct clk *clk; + u32 timeout_ms; + u64 ms_count; int ret; xdev = devm_kzalloc(dev, sizeof(*xdev), GFP_KERNEL); @@ -154,12 +162,13 @@ static int xwwdt_probe(struct platform_device *pdev) return PTR_ERR(clk); xdev->freq = clk_get_rate(clk); - if (!xdev->freq) + if (xdev->freq < 1000000) return -EINVAL; xilinx_wwdt_wdd->min_timeout = XWWDT_MIN_TIMEOUT; xilinx_wwdt_wdd->timeout = XWWDT_DEFAULT_TIMEOUT; - xilinx_wwdt_wdd->max_hw_heartbeat_ms = 1000 * xilinx_wwdt_wdd->timeout; + xilinx_wwdt_wdd->max_hw_heartbeat_ms = + div64_u64(XWWDT_MAX_COUNT_WINDOW_COMBINED, xdev->freq) * 1000; if (closed_window_percent == 0 || closed_window_percent >= 100) xdev->close_percent = XWWDT_CLOSE_WINDOW_PERCENT; @@ -167,6 +176,48 @@ static int xwwdt_probe(struct platform_device *pdev) xdev->close_percent = closed_window_percent; watchdog_init_timeout(xilinx_wwdt_wdd, wwdt_timeout, &pdev->dev); + + /* Calculate ticks for 1 milli-second */ + ms_count = div_u64(xdev->freq, 1000); + timeout_ms = xilinx_wwdt_wdd->timeout * 1000; + timeout_count = timeout_ms * ms_count; + + if (timeout_ms > xilinx_wwdt_wdd->max_hw_heartbeat_ms) { + /* + * To avoid ping restrictions until the minimum hardware heartbeat, + * we will solely rely on the open window and + * adjust the minimum hardware heartbeat to 0. + */ + xdev->closed_timeout = 0; + xdev->open_timeout = XWWDT_MAX_COUNT_WINDOW; + xilinx_wwdt_wdd->min_hw_heartbeat_ms = 0; + xilinx_wwdt_wdd->max_hw_heartbeat_ms = xilinx_wwdt_wdd->max_hw_heartbeat_ms / 2; + } else { + xdev->closed_timeout = div64_u64(timeout_count * xdev->close_percent, 100); + xilinx_wwdt_wdd->min_hw_heartbeat_ms = + div64_u64(timeout_ms * xdev->close_percent, 100); + + if (timeout_ms > xilinx_wwdt_wdd->max_hw_heartbeat_ms / 2) { + max_per_window_ms = xilinx_wwdt_wdd->max_hw_heartbeat_ms / 2; + min_per_window_ms = timeout_ms - max_per_window_ms; + + if (xilinx_wwdt_wdd->min_hw_heartbeat_ms > max_per_window_ms) { + dev_info(xilinx_wwdt_wdd->parent, + "Closed window cannot be set to %d%%. Using maximum supported value.\n", + xdev->close_percent); + xdev->closed_timeout = max_per_window_ms * ms_count; + xilinx_wwdt_wdd->min_hw_heartbeat_ms = max_per_window_ms; + } else if (xilinx_wwdt_wdd->min_hw_heartbeat_ms < min_per_window_ms) { + dev_info(xilinx_wwdt_wdd->parent, + "Closed window cannot be set to %d%%. Using minimum supported value.\n", + xdev->close_percent); + xdev->closed_timeout = min_per_window_ms * ms_count; + xilinx_wwdt_wdd->min_hw_heartbeat_ms = min_per_window_ms; + } + } + xdev->open_timeout = timeout_count - xdev->closed_timeout; + } + spin_lock_init(&xdev->spinlock); watchdog_set_drvdata(xilinx_wwdt_wdd, xdev); watchdog_set_nowayout(xilinx_wwdt_wdd, 1); diff --git a/drivers/watchdog/ziirave_wdt.c b/drivers/watchdog/ziirave_wdt.c index 5ed33df68e9a..fcc1ba02e75b 100644 --- a/drivers/watchdog/ziirave_wdt.c +++ b/drivers/watchdog/ziirave_wdt.c @@ -20,7 +20,7 @@ #include <linux/types.h> #include <linux/watchdog.h> -#include <asm/unaligned.h> +#include <linux/unaligned.h> #define ZIIRAVE_TIMEOUT_MIN 3 #define ZIIRAVE_TIMEOUT_MAX 255 @@ -715,7 +715,7 @@ static void ziirave_wdt_remove(struct i2c_client *client) } static const struct i2c_device_id ziirave_wdt_id[] = { - { "rave-wdt", 0 }, + { "rave-wdt" }, { } }; MODULE_DEVICE_TABLE(i2c, ziirave_wdt_id); |