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