From nobody Thu Oct 9 02:56:40 2025 Received: from mx.denx.de (mx.denx.de [89.58.32.78]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 201491C5D62; Sun, 22 Jun 2025 09:38:37 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=89.58.32.78 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750585120; cv=none; b=D3sot8TeEfcir2MckLDbHPXHTNpSQsXb+xnIgXNiZ5iaq+LDVqkKLYzudtWv04buvoFCGLKurR7GQIue2S3SNecK8SZFAJctHlKGqJ2+4Rpkp5qVA3qOuv5GDZOlIv/W2Z01MZoXk6722+LW6W2UbXD+CM9QbfLupUCc/YJ54s8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750585120; c=relaxed/simple; bh=ya1wLzf+PQ356sUz5QkQZEJpVwJ+/OHxun4IkXaD+ag=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=BUj5rHo4u9GfyVtIwXPvlZBde7Th7nvwGbCVlTI6jY6NMnkcH+Rf7x8sHI97i7N45s7TfGrD2yBPkjlCh4feTJZTwYn3Tm7qqSIIeEQHEp7sMxS6nmH1re/KcYUEWPsCtlvWfr6MNcPlFeNKz/ZmWNzztLcMkm5uqgdeuP7AhOo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=denx.de; spf=pass smtp.mailfrom=denx.de; dkim=pass (2048-bit key) header.d=denx.de header.i=@denx.de header.b=FSsRmxoq; arc=none smtp.client-ip=89.58.32.78 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=denx.de Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=denx.de Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=denx.de header.i=@denx.de header.b="FSsRmxoq" Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id 9235410244BF2; Sun, 22 Jun 2025 11:38:34 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=denx.de; s=mx-20241105; t=1750585116; h=from:subject:date:message-id:to:cc:mime-version: content-transfer-encoding:in-reply-to:references; bh=rvEcS7AJA2Zdhi7r7DQSSP/HHA7UcUBg9OaNOo7iCNo=; b=FSsRmxoqzkTb58N0kRe3h2MvewGvD/+In9L++Pva3L/H3HZrZELrIZrvdb8sGgxsXbHbXf ZLOVqoFmmpAKDQm5NBVbeUIRKtYbXfNI3xzMhxvn9EWDZIrUwhVjgY6Vy+hqDOweJrgUWu 9E3BPi4JIdNZu2Zy15y0PBf4y8qLBzUgnr8nt1Tzkz1SQCvll9Zbw58HqFsTVWy4C0Vq6b CS0mViHXq45skHaveQ9vNIojkpd0ltR778g5LYvZF1bhtmpO65WruwzWe9LPaDZZ2+NBg9 yMpWBj9qEL2/Til4T9pd0TJsuVmq0lZQwfOXO20CDMNwnbyvOClSZsdSm9cYJg== From: Lukasz Majewski To: Andrew Lunn , davem@davemloft.net, Eric Dumazet , Jakub Kicinski , Paolo Abeni , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Shawn Guo Cc: Sascha Hauer , Pengutronix Kernel Team , Fabio Estevam , Richard Cochran , netdev@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, imx@lists.linux.dev, linux-arm-kernel@lists.infradead.org, Stefan Wahren , Simon Horman , Lukasz Majewski Subject: [net-next v13 07/11] net: mtip: Extend the L2 switch driver with management operations Date: Sun, 22 Jun 2025 11:37:52 +0200 Message-Id: <20250622093756.2895000-8-lukma@denx.de> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20250622093756.2895000-1-lukma@denx.de> References: <20250622093756.2895000-1-lukma@denx.de> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Last-TLS-Session-Version: TLSv1.3 Content-Type: text/plain; charset="utf-8" This patch provides function necessary for manging the L2 switch. Signed-off-by: Lukasz Majewski --- Changes for v13: - New patch - created by excluding some code from large (i.e. v12 and earlier) MTIP driver --- .../net/ethernet/freescale/mtipsw/Makefile | 2 +- .../net/ethernet/freescale/mtipsw/mtipl2sw.c | 31 ++ .../net/ethernet/freescale/mtipsw/mtipl2sw.h | 23 + .../ethernet/freescale/mtipsw/mtipl2sw_mgnt.c | 443 ++++++++++++++++++ 4 files changed, 498 insertions(+), 1 deletion(-) create mode 100644 drivers/net/ethernet/freescale/mtipsw/mtipl2sw_mgnt.c diff --git a/drivers/net/ethernet/freescale/mtipsw/Makefile b/drivers/net/e= thernet/freescale/mtipsw/Makefile index bd8ffb30939a..a99aaf6ddfb2 100644 --- a/drivers/net/ethernet/freescale/mtipsw/Makefile +++ b/drivers/net/ethernet/freescale/mtipsw/Makefile @@ -1,4 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 =20 obj-$(CONFIG_FEC_MTIP_L2SW) +=3D nxp-mtipl2sw.o -nxp-mtipl2sw-objs :=3D mtipl2sw.o +nxp-mtipl2sw-objs :=3D mtipl2sw.o mtipl2sw_mgnt.o diff --git a/drivers/net/ethernet/freescale/mtipsw/mtipl2sw.c b/drivers/net= /ethernet/freescale/mtipsw/mtipl2sw.c index a4e38e0d773e..36700951cc97 100644 --- a/drivers/net/ethernet/freescale/mtipsw/mtipl2sw.c +++ b/drivers/net/ethernet/freescale/mtipsw/mtipl2sw.c @@ -482,8 +482,35 @@ static void mtip_config_switch(struct switch_enet_priv= ate *fep) =20 writel(0, fep->hwp + ESW_BKLR); =20 + /* Do NOT disable learning */ + mtip_port_learning_config(fep, 0, 0, 0); + mtip_port_learning_config(fep, 1, 0, 0); + mtip_port_learning_config(fep, 2, 0, 0); + + /* Disable blocking */ + mtip_port_blocking_config(fep, 0, 0); + mtip_port_blocking_config(fep, 1, 0); + mtip_port_blocking_config(fep, 2, 0); + writel(MCF_ESW_IMR_TXF | MCF_ESW_IMR_RXF, fep->hwp + ESW_IMR); + + mtip_port_enable_config(fep, 0, 1, 1); + mtip_port_enable_config(fep, 1, 1, 1); + mtip_port_enable_config(fep, 2, 1, 1); + + mtip_port_broadcast_config(fep, 0, 1); + mtip_port_broadcast_config(fep, 1, 1); + mtip_port_broadcast_config(fep, 2, 1); + + /* Disable multicast receive on port 0 (MGNT) */ + mtip_port_multicast_config(fep, 0, 0); + mtip_port_multicast_config(fep, 1, 1); + mtip_port_multicast_config(fep, 2, 1); + + /* Setup VLANs to provide port separation */ + if (!fep->br_offload) + mtip_switch_en_port_separation(fep); } =20 static netdev_tx_t mtip_start_xmit_port(struct sk_buff *skb, @@ -576,6 +603,10 @@ static netdev_tx_t mtip_start_xmit_port(struct sk_buff= *skb, netif_trans_update(dev); skb_tx_timestamp(skb); =20 + /* For port separation - force sending via specified port */ + if (!fep->br_offload && port !=3D 0) + mtip_forced_forward(fep, port, 1); + /* Trigger transmission start */ writel(MCF_ESW_TDAR_X_DES_ACTIVE, fep->hwp + ESW_TDAR); =20 diff --git a/drivers/net/ethernet/freescale/mtipsw/mtipl2sw.h b/drivers/net= /ethernet/freescale/mtipsw/mtipl2sw.h index ad81ef11e458..458c06f5be68 100644 --- a/drivers/net/ethernet/freescale/mtipsw/mtipl2sw.h +++ b/drivers/net/ethernet/freescale/mtipsw/mtipl2sw.h @@ -625,6 +625,29 @@ static inline int mtip_timeincrement(int time) =20 #define MTIP_PORT_FORWARDING_INIT 0xFF =20 +/* Switch Management functions */ +int mtip_vlan_input_process(struct switch_enet_private *fep, + int port, int mode, unsigned short port_vlanid, + int vlan_verify_en, int vlan_domain_num, + int vlan_domain_port); +int mtip_set_vlan_verification(struct switch_enet_private *fep, int port, + int vlan_domain_verify_en, + int vlan_discard_unknown_en); +int mtip_port_multicast_config(struct switch_enet_private *fep, int port, + bool enable); +int mtip_vlan_output_process(struct switch_enet_private *fep, int port, + int mode); +void mtip_switch_en_port_separation(struct switch_enet_private *fep); +void mtip_switch_dis_port_separation(struct switch_enet_private *fep); +int mtip_port_broadcast_config(struct switch_enet_private *fep, + int port, bool enable); +int mtip_forced_forward(struct switch_enet_private *fep, int port, bool en= able); +int mtip_port_learning_config(struct switch_enet_private *fep, int port, + bool disable, bool irq_adj); +int mtip_port_blocking_config(struct switch_enet_private *fep, int port, + bool enable); bool mtip_is_switch_netdev_port(const struct net_device *ndev); +int mtip_port_enable_config(struct switch_enet_private *fep, int port, + bool tx_en, bool rx_en); void mtip_clear_atable(struct switch_enet_private *fep); #endif /* __MTIP_L2SWITCH_H_ */ diff --git a/drivers/net/ethernet/freescale/mtipsw/mtipl2sw_mgnt.c b/driver= s/net/ethernet/freescale/mtipsw/mtipl2sw_mgnt.c new file mode 100644 index 000000000000..2178b3d02d57 --- /dev/null +++ b/drivers/net/ethernet/freescale/mtipsw/mtipl2sw_mgnt.c @@ -0,0 +1,443 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * L2 switch Controller driver for MTIP block - switch MGNT + * + * Copyright (C) 2025 DENX Software Engineering GmbH + * Lukasz Majewski + * + * Based on a previous work by: + * + * Copyright 2010-2012 Freescale Semiconductor, Inc. + * Alison Wang (b18965@freescale.com) + * Jason Jin (Jason.jin@freescale.com) + * + * Copyright (C) 2010-2013 Freescale Semiconductor, Inc. All Rights Reser= ved. + * Shrek Wu (B16972@freescale.com) + */ + +#include +#include +#include + +#include "mtipl2sw.h" + +int mtip_vlan_input_process(struct switch_enet_private *fep, + int port, int mode, unsigned short port_vlanid, + int vlan_verify_en, int vlan_domain_num, + int vlan_domain_port) +{ + /* Only modes from 1 to 4 are valid*/ + if (mode < 0 || mode > 4) { + dev_err(&fep->pdev->dev, + "%s: VLAN input processing mode (%d) not supported\n", + __func__, mode); + return -EINVAL; + } + + if (port < 0 || port > 2) { + dev_err(&fep->pdev->dev, "%s: Port (%d) not supported!\n", + __func__, port); + return -EINVAL; + } + + if (vlan_verify_en =3D=3D 1 && + (vlan_domain_num < 0 || vlan_domain_num > 32)) { + dev_err(&fep->pdev->dev, "%s: Domain out of range\n", __func__); + return -EINVAL; + } + + writel(FIELD_PREP(MCF_ESW_PID_VLANID_MASK, port_vlanid), + fep->hwp + ESW_PID(port)); + if (port =3D=3D 0) { + if (vlan_verify_en =3D=3D 1) + writel(FIELD_PREP(MCF_ESW_VRES_VLANID_MASK, + port_vlanid) | MCF_ESW_VRES_P0, + fep->hwp + ESW_VRES(vlan_domain_num)); + + writel(readl(fep->hwp + ESW_VIMEN) | MCF_ESW_VIMEN_EN0, + fep->hwp + ESW_VIMEN); + writel(readl(fep->hwp + ESW_VIMSEL) | + FIELD_PREP(MCF_ESW_VIMSEL_IM0_MASK, mode), + fep->hwp + ESW_VIMSEL); + } else if (port =3D=3D 1) { + if (vlan_verify_en =3D=3D 1) + writel(FIELD_PREP(MCF_ESW_VRES_VLANID_MASK, + port_vlanid) | MCF_ESW_VRES_P1, + fep->hwp + ESW_VRES(vlan_domain_num)); + + writel(readl(fep->hwp + ESW_VIMEN) | MCF_ESW_VIMEN_EN1, + fep->hwp + ESW_VIMEN); + writel(readl(fep->hwp + ESW_VIMSEL) | + FIELD_PREP(MCF_ESW_VIMSEL_IM1_MASK, mode), + fep->hwp + ESW_VIMSEL); + } else if (port =3D=3D 2) { + if (vlan_verify_en =3D=3D 1) + writel(FIELD_PREP(MCF_ESW_VRES_VLANID_MASK, + port_vlanid) | MCF_ESW_VRES_P2, + fep->hwp + ESW_VRES(vlan_domain_num)); + + writel(readl(fep->hwp + ESW_VIMEN) | MCF_ESW_VIMEN_EN2, + fep->hwp + ESW_VIMEN); + writel(readl(fep->hwp + ESW_VIMSEL) | + FIELD_PREP(MCF_ESW_VIMSEL_IM2_MASK, mode), + fep->hwp + ESW_VIMSEL); + } + + return 0; +} + +int mtip_vlan_output_process(struct switch_enet_private *fep, int port, + int mode) +{ + if (port < 0 || port > 2) { + dev_err(&fep->pdev->dev, "%s: Port (%d) not supported!\n", + __func__, port); + return -EINVAL; + } + + if (port =3D=3D 0) { + writel(readl(fep->hwp + ESW_VOMSEL) | + FIELD_PREP(MCF_ESW_VOMSEL_OM0_MASK, mode), + fep->hwp + ESW_VOMSEL); + } else if (port =3D=3D 1) { + writel(readl(fep->hwp + ESW_VOMSEL) | + FIELD_PREP(MCF_ESW_VOMSEL_OM1_MASK, mode), + fep->hwp + ESW_VOMSEL); + } else if (port =3D=3D 2) { + writel(readl(fep->hwp + ESW_VOMSEL) | + FIELD_PREP(MCF_ESW_VOMSEL_OM2_MASK, mode), + fep->hwp + ESW_VOMSEL); + } + + return 0; +} + +int mtip_set_vlan_verification(struct switch_enet_private *fep, int port, + int vlan_domain_verify_en, + int vlan_discard_unknown_en) +{ + if (port < 0 || port > 2) { + dev_err(&fep->pdev->dev, "%s: Port (%d) not supported!\n", + __func__, port); + return -EINVAL; + } + + if (vlan_domain_verify_en =3D=3D 1) { + if (port =3D=3D 0) + writel(readl(fep->hwp + ESW_VLANV) | MCF_ESW_VLANV_VV0, + fep->hwp + ESW_VLANV); + else if (port =3D=3D 1) + writel(readl(fep->hwp + ESW_VLANV) | MCF_ESW_VLANV_VV1, + fep->hwp + ESW_VLANV); + else if (port =3D=3D 2) + writel(readl(fep->hwp + ESW_VLANV) | MCF_ESW_VLANV_VV2, + fep->hwp + ESW_VLANV); + } else if (vlan_domain_verify_en =3D=3D 0) { + if (port =3D=3D 0) + writel(readl(fep->hwp + ESW_VLANV) & ~MCF_ESW_VLANV_VV0, + fep->hwp + ESW_VLANV); + else if (port =3D=3D 1) + writel(readl(fep->hwp + ESW_VLANV) & ~MCF_ESW_VLANV_VV1, + fep->hwp + ESW_VLANV); + else if (port =3D=3D 2) + writel(readl(fep->hwp + ESW_VLANV) & ~MCF_ESW_VLANV_VV2, + fep->hwp + ESW_VLANV); + } + + if (vlan_discard_unknown_en =3D=3D 1) { + if (port =3D=3D 0) + writel(readl(fep->hwp + ESW_VLANV) | MCF_ESW_VLANV_DU0, + fep->hwp + ESW_VLANV); + else if (port =3D=3D 1) + writel(readl(fep->hwp + ESW_VLANV) | MCF_ESW_VLANV_DU1, + fep->hwp + ESW_VLANV); + else if (port =3D=3D 2) + writel(readl(fep->hwp + ESW_VLANV) | MCF_ESW_VLANV_DU2, + fep->hwp + ESW_VLANV); + } else if (vlan_discard_unknown_en =3D=3D 0) { + if (port =3D=3D 0) + writel(readl(fep->hwp + ESW_VLANV) & ~MCF_ESW_VLANV_DU0, + fep->hwp + ESW_VLANV); + else if (port =3D=3D 1) + writel(readl(fep->hwp + ESW_VLANV) & ~MCF_ESW_VLANV_DU1, + fep->hwp + ESW_VLANV); + else if (port =3D=3D 2) + writel(readl(fep->hwp + ESW_VLANV) & ~MCF_ESW_VLANV_DU2, + fep->hwp + ESW_VLANV); + } + + dev_dbg(&fep->pdev->dev, "%s: ESW_VLANV %#x\n", __func__, + readl(fep->hwp + ESW_VLANV)); + + return 0; +} + +int mtip_port_multicast_config(struct switch_enet_private *fep, + int port, bool enable) +{ + u32 reg =3D 0; + + if (port < 0 || port > 2) { + dev_err(&fep->pdev->dev, "%s: Port (%d) not supported\n", + __func__, port); + return -EINVAL; + } + + reg =3D readl(fep->hwp + ESW_DMCR); + if (enable) { + if (port =3D=3D 0) + reg |=3D MCF_ESW_DMCR_P0; + else if (port =3D=3D 1) + reg |=3D MCF_ESW_DMCR_P1; + else if (port =3D=3D 2) + reg |=3D MCF_ESW_DMCR_P2; + } else { + if (port =3D=3D 0) + reg &=3D ~MCF_ESW_DMCR_P0; + else if (port =3D=3D 1) + reg &=3D ~MCF_ESW_DMCR_P1; + else if (port =3D=3D 2) + reg &=3D ~MCF_ESW_DMCR_P2; + } + + writel(reg, fep->hwp + ESW_DMCR); + return 0; +} + +/* enable or disable port n tx or rx + * tx_en 0 disable port n tx + * tx_en 1 enable port n tx + * rx_en 0 disable port n rx + * rx_en 1 enable port n rx + */ +int mtip_port_enable_config(struct switch_enet_private *fep, int port, + bool tx_en, bool rx_en) +{ + u32 reg =3D 0; + + if (port < 0 || port > 2) { + dev_err(&fep->pdev->dev, "%s: Port (%d) not supported\n", + __func__, port); + return -EINVAL; + } + + reg =3D readl(fep->hwp + ESW_PER); + if (tx_en) { + if (port =3D=3D 0) + reg |=3D MCF_ESW_PER_TE0; + else if (port =3D=3D 1) + reg |=3D MCF_ESW_PER_TE1; + else if (port =3D=3D 2) + reg |=3D MCF_ESW_PER_TE2; + } else { + if (port =3D=3D 0) + reg &=3D (~MCF_ESW_PER_TE0); + else if (port =3D=3D 1) + reg &=3D (~MCF_ESW_PER_TE1); + else if (port =3D=3D 2) + reg &=3D (~MCF_ESW_PER_TE2); + } + + if (rx_en) { + if (port =3D=3D 0) + reg |=3D MCF_ESW_PER_RE0; + else if (port =3D=3D 1) + reg |=3D MCF_ESW_PER_RE1; + else if (port =3D=3D 2) + reg |=3D MCF_ESW_PER_RE2; + } else { + if (port =3D=3D 0) + reg &=3D (~MCF_ESW_PER_RE0); + else if (port =3D=3D 1) + reg &=3D (~MCF_ESW_PER_RE1); + else if (port =3D=3D 2) + reg &=3D (~MCF_ESW_PER_RE2); + } + + writel(reg, fep->hwp + ESW_PER); + return 0; +} + +void mtip_switch_en_port_separation(struct switch_enet_private *fep) +{ + u32 reg; + + mtip_vlan_input_process(fep, 0, 3, 0x10, 1, 0, 0); + mtip_vlan_input_process(fep, 1, 3, 0x11, 1, 1, 0); + mtip_vlan_input_process(fep, 2, 3, 0x12, 1, 2, 0); + + reg =3D readl(fep->hwp + ESW_VRES(0)); + writel(reg | MCF_ESW_VRES_P1 | MCF_ESW_VRES_P2, + fep->hwp + ESW_VRES(0)); + + reg =3D readl(fep->hwp + ESW_VRES(1)); + writel(reg | MCF_ESW_VRES_P0, fep->hwp + ESW_VRES(1)); + + reg =3D readl(fep->hwp + ESW_VRES(2)); + writel(reg | MCF_ESW_VRES_P0, fep->hwp + ESW_VRES(2)); + + dev_dbg(&fep->pdev->dev, "%s: VRES0: 0x%x\n", + __func__, readl(fep->hwp + ESW_VRES(0))); + dev_dbg(&fep->pdev->dev, "%s: VRES1: 0x%x\n", __func__, + readl(fep->hwp + ESW_VRES(1))); + dev_dbg(&fep->pdev->dev, "%s: VRES2: 0x%x\n", __func__, + readl(fep->hwp + ESW_VRES(2))); + + mtip_set_vlan_verification(fep, 0, 1, 0); + mtip_set_vlan_verification(fep, 1, 1, 0); + mtip_set_vlan_verification(fep, 2, 1, 0); + + mtip_vlan_output_process(fep, 0, 2); + mtip_vlan_output_process(fep, 1, 2); + mtip_vlan_output_process(fep, 2, 2); +} + +void mtip_switch_dis_port_separation(struct switch_enet_private *fep) +{ + writel(0, fep->hwp + ESW_PID(0)); + writel(0, fep->hwp + ESW_PID(1)); + writel(0, fep->hwp + ESW_PID(2)); + + writel(0, fep->hwp + ESW_VRES(0)); + writel(0, fep->hwp + ESW_VRES(1)); + writel(0, fep->hwp + ESW_VRES(2)); + + writel(0, fep->hwp + ESW_VIMEN); + writel(0, fep->hwp + ESW_VIMSEL); + writel(0, fep->hwp + ESW_VLANV); + writel(0, fep->hwp + ESW_VOMSEL); +} + +int mtip_port_broadcast_config(struct switch_enet_private *fep, + int port, bool enable) +{ + u32 reg =3D 0; + + if (port < 0 || port > 2) { + dev_err(&fep->pdev->dev, "%s: Port (%d) not supported\n", + __func__, port); + return -EINVAL; + } + + reg =3D readl(fep->hwp + ESW_DBCR); + if (enable) { + if (port =3D=3D 0) + reg |=3D MCF_ESW_DBCR_P0; + else if (port =3D=3D 1) + reg |=3D MCF_ESW_DBCR_P1; + else if (port =3D=3D 2) + reg |=3D MCF_ESW_DBCR_P2; + } else { + if (port =3D=3D 0) + reg &=3D ~MCF_ESW_DBCR_P0; + else if (port =3D=3D 1) + reg &=3D ~MCF_ESW_DBCR_P1; + else if (port =3D=3D 2) + reg &=3D ~MCF_ESW_DBCR_P2; + } + + writel(reg, fep->hwp + ESW_DBCR); + return 0; +} + +/* The frame is forwarded to the forced destination ports. + * It only replace the MAC lookup function, + * all other filtering(eg.VLAN verification) act as normal + */ +int mtip_forced_forward(struct switch_enet_private *fep, int port, bool en= able) +{ + u32 reg =3D 0; + + if (port & ~GENMASK(1, 0)) { + dev_err(&fep->pdev->dev, + "%s: Forced forward for port(s): 0x%x not supported!\n", + __func__, port); + return -EINVAL; + } + + /* Enable Forced forwarding for port(s) */ + reg |=3D FIELD_PREP(MCF_ESW_P0FFEN_FD_MASK, port & GENMASK(1, 0)); + + if (enable) + reg |=3D MCF_ESW_P0FFEN_FEN; + else + reg &=3D ~MCF_ESW_P0FFEN_FEN; + + writel(reg, fep->hwp + ESW_P0FFEN); + return 0; +} + +int mtip_port_learning_config(struct switch_enet_private *fep, int port, + bool disable, bool irq_adj) +{ + u32 reg =3D 0; + + if (port < 0 || port > 2) { + dev_err(&fep->pdev->dev, "%s: Port (%d) not supported\n", + __func__, port); + return -EINVAL; + } + + reg =3D readl(fep->hwp + ESW_BKLR); + if (disable) { + if (irq_adj) + writel(readl(fep->hwp + ESW_IMR) & ~MCF_ESW_IMR_LRN, + fep->hwp + ESW_IMR); + + if (port =3D=3D 0) + reg |=3D MCF_ESW_BKLR_LD0; + else if (port =3D=3D 1) + reg |=3D MCF_ESW_BKLR_LD1; + else if (port =3D=3D 2) + reg |=3D MCF_ESW_BKLR_LD2; + } else { + if (irq_adj) + writel(readl(fep->hwp + ESW_IMR) | MCF_ESW_IMR_LRN, + fep->hwp + ESW_IMR); + + if (port =3D=3D 0) + reg &=3D ~MCF_ESW_BKLR_LD0; + else if (port =3D=3D 1) + reg &=3D ~MCF_ESW_BKLR_LD1; + else if (port =3D=3D 2) + reg &=3D ~MCF_ESW_BKLR_LD2; + } + + writel(reg, fep->hwp + ESW_BKLR); + dev_dbg(&fep->pdev->dev, "%s ESW_BKLR %#x, ESW_IMR %#x\n", __func__, + readl(fep->hwp + ESW_BKLR), readl(fep->hwp + ESW_IMR)); + + return 0; +} + +int mtip_port_blocking_config(struct switch_enet_private *fep, int port, + bool enable) +{ + u32 reg =3D 0; + + if (port < 0 || port > 2) { + dev_err(&fep->pdev->dev, "%s: Port (%d) not supported\n", + __func__, port); + return -EINVAL; + } + + reg =3D readl(fep->hwp + ESW_BKLR); + if (enable) { + if (port =3D=3D 0) + reg |=3D MCF_ESW_BKLR_BE0; + else if (port =3D=3D 1) + reg |=3D MCF_ESW_BKLR_BE1; + else if (port =3D=3D 2) + reg |=3D MCF_ESW_BKLR_BE2; + } else { + if (port =3D=3D 0) + reg &=3D ~MCF_ESW_BKLR_BE0; + else if (port =3D=3D 1) + reg &=3D ~MCF_ESW_BKLR_BE1; + else if (port =3D=3D 2) + reg &=3D ~MCF_ESW_BKLR_BE2; + } + + writel(reg, fep->hwp + ESW_BKLR); + return 0; +} --=20 2.39.5