diff options
Diffstat (limited to 'drivers/watchdog')
89 files changed, 1259 insertions, 1121 deletions
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 8188963a405b..cec868f8db3f 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -477,13 +477,6 @@ config IXP4XX_WATCHDOG Say N if you are unsure. -config KS8695_WATCHDOG - tristate "KS8695 watchdog" - depends on ARCH_KS8695 - help - Watchdog timer embedded into KS8695 processor. This will reboot your - system when the timeout is reached. - config HAVE_S3C2410_WATCHDOG bool help @@ -551,7 +544,7 @@ config OMAP_WATCHDOG config PNX4008_WATCHDOG tristate "LPC32XX Watchdog" - depends on ARCH_LPC32XX + depends on ARCH_LPC32XX || COMPILE_TEST select WATCHDOG_CORE help Say Y here if to include support for the watchdog timer @@ -662,15 +655,6 @@ config STMP3XXX_RTC_WATCHDOG To compile this driver as a module, choose M here: the module will be called stmp3xxx_rtc_wdt. -config NUC900_WATCHDOG - tristate "Nuvoton NUC900 watchdog" - depends on ARCH_W90X900 || COMPILE_TEST - help - Say Y here if to include support for the watchdog timer - for the Nuvoton NUC900 series SoCs. - To compile this driver as a module, choose M here: the - module will be called nuc900_wdt. - config TS4800_WATCHDOG tristate "TS-4800 Watchdog" depends on HAS_IOMEM && OF @@ -703,6 +687,7 @@ config MAX63XX_WATCHDOG config MAX77620_WATCHDOG tristate "Maxim Max77620 Watchdog Timer" depends on MFD_MAX77620 || COMPILE_TEST + select WATCHDOG_CORE help This is the driver for the Max77620 watchdog timer. Say 'Y' here to enable the watchdog timer support for @@ -740,6 +725,19 @@ config IMX_SC_WDT To compile this driver as a module, choose M here: the module will be called imx_sc_wdt. +config IMX7ULP_WDT + tristate "IMX7ULP Watchdog" + depends on ARCH_MXC || COMPILE_TEST + select WATCHDOG_CORE + help + This is the driver for the hardware watchdog on the Freescale + IMX7ULP and later processors. If you have one of these + processors and wish to have watchdog support enabled, + say Y, otherwise say N. + + To compile this driver as a module, choose M here: the + module will be called imx7ulp_wdt. + config UX500_WATCHDOG tristate "ST-Ericsson Ux500 watchdog" depends on MFD_DB8500_PRCMU @@ -1046,8 +1044,8 @@ config F71808E_WDT depends on X86 help This is the driver for the hardware watchdog on the Fintek F71808E, - F71862FG, F71868, F71869, F71882FG, F71889FG, F81865 and F81866 - Super I/O controllers. + F71862FG, F71868, F71869, F71882FG, F71889FG, F81803, F81865, and + F81866 Super I/O controllers. You can compile this driver directly into the kernel, or use it as a module. The module will be called f71808e_wdt. @@ -1447,6 +1445,7 @@ config SMSC37B787_WDT config TQMX86_WDT tristate "TQ-Systems TQMX86 Watchdog Timer" depends on X86 + select WATCHDOG_CORE help This is the driver for the hardware watchdog timer in the TQMX86 IO controller found on some of their ComExpress Modules. @@ -1488,6 +1487,7 @@ config W83627HF_WDT NCT6791 NCT6792 NCT6102D/04D/06D + NCT6116D This watchdog simply watches your kernel to make sure it doesn't freeze, and if it does, it reboots your computer after a certain @@ -1644,8 +1644,10 @@ config INDYDOG config JZ4740_WDT tristate "Ingenic jz4740 SoC hardware watchdog" - depends on MACH_JZ4740 || MACH_JZ4780 + depends on MIPS + depends on COMMON_CLK select WATCHDOG_CORE + select MFD_SYSCON help Hardware driver for the built-in watchdog timer on Ingenic jz4740 SoCs. diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 7caa920e7e60..2ee352bf3372 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -49,7 +49,6 @@ obj-$(CONFIG_21285_WATCHDOG) += wdt285.o obj-$(CONFIG_977_WATCHDOG) += wdt977.o obj-$(CONFIG_FTWDT010_WATCHDOG) += ftwdt010_wdt.o obj-$(CONFIG_IXP4XX_WATCHDOG) += ixp4xx_wdt.o -obj-$(CONFIG_KS8695_WATCHDOG) += ks8695_wdt.o obj-$(CONFIG_S3C2410_WATCHDOG) += s3c2410_wdt.o obj-$(CONFIG_SA1100_WATCHDOG) += sa1100_wdt.o obj-$(CONFIG_SAMA5D4_WATCHDOG) += sama5d4_wdt.o @@ -64,11 +63,11 @@ obj-$(CONFIG_RN5T618_WATCHDOG) += rn5t618_wdt.o obj-$(CONFIG_COH901327_WATCHDOG) += coh901327_wdt.o obj-$(CONFIG_NPCM7XX_WATCHDOG) += npcm_wdt.o obj-$(CONFIG_STMP3XXX_RTC_WATCHDOG) += stmp3xxx_rtc_wdt.o -obj-$(CONFIG_NUC900_WATCHDOG) += nuc900_wdt.o obj-$(CONFIG_TS4800_WATCHDOG) += ts4800_wdt.o 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_UX500_WATCHDOG) += ux500_wdt.o obj-$(CONFIG_RETU_WATCHDOG) += retu_wdt.o obj-$(CONFIG_BCM2835_WDT) += bcm2835_wdt.o diff --git a/drivers/watchdog/acquirewdt.c b/drivers/watchdog/acquirewdt.c index 848db958411e..bc6f333565d3 100644 --- a/drivers/watchdog/acquirewdt.c +++ b/drivers/watchdog/acquirewdt.c @@ -221,6 +221,7 @@ static const struct file_operations acq_fops = { .llseek = no_llseek, .write = acq_write, .unlocked_ioctl = acq_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = acq_open, .release = acq_close, }; diff --git a/drivers/watchdog/advantechwdt.c b/drivers/watchdog/advantechwdt.c index 0d02bb275b3d..0e4c18a2aa42 100644 --- a/drivers/watchdog/advantechwdt.c +++ b/drivers/watchdog/advantechwdt.c @@ -220,6 +220,7 @@ static const struct file_operations advwdt_fops = { .llseek = no_llseek, .write = advwdt_write, .unlocked_ioctl = advwdt_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = advwdt_open, .release = advwdt_close, }; diff --git a/drivers/watchdog/alim1535_wdt.c b/drivers/watchdog/alim1535_wdt.c index c157dd3d92a3..42338c7d4540 100644 --- a/drivers/watchdog/alim1535_wdt.c +++ b/drivers/watchdog/alim1535_wdt.c @@ -362,6 +362,7 @@ static const struct file_operations ali_fops = { .llseek = no_llseek, .write = ali_write, .unlocked_ioctl = ali_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = ali_open, .release = ali_release, }; diff --git a/drivers/watchdog/alim7101_wdt.c b/drivers/watchdog/alim7101_wdt.c index c8e3ab056767..5af0358f4390 100644 --- a/drivers/watchdog/alim7101_wdt.c +++ b/drivers/watchdog/alim7101_wdt.c @@ -294,6 +294,7 @@ static const struct file_operations wdt_fops = { .open = fop_open, .release = fop_close, .unlocked_ioctl = fop_ioctl, + .compat_ioctl = compat_ptr_ioctl, }; static struct miscdevice wdt_miscdev = { diff --git a/drivers/watchdog/ar7_wdt.c b/drivers/watchdog/ar7_wdt.c index 668a1c704f28..c087027ffd5d 100644 --- a/drivers/watchdog/ar7_wdt.c +++ b/drivers/watchdog/ar7_wdt.c @@ -250,6 +250,7 @@ static const struct file_operations ar7_wdt_fops = { .owner = THIS_MODULE, .write = ar7_wdt_write, .unlocked_ioctl = ar7_wdt_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = ar7_wdt_open, .release = ar7_wdt_release, .llseek = no_llseek, diff --git a/drivers/watchdog/aspeed_wdt.c b/drivers/watchdog/aspeed_wdt.c index cc71861e033a..7e00960651fa 100644 --- a/drivers/watchdog/aspeed_wdt.c +++ b/drivers/watchdog/aspeed_wdt.c @@ -34,6 +34,7 @@ static const struct aspeed_wdt_config ast2500_config = { static const struct of_device_id aspeed_wdt_of_table[] = { { .compatible = "aspeed,ast2400-wdt", .data = &ast2400_config }, { .compatible = "aspeed,ast2500-wdt", .data = &ast2500_config }, + { .compatible = "aspeed,ast2600-wdt", .data = &ast2500_config }, { }, }; MODULE_DEVICE_TABLE(of, aspeed_wdt_of_table); @@ -53,6 +54,8 @@ MODULE_DEVICE_TABLE(of, aspeed_wdt_of_table); #define WDT_CTRL_ENABLE BIT(0) #define WDT_TIMEOUT_STATUS 0x10 #define WDT_TIMEOUT_STATUS_BOOT_SECONDARY BIT(1) +#define WDT_CLEAR_TIMEOUT_STATUS 0x14 +#define WDT_CLEAR_TIMEOUT_AND_BOOT_CODE_SELECTION BIT(0) /* * WDT_RESET_WIDTH controls the characteristics of the external pulse (if @@ -165,6 +168,60 @@ static int aspeed_wdt_restart(struct watchdog_device *wdd, return 0; } +/* 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) +{ + struct aspeed_wdt *wdt = dev_get_drvdata(dev); + u32 status = readl(wdt->base + WDT_TIMEOUT_STATUS); + + return sprintf(buf, "%u\n", + !(status & WDT_TIMEOUT_STATUS_BOOT_SECONDARY)); +} + +static ssize_t access_cs0_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t size) +{ + struct aspeed_wdt *wdt = dev_get_drvdata(dev); + unsigned long val; + + if (kstrtoul(buf, 10, &val)) + return -EINVAL; + + if (val) + writel(WDT_CLEAR_TIMEOUT_AND_BOOT_CODE_SELECTION, + wdt->base + WDT_CLEAR_TIMEOUT_STATUS); + + return size; +} + +/* + * This attribute exists only if the system has booted from the alternate + * flash with 'alt-boot' option. + * + * At alternate flash the 'access_cs0' sysfs node provides: + * ast2400: a way to get access to the primary SPI flash chip at CS0 + * after booting from the alternate chip at CS1. + * ast2500: a way to restore the normal address mapping from + * (CS0->CS1, CS1->CS0) to (CS0->CS0, CS1->CS1). + * + * Clearing the boot code selection and timeout counter also resets to the + * initial state the chip select line mapping. When the SoC is in normal + * mapping state (i.e. booted from CS0), clearing those bits does nothing for + * both versions of the SoC. For alternate boot mode (booted from CS1 due to + * wdt2 expiration) the behavior differs as described above. + * + * This option can be used with wdt2 (watchdog1) only. + */ +static DEVICE_ATTR_RW(access_cs0); + +static struct attribute *bswitch_attrs[] = { + &dev_attr_access_cs0.attr, + NULL +}; +ATTRIBUTE_GROUPS(bswitch); + static const struct watchdog_ops aspeed_wdt_ops = { .start = aspeed_wdt_start, .stop = aspeed_wdt_stop, @@ -201,11 +258,6 @@ static int aspeed_wdt_probe(struct platform_device *pdev) if (IS_ERR(wdt->base)) return PTR_ERR(wdt->base); - /* - * The ast2400 wdt can run at PCLK, or 1MHz. The ast2500 only - * runs at 1MHz. We chose to always run at 1MHz, as there's no - * good reason to have a faster watchdog counter. - */ wdt->wdd.info = &aspeed_wdt_info; wdt->wdd.ops = &aspeed_wdt_ops; wdt->wdd.max_hw_heartbeat_ms = WDT_MAX_TIMEOUT_MS; @@ -221,7 +273,16 @@ static int aspeed_wdt_probe(struct platform_device *pdev) return -EINVAL; config = ofdid->data; - wdt->ctrl = WDT_CTRL_1MHZ_CLK; + /* + * On clock rates: + * - ast2400 wdt can run at PCLK, or 1MHz + * - ast2500 only runs at 1MHz, hard coding bit 4 to 1 + * - ast2600 always runs at 1MHz + * + * Set the ast2400 to run at 1MHz as it simplifies the driver. + */ + if (of_device_is_compatible(np, "aspeed,ast2400-wdt")) + wdt->ctrl = WDT_CTRL_1MHZ_CLK; /* * Control reset on a per-device basis to ensure the @@ -259,7 +320,8 @@ static int aspeed_wdt_probe(struct platform_device *pdev) set_bit(WDOG_HW_RUNNING, &wdt->wdd.status); } - if (of_device_is_compatible(np, "aspeed,ast2500-wdt")) { + if ((of_device_is_compatible(np, "aspeed,ast2500-wdt")) || + (of_device_is_compatible(np, "aspeed,ast2600-wdt"))) { u32 reg = readl(wdt->base + WDT_RESET_WIDTH); reg &= config->ext_pulse_width_mask; @@ -306,9 +368,16 @@ static int aspeed_wdt_probe(struct platform_device *pdev) } status = readl(wdt->base + WDT_TIMEOUT_STATUS); - if (status & WDT_TIMEOUT_STATUS_BOOT_SECONDARY) + 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; + } + + dev_set_drvdata(dev, wdt); + return devm_watchdog_register_device(dev, &wdt->wdd); } diff --git a/drivers/watchdog/at91rm9200_wdt.c b/drivers/watchdog/at91rm9200_wdt.c index 907a4545dee6..6d751eb8191d 100644 --- a/drivers/watchdog/at91rm9200_wdt.c +++ b/drivers/watchdog/at91rm9200_wdt.c @@ -213,6 +213,7 @@ 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, .release = at91_wdt_close, .write = at91_wdt_write, diff --git a/drivers/watchdog/at91sam9_wdt.h b/drivers/watchdog/at91sam9_wdt.h index 390941c65eee..298d545df1a1 100644 --- a/drivers/watchdog/at91sam9_wdt.h +++ b/drivers/watchdog/at91sam9_wdt.h @@ -4,33 +4,58 @@ * * Copyright (C) 2007 Andrew Victor * Copyright (C) 2007 Atmel Corporation. + * Copyright (C) 2019 Microchip Technology Inc. and its subsidiaries * * Watchdog Timer (WDT) - System peripherals regsters. * Based on AT91SAM9261 datasheet revision D. + * Based on SAM9X60 datasheet. * */ #ifndef AT91_WDT_H #define AT91_WDT_H +#include <linux/bits.h> + #define AT91_WDT_CR 0x00 /* Watchdog Control Register */ -#define AT91_WDT_WDRSTT (1 << 0) /* Restart */ -#define AT91_WDT_KEY (0xa5 << 24) /* KEY Password */ +#define AT91_WDT_WDRSTT BIT(0) /* Restart */ +#define AT91_WDT_KEY (0xa5UL << 24) /* KEY Password */ #define AT91_WDT_MR 0x04 /* Watchdog Mode Register */ -#define AT91_WDT_WDV (0xfff << 0) /* Counter Value */ -#define AT91_WDT_SET_WDV(x) ((x) & AT91_WDT_WDV) -#define AT91_WDT_WDFIEN (1 << 12) /* Fault Interrupt Enable */ -#define AT91_WDT_WDRSTEN (1 << 13) /* Reset Processor */ -#define AT91_WDT_WDRPROC (1 << 14) /* Timer Restart */ -#define AT91_WDT_WDDIS (1 << 15) /* Watchdog Disable */ -#define AT91_WDT_WDD (0xfff << 16) /* Delta Value */ -#define AT91_WDT_SET_WDD(x) (((x) << 16) & AT91_WDT_WDD) -#define AT91_WDT_WDDBGHLT (1 << 28) /* Debug Halt */ -#define AT91_WDT_WDIDLEHLT (1 << 29) /* Idle Halt */ - -#define AT91_WDT_SR 0x08 /* Watchdog Status Register */ -#define AT91_WDT_WDUNF (1 << 0) /* Watchdog Underflow */ -#define AT91_WDT_WDERR (1 << 1) /* Watchdog Error */ +#define AT91_WDT_WDV (0xfffUL << 0) /* Counter Value */ +#define AT91_WDT_SET_WDV(x) ((x) & AT91_WDT_WDV) +#define AT91_SAM9X60_PERIODRST BIT(4) /* Period Reset */ +#define AT91_SAM9X60_RPTHRST BIT(5) /* Minimum Restart Period */ +#define AT91_WDT_WDFIEN BIT(12) /* Fault Interrupt Enable */ +#define AT91_SAM9X60_WDDIS BIT(12) /* Watchdog Disable */ +#define AT91_WDT_WDRSTEN BIT(13) /* Reset Processor */ +#define AT91_WDT_WDRPROC BIT(14) /* Timer Restart */ +#define AT91_WDT_WDDIS BIT(15) /* Watchdog Disable */ +#define AT91_WDT_WDD (0xfffUL << 16) /* Delta Value */ +#define AT91_WDT_SET_WDD(x) (((x) << 16) & AT91_WDT_WDD) +#define AT91_WDT_WDDBGHLT BIT(28) /* Debug Halt */ +#define AT91_WDT_WDIDLEHLT BIT(29) /* Idle Halt */ + +#define AT91_WDT_SR 0x08 /* Watchdog Status Register */ +#define AT91_WDT_WDUNF BIT(0) /* Watchdog Underflow */ +#define AT91_WDT_WDERR BIT(1) /* Watchdog Error */ + +/* Watchdog Timer Value Register */ +#define AT91_SAM9X60_VR 0x08 + +/* Watchdog Window Level Register */ +#define AT91_SAM9X60_WLR 0x0c +/* Watchdog Period Value */ +#define AT91_SAM9X60_COUNTER (0xfffUL << 0) +#define AT91_SAM9X60_SET_COUNTER(x) ((x) & AT91_SAM9X60_COUNTER) + +/* Interrupt Enable Register */ +#define AT91_SAM9X60_IER 0x14 +/* Period Interrupt Enable */ +#define AT91_SAM9X60_PERINT BIT(0) +/* Interrupt Disable Register */ +#define AT91_SAM9X60_IDR 0x18 +/* Interrupt Status Register */ +#define AT91_SAM9X60_ISR 0x1c #endif diff --git a/drivers/watchdog/ath79_wdt.c b/drivers/watchdog/ath79_wdt.c index 2e09981fe978..d6dff97c280b 100644 --- a/drivers/watchdog/ath79_wdt.c +++ b/drivers/watchdog/ath79_wdt.c @@ -234,6 +234,7 @@ static const struct file_operations ath79_wdt_fops = { .llseek = no_llseek, .write = ath79_wdt_write, .unlocked_ioctl = ath79_wdt_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = ath79_wdt_open, .release = ath79_wdt_release, }; @@ -302,7 +303,7 @@ static int ath79_wdt_remove(struct platform_device *pdev) return 0; } -static void ath97_wdt_shutdown(struct platform_device *pdev) +static void ath79_wdt_shutdown(struct platform_device *pdev) { ath79_wdt_disable(); } @@ -318,7 +319,7 @@ MODULE_DEVICE_TABLE(of, ath79_wdt_match); static struct platform_driver ath79_wdt_driver = { .probe = ath79_wdt_probe, .remove = ath79_wdt_remove, - .shutdown = ath97_wdt_shutdown, + .shutdown = ath79_wdt_shutdown, .driver = { .name = DRIVER_NAME, .of_match_table = of_match_ptr(ath79_wdt_match), diff --git a/drivers/watchdog/bcm63xx_wdt.c b/drivers/watchdog/bcm63xx_wdt.c index e2af37c9a266..7cdb25363ea0 100644 --- a/drivers/watchdog/bcm63xx_wdt.c +++ b/drivers/watchdog/bcm63xx_wdt.c @@ -221,6 +221,7 @@ static const struct file_operations bcm63xx_wdt_fops = { .llseek = no_llseek, .write = bcm63xx_wdt_write, .unlocked_ioctl = bcm63xx_wdt_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = bcm63xx_wdt_open, .release = bcm63xx_wdt_release, }; @@ -245,7 +246,7 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev) return -ENODEV; } - bcm63xx_wdt_device.regs = devm_ioremap_nocache(&pdev->dev, r->start, + bcm63xx_wdt_device.regs = devm_ioremap(&pdev->dev, r->start, resource_size(r)); if (!bcm63xx_wdt_device.regs) { dev_err(&pdev->dev, "failed to remap I/O resources\n"); diff --git a/drivers/watchdog/bd70528_wdt.c b/drivers/watchdog/bd70528_wdt.c index b0152fef4fc7..0170b37e6674 100644 --- a/drivers/watchdog/bd70528_wdt.c +++ b/drivers/watchdog/bd70528_wdt.c @@ -97,7 +97,7 @@ EXPORT_SYMBOL(bd70528_wdt_set); /** * bd70528_wdt_lock - take WDT lock * - * @bd70528: device data for the PMIC instance we want to operate on + * @data: device data for the PMIC instance we want to operate on * * Lock WDT for arming/disarming in order to avoid race condition caused * by WDT state changes initiated by WDT and RTC drivers. @@ -114,7 +114,7 @@ EXPORT_SYMBOL(bd70528_wdt_lock); /** * bd70528_wdt_unlock - unlock WDT lock * - * @bd70528: device data for the PMIC instance we want to operate on + * @data: device data for the PMIC instance we want to operate on * * Unlock WDT lock which has previously been taken by call to * bd70528_wdt_lock. @@ -288,3 +288,4 @@ module_platform_driver(bd70528_wdt); MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>"); MODULE_DESCRIPTION("BD70528 watchdog driver"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:bd70528-wdt"); diff --git a/drivers/watchdog/cadence_wdt.c b/drivers/watchdog/cadence_wdt.c index f8d4e91d0383..672b184da875 100644 --- a/drivers/watchdog/cadence_wdt.c +++ b/drivers/watchdog/cadence_wdt.c @@ -335,8 +335,10 @@ static int cdns_wdt_probe(struct platform_device *pdev) wdt->clk = devm_clk_get(dev, NULL); if (IS_ERR(wdt->clk)) { - dev_err(dev, "input clock not found\n"); - return PTR_ERR(wdt->clk); + ret = PTR_ERR(wdt->clk); + if (ret != -EPROBE_DEFER) + dev_err(dev, "input clock not found\n"); + return ret; } ret = clk_prepare_enable(wdt->clk); @@ -367,9 +369,8 @@ static int cdns_wdt_probe(struct platform_device *pdev) return ret; platform_set_drvdata(pdev, wdt); - dev_info(dev, "Xilinx Watchdog Timer at %p with timeout %ds%s\n", - wdt->regs, cdns_wdt_device->timeout, - nowayout ? ", nowayout" : ""); + dev_info(dev, "Xilinx Watchdog Timer with timeout %ds%s\n", + cdns_wdt_device->timeout, nowayout ? ", nowayout" : ""); return 0; } diff --git a/drivers/watchdog/cpu5wdt.c b/drivers/watchdog/cpu5wdt.c index d6d53014cb68..9867a3a936df 100644 --- a/drivers/watchdog/cpu5wdt.c +++ b/drivers/watchdog/cpu5wdt.c @@ -187,6 +187,7 @@ 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, diff --git a/drivers/watchdog/cpwd.c b/drivers/watchdog/cpwd.c index b973b31179df..808eeb4779e4 100644 --- a/drivers/watchdog/cpwd.c +++ b/drivers/watchdog/cpwd.c @@ -26,6 +26,7 @@ #include <linux/interrupt.h> #include <linux/ioport.h> #include <linux/timer.h> +#include <linux/compat.h> #include <linux/slab.h> #include <linux/mutex.h> #include <linux/io.h> @@ -473,27 +474,9 @@ static long cpwd_ioctl(struct file *file, unsigned int cmd, unsigned long arg) return 0; } -static long cpwd_compat_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) +static long cpwd_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { - int rval = -ENOIOCTLCMD; - - switch (cmd) { - /* solaris ioctls are specific to this driver */ - case WIOCSTART: - case WIOCSTOP: - case WIOCGSTAT: - mutex_lock(&cpwd_mutex); - rval = cpwd_ioctl(file, cmd, arg); - mutex_unlock(&cpwd_mutex); - break; - - /* everything else is handled by the generic compat layer */ - default: - break; - } - - return rval; + return cpwd_ioctl(file, cmd, (unsigned long)compat_ptr(arg)); } static ssize_t cpwd_write(struct file *file, const char __user *buf, diff --git a/drivers/watchdog/da9062_wdt.c b/drivers/watchdog/da9062_wdt.c index e149e66a6ea9..47eefe072b40 100644 --- a/drivers/watchdog/da9062_wdt.c +++ b/drivers/watchdog/da9062_wdt.c @@ -11,6 +11,7 @@ #include <linux/platform_device.h> #include <linux/uaccess.h> #include <linux/slab.h> +#include <linux/i2c.h> #include <linux/delay.h> #include <linux/jiffies.h> #include <linux/mfd/da9062/registers.h> @@ -147,12 +148,13 @@ static int da9062_wdt_restart(struct watchdog_device *wdd, unsigned long action, void *data) { struct da9062_watchdog *wdt = watchdog_get_drvdata(wdd); + struct i2c_client *client = to_i2c_client(wdt->hw->dev); int ret; - ret = regmap_write(wdt->hw->regmap, - DA9062AA_CONTROL_F, - DA9062AA_SHUTDOWN_MASK); - if (ret) + /* Don't use regmap because it is not atomic safe */ + ret = i2c_smbus_write_byte_data(client, DA9062AA_CONTROL_F, + DA9062AA_SHUTDOWN_MASK); + if (ret < 0) dev_alert(wdt->hw->dev, "Failed to shutdown (err = %d)\n", ret); @@ -212,6 +214,7 @@ static int da9062_wdt_probe(struct platform_device *pdev) watchdog_set_restart_priority(&wdt->wdtdev, 128); watchdog_set_drvdata(&wdt->wdtdev, wdt); + dev_set_drvdata(dev, &wdt->wdtdev); ret = devm_watchdog_register_device(dev, &wdt->wdtdev); if (ret < 0) @@ -220,10 +223,34 @@ static int da9062_wdt_probe(struct platform_device *pdev) return da9062_wdt_ping(&wdt->wdtdev); } +static int __maybe_unused da9062_wdt_suspend(struct device *dev) +{ + struct watchdog_device *wdd = dev_get_drvdata(dev); + + if (watchdog_active(wdd)) + return da9062_wdt_stop(wdd); + + return 0; +} + +static int __maybe_unused da9062_wdt_resume(struct device *dev) +{ + struct watchdog_device *wdd = dev_get_drvdata(dev); + + if (watchdog_active(wdd)) + return da9062_wdt_start(wdd); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(da9062_wdt_pm_ops, + da9062_wdt_suspend, da9062_wdt_resume); + static struct platform_driver da9062_wdt_driver = { .probe = da9062_wdt_probe, .driver = { .name = "da9062-watchdog", + .pm = &da9062_wdt_pm_ops, .of_match_table = da9062_compatible_id_table, }, }; diff --git a/drivers/watchdog/diag288_wdt.c b/drivers/watchdog/diag288_wdt.c index 181440b7b4d0..aafc8d98bf9f 100644 --- a/drivers/watchdog/diag288_wdt.c +++ b/drivers/watchdog/diag288_wdt.c @@ -26,13 +26,11 @@ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/slab.h> -#include <linux/miscdevice.h> #include <linux/watchdog.h> #include <linux/suspend.h> #include <asm/ebcdic.h> #include <asm/diag.h> #include <linux/io.h> -#include <linux/uaccess.h> #define MAX_CMDLEN 240 #define DEFAULT_CMD "SYSTEM RESTART" @@ -70,7 +68,6 @@ MODULE_PARM_DESC(conceal, "Enable the CONCEAL CP option while the watchdog is ac module_param_named(nowayout, nowayout_info, bool, 0444); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default = CONFIG_WATCHDOG_NOWAYOUT)"); -MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); MODULE_ALIAS("vmwatchdog"); static int __diag288(unsigned int func, unsigned int timeout, diff --git a/drivers/watchdog/dw_wdt.c b/drivers/watchdog/dw_wdt.c index fef7c61f5555..fba21de2bbad 100644 --- a/drivers/watchdog/dw_wdt.c +++ b/drivers/watchdog/dw_wdt.c @@ -114,7 +114,15 @@ static int dw_wdt_set_timeout(struct watchdog_device *wdd, unsigned int top_s) writel(top_val | top_val << WDOG_TIMEOUT_RANGE_TOPINIT_SHIFT, dw_wdt->regs + WDOG_TIMEOUT_RANGE_REG_OFFSET); - wdd->timeout = dw_wdt_top_in_seconds(dw_wdt, top_val); + /* + * In case users set bigger timeout value than HW can support, + * kernel(watchdog_dev.c) helps to feed watchdog before + * wdd->max_hw_heartbeat_ms + */ + if (top_s * 1000 <= wdd->max_hw_heartbeat_ms) + wdd->timeout = dw_wdt_top_in_seconds(dw_wdt, top_val); + else + wdd->timeout = top_s; return 0; } @@ -135,6 +143,7 @@ static int dw_wdt_start(struct watchdog_device *wdd) struct dw_wdt *dw_wdt = to_dw_wdt(wdd); dw_wdt_set_timeout(wdd, wdd->timeout); + dw_wdt_ping(&dw_wdt->wdd); dw_wdt_arm_system_reset(dw_wdt); return 0; diff --git a/drivers/watchdog/eurotechwdt.c b/drivers/watchdog/eurotechwdt.c index 3a83a48abcae..f5ffa7be066e 100644 --- a/drivers/watchdog/eurotechwdt.c +++ b/drivers/watchdog/eurotechwdt.c @@ -371,6 +371,7 @@ static const struct file_operations eurwdt_fops = { .llseek = no_llseek, .write = eurwdt_write, .unlocked_ioctl = eurwdt_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = eurwdt_open, .release = eurwdt_release, }; diff --git a/drivers/watchdog/f71808e_wdt.c b/drivers/watchdog/f71808e_wdt.c index ff5cf1b48a4d..a3c44d75d80e 100644 --- a/drivers/watchdog/f71808e_wdt.c +++ b/drivers/watchdog/f71808e_wdt.c @@ -31,8 +31,10 @@ #define SIO_REG_DEVID 0x20 /* Device ID (2 bytes) */ #define SIO_REG_DEVREV 0x22 /* Device revision */ #define SIO_REG_MANID 0x23 /* Fintek ID (2 bytes) */ +#define SIO_REG_CLOCK_SEL 0x26 /* Clock select */ #define SIO_REG_ROM_ADDR_SEL 0x27 /* ROM address select */ #define SIO_F81866_REG_PORT_SEL 0x27 /* F81866 Multi-Function Register */ +#define SIO_REG_TSI_LEVEL_SEL 0x28 /* TSI Level select */ #define SIO_REG_MFUNCT1 0x29 /* Multi function select 1 */ #define SIO_REG_MFUNCT2 0x2a /* Multi function select 2 */ #define SIO_REG_MFUNCT3 0x2b /* Multi function select 3 */ @@ -49,6 +51,7 @@ #define SIO_F71869A_ID 0x1007 /* Chipset ID */ #define SIO_F71882_ID 0x0541 /* Chipset ID */ #define SIO_F71889_ID 0x0723 /* Chipset ID */ +#define SIO_F81803_ID 0x1210 /* Chipset ID */ #define SIO_F81865_ID 0x0704 /* Chipset ID */ #define SIO_F81866_ID 0x1010 /* Chipset ID */ @@ -108,7 +111,7 @@ MODULE_PARM_DESC(start_withtimeout, "Start watchdog timer on module load with" " given initial timeout. Zero (default) disables this feature."); enum chips { f71808fg, f71858fg, f71862fg, f71868, f71869, f71882fg, f71889fg, - f81865, f81866}; + f81803, f81865, f81866}; static const char *f71808e_names[] = { "f71808fg", @@ -118,6 +121,7 @@ static const char *f71808e_names[] = { "f71869", "f71882fg", "f71889fg", + "f81803", "f81865", "f81866", }; @@ -370,6 +374,14 @@ static int watchdog_start(void) superio_inb(watchdog.sioaddr, SIO_REG_MFUNCT3) & 0xcf); break; + case f81803: + /* Enable TSI Level register bank */ + superio_clear_bit(watchdog.sioaddr, SIO_REG_CLOCK_SEL, 3); + /* Set pin 27 to WDTRST# */ + superio_outb(watchdog.sioaddr, SIO_REG_TSI_LEVEL_SEL, 0x5f & + superio_inb(watchdog.sioaddr, SIO_REG_TSI_LEVEL_SEL)); + break; + case f81865: /* Set pin 70 to WDTRST# */ superio_clear_bit(watchdog.sioaddr, SIO_REG_MFUNCT3, 5); @@ -657,6 +669,7 @@ static const struct file_operations watchdog_fops = { .release = watchdog_release, .write = watchdog_write, .unlocked_ioctl = watchdog_ioctl, + .compat_ioctl = compat_ptr_ioctl, }; static struct miscdevice watchdog_miscdev = { @@ -809,6 +822,9 @@ static int __init f71808e_find(int sioaddr) /* Confirmed (by datasheet) not to have a watchdog. */ err = -ENODEV; goto exit; + case SIO_F81803_ID: + watchdog.type = f81803; + break; case SIO_F81865_ID: watchdog.type = f81865; break; diff --git a/drivers/watchdog/gef_wdt.c b/drivers/watchdog/gef_wdt.c index 7d5f56994f09..f6541d1b65e3 100644 --- a/drivers/watchdog/gef_wdt.c +++ b/drivers/watchdog/gef_wdt.c @@ -248,6 +248,7 @@ static const struct file_operations gef_wdt_fops = { .llseek = no_llseek, .write = gef_wdt_write, .unlocked_ioctl = gef_wdt_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = gef_wdt_open, .release = gef_wdt_release, }; diff --git a/drivers/watchdog/geodewdt.c b/drivers/watchdog/geodewdt.c index 8d105d98908e..9914a4283cb2 100644 --- a/drivers/watchdog/geodewdt.c +++ b/drivers/watchdog/geodewdt.c @@ -201,6 +201,7 @@ static const struct file_operations geodewdt_fops = { .llseek = no_llseek, .write = geodewdt_write, .unlocked_ioctl = geodewdt_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = geodewdt_open, .release = geodewdt_release, }; diff --git a/drivers/watchdog/iTCO_wdt.c b/drivers/watchdog/iTCO_wdt.c index c559f706ae7e..156360e37714 100644 --- a/drivers/watchdog/iTCO_wdt.c +++ b/drivers/watchdog/iTCO_wdt.c @@ -48,6 +48,7 @@ /* Includes */ #include <linux/acpi.h> /* For ACPI support */ +#include <linux/bits.h> /* For BIT() */ #include <linux/module.h> /* For module specific items */ #include <linux/moduleparam.h> /* For new moduleparam's */ #include <linux/types.h> /* For standard types (like size_t) */ @@ -215,6 +216,23 @@ static int update_no_reboot_bit_mem(void *priv, bool set) return 0; } +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)); + if (set) + val |= BIT(0); + else + val &= ~BIT(0); + outw(val, TCO1_CNT(p)); + newval = inw(TCO1_CNT(p)); + + /* make sure the update is successful */ + return val != newval ? -EIO : 0; +} + static void iTCO_wdt_no_reboot_bit_setup(struct iTCO_wdt_private *p, struct itco_wdt_platform_data *pdata) { @@ -224,7 +242,9 @@ static void iTCO_wdt_no_reboot_bit_setup(struct iTCO_wdt_private *p, return; } - if (p->iTCO_version >= 2) + if (p->iTCO_version >= 6) + p->update_no_reboot_bit = update_no_reboot_bit_cnt; + else if (p->iTCO_version >= 2) p->update_no_reboot_bit = update_no_reboot_bit_mem; else if (p->iTCO_version == 1) p->update_no_reboot_bit = update_no_reboot_bit_pci; @@ -452,7 +472,8 @@ static int iTCO_wdt_probe(struct platform_device *pdev) * Get the Memory-Mapped GCS or PMC register, we need it for the * NO_REBOOT flag (TCO v2 and v3). */ - if (p->iTCO_version >= 2 && !pdata->update_no_reboot_bit) { + if (p->iTCO_version >= 2 && p->iTCO_version < 6 && + !pdata->update_no_reboot_bit) { p->gcs_pmc_res = platform_get_resource(pdev, IORESOURCE_MEM, ICH_RES_MEM_GCS_PMC); @@ -502,6 +523,7 @@ static int iTCO_wdt_probe(struct platform_device *pdev) /* Clear out the (probably old) status */ switch (p->iTCO_version) { + case 6: case 5: case 4: outw(0x0008, TCO1_STS(p)); /* Clear the Time Out Status bit */ diff --git a/drivers/watchdog/ib700wdt.c b/drivers/watchdog/ib700wdt.c index 92fd7f33bc4d..2b65ea9451d1 100644 --- a/drivers/watchdog/ib700wdt.c +++ b/drivers/watchdog/ib700wdt.c @@ -259,6 +259,7 @@ static const struct file_operations ibwdt_fops = { .llseek = no_llseek, .write = ibwdt_write, .unlocked_ioctl = ibwdt_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = ibwdt_open, .release = ibwdt_close, }; diff --git a/drivers/watchdog/ibmasr.c b/drivers/watchdog/ibmasr.c index 897f7eda9e6a..4a22fe152086 100644 --- a/drivers/watchdog/ibmasr.c +++ b/drivers/watchdog/ibmasr.c @@ -344,6 +344,7 @@ static const struct file_operations asr_fops = { .llseek = no_llseek, .write = asr_write, .unlocked_ioctl = asr_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = asr_open, .release = asr_release, }; diff --git a/drivers/watchdog/imx2_wdt.c b/drivers/watchdog/imx2_wdt.c index 32af3974e6bb..f8d58bf0bf66 100644 --- a/drivers/watchdog/imx2_wdt.c +++ b/drivers/watchdog/imx2_wdt.c @@ -55,7 +55,7 @@ #define IMX2_WDT_WMCR 0x08 /* Misc Register */ -#define IMX2_WDT_MAX_TIME 128 +#define IMX2_WDT_MAX_TIME 128U #define IMX2_WDT_DEFAULT_TIME 60 /* in seconds */ #define WDOG_SEC_TO_COUNT(s) ((s * 2 - 1) << 8) @@ -72,7 +72,6 @@ module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); - static unsigned timeout; module_param(timeout, uint, 0); MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds (default=" @@ -180,7 +179,7 @@ static int imx2_wdt_set_timeout(struct watchdog_device *wdog, { unsigned int actual; - actual = min(new_timeout, wdog->max_hw_heartbeat_ms * 1000); + actual = min(new_timeout, IMX2_WDT_MAX_TIME); __imx2_wdt_set_timeout(wdog, actual); wdog->timeout = new_timeout; return 0; @@ -247,13 +246,14 @@ static const struct regmap_config imx2_wdt_regmap_config = { static int __init imx2_wdt_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; struct imx2_wdt_device *wdev; struct watchdog_device *wdog; void __iomem *base; int ret; u32 val; - wdev = devm_kzalloc(&pdev->dev, sizeof(*wdev), GFP_KERNEL); + wdev = devm_kzalloc(dev, sizeof(*wdev), GFP_KERNEL); if (!wdev) return -ENOMEM; @@ -261,16 +261,16 @@ static int __init imx2_wdt_probe(struct platform_device *pdev) if (IS_ERR(base)) return PTR_ERR(base); - wdev->regmap = devm_regmap_init_mmio_clk(&pdev->dev, NULL, base, + wdev->regmap = devm_regmap_init_mmio_clk(dev, NULL, base, &imx2_wdt_regmap_config); if (IS_ERR(wdev->regmap)) { - dev_err(&pdev->dev, "regmap init failed\n"); + dev_err(dev, "regmap init failed\n"); return PTR_ERR(wdev->regmap); } - wdev->clk = devm_clk_get(&pdev->dev, NULL); + wdev->clk = devm_clk_get(dev, NULL); if (IS_ERR(wdev->clk)) { - dev_err(&pdev->dev, "can't get Watchdog clock\n"); + dev_err(dev, "can't get Watchdog clock\n"); return PTR_ERR(wdev->clk); } @@ -280,12 +280,12 @@ static int __init imx2_wdt_probe(struct platform_device *pdev) wdog->min_timeout = 1; wdog->timeout = IMX2_WDT_DEFAULT_TIME; wdog->max_hw_heartbeat_ms = IMX2_WDT_MAX_TIME * 1000; - wdog->parent = &pdev->dev; + wdog->parent = dev; ret = platform_get_irq(pdev, 0); if (ret > 0) - if (!devm_request_irq(&pdev->dev, ret, imx2_wdt_isr, 0, - dev_name(&pdev->dev), wdog)) + if (!devm_request_irq(dev, ret, imx2_wdt_isr, 0, + dev_name(dev), wdog)) wdog->info = &imx2_wdt_pretimeout_info; ret = clk_prepare_enable(wdev->clk); @@ -295,13 +295,13 @@ static int __init imx2_wdt_probe(struct platform_device *pdev) regmap_read(wdev->regmap, IMX2_WDT_WRSR, &val); wdog->bootstatus = val & IMX2_WDT_WRSR_TOUT ? WDIOF_CARDRESET : 0; - wdev->ext_reset = of_property_read_bool(pdev->dev.of_node, + wdev->ext_reset = of_property_read_bool(dev->of_node, "fsl,ext-reset-output"); platform_set_drvdata(pdev, wdog); watchdog_set_drvdata(wdog, wdev); watchdog_set_nowayout(wdog, nowayout); watchdog_set_restart_priority(wdog, 128); - watchdog_init_timeout(wdog, timeout, &pdev->dev); + watchdog_init_timeout(wdog, timeout, dev); if (imx2_wdt_is_running(wdev)) { imx2_wdt_set_timeout(wdog, wdog->timeout); @@ -319,7 +319,7 @@ static int __init imx2_wdt_probe(struct platform_device *pdev) if (ret) goto disable_clk; - dev_info(&pdev->dev, "timeout %d sec (nowayout=%d)\n", + dev_info(dev, "timeout %d sec (nowayout=%d)\n", wdog->timeout, nowayout); return 0; @@ -359,9 +359,8 @@ static void imx2_wdt_shutdown(struct platform_device *pdev) } } -#ifdef CONFIG_PM_SLEEP /* Disable watchdog if it is active or non-active but still running */ -static int imx2_wdt_suspend(struct device *dev) +static int __maybe_unused imx2_wdt_suspend(struct device *dev) { struct watchdog_device *wdog = dev_get_drvdata(dev); struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog); @@ -382,7 +381,7 @@ static int imx2_wdt_suspend(struct device *dev) } /* Enable watchdog and configure it if necessary */ -static int imx2_wdt_resume(struct device *dev) +static int __maybe_unused imx2_wdt_resume(struct device *dev) { struct watchdog_device *wdog = dev_get_drvdata(dev); struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog); @@ -407,7 +406,6 @@ static int imx2_wdt_resume(struct device *dev) return 0; } -#endif static SIMPLE_DEV_PM_OPS(imx2_wdt_pm_ops, imx2_wdt_suspend, imx2_wdt_resume); diff --git a/drivers/watchdog/imx7ulp_wdt.c b/drivers/watchdog/imx7ulp_wdt.c new file mode 100644 index 000000000000..11b9e7c6b7f5 --- /dev/null +++ b/drivers/watchdog/imx7ulp_wdt.c @@ -0,0 +1,260 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2019 NXP. + */ + +#include <linux/clk.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/reboot.h> +#include <linux/watchdog.h> + +#define WDOG_CS 0x0 +#define WDOG_CS_CMD32EN BIT(13) +#define WDOG_CS_ULK BIT(11) +#define WDOG_CS_RCS BIT(10) +#define LPO_CLK 0x1 +#define LPO_CLK_SHIFT 8 +#define WDOG_CS_CLK (LPO_CLK << LPO_CLK_SHIFT) +#define WDOG_CS_EN BIT(7) +#define WDOG_CS_UPDATE BIT(5) + +#define WDOG_CNT 0x4 +#define WDOG_TOVAL 0x8 + +#define REFRESH_SEQ0 0xA602 +#define REFRESH_SEQ1 0xB480 +#define REFRESH ((REFRESH_SEQ1 << 16) | REFRESH_SEQ0) + +#define UNLOCK_SEQ0 0xC520 +#define UNLOCK_SEQ1 0xD928 +#define UNLOCK ((UNLOCK_SEQ1 << 16) | UNLOCK_SEQ0) + +#define DEFAULT_TIMEOUT 60 +#define MAX_TIMEOUT 128 +#define WDOG_CLOCK_RATE 1000 + +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0000); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); + +struct imx7ulp_wdt_device { + struct watchdog_device wdd; + void __iomem *base; + struct clk *clk; +}; + +static void imx7ulp_wdt_enable(struct watchdog_device *wdog, bool enable) +{ + struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog); + + u32 val = readl(wdt->base + WDOG_CS); + + writel(UNLOCK, wdt->base + WDOG_CNT); + if (enable) + writel(val | WDOG_CS_EN, wdt->base + WDOG_CS); + else + writel(val & ~WDOG_CS_EN, wdt->base + WDOG_CS); +} + +static bool imx7ulp_wdt_is_enabled(void __iomem *base) +{ + u32 val = readl(base + WDOG_CS); + + return val & WDOG_CS_EN; +} + +static int imx7ulp_wdt_ping(struct watchdog_device *wdog) +{ + struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog); + + writel(REFRESH, wdt->base + WDOG_CNT); + + return 0; +} + +static int imx7ulp_wdt_start(struct watchdog_device *wdog) +{ + + imx7ulp_wdt_enable(wdog, true); + + return 0; +} + +static int imx7ulp_wdt_stop(struct watchdog_device *wdog) +{ + imx7ulp_wdt_enable(wdog, false); + + return 0; +} + +static int imx7ulp_wdt_set_timeout(struct watchdog_device *wdog, + unsigned int timeout) +{ + struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog); + u32 val = WDOG_CLOCK_RATE * timeout; + + writel(UNLOCK, wdt->base + WDOG_CNT); + writel(val, wdt->base + WDOG_TOVAL); + + wdog->timeout = timeout; + + return 0; +} + +static int imx7ulp_wdt_restart(struct watchdog_device *wdog, + unsigned long action, void *data) +{ + struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog); + + imx7ulp_wdt_enable(wdog, true); + imx7ulp_wdt_set_timeout(&wdt->wdd, 1); + + /* wait for wdog to fire */ + while (true) + ; + + return NOTIFY_DONE; +} + +static const struct watchdog_ops imx7ulp_wdt_ops = { + .owner = THIS_MODULE, + .start = imx7ulp_wdt_start, + .stop = imx7ulp_wdt_stop, + .ping = imx7ulp_wdt_ping, + .set_timeout = imx7ulp_wdt_set_timeout, + .restart = imx7ulp_wdt_restart, +}; + +static const struct watchdog_info imx7ulp_wdt_info = { + .identity = "i.MX7ULP watchdog timer", + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | + WDIOF_MAGICCLOSE, +}; + +static void imx7ulp_wdt_init(void __iomem *base, unsigned int timeout) +{ + u32 val; + + /* unlock the wdog for reconfiguration */ + writel_relaxed(UNLOCK_SEQ0, base + WDOG_CNT); + writel_relaxed(UNLOCK_SEQ1, base + WDOG_CNT); + + /* set an initial timeout value in TOVAL */ + writel(timeout, base + WDOG_TOVAL); + /* enable 32bit command sequence and reconfigure */ + val = WDOG_CS_CMD32EN | WDOG_CS_CLK | WDOG_CS_UPDATE; + writel(val, base + WDOG_CS); +} + +static void imx7ulp_wdt_action(void *data) +{ + clk_disable_unprepare(data); +} + +static int imx7ulp_wdt_probe(struct platform_device *pdev) +{ + struct imx7ulp_wdt_device *imx7ulp_wdt; + struct device *dev = &pdev->dev; + struct watchdog_device *wdog; + int ret; + + imx7ulp_wdt = devm_kzalloc(dev, sizeof(*imx7ulp_wdt), GFP_KERNEL); + if (!imx7ulp_wdt) + return -ENOMEM; + + platform_set_drvdata(pdev, imx7ulp_wdt); + + imx7ulp_wdt->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(imx7ulp_wdt->base)) + return PTR_ERR(imx7ulp_wdt->base); + + imx7ulp_wdt->clk = devm_clk_get(dev, NULL); + if (IS_ERR(imx7ulp_wdt->clk)) { + dev_err(dev, "Failed to get watchdog clock\n"); + return PTR_ERR(imx7ulp_wdt->clk); + } + + ret = clk_prepare_enable(imx7ulp_wdt->clk); + if (ret) + return ret; + + ret = devm_add_action_or_reset(dev, imx7ulp_wdt_action, imx7ulp_wdt->clk); + if (ret) + return ret; + + wdog = &imx7ulp_wdt->wdd; + wdog->info = &imx7ulp_wdt_info; + wdog->ops = &imx7ulp_wdt_ops; + wdog->min_timeout = 1; + wdog->max_timeout = MAX_TIMEOUT; + wdog->parent = dev; + wdog->timeout = DEFAULT_TIMEOUT; + + watchdog_init_timeout(wdog, 0, dev); + watchdog_stop_on_reboot(wdog); + watchdog_stop_on_unregister(wdog); + watchdog_set_drvdata(wdog, imx7ulp_wdt); + imx7ulp_wdt_init(imx7ulp_wdt->base, wdog->timeout * WDOG_CLOCK_RATE); + + return devm_watchdog_register_device(dev, wdog); +} + +static int __maybe_unused imx7ulp_wdt_suspend(struct device *dev) +{ + struct imx7ulp_wdt_device *imx7ulp_wdt = dev_get_drvdata(dev); + + if (watchdog_active(&imx7ulp_wdt->wdd)) + imx7ulp_wdt_stop(&imx7ulp_wdt->wdd); + + clk_disable_unprepare(imx7ulp_wdt->clk); + + return 0; +} + +static int __maybe_unused imx7ulp_wdt_resume(struct device *dev) +{ + struct imx7ulp_wdt_device *imx7ulp_wdt = dev_get_drvdata(dev); + u32 timeout = imx7ulp_wdt->wdd.timeout * WDOG_CLOCK_RATE; + int ret; + + ret = clk_prepare_enable(imx7ulp_wdt->clk); + if (ret) + return ret; + + if (imx7ulp_wdt_is_enabled(imx7ulp_wdt->base)) + imx7ulp_wdt_init(imx7ulp_wdt->base, timeout); + + if (watchdog_active(&imx7ulp_wdt->wdd)) + imx7ulp_wdt_start(&imx7ulp_wdt->wdd); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(imx7ulp_wdt_pm_ops, imx7ulp_wdt_suspend, + imx7ulp_wdt_resume); + +static const struct of_device_id imx7ulp_wdt_dt_ids[] = { + { .compatible = "fsl,imx7ulp-wdt", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, imx7ulp_wdt_dt_ids); + +static struct platform_driver imx7ulp_wdt_driver = { + .probe = imx7ulp_wdt_probe, + .driver = { + .name = "imx7ulp-wdt", + .pm = &imx7ulp_wdt_pm_ops, + .of_match_table = imx7ulp_wdt_dt_ids, + }, +}; +module_platform_driver(imx7ulp_wdt_driver); + +MODULE_AUTHOR("Anson Huang <Anson.Huang@nxp.com>"); +MODULE_DESCRIPTION("Freescale i.MX7ULP watchdog driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/watchdog/imx_sc_wdt.c b/drivers/watchdog/imx_sc_wdt.c index 78eaaf75a263..8ed89f032ebf 100644 --- a/drivers/watchdog/imx_sc_wdt.c +++ b/drivers/watchdog/imx_sc_wdt.c @@ -99,8 +99,14 @@ static int imx_sc_wdt_set_pretimeout(struct watchdog_device *wdog, { struct arm_smccc_res res; + /* + * SCU firmware calculates pretimeout based on current time + * stamp instead of watchdog timeout stamp, need to convert + * the pretimeout to SCU firmware's timeout value. + */ arm_smccc_smc(IMX_SIP_TIMER, IMX_SIP_TIMER_SET_PRETIME_WDOG, - pretimeout * 1000, 0, 0, 0, 0, 0, &res); + (wdog->timeout - pretimeout) * 1000, 0, 0, 0, + 0, 0, &res); if (res.a0) return -EACCES; @@ -175,12 +181,9 @@ static int imx_sc_wdt_probe(struct platform_device *pdev) watchdog_stop_on_unregister(wdog); ret = devm_watchdog_register_device(dev, wdog); - - if (ret) { - dev_err(dev, "Failed to register watchdog device\n"); - return ret; - } - + if (ret) + return ret; + ret = imx_scu_irq_group_enable(SC_IRQ_GROUP_WDOG, SC_IRQ_WDOG, true); diff --git a/drivers/watchdog/indydog.c b/drivers/watchdog/indydog.c index 550358528084..9857bb74a723 100644 --- a/drivers/watchdog/indydog.c +++ b/drivers/watchdog/indydog.c @@ -152,6 +152,7 @@ static const struct file_operations indydog_fops = { .llseek = no_llseek, .write = indydog_write, .unlocked_ioctl = indydog_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = indydog_open, .release = indydog_release, }; diff --git a/drivers/watchdog/intel-mid_wdt.c b/drivers/watchdog/intel-mid_wdt.c index 2cdbd37c700c..470213abfd3d 100644 --- a/drivers/watchdog/intel-mid_wdt.c +++ b/drivers/watchdog/intel-mid_wdt.c @@ -134,6 +134,7 @@ static int mid_wdt_probe(struct platform_device *pdev) wdt_dev->timeout = MID_WDT_DEFAULT_TIMEOUT; wdt_dev->parent = dev; + watchdog_set_nowayout(wdt_dev, WATCHDOG_NOWAYOUT); watchdog_set_drvdata(wdt_dev, dev); ret = devm_request_irq(dev, pdata->irq, mid_wdt_irq, diff --git a/drivers/watchdog/intel_scu_watchdog.c b/drivers/watchdog/intel_scu_watchdog.c index 1c85103b750b..804e35940983 100644 --- a/drivers/watchdog/intel_scu_watchdog.c +++ b/drivers/watchdog/intel_scu_watchdog.c @@ -412,6 +412,7 @@ static const struct file_operations intel_scu_fops = { .llseek = no_llseek, .write = intel_scu_write, .unlocked_ioctl = intel_scu_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = intel_scu_open, .release = intel_scu_release, }; @@ -462,7 +463,7 @@ static int __init intel_scu_watchdog_init(void) return -ENODEV; } - tmp_addr = ioremap_nocache(watchdog_device.timer_tbl_ptr->phys_addr, + tmp_addr = ioremap(watchdog_device.timer_tbl_ptr->phys_addr, 20); if (tmp_addr == NULL) { diff --git a/drivers/watchdog/iop_wdt.c b/drivers/watchdog/iop_wdt.c index a9ccdb9a9159..6bf68d4750de 100644 --- a/drivers/watchdog/iop_wdt.c +++ b/drivers/watchdog/iop_wdt.c @@ -202,6 +202,7 @@ static const struct file_operations iop_wdt_fops = { .llseek = no_llseek, .write = iop_wdt_write, .unlocked_ioctl = iop_wdt_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = iop_wdt_open, .release = iop_wdt_release, }; diff --git a/drivers/watchdog/it8712f_wdt.c b/drivers/watchdog/it8712f_wdt.c index 2fe1a3c499ed..2fed40d14007 100644 --- a/drivers/watchdog/it8712f_wdt.c +++ b/drivers/watchdog/it8712f_wdt.c @@ -345,6 +345,7 @@ static const struct file_operations it8712f_wdt_fops = { .llseek = no_llseek, .write = it8712f_wdt_write, .unlocked_ioctl = it8712f_wdt_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = it8712f_wdt_open, .release = it8712f_wdt_release, }; diff --git a/drivers/watchdog/it87_wdt.c b/drivers/watchdog/it87_wdt.c index a4b71ebc8cab..f3bf3ea50e39 100644 --- a/drivers/watchdog/it87_wdt.c +++ b/drivers/watchdog/it87_wdt.c @@ -67,6 +67,7 @@ #define IT8726_ID 0x8726 /* the data sheet suggest wrongly 0x8716 */ #define IT8728_ID 0x8728 #define IT8783_ID 0x8783 +#define IT8786_ID 0x8786 /* GPIO Configuration Registers LDN=0x07 */ #define WDTCTRL 0x71 @@ -294,6 +295,7 @@ static int __init it87_wdt_init(void) case IT8721_ID: case IT8728_ID: case IT8783_ID: + case IT8786_ID: max_units = 65535; break; case IT8705_ID: diff --git a/drivers/watchdog/ixp4xx_wdt.c b/drivers/watchdog/ixp4xx_wdt.c index 9067998759e3..09886616fd21 100644 --- a/drivers/watchdog/ixp4xx_wdt.c +++ b/drivers/watchdog/ixp4xx_wdt.c @@ -163,6 +163,7 @@ static const struct file_operations ixp4xx_wdt_fops = { .llseek = no_llseek, .write = ixp4xx_wdt_write, .unlocked_ioctl = ixp4xx_wdt_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = ixp4xx_wdt_open, .release = ixp4xx_wdt_release, }; diff --git a/drivers/watchdog/jz4740_wdt.c b/drivers/watchdog/jz4740_wdt.c index d4a90916dd38..bdf9564efa29 100644 --- a/drivers/watchdog/jz4740_wdt.c +++ b/drivers/watchdog/jz4740_wdt.c @@ -5,6 +5,7 @@ */ #include <linux/mfd/ingenic-tcu.h> +#include <linux/mfd/syscon.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/types.h> @@ -17,19 +18,7 @@ #include <linux/slab.h> #include <linux/err.h> #include <linux/of.h> - -#include <asm/mach-jz4740/timer.h> - -#define JZ_WDT_CLOCK_PCLK 0x1 -#define JZ_WDT_CLOCK_RTC 0x2 -#define JZ_WDT_CLOCK_EXT 0x4 - -#define JZ_WDT_CLOCK_DIV_1 (0 << TCU_TCSR_PRESCALE_LSB) -#define JZ_WDT_CLOCK_DIV_4 (1 << TCU_TCSR_PRESCALE_LSB) -#define JZ_WDT_CLOCK_DIV_16 (2 << TCU_TCSR_PRESCALE_LSB) -#define JZ_WDT_CLOCK_DIV_64 (3 << TCU_TCSR_PRESCALE_LSB) -#define JZ_WDT_CLOCK_DIV_256 (4 << TCU_TCSR_PRESCALE_LSB) -#define JZ_WDT_CLOCK_DIV_1024 (5 << TCU_TCSR_PRESCALE_LSB) +#include <linux/regmap.h> #define DEFAULT_HEARTBEAT 5 #define MAX_HEARTBEAT 2048 @@ -49,15 +38,17 @@ MODULE_PARM_DESC(heartbeat, struct jz4740_wdt_drvdata { struct watchdog_device wdt; - void __iomem *base; - struct clk *rtc_clk; + struct regmap *map; + struct clk *clk; + unsigned long clk_rate; }; static int jz4740_wdt_ping(struct watchdog_device *wdt_dev) { struct jz4740_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev); - writew(0x0, drvdata->base + TCU_REG_WDT_TCNT); + regmap_write(drvdata->map, TCU_REG_WDT_TCNT, 0); + return 0; } @@ -65,35 +56,17 @@ static int jz4740_wdt_set_timeout(struct watchdog_device *wdt_dev, unsigned int new_timeout) { struct jz4740_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev); - unsigned int rtc_clk_rate; - unsigned int timeout_value; - unsigned short clock_div = JZ_WDT_CLOCK_DIV_1; - u8 tcer; - - rtc_clk_rate = clk_get_rate(drvdata->rtc_clk); - - timeout_value = rtc_clk_rate * new_timeout; - while (timeout_value > 0xffff) { - if (clock_div == JZ_WDT_CLOCK_DIV_1024) { - /* Requested timeout too high; - * use highest possible value. */ - timeout_value = 0xffff; - break; - } - timeout_value >>= 2; - clock_div += (1 << TCU_TCSR_PRESCALE_LSB); - } + u16 timeout_value = (u16)(drvdata->clk_rate * new_timeout); + unsigned int tcer; - tcer = readb(drvdata->base + TCU_REG_WDT_TCER); - writeb(0x0, drvdata->base + TCU_REG_WDT_TCER); - writew(clock_div, drvdata->base + TCU_REG_WDT_TCSR); + regmap_read(drvdata->map, TCU_REG_WDT_TCER, &tcer); + regmap_write(drvdata->map, TCU_REG_WDT_TCER, 0); - writew((u16)timeout_value, drvdata->base + TCU_REG_WDT_TDR); - writew(0x0, drvdata->base + TCU_REG_WDT_TCNT); - writew(clock_div | JZ_WDT_CLOCK_RTC, drvdata->base + TCU_REG_WDT_TCSR); + regmap_write(drvdata->map, TCU_REG_WDT_TDR, timeout_value); + regmap_write(drvdata->map, TCU_REG_WDT_TCNT, 0); if (tcer & TCU_WDT_TCER_TCEN) - writeb(TCU_WDT_TCER_TCEN, drvdata->base + TCU_REG_WDT_TCER); + regmap_write(drvdata->map, TCU_REG_WDT_TCER, TCU_WDT_TCER_TCEN); wdt_dev->timeout = new_timeout; return 0; @@ -102,16 +75,20 @@ static int jz4740_wdt_set_timeout(struct watchdog_device *wdt_dev, static int jz4740_wdt_start(struct watchdog_device *wdt_dev) { struct jz4740_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev); - u8 tcer; + unsigned int tcer; + int ret; - tcer = readb(drvdata->base + TCU_REG_WDT_TCER); + ret = clk_prepare_enable(drvdata->clk); + if (ret) + return ret; + + regmap_read(drvdata->map, TCU_REG_WDT_TCER, &tcer); - jz4740_timer_enable_watchdog(); jz4740_wdt_set_timeout(wdt_dev, wdt_dev->timeout); /* Start watchdog if it wasn't started already */ if (!(tcer & TCU_WDT_TCER_TCEN)) - writeb(TCU_WDT_TCER_TCEN, drvdata->base + TCU_REG_WDT_TCER); + regmap_write(drvdata->map, TCU_REG_WDT_TCER, TCU_WDT_TCER_TCEN); return 0; } @@ -120,8 +97,8 @@ static int jz4740_wdt_stop(struct watchdog_device *wdt_dev) { struct jz4740_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev); - writeb(0x0, drvdata->base + TCU_REG_WDT_TCER); - jz4740_timer_disable_watchdog(); + regmap_write(drvdata->map, TCU_REG_WDT_TCER, 0); + clk_disable_unprepare(drvdata->clk); return 0; } @@ -162,6 +139,7 @@ static int jz4740_wdt_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct jz4740_wdt_drvdata *drvdata; struct watchdog_device *jz4740_wdt; + long rate; int ret; drvdata = devm_kzalloc(dev, sizeof(struct jz4740_wdt_drvdata), @@ -169,27 +147,38 @@ static int jz4740_wdt_probe(struct platform_device *pdev) if (!drvdata) return -ENOMEM; - if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT) - heartbeat = DEFAULT_HEARTBEAT; + drvdata->clk = devm_clk_get(&pdev->dev, "wdt"); + if (IS_ERR(drvdata->clk)) { + dev_err(&pdev->dev, "cannot find WDT clock\n"); + return PTR_ERR(drvdata->clk); + } + + /* Set smallest clock possible */ + rate = clk_round_rate(drvdata->clk, 1); + if (rate < 0) + return rate; + ret = clk_set_rate(drvdata->clk, rate); + if (ret) + return ret; + + drvdata->clk_rate = rate; jz4740_wdt = &drvdata->wdt; jz4740_wdt->info = &jz4740_wdt_info; jz4740_wdt->ops = &jz4740_wdt_ops; - jz4740_wdt->timeout = heartbeat; jz4740_wdt->min_timeout = 1; - jz4740_wdt->max_timeout = MAX_HEARTBEAT; + jz4740_wdt->max_timeout = 0xffff / rate; + jz4740_wdt->timeout = clamp(heartbeat, + jz4740_wdt->min_timeout, + jz4740_wdt->max_timeout); jz4740_wdt->parent = dev; watchdog_set_nowayout(jz4740_wdt, nowayout); watchdog_set_drvdata(jz4740_wdt, drvdata); - drvdata->base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(drvdata->base)) - return PTR_ERR(drvdata->base); - - drvdata->rtc_clk = devm_clk_get(dev, "rtc"); - if (IS_ERR(drvdata->rtc_clk)) { - dev_err(dev, "cannot find RTC clock\n"); - return PTR_ERR(drvdata->rtc_clk); + drvdata->map = device_node_to_regmap(dev->parent->of_node); + if (!drvdata->map) { + dev_err(dev, "regmap not found\n"); + return -EINVAL; } return devm_watchdog_register_device(dev, &drvdata->wdt); diff --git a/drivers/watchdog/ks8695_wdt.c b/drivers/watchdog/ks8695_wdt.c deleted file mode 100644 index 1550ce3c5702..000000000000 --- a/drivers/watchdog/ks8695_wdt.c +++ /dev/null @@ -1,319 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Watchdog driver for Kendin/Micrel KS8695. - * - * (C) 2007 Andrew Victor - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include <linux/bitops.h> -#include <linux/errno.h> -#include <linux/fs.h> -#include <linux/init.h> -#include <linux/kernel.h> -#include <linux/miscdevice.h> -#include <linux/module.h> -#include <linux/moduleparam.h> -#include <linux/platform_device.h> -#include <linux/types.h> -#include <linux/watchdog.h> -#include <linux/io.h> -#include <linux/uaccess.h> -#include <mach/hardware.h> - -#define KS8695_TMR_OFFSET (0xF0000 + 0xE400) -#define KS8695_TMR_VA (KS8695_IO_VA + KS8695_TMR_OFFSET) - -/* - * Timer registers - */ -#define KS8695_TMCON (0x00) /* Timer Control Register */ -#define KS8695_T0TC (0x08) /* Timer 0 Timeout Count Register */ -#define TMCON_T0EN (1 << 0) /* Timer 0 Enable */ - -/* Timer0 Timeout Counter Register */ -#define T0TC_WATCHDOG (0xff) /* Enable watchdog mode */ - -#define WDT_DEFAULT_TIME 5 /* seconds */ -#define WDT_MAX_TIME 171 /* seconds */ - -static int wdt_time = WDT_DEFAULT_TIME; -static bool nowayout = WATCHDOG_NOWAYOUT; - -module_param(wdt_time, int, 0); -MODULE_PARM_DESC(wdt_time, "Watchdog time in seconds. (default=" - __MODULE_STRING(WDT_DEFAULT_TIME) ")"); - -#ifdef CONFIG_WATCHDOG_NOWAYOUT -module_param(nowayout, bool, 0); -MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" - __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); -#endif - - -static unsigned long ks8695wdt_busy; -static DEFINE_SPINLOCK(ks8695_lock); - -/* ......................................................................... */ - -/* - * Disable the watchdog. - */ -static inline void ks8695_wdt_stop(void) -{ - unsigned long tmcon; - - spin_lock(&ks8695_lock); - /* disable timer0 */ - tmcon = __raw_readl(KS8695_TMR_VA + KS8695_TMCON); - __raw_writel(tmcon & ~TMCON_T0EN, KS8695_TMR_VA + KS8695_TMCON); - spin_unlock(&ks8695_lock); -} - -/* - * Enable and reset the watchdog. - */ -static inline void ks8695_wdt_start(void) -{ - unsigned long tmcon; - unsigned long tval = wdt_time * KS8695_CLOCK_RATE; - - spin_lock(&ks8695_lock); - /* disable timer0 */ - tmcon = __raw_readl(KS8695_TMR_VA + KS8695_TMCON); - __raw_writel(tmcon & ~TMCON_T0EN, KS8695_TMR_VA + KS8695_TMCON); - - /* program timer0 */ - __raw_writel(tval | T0TC_WATCHDOG, KS8695_TMR_VA + KS8695_T0TC); - - /* re-enable timer0 */ - tmcon = __raw_readl(KS8695_TMR_VA + KS8695_TMCON); - __raw_writel(tmcon | TMCON_T0EN, KS8695_TMR_VA + KS8695_TMCON); - spin_unlock(&ks8695_lock); -} - -/* - * Reload the watchdog timer. (ie, pat the watchdog) - */ -static inline void ks8695_wdt_reload(void) -{ - unsigned long tmcon; - - spin_lock(&ks8695_lock); - /* disable, then re-enable timer0 */ - tmcon = __raw_readl(KS8695_TMR_VA + KS8695_TMCON); - __raw_writel(tmcon & ~TMCON_T0EN, KS8695_TMR_VA + KS8695_TMCON); - __raw_writel(tmcon | TMCON_T0EN, KS8695_TMR_VA + KS8695_TMCON); - spin_unlock(&ks8695_lock); -} - -/* - * Change the watchdog time interval. - */ -static int ks8695_wdt_settimeout(int new_time) -{ - /* - * All counting occurs at KS8695_CLOCK_RATE / 128 = 0.256 Hz - * - * Since WDV is a 16-bit counter, the maximum period is - * 65536 / 0.256 = 256 seconds. - */ - if ((new_time <= 0) || (new_time > WDT_MAX_TIME)) - return -EINVAL; - - /* Set new watchdog time. It will be used when - ks8695_wdt_start() is called. */ - wdt_time = new_time; - return 0; -} - -/* ......................................................................... */ - -/* - * Watchdog device is opened, and watchdog starts running. - */ -static int ks8695_wdt_open(struct inode *inode, struct file *file) -{ - if (test_and_set_bit(0, &ks8695wdt_busy)) - return -EBUSY; - - ks8695_wdt_start(); - return stream_open(inode, file); -} - -/* - * Close the watchdog device. - * If CONFIG_WATCHDOG_NOWAYOUT is NOT defined then the watchdog is also - * disabled. - */ -static int ks8695_wdt_close(struct inode *inode, struct file *file) -{ - /* Disable the watchdog when file is closed */ - if (!nowayout) - ks8695_wdt_stop(); - clear_bit(0, &ks8695wdt_busy); - return 0; -} - -static const struct watchdog_info ks8695_wdt_info = { - .identity = "ks8695 watchdog", - .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, -}; - -/* - * Handle commands from user-space. - */ -static long ks8695_wdt_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) -{ - void __user *argp = (void __user *)arg; - int __user *p = argp; - int new_value; - - switch (cmd) { - case WDIOC_GETSUPPORT: - return copy_to_user(argp, &ks8695_wdt_info, - sizeof(ks8695_wdt_info)) ? -EFAULT : 0; - case WDIOC_GETSTATUS: - case WDIOC_GETBOOTSTATUS: - return put_user(0, p); - case WDIOC_SETOPTIONS: - if (get_user(new_value, p)) - return -EFAULT; - if (new_value & WDIOS_DISABLECARD) - ks8695_wdt_stop(); - if (new_value & WDIOS_ENABLECARD) - ks8695_wdt_start(); - return 0; - case WDIOC_KEEPALIVE: - ks8695_wdt_reload(); /* pat the watchdog */ - return 0; - case WDIOC_SETTIMEOUT: - if (get_user(new_value, p)) - return -EFAULT; - if (ks8695_wdt_settimeout(new_value)) - return -EINVAL; - /* Enable new time value */ - ks8695_wdt_start(); - /* Return current value */ - return put_user(wdt_time, p); - case WDIOC_GETTIMEOUT: - return put_user(wdt_time, p); - default: - return -ENOTTY; - } -} - -/* - * Pat the watchdog whenever device is written to. - */ -static ssize_t ks8695_wdt_write(struct file *file, const char *data, - size_t len, loff_t *ppos) -{ - ks8695_wdt_reload(); /* pat the watchdog */ - return len; -} - -/* ......................................................................... */ - -static const struct file_operations ks8695wdt_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .unlocked_ioctl = ks8695_wdt_ioctl, - .open = ks8695_wdt_open, - .release = ks8695_wdt_close, - .write = ks8695_wdt_write, -}; - -static struct miscdevice ks8695wdt_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &ks8695wdt_fops, -}; - -static int ks8695wdt_probe(struct platform_device *pdev) -{ - int res; - - if (ks8695wdt_miscdev.parent) - return -EBUSY; - ks8695wdt_miscdev.parent = &pdev->dev; - - res = misc_register(&ks8695wdt_miscdev); - if (res) - return res; - - pr_info("KS8695 Watchdog Timer enabled (%d seconds%s)\n", - wdt_time, nowayout ? ", nowayout" : ""); - return 0; -} - -static int ks8695wdt_remove(struct platform_device *pdev) -{ - misc_deregister(&ks8695wdt_miscdev); - ks8695wdt_miscdev.parent = NULL; - - return 0; -} - -static void ks8695wdt_shutdown(struct platform_device *pdev) -{ - ks8695_wdt_stop(); -} - -#ifdef CONFIG_PM - -static int ks8695wdt_suspend(struct platform_device *pdev, pm_message_t message) -{ - ks8695_wdt_stop(); - return 0; -} - -static int ks8695wdt_resume(struct platform_device *pdev) -{ - if (ks8695wdt_busy) - ks8695_wdt_start(); - return 0; -} - -#else -#define ks8695wdt_suspend NULL -#define ks8695wdt_resume NULL -#endif - -static struct platform_driver ks8695wdt_driver = { - .probe = ks8695wdt_probe, - .remove = ks8695wdt_remove, - .shutdown = ks8695wdt_shutdown, - .suspend = ks8695wdt_suspend, - .resume = ks8695wdt_resume, - .driver = { - .name = "ks8695_wdt", - }, -}; - -static int __init ks8695_wdt_init(void) -{ - /* Check that the heartbeat value is within range; - if not reset to the default */ - if (ks8695_wdt_settimeout(wdt_time)) { - ks8695_wdt_settimeout(WDT_DEFAULT_TIME); - pr_info("ks8695_wdt: wdt_time value must be 1 <= wdt_time <= %i" - ", using %d\n", wdt_time, WDT_MAX_TIME); - } - return platform_driver_register(&ks8695wdt_driver); -} - -static void __exit ks8695_wdt_exit(void) -{ - platform_driver_unregister(&ks8695wdt_driver); -} - -module_init(ks8695_wdt_init); -module_exit(ks8695_wdt_exit); - -MODULE_AUTHOR("Andrew Victor"); -MODULE_DESCRIPTION("Watchdog driver for KS8695"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:ks8695_wdt"); diff --git a/drivers/watchdog/m54xx_wdt.c b/drivers/watchdog/m54xx_wdt.c index 752d03620f0a..22f335e1e164 100644 --- a/drivers/watchdog/m54xx_wdt.c +++ b/drivers/watchdog/m54xx_wdt.c @@ -183,6 +183,7 @@ static const struct file_operations m54xx_wdt_fops = { .llseek = no_llseek, .write = m54xx_wdt_write, .unlocked_ioctl = m54xx_wdt_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = m54xx_wdt_open, .release = m54xx_wdt_release, }; diff --git a/drivers/watchdog/machzwd.c b/drivers/watchdog/machzwd.c index cef2baf59dda..80ff94688487 100644 --- a/drivers/watchdog/machzwd.c +++ b/drivers/watchdog/machzwd.c @@ -361,6 +361,7 @@ static const struct file_operations zf_fops = { .llseek = no_llseek, .write = zf_write, .unlocked_ioctl = zf_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = zf_open, .release = zf_close, }; diff --git a/drivers/watchdog/menz69_wdt.c b/drivers/watchdog/menz69_wdt.c index ed18238c5407..8973f98bc6a5 100644 --- a/drivers/watchdog/menz69_wdt.c +++ b/drivers/watchdog/menz69_wdt.c @@ -168,3 +168,4 @@ module_mcb_driver(men_z069_driver); MODULE_AUTHOR("Johannes Thumshirn <jth@kernel.org>"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("mcb:16z069"); +MODULE_IMPORT_NS(MCB); diff --git a/drivers/watchdog/meson_gxbb_wdt.c b/drivers/watchdog/meson_gxbb_wdt.c index d17c1a6ed723..5a9ca10fbcfa 100644 --- a/drivers/watchdog/meson_gxbb_wdt.c +++ b/drivers/watchdog/meson_gxbb_wdt.c @@ -89,8 +89,8 @@ static unsigned int meson_gxbb_wdt_get_timeleft(struct watchdog_device *wdt_dev) reg = readl(data->reg_base + GXBB_WDT_TCNT_REG); - return ((reg >> GXBB_WDT_TCNT_CNT_SHIFT) - - (reg & GXBB_WDT_TCNT_SETUP_MASK)) / 1000; + return ((reg & GXBB_WDT_TCNT_SETUP_MASK) - + (reg >> GXBB_WDT_TCNT_CNT_SHIFT)) / 1000; } static const struct watchdog_ops meson_gxbb_wdt_ops = { diff --git a/drivers/watchdog/mixcomwd.c b/drivers/watchdog/mixcomwd.c index a86faa5000f1..d387bad377c4 100644 --- a/drivers/watchdog/mixcomwd.c +++ b/drivers/watchdog/mixcomwd.c @@ -227,6 +227,7 @@ static const struct file_operations mixcomwd_fops = { .llseek = no_llseek, .write = mixcomwd_write, .unlocked_ioctl = mixcomwd_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = mixcomwd_open, .release = mixcomwd_release, }; diff --git a/drivers/watchdog/mtk_wdt.c b/drivers/watchdog/mtk_wdt.c index 9c3d0033260d..d6a6393f609d 100644 --- a/drivers/watchdog/mtk_wdt.c +++ b/drivers/watchdog/mtk_wdt.c @@ -9,6 +9,9 @@ * Based on sunxi_wdt.c */ +#include <dt-bindings/reset-controller/mt2712-resets.h> +#include <dt-bindings/reset-controller/mt8183-resets.h> +#include <linux/delay.h> #include <linux/err.h> #include <linux/init.h> #include <linux/io.h> @@ -16,10 +19,11 @@ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/of.h> +#include <linux/of_device.h> #include <linux/platform_device.h> +#include <linux/reset-controller.h> #include <linux/types.h> #include <linux/watchdog.h> -#include <linux/delay.h> #define WDT_MAX_TIMEOUT 31 #define WDT_MIN_TIMEOUT 1 @@ -44,6 +48,9 @@ #define WDT_SWRST 0x14 #define WDT_SWRST_KEY 0x1209 +#define WDT_SWSYSRST 0x18U +#define WDT_SWSYS_RST_KEY 0x88000000 + #define DRV_NAME "mtk-wdt" #define DRV_VERSION "1.0" @@ -53,8 +60,94 @@ static unsigned int timeout; struct mtk_wdt_dev { struct watchdog_device wdt_dev; void __iomem *wdt_base; + spinlock_t lock; /* protects WDT_SWSYSRST reg */ + struct reset_controller_dev rcdev; +}; + +struct mtk_wdt_data { + int toprgu_sw_rst_num; }; +static const struct mtk_wdt_data mt2712_data = { + .toprgu_sw_rst_num = MT2712_TOPRGU_SW_RST_NUM, +}; + +static const struct mtk_wdt_data mt8183_data = { + .toprgu_sw_rst_num = MT8183_TOPRGU_SW_RST_NUM, +}; + +static int toprgu_reset_update(struct reset_controller_dev *rcdev, + unsigned long id, bool assert) +{ + unsigned int tmp; + unsigned long flags; + struct mtk_wdt_dev *data = + container_of(rcdev, struct mtk_wdt_dev, rcdev); + + spin_lock_irqsave(&data->lock, flags); + + tmp = readl(data->wdt_base + WDT_SWSYSRST); + if (assert) + tmp |= BIT(id); + else + tmp &= ~BIT(id); + tmp |= WDT_SWSYS_RST_KEY; + writel(tmp, data->wdt_base + WDT_SWSYSRST); + + spin_unlock_irqrestore(&data->lock, flags); + + return 0; +} + +static int toprgu_reset_assert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + return toprgu_reset_update(rcdev, id, true); +} + +static int toprgu_reset_deassert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + return toprgu_reset_update(rcdev, id, false); +} + +static int toprgu_reset(struct reset_controller_dev *rcdev, + unsigned long id) +{ + int ret; + + ret = toprgu_reset_assert(rcdev, id); + if (ret) + return ret; + + return toprgu_reset_deassert(rcdev, id); +} + +static const struct reset_control_ops toprgu_reset_ops = { + .assert = toprgu_reset_assert, + .deassert = toprgu_reset_deassert, + .reset = toprgu_reset, +}; + +static int toprgu_register_reset_controller(struct platform_device *pdev, + int rst_num) +{ + int ret; + struct mtk_wdt_dev *mtk_wdt = platform_get_drvdata(pdev); + + spin_lock_init(&mtk_wdt->lock); + + mtk_wdt->rcdev.owner = THIS_MODULE; + mtk_wdt->rcdev.nr_resets = rst_num; + mtk_wdt->rcdev.ops = &toprgu_reset_ops; + mtk_wdt->rcdev.of_node = pdev->dev.of_node; + ret = devm_reset_controller_register(&pdev->dev, &mtk_wdt->rcdev); + if (ret != 0) + dev_err(&pdev->dev, + "couldn't register wdt reset controller: %d\n", ret); + return ret; +} + static int mtk_wdt_restart(struct watchdog_device *wdt_dev, unsigned long action, void *data) { @@ -155,6 +248,7 @@ static int mtk_wdt_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct mtk_wdt_dev *mtk_wdt; + const struct mtk_wdt_data *wdt_data; int err; mtk_wdt = devm_kzalloc(dev, sizeof(*mtk_wdt), GFP_KERNEL); @@ -190,6 +284,13 @@ static int mtk_wdt_probe(struct platform_device *pdev) dev_info(dev, "Watchdog enabled (timeout=%d sec, nowayout=%d)\n", mtk_wdt->wdt_dev.timeout, nowayout); + wdt_data = of_device_get_match_data(dev); + if (wdt_data) { + err = toprgu_register_reset_controller(pdev, + wdt_data->toprgu_sw_rst_num); + if (err) + return err; + } return 0; } @@ -218,7 +319,9 @@ static int mtk_wdt_resume(struct device *dev) #endif static const struct of_device_id mtk_wdt_dt_ids[] = { + { .compatible = "mediatek,mt2712-wdt", .data = &mt2712_data }, { .compatible = "mediatek,mt6589-wdt" }, + { .compatible = "mediatek,mt8183-wdt", .data = &mt8183_data }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, mtk_wdt_dt_ids); diff --git a/drivers/watchdog/mtx-1_wdt.c b/drivers/watchdog/mtx-1_wdt.c index 25a92857b217..8aa1cb4a295f 100644 --- a/drivers/watchdog/mtx-1_wdt.c +++ b/drivers/watchdog/mtx-1_wdt.c @@ -181,6 +181,7 @@ 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, .write = mtx1_wdt_write, .release = mtx1_wdt_release, diff --git a/drivers/watchdog/mv64x60_wdt.c b/drivers/watchdog/mv64x60_wdt.c index 74bf7144a970..0bc72dd69b70 100644 --- a/drivers/watchdog/mv64x60_wdt.c +++ b/drivers/watchdog/mv64x60_wdt.c @@ -241,6 +241,7 @@ static const struct file_operations mv64x60_wdt_fops = { .llseek = no_llseek, .write = mv64x60_wdt_write, .unlocked_ioctl = mv64x60_wdt_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = mv64x60_wdt_open, .release = mv64x60_wdt_release, }; diff --git a/drivers/watchdog/nuc900_wdt.c b/drivers/watchdog/nuc900_wdt.c deleted file mode 100644 index db124cebe838..000000000000 --- a/drivers/watchdog/nuc900_wdt.c +++ /dev/null @@ -1,302 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (c) 2009 Nuvoton technology corporation. - * - * Wan ZongShun <mcuos.com@gmail.com> - */ - -#include <linux/bitops.h> -#include <linux/errno.h> -#include <linux/fs.h> -#include <linux/io.h> -#include <linux/clk.h> -#include <linux/kernel.h> -#include <linux/miscdevice.h> -#include <linux/module.h> -#include <linux/moduleparam.h> -#include <linux/platform_device.h> -#include <linux/slab.h> -#include <linux/interrupt.h> -#include <linux/types.h> -#include <linux/watchdog.h> -#include <linux/uaccess.h> - -#define REG_WTCR 0x1c -#define WTCLK (0x01 << 10) -#define WTE (0x01 << 7) /*wdt enable*/ -#define WTIS (0x03 << 4) -#define WTIF (0x01 << 3) -#define WTRF (0x01 << 2) -#define WTRE (0x01 << 1) -#define WTR (0x01 << 0) -/* - * The watchdog time interval can be calculated via following formula: - * WTIS real time interval (formula) - * 0x00 ((2^ 14 ) * ((external crystal freq) / 256))seconds - * 0x01 ((2^ 16 ) * ((external crystal freq) / 256))seconds - * 0x02 ((2^ 18 ) * ((external crystal freq) / 256))seconds - * 0x03 ((2^ 20 ) * ((external crystal freq) / 256))seconds - * - * The external crystal freq is 15Mhz in the nuc900 evaluation board. - * So 0x00 = +-0.28 seconds, 0x01 = +-1.12 seconds, 0x02 = +-4.48 seconds, - * 0x03 = +- 16.92 seconds.. - */ -#define WDT_HW_TIMEOUT 0x02 -#define WDT_TIMEOUT (HZ/2) -#define WDT_HEARTBEAT 15 - -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) ")"); - -struct nuc900_wdt { - struct clk *wdt_clock; - struct platform_device *pdev; - void __iomem *wdt_base; - char expect_close; - struct timer_list timer; - spinlock_t wdt_lock; - unsigned long next_heartbeat; -}; - -static unsigned long nuc900wdt_busy; -static struct nuc900_wdt *nuc900_wdt; - -static inline void nuc900_wdt_keepalive(void) -{ - unsigned int val; - - spin_lock(&nuc900_wdt->wdt_lock); - - val = __raw_readl(nuc900_wdt->wdt_base + REG_WTCR); - val |= (WTR | WTIF); - __raw_writel(val, nuc900_wdt->wdt_base + REG_WTCR); - - spin_unlock(&nuc900_wdt->wdt_lock); -} - -static inline void nuc900_wdt_start(void) -{ - unsigned int val; - - spin_lock(&nuc900_wdt->wdt_lock); - - val = __raw_readl(nuc900_wdt->wdt_base + REG_WTCR); - val |= (WTRE | WTE | WTR | WTCLK | WTIF); - val &= ~WTIS; - val |= (WDT_HW_TIMEOUT << 0x04); - __raw_writel(val, nuc900_wdt->wdt_base + REG_WTCR); - - spin_unlock(&nuc900_wdt->wdt_lock); - - nuc900_wdt->next_heartbeat = jiffies + heartbeat * HZ; - mod_timer(&nuc900_wdt->timer, jiffies + WDT_TIMEOUT); -} - -static inline void nuc900_wdt_stop(void) -{ - unsigned int val; - - del_timer(&nuc900_wdt->timer); - - spin_lock(&nuc900_wdt->wdt_lock); - - val = __raw_readl(nuc900_wdt->wdt_base + REG_WTCR); - val &= ~WTE; - __raw_writel(val, nuc900_wdt->wdt_base + REG_WTCR); - - spin_unlock(&nuc900_wdt->wdt_lock); -} - -static inline void nuc900_wdt_ping(void) -{ - nuc900_wdt->next_heartbeat = jiffies + heartbeat * HZ; -} - -static int nuc900_wdt_open(struct inode *inode, struct file *file) -{ - - if (test_and_set_bit(0, &nuc900wdt_busy)) - return -EBUSY; - - nuc900_wdt_start(); - - return stream_open(inode, file); -} - -static int nuc900_wdt_close(struct inode *inode, struct file *file) -{ - if (nuc900_wdt->expect_close == 42) - nuc900_wdt_stop(); - else { - dev_crit(&nuc900_wdt->pdev->dev, - "Unexpected close, not stopping watchdog!\n"); - nuc900_wdt_ping(); - } - - nuc900_wdt->expect_close = 0; - clear_bit(0, &nuc900wdt_busy); - return 0; -} - -static const struct watchdog_info nuc900_wdt_info = { - .identity = "nuc900 watchdog", - .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | - WDIOF_MAGICCLOSE, -}; - -static long nuc900_wdt_ioctl(struct file *file, - unsigned int cmd, unsigned long arg) -{ - void __user *argp = (void __user *)arg; - int __user *p = argp; - int new_value; - - switch (cmd) { - case WDIOC_GETSUPPORT: - return copy_to_user(argp, &nuc900_wdt_info, - sizeof(nuc900_wdt_info)) ? -EFAULT : 0; - case WDIOC_GETSTATUS: - case WDIOC_GETBOOTSTATUS: - return put_user(0, p); - - case WDIOC_KEEPALIVE: - nuc900_wdt_ping(); - return 0; - - case WDIOC_SETTIMEOUT: - if (get_user(new_value, p)) - return -EFAULT; - - heartbeat = new_value; - nuc900_wdt_ping(); - - return put_user(new_value, p); - case WDIOC_GETTIMEOUT: - return put_user(heartbeat, p); - default: - return -ENOTTY; - } -} - -static ssize_t nuc900_wdt_write(struct file *file, const char __user *data, - size_t len, loff_t *ppos) -{ - if (!len) - return 0; - - /* Scan for magic character */ - if (!nowayout) { - size_t i; - - nuc900_wdt->expect_close = 0; - - for (i = 0; i < len; i++) { - char c; - if (get_user(c, data + i)) - return -EFAULT; - if (c == 'V') { - nuc900_wdt->expect_close = 42; - break; - } - } - } - - nuc900_wdt_ping(); - return len; -} - -static void nuc900_wdt_timer_ping(struct timer_list *unused) -{ - if (time_before(jiffies, nuc900_wdt->next_heartbeat)) { - nuc900_wdt_keepalive(); - mod_timer(&nuc900_wdt->timer, jiffies + WDT_TIMEOUT); - } else - dev_warn(&nuc900_wdt->pdev->dev, "Will reset the machine !\n"); -} - -static const struct file_operations nuc900wdt_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .unlocked_ioctl = nuc900_wdt_ioctl, - .open = nuc900_wdt_open, - .release = nuc900_wdt_close, - .write = nuc900_wdt_write, -}; - -static struct miscdevice nuc900wdt_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &nuc900wdt_fops, -}; - -static int nuc900wdt_probe(struct platform_device *pdev) -{ - int ret = 0; - - nuc900_wdt = devm_kzalloc(&pdev->dev, sizeof(*nuc900_wdt), - GFP_KERNEL); - if (!nuc900_wdt) - return -ENOMEM; - - nuc900_wdt->pdev = pdev; - - spin_lock_init(&nuc900_wdt->wdt_lock); - - nuc900_wdt->wdt_base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(nuc900_wdt->wdt_base)) - return PTR_ERR(nuc900_wdt->wdt_base); - - nuc900_wdt->wdt_clock = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(nuc900_wdt->wdt_clock)) { - dev_err(&pdev->dev, "failed to find watchdog clock source\n"); - return PTR_ERR(nuc900_wdt->wdt_clock); - } - - clk_enable(nuc900_wdt->wdt_clock); - - timer_setup(&nuc900_wdt->timer, nuc900_wdt_timer_ping, 0); - - ret = misc_register(&nuc900wdt_miscdev); - if (ret) { - dev_err(&pdev->dev, "err register miscdev on minor=%d (%d)\n", - WATCHDOG_MINOR, ret); - goto err_clk; - } - - return 0; - -err_clk: - clk_disable(nuc900_wdt->wdt_clock); - return ret; -} - -static int nuc900wdt_remove(struct platform_device *pdev) -{ - misc_deregister(&nuc900wdt_miscdev); - - clk_disable(nuc900_wdt->wdt_clock); - - return 0; -} - -static struct platform_driver nuc900wdt_driver = { - .probe = nuc900wdt_probe, - .remove = nuc900wdt_remove, - .driver = { - .name = "nuc900-wdt", - }, -}; - -module_platform_driver(nuc900wdt_driver); - -MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>"); -MODULE_DESCRIPTION("Watchdog driver for NUC900"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:nuc900-wdt"); diff --git a/drivers/watchdog/nv_tco.c b/drivers/watchdog/nv_tco.c index 5f0082e300bd..d7a560e348d5 100644 --- a/drivers/watchdog/nv_tco.c +++ b/drivers/watchdog/nv_tco.c @@ -267,6 +267,7 @@ static const struct file_operations nv_tco_fops = { .llseek = no_llseek, .write = nv_tco_write, .unlocked_ioctl = nv_tco_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = nv_tco_open, .release = nv_tco_release, }; diff --git a/drivers/watchdog/orion_wdt.c b/drivers/watchdog/orion_wdt.c index cdb0d174c5e2..8e6dfe76f9c9 100644 --- a/drivers/watchdog/orion_wdt.c +++ b/drivers/watchdog/orion_wdt.c @@ -35,7 +35,15 @@ * Watchdog timer block registers. */ #define TIMER_CTRL 0x0000 -#define TIMER_A370_STATUS 0x04 +#define TIMER1_FIXED_ENABLE_BIT BIT(12) +#define WDT_AXP_FIXED_ENABLE_BIT BIT(10) +#define TIMER1_ENABLE_BIT BIT(2) + +#define TIMER_A370_STATUS 0x0004 +#define WDT_A370_EXPIRED BIT(31) +#define TIMER1_STATUS_BIT BIT(8) + +#define TIMER1_VAL_OFF 0x001c #define WDT_MAX_CYCLE_COUNT 0xffffffff @@ -43,9 +51,6 @@ #define WDT_A370_RATIO_SHIFT 5 #define WDT_A370_RATIO (1 << WDT_A370_RATIO_SHIFT) -#define WDT_AXP_FIXED_ENABLE_BIT BIT(10) -#define WDT_A370_EXPIRED BIT(31) - static bool nowayout = WATCHDOG_NOWAYOUT; static int heartbeat = -1; /* module parameter (seconds) */ @@ -158,6 +163,7 @@ static int armadaxp_wdt_clock_init(struct platform_device *pdev, struct orion_watchdog *dev) { int ret; + u32 val; dev->clk = of_clk_get_by_name(pdev->dev.of_node, "fixed"); if (IS_ERR(dev->clk)) @@ -168,10 +174,9 @@ static int armadaxp_wdt_clock_init(struct platform_device *pdev, return ret; } - /* Enable the fixed watchdog clock input */ - atomic_io_modify(dev->reg + TIMER_CTRL, - WDT_AXP_FIXED_ENABLE_BIT, - WDT_AXP_FIXED_ENABLE_BIT); + /* Fix the wdt and timer1 clock freqency to 25MHz */ + val = WDT_AXP_FIXED_ENABLE_BIT | TIMER1_FIXED_ENABLE_BIT; + atomic_io_modify(dev->reg + TIMER_CTRL, val, val); dev->clk_rate = clk_get_rate(dev->clk); return 0; @@ -183,6 +188,10 @@ static int orion_wdt_ping(struct watchdog_device *wdt_dev) /* Reload watchdog duration */ writel(dev->clk_rate * wdt_dev->timeout, dev->reg + dev->data->wdt_counter_offset); + if (dev->wdt.info->options & WDIOF_PRETIMEOUT) + writel(dev->clk_rate * (wdt_dev->timeout - wdt_dev->pretimeout), + dev->reg + TIMER1_VAL_OFF); + return 0; } @@ -194,13 +203,18 @@ static int armada375_start(struct watchdog_device *wdt_dev) /* Set watchdog duration */ writel(dev->clk_rate * wdt_dev->timeout, dev->reg + dev->data->wdt_counter_offset); + if (dev->wdt.info->options & WDIOF_PRETIMEOUT) + writel(dev->clk_rate * (wdt_dev->timeout - wdt_dev->pretimeout), + dev->reg + TIMER1_VAL_OFF); /* Clear the watchdog expiration bit */ atomic_io_modify(dev->reg + TIMER_A370_STATUS, WDT_A370_EXPIRED, 0); /* Enable watchdog timer */ - atomic_io_modify(dev->reg + TIMER_CTRL, dev->data->wdt_enable_bit, - dev->data->wdt_enable_bit); + reg = dev->data->wdt_enable_bit; + if (dev->wdt.info->options & WDIOF_PRETIMEOUT) + reg |= TIMER1_ENABLE_BIT; + atomic_io_modify(dev->reg + TIMER_CTRL, reg, reg); /* Enable reset on watchdog */ reg = readl(dev->rstout); @@ -277,7 +291,7 @@ static int orion_stop(struct watchdog_device *wdt_dev) static int armada375_stop(struct watchdog_device *wdt_dev) { struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev); - u32 reg; + u32 reg, mask; /* Disable reset on watchdog */ atomic_io_modify(dev->rstout_mask, dev->data->rstout_mask_bit, @@ -287,7 +301,10 @@ static int armada375_stop(struct watchdog_device *wdt_dev) writel(reg, dev->rstout); /* Disable watchdog timer */ - atomic_io_modify(dev->reg + TIMER_CTRL, dev->data->wdt_enable_bit, 0); + mask = dev->data->wdt_enable_bit; + if (wdt_dev->info->options & WDIOF_PRETIMEOUT) + mask |= TIMER1_ENABLE_BIT; + atomic_io_modify(dev->reg + TIMER_CTRL, mask, 0); return 0; } @@ -349,7 +366,7 @@ static unsigned int orion_wdt_get_timeleft(struct watchdog_device *wdt_dev) return readl(dev->reg + dev->data->wdt_counter_offset) / dev->clk_rate; } -static const struct watchdog_info orion_wdt_info = { +static struct watchdog_info orion_wdt_info = { .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, .identity = "Orion Watchdog", }; @@ -368,6 +385,16 @@ static irqreturn_t orion_wdt_irq(int irq, void *devid) return IRQ_HANDLED; } +static irqreturn_t orion_wdt_pre_irq(int irq, void *devid) +{ + struct orion_watchdog *dev = devid; + + atomic_io_modify(dev->reg + TIMER_A370_STATUS, + TIMER1_STATUS_BIT, 0); + watchdog_notify_pretimeout(&dev->wdt); + return IRQ_HANDLED; +} + /* * The original devicetree binding for this driver specified only * one memory resource, so in order to keep DT backwards compatibility @@ -575,7 +602,7 @@ static int orion_wdt_probe(struct platform_device *pdev) set_bit(WDOG_HW_RUNNING, &dev->wdt.status); /* Request the IRQ only after the watchdog is disabled */ - irq = platform_get_irq(pdev, 0); + irq = platform_get_irq_optional(pdev, 0); if (irq > 0) { /* * Not all supported platforms specify an interrupt for the @@ -589,6 +616,19 @@ static int orion_wdt_probe(struct platform_device *pdev) } } + /* Optional 2nd interrupt for pretimeout */ + irq = platform_get_irq_optional(pdev, 1); + if (irq > 0) { + orion_wdt_info.options |= WDIOF_PRETIMEOUT; + ret = devm_request_irq(&pdev->dev, irq, orion_wdt_pre_irq, + 0, pdev->name, dev); + if (ret < 0) { + dev_err(&pdev->dev, "failed to request IRQ\n"); + goto disable_clk; + } + } + + watchdog_set_nowayout(&dev->wdt, nowayout); ret = watchdog_register_device(&dev->wdt); if (ret) diff --git a/drivers/watchdog/pc87413_wdt.c b/drivers/watchdog/pc87413_wdt.c index 2af1a8b3f973..73fbfc99083b 100644 --- a/drivers/watchdog/pc87413_wdt.c +++ b/drivers/watchdog/pc87413_wdt.c @@ -473,6 +473,7 @@ static const struct file_operations pc87413_fops = { .llseek = no_llseek, .write = pc87413_write, .unlocked_ioctl = pc87413_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = pc87413_open, .release = pc87413_release, }; diff --git a/drivers/watchdog/pcwd.c b/drivers/watchdog/pcwd.c index c3c93e00b320..7a0587fdc52c 100644 --- a/drivers/watchdog/pcwd.c +++ b/drivers/watchdog/pcwd.c @@ -752,6 +752,7 @@ static const struct file_operations pcwd_fops = { .llseek = no_llseek, .write = pcwd_write, .unlocked_ioctl = pcwd_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = pcwd_open, .release = pcwd_close, }; diff --git a/drivers/watchdog/pcwd_pci.c b/drivers/watchdog/pcwd_pci.c index e30c1f762045..81508a42a90c 100644 --- a/drivers/watchdog/pcwd_pci.c +++ b/drivers/watchdog/pcwd_pci.c @@ -646,6 +646,7 @@ static const struct file_operations pcipcwd_fops = { .llseek = no_llseek, .write = pcipcwd_write, .unlocked_ioctl = pcipcwd_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = pcipcwd_open, .release = pcipcwd_release, }; diff --git a/drivers/watchdog/pcwd_usb.c b/drivers/watchdog/pcwd_usb.c index 6727f8ab2d18..2f44af1831d0 100644 --- a/drivers/watchdog/pcwd_usb.c +++ b/drivers/watchdog/pcwd_usb.c @@ -550,6 +550,7 @@ static const struct file_operations usb_pcwd_fops = { .llseek = no_llseek, .write = usb_pcwd_write, .unlocked_ioctl = usb_pcwd_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = usb_pcwd_open, .release = usb_pcwd_release, }; diff --git a/drivers/watchdog/pika_wdt.c b/drivers/watchdog/pika_wdt.c index 205c3c68fca1..a98abd0d3146 100644 --- a/drivers/watchdog/pika_wdt.c +++ b/drivers/watchdog/pika_wdt.c @@ -214,6 +214,7 @@ static const struct file_operations pikawdt_fops = { .release = pikawdt_release, .write = pikawdt_write, .unlocked_ioctl = pikawdt_ioctl, + .compat_ioctl = compat_ptr_ioctl, }; static struct miscdevice pikawdt_miscdev = { diff --git a/drivers/watchdog/pm8916_wdt.c b/drivers/watchdog/pm8916_wdt.c index 2d3652004e39..1213179f863c 100644 --- a/drivers/watchdog/pm8916_wdt.c +++ b/drivers/watchdog/pm8916_wdt.c @@ -163,9 +163,17 @@ static int pm8916_wdt_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq > 0) { - if (devm_request_irq(dev, irq, pm8916_wdt_isr, 0, "pm8916_wdt", - wdt)) - irq = 0; + err = devm_request_irq(dev, irq, pm8916_wdt_isr, 0, + "pm8916_wdt", wdt); + if (err) + return err; + + wdt->wdev.info = &pm8916_wdt_pt_ident; + } else { + if (irq == -EPROBE_DEFER) + return -EPROBE_DEFER; + + wdt->wdev.info = &pm8916_wdt_ident; } /* Configure watchdog to hard-reset mode */ @@ -177,7 +185,6 @@ static int pm8916_wdt_probe(struct platform_device *pdev) return err; } - wdt->wdev.info = (irq > 0) ? &pm8916_wdt_pt_ident : &pm8916_wdt_ident, wdt->wdev.ops = &pm8916_wdt_ops, wdt->wdev.parent = dev; wdt->wdev.min_timeout = PM8916_WDT_MIN_TIMEOUT; diff --git a/drivers/watchdog/pnx4008_wdt.c b/drivers/watchdog/pnx4008_wdt.c index 7b446b696f2b..e0ea133c1690 100644 --- a/drivers/watchdog/pnx4008_wdt.c +++ b/drivers/watchdog/pnx4008_wdt.c @@ -30,7 +30,6 @@ #include <linux/of.h> #include <linux/delay.h> #include <linux/reboot.h> -#include <mach/hardware.h> /* WatchDog Timer - Chapter 23 Page 207 */ diff --git a/drivers/watchdog/pnx833x_wdt.c b/drivers/watchdog/pnx833x_wdt.c index aa53babf2bab..4097d076aab8 100644 --- a/drivers/watchdog/pnx833x_wdt.c +++ b/drivers/watchdog/pnx833x_wdt.c @@ -215,6 +215,7 @@ static const struct file_operations pnx833x_wdt_fops = { .llseek = no_llseek, .write = pnx833x_wdt_write, .unlocked_ioctl = pnx833x_wdt_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = pnx833x_wdt_open, .release = pnx833x_wdt_release, }; diff --git a/drivers/watchdog/qcom-wdt.c b/drivers/watchdog/qcom-wdt.c index 7be7f87be28f..eb47fe5ed280 100644 --- a/drivers/watchdog/qcom-wdt.c +++ b/drivers/watchdog/qcom-wdt.c @@ -1,8 +1,10 @@ // SPDX-License-Identifier: GPL-2.0-only /* Copyright (c) 2014, The Linux Foundation. All rights reserved. */ +#include <linux/bits.h> #include <linux/clk.h> #include <linux/delay.h> +#include <linux/interrupt.h> #include <linux/io.h> #include <linux/kernel.h> #include <linux/module.h> @@ -19,6 +21,9 @@ enum wdt_reg { WDT_BITE_TIME, }; +#define QCOM_WDT_ENABLE BIT(0) +#define QCOM_WDT_ENABLE_IRQ BIT(1) + static const u32 reg_offset_data_apcs_tmr[] = { [WDT_RST] = 0x38, [WDT_EN] = 0x40, @@ -37,7 +42,6 @@ static const u32 reg_offset_data_kpss[] = { struct qcom_wdt { struct watchdog_device wdd; - struct clk *clk; unsigned long rate; void __iomem *base; const u32 *layout; @@ -54,15 +58,35 @@ struct qcom_wdt *to_qcom_wdt(struct watchdog_device *wdd) return container_of(wdd, struct qcom_wdt, wdd); } +static inline int qcom_get_enable(struct watchdog_device *wdd) +{ + int enable = QCOM_WDT_ENABLE; + + if (wdd->pretimeout) + enable |= QCOM_WDT_ENABLE_IRQ; + + return enable; +} + +static irqreturn_t qcom_wdt_isr(int irq, void *arg) +{ + struct watchdog_device *wdd = arg; + + watchdog_notify_pretimeout(wdd); + + return IRQ_HANDLED; +} + static int qcom_wdt_start(struct watchdog_device *wdd) { struct qcom_wdt *wdt = to_qcom_wdt(wdd); + unsigned int bark = wdd->timeout - wdd->pretimeout; writel(0, wdt_addr(wdt, WDT_EN)); writel(1, wdt_addr(wdt, WDT_RST)); - writel(wdd->timeout * wdt->rate, wdt_addr(wdt, WDT_BARK_TIME)); + writel(bark * wdt->rate, wdt_addr(wdt, WDT_BARK_TIME)); writel(wdd->timeout * wdt->rate, wdt_addr(wdt, WDT_BITE_TIME)); - writel(1, wdt_addr(wdt, WDT_EN)); + writel(qcom_get_enable(wdd), wdt_addr(wdt, WDT_EN)); return 0; } @@ -89,6 +113,13 @@ static int qcom_wdt_set_timeout(struct watchdog_device *wdd, return qcom_wdt_start(wdd); } +static int qcom_wdt_set_pretimeout(struct watchdog_device *wdd, + unsigned int timeout) +{ + wdd->pretimeout = timeout; + return qcom_wdt_start(wdd); +} + static int qcom_wdt_restart(struct watchdog_device *wdd, unsigned long action, void *data) { @@ -105,7 +136,7 @@ static int qcom_wdt_restart(struct watchdog_device *wdd, unsigned long action, writel(1, wdt_addr(wdt, WDT_RST)); writel(timeout, wdt_addr(wdt, WDT_BARK_TIME)); writel(timeout, wdt_addr(wdt, WDT_BITE_TIME)); - writel(1, wdt_addr(wdt, WDT_EN)); + writel(QCOM_WDT_ENABLE, wdt_addr(wdt, WDT_EN)); /* * Actually make sure the above sequence hits hardware before sleeping. @@ -121,6 +152,7 @@ static const struct watchdog_ops qcom_wdt_ops = { .stop = qcom_wdt_stop, .ping = qcom_wdt_ping, .set_timeout = qcom_wdt_set_timeout, + .set_pretimeout = qcom_wdt_set_pretimeout, .restart = qcom_wdt_restart, .owner = THIS_MODULE, }; @@ -133,6 +165,15 @@ static const struct watchdog_info qcom_wdt_info = { .identity = KBUILD_MODNAME, }; +static const struct watchdog_info qcom_wdt_pt_info = { + .options = WDIOF_KEEPALIVEPING + | WDIOF_MAGICCLOSE + | WDIOF_SETTIMEOUT + | WDIOF_PRETIMEOUT + | WDIOF_CARDRESET, + .identity = KBUILD_MODNAME, +}; + static void qcom_clk_disable_unprepare(void *data) { clk_disable_unprepare(data); @@ -146,7 +187,8 @@ static int qcom_wdt_probe(struct platform_device *pdev) struct device_node *np = dev->of_node; const u32 *regs; u32 percpu_offset; - int ret; + int irq, ret; + struct clk *clk; regs = of_device_get_match_data(dev); if (!regs) { @@ -173,19 +215,18 @@ static int qcom_wdt_probe(struct platform_device *pdev) if (IS_ERR(wdt->base)) return PTR_ERR(wdt->base); - wdt->clk = devm_clk_get(dev, NULL); - if (IS_ERR(wdt->clk)) { + clk = devm_clk_get(dev, NULL); + if (IS_ERR(clk)) { dev_err(dev, "failed to get input clock\n"); - return PTR_ERR(wdt->clk); + return PTR_ERR(clk); } - ret = clk_prepare_enable(wdt->clk); + ret = clk_prepare_enable(clk); if (ret) { dev_err(dev, "failed to setup clock\n"); return ret; } - ret = devm_add_action_or_reset(dev, qcom_clk_disable_unprepare, - wdt->clk); + ret = devm_add_action_or_reset(dev, qcom_clk_disable_unprepare, clk); if (ret) return ret; @@ -197,14 +238,31 @@ static int qcom_wdt_probe(struct platform_device *pdev) * that it would bite before a second elapses it's usefulness is * limited. Bail if this is the case. */ - wdt->rate = clk_get_rate(wdt->clk); + wdt->rate = clk_get_rate(clk); if (wdt->rate == 0 || wdt->rate > 0x10000000U) { dev_err(dev, "invalid clock rate\n"); return -EINVAL; } - wdt->wdd.info = &qcom_wdt_info; + /* check if there is pretimeout support */ + irq = platform_get_irq_optional(pdev, 0); + if (irq > 0) { + ret = devm_request_irq(dev, irq, qcom_wdt_isr, + IRQF_TRIGGER_RISING, + "wdt_bark", &wdt->wdd); + if (ret) + return ret; + + wdt->wdd.info = &qcom_wdt_pt_info; + wdt->wdd.pretimeout = 1; + } else { + if (irq == -EPROBE_DEFER) + return -EPROBE_DEFER; + + wdt->wdd.info = &qcom_wdt_info; + } + wdt->wdd.ops = &qcom_wdt_ops; wdt->wdd.min_timeout = 1; wdt->wdd.max_timeout = 0x10000000U / wdt->rate; diff --git a/drivers/watchdog/rc32434_wdt.c b/drivers/watchdog/rc32434_wdt.c index a8a4b3a41a90..aee3c2efd565 100644 --- a/drivers/watchdog/rc32434_wdt.c +++ b/drivers/watchdog/rc32434_wdt.c @@ -26,7 +26,7 @@ #include <linux/platform_device.h> /* For platform_driver framework */ #include <linux/spinlock.h> /* For spin_lock/spin_unlock/... */ #include <linux/uaccess.h> /* For copy_to_user/put_user/... */ -#include <linux/io.h> /* For devm_ioremap_nocache */ +#include <linux/io.h> /* For devm_ioremap */ #include <asm/mach-rc32434/integ.h> /* For the Watchdog registers */ @@ -245,6 +245,7 @@ static const struct file_operations rc32434_wdt_fops = { .llseek = no_llseek, .write = rc32434_wdt_write, .unlocked_ioctl = rc32434_wdt_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = rc32434_wdt_open, .release = rc32434_wdt_release, }; @@ -266,7 +267,7 @@ static int rc32434_wdt_probe(struct platform_device *pdev) return -ENODEV; } - wdt_reg = devm_ioremap_nocache(&pdev->dev, r->start, resource_size(r)); + wdt_reg = devm_ioremap(&pdev->dev, r->start, resource_size(r)); if (!wdt_reg) { pr_err("failed to remap I/O resources\n"); return -ENXIO; diff --git a/drivers/watchdog/rdc321x_wdt.c b/drivers/watchdog/rdc321x_wdt.c index 2e608ae6cbc7..57187efeb86f 100644 --- a/drivers/watchdog/rdc321x_wdt.c +++ b/drivers/watchdog/rdc321x_wdt.c @@ -199,6 +199,7 @@ 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, .write = rdc321x_wdt_write, .release = rdc321x_wdt_release, diff --git a/drivers/watchdog/riowd.c b/drivers/watchdog/riowd.c index b35f7be20c00..dc3c06a92f93 100644 --- a/drivers/watchdog/riowd.c +++ b/drivers/watchdog/riowd.c @@ -163,6 +163,7 @@ 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, .write = riowd_write, .release = riowd_release, diff --git a/drivers/watchdog/rn5t618_wdt.c b/drivers/watchdog/rn5t618_wdt.c index 234876047431..6e524c8e26a8 100644 --- a/drivers/watchdog/rn5t618_wdt.c +++ b/drivers/watchdog/rn5t618_wdt.c @@ -188,6 +188,7 @@ static struct platform_driver rn5t618_wdt_driver = { module_platform_driver(rn5t618_wdt_driver); +MODULE_ALIAS("platform:rn5t618-wdt"); MODULE_AUTHOR("Beniamino Galvani <b.galvani@gmail.com>"); MODULE_DESCRIPTION("RN5T618 watchdog driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/watchdog/sa1100_wdt.c b/drivers/watchdog/sa1100_wdt.c index cbd8c957182f..9b93be00109f 100644 --- a/drivers/watchdog/sa1100_wdt.c +++ b/drivers/watchdog/sa1100_wdt.c @@ -141,6 +141,7 @@ static const struct file_operations sa1100dog_fops = { .llseek = no_llseek, .write = sa1100dog_write, .unlocked_ioctl = sa1100dog_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = sa1100dog_open, .release = sa1100dog_release, }; diff --git a/drivers/watchdog/sama5d4_wdt.c b/drivers/watchdog/sama5d4_wdt.c index d193a60430b2..e5d11d6a2600 100644 --- a/drivers/watchdog/sama5d4_wdt.c +++ b/drivers/watchdog/sama5d4_wdt.c @@ -2,7 +2,7 @@ /* * Driver for Atmel SAMA5D4 Watchdog Timer * - * Copyright (C) 2015 Atmel Corporation + * Copyright (C) 2015-2019 Microchip Technology Inc. and its subsidiaries */ #include <linux/delay.h> @@ -11,6 +11,7 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/of.h> +#include <linux/of_device.h> #include <linux/of_irq.h> #include <linux/platform_device.h> #include <linux/reboot.h> @@ -29,7 +30,10 @@ struct sama5d4_wdt { struct watchdog_device wdd; void __iomem *reg_base; u32 mr; + u32 ir; unsigned long last_ping; + bool need_irq; + bool sam9x60_support; }; static int wdt_timeout; @@ -78,7 +82,12 @@ static int sama5d4_wdt_start(struct watchdog_device *wdd) { struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd); - wdt->mr &= ~AT91_WDT_WDDIS; + if (wdt->sam9x60_support) { + writel_relaxed(wdt->ir, wdt->reg_base + AT91_SAM9X60_IER); + wdt->mr &= ~AT91_SAM9X60_WDDIS; + } else { + wdt->mr &= ~AT91_WDT_WDDIS; + } wdt_write(wdt, AT91_WDT_MR, wdt->mr); return 0; @@ -88,7 +97,12 @@ static int sama5d4_wdt_stop(struct watchdog_device *wdd) { struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd); - wdt->mr |= AT91_WDT_WDDIS; + if (wdt->sam9x60_support) { + writel_relaxed(wdt->ir, wdt->reg_base + AT91_SAM9X60_IDR); + wdt->mr |= AT91_SAM9X60_WDDIS; + } else { + wdt->mr |= AT91_WDT_WDDIS; + } wdt_write(wdt, AT91_WDT_MR, wdt->mr); return 0; @@ -109,6 +123,14 @@ static int sama5d4_wdt_set_timeout(struct watchdog_device *wdd, struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd); u32 value = WDT_SEC2TICKS(timeout); + if (wdt->sam9x60_support) { + wdt_write(wdt, AT91_SAM9X60_WLR, + AT91_SAM9X60_SET_COUNTER(value)); + + wdd->timeout = timeout; + return 0; + } + wdt->mr &= ~AT91_WDT_WDV; wdt->mr |= AT91_WDT_SET_WDV(value); @@ -143,8 +165,14 @@ static const struct watchdog_ops sama5d4_wdt_ops = { static irqreturn_t sama5d4_wdt_irq_handler(int irq, void *dev_id) { struct sama5d4_wdt *wdt = platform_get_drvdata(dev_id); + u32 reg; - if (wdt_read(wdt, AT91_WDT_SR)) { + if (wdt->sam9x60_support) + reg = wdt_read(wdt, AT91_SAM9X60_ISR); + else + reg = wdt_read(wdt, AT91_WDT_SR); + + if (reg) { pr_crit("Atmel Watchdog Software Reset\n"); emergency_restart(); pr_crit("Reboot didn't succeed\n"); @@ -157,13 +185,14 @@ static int of_sama5d4_wdt_init(struct device_node *np, struct sama5d4_wdt *wdt) { const char *tmp; - wdt->mr = AT91_WDT_WDDIS; + if (wdt->sam9x60_support) + wdt->mr = AT91_SAM9X60_WDDIS; + else + wdt->mr = AT91_WDT_WDDIS; if (!of_property_read_string(np, "atmel,watchdog-type", &tmp) && !strcmp(tmp, "software")) - wdt->mr |= AT91_WDT_WDFIEN; - else - wdt->mr |= AT91_WDT_WDRSTEN; + wdt->need_irq = true; if (of_property_read_bool(np, "atmel,idle-halt")) wdt->mr |= AT91_WDT_WDIDLEHLT; @@ -176,21 +205,46 @@ static int of_sama5d4_wdt_init(struct device_node *np, struct sama5d4_wdt *wdt) static int sama5d4_wdt_init(struct sama5d4_wdt *wdt) { - u32 reg; + u32 reg, val; + + val = WDT_SEC2TICKS(WDT_DEFAULT_TIMEOUT); /* * When booting and resuming, the bootloader may have changed the * watchdog configuration. * If the watchdog is already running, we can safely update it. * Else, we have to disable it properly. */ - if (wdt_enabled) { - wdt_write_nosleep(wdt, AT91_WDT_MR, wdt->mr); - } else { + if (!wdt_enabled) { reg = wdt_read(wdt, AT91_WDT_MR); - if (!(reg & AT91_WDT_WDDIS)) + if (wdt->sam9x60_support && (!(reg & AT91_SAM9X60_WDDIS))) + wdt_write_nosleep(wdt, AT91_WDT_MR, + reg | AT91_SAM9X60_WDDIS); + else if (!wdt->sam9x60_support && + (!(reg & AT91_WDT_WDDIS))) wdt_write_nosleep(wdt, AT91_WDT_MR, reg | AT91_WDT_WDDIS); } + + if (wdt->sam9x60_support) { + if (wdt->need_irq) + wdt->ir = AT91_SAM9X60_PERINT; + else + wdt->mr |= AT91_SAM9X60_PERIODRST; + + wdt_write(wdt, AT91_SAM9X60_IER, wdt->ir); + wdt_write(wdt, AT91_SAM9X60_WLR, AT91_SAM9X60_SET_COUNTER(val)); + } else { + wdt->mr |= AT91_WDT_SET_WDD(WDT_SEC2TICKS(MAX_WDT_TIMEOUT)); + wdt->mr |= AT91_WDT_SET_WDV(val); + + if (wdt->need_irq) + wdt->mr |= AT91_WDT_WDFIEN; + else + wdt->mr |= AT91_WDT_WDRSTEN; + } + + wdt_write_nosleep(wdt, AT91_WDT_MR, wdt->mr); + return 0; } @@ -201,7 +255,6 @@ static int sama5d4_wdt_probe(struct platform_device *pdev) struct sama5d4_wdt *wdt; void __iomem *regs; u32 irq = 0; - u32 timeout; int ret; wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL); @@ -215,6 +268,8 @@ static int sama5d4_wdt_probe(struct platform_device *pdev) wdd->min_timeout = MIN_WDT_TIMEOUT; wdd->max_timeout = MAX_WDT_TIMEOUT; wdt->last_ping = jiffies; + wdt->sam9x60_support = of_device_is_compatible(dev->of_node, + "microchip,sam9x60-wdt"); watchdog_set_drvdata(wdd, wdt); @@ -224,15 +279,19 @@ static int sama5d4_wdt_probe(struct platform_device *pdev) wdt->reg_base = regs; - irq = irq_of_parse_and_map(dev->of_node, 0); - if (!irq) - dev_warn(dev, "failed to get IRQ from DT\n"); - ret = of_sama5d4_wdt_init(dev->of_node, wdt); if (ret) return ret; - if ((wdt->mr & AT91_WDT_WDFIEN) && irq) { + if (wdt->need_irq) { + irq = irq_of_parse_and_map(dev->of_node, 0); + if (!irq) { + dev_warn(dev, "failed to get IRQ from DT\n"); + wdt->need_irq = false; + } + } + + if (wdt->need_irq) { ret = devm_request_irq(dev, irq, sama5d4_wdt_irq_handler, IRQF_SHARED | IRQF_IRQPOLL | IRQF_NO_SUSPEND, pdev->name, pdev); @@ -244,11 +303,6 @@ static int sama5d4_wdt_probe(struct platform_device *pdev) watchdog_init_timeout(wdd, wdt_timeout, dev); - timeout = WDT_SEC2TICKS(wdd->timeout); - - wdt->mr |= AT91_WDT_SET_WDD(WDT_SEC2TICKS(MAX_WDT_TIMEOUT)); - wdt->mr |= AT91_WDT_SET_WDV(timeout); - ret = sama5d4_wdt_init(wdt); if (ret) return ret; @@ -269,7 +323,12 @@ static int sama5d4_wdt_probe(struct platform_device *pdev) } static const struct of_device_id sama5d4_wdt_of_match[] = { - { .compatible = "atmel,sama5d4-wdt", }, + { + .compatible = "atmel,sama5d4-wdt", + }, + { + .compatible = "microchip,sam9x60-wdt", + }, { } }; MODULE_DEVICE_TABLE(of, sama5d4_wdt_of_match); diff --git a/drivers/watchdog/sb_wdog.c b/drivers/watchdog/sb_wdog.c index 202fc8d8ca5f..da2dad00d473 100644 --- a/drivers/watchdog/sb_wdog.c +++ b/drivers/watchdog/sb_wdog.c @@ -237,6 +237,7 @@ static const struct file_operations sbwdog_fops = { .llseek = no_llseek, .write = sbwdog_write, .unlocked_ioctl = sbwdog_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = sbwdog_open, .release = sbwdog_release, }; diff --git a/drivers/watchdog/sbc60xxwdt.c b/drivers/watchdog/sbc60xxwdt.c index c3151642694c..f2cbe6d880a8 100644 --- a/drivers/watchdog/sbc60xxwdt.c +++ b/drivers/watchdog/sbc60xxwdt.c @@ -280,6 +280,7 @@ static const struct file_operations wdt_fops = { .open = fop_open, .release = fop_close, .unlocked_ioctl = fop_ioctl, + .compat_ioctl = compat_ptr_ioctl, }; static struct miscdevice wdt_miscdev = { diff --git a/drivers/watchdog/sbc7240_wdt.c b/drivers/watchdog/sbc7240_wdt.c index 12cdee7d5069..520b8dd77ed4 100644 --- a/drivers/watchdog/sbc7240_wdt.c +++ b/drivers/watchdog/sbc7240_wdt.c @@ -194,9 +194,8 @@ static long fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg) if (wdt_set_timeout(new_timeout)) return -EINVAL; - - /* Fall through */ } + /* Fall through */ case WDIOC_GETTIMEOUT: return put_user(timeout, (int __user *)arg); default: @@ -211,6 +210,7 @@ static const struct file_operations wdt_fops = { .open = fop_open, .release = fop_close, .unlocked_ioctl = fop_ioctl, + .compat_ioctl = compat_ptr_ioctl, }; static struct miscdevice wdt_miscdev = { diff --git a/drivers/watchdog/sbc_epx_c3.c b/drivers/watchdog/sbc_epx_c3.c index 86828c28843f..5e3a9ddb952e 100644 --- a/drivers/watchdog/sbc_epx_c3.c +++ b/drivers/watchdog/sbc_epx_c3.c @@ -156,6 +156,7 @@ static const struct file_operations epx_c3_fops = { .llseek = no_llseek, .write = epx_c3_write, .unlocked_ioctl = epx_c3_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = epx_c3_open, .release = epx_c3_release, }; diff --git a/drivers/watchdog/sbc_fitpc2_wdt.c b/drivers/watchdog/sbc_fitpc2_wdt.c index 3822a60a8d2b..1b20b33879c4 100644 --- a/drivers/watchdog/sbc_fitpc2_wdt.c +++ b/drivers/watchdog/sbc_fitpc2_wdt.c @@ -186,6 +186,7 @@ static const struct file_operations fitpc2_wdt_fops = { .llseek = no_llseek, .write = fitpc2_wdt_write, .unlocked_ioctl = fitpc2_wdt_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = fitpc2_wdt_open, .release = fitpc2_wdt_release, }; diff --git a/drivers/watchdog/sc1200wdt.c b/drivers/watchdog/sc1200wdt.c index 960385a766b3..9673eb12dacd 100644 --- a/drivers/watchdog/sc1200wdt.c +++ b/drivers/watchdog/sc1200wdt.c @@ -307,6 +307,7 @@ static const struct file_operations sc1200wdt_fops = { .llseek = no_llseek, .write = sc1200wdt_write, .unlocked_ioctl = sc1200wdt_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = sc1200wdt_open, .release = sc1200wdt_release, }; diff --git a/drivers/watchdog/sc520_wdt.c b/drivers/watchdog/sc520_wdt.c index a612128c5f80..fbe79bcc9297 100644 --- a/drivers/watchdog/sc520_wdt.c +++ b/drivers/watchdog/sc520_wdt.c @@ -336,6 +336,7 @@ static const struct file_operations wdt_fops = { .open = fop_open, .release = fop_close, .unlocked_ioctl = fop_ioctl, + .compat_ioctl = compat_ptr_ioctl, }; static struct miscdevice wdt_miscdev = { diff --git a/drivers/watchdog/sch311x_wdt.c b/drivers/watchdog/sch311x_wdt.c index 3612f1df381b..83949a385f62 100644 --- a/drivers/watchdog/sch311x_wdt.c +++ b/drivers/watchdog/sch311x_wdt.c @@ -337,6 +337,7 @@ static const struct file_operations sch311x_wdt_fops = { .llseek = no_llseek, .write = sch311x_wdt_write, .unlocked_ioctl = sch311x_wdt_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = sch311x_wdt_open, .release = sch311x_wdt_close, }; diff --git a/drivers/watchdog/scx200_wdt.c b/drivers/watchdog/scx200_wdt.c index 46268309ee9b..c94098acb78f 100644 --- a/drivers/watchdog/scx200_wdt.c +++ b/drivers/watchdog/scx200_wdt.c @@ -201,6 +201,7 @@ static const struct file_operations scx200_wdt_fops = { .llseek = no_llseek, .write = scx200_wdt_write, .unlocked_ioctl = scx200_wdt_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = scx200_wdt_open, .release = scx200_wdt_release, }; diff --git a/drivers/watchdog/smsc37b787_wdt.c b/drivers/watchdog/smsc37b787_wdt.c index f5713030d0f7..43de56acd767 100644 --- a/drivers/watchdog/smsc37b787_wdt.c +++ b/drivers/watchdog/smsc37b787_wdt.c @@ -505,6 +505,7 @@ static const struct file_operations wb_smsc_wdt_fops = { .llseek = no_llseek, .write = wb_smsc_wdt_write, .unlocked_ioctl = wb_smsc_wdt_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = wb_smsc_wdt_open, .release = wb_smsc_wdt_release, }; diff --git a/drivers/watchdog/sprd_wdt.c b/drivers/watchdog/sprd_wdt.c index edba4e278685..65cb55f3916f 100644 --- a/drivers/watchdog/sprd_wdt.c +++ b/drivers/watchdog/sprd_wdt.c @@ -284,10 +284,8 @@ static int sprd_wdt_probe(struct platform_device *pdev) } wdt->irq = platform_get_irq(pdev, 0); - if (wdt->irq < 0) { - dev_err(dev, "failed to get IRQ resource\n"); + if (wdt->irq < 0) return wdt->irq; - } ret = devm_request_irq(dev, wdt->irq, sprd_wdt_isr, IRQF_NO_SUSPEND, "sprd-wdt", (void *)wdt); @@ -329,10 +327,9 @@ static int sprd_wdt_probe(struct platform_device *pdev) static int __maybe_unused sprd_wdt_pm_suspend(struct device *dev) { - struct watchdog_device *wdd = dev_get_drvdata(dev); struct sprd_wdt *wdt = dev_get_drvdata(dev); - if (watchdog_active(wdd)) + if (watchdog_active(&wdt->wdd)) sprd_wdt_stop(&wdt->wdd); sprd_wdt_disable(wdt); @@ -341,7 +338,6 @@ static int __maybe_unused sprd_wdt_pm_suspend(struct device *dev) static int __maybe_unused sprd_wdt_pm_resume(struct device *dev) { - struct watchdog_device *wdd = dev_get_drvdata(dev); struct sprd_wdt *wdt = dev_get_drvdata(dev); int ret; @@ -349,7 +345,7 @@ static int __maybe_unused sprd_wdt_pm_resume(struct device *dev) if (ret) return ret; - if (watchdog_active(wdd)) { + if (watchdog_active(&wdt->wdd)) { ret = sprd_wdt_start(&wdt->wdd); if (ret) { sprd_wdt_disable(wdt); diff --git a/drivers/watchdog/stm32_iwdg.c b/drivers/watchdog/stm32_iwdg.c index a3a329011a06..25188d6bbe15 100644 --- a/drivers/watchdog/stm32_iwdg.c +++ b/drivers/watchdog/stm32_iwdg.c @@ -262,6 +262,24 @@ static int stm32_iwdg_probe(struct platform_device *pdev) watchdog_set_nowayout(wdd, WATCHDOG_NOWAYOUT); watchdog_init_timeout(wdd, 0, dev); + /* + * In case of CONFIG_WATCHDOG_HANDLE_BOOT_ENABLED is set + * (Means U-Boot/bootloaders leaves the watchdog running) + * When we get here we should make a decision to prevent + * any side effects before user space daemon will take care of it. + * The best option, taking into consideration that there is no + * way to read values back from hardware, is to enforce watchdog + * being run with deterministic values. + */ + if (IS_ENABLED(CONFIG_WATCHDOG_HANDLE_BOOT_ENABLED)) { + ret = stm32_iwdg_start(wdd); + if (ret) + return ret; + + /* Make sure the watchdog is serviced */ + set_bit(WDOG_HW_RUNNING, &wdd->status); + } + ret = devm_watchdog_register_device(dev, wdd); if (ret) return ret; diff --git a/drivers/watchdog/w83627hf_wdt.c b/drivers/watchdog/w83627hf_wdt.c index 38b31e9947aa..56a4a4030ca9 100644 --- a/drivers/watchdog/w83627hf_wdt.c +++ b/drivers/watchdog/w83627hf_wdt.c @@ -49,7 +49,7 @@ static int wdt_cfg_leave = 0xAA;/* key to lock configuration space */ enum chips { w83627hf, w83627s, w83697hf, w83697ug, w83637hf, w83627thf, w83687thf, w83627ehf, w83627dhg, w83627uhg, w83667hg, w83627dhg_p, w83667hg_b, nct6775, nct6776, nct6779, nct6791, nct6792, nct6793, - nct6795, nct6796, nct6102 }; + nct6795, nct6796, nct6102, nct6116 }; static int timeout; /* in seconds */ module_param(timeout, int, 0); @@ -94,6 +94,7 @@ MODULE_PARM_DESC(early_disable, "Disable watchdog at boot time (default=0)"); #define NCT6775_ID 0xb4 #define NCT6776_ID 0xc3 #define NCT6102_ID 0xc4 +#define NCT6116_ID 0xd2 #define NCT6779_ID 0xc5 #define NCT6791_ID 0xc8 #define NCT6792_ID 0xc9 @@ -211,6 +212,7 @@ static int w83627hf_init(struct watchdog_device *wdog, enum chips chip) case nct6795: case nct6796: case nct6102: + case nct6116: /* * These chips have a fixed WDTO# output pin (W83627UHG), * or support more than one WDTO# output pin. @@ -417,6 +419,12 @@ static int wdt_find(int addr) cr_wdt_control = NCT6102D_WDT_CONTROL; cr_wdt_csr = NCT6102D_WDT_CSR; break; + case NCT6116_ID: + ret = nct6116; + cr_wdt_timeout = NCT6102D_WDT_TIMEOUT; + cr_wdt_control = NCT6102D_WDT_CONTROL; + cr_wdt_csr = NCT6102D_WDT_CSR; + break; case 0xff: ret = -ENODEV; break; @@ -482,6 +490,7 @@ static int __init wdt_init(void) "NCT6795", "NCT6796", "NCT6102", + "NCT6116", }; /* Apply system-specific quirks */ diff --git a/drivers/watchdog/w83877f_wdt.c b/drivers/watchdog/w83877f_wdt.c index 6eb5185d6ea6..6b3b667e6f23 100644 --- a/drivers/watchdog/w83877f_wdt.c +++ b/drivers/watchdog/w83877f_wdt.c @@ -304,6 +304,7 @@ static const struct file_operations wdt_fops = { .open = fop_open, .release = fop_close, .unlocked_ioctl = fop_ioctl, + .compat_ioctl = compat_ptr_ioctl, }; static struct miscdevice wdt_miscdev = { diff --git a/drivers/watchdog/w83977f_wdt.c b/drivers/watchdog/w83977f_wdt.c index 16e9cbe72acc..5212e68c6b01 100644 --- a/drivers/watchdog/w83977f_wdt.c +++ b/drivers/watchdog/w83977f_wdt.c @@ -446,6 +446,7 @@ static const struct file_operations wdt_fops = { .llseek = no_llseek, .write = wdt_write, .unlocked_ioctl = wdt_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = wdt_open, .release = wdt_release, }; diff --git a/drivers/watchdog/wafer5823wdt.c b/drivers/watchdog/wafer5823wdt.c index 6d2071a0590d..a6925847f76f 100644 --- a/drivers/watchdog/wafer5823wdt.c +++ b/drivers/watchdog/wafer5823wdt.c @@ -230,6 +230,7 @@ static const struct file_operations wafwdt_fops = { .llseek = no_llseek, .write = wafwdt_write, .unlocked_ioctl = wafwdt_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = wafwdt_open, .release = wafwdt_close, }; diff --git a/drivers/watchdog/watchdog_core.c b/drivers/watchdog/watchdog_core.c index 21e8085b848b..861daf4f37b2 100644 --- a/drivers/watchdog/watchdog_core.c +++ b/drivers/watchdog/watchdog_core.c @@ -147,6 +147,25 @@ int watchdog_init_timeout(struct watchdog_device *wdd, } EXPORT_SYMBOL_GPL(watchdog_init_timeout); +static int watchdog_reboot_notifier(struct notifier_block *nb, + unsigned long code, void *data) +{ + struct watchdog_device *wdd; + + wdd = container_of(nb, struct watchdog_device, reboot_nb); + if (code == SYS_DOWN || code == SYS_HALT) { + if (watchdog_active(wdd)) { + int ret; + + ret = wdd->ops->stop(wdd); + if (ret) + return NOTIFY_BAD; + } + } + + return NOTIFY_DONE; +} + static int watchdog_restart_notifier(struct notifier_block *nb, unsigned long action, void *data) { @@ -235,6 +254,19 @@ static int __watchdog_register_device(struct watchdog_device *wdd) } } + if (test_bit(WDOG_STOP_ON_REBOOT, &wdd->status)) { + wdd->reboot_nb.notifier_call = watchdog_reboot_notifier; + + ret = register_reboot_notifier(&wdd->reboot_nb); + if (ret) { + pr_err("watchdog%d: Cannot register reboot notifier (%d)\n", + wdd->id, ret); + watchdog_dev_unregister(wdd); + ida_simple_remove(&watchdog_ida, id); + return ret; + } + } + if (wdd->ops->restart) { wdd->restart_nb.notifier_call = watchdog_restart_notifier; @@ -289,6 +321,9 @@ static void __watchdog_unregister_device(struct watchdog_device *wdd) if (wdd->ops->restart) unregister_restart_handler(&wdd->restart_nb); + if (test_bit(WDOG_STOP_ON_REBOOT, &wdd->status)) + unregister_reboot_notifier(&wdd->reboot_nb); + watchdog_dev_unregister(wdd); ida_simple_remove(&watchdog_ida, wdd->id); } diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c index dbd2ad4c9294..8b5c742f24e8 100644 --- a/drivers/watchdog/watchdog_dev.c +++ b/drivers/watchdog/watchdog_dev.c @@ -34,12 +34,10 @@ #include <linux/init.h> /* For __init/__exit/... */ #include <linux/hrtimer.h> /* For hrtimers */ #include <linux/kernel.h> /* For printk/panic/... */ -#include <linux/kref.h> /* For data references */ #include <linux/kthread.h> /* For kthread_work */ #include <linux/miscdevice.h> /* For handling misc devices */ #include <linux/module.h> /* For module stuff/... */ #include <linux/mutex.h> /* For mutexes */ -#include <linux/reboot.h> /* For reboot notifier */ #include <linux/slab.h> /* For memory functions */ #include <linux/types.h> /* For standard types (like size_t) */ #include <linux/watchdog.h> /* For watchdog specific items */ @@ -52,14 +50,14 @@ /* * struct watchdog_core_data - watchdog core internal data - * @kref: Reference count. + * @dev: The watchdog's internal device * @cdev: The watchdog's Character device. * @wdd: Pointer to watchdog device. * @lock: Lock for watchdog core. * @status: Watchdog core internal status bits. */ struct watchdog_core_data { - struct kref kref; + struct device dev; struct cdev cdev; struct watchdog_device *wdd; struct mutex lock; @@ -158,7 +156,8 @@ static inline void watchdog_update_worker(struct watchdog_device *wdd) ktime_t t = watchdog_next_keepalive(wdd); if (t > 0) - hrtimer_start(&wd_data->timer, t, HRTIMER_MODE_REL); + hrtimer_start(&wd_data->timer, t, + HRTIMER_MODE_REL_HARD); } else { hrtimer_cancel(&wd_data->timer); } @@ -177,7 +176,7 @@ static int __watchdog_ping(struct watchdog_device *wdd) if (ktime_after(earliest_keepalive, now)) { hrtimer_start(&wd_data->timer, ktime_sub(earliest_keepalive, now), - HRTIMER_MODE_REL); + HRTIMER_MODE_REL_HARD); return 0; } @@ -452,7 +451,26 @@ static ssize_t nowayout_show(struct device *dev, struct device_attribute *attr, return sprintf(buf, "%d\n", !!test_bit(WDOG_NO_WAY_OUT, &wdd->status)); } -static DEVICE_ATTR_RO(nowayout); + +static ssize_t nowayout_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t len) +{ + struct watchdog_device *wdd = dev_get_drvdata(dev); + unsigned int value; + int ret; + + ret = kstrtouint(buf, 0, &value); + if (ret) + return ret; + if (value > 1) + return -EINVAL; + /* nowayout cannot be disabled once set */ + if (test_bit(WDOG_NO_WAY_OUT, &wdd->status) && !value) + return -EPERM; + watchdog_set_nowayout(wdd, value); + return len; +} +static DEVICE_ATTR_RW(nowayout); static ssize_t status_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -839,7 +857,7 @@ static int watchdog_open(struct inode *inode, struct file *file) file->private_data = wd_data; if (!hw_running) - kref_get(&wd_data->kref); + get_device(&wd_data->dev); /* * open_timeout only applies for the first open from @@ -860,11 +878,11 @@ out_clear: return err; } -static void watchdog_core_data_release(struct kref *kref) +static void watchdog_core_data_release(struct device *dev) { struct watchdog_core_data *wd_data; - wd_data = container_of(kref, struct watchdog_core_data, kref); + wd_data = container_of(dev, struct watchdog_core_data, dev); kfree(wd_data); } @@ -924,7 +942,7 @@ done: */ if (!running) { module_put(wd_data->cdev.owner); - kref_put(&wd_data->kref, watchdog_core_data_release); + put_device(&wd_data->dev); } return 0; } @@ -933,6 +951,7 @@ static const struct file_operations watchdog_fops = { .owner = THIS_MODULE, .write = watchdog_write, .unlocked_ioctl = watchdog_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = watchdog_open, .release = watchdog_release, }; @@ -943,17 +962,22 @@ static struct miscdevice watchdog_miscdev = { .fops = &watchdog_fops, }; +static struct class watchdog_class = { + .name = "watchdog", + .owner = THIS_MODULE, + .dev_groups = wdt_groups, +}; + /* * watchdog_cdev_register: register watchdog character device * @wdd: watchdog device - * @devno: character device number * * Register a watchdog character device including handling the legacy * /dev/watchdog node. /dev/watchdog is actually a miscdevice and * thus we set it up like that. */ -static int watchdog_cdev_register(struct watchdog_device *wdd, dev_t devno) +static int watchdog_cdev_register(struct watchdog_device *wdd) { struct watchdog_core_data *wd_data; int err; @@ -961,7 +985,6 @@ static int watchdog_cdev_register(struct watchdog_device *wdd, dev_t devno) wd_data = kzalloc(sizeof(struct watchdog_core_data), GFP_KERNEL); if (!wd_data) return -ENOMEM; - kref_init(&wd_data->kref); mutex_init(&wd_data->lock); wd_data->wdd = wdd; @@ -971,7 +994,7 @@ static int watchdog_cdev_register(struct watchdog_device *wdd, dev_t devno) return -ENODEV; kthread_init_work(&wd_data->work, watchdog_ping_work); - hrtimer_init(&wd_data->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + hrtimer_init(&wd_data->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_HARD); wd_data->timer.function = watchdog_timer_expired; if (wdd->id == 0) { @@ -990,23 +1013,33 @@ static int watchdog_cdev_register(struct watchdog_device *wdd, dev_t devno) } } + device_initialize(&wd_data->dev); + wd_data->dev.devt = MKDEV(MAJOR(watchdog_devt), wdd->id); + wd_data->dev.class = &watchdog_class; + wd_data->dev.parent = wdd->parent; + wd_data->dev.groups = wdd->groups; + wd_data->dev.release = watchdog_core_data_release; + dev_set_drvdata(&wd_data->dev, wdd); + dev_set_name(&wd_data->dev, "watchdog%d", wdd->id); + /* Fill in the data structures */ cdev_init(&wd_data->cdev, &watchdog_fops); - wd_data->cdev.owner = wdd->ops->owner; /* Add the device */ - err = cdev_add(&wd_data->cdev, devno, 1); + err = cdev_device_add(&wd_data->cdev, &wd_data->dev); if (err) { pr_err("watchdog%d unable to add device %d:%d\n", wdd->id, MAJOR(watchdog_devt), wdd->id); if (wdd->id == 0) { misc_deregister(&watchdog_miscdev); old_wd_data = NULL; - kref_put(&wd_data->kref, watchdog_core_data_release); + put_device(&wd_data->dev); } return err; } + wd_data->cdev.owner = wdd->ops->owner; + /* Record time of most recent heartbeat as 'just before now'. */ wd_data->last_hw_keepalive = ktime_sub(ktime_get(), 1); watchdog_set_open_deadline(wd_data); @@ -1017,9 +1050,10 @@ static int watchdog_cdev_register(struct watchdog_device *wdd, dev_t devno) */ if (watchdog_hw_running(wdd)) { __module_get(wdd->ops->owner); - kref_get(&wd_data->kref); + get_device(&wd_data->dev); if (handle_boot_enabled) - hrtimer_start(&wd_data->timer, 0, HRTIMER_MODE_REL); + hrtimer_start(&wd_data->timer, 0, + HRTIMER_MODE_REL_HARD); else pr_info("watchdog%d running and kernel based pre-userspace handler disabled\n", wdd->id); @@ -1040,7 +1074,7 @@ static void watchdog_cdev_unregister(struct watchdog_device *wdd) { struct watchdog_core_data *wd_data = wdd->wd_data; - cdev_del(&wd_data->cdev); + cdev_device_del(&wd_data->cdev, &wd_data->dev); if (wdd->id == 0) { misc_deregister(&watchdog_miscdev); old_wd_data = NULL; @@ -1059,32 +1093,7 @@ static void watchdog_cdev_unregister(struct watchdog_device *wdd) hrtimer_cancel(&wd_data->timer); kthread_cancel_work_sync(&wd_data->work); - kref_put(&wd_data->kref, watchdog_core_data_release); -} - -static struct class watchdog_class = { - .name = "watchdog", - .owner = THIS_MODULE, - .dev_groups = wdt_groups, -}; - -static int watchdog_reboot_notifier(struct notifier_block *nb, - unsigned long code, void *data) -{ - struct watchdog_device *wdd; - - wdd = container_of(nb, struct watchdog_device, reboot_nb); - if (code == SYS_DOWN || code == SYS_HALT) { - if (watchdog_active(wdd)) { - int ret; - - ret = wdd->ops->stop(wdd); - if (ret) - return NOTIFY_BAD; - } - } - - return NOTIFY_DONE; + put_device(&wd_data->dev); } /* @@ -1098,41 +1107,15 @@ static int watchdog_reboot_notifier(struct notifier_block *nb, int watchdog_dev_register(struct watchdog_device *wdd) { - struct device *dev; - dev_t devno; int ret; - devno = MKDEV(MAJOR(watchdog_devt), wdd->id); - - ret = watchdog_cdev_register(wdd, devno); + ret = watchdog_cdev_register(wdd); if (ret) return ret; - dev = device_create_with_groups(&watchdog_class, wdd->parent, - devno, wdd, wdd->groups, - "watchdog%d", wdd->id); - if (IS_ERR(dev)) { - watchdog_cdev_unregister(wdd); - return PTR_ERR(dev); - } - ret = watchdog_register_pretimeout(wdd); - if (ret) { - device_destroy(&watchdog_class, devno); + if (ret) watchdog_cdev_unregister(wdd); - return ret; - } - - if (test_bit(WDOG_STOP_ON_REBOOT, &wdd->status)) { - wdd->reboot_nb.notifier_call = watchdog_reboot_notifier; - - ret = devm_register_reboot_notifier(dev, &wdd->reboot_nb); - if (ret) { - pr_err("watchdog%d: Cannot register reboot notifier (%d)\n", - wdd->id, ret); - watchdog_dev_unregister(wdd); - } - } return ret; } @@ -1148,7 +1131,6 @@ int watchdog_dev_register(struct watchdog_device *wdd) void watchdog_dev_unregister(struct watchdog_device *wdd) { watchdog_unregister_pretimeout(wdd); - device_destroy(&watchdog_class, wdd->wd_data->cdev.dev); watchdog_cdev_unregister(wdd); } diff --git a/drivers/watchdog/wdat_wdt.c b/drivers/watchdog/wdat_wdt.c index e7cf41aa26c3..b069349b52f5 100644 --- a/drivers/watchdog/wdat_wdt.c +++ b/drivers/watchdog/wdat_wdt.c @@ -202,7 +202,7 @@ static int wdat_wdt_enable_reboot(struct wdat_wdt *wdat) * WDAT specification says that the watchdog is required to reboot * the system when it fires. However, it also states that it is * recommeded to make it configurable through hardware register. We - * enable reboot now if it is configrable, just in case. + * enable reboot now if it is configurable, just in case. */ ret = wdat_wdt_run_action(wdat, ACPI_WDAT_SET_REBOOT, 0, NULL); if (ret && ret != -EOPNOTSUPP) { diff --git a/drivers/watchdog/wdrtas.c b/drivers/watchdog/wdrtas.c index 6ad7edb4a712..184a06a74f83 100644 --- a/drivers/watchdog/wdrtas.c +++ b/drivers/watchdog/wdrtas.c @@ -472,6 +472,7 @@ static const struct file_operations wdrtas_fops = { .llseek = no_llseek, .write = wdrtas_write, .unlocked_ioctl = wdrtas_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = wdrtas_open, .release = wdrtas_close, }; diff --git a/drivers/watchdog/wdt.c b/drivers/watchdog/wdt.c index 7d278b37e083..f9054cb0f8e2 100644 --- a/drivers/watchdog/wdt.c +++ b/drivers/watchdog/wdt.c @@ -523,6 +523,7 @@ static const struct file_operations wdt_fops = { .llseek = no_llseek, .write = wdt_write, .unlocked_ioctl = wdt_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = wdt_open, .release = wdt_release, }; diff --git a/drivers/watchdog/wdt285.c b/drivers/watchdog/wdt285.c index 4eacfb1ce1ac..e60993d0767e 100644 --- a/drivers/watchdog/wdt285.c +++ b/drivers/watchdog/wdt285.c @@ -168,7 +168,7 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd, soft_margin = new_margin; reload = soft_margin * (mem_fclk_21285 / 256); watchdog_ping(); - /* Fall */ + /* Fall through */ case WDIOC_GETTIMEOUT: ret = put_user(soft_margin, int_arg); break; @@ -181,6 +181,7 @@ static const struct file_operations watchdog_fops = { .llseek = no_llseek, .write = watchdog_write, .unlocked_ioctl = watchdog_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = watchdog_open, .release = watchdog_release, }; diff --git a/drivers/watchdog/wdt977.c b/drivers/watchdog/wdt977.c index 5c52c73e1839..066a4fb4d75b 100644 --- a/drivers/watchdog/wdt977.c +++ b/drivers/watchdog/wdt977.c @@ -422,6 +422,7 @@ static const struct file_operations wdt977_fops = { .llseek = no_llseek, .write = wdt977_write, .unlocked_ioctl = wdt977_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = wdt977_open, .release = wdt977_release, }; diff --git a/drivers/watchdog/wdt_pci.c b/drivers/watchdog/wdt_pci.c index 66303ab95685..e528024faa41 100644 --- a/drivers/watchdog/wdt_pci.c +++ b/drivers/watchdog/wdt_pci.c @@ -566,6 +566,7 @@ static const struct file_operations wdtpci_fops = { .llseek = no_llseek, .write = wdtpci_write, .unlocked_ioctl = wdtpci_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = wdtpci_open, .release = wdtpci_release, }; diff --git a/drivers/watchdog/ziirave_wdt.c b/drivers/watchdog/ziirave_wdt.c index dec660c509b3..4a363a8b2d20 100644 --- a/drivers/watchdog/ziirave_wdt.c +++ b/drivers/watchdog/ziirave_wdt.c @@ -21,8 +21,11 @@ #include <linux/version.h> #include <linux/watchdog.h> +#include <asm/unaligned.h> + #define ZIIRAVE_TIMEOUT_MIN 3 #define ZIIRAVE_TIMEOUT_MAX 255 +#define ZIIRAVE_TIMEOUT_DEFAULT 30 #define ZIIRAVE_PING_VALUE 0x0 @@ -48,16 +51,12 @@ static char *ziirave_reasons[] = {"power cycle", "hw watchdog", NULL, NULL, #define ZIIRAVE_FIRM_PKT_TOTAL_SIZE 20 #define ZIIRAVE_FIRM_PKT_DATA_SIZE 16 -#define ZIIRAVE_FIRM_FLASH_MEMORY_START 0x1600 -#define ZIIRAVE_FIRM_FLASH_MEMORY_END 0x2bbf +#define ZIIRAVE_FIRM_FLASH_MEMORY_START (2 * 0x1600) +#define ZIIRAVE_FIRM_FLASH_MEMORY_END (2 * 0x2bbf) +#define ZIIRAVE_FIRM_PAGE_SIZE 128 /* Received and ready for next Download packet. */ #define ZIIRAVE_FIRM_DOWNLOAD_ACK 1 -/* Currently writing to flash. Retry Download status in a moment! */ -#define ZIIRAVE_FIRM_DOWNLOAD_BUSY 2 - -/* Wait for ACK timeout in ms */ -#define ZIIRAVE_FIRM_WAIT_FOR_ACK_TIMEOUT 50 /* Firmware commands */ #define ZIIRAVE_CMD_DOWNLOAD_START 0x10 @@ -68,6 +67,12 @@ static char *ziirave_reasons[] = {"power cycle", "hw watchdog", NULL, NULL, #define ZIIRAVE_CMD_JUMP_TO_BOOTLOADER 0x0c #define ZIIRAVE_CMD_DOWNLOAD_PACKET 0x0e +#define ZIIRAVE_CMD_JUMP_TO_BOOTLOADER_MAGIC 1 +#define ZIIRAVE_CMD_RESET_PROCESSOR_MAGIC 1 + +#define ZIIRAVE_FW_VERSION_FMT "02.%02u.%02u" +#define ZIIRAVE_BL_VERSION_FMT "01.%02u.%02u" + struct ziirave_wdt_rev { unsigned char major; unsigned char minor; @@ -165,67 +170,37 @@ static unsigned int ziirave_wdt_get_timeleft(struct watchdog_device *wdd) return ret; } -static int ziirave_firm_wait_for_ack(struct watchdog_device *wdd) +static int ziirave_firm_read_ack(struct watchdog_device *wdd) { struct i2c_client *client = to_i2c_client(wdd->parent); int ret; - unsigned long timeout; - timeout = jiffies + msecs_to_jiffies(ZIIRAVE_FIRM_WAIT_FOR_ACK_TIMEOUT); - do { - if (time_after(jiffies, timeout)) - return -ETIMEDOUT; - - usleep_range(5000, 10000); - - ret = i2c_smbus_read_byte(client); - if (ret < 0) { - dev_err(&client->dev, "Failed to read byte\n"); - return ret; - } - } while (ret == ZIIRAVE_FIRM_DOWNLOAD_BUSY); + ret = i2c_smbus_read_byte(client); + if (ret < 0) { + dev_err(&client->dev, "Failed to read status byte\n"); + return ret; + } return ret == ZIIRAVE_FIRM_DOWNLOAD_ACK ? 0 : -EIO; } -static int ziirave_firm_set_read_addr(struct watchdog_device *wdd, u16 addr) +static int ziirave_firm_set_read_addr(struct watchdog_device *wdd, u32 addr) { struct i2c_client *client = to_i2c_client(wdd->parent); + const u16 addr16 = (u16)addr / 2; u8 address[2]; - address[0] = addr & 0xff; - address[1] = (addr >> 8) & 0xff; + put_unaligned_le16(addr16, address); return i2c_smbus_write_block_data(client, ZIIRAVE_CMD_DOWNLOAD_SET_READ_ADDR, - ARRAY_SIZE(address), address); -} - -static int ziirave_firm_write_block_data(struct watchdog_device *wdd, - u8 command, u8 length, const u8 *data, - bool wait_for_ack) -{ - struct i2c_client *client = to_i2c_client(wdd->parent); - int ret; - - ret = i2c_smbus_write_block_data(client, command, length, data); - if (ret) { - dev_err(&client->dev, - "Failed to send command 0x%02x: %d\n", command, ret); - return ret; - } - - if (wait_for_ack) - ret = ziirave_firm_wait_for_ack(wdd); - - return ret; + sizeof(address), address); } -static int ziirave_firm_write_byte(struct watchdog_device *wdd, u8 command, - u8 byte, bool wait_for_ack) +static bool ziirave_firm_addr_readonly(u32 addr) { - return ziirave_firm_write_block_data(wdd, command, 1, &byte, - wait_for_ack); + return addr < ZIIRAVE_FIRM_FLASH_MEMORY_START || + addr > ZIIRAVE_FIRM_FLASH_MEMORY_END; } /* @@ -240,35 +215,53 @@ static int ziirave_firm_write_byte(struct watchdog_device *wdd, u8 command, * Data0 .. Data15: Array of 16 bytes of data. * Checksum: Checksum byte to verify data integrity. */ -static int ziirave_firm_write_pkt(struct watchdog_device *wdd, - const struct ihex_binrec *rec) +static int __ziirave_firm_write_pkt(struct watchdog_device *wdd, + u32 addr, const u8 *data, u8 len) { + const u16 addr16 = (u16)addr / 2; struct i2c_client *client = to_i2c_client(wdd->parent); u8 i, checksum = 0, packet[ZIIRAVE_FIRM_PKT_TOTAL_SIZE]; int ret; - u16 addr; - memset(packet, 0, ARRAY_SIZE(packet)); + /* Check max data size */ + if (len > ZIIRAVE_FIRM_PKT_DATA_SIZE) { + dev_err(&client->dev, "Firmware packet too long (%d)\n", + len); + return -EMSGSIZE; + } + + /* + * Ignore packets that are targeting program memory outisde of + * app partition, since they will be ignored by the + * bootloader. At the same time, we need to make sure we'll + * allow zero length packet that will be sent as the last step + * of firmware update + */ + if (len && ziirave_firm_addr_readonly(addr)) + return 0; /* Packet length */ - packet[0] = (u8)be16_to_cpu(rec->len); + packet[0] = len; /* Packet address */ - addr = (be32_to_cpu(rec->addr) & 0xffff) >> 1; - packet[1] = addr & 0xff; - packet[2] = (addr & 0xff00) >> 8; + put_unaligned_le16(addr16, packet + 1); - /* Packet data */ - if (be16_to_cpu(rec->len) > ZIIRAVE_FIRM_PKT_DATA_SIZE) - return -EMSGSIZE; - memcpy(packet + 3, rec->data, be16_to_cpu(rec->len)); + memcpy(packet + 3, data, len); + memset(packet + 3 + len, 0, ZIIRAVE_FIRM_PKT_DATA_SIZE - len); /* Packet checksum */ - for (i = 0; i < ZIIRAVE_FIRM_PKT_TOTAL_SIZE - 1; i++) + for (i = 0; i < len + 3; i++) checksum += packet[i]; packet[ZIIRAVE_FIRM_PKT_TOTAL_SIZE - 1] = checksum; - ret = ziirave_firm_write_block_data(wdd, ZIIRAVE_CMD_DOWNLOAD_PACKET, - ARRAY_SIZE(packet), packet, true); + ret = i2c_smbus_write_block_data(client, ZIIRAVE_CMD_DOWNLOAD_PACKET, + sizeof(packet), packet); + if (ret) { + dev_err(&client->dev, + "Failed to send DOWNLOAD_PACKET: %d\n", ret); + return ret; + } + + ret = ziirave_firm_read_ack(wdd); if (ret) dev_err(&client->dev, "Failed to write firmware packet at address 0x%04x: %d\n", @@ -277,6 +270,30 @@ static int ziirave_firm_write_pkt(struct watchdog_device *wdd, return ret; } +static int ziirave_firm_write_pkt(struct watchdog_device *wdd, + u32 addr, const u8 *data, u8 len) +{ + const u8 max_write_len = ZIIRAVE_FIRM_PAGE_SIZE - + (addr - ALIGN_DOWN(addr, ZIIRAVE_FIRM_PAGE_SIZE)); + int ret; + + if (len > max_write_len) { + /* + * If data crossed page boundary we need to split this + * write in two + */ + ret = __ziirave_firm_write_pkt(wdd, addr, data, max_write_len); + if (ret) + return ret; + + addr += max_write_len; + data += max_write_len; + len -= max_write_len; + } + + return __ziirave_firm_write_pkt(wdd, addr, data, len); +} + static int ziirave_firm_verify(struct watchdog_device *wdd, const struct firmware *fw) { @@ -284,16 +301,12 @@ static int ziirave_firm_verify(struct watchdog_device *wdd, const struct ihex_binrec *rec; int i, ret; u8 data[ZIIRAVE_FIRM_PKT_DATA_SIZE]; - u16 addr; for (rec = (void *)fw->data; rec; rec = ihex_next_binrec(rec)) { - /* Zero length marks end of records */ - if (!be16_to_cpu(rec->len)) - break; + const u16 len = be16_to_cpu(rec->len); + const u32 addr = be32_to_cpu(rec->addr); - addr = (be32_to_cpu(rec->addr) & 0xffff) >> 1; - if (addr < ZIIRAVE_FIRM_FLASH_MEMORY_START || - addr > ZIIRAVE_FIRM_FLASH_MEMORY_END) + if (ziirave_firm_addr_readonly(addr)) continue; ret = ziirave_firm_set_read_addr(wdd, addr); @@ -304,7 +317,7 @@ static int ziirave_firm_verify(struct watchdog_device *wdd, return ret; } - for (i = 0; i < ARRAY_SIZE(data); i++) { + for (i = 0; i < len; i++) { ret = i2c_smbus_read_byte_data(client, ZIIRAVE_CMD_DOWNLOAD_READ_BYTE); if (ret < 0) { @@ -315,7 +328,7 @@ static int ziirave_firm_verify(struct watchdog_device *wdd, data[i] = ret; } - if (memcmp(data, rec->data, be16_to_cpu(rec->len))) { + if (memcmp(data, rec->data, len)) { dev_err(&client->dev, "Firmware mismatch at address 0x%04x\n", addr); return -EINVAL; @@ -329,97 +342,45 @@ static int ziirave_firm_upload(struct watchdog_device *wdd, const struct firmware *fw) { struct i2c_client *client = to_i2c_client(wdd->parent); - int ret, words_till_page_break; const struct ihex_binrec *rec; - struct ihex_binrec *rec_new; + int ret; - ret = ziirave_firm_write_byte(wdd, ZIIRAVE_CMD_JUMP_TO_BOOTLOADER, 1, - false); - if (ret) + ret = i2c_smbus_write_byte_data(client, + ZIIRAVE_CMD_JUMP_TO_BOOTLOADER, + ZIIRAVE_CMD_JUMP_TO_BOOTLOADER_MAGIC); + if (ret) { + dev_err(&client->dev, "Failed to jump to bootloader\n"); return ret; + } msleep(500); - ret = ziirave_firm_write_byte(wdd, ZIIRAVE_CMD_DOWNLOAD_START, 1, true); - if (ret) + ret = i2c_smbus_write_byte(client, ZIIRAVE_CMD_DOWNLOAD_START); + if (ret) { + dev_err(&client->dev, "Failed to start download\n"); return ret; + } + + ret = ziirave_firm_read_ack(wdd); + if (ret) { + dev_err(&client->dev, "No ACK for start download\n"); + return ret; + } msleep(500); for (rec = (void *)fw->data; rec; rec = ihex_next_binrec(rec)) { - /* Zero length marks end of records */ - if (!be16_to_cpu(rec->len)) - break; - - /* Check max data size */ - if (be16_to_cpu(rec->len) > ZIIRAVE_FIRM_PKT_DATA_SIZE) { - dev_err(&client->dev, "Firmware packet too long (%d)\n", - be16_to_cpu(rec->len)); - return -EMSGSIZE; - } - - /* Calculate words till page break */ - words_till_page_break = (64 - ((be32_to_cpu(rec->addr) >> 1) & - 0x3f)); - if ((be16_to_cpu(rec->len) >> 1) > words_till_page_break) { - /* - * Data in passes page boundary, so we need to split in - * two blocks of data. Create a packet with the first - * block of data. - */ - rec_new = kzalloc(sizeof(struct ihex_binrec) + - (words_till_page_break << 1), - GFP_KERNEL); - if (!rec_new) - return -ENOMEM; - - rec_new->len = cpu_to_be16(words_till_page_break << 1); - rec_new->addr = rec->addr; - memcpy(rec_new->data, rec->data, - be16_to_cpu(rec_new->len)); - - ret = ziirave_firm_write_pkt(wdd, rec_new); - kfree(rec_new); - if (ret) - return ret; - - /* Create a packet with the second block of data */ - rec_new = kzalloc(sizeof(struct ihex_binrec) + - be16_to_cpu(rec->len) - - (words_till_page_break << 1), - GFP_KERNEL); - if (!rec_new) - return -ENOMEM; - - /* Remaining bytes */ - rec_new->len = rec->len - - cpu_to_be16(words_till_page_break << 1); - - rec_new->addr = cpu_to_be32(be32_to_cpu(rec->addr) + - (words_till_page_break << 1)); - - memcpy(rec_new->data, - rec->data + (words_till_page_break << 1), - be16_to_cpu(rec_new->len)); - - ret = ziirave_firm_write_pkt(wdd, rec_new); - kfree(rec_new); - if (ret) - return ret; - } else { - ret = ziirave_firm_write_pkt(wdd, rec); - if (ret) - return ret; - } + ret = ziirave_firm_write_pkt(wdd, be32_to_cpu(rec->addr), + rec->data, be16_to_cpu(rec->len)); + if (ret) + return ret; } - /* For end of download, the length field will be set to 0 */ - rec_new = kzalloc(sizeof(struct ihex_binrec) + 1, GFP_KERNEL); - if (!rec_new) - return -ENOMEM; - - ret = ziirave_firm_write_pkt(wdd, rec_new); - kfree(rec_new); + /* + * Finish firmware download process by sending a zero length + * payload + */ + ret = ziirave_firm_write_pkt(wdd, 0, NULL, 0); if (ret) { dev_err(&client->dev, "Failed to send EMPTY packet: %d\n", ret); return ret; @@ -437,15 +398,22 @@ static int ziirave_firm_upload(struct watchdog_device *wdd, } /* End download operation */ - ret = ziirave_firm_write_byte(wdd, ZIIRAVE_CMD_DOWNLOAD_END, 1, false); - if (ret) + ret = i2c_smbus_write_byte(client, ZIIRAVE_CMD_DOWNLOAD_END); + if (ret) { + dev_err(&client->dev, + "Failed to end firmware download: %d\n", ret); return ret; + } /* Reset the processor */ - ret = ziirave_firm_write_byte(wdd, ZIIRAVE_CMD_RESET_PROCESSOR, 1, - false); - if (ret) + ret = i2c_smbus_write_byte_data(client, + ZIIRAVE_CMD_RESET_PROCESSOR, + ZIIRAVE_CMD_RESET_PROCESSOR_MAGIC); + if (ret) { + dev_err(&client->dev, + "Failed to reset the watchdog: %d\n", ret); return ret; + } msleep(500); @@ -478,7 +446,7 @@ static ssize_t ziirave_wdt_sysfs_show_firm(struct device *dev, if (ret) return ret; - ret = sprintf(buf, "02.%02u.%02u", w_priv->firmware_rev.major, + ret = sprintf(buf, ZIIRAVE_FW_VERSION_FMT, w_priv->firmware_rev.major, w_priv->firmware_rev.minor); mutex_unlock(&w_priv->sysfs_mutex); @@ -501,7 +469,7 @@ static ssize_t ziirave_wdt_sysfs_show_boot(struct device *dev, if (ret) return ret; - ret = sprintf(buf, "01.%02u.%02u", w_priv->bootloader_rev.major, + ret = sprintf(buf, ZIIRAVE_BL_VERSION_FMT, w_priv->bootloader_rev.major, w_priv->bootloader_rev.minor); mutex_unlock(&w_priv->sysfs_mutex); @@ -568,7 +536,8 @@ static ssize_t ziirave_wdt_sysfs_store_firm(struct device *dev, goto unlock_mutex; } - dev_info(&client->dev, "Firmware updated to version 02.%02u.%02u\n", + dev_info(&client->dev, + "Firmware updated to version " ZIIRAVE_FW_VERSION_FMT "\n", w_priv->firmware_rev.major, w_priv->firmware_rev.minor); /* Restore the watchdog timeout */ @@ -611,7 +580,7 @@ static int ziirave_wdt_init_duration(struct i2c_client *client) &reset_duration); if (ret) { dev_info(&client->dev, - "Unable to set reset pulse duration, using default\n"); + "No reset pulse duration specified, using default\n"); return 0; } } @@ -633,7 +602,10 @@ static int ziirave_wdt_probe(struct i2c_client *client, struct ziirave_wdt_data *w_priv; int val; - if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WRITE_BLOCK_DATA)) return -ENODEV; w_priv = devm_kzalloc(&client->dev, sizeof(*w_priv), GFP_KERNEL); @@ -658,57 +630,80 @@ static int ziirave_wdt_probe(struct i2c_client *client, */ if (w_priv->wdd.timeout == 0) { val = i2c_smbus_read_byte_data(client, ZIIRAVE_WDT_TIMEOUT); - if (val < 0) + if (val < 0) { + dev_err(&client->dev, "Failed to read timeout\n"); return val; + } - if (val < ZIIRAVE_TIMEOUT_MIN) - return -ENODEV; + if (val > ZIIRAVE_TIMEOUT_MAX || + val < ZIIRAVE_TIMEOUT_MIN) + val = ZIIRAVE_TIMEOUT_DEFAULT; w_priv->wdd.timeout = val; - } else { - ret = ziirave_wdt_set_timeout(&w_priv->wdd, - w_priv->wdd.timeout); - if (ret) - return ret; + } - dev_info(&client->dev, "Timeout set to %ds.", - w_priv->wdd.timeout); + ret = ziirave_wdt_set_timeout(&w_priv->wdd, w_priv->wdd.timeout); + if (ret) { + dev_err(&client->dev, "Failed to set timeout\n"); + return ret; } + dev_info(&client->dev, "Timeout set to %ds\n", w_priv->wdd.timeout); + watchdog_set_nowayout(&w_priv->wdd, nowayout); i2c_set_clientdata(client, w_priv); /* If in unconfigured state, set to stopped */ val = i2c_smbus_read_byte_data(client, ZIIRAVE_WDT_STATE); - if (val < 0) + if (val < 0) { + dev_err(&client->dev, "Failed to read state\n"); return val; + } if (val == ZIIRAVE_STATE_INITIAL) ziirave_wdt_stop(&w_priv->wdd); ret = ziirave_wdt_init_duration(client); - if (ret) + if (ret) { + dev_err(&client->dev, "Failed to init duration\n"); return ret; + } ret = ziirave_wdt_revision(client, &w_priv->firmware_rev, ZIIRAVE_WDT_FIRM_VER_MAJOR); - if (ret) + if (ret) { + dev_err(&client->dev, "Failed to read firmware version\n"); return ret; + } + + dev_info(&client->dev, + "Firmware version: " ZIIRAVE_FW_VERSION_FMT "\n", + w_priv->firmware_rev.major, w_priv->firmware_rev.minor); ret = ziirave_wdt_revision(client, &w_priv->bootloader_rev, ZIIRAVE_WDT_BOOT_VER_MAJOR); - if (ret) + if (ret) { + dev_err(&client->dev, "Failed to read bootloader version\n"); return ret; + } + + dev_info(&client->dev, + "Bootloader version: " ZIIRAVE_BL_VERSION_FMT "\n", + w_priv->bootloader_rev.major, w_priv->bootloader_rev.minor); w_priv->reset_reason = i2c_smbus_read_byte_data(client, ZIIRAVE_WDT_RESET_REASON); - if (w_priv->reset_reason < 0) + if (w_priv->reset_reason < 0) { + dev_err(&client->dev, "Failed to read reset reason\n"); return w_priv->reset_reason; + } if (w_priv->reset_reason >= ARRAY_SIZE(ziirave_reasons) || - !ziirave_reasons[w_priv->reset_reason]) + !ziirave_reasons[w_priv->reset_reason]) { + dev_err(&client->dev, "Invalid reset reason\n"); return -ENODEV; + } ret = watchdog_register_device(&w_priv->wdd); |