summaryrefslogtreecommitdiff
path: root/drivers/net/ethernet/ti
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/ethernet/ti')
-rw-r--r--drivers/net/ethernet/ti/Kconfig1
-rw-r--r--drivers/net/ethernet/ti/Makefile2
-rw-r--r--drivers/net/ethernet/ti/am65-cpsw-ethtool.c5
-rw-r--r--drivers/net/ethernet/ti/cpsw_ale.c5
-rw-r--r--drivers/net/ethernet/ti/cpsw_ethtool.c5
-rw-r--r--drivers/net/ethernet/ti/cpsw_new.c15
-rw-r--r--drivers/net/ethernet/ti/icssg/icssg_common.c1
-rw-r--r--drivers/net/ethernet/ti/icssg/icssg_prueth.c13
-rw-r--r--drivers/net/ethernet/ti/icssg/icssg_prueth.h1
-rw-r--r--drivers/net/ethernet/ti/icssg/icssg_prueth_sr1.c9
-rw-r--r--drivers/net/ethernet/ti/icssm/icssm_prueth.c545
-rw-r--r--drivers/net/ethernet/ti/icssm/icssm_prueth.h20
-rw-r--r--drivers/net/ethernet/ti/icssm/icssm_prueth_fdb_tbl.h76
-rw-r--r--drivers/net/ethernet/ti/icssm/icssm_prueth_switch.c1065
-rw-r--r--drivers/net/ethernet/ti/icssm/icssm_prueth_switch.h37
-rw-r--r--drivers/net/ethernet/ti/icssm/icssm_switch.h103
-rw-r--r--drivers/net/ethernet/ti/icssm/icssm_switchdev.c333
-rw-r--r--drivers/net/ethernet/ti/icssm/icssm_switchdev.h13
-rw-r--r--drivers/net/ethernet/ti/icssm/icssm_vlan_mcast_filter_mmap.h120
-rw-r--r--drivers/net/ethernet/ti/netcp.h8
-rw-r--r--drivers/net/ethernet/ti/netcp_core.c16
21 files changed, 2317 insertions, 76 deletions
diff --git a/drivers/net/ethernet/ti/Kconfig b/drivers/net/ethernet/ti/Kconfig
index fe5b2926d8ab..c60b04921c62 100644
--- a/drivers/net/ethernet/ti/Kconfig
+++ b/drivers/net/ethernet/ti/Kconfig
@@ -192,6 +192,7 @@ config TI_ICSSG_PRUETH
depends on NET_SWITCHDEV
depends on ARCH_K3 && OF && TI_K3_UDMA_GLUE_LAYER
depends on PTP_1588_CLOCK_OPTIONAL
+ depends on HSR || !HSR
help
Support dual Gigabit Ethernet ports over the ICSSG PRU Subsystem.
This subsystem is available starting with the AM65 platform.
diff --git a/drivers/net/ethernet/ti/Makefile b/drivers/net/ethernet/ti/Makefile
index 93c0a4d0e33a..6da50f4b7c2e 100644
--- a/drivers/net/ethernet/ti/Makefile
+++ b/drivers/net/ethernet/ti/Makefile
@@ -4,7 +4,7 @@
#
obj-$(CONFIG_TI_PRUETH) += icssm-prueth.o
-icssm-prueth-y := icssm/icssm_prueth.o
+icssm-prueth-y := icssm/icssm_prueth.o icssm/icssm_prueth_switch.o icssm/icssm_switchdev.o
obj-$(CONFIG_TI_CPSW) += cpsw-common.o
obj-$(CONFIG_TI_DAVINCI_EMAC) += cpsw-common.o
diff --git a/drivers/net/ethernet/ti/am65-cpsw-ethtool.c b/drivers/net/ethernet/ti/am65-cpsw-ethtool.c
index c57497074ae6..98d60da7cc3b 100644
--- a/drivers/net/ethernet/ti/am65-cpsw-ethtool.c
+++ b/drivers/net/ethernet/ti/am65-cpsw-ethtool.c
@@ -391,11 +391,8 @@ static int am65_cpsw_ethtool_op_begin(struct net_device *ndev)
static void am65_cpsw_ethtool_op_complete(struct net_device *ndev)
{
struct am65_cpsw_common *common = am65_ndev_to_common(ndev);
- int ret;
- ret = pm_runtime_put(common->dev);
- if (ret < 0 && ret != -EBUSY)
- dev_err(common->dev, "ethtool complete failed %d\n", ret);
+ pm_runtime_put(common->dev);
}
static void am65_cpsw_get_drvinfo(struct net_device *ndev,
diff --git a/drivers/net/ethernet/ti/cpsw_ale.c b/drivers/net/ethernet/ti/cpsw_ale.c
index fbe35af615a6..bb969dd435b4 100644
--- a/drivers/net/ethernet/ti/cpsw_ale.c
+++ b/drivers/net/ethernet/ti/cpsw_ale.c
@@ -23,11 +23,6 @@
#define BITMASK(bits) (BIT(bits) - 1)
-#define ALE_VERSION_MAJOR(rev, mask) (((rev) >> 8) & (mask))
-#define ALE_VERSION_MINOR(rev) (rev & 0xff)
-#define ALE_VERSION_1R3 0x0103
-#define ALE_VERSION_1R4 0x0104
-
/* ALE Registers */
#define ALE_IDVER 0x00
#define ALE_STATUS 0x04
diff --git a/drivers/net/ethernet/ti/cpsw_ethtool.c b/drivers/net/ethernet/ti/cpsw_ethtool.c
index bdc4db0d169c..a43f75ee269e 100644
--- a/drivers/net/ethernet/ti/cpsw_ethtool.c
+++ b/drivers/net/ethernet/ti/cpsw_ethtool.c
@@ -374,11 +374,8 @@ int cpsw_ethtool_op_begin(struct net_device *ndev)
void cpsw_ethtool_op_complete(struct net_device *ndev)
{
struct cpsw_priv *priv = netdev_priv(ndev);
- int ret;
- ret = pm_runtime_put(priv->cpsw->dev);
- if (ret < 0)
- cpsw_err(priv, drv, "ethtool complete failed %d\n", ret);
+ pm_runtime_put(priv->cpsw->dev);
}
void cpsw_get_channels(struct net_device *ndev, struct ethtool_channels *ch)
diff --git a/drivers/net/ethernet/ti/cpsw_new.c b/drivers/net/ethernet/ti/cpsw_new.c
index 21af0a10626a..7f42f58a4b03 100644
--- a/drivers/net/ethernet/ti/cpsw_new.c
+++ b/drivers/net/ethernet/ti/cpsw_new.c
@@ -1472,7 +1472,7 @@ static void cpsw_unregister_ports(struct cpsw_common *cpsw)
for (i = 0; i < cpsw->data.slaves; i++) {
ndev = cpsw->slaves[i].ndev;
- if (!ndev)
+ if (!ndev || ndev->reg_state != NETREG_REGISTERED)
continue;
priv = netdev_priv(ndev);
@@ -1494,7 +1494,6 @@ static int cpsw_register_ports(struct cpsw_common *cpsw)
if (ret) {
dev_err(cpsw->dev,
"cpsw: err registering net device%d\n", i);
- cpsw->slaves[i].ndev = NULL;
break;
}
}
@@ -2003,7 +2002,7 @@ static int cpsw_probe(struct platform_device *pdev)
/* setup netdevs */
ret = cpsw_create_ports(cpsw);
if (ret)
- goto clean_unregister_netdev;
+ goto clean_cpts;
/* Grab RX and TX IRQs. Note that we also have RX_THRESHOLD and
* MISC IRQs which are always kept disabled with this driver so
@@ -2017,14 +2016,14 @@ static int cpsw_probe(struct platform_device *pdev)
0, dev_name(dev), cpsw);
if (ret < 0) {
dev_err(dev, "error attaching irq (%d)\n", ret);
- goto clean_unregister_netdev;
+ goto clean_cpts;
}
ret = devm_request_irq(dev, cpsw->irqs_table[1], cpsw_tx_interrupt,
0, dev_name(dev), cpsw);
if (ret < 0) {
dev_err(dev, "error attaching irq (%d)\n", ret);
- goto clean_unregister_netdev;
+ goto clean_cpts;
}
if (!cpsw->cpts)
@@ -2034,7 +2033,7 @@ static int cpsw_probe(struct platform_device *pdev)
0, dev_name(&pdev->dev), cpsw);
if (ret < 0) {
dev_err(dev, "error attaching misc irq (%d)\n", ret);
- goto clean_unregister_netdev;
+ goto clean_cpts;
}
/* Enable misc CPTS evnt_pend IRQ */
@@ -2043,7 +2042,7 @@ static int cpsw_probe(struct platform_device *pdev)
skip_cpts:
ret = cpsw_register_notifiers(cpsw);
if (ret)
- goto clean_unregister_netdev;
+ goto clean_cpts;
ret = cpsw_register_devlink(cpsw);
if (ret)
@@ -2065,8 +2064,6 @@ skip_cpts:
clean_unregister_notifiers:
cpsw_unregister_notifiers(cpsw);
-clean_unregister_netdev:
- cpsw_unregister_ports(cpsw);
clean_cpts:
cpts_release(cpsw->cpts);
cpdma_ctlr_destroy(cpsw->dma);
diff --git a/drivers/net/ethernet/ti/icssg/icssg_common.c b/drivers/net/ethernet/ti/icssg/icssg_common.c
index 090aa74d3ce7..0cf9dfe0fa36 100644
--- a/drivers/net/ethernet/ti/icssg/icssg_common.c
+++ b/drivers/net/ethernet/ti/icssg/icssg_common.c
@@ -1720,7 +1720,6 @@ void prueth_netdev_exit(struct prueth *prueth,
netif_napi_del(&emac->napi_rx);
pruss_release_mem_region(prueth->pruss, &emac->dram);
- destroy_workqueue(emac->cmd_wq);
free_netdev(emac->ndev);
prueth->emac[mac] = NULL;
}
diff --git a/drivers/net/ethernet/ti/icssg/icssg_prueth.c b/drivers/net/ethernet/ti/icssg/icssg_prueth.c
index f65041662173..0939994c932f 100644
--- a/drivers/net/ethernet/ti/icssg/icssg_prueth.c
+++ b/drivers/net/ethernet/ti/icssg/icssg_prueth.c
@@ -1099,7 +1099,7 @@ static void emac_ndo_set_rx_mode(struct net_device *ndev)
{
struct prueth_emac *emac = netdev_priv(ndev);
- queue_work(emac->cmd_wq, &emac->rx_mode_work);
+ schedule_work(&emac->rx_mode_work);
}
static netdev_features_t emac_ndo_fix_features(struct net_device *ndev,
@@ -1451,11 +1451,6 @@ static int prueth_netdev_init(struct prueth *prueth,
emac->port_id = port;
emac->xdp_prog = NULL;
emac->ndev->pcpu_stat_type = NETDEV_PCPU_STAT_TSTATS;
- emac->cmd_wq = create_singlethread_workqueue("icssg_cmd_wq");
- if (!emac->cmd_wq) {
- ret = -ENOMEM;
- goto free_ndev;
- }
INIT_WORK(&emac->rx_mode_work, emac_ndo_set_rx_mode_work);
INIT_DELAYED_WORK(&emac->stats_work, icssg_stats_work_handler);
@@ -1467,7 +1462,7 @@ static int prueth_netdev_init(struct prueth *prueth,
if (ret) {
dev_err(prueth->dev, "unable to get DRAM: %d\n", ret);
ret = -ENOMEM;
- goto free_wq;
+ goto free_ndev;
}
emac->tx_ch_num = 1;
@@ -1566,8 +1561,6 @@ static int prueth_netdev_init(struct prueth *prueth,
free:
pruss_release_mem_region(prueth->pruss, &emac->dram);
-free_wq:
- destroy_workqueue(emac->cmd_wq);
free_ndev:
emac->ndev = NULL;
prueth->emac[mac] = NULL;
@@ -2236,6 +2229,7 @@ netdev_unregister:
prueth->emac[i]->ndev->phydev = NULL;
}
unregister_netdev(prueth->registered_netdevs[i]);
+ disable_work_sync(&prueth->emac[i]->rx_mode_work);
}
netdev_exit:
@@ -2295,6 +2289,7 @@ static void prueth_remove(struct platform_device *pdev)
phy_disconnect(prueth->emac[i]->ndev->phydev);
prueth->emac[i]->ndev->phydev = NULL;
unregister_netdev(prueth->registered_netdevs[i]);
+ disable_work_sync(&prueth->emac[i]->rx_mode_work);
}
for (i = 0; i < PRUETH_NUM_MACS; i++) {
diff --git a/drivers/net/ethernet/ti/icssg/icssg_prueth.h b/drivers/net/ethernet/ti/icssg/icssg_prueth.h
index 10eadd356650..3d94fa5a7ac1 100644
--- a/drivers/net/ethernet/ti/icssg/icssg_prueth.h
+++ b/drivers/net/ethernet/ti/icssg/icssg_prueth.h
@@ -236,7 +236,6 @@ struct prueth_emac {
/* Mutex to serialize access to firmware command interface */
struct mutex cmd_lock;
struct work_struct rx_mode_work;
- struct workqueue_struct *cmd_wq;
struct pruss_mem_region dram;
diff --git a/drivers/net/ethernet/ti/icssg/icssg_prueth_sr1.c b/drivers/net/ethernet/ti/icssg/icssg_prueth_sr1.c
index 7bb4f0d850cc..b8115ca47082 100644
--- a/drivers/net/ethernet/ti/icssg/icssg_prueth_sr1.c
+++ b/drivers/net/ethernet/ti/icssg/icssg_prueth_sr1.c
@@ -783,11 +783,6 @@ static int prueth_netdev_init(struct prueth *prueth,
emac->prueth = prueth;
emac->ndev = ndev;
emac->port_id = port;
- emac->cmd_wq = create_singlethread_workqueue("icssg_cmd_wq");
- if (!emac->cmd_wq) {
- ret = -ENOMEM;
- goto free_ndev;
- }
INIT_DELAYED_WORK(&emac->stats_work, icssg_stats_work_handler);
@@ -798,7 +793,7 @@ static int prueth_netdev_init(struct prueth *prueth,
if (ret) {
dev_err(prueth->dev, "unable to get DRAM: %d\n", ret);
ret = -ENOMEM;
- goto free_wq;
+ goto free_ndev;
}
/* SR1.0 uses a dedicated high priority channel
@@ -883,8 +878,6 @@ static int prueth_netdev_init(struct prueth *prueth,
free:
pruss_release_mem_region(prueth->pruss, &emac->dram);
-free_wq:
- destroy_workqueue(emac->cmd_wq);
free_ndev:
emac->ndev = NULL;
prueth->emac[mac] = NULL;
diff --git a/drivers/net/ethernet/ti/icssm/icssm_prueth.c b/drivers/net/ethernet/ti/icssm/icssm_prueth.c
index 293b7af04263..53bbd9290904 100644
--- a/drivers/net/ethernet/ti/icssm/icssm_prueth.c
+++ b/drivers/net/ethernet/ti/icssm/icssm_prueth.c
@@ -29,6 +29,8 @@
#include <net/pkt_cls.h>
#include "icssm_prueth.h"
+#include "icssm_prueth_switch.h"
+#include "icssm_vlan_mcast_filter_mmap.h"
#include "../icssg/icssg_mii_rt.h"
#include "../icssg/icss_iep.h"
@@ -145,7 +147,7 @@ static const struct prueth_queue_info queue_infos[][NUM_QUEUES] = {
},
};
-static const struct prueth_queue_desc queue_descs[][NUM_QUEUES] = {
+const struct prueth_queue_desc queue_descs[][NUM_QUEUES] = {
[PRUETH_PORT_QUEUE_HOST] = {
{ .rd_ptr = P0_Q1_BD_OFFSET, .wr_ptr = P0_Q1_BD_OFFSET, },
{ .rd_ptr = P0_Q2_BD_OFFSET, .wr_ptr = P0_Q2_BD_OFFSET, },
@@ -205,9 +207,9 @@ static void icssm_prueth_hostconfig(struct prueth *prueth)
static void icssm_prueth_mii_init(struct prueth *prueth)
{
+ u32 txcfg_reg, txcfg, txcfg2;
struct regmap *mii_rt;
u32 rxcfg_reg, rxcfg;
- u32 txcfg_reg, txcfg;
mii_rt = prueth->mii_rt;
@@ -235,17 +237,23 @@ static void icssm_prueth_mii_init(struct prueth *prueth)
(TX_START_DELAY << PRUSS_MII_RT_TXCFG_TX_START_DELAY_SHIFT) |
(TX_CLK_DELAY_100M << PRUSS_MII_RT_TXCFG_TX_CLK_DELAY_SHIFT);
+ txcfg2 = txcfg;
+ if (!PRUETH_IS_EMAC(prueth))
+ txcfg2 |= PRUSS_MII_RT_TXCFG_TX_MUX_SEL;
+
/* Configuration of Port 0 Tx */
txcfg_reg = PRUSS_MII_RT_TXCFG0;
- regmap_write(mii_rt, txcfg_reg, txcfg);
+ regmap_write(mii_rt, txcfg_reg, txcfg2);
- txcfg |= PRUSS_MII_RT_TXCFG_TX_MUX_SEL;
+ txcfg2 = txcfg;
+ if (PRUETH_IS_EMAC(prueth))
+ txcfg2 |= PRUSS_MII_RT_TXCFG_TX_MUX_SEL;
/* Configuration of Port 1 Tx */
txcfg_reg = PRUSS_MII_RT_TXCFG1;
- regmap_write(mii_rt, txcfg_reg, txcfg);
+ regmap_write(mii_rt, txcfg_reg, txcfg2);
txcfg_reg = PRUSS_MII_RT_RX_FRMS0;
@@ -292,7 +300,10 @@ static void icssm_prueth_hostinit(struct prueth *prueth)
icssm_prueth_clearmem(prueth, PRUETH_MEM_DRAM1);
/* Initialize host queues in shared RAM */
- icssm_prueth_hostconfig(prueth);
+ if (!PRUETH_IS_EMAC(prueth))
+ icssm_prueth_sw_hostconfig(prueth);
+ else
+ icssm_prueth_hostconfig(prueth);
/* Configure MII_RT */
icssm_prueth_mii_init(prueth);
@@ -499,19 +510,24 @@ static int icssm_prueth_tx_enqueue(struct prueth_emac *emac,
struct prueth_queue_desc __iomem *queue_desc;
const struct prueth_queue_info *txqueue;
struct net_device *ndev = emac->ndev;
+ struct prueth *prueth = emac->prueth;
unsigned int buffer_desc_count;
int free_blocks, update_block;
bool buffer_wrapped = false;
int write_block, read_block;
void *src_addr, *dst_addr;
int pkt_block_size;
+ void __iomem *sram;
void __iomem *dram;
int txport, pktlen;
u16 update_wr_ptr;
u32 wr_buf_desc;
void *ocmc_ram;
- dram = emac->prueth->mem[emac->dram].va;
+ if (!PRUETH_IS_EMAC(prueth))
+ dram = prueth->mem[PRUETH_MEM_DRAM1].va;
+ else
+ dram = emac->prueth->mem[emac->dram].va;
if (eth_skb_pad(skb)) {
if (netif_msg_tx_err(emac) && net_ratelimit())
netdev_err(ndev, "packet pad failed\n");
@@ -524,7 +540,10 @@ static int icssm_prueth_tx_enqueue(struct prueth_emac *emac,
pktlen = skb->len;
/* Get the tx queue */
queue_desc = emac->tx_queue_descs + queue_id;
- txqueue = &queue_infos[txport][queue_id];
+ if (!PRUETH_IS_EMAC(prueth))
+ txqueue = &sw_queue_infos[txport][queue_id];
+ else
+ txqueue = &queue_infos[txport][queue_id];
buffer_desc_count = icssm_get_buff_desc_count(txqueue);
@@ -590,7 +609,11 @@ static int icssm_prueth_tx_enqueue(struct prueth_emac *emac,
/* update first buffer descriptor */
wr_buf_desc = (pktlen << PRUETH_BD_LENGTH_SHIFT) &
PRUETH_BD_LENGTH_MASK;
- writel(wr_buf_desc, dram + readw(&queue_desc->wr_ptr));
+ sram = prueth->mem[PRUETH_MEM_SHARED_RAM].va;
+ if (!PRUETH_IS_EMAC(prueth))
+ writel(wr_buf_desc, sram + readw(&queue_desc->wr_ptr));
+ else
+ writel(wr_buf_desc, dram + readw(&queue_desc->wr_ptr));
/* update the write pointer in this queue descriptor, the firmware
* polls for this change so this will signal the start of transmission
@@ -604,7 +627,6 @@ static int icssm_prueth_tx_enqueue(struct prueth_emac *emac,
void icssm_parse_packet_info(struct prueth *prueth, u32 buffer_descriptor,
struct prueth_packet_info *pkt_info)
{
- pkt_info->shadow = !!(buffer_descriptor & PRUETH_BD_SHADOW_MASK);
pkt_info->port = (buffer_descriptor & PRUETH_BD_PORT_MASK) >>
PRUETH_BD_PORT_SHIFT;
pkt_info->length = (buffer_descriptor & PRUETH_BD_LENGTH_MASK) >>
@@ -713,11 +735,19 @@ int icssm_emac_rx_packet(struct prueth_emac *emac, u16 *bd_rd_ptr,
src_addr += actual_pkt_len;
}
+ if (PRUETH_IS_SWITCH(emac->prueth)) {
+ skb->offload_fwd_mark = READ_ONCE(emac->offload_fwd_mark);
+ if (!pkt_info->lookup_success)
+ icssm_prueth_sw_learn_fdb(emac, skb->data + ETH_ALEN);
+ }
+
skb_put(skb, actual_pkt_len);
/* send packet up the stack */
skb->protocol = eth_type_trans(skb, ndev);
+ local_bh_disable();
netif_receive_skb(skb);
+ local_bh_enable();
/* update stats */
emac->stats.rx_bytes += actual_pkt_len;
@@ -743,6 +773,7 @@ static int icssm_emac_rx_packets(struct prueth_emac *emac, int budget)
shared_ram = emac->prueth->mem[PRUETH_MEM_SHARED_RAM].va;
+ /* Start and end queue is made common for EMAC, RSTP */
start_queue = emac->rx_queue_start;
end_queue = emac->rx_queue_end;
@@ -753,8 +784,10 @@ static int icssm_emac_rx_packets(struct prueth_emac *emac, int budget)
/* search host queues for packets */
for (i = start_queue; i <= end_queue; i++) {
queue_desc = emac->rx_queue_descs + i;
- rxqueue = &queue_infos[PRUETH_PORT_HOST][i];
-
+ if (PRUETH_IS_SWITCH(emac->prueth))
+ rxqueue = &sw_queue_infos[PRUETH_PORT_HOST][i];
+ else
+ rxqueue = &queue_infos[PRUETH_PORT_HOST][i];
overflow_cnt = readb(&queue_desc->overflow_cnt);
if (overflow_cnt > 0) {
emac->stats.rx_over_errors += overflow_cnt;
@@ -879,6 +912,13 @@ static int icssm_emac_request_irqs(struct prueth_emac *emac)
return ret;
}
+/* Function to free memory related to sw */
+static void icssm_prueth_free_memory(struct prueth *prueth)
+{
+ if (PRUETH_IS_SWITCH(prueth))
+ icssm_prueth_sw_free_fdb_table(prueth);
+}
+
static void icssm_ptp_dram_init(struct prueth_emac *emac)
{
void __iomem *sram = emac->prueth->mem[PRUETH_MEM_SHARED_RAM].va;
@@ -941,20 +981,38 @@ static int icssm_emac_ndo_open(struct net_device *ndev)
if (!prueth->emac_configured)
icssm_prueth_init_ethernet_mode(prueth);
- icssm_prueth_emac_config(emac);
+ /* reset and start PRU firmware */
+ if (PRUETH_IS_SWITCH(prueth)) {
+ ret = icssm_prueth_sw_emac_config(emac);
+ if (ret)
+ return ret;
+
+ ret = icssm_prueth_sw_init_fdb_table(prueth);
+ if (ret)
+ return ret;
+ } else {
+ icssm_prueth_emac_config(emac);
+ }
if (!prueth->emac_configured) {
icssm_ptp_dram_init(emac);
ret = icss_iep_init(prueth->iep, NULL, NULL, 0);
if (ret) {
netdev_err(ndev, "Failed to initialize iep: %d\n", ret);
- goto iep_exit;
+ goto free_mem;
}
}
- ret = icssm_emac_set_boot_pru(emac, ndev);
- if (ret)
- goto iep_exit;
+ if (!PRUETH_IS_EMAC(prueth)) {
+ ret = icssm_prueth_sw_boot_prus(prueth, ndev);
+ if (ret)
+ goto iep_exit;
+ } else {
+ /* boot the PRU */
+ ret = icssm_emac_set_boot_pru(emac, ndev);
+ if (ret)
+ goto iep_exit;
+ }
ret = icssm_emac_request_irqs(emac);
if (ret)
@@ -969,19 +1027,25 @@ static int icssm_emac_ndo_open(struct net_device *ndev)
icssm_prueth_port_enable(emac, true);
prueth->emac_configured |= BIT(emac->port_id);
-
+ if (PRUETH_IS_SWITCH(prueth))
+ icssm_prueth_sw_set_stp_state(prueth, emac->port_id,
+ BR_STATE_LEARNING);
if (netif_msg_drv(emac))
dev_notice(&ndev->dev, "started\n");
return 0;
rproc_shutdown:
- rproc_shutdown(emac->pru);
+ if (!PRUETH_IS_EMAC(prueth))
+ icssm_prueth_sw_shutdown_prus(emac, ndev);
+ else
+ rproc_shutdown(emac->pru);
iep_exit:
if (!prueth->emac_configured)
icss_iep_exit(prueth->iep);
-
+free_mem:
+ icssm_prueth_free_memory(emac->prueth);
return ret;
}
@@ -1010,17 +1074,83 @@ static int icssm_emac_ndo_stop(struct net_device *ndev)
hrtimer_cancel(&emac->tx_hrtimer);
/* stop the PRU */
- rproc_shutdown(emac->pru);
+ if (!PRUETH_IS_EMAC(prueth))
+ icssm_prueth_sw_shutdown_prus(emac, ndev);
+ else
+ rproc_shutdown(emac->pru);
/* free rx interrupts */
free_irq(emac->rx_irq, ndev);
+ /* free memory related to sw */
+ icssm_prueth_free_memory(emac->prueth);
+
+ if (!prueth->emac_configured)
+ icss_iep_exit(prueth->iep);
+
if (netif_msg_drv(emac))
dev_notice(&ndev->dev, "stopped\n");
return 0;
}
+static int icssm_prueth_change_mode(struct prueth *prueth,
+ enum pruss_ethtype mode)
+{
+ bool portstatus[PRUETH_NUM_MACS];
+ struct prueth_emac *emac;
+ struct net_device *ndev;
+ int i, ret;
+
+ for (i = 0; i < PRUETH_NUM_MACS; i++) {
+ if (!prueth->emac[i]) {
+ dev_err(prueth->dev, "Unknown MAC port\n");
+ return -EINVAL;
+ }
+
+ emac = prueth->emac[i];
+ ndev = emac->ndev;
+
+ portstatus[i] = netif_running(ndev);
+ if (!portstatus[i])
+ continue;
+
+ ret = ndev->netdev_ops->ndo_stop(ndev);
+ if (ret < 0) {
+ netdev_err(ndev, "failed to stop: %d", ret);
+ return ret;
+ }
+ }
+
+ if (mode == PRUSS_ETHTYPE_EMAC || mode == PRUSS_ETHTYPE_SWITCH) {
+ prueth->eth_type = mode;
+ } else {
+ dev_err(prueth->dev, "unknown mode\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < PRUETH_NUM_MACS; i++) {
+ if (!prueth->emac[i]) {
+ dev_err(prueth->dev, "Unknown MAC port\n");
+ return -EINVAL;
+ }
+
+ emac = prueth->emac[i];
+ ndev = emac->ndev;
+
+ if (!portstatus[i])
+ continue;
+
+ ret = ndev->netdev_ops->ndo_open(ndev);
+ if (ret < 0) {
+ netdev_err(ndev, "failed to start: %d", ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
/* VLAN-tag PCP to priority queue map for EMAC/Switch/HSR/PRP used by driver
* Index is PCP val / 2.
* low - pcp 0..3 maps to Q4 for Host
@@ -1131,11 +1261,183 @@ static void icssm_emac_ndo_get_stats64(struct net_device *ndev,
stats->rx_length_errors = emac->stats.rx_length_errors;
}
+/* enable/disable MC filter */
+static void icssm_emac_mc_filter_ctrl(struct prueth_emac *emac, bool enable)
+{
+ struct prueth *prueth = emac->prueth;
+ void __iomem *mc_filter_ctrl;
+ void __iomem *ram;
+ u32 reg;
+
+ ram = prueth->mem[emac->dram].va;
+ mc_filter_ctrl = ram + ICSS_EMAC_FW_MULTICAST_FILTER_CTRL_OFFSET;
+
+ if (enable)
+ reg = ICSS_EMAC_FW_MULTICAST_FILTER_CTRL_ENABLED;
+ else
+ reg = ICSS_EMAC_FW_MULTICAST_FILTER_CTRL_DISABLED;
+
+ writeb(reg, mc_filter_ctrl);
+}
+
+/* reset MC filter bins */
+static void icssm_emac_mc_filter_reset(struct prueth_emac *emac)
+{
+ struct prueth *prueth = emac->prueth;
+ void __iomem *mc_filter_tbl;
+ u32 mc_filter_tbl_base;
+ void __iomem *ram;
+
+ ram = prueth->mem[emac->dram].va;
+ mc_filter_tbl_base = ICSS_EMAC_FW_MULTICAST_FILTER_TABLE;
+
+ mc_filter_tbl = ram + mc_filter_tbl_base;
+ memset_io(mc_filter_tbl, 0, ICSS_EMAC_FW_MULTICAST_TABLE_SIZE_BYTES);
+}
+
+/* set MC filter hashmask */
+static void icssm_emac_mc_filter_hashmask
+ (struct prueth_emac *emac,
+ u8 mask[ICSS_EMAC_FW_MULTICAST_FILTER_MASK_SIZE_BYTES])
+{
+ struct prueth *prueth = emac->prueth;
+ void __iomem *mc_filter_mask;
+ void __iomem *ram;
+
+ ram = prueth->mem[emac->dram].va;
+
+ mc_filter_mask = ram + ICSS_EMAC_FW_MULTICAST_FILTER_MASK_OFFSET;
+ memcpy_toio(mc_filter_mask, mask,
+ ICSS_EMAC_FW_MULTICAST_FILTER_MASK_SIZE_BYTES);
+}
+
+static void icssm_emac_mc_filter_bin_update(struct prueth_emac *emac, u8 hash,
+ u8 val)
+{
+ struct prueth *prueth = emac->prueth;
+ void __iomem *mc_filter_tbl;
+ void __iomem *ram;
+
+ ram = prueth->mem[emac->dram].va;
+
+ mc_filter_tbl = ram + ICSS_EMAC_FW_MULTICAST_FILTER_TABLE;
+ writeb(val, mc_filter_tbl + hash);
+}
+
+void icssm_emac_mc_filter_bin_allow(struct prueth_emac *emac, u8 hash)
+{
+ icssm_emac_mc_filter_bin_update
+ (emac, hash,
+ ICSS_EMAC_FW_MULTICAST_FILTER_HOST_RCV_ALLOWED);
+}
+
+void icssm_emac_mc_filter_bin_disallow(struct prueth_emac *emac, u8 hash)
+{
+ icssm_emac_mc_filter_bin_update
+ (emac, hash,
+ ICSS_EMAC_FW_MULTICAST_FILTER_HOST_RCV_NOT_ALLOWED);
+}
+
+u8 icssm_emac_get_mc_hash(u8 *mac, u8 *mask)
+{
+ u8 hash;
+ int j;
+
+ for (j = 0, hash = 0; j < ETH_ALEN; j++)
+ hash ^= (mac[j] & mask[j]);
+
+ return hash;
+}
+
+/**
+ * icssm_emac_ndo_set_rx_mode - EMAC set receive mode function
+ * @ndev: The EMAC network adapter
+ *
+ * Called when system wants to set the receive mode of the device.
+ *
+ */
+static void icssm_emac_ndo_set_rx_mode(struct net_device *ndev)
+{
+ struct prueth_emac *emac = netdev_priv(ndev);
+ bool promisc = ndev->flags & IFF_PROMISC;
+ struct netdev_hw_addr *ha;
+ struct prueth *prueth;
+ unsigned long flags;
+ void __iomem *sram;
+ u32 mask, reg;
+ u8 hash;
+
+ prueth = emac->prueth;
+ sram = prueth->mem[PRUETH_MEM_SHARED_RAM].va;
+ reg = readl(sram + EMAC_PROMISCUOUS_MODE_OFFSET);
+
+ /* It is a shared table. So lock the access */
+ spin_lock_irqsave(&emac->addr_lock, flags);
+
+ /* Disable and reset multicast filter, allows allmulti */
+ icssm_emac_mc_filter_ctrl(emac, false);
+ icssm_emac_mc_filter_reset(emac);
+ icssm_emac_mc_filter_hashmask(emac, emac->mc_filter_mask);
+
+ if (PRUETH_IS_EMAC(prueth)) {
+ switch (emac->port_id) {
+ case PRUETH_PORT_MII0:
+ mask = EMAC_P1_PROMISCUOUS_BIT;
+ break;
+ case PRUETH_PORT_MII1:
+ mask = EMAC_P2_PROMISCUOUS_BIT;
+ break;
+ default:
+ netdev_err(ndev, "%s: invalid port\n", __func__);
+ goto unlock;
+ }
+
+ if (promisc) {
+ /* Enable promiscuous mode */
+ reg |= mask;
+ } else {
+ /* Disable promiscuous mode */
+ reg &= ~mask;
+ }
+
+ writel(reg, sram + EMAC_PROMISCUOUS_MODE_OFFSET);
+
+ if (promisc)
+ goto unlock;
+ }
+
+ if (ndev->flags & IFF_ALLMULTI && !PRUETH_IS_SWITCH(prueth))
+ goto unlock;
+
+ icssm_emac_mc_filter_ctrl(emac, true); /* all multicast blocked */
+
+ if (netdev_mc_empty(ndev))
+ goto unlock;
+
+ netdev_for_each_mc_addr(ha, ndev) {
+ hash = icssm_emac_get_mc_hash(ha->addr, emac->mc_filter_mask);
+ icssm_emac_mc_filter_bin_allow(emac, hash);
+ }
+
+ /* Add bridge device's MC addresses as well */
+ if (prueth->hw_bridge_dev) {
+ netdev_for_each_mc_addr(ha, prueth->hw_bridge_dev) {
+ hash = icssm_emac_get_mc_hash(ha->addr,
+ emac->mc_filter_mask);
+ icssm_emac_mc_filter_bin_allow(emac, hash);
+ }
+ }
+
+unlock:
+ spin_unlock_irqrestore(&emac->addr_lock, flags);
+}
+
static const struct net_device_ops emac_netdev_ops = {
.ndo_open = icssm_emac_ndo_open,
.ndo_stop = icssm_emac_ndo_stop,
.ndo_start_xmit = icssm_emac_ndo_start_xmit,
.ndo_get_stats64 = icssm_emac_ndo_get_stats64,
+ .ndo_set_rx_mode = icssm_emac_ndo_set_rx_mode,
};
/* get emac_port corresponding to eth_node name */
@@ -1188,6 +1490,7 @@ static enum hrtimer_restart icssm_emac_tx_timer_callback(struct hrtimer *timer)
static int icssm_prueth_netdev_init(struct prueth *prueth,
struct device_node *eth_node)
{
+ const struct prueth_private_data *fw_data = prueth->fw_data;
struct prueth_emac *emac;
struct net_device *ndev;
enum prueth_port port;
@@ -1212,6 +1515,7 @@ static int icssm_prueth_netdev_init(struct prueth *prueth,
emac->prueth = prueth;
emac->ndev = ndev;
emac->port_id = port;
+ memset(&emac->mc_filter_mask[0], 0xff, ETH_ALEN);
/* by default eth_type is EMAC */
switch (port) {
@@ -1247,6 +1551,9 @@ static int icssm_prueth_netdev_init(struct prueth *prueth,
goto free;
}
+ spin_lock_init(&emac->lock);
+ spin_lock_init(&emac->addr_lock);
+
/* get mac address from DT and set private and netdev addr */
ret = of_get_ethdev_address(eth_node, ndev);
if (!is_valid_ether_addr(ndev->dev_addr)) {
@@ -1274,6 +1581,14 @@ static int icssm_prueth_netdev_init(struct prueth *prueth,
phy_remove_link_mode(emac->phydev, ETHTOOL_LINK_MODE_Pause_BIT);
phy_remove_link_mode(emac->phydev, ETHTOOL_LINK_MODE_Asym_Pause_BIT);
+ /* Protocol switching
+ * Enabling L2 Firmware offloading
+ */
+ if (fw_data->support_switch) {
+ ndev->features |= NETIF_F_HW_L2FW_DOFFLOAD;
+ ndev->hw_features |= NETIF_F_HW_L2FW_DOFFLOAD;
+ }
+
ndev->dev.of_node = eth_node;
ndev->netdev_ops = &emac_netdev_ops;
@@ -1310,6 +1625,169 @@ static void icssm_prueth_netdev_exit(struct prueth *prueth,
prueth->emac[mac] = NULL;
}
+bool icssm_prueth_sw_port_dev_check(const struct net_device *ndev)
+{
+ if (ndev->netdev_ops != &emac_netdev_ops)
+ return false;
+
+ if (ndev->features & NETIF_F_HW_L2FW_DOFFLOAD)
+ return true;
+
+ return false;
+}
+
+static int icssm_prueth_port_offload_fwd_mark_update(struct prueth *prueth)
+{
+ int set_val = 0;
+ int i, ret = 0;
+ u8 all_slaves;
+
+ all_slaves = BIT(PRUETH_PORT_MII0) | BIT(PRUETH_PORT_MII1);
+
+ if (prueth->br_members == all_slaves)
+ set_val = 1;
+
+ dev_dbg(prueth->dev, "set offload_fwd_mark %d, mbrs=0x%x\n",
+ set_val, prueth->br_members);
+
+ for (i = 0; i < PRUETH_NUM_MACS; i++) {
+ if (prueth->emac[i])
+ WRITE_ONCE(prueth->emac[i]->offload_fwd_mark, set_val);
+ }
+
+ /* Bridge is created, load switch firmware,
+ * if not already in that mode
+ */
+ if (set_val && !PRUETH_IS_SWITCH(prueth)) {
+ ret = icssm_prueth_change_mode(prueth, PRUSS_ETHTYPE_SWITCH);
+ if (ret < 0)
+ dev_err(prueth->dev, "Failed to enable Switch mode\n");
+ else
+ dev_info(prueth->dev,
+ "TI PRU ethernet now in Switch mode\n");
+ }
+
+ /* Bridge is deleted, switch to Dual EMAC mode */
+ if (!prueth->br_members && !PRUETH_IS_EMAC(prueth)) {
+ ret = icssm_prueth_change_mode(prueth, PRUSS_ETHTYPE_EMAC);
+ if (ret < 0)
+ dev_err(prueth->dev, "Failed to enable Dual EMAC mode\n");
+ else
+ dev_info(prueth->dev,
+ "TI PRU ethernet now in Dual EMAC mode\n");
+ }
+
+ return ret;
+}
+
+static int icssm_prueth_ndev_port_link(struct net_device *ndev,
+ struct net_device *br_ndev)
+{
+ struct prueth_emac *emac = netdev_priv(ndev);
+ struct prueth *prueth = emac->prueth;
+ unsigned long flags;
+ int ret = 0;
+
+ dev_dbg(prueth->dev, "%s: br_mbrs=0x%x %s\n",
+ __func__, prueth->br_members, ndev->name);
+
+ spin_lock_irqsave(&emac->addr_lock, flags);
+
+ if (!prueth->br_members) {
+ prueth->hw_bridge_dev = br_ndev;
+ } else {
+ /* This is adding the port to a second bridge,
+ * this is unsupported
+ */
+ if (prueth->hw_bridge_dev != br_ndev) {
+ spin_unlock_irqrestore(&emac->addr_lock, flags);
+ return -EOPNOTSUPP;
+ }
+ }
+
+ prueth->br_members |= BIT(emac->port_id);
+
+ spin_unlock_irqrestore(&emac->addr_lock, flags);
+
+ ret = icssm_prueth_port_offload_fwd_mark_update(prueth);
+
+ return ret;
+}
+
+static int icssm_prueth_ndev_port_unlink(struct net_device *ndev)
+{
+ struct prueth_emac *emac = netdev_priv(ndev);
+ struct prueth *prueth = emac->prueth;
+ unsigned long flags;
+ int ret = 0;
+
+ dev_dbg(prueth->dev, "emac_sw_ndev_port_unlink\n");
+
+ spin_lock_irqsave(&emac->addr_lock, flags);
+
+ prueth->br_members &= ~BIT(emac->port_id);
+
+ spin_unlock_irqrestore(&emac->addr_lock, flags);
+
+ ret = icssm_prueth_port_offload_fwd_mark_update(prueth);
+
+ spin_lock_irqsave(&emac->addr_lock, flags);
+
+ if (!prueth->br_members)
+ prueth->hw_bridge_dev = NULL;
+
+ spin_unlock_irqrestore(&emac->addr_lock, flags);
+
+ return ret;
+}
+
+static int icssm_prueth_ndev_event(struct notifier_block *unused,
+ unsigned long event, void *ptr)
+{
+ struct net_device *ndev = netdev_notifier_info_to_dev(ptr);
+ struct netdev_notifier_changeupper_info *info;
+ int ret = NOTIFY_DONE;
+
+ if (!icssm_prueth_sw_port_dev_check(ndev))
+ return NOTIFY_DONE;
+
+ switch (event) {
+ case NETDEV_CHANGEUPPER:
+ info = ptr;
+ if (netif_is_bridge_master(info->upper_dev)) {
+ if (info->linking)
+ ret = icssm_prueth_ndev_port_link
+ (ndev, info->upper_dev);
+ else
+ ret = icssm_prueth_ndev_port_unlink(ndev);
+ }
+ break;
+ default:
+ return NOTIFY_DONE;
+ }
+
+ return notifier_from_errno(ret);
+}
+
+static int icssm_prueth_register_notifiers(struct prueth *prueth)
+{
+ int ret = 0;
+
+ prueth->prueth_netdevice_nb.notifier_call = icssm_prueth_ndev_event;
+ ret = register_netdevice_notifier(&prueth->prueth_netdevice_nb);
+ if (ret) {
+ dev_err(prueth->dev,
+ "register netdevice notifier failed ret: %d\n", ret);
+ return ret;
+ }
+
+ ret = icssm_prueth_sw_register_notifiers(prueth);
+ if (ret)
+ unregister_netdevice_notifier(&prueth->prueth_netdevice_nb);
+
+ return ret;
+}
+
static int icssm_prueth_probe(struct platform_device *pdev)
{
struct device_node *eth0_node = NULL, *eth1_node = NULL;
@@ -1529,6 +2007,12 @@ static int icssm_prueth_probe(struct platform_device *pdev)
prueth->emac[PRUETH_MAC1]->ndev;
}
+ ret = icssm_prueth_register_notifiers(prueth);
+ if (ret) {
+ dev_err(dev, "can't register switchdev notifiers");
+ goto netdev_unregister;
+ }
+
dev_info(dev, "TI PRU ethernet driver initialized: %s EMAC mode\n",
(!eth0_node || !eth1_node) ? "single" : "dual");
@@ -1589,6 +2073,9 @@ static void icssm_prueth_remove(struct platform_device *pdev)
struct device_node *eth_node;
int i;
+ unregister_netdevice_notifier(&prueth->prueth_netdevice_nb);
+ icssm_prueth_sw_unregister_notifiers(prueth);
+
for (i = 0; i < PRUETH_NUM_MACS; i++) {
if (!prueth->registered_netdevs[i])
continue;
@@ -1688,11 +2175,16 @@ static struct prueth_private_data am335x_prueth_pdata = {
.fw_pru[PRUSS_PRU0] = {
.fw_name[PRUSS_ETHTYPE_EMAC] =
"ti-pruss/am335x-pru0-prueth-fw.elf",
+ .fw_name[PRUSS_ETHTYPE_SWITCH] =
+ "ti-pruss/am335x-pru0-prusw-fw.elf",
},
.fw_pru[PRUSS_PRU1] = {
.fw_name[PRUSS_ETHTYPE_EMAC] =
"ti-pruss/am335x-pru1-prueth-fw.elf",
+ .fw_name[PRUSS_ETHTYPE_SWITCH] =
+ "ti-pruss/am335x-pru1-prusw-fw.elf",
},
+ .support_switch = true,
};
/* AM437x SoC-specific firmware data */
@@ -1701,11 +2193,16 @@ static struct prueth_private_data am437x_prueth_pdata = {
.fw_pru[PRUSS_PRU0] = {
.fw_name[PRUSS_ETHTYPE_EMAC] =
"ti-pruss/am437x-pru0-prueth-fw.elf",
+ .fw_name[PRUSS_ETHTYPE_SWITCH] =
+ "ti-pruss/am437x-pru0-prusw-fw.elf",
},
.fw_pru[PRUSS_PRU1] = {
.fw_name[PRUSS_ETHTYPE_EMAC] =
"ti-pruss/am437x-pru1-prueth-fw.elf",
+ .fw_name[PRUSS_ETHTYPE_SWITCH] =
+ "ti-pruss/am437x-pru1-prusw-fw.elf",
},
+ .support_switch = true,
};
/* AM57xx SoC-specific firmware data */
@@ -1714,11 +2211,17 @@ static struct prueth_private_data am57xx_prueth_pdata = {
.fw_pru[PRUSS_PRU0] = {
.fw_name[PRUSS_ETHTYPE_EMAC] =
"ti-pruss/am57xx-pru0-prueth-fw.elf",
+ .fw_name[PRUSS_ETHTYPE_SWITCH] =
+ "ti-pruss/am57xx-pru0-prusw-fw.elf",
},
.fw_pru[PRUSS_PRU1] = {
.fw_name[PRUSS_ETHTYPE_EMAC] =
"ti-pruss/am57xx-pru1-prueth-fw.elf",
+ .fw_name[PRUSS_ETHTYPE_SWITCH] =
+ "ti-pruss/am57xx-pru1-prusw-fw.elf",
+
},
+ .support_switch = true,
};
static const struct of_device_id prueth_dt_match[] = {
diff --git a/drivers/net/ethernet/ti/icssm/icssm_prueth.h b/drivers/net/ethernet/ti/icssm/icssm_prueth.h
index 8e7e0af08144..d5b49b462c24 100644
--- a/drivers/net/ethernet/ti/icssm/icssm_prueth.h
+++ b/drivers/net/ethernet/ti/icssm/icssm_prueth.h
@@ -15,6 +15,7 @@
#include "icssm_switch.h"
#include "icssm_prueth_ptp.h"
+#include "icssm_prueth_fdb_tbl.h"
/* ICSSM size of redundancy tag */
#define ICSSM_LRE_TAG_SIZE 6
@@ -181,10 +182,12 @@ enum pruss_device {
* struct prueth_private_data - PRU Ethernet private data
* @driver_data: PRU Ethernet device name
* @fw_pru: firmware names to be used for PRUSS ethernet usecases
+ * @support_switch: boolean to indicate if switch is enabled
*/
struct prueth_private_data {
enum pruss_device driver_data;
const struct prueth_firmware fw_pru[PRUSS_NUM_PRUS];
+ bool support_switch;
};
struct prueth_emac_stats {
@@ -221,15 +224,18 @@ struct prueth_emac {
const char *phy_id;
u32 msg_enable;
u8 mac_addr[6];
+ unsigned char mc_filter_mask[ETH_ALEN]; /* for multicast filtering */
phy_interface_t phy_if;
/* spin lock used to protect
* during link configuration
*/
spinlock_t lock;
+ spinlock_t addr_lock; /* serialize access to VLAN/MC filter table */
struct hrtimer tx_hrtimer;
struct prueth_emac_stats stats;
+ int offload_fwd_mark;
};
struct prueth {
@@ -248,15 +254,27 @@ struct prueth {
struct prueth_emac *emac[PRUETH_NUM_MACS];
struct net_device *registered_netdevs[PRUETH_NUM_MACS];
+ struct net_device *hw_bridge_dev;
+ struct fdb_tbl *fdb_tbl;
+
+ struct notifier_block prueth_netdevice_nb;
+ struct notifier_block prueth_switchdev_nb;
+ struct notifier_block prueth_switchdev_bl_nb;
+
unsigned int eth_type;
size_t ocmc_ram_size;
u8 emac_configured;
+ u8 br_members;
};
+extern const struct prueth_queue_desc queue_descs[][NUM_QUEUES];
+
void icssm_parse_packet_info(struct prueth *prueth, u32 buffer_descriptor,
struct prueth_packet_info *pkt_info);
int icssm_emac_rx_packet(struct prueth_emac *emac, u16 *bd_rd_ptr,
struct prueth_packet_info *pkt_info,
const struct prueth_queue_info *rxqueue);
-
+void icssm_emac_mc_filter_bin_allow(struct prueth_emac *emac, u8 hash);
+void icssm_emac_mc_filter_bin_disallow(struct prueth_emac *emac, u8 hash);
+u8 icssm_emac_get_mc_hash(u8 *mac, u8 *mask);
#endif /* __NET_TI_PRUETH_H */
diff --git a/drivers/net/ethernet/ti/icssm/icssm_prueth_fdb_tbl.h b/drivers/net/ethernet/ti/icssm/icssm_prueth_fdb_tbl.h
new file mode 100644
index 000000000000..9089259d96ea
--- /dev/null
+++ b/drivers/net/ethernet/ti/icssm/icssm_prueth_fdb_tbl.h
@@ -0,0 +1,76 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2019-2021 Texas Instruments Incorporated - https://www.ti.com */
+#ifndef __NET_TI_PRUSS_FDB_TBL_H
+#define __NET_TI_PRUSS_FDB_TBL_H
+
+#include <linux/kernel.h>
+#include <linux/debugfs.h>
+#include "icssm_prueth.h"
+
+/* 4 bytes */
+struct fdb_index_tbl_entry {
+ /* Bucket Table index of first Bucket with this MAC address */
+ u16 bucket_idx;
+ u16 bucket_entries; /* Number of entries in this bucket */
+};
+
+/* 4 * 256 = 1024 = 0x200 bytes */
+struct fdb_index_array {
+ struct fdb_index_tbl_entry index_tbl_entry[FDB_INDEX_TBL_MAX_ENTRIES];
+};
+
+/* 10 bytes */
+struct fdb_mac_tbl_entry {
+ u8 mac[ETH_ALEN];
+ u16 age;
+ u8 port; /* 0 based: 0=port1, 1=port2 */
+ union {
+ struct {
+ u8 is_static:1;
+ u8 active:1;
+ };
+ u8 flags;
+ };
+};
+
+/* 10 * 256 = 2560 = 0xa00 bytes */
+struct fdb_mac_tbl_array {
+ struct fdb_mac_tbl_entry mac_tbl_entry[FDB_MAC_TBL_MAX_ENTRIES];
+};
+
+/* 1 byte */
+struct fdb_stp_config {
+ u8 state; /* per-port STP state (defined in FW header) */
+};
+
+/* 1 byte */
+struct fdb_flood_config {
+ u8 host_flood_enable:1;
+ u8 port1_flood_enable:1;
+ u8 port2_flood_enable:1;
+};
+
+/* 2 byte */
+struct fdb_arbitration {
+ u8 host_lock;
+ u8 pru_locks;
+};
+
+struct fdb_tbl {
+ /* fdb index table */
+ struct fdb_index_array __iomem *index_a;
+ /* fdb MAC table */
+ struct fdb_mac_tbl_array __iomem *mac_tbl_a;
+ /* port 1 stp config */
+ struct fdb_stp_config __iomem *port1_stp_cfg;
+ /* port 2 stp config */
+ struct fdb_stp_config __iomem *port2_stp_cfg;
+ /* per-port flood enable */
+ struct fdb_flood_config __iomem *flood_enable_flags;
+ /* fdb locking mechanism */
+ struct fdb_arbitration __iomem *locks;
+ /* total number of entries in hash table */
+ u16 total_entries;
+};
+
+#endif
diff --git a/drivers/net/ethernet/ti/icssm/icssm_prueth_switch.c b/drivers/net/ethernet/ti/icssm/icssm_prueth_switch.c
new file mode 100644
index 000000000000..07c08564386e
--- /dev/null
+++ b/drivers/net/ethernet/ti/icssm/icssm_prueth_switch.c
@@ -0,0 +1,1065 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Texas Instruments PRUETH Switch Driver
+ *
+ * Copyright (C) 2020-2021 Texas Instruments Incorporated - https://www.ti.com
+ */
+#include <linux/etherdevice.h>
+#include <linux/kernel.h>
+#include <linux/remoteproc.h>
+#include <net/switchdev.h>
+#include "icssm_prueth.h"
+#include "icssm_prueth_switch.h"
+#include "icssm_prueth_fdb_tbl.h"
+
+#define FDB_IDX_TBL_ENTRY(n) (&prueth->fdb_tbl->index_a->index_tbl_entry[n])
+
+#define FDB_MAC_TBL_ENTRY(n) (&prueth->fdb_tbl->mac_tbl_a->mac_tbl_entry[n])
+
+#define FLAG_IS_STATIC BIT(0)
+#define FLAG_ACTIVE BIT(1)
+
+#define FDB_LEARN 1
+#define FDB_PURGE 2
+
+struct icssm_prueth_sw_fdb_work {
+ netdevice_tracker ndev_tracker;
+ struct work_struct work;
+ struct prueth_emac *emac;
+ u8 addr[ETH_ALEN];
+ int event;
+};
+
+const struct prueth_queue_info sw_queue_infos[][NUM_QUEUES] = {
+ [PRUETH_PORT_QUEUE_HOST] = {
+ [PRUETH_QUEUE1] = {
+ P0_Q1_BUFFER_OFFSET,
+ P0_QUEUE_DESC_OFFSET,
+ P0_Q1_BD_OFFSET,
+ P0_Q1_BD_OFFSET + ((HOST_QUEUE_1_SIZE - 1) * BD_SIZE),
+ },
+ [PRUETH_QUEUE2] = {
+ P0_Q2_BUFFER_OFFSET,
+ P0_QUEUE_DESC_OFFSET + 8,
+ P0_Q2_BD_OFFSET,
+ P0_Q2_BD_OFFSET + ((HOST_QUEUE_2_SIZE - 1) * BD_SIZE),
+ },
+ [PRUETH_QUEUE3] = {
+ P0_Q3_BUFFER_OFFSET,
+ P0_QUEUE_DESC_OFFSET + 16,
+ P0_Q3_BD_OFFSET,
+ P0_Q3_BD_OFFSET + ((HOST_QUEUE_3_SIZE - 1) * BD_SIZE),
+ },
+ [PRUETH_QUEUE4] = {
+ P0_Q4_BUFFER_OFFSET,
+ P0_QUEUE_DESC_OFFSET + 24,
+ P0_Q4_BD_OFFSET,
+ P0_Q4_BD_OFFSET + ((HOST_QUEUE_4_SIZE - 1) * BD_SIZE),
+ },
+ },
+ [PRUETH_PORT_QUEUE_MII0] = {
+ [PRUETH_QUEUE1] = {
+ P1_Q1_BUFFER_OFFSET,
+ P1_Q1_BUFFER_OFFSET +
+ ((QUEUE_1_SIZE - 1) * ICSS_BLOCK_SIZE),
+ P1_Q1_BD_OFFSET,
+ P1_Q1_BD_OFFSET + ((QUEUE_1_SIZE - 1) * BD_SIZE),
+ },
+ [PRUETH_QUEUE2] = {
+ P1_Q2_BUFFER_OFFSET,
+ P1_Q2_BUFFER_OFFSET +
+ ((QUEUE_2_SIZE - 1) * ICSS_BLOCK_SIZE),
+ P1_Q2_BD_OFFSET,
+ P1_Q2_BD_OFFSET + ((QUEUE_2_SIZE - 1) * BD_SIZE),
+ },
+ [PRUETH_QUEUE3] = {
+ P1_Q3_BUFFER_OFFSET,
+ P1_Q3_BUFFER_OFFSET +
+ ((QUEUE_3_SIZE - 1) * ICSS_BLOCK_SIZE),
+ P1_Q3_BD_OFFSET,
+ P1_Q3_BD_OFFSET + ((QUEUE_3_SIZE - 1) * BD_SIZE),
+ },
+ [PRUETH_QUEUE4] = {
+ P1_Q4_BUFFER_OFFSET,
+ P1_Q4_BUFFER_OFFSET +
+ ((QUEUE_4_SIZE - 1) * ICSS_BLOCK_SIZE),
+ P1_Q4_BD_OFFSET,
+ P1_Q4_BD_OFFSET + ((QUEUE_4_SIZE - 1) * BD_SIZE),
+ },
+ },
+ [PRUETH_PORT_QUEUE_MII1] = {
+ [PRUETH_QUEUE1] = {
+ P2_Q1_BUFFER_OFFSET,
+ P2_Q1_BUFFER_OFFSET +
+ ((QUEUE_1_SIZE - 1) * ICSS_BLOCK_SIZE),
+ P2_Q1_BD_OFFSET,
+ P2_Q1_BD_OFFSET + ((QUEUE_1_SIZE - 1) * BD_SIZE),
+ },
+ [PRUETH_QUEUE2] = {
+ P2_Q2_BUFFER_OFFSET,
+ P2_Q2_BUFFER_OFFSET +
+ ((QUEUE_2_SIZE - 1) * ICSS_BLOCK_SIZE),
+ P2_Q2_BD_OFFSET,
+ P2_Q2_BD_OFFSET + ((QUEUE_2_SIZE - 1) * BD_SIZE),
+ },
+ [PRUETH_QUEUE3] = {
+ P2_Q3_BUFFER_OFFSET,
+ P2_Q3_BUFFER_OFFSET +
+ ((QUEUE_3_SIZE - 1) * ICSS_BLOCK_SIZE),
+ P2_Q3_BD_OFFSET,
+ P2_Q3_BD_OFFSET + ((QUEUE_3_SIZE - 1) * BD_SIZE),
+ },
+ [PRUETH_QUEUE4] = {
+ P2_Q4_BUFFER_OFFSET,
+ P2_Q4_BUFFER_OFFSET +
+ ((QUEUE_4_SIZE - 1) * ICSS_BLOCK_SIZE),
+ P2_Q4_BD_OFFSET,
+ P2_Q4_BD_OFFSET + ((QUEUE_4_SIZE - 1) * BD_SIZE),
+ },
+ },
+};
+
+static const struct prueth_queue_info rx_queue_infos[][NUM_QUEUES] = {
+ [PRUETH_PORT_QUEUE_HOST] = {
+ [PRUETH_QUEUE1] = {
+ P0_Q1_BUFFER_OFFSET,
+ HOST_QUEUE_DESC_OFFSET,
+ P0_Q1_BD_OFFSET,
+ P0_Q1_BD_OFFSET + ((HOST_QUEUE_1_SIZE - 1) * BD_SIZE),
+ },
+ [PRUETH_QUEUE2] = {
+ P0_Q2_BUFFER_OFFSET,
+ HOST_QUEUE_DESC_OFFSET + 8,
+ P0_Q2_BD_OFFSET,
+ P0_Q2_BD_OFFSET + ((HOST_QUEUE_2_SIZE - 1) * BD_SIZE),
+ },
+ [PRUETH_QUEUE3] = {
+ P0_Q3_BUFFER_OFFSET,
+ HOST_QUEUE_DESC_OFFSET + 16,
+ P0_Q3_BD_OFFSET,
+ P0_Q3_BD_OFFSET + ((HOST_QUEUE_3_SIZE - 1) * BD_SIZE),
+ },
+ [PRUETH_QUEUE4] = {
+ P0_Q4_BUFFER_OFFSET,
+ HOST_QUEUE_DESC_OFFSET + 24,
+ P0_Q4_BD_OFFSET,
+ P0_Q4_BD_OFFSET + ((HOST_QUEUE_4_SIZE - 1) * BD_SIZE),
+ },
+ },
+ [PRUETH_PORT_QUEUE_MII0] = {
+ [PRUETH_QUEUE1] = {
+ P1_Q1_BUFFER_OFFSET,
+ P1_QUEUE_DESC_OFFSET,
+ P1_Q1_BD_OFFSET,
+ P1_Q1_BD_OFFSET + ((QUEUE_1_SIZE - 1) * BD_SIZE),
+ },
+ [PRUETH_QUEUE2] = {
+ P1_Q2_BUFFER_OFFSET,
+ P1_QUEUE_DESC_OFFSET + 8,
+ P1_Q2_BD_OFFSET,
+ P1_Q2_BD_OFFSET + ((QUEUE_2_SIZE - 1) * BD_SIZE),
+ },
+ [PRUETH_QUEUE3] = {
+ P1_Q3_BUFFER_OFFSET,
+ P1_QUEUE_DESC_OFFSET + 16,
+ P1_Q3_BD_OFFSET,
+ P1_Q3_BD_OFFSET + ((QUEUE_3_SIZE - 1) * BD_SIZE),
+ },
+ [PRUETH_QUEUE4] = {
+ P1_Q4_BUFFER_OFFSET,
+ P1_QUEUE_DESC_OFFSET + 24,
+ P1_Q4_BD_OFFSET,
+ P1_Q4_BD_OFFSET + ((QUEUE_4_SIZE - 1) * BD_SIZE),
+ },
+ },
+ [PRUETH_PORT_QUEUE_MII1] = {
+ [PRUETH_QUEUE1] = {
+ P2_Q1_BUFFER_OFFSET,
+ P2_QUEUE_DESC_OFFSET,
+ P2_Q1_BD_OFFSET,
+ P2_Q1_BD_OFFSET + ((QUEUE_1_SIZE - 1) * BD_SIZE),
+ },
+ [PRUETH_QUEUE2] = {
+ P2_Q2_BUFFER_OFFSET,
+ P2_QUEUE_DESC_OFFSET + 8,
+ P2_Q2_BD_OFFSET,
+ P2_Q2_BD_OFFSET + ((QUEUE_2_SIZE - 1) * BD_SIZE),
+ },
+ [PRUETH_QUEUE3] = {
+ P2_Q3_BUFFER_OFFSET,
+ P2_QUEUE_DESC_OFFSET + 16,
+ P2_Q3_BD_OFFSET,
+ P2_Q3_BD_OFFSET + ((QUEUE_3_SIZE - 1) * BD_SIZE),
+ },
+ [PRUETH_QUEUE4] = {
+ P2_Q4_BUFFER_OFFSET,
+ P2_QUEUE_DESC_OFFSET + 24,
+ P2_Q4_BD_OFFSET,
+ P2_Q4_BD_OFFSET + ((QUEUE_4_SIZE - 1) * BD_SIZE),
+ },
+ },
+};
+
+void icssm_prueth_sw_free_fdb_table(struct prueth *prueth)
+{
+ if (prueth->emac_configured)
+ return;
+
+ kfree(prueth->fdb_tbl);
+ prueth->fdb_tbl = NULL;
+}
+
+void icssm_prueth_sw_fdb_tbl_init(struct prueth *prueth)
+{
+ struct fdb_tbl *t = prueth->fdb_tbl;
+ void __iomem *sram_base;
+ u8 val;
+
+ sram_base = prueth->mem[PRUETH_MEM_SHARED_RAM].va;
+
+ t->index_a = sram_base + V2_1_FDB_TBL_OFFSET;
+ t->mac_tbl_a = sram_base + FDB_MAC_TBL_OFFSET;
+ t->port1_stp_cfg = sram_base + FDB_PORT1_STP_CFG_OFFSET;
+ t->port2_stp_cfg = sram_base + FDB_PORT2_STP_CFG_OFFSET;
+ t->flood_enable_flags = sram_base + FDB_FLOOD_ENABLE_FLAGS_OFFSET;
+ t->locks = sram_base + FDB_LOCKS_OFFSET;
+
+ val = readb(t->flood_enable_flags);
+ /* host_flood_enable = 1 */
+ val |= BIT(0);
+ /* port1_flood_enable = 1 */
+ val |= BIT(1);
+ /* port2_flood_enable = 1 */
+ val |= BIT(2);
+ writeb(val, t->flood_enable_flags);
+
+ writeb(0, &t->locks->host_lock);
+ t->total_entries = 0;
+}
+
+static u8 icssm_pru_lock_done(struct fdb_tbl *fdb_tbl)
+{
+ return readb(&fdb_tbl->locks->pru_locks);
+}
+
+static int icssm_prueth_sw_fdb_spin_lock(struct fdb_tbl *fdb_tbl)
+{
+ u8 done;
+ int ret;
+
+ /* Take the host lock */
+ writeb(1, &fdb_tbl->locks->host_lock);
+
+ /* Wait for the PRUs to release their locks */
+ ret = read_poll_timeout(icssm_pru_lock_done, done, done == 0,
+ 1, 10, false, fdb_tbl);
+ if (ret == -ETIMEDOUT)
+ writeb(0, &fdb_tbl->locks->host_lock);
+
+ return ret;
+}
+
+static void icssm_prueth_sw_fdb_spin_unlock(struct fdb_tbl *fdb_tbl)
+{
+ writeb(0, &fdb_tbl->locks->host_lock);
+}
+
+static u8 icssm_prueth_sw_fdb_hash(const u8 *mac)
+{
+ return (mac[0] ^ mac[1] ^ mac[2] ^ mac[3] ^ mac[4] ^ mac[5]);
+}
+
+static int
+icssm_prueth_sw_fdb_search(struct fdb_mac_tbl_array __iomem *mac_tbl,
+ struct fdb_index_tbl_entry __iomem *bucket_info,
+ const u8 *mac)
+{
+ unsigned int bucket_entries, mac_tbl_idx;
+ u8 tmp_mac[ETH_ALEN];
+ int i;
+
+ mac_tbl_idx = readw(&bucket_info->bucket_idx);
+ bucket_entries = readw(&bucket_info->bucket_entries);
+ for (i = 0; i < bucket_entries; i++, mac_tbl_idx++) {
+ memcpy_fromio(tmp_mac, mac_tbl->mac_tbl_entry[mac_tbl_idx].mac,
+ ETH_ALEN);
+ if (ether_addr_equal(mac, tmp_mac))
+ return mac_tbl_idx;
+ }
+
+ return -ENODATA;
+}
+
+static int icssm_prueth_sw_fdb_find_open_slot(struct fdb_tbl *fdb_tbl)
+{
+ unsigned int i;
+ u8 flags;
+
+ for (i = 0; i < FDB_MAC_TBL_MAX_ENTRIES; i++) {
+ flags = readb(&fdb_tbl->mac_tbl_a->mac_tbl_entry[i].flags);
+ if (!(flags & FLAG_ACTIVE))
+ break;
+ }
+
+ return i;
+}
+
+static int
+icssm_prueth_sw_find_fdb_insert(struct fdb_tbl *fdb, struct prueth *prueth,
+ struct fdb_index_tbl_entry __iomem *bkt_info,
+ const u8 *mac, const u8 port)
+{
+ struct fdb_mac_tbl_array __iomem *mac_tbl = fdb->mac_tbl_a;
+ unsigned int bucket_entries, mac_tbl_idx;
+ struct fdb_mac_tbl_entry __iomem *e;
+ u8 mac_from_hw[ETH_ALEN];
+ s8 cmp;
+ int i;
+
+ mac_tbl_idx = readw(&bkt_info->bucket_idx);
+ bucket_entries = readw(&bkt_info->bucket_entries);
+
+ for (i = 0; i < bucket_entries; i++, mac_tbl_idx++) {
+ e = &mac_tbl->mac_tbl_entry[mac_tbl_idx];
+ memcpy_fromio(mac_from_hw, e->mac, ETH_ALEN);
+ cmp = memcmp(mac, mac_from_hw, ETH_ALEN);
+ if (cmp < 0) {
+ return mac_tbl_idx;
+ } else if (cmp == 0) {
+ if (readb(&e->port) != port) {
+ /* MAC is already in FDB, only port is
+ * different. So just update the port.
+ * Note: total_entries and bucket_entries
+ * remain the same.
+ */
+ writeb(port, &e->port);
+ }
+
+ /* MAC and port are the same, touch the fdb */
+ writew(0, &e->age);
+ return -EEXIST;
+ }
+ }
+
+ return mac_tbl_idx;
+}
+
+static int
+icssm_prueth_sw_fdb_empty_slot_left(struct fdb_mac_tbl_array __iomem *mac_tbl,
+ unsigned int mac_tbl_idx)
+{
+ u8 flags;
+ int i;
+
+ for (i = mac_tbl_idx - 1; i > -1; i--) {
+ flags = readb(&mac_tbl->mac_tbl_entry[i].flags);
+ if (!(flags & FLAG_ACTIVE))
+ break;
+ }
+
+ return i;
+}
+
+static int
+icssm_prueth_sw_fdb_empty_slot_right(struct fdb_mac_tbl_array __iomem *mac_tbl,
+ unsigned int mac_tbl_idx)
+{
+ u8 flags;
+ int i;
+
+ for (i = mac_tbl_idx; i < FDB_MAC_TBL_MAX_ENTRIES; i++) {
+ flags = readb(&mac_tbl->mac_tbl_entry[i].flags);
+ if (!(flags & FLAG_ACTIVE))
+ return i;
+ }
+
+ return -1;
+}
+
+static void icssm_prueth_sw_fdb_move_range_left(struct prueth *prueth,
+ u16 left, u16 right)
+{
+ struct fdb_mac_tbl_entry entry;
+ u32 sz = 0;
+ u16 i;
+
+ sz = sizeof(struct fdb_mac_tbl_entry);
+ for (i = left; i < right; i++) {
+ memcpy_fromio(&entry, FDB_MAC_TBL_ENTRY(i + 1), sz);
+ memcpy_toio(FDB_MAC_TBL_ENTRY(i), &entry, sz);
+ }
+}
+
+static void icssm_prueth_sw_fdb_move_range_right(struct prueth *prueth,
+ u16 left, u16 right)
+{
+ struct fdb_mac_tbl_entry entry;
+ u32 sz = 0;
+ u16 i;
+
+ sz = sizeof(struct fdb_mac_tbl_entry);
+ for (i = right; i > left; i--) {
+ memcpy_fromio(&entry, FDB_MAC_TBL_ENTRY(i - 1), sz);
+ memcpy_toio(FDB_MAC_TBL_ENTRY(i), &entry, sz);
+ }
+}
+
+static void icssm_prueth_sw_fdb_update_index_tbl(struct prueth *prueth,
+ u16 left, u16 right)
+{
+ unsigned int hash, hash_prev;
+ u8 mac[ETH_ALEN];
+ unsigned int i;
+
+ /* To ensure we don't improperly update the
+ * bucket index, initialize with an invalid
+ * hash in case we are in leftmost slot
+ */
+ hash_prev = 0xff;
+
+ if (left > 0) {
+ memcpy_fromio(mac, FDB_MAC_TBL_ENTRY(left - 1)->mac, ETH_ALEN);
+ hash_prev = icssm_prueth_sw_fdb_hash(mac);
+ }
+
+ /* For each moved element, update the bucket index */
+ for (i = left; i <= right; i++) {
+ memcpy_fromio(mac, FDB_MAC_TBL_ENTRY(i)->mac, ETH_ALEN);
+ hash = icssm_prueth_sw_fdb_hash(mac);
+
+ /* Only need to update buckets once */
+ if (hash != hash_prev)
+ writew(i, &FDB_IDX_TBL_ENTRY(hash)->bucket_idx);
+
+ hash_prev = hash;
+ }
+}
+
+static struct fdb_mac_tbl_entry __iomem *
+icssm_prueth_sw_find_free_mac(struct prueth *prueth, struct fdb_index_tbl_entry
+ __iomem *bucket_info, u8 suggested_mac_tbl_idx,
+ bool *update_indexes, const u8 *mac)
+{
+ s16 empty_slot_idx = 0, left = 0, right = 0;
+ unsigned int mti = suggested_mac_tbl_idx;
+ struct fdb_mac_tbl_array __iomem *mt;
+ struct fdb_tbl *fdb;
+ u8 flags;
+
+ fdb = prueth->fdb_tbl;
+ mt = fdb->mac_tbl_a;
+
+ flags = readb(&FDB_MAC_TBL_ENTRY(mti)->flags);
+ if (!(flags & FLAG_ACTIVE)) {
+ /* Claim the entry */
+ flags |= FLAG_ACTIVE;
+ writeb(flags, &FDB_MAC_TBL_ENTRY(mti)->flags);
+
+ return FDB_MAC_TBL_ENTRY(mti);
+ }
+
+ if (fdb->total_entries == FDB_MAC_TBL_MAX_ENTRIES)
+ return NULL;
+
+ empty_slot_idx = icssm_prueth_sw_fdb_empty_slot_left(mt, mti);
+ if (empty_slot_idx == -1) {
+ /* Nothing available on the left. But table isn't full
+ * so there must be space to the right,
+ */
+ empty_slot_idx = icssm_prueth_sw_fdb_empty_slot_right(mt, mti);
+
+ /* Shift right */
+ left = mti;
+ right = empty_slot_idx;
+ icssm_prueth_sw_fdb_move_range_right(prueth, left, right);
+
+ /* Claim the entry */
+ flags = readb(&FDB_MAC_TBL_ENTRY(mti)->flags);
+ flags |= FLAG_ACTIVE;
+ writeb(flags, &FDB_MAC_TBL_ENTRY(mti)->flags);
+
+ memcpy_toio(FDB_MAC_TBL_ENTRY(mti)->mac, mac, ETH_ALEN);
+
+ /* There is a chance we moved something in a
+ * different bucket, update index table
+ */
+ icssm_prueth_sw_fdb_update_index_tbl(prueth, left, right);
+
+ return FDB_MAC_TBL_ENTRY(mti);
+ }
+
+ if (empty_slot_idx == mti - 1) {
+ /* There is space immediately left of the open slot,
+ * which means the inserted MAC address
+ * must be the lowest-valued MAC address in bucket.
+ * Update bucket pointer accordingly.
+ */
+ writew(empty_slot_idx, &bucket_info->bucket_idx);
+
+ /* Claim the entry */
+ flags = readb(&FDB_MAC_TBL_ENTRY(empty_slot_idx)->flags);
+ flags |= FLAG_ACTIVE;
+ writeb(flags, &FDB_MAC_TBL_ENTRY(empty_slot_idx)->flags);
+
+ return FDB_MAC_TBL_ENTRY(empty_slot_idx);
+ }
+
+ /* There is empty space to the left, shift MAC table entries left */
+ left = empty_slot_idx;
+ right = mti - 1;
+ icssm_prueth_sw_fdb_move_range_left(prueth, left, right);
+
+ /* Claim the entry */
+ flags = readb(&FDB_MAC_TBL_ENTRY(mti - 1)->flags);
+ flags |= FLAG_ACTIVE;
+ writeb(flags, &FDB_MAC_TBL_ENTRY(mti - 1)->flags);
+
+ memcpy_toio(FDB_MAC_TBL_ENTRY(mti - 1)->mac, mac, ETH_ALEN);
+
+ /* There is a chance we moved something in a
+ * different bucket, update index table
+ */
+ icssm_prueth_sw_fdb_update_index_tbl(prueth, left, right);
+
+ return FDB_MAC_TBL_ENTRY(mti - 1);
+}
+
+static int icssm_prueth_sw_insert_fdb_entry(struct prueth_emac *emac,
+ const u8 *mac, u8 is_static)
+{
+ struct fdb_index_tbl_entry __iomem *bucket_info;
+ struct fdb_mac_tbl_entry __iomem *mac_info;
+ struct prueth *prueth = emac->prueth;
+ unsigned int hash_val, mac_tbl_idx;
+ struct prueth_emac *other_emac;
+ enum prueth_port other_port_id;
+ int total_fdb_entries;
+ struct fdb_tbl *fdb;
+ u8 flags;
+ s16 ret;
+ int err;
+ u16 val;
+
+ fdb = prueth->fdb_tbl;
+ other_port_id = (emac->port_id == PRUETH_PORT_MII0) ?
+ PRUETH_PORT_MII1 : PRUETH_PORT_MII0;
+
+ other_emac = prueth->emac[other_port_id - 1];
+ if (!other_emac)
+ return -EINVAL;
+
+ err = icssm_prueth_sw_fdb_spin_lock(fdb);
+ if (err) {
+ dev_err(prueth->dev, "PRU lock timeout %d\n", err);
+ return err;
+ }
+
+ if (fdb->total_entries == FDB_MAC_TBL_MAX_ENTRIES) {
+ icssm_prueth_sw_fdb_spin_unlock(fdb);
+ return -ENOMEM;
+ }
+
+ if (ether_addr_equal(mac, emac->mac_addr) ||
+ (ether_addr_equal(mac, other_emac->mac_addr))) {
+ icssm_prueth_sw_fdb_spin_unlock(fdb);
+ /* Don't insert fdb of own mac addr */
+ return -EINVAL;
+ }
+
+ /* Get the bucket that the mac belongs to */
+ hash_val = icssm_prueth_sw_fdb_hash(mac);
+ bucket_info = FDB_IDX_TBL_ENTRY(hash_val);
+
+ if (!readw(&bucket_info->bucket_entries)) {
+ mac_tbl_idx = icssm_prueth_sw_fdb_find_open_slot(fdb);
+ writew(mac_tbl_idx, &bucket_info->bucket_idx);
+ }
+
+ ret = icssm_prueth_sw_find_fdb_insert(fdb, prueth, bucket_info, mac,
+ emac->port_id - 1);
+ if (ret < 0) {
+ icssm_prueth_sw_fdb_spin_unlock(fdb);
+ /* mac is already in fdb table */
+ return 0;
+ }
+
+ mac_tbl_idx = ret;
+
+ mac_info = icssm_prueth_sw_find_free_mac(prueth, bucket_info,
+ mac_tbl_idx, NULL,
+ mac);
+ if (!mac_info) {
+ /* Should not happen */
+ dev_warn(prueth->dev, "OUT of FDB MEM\n");
+ icssm_prueth_sw_fdb_spin_unlock(fdb);
+ return -ENOMEM;
+ }
+
+ memcpy_toio(mac_info->mac, mac, ETH_ALEN);
+ writew(0, &mac_info->age);
+ writeb(emac->port_id - 1, &mac_info->port);
+
+ flags = readb(&mac_info->flags);
+ if (is_static)
+ flags |= FLAG_IS_STATIC;
+ else
+ flags &= ~FLAG_IS_STATIC;
+
+ /* bit 1 - active */
+ flags |= FLAG_ACTIVE;
+ writeb(flags, &mac_info->flags);
+
+ val = readw(&bucket_info->bucket_entries);
+ val++;
+ writew(val, &bucket_info->bucket_entries);
+
+ fdb->total_entries++;
+
+ total_fdb_entries = fdb->total_entries;
+
+ icssm_prueth_sw_fdb_spin_unlock(fdb);
+
+ dev_dbg(prueth->dev, "added fdb: %pM port=%d total_entries=%u\n",
+ mac, emac->port_id, total_fdb_entries);
+
+ return 0;
+}
+
+static int icssm_prueth_sw_delete_fdb_entry(struct prueth_emac *emac,
+ const u8 *mac, u8 is_static)
+{
+ struct fdb_index_tbl_entry __iomem *bucket_info;
+ struct fdb_mac_tbl_entry __iomem *mac_info;
+ struct fdb_mac_tbl_array __iomem *mt;
+ unsigned int hash_val, mac_tbl_idx;
+ unsigned int idx, entries;
+ struct prueth *prueth;
+ int total_fdb_entries;
+ s16 ret, left, right;
+ struct fdb_tbl *fdb;
+ u8 flags;
+ int err;
+ u16 val;
+
+ prueth = emac->prueth;
+ fdb = prueth->fdb_tbl;
+ mt = fdb->mac_tbl_a;
+
+ err = icssm_prueth_sw_fdb_spin_lock(fdb);
+ if (err) {
+ dev_err(prueth->dev, "PRU lock timeout %d\n", err);
+ return err;
+ }
+
+ if (fdb->total_entries == 0) {
+ icssm_prueth_sw_fdb_spin_unlock(fdb);
+ return 0;
+ }
+
+ /* Get the bucket that the mac belongs to */
+ hash_val = icssm_prueth_sw_fdb_hash(mac);
+ bucket_info = FDB_IDX_TBL_ENTRY(hash_val);
+
+ ret = icssm_prueth_sw_fdb_search(mt, bucket_info, mac);
+ if (ret < 0) {
+ icssm_prueth_sw_fdb_spin_unlock(fdb);
+ return ret;
+ }
+
+ mac_tbl_idx = ret;
+ mac_info = FDB_MAC_TBL_ENTRY(mac_tbl_idx);
+
+ /* Shift all elements in bucket to the left. No need to
+ * update index table since only shifting within bucket.
+ */
+ left = mac_tbl_idx;
+ idx = readw(&bucket_info->bucket_idx);
+ entries = readw(&bucket_info->bucket_entries);
+ right = idx + entries - 1;
+ icssm_prueth_sw_fdb_move_range_left(prueth, left, right);
+
+ /* Remove end of bucket from table */
+ mac_info = FDB_MAC_TBL_ENTRY(right);
+ flags = readb(&mac_info->flags);
+ /* active = 0 */
+ flags &= ~FLAG_ACTIVE;
+ writeb(flags, &mac_info->flags);
+ val = readw(&bucket_info->bucket_entries);
+ val--;
+ writew(val, &bucket_info->bucket_entries);
+ fdb->total_entries--;
+
+ total_fdb_entries = fdb->total_entries;
+
+ icssm_prueth_sw_fdb_spin_unlock(fdb);
+
+ dev_dbg(prueth->dev, "del fdb: %pM total_entries=%u\n",
+ mac, total_fdb_entries);
+
+ return 0;
+}
+
+int icssm_prueth_sw_do_purge_fdb(struct prueth_emac *emac)
+{
+ struct fdb_index_tbl_entry __iomem *bucket_info;
+ struct prueth *prueth = emac->prueth;
+ u8 flags, mac[ETH_ALEN];
+ unsigned int hash_val;
+ struct fdb_tbl *fdb;
+ int ret, i;
+ u16 val;
+
+ fdb = prueth->fdb_tbl;
+
+ ret = icssm_prueth_sw_fdb_spin_lock(fdb);
+ if (ret) {
+ dev_err(prueth->dev, "PRU lock timeout %d\n", ret);
+ return ret;
+ }
+
+ if (fdb->total_entries == 0) {
+ icssm_prueth_sw_fdb_spin_unlock(fdb);
+ return 0;
+ }
+
+ for (i = 0; i < FDB_MAC_TBL_MAX_ENTRIES; i++) {
+ flags = readb(&fdb->mac_tbl_a->mac_tbl_entry[i].flags);
+ if ((flags & FLAG_ACTIVE) && !(flags & FLAG_IS_STATIC)) {
+ /* Get the bucket that the mac belongs to */
+ memcpy_fromio(mac, FDB_MAC_TBL_ENTRY(i)->mac,
+ ETH_ALEN);
+ hash_val = icssm_prueth_sw_fdb_hash(mac);
+ bucket_info = FDB_IDX_TBL_ENTRY(hash_val);
+ flags &= ~FLAG_ACTIVE;
+ writeb(flags,
+ &fdb->mac_tbl_a->mac_tbl_entry[i].flags);
+ val = readw(&bucket_info->bucket_entries);
+ val--;
+ writew(val, &bucket_info->bucket_entries);
+ fdb->total_entries--;
+ }
+ }
+
+ icssm_prueth_sw_fdb_spin_unlock(fdb);
+ return 0;
+}
+
+int icssm_prueth_sw_init_fdb_table(struct prueth *prueth)
+{
+ if (prueth->emac_configured)
+ return 0;
+
+ prueth->fdb_tbl = kmalloc(sizeof(*prueth->fdb_tbl), GFP_KERNEL);
+ if (!prueth->fdb_tbl)
+ return -ENOMEM;
+
+ icssm_prueth_sw_fdb_tbl_init(prueth);
+
+ return 0;
+}
+
+/**
+ * icssm_prueth_sw_fdb_add - insert fdb entry
+ *
+ * @emac: EMAC data structure
+ * @fdb: fdb info
+ *
+ */
+void icssm_prueth_sw_fdb_add(struct prueth_emac *emac,
+ struct switchdev_notifier_fdb_info *fdb)
+{
+ icssm_prueth_sw_insert_fdb_entry(emac, fdb->addr, 1);
+}
+
+/**
+ * icssm_prueth_sw_fdb_del - delete fdb entry
+ *
+ * @emac: EMAC data structure
+ * @fdb: fdb info
+ *
+ */
+void icssm_prueth_sw_fdb_del(struct prueth_emac *emac,
+ struct switchdev_notifier_fdb_info *fdb)
+{
+ icssm_prueth_sw_delete_fdb_entry(emac, fdb->addr, 1);
+}
+
+static void icssm_prueth_sw_fdb_work(struct work_struct *work)
+{
+ struct icssm_prueth_sw_fdb_work *fdb_work =
+ container_of(work, struct icssm_prueth_sw_fdb_work, work);
+ struct prueth_emac *emac = fdb_work->emac;
+
+ rtnl_lock();
+
+ /* Interface is not up */
+ if (!emac->prueth->fdb_tbl)
+ goto free;
+
+ switch (fdb_work->event) {
+ case FDB_LEARN:
+ icssm_prueth_sw_insert_fdb_entry(emac, fdb_work->addr, 0);
+ break;
+ case FDB_PURGE:
+ icssm_prueth_sw_do_purge_fdb(emac);
+ break;
+ default:
+ break;
+ }
+
+free:
+ rtnl_unlock();
+ netdev_put(emac->ndev, &fdb_work->ndev_tracker);
+ kfree(fdb_work);
+}
+
+int icssm_prueth_sw_learn_fdb(struct prueth_emac *emac, u8 *src_mac)
+{
+ struct icssm_prueth_sw_fdb_work *fdb_work;
+
+ fdb_work = kzalloc(sizeof(*fdb_work), GFP_ATOMIC);
+ if (WARN_ON(!fdb_work))
+ return -ENOMEM;
+
+ INIT_WORK(&fdb_work->work, icssm_prueth_sw_fdb_work);
+
+ fdb_work->event = FDB_LEARN;
+ fdb_work->emac = emac;
+ ether_addr_copy(fdb_work->addr, src_mac);
+
+ netdev_hold(emac->ndev, &fdb_work->ndev_tracker, GFP_ATOMIC);
+ queue_work(system_long_wq, &fdb_work->work);
+ return 0;
+}
+
+int icssm_prueth_sw_purge_fdb(struct prueth_emac *emac)
+{
+ struct icssm_prueth_sw_fdb_work *fdb_work;
+
+ fdb_work = kzalloc(sizeof(*fdb_work), GFP_ATOMIC);
+ if (WARN_ON(!fdb_work))
+ return -ENOMEM;
+
+ INIT_WORK(&fdb_work->work, icssm_prueth_sw_fdb_work);
+
+ fdb_work->event = FDB_PURGE;
+ fdb_work->emac = emac;
+
+ netdev_hold(emac->ndev, &fdb_work->ndev_tracker, GFP_ATOMIC);
+ queue_work(system_long_wq, &fdb_work->work);
+ return 0;
+}
+
+void icssm_prueth_sw_hostconfig(struct prueth *prueth)
+{
+ void __iomem *dram1_base = prueth->mem[PRUETH_MEM_DRAM1].va;
+ void __iomem *dram;
+
+ /* queue information table */
+ dram = dram1_base + P0_Q1_RX_CONTEXT_OFFSET;
+ memcpy_toio(dram, sw_queue_infos[PRUETH_PORT_QUEUE_HOST],
+ sizeof(sw_queue_infos[PRUETH_PORT_QUEUE_HOST]));
+
+ /* buffer descriptor offset table*/
+ dram = dram1_base + QUEUE_DESCRIPTOR_OFFSET_ADDR;
+ writew(P0_Q1_BD_OFFSET, dram);
+ writew(P0_Q2_BD_OFFSET, dram + 2);
+ writew(P0_Q3_BD_OFFSET, dram + 4);
+ writew(P0_Q4_BD_OFFSET, dram + 6);
+
+ /* buffer offset table */
+ dram = dram1_base + QUEUE_OFFSET_ADDR;
+ writew(P0_Q1_BUFFER_OFFSET, dram);
+ writew(P0_Q2_BUFFER_OFFSET, dram + 2);
+ writew(P0_Q3_BUFFER_OFFSET, dram + 4);
+ writew(P0_Q4_BUFFER_OFFSET, dram + 6);
+
+ /* queue size lookup table */
+ dram = dram1_base + QUEUE_SIZE_ADDR;
+ writew(HOST_QUEUE_1_SIZE, dram);
+ writew(HOST_QUEUE_1_SIZE, dram + 2);
+ writew(HOST_QUEUE_1_SIZE, dram + 4);
+ writew(HOST_QUEUE_1_SIZE, dram + 6);
+
+ /* queue table */
+ dram = dram1_base + P0_QUEUE_DESC_OFFSET;
+ memcpy_toio(dram, queue_descs[PRUETH_PORT_QUEUE_HOST],
+ sizeof(queue_descs[PRUETH_PORT_QUEUE_HOST]));
+}
+
+static int icssm_prueth_sw_port_config(struct prueth *prueth,
+ enum prueth_port port_id)
+{
+ unsigned int tx_context_ofs_addr, rx_context_ofs, queue_desc_ofs;
+ void __iomem *dram, *dram_base, *dram_mac;
+ struct prueth_emac *emac;
+ void __iomem *dram1_base;
+
+ dram1_base = prueth->mem[PRUETH_MEM_DRAM1].va;
+ emac = prueth->emac[port_id - 1];
+ switch (port_id) {
+ case PRUETH_PORT_MII0:
+ tx_context_ofs_addr = TX_CONTEXT_P1_Q1_OFFSET_ADDR;
+ rx_context_ofs = P1_Q1_RX_CONTEXT_OFFSET;
+ queue_desc_ofs = P1_QUEUE_DESC_OFFSET;
+
+ /* for switch PORT MII0 mac addr is in DRAM0. */
+ dram_mac = prueth->mem[PRUETH_MEM_DRAM0].va;
+ break;
+ case PRUETH_PORT_MII1:
+ tx_context_ofs_addr = TX_CONTEXT_P2_Q1_OFFSET_ADDR;
+ rx_context_ofs = P2_Q1_RX_CONTEXT_OFFSET;
+ queue_desc_ofs = P2_QUEUE_DESC_OFFSET;
+
+ /* for switch PORT MII1 mac addr is in DRAM1. */
+ dram_mac = prueth->mem[PRUETH_MEM_DRAM1].va;
+ break;
+ default:
+ netdev_err(emac->ndev, "invalid port\n");
+ return -EINVAL;
+ }
+
+ /* setup mac address */
+ memcpy_toio(dram_mac + PORT_MAC_ADDR, emac->mac_addr, 6);
+
+ /* Remaining switch port configs are in DRAM1 */
+ dram_base = prueth->mem[PRUETH_MEM_DRAM1].va;
+
+ /* queue information table */
+ memcpy_toio(dram_base + tx_context_ofs_addr,
+ sw_queue_infos[port_id],
+ sizeof(sw_queue_infos[port_id]));
+
+ memcpy_toio(dram_base + rx_context_ofs,
+ rx_queue_infos[port_id],
+ sizeof(rx_queue_infos[port_id]));
+
+ /* buffer descriptor offset table*/
+ dram = dram_base + QUEUE_DESCRIPTOR_OFFSET_ADDR +
+ (port_id * NUM_QUEUES * sizeof(u16));
+ writew(sw_queue_infos[port_id][PRUETH_QUEUE1].buffer_desc_offset, dram);
+ writew(sw_queue_infos[port_id][PRUETH_QUEUE2].buffer_desc_offset,
+ dram + 2);
+ writew(sw_queue_infos[port_id][PRUETH_QUEUE3].buffer_desc_offset,
+ dram + 4);
+ writew(sw_queue_infos[port_id][PRUETH_QUEUE4].buffer_desc_offset,
+ dram + 6);
+
+ /* buffer offset table */
+ dram = dram_base + QUEUE_OFFSET_ADDR +
+ port_id * NUM_QUEUES * sizeof(u16);
+ writew(sw_queue_infos[port_id][PRUETH_QUEUE1].buffer_offset, dram);
+ writew(sw_queue_infos[port_id][PRUETH_QUEUE2].buffer_offset,
+ dram + 2);
+ writew(sw_queue_infos[port_id][PRUETH_QUEUE3].buffer_offset,
+ dram + 4);
+ writew(sw_queue_infos[port_id][PRUETH_QUEUE4].buffer_offset,
+ dram + 6);
+
+ /* queue size lookup table */
+ dram = dram_base + QUEUE_SIZE_ADDR +
+ port_id * NUM_QUEUES * sizeof(u16);
+ writew(QUEUE_1_SIZE, dram);
+ writew(QUEUE_2_SIZE, dram + 2);
+ writew(QUEUE_3_SIZE, dram + 4);
+ writew(QUEUE_4_SIZE, dram + 6);
+
+ /* queue table */
+ memcpy_toio(dram_base + queue_desc_ofs,
+ &queue_descs[port_id][0],
+ 4 * sizeof(queue_descs[port_id][0]));
+
+ emac->rx_queue_descs = dram1_base + P0_QUEUE_DESC_OFFSET;
+ emac->tx_queue_descs = dram1_base +
+ rx_queue_infos[port_id][PRUETH_QUEUE1].queue_desc_offset;
+
+ return 0;
+}
+
+int icssm_prueth_sw_emac_config(struct prueth_emac *emac)
+{
+ struct prueth *prueth = emac->prueth;
+ u32 sharedramaddr, ocmcaddr;
+ int ret;
+
+ /* PRU needs local shared RAM address for C28 */
+ sharedramaddr = ICSS_LOCAL_SHARED_RAM;
+ /* PRU needs real global OCMC address for C30*/
+ ocmcaddr = (u32)prueth->mem[PRUETH_MEM_OCMC].pa;
+
+ if (prueth->emac_configured & BIT(emac->port_id))
+ return 0;
+
+ ret = icssm_prueth_sw_port_config(prueth, emac->port_id);
+ if (ret)
+ return ret;
+
+ if (!prueth->emac_configured) {
+ /* Set in constant table C28 of PRUn to ICSS Shared memory */
+ pru_rproc_set_ctable(prueth->pru0, PRU_C28, sharedramaddr);
+ pru_rproc_set_ctable(prueth->pru1, PRU_C28, sharedramaddr);
+
+ /* Set in constant table C30 of PRUn to OCMC memory */
+ pru_rproc_set_ctable(prueth->pru0, PRU_C30, ocmcaddr);
+ pru_rproc_set_ctable(prueth->pru1, PRU_C30, ocmcaddr);
+ }
+ return 0;
+}
+
+int icssm_prueth_sw_boot_prus(struct prueth *prueth, struct net_device *ndev)
+{
+ const struct prueth_firmware *pru_firmwares;
+ const char *fw_name, *fw_name1;
+ int ret;
+
+ if (prueth->emac_configured)
+ return 0;
+
+ pru_firmwares = &prueth->fw_data->fw_pru[PRUSS_PRU0];
+ fw_name = pru_firmwares->fw_name[prueth->eth_type];
+ pru_firmwares = &prueth->fw_data->fw_pru[PRUSS_PRU1];
+ fw_name1 = pru_firmwares->fw_name[prueth->eth_type];
+
+ ret = rproc_set_firmware(prueth->pru0, fw_name);
+ if (ret) {
+ netdev_err(ndev, "failed to set PRU0 firmware %s: %d\n",
+ fw_name, ret);
+ return ret;
+ }
+ ret = rproc_boot(prueth->pru0);
+ if (ret) {
+ netdev_err(ndev, "failed to boot PRU0: %d\n", ret);
+ return ret;
+ }
+
+ ret = rproc_set_firmware(prueth->pru1, fw_name1);
+ if (ret) {
+ netdev_err(ndev, "failed to set PRU1 firmware %s: %d\n",
+ fw_name1, ret);
+ goto rproc0_shutdown;
+ }
+ ret = rproc_boot(prueth->pru1);
+ if (ret) {
+ netdev_err(ndev, "failed to boot PRU1: %d\n", ret);
+ goto rproc0_shutdown;
+ }
+
+ return 0;
+
+rproc0_shutdown:
+ rproc_shutdown(prueth->pru0);
+ return ret;
+}
+
+int icssm_prueth_sw_shutdown_prus(struct prueth_emac *emac,
+ struct net_device *ndev)
+{
+ struct prueth *prueth = emac->prueth;
+
+ if (prueth->emac_configured)
+ return 0;
+
+ rproc_shutdown(prueth->pru0);
+ rproc_shutdown(prueth->pru1);
+
+ return 0;
+}
diff --git a/drivers/net/ethernet/ti/icssm/icssm_prueth_switch.h b/drivers/net/ethernet/ti/icssm/icssm_prueth_switch.h
new file mode 100644
index 000000000000..e6111bba166e
--- /dev/null
+++ b/drivers/net/ethernet/ti/icssm/icssm_prueth_switch.h
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2020-2021 Texas Instruments Incorporated - https://www.ti.com
+ */
+
+#ifndef __NET_TI_PRUETH_SWITCH_H
+#define __NET_TI_PRUETH_SWITCH_H
+
+#include <net/switchdev.h>
+
+#include "icssm_prueth.h"
+#include "icssm_prueth_fdb_tbl.h"
+#include "icssm_switchdev.h"
+
+void icssm_prueth_sw_set_stp_state(struct prueth *prueth,
+ enum prueth_port port, u8 state);
+u8 icssm_prueth_sw_get_stp_state(struct prueth *prueth,
+ enum prueth_port port);
+
+extern const struct prueth_queue_info sw_queue_infos[][4];
+
+void icssm_prueth_sw_fdb_tbl_init(struct prueth *prueth);
+int icssm_prueth_sw_init_fdb_table(struct prueth *prueth);
+void icssm_prueth_sw_free_fdb_table(struct prueth *prueth);
+int icssm_prueth_sw_do_purge_fdb(struct prueth_emac *emac);
+void icssm_prueth_sw_fdb_add(struct prueth_emac *emac,
+ struct switchdev_notifier_fdb_info *fdb);
+void icssm_prueth_sw_fdb_del(struct prueth_emac *emac,
+ struct switchdev_notifier_fdb_info *fdb);
+int icssm_prueth_sw_learn_fdb(struct prueth_emac *emac, u8 *src_mac);
+int icssm_prueth_sw_purge_fdb(struct prueth_emac *emac);
+void icssm_prueth_sw_hostconfig(struct prueth *prueth);
+int icssm_prueth_sw_emac_config(struct prueth_emac *emac);
+int icssm_prueth_sw_boot_prus(struct prueth *prueth, struct net_device *ndev);
+int icssm_prueth_sw_shutdown_prus(struct prueth_emac *emac,
+ struct net_device *ndev);
+
+#endif /* __NET_TI_PRUETH_SWITCH_H */
diff --git a/drivers/net/ethernet/ti/icssm/icssm_switch.h b/drivers/net/ethernet/ti/icssm/icssm_switch.h
index 8b494ffdcde7..5ba9ce14da44 100644
--- a/drivers/net/ethernet/ti/icssm/icssm_switch.h
+++ b/drivers/net/ethernet/ti/icssm/icssm_switch.h
@@ -117,6 +117,15 @@
#define STATISTICS_OFFSET 0x1F00
#define STAT_SIZE 0x98
+/* The following offsets indicate which sections of the memory are used
+ * for switch internal tasks
+ */
+#define SWITCH_SPECIFIC_DRAM0_START_SIZE 0x100
+#define SWITCH_SPECIFIC_DRAM0_START_OFFSET 0x1F00
+
+#define SWITCH_SPECIFIC_DRAM1_START_SIZE 0x300
+#define SWITCH_SPECIFIC_DRAM1_START_OFFSET 0x1D00
+
/* Offset for storing
* 1. Storm Prevention Params
* 2. PHY Speed Offset
@@ -146,6 +155,74 @@
/* 4 bytes ? */
#define STP_INVALID_STATE_OFFSET (STATISTICS_OFFSET + STAT_SIZE + 33)
+/* DRAM1 Offsets for Switch */
+/* 4 queue descriptors for port 0 (host receive) */
+#define P0_QUEUE_DESC_OFFSET 0x1E7C
+#define P1_QUEUE_DESC_OFFSET 0x1E9C
+#define P2_QUEUE_DESC_OFFSET 0x1EBC
+/* collision descriptor of port 0 */
+#define P0_COL_QUEUE_DESC_OFFSET 0x1E64
+#define P1_COL_QUEUE_DESC_OFFSET 0x1E6C
+#define P2_COL_QUEUE_DESC_OFFSET 0x1E74
+/* Collision Status Register
+ * P0: bit 0 is pending flag, bit 1..2 indicates which queue,
+ * P1: bit 8 is pending flag, 9..10 is queue number
+ * P2: bit 16 is pending flag, 17..18 is queue number, remaining bits are 0.
+ */
+#define COLLISION_STATUS_ADDR 0x1E60
+
+#define INTERFACE_MAC_ADDR 0x1E58
+#define P2_MAC_ADDR 0x1E50
+#define P1_MAC_ADDR 0x1E48
+
+#define QUEUE_SIZE_ADDR 0x1E30
+#define QUEUE_OFFSET_ADDR 0x1E18
+#define QUEUE_DESCRIPTOR_OFFSET_ADDR 0x1E00
+
+#define COL_RX_CONTEXT_P2_OFFSET_ADDR (COL_RX_CONTEXT_P1_OFFSET_ADDR + 12)
+#define COL_RX_CONTEXT_P1_OFFSET_ADDR (COL_RX_CONTEXT_P0_OFFSET_ADDR + 12)
+#define COL_RX_CONTEXT_P0_OFFSET_ADDR (P2_Q4_RX_CONTEXT_OFFSET + 8)
+
+/* Port 2 Rx Context */
+#define P2_Q4_RX_CONTEXT_OFFSET (P2_Q3_RX_CONTEXT_OFFSET + 8)
+#define P2_Q3_RX_CONTEXT_OFFSET (P2_Q2_RX_CONTEXT_OFFSET + 8)
+#define P2_Q2_RX_CONTEXT_OFFSET (P2_Q1_RX_CONTEXT_OFFSET + 8)
+#define P2_Q1_RX_CONTEXT_OFFSET RX_CONTEXT_P2_Q1_OFFSET_ADDR
+#define RX_CONTEXT_P2_Q1_OFFSET_ADDR (P1_Q4_RX_CONTEXT_OFFSET + 8)
+
+/* Port 1 Rx Context */
+#define P1_Q4_RX_CONTEXT_OFFSET (P1_Q3_RX_CONTEXT_OFFSET + 8)
+#define P1_Q3_RX_CONTEXT_OFFSET (P1_Q2_RX_CONTEXT_OFFSET + 8)
+#define P1_Q2_RX_CONTEXT_OFFSET (P1_Q1_RX_CONTEXT_OFFSET + 8)
+#define P1_Q1_RX_CONTEXT_OFFSET (RX_CONTEXT_P1_Q1_OFFSET_ADDR)
+#define RX_CONTEXT_P1_Q1_OFFSET_ADDR (P0_Q4_RX_CONTEXT_OFFSET + 8)
+
+/* Host Port Rx Context */
+#define P0_Q4_RX_CONTEXT_OFFSET (P0_Q3_RX_CONTEXT_OFFSET + 8)
+#define P0_Q3_RX_CONTEXT_OFFSET (P0_Q2_RX_CONTEXT_OFFSET + 8)
+#define P0_Q2_RX_CONTEXT_OFFSET (P0_Q1_RX_CONTEXT_OFFSET + 8)
+#define P0_Q1_RX_CONTEXT_OFFSET RX_CONTEXT_P0_Q1_OFFSET_ADDR
+#define RX_CONTEXT_P0_Q1_OFFSET_ADDR (COL_TX_CONTEXT_P2_Q1_OFFSET_ADDR + 8)
+
+/* Port 2 Tx Collision Context */
+#define COL_TX_CONTEXT_P2_Q1_OFFSET_ADDR (COL_TX_CONTEXT_P1_Q1_OFFSET_ADDR + 8)
+/* Port 1 Tx Collision Context */
+#define COL_TX_CONTEXT_P1_Q1_OFFSET_ADDR (P2_Q4_TX_CONTEXT_OFFSET + 8)
+
+/* Port 2 */
+#define P2_Q4_TX_CONTEXT_OFFSET (P2_Q3_TX_CONTEXT_OFFSET + 8)
+#define P2_Q3_TX_CONTEXT_OFFSET (P2_Q2_TX_CONTEXT_OFFSET + 8)
+#define P2_Q2_TX_CONTEXT_OFFSET (P2_Q1_TX_CONTEXT_OFFSET + 8)
+#define P2_Q1_TX_CONTEXT_OFFSET TX_CONTEXT_P2_Q1_OFFSET_ADDR
+#define TX_CONTEXT_P2_Q1_OFFSET_ADDR (P1_Q4_TX_CONTEXT_OFFSET + 8)
+
+/* Port 1 */
+#define P1_Q4_TX_CONTEXT_OFFSET (P1_Q3_TX_CONTEXT_OFFSET + 8)
+#define P1_Q3_TX_CONTEXT_OFFSET (P1_Q2_TX_CONTEXT_OFFSET + 8)
+#define P1_Q2_TX_CONTEXT_OFFSET (P1_Q1_TX_CONTEXT_OFFSET + 8)
+#define P1_Q1_TX_CONTEXT_OFFSET TX_CONTEXT_P1_Q1_OFFSET_ADDR
+#define TX_CONTEXT_P1_Q1_OFFSET_ADDR SWITCH_SPECIFIC_DRAM1_START_OFFSET
+
/* DRAM Offsets for EMAC
* Present on Both DRAM0 and DRAM1
*/
@@ -254,4 +331,30 @@
#define P0_COL_BUFFER_OFFSET 0xEE00
#define P0_Q1_BUFFER_OFFSET 0x0000
+#define V2_1_FDB_TBL_LOC PRUETH_MEM_SHARED_RAM
+#define V2_1_FDB_TBL_OFFSET 0x2000
+
+#define FDB_INDEX_TBL_MAX_ENTRIES 256
+#define FDB_MAC_TBL_MAX_ENTRIES 256
+
+#define FDB_INDEX_TBL_OFFSET V2_1_FDB_TBL_OFFSET
+#define FDB_INDEX_TBL_SIZE (FDB_INDEX_TBL_MAX_ENTRIES * \
+ sizeof(struct fdb_index_tbl_entry))
+
+#define FDB_MAC_TBL_OFFSET (FDB_INDEX_TBL_OFFSET + FDB_INDEX_TBL_SIZE)
+#define FDB_MAC_TBL_SIZE (FDB_MAC_TBL_MAX_ENTRIES * \
+ sizeof(struct fdb_mac_tbl_entry))
+
+#define FDB_PORT1_STP_CFG_OFFSET (FDB_MAC_TBL_OFFSET + FDB_MAC_TBL_SIZE)
+#define FDB_PORT_STP_CFG_SIZE sizeof(struct fdb_stp_config)
+#define FDB_PORT2_STP_CFG_OFFSET (FDB_PORT1_STP_CFG_OFFSET + \
+ FDB_PORT_STP_CFG_SIZE)
+
+#define FDB_FLOOD_ENABLE_FLAGS_OFFSET (FDB_PORT2_STP_CFG_OFFSET + \
+ FDB_PORT_STP_CFG_SIZE)
+#define FDB_FLOOD_ENABLE_FLAGS_SIZE sizeof(struct fdb_flood_config)
+
+#define FDB_LOCKS_OFFSET (FDB_FLOOD_ENABLE_FLAGS_OFFSET + \
+ FDB_FLOOD_ENABLE_FLAGS_SIZE)
+
#endif /* __ICSS_SWITCH_H */
diff --git a/drivers/net/ethernet/ti/icssm/icssm_switchdev.c b/drivers/net/ethernet/ti/icssm/icssm_switchdev.c
new file mode 100644
index 000000000000..414ec9fc02a0
--- /dev/null
+++ b/drivers/net/ethernet/ti/icssm/icssm_switchdev.c
@@ -0,0 +1,333 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/* Texas Instruments ICSSM Ethernet Driver
+ *
+ * Copyright (C) 2018-2022 Texas Instruments Incorporated - https://www.ti.com/
+ *
+ */
+
+#include <linux/etherdevice.h>
+#include <linux/kernel.h>
+#include <linux/remoteproc.h>
+#include <net/switchdev.h>
+
+#include "icssm_prueth.h"
+#include "icssm_prueth_switch.h"
+#include "icssm_prueth_fdb_tbl.h"
+
+/* switchev event work */
+struct icssm_sw_event_work {
+ netdevice_tracker ndev_tracker;
+ struct work_struct work;
+ struct switchdev_notifier_fdb_info fdb_info;
+ struct prueth_emac *emac;
+ unsigned long event;
+};
+
+void icssm_prueth_sw_set_stp_state(struct prueth *prueth,
+ enum prueth_port port, u8 state)
+{
+ struct fdb_tbl *t = prueth->fdb_tbl;
+
+ writeb(state, port - 1 ? (void __iomem *)&t->port2_stp_cfg->state :
+ (void __iomem *)&t->port1_stp_cfg->state);
+}
+
+u8 icssm_prueth_sw_get_stp_state(struct prueth *prueth, enum prueth_port port)
+{
+ struct fdb_tbl *t = prueth->fdb_tbl;
+ u8 state;
+
+ state = readb(port - 1 ? (void __iomem *)&t->port2_stp_cfg->state :
+ (void __iomem *)&t->port1_stp_cfg->state);
+ return state;
+}
+
+static int icssm_prueth_sw_attr_set(struct net_device *ndev, const void *ctx,
+ const struct switchdev_attr *attr,
+ struct netlink_ext_ack *extack)
+{
+ struct prueth_emac *emac = netdev_priv(ndev);
+ struct prueth *prueth = emac->prueth;
+ int err = 0;
+ u8 o_state;
+
+ /* Interface is not up */
+ if (!prueth->fdb_tbl)
+ return 0;
+
+ switch (attr->id) {
+ case SWITCHDEV_ATTR_ID_PORT_STP_STATE:
+ o_state = icssm_prueth_sw_get_stp_state(prueth, emac->port_id);
+ icssm_prueth_sw_set_stp_state(prueth, emac->port_id,
+ attr->u.stp_state);
+
+ if (o_state != attr->u.stp_state)
+ icssm_prueth_sw_purge_fdb(emac);
+
+ dev_dbg(prueth->dev, "attr set: stp state:%u port:%u\n",
+ attr->u.stp_state, emac->port_id);
+ break;
+ default:
+ err = -EOPNOTSUPP;
+ break;
+ }
+
+ return err;
+}
+
+static void icssm_prueth_sw_fdb_offload(struct net_device *ndev,
+ struct switchdev_notifier_fdb_info *rcv)
+{
+ struct switchdev_notifier_fdb_info info;
+
+ info.addr = rcv->addr;
+ info.vid = rcv->vid;
+ call_switchdev_notifiers(SWITCHDEV_FDB_OFFLOADED, ndev, &info.info,
+ NULL);
+}
+
+/**
+ * icssm_sw_event_work - insert/delete fdb entry
+ *
+ * @work: work structure
+ *
+ */
+static void icssm_sw_event_work(struct work_struct *work)
+{
+ struct icssm_sw_event_work *switchdev_work =
+ container_of(work, struct icssm_sw_event_work, work);
+ struct prueth_emac *emac = switchdev_work->emac;
+ struct switchdev_notifier_fdb_info *fdb;
+ struct prueth *prueth = emac->prueth;
+ int port = emac->port_id;
+
+ rtnl_lock();
+
+ /* Interface is not up */
+ if (!emac->prueth->fdb_tbl)
+ goto free;
+
+ switch (switchdev_work->event) {
+ case SWITCHDEV_FDB_ADD_TO_DEVICE:
+ fdb = &switchdev_work->fdb_info;
+ dev_dbg(prueth->dev,
+ "prueth fdb add: MACID = %pM vid = %u flags = %u -- port %d\n",
+ fdb->addr, fdb->vid, fdb->added_by_user, port);
+
+ if (!fdb->added_by_user)
+ break;
+
+ if (fdb->is_local)
+ break;
+
+ icssm_prueth_sw_fdb_add(emac, fdb);
+ icssm_prueth_sw_fdb_offload(emac->ndev, fdb);
+ break;
+ case SWITCHDEV_FDB_DEL_TO_DEVICE:
+ fdb = &switchdev_work->fdb_info;
+ dev_dbg(prueth->dev,
+ "prueth fdb del: MACID = %pM vid = %u flags = %u -- port %d\n",
+ fdb->addr, fdb->vid, fdb->added_by_user, port);
+
+ if (fdb->is_local)
+ break;
+
+ icssm_prueth_sw_fdb_del(emac, fdb);
+ break;
+ default:
+ break;
+ }
+
+free:
+ rtnl_unlock();
+
+ netdev_put(emac->ndev, &switchdev_work->ndev_tracker);
+ kfree(switchdev_work->fdb_info.addr);
+ kfree(switchdev_work);
+}
+
+/* called under rcu_read_lock() */
+static int icssm_prueth_sw_switchdev_event(struct notifier_block *unused,
+ unsigned long event, void *ptr)
+{
+ struct net_device *ndev = switchdev_notifier_info_to_dev(ptr);
+ struct switchdev_notifier_fdb_info *fdb_info = ptr;
+ struct prueth_emac *emac = netdev_priv(ndev);
+ struct icssm_sw_event_work *switchdev_work;
+ int err;
+
+ if (!icssm_prueth_sw_port_dev_check(ndev))
+ return NOTIFY_DONE;
+
+ if (event == SWITCHDEV_PORT_ATTR_SET) {
+ err = switchdev_handle_port_attr_set
+ (ndev, ptr, icssm_prueth_sw_port_dev_check,
+ icssm_prueth_sw_attr_set);
+ return notifier_from_errno(err);
+ }
+
+ switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC);
+ if (WARN_ON(!switchdev_work))
+ return NOTIFY_BAD;
+
+ INIT_WORK(&switchdev_work->work, icssm_sw_event_work);
+ switchdev_work->emac = emac;
+ switchdev_work->event = event;
+
+ switch (event) {
+ case SWITCHDEV_FDB_ADD_TO_DEVICE:
+ case SWITCHDEV_FDB_DEL_TO_DEVICE:
+ memcpy(&switchdev_work->fdb_info, ptr,
+ sizeof(switchdev_work->fdb_info));
+ switchdev_work->fdb_info.addr = kzalloc(ETH_ALEN, GFP_ATOMIC);
+ if (!switchdev_work->fdb_info.addr)
+ goto err_addr_alloc;
+ ether_addr_copy((u8 *)switchdev_work->fdb_info.addr,
+ fdb_info->addr);
+ netdev_hold(ndev, &switchdev_work->ndev_tracker, GFP_ATOMIC);
+ break;
+ default:
+ kfree(switchdev_work);
+ return NOTIFY_DONE;
+ }
+
+ queue_work(system_long_wq, &switchdev_work->work);
+
+ return NOTIFY_DONE;
+
+err_addr_alloc:
+ kfree(switchdev_work);
+ return NOTIFY_BAD;
+}
+
+static int icssm_prueth_switchdev_obj_add(struct net_device *ndev,
+ const void *ctx,
+ const struct switchdev_obj *obj,
+ struct netlink_ext_ack *extack)
+{
+ struct switchdev_obj_port_mdb *mdb = SWITCHDEV_OBJ_PORT_MDB(obj);
+ struct prueth_emac *emac = netdev_priv(ndev);
+ struct prueth *prueth = emac->prueth;
+ int ret = 0;
+ u8 hash;
+
+ switch (obj->id) {
+ case SWITCHDEV_OBJ_ID_HOST_MDB:
+ dev_dbg(prueth->dev, "MDB add: %s: vid %u:%pM port: %x\n",
+ ndev->name, mdb->vid, mdb->addr, emac->port_id);
+ hash = icssm_emac_get_mc_hash(mdb->addr, emac->mc_filter_mask);
+ icssm_emac_mc_filter_bin_allow(emac, hash);
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+ return ret;
+}
+
+static int icssm_prueth_switchdev_obj_del(struct net_device *ndev,
+ const void *ctx,
+ const struct switchdev_obj *obj)
+{
+ struct switchdev_obj_port_mdb *mdb = SWITCHDEV_OBJ_PORT_MDB(obj);
+ struct prueth_emac *emac = netdev_priv(ndev);
+ struct prueth *prueth = emac->prueth;
+ struct netdev_hw_addr *ha;
+ u8 hash, tmp_hash;
+ int ret = 0;
+ u8 *mask;
+
+ switch (obj->id) {
+ case SWITCHDEV_OBJ_ID_HOST_MDB:
+ dev_dbg(prueth->dev, "MDB del: %s: vid %u:%pM port: %x\n",
+ ndev->name, mdb->vid, mdb->addr, emac->port_id);
+ if (prueth->hw_bridge_dev) {
+ mask = emac->mc_filter_mask;
+ hash = icssm_emac_get_mc_hash(mdb->addr, mask);
+ netdev_for_each_mc_addr(ha, prueth->hw_bridge_dev) {
+ tmp_hash = icssm_emac_get_mc_hash(ha->addr,
+ mask);
+ /* Another MC address is in the bin.
+ * Don't disable.
+ */
+ if (tmp_hash == hash)
+ return 0;
+ }
+ icssm_emac_mc_filter_bin_disallow(emac, hash);
+ }
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+ return ret;
+}
+
+/* switchdev notifiers */
+static int icssm_prueth_sw_blocking_event(struct notifier_block *unused,
+ unsigned long event, void *ptr)
+{
+ struct net_device *ndev = switchdev_notifier_info_to_dev(ptr);
+ int err;
+
+ switch (event) {
+ case SWITCHDEV_PORT_OBJ_ADD:
+ err = switchdev_handle_port_obj_add
+ (ndev, ptr, icssm_prueth_sw_port_dev_check,
+ icssm_prueth_switchdev_obj_add);
+ return notifier_from_errno(err);
+
+ case SWITCHDEV_PORT_OBJ_DEL:
+ err = switchdev_handle_port_obj_del
+ (ndev, ptr, icssm_prueth_sw_port_dev_check,
+ icssm_prueth_switchdev_obj_del);
+ return notifier_from_errno(err);
+
+ case SWITCHDEV_PORT_ATTR_SET:
+ err = switchdev_handle_port_attr_set
+ (ndev, ptr, icssm_prueth_sw_port_dev_check,
+ icssm_prueth_sw_attr_set);
+ return notifier_from_errno(err);
+
+ default:
+ break;
+ }
+
+ return NOTIFY_DONE;
+}
+
+int icssm_prueth_sw_register_notifiers(struct prueth *prueth)
+{
+ int ret = 0;
+
+ prueth->prueth_switchdev_nb.notifier_call =
+ &icssm_prueth_sw_switchdev_event;
+ ret = register_switchdev_notifier(&prueth->prueth_switchdev_nb);
+ if (ret) {
+ dev_err(prueth->dev,
+ "register switchdev notifier failed ret:%d\n", ret);
+ return ret;
+ }
+
+ prueth->prueth_switchdev_bl_nb.notifier_call =
+ &icssm_prueth_sw_blocking_event;
+ ret = register_switchdev_blocking_notifier
+ (&prueth->prueth_switchdev_bl_nb);
+ if (ret) {
+ dev_err(prueth->dev,
+ "register switchdev blocking notifier failed ret:%d\n",
+ ret);
+ unregister_switchdev_notifier(&prueth->prueth_switchdev_nb);
+ }
+
+ return ret;
+}
+
+void icssm_prueth_sw_unregister_notifiers(struct prueth *prueth)
+{
+ unregister_switchdev_blocking_notifier(&prueth->prueth_switchdev_bl_nb);
+ unregister_switchdev_notifier(&prueth->prueth_switchdev_nb);
+}
diff --git a/drivers/net/ethernet/ti/icssm/icssm_switchdev.h b/drivers/net/ethernet/ti/icssm/icssm_switchdev.h
new file mode 100644
index 000000000000..b03a98e3472e
--- /dev/null
+++ b/drivers/net/ethernet/ti/icssm/icssm_switchdev.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2020-2021 Texas Instruments Incorporated - https://www.ti.com
+ */
+
+#ifndef __NET_TI_ICSSM_SWITCHDEV_H
+#define __NET_TI_ICSSM_SWITCHDEV_H
+
+#include "icssm_prueth.h"
+
+int icssm_prueth_sw_register_notifiers(struct prueth *prueth);
+void icssm_prueth_sw_unregister_notifiers(struct prueth *prueth);
+bool icssm_prueth_sw_port_dev_check(const struct net_device *ndev);
+#endif /* __NET_TI_ICSSM_SWITCHDEV_H */
diff --git a/drivers/net/ethernet/ti/icssm/icssm_vlan_mcast_filter_mmap.h b/drivers/net/ethernet/ti/icssm/icssm_vlan_mcast_filter_mmap.h
new file mode 100644
index 000000000000..c177c19a36ef
--- /dev/null
+++ b/drivers/net/ethernet/ti/icssm/icssm_vlan_mcast_filter_mmap.h
@@ -0,0 +1,120 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/* Copyright (C) 2015-2021 Texas Instruments Incorporated - https://www.ti.com
+ *
+ * This file contains VLAN/Multicast filtering feature memory map
+ *
+ */
+
+#ifndef ICSS_VLAN_MULTICAST_FILTER_MM_H
+#define ICSS_VLAN_MULTICAST_FILTER_MM_H
+
+/* VLAN/Multicast filter defines & offsets,
+ * present on both PRU0 and PRU1 DRAM
+ */
+
+/* Feature enable/disable values for multicast filtering */
+#define ICSS_EMAC_FW_MULTICAST_FILTER_CTRL_DISABLED 0x00
+#define ICSS_EMAC_FW_MULTICAST_FILTER_CTRL_ENABLED 0x01
+
+/* Feature enable/disable values for VLAN filtering */
+#define ICSS_EMAC_FW_VLAN_FILTER_CTRL_DISABLED 0x00
+#define ICSS_EMAC_FW_VLAN_FILTER_CTRL_ENABLED 0x01
+
+/* Add/remove multicast mac id for filtering bin */
+#define ICSS_EMAC_FW_MULTICAST_FILTER_HOST_RCV_ALLOWED 0x01
+#define ICSS_EMAC_FW_MULTICAST_FILTER_HOST_RCV_NOT_ALLOWED 0x00
+
+/* Default HASH value for the multicast filtering Mask */
+#define ICSS_EMAC_FW_MULTICAST_FILTER_INIT_VAL 0xFF
+
+/* Size requirements for Multicast filtering feature */
+#define ICSS_EMAC_FW_MULTICAST_TABLE_SIZE_BYTES 256
+#define ICSS_EMAC_FW_MULTICAST_FILTER_MASK_SIZE_BYTES 6
+#define ICSS_EMAC_FW_MULTICAST_FILTER_CTRL_SIZE_BYTES 1
+#define ICSS_EMAC_FW_MULTICAST_FILTER_MASK_OVERRIDE_STATUS_SIZE_BYTES 1
+#define ICSS_EMAC_FW_MULTICAST_FILTER_DROP_CNT_SIZE_BYTES 4
+
+/* Size requirements for VLAN filtering feature : 4096 bits = 512 bytes */
+#define ICSS_EMAC_FW_VLAN_FILTER_TABLE_SIZE_BYTES 512
+#define ICSS_EMAC_FW_VLAN_FILTER_CTRL_SIZE_BYTES 1
+#define ICSS_EMAC_FW_VLAN_FILTER_DROP_CNT_SIZE_BYTES 4
+
+/* Mask override set status */
+#define ICSS_EMAC_FW_MULTICAST_FILTER_MASK_OVERRIDE_SET 1
+/* Mask override not set status */
+#define ICSS_EMAC_FW_MULTICAST_FILTER_MASK_OVERRIDE_NOT_SET 0
+/* 6 bytes HASH Mask for the MAC */
+#define ICSS_EMAC_FW_MULTICAST_FILTER_MASK_OFFSET 0xF4
+/* 0 -> multicast filtering disabled | 1 -> multicast filtering enabled */
+#define ICSS_EMAC_FW_MULTICAST_FILTER_CTRL_OFFSET \
+ (ICSS_EMAC_FW_MULTICAST_FILTER_MASK_OFFSET + \
+ ICSS_EMAC_FW_MULTICAST_FILTER_MASK_SIZE_BYTES)
+/* Status indicating if the HASH override is done or not: 0: no, 1: yes */
+#define ICSS_EMAC_FW_MULTICAST_FILTER_OVERRIDE_STATUS \
+ (ICSS_EMAC_FW_MULTICAST_FILTER_CTRL_OFFSET + \
+ ICSS_EMAC_FW_MULTICAST_FILTER_CTRL_SIZE_BYTES)
+/* Multicast drop statistics */
+#define ICSS_EMAC_FW_MULTICAST_FILTER_DROP_CNT_OFFSET \
+ (ICSS_EMAC_FW_MULTICAST_FILTER_OVERRIDE_STATUS +\
+ ICSS_EMAC_FW_MULTICAST_FILTER_MASK_OVERRIDE_STATUS_SIZE_BYTES)
+/* Multicast table */
+#define ICSS_EMAC_FW_MULTICAST_FILTER_TABLE \
+ (ICSS_EMAC_FW_MULTICAST_FILTER_DROP_CNT_OFFSET +\
+ ICSS_EMAC_FW_MULTICAST_FILTER_DROP_CNT_SIZE_BYTES)
+
+/* Multicast filter defines & offsets for LRE
+ */
+#define ICSS_LRE_FW_MULTICAST_TABLE_SEARCH_OP_CONTROL_BIT 0xE0
+/* one byte field :
+ * 0 -> multicast filtering disabled
+ * 1 -> multicast filtering enabled
+ */
+#define ICSS_LRE_FW_MULTICAST_FILTER_MASK 0xE4
+#define ICSS_LRE_FW_MULTICAST_FILTER_TABLE 0x100
+
+/* VLAN table Offsets */
+#define ICSS_EMAC_FW_VLAN_FLTR_TBL_BASE_ADDR 0x200
+#define ICSS_EMAC_FW_VLAN_FILTER_CTRL_BITMAP_OFFSET 0xEF
+#define ICSS_EMAC_FW_VLAN_FILTER_DROP_CNT_OFFSET \
+ (ICSS_EMAC_FW_VLAN_FILTER_CTRL_BITMAP_OFFSET + \
+ ICSS_EMAC_FW_VLAN_FILTER_CTRL_SIZE_BYTES)
+
+/* VLAN filter Control Bit maps */
+/* one bit field, bit 0: | 0 : VLAN filter disabled (default),
+ * 1: VLAN filter enabled
+ */
+#define ICSS_EMAC_FW_VLAN_FILTER_CTRL_ENABLE_BIT 0
+/* one bit field, bit 1: | 0 : untagged host rcv allowed (default),
+ * 1: untagged host rcv not allowed
+ */
+#define ICSS_EMAC_FW_VLAN_FILTER_UNTAG_HOST_RCV_ALLOW_CTRL_BIT 1
+/* one bit field, bit 1: | 0 : priotag host rcv allowed (default),
+ * 1: priotag host rcv not allowed
+ */
+#define ICSS_EMAC_FW_VLAN_FILTER_PRIOTAG_HOST_RCV_ALLOW_CTRL_BIT 2
+/* one bit field, bit 1: | 0 : skip sv vlan flow
+ * :1 : take sv vlan flow (not applicable for dual emac )
+ */
+#define ICSS_EMAC_FW_VLAN_FILTER_SV_VLAN_FLOW_HOST_RCV_ALLOW_CTRL_BIT 3
+
+/* VLAN IDs */
+#define ICSS_EMAC_FW_VLAN_FILTER_PRIOTAG_VID 0
+#define ICSS_EMAC_FW_VLAN_FILTER_VID_MIN 0x0000
+#define ICSS_EMAC_FW_VLAN_FILTER_VID_MAX 0x0FFF
+
+/* VLAN Filtering Commands */
+#define ICSS_EMAC_FW_VLAN_FILTER_ADD_VLAN_VID_CMD 0x00
+#define ICSS_EMAC_FW_VLAN_FILTER_REMOVE_VLAN_VID_CMD 0x01
+
+/* Switch defines for VLAN/MC filtering */
+/* SRAM
+ * VLAN filter defines & offsets
+ */
+#define ICSS_LRE_FW_VLAN_FLTR_CTRL_BYTE 0x1FE
+/* one bit field | 0 : VLAN filter disabled
+ * | 1 : VLAN filter enabled
+ */
+#define ICSS_LRE_FW_VLAN_FLTR_TBL_BASE_ADDR 0x200
+
+#endif /* ICSS_MULTICAST_FILTER_MM_H */
diff --git a/drivers/net/ethernet/ti/netcp.h b/drivers/net/ethernet/ti/netcp.h
index b9cbd3b4a8a2..9cfddaa807e2 100644
--- a/drivers/net/ethernet/ti/netcp.h
+++ b/drivers/net/ethernet/ti/netcp.h
@@ -65,14 +65,14 @@ struct netcp_addr {
struct netcp_stats {
struct u64_stats_sync syncp_rx ____cacheline_aligned_in_smp;
- u64 rx_packets;
- u64 rx_bytes;
+ u64_stats_t rx_packets;
+ u64_stats_t rx_bytes;
u32 rx_errors;
u32 rx_dropped;
struct u64_stats_sync syncp_tx ____cacheline_aligned_in_smp;
- u64 tx_packets;
- u64 tx_bytes;
+ u64_stats_t tx_packets;
+ u64_stats_t tx_bytes;
u32 tx_errors;
u32 tx_dropped;
};
diff --git a/drivers/net/ethernet/ti/netcp_core.c b/drivers/net/ethernet/ti/netcp_core.c
index 5ed1c46bbcb1..eb8fc2ed05f4 100644
--- a/drivers/net/ethernet/ti/netcp_core.c
+++ b/drivers/net/ethernet/ti/netcp_core.c
@@ -759,8 +759,8 @@ static int netcp_process_one_rx_packet(struct netcp_intf *netcp)
knav_pool_desc_put(netcp->rx_pool, desc);
u64_stats_update_begin(&rx_stats->syncp_rx);
- rx_stats->rx_packets++;
- rx_stats->rx_bytes += skb->len;
+ u64_stats_inc(&rx_stats->rx_packets);
+ u64_stats_add(&rx_stats->rx_bytes, skb->len);
u64_stats_update_end(&rx_stats->syncp_rx);
/* push skb up the stack */
@@ -1045,8 +1045,8 @@ static int netcp_process_tx_compl_packets(struct netcp_intf *netcp,
}
u64_stats_update_begin(&tx_stats->syncp_tx);
- tx_stats->tx_packets++;
- tx_stats->tx_bytes += skb->len;
+ u64_stats_inc(&tx_stats->tx_packets);
+ u64_stats_add(&tx_stats->tx_bytes, skb->len);
u64_stats_update_end(&tx_stats->syncp_tx);
dev_kfree_skb(skb);
pkts++;
@@ -1973,14 +1973,14 @@ netcp_get_stats(struct net_device *ndev, struct rtnl_link_stats64 *stats)
do {
start = u64_stats_fetch_begin(&p->syncp_rx);
- rxpackets = p->rx_packets;
- rxbytes = p->rx_bytes;
+ rxpackets = u64_stats_read(&p->rx_packets);
+ rxbytes = u64_stats_read(&p->rx_bytes);
} while (u64_stats_fetch_retry(&p->syncp_rx, start));
do {
start = u64_stats_fetch_begin(&p->syncp_tx);
- txpackets = p->tx_packets;
- txbytes = p->tx_bytes;
+ txpackets = u64_stats_read(&p->tx_packets);
+ txbytes = u64_stats_read(&p->tx_bytes);
} while (u64_stats_fetch_retry(&p->syncp_tx, start));
stats->rx_packets = rxpackets;