xref: /linux/drivers/net/dsa/mxl862xx/mxl862xx-host.c (revision 2ed2e359dea752e7758d29a423033031a0b96584)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Based upon the MaxLinear SDK driver
4  *
5  * Copyright (C) 2025 Daniel Golle <daniel@makrotopia.org>
6  * Copyright (C) 2025 John Crispin <john@phrozen.org>
7  * Copyright (C) 2024 MaxLinear Inc.
8  */
9 
10 #include <linux/bitops.h>
11 #include <linux/bits.h>
12 #include <linux/crc16.h>
13 #include <linux/iopoll.h>
14 #include <linux/limits.h>
15 #include <linux/unaligned.h>
16 #include <net/dsa.h>
17 #include "mxl862xx.h"
18 #include "mxl862xx-host.h"
19 
20 #define CTRL_BUSY_MASK			BIT(15)
21 #define CTRL_CRC_FLAG			BIT(14)
22 
23 #define LEN_RET_LEN_MASK		GENMASK(9, 0)
24 
25 #define MXL862XX_MMD_REG_CTRL		0
26 #define MXL862XX_MMD_REG_LEN_RET	1
27 #define MXL862XX_MMD_REG_DATA_FIRST	2
28 #define MXL862XX_MMD_REG_DATA_LAST	95
29 #define MXL862XX_MMD_REG_DATA_MAX_SIZE \
30 		(MXL862XX_MMD_REG_DATA_LAST - MXL862XX_MMD_REG_DATA_FIRST + 1)
31 
32 #define MMD_API_SET_DATA_0		2
33 #define MMD_API_GET_DATA_0		5
34 #define MMD_API_RST_DATA		8
35 
36 #define MXL862XX_SWITCH_RESET		0x9907
37 
mxl862xx_crc_err_work_fn(struct work_struct * work)38 static void mxl862xx_crc_err_work_fn(struct work_struct *work)
39 {
40 	struct mxl862xx_priv *priv = container_of(work, struct mxl862xx_priv,
41 						  crc_err_work);
42 	struct dsa_port *dp;
43 
44 	rtnl_lock();
45 	if (!test_bit(MXL862XX_FLAG_WORK_STOPPED, &priv->flags)) {
46 		dev_warn(&priv->mdiodev->dev,
47 			 "MDIO CRC error detected, shutting down all ports\n");
48 		dsa_switch_for_each_cpu_port(dp, priv->ds)
49 			dev_close(dp->conduit);
50 	}
51 	rtnl_unlock();
52 
53 	clear_bit(MXL862XX_FLAG_CRC_ERR, &priv->flags);
54 }
55 
56 /* Firmware CRC error codes (outside normal Zephyr errno range). */
57 #define MXL862XX_FW_CRC6_ERR		(-1024)
58 #define MXL862XX_FW_CRC16_ERR		(-1023)
59 
60 /* 3GPP CRC-6 lookup table (polynomial 0x6F).
61  * Matches the firmware's default CRC-6 implementation.
62  */
63 static const u8 mxl862xx_crc6_table[256] = {
64 	0x00, 0x2f, 0x31, 0x1e, 0x0d, 0x22, 0x3c, 0x13,
65 	0x1a, 0x35, 0x2b, 0x04, 0x17, 0x38, 0x26, 0x09,
66 	0x34, 0x1b, 0x05, 0x2a, 0x39, 0x16, 0x08, 0x27,
67 	0x2e, 0x01, 0x1f, 0x30, 0x23, 0x0c, 0x12, 0x3d,
68 	0x07, 0x28, 0x36, 0x19, 0x0a, 0x25, 0x3b, 0x14,
69 	0x1d, 0x32, 0x2c, 0x03, 0x10, 0x3f, 0x21, 0x0e,
70 	0x33, 0x1c, 0x02, 0x2d, 0x3e, 0x11, 0x0f, 0x20,
71 	0x29, 0x06, 0x18, 0x37, 0x24, 0x0b, 0x15, 0x3a,
72 	0x0e, 0x21, 0x3f, 0x10, 0x03, 0x2c, 0x32, 0x1d,
73 	0x14, 0x3b, 0x25, 0x0a, 0x19, 0x36, 0x28, 0x07,
74 	0x3a, 0x15, 0x0b, 0x24, 0x37, 0x18, 0x06, 0x29,
75 	0x20, 0x0f, 0x11, 0x3e, 0x2d, 0x02, 0x1c, 0x33,
76 	0x09, 0x26, 0x38, 0x17, 0x04, 0x2b, 0x35, 0x1a,
77 	0x13, 0x3c, 0x22, 0x0d, 0x1e, 0x31, 0x2f, 0x00,
78 	0x3d, 0x12, 0x0c, 0x23, 0x30, 0x1f, 0x01, 0x2e,
79 	0x27, 0x08, 0x16, 0x39, 0x2a, 0x05, 0x1b, 0x34,
80 	0x1c, 0x33, 0x2d, 0x02, 0x11, 0x3e, 0x20, 0x0f,
81 	0x06, 0x29, 0x37, 0x18, 0x0b, 0x24, 0x3a, 0x15,
82 	0x28, 0x07, 0x19, 0x36, 0x25, 0x0a, 0x14, 0x3b,
83 	0x32, 0x1d, 0x03, 0x2c, 0x3f, 0x10, 0x0e, 0x21,
84 	0x1b, 0x34, 0x2a, 0x05, 0x16, 0x39, 0x27, 0x08,
85 	0x01, 0x2e, 0x30, 0x1f, 0x0c, 0x23, 0x3d, 0x12,
86 	0x2f, 0x00, 0x1e, 0x31, 0x22, 0x0d, 0x13, 0x3c,
87 	0x35, 0x1a, 0x04, 0x2b, 0x38, 0x17, 0x09, 0x26,
88 	0x12, 0x3d, 0x23, 0x0c, 0x1f, 0x30, 0x2e, 0x01,
89 	0x08, 0x27, 0x39, 0x16, 0x05, 0x2a, 0x34, 0x1b,
90 	0x26, 0x09, 0x17, 0x38, 0x2b, 0x04, 0x1a, 0x35,
91 	0x3c, 0x13, 0x0d, 0x22, 0x31, 0x1e, 0x00, 0x2f,
92 	0x15, 0x3a, 0x24, 0x0b, 0x18, 0x37, 0x29, 0x06,
93 	0x0f, 0x20, 0x3e, 0x11, 0x02, 0x2d, 0x33, 0x1c,
94 	0x21, 0x0e, 0x10, 0x3f, 0x2c, 0x03, 0x1d, 0x32,
95 	0x3b, 0x14, 0x0a, 0x25, 0x36, 0x19, 0x07, 0x28,
96 };
97 
98 /* Compute 3GPP CRC-6 over the ctrl register (16 bits) and the lower
99  * 10 bits of the len_ret register. The 26-bit input is packed as
100  * { len_ret[9:0], ctrl[15:0] } and processed LSB-first through the
101  * lookup table.
102  */
mxl862xx_crc6(u16 ctrl,u16 len_ret)103 static u8 mxl862xx_crc6(u16 ctrl, u16 len_ret)
104 {
105 	u32 data = ((u32)(len_ret & LEN_RET_LEN_MASK) << 16) | ctrl;
106 	u8 crc = 0;
107 	int i;
108 
109 	for (i = 0; i < sizeof(data); i++, data >>= 8)
110 		crc = mxl862xx_crc6_table[(crc << 2) ^ (data & 0xff)] & 0x3f;
111 
112 	return crc;
113 }
114 
115 /* Encode CRC-6 into the ctrl and len_ret registers before writing them
116  * to MDIO. The caller must set ctrl = API_ID | CTRL_BUSY_MASK |
117  * CTRL_CRC_FLAG, and len_ret = parameter length (bits 0-9 only).
118  *
119  * After encoding:
120  *   ctrl[12:0]     = API ID (unchanged)
121  *   ctrl[14:13]    = CRC-6 bits 5-4
122  *   ctrl[15]       = busy flag (unchanged)
123  *   len_ret[9:0]   = parameter length (unchanged)
124  *   len_ret[13:10] = CRC-6 bits 3-0
125  *   len_ret[14]    = original ctrl[14] (CRC check flag, forwarded to FW)
126  *   len_ret[15]    = original ctrl[13] (magic bit, always 1)
127  */
mxl862xx_crc6_encode(u16 * pctrl,u16 * plen_ret)128 static void mxl862xx_crc6_encode(u16 *pctrl, u16 *plen_ret)
129 {
130 	u16 crc, ctrl, len_ret;
131 
132 	/* Set magic bit before CRC computation */
133 	*pctrl |= BIT(13);
134 
135 	crc = mxl862xx_crc6(*pctrl, *plen_ret);
136 
137 	/* Place CRC MSB (bits 5-4) into ctrl bits 13-14 */
138 	ctrl = (*pctrl & ~GENMASK(14, 13));
139 	ctrl |= (crc & 0x30) << 9;
140 
141 	/* Place CRC LSB (bits 3-0) into len_ret bits 10-13 */
142 	len_ret = *plen_ret | ((crc & 0x0f) << 10);
143 
144 	/* Forward ctrl[14] (CRC check flag) to len_ret[14],
145 	 * and ctrl[13] (magic, always 1) to len_ret[15].
146 	 */
147 	len_ret |= (*pctrl & BIT(14)) | ((*pctrl & BIT(13)) << 2);
148 
149 	*pctrl = ctrl;
150 	*plen_ret = len_ret;
151 }
152 
153 /* Verify CRC-6 on a firmware response and extract the return value.
154  *
155  * The firmware encodes the return value as a signed 11-bit integer:
156  *   - Sign bit (bit 10) in ctrl[14]
157  *   - Magnitude (bits 9-0) in len_ret[9:0]
158  * These are recoverable after CRC-6 verification by restoring the
159  * original ctrl from the auxiliary copies in len_ret[15:14].
160  *
161  * Return: 0 on CRC match (with *result set), or -EIO on mismatch.
162  */
mxl862xx_crc6_verify(u16 ctrl,u16 len_ret,int * result)163 static int mxl862xx_crc6_verify(u16 ctrl, u16 len_ret, int *result)
164 {
165 	u16 crc_recv, crc_calc;
166 
167 	/* Extract the received CRC-6 */
168 	crc_recv = ((ctrl >> 9) & 0x30) | ((len_ret >> 10) & 0x0f);
169 
170 	/* Reconstruct the original ctrl for re-computation:
171 	 *   ctrl[14] = len_ret[14] (sign bit / CRC check flag)
172 	 *   ctrl[13] = len_ret[15] >> 2 (magic bit)
173 	 */
174 	ctrl &= ~GENMASK(14, 13);
175 	ctrl |= len_ret & BIT(14);
176 	ctrl |= (len_ret & BIT(15)) >> 2;
177 
178 	crc_calc = mxl862xx_crc6(ctrl, len_ret);
179 	if (crc_recv != crc_calc)
180 		return -EIO;
181 
182 	/* Extract signed 11-bit return value:
183 	 *   bit 10 (sign) from ctrl[14], bits 9-0 from len_ret[9:0]
184 	 */
185 	*result = sign_extend32((len_ret & LEN_RET_LEN_MASK) |
186 				((ctrl & CTRL_CRC_FLAG) >> 4), 10);
187 
188 	return 0;
189 }
190 
mxl862xx_reg_read(struct mxl862xx_priv * priv,u32 addr)191 static int mxl862xx_reg_read(struct mxl862xx_priv *priv, u32 addr)
192 {
193 	return __mdiodev_c45_read(priv->mdiodev, MDIO_MMD_VEND1, addr);
194 }
195 
mxl862xx_reg_write(struct mxl862xx_priv * priv,u32 addr,u16 data)196 static int mxl862xx_reg_write(struct mxl862xx_priv *priv, u32 addr, u16 data)
197 {
198 	return __mdiodev_c45_write(priv->mdiodev, MDIO_MMD_VEND1, addr, data);
199 }
200 
mxl862xx_ctrl_read(struct mxl862xx_priv * priv)201 static int mxl862xx_ctrl_read(struct mxl862xx_priv *priv)
202 {
203 	return mxl862xx_reg_read(priv, MXL862XX_MMD_REG_CTRL);
204 }
205 
mxl862xx_busy_wait(struct mxl862xx_priv * priv)206 static int mxl862xx_busy_wait(struct mxl862xx_priv *priv)
207 {
208 	int val;
209 
210 	return readx_poll_timeout(mxl862xx_ctrl_read, priv, val,
211 				  !(val & CTRL_BUSY_MASK), 15, 500000);
212 }
213 
214 /* Issue a firmware command with CRC-6 protection on the ctrl and len_ret
215  * registers, wait for completion, and verify the response CRC-6.
216  *
217  * Return: firmware result value (>= 0) on success, or negative errno.
218  */
mxl862xx_issue_cmd(struct mxl862xx_priv * priv,u16 cmd,u16 len)219 static int mxl862xx_issue_cmd(struct mxl862xx_priv *priv, u16 cmd, u16 len)
220 {
221 	u16 ctrl_enc, len_enc;
222 	int ret, fw_result;
223 
224 	ctrl_enc = cmd | CTRL_BUSY_MASK | CTRL_CRC_FLAG;
225 	len_enc = len;
226 	mxl862xx_crc6_encode(&ctrl_enc, &len_enc);
227 
228 	ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_LEN_RET, len_enc);
229 	if (ret < 0)
230 		return ret;
231 
232 	ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_CTRL, ctrl_enc);
233 	if (ret < 0)
234 		return ret;
235 
236 	ret = mxl862xx_busy_wait(priv);
237 	if (ret < 0)
238 		return ret;
239 
240 	ret = mxl862xx_reg_read(priv, MXL862XX_MMD_REG_CTRL);
241 	if (ret < 0)
242 		return ret;
243 	ctrl_enc = ret;
244 
245 	ret = mxl862xx_reg_read(priv, MXL862XX_MMD_REG_LEN_RET);
246 	if (ret < 0)
247 		return ret;
248 	len_enc = ret;
249 
250 	ret = mxl862xx_crc6_verify(ctrl_enc, len_enc, &fw_result);
251 	if (ret) {
252 		if (!test_and_set_bit(MXL862XX_FLAG_CRC_ERR, &priv->flags))
253 			schedule_work(&priv->crc_err_work);
254 		return -EIO;
255 	}
256 
257 	return fw_result;
258 }
259 
mxl862xx_set_data(struct mxl862xx_priv * priv,u16 words)260 static int mxl862xx_set_data(struct mxl862xx_priv *priv, u16 words)
261 {
262 	u16 cmd;
263 
264 	cmd = words / MXL862XX_MMD_REG_DATA_MAX_SIZE - 1;
265 	if (!(cmd < 2))
266 		return -EINVAL;
267 
268 	cmd += MMD_API_SET_DATA_0;
269 
270 	return mxl862xx_issue_cmd(priv, cmd,
271 				  MXL862XX_MMD_REG_DATA_MAX_SIZE * sizeof(u16));
272 }
273 
mxl862xx_get_data(struct mxl862xx_priv * priv,u16 words)274 static int mxl862xx_get_data(struct mxl862xx_priv *priv, u16 words)
275 {
276 	u16 cmd;
277 
278 	cmd = words / MXL862XX_MMD_REG_DATA_MAX_SIZE;
279 	if (!(cmd > 0 && cmd < 3))
280 		return -EINVAL;
281 
282 	cmd += MMD_API_GET_DATA_0;
283 
284 	return mxl862xx_issue_cmd(priv, cmd,
285 				  MXL862XX_MMD_REG_DATA_MAX_SIZE * sizeof(u16));
286 }
287 
mxl862xx_rst_data(struct mxl862xx_priv * priv)288 static int mxl862xx_rst_data(struct mxl862xx_priv *priv)
289 {
290 	return mxl862xx_issue_cmd(priv, MMD_API_RST_DATA, 0);
291 }
292 
293 /* Minimum number of zero words in the data payload before issuing a
294  * RST_DATA command is worthwhile.  RST_DATA costs one full command
295  * round-trip (~5 MDIO transactions), so the threshold must offset that.
296  */
297 #define RST_DATA_THRESHOLD	5
298 
mxl862xx_send_cmd(struct mxl862xx_priv * priv,u16 cmd,u16 size,bool quiet)299 static int mxl862xx_send_cmd(struct mxl862xx_priv *priv, u16 cmd, u16 size,
300 			     bool quiet)
301 {
302 	int ret;
303 
304 	ret = mxl862xx_issue_cmd(priv, cmd, size);
305 
306 	/* Handle errors returned by the firmware as -EIO.
307 	 * The firmware is based on Zephyr OS and uses the errors as
308 	 * defined in errno.h of Zephyr OS. See
309 	 * https://github.com/zephyrproject-rtos/zephyr/blob/v3.7.0/lib/libc/minimal/include/errno.h
310 	 *
311 	 * The firmware signals CRC validation failures with dedicated
312 	 * error codes outside the normal Zephyr errno range:
313 	 *   -1024: CRC-6 mismatch on ctrl/len_ret registers
314 	 *   -1023: CRC-16 mismatch on data payload
315 	 */
316 	if (ret < 0) {
317 		if ((ret == MXL862XX_FW_CRC6_ERR ||
318 		     ret == MXL862XX_FW_CRC16_ERR) &&
319 		    !test_and_set_bit(MXL862XX_FLAG_CRC_ERR, &priv->flags))
320 			schedule_work(&priv->crc_err_work);
321 		if (!quiet)
322 			dev_err(&priv->mdiodev->dev,
323 				"CMD %04x returned error %d\n", cmd, ret);
324 		return -EIO;
325 	}
326 
327 	return ret;
328 }
329 
mxl862xx_api_wrap(struct mxl862xx_priv * priv,u16 cmd,void * _data,u16 size,bool read,bool quiet)330 int mxl862xx_api_wrap(struct mxl862xx_priv *priv, u16 cmd, void *_data,
331 		      u16 size, bool read, bool quiet)
332 {
333 	__le16 *data = _data;
334 	bool use_rst = false;
335 	unsigned int zeros;
336 	int ret, cmd_ret;
337 	u16 max, crc, i;
338 
339 	dev_dbg(&priv->mdiodev->dev, "CMD %04x DATA %*ph\n", cmd, size, data);
340 
341 	mutex_lock_nested(&priv->mdiodev->bus->mdio_lock, MDIO_MUTEX_NESTED);
342 
343 	max = (size + 1) / 2;
344 
345 	ret = mxl862xx_busy_wait(priv);
346 	if (ret < 0)
347 		goto out;
348 
349 	/* If the data contains enough zero words, issue RST_DATA to zero
350 	 * both the firmware buffer and MMD registers, then skip writing
351 	 * zero words individually.
352 	 */
353 	for (i = 0, zeros = 0; i < size / 2 && zeros < RST_DATA_THRESHOLD; i++)
354 		if (!get_unaligned_le16(&data[i]))
355 			zeros++;
356 
357 	if (zeros < RST_DATA_THRESHOLD && (size & 1) && !*(u8 *)&data[i])
358 		zeros++;
359 
360 	if (zeros >= RST_DATA_THRESHOLD) {
361 		ret = mxl862xx_rst_data(priv);
362 		if (ret < 0)
363 			goto out;
364 		use_rst = true;
365 	}
366 
367 	/* Compute CRC-16 over the data payload; written as an extra word
368 	 * after the data so the firmware can verify the transfer.
369 	 */
370 	crc = crc16(0xffff, (const u8 *)data, size);
371 
372 	for (i = 0; i < max + 1; i++) {
373 		u16 off = i % MXL862XX_MMD_REG_DATA_MAX_SIZE;
374 		u16 val;
375 
376 		if (i && off == 0) {
377 			/* Send command to set data when every
378 			 * MXL862XX_MMD_REG_DATA_MAX_SIZE of WORDs are written.
379 			  */
380 			ret = mxl862xx_set_data(priv, i);
381 			if (ret < 0)
382 				goto out;
383 		}
384 
385 		if (i == max) {
386 			/* Even size: full CRC word.
387 			 * Odd size: only CRC high byte remains (low byte
388 			 * was packed into the previous word).
389 			 */
390 			val = (size & 1) ? crc >> 8 : crc;
391 		} else if ((i * 2 + 1) == size) {
392 			/* Special handling for last BYTE if it's not WORD
393 			 * aligned to avoid reading beyond the allocated data
394 			 * structure.  Pack the CRC low byte into the high
395 			 * byte of this word so it sits at byte offset 'size'
396 			 * in the firmware's contiguous buffer.
397 			 */
398 			val = *(u8 *)&data[i] | ((crc & 0xff) << 8);
399 		} else {
400 			val = get_unaligned_le16(&data[i]);
401 		}
402 
403 		/* After RST_DATA, skip zero data words as the registers
404 		 * already contain zeros, but never skip the CRC word at the
405 		 * final word.
406 		 */
407 		if (use_rst && i < max && val == 0)
408 			continue;
409 
410 		ret = mxl862xx_reg_write(priv,
411 					 MXL862XX_MMD_REG_DATA_FIRST + off,
412 					 val);
413 		if (ret < 0)
414 			goto out;
415 	}
416 
417 	ret = mxl862xx_send_cmd(priv, cmd, size, quiet);
418 	if (ret < 0 || !read)
419 		goto out;
420 
421 	/* store result of mxl862xx_send_cmd() */
422 	cmd_ret = ret;
423 
424 	for (i = 0; i < max + 1; i++) {
425 		u16 off = i % MXL862XX_MMD_REG_DATA_MAX_SIZE;
426 
427 		if (i && off == 0) {
428 			/* Send command to fetch next batch of data when every
429 			 * MXL862XX_MMD_REG_DATA_MAX_SIZE of WORDs are read.
430 			  */
431 			ret = mxl862xx_get_data(priv, i);
432 			if (ret < 0)
433 				goto out;
434 		}
435 
436 		ret = mxl862xx_reg_read(priv, MXL862XX_MMD_REG_DATA_FIRST + off);
437 		if (ret < 0)
438 			goto out;
439 
440 		if (i == max) {
441 			/* Even size: full CRC word.
442 			 * Odd size: only CRC high byte remains (low byte
443 			 * was in the previous word).
444 			 */
445 			if (size & 1)
446 				crc = (crc & 0x00ff) |
447 				      (((u16)ret & 0xff) << 8);
448 			else
449 				crc = (u16)ret;
450 		} else if ((i * 2 + 1) == size) {
451 			/* Special handling for last BYTE if it's not WORD
452 			 * aligned to avoid writing beyond the allocated data
453 			 * structure.  The high byte carries the CRC low byte.
454 			 */
455 			*(uint8_t *)&data[i] = ret & 0xff;
456 			crc = (ret >> 8) & 0xff;
457 		} else {
458 			put_unaligned_le16((u16)ret, &data[i]);
459 		}
460 	}
461 
462 	if (crc16(0xffff, (const u8 *)data, size) != crc) {
463 		if (!test_and_set_bit(MXL862XX_FLAG_CRC_ERR, &priv->flags))
464 			schedule_work(&priv->crc_err_work);
465 		ret = -EIO;
466 		goto out;
467 	}
468 
469 	/* on success return the result of the mxl862xx_send_cmd() */
470 	ret = cmd_ret;
471 
472 	dev_dbg(&priv->mdiodev->dev, "RET %d DATA %*ph\n", ret, size, data);
473 
474 out:
475 	mutex_unlock(&priv->mdiodev->bus->mdio_lock);
476 
477 	return ret;
478 }
479 
mxl862xx_reset(struct mxl862xx_priv * priv)480 int mxl862xx_reset(struct mxl862xx_priv *priv)
481 {
482 	int ret;
483 
484 	mutex_lock_nested(&priv->mdiodev->bus->mdio_lock, MDIO_MUTEX_NESTED);
485 
486 	/* Software reset */
487 	ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_LEN_RET, 0);
488 	if (ret)
489 		goto out;
490 
491 	ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_CTRL, MXL862XX_SWITCH_RESET);
492 out:
493 	mutex_unlock(&priv->mdiodev->bus->mdio_lock);
494 
495 	return ret;
496 }
497 
mxl862xx_host_init(struct mxl862xx_priv * priv)498 void mxl862xx_host_init(struct mxl862xx_priv *priv)
499 {
500 	INIT_WORK(&priv->crc_err_work, mxl862xx_crc_err_work_fn);
501 }
502 
mxl862xx_host_shutdown(struct mxl862xx_priv * priv)503 void mxl862xx_host_shutdown(struct mxl862xx_priv *priv)
504 {
505 	cancel_work_sync(&priv->crc_err_work);
506 }
507