mlx_wdt.c 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  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 <[email protected]>
  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_write_bits(wdt->regmap, reg_data->reg, ~reg_data->mask,
  91. BIT(reg_data->bit));
  92. }
  93. static int mlxreg_wdt_set_timeout(struct watchdog_device *wdd,
  94. unsigned int timeout)
  95. {
  96. struct mlxreg_wdt *wdt = watchdog_get_drvdata(wdd);
  97. struct mlxreg_core_data *reg_data = &wdt->pdata->data[wdt->timeout_idx];
  98. u32 regval, set_time, hw_timeout;
  99. int rc;
  100. switch (wdt->wdt_type) {
  101. case MLX_WDT_TYPE1:
  102. rc = regmap_read(wdt->regmap, reg_data->reg, &regval);
  103. if (rc)
  104. return rc;
  105. hw_timeout = order_base_2(timeout * MLXREG_WDT_CLOCK_SCALE);
  106. regval = (regval & reg_data->mask) | hw_timeout;
  107. /* Rowndown to actual closest number of sec. */
  108. set_time = BIT(hw_timeout) / MLXREG_WDT_CLOCK_SCALE;
  109. rc = regmap_write(wdt->regmap, reg_data->reg, regval);
  110. break;
  111. case MLX_WDT_TYPE2:
  112. set_time = timeout;
  113. rc = regmap_write(wdt->regmap, reg_data->reg, timeout);
  114. break;
  115. case MLX_WDT_TYPE3:
  116. /* WD_TYPE3 has 2B set time register */
  117. set_time = timeout;
  118. if (wdt->regmap_val_sz == 1) {
  119. regval = timeout & 0xff;
  120. rc = regmap_write(wdt->regmap, reg_data->reg, regval);
  121. if (!rc) {
  122. regval = (timeout & 0xff00) >> 8;
  123. rc = regmap_write(wdt->regmap,
  124. reg_data->reg + 1, regval);
  125. }
  126. } else {
  127. rc = regmap_write(wdt->regmap, reg_data->reg, timeout);
  128. }
  129. break;
  130. default:
  131. return -EINVAL;
  132. }
  133. wdd->timeout = set_time;
  134. if (!rc) {
  135. /*
  136. * Restart watchdog with new timeout period
  137. * if watchdog is already started.
  138. */
  139. if (watchdog_active(wdd)) {
  140. rc = mlxreg_wdt_stop(wdd);
  141. if (!rc)
  142. rc = mlxreg_wdt_start(wdd);
  143. }
  144. }
  145. return rc;
  146. }
  147. static unsigned int mlxreg_wdt_get_timeleft(struct watchdog_device *wdd)
  148. {
  149. struct mlxreg_wdt *wdt = watchdog_get_drvdata(wdd);
  150. struct mlxreg_core_data *reg_data = &wdt->pdata->data[wdt->tleft_idx];
  151. u32 regval, msb, lsb;
  152. int rc;
  153. if (wdt->wdt_type == MLX_WDT_TYPE2) {
  154. rc = regmap_read(wdt->regmap, reg_data->reg, &regval);
  155. } else {
  156. /* WD_TYPE3 has 2 byte timeleft register */
  157. if (wdt->regmap_val_sz == 1) {
  158. rc = regmap_read(wdt->regmap, reg_data->reg, &lsb);
  159. if (!rc) {
  160. rc = regmap_read(wdt->regmap,
  161. reg_data->reg + 1, &msb);
  162. regval = (msb & 0xff) << 8 | (lsb & 0xff);
  163. }
  164. } else {
  165. rc = regmap_read(wdt->regmap, reg_data->reg, &regval);
  166. }
  167. }
  168. /* Return 0 timeleft in case of failure register read. */
  169. return rc == 0 ? regval : 0;
  170. }
  171. static const struct watchdog_ops mlxreg_wdt_ops_type1 = {
  172. .start = mlxreg_wdt_start,
  173. .stop = mlxreg_wdt_stop,
  174. .ping = mlxreg_wdt_ping,
  175. .set_timeout = mlxreg_wdt_set_timeout,
  176. .owner = THIS_MODULE,
  177. };
  178. static const struct watchdog_ops mlxreg_wdt_ops_type2 = {
  179. .start = mlxreg_wdt_start,
  180. .stop = mlxreg_wdt_stop,
  181. .ping = mlxreg_wdt_ping,
  182. .set_timeout = mlxreg_wdt_set_timeout,
  183. .get_timeleft = mlxreg_wdt_get_timeleft,
  184. .owner = THIS_MODULE,
  185. };
  186. static const struct watchdog_info mlxreg_wdt_main_info = {
  187. .options = MLXREG_WDT_OPTIONS_BASE
  188. | WDIOF_CARDRESET,
  189. .identity = "mlx-wdt-main",
  190. };
  191. static const struct watchdog_info mlxreg_wdt_aux_info = {
  192. .options = MLXREG_WDT_OPTIONS_BASE
  193. | WDIOF_ALARMONLY,
  194. .identity = "mlx-wdt-aux",
  195. };
  196. static void mlxreg_wdt_config(struct mlxreg_wdt *wdt,
  197. struct mlxreg_core_platform_data *pdata)
  198. {
  199. struct mlxreg_core_data *data = pdata->data;
  200. int i;
  201. wdt->reset_idx = -EINVAL;
  202. for (i = 0; i < pdata->counter; i++, data++) {
  203. if (strnstr(data->label, "action", sizeof(data->label)))
  204. wdt->action_idx = i;
  205. else if (strnstr(data->label, "timeout", sizeof(data->label)))
  206. wdt->timeout_idx = i;
  207. else if (strnstr(data->label, "timeleft", sizeof(data->label)))
  208. wdt->tleft_idx = i;
  209. else if (strnstr(data->label, "ping", sizeof(data->label)))
  210. wdt->ping_idx = i;
  211. else if (strnstr(data->label, "reset", sizeof(data->label)))
  212. wdt->reset_idx = i;
  213. }
  214. wdt->pdata = pdata;
  215. if (strnstr(pdata->identity, mlxreg_wdt_main_info.identity,
  216. sizeof(mlxreg_wdt_main_info.identity)))
  217. wdt->wdd.info = &mlxreg_wdt_main_info;
  218. else
  219. wdt->wdd.info = &mlxreg_wdt_aux_info;
  220. wdt->wdt_type = pdata->version;
  221. switch (wdt->wdt_type) {
  222. case MLX_WDT_TYPE1:
  223. wdt->wdd.ops = &mlxreg_wdt_ops_type1;
  224. wdt->wdd.max_timeout = MLXREG_WDT_MAX_TIMEOUT_TYPE1;
  225. break;
  226. case MLX_WDT_TYPE2:
  227. wdt->wdd.ops = &mlxreg_wdt_ops_type2;
  228. wdt->wdd.max_timeout = MLXREG_WDT_MAX_TIMEOUT_TYPE2;
  229. break;
  230. case MLX_WDT_TYPE3:
  231. wdt->wdd.ops = &mlxreg_wdt_ops_type2;
  232. wdt->wdd.max_timeout = MLXREG_WDT_MAX_TIMEOUT_TYPE3;
  233. break;
  234. default:
  235. break;
  236. }
  237. wdt->wdd.min_timeout = MLXREG_WDT_MIN_TIMEOUT;
  238. }
  239. static int mlxreg_wdt_init_timeout(struct mlxreg_wdt *wdt,
  240. struct mlxreg_core_platform_data *pdata)
  241. {
  242. u32 timeout;
  243. timeout = pdata->data[wdt->timeout_idx].health_cntr;
  244. return mlxreg_wdt_set_timeout(&wdt->wdd, timeout);
  245. }
  246. static int mlxreg_wdt_probe(struct platform_device *pdev)
  247. {
  248. struct device *dev = &pdev->dev;
  249. struct mlxreg_core_platform_data *pdata;
  250. struct mlxreg_wdt *wdt;
  251. int rc;
  252. pdata = dev_get_platdata(dev);
  253. if (!pdata) {
  254. dev_err(dev, "Failed to get platform data.\n");
  255. return -EINVAL;
  256. }
  257. wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
  258. if (!wdt)
  259. return -ENOMEM;
  260. wdt->wdd.parent = dev;
  261. wdt->regmap = pdata->regmap;
  262. rc = regmap_get_val_bytes(wdt->regmap);
  263. if (rc < 0)
  264. return -EINVAL;
  265. wdt->regmap_val_sz = rc;
  266. mlxreg_wdt_config(wdt, pdata);
  267. if ((pdata->features & MLXREG_CORE_WD_FEATURE_NOWAYOUT))
  268. watchdog_set_nowayout(&wdt->wdd, WATCHDOG_NOWAYOUT);
  269. watchdog_stop_on_reboot(&wdt->wdd);
  270. watchdog_stop_on_unregister(&wdt->wdd);
  271. watchdog_set_drvdata(&wdt->wdd, wdt);
  272. rc = mlxreg_wdt_init_timeout(wdt, pdata);
  273. if (rc)
  274. goto register_error;
  275. if ((pdata->features & MLXREG_CORE_WD_FEATURE_START_AT_BOOT)) {
  276. rc = mlxreg_wdt_start(&wdt->wdd);
  277. if (rc)
  278. goto register_error;
  279. set_bit(WDOG_HW_RUNNING, &wdt->wdd.status);
  280. }
  281. mlxreg_wdt_check_card_reset(wdt);
  282. rc = devm_watchdog_register_device(dev, &wdt->wdd);
  283. register_error:
  284. if (rc)
  285. dev_err(dev, "Cannot register watchdog device (err=%d)\n", rc);
  286. return rc;
  287. }
  288. static struct platform_driver mlxreg_wdt_driver = {
  289. .probe = mlxreg_wdt_probe,
  290. .driver = {
  291. .name = "mlx-wdt",
  292. },
  293. };
  294. module_platform_driver(mlxreg_wdt_driver);
  295. MODULE_AUTHOR("Michael Shych <[email protected]>");
  296. MODULE_DESCRIPTION("Mellanox watchdog driver");
  297. MODULE_LICENSE("GPL");
  298. MODULE_ALIAS("platform:mlx-wdt");