mlx_wdt.c 9.0 KB

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