diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2021-11-10 18:41:22 +0100 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2021-11-10 18:41:22 +0100 |
commit | 89d714ab6043bca7356b5c823f5335f5dce1f930 (patch) | |
tree | f7a5ccb56675dd90de30a70827e532054f724f9a | |
parent | Merge tag 'rproc-v5.16' of git://git.kernel.org/pub/scm/linux/kernel/git/remo... (diff) | |
parent | watchdog: db8500_wdt: Rename symbols (diff) | |
download | linux-89d714ab6043bca7356b5c823f5335f5dce1f930.tar.xz linux-89d714ab6043bca7356b5c823f5335f5dce1f930.zip |
Merge tag 'linux-watchdog-5.16-rc1' of git://www.linux-watchdog.org/linux-watchdog
Pull watchdog updates from Wim Van Sebroeck:
- f71808e_wdt: convert to watchdog framework
- db8500_wdt: Rename driver (was ux500_wdt.c)
- sunxi: Add compatibles for R329 and D1
- mtk: add disable_wdt_extrst support
- several other small fixes and improvements
* tag 'linux-watchdog-5.16-rc1' of git://www.linux-watchdog.org/linux-watchdog: (30 commits)
watchdog: db8500_wdt: Rename symbols
watchdog: db8500_wdt: Rename driver
watchdog: ux500_wdt: Drop platform data
watchdog: bcm63xx_wdt: fix fallthrough warning
watchdog: iTCO_wdt: No need to stop the timer in probe
watchdog: s3c2410: describe driver in KConfig
watchdog: sp5100_tco: Add support for get_timeleft
watchdog: mtk: add disable_wdt_extrst support
dt-bindings: watchdog: mtk-wdt: add disable_wdt_extrst support
watchdog: rza_wdt: Use semicolons instead of commas
watchdog: mlx-wdt: Use regmap_write_bits()
watchdog: rti-wdt: Make use of the helper function devm_platform_ioremap_resource()
watchdog: iTCO_wdt: Make use of the helper function devm_platform_ioremap_resource()
watchdog: ar7_wdt: Make use of the helper function devm_platform_ioremap_resource_byname()
watchdog: sunxi_wdt: Add support for D1
dt-bindings: watchdog: sunxi: Add compatibles for D1
ar7: fix kernel builds for compiler test
dt-bindings: watchdog: sunxi: Add compatibles for R329
watchdog: meson_gxbb_wdt: add timeout parameter
watchdog: meson_gxbb_wdt: add nowayout parameter
...
22 files changed, 384 insertions, 795 deletions
diff --git a/Documentation/devicetree/bindings/watchdog/allwinner,sun4i-a10-wdt.yaml b/Documentation/devicetree/bindings/watchdog/allwinner,sun4i-a10-wdt.yaml index 9aa3c313c49f..44cad9427ae6 100644 --- a/Documentation/devicetree/bindings/watchdog/allwinner,sun4i-a10-wdt.yaml +++ b/Documentation/devicetree/bindings/watchdog/allwinner,sun4i-a10-wdt.yaml @@ -24,16 +24,33 @@ properties: - allwinner,sun50i-a100-wdt - allwinner,sun50i-h6-wdt - allwinner,sun50i-h616-wdt + - allwinner,sun50i-r329-wdt + - allwinner,sun50i-r329-wdt-reset - const: allwinner,sun6i-a31-wdt - items: - const: allwinner,suniv-f1c100s-wdt - const: allwinner,sun4i-a10-wdt + - const: allwinner,sun20i-d1-wdt + - items: + - const: allwinner,sun20i-d1-wdt-reset + - const: allwinner,sun20i-d1-wdt reg: maxItems: 1 clocks: - maxItems: 1 + minItems: 1 + maxItems: 2 + items: + - description: High-frequency oscillator input, divided internally + - description: Low-frequency oscillator input, only found on some variants + + clock-names: + minItems: 1 + maxItems: 2 + items: + - const: hosc + - const: losc interrupts: maxItems: 1 @@ -44,6 +61,35 @@ required: - clocks - interrupts +if: + properties: + compatible: + contains: + enum: + - allwinner,sun20i-d1-wdt + - allwinner,sun20i-d1-wdt-reset + - allwinner,sun50i-r329-wdt + - allwinner,sun50i-r329-wdt-reset + +then: + properties: + clocks: + minItems: 2 + + clock-names: + minItems: 2 + + required: + - clock-names + +else: + properties: + clocks: + maxItems: 1 + + clock-names: + maxItems: 1 + unevaluatedProperties: false examples: diff --git a/Documentation/devicetree/bindings/watchdog/mtk-wdt.txt b/Documentation/devicetree/bindings/watchdog/mtk-wdt.txt index a4e31ce96e0e..0114871f887a 100644 --- a/Documentation/devicetree/bindings/watchdog/mtk-wdt.txt +++ b/Documentation/devicetree/bindings/watchdog/mtk-wdt.txt @@ -22,6 +22,7 @@ Required properties: - reg : Specifies base physical address and size of the registers. Optional properties: +- mediatek,disable-extrst: disable send output reset signal - interrupts: Watchdog pre-timeout (bark) interrupt. - timeout-sec: contains the watchdog timeout in seconds. - #reset-cells: Should be 1. @@ -31,6 +32,7 @@ Example: watchdog: watchdog@10007000 { compatible = "mediatek,mt8183-wdt", "mediatek,mt6589-wdt"; + mediatek,disable-extrst; reg = <0 0x10007000 0 0x100>; interrupts = <GIC_SPI 139 IRQ_TYPE_NONE>; timeout-sec = <10>; diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index c1d3e7c116cf..56c61c99eb23 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -36,7 +36,6 @@ #include <linux/mfd/abx500/ab8500.h> #include <linux/regulator/db8500-prcmu.h> #include <linux/regulator/machine.h> -#include <linux/platform_data/ux500_wdt.h> #include "db8500-prcmu-regs.h" /* Index of different voltages to be used when accessing AVSData */ @@ -2939,18 +2938,8 @@ static struct regulator_init_data db8500_regulators[DB8500_NUM_REGULATORS] = { }, }; -static struct ux500_wdt_data db8500_wdt_pdata = { - .timeout = 600, /* 10 minutes */ - .has_28_bits_resolution = true, -}; - static const struct mfd_cell common_prcmu_devs[] = { - { - .name = "ux500_wdt", - .platform_data = &db8500_wdt_pdata, - .pdata_size = sizeof(db8500_wdt_pdata), - .id = -1, - }, + MFD_CELL_NAME("db8500_wdt"), MFD_CELL_NAME("db8500-cpuidle"), }; diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index bf59faeb3de1..9d222ba17ec6 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -496,16 +496,18 @@ config S3C2410_WATCHDOG select WATCHDOG_CORE select MFD_SYSCON if ARCH_EXYNOS help - Watchdog timer block in the Samsung SoCs. This will reboot - the system when the timer expires with the watchdog enabled. + Watchdog timer block in the Samsung S3C24xx, S3C64xx, S5Pv210 and + Exynos SoCs. This will reboot the system when the timer expires with + the watchdog enabled. The driver is limited by the speed of the system's PCLK signal, so with reasonably fast systems (PCLK around 50-66MHz) then watchdog intervals of over approximately 20seconds are unavailable. + Choose Y/M here only if you build for such Samsung SoC. The driver can be built as a module by choosing M, and will - be called s3c2410_wdt + be called s3c2410_wdt. config SA1100_WATCHDOG tristate "SA1100/PXA2xx watchdog" @@ -561,22 +563,6 @@ config PNX4008_WATCHDOG Say N if you are unsure. -config IOP_WATCHDOG - tristate "IOP Watchdog" - depends on ARCH_IOP13XX - select WATCHDOG_NOWAYOUT if (ARCH_IOP32X || ARCH_IOP33X) - help - Say Y here if to include support for the watchdog timer - in the Intel IOP3XX & IOP13XX I/O Processors. This driver can - be built as a module by choosing M. The module will - be called iop_wdt. - - Note: The IOP13XX watchdog does an Internal Bus Reset which will - affect both cores and the peripherals of the IOP. The ATU-X - and/or ATUe configuration registers will remain intact, but if - operating as an Root Complex and/or Central Resource, the PCI-X - and/or PCIe busses will also be reset. THIS IS A VERY BIG HAMMER. - config DAVINCI_WATCHDOG tristate "DaVinci watchdog" depends on ARCH_DAVINCI || ARCH_KEYSTONE || COMPILE_TEST @@ -743,17 +729,17 @@ config IMX7ULP_WDT 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" +config DB500_WATCHDOG + tristate "ST-Ericsson DB800 watchdog" depends on MFD_DB8500_PRCMU select WATCHDOG_CORE default y help Say Y here to include Watchdog timer support for the watchdog - existing in the prcmu of ST-Ericsson Ux500 series platforms. + existing in the prcmu of ST-Ericsson DB8500 platform. To compile this driver as a module, choose M here: the - module will be called ux500_wdt. + module will be called db500_wdt. config RETU_WATCHDOG tristate "Retu watchdog" @@ -1052,6 +1038,7 @@ config EBC_C384_WDT config F71808E_WDT tristate "Fintek F718xx, F818xx Super I/O Watchdog" depends on X86 + select WATCHDOG_CORE help This is the driver for the hardware watchdog on the Fintek F71808E, F71862FG, F71868, F71869, F71882FG, F71889FG, F81803, F81865, and @@ -1679,7 +1666,7 @@ config SIBYTE_WDOG config AR7_WDT tristate "TI AR7 Watchdog Timer" - depends on AR7 || (MIPS && COMPILE_TEST) + depends on AR7 || (MIPS && 32BIT && COMPILE_TEST) help Hardware driver for the TI AR7 Watchdog Timer. diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 1bd2d6f37c53..2ee97064145b 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -56,7 +56,6 @@ obj-$(CONFIG_SAMA5D4_WATCHDOG) += sama5d4_wdt.o obj-$(CONFIG_DW_WATCHDOG) += dw_wdt.o obj-$(CONFIG_EP93XX_WATCHDOG) += ep93xx_wdt.o obj-$(CONFIG_PNX4008_WATCHDOG) += pnx4008_wdt.o -obj-$(CONFIG_IOP_WATCHDOG) += iop_wdt.o obj-$(CONFIG_DAVINCI_WATCHDOG) += davinci_wdt.o obj-$(CONFIG_K3_RTI_WATCHDOG) += rti_wdt.o obj-$(CONFIG_ORION_WATCHDOG) += orion_wdt.o @@ -69,7 +68,7 @@ obj-$(CONFIG_TS72XX_WATCHDOG) += ts72xx_wdt.o obj-$(CONFIG_IMX2_WDT) += imx2_wdt.o obj-$(CONFIG_IMX_SC_WDT) += imx_sc_wdt.o obj-$(CONFIG_IMX7ULP_WDT) += imx7ulp_wdt.o -obj-$(CONFIG_UX500_WATCHDOG) += ux500_wdt.o +obj-$(CONFIG_DB500_WATCHDOG) += db8500_wdt.o obj-$(CONFIG_RETU_WATCHDOG) += retu_wdt.o obj-$(CONFIG_BCM2835_WDT) += bcm2835_wdt.o obj-$(CONFIG_MOXART_WDT) += moxart_wdt.o diff --git a/drivers/watchdog/ar7_wdt.c b/drivers/watchdog/ar7_wdt.c index ff37dc91057d..743e171d97a3 100644 --- a/drivers/watchdog/ar7_wdt.c +++ b/drivers/watchdog/ar7_wdt.c @@ -63,8 +63,6 @@ static DEFINE_SPINLOCK(wdt_lock); /* XXX currently fixed, allows max margin ~68.72 secs */ #define prescale_value 0xffff -/* Resource of the WDT registers */ -static struct resource *ar7_regs_wdt; /* Pointer to the remapped WDT IO space */ static struct ar7_wdt *ar7_wdt; @@ -265,9 +263,7 @@ static int ar7_wdt_probe(struct platform_device *pdev) { int rc; - ar7_regs_wdt = - platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs"); - ar7_wdt = devm_ioremap_resource(&pdev->dev, ar7_regs_wdt); + ar7_wdt = devm_platform_ioremap_resource_byname(pdev, "regs"); if (IS_ERR(ar7_wdt)) return PTR_ERR(ar7_wdt); diff --git a/drivers/watchdog/bcm63xx_wdt.c b/drivers/watchdog/bcm63xx_wdt.c index 7cdb25363ea0..56cc262571a5 100644 --- a/drivers/watchdog/bcm63xx_wdt.c +++ b/drivers/watchdog/bcm63xx_wdt.c @@ -207,6 +207,8 @@ static long bcm63xx_wdt_ioctl(struct file *file, unsigned int cmd, bcm63xx_wdt_pet(); + fallthrough; + case WDIOC_GETTIMEOUT: return put_user(wdt_time, p); diff --git a/drivers/watchdog/da9062_wdt.c b/drivers/watchdog/da9062_wdt.c index 706fb09c2f24..f02cbd530538 100644 --- a/drivers/watchdog/da9062_wdt.c +++ b/drivers/watchdog/da9062_wdt.c @@ -117,6 +117,13 @@ static int da9062_wdt_ping(struct watchdog_device *wdd) struct da9062_watchdog *wdt = watchdog_get_drvdata(wdd); int ret; + /* + * Prevent pings from occurring late in system poweroff/reboot sequence + * and possibly locking out restart handler from accessing i2c bus. + */ + if (system_state > SYSTEM_RUNNING) + return 0; + ret = da9062_reset_watchdog_timer(wdt); if (ret) dev_err(wdt->hw->dev, "Failed to ping the watchdog (err = %d)\n", diff --git a/drivers/watchdog/da9063_wdt.c b/drivers/watchdog/da9063_wdt.c index 423584252606..d79ce64e26a9 100644 --- a/drivers/watchdog/da9063_wdt.c +++ b/drivers/watchdog/da9063_wdt.c @@ -121,6 +121,13 @@ static int da9063_wdt_ping(struct watchdog_device *wdd) struct da9063 *da9063 = watchdog_get_drvdata(wdd); int ret; + /* + * Prevent pings from occurring late in system poweroff/reboot sequence + * and possibly locking out restart handler from accessing i2c bus. + */ + if (system_state > SYSTEM_RUNNING) + return 0; + ret = regmap_write(da9063->regmap, DA9063_REG_CONTROL_F, DA9063_WATCHDOG); if (ret) diff --git a/drivers/watchdog/ux500_wdt.c b/drivers/watchdog/db8500_wdt.c index 072758106865..6ed8b63d310d 100644 --- a/drivers/watchdog/ux500_wdt.c +++ b/drivers/watchdog/db8500_wdt.c @@ -15,7 +15,6 @@ #include <linux/uaccess.h> #include <linux/watchdog.h> #include <linux/platform_device.h> -#include <linux/platform_data/ux500_wdt.h> #include <linux/mfd/dbx500-prcmu.h> @@ -23,7 +22,6 @@ #define WATCHDOG_MIN 0 #define WATCHDOG_MAX28 268435 /* 28 bit resolution in ms == 268435.455 s */ -#define WATCHDOG_MAX32 4294967 /* 32 bit resolution in ms == 4294967.295 s */ static unsigned int timeout = WATCHDOG_TIMEOUT; module_param(timeout, uint, 0); @@ -37,67 +35,60 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); -static int ux500_wdt_start(struct watchdog_device *wdd) +static int db8500_wdt_start(struct watchdog_device *wdd) { return prcmu_enable_a9wdog(PRCMU_WDOG_ALL); } -static int ux500_wdt_stop(struct watchdog_device *wdd) +static int db8500_wdt_stop(struct watchdog_device *wdd) { return prcmu_disable_a9wdog(PRCMU_WDOG_ALL); } -static int ux500_wdt_keepalive(struct watchdog_device *wdd) +static int db8500_wdt_keepalive(struct watchdog_device *wdd) { return prcmu_kick_a9wdog(PRCMU_WDOG_ALL); } -static int ux500_wdt_set_timeout(struct watchdog_device *wdd, +static int db8500_wdt_set_timeout(struct watchdog_device *wdd, unsigned int timeout) { - ux500_wdt_stop(wdd); + db8500_wdt_stop(wdd); prcmu_load_a9wdog(PRCMU_WDOG_ALL, timeout * 1000); - ux500_wdt_start(wdd); + db8500_wdt_start(wdd); return 0; } -static const struct watchdog_info ux500_wdt_info = { +static const struct watchdog_info db8500_wdt_info = { .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, - .identity = "Ux500 WDT", + .identity = "DB8500 WDT", .firmware_version = 1, }; -static const struct watchdog_ops ux500_wdt_ops = { +static const struct watchdog_ops db8500_wdt_ops = { .owner = THIS_MODULE, - .start = ux500_wdt_start, - .stop = ux500_wdt_stop, - .ping = ux500_wdt_keepalive, - .set_timeout = ux500_wdt_set_timeout, + .start = db8500_wdt_start, + .stop = db8500_wdt_stop, + .ping = db8500_wdt_keepalive, + .set_timeout = db8500_wdt_set_timeout, }; -static struct watchdog_device ux500_wdt = { - .info = &ux500_wdt_info, - .ops = &ux500_wdt_ops, +static struct watchdog_device db8500_wdt = { + .info = &db8500_wdt_info, + .ops = &db8500_wdt_ops, .min_timeout = WATCHDOG_MIN, - .max_timeout = WATCHDOG_MAX32, + .max_timeout = WATCHDOG_MAX28, }; -static int ux500_wdt_probe(struct platform_device *pdev) +static int db8500_wdt_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; int ret; - struct ux500_wdt_data *pdata = dev_get_platdata(dev); - if (pdata) { - if (pdata->timeout > 0) - timeout = pdata->timeout; - if (pdata->has_28_bits_resolution) - ux500_wdt.max_timeout = WATCHDOG_MAX28; - } - - ux500_wdt.parent = dev; - watchdog_set_nowayout(&ux500_wdt, nowayout); + timeout = 600; /* Default to 10 minutes */ + db8500_wdt.parent = dev; + watchdog_set_nowayout(&db8500_wdt, nowayout); /* disable auto off on sleep */ prcmu_config_a9wdog(PRCMU_WDOG_CPU1, false); @@ -105,7 +96,7 @@ static int ux500_wdt_probe(struct platform_device *pdev) /* set HW initial value */ prcmu_load_a9wdog(PRCMU_WDOG_ALL, timeout * 1000); - ret = devm_watchdog_register_device(dev, &ux500_wdt); + ret = devm_watchdog_register_device(dev, &db8500_wdt); if (ret) return ret; @@ -115,47 +106,47 @@ static int ux500_wdt_probe(struct platform_device *pdev) } #ifdef CONFIG_PM -static int ux500_wdt_suspend(struct platform_device *pdev, +static int db8500_wdt_suspend(struct platform_device *pdev, pm_message_t state) { - if (watchdog_active(&ux500_wdt)) { - ux500_wdt_stop(&ux500_wdt); + if (watchdog_active(&db8500_wdt)) { + db8500_wdt_stop(&db8500_wdt); prcmu_config_a9wdog(PRCMU_WDOG_CPU1, true); prcmu_load_a9wdog(PRCMU_WDOG_ALL, timeout * 1000); - ux500_wdt_start(&ux500_wdt); + db8500_wdt_start(&db8500_wdt); } return 0; } -static int ux500_wdt_resume(struct platform_device *pdev) +static int db8500_wdt_resume(struct platform_device *pdev) { - if (watchdog_active(&ux500_wdt)) { - ux500_wdt_stop(&ux500_wdt); + if (watchdog_active(&db8500_wdt)) { + db8500_wdt_stop(&db8500_wdt); prcmu_config_a9wdog(PRCMU_WDOG_CPU1, false); prcmu_load_a9wdog(PRCMU_WDOG_ALL, timeout * 1000); - ux500_wdt_start(&ux500_wdt); + db8500_wdt_start(&db8500_wdt); } return 0; } #else -#define ux500_wdt_suspend NULL -#define ux500_wdt_resume NULL +#define db8500_wdt_suspend NULL +#define db8500_wdt_resume NULL #endif -static struct platform_driver ux500_wdt_driver = { - .probe = ux500_wdt_probe, - .suspend = ux500_wdt_suspend, - .resume = ux500_wdt_resume, +static struct platform_driver db8500_wdt_driver = { + .probe = db8500_wdt_probe, + .suspend = db8500_wdt_suspend, + .resume = db8500_wdt_resume, .driver = { - .name = "ux500_wdt", + .name = "db8500_wdt", }, }; -module_platform_driver(ux500_wdt_driver); +module_platform_driver(db8500_wdt_driver); MODULE_AUTHOR("Jonas Aaberg <jonas.aberg@stericsson.com>"); -MODULE_DESCRIPTION("Ux500 Watchdog Driver"); +MODULE_DESCRIPTION("DB8500 Watchdog Driver"); MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:ux500_wdt"); +MODULE_ALIAS("platform:db8500_wdt"); diff --git a/drivers/watchdog/f71808e_wdt.c b/drivers/watchdog/f71808e_wdt.c index f60beec1bbae..ee90c5f943f9 100644 --- a/drivers/watchdog/f71808e_wdt.c +++ b/drivers/watchdog/f71808e_wdt.c @@ -9,16 +9,11 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/err.h> -#include <linux/fs.h> #include <linux/init.h> #include <linux/io.h> #include <linux/ioport.h> -#include <linux/miscdevice.h> #include <linux/module.h> -#include <linux/mutex.h> -#include <linux/notifier.h> -#include <linux/reboot.h> -#include <linux/uaccess.h> +#include <linux/platform_device.h> #include <linux/watchdog.h> #define DRVNAME "f71808e_wdt" @@ -81,7 +76,6 @@ static unsigned short force_id; module_param(force_id, ushort, 0); MODULE_PARM_DESC(force_id, "Override the detected device ID"); -static const int max_timeout = WATCHDOG_MAX_TIMEOUT; static int timeout = WATCHDOG_TIMEOUT; /* default timeout in seconds */ module_param(timeout, int, 0); MODULE_PARM_DESC(timeout, @@ -113,7 +107,7 @@ MODULE_PARM_DESC(start_withtimeout, "Start watchdog timer on module load with" enum chips { f71808fg, f71858fg, f71862fg, f71868, f71869, f71882fg, f71889fg, f81803, f81865, f81866}; -static const char *f71808e_names[] = { +static const char * const fintek_wdt_names[] = { "f71808fg", "f71858fg", "f71862fg", @@ -136,24 +130,20 @@ static inline int superio_enter(int base); static inline void superio_select(int base, int ld); static inline void superio_exit(int base); -struct watchdog_data { +struct fintek_wdt { + struct watchdog_device wdd; unsigned short sioaddr; enum chips type; - unsigned long opened; - struct mutex lock; - char expect_close; struct watchdog_info ident; - unsigned short timeout; u8 timer_val; /* content for the wd_time register */ char minutes_mode; u8 pulse_val; /* pulse width flag */ char pulse_mode; /* enable pulse output mode? */ - char caused_reboot; /* last reboot was by the watchdog */ }; -static struct watchdog_data watchdog = { - .lock = __MUTEX_INITIALIZER(watchdog.lock), +struct fintek_wdt_pdata { + enum chips type; }; /* Super I/O functions */ @@ -218,156 +208,142 @@ static inline void superio_exit(int base) release_region(base, 2); } -static int watchdog_set_timeout(int timeout) +static int fintek_wdt_set_timeout(struct watchdog_device *wdd, unsigned int timeout) { - if (timeout <= 0 - || timeout > max_timeout) { - pr_err("watchdog timeout out of range\n"); - return -EINVAL; - } - - mutex_lock(&watchdog.lock); + struct fintek_wdt *wd = watchdog_get_drvdata(wdd); - watchdog.timeout = timeout; if (timeout > 0xff) { - watchdog.timer_val = DIV_ROUND_UP(timeout, 60); - watchdog.minutes_mode = true; + wd->timer_val = DIV_ROUND_UP(timeout, 60); + wd->minutes_mode = true; + timeout = wd->timer_val * 60; } else { - watchdog.timer_val = timeout; - watchdog.minutes_mode = false; + wd->timer_val = timeout; + wd->minutes_mode = false; } - mutex_unlock(&watchdog.lock); + wdd->timeout = timeout; return 0; } -static int watchdog_set_pulse_width(unsigned int pw) +static int fintek_wdt_set_pulse_width(struct fintek_wdt *wd, unsigned int pw) { - int err = 0; unsigned int t1 = 25, t2 = 125, t3 = 5000; - if (watchdog.type == f71868) { + if (wd->type == f71868) { t1 = 30; t2 = 150; t3 = 6000; } - mutex_lock(&watchdog.lock); - if (pw <= 1) { - watchdog.pulse_val = 0; + wd->pulse_val = 0; } else if (pw <= t1) { - watchdog.pulse_val = 1; + wd->pulse_val = 1; } else if (pw <= t2) { - watchdog.pulse_val = 2; + wd->pulse_val = 2; } else if (pw <= t3) { - watchdog.pulse_val = 3; + wd->pulse_val = 3; } else { pr_err("pulse width out of range\n"); - err = -EINVAL; - goto exit_unlock; + return -EINVAL; } - watchdog.pulse_mode = pw; + wd->pulse_mode = pw; -exit_unlock: - mutex_unlock(&watchdog.lock); - return err; + return 0; } -static int watchdog_keepalive(void) +static int fintek_wdt_keepalive(struct watchdog_device *wdd) { - int err = 0; + struct fintek_wdt *wd = watchdog_get_drvdata(wdd); + int err; - mutex_lock(&watchdog.lock); - err = superio_enter(watchdog.sioaddr); + err = superio_enter(wd->sioaddr); if (err) - goto exit_unlock; - superio_select(watchdog.sioaddr, SIO_F71808FG_LD_WDT); + return err; + superio_select(wd->sioaddr, SIO_F71808FG_LD_WDT); - if (watchdog.minutes_mode) + if (wd->minutes_mode) /* select minutes for timer units */ - superio_set_bit(watchdog.sioaddr, F71808FG_REG_WDT_CONF, + superio_set_bit(wd->sioaddr, F71808FG_REG_WDT_CONF, F71808FG_FLAG_WD_UNIT); else /* select seconds for timer units */ - superio_clear_bit(watchdog.sioaddr, F71808FG_REG_WDT_CONF, + superio_clear_bit(wd->sioaddr, F71808FG_REG_WDT_CONF, F71808FG_FLAG_WD_UNIT); /* Set timer value */ - superio_outb(watchdog.sioaddr, F71808FG_REG_WD_TIME, - watchdog.timer_val); + superio_outb(wd->sioaddr, F71808FG_REG_WD_TIME, + wd->timer_val); - superio_exit(watchdog.sioaddr); + superio_exit(wd->sioaddr); -exit_unlock: - mutex_unlock(&watchdog.lock); - return err; + return 0; } -static int watchdog_start(void) +static int fintek_wdt_start(struct watchdog_device *wdd) { + struct fintek_wdt *wd = watchdog_get_drvdata(wdd); int err; u8 tmp; /* Make sure we don't die as soon as the watchdog is enabled below */ - err = watchdog_keepalive(); + err = fintek_wdt_keepalive(wdd); if (err) return err; - mutex_lock(&watchdog.lock); - err = superio_enter(watchdog.sioaddr); + err = superio_enter(wd->sioaddr); if (err) - goto exit_unlock; - superio_select(watchdog.sioaddr, SIO_F71808FG_LD_WDT); + return err; + superio_select(wd->sioaddr, SIO_F71808FG_LD_WDT); /* Watchdog pin configuration */ - switch (watchdog.type) { + switch (wd->type) { case f71808fg: /* Set pin 21 to GPIO23/WDTRST#, then to WDTRST# */ - superio_clear_bit(watchdog.sioaddr, SIO_REG_MFUNCT2, 3); - superio_clear_bit(watchdog.sioaddr, SIO_REG_MFUNCT3, 3); + superio_clear_bit(wd->sioaddr, SIO_REG_MFUNCT2, 3); + superio_clear_bit(wd->sioaddr, SIO_REG_MFUNCT3, 3); break; case f71862fg: if (f71862fg_pin == 63) { /* SPI must be disabled first to use this pin! */ - superio_clear_bit(watchdog.sioaddr, SIO_REG_ROM_ADDR_SEL, 6); - superio_set_bit(watchdog.sioaddr, SIO_REG_MFUNCT3, 4); + superio_clear_bit(wd->sioaddr, SIO_REG_ROM_ADDR_SEL, 6); + superio_set_bit(wd->sioaddr, SIO_REG_MFUNCT3, 4); } else if (f71862fg_pin == 56) { - superio_set_bit(watchdog.sioaddr, SIO_REG_MFUNCT1, 1); + superio_set_bit(wd->sioaddr, SIO_REG_MFUNCT1, 1); } break; case f71868: case f71869: /* GPIO14 --> WDTRST# */ - superio_clear_bit(watchdog.sioaddr, SIO_REG_MFUNCT1, 4); + superio_clear_bit(wd->sioaddr, SIO_REG_MFUNCT1, 4); break; case f71882fg: /* Set pin 56 to WDTRST# */ - superio_set_bit(watchdog.sioaddr, SIO_REG_MFUNCT1, 1); + superio_set_bit(wd->sioaddr, SIO_REG_MFUNCT1, 1); break; case f71889fg: /* set pin 40 to WDTRST# */ - superio_outb(watchdog.sioaddr, SIO_REG_MFUNCT3, - superio_inb(watchdog.sioaddr, SIO_REG_MFUNCT3) & 0xcf); + superio_outb(wd->sioaddr, SIO_REG_MFUNCT3, + superio_inb(wd->sioaddr, SIO_REG_MFUNCT3) & 0xcf); break; case f81803: /* Enable TSI Level register bank */ - superio_clear_bit(watchdog.sioaddr, SIO_REG_CLOCK_SEL, 3); + superio_clear_bit(wd->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)); + superio_outb(wd->sioaddr, SIO_REG_TSI_LEVEL_SEL, 0x5f & + superio_inb(wd->sioaddr, SIO_REG_TSI_LEVEL_SEL)); break; case f81865: /* Set pin 70 to WDTRST# */ - superio_clear_bit(watchdog.sioaddr, SIO_REG_MFUNCT3, 5); + superio_clear_bit(wd->sioaddr, SIO_REG_MFUNCT3, 5); break; case f81866: @@ -377,12 +353,12 @@ static int watchdog_start(void) * BIT5: 0 -> WDTRST# * 1 -> GPIO15 */ - tmp = superio_inb(watchdog.sioaddr, SIO_F81866_REG_PORT_SEL); + tmp = superio_inb(wd->sioaddr, SIO_F81866_REG_PORT_SEL); tmp &= ~(BIT(3) | BIT(0)); tmp |= BIT(2); - superio_outb(watchdog.sioaddr, SIO_F81866_REG_PORT_SEL, tmp); + superio_outb(wd->sioaddr, SIO_F81866_REG_PORT_SEL, tmp); - superio_clear_bit(watchdog.sioaddr, SIO_F81866_REG_GPIO1, 5); + superio_clear_bit(wd->sioaddr, SIO_F81866_REG_GPIO1, 5); break; default: @@ -394,300 +370,114 @@ static int watchdog_start(void) goto exit_superio; } - superio_select(watchdog.sioaddr, SIO_F71808FG_LD_WDT); - superio_set_bit(watchdog.sioaddr, SIO_REG_ENABLE, 0); + superio_select(wd->sioaddr, SIO_F71808FG_LD_WDT); + superio_set_bit(wd->sioaddr, SIO_REG_ENABLE, 0); - if (watchdog.type == f81865 || watchdog.type == f81866) - superio_set_bit(watchdog.sioaddr, F81865_REG_WDO_CONF, + if (wd->type == f81865 || wd->type == f81866) + superio_set_bit(wd->sioaddr, F81865_REG_WDO_CONF, F81865_FLAG_WDOUT_EN); else - superio_set_bit(watchdog.sioaddr, F71808FG_REG_WDO_CONF, + superio_set_bit(wd->sioaddr, F71808FG_REG_WDO_CONF, F71808FG_FLAG_WDOUT_EN); - superio_set_bit(watchdog.sioaddr, F71808FG_REG_WDT_CONF, + superio_set_bit(wd->sioaddr, F71808FG_REG_WDT_CONF, F71808FG_FLAG_WD_EN); - if (watchdog.pulse_mode) { + if (wd->pulse_mode) { /* Select "pulse" output mode with given duration */ - u8 wdt_conf = superio_inb(watchdog.sioaddr, + u8 wdt_conf = superio_inb(wd->sioaddr, F71808FG_REG_WDT_CONF); /* Set WD_PSWIDTH bits (1:0) */ - wdt_conf = (wdt_conf & 0xfc) | (watchdog.pulse_val & 0x03); + wdt_conf = (wdt_conf & 0xfc) | (wd->pulse_val & 0x03); /* Set WD_PULSE to "pulse" mode */ wdt_conf |= BIT(F71808FG_FLAG_WD_PULSE); - superio_outb(watchdog.sioaddr, F71808FG_REG_WDT_CONF, + superio_outb(wd->sioaddr, F71808FG_REG_WDT_CONF, wdt_conf); } else { /* Select "level" output mode */ - superio_clear_bit(watchdog.sioaddr, F71808FG_REG_WDT_CONF, + superio_clear_bit(wd->sioaddr, F71808FG_REG_WDT_CONF, F71808FG_FLAG_WD_PULSE); } exit_superio: - superio_exit(watchdog.sioaddr); -exit_unlock: - mutex_unlock(&watchdog.lock); + superio_exit(wd->sioaddr); return err; } -static int watchdog_stop(void) -{ - int err = 0; - - mutex_lock(&watchdog.lock); - err = superio_enter(watchdog.sioaddr); - if (err) - goto exit_unlock; - superio_select(watchdog.sioaddr, SIO_F71808FG_LD_WDT); - - superio_clear_bit(watchdog.sioaddr, F71808FG_REG_WDT_CONF, - F71808FG_FLAG_WD_EN); - - superio_exit(watchdog.sioaddr); - -exit_unlock: - mutex_unlock(&watchdog.lock); - - return err; -} - -static int watchdog_get_status(void) -{ - int status = 0; - - mutex_lock(&watchdog.lock); - status = (watchdog.caused_reboot) ? WDIOF_CARDRESET : 0; - mutex_unlock(&watchdog.lock); - - return status; -} - -static bool watchdog_is_running(void) -{ - /* - * if we fail to determine the watchdog's status assume it to be - * running to be on the safe side - */ - bool is_running = true; - - mutex_lock(&watchdog.lock); - if (superio_enter(watchdog.sioaddr)) - goto exit_unlock; - superio_select(watchdog.sioaddr, SIO_F71808FG_LD_WDT); - - is_running = (superio_inb(watchdog.sioaddr, SIO_REG_ENABLE) & BIT(0)) - && (superio_inb(watchdog.sioaddr, F71808FG_REG_WDT_CONF) - & BIT(F71808FG_FLAG_WD_EN)); - - superio_exit(watchdog.sioaddr); - -exit_unlock: - mutex_unlock(&watchdog.lock); - return is_running; -} - -/* /dev/watchdog api */ - -static int watchdog_open(struct inode *inode, struct file *file) +static int fintek_wdt_stop(struct watchdog_device *wdd) { + struct fintek_wdt *wd = watchdog_get_drvdata(wdd); int err; - /* If the watchdog is alive we don't need to start it again */ - if (test_and_set_bit(0, &watchdog.opened)) - return -EBUSY; - - err = watchdog_start(); - if (err) { - clear_bit(0, &watchdog.opened); + err = superio_enter(wd->sioaddr); + if (err) return err; - } - - if (nowayout) - __module_get(THIS_MODULE); + superio_select(wd->sioaddr, SIO_F71808FG_LD_WDT); - watchdog.expect_close = 0; - return stream_open(inode, file); -} + superio_clear_bit(wd->sioaddr, F71808FG_REG_WDT_CONF, + F71808FG_FLAG_WD_EN); -static int watchdog_release(struct inode *inode, struct file *file) -{ - clear_bit(0, &watchdog.opened); + superio_exit(wd->sioaddr); - if (!watchdog.expect_close) { - watchdog_keepalive(); - pr_crit("Unexpected close, not stopping watchdog!\n"); - } else if (!nowayout) { - watchdog_stop(); - } return 0; } -/* - * watchdog_write: - * @file: file handle to the watchdog - * @buf: buffer to write - * @count: count of bytes - * @ppos: pointer to the position to write. No seeks allowed - * - * A write to a watchdog device is defined as a keepalive signal. Any - * write of data will do, as we we don't define content meaning. - */ - -static ssize_t watchdog_write(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) +static bool fintek_wdt_is_running(struct fintek_wdt *wd, u8 wdt_conf) { - if (count) { - if (!nowayout) { - size_t i; - - /* In case it was set long ago */ - bool expect_close = false; - - for (i = 0; i != count; i++) { - char c; - if (get_user(c, buf + i)) - return -EFAULT; - if (c == 'V') - expect_close = true; - } - - /* Properly order writes across fork()ed processes */ - mutex_lock(&watchdog.lock); - watchdog.expect_close = expect_close; - mutex_unlock(&watchdog.lock); - } - - /* someone wrote to us, we should restart timer */ - watchdog_keepalive(); - } - return count; + return (superio_inb(wd->sioaddr, SIO_REG_ENABLE) & BIT(0)) + && (wdt_conf & BIT(F71808FG_FLAG_WD_EN)); } -/* - * watchdog_ioctl: - * @inode: inode of the device - * @file: file handle to the device - * @cmd: watchdog command - * @arg: argument pointer - * - * The watchdog API defines a common set of functions for all watchdogs - * according to their available features. - */ -static long watchdog_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) -{ - int status; - int new_options; - int new_timeout; - union { - struct watchdog_info __user *ident; - int __user *i; - } uarg; - - uarg.i = (int __user *)arg; - - switch (cmd) { - case WDIOC_GETSUPPORT: - return copy_to_user(uarg.ident, &watchdog.ident, - sizeof(watchdog.ident)) ? -EFAULT : 0; - - case WDIOC_GETSTATUS: - status = watchdog_get_status(); - if (status < 0) - return status; - return put_user(status, uarg.i); - - case WDIOC_GETBOOTSTATUS: - return put_user(0, uarg.i); - - case WDIOC_SETOPTIONS: - if (get_user(new_options, uarg.i)) - return -EFAULT; - - if (new_options & WDIOS_DISABLECARD) - watchdog_stop(); - - if (new_options & WDIOS_ENABLECARD) - return watchdog_start(); - fallthrough; - - case WDIOC_KEEPALIVE: - watchdog_keepalive(); - return 0; - - case WDIOC_SETTIMEOUT: - if (get_user(new_timeout, uarg.i)) - return -EFAULT; - - if (watchdog_set_timeout(new_timeout)) - return -EINVAL; - - watchdog_keepalive(); - fallthrough; - - case WDIOC_GETTIMEOUT: - return put_user(watchdog.timeout, uarg.i); - - default: - return -ENOTTY; - - } -} +static const struct watchdog_ops fintek_wdt_ops = { + .owner = THIS_MODULE, + .start = fintek_wdt_start, + .stop = fintek_wdt_stop, + .ping = fintek_wdt_keepalive, + .set_timeout = fintek_wdt_set_timeout, +}; -static int watchdog_notify_sys(struct notifier_block *this, unsigned long code, - void *unused) +static int fintek_wdt_probe(struct platform_device *pdev) { - if (code == SYS_DOWN || code == SYS_HALT) - watchdog_stop(); - return NOTIFY_DONE; -} + struct device *dev = &pdev->dev; + struct fintek_wdt_pdata *pdata; + struct watchdog_device *wdd; + struct fintek_wdt *wd; + int wdt_conf, err = 0; + struct resource *res; + int sioaddr; -static const struct file_operations watchdog_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .open = watchdog_open, - .release = watchdog_release, - .write = watchdog_write, - .unlocked_ioctl = watchdog_ioctl, - .compat_ioctl = compat_ptr_ioctl, -}; + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (!res) + return -ENXIO; -static struct miscdevice watchdog_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &watchdog_fops, -}; + sioaddr = res->start; -static struct notifier_block watchdog_notifier = { - .notifier_call = watchdog_notify_sys, -}; + wd = devm_kzalloc(dev, sizeof(*wd), GFP_KERNEL); + if (!wd) + return -ENOMEM; -static int __init watchdog_init(int sioaddr) -{ - int wdt_conf, err = 0; + pdata = dev->platform_data; - /* No need to lock watchdog.lock here because no entry points - * into the module have been registered yet. - */ - watchdog.sioaddr = sioaddr; - watchdog.ident.options = WDIOF_MAGICCLOSE - | WDIOF_KEEPALIVEPING - | WDIOF_CARDRESET; + wd->type = pdata->type; + wd->sioaddr = sioaddr; + wd->ident.options = WDIOF_SETTIMEOUT + | WDIOF_MAGICCLOSE + | WDIOF_KEEPALIVEPING + | WDIOF_CARDRESET; - snprintf(watchdog.ident.identity, - sizeof(watchdog.ident.identity), "%s watchdog", - f71808e_names[watchdog.type]); + snprintf(wd->ident.identity, + sizeof(wd->ident.identity), "%s watchdog", + fintek_wdt_names[wd->type]); err = superio_enter(sioaddr); if (err) return err; - superio_select(watchdog.sioaddr, SIO_F71808FG_LD_WDT); + superio_select(wd->sioaddr, SIO_F71808FG_LD_WDT); wdt_conf = superio_inb(sioaddr, F71808FG_REG_WDT_CONF); - watchdog.caused_reboot = wdt_conf & BIT(F71808FG_FLAG_WDTMOUT_STS); /* * We don't want WDTMOUT_STS to stick around till regular reboot. @@ -696,84 +486,54 @@ static int __init watchdog_init(int sioaddr) superio_outb(sioaddr, F71808FG_REG_WDT_CONF, wdt_conf | BIT(F71808FG_FLAG_WDTMOUT_STS)); - superio_exit(sioaddr); + wdd = &wd->wdd; - err = watchdog_set_timeout(timeout); - if (err) - return err; - err = watchdog_set_pulse_width(pulse_width); - if (err) - return err; + if (fintek_wdt_is_running(wd, wdt_conf)) + set_bit(WDOG_HW_RUNNING, &wdd->status); - err = register_reboot_notifier(&watchdog_notifier); - if (err) - return err; + superio_exit(sioaddr); - err = misc_register(&watchdog_miscdev); - if (err) { - pr_err("cannot register miscdev on minor=%d\n", - watchdog_miscdev.minor); - goto exit_reboot; - } + wdd->parent = dev; + wdd->info = &wd->ident; + wdd->ops = &fintek_wdt_ops; + wdd->min_timeout = 1; + wdd->max_timeout = WATCHDOG_MAX_TIMEOUT; - if (start_withtimeout) { - if (start_withtimeout <= 0 - || start_withtimeout > max_timeout) { - pr_err("starting timeout out of range\n"); - err = -EINVAL; - goto exit_miscdev; - } + watchdog_set_drvdata(wdd, wd); + watchdog_set_nowayout(wdd, nowayout); + watchdog_stop_on_unregister(wdd); + watchdog_stop_on_reboot(wdd); + watchdog_init_timeout(wdd, start_withtimeout ?: timeout, NULL); - err = watchdog_start(); - if (err) { - pr_err("cannot start watchdog timer\n"); - goto exit_miscdev; - } + if (wdt_conf & BIT(F71808FG_FLAG_WDTMOUT_STS)) + wdd->bootstatus = WDIOF_CARDRESET; - mutex_lock(&watchdog.lock); - err = superio_enter(sioaddr); - if (err) - goto exit_unlock; - superio_select(watchdog.sioaddr, SIO_F71808FG_LD_WDT); + /* + * WATCHDOG_HANDLE_BOOT_ENABLED can result in keepalive being directly + * called without a set_timeout before, so it needs to be done here + * unconditionally. + */ + fintek_wdt_set_timeout(wdd, wdd->timeout); + fintek_wdt_set_pulse_width(wd, pulse_width); - if (start_withtimeout > 0xff) { - /* select minutes for timer units */ - superio_set_bit(sioaddr, F71808FG_REG_WDT_CONF, - F71808FG_FLAG_WD_UNIT); - superio_outb(sioaddr, F71808FG_REG_WD_TIME, - DIV_ROUND_UP(start_withtimeout, 60)); - } else { - /* select seconds for timer units */ - superio_clear_bit(sioaddr, F71808FG_REG_WDT_CONF, - F71808FG_FLAG_WD_UNIT); - superio_outb(sioaddr, F71808FG_REG_WD_TIME, - start_withtimeout); + if (start_withtimeout) { + err = fintek_wdt_start(wdd); + if (err) { + dev_err(dev, "cannot start watchdog timer\n"); + return err; } - superio_exit(sioaddr); - mutex_unlock(&watchdog.lock); - - if (nowayout) - __module_get(THIS_MODULE); - - pr_info("watchdog started with initial timeout of %u sec\n", - start_withtimeout); + set_bit(WDOG_HW_RUNNING, &wdd->status); + dev_info(dev, "watchdog started with initial timeout of %u sec\n", + start_withtimeout); } - return 0; - -exit_unlock: - mutex_unlock(&watchdog.lock); -exit_miscdev: - misc_deregister(&watchdog_miscdev); -exit_reboot: - unregister_reboot_notifier(&watchdog_notifier); - - return err; + return devm_watchdog_register_device(dev, wdd); } -static int __init f71808e_find(int sioaddr) +static int __init fintek_wdt_find(int sioaddr) { + enum chips type; u16 devid; int err = superio_enter(sioaddr); if (err) @@ -789,36 +549,36 @@ static int __init f71808e_find(int sioaddr) devid = force_id ? force_id : superio_inw(sioaddr, SIO_REG_DEVID); switch (devid) { case SIO_F71808_ID: - watchdog.type = f71808fg; + type = f71808fg; break; case SIO_F71862_ID: - watchdog.type = f71862fg; + type = f71862fg; break; case SIO_F71868_ID: - watchdog.type = f71868; + type = f71868; break; case SIO_F71869_ID: case SIO_F71869A_ID: - watchdog.type = f71869; + type = f71869; break; case SIO_F71882_ID: - watchdog.type = f71882fg; + type = f71882fg; break; case SIO_F71889_ID: - watchdog.type = f71889fg; + type = f71889fg; break; case SIO_F71858_ID: /* Confirmed (by datasheet) not to have a watchdog. */ err = -ENODEV; goto exit; case SIO_F81803_ID: - watchdog.type = f81803; + type = f81803; break; case SIO_F81865_ID: - watchdog.type = f81865; + type = f81865; break; case SIO_F81866_ID: - watchdog.type = f81866; + type = f81866; break; default: pr_info("Unrecognized Fintek device: %04x\n", @@ -828,17 +588,29 @@ static int __init f71808e_find(int sioaddr) } pr_info("Found %s watchdog chip, revision %d\n", - f71808e_names[watchdog.type], + fintek_wdt_names[type], (int)superio_inb(sioaddr, SIO_REG_DEVREV)); + exit: superio_exit(sioaddr); - return err; + return err ? err : type; } -static int __init f71808e_init(void) +static struct platform_driver fintek_wdt_driver = { + .probe = fintek_wdt_probe, + .driver = { + .name = DRVNAME, + }, +}; + +static struct platform_device *fintek_wdt_pdev; + +static int __init fintek_wdt_init(void) { static const unsigned short addrs[] = { 0x2e, 0x4e }; - int err = -ENODEV; + struct fintek_wdt_pdata pdata; + struct resource wdt_res = {}; + int ret; int i; if (f71862fg_pin != 63 && f71862fg_pin != 56) { @@ -847,29 +619,42 @@ static int __init f71808e_init(void) } for (i = 0; i < ARRAY_SIZE(addrs); i++) { - err = f71808e_find(addrs[i]); - if (err == 0) + ret = fintek_wdt_find(addrs[i]); + if (ret >= 0) break; } if (i == ARRAY_SIZE(addrs)) - return err; + return ret; - return watchdog_init(addrs[i]); + pdata.type = ret; + + platform_driver_register(&fintek_wdt_driver); + + wdt_res.name = "superio port"; + wdt_res.flags = IORESOURCE_IO; + wdt_res.start = addrs[i]; + wdt_res.end = addrs[i] + 1; + + fintek_wdt_pdev = platform_device_register_resndata(NULL, DRVNAME, -1, + &wdt_res, 1, + &pdata, sizeof(pdata)); + if (IS_ERR(fintek_wdt_pdev)) { + platform_driver_unregister(&fintek_wdt_driver); + return PTR_ERR(fintek_wdt_pdev); + } + + return 0; } -static void __exit f71808e_exit(void) +static void __exit fintek_wdt_exit(void) { - if (watchdog_is_running()) { - pr_warn("Watchdog timer still running, stopping it\n"); - watchdog_stop(); - } - misc_deregister(&watchdog_miscdev); - unregister_reboot_notifier(&watchdog_notifier); + platform_device_unregister(fintek_wdt_pdev); + platform_driver_unregister(&fintek_wdt_driver); } MODULE_DESCRIPTION("F71808E Watchdog Driver"); MODULE_AUTHOR("Giel van Schijndel <me@mortis.eu>"); MODULE_LICENSE("GPL"); -module_init(f71808e_init); -module_exit(f71808e_exit); +module_init(fintek_wdt_init); +module_exit(fintek_wdt_exit); diff --git a/drivers/watchdog/iTCO_wdt.c b/drivers/watchdog/iTCO_wdt.c index ced2fc0deb8c..3f2f4343644f 100644 --- a/drivers/watchdog/iTCO_wdt.c +++ b/drivers/watchdog/iTCO_wdt.c @@ -94,7 +94,6 @@ struct iTCO_wdt_private { * NO_REBOOT flag is Memory-Mapped GCS register bit 5 (TCO version 2), * or memory-mapped PMC register bit 4 (TCO version 3). */ - struct resource *gcs_pmc_res; unsigned long __iomem *gcs_pmc; /* the lock for io operations */ spinlock_t io_lock; @@ -424,6 +423,16 @@ static unsigned int iTCO_wdt_get_timeleft(struct watchdog_device *wd_dev) return time_left; } +static void iTCO_wdt_set_running(struct iTCO_wdt_private *p) +{ + u16 val; + + /* Bit 11: TCO Timer Halt -> 0 = The TCO timer is * enabled */ + val = inw(TCO1_CNT(p)); + if (!(val & BIT(11))) + set_bit(WDOG_HW_RUNNING, &p->wddev.status); +} + /* * Kernel Interfaces */ @@ -497,10 +506,7 @@ static int iTCO_wdt_probe(struct platform_device *pdev) */ if (p->iTCO_version >= 2 && p->iTCO_version < 6 && !pdata->no_reboot_use_pmc) { - p->gcs_pmc_res = platform_get_resource(pdev, - IORESOURCE_MEM, - ICH_RES_MEM_GCS_PMC); - p->gcs_pmc = devm_ioremap_resource(dev, p->gcs_pmc_res); + p->gcs_pmc = devm_platform_ioremap_resource(pdev, ICH_RES_MEM_GCS_PMC); if (IS_ERR(p->gcs_pmc)) return PTR_ERR(p->gcs_pmc); } @@ -566,8 +572,7 @@ static int iTCO_wdt_probe(struct platform_device *pdev) watchdog_set_drvdata(&p->wddev, p); platform_set_drvdata(pdev, p); - /* Make sure the watchdog is not running */ - iTCO_wdt_stop(&p->wddev); + iTCO_wdt_set_running(p); /* Check that the heartbeat value is within it's range; if not reset to the default */ diff --git a/drivers/watchdog/iop_wdt.c b/drivers/watchdog/iop_wdt.c deleted file mode 100644 index 6bf68d4750de..000000000000 --- a/drivers/watchdog/iop_wdt.c +++ /dev/null @@ -1,250 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * drivers/char/watchdog/iop_wdt.c - * - * WDT driver for Intel I/O Processors - * Copyright (C) 2005, Intel Corporation. - * - * Based on ixp4xx driver, Copyright 2004 (c) MontaVista, Software, Inc. - * - * Curt E Bruns <curt.e.bruns@intel.com> - * Peter Milne <peter.milne@d-tacq.com> - * Dan Williams <dan.j.williams@intel.com> - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/fs.h> -#include <linux/init.h> -#include <linux/device.h> -#include <linux/miscdevice.h> -#include <linux/watchdog.h> -#include <linux/uaccess.h> -#include <mach/hardware.h> - -static bool nowayout = WATCHDOG_NOWAYOUT; -static unsigned long wdt_status; -static unsigned long boot_status; -static DEFINE_SPINLOCK(wdt_lock); - -#define WDT_IN_USE 0 -#define WDT_OK_TO_CLOSE 1 -#define WDT_ENABLED 2 - -static unsigned long iop_watchdog_timeout(void) -{ - return (0xffffffffUL / get_iop_tick_rate()); -} - -/** - * wdt_supports_disable - determine if we are accessing a iop13xx watchdog - * or iop3xx by whether it has a disable command - */ -static int wdt_supports_disable(void) -{ - int can_disable; - - if (IOP_WDTCR_EN_ARM != IOP_WDTCR_DIS_ARM) - can_disable = 1; - else - can_disable = 0; - - return can_disable; -} - -static void wdt_enable(void) -{ - /* Arm and enable the Timer to starting counting down from 0xFFFF.FFFF - * Takes approx. 10.7s to timeout - */ - spin_lock(&wdt_lock); - write_wdtcr(IOP_WDTCR_EN_ARM); - write_wdtcr(IOP_WDTCR_EN); - spin_unlock(&wdt_lock); -} - -/* returns 0 if the timer was successfully disabled */ -static int wdt_disable(void) -{ - /* Stop Counting */ - if (wdt_supports_disable()) { - spin_lock(&wdt_lock); - write_wdtcr(IOP_WDTCR_DIS_ARM); - write_wdtcr(IOP_WDTCR_DIS); - clear_bit(WDT_ENABLED, &wdt_status); - spin_unlock(&wdt_lock); - pr_info("Disabled\n"); - return 0; - } else - return 1; -} - -static int iop_wdt_open(struct inode *inode, struct file *file) -{ - if (test_and_set_bit(WDT_IN_USE, &wdt_status)) - return -EBUSY; - - clear_bit(WDT_OK_TO_CLOSE, &wdt_status); - wdt_enable(); - set_bit(WDT_ENABLED, &wdt_status); - return stream_open(inode, file); -} - -static ssize_t iop_wdt_write(struct file *file, const char *data, size_t len, - loff_t *ppos) -{ - if (len) { - if (!nowayout) { - size_t i; - - clear_bit(WDT_OK_TO_CLOSE, &wdt_status); - - for (i = 0; i != len; i++) { - char c; - - if (get_user(c, data + i)) - return -EFAULT; - if (c == 'V') - set_bit(WDT_OK_TO_CLOSE, &wdt_status); - } - } - wdt_enable(); - } - return len; -} - -static const struct watchdog_info ident = { - .options = WDIOF_CARDRESET | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING, - .identity = "iop watchdog", -}; - -static long iop_wdt_ioctl(struct file *file, - unsigned int cmd, unsigned long arg) -{ - int options; - int ret = -ENOTTY; - int __user *argp = (int __user *)arg; - - switch (cmd) { - case WDIOC_GETSUPPORT: - if (copy_to_user(argp, &ident, sizeof(ident))) - ret = -EFAULT; - else - ret = 0; - break; - - case WDIOC_GETSTATUS: - ret = put_user(0, argp); - break; - - case WDIOC_GETBOOTSTATUS: - ret = put_user(boot_status, argp); - break; - - case WDIOC_SETOPTIONS: - if (get_user(options, (int *)arg)) - return -EFAULT; - - if (options & WDIOS_DISABLECARD) { - if (!nowayout) { - if (wdt_disable() == 0) { - set_bit(WDT_OK_TO_CLOSE, &wdt_status); - ret = 0; - } else - ret = -ENXIO; - } else - ret = 0; - } - if (options & WDIOS_ENABLECARD) { - wdt_enable(); - ret = 0; - } - break; - - case WDIOC_KEEPALIVE: - wdt_enable(); - ret = 0; - break; - - case WDIOC_GETTIMEOUT: - ret = put_user(iop_watchdog_timeout(), argp); - break; - } - return ret; -} - -static int iop_wdt_release(struct inode *inode, struct file *file) -{ - int state = 1; - if (test_bit(WDT_OK_TO_CLOSE, &wdt_status)) - if (test_bit(WDT_ENABLED, &wdt_status)) - state = wdt_disable(); - - /* if the timer is not disabled reload and notify that we are still - * going down - */ - if (state != 0) { - wdt_enable(); - pr_crit("Device closed unexpectedly - reset in %lu seconds\n", - iop_watchdog_timeout()); - } - - clear_bit(WDT_IN_USE, &wdt_status); - clear_bit(WDT_OK_TO_CLOSE, &wdt_status); - - return 0; -} - -static const struct file_operations iop_wdt_fops = { - .owner = THIS_MODULE, - .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, -}; - -static struct miscdevice iop_wdt_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &iop_wdt_fops, -}; - -static int __init iop_wdt_init(void) -{ - int ret; - - /* check if the reset was caused by the watchdog timer */ - boot_status = (read_rcsr() & IOP_RCSR_WDT) ? WDIOF_CARDRESET : 0; - - /* Configure Watchdog Timeout to cause an Internal Bus (IB) Reset - * NOTE: An IB Reset will Reset both cores in the IOP342 - */ - write_wdtsr(IOP13XX_WDTCR_IB_RESET); - - /* Register after we have the device set up so we cannot race - with an open */ - ret = misc_register(&iop_wdt_miscdev); - if (ret == 0) - pr_info("timeout %lu sec\n", iop_watchdog_timeout()); - - return ret; -} - -static void __exit iop_wdt_exit(void) -{ - misc_deregister(&iop_wdt_miscdev); -} - -module_init(iop_wdt_init); -module_exit(iop_wdt_exit); - -module_param(nowayout, bool, 0); -MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started"); - -MODULE_AUTHOR("Curt E Bruns <curt.e.bruns@intel.com>"); -MODULE_DESCRIPTION("iop watchdog timer driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/watchdog/meson_gxbb_wdt.c b/drivers/watchdog/meson_gxbb_wdt.c index 5a9ca10fbcfa..945f5e65db57 100644 --- a/drivers/watchdog/meson_gxbb_wdt.c +++ b/drivers/watchdog/meson_gxbb_wdt.c @@ -29,6 +29,16 @@ #define GXBB_WDT_TCNT_SETUP_MASK (BIT(16) - 1) #define GXBB_WDT_TCNT_CNT_SHIFT 16 +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started default=" + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); + +static unsigned int timeout; +module_param(timeout, uint, 0); +MODULE_PARM_DESC(timeout, "Watchdog heartbeat in seconds=" + __MODULE_STRING(DEFAULT_TIMEOUT) ")"); + struct meson_gxbb_wdt { void __iomem *reg_base; struct watchdog_device wdt_dev; @@ -175,6 +185,8 @@ static int meson_gxbb_wdt_probe(struct platform_device *pdev) data->wdt_dev.max_hw_heartbeat_ms = GXBB_WDT_TCNT_SETUP_MASK; data->wdt_dev.min_timeout = 1; data->wdt_dev.timeout = DEFAULT_TIMEOUT; + watchdog_init_timeout(&data->wdt_dev, timeout, dev); + watchdog_set_nowayout(&data->wdt_dev, nowayout); watchdog_set_drvdata(&data->wdt_dev, data); /* Setup with 1ms timebase */ diff --git a/drivers/watchdog/mlx_wdt.c b/drivers/watchdog/mlx_wdt.c index 54193369e85c..9c5b6616fc87 100644 --- a/drivers/watchdog/mlx_wdt.c +++ b/drivers/watchdog/mlx_wdt.c @@ -100,9 +100,8 @@ static int mlxreg_wdt_ping(struct watchdog_device *wdd) struct mlxreg_wdt *wdt = watchdog_get_drvdata(wdd); struct mlxreg_core_data *reg_data = &wdt->pdata->data[wdt->ping_idx]; - return regmap_update_bits_base(wdt->regmap, reg_data->reg, - ~reg_data->mask, BIT(reg_data->bit), - NULL, false, true); + return regmap_write_bits(wdt->regmap, reg_data->reg, ~reg_data->mask, + BIT(reg_data->bit)); } static int mlxreg_wdt_set_timeout(struct watchdog_device *wdd, diff --git a/drivers/watchdog/mtk_wdt.c b/drivers/watchdog/mtk_wdt.c index 3d208d627fb0..543cf38bd04e 100644 --- a/drivers/watchdog/mtk_wdt.c +++ b/drivers/watchdog/mtk_wdt.c @@ -65,6 +65,7 @@ struct mtk_wdt_dev { void __iomem *wdt_base; spinlock_t lock; /* protects WDT_SWSYSRST reg */ struct reset_controller_dev rcdev; + bool disable_wdt_extrst; }; struct mtk_wdt_data { @@ -256,6 +257,8 @@ static int mtk_wdt_start(struct watchdog_device *wdt_dev) reg |= (WDT_MODE_IRQ_EN | WDT_MODE_DUAL_EN); else reg &= ~(WDT_MODE_IRQ_EN | WDT_MODE_DUAL_EN); + if (mtk_wdt->disable_wdt_extrst) + reg &= ~WDT_MODE_EXRST_EN; reg |= (WDT_MODE_EN | WDT_MODE_KEY); iowrite32(reg, wdt_base + WDT_MODE); @@ -381,6 +384,10 @@ static int mtk_wdt_probe(struct platform_device *pdev) if (err) return err; } + + mtk_wdt->disable_wdt_extrst = + of_property_read_bool(dev->of_node, "mediatek,disable-extrst"); + return 0; } diff --git a/drivers/watchdog/rti_wdt.c b/drivers/watchdog/rti_wdt.c index 359302f71f7e..117bc2a8eb0a 100644 --- a/drivers/watchdog/rti_wdt.c +++ b/drivers/watchdog/rti_wdt.c @@ -194,7 +194,6 @@ static int rti_wdt_probe(struct platform_device *pdev) { int ret = 0; struct device *dev = &pdev->dev; - struct resource *wdt_mem; struct watchdog_device *wdd; struct rti_wdt_device *wdt; struct clk *clk; @@ -246,8 +245,7 @@ static int rti_wdt_probe(struct platform_device *pdev) watchdog_set_nowayout(wdd, 1); watchdog_set_restart_priority(wdd, 128); - wdt_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - wdt->base = devm_ioremap_resource(dev, wdt_mem); + wdt->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(wdt->base)) { ret = PTR_ERR(wdt->base); goto err_iomap; diff --git a/drivers/watchdog/rza_wdt.c b/drivers/watchdog/rza_wdt.c index 7b6c365f7cd3..fe6c2ed35e04 100644 --- a/drivers/watchdog/rza_wdt.c +++ b/drivers/watchdog/rza_wdt.c @@ -189,8 +189,8 @@ static int rza_wdt_probe(struct platform_device *pdev) return -ENOENT; } - priv->wdev.info = &rza_wdt_ident, - priv->wdev.ops = &rza_wdt_ops, + priv->wdev.info = &rza_wdt_ident; + priv->wdev.ops = &rza_wdt_ops; priv->wdev.parent = dev; priv->cks = (u8)(uintptr_t) of_device_get_match_data(dev); diff --git a/drivers/watchdog/sp5100_tco.c b/drivers/watchdog/sp5100_tco.c index a730ecbf78cd..dd9a744f82f8 100644 --- a/drivers/watchdog/sp5100_tco.c +++ b/drivers/watchdog/sp5100_tco.c @@ -10,6 +10,7 @@ * https://www.kernelconcepts.de * * See AMD Publication 43009 "AMD SB700/710/750 Register Reference Guide", + * AMD Publication 44413 "AMD SP5100 Register Reference Guide" * AMD Publication 45482 "AMD SB800-Series Southbridges Register * Reference Guide" * AMD Publication 48751 "BIOS and Kernel Developer’s Guide (BKDG) @@ -144,6 +145,13 @@ static int tco_timer_set_timeout(struct watchdog_device *wdd, return 0; } +static unsigned int tco_timer_get_timeleft(struct watchdog_device *wdd) +{ + struct sp5100_tco *tco = watchdog_get_drvdata(wdd); + + return readl(SP5100_WDT_COUNT(tco->tcobase)); +} + static u8 sp5100_tco_read_pm_reg8(u8 index) { outb(index, SP5100_IO_PM_INDEX_REG); @@ -386,6 +394,7 @@ static const struct watchdog_ops sp5100_tco_wdt_ops = { .stop = tco_timer_stop, .ping = tco_timer_ping, .set_timeout = tco_timer_set_timeout, + .get_timeleft = tco_timer_get_timeleft, }; static int sp5100_tco_probe(struct platform_device *pdev) diff --git a/drivers/watchdog/stm32_iwdg.c b/drivers/watchdog/stm32_iwdg.c index a3436c296c97..570a71509d2a 100644 --- a/drivers/watchdog/stm32_iwdg.c +++ b/drivers/watchdog/stm32_iwdg.c @@ -237,10 +237,8 @@ static int stm32_iwdg_probe(struct platform_device *pdev) /* This is the timer base. */ wdt->regs = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(wdt->regs)) { - dev_err(dev, "Could not get resource\n"); + if (IS_ERR(wdt->regs)) return PTR_ERR(wdt->regs); - } ret = stm32_iwdg_clk_init(pdev, wdt); if (ret) diff --git a/drivers/watchdog/sunxi_wdt.c b/drivers/watchdog/sunxi_wdt.c index b50757882a98..6cf82922d3fb 100644 --- a/drivers/watchdog/sunxi_wdt.c +++ b/drivers/watchdog/sunxi_wdt.c @@ -48,6 +48,7 @@ struct sunxi_wdt_reg { u8 wdt_timeout_shift; u8 wdt_reset_mask; u8 wdt_reset_val; + u32 wdt_key_val; }; struct sunxi_wdt_dev { @@ -91,12 +92,14 @@ static int sunxi_wdt_restart(struct watchdog_device *wdt_dev, val = readl(wdt_base + regs->wdt_cfg); val &= ~(regs->wdt_reset_mask); val |= regs->wdt_reset_val; + val |= regs->wdt_key_val; writel(val, wdt_base + regs->wdt_cfg); /* Set lowest timeout and enable watchdog */ val = readl(wdt_base + regs->wdt_mode); val &= ~(WDT_TIMEOUT_MASK << regs->wdt_timeout_shift); val |= WDT_MODE_EN; + val |= regs->wdt_key_val; writel(val, wdt_base + regs->wdt_mode); /* @@ -109,6 +112,7 @@ static int sunxi_wdt_restart(struct watchdog_device *wdt_dev, mdelay(5); val = readl(wdt_base + regs->wdt_mode); val |= WDT_MODE_EN; + val |= regs->wdt_key_val; writel(val, wdt_base + regs->wdt_mode); } return 0; @@ -141,6 +145,7 @@ static int sunxi_wdt_set_timeout(struct watchdog_device *wdt_dev, reg = readl(wdt_base + regs->wdt_mode); reg &= ~(WDT_TIMEOUT_MASK << regs->wdt_timeout_shift); reg |= wdt_timeout_map[timeout] << regs->wdt_timeout_shift; + reg |= regs->wdt_key_val; writel(reg, wdt_base + regs->wdt_mode); sunxi_wdt_ping(wdt_dev); @@ -154,7 +159,7 @@ static int sunxi_wdt_stop(struct watchdog_device *wdt_dev) void __iomem *wdt_base = sunxi_wdt->wdt_base; const struct sunxi_wdt_reg *regs = sunxi_wdt->wdt_regs; - writel(0, wdt_base + regs->wdt_mode); + writel(regs->wdt_key_val, wdt_base + regs->wdt_mode); return 0; } @@ -176,11 +181,13 @@ static int sunxi_wdt_start(struct watchdog_device *wdt_dev) reg = readl(wdt_base + regs->wdt_cfg); reg &= ~(regs->wdt_reset_mask); reg |= regs->wdt_reset_val; + reg |= regs->wdt_key_val; writel(reg, wdt_base + regs->wdt_cfg); /* Enable watchdog */ reg = readl(wdt_base + regs->wdt_mode); reg |= WDT_MODE_EN; + reg |= regs->wdt_key_val; writel(reg, wdt_base + regs->wdt_mode); return 0; @@ -220,9 +227,20 @@ static const struct sunxi_wdt_reg sun6i_wdt_reg = { .wdt_reset_val = 0x01, }; +static const struct sunxi_wdt_reg sun20i_wdt_reg = { + .wdt_ctrl = 0x10, + .wdt_cfg = 0x14, + .wdt_mode = 0x18, + .wdt_timeout_shift = 4, + .wdt_reset_mask = 0x03, + .wdt_reset_val = 0x01, + .wdt_key_val = 0x16aa0000, +}; + static const struct of_device_id sunxi_wdt_dt_ids[] = { { .compatible = "allwinner,sun4i-a10-wdt", .data = &sun4i_wdt_reg }, { .compatible = "allwinner,sun6i-a31-wdt", .data = &sun6i_wdt_reg }, + { .compatible = "allwinner,sun20i-d1-wdt", .data = &sun20i_wdt_reg }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, sunxi_wdt_dt_ids); diff --git a/include/linux/platform_data/ux500_wdt.h b/include/linux/platform_data/ux500_wdt.h deleted file mode 100644 index de6a4ad41e76..000000000000 --- a/include/linux/platform_data/ux500_wdt.h +++ /dev/null @@ -1,18 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (C) ST Ericsson SA 2011 - * - * STE Ux500 Watchdog platform data - */ -#ifndef __UX500_WDT_H -#define __UX500_WDT_H - -/** - * struct ux500_wdt_data - */ -struct ux500_wdt_data { - unsigned int timeout; - bool has_28_bits_resolution; -}; - -#endif /* __UX500_WDT_H */ |