This commit adds the driver to control the Advantech EIO Watchdog block,
this block is included in the Advantech EIO Embedded Controller.

Signed-off-by: Ramiro Oliveira <[email protected]>
---
 MAINTAINERS                |   1 +
 drivers/watchdog/Kconfig   |   7 +
 drivers/watchdog/Makefile  |   1 +
 drivers/watchdog/eio_wdt.c | 672 +++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 681 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index df4b4cc31257..dfdf4f39c14b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -624,6 +624,7 @@ F:  drivers/hwmon/eio-hwmon.c
 F:     drivers/i2c/busses/i2c-eio.c
 F:     drivers/mfd/eio_core.c
 F:     drivers/video/backlight/eio_bl.c
+F:     drivers/watchdog/eio_wdt.c
 F:     include/linux/mfd/eio.h
 
 ADXL313 THREE-AXIS DIGITAL ACCELEROMETER DRIVER
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index d3b9df7d466b..2f8508e51634 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -248,6 +248,13 @@ config DA9062_WATCHDOG
 
          This driver can be built as a module. The module name is da9062_wdt.
 
+config EIO_WATCHDOG
+       tristate "Advantech EIO Watchdog"
+       depends on MFD_EIO
+       help
+         Watchdog timer driver for the Advantech EIO.
+         If unsure, say N.
+
 config GPIO_WATCHDOG
        tristate "Watchdog device controlled through GPIO-line"
        depends on OF_GPIO
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index ba52099b1253..59b5ec0246d6 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -230,6 +230,7 @@ obj-$(CONFIG_DA9052_WATCHDOG) += da9052_wdt.o
 obj-$(CONFIG_DA9055_WATCHDOG) += da9055_wdt.o
 obj-$(CONFIG_DA9062_WATCHDOG) += da9062_wdt.o
 obj-$(CONFIG_DA9063_WATCHDOG) += da9063_wdt.o
+obj-$(CONFIG_EIO_WATCHDOG) += eio_wdt.o
 obj-$(CONFIG_GPIO_WATCHDOG)    += gpio_wdt.o
 obj-$(CONFIG_WDAT_WDT) += wdat_wdt.o
 obj-$(CONFIG_WM831X_WATCHDOG) += wm831x_wdt.o
diff --git a/drivers/watchdog/eio_wdt.c b/drivers/watchdog/eio_wdt.c
new file mode 100644
index 000000000000..a81f005d82d2
--- /dev/null
+++ b/drivers/watchdog/eio_wdt.c
@@ -0,0 +1,672 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Advantech EIO Watchdog Driver
+ *
+ * Copyright (C) 2025 Advantech Co., Ltd.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/mfd/core.h>
+#include <linux/module.h>
+#include <linux/reboot.h>
+#include <linux/uaccess.h>
+#include <linux/watchdog.h>
+#include <linux/mfd/eio.h>
+
+#define WATCHDOG_TIMEOUT       60
+#define WATCHDOG_PRETIMEOUT    10
+
+/* Support Flags */
+#define SUPPORT_AVAILABLE      BIT(0)
+#define SUPPORT_PWRBTN         BIT(3)
+#define SUPPORT_IRQ            BIT(4)
+#define SUPPORT_SCI            BIT(5)
+#define SUPPORT_PIN            BIT(6)
+#define SUPPORT_RESET          BIT(7)
+
+/* PMC registers */
+#define REG_STATUS             0x00
+#define REG_CONTROL            0x02
+#define REG_EVENT              0x10
+#define REG_PWR_EVENT_TIME     0x12
+#define REG_IRQ_EVENT_TIME     0x13
+#define REG_RESET_EVENT_TIME   0x14
+#define REG_PIN_EVENT_TIME     0x15
+#define REG_SCI_EVENT_TIME     0x16
+#define REG_IRQ_NUMBER         0x17
+
+/* PMC command and control */
+#define CMD_WDT_WRITE          0x2A
+#define CMD_WDT_READ           0x2B
+#define CTRL_STOP              0x00
+#define CTRL_START             0x01
+#define CTRL_TRIGGER           0x02
+
+/* I/O register and its flags */
+#define IOREG_UNLOCK           0x87
+#define IOREG_LOCK             0xAA
+#define IOREG_LDN              0x07
+#define IOREG_LDN_PMCIO                0x0F
+#define IOREG_IRQ              0x70
+#define IOREG_WDT_STATUS       0x30
+
+/* Flags */
+#define FLAG_WDT_ENABLED       0x01
+#define FLAG_TRIGGER_IRQ       BIT(4)
+
+/* Mapping event type to supported bit */
+#define EVENT_BIT(type)        BIT(type + 2)
+
+enum event_type {
+       EVENT_NONE,
+       EVENT_PWRBTN,
+       EVENT_IRQ,
+       EVENT_SCI,
+       EVENT_PIN
+};
+
+struct eio_wdt_dev {
+       u32 event_type;
+       u32 support;
+       int irq;
+       unsigned long last_time;
+       struct regmap *iomap;
+       struct device *mfd;
+       struct device *dev;
+       struct watchdog_device wdd;
+       struct eio_dev *core;
+};
+
+static char * const type_strs[] = {
+       "NONE",
+       "PWRBTN",
+       "IRQ",
+       "SCI",
+       "PIN",
+};
+
+static u32 type_regs[] = {
+       REG_RESET_EVENT_TIME,
+       REG_PWR_EVENT_TIME,
+       REG_IRQ_EVENT_TIME,
+       REG_SCI_EVENT_TIME,
+       REG_PIN_EVENT_TIME,
+};
+
+/* Specify the pin triggered on pretimeout or timeout */
+static char *event_type = "NONE";
+module_param(event_type, charp, 0);
+MODULE_PARM_DESC(event_type, "Watchdog timeout event type (NONE, PWRBTN, IRQ, 
SCI, PIN)");
+
+/* Specify the IRQ number when the IRQ event is triggered */
+static int irq;
+module_param(irq, int, 0);
+MODULE_PARM_DESC(irq, "The IRQ number for IRQ event");
+
+static int timeout;
+module_param(timeout, int, 0444);
+MODULE_PARM_DESC(timeout, "Set PMC command timeout value.\n");
+
+static int pmc_write(struct device *dev, u8 ctrl, void *data)
+{
+       struct pmc_op op = {
+               .cmd       = CMD_WDT_WRITE,
+               .control   = ctrl,
+               .payload   = data,
+               .size     = (ctrl <= REG_EVENT) ? 1 :
+                           (ctrl >= REG_IRQ_NUMBER) ? 1 : 4,
+               .timeout   = timeout,
+       };
+       return eio_core_pmc_operation(dev, &op);
+}
+
+static int pmc_read(struct device *dev, u8 ctrl, void *data)
+{
+       struct pmc_op op = {
+               .cmd       = CMD_WDT_READ,
+               .control   = ctrl,
+               .payload   = data,
+               .size     = (ctrl <= REG_EVENT) ? 1 :
+                           (ctrl >= REG_IRQ_NUMBER) ? 1 : 4,
+               .timeout   = timeout,
+       };
+       return eio_core_pmc_operation(dev, &op);
+}
+
+static int wdt_set_timeout(struct watchdog_device *wdd, unsigned int timeout)
+{
+       struct eio_wdt_dev *eio_wdt = watchdog_get_drvdata(wdd);
+
+       wdd->timeout = timeout;
+       dev_info(eio_wdt->dev, "Set timeout: %u\n", timeout);
+
+       return 0;
+}
+
+static int wdt_set_pretimeout(struct watchdog_device *wdd, unsigned int 
pretimeout)
+{
+       struct eio_wdt_dev *eio_wdt = watchdog_get_drvdata(wdd);
+
+       wdd->pretimeout = pretimeout;
+       dev_info(eio_wdt->dev, "Set pretimeout: %u\n", pretimeout);
+
+       return 0;
+}
+
+static int wdt_get_type(struct eio_wdt_dev *eio_wdt)
+{
+       int i;
+
+       for (i = 1; i < ARRAY_SIZE(type_strs); i++) {
+               if (strcasecmp(event_type, type_strs[i]) == 0) {
+                       if ((eio_wdt->support & EVENT_BIT(i)) == 0) {
+                               dev_err(eio_wdt->dev,
+                                       "This board doesn't support %s trigger 
type\n",
+                                       event_type);
+                               return -EINVAL;
+                       }
+
+                       dev_info(eio_wdt->dev, "Trigger type is %d:%s\n",
+                                i, type_strs[i]);
+                       eio_wdt->event_type = i;
+                       return 0;
+               }
+       }
+
+       dev_info(eio_wdt->dev, "Event type: %s\n",
+                type_strs[eio_wdt->event_type]);
+       return 0;
+}
+
+static int get_time(struct eio_wdt_dev *eio_wdt, u8 ctrl, u32 *val)
+{
+       int ret;
+
+       ret = pmc_read(eio_wdt->mfd, ctrl, val);
+       if (ret)
+               return ret;
+
+       /* ms to sec */
+       *val /= 1000;
+
+       return 0;
+}
+
+static int set_time(struct eio_wdt_dev *eio_wdt, u8 ctrl, u32 time)
+{
+       /* sec to ms */
+       time *= 1000;
+
+       return pmc_write(eio_wdt->mfd, ctrl, &time);
+}
+
+static int wdt_set_config(struct eio_wdt_dev *eio_wdt)
+{
+       int ret, type;
+       u32 event_time = 0;
+       u32 reset_time = 0;
+
+       if (eio_wdt->event_type > EVENT_PIN)
+               return -EFAULT;
+
+       /* Calculate event time and reset time */
+       if (eio_wdt->wdd.pretimeout && eio_wdt->wdd.timeout) {
+               if (eio_wdt->wdd.timeout < eio_wdt->wdd.pretimeout)
+                       return -EINVAL;
+
+               reset_time = eio_wdt->wdd.timeout;
+               event_time = eio_wdt->wdd.timeout - eio_wdt->wdd.pretimeout;
+
+       } else if (eio_wdt->wdd.timeout) {
+               reset_time = eio_wdt->event_type ?      0 : 
eio_wdt->wdd.timeout;
+               event_time = eio_wdt->event_type ? eio_wdt->wdd.timeout : 0;
+       }
+
+       /* Set reset time */
+       ret = set_time(eio_wdt, REG_RESET_EVENT_TIME, reset_time);
+       if (ret)
+               return ret;
+
+       /* Set every other times */
+       for (type = 1; type < ARRAY_SIZE(type_regs); type++) {
+               ret = set_time(eio_wdt, type_regs[type],
+                              (eio_wdt->event_type == type) ? event_time : 0);
+               if (ret)
+                       return ret;
+       }
+
+       dev_dbg(eio_wdt->dev, "Config wdt reset time %u\n", reset_time);
+       dev_dbg(eio_wdt->dev, "Config wdt event time %u\n", event_time);
+       dev_dbg(eio_wdt->dev, "Config wdt event type %s\n",
+               type_strs[eio_wdt->event_type]);
+
+       return 0;
+}
+
+static int wdt_get_config(struct eio_wdt_dev *eio_wdt)
+{
+       int ret, type;
+       u32 event_time = 0, reset_time = 0;
+
+       /* Get Reset Time */
+       ret = get_time(eio_wdt, REG_RESET_EVENT_TIME, &reset_time);
+       if (ret)
+               return ret;
+
+       dev_dbg(eio_wdt->dev, "Timeout H/W default timeout: %u secs\n", 
reset_time);
+
+       /* Get every other times */
+       for (type = 1; type < ARRAY_SIZE(type_regs); type++) {
+               if ((eio_wdt->support & EVENT_BIT(type)) == 0)
+                       continue;
+
+               ret = get_time(eio_wdt, type_regs[type], &event_time);
+               if (ret)
+                       return ret;
+
+               if (event_time == 0)
+                       continue;
+
+               if (reset_time) {
+                       if (reset_time < event_time)
+                               continue;
+
+                       eio_wdt->wdd.timeout    = reset_time;
+                       eio_wdt->wdd.pretimeout = reset_time - event_time;
+
+                       dev_dbg(eio_wdt->dev,
+                               "Pretimeout H/W enabled with event %s of %u 
secs\n",
+                               type_strs[type], eio_wdt->wdd.pretimeout);
+               } else {
+                       eio_wdt->wdd.timeout    = event_time;
+                       eio_wdt->wdd.pretimeout = 0;
+               }
+
+               eio_wdt->event_type = type;
+
+               dev_dbg(eio_wdt->dev, "Timeout H/W enabled of %u secs\n",
+                       eio_wdt->wdd.timeout);
+               return 0;
+       }
+
+       eio_wdt->event_type         = EVENT_NONE;
+       eio_wdt->wdd.pretimeout     = reset_time ? 0 : WATCHDOG_PRETIMEOUT;
+       eio_wdt->wdd.timeout        = reset_time ? reset_time : 
WATCHDOG_TIMEOUT;
+
+       dev_dbg(eio_wdt->dev, "Pretimeout H/W disabled\n");
+       return 0;
+}
+
+static int set_ctrl(struct eio_wdt_dev *eio_wdt, u8 ctrl)
+{
+       return pmc_write(eio_wdt->mfd, REG_CONTROL, &ctrl);
+}
+
+static int wdt_start(struct watchdog_device *wdd)
+{
+       struct eio_wdt_dev *eio_wdt = watchdog_get_drvdata(wdd);
+       int ret;
+
+       ret = wdt_set_config(eio_wdt);
+       if (ret)
+               return ret;
+
+       ret = set_ctrl(eio_wdt, CTRL_START);
+       if (!ret) {
+               eio_wdt->last_time = jiffies;
+               dev_dbg(eio_wdt->dev, "Watchdog started\n");
+       }
+
+       return ret;
+}
+
+static int wdt_stop(struct watchdog_device *wdd)
+{
+       struct eio_wdt_dev *eio_wdt = watchdog_get_drvdata(wdd);
+       int ret;
+
+       dev_dbg(eio_wdt->dev, "Watchdog stopped\n");
+       eio_wdt->last_time = 0;
+
+       ret = set_ctrl(eio_wdt, CTRL_STOP);
+       return ret;
+}
+
+static int wdt_ping(struct watchdog_device *wdd)
+{
+       struct eio_wdt_dev *eio_wdt = watchdog_get_drvdata(wdd);
+       int ret;
+
+       dev_dbg(eio_wdt->dev, "Watchdog ping\n");
+
+       ret = set_ctrl(eio_wdt, CTRL_TRIGGER);
+       if (!ret)
+               eio_wdt->last_time = jiffies;
+
+       return ret;
+}
+
+static unsigned int wdt_get_timeleft(struct watchdog_device *wdd)
+{
+       struct eio_wdt_dev *eio_wdt = watchdog_get_drvdata(wdd);
+       unsigned int timeleft = 0;
+
+       if (eio_wdt->last_time && wdd->timeout) {
+               unsigned long delta   = jiffies - eio_wdt->last_time;
+               unsigned int  elapsed = (unsigned int)(delta / HZ);
+
+               if (elapsed < wdd->timeout)
+                       timeleft = wdd->timeout - elapsed;
+       }
+       return timeleft;
+}
+
+static int wdt_support(struct eio_wdt_dev *eio_wdt)
+{
+       u8 support;
+
+       if (pmc_read(eio_wdt->mfd, REG_STATUS, &support))
+               return -EIO;
+
+       if (!(support & SUPPORT_AVAILABLE))
+               return -ENODEV;
+
+       if ((support & SUPPORT_RESET) != SUPPORT_RESET)
+               return -ENODEV;
+
+       eio_wdt->support = support;
+
+       return 0;
+}
+
+static int wdt_get_irq_io(struct eio_wdt_dev *eio_wdt)
+{
+       int ret  = 0;
+       int idx  = EIO_PNP_INDEX;
+       int data = EIO_PNP_DATA;
+       struct regmap *map = eio_wdt->iomap;
+
+       mutex_lock(&eio_wdt->core->mutex);
+
+       /* Unlock EC IO port */
+       ret |= regmap_write(map, idx, IOREG_UNLOCK);
+       ret |= regmap_write(map, idx, IOREG_UNLOCK);
+
+       /* Select logical device to PMC */
+       ret |= regmap_write(map, idx,  IOREG_LDN);
+       ret |= regmap_write(map, data, IOREG_LDN_PMCIO);
+
+       /* Get IRQ number */
+       ret |= regmap_write(map, idx,  IOREG_IRQ);
+       ret |= regmap_read(map, data, &eio_wdt->irq);
+
+       /* Lock back */
+       ret |= regmap_write(map, idx, IOREG_LOCK);
+
+       mutex_unlock(&eio_wdt->core->mutex);
+
+       return ret ? -EIO : 0;
+}
+
+static int wdt_get_irq_pmc(struct eio_wdt_dev *eio_wdt)
+{
+       return pmc_read(eio_wdt->mfd, REG_IRQ_NUMBER, &eio_wdt->irq);
+}
+
+static int wdt_get_irq(struct eio_wdt_dev *eio_wdt)
+{
+       int ret;
+
+       if (!(eio_wdt->support & BIT(EVENT_IRQ)))
+               return -ENODEV;
+
+       ret = wdt_get_irq_pmc(eio_wdt);
+       if (ret) {
+               dev_err(eio_wdt->dev, "Error get irq by pmc\n");
+               return ret;
+       }
+
+       if (eio_wdt->irq)
+               return 0;
+
+       /* Fallback: get IRQ number from EC IO space */
+       ret = wdt_get_irq_io(eio_wdt);
+       if (ret) {
+               dev_err(eio_wdt->dev, "Error get irq by io\n");
+               return ret;
+       }
+
+       if (!eio_wdt->irq) {
+               dev_err(eio_wdt->dev, "Error IRQ number = 0\n");
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static int wdt_set_irq_io(struct eio_wdt_dev *eio_wdt)
+{
+       int ret  = 0;
+       int idx  = EIO_PNP_INDEX;
+       int data = EIO_PNP_DATA;
+       struct regmap *map = eio_wdt->iomap;
+
+       mutex_lock(&eio_wdt->core->mutex);
+
+       /* Unlock EC IO port */
+       ret |= regmap_write(map, idx, IOREG_UNLOCK);
+       ret |= regmap_write(map, idx, IOREG_UNLOCK);
+
+       /* Select logical device to PMC */
+       ret |= regmap_write(map, idx,  IOREG_LDN);
+       ret |= regmap_write(map, data, IOREG_LDN_PMCIO);
+
+       /* Enable WDT */
+       ret |= regmap_write(map, idx,  IOREG_WDT_STATUS);
+       ret |= regmap_write(map, data, FLAG_WDT_ENABLED);
+
+       /* Set IRQ number */
+       ret |= regmap_write(map, idx,  IOREG_IRQ);
+       ret |= regmap_write(map, data, eio_wdt->irq);
+
+       /* Lock back */
+       ret |= regmap_write(map, idx, IOREG_LOCK);
+
+       mutex_unlock(&eio_wdt->core->mutex);
+
+       return ret ? -EIO : 0;
+}
+
+static int wdt_set_irq_pmc(struct eio_wdt_dev *eio_wdt)
+{
+       return pmc_write(eio_wdt->mfd, REG_IRQ_NUMBER, &eio_wdt->irq);
+}
+
+static int wdt_set_irq(struct eio_wdt_dev *eio_wdt)
+{
+       int ret;
+
+       if (!(eio_wdt->support & BIT(EVENT_IRQ)))
+               return -ENODEV;
+
+       ret = wdt_set_irq_io(eio_wdt);
+       if (ret) {
+               dev_err(eio_wdt->dev, "Error set irq by io\n");
+               return ret;
+       }
+
+       ret = wdt_set_irq_pmc(eio_wdt);
+       if (ret) {
+               dev_err(eio_wdt->dev, "Error set irq by pmc\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static int wdt_get_irq_event(struct eio_wdt_dev *eio_wdt)
+{
+       u8 status;
+
+       if (pmc_read(eio_wdt->mfd, REG_EVENT, &status))
+               return 0;
+
+       return status;
+}
+
+static irqreturn_t wdt_isr(int irq, void *arg)
+{
+       return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t wdt_threaded_isr(int irq, void *arg)
+{
+       struct eio_wdt_dev *eio_wdt = arg;
+       u8 status = wdt_get_irq_event(eio_wdt) & FLAG_TRIGGER_IRQ;
+
+       if (!status)
+               return IRQ_NONE;
+
+       if (eio_wdt->wdd.pretimeout) {
+               watchdog_notify_pretimeout(&eio_wdt->wdd);
+       } else {
+               dev_crit(eio_wdt->dev, "Watchdog expired, rebooting\n");
+               emergency_restart();
+       }
+
+       return IRQ_HANDLED;
+}
+
+static int query_irq(struct eio_wdt_dev *eio_wdt)
+{
+       int ret = 0;
+
+       if (irq) {
+               eio_wdt->irq = irq;
+       } else {
+               ret = wdt_get_irq(eio_wdt);
+               if (ret)
+                       return ret;
+       }
+
+       dev_dbg(eio_wdt->dev, "IRQ = %d\n", eio_wdt->irq);
+
+       return wdt_set_irq(eio_wdt);
+}
+
+static int wdt_init(struct eio_wdt_dev *eio_wdt)
+{
+       int ret;
+
+       ret = wdt_support(eio_wdt);
+       if (ret)
+               return ret;
+
+       ret = wdt_get_config(eio_wdt);
+       if (ret)
+               return ret;
+
+       ret = wdt_get_type(eio_wdt);
+       if (ret)
+               return ret;
+
+       if (eio_wdt->event_type == EVENT_IRQ)
+               ret = query_irq(eio_wdt);
+
+       return ret;
+}
+
+static const struct watchdog_ops wdt_ops = {
+       .owner          = THIS_MODULE,
+       .start          = wdt_start,
+       .stop           = wdt_stop,
+       .ping           = wdt_ping,
+       .set_timeout    = wdt_set_timeout,
+       .get_timeleft   = wdt_get_timeleft,
+       .set_pretimeout = wdt_set_pretimeout,
+};
+
+static struct watchdog_info wdinfo = {
+       .identity = KBUILD_MODNAME,
+       .options  = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING |
+                   WDIOF_PRETIMEOUT | WDIOF_MAGICCLOSE,
+};
+
+static int eio_wdt_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct eio_wdt_dev *eio_wdt;
+       struct watchdog_device *wdd;
+       int ret = 0;
+
+       eio_wdt = devm_kzalloc(dev, sizeof(*eio_wdt), GFP_KERNEL);
+       if (!eio_wdt)
+               return -ENOMEM;
+
+       eio_wdt->dev = dev;
+       eio_wdt->mfd = dev->parent;
+       eio_wdt->iomap = dev_get_regmap(dev->parent, NULL);
+       if (!eio_wdt->iomap)
+               return dev_err_probe(dev, -ENODEV, "parent regmap missing\n");
+
+       eio_wdt->core = dev_get_drvdata(dev->parent);
+       if (!eio_wdt->core)
+               return dev_err_probe(dev, -ENODEV, "eio_core not present\n");
+
+       ret = wdt_init(eio_wdt);
+       if (ret) {
+               dev_err(dev, "wdt_init fail\n");
+               return -EIO;
+       }
+
+       if (eio_wdt->event_type == EVENT_IRQ) {
+               ret = devm_request_threaded_irq(dev, eio_wdt->irq,
+                                               wdt_isr, wdt_threaded_isr,
+                                               IRQF_SHARED | IRQF_ONESHOT, 
pdev->name,
+                                               eio_wdt);
+               if (ret) {
+                       dev_err(dev, "IRQ %d request fail:%d. Disabled.\n",
+                               eio_wdt->irq, ret);
+                       return ret;
+               }
+       }
+
+       wdd = &eio_wdt->wdd;
+       wdd->info        = &wdinfo;
+       wdd->ops         = &wdt_ops;
+       wdd->parent      = dev;
+       wdd->min_timeout = 1;
+       wdd->max_timeout = 0x7FFF;
+
+       ret = watchdog_init_timeout(wdd, wdd->timeout, dev);
+       if (ret) {
+               dev_err(dev, "Init timeout fail\n");
+               return ret;
+       }
+
+       watchdog_stop_on_reboot(&eio_wdt->wdd);
+       watchdog_stop_on_unregister(&eio_wdt->wdd);
+
+       watchdog_set_drvdata(&eio_wdt->wdd, eio_wdt);
+       platform_set_drvdata(pdev, eio_wdt);
+
+       ret = devm_watchdog_register_device(dev, &eio_wdt->wdd);
+       if (ret)
+               dev_err(dev, "Cannot register watchdog device (err: %d)\n", 
ret);
+
+       return ret;
+}
+
+static struct platform_driver eio_wdt_driver = {
+       .probe  = eio_wdt_probe,
+       .driver = {
+               .name = "eio_wdt",
+       },
+};
+module_platform_driver(eio_wdt_driver);
+
+MODULE_AUTHOR("Wenkai Chung <[email protected]>");
+MODULE_AUTHOR("Ramiro Oliveira <[email protected]>");
+MODULE_DESCRIPTION("Watchdog interface for Advantech EIO embedded controller");
+MODULE_LICENSE("GPL");

-- 
2.43.0

Reply via email to