Commit 3d33e6dd authored by Linus Torvalds's avatar Linus Torvalds

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

Pull watchdog updates from Wim Van Sebroeck:

 - new driver for Exar/MaxLinear XR28V38x

 - support for exynosautov9 SoC

 - support for Renesas R-Car V5H (R8A779G0) and RZ/V2M (r9a09g011) SoC

 - support for imx93

 - several other fixes and improvements

* tag 'linux-watchdog-6.1-rc1' of git://www.linux-watchdog.org/linux-watchdog: (36 commits)
  watchdog: twl4030_wdt: add missing mod_devicetable.h include
  dt-bindings: watchdog: migrate mt7621 text bindings to YAML
  watchdog: sp5100_tco: Add "action" module parameter
  watchdog: imx93: add watchdog timer on imx93
  watchdog: imx7ulp_wdt: init wdog when it was active
  watchdog: imx7ulp_wdt: Handle wdog reconfigure failure
  watchdog: imx7ulp_wdt: Fix RCS timeout issue
  watchdog: imx7ulp_wdt: Check CMD32EN in wdog init
  watchdog: imx7ulp: Add explict memory barrier for unlock sequence
  watchdog: imx7ulp: Move suspend/resume to noirq phase
  watchdog: rti-wdt:using the pm_runtime_resume_and_get to simplify the code
  dt-bindings: watchdog: rockchip: add rockchip,rk3128-wdt
  watchdog: s3c2410_wdt: support exynosautov9 watchdog
  dt-bindings: watchdog: add exynosautov9 compatible
  watchdog: npcm: Enable clock if provided
  watchdog: meson: keep running if already active
  watchdog: dt-bindings: atmel,at91sam9-wdt: convert to json-schema
  watchdog: armada_37xx_wdt: Fix .set_timeout callback
  watchdog: sa1100: make variable sa1100dog_driver static
  watchdog: w83977f_wdt: Fix comment typo
  ...
parents 524d0c68 099d387e
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
# Copyright (C) 2022 Microchip Technology, Inc. and its subsidiaries
%YAML 1.2
---
$id: http://devicetree.org/schemas/watchdog/atmel,at91sam9-wdt.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Atmel Watchdog Timers
maintainers:
- Eugen Hristev <eugen.hristev@microchip.com>
properties:
compatible:
const: atmel,at91sam9260-wdt
reg:
maxItems: 1
clocks:
maxItems: 1
interrupts:
maxItems: 1
atmel,max-heartbeat-sec:
description:
Should contain the maximum heartbeat value in seconds. This value
should be less or equal to 16. It is used to compute the WDV field.
maximum: 16
atmel,min-heartbeat-sec:
description:
Should contain the minimum heartbeat value in seconds. This value
must be smaller than the max-heartbeat-sec value. It is used to
compute the WDD field.
maximum: 16
atmel,watchdog-type:
$ref: /schemas/types.yaml#/definitions/string
description: |
Should be hardware or software.
oneOf:
- description:
Hardware watchdog uses the at91 watchdog reset.
const: hardware
- description: |
Software watchdog uses the watchdog interrupt
to trigger a software reset.
const: software
default: hardware
atmel,reset-type:
$ref: /schemas/types.yaml#/definitions/string
description: |
Should be proc or all. This is valid only when using hardware watchdog.
oneOf:
- description:
Assert peripherals and processor reset signals.
const: all
- description:
Assert the processor reset signal.
const: proc
default: all
atmel,disable:
$ref: /schemas/types.yaml#/definitions/flag
description:
Should be present if you want to stop the watchdog.
atmel,idle-halt:
$ref: /schemas/types.yaml#/definitions/flag
description: |
Should be present if you want to stop the watchdog when
entering idle state.
CAUTION: This property should be used with care, it actually makes the
watchdog not counting when the CPU is in idle state, therefore the
watchdog reset time depends on mean CPU usage and will not reset at all
if the CPU stops working while it is in idle state, which is probably
not what you want.
atmel,dbg-halt:
$ref: /schemas/types.yaml#/definitions/flag
description: |
Should be present if you want to stop the watchdog when
entering debug state.
required:
- compatible
- reg
- clocks
allOf:
- $ref: watchdog.yaml#
- if:
properties:
atmel,reset-type:
enum:
- all
- proc
then:
properties:
atmel,watchdog-type:
const: hardware
dependencies:
atmel,reset-type: ['atmel,watchdog-type']
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
watchdog@fffffd40 {
compatible = "atmel,at91sam9260-wdt";
reg = <0xfffffd40 0x10>;
interrupts = <1 IRQ_TYPE_LEVEL_HIGH 7>;
clocks = <&clk32k>;
timeout-sec = <15>;
atmel,watchdog-type = "hardware";
atmel,reset-type = "all";
atmel,dbg-halt;
atmel,idle-halt;
atmel,max-heartbeat-sec = <16>;
atmel,min-heartbeat-sec = <0>;
};
* Atmel Watchdog Timers
** at91sam9-wdt
Required properties:
- compatible: must be "atmel,at91sam9260-wdt".
- reg: physical base address of the controller and length of memory mapped
region.
- clocks: phandle to input clock.
Optional properties:
- timeout-sec: contains the watchdog timeout in seconds.
- interrupts : Should contain WDT interrupt.
- atmel,max-heartbeat-sec : Should contain the maximum heartbeat value in
seconds. This value should be less or equal to 16. It is used to
compute the WDV field.
- atmel,min-heartbeat-sec : Should contain the minimum heartbeat value in
seconds. This value must be smaller than the max-heartbeat-sec value.
It is used to compute the WDD field.
- atmel,watchdog-type : Should be "hardware" or "software". Hardware watchdog
use the at91 watchdog reset. Software watchdog use the watchdog
interrupt to trigger a software reset.
- atmel,reset-type : Should be "proc" or "all".
"all" : assert peripherals and processor reset signals
"proc" : assert the processor reset signal
This is valid only when using "hardware" watchdog.
- atmel,disable : Should be present if you want to disable the watchdog.
- atmel,idle-halt : Should be present if you want to stop the watchdog when
entering idle state.
CAUTION: This property should be used with care, it actually makes the
watchdog not counting when the CPU is in idle state, therefore the
watchdog reset time depends on mean CPU usage and will not reset at all
if the CPU stop working while it is in idle state, which is probably
not what you want.
- atmel,dbg-halt : Should be present if you want to stop the watchdog when
entering debug state.
Example:
watchdog@fffffd40 {
compatible = "atmel,at91sam9260-wdt";
reg = <0xfffffd40 0x10>;
interrupts = <1 IRQ_TYPE_LEVEL_HIGH 7>;
clocks = <&clk32k>;
timeout-sec = <15>;
atmel,watchdog-type = "hardware";
atmel,reset-type = "all";
atmel,dbg-halt;
atmel,idle-halt;
atmel,max-heartbeat-sec = <16>;
atmel,min-heartbeat-sec = <0>;
};
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/watchdog/mediatek,mt7621-wdt.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Ralink Watchdog Timers
maintainers:
- Sergio Paracuellos <sergio.paracuellos@gmail.com>
allOf:
- $ref: watchdog.yaml#
properties:
compatible:
const: mediatek,mt7621-wdt
reg:
maxItems: 1
required:
- compatible
- reg
additionalProperties: false
examples:
- |
watchdog@100 {
compatible = "mediatek,mt7621-wdt";
reg = <0x100 0x100>;
};
Ralink Watchdog Timers
Required properties:
- compatible: must be "mediatek,mt7621-wdt"
- reg: physical base address of the controller and length of the register range
Example:
watchdog@100 {
compatible = "mediatek,mt7621-wdt";
reg = <0x100 0x10>;
};
Xilinx AXI/PLB soft-core watchdog Device Tree Bindings
---------------------------------------------------------
Required properties:
- compatible : Should be "xlnx,xps-timebase-wdt-1.00.a" or
"xlnx,xps-timebase-wdt-1.01.a".
- reg : Physical base address and size
Optional properties:
- clocks : Input clock specifier. Refer to common clock
bindings.
- clock-frequency : Frequency of clock in Hz
- xlnx,wdt-enable-once : 0 - Watchdog can be restarted
1 - Watchdog can be enabled just once
- xlnx,wdt-interval : Watchdog timeout interval in 2^<val> clock cycles,
<val> is integer from 8 to 31.
Example:
axi-timebase-wdt@40100000 {
clock-frequency = <50000000>;
compatible = "xlnx,xps-timebase-wdt-1.00.a";
clocks = <&clkc 15>;
reg = <0x40100000 0x10000>;
xlnx,wdt-enable-once = <0x0>;
xlnx,wdt-interval = <0x1b>;
} ;
......@@ -31,6 +31,11 @@ properties:
- renesas,r9a07g054-wdt # RZ/V2L
- const: renesas,rzg2l-wdt
- items:
- enum:
- renesas,r9a09g011-wdt # RZ/V2M
- const: renesas,rzv2m-wdt # RZ/V2M
- items:
- enum:
- renesas,r8a7742-wdt # RZ/G1H
......@@ -65,18 +70,35 @@ properties:
- enum:
- renesas,r8a779a0-wdt # R-Car V3U
- renesas,r8a779f0-wdt # R-Car S4-8
- renesas,r8a779g0-wdt # R-Car V4H
- const: renesas,rcar-gen4-wdt # R-Car Gen4
reg:
maxItems: 1
interrupts: true
interrupt-names: true
clocks: true
clock-names: true
interrupts:
minItems: 1
items:
- description: Timeout
- description: Parity error
interrupt-names:
minItems: 1
items:
- const: wdt
- const: perrout
clocks:
minItems: 1
items:
- description: Register access clock
- description: Main clock
clock-names:
minItems: 1
items:
- const: pclk
- const: oscclk
power-domains:
maxItems: 1
......@@ -89,6 +111,7 @@ properties:
required:
- compatible
- reg
- interrupts
- clocks
allOf:
......@@ -113,31 +136,38 @@ allOf:
contains:
enum:
- renesas,rzg2l-wdt
- renesas,rzv2m-wdt
then:
properties:
interrupts:
maxItems: 2
interrupt-names:
items:
- const: wdt
- const: perrout
clocks:
items:
- description: Register access clock
- description: Main clock
minItems: 2
clock-names:
items:
- const: pclk
- const: oscclk
minItems: 2
required:
- clock-names
else:
properties:
clocks:
maxItems: 1
- if:
properties:
compatible:
contains:
enum:
- renesas,rzg2l-wdt
then:
properties:
interrupts:
minItems: 2
interrupt-names:
minItems: 2
required:
- interrupt-names
else:
properties:
interrupts:
maxItems: 1
clocks:
maxItems: 1
additionalProperties: false
......@@ -145,9 +175,11 @@ examples:
- |
#include <dt-bindings/clock/r8a7795-cpg-mssr.h>
#include <dt-bindings/power/r8a7795-sysc.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
wdt0: watchdog@e6020000 {
compatible = "renesas,r8a7795-wdt", "renesas,rcar-gen3-wdt";
reg = <0xe6020000 0x0c>;
interrupts = <GIC_SPI 140 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cpg CPG_MOD 402>;
power-domains = <&sysc R8A7795_PD_ALWAYS_ON>;
resets = <&cpg 402>;
......
......@@ -23,6 +23,7 @@ properties:
- samsung,exynos5420-wdt # for Exynos5420
- samsung,exynos7-wdt # for Exynos7
- samsung,exynos850-wdt # for Exynos850
- samsung,exynosautov9-wdt # for Exynosautov9
reg:
maxItems: 1
......@@ -67,6 +68,7 @@ allOf:
- samsung,exynos5420-wdt
- samsung,exynos7-wdt
- samsung,exynos850-wdt
- samsung,exynosautov9-wdt
then:
required:
- samsung,syscon-phandle
......@@ -76,6 +78,7 @@ allOf:
contains:
enum:
- samsung,exynos850-wdt
- samsung,exynosautov9-wdt
then:
properties:
clocks:
......
......@@ -20,6 +20,7 @@ properties:
- enum:
- rockchip,px30-wdt
- rockchip,rk3066-wdt
- rockchip,rk3128-wdt
- rockchip,rk3188-wdt
- rockchip,rk3228-wdt
- rockchip,rk3288-wdt
......
......@@ -35,20 +35,16 @@ additionalProperties: false
examples:
- |
#include <dt-bindings/clock/toshiba,tmpv770x.h>
soc {
#address-cells = <2>;
#size-cells = <2>;
wdt_clk: wdt-clk {
compatible = "fixed-clock";
clock-frequency = <150000000>;
#clock-cells = <0>;
};
watchdog@28330000 {
wdt: watchdog@28330000 {
compatible = "toshiba,visconti-wdt";
reg = <0 0x28330000 0 0x1000>;
clocks = <&wdt_clk>;
timeout-sec = <20>;
clocks = <&pismu TMPV770X_CLK_WDTCLK>;
};
};
# SPDX-License-Identifier: GPL-2.0-or-later OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/watchdog/xlnx,xps-timebase-wdt.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Xilinx AXI/PLB softcore and window Watchdog Timer
maintainers:
- Shubhrajyoti Datta <shubhrajyoti.datta@xilinx.com>
- Srinivas Neeli <srinivas.neeli@xilinx.com>
description:
The Timebase watchdog timer(WDT) is a free-running 32 bit counter.
WDT uses a dual-expiration architecture. After one expiration of
the timeout interval, an interrupt is generated and the WDT state
bit is set to one in the status register. If the state bit is not
cleared (by writing a one to the state bit) before the next
expiration of the timeout interval, a WDT reset is generated.
allOf:
- $ref: watchdog.yaml#
properties:
compatible:
enum:
- xlnx,xps-timebase-wdt-1.01.a
- xlnx,xps-timebase-wdt-1.00.a
reg:
maxItems: 1
clocks:
maxItems: 1
clock-frequency:
description: Frequency of clock in Hz
xlnx,wdt-interval:
$ref: /schemas/types.yaml#/definitions/uint32
description: Watchdog timeout interval
minimum: 8
maximum: 32
xlnx,wdt-enable-once:
$ref: /schemas/types.yaml#/definitions/uint32
enum: [0, 1]
description: If watchdog is configured as enable once,
then the watchdog cannot be disabled after
it has been enabled.
required:
- compatible
- reg
unevaluatedProperties: false
examples:
- |
watchdog@40100000 {
compatible = "xlnx,xps-timebase-wdt-1.00.a";
reg = <0x40100000 0x1000>;
clock-frequency = <50000000>;
clocks = <&clkc 15>;
xlnx,wdt-enable-once = <0x0>;
xlnx,wdt-interval = <0x1b>;
};
...
......@@ -1089,6 +1089,17 @@ config EBC_C384_WDT
WinSystems EBC-C384 motherboard. The timeout may be configured via
the timeout module parameter.
config EXAR_WDT
tristate "Exar Watchdog Timer"
depends on X86
select WATCHDOG_CORE
help
Enables watchdog timer support for the watchdog timer present
in some Exar/MaxLinear UART chips like the XR28V38x.
To compile this driver as a module, choose M here: the
module will be called exar_wdt.
config F71808E_WDT
tristate "Fintek F718xx, F818xx Super I/O Watchdog"
depends on X86
......@@ -1315,7 +1326,7 @@ config IT87_WDT
config HP_WATCHDOG
tristate "HP ProLiant iLO2+ Hardware Watchdog Timer"
select WATCHDOG_CORE
depends on X86 && PCI
depends on (ARM64 || X86) && PCI
help
A software monitoring watchdog and NMI handling driver. This driver
will detect lockups and provide a stack trace. This is a driver that
......@@ -1325,7 +1336,7 @@ config HP_WATCHDOG
config HPWDT_NMI_DECODING
bool "NMI support for the HP ProLiant iLO2+ Hardware Watchdog Timer"
depends on HP_WATCHDOG
depends on X86 && HP_WATCHDOG
default y
help
Enables the NMI handler for the watchdog pretimeout NMI and the iLO
......
......@@ -105,6 +105,7 @@ obj-$(CONFIG_ADVANTECH_WDT) += advantechwdt.o
obj-$(CONFIG_ALIM1535_WDT) += alim1535_wdt.o
obj-$(CONFIG_ALIM7101_WDT) += alim7101_wdt.o
obj-$(CONFIG_EBC_C384_WDT) += ebc-c384_wdt.o
obj-$(CONFIG_EXAR_WDT) += exar_wdt.o
obj-$(CONFIG_F71808E_WDT) += f71808e_wdt.o
obj-$(CONFIG_SP5100_TCO) += sp5100_tco.o
obj-$(CONFIG_GEODE_WDT) += geodewdt.o
......
......@@ -179,6 +179,8 @@ static int armada_37xx_wdt_set_timeout(struct watchdog_device *wdt,
dev->timeout = (u64)dev->clk_rate * timeout;
do_div(dev->timeout, CNTR_CTRL_PRESCALE_MIN);
set_counter_value(dev, CNTR_ID_WDOG, dev->timeout);
return 0;
}
......
......@@ -332,18 +332,18 @@ static int aspeed_wdt_probe(struct platform_device *pdev)
u32 reg = readl(wdt->base + WDT_RESET_WIDTH);
reg &= config->ext_pulse_width_mask;
if (of_property_read_bool(np, "aspeed,ext-push-pull"))
reg |= WDT_PUSH_PULL_MAGIC;
if (of_property_read_bool(np, "aspeed,ext-active-high"))
reg |= WDT_ACTIVE_HIGH_MAGIC;
else
reg |= WDT_OPEN_DRAIN_MAGIC;
reg |= WDT_ACTIVE_LOW_MAGIC;
writel(reg, wdt->base + WDT_RESET_WIDTH);
reg &= config->ext_pulse_width_mask;
if (of_property_read_bool(np, "aspeed,ext-active-high"))
reg |= WDT_ACTIVE_HIGH_MAGIC;
if (of_property_read_bool(np, "aspeed,ext-push-pull"))
reg |= WDT_PUSH_PULL_MAGIC;
else
reg |= WDT_ACTIVE_LOW_MAGIC;
reg |= WDT_OPEN_DRAIN_MAGIC;
writel(reg, wdt->base + WDT_RESET_WIDTH);
}
......
......@@ -9,8 +9,8 @@
#include <linux/gpio/consumer.h>
#include <linux/mfd/rohm-bd957x.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/watchdog.h>
......@@ -202,10 +202,10 @@ static int bd957x_set_wdt_mode(struct bd9576_wdt_priv *priv, int hw_margin,
static int bd9576_wdt_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *np = dev->parent->of_node;
struct bd9576_wdt_priv *priv;
u32 hw_margin[2];
u32 hw_margin_max = BD957X_WDT_DEFAULT_MARGIN, hw_margin_min = 0;
int count;
int ret;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
......@@ -221,40 +221,51 @@ static int bd9576_wdt_probe(struct platform_device *pdev)
return -ENODEV;
}
priv->gpiod_en = devm_gpiod_get_from_of_node(dev, dev->parent->of_node,
"rohm,watchdog-enable-gpios",
0, GPIOD_OUT_LOW,
"watchdog-enable");
priv->gpiod_en = devm_fwnode_gpiod_get(dev, dev_fwnode(dev->parent),
"rohm,watchdog-enable",
GPIOD_OUT_LOW,
"watchdog-enable");
if (IS_ERR(priv->gpiod_en))
return dev_err_probe(dev, PTR_ERR(priv->gpiod_en),
"getting watchdog-enable GPIO failed\n");
priv->gpiod_ping = devm_gpiod_get_from_of_node(dev, dev->parent->of_node,
"rohm,watchdog-ping-gpios",
0, GPIOD_OUT_LOW,
"watchdog-ping");
priv->gpiod_ping = devm_fwnode_gpiod_get(dev, dev_fwnode(dev->parent),
"rohm,watchdog-ping",
GPIOD_OUT_LOW,
"watchdog-ping");
if (IS_ERR(priv->gpiod_ping))
return dev_err_probe(dev, PTR_ERR(priv->gpiod_ping),
"getting watchdog-ping GPIO failed\n");
ret = of_property_read_variable_u32_array(np, "rohm,hw-timeout-ms",
&hw_margin[0], 1, 2);
if (ret < 0 && ret != -EINVAL)
return ret;
count = device_property_count_u32(dev->parent, "rohm,hw-timeout-ms");
if (count < 0 && count != -EINVAL)
return count;
if (count > 0) {
if (count > ARRAY_SIZE(hw_margin))
return -EINVAL;
if (ret == 1)
hw_margin_max = hw_margin[0];
ret = device_property_read_u32_array(dev->parent,
"rohm,hw-timeout-ms",
hw_margin, count);
if (ret < 0)
return ret;
if (ret == 2) {
hw_margin_max = hw_margin[1];
hw_margin_min = hw_margin[0];
if (count == 1)
hw_margin_max = hw_margin[0];
if (count == 2) {
hw_margin_max = hw_margin[1];
hw_margin_min = hw_margin[0];
}
}
ret = bd957x_set_wdt_mode(priv, hw_margin_max, hw_margin_min);
if (ret)
return ret;
priv->always_running = of_property_read_bool(np, "always-running");
priv->always_running = device_property_read_bool(dev->parent,
"always-running");
watchdog_set_drvdata(&priv->wdd, priv);
......
......@@ -192,7 +192,7 @@ static void eurwdt_ping(void)
* @ppos: pointer to the position to write. No seeks allowed
*
* A write to a watchdog device is defined as a keepalive signal. Any
* write of data will do, as we we don't define content meaning.
* write of data will do, as we don't define content meaning.
*/
static ssize_t eurwdt_write(struct file *file, const char __user *buf,
......
// SPDX-License-Identifier: GPL-2.0+
/*
* exar_wdt.c - Driver for the watchdog present in some
* Exar/MaxLinear UART chips like the XR28V38x.
*
* (c) Copyright 2022 D. Müller <d.mueller@elsoft.ch>.
*
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/io.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/watchdog.h>
#define DRV_NAME "exar_wdt"
static const unsigned short sio_config_ports[] = { 0x2e, 0x4e };
static const unsigned char sio_enter_keys[] = { 0x67, 0x77, 0x87, 0xA0 };
#define EXAR_EXIT_KEY 0xAA
#define EXAR_LDN 0x07
#define EXAR_DID 0x20
#define EXAR_VID 0x23
#define EXAR_WDT 0x26
#define EXAR_ACT 0x30
#define EXAR_RTBASE 0x60
#define EXAR_WDT_LDEV 0x08
#define EXAR_VEN_ID 0x13A8
#define EXAR_DEV_382 0x0382
#define EXAR_DEV_384 0x0384
/* WDT runtime registers */
#define WDT_CTRL 0x00
#define WDT_VAL 0x01
#define WDT_UNITS_10MS 0x0 /* the 10 millisec unit of the HW is not used */
#define WDT_UNITS_SEC 0x2
#define WDT_UNITS_MIN 0x4
/* default WDT control for WDTOUT signal activ / rearm by read */
#define EXAR_WDT_DEF_CONF 0
struct wdt_pdev_node {
struct list_head list;
struct platform_device *pdev;
const char name[16];
};
struct wdt_priv {
/* the lock for WDT io operations */
spinlock_t io_lock;
struct resource wdt_res;
struct watchdog_device wdt_dev;
unsigned short did;
unsigned short config_port;
unsigned char enter_key;
unsigned char unit;
unsigned char timeout;
};
#define WATCHDOG_TIMEOUT 60
static int timeout = WATCHDOG_TIMEOUT;
module_param(timeout, int, 0);
MODULE_PARM_DESC(timeout,
"Watchdog timeout in seconds. 1<=timeout<=15300, default="
__MODULE_STRING(WATCHDOG_TIMEOUT) ".");
static bool nowayout = WATCHDOG_NOWAYOUT;
module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout,
"Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
static int exar_sio_enter(const unsigned short config_port,
const unsigned char key)
{
if (!request_muxed_region(config_port, 2, DRV_NAME))
return -EBUSY;
/* write the ENTER-KEY twice */
outb(key, config_port);
outb(key, config_port);
return 0;
}
static void exar_sio_exit(const unsigned short config_port)
{
outb(EXAR_EXIT_KEY, config_port);
release_region(config_port, 2);
}
static unsigned char exar_sio_read(const unsigned short config_port,
const unsigned char reg)
{
outb(reg, config_port);
return inb(config_port + 1);
}
static void exar_sio_write(const unsigned short config_port,
const unsigned char reg, const unsigned char val)
{
outb(reg, config_port);
outb(val, config_port + 1);
}
static unsigned short exar_sio_read16(const unsigned short config_port,
const unsigned char reg)
{
unsigned char msb, lsb;
msb = exar_sio_read(config_port, reg);
lsb = exar_sio_read(config_port, reg + 1);
return (msb << 8) | lsb;
}
static void exar_sio_select_wdt(const unsigned short config_port)
{
exar_sio_write(config_port, EXAR_LDN, EXAR_WDT_LDEV);
}
static void exar_wdt_arm(const struct wdt_priv *priv)
{
unsigned short rt_base = priv->wdt_res.start;
/* write timeout value twice to arm watchdog */
outb(priv->timeout, rt_base + WDT_VAL);
outb(priv->timeout, rt_base + WDT_VAL);
}
static void exar_wdt_disarm(const struct wdt_priv *priv)
{
unsigned short rt_base = priv->wdt_res.start;
/*
* use two accesses with different values to make sure
* that a combination of a previous single access and
* the ones below with the same value are not falsely
* interpreted as "arm watchdog"
*/
outb(0xFF, rt_base + WDT_VAL);
outb(0, rt_base + WDT_VAL);
}
static int exar_wdt_start(struct watchdog_device *wdog)
{
struct wdt_priv *priv = watchdog_get_drvdata(wdog);
unsigned short rt_base = priv->wdt_res.start;
spin_lock(&priv->io_lock);
exar_wdt_disarm(priv);
outb(priv->unit, rt_base + WDT_CTRL);
exar_wdt_arm(priv);
spin_unlock(&priv->io_lock);
return 0;
}
static int exar_wdt_stop(struct watchdog_device *wdog)
{
struct wdt_priv *priv = watchdog_get_drvdata(wdog);
spin_lock(&priv->io_lock);
exar_wdt_disarm(priv);
spin_unlock(&priv->io_lock);
return 0;
}
static int exar_wdt_keepalive(struct watchdog_device *wdog)
{
struct wdt_priv *priv = watchdog_get_drvdata(wdog);
unsigned short rt_base = priv->wdt_res.start;
spin_lock(&priv->io_lock);
/* reading the WDT_VAL reg will feed the watchdog */
inb(rt_base + WDT_VAL);
spin_unlock(&priv->io_lock);
return 0;
}
static int exar_wdt_set_timeout(struct watchdog_device *wdog, unsigned int t)
{
struct wdt_priv *priv = watchdog_get_drvdata(wdog);
bool unit_min = false;
/*
* if new timeout is bigger then 255 seconds, change the
* unit to minutes and round the timeout up to the next whole minute
*/
if (t > 255) {
unit_min = true;
t = DIV_ROUND_UP(t, 60);
}
/* save for later use in exar_wdt_start() */
priv->unit = unit_min ? WDT_UNITS_MIN : WDT_UNITS_SEC;
priv->timeout = t;
wdog->timeout = unit_min ? t * 60 : t;
if (watchdog_hw_running(wdog))
exar_wdt_start(wdog);
return 0;
}
static const struct watchdog_info exar_wdt_info = {
.options = WDIOF_KEEPALIVEPING |
WDIOF_SETTIMEOUT |
WDIOF_MAGICCLOSE,
.identity = "Exar/MaxLinear XR28V38x Watchdog",
};
static const struct watchdog_ops exar_wdt_ops = {
.owner = THIS_MODULE,
.start = exar_wdt_start,
.stop = exar_wdt_stop,
.ping = exar_wdt_keepalive,
.set_timeout = exar_wdt_set_timeout,
};
static int exar_wdt_config(struct watchdog_device *wdog,
const unsigned char conf)
{
struct wdt_priv *priv = watchdog_get_drvdata(wdog);
int ret;
ret = exar_sio_enter(priv->config_port, priv->enter_key);
if (ret)
return ret;
exar_sio_select_wdt(priv->config_port);
exar_sio_write(priv->config_port, EXAR_WDT, conf);
exar_sio_exit(priv->config_port);
return 0;
}
static int __init exar_wdt_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct wdt_priv *priv = dev->platform_data;
struct watchdog_device *wdt_dev = &priv->wdt_dev;
struct resource *res;
int ret;
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
if (!res)
return -ENXIO;
spin_lock_init(&priv->io_lock);
wdt_dev->info = &exar_wdt_info;
wdt_dev->ops = &exar_wdt_ops;
wdt_dev->min_timeout = 1;
wdt_dev->max_timeout = 255 * 60;
watchdog_init_timeout(wdt_dev, timeout, NULL);
watchdog_set_nowayout(wdt_dev, nowayout);
watchdog_stop_on_reboot(wdt_dev);
watchdog_stop_on_unregister(wdt_dev);
watchdog_set_drvdata(wdt_dev, priv);
ret = exar_wdt_config(wdt_dev, EXAR_WDT_DEF_CONF);
if (ret)
return ret;
exar_wdt_set_timeout(wdt_dev, timeout);
/* Make sure that the watchdog is not running */
exar_wdt_stop(wdt_dev);
ret = devm_watchdog_register_device(dev, wdt_dev);
if (ret)
return ret;
dev_info(dev, "XR28V%X WDT initialized. timeout=%d sec (nowayout=%d)\n",
priv->did, timeout, nowayout);
return 0;
}
static unsigned short __init exar_detect(const unsigned short config_port,
const unsigned char key,
unsigned short *rt_base)
{
int ret;
unsigned short base = 0;
unsigned short vid, did;
ret = exar_sio_enter(config_port, key);
if (ret)
return 0;
vid = exar_sio_read16(config_port, EXAR_VID);
did = exar_sio_read16(config_port, EXAR_DID);
/* check for the vendor and device IDs we currently know about */
if (vid == EXAR_VEN_ID &&
(did == EXAR_DEV_382 ||
did == EXAR_DEV_384)) {
exar_sio_select_wdt(config_port);
/* is device active? */
if (exar_sio_read(config_port, EXAR_ACT) == 0x01)
base = exar_sio_read16(config_port, EXAR_RTBASE);
}
exar_sio_exit(config_port);
if (base) {
pr_debug("Found a XR28V%X WDT (conf: 0x%x / rt: 0x%04x)\n",
did, config_port, base);
*rt_base = base;
return did;
}
return 0;
}
static struct platform_driver exar_wdt_driver = {
.driver = {
.name = DRV_NAME,
},
};
static LIST_HEAD(pdev_list);
static int __init exar_wdt_register(struct wdt_priv *priv, const int idx)
{
struct wdt_pdev_node *n;
n = kzalloc(sizeof(*n), GFP_KERNEL);
if (!n)
return -ENOMEM;
INIT_LIST_HEAD(&n->list);
scnprintf((char *)n->name, sizeof(n->name), DRV_NAME ".%d", idx);
priv->wdt_res.name = n->name;
n->pdev = platform_device_register_resndata(NULL, DRV_NAME, idx,
&priv->wdt_res, 1,
priv, sizeof(*priv));
if (IS_ERR(n->pdev)) {
kfree(n);
return PTR_ERR(n->pdev);
}
list_add_tail(&n->list, &pdev_list);
return 0;
}
static void exar_wdt_unregister(void)
{
struct wdt_pdev_node *n, *t;
list_for_each_entry_safe(n, t, &pdev_list, list) {
platform_device_unregister(n->pdev);
list_del(&n->list);
kfree(n);
}
}
static int __init exar_wdt_init(void)
{
int ret, i, j, idx = 0;
/* search for active Exar watchdogs on all possible locations */
for (i = 0; i < ARRAY_SIZE(sio_config_ports); i++) {
for (j = 0; j < ARRAY_SIZE(sio_enter_keys); j++) {
unsigned short did, rt_base = 0;
did = exar_detect(sio_config_ports[i],
sio_enter_keys[j],
&rt_base);
if (did) {
struct wdt_priv priv = {
.wdt_res = DEFINE_RES_IO(rt_base, 2),
.did = did,
.config_port = sio_config_ports[i],
.enter_key = sio_enter_keys[j],
};
ret = exar_wdt_register(&priv, idx);
if (!ret)
idx++;
}
}
}
if (!idx)
return -ENODEV;
ret = platform_driver_probe(&exar_wdt_driver, exar_wdt_probe);
if (ret)
exar_wdt_unregister();
return ret;
}
static void __exit exar_wdt_exit(void)
{
exar_wdt_unregister();
platform_driver_unregister(&exar_wdt_driver);
}
module_init(exar_wdt_init);
module_exit(exar_wdt_exit);
MODULE_AUTHOR("David Müller <d.mueller@elsoft.ch>");
MODULE_DESCRIPTION("Exar/MaxLinear Watchdog Driver");
MODULE_LICENSE("GPL");
......@@ -47,21 +47,28 @@ struct ftwdt010_wdt *to_ftwdt010_wdt(struct watchdog_device *wdd)
return container_of(wdd, struct ftwdt010_wdt, wdd);
}
static int ftwdt010_wdt_start(struct watchdog_device *wdd)
static void ftwdt010_enable(struct ftwdt010_wdt *gwdt,
unsigned int timeout,
bool need_irq)
{
struct ftwdt010_wdt *gwdt = to_ftwdt010_wdt(wdd);
u32 enable;
writel(wdd->timeout * WDT_CLOCK, gwdt->base + FTWDT010_WDLOAD);
writel(timeout * WDT_CLOCK, gwdt->base + FTWDT010_WDLOAD);
writel(WDRESTART_MAGIC, gwdt->base + FTWDT010_WDRESTART);
/* set clock before enabling */
enable = WDCR_CLOCK_5MHZ | WDCR_SYS_RST;
writel(enable, gwdt->base + FTWDT010_WDCR);
if (gwdt->has_irq)
if (need_irq)
enable |= WDCR_WDINTR;
enable |= WDCR_ENABLE;
writel(enable, gwdt->base + FTWDT010_WDCR);
}
static int ftwdt010_wdt_start(struct watchdog_device *wdd)
{
struct ftwdt010_wdt *gwdt = to_ftwdt010_wdt(wdd);
ftwdt010_enable(gwdt, wdd->timeout, gwdt->has_irq);
return 0;
}
......@@ -93,6 +100,13 @@ static int ftwdt010_wdt_set_timeout(struct watchdog_device *wdd,
return 0;
}
static int ftwdt010_wdt_restart(struct watchdog_device *wdd,
unsigned long action, void *data)
{
ftwdt010_enable(to_ftwdt010_wdt(wdd), 0, false);
return 0;
}
static irqreturn_t ftwdt010_wdt_interrupt(int irq, void *data)
{
struct ftwdt010_wdt *gwdt = data;
......@@ -107,6 +121,7 @@ static const struct watchdog_ops ftwdt010_wdt_ops = {
.stop = ftwdt010_wdt_stop,
.ping = ftwdt010_wdt_ping,
.set_timeout = ftwdt010_wdt_set_timeout,
.restart = ftwdt010_wdt_restart,
.owner = THIS_MODULE,
};
......@@ -156,7 +171,7 @@ static int ftwdt010_wdt_probe(struct platform_device *pdev)
}
irq = platform_get_irq(pdev, 0);
if (irq) {
if (irq > 0) {
ret = devm_request_irq(dev, irq, ftwdt010_wdt_interrupt, 0,
"watchdog bark", gwdt);
if (ret)
......
......@@ -20,7 +20,9 @@
#include <linux/pci_ids.h>
#include <linux/types.h>
#include <linux/watchdog.h>
#ifdef CONFIG_HPWDT_NMI_DECODING
#include <asm/nmi.h>
#endif
#include <linux/crash_dump.h>
#define HPWDT_VERSION "2.0.4"
......
......@@ -9,12 +9,15 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/reboot.h>
#include <linux/watchdog.h>
#define WDOG_CS 0x0
#define WDOG_CS_FLG BIT(14)
#define WDOG_CS_CMD32EN BIT(13)
#define WDOG_CS_PRES BIT(12)
#define WDOG_CS_ULK BIT(11)
#define WDOG_CS_RCS BIT(10)
#define LPO_CLK 0x1
......@@ -39,60 +42,105 @@
#define DEFAULT_TIMEOUT 60
#define MAX_TIMEOUT 128
#define WDOG_CLOCK_RATE 1000
#define WDOG_WAIT_TIMEOUT 20
#define WDOG_ULK_WAIT_TIMEOUT 1000
#define WDOG_RCS_WAIT_TIMEOUT 10000
#define WDOG_RCS_POST_WAIT 3000
#define RETRY_MAX 5
static bool nowayout = WATCHDOG_NOWAYOUT;
module_param(nowayout, bool, 0000);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
struct imx_wdt_hw_feature {
bool prescaler_enable;
u32 wdog_clock_rate;
};
struct imx7ulp_wdt_device {
struct watchdog_device wdd;
void __iomem *base;
struct clk *clk;
bool post_rcs_wait;
const struct imx_wdt_hw_feature *hw;
};
static int imx7ulp_wdt_wait(void __iomem *base, u32 mask)
static int imx7ulp_wdt_wait_ulk(void __iomem *base)
{
u32 val = readl(base + WDOG_CS);
if (!(val & mask) && readl_poll_timeout_atomic(base + WDOG_CS, val,
val & mask, 0,
WDOG_WAIT_TIMEOUT))
if (!(val & WDOG_CS_ULK) &&
readl_poll_timeout_atomic(base + WDOG_CS, val,
val & WDOG_CS_ULK, 0,
WDOG_ULK_WAIT_TIMEOUT))
return -ETIMEDOUT;
return 0;
}
static int imx7ulp_wdt_enable(struct watchdog_device *wdog, bool enable)
static int imx7ulp_wdt_wait_rcs(struct imx7ulp_wdt_device *wdt)
{
struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog);
int ret = 0;
u32 val = readl(wdt->base + WDOG_CS);
u64 timeout = (val & WDOG_CS_PRES) ?
WDOG_RCS_WAIT_TIMEOUT * 256 : WDOG_RCS_WAIT_TIMEOUT;
unsigned long wait_min = (val & WDOG_CS_PRES) ?
WDOG_RCS_POST_WAIT * 256 : WDOG_RCS_POST_WAIT;
if (!(val & WDOG_CS_RCS) &&
readl_poll_timeout(wdt->base + WDOG_CS, val, val & WDOG_CS_RCS, 100,
timeout))
ret = -ETIMEDOUT;
/* Wait 2.5 clocks after RCS done */
if (wdt->post_rcs_wait)
usleep_range(wait_min, wait_min + 2000);
return ret;
}
static int _imx7ulp_wdt_enable(struct imx7ulp_wdt_device *wdt, bool enable)
{
u32 val = readl(wdt->base + WDOG_CS);
int ret;
local_irq_disable();
writel(UNLOCK, wdt->base + WDOG_CNT);
ret = imx7ulp_wdt_wait(wdt->base, WDOG_CS_ULK);
ret = imx7ulp_wdt_wait_ulk(wdt->base);
if (ret)
goto enable_out;
if (enable)
writel(val | WDOG_CS_EN, wdt->base + WDOG_CS);
else
writel(val & ~WDOG_CS_EN, wdt->base + WDOG_CS);
imx7ulp_wdt_wait(wdt->base, WDOG_CS_RCS);
enable_out:
local_irq_enable();
ret = imx7ulp_wdt_wait_rcs(wdt);
return ret;
enable_out:
local_irq_enable();
return ret;
}
static bool imx7ulp_wdt_is_enabled(void __iomem *base)
static int imx7ulp_wdt_enable(struct watchdog_device *wdog, bool enable)
{
u32 val = readl(base + WDOG_CS);
struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog);
int ret;
u32 val;
u32 loop = RETRY_MAX;
do {
ret = _imx7ulp_wdt_enable(wdt, enable);
val = readl(wdt->base + WDOG_CS);
} while (--loop > 0 && ((!!(val & WDOG_CS_EN)) != enable || ret));
return val & WDOG_CS_EN;
if (loop == 0)
return -EBUSY;
return ret;
}
static int imx7ulp_wdt_ping(struct watchdog_device *wdog)
......@@ -114,26 +162,44 @@ static int imx7ulp_wdt_stop(struct watchdog_device *wdog)
return imx7ulp_wdt_enable(wdog, false);
}
static int imx7ulp_wdt_set_timeout(struct watchdog_device *wdog,
unsigned int timeout)
static int _imx7ulp_wdt_set_timeout(struct imx7ulp_wdt_device *wdt,
unsigned int toval)
{
struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog);
u32 val = WDOG_CLOCK_RATE * timeout;
int ret;
local_irq_disable();
writel(UNLOCK, wdt->base + WDOG_CNT);
ret = imx7ulp_wdt_wait(wdt->base, WDOG_CS_ULK);
ret = imx7ulp_wdt_wait_ulk(wdt->base);
if (ret)
goto timeout_out;
writel(val, wdt->base + WDOG_TOVAL);
imx7ulp_wdt_wait(wdt->base, WDOG_CS_RCS);
wdog->timeout = timeout;
writel(toval, wdt->base + WDOG_TOVAL);
local_irq_enable();
ret = imx7ulp_wdt_wait_rcs(wdt);
return ret;
timeout_out:
local_irq_enable();
return ret;
}
static int imx7ulp_wdt_set_timeout(struct watchdog_device *wdog,
unsigned int timeout)
{
struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog);
u32 toval = wdt->hw->wdog_clock_rate * timeout;
u32 val;
int ret;
u32 loop = RETRY_MAX;
do {
ret = _imx7ulp_wdt_set_timeout(wdt, toval);
val = readl(wdt->base + WDOG_TOVAL);
} while (--loop > 0 && (val != toval || ret));
if (loop == 0)
return -EBUSY;
wdog->timeout = timeout;
return ret;
}
......@@ -173,29 +239,62 @@ static const struct watchdog_info imx7ulp_wdt_info = {
WDIOF_MAGICCLOSE,
};
static int imx7ulp_wdt_init(void __iomem *base, unsigned int timeout)
static int _imx7ulp_wdt_init(struct imx7ulp_wdt_device *wdt, unsigned int timeout, unsigned int cs)
{
u32 val;
int ret;
local_irq_disable();
/* unlock the wdog for reconfiguration */
writel_relaxed(UNLOCK_SEQ0, base + WDOG_CNT);
writel_relaxed(UNLOCK_SEQ1, base + WDOG_CNT);
ret = imx7ulp_wdt_wait(base, WDOG_CS_ULK);
val = readl(wdt->base + WDOG_CS);
if (val & WDOG_CS_CMD32EN) {
writel(UNLOCK, wdt->base + WDOG_CNT);
} else {
mb();
/* unlock the wdog for reconfiguration */
writel_relaxed(UNLOCK_SEQ0, wdt->base + WDOG_CNT);
writel_relaxed(UNLOCK_SEQ1, wdt->base + WDOG_CNT);
mb();
}
ret = imx7ulp_wdt_wait_ulk(wdt->base);
if (ret)
goto init_out;
/* set an initial timeout value in TOVAL */
writel(timeout, base + WDOG_TOVAL);
/* enable 32bit command sequence and reconfigure */
val = WDOG_CS_CMD32EN | WDOG_CS_CLK | WDOG_CS_UPDATE |
WDOG_CS_WAIT | WDOG_CS_STOP;
writel(val, base + WDOG_CS);
imx7ulp_wdt_wait(base, WDOG_CS_RCS);
writel(timeout, wdt->base + WDOG_TOVAL);
writel(cs, wdt->base + WDOG_CS);
local_irq_enable();
ret = imx7ulp_wdt_wait_rcs(wdt);
return ret;
init_out:
local_irq_enable();
return ret;
}
static int imx7ulp_wdt_init(struct imx7ulp_wdt_device *wdt, unsigned int timeout)
{
/* enable 32bit command sequence and reconfigure */
u32 val = WDOG_CS_CMD32EN | WDOG_CS_CLK | WDOG_CS_UPDATE |
WDOG_CS_WAIT | WDOG_CS_STOP;
u32 cs, toval;
int ret;
u32 loop = RETRY_MAX;
if (wdt->hw->prescaler_enable)
val |= WDOG_CS_PRES;
do {
ret = _imx7ulp_wdt_init(wdt, timeout, val);
toval = readl(wdt->base + WDOG_TOVAL);
cs = readl(wdt->base + WDOG_CS);
cs &= ~(WDOG_CS_FLG | WDOG_CS_ULK | WDOG_CS_RCS);
} while (--loop > 0 && (cs != val || toval != timeout || ret));
if (loop == 0)
return -EBUSY;
return ret;
}
......@@ -228,6 +327,15 @@ static int imx7ulp_wdt_probe(struct platform_device *pdev)
return PTR_ERR(imx7ulp_wdt->clk);
}
imx7ulp_wdt->post_rcs_wait = true;
if (of_device_is_compatible(dev->of_node,
"fsl,imx8ulp-wdt")) {
dev_info(dev, "imx8ulp wdt probe\n");
imx7ulp_wdt->post_rcs_wait = false;
} else {
dev_info(dev, "imx7ulp wdt probe\n");
}
ret = clk_prepare_enable(imx7ulp_wdt->clk);
if (ret)
return ret;
......@@ -248,14 +356,16 @@ static int imx7ulp_wdt_probe(struct platform_device *pdev)
watchdog_stop_on_reboot(wdog);
watchdog_stop_on_unregister(wdog);
watchdog_set_drvdata(wdog, imx7ulp_wdt);
ret = imx7ulp_wdt_init(imx7ulp_wdt->base, wdog->timeout * WDOG_CLOCK_RATE);
imx7ulp_wdt->hw = of_device_get_match_data(dev);
ret = imx7ulp_wdt_init(imx7ulp_wdt, wdog->timeout * imx7ulp_wdt->hw->wdog_clock_rate);
if (ret)
return ret;
return devm_watchdog_register_device(dev, wdog);
}
static int __maybe_unused imx7ulp_wdt_suspend(struct device *dev)
static int __maybe_unused imx7ulp_wdt_suspend_noirq(struct device *dev)
{
struct imx7ulp_wdt_device *imx7ulp_wdt = dev_get_drvdata(dev);
......@@ -267,30 +377,44 @@ static int __maybe_unused imx7ulp_wdt_suspend(struct device *dev)
return 0;
}
static int __maybe_unused imx7ulp_wdt_resume(struct device *dev)
static int __maybe_unused imx7ulp_wdt_resume_noirq(struct device *dev)
{
struct imx7ulp_wdt_device *imx7ulp_wdt = dev_get_drvdata(dev);
u32 timeout = imx7ulp_wdt->wdd.timeout * WDOG_CLOCK_RATE;
u32 timeout = imx7ulp_wdt->wdd.timeout * imx7ulp_wdt->hw->wdog_clock_rate;
int ret;
ret = clk_prepare_enable(imx7ulp_wdt->clk);
if (ret)
return ret;
if (imx7ulp_wdt_is_enabled(imx7ulp_wdt->base))
imx7ulp_wdt_init(imx7ulp_wdt->base, timeout);
if (watchdog_active(&imx7ulp_wdt->wdd))
if (watchdog_active(&imx7ulp_wdt->wdd)) {
imx7ulp_wdt_init(imx7ulp_wdt, timeout);
imx7ulp_wdt_start(&imx7ulp_wdt->wdd);
imx7ulp_wdt_ping(&imx7ulp_wdt->wdd);
}
return 0;
}
static SIMPLE_DEV_PM_OPS(imx7ulp_wdt_pm_ops, imx7ulp_wdt_suspend,
imx7ulp_wdt_resume);
static const struct dev_pm_ops imx7ulp_wdt_pm_ops = {
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(imx7ulp_wdt_suspend_noirq,
imx7ulp_wdt_resume_noirq)
};
static const struct imx_wdt_hw_feature imx7ulp_wdt_hw = {
.prescaler_enable = false,
.wdog_clock_rate = 1000,
};
static const struct imx_wdt_hw_feature imx93_wdt_hw = {
.prescaler_enable = true,
.wdog_clock_rate = 125,
};
static const struct of_device_id imx7ulp_wdt_dt_ids[] = {
{ .compatible = "fsl,imx7ulp-wdt", },
{ .compatible = "fsl,imx8ulp-wdt", .data = &imx7ulp_wdt_hw, },
{ .compatible = "fsl,imx7ulp-wdt", .data = &imx7ulp_wdt_hw, },
{ .compatible = "fsl,imx93-wdt", .data = &imx93_wdt_hw, },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, imx7ulp_wdt_dt_ids);
......
......@@ -156,6 +156,7 @@ static int meson_gxbb_wdt_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct meson_gxbb_wdt *data;
int ret;
u32 ctrl_reg;
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data)
......@@ -189,13 +190,26 @@ static int meson_gxbb_wdt_probe(struct platform_device *pdev)
watchdog_set_nowayout(&data->wdt_dev, nowayout);
watchdog_set_drvdata(&data->wdt_dev, data);
ctrl_reg = readl(data->reg_base + GXBB_WDT_CTRL_REG) &
GXBB_WDT_CTRL_EN;
if (ctrl_reg) {
/* Watchdog is running - keep it running but extend timeout
* to the maximum while setting the timebase
*/
set_bit(WDOG_HW_RUNNING, &data->wdt_dev.status);
meson_gxbb_wdt_set_timeout(&data->wdt_dev,
GXBB_WDT_TCNT_SETUP_MASK / 1000);
}
/* Setup with 1ms timebase */
writel(((clk_get_rate(data->clk) / 1000) & GXBB_WDT_CTRL_DIV_MASK) |
GXBB_WDT_CTRL_EE_RESET |
GXBB_WDT_CTRL_CLK_EN |
GXBB_WDT_CTRL_CLKDIV_EN,
data->reg_base + GXBB_WDT_CTRL_REG);
ctrl_reg |= ((clk_get_rate(data->clk) / 1000) &
GXBB_WDT_CTRL_DIV_MASK) |
GXBB_WDT_CTRL_EE_RESET |
GXBB_WDT_CTRL_CLK_EN |
GXBB_WDT_CTRL_CLKDIV_EN;
writel(ctrl_reg, data->reg_base + GXBB_WDT_CTRL_REG);
meson_gxbb_wdt_set_timeout(&data->wdt_dev, data->wdt_dev.timeout);
return devm_watchdog_register_device(dev, &data->wdt_dev);
......
......@@ -3,6 +3,7 @@
// Copyright (c) 2018 IBM Corp.
#include <linux/bitops.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
......@@ -43,6 +44,7 @@
struct npcm_wdt {
struct watchdog_device wdd;
void __iomem *reg;
struct clk *clk;
};
static inline struct npcm_wdt *to_npcm_wdt(struct watchdog_device *wdd)
......@@ -66,6 +68,9 @@ static int npcm_wdt_start(struct watchdog_device *wdd)
struct npcm_wdt *wdt = to_npcm_wdt(wdd);
u32 val;
if (wdt->clk)
clk_prepare_enable(wdt->clk);
if (wdd->timeout < 2)
val = 0x800;
else if (wdd->timeout < 3)
......@@ -100,6 +105,9 @@ static int npcm_wdt_stop(struct watchdog_device *wdd)
writel(0, wdt->reg);
if (wdt->clk)
clk_disable_unprepare(wdt->clk);
return 0;
}
......@@ -147,6 +155,10 @@ static int npcm_wdt_restart(struct watchdog_device *wdd,
{
struct npcm_wdt *wdt = to_npcm_wdt(wdd);
/* For reset, we start the WDT clock and leave it running. */
if (wdt->clk)
clk_prepare_enable(wdt->clk);
writel(NPCM_WTR | NPCM_WTRE | NPCM_WTE, wdt->reg);
udelay(1000);
......@@ -191,6 +203,10 @@ static int npcm_wdt_probe(struct platform_device *pdev)
if (IS_ERR(wdt->reg))
return PTR_ERR(wdt->reg);
wdt->clk = devm_clk_get_optional(&pdev->dev, NULL);
if (IS_ERR(wdt->clk))
return PTR_ERR(wdt->clk);
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
......
......@@ -225,9 +225,8 @@ static int rti_wdt_probe(struct platform_device *pdev)
wdt->freq = wdt->freq * 9 / 10;
pm_runtime_enable(dev);
ret = pm_runtime_get_sync(dev);
ret = pm_runtime_resume_and_get(dev);
if (ret < 0) {
pm_runtime_put_noidle(dev);
pm_runtime_disable(&pdev->dev);
return dev_err_probe(dev, ret, "runtime pm failed\n");
}
......
......@@ -10,7 +10,7 @@
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/reset.h>
......@@ -40,6 +40,11 @@ module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
enum rz_wdt_type {
WDT_RZG2L,
WDT_RZV2M,
};
struct rzg2l_wdt_priv {
void __iomem *base;
struct watchdog_device wdev;
......@@ -48,6 +53,7 @@ struct rzg2l_wdt_priv {
unsigned long delay;
struct clk *pclk;
struct clk *osc_clk;
enum rz_wdt_type devtype;
};
static void rzg2l_wdt_wait_delay(struct rzg2l_wdt_priv *priv)
......@@ -142,11 +148,29 @@ static int rzg2l_wdt_restart(struct watchdog_device *wdev,
clk_prepare_enable(priv->pclk);
clk_prepare_enable(priv->osc_clk);
/* Generate Reset (WDTRSTB) Signal on parity error */
rzg2l_wdt_write(priv, 0, PECR);
if (priv->devtype == WDT_RZG2L) {
/* Generate Reset (WDTRSTB) Signal on parity error */
rzg2l_wdt_write(priv, 0, PECR);
/* Force parity error */
rzg2l_wdt_write(priv, PEEN_FORCE, PEEN);
} else {
/* RZ/V2M doesn't have parity error registers */
wdev->timeout = 0;
/* Initialize time out */
rzg2l_wdt_init_timeout(wdev);
/* Force parity error */
rzg2l_wdt_write(priv, PEEN_FORCE, PEEN);
/* Initialize watchdog counter register */
rzg2l_wdt_write(priv, 0, WDTTIM);
/* Enable watchdog timer*/
rzg2l_wdt_write(priv, WDTCNT_WDTEN, WDTCNT);
/* Wait 2 consecutive overflow cycles for reset */
mdelay(DIV_ROUND_UP(2 * 0xFFFFF * 1000, priv->osc_clk_rate));
}
return 0;
}
......@@ -227,6 +251,8 @@ static int rzg2l_wdt_probe(struct platform_device *pdev)
if (ret)
return dev_err_probe(dev, ret, "failed to deassert");
priv->devtype = (uintptr_t)of_device_get_match_data(dev);
pm_runtime_enable(&pdev->dev);
priv->wdev.info = &rzg2l_wdt_ident;
......@@ -255,7 +281,8 @@ static int rzg2l_wdt_probe(struct platform_device *pdev)
}
static const struct of_device_id rzg2l_wdt_ids[] = {
{ .compatible = "renesas,rzg2l-wdt", },
{ .compatible = "renesas,rzg2l-wdt", .data = (void *)WDT_RZG2L },
{ .compatible = "renesas,rzv2m-wdt", .data = (void *)WDT_RZV2M },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, rzg2l_wdt_ids);
......
......@@ -60,9 +60,13 @@
#define EXYNOS850_CLUSTER0_NONCPU_INT_EN 0x1244
#define EXYNOS850_CLUSTER1_NONCPU_OUT 0x1620
#define EXYNOS850_CLUSTER1_NONCPU_INT_EN 0x1644
#define EXYNOSAUTOV9_CLUSTER1_NONCPU_OUT 0x1520
#define EXYNOSAUTOV9_CLUSTER1_NONCPU_INT_EN 0x1544
#define EXYNOS850_CLUSTER0_WDTRESET_BIT 24
#define EXYNOS850_CLUSTER1_WDTRESET_BIT 23
#define EXYNOSAUTOV9_CLUSTER0_WDTRESET_BIT 25
#define EXYNOSAUTOV9_CLUSTER1_WDTRESET_BIT 24
/**
* DOC: Quirk flags for different Samsung watchdog IP-cores
......@@ -236,6 +240,30 @@ static const struct s3c2410_wdt_variant drv_data_exynos850_cl1 = {
QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_CNT_EN,
};
static const struct s3c2410_wdt_variant drv_data_exynosautov9_cl0 = {
.mask_reset_reg = EXYNOS850_CLUSTER0_NONCPU_INT_EN,
.mask_bit = 2,
.mask_reset_inv = true,
.rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET,
.rst_stat_bit = EXYNOSAUTOV9_CLUSTER0_WDTRESET_BIT,
.cnt_en_reg = EXYNOS850_CLUSTER0_NONCPU_OUT,
.cnt_en_bit = 7,
.quirks = QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_MASK_RESET |
QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_CNT_EN,
};
static const struct s3c2410_wdt_variant drv_data_exynosautov9_cl1 = {
.mask_reset_reg = EXYNOSAUTOV9_CLUSTER1_NONCPU_INT_EN,
.mask_bit = 2,
.mask_reset_inv = true,
.rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET,
.rst_stat_bit = EXYNOSAUTOV9_CLUSTER1_WDTRESET_BIT,
.cnt_en_reg = EXYNOSAUTOV9_CLUSTER1_NONCPU_OUT,
.cnt_en_bit = 7,
.quirks = QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_MASK_RESET |
QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_CNT_EN,
};
static const struct of_device_id s3c2410_wdt_match[] = {
{ .compatible = "samsung,s3c2410-wdt",
.data = &drv_data_s3c2410 },
......@@ -249,6 +277,8 @@ static const struct of_device_id s3c2410_wdt_match[] = {
.data = &drv_data_exynos7 },
{ .compatible = "samsung,exynos850-wdt",
.data = &drv_data_exynos850_cl0 },
{ .compatible = "samsung,exynosautov9-wdt",
.data = &drv_data_exynosautov9_cl0 },
{},
};
MODULE_DEVICE_TABLE(of, s3c2410_wdt_match);
......@@ -630,8 +660,9 @@ s3c2410_get_wdt_drv_data(struct platform_device *pdev)
}
#ifdef CONFIG_OF
/* Choose Exynos850 driver data w.r.t. cluster index */
if (variant == &drv_data_exynos850_cl0) {
/* Choose Exynos850/ExynosAutov9 driver data w.r.t. cluster index */
if (variant == &drv_data_exynos850_cl0 ||
variant == &drv_data_exynosautov9_cl0) {
u32 index;
int err;
......@@ -644,9 +675,11 @@ s3c2410_get_wdt_drv_data(struct platform_device *pdev)
switch (index) {
case 0:
return &drv_data_exynos850_cl0;
return variant;
case 1:
return &drv_data_exynos850_cl1;
return (variant == &drv_data_exynos850_cl0) ?
&drv_data_exynos850_cl1 :
&drv_data_exynosautov9_cl1;
default:
dev_err(dev, "wrong cluster index: %u\n", index);
return NULL;
......
......@@ -238,7 +238,7 @@ static int sa1100dog_remove(struct platform_device *pdev)
return 0;
}
struct platform_driver sa1100dog_driver = {
static struct platform_driver sa1100dog_driver = {
.driver.name = "sa1100_wdt",
.probe = sa1100dog_probe,
.remove = sa1100dog_remove,
......
......@@ -65,6 +65,12 @@ static struct pci_dev *sp5100_tco_pci;
/* module parameters */
#define WATCHDOG_ACTION 0
static bool action = WATCHDOG_ACTION;
module_param(action, bool, 0);
MODULE_PARM_DESC(action, "Action taken when watchdog expires, 0 to reset, 1 to poweroff (default="
__MODULE_STRING(WATCHDOG_ACTION) ")");
#define WATCHDOG_HEARTBEAT 60 /* 60 sec default heartbeat. */
static int heartbeat = WATCHDOG_HEARTBEAT; /* in seconds */
module_param(heartbeat, int, 0);
......@@ -297,8 +303,11 @@ static int sp5100_tco_timer_init(struct sp5100_tco *tco)
if (val & SP5100_WDT_FIRED)
wdd->bootstatus = WDIOF_CARDRESET;
/* Set watchdog action to reset the system */
val &= ~SP5100_WDT_ACTION_RESET;
/* Set watchdog action */
if (action)
val |= SP5100_WDT_ACTION_RESET;
else
val &= ~SP5100_WDT_ACTION_RESET;
writel(val, SP5100_WDT_CONTROL(tco->tcobase));
/* Set a reasonable heartbeat before we stop the timer */
......
......@@ -9,6 +9,7 @@
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/mod_devicetable.h>
#include <linux/watchdog.h>
#include <linux/platform_device.h>
#include <linux/mfd/twl.h>
......
......@@ -113,6 +113,10 @@ MODULE_PARM_DESC(early_disable, "Disable watchdog at boot time (default=0)");
#define W836X7HF_WDT_CSR 0xf7
#define NCT6102D_WDT_CSR 0xf2
#define WDT_CSR_STATUS 0x10
#define WDT_CSR_KBD 0x40
#define WDT_CSR_MOUSE 0x80
static void superio_outb(int reg, int val)
{
outb(reg, WDT_EFER);
......@@ -244,8 +248,12 @@ static int w83627hf_init(struct watchdog_device *wdog, enum chips chip)
t = superio_inb(cr_wdt_control) & ~0x0C;
superio_outb(cr_wdt_control, t);
/* reset trigger, disable keyboard & mouse turning off watchdog */
t = superio_inb(cr_wdt_csr) & ~0xD0;
t = superio_inb(cr_wdt_csr);
if (t & WDT_CSR_STATUS)
wdog->bootstatus |= WDIOF_CARDRESET;
/* reset status, disable keyboard & mouse turning off watchdog */
t &= ~(WDT_CSR_STATUS | WDT_CSR_KBD | WDT_CSR_MOUSE);
superio_outb(cr_wdt_csr, t);
superio_exit();
......
......@@ -321,7 +321,7 @@ static int wdt_release(struct inode *inode, struct file *file)
* @ppos: pointer to the position to write. No seeks allowed
*
* A write to a watchdog device is defined as a keepalive signal. Any
* write of data will do, as we we don't define content meaning.
* write of data will do, as we don't define content meaning.
*/
static ssize_t wdt_write(struct file *file, const char __user *buf,
......
......@@ -1015,7 +1015,11 @@ static int watchdog_cdev_register(struct watchdog_device *wdd)
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);
err = dev_set_name(&wd_data->dev, "watchdog%d", wdd->id);
if (err) {
put_device(&wd_data->dev);
return err;
}
kthread_init_work(&wd_data->work, watchdog_ping_work);
hrtimer_init(&wd_data->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_HARD);
......
......@@ -342,9 +342,8 @@ static int wdat_wdt_probe(struct platform_device *pdev)
return -EINVAL;
wdat->period = tbl->timer_period;
wdat->wdd.min_hw_heartbeat_ms = wdat->period * tbl->min_count;
wdat->wdd.max_hw_heartbeat_ms = wdat->period * tbl->max_count;
wdat->wdd.min_timeout = 1;
wdat->wdd.min_timeout = DIV_ROUND_UP(wdat->period * tbl->min_count, 1000);
wdat->wdd.max_timeout = wdat->period * tbl->max_count / 1000;
wdat->stopped_in_sleep = tbl->flags & ACPI_WDAT_STOPPED;
wdat->wdd.info = &wdat_wdt_info;
wdat->wdd.ops = &wdat_wdt_ops;
......
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