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