Linux Watchdog driver development
 help / color / mirror / Atom feed
From: Thomas Kastner <code@kastnerfamily.de>
To: Guenter Roeck <linux@roeck-us.net>
Cc: Thomas Kastner <thomas.kastner@advantech.com>,
	linux-watchdog@vger.kernel.org
Subject: Re: [PATCH v4 20221019] watchdog: Add Advantech EC watchdog driver
Date: Mon, 24 Oct 2022 17:07:12 +0200	[thread overview]
Message-ID: <Y1aqIL5GdNvEM07o@EIS-S230> (raw)
In-Reply-To: <b428bc31-2ff7-e6c7-33fa-96457ee7ba93@roeck-us.net>

On Wed, Oct 19, 2022 at 05:56:52PM -0700, Guenter Roeck wrote:
> On 10/19/22 00:39, 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>
> 
> Reviewed-by: Guenter Roeck <linux@roeck-us.net>

Tested-by: Thomas Kastner <thomas.kastner@advantech.com>

Tested on a variety of Advantech platforms (e.g. EIS-S230, DS-200) by myself as well as our AE team.

Regards,
Thomas

> 
> > ---
> > 
> > v4:
> > - fixed typo in MODULE_AUTHOR email address
> > 
> > v3:
> > - removed author email and disclaimer in file header
> > - changed EC_ADDR_DATA/CMD to be based on EC_BASE_ADDR instead of fixed
> > - fixed checkpatch.pl reported issues
> >    long line preferred over "quoted strings split across lines" due to grep
> > 
> > v2:
> > - added accessor function for EC IO access to remove unnecessary waits
> > - moved EC probe to adv_ec_wdt_init()
> > - implemented cosmetic changes found in the code review
> > - removed debug output and unnecessary error messages
> > 
> > v1:
> > - initial version
> > 
> >   drivers/watchdog/Kconfig            |   7 +
> >   drivers/watchdog/Makefile           |   1 +
> >   drivers/watchdog/advantech_ec_wdt.c | 205 ++++++++++++++++++++++++++++
> >   3 files changed, 213 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..7c380f90ca58
> > --- /dev/null
> > +++ b/drivers/watchdog/advantech_ec_wdt.c
> > @@ -0,0 +1,205 @@
> > +// 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.
> > + *
> > + *	Copyright (C) 2022 Advantech Europe B.V.
> > + */
> > +
> > +#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		(EC_BASE_ADDR + 1)
> > +#define EC_ADDR_DATA		EC_BASE_ADDR
> > +#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.kastner@advantech.com>");
> > +MODULE_DESCRIPTION("Advantech Embedded Controller Watchdog Device Driver");
> > +MODULE_LICENSE("GPL");
> > +MODULE_VERSION("20221019");
> > +MODULE_ALIAS("isa:" DRIVER_NAME);
> 

      reply	other threads:[~2022-10-24 16:29 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-10-19  7:39 [PATCH v4 20221019] watchdog: Add Advantech EC watchdog driver Thomas Kastner
2022-10-20  0:56 ` Guenter Roeck
2022-10-24 15:07   ` Thomas Kastner [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=Y1aqIL5GdNvEM07o@EIS-S230 \
    --to=code@kastnerfamily.de \
    --cc=linux-watchdog@vger.kernel.org \
    --cc=linux@roeck-us.net \
    --cc=thomas.kastner@advantech.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox