162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// mcp251xfd - Microchip MCP251xFD Family CAN controller driver 462306a36Sopenharmony_ci// 562306a36Sopenharmony_ci// Copyright (c) 2020, 2021 Pengutronix, 662306a36Sopenharmony_ci// Marc Kleine-Budde <kernel@pengutronix.de> 762306a36Sopenharmony_ci// Copyright (C) 2015-2018 Etnaviv Project 862306a36Sopenharmony_ci// 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/devcoredump.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include "mcp251xfd.h" 1362306a36Sopenharmony_ci#include "mcp251xfd-dump.h" 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_cistruct mcp251xfd_dump_iter { 1662306a36Sopenharmony_ci void *start; 1762306a36Sopenharmony_ci struct mcp251xfd_dump_object_header *hdr; 1862306a36Sopenharmony_ci void *data; 1962306a36Sopenharmony_ci}; 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistruct mcp251xfd_dump_reg_space { 2262306a36Sopenharmony_ci u16 base; 2362306a36Sopenharmony_ci u16 size; 2462306a36Sopenharmony_ci}; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistruct mcp251xfd_dump_ring { 2762306a36Sopenharmony_ci enum mcp251xfd_dump_object_ring_key key; 2862306a36Sopenharmony_ci u32 val; 2962306a36Sopenharmony_ci}; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistatic const struct mcp251xfd_dump_reg_space mcp251xfd_dump_reg_space[] = { 3262306a36Sopenharmony_ci { 3362306a36Sopenharmony_ci .base = MCP251XFD_REG_CON, 3462306a36Sopenharmony_ci .size = MCP251XFD_REG_FLTOBJ(32) - MCP251XFD_REG_CON, 3562306a36Sopenharmony_ci }, { 3662306a36Sopenharmony_ci .base = MCP251XFD_RAM_START, 3762306a36Sopenharmony_ci .size = MCP251XFD_RAM_SIZE, 3862306a36Sopenharmony_ci }, { 3962306a36Sopenharmony_ci .base = MCP251XFD_REG_OSC, 4062306a36Sopenharmony_ci .size = MCP251XFD_REG_DEVID - MCP251XFD_REG_OSC, 4162306a36Sopenharmony_ci }, 4262306a36Sopenharmony_ci}; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistatic void mcp251xfd_dump_header(struct mcp251xfd_dump_iter *iter, 4562306a36Sopenharmony_ci enum mcp251xfd_dump_object_type object_type, 4662306a36Sopenharmony_ci const void *data_end) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci struct mcp251xfd_dump_object_header *hdr = iter->hdr; 4962306a36Sopenharmony_ci unsigned int len; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci len = data_end - iter->data; 5262306a36Sopenharmony_ci if (!len) 5362306a36Sopenharmony_ci return; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci hdr->magic = cpu_to_le32(MCP251XFD_DUMP_MAGIC); 5662306a36Sopenharmony_ci hdr->type = cpu_to_le32(object_type); 5762306a36Sopenharmony_ci hdr->offset = cpu_to_le32(iter->data - iter->start); 5862306a36Sopenharmony_ci hdr->len = cpu_to_le32(len); 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci iter->hdr++; 6162306a36Sopenharmony_ci iter->data += len; 6262306a36Sopenharmony_ci} 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic void mcp251xfd_dump_registers(const struct mcp251xfd_priv *priv, 6562306a36Sopenharmony_ci struct mcp251xfd_dump_iter *iter) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci const int val_bytes = regmap_get_val_bytes(priv->map_rx); 6862306a36Sopenharmony_ci struct mcp251xfd_dump_object_reg *reg = iter->data; 6962306a36Sopenharmony_ci unsigned int i, j; 7062306a36Sopenharmony_ci int err; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(mcp251xfd_dump_reg_space); i++) { 7362306a36Sopenharmony_ci const struct mcp251xfd_dump_reg_space *reg_space; 7462306a36Sopenharmony_ci void *buf; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci reg_space = &mcp251xfd_dump_reg_space[i]; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci buf = kmalloc(reg_space->size, GFP_KERNEL); 7962306a36Sopenharmony_ci if (!buf) 8062306a36Sopenharmony_ci goto out; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci err = regmap_bulk_read(priv->map_reg, reg_space->base, 8362306a36Sopenharmony_ci buf, reg_space->size / val_bytes); 8462306a36Sopenharmony_ci if (err) { 8562306a36Sopenharmony_ci kfree(buf); 8662306a36Sopenharmony_ci continue; 8762306a36Sopenharmony_ci } 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci for (j = 0; j < reg_space->size; j += sizeof(u32), reg++) { 9062306a36Sopenharmony_ci reg->reg = cpu_to_le32(reg_space->base + j); 9162306a36Sopenharmony_ci reg->val = cpu_to_le32p(buf + j); 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci kfree(buf); 9562306a36Sopenharmony_ci } 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci out: 9862306a36Sopenharmony_ci mcp251xfd_dump_header(iter, MCP251XFD_DUMP_OBJECT_TYPE_REG, reg); 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_cistatic void mcp251xfd_dump_ring(struct mcp251xfd_dump_iter *iter, 10262306a36Sopenharmony_ci enum mcp251xfd_dump_object_type object_type, 10362306a36Sopenharmony_ci const struct mcp251xfd_dump_ring *dump_ring, 10462306a36Sopenharmony_ci unsigned int len) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci struct mcp251xfd_dump_object_reg *reg = iter->data; 10762306a36Sopenharmony_ci unsigned int i; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci for (i = 0; i < len; i++, reg++) { 11062306a36Sopenharmony_ci reg->reg = cpu_to_le32(dump_ring[i].key); 11162306a36Sopenharmony_ci reg->val = cpu_to_le32(dump_ring[i].val); 11262306a36Sopenharmony_ci } 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci mcp251xfd_dump_header(iter, object_type, reg); 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic void mcp251xfd_dump_tef_ring(const struct mcp251xfd_priv *priv, 11862306a36Sopenharmony_ci struct mcp251xfd_dump_iter *iter) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci const struct mcp251xfd_tef_ring *tef = priv->tef; 12162306a36Sopenharmony_ci const struct mcp251xfd_tx_ring *tx = priv->tx; 12262306a36Sopenharmony_ci const struct mcp251xfd_dump_ring dump_ring[] = { 12362306a36Sopenharmony_ci { 12462306a36Sopenharmony_ci .key = MCP251XFD_DUMP_OBJECT_RING_KEY_HEAD, 12562306a36Sopenharmony_ci .val = tef->head, 12662306a36Sopenharmony_ci }, { 12762306a36Sopenharmony_ci .key = MCP251XFD_DUMP_OBJECT_RING_KEY_TAIL, 12862306a36Sopenharmony_ci .val = tef->tail, 12962306a36Sopenharmony_ci }, { 13062306a36Sopenharmony_ci .key = MCP251XFD_DUMP_OBJECT_RING_KEY_BASE, 13162306a36Sopenharmony_ci .val = 0, 13262306a36Sopenharmony_ci }, { 13362306a36Sopenharmony_ci .key = MCP251XFD_DUMP_OBJECT_RING_KEY_NR, 13462306a36Sopenharmony_ci .val = 0, 13562306a36Sopenharmony_ci }, { 13662306a36Sopenharmony_ci .key = MCP251XFD_DUMP_OBJECT_RING_KEY_FIFO_NR, 13762306a36Sopenharmony_ci .val = 0, 13862306a36Sopenharmony_ci }, { 13962306a36Sopenharmony_ci .key = MCP251XFD_DUMP_OBJECT_RING_KEY_OBJ_NUM, 14062306a36Sopenharmony_ci .val = tx->obj_num, 14162306a36Sopenharmony_ci }, { 14262306a36Sopenharmony_ci .key = MCP251XFD_DUMP_OBJECT_RING_KEY_OBJ_SIZE, 14362306a36Sopenharmony_ci .val = sizeof(struct mcp251xfd_hw_tef_obj), 14462306a36Sopenharmony_ci }, 14562306a36Sopenharmony_ci }; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci mcp251xfd_dump_ring(iter, MCP251XFD_DUMP_OBJECT_TYPE_TEF, 14862306a36Sopenharmony_ci dump_ring, ARRAY_SIZE(dump_ring)); 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_cistatic void mcp251xfd_dump_rx_ring_one(const struct mcp251xfd_priv *priv, 15262306a36Sopenharmony_ci struct mcp251xfd_dump_iter *iter, 15362306a36Sopenharmony_ci const struct mcp251xfd_rx_ring *rx) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci const struct mcp251xfd_dump_ring dump_ring[] = { 15662306a36Sopenharmony_ci { 15762306a36Sopenharmony_ci .key = MCP251XFD_DUMP_OBJECT_RING_KEY_HEAD, 15862306a36Sopenharmony_ci .val = rx->head, 15962306a36Sopenharmony_ci }, { 16062306a36Sopenharmony_ci .key = MCP251XFD_DUMP_OBJECT_RING_KEY_TAIL, 16162306a36Sopenharmony_ci .val = rx->tail, 16262306a36Sopenharmony_ci }, { 16362306a36Sopenharmony_ci .key = MCP251XFD_DUMP_OBJECT_RING_KEY_BASE, 16462306a36Sopenharmony_ci .val = rx->base, 16562306a36Sopenharmony_ci }, { 16662306a36Sopenharmony_ci .key = MCP251XFD_DUMP_OBJECT_RING_KEY_NR, 16762306a36Sopenharmony_ci .val = rx->nr, 16862306a36Sopenharmony_ci }, { 16962306a36Sopenharmony_ci .key = MCP251XFD_DUMP_OBJECT_RING_KEY_FIFO_NR, 17062306a36Sopenharmony_ci .val = rx->fifo_nr, 17162306a36Sopenharmony_ci }, { 17262306a36Sopenharmony_ci .key = MCP251XFD_DUMP_OBJECT_RING_KEY_OBJ_NUM, 17362306a36Sopenharmony_ci .val = rx->obj_num, 17462306a36Sopenharmony_ci }, { 17562306a36Sopenharmony_ci .key = MCP251XFD_DUMP_OBJECT_RING_KEY_OBJ_SIZE, 17662306a36Sopenharmony_ci .val = rx->obj_size, 17762306a36Sopenharmony_ci }, 17862306a36Sopenharmony_ci }; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci mcp251xfd_dump_ring(iter, MCP251XFD_DUMP_OBJECT_TYPE_RX, 18162306a36Sopenharmony_ci dump_ring, ARRAY_SIZE(dump_ring)); 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistatic void mcp251xfd_dump_rx_ring(const struct mcp251xfd_priv *priv, 18562306a36Sopenharmony_ci struct mcp251xfd_dump_iter *iter) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci struct mcp251xfd_rx_ring *rx_ring; 18862306a36Sopenharmony_ci unsigned int i; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci mcp251xfd_for_each_rx_ring(priv, rx_ring, i) 19162306a36Sopenharmony_ci mcp251xfd_dump_rx_ring_one(priv, iter, rx_ring); 19262306a36Sopenharmony_ci} 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_cistatic void mcp251xfd_dump_tx_ring(const struct mcp251xfd_priv *priv, 19562306a36Sopenharmony_ci struct mcp251xfd_dump_iter *iter) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci const struct mcp251xfd_tx_ring *tx = priv->tx; 19862306a36Sopenharmony_ci const struct mcp251xfd_dump_ring dump_ring[] = { 19962306a36Sopenharmony_ci { 20062306a36Sopenharmony_ci .key = MCP251XFD_DUMP_OBJECT_RING_KEY_HEAD, 20162306a36Sopenharmony_ci .val = tx->head, 20262306a36Sopenharmony_ci }, { 20362306a36Sopenharmony_ci .key = MCP251XFD_DUMP_OBJECT_RING_KEY_TAIL, 20462306a36Sopenharmony_ci .val = tx->tail, 20562306a36Sopenharmony_ci }, { 20662306a36Sopenharmony_ci .key = MCP251XFD_DUMP_OBJECT_RING_KEY_BASE, 20762306a36Sopenharmony_ci .val = tx->base, 20862306a36Sopenharmony_ci }, { 20962306a36Sopenharmony_ci .key = MCP251XFD_DUMP_OBJECT_RING_KEY_NR, 21062306a36Sopenharmony_ci .val = tx->nr, 21162306a36Sopenharmony_ci }, { 21262306a36Sopenharmony_ci .key = MCP251XFD_DUMP_OBJECT_RING_KEY_FIFO_NR, 21362306a36Sopenharmony_ci .val = tx->fifo_nr, 21462306a36Sopenharmony_ci }, { 21562306a36Sopenharmony_ci .key = MCP251XFD_DUMP_OBJECT_RING_KEY_OBJ_NUM, 21662306a36Sopenharmony_ci .val = tx->obj_num, 21762306a36Sopenharmony_ci }, { 21862306a36Sopenharmony_ci .key = MCP251XFD_DUMP_OBJECT_RING_KEY_OBJ_SIZE, 21962306a36Sopenharmony_ci .val = tx->obj_size, 22062306a36Sopenharmony_ci }, 22162306a36Sopenharmony_ci }; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci mcp251xfd_dump_ring(iter, MCP251XFD_DUMP_OBJECT_TYPE_TX, 22462306a36Sopenharmony_ci dump_ring, ARRAY_SIZE(dump_ring)); 22562306a36Sopenharmony_ci} 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_cistatic void mcp251xfd_dump_end(const struct mcp251xfd_priv *priv, 22862306a36Sopenharmony_ci struct mcp251xfd_dump_iter *iter) 22962306a36Sopenharmony_ci{ 23062306a36Sopenharmony_ci struct mcp251xfd_dump_object_header *hdr = iter->hdr; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci hdr->magic = cpu_to_le32(MCP251XFD_DUMP_MAGIC); 23362306a36Sopenharmony_ci hdr->type = cpu_to_le32(MCP251XFD_DUMP_OBJECT_TYPE_END); 23462306a36Sopenharmony_ci hdr->offset = cpu_to_le32(0); 23562306a36Sopenharmony_ci hdr->len = cpu_to_le32(0); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci /* provoke NULL pointer access, if used after END object */ 23862306a36Sopenharmony_ci iter->hdr = NULL; 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_civoid mcp251xfd_dump(const struct mcp251xfd_priv *priv) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci struct mcp251xfd_dump_iter iter; 24462306a36Sopenharmony_ci unsigned int rings_num, obj_num; 24562306a36Sopenharmony_ci unsigned int file_size = 0; 24662306a36Sopenharmony_ci unsigned int i; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci /* register space + end marker */ 24962306a36Sopenharmony_ci obj_num = 2; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci /* register space */ 25262306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(mcp251xfd_dump_reg_space); i++) 25362306a36Sopenharmony_ci file_size += mcp251xfd_dump_reg_space[i].size / sizeof(u32) * 25462306a36Sopenharmony_ci sizeof(struct mcp251xfd_dump_object_reg); 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci /* TEF ring, RX rings, TX ring */ 25762306a36Sopenharmony_ci rings_num = 1 + priv->rx_ring_num + 1; 25862306a36Sopenharmony_ci obj_num += rings_num; 25962306a36Sopenharmony_ci file_size += rings_num * __MCP251XFD_DUMP_OBJECT_RING_KEY_MAX * 26062306a36Sopenharmony_ci sizeof(struct mcp251xfd_dump_object_reg); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci /* size of the headers */ 26362306a36Sopenharmony_ci file_size += sizeof(*iter.hdr) * obj_num; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci /* allocate the file in vmalloc memory, it's likely to be big */ 26662306a36Sopenharmony_ci iter.start = __vmalloc(file_size, GFP_KERNEL | __GFP_NOWARN | 26762306a36Sopenharmony_ci __GFP_ZERO | __GFP_NORETRY); 26862306a36Sopenharmony_ci if (!iter.start) { 26962306a36Sopenharmony_ci netdev_warn(priv->ndev, "Failed to allocate devcoredump file.\n"); 27062306a36Sopenharmony_ci return; 27162306a36Sopenharmony_ci } 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci /* point the data member after the headers */ 27462306a36Sopenharmony_ci iter.hdr = iter.start; 27562306a36Sopenharmony_ci iter.data = &iter.hdr[obj_num]; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci mcp251xfd_dump_registers(priv, &iter); 27862306a36Sopenharmony_ci mcp251xfd_dump_tef_ring(priv, &iter); 27962306a36Sopenharmony_ci mcp251xfd_dump_rx_ring(priv, &iter); 28062306a36Sopenharmony_ci mcp251xfd_dump_tx_ring(priv, &iter); 28162306a36Sopenharmony_ci mcp251xfd_dump_end(priv, &iter); 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci dev_coredumpv(&priv->spi->dev, iter.start, 28462306a36Sopenharmony_ci iter.data - iter.start, GFP_KERNEL); 28562306a36Sopenharmony_ci} 286