Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'net-dsa-mxl862xx-mdio-bus-integrity-and-optimization'

Daniel Golle says:

====================
net: dsa: mxl862xx: MDIO bus integrity and optimization

The MxL862xx firmware offers opt-in CRC validation on the MDIO/MMD
command interface to guard against bit errors on the bus.
The driver has not used this until now.

This series enables CRC protection on both the command registers (CRC-6)
and data payloads (CRC-16), and reduces MDIO bus traffic by bulk-zeroing
registers instead of writing zero-valued words individually.
====================

Link: https://patch.msgid.link/cover.1774185953.git.daniel@makrotopia.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

+331 -62
+1
drivers/net/dsa/mxl862xx/Kconfig
··· 2 2 config NET_DSA_MXL862 3 3 tristate "MaxLinear MxL862xx" 4 4 depends on NET_DSA 5 + select CRC16 5 6 select NET_DSA_TAG_MXL_862XX 6 7 help 7 8 This enables support for the MaxLinear MxL862xx switch family.
+315 -62
drivers/net/dsa/mxl862xx/mxl862xx-host.c
··· 7 7 * Copyright (C) 2024 MaxLinear Inc. 8 8 */ 9 9 10 + #include <linux/bitops.h> 10 11 #include <linux/bits.h> 12 + #include <linux/crc16.h> 11 13 #include <linux/iopoll.h> 12 14 #include <linux/limits.h> 13 15 #include <net/dsa.h> ··· 17 15 #include "mxl862xx-host.h" 18 16 19 17 #define CTRL_BUSY_MASK BIT(15) 18 + #define CTRL_CRC_FLAG BIT(14) 19 + 20 + #define LEN_RET_LEN_MASK GENMASK(9, 0) 20 21 21 22 #define MXL862XX_MMD_REG_CTRL 0 22 23 #define MXL862XX_MMD_REG_LEN_RET 1 ··· 32 27 #define MMD_API_GET_DATA_0 5 33 28 #define MMD_API_RST_DATA 8 34 29 35 - #define MXL862XX_SWITCH_RESET 0x9907 30 + #define MXL862XX_SWITCH_RESET 0x9907 31 + 32 + static void mxl862xx_crc_err_work_fn(struct work_struct *work) 33 + { 34 + struct mxl862xx_priv *priv = container_of(work, struct mxl862xx_priv, 35 + crc_err_work); 36 + struct dsa_port *dp; 37 + 38 + dev_warn(&priv->mdiodev->dev, 39 + "MDIO CRC error detected, shutting down all ports\n"); 40 + 41 + rtnl_lock(); 42 + dsa_switch_for_each_cpu_port(dp, priv->ds) 43 + dev_close(dp->conduit); 44 + rtnl_unlock(); 45 + 46 + clear_bit(0, &priv->crc_err); 47 + } 48 + 49 + /* Firmware CRC error codes (outside normal Zephyr errno range). */ 50 + #define MXL862XX_FW_CRC6_ERR (-1024) 51 + #define MXL862XX_FW_CRC16_ERR (-1023) 52 + 53 + /* 3GPP CRC-6 lookup table (polynomial 0x6F). 54 + * Matches the firmware's default CRC-6 implementation. 55 + */ 56 + static const u8 mxl862xx_crc6_table[256] = { 57 + 0x00, 0x2f, 0x31, 0x1e, 0x0d, 0x22, 0x3c, 0x13, 58 + 0x1a, 0x35, 0x2b, 0x04, 0x17, 0x38, 0x26, 0x09, 59 + 0x34, 0x1b, 0x05, 0x2a, 0x39, 0x16, 0x08, 0x27, 60 + 0x2e, 0x01, 0x1f, 0x30, 0x23, 0x0c, 0x12, 0x3d, 61 + 0x07, 0x28, 0x36, 0x19, 0x0a, 0x25, 0x3b, 0x14, 62 + 0x1d, 0x32, 0x2c, 0x03, 0x10, 0x3f, 0x21, 0x0e, 63 + 0x33, 0x1c, 0x02, 0x2d, 0x3e, 0x11, 0x0f, 0x20, 64 + 0x29, 0x06, 0x18, 0x37, 0x24, 0x0b, 0x15, 0x3a, 65 + 0x0e, 0x21, 0x3f, 0x10, 0x03, 0x2c, 0x32, 0x1d, 66 + 0x14, 0x3b, 0x25, 0x0a, 0x19, 0x36, 0x28, 0x07, 67 + 0x3a, 0x15, 0x0b, 0x24, 0x37, 0x18, 0x06, 0x29, 68 + 0x20, 0x0f, 0x11, 0x3e, 0x2d, 0x02, 0x1c, 0x33, 69 + 0x09, 0x26, 0x38, 0x17, 0x04, 0x2b, 0x35, 0x1a, 70 + 0x13, 0x3c, 0x22, 0x0d, 0x1e, 0x31, 0x2f, 0x00, 71 + 0x3d, 0x12, 0x0c, 0x23, 0x30, 0x1f, 0x01, 0x2e, 72 + 0x27, 0x08, 0x16, 0x39, 0x2a, 0x05, 0x1b, 0x34, 73 + 0x1c, 0x33, 0x2d, 0x02, 0x11, 0x3e, 0x20, 0x0f, 74 + 0x06, 0x29, 0x37, 0x18, 0x0b, 0x24, 0x3a, 0x15, 75 + 0x28, 0x07, 0x19, 0x36, 0x25, 0x0a, 0x14, 0x3b, 76 + 0x32, 0x1d, 0x03, 0x2c, 0x3f, 0x10, 0x0e, 0x21, 77 + 0x1b, 0x34, 0x2a, 0x05, 0x16, 0x39, 0x27, 0x08, 78 + 0x01, 0x2e, 0x30, 0x1f, 0x0c, 0x23, 0x3d, 0x12, 79 + 0x2f, 0x00, 0x1e, 0x31, 0x22, 0x0d, 0x13, 0x3c, 80 + 0x35, 0x1a, 0x04, 0x2b, 0x38, 0x17, 0x09, 0x26, 81 + 0x12, 0x3d, 0x23, 0x0c, 0x1f, 0x30, 0x2e, 0x01, 82 + 0x08, 0x27, 0x39, 0x16, 0x05, 0x2a, 0x34, 0x1b, 83 + 0x26, 0x09, 0x17, 0x38, 0x2b, 0x04, 0x1a, 0x35, 84 + 0x3c, 0x13, 0x0d, 0x22, 0x31, 0x1e, 0x00, 0x2f, 85 + 0x15, 0x3a, 0x24, 0x0b, 0x18, 0x37, 0x29, 0x06, 86 + 0x0f, 0x20, 0x3e, 0x11, 0x02, 0x2d, 0x33, 0x1c, 87 + 0x21, 0x0e, 0x10, 0x3f, 0x2c, 0x03, 0x1d, 0x32, 88 + 0x3b, 0x14, 0x0a, 0x25, 0x36, 0x19, 0x07, 0x28, 89 + }; 90 + 91 + /* Compute 3GPP CRC-6 over the ctrl register (16 bits) and the lower 92 + * 10 bits of the len_ret register. The 26-bit input is packed as 93 + * { len_ret[9:0], ctrl[15:0] } and processed LSB-first through the 94 + * lookup table. 95 + */ 96 + static u8 mxl862xx_crc6(u16 ctrl, u16 len_ret) 97 + { 98 + u32 data = ((u32)(len_ret & LEN_RET_LEN_MASK) << 16) | ctrl; 99 + u8 crc = 0; 100 + int i; 101 + 102 + for (i = 0; i < sizeof(data); i++, data >>= 8) 103 + crc = mxl862xx_crc6_table[(crc << 2) ^ (data & 0xff)] & 0x3f; 104 + 105 + return crc; 106 + } 107 + 108 + /* Encode CRC-6 into the ctrl and len_ret registers before writing them 109 + * to MDIO. The caller must set ctrl = API_ID | CTRL_BUSY_MASK | 110 + * CTRL_CRC_FLAG, and len_ret = parameter length (bits 0-9 only). 111 + * 112 + * After encoding: 113 + * ctrl[12:0] = API ID (unchanged) 114 + * ctrl[14:13] = CRC-6 bits 5-4 115 + * ctrl[15] = busy flag (unchanged) 116 + * len_ret[9:0] = parameter length (unchanged) 117 + * len_ret[13:10] = CRC-6 bits 3-0 118 + * len_ret[14] = original ctrl[14] (CRC check flag, forwarded to FW) 119 + * len_ret[15] = original ctrl[13] (magic bit, always 1) 120 + */ 121 + static void mxl862xx_crc6_encode(u16 *pctrl, u16 *plen_ret) 122 + { 123 + u16 crc, ctrl, len_ret; 124 + 125 + /* Set magic bit before CRC computation */ 126 + *pctrl |= BIT(13); 127 + 128 + crc = mxl862xx_crc6(*pctrl, *plen_ret); 129 + 130 + /* Place CRC MSB (bits 5-4) into ctrl bits 13-14 */ 131 + ctrl = (*pctrl & ~GENMASK(14, 13)); 132 + ctrl |= (crc & 0x30) << 9; 133 + 134 + /* Place CRC LSB (bits 3-0) into len_ret bits 10-13 */ 135 + len_ret = *plen_ret | ((crc & 0x0f) << 10); 136 + 137 + /* Forward ctrl[14] (CRC check flag) to len_ret[14], 138 + * and ctrl[13] (magic, always 1) to len_ret[15]. 139 + */ 140 + len_ret |= (*pctrl & BIT(14)) | ((*pctrl & BIT(13)) << 2); 141 + 142 + *pctrl = ctrl; 143 + *plen_ret = len_ret; 144 + } 145 + 146 + /* Verify CRC-6 on a firmware response and extract the return value. 147 + * 148 + * The firmware encodes the return value as a signed 11-bit integer: 149 + * - Sign bit (bit 10) in ctrl[14] 150 + * - Magnitude (bits 9-0) in len_ret[9:0] 151 + * These are recoverable after CRC-6 verification by restoring the 152 + * original ctrl from the auxiliary copies in len_ret[15:14]. 153 + * 154 + * Return: 0 on CRC match (with *result set), or -EIO on mismatch. 155 + */ 156 + static int mxl862xx_crc6_verify(u16 ctrl, u16 len_ret, int *result) 157 + { 158 + u16 crc_recv, crc_calc; 159 + 160 + /* Extract the received CRC-6 */ 161 + crc_recv = ((ctrl >> 9) & 0x30) | ((len_ret >> 10) & 0x0f); 162 + 163 + /* Reconstruct the original ctrl for re-computation: 164 + * ctrl[14] = len_ret[14] (sign bit / CRC check flag) 165 + * ctrl[13] = len_ret[15] >> 2 (magic bit) 166 + */ 167 + ctrl &= ~GENMASK(14, 13); 168 + ctrl |= len_ret & BIT(14); 169 + ctrl |= (len_ret & BIT(15)) >> 2; 170 + 171 + crc_calc = mxl862xx_crc6(ctrl, len_ret); 172 + if (crc_recv != crc_calc) 173 + return -EIO; 174 + 175 + /* Extract signed 11-bit return value: 176 + * bit 10 (sign) from ctrl[14], bits 9-0 from len_ret[9:0] 177 + */ 178 + *result = sign_extend32((len_ret & LEN_RET_LEN_MASK) | 179 + ((ctrl & CTRL_CRC_FLAG) >> 4), 10); 180 + 181 + return 0; 182 + } 36 183 37 184 static int mxl862xx_reg_read(struct mxl862xx_priv *priv, u32 addr) 38 185 { ··· 209 52 !(val & CTRL_BUSY_MASK), 15, 500000); 210 53 } 211 54 212 - static int mxl862xx_set_data(struct mxl862xx_priv *priv, u16 words) 55 + /* Issue a firmware command with CRC-6 protection on the ctrl and len_ret 56 + * registers, wait for completion, and verify the response CRC-6. 57 + * 58 + * Return: firmware result value (>= 0) on success, or negative errno. 59 + */ 60 + static int mxl862xx_issue_cmd(struct mxl862xx_priv *priv, u16 cmd, u16 len) 213 61 { 214 - int ret; 215 - u16 cmd; 62 + u16 ctrl_enc, len_enc; 63 + int ret, fw_result; 216 64 217 - ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_LEN_RET, 218 - MXL862XX_MMD_REG_DATA_MAX_SIZE * sizeof(u16)); 65 + ctrl_enc = cmd | CTRL_BUSY_MASK | CTRL_CRC_FLAG; 66 + len_enc = len; 67 + mxl862xx_crc6_encode(&ctrl_enc, &len_enc); 68 + 69 + ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_LEN_RET, len_enc); 219 70 if (ret < 0) 220 71 return ret; 72 + 73 + ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_CTRL, ctrl_enc); 74 + if (ret < 0) 75 + return ret; 76 + 77 + ret = mxl862xx_busy_wait(priv); 78 + if (ret < 0) 79 + return ret; 80 + 81 + ret = mxl862xx_reg_read(priv, MXL862XX_MMD_REG_CTRL); 82 + if (ret < 0) 83 + return ret; 84 + ctrl_enc = ret; 85 + 86 + ret = mxl862xx_reg_read(priv, MXL862XX_MMD_REG_LEN_RET); 87 + if (ret < 0) 88 + return ret; 89 + len_enc = ret; 90 + 91 + ret = mxl862xx_crc6_verify(ctrl_enc, len_enc, &fw_result); 92 + if (ret) { 93 + if (!test_and_set_bit(0, &priv->crc_err)) 94 + schedule_work(&priv->crc_err_work); 95 + return -EIO; 96 + } 97 + 98 + return fw_result; 99 + } 100 + 101 + static int mxl862xx_set_data(struct mxl862xx_priv *priv, u16 words) 102 + { 103 + u16 cmd; 221 104 222 105 cmd = words / MXL862XX_MMD_REG_DATA_MAX_SIZE - 1; 223 106 if (!(cmd < 2)) 224 107 return -EINVAL; 225 108 226 109 cmd += MMD_API_SET_DATA_0; 227 - ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_CTRL, 228 - cmd | CTRL_BUSY_MASK); 229 - if (ret < 0) 230 - return ret; 231 110 232 - return mxl862xx_busy_wait(priv); 111 + return mxl862xx_issue_cmd(priv, cmd, 112 + MXL862XX_MMD_REG_DATA_MAX_SIZE * sizeof(u16)); 233 113 } 234 114 235 115 static int mxl862xx_get_data(struct mxl862xx_priv *priv, u16 words) 236 116 { 237 - int ret; 238 117 u16 cmd; 239 - 240 - ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_LEN_RET, 241 - MXL862XX_MMD_REG_DATA_MAX_SIZE * sizeof(u16)); 242 - if (ret < 0) 243 - return ret; 244 118 245 119 cmd = words / MXL862XX_MMD_REG_DATA_MAX_SIZE; 246 120 if (!(cmd > 0 && cmd < 3)) 247 121 return -EINVAL; 248 122 249 123 cmd += MMD_API_GET_DATA_0; 250 - ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_CTRL, 251 - cmd | CTRL_BUSY_MASK); 252 - if (ret < 0) 253 - return ret; 254 124 255 - return mxl862xx_busy_wait(priv); 125 + return mxl862xx_issue_cmd(priv, cmd, 126 + MXL862XX_MMD_REG_DATA_MAX_SIZE * sizeof(u16)); 256 127 } 257 128 258 - static int mxl862xx_firmware_return(int ret) 129 + static int mxl862xx_rst_data(struct mxl862xx_priv *priv) 259 130 { 260 - /* Only 16-bit values are valid. */ 261 - if (WARN_ON(ret & GENMASK(31, 16))) 262 - return -EINVAL; 263 - 264 - /* Interpret value as signed 16-bit integer. */ 265 - return (s16)ret; 131 + return mxl862xx_issue_cmd(priv, MMD_API_RST_DATA, 0); 266 132 } 133 + 134 + /* Minimum number of zero words in the data payload before issuing a 135 + * RST_DATA command is worthwhile. RST_DATA costs one full command 136 + * round-trip (~5 MDIO transactions), so the threshold must offset that. 137 + */ 138 + #define RST_DATA_THRESHOLD 5 267 139 268 140 static int mxl862xx_send_cmd(struct mxl862xx_priv *priv, u16 cmd, u16 size, 269 141 bool quiet) 270 142 { 271 143 int ret; 272 144 273 - ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_LEN_RET, size); 274 - if (ret) 275 - return ret; 145 + ret = mxl862xx_issue_cmd(priv, cmd, size); 276 146 277 - ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_CTRL, 278 - cmd | CTRL_BUSY_MASK); 279 - if (ret) 280 - return ret; 281 - 282 - ret = mxl862xx_busy_wait(priv); 283 - if (ret) 284 - return ret; 285 - 286 - ret = mxl862xx_reg_read(priv, MXL862XX_MMD_REG_LEN_RET); 287 - if (ret < 0) 288 - return ret; 289 - 290 - /* handle errors returned by the firmware as -EIO 147 + /* Handle errors returned by the firmware as -EIO. 291 148 * The firmware is based on Zephyr OS and uses the errors as 292 149 * defined in errno.h of Zephyr OS. See 293 150 * https://github.com/zephyrproject-rtos/zephyr/blob/v3.7.0/lib/libc/minimal/include/errno.h 151 + * 152 + * The firmware signals CRC validation failures with dedicated 153 + * error codes outside the normal Zephyr errno range: 154 + * -1024: CRC-6 mismatch on ctrl/len_ret registers 155 + * -1023: CRC-16 mismatch on data payload 294 156 */ 295 - ret = mxl862xx_firmware_return(ret); 296 157 if (ret < 0) { 158 + if ((ret == MXL862XX_FW_CRC6_ERR || 159 + ret == MXL862XX_FW_CRC16_ERR) && 160 + !test_and_set_bit(0, &priv->crc_err)) 161 + schedule_work(&priv->crc_err_work); 297 162 if (!quiet) 298 163 dev_err(&priv->mdiodev->dev, 299 164 "CMD %04x returned error %d\n", cmd, ret); ··· 329 150 u16 size, bool read, bool quiet) 330 151 { 331 152 __le16 *data = _data; 153 + bool use_rst = false; 154 + unsigned int zeros; 332 155 int ret, cmd_ret; 333 - u16 max, i; 156 + u16 max, crc, i; 334 157 335 158 dev_dbg(&priv->mdiodev->dev, "CMD %04x DATA %*ph\n", cmd, size, data); 336 159 ··· 344 163 if (ret < 0) 345 164 goto out; 346 165 347 - for (i = 0; i < max; i++) { 166 + /* If the data contains enough zero words, issue RST_DATA to zero 167 + * both the firmware buffer and MMD registers, then skip writing 168 + * zero words individually. 169 + */ 170 + for (i = 0, zeros = 0; i < size / 2 && zeros < RST_DATA_THRESHOLD; i++) 171 + if (!data[i]) 172 + zeros++; 173 + 174 + if (zeros < RST_DATA_THRESHOLD && (size & 1) && !*(u8 *)&data[i]) 175 + zeros++; 176 + 177 + if (zeros >= RST_DATA_THRESHOLD) { 178 + ret = mxl862xx_rst_data(priv); 179 + if (ret < 0) 180 + goto out; 181 + use_rst = true; 182 + } 183 + 184 + /* Compute CRC-16 over the data payload; written as an extra word 185 + * after the data so the firmware can verify the transfer. 186 + */ 187 + crc = crc16(0xffff, (const u8 *)data, size); 188 + 189 + for (i = 0; i < max + 1; i++) { 348 190 u16 off = i % MXL862XX_MMD_REG_DATA_MAX_SIZE; 191 + u16 val; 349 192 350 193 if (i && off == 0) { 351 194 /* Send command to set data when every 352 195 * MXL862XX_MMD_REG_DATA_MAX_SIZE of WORDs are written. 353 - */ 196 + */ 354 197 ret = mxl862xx_set_data(priv, i); 355 198 if (ret < 0) 356 199 goto out; 357 200 } 358 201 359 - if ((i * 2 + 1) == size) 360 - ret = mxl862xx_reg_write(priv, 361 - MXL862XX_MMD_REG_DATA_FIRST + off, 362 - *(u8 *)&data[i]); 363 - else 364 - ret = mxl862xx_reg_write(priv, 365 - MXL862XX_MMD_REG_DATA_FIRST + off, 366 - le16_to_cpu(data[i])); 202 + if (i == max) { 203 + /* Even size: full CRC word. 204 + * Odd size: only CRC high byte remains (low byte 205 + * was packed into the previous word). 206 + */ 207 + val = (size & 1) ? crc >> 8 : crc; 208 + } else if ((i * 2 + 1) == size) { 209 + /* Special handling for last BYTE if it's not WORD 210 + * aligned to avoid reading beyond the allocated data 211 + * structure. Pack the CRC low byte into the high 212 + * byte of this word so it sits at byte offset 'size' 213 + * in the firmware's contiguous buffer. 214 + */ 215 + val = *(u8 *)&data[i] | ((crc & 0xff) << 8); 216 + } else { 217 + val = le16_to_cpu(data[i]); 218 + } 219 + 220 + /* After RST_DATA, skip zero data words as the registers 221 + * already contain zeros, but never skip the CRC word at the 222 + * final word. 223 + */ 224 + if (use_rst && i < max && val == 0) 225 + continue; 226 + 227 + ret = mxl862xx_reg_write(priv, 228 + MXL862XX_MMD_REG_DATA_FIRST + off, 229 + val); 367 230 if (ret < 0) 368 231 goto out; 369 232 } ··· 419 194 /* store result of mxl862xx_send_cmd() */ 420 195 cmd_ret = ret; 421 196 422 - for (i = 0; i < max; i++) { 197 + for (i = 0; i < max + 1; i++) { 423 198 u16 off = i % MXL862XX_MMD_REG_DATA_MAX_SIZE; 424 199 425 200 if (i && off == 0) { 426 201 /* Send command to fetch next batch of data when every 427 202 * MXL862XX_MMD_REG_DATA_MAX_SIZE of WORDs are read. 428 - */ 203 + */ 429 204 ret = mxl862xx_get_data(priv, i); 430 205 if (ret < 0) 431 206 goto out; ··· 435 210 if (ret < 0) 436 211 goto out; 437 212 438 - if ((i * 2 + 1) == size) { 213 + if (i == max) { 214 + /* Even size: full CRC word. 215 + * Odd size: only CRC high byte remains (low byte 216 + * was in the previous word). 217 + */ 218 + if (size & 1) 219 + crc = (crc & 0x00ff) | 220 + (((u16)ret & 0xff) << 8); 221 + else 222 + crc = (u16)ret; 223 + } else if ((i * 2 + 1) == size) { 439 224 /* Special handling for last BYTE if it's not WORD 440 225 * aligned to avoid writing beyond the allocated data 441 - * structure. 226 + * structure. The high byte carries the CRC low byte. 442 227 */ 443 228 *(uint8_t *)&data[i] = ret & 0xff; 229 + crc = (ret >> 8) & 0xff; 444 230 } else { 445 231 data[i] = cpu_to_le16((u16)ret); 446 232 } 233 + } 234 + 235 + if (crc16(0xffff, (const u8 *)data, size) != crc) { 236 + if (!test_and_set_bit(0, &priv->crc_err)) 237 + schedule_work(&priv->crc_err_work); 238 + ret = -EIO; 239 + goto out; 447 240 } 448 241 449 242 /* on success return the result of the mxl862xx_send_cmd() */ ··· 491 248 mutex_unlock(&priv->mdiodev->bus->mdio_lock); 492 249 493 250 return ret; 251 + } 252 + 253 + void mxl862xx_host_init(struct mxl862xx_priv *priv) 254 + { 255 + INIT_WORK(&priv->crc_err_work, mxl862xx_crc_err_work_fn); 256 + } 257 + 258 + void mxl862xx_host_shutdown(struct mxl862xx_priv *priv) 259 + { 260 + cancel_work_sync(&priv->crc_err_work); 494 261 }
+2
drivers/net/dsa/mxl862xx/mxl862xx-host.h
··· 5 5 6 6 #include "mxl862xx.h" 7 7 8 + void mxl862xx_host_init(struct mxl862xx_priv *priv); 9 + void mxl862xx_host_shutdown(struct mxl862xx_priv *priv); 8 10 int mxl862xx_api_wrap(struct mxl862xx_priv *priv, u16 cmd, void *data, u16 size, 9 11 bool read, bool quiet); 10 12 int mxl862xx_reset(struct mxl862xx_priv *priv);
+11
drivers/net/dsa/mxl862xx/mxl862xx.c
··· 424 424 ds->ops = &mxl862xx_switch_ops; 425 425 ds->phylink_mac_ops = &mxl862xx_phylink_mac_ops; 426 426 ds->num_ports = MXL862XX_MAX_PORTS; 427 + mxl862xx_host_init(priv); 427 428 428 429 dev_set_drvdata(dev, ds); 429 430 ··· 434 433 static void mxl862xx_remove(struct mdio_device *mdiodev) 435 434 { 436 435 struct dsa_switch *ds = dev_get_drvdata(&mdiodev->dev); 436 + struct mxl862xx_priv *priv; 437 437 438 438 if (!ds) 439 439 return; 440 440 441 + priv = ds->priv; 442 + 441 443 dsa_unregister_switch(ds); 444 + 445 + mxl862xx_host_shutdown(priv); 442 446 } 443 447 444 448 static void mxl862xx_shutdown(struct mdio_device *mdiodev) 445 449 { 446 450 struct dsa_switch *ds = dev_get_drvdata(&mdiodev->dev); 451 + struct mxl862xx_priv *priv; 447 452 448 453 if (!ds) 449 454 return; 450 455 456 + priv = ds->priv; 457 + 451 458 dsa_switch_shutdown(ds); 459 + 460 + mxl862xx_host_shutdown(priv); 452 461 453 462 dev_set_drvdata(&mdiodev->dev, NULL); 454 463 }
+2
drivers/net/dsa/mxl862xx/mxl862xx.h
··· 11 11 struct mxl862xx_priv { 12 12 struct dsa_switch *ds; 13 13 struct mdio_device *mdiodev; 14 + struct work_struct crc_err_work; 15 + unsigned long crc_err; 14 16 }; 15 17 16 18 #endif /* __MXL862XX_H */