Linux Audio

Check our new training course

In-person Linux kernel drivers training

Jun 16-20, 2025
Register
Loading...
v6.8
  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 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}
v6.9.4
  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 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}