^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0+
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * Watchdog driver for TQMx86 PLD.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * The watchdog supports power of 2 timeouts from 1 to 4096sec.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) * Once started, it cannot be stopped.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) * Based on the vendor code written by Vadim V.Vlasov
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) * <vvlasov@dev.rtsoft.ru>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) #include <linux/io.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) #include <linux/log2.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) #include <linux/platform_device.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) #include <linux/timer.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) #include <linux/watchdog.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) /* default timeout (secs) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) #define WDT_TIMEOUT 32
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) static unsigned int timeout;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) module_param(timeout, uint, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) MODULE_PARM_DESC(timeout,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) "Watchdog timeout in seconds. (1<=timeout<=4096, default="
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) __MODULE_STRING(WDT_TIMEOUT) ")");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) struct tqmx86_wdt {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) struct watchdog_device wdd;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) void __iomem *io_base;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) #define TQMX86_WDCFG 0x00 /* Watchdog Configuration Register */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) #define TQMX86_WDCS 0x01 /* Watchdog Config/Status Register */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) static int tqmx86_wdt_start(struct watchdog_device *wdd)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) struct tqmx86_wdt *priv = watchdog_get_drvdata(wdd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) iowrite8(0x81, priv->io_base + TQMX86_WDCS);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) static int tqmx86_wdt_set_timeout(struct watchdog_device *wdd, unsigned int t)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) struct tqmx86_wdt *priv = watchdog_get_drvdata(wdd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) u8 val;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) t = roundup_pow_of_two(t);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) val = ilog2(t) | 0x90;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) val += 3; /* values 0,1,2 correspond to 0.125,0.25,0.5s timeouts */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) iowrite8(val, priv->io_base + TQMX86_WDCFG);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) wdd->timeout = t;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) static const struct watchdog_info tqmx86_wdt_info = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) .options = WDIOF_SETTIMEOUT |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) WDIOF_KEEPALIVEPING,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) .identity = "TQMx86 Watchdog",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) static struct watchdog_ops tqmx86_wdt_ops = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) .owner = THIS_MODULE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) .start = tqmx86_wdt_start,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) .set_timeout = tqmx86_wdt_set_timeout,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) static int tqmx86_wdt_probe(struct platform_device *pdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) struct device *dev = &pdev->dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) struct tqmx86_wdt *priv;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) struct resource *res;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) int err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) if (!priv)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) res = platform_get_resource(pdev, IORESOURCE_IO, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) if (!res)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) return -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) priv->io_base = devm_ioport_map(dev, res->start, resource_size(res));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) if (!priv->io_base)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) watchdog_set_drvdata(&priv->wdd, priv);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) priv->wdd.parent = dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) priv->wdd.info = &tqmx86_wdt_info;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) priv->wdd.ops = &tqmx86_wdt_ops;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) priv->wdd.min_timeout = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) priv->wdd.max_timeout = 4096;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) priv->wdd.max_hw_heartbeat_ms = 4096*1000;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) priv->wdd.timeout = WDT_TIMEOUT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) watchdog_init_timeout(&priv->wdd, timeout, dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) watchdog_set_nowayout(&priv->wdd, WATCHDOG_NOWAYOUT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) tqmx86_wdt_set_timeout(&priv->wdd, priv->wdd.timeout);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) err = devm_watchdog_register_device(dev, &priv->wdd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) if (err)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) dev_info(dev, "TQMx86 watchdog\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) static struct platform_driver tqmx86_wdt_driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) .driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) .name = "tqmx86-wdt",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) .probe = tqmx86_wdt_probe,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) module_platform_driver(tqmx86_wdt_driver);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) MODULE_AUTHOR("Andrew Lunn <andrew@lunn.ch>");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) MODULE_DESCRIPTION("TQMx86 Watchdog");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) MODULE_ALIAS("platform:tqmx86-wdt");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) MODULE_LICENSE("GPL");