Re: [PATCH v6 5/5] watchdog: aaeon: Add watchdog driver for SRG-IMX8P MCU
Guenter Roeck <[email protected]>
| Newsgroups | org.kernel.vger.linux-watchdog,dev.linux.lists.imx,org.infradead.lists.linux-arm-kernel,org.kernel.vger.linux-devicetree,org.kernel.vger.linux-gpio,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
On 6/30/26 05:51, Thomas Perrot (Schneider Electric) wrote: > Add watchdog driver for the Aaeon SRG-IMX8P embedded controller. > This driver provides system monitoring and recovery capabilities > through the MCU's watchdog timer. > > The watchdog supports start, stop, and ping operations with a maximum > hardware heartbeat of 25 seconds and a default timeout of 240 seconds. > The software timeout can be changed via the WDIOC_SETTIMEOUT ioctl, > the DT timeout-sec property, or the watchdog_timeout kernel boot > parameter. > > Co-developed-by: Jérémie Dautheribes (Schneider Electric) <[email protected]> > Signed-off-by: Jérémie Dautheribes (Schneider Electric) <[email protected]> > Signed-off-by: Thomas Perrot (Schneider Electric) <[email protected]> > Acked-by: Guenter Roeck <[email protected]> > --- > MAINTAINERS | 1 + > drivers/watchdog/Kconfig | 10 +++ > drivers/watchdog/Makefile | 1 + > drivers/watchdog/aaeon_mcu_wdt.c | 144 +++++++++++++++++++++++++++++++++++++++ > 4 files changed, 156 insertions(+) > > diff --git a/MAINTAINERS b/MAINTAINERS > index 2538f8c4bc14..7b92af42c9fd 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -193,6 +193,7 @@ S: Maintained > F: Documentation/devicetree/bindings/mfd/aaeon,srg-imx8p-mcu.yaml > F: drivers/gpio/gpio-aaeon-mcu.c > F: drivers/mfd/aaeon-mcu.c > +F: drivers/watchdog/aaeon_mcu_wdt.c > F: include/linux/mfd/aaeon-mcu.h > > AAEON UPBOARD FPGA MFD DRIVER > diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig > index d3b9df7d466b..f67a0b453316 100644 > --- a/drivers/watchdog/Kconfig > +++ b/drivers/watchdog/Kconfig > @@ -420,6 +420,16 @@ config SL28CPLD_WATCHDOG > > # ARM Architecture > > +config AAEON_MCU_WATCHDOG > + tristate "Aaeon MCU Watchdog" > + depends on MFD_AAEON_MCU > + select WATCHDOG_CORE > + help > + Select this option to enable watchdog timer support for the Aaeon > + SRG-IMX8P onboard microcontroller (MCU). This driver provides > + watchdog functionality through the MCU, allowing system monitoring > + and automatic recovery from system hangs. > + > config AIROHA_WATCHDOG > tristate "Airoha EN7581 Watchdog" > depends on ARCH_AIROHA || COMPILE_TEST > diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile > index ba52099b1253..2deec425d3ea 100644 > --- a/drivers/watchdog/Makefile > +++ b/drivers/watchdog/Makefile > @@ -37,6 +37,7 @@ obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb.o > # ALPHA Architecture > > # ARM Architecture > +obj-$(CONFIG_AAEON_MCU_WATCHDOG) += aaeon_mcu_wdt.o > obj-$(CONFIG_ARM_SP805_WATCHDOG) += sp805_wdt.o > obj-$(CONFIG_ARM_SBSA_WATCHDOG) += sbsa_gwdt.o > obj-$(CONFIG_ARMADA_37XX_WATCHDOG) += armada_37xx_wdt.o > diff --git a/drivers/watchdog/aaeon_mcu_wdt.c b/drivers/watchdog/aaeon_mcu_wdt.c > new file mode 100644 > index 000000000000..347ee8269bfd > --- /dev/null > +++ b/drivers/watchdog/aaeon_mcu_wdt.c > @@ -0,0 +1,144 @@ > +// SPDX-License-Identifier: GPL-2.0-or-later > +/* > + * Aaeon MCU Watchdog driver > + * > + * Copyright (C) 2026 Bootlin > + * Author: Jérémie Dautheribes <[email protected]> > + * Author: Thomas Perrot <[email protected]> > + */ > + > +#include <linux/mfd/aaeon-mcu.h> > +#include <linux/module.h> > +#include <linux/platform_device.h> > +#include <linux/regmap.h> > +#include <linux/watchdog.h> > + > +#define AAEON_MCU_PING_WDT 0x73 > + > +#define AAEON_MCU_WDT_TIMEOUT 240 > +#define AAEON_MCU_WDT_HEARTBEAT_MS 25000 > +#define AAEON_MCU_WDT_MIN_TIMEOUT 1 > +#define AAEON_MCU_WDT_MAX_TIMEOUT 3600 > + > +static unsigned int timeout; > +module_param(timeout, uint, 0); > +MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds"); > + > +struct aaeon_mcu_wdt { > + struct watchdog_device wdt; > + struct regmap *regmap; > +}; > + > +static int aaeon_mcu_wdt_cmd(struct aaeon_mcu_wdt *data, u8 opcode, u8 arg) > +{ > + return regmap_write(data->regmap, AAEON_MCU_REG(opcode, arg), 0); > +} > + > +static int aaeon_mcu_wdt_start(struct watchdog_device *wdt) > +{ > + struct aaeon_mcu_wdt *data = watchdog_get_drvdata(wdt); > + > + return aaeon_mcu_wdt_cmd(data, AAEON_MCU_CONTROL_WDT_OPCODE, 0x01); > +} > + > +static int aaeon_mcu_wdt_status(struct watchdog_device *wdt, bool *enabled) > +{ > + struct aaeon_mcu_wdt *data = watchdog_get_drvdata(wdt); > + unsigned int rsp; > + int ret; > + > + ret = regmap_read(data->regmap, > + AAEON_MCU_REG(AAEON_MCU_CONTROL_WDT_OPCODE, 0x02), > + &rsp); > + if (ret) > + return ret; > + > + *enabled = rsp == 0x01; > + return 0; > +} > + > +static int aaeon_mcu_wdt_stop(struct watchdog_device *wdt) > +{ > + struct aaeon_mcu_wdt *data = watchdog_get_drvdata(wdt); > + > + return aaeon_mcu_wdt_cmd(data, AAEON_MCU_CONTROL_WDT_OPCODE, 0x00); > +} > + > +static int aaeon_mcu_wdt_ping(struct watchdog_device *wdt) > +{ > + struct aaeon_mcu_wdt *data = watchdog_get_drvdata(wdt); > + > + return aaeon_mcu_wdt_cmd(data, AAEON_MCU_PING_WDT, 0x00); > +} > + > +static const struct watchdog_info aaeon_mcu_wdt_info = { > + .identity = "Aaeon MCU Watchdog", > + .options = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT > +}; > + > +static const struct watchdog_ops aaeon_mcu_wdt_ops = { > + .owner = THIS_MODULE, > + .start = aaeon_mcu_wdt_start, > + .stop = aaeon_mcu_wdt_stop, > + .ping = aaeon_mcu_wdt_ping, > +}; > + > +static int aaeon_mcu_wdt_probe(struct platform_device *pdev) > +{ > + struct device *dev = &pdev->dev; > + struct watchdog_device *wdt; > + struct aaeon_mcu_wdt *data; > + bool enabled; > + int ret; > + > + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); > + if (!data) > + return -ENOMEM; > + > + data->regmap = dev_get_regmap(dev->parent, NULL); > + if (!data->regmap) > + return -ENODEV; > + > + wdt = &data->wdt; > + wdt->parent = dev; > + wdt->info = &aaeon_mcu_wdt_info; > + wdt->ops = &aaeon_mcu_wdt_ops; > + /* > + * The MCU firmware has a fixed hardware timeout of 25 seconds that > + * cannot be changed. The watchdog core handles automatic pinging to > + * support software timeouts longer than the hardware limit. The default > + * software timeout of 240 seconds can be overridden via the DT > + * timeout-sec property or the watchdog_timeout kernel boot parameter. > + */ > + wdt->timeout = AAEON_MCU_WDT_TIMEOUT; > + wdt->min_timeout = AAEON_MCU_WDT_MIN_TIMEOUT; > + wdt->max_timeout = AAEON_MCU_WDT_MAX_TIMEOUT; > + wdt->max_hw_heartbeat_ms = AAEON_MCU_WDT_HEARTBEAT_MS; Either max_timeout or max_hw_heartbeat_ms should be set, but not both. From the include file: * @max_timeout:The watchdog devices maximum timeout value (in seconds) * as configurable from user space. Only relevant if * max_hw_heartbeat_ms is not provided. In other words, max_timeout is ignored by the watchdog core. > + watchdog_init_timeout(wdt, timeout, dev); As pointed out by Sashiko, this will not initialize the timeout from devicetree. You'll need to either adjust the code or the comment above. Thanks, Guenter > + > + watchdog_set_drvdata(wdt, data); > + watchdog_stop_on_reboot(wdt); > + > + ret = aaeon_mcu_wdt_status(wdt, &enabled); > + if (ret) > + return ret; > + > + if (enabled) > + set_bit(WDOG_HW_RUNNING, &wdt->status); > + > + return devm_watchdog_register_device(dev, wdt); > +} > + > +static struct platform_driver aaeon_mcu_wdt_driver = { > + .driver = { > + .name = "aaeon-mcu-wdt", > + }, > + .probe = aaeon_mcu_wdt_probe, > +}; > + > +module_platform_driver(aaeon_mcu_wdt_driver); > + > +MODULE_ALIAS("platform:aaeon-mcu-wdt"); > +MODULE_DESCRIPTION("Aaeon MCU Watchdog Driver"); > +MODULE_AUTHOR("Jérémie Dautheribes <[email protected]>"); > +MODULE_LICENSE("GPL"); >