286 lines · c
1// SPDX-License-Identifier: GPL-2.02//3// mcp251xfd - Microchip MCP251xFD Family CAN controller driver4//5// Copyright (c) 2020, 2021 Pengutronix,6// Marc Kleine-Budde <kernel@pengutronix.de>7// Copyright (C) 2015-2018 Etnaviv Project8//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 97out: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 rings, TX ring */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}286