Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * Mellanox watchdog driver
0004  *
0005  * Copyright (C) 2019 Mellanox Technologies
0006  * Copyright (C) 2019 Michael Shych <mshych@mellanox.com>
0007  */
0008 
0009 #include <linux/bitops.h>
0010 #include <linux/device.h>
0011 #include <linux/errno.h>
0012 #include <linux/log2.h>
0013 #include <linux/module.h>
0014 #include <linux/platform_data/mlxreg.h>
0015 #include <linux/platform_device.h>
0016 #include <linux/regmap.h>
0017 #include <linux/spinlock.h>
0018 #include <linux/types.h>
0019 #include <linux/watchdog.h>
0020 
0021 #define MLXREG_WDT_CLOCK_SCALE      1000
0022 #define MLXREG_WDT_MAX_TIMEOUT_TYPE1    32
0023 #define MLXREG_WDT_MAX_TIMEOUT_TYPE2    255
0024 #define MLXREG_WDT_MAX_TIMEOUT_TYPE3    65535
0025 #define MLXREG_WDT_MIN_TIMEOUT      1
0026 #define MLXREG_WDT_OPTIONS_BASE (WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE | \
0027                  WDIOF_SETTIMEOUT)
0028 
0029 /**
0030  * struct mlxreg_wdt - wd private data:
0031  *
0032  * @wdd:    watchdog device;
0033  * @device: basic device;
0034  * @pdata:  data received from platform driver;
0035  * @regmap: register map of parent device;
0036  * @timeout:    defined timeout in sec.;
0037  * @action_idx: index for direct access to action register;
0038  * @timeout_idx:index for direct access to TO register;
0039  * @tleft_idx:  index for direct access to time left register;
0040  * @ping_idx:   index for direct access to ping register;
0041  * @reset_idx:  index for direct access to reset cause register;
0042  * @wd_type:    watchdog HW type;
0043  */
0044 struct mlxreg_wdt {
0045     struct watchdog_device wdd;
0046     struct mlxreg_core_platform_data *pdata;
0047     void *regmap;
0048     int action_idx;
0049     int timeout_idx;
0050     int tleft_idx;
0051     int ping_idx;
0052     int reset_idx;
0053     int regmap_val_sz;
0054     enum mlxreg_wdt_type wdt_type;
0055 };
0056 
0057 static void mlxreg_wdt_check_card_reset(struct mlxreg_wdt *wdt)
0058 {
0059     struct mlxreg_core_data *reg_data;
0060     u32 regval;
0061     int rc;
0062 
0063     if (wdt->reset_idx == -EINVAL)
0064         return;
0065 
0066     if (!(wdt->wdd.info->options & WDIOF_CARDRESET))
0067         return;
0068 
0069     reg_data = &wdt->pdata->data[wdt->reset_idx];
0070     rc = regmap_read(wdt->regmap, reg_data->reg, &regval);
0071     if (!rc) {
0072         if (regval & ~reg_data->mask) {
0073             wdt->wdd.bootstatus = WDIOF_CARDRESET;
0074             dev_info(wdt->wdd.parent,
0075                  "watchdog previously reset the CPU\n");
0076         }
0077     }
0078 }
0079 
0080 static int mlxreg_wdt_start(struct watchdog_device *wdd)
0081 {
0082     struct mlxreg_wdt *wdt = watchdog_get_drvdata(wdd);
0083     struct mlxreg_core_data *reg_data = &wdt->pdata->data[wdt->action_idx];
0084 
0085     return regmap_update_bits(wdt->regmap, reg_data->reg, ~reg_data->mask,
0086                   BIT(reg_data->bit));
0087 }
0088 
0089 static int mlxreg_wdt_stop(struct watchdog_device *wdd)
0090 {
0091     struct mlxreg_wdt *wdt = watchdog_get_drvdata(wdd);
0092     struct mlxreg_core_data *reg_data = &wdt->pdata->data[wdt->action_idx];
0093 
0094     return regmap_update_bits(wdt->regmap, reg_data->reg, ~reg_data->mask,
0095                   ~BIT(reg_data->bit));
0096 }
0097 
0098 static int mlxreg_wdt_ping(struct watchdog_device *wdd)
0099 {
0100     struct mlxreg_wdt *wdt = watchdog_get_drvdata(wdd);
0101     struct mlxreg_core_data *reg_data = &wdt->pdata->data[wdt->ping_idx];
0102 
0103     return regmap_write_bits(wdt->regmap, reg_data->reg, ~reg_data->mask,
0104                  BIT(reg_data->bit));
0105 }
0106 
0107 static int mlxreg_wdt_set_timeout(struct watchdog_device *wdd,
0108                   unsigned int timeout)
0109 {
0110     struct mlxreg_wdt *wdt = watchdog_get_drvdata(wdd);
0111     struct mlxreg_core_data *reg_data = &wdt->pdata->data[wdt->timeout_idx];
0112     u32 regval, set_time, hw_timeout;
0113     int rc;
0114 
0115     switch (wdt->wdt_type) {
0116     case MLX_WDT_TYPE1:
0117         rc = regmap_read(wdt->regmap, reg_data->reg, &regval);
0118         if (rc)
0119             return rc;
0120 
0121         hw_timeout = order_base_2(timeout * MLXREG_WDT_CLOCK_SCALE);
0122         regval = (regval & reg_data->mask) | hw_timeout;
0123         /* Rowndown to actual closest number of sec. */
0124         set_time = BIT(hw_timeout) / MLXREG_WDT_CLOCK_SCALE;
0125         rc = regmap_write(wdt->regmap, reg_data->reg, regval);
0126         break;
0127     case MLX_WDT_TYPE2:
0128         set_time = timeout;
0129         rc = regmap_write(wdt->regmap, reg_data->reg, timeout);
0130         break;
0131     case MLX_WDT_TYPE3:
0132         /* WD_TYPE3 has 2B set time register */
0133         set_time = timeout;
0134         if (wdt->regmap_val_sz == 1) {
0135             regval = timeout & 0xff;
0136             rc = regmap_write(wdt->regmap, reg_data->reg, regval);
0137             if (!rc) {
0138                 regval = (timeout & 0xff00) >> 8;
0139                 rc = regmap_write(wdt->regmap,
0140                         reg_data->reg + 1, regval);
0141             }
0142         } else {
0143             rc = regmap_write(wdt->regmap, reg_data->reg, timeout);
0144         }
0145         break;
0146     default:
0147         return -EINVAL;
0148     }
0149 
0150     wdd->timeout = set_time;
0151     if (!rc) {
0152         /*
0153          * Restart watchdog with new timeout period
0154          * if watchdog is already started.
0155          */
0156         if (watchdog_active(wdd)) {
0157             rc = mlxreg_wdt_stop(wdd);
0158             if (!rc)
0159                 rc = mlxreg_wdt_start(wdd);
0160         }
0161     }
0162 
0163     return rc;
0164 }
0165 
0166 static unsigned int mlxreg_wdt_get_timeleft(struct watchdog_device *wdd)
0167 {
0168     struct mlxreg_wdt *wdt = watchdog_get_drvdata(wdd);
0169     struct mlxreg_core_data *reg_data = &wdt->pdata->data[wdt->tleft_idx];
0170     u32 regval, msb, lsb;
0171     int rc;
0172 
0173     if (wdt->wdt_type == MLX_WDT_TYPE2) {
0174         rc = regmap_read(wdt->regmap, reg_data->reg, &regval);
0175     } else {
0176         /* WD_TYPE3 has 2 byte timeleft register */
0177         if (wdt->regmap_val_sz == 1) {
0178             rc = regmap_read(wdt->regmap, reg_data->reg, &lsb);
0179             if (!rc) {
0180                 rc = regmap_read(wdt->regmap,
0181                         reg_data->reg + 1, &msb);
0182                 regval = (msb & 0xff) << 8 | (lsb & 0xff);
0183             }
0184         } else {
0185             rc = regmap_read(wdt->regmap, reg_data->reg, &regval);
0186         }
0187     }
0188 
0189     /* Return 0 timeleft in case of failure register read. */
0190     return rc == 0 ? regval : 0;
0191 }
0192 
0193 static const struct watchdog_ops mlxreg_wdt_ops_type1 = {
0194     .start      = mlxreg_wdt_start,
0195     .stop       = mlxreg_wdt_stop,
0196     .ping       = mlxreg_wdt_ping,
0197     .set_timeout    = mlxreg_wdt_set_timeout,
0198     .owner      = THIS_MODULE,
0199 };
0200 
0201 static const struct watchdog_ops mlxreg_wdt_ops_type2 = {
0202     .start      = mlxreg_wdt_start,
0203     .stop       = mlxreg_wdt_stop,
0204     .ping       = mlxreg_wdt_ping,
0205     .set_timeout    = mlxreg_wdt_set_timeout,
0206     .get_timeleft   = mlxreg_wdt_get_timeleft,
0207     .owner      = THIS_MODULE,
0208 };
0209 
0210 static const struct watchdog_info mlxreg_wdt_main_info = {
0211     .options    = MLXREG_WDT_OPTIONS_BASE
0212             | WDIOF_CARDRESET,
0213     .identity   = "mlx-wdt-main",
0214 };
0215 
0216 static const struct watchdog_info mlxreg_wdt_aux_info = {
0217     .options    = MLXREG_WDT_OPTIONS_BASE
0218             | WDIOF_ALARMONLY,
0219     .identity   = "mlx-wdt-aux",
0220 };
0221 
0222 static void mlxreg_wdt_config(struct mlxreg_wdt *wdt,
0223                   struct mlxreg_core_platform_data *pdata)
0224 {
0225     struct mlxreg_core_data *data = pdata->data;
0226     int i;
0227 
0228     wdt->reset_idx = -EINVAL;
0229     for (i = 0; i < pdata->counter; i++, data++) {
0230         if (strnstr(data->label, "action", sizeof(data->label)))
0231             wdt->action_idx = i;
0232         else if (strnstr(data->label, "timeout", sizeof(data->label)))
0233             wdt->timeout_idx = i;
0234         else if (strnstr(data->label, "timeleft", sizeof(data->label)))
0235             wdt->tleft_idx = i;
0236         else if (strnstr(data->label, "ping", sizeof(data->label)))
0237             wdt->ping_idx = i;
0238         else if (strnstr(data->label, "reset", sizeof(data->label)))
0239             wdt->reset_idx = i;
0240     }
0241 
0242     wdt->pdata = pdata;
0243     if (strnstr(pdata->identity, mlxreg_wdt_main_info.identity,
0244             sizeof(mlxreg_wdt_main_info.identity)))
0245         wdt->wdd.info = &mlxreg_wdt_main_info;
0246     else
0247         wdt->wdd.info = &mlxreg_wdt_aux_info;
0248 
0249     wdt->wdt_type = pdata->version;
0250     switch (wdt->wdt_type) {
0251     case MLX_WDT_TYPE1:
0252         wdt->wdd.ops = &mlxreg_wdt_ops_type1;
0253         wdt->wdd.max_timeout = MLXREG_WDT_MAX_TIMEOUT_TYPE1;
0254         break;
0255     case MLX_WDT_TYPE2:
0256         wdt->wdd.ops = &mlxreg_wdt_ops_type2;
0257         wdt->wdd.max_timeout = MLXREG_WDT_MAX_TIMEOUT_TYPE2;
0258         break;
0259     case MLX_WDT_TYPE3:
0260         wdt->wdd.ops = &mlxreg_wdt_ops_type2;
0261         wdt->wdd.max_timeout = MLXREG_WDT_MAX_TIMEOUT_TYPE3;
0262         break;
0263     default:
0264         break;
0265     }
0266 
0267     wdt->wdd.min_timeout = MLXREG_WDT_MIN_TIMEOUT;
0268 }
0269 
0270 static int mlxreg_wdt_init_timeout(struct mlxreg_wdt *wdt,
0271                    struct mlxreg_core_platform_data *pdata)
0272 {
0273     u32 timeout;
0274 
0275     timeout = pdata->data[wdt->timeout_idx].health_cntr;
0276     return mlxreg_wdt_set_timeout(&wdt->wdd, timeout);
0277 }
0278 
0279 static int mlxreg_wdt_probe(struct platform_device *pdev)
0280 {
0281     struct device *dev = &pdev->dev;
0282     struct mlxreg_core_platform_data *pdata;
0283     struct mlxreg_wdt *wdt;
0284     int rc;
0285 
0286     pdata = dev_get_platdata(dev);
0287     if (!pdata) {
0288         dev_err(dev, "Failed to get platform data.\n");
0289         return -EINVAL;
0290     }
0291     wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
0292     if (!wdt)
0293         return -ENOMEM;
0294 
0295     wdt->wdd.parent = dev;
0296     wdt->regmap = pdata->regmap;
0297     rc = regmap_get_val_bytes(wdt->regmap);
0298     if (rc < 0)
0299         return -EINVAL;
0300 
0301     wdt->regmap_val_sz = rc;
0302     mlxreg_wdt_config(wdt, pdata);
0303 
0304     if ((pdata->features & MLXREG_CORE_WD_FEATURE_NOWAYOUT))
0305         watchdog_set_nowayout(&wdt->wdd, WATCHDOG_NOWAYOUT);
0306     watchdog_stop_on_reboot(&wdt->wdd);
0307     watchdog_stop_on_unregister(&wdt->wdd);
0308     watchdog_set_drvdata(&wdt->wdd, wdt);
0309     rc = mlxreg_wdt_init_timeout(wdt, pdata);
0310     if (rc)
0311         goto register_error;
0312 
0313     if ((pdata->features & MLXREG_CORE_WD_FEATURE_START_AT_BOOT)) {
0314         rc = mlxreg_wdt_start(&wdt->wdd);
0315         if (rc)
0316             goto register_error;
0317         set_bit(WDOG_HW_RUNNING, &wdt->wdd.status);
0318     }
0319     mlxreg_wdt_check_card_reset(wdt);
0320     rc = devm_watchdog_register_device(dev, &wdt->wdd);
0321 
0322 register_error:
0323     if (rc)
0324         dev_err(dev, "Cannot register watchdog device (err=%d)\n", rc);
0325     return rc;
0326 }
0327 
0328 static struct platform_driver mlxreg_wdt_driver = {
0329     .probe  = mlxreg_wdt_probe,
0330     .driver = {
0331             .name = "mlx-wdt",
0332     },
0333 };
0334 
0335 module_platform_driver(mlxreg_wdt_driver);
0336 
0337 MODULE_AUTHOR("Michael Shych <michaelsh@mellanox.com>");
0338 MODULE_DESCRIPTION("Mellanox watchdog driver");
0339 MODULE_LICENSE("GPL");
0340 MODULE_ALIAS("platform:mlx-wdt");