netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC PATCH net-next 0/4] Introduce switch mode and TAPRIO offload support for ICSSG driver
@ 2023-08-30 11:08 MD Danish Anwar
  2023-08-30 11:08 ` [RFC PATCH net-next 1/4] net: ti: icssg-prueth: Add helper functions to configure FDB MD Danish Anwar
                   ` (3 more replies)
  0 siblings, 4 replies; 22+ messages in thread
From: MD Danish Anwar @ 2023-08-30 11:08 UTC (permalink / raw)
  To: Simon Horman, Roger Quadros, Vignesh Raghavendra, Jacob Keller,
	Andrew Lunn, MD Danish Anwar, Richard Cochran, Paolo Abeni,
	Jakub Kicinski, Eric Dumazet, David S. Miller
  Cc: linux-kernel, netdev, srk, r-gunasekaran

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

Thanks and Regards,
Md Danish Anwar

MD Danish Anwar (3):
  net: ti: icssg-prueth: Add helper functions to configure FDB
  net: ti: icssg-switch: Add switchdev based driver for ethernet switch
    support
  net: ti: icssg-prueth: Add support for ICSSG switch firmware on AM654
    PG2.0 EVM

Roger Quadros (1):
  net: ti: icssg_prueth: add TAPRIO offload support

 drivers/net/ethernet/ti/Kconfig               |   1 +
 drivers/net/ethernet/ti/Makefile              |   4 +-
 drivers/net/ethernet/ti/icssg/icssg_config.c  | 324 +++++++++++-
 drivers/net/ethernet/ti/icssg/icssg_config.h  |  25 +
 drivers/net/ethernet/ti/icssg/icssg_prueth.c  | 367 +++++++++++++-
 drivers/net/ethernet/ti/icssg/icssg_prueth.h  |  55 ++
 drivers/net/ethernet/ti/icssg/icssg_qos.c     | 294 +++++++++++
 drivers/net/ethernet/ti/icssg/icssg_qos.h     | 119 +++++
 .../net/ethernet/ti/icssg/icssg_switchdev.c   | 478 ++++++++++++++++++
 .../net/ethernet/ti/icssg/icssg_switchdev.h   |  13 +
 10 files changed, 1666 insertions(+), 14 deletions(-)
 create mode 100644 drivers/net/ethernet/ti/icssg/icssg_qos.c
 create mode 100644 drivers/net/ethernet/ti/icssg/icssg_qos.h
 create mode 100644 drivers/net/ethernet/ti/icssg/icssg_switchdev.c
 create mode 100644 drivers/net/ethernet/ti/icssg/icssg_switchdev.h

-- 
2.34.1


^ permalink raw reply	[flat|nested] 22+ messages in thread

* [RFC PATCH net-next 1/4] net: ti: icssg-prueth: Add helper functions to configure FDB
  2023-08-30 11:08 [RFC PATCH net-next 0/4] Introduce switch mode and TAPRIO offload support for ICSSG driver MD Danish Anwar
@ 2023-08-30 11:08 ` MD Danish Anwar
  2023-09-04 14:02   ` Andrew Lunn
  2023-08-30 11:08 ` [RFC PATCH net-next 2/4] net: ti: icssg-switch: Add switchdev based driver for ethernet switch support MD Danish Anwar
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 22+ messages in thread
From: MD Danish Anwar @ 2023-08-30 11:08 UTC (permalink / raw)
  To: Simon Horman, Roger Quadros, Vignesh Raghavendra, Jacob Keller,
	Andrew Lunn, MD Danish Anwar, Richard Cochran, Paolo Abeni,
	Jakub Kicinski, Eric Dumazet, David S. Miller
  Cc: linux-kernel, netdev, srk, r-gunasekaran

Introduce helper functions to configure firmware FDB tables, VLAN tables
and Port VLAN ID settings to aid adding Switch mode support.

Signed-off-by: MD Danish Anwar <danishanwar@ti.com>
---
 drivers/net/ethernet/ti/icssg/icssg_config.c | 195 +++++++++++++++++++
 drivers/net/ethernet/ti/icssg/icssg_config.h |  19 ++
 drivers/net/ethernet/ti/icssg/icssg_prueth.h |  12 ++
 3 files changed, 226 insertions(+)

diff --git a/drivers/net/ethernet/ti/icssg/icssg_config.c b/drivers/net/ethernet/ti/icssg/icssg_config.c
index 933b84666574..3fccdcc20bdb 100644
--- a/drivers/net/ethernet/ti/icssg/icssg_config.c
+++ b/drivers/net/ethernet/ti/icssg/icssg_config.c
@@ -455,3 +455,198 @@ void icssg_config_set_speed(struct prueth_emac *emac)
 
 	writeb(fw_speed, emac->dram.va + PORT_LINK_SPEED_OFFSET);
 }
+
+int icssg_send_fdb_msg(struct prueth_emac *emac, struct mgmt_cmd *cmd,
+		       struct mgmt_cmd_rsp *rsp)
+{
+	struct prueth *prueth = emac->prueth;
+	int slice = prueth_emac_slice(emac);
+	int i = 10000;
+	int addr;
+
+	addr = icssg_queue_pop(prueth, slice == 0 ?
+			       ICSSG_CMD_POP_SLICE0 : ICSSG_CMD_POP_SLICE1);
+	if (addr < 0)
+		return addr;
+
+	/* First 4 bytes have FW owned buffer linking info which should
+	 * not be touched
+	 */
+	memcpy_toio(prueth->shram.va + addr + 4, cmd, sizeof(*cmd));
+	icssg_queue_push(prueth, slice == 0 ?
+			 ICSSG_CMD_PUSH_SLICE0 : ICSSG_CMD_PUSH_SLICE1, addr);
+	while (i--) {
+		addr = icssg_queue_pop(prueth, slice == 0 ?
+				       ICSSG_RSP_POP_SLICE0 : ICSSG_RSP_POP_SLICE1);
+		if (addr < 0) {
+			usleep_range(1000, 2000);
+			continue;
+		}
+
+		memcpy_fromio(rsp, prueth->shram.va + addr, sizeof(*rsp));
+		/* Return buffer back for to pool */
+		icssg_queue_push(prueth, slice == 0 ?
+				 ICSSG_RSP_PUSH_SLICE0 : ICSSG_RSP_PUSH_SLICE1, addr);
+		break;
+	}
+	if (i <= 0) {
+		netdev_err(emac->ndev, "Timedout sending HWQ message\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+int icssg_fdb_add_del(struct prueth_emac *emac, const unsigned char *addr,
+		      u8 vid, u8 fid_c2, bool add)
+{
+	struct mgmt_cmd_rsp fdb_cmd_rsp = { 0 };
+	int slice = prueth_emac_slice(emac);
+	struct mgmt_cmd fdb_cmd = { 0 };
+	u8 mac_fid[ETH_ALEN + 2];
+	u16 fdb_slot;
+	u8 fid = vid;
+	int ret, i;
+
+	for (i = 0; i < ETH_ALEN; i++)
+		mac_fid[i] = addr[i];
+
+	/* 1-1 VID-FID mapping is already setup */
+	mac_fid[ETH_ALEN] = fid;
+	mac_fid[ETH_ALEN + 1] = 0;
+
+	fdb_slot = bitrev32(crc32_le(0, mac_fid, 8)) & PRUETH_SWITCH_FDB_MASK;
+
+	fdb_cmd.header = ICSSG_FW_MGMT_CMD_HEADER;
+	fdb_cmd.type   = ICSSG_FW_MGMT_FDB_CMD_TYPE;
+	fdb_cmd.seqnum = ++(emac->prueth->icssg_hwcmdseq);
+	if (add)
+		fdb_cmd.param  = ICSS_CMD_ADD_FDB;
+	else
+		fdb_cmd.param = ICSS_CMD_DEL_FDB;
+
+	fdb_cmd.param |= (slice << 4);
+
+	fid_c2 |= ICSSG_FDB_ENTRY_VALID;
+	memcpy(&fdb_cmd.cmd_args[0], addr, 4);
+	memcpy(&fdb_cmd.cmd_args[1], &addr[4], 2);
+	fdb_cmd.cmd_args[1] |= ((fid << 16) | (fid_c2 << 24));
+	fdb_cmd.cmd_args[2] = fdb_slot;
+
+	netdev_dbg(emac->ndev, "MAC %pM slot %X vlan %X FID %X\n",
+		   addr, fdb_slot, vid, fid);
+
+	ret = icssg_send_fdb_msg(emac, &fdb_cmd, &fdb_cmd_rsp);
+	if (ret)
+		return ret;
+
+	WARN_ON(fdb_cmd.seqnum != fdb_cmd_rsp.seqnum);
+	if (fdb_cmd_rsp.status == 1)
+		return 0;
+
+	return -EINVAL;
+}
+
+int icssg_fdb_lookup(struct prueth_emac *emac, const unsigned char *addr,
+		     u8 vid)
+{
+	struct mgmt_cmd_rsp fdb_cmd_rsp = { 0 };
+	int slice = prueth_emac_slice(emac);
+	struct mgmt_cmd fdb_cmd = { 0 };
+	struct prueth_fdb_slot *slot;
+	u8 mac_fid[ETH_ALEN + 2];
+	u16 fdb_slot;
+	u8 fid = vid;
+	int ret, i;
+
+	for (i = 0; i < ETH_ALEN; i++)
+		mac_fid[i] = addr[i];
+
+	/* 1-1 VID-FID mapping is already setup */
+	mac_fid[ETH_ALEN] = fid;
+	mac_fid[ETH_ALEN + 1] = 0;
+
+	fdb_slot = bitrev32(crc32_le(0, mac_fid, 8)) & PRUETH_SWITCH_FDB_MASK;
+
+	fdb_cmd.header = ICSSG_FW_MGMT_CMD_HEADER;
+	fdb_cmd.type   = ICSSG_FW_MGMT_FDB_CMD_TYPE;
+	fdb_cmd.seqnum = ++(emac->prueth->icssg_hwcmdseq);
+	fdb_cmd.param  = ICSS_CMD_GET_FDB_SLOT;
+
+	fdb_cmd.param |= (slice << 4);
+
+	memcpy(&fdb_cmd.cmd_args[0], addr, 4);
+	memcpy(&fdb_cmd.cmd_args[1], &addr[4], 2);
+	fdb_cmd.cmd_args[1] |= fid << 16;
+	fdb_cmd.cmd_args[2] = fdb_slot;
+
+	ret = icssg_send_fdb_msg(emac, &fdb_cmd, &fdb_cmd_rsp);
+	if (ret)
+		return ret;
+
+	WARN_ON(fdb_cmd.seqnum != fdb_cmd_rsp.seqnum);
+
+	slot = (struct prueth_fdb_slot __force *)emac->dram.va + FDB_CMD_BUFFER;
+	for (i = 0; i < 4; i++) {
+		if (ether_addr_equal(addr, slot->mac) && vid == slot->fid)
+			return (slot->fid_c2 & ~ICSSG_FDB_ENTRY_VALID);
+		slot++;
+	}
+
+	return 0;
+}
+
+void icssg_vtbl_modify(struct prueth_emac *emac, u8 vid, u8 port_mask,
+		       u8 untag_mask, bool add)
+{
+	struct prueth *prueth = emac->prueth;
+	struct prueth_vlan_tbl *tbl;
+	u8 fid_c1;
+
+	tbl = prueth->vlan_tbl;
+	fid_c1 = tbl[vid].fid_c1;
+
+	/* FID_C1: bit0..2 port membership mask,
+	 * bit3..5 tagging mask for each port
+	 * bit6 Stream VID (not handled currently)
+	 * bit7 MC flood (not handled currently)
+	 */
+	if (add) {
+		fid_c1 |= (port_mask | port_mask << 3);
+		fid_c1 &= ~(untag_mask << 3);
+	} else {
+		fid_c1 &= ~(port_mask | port_mask << 3);
+	}
+
+	tbl[vid].fid_c1 = fid_c1;
+}
+
+u16 icssg_get_pvid(struct prueth_emac *emac)
+{
+	struct prueth *prueth = emac->prueth;
+	u32 pvid;
+
+	if (emac->port_id == PRUETH_PORT_MII0)
+		pvid = readl(prueth->shram.va + EMAC_ICSSG_SWITCH_PORT1_DEFAULT_VLAN_OFFSET);
+	else
+		pvid = readl(prueth->shram.va + EMAC_ICSSG_SWITCH_PORT2_DEFAULT_VLAN_OFFSET);
+
+	pvid = pvid >> 24;
+
+	return pvid;
+}
+
+void icssg_set_pvid(struct prueth *prueth, u8 vid, u8 port)
+{
+	u32 pvid;
+
+	/* only 256 VLANs are supported */
+	pvid = (u32 __force)cpu_to_be32((ETH_P_8021Q << 16) | (vid & 0xff));
+
+	if (port == PRUETH_PORT_MII0)
+		writel(pvid, prueth->shram.va + EMAC_ICSSG_SWITCH_PORT1_DEFAULT_VLAN_OFFSET);
+	else if (port == PRUETH_PORT_MII1)
+		writel(pvid, prueth->shram.va + EMAC_ICSSG_SWITCH_PORT2_DEFAULT_VLAN_OFFSET);
+	else
+		writel(pvid, prueth->shram.va + EMAC_ICSSG_SWITCH_PORT0_DEFAULT_VLAN_OFFSET);
+}
diff --git a/drivers/net/ethernet/ti/icssg/icssg_config.h b/drivers/net/ethernet/ti/icssg/icssg_config.h
index 43eb0922172a..0d5d5d253b7a 100644
--- a/drivers/net/ethernet/ti/icssg/icssg_config.h
+++ b/drivers/net/ethernet/ti/icssg/icssg_config.h
@@ -35,6 +35,8 @@ struct icssg_flow_cfg {
 	(2 * (PRUETH_EMAC_BUF_POOL_SIZE * PRUETH_NUM_BUF_POOLS + \
 	 PRUETH_EMAC_RX_CTX_BUF_SIZE * 2))
 
+#define PRUETH_SWITCH_FDB_MASK ((SIZE_OF_FDB / NUMBER_OF_FDB_BUCKET_ENTRIES) - 1)
+
 struct icssg_rxq_ctx {
 	__le32 start[3];
 	__le32 end;
@@ -146,6 +148,23 @@ struct icssg_setclock_desc {
 #define ICSSG_TS_PUSH_SLICE0	40
 #define ICSSG_TS_PUSH_SLICE1	41
 
+struct mgmt_cmd {
+	u8 param;
+	u8 seqnum;
+	u8 type;
+	u8 header;
+	u32 cmd_args[3];
+} __packed;
+
+struct mgmt_cmd_rsp {
+	u32 reserved;
+	u8 status;
+	u8 seqnum;
+	u8 type;
+	u8 header;
+	u32 cmd_args[3];
+} __packed;
+
 /* FDB FID_C2 flag definitions */
 /* Indicates host port membership.*/
 #define ICSSG_FDB_ENTRY_P0_MEMBERSHIP         BIT(0)
diff --git a/drivers/net/ethernet/ti/icssg/icssg_prueth.h b/drivers/net/ethernet/ti/icssg/icssg_prueth.h
index 3fe80a8758d3..1011917924c8 100644
--- a/drivers/net/ethernet/ti/icssg/icssg_prueth.h
+++ b/drivers/net/ethernet/ti/icssg/icssg_prueth.h
@@ -209,6 +209,7 @@ struct prueth_pdata {
  * @emacs_initialized: num of EMACs/ext ports that are up/running
  * @iep0: pointer to IEP0 device
  * @iep1: pointer to IEP1 device
+ * @vlan_tbl: VLAN-FID table pointer
  */
 struct prueth {
 	struct device *dev;
@@ -233,6 +234,7 @@ struct prueth {
 	int emacs_initialized;
 	struct icss_iep *iep0;
 	struct icss_iep *iep1;
+	struct prueth_vlan_tbl *vlan_tbl;
 };
 
 struct emac_tx_ts_response {
@@ -277,6 +279,16 @@ int icssg_queue_pop(struct prueth *prueth, u8 queue);
 void icssg_queue_push(struct prueth *prueth, int queue, u16 addr);
 u32 icssg_queue_level(struct prueth *prueth, int queue);
 
+int icssg_send_fdb_msg(struct prueth_emac *emac, struct mgmt_cmd *cmd,
+		       struct mgmt_cmd_rsp *rsp);
+int icssg_fdb_add_del(struct prueth_emac *emac,  const unsigned char *addr,
+		      u8 vid, u8 fid_c2, bool add);
+int icssg_fdb_lookup(struct prueth_emac *emac, const unsigned char *addr,
+		     u8 vid);
+void icssg_vtbl_modify(struct prueth_emac *emac, u8 vid, u8 port_mask,
+		       u8 untag_mask, bool add);
+u16 icssg_get_pvid(struct prueth_emac *emac);
+void icssg_set_pvid(struct prueth *prueth, u8 vid, u8 port);
 #define prueth_napi_to_tx_chn(pnapi) \
 	container_of(pnapi, struct prueth_tx_chn, napi_tx)
 
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [RFC PATCH net-next 2/4] net: ti: icssg-switch: Add switchdev based driver for ethernet switch support
  2023-08-30 11:08 [RFC PATCH net-next 0/4] Introduce switch mode and TAPRIO offload support for ICSSG driver MD Danish Anwar
  2023-08-30 11:08 ` [RFC PATCH net-next 1/4] net: ti: icssg-prueth: Add helper functions to configure FDB MD Danish Anwar
@ 2023-08-30 11:08 ` MD Danish Anwar
  2023-08-30 11:08 ` [RFC PATCH net-next 3/4] net: ti: icssg-prueth: Add support for ICSSG switch firmware on AM654 PG2.0 EVM MD Danish Anwar
  2023-08-30 11:08 ` [RFC PATCH net-next 4/4] net: ti: icssg_prueth: add TAPRIO offload support MD Danish Anwar
  3 siblings, 0 replies; 22+ messages in thread
From: MD Danish Anwar @ 2023-08-30 11:08 UTC (permalink / raw)
  To: Simon Horman, Roger Quadros, Vignesh Raghavendra, Jacob Keller,
	Andrew Lunn, MD Danish Anwar, Richard Cochran, Paolo Abeni,
	Jakub Kicinski, Eric Dumazet, David S. Miller
  Cc: linux-kernel, netdev, srk, r-gunasekaran

ICSSG can operating in switch mode with 2 ext port and 1 host port with
VLAN/FDB/MDB and STP offloading. Add switchdev based driver to
support the same.

Driver itself will be integrated with icssg_prueth in future commits

Signed-off-by: MD Danish Anwar <danishanwar@ti.com>
---
 drivers/net/ethernet/ti/icssg/icssg_prueth.c  |   1 +
 drivers/net/ethernet/ti/icssg/icssg_prueth.h  |  36 ++
 .../net/ethernet/ti/icssg/icssg_switchdev.c   | 478 ++++++++++++++++++
 .../net/ethernet/ti/icssg/icssg_switchdev.h   |  13 +
 4 files changed, 528 insertions(+)
 create mode 100644 drivers/net/ethernet/ti/icssg/icssg_switchdev.c
 create mode 100644 drivers/net/ethernet/ti/icssg/icssg_switchdev.h

diff --git a/drivers/net/ethernet/ti/icssg/icssg_prueth.c b/drivers/net/ethernet/ti/icssg/icssg_prueth.c
index 410612f43cbd..c35fb51f08af 100644
--- a/drivers/net/ethernet/ti/icssg/icssg_prueth.c
+++ b/drivers/net/ethernet/ti/icssg/icssg_prueth.c
@@ -30,6 +30,7 @@
 
 #include "icssg_prueth.h"
 #include "icssg_mii_rt.h"
+#include "icssg_switchdev.h"
 #include "../k3-cppi-desc-pool.h"
 
 #define PRUETH_MODULE_DESCRIPTION "PRUSS ICSSG Ethernet driver"
diff --git a/drivers/net/ethernet/ti/icssg/icssg_prueth.h b/drivers/net/ethernet/ti/icssg/icssg_prueth.h
index 1011917924c8..6e18da06c786 100644
--- a/drivers/net/ethernet/ti/icssg/icssg_prueth.h
+++ b/drivers/net/ethernet/ti/icssg/icssg_prueth.h
@@ -118,6 +118,15 @@ struct prueth_rx_chn {
 	char name[32];
 };
 
+enum prueth_devlink_param_id {
+	PRUETH_DEVLINK_PARAM_ID_BASE = DEVLINK_PARAM_GENERIC_ID_MAX,
+	PRUETH_DL_PARAM_SWITCH_MODE,
+};
+
+struct prueth_devlink {
+	struct prueth *prueth;
+};
+
 /* There are 4 Tx DMA channels, but the highest priority is CH3 (thread 3)
  * and lower three are lower priority channels or threads.
  */
@@ -173,6 +182,10 @@ struct prueth_emac {
 
 	struct pruss_mem_region dram;
 
+	bool offload_fwd_mark;
+	struct devlink_port devlink_port;
+	int port_vlan;
+
 	struct delayed_work stats_work;
 	u64 stats[ICSSG_NUM_STATS];
 };
@@ -181,10 +194,12 @@ struct prueth_emac {
  * struct prueth_pdata - PRUeth platform data
  * @fdqring_mode: Free desc queue mode
  * @quirk_10m_link_issue: 10M link detect errata
+ * @switch_mode: switch firmware support
  */
 struct prueth_pdata {
 	enum k3_ring_mode fdqring_mode;
 	u32	quirk_10m_link_issue:1;
+	u32	switch_mode:1;
 };
 
 /**
@@ -210,6 +225,16 @@ struct prueth_pdata {
  * @iep0: pointer to IEP0 device
  * @iep1: pointer to IEP1 device
  * @vlan_tbl: VLAN-FID table pointer
+ * @hw_bridge_dev: pointer to HW bridge net device
+ * @br_members: bitmask of bridge member ports
+ * @prueth_netdevice_nb: netdevice notifier block
+ * @prueth_switchdevice_nb: switchdev notifier block
+ * @prueth_switchdev_bl_nb: switchdev blocking notifier block
+ * @is_switch_mode: flag to indicate if device is in Switch mode
+ * @is_switchmode_supported: indicates platform support for switch mode
+ * @switch_id: ID for mapping switch ports to bridge
+ * @default_vlan: Default VLAN for host
+ * @devlink: pointer to devlink
  */
 struct prueth {
 	struct device *dev;
@@ -235,6 +260,17 @@ struct prueth {
 	struct icss_iep *iep0;
 	struct icss_iep *iep1;
 	struct prueth_vlan_tbl *vlan_tbl;
+
+	struct net_device *hw_bridge_dev;
+	u8 br_members;
+	struct notifier_block prueth_netdevice_nb;
+	struct notifier_block prueth_switchdev_nb;
+	struct notifier_block prueth_switchdev_bl_nb;
+	bool is_switch_mode;
+	bool is_switchmode_supported;
+	unsigned char switch_id[MAX_PHYS_ITEM_ID_LEN];
+	int default_vlan;
+	struct devlink *devlink;
 };
 
 struct emac_tx_ts_response {
diff --git a/drivers/net/ethernet/ti/icssg/icssg_switchdev.c b/drivers/net/ethernet/ti/icssg/icssg_switchdev.c
new file mode 100644
index 000000000000..48d8ed4fa7a8
--- /dev/null
+++ b/drivers/net/ethernet/ti/icssg/icssg_switchdev.c
@@ -0,0 +1,478 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/* Texas Instruments K3 ICSSG Ethernet Switchdev Driver
+ *
+ * Copyright (C) 2021 Texas Instruments Incorporated - https://www.ti.com/
+ *
+ */
+
+#include <linux/etherdevice.h>
+#include <linux/if_bridge.h>
+#include <linux/netdevice.h>
+#include <linux/workqueue.h>
+#include <net/switchdev.h>
+
+#include "icssg_prueth.h"
+#include "icssg_switchdev.h"
+#include "icss_mii_rt.h"
+
+struct prueth_switchdev_event_work {
+	struct work_struct work;
+	struct switchdev_notifier_fdb_info fdb_info;
+	struct prueth_emac *emac;
+	unsigned long event;
+};
+
+static int prueth_switchdev_stp_state_set(struct prueth_emac *emac,
+					  u8 state)
+{
+	enum icssg_port_state_cmd emac_state;
+	int ret = 0;
+
+	switch (state) {
+	case BR_STATE_FORWARDING:
+		emac_state = ICSSG_EMAC_PORT_FORWARD;
+		break;
+	case BR_STATE_DISABLED:
+		emac_state = ICSSG_EMAC_PORT_DISABLE;
+		break;
+	case BR_STATE_LEARNING:
+	case BR_STATE_LISTENING:
+	case BR_STATE_BLOCKING:
+		emac_state = ICSSG_EMAC_PORT_BLOCK;
+		break;
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	emac_set_port_state(emac, emac_state);
+	netdev_dbg(emac->ndev, "STP state: %u\n", emac_state);
+
+	return ret;
+}
+
+static int prueth_switchdev_attr_br_flags_set(struct prueth_emac *emac,
+					      struct net_device *orig_dev,
+					      struct switchdev_brport_flags brport_flags)
+{
+	enum icssg_port_state_cmd emac_state;
+
+	if (brport_flags.mask & BR_MCAST_FLOOD)
+		emac_state = ICSSG_EMAC_PORT_MC_FLOODING_ENABLE;
+	else
+		emac_state = ICSSG_EMAC_PORT_MC_FLOODING_DISABLE;
+
+	netdev_dbg(emac->ndev, "BR_MCAST_FLOOD: %d port %u\n",
+		   emac_state, emac->port_id);
+
+	emac_set_port_state(emac, emac_state);
+
+	return 0;
+}
+
+static int prueth_switchdev_attr_br_flags_pre_set(struct net_device *netdev,
+						  struct switchdev_brport_flags brport_flags)
+{
+	if (brport_flags.mask & ~(BR_LEARNING | BR_MCAST_FLOOD))
+		return -EINVAL;
+
+	return 0;
+}
+
+static int prueth_switchdev_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);
+	int ret;
+
+	netdev_dbg(ndev, "attr: id %u port: %u\n", attr->id, emac->port_id);
+
+	switch (attr->id) {
+	case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS:
+		ret = prueth_switchdev_attr_br_flags_pre_set(ndev,
+							     attr->u.brport_flags);
+		break;
+	case SWITCHDEV_ATTR_ID_PORT_STP_STATE:
+		ret = prueth_switchdev_stp_state_set(emac,
+						     attr->u.stp_state);
+		netdev_dbg(ndev, "stp state: %u\n", attr->u.stp_state);
+		break;
+	case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS:
+		ret = prueth_switchdev_attr_br_flags_set(emac, attr->orig_dev,
+							 attr->u.brport_flags);
+		break;
+	default:
+		ret = -EOPNOTSUPP;
+		break;
+	}
+
+	return ret;
+}
+
+static void prueth_switchdev_fdb_offload_notify(struct net_device *ndev,
+						struct switchdev_notifier_fdb_info *rcv)
+{
+	struct switchdev_notifier_fdb_info info;
+
+	memset(&info, 0, sizeof(info));
+	info.addr = rcv->addr;
+	info.vid = rcv->vid;
+	info.offloaded = true;
+	call_switchdev_notifiers(SWITCHDEV_FDB_OFFLOADED,
+				 ndev, &info.info, NULL);
+}
+
+static void prueth_switchdev_event_work(struct work_struct *work)
+{
+	struct prueth_switchdev_event_work *switchdev_work =
+		container_of(work, struct prueth_switchdev_event_work, work);
+	struct prueth_emac *emac = switchdev_work->emac;
+	struct switchdev_notifier_fdb_info *fdb;
+	int port_id = emac->port_id;
+	int ret;
+
+	rtnl_lock();
+	switch (switchdev_work->event) {
+	case SWITCHDEV_FDB_ADD_TO_DEVICE:
+		fdb = &switchdev_work->fdb_info;
+
+		netdev_dbg(emac->ndev, "prueth_fdb_add: MACID = %pM vid = %u flags = %u %u -- port %d\n",
+			   fdb->addr, fdb->vid, fdb->added_by_user,
+			   fdb->offloaded, port_id);
+
+		if (!fdb->added_by_user)
+			break;
+		if (memcmp(emac->mac_addr, (u8 *)fdb->addr, ETH_ALEN) == 0)
+			break;
+
+		ret = icssg_fdb_add_del(emac, fdb->addr, fdb->vid,
+					BIT(port_id), true);
+		if (!ret)
+			prueth_switchdev_fdb_offload_notify(emac->ndev, fdb);
+		break;
+	case SWITCHDEV_FDB_DEL_TO_DEVICE:
+		fdb = &switchdev_work->fdb_info;
+
+		netdev_dbg(emac->ndev, "prueth_fdb_del: MACID = %pM vid = %u flags = %u %u -- port %d\n",
+			   fdb->addr, fdb->vid, fdb->added_by_user,
+			   fdb->offloaded, port_id);
+
+		if (!fdb->added_by_user)
+			break;
+		if (memcmp(emac->mac_addr, (u8 *)fdb->addr, ETH_ALEN) == 0)
+			break;
+		icssg_fdb_add_del(emac, fdb->addr, fdb->vid,
+				  BIT(port_id), false);
+		break;
+	default:
+		break;
+	}
+	rtnl_unlock();
+
+	kfree(switchdev_work->fdb_info.addr);
+	kfree(switchdev_work);
+	dev_put(emac->ndev);
+}
+
+static int prueth_switchdev_event(struct notifier_block *unused,
+				  unsigned long event, void *ptr)
+{
+	struct net_device *ndev = switchdev_notifier_info_to_dev(ptr);
+	struct prueth_switchdev_event_work *switchdev_work;
+	struct switchdev_notifier_fdb_info *fdb_info = ptr;
+	struct prueth_emac *emac = netdev_priv(ndev);
+	int err;
+
+	if (!prueth_dev_check(ndev))
+		return NOTIFY_DONE;
+
+	if (event == SWITCHDEV_PORT_ATTR_SET) {
+		err = switchdev_handle_port_attr_set(ndev, ptr,
+						     prueth_dev_check,
+						     prueth_switchdev_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, prueth_switchdev_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);
+		dev_hold(ndev);
+		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 prueth_switchdev_vlan_add(struct prueth_emac *emac, bool untag, bool pvid,
+				     u8 vid, struct net_device *orig_dev)
+{
+	bool cpu_port = netif_is_bridge_master(orig_dev);
+	int untag_mask = 0;
+	int port_mask;
+	int ret = 0;
+
+	if (cpu_port)
+		port_mask = BIT(PRUETH_PORT_HOST);
+	else
+		port_mask = BIT(emac->port_id);
+
+	if (untag)
+		untag_mask = port_mask;
+
+	icssg_vtbl_modify(emac, vid, port_mask, untag_mask, true);
+
+	netdev_dbg(emac->ndev, "VID add vid:%u port_mask:%X untag_mask %X PVID %d\n",
+		   vid, port_mask, untag_mask, pvid);
+
+	if (!pvid)
+		return ret;
+
+	icssg_set_pvid(emac->prueth, vid, emac->port_id);
+
+	return ret;
+}
+
+static int prueth_switchdev_vlan_del(struct prueth_emac *emac, u16 vid,
+				     struct net_device *orig_dev)
+{
+	bool cpu_port = netif_is_bridge_master(orig_dev);
+	int port_mask;
+	int ret = 0;
+
+	if (cpu_port)
+		port_mask = BIT(PRUETH_PORT_HOST);
+	else
+		port_mask = BIT(emac->port_id);
+
+	icssg_vtbl_modify(emac, vid, port_mask, 0, false);
+
+	if (cpu_port)
+		icssg_fdb_add_del(emac, emac->mac_addr, vid,
+				  BIT(PRUETH_PORT_HOST), false);
+
+	if (vid == icssg_get_pvid(emac))
+		icssg_set_pvid(emac->prueth, 0, emac->port_id);
+
+	netdev_dbg(emac->ndev, "VID del vid:%u port_mask:%X\n",
+		   vid, port_mask);
+
+	return ret;
+}
+
+static int prueth_switchdev_vlans_add(struct prueth_emac *emac,
+				      const struct switchdev_obj_port_vlan *vlan)
+{
+	bool untag = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
+	struct net_device *orig_dev = vlan->obj.orig_dev;
+	bool cpu_port = netif_is_bridge_master(orig_dev);
+	bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
+
+	netdev_dbg(emac->ndev, "VID add vid:%u flags:%X\n",
+		   vlan->vid, vlan->flags);
+
+	if (cpu_port && !(vlan->flags & BRIDGE_VLAN_INFO_BRENTRY))
+		return 0;
+
+	if (vlan->vid > 0xff)
+		return 0;
+
+	return prueth_switchdev_vlan_add(emac, untag, pvid, vlan->vid,
+					 orig_dev);
+}
+
+static int prueth_switchdev_vlans_del(struct prueth_emac *emac,
+				      const struct switchdev_obj_port_vlan *vlan)
+{
+	if (vlan->vid > 0xff)
+		return 0;
+
+	return prueth_switchdev_vlan_del(emac, vlan->vid,
+					 vlan->obj.orig_dev);
+}
+
+static int prueth_switchdev_mdb_add(struct prueth_emac *emac,
+				    struct switchdev_obj_port_mdb *mdb)
+{
+	struct net_device *orig_dev = mdb->obj.orig_dev;
+	u8 port_mask, fid_c2;
+	bool cpu_port;
+	int err;
+
+	cpu_port = netif_is_bridge_master(orig_dev);
+
+	if (cpu_port)
+		port_mask = BIT(PRUETH_PORT_HOST);
+	else
+		port_mask = BIT(emac->port_id);
+
+	fid_c2 = icssg_fdb_lookup(emac, mdb->addr, mdb->vid);
+
+	err = icssg_fdb_add_del(emac, mdb->addr, mdb->vid, fid_c2 | port_mask, true);
+	netdev_dbg(emac->ndev, "MDB add vid %u:%pM  ports: %X\n",
+		   mdb->vid, mdb->addr, port_mask);
+
+	return err;
+}
+
+static int prueth_switchdev_mdb_del(struct prueth_emac *emac,
+				    struct switchdev_obj_port_mdb *mdb)
+{
+	struct net_device *orig_dev = mdb->obj.orig_dev;
+	int del_mask, ret, fid_c2;
+	bool cpu_port;
+
+	cpu_port = netif_is_bridge_master(orig_dev);
+
+	if (cpu_port)
+		del_mask = BIT(PRUETH_PORT_HOST);
+	else
+		del_mask = BIT(emac->port_id);
+
+	fid_c2 = icssg_fdb_lookup(emac, mdb->addr, mdb->vid);
+
+	if (fid_c2 & ~del_mask)
+		ret = icssg_fdb_add_del(emac, mdb->addr, mdb->vid, fid_c2 & ~del_mask, true);
+	else
+		ret = icssg_fdb_add_del(emac, mdb->addr, mdb->vid, 0, false);
+
+	netdev_dbg(emac->ndev, "MDB del vid %u:%pM  ports: %X\n",
+		   mdb->vid, mdb->addr, del_mask);
+
+	return ret;
+}
+
+static int 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_vlan *vlan = SWITCHDEV_OBJ_PORT_VLAN(obj);
+	struct switchdev_obj_port_mdb *mdb = SWITCHDEV_OBJ_PORT_MDB(obj);
+	struct prueth_emac *emac = netdev_priv(ndev);
+	int err = 0;
+
+	netdev_dbg(ndev, "obj_add: id %u port: %u\n", obj->id, emac->port_id);
+
+	switch (obj->id) {
+	case SWITCHDEV_OBJ_ID_PORT_VLAN:
+		err = prueth_switchdev_vlans_add(emac, vlan);
+		break;
+	case SWITCHDEV_OBJ_ID_PORT_MDB:
+	case SWITCHDEV_OBJ_ID_HOST_MDB:
+		err = prueth_switchdev_mdb_add(emac, mdb);
+		break;
+	default:
+		err = -EOPNOTSUPP;
+		break;
+	}
+
+	return err;
+}
+
+static int prueth_switchdev_obj_del(struct net_device *ndev, const void *ctx,
+				    const struct switchdev_obj *obj)
+{
+	struct switchdev_obj_port_vlan *vlan = SWITCHDEV_OBJ_PORT_VLAN(obj);
+	struct switchdev_obj_port_mdb *mdb = SWITCHDEV_OBJ_PORT_MDB(obj);
+	struct prueth_emac *emac = netdev_priv(ndev);
+	int err = 0;
+
+	netdev_dbg(ndev, "obj_del: id %u port: %u\n", obj->id, emac->port_id);
+
+	switch (obj->id) {
+	case SWITCHDEV_OBJ_ID_PORT_VLAN:
+		err = prueth_switchdev_vlans_del(emac, vlan);
+		break;
+	case SWITCHDEV_OBJ_ID_PORT_MDB:
+	case SWITCHDEV_OBJ_ID_HOST_MDB:
+		err = prueth_switchdev_mdb_del(emac, mdb);
+		break;
+	default:
+		err = -EOPNOTSUPP;
+		break;
+	}
+
+	return err;
+}
+
+static int prueth_switchdev_blocking_event(struct notifier_block *unused,
+					   unsigned long event, void *ptr)
+{
+	struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
+	int err;
+
+	switch (event) {
+	case SWITCHDEV_PORT_OBJ_ADD:
+		err = switchdev_handle_port_obj_add(dev, ptr,
+						    prueth_dev_check,
+						    prueth_switchdev_obj_add);
+		return notifier_from_errno(err);
+	case SWITCHDEV_PORT_OBJ_DEL:
+		err = switchdev_handle_port_obj_del(dev, ptr,
+						    prueth_dev_check,
+						    prueth_switchdev_obj_del);
+		return notifier_from_errno(err);
+	case SWITCHDEV_PORT_ATTR_SET:
+		err = switchdev_handle_port_attr_set(dev, ptr,
+						     prueth_dev_check,
+						     prueth_switchdev_attr_set);
+		return notifier_from_errno(err);
+	default:
+		break;
+	}
+
+	return NOTIFY_DONE;
+}
+
+int prueth_switchdev_register_notifiers(struct prueth *prueth)
+{
+	int ret = 0;
+
+	prueth->prueth_switchdev_nb.notifier_call = &prueth_switchdev_event;
+	ret = register_switchdev_notifier(&prueth->prueth_switchdev_nb);
+	if (ret) {
+		dev_err(prueth->dev, "register switchdev notifier fail ret:%d\n",
+			ret);
+		return ret;
+	}
+
+	prueth->prueth_switchdev_bl_nb.notifier_call = &prueth_switchdev_blocking_event;
+	ret = register_switchdev_blocking_notifier(&prueth->prueth_switchdev_bl_nb);
+	if (ret) {
+		dev_err(prueth->dev, "register switchdev blocking notifier ret:%d\n",
+			ret);
+		unregister_switchdev_notifier(&prueth->prueth_switchdev_nb);
+	}
+
+	return ret;
+}
+
+void prueth_switchdev_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/icssg/icssg_switchdev.h b/drivers/net/ethernet/ti/icssg/icssg_switchdev.h
new file mode 100644
index 000000000000..0e64e7760a00
--- /dev/null
+++ b/drivers/net/ethernet/ti/icssg/icssg_switchdev.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2021 Texas Instruments Incorporated - https://www.ti.com/
+ */
+#ifndef __NET_TI_ICSSG_SWITCHDEV_H
+#define __NET_TI_ICSSG_SWITCHDEV_H
+
+#include "icssg_prueth.h"
+
+int prueth_switchdev_register_notifiers(struct prueth *prueth);
+void prueth_switchdev_unregister_notifiers(struct prueth *prueth);
+bool prueth_dev_check(const struct net_device *ndev);
+
+#endif /* __NET_TI_ICSSG_SWITCHDEV_H */
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [RFC PATCH net-next 3/4] net: ti: icssg-prueth: Add support for ICSSG switch firmware on AM654 PG2.0 EVM
  2023-08-30 11:08 [RFC PATCH net-next 0/4] Introduce switch mode and TAPRIO offload support for ICSSG driver MD Danish Anwar
  2023-08-30 11:08 ` [RFC PATCH net-next 1/4] net: ti: icssg-prueth: Add helper functions to configure FDB MD Danish Anwar
  2023-08-30 11:08 ` [RFC PATCH net-next 2/4] net: ti: icssg-switch: Add switchdev based driver for ethernet switch support MD Danish Anwar
@ 2023-08-30 11:08 ` MD Danish Anwar
  2023-09-04 14:08   ` Andrew Lunn
  2023-08-30 11:08 ` [RFC PATCH net-next 4/4] net: ti: icssg_prueth: add TAPRIO offload support MD Danish Anwar
  3 siblings, 1 reply; 22+ messages in thread
From: MD Danish Anwar @ 2023-08-30 11:08 UTC (permalink / raw)
  To: Simon Horman, Roger Quadros, Vignesh Raghavendra, Jacob Keller,
	Andrew Lunn, MD Danish Anwar, Richard Cochran, Paolo Abeni,
	Jakub Kicinski, Eric Dumazet, David S. Miller
  Cc: linux-kernel, netdev, srk, r-gunasekaran

Add support for ICSSG switch firmware using existing Dual EMAC driver
with switchdev and devlink framework.

Limitations:
VLAN offloading is limited to 0-256 IDs.
MDB/FDB static entries are limited to 511 entries and different FDBs can
hash to same bucket and thus may not completely offloaded

Switch mode requires loading of new firmware into ICSSG cores. This
means interfaces have to taken down and then reconfigured to switch mode
using devlink.

Example assuming ETH1 and ETH2 as ICSSG2 interfaces:

Switch to ICSSG Switch mode:
 ip link set dev eth1 down
 ip link set dev eth2 down
 devlink dev param set platform/icssg2-eth name \
 switch_mode value 1 cmode runtime
 ip link add name br0 type bridge
 ip link set dev eth1 master br0
 ip link set dev eth2 master br0
 ip link set dev br0 up
 ip link set dev eth1 up
 ip link set dev eth2 up
 bridge vlan add dev br0 vid 1 pvid untagged self

Going back to Dual EMAC mode:

 ip link set dev br0 down
 ip link set dev eth1 nomaster
 ip link set dev eth2 nomaster
 ip link set dev eth1 down
 ip link set dev eth2 down
 devlink dev param set platform/icssg2-eth name \
 switch_mode value 0 cmode runtime
 ip link del name br0 type bridge
 ip link set dev eth1 up
 ip link set dev eth2 up

By default, Dual EMAC firmware is loaded, and can be changed to switch
mode by above steps

Signed-off-by: MD Danish Anwar <danishanwar@ti.com>
---
 drivers/net/ethernet/ti/Kconfig               |   1 +
 drivers/net/ethernet/ti/Makefile              |   3 +-
 drivers/net/ethernet/ti/icssg/icssg_config.c  | 129 ++++++-
 drivers/net/ethernet/ti/icssg/icssg_config.h  |   6 +
 drivers/net/ethernet/ti/icssg/icssg_prueth.c  | 361 +++++++++++++++++-
 .../net/ethernet/ti/icssg/icssg_switchdev.c   |   2 +-
 6 files changed, 488 insertions(+), 14 deletions(-)

diff --git a/drivers/net/ethernet/ti/Kconfig b/drivers/net/ethernet/ti/Kconfig
index 88b5b1b47779..2e3ef9c092c8 100644
--- a/drivers/net/ethernet/ti/Kconfig
+++ b/drivers/net/ethernet/ti/Kconfig
@@ -188,6 +188,7 @@ config TI_ICSSG_PRUETH
 	select PHYLIB
 	select TI_ICSS_IEP
 	depends on PRU_REMOTEPROC
+	depends on NET_SWITCHDEV
 	depends on ARCH_K3 && OF && TI_K3_UDMA_GLUE_LAYER
 	help
 	  Support dual Gigabit Ethernet ports over the ICSSG PRU Subsystem.
diff --git a/drivers/net/ethernet/ti/Makefile b/drivers/net/ethernet/ti/Makefile
index 34fd7a716ba6..3adceff760ce 100644
--- a/drivers/net/ethernet/ti/Makefile
+++ b/drivers/net/ethernet/ti/Makefile
@@ -37,5 +37,6 @@ icssg-prueth-y := k3-cppi-desc-pool.o \
 		  icssg/icssg_config.o \
 		  icssg/icssg_mii_cfg.o \
 		  icssg/icssg_stats.o \
-		  icssg/icssg_ethtool.o
+		  icssg/icssg_ethtool.o \
+		  icssg/icssg_switchdev.o
 obj-$(CONFIG_TI_ICSS_IEP) += icssg/icss_iep.o
diff --git a/drivers/net/ethernet/ti/icssg/icssg_config.c b/drivers/net/ethernet/ti/icssg/icssg_config.c
index 3fccdcc20bdb..03968dbc2d62 100644
--- a/drivers/net/ethernet/ti/icssg/icssg_config.c
+++ b/drivers/net/ethernet/ti/icssg/icssg_config.c
@@ -105,28 +105,49 @@ static const struct map hwq_map[2][ICSSG_NUM_OTHER_QUEUES] = {
 	},
 };
 
+static void icssg_config_mii_init_switch(struct prueth_emac *emac)
+{
+	struct prueth *prueth = emac->prueth;
+	int mii = prueth_emac_slice(emac);
+	u32 txcfg_reg, pcnt_reg, txcfg;
+	struct regmap *mii_rt;
+
+	mii_rt = prueth->mii_rt;
+
+	txcfg_reg = (mii == ICSS_MII0) ? PRUSS_MII_RT_TXCFG0 :
+				       PRUSS_MII_RT_TXCFG1;
+	pcnt_reg = (mii == ICSS_MII0) ? PRUSS_MII_RT_RX_PCNT0 :
+				       PRUSS_MII_RT_RX_PCNT1;
+
+	txcfg = PRUSS_MII_RT_TXCFG_TX_ENABLE |
+		PRUSS_MII_RT_TXCFG_TX_AUTO_PREAMBLE |
+		PRUSS_MII_RT_TXCFG_TX_IPG_WIRE_CLK_EN;
+
+	if (emac->phy_if == PHY_INTERFACE_MODE_MII && mii == ICSS_MII1)
+		txcfg |= PRUSS_MII_RT_TXCFG_TX_MUX_SEL;
+	else if (emac->phy_if != PHY_INTERFACE_MODE_MII && mii == ICSS_MII0)
+		txcfg |= PRUSS_MII_RT_TXCFG_TX_MUX_SEL;
+
+	regmap_write(mii_rt, txcfg_reg, txcfg);
+	regmap_write(mii_rt, pcnt_reg, 0x1);
+}
+
 static void icssg_config_mii_init(struct prueth_emac *emac)
 {
-	u32 rxcfg, txcfg, rxcfg_reg, txcfg_reg, pcnt_reg;
 	struct prueth *prueth = emac->prueth;
 	int slice = prueth_emac_slice(emac);
+	u32 txcfg, txcfg_reg, pcnt_reg;
 	struct regmap *mii_rt;
 
 	mii_rt = prueth->mii_rt;
 
-	rxcfg_reg = (slice == ICSS_MII0) ? PRUSS_MII_RT_RXCFG0 :
-				       PRUSS_MII_RT_RXCFG1;
 	txcfg_reg = (slice == ICSS_MII0) ? PRUSS_MII_RT_TXCFG0 :
 				       PRUSS_MII_RT_TXCFG1;
 	pcnt_reg = (slice == ICSS_MII0) ? PRUSS_MII_RT_RX_PCNT0 :
 				       PRUSS_MII_RT_RX_PCNT1;
 
-	rxcfg = MII_RXCFG_DEFAULT;
 	txcfg = MII_TXCFG_DEFAULT;
 
-	if (slice == ICSS_MII1)
-		rxcfg |= PRUSS_MII_RT_RXCFG_RX_MUX_SEL;
-
 	/* In MII mode TX lines swapped inside ICSSG, so TX_MUX_SEL cfg need
 	 * to be swapped also comparing to RGMII mode.
 	 */
@@ -135,7 +156,6 @@ static void icssg_config_mii_init(struct prueth_emac *emac)
 	else if (emac->phy_if != PHY_INTERFACE_MODE_MII && slice == ICSS_MII1)
 		txcfg |= PRUSS_MII_RT_TXCFG_TX_MUX_SEL;
 
-	regmap_write(mii_rt, rxcfg_reg, rxcfg);
 	regmap_write(mii_rt, txcfg_reg, txcfg);
 	regmap_write(mii_rt, pcnt_reg, 0x1);
 }
@@ -249,6 +269,60 @@ static int emac_r30_is_done(struct prueth_emac *emac)
 	return 1;
 }
 
+static int prueth_switch_buffer_setup(struct prueth_emac *emac)
+{
+	struct icssg_buffer_pool_cfg __iomem *bpool_cfg;
+	struct icssg_rxq_ctx __iomem *rxq_ctx;
+	struct prueth *prueth = emac->prueth;
+	int slice = prueth_emac_slice(emac);
+	u32 addr;
+	int i;
+
+	addr = lower_32_bits(prueth->msmcram.pa);
+	if (slice)
+		addr += PRUETH_NUM_BUF_POOLS * PRUETH_EMAC_BUF_POOL_SIZE;
+
+	if (addr % SZ_64K) {
+		dev_warn(prueth->dev, "buffer pool needs to be 64KB aligned\n");
+		return -EINVAL;
+	}
+
+	bpool_cfg = emac->dram.va + BUFFER_POOL_0_ADDR_OFFSET;
+	/* workaround for f/w bug. bpool 0 needs to be initilalized */
+	for (i = 0; i <  PRUETH_NUM_BUF_POOLS; i++) {
+		writel(addr, &bpool_cfg[i].addr);
+		writel(PRUETH_EMAC_BUF_POOL_SIZE, &bpool_cfg[i].len);
+		addr += PRUETH_EMAC_BUF_POOL_SIZE;
+	}
+
+	if (!slice)
+		addr += PRUETH_NUM_BUF_POOLS * PRUETH_EMAC_BUF_POOL_SIZE;
+	else
+		addr += PRUETH_SW_NUM_BUF_POOLS_HOST * PRUETH_SW_BUF_POOL_SIZE_HOST;
+
+	for (i = PRUETH_NUM_BUF_POOLS;
+	     i <  PRUETH_SW_NUM_BUF_POOLS_HOST + PRUETH_NUM_BUF_POOLS;
+	     i++) {
+		writel(addr, &bpool_cfg[i].addr);
+		writel(PRUETH_SW_BUF_POOL_SIZE_HOST, &bpool_cfg[i].len);
+		addr += PRUETH_SW_BUF_POOL_SIZE_HOST;
+	}
+
+	if (!slice)
+		addr += PRUETH_SW_NUM_BUF_POOLS_HOST * PRUETH_SW_BUF_POOL_SIZE_HOST;
+	else
+		addr += PRUETH_EMAC_RX_CTX_BUF_SIZE;
+
+	rxq_ctx = emac->dram.va + HOST_RX_Q_PRE_CONTEXT_OFFSET;
+	for (i = 0; i < 3; i++)
+		writel(addr, &rxq_ctx->start[i]);
+
+	addr += PRUETH_EMAC_RX_CTX_BUF_SIZE;
+	writel(addr - SZ_2K, &rxq_ctx->end);
+
+	return 0;
+}
+
 static int prueth_emac_buffer_setup(struct prueth_emac *emac)
 {
 	struct icssg_buffer_pool_cfg __iomem *bpool_cfg;
@@ -325,13 +399,40 @@ static void icssg_init_emac_mode(struct prueth *prueth)
 	icssg_class_set_host_mac_addr(prueth->miig_rt, mac);
 }
 
+static void icssg_init_switch_mode(struct prueth *prueth)
+{
+	u32 addr = prueth->shram.pa + EMAC_ICSSG_SWITCH_DEFAULT_VLAN_TABLE_OFFSET;
+	int i;
+
+	if (prueth->emacs_initialized)
+		return;
+
+	/* Set VLAN TABLE address base */
+	regmap_update_bits(prueth->miig_rt, FDB_GEN_CFG1, SMEM_VLAN_OFFSET_MASK,
+			   addr <<  SMEM_VLAN_OFFSET);
+	/* Set enable VLAN aware mode, and FDBs for all PRUs */
+	regmap_write(prueth->miig_rt, FDB_GEN_CFG2, FDB_EN_ALL);
+	prueth->vlan_tbl = (struct prueth_vlan_tbl __force *)prueth->shram.va +
+			    EMAC_ICSSG_SWITCH_DEFAULT_VLAN_TABLE_OFFSET;
+	for (i = 0; i < SZ_4K - 1; i++) {
+		prueth->vlan_tbl[i].fid = i;
+		prueth->vlan_tbl[i].fid_c1 = 0;
+	}
+
+	icssg_class_set_host_mac_addr(prueth->miig_rt, prueth->hw_bridge_dev->dev_addr);
+	icssg_set_pvid(prueth, prueth->default_vlan, PRUETH_PORT_HOST);
+}
+
 int icssg_config(struct prueth *prueth, struct prueth_emac *emac, int slice)
 {
 	void __iomem *config = emac->dram.va + ICSSG_CONFIG_OFFSET;
 	struct icssg_flow_cfg __iomem *flow_cfg;
 	int ret;
 
-	icssg_init_emac_mode(prueth);
+	if (prueth->is_switch_mode)
+		icssg_init_switch_mode(prueth);
+	else
+		icssg_init_emac_mode(prueth);
 
 	memset_io(config, 0, TAS_GATE_MASK_LIST0);
 	icssg_miig_queues_init(prueth, slice);
@@ -345,7 +446,10 @@ int icssg_config(struct prueth *prueth, struct prueth_emac *emac, int slice)
 	regmap_update_bits(prueth->miig_rt, ICSSG_CFG_OFFSET,
 			   ICSSG_CFG_DEFAULT, ICSSG_CFG_DEFAULT);
 	icssg_miig_set_interface_mode(prueth->miig_rt, slice, emac->phy_if);
-	icssg_config_mii_init(emac);
+	if (prueth->is_switch_mode)
+		icssg_config_mii_init_switch(emac);
+	else
+		icssg_config_mii_init(emac);
 	icssg_config_ipg(emac);
 	icssg_update_rgmii_cfg(prueth->miig_rt, emac);
 
@@ -368,7 +472,10 @@ int icssg_config(struct prueth *prueth, struct prueth_emac *emac, int slice)
 	writeb(0, config + SPL_PKT_DEFAULT_PRIORITY);
 	writeb(0, config + QUEUE_NUM_UNTAGGED);
 
-	ret = prueth_emac_buffer_setup(emac);
+	if (prueth->is_switch_mode)
+		ret = prueth_switch_buffer_setup(emac);
+	else
+		ret = prueth_emac_buffer_setup(emac);
 	if (ret)
 		return ret;
 
diff --git a/drivers/net/ethernet/ti/icssg/icssg_config.h b/drivers/net/ethernet/ti/icssg/icssg_config.h
index 0d5d5d253b7a..09a021078ff5 100644
--- a/drivers/net/ethernet/ti/icssg/icssg_config.h
+++ b/drivers/net/ethernet/ti/icssg/icssg_config.h
@@ -35,6 +35,12 @@ struct icssg_flow_cfg {
 	(2 * (PRUETH_EMAC_BUF_POOL_SIZE * PRUETH_NUM_BUF_POOLS + \
 	 PRUETH_EMAC_RX_CTX_BUF_SIZE * 2))
 
+#define PRUETH_SW_BUF_POOL_SIZE_HOST	SZ_2K
+#define PRUETH_SW_NUM_BUF_POOLS_HOST	16
+#define MSMC_RAM_SIZE_SWITCH_MODE \
+	(MSMC_RAM_SIZE + \
+	(2 * PRUETH_SW_BUF_POOL_SIZE_HOST * PRUETH_SW_NUM_BUF_POOLS_HOST))
+
 #define PRUETH_SWITCH_FDB_MASK ((SIZE_OF_FDB / NUMBER_OF_FDB_BUCKET_ENTRIES) - 1)
 
 struct icssg_rxq_ctx {
diff --git a/drivers/net/ethernet/ti/icssg/icssg_prueth.c b/drivers/net/ethernet/ti/icssg/icssg_prueth.c
index c35fb51f08af..5b7e7297ce23 100644
--- a/drivers/net/ethernet/ti/icssg/icssg_prueth.c
+++ b/drivers/net/ethernet/ti/icssg/icssg_prueth.c
@@ -558,6 +558,8 @@ static int emac_rx_packet(struct prueth_emac *emac, u32 flow_id)
 	} else {
 		/* send the filled skb up the n/w stack */
 		skb_put(skb, pkt_len);
+		if (emac->prueth->is_switch_mode)
+			skb->offload_fwd_mark = emac->offload_fwd_mark;
 		skb->protocol = eth_type_trans(skb, ndev);
 		napi_gro_receive(&emac->napi_rx, skb);
 		ndev->stats.rx_bytes += pkt_len;
@@ -890,6 +892,19 @@ struct icssg_firmwares {
 	char *txpru;
 };
 
+static struct icssg_firmwares icssg_switch_firmwares[] = {
+	{
+		.pru = "ti-pruss/am65x-sr2-pru0-prusw-fw.elf",
+		.rtu = "ti-pruss/am65x-sr2-rtu0-prusw-fw.elf",
+		.txpru = "ti-pruss/am65x-sr2-txpru0-prusw-fw.elf",
+	},
+	{
+		.pru = "ti-pruss/am65x-sr2-pru1-prusw-fw.elf",
+		.rtu = "ti-pruss/am65x-sr2-rtu1-prusw-fw.elf",
+		.txpru = "ti-pruss/am65x-sr2-txpru1-prusw-fw.elf",
+	}
+};
+
 static struct icssg_firmwares icssg_emac_firmwares[] = {
 	{
 		.pru = "ti-pruss/am65x-sr2-pru0-prueth-fw.elf",
@@ -909,7 +924,10 @@ static int prueth_emac_start(struct prueth *prueth, struct prueth_emac *emac)
 	struct device *dev = prueth->dev;
 	int slice, ret;
 
-	firmwares = icssg_emac_firmwares;
+	if (prueth->is_switch_mode)
+		firmwares = icssg_switch_firmwares;
+	else
+		firmwares = icssg_emac_firmwares;
 
 	slice = prueth_emac_slice(emac);
 	if (slice < 0) {
@@ -1405,6 +1423,19 @@ static int emac_ndo_open(struct net_device *ndev)
 
 	queue_work(system_long_wq, &emac->stats_work.work);
 
+	if (prueth->is_switch_mode) {
+		icssg_fdb_add_del(emac, eth_stp_addr, prueth->default_vlan,
+				  ICSSG_FDB_ENTRY_P0_MEMBERSHIP |
+				  ICSSG_FDB_ENTRY_P1_MEMBERSHIP |
+				  ICSSG_FDB_ENTRY_P2_MEMBERSHIP |
+				  ICSSG_FDB_ENTRY_BLOCK,
+				  true);
+		icssg_vtbl_modify(emac, emac->port_vlan, BIT(emac->port_id),
+				  BIT(emac->port_id), true);
+		icssg_set_pvid(emac->prueth, emac->port_vlan, emac->port_id);
+		emac_set_port_state(emac, ICSSG_EMAC_PORT_VLAN_AWARE_ENABLE);
+	}
+
 	return 0;
 
 reset_tx_chan:
@@ -1929,6 +1960,308 @@ static void prueth_put_cores(struct prueth *prueth, int slice)
 		pru_rproc_put(prueth->pru[slice]);
 }
 
+static void prueth_offload_fwd_mark_update(struct prueth *prueth)
+{
+	int set_val = 0;
+	int i;
+
+	if (prueth->br_members == (PRUETH_PORT_MII0 | PRUETH_PORT_MII1))
+		set_val = 1;
+
+	dev_dbg(prueth->dev, "set offload_fwd_mark %d\n", set_val);
+
+	for (i = PRUETH_MAC0; i < PRUETH_NUM_MACS; i++) {
+		struct prueth_emac *emac = prueth->emac[i];
+
+		if (!emac || !emac->ndev)
+			continue;
+
+		emac->offload_fwd_mark = set_val;
+	}
+}
+
+bool prueth_dev_check(const struct net_device *ndev)
+{
+	if (ndev->netdev_ops == &emac_netdev_ops && netif_running(ndev)) {
+		struct prueth_emac *emac = netdev_priv(ndev);
+
+		return emac->prueth->is_switch_mode;
+	}
+
+	return false;
+}
+
+static int prueth_netdevice_port_link(struct net_device *ndev, struct net_device *br_ndev)
+{
+	struct prueth_emac *emac = netdev_priv(ndev);
+	struct prueth *prueth = emac->prueth;
+
+	if (!prueth->is_switch_mode)
+		return NOTIFY_DONE;
+
+	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)
+			return -EOPNOTSUPP;
+	}
+
+	prueth->br_members |= BIT(emac->port_id);
+
+	prueth_offload_fwd_mark_update(prueth);
+
+	return NOTIFY_DONE;
+}
+
+static void prueth_netdevice_port_unlink(struct net_device *ndev)
+{
+	struct prueth_emac *emac = netdev_priv(ndev);
+	struct prueth *prueth = emac->prueth;
+
+	prueth->br_members &= ~BIT(emac->port_id);
+
+	prueth_offload_fwd_mark_update(prueth);
+
+	if (!prueth->br_members)
+		prueth->hw_bridge_dev = NULL;
+}
+
+/* netdev notifier */
+static int prueth_netdevice_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 (ndev->netdev_ops != &emac_netdev_ops)
+		return NOTIFY_DONE;
+
+	switch (event) {
+	case NETDEV_CHANGEUPPER:
+		info = ptr;
+
+		if (netif_is_bridge_master(info->upper_dev)) {
+			if (info->linking)
+				ret = prueth_netdevice_port_link(ndev, info->upper_dev);
+			else
+				prueth_netdevice_port_unlink(ndev);
+		}
+		break;
+	default:
+		return NOTIFY_DONE;
+	}
+
+	return notifier_from_errno(ret);
+}
+
+static int prueth_register_notifiers(struct prueth *prueth)
+{
+	int ret = 0;
+
+	prueth->prueth_netdevice_nb.notifier_call = &prueth_netdevice_event;
+	ret = register_netdevice_notifier(&prueth->prueth_netdevice_nb);
+	if (ret) {
+		dev_err(prueth->dev, "can't register netdevice notifier\n");
+		return ret;
+	}
+
+	ret = prueth_switchdev_register_notifiers(prueth);
+	if (ret)
+		unregister_netdevice_notifier(&prueth->prueth_netdevice_nb);
+
+	return ret;
+}
+
+static void prueth_unregister_notifiers(struct prueth *prueth)
+{
+	prueth_switchdev_unregister_notifiers(prueth);
+	unregister_netdevice_notifier(&prueth->prueth_netdevice_nb);
+}
+
+static const struct devlink_ops prueth_devlink_ops = {};
+
+static int prueth_dl_switch_mode_get(struct devlink *dl, u32 id,
+				     struct devlink_param_gset_ctx *ctx)
+{
+	struct prueth_devlink *dl_priv = devlink_priv(dl);
+	struct prueth *prueth = dl_priv->prueth;
+
+	dev_dbg(prueth->dev, "%s id:%u\n", __func__, id);
+
+	if (id != PRUETH_DL_PARAM_SWITCH_MODE)
+		return -EOPNOTSUPP;
+
+	ctx->val.vbool = prueth->is_switch_mode;
+
+	return 0;
+}
+
+static int prueth_dl_switch_mode_set(struct devlink *dl, u32 id,
+				     struct devlink_param_gset_ctx *ctx)
+{
+	struct prueth_devlink *dl_priv = devlink_priv(dl);
+	struct prueth *prueth = dl_priv->prueth;
+	bool switch_en = ctx->val.vbool;
+	int i;
+
+	dev_dbg(prueth->dev, "%s id:%u\n", __func__, id);
+
+	if (id != PRUETH_DL_PARAM_SWITCH_MODE)
+		return -EOPNOTSUPP;
+
+	if (switch_en == prueth->is_switch_mode)
+		return 0;
+
+	if (!switch_en && prueth->br_members) {
+		dev_err(prueth->dev, "Remove ports from bridge before disabling switch mode\n");
+		return -EINVAL;
+	}
+
+	rtnl_lock();
+
+	prueth->default_vlan = 1;
+	prueth->is_switch_mode = switch_en;
+
+	for (i = PRUETH_MAC0; i < PRUETH_NUM_MACS; i++) {
+		struct net_device *sl_ndev = prueth->emac[i]->ndev;
+
+		if (!sl_ndev || !netif_running(sl_ndev))
+			continue;
+
+		dev_err(prueth->dev, "Cannot switch modes when i/f are up\n");
+		goto exit;
+	}
+
+	for (i = PRUETH_MAC0; i < PRUETH_NUM_MACS; i++) {
+		struct net_device *sl_ndev = prueth->emac[i]->ndev;
+		struct prueth_emac *emac;
+
+		if (!sl_ndev)
+			continue;
+
+		emac = netdev_priv(sl_ndev);
+		if (switch_en)
+			emac->port_vlan = prueth->default_vlan;
+		else
+			emac->port_vlan = 0;
+	}
+
+	dev_info(prueth->dev, "Enabling %s mode\n",
+		 switch_en ? "switch" : "Dual EMAC");
+
+exit:
+	rtnl_unlock();
+
+	return 0;
+}
+
+static const struct devlink_param prueth_devlink_params[] = {
+	DEVLINK_PARAM_DRIVER(PRUETH_DL_PARAM_SWITCH_MODE, "switch_mode",
+			     DEVLINK_PARAM_TYPE_BOOL,
+			     BIT(DEVLINK_PARAM_CMODE_RUNTIME),
+			     prueth_dl_switch_mode_get,
+			     prueth_dl_switch_mode_set, NULL),
+};
+
+static void prueth_unregister_devlink_ports(struct prueth *prueth)
+{
+	struct devlink_port *dl_port;
+	struct prueth_emac *emac;
+	int i;
+
+	for (i = PRUETH_MAC0; i < PRUETH_NUM_MACS; i++) {
+		emac = prueth->emac[i];
+		if (!emac)
+			continue;
+
+		dl_port = &emac->devlink_port;
+
+		if (dl_port->registered)
+			devlink_port_unregister(dl_port);
+	}
+}
+
+static int prueth_register_devlink(struct prueth *prueth)
+{
+	struct devlink_port_attrs attrs = {};
+	struct device *dev = prueth->dev;
+	struct prueth_devlink *dl_priv;
+	struct devlink_port *dl_port;
+	struct prueth_emac *emac;
+	int ret = 0;
+	int i;
+
+	prueth->devlink =
+		devlink_alloc(&prueth_devlink_ops, sizeof(*dl_priv), dev);
+	if (!prueth->devlink)
+		return -ENOMEM;
+
+	dl_priv = devlink_priv(prueth->devlink);
+	dl_priv->prueth = prueth;
+
+	/* Provide devlink hook to switch mode when multiple external ports
+	 * are present NUSS switchdev driver is enabled.
+	 */
+	if (prueth->is_switchmode_supported) {
+		ret = devlink_params_register(prueth->devlink,
+					      prueth_devlink_params,
+					      ARRAY_SIZE(prueth_devlink_params));
+		if (ret) {
+			dev_err(dev, "devlink params reg fail ret:%d\n", ret);
+			goto dl_unreg;
+		}
+	}
+
+	for (i = PRUETH_MAC0; i < PRUETH_NUM_MACS; i++) {
+		emac = prueth->emac[i];
+		if (!emac)
+			continue;
+
+		dl_port = &emac->devlink_port;
+
+		attrs.flavour = DEVLINK_PORT_FLAVOUR_PHYSICAL;
+		attrs.phys.port_number = emac->port_id;
+		attrs.switch_id.id_len = sizeof(resource_size_t);
+		memcpy(attrs.switch_id.id, prueth->switch_id, attrs.switch_id.id_len);
+		devlink_port_attrs_set(dl_port, &attrs);
+
+		ret = devlink_port_register(prueth->devlink, dl_port, emac->port_id);
+		if (ret) {
+			dev_err(dev, "devlink_port reg fail for port %d, ret:%d\n",
+				emac->port_id, ret);
+			goto dl_port_unreg;
+		}
+	}
+
+	devlink_register(prueth->devlink);
+	return ret;
+
+dl_port_unreg:
+	prueth_unregister_devlink_ports(prueth);
+dl_unreg:
+	devlink_free(prueth->devlink);
+
+	return ret;
+}
+
+static void prueth_unregister_devlink(struct prueth *prueth)
+{
+	devlink_unregister(prueth->devlink);
+
+	if (prueth->is_switchmode_supported) {
+		devlink_params_unregister(prueth->devlink, prueth_devlink_params,
+					  ARRAY_SIZE(prueth_devlink_params));
+	}
+
+	prueth_unregister_devlink_ports(prueth);
+	devlink_unregister(prueth->devlink);
+	devlink_free(prueth->devlink);
+}
+
 static const struct of_device_id prueth_dt_match[];
 
 static int prueth_probe(struct platform_device *pdev)
@@ -2063,6 +2396,10 @@ static int prueth_probe(struct platform_device *pdev)
 	}
 
 	msmc_ram_size = MSMC_RAM_SIZE;
+	prueth->is_switchmode_supported = prueth->pdata.switch_mode;
+	if (prueth->is_switchmode_supported)
+		msmc_ram_size = MSMC_RAM_SIZE_SWITCH_MODE;
+
 
 	/* NOTE: FW bug needs buffer base to be 64KB aligned */
 	prueth->msmcram.va =
@@ -2128,8 +2465,15 @@ static int prueth_probe(struct platform_device *pdev)
 		prueth->emac[PRUETH_MAC1]->iep = prueth->iep0;
 	}
 
+	ret = prueth_register_devlink(prueth);
+	if (ret)
+		goto netdev_exit;
+
 	/* register the network devices */
 	if (eth0_node) {
+		SET_NETDEV_DEVLINK_PORT(prueth->emac[PRUETH_MAC0]->ndev,
+					&prueth->emac[PRUETH_MAC0]->devlink_port);
+
 		ret = register_netdev(prueth->emac[PRUETH_MAC0]->ndev);
 		if (ret) {
 			dev_err(dev, "can't register netdev for port MII0");
@@ -2143,6 +2487,9 @@ static int prueth_probe(struct platform_device *pdev)
 	}
 
 	if (eth1_node) {
+		SET_NETDEV_DEVLINK_PORT(prueth->emac[PRUETH_MAC1]->ndev,
+					&prueth->emac[PRUETH_MAC1]->devlink_port);
+
 		ret = register_netdev(prueth->emac[PRUETH_MAC1]->ndev);
 		if (ret) {
 			dev_err(dev, "can't register netdev for port MII1");
@@ -2154,6 +2501,14 @@ static int prueth_probe(struct platform_device *pdev)
 		phy_attached_info(prueth->emac[PRUETH_MAC1]->ndev->phydev);
 	}
 
+	if (prueth->is_switchmode_supported) {
+		ret = prueth_register_notifiers(prueth);
+		if (ret)
+			goto netdev_unregister;
+
+		sprintf(prueth->switch_id, "%s", dev_name(dev));
+	}
+
 	dev_info(dev, "TI PRU ethernet driver initialized: %s EMAC mode\n",
 		 (!eth0_node || !eth1_node) ? "single" : "dual");
 
@@ -2215,6 +2570,8 @@ static void prueth_remove(struct platform_device *pdev)
 	struct device_node *eth_node;
 	int i;
 
+	prueth_unregister_notifiers(prueth);
+
 	for (i = 0; i < PRUETH_NUM_MACS; i++) {
 		if (!prueth->registered_netdevs[i])
 			continue;
@@ -2223,6 +2580,7 @@ static void prueth_remove(struct platform_device *pdev)
 		prueth->emac[i]->ndev->phydev = NULL;
 		unregister_netdev(prueth->registered_netdevs[i]);
 	}
+	prueth_unregister_devlink(prueth);
 
 	for (i = 0; i < PRUETH_NUM_MACS; i++) {
 		eth_node = prueth->eth_node[i];
@@ -2312,6 +2670,7 @@ static const struct dev_pm_ops prueth_dev_pm_ops = {
 static const struct prueth_pdata am654_icssg_pdata = {
 	.fdqring_mode = K3_RINGACC_RING_MODE_MESSAGE,
 	.quirk_10m_link_issue = 1,
+	.switch_mode = 1,
 };
 
 static const struct of_device_id prueth_dt_match[] = {
diff --git a/drivers/net/ethernet/ti/icssg/icssg_switchdev.c b/drivers/net/ethernet/ti/icssg/icssg_switchdev.c
index 48d8ed4fa7a8..90d0d98e0ef9 100644
--- a/drivers/net/ethernet/ti/icssg/icssg_switchdev.c
+++ b/drivers/net/ethernet/ti/icssg/icssg_switchdev.c
@@ -14,7 +14,7 @@
 
 #include "icssg_prueth.h"
 #include "icssg_switchdev.h"
-#include "icss_mii_rt.h"
+#include "icssg_mii_rt.h"
 
 struct prueth_switchdev_event_work {
 	struct work_struct work;
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [RFC PATCH net-next 4/4] net: ti: icssg_prueth: add TAPRIO offload support
  2023-08-30 11:08 [RFC PATCH net-next 0/4] Introduce switch mode and TAPRIO offload support for ICSSG driver MD Danish Anwar
                   ` (2 preceding siblings ...)
  2023-08-30 11:08 ` [RFC PATCH net-next 3/4] net: ti: icssg-prueth: Add support for ICSSG switch firmware on AM654 PG2.0 EVM MD Danish Anwar
@ 2023-08-30 11:08 ` MD Danish Anwar
  2023-09-04 14:12   ` Andrew Lunn
  2023-09-07 12:23   ` Roger Quadros
  3 siblings, 2 replies; 22+ messages in thread
From: MD Danish Anwar @ 2023-08-30 11:08 UTC (permalink / raw)
  To: Simon Horman, Roger Quadros, Vignesh Raghavendra, Jacob Keller,
	Andrew Lunn, MD Danish Anwar, Richard Cochran, Paolo Abeni,
	Jakub Kicinski, Eric Dumazet, David S. Miller
  Cc: linux-kernel, netdev, srk, r-gunasekaran

From: Roger Quadros <rogerq@kernel.org>

ICSSG dual-emac f/w supports Enhanced Scheduled Traffic (EST – defined
in P802.1Qbv/D2.2 that later got included in IEEE 802.1Q-2018)
configuration. EST allows express queue traffic to be scheduled
(placed) on the wire at specific repeatable time intervals. In
Linux kernel, EST configuration is done through tc command and
the taprio scheduler in the net core implements a software only
scheduler (SCH_TAPRIO). If the NIC is capable of EST configuration,
user indicate "flag 2" in the command which is then parsed by
taprio scheduler in net core and indicate that the command is to
be offloaded to h/w. taprio then offloads the command to the
driver by calling ndo_setup_tc() ndo ops. This patch implements
ndo_setup_tc() to offload EST configuration to ICSSG.

Signed-off-by: Roger Quadros <rogerq@ti.com>
Signed-off-by: Vignesh Raghavendra <vigneshr@ti.com>
Signed-off-by: MD Danish Anwar <danishanwar@ti.com>
---
 drivers/net/ethernet/ti/Makefile             |   3 +-
 drivers/net/ethernet/ti/icssg/icssg_prueth.c |   5 +-
 drivers/net/ethernet/ti/icssg/icssg_prueth.h |   7 +
 drivers/net/ethernet/ti/icssg/icssg_qos.c    | 294 +++++++++++++++++++
 drivers/net/ethernet/ti/icssg/icssg_qos.h    | 119 ++++++++
 5 files changed, 426 insertions(+), 2 deletions(-)
 create mode 100644 drivers/net/ethernet/ti/icssg/icssg_qos.c
 create mode 100644 drivers/net/ethernet/ti/icssg/icssg_qos.h

diff --git a/drivers/net/ethernet/ti/Makefile b/drivers/net/ethernet/ti/Makefile
index 3adceff760ce..de348c20eff9 100644
--- a/drivers/net/ethernet/ti/Makefile
+++ b/drivers/net/ethernet/ti/Makefile
@@ -38,5 +38,6 @@ icssg-prueth-y := k3-cppi-desc-pool.o \
 		  icssg/icssg_mii_cfg.o \
 		  icssg/icssg_stats.o \
 		  icssg/icssg_ethtool.o \
-		  icssg/icssg_switchdev.o
+		  icssg/icssg_switchdev.o \
+		  icssg/icssg_qos.o
 obj-$(CONFIG_TI_ICSS_IEP) += icssg/icss_iep.o
diff --git a/drivers/net/ethernet/ti/icssg/icssg_prueth.c b/drivers/net/ethernet/ti/icssg/icssg_prueth.c
index 5b7e7297ce23..3236af45aa4e 100644
--- a/drivers/net/ethernet/ti/icssg/icssg_prueth.c
+++ b/drivers/net/ethernet/ti/icssg/icssg_prueth.c
@@ -1179,7 +1179,7 @@ static int emac_phy_connect(struct prueth_emac *emac)
 	return 0;
 }
 
-static u64 prueth_iep_gettime(void *clockops_data, struct ptp_system_timestamp *sts)
+u64 prueth_iep_gettime(void *clockops_data, struct ptp_system_timestamp *sts)
 {
 	u32 hi_rollover_count, hi_rollover_count_r;
 	struct prueth_emac *emac = clockops_data;
@@ -1416,6 +1416,8 @@ static int emac_ndo_open(struct net_device *ndev)
 		napi_enable(&emac->tx_chns[i].napi_tx);
 	napi_enable(&emac->napi_rx);
 
+	icssg_qos_init(ndev);
+
 	/* start PHY */
 	phy_start(ndev->phydev);
 
@@ -1695,6 +1697,7 @@ static const struct net_device_ops emac_netdev_ops = {
 	.ndo_set_rx_mode = emac_ndo_set_rx_mode,
 	.ndo_eth_ioctl = emac_ndo_ioctl,
 	.ndo_get_stats64 = emac_ndo_get_stats64,
+	.ndo_setup_tc = icssg_qos_ndo_setup_tc,
 };
 
 /* get emac_port corresponding to eth_node name */
diff --git a/drivers/net/ethernet/ti/icssg/icssg_prueth.h b/drivers/net/ethernet/ti/icssg/icssg_prueth.h
index 6e18da06c786..43b67213d8c7 100644
--- a/drivers/net/ethernet/ti/icssg/icssg_prueth.h
+++ b/drivers/net/ethernet/ti/icssg/icssg_prueth.h
@@ -37,6 +37,7 @@
 #include "icssg_config.h"
 #include "icss_iep.h"
 #include "icssg_switch_map.h"
+#include "icssg_qos.h"
 
 #define PRUETH_MAX_MTU          (2000 - ETH_HLEN - ETH_FCS_LEN)
 #define PRUETH_MIN_PKT_SIZE     (VLAN_ETH_ZLEN)
@@ -186,6 +187,9 @@ struct prueth_emac {
 	struct devlink_port devlink_port;
 	int port_vlan;
 
+	struct prueth_qos qos;
+	struct work_struct ts_work;
+
 	struct delayed_work stats_work;
 	u64 stats[ICSSG_NUM_STATS];
 };
@@ -331,4 +335,7 @@ void icssg_set_pvid(struct prueth *prueth, u8 vid, u8 port);
 void emac_stats_work_handler(struct work_struct *work);
 void emac_update_hardware_stats(struct prueth_emac *emac);
 int emac_get_stat_by_name(struct prueth_emac *emac, char *stat_name);
+
+u64 prueth_iep_gettime(void *clockops_data, struct ptp_system_timestamp *sts);
+
 #endif /* __NET_TI_ICSSG_PRUETH_H */
diff --git a/drivers/net/ethernet/ti/icssg/icssg_qos.c b/drivers/net/ethernet/ti/icssg/icssg_qos.c
new file mode 100644
index 000000000000..e8102703e257
--- /dev/null
+++ b/drivers/net/ethernet/ti/icssg/icssg_qos.c
@@ -0,0 +1,294 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Texas Instruments ICSSG PRUETH QoS submodule
+ * Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com/
+ */
+
+#include <linux/printk.h>
+#include "icssg_prueth.h"
+#include "icssg_switch_map.h"
+
+static void icssg_qos_tas_init(struct net_device *ndev);
+
+void icssg_qos_init(struct net_device *ndev)
+{
+	icssg_qos_tas_init(ndev);
+
+	/* IET init goes here */
+}
+
+static void tas_update_fw_list_pointers(struct prueth_emac *emac)
+{
+	struct tas_config *tas = &emac->qos.tas.config;
+
+	if ((readb(tas->active_list)) == TAS_LIST0) {
+		tas->fw_active_list = (struct tas_firmware_list __force *)emac->dram.va +
+				       TAS_GATE_MASK_LIST0;
+		tas->fw_shadow_list = (struct tas_firmware_list __force *)emac->dram.va +
+				       TAS_GATE_MASK_LIST1;
+	} else {
+		tas->fw_active_list = (struct tas_firmware_list __force *)emac->dram.va +
+				       TAS_GATE_MASK_LIST1;
+		tas->fw_shadow_list = (struct tas_firmware_list __force *)emac->dram.va +
+				       TAS_GATE_MASK_LIST0;
+	}
+}
+
+static void tas_update_maxsdu_table(struct prueth_emac *emac)
+{
+	struct tas_config *tas = &emac->qos.tas.config;
+	u16 __iomem *max_sdu_tbl_ptr;
+	u8 gate_idx;
+
+	/* update the maxsdu table */
+	max_sdu_tbl_ptr = emac->dram.va + TAS_QUEUE_MAX_SDU_LIST;
+
+	for (gate_idx = 0; gate_idx < TAS_MAX_NUM_QUEUES; gate_idx++)
+		writew(tas->max_sdu_table.max_sdu[gate_idx], &max_sdu_tbl_ptr[gate_idx]);
+}
+
+static void tas_reset(struct prueth_emac *emac)
+{
+	struct tas_config *tas = &emac->qos.tas.config;
+	int i;
+
+	for (i = 0; i < TAS_MAX_NUM_QUEUES; i++)
+		tas->max_sdu_table.max_sdu[i] = 2048;
+
+	tas_update_maxsdu_table(emac);
+
+	writeb(TAS_LIST0, tas->active_list);
+
+	memset_io((void __iomem __force *)tas->fw_active_list, 0, sizeof(*tas->fw_active_list));
+	memset_io((void __iomem __force *)tas->fw_shadow_list, 0, sizeof(*tas->fw_shadow_list));
+}
+
+static int tas_set_state(struct prueth_emac *emac, enum tas_state state)
+{
+	struct tas_config *tas = &emac->qos.tas.config;
+	int ret;
+
+	if (tas->state == state)
+		return 0;
+
+	switch (state) {
+	case TAS_STATE_RESET:
+		tas_reset(emac);
+		ret = emac_set_port_state(emac, ICSSG_EMAC_PORT_TAS_RESET);
+		tas->state = TAS_STATE_RESET;
+		break;
+	case TAS_STATE_ENABLE:
+		ret = emac_set_port_state(emac, ICSSG_EMAC_PORT_TAS_ENABLE);
+		tas->state = TAS_STATE_ENABLE;
+		break;
+	case TAS_STATE_DISABLE:
+		ret = emac_set_port_state(emac, ICSSG_EMAC_PORT_TAS_DISABLE);
+		tas->state = TAS_STATE_DISABLE;
+		break;
+	default:
+		netdev_err(emac->ndev, "%s: unsupported state\n", __func__);
+		ret = -EINVAL;
+		break;
+	}
+
+	if (ret)
+		netdev_err(emac->ndev, "TAS set state failed %d\n", ret);
+	return ret;
+}
+
+static int tas_set_trigger_list_change(struct prueth_emac *emac)
+{
+	struct tc_taprio_qopt_offload *admin_list = emac->qos.tas.taprio_admin;
+	struct tas_config *tas = &emac->qos.tas.config;
+	struct ptp_system_timestamp sts;
+	u32 change_cycle_count;
+	u32 cycle_time;
+	u64 base_time;
+	u64 cur_time;
+
+	cycle_time = admin_list->cycle_time - 4; /* -4ns to compensate for IEP wraparound time */
+	base_time = admin_list->base_time;
+	cur_time = prueth_iep_gettime(emac, &sts);
+
+	if (base_time > cur_time)
+		change_cycle_count = DIV_ROUND_UP_ULL(base_time - cur_time, cycle_time);
+	else
+		change_cycle_count = 1;
+
+	writel(cycle_time, emac->dram.va + TAS_ADMIN_CYCLE_TIME);
+	writel(change_cycle_count, emac->dram.va + TAS_CONFIG_CHANGE_CYCLE_COUNT);
+	writeb(admin_list->num_entries, emac->dram.va + TAS_ADMIN_LIST_LENGTH);
+
+	/* config_change cleared by f/w to ack reception of new shadow list */
+	writeb(1, &tas->config_list->config_change);
+	/* config_pending cleared by f/w when new shadow list is copied to active list */
+	writeb(1, &tas->config_list->config_pending);
+
+	return emac_set_port_state(emac, ICSSG_EMAC_PORT_TAS_TRIGGER);
+}
+
+static int tas_update_oper_list(struct prueth_emac *emac)
+{
+	struct tc_taprio_qopt_offload *admin_list = emac->qos.tas.taprio_admin;
+	struct tas_config *tas = &emac->qos.tas.config;
+	u32 tas_acc_gate_close_time = 0;
+	u8 idx, gate_idx, val;
+	int ret;
+
+	tas_update_fw_list_pointers(emac);
+
+	for (idx = 0; idx < admin_list->num_entries; idx++) {
+		tas->fw_shadow_list->gate_mask_list[idx] = admin_list->entries[idx].gate_mask;
+		tas_acc_gate_close_time += admin_list->entries[idx].interval;
+
+		/* extend last entry till end of cycle time */
+		if (idx == admin_list->num_entries - 1)
+			tas->fw_shadow_list->win_end_time_list[idx] = admin_list->cycle_time;
+		else
+			tas->fw_shadow_list->win_end_time_list[idx] = tas_acc_gate_close_time;
+	}
+
+	/* clear remaining entries */
+	for (idx = admin_list->num_entries; idx < TAS_MAX_CMD_LISTS; idx++) {
+		tas->fw_shadow_list->gate_mask_list[idx] = 0;
+		tas->fw_shadow_list->win_end_time_list[idx] = 0;
+	}
+
+	/* update the Array of gate close time for each queue in each window */
+	for (idx = 0 ; idx < admin_list->num_entries; idx++) {
+		/* On Linux, only PRUETH_MAX_TX_QUEUES are supported per port */
+		for (gate_idx = 0; gate_idx < PRUETH_MAX_TX_QUEUES; gate_idx++) {
+			u32 gate_close_time = 0;
+
+			if (tas->fw_shadow_list->gate_mask_list[idx] & BIT(gate_idx))
+				gate_close_time = tas->fw_shadow_list->win_end_time_list[idx];
+
+			tas->fw_shadow_list->gate_close_time_list[idx][gate_idx] = gate_close_time;
+		}
+	}
+
+	/* tell f/w to swap active & shadow list */
+	ret = tas_set_trigger_list_change(emac);
+	if (ret) {
+		netdev_err(emac->ndev, "failed to swap f/w config list: %d\n", ret);
+		return ret;
+	}
+
+	/* Wait for completion */
+	ret = readb_poll_timeout(&tas->config_list->config_change, val, !val,
+				 USEC_PER_MSEC, 10 * USEC_PER_MSEC);
+	if (ret) {
+		netdev_err(emac->ndev, "TAS list change completion time out\n");
+		return ret;
+	}
+
+	tas_update_fw_list_pointers(emac);
+
+	return 0;
+}
+
+static int emac_set_taprio(struct prueth_emac *emac)
+{
+	struct tc_taprio_qopt_offload *taprio = emac->qos.tas.taprio_admin;
+	int ret;
+
+	if (taprio->cmd == TAPRIO_CMD_DESTROY)
+		return tas_set_state(emac, TAS_STATE_DISABLE);
+
+	if (taprio->cmd != TAPRIO_CMD_REPLACE)
+		return -EOPNOTSUPP;
+
+	ret = tas_update_oper_list(emac);
+	if (ret)
+		return ret;
+
+	return tas_set_state(emac, TAS_STATE_ENABLE);
+}
+
+static void emac_cp_taprio(struct tc_taprio_qopt_offload *from,
+			   struct tc_taprio_qopt_offload *to)
+{
+	int i;
+
+	*to = *from;
+	for (i = 0; i < from->num_entries; i++)
+		to->entries[i] = from->entries[i];
+}
+
+static int emac_setup_taprio(struct net_device *ndev, struct tc_taprio_qopt_offload *taprio)
+{
+	struct prueth_emac *emac = netdev_priv(ndev);
+	struct tc_taprio_qopt_offload *est_new;
+	int ret, idx;
+
+	if (!netif_running(ndev)) {
+		netdev_err(ndev, "interface is down, link speed unknown\n");
+		return -ENETDOWN;
+	}
+
+	if (taprio->cycle_time_extension) {
+		netdev_err(ndev, "Failed to set cycle time extension");
+		return -EOPNOTSUPP;
+	}
+
+	if (taprio->num_entries == 0 ||
+	    taprio->num_entries > TAS_MAX_CMD_LISTS) {
+		netdev_err(ndev, "unsupported num_entries %ld in taprio config\n",
+			   taprio->num_entries);
+		return -EINVAL;
+	}
+
+	/* If any time_interval is 0 in between the list, then exit */
+	for (idx = 0; idx < taprio->num_entries; idx++) {
+		if (taprio->entries[idx].interval == 0) {
+			netdev_err(ndev, "0 interval in taprio config not supported\n");
+			return -EINVAL;
+		}
+	}
+
+	if (emac->qos.tas.taprio_admin)
+		devm_kfree(&ndev->dev, emac->qos.tas.taprio_admin);
+
+	est_new = devm_kzalloc(&ndev->dev,
+			       struct_size(est_new, entries, taprio->num_entries),
+			       GFP_KERNEL);
+	emac_cp_taprio(taprio, est_new);
+	emac->qos.tas.taprio_admin = est_new;
+	ret = emac_set_taprio(emac);
+	if (ret)
+		devm_kfree(&ndev->dev, est_new);
+
+	return ret;
+}
+
+int icssg_qos_ndo_setup_tc(struct net_device *ndev, enum tc_setup_type type,
+			   void *type_data)
+{
+	switch (type) {
+	case TC_SETUP_QDISC_TAPRIO:
+		return emac_setup_taprio(ndev, type_data);
+	default:
+		return -EOPNOTSUPP;
+	}
+}
+
+static void icssg_qos_tas_init(struct net_device *ndev)
+{
+	struct prueth_emac *emac = netdev_priv(ndev);
+	bool need_setup = false;
+	struct tas_config *tas;
+
+	tas = &emac->qos.tas.config;
+
+	if (tas->state == TAS_STATE_ENABLE)
+		need_setup = true;
+
+	tas->config_list = emac->dram.va + TAS_CONFIG_CHANGE_TIME;
+	tas->active_list = emac->dram.va + TAS_ACTIVE_LIST_INDEX;
+
+	tas_update_fw_list_pointers(emac);
+
+	tas_set_state(emac, TAS_STATE_RESET);
+
+	if (need_setup)
+		emac_set_taprio(emac);
+}
diff --git a/drivers/net/ethernet/ti/icssg/icssg_qos.h b/drivers/net/ethernet/ti/icssg/icssg_qos.h
new file mode 100644
index 000000000000..645c367bf9d0
--- /dev/null
+++ b/drivers/net/ethernet/ti/icssg/icssg_qos.h
@@ -0,0 +1,119 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com/
+ */
+
+#ifndef __NET_TI_ICSSG_QOS_H
+#define __NET_TI_ICSSG_QOS_H
+
+#include <linux/atomic.h>
+#include <linux/netdevice.h>
+#include <net/pkt_sched.h>
+
+/**
+ * Maximum number of gate command entries in each list.
+ */
+#define TAS_MAX_CMD_LISTS   (16)
+
+/**
+ * Maximum number of transmit queues supported by implementation
+ */
+#define TAS_MAX_NUM_QUEUES  (8)
+
+/**
+ * Minimum cycle time supported by implementation (in ns)
+ */
+#define TAS_MIN_CYCLE_TIME  (1000000)
+
+/**
+ * Minimum TAS window duration supported by implementation (in ns)
+ */
+#define TAS_MIN_WINDOW_DURATION  (10000)
+
+/**
+ * List number 0 or 1. Also the value at memory location TAS_ACTIVE_LIST_INDEX
+ */
+enum tas_list_num {
+	TAS_LIST0 = 0,
+	TAS_LIST1 = 1
+};
+
+/**
+ * state of TAS in f/w
+ */
+enum tas_state {
+	/* PRU's are idle */
+	TAS_STATE_DISABLE = 0,
+	/* Enable TAS */
+	TAS_STATE_ENABLE = 1,
+	/* Firmware will reset the state machine */
+	TAS_STATE_RESET = 2,
+};
+
+/**
+ * Config state machine variables. See IEEE Std 802.1Q-2018 8.6.8.4
+ */
+struct tas_config_list {
+	/* New list is copied at this time */
+	u64 config_change_time;
+	/* config change error counter, incremented if
+	 * admin->BaseTime < current time and TAS_enabled is true
+	 */
+	u32 config_change_error_counter;
+	/* True if list update is pending */
+	u8 config_pending;
+	/* Set to true when application trigger updating of admin list
+	 * to active list, cleared when configChangeTime is updated
+	 */
+	u8 config_change;
+};
+
+/**
+ * Max SDU table. See IEEE Std 802.1Q-2018 12.29.1.1
+ */
+struct tas_max_sdu_table {
+	u16 max_sdu[TAS_MAX_NUM_QUEUES];
+};
+
+/**
+ * TAS List Structure based on firmware memory map
+ */
+struct tas_firmware_list {
+	/* window gate mask list */
+	u8 gate_mask_list[TAS_MAX_CMD_LISTS];
+	/* window end time list */
+	u32 win_end_time_list[TAS_MAX_CMD_LISTS];
+	/* Array of gate close time for each queue in each window */
+	u32 gate_close_time_list[TAS_MAX_CMD_LISTS][TAS_MAX_NUM_QUEUES];
+};
+
+/**
+ * Main Time Aware Shaper Handle
+ */
+struct tas_config {
+	enum tas_state state;
+	struct tas_max_sdu_table max_sdu_table;
+	/* Config change variables */
+	struct tas_config_list __iomem *config_list;
+	/* Whether list 1 or list 2 is the operating list */
+	u8 __iomem *active_list;
+	/* active List pointer, used by firmware */
+	struct tas_firmware_list *fw_active_list;
+	/* shadow List pointer, used by driver */
+	struct tas_firmware_list *fw_shadow_list;
+};
+
+struct prueth_qos_tas {
+	struct tc_taprio_qopt_offload *taprio_admin;
+	struct tc_taprio_qopt_offload *taprio_oper;
+	struct tas_config config;
+};
+
+struct prueth_qos {
+	/* IET data structure goes here */
+	struct prueth_qos_tas tas;
+};
+
+void icssg_qos_init(struct net_device *ndev);
+int icssg_qos_ndo_setup_tc(struct net_device *ndev, enum tc_setup_type type,
+			   void *type_data);
+#endif /* __NET_TI_ICSSG_QOS_H */
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* Re: [RFC PATCH net-next 1/4] net: ti: icssg-prueth: Add helper functions to configure FDB
  2023-08-30 11:08 ` [RFC PATCH net-next 1/4] net: ti: icssg-prueth: Add helper functions to configure FDB MD Danish Anwar
@ 2023-09-04 14:02   ` Andrew Lunn
  2023-09-05  8:36     ` MD Danish Anwar
  0 siblings, 1 reply; 22+ messages in thread
From: Andrew Lunn @ 2023-09-04 14:02 UTC (permalink / raw)
  To: MD Danish Anwar
  Cc: Simon Horman, Roger Quadros, Vignesh Raghavendra, Jacob Keller,
	Richard Cochran, Paolo Abeni, Jakub Kicinski, Eric Dumazet,
	David S. Miller, linux-kernel, netdev, srk, r-gunasekaran

> +int icssg_send_fdb_msg(struct prueth_emac *emac, struct mgmt_cmd *cmd,
> +		       struct mgmt_cmd_rsp *rsp)
> +{
> +	struct prueth *prueth = emac->prueth;
> +	int slice = prueth_emac_slice(emac);
> +	int i = 10000;
> +	int addr;
> +
> +	addr = icssg_queue_pop(prueth, slice == 0 ?
> +			       ICSSG_CMD_POP_SLICE0 : ICSSG_CMD_POP_SLICE1);
> +	if (addr < 0)
> +		return addr;
> +
> +	/* First 4 bytes have FW owned buffer linking info which should
> +	 * not be touched
> +	 */
> +	memcpy_toio(prueth->shram.va + addr + 4, cmd, sizeof(*cmd));
> +	icssg_queue_push(prueth, slice == 0 ?
> +			 ICSSG_CMD_PUSH_SLICE0 : ICSSG_CMD_PUSH_SLICE1, addr);
> +	while (i--) {
> +		addr = icssg_queue_pop(prueth, slice == 0 ?
> +				       ICSSG_RSP_POP_SLICE0 : ICSSG_RSP_POP_SLICE1);
> +		if (addr < 0) {
> +			usleep_range(1000, 2000);
> +			continue;
> +		}

Please try to make use of include/linux/iopoll.h.

> +	if (i <= 0) {
> +		netdev_err(emac->ndev, "Timedout sending HWQ message\n");
> +		return -EINVAL;

Using iopoll.h will fix this, but -ETIMEDOUT, not -EINVAL.

      Andrew

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [RFC PATCH net-next 3/4] net: ti: icssg-prueth: Add support for ICSSG switch firmware on AM654 PG2.0 EVM
  2023-08-30 11:08 ` [RFC PATCH net-next 3/4] net: ti: icssg-prueth: Add support for ICSSG switch firmware on AM654 PG2.0 EVM MD Danish Anwar
@ 2023-09-04 14:08   ` Andrew Lunn
  2023-09-05  8:43     ` MD Danish Anwar
  0 siblings, 1 reply; 22+ messages in thread
From: Andrew Lunn @ 2023-09-04 14:08 UTC (permalink / raw)
  To: MD Danish Anwar
  Cc: Simon Horman, Roger Quadros, Vignesh Raghavendra, Jacob Keller,
	Richard Cochran, Paolo Abeni, Jakub Kicinski, Eric Dumazet,
	David S. Miller, linux-kernel, netdev, srk, r-gunasekaran

> Switch mode requires loading of new firmware into ICSSG cores. This
> means interfaces have to taken down and then reconfigured to switch mode
> using devlink.

Can you always run it in switch mode, just not have the ports in a
bridge?

	Andrew

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [RFC PATCH net-next 4/4] net: ti: icssg_prueth: add TAPRIO offload support
  2023-08-30 11:08 ` [RFC PATCH net-next 4/4] net: ti: icssg_prueth: add TAPRIO offload support MD Danish Anwar
@ 2023-09-04 14:12   ` Andrew Lunn
  2023-09-05  8:37     ` MD Danish Anwar
  2023-09-07 12:23   ` Roger Quadros
  1 sibling, 1 reply; 22+ messages in thread
From: Andrew Lunn @ 2023-09-04 14:12 UTC (permalink / raw)
  To: MD Danish Anwar
  Cc: Simon Horman, Roger Quadros, Vignesh Raghavendra, Jacob Keller,
	Richard Cochran, Paolo Abeni, Jakub Kicinski, Eric Dumazet,
	David S. Miller, linux-kernel, netdev, srk, r-gunasekaran

> +void icssg_qos_init(struct net_device *ndev)
> +{
> +	icssg_qos_tas_init(ndev);
> +
> +	/* IET init goes here */
> +}

This sees like a TODO?

     Andrew

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [RFC PATCH net-next 1/4] net: ti: icssg-prueth: Add helper functions to configure FDB
  2023-09-04 14:02   ` Andrew Lunn
@ 2023-09-05  8:36     ` MD Danish Anwar
  2023-09-07 11:57       ` Roger Quadros
  0 siblings, 1 reply; 22+ messages in thread
From: MD Danish Anwar @ 2023-09-05  8:36 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: Simon Horman, Roger Quadros, Vignesh Raghavendra, Jacob Keller,
	Richard Cochran, Paolo Abeni, Jakub Kicinski, Eric Dumazet,
	David S. Miller, linux-kernel, netdev, srk, r-gunasekaran

Hi Andrew

On 04/09/23 19:32, Andrew Lunn wrote:
>> +int icssg_send_fdb_msg(struct prueth_emac *emac, struct mgmt_cmd *cmd,
>> +		       struct mgmt_cmd_rsp *rsp)
>> +{
>> +	struct prueth *prueth = emac->prueth;
>> +	int slice = prueth_emac_slice(emac);
>> +	int i = 10000;
>> +	int addr;
>> +
>> +	addr = icssg_queue_pop(prueth, slice == 0 ?
>> +			       ICSSG_CMD_POP_SLICE0 : ICSSG_CMD_POP_SLICE1);
>> +	if (addr < 0)
>> +		return addr;
>> +
>> +	/* First 4 bytes have FW owned buffer linking info which should
>> +	 * not be touched
>> +	 */
>> +	memcpy_toio(prueth->shram.va + addr + 4, cmd, sizeof(*cmd));
>> +	icssg_queue_push(prueth, slice == 0 ?
>> +			 ICSSG_CMD_PUSH_SLICE0 : ICSSG_CMD_PUSH_SLICE1, addr);
>> +	while (i--) {
>> +		addr = icssg_queue_pop(prueth, slice == 0 ?
>> +				       ICSSG_RSP_POP_SLICE0 : ICSSG_RSP_POP_SLICE1);
>> +		if (addr < 0) {
>> +			usleep_range(1000, 2000);
>> +			continue;
>> +		}
> 
> Please try to make use of include/linux/iopoll.h.
> 

I don't think APIs from iopoll.h will be useful here.
readl_poll_timeout() periodically polls an address until a condition is
met or a timeout occurs. It takes address, condition as argument and
store the value read from the address in val.

Here in our use case we need to continuously read the value returned
from icssg_queue_pop() and check if that is valid or not. If it's not
valid, we keep polling until timeout happens.

icssg_queue_pop() does two read operations. It checks if the queue
number is valid or not. Then it reads the ICSSG_QUEUE_CNT_OFFSET for
that queue, if the value read is zero it returns inval. After that it
reads the value from ICSSG_QUEUE_OFFSET of that queue and store it in
'val'. The returned value from icssg_queue_pop() is checked
continuously, if it's an error code, we keep polling. If it's a good
value then we call icssg_queue_push() with that value. As you can see
from the below definition of icssg_queue_pop() we are doing two reads
and two checks for error. I don't think this can be achieved by using
APIs in iopoll.h. readl_poll_timeout() reads from a single address
directly but we don't ave a single address that we can pass to
readl_poll_timeout() as an argument as we have to do two reads from two
different addresses during each poll.

So I don't think we can use iopoll.h here. Please let me know if this
looks ok to you or if there is any other way we can use iopoll.h here

int icssg_queue_pop(struct prueth *prueth, u8 queue)
{
    u32 val, cnt;

    if (queue >= ICSSG_QUEUES_MAX)
	return -EINVAL;

    regmap_read(prueth->miig_rt, ICSSG_QUEUE_CNT_OFFSET + 4*queue,&cnt);
    if (!cnt)
	return -EINVAL;

    regmap_read(prueth->miig_rt, ICSSG_QUEUE_OFFSET + 4 * queue, &val);

    return val;
}

>> +	if (i <= 0) {
>> +		netdev_err(emac->ndev, "Timedout sending HWQ message\n");
>> +		return -EINVAL;
> 
> Using iopoll.h will fix this, but -ETIMEDOUT, not -EINVAL.
> 

-ETIMEDOUT is actually a better suited error code here, I will change
-EINVAL to -ETIMEDOUT in this if check.

>       Andrew
> 

-- 
Thanks and Regards,
Danish

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [RFC PATCH net-next 4/4] net: ti: icssg_prueth: add TAPRIO offload support
  2023-09-04 14:12   ` Andrew Lunn
@ 2023-09-05  8:37     ` MD Danish Anwar
  0 siblings, 0 replies; 22+ messages in thread
From: MD Danish Anwar @ 2023-09-05  8:37 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: Simon Horman, Roger Quadros, Vignesh Raghavendra, Jacob Keller,
	Richard Cochran, Paolo Abeni, Jakub Kicinski, Eric Dumazet,
	David S. Miller, linux-kernel, netdev, srk, r-gunasekaran

On 04/09/23 19:42, Andrew Lunn wrote:
>> +void icssg_qos_init(struct net_device *ndev)
>> +{
>> +	icssg_qos_tas_init(ndev);
>> +
>> +	/* IET init goes here */
>> +}
> 
> This sees like a TODO?
> 
>      Andrew

It's actually a TODO only. IET support will come in later patches. I
will drop this comment from here.

-- 
Thanks and Regards,
Danish

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [RFC PATCH net-next 3/4] net: ti: icssg-prueth: Add support for ICSSG switch firmware on AM654 PG2.0 EVM
  2023-09-04 14:08   ` Andrew Lunn
@ 2023-09-05  8:43     ` MD Danish Anwar
  2023-09-08  7:46       ` Roger Quadros
  0 siblings, 1 reply; 22+ messages in thread
From: MD Danish Anwar @ 2023-09-05  8:43 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: Simon Horman, Roger Quadros, Vignesh Raghavendra, Jacob Keller,
	Richard Cochran, Paolo Abeni, Jakub Kicinski, Eric Dumazet,
	David S. Miller, linux-kernel, netdev, srk, r-gunasekaran

On 04/09/23 19:38, Andrew Lunn wrote:
>> Switch mode requires loading of new firmware into ICSSG cores. This
>> means interfaces have to taken down and then reconfigured to switch mode
>> using devlink.
> 
> Can you always run it in switch mode, just not have the ports in a
> bridge?
> 
> 	Andrew

No, we can't always run it in switch mode. Switch mode requires loading
of different firmware. The switch firmware only supports switch
operations. If the ports are not in a bridge in switch mode, the normal
functionalities will not work. We will not be able to send / receive /
forward packets in switch mode without bridge.

When device is booted up, the dual EMAC firmware is loaded and ICSSG
works in dual EMAC mode with both ports doing independent TX / RX.

When switch mode is enabled, dual EMAC firmware is unloaded and switch
firmware is loaded. The ports become part of the bridge and the two port
together acts as a switch.

-- 
Thanks and Regards,
Danish

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [RFC PATCH net-next 1/4] net: ti: icssg-prueth: Add helper functions to configure FDB
  2023-09-05  8:36     ` MD Danish Anwar
@ 2023-09-07 11:57       ` Roger Quadros
  2023-09-08  5:30         ` [EXTERNAL] " MD Danish Anwar
  0 siblings, 1 reply; 22+ messages in thread
From: Roger Quadros @ 2023-09-07 11:57 UTC (permalink / raw)
  To: MD Danish Anwar, Andrew Lunn
  Cc: Simon Horman, Vignesh Raghavendra, Jacob Keller, Richard Cochran,
	Paolo Abeni, Jakub Kicinski, Eric Dumazet, David S. Miller,
	linux-kernel, netdev, srk, r-gunasekaran



On 05/09/2023 11:36, MD Danish Anwar wrote:
> Hi Andrew
> 
> On 04/09/23 19:32, Andrew Lunn wrote:
>>> +int icssg_send_fdb_msg(struct prueth_emac *emac, struct mgmt_cmd *cmd,
>>> +		       struct mgmt_cmd_rsp *rsp)
>>> +{
>>> +	struct prueth *prueth = emac->prueth;
>>> +	int slice = prueth_emac_slice(emac);
>>> +	int i = 10000;
>>> +	int addr;
>>> +
>>> +	addr = icssg_queue_pop(prueth, slice == 0 ?
>>> +			       ICSSG_CMD_POP_SLICE0 : ICSSG_CMD_POP_SLICE1);
>>> +	if (addr < 0)
>>> +		return addr;
>>> +
>>> +	/* First 4 bytes have FW owned buffer linking info which should
>>> +	 * not be touched
>>> +	 */
>>> +	memcpy_toio(prueth->shram.va + addr + 4, cmd, sizeof(*cmd));
>>> +	icssg_queue_push(prueth, slice == 0 ?
>>> +			 ICSSG_CMD_PUSH_SLICE0 : ICSSG_CMD_PUSH_SLICE1, addr);
>>> +	while (i--) {
>>> +		addr = icssg_queue_pop(prueth, slice == 0 ?
>>> +				       ICSSG_RSP_POP_SLICE0 : ICSSG_RSP_POP_SLICE1);
>>> +		if (addr < 0) {
>>> +			usleep_range(1000, 2000);
>>> +			continue;
>>> +		}
>>
>> Please try to make use of include/linux/iopoll.h.
>>
> 
> I don't think APIs from iopoll.h will be useful here.
> readl_poll_timeout() periodically polls an address until a condition is
> met or a timeout occurs. It takes address, condition as argument and
> store the value read from the address in val.

You need to use read_poll_timeout() and provide the read function as
first argument 'op'. The arguments to the read function can be passed as is
at the end. Please read description of read_poll_timeout()

> 
> Here in our use case we need to continuously read the value returned
> from icssg_queue_pop() and check if that is valid or not. If it's not
> valid, we keep polling until timeout happens.
> 
> icssg_queue_pop() does two read operations. It checks if the queue
> number is valid or not. Then it reads the ICSSG_QUEUE_CNT_OFFSET for
> that queue, if the value read is zero it returns inval. After that it
> reads the value from ICSSG_QUEUE_OFFSET of that queue and store it in
> 'val'. The returned value from icssg_queue_pop() is checked
> continuously, if it's an error code, we keep polling. If it's a good
> value then we call icssg_queue_push() with that value. As you can see
> from the below definition of icssg_queue_pop() we are doing two reads
> and two checks for error. I don't think this can be achieved by using
> APIs in iopoll.h. readl_poll_timeout() reads from a single address
> directly but we don't ave a single address that we can pass to
> readl_poll_timeout() as an argument as we have to do two reads from two
> different addresses during each poll.
> 
> So I don't think we can use iopoll.h here. Please let me know if this
> looks ok to you or if there is any other way we can use iopoll.h here
> 
> int icssg_queue_pop(struct prueth *prueth, u8 queue)
> {
>     u32 val, cnt;
> 
>     if (queue >= ICSSG_QUEUES_MAX)
> 	return -EINVAL;
> 
>     regmap_read(prueth->miig_rt, ICSSG_QUEUE_CNT_OFFSET + 4*queue,&cnt);
>     if (!cnt)
> 	return -EINVAL;
> 
>     regmap_read(prueth->miig_rt, ICSSG_QUEUE_OFFSET + 4 * queue, &val);
> 
>     return val;
> }
> 
>>> +	if (i <= 0) {
>>> +		netdev_err(emac->ndev, "Timedout sending HWQ message\n");
>>> +		return -EINVAL;
>>
>> Using iopoll.h will fix this, but -ETIMEDOUT, not -EINVAL.
>>
> 
> -ETIMEDOUT is actually a better suited error code here, I will change
> -EINVAL to -ETIMEDOUT in this if check.
> 
>>       Andrew
>>
> 

-- 
cheers,
-roger

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [RFC PATCH net-next 4/4] net: ti: icssg_prueth: add TAPRIO offload support
  2023-08-30 11:08 ` [RFC PATCH net-next 4/4] net: ti: icssg_prueth: add TAPRIO offload support MD Danish Anwar
  2023-09-04 14:12   ` Andrew Lunn
@ 2023-09-07 12:23   ` Roger Quadros
  2023-09-08  5:31     ` [EXTERNAL] " MD Danish Anwar
  1 sibling, 1 reply; 22+ messages in thread
From: Roger Quadros @ 2023-09-07 12:23 UTC (permalink / raw)
  To: MD Danish Anwar, Simon Horman, Vignesh Raghavendra, Jacob Keller,
	Andrew Lunn, Richard Cochran, Paolo Abeni, Jakub Kicinski,
	Eric Dumazet, David S. Miller
  Cc: linux-kernel, netdev, srk, r-gunasekaran



On 30/08/2023 14:08, MD Danish Anwar wrote:
> From: Roger Quadros <rogerq@kernel.org>
> 
> ICSSG dual-emac f/w supports Enhanced Scheduled Traffic (EST – defined
> in P802.1Qbv/D2.2 that later got included in IEEE 802.1Q-2018)
> configuration. EST allows express queue traffic to be scheduled
> (placed) on the wire at specific repeatable time intervals. In
> Linux kernel, EST configuration is done through tc command and
> the taprio scheduler in the net core implements a software only
> scheduler (SCH_TAPRIO). If the NIC is capable of EST configuration,
> user indicate "flag 2" in the command which is then parsed by
> taprio scheduler in net core and indicate that the command is to
> be offloaded to h/w. taprio then offloads the command to the
> driver by calling ndo_setup_tc() ndo ops. This patch implements
> ndo_setup_tc() to offload EST configuration to ICSSG.
> 
> Signed-off-by: Roger Quadros <rogerq@ti.com>
> Signed-off-by: Vignesh Raghavendra <vigneshr@ti.com>
> Signed-off-by: MD Danish Anwar <danishanwar@ti.com>
> ---
>  drivers/net/ethernet/ti/Makefile             |   3 +-
>  drivers/net/ethernet/ti/icssg/icssg_prueth.c |   5 +-
>  drivers/net/ethernet/ti/icssg/icssg_prueth.h |   7 +
>  drivers/net/ethernet/ti/icssg/icssg_qos.c    | 294 +++++++++++++++++++
>  drivers/net/ethernet/ti/icssg/icssg_qos.h    | 119 ++++++++
>  5 files changed, 426 insertions(+), 2 deletions(-)
>  create mode 100644 drivers/net/ethernet/ti/icssg/icssg_qos.c
>  create mode 100644 drivers/net/ethernet/ti/icssg/icssg_qos.h
> 
> diff --git a/drivers/net/ethernet/ti/Makefile b/drivers/net/ethernet/ti/Makefile
> index 3adceff760ce..de348c20eff9 100644
> --- a/drivers/net/ethernet/ti/Makefile
> +++ b/drivers/net/ethernet/ti/Makefile
> @@ -38,5 +38,6 @@ icssg-prueth-y := k3-cppi-desc-pool.o \
>  		  icssg/icssg_mii_cfg.o \
>  		  icssg/icssg_stats.o \
>  		  icssg/icssg_ethtool.o \
> -		  icssg/icssg_switchdev.o
> +		  icssg/icssg_switchdev.o \
> +		  icssg/icssg_qos.o
>  obj-$(CONFIG_TI_ICSS_IEP) += icssg/icss_iep.o
> diff --git a/drivers/net/ethernet/ti/icssg/icssg_prueth.c b/drivers/net/ethernet/ti/icssg/icssg_prueth.c
> index 5b7e7297ce23..3236af45aa4e 100644
> --- a/drivers/net/ethernet/ti/icssg/icssg_prueth.c
> +++ b/drivers/net/ethernet/ti/icssg/icssg_prueth.c
> @@ -1179,7 +1179,7 @@ static int emac_phy_connect(struct prueth_emac *emac)
>  	return 0;
>  }
>  
> -static u64 prueth_iep_gettime(void *clockops_data, struct ptp_system_timestamp *sts)
> +u64 prueth_iep_gettime(void *clockops_data, struct ptp_system_timestamp *sts)
>  {
>  	u32 hi_rollover_count, hi_rollover_count_r;
>  	struct prueth_emac *emac = clockops_data;
> @@ -1416,6 +1416,8 @@ static int emac_ndo_open(struct net_device *ndev)
>  		napi_enable(&emac->tx_chns[i].napi_tx);
>  	napi_enable(&emac->napi_rx);
>  
> +	icssg_qos_init(ndev);
> +
>  	/* start PHY */
>  	phy_start(ndev->phydev);
>  
> @@ -1695,6 +1697,7 @@ static const struct net_device_ops emac_netdev_ops = {
>  	.ndo_set_rx_mode = emac_ndo_set_rx_mode,
>  	.ndo_eth_ioctl = emac_ndo_ioctl,
>  	.ndo_get_stats64 = emac_ndo_get_stats64,
> +	.ndo_setup_tc = icssg_qos_ndo_setup_tc,
>  };
>  
>  /* get emac_port corresponding to eth_node name */
> diff --git a/drivers/net/ethernet/ti/icssg/icssg_prueth.h b/drivers/net/ethernet/ti/icssg/icssg_prueth.h
> index 6e18da06c786..43b67213d8c7 100644
> --- a/drivers/net/ethernet/ti/icssg/icssg_prueth.h
> +++ b/drivers/net/ethernet/ti/icssg/icssg_prueth.h
> @@ -37,6 +37,7 @@
>  #include "icssg_config.h"
>  #include "icss_iep.h"
>  #include "icssg_switch_map.h"
> +#include "icssg_qos.h"
>  
>  #define PRUETH_MAX_MTU          (2000 - ETH_HLEN - ETH_FCS_LEN)
>  #define PRUETH_MIN_PKT_SIZE     (VLAN_ETH_ZLEN)
> @@ -186,6 +187,9 @@ struct prueth_emac {
>  	struct devlink_port devlink_port;
>  	int port_vlan;
>  
> +	struct prueth_qos qos;
> +	struct work_struct ts_work;
> +
>  	struct delayed_work stats_work;
>  	u64 stats[ICSSG_NUM_STATS];
>  };
> @@ -331,4 +335,7 @@ void icssg_set_pvid(struct prueth *prueth, u8 vid, u8 port);
>  void emac_stats_work_handler(struct work_struct *work);
>  void emac_update_hardware_stats(struct prueth_emac *emac);
>  int emac_get_stat_by_name(struct prueth_emac *emac, char *stat_name);
> +
> +u64 prueth_iep_gettime(void *clockops_data, struct ptp_system_timestamp *sts);
> +
>  #endif /* __NET_TI_ICSSG_PRUETH_H */
> diff --git a/drivers/net/ethernet/ti/icssg/icssg_qos.c b/drivers/net/ethernet/ti/icssg/icssg_qos.c
> new file mode 100644
> index 000000000000..e8102703e257
> --- /dev/null
> +++ b/drivers/net/ethernet/ti/icssg/icssg_qos.c
> @@ -0,0 +1,294 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/* Texas Instruments ICSSG PRUETH QoS submodule
> + * Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com/

2023 here and rest of series.

> + */
> +
> +#include <linux/printk.h>
> +#include "icssg_prueth.h"
> +#include "icssg_switch_map.h"
> +
> +static void icssg_qos_tas_init(struct net_device *ndev);
> +
> +void icssg_qos_init(struct net_device *ndev)
> +{
> +	icssg_qos_tas_init(ndev);
> +
> +	/* IET init goes here */

Please drop this comment.

> +}
> +

<snip>

-- 
cheers,
-roger

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [EXTERNAL] Re: [RFC PATCH net-next 1/4] net: ti: icssg-prueth: Add helper functions to configure FDB
  2023-09-07 11:57       ` Roger Quadros
@ 2023-09-08  5:30         ` MD Danish Anwar
  0 siblings, 0 replies; 22+ messages in thread
From: MD Danish Anwar @ 2023-09-08  5:30 UTC (permalink / raw)
  To: Roger Quadros, Andrew Lunn
  Cc: Simon Horman, Vignesh Raghavendra, Jacob Keller, Richard Cochran,
	Paolo Abeni, Jakub Kicinski, Eric Dumazet, David S. Miller,
	linux-kernel, netdev, srk, r-gunasekaran

On 07/09/23 17:27, Roger Quadros wrote:
> 
> 
> On 05/09/2023 11:36, MD Danish Anwar wrote:
>> Hi Andrew
>>
>> On 04/09/23 19:32, Andrew Lunn wrote:
>>>> +int icssg_send_fdb_msg(struct prueth_emac *emac, struct mgmt_cmd *cmd,
>>>> +		       struct mgmt_cmd_rsp *rsp)
>>>> +{
>>>> +	struct prueth *prueth = emac->prueth;
>>>> +	int slice = prueth_emac_slice(emac);
>>>> +	int i = 10000;
>>>> +	int addr;
>>>> +
>>>> +	addr = icssg_queue_pop(prueth, slice == 0 ?
>>>> +			       ICSSG_CMD_POP_SLICE0 : ICSSG_CMD_POP_SLICE1);
>>>> +	if (addr < 0)
>>>> +		return addr;
>>>> +
>>>> +	/* First 4 bytes have FW owned buffer linking info which should
>>>> +	 * not be touched
>>>> +	 */
>>>> +	memcpy_toio(prueth->shram.va + addr + 4, cmd, sizeof(*cmd));
>>>> +	icssg_queue_push(prueth, slice == 0 ?
>>>> +			 ICSSG_CMD_PUSH_SLICE0 : ICSSG_CMD_PUSH_SLICE1, addr);
>>>> +	while (i--) {
>>>> +		addr = icssg_queue_pop(prueth, slice == 0 ?
>>>> +				       ICSSG_RSP_POP_SLICE0 : ICSSG_RSP_POP_SLICE1);
>>>> +		if (addr < 0) {
>>>> +			usleep_range(1000, 2000);
>>>> +			continue;
>>>> +		}
>>>
>>> Please try to make use of include/linux/iopoll.h.
>>>
>>
>> I don't think APIs from iopoll.h will be useful here.
>> readl_poll_timeout() periodically polls an address until a condition is
>> met or a timeout occurs. It takes address, condition as argument and
>> store the value read from the address in val.
> 
> You need to use read_poll_timeout() and provide the read function as
> first argument 'op'. The arguments to the read function can be passed as is
> at the end. Please read description of read_poll_timeout()
> 

I was only looking into real/b/w_poll_timeout() as it is mentioned in
iopoll.h to not use read_poll_timeout() directly. Anyways, I will use
read_poll_timeout() here with icssg_pop_queue() as a read API.

>>
>> Here in our use case we need to continuously read the value returned
>> from icssg_queue_pop() and check if that is valid or not. If it's not
>> valid, we keep polling until timeout happens.
>>
>> icssg_queue_pop() does two read operations. It checks if the queue
>> number is valid or not. Then it reads the ICSSG_QUEUE_CNT_OFFSET for
>> that queue, if the value read is zero it returns inval. After that it
>> reads the value from ICSSG_QUEUE_OFFSET of that queue and store it in
>> 'val'. The returned value from icssg_queue_pop() is checked
>> continuously, if it's an error code, we keep polling. If it's a good
>> value then we call icssg_queue_push() with that value. As you can see
>> from the below definition of icssg_queue_pop() we are doing two reads
>> and two checks for error. I don't think this can be achieved by using
>> APIs in iopoll.h. readl_poll_timeout() reads from a single address
>> directly but we don't ave a single address that we can pass to
>> readl_poll_timeout() as an argument as we have to do two reads from two
>> different addresses during each poll.
>>
>> So I don't think we can use iopoll.h here. Please let me know if this
>> looks ok to you or if there is any other way we can use iopoll.h here
>>
>> int icssg_queue_pop(struct prueth *prueth, u8 queue)
>> {
>>     u32 val, cnt;
>>
>>     if (queue >= ICSSG_QUEUES_MAX)
>> 	return -EINVAL;
>>
>>     regmap_read(prueth->miig_rt, ICSSG_QUEUE_CNT_OFFSET + 4*queue,&cnt);
>>     if (!cnt)
>> 	return -EINVAL;
>>
>>     regmap_read(prueth->miig_rt, ICSSG_QUEUE_OFFSET + 4 * queue, &val);
>>
>>     return val;
>> }
>>
>>>> +	if (i <= 0) {
>>>> +		netdev_err(emac->ndev, "Timedout sending HWQ message\n");
>>>> +		return -EINVAL;
>>>
>>> Using iopoll.h will fix this, but -ETIMEDOUT, not -EINVAL.
>>>
>>
>> -ETIMEDOUT is actually a better suited error code here, I will change
>> -EINVAL to -ETIMEDOUT in this if check.
>>
>>>       Andrew
>>>
>>
> 

-- 
Thanks and Regards,
Danish

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [EXTERNAL] Re: [RFC PATCH net-next 4/4] net: ti: icssg_prueth: add TAPRIO offload support
  2023-09-07 12:23   ` Roger Quadros
@ 2023-09-08  5:31     ` MD Danish Anwar
  0 siblings, 0 replies; 22+ messages in thread
From: MD Danish Anwar @ 2023-09-08  5:31 UTC (permalink / raw)
  To: Roger Quadros, Simon Horman, Vignesh Raghavendra, Jacob Keller,
	Andrew Lunn, Richard Cochran, Paolo Abeni, Jakub Kicinski,
	Eric Dumazet, David S. Miller
  Cc: linux-kernel, netdev, srk, r-gunasekaran

On 07/09/23 17:53, Roger Quadros wrote:
> 
> 
> On 30/08/2023 14:08, MD Danish Anwar wrote:
>> From: Roger Quadros <rogerq@kernel.org>
>>
>> ICSSG dual-emac f/w supports Enhanced Scheduled Traffic (EST – defined
>> in P802.1Qbv/D2.2 that later got included in IEEE 802.1Q-2018)
>> configuration. EST allows express queue traffic to be scheduled
>> (placed) on the wire at specific repeatable time intervals. In
>> Linux kernel, EST configuration is done through tc command and
>> the taprio scheduler in the net core implements a software only
>> scheduler (SCH_TAPRIO). If the NIC is capable of EST configuration,
>> user indicate "flag 2" in the command which is then parsed by
>> taprio scheduler in net core and indicate that the command is to
>> be offloaded to h/w. taprio then offloads the command to the
>> driver by calling ndo_setup_tc() ndo ops. This patch implements
>> ndo_setup_tc() to offload EST configuration to ICSSG.
>>
>> Signed-off-by: Roger Quadros <rogerq@ti.com>
>> Signed-off-by: Vignesh Raghavendra <vigneshr@ti.com>
>> Signed-off-by: MD Danish Anwar <danishanwar@ti.com>
>> ---
>>  drivers/net/ethernet/ti/Makefile             |   3 +-
>>  drivers/net/ethernet/ti/icssg/icssg_prueth.c |   5 +-
>>  drivers/net/ethernet/ti/icssg/icssg_prueth.h |   7 +
>>  drivers/net/ethernet/ti/icssg/icssg_qos.c    | 294 +++++++++++++++++++
>>  drivers/net/ethernet/ti/icssg/icssg_qos.h    | 119 ++++++++
>>  5 files changed, 426 insertions(+), 2 deletions(-)
>>  create mode 100644 drivers/net/ethernet/ti/icssg/icssg_qos.c
>>  create mode 100644 drivers/net/ethernet/ti/icssg/icssg_qos.h
>>
>> diff --git a/drivers/net/ethernet/ti/Makefile b/drivers/net/ethernet/ti/Makefile
>> index 3adceff760ce..de348c20eff9 100644
>> --- a/drivers/net/ethernet/ti/Makefile
>> +++ b/drivers/net/ethernet/ti/Makefile
>> @@ -38,5 +38,6 @@ icssg-prueth-y := k3-cppi-desc-pool.o \
>>  		  icssg/icssg_mii_cfg.o \
>>  		  icssg/icssg_stats.o \
>>  		  icssg/icssg_ethtool.o \
>> -		  icssg/icssg_switchdev.o
>> +		  icssg/icssg_switchdev.o \
>> +		  icssg/icssg_qos.o
>>  obj-$(CONFIG_TI_ICSS_IEP) += icssg/icss_iep.o
>> diff --git a/drivers/net/ethernet/ti/icssg/icssg_prueth.c b/drivers/net/ethernet/ti/icssg/icssg_prueth.c
>> index 5b7e7297ce23..3236af45aa4e 100644
>> --- a/drivers/net/ethernet/ti/icssg/icssg_prueth.c
>> +++ b/drivers/net/ethernet/ti/icssg/icssg_prueth.c
>> @@ -1179,7 +1179,7 @@ static int emac_phy_connect(struct prueth_emac *emac)
>>  	return 0;
>>  }
>>  
>> -static u64 prueth_iep_gettime(void *clockops_data, struct ptp_system_timestamp *sts)
>> +u64 prueth_iep_gettime(void *clockops_data, struct ptp_system_timestamp *sts)
>>  {
>>  	u32 hi_rollover_count, hi_rollover_count_r;
>>  	struct prueth_emac *emac = clockops_data;
>> @@ -1416,6 +1416,8 @@ static int emac_ndo_open(struct net_device *ndev)
>>  		napi_enable(&emac->tx_chns[i].napi_tx);
>>  	napi_enable(&emac->napi_rx);
>>  
>> +	icssg_qos_init(ndev);
>> +
>>  	/* start PHY */
>>  	phy_start(ndev->phydev);
>>  
>> @@ -1695,6 +1697,7 @@ static const struct net_device_ops emac_netdev_ops = {
>>  	.ndo_set_rx_mode = emac_ndo_set_rx_mode,
>>  	.ndo_eth_ioctl = emac_ndo_ioctl,
>>  	.ndo_get_stats64 = emac_ndo_get_stats64,
>> +	.ndo_setup_tc = icssg_qos_ndo_setup_tc,
>>  };
>>  
>>  /* get emac_port corresponding to eth_node name */
>> diff --git a/drivers/net/ethernet/ti/icssg/icssg_prueth.h b/drivers/net/ethernet/ti/icssg/icssg_prueth.h
>> index 6e18da06c786..43b67213d8c7 100644
>> --- a/drivers/net/ethernet/ti/icssg/icssg_prueth.h
>> +++ b/drivers/net/ethernet/ti/icssg/icssg_prueth.h
>> @@ -37,6 +37,7 @@
>>  #include "icssg_config.h"
>>  #include "icss_iep.h"
>>  #include "icssg_switch_map.h"
>> +#include "icssg_qos.h"
>>  
>>  #define PRUETH_MAX_MTU          (2000 - ETH_HLEN - ETH_FCS_LEN)
>>  #define PRUETH_MIN_PKT_SIZE     (VLAN_ETH_ZLEN)
>> @@ -186,6 +187,9 @@ struct prueth_emac {
>>  	struct devlink_port devlink_port;
>>  	int port_vlan;
>>  
>> +	struct prueth_qos qos;
>> +	struct work_struct ts_work;
>> +
>>  	struct delayed_work stats_work;
>>  	u64 stats[ICSSG_NUM_STATS];
>>  };
>> @@ -331,4 +335,7 @@ void icssg_set_pvid(struct prueth *prueth, u8 vid, u8 port);
>>  void emac_stats_work_handler(struct work_struct *work);
>>  void emac_update_hardware_stats(struct prueth_emac *emac);
>>  int emac_get_stat_by_name(struct prueth_emac *emac, char *stat_name);
>> +
>> +u64 prueth_iep_gettime(void *clockops_data, struct ptp_system_timestamp *sts);
>> +
>>  #endif /* __NET_TI_ICSSG_PRUETH_H */
>> diff --git a/drivers/net/ethernet/ti/icssg/icssg_qos.c b/drivers/net/ethernet/ti/icssg/icssg_qos.c
>> new file mode 100644
>> index 000000000000..e8102703e257
>> --- /dev/null
>> +++ b/drivers/net/ethernet/ti/icssg/icssg_qos.c
>> @@ -0,0 +1,294 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/* Texas Instruments ICSSG PRUETH QoS submodule
>> + * Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com/
> 
> 2023 here and rest of series.
> 

Sure Roger.

>> + */
>> +
>> +#include <linux/printk.h>
>> +#include "icssg_prueth.h"
>> +#include "icssg_switch_map.h"
>> +
>> +static void icssg_qos_tas_init(struct net_device *ndev);
>> +
>> +void icssg_qos_init(struct net_device *ndev)
>> +{
>> +	icssg_qos_tas_init(ndev);
>> +
>> +	/* IET init goes here */
> 
> Please drop this comment.
> 

Will drop this and post next revision.

>> +}
>> +
> 
> <snip>
> 

-- 
Thanks and Regards,
Danish

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [RFC PATCH net-next 3/4] net: ti: icssg-prueth: Add support for ICSSG switch firmware on AM654 PG2.0 EVM
  2023-09-05  8:43     ` MD Danish Anwar
@ 2023-09-08  7:46       ` Roger Quadros
  2023-09-08  8:17         ` MD Danish Anwar
  0 siblings, 1 reply; 22+ messages in thread
From: Roger Quadros @ 2023-09-08  7:46 UTC (permalink / raw)
  To: MD Danish Anwar, Andrew Lunn
  Cc: Simon Horman, Vignesh Raghavendra, Jacob Keller, Richard Cochran,
	Paolo Abeni, Jakub Kicinski, Eric Dumazet, David S. Miller,
	linux-kernel, netdev, srk, r-gunasekaran, Pekka Varis



On 05/09/2023 11:43, MD Danish Anwar wrote:
> On 04/09/23 19:38, Andrew Lunn wrote:
>>> Switch mode requires loading of new firmware into ICSSG cores. This
>>> means interfaces have to taken down and then reconfigured to switch mode
>>> using devlink.
>>
>> Can you always run it in switch mode, just not have the ports in a
>> bridge?
>>
>> 	Andrew
> 
> No, we can't always run it in switch mode. Switch mode requires loading
> of different firmware. The switch firmware only supports switch
> operations. If the ports are not in a bridge in switch mode, the normal
> functionalities will not work. We will not be able to send / receive /
> forward packets in switch mode without bridge.
> 
> When device is booted up, the dual EMAC firmware is loaded and ICSSG
> works in dual EMAC mode with both ports doing independent TX / RX.
> 
> When switch mode is enabled, dual EMAC firmware is unloaded and switch
> firmware is loaded. The ports become part of the bridge and the two port
> together acts as a switch.
> 

Since we are loading the switch firmware and the switch logic is in firmware,
it means we don't really need Linux help to do basic switching on the external
ports.

I suppose Andrews question was, can it work as a switch after switching
from dual-emac to switch mode and not setting up the Linux bridge.

e.g. Looking at your command list

> Switch to ICSSG Switch mode:
>  ip link set dev eth1 down
>  ip link set dev eth2 down
>  devlink dev param set platform/icssg2-eth name \
>  switch_mode value 1 cmode runtime

At this point, can it work as a switch. If not, why?

>  ip link add name br0 type bridge
>  ip link set dev eth1 master br0
>  ip link set dev eth2 master br0
>  ip link set dev br0 up
>  ip link set dev eth1 up
>  ip link set dev eth2 up
>  bridge vlan add dev br0 vid 1 pvid untagged self

-- 
cheers,
-roger

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [RFC PATCH net-next 3/4] net: ti: icssg-prueth: Add support for ICSSG switch firmware on AM654 PG2.0 EVM
  2023-09-08  7:46       ` Roger Quadros
@ 2023-09-08  8:17         ` MD Danish Anwar
  2023-09-13  6:44           ` MD Danish Anwar
  0 siblings, 1 reply; 22+ messages in thread
From: MD Danish Anwar @ 2023-09-08  8:17 UTC (permalink / raw)
  To: Roger Quadros, Andrew Lunn
  Cc: Simon Horman, Vignesh Raghavendra, Jacob Keller, Richard Cochran,
	Paolo Abeni, Jakub Kicinski, Eric Dumazet, David S. Miller,
	linux-kernel, netdev, srk, r-gunasekaran, Pekka Varis

On 08/09/23 13:16, Roger Quadros wrote:
> 
> 
> On 05/09/2023 11:43, MD Danish Anwar wrote:
>> On 04/09/23 19:38, Andrew Lunn wrote:
>>>> Switch mode requires loading of new firmware into ICSSG cores. This
>>>> means interfaces have to taken down and then reconfigured to switch mode
>>>> using devlink.
>>>
>>> Can you always run it in switch mode, just not have the ports in a
>>> bridge?
>>>
>>> 	Andrew
>>
>> No, we can't always run it in switch mode. Switch mode requires loading
>> of different firmware. The switch firmware only supports switch
>> operations. If the ports are not in a bridge in switch mode, the normal
>> functionalities will not work. We will not be able to send / receive /
>> forward packets in switch mode without bridge.
>>
>> When device is booted up, the dual EMAC firmware is loaded and ICSSG
>> works in dual EMAC mode with both ports doing independent TX / RX.
>>
>> When switch mode is enabled, dual EMAC firmware is unloaded and switch
>> firmware is loaded. The ports become part of the bridge and the two port
>> together acts as a switch.
>>
> 
> Since we are loading the switch firmware and the switch logic is in firmware,
> it means we don't really need Linux help to do basic switching on the external
> ports.
> 
> I suppose Andrews question was, can it work as a switch after switching
> from dual-emac to switch mode and not setting up the Linux bridge.
> 

I did some further testing on switch mode. The basic functionality would
work without a bridge as well. This will need one modification in driver
but even without bridge switching will work.

When enabling switch mode the driver sets the HOST_MAC_ADDR to the
bridge's addr. If bridge is not there, this will result in KERNEL NULL
POINTER crash.

icssg_class_set_host_mac_addr(prueth->miig_rt,
prueth->hw_bridge_dev->dev_addr);

However if we change this to only set when bridge is there, it works

if (prueth->hw_bridge_dev)
	icssg_class_set_host_mac_addr(prueth->miig_rt,
prueth->hw_bridge_dev->dev_addr);

With this change forwarding works in switch mode without setting up the
bridge. Just loading the switch firmware is enough.


> e.g. Looking at your command list
> 
>> Switch to ICSSG Switch mode:
>>  ip link set dev eth1 down
>>  ip link set dev eth2 down
>>  devlink dev param set platform/icssg2-eth name \
>>  switch_mode value 1 cmode runtime
> 
> At this point, can it work as a switch. If not, why?>

To summarize, yes it can work at this point.

>>  ip link add name br0 type bridge
>>  ip link set dev eth1 master br0
>>  ip link set dev eth2 master br0
>>  ip link set dev br0 up
>>  ip link set dev eth1 up
>>  ip link set dev eth2 up
>>  bridge vlan add dev br0 vid 1 pvid untagged self
> 

-- 
Thanks and Regards,
Danish

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [RFC PATCH net-next 3/4] net: ti: icssg-prueth: Add support for ICSSG switch firmware on AM654 PG2.0 EVM
  2023-09-08  8:17         ` MD Danish Anwar
@ 2023-09-13  6:44           ` MD Danish Anwar
  2023-09-13 12:19             ` Andrew Lunn
  0 siblings, 1 reply; 22+ messages in thread
From: MD Danish Anwar @ 2023-09-13  6:44 UTC (permalink / raw)
  To: Roger Quadros, Andrew Lunn
  Cc: Simon Horman, Vignesh Raghavendra, Jacob Keller, Richard Cochran,
	Paolo Abeni, Jakub Kicinski, Eric Dumazet, David S. Miller,
	linux-kernel, netdev, srk, r-gunasekaran, Pekka Varis

Hi Roger / Andrew,

On 08/09/23 13:47, MD Danish Anwar wrote:
> On 08/09/23 13:16, Roger Quadros wrote:
>>
>>
>> On 05/09/2023 11:43, MD Danish Anwar wrote:
>>> On 04/09/23 19:38, Andrew Lunn wrote:
>>>>> Switch mode requires loading of new firmware into ICSSG cores. This
>>>>> means interfaces have to taken down and then reconfigured to switch mode
>>>>> using devlink.
>>>>
>>>> Can you always run it in switch mode, just not have the ports in a
>>>> bridge?
>>>>
>>>> 	Andrew
>>>
>>> No, we can't always run it in switch mode. Switch mode requires loading
>>> of different firmware. The switch firmware only supports switch
>>> operations. If the ports are not in a bridge in switch mode, the normal
>>> functionalities will not work. We will not be able to send / receive /
>>> forward packets in switch mode without bridge.
>>>
>>> When device is booted up, the dual EMAC firmware is loaded and ICSSG
>>> works in dual EMAC mode with both ports doing independent TX / RX.
>>>
>>> When switch mode is enabled, dual EMAC firmware is unloaded and switch
>>> firmware is loaded. The ports become part of the bridge and the two port
>>> together acts as a switch.
>>>
>>
>> Since we are loading the switch firmware and the switch logic is in firmware,
>> it means we don't really need Linux help to do basic switching on the external
>> ports.
>>
>> I suppose Andrews question was, can it work as a switch after switching
>> from dual-emac to switch mode and not setting up the Linux bridge.
>>
> 
> I did some further testing on switch mode. The basic functionality would
> work without a bridge as well. This will need one modification in driver
> but even without bridge switching will work.
> 
> When enabling switch mode the driver sets the HOST_MAC_ADDR to the
> bridge's addr. If bridge is not there, this will result in KERNEL NULL
> POINTER crash.
> 
> icssg_class_set_host_mac_addr(prueth->miig_rt,
> prueth->hw_bridge_dev->dev_addr);
> 
> However if we change this to only set when bridge is there, it works
> 
> if (prueth->hw_bridge_dev)
> 	icssg_class_set_host_mac_addr(prueth->miig_rt,
> prueth->hw_bridge_dev->dev_addr);
> 
> With this change forwarding works in switch mode without setting up the
> bridge. Just loading the switch firmware is enough.
> 
> 
>> e.g. Looking at your command list
>>
>>> Switch to ICSSG Switch mode:
>>>  ip link set dev eth1 down
>>>  ip link set dev eth2 down
>>>  devlink dev param set platform/icssg2-eth name \
>>>  switch_mode value 1 cmode runtime
>>
>> At this point, can it work as a switch. If not, why?>
> 
> To summarize, yes it can work at this point.
> 

As discussed on this thread, switching operation can work with the ICSSG
switch firmware, without creating bridge. However without bridge only
forwarding works. If we want the switch to consume packets bridge is
required.

ICSSG switch firmware without bridge
  - Forwarding works but packets can not be consumed by switch.

ICSSG switch firmware without bridge
  - Forwarding works and packets can be consumed by switch.

In order to consume the packets, creating a bridge is required.

I will keep the commands in commit message as it is. Please let me know
if this is OK to you or if any change is required.

>>>  ip link add name br0 type bridge
>>>  ip link set dev eth1 master br0
>>>  ip link set dev eth2 master br0
>>>  ip link set dev br0 up
>>>  ip link set dev eth1 up
>>>  ip link set dev eth2 up
>>>  bridge vlan add dev br0 vid 1 pvid untagged self
>>
> 

-- 
Thanks and Regards,
Danish

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [RFC PATCH net-next 3/4] net: ti: icssg-prueth: Add support for ICSSG switch firmware on AM654 PG2.0 EVM
  2023-09-13  6:44           ` MD Danish Anwar
@ 2023-09-13 12:19             ` Andrew Lunn
  2023-09-21 11:19               ` MD Danish Anwar
  0 siblings, 1 reply; 22+ messages in thread
From: Andrew Lunn @ 2023-09-13 12:19 UTC (permalink / raw)
  To: MD Danish Anwar
  Cc: Roger Quadros, Simon Horman, Vignesh Raghavendra, Jacob Keller,
	Richard Cochran, Paolo Abeni, Jakub Kicinski, Eric Dumazet,
	David S. Miller, linux-kernel, netdev, srk, r-gunasekaran,
	Pekka Varis

> As discussed on this thread, switching operation can work with the ICSSG
> switch firmware, without creating bridge. However without bridge only
> forwarding works. If we want the switch to consume packets bridge is
> required.

What packets will the switch consume? The only packets i can think of
are pause frames. Everything else get passed to the CPU.

You also need to think of what happens when a single switch port is
added to the bridge, and an external port, like a tun/tap device for a
VPN is added to the bridge.

For most switches, a port not being a member of a switch means the
port is pretty dumb and every frame is forwarded to the CPU. There are
however some switches which perform address learning as usual,
learning if an address is on the port, or on the CPU. Maybe you can
see if that is possible.

It might be you need your firmware people involved to produce a new
firmware version which combines both firmwares in one.

	 Andrew

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [RFC PATCH net-next 3/4] net: ti: icssg-prueth: Add support for ICSSG switch firmware on AM654 PG2.0 EVM
  2023-09-13 12:19             ` Andrew Lunn
@ 2023-09-21 11:19               ` MD Danish Anwar
  2023-09-21 13:37                 ` Andrew Lunn
  0 siblings, 1 reply; 22+ messages in thread
From: MD Danish Anwar @ 2023-09-21 11:19 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: Roger Quadros, Simon Horman, Vignesh Raghavendra, Jacob Keller,
	Richard Cochran, Paolo Abeni, Jakub Kicinski, Eric Dumazet,
	David S. Miller, linux-kernel, netdev, srk, r-gunasekaran,
	Pekka Varis

Hi Andrew,

On 13/09/23 17:49, Andrew Lunn wrote:
>> As discussed on this thread, switching operation can work with the ICSSG
>> switch firmware, without creating bridge. However without bridge only
>> forwarding works. If we want the switch to consume packets bridge is
>> required.
> 
> What packets will the switch consume? The only packets i can think of
> are pause frames. Everything else get passed to the CPU.
> 
> You also need to think of what happens when a single switch port is
> added to the bridge, and an external port, like a tun/tap device for a
> VPN is added to the bridge.
> 
> For most switches, a port not being a member of a switch means the
> port is pretty dumb and every frame is forwarded to the CPU. There are
> however some switches which perform address learning as usual,
> learning if an address is on the port, or on the CPU. Maybe you can
> see if that is possible.
> 
> It might be you need your firmware people involved to produce a new
> firmware version which combines both firmwares in one.
> 

Thanks for the offline discussion and explanations. As discussed, we can
not have one combined firmware to do both switch operations and dual
emac operations. It is required to have two different firmwares.
Currently which firmware to load is decided by flag 'is_switch_mode'
which is set / unset by devlink. I will not use devlink here as asked by
you. Instead, I'll use the approach suggested by you.
ndo_open() will load the dual mac firmware. I'll swap to switch firmware
when the second port is added to the same bridge as the first port.

I will re-work the changes and post v2 soon.

> 	 Andrew

-- 
Thanks and Regards,
Danish

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [RFC PATCH net-next 3/4] net: ti: icssg-prueth: Add support for ICSSG switch firmware on AM654 PG2.0 EVM
  2023-09-21 11:19               ` MD Danish Anwar
@ 2023-09-21 13:37                 ` Andrew Lunn
  2023-09-22  7:04                   ` MD Danish Anwar
  0 siblings, 1 reply; 22+ messages in thread
From: Andrew Lunn @ 2023-09-21 13:37 UTC (permalink / raw)
  To: MD Danish Anwar
  Cc: Roger Quadros, Simon Horman, Vignesh Raghavendra, Jacob Keller,
	Richard Cochran, Paolo Abeni, Jakub Kicinski, Eric Dumazet,
	David S. Miller, linux-kernel, netdev, srk, r-gunasekaran,
	Pekka Varis

> Thanks for the offline discussion and explanations. As discussed, we can
> not have one combined firmware to do both switch operations and dual
> emac operations. It is required to have two different firmwares.
> Currently which firmware to load is decided by flag 'is_switch_mode'
> which is set / unset by devlink. I will not use devlink here as asked by
> you. Instead, I'll use the approach suggested by you.
> ndo_open() will load the dual mac firmware. I'll swap to switch firmware
> when the second port is added to the same bridge as the first port.
> 
> I will re-work the changes and post v2 soon.

I'm sceptical you can actually make this work correctly, but lets see
what v2 contains.

     Andrew

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [RFC PATCH net-next 3/4] net: ti: icssg-prueth: Add support for ICSSG switch firmware on AM654 PG2.0 EVM
  2023-09-21 13:37                 ` Andrew Lunn
@ 2023-09-22  7:04                   ` MD Danish Anwar
  0 siblings, 0 replies; 22+ messages in thread
From: MD Danish Anwar @ 2023-09-22  7:04 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: Roger Quadros, Simon Horman, Vignesh Raghavendra, Jacob Keller,
	Richard Cochran, Paolo Abeni, Jakub Kicinski, Eric Dumazet,
	David S. Miller, linux-kernel, netdev, srk, r-gunasekaran,
	Pekka Varis

On 21/09/23 19:07, Andrew Lunn wrote:
>> Thanks for the offline discussion and explanations. As discussed, we can
>> not have one combined firmware to do both switch operations and dual
>> emac operations. It is required to have two different firmwares.
>> Currently which firmware to load is decided by flag 'is_switch_mode'
>> which is set / unset by devlink. I will not use devlink here as asked by
>> you. Instead, I'll use the approach suggested by you.
>> ndo_open() will load the dual mac firmware. I'll swap to switch firmware
>> when the second port is added to the same bridge as the first port.
>>
>> I will re-work the changes and post v2 soon.
> 
> I'm sceptical you can actually make this work correctly, but lets see
> what v2 contains.
> 

Sure. I will try to make this work and post v2 once it's ready.

>      Andrew

-- 
Thanks and Regards,
Danish

^ permalink raw reply	[flat|nested] 22+ messages in thread

end of thread, other threads:[~2023-09-22  7:05 UTC | newest]

Thread overview: 22+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-08-30 11:08 [RFC PATCH net-next 0/4] Introduce switch mode and TAPRIO offload support for ICSSG driver MD Danish Anwar
2023-08-30 11:08 ` [RFC PATCH net-next 1/4] net: ti: icssg-prueth: Add helper functions to configure FDB MD Danish Anwar
2023-09-04 14:02   ` Andrew Lunn
2023-09-05  8:36     ` MD Danish Anwar
2023-09-07 11:57       ` Roger Quadros
2023-09-08  5:30         ` [EXTERNAL] " MD Danish Anwar
2023-08-30 11:08 ` [RFC PATCH net-next 2/4] net: ti: icssg-switch: Add switchdev based driver for ethernet switch support MD Danish Anwar
2023-08-30 11:08 ` [RFC PATCH net-next 3/4] net: ti: icssg-prueth: Add support for ICSSG switch firmware on AM654 PG2.0 EVM MD Danish Anwar
2023-09-04 14:08   ` Andrew Lunn
2023-09-05  8:43     ` MD Danish Anwar
2023-09-08  7:46       ` Roger Quadros
2023-09-08  8:17         ` MD Danish Anwar
2023-09-13  6:44           ` MD Danish Anwar
2023-09-13 12:19             ` Andrew Lunn
2023-09-21 11:19               ` MD Danish Anwar
2023-09-21 13:37                 ` Andrew Lunn
2023-09-22  7:04                   ` MD Danish Anwar
2023-08-30 11:08 ` [RFC PATCH net-next 4/4] net: ti: icssg_prueth: add TAPRIO offload support MD Danish Anwar
2023-09-04 14:12   ` Andrew Lunn
2023-09-05  8:37     ` MD Danish Anwar
2023-09-07 12:23   ` Roger Quadros
2023-09-08  5:31     ` [EXTERNAL] " MD Danish Anwar

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).