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

net: dsa: rzn1-a5psw: add FDB support

This commits add forwarding database support to the driver. It
implements fdb_add(), fdb_del() and fdb_dump().

Signed-off-by: Clément Léger <clement.leger@bootlin.com>
Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Clément Léger and committed by
David S. Miller
5edf246c c7243fd4

+185
+168
drivers/net/dsa/rzn1_a5psw.c
··· 375 375 a5psw_port_fdb_flush(a5psw, port); 376 376 } 377 377 378 + static int a5psw_lk_execute_lookup(struct a5psw *a5psw, union lk_data *lk_data, 379 + u16 *entry) 380 + { 381 + u32 ctrl; 382 + int ret; 383 + 384 + a5psw_reg_writel(a5psw, A5PSW_LK_DATA_LO, lk_data->lo); 385 + a5psw_reg_writel(a5psw, A5PSW_LK_DATA_HI, lk_data->hi); 386 + 387 + ctrl = A5PSW_LK_ADDR_CTRL_LOOKUP; 388 + ret = a5psw_lk_execute_ctrl(a5psw, &ctrl); 389 + if (ret) 390 + return ret; 391 + 392 + *entry = ctrl & A5PSW_LK_ADDR_CTRL_ADDRESS; 393 + 394 + return 0; 395 + } 396 + 397 + static int a5psw_port_fdb_add(struct dsa_switch *ds, int port, 398 + const unsigned char *addr, u16 vid, 399 + struct dsa_db db) 400 + { 401 + struct a5psw *a5psw = ds->priv; 402 + union lk_data lk_data = {0}; 403 + bool inc_learncount = false; 404 + int ret = 0; 405 + u16 entry; 406 + u32 reg; 407 + 408 + ether_addr_copy(lk_data.entry.mac, addr); 409 + lk_data.entry.port_mask = BIT(port); 410 + 411 + mutex_lock(&a5psw->lk_lock); 412 + 413 + /* Set the value to be written in the lookup table */ 414 + ret = a5psw_lk_execute_lookup(a5psw, &lk_data, &entry); 415 + if (ret) 416 + goto lk_unlock; 417 + 418 + lk_data.hi = a5psw_reg_readl(a5psw, A5PSW_LK_DATA_HI); 419 + if (!lk_data.entry.valid) { 420 + inc_learncount = true; 421 + /* port_mask set to 0x1f when entry is not valid, clear it */ 422 + lk_data.entry.port_mask = 0; 423 + lk_data.entry.prio = 0; 424 + } 425 + 426 + lk_data.entry.port_mask |= BIT(port); 427 + lk_data.entry.is_static = 1; 428 + lk_data.entry.valid = 1; 429 + 430 + a5psw_reg_writel(a5psw, A5PSW_LK_DATA_HI, lk_data.hi); 431 + 432 + reg = A5PSW_LK_ADDR_CTRL_WRITE | entry; 433 + ret = a5psw_lk_execute_ctrl(a5psw, &reg); 434 + if (ret) 435 + goto lk_unlock; 436 + 437 + if (inc_learncount) { 438 + reg = A5PSW_LK_LEARNCOUNT_MODE_INC; 439 + a5psw_reg_writel(a5psw, A5PSW_LK_LEARNCOUNT, reg); 440 + } 441 + 442 + lk_unlock: 443 + mutex_unlock(&a5psw->lk_lock); 444 + 445 + return ret; 446 + } 447 + 448 + static int a5psw_port_fdb_del(struct dsa_switch *ds, int port, 449 + const unsigned char *addr, u16 vid, 450 + struct dsa_db db) 451 + { 452 + struct a5psw *a5psw = ds->priv; 453 + union lk_data lk_data = {0}; 454 + bool clear = false; 455 + u16 entry; 456 + u32 reg; 457 + int ret; 458 + 459 + ether_addr_copy(lk_data.entry.mac, addr); 460 + 461 + mutex_lock(&a5psw->lk_lock); 462 + 463 + ret = a5psw_lk_execute_lookup(a5psw, &lk_data, &entry); 464 + if (ret) 465 + goto lk_unlock; 466 + 467 + lk_data.hi = a5psw_reg_readl(a5psw, A5PSW_LK_DATA_HI); 468 + 469 + /* Our hardware does not associate any VID to the FDB entries so this 470 + * means that if two entries were added for the same mac but for 471 + * different VID, then, on the deletion of the first one, we would also 472 + * delete the second one. Since there is unfortunately nothing we can do 473 + * about that, do not return an error... 474 + */ 475 + if (!lk_data.entry.valid) 476 + goto lk_unlock; 477 + 478 + lk_data.entry.port_mask &= ~BIT(port); 479 + /* If there is no more port in the mask, clear the entry */ 480 + if (lk_data.entry.port_mask == 0) 481 + clear = true; 482 + 483 + a5psw_reg_writel(a5psw, A5PSW_LK_DATA_HI, lk_data.hi); 484 + 485 + reg = entry; 486 + if (clear) 487 + reg |= A5PSW_LK_ADDR_CTRL_CLEAR; 488 + else 489 + reg |= A5PSW_LK_ADDR_CTRL_WRITE; 490 + 491 + ret = a5psw_lk_execute_ctrl(a5psw, &reg); 492 + if (ret) 493 + goto lk_unlock; 494 + 495 + /* Decrement LEARNCOUNT */ 496 + if (clear) { 497 + reg = A5PSW_LK_LEARNCOUNT_MODE_DEC; 498 + a5psw_reg_writel(a5psw, A5PSW_LK_LEARNCOUNT, reg); 499 + } 500 + 501 + lk_unlock: 502 + mutex_unlock(&a5psw->lk_lock); 503 + 504 + return ret; 505 + } 506 + 507 + static int a5psw_port_fdb_dump(struct dsa_switch *ds, int port, 508 + dsa_fdb_dump_cb_t *cb, void *data) 509 + { 510 + struct a5psw *a5psw = ds->priv; 511 + union lk_data lk_data; 512 + int i = 0, ret = 0; 513 + u32 reg; 514 + 515 + mutex_lock(&a5psw->lk_lock); 516 + 517 + for (i = 0; i < A5PSW_TABLE_ENTRIES; i++) { 518 + reg = A5PSW_LK_ADDR_CTRL_READ | A5PSW_LK_ADDR_CTRL_WAIT | i; 519 + 520 + ret = a5psw_lk_execute_ctrl(a5psw, &reg); 521 + if (ret) 522 + goto out_unlock; 523 + 524 + lk_data.hi = a5psw_reg_readl(a5psw, A5PSW_LK_DATA_HI); 525 + /* If entry is not valid or does not contain the port, skip */ 526 + if (!lk_data.entry.valid || 527 + !(lk_data.entry.port_mask & BIT(port))) 528 + continue; 529 + 530 + lk_data.lo = a5psw_reg_readl(a5psw, A5PSW_LK_DATA_LO); 531 + 532 + ret = cb(lk_data.entry.mac, 0, lk_data.entry.is_static, data); 533 + if (ret) 534 + goto out_unlock; 535 + } 536 + 537 + out_unlock: 538 + mutex_unlock(&a5psw->lk_lock); 539 + 540 + return ret; 541 + } 542 + 378 543 static u64 a5psw_read_stat(struct a5psw *a5psw, u32 offset, int port) 379 544 { 380 545 u32 reg_lo, reg_hi; ··· 756 591 .port_bridge_leave = a5psw_port_bridge_leave, 757 592 .port_stp_state_set = a5psw_port_stp_state_set, 758 593 .port_fast_age = a5psw_port_fast_age, 594 + .port_fdb_add = a5psw_port_fdb_add, 595 + .port_fdb_del = a5psw_port_fdb_del, 596 + .port_fdb_dump = a5psw_port_fdb_dump, 759 597 }; 760 598 761 599 static int a5psw_mdio_wait_busy(struct a5psw *a5psw)
+17
drivers/net/dsa/rzn1_a5psw.h
··· 211 211 #define A5PSW_CTRL_TIMEOUT 1000 212 212 #define A5PSW_TABLE_ENTRIES 8192 213 213 214 + struct fdb_entry { 215 + u8 mac[ETH_ALEN]; 216 + u16 valid:1; 217 + u16 is_static:1; 218 + u16 prio:3; 219 + u16 port_mask:5; 220 + u16 reserved:6; 221 + } __packed; 222 + 223 + union lk_data { 224 + struct { 225 + u32 lo; 226 + u32 hi; 227 + }; 228 + struct fdb_entry entry; 229 + }; 230 + 214 231 /** 215 232 * struct a5psw - switch struct 216 233 * @base: Base address of the switch