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 'stp-rstp-switch-support-for-pru-icssm-ethernet-driver'

Parvathi Pudi says:

====================
STP/RSTP SWITCH support for PRU-ICSSM Ethernet driver

The DUAL-EMAC patch series for Megabit Industrial Communication Sub-system
(ICSSM), which provides the foundational support for Ethernet functionality
over PRU-ICSS on the TI SOCs (AM335x, AM437x, and AM57x), was merged into
net-next recently [1].

This patch series enhances the PRU-ICSSM Ethernet driver to support bridge
(STP/RSTP) SWITCH mode, which has been implemented using the "switchdev"
framework and interacts with the "mstp daemon" for STP and RSTP management
in userspace.

When the SWITCH mode is enabled, forwarding of Ethernet packets using
either the traditional store-and-forward mechanism or via cut-through is
offloaded to the two PRU based Ethernet interfaces available within the
ICSSM. The firmware running on the PRU inspects the bridge port states and
performs necessary checks before forwarding a packet. This improves the
overall system performance and significantly reduces the packet forwarding
latency.

Protocol switching from Dual-EMAC to bridge (STP/RSTP) SWITCH mode can be
done as follows.

Assuming eth2 and eth3 are the two physical ports of the ICSS2 instance:

>> brctl addbr br0
>> ip maddr add 01:80:c2:00:00:00 dev br0
>> ip link set dev br0 address $(cat /sys/class/net/eth2/address)
>> brctl addif br0 eth2
>> brctl addif br0 eth3
>> mstpd
>> brctl stp br0 on
>> mstpctl setforcevers br0 rstp
>> ip link set dev br0 up

To revert back to the default dual EMAC mode, the steps are as follows:

>> ip link set dev br0 down
>> brctl delif br0 eth2
>> brctl delif br0 eth3
>> brctl delbr br0

The patches presented in this series have gone through the patch verification
tools and no warnings or errors are reported.
====================

Link: https://patch.msgid.link/20260130124559.1182780-1-parvathi@couthit.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

+2291 -23
+1 -1
drivers/net/ethernet/ti/Makefile
··· 4 4 # 5 5 6 6 obj-$(CONFIG_TI_PRUETH) += icssm-prueth.o 7 - icssm-prueth-y := icssm/icssm_prueth.o 7 + icssm-prueth-y := icssm/icssm_prueth.o icssm/icssm_prueth_switch.o icssm/icssm_switchdev.o 8 8 9 9 obj-$(CONFIG_TI_CPSW) += cpsw-common.o 10 10 obj-$(CONFIG_TI_DAVINCI_EMAC) += cpsw-common.o
+524 -21
drivers/net/ethernet/ti/icssm/icssm_prueth.c
··· 29 29 #include <net/pkt_cls.h> 30 30 31 31 #include "icssm_prueth.h" 32 + #include "icssm_prueth_switch.h" 33 + #include "icssm_vlan_mcast_filter_mmap.h" 32 34 #include "../icssg/icssg_mii_rt.h" 33 35 #include "../icssg/icss_iep.h" 34 36 ··· 147 145 }, 148 146 }; 149 147 150 - static const struct prueth_queue_desc queue_descs[][NUM_QUEUES] = { 148 + const struct prueth_queue_desc queue_descs[][NUM_QUEUES] = { 151 149 [PRUETH_PORT_QUEUE_HOST] = { 152 150 { .rd_ptr = P0_Q1_BD_OFFSET, .wr_ptr = P0_Q1_BD_OFFSET, }, 153 151 { .rd_ptr = P0_Q2_BD_OFFSET, .wr_ptr = P0_Q2_BD_OFFSET, }, ··· 207 205 208 206 static void icssm_prueth_mii_init(struct prueth *prueth) 209 207 { 208 + u32 txcfg_reg, txcfg, txcfg2; 210 209 struct regmap *mii_rt; 211 210 u32 rxcfg_reg, rxcfg; 212 - u32 txcfg_reg, txcfg; 213 211 214 212 mii_rt = prueth->mii_rt; 215 213 ··· 237 235 (TX_START_DELAY << PRUSS_MII_RT_TXCFG_TX_START_DELAY_SHIFT) | 238 236 (TX_CLK_DELAY_100M << PRUSS_MII_RT_TXCFG_TX_CLK_DELAY_SHIFT); 239 237 238 + txcfg2 = txcfg; 239 + if (!PRUETH_IS_EMAC(prueth)) 240 + txcfg2 |= PRUSS_MII_RT_TXCFG_TX_MUX_SEL; 241 + 240 242 /* Configuration of Port 0 Tx */ 241 243 txcfg_reg = PRUSS_MII_RT_TXCFG0; 242 244 243 - regmap_write(mii_rt, txcfg_reg, txcfg); 245 + regmap_write(mii_rt, txcfg_reg, txcfg2); 244 246 245 - txcfg |= PRUSS_MII_RT_TXCFG_TX_MUX_SEL; 247 + txcfg2 = txcfg; 248 + if (PRUETH_IS_EMAC(prueth)) 249 + txcfg2 |= PRUSS_MII_RT_TXCFG_TX_MUX_SEL; 246 250 247 251 /* Configuration of Port 1 Tx */ 248 252 txcfg_reg = PRUSS_MII_RT_TXCFG1; 249 253 250 - regmap_write(mii_rt, txcfg_reg, txcfg); 254 + regmap_write(mii_rt, txcfg_reg, txcfg2); 251 255 252 256 txcfg_reg = PRUSS_MII_RT_RX_FRMS0; 253 257 ··· 300 292 icssm_prueth_clearmem(prueth, PRUETH_MEM_DRAM1); 301 293 302 294 /* Initialize host queues in shared RAM */ 303 - icssm_prueth_hostconfig(prueth); 295 + if (!PRUETH_IS_EMAC(prueth)) 296 + icssm_prueth_sw_hostconfig(prueth); 297 + else 298 + icssm_prueth_hostconfig(prueth); 304 299 305 300 /* Configure MII_RT */ 306 301 icssm_prueth_mii_init(prueth); ··· 510 499 struct prueth_queue_desc __iomem *queue_desc; 511 500 const struct prueth_queue_info *txqueue; 512 501 struct net_device *ndev = emac->ndev; 502 + struct prueth *prueth = emac->prueth; 513 503 unsigned int buffer_desc_count; 514 504 int free_blocks, update_block; 515 505 bool buffer_wrapped = false; 516 506 int write_block, read_block; 517 507 void *src_addr, *dst_addr; 518 508 int pkt_block_size; 509 + void __iomem *sram; 519 510 void __iomem *dram; 520 511 int txport, pktlen; 521 512 u16 update_wr_ptr; 522 513 u32 wr_buf_desc; 523 514 void *ocmc_ram; 524 515 525 - dram = emac->prueth->mem[emac->dram].va; 516 + if (!PRUETH_IS_EMAC(prueth)) 517 + dram = prueth->mem[PRUETH_MEM_DRAM1].va; 518 + else 519 + dram = emac->prueth->mem[emac->dram].va; 526 520 if (eth_skb_pad(skb)) { 527 521 if (netif_msg_tx_err(emac) && net_ratelimit()) 528 522 netdev_err(ndev, "packet pad failed\n"); ··· 540 524 pktlen = skb->len; 541 525 /* Get the tx queue */ 542 526 queue_desc = emac->tx_queue_descs + queue_id; 543 - txqueue = &queue_infos[txport][queue_id]; 527 + if (!PRUETH_IS_EMAC(prueth)) 528 + txqueue = &sw_queue_infos[txport][queue_id]; 529 + else 530 + txqueue = &queue_infos[txport][queue_id]; 544 531 545 532 buffer_desc_count = icssm_get_buff_desc_count(txqueue); 546 533 ··· 609 590 /* update first buffer descriptor */ 610 591 wr_buf_desc = (pktlen << PRUETH_BD_LENGTH_SHIFT) & 611 592 PRUETH_BD_LENGTH_MASK; 612 - writel(wr_buf_desc, dram + readw(&queue_desc->wr_ptr)); 593 + sram = prueth->mem[PRUETH_MEM_SHARED_RAM].va; 594 + if (!PRUETH_IS_EMAC(prueth)) 595 + writel(wr_buf_desc, sram + readw(&queue_desc->wr_ptr)); 596 + else 597 + writel(wr_buf_desc, dram + readw(&queue_desc->wr_ptr)); 613 598 614 599 /* update the write pointer in this queue descriptor, the firmware 615 600 * polls for this change so this will signal the start of transmission ··· 627 604 void icssm_parse_packet_info(struct prueth *prueth, u32 buffer_descriptor, 628 605 struct prueth_packet_info *pkt_info) 629 606 { 630 - pkt_info->shadow = !!(buffer_descriptor & PRUETH_BD_SHADOW_MASK); 631 607 pkt_info->port = (buffer_descriptor & PRUETH_BD_PORT_MASK) >> 632 608 PRUETH_BD_PORT_SHIFT; 633 609 pkt_info->length = (buffer_descriptor & PRUETH_BD_LENGTH_MASK) >> ··· 735 713 src_addr += actual_pkt_len; 736 714 } 737 715 716 + if (PRUETH_IS_SWITCH(emac->prueth)) { 717 + skb->offload_fwd_mark = READ_ONCE(emac->offload_fwd_mark); 718 + if (!pkt_info->lookup_success) 719 + icssm_prueth_sw_learn_fdb(emac, skb->data + ETH_ALEN); 720 + } 721 + 738 722 skb_put(skb, actual_pkt_len); 739 723 740 724 /* send packet up the stack */ 741 725 skb->protocol = eth_type_trans(skb, ndev); 726 + local_bh_disable(); 742 727 netif_receive_skb(skb); 728 + local_bh_enable(); 743 729 744 730 /* update stats */ 745 731 emac->stats.rx_bytes += actual_pkt_len; ··· 773 743 774 744 shared_ram = emac->prueth->mem[PRUETH_MEM_SHARED_RAM].va; 775 745 746 + /* Start and end queue is made common for EMAC, RSTP */ 776 747 start_queue = emac->rx_queue_start; 777 748 end_queue = emac->rx_queue_end; 778 749 ··· 784 753 /* search host queues for packets */ 785 754 for (i = start_queue; i <= end_queue; i++) { 786 755 queue_desc = emac->rx_queue_descs + i; 787 - rxqueue = &queue_infos[PRUETH_PORT_HOST][i]; 788 - 756 + if (PRUETH_IS_SWITCH(emac->prueth)) 757 + rxqueue = &sw_queue_infos[PRUETH_PORT_HOST][i]; 758 + else 759 + rxqueue = &queue_infos[PRUETH_PORT_HOST][i]; 789 760 overflow_cnt = readb(&queue_desc->overflow_cnt); 790 761 if (overflow_cnt > 0) { 791 762 emac->stats.rx_over_errors += overflow_cnt; ··· 912 879 return ret; 913 880 } 914 881 882 + /* Function to free memory related to sw */ 883 + static void icssm_prueth_free_memory(struct prueth *prueth) 884 + { 885 + if (PRUETH_IS_SWITCH(prueth)) 886 + icssm_prueth_sw_free_fdb_table(prueth); 887 + } 888 + 915 889 static void icssm_ptp_dram_init(struct prueth_emac *emac) 916 890 { 917 891 void __iomem *sram = emac->prueth->mem[PRUETH_MEM_SHARED_RAM].va; ··· 981 941 if (!prueth->emac_configured) 982 942 icssm_prueth_init_ethernet_mode(prueth); 983 943 984 - icssm_prueth_emac_config(emac); 944 + /* reset and start PRU firmware */ 945 + if (PRUETH_IS_SWITCH(prueth)) { 946 + ret = icssm_prueth_sw_emac_config(emac); 947 + if (ret) 948 + return ret; 949 + 950 + ret = icssm_prueth_sw_init_fdb_table(prueth); 951 + if (ret) 952 + return ret; 953 + } else { 954 + icssm_prueth_emac_config(emac); 955 + } 985 956 986 957 if (!prueth->emac_configured) { 987 958 icssm_ptp_dram_init(emac); 988 959 ret = icss_iep_init(prueth->iep, NULL, NULL, 0); 989 960 if (ret) { 990 961 netdev_err(ndev, "Failed to initialize iep: %d\n", ret); 991 - goto iep_exit; 962 + goto free_mem; 992 963 } 993 964 } 994 965 995 - ret = icssm_emac_set_boot_pru(emac, ndev); 996 - if (ret) 997 - goto iep_exit; 966 + if (!PRUETH_IS_EMAC(prueth)) { 967 + ret = icssm_prueth_sw_boot_prus(prueth, ndev); 968 + if (ret) 969 + goto iep_exit; 970 + } else { 971 + /* boot the PRU */ 972 + ret = icssm_emac_set_boot_pru(emac, ndev); 973 + if (ret) 974 + goto iep_exit; 975 + } 998 976 999 977 ret = icssm_emac_request_irqs(emac); 1000 978 if (ret) ··· 1027 969 icssm_prueth_port_enable(emac, true); 1028 970 1029 971 prueth->emac_configured |= BIT(emac->port_id); 1030 - 972 + if (PRUETH_IS_SWITCH(prueth)) 973 + icssm_prueth_sw_set_stp_state(prueth, emac->port_id, 974 + BR_STATE_LEARNING); 1031 975 if (netif_msg_drv(emac)) 1032 976 dev_notice(&ndev->dev, "started\n"); 1033 977 1034 978 return 0; 1035 979 1036 980 rproc_shutdown: 1037 - rproc_shutdown(emac->pru); 981 + if (!PRUETH_IS_EMAC(prueth)) 982 + icssm_prueth_sw_shutdown_prus(emac, ndev); 983 + else 984 + rproc_shutdown(emac->pru); 1038 985 1039 986 iep_exit: 1040 987 if (!prueth->emac_configured) 1041 988 icss_iep_exit(prueth->iep); 1042 - 989 + free_mem: 990 + icssm_prueth_free_memory(emac->prueth); 1043 991 return ret; 1044 992 } 1045 993 ··· 1074 1010 hrtimer_cancel(&emac->tx_hrtimer); 1075 1011 1076 1012 /* stop the PRU */ 1077 - rproc_shutdown(emac->pru); 1013 + if (!PRUETH_IS_EMAC(prueth)) 1014 + icssm_prueth_sw_shutdown_prus(emac, ndev); 1015 + else 1016 + rproc_shutdown(emac->pru); 1078 1017 1079 1018 /* free rx interrupts */ 1080 1019 free_irq(emac->rx_irq, ndev); 1081 1020 1021 + /* free memory related to sw */ 1022 + icssm_prueth_free_memory(emac->prueth); 1023 + 1024 + if (!prueth->emac_configured) 1025 + icss_iep_exit(prueth->iep); 1026 + 1082 1027 if (netif_msg_drv(emac)) 1083 1028 dev_notice(&ndev->dev, "stopped\n"); 1029 + 1030 + return 0; 1031 + } 1032 + 1033 + static int icssm_prueth_change_mode(struct prueth *prueth, 1034 + enum pruss_ethtype mode) 1035 + { 1036 + bool portstatus[PRUETH_NUM_MACS]; 1037 + struct prueth_emac *emac; 1038 + struct net_device *ndev; 1039 + int i, ret; 1040 + 1041 + for (i = 0; i < PRUETH_NUM_MACS; i++) { 1042 + if (!prueth->emac[i]) { 1043 + dev_err(prueth->dev, "Unknown MAC port\n"); 1044 + return -EINVAL; 1045 + } 1046 + 1047 + emac = prueth->emac[i]; 1048 + ndev = emac->ndev; 1049 + 1050 + portstatus[i] = netif_running(ndev); 1051 + if (!portstatus[i]) 1052 + continue; 1053 + 1054 + ret = ndev->netdev_ops->ndo_stop(ndev); 1055 + if (ret < 0) { 1056 + netdev_err(ndev, "failed to stop: %d", ret); 1057 + return ret; 1058 + } 1059 + } 1060 + 1061 + if (mode == PRUSS_ETHTYPE_EMAC || mode == PRUSS_ETHTYPE_SWITCH) { 1062 + prueth->eth_type = mode; 1063 + } else { 1064 + dev_err(prueth->dev, "unknown mode\n"); 1065 + return -EINVAL; 1066 + } 1067 + 1068 + for (i = 0; i < PRUETH_NUM_MACS; i++) { 1069 + if (!prueth->emac[i]) { 1070 + dev_err(prueth->dev, "Unknown MAC port\n"); 1071 + return -EINVAL; 1072 + } 1073 + 1074 + emac = prueth->emac[i]; 1075 + ndev = emac->ndev; 1076 + 1077 + if (!portstatus[i]) 1078 + continue; 1079 + 1080 + ret = ndev->netdev_ops->ndo_open(ndev); 1081 + if (ret < 0) { 1082 + netdev_err(ndev, "failed to start: %d", ret); 1083 + return ret; 1084 + } 1085 + } 1084 1086 1085 1087 return 0; 1086 1088 } ··· 1261 1131 stats->rx_length_errors = emac->stats.rx_length_errors; 1262 1132 } 1263 1133 1134 + /* enable/disable MC filter */ 1135 + static void icssm_emac_mc_filter_ctrl(struct prueth_emac *emac, bool enable) 1136 + { 1137 + struct prueth *prueth = emac->prueth; 1138 + void __iomem *mc_filter_ctrl; 1139 + void __iomem *ram; 1140 + u32 reg; 1141 + 1142 + ram = prueth->mem[emac->dram].va; 1143 + mc_filter_ctrl = ram + ICSS_EMAC_FW_MULTICAST_FILTER_CTRL_OFFSET; 1144 + 1145 + if (enable) 1146 + reg = ICSS_EMAC_FW_MULTICAST_FILTER_CTRL_ENABLED; 1147 + else 1148 + reg = ICSS_EMAC_FW_MULTICAST_FILTER_CTRL_DISABLED; 1149 + 1150 + writeb(reg, mc_filter_ctrl); 1151 + } 1152 + 1153 + /* reset MC filter bins */ 1154 + static void icssm_emac_mc_filter_reset(struct prueth_emac *emac) 1155 + { 1156 + struct prueth *prueth = emac->prueth; 1157 + void __iomem *mc_filter_tbl; 1158 + u32 mc_filter_tbl_base; 1159 + void __iomem *ram; 1160 + 1161 + ram = prueth->mem[emac->dram].va; 1162 + mc_filter_tbl_base = ICSS_EMAC_FW_MULTICAST_FILTER_TABLE; 1163 + 1164 + mc_filter_tbl = ram + mc_filter_tbl_base; 1165 + memset_io(mc_filter_tbl, 0, ICSS_EMAC_FW_MULTICAST_TABLE_SIZE_BYTES); 1166 + } 1167 + 1168 + /* set MC filter hashmask */ 1169 + static void icssm_emac_mc_filter_hashmask 1170 + (struct prueth_emac *emac, 1171 + u8 mask[ICSS_EMAC_FW_MULTICAST_FILTER_MASK_SIZE_BYTES]) 1172 + { 1173 + struct prueth *prueth = emac->prueth; 1174 + void __iomem *mc_filter_mask; 1175 + void __iomem *ram; 1176 + 1177 + ram = prueth->mem[emac->dram].va; 1178 + 1179 + mc_filter_mask = ram + ICSS_EMAC_FW_MULTICAST_FILTER_MASK_OFFSET; 1180 + memcpy_toio(mc_filter_mask, mask, 1181 + ICSS_EMAC_FW_MULTICAST_FILTER_MASK_SIZE_BYTES); 1182 + } 1183 + 1184 + static void icssm_emac_mc_filter_bin_update(struct prueth_emac *emac, u8 hash, 1185 + u8 val) 1186 + { 1187 + struct prueth *prueth = emac->prueth; 1188 + void __iomem *mc_filter_tbl; 1189 + void __iomem *ram; 1190 + 1191 + ram = prueth->mem[emac->dram].va; 1192 + 1193 + mc_filter_tbl = ram + ICSS_EMAC_FW_MULTICAST_FILTER_TABLE; 1194 + writeb(val, mc_filter_tbl + hash); 1195 + } 1196 + 1197 + void icssm_emac_mc_filter_bin_allow(struct prueth_emac *emac, u8 hash) 1198 + { 1199 + icssm_emac_mc_filter_bin_update 1200 + (emac, hash, 1201 + ICSS_EMAC_FW_MULTICAST_FILTER_HOST_RCV_ALLOWED); 1202 + } 1203 + 1204 + void icssm_emac_mc_filter_bin_disallow(struct prueth_emac *emac, u8 hash) 1205 + { 1206 + icssm_emac_mc_filter_bin_update 1207 + (emac, hash, 1208 + ICSS_EMAC_FW_MULTICAST_FILTER_HOST_RCV_NOT_ALLOWED); 1209 + } 1210 + 1211 + u8 icssm_emac_get_mc_hash(u8 *mac, u8 *mask) 1212 + { 1213 + u8 hash; 1214 + int j; 1215 + 1216 + for (j = 0, hash = 0; j < ETH_ALEN; j++) 1217 + hash ^= (mac[j] & mask[j]); 1218 + 1219 + return hash; 1220 + } 1221 + 1222 + /** 1223 + * icssm_emac_ndo_set_rx_mode - EMAC set receive mode function 1224 + * @ndev: The EMAC network adapter 1225 + * 1226 + * Called when system wants to set the receive mode of the device. 1227 + * 1228 + */ 1229 + static void icssm_emac_ndo_set_rx_mode(struct net_device *ndev) 1230 + { 1231 + struct prueth_emac *emac = netdev_priv(ndev); 1232 + bool promisc = ndev->flags & IFF_PROMISC; 1233 + struct netdev_hw_addr *ha; 1234 + struct prueth *prueth; 1235 + unsigned long flags; 1236 + void __iomem *sram; 1237 + u32 mask, reg; 1238 + u8 hash; 1239 + 1240 + prueth = emac->prueth; 1241 + sram = prueth->mem[PRUETH_MEM_SHARED_RAM].va; 1242 + reg = readl(sram + EMAC_PROMISCUOUS_MODE_OFFSET); 1243 + 1244 + /* It is a shared table. So lock the access */ 1245 + spin_lock_irqsave(&emac->addr_lock, flags); 1246 + 1247 + /* Disable and reset multicast filter, allows allmulti */ 1248 + icssm_emac_mc_filter_ctrl(emac, false); 1249 + icssm_emac_mc_filter_reset(emac); 1250 + icssm_emac_mc_filter_hashmask(emac, emac->mc_filter_mask); 1251 + 1252 + if (PRUETH_IS_EMAC(prueth)) { 1253 + switch (emac->port_id) { 1254 + case PRUETH_PORT_MII0: 1255 + mask = EMAC_P1_PROMISCUOUS_BIT; 1256 + break; 1257 + case PRUETH_PORT_MII1: 1258 + mask = EMAC_P2_PROMISCUOUS_BIT; 1259 + break; 1260 + default: 1261 + netdev_err(ndev, "%s: invalid port\n", __func__); 1262 + goto unlock; 1263 + } 1264 + 1265 + if (promisc) { 1266 + /* Enable promiscuous mode */ 1267 + reg |= mask; 1268 + } else { 1269 + /* Disable promiscuous mode */ 1270 + reg &= ~mask; 1271 + } 1272 + 1273 + writel(reg, sram + EMAC_PROMISCUOUS_MODE_OFFSET); 1274 + 1275 + if (promisc) 1276 + goto unlock; 1277 + } 1278 + 1279 + if (ndev->flags & IFF_ALLMULTI && !PRUETH_IS_SWITCH(prueth)) 1280 + goto unlock; 1281 + 1282 + icssm_emac_mc_filter_ctrl(emac, true); /* all multicast blocked */ 1283 + 1284 + if (netdev_mc_empty(ndev)) 1285 + goto unlock; 1286 + 1287 + netdev_for_each_mc_addr(ha, ndev) { 1288 + hash = icssm_emac_get_mc_hash(ha->addr, emac->mc_filter_mask); 1289 + icssm_emac_mc_filter_bin_allow(emac, hash); 1290 + } 1291 + 1292 + /* Add bridge device's MC addresses as well */ 1293 + if (prueth->hw_bridge_dev) { 1294 + netdev_for_each_mc_addr(ha, prueth->hw_bridge_dev) { 1295 + hash = icssm_emac_get_mc_hash(ha->addr, 1296 + emac->mc_filter_mask); 1297 + icssm_emac_mc_filter_bin_allow(emac, hash); 1298 + } 1299 + } 1300 + 1301 + unlock: 1302 + spin_unlock_irqrestore(&emac->addr_lock, flags); 1303 + } 1304 + 1264 1305 static const struct net_device_ops emac_netdev_ops = { 1265 1306 .ndo_open = icssm_emac_ndo_open, 1266 1307 .ndo_stop = icssm_emac_ndo_stop, 1267 1308 .ndo_start_xmit = icssm_emac_ndo_start_xmit, 1268 1309 .ndo_get_stats64 = icssm_emac_ndo_get_stats64, 1310 + .ndo_set_rx_mode = icssm_emac_ndo_set_rx_mode, 1269 1311 }; 1270 1312 1271 1313 /* get emac_port corresponding to eth_node name */ ··· 1490 1188 static int icssm_prueth_netdev_init(struct prueth *prueth, 1491 1189 struct device_node *eth_node) 1492 1190 { 1191 + const struct prueth_private_data *fw_data = prueth->fw_data; 1493 1192 struct prueth_emac *emac; 1494 1193 struct net_device *ndev; 1495 1194 enum prueth_port port; ··· 1515 1212 emac->prueth = prueth; 1516 1213 emac->ndev = ndev; 1517 1214 emac->port_id = port; 1215 + memset(&emac->mc_filter_mask[0], 0xff, ETH_ALEN); 1518 1216 1519 1217 /* by default eth_type is EMAC */ 1520 1218 switch (port) { ··· 1551 1247 goto free; 1552 1248 } 1553 1249 1250 + spin_lock_init(&emac->lock); 1251 + spin_lock_init(&emac->addr_lock); 1252 + 1554 1253 /* get mac address from DT and set private and netdev addr */ 1555 1254 ret = of_get_ethdev_address(eth_node, ndev); 1556 1255 if (!is_valid_ether_addr(ndev->dev_addr)) { ··· 1580 1273 1581 1274 phy_remove_link_mode(emac->phydev, ETHTOOL_LINK_MODE_Pause_BIT); 1582 1275 phy_remove_link_mode(emac->phydev, ETHTOOL_LINK_MODE_Asym_Pause_BIT); 1276 + 1277 + /* Protocol switching 1278 + * Enabling L2 Firmware offloading 1279 + */ 1280 + if (fw_data->support_switch) { 1281 + ndev->features |= NETIF_F_HW_L2FW_DOFFLOAD; 1282 + ndev->hw_features |= NETIF_F_HW_L2FW_DOFFLOAD; 1283 + } 1583 1284 1584 1285 ndev->dev.of_node = eth_node; 1585 1286 ndev->netdev_ops = &emac_netdev_ops; ··· 1623 1308 1624 1309 netif_napi_del(&emac->napi); 1625 1310 prueth->emac[mac] = NULL; 1311 + } 1312 + 1313 + bool icssm_prueth_sw_port_dev_check(const struct net_device *ndev) 1314 + { 1315 + if (ndev->netdev_ops != &emac_netdev_ops) 1316 + return false; 1317 + 1318 + if (ndev->features & NETIF_F_HW_L2FW_DOFFLOAD) 1319 + return true; 1320 + 1321 + return false; 1322 + } 1323 + 1324 + static int icssm_prueth_port_offload_fwd_mark_update(struct prueth *prueth) 1325 + { 1326 + int set_val = 0; 1327 + int i, ret = 0; 1328 + u8 all_slaves; 1329 + 1330 + all_slaves = BIT(PRUETH_PORT_MII0) | BIT(PRUETH_PORT_MII1); 1331 + 1332 + if (prueth->br_members == all_slaves) 1333 + set_val = 1; 1334 + 1335 + dev_dbg(prueth->dev, "set offload_fwd_mark %d, mbrs=0x%x\n", 1336 + set_val, prueth->br_members); 1337 + 1338 + for (i = 0; i < PRUETH_NUM_MACS; i++) { 1339 + if (prueth->emac[i]) 1340 + WRITE_ONCE(prueth->emac[i]->offload_fwd_mark, set_val); 1341 + } 1342 + 1343 + /* Bridge is created, load switch firmware, 1344 + * if not already in that mode 1345 + */ 1346 + if (set_val && !PRUETH_IS_SWITCH(prueth)) { 1347 + ret = icssm_prueth_change_mode(prueth, PRUSS_ETHTYPE_SWITCH); 1348 + if (ret < 0) 1349 + dev_err(prueth->dev, "Failed to enable Switch mode\n"); 1350 + else 1351 + dev_info(prueth->dev, 1352 + "TI PRU ethernet now in Switch mode\n"); 1353 + } 1354 + 1355 + /* Bridge is deleted, switch to Dual EMAC mode */ 1356 + if (!prueth->br_members && !PRUETH_IS_EMAC(prueth)) { 1357 + ret = icssm_prueth_change_mode(prueth, PRUSS_ETHTYPE_EMAC); 1358 + if (ret < 0) 1359 + dev_err(prueth->dev, "Failed to enable Dual EMAC mode\n"); 1360 + else 1361 + dev_info(prueth->dev, 1362 + "TI PRU ethernet now in Dual EMAC mode\n"); 1363 + } 1364 + 1365 + return ret; 1366 + } 1367 + 1368 + static int icssm_prueth_ndev_port_link(struct net_device *ndev, 1369 + struct net_device *br_ndev) 1370 + { 1371 + struct prueth_emac *emac = netdev_priv(ndev); 1372 + struct prueth *prueth = emac->prueth; 1373 + unsigned long flags; 1374 + int ret = 0; 1375 + 1376 + dev_dbg(prueth->dev, "%s: br_mbrs=0x%x %s\n", 1377 + __func__, prueth->br_members, ndev->name); 1378 + 1379 + spin_lock_irqsave(&emac->addr_lock, flags); 1380 + 1381 + if (!prueth->br_members) { 1382 + prueth->hw_bridge_dev = br_ndev; 1383 + } else { 1384 + /* This is adding the port to a second bridge, 1385 + * this is unsupported 1386 + */ 1387 + if (prueth->hw_bridge_dev != br_ndev) { 1388 + spin_unlock_irqrestore(&emac->addr_lock, flags); 1389 + return -EOPNOTSUPP; 1390 + } 1391 + } 1392 + 1393 + prueth->br_members |= BIT(emac->port_id); 1394 + 1395 + spin_unlock_irqrestore(&emac->addr_lock, flags); 1396 + 1397 + ret = icssm_prueth_port_offload_fwd_mark_update(prueth); 1398 + 1399 + return ret; 1400 + } 1401 + 1402 + static int icssm_prueth_ndev_port_unlink(struct net_device *ndev) 1403 + { 1404 + struct prueth_emac *emac = netdev_priv(ndev); 1405 + struct prueth *prueth = emac->prueth; 1406 + unsigned long flags; 1407 + int ret = 0; 1408 + 1409 + dev_dbg(prueth->dev, "emac_sw_ndev_port_unlink\n"); 1410 + 1411 + spin_lock_irqsave(&emac->addr_lock, flags); 1412 + 1413 + prueth->br_members &= ~BIT(emac->port_id); 1414 + 1415 + spin_unlock_irqrestore(&emac->addr_lock, flags); 1416 + 1417 + ret = icssm_prueth_port_offload_fwd_mark_update(prueth); 1418 + 1419 + spin_lock_irqsave(&emac->addr_lock, flags); 1420 + 1421 + if (!prueth->br_members) 1422 + prueth->hw_bridge_dev = NULL; 1423 + 1424 + spin_unlock_irqrestore(&emac->addr_lock, flags); 1425 + 1426 + return ret; 1427 + } 1428 + 1429 + static int icssm_prueth_ndev_event(struct notifier_block *unused, 1430 + unsigned long event, void *ptr) 1431 + { 1432 + struct net_device *ndev = netdev_notifier_info_to_dev(ptr); 1433 + struct netdev_notifier_changeupper_info *info; 1434 + int ret = NOTIFY_DONE; 1435 + 1436 + if (!icssm_prueth_sw_port_dev_check(ndev)) 1437 + return NOTIFY_DONE; 1438 + 1439 + switch (event) { 1440 + case NETDEV_CHANGEUPPER: 1441 + info = ptr; 1442 + if (netif_is_bridge_master(info->upper_dev)) { 1443 + if (info->linking) 1444 + ret = icssm_prueth_ndev_port_link 1445 + (ndev, info->upper_dev); 1446 + else 1447 + ret = icssm_prueth_ndev_port_unlink(ndev); 1448 + } 1449 + break; 1450 + default: 1451 + return NOTIFY_DONE; 1452 + } 1453 + 1454 + return notifier_from_errno(ret); 1455 + } 1456 + 1457 + static int icssm_prueth_register_notifiers(struct prueth *prueth) 1458 + { 1459 + int ret = 0; 1460 + 1461 + prueth->prueth_netdevice_nb.notifier_call = icssm_prueth_ndev_event; 1462 + ret = register_netdevice_notifier(&prueth->prueth_netdevice_nb); 1463 + if (ret) { 1464 + dev_err(prueth->dev, 1465 + "register netdevice notifier failed ret: %d\n", ret); 1466 + return ret; 1467 + } 1468 + 1469 + ret = icssm_prueth_sw_register_notifiers(prueth); 1470 + if (ret) 1471 + unregister_netdevice_notifier(&prueth->prueth_netdevice_nb); 1472 + 1473 + return ret; 1626 1474 } 1627 1475 1628 1476 static int icssm_prueth_probe(struct platform_device *pdev) ··· 2007 1529 prueth->emac[PRUETH_MAC1]->ndev; 2008 1530 } 2009 1531 1532 + ret = icssm_prueth_register_notifiers(prueth); 1533 + if (ret) { 1534 + dev_err(dev, "can't register switchdev notifiers"); 1535 + goto netdev_unregister; 1536 + } 1537 + 2010 1538 dev_info(dev, "TI PRU ethernet driver initialized: %s EMAC mode\n", 2011 1539 (!eth0_node || !eth1_node) ? "single" : "dual"); 2012 1540 ··· 2072 1588 struct prueth *prueth = platform_get_drvdata(pdev); 2073 1589 struct device_node *eth_node; 2074 1590 int i; 1591 + 1592 + unregister_netdevice_notifier(&prueth->prueth_netdevice_nb); 1593 + icssm_prueth_sw_unregister_notifiers(prueth); 2075 1594 2076 1595 for (i = 0; i < PRUETH_NUM_MACS; i++) { 2077 1596 if (!prueth->registered_netdevs[i]) ··· 2175 1688 .fw_pru[PRUSS_PRU0] = { 2176 1689 .fw_name[PRUSS_ETHTYPE_EMAC] = 2177 1690 "ti-pruss/am335x-pru0-prueth-fw.elf", 1691 + .fw_name[PRUSS_ETHTYPE_SWITCH] = 1692 + "ti-pruss/am335x-pru0-prusw-fw.elf", 2178 1693 }, 2179 1694 .fw_pru[PRUSS_PRU1] = { 2180 1695 .fw_name[PRUSS_ETHTYPE_EMAC] = 2181 1696 "ti-pruss/am335x-pru1-prueth-fw.elf", 1697 + .fw_name[PRUSS_ETHTYPE_SWITCH] = 1698 + "ti-pruss/am335x-pru1-prusw-fw.elf", 2182 1699 }, 1700 + .support_switch = true, 2183 1701 }; 2184 1702 2185 1703 /* AM437x SoC-specific firmware data */ ··· 2193 1701 .fw_pru[PRUSS_PRU0] = { 2194 1702 .fw_name[PRUSS_ETHTYPE_EMAC] = 2195 1703 "ti-pruss/am437x-pru0-prueth-fw.elf", 1704 + .fw_name[PRUSS_ETHTYPE_SWITCH] = 1705 + "ti-pruss/am437x-pru0-prusw-fw.elf", 2196 1706 }, 2197 1707 .fw_pru[PRUSS_PRU1] = { 2198 1708 .fw_name[PRUSS_ETHTYPE_EMAC] = 2199 1709 "ti-pruss/am437x-pru1-prueth-fw.elf", 1710 + .fw_name[PRUSS_ETHTYPE_SWITCH] = 1711 + "ti-pruss/am437x-pru1-prusw-fw.elf", 2200 1712 }, 1713 + .support_switch = true, 2201 1714 }; 2202 1715 2203 1716 /* AM57xx SoC-specific firmware data */ ··· 2211 1714 .fw_pru[PRUSS_PRU0] = { 2212 1715 .fw_name[PRUSS_ETHTYPE_EMAC] = 2213 1716 "ti-pruss/am57xx-pru0-prueth-fw.elf", 1717 + .fw_name[PRUSS_ETHTYPE_SWITCH] = 1718 + "ti-pruss/am57xx-pru0-prusw-fw.elf", 2214 1719 }, 2215 1720 .fw_pru[PRUSS_PRU1] = { 2216 1721 .fw_name[PRUSS_ETHTYPE_EMAC] = 2217 1722 "ti-pruss/am57xx-pru1-prueth-fw.elf", 1723 + .fw_name[PRUSS_ETHTYPE_SWITCH] = 1724 + "ti-pruss/am57xx-pru1-prusw-fw.elf", 1725 + 2218 1726 }, 1727 + .support_switch = true, 2219 1728 }; 2220 1729 2221 1730 static const struct of_device_id prueth_dt_match[] = {
+19 -1
drivers/net/ethernet/ti/icssm/icssm_prueth.h
··· 15 15 16 16 #include "icssm_switch.h" 17 17 #include "icssm_prueth_ptp.h" 18 + #include "icssm_prueth_fdb_tbl.h" 18 19 19 20 /* ICSSM size of redundancy tag */ 20 21 #define ICSSM_LRE_TAG_SIZE 6 ··· 182 181 * struct prueth_private_data - PRU Ethernet private data 183 182 * @driver_data: PRU Ethernet device name 184 183 * @fw_pru: firmware names to be used for PRUSS ethernet usecases 184 + * @support_switch: boolean to indicate if switch is enabled 185 185 */ 186 186 struct prueth_private_data { 187 187 enum pruss_device driver_data; 188 188 const struct prueth_firmware fw_pru[PRUSS_NUM_PRUS]; 189 + bool support_switch; 189 190 }; 190 191 191 192 struct prueth_emac_stats { ··· 224 221 const char *phy_id; 225 222 u32 msg_enable; 226 223 u8 mac_addr[6]; 224 + unsigned char mc_filter_mask[ETH_ALEN]; /* for multicast filtering */ 227 225 phy_interface_t phy_if; 228 226 229 227 /* spin lock used to protect 230 228 * during link configuration 231 229 */ 232 230 spinlock_t lock; 231 + spinlock_t addr_lock; /* serialize access to VLAN/MC filter table */ 233 232 234 233 struct hrtimer tx_hrtimer; 235 234 struct prueth_emac_stats stats; 235 + int offload_fwd_mark; 236 236 }; 237 237 238 238 struct prueth { ··· 254 248 struct prueth_emac *emac[PRUETH_NUM_MACS]; 255 249 struct net_device *registered_netdevs[PRUETH_NUM_MACS]; 256 250 251 + struct net_device *hw_bridge_dev; 252 + struct fdb_tbl *fdb_tbl; 253 + 254 + struct notifier_block prueth_netdevice_nb; 255 + struct notifier_block prueth_switchdev_nb; 256 + struct notifier_block prueth_switchdev_bl_nb; 257 + 257 258 unsigned int eth_type; 258 259 size_t ocmc_ram_size; 259 260 u8 emac_configured; 261 + u8 br_members; 260 262 }; 263 + 264 + extern const struct prueth_queue_desc queue_descs[][NUM_QUEUES]; 261 265 262 266 void icssm_parse_packet_info(struct prueth *prueth, u32 buffer_descriptor, 263 267 struct prueth_packet_info *pkt_info); 264 268 int icssm_emac_rx_packet(struct prueth_emac *emac, u16 *bd_rd_ptr, 265 269 struct prueth_packet_info *pkt_info, 266 270 const struct prueth_queue_info *rxqueue); 267 - 271 + void icssm_emac_mc_filter_bin_allow(struct prueth_emac *emac, u8 hash); 272 + void icssm_emac_mc_filter_bin_disallow(struct prueth_emac *emac, u8 hash); 273 + u8 icssm_emac_get_mc_hash(u8 *mac, u8 *mask); 268 274 #endif /* __NET_TI_PRUETH_H */
+76
drivers/net/ethernet/ti/icssm/icssm_prueth_fdb_tbl.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* Copyright (C) 2019-2021 Texas Instruments Incorporated - https://www.ti.com */ 3 + #ifndef __NET_TI_PRUSS_FDB_TBL_H 4 + #define __NET_TI_PRUSS_FDB_TBL_H 5 + 6 + #include <linux/kernel.h> 7 + #include <linux/debugfs.h> 8 + #include "icssm_prueth.h" 9 + 10 + /* 4 bytes */ 11 + struct fdb_index_tbl_entry { 12 + /* Bucket Table index of first Bucket with this MAC address */ 13 + u16 bucket_idx; 14 + u16 bucket_entries; /* Number of entries in this bucket */ 15 + }; 16 + 17 + /* 4 * 256 = 1024 = 0x200 bytes */ 18 + struct fdb_index_array { 19 + struct fdb_index_tbl_entry index_tbl_entry[FDB_INDEX_TBL_MAX_ENTRIES]; 20 + }; 21 + 22 + /* 10 bytes */ 23 + struct fdb_mac_tbl_entry { 24 + u8 mac[ETH_ALEN]; 25 + u16 age; 26 + u8 port; /* 0 based: 0=port1, 1=port2 */ 27 + union { 28 + struct { 29 + u8 is_static:1; 30 + u8 active:1; 31 + }; 32 + u8 flags; 33 + }; 34 + }; 35 + 36 + /* 10 * 256 = 2560 = 0xa00 bytes */ 37 + struct fdb_mac_tbl_array { 38 + struct fdb_mac_tbl_entry mac_tbl_entry[FDB_MAC_TBL_MAX_ENTRIES]; 39 + }; 40 + 41 + /* 1 byte */ 42 + struct fdb_stp_config { 43 + u8 state; /* per-port STP state (defined in FW header) */ 44 + }; 45 + 46 + /* 1 byte */ 47 + struct fdb_flood_config { 48 + u8 host_flood_enable:1; 49 + u8 port1_flood_enable:1; 50 + u8 port2_flood_enable:1; 51 + }; 52 + 53 + /* 2 byte */ 54 + struct fdb_arbitration { 55 + u8 host_lock; 56 + u8 pru_locks; 57 + }; 58 + 59 + struct fdb_tbl { 60 + /* fdb index table */ 61 + struct fdb_index_array __iomem *index_a; 62 + /* fdb MAC table */ 63 + struct fdb_mac_tbl_array __iomem *mac_tbl_a; 64 + /* port 1 stp config */ 65 + struct fdb_stp_config __iomem *port1_stp_cfg; 66 + /* port 2 stp config */ 67 + struct fdb_stp_config __iomem *port2_stp_cfg; 68 + /* per-port flood enable */ 69 + struct fdb_flood_config __iomem *flood_enable_flags; 70 + /* fdb locking mechanism */ 71 + struct fdb_arbitration __iomem *locks; 72 + /* total number of entries in hash table */ 73 + u16 total_entries; 74 + }; 75 + 76 + #endif
+1065
drivers/net/ethernet/ti/icssm/icssm_prueth_switch.c
··· 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(sizeof(*prueth->fdb_tbl), GFP_KERNEL); 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(sizeof(*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(sizeof(*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 + }
+37
drivers/net/ethernet/ti/icssm/icssm_prueth_switch.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* Copyright (C) 2020-2021 Texas Instruments Incorporated - https://www.ti.com 3 + */ 4 + 5 + #ifndef __NET_TI_PRUETH_SWITCH_H 6 + #define __NET_TI_PRUETH_SWITCH_H 7 + 8 + #include <net/switchdev.h> 9 + 10 + #include "icssm_prueth.h" 11 + #include "icssm_prueth_fdb_tbl.h" 12 + #include "icssm_switchdev.h" 13 + 14 + void icssm_prueth_sw_set_stp_state(struct prueth *prueth, 15 + enum prueth_port port, u8 state); 16 + u8 icssm_prueth_sw_get_stp_state(struct prueth *prueth, 17 + enum prueth_port port); 18 + 19 + extern const struct prueth_queue_info sw_queue_infos[][4]; 20 + 21 + void icssm_prueth_sw_fdb_tbl_init(struct prueth *prueth); 22 + int icssm_prueth_sw_init_fdb_table(struct prueth *prueth); 23 + void icssm_prueth_sw_free_fdb_table(struct prueth *prueth); 24 + int icssm_prueth_sw_do_purge_fdb(struct prueth_emac *emac); 25 + void icssm_prueth_sw_fdb_add(struct prueth_emac *emac, 26 + struct switchdev_notifier_fdb_info *fdb); 27 + void icssm_prueth_sw_fdb_del(struct prueth_emac *emac, 28 + struct switchdev_notifier_fdb_info *fdb); 29 + int icssm_prueth_sw_learn_fdb(struct prueth_emac *emac, u8 *src_mac); 30 + int icssm_prueth_sw_purge_fdb(struct prueth_emac *emac); 31 + void icssm_prueth_sw_hostconfig(struct prueth *prueth); 32 + int icssm_prueth_sw_emac_config(struct prueth_emac *emac); 33 + int icssm_prueth_sw_boot_prus(struct prueth *prueth, struct net_device *ndev); 34 + int icssm_prueth_sw_shutdown_prus(struct prueth_emac *emac, 35 + struct net_device *ndev); 36 + 37 + #endif /* __NET_TI_PRUETH_SWITCH_H */
+103
drivers/net/ethernet/ti/icssm/icssm_switch.h
··· 117 117 #define STATISTICS_OFFSET 0x1F00 118 118 #define STAT_SIZE 0x98 119 119 120 + /* The following offsets indicate which sections of the memory are used 121 + * for switch internal tasks 122 + */ 123 + #define SWITCH_SPECIFIC_DRAM0_START_SIZE 0x100 124 + #define SWITCH_SPECIFIC_DRAM0_START_OFFSET 0x1F00 125 + 126 + #define SWITCH_SPECIFIC_DRAM1_START_SIZE 0x300 127 + #define SWITCH_SPECIFIC_DRAM1_START_OFFSET 0x1D00 128 + 120 129 /* Offset for storing 121 130 * 1. Storm Prevention Params 122 131 * 2. PHY Speed Offset ··· 154 145 #define STORM_PREVENTION_OFFSET_UC (STATISTICS_OFFSET + STAT_SIZE + 29) 155 146 /* 4 bytes ? */ 156 147 #define STP_INVALID_STATE_OFFSET (STATISTICS_OFFSET + STAT_SIZE + 33) 148 + 149 + /* DRAM1 Offsets for Switch */ 150 + /* 4 queue descriptors for port 0 (host receive) */ 151 + #define P0_QUEUE_DESC_OFFSET 0x1E7C 152 + #define P1_QUEUE_DESC_OFFSET 0x1E9C 153 + #define P2_QUEUE_DESC_OFFSET 0x1EBC 154 + /* collision descriptor of port 0 */ 155 + #define P0_COL_QUEUE_DESC_OFFSET 0x1E64 156 + #define P1_COL_QUEUE_DESC_OFFSET 0x1E6C 157 + #define P2_COL_QUEUE_DESC_OFFSET 0x1E74 158 + /* Collision Status Register 159 + * P0: bit 0 is pending flag, bit 1..2 indicates which queue, 160 + * P1: bit 8 is pending flag, 9..10 is queue number 161 + * P2: bit 16 is pending flag, 17..18 is queue number, remaining bits are 0. 162 + */ 163 + #define COLLISION_STATUS_ADDR 0x1E60 164 + 165 + #define INTERFACE_MAC_ADDR 0x1E58 166 + #define P2_MAC_ADDR 0x1E50 167 + #define P1_MAC_ADDR 0x1E48 168 + 169 + #define QUEUE_SIZE_ADDR 0x1E30 170 + #define QUEUE_OFFSET_ADDR 0x1E18 171 + #define QUEUE_DESCRIPTOR_OFFSET_ADDR 0x1E00 172 + 173 + #define COL_RX_CONTEXT_P2_OFFSET_ADDR (COL_RX_CONTEXT_P1_OFFSET_ADDR + 12) 174 + #define COL_RX_CONTEXT_P1_OFFSET_ADDR (COL_RX_CONTEXT_P0_OFFSET_ADDR + 12) 175 + #define COL_RX_CONTEXT_P0_OFFSET_ADDR (P2_Q4_RX_CONTEXT_OFFSET + 8) 176 + 177 + /* Port 2 Rx Context */ 178 + #define P2_Q4_RX_CONTEXT_OFFSET (P2_Q3_RX_CONTEXT_OFFSET + 8) 179 + #define P2_Q3_RX_CONTEXT_OFFSET (P2_Q2_RX_CONTEXT_OFFSET + 8) 180 + #define P2_Q2_RX_CONTEXT_OFFSET (P2_Q1_RX_CONTEXT_OFFSET + 8) 181 + #define P2_Q1_RX_CONTEXT_OFFSET RX_CONTEXT_P2_Q1_OFFSET_ADDR 182 + #define RX_CONTEXT_P2_Q1_OFFSET_ADDR (P1_Q4_RX_CONTEXT_OFFSET + 8) 183 + 184 + /* Port 1 Rx Context */ 185 + #define P1_Q4_RX_CONTEXT_OFFSET (P1_Q3_RX_CONTEXT_OFFSET + 8) 186 + #define P1_Q3_RX_CONTEXT_OFFSET (P1_Q2_RX_CONTEXT_OFFSET + 8) 187 + #define P1_Q2_RX_CONTEXT_OFFSET (P1_Q1_RX_CONTEXT_OFFSET + 8) 188 + #define P1_Q1_RX_CONTEXT_OFFSET (RX_CONTEXT_P1_Q1_OFFSET_ADDR) 189 + #define RX_CONTEXT_P1_Q1_OFFSET_ADDR (P0_Q4_RX_CONTEXT_OFFSET + 8) 190 + 191 + /* Host Port Rx Context */ 192 + #define P0_Q4_RX_CONTEXT_OFFSET (P0_Q3_RX_CONTEXT_OFFSET + 8) 193 + #define P0_Q3_RX_CONTEXT_OFFSET (P0_Q2_RX_CONTEXT_OFFSET + 8) 194 + #define P0_Q2_RX_CONTEXT_OFFSET (P0_Q1_RX_CONTEXT_OFFSET + 8) 195 + #define P0_Q1_RX_CONTEXT_OFFSET RX_CONTEXT_P0_Q1_OFFSET_ADDR 196 + #define RX_CONTEXT_P0_Q1_OFFSET_ADDR (COL_TX_CONTEXT_P2_Q1_OFFSET_ADDR + 8) 197 + 198 + /* Port 2 Tx Collision Context */ 199 + #define COL_TX_CONTEXT_P2_Q1_OFFSET_ADDR (COL_TX_CONTEXT_P1_Q1_OFFSET_ADDR + 8) 200 + /* Port 1 Tx Collision Context */ 201 + #define COL_TX_CONTEXT_P1_Q1_OFFSET_ADDR (P2_Q4_TX_CONTEXT_OFFSET + 8) 202 + 203 + /* Port 2 */ 204 + #define P2_Q4_TX_CONTEXT_OFFSET (P2_Q3_TX_CONTEXT_OFFSET + 8) 205 + #define P2_Q3_TX_CONTEXT_OFFSET (P2_Q2_TX_CONTEXT_OFFSET + 8) 206 + #define P2_Q2_TX_CONTEXT_OFFSET (P2_Q1_TX_CONTEXT_OFFSET + 8) 207 + #define P2_Q1_TX_CONTEXT_OFFSET TX_CONTEXT_P2_Q1_OFFSET_ADDR 208 + #define TX_CONTEXT_P2_Q1_OFFSET_ADDR (P1_Q4_TX_CONTEXT_OFFSET + 8) 209 + 210 + /* Port 1 */ 211 + #define P1_Q4_TX_CONTEXT_OFFSET (P1_Q3_TX_CONTEXT_OFFSET + 8) 212 + #define P1_Q3_TX_CONTEXT_OFFSET (P1_Q2_TX_CONTEXT_OFFSET + 8) 213 + #define P1_Q2_TX_CONTEXT_OFFSET (P1_Q1_TX_CONTEXT_OFFSET + 8) 214 + #define P1_Q1_TX_CONTEXT_OFFSET TX_CONTEXT_P1_Q1_OFFSET_ADDR 215 + #define TX_CONTEXT_P1_Q1_OFFSET_ADDR SWITCH_SPECIFIC_DRAM1_START_OFFSET 157 216 158 217 /* DRAM Offsets for EMAC 159 218 * Present on Both DRAM0 and DRAM1 ··· 330 253 ICSS_BLOCK_SIZE) 331 254 #define P0_COL_BUFFER_OFFSET 0xEE00 332 255 #define P0_Q1_BUFFER_OFFSET 0x0000 256 + 257 + #define V2_1_FDB_TBL_LOC PRUETH_MEM_SHARED_RAM 258 + #define V2_1_FDB_TBL_OFFSET 0x2000 259 + 260 + #define FDB_INDEX_TBL_MAX_ENTRIES 256 261 + #define FDB_MAC_TBL_MAX_ENTRIES 256 262 + 263 + #define FDB_INDEX_TBL_OFFSET V2_1_FDB_TBL_OFFSET 264 + #define FDB_INDEX_TBL_SIZE (FDB_INDEX_TBL_MAX_ENTRIES * \ 265 + sizeof(struct fdb_index_tbl_entry)) 266 + 267 + #define FDB_MAC_TBL_OFFSET (FDB_INDEX_TBL_OFFSET + FDB_INDEX_TBL_SIZE) 268 + #define FDB_MAC_TBL_SIZE (FDB_MAC_TBL_MAX_ENTRIES * \ 269 + sizeof(struct fdb_mac_tbl_entry)) 270 + 271 + #define FDB_PORT1_STP_CFG_OFFSET (FDB_MAC_TBL_OFFSET + FDB_MAC_TBL_SIZE) 272 + #define FDB_PORT_STP_CFG_SIZE sizeof(struct fdb_stp_config) 273 + #define FDB_PORT2_STP_CFG_OFFSET (FDB_PORT1_STP_CFG_OFFSET + \ 274 + FDB_PORT_STP_CFG_SIZE) 275 + 276 + #define FDB_FLOOD_ENABLE_FLAGS_OFFSET (FDB_PORT2_STP_CFG_OFFSET + \ 277 + FDB_PORT_STP_CFG_SIZE) 278 + #define FDB_FLOOD_ENABLE_FLAGS_SIZE sizeof(struct fdb_flood_config) 279 + 280 + #define FDB_LOCKS_OFFSET (FDB_FLOOD_ENABLE_FLAGS_OFFSET + \ 281 + FDB_FLOOD_ENABLE_FLAGS_SIZE) 333 282 334 283 #endif /* __ICSS_SWITCH_H */
+333
drivers/net/ethernet/ti/icssm/icssm_switchdev.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + 3 + /* Texas Instruments ICSSM Ethernet Driver 4 + * 5 + * Copyright (C) 2018-2022 Texas Instruments Incorporated - https://www.ti.com/ 6 + * 7 + */ 8 + 9 + #include <linux/etherdevice.h> 10 + #include <linux/kernel.h> 11 + #include <linux/remoteproc.h> 12 + #include <net/switchdev.h> 13 + 14 + #include "icssm_prueth.h" 15 + #include "icssm_prueth_switch.h" 16 + #include "icssm_prueth_fdb_tbl.h" 17 + 18 + /* switchev event work */ 19 + struct icssm_sw_event_work { 20 + netdevice_tracker ndev_tracker; 21 + struct work_struct work; 22 + struct switchdev_notifier_fdb_info fdb_info; 23 + struct prueth_emac *emac; 24 + unsigned long event; 25 + }; 26 + 27 + void icssm_prueth_sw_set_stp_state(struct prueth *prueth, 28 + enum prueth_port port, u8 state) 29 + { 30 + struct fdb_tbl *t = prueth->fdb_tbl; 31 + 32 + writeb(state, port - 1 ? (void __iomem *)&t->port2_stp_cfg->state : 33 + (void __iomem *)&t->port1_stp_cfg->state); 34 + } 35 + 36 + u8 icssm_prueth_sw_get_stp_state(struct prueth *prueth, enum prueth_port port) 37 + { 38 + struct fdb_tbl *t = prueth->fdb_tbl; 39 + u8 state; 40 + 41 + state = readb(port - 1 ? (void __iomem *)&t->port2_stp_cfg->state : 42 + (void __iomem *)&t->port1_stp_cfg->state); 43 + return state; 44 + } 45 + 46 + static int icssm_prueth_sw_attr_set(struct net_device *ndev, const void *ctx, 47 + const struct switchdev_attr *attr, 48 + struct netlink_ext_ack *extack) 49 + { 50 + struct prueth_emac *emac = netdev_priv(ndev); 51 + struct prueth *prueth = emac->prueth; 52 + int err = 0; 53 + u8 o_state; 54 + 55 + /* Interface is not up */ 56 + if (!prueth->fdb_tbl) 57 + return 0; 58 + 59 + switch (attr->id) { 60 + case SWITCHDEV_ATTR_ID_PORT_STP_STATE: 61 + o_state = icssm_prueth_sw_get_stp_state(prueth, emac->port_id); 62 + icssm_prueth_sw_set_stp_state(prueth, emac->port_id, 63 + attr->u.stp_state); 64 + 65 + if (o_state != attr->u.stp_state) 66 + icssm_prueth_sw_purge_fdb(emac); 67 + 68 + dev_dbg(prueth->dev, "attr set: stp state:%u port:%u\n", 69 + attr->u.stp_state, emac->port_id); 70 + break; 71 + default: 72 + err = -EOPNOTSUPP; 73 + break; 74 + } 75 + 76 + return err; 77 + } 78 + 79 + static void icssm_prueth_sw_fdb_offload(struct net_device *ndev, 80 + struct switchdev_notifier_fdb_info *rcv) 81 + { 82 + struct switchdev_notifier_fdb_info info; 83 + 84 + info.addr = rcv->addr; 85 + info.vid = rcv->vid; 86 + call_switchdev_notifiers(SWITCHDEV_FDB_OFFLOADED, ndev, &info.info, 87 + NULL); 88 + } 89 + 90 + /** 91 + * icssm_sw_event_work - insert/delete fdb entry 92 + * 93 + * @work: work structure 94 + * 95 + */ 96 + static void icssm_sw_event_work(struct work_struct *work) 97 + { 98 + struct icssm_sw_event_work *switchdev_work = 99 + container_of(work, struct icssm_sw_event_work, work); 100 + struct prueth_emac *emac = switchdev_work->emac; 101 + struct switchdev_notifier_fdb_info *fdb; 102 + struct prueth *prueth = emac->prueth; 103 + int port = emac->port_id; 104 + 105 + rtnl_lock(); 106 + 107 + /* Interface is not up */ 108 + if (!emac->prueth->fdb_tbl) 109 + goto free; 110 + 111 + switch (switchdev_work->event) { 112 + case SWITCHDEV_FDB_ADD_TO_DEVICE: 113 + fdb = &switchdev_work->fdb_info; 114 + dev_dbg(prueth->dev, 115 + "prueth fdb add: MACID = %pM vid = %u flags = %u -- port %d\n", 116 + fdb->addr, fdb->vid, fdb->added_by_user, port); 117 + 118 + if (!fdb->added_by_user) 119 + break; 120 + 121 + if (fdb->is_local) 122 + break; 123 + 124 + icssm_prueth_sw_fdb_add(emac, fdb); 125 + icssm_prueth_sw_fdb_offload(emac->ndev, fdb); 126 + break; 127 + case SWITCHDEV_FDB_DEL_TO_DEVICE: 128 + fdb = &switchdev_work->fdb_info; 129 + dev_dbg(prueth->dev, 130 + "prueth fdb del: MACID = %pM vid = %u flags = %u -- port %d\n", 131 + fdb->addr, fdb->vid, fdb->added_by_user, port); 132 + 133 + if (fdb->is_local) 134 + break; 135 + 136 + icssm_prueth_sw_fdb_del(emac, fdb); 137 + break; 138 + default: 139 + break; 140 + } 141 + 142 + free: 143 + rtnl_unlock(); 144 + 145 + netdev_put(emac->ndev, &switchdev_work->ndev_tracker); 146 + kfree(switchdev_work->fdb_info.addr); 147 + kfree(switchdev_work); 148 + } 149 + 150 + /* called under rcu_read_lock() */ 151 + static int icssm_prueth_sw_switchdev_event(struct notifier_block *unused, 152 + unsigned long event, void *ptr) 153 + { 154 + struct net_device *ndev = switchdev_notifier_info_to_dev(ptr); 155 + struct switchdev_notifier_fdb_info *fdb_info = ptr; 156 + struct prueth_emac *emac = netdev_priv(ndev); 157 + struct icssm_sw_event_work *switchdev_work; 158 + int err; 159 + 160 + if (!icssm_prueth_sw_port_dev_check(ndev)) 161 + return NOTIFY_DONE; 162 + 163 + if (event == SWITCHDEV_PORT_ATTR_SET) { 164 + err = switchdev_handle_port_attr_set 165 + (ndev, ptr, icssm_prueth_sw_port_dev_check, 166 + icssm_prueth_sw_attr_set); 167 + return notifier_from_errno(err); 168 + } 169 + 170 + switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC); 171 + if (WARN_ON(!switchdev_work)) 172 + return NOTIFY_BAD; 173 + 174 + INIT_WORK(&switchdev_work->work, icssm_sw_event_work); 175 + switchdev_work->emac = emac; 176 + switchdev_work->event = event; 177 + 178 + switch (event) { 179 + case SWITCHDEV_FDB_ADD_TO_DEVICE: 180 + case SWITCHDEV_FDB_DEL_TO_DEVICE: 181 + memcpy(&switchdev_work->fdb_info, ptr, 182 + sizeof(switchdev_work->fdb_info)); 183 + switchdev_work->fdb_info.addr = kzalloc(ETH_ALEN, GFP_ATOMIC); 184 + if (!switchdev_work->fdb_info.addr) 185 + goto err_addr_alloc; 186 + ether_addr_copy((u8 *)switchdev_work->fdb_info.addr, 187 + fdb_info->addr); 188 + netdev_hold(ndev, &switchdev_work->ndev_tracker, GFP_ATOMIC); 189 + break; 190 + default: 191 + kfree(switchdev_work); 192 + return NOTIFY_DONE; 193 + } 194 + 195 + queue_work(system_long_wq, &switchdev_work->work); 196 + 197 + return NOTIFY_DONE; 198 + 199 + err_addr_alloc: 200 + kfree(switchdev_work); 201 + return NOTIFY_BAD; 202 + } 203 + 204 + static int icssm_prueth_switchdev_obj_add(struct net_device *ndev, 205 + const void *ctx, 206 + const struct switchdev_obj *obj, 207 + struct netlink_ext_ack *extack) 208 + { 209 + struct switchdev_obj_port_mdb *mdb = SWITCHDEV_OBJ_PORT_MDB(obj); 210 + struct prueth_emac *emac = netdev_priv(ndev); 211 + struct prueth *prueth = emac->prueth; 212 + int ret = 0; 213 + u8 hash; 214 + 215 + switch (obj->id) { 216 + case SWITCHDEV_OBJ_ID_HOST_MDB: 217 + dev_dbg(prueth->dev, "MDB add: %s: vid %u:%pM port: %x\n", 218 + ndev->name, mdb->vid, mdb->addr, emac->port_id); 219 + hash = icssm_emac_get_mc_hash(mdb->addr, emac->mc_filter_mask); 220 + icssm_emac_mc_filter_bin_allow(emac, hash); 221 + break; 222 + default: 223 + ret = -EOPNOTSUPP; 224 + break; 225 + } 226 + 227 + return ret; 228 + } 229 + 230 + static int icssm_prueth_switchdev_obj_del(struct net_device *ndev, 231 + const void *ctx, 232 + const struct switchdev_obj *obj) 233 + { 234 + struct switchdev_obj_port_mdb *mdb = SWITCHDEV_OBJ_PORT_MDB(obj); 235 + struct prueth_emac *emac = netdev_priv(ndev); 236 + struct prueth *prueth = emac->prueth; 237 + struct netdev_hw_addr *ha; 238 + u8 hash, tmp_hash; 239 + int ret = 0; 240 + u8 *mask; 241 + 242 + switch (obj->id) { 243 + case SWITCHDEV_OBJ_ID_HOST_MDB: 244 + dev_dbg(prueth->dev, "MDB del: %s: vid %u:%pM port: %x\n", 245 + ndev->name, mdb->vid, mdb->addr, emac->port_id); 246 + if (prueth->hw_bridge_dev) { 247 + mask = emac->mc_filter_mask; 248 + hash = icssm_emac_get_mc_hash(mdb->addr, mask); 249 + netdev_for_each_mc_addr(ha, prueth->hw_bridge_dev) { 250 + tmp_hash = icssm_emac_get_mc_hash(ha->addr, 251 + mask); 252 + /* Another MC address is in the bin. 253 + * Don't disable. 254 + */ 255 + if (tmp_hash == hash) 256 + return 0; 257 + } 258 + icssm_emac_mc_filter_bin_disallow(emac, hash); 259 + } 260 + break; 261 + default: 262 + ret = -EOPNOTSUPP; 263 + break; 264 + } 265 + 266 + return ret; 267 + } 268 + 269 + /* switchdev notifiers */ 270 + static int icssm_prueth_sw_blocking_event(struct notifier_block *unused, 271 + unsigned long event, void *ptr) 272 + { 273 + struct net_device *ndev = switchdev_notifier_info_to_dev(ptr); 274 + int err; 275 + 276 + switch (event) { 277 + case SWITCHDEV_PORT_OBJ_ADD: 278 + err = switchdev_handle_port_obj_add 279 + (ndev, ptr, icssm_prueth_sw_port_dev_check, 280 + icssm_prueth_switchdev_obj_add); 281 + return notifier_from_errno(err); 282 + 283 + case SWITCHDEV_PORT_OBJ_DEL: 284 + err = switchdev_handle_port_obj_del 285 + (ndev, ptr, icssm_prueth_sw_port_dev_check, 286 + icssm_prueth_switchdev_obj_del); 287 + return notifier_from_errno(err); 288 + 289 + case SWITCHDEV_PORT_ATTR_SET: 290 + err = switchdev_handle_port_attr_set 291 + (ndev, ptr, icssm_prueth_sw_port_dev_check, 292 + icssm_prueth_sw_attr_set); 293 + return notifier_from_errno(err); 294 + 295 + default: 296 + break; 297 + } 298 + 299 + return NOTIFY_DONE; 300 + } 301 + 302 + int icssm_prueth_sw_register_notifiers(struct prueth *prueth) 303 + { 304 + int ret = 0; 305 + 306 + prueth->prueth_switchdev_nb.notifier_call = 307 + &icssm_prueth_sw_switchdev_event; 308 + ret = register_switchdev_notifier(&prueth->prueth_switchdev_nb); 309 + if (ret) { 310 + dev_err(prueth->dev, 311 + "register switchdev notifier failed ret:%d\n", ret); 312 + return ret; 313 + } 314 + 315 + prueth->prueth_switchdev_bl_nb.notifier_call = 316 + &icssm_prueth_sw_blocking_event; 317 + ret = register_switchdev_blocking_notifier 318 + (&prueth->prueth_switchdev_bl_nb); 319 + if (ret) { 320 + dev_err(prueth->dev, 321 + "register switchdev blocking notifier failed ret:%d\n", 322 + ret); 323 + unregister_switchdev_notifier(&prueth->prueth_switchdev_nb); 324 + } 325 + 326 + return ret; 327 + } 328 + 329 + void icssm_prueth_sw_unregister_notifiers(struct prueth *prueth) 330 + { 331 + unregister_switchdev_blocking_notifier(&prueth->prueth_switchdev_bl_nb); 332 + unregister_switchdev_notifier(&prueth->prueth_switchdev_nb); 333 + }
+13
drivers/net/ethernet/ti/icssm/icssm_switchdev.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* Copyright (C) 2020-2021 Texas Instruments Incorporated - https://www.ti.com 3 + */ 4 + 5 + #ifndef __NET_TI_ICSSM_SWITCHDEV_H 6 + #define __NET_TI_ICSSM_SWITCHDEV_H 7 + 8 + #include "icssm_prueth.h" 9 + 10 + int icssm_prueth_sw_register_notifiers(struct prueth *prueth); 11 + void icssm_prueth_sw_unregister_notifiers(struct prueth *prueth); 12 + bool icssm_prueth_sw_port_dev_check(const struct net_device *ndev); 13 + #endif /* __NET_TI_ICSSM_SWITCHDEV_H */
+120
drivers/net/ethernet/ti/icssm/icssm_vlan_mcast_filter_mmap.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + 3 + /* Copyright (C) 2015-2021 Texas Instruments Incorporated - https://www.ti.com 4 + * 5 + * This file contains VLAN/Multicast filtering feature memory map 6 + * 7 + */ 8 + 9 + #ifndef ICSS_VLAN_MULTICAST_FILTER_MM_H 10 + #define ICSS_VLAN_MULTICAST_FILTER_MM_H 11 + 12 + /* VLAN/Multicast filter defines & offsets, 13 + * present on both PRU0 and PRU1 DRAM 14 + */ 15 + 16 + /* Feature enable/disable values for multicast filtering */ 17 + #define ICSS_EMAC_FW_MULTICAST_FILTER_CTRL_DISABLED 0x00 18 + #define ICSS_EMAC_FW_MULTICAST_FILTER_CTRL_ENABLED 0x01 19 + 20 + /* Feature enable/disable values for VLAN filtering */ 21 + #define ICSS_EMAC_FW_VLAN_FILTER_CTRL_DISABLED 0x00 22 + #define ICSS_EMAC_FW_VLAN_FILTER_CTRL_ENABLED 0x01 23 + 24 + /* Add/remove multicast mac id for filtering bin */ 25 + #define ICSS_EMAC_FW_MULTICAST_FILTER_HOST_RCV_ALLOWED 0x01 26 + #define ICSS_EMAC_FW_MULTICAST_FILTER_HOST_RCV_NOT_ALLOWED 0x00 27 + 28 + /* Default HASH value for the multicast filtering Mask */ 29 + #define ICSS_EMAC_FW_MULTICAST_FILTER_INIT_VAL 0xFF 30 + 31 + /* Size requirements for Multicast filtering feature */ 32 + #define ICSS_EMAC_FW_MULTICAST_TABLE_SIZE_BYTES 256 33 + #define ICSS_EMAC_FW_MULTICAST_FILTER_MASK_SIZE_BYTES 6 34 + #define ICSS_EMAC_FW_MULTICAST_FILTER_CTRL_SIZE_BYTES 1 35 + #define ICSS_EMAC_FW_MULTICAST_FILTER_MASK_OVERRIDE_STATUS_SIZE_BYTES 1 36 + #define ICSS_EMAC_FW_MULTICAST_FILTER_DROP_CNT_SIZE_BYTES 4 37 + 38 + /* Size requirements for VLAN filtering feature : 4096 bits = 512 bytes */ 39 + #define ICSS_EMAC_FW_VLAN_FILTER_TABLE_SIZE_BYTES 512 40 + #define ICSS_EMAC_FW_VLAN_FILTER_CTRL_SIZE_BYTES 1 41 + #define ICSS_EMAC_FW_VLAN_FILTER_DROP_CNT_SIZE_BYTES 4 42 + 43 + /* Mask override set status */ 44 + #define ICSS_EMAC_FW_MULTICAST_FILTER_MASK_OVERRIDE_SET 1 45 + /* Mask override not set status */ 46 + #define ICSS_EMAC_FW_MULTICAST_FILTER_MASK_OVERRIDE_NOT_SET 0 47 + /* 6 bytes HASH Mask for the MAC */ 48 + #define ICSS_EMAC_FW_MULTICAST_FILTER_MASK_OFFSET 0xF4 49 + /* 0 -> multicast filtering disabled | 1 -> multicast filtering enabled */ 50 + #define ICSS_EMAC_FW_MULTICAST_FILTER_CTRL_OFFSET \ 51 + (ICSS_EMAC_FW_MULTICAST_FILTER_MASK_OFFSET + \ 52 + ICSS_EMAC_FW_MULTICAST_FILTER_MASK_SIZE_BYTES) 53 + /* Status indicating if the HASH override is done or not: 0: no, 1: yes */ 54 + #define ICSS_EMAC_FW_MULTICAST_FILTER_OVERRIDE_STATUS \ 55 + (ICSS_EMAC_FW_MULTICAST_FILTER_CTRL_OFFSET + \ 56 + ICSS_EMAC_FW_MULTICAST_FILTER_CTRL_SIZE_BYTES) 57 + /* Multicast drop statistics */ 58 + #define ICSS_EMAC_FW_MULTICAST_FILTER_DROP_CNT_OFFSET \ 59 + (ICSS_EMAC_FW_MULTICAST_FILTER_OVERRIDE_STATUS +\ 60 + ICSS_EMAC_FW_MULTICAST_FILTER_MASK_OVERRIDE_STATUS_SIZE_BYTES) 61 + /* Multicast table */ 62 + #define ICSS_EMAC_FW_MULTICAST_FILTER_TABLE \ 63 + (ICSS_EMAC_FW_MULTICAST_FILTER_DROP_CNT_OFFSET +\ 64 + ICSS_EMAC_FW_MULTICAST_FILTER_DROP_CNT_SIZE_BYTES) 65 + 66 + /* Multicast filter defines & offsets for LRE 67 + */ 68 + #define ICSS_LRE_FW_MULTICAST_TABLE_SEARCH_OP_CONTROL_BIT 0xE0 69 + /* one byte field : 70 + * 0 -> multicast filtering disabled 71 + * 1 -> multicast filtering enabled 72 + */ 73 + #define ICSS_LRE_FW_MULTICAST_FILTER_MASK 0xE4 74 + #define ICSS_LRE_FW_MULTICAST_FILTER_TABLE 0x100 75 + 76 + /* VLAN table Offsets */ 77 + #define ICSS_EMAC_FW_VLAN_FLTR_TBL_BASE_ADDR 0x200 78 + #define ICSS_EMAC_FW_VLAN_FILTER_CTRL_BITMAP_OFFSET 0xEF 79 + #define ICSS_EMAC_FW_VLAN_FILTER_DROP_CNT_OFFSET \ 80 + (ICSS_EMAC_FW_VLAN_FILTER_CTRL_BITMAP_OFFSET + \ 81 + ICSS_EMAC_FW_VLAN_FILTER_CTRL_SIZE_BYTES) 82 + 83 + /* VLAN filter Control Bit maps */ 84 + /* one bit field, bit 0: | 0 : VLAN filter disabled (default), 85 + * 1: VLAN filter enabled 86 + */ 87 + #define ICSS_EMAC_FW_VLAN_FILTER_CTRL_ENABLE_BIT 0 88 + /* one bit field, bit 1: | 0 : untagged host rcv allowed (default), 89 + * 1: untagged host rcv not allowed 90 + */ 91 + #define ICSS_EMAC_FW_VLAN_FILTER_UNTAG_HOST_RCV_ALLOW_CTRL_BIT 1 92 + /* one bit field, bit 1: | 0 : priotag host rcv allowed (default), 93 + * 1: priotag host rcv not allowed 94 + */ 95 + #define ICSS_EMAC_FW_VLAN_FILTER_PRIOTAG_HOST_RCV_ALLOW_CTRL_BIT 2 96 + /* one bit field, bit 1: | 0 : skip sv vlan flow 97 + * :1 : take sv vlan flow (not applicable for dual emac ) 98 + */ 99 + #define ICSS_EMAC_FW_VLAN_FILTER_SV_VLAN_FLOW_HOST_RCV_ALLOW_CTRL_BIT 3 100 + 101 + /* VLAN IDs */ 102 + #define ICSS_EMAC_FW_VLAN_FILTER_PRIOTAG_VID 0 103 + #define ICSS_EMAC_FW_VLAN_FILTER_VID_MIN 0x0000 104 + #define ICSS_EMAC_FW_VLAN_FILTER_VID_MAX 0x0FFF 105 + 106 + /* VLAN Filtering Commands */ 107 + #define ICSS_EMAC_FW_VLAN_FILTER_ADD_VLAN_VID_CMD 0x00 108 + #define ICSS_EMAC_FW_VLAN_FILTER_REMOVE_VLAN_VID_CMD 0x01 109 + 110 + /* Switch defines for VLAN/MC filtering */ 111 + /* SRAM 112 + * VLAN filter defines & offsets 113 + */ 114 + #define ICSS_LRE_FW_VLAN_FLTR_CTRL_BYTE 0x1FE 115 + /* one bit field | 0 : VLAN filter disabled 116 + * | 1 : VLAN filter enabled 117 + */ 118 + #define ICSS_LRE_FW_VLAN_FLTR_TBL_BASE_ADDR 0x200 119 + 120 + #endif /* ICSS_MULTICAST_FILTER_MM_H */