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, ®val); 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, ®val); 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, ®val); 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 mlxreg_core_platform_data *pdata; 237 struct mlxreg_wdt *wdt; 238 int rc; 239 240 pdata = dev_get_platdata(&pdev->dev); 241 if (!pdata) { 242 dev_err(&pdev->dev, "Failed to get platform data.\n"); 243 return -EINVAL; 244 } 245 wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); 246 if (!wdt) 247 return -ENOMEM; 248 249 wdt->wdd.parent = &pdev->dev; 250 wdt->regmap = pdata->regmap; 251 mlxreg_wdt_config(wdt, pdata); 252 253 if ((pdata->features & MLXREG_CORE_WD_FEATURE_NOWAYOUT)) 254 watchdog_set_nowayout(&wdt->wdd, WATCHDOG_NOWAYOUT); 255 watchdog_stop_on_reboot(&wdt->wdd); 256 watchdog_stop_on_unregister(&wdt->wdd); 257 watchdog_set_drvdata(&wdt->wdd, wdt); 258 rc = mlxreg_wdt_init_timeout(wdt, pdata); 259 if (rc) 260 goto register_error; 261 262 if ((pdata->features & MLXREG_CORE_WD_FEATURE_START_AT_BOOT)) { 263 rc = mlxreg_wdt_start(&wdt->wdd); 264 if (rc) 265 goto register_error; 266 set_bit(WDOG_HW_RUNNING, &wdt->wdd.status); 267 } 268 mlxreg_wdt_check_card_reset(wdt); 269 rc = devm_watchdog_register_device(&pdev->dev, &wdt->wdd); 270 271 register_error: 272 if (rc) 273 dev_err(&pdev->dev, 274 "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"); 291