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