From e3c21e088f8936a004a3d9f2f4e14b3f2bdd6ea9 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Wed, 19 Dec 2018 16:18:20 +0100 Subject: [PATCH] watchdog: tqmx86: Add watchdog driver for the IO controller Some TQ-Systems ComExpress modules have an IO controller with a watchdog timer. Signed-off-by: Andrew Lunn Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- drivers/watchdog/Kconfig | 12 ++++ drivers/watchdog/Makefile | 1 + drivers/watchdog/tqmx86_wdt.c | 126 ++++++++++++++++++++++++++++++++++ 3 files changed, 139 insertions(+) create mode 100644 drivers/watchdog/tqmx86_wdt.c diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 6dcd5603a3e4..57f017d74a97 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -1316,6 +1316,18 @@ config SMSC37B787_WDT Most people will say N. +config TQMX86_WDT + tristate "TQ-Systems TQMX86 Watchdog Timer" + depends on X86 + help + This is the driver for the hardware watchdog timer in the TQMX86 IO + controller found on some of their ComExpress Modules. + + To compile this driver as a module, choose M here; the module + will be called tqmx86_wdt. + + Most people will say N. + config VIA_WDT tristate "VIA Watchdog Timer" depends on X86 && PCI diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index cc90e723135e..a0917ef28e07 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -130,6 +130,7 @@ obj-$(CONFIG_SBC7240_WDT) += sbc7240_wdt.o obj-$(CONFIG_CPU5_WDT) += cpu5wdt.o obj-$(CONFIG_SMSC_SCH311X_WDT) += sch311x_wdt.o obj-$(CONFIG_SMSC37B787_WDT) += smsc37b787_wdt.o +obj-$(CONFIG_TQMX86_WDT) += tqmx86_wdt.o obj-$(CONFIG_VIA_WDT) += via_wdt.o obj-$(CONFIG_W83627HF_WDT) += w83627hf_wdt.o obj-$(CONFIG_W83877F_WDT) += w83877f_wdt.o diff --git a/drivers/watchdog/tqmx86_wdt.c b/drivers/watchdog/tqmx86_wdt.c new file mode 100644 index 000000000000..0d3a0fbbd7a5 --- /dev/null +++ b/drivers/watchdog/tqmx86_wdt.c @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Watchdog driver for TQMx86 PLD. + * + * The watchdog supports power of 2 timeouts from 1 to 4096sec. + * Once started, it cannot be stopped. + * + * Based on the vendor code written by Vadim V.Vlasov + * + */ + +#include +#include +#include +#include +#include +#include + +/* default timeout (secs) */ +#define WDT_TIMEOUT 32 + +static unsigned int timeout; +module_param(timeout, uint, 0); +MODULE_PARM_DESC(timeout, + "Watchdog timeout in seconds. (1<=timeout<=4096, default=" + __MODULE_STRING(WDT_TIMEOUT) ")"); +struct tqmx86_wdt { + struct watchdog_device wdd; + void __iomem *io_base; +}; + +#define TQMX86_WDCFG 0x00 /* Watchdog Configuration Register */ +#define TQMX86_WDCS 0x01 /* Watchdog Config/Status Register */ + +static int tqmx86_wdt_start(struct watchdog_device *wdd) +{ + struct tqmx86_wdt *priv = watchdog_get_drvdata(wdd); + + iowrite8(0x81, priv->io_base + TQMX86_WDCS); + + return 0; +} + +static int tqmx86_wdt_set_timeout(struct watchdog_device *wdd, unsigned int t) +{ + struct tqmx86_wdt *priv = watchdog_get_drvdata(wdd); + u8 val; + + t = roundup_pow_of_two(t); + val = ilog2(t) | 0x90; + val += 3; /* values 0,1,2 correspond to 0.125,0.25,0.5s timeouts */ + iowrite8(val, priv->io_base + TQMX86_WDCFG); + + wdd->timeout = t; + + return 0; +} + +static const struct watchdog_info tqmx86_wdt_info = { + .options = WDIOF_SETTIMEOUT | + WDIOF_KEEPALIVEPING, + .identity = "TQMx86 Watchdog", +}; + +static struct watchdog_ops tqmx86_wdt_ops = { + .owner = THIS_MODULE, + .start = tqmx86_wdt_start, + .set_timeout = tqmx86_wdt_set_timeout, +}; + +static int tqmx86_wdt_probe(struct platform_device *pdev) +{ + struct tqmx86_wdt *priv; + struct resource *res; + int err; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (IS_ERR(res)) + return PTR_ERR(res); + + priv->io_base = devm_ioport_map(&pdev->dev, res->start, + resource_size(res)); + if (IS_ERR(priv->io_base)) + return PTR_ERR(priv->io_base); + + watchdog_set_drvdata(&priv->wdd, priv); + + priv->wdd.parent = &pdev->dev; + priv->wdd.info = &tqmx86_wdt_info; + priv->wdd.ops = &tqmx86_wdt_ops; + priv->wdd.min_timeout = 1; + priv->wdd.max_timeout = 4096; + priv->wdd.max_hw_heartbeat_ms = 4096*1000; + priv->wdd.timeout = WDT_TIMEOUT; + + watchdog_init_timeout(&priv->wdd, timeout, &pdev->dev); + watchdog_set_nowayout(&priv->wdd, WATCHDOG_NOWAYOUT); + + tqmx86_wdt_set_timeout(&priv->wdd, priv->wdd.timeout); + + err = devm_watchdog_register_device(&pdev->dev, &priv->wdd); + if (err) + return err; + + dev_info(&pdev->dev, "TQMx86 watchdog\n"); + + return 0; +} + +static struct platform_driver tqmx86_wdt_driver = { + .driver = { + .name = "tqmx86-wdt", + }, + .probe = tqmx86_wdt_probe, +}; + +module_platform_driver(tqmx86_wdt_driver); + +MODULE_AUTHOR("Andrew Lunn "); +MODULE_DESCRIPTION("TQMx86 Watchdog"); +MODULE_ALIAS("platform:tqmx86-wdt"); +MODULE_LICENSE("GPL");