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 
15 struct mcp251xfd_dump_iter {
16 	void *start;
17 	struct mcp251xfd_dump_object_header *hdr;
18 	void *data;
19 };
20 
21 struct mcp251xfd_dump_reg_space {
22 	u16 base;
23 	u16 size;
24 };
25 
26 struct mcp251xfd_dump_ring {
27 	enum mcp251xfd_dump_object_ring_key key;
28 	u32 val;
29 };
30 
31 static 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 
mcp251xfd_dump_header(struct mcp251xfd_dump_iter * iter,enum mcp251xfd_dump_object_type object_type,const void * data_end)44 static 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 
mcp251xfd_dump_registers(const struct mcp251xfd_priv * priv,struct mcp251xfd_dump_iter * iter)64 static 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 
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)101 static 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 
mcp251xfd_dump_tef_ring(const struct mcp251xfd_priv * priv,struct mcp251xfd_dump_iter * iter)117 static 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 
mcp251xfd_dump_rx_ring_one(const struct mcp251xfd_priv * priv,struct mcp251xfd_dump_iter * iter,const struct mcp251xfd_rx_ring * rx)151 static 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 
mcp251xfd_dump_rx_ring(const struct mcp251xfd_priv * priv,struct mcp251xfd_dump_iter * iter)184 static 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 
mcp251xfd_dump_tx_ring(const struct mcp251xfd_priv * priv,struct mcp251xfd_dump_iter * iter)194 static 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 
mcp251xfd_dump_end(const struct mcp251xfd_priv * priv,struct mcp251xfd_dump_iter * iter)227 static 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 
mcp251xfd_dump(const struct mcp251xfd_priv * priv)241 void 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