brintos

brintos / linux-shallow public Read only

0
0
Text · 7.2 KiB · 0503213 Raw
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