root/drivers/watchdog/bcm47xx_wdt.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. bcm47xx_wdt_get
  2. bcm47xx_wdt_hard_keepalive
  3. bcm47xx_wdt_hard_start
  4. bcm47xx_wdt_hard_stop
  5. bcm47xx_wdt_hard_set_timeout
  6. bcm47xx_wdt_restart
  7. bcm47xx_wdt_soft_timer_tick
  8. bcm47xx_wdt_soft_keepalive
  9. bcm47xx_wdt_soft_start
  10. bcm47xx_wdt_soft_stop
  11. bcm47xx_wdt_soft_set_timeout
  12. bcm47xx_wdt_probe
  13. bcm47xx_wdt_remove

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

/* [<][>][^][v][top][bottom][index][help] */