mcp251xfd-dump.c 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. // SPDX-License-Identifier: GPL-2.0
  2. //
  3. // mcp251xfd - Microchip MCP251xFD Family CAN controller driver
  4. //
  5. // Copyright (c) 2020, 2021 Pengutronix,
  6. // Marc Kleine-Budde <[email protected]>
  7. // Copyright (C) 2015-2018 Etnaviv Project
  8. //
  9. #include <linux/devcoredump.h>
  10. #include "mcp251xfd.h"
  11. #include "mcp251xfd-dump.h"
  12. struct mcp251xfd_dump_iter {
  13. void *start;
  14. struct mcp251xfd_dump_object_header *hdr;
  15. void *data;
  16. };
  17. struct mcp251xfd_dump_reg_space {
  18. u16 base;
  19. u16 size;
  20. };
  21. struct mcp251xfd_dump_ring {
  22. enum mcp251xfd_dump_object_ring_key key;
  23. u32 val;
  24. };
  25. static const struct mcp251xfd_dump_reg_space mcp251xfd_dump_reg_space[] = {
  26. {
  27. .base = MCP251XFD_REG_CON,
  28. .size = MCP251XFD_REG_FLTOBJ(32) - MCP251XFD_REG_CON,
  29. }, {
  30. .base = MCP251XFD_RAM_START,
  31. .size = MCP251XFD_RAM_SIZE,
  32. }, {
  33. .base = MCP251XFD_REG_OSC,
  34. .size = MCP251XFD_REG_DEVID - MCP251XFD_REG_OSC,
  35. },
  36. };
  37. static void mcp251xfd_dump_header(struct mcp251xfd_dump_iter *iter,
  38. enum mcp251xfd_dump_object_type object_type,
  39. const void *data_end)
  40. {
  41. struct mcp251xfd_dump_object_header *hdr = iter->hdr;
  42. unsigned int len;
  43. len = data_end - iter->data;
  44. if (!len)
  45. return;
  46. hdr->magic = cpu_to_le32(MCP251XFD_DUMP_MAGIC);
  47. hdr->type = cpu_to_le32(object_type);
  48. hdr->offset = cpu_to_le32(iter->data - iter->start);
  49. hdr->len = cpu_to_le32(len);
  50. iter->hdr++;
  51. iter->data += len;
  52. }
  53. static void mcp251xfd_dump_registers(const struct mcp251xfd_priv *priv,
  54. struct mcp251xfd_dump_iter *iter)
  55. {
  56. const int val_bytes = regmap_get_val_bytes(priv->map_rx);
  57. struct mcp251xfd_dump_object_reg *reg = iter->data;
  58. unsigned int i, j;
  59. int err;
  60. for (i = 0; i < ARRAY_SIZE(mcp251xfd_dump_reg_space); i++) {
  61. const struct mcp251xfd_dump_reg_space *reg_space;
  62. void *buf;
  63. reg_space = &mcp251xfd_dump_reg_space[i];
  64. buf = kmalloc(reg_space->size, GFP_KERNEL);
  65. if (!buf)
  66. goto out;
  67. err = regmap_bulk_read(priv->map_reg, reg_space->base,
  68. buf, reg_space->size / val_bytes);
  69. if (err) {
  70. kfree(buf);
  71. continue;
  72. }
  73. for (j = 0; j < reg_space->size; j += sizeof(u32), reg++) {
  74. reg->reg = cpu_to_le32(reg_space->base + j);
  75. reg->val = cpu_to_le32p(buf + j);
  76. }
  77. kfree(buf);
  78. }
  79. out:
  80. mcp251xfd_dump_header(iter, MCP251XFD_DUMP_OBJECT_TYPE_REG, reg);
  81. }
  82. static void mcp251xfd_dump_ring(struct mcp251xfd_dump_iter *iter,
  83. enum mcp251xfd_dump_object_type object_type,
  84. const struct mcp251xfd_dump_ring *dump_ring,
  85. unsigned int len)
  86. {
  87. struct mcp251xfd_dump_object_reg *reg = iter->data;
  88. unsigned int i;
  89. for (i = 0; i < len; i++, reg++) {
  90. reg->reg = cpu_to_le32(dump_ring[i].key);
  91. reg->val = cpu_to_le32(dump_ring[i].val);
  92. }
  93. mcp251xfd_dump_header(iter, object_type, reg);
  94. }
  95. static void mcp251xfd_dump_tef_ring(const struct mcp251xfd_priv *priv,
  96. struct mcp251xfd_dump_iter *iter)
  97. {
  98. const struct mcp251xfd_tef_ring *tef = priv->tef;
  99. const struct mcp251xfd_tx_ring *tx = priv->tx;
  100. const struct mcp251xfd_dump_ring dump_ring[] = {
  101. {
  102. .key = MCP251XFD_DUMP_OBJECT_RING_KEY_HEAD,
  103. .val = tef->head,
  104. }, {
  105. .key = MCP251XFD_DUMP_OBJECT_RING_KEY_TAIL,
  106. .val = tef->tail,
  107. }, {
  108. .key = MCP251XFD_DUMP_OBJECT_RING_KEY_BASE,
  109. .val = 0,
  110. }, {
  111. .key = MCP251XFD_DUMP_OBJECT_RING_KEY_NR,
  112. .val = 0,
  113. }, {
  114. .key = MCP251XFD_DUMP_OBJECT_RING_KEY_FIFO_NR,
  115. .val = 0,
  116. }, {
  117. .key = MCP251XFD_DUMP_OBJECT_RING_KEY_OBJ_NUM,
  118. .val = tx->obj_num,
  119. }, {
  120. .key = MCP251XFD_DUMP_OBJECT_RING_KEY_OBJ_SIZE,
  121. .val = sizeof(struct mcp251xfd_hw_tef_obj),
  122. },
  123. };
  124. mcp251xfd_dump_ring(iter, MCP251XFD_DUMP_OBJECT_TYPE_TEF,
  125. dump_ring, ARRAY_SIZE(dump_ring));
  126. }
  127. static void mcp251xfd_dump_rx_ring_one(const struct mcp251xfd_priv *priv,
  128. struct mcp251xfd_dump_iter *iter,
  129. const struct mcp251xfd_rx_ring *rx)
  130. {
  131. const struct mcp251xfd_dump_ring dump_ring[] = {
  132. {
  133. .key = MCP251XFD_DUMP_OBJECT_RING_KEY_HEAD,
  134. .val = rx->head,
  135. }, {
  136. .key = MCP251XFD_DUMP_OBJECT_RING_KEY_TAIL,
  137. .val = rx->tail,
  138. }, {
  139. .key = MCP251XFD_DUMP_OBJECT_RING_KEY_BASE,
  140. .val = rx->base,
  141. }, {
  142. .key = MCP251XFD_DUMP_OBJECT_RING_KEY_NR,
  143. .val = rx->nr,
  144. }, {
  145. .key = MCP251XFD_DUMP_OBJECT_RING_KEY_FIFO_NR,
  146. .val = rx->fifo_nr,
  147. }, {
  148. .key = MCP251XFD_DUMP_OBJECT_RING_KEY_OBJ_NUM,
  149. .val = rx->obj_num,
  150. }, {
  151. .key = MCP251XFD_DUMP_OBJECT_RING_KEY_OBJ_SIZE,
  152. .val = rx->obj_size,
  153. },
  154. };
  155. mcp251xfd_dump_ring(iter, MCP251XFD_DUMP_OBJECT_TYPE_RX,
  156. dump_ring, ARRAY_SIZE(dump_ring));
  157. }
  158. static void mcp251xfd_dump_rx_ring(const struct mcp251xfd_priv *priv,
  159. struct mcp251xfd_dump_iter *iter)
  160. {
  161. struct mcp251xfd_rx_ring *rx_ring;
  162. unsigned int i;
  163. mcp251xfd_for_each_rx_ring(priv, rx_ring, i)
  164. mcp251xfd_dump_rx_ring_one(priv, iter, rx_ring);
  165. }
  166. static void mcp251xfd_dump_tx_ring(const struct mcp251xfd_priv *priv,
  167. struct mcp251xfd_dump_iter *iter)
  168. {
  169. const struct mcp251xfd_tx_ring *tx = priv->tx;
  170. const struct mcp251xfd_dump_ring dump_ring[] = {
  171. {
  172. .key = MCP251XFD_DUMP_OBJECT_RING_KEY_HEAD,
  173. .val = tx->head,
  174. }, {
  175. .key = MCP251XFD_DUMP_OBJECT_RING_KEY_TAIL,
  176. .val = tx->tail,
  177. }, {
  178. .key = MCP251XFD_DUMP_OBJECT_RING_KEY_BASE,
  179. .val = tx->base,
  180. }, {
  181. .key = MCP251XFD_DUMP_OBJECT_RING_KEY_NR,
  182. .val = tx->nr,
  183. }, {
  184. .key = MCP251XFD_DUMP_OBJECT_RING_KEY_FIFO_NR,
  185. .val = tx->fifo_nr,
  186. }, {
  187. .key = MCP251XFD_DUMP_OBJECT_RING_KEY_OBJ_NUM,
  188. .val = tx->obj_num,
  189. }, {
  190. .key = MCP251XFD_DUMP_OBJECT_RING_KEY_OBJ_SIZE,
  191. .val = tx->obj_size,
  192. },
  193. };
  194. mcp251xfd_dump_ring(iter, MCP251XFD_DUMP_OBJECT_TYPE_TX,
  195. dump_ring, ARRAY_SIZE(dump_ring));
  196. }
  197. static void mcp251xfd_dump_end(const struct mcp251xfd_priv *priv,
  198. struct mcp251xfd_dump_iter *iter)
  199. {
  200. struct mcp251xfd_dump_object_header *hdr = iter->hdr;
  201. hdr->magic = cpu_to_le32(MCP251XFD_DUMP_MAGIC);
  202. hdr->type = cpu_to_le32(MCP251XFD_DUMP_OBJECT_TYPE_END);
  203. hdr->offset = cpu_to_le32(0);
  204. hdr->len = cpu_to_le32(0);
  205. /* provoke NULL pointer access, if used after END object */
  206. iter->hdr = NULL;
  207. }
  208. void mcp251xfd_dump(const struct mcp251xfd_priv *priv)
  209. {
  210. struct mcp251xfd_dump_iter iter;
  211. unsigned int rings_num, obj_num;
  212. unsigned int file_size = 0;
  213. unsigned int i;
  214. /* register space + end marker */
  215. obj_num = 2;
  216. /* register space */
  217. for (i = 0; i < ARRAY_SIZE(mcp251xfd_dump_reg_space); i++)
  218. file_size += mcp251xfd_dump_reg_space[i].size / sizeof(u32) *
  219. sizeof(struct mcp251xfd_dump_object_reg);
  220. /* TEF ring, RX rings, TX ring */
  221. rings_num = 1 + priv->rx_ring_num + 1;
  222. obj_num += rings_num;
  223. file_size += rings_num * __MCP251XFD_DUMP_OBJECT_RING_KEY_MAX *
  224. sizeof(struct mcp251xfd_dump_object_reg);
  225. /* size of the headers */
  226. file_size += sizeof(*iter.hdr) * obj_num;
  227. /* allocate the file in vmalloc memory, it's likely to be big */
  228. iter.start = __vmalloc(file_size, GFP_KERNEL | __GFP_NOWARN |
  229. __GFP_ZERO | __GFP_NORETRY);
  230. if (!iter.start) {
  231. netdev_warn(priv->ndev, "Failed to allocate devcoredump file.\n");
  232. return;
  233. }
  234. /* point the data member after the headers */
  235. iter.hdr = iter.start;
  236. iter.data = &iter.hdr[obj_num];
  237. mcp251xfd_dump_registers(priv, &iter);
  238. mcp251xfd_dump_tef_ring(priv, &iter);
  239. mcp251xfd_dump_rx_ring(priv, &iter);
  240. mcp251xfd_dump_tx_ring(priv, &iter);
  241. mcp251xfd_dump_end(priv, &iter);
  242. dev_coredumpv(&priv->spi->dev, iter.start,
  243. iter.data - iter.start, GFP_KERNEL);
  244. }