Skip to contentSkip to conversation

Add Realtek Otto WDT support

PATCH Superseded Sander Vanheule 2 participants 9 messages Nov 4, 2021 – Nov 18, 2021

Subsystems (3)

v3 of 4 Superseded Latest is v4 →

lore
Share & tools
lore

Sander Vanheule

author maintainer
#0 lore raw

v3 cover Add Realtek Otto WDT support

This watchdog timer is found on Realtek's Otto MIPS platforms, including the RTL838x, RTL839x, and RTL930x series of ethernet switch SoCs. It has a number of reset modes (SoC, CPU, software), and can provide pretimeout interrupts.

The timer has two timeout phases. Both phases have a maximum duration of 32 prescaled clock ticks, which is ca. 43s with a clock of 200MHz:
- Phase 1: During this phase, the WDT can be pinged to reset the timeout.
- Phase 2: Starts after phase 1 has timed out, and only serves to give the system some time to clean up, or notify others that it's going to reset. During this phase, pinging the WDT has no effect, and a reset is unavoidable.

The driver has been tested on a Zyxel GS1900-8 (RTL8380, mainline kernel and OpenWrt), a Zyxel GS1900-48 (RTL8393, mainline), a Netgear GS110TPPv1 (RTL8381, mainline), and a Zyxel XGS1250-12 (RTL9203B, Openwrt).

Changes since v2:

- Fix off-by-one error in prescale assignment

Main changes since v1:

- Drop implementation of phase2 irq, since it is only triggered on system reset
- Drop redundant value checks and lock
- Add RTL930x compatibility

Sander Vanheule (2):
dt-bindings: watchdog: Realtek Otto WDT binding
watchdog: Add Realtek Otto watchdog timer

 .../bindings/watchdog/realtek,otto-wdt.yaml   |  91 +++++
 MAINTAINERS                                   |   7 +
 drivers/watchdog/Kconfig                      |  13 +
 drivers/watchdog/Makefile                     |   1 +
 drivers/watchdog/realtek_otto_wdt.c           | 361 ++++++++++++++++++
 5 files changed, 473 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/watchdog/realtek,otto-wdt.yaml
 create mode 100644 drivers/watchdog/realtek_otto_wdt.c
Signature · 1 line
2.31.1
author maintainer
#1 lore raw

PATCH v3 1/2 dt-bindings: watchdog: Realtek Otto WDT binding

Add a binding description for Realtek's watchdog timer as found on several of their MIPS-based SoCs (codenamed Otto), such as the RTL838x, RTL839x, and RTL930x series of switch SoCs.

--
v2:
- add realtek,rtl9300-wdt compatible
- make interrupts property required
- add Rob's Reviewed-by tag

---
 .../bindings/watchdog/realtek,otto-wdt.yaml   | 91 +++++++++++++++++++
 1 file changed, 91 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/watchdog/realtek,otto-wdt.yaml
Documentation/devicetree/bindings/watchdog/realtek,otto-wdt.yaml new file +91 -0
new file mode 100644
index 000000000000..11b220a5e0f6
@@ -0,0 +1,91 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/watchdog/realtek,otto-wdt.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Realtek Otto watchdog timer
+
+maintainers:
+  - Sander Vanheule <[email protected]>
+
+description: |
+  The timer has two timeout phases. Both phases have a maximum duration of 32
+  prescaled clock ticks, which is ca. 43s with a bus clock of 200MHz. The
+  minimum duration of each phase is one tick. Each phase can trigger an
+  interrupt, although the phase 2 interrupt will occur with the system reset.
+  - Phase 1: During this phase, the WDT can be pinged to reset the timeout.
+  - Phase 2: Starts after phase 1 has timed out, and only serves to give the
+    system some time to clean up, or notify others that it's going to reset.
+    During this phase, pinging the WDT has no effect, and a reset is
+    unavoidable, unless the WDT is disabled.
+
+allOf:
+  - $ref: watchdog.yaml#
+
+properties:
+  compatible:
+    enum:
+      - realtek,rtl8380-wdt
+      - realtek,rtl8390-wdt
+      - realtek,rtl9300-wdt
+
+  reg:
+    maxItems: 1
+
+  clocks:
+    maxItems: 1
+
+  interrupts:
+    items:
+      - description: interrupt specifier for pretimeout
+      - description: interrupt specifier for timeout
+
+  interrupt-names:
+    items:
+      - const: phase1
+      - const: phase2
+
+  realtek,reset-mode:
+    $ref: /schemas/types.yaml#/definitions/string
+    description: |
+      Specify how the system is reset after a timeout. Defaults to "cpu" if
+      left unspecified.
+    oneOf:
+      - description: Reset the entire chip
+        const: soc
+      - description: |
+          Reset the CPU and IPsec engine, but leave other peripherals untouched
+        const: cpu
+      - description: |
+          Reset the execution pointer, but don't actually reset any hardware
+        const: software
+
+required:
+  - compatible
+  - reg
+  - clocks
+  - interrupts
+
+unevaluatedProperties: false
+
+dependencies:
+  interrupts: [ interrupt-names ]
+
+examples:
+  - |
+    watchdog: watchdog@3150 {
+        compatible = "realtek,rtl8380-wdt";
+        reg = <0x3150 0xc>;
+
+        realtek,reset-mode = "soc";
+
+        clocks = <&lxbus_clock>;
+        timeout-sec = <20>;
+
+        interrupt-parent = <&rtlintc>;
+        interrupt-names = "phase1", "phase2";
+        interrupts = <19>, <18>;
+    };
+
+...
Signature · 1 line
2.31.1
maintainer Reviewed-by on v3 1/2
#2 lore raw

Re: [PATCH v3 1/2] dt-bindings: watchdog: Realtek Otto WDT binding

Quoted 6 lines · Sander Vanheule

Add a binding description for Realtek's watchdog timer as found on several of their MIPS-based SoCs (codenamed Otto), such as the RTL838x, RTL839x, and RTL930x series of switch SoCs.

Quoted 2 lines

--

For the future, change log after '---', please.

Quoted 109 lines

v2:
- add realtek,rtl9300-wdt compatible
- make interrupts property required
- add Rob's Reviewed-by tag

---
 .../bindings/watchdog/realtek,otto-wdt.yaml   | 91 +++++++++++++++++++
 1 file changed, 91 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/watchdog/realtek,otto-wdt.yaml
Documentation/devicetree/bindings/watchdog/realtek,otto-wdt.yaml new file +91 -0
new file mode 100644
index 000000000000..11b220a5e0f6
@@ -0,0 +1,91 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/watchdog/realtek,otto-wdt.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Realtek Otto watchdog timer
+
+maintainers:
+  - Sander Vanheule <[email protected]>
+
+description: |
+  The timer has two timeout phases. Both phases have a maximum duration of 32
+  prescaled clock ticks, which is ca. 43s with a bus clock of 200MHz. The
+  minimum duration of each phase is one tick. Each phase can trigger an
+  interrupt, although the phase 2 interrupt will occur with the system reset.
+  - Phase 1: During this phase, the WDT can be pinged to reset the timeout.
+  - Phase 2: Starts after phase 1 has timed out, and only serves to give the
+    system some time to clean up, or notify others that it's going to reset.
+    During this phase, pinging the WDT has no effect, and a reset is
+    unavoidable, unless the WDT is disabled.
+
+allOf:
+  - $ref: watchdog.yaml#
+
+properties:
+  compatible:
+    enum:
+      - realtek,rtl8380-wdt
+      - realtek,rtl8390-wdt
+      - realtek,rtl9300-wdt
+
+  reg:
+    maxItems: 1
+
+  clocks:
+    maxItems: 1
+
+  interrupts:
+    items:
+      - description: interrupt specifier for pretimeout
+      - description: interrupt specifier for timeout
+
+  interrupt-names:
+    items:
+      - const: phase1
+      - const: phase2
+
+  realtek,reset-mode:
+    $ref: /schemas/types.yaml#/definitions/string
+    description: |
+      Specify how the system is reset after a timeout. Defaults to "cpu" if
+      left unspecified.
+    oneOf:
+      - description: Reset the entire chip
+        const: soc
+      - description: |
+          Reset the CPU and IPsec engine, but leave other peripherals untouched
+        const: cpu
+      - description: |
+          Reset the execution pointer, but don't actually reset any hardware
+        const: software
+
+required:
+  - compatible
+  - reg
+  - clocks
+  - interrupts
+
+unevaluatedProperties: false
+
+dependencies:
+  interrupts: [ interrupt-names ]
+
+examples:
+  - |
+    watchdog: watchdog@3150 {
+        compatible = "realtek,rtl8380-wdt";
+        reg = <0x3150 0xc>;
+
+        realtek,reset-mode = "soc";
+
+        clocks = <&lxbus_clock>;
+        timeout-sec = <20>;
+
+        interrupt-parent = <&rtlintc>;
+        interrupt-names = "phase1", "phase2";
+        interrupts = <19>, <18>;
+    };
+
+...
Signature · 1 line
2.31.1
author maintainer
#3 lore raw

PATCH v3 2/2 watchdog: Add Realtek Otto watchdog timer

In reply to Sander Vanheule

Realtek MIPS SoCs (platform name Otto) have a watchdog timer with pretimeout notifitication support. The WDT can (partially) hard reset, or soft reset the SoC.

This driver implements all features as described in the devicetree binding, except the phase2 interrupt, and also functions as a restart handler. The cpu reset mode is considered to be a "warm" restart, since this mode does not reset all peripherals. Being an embedded system though, the "cpu" and "software" modes will still cause the bootloader to run on restart.

It is not known how a forced system reset can be disabled on the supported platforms. This means that the phase2 interrupt will only fire at the same time as reset, so implementing phase2 is of little use.

--
v2 -> v3:
- fix off-by-one error in PRESCALE assignment

v1 -> v2:
- drop raw_spinlock, locking is not required
- replace devm_free_irq with disable_irq
- use max_hw_heartbeat_ms instead of max_timeout
- drop redundant timeout value checks
- replace ROUND_CLOSEST by ROUND_UP for tick counts, and flooring division for timeout values
- change COMMON_CLK 'select' into 'depends on'
- add MODULE_DEVICE_TABLE for OF ID table
- add realtek,rtl9300 compatible
- drop phase2 irq

---
 MAINTAINERS                         |   7 +
 drivers/watchdog/Kconfig            |  13 +
 drivers/watchdog/Makefile           |   1 +
 drivers/watchdog/realtek_otto_wdt.c | 361 ++++++++++++++++++++++++++++
 4 files changed, 382 insertions(+)
 create mode 100644 drivers/watchdog/realtek_otto_wdt.c
MAINTAINERS +7 -0
index 2c9070aeba2a..54c8f788d3e5 100644
@@ -15991,6 +15991,13 @@ S:	Maintained
 F:	include/sound/rt*.h
 F:	sound/soc/codecs/rt*
 
+REALTEK OTTO WATCHDOG
+M:	Sander Vanheule <[email protected]>
+L:	[email protected]
+S:	Maintained
+F:	Documentation/devicetree/bindings/watchdog/realtek,otto-wdt.yaml
+F:	driver/watchdog/realtek_otto_wdt.c
+
 REALTEK RTL83xx SMI DSA ROUTER CHIPS
 M:	Linus Walleij <[email protected]>
 S:	Maintained
drivers/watchdog/Kconfig +13 -0
index bf59faeb3de1..d308e13a9aa1 100644
@@ -954,6 +954,19 @@ config RTD119X_WATCHDOG
 	  Say Y here to include support for the watchdog timer in
 	  Realtek RTD1295 SoCs.
 
+config REALTEK_OTTO_WDT
+	tristate "Realtek Otto MIPS watchdog support"
+	depends on MACH_REALTEK_RTL || COMPILE_TEST
+	depends on COMMON_CLK
+	select WATCHDOG_CORE
+	default MACH_REALTEK_RTL
+	help
+	  Say Y here to include support for the watchdog timer on Realtek
+	  RTL838x, RTL839x, RTL930x SoCs. This watchdog has pretimeout
+	  notifications and system reset on timeout.
+
+	  When built as a module this will be called realtek_otto_wdt.
+
 config SPRD_WATCHDOG
 	tristate "Spreadtrum watchdog support"
 	depends on ARCH_SPRD || COMPILE_TEST
drivers/watchdog/Makefile +1 -0
index 1bd2d6f37c53..a8dccf819163 100644
@@ -171,6 +171,7 @@ obj-$(CONFIG_IMGPDC_WDT) += imgpdc_wdt.o
 obj-$(CONFIG_MT7621_WDT) += mt7621_wdt.o
 obj-$(CONFIG_PIC32_WDT) += pic32-wdt.o
 obj-$(CONFIG_PIC32_DMT) += pic32-dmt.o
+obj-$(CONFIG_REALTEK_OTTO_WDT) += realtek_otto_wdt.o
 
 # PARISC Architecture
 
drivers/watchdog/realtek_otto_wdt.c new file +361 -0

361 lines not on this page · raw message

Signature · 1 line
2.31.1
maintainer Reviewed-by on v3 2/2
#4 lore raw

Re: [PATCH v3 2/2] watchdog: Add Realtek Otto watchdog timer

Quoted 11 lines · Sander Vanheule

Realtek MIPS SoCs (platform name Otto) have a watchdog timer with pretimeout notifitication support. The WDT can (partially) hard reset, or soft reset the SoC.

This driver implements all features as described in the devicetree binding, except the phase2 interrupt, and also functions as a restart handler. The cpu reset mode is considered to be a "warm" restart, since this mode does not reset all peripherals. Being an embedded system though, the "cpu" and "software" modes will still cause the bootloader to run on restart.

Quoted 5 lines

It is not known how a forced system reset can be disabled on the supported platforms. This means that the phase2 interrupt will only fire at the same time as reset, so implementing phase2 is of little use.

Quoted 2 lines

--

Note for the future: Change log should be after '---'.

Quoted 446 lines

v2 -> v3:
- fix off-by-one error in PRESCALE assignment

v1 -> v2:
- drop raw_spinlock, locking is not required
- replace devm_free_irq with disable_irq
- use max_hw_heartbeat_ms instead of max_timeout
- drop redundant timeout value checks
- replace ROUND_CLOSEST by ROUND_UP for tick counts, and flooring division for timeout values
- change COMMON_CLK 'select' into 'depends on'
- add MODULE_DEVICE_TABLE for OF ID table
- add realtek,rtl9300 compatible
- drop phase2 irq

---
 MAINTAINERS                         |   7 +
 drivers/watchdog/Kconfig            |  13 +
 drivers/watchdog/Makefile           |   1 +
 drivers/watchdog/realtek_otto_wdt.c | 361 ++++++++++++++++++++++++++++
 4 files changed, 382 insertions(+)
 create mode 100644 drivers/watchdog/realtek_otto_wdt.c
MAINTAINERS +7 -0
index 2c9070aeba2a..54c8f788d3e5 100644
@@ -15991,6 +15991,13 @@ S:	Maintained
 F:	include/sound/rt*.h
 F:	sound/soc/codecs/rt*
 
+REALTEK OTTO WATCHDOG
+M:	Sander Vanheule <[email protected]>
+L:	[email protected]
+S:	Maintained
+F:	Documentation/devicetree/bindings/watchdog/realtek,otto-wdt.yaml
+F:	driver/watchdog/realtek_otto_wdt.c
+
 REALTEK RTL83xx SMI DSA ROUTER CHIPS
 M:	Linus Walleij <[email protected]>
 S:	Maintained
drivers/watchdog/Kconfig +13 -0
index bf59faeb3de1..d308e13a9aa1 100644
@@ -954,6 +954,19 @@ config RTD119X_WATCHDOG
 	  Say Y here to include support for the watchdog timer in
 	  Realtek RTD1295 SoCs.
 
+config REALTEK_OTTO_WDT
+	tristate "Realtek Otto MIPS watchdog support"
+	depends on MACH_REALTEK_RTL || COMPILE_TEST
+	depends on COMMON_CLK
+	select WATCHDOG_CORE
+	default MACH_REALTEK_RTL
+	help
+	  Say Y here to include support for the watchdog timer on Realtek
+	  RTL838x, RTL839x, RTL930x SoCs. This watchdog has pretimeout
+	  notifications and system reset on timeout.
+
+	  When built as a module this will be called realtek_otto_wdt.
+
 config SPRD_WATCHDOG
 	tristate "Spreadtrum watchdog support"
 	depends on ARCH_SPRD || COMPILE_TEST
drivers/watchdog/Makefile +1 -0
index 1bd2d6f37c53..a8dccf819163 100644
@@ -171,6 +171,7 @@ obj-$(CONFIG_IMGPDC_WDT) += imgpdc_wdt.o
 obj-$(CONFIG_MT7621_WDT) += mt7621_wdt.o
 obj-$(CONFIG_PIC32_WDT) += pic32-wdt.o
 obj-$(CONFIG_PIC32_DMT) += pic32-dmt.o
+obj-$(CONFIG_REALTEK_OTTO_WDT) += realtek_otto_wdt.o
 
 # PARISC Architecture
 
drivers/watchdog/realtek_otto_wdt.c new file +361 -0
new file mode 100644
index 000000000000..48bc8dfefc25
@@ -0,0 +1,361 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+/*
+ * Realtek Otto MIPS platform watchdog
+ *
+ * Watchdog timer that will reset the system after timeout, using the selected
+ * reset mode.
+ *
+ * Counter scaling and timeouts:
+ * - Base prescale of (2 << 25), providing tick duration T_0: 168ms @ 200MHz
+ * - PRESCALE: logarithmic prescaler adding a factor of {1, 2, 4, 8}
+ * - Phase 1: Times out after (PHASE1 + 1) × PRESCALE × T_0
+ *   Generates an interrupt, WDT cannot be stopped after phase 1
+ * - Phase 2: starts after phase 1, times out after (PHASE2 + 1) × PRESCALE × T_0
+ *   Resets the system according to RST_MODE
+ */
+
+#include <linux/bits.h>
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/math.h>
+#include <linux/minmax.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/reboot.h>
+#include <linux/watchdog.h>
+
+#define OTTO_WDT_REG_CNTR		0x0
+#define OTTO_WDT_CNTR_PING		BIT(31)
+
+#define OTTO_WDT_REG_INTR		0x4
+#define OTTO_WDT_INTR_PHASE_1		BIT(31)
+#define OTTO_WDT_INTR_PHASE_2		BIT(30)
+
+#define OTTO_WDT_REG_CTRL		0x8
+#define OTTO_WDT_CTRL_ENABLE		BIT(31)
+#define OTTO_WDT_CTRL_PRESCALE		GENMASK(30, 29)
+#define OTTO_WDT_CTRL_PHASE1		GENMASK(26, 22)
+#define OTTO_WDT_CTRL_PHASE2		GENMASK(19, 15)
+#define OTTO_WDT_CTRL_RST_MODE		GENMASK(1, 0)
+#define OTTO_WDT_MODE_SOC		0
+#define OTTO_WDT_MODE_CPU		1
+#define OTTO_WDT_MODE_SOFTWARE		2
+#define OTTO_WDT_CTRL_DEFAULT		OTTO_WDT_MODE_CPU
+
+#define OTTO_WDT_PRESCALE_MAX		3
+
+/*
+ * One higher than the max values contained in PHASE{1,2}, since a value of 0
+ * corresponds to one tick.
+ */
+#define OTTO_WDT_PHASE_TICKS_MAX	32
+
+/*
+ * The maximum reset delay is actually 2×32 ticks, but that would require large
+ * pretimeout values for timeouts longer than 32 ticks. Limit the maximum timeout
+ * to 32 + 1 to ensure small pretimeout values can be configured as expected.
+ */
+#define OTTO_WDT_TIMEOUT_TICKS_MAX	(OTTO_WDT_PHASE_TICKS_MAX + 1)
+
+struct otto_wdt_ctrl {
+	struct watchdog_device wdev;
+	struct device *dev;
+	void __iomem *base;
+	struct clk *clk;
+	int irq_phase1;
+};
+
+static int otto_wdt_start(struct watchdog_device *wdev)
+{
+	struct otto_wdt_ctrl *ctrl = watchdog_get_drvdata(wdev);
+	u32 v;
+
+	v = ioread32(ctrl->base + OTTO_WDT_REG_CTRL);
+	v |= OTTO_WDT_CTRL_ENABLE;
+	iowrite32(v, ctrl->base + OTTO_WDT_REG_CTRL);
+
+	return 0;
+}
+
+static int otto_wdt_stop(struct watchdog_device *wdev)
+{
+	struct otto_wdt_ctrl *ctrl = watchdog_get_drvdata(wdev);
+	u32 v;
+
+	v = ioread32(ctrl->base + OTTO_WDT_REG_CTRL);
+	v &= ~OTTO_WDT_CTRL_ENABLE;
+	iowrite32(v, ctrl->base + OTTO_WDT_REG_CTRL);
+
+	return 0;
+}
+
+static int otto_wdt_ping(struct watchdog_device *wdev)
+{
+	struct otto_wdt_ctrl *ctrl = watchdog_get_drvdata(wdev);
+
+	iowrite32(OTTO_WDT_CNTR_PING, ctrl->base + OTTO_WDT_REG_CNTR);
+
+	return 0;
+}
+
+static int otto_wdt_tick_ms(struct otto_wdt_ctrl *ctrl, int prescale)
+{
+	unsigned int rate_khz = clk_get_rate(ctrl->clk) / 1000;
+
+	if (!rate_khz)
+		return 0;
+
+	return DIV_ROUND_CLOSEST(1 << (25 + prescale), rate_khz);
+}
+
+/*
+ * The timer asserts the PHASE1/PHASE2 IRQs when the number of ticks exceeds
+ * the value stored in those fields. This means each phase will run for at least
+ * one tick, so small values need to be clamped to correctly reflect the timeout.
+ */
+static inline unsigned int div_round_ticks(unsigned int val, unsigned int tick_duration,
+		unsigned int min_ticks)
+{
+	return max(min_ticks, DIV_ROUND_UP(val, tick_duration));
+}
+
+static int otto_wdt_determine_timeouts(struct watchdog_device *wdev, unsigned int timeout,
+		unsigned int pretimeout)
+{
+	struct otto_wdt_ctrl *ctrl = watchdog_get_drvdata(wdev);
+	unsigned int pretimeout_ms = pretimeout * 1000;
+	unsigned int timeout_ms = timeout * 1000;
+	unsigned int prescale_next = 0;
+	unsigned int phase1_ticks;
+	unsigned int phase2_ticks;
+	unsigned int total_ticks;
+	unsigned int prescale;
+	unsigned int tick_ms;
+	u32 v;
+
+	do {
+		prescale = prescale_next;
+		if (prescale > OTTO_WDT_PRESCALE_MAX)
+			return -EINVAL;
+
+		tick_ms = otto_wdt_tick_ms(ctrl, prescale);
+		total_ticks = div_round_ticks(timeout_ms, tick_ms, 2);
+		phase1_ticks = div_round_ticks(timeout_ms - pretimeout_ms, tick_ms, 1);
+		phase2_ticks = total_ticks - phase1_ticks;
+
+		prescale_next++;
+	} while (phase1_ticks > OTTO_WDT_PHASE_TICKS_MAX
+		|| phase2_ticks > OTTO_WDT_PHASE_TICKS_MAX);
+
+	v = ioread32(ctrl->base + OTTO_WDT_REG_CTRL);
+
+	v &= ~(OTTO_WDT_CTRL_PRESCALE | OTTO_WDT_CTRL_PHASE1 | OTTO_WDT_CTRL_PHASE2);
+	v |= FIELD_PREP(OTTO_WDT_CTRL_PHASE1, phase1_ticks - 1);
+	v |= FIELD_PREP(OTTO_WDT_CTRL_PHASE2, phase2_ticks - 1);
+	v |= FIELD_PREP(OTTO_WDT_CTRL_PRESCALE, prescale);
+
+	iowrite32(v, ctrl->base + OTTO_WDT_REG_CTRL);
+
+	timeout_ms = total_ticks * tick_ms;
+	ctrl->wdev.timeout = timeout_ms / 1000;
+
+	pretimeout_ms = phase2_ticks * tick_ms;
+	ctrl->wdev.pretimeout = pretimeout_ms / 1000;
+
+	return 0;
+}
+
+static int otto_wdt_set_timeout(struct watchdog_device *wdev, unsigned int val)
+{
+	return otto_wdt_determine_timeouts(wdev, val, min(wdev->pretimeout, val - 1));
+}
+
+static int otto_wdt_set_pretimeout(struct watchdog_device *wdev, unsigned int val)
+{
+	return otto_wdt_determine_timeouts(wdev, wdev->timeout, val);
+}
+
+static int otto_wdt_restart(struct watchdog_device *wdev, unsigned long reboot_mode,
+		void *data)
+{
+	struct otto_wdt_ctrl *ctrl = watchdog_get_drvdata(wdev);
+	u32 reset_mode;
+	u32 v;
+
+	disable_irq(ctrl->irq_phase1);
+
+	switch (reboot_mode) {
+	case REBOOT_SOFT:
+		reset_mode = OTTO_WDT_MODE_SOFTWARE;
+		break;
+	case REBOOT_WARM:
+		reset_mode = OTTO_WDT_MODE_CPU;
+		break;
+	default:
+		reset_mode = OTTO_WDT_MODE_SOC;
+		break;
+	}
+
+	/* Configure for shortest timeout and wait for reset to occur */
+	v = FIELD_PREP(OTTO_WDT_CTRL_RST_MODE, reset_mode) | OTTO_WDT_CTRL_ENABLE;
+	iowrite32(v, ctrl->base + OTTO_WDT_REG_CTRL);
+
+	mdelay(3 * otto_wdt_tick_ms(ctrl, 0));
+
+	return 0;
+}
+
+static irqreturn_t otto_wdt_phase1_isr(int irq, void *dev_id)
+{
+	struct otto_wdt_ctrl *ctrl = dev_id;
+
+	iowrite32(OTTO_WDT_INTR_PHASE_1, ctrl->base + OTTO_WDT_REG_INTR);
+	dev_crit(ctrl->dev, "phase 1 timeout\n");
+	watchdog_notify_pretimeout(&ctrl->wdev);
+
+	return IRQ_HANDLED;
+}
+
+static const struct watchdog_ops otto_wdt_ops = {
+	.owner = THIS_MODULE,
+	.start = otto_wdt_start,
+	.stop = otto_wdt_stop,
+	.ping = otto_wdt_ping,
+	.set_timeout = otto_wdt_set_timeout,
+	.set_pretimeout = otto_wdt_set_pretimeout,
+	.restart = otto_wdt_restart,
+};
+
+static const struct watchdog_info otto_wdt_info = {
+	.identity = "Realtek Otto watchdog timer",
+	.options = WDIOF_KEEPALIVEPING |
+		WDIOF_MAGICCLOSE |
+		WDIOF_SETTIMEOUT |
+		WDIOF_PRETIMEOUT,
+};
+
+static int otto_wdt_probe_reset_mode(struct otto_wdt_ctrl *ctrl)
+{
+	static const char *mode_property = "realtek,reset-mode";
+	const struct fwnode_handle *node = ctrl->dev->fwnode;
+	int mode_count;
+	u32 mode;
+	u32 v;
+
+	if (!node)
+		return -ENXIO;
+
+	mode_count = fwnode_property_string_array_count(node, mode_property);
+	if (mode_count < 0)
+		return mode_count;
+	else if (mode_count == 0)
+		return 0;
+	else if (mode_count != 1)
+		return -EINVAL;
+
+	if (fwnode_property_match_string(node, mode_property, "soc") == 0)
+		mode = OTTO_WDT_MODE_SOC;
+	else if (fwnode_property_match_string(node, mode_property, "cpu") == 0)
+		mode = OTTO_WDT_MODE_CPU;
+	else if (fwnode_property_match_string(node, mode_property, "software") == 0)
+		mode = OTTO_WDT_MODE_SOFTWARE;
+	else
+		return -EINVAL;
+
+	v = ioread32(ctrl->base + OTTO_WDT_REG_CTRL);
+	v &= ~OTTO_WDT_CTRL_RST_MODE;
+	v |= FIELD_PREP(OTTO_WDT_CTRL_RST_MODE, mode);
+	iowrite32(v, ctrl->base + OTTO_WDT_REG_CTRL);
+
+	return 0;
+}
+
+static int otto_wdt_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct otto_wdt_ctrl *ctrl;
+	unsigned int max_tick_ms;
+	int ret;
+
+	ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL);
+	if (!ctrl)
+		return -ENOMEM;
+
+	ctrl->dev = dev;
+	ctrl->base = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(ctrl->base))
+		return PTR_ERR(ctrl->base);
+
+	/* Clear any old interrupts and reset initial state */
+	iowrite32(OTTO_WDT_INTR_PHASE_1 | OTTO_WDT_INTR_PHASE_2,
+			ctrl->base + OTTO_WDT_REG_INTR);
+	iowrite32(OTTO_WDT_CTRL_DEFAULT, ctrl->base + OTTO_WDT_REG_CTRL);
+
+	ctrl->clk = devm_clk_get(dev, NULL);
+	if (IS_ERR(ctrl->clk))
+		return dev_err_probe(dev, PTR_ERR(ctrl->clk), "Failed to get clock\n");
+
+	ctrl->irq_phase1 = platform_get_irq_byname(pdev, "phase1");
+	if (ctrl->irq_phase1 < 0)
+		return dev_err_probe(dev, ctrl->irq_phase1, "phase1 IRQ not found\n");
+
+	ret = devm_request_irq(dev, ctrl->irq_phase1, otto_wdt_phase1_isr, 0,
+			"realtek-otto-wdt", ctrl);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to get IRQ for phase1\n");
+
+	ret = otto_wdt_probe_reset_mode(ctrl);
+	if (ret)
+		return dev_err_probe(dev, ret, "Invalid reset mode specified\n");
+
+	ctrl->wdev.parent = dev;
+	ctrl->wdev.info = &otto_wdt_info;
+	ctrl->wdev.ops = &otto_wdt_ops;
+
+	/*
+	 * Since pretimeout cannot be disabled, min. timeout is twice the
+	 * subsystem resolution. max. timeout is ca. 43s at a bus clock of 200MHz.
+	 */
+	ctrl->wdev.min_timeout = 2;
+	max_tick_ms = otto_wdt_tick_ms(ctrl, OTTO_WDT_PRESCALE_MAX);
+	ctrl->wdev.max_hw_heartbeat_ms = max_tick_ms * OTTO_WDT_TIMEOUT_TICKS_MAX;
+	ctrl->wdev.timeout = min(30U, ctrl->wdev.max_hw_heartbeat_ms / 1000);
+
+	watchdog_set_drvdata(&ctrl->wdev, ctrl);
+	watchdog_init_timeout(&ctrl->wdev, 0, dev);
+	watchdog_stop_on_reboot(&ctrl->wdev);
+	watchdog_set_restart_priority(&ctrl->wdev, 128);
+
+	ret = otto_wdt_determine_timeouts(&ctrl->wdev, ctrl->wdev.timeout, 1);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to set timeout\n");
+
+	return devm_watchdog_register_device(dev, &ctrl->wdev);
+}
+
+static const struct of_device_id otto_wdt_ids[] = {
+	{ .compatible = "realtek,rtl8380-wdt" },
+	{ .compatible = "realtek,rtl8390-wdt" },
+	{ .compatible = "realtek,rtl9300-wdt" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, otto_wdt_ids);
+
+static struct platform_driver otto_wdt_driver = {
+	.probe = otto_wdt_probe,
+	.driver = {
+		.name = "realtek-otto-watchdog",
+		.of_match_table	= otto_wdt_ids,
+	},
+};
+module_platform_driver(otto_wdt_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Sander Vanheule <[email protected]>");
+MODULE_DESCRIPTION("Realtek Otto watchdog timer driver");
Signature · 1 line
2.31.1
author maintainer
#5 lore raw

Re: [PATCH v3 2/2] watchdog: Add Realtek Otto watchdog timer

Hi Guenter,

Quoted 12 lines · Guenter Roeck
Quoted 11 lines · Sander Vanheule

Realtek MIPS SoCs (platform name Otto) have a watchdog timer with pretimeout notifitication support. The WDT can (partially) hard reset, or soft reset the SoC.

This driver implements all features as described in the devicetree binding, except the phase2 interrupt, and also functions as a restart handler. The cpu reset mode is considered to be a "warm" restart, since this mode does not reset all peripherals. Being an embedded system though, the "cpu" and "software" modes will still cause the bootloader to run on restart.

Quoted 7 lines
Quoted 5 lines

It is not known how a forced system reset can be disabled on the supported platforms. This means that the phase2 interrupt will only fire at the same time as reset, so implementing phase2 is of little use.

Thank you for the review! In the meantime, I was preparing a v4 of this series with some small changes (see inline below). Could you let me know if I can keep your Reviewed-by with those changes?

Quoted 5 lines
Quoted 3 lines

--

Note for the future: Change log should be after '---'.

Will do!

Quoted 355 lines
Quoted 355 lines

v2 -> v3:
- fix off-by-one error in PRESCALE assignment

v1 -> v2:
- drop raw_spinlock, locking is not required
- replace devm_free_irq with disable_irq
- use max_hw_heartbeat_ms instead of max_timeout
- drop redundant timeout value checks
- replace ROUND_CLOSEST by ROUND_UP for tick counts, and flooring division for timeout values
- change COMMON_CLK 'select' into 'depends on'
- add MODULE_DEVICE_TABLE for OF ID table
- add realtek,rtl9300 compatible
- drop phase2 irq
---
 MAINTAINERS                         |   7 + drivers/watchdog/Kconfig            |  13 + drivers/watchdog/Makefile           |   1 + drivers/watchdog/realtek_otto_wdt.c | 361 ++++++++++++++++++++++++++++

 4 files changed, 382 insertions(+)

 create mode 100644 drivers/watchdog/realtek_otto_wdt.c

MAINTAINERS +0 -0
index 2c9070aeba2a..54c8f788d3e5 100644
@@ -15991,6 +15991,13 @@ S:     Maintained

 F:     include/sound/rt*.h
 F:     sound/soc/codecs/rt*

MAINTAINERS +7 -0
+REALTEK OTTO WATCHDOG
+M:     Sander Vanheule <[email protected]>
+L:     [email protected]
+S:     Maintained
+F:     Documentation/devicetree/bindings/watchdog/realtek,otto-wdt.yaml
+F:     driver/watchdog/realtek_otto_wdt.c
+

 REALTEK RTL83xx SMI DSA ROUTER CHIPS
 M:     Linus Walleij <[email protected]>
 S:     Maintained

drivers/watchdog/Kconfig +0 -0
index bf59faeb3de1..d308e13a9aa1 100644
@@ -954,6 +954,19 @@ config RTD119X_WATCHDOG

          Say Y here to include support for the watchdog timer in Realtek RTD1295 SoCs.

drivers/watchdog/Kconfig +13 -0
+config REALTEK_OTTO_WDT
+       tristate "Realtek Otto MIPS watchdog support"
+       depends on MACH_REALTEK_RTL || COMPILE_TEST
+       depends on COMMON_CLK
+       select WATCHDOG_CORE
+       default MACH_REALTEK_RTL
+       help
+         Say Y here to include support for the watchdog timer on Realtek
+         RTL838x, RTL839x, RTL930x SoCs. This watchdog has pretimeout
+         notifications and system reset on timeout.
+
+         When built as a module this will be called realtek_otto_wdt.
+

 config SPRD_WATCHDOG
        tristate "Spreadtrum watchdog support"
        depends on ARCH_SPRD || COMPILE_TEST

drivers/watchdog/Makefile +0 -0
index 1bd2d6f37c53..a8dccf819163 100644
@@ -171,6 +171,7 @@ obj-$(CONFIG_IMGPDC_WDT) += imgpdc_wdt.o

 obj-$(CONFIG_MT7621_WDT) += mt7621_wdt.o obj-$(CONFIG_PIC32_WDT) += pic32-wdt.o obj-$(CONFIG_PIC32_DMT) += pic32-dmt.o +obj-$(CONFIG_REALTEK_OTTO_WDT) += realtek_otto_wdt.o
 # PARISC Architecture

drivers/watchdog/realtek_otto_wdt.c new file +122 -0
new file mode 100644
index 000000000000..48bc8dfefc25
@@ -0,0 +1,361 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+/*
+ * Realtek Otto MIPS platform watchdog
+ *
+ * Watchdog timer that will reset the system after timeout, using the selected
+ * reset mode.
+ *
+ * Counter scaling and timeouts:
+ * - Base prescale of (2 << 25), providing tick duration T_0: 168ms @ 200MHz
+ * - PRESCALE: logarithmic prescaler adding a factor of {1, 2, 4, 8}
+ * - Phase 1: Times out after (PHASE1 + 1) × PRESCALE × T_0
+ *   Generates an interrupt, WDT cannot be stopped after phase 1
+ * - Phase 2: starts after phase 1, times out after (PHASE2 + 1) × PRESCALE × T_0
+ *   Resets the system according to RST_MODE
+ */
+
+#include <linux/bits.h>
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/math.h>
+#include <linux/minmax.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/reboot.h>
+#include <linux/watchdog.h>
+
+#define OTTO_WDT_REG_CNTR              0x0
+#define OTTO_WDT_CNTR_PING             BIT(31)
+
+#define OTTO_WDT_REG_INTR              0x4
+#define OTTO_WDT_INTR_PHASE_1          BIT(31)
+#define OTTO_WDT_INTR_PHASE_2          BIT(30)
+
+#define OTTO_WDT_REG_CTRL              0x8
+#define OTTO_WDT_CTRL_ENABLE           BIT(31)
+#define OTTO_WDT_CTRL_PRESCALE         GENMASK(30, 29)
+#define OTTO_WDT_CTRL_PHASE1           GENMASK(26, 22)
+#define OTTO_WDT_CTRL_PHASE2           GENMASK(19, 15)
+#define OTTO_WDT_CTRL_RST_MODE         GENMASK(1, 0)
+#define OTTO_WDT_MODE_SOC              0
+#define OTTO_WDT_MODE_CPU              1
+#define OTTO_WDT_MODE_SOFTWARE         2
+#define OTTO_WDT_CTRL_DEFAULT          OTTO_WDT_MODE_CPU
+
+#define OTTO_WDT_PRESCALE_MAX          3
+
+/*
+ * One higher than the max values contained in PHASE{1,2}, since a value of 0
+ * corresponds to one tick.
+ */
+#define OTTO_WDT_PHASE_TICKS_MAX       32
+
+/*
+ * The maximum reset delay is actually 2×32 ticks, but that would require large
+ * pretimeout values for timeouts longer than 32 ticks. Limit the maximum timeout
+ * to 32 + 1 to ensure small pretimeout values can be configured as expected.
+ */
+#define OTTO_WDT_TIMEOUT_TICKS_MAX     (OTTO_WDT_PHASE_TICKS_MAX + 1)
+
+struct otto_wdt_ctrl {
+       struct watchdog_device wdev;
+       struct device *dev;
+       void __iomem *base;
+       struct clk *clk;
+       int irq_phase1;
+};
+
+static int otto_wdt_start(struct watchdog_device *wdev)
+{
+       struct otto_wdt_ctrl *ctrl = watchdog_get_drvdata(wdev);
+       u32 v;
+
+       v = ioread32(ctrl->base + OTTO_WDT_REG_CTRL);
+       v |= OTTO_WDT_CTRL_ENABLE;
+       iowrite32(v, ctrl->base + OTTO_WDT_REG_CTRL);
+
+       return 0;
+}
+
+static int otto_wdt_stop(struct watchdog_device *wdev)
+{
+       struct otto_wdt_ctrl *ctrl = watchdog_get_drvdata(wdev);
+       u32 v;
+
+       v = ioread32(ctrl->base + OTTO_WDT_REG_CTRL);
+       v &= ~OTTO_WDT_CTRL_ENABLE;
+       iowrite32(v, ctrl->base + OTTO_WDT_REG_CTRL);
+
+       return 0;
+}
+
+static int otto_wdt_ping(struct watchdog_device *wdev)
+{
+       struct otto_wdt_ctrl *ctrl = watchdog_get_drvdata(wdev);
+
+       iowrite32(OTTO_WDT_CNTR_PING, ctrl->base + OTTO_WDT_REG_CNTR);
+
+       return 0;
+}
+
+static int otto_wdt_tick_ms(struct otto_wdt_ctrl *ctrl, int prescale)
+{
+       unsigned int rate_khz = clk_get_rate(ctrl->clk) / 1000;
+
+       if (!rate_khz)
+               return 0;
+
+       return DIV_ROUND_CLOSEST(1 << (25 + prescale), rate_khz);
+}
+
+/*
+ * The timer asserts the PHASE1/PHASE2 IRQs when the number of ticks exceeds
+ * the value stored in those fields. This means each phase will run for at least
+ * one tick, so small values need to be clamped to correctly reflect the timeout.
+ */
+static inline unsigned int div_round_ticks(unsigned int val, unsigned int

tick_duration,
+               unsigned int min_ticks)

drivers/watchdog/realtek_otto_wdt.c new file +5 -0
+{
+       return max(min_ticks, DIV_ROUND_UP(val, tick_duration));
+}
+
+static int otto_wdt_determine_timeouts(struct watchdog_device *wdev, unsigned int

timeout,
+               unsigned int pretimeout)

drivers/watchdog/realtek_otto_wdt.c new file +141 -0
+{
+       struct otto_wdt_ctrl *ctrl = watchdog_get_drvdata(wdev);
+       unsigned int pretimeout_ms = pretimeout * 1000;
+       unsigned int timeout_ms = timeout * 1000;
+       unsigned int prescale_next = 0;
+       unsigned int phase1_ticks;
+       unsigned int phase2_ticks;
+       unsigned int total_ticks;
+       unsigned int prescale;
+       unsigned int tick_ms;
+       u32 v;
+
+       do {
+               prescale = prescale_next;
+               if (prescale > OTTO_WDT_PRESCALE_MAX)
+                       return -EINVAL;
+
+               tick_ms = otto_wdt_tick_ms(ctrl, prescale);
+               total_ticks = div_round_ticks(timeout_ms, tick_ms, 2);
+               phase1_ticks = div_round_ticks(timeout_ms - pretimeout_ms, tick_ms, 1);
+               phase2_ticks = total_ticks - phase1_ticks;
+
+               prescale_next++;
+       } while (phase1_ticks > OTTO_WDT_PHASE_TICKS_MAX
+               || phase2_ticks > OTTO_WDT_PHASE_TICKS_MAX);
+
+       v = ioread32(ctrl->base + OTTO_WDT_REG_CTRL);
+
+       v &= ~(OTTO_WDT_CTRL_PRESCALE | OTTO_WDT_CTRL_PHASE1 | OTTO_WDT_CTRL_PHASE2);
+       v |= FIELD_PREP(OTTO_WDT_CTRL_PHASE1, phase1_ticks - 1);
+       v |= FIELD_PREP(OTTO_WDT_CTRL_PHASE2, phase2_ticks - 1);
+       v |= FIELD_PREP(OTTO_WDT_CTRL_PRESCALE, prescale);
+
+       iowrite32(v, ctrl->base + OTTO_WDT_REG_CTRL);
+
+       timeout_ms = total_ticks * tick_ms;
+       ctrl->wdev.timeout = timeout_ms / 1000;
+
+       pretimeout_ms = phase2_ticks * tick_ms;
+       ctrl->wdev.pretimeout = pretimeout_ms / 1000;
+
+       return 0;
+}
+
+static int otto_wdt_set_timeout(struct watchdog_device *wdev, unsigned int val)
+{
+       return otto_wdt_determine_timeouts(wdev, val, min(wdev->pretimeout, val - 1));
+}
+
+static int otto_wdt_set_pretimeout(struct watchdog_device *wdev, unsigned int val)
+{
+       return otto_wdt_determine_timeouts(wdev, wdev->timeout, val);
+}
+
+static int otto_wdt_restart(struct watchdog_device *wdev, unsigned long reboot_mode,
+               void *data)
+{
+       struct otto_wdt_ctrl *ctrl = watchdog_get_drvdata(wdev);
+       u32 reset_mode;
+       u32 v;
+
+       disable_irq(ctrl->irq_phase1);
+
+       switch (reboot_mode) {
+       case REBOOT_SOFT:
+               reset_mode = OTTO_WDT_MODE_SOFTWARE;
+               break;
+       case REBOOT_WARM:
+               reset_mode = OTTO_WDT_MODE_CPU;
+               break;
+       default:
+               reset_mode = OTTO_WDT_MODE_SOC;
+               break;
+       }
+
+       /* Configure for shortest timeout and wait for reset to occur */
+       v = FIELD_PREP(OTTO_WDT_CTRL_RST_MODE, reset_mode) | OTTO_WDT_CTRL_ENABLE;
+       iowrite32(v, ctrl->base + OTTO_WDT_REG_CTRL);
+
+       mdelay(3 * otto_wdt_tick_ms(ctrl, 0));
+
+       return 0;
+}
+
+static irqreturn_t otto_wdt_phase1_isr(int irq, void *dev_id)
+{
+       struct otto_wdt_ctrl *ctrl = dev_id;
+
+       iowrite32(OTTO_WDT_INTR_PHASE_1, ctrl->base + OTTO_WDT_REG_INTR);
+       dev_crit(ctrl->dev, "phase 1 timeout\n");
+       watchdog_notify_pretimeout(&ctrl->wdev);
+
+       return IRQ_HANDLED;
+}
+
+static const struct watchdog_ops otto_wdt_ops = {
+       .owner = THIS_MODULE,
+       .start = otto_wdt_start,
+       .stop = otto_wdt_stop,
+       .ping = otto_wdt_ping,
+       .set_timeout = otto_wdt_set_timeout,
+       .set_pretimeout = otto_wdt_set_pretimeout,
+       .restart = otto_wdt_restart,
+};
+
+static const struct watchdog_info otto_wdt_info = {
+       .identity = "Realtek Otto watchdog timer",
+       .options = WDIOF_KEEPALIVEPING |
+               WDIOF_MAGICCLOSE |
+               WDIOF_SETTIMEOUT |
+               WDIOF_PRETIMEOUT,
+};
+
+static int otto_wdt_probe_reset_mode(struct otto_wdt_ctrl *ctrl)
+{
+       static const char *mode_property = "realtek,reset-mode";
+       const struct fwnode_handle *node = ctrl->dev->fwnode;
+       int mode_count;
+       u32 mode;
+       u32 v;
+
+       if (!node)
+               return -ENXIO;
+
+       mode_count = fwnode_property_string_array_count(node, mode_property);
+       if (mode_count < 0)
+               return mode_count;
+       else if (mode_count == 0)
+               return 0;
+       else if (mode_count != 1)
+               return -EINVAL;
+
+       if (fwnode_property_match_string(node, mode_property, "soc") == 0)
+               mode = OTTO_WDT_MODE_SOC;
+       else if (fwnode_property_match_string(node, mode_property, "cpu") == 0)
+               mode = OTTO_WDT_MODE_CPU;
+       else if (fwnode_property_match_string(node, mode_property, "software") == 0)
+               mode = OTTO_WDT_MODE_SOFTWARE;
+       else
+               return -EINVAL;
+
Quoted 8 lines

drivers/watchdog/realtek_otto_wdt.c line 85

Quoted 8 lines

+       v = ioread32(ctrl->base + OTTO_WDT_REG_CTRL); +       v &= ~OTTO_WDT_CTRL_RST_MODE;
+       v |= FIELD_PREP(OTTO_WDT_CTRL_RST_MODE, mode); +       iowrite32(v, ctrl->base + OTTO_WDT_REG_CTRL);

drivers/watchdog/realtek_otto_wdt.c new file +4 -0
+
+       return 0;
+}
+

This is the first time I'm using the clocks framework. Since I'm currently using a fixed- clock node in my DTS, I guess this clock is just 'always on'. As I understand, I should also use clock_{prepare_enable,disable_unprepare}.

I would add the following small function...

-- >8 --
+static void otto_wdt_clock_action(void *data)
+{
+ clk_disable_unprepare(data);
+}
+
-- 8< --

Quoted 16 lines
Quoted 16 lines
drivers/watchdog/realtek_otto_wdt.c new file +16 -0
+static int otto_wdt_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct otto_wdt_ctrl *ctrl;
+       unsigned int max_tick_ms;
+       int ret;
+
+       ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL);
+       if (!ctrl)
+               return -ENOMEM;
+
+       ctrl->dev = dev;
+       ctrl->base = devm_platform_ioremap_resource(pdev, 0);
+       if (IS_ERR(ctrl->base))
+               return PTR_ERR(ctrl->base);
+
Quoted 8 lines
Quoted 8 lines

+       /* Clear any old interrupts and reset initial state */
+       iowrite32(OTTO_WDT_INTR_PHASE_1 | OTTO_WDT_INTR_PHASE_2,
+                       ctrl->base + OTTO_WDT_REG_INTR);
+       iowrite32(OTTO_WDT_CTRL_DEFAULT, ctrl->base + OTTO_WDT_REG_CTRL); +
+       ctrl->clk = devm_clk_get(dev, NULL);
+       if (IS_ERR(ctrl->clk))
+               return dev_err_probe(dev, PTR_ERR(ctrl->clk), "Failed to get clock\n");

... and these checks when probing:

-- >8 --
+ ret = clk_prepare_enable(ctrl->clk);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to enable clock\n");
+
+ ret = devm_add_action_or_reset(dev, otto_wdt_clock_action, ctrl->clk);
+ if (ret)
+ return ret;
-- 8< --

Best,
Sander

Quoted 62 lines
Quoted 62 lines
drivers/watchdog/realtek_otto_wdt.c new file +59 -0
+
+       ctrl->irq_phase1 = platform_get_irq_byname(pdev, "phase1");
+       if (ctrl->irq_phase1 < 0)
+               return dev_err_probe(dev, ctrl->irq_phase1, "phase1 IRQ not found\n");
+
+       ret = devm_request_irq(dev, ctrl->irq_phase1, otto_wdt_phase1_isr, 0,
+                       "realtek-otto-wdt", ctrl);
+       if (ret)
+               return dev_err_probe(dev, ret, "Failed to get IRQ for phase1\n");
+
+       ret = otto_wdt_probe_reset_mode(ctrl);
+       if (ret)
+               return dev_err_probe(dev, ret, "Invalid reset mode specified\n");
+
+       ctrl->wdev.parent = dev;
+       ctrl->wdev.info = &otto_wdt_info;
+       ctrl->wdev.ops = &otto_wdt_ops;
+
+       /*
+        * Since pretimeout cannot be disabled, min. timeout is twice the
+        * subsystem resolution. max. timeout is ca. 43s at a bus clock of 200MHz.
+        */
+       ctrl->wdev.min_timeout = 2;
+       max_tick_ms = otto_wdt_tick_ms(ctrl, OTTO_WDT_PRESCALE_MAX);
+       ctrl->wdev.max_hw_heartbeat_ms = max_tick_ms * OTTO_WDT_TIMEOUT_TICKS_MAX;
+       ctrl->wdev.timeout = min(30U, ctrl->wdev.max_hw_heartbeat_ms / 1000);
+
+       watchdog_set_drvdata(&ctrl->wdev, ctrl);
+       watchdog_init_timeout(&ctrl->wdev, 0, dev);
+       watchdog_stop_on_reboot(&ctrl->wdev);
+       watchdog_set_restart_priority(&ctrl->wdev, 128);
+
+       ret = otto_wdt_determine_timeouts(&ctrl->wdev, ctrl->wdev.timeout, 1);
+       if (ret)
+               return dev_err_probe(dev, ret, "Failed to set timeout\n");
+
+       return devm_watchdog_register_device(dev, &ctrl->wdev);
+}
+
+static const struct of_device_id otto_wdt_ids[] = {
+       { .compatible = "realtek,rtl8380-wdt" },
+       { .compatible = "realtek,rtl8390-wdt" },
+       { .compatible = "realtek,rtl9300-wdt" },
+       { }
+};
+MODULE_DEVICE_TABLE(of, otto_wdt_ids);
+
+static struct platform_driver otto_wdt_driver = {
+       .probe = otto_wdt_probe,
+       .driver = {
+               .name = "realtek-otto-watchdog",
+               .of_match_table = otto_wdt_ids,
+       },
+};
+module_platform_driver(otto_wdt_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Sander Vanheule <[email protected]>");
+MODULE_DESCRIPTION("Realtek Otto watchdog timer driver");
Signature · 1 line
2.31.1
maintainer
#6 lore raw

Re: [PATCH v3 2/2] watchdog: Add Realtek Otto watchdog timer

Quoted 19 lines · Sander Vanheule

Hi Guenter,

Quoted 16 lines · Guenter Roeck
Quoted 15 lines · Sander Vanheule

Realtek MIPS SoCs (platform name Otto) have a watchdog timer with pretimeout notifitication support. The WDT can (partially) hard reset, or soft reset the SoC.

This driver implements all features as described in the devicetree binding, except the phase2 interrupt, and also functions as a restart handler. The cpu reset mode is considered to be a "warm" restart, since this mode does not reset all peripherals. Being an embedded system though, the "cpu" and "software" modes will still cause the bootloader to run on restart.

It is not known how a forced system reset can be disabled on the supported platforms. This means that the phase2 interrupt will only fire at the same time as reset, so implementing phase2 is of little use.

Quoted 8 lines
Quoted 3 lines
Quoted 1 line

Thank you for the review! In the meantime, I was preparing a v4 of this series with some small changes (see inline below). Could you let me know if I can keep your Reviewed-by with those changes?

I think it would be better to re-review it.

Also, if the clock rate desn't change, you should really read the rate once in probe, make sure it is sane, and store the result in struct otto_wdt_ctrl.

Thanks,
Guenter

Quoted 490 lines
Quoted 5 lines
Quoted 3 lines

--

Note for the future: Change log should be after '---'.

Will do!

Quoted 355 lines
Quoted 355 lines

v2 -> v3:
- fix off-by-one error in PRESCALE assignment

v1 -> v2:
- drop raw_spinlock, locking is not required
- replace devm_free_irq with disable_irq
- use max_hw_heartbeat_ms instead of max_timeout
- drop redundant timeout value checks
- replace ROUND_CLOSEST by ROUND_UP for tick counts, and flooring division for timeout values
- change COMMON_CLK 'select' into 'depends on'
- add MODULE_DEVICE_TABLE for OF ID table
- add realtek,rtl9300 compatible
- drop phase2 irq

---
  MAINTAINERS                         |   7 +
  drivers/watchdog/Kconfig            |  13 +
  drivers/watchdog/Makefile           |   1 +
  drivers/watchdog/realtek_otto_wdt.c | 361 ++++++++++++++++++++++++++++
  4 files changed, 382 insertions(+)

 create mode 100644 drivers/watchdog/realtek_otto_wdt.c

MAINTAINERS +7 -0
index 2c9070aeba2a..54c8f788d3e5 100644
@@ -15991,6 +15991,13 @@ S:     Maintained
  F:     include/sound/rt*.h
  F:     sound/soc/codecs/rt*
 
+REALTEK OTTO WATCHDOG
+M:     Sander Vanheule <[email protected]>
+L:     [email protected]
+S:     Maintained
+F:     Documentation/devicetree/bindings/watchdog/realtek,otto-wdt.yaml
+F:     driver/watchdog/realtek_otto_wdt.c
+
  REALTEK RTL83xx SMI DSA ROUTER CHIPS
  M:     Linus Walleij <[email protected]>
  S:     Maintained
drivers/watchdog/Kconfig +13 -0
index bf59faeb3de1..d308e13a9aa1 100644
@@ -954,6 +954,19 @@ config RTD119X_WATCHDOG
           Say Y here to include support for the watchdog timer in
           Realtek RTD1295 SoCs.
 
+config REALTEK_OTTO_WDT
+       tristate "Realtek Otto MIPS watchdog support"
+       depends on MACH_REALTEK_RTL || COMPILE_TEST
+       depends on COMMON_CLK
+       select WATCHDOG_CORE
+       default MACH_REALTEK_RTL
+       help
+         Say Y here to include support for the watchdog timer on Realtek
+         RTL838x, RTL839x, RTL930x SoCs. This watchdog has pretimeout
+         notifications and system reset on timeout.
+
+         When built as a module this will be called realtek_otto_wdt.
+
  config SPRD_WATCHDOG
         tristate "Spreadtrum watchdog support"
         depends on ARCH_SPRD || COMPILE_TEST
drivers/watchdog/Makefile +1 -0
index 1bd2d6f37c53..a8dccf819163 100644
@@ -171,6 +171,7 @@ obj-$(CONFIG_IMGPDC_WDT) += imgpdc_wdt.o
  obj-$(CONFIG_MT7621_WDT) += mt7621_wdt.o
  obj-$(CONFIG_PIC32_WDT) += pic32-wdt.o
  obj-$(CONFIG_PIC32_DMT) += pic32-dmt.o
+obj-$(CONFIG_REALTEK_OTTO_WDT) += realtek_otto_wdt.o
 
  # PARISC Architecture
 
drivers/watchdog/realtek_otto_wdt.c new file +122 -0
new file mode 100644
index 000000000000..48bc8dfefc25
@@ -0,0 +1,361 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+/*
+ * Realtek Otto MIPS platform watchdog
+ *
+ * Watchdog timer that will reset the system after timeout, using the selected
+ * reset mode.
+ *
+ * Counter scaling and timeouts:
+ * - Base prescale of (2 << 25), providing tick duration T_0: 168ms @ 200MHz
+ * - PRESCALE: logarithmic prescaler adding a factor of {1, 2, 4, 8}
+ * - Phase 1: Times out after (PHASE1 + 1) × PRESCALE × T_0
+ *   Generates an interrupt, WDT cannot be stopped after phase 1
+ * - Phase 2: starts after phase 1, times out after (PHASE2 + 1) × PRESCALE × T_0
+ *   Resets the system according to RST_MODE
+ */
+
+#include <linux/bits.h>
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/math.h>
+#include <linux/minmax.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/reboot.h>
+#include <linux/watchdog.h>
+
+#define OTTO_WDT_REG_CNTR              0x0
+#define OTTO_WDT_CNTR_PING             BIT(31)
+
+#define OTTO_WDT_REG_INTR              0x4
+#define OTTO_WDT_INTR_PHASE_1          BIT(31)
+#define OTTO_WDT_INTR_PHASE_2          BIT(30)
+
+#define OTTO_WDT_REG_CTRL              0x8
+#define OTTO_WDT_CTRL_ENABLE           BIT(31)
+#define OTTO_WDT_CTRL_PRESCALE         GENMASK(30, 29)
+#define OTTO_WDT_CTRL_PHASE1           GENMASK(26, 22)
+#define OTTO_WDT_CTRL_PHASE2           GENMASK(19, 15)
+#define OTTO_WDT_CTRL_RST_MODE         GENMASK(1, 0)
+#define OTTO_WDT_MODE_SOC              0
+#define OTTO_WDT_MODE_CPU              1
+#define OTTO_WDT_MODE_SOFTWARE         2
+#define OTTO_WDT_CTRL_DEFAULT          OTTO_WDT_MODE_CPU
+
+#define OTTO_WDT_PRESCALE_MAX          3
+
+/*
+ * One higher than the max values contained in PHASE{1,2}, since a value of 0
+ * corresponds to one tick.
+ */
+#define OTTO_WDT_PHASE_TICKS_MAX       32
+
+/*
+ * The maximum reset delay is actually 2×32 ticks, but that would require large
+ * pretimeout values for timeouts longer than 32 ticks. Limit the maximum timeout
+ * to 32 + 1 to ensure small pretimeout values can be configured as expected.
+ */
+#define OTTO_WDT_TIMEOUT_TICKS_MAX     (OTTO_WDT_PHASE_TICKS_MAX + 1)
+
+struct otto_wdt_ctrl {
+       struct watchdog_device wdev;
+       struct device *dev;
+       void __iomem *base;
+       struct clk *clk;
+       int irq_phase1;
+};
+
+static int otto_wdt_start(struct watchdog_device *wdev)
+{
+       struct otto_wdt_ctrl *ctrl = watchdog_get_drvdata(wdev);
+       u32 v;
+
+       v = ioread32(ctrl->base + OTTO_WDT_REG_CTRL);
+       v |= OTTO_WDT_CTRL_ENABLE;
+       iowrite32(v, ctrl->base + OTTO_WDT_REG_CTRL);
+
+       return 0;
+}
+
+static int otto_wdt_stop(struct watchdog_device *wdev)
+{
+       struct otto_wdt_ctrl *ctrl = watchdog_get_drvdata(wdev);
+       u32 v;
+
+       v = ioread32(ctrl->base + OTTO_WDT_REG_CTRL);
+       v &= ~OTTO_WDT_CTRL_ENABLE;
+       iowrite32(v, ctrl->base + OTTO_WDT_REG_CTRL);
+
+       return 0;
+}
+
+static int otto_wdt_ping(struct watchdog_device *wdev)
+{
+       struct otto_wdt_ctrl *ctrl = watchdog_get_drvdata(wdev);
+
+       iowrite32(OTTO_WDT_CNTR_PING, ctrl->base + OTTO_WDT_REG_CNTR);
+
+       return 0;
+}
+
+static int otto_wdt_tick_ms(struct otto_wdt_ctrl *ctrl, int prescale)
+{
+       unsigned int rate_khz = clk_get_rate(ctrl->clk) / 1000;
+
+       if (!rate_khz)
+               return 0;
+
+       return DIV_ROUND_CLOSEST(1 << (25 + prescale), rate_khz);
+}
+
+/*
+ * The timer asserts the PHASE1/PHASE2 IRQs when the number of ticks exceeds
+ * the value stored in those fields. This means each phase will run for at least
+ * one tick, so small values need to be clamped to correctly reflect the timeout.
+ */
+static inline unsigned int div_round_ticks(unsigned int val, unsigned int

tick_duration,
+               unsigned int min_ticks)

drivers/watchdog/realtek_otto_wdt.c new file +5 -0
+{
+       return max(min_ticks, DIV_ROUND_UP(val, tick_duration));
+}
+
+static int otto_wdt_determine_timeouts(struct watchdog_device *wdev, unsigned int

timeout,
+               unsigned int pretimeout)

drivers/watchdog/realtek_otto_wdt.c new file +141 -0
+{
+       struct otto_wdt_ctrl *ctrl = watchdog_get_drvdata(wdev);
+       unsigned int pretimeout_ms = pretimeout * 1000;
+       unsigned int timeout_ms = timeout * 1000;
+       unsigned int prescale_next = 0;
+       unsigned int phase1_ticks;
+       unsigned int phase2_ticks;
+       unsigned int total_ticks;
+       unsigned int prescale;
+       unsigned int tick_ms;
+       u32 v;
+
+       do {
+               prescale = prescale_next;
+               if (prescale > OTTO_WDT_PRESCALE_MAX)
+                       return -EINVAL;
+
+               tick_ms = otto_wdt_tick_ms(ctrl, prescale);
+               total_ticks = div_round_ticks(timeout_ms, tick_ms, 2);
+               phase1_ticks = div_round_ticks(timeout_ms - pretimeout_ms, tick_ms, 1);
+               phase2_ticks = total_ticks - phase1_ticks;
+
+               prescale_next++;
+       } while (phase1_ticks > OTTO_WDT_PHASE_TICKS_MAX
+               || phase2_ticks > OTTO_WDT_PHASE_TICKS_MAX);
+
+       v = ioread32(ctrl->base + OTTO_WDT_REG_CTRL);
+
+       v &= ~(OTTO_WDT_CTRL_PRESCALE | OTTO_WDT_CTRL_PHASE1 | OTTO_WDT_CTRL_PHASE2);
+       v |= FIELD_PREP(OTTO_WDT_CTRL_PHASE1, phase1_ticks - 1);
+       v |= FIELD_PREP(OTTO_WDT_CTRL_PHASE2, phase2_ticks - 1);
+       v |= FIELD_PREP(OTTO_WDT_CTRL_PRESCALE, prescale);
+
+       iowrite32(v, ctrl->base + OTTO_WDT_REG_CTRL);
+
+       timeout_ms = total_ticks * tick_ms;
+       ctrl->wdev.timeout = timeout_ms / 1000;
+
+       pretimeout_ms = phase2_ticks * tick_ms;
+       ctrl->wdev.pretimeout = pretimeout_ms / 1000;
+
+       return 0;
+}
+
+static int otto_wdt_set_timeout(struct watchdog_device *wdev, unsigned int val)
+{
+       return otto_wdt_determine_timeouts(wdev, val, min(wdev->pretimeout, val - 1));
+}
+
+static int otto_wdt_set_pretimeout(struct watchdog_device *wdev, unsigned int val)
+{
+       return otto_wdt_determine_timeouts(wdev, wdev->timeout, val);
+}
+
+static int otto_wdt_restart(struct watchdog_device *wdev, unsigned long reboot_mode,
+               void *data)
+{
+       struct otto_wdt_ctrl *ctrl = watchdog_get_drvdata(wdev);
+       u32 reset_mode;
+       u32 v;
+
+       disable_irq(ctrl->irq_phase1);
+
+       switch (reboot_mode) {
+       case REBOOT_SOFT:
+               reset_mode = OTTO_WDT_MODE_SOFTWARE;
+               break;
+       case REBOOT_WARM:
+               reset_mode = OTTO_WDT_MODE_CPU;
+               break;
+       default:
+               reset_mode = OTTO_WDT_MODE_SOC;
+               break;
+       }
+
+       /* Configure for shortest timeout and wait for reset to occur */
+       v = FIELD_PREP(OTTO_WDT_CTRL_RST_MODE, reset_mode) | OTTO_WDT_CTRL_ENABLE;
+       iowrite32(v, ctrl->base + OTTO_WDT_REG_CTRL);
+
+       mdelay(3 * otto_wdt_tick_ms(ctrl, 0));
+
+       return 0;
+}
+
+static irqreturn_t otto_wdt_phase1_isr(int irq, void *dev_id)
+{
+       struct otto_wdt_ctrl *ctrl = dev_id;
+
+       iowrite32(OTTO_WDT_INTR_PHASE_1, ctrl->base + OTTO_WDT_REG_INTR);
+       dev_crit(ctrl->dev, "phase 1 timeout\n");
+       watchdog_notify_pretimeout(&ctrl->wdev);
+
+       return IRQ_HANDLED;
+}
+
+static const struct watchdog_ops otto_wdt_ops = {
+       .owner = THIS_MODULE,
+       .start = otto_wdt_start,
+       .stop = otto_wdt_stop,
+       .ping = otto_wdt_ping,
+       .set_timeout = otto_wdt_set_timeout,
+       .set_pretimeout = otto_wdt_set_pretimeout,
+       .restart = otto_wdt_restart,
+};
+
+static const struct watchdog_info otto_wdt_info = {
+       .identity = "Realtek Otto watchdog timer",
+       .options = WDIOF_KEEPALIVEPING |
+               WDIOF_MAGICCLOSE |
+               WDIOF_SETTIMEOUT |
+               WDIOF_PRETIMEOUT,
+};
+
+static int otto_wdt_probe_reset_mode(struct otto_wdt_ctrl *ctrl)
+{
+       static const char *mode_property = "realtek,reset-mode";
+       const struct fwnode_handle *node = ctrl->dev->fwnode;
+       int mode_count;
+       u32 mode;
+       u32 v;
+
+       if (!node)
+               return -ENXIO;
+
+       mode_count = fwnode_property_string_array_count(node, mode_property);
+       if (mode_count < 0)
+               return mode_count;
+       else if (mode_count == 0)
+               return 0;
+       else if (mode_count != 1)
+               return -EINVAL;
+
+       if (fwnode_property_match_string(node, mode_property, "soc") == 0)
+               mode = OTTO_WDT_MODE_SOC;
+       else if (fwnode_property_match_string(node, mode_property, "cpu") == 0)
+               mode = OTTO_WDT_MODE_CPU;
+       else if (fwnode_property_match_string(node, mode_property, "software") == 0)
+               mode = OTTO_WDT_MODE_SOFTWARE;
+       else
+               return -EINVAL;
+
Quoted 8 lines

drivers/watchdog/realtek_otto_wdt.c line 85

Quoted 8 lines

+       v = ioread32(ctrl->base + OTTO_WDT_REG_CTRL); +       v &= ~OTTO_WDT_CTRL_RST_MODE;
+       v |= FIELD_PREP(OTTO_WDT_CTRL_RST_MODE, mode); +       iowrite32(v, ctrl->base + OTTO_WDT_REG_CTRL);

drivers/watchdog/realtek_otto_wdt.c new file +4 -0
+
+       return 0;
+}
+

This is the first time I'm using the clocks framework. Since I'm currently using a fixed- clock node in my DTS, I guess this clock is just 'always on'. As I understand, I should also use clock_{prepare_enable,disable_unprepare}.

I would add the following small function...

-- >8 --

drivers/watchdog/realtek_otto_wdt.c new file +5 -1
+static void otto_wdt_clock_action(void *data)
+{
+       clk_disable_unprepare(data);
+}
+
-- 8< --
Quoted 17 lines
Quoted 17 lines
drivers/watchdog/realtek_otto_wdt.c new file +16 -0
+static int otto_wdt_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct otto_wdt_ctrl *ctrl;
+       unsigned int max_tick_ms;
+       int ret;
+
+       ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL);
+       if (!ctrl)
+               return -ENOMEM;
+
+       ctrl->dev = dev;
+       ctrl->base = devm_platform_ioremap_resource(pdev, 0);
+       if (IS_ERR(ctrl->base))
+               return PTR_ERR(ctrl->base);
+
Quoted 8 lines
Quoted 8 lines

+       /* Clear any old interrupts and reset initial state */
+       iowrite32(OTTO_WDT_INTR_PHASE_1 | OTTO_WDT_INTR_PHASE_2,
+                       ctrl->base + OTTO_WDT_REG_INTR);
+       iowrite32(OTTO_WDT_CTRL_DEFAULT, ctrl->base + OTTO_WDT_REG_CTRL); +
+       ctrl->clk = devm_clk_get(dev, NULL);
+       if (IS_ERR(ctrl->clk))
+               return dev_err_probe(dev, PTR_ERR(ctrl->clk), "Failed to get clock\n");

... and these checks when probing:

-- >8 --

(quoted diff) +7 -1
+       ret = clk_prepare_enable(ctrl->clk);
+       if (ret)
+               return dev_err_probe(dev, ret, "Failed to enable clock\n");
+
+       ret = devm_add_action_or_reset(dev, otto_wdt_clock_action, ctrl->clk);
+       if (ret)
+               return ret;
-- 8< --

Best,
Sander

Quoted 62 lines
Quoted 62 lines
drivers/watchdog/realtek_otto_wdt.c new file +59 -0
+
+       ctrl->irq_phase1 = platform_get_irq_byname(pdev, "phase1");
+       if (ctrl->irq_phase1 < 0)
+               return dev_err_probe(dev, ctrl->irq_phase1, "phase1 IRQ not found\n");
+
+       ret = devm_request_irq(dev, ctrl->irq_phase1, otto_wdt_phase1_isr, 0,
+                       "realtek-otto-wdt", ctrl);
+       if (ret)
+               return dev_err_probe(dev, ret, "Failed to get IRQ for phase1\n");
+
+       ret = otto_wdt_probe_reset_mode(ctrl);
+       if (ret)
+               return dev_err_probe(dev, ret, "Invalid reset mode specified\n");
+
+       ctrl->wdev.parent = dev;
+       ctrl->wdev.info = &otto_wdt_info;
+       ctrl->wdev.ops = &otto_wdt_ops;
+
+       /*
+        * Since pretimeout cannot be disabled, min. timeout is twice the
+        * subsystem resolution. max. timeout is ca. 43s at a bus clock of 200MHz.
+        */
+       ctrl->wdev.min_timeout = 2;
+       max_tick_ms = otto_wdt_tick_ms(ctrl, OTTO_WDT_PRESCALE_MAX);
+       ctrl->wdev.max_hw_heartbeat_ms = max_tick_ms * OTTO_WDT_TIMEOUT_TICKS_MAX;
+       ctrl->wdev.timeout = min(30U, ctrl->wdev.max_hw_heartbeat_ms / 1000);
+
+       watchdog_set_drvdata(&ctrl->wdev, ctrl);
+       watchdog_init_timeout(&ctrl->wdev, 0, dev);
+       watchdog_stop_on_reboot(&ctrl->wdev);
+       watchdog_set_restart_priority(&ctrl->wdev, 128);
+
+       ret = otto_wdt_determine_timeouts(&ctrl->wdev, ctrl->wdev.timeout, 1);
+       if (ret)
+               return dev_err_probe(dev, ret, "Failed to set timeout\n");
+
+       return devm_watchdog_register_device(dev, &ctrl->wdev);
+}
+
+static const struct of_device_id otto_wdt_ids[] = {
+       { .compatible = "realtek,rtl8380-wdt" },
+       { .compatible = "realtek,rtl8390-wdt" },
+       { .compatible = "realtek,rtl9300-wdt" },
+       { }
+};
+MODULE_DEVICE_TABLE(of, otto_wdt_ids);
+
+static struct platform_driver otto_wdt_driver = {
+       .probe = otto_wdt_probe,
+       .driver = {
+               .name = "realtek-otto-watchdog",
+               .of_match_table = otto_wdt_ids,
+       },
+};
+module_platform_driver(otto_wdt_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Sander Vanheule <[email protected]>");
+MODULE_DESCRIPTION("Realtek Otto watchdog timer driver");
Signature · 1 line
2.31.1
author maintainer
#7 lore raw

Re: [PATCH v3 2/2] watchdog: Add Realtek Otto watchdog timer

Quoted 31 lines · Guenter Roeck
Quoted 21 lines · Sander Vanheule

Hi Guenter,

Quoted 18 lines · Guenter Roeck
Quoted 16 lines · Sander Vanheule

Realtek MIPS SoCs (platform name Otto) have a watchdog timer with pretimeout notifitication support. The WDT can (partially) hard reset, or soft reset the SoC.

This driver implements all features as described in the devicetree binding, except the phase2 interrupt, and also functions as a restart handler. The cpu reset mode is considered to be a "warm" restart, since this mode does not reset all peripherals. Being an embedded system though, the "cpu" and "software" modes will still cause the bootloader to run on restart.

It is not known how a forced system reset can be disabled on the supported platforms. This means that the phase2 interrupt will only fire at the same time as reset, so implementing phase2 is of little use.

Quoted 8 lines
Quoted 1 line

Thank you for the review! In the meantime, I was preparing a v4 of this series with some
small changes (see inline below). Could you let me know if I can keep your Reviewed-by
with those changes?

Quoted 5 lines

I think it would be better to re-review it.

Also, if the clock rate desn't change, you should really read the rate once in probe,
make sure it is sane, and store the result in struct otto_wdt_ctrl.

Thanks for the update. I'll refresh the patches later today and send out the v4.

Best,
Sander

Quoted 536 lines

Thanks,
Guenter

Quoted 531 lines
Quoted 5 lines
Quoted 3 lines

--

Note for the future: Change log should be after '---'.

Will do!

Quoted 381 lines
Quoted 381 lines

v2 -> v3:
- fix off-by-one error in PRESCALE assignment

v1 -> v2:
- drop raw_spinlock, locking is not required
- replace devm_free_irq with disable_irq
- use max_hw_heartbeat_ms instead of max_timeout
- drop redundant timeout value checks
- replace ROUND_CLOSEST by ROUND_UP for tick counts, and flooring division for timeout values
- change COMMON_CLK 'select' into 'depends on'
- add MODULE_DEVICE_TABLE for OF ID table
- add realtek,rtl9300 compatible
- drop phase2 irq
---
  MAINTAINERS                         |   7 + drivers/watchdog/Kconfig            |  13 + drivers/watchdog/Makefile           |   1 + drivers/watchdog/realtek_otto_wdt.c | 361 ++++++++++++++++++++++++++++

  4 files changed, 382 insertions(+)

  create mode 100644 drivers/watchdog/realtek_otto_wdt.c

MAINTAINERS +0 -0
index 2c9070aeba2a..54c8f788d3e5 100644
@@ -15991,6 +15991,13 @@ S:     Maintained

  F:     include/sound/rt*.h
  F:     sound/soc/codecs/rt*

MAINTAINERS +7 -0
+REALTEK OTTO WATCHDOG
+M:     Sander Vanheule <[email protected]>
+L:     [email protected]
+S:     Maintained
+F:     Documentation/devicetree/bindings/watchdog/realtek,otto-wdt.yaml
+F:     driver/watchdog/realtek_otto_wdt.c
+

  REALTEK RTL83xx SMI DSA ROUTER CHIPS
  M:     Linus Walleij <[email protected]>
  S:     Maintained

drivers/watchdog/Kconfig +0 -0
index bf59faeb3de1..d308e13a9aa1 100644
@@ -954,6 +954,19 @@ config RTD119X_WATCHDOG

           Say Y here to include support for the watchdog timer in Realtek RTD1295 SoCs.

+config REALTEK_OTTO_WDT
+       tristate "Realtek Otto MIPS watchdog support"
+       depends on MACH_REALTEK_RTL || COMPILE_TEST
+       depends on COMMON_CLK
+       select WATCHDOG_CORE
+       default MACH_REALTEK_RTL
+       help
+         Say Y here to include support for the watchdog timer on Realtek
+         RTL838x, RTL839x, RTL930x SoCs. This watchdog has pretimeout +         notifications and system reset on timeout.

drivers/watchdog/Kconfig +3 -0
+
+         When built as a module this will be called realtek_otto_wdt.
+

  config SPRD_WATCHDOG
         tristate "Spreadtrum watchdog support"
         depends on ARCH_SPRD || COMPILE_TEST

drivers/watchdog/Makefile +0 -0
index 1bd2d6f37c53..a8dccf819163 100644
@@ -171,6 +171,7 @@ obj-$(CONFIG_IMGPDC_WDT) += imgpdc_wdt.o

  obj-$(CONFIG_MT7621_WDT) += mt7621_wdt.o obj-$(CONFIG_PIC32_WDT) += pic32-wdt.o obj-$(CONFIG_PIC32_DMT) += pic32-dmt.o +obj-$(CONFIG_REALTEK_OTTO_WDT) += realtek_otto_wdt.o
  # PARISC Architecture

drivers/watchdog/realtek_otto_wdt.c new file +6 -0
new file mode 100644
index 000000000000..48bc8dfefc25
@@ -0,0 +1,361 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+/*
+ * Realtek Otto MIPS platform watchdog
+ *
+ * Watchdog timer that will reset the system after timeout, using the

selected
+ * reset mode.
+ *
+ * Counter scaling and timeouts:
+ * - Base prescale of (2 << 25), providing tick duration T_0: 168ms @ 200MHz
+ * - PRESCALE: logarithmic prescaler adding a factor of {1, 2, 4, 8}
+ * - Phase 1: Times out after (PHASE1 + 1) × PRESCALE × T_0
+ *   Generates an interrupt, WDT cannot be stopped after phase 1
+ * - Phase 2: starts after phase 1, times out after (PHASE2 + 1) × PRESCALE × T_0
+ *   Resets the system according to RST_MODE
+ */

drivers/watchdog/realtek_otto_wdt.c new file +38 -0
+
+#include <linux/bits.h>
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/math.h>
+#include <linux/minmax.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/reboot.h>
+#include <linux/watchdog.h>
+
+#define OTTO_WDT_REG_CNTR              0x0
+#define OTTO_WDT_CNTR_PING             BIT(31)
+
+#define OTTO_WDT_REG_INTR              0x4
+#define OTTO_WDT_INTR_PHASE_1          BIT(31)
+#define OTTO_WDT_INTR_PHASE_2          BIT(30)
+
+#define OTTO_WDT_REG_CTRL              0x8
+#define OTTO_WDT_CTRL_ENABLE           BIT(31)
+#define OTTO_WDT_CTRL_PRESCALE         GENMASK(30, 29)
+#define OTTO_WDT_CTRL_PHASE1           GENMASK(26, 22)
+#define OTTO_WDT_CTRL_PHASE2           GENMASK(19, 15)
+#define OTTO_WDT_CTRL_RST_MODE         GENMASK(1, 0)
+#define OTTO_WDT_MODE_SOC              0
+#define OTTO_WDT_MODE_CPU              1
+#define OTTO_WDT_MODE_SOFTWARE         2
+#define OTTO_WDT_CTRL_DEFAULT          OTTO_WDT_MODE_CPU
+
+#define OTTO_WDT_PRESCALE_MAX          3
+
+/*
+ * One higher than the max values contained in PHASE{1,2}, since a

value of 0
+ * corresponds to one tick.
+ */

drivers/watchdog/realtek_otto_wdt.c new file +4 -0
+#define OTTO_WDT_PHASE_TICKS_MAX       32
+
+/*
+ * The maximum reset delay is actually 2×32 ticks, but that would

require large
+ * pretimeout values for timeouts longer than 32 ticks. Limit the maximum timeout
+ * to 32 + 1 to ensure small pretimeout values can be configured as expected.
+ */

drivers/watchdog/realtek_otto_wdt.c new file +55 -0
+#define OTTO_WDT_TIMEOUT_TICKS_MAX     (OTTO_WDT_PHASE_TICKS_MAX + 1)
+
+struct otto_wdt_ctrl {
+       struct watchdog_device wdev;
+       struct device *dev;
+       void __iomem *base;
+       struct clk *clk;
+       int irq_phase1;
+};
+
+static int otto_wdt_start(struct watchdog_device *wdev)
+{
+       struct otto_wdt_ctrl *ctrl = watchdog_get_drvdata(wdev);
+       u32 v;
+
+       v = ioread32(ctrl->base + OTTO_WDT_REG_CTRL);
+       v |= OTTO_WDT_CTRL_ENABLE;
+       iowrite32(v, ctrl->base + OTTO_WDT_REG_CTRL);
+
+       return 0;
+}
+
+static int otto_wdt_stop(struct watchdog_device *wdev)
+{
+       struct otto_wdt_ctrl *ctrl = watchdog_get_drvdata(wdev);
+       u32 v;
+
+       v = ioread32(ctrl->base + OTTO_WDT_REG_CTRL);
+       v &= ~OTTO_WDT_CTRL_ENABLE;
+       iowrite32(v, ctrl->base + OTTO_WDT_REG_CTRL);
+
+       return 0;
+}
+
+static int otto_wdt_ping(struct watchdog_device *wdev)
+{
+       struct otto_wdt_ctrl *ctrl = watchdog_get_drvdata(wdev);
+
+       iowrite32(OTTO_WDT_CNTR_PING, ctrl->base + OTTO_WDT_REG_CNTR);
+
+       return 0;
+}
+
+static int otto_wdt_tick_ms(struct otto_wdt_ctrl *ctrl, int prescale)
+{
+       unsigned int rate_khz = clk_get_rate(ctrl->clk) / 1000;
+
+       if (!rate_khz)
+               return 0;
+
+       return DIV_ROUND_CLOSEST(1 << (25 + prescale), rate_khz);
+}
+
+/*
+ * The timer asserts the PHASE1/PHASE2 IRQs when the number of ticks

exceeds
+ * the value stored in those fields. This means each phase will run for at least
+ * one tick, so small values need to be clamped to correctly reflect the timeout.
+ */
+static inline unsigned int div_round_ticks(unsigned int val, unsigned int
tick_duration,
+               unsigned int min_ticks)

drivers/watchdog/realtek_otto_wdt.c new file +5 -0
+{
+       return max(min_ticks, DIV_ROUND_UP(val, tick_duration));
+}
+
+static int otto_wdt_determine_timeouts(struct watchdog_device *wdev,

unsigned int
timeout,
+               unsigned int pretimeout)

drivers/watchdog/realtek_otto_wdt.c new file +20 -0
+{
+       struct otto_wdt_ctrl *ctrl = watchdog_get_drvdata(wdev);
+       unsigned int pretimeout_ms = pretimeout * 1000;
+       unsigned int timeout_ms = timeout * 1000;
+       unsigned int prescale_next = 0;
+       unsigned int phase1_ticks;
+       unsigned int phase2_ticks;
+       unsigned int total_ticks;
+       unsigned int prescale;
+       unsigned int tick_ms;
+       u32 v;
+
+       do {
+               prescale = prescale_next;
+               if (prescale > OTTO_WDT_PRESCALE_MAX)
+                       return -EINVAL;
+
+               tick_ms = otto_wdt_tick_ms(ctrl, prescale);
+               total_ticks = div_round_ticks(timeout_ms, tick_ms, 2);
+               phase1_ticks = div_round_ticks(timeout_ms -

pretimeout_ms, tick_ms, 1);
+               phase2_ticks = total_ticks - phase1_ticks;

drivers/watchdog/realtek_otto_wdt.c new file +8 -0
+
+               prescale_next++;
+       } while (phase1_ticks > OTTO_WDT_PHASE_TICKS_MAX
+               || phase2_ticks > OTTO_WDT_PHASE_TICKS_MAX);
+
+       v = ioread32(ctrl->base + OTTO_WDT_REG_CTRL);
+
+       v &= ~(OTTO_WDT_CTRL_PRESCALE | OTTO_WDT_CTRL_PHASE1 |

OTTO_WDT_CTRL_PHASE2);

+       v |= FIELD_PREP(OTTO_WDT_CTRL_PHASE1, phase1_ticks - 1);
+       v |= FIELD_PREP(OTTO_WDT_CTRL_PHASE2, phase2_ticks - 1);
+       v |= FIELD_PREP(OTTO_WDT_CTRL_PRESCALE, prescale);
drivers/watchdog/realtek_otto_wdt.c new file +13 -0
+
+       iowrite32(v, ctrl->base + OTTO_WDT_REG_CTRL);
+
+       timeout_ms = total_ticks * tick_ms;
+       ctrl->wdev.timeout = timeout_ms / 1000;
+
+       pretimeout_ms = phase2_ticks * tick_ms;
+       ctrl->wdev.pretimeout = pretimeout_ms / 1000;
+
+       return 0;
+}
+
+static int otto_wdt_set_timeout(struct watchdog_device *wdev, unsigned

int val)
+{
+       return otto_wdt_determine_timeouts(wdev, val, min(wdev-

Quoted 1 line

pretimeout, val - 1));

drivers/watchdog/realtek_otto_wdt.c new file +3 -0
+}
+
+static int otto_wdt_set_pretimeout(struct watchdog_device *wdev,

unsigned int val)

drivers/watchdog/realtek_otto_wdt.c new file +5 -0
+{
+       return otto_wdt_determine_timeouts(wdev, wdev->timeout, val);
+}
+
+static int otto_wdt_restart(struct watchdog_device *wdev, unsigned long

reboot_mode,
+               void *data)

drivers/watchdog/realtek_otto_wdt.c new file +21 -0
+{
+       struct otto_wdt_ctrl *ctrl = watchdog_get_drvdata(wdev);
+       u32 reset_mode;
+       u32 v;
+
+       disable_irq(ctrl->irq_phase1);
+
+       switch (reboot_mode) {
+       case REBOOT_SOFT:
+               reset_mode = OTTO_WDT_MODE_SOFTWARE;
+               break;
+       case REBOOT_WARM:
+               reset_mode = OTTO_WDT_MODE_CPU;
+               break;
+       default:
+               reset_mode = OTTO_WDT_MODE_SOC;
+               break;
+       }
+
+       /* Configure for shortest timeout and wait for reset to occur */
+       v = FIELD_PREP(OTTO_WDT_CTRL_RST_MODE, reset_mode) |

OTTO_WDT_CTRL_ENABLE;
+       iowrite32(v, ctrl->base + OTTO_WDT_REG_CTRL);

drivers/watchdog/realtek_otto_wdt.c new file +11 -0
+
+       mdelay(3 * otto_wdt_tick_ms(ctrl, 0));
+
+       return 0;
+}
+
+static irqreturn_t otto_wdt_phase1_isr(int irq, void *dev_id)
+{
+       struct otto_wdt_ctrl *ctrl = dev_id;
+
+       iowrite32(OTTO_WDT_INTR_PHASE_1, ctrl->base +

OTTO_WDT_REG_INTR);
+       dev_crit(ctrl->dev, "phase 1 timeout\n");
+       watchdog_notify_pretimeout(&ctrl->wdev);

drivers/watchdog/realtek_otto_wdt.c new file +34 -0
+
+       return IRQ_HANDLED;
+}
+
+static const struct watchdog_ops otto_wdt_ops = {
+       .owner = THIS_MODULE,
+       .start = otto_wdt_start,
+       .stop = otto_wdt_stop,
+       .ping = otto_wdt_ping,
+       .set_timeout = otto_wdt_set_timeout,
+       .set_pretimeout = otto_wdt_set_pretimeout,
+       .restart = otto_wdt_restart,
+};
+
+static const struct watchdog_info otto_wdt_info = {
+       .identity = "Realtek Otto watchdog timer",
+       .options = WDIOF_KEEPALIVEPING |
+               WDIOF_MAGICCLOSE |
+               WDIOF_SETTIMEOUT |
+               WDIOF_PRETIMEOUT,
+};
+
+static int otto_wdt_probe_reset_mode(struct otto_wdt_ctrl *ctrl)
+{
+       static const char *mode_property = "realtek,reset-mode";
+       const struct fwnode_handle *node = ctrl->dev->fwnode;
+       int mode_count;
+       u32 mode;
+       u32 v;
+
+       if (!node)
+               return -ENXIO;
+
+       mode_count = fwnode_property_string_array_count(node,

mode_property);
+       if (mode_count < 0)
+               return mode_count;
+       else if (mode_count == 0)
+               return 0;
+       else if (mode_count != 1)
+               return -EINVAL;
+
+       if (fwnode_property_match_string(node, mode_property, "soc") ==
0)
+               mode = OTTO_WDT_MODE_SOC;
+       else if (fwnode_property_match_string(node, mode_property, "cpu") == 0)
+               mode = OTTO_WDT_MODE_CPU;
+       else if (fwnode_property_match_string(node, mode_property, "software") == 0)
+               mode = OTTO_WDT_MODE_SOFTWARE;
+       else
+               return -EINVAL;
+

Quoted 8 lines

drivers/watchdog/realtek_otto_wdt.c line 85

Quoted 8 lines

+       v = ioread32(ctrl->base + OTTO_WDT_REG_CTRL); +       v &= ~OTTO_WDT_CTRL_RST_MODE;
+       v |= FIELD_PREP(OTTO_WDT_CTRL_RST_MODE, mode); +       iowrite32(v, ctrl->base + OTTO_WDT_REG_CTRL);

drivers/watchdog/realtek_otto_wdt.c new file +4 -0
+
+       return 0;
+}
+

This is the first time I'm using the clocks framework. Since I'm currently using a fixed-
clock node in my DTS, I guess this clock is just 'always on'. As I understand, I should
also use clock_{prepare_enable,disable_unprepare}.

I would add the following small function...

-- >8 --

drivers/watchdog/realtek_otto_wdt.c new file +5 -1
+static void otto_wdt_clock_action(void *data)
+{
+       clk_disable_unprepare(data);
+}
+
-- 8< --
Quoted 19 lines
Quoted 19 lines
drivers/watchdog/realtek_otto_wdt.c new file +18 -0
+static int otto_wdt_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct otto_wdt_ctrl *ctrl;
+       unsigned int max_tick_ms;
+       int ret;
+
+       ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL);
+       if (!ctrl)
+               return -ENOMEM;
+
+       ctrl->dev = dev;
+       ctrl->base = devm_platform_ioremap_resource(pdev, 0);
+       if (IS_ERR(ctrl->base))
+               return PTR_ERR(ctrl->base);
+
+       /* Clear any old interrupts and reset initial state */
+       iowrite32(OTTO_WDT_INTR_PHASE_1 | OTTO_WDT_INTR_PHASE_2,
Quoted 8 lines
Quoted 8 lines

+                       ctrl->base + OTTO_WDT_REG_INTR);
+       iowrite32(OTTO_WDT_CTRL_DEFAULT, ctrl->base + OTTO_WDT_REG_CTRL);
+
+       ctrl->clk = devm_clk_get(dev, NULL);
+       if (IS_ERR(ctrl->clk))
+               return dev_err_probe(dev, PTR_ERR(ctrl->clk), "Failed to get clock\n");

... and these checks when probing:

-- >8 --
+       ret = clk_prepare_enable(ctrl->clk);
+       if (ret)
+               return dev_err_probe(dev, ret, "Failed to enable clock\n"); +
+       ret = devm_add_action_or_reset(dev, otto_wdt_clock_action, ctrl-

Quoted 1 line

clk);

+       if (ret)
+               return ret;
-- 8< --

Best,
Sander

Quoted 72 lines
Quoted 72 lines

+
+       ctrl->irq_phase1 = platform_get_irq_byname(pdev, "phase1"); +       if (ctrl->irq_phase1 < 0)
+               return dev_err_probe(dev, ctrl->irq_phase1, "phase1 IRQ not found\n");
+
+       ret = devm_request_irq(dev, ctrl->irq_phase1, otto_wdt_phase1_isr, 0,
+                       "realtek-otto-wdt", ctrl);
+       if (ret)
+               return dev_err_probe(dev, ret, "Failed to get IRQ for phase1\n");
+
+       ret = otto_wdt_probe_reset_mode(ctrl);
+       if (ret)
+               return dev_err_probe(dev, ret, "Invalid reset mode specified\n");

drivers/watchdog/realtek_otto_wdt.c new file +7 -0
+
+       ctrl->wdev.parent = dev;
+       ctrl->wdev.info = &otto_wdt_info;
+       ctrl->wdev.ops = &otto_wdt_ops;
+
+       /*
+        * Since pretimeout cannot be disabled, min. timeout is twice

the
+        * subsystem resolution. max. timeout is ca. 43s at a bus clock of 200MHz.
+        */
+       ctrl->wdev.min_timeout = 2;
+       max_tick_ms = otto_wdt_tick_ms(ctrl, OTTO_WDT_PRESCALE_MAX); +       ctrl->wdev.max_hw_heartbeat_ms = max_tick_ms * OTTO_WDT_TIMEOUT_TICKS_MAX;
+       ctrl->wdev.timeout = min(30U, ctrl->wdev.max_hw_heartbeat_ms / 1000);

drivers/watchdog/realtek_otto_wdt.c new file +7 -0
+
+       watchdog_set_drvdata(&ctrl->wdev, ctrl);
+       watchdog_init_timeout(&ctrl->wdev, 0, dev);
+       watchdog_stop_on_reboot(&ctrl->wdev);
+       watchdog_set_restart_priority(&ctrl->wdev, 128);
+
+       ret = otto_wdt_determine_timeouts(&ctrl->wdev, ctrl-
Quoted 1 line

wdev.timeout, 1);

+       if (ret)
+               return dev_err_probe(dev, ret, "Failed to set timeout\n");

drivers/watchdog/realtek_otto_wdt.c new file +24 -0
+
+       return devm_watchdog_register_device(dev, &ctrl->wdev);
+}
+
+static const struct of_device_id otto_wdt_ids[] = {
+       { .compatible = "realtek,rtl8380-wdt" },
+       { .compatible = "realtek,rtl8390-wdt" },
+       { .compatible = "realtek,rtl9300-wdt" },
+       { }
+};
+MODULE_DEVICE_TABLE(of, otto_wdt_ids);
+
+static struct platform_driver otto_wdt_driver = {
+       .probe = otto_wdt_probe,
+       .driver = {
+               .name = "realtek-otto-watchdog",
+               .of_match_table = otto_wdt_ids,
+       },
+};
+module_platform_driver(otto_wdt_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Sander Vanheule <[email protected]>");
+MODULE_DESCRIPTION("Realtek Otto watchdog timer driver");
Signature · 1 line
2.31.1
author maintainer
#8 lore raw

Re: [PATCH v3 2/2] watchdog: Add Realtek Otto watchdog timer

In reply to Guenter Roeck

Hi Guenter,

Quoted 22 lines · Guenter Roeck
Quoted 21 lines · Sander Vanheule

Hi Guenter,

Quoted 18 lines · Guenter Roeck
Quoted 16 lines · Sander Vanheule

Realtek MIPS SoCs (platform name Otto) have a watchdog timer with pretimeout notifitication support. The WDT can (partially) hard reset, or soft reset the SoC.

This driver implements all features as described in the devicetree binding, except the phase2 interrupt, and also functions as a restart handler. The cpu reset mode is considered to be a "warm" restart, since this mode does not reset all peripherals. Being an embedded system though, the "cpu" and "software" modes will still cause the bootloader to run on restart.

It is not known how a forced system reset can be disabled on the supported platforms. This means that the phase2 interrupt will only fire at the same time as reset, so implementing phase2 is of little use.

Quoted 8 lines
Quoted 6 lines
Quoted 1 line

Thank you for the review! In the meantime, I was preparing a v4 of this series with some small changes (see inline below). Could you let me know if I can keep your Reviewed-by with those changes?

I think it would be better to re-review it.

I sent out the v4 a bit later than expected, because I hit a regression for the irqchip on this platform:
https://lore.kernel.org/all/[email protected]/

Best,
Sander