Commit 45860394 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'linux-watchdog-5.9-rc1' of git://www.linux-watchdog.org/linux-watchdog

Pull watchdog updates from Wim Van Sebroeck:

 - f71808e_wdt imporvements

 - dw_wdt improvements

 - mlx-wdt: support new watchdog type with longer timeout period

 - fallthrough pseudo-keyword replacements

 - overall small fixes and improvements

* tag 'linux-watchdog-5.9-rc1' of git://www.linux-watchdog.org/linux-watchdog: (35 commits)
  watchdog: rti-wdt: balance pm runtime enable calls
  watchdog: rti-wdt: attach to running watchdog during probe
  watchdog: add support for adjusting last known HW keepalive time
  watchdog: use __watchdog_ping in startup
  watchdog: softdog: Add options 'soft_reboot_cmd' and 'soft_active_on_boot'
  watchdog: pcwd_usb: remove needless check before usb_free_coherent()
  watchdog: Replace HTTP links with HTTPS ones
  dt-bindings: watchdog: renesas,wdt: Document r8a774e1 support
  watchdog: initialize device before misc_register
  watchdog: booke_wdt: Add common nowayout parameter driver
  watchdog: scx200_wdt: Use fallthrough pseudo-keyword
  watchdog: Use fallthrough pseudo-keyword
  watchdog: f71808e_wdt: do stricter parameter validation
  watchdog: f71808e_wdt: clear watchdog timeout occurred flag
  watchdog: f71808e_wdt: remove use of wrong watchdog_info option
  watchdog: f71808e_wdt: indicate WDIOF_CARDRESET support in watchdog_info.options
  docs: watchdog: codify ident.options as superset of possible status flags
  dt-bindings: watchdog: Add compatible for QCS404, SC7180, SDM845, SM8150
  dt-bindings: watchdog: Convert QCOM watchdog timer bindings to YAML
  watchdog: dw_wdt: Add DebugFS files
  ...
parents 407bc8d8 d5b29c2c
...@@ -11,8 +11,8 @@ Optional properties: ...@@ -11,8 +11,8 @@ Optional properties:
See clock-bindings.txt See clock-bindings.txt
Documentation: Documentation:
Davinci DM646x - http://www.ti.com/lit/ug/spruer5b/spruer5b.pdf Davinci DM646x - https://www.ti.com/lit/ug/spruer5b/spruer5b.pdf
Keystone - http://www.ti.com/lit/ug/sprugv5a/sprugv5a.pdf Keystone - https://www.ti.com/lit/ug/sprugv5a/sprugv5a.pdf
Examples: Examples:
......
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>;
};
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>;
};
# 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 <saiprakash.ranjan@codeaurora.org>
allOf:
- $ref: watchdog.yaml#
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
- 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>;
};
...@@ -41,6 +41,7 @@ properties: ...@@ -41,6 +41,7 @@ properties:
- renesas,r8a774a1-wdt # RZ/G2M - renesas,r8a774a1-wdt # RZ/G2M
- renesas,r8a774b1-wdt # RZ/G2N - renesas,r8a774b1-wdt # RZ/G2N
- renesas,r8a774c0-wdt # RZ/G2E - renesas,r8a774c0-wdt # RZ/G2E
- renesas,r8a774e1-wdt # RZ/G2H
- renesas,r8a7795-wdt # R-Car H3 - renesas,r8a7795-wdt # R-Car H3
- renesas,r8a7796-wdt # R-Car M3-W - renesas,r8a7796-wdt # R-Car M3-W
- renesas,r8a77961-wdt # R-Car M3-W+ - renesas,r8a77961-wdt # R-Car M3-W+
......
# 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 <jamie@jamieiles.com>
properties:
compatible:
const: snps,dw-wdt
reg:
maxItems: 1
interrupts:
description: DW Watchdog pre-timeout interrupt
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
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:
- compatible
- reg
- clocks
examples:
- |
watchdog@ffd02000 {
compatible = "snps,dw-wdt";
reg = <0xffd02000 0x1000>;
interrupts = <0 171 4>;
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>;
};
...
...@@ -24,10 +24,19 @@ Type 2: ...@@ -24,10 +24,19 @@ Type 2:
Maximum timeout is 255 sec. Maximum timeout is 255 sec.
Get time-left is supported. 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 Type 1 HW watchdog implementation exist in old systems and
all new systems have type 2 HW watchdog. all new systems have type 2 HW watchdog.
Two types of HW implementation have also different register map. 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. Mellanox system can have 2 watchdogs: main and auxiliary.
Main and auxiliary watchdog devices can be enabled together Main and auxiliary watchdog devices can be enabled together
on the same system. on the same system.
...@@ -54,3 +63,4 @@ The driver checks during initialization if the previous system reset ...@@ -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. 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. Access to HW registers is performed through a generic regmap interface.
Programmable logic device registers have little-endian order.
...@@ -168,7 +168,7 @@ the fields returned in the ident struct are: ...@@ -168,7 +168,7 @@ the fields returned in the ident struct are:
the options field can have the following bits set, and describes what 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 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 WDIOF_OVERHEAT Reset due to CPU overheat
......
...@@ -336,3 +336,15 @@ an action is taken by a preconfigured pretimeout governor preassigned to ...@@ -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 the watchdog device. If watchdog pretimeout governor framework is not
enabled, watchdog_notify_pretimeout() prints a notification message to enabled, watchdog_notify_pretimeout() prints a notification message to
the kernel log buffer. 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.
...@@ -1496,7 +1496,7 @@ ARM SMC WATCHDOG DRIVER ...@@ -1496,7 +1496,7 @@ ARM SMC WATCHDOG DRIVER
M: Julius Werner <jwerner@chromium.org> M: Julius Werner <jwerner@chromium.org>
R: Evan Benn <evanbenn@chromium.org> R: Evan Benn <evanbenn@chromium.org>
S: Maintained 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 F: drivers/watchdog/arm_smc_wdt.c
ARM SMMU DRIVERS ARM SMMU DRIVERS
......
...@@ -186,7 +186,9 @@ ...@@ -186,7 +186,9 @@
#define MLXPLAT_CPLD_WD_RESET_ACT_MASK GENMASK(7, 1) #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_FAN_ACT_MASK (GENMASK(7, 0) & ~BIT(4))
#define MLXPLAT_CPLD_WD_COUNT_ACT_MASK (GENMASK(7, 0) & ~BIT(7)) #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_WD_DFLT_TIMEOUT 30
#define MLXPLAT_CPLD_WD3_DFLT_TIMEOUT 600
#define MLXPLAT_CPLD_WD_MAX_DEVS 2 #define MLXPLAT_CPLD_WD_MAX_DEVS 2
/* mlxplat_priv - platform private data /* mlxplat_priv - platform private data
...@@ -2084,6 +2086,84 @@ static struct mlxreg_core_platform_data mlxplat_mlxcpld_wd_set_type2[] = { ...@@ -2084,6 +2086,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) static bool mlxplat_mlxcpld_writeable_reg(struct device *dev, unsigned int reg)
{ {
switch (reg) { switch (reg) {
...@@ -2114,8 +2194,10 @@ static bool mlxplat_mlxcpld_writeable_reg(struct device *dev, unsigned int reg) ...@@ -2114,8 +2194,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_TMR_OFFSET:
case MLXPLAT_CPLD_LPC_REG_WD1_ACT_OFFSET: case MLXPLAT_CPLD_LPC_REG_WD1_ACT_OFFSET:
case MLXPLAT_CPLD_LPC_REG_WD2_TMR_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_WD2_ACT_OFFSET:
case MLXPLAT_CPLD_LPC_REG_WD3_TMR_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_WD3_ACT_OFFSET:
case MLXPLAT_CPLD_LPC_REG_PWM1_OFFSET: case MLXPLAT_CPLD_LPC_REG_PWM1_OFFSET:
case MLXPLAT_CPLD_LPC_REG_PWM_CONTROL_OFFSET: case MLXPLAT_CPLD_LPC_REG_PWM_CONTROL_OFFSET:
...@@ -2742,6 +2824,27 @@ static int mlxplat_mlxcpld_verify_bus_topology(int *nr) ...@@ -2742,6 +2824,27 @@ static int mlxplat_mlxcpld_verify_bus_topology(int *nr)
return 0; 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,
&regval);
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) static int __init mlxplat_init(void)
{ {
struct mlxplat_priv *priv; struct mlxplat_priv *priv;
...@@ -2874,6 +2977,9 @@ static int __init mlxplat_init(void) ...@@ -2874,6 +2977,9 @@ static int __init mlxplat_init(void)
} }
/* Add WD drivers. */ /* 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++) { for (j = 0; j < MLXPLAT_CPLD_WD_MAX_DEVS; j++) {
if (mlxplat_wd_data[j]) { if (mlxplat_wd_data[j]) {
mlxplat_wd_data[j]->regmap = priv->regmap; mlxplat_wd_data[j]->regmap = priv->regmap;
......
...@@ -1027,7 +1027,7 @@ config ADVANTECH_WDT ...@@ -1027,7 +1027,7 @@ config ADVANTECH_WDT
If you are configuring a Linux kernel for the Advantech single-board If you are configuring a Linux kernel for the Advantech single-board
computer, say `Y' here to support its built-in watchdog timer computer, say `Y' here to support its built-in watchdog timer
feature. More information can be found at feature. More information can be found at
<http://www.advantech.com.tw/products/> <https://www.advantech.com.tw/products/>
config ALIM1535_WDT config ALIM1535_WDT
tristate "ALi M1535 PMU Watchdog Timer" tristate "ALi M1535 PMU Watchdog Timer"
......
...@@ -177,7 +177,7 @@ static long advwdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ...@@ -177,7 +177,7 @@ static long advwdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
if (advwdt_set_heartbeat(new_timeout)) if (advwdt_set_heartbeat(new_timeout))
return -EINVAL; return -EINVAL;
advwdt_ping(); advwdt_ping();
/* fall through */ fallthrough;
case WDIOC_GETTIMEOUT: case WDIOC_GETTIMEOUT:
return put_user(timeout, p); return put_user(timeout, p);
default: default:
......
...@@ -220,7 +220,7 @@ static long ali_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ...@@ -220,7 +220,7 @@ static long ali_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
return -EINVAL; return -EINVAL;
ali_keepalive(); ali_keepalive();
} }
/* fall through */ fallthrough;
case WDIOC_GETTIMEOUT: case WDIOC_GETTIMEOUT:
return put_user(timeout, p); return put_user(timeout, p);
default: default:
......
...@@ -279,7 +279,7 @@ static long fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ...@@ -279,7 +279,7 @@ static long fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
timeout = new_timeout; timeout = new_timeout;
wdt_keepalive(); wdt_keepalive();
} }
/* Fall through */ fallthrough;
case WDIOC_GETTIMEOUT: case WDIOC_GETTIMEOUT:
return put_user(timeout, p); return put_user(timeout, p);
default: default:
......
...@@ -235,8 +235,7 @@ static long ar7_wdt_ioctl(struct file *file, ...@@ -235,8 +235,7 @@ static long ar7_wdt_ioctl(struct file *file,
ar7_wdt_update_margin(new_margin); ar7_wdt_update_margin(new_margin);
ar7_wdt_kick(1); ar7_wdt_kick(1);
spin_unlock(&wdt_lock); spin_unlock(&wdt_lock);
/* Fall through */ fallthrough;
case WDIOC_GETTIMEOUT: case WDIOC_GETTIMEOUT:
if (put_user(margin, (int *)arg)) if (put_user(margin, (int *)arg))
return -EFAULT; return -EFAULT;
......
...@@ -215,8 +215,8 @@ static long ath79_wdt_ioctl(struct file *file, unsigned int cmd, ...@@ -215,8 +215,8 @@ static long ath79_wdt_ioctl(struct file *file, unsigned int cmd,
err = ath79_wdt_set_timeout(t); err = ath79_wdt_set_timeout(t);
if (err) if (err)
break; break;
fallthrough;
/* fallthrough */
case WDIOC_GETTIMEOUT: case WDIOC_GETTIMEOUT:
err = put_user(timeout, p); err = put_user(timeout, p);
break; break;
......
...@@ -279,7 +279,7 @@ static int bcm_kona_wdt_probe(struct platform_device *pdev) ...@@ -279,7 +279,7 @@ static int bcm_kona_wdt_probe(struct platform_device *pdev)
wdt->base = devm_platform_ioremap_resource(pdev, 0); wdt->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(wdt->base)) if (IS_ERR(wdt->base))
return -ENODEV; return PTR_ERR(wdt->base);
wdt->resolution = SECWDOG_DEFAULT_RESOLUTION; wdt->resolution = SECWDOG_DEFAULT_RESOLUTION;
ret = bcm_kona_wdt_set_resolution_reg(wdt); ret = bcm_kona_wdt_set_resolution_reg(wdt);
......
...@@ -39,6 +39,11 @@ static bool booke_wdt_enabled; ...@@ -39,6 +39,11 @@ static bool booke_wdt_enabled;
module_param(booke_wdt_enabled, bool, 0); module_param(booke_wdt_enabled, bool, 0);
static int booke_wdt_period = CONFIG_BOOKE_WDT_DEFAULT_TIMEOUT; static int booke_wdt_period = CONFIG_BOOKE_WDT_DEFAULT_TIMEOUT;
module_param(booke_wdt_period, int, 0); 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 #ifdef CONFIG_PPC_FSL_BOOK3E
...@@ -215,7 +220,6 @@ static void __exit booke_wdt_exit(void) ...@@ -215,7 +220,6 @@ static void __exit booke_wdt_exit(void)
static int __init booke_wdt_init(void) static int __init booke_wdt_init(void)
{ {
int ret = 0; int ret = 0;
bool nowayout = WATCHDOG_NOWAYOUT;
pr_info("powerpc book-e watchdog driver loaded\n"); pr_info("powerpc book-e watchdog driver loaded\n");
booke_wdt_info.firmware_version = cur_cpu_spec->pvr_value; booke_wdt_info.firmware_version = cur_cpu_spec->pvr_value;
......
This diff is collapsed.
...@@ -286,7 +286,7 @@ static long eurwdt_ioctl(struct file *file, ...@@ -286,7 +286,7 @@ static long eurwdt_ioctl(struct file *file,
eurwdt_timeout = time; eurwdt_timeout = time;
eurwdt_set_timeout(time); eurwdt_set_timeout(time);
spin_unlock(&eurwdt_lock); spin_unlock(&eurwdt_lock);
/* fall through */ fallthrough;
case WDIOC_GETTIMEOUT: case WDIOC_GETTIMEOUT:
return put_user(eurwdt_timeout, p); return put_user(eurwdt_timeout, p);
......
...@@ -306,27 +306,6 @@ static int watchdog_keepalive(void) ...@@ -306,27 +306,6 @@ static int watchdog_keepalive(void)
return err; 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) static int watchdog_start(void)
{ {
int err; int err;
...@@ -352,9 +331,13 @@ static int watchdog_start(void) ...@@ -352,9 +331,13 @@ static int watchdog_start(void)
break; break;
case f71862fg: case f71862fg:
err = f71862fg_pin_configure(watchdog.sioaddr); if (f71862fg_pin == 63) {
if (err) /* SPI must be disabled first to use this pin! */
goto exit_superio; 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; break;
case f71868: case f71868:
...@@ -629,7 +612,7 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd, ...@@ -629,7 +612,7 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd,
if (new_options & WDIOS_ENABLECARD) if (new_options & WDIOS_ENABLECARD)
return watchdog_start(); return watchdog_start();
/* fall through */ fallthrough;
case WDIOC_KEEPALIVE: case WDIOC_KEEPALIVE:
watchdog_keepalive(); watchdog_keepalive();
...@@ -643,7 +626,7 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd, ...@@ -643,7 +626,7 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd,
return -EINVAL; return -EINVAL;
watchdog_keepalive(); watchdog_keepalive();
/* fall through */ fallthrough;
case WDIOC_GETTIMEOUT: case WDIOC_GETTIMEOUT:
return put_user(watchdog.timeout, uarg.i); return put_user(watchdog.timeout, uarg.i);
...@@ -690,9 +673,9 @@ static int __init watchdog_init(int sioaddr) ...@@ -690,9 +673,9 @@ static int __init watchdog_init(int sioaddr)
* into the module have been registered yet. * into the module have been registered yet.
*/ */
watchdog.sioaddr = sioaddr; watchdog.sioaddr = sioaddr;
watchdog.ident.options = WDIOC_SETTIMEOUT watchdog.ident.options = WDIOF_MAGICCLOSE
| WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING
| WDIOF_KEEPALIVEPING; | WDIOF_CARDRESET;
snprintf(watchdog.ident.identity, snprintf(watchdog.ident.identity,
sizeof(watchdog.ident.identity), "%s watchdog", sizeof(watchdog.ident.identity), "%s watchdog",
...@@ -706,6 +689,13 @@ static int __init watchdog_init(int sioaddr) ...@@ -706,6 +689,13 @@ static int __init watchdog_init(int sioaddr)
wdt_conf = superio_inb(sioaddr, F71808FG_REG_WDT_CONF); wdt_conf = superio_inb(sioaddr, F71808FG_REG_WDT_CONF);
watchdog.caused_reboot = wdt_conf & BIT(F71808FG_FLAG_WDTMOUT_STS); 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); superio_exit(sioaddr);
err = watchdog_set_timeout(timeout); err = watchdog_set_timeout(timeout);
...@@ -803,7 +793,6 @@ static int __init f71808e_find(int sioaddr) ...@@ -803,7 +793,6 @@ static int __init f71808e_find(int sioaddr)
break; break;
case SIO_F71862_ID: case SIO_F71862_ID:
watchdog.type = f71862fg; watchdog.type = f71862fg;
err = f71862fg_pin_configure(0); /* validate module parameter */
break; break;
case SIO_F71868_ID: case SIO_F71868_ID:
watchdog.type = f71868; watchdog.type = f71868;
...@@ -852,6 +841,11 @@ static int __init f71808e_init(void) ...@@ -852,6 +841,11 @@ static int __init f71808e_init(void)
int err = -ENODEV; int err = -ENODEV;
int i; 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++) { for (i = 0; i < ARRAY_SIZE(addrs); i++) {
err = f71808e_find(addrs[i]); err = f71808e_find(addrs[i]);
if (err == 0) if (err == 0)
......
...@@ -201,7 +201,7 @@ static long gef_wdt_ioctl(struct file *file, unsigned int cmd, ...@@ -201,7 +201,7 @@ static long gef_wdt_ioctl(struct file *file, unsigned int cmd,
if (get_user(timeout, (int __user *)argp)) if (get_user(timeout, (int __user *)argp))
return -EFAULT; return -EFAULT;
gef_wdt_set_timeout(timeout); gef_wdt_set_timeout(timeout);
/* Fall through */ fallthrough;
case WDIOC_GETTIMEOUT: case WDIOC_GETTIMEOUT:
if (put_user(gef_wdt_timeout, (int __user *)argp)) if (put_user(gef_wdt_timeout, (int __user *)argp))
......
...@@ -185,7 +185,7 @@ static long geodewdt_ioctl(struct file *file, unsigned int cmd, ...@@ -185,7 +185,7 @@ static long geodewdt_ioctl(struct file *file, unsigned int cmd,
if (geodewdt_set_heartbeat(interval)) if (geodewdt_set_heartbeat(interval))
return -EINVAL; return -EINVAL;
/* Fall through */ fallthrough;
case WDIOC_GETTIMEOUT: case WDIOC_GETTIMEOUT:
return put_user(timeout, p); return put_user(timeout, p);
......
...@@ -214,7 +214,7 @@ static long ibwdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ...@@ -214,7 +214,7 @@ static long ibwdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
if (ibwdt_set_heartbeat(new_margin)) if (ibwdt_set_heartbeat(new_margin))
return -EINVAL; return -EINVAL;
ibwdt_ping(); ibwdt_ping();
/* fall through */ fallthrough;
case WDIOC_GETTIMEOUT: case WDIOC_GETTIMEOUT:
return put_user(timeout, p); return put_user(timeout, p);
......
...@@ -303,7 +303,7 @@ static long it8712f_wdt_ioctl(struct file *file, unsigned int cmd, ...@@ -303,7 +303,7 @@ static long it8712f_wdt_ioctl(struct file *file, unsigned int cmd,
superio_exit(); superio_exit();
it8712f_wdt_ping(); it8712f_wdt_ping();
/* Fall through */ fallthrough;
case WDIOC_GETTIMEOUT: case WDIOC_GETTIMEOUT:
if (put_user(margin, p)) if (put_user(margin, p))
return -EFAULT; return -EFAULT;
......
...@@ -136,7 +136,7 @@ static long ixp4xx_wdt_ioctl(struct file *file, unsigned int cmd, ...@@ -136,7 +136,7 @@ static long ixp4xx_wdt_ioctl(struct file *file, unsigned int cmd,
heartbeat = time; heartbeat = time;
wdt_enable(); wdt_enable();
/* Fall through */ fallthrough;
case WDIOC_GETTIMEOUT: case WDIOC_GETTIMEOUT:
ret = put_user(heartbeat, (int *)arg); ret = put_user(heartbeat, (int *)arg);
......
...@@ -155,7 +155,7 @@ static long m54xx_wdt_ioctl(struct file *file, unsigned int cmd, ...@@ -155,7 +155,7 @@ static long m54xx_wdt_ioctl(struct file *file, unsigned int cmd,
heartbeat = time; heartbeat = time;
wdt_enable(); wdt_enable();
/* Fall through */ fallthrough;
case WDIOC_GETTIMEOUT: case WDIOC_GETTIMEOUT:
ret = put_user(heartbeat, (int *)arg); ret = put_user(heartbeat, (int *)arg);
......
...@@ -171,7 +171,7 @@ static inline void zf_set_timer(unsigned short new, unsigned char n) ...@@ -171,7 +171,7 @@ static inline void zf_set_timer(unsigned short new, unsigned char n)
switch (n) { switch (n) {
case WD1: case WD1:
zf_writew(COUNTER_1, new); zf_writew(COUNTER_1, new);
/* fall through */ fallthrough;
case WD2: case WD2:
zf_writeb(COUNTER_2, new > 0xff ? 0xff : new); zf_writeb(COUNTER_2, new > 0xff ? 0xff : new);
default: default:
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#define MLXREG_WDT_CLOCK_SCALE 1000 #define MLXREG_WDT_CLOCK_SCALE 1000
#define MLXREG_WDT_MAX_TIMEOUT_TYPE1 32 #define MLXREG_WDT_MAX_TIMEOUT_TYPE1 32
#define MLXREG_WDT_MAX_TIMEOUT_TYPE2 255 #define MLXREG_WDT_MAX_TIMEOUT_TYPE2 255
#define MLXREG_WDT_MAX_TIMEOUT_TYPE3 65535
#define MLXREG_WDT_MIN_TIMEOUT 1 #define MLXREG_WDT_MIN_TIMEOUT 1
#define MLXREG_WDT_OPTIONS_BASE (WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE | \ #define MLXREG_WDT_OPTIONS_BASE (WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE | \
WDIOF_SETTIMEOUT) WDIOF_SETTIMEOUT)
...@@ -49,6 +50,7 @@ struct mlxreg_wdt { ...@@ -49,6 +50,7 @@ struct mlxreg_wdt {
int tleft_idx; int tleft_idx;
int ping_idx; int ping_idx;
int reset_idx; int reset_idx;
int regmap_val_sz;
enum mlxreg_wdt_type wdt_type; enum mlxreg_wdt_type wdt_type;
}; };
...@@ -111,7 +113,8 @@ static int mlxreg_wdt_set_timeout(struct watchdog_device *wdd, ...@@ -111,7 +113,8 @@ static int mlxreg_wdt_set_timeout(struct watchdog_device *wdd,
u32 regval, set_time, hw_timeout; u32 regval, set_time, hw_timeout;
int rc; 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, &regval); rc = regmap_read(wdt->regmap, reg_data->reg, &regval);
if (rc) if (rc)
return rc; return rc;
...@@ -120,14 +123,32 @@ static int mlxreg_wdt_set_timeout(struct watchdog_device *wdd, ...@@ -120,14 +123,32 @@ static int mlxreg_wdt_set_timeout(struct watchdog_device *wdd,
regval = (regval & reg_data->mask) | hw_timeout; regval = (regval & reg_data->mask) | hw_timeout;
/* Rowndown to actual closest number of sec. */ /* Rowndown to actual closest number of sec. */
set_time = BIT(hw_timeout) / MLXREG_WDT_CLOCK_SCALE; 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; 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; wdd->timeout = set_time;
rc = regmap_write(wdt->regmap, reg_data->reg, regval);
if (!rc) { if (!rc) {
/* /*
* Restart watchdog with new timeout period * Restart watchdog with new timeout period
...@@ -147,10 +168,25 @@ static unsigned int mlxreg_wdt_get_timeleft(struct watchdog_device *wdd) ...@@ -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_wdt *wdt = watchdog_get_drvdata(wdd);
struct mlxreg_core_data *reg_data = &wdt->pdata->data[wdt->tleft_idx]; struct mlxreg_core_data *reg_data = &wdt->pdata->data[wdt->tleft_idx];
u32 regval; u32 regval, msb, lsb;
int rc; int rc;
rc = regmap_read(wdt->regmap, reg_data->reg, &regval); if (wdt->wdt_type == MLX_WDT_TYPE2) {
rc = regmap_read(wdt->regmap, reg_data->reg, &regval);
} 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, &regval);
}
}
/* Return 0 timeleft in case of failure register read. */ /* Return 0 timeleft in case of failure register read. */
return rc == 0 ? regval : 0; return rc == 0 ? regval : 0;
} }
...@@ -212,13 +248,23 @@ static void mlxreg_wdt_config(struct mlxreg_wdt *wdt, ...@@ -212,13 +248,23 @@ static void mlxreg_wdt_config(struct mlxreg_wdt *wdt,
wdt->wdd.info = &mlxreg_wdt_aux_info; wdt->wdd.info = &mlxreg_wdt_aux_info;
wdt->wdt_type = pdata->version; wdt->wdt_type = pdata->version;
if (wdt->wdt_type == MLX_WDT_TYPE2) { switch (wdt->wdt_type) {
wdt->wdd.ops = &mlxreg_wdt_ops_type2; case MLX_WDT_TYPE1:
wdt->wdd.max_timeout = MLXREG_WDT_MAX_TIMEOUT_TYPE2;
} else {
wdt->wdd.ops = &mlxreg_wdt_ops_type1; wdt->wdd.ops = &mlxreg_wdt_ops_type1;
wdt->wdd.max_timeout = MLXREG_WDT_MAX_TIMEOUT_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; wdt->wdd.min_timeout = MLXREG_WDT_MIN_TIMEOUT;
} }
...@@ -249,6 +295,11 @@ static int mlxreg_wdt_probe(struct platform_device *pdev) ...@@ -249,6 +295,11 @@ static int mlxreg_wdt_probe(struct platform_device *pdev)
wdt->wdd.parent = dev; wdt->wdd.parent = dev;
wdt->regmap = pdata->regmap; 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); mlxreg_wdt_config(wdt, pdata);
if ((pdata->features & MLXREG_CORE_WD_FEATURE_NOWAYOUT)) if ((pdata->features & MLXREG_CORE_WD_FEATURE_NOWAYOUT))
......
...@@ -222,7 +222,7 @@ static long mv64x60_wdt_ioctl(struct file *file, ...@@ -222,7 +222,7 @@ static long mv64x60_wdt_ioctl(struct file *file,
if (get_user(timeout, (int __user *)argp)) if (get_user(timeout, (int __user *)argp))
return -EFAULT; return -EFAULT;
mv64x60_wdt_set_timeout(timeout); mv64x60_wdt_set_timeout(timeout);
/* Fall through */ fallthrough;
case WDIOC_GETTIMEOUT: case WDIOC_GETTIMEOUT:
if (put_user(mv64x60_wdt_timeout, (int __user *)argp)) if (put_user(mv64x60_wdt_timeout, (int __user *)argp))
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Based off i8xx_tco.c: * Based off i8xx_tco.c:
* (c) Copyright 2000 kernel concepts <nils@kernelconcepts.de>, All Rights * (c) Copyright 2000 kernel concepts <nils@kernelconcepts.de>, All Rights
* Reserved. * Reserved.
* http://www.kernelconcepts.de * https://www.kernelconcepts.de
* *
* TCO timer driver for NV chipsets * TCO timer driver for NV chipsets
* based on softdog.c by Alan Cox <alan@redhat.com> * based on softdog.c by Alan Cox <alan@redhat.com>
...@@ -250,7 +250,7 @@ static long nv_tco_ioctl(struct file *file, unsigned int cmd, ...@@ -250,7 +250,7 @@ static long nv_tco_ioctl(struct file *file, unsigned int cmd,
if (tco_timer_set_heartbeat(new_heartbeat)) if (tco_timer_set_heartbeat(new_heartbeat))
return -EINVAL; return -EINVAL;
tco_timer_keepalive(); tco_timer_keepalive();
/* Fall through */ fallthrough;
case WDIOC_GETTIMEOUT: case WDIOC_GETTIMEOUT:
return put_user(heartbeat, p); return put_user(heartbeat, p);
default: default:
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* *
* (c) Copyright 2000 kernel concepts <nils@kernelconcepts.de>, All Rights * (c) Copyright 2000 kernel concepts <nils@kernelconcepts.de>, All Rights
* Reserved. * Reserved.
* http://www.kernelconcepts.de * https://www.kernelconcepts.de
* *
* Neither kernel concepts nor Nils Faerber admit liability nor provide * Neither kernel concepts nor Nils Faerber admit liability nor provide
* warranty for any of this software. This material is provided * warranty for any of this software. This material is provided
......
...@@ -433,7 +433,7 @@ static long pc87413_ioctl(struct file *file, unsigned int cmd, ...@@ -433,7 +433,7 @@ static long pc87413_ioctl(struct file *file, unsigned int cmd,
return -EINVAL; return -EINVAL;
timeout = new_timeout; timeout = new_timeout;
pc87413_refresh(); pc87413_refresh();
/* fall through - and return the new timeout... */ fallthrough; /* and return the new timeout */
case WDIOC_GETTIMEOUT: case WDIOC_GETTIMEOUT:
new_timeout = timeout * 60; new_timeout = timeout * 60;
return put_user(new_timeout, uarg.i); return put_user(new_timeout, uarg.i);
......
...@@ -651,7 +651,7 @@ static long pcwd_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ...@@ -651,7 +651,7 @@ static long pcwd_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
return -EINVAL; return -EINVAL;
pcwd_keepalive(); pcwd_keepalive();
/* Fall through */ fallthrough;
case WDIOC_GETTIMEOUT: case WDIOC_GETTIMEOUT:
return put_user(heartbeat, argp); return put_user(heartbeat, argp);
......
...@@ -542,7 +542,7 @@ static long pcipcwd_ioctl(struct file *file, unsigned int cmd, ...@@ -542,7 +542,7 @@ static long pcipcwd_ioctl(struct file *file, unsigned int cmd,
pcipcwd_keepalive(); pcipcwd_keepalive();
} }
/* fall through */ fallthrough;
case WDIOC_GETTIMEOUT: case WDIOC_GETTIMEOUT:
return put_user(heartbeat, p); return put_user(heartbeat, p);
......
...@@ -452,7 +452,7 @@ static long usb_pcwd_ioctl(struct file *file, unsigned int cmd, ...@@ -452,7 +452,7 @@ static long usb_pcwd_ioctl(struct file *file, unsigned int cmd,
usb_pcwd_keepalive(usb_pcwd_device); usb_pcwd_keepalive(usb_pcwd_device);
} }
/* fall through */ fallthrough;
case WDIOC_GETTIMEOUT: case WDIOC_GETTIMEOUT:
return put_user(heartbeat, p); return put_user(heartbeat, p);
...@@ -585,9 +585,8 @@ static struct notifier_block usb_pcwd_notifier = { ...@@ -585,9 +585,8 @@ static struct notifier_block usb_pcwd_notifier = {
static inline void usb_pcwd_delete(struct usb_pcwd_private *usb_pcwd) static inline void usb_pcwd_delete(struct usb_pcwd_private *usb_pcwd)
{ {
usb_free_urb(usb_pcwd->intr_urb); usb_free_urb(usb_pcwd->intr_urb);
if (usb_pcwd->intr_buffer != NULL) usb_free_coherent(usb_pcwd->udev, usb_pcwd->intr_size,
usb_free_coherent(usb_pcwd->udev, usb_pcwd->intr_size, usb_pcwd->intr_buffer, usb_pcwd->intr_dma);
usb_pcwd->intr_buffer, usb_pcwd->intr_dma);
kfree(usb_pcwd); kfree(usb_pcwd);
} }
......
...@@ -230,7 +230,7 @@ static long rc32434_wdt_ioctl(struct file *file, unsigned int cmd, ...@@ -230,7 +230,7 @@ static long rc32434_wdt_ioctl(struct file *file, unsigned int cmd,
return -EFAULT; return -EFAULT;
if (rc32434_wdt_set(new_timeout)) if (rc32434_wdt_set(new_timeout))
return -EINVAL; return -EINVAL;
/* Fall through */ fallthrough;
case WDIOC_GETTIMEOUT: case WDIOC_GETTIMEOUT:
return copy_to_user(argp, &timeout, sizeof(int)) ? -EFAULT : 0; return copy_to_user(argp, &timeout, sizeof(int)) ? -EFAULT : 0;
default: default:
......
...@@ -134,7 +134,7 @@ static long riowd_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) ...@@ -134,7 +134,7 @@ static long riowd_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
return -EINVAL; return -EINVAL;
riowd_timeout = (new_margin + 59) / 60; riowd_timeout = (new_margin + 59) / 60;
riowd_writereg(p, riowd_timeout, WDTO_INDEX); riowd_writereg(p, riowd_timeout, WDTO_INDEX);
/* Fall through */ fallthrough;
case WDIOC_GETTIMEOUT: case WDIOC_GETTIMEOUT:
return put_user(riowd_timeout * 60, (int __user *)argp); return put_user(riowd_timeout * 60, (int __user *)argp);
......
...@@ -35,7 +35,11 @@ ...@@ -35,7 +35,11 @@
#define RTIWWDRX_NMI 0xa #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 #define WDENABLE_KEY 0xa98559da
...@@ -48,7 +52,7 @@ ...@@ -48,7 +52,7 @@
#define DWDST BIT(1) #define DWDST BIT(1)
static int heartbeat; static int heartbeat = DEFAULT_HEARTBEAT;
/* /*
* struct to hold data for each WDT device * struct to hold data for each WDT device
...@@ -79,11 +83,9 @@ static int rti_wdt_start(struct watchdog_device *wdd) ...@@ -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. * be petted during the open window; not too early or not too late.
* The HW configuration options only allow for the open window size * 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 * 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 * window as large as possible so we select the 50% option.
* any glitches, we accommodate 5% safety margin also, so we setup
* the min_hw_hearbeat at 55% of the timeout period.
*/ */
wdd->min_hw_heartbeat_ms = 11 * wdd->timeout * 1000 / 20; wdd->min_hw_heartbeat_ms = 500 * wdd->timeout;
/* Generate NMI when wdt expires */ /* Generate NMI when wdt expires */
writel_relaxed(RTIWWDRX_NMI, wdt->base + RTIWWDRXCTRL); writel_relaxed(RTIWWDRX_NMI, wdt->base + RTIWWDRXCTRL);
...@@ -110,7 +112,48 @@ static int rti_wdt_ping(struct watchdog_device *wdd) ...@@ -110,7 +112,48 @@ static int rti_wdt_ping(struct watchdog_device *wdd)
return 0; 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; u64 timer_counter;
u32 val; u32 val;
...@@ -123,11 +166,18 @@ static unsigned int rti_wdt_get_timeleft(struct watchdog_device *wdd) ...@@ -123,11 +166,18 @@ static unsigned int rti_wdt_get_timeleft(struct watchdog_device *wdd)
timer_counter = readl_relaxed(wdt->base + RTIDWDCNTR); timer_counter = readl_relaxed(wdt->base + RTIDWDCNTR);
timer_counter *= 1000;
do_div(timer_counter, wdt->freq); do_div(timer_counter, wdt->freq);
return timer_counter; 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 = { static const struct watchdog_info rti_wdt_info = {
.options = WDIOF_KEEPALIVEPING, .options = WDIOF_KEEPALIVEPING,
.identity = "K3 RTI Watchdog", .identity = "K3 RTI Watchdog",
...@@ -148,6 +198,7 @@ static int rti_wdt_probe(struct platform_device *pdev) ...@@ -148,6 +198,7 @@ static int rti_wdt_probe(struct platform_device *pdev)
struct watchdog_device *wdd; struct watchdog_device *wdd;
struct rti_wdt_device *wdt; struct rti_wdt_device *wdt;
struct clk *clk; struct clk *clk;
u32 last_ping = 0;
wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL); wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
if (!wdt) if (!wdt)
...@@ -169,6 +220,14 @@ static int rti_wdt_probe(struct platform_device *pdev) ...@@ -169,6 +220,14 @@ static int rti_wdt_probe(struct platform_device *pdev)
return -EINVAL; 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); pm_runtime_enable(dev);
ret = pm_runtime_get_sync(dev); ret = pm_runtime_get_sync(dev);
if (ret) { if (ret) {
...@@ -185,11 +244,8 @@ static int rti_wdt_probe(struct platform_device *pdev) ...@@ -185,11 +244,8 @@ static int rti_wdt_probe(struct platform_device *pdev)
wdd->min_timeout = 1; wdd->min_timeout = 1;
wdd->max_hw_heartbeat_ms = (WDT_PRELOAD_MAX << WDT_PRELOAD_SHIFT) / wdd->max_hw_heartbeat_ms = (WDT_PRELOAD_MAX << WDT_PRELOAD_SHIFT) /
wdt->freq * 1000; wdt->freq * 1000;
wdd->timeout = DEFAULT_HEARTBEAT;
wdd->parent = dev; wdd->parent = dev;
watchdog_init_timeout(wdd, heartbeat, dev);
watchdog_set_drvdata(wdd, wdt); watchdog_set_drvdata(wdd, wdt);
watchdog_set_nowayout(wdd, 1); watchdog_set_nowayout(wdd, 1);
watchdog_set_restart_priority(wdd, 128); watchdog_set_restart_priority(wdd, 128);
...@@ -201,16 +257,53 @@ static int rti_wdt_probe(struct platform_device *pdev) ...@@ -201,16 +257,53 @@ static int rti_wdt_probe(struct platform_device *pdev)
goto err_iomap; 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); ret = watchdog_register_device(wdd);
if (ret) { if (ret) {
dev_err(dev, "cannot register watchdog device\n"); dev_err(dev, "cannot register watchdog device\n");
goto err_iomap; goto err_iomap;
} }
if (last_ping)
watchdog_set_last_hw_keepalive(wdd, last_ping);
return 0; return 0;
err_iomap: err_iomap:
pm_runtime_put_sync(&pdev->dev); pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
return ret; return ret;
} }
...@@ -221,6 +314,7 @@ static int rti_wdt_remove(struct platform_device *pdev) ...@@ -221,6 +314,7 @@ static int rti_wdt_remove(struct platform_device *pdev)
watchdog_unregister_device(&wdt->wdd); watchdog_unregister_device(&wdt->wdd);
pm_runtime_put(&pdev->dev); pm_runtime_put(&pdev->dev);
pm_runtime_disable(&pdev->dev);
return 0; return 0;
} }
......
...@@ -127,7 +127,7 @@ static long sa1100dog_ioctl(struct file *file, unsigned int cmd, ...@@ -127,7 +127,7 @@ static long sa1100dog_ioctl(struct file *file, unsigned int cmd,
pre_margin = oscr_freq * time; pre_margin = oscr_freq * time;
writel_relaxed(readl_relaxed(OSCR) + pre_margin, OSMR3); writel_relaxed(readl_relaxed(OSCR) + pre_margin, OSMR3);
/*fall through*/ fallthrough;
case WDIOC_GETTIMEOUT: case WDIOC_GETTIMEOUT:
ret = put_user(pre_margin / oscr_freq, p); ret = put_user(pre_margin / oscr_freq, p);
......
...@@ -202,7 +202,7 @@ static long sbwdog_ioctl(struct file *file, unsigned int cmd, ...@@ -202,7 +202,7 @@ static long sbwdog_ioctl(struct file *file, unsigned int cmd,
timeout = time; timeout = time;
sbwdog_set(user_dog, timeout); sbwdog_set(user_dog, timeout);
sbwdog_pet(user_dog); sbwdog_pet(user_dog);
/* Fall through */ fallthrough;
case WDIOC_GETTIMEOUT: case WDIOC_GETTIMEOUT:
/* /*
......
...@@ -265,7 +265,7 @@ static long fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ...@@ -265,7 +265,7 @@ static long fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
timeout = new_timeout; timeout = new_timeout;
wdt_keepalive(); wdt_keepalive();
} }
/* Fall through */ fallthrough;
case WDIOC_GETTIMEOUT: case WDIOC_GETTIMEOUT:
return put_user(timeout, p); return put_user(timeout, p);
default: default:
......
...@@ -195,7 +195,7 @@ static long fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ...@@ -195,7 +195,7 @@ static long fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
if (wdt_set_timeout(new_timeout)) if (wdt_set_timeout(new_timeout))
return -EINVAL; return -EINVAL;
} }
/* Fall through */ fallthrough;
case WDIOC_GETTIMEOUT: case WDIOC_GETTIMEOUT:
return put_user(timeout, (int __user *)arg); return put_user(timeout, (int __user *)arg);
default: default:
......
...@@ -154,7 +154,7 @@ static long fitpc2_wdt_ioctl(struct file *file, unsigned int cmd, ...@@ -154,7 +154,7 @@ static long fitpc2_wdt_ioctl(struct file *file, unsigned int cmd,
margin = time; margin = time;
wdt_enable(); wdt_enable();
/* Fall through */ fallthrough;
case WDIOC_GETTIMEOUT: case WDIOC_GETTIMEOUT:
ret = put_user(margin, (int *)arg); ret = put_user(margin, (int *)arg);
......
...@@ -321,7 +321,7 @@ static long fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ...@@ -321,7 +321,7 @@ static long fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
wdt_keepalive(); wdt_keepalive();
} }
/* Fall through */ fallthrough;
case WDIOC_GETTIMEOUT: case WDIOC_GETTIMEOUT:
return put_user(timeout, p); return put_user(timeout, p);
default: default:
......
...@@ -295,7 +295,7 @@ static long sch311x_wdt_ioctl(struct file *file, unsigned int cmd, ...@@ -295,7 +295,7 @@ static long sch311x_wdt_ioctl(struct file *file, unsigned int cmd,
if (sch311x_wdt_set_heartbeat(new_timeout)) if (sch311x_wdt_set_heartbeat(new_timeout))
return -EINVAL; return -EINVAL;
sch311x_wdt_keepalive(); sch311x_wdt_keepalive();
/* Fall through */ fallthrough;
case WDIOC_GETTIMEOUT: case WDIOC_GETTIMEOUT:
return put_user(timeout, p); return put_user(timeout, p);
default: default:
......
...@@ -186,7 +186,7 @@ static long scx200_wdt_ioctl(struct file *file, unsigned int cmd, ...@@ -186,7 +186,7 @@ static long scx200_wdt_ioctl(struct file *file, unsigned int cmd,
margin = new_margin; margin = new_margin;
scx200_wdt_update_margin(); scx200_wdt_update_margin();
scx200_wdt_ping(); scx200_wdt_ping();
/* Fall through */ fallthrough;
case WDIOC_GETTIMEOUT: case WDIOC_GETTIMEOUT:
if (put_user(margin, p)) if (put_user(margin, p))
return -EFAULT; return -EFAULT;
......
...@@ -474,7 +474,7 @@ static long wb_smsc_wdt_ioctl(struct file *file, ...@@ -474,7 +474,7 @@ static long wb_smsc_wdt_ioctl(struct file *file,
return -EINVAL; return -EINVAL;
timeout = new_timeout; timeout = new_timeout;
wb_smsc_wdt_set_timeout(timeout); wb_smsc_wdt_set_timeout(timeout);
/* fall through - and return the new timeout... */ fallthrough; /* and return the new timeout */
case WDIOC_GETTIMEOUT: case WDIOC_GETTIMEOUT:
new_timeout = timeout; new_timeout = timeout;
if (unit == UNIT_MINUTE) if (unit == UNIT_MINUTE)
......
...@@ -20,11 +20,13 @@ ...@@ -20,11 +20,13 @@
#include <linux/hrtimer.h> #include <linux/hrtimer.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/kthread.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/moduleparam.h> #include <linux/moduleparam.h>
#include <linux/reboot.h> #include <linux/reboot.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/watchdog.h> #include <linux/watchdog.h>
#include <linux/workqueue.h>
#define TIMER_MARGIN 60 /* Default is 60 seconds */ #define TIMER_MARGIN 60 /* Default is 60 seconds */
static unsigned int soft_margin = TIMER_MARGIN; /* in seconds */ static unsigned int soft_margin = TIMER_MARGIN; /* in seconds */
...@@ -49,11 +51,34 @@ module_param(soft_panic, int, 0); ...@@ -49,11 +51,34 @@ module_param(soft_panic, int, 0);
MODULE_PARM_DESC(soft_panic, MODULE_PARM_DESC(soft_panic,
"Softdog action, set to 1 to panic, 0 to reboot (default=0)"); "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_ticktock;
static struct hrtimer softdog_preticktock; 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 enum hrtimer_restart softdog_fire(struct hrtimer *timer)
{ {
static bool soft_reboot_fired;
module_put(THIS_MODULE); module_put(THIS_MODULE);
if (soft_noboot) { if (soft_noboot) {
pr_crit("Triggered - Reboot ignored\n"); pr_crit("Triggered - Reboot ignored\n");
...@@ -62,6 +87,33 @@ static enum hrtimer_restart softdog_fire(struct hrtimer *timer) ...@@ -62,6 +87,33 @@ static enum hrtimer_restart softdog_fire(struct hrtimer *timer)
panic("Software Watchdog Timer expired"); panic("Software Watchdog Timer expired");
} else { } else {
pr_crit("Initiating system reboot\n"); 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(); emergency_restart();
pr_crit("Reboot didn't ?????\n"); pr_crit("Reboot didn't ?????\n");
} }
...@@ -145,12 +197,17 @@ static int __init softdog_init(void) ...@@ -145,12 +197,17 @@ static int __init softdog_init(void)
softdog_preticktock.function = softdog_pretimeout; softdog_preticktock.function = softdog_pretimeout;
} }
if (soft_active_on_boot)
softdog_ping(&softdog_dev);
ret = watchdog_register_device(&softdog_dev); ret = watchdog_register_device(&softdog_dev);
if (ret) if (ret)
return ret; return ret;
pr_info("initialized. soft_noboot=%d soft_margin=%d sec soft_panic=%d (nowayout=%d)\n", pr_info("initialized. soft_noboot=%d soft_margin=%d sec soft_panic=%d (nowayout=%d)\n",
soft_noboot, softdog_dev.timeout, soft_panic, nowayout); soft_noboot, softdog_dev.timeout, soft_panic, nowayout);
pr_info(" soft_reboot_cmd=%s soft_active_on_boot=%d\n",
soft_reboot_cmd ?: "<not set>", soft_active_on_boot);
return 0; return 0;
} }
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Based on i8xx_tco.c: * Based on i8xx_tco.c:
* (c) Copyright 2000 kernel concepts <nils@kernelconcepts.de>, All Rights * (c) Copyright 2000 kernel concepts <nils@kernelconcepts.de>, All Rights
* Reserved. * Reserved.
* http://www.kernelconcepts.de * https://www.kernelconcepts.de
* *
* See AMD Publication 43009 "AMD SB700/710/750 Register Reference Guide", * See AMD Publication 43009 "AMD SB700/710/750 Register Reference Guide",
* AMD Publication 45482 "AMD SB800-Series Southbridges Register * AMD Publication 45482 "AMD SB800-Series Southbridges Register
......
...@@ -235,7 +235,7 @@ static int sunxi_wdt_probe(struct platform_device *pdev) ...@@ -235,7 +235,7 @@ static int sunxi_wdt_probe(struct platform_device *pdev)
sunxi_wdt = devm_kzalloc(dev, sizeof(*sunxi_wdt), GFP_KERNEL); sunxi_wdt = devm_kzalloc(dev, sizeof(*sunxi_wdt), GFP_KERNEL);
if (!sunxi_wdt) if (!sunxi_wdt)
return -EINVAL; return -ENOMEM;
sunxi_wdt->wdt_regs = of_device_get_match_data(dev); sunxi_wdt->wdt_regs = of_device_get_match_data(dev);
if (!sunxi_wdt->wdt_regs) if (!sunxi_wdt->wdt_regs)
......
...@@ -289,7 +289,7 @@ static long fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ...@@ -289,7 +289,7 @@ static long fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
timeout = new_timeout; timeout = new_timeout;
wdt_keepalive(); wdt_keepalive();
} }
/* Fall through */ fallthrough;
case WDIOC_GETTIMEOUT: case WDIOC_GETTIMEOUT:
return put_user(timeout, p); return put_user(timeout, p);
default: default:
......
...@@ -422,7 +422,7 @@ static long wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ...@@ -422,7 +422,7 @@ static long wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
return -EINVAL; return -EINVAL;
wdt_keepalive(); wdt_keepalive();
/* Fall through */ fallthrough;
case WDIOC_GETTIMEOUT: case WDIOC_GETTIMEOUT:
return put_user(timeout, uarg.i); return put_user(timeout, uarg.i);
......
...@@ -174,7 +174,7 @@ static long wafwdt_ioctl(struct file *file, unsigned int cmd, ...@@ -174,7 +174,7 @@ static long wafwdt_ioctl(struct file *file, unsigned int cmd,
timeout = new_timeout; timeout = new_timeout;
wafwdt_stop(); wafwdt_stop();
wafwdt_start(); wafwdt_start();
/* Fall through */ fallthrough;
case WDIOC_GETTIMEOUT: case WDIOC_GETTIMEOUT:
return put_user(timeout, p); return put_user(timeout, p);
......
...@@ -275,15 +275,18 @@ static int watchdog_start(struct watchdog_device *wdd) ...@@ -275,15 +275,18 @@ static int watchdog_start(struct watchdog_device *wdd)
set_bit(_WDOG_KEEPALIVE, &wd_data->status); set_bit(_WDOG_KEEPALIVE, &wd_data->status);
started_at = ktime_get(); started_at = ktime_get();
if (watchdog_hw_running(wdd) && wdd->ops->ping) if (watchdog_hw_running(wdd) && wdd->ops->ping) {
err = wdd->ops->ping(wdd); err = __watchdog_ping(wdd);
else if (err == 0)
set_bit(WDOG_ACTIVE, &wdd->status);
} else {
err = wdd->ops->start(wdd); err = wdd->ops->start(wdd);
if (err == 0) { if (err == 0) {
set_bit(WDOG_ACTIVE, &wdd->status); set_bit(WDOG_ACTIVE, &wdd->status);
wd_data->last_keepalive = started_at; wd_data->last_keepalive = started_at;
wd_data->last_hw_keepalive = started_at; wd_data->last_hw_keepalive = started_at;
watchdog_update_worker(wdd); watchdog_update_worker(wdd);
}
} }
return err; return err;
...@@ -587,7 +590,7 @@ static DEVICE_ATTR_RW(pretimeout_governor); ...@@ -587,7 +590,7 @@ static DEVICE_ATTR_RW(pretimeout_governor);
static umode_t wdt_is_visible(struct kobject *kobj, struct attribute *attr, static umode_t wdt_is_visible(struct kobject *kobj, struct attribute *attr,
int n) 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); struct watchdog_device *wdd = dev_get_drvdata(dev);
umode_t mode = attr->mode; umode_t mode = attr->mode;
...@@ -776,7 +779,7 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd, ...@@ -776,7 +779,7 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd,
err = watchdog_ping(wdd); err = watchdog_ping(wdd);
if (err < 0) if (err < 0)
break; break;
/* fall through */ fallthrough;
case WDIOC_GETTIMEOUT: case WDIOC_GETTIMEOUT:
/* timeout == 0 means that we don't know the timeout */ /* timeout == 0 means that we don't know the timeout */
if (wdd->timeout == 0) { if (wdd->timeout == 0) {
...@@ -916,7 +919,7 @@ static int watchdog_release(struct inode *inode, struct file *file) ...@@ -916,7 +919,7 @@ static int watchdog_release(struct inode *inode, struct file *file)
* or if WDIOF_MAGICCLOSE is not set. If nowayout was set then * or if WDIOF_MAGICCLOSE is not set. If nowayout was set then
* watchdog_stop will fail. * watchdog_stop will fail.
*/ */
if (!test_bit(WDOG_ACTIVE, &wdd->status)) if (!watchdog_active(wdd))
err = 0; err = 0;
else if (test_and_clear_bit(_WDOG_ALLOW_RELEASE, &wd_data->status) || else if (test_and_clear_bit(_WDOG_ALLOW_RELEASE, &wd_data->status) ||
!(wdd->info->options & WDIOF_MAGICCLOSE)) !(wdd->info->options & WDIOF_MAGICCLOSE))
...@@ -994,6 +997,15 @@ static int watchdog_cdev_register(struct watchdog_device *wdd) ...@@ -994,6 +997,15 @@ static int watchdog_cdev_register(struct watchdog_device *wdd)
if (IS_ERR_OR_NULL(watchdog_kworker)) if (IS_ERR_OR_NULL(watchdog_kworker))
return -ENODEV; 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); kthread_init_work(&wd_data->work, watchdog_ping_work);
hrtimer_init(&wd_data->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_HARD); hrtimer_init(&wd_data->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_HARD);
wd_data->timer.function = watchdog_timer_expired; wd_data->timer.function = watchdog_timer_expired;
...@@ -1014,15 +1026,6 @@ static int watchdog_cdev_register(struct watchdog_device *wdd) ...@@ -1014,15 +1026,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 */ /* Fill in the data structures */
cdev_init(&wd_data->cdev, &watchdog_fops); cdev_init(&wd_data->cdev, &watchdog_fops);
...@@ -1135,6 +1138,36 @@ void watchdog_dev_unregister(struct watchdog_device *wdd) ...@@ -1135,6 +1138,36 @@ void watchdog_dev_unregister(struct watchdog_device *wdd)
watchdog_cdev_unregister(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 * watchdog_dev_init: init dev part of watchdog core
* *
......
...@@ -389,7 +389,7 @@ static long wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ...@@ -389,7 +389,7 @@ static long wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
if (wdt_set_heartbeat(new_heartbeat)) if (wdt_set_heartbeat(new_heartbeat))
return -EINVAL; return -EINVAL;
wdt_ping(); wdt_ping();
/* Fall through */ fallthrough;
case WDIOC_GETTIMEOUT: case WDIOC_GETTIMEOUT:
return put_user(heartbeat, p); return put_user(heartbeat, p);
default: default:
......
...@@ -168,7 +168,7 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd, ...@@ -168,7 +168,7 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd,
soft_margin = new_margin; soft_margin = new_margin;
reload = soft_margin * (mem_fclk_21285 / 256); reload = soft_margin * (mem_fclk_21285 / 256);
watchdog_ping(); watchdog_ping();
/* Fall through */ fallthrough;
case WDIOC_GETTIMEOUT: case WDIOC_GETTIMEOUT:
ret = put_user(soft_margin, int_arg); ret = put_user(soft_margin, int_arg);
break; break;
......
...@@ -398,7 +398,7 @@ static long wdt977_ioctl(struct file *file, unsigned int cmd, ...@@ -398,7 +398,7 @@ static long wdt977_ioctl(struct file *file, unsigned int cmd,
return -EINVAL; return -EINVAL;
wdt977_keepalive(); wdt977_keepalive();
/* Fall through */ fallthrough;
case WDIOC_GETTIMEOUT: case WDIOC_GETTIMEOUT:
return put_user(timeout, uarg.i); return put_user(timeout, uarg.i);
......
...@@ -426,7 +426,7 @@ static long wdtpci_ioctl(struct file *file, unsigned int cmd, ...@@ -426,7 +426,7 @@ static long wdtpci_ioctl(struct file *file, unsigned int cmd,
if (wdtpci_set_heartbeat(new_heartbeat)) if (wdtpci_set_heartbeat(new_heartbeat))
return -EINVAL; return -EINVAL;
wdtpci_ping(); wdtpci_ping();
/* fall through */ fallthrough;
case WDIOC_GETTIMEOUT: case WDIOC_GETTIMEOUT:
return put_user(heartbeat, p); return put_user(heartbeat, p);
default: default:
......
...@@ -43,10 +43,13 @@ ...@@ -43,10 +43,13 @@
* *
* TYPE1 HW watchdog implementation exist in old systems. * TYPE1 HW watchdog implementation exist in old systems.
* All new systems have TYPE2 HW watchdog. * 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 { enum mlxreg_wdt_type {
MLX_WDT_TYPE1, MLX_WDT_TYPE1,
MLX_WDT_TYPE2, MLX_WDT_TYPE2,
MLX_WDT_TYPE3,
}; };
/** /**
...@@ -93,7 +96,7 @@ struct mlxreg_core_data { ...@@ -93,7 +96,7 @@ struct mlxreg_core_data {
umode_t mode; umode_t mode;
struct device_node *np; struct device_node *np;
struct mlxreg_hotplug_device hpdev; struct mlxreg_hotplug_device hpdev;
u8 health_cntr; u32 health_cntr;
bool attached; bool attached;
u8 regnum; u8 regnum;
}; };
......
...@@ -210,6 +210,8 @@ extern int watchdog_init_timeout(struct watchdog_device *wdd, ...@@ -210,6 +210,8 @@ extern int watchdog_init_timeout(struct watchdog_device *wdd,
extern int watchdog_register_device(struct watchdog_device *); extern int watchdog_register_device(struct watchdog_device *);
extern void watchdog_unregister_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 */ /* devres register variant */
int devm_watchdog_register_device(struct device *dev, struct watchdog_device *); int devm_watchdog_register_device(struct device *dev, struct watchdog_device *);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment