From patchwork Fri Apr 18 06:07:10 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lukasz Majewski X-Patchwork-Id: 14056729 X-Patchwork-Delegate: kuba@kernel.org 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 777E5255240; Fri, 18 Apr 2025 06:07:52 +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=1744956475; cv=none; b=s6nNoVlx3XGfVcj+HXIBqz7vXwa1XsXcz08Go4xAXSdCplF82xVpWPIUGyiomPbMuvJcVzS8l0GwLIt7MBg8vEMRxZO/XTye7Uq7I4lTq/NFe7XZsl8ZefEuKYLWMJuCeAcxJf2ylQjp+Ao+vzkKyZI82cGCIVSlV3wXMhMQxrc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744956475; c=relaxed/simple; bh=rS52dXVugZIHeYYAdUwoLqpkDMmOUevnPcF8e2zjuHQ=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=OtbSVkmofCVA9asAh/NYHm5q3t01vrevqpJHzlV5dzYW8VQ8GlTv1Sgfnj16fZ7R6E4sh9DbV+4RcLXVfhRtw4QLvIM0b0QiZMjcE5ZghN9KC35u4JRS3UBnYrewLre9bZhtTWKghCjKdWHbrP5ETT/BWQBWBJbDykhXIm0UWTc= 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=G+R+UC0v; 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="G+R+UC0v" Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id 9EFD91029339C; Fri, 18 Apr 2025 08:07:42 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=denx.de; s=mx-20241105; t=1744956463; h=from:subject:date:message-id:to:cc:mime-version: content-transfer-encoding:in-reply-to:references; bh=LKXWneHG5Mjcr5LyLwk+xqebV6vCoTQDQ6Sj7uOB6o0=; b=G+R+UC0vI5mgZC/zD05wjbM0UM4k/ktfyScG8+uQ9DEJcTgsZg+pQphu9FoJPIOftPtXNA xtiDfkAkKbUZ3Otay6ctmV5nvJbdwwWVfQWOnk1IyuygCYiBFgCFQ0T2pXD4NV4GarYv12 MbrdRL/ti0Y7+yBnjL3zhMOcvnmqgV9awjeoMZ11zE2SMez5ITWFUnGlHQn91QA04hMru7 VNB2I20G6ikEoC4cuuV9tWzDnmpVt10Nat3lfwymDU2+mAo0tj9fZLwgZjfu8n4avOYfd1 KksFk2jXrsyDTYGYX9wHIypvbdG3D9HgEHt8Y/P16qM4TlgdWrIHHrw1noXR6A== 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 v6 1/7] dt-bindings: net: Add MTIP L2 switch description Date: Fri, 18 Apr 2025 08:07:10 +0200 Message-Id: <20250418060716.3498031-2-lukma@denx.de> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20250418060716.3498031-1-lukma@denx.de> References: <20250418060716.3498031-1-lukma@denx.de> Precedence: bulk X-Mailing-List: netdev@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Last-TLS-Session-Version: TLSv1.3 X-Patchwork-Delegate: kuba@kernel.org This patch provides description of the MTIP L2 switch available in some NXP's SOCs - e.g. imx287. Signed-off-by: Lukasz Majewski --- Changes for v2: - Rename the file to match exactly the compatible (nxp,imx287-mtip-switch) Changes for v3: - Remove '-' from const:'nxp,imx287-mtip-switch' - Use '^port@[12]+$' for port patternProperties - Drop status = "okay"; - Provide proper indentation for 'example' binding (replace 8 spaces with 4 spaces) - Remove smsc,disable-energy-detect; property - Remove interrupt-parent and interrupts properties as not required - Remove #address-cells and #size-cells from required properties check - remove description from reg: - Add $ref: ethernet-switch.yaml# Changes for v4: - Use $ref: ethernet-switch.yaml#/$defs/ethernet-ports and remove already referenced properties - Rename file to nxp,imx28-mtip-switch.yaml Changes for v5: - Provide proper description for 'ethernet-port' node Changes for v6: - Proper usage of $ref: ethernet-switch.yaml#/$defs/ethernet-ports/patternProperties when specifying the 'ethernet-ports' property - Add description and check for interrupt-names property --- .../bindings/net/nxp,imx28-mtip-switch.yaml | 148 ++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 Documentation/devicetree/bindings/net/nxp,imx28-mtip-switch.yaml diff --git a/Documentation/devicetree/bindings/net/nxp,imx28-mtip-switch.yaml b/Documentation/devicetree/bindings/net/nxp,imx28-mtip-switch.yaml new file mode 100644 index 000000000000..3e2d724074d5 --- /dev/null +++ b/Documentation/devicetree/bindings/net/nxp,imx28-mtip-switch.yaml @@ -0,0 +1,148 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/nxp,imx28-mtip-switch.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NXP SoC Ethernet Switch Controller (L2 MoreThanIP switch) + +maintainers: + - Lukasz Majewski + +description: + The 2-port switch ethernet subsystem provides ethernet packet (L2) + communication and can be configured as an ethernet switch. It provides the + reduced media independent interface (RMII), the management data input + output (MDIO) for physical layer device (PHY) management. + +properties: + compatible: + const: nxp,imx28-mtip-switch + + reg: + maxItems: 1 + + phy-supply: + description: + Regulator that powers Ethernet PHYs. + + clocks: + items: + - description: Register accessing clock + - description: Bus access clock + - description: Output clock for external device - e.g. PHY source clock + - description: IEEE1588 timer clock + + clock-names: + items: + - const: ipg + - const: ahb + - const: enet_out + - const: ptp + + interrupts: + items: + - description: Switch interrupt + - description: ENET0 interrupt + - description: ENET1 interrupt + + interrupt-names: + items: + - const: mtipl2sw + - const: enet0 + - const: enet1 + + pinctrl-names: true + + ethernet-ports: + type: object + $ref: ethernet-switch.yaml#/$defs/ethernet-ports/patternProperties + additionalProperties: true + + patternProperties: + '^ethernet-port@[12]$': + type: object + additionalProperties: true + properties: + reg: + items: + - enum: [1, 2] + description: MTIP L2 switch port number + + required: + - reg + - label + - phy-mode + - phy-handle + + mdio: + type: object + $ref: mdio.yaml# + unevaluatedProperties: false + description: + Specifies the mdio bus in the switch, used as a container for phy nodes. + +required: + - compatible + - reg + - clocks + - clock-names + - interrupts + - interrupt-names + - mdio + - ethernet-ports + +unevaluatedProperties: false + +examples: + - | + #include + switch@800f0000 { + compatible = "nxp,imx28-mtip-switch"; + reg = <0x800f0000 0x20000>; + pinctrl-names = "default"; + pinctrl-0 = <&mac0_pins_a>, <&mac1_pins_a>; + phy-supply = <®_fec_3v3>; + interrupts = <100>, <101>, <102>; + interrupt-names = "mtipl2sw", "enet0", "enet1"; + clocks = <&clks 57>, <&clks 57>, <&clks 64>, <&clks 35>; + clock-names = "ipg", "ahb", "enet_out", "ptp"; + + ethernet-ports { + #address-cells = <1>; + #size-cells = <0>; + + mtip_port1: ethernet-port@1 { + reg = <1>; + label = "lan0"; + local-mac-address = [ 00 00 00 00 00 00 ]; + phy-mode = "rmii"; + phy-handle = <ðphy0>; + }; + + mtip_port2: ethernet-port@2 { + reg = <2>; + label = "lan1"; + local-mac-address = [ 00 00 00 00 00 00 ]; + phy-mode = "rmii"; + phy-handle = <ðphy1>; + }; + }; + + mdio_sw: mdio { + #address-cells = <1>; + #size-cells = <0>; + + reset-gpios = <&gpio2 13 0>; + reset-delay-us = <25000>; + reset-post-delay-us = <10000>; + + ethphy0: ethernet-phy@0 { + reg = <0>; + }; + + ethphy1: ethernet-phy@1 { + reg = <1>; + }; + }; + }; From patchwork Fri Apr 18 06:07:11 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lukasz Majewski X-Patchwork-Id: 14056730 X-Patchwork-Delegate: kuba@kernel.org 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 5C82B2641E9; Fri, 18 Apr 2025 06:07:54 +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=1744956476; cv=none; b=nwbMAO52DrOtCswlmroaVZ5RqIAKBGkROmI5Ws/dZ2oIbSwF7/dDsLD66IKuFHPl04QE+6gcRbMVy4xnwe1n1uEpcxGvAc2nognVenoYrb5x3mfV6lHgOLNvzfBZpVjTlgEohTDf9NLkyeUyWXXLqdcoGA9bwVIxdDfSeDY0tZ4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744956476; c=relaxed/simple; bh=BrMNhqZl+0zkAODov3wygJxHlIe8R5ksa2XVadwAptw=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=XTC3/eyy6mdnq9rXjOuW4lMKSDN0s5fACBqDvdCrY3MiD2CM5GnFWlM+WH1fMeZfx0jFTd2YtYKO4T3gaKFbynfonMetO3KZYHvX5b+hYR/a5PqAHQI8FP3l1y1TRpQGk5lwbJQSA78BFoZnNQRn2DiBhJGxY7RCIQFG8Jdi5DM= 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=Wq9c13y2; 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="Wq9c13y2" Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id DA52110293397; Fri, 18 Apr 2025 08:07:44 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=denx.de; s=mx-20241105; t=1744956466; h=from:subject:date:message-id:to:cc:mime-version: content-transfer-encoding:in-reply-to:references; bh=ayBcBvwZtTlfCl+i4LAQIyGkr3eJ4aPEB/JfSb2XRlo=; b=Wq9c13y2Pr4R7I6K8cqXRmShpYZJXp6T7Zi/APumvmTTyP1c0kfz35BwNNpjOxaRs9AwdJ RnBzhFmT7/w9IuzvbCSnbxqkX00cOdsvm4/3eE9F76peZltbISc2BDwgZnxXrOLXJM5Las z1e0Qz7MXgvnalWrdGgkwC2YQ6lsBLQkjhpocmEta7XbMikLHIPX5PiyEQOIcNlUjSnBaT i0PINOtweKlxLiWuhnfOGU0PAl/ffAPJOr+ulZaWoNvT6dpY65B3GPRabhfoocF8PQBxNN WT0msVY78GuaftzzLyPT37xtsVmhP9NocMam1Np2AvlqIRhsjwl2YJ39pFFpbg== 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 , Andrew Lunn Subject: [net-next v6 2/7] ARM: dts: nxp: mxs: Adjust the imx28.dtsi L2 switch description Date: Fri, 18 Apr 2025 08:07:11 +0200 Message-Id: <20250418060716.3498031-3-lukma@denx.de> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20250418060716.3498031-1-lukma@denx.de> References: <20250418060716.3498031-1-lukma@denx.de> Precedence: bulk X-Mailing-List: netdev@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Last-TLS-Session-Version: TLSv1.3 X-Patchwork-Delegate: kuba@kernel.org The current range of 'reg' property is too small to allow full control of the L2 switch on imx287. As this IP block also uses ENET-MAC blocks for its operation, the address range for it must be included as well. Moreover, some SoC common properties (like compatible, clocks, interrupts numbers) have been moved to this node. Signed-off-by: Lukasz Majewski Reviewed-by: Andrew Lunn --- Changes for v2: - adding extra properties (like compatible, clocks, interupts) Changes for v3: - None Changes for v4: - Rename imx287 with imx28 (as the former is not used in kernel anymore) Changes for v5: - None Changes for v6: - Add interrupt-names property --- arch/arm/boot/dts/nxp/mxs/imx28.dtsi | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/arch/arm/boot/dts/nxp/mxs/imx28.dtsi b/arch/arm/boot/dts/nxp/mxs/imx28.dtsi index bbea8b77386f..2a484b215c8e 100644 --- a/arch/arm/boot/dts/nxp/mxs/imx28.dtsi +++ b/arch/arm/boot/dts/nxp/mxs/imx28.dtsi @@ -1321,8 +1321,13 @@ mac1: ethernet@800f4000 { status = "disabled"; }; - eth_switch: switch@800f8000 { - reg = <0x800f8000 0x8000>; + eth_switch: switch@800f0000 { + compatible = "nxp,imx28-mtip-switch"; + reg = <0x800f0000 0x20000>; + interrupts = <100>, <101>, <102>; + interrupt-names = "mtipl2sw", "enet0", "enet1"; + clocks = <&clks 57>, <&clks 57>, <&clks 64>, <&clks 35>; + clock-names = "ipg", "ahb", "enet_out", "ptp"; status = "disabled"; }; }; From patchwork Fri Apr 18 06:07:12 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lukasz Majewski X-Patchwork-Id: 14056732 X-Patchwork-Delegate: kuba@kernel.org 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 4ED12268C5E; Fri, 18 Apr 2025 06:07:55 +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=1744956478; cv=none; b=cGZS2we8VPNt8pyXDe/OZmPa+Dg6MvEfdYUbT88j4LZb72q6f9ia+yjNIgLHpjDaWSxOISwYeD88S5hMXOX+0Pyxasn+veVAzEzvi9f6n2scd3TiUZOqIB0H1DyR1dwSmsnw1ZmcFv3SnzS5euGkC+yBKxy8Lnz38zdcuoB7u20= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744956478; c=relaxed/simple; bh=TwS2N2WcXlghXScMQhAVbu6v+2/0vaUBHcX4FQsrDyU=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=Db7RQwXOx6cyDv2fEJqNHlZedUN0dPuFgJUJwrm77ihItDh6pWH0H5psyFDGs+kwZM9PUhgAqrNLUyUxWbVuxOqYEg0h0NomBNH5kuTs+yNp/vHXKyys6+iqIehcxa8DUPH1s8vknEqColY4xkbGqmEFAiqMhCUh4CQK/WKjgMc= 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=JxR6vzU/; 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="JxR6vzU/" Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id A44F6102933A2; Fri, 18 Apr 2025 08:07:46 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=denx.de; s=mx-20241105; t=1744956468; h=from:subject:date:message-id:to:cc:mime-version: content-transfer-encoding:in-reply-to:references; bh=xgFAa+8bRILV7W/eI8JgsQDpOF5KBsCQ37CjVLpnr+k=; b=JxR6vzU/h333bUqvd2r5ggW6s4pNUVcZSfihzhAvOLC/2W8VvlmlYOaDSf+yo/L+sti1NO OwUf6qgTdzrcaI8BQaJU4pOoS6IUR9krChkxGpygj7ArVU0oe13gdpyYEVLGqGGIPMJ1mN rX4/tEG24WixVFxkM0OhjnDursLg65wAni9D4SRDpsSr2QOZTLKtM+BbfV3jzCEvCuhYKl m2hYMbTJyoD24EY2PveuXqpL4kzVPdFTyMSmc3ypwJOTjZogijh1ERvhJ8yfrQT8wOayk+ m4qHDnOiPoLnSAtJk/I3cMHkcVAeUENelH8eiBiECqhFxEgJgmt22ZlwurIVPQ== 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 , Andrew Lunn Subject: [net-next v6 3/7] ARM: dts: nxp: mxs: Adjust XEA board's DTS to support L2 switch Date: Fri, 18 Apr 2025 08:07:12 +0200 Message-Id: <20250418060716.3498031-4-lukma@denx.de> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20250418060716.3498031-1-lukma@denx.de> References: <20250418060716.3498031-1-lukma@denx.de> Precedence: bulk X-Mailing-List: netdev@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Last-TLS-Session-Version: TLSv1.3 X-Patchwork-Delegate: kuba@kernel.org The description is similar to the one used with the new CPSW driver. Signed-off-by: Lukasz Majewski Reviewed-by: Andrew Lunn Reviewed-by: Stefan Wahren --- Changes for v2: - Remove properties which are common for the imx28(7) SoC - Use mdio properties to perform L2 switch reset (avoid using deprecated properties) Changes for v3: - Replace IRQ_TYPE_EDGE_FALLING with IRQ_TYPE_LEVEL_LOW - Update comment regarding PHY interrupts s/AND/OR/g Changes for v4: - Use GPIO_ACTIVE_LOW instead of 0 in 'reset-gpios' - Replace port@[12] with ethernet-port@[12] Changes for v5: - Add proper multiline comment for IRQs description Changes for v6: - None --- arch/arm/boot/dts/nxp/mxs/imx28-xea.dts | 56 +++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/arch/arm/boot/dts/nxp/mxs/imx28-xea.dts b/arch/arm/boot/dts/nxp/mxs/imx28-xea.dts index 6c5e6856648a..69032b29d767 100644 --- a/arch/arm/boot/dts/nxp/mxs/imx28-xea.dts +++ b/arch/arm/boot/dts/nxp/mxs/imx28-xea.dts @@ -5,6 +5,7 @@ */ /dts-v1/; +#include #include "imx28-lwe.dtsi" / { @@ -90,6 +91,61 @@ ®_usb_5v { gpio = <&gpio0 2 0>; }; +ð_switch { + pinctrl-names = "default"; + pinctrl-0 = <&mac0_pins_a>, <&mac1_pins_a>; + phy-supply = <®_fec_3v3>; + status = "okay"; + + ethernet-ports { + #address-cells = <1>; + #size-cells = <0>; + + mtip_port1: ethernet-port@1 { + reg = <1>; + label = "lan0"; + local-mac-address = [ 00 00 00 00 00 00 ]; + phy-mode = "rmii"; + phy-handle = <ðphy0>; + }; + + mtip_port2: ethernet-port@2 { + reg = <2>; + label = "lan1"; + local-mac-address = [ 00 00 00 00 00 00 ]; + phy-mode = "rmii"; + phy-handle = <ðphy1>; + }; + }; + + mdio_sw: mdio { + #address-cells = <1>; + #size-cells = <0>; + + reset-gpios = <&gpio3 21 GPIO_ACTIVE_LOW>; + reset-delay-us = <25000>; + reset-post-delay-us = <10000>; + + ethphy0: ethernet-phy@0 { + reg = <0>; + smsc,disable-energy-detect; + /* + * Both PHYs (i.e. 0,1) have the same, single GPIO, + * line to handle both, their interrupts (OR'ed) + */ + interrupt-parent = <&gpio4>; + interrupts = <13 IRQ_TYPE_LEVEL_LOW>; + }; + + ethphy1: ethernet-phy@1 { + reg = <1>; + smsc,disable-energy-detect; + interrupt-parent = <&gpio4>; + interrupts = <13 IRQ_TYPE_LEVEL_LOW>; + }; + }; +}; + &spi2_pins_a { fsl,pinmux-ids = < MX28_PAD_SSP2_SCK__SSP2_SCK From patchwork Fri Apr 18 06:07:13 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lukasz Majewski X-Patchwork-Id: 14056735 X-Patchwork-Delegate: kuba@kernel.org 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 2B5D8233145; Fri, 18 Apr 2025 06:07:51 +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=1744956479; cv=none; b=t6cNNsXvykUp6ZQggt3/JJJTfhByunvRRnZEuO+JkG0I8H8jRRIBDB/YXVufbGrHHdRq3VAq6RycT0q/b88GWTZk3iX/Hy9inwRbvgkD7jDSVMrzCZ2KiqEW9133xp94orQK274fVCxwuInHx4OwsxqmNtLZAoyteVrBZ+iarfQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744956479; c=relaxed/simple; bh=03BijV8o9Iu5QeeTtVNhxcjU9OWPHbvuCBlcaMKUoRE=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=HPdxQPUePTOph9iUihcz5pFH2oMiddJtpoPD7DeElCmmRNZqzUWko7dt3+P7EV/3j9yL8FvG0SnFYZP7uqKOhGZb0o+mkbi0BjJIqQLTUZoqBDVZvxMBc3ablMOtQ2IPZTQ3E8CvPZlvJxbX47Gr1d0s/CPq8jrrZpCic64YNZ8= 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=ZqUN2SiZ; 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="ZqUN2SiZ" Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id 4C007102933A0; Fri, 18 Apr 2025 08:07:48 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=denx.de; s=mx-20241105; t=1744956469; h=from:subject:date:message-id:to:cc:mime-version: content-transfer-encoding:in-reply-to:references; bh=5Jl2LD9ss1D1V+Hdgth6bfAMfkZ77GhWMNw+AHMJVb4=; b=ZqUN2SiZXNhJQfNiBgGs0L3fzJ+Yj5fuG318f++lCdVbUTapOnkm7KxS72aFk/CICJZrBs 1dUbSgRcY3c56V37C36tswSoawnTCV9/iFeaR5g2dYzWF+KVn/gokWQw5CcSBueOwKV9oi 7/39yqZ1lSwTYYnDH45w7GZiNVrcLQNjhyVWBVYMy2Dj1+qll2HkXGeeK1iiIy0p2EtUob 94ChLIz7FFgOW54zbksoVfpXFejSGswmgz8l7SNrE9Vt/gNXCzjYqG33M3Ulip8M8pDHrP 0XdYFF+/GSSXP4nxH6d1mpTei5OjAfvBtqPzzmVc7OOdgVjBlPFeaXmtyl1sqA== 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 v6 4/7] net: mtip: The L2 switch driver for imx287 Date: Fri, 18 Apr 2025 08:07:13 +0200 Message-Id: <20250418060716.3498031-5-lukma@denx.de> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20250418060716.3498031-1-lukma@denx.de> References: <20250418060716.3498031-1-lukma@denx.de> Precedence: bulk X-Mailing-List: netdev@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Last-TLS-Session-Version: TLSv1.3 X-Patchwork-Delegate: kuba@kernel.org This patch series provides support for More Than IP L2 switch embedded in the imx287 SoC. This is a two port switch (placed between uDMA[01] and MAC-NET[01]), which can be used for offloading the network traffic. It can be used interchangeably with current FEC driver - to be more specific: one can use either of it, depending on the requirements. The biggest difference is the usage of DMA - when FEC is used, separate DMAs are available for each ENET-MAC block. However, with switch enabled - only the DMA0 is used to send/receive data to/form switch (and then switch sends them to respecitive ports). Signed-off-by: Lukasz Majewski Reviewed-by: Stefan Wahren --- Changes for v2: - Remove not needed comments - Restore udelay(10) for switch reset (such delay is explicitly specifed in the documentation - Add COMPILE_TEST - replace pr_* with dev_* - Use for_each_available_child_of_node_scoped() - Use devm_* function for memory allocation - Remove printing information about the HW and SW revision of the driver - Use devm_regulator_get_optional() - Change compatible prefix from 'fsl' to more up to date 'nxp' - Remove .owner = THIS_MODULE - Use devm_platform_ioremap_resource(pdev, 0); - Use devm_request_irq() - Use devm_regulator_get_enable_optional() - Replace clk_prepare_enable() and devm_clk_get() with single call to devm_clk_get_optional_enabled() - Cleanup error patch when function calls in probe fail - Refactor the mtip_reset_phy() to serve as mdio bus reset callback - Add myself as the MTIP L2 switch maintainer (squashed the separated commit) - More descriptive help paragraphs (> 4 lines) Changes for v3: - Remove 'bridge_offloading' module parameter (to bridge ports just after probe) - Remove forward references - Fix reverse christmas tree formatting in functions - Convert eligible comments to kernel doc format - Remove extra MAC address validation check at esw_mac_addr_static() - Remove mtip_print_link_status() and replace it with phy_print_status() - Avoid changing phy device state in the driver (instead use functions exported by the phy API) - Do not print extra information regarding PHY (which is printed by phylib) - e.g. net lan0: lan0: MTIP eth L2 switch 1e:ce:a5:0b:4c:12 - Remove VERSION from the driver - now we rely on the SHA1 in Linux mainline tree - Remove zeroing of the net device private area (shall be already done during allocation) - Refactor the code to remove mtip_ndev_setup() - Use -ENOMEM instead of -1 return code when allocation fails - Replace dev_info() with dev_dbg() to reduce number of information print on normal operation - Return ret instead of 0 from mtip_ndev_init() - Remove fep->mii_timeout flag from the driver - Remove not used stop_gpr_* fields in mtip_devinfo struct - Remove platform_device_id description for mtipl2sw driver - Add MODULE_DEVICE_TABLE() for mtip_of_match - Remove MODULE_ALIAS() Changes for v4: - Rename imx287 with imx28 (as the former is not used in kernel anymore) - Reorder the place where ENET interface is initialized - without this change the enet_out clock has default (25 MHz) value, which causes issues during reset (RMII's 50 MHz is required for proper PHY reset). - Use PAUR instead of PAUR register to program MAC address - Replace eth_mac_addr() with eth_hw_addr_set() - Write to HW the randomly generated MAC address (if required) - Adjust the reset code - s/read_atable/mtip_read_atable/g and s/write_atable/mtip_write_atable/g - Add clk_disable() and netif_napi_del() when errors occur during mtip_open() - refactor the error handling path. - Refactor the mtip_set_multicast_list() to write (now) correct values to ENET-FEC registers. - Replace dev_warn() with dev_err() - Use GPIO_ACTIVE_LOW to indicate polarity in DTS - Refactor code to check if network device is the switch device - Remove mtip_port_dev_check() - Refactor mtip_ndev_port_link() avoid starting HW offloading for bridge when MTIP ports are parts of two distinct bridges - Replace del_timer() with timer_delete_sync() Changes for v5: - Fix spelling in Kconfig - Replace tmp with reg or register name - Replace tmpaddr with mac_addr - Use mac address assignment (from registers) code similar to fec_main.c (as it shall handle properly generic endianess) - Add description for fep: in the mtip_update_atable_static() kernel doc - Replace writel(bdp, &fep->cur_rx) with fep->cur_rx = bdp; - Fix spelling of transmit - Remove not needed white spaces in mtipl2sw.h - Remove '_t' from struct mtip_addr_table_t - Provide proper alignment in the mtipl2sw.h - Add blank line after local header in mtipl2sw_br.c - Use %p instead of %x (and cast) for fep in debug message - Disable L2 switch in-HW offloading when only one of eligible ports is removed from the bridge - Sort includes in the patch set alphabethically - Introduce FEC_QUIRK_SWAP_FRAME to avoid #ifdef for imx28 proper operation - Move 'mtip_port_info g_info' to struct switch_enet_private - Replace some unsigned int with u32 (on data fields with 32 bit size) - Remove not relevant comments from mtip_enet_init() - Refactor functions definitions to be void when no other value than 0 is returned. - Use capital letters in HEX constants - Use u32 instead of unsigned int when applicable - Add error handling code after the dma_map_single() is called - The MCF_FEC_MSCR register can be written unconditionally for all supported platforms. - Use IS_ENABLED() instead of #ifdef in mtip_timeout() - Replace dev_info() with dev_warn_ratelimited() in mtip_switch_rx() - Add code to handle situation when there is no memory - Remove kfree(fep->mii_bus->irq) - Provide more verbose output of mdio_{read|write} functions - Handle error when clk_enable() fails in mtip_open() - Use dev_dbg() at mtip_set_multicast_list() - Simplify the mtip_is_switch_netdev_port() function to return condition check value - Add dev_dbg() when of_get_mac_address() fails (as it may not be provided) - Remove return ret; in mtip_register_notifiers() - Replace int to bool in mtipl2sw_mgnt.c file's function definitions - Replace unsigned int/long with u32 where applicable (where access to 32 bit registers is performed) - Refactor code in mtip_{read|write}_atable() to be more readable - Remove code added for not (yet) supported IMX's vf610 SoC - Remove do { } while(); loop from mtip_interrupt() function - Introduce MTIP_PORT_FORWARDING_INIT to indicate intial value for port forwarding - Replace 'unsigned long' to 'u32' in mtipl2sw.h - Replace 'unsigned short' to 'u16' in mtipl2sw.h - use %#x in dev_dbg() - Call SET_NETDEV_DEV() macro to set network device' parent - otherwise phy_attach_direct() will fail. Changes for v6: - Use dev_name(&pdev->dev) when requesting IRQ (to be in sync with other subsystems) - Use platform_get_irq_byname() for beter readability - Replace ARCH_MXS with SOC_IMX28 - Replace 2048 with MTIP_ATABLE_MEM_NUM_ENTRIES - Remove check if fep == NULL in mtip_aging_timer() as timer can be setup only after the fep structure is allocated and already filled durring probe() execution --- MAINTAINERS | 7 + drivers/net/ethernet/freescale/Kconfig | 1 + drivers/net/ethernet/freescale/Makefile | 1 + drivers/net/ethernet/freescale/mtipsw/Kconfig | 13 + .../net/ethernet/freescale/mtipsw/Makefile | 3 + .../net/ethernet/freescale/mtipsw/mtipl2sw.c | 1989 +++++++++++++++++ .../net/ethernet/freescale/mtipsw/mtipl2sw.h | 788 +++++++ .../ethernet/freescale/mtipsw/mtipl2sw_br.c | 120 + .../ethernet/freescale/mtipsw/mtipl2sw_mgnt.c | 449 ++++ 9 files changed, 3371 insertions(+) create mode 100644 drivers/net/ethernet/freescale/mtipsw/Kconfig create mode 100644 drivers/net/ethernet/freescale/mtipsw/Makefile create mode 100644 drivers/net/ethernet/freescale/mtipsw/mtipl2sw.c create mode 100644 drivers/net/ethernet/freescale/mtipsw/mtipl2sw.h create mode 100644 drivers/net/ethernet/freescale/mtipsw/mtipl2sw_br.c create mode 100644 drivers/net/ethernet/freescale/mtipsw/mtipl2sw_mgnt.c diff --git a/MAINTAINERS b/MAINTAINERS index 657a67f9031e..6a097ab68fc3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9449,6 +9449,13 @@ S: Maintained F: Documentation/devicetree/bindings/i2c/i2c-mpc.yaml F: drivers/i2c/busses/i2c-mpc.c +FREESCALE MTIP ETHERNET SWITCH DRIVER +M: Lukasz Majewski +L: netdev@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/net/nxp,imx28-mtip-switch.yaml +F: drivers/net/ethernet/freescale/mtipsw/* + FREESCALE QORIQ DPAA ETHERNET DRIVER M: Madalin Bucur L: netdev@vger.kernel.org diff --git a/drivers/net/ethernet/freescale/Kconfig b/drivers/net/ethernet/freescale/Kconfig index a2d7300925a8..056a11c3a74e 100644 --- a/drivers/net/ethernet/freescale/Kconfig +++ b/drivers/net/ethernet/freescale/Kconfig @@ -60,6 +60,7 @@ config FEC_MPC52xx_MDIO source "drivers/net/ethernet/freescale/fs_enet/Kconfig" source "drivers/net/ethernet/freescale/fman/Kconfig" +source "drivers/net/ethernet/freescale/mtipsw/Kconfig" config FSL_PQ_MDIO tristate "Freescale PQ MDIO" diff --git a/drivers/net/ethernet/freescale/Makefile b/drivers/net/ethernet/freescale/Makefile index de7b31842233..0e6cacb0948a 100644 --- a/drivers/net/ethernet/freescale/Makefile +++ b/drivers/net/ethernet/freescale/Makefile @@ -25,3 +25,4 @@ obj-$(CONFIG_FSL_DPAA_ETH) += dpaa/ obj-$(CONFIG_FSL_DPAA2_ETH) += dpaa2/ obj-y += enetc/ +obj-y += mtipsw/ diff --git a/drivers/net/ethernet/freescale/mtipsw/Kconfig b/drivers/net/ethernet/freescale/mtipsw/Kconfig new file mode 100644 index 000000000000..a6fbdb59854f --- /dev/null +++ b/drivers/net/ethernet/freescale/mtipsw/Kconfig @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-2.0-only +config FEC_MTIP_L2SW + tristate "MoreThanIP L2 switch support to FEC driver" + depends on OF + depends on NET_SWITCHDEV + depends on BRIDGE + depends on SOC_IMX28 || COMPILE_TEST + help + This enables support for the MoreThan IP L2 switch on i.MX + SoCs (e.g. iMX287). It offloads bridging to this IP block's + hardware and allows switch management with standard Linux tools. + This switch driver can be used interchangeable with the already + available FEC driver, depending on the use case's requirements. diff --git a/drivers/net/ethernet/freescale/mtipsw/Makefile b/drivers/net/ethernet/freescale/mtipsw/Makefile new file mode 100644 index 000000000000..4d69db2226a6 --- /dev/null +++ b/drivers/net/ethernet/freescale/mtipsw/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_FEC_MTIP_L2SW) += mtipl2sw.o mtipl2sw_mgnt.o mtipl2sw_br.o diff --git a/drivers/net/ethernet/freescale/mtipsw/mtipl2sw.c b/drivers/net/ethernet/freescale/mtipsw/mtipl2sw.c new file mode 100644 index 000000000000..62d0ba75ec3e --- /dev/null +++ b/drivers/net/ethernet/freescale/mtipsw/mtipl2sw.c @@ -0,0 +1,1989 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * L2 switch Controller (Ethernet L2 switch) driver for MTIP block. + * + * 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 Reserved. + * Shrek Wu (B16972@freescale.com) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mtipl2sw.h" + +static void swap_buffer(void *bufaddr, int len) +{ + int i; + unsigned int *buf = bufaddr; + + for (i = 0; i < len; i += 4, buf++) + swab32s(buf); +} + +struct mtip_devinfo { + u32 quirks; +}; + +static void mtip_enet_init(struct switch_enet_private *fep, int port) +{ + void __iomem *enet_addr = fep->enet_addr; + u32 mii_speed, holdtime, reg; + + if (port == 2) + enet_addr += MCF_ESW_ENET_PORT_OFFSET; + + reg = MCF_FEC_RCR_PROM | MCF_FEC_RCR_MII_MODE | + MCF_FEC_RCR_MAX_FL(1522); + + if (fep->phy_interface[port - 1] == PHY_INTERFACE_MODE_RMII) + reg |= MCF_FEC_RCR_RMII_MODE; + + writel(reg, enet_addr + MCF_FEC_RCR); + + writel(MCF_FEC_TCR_FDEN, enet_addr + MCF_FEC_TCR); + writel(MCF_FEC_ECR_ETHER_EN, enet_addr + MCF_FEC_ECR); + + mii_speed = DIV_ROUND_UP(clk_get_rate(fep->clk_ipg), 5000000); + mii_speed--; + + holdtime = DIV_ROUND_UP(clk_get_rate(fep->clk_ipg), 100000000) - 1; + + fep->phy_speed = mii_speed << 1 | holdtime << 8; + + writel(fep->phy_speed, enet_addr + MCF_FEC_MSCR); +} + +static void mtip_setup_mac(struct net_device *dev) +{ + struct mtip_ndev_priv *priv = netdev_priv(dev); + struct switch_enet_private *fep = priv->fep; + unsigned char *iap, mac_addr[ETH_ALEN]; + + /* Use MAC address from DTS */ + iap = &fep->mac[priv->portnum - 1][0]; + + /* Use MAC address set by bootloader */ + if (!is_valid_ether_addr(iap)) { + *((__be32 *)&mac_addr[0]) = + cpu_to_be32(readl(fep->enet_addr + MCF_FEC_PALR)); + *((__be16 *)&mac_addr[4]) = + cpu_to_be16(readl(fep->enet_addr + + MCF_FEC_PAUR) >> 16); + iap = &mac_addr[0]; + } + + /* Use random MAC address */ + if (!is_valid_ether_addr(iap)) { + eth_hw_addr_random(dev); + dev_info(&fep->pdev->dev, "Using random MAC address: %pM\n", + dev->dev_addr); + iap = (unsigned char *)dev->dev_addr; + } + + /* Adjust MAC if using macaddr (and increment if needed) */ + eth_hw_addr_gen(dev, iap, priv->portnum - 1); +} + +/** + * crc8_calc - calculate CRC for MAC storage + * + * @pmacaddress: A 6-byte array with the MAC address. The first byte is + * the first byte transmitted. + * + * Calculate Galois Field Arithmetic CRC for Polynom x^8+x^2+x+1. + * It omits the final shift in of 8 zeroes a "normal" CRC would do + * (getting the remainder). + * + * Examples (hexadecimal values):
+ * 10-11-12-13-14-15 => CRC=0xc2 + * 10-11-cc-dd-ee-00 => CRC=0xe6 + * + * Return: The 8-bit CRC in bits 7:0 + */ +static int crc8_calc(unsigned char *pmacaddress) +{ + int byt; /* byte index */ + int bit; /* bit index */ + int crc = 0x12; + int inval; + + for (byt = 0; byt < ETH_ALEN; byt++) { + inval = (((int)pmacaddress[byt]) & 0xFF); + /* shift bit 0 to bit 8 so all our bits + * travel through bit 8 + * (simplifies below calc) + */ + inval <<= 8; + + for (bit = 0; bit < 8; bit++) { + /* next input bit comes into d7 after shift */ + crc |= inval & 0x100; + if (crc & 0x01) + /* before shift */ + crc ^= 0x1C0; + + crc >>= 1; + inval >>= 1; + } + } + /* upper bits are clean as we shifted in zeroes! */ + return crc; +} + +static void mtip_read_atable(struct switch_enet_private *fep, int index, + u32 *read_lo, u32 *read_hi) +{ + struct addr_table64b_entry *atable_base = + fep->hwentry->mtip_table64b_entry; + + *read_lo = readl(&atable_base[index].lo); + *read_hi = readl(&atable_base[index].hi); +} + +static void mtip_write_atable(struct switch_enet_private *fep, int index, + u32 write_lo, u32 write_hi) +{ + struct addr_table64b_entry *atable_base = + fep->hwentry->mtip_table64b_entry; + + writel(write_lo, &atable_base[index].lo); + writel(write_hi, &atable_base[index].hi); +} + +/** + * mtip_portinfofifo_read - Read element from receive FIFO + * + * @fep: Structure describing switch + * + * Read one element from the HW receive FIFO (Queue) + * if available and return it. + * + * Return: mtip_port_info or NULL if no data is available. + */ +static +struct mtip_port_info *mtip_portinfofifo_read(struct switch_enet_private *fep) +{ + struct mtip_port_info *info = &fep->g_info; + struct switch_t *fecp = fep->hwp; + u32 reg; + + reg = readl(&fecp->ESW_LSR); + if (reg == 0) { + dev_dbg(&fep->pdev->dev, "%s: ESW_LSR = 0x%x\n", __func__, reg); + return NULL; + } + + /* read word from FIFO */ + info->maclo = readl(&fecp->ESW_LREC0); + if (info->maclo == 0) { + dev_dbg(&fep->pdev->dev, "%s: mac lo 0x%x\n", __func__, + info->maclo); + return NULL; + } + + /* read 2nd word from FIFO */ + reg = readl(&fecp->ESW_LREC1); + info->machi = reg & 0xFFFF; + info->hash = (reg >> 16) & 0xFF; + info->port = (reg >> 24) & 0xF; + + return info; +} + +static void mtip_atable_get_entry_port_number(struct switch_enet_private *fep, + unsigned char *mac_addr, u8 *port) +{ + int block_index, block_index_end, entry; + u32 mac_addr_lo, mac_addr_hi; + u32 read_lo, read_hi; + + mac_addr_lo = (u32)((mac_addr[3] << 24) | (mac_addr[2] << 16) | + (mac_addr[1] << 8) | mac_addr[0]); + mac_addr_hi = (u32)((mac_addr[5] << 8) | (mac_addr[4])); + + block_index = GET_BLOCK_PTR(crc8_calc(mac_addr)); + block_index_end = block_index + ATABLE_ENTRY_PER_SLOT; + + /* now search all the entries in the selected block */ + for (entry = block_index; entry < block_index_end; entry++) { + mtip_read_atable(fep, entry, &read_lo, &read_hi); + *port = MTIP_PORT_FORWARDING_INIT; + + if (read_lo == mac_addr_lo && + ((read_hi & 0x0000FFFF) == + (mac_addr_hi & 0x0000FFFF))) { + /* found the correct address */ + if ((read_hi & (1 << 16)) && (!(read_hi & (1 << 17)))) + *port = AT_EXTRACT_PORT(read_hi); + break; + } + } + + dev_dbg(&fep->pdev->dev, "%s: MAC: %pM PORT: 0x%x\n", __func__, + mac_addr, *port); +} + +/* Clear complete MAC Look Up Table */ +void mtip_clear_atable(struct switch_enet_private *fep) +{ + int index; + + for (index = 0; index < MTIP_ATABLE_MEM_NUM_ENTRIES; index++) + mtip_write_atable(fep, index, 0, 0); +} + +/** + * mtip_update_atable_static - Update switch static address table + * + * @mac_addr: Pointer to the array containing MAC address to + * be put as static entry + * @port: Port bitmask numbers to be added in static entry, + * valid values are 1-7 + * @priority: The priority for the static entry in table + * + * @fep: Pointer to the structure describing the switch + * + * Updates MAC address lookup table with a static entry. + * + * Searches if the MAC address is already there in the block and replaces + * the older entry with the new one. If MAC address is not there then puts + * a new entry in the first empty slot available in the block. + * + * Return: 0 for a successful update else -ENOSPC when no slot available + */ +static int mtip_update_atable_static(unsigned char *mac_addr, unsigned int port, + unsigned int priority, + struct switch_enet_private *fep) +{ + unsigned long block_index, entry, index_end; + u32 write_lo, write_hi, read_lo, read_hi; + + write_lo = (u32)((mac_addr[3] << 24) | (mac_addr[2] << 16) | + (mac_addr[1] << 8) | mac_addr[0]); + write_hi = (u32)(0 | (port << AT_SENTRY_PORTMASK_shift) | + (priority << AT_SENTRY_PRIO_shift) | + (AT_ENTRY_TYPE_STATIC << AT_ENTRY_TYPE_shift) | + (AT_ENTRY_RECORD_VALID << AT_ENTRY_VALID_shift) | + (mac_addr[5] << 8) | (mac_addr[4])); + + block_index = GET_BLOCK_PTR(crc8_calc(mac_addr)); + index_end = block_index + ATABLE_ENTRY_PER_SLOT; + /* Now search all the entries in the selected block */ + for (entry = block_index; entry < index_end; entry++) { + mtip_read_atable(fep, entry, &read_lo, &read_hi); + /* MAC address matched, so update the + * existing entry + * even if its a dynamic one + */ + if (read_lo == write_lo && + ((read_hi & 0x0000FFFF) == + (write_hi & 0x0000FFFF))) { + mtip_write_atable(fep, entry, write_lo, write_hi); + return 0; + } else if (!(read_hi & (1 << 16))) { + /* Fill this empty slot (valid bit zero), + * assuming no holes in the block + */ + mtip_write_atable(fep, entry, write_lo, write_hi); + fep->at_curr_entries++; + return 0; + } + } + + /* No space available for this static entry */ + return -ENOSPC; +} + +static bool mtip_update_atable_dynamic1(u32 write_lo, u32 write_hi, + int block_index, unsigned int port, + unsigned int curr_time, + struct switch_enet_private *fep) +{ + unsigned long entry, index_end; + int time, timeold, indexold; + u32 read_lo, read_hi; + unsigned long conf; + + /* prepare update port and timestamp */ + conf = AT_ENTRY_RECORD_VALID << AT_ENTRY_VALID_shift; + conf |= AT_ENTRY_TYPE_DYNAMIC << AT_ENTRY_TYPE_shift; + conf |= curr_time << AT_DENTRY_TIME_shift; + conf |= port << AT_DENTRY_PORT_shift; + conf |= write_hi; + + /* linear search through all slot + * entries and update if found + */ + index_end = block_index + ATABLE_ENTRY_PER_SLOT; + /* Now search all the entries in the selected block */ + for (entry = block_index; entry < index_end; entry++) { + mtip_read_atable(fep, entry, &read_lo, &read_hi); + if (read_lo == write_lo && + ((read_hi & 0x0000FFFF) == + (write_hi & 0x0000FFFF))) { + /* found correct address, + * update timestamp. + */ + mtip_write_atable(fep, entry, write_lo, conf); + + return false; + } else if (!(read_hi & (1 << 16))) { + /* slot is empty, then use it + * for new entry + * Note: There are no holes, + * therefore cannot be any + * more that need to be compared. + */ + mtip_write_atable(fep, entry, write_lo, conf); + /* statistics (we do it between writing + * .hi an .lo due to + * hardware limitation... + */ + fep->at_curr_entries++; + /* newly inserted */ + + return true; + } + } + + /* No more entry available in block overwrite oldest */ + timeold = 0; + indexold = 0; + for (entry = block_index; entry < index_end; entry++) { + mtip_read_atable(fep, entry, &read_lo, &read_hi); + time = AT_EXTRACT_TIMESTAMP(read_hi); + dev_dbg(&fep->pdev->dev, "%s : time %x currtime %x\n", + __func__, time, curr_time); + time = TIMEDELTA(curr_time, time); + if (time > timeold) { + /* is it older ? */ + timeold = time; + indexold = entry; + } + } + + mtip_write_atable(fep, indexold, write_lo, conf); + + /* Statistics (do it inbetween writing to .lo and .hi */ + fep->at_block_overflows++; + dev_err(&fep->pdev->dev, "%s update time, at_block_overflows %x\n", + __func__, fep->at_block_overflows); + /* newly inserted */ + return true; +} + +/* dynamicms MAC address table learn and migration */ +static void +mtip_atable_dynamicms_learn_migration(struct switch_enet_private *fep, + int curr_time, unsigned char *mac, + u8 *rx_port) +{ + u8 port = MTIP_PORT_FORWARDING_INIT; + struct mtip_port_info *port_info; + u32 rx_mac_lo, rx_mac_hi; + unsigned long flags; + int index; + + spin_lock_irqsave(&fep->learn_lock, flags); + + if (mac && is_valid_ether_addr(mac)) { + rx_mac_lo = (u32)((mac[3] << 24) | (mac[2] << 16) | + (mac[1] << 8) | mac[0]); + rx_mac_hi = (u32)((mac[5] << 8) | (mac[4])); + } + + port_info = mtip_portinfofifo_read(fep); + while (port_info) { + /* get block index from lookup table */ + index = GET_BLOCK_PTR(port_info->hash); + mtip_update_atable_dynamic1(port_info->maclo, port_info->machi, + index, port_info->port, + curr_time, fep); + + if (mac && is_valid_ether_addr(mac) && + port == MTIP_PORT_FORWARDING_INIT) { + if (rx_mac_lo == port_info->maclo && + rx_mac_hi == port_info->machi) { + /* The newly learned MAC is the source of + * our filtered frame. + */ + port = (u8)port_info->port; + } + } + port_info = mtip_portinfofifo_read(fep); + } + + if (rx_port) + *rx_port = port; + + spin_unlock_irqrestore(&fep->learn_lock, flags); +} + +static void mtip_aging_timer(struct timer_list *t) +{ + struct switch_enet_private *fep = from_timer(fep, t, timer_aging); + + fep->curr_time = mtip_timeincrement(fep->curr_time); + + mod_timer(&fep->timer_aging, + jiffies + msecs_to_jiffies(LEARNING_AGING_INTERVAL)); +} + +static void esw_mac_addr_static(struct switch_enet_private *fep) +{ + int i; + + for (i = 0; i < SWITCH_EPORT_NUMBER; i++) + mtip_update_atable_static((unsigned char *) + fep->ndev[i]->dev_addr, 7, 7, fep); +} + +static void mtip_config_switch(struct switch_enet_private *fep) +{ + struct switch_t *fecp = fep->hwp; + + esw_mac_addr_static(fep); + + writel(0, &fecp->ESW_BKLR); + + /* 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, + &fecp->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); +} + +static netdev_tx_t mtip_start_xmit_port(struct sk_buff *skb, + struct net_device *dev, int port) +{ + struct mtip_ndev_priv *priv = netdev_priv(dev); + struct switch_enet_private *fep = priv->fep; + struct switch_t *fecp = fep->hwp; + unsigned short status; + unsigned long flags; + struct cbd_t *bdp; + void *bufaddr; + + spin_lock_irqsave(&fep->hw_lock, flags); + + if (!fep->link[0] && !fep->link[1]) { + /* Link is down or autonegotiation is in progress. */ + netif_stop_queue(dev); + spin_unlock_irqrestore(&fep->hw_lock, flags); + return NETDEV_TX_BUSY; + } + + /* Fill in a Tx ring entry */ + bdp = fep->cur_tx; + + status = bdp->cbd_sc; + + if (status & BD_ENET_TX_READY) { + /* All transmit buffers are full. Bail out. + * This should not happen, since dev->tbusy should be set. + */ + dev_err(&fep->pdev->dev, "%s: tx queue full!.\n", dev->name); + spin_unlock_irqrestore(&fep->hw_lock, flags); + return NETDEV_TX_BUSY; + } + + /* Clear all of the status flags */ + status &= ~BD_ENET_TX_STATS; + + /* Set buffer length and buffer pointer */ + bufaddr = skb->data; + bdp->cbd_datlen = skb->len; + + /* On some FEC implementations data must be aligned on + * 4-byte boundaries. Use bounce buffers to copy data + * and get it aligned. + */ + if ((unsigned long)bufaddr & MTIP_ALIGNMENT) { + unsigned int index; + + index = bdp - fep->tx_bd_base; + memcpy(fep->tx_bounce[index], + (void *)skb->data, skb->len); + bufaddr = fep->tx_bounce[index]; + } + + if (fep->quirks & FEC_QUIRK_SWAP_FRAME) + swap_buffer(bufaddr, skb->len); + + /* Save skb pointer. */ + fep->tx_skbuff[fep->skb_cur] = skb; + + dev->stats.tx_bytes += skb->len; + fep->skb_cur = (fep->skb_cur + 1) & TX_RING_MOD_MASK; + + /* Push the data cache so the CPM does not get stale memory + * data. + */ + bdp->cbd_bufaddr = dma_map_single(&fep->pdev->dev, bufaddr, + MTIP_SWITCH_TX_FRSIZE, + DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(&fep->pdev->dev, bdp->cbd_bufaddr))) { + dev_err(&fep->pdev->dev, + "Failed to map descriptor tx buffer\n"); + dev->stats.tx_errors++; + dev->stats.tx_dropped++; + dev_kfree_skb_any(skb); + goto err; + } + + /* Send it on its way. Tell FEC it's ready, interrupt when done, + * it's the last BD of the frame, and to put the CRC on the end. + */ + + status |= (BD_ENET_TX_READY | BD_ENET_TX_INTR + | BD_ENET_TX_LAST | BD_ENET_TX_TC); + bdp->cbd_sc = status; + + netif_trans_update(dev); + skb_tx_timestamp(skb); + + /* For port separation - force sending via specified port */ + if (!fep->br_offload && port != 0) + mtip_forced_forward(fep, port, 1); + + /* Trigger transmission start */ + writel(MCF_ESW_TDAR_X_DES_ACTIVE, &fecp->ESW_TDAR); + + /* If this was the last BD in the ring, + * start at the beginning again. + */ + if (status & BD_ENET_TX_WRAP) + bdp = fep->tx_bd_base; + else + bdp++; + + if (bdp == fep->dirty_tx) { + fep->tx_full = 1; + netif_stop_queue(dev); + } + + fep->cur_tx = bdp; + err: + spin_unlock_irqrestore(&fep->hw_lock, flags); + + return NETDEV_TX_OK; +} + +static netdev_tx_t mtip_start_xmit(struct sk_buff *skb, + struct net_device *dev) +{ + struct mtip_ndev_priv *priv = netdev_priv(dev); + + return mtip_start_xmit_port(skb, dev, priv->portnum); +} + +static void mtip_configure_enet_mii(struct switch_enet_private *fep, int port) +{ + struct phy_device *phydev = fep->phy_dev[port - 1]; + struct net_device *dev = fep->ndev[port - 1]; + void __iomem *enet_addr = fep->enet_addr; + int duplex = fep->full_duplex[port - 1]; + u32 rcr; + + if (port == 2) + enet_addr += MCF_ESW_ENET_PORT_OFFSET; + + /* ECR */ + writel(MCF_FEC_ECR_MAGIC_ENA, enet_addr + MCF_FEC_ECR); + + /* EMRBR */ + writel(PKT_MAXBLR_SIZE, enet_addr + MCF_FEC_EMRBR); + + /* set the receive and transmit BDs ring base to + * hardware registers(ERDSR & ETDSR) + */ + writel(fep->bd_dma, enet_addr + MCF_FEC_ERDSR); + writel((unsigned long)fep->bd_dma + sizeof(struct cbd_t) * RX_RING_SIZE, + enet_addr + MCF_FEC_ETDSR); + + writel(fep->phy_speed, enet_addr + MCF_FEC_MSCR); + + /* EIR */ + writel(0, enet_addr + MCF_FEC_EIR); + + /* IAUR */ + writel(0, enet_addr + MCF_FEC_IAUR); + + /* IALR */ + writel(0, enet_addr + MCF_FEC_IALR); + + /* GAUR */ + writel(0, enet_addr + MCF_FEC_GAUR); + + /* GALR */ + writel(0, enet_addr + MCF_FEC_GALR); + + /* EMRBR */ + writel(PKT_MAXBLR_SIZE, enet_addr + MCF_FEC_EMRBR); + + /* EIMR */ + writel(0, enet_addr + MCF_FEC_EIMR); + + /* PALR PAUR */ + /* Set the station address for the ENET Adapter */ + writel(dev->dev_addr[3] | + dev->dev_addr[2] << 8 | + dev->dev_addr[1] << 16 | + dev->dev_addr[0] << 24, enet_addr + MCF_FEC_PALR); + writel(dev->dev_addr[5] << 16 | + (dev->dev_addr[4] + (unsigned char)(0)) << 24, + enet_addr + MCF_FEC_PAUR); + + /* RCR */ + rcr = readl(enet_addr + MCF_FEC_RCR); + if (phydev && phydev->speed == SPEED_100) + rcr &= ~MCF_FEC_RCR_RMII_10BASET; + else + rcr |= MCF_FEC_RCR_RMII_10BASET; + + if (duplex == DUPLEX_FULL) + rcr &= ~MCF_FEC_RCR_DRT; + else + rcr |= MCF_FEC_RCR_DRT; + + writel(rcr, enet_addr + MCF_FEC_RCR); + + /* TCR */ + if (duplex == DUPLEX_FULL) + writel(0x1C, enet_addr + MCF_FEC_TCR); + else + writel(0x18, enet_addr + MCF_FEC_TCR); + + /* ECR */ + writel(readl(enet_addr + MCF_FEC_ECR) | MCF_FEC_ECR_ETHER_EN, + enet_addr + MCF_FEC_ECR); +} + +/* This function is called to start or restart the FEC during a link + * change. This only happens when switching between half and full + * duplex. + */ +static void mtip_switch_restart(struct net_device *dev, int duplex0, + int duplex1) +{ + struct mtip_ndev_priv *priv = netdev_priv(dev); + struct switch_enet_private *fep = priv->fep; + struct switch_t *fecp = fep->hwp; + int i; + + /* Perform a reset. We should wait for this. */ + writel(MCF_ESW_MODE_SW_RST, &fecp->ESW_MODE); + + /* Delay of 10us specified in the documentation to perform + * SW reset by the switch internally. + */ + udelay(10); + writel(MCF_ESW_MODE_STATRST, &fecp->ESW_MODE); + writel(MCF_ESW_MODE_SW_EN, &fecp->ESW_MODE); + + /* Management port configuration, + * make port 0 as management port + */ + writel(0, &fecp->ESW_BMPC); + + /* Clear any outstanding interrupt */ + writel(0xFFFFFFFF, &fecp->ESW_ISR); + + /* Set backpressure threshold to minimize discarded frames + * during due to congestion. + */ + writel(P0BC_THRESHOLD, &fecp->ESW_P0BCT); + + /* Set maximum receive buffer size */ + writel(PKT_MAXBLR_SIZE, &fecp->ESW_MRBR); + + /* Set receive and transmit descriptor base */ + writel(fep->bd_dma, &fecp->ESW_RDSR); + writel((unsigned long)fep->bd_dma + + sizeof(struct cbd_t) * RX_RING_SIZE, + &fecp->ESW_TDSR); + + fep->cur_tx = fep->tx_bd_base; + fep->cur_rx = fep->rx_bd_base; + fep->dirty_tx = fep->cur_tx; + + /* Reset SKB transmit buffers */ + fep->skb_cur = 0; + fep->skb_dirty = 0; + for (i = 0; i <= TX_RING_MOD_MASK; i++) { + if (fep->tx_skbuff[i]) { + dev_kfree_skb_any(fep->tx_skbuff[i]); + fep->tx_skbuff[i] = NULL; + } + } + + fep->full_duplex[0] = duplex0; + fep->full_duplex[1] = duplex1; + + mtip_configure_enet_mii(fep, 1); + mtip_configure_enet_mii(fep, 2); + mtip_clear_atable(fep); + + /* And last, enable the transmit and receive processing */ + writel(MCF_ESW_RDAR_R_DES_ACTIVE, &fecp->ESW_RDAR); + + /* Enable interrupts we wish to service */ + writel(0xFFFFFFFF, &fecp->ESW_ISR); + writel(MCF_ESW_IMR_TXF | MCF_ESW_IMR_RXF, + &fecp->ESW_IMR); + + mtip_config_switch(fep); +} + +static void mtip_timeout(struct net_device *dev, unsigned int txqueue) +{ + struct mtip_ndev_priv *priv = netdev_priv(dev); + struct switch_enet_private *fep = priv->fep; + struct cbd_t *bdp; + int i; + + dev->stats.tx_errors++; + + if (IS_ENABLED(CONFIG_SWITCH_DEBUG)) { + dev_info(&dev->dev, "%s: transmit timed out.\n", dev->name); + dev_info(&dev->dev, + "Ring data: cur_tx %lx%s, dirty_tx %lx cur_rx: %lx\n", + (unsigned long)fep->cur_tx, + fep->tx_full ? " (full)" : "", + (unsigned long)fep->dirty_tx, + (unsigned long)fep->cur_rx); + + bdp = fep->tx_bd_base; + dev_info(&dev->dev, " tx: %u buffers\n", TX_RING_SIZE); + for (i = 0; i < TX_RING_SIZE; i++) { + dev_info(&dev->dev, " %08x: %04x %04x %08x\n", + (uint)bdp, bdp->cbd_sc, bdp->cbd_datlen, + (int)bdp->cbd_bufaddr); + bdp++; + } + + bdp = fep->rx_bd_base; + dev_info(&dev->dev, " rx: %lu buffers\n", + (unsigned long)RX_RING_SIZE); + for (i = 0 ; i < RX_RING_SIZE; i++) { + dev_info(&dev->dev, " %08x: %04x %04x %08x\n", + (uint)bdp, + bdp->cbd_sc, bdp->cbd_datlen, + (int)bdp->cbd_bufaddr); + bdp++; + } + } + + rtnl_lock(); + if (netif_device_present(dev) || netif_running(dev)) { + napi_disable(&fep->napi); + netif_tx_lock_bh(dev); + mtip_switch_restart(dev, fep->full_duplex[0], + fep->full_duplex[1]); + netif_tx_wake_all_queues(dev); + netif_tx_unlock_bh(dev); + napi_enable(&fep->napi); + } + rtnl_unlock(); +} + +static irqreturn_t mtip_interrupt(int irq, void *ptr_fep) +{ + struct switch_enet_private *fep = ptr_fep; + struct switch_t *fecp = fep->hwp; + irqreturn_t ret = IRQ_NONE; + u32 int_events, int_imask; + + /* Get the interrupt events that caused us to be here */ + int_events = readl(&fecp->ESW_ISR); + writel(int_events, &fecp->ESW_ISR); + + if (int_events & (MCF_ESW_ISR_RXF | MCF_ESW_ISR_TXF)) { + ret = IRQ_HANDLED; + /* Disable the RX interrupt */ + if (napi_schedule_prep(&fep->napi)) { + int_imask = readl(&fecp->ESW_IMR); + int_imask &= ~MCF_ESW_IMR_RXF; + writel(int_imask, &fecp->ESW_IMR); + __napi_schedule(&fep->napi); + } + } + + return ret; +} + +static void mtip_switch_tx(struct net_device *dev) +{ + struct mtip_ndev_priv *priv = netdev_priv(dev); + struct switch_enet_private *fep = priv->fep; + unsigned short status; + struct sk_buff *skb; + unsigned long flags; + struct cbd_t *bdp; + + spin_lock_irqsave(&fep->hw_lock, flags); + bdp = fep->dirty_tx; + + while (((status = bdp->cbd_sc) & BD_ENET_TX_READY) == 0) { + if (bdp == fep->cur_tx && fep->tx_full == 0) + break; + + dma_unmap_single(&fep->pdev->dev, bdp->cbd_bufaddr, + MTIP_SWITCH_TX_FRSIZE, DMA_TO_DEVICE); + bdp->cbd_bufaddr = 0; + skb = fep->tx_skbuff[fep->skb_dirty]; + /* Check for errors */ + if (status & (BD_ENET_TX_HB | BD_ENET_TX_LC | + BD_ENET_TX_RL | BD_ENET_TX_UN | + BD_ENET_TX_CSL)) { + dev->stats.tx_errors++; + if (status & BD_ENET_TX_HB) /* No heartbeat */ + dev->stats.tx_heartbeat_errors++; + if (status & BD_ENET_TX_LC) /* Late collision */ + dev->stats.tx_window_errors++; + if (status & BD_ENET_TX_RL) /* Retrans limit */ + dev->stats.tx_aborted_errors++; + if (status & BD_ENET_TX_UN) /* Underrun */ + dev->stats.tx_fifo_errors++; + if (status & BD_ENET_TX_CSL) /* Carrier lost */ + dev->stats.tx_carrier_errors++; + } else { + dev->stats.tx_packets++; + } + + if (status & BD_ENET_TX_READY) + dev_err(&fep->pdev->dev, + "Enet xmit interrupt and TX_READY.\n"); + + /* Deferred means some collisions occurred during transmit, + * but we eventually sent the packet OK. + */ + if (status & BD_ENET_TX_DEF) + dev->stats.collisions++; + + /* Free the sk buffer associated with this last transmit */ + dev_consume_skb_irq(skb); + fep->tx_skbuff[fep->skb_dirty] = NULL; + fep->skb_dirty = (fep->skb_dirty + 1) & TX_RING_MOD_MASK; + + /* Update pointer to next buffer descriptor to be transmitted */ + if (status & BD_ENET_TX_WRAP) + bdp = fep->tx_bd_base; + else + bdp++; + + /* Since we have freed up a buffer, the ring is no longer + * full. + */ + if (fep->tx_full) { + fep->tx_full = 0; + if (netif_queue_stopped(dev)) + netif_wake_queue(dev); + } + } + fep->dirty_tx = bdp; + spin_unlock_irqrestore(&fep->hw_lock, flags); +} + +/* During a receive, the cur_rx points to the current incoming buffer. + * When we update through the ring, if the next incoming buffer has + * not been given to the system, we just set the empty indicator, + * effectively tossing the packet. + */ +static int mtip_switch_rx(struct net_device *dev, int budget, int *port) +{ + struct mtip_ndev_priv *priv = netdev_priv(dev); + u8 *data, rx_port = MTIP_PORT_FORWARDING_INIT; + struct switch_enet_private *fep = priv->fep; + struct switch_t *fecp = fep->hwp; + unsigned short status, pkt_len; + struct net_device *pndev; + struct ethhdr *eth_hdr; + int pkt_received = 0; + struct sk_buff *skb; + unsigned long flags; + struct cbd_t *bdp; + + spin_lock_irqsave(&fep->hw_lock, flags); + + /* First, grab all of the stats for the incoming packet. + * These get messed up if we get called due to a busy condition. + */ + bdp = fep->cur_rx; + + while (!((status = bdp->cbd_sc) & BD_ENET_RX_EMPTY)) { + if (pkt_received >= budget) + break; + + pkt_received++; + /* Since we have allocated space to hold a complete frame, + * the last indicator should be set. + */ + if ((status & BD_ENET_RX_LAST) == 0) + dev_warn_ratelimited(&dev->dev, + "SWITCH ENET: rcv is not +last\n"); + + if (!fep->usage_count) + goto rx_processing_done; + + /* Check for errors. */ + if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH | BD_ENET_RX_NO | + BD_ENET_RX_CR | BD_ENET_RX_OV)) { + dev->stats.rx_errors++; + if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH)) { + /* Frame too long or too short. */ + dev->stats.rx_length_errors++; + } + if (status & BD_ENET_RX_NO) /* Frame alignment */ + dev->stats.rx_frame_errors++; + if (status & BD_ENET_RX_CR) /* CRC Error */ + dev->stats.rx_crc_errors++; + if (status & BD_ENET_RX_OV) /* FIFO overrun */ + dev->stats.rx_fifo_errors++; + } + + /* Report late collisions as a frame error. + * On this error, the BD is closed, but we don't know what we + * have in the buffer. So, just drop this frame on the floor. + */ + if (status & BD_ENET_RX_CL) { + dev->stats.rx_errors++; + dev->stats.rx_frame_errors++; + goto rx_processing_done; + } + + /* Process the incoming frame */ + pkt_len = bdp->cbd_datlen; + data = (__u8 *)__va(bdp->cbd_bufaddr); + + dma_unmap_single(&fep->pdev->dev, bdp->cbd_bufaddr, + bdp->cbd_datlen, DMA_FROM_DEVICE); + + if (fep->quirks & FEC_QUIRK_SWAP_FRAME) + swap_buffer(data, pkt_len); + + if (data) { + eth_hdr = (struct ethhdr *)data; + mtip_atable_get_entry_port_number(fep, + eth_hdr->h_source, + &rx_port); + if (rx_port == MTIP_PORT_FORWARDING_INIT) + mtip_atable_dynamicms_learn_migration(fep, + fep->curr_time, + eth_hdr->h_source, + &rx_port); + } + + if (!fep->br_offload && (rx_port == 1 || rx_port == 2)) + pndev = fep->ndev[rx_port - 1]; + else + pndev = dev; + + *port = rx_port; + pndev->stats.rx_packets++; + pndev->stats.rx_bytes += pkt_len; + + /* This does 16 byte alignment, exactly what we need. + * The packet length includes FCS, but we don't want to + * include that when passing upstream as it messes up + * bridging applications. + */ + skb = netdev_alloc_skb(pndev, pkt_len + NET_IP_ALIGN); + if (unlikely(!skb)) { + dev_dbg(&fep->pdev->dev, + "%s: Memory squeeze, dropping packet.\n", + pndev->name); + pndev->stats.rx_dropped++; + goto err_mem; + } else { + skb_reserve(skb, NET_IP_ALIGN); + skb_put(skb, pkt_len); /* Make room */ + skb_copy_to_linear_data(skb, data, pkt_len); + skb->protocol = eth_type_trans(skb, pndev); + napi_gro_receive(&fep->napi, skb); + } + + bdp->cbd_bufaddr = dma_map_single(&fep->pdev->dev, data, + bdp->cbd_datlen, + DMA_FROM_DEVICE); + if (unlikely(dma_mapping_error(&fep->pdev->dev, + bdp->cbd_bufaddr))) { + dev_err(&fep->pdev->dev, + "Failed to map descriptor rx buffer\n"); + pndev->stats.rx_errors++; + pndev->stats.rx_dropped++; + dev_kfree_skb_any(skb); + goto err_mem; + } + + rx_processing_done: + /* Clear the status flags for this buffer */ + status &= ~BD_ENET_RX_STATS; + + /* Mark the buffer empty */ + status |= BD_ENET_RX_EMPTY; + bdp->cbd_sc = status; + + /* Update BD pointer to next entry */ + if (status & BD_ENET_RX_WRAP) + bdp = fep->rx_bd_base; + else + bdp++; + + /* Doing this here will keep the FEC running while we process + * incoming frames. On a heavily loaded network, we should be + * able to keep up at the expense of system resources. + */ + writel(MCF_ESW_RDAR_R_DES_ACTIVE, &fecp->ESW_RDAR); + } /* while (!((status = bdp->cbd_sc) & BD_ENET_RX_EMPTY)) */ + + fep->cur_rx = bdp; + spin_unlock_irqrestore(&fep->hw_lock, flags); + + return pkt_received; + + err_mem: + spin_unlock_irqrestore(&fep->hw_lock, flags); + return -ENOMEM; +} + +static void mtip_adjust_link(struct net_device *dev) +{ + struct mtip_ndev_priv *priv = netdev_priv(dev); + struct switch_enet_private *fep = priv->fep; + struct phy_device *phy_dev; + int status_change = 0, idx; + unsigned long flags; + + spin_lock_irqsave(&fep->hw_lock, flags); + + idx = priv->portnum - 1; + phy_dev = fep->phy_dev[idx]; + + /* Duplex link change */ + if (phy_dev->link && fep->full_duplex[idx] != phy_dev->duplex) { + if (idx == 0) + mtip_switch_restart(dev, phy_dev->duplex, + fep->full_duplex[!idx]); + else + mtip_switch_restart(dev, fep->full_duplex[!idx], + phy_dev->duplex); + status_change = 1; + } + + /* Link on or off change */ + if (phy_dev->link != fep->link[idx]) { + fep->link[idx] = phy_dev->link; + if (phy_dev->link) { + if (idx == 0) + mtip_switch_restart(dev, phy_dev->duplex, + fep->full_duplex[!idx]); + else + mtip_switch_restart(dev, fep->full_duplex[!idx], + phy_dev->duplex); + /* if link becomes up and tx be stopped, start it */ + if (netif_queue_stopped(dev)) { + netif_start_queue(dev); + netif_wake_queue(dev); + } + } + status_change = 1; + } + + spin_unlock_irqrestore(&fep->hw_lock, flags); + + if (status_change) + phy_print_status(phy_dev); +} + +static int mtip_mdio_wait(struct switch_enet_private *fep) +{ + uint ievent = 0; + int ret; + + ret = readl_poll_timeout_atomic(fep->enet_addr + MCF_FEC_EIR, ievent, + ievent & MCF_ENET_MII, 2, 30000); + if (!ret) + writel(MCF_ENET_MII, fep->enet_addr + MCF_FEC_EIR); + + return ret; +} + +static int mtip_mdio_read(struct mii_bus *bus, int mii_id, int regnum) +{ + struct switch_enet_private *fep = bus->priv; + int ret; + + /* start a read op */ + writel(FEC_MMFR_ST | FEC_MMFR_OP_READ | + FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(regnum) | + FEC_MMFR_TA, fep->enet_addr + MCF_FEC_MII_DATA); + + /* wait for end of transfer */ + ret = mtip_mdio_wait(fep); + if (ret) { + dev_err(&fep->pdev->dev, "MTIP: MDIO (%s:%d) read timeout\n", + bus->id, mii_id); + return ret; + } + + /* return value */ + return FEC_MMFR_DATA(readl(fep->enet_addr + MCF_FEC_MII_DATA)); +} + +static int mtip_mdio_write(struct mii_bus *bus, int mii_id, int regnum, + u16 value) +{ + struct switch_enet_private *fep = bus->priv; + int ret; + + /* start a write op */ + writel(FEC_MMFR_ST | FEC_MMFR_OP_WRITE | + FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(regnum) | + FEC_MMFR_TA | FEC_MMFR_DATA(value), + fep->enet_addr + MCF_FEC_MII_DATA); + + /* wait for end of transfer */ + ret = mtip_mdio_wait(fep); + if (ret) + dev_err(&fep->pdev->dev, "MTIP: MDIO (%s:%d) write timeout\n", + bus->id, mii_id); + + return ret; +} + +static int mtip_mii_probe(struct net_device *dev) +{ + struct mtip_ndev_priv *priv = netdev_priv(dev); + struct switch_enet_private *fep = priv->fep; + struct phy_device *phy_dev = NULL; + int port_idx = priv->portnum - 1; + + if (fep->phy_np[port_idx]) { + phy_dev = of_phy_connect(dev, fep->phy_np[port_idx], + &mtip_adjust_link, 0, + fep->phy_interface[port_idx]); + if (!phy_dev) { + netdev_err(dev, "Unable to connect to phy\n"); + return -ENODEV; + } + } + + phy_set_max_speed(phy_dev, 100); + fep->phy_dev[port_idx] = phy_dev; + fep->link[port_idx] = 0; + fep->full_duplex[port_idx] = 0; + + dev_dbg(&dev->dev, + "MTIP PHY driver [%s] (mii_bus:phy_addr=%s, irq=%d)\n", + fep->phy_dev[port_idx]->drv->name, + phydev_name(fep->phy_dev[port_idx]), + fep->phy_dev[port_idx]->irq); + + return 0; +} + +static int mtip_mdiobus_reset(struct mii_bus *bus) +{ + if (!bus || !bus->reset_gpiod) { + dev_err(&bus->dev, "Reset GPIO pin not provided!\n"); + return -EINVAL; + } + + gpiod_set_value_cansleep(bus->reset_gpiod, 0); + + /* Extra time to allow: + * 1. GPIO RESET pin go high to prevent situation where its value is + * "LOW" as it is NOT configured. + * 2. The ENET CLK to stabilize before GPIO RESET is asserted + */ + usleep_range(200, 300); + + gpiod_set_value_cansleep(bus->reset_gpiod, 1); + usleep_range(bus->reset_delay_us, bus->reset_delay_us + 1000); + gpiod_set_value_cansleep(bus->reset_gpiod, 0); + + if (bus->reset_post_delay_us > 0) + usleep_range(bus->reset_post_delay_us, + bus->reset_post_delay_us + 1000); + + return 0; +} + +static int mtip_mii_init(struct switch_enet_private *fep, + struct platform_device *pdev) +{ + struct device_node *node; + int err = -ENXIO; + + /* Clear MMFR to avoid to generate MII event by writing MSCR. + * MII event generation condition: + * - writing MSCR: + * - mmfr[31:0]_not_zero & mscr[7:0]_is_zero & + * mscr_reg_data_in[7:0] != 0 + * - writing MMFR: + * - mscr[7:0]_not_zero + */ + writel(0, fep->hwp + MCF_FEC_MII_DATA); + /* Clear any pending transaction complete indication */ + writel(MCF_ENET_MII, fep->enet_addr + MCF_FEC_EIR); + + fep->mii_bus = mdiobus_alloc(); + if (!fep->mii_bus) { + err = -ENOMEM; + goto err_out; + } + + fep->mii_bus->name = "mtip_mii_bus"; + fep->mii_bus->read = mtip_mdio_read; + fep->mii_bus->write = mtip_mdio_write; + fep->mii_bus->reset = mtip_mdiobus_reset; + snprintf(fep->mii_bus->id, MII_BUS_ID_SIZE, "%x", 0); + fep->mii_bus->priv = fep; + fep->mii_bus->parent = &pdev->dev; + + node = of_get_child_by_name(pdev->dev.of_node, "mdio"); + if (node) + dev_err(&fep->pdev->dev, "%s: PHY name: %s\n", + __func__, node->name); + + err = of_mdiobus_register(fep->mii_bus, node); + if (node) + of_node_put(node); + if (err) + goto err_out_free_mdiobus; + + return 0; + +err_out_free_mdiobus: + mdiobus_free(fep->mii_bus); +err_out: + return err; +} + +static void mtip_mii_remove(struct switch_enet_private *fep) +{ + int i; + + for (i = 0; i < SWITCH_EPORT_NUMBER; i++) { + if (fep->phy_np[i]) + of_node_put(fep->phy_np[i]); + + if (fep->phy_dev[i]) + phy_disconnect(fep->phy_dev[i]); + } + + mdiobus_unregister(fep->mii_bus); + mdiobus_free(fep->mii_bus); +} + +static void mtip_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + struct mtip_ndev_priv *priv = netdev_priv(dev); + struct switch_enet_private *fep = priv->fep; + + strscpy(info->driver, fep->pdev->dev.driver->name, + sizeof(info->driver)); + strscpy(info->bus_info, dev_name(&dev->dev), + sizeof(info->bus_info)); +} + +static void mtip_free_buffers(struct net_device *dev) +{ + struct mtip_ndev_priv *priv = netdev_priv(dev); + struct switch_enet_private *fep = priv->fep; + struct sk_buff *skb; + struct cbd_t *bdp; + int i; + + bdp = fep->rx_bd_base; + for (i = 0; i < RX_RING_SIZE; i++) { + skb = fep->rx_skbuff[i]; + + if (bdp->cbd_bufaddr) + dma_unmap_single(&fep->pdev->dev, bdp->cbd_bufaddr, + MTIP_SWITCH_RX_FRSIZE, + DMA_FROM_DEVICE); + if (skb) + dev_kfree_skb(skb); + bdp++; + } + + bdp = fep->tx_bd_base; + for (i = 0; i < TX_RING_SIZE; i++) + kfree(fep->tx_bounce[i]); +} + +static int mtip_alloc_buffers(struct net_device *dev) +{ + struct mtip_ndev_priv *priv = netdev_priv(dev); + struct switch_enet_private *fep = priv->fep; + struct sk_buff *skb; + struct cbd_t *bdp; + int i; + + bdp = fep->rx_bd_base; + for (i = 0; i < RX_RING_SIZE; i++) { + skb = netdev_alloc_skb(dev, MTIP_SWITCH_RX_FRSIZE); + if (!skb) + goto err; + + fep->rx_skbuff[i] = skb; + + bdp->cbd_bufaddr = dma_map_single(&fep->pdev->dev, skb->data, + MTIP_SWITCH_RX_FRSIZE, + DMA_FROM_DEVICE); + if (unlikely(dma_mapping_error(&fep->pdev->dev, + bdp->cbd_bufaddr))) { + dev_err(&fep->pdev->dev, + "Failed to map descriptor rx buffer\n"); + dev_kfree_skb_any(skb); + goto err; + } + + bdp->cbd_sc = BD_ENET_RX_EMPTY; + bdp++; + } + + /* Set the last buffer to wrap. */ + bdp--; + bdp->cbd_sc |= BD_SC_WRAP; + + bdp = fep->tx_bd_base; + for (i = 0; i < TX_RING_SIZE; i++) { + fep->tx_bounce[i] = kmalloc(MTIP_SWITCH_TX_FRSIZE, GFP_KERNEL); + + bdp->cbd_sc = 0; + bdp->cbd_bufaddr = 0; + bdp++; + } + + /* Set the last buffer to wrap. */ + bdp--; + bdp->cbd_sc |= BD_SC_WRAP; + + return 0; + + err: + mtip_free_buffers(dev); + return -ENOMEM; +} + +static int mtip_rx_napi(struct napi_struct *napi, int budget) +{ + struct mtip_ndev_priv *priv = netdev_priv(napi->dev); + struct switch_enet_private *fep = priv->fep; + struct switch_t *fecp = fep->hwp; + int pkts, port; + + pkts = mtip_switch_rx(napi->dev, budget, &port); + if (pkts == -ENOMEM) { + napi_complete(napi); + return 0; + } + + if (!fep->br_offload && + (port == 1 || port == 2) && fep->ndev[port - 1]) + mtip_switch_tx(fep->ndev[port - 1]); + else + mtip_switch_tx(napi->dev); + + if (pkts < budget) { + napi_complete_done(napi, pkts); + /* Set default interrupt mask for L2 switch */ + writel(MCF_ESW_IMR_RXF | MCF_ESW_IMR_TXF, + &fecp->ESW_IMR); + } + return pkts; +} + +static int mtip_open(struct net_device *dev) +{ + struct mtip_ndev_priv *priv = netdev_priv(dev); + struct switch_enet_private *fep = priv->fep; + int ret, port_idx = priv->portnum - 1; + + if (fep->usage_count == 0) { + ret = clk_enable(fep->clk_ipg); + if (ret) { + dev_err(&fep->pdev->dev, + "Cannot enable switch IPG clock\n"); + return ret; + } + + netif_napi_add(dev, &fep->napi, mtip_rx_napi); + + ret = mtip_alloc_buffers(dev); + if (ret) + goto mtip_alloc_buffers_err; + } + + fep->link[port_idx] = 0; + + /* Probe and connect to PHY when open the interface, if already + * NOT done in the switch driver probe (or when the device is + * re-opened). + */ + ret = mtip_mii_probe(dev); + if (ret) + goto mtip_mii_probe_err; + + phy_start(fep->phy_dev[port_idx]); + + if (fep->usage_count == 0) { + napi_enable(&fep->napi); + mtip_switch_restart(dev, 1, 1); + + fep->curr_time = 0; + netif_start_queue(dev); + } + + fep->usage_count++; + return 0; + + mtip_mii_probe_err: + mtip_free_buffers(dev); + mtip_alloc_buffers_err: + if (fep->usage_count == 0) { + netif_napi_del(&fep->napi); + clk_disable(fep->clk_ipg); + } + return ret; +}; + +static int mtip_close(struct net_device *dev) +{ + struct mtip_ndev_priv *priv = netdev_priv(dev); + struct switch_enet_private *fep = priv->fep; + int idx = priv->portnum - 1; + + fep->link[idx] = 0; + + if (fep->phy_dev[idx]) { + phy_stop(fep->phy_dev[idx]); + netif_stop_queue(dev); + phy_disconnect(fep->phy_dev[idx]); + fep->phy_dev[idx] = NULL; + } + + if (fep->usage_count == 1) { + napi_disable(&fep->napi); + netif_napi_del(&fep->napi); + mtip_free_buffers(dev); + clk_disable(fep->clk_ipg); + } + + fep->usage_count--; + + return 0; +} + +#define FEC_HASH_BITS 6 /* #bits in hash */ +static void mtip_set_multicast_list(struct net_device *dev) +{ + struct mtip_ndev_priv *priv = netdev_priv(dev); + unsigned int hash_high = 0, hash_low = 0, crc; + struct switch_enet_private *fep = priv->fep; + void __iomem *enet_addr = fep->enet_addr; + struct netdev_hw_addr *ha; + unsigned char hash; + + if (priv->portnum == 2) + enet_addr += MCF_ESW_ENET_PORT_OFFSET; + + if (dev->flags & IFF_PROMISC) { + /* Promisc mode is required for switch - it is + * already enabled during driver's probe. + */ + dev_dbg(&dev->dev, "%s: IFF_PROMISC\n", __func__); + return; + } + + if (dev->flags & IFF_ALLMULTI) { + dev_dbg(&dev->dev, "%s: IFF_ALLMULTI\n", __func__); + + /* Allow all multicast addresses */ + writel(0xFFFFFFFF, enet_addr + MCF_FEC_GRP_HASH_TABLE_HIGH); + writel(0xFFFFFFFF, enet_addr + MCF_FEC_GRP_HASH_TABLE_LOW); + + return; + } + + netdev_for_each_mc_addr(ha, dev) { + /* Calculate crc32 value of mac address */ + crc = ether_crc_le(dev->addr_len, ha->addr); + + /* Only upper 6 bits (FEC_HASH_BITS) are used + * which point to specific bit in the hash registers + */ + hash = (crc >> (32 - FEC_HASH_BITS)) & 0x3F; + + if (hash > 31) + hash_high |= 1 << (hash - 32); + else + hash_low |= 1 << hash; + } + + writel(hash_high, enet_addr + MCF_FEC_GRP_HASH_TABLE_HIGH); + writel(hash_low, enet_addr + MCF_FEC_GRP_HASH_TABLE_LOW); +} + +static int mtip_set_mac_address(struct net_device *dev, void *p) +{ + struct mtip_ndev_priv *priv = netdev_priv(dev); + struct switch_enet_private *fep = priv->fep; + void __iomem *enet_addr = fep->enet_addr; + struct sockaddr *addr = p; + + if (!is_valid_ether_addr(addr->sa_data)) + return -EADDRNOTAVAIL; + eth_hw_addr_set(dev, addr->sa_data); + + if (priv->portnum == 2) + enet_addr += MCF_ESW_ENET_PORT_OFFSET; + + writel(dev->dev_addr[3] | (dev->dev_addr[2] << 8) | + (dev->dev_addr[1] << 16) | (dev->dev_addr[0] << 24), + enet_addr + MCF_FEC_PALR); + writel((dev->dev_addr[5] << 16) | (dev->dev_addr[4] << 24), + enet_addr + MCF_FEC_PAUR); + + return mtip_update_atable_static((unsigned char *)dev->dev_addr, + 7, 7, fep); +} + +static const struct ethtool_ops mtip_ethtool_ops = { + .get_link_ksettings = phy_ethtool_get_link_ksettings, + .set_link_ksettings = phy_ethtool_set_link_ksettings, + .get_drvinfo = mtip_get_drvinfo, + .get_link = ethtool_op_get_link, + .get_ts_info = ethtool_op_get_ts_info, +}; + +static const struct net_device_ops mtip_netdev_ops = { + .ndo_open = mtip_open, + .ndo_stop = mtip_close, + .ndo_start_xmit = mtip_start_xmit, + .ndo_set_rx_mode = mtip_set_multicast_list, + .ndo_tx_timeout = mtip_timeout, + .ndo_set_mac_address = mtip_set_mac_address, +}; + +bool mtip_is_switch_netdev_port(const struct net_device *ndev) +{ + return ndev->netdev_ops == &mtip_netdev_ops; +} + +static int __init mtip_switch_dma_init(struct switch_enet_private *fep) +{ + struct cbd_t *bdp, *cbd_base; + int ret, i; + + /* Check mask of the streaming and coherent API */ + ret = dma_set_mask_and_coherent(&fep->pdev->dev, DMA_BIT_MASK(32)); + if (ret < 0) { + dev_err(&fep->pdev->dev, "No suitable DMA available\n"); + return ret; + } + + /* Allocate memory for buffer descriptors */ + cbd_base = dma_alloc_coherent(&fep->pdev->dev, PAGE_SIZE, &fep->bd_dma, + GFP_KERNEL); + if (!cbd_base) + return -ENOMEM; + + /* Set receive and transmit descriptor base */ + fep->rx_bd_base = cbd_base; + fep->tx_bd_base = cbd_base + RX_RING_SIZE; + + /* Initialize the receive buffer descriptors */ + bdp = fep->rx_bd_base; + for (i = 0; i < RX_RING_SIZE; i++) { + bdp->cbd_sc = 0; + bdp++; + } + + /* Set the last buffer to wrap */ + bdp--; + bdp->cbd_sc |= BD_SC_WRAP; + + /* ...and the same for transmit */ + bdp = fep->tx_bd_base; + for (i = 0; i < TX_RING_SIZE; i++) { + /* Initialize the BD for every fragment in the page */ + bdp->cbd_sc = 0; + bdp->cbd_bufaddr = 0; + bdp++; + } + + /* Set the last buffer to wrap */ + bdp--; + bdp->cbd_sc |= BD_SC_WRAP; + + return 0; +} + +static void mtip_ndev_cleanup(struct switch_enet_private *fep) +{ + int i; + + for (i = 0; i < SWITCH_EPORT_NUMBER; i++) { + if (fep->ndev[i]) { + unregister_netdev(fep->ndev[i]); + free_netdev(fep->ndev[i]); + } + } +} + +static int mtip_ndev_init(struct switch_enet_private *fep, + struct platform_device *pdev) +{ + struct mtip_ndev_priv *priv; + int i, ret = 0; + + for (i = 0; i < SWITCH_EPORT_NUMBER; i++) { + fep->ndev[i] = alloc_netdev(sizeof(struct mtip_ndev_priv), + fep->ndev_name[i], NET_NAME_USER, + ether_setup); + if (!fep->ndev[i]) { + ret = -ENOMEM; + break; + } + + fep->ndev[i]->ethtool_ops = &mtip_ethtool_ops; + fep->ndev[i]->netdev_ops = &mtip_netdev_ops; + SET_NETDEV_DEV(fep->ndev[i], &pdev->dev); + + priv = netdev_priv(fep->ndev[i]); + priv->dev = fep->ndev[i]; + priv->fep = fep; + priv->portnum = i + 1; + fep->ndev[i]->irq = fep->irq; + + mtip_setup_mac(fep->ndev[i]); + + ret = register_netdev(fep->ndev[i]); + if (ret) { + dev_err(&fep->ndev[i]->dev, + "%s: ndev %s register err: %d\n", __func__, + fep->ndev[i]->name, ret); + break; + } + dev_dbg(&fep->ndev[i]->dev, "%s: MTIP eth L2 switch %pM\n", + fep->ndev[i]->name, fep->ndev[i]->dev_addr); + } + + if (ret) + mtip_ndev_cleanup(fep); + + return ret; +} + +static int mtip_parse_of(struct switch_enet_private *fep, + struct device_node *np) +{ + struct device_node *p; + unsigned int port_num; + int ret = 0; + + p = of_find_node_by_name(np, "ethernet-ports"); + + for_each_available_child_of_node_scoped(p, port) { + if (of_property_read_u32(port, "reg", &port_num)) + continue; + + if (port_num > SWITCH_EPORT_NUMBER) { + dev_err(&fep->pdev->dev, + "%s: The switch supports up to %d ports!\n", + __func__, SWITCH_EPORT_NUMBER); + goto of_get_err; + } + + fep->n_ports = port_num; + ret = of_get_mac_address(port, &fep->mac[port_num - 1][0]); + if (ret) + dev_dbg(&fep->pdev->dev, + "of_get_mac_address(%pOF) failed (%d)!\n", + port, ret); + + ret = of_property_read_string(port, "label", + &fep->ndev_name[port_num - 1]); + if (ret < 0) { + dev_err(&fep->pdev->dev, + "%s: Cannot get ethernet port name (%d)!\n", + __func__, ret); + goto of_get_err; + } + + ret = of_get_phy_mode(port, &fep->phy_interface[port_num - 1]); + if (ret < 0) { + dev_err(&fep->pdev->dev, + "%s: Cannot get PHY mode (%d)!\n", __func__, + ret); + goto of_get_err; + } + + fep->phy_np[port_num - 1] = of_parse_phandle(port, + "phy-handle", 0); + } + + of_get_err: + of_node_put(p); + + return ret; +} + +static int mtip_sw_learning(void *arg) +{ + struct switch_enet_private *fep = arg; + + while (!kthread_should_stop()) { + set_current_state(TASK_INTERRUPTIBLE); + /* check learning record valid */ + mtip_atable_dynamicms_learn_migration(fep, fep->curr_time, + NULL, NULL); + schedule_timeout(HZ / 100); + } + + return 0; +} + +static void mtip_mii_unregister(struct switch_enet_private *fep) +{ + mdiobus_unregister(fep->mii_bus); + mdiobus_free(fep->mii_bus); +} + +static const struct mtip_devinfo mtip_imx28_l2switch_info = { + .quirks = FEC_QUIRK_BUG_CAPTURE | FEC_QUIRK_SINGLE_MDIO | + FEC_QUIRK_SWAP_FRAME, +}; + +static const struct of_device_id mtipl2_of_match[] = { + { .compatible = "nxp,imx28-mtip-switch", + .data = &mtip_imx28_l2switch_info}, + { /* sentinel */ } +} +MODULE_DEVICE_TABLE(of, mtipl2_of_match); + +static int mtip_sw_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + const struct of_device_id *of_id; + struct switch_enet_private *fep; + struct mtip_devinfo *dev_info; + struct switch_t *fecp; + int ret; + + fep = devm_kzalloc(&pdev->dev, sizeof(*fep), GFP_KERNEL); + if (!fep) + return -ENOMEM; + + of_id = of_match_node(mtipl2_of_match, pdev->dev.of_node); + if (of_id) { + dev_info = (struct mtip_devinfo *)of_id->data; + if (dev_info) + fep->quirks = dev_info->quirks; + } + + fep->pdev = pdev; + platform_set_drvdata(pdev, fep); + + fep->enet_addr = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(fep->enet_addr)) + return PTR_ERR(fep->enet_addr); + + fep->irq = platform_get_irq_byname(pdev, "mtipl2sw"); + if (fep->irq < 0) + return fep->irq; + + ret = mtip_parse_of(fep, np); + if (ret < 0) { + dev_err(&pdev->dev, "%s: OF parse error (%d)!\n", __func__, + ret); + return ret; + } + + /* Create an Ethernet device instance. + * The switch lookup address memory starts at 0x800FC000 + */ + fep->hwp_enet = fep->enet_addr; + fecp = (struct switch_t *)(fep->enet_addr + ENET_SWI_PHYS_ADDR_OFFSET); + + fep->hwp = fecp; + fep->hwentry = (struct mtip_addr_table *) + ((unsigned long)fecp + MCF_ESW_LOOKUP_MEM_OFFSET); + + ret = devm_regulator_get_enable_optional(&pdev->dev, "phy"); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "Unable to get and enable 'phy'\n"); + + fep->clk_ipg = devm_clk_get_enabled(&pdev->dev, "ipg"); + if (IS_ERR(fep->clk_ipg)) + return dev_err_probe(&pdev->dev, PTR_ERR(fep->clk_ipg), + "Unable to acquire 'ipg' clock\n"); + + fep->clk_ahb = devm_clk_get_enabled(&pdev->dev, "ahb"); + if (IS_ERR(fep->clk_ahb)) + return dev_err_probe(&pdev->dev, PTR_ERR(fep->clk_ahb), + "Unable to acquire 'ahb' clock\n"); + + fep->clk_enet_out = devm_clk_get_optional_enabled(&pdev->dev, + "enet_out"); + if (IS_ERR(fep->clk_enet_out)) + return dev_err_probe(&pdev->dev, PTR_ERR(fep->clk_enet_out), + "Unable to acquire 'enet_out' clock\n"); + + /* setup MII interface for external switch ports */ + mtip_enet_init(fep, 1); + mtip_enet_init(fep, 2); + + spin_lock_init(&fep->learn_lock); + spin_lock_init(&fep->hw_lock); + spin_lock_init(&fep->mii_lock); + + ret = devm_request_irq(&pdev->dev, fep->irq, mtip_interrupt, 0, + dev_name(&pdev->dev), fep); + if (ret) + return dev_err_probe(&pdev->dev, fep->irq, + "Could not alloc IRQ\n"); + + ret = mtip_register_notifiers(fep); + if (ret) + return ret; + + ret = mtip_ndev_init(fep, pdev); + if (ret) { + dev_err(&pdev->dev, "%s: Failed to create virtual ndev (%d)\n", + __func__, ret); + goto ndev_init_err; + } + + ret = mtip_switch_dma_init(fep); + if (ret) { + dev_err(&pdev->dev, "%s: ethernet switch init fail (%d)!\n", + __func__, ret); + goto dma_init_err; + } + + ret = mtip_mii_init(fep, pdev); + if (ret) { + dev_err(&pdev->dev, "%s: Cannot init phy bus (%d)!\n", __func__, + ret); + goto mii_init_err; + } + /* setup timer for learning aging function */ + timer_setup(&fep->timer_aging, mtip_aging_timer, 0); + mod_timer(&fep->timer_aging, + jiffies + msecs_to_jiffies(LEARNING_AGING_INTERVAL)); + + fep->task = kthread_run(mtip_sw_learning, fep, "mtip_l2sw_learning"); + if (IS_ERR(fep->task)) { + ret = PTR_ERR(fep->task); + dev_err(&pdev->dev, "%s: learning kthread_run error (%d)!\n", + __func__, ret); + goto task_learning_err; + } + + return 0; + + task_learning_err: + timer_delete_sync(&fep->timer_aging); + mtip_mii_unregister(fep); + mii_init_err: + dma_init_err: + mtip_ndev_cleanup(fep); + ndev_init_err: + mtip_unregister_notifiers(fep); + + return ret; +} + +static void mtip_sw_remove(struct platform_device *pdev) +{ + struct switch_enet_private *fep = platform_get_drvdata(pdev); + + mtip_unregister_notifiers(fep); + mtip_ndev_cleanup(fep); + + mtip_mii_remove(fep); + + kthread_stop(fep->task); + timer_delete_sync(&fep->timer_aging); + platform_set_drvdata(pdev, NULL); + + kfree(fep); +} + +static struct platform_driver mtipl2plat_driver = { + .driver = { + .name = "mtipl2sw", + .of_match_table = mtipl2_of_match, + .suppress_bind_attrs = true, + }, + .probe = mtip_sw_probe, + .remove = mtip_sw_remove, +}; + +module_platform_driver(mtipl2plat_driver); + +MODULE_AUTHOR("Lukasz Majewski "); +MODULE_DESCRIPTION("Driver for MTIP L2 on SOC switch"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/freescale/mtipsw/mtipl2sw.h b/drivers/net/ethernet/freescale/mtipsw/mtipl2sw.h new file mode 100644 index 000000000000..bdc5376d6f3d --- /dev/null +++ b/drivers/net/ethernet/freescale/mtipsw/mtipl2sw.h @@ -0,0 +1,788 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2025 DENX Software Engineering GmbH + * Lukasz Majewski + */ + +#ifndef __MTIP_L2SWITCH_H_ +#define __MTIP_L2SWITCH_H_ + +#include +#include +#include +#include +#include +#include + +#define PKT_MAXBUF_SIZE 1518 +#define PKT_MINBUF_SIZE 64 +#define PKT_MAXBLR_SIZE 1520 + +/* The number of Tx and Rx buffers. These are allocated from the page + * pool. The code may assume these are power of two, so it is best + * to keep them that size. + * We don't need to allocate pages for the transmitter. We just use + * the skbuffer directly. + */ +#define MTIP_SWITCH_RX_PAGES 8 +#define MTIP_SWITCH_RX_FRSIZE 2048 +#define MTIP_SWITCH_RX_FRPPG (PAGE_SIZE / MTIP_SWITCH_RX_FRSIZE) +#define RX_RING_SIZE (MTIP_SWITCH_RX_FRPPG * MTIP_SWITCH_RX_PAGES) +#define MTIP_SWITCH_TX_FRSIZE 2048 +#define MTIP_SWITCH_TX_FRPPG (PAGE_SIZE / MTIP_SWITCH_TX_FRSIZE) + +#define TX_RING_SIZE 16 /* Must be power of two */ +#define TX_RING_MOD_MASK 15 /* for this to work */ + +#define SWITCH_EPORT_NUMBER 2 + +#if (((RX_RING_SIZE + TX_RING_SIZE) * 8) > PAGE_SIZE) +#error "L2SWITCH: descriptor ring size constants too large" +#endif +struct mtip_port_statistics_status { + /*outgoing frames discarded due to transmit queue congestion*/ + u32 MCF_ESW_POQC; + /*incoming frames discarded due to VLAN domain mismatch*/ + u32 MCF_ESW_PMVID; + /*incoming frames discarded due to untagged discard*/ + u32 MCF_ESW_PMVTAG; + /*incoming frames discarded due port is in blocking state*/ + u32 MCF_ESW_PBL; +}; + +struct switch_t { + u32 ESW_REVISION; + u32 ESW_SCRATCH; + u32 ESW_PER; + u32 reserved0[1]; + u32 ESW_VLANV; + u32 ESW_DBCR; + u32 ESW_DMCR; + u32 ESW_BKLR; + u32 ESW_BMPC; + u32 ESW_MODE; + u32 ESW_VIMSEL; + u32 ESW_VOMSEL; + u32 ESW_VIMEN; + u32 ESW_VID;/*0x34*/ + /*from 0x38 0x3C*/ + u32 esw_reserved0[2]; + u32 ESW_MCR;/*0x40*/ + u32 ESW_EGMAP; + u32 ESW_INGMAP; + u32 ESW_INGSAL; + u32 ESW_INGSAH; + u32 ESW_INGDAL; + u32 ESW_INGDAH; + u32 ESW_ENGSAL; + u32 ESW_ENGSAH; + u32 ESW_ENGDAL; + u32 ESW_ENGDAH; + u32 ESW_MCVAL;/*0x6C*/ + /*from 0x70--0x7C*/ + u32 esw_reserved1[4]; + u32 ESW_MMSR;/*0x80*/ + u32 ESW_LMT; + u32 ESW_LFC; + u32 ESW_PCSR; + u32 ESW_IOSR; + u32 ESW_QWT;/*0x94*/ + u32 esw_reserved2[1];/*0x98*/ + u32 ESW_P0BCT;/*0x9C*/ + /*from 0xA0-0xB8*/ + u32 esw_reserved3[7]; + u32 ESW_P0FFEN;/*0xBC*/ + u32 ESW_PSNP[8]; + u32 ESW_IPSNP[8]; + u32 ESW_PVRES[3]; + /*from 0x10C-0x13C*/ + u32 esw_reserved4[13]; + u32 ESW_IPRES;/*0x140*/ + /*from 0x144-0x17C*/ + u32 esw_reserved5[15]; + u32 ESW_PRES[3]; + /*from 0x18C-0x1FC*/ + u32 esw_reserved6[29]; + u32 ESW_PID[3]; + /*from 0x20C-0x27C*/ + u32 esw_reserved7[29]; + u32 ESW_VRES[32]; + u32 ESW_DISCN;/*0x300*/ + u32 ESW_DISCB; + u32 ESW_NDISCN; + u32 ESW_NDISCB;/*0xFC0DC30C*/ + struct mtip_port_statistics_status port_statistics_status[3]; + /*from 0x340-0x400*/ + u32 esw_reserved8[48]; + /*0xFC0DC400---0xFC0DC418*/ + u32 ESW_ISR; + u32 ESW_IMR; + u32 ESW_RDSR; + u32 ESW_TDSR; + u32 ESW_MRBR; + u32 ESW_RDAR; + u32 ESW_TDAR; + /*from 0x420-0x4FC*/ + u32 esw_reserved9[57]; + /*0xFC0DC500---0xFC0DC508*/ + u32 ESW_LREC0; + u32 ESW_LREC1; + u32 ESW_LSR; +}; + +struct addr_table64b_entry { + u32 lo; /* lower 32 bits */ + u32 hi; /* upper 32 bits */ +}; + +struct mtip_addr_table { + struct addr_table64b_entry mtip_table64b_entry[2048]; +}; + +#define MCF_ESW_LOOKUP_MEM_OFFSET 0x4000 +#define MCF_ESW_ENET_PORT_OFFSET 0x4000 +#define ENET_SWI_PHYS_ADDR_OFFSET 0x8000 +#define MCF_ESW_PER (0x08) +#define MCF_ESW_DBCR (0x14) +#define MCF_ESW_IMR (0x404) + +#define MCF_FEC_BASE_ADDR (fep->enet_addr) +#define MCF_FEC_EIR (0x04) +#define MCF_FEC_EIMR (0x08) +#define MCF_FEC_MMFR (0x40) +#define MCF_FEC_MSCR (0x44) + +#define MCF_FEC_RCR (0x84) +#define MCF_FEC_TCR (0xC4) +#define MCF_FEC_ECR (0x24) + +#define MCF_FEC_PALR (0xE4) +#define MCF_FEC_PAUR (0xE8) + +#define MCF_FEC_ERDSR (0x180) +#define MCF_FEC_ETDSR (0x184) + +#define MCF_FEC_IAUR (0x118) +#define MCF_FEC_IALR (0x11C) + +#define MCF_FEC_GAUR (0x120) +#define MCF_FEC_GALR (0x124) + +#define MCF_FEC_EMRBR (0x188) + +#define MCF_FEC_RCR_DRT BIT(1) +#define MCF_FEC_RCR_MII_MODE BIT(2) +#define MCF_FEC_RCR_PROM BIT(3) +#define MCF_FEC_RCR_FCE BIT(5) +#define MCF_FEC_RCR_RMII_MODE BIT(8) +#define MCF_FEC_RCR_RMII_10BASET BIT(9) +#define MCF_FEC_RCR_MAX_FL(x) (((x) & 0x00003FFF) << 16) +#define MCF_FEC_RCR_CRC_FWD BIT(14) +#define MCF_FEC_RCR_NO_LGTH_CHECK BIT(30) +#define MCF_FEC_TCR_FDEN BIT(2) + +#define MCF_FEC_ECR_RESET BIT(0) +#define MCF_FEC_ECR_ETHER_EN BIT(1) +#define MCF_FEC_ECR_MAGIC_ENA BIT(2) +#define MCF_FEC_ECR_ENA_1588 BIT(4) + +#define MTIP_ALIGNMENT 0xf +#define MCF_ENET_MII BIT(23) + +/* FEC MII MMFR bits definition */ +#define FEC_MMFR_ST BIT(30) +#define FEC_MMFR_OP_READ BIT(29) +#define FEC_MMFR_OP_WRITE BIT(28) +#define FEC_MMFR_PA(v) (((v) & 0x1F) << 23) +#define FEC_MMFR_RA(v) (((v) & 0x1F) << 18) +#define FEC_MMFR_TA (2 << 16) +#define FEC_MMFR_DATA(v) ((v) & 0xffff) + +/* Port 0 backpressure congestion threshold */ +#define P0BC_THRESHOLD 0x40 +#define LEARNING_AGING_INTERVAL 100 +/* Info received from Hardware Learning FIFO, + * holding MAC address and corresponding Hash Value and + * port number where the frame was received (disassembled). + */ +struct mtip_port_info { + /* MAC lower 32 bits (first byte is 7:0). */ + u32 maclo; + /* MAC upper 16 bits (47:32). */ + u32 machi; + /* the hash value for this MAC address. */ + u32 hash; + /* the port number this MAC address is associated with. */ + u32 port; +}; + +/* Define the buffer descriptor structure. */ +struct cbd_t { + u16 cbd_datlen; /* Data length */ + u16 cbd_sc; /* Control and status info */ + u32 cbd_bufaddr; /* Buffer address */ +}; + +/* The switch buffer descriptors track the ring buffers. The rx_bd_base and + * tx_bd_base always point to the base of the buffer descriptors. The + * cur_rx and cur_tx point to the currently available buffer. + * The dirty_tx tracks the current buffer that is being sent by the + * controller. The cur_tx and dirty_tx are equal under both completely + * empty and completely full conditions. The empty/ready indicator in + * the buffer descriptor determines the actual condition. + */ +struct switch_enet_private { + /* Base addresses for HW registers of the switch device */ + void __iomem *hwp_enet; + struct switch_t *hwp; + struct mtip_addr_table *hwentry; + void __iomem *enet_addr; + + struct platform_device *pdev; + + /* Switch internals */ + struct mtip_port_info g_info; + + /* Clocks */ + struct clk *clk_ipg; + struct clk *clk_ahb; + struct clk *clk_enet_out; + + /* skbuff */ + unsigned char *tx_bounce[TX_RING_SIZE]; + struct sk_buff *tx_skbuff[TX_RING_SIZE]; + struct sk_buff *rx_skbuff[RX_RING_SIZE]; + ushort skb_cur; + ushort skb_dirty; + + /* DMA */ + dma_addr_t bd_dma; + struct cbd_t *rx_bd_base; /* Address of Rx and Tx buffers. */ + struct cbd_t *tx_bd_base; + struct cbd_t *cur_rx, *cur_tx; /* The next free ring entry */ + struct cbd_t *dirty_tx; /* The ring entries to be free()ed. */ + uint tx_full; + + /* Locking */ + spinlock_t hw_lock; /* Lock for HW configuration */ + spinlock_t mii_lock; /* Lock for MII operation */ + spinlock_t learn_lock; /* Lock for learning DB adjustments */ + + /* NAPI support */ + struct napi_struct napi; + + /* Timer for Aging */ + struct timer_list timer_aging; + struct task_struct *task; + int at_block_overflows; + int at_curr_entries; + int curr_time; + + /* PHY and MDIO */ + struct mii_bus *mii_bus; + struct phy_device *phy_dev[SWITCH_EPORT_NUMBER]; + uint phy_speed; + int link[SWITCH_EPORT_NUMBER]; + int full_duplex[SWITCH_EPORT_NUMBER]; + phy_interface_t phy_interface[SWITCH_EPORT_NUMBER]; + struct device_node *phy_np[SWITCH_EPORT_NUMBER]; + + /* IRQ number */ + int irq; + + /* lan[01] ports */ + int n_ports; + const char *ndev_name[SWITCH_EPORT_NUMBER]; + struct net_device *ndev[SWITCH_EPORT_NUMBER]; + unsigned char mac[SWITCH_EPORT_NUMBER][ETH_ALEN]; + + /* Switch state */ + u8 br_members; /* Bit field with active members */ + u8 br_offload; /* Bridge in-HW offloading flag */ + int usage_count; /* Number of configured ports */ + + /* Driver related */ + u32 quirks; +}; + +struct mtip_ndev_priv { + int portnum; + struct net_device *dev; + struct net_device_stats stats; + struct net_device *master_dev; + struct switch_enet_private *fep; +}; + +#define MCF_FEC_MII_DATA 0x040 /* MII manage frame reg */ +#define MCF_FEC_GRP_HASH_TABLE_HIGH 0x120 /* High 32bits hash table */ +#define MCF_FEC_GRP_HASH_TABLE_LOW 0x124 /* Low 32bits hash table */ + +#define BD_SC_EMPTY ((ushort)0x8000) /* Receive is empty */ +#define BD_SC_READY ((ushort)0x8000) /* Transmit is ready */ +#define BD_SC_WRAP ((ushort)0x2000) /* Last buffer descriptor */ +#define BD_SC_INTRPT ((ushort)0x1000) /* Interrupt on change */ +#define BD_SC_CM ((ushort)0x0200) /* Continuous mode */ +#define BD_SC_ID ((ushort)0x0100) /* Rec'd too many idles */ +#define BD_SC_P ((ushort)0x0100) /* xmt preamble */ +#define BD_SC_BR ((ushort)0x0020) /* Break received */ +#define BD_SC_FR ((ushort)0x0010) /* Framing error */ +#define BD_SC_PR ((ushort)0x0008) /* Parity error */ +#define BD_SC_OV ((ushort)0x0002) /* Overrun */ +#define BD_SC_CD ((ushort)0x0001) + +/* Buffer descriptor control/status used by Ethernet receive. */ +#define BD_ENET_RX_EMPTY ((ushort)0x8000) +#define BD_ENET_RX_WRAP ((ushort)0x2000) +#define BD_ENET_RX_INTR ((ushort)0x1000) +#define BD_ENET_RX_LAST ((ushort)0x0800) +#define BD_ENET_RX_FIRST ((ushort)0x0400) +#define BD_ENET_RX_MISS ((ushort)0x0100) +#define BD_ENET_RX_LG ((ushort)0x0020) +#define BD_ENET_RX_NO ((ushort)0x0010) +#define BD_ENET_RX_SH ((ushort)0x0008) +#define BD_ENET_RX_CR ((ushort)0x0004) +#define BD_ENET_RX_OV ((ushort)0x0002) +#define BD_ENET_RX_CL ((ushort)0x0001) +/* All status bits */ +#define BD_ENET_RX_STATS ((ushort)0x013f) + +/* Buffer descriptor control/status used by Ethernet transmit.*/ +#define BD_ENET_TX_READY ((ushort)0x8000) +#define BD_ENET_TX_PAD ((ushort)0x4000) +#define BD_ENET_TX_WRAP ((ushort)0x2000) +#define BD_ENET_TX_INTR ((ushort)0x1000) +#define BD_ENET_TX_LAST ((ushort)0x0800) +#define BD_ENET_TX_TC ((ushort)0x0400) +#define BD_ENET_TX_DEF ((ushort)0x0200) +#define BD_ENET_TX_HB ((ushort)0x0100) +#define BD_ENET_TX_LC ((ushort)0x0080) +#define BD_ENET_TX_RL ((ushort)0x0040) +#define BD_ENET_TX_RCMASK ((ushort)0x003c) +#define BD_ENET_TX_UN ((ushort)0x0002) +#define BD_ENET_TX_CSL ((ushort)0x0001) +/* All status bits */ +#define BD_ENET_TX_STATS ((ushort)0x03ff) + +/* Copy from validation code */ +#define RX_BUFFER_SIZE 256 +#define TX_BUFFER_SIZE 256 + +#define TX_BD_R BIT(15) +#define TX_BD_TO1 BIT(14) +#define TX_BD_W BIT(13) +#define TX_BD_TO2 BIT(12) +#define TX_BD_L BIT(11) +#define TX_BD_TC BIT(10) + +#define TX_BD_INT BIT(30) +#define TX_BD_TS BIT(29) +#define TX_BD_PINS BIT(28) +#define TX_BD_IINS BIT(27) +#define TX_BD_TXE BIT(15) +#define TX_BD_UE BIT(13) +#define TX_BD_EE BIT(12) +#define TX_BD_FE BIT(11) +#define TX_BD_LCE BIT(10) +#define TX_BD_OE BIT(9) +#define TX_BD_TSE BIT(8) +#define TX_BD_BDU BIT(31) + +#define RX_BD_E BIT(15) +#define RX_BD_R01 BIT(14) +#define RX_BD_W BIT(13) +#define RX_BD_R02 BIT(12) +#define RX_BD_L BIT(11) +#define RX_BD_M BIT(8) +#define RX_BD_BC BIT(7) +#define RX_BD_MC BIT(6) +#define RX_BD_LG BIT(5) +#define RX_BD_NO BIT(4) +#define RX_BD_CR BIT(2) +#define RX_BD_OV BIT(1) +#define RX_BD_TR BIT(0) + +#define RX_BD_ME BIT(31) +#define RX_BD_PE 0x04000000 +#define RX_BD_CE 0x02000000 +#define RX_BD_UC 0x01000000 +#define RX_BD_INT 0x00800000 +#define RX_BD_ICE BIT(5) +#define RX_BD_PCR BIT(4) +#define RX_BD_VLAN BIT(2) +#define RX_BD_IPV6 BIT(1) +#define RX_BD_FRAG BIT(0) +#define RX_BD_BDU BIT(31) +/****************************************************************************/ + +/* Address Table size in bytes(2048 64bit entry ) */ +#define MTIP_ATABLE_MEM_SIZE (2048 * 8) +/* How many 64-bit elements fit in the address table */ +#define MTIP_ATABLE_MEM_NUM_ENTRIES (2048) +/* Address Table Maximum number of entries in each Slot */ +#define ATABLE_ENTRY_PER_SLOT 8 +/* log2(ATABLE_ENTRY_PER_SLOT)*/ +#define ATABLE_ENTRY_PER_SLOT_bits 3 +/* entry size in byte */ +#define ATABLE_ENTRY_SIZE 8 +/* slot size in byte */ +#define ATABLE_SLOT_SIZE (ATABLE_ENTRY_PER_SLOT * ATABLE_ENTRY_SIZE) +/* width of timestamp variable (bits) within address table entry */ +#define AT_DENTRY_TIMESTAMP_WIDTH 10 +/* number of bits for port number storage */ +#define AT_DENTRY_PORT_WIDTH 4 +/* number of bits for port bitmask number storage */ +#define AT_SENTRY_PORT_WIDTH 11 +/* address table static entry port bitmask start address bit */ +#define AT_SENTRY_PORTMASK_shift 21 +/* address table static entry priority start address bit */ +#define AT_SENTRY_PRIO_shift 18 +/* address table dynamic entry port start address bit */ +#define AT_DENTRY_PORT_shift 28 +/* address table dynamic entry timestamp start address bit */ +#define AT_DENTRY_TIME_shift 18 +/* address table entry record type start address bit */ +#define AT_ENTRY_TYPE_shift 17 +/* address table entry record type bit: 1 static, 0 dynamic */ +#define AT_ENTRY_TYPE_STATIC 1 +#define AT_ENTRY_TYPE_DYNAMIC 0 +/* address table entry record valid start address bit */ +#define AT_ENTRY_VALID_shift 16 +#define AT_ENTRY_RECORD_VALID 1 + +/* return block corresponding to the 8 bit hash value calculated */ +#define GET_BLOCK_PTR(hash) ((hash) << 3) +#define AT_EXTRACT_TIMESTAMP(x) \ + (((x) >> AT_DENTRY_TIME_shift) & ((1 << AT_DENTRY_TIMESTAMP_WIDTH) - 1)) +#define AT_EXTRACT_PORT(x) \ + (((x) >> AT_DENTRY_PORT_shift) & ((1 << AT_DENTRY_PORT_WIDTH) - 1)) +#define TIMEDELTA(newtime, oldtime) \ + (((newtime) - (oldtime)) & \ + ((1 << AT_DENTRY_TIMESTAMP_WIDTH) - 1)) + +/* increment time value respecting modulo. */ +static inline int mtip_timeincrement(int time) +{ + return (time + 1) & ((1 << AT_DENTRY_TIMESTAMP_WIDTH) - 1); +} + +/* ------------------------------------------------------------------------- */ +/* Bit definitions and macros for MCF_ESW_REVISION */ +#define MCF_MTIP_REVISION_CORE_REVISION(x) ((x) & 0x0000FFFF) +#define MCF_MTIP_REVISION_CUSTOMER_REVISION(x) (((x) & 0xFFFF0000) >> 16) + +/* Bit definitions and macros for MCF_ESW_PER */ +#define MCF_ESW_PER_TE0 BIT(0) +#define MCF_ESW_PER_TE1 BIT(1) +#define MCF_ESW_PER_TE2 BIT(2) +#define MCF_ESW_PER_RE0 BIT(16) +#define MCF_ESW_PER_RE1 BIT(17) +#define MCF_ESW_PER_RE2 BIT(18) + +/* Bit definitions and macros for MCF_ESW_VLANV */ +#define MCF_ESW_VLANV_VV0 BIT(0) +#define MCF_ESW_VLANV_VV1 BIT(1) +#define MCF_ESW_VLANV_VV2 BIT(2) +#define MCF_ESW_VLANV_DU0 BIT(16) +#define MCF_ESW_VLANV_DU1 BIT(17) +#define MCF_ESW_VLANV_DU2 BIT(18) + +/* Bit definitions and macros for MCF_ESW_DBCR */ +#define MCF_ESW_DBCR_P0 BIT(0) +#define MCF_ESW_DBCR_P1 BIT(1) +#define MCF_ESW_DBCR_P2 BIT(2) + +/* Bit definitions and macros for MCF_ESW_DMCR */ +#define MCF_ESW_DMCR_P0 BIT(0) +#define MCF_ESW_DMCR_P1 BIT(1) +#define MCF_ESW_DMCR_P2 BIT(2) + +/* Bit definitions and macros for MCF_ESW_BKLR */ +#define MCF_ESW_BKLR_BE0 BIT(0) +#define MCF_ESW_BKLR_BE1 BIT(1) +#define MCF_ESW_BKLR_BE2 BIT(2) +#define MCF_ESW_BKLR_LD0 BIT(16) +#define MCF_ESW_BKLR_LD1 BIT(17) +#define MCF_ESW_BKLR_LD2 BIT(18) + +/* Bit definitions and macros for MCF_ESW_BMPC */ +#define MCF_ESW_BMPC_PORT(x) (((x) & 0x0000000F) << 0) +#define MCF_ESW_BMPC_MSG_TX BIT(5) +#define MCF_ESW_BMPC_EN BIT(6) +#define MCF_ESW_BMPC_DIS BIT(7) +#define MCF_ESW_BMPC_PRIORITY(x) (((x) & 0x00000007) << 13) +#define MCF_ESW_BMPC_PORTMASK(x) (((x) & 0x00000007) << 16) + +/* Bit definitions and macros for MCF_ESW_MODE */ +#define MCF_ESW_MODE_SW_RST BIT(0) +#define MCF_ESW_MODE_SW_EN BIT(1) +#define MCF_ESW_MODE_STOP BIT(7) +#define MCF_ESW_MODE_CRC_TRAN BIT(8) +#define MCF_ESW_MODE_P0CT BIT(9) +#define MCF_ESW_MODE_STATRST BIT(31) + +/* Bit definitions and macros for MCF_ESW_VIMSEL */ +#define MCF_ESW_VIMSEL_IM0(x) (((x) & 0x00000003) << 0) +#define MCF_ESW_VIMSEL_IM1(x) (((x) & 0x00000003) << 2) +#define MCF_ESW_VIMSEL_IM2(x) (((x) & 0x00000003) << 4) + +/* Bit definitions and macros for MCF_ESW_VOMSEL */ +#define MCF_ESW_VOMSEL_OM0(x) (((x) & 0x00000003) << 0) +#define MCF_ESW_VOMSEL_OM1(x) (((x) & 0x00000003) << 2) +#define MCF_ESW_VOMSEL_OM2(x) (((x) & 0x00000003) << 4) + +/* Bit definitions and macros for MCF_ESW_VIMEN */ +#define MCF_ESW_VIMEN_EN0 BIT(0) +#define MCF_ESW_VIMEN_EN1 BIT(1) +#define MCF_ESW_VIMEN_EN2 BIT(2) + +/* Bit definitions and macros for MCF_ESW_VID */ +#define MCF_ESW_VID_TAG(x) (((x) & 0xFFFFFFFF) << 0) + +/* Bit definitions and macros for MCF_ESW_MCR */ +#define MCF_ESW_MCR_PORT(x) (((x) & 0x0000000F) << 0) +#define MCF_ESW_MCR_MEN BIT(4) +#define MCF_ESW_MCR_INGMAP BIT(5) +#define MCF_ESW_MCR_EGMAP BIT(6) +#define MCF_ESW_MCR_INGSA BIT(7) +#define MCF_ESW_MCR_INGDA BIT(8) +#define MCF_ESW_MCR_EGSA BIT(9) +#define MCF_ESW_MCR_EGDA BIT(10) + +/* Bit definitions and macros for MCF_ESW_EGMAP */ +#define MCF_ESW_EGMAP_EG0 BIT(0) +#define MCF_ESW_EGMAP_EG1 BIT(1) +#define MCF_ESW_EGMAP_EG2 BIT(2) + +/* Bit definitions and macros for MCF_ESW_INGMAP */ +#define MCF_ESW_INGMAP_ING0 BIT(0) +#define MCF_ESW_INGMAP_ING1 BIT(1) +#define MCF_ESW_INGMAP_ING2 BIT(2) + +/* Bit definitions and macros for MCF_ESW_INGSAL */ +#define MCF_ESW_INGSAL_ADDLOW(x) (((x) & 0xFFFFFFFF) << 0) + +/* Bit definitions and macros for MCF_ESW_INGSAH */ +#define MCF_ESW_INGSAH_ADDHIGH(x) (((x) & 0x0000FFFF) << 0) + +/* Bit definitions and macros for MCF_ESW_INGDAL */ +#define MCF_ESW_INGDAL_ADDLOW(x) (((x) & 0xFFFFFFFF) << 0) + +/* Bit definitions and macros for MCF_ESW_INGDAH */ +#define MCF_ESW_INGDAH_ADDHIGH(x) (((x) & 0x0000FFFF) << 0) + +/* Bit definitions and macros for MCF_ESW_ENGSAL */ +#define MCF_ESW_ENGSAL_ADDLOW(x) (((x) & 0xFFFFFFFF) << 0) + +/* Bit definitions and macros for MCF_ESW_ENGSAH */ +#define MCF_ESW_ENGSAH_ADDHIGH(x) (((x) & 0x0000FFFF) << 0) + +/* Bit definitions and macros for MCF_ESW_ENGDAL */ +#define MCF_ESW_ENGDAL_ADDLOW(x) (((x) & 0xFFFFFFFF) << 0) + +/* Bit definitions and macros for MCF_ESW_ENGDAH */ +#define MCF_ESW_ENGDAH_ADDHIGH(x) (((x) & 0x0000FFFF) << 0) + +/* Bit definitions and macros for MCF_ESW_MCVAL */ +#define MCF_ESW_MCVAL_COUNT(x) (((x) & 0x000000FF) << 0) + +/* Bit definitions and macros for MCF_ESW_MMSR */ +#define MCF_ESW_MMSR_BUSY BIT(0) +#define MCF_ESW_MMSR_NOCELL BIT(1) +#define MCF_ESW_MMSR_MEMFULL BIT(2) +#define MCF_ESW_MMSR_MFLATCH BIT(3) +#define MCF_ESW_MMSR_DQ_GRNT BIT(6) +#define MCF_ESW_MMSR_CELLS_AVAIL(x) (((x) & 0x000000FF) << 16) + +/* Bit definitions and macros for MCF_ESW_LMT */ +#define MCF_ESW_LMT_THRESH(x) (((x) & 0x000000FF) << 0) + +/* Bit definitions and macros for MCF_ESW_LFC */ +#define MCF_ESW_LFC_COUNT(x) (((x) & 0xFFFFFFFF) << 0) + +/* Bit definitions and macros for MCF_ESW_PCSR */ +#define MCF_ESW_PCSR_PC0 BIT(0) +#define MCF_ESW_PCSR_PC1 BIT(1) +#define MCF_ESW_PCSR_PC2 BIT(2) + +/* Bit definitions and macros for MCF_ESW_IOSR */ +#define MCF_ESW_IOSR_OR0 BIT(0) +#define MCF_ESW_IOSR_OR1 BIT(1) +#define MCF_ESW_IOSR_OR2 BIT(2) + +/* Bit definitions and macros for MCF_ESW_QWT */ +#define MCF_ESW_QWT_Q0WT(x) (((x) & 0x0000001F) << 0) +#define MCF_ESW_QWT_Q1WT(x) (((x) & 0x0000001F) << 8) +#define MCF_ESW_QWT_Q2WT(x) (((x) & 0x0000001F) << 16) +#define MCF_ESW_QWT_Q3WT(x) (((x) & 0x0000001F) << 24) + +/* Bit definitions and macros for MCF_ESW_P0BCT */ +#define MCF_ESW_P0BCT_THRESH(x) (((x) & 0x000000FF) << 0) + +/* Bit definitions and macros for MCF_ESW_P0FFEN */ +#define MCF_ESW_P0FFEN_FEN BIT(0) +#define MCF_ESW_P0FFEN_FD(x) (((x) & 0x00000003) << 2) + +/* Bit definitions and macros for MCF_ESW_PSNP */ +#define MCF_ESW_PSNP_EN BIT(0) +#define MCF_ESW_PSNP_MODE(x) (((x) & 0x00000003) << 1) +#define MCF_ESW_PSNP_CD BIT(3) +#define MCF_ESW_PSNP_CS BIT(4) +#define MCF_ESW_PSNP_PORT_COMPARE(x) (((x) & 0x0000FFFF) << 16) + +/* Bit definitions and macros for MCF_ESW_IPSNP */ +#define MCF_ESW_IPSNP_EN BIT(0) +#define MCF_ESW_IPSNP_MODE(x) (((x) & 0x00000003) << 1) +#define MCF_ESW_IPSNP_PROTOCOL(x) (((x) & 0x000000FF) << 8) + +/* Bit definitions and macros for MCF_ESW_PVRES */ +#define MCF_ESW_PVRES_PRI0(x) (((x) & 0x00000007) << 0) +#define MCF_ESW_PVRES_PRI1(x) (((x) & 0x00000007) << 3) +#define MCF_ESW_PVRES_PRI2(x) (((x) & 0x00000007) << 6) +#define MCF_ESW_PVRES_PRI3(x) (((x) & 0x00000007) << 9) +#define MCF_ESW_PVRES_PRI4(x) (((x) & 0x00000007) << 12) +#define MCF_ESW_PVRES_PRI5(x) (((x) & 0x00000007) << 15) +#define MCF_ESW_PVRES_PRI6(x) (((x) & 0x00000007) << 18) +#define MCF_ESW_PVRES_PRI7(x) (((x) & 0x00000007) << 21) + +/* Bit definitions and macros for MCF_ESW_IPRES */ +#define MCF_ESW_IPRES_ADDRESS(x) (((x) & 0x000000FF) << 0) +#define MCF_ESW_IPRES_IPV4SEL BIT(8) +#define MCF_ESW_IPRES_PRI0(x) (((x) & 0x00000003) << 9) +#define MCF_ESW_IPRES_PRI1(x) (((x) & 0x00000003) << 11) +#define MCF_ESW_IPRES_PRI2(x) (((x) & 0x00000003) << 13) +#define MCF_ESW_IPRES_READ BIT(31) + +/* Bit definitions and macros for MCF_ESW_PRES */ +#define MCF_ESW_PRES_VLAN BIT(0) +#define MCF_ESW_PRES_IP BIT(1) +#define MCF_ESW_PRES_MAC BIT(2) +#define MCF_ESW_PRES_DFLT_PRI(x) (((x) & 0x00000007) << 4) + +/* Bit definitions and macros for MCF_ESW_PID */ +#define MCF_ESW_PID_VLANID(x) (((x) & 0x0000FFFF) << 0) + +/* Bit definitions and macros for MCF_ESW_VRES */ +#define MCF_ESW_VRES_P0 BIT(0) +#define MCF_ESW_VRES_P1 BIT(1) +#define MCF_ESW_VRES_P2 BIT(2) +#define MCF_ESW_VRES_VLANID(x) (((x) & 0x00000FFF) << 3) + +/* Bit definitions and macros for MCF_ESW_DISCN */ +#define MCF_ESW_DISCN_COUNT(x) (((x) & 0xFFFFFFFF) << 0) + +/* Bit definitions and macros for MCF_ESW_DISCB */ +#define MCF_ESW_DISCB_COUNT(x) (((x) & 0xFFFFFFFF) << 0) + +/* Bit definitions and macros for MCF_ESW_NDISCN */ +#define MCF_ESW_NDISCN_COUNT(x) (((x) & 0xFFFFFFFF) << 0) + +/* Bit definitions and macros for MCF_ESW_NDISCB */ +#define MCF_ESW_NDISCB_COUNT(x) (((x) & 0xFFFFFFFF) << 0) + +/* Bit definitions and macros for MCF_ESW_POQC */ +#define MCF_ESW_POQC_COUNT(x) (((x) & 0xFFFFFFFF) << 0) + +/* Bit definitions and macros for MCF_ESW_PMVID */ +#define MCF_ESW_PMVID_COUNT(x) (((x) & 0xFFFFFFFF) << 0) + +/* Bit definitions and macros for MCF_ESW_PMVTAG */ +#define MCF_ESW_PMVTAG_COUNT(x) (((x) & 0xFFFFFFFF) << 0) + +/* Bit definitions and macros for MCF_ESW_PBL */ +#define MCF_ESW_PBL_COUNT(x) (((x) & 0xFFFFFFFF) << 0) + +/* Bit definitions and macros for MCF_ESW_ISR */ +#define MCF_ESW_ISR_EBERR BIT(0) +#define MCF_ESW_ISR_RXB BIT(1) +#define MCF_ESW_ISR_RXF BIT(2) +#define MCF_ESW_ISR_TXB BIT(3) +#define MCF_ESW_ISR_TXF BIT(4) +#define MCF_ESW_ISR_QM BIT(5) +#define MCF_ESW_ISR_OD0 BIT(6) +#define MCF_ESW_ISR_OD1 BIT(7) +#define MCF_ESW_ISR_OD2 BIT(8) +#define MCF_ESW_ISR_LRN BIT(9) + +/* Bit definitions and macros for MCF_ESW_IMR */ +#define MCF_ESW_IMR_EBERR BIT(0) +#define MCF_ESW_IMR_RXB BIT(1) +#define MCF_ESW_IMR_RXF BIT(2) +#define MCF_ESW_IMR_TXB BIT(3) +#define MCF_ESW_IMR_TXF BIT(4) +#define MCF_ESW_IMR_QM BIT(5) +#define MCF_ESW_IMR_OD0 BIT(6) +#define MCF_ESW_IMR_OD1 BIT(7) +#define MCF_ESW_IMR_OD2 BIT(8) +#define MCF_ESW_IMR_LRN BIT(9) + +/* Bit definitions and macros for MCF_ESW_RDSR */ +#define MCF_ESW_RDSR_ADDRESS(x) (((x) & 0x3FFFFFFF) << 2) + +/* Bit definitions and macros for MCF_ESW_TDSR */ +#define MCF_ESW_TDSR_ADDRESS(x) (((x) & 0x3FFFFFFF) << 2) + +/* Bit definitions and macros for MCF_ESW_MRBR */ +#define MCF_ESW_MRBR_SIZE(x) (((x) & 0x000003FF) << 4) + +/* Bit definitions and macros for MCF_ESW_RDAR */ +#define MCF_ESW_RDAR_R_DES_ACTIVE BIT(24) + +/* Bit definitions and macros for MCF_ESW_TDAR */ +#define MCF_ESW_TDAR_X_DES_ACTIVE BIT(24) + +/* Bit definitions and macros for MCF_ESW_LREC0 */ +#define MCF_ESW_LREC0_MACADDR0(x) (((x) & 0xFFFFFFFF) << 0) + +/* Bit definitions and macros for MCF_ESW_LREC1 */ +#define MCF_ESW_LREC1_MACADDR1(x) (((x) & 0x0000FFFF) << 0) +#define MCF_ESW_LREC1_HASH(x) (((x) & 0x000000FF) << 16) +#define MCF_ESW_LREC1_SWPORT(x) (((x) & 0x00000003) << 24) + +/* Bit definitions and macros for MCF_ESW_LSR */ +#define MCF_ESW_LSR_DA BIT(0) + +/* QUIRKS */ +/* Controller needs driver to swap frame */ +#define FEC_QUIRK_SWAP_FRAME BIT(1) +/* ENET Block Guide/ Chapter for the iMX6SX (PELE) address one issue: + * After set ENET_ATCR[Capture], there need some time cycles before the counter + * value is capture in the register clock domain. + * The wait-time-cycles is at least 6 clock cycles of the slower clock between + * the register clock and the 1588 clock. The 1588 ts_clk is fixed to 25Mhz, + * register clock is 66Mhz, so the wait-time-cycles must be greater than 240ns + * (40ns * 6). + */ +#define FEC_QUIRK_BUG_CAPTURE BIT(10) +/* Controller has only one MDIO bus */ +#define FEC_QUIRK_SINGLE_MDIO BIT(11) + +#define MTIP_PORT_FORWARDING_INIT 0xFF + +/* 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 enable); +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_register_notifiers(struct switch_enet_private *fep); +void mtip_unregister_notifiers(struct switch_enet_private *fep); +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_br.c b/drivers/net/ethernet/freescale/mtipsw/mtipl2sw_br.c new file mode 100644 index 000000000000..edfd95a7790d --- /dev/null +++ b/drivers/net/ethernet/freescale/mtipsw/mtipl2sw_br.c @@ -0,0 +1,120 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * L2 switch Controller driver for MTIP block - bridge network interface + * + * Copyright (C) 2025 DENX Software Engineering GmbH + * Lukasz Majewski + */ + +#include +#include +#include + +#include "mtipl2sw.h" + +static int mtip_ndev_port_link(struct net_device *ndev, + struct net_device *br_ndev, + struct netlink_ext_ack *extack) +{ + struct mtip_ndev_priv *priv = netdev_priv(ndev), *other_priv; + struct switch_enet_private *fep = priv->fep; + struct net_device *other_ndev; + + /* Check if one port of MTIP switch is already bridged */ + if (fep->br_members && !fep->br_offload) { + /* Get the second bridge ndev */ + other_ndev = fep->ndev[fep->br_members - 1]; + other_priv = netdev_priv(other_ndev); + if (other_priv->master_dev != br_ndev) { + NL_SET_ERR_MSG_MOD(extack, + "L2 offloading only possible for the same bridge!"); + return notifier_from_errno(-EOPNOTSUPP); + } + + fep->br_offload = 1; + mtip_switch_dis_port_separation(fep); + mtip_clear_atable(fep); + } + + if (!priv->master_dev) + priv->master_dev = br_ndev; + + fep->br_members |= BIT(priv->portnum - 1); + + dev_dbg(&ndev->dev, + "%s: ndev: %s br: %s fep: %p members: 0x%x offload: %d\n", + __func__, ndev->name, br_ndev->name, fep, fep->br_members, + fep->br_offload); + + return NOTIFY_DONE; +} + +static void mtip_netdevice_port_unlink(struct net_device *ndev) +{ + struct mtip_ndev_priv *priv = netdev_priv(ndev); + struct switch_enet_private *fep = priv->fep; + + dev_dbg(&ndev->dev, "%s: ndev: %s members: 0x%x\n", __func__, + ndev->name, fep->br_members); + + fep->br_members &= ~BIT(priv->portnum - 1); + priv->master_dev = NULL; + + if (fep->br_members && fep->br_offload) { + fep->br_offload = 0; + mtip_switch_en_port_separation(fep); + mtip_clear_atable(fep); + } +} + +/* netdev notifier */ +static int mtip_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 = ptr; + struct netlink_ext_ack *extack; + int ret = NOTIFY_DONE; + + if (!mtip_is_switch_netdev_port(ndev)) + return NOTIFY_DONE; + + extack = netdev_notifier_info_to_extack(&info->info); + + switch (event) { + case NETDEV_CHANGEUPPER: + if (!netif_is_bridge_master(info->upper_dev)) + break; + + if (info->linking) + ret = mtip_ndev_port_link(ndev, info->upper_dev, + extack); + else + mtip_netdevice_port_unlink(ndev); + + break; + default: + return NOTIFY_DONE; + } + + return notifier_from_errno(ret); +} + +static struct notifier_block mtip_netdevice_nb __read_mostly = { + .notifier_call = mtip_netdevice_event, +}; + +int mtip_register_notifiers(struct switch_enet_private *fep) +{ + int ret = register_netdevice_notifier(&mtip_netdevice_nb); + + if (ret) + dev_err(&fep->pdev->dev, "can't register netdevice notifier\n"); + + return ret; +} + +void mtip_unregister_notifiers(struct switch_enet_private *fep) +{ + unregister_netdevice_notifier(&mtip_netdevice_nb); +} diff --git a/drivers/net/ethernet/freescale/mtipsw/mtipl2sw_mgnt.c b/drivers/net/ethernet/freescale/mtipsw/mtipl2sw_mgnt.c new file mode 100644 index 000000000000..844d5ca902ca --- /dev/null +++ b/drivers/net/ethernet/freescale/mtipsw/mtipl2sw_mgnt.c @@ -0,0 +1,449 @@ +// 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 Reserved. + * 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) +{ + struct switch_t *fecp = fep->hwp; + + /* 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 == 1 && + (vlan_domain_num < 0 || vlan_domain_num > 32)) { + dev_err(&fep->pdev->dev, "%s: Domain out of range\n", __func__); + return -EINVAL; + } + + fecp->ESW_PID[port] = MCF_ESW_PID_VLANID(port_vlanid); + if (port == 0) { + if (vlan_verify_en == 1) + writel(MCF_ESW_VRES_VLANID(port_vlanid) | + MCF_ESW_VRES_P0, + &fecp->ESW_VRES[vlan_domain_num]); + + writel(readl(&fecp->ESW_VIMEN) | MCF_ESW_VIMEN_EN0, + &fecp->ESW_VIMEN); + writel(readl(&fecp->ESW_VIMSEL) | MCF_ESW_VIMSEL_IM0(mode), + &fecp->ESW_VIMSEL); + } else if (port == 1) { + if (vlan_verify_en == 1) + writel(MCF_ESW_VRES_VLANID(port_vlanid) | + MCF_ESW_VRES_P1, + &fecp->ESW_VRES[vlan_domain_num]); + + writel(readl(&fecp->ESW_VIMEN) | MCF_ESW_VIMEN_EN1, + &fecp->ESW_VIMEN); + writel(readl(&fecp->ESW_VIMSEL) | MCF_ESW_VIMSEL_IM1(mode), + &fecp->ESW_VIMSEL); + } else if (port == 2) { + if (vlan_verify_en == 1) + writel(MCF_ESW_VRES_VLANID(port_vlanid) | + MCF_ESW_VRES_P2, + &fecp->ESW_VRES[vlan_domain_num]); + + writel(readl(&fecp->ESW_VIMEN) | MCF_ESW_VIMEN_EN2, + &fecp->ESW_VIMEN); + writel(readl(&fecp->ESW_VIMSEL) | MCF_ESW_VIMSEL_IM2(mode), + &fecp->ESW_VIMSEL); + } + + return 0; +} + +int mtip_vlan_output_process(struct switch_enet_private *fep, int port, + int mode) +{ + struct switch_t *fecp = fep->hwp; + + if (port < 0 || port > 2) { + dev_err(&fep->pdev->dev, "%s: Port (%d) not supported!\n", + __func__, port); + return -EINVAL; + } + + if (port == 0) { + writel(readl(&fecp->ESW_VOMSEL) | MCF_ESW_VOMSEL_OM0(mode), + &fecp->ESW_VOMSEL); + } else if (port == 1) { + writel(readl(&fecp->ESW_VOMSEL) | MCF_ESW_VOMSEL_OM1(mode), + &fecp->ESW_VOMSEL); + } else if (port == 2) { + writel(readl(&fecp->ESW_VOMSEL) | MCF_ESW_VOMSEL_OM2(mode), + &fecp->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) +{ + struct switch_t *fecp = fep->hwp; + + 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 == 1) { + if (port == 0) + writel(readl(&fecp->ESW_VLANV) | MCF_ESW_VLANV_VV0, + &fecp->ESW_VLANV); + else if (port == 1) + writel(readl(&fecp->ESW_VLANV) | MCF_ESW_VLANV_VV1, + &fecp->ESW_VLANV); + else if (port == 2) + writel(readl(&fecp->ESW_VLANV) | MCF_ESW_VLANV_VV2, + &fecp->ESW_VLANV); + } else if (vlan_domain_verify_en == 0) { + if (port == 0) + writel(readl(&fecp->ESW_VLANV) & ~MCF_ESW_VLANV_VV0, + &fecp->ESW_VLANV); + else if (port == 1) + writel(readl(&fecp->ESW_VLANV) & ~MCF_ESW_VLANV_VV1, + &fecp->ESW_VLANV); + else if (port == 2) + writel(readl(&fecp->ESW_VLANV) & ~MCF_ESW_VLANV_VV2, + &fecp->ESW_VLANV); + } + + if (vlan_discard_unknown_en == 1) { + if (port == 0) + writel(readl(&fecp->ESW_VLANV) | MCF_ESW_VLANV_DU0, + &fecp->ESW_VLANV); + else if (port == 1) + writel(readl(&fecp->ESW_VLANV) | MCF_ESW_VLANV_DU1, + &fecp->ESW_VLANV); + else if (port == 2) + writel(readl(&fecp->ESW_VLANV) | MCF_ESW_VLANV_DU2, + &fecp->ESW_VLANV); + } else if (vlan_discard_unknown_en == 0) { + if (port == 0) + writel(readl(&fecp->ESW_VLANV) & ~MCF_ESW_VLANV_DU0, + &fecp->ESW_VLANV); + else if (port == 1) + writel(readl(&fecp->ESW_VLANV) & ~MCF_ESW_VLANV_DU1, + &fecp->ESW_VLANV); + else if (port == 2) + writel(readl(&fecp->ESW_VLANV) & ~MCF_ESW_VLANV_DU2, + &fecp->ESW_VLANV); + } + + dev_dbg(&fep->pdev->dev, "%s: ESW_VLANV %#x\n", __func__, + fecp->ESW_VLANV); + + return 0; +} + +int mtip_port_multicast_config(struct switch_enet_private *fep, + int port, bool enable) +{ + struct switch_t *fecp = fep->hwp; + u32 reg = 0; + + if (port < 0 || port > 2) { + dev_err(&fep->pdev->dev, "%s: Port (%d) not supported\n", + __func__, port); + return -EINVAL; + } + + reg = readl(&fecp->ESW_DMCR); + if (enable) { + if (port == 0) + reg |= MCF_ESW_DMCR_P0; + else if (port == 1) + reg |= MCF_ESW_DMCR_P1; + else if (port == 2) + reg |= MCF_ESW_DMCR_P2; + } else { + if (port == 0) + reg &= ~MCF_ESW_DMCR_P0; + else if (port == 1) + reg &= ~MCF_ESW_DMCR_P1; + else if (port == 2) + reg &= ~MCF_ESW_DMCR_P2; + } + + writel(reg, &fecp->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) +{ + struct switch_t *fecp = fep->hwp; + u32 reg = 0; + + if (port < 0 || port > 2) { + dev_err(&fep->pdev->dev, "%s: Port (%d) not supported\n", + __func__, port); + return -EINVAL; + } + + reg = readl(&fecp->ESW_PER); + if (tx_en) { + if (port == 0) + reg |= MCF_ESW_PER_TE0; + else if (port == 1) + reg |= MCF_ESW_PER_TE1; + else if (port == 2) + reg |= MCF_ESW_PER_TE2; + } else { + if (port == 0) + reg &= (~MCF_ESW_PER_TE0); + else if (port == 1) + reg &= (~MCF_ESW_PER_TE1); + else if (port == 2) + reg &= (~MCF_ESW_PER_TE2); + } + + if (rx_en) { + if (port == 0) + reg |= MCF_ESW_PER_RE0; + else if (port == 1) + reg |= MCF_ESW_PER_RE1; + else if (port == 2) + reg |= MCF_ESW_PER_RE2; + } else { + if (port == 0) + reg &= (~MCF_ESW_PER_RE0); + else if (port == 1) + reg &= (~MCF_ESW_PER_RE1); + else if (port == 2) + reg &= (~MCF_ESW_PER_RE2); + } + + writel(reg, &fecp->ESW_PER); + return 0; +} + +void mtip_switch_en_port_separation(struct switch_enet_private *fep) +{ + struct switch_t *fecp = fep->hwp; + 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 = readl(&fecp->ESW_VRES[0]); + writel(reg | MCF_ESW_VRES_P1 | MCF_ESW_VRES_P2, + &fecp->ESW_VRES[0]); + + reg = readl(&fecp->ESW_VRES[1]); + writel(reg | MCF_ESW_VRES_P0, &fecp->ESW_VRES[1]); + + reg = readl(&fecp->ESW_VRES[2]); + writel(reg | MCF_ESW_VRES_P0, &fecp->ESW_VRES[2]); + + dev_dbg(&fep->pdev->dev, "%s: VRES0: 0x%x\n", + __func__, readl(&fecp->ESW_VRES[0])); + dev_dbg(&fep->pdev->dev, "%s: VRES1: 0x%x\n", __func__, + readl(&fecp->ESW_VRES[1])); + dev_dbg(&fep->pdev->dev, "%s: VRES2: 0x%x\n", __func__, + readl(&fecp->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) +{ + struct switch_t *fecp = fep->hwp; + + writel(0, &fecp->ESW_PID[0]); + writel(0, &fecp->ESW_PID[1]); + writel(0, &fecp->ESW_PID[2]); + + writel(0, &fecp->ESW_VRES[0]); + writel(0, &fecp->ESW_VRES[1]); + writel(0, &fecp->ESW_VRES[2]); + + writel(0, &fecp->ESW_VIMEN); + writel(0, &fecp->ESW_VIMSEL); + writel(0, &fecp->ESW_VLANV); + writel(0, &fecp->ESW_VOMSEL); +} + +int mtip_port_broadcast_config(struct switch_enet_private *fep, + int port, bool enable) +{ + struct switch_t *fecp = fep->hwp; + u32 reg = 0; + + if (port < 0 || port > 2) { + dev_err(&fep->pdev->dev, "%s: Port (%d) not supported\n", + __func__, port); + return -EINVAL; + } + + reg = readl(&fecp->ESW_DBCR); + if (enable) { + if (port == 0) + reg |= MCF_ESW_DBCR_P0; + else if (port == 1) + reg |= MCF_ESW_DBCR_P1; + else if (port == 2) + reg |= MCF_ESW_DBCR_P2; + } else { + if (port == 0) + reg &= ~MCF_ESW_DBCR_P0; + else if (port == 1) + reg &= ~MCF_ESW_DBCR_P1; + else if (port == 2) + reg &= ~MCF_ESW_DBCR_P2; + } + + writel(reg, &fecp->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 enable) +{ + struct switch_t *fecp = fep->hwp; + u32 reg = 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 |= MCF_ESW_P0FFEN_FD(port & GENMASK(1, 0)); + + if (enable) + reg |= MCF_ESW_P0FFEN_FEN; + else + reg &= ~MCF_ESW_P0FFEN_FEN; + + writel(reg, &fecp->ESW_P0FFEN); + return 0; +} + +int mtip_port_learning_config(struct switch_enet_private *fep, int port, + bool disable, bool irq_adj) +{ + struct switch_t *fecp = fep->hwp; + u32 reg = 0; + + if (port < 0 || port > 2) { + dev_err(&fep->pdev->dev, "%s: Port (%d) not supported\n", + __func__, port); + return -EINVAL; + } + + reg = readl(&fecp->ESW_BKLR); + if (disable) { + if (irq_adj) + fecp->ESW_IMR &= ~MCF_ESW_IMR_LRN; + + if (port == 0) + reg |= MCF_ESW_BKLR_LD0; + else if (port == 1) + reg |= MCF_ESW_BKLR_LD1; + else if (port == 2) + reg |= MCF_ESW_BKLR_LD2; + } else { + if (irq_adj) + fecp->ESW_IMR |= MCF_ESW_IMR_LRN; + + if (port == 0) + reg &= ~MCF_ESW_BKLR_LD0; + else if (port == 1) + reg &= ~MCF_ESW_BKLR_LD1; + else if (port == 2) + reg &= ~MCF_ESW_BKLR_LD2; + } + + writel(reg, &fecp->ESW_BKLR); + dev_dbg(&fep->pdev->dev, "%s ESW_BKLR %#x, ESW_IMR %#x\n", __func__, + readl(&fecp->ESW_BKLR), readl(&fecp->ESW_IMR)); + + return 0; +} + +int mtip_port_blocking_config(struct switch_enet_private *fep, int port, + bool enable) +{ + struct switch_t *fecp = fep->hwp; + u32 reg = 0; + + if (port < 0 || port > 2) { + dev_err(&fep->pdev->dev, "%s: Port (%d) not supported\n", + __func__, port); + return -EINVAL; + } + + reg = readl(&fecp->ESW_BKLR); + if (enable) { + if (port == 0) + reg |= MCF_ESW_BKLR_BE0; + else if (port == 1) + reg |= MCF_ESW_BKLR_BE1; + else if (port == 2) + reg |= MCF_ESW_BKLR_BE2; + } else { + if (port == 0) + reg &= ~MCF_ESW_BKLR_BE0; + else if (port == 1) + reg &= ~MCF_ESW_BKLR_BE1; + else if (port == 2) + reg &= ~MCF_ESW_BKLR_BE2; + } + + writel(reg, &fecp->ESW_BKLR); + return 0; +} From patchwork Fri Apr 18 06:07:14 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lukasz Majewski X-Patchwork-Id: 14056731 X-Patchwork-Delegate: kuba@kernel.org 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 E820A268FCD; Fri, 18 Apr 2025 06:07:55 +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=1744956478; cv=none; b=gIAqB9oC9bZdqFyAk7K+RRvnbXP1/+64l/Q4cFxMtNDy5jWSOPfsJ34ijcPvZPlDS7UIHJN71lrR3dC6tdHyLeju5RbG7hDKHPkUs4oYuWmg2Wx2Uo04s+ke9yge+d/y8PNW3FFKXuv8ktOsZ0rdUYX9cKhXOw2cz1qaLY4qZoM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744956478; c=relaxed/simple; bh=qKFZ4aYtvbYFwfW1KyHLDbosU0eq6byz2CVLD3yZy6Y=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=lCoSmCIDKVpBXGgZvXApldjrDV0ZMSLKz5P0f2mvG+ZE0th6iWvalB+NmXg9xlct1s0kgNUrO1YL1xgUMOE4b+Xlaz/t7iAQLvWF3aVOYKvrhmRgiFLmnknNQ9+fk7m8M4cwFOM9sYWbfPbqehwLoc85WmzMUdzb/YFq+NSwFes= 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=du4o32Vb; 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="du4o32Vb" Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id 51D23102933A6; Fri, 18 Apr 2025 08:07:50 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=denx.de; s=mx-20241105; t=1744956471; h=from:subject:date:message-id:to:cc:mime-version: content-transfer-encoding:in-reply-to:references; bh=GJBeU/rnFZPjbhJxcQ6V2MkB/H8xOWywLoSrGR8U0x4=; b=du4o32VbQcfzuFrgKrCkupQ/Oaalh2xlIM4LSIM6yTC+MyPD/Ms4X7dKpFCRZKlXoCspiP bdYN7zfutFReylcrlieiTObQj90odwIj/li5AfOjsXfhKYXwZ8PKHlD5rl+E7EjJAdq6tf dqmPvVYXTxdwZ8iKcyHZP7r5v4OSQ4n1DkKhnPN8/rupf5dTA/qxoHb+JUna3i3lFAt2OP VyAeG5WKZHrVdMKUtJz7yqRJgZc8O3EzHaLKPszxFda5WZsifGTA7AWRIJKn+rupGmGhwM boflQlI1irbxY8ihrnRJM3qtevelbw5+9RSNpbh1IQfx6BhY0KA+rm0tZp9DJQ== 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 v6 5/7] ARM: mxs_defconfig: Enable CONFIG_NFS_FSCACHE Date: Fri, 18 Apr 2025 08:07:14 +0200 Message-Id: <20250418060716.3498031-6-lukma@denx.de> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20250418060716.3498031-1-lukma@denx.de> References: <20250418060716.3498031-1-lukma@denx.de> Precedence: bulk X-Mailing-List: netdev@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Last-TLS-Session-Version: TLSv1.3 X-Patchwork-Delegate: kuba@kernel.org It is not possible to enable by user the CONFIG_NETFS_SUPPORT anymore and hence it depends on CONFIG_NFS_FSCACHE being enabled. This patch fixes potential performance regression for NFS on the mxs devices. Signed-off-by: Lukasz Majewski Suggested-by: Stefan Wahren --- Changes for v6: - New patch --- arch/arm/configs/mxs_defconfig | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/arch/arm/configs/mxs_defconfig b/arch/arm/configs/mxs_defconfig index c76d66135abb..22f7639f61fe 100644 --- a/arch/arm/configs/mxs_defconfig +++ b/arch/arm/configs/mxs_defconfig @@ -138,8 +138,6 @@ CONFIG_PWM_MXS=y CONFIG_NVMEM_MXS_OCOTP=y CONFIG_EXT4_FS=y # CONFIG_DNOTIFY is not set -CONFIG_NETFS_SUPPORT=m -CONFIG_FSCACHE=y CONFIG_FSCACHE_STATS=y CONFIG_CACHEFILES=m CONFIG_VFAT_FS=y @@ -155,6 +153,7 @@ CONFIG_NFS_FS=y CONFIG_NFS_V3_ACL=y CONFIG_NFS_V4=y CONFIG_ROOT_NFS=y +CONFIG_NFS_FSCACHE=y CONFIG_NLS_CODEPAGE_437=y CONFIG_NLS_CODEPAGE_850=y CONFIG_NLS_ISO8859_1=y From patchwork Fri Apr 18 06:07:15 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lukasz Majewski X-Patchwork-Id: 14056734 X-Patchwork-Delegate: kuba@kernel.org 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 E815A268FCC; Fri, 18 Apr 2025 06:07:55 +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=1744956479; cv=none; b=W3df9TOqwOouISEIXkKY48ApI63u2dsiOLurDPrvK3Yd4X+RQ1CY70EtTo/0kdOQEQhBoGDzZQAp4fiRVAOI5+w5ZNeU+hnPnRmrVFG6MOhVg2TU1Lv7FJnhm+MT02nlNZ25CojiiaNa++VOZScaJnzD0/p9rrMAPEsp0503s2w= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744956479; c=relaxed/simple; bh=Wd+5EB9fq3LoFiUcGnEGbfHdmLwl+6UEoPFAc9tUUng=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=axjuMliMBbxNH/o7ETQcqDNi6Ng5nH2WmlFX3/fhw0m1COw7My+w/vIJIgrTfYwIN5wzclYVoSygr4qIPHFeez6LqL8PwA8QRlT+YboxZQNjJmSDlhzIi8TTjDpPBCqpktD/78gypLTdfmlf+ftvg8KYJMKc0Pts6JqkU7+xOog= 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=Ek7dy+uC; 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="Ek7dy+uC" Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id 00430102933A4; Fri, 18 Apr 2025 08:07:51 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=denx.de; s=mx-20241105; t=1744956473; h=from:subject:date:message-id:to:cc:mime-version: content-transfer-encoding:in-reply-to:references; bh=1bc/l8RQ6IGv08DVD+8pR8yhxUHNhK0UV4UbPtwrrso=; b=Ek7dy+uCROq3uBXGZqhiYWttFIVtr48dwyzQEV4uP5/BgFTMu6C70AAks3jidNgZ8FHk2g 17cenalX+bwluCLMTZK4cZPsx1+y/5WtSZdTTJFGEijfDzF27+q3X178uii2pfd8Nt8SRJ YvKnDKIIZDIpt62sWvVBKptBZ45KjWG13HaXumqmuPRgm/3vKr/S219tUETmAJ0Fw+xyfp nud4UvICV1zWYyTpZyGbP6pvQroePfXMHmwsjr0a3Oa+tKVl+j8eO3vMKdwUV1/Xo1LIec W4YAjbYBYNPbmDym9ZzTOpWmp7AY3jDr3L1vwinNOeDWyOPOiIqO0mz4jxZx9g== 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 v6 6/7] ARM: mxs_defconfig: Update mxs_defconfig to 6.15-rc1 Date: Fri, 18 Apr 2025 08:07:15 +0200 Message-Id: <20250418060716.3498031-7-lukma@denx.de> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20250418060716.3498031-1-lukma@denx.de> References: <20250418060716.3498031-1-lukma@denx.de> Precedence: bulk X-Mailing-List: netdev@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Last-TLS-Session-Version: TLSv1.3 X-Patchwork-Delegate: kuba@kernel.org This file is the updated version of mxs_defconfig for the v6.15-rc1 linux-next. Detailed description of removed configuration entries: -CONFIG_MTD_M25P80=y -> it has been replaced MTD_SPI_NOR (which is enabled) -CONFIG_SMSC_PHY=y -> is enabled implicit by USB_NET_SMSC95XX -CONFIG_GPIO_SYSFS=y -> it has been deprecated by moving to EXPERT and its replacement GPIO_CDEV is enabled by default Signed-off-by: Lukasz Majewski Suggested-by: Stefan Wahren --- Changes for v5: - New patch Changes for v6: - Add detailed description on the removed configuration options after update --- arch/arm/configs/mxs_defconfig | 7 ------- 1 file changed, 7 deletions(-) diff --git a/arch/arm/configs/mxs_defconfig b/arch/arm/configs/mxs_defconfig index 22f7639f61fe..b1a31cb914c8 100644 --- a/arch/arm/configs/mxs_defconfig +++ b/arch/arm/configs/mxs_defconfig @@ -32,9 +32,6 @@ CONFIG_INET=y CONFIG_IP_PNP=y CONFIG_IP_PNP_DHCP=y CONFIG_SYN_COOKIES=y -# CONFIG_INET_XFRM_MODE_TRANSPORT is not set -# CONFIG_INET_XFRM_MODE_TUNNEL is not set -# CONFIG_INET_XFRM_MODE_BEET is not set # CONFIG_INET_DIAG is not set # CONFIG_IPV6 is not set CONFIG_CAN=m @@ -45,7 +42,6 @@ CONFIG_MTD=y CONFIG_MTD_CMDLINE_PARTS=y CONFIG_MTD_BLOCK=y CONFIG_MTD_DATAFLASH=y -CONFIG_MTD_M25P80=y CONFIG_MTD_SST25L=y CONFIG_MTD_RAW_NAND=y CONFIG_MTD_NAND_GPMI_NAND=y @@ -60,7 +56,6 @@ CONFIG_ENC28J60=y CONFIG_ICPLUS_PHY=y CONFIG_MICREL_PHY=y CONFIG_REALTEK_PHY=y -CONFIG_SMSC_PHY=y CONFIG_CAN_FLEXCAN=m CONFIG_USB_USBNET=y CONFIG_USB_NET_SMSC95XX=y @@ -77,13 +72,11 @@ CONFIG_SERIAL_AMBA_PL011=y CONFIG_SERIAL_AMBA_PL011_CONSOLE=y CONFIG_SERIAL_MXS_AUART=y # CONFIG_HW_RANDOM is not set -# CONFIG_I2C_COMPAT is not set CONFIG_I2C_CHARDEV=y CONFIG_I2C_MXS=y CONFIG_SPI=y CONFIG_SPI_GPIO=m CONFIG_SPI_MXS=y -CONFIG_GPIO_SYSFS=y # CONFIG_HWMON is not set CONFIG_WATCHDOG=y CONFIG_STMP3XXX_RTC_WATCHDOG=y From patchwork Fri Apr 18 06:07:16 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lukasz Majewski X-Patchwork-Id: 14056733 X-Patchwork-Delegate: kuba@kernel.org 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 4B41F2690ED; Fri, 18 Apr 2025 06:07:57 +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=1744956479; cv=none; b=FSpZ55CuQZdoUR2bF7ZBgoGX6Au3l00vSEJgDqelA/vmArWHKz7TuDSXOeSONnFQUcu5HuKOGUkFV1P4dK1+yRTVCchC5R9xR5bGSVqsrPV21LPvOuAluFKiOkMF2lM/R8COXthGO1lCH2l6G9imEmAW5fLu/Eph/1maN2xC+Lw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744956479; c=relaxed/simple; bh=hXU6iBOkVqNqvJnyn5amxCjYXTuii/dZdBmG/kRfM3g=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=uYvVM9BBT/K0BROhDhPaPO1SpsRZnSQxQt5Svq5Wevn6qqOGWSO8wORnify9ajtcq2sCkFZJHV0AgMObdOvBHogYFB/JQ4w8EPGcDIzcNJs9kfe0WhH6nLFQk86B7UEqsbU4JaDFHtRdqs8KTGQi/oD7wqKiT0Rr14TgC5GhW50= 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=HWbwTmck; 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="HWbwTmck" Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id B455E102933AA; Fri, 18 Apr 2025 08:07:53 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=denx.de; s=mx-20241105; t=1744956475; h=from:subject:date:message-id:to:cc:mime-version: content-transfer-encoding:in-reply-to:references; bh=m0VF+kM9OTbKnvQE6hc/9mZ+Q2P4CfCIFoAJparHrkA=; b=HWbwTmckQeuFXyh9j0TkDiY/+fWDN4W2SFMuuV2XWkhfiSXnG+XuaPx5USvt/Wvsy1vGCX qVrE8ZsnPmL4HTjsWJAG5APN3HwHkrIdEtbnzqwO0z1QYS1fGf2ZDnQlyY3ePgj95ZXFlH X3mV66xFNZV/Gi/8OMMkmPX5BDz4un2WhysDJjQFAavra7diJCoGMPzPj3xxHASt9AGJn4 CVdzDEURN0WXBbrEip8l1yVNNjvjBoryMsnSP4LgwpUpehc9QAKOnTlSTYAzFPtTdY6Zz3 JSPKO7a+7GAwz/aw7sznqLWOrqP+R0MDBy/MWaM3+L/wCZN+uOF8cjJsc/wplQ== 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 v6 7/7] ARM: mxs_defconfig: Enable CONFIG_FEC_MTIP_L2SW to support MTIP L2 switch Date: Fri, 18 Apr 2025 08:07:16 +0200 Message-Id: <20250418060716.3498031-8-lukma@denx.de> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20250418060716.3498031-1-lukma@denx.de> References: <20250418060716.3498031-1-lukma@denx.de> Precedence: bulk X-Mailing-List: netdev@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Last-TLS-Session-Version: TLSv1.3 X-Patchwork-Delegate: kuba@kernel.org This patch enables support for More Than IP L2 switch available on some imx28[7] devices. Moreover, it also enables CONFIG_SWITCHDEV and CONFIG_BRIDGE required by this driver for correct operation. Signed-off-by: Lukasz Majewski Reviewed-by: Stefan Wahren --- Changes for v4: - New patch Changes for v5: - Apply this patch on top of patch, which updates mxs_defconfig to v6.15-rc1 - Add more verbose commit message with explanation why SWITCHDEV and BRIDGE must be enabled as well Changes for v6: - None --- arch/arm/configs/mxs_defconfig | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/arm/configs/mxs_defconfig b/arch/arm/configs/mxs_defconfig index b1a31cb914c8..ef4556222274 100644 --- a/arch/arm/configs/mxs_defconfig +++ b/arch/arm/configs/mxs_defconfig @@ -34,6 +34,8 @@ CONFIG_IP_PNP_DHCP=y CONFIG_SYN_COOKIES=y # CONFIG_INET_DIAG is not set # CONFIG_IPV6 is not set +CONFIG_BRIDGE=y +CONFIG_NET_SWITCHDEV=y CONFIG_CAN=m # CONFIG_WIRELESS is not set CONFIG_DEVTMPFS=y @@ -52,6 +54,7 @@ CONFIG_EEPROM_AT24=y CONFIG_SCSI=y CONFIG_BLK_DEV_SD=y CONFIG_NETDEVICES=y +CONFIG_FEC_MTIP_L2SW=y CONFIG_ENC28J60=y CONFIG_ICPLUS_PHY=y CONFIG_MICREL_PHY=y