From patchwork Tue Jun 22 14:41:09 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lukasz Majewski X-Patchwork-Id: 12337739 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-18.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER, INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 0682BC48BE5 for ; Tue, 22 Jun 2021 14:41:42 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id DD05961374 for ; Tue, 22 Jun 2021 14:41:41 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231947AbhFVOnz (ORCPT ); Tue, 22 Jun 2021 10:43:55 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:48324 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231514AbhFVOnw (ORCPT ); Tue, 22 Jun 2021 10:43:52 -0400 Received: from phobos.denx.de (phobos.denx.de [IPv6:2a01:238:438b:c500:173d:9f52:ddab:ee01]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 93940C061574 for ; Tue, 22 Jun 2021 07:41:36 -0700 (PDT) Received: from localhost.localdomain (85-222-111-42.dynamic.chello.pl [85.222.111.42]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (No client certificate requested) (Authenticated sender: lukma@denx.de) by phobos.denx.de (Postfix) with ESMTPSA id DBD49829F9; Tue, 22 Jun 2021 16:41:32 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=denx.de; s=phobos-20191101; t=1624372893; bh=XBmaZ49ciAVJrRh7lkhd3wM4W5Go7mEqDiemcK4VYBw=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=nMv4RCkB8SmpZkseaZ+tuERG0Y1tv8KPm6g5XNGF5pwrp4yai4xONd/eEC61efNym Dka3gfInYEBvrbcTXLTBW2mpCKVYGLgY02H5Z2vwewgOCC9nT10Hms7WplLjp7S1nI FU/KTD/JoIgnCo7JpIiJ6R8hSQ+QKZ9Lig/T22JH03NNlKxACJ4DrA0XMD9GInpZ1c 9IsvXobMG+aECbT5T0jj6gD8EVRghiIDdrBBNXeiMjAt3Zmc/8OjxfefygRTeCOAzT JPhxIuKuuXbTP8n2sAAy6PntXyKBm8ucZOwsrJDtDrWElReHdag4vBBy7uBCRYyw+C NRna4cbwdSYMw== From: Lukasz Majewski To: "David S . Miller" , Jakub Kicinski , Madalin Bucur , Nicolas Ferre , Joakim Zhang , Andrew Lunn , Florian Fainelli , Vladimir Oltean Cc: netdev@vger.kernel.org, Arnd Bergmann , Mark Einon , NXP Linux Team , linux-kernel@lists.infradead.org, Lukasz Majewski Subject: [RFC 1/3] ARM: dts: imx28: Add description for L2 switch on XEA board Date: Tue, 22 Jun 2021 16:41:09 +0200 Message-Id: <20210622144111.19647-2-lukma@denx.de> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20210622144111.19647-1-lukma@denx.de> References: <20210622144111.19647-1-lukma@denx.de> MIME-Version: 1.0 X-Virus-Scanned: clamav-milter 0.103.2 at phobos.denx.de X-Virus-Status: Clean Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-State: RFC The 'eth_switch' node is now extendfed to enable support for L2 switch. Moreover, the mac[01] nodes are defined as well and linked to the former with 'phy-handle' property. Signed-off-by: Lukasz Majewski --- arch/arm/boot/dts/imx28-xea.dts | 42 +++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/arch/arm/boot/dts/imx28-xea.dts b/arch/arm/boot/dts/imx28-xea.dts index d649822febed..94ff801669c4 100644 --- a/arch/arm/boot/dts/imx28-xea.dts +++ b/arch/arm/boot/dts/imx28-xea.dts @@ -23,6 +23,48 @@ status = "okay"; }; +&mac0 { + pinctrl-names = "default"; + pinctrl-0 = <&mac0_pins_a>; + phy-mode = "rmii"; + phy-supply = <®_fec_3v3>; + phy-reset-gpios = <&gpio2 13 0>; + phy-reset-duration = <100>; + local-mac-address = [ 00 11 22 AA BB CC ]; + status = "okay"; +}; + +&mac1 { + pinctrl-names = "default"; + pinctrl-0 = <&mac1_pins_a>; + phy-mode = "rmii"; + phy-supply = <®_fec_3v3>; + local-mac-address = [ 00 11 22 AA BB DD ]; + status = "okay"; +}; + +ð_switch { + compatible = "imx,mtip-l2switch"; + reg = <0x800f8000 0x400>, <0x800fC000 0x4000>, <0x800f8400 0x400>; + + interrupts = <100>; + status = "okay"; + + ports { + port1@1 { + reg = <1>; + label = "eth0"; + phy-handle = <&mac0>; + }; + + port2@2 { + reg = <2>; + label = "eth1"; + phy-handle = <&mac1>; + }; + }; +}; + &pinctrl { pinctrl-names = "default"; pinctrl-0 = <&hog_pins_a &hog_pins_tiva>; From patchwork Tue Jun 22 14:41:10 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lukasz Majewski X-Patchwork-Id: 12337741 X-Patchwork-Delegate: kuba@kernel.org Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-18.7 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER, INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id E5EAEC49EA2 for ; Tue, 22 Jun 2021 14:41:44 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id C65FA60241 for ; Tue, 22 Jun 2021 14:41:44 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231981AbhFVOn7 (ORCPT ); Tue, 22 Jun 2021 10:43:59 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:48332 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231788AbhFVOnw (ORCPT ); Tue, 22 Jun 2021 10:43:52 -0400 Received: from phobos.denx.de (phobos.denx.de [IPv6:2a01:238:438b:c500:173d:9f52:ddab:ee01]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 9639CC06175F for ; Tue, 22 Jun 2021 07:41:36 -0700 (PDT) Received: from localhost.localdomain (85-222-111-42.dynamic.chello.pl [85.222.111.42]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (No client certificate requested) (Authenticated sender: lukma@denx.de) by phobos.denx.de (Postfix) with ESMTPSA id 7E41B82A13; Tue, 22 Jun 2021 16:41:33 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=denx.de; s=phobos-20191101; t=1624372894; bh=hLQa4xeLXjVcE4A1UCU3uy7RZq155CdCD7VyfSf9IcM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=ONsblKa5EIkgL9eJ1/kltbqUApgeiukoV6UR8Xr5K8/9gKPqKjcbzuA4YR3OVn6Fw pyNO4zTjhfNPVgEJUiEJbarq1Rcm4eYrcQ765oj24p8pY954jIKTf1CMNESmegA3MM rrMA6qfxB3n+1ikf14ctncQzSFTa1mMm/zm1p/OE14ZM+Odr4kchQQ8eplRUOFQ3xE 19iSe2izkOpOE+nH1RPNpXI+lL3swr0gi1kOiJkt0VpOoHd6so/6jzlWZ2c6i3CxOi UITH/EPkpU8Uf8T+fK4TkB0jqRLTQNbVJdrpCf31ZFrGHL6n2zslASdKNPNQoc4Q88 RdeTszg5lOKJg== From: Lukasz Majewski To: "David S . Miller" , Jakub Kicinski , Madalin Bucur , Nicolas Ferre , Joakim Zhang , Andrew Lunn , Florian Fainelli , Vladimir Oltean Cc: netdev@vger.kernel.org, Arnd Bergmann , Mark Einon , NXP Linux Team , linux-kernel@lists.infradead.org, Lukasz Majewski Subject: [RFC 2/3] net: Provide switchdev driver for NXP's More Than IP L2 switch Date: Tue, 22 Jun 2021 16:41:10 +0200 Message-Id: <20210622144111.19647-3-lukma@denx.de> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20210622144111.19647-1-lukma@denx.de> References: <20210622144111.19647-1-lukma@denx.de> MIME-Version: 1.0 X-Virus-Scanned: clamav-milter 0.103.2 at phobos.denx.de X-Virus-Status: Clean Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: kuba@kernel.org X-Patchwork-State: RFC This change provides driver for More Than IP L2 switch IP block to hook into switchdev subsystem to provide bridging offloading for i.MX28 SoC (imx287 to be precise). This code is responsible for configuring this device as L2 bridge when one decides to bridge eth[01] interfaces (so no vlan, filtering table aging supported). This driver shall be regarded as a complementary one for NXP's FEC (fec_main.c). It reuses some code from it. Signed-off-by: Lukasz Majewski --- drivers/net/ethernet/freescale/Kconfig | 1 + drivers/net/ethernet/freescale/Makefile | 1 + drivers/net/ethernet/freescale/mtipsw/Kconfig | 11 + .../net/ethernet/freescale/mtipsw/Makefile | 3 + .../net/ethernet/freescale/mtipsw/fec_mtip.c | 438 ++++++++++++++++++ .../net/ethernet/freescale/mtipsw/fec_mtip.h | 213 +++++++++ 6 files changed, 667 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/fec_mtip.c create mode 100644 drivers/net/ethernet/freescale/mtipsw/fec_mtip.h diff --git a/drivers/net/ethernet/freescale/Kconfig b/drivers/net/ethernet/freescale/Kconfig index 3f9175bdce77..3cf703aa2a00 100644 --- a/drivers/net/ethernet/freescale/Kconfig +++ b/drivers/net/ethernet/freescale/Kconfig @@ -102,5 +102,6 @@ config GIANFAR source "drivers/net/ethernet/freescale/dpaa/Kconfig" source "drivers/net/ethernet/freescale/dpaa2/Kconfig" source "drivers/net/ethernet/freescale/enetc/Kconfig" +source "drivers/net/ethernet/freescale/mtipsw/Kconfig" endif # NET_VENDOR_FREESCALE diff --git a/drivers/net/ethernet/freescale/Makefile b/drivers/net/ethernet/freescale/Makefile index 67c436400352..12ed0c13f739 100644 --- a/drivers/net/ethernet/freescale/Makefile +++ b/drivers/net/ethernet/freescale/Makefile @@ -27,3 +27,4 @@ obj-$(CONFIG_FSL_DPAA2_ETH) += dpaa2/ obj-$(CONFIG_FSL_ENETC) += enetc/ obj-$(CONFIG_FSL_ENETC_MDIO) += enetc/ obj-$(CONFIG_FSL_ENETC_VF) += enetc/ +obj-$(CONFIG_FEC_MTIP_L2SW) += mtipsw/ diff --git a/drivers/net/ethernet/freescale/mtipsw/Kconfig b/drivers/net/ethernet/freescale/mtipsw/Kconfig new file mode 100644 index 000000000000..e7f40dcad0d0 --- /dev/null +++ b/drivers/net/ethernet/freescale/mtipsw/Kconfig @@ -0,0 +1,11 @@ +# 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 ARCH_MXS + select FIXED_PHY + help + This enables support for the MoreThan IP on i.MX SoCs (e.g. iMX28) + L2 switch. diff --git a/drivers/net/ethernet/freescale/mtipsw/Makefile b/drivers/net/ethernet/freescale/mtipsw/Makefile new file mode 100644 index 000000000000..1aac85f92750 --- /dev/null +++ b/drivers/net/ethernet/freescale/mtipsw/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_FEC_MTIP_L2SW) += fec_mtip.o diff --git a/drivers/net/ethernet/freescale/mtipsw/fec_mtip.c b/drivers/net/ethernet/freescale/mtipsw/fec_mtip.c new file mode 100644 index 000000000000..fe4e3fb34295 --- /dev/null +++ b/drivers/net/ethernet/freescale/mtipsw/fec_mtip.c @@ -0,0 +1,438 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2021 Lukasz Majewski + * Lukasz Majewski + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fec_mtip.h" +#include "../fec.h" + +static void mtipl2_setup_desc_active(struct mtipl2sw_priv *priv, int id) +{ + struct fec_enet_private *fec = priv->fep[id]; + + fec->rx_queue[0]->bd.reg_desc_active = + fec_hwp(fec) + fec_offset_des_active_rxq(fec, 0); + + fec->tx_queue[0]->bd.reg_desc_active = + fec_hwp(fec) + fec_offset_des_active_txq(fec, 0); +} + +static int mtipl2_en_rx(struct mtipl2sw_priv *priv) +{ + writel(MCF_ESW_RDAR_R_DES_ACTIVE, priv->hwpsw + MCF_ESW_R_RDAR); + + return 0; +} + +static void read_atable(struct mtipl2sw_priv *priv, int index, + unsigned long *read_lo, unsigned long *read_hi) +{ + unsigned long atable_base = (unsigned long)priv->hwentry; + + *read_lo = readl((const volatile void*)atable_base + (index << 3)); + *read_hi = readl((const volatile void*)atable_base + (index << 3) + 4); +} + +static void write_atable(struct mtipl2sw_priv *priv, int index, + unsigned long write_lo, unsigned long write_hi) +{ + unsigned long atable_base = (unsigned long)priv->hwentry; + + writel(write_lo, (volatile void *)atable_base + (index << 3)); + writel(write_hi, (volatile void *)atable_base + (index << 3) + 4); +} + +/* + * Clear complete MAC Look Up Table + */ +static void esw_clear_atable(struct mtipl2sw_priv *priv) +{ + int index; + for (index = 0; index < 2048; index++) + write_atable(priv, index, 0, 0); +} + +static int mtipl2_sw_enable(struct mtipl2sw_priv *priv) +{ + /* + * L2 switch - reset + */ + writel(MCF_ESW_MODE_SW_RST, &priv->fecp->ESW_MODE); + udelay(10); + + /* Configure switch*/ + writel(MCF_ESW_MODE_STATRST, &priv->fecp->ESW_MODE); + writel(MCF_ESW_MODE_SW_EN, &priv->fecp->ESW_MODE); + + /* Management port configuration, make port 0 as management port */ + writel(0, &priv->fecp->ESW_BMPC); + + /* + * Set backpressure threshold to minimize discarded frames + * during due to congestion. + */ + writel(P0BC_THRESHOLD, &priv->fecp->ESW_P0BCT); + + /* Set the max rx buffer size */ + writel(L2SW_PKT_MAXBLR_SIZE, priv->hwpsw + MCF_ESW_R_BUFF_SIZE); + /* Enable broadcast on all ports */ + writel(0x7, &priv->fecp->ESW_DBCR); + + /* Enable multicast on all ports */ + writel(0x7, &priv->fecp->ESW_DMCR); + + esw_clear_atable(priv); + + /* Clear all pending interrupts */ + writel(0xffffffff, priv->hwpsw + FEC_IEVENT); + + /* Enable interrupts we wish to service */ + writel(FEC_MTIP_DEFAULT_IMASK, priv->hwpsw + FEC_IMASK); + priv->l2sw_on = true; + + return 0; +} + +static void mtipl2_sw_disable(struct mtipl2sw_priv *priv) +{ + writel(0, &priv->fecp->ESW_MODE); +} + +static int mtipl2_port_enable (struct mtipl2sw_priv *priv, int port) +{ + u32 l2_ports_en; + + pr_err("%s: PORT ENABLE %d\n", __func__, port); + + /* Enable tx/rx on L2 switch ports */ + l2_ports_en = readl(&priv->fecp->ESW_PER); + if (!(l2_ports_en & MCF_ESW_ENA_PORT_0)) + l2_ports_en = MCF_ESW_ENA_PORT_0; + + if (port == 0 && !(l2_ports_en & MCF_ESW_ENA_PORT_1)) + l2_ports_en |= MCF_ESW_ENA_PORT_1; + + if (port == 1 && !(l2_ports_en & MCF_ESW_ENA_PORT_2)) + l2_ports_en |= MCF_ESW_ENA_PORT_2; + + writel(l2_ports_en, &priv->fecp->ESW_PER); + + /* + * Check if MAC IP block is already enabled (after switch initializtion) + * or if we need to enable it after mtipl2_port_disable was called. + */ + + return 0; +} + +static void mtipl2_port_disable (struct mtipl2sw_priv *priv, int port) +{ + u32 l2_ports_en; + + pr_err(" %s: PORT DISABLE %d\n", __func__, port); + + l2_ports_en = readl(&priv->fecp->ESW_PER); + if (port == 0) + l2_ports_en &= ~MCF_ESW_ENA_PORT_1; + + if (port == 1) + l2_ports_en &= ~MCF_ESW_ENA_PORT_2; + + /* Finally disable tx/rx on port 0 */ + if (!(l2_ports_en & MCF_ESW_ENA_PORT_1) && + !(l2_ports_en & MCF_ESW_ENA_PORT_2)) + l2_ports_en &= ~MCF_ESW_ENA_PORT_0; + + writel(l2_ports_en, &priv->fecp->ESW_PER); +} + +irqreturn_t +mtip_l2sw_interrupt_handler(int irq, void *dev_id) +{ + struct mtipl2sw_priv *priv = dev_id; + struct fec_enet_private *fep = priv->fep[0]; + irqreturn_t ret = IRQ_NONE; + u32 int_events, int_imask; + + int_events = readl(fec_hwp(fep) + FEC_IEVENT); + writel(int_events, fec_hwp(fep) + FEC_IEVENT); + + if ((int_events & FEC_MTIP_DEFAULT_IMASK) && fep->link) { + ret = IRQ_HANDLED; + + if (napi_schedule_prep(&fep->napi)) { + /* Disable RX interrupts */ + int_imask = readl(fec_hwp(fep) + FEC_IMASK); + int_imask &= ~FEC_MTIP_ENET_RXF; + writel(int_imask, fec_hwp(fep) + FEC_IMASK); + __napi_schedule(&fep->napi); + } + } + + return ret; +} + +static int mtipl2_parse_of(struct mtipl2sw_priv *priv, struct device_node *np) +{ + struct device_node *port, *p, *fep_np; + struct platform_device *pdev; + struct net_device *ndev; + unsigned int port_num; + + p = of_find_node_by_name(np, "ports"); + + for_each_available_child_of_node(p, port) { + if (of_property_read_u32(port, "reg", &port_num)) + continue; + + priv->n_ports = port_num; + + fep_np = of_parse_phandle(port, "phy-handle", 0); + pdev = of_find_device_by_node(fep_np); + ndev = platform_get_drvdata(pdev); + priv->fep[port_num - 1] = netdev_priv(ndev); + put_device(&pdev->dev); + } + + of_node_put(p); + + return 0; +} + +int mtipl2_ndev_port_link(struct net_device *ndev, + struct net_device *br_ndev) +{ + struct fec_enet_private *fec = netdev_priv(ndev); + struct mtipl2sw_priv *priv = fec->mtipl2; + + pr_err("%s: ndev: %s br: %s fec: 0x%x 0x%x\n", __func__, ndev->name, + br_ndev->name, (unsigned int) fec, fec->dev_id); + + /* Check if MTIP switch is already enabled */ + if(!priv->l2sw_on) { + /* + * Close running network connections - to safely enable + * support for mtip L2 switching. + */ + if (netif_oper_up(priv->fep[0]->netdev)) + fec_enet_close(priv->fep[0]->netdev); + + if (netif_oper_up(priv->fep[1]->netdev)) + fec_enet_close(priv->fep[1]->netdev); + + /* Configure and enable the L2 switch IP block */ + mtipl2_sw_enable(priv); + + if(!priv->br_ndev) + priv->br_ndev = br_ndev; + } + + priv->br_members |= BIT(fec->dev_id); + + /* Enable internal switch port */ + mtipl2_port_enable(fec->mtipl2, fec->dev_id); + + if (fec->dev_id == 1) + return NOTIFY_DONE; + + /* + * Set addresses for DMA0 proper operation to point to L2 switch + * IP block. + * + * The eth1 FEC driver is now only used for controlling the PHY device. + */ + fec->mtip_l2sw = true; + + mtipl2_setup_desc_active(priv, fec->dev_id); + fec_enet_open(priv->fep[fec->dev_id]->netdev); + + mtipl2_en_rx(fec->mtipl2); + + return NOTIFY_DONE; +} + +static void mtipl2_netdevice_port_unlink(struct net_device *ndev) +{ + struct fec_enet_private *fec = netdev_priv(ndev); + struct mtipl2sw_priv *priv = fec->mtipl2; + + pr_err("%s: ndev: %s id: %d\n", __func__, ndev->name, fec->dev_id); + + /* Disable internal switch port */ + mtipl2_port_disable(fec->mtipl2, fec->dev_id); + + if (netif_oper_up(priv->fep[fec->dev_id]->netdev)) + fec_enet_close(priv->fep[fec->dev_id]->netdev); + + priv->br_members &= ~BIT(fec->dev_id); + + fec->mtip_l2sw = false; + priv->br_ndev = NULL; + + mtipl2_setup_desc_active(priv, fec->dev_id); + fec_enet_open(priv->fep[fec->dev_id]->netdev); + + if (!priv->br_members) { + mtipl2_sw_disable(priv); + priv->l2sw_on = false; + } +} + +bool mtipl2_port_dev_check(const struct net_device *ndev) +{ + if(!fec_get_priv(ndev)) + return false; + + return true; +} + +/* netdev notifier */ +static int mtipl2_netdevice_event(struct notifier_block *unused, + unsigned long event, void *ptr) +{ + struct net_device *ndev = netdev_notifier_info_to_dev(ptr); + struct netdev_notifier_changeupper_info *info; + int ret = NOTIFY_DONE; + + if (!mtipl2_port_dev_check(ndev)) + return NOTIFY_DONE; + + pr_err("%s: ndev: %s event: 0x%lx\n", __func__, ndev->name, event); + + switch (event) { + case NETDEV_CHANGEUPPER: + info = ptr; + + if (netif_is_bridge_master(info->upper_dev)) { + if (info->linking) + ret = mtipl2_ndev_port_link(ndev, + info->upper_dev); + else + mtipl2_netdevice_port_unlink(ndev); + } + break; + default: + return NOTIFY_DONE; + } + + return notifier_from_errno(ret); +} + +static struct notifier_block mtipl2_netdevice_nb __read_mostly = { + .notifier_call = mtipl2_netdevice_event, +}; + +static int mtipl2_register_notifiers(struct mtipl2sw_priv *priv) +{ + int ret = 0; + + ret = register_netdevice_notifier(&mtipl2_netdevice_nb); + if (ret) { + dev_err(priv->dev, "can't register netdevice notifier\n"); + return ret; + } + + return ret; +} + +static void mtipl2_unregister_notifiers(struct mtipl2sw_priv *priv) +{ + unregister_netdevice_notifier(&mtipl2_netdevice_nb); +} + +static int mtipl2_sw_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct mtipl2sw_priv *priv; + struct resource *r; + int ret; + + pr_err("fec: %s\n", __func__); + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + mtipl2_parse_of(priv, np); + + /* Wait till eth[01] interfaces are up and running */ + if (!priv->fep[0] || !priv->fep[1] || + !netif_device_present(priv->fep[0]->netdev) || + !netif_device_present(priv->fep[1]->netdev)) + return -EPROBE_DEFER; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->fecp = devm_ioremap_resource(&pdev->dev, r); + + r = platform_get_resource(pdev, IORESOURCE_MEM, 1); + priv->hwentry = devm_ioremap_resource(&pdev->dev, r); + + r = platform_get_resource(pdev, IORESOURCE_MEM, 2); + priv->hwpsw = devm_ioremap_resource(&pdev->dev, r); + + /* + * MAC{01} interrupt and descriptors registers have 4 bytes + * offset when compared to L2 switch IP block. + * + * When L2 switch is added "between" ENET (eth0) and MAC{01} + * the code for interrupts and setting up descriptors is + * reused. + * + * As for example FEC_IMASK are used also on MAC{01} to + * perform MII transmission it is better to subtract the + * offset from the outset and reuse defines. + */ + priv->hwpsw -= L2SW_CTRL_REG_OFFSET; + + priv->fep[0]->hwpsw = priv->hwpsw; + priv->fep[1]->hwpsw = priv->hwpsw; + + priv->fep[0]->mtipl2 = priv; + priv->fep[1]->mtipl2 = priv; + + ret = devm_request_irq(&pdev->dev, platform_get_irq(pdev, 0), + mtip_l2sw_interrupt_handler, + 0, "mtip_l2sw", priv); + + ret = mtipl2_register_notifiers(priv); + if (ret) + goto clean_unregister_netdev; + + return 0; + + clean_unregister_netdev: + mtipl2_unregister_notifiers(priv); + + return ret; +} + +static const struct of_device_id mtipl2_of_match[] = { + { .compatible = "imx,mtip-l2switch" }, + { /* sentinel */ }, +}; + +static struct platform_driver mtipl2plat_driver = { + .probe = mtipl2_sw_probe, + .driver = { + .name = "mtipl2sw", + .of_match_table = mtipl2_of_match, + }, +}; + +module_platform_driver(mtipl2plat_driver); + +MODULE_AUTHOR("Lukasz Majewski "); +MODULE_DESCRIPTION("Driver for MTIP L2 on SOC switch"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:mtipl2sw"); diff --git a/drivers/net/ethernet/freescale/mtipsw/fec_mtip.h b/drivers/net/ethernet/freescale/mtipsw/fec_mtip.h new file mode 100644 index 000000000000..6f989cde0093 --- /dev/null +++ b/drivers/net/ethernet/freescale/mtipsw/fec_mtip.h @@ -0,0 +1,213 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2021 DENX Software Engineering GmbH + * Lukasz Majewski + */ + +#ifndef __MTIP_L2SWITCH_H_ +#define __MTIP_L2SWITCH_H_ + +/* 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_STATRST BIT(31) + +/* Port 0 backpressure congestion threshold */ +#define P0BC_THRESHOLD 0x40 + +struct esw_output_queue_status { + unsigned long ESW_MMSR; + unsigned long ESW_LMT; + unsigned long ESW_LFC; + unsigned long ESW_PCSR; + unsigned long ESW_IOSR; + unsigned long ESW_QWT; + unsigned long esw_reserved; + unsigned long ESW_P0BCT; +}; +struct esw_statistics_status { + /* + * Total number of incoming frames processed + * but discarded in switch + */ + unsigned long ESW_DISCN; + /*Sum of bytes of frames counted in ESW_DISCN*/ + unsigned long ESW_DISCB; + /* + * Total number of incoming frames processed + * but not discarded in switch + */ + unsigned long ESW_NDISCN; + /*Sum of bytes of frames counted in ESW_NDISCN*/ + unsigned long ESW_NDISCB; +}; + +struct esw_port_statistics_status { + /*outgoing frames discarded due to transmit queue congestion*/ + unsigned long MCF_ESW_POQC; + /*incoming frames discarded due to VLAN domain mismatch*/ + unsigned long MCF_ESW_PMVID; + /*incoming frames discarded due to untagged discard*/ + unsigned long MCF_ESW_PMVTAG; + /*incoming frames discarded due port is in blocking state*/ + unsigned long MCF_ESW_PBL; +}; + +struct l2switch_t { + unsigned long ESW_REVISION; + unsigned long ESW_SCRATCH; + unsigned long ESW_PER; + unsigned long reserved0[1]; + unsigned long ESW_VLANV; + unsigned long ESW_DBCR; + unsigned long ESW_DMCR; + unsigned long ESW_BKLR; + unsigned long ESW_BMPC; + unsigned long ESW_MODE; + unsigned long ESW_VIMSEL; + unsigned long ESW_VOMSEL; + unsigned long ESW_VIMEN; + unsigned long ESW_VID;/*0x34*/ + /*from 0x38 0x3C*/ + unsigned long esw_reserved0[2]; + unsigned long ESW_MCR;/*0x40*/ + unsigned long ESW_EGMAP; + unsigned long ESW_INGMAP; + unsigned long ESW_INGSAL; + unsigned long ESW_INGSAH; + unsigned long ESW_INGDAL; + unsigned long ESW_INGDAH; + unsigned long ESW_ENGSAL; + unsigned long ESW_ENGSAH; + unsigned long ESW_ENGDAL; + unsigned long ESW_ENGDAH; + unsigned long ESW_MCVAL;/*0x6C*/ + /*from 0x70--0x7C*/ + unsigned long esw_reserved1[4]; + unsigned long ESW_MMSR;/*0x80*/ + unsigned long ESW_LMT; + unsigned long ESW_LFC; + unsigned long ESW_PCSR; + unsigned long ESW_IOSR; + unsigned long ESW_QWT;/*0x94*/ + unsigned long esw_reserved2[1];/*0x98*/ + unsigned long ESW_P0BCT;/*0x9C*/ + /*from 0xA0-0xB8*/ + unsigned long esw_reserved3[7]; + unsigned long ESW_P0FFEN;/*0xBC*/ + unsigned long ESW_PSNP[8]; + unsigned long ESW_IPSNP[8]; + unsigned long ESW_PVRES[3]; + /*from 0x10C-0x13C*/ + unsigned long esw_reserved4[13]; + unsigned long ESW_IPRES;/*0x140*/ + /*from 0x144-0x17C*/ + unsigned long esw_reserved5[15]; + unsigned long ESW_PRES[3]; + /*from 0x18C-0x1FC*/ + unsigned long esw_reserved6[29]; + unsigned long ESW_PID[3]; + /*from 0x20C-0x27C*/ + unsigned long esw_reserved7[29]; + unsigned long ESW_VRES[32]; + unsigned long ESW_DISCN;/*0x300*/ + unsigned long ESW_DISCB; + unsigned long ESW_NDISCN; + unsigned long ESW_NDISCB;/*0xFC0DC30C*/ + struct esw_port_statistics_status port_statistics_status[3]; + /*from 0x340-0x400*/ + unsigned long esw_reserved8[48]; + + /*0xFC0DC400---0xFC0DC418*/ + /*unsigned long MCF_ESW_ISR;*/ + unsigned long switch_ievent; /* Interrupt event reg */ + /*unsigned long MCF_ESW_IMR;*/ + unsigned long switch_imask; /* Interrupt mask reg */ + /*unsigned long MCF_ESW_RDSR;*/ + unsigned long fec_r_des_start; /* Receive descriptor ring */ + /*unsigned long MCF_ESW_TDSR;*/ + unsigned long fec_x_des_start; /* Transmit descriptor ring */ + /*unsigned long MCF_ESW_MRBR;*/ + unsigned long fec_r_buff_size; /* Maximum receive buff size */ + /*unsigned long MCF_ESW_RDAR;*/ + unsigned long fec_r_des_active; /* Receive descriptor reg */ + /*unsigned long MCF_ESW_TDAR;*/ + unsigned long fec_x_des_active; /* Transmit descriptor reg */ + /*from 0x420-0x4FC*/ + unsigned long esw_reserved9[57]; + + /*0xFC0DC500---0xFC0DC508*/ + unsigned long ESW_LREC0; + unsigned long ESW_LREC1; + unsigned long ESW_LSR; +}; + +struct AddrTable64bEntry { + unsigned int lo; /* lower 32 bits */ + unsigned int hi; /* upper 32 bits */ +}; + +struct eswAddrTable_t { + struct AddrTable64bEntry eswTable64bEntry[2048]; +}; + +struct mtipl2sw_priv { + struct fec_enet_private *fep[2]; + void __iomem *hwpsw; + struct device *dev; + struct net_device *br_ndev; + + /* + * Flag to indicate if L2 switch IP block is initialized and + * running. + */ + bool l2sw_on; + + /* Number of ports */ + int n_ports; + + /* Bit field with active members */ + u8 br_members; + + /* Registers to configure/setup L2 switch IP block */ + struct l2switch_t *fecp; + + /* Look-up MAC address table start from 0x800FC000 */ + struct eswAddrTable_t *hwentry; +}; + +#define MCF_ESW_RDAR_R_DES_ACTIVE BIT(24) + +/* HW_ENET_SWI_PORT_ENA */ +#define MCF_ESW_ENA_TRANSMIT_0 BIT(0) +#define MCF_ESW_ENA_TRANSMIT_1 BIT(1) +#define MCF_ESW_ENA_TRANSMIT_2 BIT(2) + +#define MCF_ESW_ENA_RECEIVE_0 BIT(16) +#define MCF_ESW_ENA_RECEIVE_1 BIT(17) +#define MCF_ESW_ENA_RECEIVE_2 BIT(18) + +#define MCF_ESW_ENA_PORT_0 (MCF_ESW_ENA_TRANSMIT_0 | MCF_ESW_ENA_RECEIVE_0) +#define MCF_ESW_ENA_PORT_1 (MCF_ESW_ENA_TRANSMIT_1 | MCF_ESW_ENA_RECEIVE_1) +#define MCF_ESW_ENA_PORT_2 (MCF_ESW_ENA_TRANSMIT_2 | MCF_ESW_ENA_RECEIVE_2) + +/* L2 switch Maximum receive buff size */ +#define MCF_ESW_R_BUFF_SIZE 0x14 +/* L2 switch Receive Descriptor Active Register */ +#define MCF_ESW_R_RDAR 0x18 + +/* + * MCF_FEC_EMRBR corresponds to FEC_R_BUFF_SIZE_0 in fec main driver. + * It is duplicated as for L2 switch FEC_R_BUFF_SIZE_0 is aliased + * to different offset in switch IC. Hence the need to introduce new + * #define. + */ +#define MCF_FEC_EMRBR (0x188) + +#define MCF_FEC_ERDSR(x) ((x) << 2) + +#define L2SW_PKT_MAXBLR_SIZE 1520 + +#define L2SW_CTRL_REG_OFFSET 0x4 + +#endif /* __MTIP_L2SWITCH_H_ */ From patchwork Tue Jun 22 14:41:11 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lukasz Majewski X-Patchwork-Id: 12337737 X-Patchwork-Delegate: kuba@kernel.org Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-23.7 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER, INCLUDES_PATCH,MAILING_LIST_MULTI,MENTIONS_GIT_HOSTING,SPF_HELO_NONE,SPF_PASS, URIBL_BLOCKED,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id B4336C2B9F4 for ; Tue, 22 Jun 2021 14:41:38 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 94A7561378 for ; Tue, 22 Jun 2021 14:41:38 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231815AbhFVOnx (ORCPT ); Tue, 22 Jun 2021 10:43:53 -0400 Received: from phobos.denx.de ([85.214.62.61]:38992 "EHLO phobos.denx.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230047AbhFVOnv (ORCPT ); Tue, 22 Jun 2021 10:43:51 -0400 Received: from localhost.localdomain (85-222-111-42.dynamic.chello.pl [85.222.111.42]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (No client certificate requested) (Authenticated sender: lukma@denx.de) by phobos.denx.de (Postfix) with ESMTPSA id 2972C82BB3; Tue, 22 Jun 2021 16:41:34 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=denx.de; s=phobos-20191101; t=1624372894; bh=dG3L77RP3vePSdkRGpzhm3S4Y8rUB4lLeRhJWY6YmMU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=M4Rds+pIB5r3PZSJcaPMwol0/0xKBgwLhFI9uAFdbx5oxCcSXhUPqA4q99EuqaQC+ 6rLjO1aYeQqJV5OK84EtUtnc1nQG1V75hNn6qMy5Ykkwmb/upISfSNlQl+AKpEJt+z Lpi212BjK9YmOpN0SwfQz2LlnfYFZFtnFTY5Qeud3puW3vCW86P4o0LgrGtwNiP8me c9IlGAJDgzMZuQATfce3xqdIyK5gkH4AKnAaVjeycNPD+ozsbt22VsiN3ZJp/D8B/C 22JjfYNcTZvfpkfM45/8jy/Wp/ae4XxZdz3Gi3OFRFurZnqUTpfDQq3EeJA4iX+hQl fgqkWkvlKDN/w== From: Lukasz Majewski To: "David S . Miller" , Jakub Kicinski , Madalin Bucur , Nicolas Ferre , Joakim Zhang , Andrew Lunn , Florian Fainelli , Vladimir Oltean Cc: netdev@vger.kernel.org, Arnd Bergmann , Mark Einon , NXP Linux Team , linux-kernel@lists.infradead.org, Lukasz Majewski Subject: [RFC 3/3] net: imx: Adjust fec_main.c to provide support for L2 switch Date: Tue, 22 Jun 2021 16:41:11 +0200 Message-Id: <20210622144111.19647-4-lukma@denx.de> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20210622144111.19647-1-lukma@denx.de> References: <20210622144111.19647-1-lukma@denx.de> MIME-Version: 1.0 X-Virus-Scanned: clamav-milter 0.103.2 at phobos.denx.de X-Virus-Status: Clean Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: kuba@kernel.org X-Patchwork-State: RFC This patch provides the code for re-using generic FEC i.MX code for More Than IP L2 switch. The trick here is to reconfigure FEC driver to use DMA0 as the one connected to switch's port 0. Port 1 and 2 are then connected to ENET-MAC. The internal connection diagram can be found here [0]. This code has been developed on i.MX287 device, but this switch (from More ThanIP) can be also found on e.g. NXP's Vybrid VF610 SoC. To reuse as much code as possible it was also necessary to introduce fec_hwp() wrapper, which either returns pointer to MAC or L2 switch interrupt and buffer descriptors. Following registers shall be used with it: FEC_{IEVENT|IMASK}, FEC_MTIP_R_DES_ACTIVE_0, FEC_MTIP_X_DES_ACTIVE_0, FEC_MTIP_R_DES_START_0, FEC_MTIP_X_DES_START_0 This driver introduces special wrappers to map on fly DMA0 to either ENET-MAC0 or L2 switch port 0. The most intrusive changes when L2 switch is used: - Disable RACC (Receive Data Accelerator) When this feature is enabled the data is shifted by 16 bits and hence received packets are malformed when we try to pass data from one switch port to another. - L2 switch shall pass the whole pkt_len packet. Current FEC code subtract 4 to remove FCS The control of FEC clock and promisc is handled by bridge driver, so there is no need for adjustments. This driver forward ports the legacy driver [1][2] into contemporary Linux. Last but not least - some functions had to be exported to allow proper configuration of ethernet interfaces when bridging was enabled/disabled. Links: [0] - Chapter 29.3.1 in "i.MX28 Applications Processor Reference Manual, Rev. 2, 08/2013" [1] >From 2.6 i.MX kernel: Repo: git://git.freescale.com/imx/linux-2.6-imx.git Branch: imx_2.6.35_maintain SHA1: b3912bb8a4caf3ec50909135e88af959982c43ca [2] - https://github.com/lmajewski/linux-imx28-l2switch/commits/master Signed-off-by: Lukasz Majewski --- drivers/net/ethernet/freescale/fec.h | 36 ++++++ drivers/net/ethernet/freescale/fec_main.c | 139 ++++++++++++++++++---- 2 files changed, 151 insertions(+), 24 deletions(-) diff --git a/drivers/net/ethernet/freescale/fec.h b/drivers/net/ethernet/freescale/fec.h index 0602d5d5d2ee..dc2d31321cbd 100644 --- a/drivers/net/ethernet/freescale/fec.h +++ b/drivers/net/ethernet/freescale/fec.h @@ -29,6 +29,10 @@ */ #define FEC_IEVENT 0x004 /* Interrupt event reg */ #define FEC_IMASK 0x008 /* Interrupt mask reg */ +#ifdef CONFIG_FEC_MTIP_L2SW +#define FEC_MTIP_R_DES_ACTIVE_0 0x018 /* L2 switch Receive descriptor reg */ +#define FEC_MTIP_X_DES_ACTIVE_0 0x01C /* L2 switch Transmit descriptor reg */ +#endif #define FEC_R_DES_ACTIVE_0 0x010 /* Receive descriptor reg */ #define FEC_X_DES_ACTIVE_0 0x014 /* Transmit descriptor reg */ #define FEC_ECNTRL 0x024 /* Ethernet control reg */ @@ -59,6 +63,10 @@ #define FEC_R_DES_START_2 0x16c /* Receive descriptor ring 2 */ #define FEC_X_DES_START_2 0x170 /* Transmit descriptor ring 2 */ #define FEC_R_BUFF_SIZE_2 0x174 /* Maximum receive buff ring2 size */ +#ifdef CONFIG_FEC_MTIP_L2SW +#define FEC_MTIP_R_DES_START_0 0x0C /* L2 switch Receive descriptor ring */ +#define FEC_MTIP_X_DES_START_0 0x10 /* L2 switch Transmit descriptor ring */ +#endif #define FEC_R_DES_START_0 0x180 /* Receive descriptor ring */ #define FEC_X_DES_START_0 0x184 /* Transmit descriptor ring */ #define FEC_R_BUFF_SIZE_0 0x188 /* Maximum receive buff size */ @@ -376,6 +384,12 @@ struct bufdesc_ex { #define FEC_ENET_TS_AVAIL ((uint)0x00010000) #define FEC_ENET_TS_TIMER ((uint)0x00008000) +#ifdef CONFIG_FEC_MTIP_L2SW +#define FEC_MTIP_ENET_TXF ((uint)0x00000010) /* Full frame transmitted */ +#define FEC_MTIP_ENET_RXF ((uint)0x00000004) /* Full frame received */ +#define FEC_MTIP_DEFAULT_IMASK (FEC_MTIP_ENET_TXF | FEC_MTIP_ENET_RXF) +#define FEC_MTIP_RX_DISABLED_IMASK (FEC_MTIP_DEFAULT_IMASK & (~FEC_MTIP_ENET_RXF)) +#endif #define FEC_DEFAULT_IMASK (FEC_ENET_TXF | FEC_ENET_RXF) #define FEC_RX_DISABLED_IMASK (FEC_DEFAULT_IMASK & (~FEC_ENET_RXF)) @@ -595,9 +609,31 @@ struct fec_enet_private { int pps_enable; unsigned int next_counter; +#ifdef CONFIG_FEC_MTIP_L2SW + /* More Than IP L2 switch */ + void __iomem *hwpsw; + struct mtipl2sw_priv *mtipl2; + bool mtip_l2sw; +#endif u64 ethtool_stats[]; }; +/* MTIP L2 switch */ +/* Get proper base address for either L2 switch or MAC ENET */ +static inline void __iomem *fec_hwp(struct fec_enet_private *fep) +{ +#ifdef CONFIG_FEC_MTIP_L2SW + if (fep->mtip_l2sw) + return fep->hwpsw; +#endif + return fep->hwp; +} + +int fec_enet_close(struct net_device *ndev); +int fec_enet_open(struct net_device *ndev); +const unsigned short fec_offset_des_active_rxq(struct fec_enet_private *, int); +const unsigned short fec_offset_des_active_txq(struct fec_enet_private *, int); +struct fec_enet_private *fec_get_priv(const struct net_device *ndev); void fec_ptp_init(struct platform_device *pdev, int irq_idx); void fec_ptp_stop(struct platform_device *pdev); void fec_ptp_start_cyclecounter(struct net_device *ndev); diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index 3db882322b2b..797ed7e443ee 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -69,6 +69,9 @@ #include #include "fec.h" +#ifdef CONFIG_FEC_MTIP_L2SW +#include "./mtipsw/fec_mtip.h" +#endif static void set_multicast_list(struct net_device *ndev); static void fec_enet_itr_coal_init(struct net_device *ndev); @@ -99,9 +102,11 @@ static const struct fec_devinfo fec_imx27_info = { static const struct fec_devinfo fec_imx28_info = { .quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_SWAP_FRAME | - FEC_QUIRK_SINGLE_MDIO | FEC_QUIRK_HAS_RACC | - FEC_QUIRK_HAS_FRREG | FEC_QUIRK_CLEAR_SETUP_MII | - FEC_QUIRK_NO_HARD_RESET, + FEC_QUIRK_SINGLE_MDIO | FEC_QUIRK_HAS_FRREG | +#ifndef CONFIG_FEC_MTIP_L2SW + FEC_QUIRK_HAS_RACC | +#endif + FEC_QUIRK_CLEAR_SETUP_MII | FEC_QUIRK_NO_HARD_RESET, }; static const struct fec_devinfo fec_imx6q_info = { @@ -278,6 +283,46 @@ MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address"); static int mii_cnt; +static inline const unsigned short +offset_des_start_rxq(struct fec_enet_private *fep, int i) +{ +#ifdef CONFIG_FEC_MTIP_L2SW + if (fep->mtip_l2sw) + return FEC_MTIP_R_DES_START_0; +#endif + return FEC_R_DES_START(i); +} + +static inline const u32 +fec_rx_disabled_imask(struct fec_enet_private *fep) +{ +#ifdef CONFIG_FEC_MTIP_L2SW + if (fep->mtip_l2sw) + return FEC_MTIP_RX_DISABLED_IMASK; +#endif + return FEC_RX_DISABLED_IMASK; +} + +static inline const u32 +fec_default_imask(struct fec_enet_private *fep) +{ +#ifdef CONFIG_FEC_MTIP_L2SW + if (fep->mtip_l2sw) + return FEC_MTIP_DEFAULT_IMASK; +#endif + return FEC_DEFAULT_IMASK; +} + +static inline const unsigned short +offset_des_start_txq(struct fec_enet_private *fep, int i) +{ +#ifdef CONFIG_FEC_MTIP_L2SW + if (fep->mtip_l2sw) + return FEC_MTIP_X_DES_START_0; +#endif + return FEC_X_DES_START(i); +} + static struct bufdesc *fec_enet_get_nextdesc(struct bufdesc *bdp, struct bufdesc_prop *bd) { @@ -898,8 +943,9 @@ static void fec_enet_enable_ring(struct net_device *ndev) for (i = 0; i < fep->num_rx_queues; i++) { rxq = fep->rx_queue[i]; - writel(rxq->bd.dma, fep->hwp + FEC_R_DES_START(i)); - writel(PKT_MAXBUF_SIZE, fep->hwp + FEC_R_BUFF_SIZE(i)); + writel(rxq->bd.dma, fec_hwp(fep) + + offset_des_start_rxq(fep, i)); + writel(PKT_MAXBUF_SIZE, fec_hwp(fep) + FEC_R_BUFF_SIZE(i)); /* enable DMA1/2 */ if (i) @@ -909,8 +955,8 @@ static void fec_enet_enable_ring(struct net_device *ndev) for (i = 0; i < fep->num_tx_queues; i++) { txq = fep->tx_queue[i]; - writel(txq->bd.dma, fep->hwp + FEC_X_DES_START(i)); - + writel(txq->bd.dma, fec_hwp(fep) + + offset_des_start_txq(fep, i)); /* enable DMA1/2 */ if (i) writel(DMA_CLASS_EN | IDLE_SLOPE(i), @@ -1117,9 +1163,9 @@ fec_restart(struct net_device *ndev) /* Enable interrupts we wish to service */ if (fep->link) - writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK); + writel(fec_default_imask(fep), fec_hwp(fep) + FEC_IMASK); else - writel(0, fep->hwp + FEC_IMASK); + writel(0, fec_hwp(fep) + FEC_IMASK); /* Init the interrupt coalescing */ fec_enet_itr_coal_init(ndev); @@ -1170,9 +1216,10 @@ fec_stop(struct net_device *ndev) writel(1, fep->hwp + FEC_ECNTRL); udelay(10); } - writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK); + writel(fec_default_imask(fep), fec_hwp(fep) + FEC_IMASK); } else { - writel(FEC_DEFAULT_IMASK | FEC_ENET_WAKEUP, fep->hwp + FEC_IMASK); + writel(fec_default_imask(fep) | FEC_ENET_WAKEUP, + fec_hwp(fep) + FEC_IMASK); val = readl(fep->hwp + FEC_ECNTRL); val |= (FEC_ECR_MAGICEN | FEC_ECR_SLEEP); writel(val, fep->hwp + FEC_ECNTRL); @@ -1413,7 +1460,7 @@ fec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id) unsigned short status; struct sk_buff *skb_new = NULL; struct sk_buff *skb; - ushort pkt_len; + ushort pkt_len, l2pl; __u8 *data; int pkt_received = 0; struct bufdesc_ex *ebdp = NULL; @@ -1439,7 +1486,7 @@ fec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id) break; pkt_received++; - writel(FEC_ENET_RXF, fep->hwp + FEC_IEVENT); + writel(FEC_ENET_RXF, fec_hwp(fep) + FEC_IEVENT); /* Check for errors. */ status ^= BD_ENET_RX_LAST; @@ -1479,7 +1526,17 @@ fec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id) * include that when passing upstream as it messes up * bridging applications. */ - is_copybreak = fec_enet_copybreak(ndev, &skb, bdp, pkt_len - 4, + /* + * When the L2 switch support is enabled the FCS is removed + * by it and hence the pkt_len shall be passed without + * substracting 4 bytes. + */ + l2pl = pkt_len - 4; +#ifdef CONFIG_FEC_MTIP_L2SW + if (fep->mtip_l2sw) + l2pl = pkt_len; +#endif + is_copybreak = fec_enet_copybreak(ndev, &skb, bdp, l2pl, need_swap); if (!is_copybreak) { skb_new = netdev_alloc_skb(ndev, FEC_ENET_RX_FRSIZE); @@ -1494,7 +1551,7 @@ fec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id) } prefetch(skb->data - NET_IP_ALIGN); - skb_put(skb, pkt_len - 4); + skb_put(skb, l2pl); data = skb->data; if (!is_copybreak && need_swap) @@ -1654,7 +1711,7 @@ static int fec_enet_rx_napi(struct napi_struct *napi, int budget) if (done < budget) { napi_complete_done(napi, done); - writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK); + writel(fec_default_imask(fep), fec_hwp(fep) + FEC_IMASK); } return done; @@ -2971,7 +3028,7 @@ static int fec_enet_alloc_buffers(struct net_device *ndev) return 0; } -static int +int fec_enet_open(struct net_device *ndev) { struct fec_enet_private *fep = netdev_priv(ndev); @@ -3042,8 +3099,9 @@ fec_enet_open(struct net_device *ndev) pinctrl_pm_select_sleep_state(&fep->pdev->dev); return ret; } +EXPORT_SYMBOL_GPL(fec_enet_open) -static int +int fec_enet_close(struct net_device *ndev) { struct fec_enet_private *fep = netdev_priv(ndev); @@ -3072,6 +3130,7 @@ fec_enet_close(struct net_device *ndev) return 0; } +EXPORT_SYMBOL_GPL(fec_enet_close); /* Set or clear the multicast filter for this adaptor. * Skeleton taken from sunlance driver. @@ -3240,14 +3299,45 @@ static const struct net_device_ops fec_netdev_ops = { .ndo_set_features = fec_set_features, }; -static const unsigned short offset_des_active_rxq[] = { +#ifdef CONFIG_FEC_MTIP_L2SW +struct fec_enet_private *fec_get_priv(const struct net_device *ndev) +{ + if (ndev->netdev_ops == &fec_netdev_ops) + return netdev_priv(ndev); + + return NULL; +} +EXPORT_SYMBOL_GPL(fec_get_priv) +#endif + +static const unsigned short __offset_des_active_rxq[] = { FEC_R_DES_ACTIVE_0, FEC_R_DES_ACTIVE_1, FEC_R_DES_ACTIVE_2 }; -static const unsigned short offset_des_active_txq[] = { +static const unsigned short __offset_des_active_txq[] = { FEC_X_DES_ACTIVE_0, FEC_X_DES_ACTIVE_1, FEC_X_DES_ACTIVE_2 }; +const unsigned short +fec_offset_des_active_rxq(struct fec_enet_private *fep, int i) +{ +#ifdef CONFIG_FEC_MTIP_L2SW + if (fep->mtip_l2sw) + return FEC_MTIP_R_DES_ACTIVE_0; +#endif + return __offset_des_active_rxq[i]; +} + +const unsigned short +fec_offset_des_active_txq(struct fec_enet_private *fep, int i) +{ +#ifdef CONFIG_FEC_MTIP_L2SW + if (fep->mtip_l2sw) + return FEC_MTIP_X_DES_ACTIVE_0; +#endif + return __offset_des_active_txq[i]; +} + /* * XXX: We need to clean up on failure exits here. * @@ -3307,7 +3397,8 @@ static int fec_enet_init(struct net_device *ndev) rxq->bd.dma = bd_dma; rxq->bd.dsize = dsize; rxq->bd.dsize_log2 = dsize_log2; - rxq->bd.reg_desc_active = fep->hwp + offset_des_active_rxq[i]; + rxq->bd.reg_desc_active = + fec_hwp(fep) + fec_offset_des_active_rxq(fep, i); bd_dma += size; cbd_base = (struct bufdesc *)(((void *)cbd_base) + size); rxq->bd.last = (struct bufdesc *)(((void *)cbd_base) - dsize); @@ -3323,7 +3414,8 @@ static int fec_enet_init(struct net_device *ndev) txq->bd.dma = bd_dma; txq->bd.dsize = dsize; txq->bd.dsize_log2 = dsize_log2; - txq->bd.reg_desc_active = fep->hwp + offset_des_active_txq[i]; + txq->bd.reg_desc_active = + fec_hwp(fep) + fec_offset_des_active_txq(fep, i); bd_dma += size; cbd_base = (struct bufdesc *)(((void *)cbd_base) + size); txq->bd.last = (struct bufdesc *)(((void *)cbd_base) - dsize); @@ -3334,8 +3426,7 @@ static int fec_enet_init(struct net_device *ndev) ndev->watchdog_timeo = TX_TIMEOUT; ndev->netdev_ops = &fec_netdev_ops; ndev->ethtool_ops = &fec_enet_ethtool_ops; - - writel(FEC_RX_DISABLED_IMASK, fep->hwp + FEC_IMASK); + writel(fec_rx_disabled_imask(fep), fec_hwp(fep) + FEC_IMASK); netif_napi_add(ndev, &fep->napi, fec_enet_rx_napi, NAPI_POLL_WEIGHT); if (fep->quirks & FEC_QUIRK_HAS_VLAN)