Re: [PATCH] wdt: add HiSilicon watchdog driver support
On 1/19/2024 12:24 PM, Yang Xiwen via B4 Relay wrote: From: Yang Xiwen This watchdog core is found on many HiSilicon SoCs. Add support for it. Signed-off-by: Yang Xiwen --- drivers/watchdog/Kconfig| 10 +++ drivers/watchdog/Makefile | 1 + drivers/watchdog/hisi_wdt.c | 196 3 files changed, 207 insertions(+) diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 569726119c..ddf44f63d2 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -32,6 +32,7 @@ config WATCHDOG_TIMEOUT_MSECS default 16000 if ARCH_SUNXI default 5376 if ULP_WATCHDOG default 15000 if ARCH_BCM283X + range 500 18 if ARCH_HISI default 6 help Watchdog timeout in msec @@ -171,6 +172,15 @@ config WDT_GPIO doc/device-tree-bindings/watchdog/gpio-wdt.txt for information on how to describe the watchdog in device tree. +config WDT_HISI + bool "HiSilicon watchdog timer support" + depends on WDT + depends on CLK && DM_RESET + imply WATCHDOG + help + Select this to enable HiSilicon watchdog timer. + Currently supports Hi3798MV200 only. + config WDT_MAX6370 bool "MAX6370 watchdog timer support" depends on WDT diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 5520d3d9ae..3436aa6389 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -30,6 +30,7 @@ obj-$(CONFIG_WDT_ORION) += orion_wdt.o obj-$(CONFIG_WDT_CDNS) += cdns_wdt.o obj-$(CONFIG_WDT_FTWDT010) += ftwdt010_wdt.o obj-$(CONFIG_WDT_GPIO) += gpio_wdt.o +obj-$(CONFIG_WDT_HISI) += hisi_wdt.o obj-$(CONFIG_WDT_MAX6370) += max6370_wdt.o obj-$(CONFIG_WDT_MCF) += mcf_wdt.o obj-$(CONFIG_WDT_MESON_GXBB) += meson_gxbb_wdt.o diff --git a/drivers/watchdog/hisi_wdt.c b/drivers/watchdog/hisi_wdt.c new file mode 100644 index 00..5be9e90865 --- /dev/null +++ b/drivers/watchdog/hisi_wdt.c @@ -0,0 +1,196 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Watchdog driver for HiSilicon SoCs + * + * Copyright 2024 (r) Yang Xiwen + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* CONTROL register definitions */ +#define HISI_WDG_RES_ENBIT(1) +#define HISI_WDG_INT_ENBIT(0) + +/* RIS(Raw Interrupt Status) register definitions */ +#define HISI_WDG_RIS BIT(0) + +/* MIS(Masked Interrupt Status) register definitions*/ +#define HISI_WDG_MIS BIT(0) + +/* LOCK register magic */ +// Write this value to unlock watchdog +#define HISI_WDG_LOCK_MAGIC0x1ACCE551 +// Read values +#define HISI_WDG_LOCK_WA 0x0 +#define HISI_WDG_LOCK_RO 0x1 + +struct hisi_wdg_reg { + u32 load; // 0x + u32 value; // 0x0004 + u32 control; // 0x0008 + u32 intclr; // 0x000c + u32 ris; // 0x0010 + u32 mis; // 0x0014 + u32 reserved[762]; // 0x0018 + u32 lock; // 0x0c00 +}; + +struct hisi_wdt_priv { + struct hisi_wdg_reg __iomem *reg; + struct clk *clk; + struct reset_ctl *rst; +}; + +static inline void hisi_wdt_unlock(struct hisi_wdg_reg __iomem *reg) +{ + reg->lock = HISI_WDG_LOCK_MAGIC; +} + +static inline void hisi_wdt_lock(struct hisi_wdg_reg __iomem *reg) +{ + // Any value other than HISI_WDG_LOCK_MAGIC would lock the registers + reg->lock = 0; +} + +static int hisi_wdt_start(struct udevice *dev, u64 timeout_ms, ulong flags) +{ + struct hisi_wdt_priv *priv = dev_get_priv(dev); + u64 rate; + u64 val; + + rate = clk_get_rate(priv->clk); + + /* This may overflow */ + val = mul_u64_u32_div(timeout_ms, rate, 1000); + if (val > UINT32_MAX) { + dev_warn(dev, "timeout_ms too large, using maximum.\n"); + val = UINT32_MAX; + } + + hisi_wdt_unlock(priv->reg); + + priv->reg->load = (u32) val; + priv->reg->control |= (HISI_WDG_RES_EN | HISI_WDG_INT_EN); + + hisi_wdt_lock(priv->reg); + + return 0; +} + +static int hisi_wdt_stop(struct udevice *dev) +{ + struct hisi_wdt_priv *priv = dev_get_priv(dev); + + hisi_wdt_unlock(priv->reg); + // disabling interrupt also disables counting + priv->reg->control &= ~HISI_WDG_INT_EN; + + hisi_wdt_lock(priv->reg); + + return 0; +} + +static int hisi_wdt_reset(struct udevice *dev) +{ + struct hisi_wdt_priv *priv = dev_get_priv(dev); + + hisi_wdt_unlock(priv->reg); + + // any value written to INTCLR would result a counter reload + priv->reg->intclr = 0; + + hisi_wdt_lock(priv->reg); + + return 0; +} + +static int hisi_wdt_expire_now(struct udevice *dev, ulong flags) +{ + return hisi_wdt_start(dev, 1, flags); +} + +static const struct wdt_ops hisi_wdt_ops = { + .start = hisi_wdt_start, + .stop =
[PATCH] wdt: add HiSilicon watchdog driver support
From: Yang Xiwen This watchdog core is found on many HiSilicon SoCs. Add support for it. Signed-off-by: Yang Xiwen --- drivers/watchdog/Kconfig| 10 +++ drivers/watchdog/Makefile | 1 + drivers/watchdog/hisi_wdt.c | 196 3 files changed, 207 insertions(+) diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 569726119c..ddf44f63d2 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -32,6 +32,7 @@ config WATCHDOG_TIMEOUT_MSECS default 16000 if ARCH_SUNXI default 5376 if ULP_WATCHDOG default 15000 if ARCH_BCM283X + range 500 18 if ARCH_HISI default 6 help Watchdog timeout in msec @@ -171,6 +172,15 @@ config WDT_GPIO doc/device-tree-bindings/watchdog/gpio-wdt.txt for information on how to describe the watchdog in device tree. +config WDT_HISI + bool "HiSilicon watchdog timer support" + depends on WDT + depends on CLK && DM_RESET + imply WATCHDOG + help + Select this to enable HiSilicon watchdog timer. + Currently supports Hi3798MV200 only. + config WDT_MAX6370 bool "MAX6370 watchdog timer support" depends on WDT diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 5520d3d9ae..3436aa6389 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -30,6 +30,7 @@ obj-$(CONFIG_WDT_ORION) += orion_wdt.o obj-$(CONFIG_WDT_CDNS) += cdns_wdt.o obj-$(CONFIG_WDT_FTWDT010) += ftwdt010_wdt.o obj-$(CONFIG_WDT_GPIO) += gpio_wdt.o +obj-$(CONFIG_WDT_HISI) += hisi_wdt.o obj-$(CONFIG_WDT_MAX6370) += max6370_wdt.o obj-$(CONFIG_WDT_MCF) += mcf_wdt.o obj-$(CONFIG_WDT_MESON_GXBB) += meson_gxbb_wdt.o diff --git a/drivers/watchdog/hisi_wdt.c b/drivers/watchdog/hisi_wdt.c new file mode 100644 index 00..5be9e90865 --- /dev/null +++ b/drivers/watchdog/hisi_wdt.c @@ -0,0 +1,196 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Watchdog driver for HiSilicon SoCs + * + * Copyright 2024 (r) Yang Xiwen + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* CONTROL register definitions */ +#define HISI_WDG_RES_ENBIT(1) +#define HISI_WDG_INT_ENBIT(0) + +/* RIS(Raw Interrupt Status) register definitions */ +#define HISI_WDG_RIS BIT(0) + +/* MIS(Masked Interrupt Status) register definitions*/ +#define HISI_WDG_MIS BIT(0) + +/* LOCK register magic */ +// Write this value to unlock watchdog +#define HISI_WDG_LOCK_MAGIC0x1ACCE551 +// Read values +#define HISI_WDG_LOCK_WA 0x0 +#define HISI_WDG_LOCK_RO 0x1 + +struct hisi_wdg_reg { + u32 load; // 0x + u32 value; // 0x0004 + u32 control; // 0x0008 + u32 intclr; // 0x000c + u32 ris; // 0x0010 + u32 mis; // 0x0014 + u32 reserved[762]; // 0x0018 + u32 lock; // 0x0c00 +}; + +struct hisi_wdt_priv { + struct hisi_wdg_reg __iomem *reg; + struct clk *clk; + struct reset_ctl *rst; +}; + +static inline void hisi_wdt_unlock(struct hisi_wdg_reg __iomem *reg) +{ + reg->lock = HISI_WDG_LOCK_MAGIC; +} + +static inline void hisi_wdt_lock(struct hisi_wdg_reg __iomem *reg) +{ + // Any value other than HISI_WDG_LOCK_MAGIC would lock the registers + reg->lock = 0; +} + +static int hisi_wdt_start(struct udevice *dev, u64 timeout_ms, ulong flags) +{ + struct hisi_wdt_priv *priv = dev_get_priv(dev); + u64 rate; + u64 val; + + rate = clk_get_rate(priv->clk); + + /* This may overflow */ + val = mul_u64_u32_div(timeout_ms, rate, 1000); + if (val > UINT32_MAX) { + dev_warn(dev, "timeout_ms too large, using maximum.\n"); + val = UINT32_MAX; + } + + hisi_wdt_unlock(priv->reg); + + priv->reg->load = (u32) val; + priv->reg->control |= (HISI_WDG_RES_EN | HISI_WDG_INT_EN); + + hisi_wdt_lock(priv->reg); + + return 0; +} + +static int hisi_wdt_stop(struct udevice *dev) +{ + struct hisi_wdt_priv *priv = dev_get_priv(dev); + + hisi_wdt_unlock(priv->reg); + // disabling interrupt also disables counting + priv->reg->control &= ~HISI_WDG_INT_EN; + + hisi_wdt_lock(priv->reg); + + return 0; +} + +static int hisi_wdt_reset(struct udevice *dev) +{ + struct hisi_wdt_priv *priv = dev_get_priv(dev); + + hisi_wdt_unlock(priv->reg); + + // any value written to INTCLR would result a counter reload + priv->reg->intclr = 0; + + hisi_wdt_lock(priv->reg); + + return 0; +} + +static int hisi_wdt_expire_now(struct udevice *dev, ulong flags) +{ + return hisi_wdt_start(dev, 1, flags); +} + +static const struct wdt_ops hisi_wdt_ops = { + .start = hisi_wdt_start, + .stop = hisi_wdt_stop, + .reset = hisi_wdt_reset, +