1 // SPDX-License-Identifier: GPL-2.0 2 /* Copyright (c) 2018-2019, Vladimir Oltean <olteanv@gmail.com> 3 */ 4 #include "sja1105.h" 5 6 #define SJA1105_SIZE_DYN_CMD 4 7 8 #define SJA1105ET_SIZE_MAC_CONFIG_DYN_ENTRY \ 9 SJA1105_SIZE_DYN_CMD 10 11 #define SJA1105ET_SIZE_L2_LOOKUP_DYN_CMD \ 12 (SJA1105_SIZE_DYN_CMD + SJA1105ET_SIZE_L2_LOOKUP_ENTRY) 13 14 #define SJA1105PQRS_SIZE_L2_LOOKUP_DYN_CMD \ 15 (SJA1105_SIZE_DYN_CMD + SJA1105PQRS_SIZE_L2_LOOKUP_ENTRY) 16 17 #define SJA1105_SIZE_VLAN_LOOKUP_DYN_CMD \ 18 (SJA1105_SIZE_DYN_CMD + 4 + SJA1105_SIZE_VLAN_LOOKUP_ENTRY) 19 20 #define SJA1105_SIZE_L2_FORWARDING_DYN_CMD \ 21 (SJA1105_SIZE_DYN_CMD + SJA1105_SIZE_L2_FORWARDING_ENTRY) 22 23 #define SJA1105ET_SIZE_MAC_CONFIG_DYN_CMD \ 24 (SJA1105_SIZE_DYN_CMD + SJA1105ET_SIZE_MAC_CONFIG_DYN_ENTRY) 25 26 #define SJA1105PQRS_SIZE_MAC_CONFIG_DYN_CMD \ 27 (SJA1105_SIZE_DYN_CMD + SJA1105PQRS_SIZE_MAC_CONFIG_ENTRY) 28 29 #define SJA1105ET_SIZE_L2_LOOKUP_PARAMS_DYN_CMD \ 30 SJA1105_SIZE_DYN_CMD 31 32 #define SJA1105ET_SIZE_GENERAL_PARAMS_DYN_CMD \ 33 SJA1105_SIZE_DYN_CMD 34 35 #define SJA1105_MAX_DYN_CMD_SIZE \ 36 SJA1105PQRS_SIZE_MAC_CONFIG_DYN_CMD 37 38 struct sja1105_dyn_cmd { 39 bool search; 40 u64 valid; 41 u64 rdwrset; 42 u64 errors; 43 u64 valident; 44 u64 index; 45 }; 46 47 enum sja1105_hostcmd { 48 SJA1105_HOSTCMD_SEARCH = 1, 49 SJA1105_HOSTCMD_READ = 2, 50 SJA1105_HOSTCMD_WRITE = 3, 51 SJA1105_HOSTCMD_INVALIDATE = 4, 52 }; 53 54 static void 55 sja1105pqrs_l2_lookup_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd, 56 enum packing_op op) 57 { 58 u8 *p = buf + SJA1105PQRS_SIZE_L2_LOOKUP_ENTRY; 59 const int size = SJA1105_SIZE_DYN_CMD; 60 u64 lockeds = 0; 61 u64 hostcmd; 62 63 sja1105_packing(p, &cmd->valid, 31, 31, size, op); 64 sja1105_packing(p, &cmd->rdwrset, 30, 30, size, op); 65 sja1105_packing(p, &cmd->errors, 29, 29, size, op); 66 sja1105_packing(p, &lockeds, 28, 28, size, op); 67 sja1105_packing(p, &cmd->valident, 27, 27, size, op); 68 69 /* VALIDENT is supposed to indicate "keep or not", but in SJA1105 E/T, 70 * using it to delete a management route was unsupported. UM10944 71 * said about it: 72 * 73 * In case of a write access with the MGMTROUTE flag set, 74 * the flag will be ignored. It will always be found cleared 75 * for read accesses with the MGMTROUTE flag set. 76 * 77 * SJA1105 P/Q/R/S keeps the same behavior w.r.t. VALIDENT, but there 78 * is now another flag called HOSTCMD which does more stuff (quoting 79 * from UM11040): 80 * 81 * A write request is accepted only when HOSTCMD is set to write host 82 * or invalid. A read request is accepted only when HOSTCMD is set to 83 * search host or read host. 84 * 85 * So it is possible to translate a RDWRSET/VALIDENT combination into 86 * HOSTCMD so that we keep the dynamic command API in place, and 87 * at the same time achieve compatibility with the management route 88 * command structure. 89 */ 90 if (cmd->rdwrset == SPI_READ) { 91 if (cmd->search) 92 hostcmd = SJA1105_HOSTCMD_SEARCH; 93 else 94 hostcmd = SJA1105_HOSTCMD_READ; 95 } else { 96 /* SPI_WRITE */ 97 if (cmd->valident) 98 hostcmd = SJA1105_HOSTCMD_WRITE; 99 else 100 hostcmd = SJA1105_HOSTCMD_INVALIDATE; 101 } 102 sja1105_packing(p, &hostcmd, 25, 23, size, op); 103 104 /* Hack - The hardware takes the 'index' field within 105 * struct sja1105_l2_lookup_entry as the index on which this command 106 * will operate. However it will ignore everything else, so 'index' 107 * is logically part of command but physically part of entry. 108 * Populate the 'index' entry field from within the command callback, 109 * such that our API doesn't need to ask for a full-blown entry 110 * structure when e.g. a delete is requested. 111 */ 112 sja1105_packing(buf, &cmd->index, 15, 6, 113 SJA1105PQRS_SIZE_L2_LOOKUP_ENTRY, op); 114 } 115 116 static void 117 sja1105et_l2_lookup_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd, 118 enum packing_op op) 119 { 120 u8 *p = buf + SJA1105ET_SIZE_L2_LOOKUP_ENTRY; 121 const int size = SJA1105_SIZE_DYN_CMD; 122 123 sja1105_packing(p, &cmd->valid, 31, 31, size, op); 124 sja1105_packing(p, &cmd->rdwrset, 30, 30, size, op); 125 sja1105_packing(p, &cmd->errors, 29, 29, size, op); 126 sja1105_packing(p, &cmd->valident, 27, 27, size, op); 127 /* Hack - see comments above. */ 128 sja1105_packing(buf, &cmd->index, 29, 20, 129 SJA1105ET_SIZE_L2_LOOKUP_ENTRY, op); 130 } 131 132 static void 133 sja1105et_mgmt_route_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd, 134 enum packing_op op) 135 { 136 u8 *p = buf + SJA1105ET_SIZE_L2_LOOKUP_ENTRY; 137 u64 mgmtroute = 1; 138 139 sja1105et_l2_lookup_cmd_packing(buf, cmd, op); 140 if (op == PACK) 141 sja1105_pack(p, &mgmtroute, 26, 26, SJA1105_SIZE_DYN_CMD); 142 } 143 144 static size_t sja1105et_mgmt_route_entry_packing(void *buf, void *entry_ptr, 145 enum packing_op op) 146 { 147 struct sja1105_mgmt_entry *entry = entry_ptr; 148 const size_t size = SJA1105ET_SIZE_L2_LOOKUP_ENTRY; 149 150 /* UM10944: To specify if a PTP egress timestamp shall be captured on 151 * each port upon transmission of the frame, the LSB of VLANID in the 152 * ENTRY field provided by the host must be set. 153 * Bit 1 of VLANID then specifies the register where the timestamp for 154 * this port is stored in. 155 */ 156 sja1105_packing(buf, &entry->tsreg, 85, 85, size, op); 157 sja1105_packing(buf, &entry->takets, 84, 84, size, op); 158 sja1105_packing(buf, &entry->macaddr, 83, 36, size, op); 159 sja1105_packing(buf, &entry->destports, 35, 31, size, op); 160 sja1105_packing(buf, &entry->enfport, 30, 30, size, op); 161 return size; 162 } 163 164 static void 165 sja1105pqrs_mgmt_route_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd, 166 enum packing_op op) 167 { 168 u8 *p = buf + SJA1105PQRS_SIZE_L2_LOOKUP_ENTRY; 169 u64 mgmtroute = 1; 170 171 sja1105pqrs_l2_lookup_cmd_packing(buf, cmd, op); 172 if (op == PACK) 173 sja1105_pack(p, &mgmtroute, 26, 26, SJA1105_SIZE_DYN_CMD); 174 } 175 176 static size_t sja1105pqrs_mgmt_route_entry_packing(void *buf, void *entry_ptr, 177 enum packing_op op) 178 { 179 const size_t size = SJA1105PQRS_SIZE_L2_LOOKUP_ENTRY; 180 struct sja1105_mgmt_entry *entry = entry_ptr; 181 182 /* In P/Q/R/S, enfport got renamed to mgmtvalid, but its purpose 183 * is the same (driver uses it to confirm that frame was sent). 184 * So just keep the name from E/T. 185 */ 186 sja1105_packing(buf, &entry->tsreg, 71, 71, size, op); 187 sja1105_packing(buf, &entry->takets, 70, 70, size, op); 188 sja1105_packing(buf, &entry->macaddr, 69, 22, size, op); 189 sja1105_packing(buf, &entry->destports, 21, 17, size, op); 190 sja1105_packing(buf, &entry->enfport, 16, 16, size, op); 191 return size; 192 } 193 194 /* In E/T, entry is at addresses 0x27-0x28. There is a 4 byte gap at 0x29, 195 * and command is at 0x2a. Similarly in P/Q/R/S there is a 1 register gap 196 * between entry (0x2d, 0x2e) and command (0x30). 197 */ 198 static void 199 sja1105_vlan_lookup_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd, 200 enum packing_op op) 201 { 202 u8 *p = buf + SJA1105_SIZE_VLAN_LOOKUP_ENTRY + 4; 203 const int size = SJA1105_SIZE_DYN_CMD; 204 205 sja1105_packing(p, &cmd->valid, 31, 31, size, op); 206 sja1105_packing(p, &cmd->rdwrset, 30, 30, size, op); 207 sja1105_packing(p, &cmd->valident, 27, 27, size, op); 208 /* Hack - see comments above, applied for 'vlanid' field of 209 * struct sja1105_vlan_lookup_entry. 210 */ 211 sja1105_packing(buf, &cmd->index, 38, 27, 212 SJA1105_SIZE_VLAN_LOOKUP_ENTRY, op); 213 } 214 215 static void 216 sja1105_l2_forwarding_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd, 217 enum packing_op op) 218 { 219 u8 *p = buf + SJA1105_SIZE_L2_FORWARDING_ENTRY; 220 const int size = SJA1105_SIZE_DYN_CMD; 221 222 sja1105_packing(p, &cmd->valid, 31, 31, size, op); 223 sja1105_packing(p, &cmd->errors, 30, 30, size, op); 224 sja1105_packing(p, &cmd->rdwrset, 29, 29, size, op); 225 sja1105_packing(p, &cmd->index, 4, 0, size, op); 226 } 227 228 static void 229 sja1105et_mac_config_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd, 230 enum packing_op op) 231 { 232 const int size = SJA1105_SIZE_DYN_CMD; 233 /* Yup, user manual definitions are reversed */ 234 u8 *reg1 = buf + 4; 235 236 sja1105_packing(reg1, &cmd->valid, 31, 31, size, op); 237 sja1105_packing(reg1, &cmd->index, 26, 24, size, op); 238 } 239 240 static size_t sja1105et_mac_config_entry_packing(void *buf, void *entry_ptr, 241 enum packing_op op) 242 { 243 const int size = SJA1105ET_SIZE_MAC_CONFIG_DYN_ENTRY; 244 struct sja1105_mac_config_entry *entry = entry_ptr; 245 /* Yup, user manual definitions are reversed */ 246 u8 *reg1 = buf + 4; 247 u8 *reg2 = buf; 248 249 sja1105_packing(reg1, &entry->speed, 30, 29, size, op); 250 sja1105_packing(reg1, &entry->drpdtag, 23, 23, size, op); 251 sja1105_packing(reg1, &entry->drpuntag, 22, 22, size, op); 252 sja1105_packing(reg1, &entry->retag, 21, 21, size, op); 253 sja1105_packing(reg1, &entry->dyn_learn, 20, 20, size, op); 254 sja1105_packing(reg1, &entry->egress, 19, 19, size, op); 255 sja1105_packing(reg1, &entry->ingress, 18, 18, size, op); 256 sja1105_packing(reg1, &entry->ing_mirr, 17, 17, size, op); 257 sja1105_packing(reg1, &entry->egr_mirr, 16, 16, size, op); 258 sja1105_packing(reg1, &entry->vlanprio, 14, 12, size, op); 259 sja1105_packing(reg1, &entry->vlanid, 11, 0, size, op); 260 sja1105_packing(reg2, &entry->tp_delin, 31, 16, size, op); 261 sja1105_packing(reg2, &entry->tp_delout, 15, 0, size, op); 262 /* MAC configuration table entries which can't be reconfigured: 263 * top, base, enabled, ifg, maxage, drpnona664 264 */ 265 /* Bogus return value, not used anywhere */ 266 return 0; 267 } 268 269 static void 270 sja1105pqrs_mac_config_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd, 271 enum packing_op op) 272 { 273 const int size = SJA1105ET_SIZE_MAC_CONFIG_DYN_ENTRY; 274 u8 *p = buf + SJA1105PQRS_SIZE_MAC_CONFIG_ENTRY; 275 276 sja1105_packing(p, &cmd->valid, 31, 31, size, op); 277 sja1105_packing(p, &cmd->errors, 30, 30, size, op); 278 sja1105_packing(p, &cmd->rdwrset, 29, 29, size, op); 279 sja1105_packing(p, &cmd->index, 2, 0, size, op); 280 } 281 282 static void 283 sja1105et_l2_lookup_params_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd, 284 enum packing_op op) 285 { 286 sja1105_packing(buf, &cmd->valid, 31, 31, 287 SJA1105ET_SIZE_L2_LOOKUP_PARAMS_DYN_CMD, op); 288 } 289 290 static size_t 291 sja1105et_l2_lookup_params_entry_packing(void *buf, void *entry_ptr, 292 enum packing_op op) 293 { 294 struct sja1105_l2_lookup_params_entry *entry = entry_ptr; 295 296 sja1105_packing(buf, &entry->poly, 7, 0, 297 SJA1105ET_SIZE_L2_LOOKUP_PARAMS_DYN_CMD, op); 298 /* Bogus return value, not used anywhere */ 299 return 0; 300 } 301 302 static void 303 sja1105et_general_params_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd, 304 enum packing_op op) 305 { 306 const int size = SJA1105ET_SIZE_GENERAL_PARAMS_DYN_CMD; 307 308 sja1105_packing(buf, &cmd->valid, 31, 31, size, op); 309 sja1105_packing(buf, &cmd->errors, 30, 30, size, op); 310 } 311 312 static size_t 313 sja1105et_general_params_entry_packing(void *buf, void *entry_ptr, 314 enum packing_op op) 315 { 316 struct sja1105_general_params_entry *entry = entry_ptr; 317 const int size = SJA1105ET_SIZE_GENERAL_PARAMS_DYN_CMD; 318 319 sja1105_packing(buf, &entry->mirr_port, 2, 0, size, op); 320 /* Bogus return value, not used anywhere */ 321 return 0; 322 } 323 324 #define OP_READ BIT(0) 325 #define OP_WRITE BIT(1) 326 #define OP_DEL BIT(2) 327 #define OP_SEARCH BIT(3) 328 329 /* SJA1105E/T: First generation */ 330 struct sja1105_dynamic_table_ops sja1105et_dyn_ops[BLK_IDX_MAX_DYN] = { 331 [BLK_IDX_L2_LOOKUP] = { 332 .entry_packing = sja1105et_l2_lookup_entry_packing, 333 .cmd_packing = sja1105et_l2_lookup_cmd_packing, 334 .access = (OP_READ | OP_WRITE | OP_DEL), 335 .max_entry_count = SJA1105_MAX_L2_LOOKUP_COUNT, 336 .packed_size = SJA1105ET_SIZE_L2_LOOKUP_DYN_CMD, 337 .addr = 0x20, 338 }, 339 [BLK_IDX_MGMT_ROUTE] = { 340 .entry_packing = sja1105et_mgmt_route_entry_packing, 341 .cmd_packing = sja1105et_mgmt_route_cmd_packing, 342 .access = (OP_READ | OP_WRITE), 343 .max_entry_count = SJA1105_NUM_PORTS, 344 .packed_size = SJA1105ET_SIZE_L2_LOOKUP_DYN_CMD, 345 .addr = 0x20, 346 }, 347 [BLK_IDX_L2_POLICING] = {0}, 348 [BLK_IDX_VLAN_LOOKUP] = { 349 .entry_packing = sja1105_vlan_lookup_entry_packing, 350 .cmd_packing = sja1105_vlan_lookup_cmd_packing, 351 .access = (OP_WRITE | OP_DEL), 352 .max_entry_count = SJA1105_MAX_VLAN_LOOKUP_COUNT, 353 .packed_size = SJA1105_SIZE_VLAN_LOOKUP_DYN_CMD, 354 .addr = 0x27, 355 }, 356 [BLK_IDX_L2_FORWARDING] = { 357 .entry_packing = sja1105_l2_forwarding_entry_packing, 358 .cmd_packing = sja1105_l2_forwarding_cmd_packing, 359 .max_entry_count = SJA1105_MAX_L2_FORWARDING_COUNT, 360 .access = OP_WRITE, 361 .packed_size = SJA1105_SIZE_L2_FORWARDING_DYN_CMD, 362 .addr = 0x24, 363 }, 364 [BLK_IDX_MAC_CONFIG] = { 365 .entry_packing = sja1105et_mac_config_entry_packing, 366 .cmd_packing = sja1105et_mac_config_cmd_packing, 367 .max_entry_count = SJA1105_MAX_MAC_CONFIG_COUNT, 368 .access = OP_WRITE, 369 .packed_size = SJA1105ET_SIZE_MAC_CONFIG_DYN_CMD, 370 .addr = 0x36, 371 }, 372 [BLK_IDX_L2_LOOKUP_PARAMS] = { 373 .entry_packing = sja1105et_l2_lookup_params_entry_packing, 374 .cmd_packing = sja1105et_l2_lookup_params_cmd_packing, 375 .max_entry_count = SJA1105_MAX_L2_LOOKUP_PARAMS_COUNT, 376 .access = OP_WRITE, 377 .packed_size = SJA1105ET_SIZE_L2_LOOKUP_PARAMS_DYN_CMD, 378 .addr = 0x38, 379 }, 380 [BLK_IDX_L2_FORWARDING_PARAMS] = {0}, 381 [BLK_IDX_AVB_PARAMS] = {0}, 382 [BLK_IDX_GENERAL_PARAMS] = { 383 .entry_packing = sja1105et_general_params_entry_packing, 384 .cmd_packing = sja1105et_general_params_cmd_packing, 385 .max_entry_count = SJA1105_MAX_GENERAL_PARAMS_COUNT, 386 .access = OP_WRITE, 387 .packed_size = SJA1105ET_SIZE_GENERAL_PARAMS_DYN_CMD, 388 .addr = 0x34, 389 }, 390 [BLK_IDX_XMII_PARAMS] = {0}, 391 }; 392 393 /* SJA1105P/Q/R/S: Second generation */ 394 struct sja1105_dynamic_table_ops sja1105pqrs_dyn_ops[BLK_IDX_MAX_DYN] = { 395 [BLK_IDX_L2_LOOKUP] = { 396 .entry_packing = sja1105pqrs_l2_lookup_entry_packing, 397 .cmd_packing = sja1105pqrs_l2_lookup_cmd_packing, 398 .access = (OP_READ | OP_WRITE | OP_DEL | OP_SEARCH), 399 .max_entry_count = SJA1105_MAX_L2_LOOKUP_COUNT, 400 .packed_size = SJA1105PQRS_SIZE_L2_LOOKUP_DYN_CMD, 401 .addr = 0x24, 402 }, 403 [BLK_IDX_MGMT_ROUTE] = { 404 .entry_packing = sja1105pqrs_mgmt_route_entry_packing, 405 .cmd_packing = sja1105pqrs_mgmt_route_cmd_packing, 406 .access = (OP_READ | OP_WRITE | OP_DEL | OP_SEARCH), 407 .max_entry_count = SJA1105_NUM_PORTS, 408 .packed_size = SJA1105PQRS_SIZE_L2_LOOKUP_DYN_CMD, 409 .addr = 0x24, 410 }, 411 [BLK_IDX_L2_POLICING] = {0}, 412 [BLK_IDX_VLAN_LOOKUP] = { 413 .entry_packing = sja1105_vlan_lookup_entry_packing, 414 .cmd_packing = sja1105_vlan_lookup_cmd_packing, 415 .access = (OP_READ | OP_WRITE | OP_DEL), 416 .max_entry_count = SJA1105_MAX_VLAN_LOOKUP_COUNT, 417 .packed_size = SJA1105_SIZE_VLAN_LOOKUP_DYN_CMD, 418 .addr = 0x2D, 419 }, 420 [BLK_IDX_L2_FORWARDING] = { 421 .entry_packing = sja1105_l2_forwarding_entry_packing, 422 .cmd_packing = sja1105_l2_forwarding_cmd_packing, 423 .max_entry_count = SJA1105_MAX_L2_FORWARDING_COUNT, 424 .access = OP_WRITE, 425 .packed_size = SJA1105_SIZE_L2_FORWARDING_DYN_CMD, 426 .addr = 0x2A, 427 }, 428 [BLK_IDX_MAC_CONFIG] = { 429 .entry_packing = sja1105pqrs_mac_config_entry_packing, 430 .cmd_packing = sja1105pqrs_mac_config_cmd_packing, 431 .max_entry_count = SJA1105_MAX_MAC_CONFIG_COUNT, 432 .access = (OP_READ | OP_WRITE), 433 .packed_size = SJA1105PQRS_SIZE_MAC_CONFIG_DYN_CMD, 434 .addr = 0x4B, 435 }, 436 [BLK_IDX_L2_LOOKUP_PARAMS] = { 437 .entry_packing = sja1105et_l2_lookup_params_entry_packing, 438 .cmd_packing = sja1105et_l2_lookup_params_cmd_packing, 439 .max_entry_count = SJA1105_MAX_L2_LOOKUP_PARAMS_COUNT, 440 .access = (OP_READ | OP_WRITE), 441 .packed_size = SJA1105ET_SIZE_L2_LOOKUP_PARAMS_DYN_CMD, 442 .addr = 0x38, 443 }, 444 [BLK_IDX_L2_FORWARDING_PARAMS] = {0}, 445 [BLK_IDX_AVB_PARAMS] = {0}, 446 [BLK_IDX_GENERAL_PARAMS] = { 447 .entry_packing = sja1105et_general_params_entry_packing, 448 .cmd_packing = sja1105et_general_params_cmd_packing, 449 .max_entry_count = SJA1105_MAX_GENERAL_PARAMS_COUNT, 450 .access = OP_WRITE, 451 .packed_size = SJA1105ET_SIZE_GENERAL_PARAMS_DYN_CMD, 452 .addr = 0x34, 453 }, 454 [BLK_IDX_XMII_PARAMS] = {0}, 455 }; 456 457 /* Provides read access to the settings through the dynamic interface 458 * of the switch. 459 * @blk_idx is used as key to select from the sja1105_dynamic_table_ops. 460 * The selection is limited by the hardware in respect to which 461 * configuration blocks can be read through the dynamic interface. 462 * @index is used to retrieve a particular table entry. If negative, 463 * (and if the @blk_idx supports the searching operation) a search 464 * is performed by the @entry parameter. 465 * @entry Type-casted to an unpacked structure that holds a table entry 466 * of the type specified in @blk_idx. 467 * Usually an output argument. If @index is negative, then this 468 * argument is used as input/output: it should be pre-populated 469 * with the element to search for. Entries which support the 470 * search operation will have an "index" field (not the @index 471 * argument to this function) and that is where the found index 472 * will be returned (or left unmodified - thus negative - if not 473 * found). 474 */ 475 int sja1105_dynamic_config_read(struct sja1105_private *priv, 476 enum sja1105_blk_idx blk_idx, 477 int index, void *entry) 478 { 479 const struct sja1105_dynamic_table_ops *ops; 480 struct sja1105_dyn_cmd cmd = {0}; 481 /* SPI payload buffer */ 482 u8 packed_buf[SJA1105_MAX_DYN_CMD_SIZE] = {0}; 483 int retries = 3; 484 int rc; 485 486 if (blk_idx >= BLK_IDX_MAX_DYN) 487 return -ERANGE; 488 489 ops = &priv->info->dyn_ops[blk_idx]; 490 491 if (index >= 0 && index >= ops->max_entry_count) 492 return -ERANGE; 493 if (index < 0 && !(ops->access & OP_SEARCH)) 494 return -EOPNOTSUPP; 495 if (!(ops->access & OP_READ)) 496 return -EOPNOTSUPP; 497 if (ops->packed_size > SJA1105_MAX_DYN_CMD_SIZE) 498 return -ERANGE; 499 if (!ops->cmd_packing) 500 return -EOPNOTSUPP; 501 if (!ops->entry_packing) 502 return -EOPNOTSUPP; 503 504 cmd.valid = true; /* Trigger action on table entry */ 505 cmd.rdwrset = SPI_READ; /* Action is read */ 506 if (index < 0) { 507 /* Avoid copying a signed negative number to an u64 */ 508 cmd.index = 0; 509 cmd.search = true; 510 } else { 511 cmd.index = index; 512 cmd.search = false; 513 } 514 cmd.valident = true; 515 ops->cmd_packing(packed_buf, &cmd, PACK); 516 517 if (cmd.search) 518 ops->entry_packing(packed_buf, entry, PACK); 519 520 /* Send SPI write operation: read config table entry */ 521 rc = sja1105_spi_send_packed_buf(priv, SPI_WRITE, ops->addr, 522 packed_buf, ops->packed_size); 523 if (rc < 0) 524 return rc; 525 526 /* Loop until we have confirmation that hardware has finished 527 * processing the command and has cleared the VALID field 528 */ 529 do { 530 memset(packed_buf, 0, ops->packed_size); 531 532 /* Retrieve the read operation's result */ 533 rc = sja1105_spi_send_packed_buf(priv, SPI_READ, ops->addr, 534 packed_buf, ops->packed_size); 535 if (rc < 0) 536 return rc; 537 538 cmd = (struct sja1105_dyn_cmd) {0}; 539 ops->cmd_packing(packed_buf, &cmd, UNPACK); 540 /* UM10944: [valident] will always be found cleared 541 * during a read access with MGMTROUTE set. 542 * So don't error out in that case. 543 */ 544 if (!cmd.valident && blk_idx != BLK_IDX_MGMT_ROUTE) 545 return -ENOENT; 546 cpu_relax(); 547 } while (cmd.valid && --retries); 548 549 if (cmd.valid) 550 return -ETIMEDOUT; 551 552 /* Don't dereference possibly NULL pointer - maybe caller 553 * only wanted to see whether the entry existed or not. 554 */ 555 if (entry) 556 ops->entry_packing(packed_buf, entry, UNPACK); 557 return 0; 558 } 559 560 int sja1105_dynamic_config_write(struct sja1105_private *priv, 561 enum sja1105_blk_idx blk_idx, 562 int index, void *entry, bool keep) 563 { 564 const struct sja1105_dynamic_table_ops *ops; 565 struct sja1105_dyn_cmd cmd = {0}; 566 /* SPI payload buffer */ 567 u8 packed_buf[SJA1105_MAX_DYN_CMD_SIZE] = {0}; 568 int rc; 569 570 if (blk_idx >= BLK_IDX_MAX_DYN) 571 return -ERANGE; 572 573 ops = &priv->info->dyn_ops[blk_idx]; 574 575 if (index >= ops->max_entry_count) 576 return -ERANGE; 577 if (index < 0) 578 return -ERANGE; 579 if (!(ops->access & OP_WRITE)) 580 return -EOPNOTSUPP; 581 if (!keep && !(ops->access & OP_DEL)) 582 return -EOPNOTSUPP; 583 if (ops->packed_size > SJA1105_MAX_DYN_CMD_SIZE) 584 return -ERANGE; 585 586 cmd.valident = keep; /* If false, deletes entry */ 587 cmd.valid = true; /* Trigger action on table entry */ 588 cmd.rdwrset = SPI_WRITE; /* Action is write */ 589 cmd.index = index; 590 591 if (!ops->cmd_packing) 592 return -EOPNOTSUPP; 593 ops->cmd_packing(packed_buf, &cmd, PACK); 594 595 if (!ops->entry_packing) 596 return -EOPNOTSUPP; 597 /* Don't dereference potentially NULL pointer if just 598 * deleting a table entry is what was requested. For cases 599 * where 'index' field is physically part of entry structure, 600 * and needed here, we deal with that in the cmd_packing callback. 601 */ 602 if (keep) 603 ops->entry_packing(packed_buf, entry, PACK); 604 605 /* Send SPI write operation: read config table entry */ 606 rc = sja1105_spi_send_packed_buf(priv, SPI_WRITE, ops->addr, 607 packed_buf, ops->packed_size); 608 if (rc < 0) 609 return rc; 610 611 cmd = (struct sja1105_dyn_cmd) {0}; 612 ops->cmd_packing(packed_buf, &cmd, UNPACK); 613 if (cmd.errors) 614 return -EINVAL; 615 616 return 0; 617 } 618 619 static u8 sja1105_crc8_add(u8 crc, u8 byte, u8 poly) 620 { 621 int i; 622 623 for (i = 0; i < 8; i++) { 624 if ((crc ^ byte) & (1 << 7)) { 625 crc <<= 1; 626 crc ^= poly; 627 } else { 628 crc <<= 1; 629 } 630 byte <<= 1; 631 } 632 return crc; 633 } 634 635 /* CRC8 algorithm with non-reversed input, non-reversed output, 636 * no input xor and no output xor. Code customized for receiving 637 * the SJA1105 E/T FDB keys (vlanid, macaddr) as input. CRC polynomial 638 * is also received as argument in the Koopman notation that the switch 639 * hardware stores it in. 640 */ 641 u8 sja1105et_fdb_hash(struct sja1105_private *priv, const u8 *addr, u16 vid) 642 { 643 struct sja1105_l2_lookup_params_entry *l2_lookup_params = 644 priv->static_config.tables[BLK_IDX_L2_LOOKUP_PARAMS].entries; 645 u64 poly_koopman = l2_lookup_params->poly; 646 /* Convert polynomial from Koopman to 'normal' notation */ 647 u8 poly = (u8)(1 + (poly_koopman << 1)); 648 u64 vlanid = l2_lookup_params->shared_learn ? 0 : vid; 649 u64 input = (vlanid << 48) | ether_addr_to_u64(addr); 650 u8 crc = 0; /* seed */ 651 int i; 652 653 /* Mask the eight bytes starting from MSB one at a time */ 654 for (i = 56; i >= 0; i -= 8) { 655 u8 byte = (input & (0xffull << i)) >> i; 656 657 crc = sja1105_crc8_add(crc, byte, poly); 658 } 659 return crc; 660 } 661