123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285 |
- // SPDX-License-Identifier: GPL-2.0
- //
- // mcp251xfd - Microchip MCP251xFD Family CAN controller driver
- //
- // Copyright (c) 2020, 2021 Pengutronix,
- // Marc Kleine-Budde <[email protected]>
- // Copyright (C) 2015-2018 Etnaviv Project
- //
- #include <linux/devcoredump.h>
- #include "mcp251xfd.h"
- #include "mcp251xfd-dump.h"
- struct mcp251xfd_dump_iter {
- void *start;
- struct mcp251xfd_dump_object_header *hdr;
- void *data;
- };
- struct mcp251xfd_dump_reg_space {
- u16 base;
- u16 size;
- };
- struct mcp251xfd_dump_ring {
- enum mcp251xfd_dump_object_ring_key key;
- u32 val;
- };
- static const struct mcp251xfd_dump_reg_space mcp251xfd_dump_reg_space[] = {
- {
- .base = MCP251XFD_REG_CON,
- .size = MCP251XFD_REG_FLTOBJ(32) - MCP251XFD_REG_CON,
- }, {
- .base = MCP251XFD_RAM_START,
- .size = MCP251XFD_RAM_SIZE,
- }, {
- .base = MCP251XFD_REG_OSC,
- .size = MCP251XFD_REG_DEVID - MCP251XFD_REG_OSC,
- },
- };
- static void mcp251xfd_dump_header(struct mcp251xfd_dump_iter *iter,
- enum mcp251xfd_dump_object_type object_type,
- const void *data_end)
- {
- struct mcp251xfd_dump_object_header *hdr = iter->hdr;
- unsigned int len;
- len = data_end - iter->data;
- if (!len)
- return;
- hdr->magic = cpu_to_le32(MCP251XFD_DUMP_MAGIC);
- hdr->type = cpu_to_le32(object_type);
- hdr->offset = cpu_to_le32(iter->data - iter->start);
- hdr->len = cpu_to_le32(len);
- iter->hdr++;
- iter->data += len;
- }
- static void mcp251xfd_dump_registers(const struct mcp251xfd_priv *priv,
- struct mcp251xfd_dump_iter *iter)
- {
- const int val_bytes = regmap_get_val_bytes(priv->map_rx);
- struct mcp251xfd_dump_object_reg *reg = iter->data;
- unsigned int i, j;
- int err;
- for (i = 0; i < ARRAY_SIZE(mcp251xfd_dump_reg_space); i++) {
- const struct mcp251xfd_dump_reg_space *reg_space;
- void *buf;
- reg_space = &mcp251xfd_dump_reg_space[i];
- buf = kmalloc(reg_space->size, GFP_KERNEL);
- if (!buf)
- goto out;
- err = regmap_bulk_read(priv->map_reg, reg_space->base,
- buf, reg_space->size / val_bytes);
- if (err) {
- kfree(buf);
- continue;
- }
- for (j = 0; j < reg_space->size; j += sizeof(u32), reg++) {
- reg->reg = cpu_to_le32(reg_space->base + j);
- reg->val = cpu_to_le32p(buf + j);
- }
- kfree(buf);
- }
- out:
- mcp251xfd_dump_header(iter, MCP251XFD_DUMP_OBJECT_TYPE_REG, reg);
- }
- static void mcp251xfd_dump_ring(struct mcp251xfd_dump_iter *iter,
- enum mcp251xfd_dump_object_type object_type,
- const struct mcp251xfd_dump_ring *dump_ring,
- unsigned int len)
- {
- struct mcp251xfd_dump_object_reg *reg = iter->data;
- unsigned int i;
- for (i = 0; i < len; i++, reg++) {
- reg->reg = cpu_to_le32(dump_ring[i].key);
- reg->val = cpu_to_le32(dump_ring[i].val);
- }
- mcp251xfd_dump_header(iter, object_type, reg);
- }
- static void mcp251xfd_dump_tef_ring(const struct mcp251xfd_priv *priv,
- struct mcp251xfd_dump_iter *iter)
- {
- const struct mcp251xfd_tef_ring *tef = priv->tef;
- const struct mcp251xfd_tx_ring *tx = priv->tx;
- const struct mcp251xfd_dump_ring dump_ring[] = {
- {
- .key = MCP251XFD_DUMP_OBJECT_RING_KEY_HEAD,
- .val = tef->head,
- }, {
- .key = MCP251XFD_DUMP_OBJECT_RING_KEY_TAIL,
- .val = tef->tail,
- }, {
- .key = MCP251XFD_DUMP_OBJECT_RING_KEY_BASE,
- .val = 0,
- }, {
- .key = MCP251XFD_DUMP_OBJECT_RING_KEY_NR,
- .val = 0,
- }, {
- .key = MCP251XFD_DUMP_OBJECT_RING_KEY_FIFO_NR,
- .val = 0,
- }, {
- .key = MCP251XFD_DUMP_OBJECT_RING_KEY_OBJ_NUM,
- .val = tx->obj_num,
- }, {
- .key = MCP251XFD_DUMP_OBJECT_RING_KEY_OBJ_SIZE,
- .val = sizeof(struct mcp251xfd_hw_tef_obj),
- },
- };
- mcp251xfd_dump_ring(iter, MCP251XFD_DUMP_OBJECT_TYPE_TEF,
- dump_ring, ARRAY_SIZE(dump_ring));
- }
- static void mcp251xfd_dump_rx_ring_one(const struct mcp251xfd_priv *priv,
- struct mcp251xfd_dump_iter *iter,
- const struct mcp251xfd_rx_ring *rx)
- {
- const struct mcp251xfd_dump_ring dump_ring[] = {
- {
- .key = MCP251XFD_DUMP_OBJECT_RING_KEY_HEAD,
- .val = rx->head,
- }, {
- .key = MCP251XFD_DUMP_OBJECT_RING_KEY_TAIL,
- .val = rx->tail,
- }, {
- .key = MCP251XFD_DUMP_OBJECT_RING_KEY_BASE,
- .val = rx->base,
- }, {
- .key = MCP251XFD_DUMP_OBJECT_RING_KEY_NR,
- .val = rx->nr,
- }, {
- .key = MCP251XFD_DUMP_OBJECT_RING_KEY_FIFO_NR,
- .val = rx->fifo_nr,
- }, {
- .key = MCP251XFD_DUMP_OBJECT_RING_KEY_OBJ_NUM,
- .val = rx->obj_num,
- }, {
- .key = MCP251XFD_DUMP_OBJECT_RING_KEY_OBJ_SIZE,
- .val = rx->obj_size,
- },
- };
- mcp251xfd_dump_ring(iter, MCP251XFD_DUMP_OBJECT_TYPE_RX,
- dump_ring, ARRAY_SIZE(dump_ring));
- }
- static void mcp251xfd_dump_rx_ring(const struct mcp251xfd_priv *priv,
- struct mcp251xfd_dump_iter *iter)
- {
- struct mcp251xfd_rx_ring *rx_ring;
- unsigned int i;
- mcp251xfd_for_each_rx_ring(priv, rx_ring, i)
- mcp251xfd_dump_rx_ring_one(priv, iter, rx_ring);
- }
- static void mcp251xfd_dump_tx_ring(const struct mcp251xfd_priv *priv,
- struct mcp251xfd_dump_iter *iter)
- {
- const struct mcp251xfd_tx_ring *tx = priv->tx;
- const struct mcp251xfd_dump_ring dump_ring[] = {
- {
- .key = MCP251XFD_DUMP_OBJECT_RING_KEY_HEAD,
- .val = tx->head,
- }, {
- .key = MCP251XFD_DUMP_OBJECT_RING_KEY_TAIL,
- .val = tx->tail,
- }, {
- .key = MCP251XFD_DUMP_OBJECT_RING_KEY_BASE,
- .val = tx->base,
- }, {
- .key = MCP251XFD_DUMP_OBJECT_RING_KEY_NR,
- .val = tx->nr,
- }, {
- .key = MCP251XFD_DUMP_OBJECT_RING_KEY_FIFO_NR,
- .val = tx->fifo_nr,
- }, {
- .key = MCP251XFD_DUMP_OBJECT_RING_KEY_OBJ_NUM,
- .val = tx->obj_num,
- }, {
- .key = MCP251XFD_DUMP_OBJECT_RING_KEY_OBJ_SIZE,
- .val = tx->obj_size,
- },
- };
- mcp251xfd_dump_ring(iter, MCP251XFD_DUMP_OBJECT_TYPE_TX,
- dump_ring, ARRAY_SIZE(dump_ring));
- }
- static void mcp251xfd_dump_end(const struct mcp251xfd_priv *priv,
- struct mcp251xfd_dump_iter *iter)
- {
- struct mcp251xfd_dump_object_header *hdr = iter->hdr;
- hdr->magic = cpu_to_le32(MCP251XFD_DUMP_MAGIC);
- hdr->type = cpu_to_le32(MCP251XFD_DUMP_OBJECT_TYPE_END);
- hdr->offset = cpu_to_le32(0);
- hdr->len = cpu_to_le32(0);
- /* provoke NULL pointer access, if used after END object */
- iter->hdr = NULL;
- }
- void mcp251xfd_dump(const struct mcp251xfd_priv *priv)
- {
- struct mcp251xfd_dump_iter iter;
- unsigned int rings_num, obj_num;
- unsigned int file_size = 0;
- unsigned int i;
- /* register space + end marker */
- obj_num = 2;
- /* register space */
- for (i = 0; i < ARRAY_SIZE(mcp251xfd_dump_reg_space); i++)
- file_size += mcp251xfd_dump_reg_space[i].size / sizeof(u32) *
- sizeof(struct mcp251xfd_dump_object_reg);
- /* TEF ring, RX rings, TX ring */
- rings_num = 1 + priv->rx_ring_num + 1;
- obj_num += rings_num;
- file_size += rings_num * __MCP251XFD_DUMP_OBJECT_RING_KEY_MAX *
- sizeof(struct mcp251xfd_dump_object_reg);
- /* size of the headers */
- file_size += sizeof(*iter.hdr) * obj_num;
- /* allocate the file in vmalloc memory, it's likely to be big */
- iter.start = __vmalloc(file_size, GFP_KERNEL | __GFP_NOWARN |
- __GFP_ZERO | __GFP_NORETRY);
- if (!iter.start) {
- netdev_warn(priv->ndev, "Failed to allocate devcoredump file.\n");
- return;
- }
- /* point the data member after the headers */
- iter.hdr = iter.start;
- iter.data = &iter.hdr[obj_num];
- mcp251xfd_dump_registers(priv, &iter);
- mcp251xfd_dump_tef_ring(priv, &iter);
- mcp251xfd_dump_rx_ring(priv, &iter);
- mcp251xfd_dump_tx_ring(priv, &iter);
- mcp251xfd_dump_end(priv, &iter);
- dev_coredumpv(&priv->spi->dev, iter.start,
- iter.data - iter.start, GFP_KERNEL);
- }
|