root/drivers/watchdog/mlx_wdt.c

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

DEFINITIONS

This source file includes following definitions.
  1. mlxreg_wdt_check_card_reset
  2. mlxreg_wdt_start
  3. mlxreg_wdt_stop
  4. mlxreg_wdt_ping
  5. mlxreg_wdt_set_timeout
  6. mlxreg_wdt_get_timeleft
  7. mlxreg_wdt_config
  8. mlxreg_wdt_init_timeout
  9. mlxreg_wdt_probe

   1 // SPDX-License-Identifier: GPL-2.0+
   2 /*
   3  * Mellanox watchdog driver
   4  *
   5  * Copyright (C) 2019 Mellanox Technologies
   6  * Copyright (C) 2019 Michael Shych <mshych@mellanox.com>
   7  */
   8 
   9 #include <linux/bitops.h>
  10 #include <linux/device.h>
  11 #include <linux/errno.h>
  12 #include <linux/log2.h>
  13 #include <linux/module.h>
  14 #include <linux/platform_data/mlxreg.h>
  15 #include <linux/platform_device.h>
  16 #include <linux/regmap.h>
  17 #include <linux/spinlock.h>
  18 #include <linux/types.h>
  19 #include <linux/watchdog.h>
  20 
  21 #define MLXREG_WDT_CLOCK_SCALE          1000
  22 #define MLXREG_WDT_MAX_TIMEOUT_TYPE1    32
  23 #define MLXREG_WDT_MAX_TIMEOUT_TYPE2    255
  24 #define MLXREG_WDT_MIN_TIMEOUT          1
  25 #define MLXREG_WDT_OPTIONS_BASE (WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE | \
  26                                  WDIOF_SETTIMEOUT)
  27 
  28 /**
  29  * struct mlxreg_wdt - wd private data:
  30  *
  31  * @wdd:        watchdog device;
  32  * @device:     basic device;
  33  * @pdata:      data received from platform driver;
  34  * @regmap:     register map of parent device;
  35  * @timeout:    defined timeout in sec.;
  36  * @action_idx: index for direct access to action register;
  37  * @timeout_idx:index for direct access to TO register;
  38  * @tleft_idx:  index for direct access to time left register;
  39  * @ping_idx:   index for direct access to ping register;
  40  * @reset_idx:  index for direct access to reset cause register;
  41  * @wd_type:    watchdog HW type;
  42  */
  43 struct mlxreg_wdt {
  44         struct watchdog_device wdd;
  45         struct mlxreg_core_platform_data *pdata;
  46         void *regmap;
  47         int action_idx;
  48         int timeout_idx;
  49         int tleft_idx;
  50         int ping_idx;
  51         int reset_idx;
  52         enum mlxreg_wdt_type wdt_type;
  53 };
  54 
  55 static void mlxreg_wdt_check_card_reset(struct mlxreg_wdt *wdt)
  56 {
  57         struct mlxreg_core_data *reg_data;
  58         u32 regval;
  59         int rc;
  60 
  61         if (wdt->reset_idx == -EINVAL)
  62                 return;
  63 
  64         if (!(wdt->wdd.info->options & WDIOF_CARDRESET))
  65                 return;
  66 
  67         reg_data = &wdt->pdata->data[wdt->reset_idx];
  68         rc = regmap_read(wdt->regmap, reg_data->reg, &regval);
  69         if (!rc) {
  70                 if (regval & ~reg_data->mask) {
  71                         wdt->wdd.bootstatus = WDIOF_CARDRESET;
  72                         dev_info(wdt->wdd.parent,
  73                                  "watchdog previously reset the CPU\n");
  74                 }
  75         }
  76 }
  77 
  78 static int mlxreg_wdt_start(struct watchdog_device *wdd)
  79 {
  80         struct mlxreg_wdt *wdt = watchdog_get_drvdata(wdd);
  81         struct mlxreg_core_data *reg_data = &wdt->pdata->data[wdt->action_idx];
  82 
  83         return regmap_update_bits(wdt->regmap, reg_data->reg, ~reg_data->mask,
  84                                   BIT(reg_data->bit));
  85 }
  86 
  87 static int mlxreg_wdt_stop(struct watchdog_device *wdd)
  88 {
  89         struct mlxreg_wdt *wdt = watchdog_get_drvdata(wdd);
  90         struct mlxreg_core_data *reg_data = &wdt->pdata->data[wdt->action_idx];
  91 
  92         return regmap_update_bits(wdt->regmap, reg_data->reg, ~reg_data->mask,
  93                                   ~BIT(reg_data->bit));
  94 }
  95 
  96 static int mlxreg_wdt_ping(struct watchdog_device *wdd)
  97 {
  98         struct mlxreg_wdt *wdt = watchdog_get_drvdata(wdd);
  99         struct mlxreg_core_data *reg_data = &wdt->pdata->data[wdt->ping_idx];
 100 
 101         return regmap_update_bits_base(wdt->regmap, reg_data->reg,
 102                                        ~reg_data->mask, BIT(reg_data->bit),
 103                                        NULL, false, true);
 104 }
 105 
 106 static int mlxreg_wdt_set_timeout(struct watchdog_device *wdd,
 107                                   unsigned int timeout)
 108 {
 109         struct mlxreg_wdt *wdt = watchdog_get_drvdata(wdd);
 110         struct mlxreg_core_data *reg_data = &wdt->pdata->data[wdt->timeout_idx];
 111         u32 regval, set_time, hw_timeout;
 112         int rc;
 113 
 114         if (wdt->wdt_type == MLX_WDT_TYPE1) {
 115                 rc = regmap_read(wdt->regmap, reg_data->reg, &regval);
 116                 if (rc)
 117                         return rc;
 118 
 119                 hw_timeout = order_base_2(timeout * MLXREG_WDT_CLOCK_SCALE);
 120                 regval = (regval & reg_data->mask) | hw_timeout;
 121                 /* Rowndown to actual closest number of sec. */
 122                 set_time = BIT(hw_timeout) / MLXREG_WDT_CLOCK_SCALE;
 123         } else {
 124                 set_time = timeout;
 125                 regval = timeout;
 126         }
 127 
 128         wdd->timeout = set_time;
 129         rc = regmap_write(wdt->regmap, reg_data->reg, regval);
 130 
 131         if (!rc) {
 132                 /*
 133                  * Restart watchdog with new timeout period
 134                  * if watchdog is already started.
 135                  */
 136                 if (watchdog_active(wdd)) {
 137                         rc = mlxreg_wdt_stop(wdd);
 138                         if (!rc)
 139                                 rc = mlxreg_wdt_start(wdd);
 140                 }
 141         }
 142 
 143         return rc;
 144 }
 145 
 146 static unsigned int mlxreg_wdt_get_timeleft(struct watchdog_device *wdd)
 147 {
 148         struct mlxreg_wdt *wdt = watchdog_get_drvdata(wdd);
 149         struct mlxreg_core_data *reg_data = &wdt->pdata->data[wdt->tleft_idx];
 150         u32 regval;
 151         int rc;
 152 
 153         rc = regmap_read(wdt->regmap, reg_data->reg, &regval);
 154         /* Return 0 timeleft in case of failure register read. */
 155         return rc == 0 ? regval : 0;
 156 }
 157 
 158 static const struct watchdog_ops mlxreg_wdt_ops_type1 = {
 159         .start          = mlxreg_wdt_start,
 160         .stop           = mlxreg_wdt_stop,
 161         .ping           = mlxreg_wdt_ping,
 162         .set_timeout    = mlxreg_wdt_set_timeout,
 163         .owner          = THIS_MODULE,
 164 };
 165 
 166 static const struct watchdog_ops mlxreg_wdt_ops_type2 = {
 167         .start          = mlxreg_wdt_start,
 168         .stop           = mlxreg_wdt_stop,
 169         .ping           = mlxreg_wdt_ping,
 170         .set_timeout    = mlxreg_wdt_set_timeout,
 171         .get_timeleft   = mlxreg_wdt_get_timeleft,
 172         .owner          = THIS_MODULE,
 173 };
 174 
 175 static const struct watchdog_info mlxreg_wdt_main_info = {
 176         .options        = MLXREG_WDT_OPTIONS_BASE
 177                         | WDIOF_CARDRESET,
 178         .identity       = "mlx-wdt-main",
 179 };
 180 
 181 static const struct watchdog_info mlxreg_wdt_aux_info = {
 182         .options        = MLXREG_WDT_OPTIONS_BASE
 183                         | WDIOF_ALARMONLY,
 184         .identity       = "mlx-wdt-aux",
 185 };
 186 
 187 static void mlxreg_wdt_config(struct mlxreg_wdt *wdt,
 188                               struct mlxreg_core_platform_data *pdata)
 189 {
 190         struct mlxreg_core_data *data = pdata->data;
 191         int i;
 192 
 193         wdt->reset_idx = -EINVAL;
 194         for (i = 0; i < pdata->counter; i++, data++) {
 195                 if (strnstr(data->label, "action", sizeof(data->label)))
 196                         wdt->action_idx = i;
 197                 else if (strnstr(data->label, "timeout", sizeof(data->label)))
 198                         wdt->timeout_idx = i;
 199                 else if (strnstr(data->label, "timeleft", sizeof(data->label)))
 200                         wdt->tleft_idx = i;
 201                 else if (strnstr(data->label, "ping", sizeof(data->label)))
 202                         wdt->ping_idx = i;
 203                 else if (strnstr(data->label, "reset", sizeof(data->label)))
 204                         wdt->reset_idx = i;
 205         }
 206 
 207         wdt->pdata = pdata;
 208         if (strnstr(pdata->identity, mlxreg_wdt_main_info.identity,
 209                     sizeof(mlxreg_wdt_main_info.identity)))
 210                 wdt->wdd.info = &mlxreg_wdt_main_info;
 211         else
 212                 wdt->wdd.info = &mlxreg_wdt_aux_info;
 213 
 214         wdt->wdt_type = pdata->version;
 215         if (wdt->wdt_type == MLX_WDT_TYPE2) {
 216                 wdt->wdd.ops = &mlxreg_wdt_ops_type2;
 217                 wdt->wdd.max_timeout = MLXREG_WDT_MAX_TIMEOUT_TYPE2;
 218         } else {
 219                 wdt->wdd.ops = &mlxreg_wdt_ops_type1;
 220                 wdt->wdd.max_timeout = MLXREG_WDT_MAX_TIMEOUT_TYPE1;
 221         }
 222         wdt->wdd.min_timeout = MLXREG_WDT_MIN_TIMEOUT;
 223 }
 224 
 225 static int mlxreg_wdt_init_timeout(struct mlxreg_wdt *wdt,
 226                                    struct mlxreg_core_platform_data *pdata)
 227 {
 228         u32 timeout;
 229 
 230         timeout = pdata->data[wdt->timeout_idx].health_cntr;
 231         return mlxreg_wdt_set_timeout(&wdt->wdd, timeout);
 232 }
 233 
 234 static int mlxreg_wdt_probe(struct platform_device *pdev)
 235 {
 236         struct device *dev = &pdev->dev;
 237         struct mlxreg_core_platform_data *pdata;
 238         struct mlxreg_wdt *wdt;
 239         int rc;
 240 
 241         pdata = dev_get_platdata(dev);
 242         if (!pdata) {
 243                 dev_err(dev, "Failed to get platform data.\n");
 244                 return -EINVAL;
 245         }
 246         wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
 247         if (!wdt)
 248                 return -ENOMEM;
 249 
 250         wdt->wdd.parent = dev;
 251         wdt->regmap = pdata->regmap;
 252         mlxreg_wdt_config(wdt, pdata);
 253 
 254         if ((pdata->features & MLXREG_CORE_WD_FEATURE_NOWAYOUT))
 255                 watchdog_set_nowayout(&wdt->wdd, WATCHDOG_NOWAYOUT);
 256         watchdog_stop_on_reboot(&wdt->wdd);
 257         watchdog_stop_on_unregister(&wdt->wdd);
 258         watchdog_set_drvdata(&wdt->wdd, wdt);
 259         rc = mlxreg_wdt_init_timeout(wdt, pdata);
 260         if (rc)
 261                 goto register_error;
 262 
 263         if ((pdata->features & MLXREG_CORE_WD_FEATURE_START_AT_BOOT)) {
 264                 rc = mlxreg_wdt_start(&wdt->wdd);
 265                 if (rc)
 266                         goto register_error;
 267                 set_bit(WDOG_HW_RUNNING, &wdt->wdd.status);
 268         }
 269         mlxreg_wdt_check_card_reset(wdt);
 270         rc = devm_watchdog_register_device(dev, &wdt->wdd);
 271 
 272 register_error:
 273         if (rc)
 274                 dev_err(dev, "Cannot register watchdog device (err=%d)\n", rc);
 275         return rc;
 276 }
 277 
 278 static struct platform_driver mlxreg_wdt_driver = {
 279         .probe  = mlxreg_wdt_probe,
 280         .driver = {
 281                         .name = "mlx-wdt",
 282         },
 283 };
 284 
 285 module_platform_driver(mlxreg_wdt_driver);
 286 
 287 MODULE_AUTHOR("Michael Shych <michaelsh@mellanox.com>");
 288 MODULE_DESCRIPTION("Mellanox watchdog driver");
 289 MODULE_LICENSE("GPL");
 290 MODULE_ALIAS("platform:mlx-wdt");

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