1 // SPDX-License-Identifier: GPL-2.0 2 /* Texas Instruments PRUETH Switch Driver 3 * 4 * Copyright (C) 2020-2021 Texas Instruments Incorporated - https://www.ti.com 5 */ 6 #include <linux/etherdevice.h> 7 #include <linux/kernel.h> 8 #include <linux/remoteproc.h> 9 #include <net/switchdev.h> 10 #include "icssm_prueth.h" 11 #include "icssm_prueth_switch.h" 12 #include "icssm_prueth_fdb_tbl.h" 13 14 #define FDB_IDX_TBL_ENTRY(n) (&prueth->fdb_tbl->index_a->index_tbl_entry[n]) 15 16 #define FDB_MAC_TBL_ENTRY(n) (&prueth->fdb_tbl->mac_tbl_a->mac_tbl_entry[n]) 17 18 #define FLAG_IS_STATIC BIT(0) 19 #define FLAG_ACTIVE BIT(1) 20 21 #define FDB_LEARN 1 22 #define FDB_PURGE 2 23 24 struct icssm_prueth_sw_fdb_work { 25 netdevice_tracker ndev_tracker; 26 struct work_struct work; 27 struct prueth_emac *emac; 28 u8 addr[ETH_ALEN]; 29 int event; 30 }; 31 32 const struct prueth_queue_info sw_queue_infos[][NUM_QUEUES] = { 33 [PRUETH_PORT_QUEUE_HOST] = { 34 [PRUETH_QUEUE1] = { 35 P0_Q1_BUFFER_OFFSET, 36 P0_QUEUE_DESC_OFFSET, 37 P0_Q1_BD_OFFSET, 38 P0_Q1_BD_OFFSET + ((HOST_QUEUE_1_SIZE - 1) * BD_SIZE), 39 }, 40 [PRUETH_QUEUE2] = { 41 P0_Q2_BUFFER_OFFSET, 42 P0_QUEUE_DESC_OFFSET + 8, 43 P0_Q2_BD_OFFSET, 44 P0_Q2_BD_OFFSET + ((HOST_QUEUE_2_SIZE - 1) * BD_SIZE), 45 }, 46 [PRUETH_QUEUE3] = { 47 P0_Q3_BUFFER_OFFSET, 48 P0_QUEUE_DESC_OFFSET + 16, 49 P0_Q3_BD_OFFSET, 50 P0_Q3_BD_OFFSET + ((HOST_QUEUE_3_SIZE - 1) * BD_SIZE), 51 }, 52 [PRUETH_QUEUE4] = { 53 P0_Q4_BUFFER_OFFSET, 54 P0_QUEUE_DESC_OFFSET + 24, 55 P0_Q4_BD_OFFSET, 56 P0_Q4_BD_OFFSET + ((HOST_QUEUE_4_SIZE - 1) * BD_SIZE), 57 }, 58 }, 59 [PRUETH_PORT_QUEUE_MII0] = { 60 [PRUETH_QUEUE1] = { 61 P1_Q1_BUFFER_OFFSET, 62 P1_Q1_BUFFER_OFFSET + 63 ((QUEUE_1_SIZE - 1) * ICSS_BLOCK_SIZE), 64 P1_Q1_BD_OFFSET, 65 P1_Q1_BD_OFFSET + ((QUEUE_1_SIZE - 1) * BD_SIZE), 66 }, 67 [PRUETH_QUEUE2] = { 68 P1_Q2_BUFFER_OFFSET, 69 P1_Q2_BUFFER_OFFSET + 70 ((QUEUE_2_SIZE - 1) * ICSS_BLOCK_SIZE), 71 P1_Q2_BD_OFFSET, 72 P1_Q2_BD_OFFSET + ((QUEUE_2_SIZE - 1) * BD_SIZE), 73 }, 74 [PRUETH_QUEUE3] = { 75 P1_Q3_BUFFER_OFFSET, 76 P1_Q3_BUFFER_OFFSET + 77 ((QUEUE_3_SIZE - 1) * ICSS_BLOCK_SIZE), 78 P1_Q3_BD_OFFSET, 79 P1_Q3_BD_OFFSET + ((QUEUE_3_SIZE - 1) * BD_SIZE), 80 }, 81 [PRUETH_QUEUE4] = { 82 P1_Q4_BUFFER_OFFSET, 83 P1_Q4_BUFFER_OFFSET + 84 ((QUEUE_4_SIZE - 1) * ICSS_BLOCK_SIZE), 85 P1_Q4_BD_OFFSET, 86 P1_Q4_BD_OFFSET + ((QUEUE_4_SIZE - 1) * BD_SIZE), 87 }, 88 }, 89 [PRUETH_PORT_QUEUE_MII1] = { 90 [PRUETH_QUEUE1] = { 91 P2_Q1_BUFFER_OFFSET, 92 P2_Q1_BUFFER_OFFSET + 93 ((QUEUE_1_SIZE - 1) * ICSS_BLOCK_SIZE), 94 P2_Q1_BD_OFFSET, 95 P2_Q1_BD_OFFSET + ((QUEUE_1_SIZE - 1) * BD_SIZE), 96 }, 97 [PRUETH_QUEUE2] = { 98 P2_Q2_BUFFER_OFFSET, 99 P2_Q2_BUFFER_OFFSET + 100 ((QUEUE_2_SIZE - 1) * ICSS_BLOCK_SIZE), 101 P2_Q2_BD_OFFSET, 102 P2_Q2_BD_OFFSET + ((QUEUE_2_SIZE - 1) * BD_SIZE), 103 }, 104 [PRUETH_QUEUE3] = { 105 P2_Q3_BUFFER_OFFSET, 106 P2_Q3_BUFFER_OFFSET + 107 ((QUEUE_3_SIZE - 1) * ICSS_BLOCK_SIZE), 108 P2_Q3_BD_OFFSET, 109 P2_Q3_BD_OFFSET + ((QUEUE_3_SIZE - 1) * BD_SIZE), 110 }, 111 [PRUETH_QUEUE4] = { 112 P2_Q4_BUFFER_OFFSET, 113 P2_Q4_BUFFER_OFFSET + 114 ((QUEUE_4_SIZE - 1) * ICSS_BLOCK_SIZE), 115 P2_Q4_BD_OFFSET, 116 P2_Q4_BD_OFFSET + ((QUEUE_4_SIZE - 1) * BD_SIZE), 117 }, 118 }, 119 }; 120 121 static const struct prueth_queue_info rx_queue_infos[][NUM_QUEUES] = { 122 [PRUETH_PORT_QUEUE_HOST] = { 123 [PRUETH_QUEUE1] = { 124 P0_Q1_BUFFER_OFFSET, 125 HOST_QUEUE_DESC_OFFSET, 126 P0_Q1_BD_OFFSET, 127 P0_Q1_BD_OFFSET + ((HOST_QUEUE_1_SIZE - 1) * BD_SIZE), 128 }, 129 [PRUETH_QUEUE2] = { 130 P0_Q2_BUFFER_OFFSET, 131 HOST_QUEUE_DESC_OFFSET + 8, 132 P0_Q2_BD_OFFSET, 133 P0_Q2_BD_OFFSET + ((HOST_QUEUE_2_SIZE - 1) * BD_SIZE), 134 }, 135 [PRUETH_QUEUE3] = { 136 P0_Q3_BUFFER_OFFSET, 137 HOST_QUEUE_DESC_OFFSET + 16, 138 P0_Q3_BD_OFFSET, 139 P0_Q3_BD_OFFSET + ((HOST_QUEUE_3_SIZE - 1) * BD_SIZE), 140 }, 141 [PRUETH_QUEUE4] = { 142 P0_Q4_BUFFER_OFFSET, 143 HOST_QUEUE_DESC_OFFSET + 24, 144 P0_Q4_BD_OFFSET, 145 P0_Q4_BD_OFFSET + ((HOST_QUEUE_4_SIZE - 1) * BD_SIZE), 146 }, 147 }, 148 [PRUETH_PORT_QUEUE_MII0] = { 149 [PRUETH_QUEUE1] = { 150 P1_Q1_BUFFER_OFFSET, 151 P1_QUEUE_DESC_OFFSET, 152 P1_Q1_BD_OFFSET, 153 P1_Q1_BD_OFFSET + ((QUEUE_1_SIZE - 1) * BD_SIZE), 154 }, 155 [PRUETH_QUEUE2] = { 156 P1_Q2_BUFFER_OFFSET, 157 P1_QUEUE_DESC_OFFSET + 8, 158 P1_Q2_BD_OFFSET, 159 P1_Q2_BD_OFFSET + ((QUEUE_2_SIZE - 1) * BD_SIZE), 160 }, 161 [PRUETH_QUEUE3] = { 162 P1_Q3_BUFFER_OFFSET, 163 P1_QUEUE_DESC_OFFSET + 16, 164 P1_Q3_BD_OFFSET, 165 P1_Q3_BD_OFFSET + ((QUEUE_3_SIZE - 1) * BD_SIZE), 166 }, 167 [PRUETH_QUEUE4] = { 168 P1_Q4_BUFFER_OFFSET, 169 P1_QUEUE_DESC_OFFSET + 24, 170 P1_Q4_BD_OFFSET, 171 P1_Q4_BD_OFFSET + ((QUEUE_4_SIZE - 1) * BD_SIZE), 172 }, 173 }, 174 [PRUETH_PORT_QUEUE_MII1] = { 175 [PRUETH_QUEUE1] = { 176 P2_Q1_BUFFER_OFFSET, 177 P2_QUEUE_DESC_OFFSET, 178 P2_Q1_BD_OFFSET, 179 P2_Q1_BD_OFFSET + ((QUEUE_1_SIZE - 1) * BD_SIZE), 180 }, 181 [PRUETH_QUEUE2] = { 182 P2_Q2_BUFFER_OFFSET, 183 P2_QUEUE_DESC_OFFSET + 8, 184 P2_Q2_BD_OFFSET, 185 P2_Q2_BD_OFFSET + ((QUEUE_2_SIZE - 1) * BD_SIZE), 186 }, 187 [PRUETH_QUEUE3] = { 188 P2_Q3_BUFFER_OFFSET, 189 P2_QUEUE_DESC_OFFSET + 16, 190 P2_Q3_BD_OFFSET, 191 P2_Q3_BD_OFFSET + ((QUEUE_3_SIZE - 1) * BD_SIZE), 192 }, 193 [PRUETH_QUEUE4] = { 194 P2_Q4_BUFFER_OFFSET, 195 P2_QUEUE_DESC_OFFSET + 24, 196 P2_Q4_BD_OFFSET, 197 P2_Q4_BD_OFFSET + ((QUEUE_4_SIZE - 1) * BD_SIZE), 198 }, 199 }, 200 }; 201 202 void icssm_prueth_sw_free_fdb_table(struct prueth *prueth) 203 { 204 if (prueth->emac_configured) 205 return; 206 207 kfree(prueth->fdb_tbl); 208 prueth->fdb_tbl = NULL; 209 } 210 211 void icssm_prueth_sw_fdb_tbl_init(struct prueth *prueth) 212 { 213 struct fdb_tbl *t = prueth->fdb_tbl; 214 void __iomem *sram_base; 215 u8 val; 216 217 sram_base = prueth->mem[PRUETH_MEM_SHARED_RAM].va; 218 219 t->index_a = sram_base + V2_1_FDB_TBL_OFFSET; 220 t->mac_tbl_a = sram_base + FDB_MAC_TBL_OFFSET; 221 t->port1_stp_cfg = sram_base + FDB_PORT1_STP_CFG_OFFSET; 222 t->port2_stp_cfg = sram_base + FDB_PORT2_STP_CFG_OFFSET; 223 t->flood_enable_flags = sram_base + FDB_FLOOD_ENABLE_FLAGS_OFFSET; 224 t->locks = sram_base + FDB_LOCKS_OFFSET; 225 226 val = readb(t->flood_enable_flags); 227 /* host_flood_enable = 1 */ 228 val |= BIT(0); 229 /* port1_flood_enable = 1 */ 230 val |= BIT(1); 231 /* port2_flood_enable = 1 */ 232 val |= BIT(2); 233 writeb(val, t->flood_enable_flags); 234 235 writeb(0, &t->locks->host_lock); 236 t->total_entries = 0; 237 } 238 239 static u8 icssm_pru_lock_done(struct fdb_tbl *fdb_tbl) 240 { 241 return readb(&fdb_tbl->locks->pru_locks); 242 } 243 244 static int icssm_prueth_sw_fdb_spin_lock(struct fdb_tbl *fdb_tbl) 245 { 246 u8 done; 247 int ret; 248 249 /* Take the host lock */ 250 writeb(1, &fdb_tbl->locks->host_lock); 251 252 /* Wait for the PRUs to release their locks */ 253 ret = read_poll_timeout(icssm_pru_lock_done, done, done == 0, 254 1, 10, false, fdb_tbl); 255 if (ret == -ETIMEDOUT) 256 writeb(0, &fdb_tbl->locks->host_lock); 257 258 return ret; 259 } 260 261 static void icssm_prueth_sw_fdb_spin_unlock(struct fdb_tbl *fdb_tbl) 262 { 263 writeb(0, &fdb_tbl->locks->host_lock); 264 } 265 266 static u8 icssm_prueth_sw_fdb_hash(const u8 *mac) 267 { 268 return (mac[0] ^ mac[1] ^ mac[2] ^ mac[3] ^ mac[4] ^ mac[5]); 269 } 270 271 static int 272 icssm_prueth_sw_fdb_search(struct fdb_mac_tbl_array __iomem *mac_tbl, 273 struct fdb_index_tbl_entry __iomem *bucket_info, 274 const u8 *mac) 275 { 276 unsigned int bucket_entries, mac_tbl_idx; 277 u8 tmp_mac[ETH_ALEN]; 278 int i; 279 280 mac_tbl_idx = readw(&bucket_info->bucket_idx); 281 bucket_entries = readw(&bucket_info->bucket_entries); 282 for (i = 0; i < bucket_entries; i++, mac_tbl_idx++) { 283 memcpy_fromio(tmp_mac, mac_tbl->mac_tbl_entry[mac_tbl_idx].mac, 284 ETH_ALEN); 285 if (ether_addr_equal(mac, tmp_mac)) 286 return mac_tbl_idx; 287 } 288 289 return -ENODATA; 290 } 291 292 static int icssm_prueth_sw_fdb_find_open_slot(struct fdb_tbl *fdb_tbl) 293 { 294 unsigned int i; 295 u8 flags; 296 297 for (i = 0; i < FDB_MAC_TBL_MAX_ENTRIES; i++) { 298 flags = readb(&fdb_tbl->mac_tbl_a->mac_tbl_entry[i].flags); 299 if (!(flags & FLAG_ACTIVE)) 300 break; 301 } 302 303 return i; 304 } 305 306 static int 307 icssm_prueth_sw_find_fdb_insert(struct fdb_tbl *fdb, struct prueth *prueth, 308 struct fdb_index_tbl_entry __iomem *bkt_info, 309 const u8 *mac, const u8 port) 310 { 311 struct fdb_mac_tbl_array __iomem *mac_tbl = fdb->mac_tbl_a; 312 unsigned int bucket_entries, mac_tbl_idx; 313 struct fdb_mac_tbl_entry __iomem *e; 314 u8 mac_from_hw[ETH_ALEN]; 315 s8 cmp; 316 int i; 317 318 mac_tbl_idx = readw(&bkt_info->bucket_idx); 319 bucket_entries = readw(&bkt_info->bucket_entries); 320 321 for (i = 0; i < bucket_entries; i++, mac_tbl_idx++) { 322 e = &mac_tbl->mac_tbl_entry[mac_tbl_idx]; 323 memcpy_fromio(mac_from_hw, e->mac, ETH_ALEN); 324 cmp = memcmp(mac, mac_from_hw, ETH_ALEN); 325 if (cmp < 0) { 326 return mac_tbl_idx; 327 } else if (cmp == 0) { 328 if (readb(&e->port) != port) { 329 /* MAC is already in FDB, only port is 330 * different. So just update the port. 331 * Note: total_entries and bucket_entries 332 * remain the same. 333 */ 334 writeb(port, &e->port); 335 } 336 337 /* MAC and port are the same, touch the fdb */ 338 writew(0, &e->age); 339 return -EEXIST; 340 } 341 } 342 343 return mac_tbl_idx; 344 } 345 346 static int 347 icssm_prueth_sw_fdb_empty_slot_left(struct fdb_mac_tbl_array __iomem *mac_tbl, 348 unsigned int mac_tbl_idx) 349 { 350 u8 flags; 351 int i; 352 353 for (i = mac_tbl_idx - 1; i > -1; i--) { 354 flags = readb(&mac_tbl->mac_tbl_entry[i].flags); 355 if (!(flags & FLAG_ACTIVE)) 356 break; 357 } 358 359 return i; 360 } 361 362 static int 363 icssm_prueth_sw_fdb_empty_slot_right(struct fdb_mac_tbl_array __iomem *mac_tbl, 364 unsigned int mac_tbl_idx) 365 { 366 u8 flags; 367 int i; 368 369 for (i = mac_tbl_idx; i < FDB_MAC_TBL_MAX_ENTRIES; i++) { 370 flags = readb(&mac_tbl->mac_tbl_entry[i].flags); 371 if (!(flags & FLAG_ACTIVE)) 372 return i; 373 } 374 375 return -1; 376 } 377 378 static void icssm_prueth_sw_fdb_move_range_left(struct prueth *prueth, 379 u16 left, u16 right) 380 { 381 struct fdb_mac_tbl_entry entry; 382 u32 sz = 0; 383 u16 i; 384 385 sz = sizeof(struct fdb_mac_tbl_entry); 386 for (i = left; i < right; i++) { 387 memcpy_fromio(&entry, FDB_MAC_TBL_ENTRY(i + 1), sz); 388 memcpy_toio(FDB_MAC_TBL_ENTRY(i), &entry, sz); 389 } 390 } 391 392 static void icssm_prueth_sw_fdb_move_range_right(struct prueth *prueth, 393 u16 left, u16 right) 394 { 395 struct fdb_mac_tbl_entry entry; 396 u32 sz = 0; 397 u16 i; 398 399 sz = sizeof(struct fdb_mac_tbl_entry); 400 for (i = right; i > left; i--) { 401 memcpy_fromio(&entry, FDB_MAC_TBL_ENTRY(i - 1), sz); 402 memcpy_toio(FDB_MAC_TBL_ENTRY(i), &entry, sz); 403 } 404 } 405 406 static void icssm_prueth_sw_fdb_update_index_tbl(struct prueth *prueth, 407 u16 left, u16 right) 408 { 409 unsigned int hash, hash_prev; 410 u8 mac[ETH_ALEN]; 411 unsigned int i; 412 413 /* To ensure we don't improperly update the 414 * bucket index, initialize with an invalid 415 * hash in case we are in leftmost slot 416 */ 417 hash_prev = 0xff; 418 419 if (left > 0) { 420 memcpy_fromio(mac, FDB_MAC_TBL_ENTRY(left - 1)->mac, ETH_ALEN); 421 hash_prev = icssm_prueth_sw_fdb_hash(mac); 422 } 423 424 /* For each moved element, update the bucket index */ 425 for (i = left; i <= right; i++) { 426 memcpy_fromio(mac, FDB_MAC_TBL_ENTRY(i)->mac, ETH_ALEN); 427 hash = icssm_prueth_sw_fdb_hash(mac); 428 429 /* Only need to update buckets once */ 430 if (hash != hash_prev) 431 writew(i, &FDB_IDX_TBL_ENTRY(hash)->bucket_idx); 432 433 hash_prev = hash; 434 } 435 } 436 437 static struct fdb_mac_tbl_entry __iomem * 438 icssm_prueth_sw_find_free_mac(struct prueth *prueth, struct fdb_index_tbl_entry 439 __iomem *bucket_info, u8 suggested_mac_tbl_idx, 440 bool *update_indexes, const u8 *mac) 441 { 442 s16 empty_slot_idx = 0, left = 0, right = 0; 443 unsigned int mti = suggested_mac_tbl_idx; 444 struct fdb_mac_tbl_array __iomem *mt; 445 struct fdb_tbl *fdb; 446 u8 flags; 447 448 fdb = prueth->fdb_tbl; 449 mt = fdb->mac_tbl_a; 450 451 flags = readb(&FDB_MAC_TBL_ENTRY(mti)->flags); 452 if (!(flags & FLAG_ACTIVE)) { 453 /* Claim the entry */ 454 flags |= FLAG_ACTIVE; 455 writeb(flags, &FDB_MAC_TBL_ENTRY(mti)->flags); 456 457 return FDB_MAC_TBL_ENTRY(mti); 458 } 459 460 if (fdb->total_entries == FDB_MAC_TBL_MAX_ENTRIES) 461 return NULL; 462 463 empty_slot_idx = icssm_prueth_sw_fdb_empty_slot_left(mt, mti); 464 if (empty_slot_idx == -1) { 465 /* Nothing available on the left. But table isn't full 466 * so there must be space to the right, 467 */ 468 empty_slot_idx = icssm_prueth_sw_fdb_empty_slot_right(mt, mti); 469 470 /* Shift right */ 471 left = mti; 472 right = empty_slot_idx; 473 icssm_prueth_sw_fdb_move_range_right(prueth, left, right); 474 475 /* Claim the entry */ 476 flags = readb(&FDB_MAC_TBL_ENTRY(mti)->flags); 477 flags |= FLAG_ACTIVE; 478 writeb(flags, &FDB_MAC_TBL_ENTRY(mti)->flags); 479 480 memcpy_toio(FDB_MAC_TBL_ENTRY(mti)->mac, mac, ETH_ALEN); 481 482 /* There is a chance we moved something in a 483 * different bucket, update index table 484 */ 485 icssm_prueth_sw_fdb_update_index_tbl(prueth, left, right); 486 487 return FDB_MAC_TBL_ENTRY(mti); 488 } 489 490 if (empty_slot_idx == mti - 1) { 491 /* There is space immediately left of the open slot, 492 * which means the inserted MAC address 493 * must be the lowest-valued MAC address in bucket. 494 * Update bucket pointer accordingly. 495 */ 496 writew(empty_slot_idx, &bucket_info->bucket_idx); 497 498 /* Claim the entry */ 499 flags = readb(&FDB_MAC_TBL_ENTRY(empty_slot_idx)->flags); 500 flags |= FLAG_ACTIVE; 501 writeb(flags, &FDB_MAC_TBL_ENTRY(empty_slot_idx)->flags); 502 503 return FDB_MAC_TBL_ENTRY(empty_slot_idx); 504 } 505 506 /* There is empty space to the left, shift MAC table entries left */ 507 left = empty_slot_idx; 508 right = mti - 1; 509 icssm_prueth_sw_fdb_move_range_left(prueth, left, right); 510 511 /* Claim the entry */ 512 flags = readb(&FDB_MAC_TBL_ENTRY(mti - 1)->flags); 513 flags |= FLAG_ACTIVE; 514 writeb(flags, &FDB_MAC_TBL_ENTRY(mti - 1)->flags); 515 516 memcpy_toio(FDB_MAC_TBL_ENTRY(mti - 1)->mac, mac, ETH_ALEN); 517 518 /* There is a chance we moved something in a 519 * different bucket, update index table 520 */ 521 icssm_prueth_sw_fdb_update_index_tbl(prueth, left, right); 522 523 return FDB_MAC_TBL_ENTRY(mti - 1); 524 } 525 526 static int icssm_prueth_sw_insert_fdb_entry(struct prueth_emac *emac, 527 const u8 *mac, u8 is_static) 528 { 529 struct fdb_index_tbl_entry __iomem *bucket_info; 530 struct fdb_mac_tbl_entry __iomem *mac_info; 531 struct prueth *prueth = emac->prueth; 532 unsigned int hash_val, mac_tbl_idx; 533 struct prueth_emac *other_emac; 534 enum prueth_port other_port_id; 535 int total_fdb_entries; 536 struct fdb_tbl *fdb; 537 u8 flags; 538 s16 ret; 539 int err; 540 u16 val; 541 542 fdb = prueth->fdb_tbl; 543 other_port_id = (emac->port_id == PRUETH_PORT_MII0) ? 544 PRUETH_PORT_MII1 : PRUETH_PORT_MII0; 545 546 other_emac = prueth->emac[other_port_id - 1]; 547 if (!other_emac) 548 return -EINVAL; 549 550 err = icssm_prueth_sw_fdb_spin_lock(fdb); 551 if (err) { 552 dev_err(prueth->dev, "PRU lock timeout %d\n", err); 553 return err; 554 } 555 556 if (fdb->total_entries == FDB_MAC_TBL_MAX_ENTRIES) { 557 icssm_prueth_sw_fdb_spin_unlock(fdb); 558 return -ENOMEM; 559 } 560 561 if (ether_addr_equal(mac, emac->mac_addr) || 562 (ether_addr_equal(mac, other_emac->mac_addr))) { 563 icssm_prueth_sw_fdb_spin_unlock(fdb); 564 /* Don't insert fdb of own mac addr */ 565 return -EINVAL; 566 } 567 568 /* Get the bucket that the mac belongs to */ 569 hash_val = icssm_prueth_sw_fdb_hash(mac); 570 bucket_info = FDB_IDX_TBL_ENTRY(hash_val); 571 572 if (!readw(&bucket_info->bucket_entries)) { 573 mac_tbl_idx = icssm_prueth_sw_fdb_find_open_slot(fdb); 574 writew(mac_tbl_idx, &bucket_info->bucket_idx); 575 } 576 577 ret = icssm_prueth_sw_find_fdb_insert(fdb, prueth, bucket_info, mac, 578 emac->port_id - 1); 579 if (ret < 0) { 580 icssm_prueth_sw_fdb_spin_unlock(fdb); 581 /* mac is already in fdb table */ 582 return 0; 583 } 584 585 mac_tbl_idx = ret; 586 587 mac_info = icssm_prueth_sw_find_free_mac(prueth, bucket_info, 588 mac_tbl_idx, NULL, 589 mac); 590 if (!mac_info) { 591 /* Should not happen */ 592 dev_warn(prueth->dev, "OUT of FDB MEM\n"); 593 icssm_prueth_sw_fdb_spin_unlock(fdb); 594 return -ENOMEM; 595 } 596 597 memcpy_toio(mac_info->mac, mac, ETH_ALEN); 598 writew(0, &mac_info->age); 599 writeb(emac->port_id - 1, &mac_info->port); 600 601 flags = readb(&mac_info->flags); 602 if (is_static) 603 flags |= FLAG_IS_STATIC; 604 else 605 flags &= ~FLAG_IS_STATIC; 606 607 /* bit 1 - active */ 608 flags |= FLAG_ACTIVE; 609 writeb(flags, &mac_info->flags); 610 611 val = readw(&bucket_info->bucket_entries); 612 val++; 613 writew(val, &bucket_info->bucket_entries); 614 615 fdb->total_entries++; 616 617 total_fdb_entries = fdb->total_entries; 618 619 icssm_prueth_sw_fdb_spin_unlock(fdb); 620 621 dev_dbg(prueth->dev, "added fdb: %pM port=%d total_entries=%u\n", 622 mac, emac->port_id, total_fdb_entries); 623 624 return 0; 625 } 626 627 static int icssm_prueth_sw_delete_fdb_entry(struct prueth_emac *emac, 628 const u8 *mac, u8 is_static) 629 { 630 struct fdb_index_tbl_entry __iomem *bucket_info; 631 struct fdb_mac_tbl_entry __iomem *mac_info; 632 struct fdb_mac_tbl_array __iomem *mt; 633 unsigned int hash_val, mac_tbl_idx; 634 unsigned int idx, entries; 635 struct prueth *prueth; 636 int total_fdb_entries; 637 s16 ret, left, right; 638 struct fdb_tbl *fdb; 639 u8 flags; 640 int err; 641 u16 val; 642 643 prueth = emac->prueth; 644 fdb = prueth->fdb_tbl; 645 mt = fdb->mac_tbl_a; 646 647 err = icssm_prueth_sw_fdb_spin_lock(fdb); 648 if (err) { 649 dev_err(prueth->dev, "PRU lock timeout %d\n", err); 650 return err; 651 } 652 653 if (fdb->total_entries == 0) { 654 icssm_prueth_sw_fdb_spin_unlock(fdb); 655 return 0; 656 } 657 658 /* Get the bucket that the mac belongs to */ 659 hash_val = icssm_prueth_sw_fdb_hash(mac); 660 bucket_info = FDB_IDX_TBL_ENTRY(hash_val); 661 662 ret = icssm_prueth_sw_fdb_search(mt, bucket_info, mac); 663 if (ret < 0) { 664 icssm_prueth_sw_fdb_spin_unlock(fdb); 665 return ret; 666 } 667 668 mac_tbl_idx = ret; 669 mac_info = FDB_MAC_TBL_ENTRY(mac_tbl_idx); 670 671 /* Shift all elements in bucket to the left. No need to 672 * update index table since only shifting within bucket. 673 */ 674 left = mac_tbl_idx; 675 idx = readw(&bucket_info->bucket_idx); 676 entries = readw(&bucket_info->bucket_entries); 677 right = idx + entries - 1; 678 icssm_prueth_sw_fdb_move_range_left(prueth, left, right); 679 680 /* Remove end of bucket from table */ 681 mac_info = FDB_MAC_TBL_ENTRY(right); 682 flags = readb(&mac_info->flags); 683 /* active = 0 */ 684 flags &= ~FLAG_ACTIVE; 685 writeb(flags, &mac_info->flags); 686 val = readw(&bucket_info->bucket_entries); 687 val--; 688 writew(val, &bucket_info->bucket_entries); 689 fdb->total_entries--; 690 691 total_fdb_entries = fdb->total_entries; 692 693 icssm_prueth_sw_fdb_spin_unlock(fdb); 694 695 dev_dbg(prueth->dev, "del fdb: %pM total_entries=%u\n", 696 mac, total_fdb_entries); 697 698 return 0; 699 } 700 701 int icssm_prueth_sw_do_purge_fdb(struct prueth_emac *emac) 702 { 703 struct fdb_index_tbl_entry __iomem *bucket_info; 704 struct prueth *prueth = emac->prueth; 705 u8 flags, mac[ETH_ALEN]; 706 unsigned int hash_val; 707 struct fdb_tbl *fdb; 708 int ret, i; 709 u16 val; 710 711 fdb = prueth->fdb_tbl; 712 713 ret = icssm_prueth_sw_fdb_spin_lock(fdb); 714 if (ret) { 715 dev_err(prueth->dev, "PRU lock timeout %d\n", ret); 716 return ret; 717 } 718 719 if (fdb->total_entries == 0) { 720 icssm_prueth_sw_fdb_spin_unlock(fdb); 721 return 0; 722 } 723 724 for (i = 0; i < FDB_MAC_TBL_MAX_ENTRIES; i++) { 725 flags = readb(&fdb->mac_tbl_a->mac_tbl_entry[i].flags); 726 if ((flags & FLAG_ACTIVE) && !(flags & FLAG_IS_STATIC)) { 727 /* Get the bucket that the mac belongs to */ 728 memcpy_fromio(mac, FDB_MAC_TBL_ENTRY(i)->mac, 729 ETH_ALEN); 730 hash_val = icssm_prueth_sw_fdb_hash(mac); 731 bucket_info = FDB_IDX_TBL_ENTRY(hash_val); 732 flags &= ~FLAG_ACTIVE; 733 writeb(flags, 734 &fdb->mac_tbl_a->mac_tbl_entry[i].flags); 735 val = readw(&bucket_info->bucket_entries); 736 val--; 737 writew(val, &bucket_info->bucket_entries); 738 fdb->total_entries--; 739 } 740 } 741 742 icssm_prueth_sw_fdb_spin_unlock(fdb); 743 return 0; 744 } 745 746 int icssm_prueth_sw_init_fdb_table(struct prueth *prueth) 747 { 748 if (prueth->emac_configured) 749 return 0; 750 751 prueth->fdb_tbl = kmalloc_obj(*prueth->fdb_tbl); 752 if (!prueth->fdb_tbl) 753 return -ENOMEM; 754 755 icssm_prueth_sw_fdb_tbl_init(prueth); 756 757 return 0; 758 } 759 760 /** 761 * icssm_prueth_sw_fdb_add - insert fdb entry 762 * 763 * @emac: EMAC data structure 764 * @fdb: fdb info 765 * 766 */ 767 void icssm_prueth_sw_fdb_add(struct prueth_emac *emac, 768 struct switchdev_notifier_fdb_info *fdb) 769 { 770 icssm_prueth_sw_insert_fdb_entry(emac, fdb->addr, 1); 771 } 772 773 /** 774 * icssm_prueth_sw_fdb_del - delete fdb entry 775 * 776 * @emac: EMAC data structure 777 * @fdb: fdb info 778 * 779 */ 780 void icssm_prueth_sw_fdb_del(struct prueth_emac *emac, 781 struct switchdev_notifier_fdb_info *fdb) 782 { 783 icssm_prueth_sw_delete_fdb_entry(emac, fdb->addr, 1); 784 } 785 786 static void icssm_prueth_sw_fdb_work(struct work_struct *work) 787 { 788 struct icssm_prueth_sw_fdb_work *fdb_work = 789 container_of(work, struct icssm_prueth_sw_fdb_work, work); 790 struct prueth_emac *emac = fdb_work->emac; 791 792 rtnl_lock(); 793 794 /* Interface is not up */ 795 if (!emac->prueth->fdb_tbl) 796 goto free; 797 798 switch (fdb_work->event) { 799 case FDB_LEARN: 800 icssm_prueth_sw_insert_fdb_entry(emac, fdb_work->addr, 0); 801 break; 802 case FDB_PURGE: 803 icssm_prueth_sw_do_purge_fdb(emac); 804 break; 805 default: 806 break; 807 } 808 809 free: 810 rtnl_unlock(); 811 netdev_put(emac->ndev, &fdb_work->ndev_tracker); 812 kfree(fdb_work); 813 } 814 815 int icssm_prueth_sw_learn_fdb(struct prueth_emac *emac, u8 *src_mac) 816 { 817 struct icssm_prueth_sw_fdb_work *fdb_work; 818 819 fdb_work = kzalloc_obj(*fdb_work, GFP_ATOMIC); 820 if (WARN_ON(!fdb_work)) 821 return -ENOMEM; 822 823 INIT_WORK(&fdb_work->work, icssm_prueth_sw_fdb_work); 824 825 fdb_work->event = FDB_LEARN; 826 fdb_work->emac = emac; 827 ether_addr_copy(fdb_work->addr, src_mac); 828 829 netdev_hold(emac->ndev, &fdb_work->ndev_tracker, GFP_ATOMIC); 830 queue_work(system_long_wq, &fdb_work->work); 831 return 0; 832 } 833 834 int icssm_prueth_sw_purge_fdb(struct prueth_emac *emac) 835 { 836 struct icssm_prueth_sw_fdb_work *fdb_work; 837 838 fdb_work = kzalloc_obj(*fdb_work, GFP_ATOMIC); 839 if (WARN_ON(!fdb_work)) 840 return -ENOMEM; 841 842 INIT_WORK(&fdb_work->work, icssm_prueth_sw_fdb_work); 843 844 fdb_work->event = FDB_PURGE; 845 fdb_work->emac = emac; 846 847 netdev_hold(emac->ndev, &fdb_work->ndev_tracker, GFP_ATOMIC); 848 queue_work(system_long_wq, &fdb_work->work); 849 return 0; 850 } 851 852 void icssm_prueth_sw_hostconfig(struct prueth *prueth) 853 { 854 void __iomem *dram1_base = prueth->mem[PRUETH_MEM_DRAM1].va; 855 void __iomem *dram; 856 857 /* queue information table */ 858 dram = dram1_base + P0_Q1_RX_CONTEXT_OFFSET; 859 memcpy_toio(dram, sw_queue_infos[PRUETH_PORT_QUEUE_HOST], 860 sizeof(sw_queue_infos[PRUETH_PORT_QUEUE_HOST])); 861 862 /* buffer descriptor offset table*/ 863 dram = dram1_base + QUEUE_DESCRIPTOR_OFFSET_ADDR; 864 writew(P0_Q1_BD_OFFSET, dram); 865 writew(P0_Q2_BD_OFFSET, dram + 2); 866 writew(P0_Q3_BD_OFFSET, dram + 4); 867 writew(P0_Q4_BD_OFFSET, dram + 6); 868 869 /* buffer offset table */ 870 dram = dram1_base + QUEUE_OFFSET_ADDR; 871 writew(P0_Q1_BUFFER_OFFSET, dram); 872 writew(P0_Q2_BUFFER_OFFSET, dram + 2); 873 writew(P0_Q3_BUFFER_OFFSET, dram + 4); 874 writew(P0_Q4_BUFFER_OFFSET, dram + 6); 875 876 /* queue size lookup table */ 877 dram = dram1_base + QUEUE_SIZE_ADDR; 878 writew(HOST_QUEUE_1_SIZE, dram); 879 writew(HOST_QUEUE_1_SIZE, dram + 2); 880 writew(HOST_QUEUE_1_SIZE, dram + 4); 881 writew(HOST_QUEUE_1_SIZE, dram + 6); 882 883 /* queue table */ 884 dram = dram1_base + P0_QUEUE_DESC_OFFSET; 885 memcpy_toio(dram, queue_descs[PRUETH_PORT_QUEUE_HOST], 886 sizeof(queue_descs[PRUETH_PORT_QUEUE_HOST])); 887 } 888 889 static int icssm_prueth_sw_port_config(struct prueth *prueth, 890 enum prueth_port port_id) 891 { 892 unsigned int tx_context_ofs_addr, rx_context_ofs, queue_desc_ofs; 893 void __iomem *dram, *dram_base, *dram_mac; 894 struct prueth_emac *emac; 895 void __iomem *dram1_base; 896 897 dram1_base = prueth->mem[PRUETH_MEM_DRAM1].va; 898 emac = prueth->emac[port_id - 1]; 899 switch (port_id) { 900 case PRUETH_PORT_MII0: 901 tx_context_ofs_addr = TX_CONTEXT_P1_Q1_OFFSET_ADDR; 902 rx_context_ofs = P1_Q1_RX_CONTEXT_OFFSET; 903 queue_desc_ofs = P1_QUEUE_DESC_OFFSET; 904 905 /* for switch PORT MII0 mac addr is in DRAM0. */ 906 dram_mac = prueth->mem[PRUETH_MEM_DRAM0].va; 907 break; 908 case PRUETH_PORT_MII1: 909 tx_context_ofs_addr = TX_CONTEXT_P2_Q1_OFFSET_ADDR; 910 rx_context_ofs = P2_Q1_RX_CONTEXT_OFFSET; 911 queue_desc_ofs = P2_QUEUE_DESC_OFFSET; 912 913 /* for switch PORT MII1 mac addr is in DRAM1. */ 914 dram_mac = prueth->mem[PRUETH_MEM_DRAM1].va; 915 break; 916 default: 917 netdev_err(emac->ndev, "invalid port\n"); 918 return -EINVAL; 919 } 920 921 /* setup mac address */ 922 memcpy_toio(dram_mac + PORT_MAC_ADDR, emac->mac_addr, 6); 923 924 /* Remaining switch port configs are in DRAM1 */ 925 dram_base = prueth->mem[PRUETH_MEM_DRAM1].va; 926 927 /* queue information table */ 928 memcpy_toio(dram_base + tx_context_ofs_addr, 929 sw_queue_infos[port_id], 930 sizeof(sw_queue_infos[port_id])); 931 932 memcpy_toio(dram_base + rx_context_ofs, 933 rx_queue_infos[port_id], 934 sizeof(rx_queue_infos[port_id])); 935 936 /* buffer descriptor offset table*/ 937 dram = dram_base + QUEUE_DESCRIPTOR_OFFSET_ADDR + 938 (port_id * NUM_QUEUES * sizeof(u16)); 939 writew(sw_queue_infos[port_id][PRUETH_QUEUE1].buffer_desc_offset, dram); 940 writew(sw_queue_infos[port_id][PRUETH_QUEUE2].buffer_desc_offset, 941 dram + 2); 942 writew(sw_queue_infos[port_id][PRUETH_QUEUE3].buffer_desc_offset, 943 dram + 4); 944 writew(sw_queue_infos[port_id][PRUETH_QUEUE4].buffer_desc_offset, 945 dram + 6); 946 947 /* buffer offset table */ 948 dram = dram_base + QUEUE_OFFSET_ADDR + 949 port_id * NUM_QUEUES * sizeof(u16); 950 writew(sw_queue_infos[port_id][PRUETH_QUEUE1].buffer_offset, dram); 951 writew(sw_queue_infos[port_id][PRUETH_QUEUE2].buffer_offset, 952 dram + 2); 953 writew(sw_queue_infos[port_id][PRUETH_QUEUE3].buffer_offset, 954 dram + 4); 955 writew(sw_queue_infos[port_id][PRUETH_QUEUE4].buffer_offset, 956 dram + 6); 957 958 /* queue size lookup table */ 959 dram = dram_base + QUEUE_SIZE_ADDR + 960 port_id * NUM_QUEUES * sizeof(u16); 961 writew(QUEUE_1_SIZE, dram); 962 writew(QUEUE_2_SIZE, dram + 2); 963 writew(QUEUE_3_SIZE, dram + 4); 964 writew(QUEUE_4_SIZE, dram + 6); 965 966 /* queue table */ 967 memcpy_toio(dram_base + queue_desc_ofs, 968 &queue_descs[port_id][0], 969 4 * sizeof(queue_descs[port_id][0])); 970 971 emac->rx_queue_descs = dram1_base + P0_QUEUE_DESC_OFFSET; 972 emac->tx_queue_descs = dram1_base + 973 rx_queue_infos[port_id][PRUETH_QUEUE1].queue_desc_offset; 974 975 return 0; 976 } 977 978 int icssm_prueth_sw_emac_config(struct prueth_emac *emac) 979 { 980 struct prueth *prueth = emac->prueth; 981 u32 sharedramaddr, ocmcaddr; 982 int ret; 983 984 /* PRU needs local shared RAM address for C28 */ 985 sharedramaddr = ICSS_LOCAL_SHARED_RAM; 986 /* PRU needs real global OCMC address for C30*/ 987 ocmcaddr = (u32)prueth->mem[PRUETH_MEM_OCMC].pa; 988 989 if (prueth->emac_configured & BIT(emac->port_id)) 990 return 0; 991 992 ret = icssm_prueth_sw_port_config(prueth, emac->port_id); 993 if (ret) 994 return ret; 995 996 if (!prueth->emac_configured) { 997 /* Set in constant table C28 of PRUn to ICSS Shared memory */ 998 pru_rproc_set_ctable(prueth->pru0, PRU_C28, sharedramaddr); 999 pru_rproc_set_ctable(prueth->pru1, PRU_C28, sharedramaddr); 1000 1001 /* Set in constant table C30 of PRUn to OCMC memory */ 1002 pru_rproc_set_ctable(prueth->pru0, PRU_C30, ocmcaddr); 1003 pru_rproc_set_ctable(prueth->pru1, PRU_C30, ocmcaddr); 1004 } 1005 return 0; 1006 } 1007 1008 int icssm_prueth_sw_boot_prus(struct prueth *prueth, struct net_device *ndev) 1009 { 1010 const struct prueth_firmware *pru_firmwares; 1011 const char *fw_name, *fw_name1; 1012 int ret; 1013 1014 if (prueth->emac_configured) 1015 return 0; 1016 1017 pru_firmwares = &prueth->fw_data->fw_pru[PRUSS_PRU0]; 1018 fw_name = pru_firmwares->fw_name[prueth->eth_type]; 1019 pru_firmwares = &prueth->fw_data->fw_pru[PRUSS_PRU1]; 1020 fw_name1 = pru_firmwares->fw_name[prueth->eth_type]; 1021 1022 ret = rproc_set_firmware(prueth->pru0, fw_name); 1023 if (ret) { 1024 netdev_err(ndev, "failed to set PRU0 firmware %s: %d\n", 1025 fw_name, ret); 1026 return ret; 1027 } 1028 ret = rproc_boot(prueth->pru0); 1029 if (ret) { 1030 netdev_err(ndev, "failed to boot PRU0: %d\n", ret); 1031 return ret; 1032 } 1033 1034 ret = rproc_set_firmware(prueth->pru1, fw_name1); 1035 if (ret) { 1036 netdev_err(ndev, "failed to set PRU1 firmware %s: %d\n", 1037 fw_name1, ret); 1038 goto rproc0_shutdown; 1039 } 1040 ret = rproc_boot(prueth->pru1); 1041 if (ret) { 1042 netdev_err(ndev, "failed to boot PRU1: %d\n", ret); 1043 goto rproc0_shutdown; 1044 } 1045 1046 return 0; 1047 1048 rproc0_shutdown: 1049 rproc_shutdown(prueth->pru0); 1050 return ret; 1051 } 1052 1053 int icssm_prueth_sw_shutdown_prus(struct prueth_emac *emac, 1054 struct net_device *ndev) 1055 { 1056 struct prueth *prueth = emac->prueth; 1057 1058 if (prueth->emac_configured) 1059 return 0; 1060 1061 rproc_shutdown(prueth->pru0); 1062 rproc_shutdown(prueth->pru1); 1063 1064 return 0; 1065 } 1066