linux/drivers/watchdog/bcm47xx_wdt.c
<<
>>
Prefs
   1/*
   2 *  Watchdog driver for Broadcom BCM47XX
   3 *
   4 *  Copyright (C) 2008 Aleksandar Radovanovic <biblbroks@sezampro.rs>
   5 *  Copyright (C) 2009 Matthieu CASTET <castet.matthieu@free.fr>
   6 *  Copyright (C) 2012-2013 Hauke Mehrtens <hauke@hauke-m.de>
   7 *
   8 *  This program is free software; you can redistribute it and/or
   9 *  modify it under the terms of the GNU General Public License
  10 *  as published by the Free Software Foundation; either version
  11 *  2 of the License, or (at your option) any later version.
  12 */
  13
  14#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  15
  16#include <linux/bcm47xx_wdt.h>
  17#include <linux/bitops.h>
  18#include <linux/errno.h>
  19#include <linux/init.h>
  20#include <linux/kernel.h>
  21#include <linux/module.h>
  22#include <linux/moduleparam.h>
  23#include <linux/platform_device.h>
  24#include <linux/reboot.h>
  25#include <linux/types.h>
  26#include <linux/watchdog.h>
  27#include <linux/timer.h>
  28#include <linux/jiffies.h>
  29
  30#define DRV_NAME                "bcm47xx_wdt"
  31
  32#define WDT_DEFAULT_TIME        30      /* seconds */
  33#define WDT_SOFTTIMER_MAX       255     /* seconds */
  34#define WDT_SOFTTIMER_THRESHOLD 60      /* seconds */
  35
  36static int timeout = WDT_DEFAULT_TIME;
  37static bool nowayout = WATCHDOG_NOWAYOUT;
  38
  39module_param(timeout, int, 0);
  40MODULE_PARM_DESC(timeout, "Watchdog time in seconds. (default="
  41                                __MODULE_STRING(WDT_DEFAULT_TIME) ")");
  42
  43module_param(nowayout, bool, 0);
  44MODULE_PARM_DESC(nowayout,
  45                "Watchdog cannot be stopped once started (default="
  46                                __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
  47
  48static inline struct bcm47xx_wdt *bcm47xx_wdt_get(struct watchdog_device *wdd)
  49{
  50        return container_of(wdd, struct bcm47xx_wdt, wdd);
  51}
  52
  53static int bcm47xx_wdt_hard_keepalive(struct watchdog_device *wdd)
  54{
  55        struct bcm47xx_wdt *wdt = bcm47xx_wdt_get(wdd);
  56
  57        wdt->timer_set_ms(wdt, wdd->timeout * 1000);
  58
  59        return 0;
  60}
  61
  62static int bcm47xx_wdt_hard_start(struct watchdog_device *wdd)
  63{
  64        return 0;
  65}
  66
  67static int bcm47xx_wdt_hard_stop(struct watchdog_device *wdd)
  68{
  69        struct bcm47xx_wdt *wdt = bcm47xx_wdt_get(wdd);
  70
  71        wdt->timer_set(wdt, 0);
  72
  73        return 0;
  74}
  75
  76static int bcm47xx_wdt_hard_set_timeout(struct watchdog_device *wdd,
  77                                        unsigned int new_time)
  78{
  79        struct bcm47xx_wdt *wdt = bcm47xx_wdt_get(wdd);
  80        u32 max_timer = wdt->max_timer_ms;
  81
  82        if (new_time < 1 || new_time > max_timer / 1000) {
  83                pr_warn("timeout value must be 1<=x<=%d, using %d\n",
  84                        max_timer / 1000, new_time);
  85                return -EINVAL;
  86        }
  87
  88        wdd->timeout = new_time;
  89        return 0;
  90}
  91
  92static struct watchdog_ops bcm47xx_wdt_hard_ops = {
  93        .owner          = THIS_MODULE,
  94        .start          = bcm47xx_wdt_hard_start,
  95        .stop           = bcm47xx_wdt_hard_stop,
  96        .ping           = bcm47xx_wdt_hard_keepalive,
  97        .set_timeout    = bcm47xx_wdt_hard_set_timeout,
  98};
  99
 100static void bcm47xx_wdt_soft_timer_tick(unsigned long data)
 101{
 102        struct bcm47xx_wdt *wdt = (struct bcm47xx_wdt *)data;
 103        u32 next_tick = min(wdt->wdd.timeout * 1000, wdt->max_timer_ms);
 104
 105        if (!atomic_dec_and_test(&wdt->soft_ticks)) {
 106                wdt->timer_set_ms(wdt, next_tick);
 107                mod_timer(&wdt->soft_timer, jiffies + HZ);
 108        } else {
 109                pr_crit("Watchdog will fire soon!!!\n");
 110        }
 111}
 112
 113static int bcm47xx_wdt_soft_keepalive(struct watchdog_device *wdd)
 114{
 115        struct bcm47xx_wdt *wdt = bcm47xx_wdt_get(wdd);
 116
 117        atomic_set(&wdt->soft_ticks, wdd->timeout);
 118
 119        return 0;
 120}
 121
 122static int bcm47xx_wdt_soft_start(struct watchdog_device *wdd)
 123{
 124        struct bcm47xx_wdt *wdt = bcm47xx_wdt_get(wdd);
 125
 126        bcm47xx_wdt_soft_keepalive(wdd);
 127        bcm47xx_wdt_soft_timer_tick((unsigned long)wdt);
 128
 129        return 0;
 130}
 131
 132static int bcm47xx_wdt_soft_stop(struct watchdog_device *wdd)
 133{
 134        struct bcm47xx_wdt *wdt = bcm47xx_wdt_get(wdd);
 135
 136        del_timer_sync(&wdt->soft_timer);
 137        wdt->timer_set(wdt, 0);
 138
 139        return 0;
 140}
 141
 142static int bcm47xx_wdt_soft_set_timeout(struct watchdog_device *wdd,
 143                                        unsigned int new_time)
 144{
 145        if (new_time < 1 || new_time > WDT_SOFTTIMER_MAX) {
 146                pr_warn("timeout value must be 1<=x<=%d, using %d\n",
 147                        WDT_SOFTTIMER_MAX, new_time);
 148                return -EINVAL;
 149        }
 150
 151        wdd->timeout = new_time;
 152        return 0;
 153}
 154
 155static const struct watchdog_info bcm47xx_wdt_info = {
 156        .identity       = DRV_NAME,
 157        .options        = WDIOF_SETTIMEOUT |
 158                                WDIOF_KEEPALIVEPING |
 159                                WDIOF_MAGICCLOSE,
 160};
 161
 162static int bcm47xx_wdt_notify_sys(struct notifier_block *this,
 163                                  unsigned long code, void *unused)
 164{
 165        struct bcm47xx_wdt *wdt;
 166
 167        wdt = container_of(this, struct bcm47xx_wdt, notifier);
 168        if (code == SYS_DOWN || code == SYS_HALT)
 169                wdt->wdd.ops->stop(&wdt->wdd);
 170        return NOTIFY_DONE;
 171}
 172
 173static struct watchdog_ops bcm47xx_wdt_soft_ops = {
 174        .owner          = THIS_MODULE,
 175        .start          = bcm47xx_wdt_soft_start,
 176        .stop           = bcm47xx_wdt_soft_stop,
 177        .ping           = bcm47xx_wdt_soft_keepalive,
 178        .set_timeout    = bcm47xx_wdt_soft_set_timeout,
 179};
 180
 181static int bcm47xx_wdt_probe(struct platform_device *pdev)
 182{
 183        int ret;
 184        bool soft;
 185        struct bcm47xx_wdt *wdt = dev_get_platdata(&pdev->dev);
 186
 187        if (!wdt)
 188                return -ENXIO;
 189
 190        soft = wdt->max_timer_ms < WDT_SOFTTIMER_THRESHOLD * 1000;
 191
 192        if (soft) {
 193                wdt->wdd.ops = &bcm47xx_wdt_soft_ops;
 194                setup_timer(&wdt->soft_timer, bcm47xx_wdt_soft_timer_tick,
 195                            (long unsigned int)wdt);
 196        } else {
 197                wdt->wdd.ops = &bcm47xx_wdt_hard_ops;
 198        }
 199
 200        wdt->wdd.info = &bcm47xx_wdt_info;
 201        wdt->wdd.timeout = WDT_DEFAULT_TIME;
 202        ret = wdt->wdd.ops->set_timeout(&wdt->wdd, timeout);
 203        if (ret)
 204                goto err_timer;
 205        watchdog_set_nowayout(&wdt->wdd, nowayout);
 206
 207        wdt->notifier.notifier_call = &bcm47xx_wdt_notify_sys;
 208
 209        ret = register_reboot_notifier(&wdt->notifier);
 210        if (ret)
 211                goto err_timer;
 212
 213        ret = watchdog_register_device(&wdt->wdd);
 214        if (ret)
 215                goto err_notifier;
 216
 217        dev_info(&pdev->dev, "BCM47xx Watchdog Timer enabled (%d seconds%s%s)\n",
 218                timeout, nowayout ? ", nowayout" : "",
 219                soft ? ", Software Timer" : "");
 220        return 0;
 221
 222err_notifier:
 223        unregister_reboot_notifier(&wdt->notifier);
 224err_timer:
 225        if (soft)
 226                del_timer_sync(&wdt->soft_timer);
 227
 228        return ret;
 229}
 230
 231static int bcm47xx_wdt_remove(struct platform_device *pdev)
 232{
 233        struct bcm47xx_wdt *wdt = dev_get_platdata(&pdev->dev);
 234
 235        if (!wdt)
 236                return -ENXIO;
 237
 238        watchdog_unregister_device(&wdt->wdd);
 239        unregister_reboot_notifier(&wdt->notifier);
 240
 241        return 0;
 242}
 243
 244static struct platform_driver bcm47xx_wdt_driver = {
 245        .driver         = {
 246                .owner  = THIS_MODULE,
 247                .name   = "bcm47xx-wdt",
 248        },
 249        .probe          = bcm47xx_wdt_probe,
 250        .remove         = bcm47xx_wdt_remove,
 251};
 252
 253module_platform_driver(bcm47xx_wdt_driver);
 254
 255MODULE_AUTHOR("Aleksandar Radovanovic");
 256MODULE_AUTHOR("Hauke Mehrtens <hauke@hauke-m.de>");
 257MODULE_DESCRIPTION("Watchdog driver for Broadcom BCM47xx");
 258MODULE_LICENSE("GPL");
 259
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.