Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH net-next v2] net: airoha: add ethtool priv_flags support for LAN/WAN and GDM2 loopback
@ 2026-06-07 17:19 Lorenzo Bianconi
  2026-06-07 17:59 ` Andrew Lunn
  0 siblings, 1 reply; 4+ messages in thread
From: Lorenzo Bianconi @ 2026-06-07 17:19 UTC (permalink / raw)
  To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Lorenzo Bianconi
  Cc: linux-arm-kernel, linux-mediatek, netdev, Madhur Agrawal

Introduce ethtool private flags infrastructure for the airoha ethernet
driver, allowing userspace to configure per-device behavior via ethtool.
Implement the "wan" private flag to let the user select whether a GDM
port is used as a hardware LAN or WAN interface. GDM2 is fixed as WAN
only, GDM1 is fixed as LAN only, while GDM3 and GDM4 can be switched
between LAN and WAN at runtime (when the interface is not running).
When a GDM3/GDM4 port is set to WAN mode, enable GDM2 loopback to
support hardware QoS. Conversely, when switching back to LAN mode,
disable the GDM2 loopback and restore the default forwarding
configuration.
Add airoha_disable_gdm2_loopback() as the counterpart of the existing
airoha_enable_gdm2_loopback(), and define FC_MAP6_DEF_VALUE for use
during loopback teardown.

Example usage to configure eth1 (GDM3/GDM4) as WAN:

$ ethtool --show-priv-flags eth1
Private flags for eth1:
wan: off

$ ethtool --set-priv-flags eth1 wan on

$ ethtool --show-priv-flags eth1
Private flags for eth1:
wan: on

To revert back to LAN mode:

$ ethtool --set-priv-flags eth1 wan off

Tested-by: Madhur Agrawal <madhur.agrawal@airoha.com>
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
Changes in v2:
- Rework airoha_dev_set_wan_flag routine
- Enable GDM_STRIP_CRC_MASK in airoha_disable_gdm2_loopback()
- Do not always reset REG_SRC_PORT_FC_MAP6 in
  airoha_disable_gdm2_loopback() but use the same condition used in
  airoha_enable_gdm2_loopback().
- Link to v1: https://lore.kernel.org/r/20260606-airoha-ethtool-priv_flags-v1-1-401b2c9fe9f1@kernel.org
---
 drivers/net/ethernet/airoha/airoha_eth.c  | 173 ++++++++++++++++++++++++++++++
 drivers/net/ethernet/airoha/airoha_regs.h |   1 +
 2 files changed, 174 insertions(+)

diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
index 5a8e84fa9918..5dd160dbbbc1 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -1960,6 +1960,49 @@ static int airoha_enable_gdm2_loopback(struct airoha_gdm_dev *dev)
 	return 0;
 }
 
+static int airoha_disable_gdm2_loopback(struct airoha_gdm_dev *dev)
+{
+	struct airoha_gdm_port *port = dev->port;
+	struct airoha_eth *eth = dev->eth;
+	int i, src_port;
+	u32 pse_port;
+
+	src_port = eth->soc->ops.get_sport(dev->port, dev->nbq);
+	if (src_port < 0)
+		return src_port;
+
+	airoha_fe_clear(eth,
+			REG_SP_DFT_CPORT(src_port >> fls(SP_CPORT_DFT_MASK)),
+			SP_CPORT_MASK(src_port & SP_CPORT_DFT_MASK));
+
+	airoha_fe_set(eth, REG_GDM_FWD_CFG(AIROHA_GDM2_IDX),
+		      GDM_STRIP_CRC_MASK);
+	airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(AIROHA_GDM2_IDX),
+				    FE_PSE_PORT_DROP);
+	airoha_fe_clear(eth, REG_GDM_LPBK_CFG(AIROHA_GDM2_IDX),
+			LPBK_CHAN_MASK | LPBK_MODE_MASK | LPBK_EN_MASK);
+	pse_port = airoha_ppe_is_enabled(eth, 1) ? FE_PSE_PORT_PPE2
+						 : FE_PSE_PORT_PPE1;
+	airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(AIROHA_GDM2_IDX),
+				    pse_port);
+
+	airoha_fe_rmw(eth, REG_FE_WAN_PORT, WAN0_MASK,
+		      FIELD_PREP(WAN0_MASK, AIROHA_GDM2_IDX));
+
+	for (i = 0; i < eth->soc->num_ppe; i++)
+		airoha_fe_clear(eth, REG_PPE_DFT_CPORT(i, AIROHA_GDM2_IDX),
+				DFT_CPORT_MASK(AIROHA_GDM2_IDX));
+
+	/* Enable VIP and IFC for GDM2 */
+	airoha_fe_set(eth, REG_FE_VIP_PORT_EN, BIT(AIROHA_GDM2_IDX));
+	airoha_fe_set(eth, REG_FE_IFC_PORT_EN, BIT(AIROHA_GDM2_IDX));
+
+	if (port->id == AIROHA_GDM4_IDX && airoha_is_7581(eth))
+		airoha_fe_wr(eth, REG_SRC_PORT_FC_MAP6, FC_MAP6_DEF_VALUE);
+
+	return 0;
+}
+
 static struct airoha_gdm_dev *
 airoha_get_wan_gdm_dev(struct airoha_eth *eth)
 {
@@ -2296,6 +2339,77 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
 	return NETDEV_TX_OK;
 }
 
+struct airoha_ethool_priv_flags {
+	char name[ETH_GSTRING_LEN];
+	int (*handler)(struct net_device *netdev, u32 flags);
+};
+
+static int airoha_dev_set_wan_flag(struct net_device *netdev, u32 flags)
+{
+	struct airoha_gdm_dev *dev = netdev_priv(netdev);
+	struct airoha_gdm_port *port = dev->port;
+	struct airoha_eth *eth = dev->eth;
+	int err;
+
+	if (port->id != AIROHA_GDM3_IDX &&
+	    port->id != AIROHA_GDM4_IDX) {
+		/* GDM1 can be used just as LAN while GDM2 can be configured
+		 * only as WAN
+		 */
+		return -EOPNOTSUPP;
+	}
+
+	if (netif_running(netdev))
+		return -EBUSY;
+
+	if (flags & AIROHA_PRIV_F_WAN) {
+		struct airoha_gdm_dev *wan_dev;
+
+		/* Verify the WAN device is not already configured */
+		wan_dev = airoha_get_wan_gdm_dev(eth);
+		if (wan_dev && wan_dev != dev)
+			return -EBUSY;
+
+		dev->flags |= AIROHA_PRIV_F_WAN;
+		airoha_dev_set_qdma(dev);
+		err = airoha_enable_gdm2_loopback(dev);
+		if (err)
+			goto error;
+	} else {
+		err = airoha_disable_gdm2_loopback(dev);
+		if (err)
+			return err;
+
+		dev->flags &= ~AIROHA_PRIV_F_WAN;
+		airoha_dev_set_qdma(dev);
+	}
+
+	err = airoha_set_macaddr(dev, netdev->dev_addr);
+	if (err)
+		goto error;
+
+	return 0;
+error:
+	/* Restore previous LAN or WAN configuration */
+	if (flags & AIROHA_PRIV_F_WAN) {
+		airoha_disable_gdm2_loopback(dev);
+		dev->flags &= ~AIROHA_PRIV_F_WAN;
+		airoha_dev_set_qdma(dev);
+	} else {
+		dev->flags |= AIROHA_PRIV_F_WAN;
+		airoha_dev_set_qdma(dev);
+		airoha_enable_gdm2_loopback(dev);
+	}
+
+	return err;
+}
+
+static const struct airoha_ethool_priv_flags airoha_eth_priv_flags[] = {
+	{ "wan", airoha_dev_set_wan_flag },
+};
+
+#define AIROHA_PRIV_FLAGS_STR_LEN	ARRAY_SIZE(airoha_eth_priv_flags)
+
 static void airoha_ethtool_get_drvinfo(struct net_device *netdev,
 				       struct ethtool_drvinfo *info)
 {
@@ -2304,6 +2418,7 @@ static void airoha_ethtool_get_drvinfo(struct net_device *netdev,
 
 	strscpy(info->driver, eth->dev->driver->name, sizeof(info->driver));
 	strscpy(info->bus_info, dev_name(eth->dev), sizeof(info->bus_info));
+	info->n_priv_flags = AIROHA_PRIV_FLAGS_STR_LEN;
 }
 
 static void airoha_ethtool_get_mac_stats(struct net_device *netdev,
@@ -2368,6 +2483,60 @@ airoha_ethtool_get_rmon_stats(struct net_device *netdev,
 	} while (u64_stats_fetch_retry(&port->stats.syncp, start));
 }
 
+static int airoha_ethtool_set_priv_flags(struct net_device *netdev, u32 flags)
+{
+	struct airoha_gdm_dev *dev = netdev_priv(netdev);
+	int i;
+
+	for (i = 0; i < AIROHA_PRIV_FLAGS_STR_LEN; i++) {
+		int err;
+
+		if (!((dev->flags ^ flags) & BIT(i)))
+			continue;
+
+		if (!airoha_eth_priv_flags[i].handler)
+			continue;
+
+		err = airoha_eth_priv_flags[i].handler(netdev, flags);
+		if (err)
+			return err;
+	}
+
+	return 0;
+}
+
+static u32 airoha_ethtool_get_priv_flags(struct net_device *netdev)
+{
+	struct airoha_gdm_dev *dev = netdev_priv(netdev);
+
+	return dev->flags;
+}
+
+static int airoha_ethtool_get_sset_count(struct net_device *netdev, int sset)
+{
+	switch (sset) {
+	case ETH_SS_PRIV_FLAGS:
+		return AIROHA_PRIV_FLAGS_STR_LEN;
+	default:
+		return -EOPNOTSUPP;
+	}
+}
+
+static void airoha_ethtool_get_strings(struct net_device *netdev,
+				       u32 stringset, u8 *data)
+{
+	int i;
+
+	switch (stringset) {
+	case ETH_SS_PRIV_FLAGS:
+		for (i = 0; i < AIROHA_PRIV_FLAGS_STR_LEN; i++)
+			ethtool_puts(&data, airoha_eth_priv_flags[i].name);
+		break;
+	default:
+		break;
+	}
+}
+
 static int airoha_qdma_set_chan_tx_sched(struct net_device *netdev,
 					 int channel, enum tx_sched_mode mode,
 					 const u16 *weights, u8 n_weights)
@@ -3094,6 +3263,10 @@ static const struct ethtool_ops airoha_ethtool_ops = {
 	.get_rmon_stats		= airoha_ethtool_get_rmon_stats,
 	.get_link_ksettings	= phy_ethtool_get_link_ksettings,
 	.get_link		= ethtool_op_get_link,
+	.set_priv_flags		= airoha_ethtool_set_priv_flags,
+	.get_priv_flags		= airoha_ethtool_get_priv_flags,
+	.get_sset_count		= airoha_ethtool_get_sset_count,
+	.get_strings		= airoha_ethtool_get_strings,
 };
 
 static int airoha_metadata_dst_alloc(struct airoha_gdm_port *port)
diff --git a/drivers/net/ethernet/airoha/airoha_regs.h b/drivers/net/ethernet/airoha/airoha_regs.h
index 436f3c8779c1..4e17dfbcf2b8 100644
--- a/drivers/net/ethernet/airoha/airoha_regs.h
+++ b/drivers/net/ethernet/airoha/airoha_regs.h
@@ -376,6 +376,7 @@
 
 #define REG_SRC_PORT_FC_MAP6		0x2298
 #define FC_ID_OF_SRC_PORT_MASK(_n)	GENMASK(4 + ((_n) << 3), ((_n) << 3))
+#define FC_MAP6_DEF_VALUE		0x1b1a1918
 
 #define REG_CDM5_RX_OQ1_DROP_CNT	0x29d4
 

---
base-commit: 903db046d5579bef0ea699eae4b279dd6455fc9f
change-id: 20260606-airoha-ethtool-priv_flags-b6aa70caa780

Best regards,
-- 
Lorenzo Bianconi <lorenzo@kernel.org>



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

end of thread, other threads:[~2026-06-07 18:48 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-07 17:19 [PATCH net-next v2] net: airoha: add ethtool priv_flags support for LAN/WAN and GDM2 loopback Lorenzo Bianconi
2026-06-07 17:59 ` Andrew Lunn
2026-06-07 18:07   ` Lorenzo Bianconi
2026-06-07 18:48     ` Andrew Lunn

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox