From 5c03f9f4d36292150c14ebd90788c4d3273ed9dc Mon Sep 17 00:00:00 2001 From: Chin-Ting Kuo Date: Mon, 13 Jan 2025 17:37:37 +0800 Subject: watchdog: aspeed: Update bootstatus handling The boot status in the watchdog device struct is updated during controller probe stage. Application layer can get the boot status through the command, cat /sys/class/watchdog/watchdogX/bootstatus. The bootstatus can be, WDIOF_CARDRESET => System is reset due to WDT timeout occurs. Others => Other reset events, e.g., power on reset. On ASPEED platforms, boot status is recorded in the SCU registers. - AST2400: Only a bit is used to represent system reset triggered by any WDT controller. - AST2500/AST2600: System reset triggered by different WDT controllers can be distinguished by different SCU bits. Besides, on AST2400 and AST2500, since alternating boot event is also triggered by using WDT timeout mechanism, it is classified as WDIOF_CARDRESET. Signed-off-by: Chin-Ting Kuo Reviewed-by: Andrew Jeffery Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20250113093737.845097-2-chin-ting_kuo@aspeedtech.com Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/aspeed_wdt.c | 81 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 79 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/aspeed_wdt.c b/drivers/watchdog/aspeed_wdt.c index b4773a6aaf8c..369635b38ca0 100644 --- a/drivers/watchdog/aspeed_wdt.c +++ b/drivers/watchdog/aspeed_wdt.c @@ -11,21 +11,30 @@ #include #include #include +#include #include #include #include #include +#include #include static bool nowayout = WATCHDOG_NOWAYOUT; module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); +struct aspeed_wdt_scu { + const char *compatible; + u32 reset_status_reg; + u32 wdt_reset_mask; + u32 wdt_reset_mask_shift; +}; struct aspeed_wdt_config { u32 ext_pulse_width_mask; u32 irq_shift; u32 irq_mask; + struct aspeed_wdt_scu scu; }; struct aspeed_wdt { @@ -39,18 +48,36 @@ static const struct aspeed_wdt_config ast2400_config = { .ext_pulse_width_mask = 0xff, .irq_shift = 0, .irq_mask = 0, + .scu = { + .compatible = "aspeed,ast2400-scu", + .reset_status_reg = 0x3c, + .wdt_reset_mask = 0x1, + .wdt_reset_mask_shift = 1, + }, }; static const struct aspeed_wdt_config ast2500_config = { .ext_pulse_width_mask = 0xfffff, .irq_shift = 12, .irq_mask = GENMASK(31, 12), + .scu = { + .compatible = "aspeed,ast2500-scu", + .reset_status_reg = 0x3c, + .wdt_reset_mask = 0x1, + .wdt_reset_mask_shift = 2, + }, }; static const struct aspeed_wdt_config ast2600_config = { .ext_pulse_width_mask = 0xfffff, .irq_shift = 0, .irq_mask = GENMASK(31, 10), + .scu = { + .compatible = "aspeed,ast2600-scu", + .reset_status_reg = 0x74, + .wdt_reset_mask = 0xf, + .wdt_reset_mask_shift = 16, + }, }; static const struct of_device_id aspeed_wdt_of_table[] = { @@ -213,6 +240,56 @@ static int aspeed_wdt_restart(struct watchdog_device *wdd, return 0; } +static void aspeed_wdt_update_bootstatus(struct platform_device *pdev, + struct aspeed_wdt *wdt) +{ + const struct resource *res; + struct aspeed_wdt_scu scu = wdt->cfg->scu; + struct regmap *scu_base; + u32 reset_mask_width; + u32 reset_mask_shift; + u32 idx = 0; + u32 status; + int ret; + + if (!of_device_is_compatible(pdev->dev.of_node, "aspeed,ast2400-wdt")) { + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + idx = ((intptr_t)wdt->base & 0x00000fff) / resource_size(res); + } + + scu_base = syscon_regmap_lookup_by_compatible(scu.compatible); + if (IS_ERR(scu_base)) { + wdt->wdd.bootstatus = WDIOS_UNKNOWN; + return; + } + + ret = regmap_read(scu_base, scu.reset_status_reg, &status); + if (ret) { + wdt->wdd.bootstatus = WDIOS_UNKNOWN; + return; + } + + reset_mask_width = hweight32(scu.wdt_reset_mask); + reset_mask_shift = scu.wdt_reset_mask_shift + + reset_mask_width * idx; + + if (status & (scu.wdt_reset_mask << reset_mask_shift)) + wdt->wdd.bootstatus = WDIOF_CARDRESET; + + /* clear wdt reset event flag */ + if (of_device_is_compatible(pdev->dev.of_node, "aspeed,ast2400-wdt") || + of_device_is_compatible(pdev->dev.of_node, "aspeed,ast2500-wdt")) { + ret = regmap_read(scu_base, scu.reset_status_reg, &status); + if (!ret) { + status &= ~(scu.wdt_reset_mask << reset_mask_shift); + regmap_write(scu_base, scu.reset_status_reg, status); + } + } else { + regmap_write(scu_base, scu.reset_status_reg, + scu.wdt_reset_mask << reset_mask_shift); + } +} + /* access_cs0 shows if cs0 is accessible, hence the reverted bit */ static ssize_t access_cs0_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -458,10 +535,10 @@ static int aspeed_wdt_probe(struct platform_device *pdev) writel(duration - 1, wdt->base + WDT_RESET_WIDTH); } + aspeed_wdt_update_bootstatus(pdev, wdt); + status = readl(wdt->base + WDT_TIMEOUT_STATUS); if (status & WDT_TIMEOUT_STATUS_BOOT_SECONDARY) { - wdt->wdd.bootstatus = WDIOF_CARDRESET; - if (of_device_is_compatible(np, "aspeed,ast2400-wdt") || of_device_is_compatible(np, "aspeed,ast2500-wdt")) wdt->wdd.groups = bswitch_groups; -- cgit v1.2.3-59-g8ed1b From f285bd8c74d3deefd36dfee8bcbbdd781fa6fc21 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Thu, 16 Jan 2025 14:46:04 -0800 Subject: watchdog: cros-ec: Add newlines to printks Add newlines to printk messages so that the next record is more easily readable. Cc: Lukasz Majczak Cc: Benson Leung Signed-off-by: Stephen Boyd Reviewed-by: Benson Leung Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20250116224605.110870-1-swboyd@chromium.org Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/cros_ec_wdt.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/cros_ec_wdt.c b/drivers/watchdog/cros_ec_wdt.c index ba045e29f9a5..716c23f4388c 100644 --- a/drivers/watchdog/cros_ec_wdt.c +++ b/drivers/watchdog/cros_ec_wdt.c @@ -58,7 +58,7 @@ static int cros_ec_wdt_ping(struct watchdog_device *wdd) arg.req.command = EC_HANG_DETECT_CMD_RELOAD; ret = cros_ec_wdt_send_cmd(cros_ec, &arg); if (ret < 0) - dev_dbg(wdd->parent, "Failed to ping watchdog (%d)", ret); + dev_dbg(wdd->parent, "Failed to ping watchdog (%d)\n", ret); return ret; } @@ -74,7 +74,7 @@ static int cros_ec_wdt_start(struct watchdog_device *wdd) arg.req.reboot_timeout_sec = wdd->timeout; ret = cros_ec_wdt_send_cmd(cros_ec, &arg); if (ret < 0) - dev_dbg(wdd->parent, "Failed to start watchdog (%d)", ret); + dev_dbg(wdd->parent, "Failed to start watchdog (%d)\n", ret); return ret; } @@ -88,7 +88,7 @@ static int cros_ec_wdt_stop(struct watchdog_device *wdd) arg.req.command = EC_HANG_DETECT_CMD_CANCEL; ret = cros_ec_wdt_send_cmd(cros_ec, &arg); if (ret < 0) - dev_dbg(wdd->parent, "Failed to stop watchdog (%d)", ret); + dev_dbg(wdd->parent, "Failed to stop watchdog (%d)\n", ret); return ret; } @@ -136,7 +136,7 @@ static int cros_ec_wdt_probe(struct platform_device *pdev) arg.req.command = EC_HANG_DETECT_CMD_GET_STATUS; ret = cros_ec_wdt_send_cmd(cros_ec, &arg); if (ret < 0) - return dev_err_probe(dev, ret, "Failed to get watchdog bootstatus"); + return dev_err_probe(dev, ret, "Failed to get watchdog bootstatus\n"); wdd->parent = &pdev->dev; wdd->info = &cros_ec_wdt_ident; @@ -150,7 +150,7 @@ static int cros_ec_wdt_probe(struct platform_device *pdev) arg.req.command = EC_HANG_DETECT_CMD_CLEAR_STATUS; ret = cros_ec_wdt_send_cmd(cros_ec, &arg); if (ret < 0) - return dev_err_probe(dev, ret, "Failed to clear watchdog bootstatus"); + return dev_err_probe(dev, ret, "Failed to clear watchdog bootstatus\n"); watchdog_stop_on_reboot(wdd); watchdog_stop_on_unregister(wdd); -- cgit v1.2.3-59-g8ed1b From 331c8349605c8fa2f9040c39fe8c40afe3fdc3c3 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Sun, 26 Jan 2025 13:26:31 +0000 Subject: watchdog: Enable RZV2HWDT driver depend on ARCH_RENESAS RZ/G3E watchdog timer IP is similar to the one found on RZ/V2H. Both these SoCs belong to the ARCH_RENESAS family. So, it makes sense to use ARCH_RENESAS rather than ARCH_R9A09G057 to enable the RZV2HWDT driver. Signed-off-by: Biju Das Reviewed-by: Geert Uytterhoeven Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20250126132633.31956-3-biju.das.jz@bp.renesas.com Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/Kconfig | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index f81705f8539a..b9d23f98a436 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -963,13 +963,14 @@ config RENESAS_RZG2LWDT Renesas RZ/G2L SoCs. These watchdogs can be used to reset a system. config RENESAS_RZV2HWDT - tristate "Renesas RZ/V2H(P) WDT Watchdog" - depends on ARCH_R9A09G057 || COMPILE_TEST + tristate "Renesas RZ/{G3E,V2H(P)} WDT Watchdog" + depends on ARCH_RENESAS || COMPILE_TEST depends on PM || COMPILE_TEST select WATCHDOG_CORE help This driver adds watchdog support for the integrated watchdogs in the - Renesas RZ/V2H(P) SoCs. These watchdogs can be used to reset a system. + Renesas RZ/{G3E,V2H(P)} SoCs. These watchdogs can be used to reset a + system. config ASPEED_WATCHDOG tristate "Aspeed BMC watchdog support" -- cgit v1.2.3-59-g8ed1b From c284153a2c5537db4fec51ac850c17d2eb1ffcfe Mon Sep 17 00:00:00 2001 From: Mark Pearson Date: Fri, 31 Jan 2025 15:48:55 -0500 Subject: watchdog: lenovo_se30_wdt: Watchdog driver for Lenovo SE30 platform Watchdog driver implementation for Lenovo SE30 platform. Signed-off-by: Mark Pearson Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20250131204855.1827-1-mpearson-lenovo@squebb.ca Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/Kconfig | 12 ++ drivers/watchdog/Makefile | 1 + drivers/watchdog/lenovo_se30_wdt.c | 393 +++++++++++++++++++++++++++++++++++++ 3 files changed, 406 insertions(+) create mode 100644 drivers/watchdog/lenovo_se30_wdt.c (limited to 'drivers') diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index b9d23f98a436..123d1f564f7c 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -279,6 +279,18 @@ config LENOVO_SE10_WDT This driver can also be built as a module. If so, the module will be called lenovo-se10-wdt. +config LENOVO_SE30_WDT + tristate "Lenovo SE30 Watchdog" + depends on (X86 && DMI) || COMPILE_TEST + depends on HAS_IOPORT + select WATCHDOG_CORE + help + If you say yes here you get support for the watchdog + functionality for the Lenovo SE30 platform. + + This driver can also be built as a module. If so, the module + will be called lenovo-se30-wdt. + config MENF21BMC_WATCHDOG tristate "MEN 14F021P00 BMC Watchdog" depends on MFD_MENF21BMC || COMPILE_TEST diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 8411626fa162..c9482904bf87 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -124,6 +124,7 @@ obj-$(CONFIG_I6300ESB_WDT) += i6300esb.o obj-$(CONFIG_IE6XX_WDT) += ie6xx_wdt.o obj-$(CONFIG_ITCO_WDT) += iTCO_wdt.o obj-$(CONFIG_LENOVO_SE10_WDT) += lenovo_se10_wdt.o +obj-$(CONFIG_LENOVO_SE30_WDT) += lenovo_se30_wdt.o ifeq ($(CONFIG_ITCO_VENDOR_SUPPORT),y) obj-$(CONFIG_ITCO_WDT) += iTCO_vendor_support.o endif diff --git a/drivers/watchdog/lenovo_se30_wdt.c b/drivers/watchdog/lenovo_se30_wdt.c new file mode 100644 index 000000000000..f25429da0cec --- /dev/null +++ b/drivers/watchdog/lenovo_se30_wdt.c @@ -0,0 +1,393 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * WDT driver for Lenovo SE30 device + */ + +#define dev_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include + +#define IOREGION_OFFSET 4 /* Use EC port 1 */ +#define IOREGION_LENGTH 4 + +#define WATCHDOG_TIMEOUT 60 + +#define MIN_TIMEOUT 1 +#define MAX_TIMEOUT 255 +#define MAX_WAIT 10 + +static int timeout; /* in seconds */ +module_param(timeout, int, 0); +MODULE_PARM_DESC(timeout, + "Watchdog timeout in seconds. 1 <= timeout <= 255, 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) ")"); + +#define LNV_SE30_NAME "lenovo-se30-wdt" +#define LNV_SE30_ID 0x0110 +#define CHIPID_MASK 0xFFF0 + +#define CHIPID_REG 0x20 +#define SIO_REG 0x2e +#define LDN_REG 0x07 +#define UNLOCK_KEY 0x87 +#define LOCK_KEY 0xAA +#define LD_NUM_SHM 0x0F +#define LD_BASE_ADDR 0xF8 + +#define WDT_MODULE 0x10 +#define WDT_CFG_INDEX 0x15 /* WD configuration register */ +#define WDT_CNT_INDEX 0x16 /* WD timer count register */ +#define WDT_CFG_RESET 0x2 + +/* Host Interface WIN2 offset definition */ +#define SHM_WIN_SIZE 0xFF +#define SHM_WIN_MOD_OFFSET 0x01 +#define SHM_WIN_CMD_OFFSET 0x02 +#define SHM_WIN_SEL_OFFSET 0x03 +#define SHM_WIN_CTL_OFFSET 0x04 +#define VAL_SHM_WIN_CTRL_WR 0x40 +#define VAL_SHM_WIN_CTRL_RD 0x80 +#define SHM_WIN_ID_OFFSET 0x08 +#define SHM_WIN_DAT_OFFSET 0x10 + +struct nct6692_reg { + unsigned char mod; + unsigned char cmd; + unsigned char sel; + unsigned int idx; +}; + +/* Watchdog is based on NCT6692 device */ +struct lenovo_se30_wdt { + unsigned char __iomem *shm_base_addr; + struct nct6692_reg wdt_cfg; + struct nct6692_reg wdt_cnt; + struct watchdog_device wdt; +}; + +static inline void superio_outb(int ioreg, int reg, int val) +{ + outb(reg, ioreg); + outb(val, ioreg + 1); +} + +static inline int superio_inb(int ioreg, int reg) +{ + outb(reg, ioreg); + return inb(ioreg + 1); +} + +static inline int superio_enter(int key, int addr, const char *name) +{ + if (!request_muxed_region(addr, 2, name)) { + pr_err("I/O address 0x%04x already in use\n", addr); + return -EBUSY; + } + outb(key, addr); /* Enter extended function mode */ + outb(key, addr); /* Again according to manual */ + + return 0; +} + +static inline void superio_exit(int key, int addr) +{ + outb(key, addr); /* Leave extended function mode */ + release_region(addr, 2); +} + +static int shm_get_ready(unsigned char __iomem *shm_base_addr, + const struct nct6692_reg *reg) +{ + unsigned char pre_id, new_id; + int loop = 0; + + iowrite8(reg->mod, shm_base_addr + SHM_WIN_MOD_OFFSET); + iowrite8(reg->cmd, shm_base_addr + SHM_WIN_CMD_OFFSET); + iowrite8(reg->sel, shm_base_addr + SHM_WIN_SEL_OFFSET); + + pre_id = ioread8(shm_base_addr + SHM_WIN_ID_OFFSET); + iowrite8(VAL_SHM_WIN_CTRL_RD, shm_base_addr + SHM_WIN_CTL_OFFSET); + + /* Loop checking when interface is ready */ + while (loop < MAX_WAIT) { + new_id = ioread8(shm_base_addr + SHM_WIN_ID_OFFSET); + if (new_id != pre_id) + return 0; + loop++; + usleep_range(10, 125); + } + return -ETIMEDOUT; +} + +static int read_shm_win(unsigned char __iomem *shm_base_addr, + const struct nct6692_reg *reg, + unsigned char idx_offset, + unsigned char *data) +{ + int err = shm_get_ready(shm_base_addr, reg); + + if (err) + return err; + *data = ioread8(shm_base_addr + SHM_WIN_DAT_OFFSET + reg->idx + idx_offset); + return 0; +} + +static int write_shm_win(unsigned char __iomem *shm_base_addr, + const struct nct6692_reg *reg, + unsigned char idx_offset, + unsigned char val) +{ + int err = shm_get_ready(shm_base_addr, reg); + + if (err) + return err; + iowrite8(val, shm_base_addr + SHM_WIN_DAT_OFFSET + reg->idx + idx_offset); + iowrite8(VAL_SHM_WIN_CTRL_WR, shm_base_addr + SHM_WIN_CTL_OFFSET); + err = shm_get_ready(shm_base_addr, reg); + return err; +} + +static int lenovo_se30_wdt_enable(struct lenovo_se30_wdt *data, unsigned int timeout) +{ + if (timeout) { + int err = write_shm_win(data->shm_base_addr, &data->wdt_cfg, 0, WDT_CFG_RESET); + + if (err) + return err; + } + return write_shm_win(data->shm_base_addr, &data->wdt_cnt, 0, timeout); +} + +static int lenovo_se30_wdt_start(struct watchdog_device *wdog) +{ + struct lenovo_se30_wdt *data = watchdog_get_drvdata(wdog); + + return lenovo_se30_wdt_enable(data, wdog->timeout); +} + +static int lenovo_se30_wdt_stop(struct watchdog_device *wdog) +{ + struct lenovo_se30_wdt *data = watchdog_get_drvdata(wdog); + + return lenovo_se30_wdt_enable(data, 0); +} + +static unsigned int lenovo_se30_wdt_get_timeleft(struct watchdog_device *wdog) +{ + struct lenovo_se30_wdt *data = watchdog_get_drvdata(wdog); + unsigned char timeleft; + int err; + + err = read_shm_win(data->shm_base_addr, &data->wdt_cnt, 0, &timeleft); + if (err) + return 0; + return timeleft; +} + +static int lenovo_se30_wdt_ping(struct watchdog_device *wdt) +{ + struct lenovo_se30_wdt *data = watchdog_get_drvdata(wdt); + int err = 0; + + /* + * Device does not support refreshing WDT_TIMER_REG register when + * the watchdog is active. Need to disable, feed and enable again + */ + err = lenovo_se30_wdt_enable(data, 0); + if (err) + return err; + + err = write_shm_win(data->shm_base_addr, &data->wdt_cnt, 0, wdt->timeout); + if (!err) + err = lenovo_se30_wdt_enable(data, wdt->timeout); + + return err; +} + +static const struct watchdog_info lenovo_se30_wdt_info = { + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | + WDIOF_MAGICCLOSE, + .identity = "Lenovo SE30 watchdog", +}; + +static const struct watchdog_ops lenovo_se30_wdt_ops = { + .owner = THIS_MODULE, + .start = lenovo_se30_wdt_start, + .stop = lenovo_se30_wdt_stop, + .ping = lenovo_se30_wdt_ping, + .get_timeleft = lenovo_se30_wdt_get_timeleft, +}; + +static int lenovo_se30_wdt_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct lenovo_se30_wdt *priv; + unsigned long base_phys; + unsigned short val; + int err; + + err = superio_enter(UNLOCK_KEY, SIO_REG, LNV_SE30_NAME); + if (err) + return err; + + val = superio_inb(SIO_REG, CHIPID_REG) << 8; + val |= superio_inb(SIO_REG, CHIPID_REG + 1); + + if ((val & CHIPID_MASK) != LNV_SE30_ID) { + superio_exit(LOCK_KEY, SIO_REG); + return -ENODEV; + } + + superio_outb(SIO_REG, LDN_REG, LD_NUM_SHM); + base_phys = (superio_inb(SIO_REG, LD_BASE_ADDR) | + (superio_inb(SIO_REG, LD_BASE_ADDR + 1) << 8) | + (superio_inb(SIO_REG, LD_BASE_ADDR + 2) << 16) | + (superio_inb(SIO_REG, LD_BASE_ADDR + 3) << 24)) & + 0xFFFFFFFF; + + superio_exit(LOCK_KEY, SIO_REG); + if (base_phys == 0xFFFFFFFF || base_phys == 0) + return -ENODEV; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + if (!devm_request_mem_region(dev, base_phys, SHM_WIN_SIZE, LNV_SE30_NAME)) + return -EBUSY; + + priv->shm_base_addr = devm_ioremap(dev, base_phys, SHM_WIN_SIZE); + + priv->wdt_cfg.mod = WDT_MODULE; + priv->wdt_cfg.idx = WDT_CFG_INDEX; + priv->wdt_cnt.mod = WDT_MODULE; + priv->wdt_cnt.idx = WDT_CNT_INDEX; + + priv->wdt.ops = &lenovo_se30_wdt_ops; + priv->wdt.info = &lenovo_se30_wdt_info; + priv->wdt.timeout = WATCHDOG_TIMEOUT; /* Set default timeout */ + priv->wdt.min_timeout = MIN_TIMEOUT; + priv->wdt.max_timeout = MAX_TIMEOUT; + priv->wdt.parent = dev; + + watchdog_init_timeout(&priv->wdt, timeout, dev); + watchdog_set_drvdata(&priv->wdt, priv); + watchdog_set_nowayout(&priv->wdt, nowayout); + watchdog_stop_on_reboot(&priv->wdt); + watchdog_stop_on_unregister(&priv->wdt); + + return devm_watchdog_register_device(dev, &priv->wdt); +} + +static struct platform_device *pdev; + +static struct platform_driver lenovo_se30_wdt_driver = { + .driver = { + .name = LNV_SE30_NAME, + }, + .probe = lenovo_se30_wdt_probe, +}; + +static int lenovo_se30_create_platform_device(const struct dmi_system_id *id) +{ + int err; + + pdev = platform_device_alloc(LNV_SE30_NAME, -1); + if (!pdev) + return -ENOMEM; + + err = platform_device_add(pdev); + if (err) + platform_device_put(pdev); + + return err; +} + +static const struct dmi_system_id lenovo_se30_wdt_dmi_table[] __initconst = { + { + .ident = "LENOVO-SE30", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "11NA"), + }, + .callback = lenovo_se30_create_platform_device, + }, + { + .ident = "LENOVO-SE30", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "11NB"), + }, + .callback = lenovo_se30_create_platform_device, + }, + { + .ident = "LENOVO-SE30", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "11NC"), + }, + .callback = lenovo_se30_create_platform_device, + }, + { + .ident = "LENOVO-SE30", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "11NH"), + }, + .callback = lenovo_se30_create_platform_device, + }, + { + .ident = "LENOVO-SE30", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "11NJ"), + }, + .callback = lenovo_se30_create_platform_device, + }, + { + .ident = "LENOVO-SE30", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "11NK"), + }, + .callback = lenovo_se30_create_platform_device, + }, + {} +}; +MODULE_DEVICE_TABLE(dmi, lenovo_se30_wdt_dmi_table); + +static int __init lenovo_se30_wdt_init(void) +{ + if (!dmi_check_system(lenovo_se30_wdt_dmi_table)) + return -ENODEV; + + return platform_driver_register(&lenovo_se30_wdt_driver); +} + +static void __exit lenovo_se30_wdt_exit(void) +{ + if (pdev) + platform_device_unregister(pdev); + platform_driver_unregister(&lenovo_se30_wdt_driver); +} + +module_init(lenovo_se30_wdt_init); +module_exit(lenovo_se30_wdt_exit); + +MODULE_AUTHOR("Mark Pearson "); +MODULE_AUTHOR("David Ober "); +MODULE_DESCRIPTION("Lenovo SE30 watchdog driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-59-g8ed1b From 480ee8a260e6f87cbcdaff77ac2cbf6dc03f0f4f Mon Sep 17 00:00:00 2001 From: Kyunghwan Seo Date: Thu, 13 Feb 2025 09:41:04 +0900 Subject: watchdog: s3c2410_wdt: Fix PMU register bits for ExynosAutoV920 SoC Fix the PMU register bits for the ExynosAutoV920 SoC. This SoC has different bit information compared to its previous version, ExynosAutoV9, and we have made the necessary adjustments. rst_stat_bit: - ExynosAutoV920 cl0 : 0 - ExynosAutoV920 cl1 : 1 cnt_en_bit: - ExynosAutoV920 cl0 : 8 - ExynosAutoV920 cl1 : 8 Signed-off-by: Kyunghwan Seo Signed-off-by: Sangwook Shin Reviewed-by: Alim Akhtar Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20250213004104.3881711-1-sw617.shin@samsung.com Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/s3c2410_wdt.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c index 30450e99e5e9..bdd81d8074b2 100644 --- a/drivers/watchdog/s3c2410_wdt.c +++ b/drivers/watchdog/s3c2410_wdt.c @@ -72,6 +72,8 @@ #define EXYNOS850_CLUSTER1_WDTRESET_BIT 23 #define EXYNOSAUTOV9_CLUSTER0_WDTRESET_BIT 25 #define EXYNOSAUTOV9_CLUSTER1_WDTRESET_BIT 24 +#define EXYNOSAUTOV920_CLUSTER0_WDTRESET_BIT 0 +#define EXYNOSAUTOV920_CLUSTER1_WDTRESET_BIT 1 #define GS_CLUSTER0_NONCPU_OUT 0x1220 #define GS_CLUSTER1_NONCPU_OUT 0x1420 @@ -312,9 +314,9 @@ static const struct s3c2410_wdt_variant drv_data_exynosautov920_cl0 = { .mask_bit = 2, .mask_reset_inv = true, .rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET, - .rst_stat_bit = EXYNOSAUTOV9_CLUSTER0_WDTRESET_BIT, + .rst_stat_bit = EXYNOSAUTOV920_CLUSTER0_WDTRESET_BIT, .cnt_en_reg = EXYNOSAUTOV920_CLUSTER0_NONCPU_OUT, - .cnt_en_bit = 7, + .cnt_en_bit = 8, .quirks = QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_MASK_RESET | QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_CNT_EN | QUIRK_HAS_DBGACK_BIT, @@ -325,9 +327,9 @@ static const struct s3c2410_wdt_variant drv_data_exynosautov920_cl1 = { .mask_bit = 2, .mask_reset_inv = true, .rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET, - .rst_stat_bit = EXYNOSAUTOV9_CLUSTER1_WDTRESET_BIT, + .rst_stat_bit = EXYNOSAUTOV920_CLUSTER1_WDTRESET_BIT, .cnt_en_reg = EXYNOSAUTOV920_CLUSTER1_NONCPU_OUT, - .cnt_en_bit = 7, + .cnt_en_bit = 8, .quirks = QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_MASK_RESET | QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_CNT_EN | QUIRK_HAS_DBGACK_BIT, -- cgit v1.2.3-59-g8ed1b From f1c16aa612dcc5b03f5d6f39ca53f791d36850e1 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 4 Mar 2025 15:45:35 +0200 Subject: watchdog: nic7018_wdt: tidy up ACPI ID table Tidy up ACPI ID table: - drop ACPI_PTR() and hence replace acpi.h with mod_devicetable.h - remove explicit driver_data initializer - drop comma in the terminator entry With that done, extend compile test coverage. Signed-off-by: Andy Shevchenko Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20250304140114.1812452-1-andriy.shevchenko@linux.intel.com Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/Kconfig | 3 ++- drivers/watchdog/nic7018_wdt.c | 9 +++++---- 2 files changed, 7 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 123d1f564f7c..0d8d37f712e8 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -1743,7 +1743,8 @@ config NI903X_WDT config NIC7018_WDT tristate "NIC7018 Watchdog" - depends on X86 && ACPI + depends on HAS_IOPORT + depends on ACPI || COMPILE_TEST select WATCHDOG_CORE help Support for National Instruments NIC7018 Watchdog. diff --git a/drivers/watchdog/nic7018_wdt.c b/drivers/watchdog/nic7018_wdt.c index 44982b37ba6f..44b5298f599a 100644 --- a/drivers/watchdog/nic7018_wdt.c +++ b/drivers/watchdog/nic7018_wdt.c @@ -3,12 +3,13 @@ * Copyright (C) 2016 National Instruments Corp. */ -#include #include #include #include +#include #include #include +#include #include #define LOCK 0xA5 @@ -229,8 +230,8 @@ static void nic7018_remove(struct platform_device *pdev) } static const struct acpi_device_id nic7018_device_ids[] = { - {"NIC7018", 0}, - {"", 0}, + { "NIC7018" }, + { } }; MODULE_DEVICE_TABLE(acpi, nic7018_device_ids); @@ -239,7 +240,7 @@ static struct platform_driver watchdog_driver = { .remove = nic7018_remove, .driver = { .name = KBUILD_MODNAME, - .acpi_match_table = ACPI_PTR(nic7018_device_ids), + .acpi_match_table = nic7018_device_ids, }, }; -- cgit v1.2.3-59-g8ed1b From 0ccd5d56e6b2f342096a362ac24785d4be9c64a2 Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Tue, 11 Mar 2025 20:50:05 +1100 Subject: watchdog: lenovo_se30_wdt: include io.h for devm_ioremap() After merging the watchdog tree, today's linux-next build (x86_64 allmodconfig) failed like this: drivers/watchdog/lenovo_se30_wdt.c: In function 'lenovo_se30_wdt_probe': drivers/watchdog/lenovo_se30_wdt.c:272:31: error: implicit declaration of function 'devm_ioremap' [-Wimplicit-function-declaration] 272 | priv->shm_base_addr = devm_ioremap(dev, base_phys, SHM_WIN_SIZE); | ^~~~~~~~~~~~ drivers/watchdog/lenovo_se30_wdt.c:272:29: error: assignment to 'unsigned char *' from 'int' makes pointer from integer without a cast [-Wint-conversion] 272 | priv->shm_base_addr = devm_ioremap(dev, base_phys, SHM_WIN_SIZE); | ^ Caused by commit c284153a2c55 ("watchdog: lenovo_se30_wdt: Watchdog driver for Lenovo SE30 platform") Somewhere alogn the way a change to some include file means that linux/io.h is no longer implicitly included. I have added the following patch for today. Fixes: c284153a2c55 ("watchdog: lenovo_se30_wdt: Watchdog driver for Lenovo SE30 platform") Signed-off-by: Stephen Rothwell Reviewed-by: Mark Pearson Reviewed-by: Wim Van Sebroeck Link: https://lore.kernel.org/r/20250311210305.3c5a2313@canb.auug.org.au Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/lenovo_se30_wdt.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/watchdog/lenovo_se30_wdt.c b/drivers/watchdog/lenovo_se30_wdt.c index f25429da0cec..024b842499b3 100644 --- a/drivers/watchdog/lenovo_se30_wdt.c +++ b/drivers/watchdog/lenovo_se30_wdt.c @@ -5,6 +5,7 @@ #define dev_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include #include -- cgit v1.2.3-59-g8ed1b From 594e1e368f09cea198f991ab122dd72ed559a383 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 11 Mar 2025 07:10:09 -0700 Subject: watchdog: Convert to use device property Use device_property_read_u32() instead of of_property_read_u32() to support reading the timeout from non-devicetree sources. Signed-off-by: Guenter Roeck Link: https://lore.kernel.org/r/20250311141009.756975-1-linux@roeck-us.net Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/watchdog_core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/watchdog_core.c b/drivers/watchdog/watchdog_core.c index d46d8c8c01f2..6152dba4b52c 100644 --- a/drivers/watchdog/watchdog_core.c +++ b/drivers/watchdog/watchdog_core.c @@ -33,7 +33,8 @@ #include /* For __init/__exit/... */ #include /* For ida_* macros */ #include /* For IS_ERR macros */ -#include /* For of_get_timeout_sec */ +#include /* For of_alias_get_id */ +#include /* For device_property_read_u32 */ #include #include "watchdog_core.h" /* For watchdog_dev_register/... */ @@ -137,8 +138,7 @@ int watchdog_init_timeout(struct watchdog_device *wdd, } /* try to get the timeout_sec property */ - if (dev && dev->of_node && - of_property_read_u32(dev->of_node, "timeout-sec", &t) == 0) { + if (dev && device_property_read_u32(dev, "timeout-sec", &t) == 0) { if (t && !watchdog_timeout_invalid(wdd, t)) { wdd->timeout = t; return 0; -- cgit v1.2.3-59-g8ed1b From ad3746700ae238a96a9a4244d02a2e8eb00f157e Mon Sep 17 00:00:00 2001 From: Chen Ni Date: Thu, 13 Mar 2025 16:44:19 +0800 Subject: watchdog: npcm: Remove unnecessary NULL check before clk_prepare_enable/clk_disable_unprepare clk_prepare_enable() and clk_disable_unprepare() already checked NULL clock parameter.Remove unneeded NULL check for clk here. Signed-off-by: Chen Ni Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20250313084420.2481763-1-nichen@iscas.ac.cn Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/npcm_wdt.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/npcm_wdt.c b/drivers/watchdog/npcm_wdt.c index a5dd1c230137..e62ea054bc61 100644 --- a/drivers/watchdog/npcm_wdt.c +++ b/drivers/watchdog/npcm_wdt.c @@ -68,8 +68,7 @@ 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); + clk_prepare_enable(wdt->clk); if (wdd->timeout < 2) val = 0x800; @@ -105,8 +104,7 @@ static int npcm_wdt_stop(struct watchdog_device *wdd) writel(0, wdt->reg); - if (wdt->clk) - clk_disable_unprepare(wdt->clk); + clk_disable_unprepare(wdt->clk); return 0; } @@ -156,8 +154,7 @@ 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); + clk_prepare_enable(wdt->clk); writel(NPCM_WTR | NPCM_WTRE | NPCM_WTE, wdt->reg); udelay(1000); -- cgit v1.2.3-59-g8ed1b From 48a136639ec233614a61653e19f559977d5da2b5 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 14 Mar 2025 17:02:44 +0100 Subject: watchdog: aspeed: fix 64-bit division On 32-bit architectures, the new calculation causes a build failure: ld.lld-21: error: undefined symbol: __aeabi_uldivmod Since neither value is ever larger than a register, cast both sides into a uintptr_t. Fixes: 5c03f9f4d362 ("watchdog: aspeed: Update bootstatus handling") Signed-off-by: Arnd Bergmann Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20250314160248.502324-1-arnd@kernel.org Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/aspeed_wdt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/watchdog/aspeed_wdt.c b/drivers/watchdog/aspeed_wdt.c index 369635b38ca0..837e15701c0e 100644 --- a/drivers/watchdog/aspeed_wdt.c +++ b/drivers/watchdog/aspeed_wdt.c @@ -254,7 +254,7 @@ static void aspeed_wdt_update_bootstatus(struct platform_device *pdev, if (!of_device_is_compatible(pdev->dev.of_node, "aspeed,ast2400-wdt")) { res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - idx = ((intptr_t)wdt->base & 0x00000fff) / resource_size(res); + idx = ((intptr_t)wdt->base & 0x00000fff) / (uintptr_t)resource_size(res); } scu_base = syscon_regmap_lookup_by_compatible(scu.compatible); -- cgit v1.2.3-59-g8ed1b From 9bc64d338b0b4b2061049df8b701f9786857690e Mon Sep 17 00:00:00 2001 From: Andre Przywara Date: Fri, 7 Mar 2025 00:57:01 +0000 Subject: watchdog: sunxi_wdt: Add support for Allwinner A523 The Allwinner A523 SoC comes with a watchdog very similar to the ones in the previous Allwinner SoCs, but oddly enough moves the first half of its registers up by one word. Since we have different offsets for these registers across the other SoCs as well, this can simply be modelled by just stating the new offsets in our per-SoC struct. The rest of the IP is the same as in the D1, although the A523 moves its watchdog to a separate MMIO frame, so it's not embedded in the timer anymore. The driver can be ignorant of this, because the DT will take care of this. Add a new struct for the A523, specifying the SoC-specific details, and tie the new DT compatible string to it. Signed-off-by: Andre Przywara Reviewed-by: Jernej Skrabec Link: https://lore.kernel.org/r/20250307005712.16828-5-andre.przywara@arm.com Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/sunxi_wdt.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers') diff --git a/drivers/watchdog/sunxi_wdt.c b/drivers/watchdog/sunxi_wdt.c index b85354a99582..b6c761acc3de 100644 --- a/drivers/watchdog/sunxi_wdt.c +++ b/drivers/watchdog/sunxi_wdt.c @@ -236,10 +236,21 @@ static const struct sunxi_wdt_reg sun20i_wdt_reg = { .wdt_key_val = 0x16aa0000, }; +static const struct sunxi_wdt_reg sun55i_wdt_reg = { + .wdt_ctrl = 0x0c, + .wdt_cfg = 0x10, + .wdt_mode = 0x14, + .wdt_timeout_shift = 4, + .wdt_reset_mask = 0x03, + .wdt_reset_val = 0x01, + .wdt_key_val = 0x16aa0000, +}; + static const struct of_device_id sunxi_wdt_dt_ids[] = { { .compatible = "allwinner,sun4i-a10-wdt", .data = &sun4i_wdt_reg }, { .compatible = "allwinner,sun6i-a31-wdt", .data = &sun6i_wdt_reg }, { .compatible = "allwinner,sun20i-d1-wdt", .data = &sun20i_wdt_reg }, + { .compatible = "allwinner,sun55i-a523-wdt", .data = &sun55i_wdt_reg }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, sunxi_wdt_dt_ids); -- cgit v1.2.3-59-g8ed1b