diff mbox series

drivers: watchdog: advantech_ec_wdt.c Add Advantech EC watchdog driver

Message ID Y00S08UV7g6ZSm9J@EIS-S230 (mailing list archive)
State Changes Requested
Headers show
Series drivers: watchdog: advantech_ec_wdt.c Add Advantech EC watchdog driver | expand

Commit Message

Thomas Kastner Oct. 17, 2022, 8:31 a.m. UTC
This patch adds the 'advantech_ec_wdt' kernel module which provides
WDT support for Advantech platforms with ITE based Embedded Controller.

Signed-off-by: Thomas Kastner <thomas.kastner@advantech.com>
---
 drivers/watchdog/Kconfig            |   7 +
 drivers/watchdog/Makefile           |   1 +
 drivers/watchdog/advantech_ec_wdt.c | 212 ++++++++++++++++++++++++++++
 3 files changed, 220 insertions(+)
 create mode 100644 drivers/watchdog/advantech_ec_wdt.c

Comments

Guenter Roeck Oct. 17, 2022, 12:24 p.m. UTC | #1
On 10/17/22 01:31, Thomas Kastner wrote:
> This patch adds the 'advantech_ec_wdt' kernel module which provides
> WDT support for Advantech platforms with ITE based Embedded Controller.
> 
> Signed-off-by: Thomas Kastner <thomas.kastner@advantech.com>

Please drop "drivers:" and the driver name from the subject,
version your patches, and provide change logs.

Also, please fix the following problems reported by checkpatch.

CHECK: Alignment should match open parenthesis
#144: FILE: drivers/watchdog/advantech_ec_wdt.c:55:
+MODULE_PARM_DESC(timeout,
+	"Default Watchdog timer setting ("

WARNING: quoted string split across lines
#146: FILE: drivers/watchdog/advantech_ec_wdt.c:57:
+	__MODULE_STRING(DEFAULT_TIME) "s)."
+	"The range is from " __MODULE_STRING(MIN_TIME)

CHECK: Please don't use multiple blank lines
#270: FILE: drivers/watchdog/advantech_ec_wdt.c:181:
+
+

Additional comments inline.

Thanks,
Guenter

> ---
>   drivers/watchdog/Kconfig            |   7 +
>   drivers/watchdog/Makefile           |   1 +
>   drivers/watchdog/advantech_ec_wdt.c | 212 ++++++++++++++++++++++++++++
>   3 files changed, 220 insertions(+)
>   create mode 100644 drivers/watchdog/advantech_ec_wdt.c
> 
> diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
> index 688922fc4edb..a990016f791c 100644
> --- a/drivers/watchdog/Kconfig
> +++ b/drivers/watchdog/Kconfig
> @@ -1055,6 +1055,13 @@ config ADVANTECH_WDT
>   	  feature. More information can be found at
>   	  <https://www.advantech.com.tw/products/>
>   
> +config ADVANTECH_EC_WDT
> +	tristate "Advantech Embedded Controller Watchdog Timer"
> +	depends on X86
> +	help
> +		This driver supports Advantech products with ITE based Embedded Controller.
> +		It does not support Advantech products with other ECs or without EC.
> +
>   config ALIM1535_WDT
>   	tristate "ALi M1535 PMU Watchdog Timer"
>   	depends on X86 && PCI
> diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
> index cdeb119e6e61..2768dc2348af 100644
> --- a/drivers/watchdog/Makefile
> +++ b/drivers/watchdog/Makefile
> @@ -102,6 +102,7 @@ obj-$(CONFIG_SUNPLUS_WATCHDOG) += sunplus_wdt.o
>   # X86 (i386 + ia64 + x86_64) Architecture
>   obj-$(CONFIG_ACQUIRE_WDT) += acquirewdt.o
>   obj-$(CONFIG_ADVANTECH_WDT) += advantechwdt.o
> +obj-$(CONFIG_ADVANTECH_EC_WDT) += advantech_ec_wdt.o
>   obj-$(CONFIG_ALIM1535_WDT) += alim1535_wdt.o
>   obj-$(CONFIG_ALIM7101_WDT) += alim7101_wdt.o
>   obj-$(CONFIG_EBC_C384_WDT) += ebc-c384_wdt.o
> diff --git a/drivers/watchdog/advantech_ec_wdt.c b/drivers/watchdog/advantech_ec_wdt.c
> new file mode 100644
> index 000000000000..9f00fd2e5e29
> --- /dev/null
> +++ b/drivers/watchdog/advantech_ec_wdt.c
> @@ -0,0 +1,212 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + *	Advantech Embedded Controller Watchdog Driver
> + *
> + *	This driver supports Advantech products with ITE based Embedded Controller.
> + *	It does not support Advantech products with other ECs or without EC.
> + *
> + *	This software is provided "as is" without warranty of any kind.
> + *

FWIW, this is implied in the GPL reference above.

> + *	Copyright (C) 2022 Advantech Europe B.V.
> + *	<thomas.kastner@advantech.com>

The e-mail is already listed in MODULE_AUTHOR below. It tends to
become obsolete, so I would suggest to drop it here.

> + */
> +
> +#include <linux/delay.h>
> +#include <linux/io.h>
> +#include <linux/isa.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/moduleparam.h>
> +#include <linux/watchdog.h>
> +
> +#define DRIVER_NAME		"advantech_ec_wdt"
> +
> +/* EC IO region */
> +#define EC_BASE_ADDR		0x299
> +#define EC_ADDR_EXTENT		2
> +
> +/* EC minimum IO access delay in ms */
> +#define EC_MIN_DELAY		10
> +
> +/* EC interface definitions */
> +#define EC_ADDR_CMD		0x29A
> +#define EC_ADDR_DATA		0x299

I would suggest to use EC_BASE_ADDR and (EC_BASE_ADDR + 1) for those addresses.

> +#define EC_CMD_EC_PROBE		0x30
> +#define EC_CMD_COMM		0x89
> +#define EC_CMD_WDT_START	0x28
> +#define EC_CMD_WDT_STOP		0x29
> +#define EC_CMD_WDT_RESET	0x2A
> +#define EC_DAT_EN_DLY_H		0x58
> +#define EC_DAT_EN_DLY_L		0x59
> +#define EC_DAT_RST_DLY_H	0x5E
> +#define EC_DAT_RST_DLY_L	0x5F
> +#define EC_MAGIC		0x95
> +
> +/* module parameters */
> +#define MIN_TIME		1
> +#define MAX_TIME		6000 /* 100 minutes */
> +#define DEFAULT_TIME		60
> +
> +static unsigned int timeout;
> +static ktime_t ec_timestamp;
> +
> +module_param(timeout, uint, 0);
> +MODULE_PARM_DESC(timeout,
> +	"Default Watchdog timer setting ("
> +	__MODULE_STRING(DEFAULT_TIME) "s)."
> +	"The range is from " __MODULE_STRING(MIN_TIME)
> +	" to " __MODULE_STRING(MAX_TIME) ".");
> +
> +static void adv_ec_wdt_timing_gate(void)
> +{
> +	ktime_t time_cur, time_delta;
> +
> +	/* ensure minimum delay between IO accesses*/
> +	time_cur = ktime_get();
> +	time_delta = ktime_to_ms(ktime_sub(time_cur, ec_timestamp));
> +	if (time_delta < EC_MIN_DELAY) {
> +		time_delta = EC_MIN_DELAY - time_delta;
> +		usleep_range(time_delta * 1000, (time_delta + 1) * 1000);
> +	}
> +	ec_timestamp = ktime_get();
> +}
> +
> +static void adv_ec_wdt_outb(unsigned char value, unsigned short port)
> +{
> +	adv_ec_wdt_timing_gate();
> +	outb(value, port);
> +}
> +
> +static unsigned char adv_ec_wdt_inb(unsigned short port)
> +{
> +	adv_ec_wdt_timing_gate();
> +	return inb(port);
> +}
> +
> +static int adv_ec_wdt_ping(struct watchdog_device *wdd)
> +{
> +	adv_ec_wdt_outb(EC_CMD_WDT_RESET, EC_ADDR_CMD);
> +	return 0;
> +}
> +
> +static int adv_ec_wdt_set_timeout(struct watchdog_device *wdd, unsigned int t)
> +{
> +	unsigned int val;
> +
> +	/* scale time to EC 100 ms base */
> +	val = t * 10;
> +
> +	/* reset enable delay, just in case it was set by BIOS etc. */
> +	adv_ec_wdt_outb(EC_CMD_COMM, EC_ADDR_CMD);
> +	adv_ec_wdt_outb(EC_DAT_EN_DLY_H, EC_ADDR_DATA);
> +	adv_ec_wdt_outb(0, EC_ADDR_DATA);
> +
> +	adv_ec_wdt_outb(EC_CMD_COMM, EC_ADDR_CMD);
> +	adv_ec_wdt_outb(EC_DAT_EN_DLY_L, EC_ADDR_DATA);
> +	adv_ec_wdt_outb(0, EC_ADDR_DATA);
> +
> +	/* set reset delay */
> +	adv_ec_wdt_outb(EC_CMD_COMM, EC_ADDR_CMD);
> +	adv_ec_wdt_outb(EC_DAT_RST_DLY_H, EC_ADDR_DATA);
> +	adv_ec_wdt_outb(val >> 8, EC_ADDR_DATA);
> +
> +	adv_ec_wdt_outb(EC_CMD_COMM, EC_ADDR_CMD);
> +	adv_ec_wdt_outb(EC_DAT_RST_DLY_L, EC_ADDR_DATA);
> +	adv_ec_wdt_outb(val & 0xFF, EC_ADDR_DATA);
> +
> +	wdd->timeout = t;
> +	return 0;
> +}
> +
> +static int adv_ec_wdt_start(struct watchdog_device *wdd)
> +{
> +	adv_ec_wdt_set_timeout(wdd, wdd->timeout);
> +	adv_ec_wdt_outb(EC_CMD_WDT_START, EC_ADDR_CMD);
> +
> +	return 0;
> +}
> +
> +static int adv_ec_wdt_stop(struct watchdog_device *wdd)
> +{
> +	adv_ec_wdt_outb(EC_CMD_WDT_STOP, EC_ADDR_CMD);
> +
> +	return 0;
> +}
> +
> +static const struct watchdog_info adv_ec_wdt_info = {
> +	.identity =	DRIVER_NAME,
> +	.options =	WDIOF_SETTIMEOUT |
> +			WDIOF_MAGICCLOSE |
> +			WDIOF_KEEPALIVEPING,
> +};
> +
> +static const struct watchdog_ops adv_ec_wdt_ops = {
> +	.owner =	THIS_MODULE,
> +	.start =	adv_ec_wdt_start,
> +	.stop =		adv_ec_wdt_stop,
> +	.ping =		adv_ec_wdt_ping,
> +	.set_timeout =	adv_ec_wdt_set_timeout,
> +};
> +
> +static struct watchdog_device adv_ec_wdt_dev = {
> +	.info =		&adv_ec_wdt_info,
> +	.ops =		&adv_ec_wdt_ops,
> +	.min_timeout =	MIN_TIME,
> +	.max_timeout =	MAX_TIME,
> +	.timeout =	DEFAULT_TIME,
> +};
> +
> +static int adv_ec_wdt_probe(struct device *dev, unsigned int id)
> +{
> +	if (!devm_request_region(dev, EC_BASE_ADDR, EC_ADDR_EXTENT, dev_name(dev))) {
> +		dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n",
> +			EC_BASE_ADDR, EC_BASE_ADDR + EC_ADDR_EXTENT);
> +		return -EBUSY;
> +	}
> +
> +	watchdog_init_timeout(&adv_ec_wdt_dev, timeout, dev);
> +	watchdog_stop_on_reboot(&adv_ec_wdt_dev);
> +	watchdog_stop_on_unregister(&adv_ec_wdt_dev);
> +
> +	return devm_watchdog_register_device(dev, &adv_ec_wdt_dev);
> +}
> +
> +static struct isa_driver adv_ec_wdt_driver = {
> +	.probe		= adv_ec_wdt_probe,
> +	.driver		= {
> +	.name		= DRIVER_NAME,
> +	},
> +};
> +
> +
> +static int __init adv_ec_wdt_init(void)
> +{
> +	unsigned int val;
> +
> +	/* quick probe for EC */
> +	if (!request_region(EC_BASE_ADDR, EC_ADDR_EXTENT, DRIVER_NAME))
> +		return -EBUSY;
> +
> +	adv_ec_wdt_outb(EC_CMD_EC_PROBE, EC_ADDR_CMD);
> +	val = adv_ec_wdt_inb(EC_ADDR_DATA);
> +	release_region(EC_BASE_ADDR, EC_ADDR_EXTENT);
> +
> +	if (val != EC_MAGIC)
> +		return -ENODEV;
> +
> +	return isa_register_driver(&adv_ec_wdt_driver, 1);
> +}
> +
> +static void __exit adv_ec_wdt_exit(void)
> +{
> +	isa_unregister_driver(&adv_ec_wdt_driver);
> +}
> +
> +module_init(adv_ec_wdt_init);
> +module_exit(adv_ec_wdt_exit);
> +
> +MODULE_AUTHOR("Thomas Kastner <thomas.kastne@advantech.com>");
> +MODULE_DESCRIPTION("Advantech Embedded Controller Watchdog Device Driver");
> +MODULE_LICENSE("GPL");
> +MODULE_VERSION("20221017");
> +MODULE_ALIAS("isa:" DRIVER_NAME);
Thomas Kastner Oct. 17, 2022, 1:08 p.m. UTC | #2
On Mon, Oct 17, 2022 at 05:24:07AM -0700, Guenter Roeck wrote:
> 
> Please drop "drivers:" and the driver name from the subject,
> version your patches, and provide change logs.
> 
> Also, please fix the following problems reported by checkpatch.

Thanks for your patience. 

Can you please confirm the version of checkpatch or any parameters used?

I'm using v0.32 which seems to be the latest in GKH's staging tree as
well as Linus' mainline. It has just given me the quoted string warning,
but not the others as below- although of course those are valid.
Just trying to get to the same result...

No questions on the other items, Will implement the changes and provide
a versioned v3 with change notes later.

> 
> CHECK: Alignment should match open parenthesis
> #144: FILE: drivers/watchdog/advantech_ec_wdt.c:55:
> +MODULE_PARM_DESC(timeout,
> +	"Default Watchdog timer setting ("
> 
> WARNING: quoted string split across lines
> #146: FILE: drivers/watchdog/advantech_ec_wdt.c:57:
> +	__MODULE_STRING(DEFAULT_TIME) "s)."
> +	"The range is from " __MODULE_STRING(MIN_TIME)
> 
> CHECK: Please don't use multiple blank lines
> #270: FILE: drivers/watchdog/advantech_ec_wdt.c:181:
Guenter Roeck Oct. 17, 2022, 1:12 p.m. UTC | #3
On 10/17/22 06:08, Thomas Kastner wrote:
> On Mon, Oct 17, 2022 at 05:24:07AM -0700, Guenter Roeck wrote:
>>
>> Please drop "drivers:" and the driver name from the subject,
>> version your patches, and provide change logs.
>>
>> Also, please fix the following problems reported by checkpatch.
> 
> Thanks for your patience.
> 
> Can you please confirm the version of checkpatch or any parameters used?
> 
> I'm using v0.32 which seems to be the latest in GKH's staging tree as
> well as Linus' mainline. It has just given me the quoted string warning,
> but not the others as below- although of course those are valid.
> Just trying to get to the same result...
> 
> No questions on the other items, Will implement the changes and provide
> a versioned v3 with change notes later.
> 

CHECK is only reported if checkpatch.pl --strict is used.

Guenter

>>
>> CHECK: Alignment should match open parenthesis
>> #144: FILE: drivers/watchdog/advantech_ec_wdt.c:55:
>> +MODULE_PARM_DESC(timeout,
>> +	"Default Watchdog timer setting ("
>>
>> WARNING: quoted string split across lines
>> #146: FILE: drivers/watchdog/advantech_ec_wdt.c:57:
>> +	__MODULE_STRING(DEFAULT_TIME) "s)."
>> +	"The range is from " __MODULE_STRING(MIN_TIME)
>>
>> CHECK: Please don't use multiple blank lines
>> #270: FILE: drivers/watchdog/advantech_ec_wdt.c:181:
diff mbox series

Patch

diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 688922fc4edb..a990016f791c 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -1055,6 +1055,13 @@  config ADVANTECH_WDT
 	  feature. More information can be found at
 	  <https://www.advantech.com.tw/products/>
 
+config ADVANTECH_EC_WDT
+	tristate "Advantech Embedded Controller Watchdog Timer"
+	depends on X86
+	help
+		This driver supports Advantech products with ITE based Embedded Controller.
+		It does not support Advantech products with other ECs or without EC.
+
 config ALIM1535_WDT
 	tristate "ALi M1535 PMU Watchdog Timer"
 	depends on X86 && PCI
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index cdeb119e6e61..2768dc2348af 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -102,6 +102,7 @@  obj-$(CONFIG_SUNPLUS_WATCHDOG) += sunplus_wdt.o
 # X86 (i386 + ia64 + x86_64) Architecture
 obj-$(CONFIG_ACQUIRE_WDT) += acquirewdt.o
 obj-$(CONFIG_ADVANTECH_WDT) += advantechwdt.o
+obj-$(CONFIG_ADVANTECH_EC_WDT) += advantech_ec_wdt.o
 obj-$(CONFIG_ALIM1535_WDT) += alim1535_wdt.o
 obj-$(CONFIG_ALIM7101_WDT) += alim7101_wdt.o
 obj-$(CONFIG_EBC_C384_WDT) += ebc-c384_wdt.o
diff --git a/drivers/watchdog/advantech_ec_wdt.c b/drivers/watchdog/advantech_ec_wdt.c
new file mode 100644
index 000000000000..9f00fd2e5e29
--- /dev/null
+++ b/drivers/watchdog/advantech_ec_wdt.c
@@ -0,0 +1,212 @@ 
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ *	Advantech Embedded Controller Watchdog Driver
+ *
+ *	This driver supports Advantech products with ITE based Embedded Controller.
+ *	It does not support Advantech products with other ECs or without EC.
+ *
+ *	This software is provided "as is" without warranty of any kind.
+ *
+ *	Copyright (C) 2022 Advantech Europe B.V.
+ *	<thomas.kastner@advantech.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/isa.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/watchdog.h>
+
+#define DRIVER_NAME		"advantech_ec_wdt"
+
+/* EC IO region */
+#define EC_BASE_ADDR		0x299
+#define EC_ADDR_EXTENT		2
+
+/* EC minimum IO access delay in ms */
+#define EC_MIN_DELAY		10
+
+/* EC interface definitions */
+#define EC_ADDR_CMD		0x29A
+#define EC_ADDR_DATA		0x299
+#define EC_CMD_EC_PROBE		0x30
+#define EC_CMD_COMM		0x89
+#define EC_CMD_WDT_START	0x28
+#define EC_CMD_WDT_STOP		0x29
+#define EC_CMD_WDT_RESET	0x2A
+#define EC_DAT_EN_DLY_H		0x58
+#define EC_DAT_EN_DLY_L		0x59
+#define EC_DAT_RST_DLY_H	0x5E
+#define EC_DAT_RST_DLY_L	0x5F
+#define EC_MAGIC		0x95
+
+/* module parameters */
+#define MIN_TIME		1
+#define MAX_TIME		6000 /* 100 minutes */
+#define DEFAULT_TIME		60
+
+static unsigned int timeout;
+static ktime_t ec_timestamp;
+
+module_param(timeout, uint, 0);
+MODULE_PARM_DESC(timeout,
+	"Default Watchdog timer setting ("
+	__MODULE_STRING(DEFAULT_TIME) "s)."
+	"The range is from " __MODULE_STRING(MIN_TIME)
+	" to " __MODULE_STRING(MAX_TIME) ".");
+
+static void adv_ec_wdt_timing_gate(void)
+{
+	ktime_t time_cur, time_delta;
+
+	/* ensure minimum delay between IO accesses*/
+	time_cur = ktime_get();
+	time_delta = ktime_to_ms(ktime_sub(time_cur, ec_timestamp));
+	if (time_delta < EC_MIN_DELAY) {
+		time_delta = EC_MIN_DELAY - time_delta;
+		usleep_range(time_delta * 1000, (time_delta + 1) * 1000);
+	}
+	ec_timestamp = ktime_get();
+}
+
+static void adv_ec_wdt_outb(unsigned char value, unsigned short port)
+{
+	adv_ec_wdt_timing_gate();
+	outb(value, port);
+}
+
+static unsigned char adv_ec_wdt_inb(unsigned short port)
+{
+	adv_ec_wdt_timing_gate();
+	return inb(port);
+}
+
+static int adv_ec_wdt_ping(struct watchdog_device *wdd)
+{
+	adv_ec_wdt_outb(EC_CMD_WDT_RESET, EC_ADDR_CMD);
+	return 0;
+}
+
+static int adv_ec_wdt_set_timeout(struct watchdog_device *wdd, unsigned int t)
+{
+	unsigned int val;
+
+	/* scale time to EC 100 ms base */
+	val = t * 10;
+
+	/* reset enable delay, just in case it was set by BIOS etc. */
+	adv_ec_wdt_outb(EC_CMD_COMM, EC_ADDR_CMD);
+	adv_ec_wdt_outb(EC_DAT_EN_DLY_H, EC_ADDR_DATA);
+	adv_ec_wdt_outb(0, EC_ADDR_DATA);
+
+	adv_ec_wdt_outb(EC_CMD_COMM, EC_ADDR_CMD);
+	adv_ec_wdt_outb(EC_DAT_EN_DLY_L, EC_ADDR_DATA);
+	adv_ec_wdt_outb(0, EC_ADDR_DATA);
+
+	/* set reset delay */
+	adv_ec_wdt_outb(EC_CMD_COMM, EC_ADDR_CMD);
+	adv_ec_wdt_outb(EC_DAT_RST_DLY_H, EC_ADDR_DATA);
+	adv_ec_wdt_outb(val >> 8, EC_ADDR_DATA);
+
+	adv_ec_wdt_outb(EC_CMD_COMM, EC_ADDR_CMD);
+	adv_ec_wdt_outb(EC_DAT_RST_DLY_L, EC_ADDR_DATA);
+	adv_ec_wdt_outb(val & 0xFF, EC_ADDR_DATA);
+
+	wdd->timeout = t;
+	return 0;
+}
+
+static int adv_ec_wdt_start(struct watchdog_device *wdd)
+{
+	adv_ec_wdt_set_timeout(wdd, wdd->timeout);
+	adv_ec_wdt_outb(EC_CMD_WDT_START, EC_ADDR_CMD);
+
+	return 0;
+}
+
+static int adv_ec_wdt_stop(struct watchdog_device *wdd)
+{
+	adv_ec_wdt_outb(EC_CMD_WDT_STOP, EC_ADDR_CMD);
+
+	return 0;
+}
+
+static const struct watchdog_info adv_ec_wdt_info = {
+	.identity =	DRIVER_NAME,
+	.options =	WDIOF_SETTIMEOUT |
+			WDIOF_MAGICCLOSE |
+			WDIOF_KEEPALIVEPING,
+};
+
+static const struct watchdog_ops adv_ec_wdt_ops = {
+	.owner =	THIS_MODULE,
+	.start =	adv_ec_wdt_start,
+	.stop =		adv_ec_wdt_stop,
+	.ping =		adv_ec_wdt_ping,
+	.set_timeout =	adv_ec_wdt_set_timeout,
+};
+
+static struct watchdog_device adv_ec_wdt_dev = {
+	.info =		&adv_ec_wdt_info,
+	.ops =		&adv_ec_wdt_ops,
+	.min_timeout =	MIN_TIME,
+	.max_timeout =	MAX_TIME,
+	.timeout =	DEFAULT_TIME,
+};
+
+static int adv_ec_wdt_probe(struct device *dev, unsigned int id)
+{
+	if (!devm_request_region(dev, EC_BASE_ADDR, EC_ADDR_EXTENT, dev_name(dev))) {
+		dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n",
+			EC_BASE_ADDR, EC_BASE_ADDR + EC_ADDR_EXTENT);
+		return -EBUSY;
+	}
+
+	watchdog_init_timeout(&adv_ec_wdt_dev, timeout, dev);
+	watchdog_stop_on_reboot(&adv_ec_wdt_dev);
+	watchdog_stop_on_unregister(&adv_ec_wdt_dev);
+
+	return devm_watchdog_register_device(dev, &adv_ec_wdt_dev);
+}
+
+static struct isa_driver adv_ec_wdt_driver = {
+	.probe		= adv_ec_wdt_probe,
+	.driver		= {
+	.name		= DRIVER_NAME,
+	},
+};
+
+
+static int __init adv_ec_wdt_init(void)
+{
+	unsigned int val;
+
+	/* quick probe for EC */
+	if (!request_region(EC_BASE_ADDR, EC_ADDR_EXTENT, DRIVER_NAME))
+		return -EBUSY;
+
+	adv_ec_wdt_outb(EC_CMD_EC_PROBE, EC_ADDR_CMD);
+	val = adv_ec_wdt_inb(EC_ADDR_DATA);
+	release_region(EC_BASE_ADDR, EC_ADDR_EXTENT);
+
+	if (val != EC_MAGIC)
+		return -ENODEV;
+
+	return isa_register_driver(&adv_ec_wdt_driver, 1);
+}
+
+static void __exit adv_ec_wdt_exit(void)
+{
+	isa_unregister_driver(&adv_ec_wdt_driver);
+}
+
+module_init(adv_ec_wdt_init);
+module_exit(adv_ec_wdt_exit);
+
+MODULE_AUTHOR("Thomas Kastner <thomas.kastne@advantech.com>");
+MODULE_DESCRIPTION("Advantech Embedded Controller Watchdog Device Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("20221017");
+MODULE_ALIAS("isa:" DRIVER_NAME);