diff options
| author | Andrew Lunn <andrew@lunn.ch> | 2018-12-19 10:18:20 -0500 |
|---|---|---|
| committer | Wim Van Sebroeck <wim@linux-watchdog.org> | 2018-12-24 07:15:07 -0500 |
| commit | e3c21e088f8936a004a3d9f2f4e14b3f2bdd6ea9 (patch) | |
| tree | da813b49c8ec459e5ab62e645322aee76824c213 | |
| parent | 6797f292e4f59211c1763de87a69c9a55fd9caef (diff) | |
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 <andrew@lunn.ch>
Reviewed-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Wim Van Sebroeck <wim@linux-watchdog.org>
| -rw-r--r-- | drivers/watchdog/Kconfig | 12 | ||||
| -rw-r--r-- | drivers/watchdog/Makefile | 1 | ||||
| -rw-r--r-- | drivers/watchdog/tqmx86_wdt.c | 126 |
3 files changed, 139 insertions, 0 deletions
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 | |||
| 1316 | 1316 | ||
| 1317 | Most people will say N. | 1317 | Most people will say N. |
| 1318 | 1318 | ||
| 1319 | config TQMX86_WDT | ||
| 1320 | tristate "TQ-Systems TQMX86 Watchdog Timer" | ||
| 1321 | depends on X86 | ||
| 1322 | help | ||
| 1323 | This is the driver for the hardware watchdog timer in the TQMX86 IO | ||
| 1324 | controller found on some of their ComExpress Modules. | ||
| 1325 | |||
| 1326 | To compile this driver as a module, choose M here; the module | ||
| 1327 | will be called tqmx86_wdt. | ||
| 1328 | |||
| 1329 | Most people will say N. | ||
| 1330 | |||
| 1319 | config VIA_WDT | 1331 | config VIA_WDT |
| 1320 | tristate "VIA Watchdog Timer" | 1332 | tristate "VIA Watchdog Timer" |
| 1321 | depends on X86 && PCI | 1333 | 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 | |||
| 130 | obj-$(CONFIG_CPU5_WDT) += cpu5wdt.o | 130 | obj-$(CONFIG_CPU5_WDT) += cpu5wdt.o |
| 131 | obj-$(CONFIG_SMSC_SCH311X_WDT) += sch311x_wdt.o | 131 | obj-$(CONFIG_SMSC_SCH311X_WDT) += sch311x_wdt.o |
| 132 | obj-$(CONFIG_SMSC37B787_WDT) += smsc37b787_wdt.o | 132 | obj-$(CONFIG_SMSC37B787_WDT) += smsc37b787_wdt.o |
| 133 | obj-$(CONFIG_TQMX86_WDT) += tqmx86_wdt.o | ||
| 133 | obj-$(CONFIG_VIA_WDT) += via_wdt.o | 134 | obj-$(CONFIG_VIA_WDT) += via_wdt.o |
| 134 | obj-$(CONFIG_W83627HF_WDT) += w83627hf_wdt.o | 135 | obj-$(CONFIG_W83627HF_WDT) += w83627hf_wdt.o |
| 135 | obj-$(CONFIG_W83877F_WDT) += w83877f_wdt.o | 136 | 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 @@ | |||
| 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 tqmx86_wdt *priv; | ||
| 74 | struct resource *res; | ||
| 75 | int err; | ||
| 76 | |||
| 77 | priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); | ||
| 78 | if (!priv) | ||
| 79 | return -ENOMEM; | ||
| 80 | |||
| 81 | res = platform_get_resource(pdev, IORESOURCE_IO, 0); | ||
| 82 | if (IS_ERR(res)) | ||
| 83 | return PTR_ERR(res); | ||
| 84 | |||
| 85 | priv->io_base = devm_ioport_map(&pdev->dev, res->start, | ||
| 86 | resource_size(res)); | ||
| 87 | if (IS_ERR(priv->io_base)) | ||
| 88 | return PTR_ERR(priv->io_base); | ||
| 89 | |||
| 90 | watchdog_set_drvdata(&priv->wdd, priv); | ||
| 91 | |||
| 92 | priv->wdd.parent = &pdev->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, &pdev->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(&pdev->dev, &priv->wdd); | ||
| 106 | if (err) | ||
| 107 | return err; | ||
| 108 | |||
| 109 | dev_info(&pdev->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"); | ||
