linux/drivers/watchdog/ts72xx_wdt.c
<<
>>
Prefs
   1/*
   2 * Watchdog driver for Technologic Systems TS-72xx based SBCs
   3 * (TS-7200, TS-7250 and TS-7260). These boards have external
   4 * glue logic CPLD chip, which includes programmable watchdog
   5 * timer.
   6 *
   7 * Copyright (c) 2009 Mika Westerberg <mika.westerberg@iki.fi>
   8 *
   9 * This driver is based on ep93xx_wdt and wm831x_wdt drivers.
  10 *
  11 * This file is licensed under the terms of the GNU General Public
  12 * License version 2. This program is licensed "as is" without any
  13 * warranty of any kind, whether express or implied.
  14 */
  15
  16#include <linux/platform_device.h>
  17#include <linux/module.h>
  18#include <linux/watchdog.h>
  19#include <linux/io.h>
  20
  21#define TS72XX_WDT_DEFAULT_TIMEOUT      30
  22
  23static int timeout;
  24module_param(timeout, int, 0);
  25MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds.");
  26
  27static bool nowayout = WATCHDOG_NOWAYOUT;
  28module_param(nowayout, bool, 0);
  29MODULE_PARM_DESC(nowayout, "Disable watchdog shutdown on close");
  30
  31/* priv->control_reg */
  32#define TS72XX_WDT_CTRL_DISABLE         0x00
  33#define TS72XX_WDT_CTRL_250MS           0x01
  34#define TS72XX_WDT_CTRL_500MS           0x02
  35#define TS72XX_WDT_CTRL_1SEC            0x03
  36#define TS72XX_WDT_CTRL_RESERVED        0x04
  37#define TS72XX_WDT_CTRL_2SEC            0x05
  38#define TS72XX_WDT_CTRL_4SEC            0x06
  39#define TS72XX_WDT_CTRL_8SEC            0x07
  40
  41/* priv->feed_reg */
  42#define TS72XX_WDT_FEED_VAL             0x05
  43
  44struct ts72xx_wdt_priv {
  45        void __iomem    *control_reg;
  46        void __iomem    *feed_reg;
  47        struct watchdog_device wdd;
  48        unsigned char regval;
  49};
  50
  51static int ts72xx_wdt_start(struct watchdog_device *wdd)
  52{
  53        struct ts72xx_wdt_priv *priv = watchdog_get_drvdata(wdd);
  54
  55        writeb(TS72XX_WDT_FEED_VAL, priv->feed_reg);
  56        writeb(priv->regval, priv->control_reg);
  57
  58        return 0;
  59}
  60
  61static int ts72xx_wdt_stop(struct watchdog_device *wdd)
  62{
  63        struct ts72xx_wdt_priv *priv = watchdog_get_drvdata(wdd);
  64
  65        writeb(TS72XX_WDT_FEED_VAL, priv->feed_reg);
  66        writeb(TS72XX_WDT_CTRL_DISABLE, priv->control_reg);
  67
  68        return 0;
  69}
  70
  71static int ts72xx_wdt_ping(struct watchdog_device *wdd)
  72{
  73        struct ts72xx_wdt_priv *priv = watchdog_get_drvdata(wdd);
  74
  75        writeb(TS72XX_WDT_FEED_VAL, priv->feed_reg);
  76
  77        return 0;
  78}
  79
  80static int ts72xx_wdt_settimeout(struct watchdog_device *wdd, unsigned int to)
  81{
  82        struct ts72xx_wdt_priv *priv = watchdog_get_drvdata(wdd);
  83
  84        if (to == 1) {
  85                priv->regval = TS72XX_WDT_CTRL_1SEC;
  86        } else if (to == 2) {
  87                priv->regval = TS72XX_WDT_CTRL_2SEC;
  88        } else if (to <= 4) {
  89                priv->regval = TS72XX_WDT_CTRL_4SEC;
  90                to = 4;
  91        } else {
  92                priv->regval = TS72XX_WDT_CTRL_8SEC;
  93                if (to <= 8)
  94                        to = 8;
  95        }
  96
  97        wdd->timeout = to;
  98
  99        if (watchdog_active(wdd)) {
 100                ts72xx_wdt_stop(wdd);
 101                ts72xx_wdt_start(wdd);
 102        }
 103
 104        return 0;
 105}
 106
 107static const struct watchdog_info ts72xx_wdt_ident = {
 108        .options                = WDIOF_KEEPALIVEPING |
 109                                  WDIOF_SETTIMEOUT |
 110                                  WDIOF_MAGICCLOSE,
 111        .firmware_version       = 1,
 112        .identity               = "TS-72XX WDT",
 113};
 114
 115static const struct watchdog_ops ts72xx_wdt_ops = {
 116        .owner          = THIS_MODULE,
 117        .start          = ts72xx_wdt_start,
 118        .stop           = ts72xx_wdt_stop,
 119        .ping           = ts72xx_wdt_ping,
 120        .set_timeout    = ts72xx_wdt_settimeout,
 121};
 122
 123static int ts72xx_wdt_probe(struct platform_device *pdev)
 124{
 125        struct device *dev = &pdev->dev;
 126        struct ts72xx_wdt_priv *priv;
 127        struct watchdog_device *wdd;
 128        int ret;
 129
 130        priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
 131        if (!priv)
 132                return -ENOMEM;
 133
 134        priv->control_reg = devm_platform_ioremap_resource(pdev, 0);
 135        if (IS_ERR(priv->control_reg))
 136                return PTR_ERR(priv->control_reg);
 137
 138        priv->feed_reg = devm_platform_ioremap_resource(pdev, 1);
 139        if (IS_ERR(priv->feed_reg))
 140                return PTR_ERR(priv->feed_reg);
 141
 142        wdd = &priv->wdd;
 143        wdd->info = &ts72xx_wdt_ident;
 144        wdd->ops = &ts72xx_wdt_ops;
 145        wdd->min_timeout = 1;
 146        wdd->max_hw_heartbeat_ms = 8000;
 147        wdd->parent = dev;
 148
 149        watchdog_set_nowayout(wdd, nowayout);
 150
 151        wdd->timeout = TS72XX_WDT_DEFAULT_TIMEOUT;
 152        watchdog_init_timeout(wdd, timeout, dev);
 153
 154        watchdog_set_drvdata(wdd, priv);
 155
 156        ret = devm_watchdog_register_device(dev, wdd);
 157        if (ret)
 158                return ret;
 159
 160        dev_info(dev, "TS-72xx Watchdog driver\n");
 161
 162        return 0;
 163}
 164
 165static struct platform_driver ts72xx_wdt_driver = {
 166        .probe          = ts72xx_wdt_probe,
 167        .driver         = {
 168                .name   = "ts72xx-wdt",
 169        },
 170};
 171
 172module_platform_driver(ts72xx_wdt_driver);
 173
 174MODULE_AUTHOR("Mika Westerberg <mika.westerberg@iki.fi>");
 175MODULE_DESCRIPTION("TS-72xx SBC Watchdog");
 176MODULE_LICENSE("GPL");
 177MODULE_ALIAS("platform:ts72xx-wdt");
 178