From 923a3a863ae0c26876d704fb3453069e11ebdcb6 Mon Sep 17 00:00:00 2001 From: Michael Shych Date: Mon, 4 May 2020 17:14:24 +0300 Subject: platform_data/mlxreg: support new watchdog type with longer timeout period Add new watchdog type 3 with longer timeout period. Extend size of health_cntr field that that can be used to init watchdog timeout period. Signed-off-by: Michael Shych Reviewed-by: Vadim Pasternak Acked-by: Andy Shevchenko Link: https://lore.kernel.org/r/20200504141427.17685-2-michaelsh@mellanox.com Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- include/linux/platform_data/mlxreg.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/include/linux/platform_data/mlxreg.h b/include/linux/platform_data/mlxreg.h index b8da8aef2446..2c5e58d1d77b 100644 --- a/include/linux/platform_data/mlxreg.h +++ b/include/linux/platform_data/mlxreg.h @@ -43,10 +43,13 @@ * * TYPE1 HW watchdog implementation exist in old systems. * All new systems have TYPE2 HW watchdog. + * TYPE3 HW watchdog can exist on all systems with new CPLD. + * TYPE3 is selected by WD capability bit. */ enum mlxreg_wdt_type { MLX_WDT_TYPE1, MLX_WDT_TYPE2, + MLX_WDT_TYPE3, }; /** @@ -90,7 +93,7 @@ struct mlxreg_core_data { umode_t mode; struct device_node *np; struct mlxreg_hotplug_device hpdev; - u8 health_cntr; + u32 health_cntr; bool attached; }; -- cgit v1.2.3-59-g8ed1b From 7772b993fd96dc8f776356b1d5e18a4df7e68268 Mon Sep 17 00:00:00 2001 From: Michael Shych Date: Mon, 4 May 2020 17:14:25 +0300 Subject: platform/x86: mlx-platform: support new watchdog type with longer timeout Add verification of WD capability in order to distinguish between the existing WD types and new type, implemented in CPLD. Add configuration for a new WD type. Change access mode for watchdog registers. Signed-off-by: Michael Shych Reviewed-by: Vadim Pasternak Acked-by: Andy Shevchenko Link: https://lore.kernel.org/r/20200504141427.17685-3-michaelsh@mellanox.com Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/platform/x86/mlx-platform.c | 106 ++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) diff --git a/drivers/platform/x86/mlx-platform.c b/drivers/platform/x86/mlx-platform.c index c27548fd386a..9d3371cd58d5 100644 --- a/drivers/platform/x86/mlx-platform.c +++ b/drivers/platform/x86/mlx-platform.c @@ -178,7 +178,9 @@ #define MLXPLAT_CPLD_WD_RESET_ACT_MASK GENMASK(7, 1) #define MLXPLAT_CPLD_WD_FAN_ACT_MASK (GENMASK(7, 0) & ~BIT(4)) #define MLXPLAT_CPLD_WD_COUNT_ACT_MASK (GENMASK(7, 0) & ~BIT(7)) +#define MLXPLAT_CPLD_WD_CPBLTY_MASK (GENMASK(7, 0) & ~BIT(6)) #define MLXPLAT_CPLD_WD_DFLT_TIMEOUT 30 +#define MLXPLAT_CPLD_WD3_DFLT_TIMEOUT 600 #define MLXPLAT_CPLD_WD_MAX_DEVS 2 /* mlxplat_priv - platform private data @@ -1959,6 +1961,84 @@ static struct mlxreg_core_platform_data mlxplat_mlxcpld_wd_set_type2[] = { }, }; +/* Watchdog type3: hardware implementation version 3 + * Can be on all systems. It's differentiated by WD capability bit. + * Old systems (MSN2700, MSN2410, MSN2740, MSN2100 and MSN2140) + * still have only one main watchdog. + */ +static struct mlxreg_core_data mlxplat_mlxcpld_wd_main_regs_type3[] = { + { + .label = "action", + .reg = MLXPLAT_CPLD_LPC_REG_WD2_ACT_OFFSET, + .mask = MLXPLAT_CPLD_WD_RESET_ACT_MASK, + .bit = 0, + }, + { + .label = "timeout", + .reg = MLXPLAT_CPLD_LPC_REG_WD2_TMR_OFFSET, + .mask = MLXPLAT_CPLD_WD_TYPE2_TO_MASK, + .health_cntr = MLXPLAT_CPLD_WD3_DFLT_TIMEOUT, + }, + { + .label = "timeleft", + .reg = MLXPLAT_CPLD_LPC_REG_WD2_TMR_OFFSET, + .mask = MLXPLAT_CPLD_WD_TYPE2_TO_MASK, + }, + { + .label = "ping", + .reg = MLXPLAT_CPLD_LPC_REG_WD2_ACT_OFFSET, + .mask = MLXPLAT_CPLD_WD_RESET_ACT_MASK, + .bit = 0, + }, + { + .label = "reset", + .reg = MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(6), + .bit = 6, + }, +}; + +static struct mlxreg_core_data mlxplat_mlxcpld_wd_aux_regs_type3[] = { + { + .label = "action", + .reg = MLXPLAT_CPLD_LPC_REG_WD3_ACT_OFFSET, + .mask = MLXPLAT_CPLD_WD_FAN_ACT_MASK, + .bit = 4, + }, + { + .label = "timeout", + .reg = MLXPLAT_CPLD_LPC_REG_WD3_TMR_OFFSET, + .mask = MLXPLAT_CPLD_WD_TYPE2_TO_MASK, + .health_cntr = MLXPLAT_CPLD_WD3_DFLT_TIMEOUT, + }, + { + .label = "timeleft", + .reg = MLXPLAT_CPLD_LPC_REG_WD3_TMR_OFFSET, + .mask = MLXPLAT_CPLD_WD_TYPE2_TO_MASK, + }, + { + .label = "ping", + .reg = MLXPLAT_CPLD_LPC_REG_WD3_ACT_OFFSET, + .mask = MLXPLAT_CPLD_WD_FAN_ACT_MASK, + .bit = 4, + }, +}; + +static struct mlxreg_core_platform_data mlxplat_mlxcpld_wd_set_type3[] = { + { + .data = mlxplat_mlxcpld_wd_main_regs_type3, + .counter = ARRAY_SIZE(mlxplat_mlxcpld_wd_main_regs_type3), + .version = MLX_WDT_TYPE3, + .identity = "mlx-wdt-main", + }, + { + .data = mlxplat_mlxcpld_wd_aux_regs_type3, + .counter = ARRAY_SIZE(mlxplat_mlxcpld_wd_aux_regs_type3), + .version = MLX_WDT_TYPE3, + .identity = "mlx-wdt-aux", + }, +}; + static bool mlxplat_mlxcpld_writeable_reg(struct device *dev, unsigned int reg) { switch (reg) { @@ -1989,8 +2069,10 @@ static bool mlxplat_mlxcpld_writeable_reg(struct device *dev, unsigned int reg) case MLXPLAT_CPLD_LPC_REG_WD1_TMR_OFFSET: case MLXPLAT_CPLD_LPC_REG_WD1_ACT_OFFSET: case MLXPLAT_CPLD_LPC_REG_WD2_TMR_OFFSET: + case MLXPLAT_CPLD_LPC_REG_WD2_TLEFT_OFFSET: case MLXPLAT_CPLD_LPC_REG_WD2_ACT_OFFSET: case MLXPLAT_CPLD_LPC_REG_WD3_TMR_OFFSET: + case MLXPLAT_CPLD_LPC_REG_WD3_TLEFT_OFFSET: case MLXPLAT_CPLD_LPC_REG_WD3_ACT_OFFSET: case MLXPLAT_CPLD_LPC_REG_PWM1_OFFSET: case MLXPLAT_CPLD_LPC_REG_PWM_CONTROL_OFFSET: @@ -2601,6 +2683,27 @@ static int mlxplat_mlxcpld_verify_bus_topology(int *nr) return 0; } +static int mlxplat_mlxcpld_check_wd_capability(void *regmap) +{ + u32 regval; + int i, rc; + + rc = regmap_read(regmap, MLXPLAT_CPLD_LPC_REG_PSU_I2C_CAP_OFFSET, + ®val); + if (rc) + return rc; + + if (!(regval & ~MLXPLAT_CPLD_WD_CPBLTY_MASK)) { + for (i = 0; i < ARRAY_SIZE(mlxplat_mlxcpld_wd_set_type3); i++) { + if (mlxplat_wd_data[i]) + mlxplat_wd_data[i] = + &mlxplat_mlxcpld_wd_set_type3[i]; + } + } + + return 0; +} + static int __init mlxplat_init(void) { struct mlxplat_priv *priv; @@ -2733,6 +2836,9 @@ static int __init mlxplat_init(void) } /* Add WD drivers. */ + err = mlxplat_mlxcpld_check_wd_capability(priv->regmap); + if (err) + goto fail_platform_wd_register; for (j = 0; j < MLXPLAT_CPLD_WD_MAX_DEVS; j++) { if (mlxplat_wd_data[j]) { mlxplat_wd_data[j]->regmap = priv->regmap; -- cgit v1.2.3-59-g8ed1b From eee851143bca4422eeee3bb2e104b85537ba449d Mon Sep 17 00:00:00 2001 From: Michael Shych Date: Mon, 4 May 2020 17:14:26 +0300 Subject: watchdog: mlx-wdt: support new watchdog type with longer timeout period New programmable logic device can have watchdog type 3 implementation. It's same as Type 2 with extended maximum timeout period. Maximum timeout is up-to 65535 sec. Type 3 HW watchdog implementation can exist on all Mellanox systems. It is differentiated by WD capability bit. Signed-off-by: Michael Shych Reviewed-by: Vadim Pasternak Acked-by: Guenter Roeck Link: https://lore.kernel.org/r/20200504141427.17685-4-michaelsh@mellanox.com Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/mlx_wdt.c | 73 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 62 insertions(+), 11 deletions(-) diff --git a/drivers/watchdog/mlx_wdt.c b/drivers/watchdog/mlx_wdt.c index 03b9ac4b99af..54193369e85c 100644 --- a/drivers/watchdog/mlx_wdt.c +++ b/drivers/watchdog/mlx_wdt.c @@ -21,6 +21,7 @@ #define MLXREG_WDT_CLOCK_SCALE 1000 #define MLXREG_WDT_MAX_TIMEOUT_TYPE1 32 #define MLXREG_WDT_MAX_TIMEOUT_TYPE2 255 +#define MLXREG_WDT_MAX_TIMEOUT_TYPE3 65535 #define MLXREG_WDT_MIN_TIMEOUT 1 #define MLXREG_WDT_OPTIONS_BASE (WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE | \ WDIOF_SETTIMEOUT) @@ -49,6 +50,7 @@ struct mlxreg_wdt { int tleft_idx; int ping_idx; int reset_idx; + int regmap_val_sz; enum mlxreg_wdt_type wdt_type; }; @@ -111,7 +113,8 @@ static int mlxreg_wdt_set_timeout(struct watchdog_device *wdd, u32 regval, set_time, hw_timeout; int rc; - if (wdt->wdt_type == MLX_WDT_TYPE1) { + switch (wdt->wdt_type) { + case MLX_WDT_TYPE1: rc = regmap_read(wdt->regmap, reg_data->reg, ®val); if (rc) return rc; @@ -120,14 +123,32 @@ static int mlxreg_wdt_set_timeout(struct watchdog_device *wdd, regval = (regval & reg_data->mask) | hw_timeout; /* Rowndown to actual closest number of sec. */ set_time = BIT(hw_timeout) / MLXREG_WDT_CLOCK_SCALE; - } else { + rc = regmap_write(wdt->regmap, reg_data->reg, regval); + break; + case MLX_WDT_TYPE2: + set_time = timeout; + rc = regmap_write(wdt->regmap, reg_data->reg, timeout); + break; + case MLX_WDT_TYPE3: + /* WD_TYPE3 has 2B set time register */ set_time = timeout; - regval = timeout; + if (wdt->regmap_val_sz == 1) { + regval = timeout & 0xff; + rc = regmap_write(wdt->regmap, reg_data->reg, regval); + if (!rc) { + regval = (timeout & 0xff00) >> 8; + rc = regmap_write(wdt->regmap, + reg_data->reg + 1, regval); + } + } else { + rc = regmap_write(wdt->regmap, reg_data->reg, timeout); + } + break; + default: + return -EINVAL; } wdd->timeout = set_time; - rc = regmap_write(wdt->regmap, reg_data->reg, regval); - if (!rc) { /* * Restart watchdog with new timeout period @@ -147,10 +168,25 @@ static unsigned int mlxreg_wdt_get_timeleft(struct watchdog_device *wdd) { struct mlxreg_wdt *wdt = watchdog_get_drvdata(wdd); struct mlxreg_core_data *reg_data = &wdt->pdata->data[wdt->tleft_idx]; - u32 regval; + u32 regval, msb, lsb; int rc; - rc = regmap_read(wdt->regmap, reg_data->reg, ®val); + if (wdt->wdt_type == MLX_WDT_TYPE2) { + rc = regmap_read(wdt->regmap, reg_data->reg, ®val); + } else { + /* WD_TYPE3 has 2 byte timeleft register */ + if (wdt->regmap_val_sz == 1) { + rc = regmap_read(wdt->regmap, reg_data->reg, &lsb); + if (!rc) { + rc = regmap_read(wdt->regmap, + reg_data->reg + 1, &msb); + regval = (msb & 0xff) << 8 | (lsb & 0xff); + } + } else { + rc = regmap_read(wdt->regmap, reg_data->reg, ®val); + } + } + /* Return 0 timeleft in case of failure register read. */ return rc == 0 ? regval : 0; } @@ -212,13 +248,23 @@ static void mlxreg_wdt_config(struct mlxreg_wdt *wdt, wdt->wdd.info = &mlxreg_wdt_aux_info; wdt->wdt_type = pdata->version; - if (wdt->wdt_type == MLX_WDT_TYPE2) { - wdt->wdd.ops = &mlxreg_wdt_ops_type2; - wdt->wdd.max_timeout = MLXREG_WDT_MAX_TIMEOUT_TYPE2; - } else { + switch (wdt->wdt_type) { + case MLX_WDT_TYPE1: wdt->wdd.ops = &mlxreg_wdt_ops_type1; wdt->wdd.max_timeout = MLXREG_WDT_MAX_TIMEOUT_TYPE1; + break; + case MLX_WDT_TYPE2: + wdt->wdd.ops = &mlxreg_wdt_ops_type2; + wdt->wdd.max_timeout = MLXREG_WDT_MAX_TIMEOUT_TYPE2; + break; + case MLX_WDT_TYPE3: + wdt->wdd.ops = &mlxreg_wdt_ops_type2; + wdt->wdd.max_timeout = MLXREG_WDT_MAX_TIMEOUT_TYPE3; + break; + default: + break; } + wdt->wdd.min_timeout = MLXREG_WDT_MIN_TIMEOUT; } @@ -249,6 +295,11 @@ static int mlxreg_wdt_probe(struct platform_device *pdev) wdt->wdd.parent = dev; wdt->regmap = pdata->regmap; + rc = regmap_get_val_bytes(wdt->regmap); + if (rc < 0) + return -EINVAL; + + wdt->regmap_val_sz = rc; mlxreg_wdt_config(wdt, pdata); if ((pdata->features & MLXREG_CORE_WD_FEATURE_NOWAYOUT)) -- cgit v1.2.3-59-g8ed1b From d6e6d5627f0aaa16d6b6e94238d62a594a35b5ce Mon Sep 17 00:00:00 2001 From: Michael Shych Date: Mon, 4 May 2020 17:14:27 +0300 Subject: docs: watchdog: mlx-wdt: Add description of new watchdog type 3 Add documentation with details of new type of Mellanox watchdog driver. Signed-off-by: Michael Shych Reviewed-by: Vadim Pasternak Acked-by: Guenter Roeck Link: https://lore.kernel.org/r/20200504141427.17685-5-michaelsh@mellanox.com Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- Documentation/watchdog/mlx-wdt.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Documentation/watchdog/mlx-wdt.rst b/Documentation/watchdog/mlx-wdt.rst index bf5bafac47f0..35e690dea9db 100644 --- a/Documentation/watchdog/mlx-wdt.rst +++ b/Documentation/watchdog/mlx-wdt.rst @@ -24,10 +24,19 @@ Type 2: Maximum timeout is 255 sec. Get time-left is supported. +Type 3: + Same as Type 2 with extended maximum timeout period. + Maximum timeout is 65535 sec. + Type 1 HW watchdog implementation exist in old systems and all new systems have type 2 HW watchdog. Two types of HW implementation have also different register map. +Type 3 HW watchdog implementation can exist on all Mellanox systems +with new programmer logic device. +It's differentiated by WD capability bit. +Old systems still have only one main watchdog. + Mellanox system can have 2 watchdogs: main and auxiliary. Main and auxiliary watchdog devices can be enabled together on the same system. @@ -54,3 +63,4 @@ The driver checks during initialization if the previous system reset was done by the watchdog. If yes, it makes a notification about this event. Access to HW registers is performed through a generic regmap interface. +Programmable logic device registers have little-endian order. -- cgit v1.2.3-59-g8ed1b From 0be01476dd527dfa00976911208332c706a1b752 Mon Sep 17 00:00:00 2001 From: Bumsik Kim Date: Fri, 29 May 2020 10:24:28 +0900 Subject: watchdog: test_bit() => watchdog_active() Use the dedicated function watchdog_active() instead of the generic test_bit() function. It is done using the following Coccinelle script: @@ identifier wdd; @@ - test_bit(WDOG_ACTIVE, &wdd->status) + watchdog_active(wdd) Signed-off-by: Bumsik Kim Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20200529012428.84684-1-k.bumsik@gmail.com Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/watchdog_dev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c index 7e4cd34a8c20..3ae608d78af2 100644 --- a/drivers/watchdog/watchdog_dev.c +++ b/drivers/watchdog/watchdog_dev.c @@ -916,7 +916,7 @@ static int watchdog_release(struct inode *inode, struct file *file) * or if WDIOF_MAGICCLOSE is not set. If nowayout was set then * watchdog_stop will fail. */ - if (!test_bit(WDOG_ACTIVE, &wdd->status)) + if (!watchdog_active(wdd)) err = 0; else if (test_and_clear_bit(_WDOG_ALLOW_RELEASE, &wd_data->status) || !(wdd->info->options & WDIOF_MAGICCLOSE)) -- cgit v1.2.3-59-g8ed1b From ff01cb1ca1f9e02e54e562bb97999e4bf1726a9e Mon Sep 17 00:00:00 2001 From: Martin Wu Date: Fri, 29 May 2020 17:45:14 +0800 Subject: watchdog: sunxi_wdt: fix improper error exit code sunxi_wdt_probe() should return -ENOMEM when devm_kzalloc() fails. Signed-off-by: Martin Wu Signed-off-by: Frank Lee Acked-by: Maxime Ripard Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20200529094514.26374-1-frank@allwinnertech.com Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/sunxi_wdt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/watchdog/sunxi_wdt.c b/drivers/watchdog/sunxi_wdt.c index 5f05a45ac187..b50757882a98 100644 --- a/drivers/watchdog/sunxi_wdt.c +++ b/drivers/watchdog/sunxi_wdt.c @@ -235,7 +235,7 @@ static int sunxi_wdt_probe(struct platform_device *pdev) sunxi_wdt = devm_kzalloc(dev, sizeof(*sunxi_wdt), GFP_KERNEL); if (!sunxi_wdt) - return -EINVAL; + return -ENOMEM; sunxi_wdt->wdt_regs = of_device_get_match_data(dev); if (!sunxi_wdt->wdt_regs) -- cgit v1.2.3-59-g8ed1b From fd998972458f06d3fadbf4e55a3725c4edc17c12 Mon Sep 17 00:00:00 2001 From: Tiezhu Yang Date: Mon, 25 May 2020 15:31:04 +0800 Subject: watchdog: bcm_kona_wdt: Use correct return value for bcm_kona_wdt_probe() When call function devm_platform_ioremap_resource(), we should use IS_ERR() to check the return value and return PTR_ERR() if failed. Signed-off-by: Tiezhu Yang Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/1590391864-308-1-git-send-email-yangtiezhu@loongson.cn Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/bcm_kona_wdt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/watchdog/bcm_kona_wdt.c b/drivers/watchdog/bcm_kona_wdt.c index eb850a8d19df..8237c4e9c2a0 100644 --- a/drivers/watchdog/bcm_kona_wdt.c +++ b/drivers/watchdog/bcm_kona_wdt.c @@ -279,7 +279,7 @@ static int bcm_kona_wdt_probe(struct platform_device *pdev) wdt->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(wdt->base)) - return -ENODEV; + return PTR_ERR(wdt->base); wdt->resolution = SECWDOG_DEFAULT_RESOLUTION; ret = bcm_kona_wdt_set_resolution_reg(wdt); -- cgit v1.2.3-59-g8ed1b From 893c3d82b425d8d448da030c80420072ff4edbf1 Mon Sep 17 00:00:00 2001 From: Wang Qing Date: Fri, 12 Jun 2020 15:03:04 +0800 Subject: watchdog: Use kobj_to_dev() API Use kobj_to_dev() API instead of container_of(). Signed-off-by: Wang Qing Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/1591945384-14587-1-git-send-email-wangqing@vivo.com Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/watchdog_dev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c index 3ae608d78af2..de02d90a4878 100644 --- a/drivers/watchdog/watchdog_dev.c +++ b/drivers/watchdog/watchdog_dev.c @@ -587,7 +587,7 @@ static DEVICE_ATTR_RW(pretimeout_governor); static umode_t wdt_is_visible(struct kobject *kobj, struct attribute *attr, int n) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct watchdog_device *wdd = dev_get_drvdata(dev); umode_t mode = attr->mode; -- cgit v1.2.3-59-g8ed1b From 04daa8c049bc0cd4747d671d8b855f3d0fa39997 Mon Sep 17 00:00:00 2001 From: Lukas Bulwahn Date: Tue, 2 Jun 2020 07:21:04 +0200 Subject: MAINTAINERS: rectify entry in ARM SMC WATCHDOG DRIVER Commit 5c24a28b4eb8 ("dt-bindings: watchdog: Add ARM smc wdt for mt8173 watchdog") added the new ARM SMC WATCHDOG DRIVER entry in MAINTAINERS, but slipped in a minor mistake. Luckily, ./scripts/get_maintainer.pl --self-test=patterns complains: warning: no file matches F: devicetree/bindings/watchdog/arm-smc-wdt.yaml Update file entry to intended file location. Signed-off-by: Lukas Bulwahn Reviewed-by: Guenter Roeck Reviewed-by: Julius Werner Reviewed-by: Evan Benn Link: https://lore.kernel.org/r/20200602052104.7795-1-lukas.bulwahn@gmail.com Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index f0569cf304ca..284c14fe080c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1489,7 +1489,7 @@ ARM SMC WATCHDOG DRIVER M: Julius Werner R: Evan Benn S: Maintained -F: devicetree/bindings/watchdog/arm-smc-wdt.yaml +F: Documentation/devicetree/bindings/watchdog/arm-smc-wdt.yaml F: drivers/watchdog/arm_smc_wdt.c ARM SMMU DRIVERS -- cgit v1.2.3-59-g8ed1b From 9807a8884143148f2ee5a882d6273806403e9345 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Sat, 30 May 2020 10:35:51 +0300 Subject: dt-bindings: watchdog: Convert DW WDT binding to DT schema Modern device tree bindings are supposed to be created as YAML-files in accordance with dt-schema. This commit replaces the DW Watchdog legacy bare text bindings with YAML file. As before the binding states that the corresponding dts node is supposed to have a registers range, a watchdog timer references clock source, optional reset line and pre-timeout interrupt. Signed-off-by: Serge Semin Reviewed-by: Rob Herring Reviewed-by: Guenter Roeck Cc: Alexey Malahov Cc: Thomas Bogendoerfer Cc: Arnd Bergmann Cc: linux-mips@vger.kernel.org Link: https://lore.kernel.org/r/20200530073557.22661-2-Sergey.Semin@baikalelectronics.ru Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- .../devicetree/bindings/watchdog/dw_wdt.txt | 24 ----------- .../devicetree/bindings/watchdog/snps,dw-wdt.yaml | 50 ++++++++++++++++++++++ 2 files changed, 50 insertions(+), 24 deletions(-) delete mode 100644 Documentation/devicetree/bindings/watchdog/dw_wdt.txt create mode 100644 Documentation/devicetree/bindings/watchdog/snps,dw-wdt.yaml diff --git a/Documentation/devicetree/bindings/watchdog/dw_wdt.txt b/Documentation/devicetree/bindings/watchdog/dw_wdt.txt deleted file mode 100644 index eb0914420c7c..000000000000 --- a/Documentation/devicetree/bindings/watchdog/dw_wdt.txt +++ /dev/null @@ -1,24 +0,0 @@ -Synopsys Designware Watchdog Timer - -Required Properties: - -- compatible : Should contain "snps,dw-wdt" -- reg : Base address and size of the watchdog timer registers. -- clocks : phandle + clock-specifier for the clock that drives the - watchdog timer. - -Optional Properties: - -- interrupts : The interrupt used for the watchdog timeout warning. -- resets : phandle pointing to the system reset controller with - line index for the watchdog. - -Example: - - watchdog0: wd@ffd02000 { - compatible = "snps,dw-wdt"; - reg = <0xffd02000 0x1000>; - interrupts = <0 171 4>; - clocks = <&per_base_clk>; - resets = <&rst WDT0_RESET>; - }; diff --git a/Documentation/devicetree/bindings/watchdog/snps,dw-wdt.yaml b/Documentation/devicetree/bindings/watchdog/snps,dw-wdt.yaml new file mode 100644 index 000000000000..4f6944756ab4 --- /dev/null +++ b/Documentation/devicetree/bindings/watchdog/snps,dw-wdt.yaml @@ -0,0 +1,50 @@ +# SPDX-License-Identifier: GPL-2.0-only +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/watchdog/snps,dw-wdt.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Synopsys Designware Watchdog Timer + +allOf: + - $ref: "watchdog.yaml#" + +maintainers: + - Jamie Iles + +properties: + compatible: + const: snps,dw-wdt + + reg: + maxItems: 1 + + interrupts: + description: DW Watchdog pre-timeout interrupt + maxItems: 1 + + clocks: + items: + - description: Watchdog timer reference clock + + resets: + description: Phandle to the DW Watchdog reset lane + maxItems: 1 + +unevaluatedProperties: false + +required: + - compatible + - reg + - clocks + +examples: + - | + watchdog@ffd02000 { + compatible = "snps,dw-wdt"; + reg = <0xffd02000 0x1000>; + interrupts = <0 171 4>; + clocks = <&per_base_clk>; + resets = <&wdt_rst>; + }; +... -- cgit v1.2.3-59-g8ed1b From 5b4f68f808ff96a0a48456ff8da250a2caf3f545 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Sat, 30 May 2020 10:35:52 +0300 Subject: dt-bindings: watchdog: dw-wdt: Support devices with asynch clocks DW Watchdog IP core can be synthesised with asynchronous timer/APB clocks support (WDT_ASYNC_CLK_MODE_ENABLE == 1). In this case separate clock signals are supposed to be used to feed watchdog timer and APB interface of the device. Let's update the DW Watchdog DT node schema so it would support the optional APB3 bus clock specified along with the mandatory watchdog timer reference clock. Signed-off-by: Serge Semin Reviewed-by: Rob Herring Reviewed-by: Guenter Roeck Cc: Alexey Malahov Cc: Thomas Bogendoerfer Cc: Arnd Bergmann Cc: linux-mips@vger.kernel.org Link: https://lore.kernel.org/r/20200530073557.22661-3-Sergey.Semin@baikalelectronics.ru Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- Documentation/devicetree/bindings/watchdog/snps,dw-wdt.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Documentation/devicetree/bindings/watchdog/snps,dw-wdt.yaml b/Documentation/devicetree/bindings/watchdog/snps,dw-wdt.yaml index 4f6944756ab4..5bf6dc6377f3 100644 --- a/Documentation/devicetree/bindings/watchdog/snps,dw-wdt.yaml +++ b/Documentation/devicetree/bindings/watchdog/snps,dw-wdt.yaml @@ -24,8 +24,16 @@ properties: maxItems: 1 clocks: + minItems: 1 items: - description: Watchdog timer reference clock + - description: APB3 interface clock + + clock-names: + minItems: 1 + items: + - const: tclk + - const: pclk resets: description: Phandle to the DW Watchdog reset lane -- cgit v1.2.3-59-g8ed1b From 4ce4e7fdc3c924ea3e6d5d68178127b46568d8df Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Sat, 30 May 2020 10:35:53 +0300 Subject: dt-bindings: watchdog: dw-wdt: Add watchdog TOPs array property In case if DW Watchdog IP core is built with WDT_USE_FIX_TOP == false, a custom timeout periods are used to preset the timer counter. In this case that periods should be specified in a new "snps,watchdog-tops" property of the DW watchdog dts node. Signed-off-by: Serge Semin Reviewed-by: Rob Herring Reviewed-by: Guenter Roeck Cc: Alexey Malahov Cc: Thomas Bogendoerfer Cc: Arnd Bergmann Cc: linux-mips@vger.kernel.org Link: https://lore.kernel.org/r/20200530073557.22661-4-Sergey.Semin@baikalelectronics.ru Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- .../devicetree/bindings/watchdog/snps,dw-wdt.yaml | 32 ++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/Documentation/devicetree/bindings/watchdog/snps,dw-wdt.yaml b/Documentation/devicetree/bindings/watchdog/snps,dw-wdt.yaml index 5bf6dc6377f3..d9fc7bb851b1 100644 --- a/Documentation/devicetree/bindings/watchdog/snps,dw-wdt.yaml +++ b/Documentation/devicetree/bindings/watchdog/snps,dw-wdt.yaml @@ -39,6 +39,23 @@ properties: description: Phandle to the DW Watchdog reset lane maxItems: 1 + snps,watchdog-tops: + $ref: /schemas/types.yaml#/definitions/uint32-array + description: | + DW APB Watchdog custom timer intervals - Timeout Period ranges (TOPs). + Each TOP is a number loaded into the watchdog counter at the moment of + the timer restart. The counter decrementing happens each tick of the + reference clock. Therefore the TOPs array is equivalent to an array of + the timer expiration intervals supported by the DW APB Watchdog. Note + DW APB Watchdog IP-core might be synthesized with fixed TOP values, + in which case this property is unnecessary with default TOPs utilized. + default: [0x0001000 0x0002000 0x0004000 0x0008000 + 0x0010000 0x0020000 0x0040000 0x0080000 + 0x0100000 0x0200000 0x0400000 0x0800000 + 0x1000000 0x2000000 0x4000000 0x8000000] + minItems: 16 + maxItems: 16 + unevaluatedProperties: false required: @@ -55,4 +72,19 @@ examples: clocks = <&per_base_clk>; resets = <&wdt_rst>; }; + + - | + watchdog@ffd02000 { + compatible = "snps,dw-wdt"; + reg = <0xffd02000 0x1000>; + interrupts = <0 171 4>; + clocks = <&per_base_clk>; + clock-names = "tclk"; + snps,watchdog-tops = <0x000000FF 0x000001FF 0x000003FF + 0x000007FF 0x0000FFFF 0x0001FFFF + 0x0003FFFF 0x0007FFFF 0x000FFFFF + 0x001FFFFF 0x003FFFFF 0x007FFFFF + 0x00FFFFFF 0x01FFFFFF 0x03FFFFFF + 0x07FFFFFF>; + }; ... -- cgit v1.2.3-59-g8ed1b From 86445535887e580036d0094a849126703a33fd62 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Sat, 30 May 2020 10:35:54 +0300 Subject: watchdog: dw_wdt: Support devices with non-fixed TOP values In case if the DW Watchdog IP core is synthesised with WDT_USE_FIX_TOP == false, the TOP interval indexes make the device to load a custom periods to the counter. These periods are hardwired at the IP synthesis stage and can be within [2^8, 2^(WDT_CNT_WIDTH - 1)]. Alas their values can't be detected at runtime and must be somehow supplied to the driver so one could properly determine the watchdog timeout intervals. For this purpose we suggest to have a vendor- specific dts property "snps,watchdog-tops" utilized, which would provide an array of sixteen counter values. At device probe stage they will be used to initialize the watchdog device timeouts determined from the array values and current clocks source rate. In order to have custom TOP values supported the driver must be altered in the following way. First of all the fixed-top values ready-to-use array must be determined for compatibility with currently supported devices, which were synthesised with WDT_USE_FIX_TOP == true. It will be used if either fixed TOP feature is detected being enabled or no custom TOPs are fetched from the device dt node. Secondly at the probe stage we must initialize an array of the watchdog timeouts corresponding to the detected TOPs list and the reference clock rate. For generality the procedure of initialization is designed in a way to support the TOPs array with no limitations on the items order or value. Finally the watchdog period search methods should be altered to support the new timeouts data structure. Signed-off-by: Serge Semin Reviewed-by: Guenter Roeck Cc: Alexey Malahov Cc: Thomas Bogendoerfer Cc: Arnd Bergmann Cc: Rob Herring Cc: linux-mips@vger.kernel.org Cc: devicetree@vger.kernel.org Link: https://lore.kernel.org/r/20200530073557.22661-5-Sergey.Semin@baikalelectronics.ru Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/dw_wdt.c | 191 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 167 insertions(+), 24 deletions(-) diff --git a/drivers/watchdog/dw_wdt.c b/drivers/watchdog/dw_wdt.c index fba21de2bbad..693c0d1fd796 100644 --- a/drivers/watchdog/dw_wdt.c +++ b/drivers/watchdog/dw_wdt.c @@ -13,6 +13,8 @@ */ #include +#include +#include #include #include #include @@ -34,21 +36,40 @@ #define WDOG_CURRENT_COUNT_REG_OFFSET 0x08 #define WDOG_COUNTER_RESTART_REG_OFFSET 0x0c #define WDOG_COUNTER_RESTART_KICK_VALUE 0x76 +#define WDOG_COMP_PARAMS_1_REG_OFFSET 0xf4 +#define WDOG_COMP_PARAMS_1_USE_FIX_TOP BIT(6) -/* The maximum TOP (timeout period) value that can be set in the watchdog. */ -#define DW_WDT_MAX_TOP 15 +/* There are sixteen TOPs (timeout periods) that can be set in the watchdog. */ +#define DW_WDT_NUM_TOPS 16 +#define DW_WDT_FIX_TOP(_idx) (1U << (16 + _idx)) #define DW_WDT_DEFAULT_SECONDS 30 +static const u32 dw_wdt_fix_tops[DW_WDT_NUM_TOPS] = { + DW_WDT_FIX_TOP(0), DW_WDT_FIX_TOP(1), DW_WDT_FIX_TOP(2), + DW_WDT_FIX_TOP(3), DW_WDT_FIX_TOP(4), DW_WDT_FIX_TOP(5), + DW_WDT_FIX_TOP(6), DW_WDT_FIX_TOP(7), DW_WDT_FIX_TOP(8), + DW_WDT_FIX_TOP(9), DW_WDT_FIX_TOP(10), DW_WDT_FIX_TOP(11), + DW_WDT_FIX_TOP(12), DW_WDT_FIX_TOP(13), DW_WDT_FIX_TOP(14), + DW_WDT_FIX_TOP(15) +}; + 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 dw_wdt_timeout { + u32 top_val; + unsigned int sec; + unsigned int msec; +}; + struct dw_wdt { void __iomem *regs; struct clk *clk; unsigned long rate; + struct dw_wdt_timeout timeouts[DW_WDT_NUM_TOPS]; struct watchdog_device wdd; struct reset_control *rst; /* Save/restore */ @@ -64,20 +85,66 @@ static inline int dw_wdt_is_enabled(struct dw_wdt *dw_wdt) WDOG_CONTROL_REG_WDT_EN_MASK; } -static inline int dw_wdt_top_in_seconds(struct dw_wdt *dw_wdt, unsigned top) +static unsigned int dw_wdt_find_best_top(struct dw_wdt *dw_wdt, + unsigned int timeout, u32 *top_val) { + int idx; + /* - * There are 16 possible timeout values in 0..15 where the number of - * cycles is 2 ^ (16 + i) and the watchdog counts down. + * Find a TOP with timeout greater or equal to the requested number. + * Note we'll select a TOP with maximum timeout if the requested + * timeout couldn't be reached. */ - return (1U << (16 + top)) / dw_wdt->rate; + for (idx = 0; idx < DW_WDT_NUM_TOPS; ++idx) { + if (dw_wdt->timeouts[idx].sec >= timeout) + break; + } + + if (idx == DW_WDT_NUM_TOPS) + --idx; + + *top_val = dw_wdt->timeouts[idx].top_val; + + return dw_wdt->timeouts[idx].sec; } -static int dw_wdt_get_top(struct dw_wdt *dw_wdt) +static unsigned int dw_wdt_get_min_timeout(struct dw_wdt *dw_wdt) { - int top = readl(dw_wdt->regs + WDOG_TIMEOUT_RANGE_REG_OFFSET) & 0xF; + int idx; + + /* + * We'll find a timeout greater or equal to one second anyway because + * the driver probe would have failed if there was none. + */ + for (idx = 0; idx < DW_WDT_NUM_TOPS; ++idx) { + if (dw_wdt->timeouts[idx].sec) + break; + } - return dw_wdt_top_in_seconds(dw_wdt, top); + return dw_wdt->timeouts[idx].sec; +} + +static unsigned int dw_wdt_get_max_timeout_ms(struct dw_wdt *dw_wdt) +{ + struct dw_wdt_timeout *timeout = &dw_wdt->timeouts[DW_WDT_NUM_TOPS - 1]; + u64 msec; + + msec = (u64)timeout->sec * MSEC_PER_SEC + timeout->msec; + + return msec < UINT_MAX ? msec : UINT_MAX; +} + +static unsigned int dw_wdt_get_timeout(struct dw_wdt *dw_wdt) +{ + int top_val = readl(dw_wdt->regs + WDOG_TIMEOUT_RANGE_REG_OFFSET) & 0xF; + int idx; + + for (idx = 0; idx < DW_WDT_NUM_TOPS; ++idx) { + if (dw_wdt->timeouts[idx].top_val == top_val) + break; + } + + return dw_wdt->timeouts[idx].sec; } static int dw_wdt_ping(struct watchdog_device *wdd) @@ -93,17 +160,10 @@ static int dw_wdt_ping(struct watchdog_device *wdd) static int dw_wdt_set_timeout(struct watchdog_device *wdd, unsigned int top_s) { struct dw_wdt *dw_wdt = to_dw_wdt(wdd); - int i, top_val = DW_WDT_MAX_TOP; + unsigned int timeout; + u32 top_val; - /* - * Iterate over the timeout values until we find the closest match. We - * always look for >=. - */ - for (i = 0; i <= DW_WDT_MAX_TOP; ++i) - if (dw_wdt_top_in_seconds(dw_wdt, i) >= top_s) { - top_val = i; - break; - } + timeout = dw_wdt_find_best_top(dw_wdt, top_s, &top_val); /* * Set the new value in the watchdog. Some versions of dw_wdt @@ -120,7 +180,7 @@ static int dw_wdt_set_timeout(struct watchdog_device *wdd, unsigned int top_s) * 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); + wdd->timeout = timeout; else wdd->timeout = top_s; @@ -238,6 +298,86 @@ static int dw_wdt_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(dw_wdt_pm_ops, dw_wdt_suspend, dw_wdt_resume); +/* + * In case if DW WDT IP core is synthesized with fixed TOP feature disabled the + * TOPs array can be arbitrary ordered with nearly any sixteen uint numbers + * depending on the system engineer imagination. The next method handles the + * passed TOPs array to pre-calculate the effective timeouts and to sort the + * TOP items out in the ascending order with respect to the timeouts. + */ + +static void dw_wdt_handle_tops(struct dw_wdt *dw_wdt, const u32 *tops) +{ + struct dw_wdt_timeout tout, *dst; + int val, tidx; + u64 msec; + + /* + * We walk over the passed TOPs array and calculate corresponding + * timeouts in seconds and milliseconds. The milliseconds granularity + * is needed to distinguish the TOPs with very close timeouts and to + * set the watchdog max heartbeat setting further. + */ + for (val = 0; val < DW_WDT_NUM_TOPS; ++val) { + tout.top_val = val; + tout.sec = tops[val] / dw_wdt->rate; + msec = (u64)tops[val] * MSEC_PER_SEC; + do_div(msec, dw_wdt->rate); + tout.msec = msec - ((u64)tout.sec * MSEC_PER_SEC); + + /* + * Find a suitable place for the current TOP in the timeouts + * array so that the list is remained in the ascending order. + */ + for (tidx = 0; tidx < val; ++tidx) { + dst = &dw_wdt->timeouts[tidx]; + if (tout.sec > dst->sec || (tout.sec == dst->sec && + tout.msec >= dst->msec)) + continue; + else + swap(*dst, tout); + } + + dw_wdt->timeouts[val] = tout; + } +} + +static int dw_wdt_init_timeouts(struct dw_wdt *dw_wdt, struct device *dev) +{ + u32 data, of_tops[DW_WDT_NUM_TOPS]; + const u32 *tops; + int ret; + + /* + * Retrieve custom or fixed counter values depending on the + * WDT_USE_FIX_TOP flag found in the component specific parameters + * #1 register. + */ + data = readl(dw_wdt->regs + WDOG_COMP_PARAMS_1_REG_OFFSET); + if (data & WDOG_COMP_PARAMS_1_USE_FIX_TOP) { + tops = dw_wdt_fix_tops; + } else { + ret = of_property_read_variable_u32_array(dev_of_node(dev), + "snps,watchdog-tops", of_tops, DW_WDT_NUM_TOPS, + DW_WDT_NUM_TOPS); + if (ret < 0) { + dev_warn(dev, "No valid TOPs array specified\n"); + tops = dw_wdt_fix_tops; + } else { + tops = of_tops; + } + } + + /* Convert the specified TOPs into an array of watchdog timeouts. */ + dw_wdt_handle_tops(dw_wdt, tops); + if (!dw_wdt->timeouts[DW_WDT_NUM_TOPS - 1].sec) { + dev_err(dev, "No any valid TOP detected\n"); + return -EINVAL; + } + + return 0; +} + static int dw_wdt_drv_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -275,12 +415,15 @@ static int dw_wdt_drv_probe(struct platform_device *pdev) reset_control_deassert(dw_wdt->rst); + ret = dw_wdt_init_timeouts(dw_wdt, dev); + if (ret) + goto out_disable_clk; + wdd = &dw_wdt->wdd; wdd->info = &dw_wdt_ident; wdd->ops = &dw_wdt_ops; - wdd->min_timeout = 1; - wdd->max_hw_heartbeat_ms = - dw_wdt_top_in_seconds(dw_wdt, DW_WDT_MAX_TOP) * 1000; + wdd->min_timeout = dw_wdt_get_min_timeout(dw_wdt); + wdd->max_hw_heartbeat_ms = dw_wdt_get_max_timeout_ms(dw_wdt); wdd->parent = dev; watchdog_set_drvdata(wdd, dw_wdt); @@ -293,7 +436,7 @@ static int dw_wdt_drv_probe(struct platform_device *pdev) * devicetree. */ if (dw_wdt_is_enabled(dw_wdt)) { - wdd->timeout = dw_wdt_get_top(dw_wdt); + wdd->timeout = dw_wdt_get_timeout(dw_wdt); set_bit(WDOG_HW_RUNNING, &wdd->status); } else { wdd->timeout = DW_WDT_DEFAULT_SECONDS; -- cgit v1.2.3-59-g8ed1b From a16f58bf154cdf589cf72cb1596b6217da5872a1 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Sat, 30 May 2020 10:35:55 +0300 Subject: watchdog: dw_wdt: Support devices with asynch clocks DW Watchdog IP core can be synthesised with asynchronous timer/APB clocks support (WDT_ASYNC_CLK_MODE_ENABLE == 1). In this case separate clock signals are supposed to be used to feed watchdog timer and APB interface of the device. Currently the driver supports the synchronous mode only. Since there is no way to determine which mode was actually activated for device from its registers, we have to rely on the platform device configuration data. If optional "pclk" clock source is supplied, we consider the device working in asynchronous mode, otherwise the driver falls back to the synchronous configuration. Signed-off-by: Serge Semin Reviewed-by: Guenter Roeck Cc: Alexey Malahov Cc: Thomas Bogendoerfer Cc: Arnd Bergmann Cc: Rob Herring Cc: linux-mips@vger.kernel.org Cc: devicetree@vger.kernel.org Link: https://lore.kernel.org/r/20200530073557.22661-6-Sergey.Semin@baikalelectronics.ru Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/dw_wdt.c | 48 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 43 insertions(+), 5 deletions(-) diff --git a/drivers/watchdog/dw_wdt.c b/drivers/watchdog/dw_wdt.c index 693c0d1fd796..efbc36872670 100644 --- a/drivers/watchdog/dw_wdt.c +++ b/drivers/watchdog/dw_wdt.c @@ -68,6 +68,7 @@ struct dw_wdt_timeout { struct dw_wdt { void __iomem *regs; struct clk *clk; + struct clk *pclk; unsigned long rate; struct dw_wdt_timeout timeouts[DW_WDT_NUM_TOPS]; struct watchdog_device wdd; @@ -274,6 +275,7 @@ static int dw_wdt_suspend(struct device *dev) dw_wdt->control = readl(dw_wdt->regs + WDOG_CONTROL_REG_OFFSET); dw_wdt->timeout = readl(dw_wdt->regs + WDOG_TIMEOUT_RANGE_REG_OFFSET); + clk_disable_unprepare(dw_wdt->pclk); clk_disable_unprepare(dw_wdt->clk); return 0; @@ -287,6 +289,12 @@ static int dw_wdt_resume(struct device *dev) if (err) return err; + err = clk_prepare_enable(dw_wdt->pclk); + if (err) { + clk_disable_unprepare(dw_wdt->clk); + return err; + } + writel(dw_wdt->timeout, dw_wdt->regs + WDOG_TIMEOUT_RANGE_REG_OFFSET); writel(dw_wdt->control, dw_wdt->regs + WDOG_CONTROL_REG_OFFSET); @@ -393,9 +401,18 @@ static int dw_wdt_drv_probe(struct platform_device *pdev) if (IS_ERR(dw_wdt->regs)) return PTR_ERR(dw_wdt->regs); - dw_wdt->clk = devm_clk_get(dev, NULL); - if (IS_ERR(dw_wdt->clk)) - return PTR_ERR(dw_wdt->clk); + /* + * Try to request the watchdog dedicated timer clock source. It must + * be supplied if asynchronous mode is enabled. Otherwise fallback + * to the common timer/bus clocks configuration, in which the very + * first found clock supply both timer and APB signals. + */ + dw_wdt->clk = devm_clk_get(dev, "tclk"); + if (IS_ERR(dw_wdt->clk)) { + dw_wdt->clk = devm_clk_get(dev, NULL); + if (IS_ERR(dw_wdt->clk)) + return PTR_ERR(dw_wdt->clk); + } ret = clk_prepare_enable(dw_wdt->clk); if (ret) @@ -407,10 +424,27 @@ static int dw_wdt_drv_probe(struct platform_device *pdev) goto out_disable_clk; } + /* + * Request APB clock if device is configured with async clocks mode. + * In this case both tclk and pclk clocks are supposed to be specified. + * Alas we can't know for sure whether async mode was really activated, + * so the pclk phandle reference is left optional. If it couldn't be + * found we consider the device configured in synchronous clocks mode. + */ + dw_wdt->pclk = devm_clk_get_optional(dev, "pclk"); + if (IS_ERR(dw_wdt->pclk)) { + ret = PTR_ERR(dw_wdt->pclk); + goto out_disable_clk; + } + + ret = clk_prepare_enable(dw_wdt->pclk); + if (ret) + goto out_disable_clk; + dw_wdt->rst = devm_reset_control_get_optional_shared(&pdev->dev, NULL); if (IS_ERR(dw_wdt->rst)) { ret = PTR_ERR(dw_wdt->rst); - goto out_disable_clk; + goto out_disable_pclk; } reset_control_deassert(dw_wdt->rst); @@ -449,10 +483,13 @@ static int dw_wdt_drv_probe(struct platform_device *pdev) ret = watchdog_register_device(wdd); if (ret) - goto out_disable_clk; + goto out_disable_pclk; return 0; +out_disable_pclk: + clk_disable_unprepare(dw_wdt->pclk); + out_disable_clk: clk_disable_unprepare(dw_wdt->clk); return ret; @@ -464,6 +501,7 @@ static int dw_wdt_drv_remove(struct platform_device *pdev) watchdog_unregister_device(&dw_wdt->wdd); reset_control_assert(dw_wdt->rst); + clk_disable_unprepare(dw_wdt->pclk); clk_disable_unprepare(dw_wdt->clk); return 0; -- cgit v1.2.3-59-g8ed1b From 46a1946314bfe7db46e1f28e0ce2b6f701fae50b Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Sat, 30 May 2020 10:35:56 +0300 Subject: watchdog: dw_wdt: Add pre-timeouts support DW Watchdog can rise an interrupt in case if IRQ request mode is enabled and timer reaches the zero value. In this case the IRQ lane is left pending until either the next watchdog kick event (watchdog restart) or until the WDT_EOI register is read or the device/system reset. This interface can be used to implement the pre-timeout functionality optionally provided by the Linux kernel watchdog devices. IRQ mode provides a two stages timeout interface. It means the IRQ is raised when the counter reaches zero, while the system reset occurs only after subsequent timeout if the timer restart is not performed. Due to this peculiarity the pre-timeout value is actually set to the achieved hardware timeout, while the real watchdog timeout is considered to be twice as much of it. This applies a significant limitation on the pre-timeout values, so current implementation supports either zero value, which disables the pre-timeout events, or non-zero values, which imply the pre-timeout to be at least half of the current watchdog timeout. Note that we ask the interrupt controller to detect the rising-edge pre-timeout interrupts to prevent the high-level-IRQs flood, since if the pre-timeout happens, the IRQ lane will be left pending until it's cleared by the timer restart. Signed-off-by: Serge Semin Reviewed-by: Guenter Roeck Cc: Alexey Malahov Cc: Thomas Bogendoerfer Cc: Arnd Bergmann Cc: Rob Herring Cc: linux-mips@vger.kernel.org Cc: devicetree@vger.kernel.org Link: https://lore.kernel.org/r/20200530073557.22661-7-Sergey.Semin@baikalelectronics.ru Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/dw_wdt.c | 138 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 130 insertions(+), 8 deletions(-) diff --git a/drivers/watchdog/dw_wdt.c b/drivers/watchdog/dw_wdt.c index efbc36872670..7a171920dd21 100644 --- a/drivers/watchdog/dw_wdt.c +++ b/drivers/watchdog/dw_wdt.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -36,6 +37,8 @@ #define WDOG_CURRENT_COUNT_REG_OFFSET 0x08 #define WDOG_COUNTER_RESTART_REG_OFFSET 0x0c #define WDOG_COUNTER_RESTART_KICK_VALUE 0x76 +#define WDOG_INTERRUPT_STATUS_REG_OFFSET 0x10 +#define WDOG_INTERRUPT_CLEAR_REG_OFFSET 0x14 #define WDOG_COMP_PARAMS_1_REG_OFFSET 0xf4 #define WDOG_COMP_PARAMS_1_USE_FIX_TOP BIT(6) @@ -59,6 +62,11 @@ module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started " "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); +enum dw_wdt_rmod { + DW_WDT_RMOD_RESET = 1, + DW_WDT_RMOD_IRQ = 2 +}; + struct dw_wdt_timeout { u32 top_val; unsigned int sec; @@ -70,6 +78,7 @@ struct dw_wdt { struct clk *clk; struct clk *pclk; unsigned long rate; + enum dw_wdt_rmod rmod; struct dw_wdt_timeout timeouts[DW_WDT_NUM_TOPS]; struct watchdog_device wdd; struct reset_control *rst; @@ -86,6 +95,20 @@ static inline int dw_wdt_is_enabled(struct dw_wdt *dw_wdt) WDOG_CONTROL_REG_WDT_EN_MASK; } +static void dw_wdt_update_mode(struct dw_wdt *dw_wdt, enum dw_wdt_rmod rmod) +{ + u32 val; + + val = readl(dw_wdt->regs + WDOG_CONTROL_REG_OFFSET); + if (rmod == DW_WDT_RMOD_IRQ) + val |= WDOG_CONTROL_REG_RESP_MODE_MASK; + else + val &= ~WDOG_CONTROL_REG_RESP_MODE_MASK; + writel(val, dw_wdt->regs + WDOG_CONTROL_REG_OFFSET); + + dw_wdt->rmod = rmod; +} + static unsigned int dw_wdt_find_best_top(struct dw_wdt *dw_wdt, unsigned int timeout, u32 *top_val) { @@ -145,7 +168,11 @@ static unsigned int dw_wdt_get_timeout(struct dw_wdt *dw_wdt) break; } - return dw_wdt->timeouts[idx].sec; + /* + * In IRQ mode due to the two stages counter, the actual timeout is + * twice greater than the TOP setting. + */ + return dw_wdt->timeouts[idx].sec * dw_wdt->rmod; } static int dw_wdt_ping(struct watchdog_device *wdd) @@ -164,7 +191,20 @@ static int dw_wdt_set_timeout(struct watchdog_device *wdd, unsigned int top_s) unsigned int timeout; u32 top_val; - timeout = dw_wdt_find_best_top(dw_wdt, top_s, &top_val); + /* + * Note IRQ mode being enabled means having a non-zero pre-timeout + * setup. In this case we try to find a TOP as close to the half of the + * requested timeout as possible since DW Watchdog IRQ mode is designed + * in two stages way - first timeout rises the pre-timeout interrupt, + * second timeout performs the system reset. So basically the effective + * watchdog-caused reset happens after two watchdog TOPs elapsed. + */ + timeout = dw_wdt_find_best_top(dw_wdt, DIV_ROUND_UP(top_s, dw_wdt->rmod), + &top_val); + if (dw_wdt->rmod == DW_WDT_RMOD_IRQ) + wdd->pretimeout = timeout; + else + wdd->pretimeout = 0; /* * Set the new value in the watchdog. Some versions of dw_wdt @@ -175,25 +215,47 @@ 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); + /* Kick new TOP value into the watchdog counter if activated. */ + if (watchdog_active(wdd)) + dw_wdt_ping(wdd); + /* * 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 = timeout; + wdd->timeout = timeout * dw_wdt->rmod; else wdd->timeout = top_s; return 0; } +static int dw_wdt_set_pretimeout(struct watchdog_device *wdd, unsigned int req) +{ + struct dw_wdt *dw_wdt = to_dw_wdt(wdd); + + /* + * We ignore actual value of the timeout passed from user-space + * using it as a flag whether the pretimeout functionality is intended + * to be activated. + */ + dw_wdt_update_mode(dw_wdt, req ? DW_WDT_RMOD_IRQ : DW_WDT_RMOD_RESET); + dw_wdt_set_timeout(wdd, wdd->timeout); + + return 0; +} + static void dw_wdt_arm_system_reset(struct dw_wdt *dw_wdt) { u32 val = readl(dw_wdt->regs + WDOG_CONTROL_REG_OFFSET); - /* Disable interrupt mode; always perform system reset. */ - val &= ~WDOG_CONTROL_REG_RESP_MODE_MASK; + /* Disable/enable interrupt mode depending on the RMOD flag. */ + if (dw_wdt->rmod == DW_WDT_RMOD_IRQ) + val |= WDOG_CONTROL_REG_RESP_MODE_MASK; + else + val &= ~WDOG_CONTROL_REG_RESP_MODE_MASK; /* Enable watchdog. */ val |= WDOG_CONTROL_REG_WDT_EN_MASK; writel(val, dw_wdt->regs + WDOG_CONTROL_REG_OFFSET); @@ -231,6 +293,7 @@ static int dw_wdt_restart(struct watchdog_device *wdd, struct dw_wdt *dw_wdt = to_dw_wdt(wdd); writel(0, dw_wdt->regs + WDOG_TIMEOUT_RANGE_REG_OFFSET); + dw_wdt_update_mode(dw_wdt, DW_WDT_RMOD_RESET); if (dw_wdt_is_enabled(dw_wdt)) writel(WDOG_COUNTER_RESTART_KICK_VALUE, dw_wdt->regs + WDOG_COUNTER_RESTART_REG_OFFSET); @@ -246,9 +309,19 @@ static int dw_wdt_restart(struct watchdog_device *wdd, static unsigned int dw_wdt_get_timeleft(struct watchdog_device *wdd) { struct dw_wdt *dw_wdt = to_dw_wdt(wdd); + unsigned int sec; + u32 val; + + val = readl(dw_wdt->regs + WDOG_CURRENT_COUNT_REG_OFFSET); + sec = val / dw_wdt->rate; - return readl(dw_wdt->regs + WDOG_CURRENT_COUNT_REG_OFFSET) / - dw_wdt->rate; + if (dw_wdt->rmod == DW_WDT_RMOD_IRQ) { + val = readl(dw_wdt->regs + WDOG_INTERRUPT_STATUS_REG_OFFSET); + if (!val) + sec += wdd->pretimeout; + } + + return sec; } static const struct watchdog_info dw_wdt_ident = { @@ -257,16 +330,41 @@ static const struct watchdog_info dw_wdt_ident = { .identity = "Synopsys DesignWare Watchdog", }; +static const struct watchdog_info dw_wdt_pt_ident = { + .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | + WDIOF_PRETIMEOUT | WDIOF_MAGICCLOSE, + .identity = "Synopsys DesignWare Watchdog", +}; + static const struct watchdog_ops dw_wdt_ops = { .owner = THIS_MODULE, .start = dw_wdt_start, .stop = dw_wdt_stop, .ping = dw_wdt_ping, .set_timeout = dw_wdt_set_timeout, + .set_pretimeout = dw_wdt_set_pretimeout, .get_timeleft = dw_wdt_get_timeleft, .restart = dw_wdt_restart, }; +static irqreturn_t dw_wdt_irq(int irq, void *devid) +{ + struct dw_wdt *dw_wdt = devid; + u32 val; + + /* + * We don't clear the IRQ status. It's supposed to be done by the + * following ping operations. + */ + val = readl(dw_wdt->regs + WDOG_INTERRUPT_STATUS_REG_OFFSET); + if (!val) + return IRQ_NONE; + + watchdog_notify_pretimeout(&dw_wdt->wdd); + + return IRQ_HANDLED; +} + #ifdef CONFIG_PM_SLEEP static int dw_wdt_suspend(struct device *dev) { @@ -447,6 +545,31 @@ static int dw_wdt_drv_probe(struct platform_device *pdev) goto out_disable_pclk; } + /* Enable normal reset without pre-timeout by default. */ + dw_wdt_update_mode(dw_wdt, DW_WDT_RMOD_RESET); + + /* + * Pre-timeout IRQ is optional, since some hardware may lack support + * of it. Note we must request rising-edge IRQ, since the lane is left + * pending either until the next watchdog kick event or up to the + * system reset. + */ + ret = platform_get_irq_optional(pdev, 0); + if (ret > 0) { + ret = devm_request_irq(dev, ret, dw_wdt_irq, + IRQF_SHARED | IRQF_TRIGGER_RISING, + pdev->name, dw_wdt); + if (ret) + goto out_disable_pclk; + + dw_wdt->wdd.info = &dw_wdt_pt_ident; + } else { + if (ret == -EPROBE_DEFER) + goto out_disable_pclk; + + dw_wdt->wdd.info = &dw_wdt_ident; + } + reset_control_deassert(dw_wdt->rst); ret = dw_wdt_init_timeouts(dw_wdt, dev); @@ -454,7 +577,6 @@ static int dw_wdt_drv_probe(struct platform_device *pdev) goto out_disable_clk; wdd = &dw_wdt->wdd; - wdd->info = &dw_wdt_ident; wdd->ops = &dw_wdt_ops; wdd->min_timeout = dw_wdt_get_min_timeout(dw_wdt); wdd->max_hw_heartbeat_ms = dw_wdt_get_max_timeout_ms(dw_wdt); -- cgit v1.2.3-59-g8ed1b From 4105f19fd0ce51b571f8112457182dfad142768e Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Sat, 30 May 2020 10:35:57 +0300 Subject: watchdog: dw_wdt: Add DebugFS files For the sake of the easier device-driver debug procedure, we added a DebugFS file with the controller registers state. It's available only if kernel is configured with DebugFS support. Signed-off-by: Serge Semin Reviewed-by: Guenter Roeck Cc: Alexey Malahov Cc: Thomas Bogendoerfer Cc: Arnd Bergmann Cc: Rob Herring Cc: linux-mips@vger.kernel.org Cc: devicetree@vger.kernel.org Link: https://lore.kernel.org/r/20200530073557.22661-8-Sergey.Semin@baikalelectronics.ru Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/dw_wdt.c | 68 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/drivers/watchdog/dw_wdt.c b/drivers/watchdog/dw_wdt.c index 7a171920dd21..4fec2c6a3c01 100644 --- a/drivers/watchdog/dw_wdt.c +++ b/drivers/watchdog/dw_wdt.c @@ -28,6 +28,7 @@ #include #include #include +#include #define WDOG_CONTROL_REG_OFFSET 0x00 #define WDOG_CONTROL_REG_WDT_EN_MASK 0x01 @@ -39,8 +40,14 @@ #define WDOG_COUNTER_RESTART_KICK_VALUE 0x76 #define WDOG_INTERRUPT_STATUS_REG_OFFSET 0x10 #define WDOG_INTERRUPT_CLEAR_REG_OFFSET 0x14 +#define WDOG_COMP_PARAMS_5_REG_OFFSET 0xe4 +#define WDOG_COMP_PARAMS_4_REG_OFFSET 0xe8 +#define WDOG_COMP_PARAMS_3_REG_OFFSET 0xec +#define WDOG_COMP_PARAMS_2_REG_OFFSET 0xf0 #define WDOG_COMP_PARAMS_1_REG_OFFSET 0xf4 #define WDOG_COMP_PARAMS_1_USE_FIX_TOP BIT(6) +#define WDOG_COMP_VERSION_REG_OFFSET 0xf8 +#define WDOG_COMP_TYPE_REG_OFFSET 0xfc /* There are sixteen TOPs (timeout periods) that can be set in the watchdog. */ #define DW_WDT_NUM_TOPS 16 @@ -85,6 +92,10 @@ struct dw_wdt { /* Save/restore */ u32 control; u32 timeout; + +#ifdef CONFIG_DEBUG_FS + struct dentry *dbgfs_dir; +#endif }; #define to_dw_wdt(wdd) container_of(wdd, struct dw_wdt, wdd) @@ -484,6 +495,59 @@ static int dw_wdt_init_timeouts(struct dw_wdt *dw_wdt, struct device *dev) return 0; } +#ifdef CONFIG_DEBUG_FS + +#define DW_WDT_DBGFS_REG(_name, _off) \ +{ \ + .name = _name, \ + .offset = _off \ +} + +static const struct debugfs_reg32 dw_wdt_dbgfs_regs[] = { + DW_WDT_DBGFS_REG("cr", WDOG_CONTROL_REG_OFFSET), + DW_WDT_DBGFS_REG("torr", WDOG_TIMEOUT_RANGE_REG_OFFSET), + DW_WDT_DBGFS_REG("ccvr", WDOG_CURRENT_COUNT_REG_OFFSET), + DW_WDT_DBGFS_REG("crr", WDOG_COUNTER_RESTART_REG_OFFSET), + DW_WDT_DBGFS_REG("stat", WDOG_INTERRUPT_STATUS_REG_OFFSET), + DW_WDT_DBGFS_REG("param5", WDOG_COMP_PARAMS_5_REG_OFFSET), + DW_WDT_DBGFS_REG("param4", WDOG_COMP_PARAMS_4_REG_OFFSET), + DW_WDT_DBGFS_REG("param3", WDOG_COMP_PARAMS_3_REG_OFFSET), + DW_WDT_DBGFS_REG("param2", WDOG_COMP_PARAMS_2_REG_OFFSET), + DW_WDT_DBGFS_REG("param1", WDOG_COMP_PARAMS_1_REG_OFFSET), + DW_WDT_DBGFS_REG("version", WDOG_COMP_VERSION_REG_OFFSET), + DW_WDT_DBGFS_REG("type", WDOG_COMP_TYPE_REG_OFFSET) +}; + +static void dw_wdt_dbgfs_init(struct dw_wdt *dw_wdt) +{ + struct device *dev = dw_wdt->wdd.parent; + struct debugfs_regset32 *regset; + + regset = devm_kzalloc(dev, sizeof(*regset), GFP_KERNEL); + if (!regset) + return; + + regset->regs = dw_wdt_dbgfs_regs; + regset->nregs = ARRAY_SIZE(dw_wdt_dbgfs_regs); + regset->base = dw_wdt->regs; + + dw_wdt->dbgfs_dir = debugfs_create_dir(dev_name(dev), NULL); + + debugfs_create_regset32("registers", 0444, dw_wdt->dbgfs_dir, regset); +} + +static void dw_wdt_dbgfs_clear(struct dw_wdt *dw_wdt) +{ + debugfs_remove_recursive(dw_wdt->dbgfs_dir); +} + +#else /* !CONFIG_DEBUG_FS */ + +static void dw_wdt_dbgfs_init(struct dw_wdt *dw_wdt) {} +static void dw_wdt_dbgfs_clear(struct dw_wdt *dw_wdt) {} + +#endif /* !CONFIG_DEBUG_FS */ + static int dw_wdt_drv_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -607,6 +671,8 @@ static int dw_wdt_drv_probe(struct platform_device *pdev) if (ret) goto out_disable_pclk; + dw_wdt_dbgfs_init(dw_wdt); + return 0; out_disable_pclk: @@ -621,6 +687,8 @@ static int dw_wdt_drv_remove(struct platform_device *pdev) { struct dw_wdt *dw_wdt = platform_get_drvdata(pdev); + dw_wdt_dbgfs_clear(dw_wdt); + watchdog_unregister_device(&dw_wdt->wdd); reset_control_assert(dw_wdt->rst); clk_disable_unprepare(dw_wdt->pclk); -- cgit v1.2.3-59-g8ed1b From 137e9e68dd3805deb9fa43b3eb1a963a2d093746 Mon Sep 17 00:00:00 2001 From: Sai Prakash Ranjan Date: Fri, 26 Jun 2020 00:59:04 +0530 Subject: dt-bindings: watchdog: Convert QCOM watchdog timer bindings to YAML Convert QCOM watchdog timer bindings to DT schema format using json-schema. Signed-off-by: Sai Prakash Ranjan Reviewed-by: Stephen Boyd Reviewed-by: Rob Herring Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/77b47aad9d17cb4ec8359bdbbb30c33d818117e6.1593112534.git.saiprakash.ranjan@codeaurora.org Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- .../devicetree/bindings/watchdog/qcom-wdt.txt | 28 -------------- .../devicetree/bindings/watchdog/qcom-wdt.yaml | 44 ++++++++++++++++++++++ 2 files changed, 44 insertions(+), 28 deletions(-) delete mode 100644 Documentation/devicetree/bindings/watchdog/qcom-wdt.txt create mode 100644 Documentation/devicetree/bindings/watchdog/qcom-wdt.yaml diff --git a/Documentation/devicetree/bindings/watchdog/qcom-wdt.txt b/Documentation/devicetree/bindings/watchdog/qcom-wdt.txt deleted file mode 100644 index 41aeaa2ff0f8..000000000000 --- a/Documentation/devicetree/bindings/watchdog/qcom-wdt.txt +++ /dev/null @@ -1,28 +0,0 @@ -Qualcomm Krait Processor Sub-system (KPSS) Watchdog ---------------------------------------------------- - -Required properties : -- compatible : shall contain only one of the following: - - "qcom,kpss-wdt-msm8960" - "qcom,kpss-wdt-apq8064" - "qcom,kpss-wdt-ipq8064" - "qcom,kpss-wdt-ipq4019" - "qcom,kpss-timer" - "qcom,scss-timer" - "qcom,kpss-wdt" - -- reg : shall contain base register location and length -- clocks : shall contain the input clock - -Optional properties : -- timeout-sec : shall contain the default watchdog timeout in seconds, - if unset, the default timeout is 30 seconds - -Example: - watchdog@208a038 { - compatible = "qcom,kpss-wdt-ipq8064"; - reg = <0x0208a038 0x40>; - clocks = <&sleep_clk>; - timeout-sec = <10>; - }; diff --git a/Documentation/devicetree/bindings/watchdog/qcom-wdt.yaml b/Documentation/devicetree/bindings/watchdog/qcom-wdt.yaml new file mode 100644 index 000000000000..5448cc537a03 --- /dev/null +++ b/Documentation/devicetree/bindings/watchdog/qcom-wdt.yaml @@ -0,0 +1,44 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/watchdog/qcom-wdt.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm Krait Processor Sub-system (KPSS) Watchdog timer + +maintainers: + - Sai Prakash Ranjan + +allOf: + - $ref: watchdog.yaml# + +properties: + compatible: + enum: + - qcom,kpss-timer + - qcom,kpss-wdt + - qcom,kpss-wdt-apq8064 + - qcom,kpss-wdt-ipq4019 + - qcom,kpss-wdt-ipq8064 + - qcom,kpss-wdt-msm8960 + - qcom,scss-timer + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + +required: + - compatible + - reg + - clocks + +examples: + - | + watchdog@208a038 { + compatible = "qcom,kpss-wdt-ipq8064"; + reg = <0x0208a038 0x40>; + clocks = <&sleep_clk>; + timeout-sec = <10>; + }; -- cgit v1.2.3-59-g8ed1b From a4fd26f6187b13cace0f90029fe790799e34e4e2 Mon Sep 17 00:00:00 2001 From: Sai Prakash Ranjan Date: Fri, 26 Jun 2020 00:59:05 +0530 Subject: dt-bindings: watchdog: Add compatible for QCS404, SC7180, SDM845, SM8150 Add missing compatible for watchdog timer on QCS404, SC7180, SDM845 and SM8150 SoCs. Signed-off-by: Sai Prakash Ranjan Reviewed-by: Stephen Boyd Acked-by: Rob Herring Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/09da1ba319dc4a27ef4e4e177e67e68f1cb4f35b.1593112534.git.saiprakash.ranjan@codeaurora.org Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- Documentation/devicetree/bindings/watchdog/qcom-wdt.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/watchdog/qcom-wdt.yaml b/Documentation/devicetree/bindings/watchdog/qcom-wdt.yaml index 5448cc537a03..0709ddf0b6a5 100644 --- a/Documentation/devicetree/bindings/watchdog/qcom-wdt.yaml +++ b/Documentation/devicetree/bindings/watchdog/qcom-wdt.yaml @@ -15,6 +15,10 @@ allOf: properties: compatible: enum: + - qcom,apss-wdt-qcs404 + - qcom,apss-wdt-sc7180 + - qcom,apss-wdt-sdm845 + - qcom,apss-wdt-sm8150 - qcom,kpss-timer - qcom,kpss-wdt - qcom,kpss-wdt-apq8064 -- cgit v1.2.3-59-g8ed1b From d51d3852d1fd28409107274dc1d8077022cc0dc2 Mon Sep 17 00:00:00 2001 From: Ahmad Fatoum Date: Thu, 11 Jun 2020 21:17:42 +0200 Subject: docs: watchdog: codify ident.options as superset of possible status flags The FIXME comment has been in-tree since the very first git commit. The described behavior has been since relied on by some userspace, e.g. the util-linux wdctl command and has been ignored by some kernelspace, like the f71808e_wdt driver. The functionality is useful to have to be able to differentiate between a driver that doesn't support WDIOF_CARDRESET and one that does, but hasn't had a watchdog reset, thus drop the FIXME to encourage drivers adopting this convention. Signed-off-by: Ahmad Fatoum Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20200611191750.28096-2-a.fatoum@pengutronix.de Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- Documentation/watchdog/watchdog-api.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/watchdog/watchdog-api.rst b/Documentation/watchdog/watchdog-api.rst index c6c1e9fa9f73..800dcd7586f2 100644 --- a/Documentation/watchdog/watchdog-api.rst +++ b/Documentation/watchdog/watchdog-api.rst @@ -168,7 +168,7 @@ the fields returned in the ident struct are: the options field can have the following bits set, and describes what kind of information that the GET_STATUS and GET_BOOT_STATUS ioctls can -return. [FIXME -- Is this correct?] +return. ================ ========================= WDIOF_OVERHEAT Reset due to CPU overheat -- cgit v1.2.3-59-g8ed1b From e871e93fb08a619dfc015974a05768ed6880fd82 Mon Sep 17 00:00:00 2001 From: Ahmad Fatoum Date: Thu, 11 Jun 2020 21:17:43 +0200 Subject: watchdog: f71808e_wdt: indicate WDIOF_CARDRESET support in watchdog_info.options The driver supports populating bootstatus with WDIOF_CARDRESET, but so far userspace couldn't portably determine whether absence of this flag meant no watchdog reset or no driver support. Or-in the bit to fix this. Fixes: b97cb21a4634 ("watchdog: f71808e_wdt: Fix WDTMOUT_STS register read") Cc: stable@vger.kernel.org Signed-off-by: Ahmad Fatoum Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20200611191750.28096-3-a.fatoum@pengutronix.de Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/f71808e_wdt.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/watchdog/f71808e_wdt.c b/drivers/watchdog/f71808e_wdt.c index a3c44d75d80e..c8ce80c13403 100644 --- a/drivers/watchdog/f71808e_wdt.c +++ b/drivers/watchdog/f71808e_wdt.c @@ -692,7 +692,8 @@ static int __init watchdog_init(int sioaddr) watchdog.sioaddr = sioaddr; watchdog.ident.options = WDIOC_SETTIMEOUT | WDIOF_MAGICCLOSE - | WDIOF_KEEPALIVEPING; + | WDIOF_KEEPALIVEPING + | WDIOF_CARDRESET; snprintf(watchdog.ident.identity, sizeof(watchdog.ident.identity), "%s watchdog", -- cgit v1.2.3-59-g8ed1b From 802141462d844f2e6a4d63a12260d79b7afc4c34 Mon Sep 17 00:00:00 2001 From: Ahmad Fatoum Date: Thu, 11 Jun 2020 21:17:44 +0200 Subject: watchdog: f71808e_wdt: remove use of wrong watchdog_info option The flags that should be or-ed into the watchdog_info.options by drivers all start with WDIOF_, e.g. WDIOF_SETTIMEOUT, which indicates that the driver's watchdog_ops has a usable set_timeout. WDIOC_SETTIMEOUT was used instead, which expands to 0xc0045706, which equals: WDIOF_FANFAULT | WDIOF_EXTERN1 | WDIOF_PRETIMEOUT | WDIOF_ALARMONLY | WDIOF_MAGICCLOSE | 0xc0045000 These were so far indicated to userspace on WDIOC_GETSUPPORT. As the driver has not yet been migrated to the new watchdog kernel API, the constant can just be dropped without substitute. Fixes: 96cb4eb019ce ("watchdog: f71808e_wdt: new watchdog driver for Fintek F71808E and F71882FG") Cc: stable@vger.kernel.org Signed-off-by: Ahmad Fatoum Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20200611191750.28096-4-a.fatoum@pengutronix.de Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/f71808e_wdt.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/watchdog/f71808e_wdt.c b/drivers/watchdog/f71808e_wdt.c index c8ce80c13403..8e5584c54423 100644 --- a/drivers/watchdog/f71808e_wdt.c +++ b/drivers/watchdog/f71808e_wdt.c @@ -690,8 +690,7 @@ static int __init watchdog_init(int sioaddr) * into the module have been registered yet. */ watchdog.sioaddr = sioaddr; - watchdog.ident.options = WDIOC_SETTIMEOUT - | WDIOF_MAGICCLOSE + watchdog.ident.options = WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING | WDIOF_CARDRESET; -- cgit v1.2.3-59-g8ed1b From 4f39d575844148fbf3081571a1f3b4ae04150958 Mon Sep 17 00:00:00 2001 From: Ahmad Fatoum Date: Thu, 11 Jun 2020 21:17:45 +0200 Subject: watchdog: f71808e_wdt: clear watchdog timeout occurred flag The flag indicating a watchdog timeout having occurred normally persists till Power-On Reset of the Fintek Super I/O chip. The user can clear it by writing a `1' to the bit. The driver doesn't offer a restart method, so regular system reboot might not reset the Super I/O and if the watchdog isn't enabled, we won't touch the register containing the bit on the next boot. In this case all subsequent regular reboots will be wrongly flagged by the driver as being caused by the watchdog. Fix this by having the flag cleared after read. This is also done by other drivers like those for the i6300esb and mpc8xxx_wdt. Fixes: b97cb21a4634 ("watchdog: f71808e_wdt: Fix WDTMOUT_STS register read") Cc: stable@vger.kernel.org Signed-off-by: Ahmad Fatoum Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20200611191750.28096-5-a.fatoum@pengutronix.de Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/f71808e_wdt.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/watchdog/f71808e_wdt.c b/drivers/watchdog/f71808e_wdt.c index 8e5584c54423..26bf366aebc2 100644 --- a/drivers/watchdog/f71808e_wdt.c +++ b/drivers/watchdog/f71808e_wdt.c @@ -706,6 +706,13 @@ static int __init watchdog_init(int sioaddr) 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. + * Write 1 to the bit to clear it to zero. + */ + superio_outb(sioaddr, F71808FG_REG_WDT_CONF, + wdt_conf | BIT(F71808FG_FLAG_WDTMOUT_STS)); + superio_exit(sioaddr); err = watchdog_set_timeout(timeout); -- cgit v1.2.3-59-g8ed1b From 5edc8c687357b40469d90e3dab0b59b72eaabf59 Mon Sep 17 00:00:00 2001 From: Ahmad Fatoum Date: Thu, 11 Jun 2020 21:17:46 +0200 Subject: watchdog: f71808e_wdt: do stricter parameter validation We check the f71862fg_pin module parameter every time a watchdog device for the f71862fg is opened, but the parameter can't change at runtime. If we move the check to the start of init: - We catch userspace passing invalid, but unused, values - We check the condition only once - We simplify the code Do so. Signed-off-by: Ahmad Fatoum Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20200611191750.28096-6-a.fatoum@pengutronix.de Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/f71808e_wdt.c | 37 ++++++++++++------------------------- 1 file changed, 12 insertions(+), 25 deletions(-) diff --git a/drivers/watchdog/f71808e_wdt.c b/drivers/watchdog/f71808e_wdt.c index 26bf366aebc2..9f7823819ed1 100644 --- a/drivers/watchdog/f71808e_wdt.c +++ b/drivers/watchdog/f71808e_wdt.c @@ -306,27 +306,6 @@ exit_unlock: return err; } -static int f71862fg_pin_configure(unsigned short ioaddr) -{ - /* When ioaddr is non-zero the calling function has to take care of - mutex handling and superio preparation! */ - - if (f71862fg_pin == 63) { - if (ioaddr) { - /* SPI must be disabled first to use this pin! */ - superio_clear_bit(ioaddr, SIO_REG_ROM_ADDR_SEL, 6); - superio_set_bit(ioaddr, SIO_REG_MFUNCT3, 4); - } - } else if (f71862fg_pin == 56) { - if (ioaddr) - superio_set_bit(ioaddr, SIO_REG_MFUNCT1, 1); - } else { - pr_err("Invalid argument f71862fg_pin=%d\n", f71862fg_pin); - return -EINVAL; - } - return 0; -} - static int watchdog_start(void) { int err; @@ -352,9 +331,13 @@ static int watchdog_start(void) break; case f71862fg: - err = f71862fg_pin_configure(watchdog.sioaddr); - if (err) - goto exit_superio; + 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); + } else if (f71862fg_pin == 56) { + superio_set_bit(watchdog.sioaddr, SIO_REG_MFUNCT1, 1); + } break; case f71868: @@ -810,7 +793,6 @@ static int __init f71808e_find(int sioaddr) break; case SIO_F71862_ID: watchdog.type = f71862fg; - err = f71862fg_pin_configure(0); /* validate module parameter */ break; case SIO_F71868_ID: watchdog.type = f71868; @@ -859,6 +841,11 @@ static int __init f71808e_init(void) int err = -ENODEV; int i; + if (f71862fg_pin != 63 && f71862fg_pin != 56) { + pr_err("Invalid argument f71862fg_pin=%d\n", f71862fg_pin); + return -EINVAL; + } + for (i = 0; i < ARRAY_SIZE(addrs); i++) { err = f71808e_find(addrs[i]); if (err == 0) -- cgit v1.2.3-59-g8ed1b From bd490f8222510dea748b495a502910119060e869 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Tue, 7 Jul 2020 12:11:21 -0500 Subject: watchdog: Use fallthrough pseudo-keyword Replace the existing /* fall through */ comments and its variants with the new pseudo-keyword macro fallthrough[1]. Also, remove unnecessary fall-through markings when it is the case. [1] https://www.kernel.org/doc/html/latest/process/deprecated.html?highlight=fallthrough#implicit-switch-case-fall-through Signed-off-by: Gustavo A. R. Silva Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20200707171121.GA13472@embeddedor Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/advantechwdt.c | 2 +- drivers/watchdog/alim1535_wdt.c | 2 +- drivers/watchdog/alim7101_wdt.c | 2 +- drivers/watchdog/ar7_wdt.c | 3 +-- drivers/watchdog/ath79_wdt.c | 2 +- drivers/watchdog/eurotechwdt.c | 2 +- drivers/watchdog/f71808e_wdt.c | 4 ++-- drivers/watchdog/gef_wdt.c | 2 +- drivers/watchdog/geodewdt.c | 2 +- drivers/watchdog/ib700wdt.c | 2 +- drivers/watchdog/it8712f_wdt.c | 2 +- drivers/watchdog/ixp4xx_wdt.c | 2 +- drivers/watchdog/m54xx_wdt.c | 2 +- drivers/watchdog/machzwd.c | 2 +- drivers/watchdog/mv64x60_wdt.c | 2 +- drivers/watchdog/nv_tco.c | 2 +- drivers/watchdog/pc87413_wdt.c | 2 +- drivers/watchdog/pcwd.c | 2 +- drivers/watchdog/pcwd_pci.c | 2 +- drivers/watchdog/pcwd_usb.c | 2 +- drivers/watchdog/rc32434_wdt.c | 2 +- drivers/watchdog/riowd.c | 2 +- drivers/watchdog/sa1100_wdt.c | 2 +- drivers/watchdog/sb_wdog.c | 2 +- drivers/watchdog/sbc60xxwdt.c | 2 +- drivers/watchdog/sbc7240_wdt.c | 2 +- drivers/watchdog/sbc_fitpc2_wdt.c | 2 +- drivers/watchdog/sc520_wdt.c | 2 +- drivers/watchdog/sch311x_wdt.c | 2 +- drivers/watchdog/smsc37b787_wdt.c | 2 +- drivers/watchdog/w83877f_wdt.c | 2 +- drivers/watchdog/w83977f_wdt.c | 2 +- drivers/watchdog/wafer5823wdt.c | 2 +- drivers/watchdog/watchdog_dev.c | 2 +- drivers/watchdog/wdt.c | 2 +- drivers/watchdog/wdt285.c | 2 +- drivers/watchdog/wdt977.c | 2 +- drivers/watchdog/wdt_pci.c | 2 +- 38 files changed, 39 insertions(+), 40 deletions(-) diff --git a/drivers/watchdog/advantechwdt.c b/drivers/watchdog/advantechwdt.c index 0e4c18a2aa42..554fe85da50e 100644 --- a/drivers/watchdog/advantechwdt.c +++ b/drivers/watchdog/advantechwdt.c @@ -177,7 +177,7 @@ static long advwdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg) if (advwdt_set_heartbeat(new_timeout)) return -EINVAL; advwdt_ping(); - /* fall through */ + fallthrough; case WDIOC_GETTIMEOUT: return put_user(timeout, p); default: diff --git a/drivers/watchdog/alim1535_wdt.c b/drivers/watchdog/alim1535_wdt.c index 42338c7d4540..bfb9a91ca1df 100644 --- a/drivers/watchdog/alim1535_wdt.c +++ b/drivers/watchdog/alim1535_wdt.c @@ -220,7 +220,7 @@ static long ali_ioctl(struct file *file, unsigned int cmd, unsigned long arg) return -EINVAL; ali_keepalive(); } - /* fall through */ + fallthrough; case WDIOC_GETTIMEOUT: return put_user(timeout, p); default: diff --git a/drivers/watchdog/alim7101_wdt.c b/drivers/watchdog/alim7101_wdt.c index 5af0358f4390..4ff7f5afb7aa 100644 --- a/drivers/watchdog/alim7101_wdt.c +++ b/drivers/watchdog/alim7101_wdt.c @@ -279,7 +279,7 @@ static long fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg) timeout = new_timeout; wdt_keepalive(); } - /* Fall through */ + fallthrough; case WDIOC_GETTIMEOUT: return put_user(timeout, p); default: diff --git a/drivers/watchdog/ar7_wdt.c b/drivers/watchdog/ar7_wdt.c index c087027ffd5d..ff37dc91057d 100644 --- a/drivers/watchdog/ar7_wdt.c +++ b/drivers/watchdog/ar7_wdt.c @@ -235,8 +235,7 @@ static long ar7_wdt_ioctl(struct file *file, ar7_wdt_update_margin(new_margin); ar7_wdt_kick(1); spin_unlock(&wdt_lock); - /* Fall through */ - + fallthrough; case WDIOC_GETTIMEOUT: if (put_user(margin, (int *)arg)) return -EFAULT; diff --git a/drivers/watchdog/ath79_wdt.c b/drivers/watchdog/ath79_wdt.c index d6dff97c280b..0f18f06a21b6 100644 --- a/drivers/watchdog/ath79_wdt.c +++ b/drivers/watchdog/ath79_wdt.c @@ -215,8 +215,8 @@ static long ath79_wdt_ioctl(struct file *file, unsigned int cmd, err = ath79_wdt_set_timeout(t); if (err) break; + fallthrough; - /* fallthrough */ case WDIOC_GETTIMEOUT: err = put_user(timeout, p); break; diff --git a/drivers/watchdog/eurotechwdt.c b/drivers/watchdog/eurotechwdt.c index f5ffa7be066e..2418ebb707bd 100644 --- a/drivers/watchdog/eurotechwdt.c +++ b/drivers/watchdog/eurotechwdt.c @@ -286,7 +286,7 @@ static long eurwdt_ioctl(struct file *file, eurwdt_timeout = time; eurwdt_set_timeout(time); spin_unlock(&eurwdt_lock); - /* fall through */ + fallthrough; case WDIOC_GETTIMEOUT: return put_user(eurwdt_timeout, p); diff --git a/drivers/watchdog/f71808e_wdt.c b/drivers/watchdog/f71808e_wdt.c index 9f7823819ed1..f60beec1bbae 100644 --- a/drivers/watchdog/f71808e_wdt.c +++ b/drivers/watchdog/f71808e_wdt.c @@ -612,7 +612,7 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd, if (new_options & WDIOS_ENABLECARD) return watchdog_start(); - /* fall through */ + fallthrough; case WDIOC_KEEPALIVE: watchdog_keepalive(); @@ -626,7 +626,7 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd, return -EINVAL; watchdog_keepalive(); - /* fall through */ + fallthrough; case WDIOC_GETTIMEOUT: return put_user(watchdog.timeout, uarg.i); diff --git a/drivers/watchdog/gef_wdt.c b/drivers/watchdog/gef_wdt.c index f6541d1b65e3..df5406aa7d25 100644 --- a/drivers/watchdog/gef_wdt.c +++ b/drivers/watchdog/gef_wdt.c @@ -201,7 +201,7 @@ static long gef_wdt_ioctl(struct file *file, unsigned int cmd, if (get_user(timeout, (int __user *)argp)) return -EFAULT; gef_wdt_set_timeout(timeout); - /* Fall through */ + fallthrough; case WDIOC_GETTIMEOUT: if (put_user(gef_wdt_timeout, (int __user *)argp)) diff --git a/drivers/watchdog/geodewdt.c b/drivers/watchdog/geodewdt.c index 9914a4283cb2..83418924e30a 100644 --- a/drivers/watchdog/geodewdt.c +++ b/drivers/watchdog/geodewdt.c @@ -185,7 +185,7 @@ static long geodewdt_ioctl(struct file *file, unsigned int cmd, if (geodewdt_set_heartbeat(interval)) return -EINVAL; - /* Fall through */ + fallthrough; case WDIOC_GETTIMEOUT: return put_user(timeout, p); diff --git a/drivers/watchdog/ib700wdt.c b/drivers/watchdog/ib700wdt.c index 2b65ea9451d1..a0ddedc362fc 100644 --- a/drivers/watchdog/ib700wdt.c +++ b/drivers/watchdog/ib700wdt.c @@ -214,7 +214,7 @@ static long ibwdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg) if (ibwdt_set_heartbeat(new_margin)) return -EINVAL; ibwdt_ping(); - /* fall through */ + fallthrough; case WDIOC_GETTIMEOUT: return put_user(timeout, p); diff --git a/drivers/watchdog/it8712f_wdt.c b/drivers/watchdog/it8712f_wdt.c index 2fed40d14007..9b89d2f09568 100644 --- a/drivers/watchdog/it8712f_wdt.c +++ b/drivers/watchdog/it8712f_wdt.c @@ -303,7 +303,7 @@ static long it8712f_wdt_ioctl(struct file *file, unsigned int cmd, superio_exit(); it8712f_wdt_ping(); - /* Fall through */ + fallthrough; case WDIOC_GETTIMEOUT: if (put_user(margin, p)) return -EFAULT; diff --git a/drivers/watchdog/ixp4xx_wdt.c b/drivers/watchdog/ixp4xx_wdt.c index 09886616fd21..aae29dcfaf11 100644 --- a/drivers/watchdog/ixp4xx_wdt.c +++ b/drivers/watchdog/ixp4xx_wdt.c @@ -136,7 +136,7 @@ static long ixp4xx_wdt_ioctl(struct file *file, unsigned int cmd, heartbeat = time; wdt_enable(); - /* Fall through */ + fallthrough; case WDIOC_GETTIMEOUT: ret = put_user(heartbeat, (int *)arg); diff --git a/drivers/watchdog/m54xx_wdt.c b/drivers/watchdog/m54xx_wdt.c index 60ed6252e5f4..f388a769dbd3 100644 --- a/drivers/watchdog/m54xx_wdt.c +++ b/drivers/watchdog/m54xx_wdt.c @@ -155,7 +155,7 @@ static long m54xx_wdt_ioctl(struct file *file, unsigned int cmd, heartbeat = time; wdt_enable(); - /* Fall through */ + fallthrough; case WDIOC_GETTIMEOUT: ret = put_user(heartbeat, (int *)arg); diff --git a/drivers/watchdog/machzwd.c b/drivers/watchdog/machzwd.c index 80ff94688487..743377c5b173 100644 --- a/drivers/watchdog/machzwd.c +++ b/drivers/watchdog/machzwd.c @@ -171,7 +171,7 @@ static inline void zf_set_timer(unsigned short new, unsigned char n) switch (n) { case WD1: zf_writew(COUNTER_1, new); - /* fall through */ + fallthrough; case WD2: zf_writeb(COUNTER_2, new > 0xff ? 0xff : new); default: diff --git a/drivers/watchdog/mv64x60_wdt.c b/drivers/watchdog/mv64x60_wdt.c index 0bc72dd69b70..894aa63488d3 100644 --- a/drivers/watchdog/mv64x60_wdt.c +++ b/drivers/watchdog/mv64x60_wdt.c @@ -222,7 +222,7 @@ static long mv64x60_wdt_ioctl(struct file *file, if (get_user(timeout, (int __user *)argp)) return -EFAULT; mv64x60_wdt_set_timeout(timeout); - /* Fall through */ + fallthrough; case WDIOC_GETTIMEOUT: if (put_user(mv64x60_wdt_timeout, (int __user *)argp)) diff --git a/drivers/watchdog/nv_tco.c b/drivers/watchdog/nv_tco.c index d7a560e348d5..d15ad8583a59 100644 --- a/drivers/watchdog/nv_tco.c +++ b/drivers/watchdog/nv_tco.c @@ -250,7 +250,7 @@ static long nv_tco_ioctl(struct file *file, unsigned int cmd, if (tco_timer_set_heartbeat(new_heartbeat)) return -EINVAL; tco_timer_keepalive(); - /* Fall through */ + fallthrough; case WDIOC_GETTIMEOUT: return put_user(heartbeat, p); default: diff --git a/drivers/watchdog/pc87413_wdt.c b/drivers/watchdog/pc87413_wdt.c index 73fbfc99083b..2d4504302c9e 100644 --- a/drivers/watchdog/pc87413_wdt.c +++ b/drivers/watchdog/pc87413_wdt.c @@ -433,7 +433,7 @@ static long pc87413_ioctl(struct file *file, unsigned int cmd, return -EINVAL; timeout = new_timeout; pc87413_refresh(); - /* fall through - and return the new timeout... */ + fallthrough; /* and return the new timeout */ case WDIOC_GETTIMEOUT: new_timeout = timeout * 60; return put_user(new_timeout, uarg.i); diff --git a/drivers/watchdog/pcwd.c b/drivers/watchdog/pcwd.c index 7a0587fdc52c..e86fa7f8351d 100644 --- a/drivers/watchdog/pcwd.c +++ b/drivers/watchdog/pcwd.c @@ -651,7 +651,7 @@ static long pcwd_ioctl(struct file *file, unsigned int cmd, unsigned long arg) return -EINVAL; pcwd_keepalive(); - /* Fall through */ + fallthrough; case WDIOC_GETTIMEOUT: return put_user(heartbeat, argp); diff --git a/drivers/watchdog/pcwd_pci.c b/drivers/watchdog/pcwd_pci.c index 81508a42a90c..54d86fcb1837 100644 --- a/drivers/watchdog/pcwd_pci.c +++ b/drivers/watchdog/pcwd_pci.c @@ -542,7 +542,7 @@ static long pcipcwd_ioctl(struct file *file, unsigned int cmd, pcipcwd_keepalive(); } - /* fall through */ + fallthrough; case WDIOC_GETTIMEOUT: return put_user(heartbeat, p); diff --git a/drivers/watchdog/pcwd_usb.c b/drivers/watchdog/pcwd_usb.c index 2f44af1831d0..79efca47cebb 100644 --- a/drivers/watchdog/pcwd_usb.c +++ b/drivers/watchdog/pcwd_usb.c @@ -452,7 +452,7 @@ static long usb_pcwd_ioctl(struct file *file, unsigned int cmd, usb_pcwd_keepalive(usb_pcwd_device); } - /* fall through */ + fallthrough; case WDIOC_GETTIMEOUT: return put_user(heartbeat, p); diff --git a/drivers/watchdog/rc32434_wdt.c b/drivers/watchdog/rc32434_wdt.c index aee3c2efd565..e74802f3a32e 100644 --- a/drivers/watchdog/rc32434_wdt.c +++ b/drivers/watchdog/rc32434_wdt.c @@ -230,7 +230,7 @@ static long rc32434_wdt_ioctl(struct file *file, unsigned int cmd, return -EFAULT; if (rc32434_wdt_set(new_timeout)) return -EINVAL; - /* Fall through */ + fallthrough; case WDIOC_GETTIMEOUT: return copy_to_user(argp, &timeout, sizeof(int)) ? -EFAULT : 0; default: diff --git a/drivers/watchdog/riowd.c b/drivers/watchdog/riowd.c index 1b9a6dc8f982..7008596a575f 100644 --- a/drivers/watchdog/riowd.c +++ b/drivers/watchdog/riowd.c @@ -134,7 +134,7 @@ static long riowd_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) return -EINVAL; riowd_timeout = (new_margin + 59) / 60; riowd_writereg(p, riowd_timeout, WDTO_INDEX); - /* Fall through */ + fallthrough; case WDIOC_GETTIMEOUT: return put_user(riowd_timeout * 60, (int __user *)argp); diff --git a/drivers/watchdog/sa1100_wdt.c b/drivers/watchdog/sa1100_wdt.c index 9b93be00109f..27846c6bdfb0 100644 --- a/drivers/watchdog/sa1100_wdt.c +++ b/drivers/watchdog/sa1100_wdt.c @@ -127,7 +127,7 @@ static long sa1100dog_ioctl(struct file *file, unsigned int cmd, pre_margin = oscr_freq * time; writel_relaxed(readl_relaxed(OSCR) + pre_margin, OSMR3); - /*fall through*/ + fallthrough; case WDIOC_GETTIMEOUT: ret = put_user(pre_margin / oscr_freq, p); diff --git a/drivers/watchdog/sb_wdog.c b/drivers/watchdog/sb_wdog.c index da2dad00d473..504be461f992 100644 --- a/drivers/watchdog/sb_wdog.c +++ b/drivers/watchdog/sb_wdog.c @@ -202,7 +202,7 @@ static long sbwdog_ioctl(struct file *file, unsigned int cmd, timeout = time; sbwdog_set(user_dog, timeout); sbwdog_pet(user_dog); - /* Fall through */ + fallthrough; case WDIOC_GETTIMEOUT: /* diff --git a/drivers/watchdog/sbc60xxwdt.c b/drivers/watchdog/sbc60xxwdt.c index f2cbe6d880a8..a947a63fb44a 100644 --- a/drivers/watchdog/sbc60xxwdt.c +++ b/drivers/watchdog/sbc60xxwdt.c @@ -265,7 +265,7 @@ static long fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg) timeout = new_timeout; wdt_keepalive(); } - /* Fall through */ + fallthrough; case WDIOC_GETTIMEOUT: return put_user(timeout, p); default: diff --git a/drivers/watchdog/sbc7240_wdt.c b/drivers/watchdog/sbc7240_wdt.c index 520b8dd77ed4..d640b26e18a6 100644 --- a/drivers/watchdog/sbc7240_wdt.c +++ b/drivers/watchdog/sbc7240_wdt.c @@ -195,7 +195,7 @@ static long fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg) if (wdt_set_timeout(new_timeout)) return -EINVAL; } - /* Fall through */ + fallthrough; case WDIOC_GETTIMEOUT: return put_user(timeout, (int __user *)arg); default: diff --git a/drivers/watchdog/sbc_fitpc2_wdt.c b/drivers/watchdog/sbc_fitpc2_wdt.c index 1b20b33879c4..04483d6453d6 100644 --- a/drivers/watchdog/sbc_fitpc2_wdt.c +++ b/drivers/watchdog/sbc_fitpc2_wdt.c @@ -154,7 +154,7 @@ static long fitpc2_wdt_ioctl(struct file *file, unsigned int cmd, margin = time; wdt_enable(); - /* Fall through */ + fallthrough; case WDIOC_GETTIMEOUT: ret = put_user(margin, (int *)arg); diff --git a/drivers/watchdog/sc520_wdt.c b/drivers/watchdog/sc520_wdt.c index fbe79bcc9297..e66e6b905964 100644 --- a/drivers/watchdog/sc520_wdt.c +++ b/drivers/watchdog/sc520_wdt.c @@ -321,7 +321,7 @@ static long fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg) wdt_keepalive(); } - /* Fall through */ + fallthrough; case WDIOC_GETTIMEOUT: return put_user(timeout, p); default: diff --git a/drivers/watchdog/sch311x_wdt.c b/drivers/watchdog/sch311x_wdt.c index 83949a385f62..d8b77fe10eba 100644 --- a/drivers/watchdog/sch311x_wdt.c +++ b/drivers/watchdog/sch311x_wdt.c @@ -295,7 +295,7 @@ static long sch311x_wdt_ioctl(struct file *file, unsigned int cmd, if (sch311x_wdt_set_heartbeat(new_timeout)) return -EINVAL; sch311x_wdt_keepalive(); - /* Fall through */ + fallthrough; case WDIOC_GETTIMEOUT: return put_user(timeout, p); default: diff --git a/drivers/watchdog/smsc37b787_wdt.c b/drivers/watchdog/smsc37b787_wdt.c index 43de56acd767..7463df479d11 100644 --- a/drivers/watchdog/smsc37b787_wdt.c +++ b/drivers/watchdog/smsc37b787_wdt.c @@ -474,7 +474,7 @@ static long wb_smsc_wdt_ioctl(struct file *file, return -EINVAL; timeout = new_timeout; wb_smsc_wdt_set_timeout(timeout); - /* fall through - and return the new timeout... */ + fallthrough; /* and return the new timeout */ case WDIOC_GETTIMEOUT: new_timeout = timeout; if (unit == UNIT_MINUTE) diff --git a/drivers/watchdog/w83877f_wdt.c b/drivers/watchdog/w83877f_wdt.c index 6b3b667e6f23..5772cc5d3780 100644 --- a/drivers/watchdog/w83877f_wdt.c +++ b/drivers/watchdog/w83877f_wdt.c @@ -289,7 +289,7 @@ static long fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg) timeout = new_timeout; wdt_keepalive(); } - /* Fall through */ + fallthrough; case WDIOC_GETTIMEOUT: return put_user(timeout, p); default: diff --git a/drivers/watchdog/w83977f_wdt.c b/drivers/watchdog/w83977f_wdt.c index 5212e68c6b01..fd64ae77780a 100644 --- a/drivers/watchdog/w83977f_wdt.c +++ b/drivers/watchdog/w83977f_wdt.c @@ -422,7 +422,7 @@ static long wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg) return -EINVAL; wdt_keepalive(); - /* Fall through */ + fallthrough; case WDIOC_GETTIMEOUT: return put_user(timeout, uarg.i); diff --git a/drivers/watchdog/wafer5823wdt.c b/drivers/watchdog/wafer5823wdt.c index a6925847f76f..a8a1ed215e1e 100644 --- a/drivers/watchdog/wafer5823wdt.c +++ b/drivers/watchdog/wafer5823wdt.c @@ -174,7 +174,7 @@ static long wafwdt_ioctl(struct file *file, unsigned int cmd, timeout = new_timeout; wafwdt_stop(); wafwdt_start(); - /* Fall through */ + fallthrough; case WDIOC_GETTIMEOUT: return put_user(timeout, p); diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c index de02d90a4878..b277eebd377e 100644 --- a/drivers/watchdog/watchdog_dev.c +++ b/drivers/watchdog/watchdog_dev.c @@ -776,7 +776,7 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd, err = watchdog_ping(wdd); if (err < 0) break; - /* fall through */ + fallthrough; case WDIOC_GETTIMEOUT: /* timeout == 0 means that we don't know the timeout */ if (wdd->timeout == 0) { diff --git a/drivers/watchdog/wdt.c b/drivers/watchdog/wdt.c index f9054cb0f8e2..a9e40b5c633e 100644 --- a/drivers/watchdog/wdt.c +++ b/drivers/watchdog/wdt.c @@ -389,7 +389,7 @@ static long wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg) if (wdt_set_heartbeat(new_heartbeat)) return -EINVAL; wdt_ping(); - /* Fall through */ + fallthrough; case WDIOC_GETTIMEOUT: return put_user(heartbeat, p); default: diff --git a/drivers/watchdog/wdt285.c b/drivers/watchdog/wdt285.c index e60993d0767e..110249e5f642 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 through */ + fallthrough; case WDIOC_GETTIMEOUT: ret = put_user(soft_margin, int_arg); break; diff --git a/drivers/watchdog/wdt977.c b/drivers/watchdog/wdt977.c index 066a4fb4d75b..c9b8e863f70f 100644 --- a/drivers/watchdog/wdt977.c +++ b/drivers/watchdog/wdt977.c @@ -398,7 +398,7 @@ static long wdt977_ioctl(struct file *file, unsigned int cmd, return -EINVAL; wdt977_keepalive(); - /* Fall through */ + fallthrough; case WDIOC_GETTIMEOUT: return put_user(timeout, uarg.i); diff --git a/drivers/watchdog/wdt_pci.c b/drivers/watchdog/wdt_pci.c index e528024faa41..c3254ba5ace6 100644 --- a/drivers/watchdog/wdt_pci.c +++ b/drivers/watchdog/wdt_pci.c @@ -426,7 +426,7 @@ static long wdtpci_ioctl(struct file *file, unsigned int cmd, if (wdtpci_set_heartbeat(new_heartbeat)) return -EINVAL; wdtpci_ping(); - /* fall through */ + fallthrough; case WDIOC_GETTIMEOUT: return put_user(heartbeat, p); default: -- cgit v1.2.3-59-g8ed1b From 55a1b87e07fdac9194d8b0c3ded3c4d01e9234ad Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Fri, 17 Jul 2020 11:40:59 -0500 Subject: watchdog: scx200_wdt: Use fallthrough pseudo-keyword Replace the existing /* fall through */ comments and its variants with the new pseudo-keyword macro fallthrough[1]. [1] https://www.kernel.org/doc/html/v5.7-rc7/process/deprecated.html?highlight=fallthrough#implicit-switch-case-fall-through Signed-off-by: Gustavo A. R. Silva Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20200717164059.GA26947@embeddedor Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/scx200_wdt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/watchdog/scx200_wdt.c b/drivers/watchdog/scx200_wdt.c index c94098acb78f..7b5e18323f3f 100644 --- a/drivers/watchdog/scx200_wdt.c +++ b/drivers/watchdog/scx200_wdt.c @@ -186,7 +186,7 @@ static long scx200_wdt_ioctl(struct file *file, unsigned int cmd, margin = new_margin; scx200_wdt_update_margin(); scx200_wdt_ping(); - /* Fall through */ + fallthrough; case WDIOC_GETTIMEOUT: if (put_user(margin, p)) return -EFAULT; -- cgit v1.2.3-59-g8ed1b From ee86a03a5dc64899edb39fe1ce21429fed98d107 Mon Sep 17 00:00:00 2001 From: Timothy Myers Date: Mon, 6 Jul 2020 16:33:31 +0000 Subject: watchdog: booke_wdt: Add common nowayout parameter driver Add the common "nowayout" parameter to booke_wdt to make this behavior selectable at runtime and to make the implementation more consistent with many other watchdog drivers. Signed-off-by: Timothy Myers Reviewed-by: Guenter Roeck [groeck: Dropped version data, cleaned up subject line] Link: https://lore.kernel.org/r/CH2PR19MB359059AA5C8917D8D24633FF9D690@CH2PR19MB3590.namprd19.prod.outlook.com Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/booke_wdt.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/watchdog/booke_wdt.c b/drivers/watchdog/booke_wdt.c index 9d09bbfdef20..7817fb976f9c 100644 --- a/drivers/watchdog/booke_wdt.c +++ b/drivers/watchdog/booke_wdt.c @@ -39,6 +39,11 @@ static bool booke_wdt_enabled; module_param(booke_wdt_enabled, bool, 0); static int booke_wdt_period = CONFIG_BOOKE_WDT_DEFAULT_TIMEOUT; module_param(booke_wdt_period, int, 0); +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) ")"); #ifdef CONFIG_PPC_FSL_BOOK3E @@ -215,7 +220,6 @@ static void __exit booke_wdt_exit(void) static int __init booke_wdt_init(void) { int ret = 0; - bool nowayout = WATCHDOG_NOWAYOUT; pr_info("powerpc book-e watchdog driver loaded\n"); booke_wdt_info.firmware_version = cur_cpu_spec->pvr_value; -- cgit v1.2.3-59-g8ed1b From cb36e29bb0e4b0c33c3d5866a0a4aebace4c99b7 Mon Sep 17 00:00:00 2001 From: Krzysztof Sobota Date: Fri, 17 Jul 2020 12:31:09 +0200 Subject: watchdog: initialize device before misc_register When watchdog device is being registered, it calls misc_register that makes watchdog available for systemd to open. This is a data race scenario, because when device is open it may still have device struct not initialized - this in turn causes a crash. This patch moves device initialization before misc_register call and it solves the problem printed below. ------------[ cut here ]------------ WARNING: CPU: 3 PID: 1 at lib/kobject.c:612 kobject_get+0x50/0x54 kobject: '(null)' ((ptrval)): is not initialized, yet kobject_get() is being called. Modules linked in: k2_reset_status(O) davinci_wdt(+) sfn_platform_hwbcn(O) fsmddg_sfn(O) clk_misc_mmap(O) clk_sw_bcn(O) fsp_reset(O) cma_mod(O) slave_sup_notif(O) fpga_master(O) latency(O+) evnotify(O) enable_arm_pmu(O) xge(O) rio_mport_cdev br_netfilter bridge stp llc nvrd_checksum(O) ipv6 CPU: 3 PID: 1 Comm: systemd Tainted: G O 4.19.113-g2579778-fsm4_k2 #1 Hardware name: Keystone [] (unwind_backtrace) from [] (show_stack+0x18/0x1c) [] (show_stack) from [] (dump_stack+0xb4/0xe8) [] (dump_stack) from [] (__warn+0xfc/0x114) [] (__warn) from [] (warn_slowpath_fmt+0x50/0x74) [] (warn_slowpath_fmt) from [] (kobject_get+0x50/0x54) [] (kobject_get) from [] (get_device+0x1c/0x24) [] (get_device) from [] (watchdog_open+0x90/0xf0) [] (watchdog_open) from [] (misc_open+0x130/0x17c) [] (misc_open) from [] (chrdev_open+0xec/0x1a8) [] (chrdev_open) from [] (do_dentry_open+0x204/0x3cc) [] (do_dentry_open) from [] (path_openat+0x330/0x1148) [] (path_openat) from [] (do_filp_open+0x78/0xec) [] (do_filp_open) from [] (do_sys_open+0x130/0x1f4) [] (do_sys_open) from [] (ret_fast_syscall+0x0/0x28) Exception stack(0xd2ceffa8 to 0xd2cefff0) ffa0: b6f69968 00000000 ffffff9c b6ebd210 000a0001 00000000 ffc0: b6f69968 00000000 00000000 00000142 fffffffd ffffffff 00b65530 bed7bb78 ffe0: 00000142 bed7ba70 b6cc2503 b6cc41d6 ---[ end trace 7b16eb105513974f ]--- ------------[ cut here ]------------ WARNING: CPU: 3 PID: 1 at lib/refcount.c:153 kobject_get+0x24/0x54 refcount_t: increment on 0; use-after-free. Modules linked in: k2_reset_status(O) davinci_wdt(+) sfn_platform_hwbcn(O) fsmddg_sfn(O) clk_misc_mmap(O) clk_sw_bcn(O) fsp_reset(O) cma_mod(O) slave_sup_notif(O) fpga_master(O) latency(O+) evnotify(O) enable_arm_pmu(O) xge(O) rio_mport_cdev br_netfilter bridge stp llc nvrd_checksum(O) ipv6 CPU: 3 PID: 1 Comm: systemd Tainted: G W O 4.19.113-g2579778-fsm4_k2 #1 Hardware name: Keystone [] (unwind_backtrace) from [] (show_stack+0x18/0x1c) [] (show_stack) from [] (dump_stack+0xb4/0xe8) [] (dump_stack) from [] (__warn+0xfc/0x114) [] (__warn) from [] (warn_slowpath_fmt+0x50/0x74) [] (warn_slowpath_fmt) from [] (kobject_get+0x24/0x54) [] (kobject_get) from [] (get_device+0x1c/0x24) [] (get_device) from [] (watchdog_open+0x90/0xf0) [] (watchdog_open) from [] (misc_open+0x130/0x17c) [] (misc_open) from [] (chrdev_open+0xec/0x1a8) [] (chrdev_open) from [] (do_dentry_open+0x204/0x3cc) [] (do_dentry_open) from [] (path_openat+0x330/0x1148) [] (path_openat) from [] (do_filp_open+0x78/0xec) [] (do_filp_open) from [] (do_sys_open+0x130/0x1f4) [] (do_sys_open) from [] (ret_fast_syscall+0x0/0x28) Exception stack(0xd2ceffa8 to 0xd2cefff0) ffa0: b6f69968 00000000 ffffff9c b6ebd210 000a0001 00000000 ffc0: b6f69968 00000000 00000000 00000142 fffffffd ffffffff 00b65530 bed7bb78 ffe0: 00000142 bed7ba70 b6cc2503 b6cc41d6 ---[ end trace 7b16eb1055139750 ]--- Fixes: 72139dfa2464 ("watchdog: Fix the race between the release of watchdog_core_data and cdev") Reviewed-by: Guenter Roeck Reviewed-by: Alexander Sverdlin Signed-off-by: Krzysztof Sobota Link: https://lore.kernel.org/r/20200717103109.14660-1-krzysztof.sobota@nokia.com Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/watchdog_dev.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c index b277eebd377e..b7d750b24e49 100644 --- a/drivers/watchdog/watchdog_dev.c +++ b/drivers/watchdog/watchdog_dev.c @@ -994,6 +994,15 @@ static int watchdog_cdev_register(struct watchdog_device *wdd) if (IS_ERR_OR_NULL(watchdog_kworker)) return -ENODEV; + 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); + kthread_init_work(&wd_data->work, watchdog_ping_work); hrtimer_init(&wd_data->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_HARD); wd_data->timer.function = watchdog_timer_expired; @@ -1014,15 +1023,6 @@ static int watchdog_cdev_register(struct watchdog_device *wdd) } } - 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); -- cgit v1.2.3-59-g8ed1b From d821ab28dfd2fa85a9aed133e9bfd368c156cc04 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Wed, 15 Jul 2020 12:09:06 +0100 Subject: dt-bindings: watchdog: renesas,wdt: Document r8a774e1 support RZ/G2H (a.k.a. R8A774E1) watchdog implementation is compatible with R-Car Gen3, therefore add the relevant documentation. Signed-off-by: Lad Prabhakar Reviewed-by: Geert Uytterhoeven Reviewed-by: Guenter Roeck Acked-by: Rob Herring Link: https://lore.kernel.org/r/1594811350-14066-17-git-send-email-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- Documentation/devicetree/bindings/watchdog/renesas,wdt.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/watchdog/renesas,wdt.yaml b/Documentation/devicetree/bindings/watchdog/renesas,wdt.yaml index 572f4c912fef..6933005b52bd 100644 --- a/Documentation/devicetree/bindings/watchdog/renesas,wdt.yaml +++ b/Documentation/devicetree/bindings/watchdog/renesas,wdt.yaml @@ -41,6 +41,7 @@ properties: - renesas,r8a774a1-wdt # RZ/G2M - renesas,r8a774b1-wdt # RZ/G2N - renesas,r8a774c0-wdt # RZ/G2E + - renesas,r8a774e1-wdt # RZ/G2H - renesas,r8a7795-wdt # R-Car H3 - renesas,r8a7796-wdt # R-Car M3-W - renesas,r8a77961-wdt # R-Car M3-W+ -- cgit v1.2.3-59-g8ed1b From 2ab77a34d070b1611a52b50c7cfae074153d475b Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 13 Jul 2020 22:58:21 +0200 Subject: watchdog: Replace HTTP links with HTTPS ones Rationale: Reduces attack surface on kernel devs opening the links for MITM as HTTPS traffic is much harder to manipulate. Deterministic algorithm: For each file: If not .svg: For each line: If doesn't contain `\bxmlns\b`: For each link, `\bhttp://[^# \t\r\n]*(?:\w|/)`: If neither `\bgnu\.org/license`, nor `\bmozilla\.org/MPL\b`: If both the HTTP and HTTPS versions return 200 OK and serve the same content: Replace HTTP with HTTPS. Signed-off-by: Alexander A. Klimov Reviewed-by: Guenter Roeck Acked-by: Rob Herring Link: https://lore.kernel.org/r/20200713205821.38223-1-grandmaster@al2klimov.de Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- Documentation/devicetree/bindings/watchdog/davinci-wdt.txt | 4 ++-- drivers/watchdog/Kconfig | 2 +- drivers/watchdog/dw_wdt.c | 2 +- drivers/watchdog/nv_tco.c | 2 +- drivers/watchdog/nv_tco.h | 2 +- drivers/watchdog/sp5100_tco.c | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Documentation/devicetree/bindings/watchdog/davinci-wdt.txt b/Documentation/devicetree/bindings/watchdog/davinci-wdt.txt index e60b9a13bdcb..aa10b8ec36e2 100644 --- a/Documentation/devicetree/bindings/watchdog/davinci-wdt.txt +++ b/Documentation/devicetree/bindings/watchdog/davinci-wdt.txt @@ -11,8 +11,8 @@ Optional properties: See clock-bindings.txt Documentation: -Davinci DM646x - http://www.ti.com/lit/ug/spruer5b/spruer5b.pdf -Keystone - http://www.ti.com/lit/ug/sprugv5a/sprugv5a.pdf +Davinci DM646x - https://www.ti.com/lit/ug/spruer5b/spruer5b.pdf +Keystone - https://www.ti.com/lit/ug/sprugv5a/sprugv5a.pdf Examples: diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 4f4687c46d38..ab7aad5a1e69 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -1027,7 +1027,7 @@ config ADVANTECH_WDT If you are configuring a Linux kernel for the Advantech single-board computer, say `Y' here to support its built-in watchdog timer feature. More information can be found at - + config ALIM1535_WDT tristate "ALi M1535 PMU Watchdog Timer" diff --git a/drivers/watchdog/dw_wdt.c b/drivers/watchdog/dw_wdt.c index 4fec2c6a3c01..32d0e1781e63 100644 --- a/drivers/watchdog/dw_wdt.c +++ b/drivers/watchdog/dw_wdt.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright 2010-2011 Picochip Ltd., Jamie Iles - * http://www.picochip.com + * https://www.picochip.com * * This file implements a driver for the Synopsys DesignWare watchdog device * in the many subsystems. The watchdog has 16 different timeout periods diff --git a/drivers/watchdog/nv_tco.c b/drivers/watchdog/nv_tco.c index d15ad8583a59..f6902a337422 100644 --- a/drivers/watchdog/nv_tco.c +++ b/drivers/watchdog/nv_tco.c @@ -7,7 +7,7 @@ * Based off i8xx_tco.c: * (c) Copyright 2000 kernel concepts , All Rights * Reserved. - * http://www.kernelconcepts.de + * https://www.kernelconcepts.de * * TCO timer driver for NV chipsets * based on softdog.c by Alan Cox diff --git a/drivers/watchdog/nv_tco.h b/drivers/watchdog/nv_tco.h index d325e528010f..c65f82588386 100644 --- a/drivers/watchdog/nv_tco.h +++ b/drivers/watchdog/nv_tco.h @@ -9,7 +9,7 @@ * * (c) Copyright 2000 kernel concepts , All Rights * Reserved. - * http://www.kernelconcepts.de + * https://www.kernelconcepts.de * * Neither kernel concepts nor Nils Faerber admit liability nor provide * warranty for any of this software. This material is provided diff --git a/drivers/watchdog/sp5100_tco.c b/drivers/watchdog/sp5100_tco.c index 93bd302ae7c5..85e9664318c9 100644 --- a/drivers/watchdog/sp5100_tco.c +++ b/drivers/watchdog/sp5100_tco.c @@ -7,7 +7,7 @@ * Based on i8xx_tco.c: * (c) Copyright 2000 kernel concepts , All Rights * Reserved. - * http://www.kernelconcepts.de + * https://www.kernelconcepts.de * * See AMD Publication 43009 "AMD SB700/710/750 Register Reference Guide", * AMD Publication 45482 "AMD SB800-Series Southbridges Register -- cgit v1.2.3-59-g8ed1b From f1889c9066722f56428978ce9431a6cacabecb57 Mon Sep 17 00:00:00 2001 From: Xu Wang Date: Mon, 13 Jul 2020 05:53:48 +0000 Subject: watchdog: pcwd_usb: remove needless check before usb_free_coherent() usb_free_coherent() is safe with NULL usb_pcwd->intr_buffer and this check is not required. Signed-off-by: Xu Wang Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20200713055348.21620-1-vulab@iscas.ac.cn Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/pcwd_usb.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/watchdog/pcwd_usb.c b/drivers/watchdog/pcwd_usb.c index 79efca47cebb..41a928eb91ed 100644 --- a/drivers/watchdog/pcwd_usb.c +++ b/drivers/watchdog/pcwd_usb.c @@ -585,9 +585,8 @@ static struct notifier_block usb_pcwd_notifier = { static inline void usb_pcwd_delete(struct usb_pcwd_private *usb_pcwd) { usb_free_urb(usb_pcwd->intr_urb); - if (usb_pcwd->intr_buffer != NULL) - usb_free_coherent(usb_pcwd->udev, usb_pcwd->intr_size, - usb_pcwd->intr_buffer, usb_pcwd->intr_dma); + usb_free_coherent(usb_pcwd->udev, usb_pcwd->intr_size, + usb_pcwd->intr_buffer, usb_pcwd->intr_dma); kfree(usb_pcwd); } -- cgit v1.2.3-59-g8ed1b From 36a8947c6b5423b2fb867a7af87ae40eb12f4123 Mon Sep 17 00:00:00 2001 From: Woody Lin Date: Wed, 8 Jul 2020 16:32:19 +0800 Subject: watchdog: softdog: Add options 'soft_reboot_cmd' and 'soft_active_on_boot' Add module parameters 'soft_reboot_cmd' and 'soft_active_on_boot' for customizing softdog configuration; config reboot command by assigning soft_reboot_cmd, and set soft_active_on_boot to start up softdog timer at module initialization stage. Signed-off-by: Woody Lin Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20200708083218.3157213-1-woodylin@google.com Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/softdog.c | 57 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/drivers/watchdog/softdog.c b/drivers/watchdog/softdog.c index 3e4885c1545e..7a1096265f18 100644 --- a/drivers/watchdog/softdog.c +++ b/drivers/watchdog/softdog.c @@ -20,11 +20,13 @@ #include #include #include +#include #include #include #include #include #include +#include #define TIMER_MARGIN 60 /* Default is 60 seconds */ static unsigned int soft_margin = TIMER_MARGIN; /* in seconds */ @@ -49,11 +51,34 @@ module_param(soft_panic, int, 0); MODULE_PARM_DESC(soft_panic, "Softdog action, set to 1 to panic, 0 to reboot (default=0)"); +static char *soft_reboot_cmd; +module_param(soft_reboot_cmd, charp, 0000); +MODULE_PARM_DESC(soft_reboot_cmd, + "Set reboot command. Emergency reboot takes place if unset"); + +static bool soft_active_on_boot; +module_param(soft_active_on_boot, bool, 0000); +MODULE_PARM_DESC(soft_active_on_boot, + "Set to true to active Softdog on boot (default=false)"); + static struct hrtimer softdog_ticktock; static struct hrtimer softdog_preticktock; +static int reboot_kthread_fn(void *data) +{ + kernel_restart(soft_reboot_cmd); + return -EPERM; /* Should not reach here */ +} + +static void reboot_work_fn(struct work_struct *unused) +{ + kthread_run(reboot_kthread_fn, NULL, "softdog_reboot"); +} + static enum hrtimer_restart softdog_fire(struct hrtimer *timer) { + static bool soft_reboot_fired; + module_put(THIS_MODULE); if (soft_noboot) { pr_crit("Triggered - Reboot ignored\n"); @@ -62,6 +87,33 @@ static enum hrtimer_restart softdog_fire(struct hrtimer *timer) panic("Software Watchdog Timer expired"); } else { pr_crit("Initiating system reboot\n"); + if (!soft_reboot_fired && soft_reboot_cmd != NULL) { + static DECLARE_WORK(reboot_work, reboot_work_fn); + /* + * The 'kernel_restart' is a 'might-sleep' operation. + * Also, executing it in system-wide workqueues blocks + * any driver from using the same workqueue in its + * shutdown callback function. Thus, we should execute + * the 'kernel_restart' in a standalone kernel thread. + * But since starting a kernel thread is also a + * 'might-sleep' operation, so the 'reboot_work' is + * required as a launcher of the kernel thread. + * + * After request the reboot, restart the timer to + * schedule an 'emergency_restart' reboot after + * 'TIMER_MARGIN' seconds. It's because if the softdog + * hangs, it might be because of scheduling issues. And + * if that is the case, both 'schedule_work' and + * 'kernel_restart' may possibly be malfunctional at the + * same time. + */ + soft_reboot_fired = true; + schedule_work(&reboot_work); + hrtimer_add_expires_ns(timer, + (u64)TIMER_MARGIN * NSEC_PER_SEC); + + return HRTIMER_RESTART; + } emergency_restart(); pr_crit("Reboot didn't ?????\n"); } @@ -145,12 +197,17 @@ static int __init softdog_init(void) softdog_preticktock.function = softdog_pretimeout; } + if (soft_active_on_boot) + softdog_ping(&softdog_dev); + ret = watchdog_register_device(&softdog_dev); if (ret) return ret; pr_info("initialized. soft_noboot=%d soft_margin=%d sec soft_panic=%d (nowayout=%d)\n", soft_noboot, softdog_dev.timeout, soft_panic, nowayout); + pr_info(" soft_reboot_cmd=%s soft_active_on_boot=%d\n", + soft_reboot_cmd ?: "", soft_active_on_boot); return 0; } -- cgit v1.2.3-59-g8ed1b From fbbe35dfcf949f4d6cf987648e52a85fc0c2a23a Mon Sep 17 00:00:00 2001 From: Tero Kristo Date: Fri, 17 Jul 2020 16:29:55 +0300 Subject: watchdog: use __watchdog_ping in startup Current watchdog startup functionality does not respect the minimum hw heartbeat setup and the last watchdog ping timeframe when watchdog is already running and userspace process attaches to it. Fix this by using the __watchdog_ping from the startup also. For this code path, we can also let the __watchdog_ping handle the bookkeeping for the worker and last keepalive times. Signed-off-by: Tero Kristo Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20200717132958.14304-2-t-kristo@ti.com Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/watchdog_dev.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c index b7d750b24e49..0ad1c393c00e 100644 --- a/drivers/watchdog/watchdog_dev.c +++ b/drivers/watchdog/watchdog_dev.c @@ -275,15 +275,18 @@ static int watchdog_start(struct watchdog_device *wdd) set_bit(_WDOG_KEEPALIVE, &wd_data->status); started_at = ktime_get(); - if (watchdog_hw_running(wdd) && wdd->ops->ping) - err = wdd->ops->ping(wdd); - else + if (watchdog_hw_running(wdd) && wdd->ops->ping) { + err = __watchdog_ping(wdd); + if (err == 0) + set_bit(WDOG_ACTIVE, &wdd->status); + } else { err = wdd->ops->start(wdd); - if (err == 0) { - set_bit(WDOG_ACTIVE, &wdd->status); - wd_data->last_keepalive = started_at; - wd_data->last_hw_keepalive = started_at; - watchdog_update_worker(wdd); + if (err == 0) { + set_bit(WDOG_ACTIVE, &wdd->status); + wd_data->last_keepalive = started_at; + wd_data->last_hw_keepalive = started_at; + watchdog_update_worker(wdd); + } } return err; -- cgit v1.2.3-59-g8ed1b From cef9572e9af373cefd1adc9e771e89670da5da3c Mon Sep 17 00:00:00 2001 From: Tero Kristo Date: Fri, 17 Jul 2020 16:29:56 +0300 Subject: watchdog: add support for adjusting last known HW keepalive time Certain watchdogs require the watchdog only to be pinged within a specific time window, pinging too early or too late cause the watchdog to fire. In cases where this sort of watchdog has been started before kernel comes up, we must adjust the watchdog keepalive window to match the actually running timer, so add a new driver API for this purpose. Signed-off-by: Tero Kristo Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20200717132958.14304-3-t-kristo@ti.com Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- Documentation/watchdog/watchdog-kernel-api.rst | 12 +++++++++++ drivers/watchdog/watchdog_dev.c | 30 ++++++++++++++++++++++++++ include/linux/watchdog.h | 2 ++ 3 files changed, 44 insertions(+) diff --git a/Documentation/watchdog/watchdog-kernel-api.rst b/Documentation/watchdog/watchdog-kernel-api.rst index 068a55ee0d4a..baf44e986b07 100644 --- a/Documentation/watchdog/watchdog-kernel-api.rst +++ b/Documentation/watchdog/watchdog-kernel-api.rst @@ -336,3 +336,15 @@ an action is taken by a preconfigured pretimeout governor preassigned to the watchdog device. If watchdog pretimeout governor framework is not enabled, watchdog_notify_pretimeout() prints a notification message to the kernel log buffer. + +To set the last known HW keepalive time for a watchdog, the following function +should be used:: + + int watchdog_set_last_hw_keepalive(struct watchdog_device *wdd, + unsigned int last_ping_ms) + +This function must be called immediately after watchdog registration. It +sets the last known hardware heartbeat to have happened last_ping_ms before +current time. Calling this is only needed if the watchdog is already running +when probe is called, and the watchdog can only be pinged after the +min_hw_heartbeat_ms time has passed from the last ping. diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c index 0ad1c393c00e..531e74994b61 100644 --- a/drivers/watchdog/watchdog_dev.c +++ b/drivers/watchdog/watchdog_dev.c @@ -1138,6 +1138,36 @@ void watchdog_dev_unregister(struct watchdog_device *wdd) watchdog_cdev_unregister(wdd); } +/* + * watchdog_set_last_hw_keepalive: set last HW keepalive time for watchdog + * @wdd: watchdog device + * @last_ping_ms: time since last HW heartbeat + * + * Adjusts the last known HW keepalive time for a watchdog timer. + * This is needed if the watchdog is already running when the probe + * function is called, and it can't be pinged immediately. This + * function must be called immediately after watchdog registration, + * and min_hw_heartbeat_ms must be set for this to be useful. + */ +int watchdog_set_last_hw_keepalive(struct watchdog_device *wdd, + unsigned int last_ping_ms) +{ + struct watchdog_core_data *wd_data; + ktime_t now; + + if (!wdd) + return -EINVAL; + + wd_data = wdd->wd_data; + + now = ktime_get(); + + wd_data->last_hw_keepalive = ktime_sub(now, ms_to_ktime(last_ping_ms)); + + return __watchdog_ping(wdd); +} +EXPORT_SYMBOL_GPL(watchdog_set_last_hw_keepalive); + /* * watchdog_dev_init: init dev part of watchdog core * diff --git a/include/linux/watchdog.h b/include/linux/watchdog.h index 1464ce6ffa31..9b19e6bb68b5 100644 --- a/include/linux/watchdog.h +++ b/include/linux/watchdog.h @@ -210,6 +210,8 @@ extern int watchdog_init_timeout(struct watchdog_device *wdd, extern int watchdog_register_device(struct watchdog_device *); extern void watchdog_unregister_device(struct watchdog_device *); +int watchdog_set_last_hw_keepalive(struct watchdog_device *, unsigned int); + /* devres register variant */ int devm_watchdog_register_device(struct device *dev, struct watchdog_device *); -- cgit v1.2.3-59-g8ed1b From 5527483f8f7c8edadb5ad4fe8fcb569f565369ea Mon Sep 17 00:00:00 2001 From: Tero Kristo Date: Fri, 17 Jul 2020 16:29:57 +0300 Subject: watchdog: rti-wdt: attach to running watchdog during probe If the RTI watchdog is running already during probe, the driver must configure itself to match the HW. Window size and timeout is probed from hardware, and the last keepalive ping is adjusted to match it also. Signed-off-by: Tero Kristo Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20200717132958.14304-4-t-kristo@ti.com Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/rti_wdt.c | 112 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 102 insertions(+), 10 deletions(-) diff --git a/drivers/watchdog/rti_wdt.c b/drivers/watchdog/rti_wdt.c index d456dd72d99a..7cbdc178ffe8 100644 --- a/drivers/watchdog/rti_wdt.c +++ b/drivers/watchdog/rti_wdt.c @@ -35,7 +35,11 @@ #define RTIWWDRX_NMI 0xa -#define RTIWWDSIZE_50P 0x50 +#define RTIWWDSIZE_50P 0x50 +#define RTIWWDSIZE_25P 0x500 +#define RTIWWDSIZE_12P5 0x5000 +#define RTIWWDSIZE_6P25 0x50000 +#define RTIWWDSIZE_3P125 0x500000 #define WDENABLE_KEY 0xa98559da @@ -48,7 +52,7 @@ #define DWDST BIT(1) -static int heartbeat; +static int heartbeat = DEFAULT_HEARTBEAT; /* * struct to hold data for each WDT device @@ -79,11 +83,9 @@ static int rti_wdt_start(struct watchdog_device *wdd) * be petted during the open window; not too early or not too late. * The HW configuration options only allow for the open window size * to be 50% or less than that; we obviouly want to configure the open - * window as large as possible so we select the 50% option. To avoid - * any glitches, we accommodate 5% safety margin also, so we setup - * the min_hw_hearbeat at 55% of the timeout period. + * window as large as possible so we select the 50% option. */ - wdd->min_hw_heartbeat_ms = 11 * wdd->timeout * 1000 / 20; + wdd->min_hw_heartbeat_ms = 500 * wdd->timeout; /* Generate NMI when wdt expires */ writel_relaxed(RTIWWDRX_NMI, wdt->base + RTIWWDRXCTRL); @@ -110,7 +112,48 @@ static int rti_wdt_ping(struct watchdog_device *wdd) return 0; } -static unsigned int rti_wdt_get_timeleft(struct watchdog_device *wdd) +static int rti_wdt_setup_hw_hb(struct watchdog_device *wdd, u32 wsize) +{ + /* + * RTI only supports a windowed mode, where the watchdog can only + * be petted during the open window; not too early or not too late. + * The HW configuration options only allow for the open window size + * to be 50% or less than that. + */ + switch (wsize) { + case RTIWWDSIZE_50P: + /* 50% open window => 50% min heartbeat */ + wdd->min_hw_heartbeat_ms = 500 * heartbeat; + break; + + case RTIWWDSIZE_25P: + /* 25% open window => 75% min heartbeat */ + wdd->min_hw_heartbeat_ms = 750 * heartbeat; + break; + + case RTIWWDSIZE_12P5: + /* 12.5% open window => 87.5% min heartbeat */ + wdd->min_hw_heartbeat_ms = 875 * heartbeat; + break; + + case RTIWWDSIZE_6P25: + /* 6.5% open window => 93.5% min heartbeat */ + wdd->min_hw_heartbeat_ms = 935 * heartbeat; + break; + + case RTIWWDSIZE_3P125: + /* 3.125% open window => 96.9% min heartbeat */ + wdd->min_hw_heartbeat_ms = 969 * heartbeat; + break; + + default: + return -EINVAL; + } + + return 0; +} + +static unsigned int rti_wdt_get_timeleft_ms(struct watchdog_device *wdd) { u64 timer_counter; u32 val; @@ -123,11 +166,18 @@ static unsigned int rti_wdt_get_timeleft(struct watchdog_device *wdd) timer_counter = readl_relaxed(wdt->base + RTIDWDCNTR); + timer_counter *= 1000; + do_div(timer_counter, wdt->freq); return timer_counter; } +static unsigned int rti_wdt_get_timeleft(struct watchdog_device *wdd) +{ + return rti_wdt_get_timeleft_ms(wdd) / 1000; +} + static const struct watchdog_info rti_wdt_info = { .options = WDIOF_KEEPALIVEPING, .identity = "K3 RTI Watchdog", @@ -148,6 +198,7 @@ static int rti_wdt_probe(struct platform_device *pdev) struct watchdog_device *wdd; struct rti_wdt_device *wdt; struct clk *clk; + u32 last_ping = 0; wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL); if (!wdt) @@ -169,6 +220,14 @@ static int rti_wdt_probe(struct platform_device *pdev) return -EINVAL; } + /* + * If watchdog is running at 32k clock, it is not accurate. + * Adjust frequency down in this case so that we don't pet + * the watchdog too often. + */ + if (wdt->freq < 32768) + wdt->freq = wdt->freq * 9 / 10; + pm_runtime_enable(dev); ret = pm_runtime_get_sync(dev); if (ret) { @@ -185,11 +244,8 @@ static int rti_wdt_probe(struct platform_device *pdev) wdd->min_timeout = 1; wdd->max_hw_heartbeat_ms = (WDT_PRELOAD_MAX << WDT_PRELOAD_SHIFT) / wdt->freq * 1000; - wdd->timeout = DEFAULT_HEARTBEAT; wdd->parent = dev; - watchdog_init_timeout(wdd, heartbeat, dev); - watchdog_set_drvdata(wdd, wdt); watchdog_set_nowayout(wdd, 1); watchdog_set_restart_priority(wdd, 128); @@ -201,12 +257,48 @@ static int rti_wdt_probe(struct platform_device *pdev) goto err_iomap; } + if (readl(wdt->base + RTIDWDCTRL) == WDENABLE_KEY) { + u32 time_left_ms; + u64 heartbeat_ms; + u32 wsize; + + set_bit(WDOG_HW_RUNNING, &wdd->status); + time_left_ms = rti_wdt_get_timeleft_ms(wdd); + heartbeat_ms = readl(wdt->base + RTIDWDPRLD); + heartbeat_ms <<= WDT_PRELOAD_SHIFT; + heartbeat_ms *= 1000; + do_div(heartbeat_ms, wdt->freq); + if (heartbeat_ms != heartbeat * 1000) + dev_warn(dev, "watchdog already running, ignoring heartbeat config!\n"); + + heartbeat = heartbeat_ms; + heartbeat /= 1000; + + wsize = readl(wdt->base + RTIWWDSIZECTRL); + ret = rti_wdt_setup_hw_hb(wdd, wsize); + if (ret) { + dev_err(dev, "bad window size.\n"); + goto err_iomap; + } + + last_ping = heartbeat_ms - time_left_ms; + if (time_left_ms > heartbeat_ms) { + dev_warn(dev, "time_left > heartbeat? Assuming last ping just before now.\n"); + last_ping = 0; + } + } + + watchdog_init_timeout(wdd, heartbeat, dev); + ret = watchdog_register_device(wdd); if (ret) { dev_err(dev, "cannot register watchdog device\n"); goto err_iomap; } + if (last_ping) + watchdog_set_last_hw_keepalive(wdd, last_ping); + return 0; err_iomap: -- cgit v1.2.3-59-g8ed1b From d5b29c2c5ba2bd5bbdb5b744659984185d17d079 Mon Sep 17 00:00:00 2001 From: Tero Kristo Date: Fri, 17 Jul 2020 16:29:58 +0300 Subject: watchdog: rti-wdt: balance pm runtime enable calls PM runtime should be disabled in the fail path of probe and when the driver is removed. Fixes: 2d63908bdbfb ("watchdog: Add K3 RTI watchdog support") Signed-off-by: Tero Kristo Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20200717132958.14304-5-t-kristo@ti.com Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/rti_wdt.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/watchdog/rti_wdt.c b/drivers/watchdog/rti_wdt.c index 7cbdc178ffe8..705e8f7523e8 100644 --- a/drivers/watchdog/rti_wdt.c +++ b/drivers/watchdog/rti_wdt.c @@ -303,6 +303,7 @@ static int rti_wdt_probe(struct platform_device *pdev) err_iomap: pm_runtime_put_sync(&pdev->dev); + pm_runtime_disable(&pdev->dev); return ret; } @@ -313,6 +314,7 @@ static int rti_wdt_remove(struct platform_device *pdev) watchdog_unregister_device(&wdt->wdd); pm_runtime_put(&pdev->dev); + pm_runtime_disable(&pdev->dev); return 0; } -- cgit v1.2.3-59-g8ed1b