// 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 device *dev = &pdev->dev; struct tqmx86_wdt *priv; struct resource *res; int err; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; res = platform_get_resource(pdev, IORESOURCE_IO, 0); if (!res) return -ENODEV; priv->io_base = devm_ioport_map(dev, res->start, resource_size(res)); if (!priv->io_base) return -ENOMEM; watchdog_set_drvdata(&priv->wdd, priv); priv->wdd.parent = 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, dev); watchdog_set_nowayout(&priv->wdd, WATCHDOG_NOWAYOUT); tqmx86_wdt_set_timeout(&priv->wdd, priv->wdd.timeout); err = devm_watchdog_register_device(dev, &priv->wdd); if (err) return err; dev_info(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");