| 1 | |
| 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 | * <[email protected]>
|
| 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 |
|
| 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
|
| 33 | #define TQMX86_WDCS 0x01
|
| 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;
|
| 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 const 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 <[email protected]>");
|
| 124 | MODULE_DESCRIPTION("TQMx86 Watchdog");
|
| 125 | MODULE_ALIAS("platform:tqmx86-wdt");
|
| 126 | MODULE_LICENSE("GPL");
|