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 'introduce-switch-mode-support-for-icssg-driver'

MD Danish Anwar says:

====================
Introduce switch mode support for ICSSG driver

This series adds support for switch-mode for ICSSG driver. This series
also introduces helper APIs to configure firmware maintained FDB
(Forwarding Database) and VLAN tables. These APIs are later used by ICSSG
driver in switch mode.

Now the driver will boot by default in dual EMAC mode. When first ICSSG
interface is added to bridge driver will still be in EMAC mode. As soon as
second ICSSG interface is added to same bridge, switch-mode will be
enabled and switch firmwares will be loaded to PRU cores. The driver will
remain in dual EMAC mode if ICSSG interfaces are added to two different
bridges or if two different interfaces (One ICSSG, one other) is added to
the same bridge. We'll only enable is_switch_mode flag when two ICSSG
interfaces are added to same bridge.

We start in dual MAC mode. Let's say lan0 and lan1 are ICSSG interfaces

ip link add name br0 type bridge
ip link set lan0 master br0

At this point, we get a CHANGEUPPER event. Only one port is a member of
the bridge, so we will still be in dual MAC mode.

ip link set lan1 master br0

We get a second CHANGEUPPER event, the second interface lan1 is also ICSSG
interface so we will set the is_switch_mode flag and when interfaces are
brought up again, ICSSG switch firmwares will be loaded to PRU Cores.

There are some other cases to consider as well.

ip link add name br0 type bridge
ip link add name br1 type bridge

ip link set lan0 master br0
ip link set ppp0 master br0

Here we are adding lan0 (ICSSG) and ppp0 (non ICSSG) to same bridge, as
they both are not ICSSG, we will still be running in dual EMAC mode.

ip link set lan1 master br1
ip link set vpn0 master br1

Here we are adding lan1 (ICSSG) and vpn0 (non ICSSG) to same bridge, as
they both are not ICSSG, we will still be running in dual EMAC mode.

This is v6 of the series.

Changes from v5 to v6:
*) Removed __packed from structures in icssg_config.h file.
*) Added RB tags of Andrew Lunn <andrew@lunn.ch> to patch 2/3 and patch
3/3 of this series.

Changes from v4 to v5:
*) Rebased on 6.10-rc1.
*) Dropped the RFC tag.

Changes from v3 to v4:
*) Added RFC tag as net-next is closed now.
*) Modified the driver to remove the need of bringing interfaces up / down
for enabling / disabling switch mode. Now switch mode can be enabled
without bringig interfaces up / down as requested by Andrew Lunn
<andrew@lunn.ch>
*) Modified commit message of patch 3/3.

Changes from v2 to v3:
*) Dropped RFC tag.
*) Used ether_addr_copy() instead of manually copying mac address using
for loop in patch 1/3 as suggested by Andrew Lunn <andrew@lunn.ch>
*) Added helper API icssg_fdb_setup() in patch 1/3 to reduce code
duplication as suggested by Andrew Lunn <andrew@lunn.ch>
*) In prueth_switchdev_stp_state_set() removed BR_STATE_LEARNING as
learning without forwarding is not supported by ICSSG firmware.
*) Used ether_addr_equal() wherever possible in patch 2/3 as suggested
by Andrew Lunn <andrew@lunn.ch>
*) Fixed typo "nit: s/prueth_switchdevice_nb/prueth_switchdev_nb/" in
patch 2/3 as suggested by Simon Horman <horms@kernel.org>
*) Squashed "#include "icssg_mii_rt.h" to patch 2/3 from patch 3/3 as
suggested by Simon Horman <horms@kernel.org>
*) Rebased on latest net-next/main.

Changes from v1 to v2:
*) Removed TAPRIO support patch from this series.
*) Stopped using devlink for enabling switch-mode as suggested by Andrew L
*) Added read_poll_timeout() in patch 1 / 3 as suggested by Andrew L.

v1 https://lore.kernel.org/all/20230830110847.1219515-4-danishanwar@ti.com/
v2 https://lore.kernel.org/all/20240118071005.1514498-1-danishanwar@ti.com/
v3 https://lore.kernel.org/all/20240327114054.1907278-1-danishanwar@ti.com/
v4 https://lore.kernel.org/all/20240515060320.2783244-1-danishanwar@ti.com/
v5 https://lore.kernel.org/all/20240527052738.152821-1-danishanwar@ti.com/

Thanks and Regards,
Md Danish Anwar
====================

Link: https://lore.kernel.org/r/20240528113734.379422-1-danishanwar@ti.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>

+1115 -21
+1
drivers/net/ethernet/ti/Kconfig
··· 204 204 select TI_ICSS_IEP 205 205 select TI_K3_CPPI_DESC_POOL 206 206 depends on PRU_REMOTEPROC 207 + depends on NET_SWITCHDEV 207 208 depends on ARCH_K3 && OF && TI_K3_UDMA_GLUE_LAYER 208 209 help 209 210 Support dual Gigabit Ethernet ports over the ICSSG PRU Subsystem.
+2 -1
drivers/net/ethernet/ti/Makefile
··· 39 39 icssg/icssg_config.o \ 40 40 icssg/icssg_mii_cfg.o \ 41 41 icssg/icssg_stats.o \ 42 - icssg/icssg_ethtool.o 42 + icssg/icssg_ethtool.o \ 43 + icssg/icssg_switchdev.o 43 44 obj-$(CONFIG_TI_ICSSG_PRUETH_SR1) += icssg-prueth-sr1.o 44 45 icssg-prueth-sr1-y := icssg/icssg_prueth_sr1.o \ 45 46 icssg/icssg_common.o \
+1 -1
drivers/net/ethernet/ti/icssg/icssg_classifier.c
··· 455 455 { 456 456 const u8 mask_addr[] = { 0, 0, 0, 0, 0, 0, }; 457 457 458 - rx_class_ft1_set_start_len(miig_rt, slice, 0, 6); 458 + rx_class_ft1_set_start_len(miig_rt, slice, 6, 6); 459 459 rx_class_ft1_set_da(miig_rt, slice, 0, mac_addr); 460 460 rx_class_ft1_set_da_mask(miig_rt, slice, 0, mask_addr); 461 461 rx_class_ft1_cfg_set_type(miig_rt, slice, 0, FT1_CFG_TYPE_EQ);
+2
drivers/net/ethernet/ti/icssg/icssg_common.c
··· 581 581 } else { 582 582 /* send the filled skb up the n/w stack */ 583 583 skb_put(skb, pkt_len); 584 + if (emac->prueth->is_switch_mode) 585 + skb->offload_fwd_mark = emac->offload_fwd_mark; 584 586 skb->protocol = eth_type_trans(skb, ndev); 585 587 napi_gro_receive(&emac->napi_rx, skb); 586 588 ndev->stats.rx_bytes += pkt_len;
+310 -16
drivers/net/ethernet/ti/icssg/icssg_config.c
··· 107 107 }, 108 108 }; 109 109 110 - static void icssg_config_mii_init(struct prueth_emac *emac) 110 + static void icssg_config_mii_init_switch(struct prueth_emac *emac) 111 111 { 112 - u32 rxcfg, txcfg, rxcfg_reg, txcfg_reg, pcnt_reg; 113 112 struct prueth *prueth = emac->prueth; 114 - int slice = prueth_emac_slice(emac); 113 + int mii = prueth_emac_slice(emac); 114 + u32 txcfg_reg, pcnt_reg, txcfg; 115 115 struct regmap *mii_rt; 116 116 117 117 mii_rt = prueth->mii_rt; 118 118 119 - rxcfg_reg = (slice == ICSS_MII0) ? PRUSS_MII_RT_RXCFG0 : 120 - PRUSS_MII_RT_RXCFG1; 119 + txcfg_reg = (mii == ICSS_MII0) ? PRUSS_MII_RT_TXCFG0 : 120 + PRUSS_MII_RT_TXCFG1; 121 + pcnt_reg = (mii == ICSS_MII0) ? PRUSS_MII_RT_RX_PCNT0 : 122 + PRUSS_MII_RT_RX_PCNT1; 123 + 124 + txcfg = PRUSS_MII_RT_TXCFG_TX_ENABLE | 125 + PRUSS_MII_RT_TXCFG_TX_AUTO_PREAMBLE | 126 + PRUSS_MII_RT_TXCFG_TX_IPG_WIRE_CLK_EN; 127 + 128 + if (emac->phy_if == PHY_INTERFACE_MODE_MII && mii == ICSS_MII1) 129 + txcfg |= PRUSS_MII_RT_TXCFG_TX_MUX_SEL; 130 + else if (emac->phy_if != PHY_INTERFACE_MODE_MII && mii == ICSS_MII0) 131 + txcfg |= PRUSS_MII_RT_TXCFG_TX_MUX_SEL; 132 + 133 + regmap_write(mii_rt, txcfg_reg, txcfg); 134 + regmap_write(mii_rt, pcnt_reg, 0x1); 135 + } 136 + 137 + static void icssg_config_mii_init(struct prueth_emac *emac) 138 + { 139 + struct prueth *prueth = emac->prueth; 140 + int slice = prueth_emac_slice(emac); 141 + u32 txcfg, txcfg_reg, pcnt_reg; 142 + struct regmap *mii_rt; 143 + 144 + mii_rt = prueth->mii_rt; 145 + 121 146 txcfg_reg = (slice == ICSS_MII0) ? PRUSS_MII_RT_TXCFG0 : 122 147 PRUSS_MII_RT_TXCFG1; 123 148 pcnt_reg = (slice == ICSS_MII0) ? PRUSS_MII_RT_RX_PCNT0 : 124 149 PRUSS_MII_RT_RX_PCNT1; 125 150 126 - rxcfg = MII_RXCFG_DEFAULT; 127 151 txcfg = MII_TXCFG_DEFAULT; 128 - 129 - if (slice == ICSS_MII1) 130 - rxcfg |= PRUSS_MII_RT_RXCFG_RX_MUX_SEL; 131 152 132 153 /* In MII mode TX lines swapped inside ICSSG, so TX_MUX_SEL cfg need 133 154 * to be swapped also comparing to RGMII mode. ··· 158 137 else if (emac->phy_if != PHY_INTERFACE_MODE_MII && slice == ICSS_MII1) 159 138 txcfg |= PRUSS_MII_RT_TXCFG_TX_MUX_SEL; 160 139 161 - regmap_write(mii_rt, rxcfg_reg, rxcfg); 162 140 regmap_write(mii_rt, txcfg_reg, txcfg); 163 141 regmap_write(mii_rt, pcnt_reg, 0x1); 164 142 } ··· 277 257 return 1; 278 258 } 279 259 260 + static int prueth_switch_buffer_setup(struct prueth_emac *emac) 261 + { 262 + struct icssg_buffer_pool_cfg __iomem *bpool_cfg; 263 + struct icssg_rxq_ctx __iomem *rxq_ctx; 264 + struct prueth *prueth = emac->prueth; 265 + int slice = prueth_emac_slice(emac); 266 + u32 addr; 267 + int i; 268 + 269 + addr = lower_32_bits(prueth->msmcram.pa); 270 + if (slice) 271 + addr += PRUETH_NUM_BUF_POOLS * PRUETH_EMAC_BUF_POOL_SIZE; 272 + 273 + if (addr % SZ_64K) { 274 + dev_warn(prueth->dev, "buffer pool needs to be 64KB aligned\n"); 275 + return -EINVAL; 276 + } 277 + 278 + bpool_cfg = emac->dram.va + BUFFER_POOL_0_ADDR_OFFSET; 279 + /* workaround for f/w bug. bpool 0 needs to be initialized */ 280 + for (i = 0; i < PRUETH_NUM_BUF_POOLS; i++) { 281 + writel(addr, &bpool_cfg[i].addr); 282 + writel(PRUETH_EMAC_BUF_POOL_SIZE, &bpool_cfg[i].len); 283 + addr += PRUETH_EMAC_BUF_POOL_SIZE; 284 + } 285 + 286 + if (!slice) 287 + addr += PRUETH_NUM_BUF_POOLS * PRUETH_EMAC_BUF_POOL_SIZE; 288 + else 289 + addr += PRUETH_SW_NUM_BUF_POOLS_HOST * PRUETH_SW_BUF_POOL_SIZE_HOST; 290 + 291 + for (i = PRUETH_NUM_BUF_POOLS; 292 + i < 2 * PRUETH_SW_NUM_BUF_POOLS_HOST + PRUETH_NUM_BUF_POOLS; 293 + i++) { 294 + /* The driver only uses first 4 queues per PRU so only initialize them */ 295 + if (i % PRUETH_SW_NUM_BUF_POOLS_HOST < PRUETH_SW_NUM_BUF_POOLS_PER_PRU) { 296 + writel(addr, &bpool_cfg[i].addr); 297 + writel(PRUETH_SW_BUF_POOL_SIZE_HOST, &bpool_cfg[i].len); 298 + addr += PRUETH_SW_BUF_POOL_SIZE_HOST; 299 + } else { 300 + writel(0, &bpool_cfg[i].addr); 301 + writel(0, &bpool_cfg[i].len); 302 + } 303 + } 304 + 305 + if (!slice) 306 + addr += PRUETH_SW_NUM_BUF_POOLS_HOST * PRUETH_SW_BUF_POOL_SIZE_HOST; 307 + else 308 + addr += PRUETH_EMAC_RX_CTX_BUF_SIZE; 309 + 310 + rxq_ctx = emac->dram.va + HOST_RX_Q_PRE_CONTEXT_OFFSET; 311 + for (i = 0; i < 3; i++) 312 + writel(addr, &rxq_ctx->start[i]); 313 + 314 + addr += PRUETH_EMAC_RX_CTX_BUF_SIZE; 315 + writel(addr - SZ_2K, &rxq_ctx->end); 316 + 317 + return 0; 318 + } 319 + 280 320 static int prueth_emac_buffer_setup(struct prueth_emac *emac) 281 321 { 282 322 struct icssg_buffer_pool_cfg __iomem *bpool_cfg; ··· 401 321 /* When the device is configured as a bridge and it is being brought 402 322 * back to the emac mode, the host mac address has to be set as 0. 403 323 */ 324 + u32 addr = prueth->shram.pa + EMAC_ICSSG_SWITCH_DEFAULT_VLAN_TABLE_OFFSET; 325 + int i; 404 326 u8 mac[ETH_ALEN] = { 0 }; 405 327 406 328 if (prueth->emacs_initialized) 407 329 return; 408 330 409 - regmap_update_bits(prueth->miig_rt, FDB_GEN_CFG1, 410 - SMEM_VLAN_OFFSET_MASK, 0); 411 - regmap_write(prueth->miig_rt, FDB_GEN_CFG2, 0); 331 + /* Set VLAN TABLE address base */ 332 + regmap_update_bits(prueth->miig_rt, FDB_GEN_CFG1, SMEM_VLAN_OFFSET_MASK, 333 + addr << SMEM_VLAN_OFFSET); 334 + /* Set enable VLAN aware mode, and FDBs for all PRUs */ 335 + regmap_write(prueth->miig_rt, FDB_GEN_CFG2, (FDB_PRU0_EN | FDB_PRU1_EN | FDB_HOST_EN)); 336 + prueth->vlan_tbl = (struct prueth_vlan_tbl __force *)(prueth->shram.va + 337 + EMAC_ICSSG_SWITCH_DEFAULT_VLAN_TABLE_OFFSET); 338 + for (i = 0; i < SZ_4K - 1; i++) { 339 + prueth->vlan_tbl[i].fid = i; 340 + prueth->vlan_tbl[i].fid_c1 = 0; 341 + } 412 342 /* Clear host MAC address */ 413 343 icssg_class_set_host_mac_addr(prueth->miig_rt, mac); 344 + } 345 + 346 + static void icssg_init_switch_mode(struct prueth *prueth) 347 + { 348 + u32 addr = prueth->shram.pa + EMAC_ICSSG_SWITCH_DEFAULT_VLAN_TABLE_OFFSET; 349 + int i; 350 + 351 + if (prueth->emacs_initialized) 352 + return; 353 + 354 + /* Set VLAN TABLE address base */ 355 + regmap_update_bits(prueth->miig_rt, FDB_GEN_CFG1, SMEM_VLAN_OFFSET_MASK, 356 + addr << SMEM_VLAN_OFFSET); 357 + /* Set enable VLAN aware mode, and FDBs for all PRUs */ 358 + regmap_write(prueth->miig_rt, FDB_GEN_CFG2, FDB_EN_ALL); 359 + prueth->vlan_tbl = (struct prueth_vlan_tbl __force *)(prueth->shram.va + 360 + EMAC_ICSSG_SWITCH_DEFAULT_VLAN_TABLE_OFFSET); 361 + for (i = 0; i < SZ_4K - 1; i++) { 362 + prueth->vlan_tbl[i].fid = i; 363 + prueth->vlan_tbl[i].fid_c1 = 0; 364 + } 365 + 366 + if (prueth->hw_bridge_dev) 367 + icssg_class_set_host_mac_addr(prueth->miig_rt, prueth->hw_bridge_dev->dev_addr); 368 + icssg_set_pvid(prueth, prueth->default_vlan, PRUETH_PORT_HOST); 414 369 } 415 370 416 371 int icssg_config(struct prueth *prueth, struct prueth_emac *emac, int slice) ··· 454 339 struct icssg_flow_cfg __iomem *flow_cfg; 455 340 int ret; 456 341 457 - icssg_init_emac_mode(prueth); 342 + if (prueth->is_switch_mode) 343 + icssg_init_switch_mode(prueth); 344 + else 345 + icssg_init_emac_mode(prueth); 458 346 459 347 memset_io(config, 0, TAS_GATE_MASK_LIST0); 460 348 icssg_miig_queues_init(prueth, slice); ··· 471 353 regmap_update_bits(prueth->miig_rt, ICSSG_CFG_OFFSET, 472 354 ICSSG_CFG_DEFAULT, ICSSG_CFG_DEFAULT); 473 355 icssg_miig_set_interface_mode(prueth->miig_rt, slice, emac->phy_if); 474 - icssg_config_mii_init(emac); 356 + if (prueth->is_switch_mode) 357 + icssg_config_mii_init_switch(emac); 358 + else 359 + icssg_config_mii_init(emac); 475 360 icssg_config_ipg(emac); 476 361 icssg_update_rgmii_cfg(prueth->miig_rt, emac); 477 362 ··· 497 376 writeb(0, config + SPL_PKT_DEFAULT_PRIORITY); 498 377 writeb(0, config + QUEUE_NUM_UNTAGGED); 499 378 500 - ret = prueth_emac_buffer_setup(emac); 379 + if (prueth->is_switch_mode) 380 + ret = prueth_switch_buffer_setup(emac); 381 + else 382 + ret = prueth_emac_buffer_setup(emac); 501 383 if (ret) 502 384 return ret; 503 385 ··· 600 476 fw_speed |= FW_LINK_SPEED_HD; 601 477 602 478 writeb(fw_speed, emac->dram.va + PORT_LINK_SPEED_OFFSET); 479 + } 480 + 481 + int icssg_send_fdb_msg(struct prueth_emac *emac, struct mgmt_cmd *cmd, 482 + struct mgmt_cmd_rsp *rsp) 483 + { 484 + struct prueth *prueth = emac->prueth; 485 + int slice = prueth_emac_slice(emac); 486 + int addr, ret; 487 + 488 + addr = icssg_queue_pop(prueth, slice == 0 ? 489 + ICSSG_CMD_POP_SLICE0 : ICSSG_CMD_POP_SLICE1); 490 + if (addr < 0) 491 + return addr; 492 + 493 + /* First 4 bytes have FW owned buffer linking info which should 494 + * not be touched 495 + */ 496 + memcpy_toio(prueth->shram.va + addr + 4, cmd, sizeof(*cmd)); 497 + icssg_queue_push(prueth, slice == 0 ? 498 + ICSSG_CMD_PUSH_SLICE0 : ICSSG_CMD_PUSH_SLICE1, addr); 499 + ret = read_poll_timeout(icssg_queue_pop, addr, addr >= 0, 500 + 2000, 20000000, false, prueth, slice == 0 ? 501 + ICSSG_RSP_POP_SLICE0 : ICSSG_RSP_POP_SLICE1); 502 + if (ret) { 503 + netdev_err(emac->ndev, "Timedout sending HWQ message\n"); 504 + return ret; 505 + } 506 + 507 + memcpy_fromio(rsp, prueth->shram.va + addr, sizeof(*rsp)); 508 + /* Return buffer back for to pool */ 509 + icssg_queue_push(prueth, slice == 0 ? 510 + ICSSG_RSP_PUSH_SLICE0 : ICSSG_RSP_PUSH_SLICE1, addr); 511 + 512 + return 0; 513 + } 514 + 515 + static void icssg_fdb_setup(struct prueth_emac *emac, struct mgmt_cmd *fdb_cmd, 516 + const unsigned char *addr, u8 fid, int cmd) 517 + { 518 + int slice = prueth_emac_slice(emac); 519 + u8 mac_fid[ETH_ALEN + 2]; 520 + u16 fdb_slot; 521 + 522 + ether_addr_copy(mac_fid, addr); 523 + 524 + /* 1-1 VID-FID mapping is already setup */ 525 + mac_fid[ETH_ALEN] = fid; 526 + mac_fid[ETH_ALEN + 1] = 0; 527 + 528 + fdb_slot = bitrev32(crc32_le(0, mac_fid, 8)) & PRUETH_SWITCH_FDB_MASK; 529 + 530 + fdb_cmd->header = ICSSG_FW_MGMT_CMD_HEADER; 531 + fdb_cmd->type = ICSSG_FW_MGMT_FDB_CMD_TYPE; 532 + fdb_cmd->seqnum = ++(emac->prueth->icssg_hwcmdseq); 533 + fdb_cmd->param = cmd; 534 + fdb_cmd->param |= (slice << 4); 535 + 536 + memcpy(&fdb_cmd->cmd_args[0], addr, 4); 537 + memcpy(&fdb_cmd->cmd_args[1], &addr[4], 2); 538 + fdb_cmd->cmd_args[2] = fdb_slot; 539 + 540 + netdev_dbg(emac->ndev, "MAC %pM slot %X FID %X\n", addr, fdb_slot, fid); 541 + } 542 + 543 + int icssg_fdb_add_del(struct prueth_emac *emac, const unsigned char *addr, 544 + u8 vid, u8 fid_c2, bool add) 545 + { 546 + struct mgmt_cmd_rsp fdb_cmd_rsp = { 0 }; 547 + struct mgmt_cmd fdb_cmd = { 0 }; 548 + u8 fid = vid; 549 + int ret; 550 + 551 + icssg_fdb_setup(emac, &fdb_cmd, addr, fid, add ? ICSS_CMD_ADD_FDB : ICSS_CMD_DEL_FDB); 552 + 553 + fid_c2 |= ICSSG_FDB_ENTRY_VALID; 554 + fdb_cmd.cmd_args[1] |= ((fid << 16) | (fid_c2 << 24)); 555 + 556 + ret = icssg_send_fdb_msg(emac, &fdb_cmd, &fdb_cmd_rsp); 557 + if (ret) 558 + return ret; 559 + 560 + WARN_ON(fdb_cmd.seqnum != fdb_cmd_rsp.seqnum); 561 + if (fdb_cmd_rsp.status == 1) 562 + return 0; 563 + 564 + return -EINVAL; 565 + } 566 + 567 + int icssg_fdb_lookup(struct prueth_emac *emac, const unsigned char *addr, 568 + u8 vid) 569 + { 570 + struct mgmt_cmd_rsp fdb_cmd_rsp = { 0 }; 571 + struct mgmt_cmd fdb_cmd = { 0 }; 572 + struct prueth_fdb_slot *slot; 573 + u8 fid = vid; 574 + int ret, i; 575 + 576 + icssg_fdb_setup(emac, &fdb_cmd, addr, fid, ICSS_CMD_GET_FDB_SLOT); 577 + 578 + fdb_cmd.cmd_args[1] |= fid << 16; 579 + 580 + ret = icssg_send_fdb_msg(emac, &fdb_cmd, &fdb_cmd_rsp); 581 + if (ret) 582 + return ret; 583 + 584 + WARN_ON(fdb_cmd.seqnum != fdb_cmd_rsp.seqnum); 585 + 586 + slot = (struct prueth_fdb_slot __force *)(emac->dram.va + FDB_CMD_BUFFER); 587 + for (i = 0; i < 4; i++) { 588 + if (ether_addr_equal(addr, slot->mac) && vid == slot->fid) 589 + return (slot->fid_c2 & ~ICSSG_FDB_ENTRY_VALID); 590 + slot++; 591 + } 592 + 593 + return 0; 594 + } 595 + 596 + void icssg_vtbl_modify(struct prueth_emac *emac, u8 vid, u8 port_mask, 597 + u8 untag_mask, bool add) 598 + { 599 + struct prueth *prueth = emac->prueth; 600 + struct prueth_vlan_tbl *tbl; 601 + u8 fid_c1; 602 + 603 + tbl = prueth->vlan_tbl; 604 + fid_c1 = tbl[vid].fid_c1; 605 + 606 + /* FID_C1: bit0..2 port membership mask, 607 + * bit3..5 tagging mask for each port 608 + * bit6 Stream VID (not handled currently) 609 + * bit7 MC flood (not handled currently) 610 + */ 611 + if (add) { 612 + fid_c1 |= (port_mask | port_mask << 3); 613 + fid_c1 &= ~(untag_mask << 3); 614 + } else { 615 + fid_c1 &= ~(port_mask | port_mask << 3); 616 + } 617 + 618 + tbl[vid].fid_c1 = fid_c1; 619 + } 620 + 621 + u16 icssg_get_pvid(struct prueth_emac *emac) 622 + { 623 + struct prueth *prueth = emac->prueth; 624 + u32 pvid; 625 + 626 + if (emac->port_id == PRUETH_PORT_MII0) 627 + pvid = readl(prueth->shram.va + EMAC_ICSSG_SWITCH_PORT1_DEFAULT_VLAN_OFFSET); 628 + else 629 + pvid = readl(prueth->shram.va + EMAC_ICSSG_SWITCH_PORT2_DEFAULT_VLAN_OFFSET); 630 + 631 + pvid = pvid >> 24; 632 + 633 + return pvid; 634 + } 635 + 636 + void icssg_set_pvid(struct prueth *prueth, u8 vid, u8 port) 637 + { 638 + u32 pvid; 639 + 640 + /* only 256 VLANs are supported */ 641 + pvid = (u32 __force)cpu_to_be32((ETH_P_8021Q << 16) | (vid & 0xff)); 642 + 643 + if (port == PRUETH_PORT_MII0) 644 + writel(pvid, prueth->shram.va + EMAC_ICSSG_SWITCH_PORT1_DEFAULT_VLAN_OFFSET); 645 + else if (port == PRUETH_PORT_MII1) 646 + writel(pvid, prueth->shram.va + EMAC_ICSSG_SWITCH_PORT2_DEFAULT_VLAN_OFFSET); 647 + else 648 + writel(pvid, prueth->shram.va + EMAC_ICSSG_SWITCH_PORT0_DEFAULT_VLAN_OFFSET); 603 649 }
+26
drivers/net/ethernet/ti/icssg/icssg_config.h
··· 35 35 (2 * (PRUETH_EMAC_BUF_POOL_SIZE * PRUETH_NUM_BUF_POOLS + \ 36 36 PRUETH_EMAC_RX_CTX_BUF_SIZE * 2)) 37 37 38 + #define PRUETH_SW_BUF_POOL_SIZE_HOST SZ_4K 39 + #define PRUETH_SW_NUM_BUF_POOLS_HOST 8 40 + #define PRUETH_SW_NUM_BUF_POOLS_PER_PRU 4 41 + #define MSMC_RAM_SIZE_SWITCH_MODE \ 42 + (MSMC_RAM_SIZE + \ 43 + (2 * PRUETH_SW_BUF_POOL_SIZE_HOST * PRUETH_SW_NUM_BUF_POOLS_HOST)) 44 + 45 + #define PRUETH_SWITCH_FDB_MASK ((SIZE_OF_FDB / NUMBER_OF_FDB_BUCKET_ENTRIES) - 1) 46 + 38 47 struct icssg_rxq_ctx { 39 48 __le32 start[3]; 40 49 __le32 end; ··· 210 201 211 202 #define ICSSG_TS_PUSH_SLICE0 40 212 203 #define ICSSG_TS_PUSH_SLICE1 41 204 + 205 + struct mgmt_cmd { 206 + u8 param; 207 + u8 seqnum; 208 + u8 type; 209 + u8 header; 210 + u32 cmd_args[3]; 211 + }; 212 + 213 + struct mgmt_cmd_rsp { 214 + u32 reserved; 215 + u8 status; 216 + u8 seqnum; 217 + u8 type; 218 + u8 header; 219 + u32 cmd_args[3]; 220 + }; 213 221 214 222 /* FDB FID_C2 flag definitions */ 215 223 /* Indicates host port membership.*/
+247 -3
drivers/net/ethernet/ti/icssg/icssg_prueth.c
··· 27 27 #include <linux/remoteproc/pruss.h> 28 28 #include <linux/regmap.h> 29 29 #include <linux/remoteproc.h> 30 + #include <net/switchdev.h> 30 31 31 32 #include "icssg_prueth.h" 32 33 #include "icssg_mii_rt.h" 34 + #include "icssg_switchdev.h" 33 35 #include "../k3-cppi-desc-pool.h" 34 36 35 37 #define PRUETH_MODULE_DESCRIPTION "PRUSS ICSSG Ethernet driver" 38 + 39 + #define DEFAULT_VID 1 40 + #define DEFAULT_PORT_MASK 1 41 + #define DEFAULT_UNTAG_MASK 1 36 42 37 43 /* CTRLMMR_ICSSG_RGMII_CTRL register bits */ 38 44 #define ICSSG_CTRL_RGMII_ID_MODE BIT(24) ··· 118 112 return IRQ_HANDLED; 119 113 } 120 114 115 + static struct icssg_firmwares icssg_switch_firmwares[] = { 116 + { 117 + .pru = "ti-pruss/am65x-sr2-pru0-prusw-fw.elf", 118 + .rtu = "ti-pruss/am65x-sr2-rtu0-prusw-fw.elf", 119 + .txpru = "ti-pruss/am65x-sr2-txpru0-prusw-fw.elf", 120 + }, 121 + { 122 + .pru = "ti-pruss/am65x-sr2-pru1-prusw-fw.elf", 123 + .rtu = "ti-pruss/am65x-sr2-rtu1-prusw-fw.elf", 124 + .txpru = "ti-pruss/am65x-sr2-txpru1-prusw-fw.elf", 125 + } 126 + }; 127 + 121 128 static struct icssg_firmwares icssg_emac_firmwares[] = { 122 129 { 123 130 .pru = "ti-pruss/am65x-sr2-pru0-prueth-fw.elf", ··· 150 131 struct device *dev = prueth->dev; 151 132 int slice, ret; 152 133 153 - firmwares = icssg_emac_firmwares; 134 + if (prueth->is_switch_mode) 135 + firmwares = icssg_switch_firmwares; 136 + else 137 + firmwares = icssg_emac_firmwares; 154 138 155 139 slice = prueth_emac_slice(emac); 156 140 if (slice < 0) { ··· 467 445 ether_addr_copy(emac->mac_addr, ndev->dev_addr); 468 446 469 447 icssg_class_set_mac_addr(prueth->miig_rt, slice, emac->mac_addr); 470 - icssg_ft1_set_mac_addr(prueth->miig_rt, slice, emac->mac_addr); 471 - 472 448 icssg_class_default(prueth->miig_rt, slice, 0, false); 449 + icssg_ft1_set_mac_addr(prueth->miig_rt, slice, emac->mac_addr); 473 450 474 451 /* Notify the stack of the actual queue counts. */ 475 452 ret = netif_set_real_num_tx_queues(ndev, num_data_chn); ··· 854 833 return ret; 855 834 } 856 835 836 + bool prueth_dev_check(const struct net_device *ndev) 837 + { 838 + if (ndev->netdev_ops == &emac_netdev_ops && netif_running(ndev)) { 839 + struct prueth_emac *emac = netdev_priv(ndev); 840 + 841 + return emac->prueth->is_switch_mode; 842 + } 843 + 844 + return false; 845 + } 846 + 847 + static void prueth_offload_fwd_mark_update(struct prueth *prueth) 848 + { 849 + int set_val = 0; 850 + int i; 851 + 852 + if (prueth->br_members == (BIT(PRUETH_PORT_MII0) | BIT(PRUETH_PORT_MII1))) 853 + set_val = 1; 854 + 855 + dev_dbg(prueth->dev, "set offload_fwd_mark %d\n", set_val); 856 + 857 + for (i = PRUETH_MAC0; i < PRUETH_NUM_MACS; i++) { 858 + struct prueth_emac *emac = prueth->emac[i]; 859 + 860 + if (!emac || !emac->ndev) 861 + continue; 862 + 863 + emac->offload_fwd_mark = set_val; 864 + } 865 + } 866 + 867 + static void prueth_emac_restart(struct prueth *prueth) 868 + { 869 + struct prueth_emac *emac0 = prueth->emac[PRUETH_MAC0]; 870 + struct prueth_emac *emac1 = prueth->emac[PRUETH_MAC1]; 871 + 872 + /* Detach the net_device for both PRUeth ports*/ 873 + if (netif_running(emac0->ndev)) 874 + netif_device_detach(emac0->ndev); 875 + if (netif_running(emac1->ndev)) 876 + netif_device_detach(emac1->ndev); 877 + 878 + /* Disable both PRUeth ports */ 879 + emac_set_port_state(emac0, ICSSG_EMAC_PORT_DISABLE); 880 + emac_set_port_state(emac1, ICSSG_EMAC_PORT_DISABLE); 881 + 882 + /* Stop both pru cores for both PRUeth ports*/ 883 + prueth_emac_stop(emac0); 884 + prueth->emacs_initialized--; 885 + prueth_emac_stop(emac1); 886 + prueth->emacs_initialized--; 887 + 888 + /* Start both pru cores for both PRUeth ports */ 889 + prueth_emac_start(prueth, emac0); 890 + prueth->emacs_initialized++; 891 + prueth_emac_start(prueth, emac1); 892 + prueth->emacs_initialized++; 893 + 894 + /* Enable forwarding for both PRUeth ports */ 895 + emac_set_port_state(emac0, ICSSG_EMAC_PORT_FORWARD); 896 + emac_set_port_state(emac1, ICSSG_EMAC_PORT_FORWARD); 897 + 898 + /* Attache net_device for both PRUeth ports */ 899 + netif_device_attach(emac0->ndev); 900 + netif_device_attach(emac1->ndev); 901 + } 902 + 903 + static void icssg_enable_switch_mode(struct prueth *prueth) 904 + { 905 + struct prueth_emac *emac; 906 + int mac; 907 + 908 + prueth_emac_restart(prueth); 909 + 910 + for (mac = PRUETH_MAC0; mac < PRUETH_NUM_MACS; mac++) { 911 + emac = prueth->emac[mac]; 912 + if (netif_running(emac->ndev)) { 913 + icssg_fdb_add_del(emac, eth_stp_addr, prueth->default_vlan, 914 + ICSSG_FDB_ENTRY_P0_MEMBERSHIP | 915 + ICSSG_FDB_ENTRY_P1_MEMBERSHIP | 916 + ICSSG_FDB_ENTRY_P2_MEMBERSHIP | 917 + ICSSG_FDB_ENTRY_BLOCK, 918 + true); 919 + icssg_vtbl_modify(emac, emac->port_vlan | DEFAULT_VID, 920 + BIT(emac->port_id) | DEFAULT_PORT_MASK, 921 + BIT(emac->port_id) | DEFAULT_UNTAG_MASK, 922 + true); 923 + icssg_set_pvid(prueth, emac->port_vlan, emac->port_id); 924 + emac_set_port_state(emac, ICSSG_EMAC_PORT_VLAN_AWARE_ENABLE); 925 + } 926 + } 927 + } 928 + 929 + static int prueth_netdevice_port_link(struct net_device *ndev, 930 + struct net_device *br_ndev, 931 + struct netlink_ext_ack *extack) 932 + { 933 + struct prueth_emac *emac = netdev_priv(ndev); 934 + struct prueth *prueth = emac->prueth; 935 + int err; 936 + 937 + if (!prueth->br_members) { 938 + prueth->hw_bridge_dev = br_ndev; 939 + } else { 940 + /* This is adding the port to a second bridge, this is 941 + * unsupported 942 + */ 943 + if (prueth->hw_bridge_dev != br_ndev) 944 + return -EOPNOTSUPP; 945 + } 946 + 947 + err = switchdev_bridge_port_offload(br_ndev, ndev, emac, 948 + &prueth->prueth_switchdev_nb, 949 + &prueth->prueth_switchdev_bl_nb, 950 + false, extack); 951 + if (err) 952 + return err; 953 + 954 + prueth->br_members |= BIT(emac->port_id); 955 + 956 + if (!prueth->is_switch_mode) { 957 + if (prueth->br_members & BIT(PRUETH_PORT_MII0) && 958 + prueth->br_members & BIT(PRUETH_PORT_MII1)) { 959 + prueth->is_switch_mode = true; 960 + prueth->default_vlan = 1; 961 + emac->port_vlan = prueth->default_vlan; 962 + icssg_enable_switch_mode(prueth); 963 + } 964 + } 965 + 966 + prueth_offload_fwd_mark_update(prueth); 967 + 968 + return NOTIFY_DONE; 969 + } 970 + 971 + static void prueth_netdevice_port_unlink(struct net_device *ndev) 972 + { 973 + struct prueth_emac *emac = netdev_priv(ndev); 974 + struct prueth *prueth = emac->prueth; 975 + 976 + prueth->br_members &= ~BIT(emac->port_id); 977 + 978 + if (prueth->is_switch_mode) { 979 + prueth->is_switch_mode = false; 980 + emac->port_vlan = 0; 981 + prueth_emac_restart(prueth); 982 + } 983 + 984 + prueth_offload_fwd_mark_update(prueth); 985 + 986 + if (!prueth->br_members) 987 + prueth->hw_bridge_dev = NULL; 988 + } 989 + 990 + /* netdev notifier */ 991 + static int prueth_netdevice_event(struct notifier_block *unused, 992 + unsigned long event, void *ptr) 993 + { 994 + struct netlink_ext_ack *extack = netdev_notifier_info_to_extack(ptr); 995 + struct net_device *ndev = netdev_notifier_info_to_dev(ptr); 996 + struct netdev_notifier_changeupper_info *info; 997 + int ret = NOTIFY_DONE; 998 + 999 + if (ndev->netdev_ops != &emac_netdev_ops) 1000 + return NOTIFY_DONE; 1001 + 1002 + switch (event) { 1003 + case NETDEV_CHANGEUPPER: 1004 + info = ptr; 1005 + 1006 + if (netif_is_bridge_master(info->upper_dev)) { 1007 + if (info->linking) 1008 + ret = prueth_netdevice_port_link(ndev, info->upper_dev, extack); 1009 + else 1010 + prueth_netdevice_port_unlink(ndev); 1011 + } 1012 + break; 1013 + default: 1014 + return NOTIFY_DONE; 1015 + } 1016 + 1017 + return notifier_from_errno(ret); 1018 + } 1019 + 1020 + static int prueth_register_notifiers(struct prueth *prueth) 1021 + { 1022 + int ret = 0; 1023 + 1024 + prueth->prueth_netdevice_nb.notifier_call = &prueth_netdevice_event; 1025 + ret = register_netdevice_notifier(&prueth->prueth_netdevice_nb); 1026 + if (ret) { 1027 + dev_err(prueth->dev, "can't register netdevice notifier\n"); 1028 + return ret; 1029 + } 1030 + 1031 + ret = prueth_switchdev_register_notifiers(prueth); 1032 + if (ret) 1033 + unregister_netdevice_notifier(&prueth->prueth_netdevice_nb); 1034 + 1035 + return ret; 1036 + } 1037 + 1038 + static void prueth_unregister_notifiers(struct prueth *prueth) 1039 + { 1040 + prueth_switchdev_unregister_notifiers(prueth); 1041 + unregister_netdevice_notifier(&prueth->prueth_netdevice_nb); 1042 + } 1043 + 857 1044 static int prueth_probe(struct platform_device *pdev) 858 1045 { 859 1046 struct device_node *eth_node, *eth_ports_node; ··· 1189 960 } 1190 961 1191 962 msmc_ram_size = MSMC_RAM_SIZE; 963 + prueth->is_switchmode_supported = prueth->pdata.switch_mode; 964 + if (prueth->is_switchmode_supported) 965 + msmc_ram_size = MSMC_RAM_SIZE_SWITCH_MODE; 1192 966 1193 967 /* NOTE: FW bug needs buffer base to be 64KB aligned */ 1194 968 prueth->msmcram.va = ··· 1297 1065 phy_attached_info(prueth->emac[PRUETH_MAC1]->ndev->phydev); 1298 1066 } 1299 1067 1068 + if (prueth->is_switchmode_supported) { 1069 + ret = prueth_register_notifiers(prueth); 1070 + if (ret) 1071 + goto netdev_unregister; 1072 + 1073 + sprintf(prueth->switch_id, "%s", dev_name(dev)); 1074 + } 1075 + 1300 1076 dev_info(dev, "TI PRU ethernet driver initialized: %s EMAC mode\n", 1301 1077 (!eth0_node || !eth1_node) ? "single" : "dual"); 1302 1078 ··· 1374 1134 struct device_node *eth_node; 1375 1135 int i; 1376 1136 1137 + prueth_unregister_notifiers(prueth); 1138 + 1377 1139 for (i = 0; i < PRUETH_NUM_MACS; i++) { 1378 1140 if (!prueth->registered_netdevs[i]) 1379 1141 continue; ··· 1417 1175 static const struct prueth_pdata am654_icssg_pdata = { 1418 1176 .fdqring_mode = K3_RINGACC_RING_MODE_MESSAGE, 1419 1177 .quirk_10m_link_issue = 1, 1178 + .switch_mode = 1, 1420 1179 }; 1421 1180 1422 1181 static const struct prueth_pdata am64x_icssg_pdata = { 1423 1182 .fdqring_mode = K3_RINGACC_RING_MODE_RING, 1183 + .switch_mode = 1, 1424 1184 }; 1425 1185 1426 1186 static const struct of_device_id prueth_dt_match[] = {
+36
drivers/net/ethernet/ti/icssg/icssg_prueth.h
··· 186 186 187 187 struct pruss_mem_region dram; 188 188 189 + bool offload_fwd_mark; 190 + int port_vlan; 191 + 189 192 struct delayed_work stats_work; 190 193 u64 stats[ICSSG_NUM_STATS]; 191 194 ··· 201 198 * struct prueth_pdata - PRUeth platform data 202 199 * @fdqring_mode: Free desc queue mode 203 200 * @quirk_10m_link_issue: 10M link detect errata 201 + * @switch_mode: switch firmware support 204 202 */ 205 203 struct prueth_pdata { 206 204 enum k3_ring_mode fdqring_mode; 207 205 u32 quirk_10m_link_issue:1; 206 + u32 switch_mode:1; 208 207 }; 209 208 210 209 struct icssg_firmwares { ··· 237 232 * @emacs_initialized: num of EMACs/ext ports that are up/running 238 233 * @iep0: pointer to IEP0 device 239 234 * @iep1: pointer to IEP1 device 235 + * @vlan_tbl: VLAN-FID table pointer 236 + * @hw_bridge_dev: pointer to HW bridge net device 237 + * @br_members: bitmask of bridge member ports 238 + * @prueth_netdevice_nb: netdevice notifier block 239 + * @prueth_switchdev_nb: switchdev notifier block 240 + * @prueth_switchdev_bl_nb: switchdev blocking notifier block 241 + * @is_switch_mode: flag to indicate if device is in Switch mode 242 + * @is_switchmode_supported: indicates platform support for switch mode 243 + * @switch_id: ID for mapping switch ports to bridge 244 + * @default_vlan: Default VLAN for host 240 245 */ 241 246 struct prueth { 242 247 struct device *dev; ··· 271 256 int emacs_initialized; 272 257 struct icss_iep *iep0; 273 258 struct icss_iep *iep1; 259 + struct prueth_vlan_tbl *vlan_tbl; 260 + 261 + struct net_device *hw_bridge_dev; 262 + u8 br_members; 263 + struct notifier_block prueth_netdevice_nb; 264 + struct notifier_block prueth_switchdev_nb; 265 + struct notifier_block prueth_switchdev_bl_nb; 266 + bool is_switch_mode; 267 + bool is_switchmode_supported; 268 + unsigned char switch_id[MAX_PHYS_ITEM_ID_LEN]; 269 + int default_vlan; 274 270 }; 275 271 276 272 struct emac_tx_ts_response { ··· 339 313 void icssg_queue_push(struct prueth *prueth, int queue, u16 addr); 340 314 u32 icssg_queue_level(struct prueth *prueth, int queue); 341 315 316 + int icssg_send_fdb_msg(struct prueth_emac *emac, struct mgmt_cmd *cmd, 317 + struct mgmt_cmd_rsp *rsp); 318 + int icssg_fdb_add_del(struct prueth_emac *emac, const unsigned char *addr, 319 + u8 vid, u8 fid_c2, bool add); 320 + int icssg_fdb_lookup(struct prueth_emac *emac, const unsigned char *addr, 321 + u8 vid); 322 + void icssg_vtbl_modify(struct prueth_emac *emac, u8 vid, u8 port_mask, 323 + u8 untag_mask, bool add); 324 + u16 icssg_get_pvid(struct prueth_emac *emac); 325 + void icssg_set_pvid(struct prueth *prueth, u8 vid, u8 port); 342 326 #define prueth_napi_to_tx_chn(pnapi) \ 343 327 container_of(pnapi, struct prueth_tx_chn, napi_tx) 344 328
+477
drivers/net/ethernet/ti/icssg/icssg_switchdev.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + 3 + /* Texas Instruments K3 ICSSG Ethernet Switchdev Driver 4 + * 5 + * Copyright (C) 2021 Texas Instruments Incorporated - https://www.ti.com/ 6 + * 7 + */ 8 + 9 + #include <linux/etherdevice.h> 10 + #include <linux/if_bridge.h> 11 + #include <linux/netdevice.h> 12 + #include <linux/workqueue.h> 13 + #include <net/switchdev.h> 14 + 15 + #include "icssg_prueth.h" 16 + #include "icssg_switchdev.h" 17 + #include "icssg_mii_rt.h" 18 + 19 + struct prueth_switchdev_event_work { 20 + struct work_struct work; 21 + struct switchdev_notifier_fdb_info fdb_info; 22 + struct prueth_emac *emac; 23 + unsigned long event; 24 + }; 25 + 26 + static int prueth_switchdev_stp_state_set(struct prueth_emac *emac, 27 + u8 state) 28 + { 29 + enum icssg_port_state_cmd emac_state; 30 + int ret = 0; 31 + 32 + switch (state) { 33 + case BR_STATE_FORWARDING: 34 + emac_state = ICSSG_EMAC_PORT_FORWARD; 35 + break; 36 + case BR_STATE_DISABLED: 37 + emac_state = ICSSG_EMAC_PORT_DISABLE; 38 + break; 39 + case BR_STATE_LISTENING: 40 + case BR_STATE_BLOCKING: 41 + emac_state = ICSSG_EMAC_PORT_BLOCK; 42 + break; 43 + default: 44 + return -EOPNOTSUPP; 45 + } 46 + 47 + emac_set_port_state(emac, emac_state); 48 + netdev_dbg(emac->ndev, "STP state: %u\n", emac_state); 49 + 50 + return ret; 51 + } 52 + 53 + static int prueth_switchdev_attr_br_flags_set(struct prueth_emac *emac, 54 + struct net_device *orig_dev, 55 + struct switchdev_brport_flags brport_flags) 56 + { 57 + enum icssg_port_state_cmd emac_state; 58 + 59 + if (brport_flags.mask & BR_MCAST_FLOOD) 60 + emac_state = ICSSG_EMAC_PORT_MC_FLOODING_ENABLE; 61 + else 62 + emac_state = ICSSG_EMAC_PORT_MC_FLOODING_DISABLE; 63 + 64 + netdev_dbg(emac->ndev, "BR_MCAST_FLOOD: %d port %u\n", 65 + emac_state, emac->port_id); 66 + 67 + emac_set_port_state(emac, emac_state); 68 + 69 + return 0; 70 + } 71 + 72 + static int prueth_switchdev_attr_br_flags_pre_set(struct net_device *netdev, 73 + struct switchdev_brport_flags brport_flags) 74 + { 75 + if (brport_flags.mask & ~(BR_LEARNING | BR_MCAST_FLOOD)) 76 + return -EINVAL; 77 + 78 + return 0; 79 + } 80 + 81 + static int prueth_switchdev_attr_set(struct net_device *ndev, const void *ctx, 82 + const struct switchdev_attr *attr, 83 + struct netlink_ext_ack *extack) 84 + { 85 + struct prueth_emac *emac = netdev_priv(ndev); 86 + int ret; 87 + 88 + netdev_dbg(ndev, "attr: id %u port: %u\n", attr->id, emac->port_id); 89 + 90 + switch (attr->id) { 91 + case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS: 92 + ret = prueth_switchdev_attr_br_flags_pre_set(ndev, 93 + attr->u.brport_flags); 94 + break; 95 + case SWITCHDEV_ATTR_ID_PORT_STP_STATE: 96 + ret = prueth_switchdev_stp_state_set(emac, 97 + attr->u.stp_state); 98 + netdev_dbg(ndev, "stp state: %u\n", attr->u.stp_state); 99 + break; 100 + case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS: 101 + ret = prueth_switchdev_attr_br_flags_set(emac, attr->orig_dev, 102 + attr->u.brport_flags); 103 + break; 104 + default: 105 + ret = -EOPNOTSUPP; 106 + break; 107 + } 108 + 109 + return ret; 110 + } 111 + 112 + static void prueth_switchdev_fdb_offload_notify(struct net_device *ndev, 113 + struct switchdev_notifier_fdb_info *rcv) 114 + { 115 + struct switchdev_notifier_fdb_info info; 116 + 117 + memset(&info, 0, sizeof(info)); 118 + info.addr = rcv->addr; 119 + info.vid = rcv->vid; 120 + info.offloaded = true; 121 + call_switchdev_notifiers(SWITCHDEV_FDB_OFFLOADED, 122 + ndev, &info.info, NULL); 123 + } 124 + 125 + static void prueth_switchdev_event_work(struct work_struct *work) 126 + { 127 + struct prueth_switchdev_event_work *switchdev_work = 128 + container_of(work, struct prueth_switchdev_event_work, work); 129 + struct prueth_emac *emac = switchdev_work->emac; 130 + struct switchdev_notifier_fdb_info *fdb; 131 + int port_id = emac->port_id; 132 + int ret; 133 + 134 + rtnl_lock(); 135 + switch (switchdev_work->event) { 136 + case SWITCHDEV_FDB_ADD_TO_DEVICE: 137 + fdb = &switchdev_work->fdb_info; 138 + 139 + netdev_dbg(emac->ndev, "prueth_fdb_add: MACID = %pM vid = %u flags = %u %u -- port %d\n", 140 + fdb->addr, fdb->vid, fdb->added_by_user, 141 + fdb->offloaded, port_id); 142 + 143 + if (!fdb->added_by_user) 144 + break; 145 + if (!ether_addr_equal(emac->mac_addr, fdb->addr)) 146 + break; 147 + 148 + ret = icssg_fdb_add_del(emac, fdb->addr, fdb->vid, 149 + BIT(port_id), true); 150 + if (!ret) 151 + prueth_switchdev_fdb_offload_notify(emac->ndev, fdb); 152 + break; 153 + case SWITCHDEV_FDB_DEL_TO_DEVICE: 154 + fdb = &switchdev_work->fdb_info; 155 + 156 + netdev_dbg(emac->ndev, "prueth_fdb_del: MACID = %pM vid = %u flags = %u %u -- port %d\n", 157 + fdb->addr, fdb->vid, fdb->added_by_user, 158 + fdb->offloaded, port_id); 159 + 160 + if (!fdb->added_by_user) 161 + break; 162 + if (!ether_addr_equal(emac->mac_addr, fdb->addr)) 163 + break; 164 + icssg_fdb_add_del(emac, fdb->addr, fdb->vid, 165 + BIT(port_id), false); 166 + break; 167 + default: 168 + break; 169 + } 170 + rtnl_unlock(); 171 + 172 + kfree(switchdev_work->fdb_info.addr); 173 + kfree(switchdev_work); 174 + dev_put(emac->ndev); 175 + } 176 + 177 + static int prueth_switchdev_event(struct notifier_block *unused, 178 + unsigned long event, void *ptr) 179 + { 180 + struct net_device *ndev = switchdev_notifier_info_to_dev(ptr); 181 + struct prueth_switchdev_event_work *switchdev_work; 182 + struct switchdev_notifier_fdb_info *fdb_info = ptr; 183 + struct prueth_emac *emac = netdev_priv(ndev); 184 + int err; 185 + 186 + if (!prueth_dev_check(ndev)) 187 + return NOTIFY_DONE; 188 + 189 + if (event == SWITCHDEV_PORT_ATTR_SET) { 190 + err = switchdev_handle_port_attr_set(ndev, ptr, 191 + prueth_dev_check, 192 + prueth_switchdev_attr_set); 193 + return notifier_from_errno(err); 194 + } 195 + 196 + switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC); 197 + if (WARN_ON(!switchdev_work)) 198 + return NOTIFY_BAD; 199 + 200 + INIT_WORK(&switchdev_work->work, prueth_switchdev_event_work); 201 + switchdev_work->emac = emac; 202 + switchdev_work->event = event; 203 + 204 + switch (event) { 205 + case SWITCHDEV_FDB_ADD_TO_DEVICE: 206 + case SWITCHDEV_FDB_DEL_TO_DEVICE: 207 + memcpy(&switchdev_work->fdb_info, ptr, 208 + sizeof(switchdev_work->fdb_info)); 209 + switchdev_work->fdb_info.addr = kzalloc(ETH_ALEN, GFP_ATOMIC); 210 + if (!switchdev_work->fdb_info.addr) 211 + goto err_addr_alloc; 212 + ether_addr_copy((u8 *)switchdev_work->fdb_info.addr, 213 + fdb_info->addr); 214 + dev_hold(ndev); 215 + break; 216 + default: 217 + kfree(switchdev_work); 218 + return NOTIFY_DONE; 219 + } 220 + 221 + queue_work(system_long_wq, &switchdev_work->work); 222 + 223 + return NOTIFY_DONE; 224 + 225 + err_addr_alloc: 226 + kfree(switchdev_work); 227 + return NOTIFY_BAD; 228 + } 229 + 230 + static int prueth_switchdev_vlan_add(struct prueth_emac *emac, bool untag, bool pvid, 231 + u8 vid, struct net_device *orig_dev) 232 + { 233 + bool cpu_port = netif_is_bridge_master(orig_dev); 234 + int untag_mask = 0; 235 + int port_mask; 236 + int ret = 0; 237 + 238 + if (cpu_port) 239 + port_mask = BIT(PRUETH_PORT_HOST); 240 + else 241 + port_mask = BIT(emac->port_id); 242 + 243 + if (untag) 244 + untag_mask = port_mask; 245 + 246 + icssg_vtbl_modify(emac, vid, port_mask, untag_mask, true); 247 + 248 + netdev_dbg(emac->ndev, "VID add vid:%u port_mask:%X untag_mask %X PVID %d\n", 249 + vid, port_mask, untag_mask, pvid); 250 + 251 + if (!pvid) 252 + return ret; 253 + 254 + icssg_set_pvid(emac->prueth, vid, emac->port_id); 255 + 256 + return ret; 257 + } 258 + 259 + static int prueth_switchdev_vlan_del(struct prueth_emac *emac, u16 vid, 260 + struct net_device *orig_dev) 261 + { 262 + bool cpu_port = netif_is_bridge_master(orig_dev); 263 + int port_mask; 264 + int ret = 0; 265 + 266 + if (cpu_port) 267 + port_mask = BIT(PRUETH_PORT_HOST); 268 + else 269 + port_mask = BIT(emac->port_id); 270 + 271 + icssg_vtbl_modify(emac, vid, port_mask, 0, false); 272 + 273 + if (cpu_port) 274 + icssg_fdb_add_del(emac, emac->mac_addr, vid, 275 + BIT(PRUETH_PORT_HOST), false); 276 + 277 + if (vid == icssg_get_pvid(emac)) 278 + icssg_set_pvid(emac->prueth, 0, emac->port_id); 279 + 280 + netdev_dbg(emac->ndev, "VID del vid:%u port_mask:%X\n", 281 + vid, port_mask); 282 + 283 + return ret; 284 + } 285 + 286 + static int prueth_switchdev_vlans_add(struct prueth_emac *emac, 287 + const struct switchdev_obj_port_vlan *vlan) 288 + { 289 + bool untag = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; 290 + struct net_device *orig_dev = vlan->obj.orig_dev; 291 + bool cpu_port = netif_is_bridge_master(orig_dev); 292 + bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; 293 + 294 + netdev_dbg(emac->ndev, "VID add vid:%u flags:%X\n", 295 + vlan->vid, vlan->flags); 296 + 297 + if (cpu_port && !(vlan->flags & BRIDGE_VLAN_INFO_BRENTRY)) 298 + return 0; 299 + 300 + if (vlan->vid > 0xff) 301 + return 0; 302 + 303 + return prueth_switchdev_vlan_add(emac, untag, pvid, vlan->vid, 304 + orig_dev); 305 + } 306 + 307 + static int prueth_switchdev_vlans_del(struct prueth_emac *emac, 308 + const struct switchdev_obj_port_vlan *vlan) 309 + { 310 + if (vlan->vid > 0xff) 311 + return 0; 312 + 313 + return prueth_switchdev_vlan_del(emac, vlan->vid, 314 + vlan->obj.orig_dev); 315 + } 316 + 317 + static int prueth_switchdev_mdb_add(struct prueth_emac *emac, 318 + struct switchdev_obj_port_mdb *mdb) 319 + { 320 + struct net_device *orig_dev = mdb->obj.orig_dev; 321 + u8 port_mask, fid_c2; 322 + bool cpu_port; 323 + int err; 324 + 325 + cpu_port = netif_is_bridge_master(orig_dev); 326 + 327 + if (cpu_port) 328 + port_mask = BIT(PRUETH_PORT_HOST); 329 + else 330 + port_mask = BIT(emac->port_id); 331 + 332 + fid_c2 = icssg_fdb_lookup(emac, mdb->addr, mdb->vid); 333 + 334 + err = icssg_fdb_add_del(emac, mdb->addr, mdb->vid, fid_c2 | port_mask, true); 335 + netdev_dbg(emac->ndev, "MDB add vid %u:%pM ports: %X\n", 336 + mdb->vid, mdb->addr, port_mask); 337 + 338 + return err; 339 + } 340 + 341 + static int prueth_switchdev_mdb_del(struct prueth_emac *emac, 342 + struct switchdev_obj_port_mdb *mdb) 343 + { 344 + struct net_device *orig_dev = mdb->obj.orig_dev; 345 + int del_mask, ret, fid_c2; 346 + bool cpu_port; 347 + 348 + cpu_port = netif_is_bridge_master(orig_dev); 349 + 350 + if (cpu_port) 351 + del_mask = BIT(PRUETH_PORT_HOST); 352 + else 353 + del_mask = BIT(emac->port_id); 354 + 355 + fid_c2 = icssg_fdb_lookup(emac, mdb->addr, mdb->vid); 356 + 357 + if (fid_c2 & ~del_mask) 358 + ret = icssg_fdb_add_del(emac, mdb->addr, mdb->vid, fid_c2 & ~del_mask, true); 359 + else 360 + ret = icssg_fdb_add_del(emac, mdb->addr, mdb->vid, 0, false); 361 + 362 + netdev_dbg(emac->ndev, "MDB del vid %u:%pM ports: %X\n", 363 + mdb->vid, mdb->addr, del_mask); 364 + 365 + return ret; 366 + } 367 + 368 + static int prueth_switchdev_obj_add(struct net_device *ndev, const void *ctx, 369 + const struct switchdev_obj *obj, 370 + struct netlink_ext_ack *extack) 371 + { 372 + struct switchdev_obj_port_vlan *vlan = SWITCHDEV_OBJ_PORT_VLAN(obj); 373 + struct switchdev_obj_port_mdb *mdb = SWITCHDEV_OBJ_PORT_MDB(obj); 374 + struct prueth_emac *emac = netdev_priv(ndev); 375 + int err = 0; 376 + 377 + netdev_dbg(ndev, "obj_add: id %u port: %u\n", obj->id, emac->port_id); 378 + 379 + switch (obj->id) { 380 + case SWITCHDEV_OBJ_ID_PORT_VLAN: 381 + err = prueth_switchdev_vlans_add(emac, vlan); 382 + break; 383 + case SWITCHDEV_OBJ_ID_PORT_MDB: 384 + case SWITCHDEV_OBJ_ID_HOST_MDB: 385 + err = prueth_switchdev_mdb_add(emac, mdb); 386 + break; 387 + default: 388 + err = -EOPNOTSUPP; 389 + break; 390 + } 391 + 392 + return err; 393 + } 394 + 395 + static int prueth_switchdev_obj_del(struct net_device *ndev, const void *ctx, 396 + const struct switchdev_obj *obj) 397 + { 398 + struct switchdev_obj_port_vlan *vlan = SWITCHDEV_OBJ_PORT_VLAN(obj); 399 + struct switchdev_obj_port_mdb *mdb = SWITCHDEV_OBJ_PORT_MDB(obj); 400 + struct prueth_emac *emac = netdev_priv(ndev); 401 + int err = 0; 402 + 403 + netdev_dbg(ndev, "obj_del: id %u port: %u\n", obj->id, emac->port_id); 404 + 405 + switch (obj->id) { 406 + case SWITCHDEV_OBJ_ID_PORT_VLAN: 407 + err = prueth_switchdev_vlans_del(emac, vlan); 408 + break; 409 + case SWITCHDEV_OBJ_ID_PORT_MDB: 410 + case SWITCHDEV_OBJ_ID_HOST_MDB: 411 + err = prueth_switchdev_mdb_del(emac, mdb); 412 + break; 413 + default: 414 + err = -EOPNOTSUPP; 415 + break; 416 + } 417 + 418 + return err; 419 + } 420 + 421 + static int prueth_switchdev_blocking_event(struct notifier_block *unused, 422 + unsigned long event, void *ptr) 423 + { 424 + struct net_device *dev = switchdev_notifier_info_to_dev(ptr); 425 + int err; 426 + 427 + switch (event) { 428 + case SWITCHDEV_PORT_OBJ_ADD: 429 + err = switchdev_handle_port_obj_add(dev, ptr, 430 + prueth_dev_check, 431 + prueth_switchdev_obj_add); 432 + return notifier_from_errno(err); 433 + case SWITCHDEV_PORT_OBJ_DEL: 434 + err = switchdev_handle_port_obj_del(dev, ptr, 435 + prueth_dev_check, 436 + prueth_switchdev_obj_del); 437 + return notifier_from_errno(err); 438 + case SWITCHDEV_PORT_ATTR_SET: 439 + err = switchdev_handle_port_attr_set(dev, ptr, 440 + prueth_dev_check, 441 + prueth_switchdev_attr_set); 442 + return notifier_from_errno(err); 443 + default: 444 + break; 445 + } 446 + 447 + return NOTIFY_DONE; 448 + } 449 + 450 + int prueth_switchdev_register_notifiers(struct prueth *prueth) 451 + { 452 + int ret = 0; 453 + 454 + prueth->prueth_switchdev_nb.notifier_call = &prueth_switchdev_event; 455 + ret = register_switchdev_notifier(&prueth->prueth_switchdev_nb); 456 + if (ret) { 457 + dev_err(prueth->dev, "register switchdev notifier fail ret:%d\n", 458 + ret); 459 + return ret; 460 + } 461 + 462 + prueth->prueth_switchdev_bl_nb.notifier_call = &prueth_switchdev_blocking_event; 463 + ret = register_switchdev_blocking_notifier(&prueth->prueth_switchdev_bl_nb); 464 + if (ret) { 465 + dev_err(prueth->dev, "register switchdev blocking notifier ret:%d\n", 466 + ret); 467 + unregister_switchdev_notifier(&prueth->prueth_switchdev_nb); 468 + } 469 + 470 + return ret; 471 + } 472 + 473 + void prueth_switchdev_unregister_notifiers(struct prueth *prueth) 474 + { 475 + unregister_switchdev_blocking_notifier(&prueth->prueth_switchdev_bl_nb); 476 + unregister_switchdev_notifier(&prueth->prueth_switchdev_nb); 477 + }
+13
drivers/net/ethernet/ti/icssg/icssg_switchdev.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* Copyright (C) 2021 Texas Instruments Incorporated - https://www.ti.com/ 3 + */ 4 + #ifndef __NET_TI_ICSSG_SWITCHDEV_H 5 + #define __NET_TI_ICSSG_SWITCHDEV_H 6 + 7 + #include "icssg_prueth.h" 8 + 9 + int prueth_switchdev_register_notifiers(struct prueth *prueth); 10 + void prueth_switchdev_unregister_notifiers(struct prueth *prueth); 11 + bool prueth_dev_check(const struct net_device *ndev); 12 + 13 + #endif /* __NET_TI_ICSSG_SWITCHDEV_H */