From patchwork Mon Jun 20 11:08:31 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?b?Q2zDqW1lbnQgTMOpZ2Vy?= X-Patchwork-Id: 583450 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 0EEE9CCA479 for ; Mon, 20 Jun 2022 11:10:01 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S241275AbiFTLKA (ORCPT ); Mon, 20 Jun 2022 07:10:00 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:59262 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S241245AbiFTLJw (ORCPT ); Mon, 20 Jun 2022 07:09:52 -0400 Received: from relay10.mail.gandi.net (relay10.mail.gandi.net [217.70.178.230]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 07062B39; Mon, 20 Jun 2022 04:09:50 -0700 (PDT) Received: (Authenticated sender: clement.leger@bootlin.com) by mail.gandi.net (Postfix) with ESMTPSA id 1E98E240003; Mon, 20 Jun 2022 11:09:47 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1655723389; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=tExyiPeImu1Nxom9h3UVjRKBgN09NvUrwAIoO3Vi5fo=; b=M+9/XAwaoppX9CeOh3j11B3QaCPSuD4yk02CiY7WbgioC9z/M3ODt2OQWqH/En/NohY4CN NH8ZojulZUtLP3IXYgqOFbjzFss8wxpc60yGgnPUit0FtclSgOzfQ+DIZyrNR7HsDNQHqs 5bEbh6bE1iI857N/HOF6yn+dBMQQooFhMrlXiXOH+vfLRosAUvWdvm5L/R37BWEtXAe72Y A7nZ6cMdQUODzdMWl6PM2h1svJBazXp5jYDf796GhPximki7RGRThz2JIZbws2EvrYPqFw 2XAFbubxIhhIeybk8Sw+HQzGt0zyFUqMPkbhE9xQuRmoA+d4IX/U2yPFwykrcw== From: =?utf-8?b?Q2zDqW1lbnQgTMOpZ2Vy?= To: Andrew Lunn , Vivien Didelot , Florian Fainelli , Vladimir Oltean , "David S . Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Rob Herring , Krzysztof Kozlowski , Geert Uytterhoeven , Magnus Damm , Heiner Kallweit , Russell King , Alexandre Torgue , Giuseppe Cavallaro , Jose Abreu Cc: =?utf-8?b?Q2zDqW1lbnQgTMOpZ2Vy?= , Thomas Petazzoni , Herve Codina , =?utf-8?q?Miqu=C3=A8l_Raynal?= , Milan Stevanovic , Jimmy Lalande , Pascal Eberhard , linux-kernel@vger.kernel.org, devicetree@vger.kernel.org, linux-renesas-soc@vger.kernel.org, netdev@vger.kernel.org Subject: [PATCH net-next v8 01/16] net: dsa: allow port_bridge_join() to override extack message Date: Mon, 20 Jun 2022 13:08:31 +0200 Message-Id: <20220620110846.374787-2-clement.leger@bootlin.com> X-Mailer: git-send-email 2.36.1 In-Reply-To: <20220620110846.374787-1-clement.leger@bootlin.com> References: <20220620110846.374787-1-clement.leger@bootlin.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: devicetree@vger.kernel.org Some drivers might report that they are unable to bridge ports by returning -EOPNOTSUPP, but still wants to override extack message. In order to do so, in dsa_slave_changeupper(), if port_bridge_join() returns -EOPNOTSUPP, check if extack message is set and if so, do not override it. Signed-off-by: Clément Léger Reviewed-by: Vladimir Oltean Reviewed-by: Florian Fainelli --- net/dsa/slave.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 2e1ac638d135..509b98dd9954 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -2460,8 +2460,9 @@ static int dsa_slave_changeupper(struct net_device *dev, if (!err) dsa_bridge_mtu_normalization(dp); if (err == -EOPNOTSUPP) { - NL_SET_ERR_MSG_MOD(extack, - "Offloading not supported"); + if (!extack->_msg) + NL_SET_ERR_MSG_MOD(extack, + "Offloading not supported"); err = 0; } err = notifier_from_errno(err); From patchwork Mon Jun 20 11:08:33 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?b?Q2zDqW1lbnQgTMOpZ2Vy?= X-Patchwork-Id: 583449 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 403A3CCA480 for ; Mon, 20 Jun 2022 11:10:05 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S241327AbiFTLKC (ORCPT ); Mon, 20 Jun 2022 07:10:02 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:59352 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S241291AbiFTLJ7 (ORCPT ); Mon, 20 Jun 2022 07:09:59 -0400 Received: from relay10.mail.gandi.net (relay10.mail.gandi.net [217.70.178.230]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 96D3A5FCE; Mon, 20 Jun 2022 04:09:56 -0700 (PDT) Received: (Authenticated sender: clement.leger@bootlin.com) by mail.gandi.net (Postfix) with ESMTPSA id A31D9240002; Mon, 20 Jun 2022 11:09:52 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1655723395; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=ENiIOgykgSpx1knvrJI0hPtPg1YoaWo9Ba1CxSaTMF4=; b=JP90e+jxq7zRrN57Xjl0Q5UkeAaG6tJpMztNXQ5/NB0GkMUCBsQXNgJRW5+J0U7w13qOI1 93YbMfLftFN+/WON7XGugk3EPTpVN3JbrfYoflTLPO514Ic9EcMWZuBN40Bsww6vYS6Udx ZMQnr0W095vOf2YR2SwrFnSiY3lX3i919vntUIwARaEchGVljwQGyFcdsq2QVRq91bZ0Ir +48XN7kbRviHvZXo91SDbmglBYQuAAThjUDHYhEUyZtrmGy0bXgF51BQ2gP90+KIm2VMFs Xb1W7OeF75Ho1AHRLS/h7aNKmcBhKwuch4hjExoAicniTwLuKaDFBRII2KfgTQ== From: =?utf-8?b?Q2zDqW1lbnQgTMOpZ2Vy?= To: Andrew Lunn , Vivien Didelot , Florian Fainelli , Vladimir Oltean , "David S . Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Rob Herring , Krzysztof Kozlowski , Geert Uytterhoeven , Magnus Damm , Heiner Kallweit , Russell King , Alexandre Torgue , Giuseppe Cavallaro , Jose Abreu Cc: =?utf-8?b?Q2zDqW1lbnQgTMOpZ2Vy?= , Thomas Petazzoni , Herve Codina , =?utf-8?q?Miqu=C3=A8l_Raynal?= , Milan Stevanovic , Jimmy Lalande , Pascal Eberhard , linux-kernel@vger.kernel.org, devicetree@vger.kernel.org, linux-renesas-soc@vger.kernel.org, netdev@vger.kernel.org Subject: [PATCH net-next v8 03/16] net: dsa: add Renesas RZ/N1 switch tag driver Date: Mon, 20 Jun 2022 13:08:33 +0200 Message-Id: <20220620110846.374787-4-clement.leger@bootlin.com> X-Mailer: git-send-email 2.36.1 In-Reply-To: <20220620110846.374787-1-clement.leger@bootlin.com> References: <20220620110846.374787-1-clement.leger@bootlin.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: devicetree@vger.kernel.org The switch that is present on the Renesas RZ/N1 SoC uses a specific VLAN value followed by 6 bytes which contains forwarding configuration. Signed-off-by: Clément Léger Reviewed-by: Vladimir Oltean Reviewed-by: Florian Fainelli --- include/net/dsa.h | 2 + include/uapi/linux/if_ether.h | 1 + net/dsa/Kconfig | 7 +++ net/dsa/Makefile | 1 + net/dsa/tag_rzn1_a5psw.c | 113 ++++++++++++++++++++++++++++++++++ 5 files changed, 124 insertions(+) create mode 100644 net/dsa/tag_rzn1_a5psw.c diff --git a/include/net/dsa.h b/include/net/dsa.h index 64da5ed27fdc..33283eeda697 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -53,6 +53,7 @@ struct phylink_link_state; #define DSA_TAG_PROTO_SJA1110_VALUE 23 #define DSA_TAG_PROTO_RTL8_4_VALUE 24 #define DSA_TAG_PROTO_RTL8_4T_VALUE 25 +#define DSA_TAG_PROTO_RZN1_A5PSW_VALUE 26 enum dsa_tag_protocol { DSA_TAG_PROTO_NONE = DSA_TAG_PROTO_NONE_VALUE, @@ -81,6 +82,7 @@ enum dsa_tag_protocol { DSA_TAG_PROTO_SJA1110 = DSA_TAG_PROTO_SJA1110_VALUE, DSA_TAG_PROTO_RTL8_4 = DSA_TAG_PROTO_RTL8_4_VALUE, DSA_TAG_PROTO_RTL8_4T = DSA_TAG_PROTO_RTL8_4T_VALUE, + DSA_TAG_PROTO_RZN1_A5PSW = DSA_TAG_PROTO_RZN1_A5PSW_VALUE, }; struct dsa_switch; diff --git a/include/uapi/linux/if_ether.h b/include/uapi/linux/if_ether.h index 1d0bccc3fa54..d370165bc621 100644 --- a/include/uapi/linux/if_ether.h +++ b/include/uapi/linux/if_ether.h @@ -116,6 +116,7 @@ #define ETH_P_QINQ3 0x9300 /* deprecated QinQ VLAN [ NOT AN OFFICIALLY REGISTERED ID ] */ #define ETH_P_EDSA 0xDADA /* Ethertype DSA [ NOT AN OFFICIALLY REGISTERED ID ] */ #define ETH_P_DSA_8021Q 0xDADB /* Fake VLAN Header for DSA [ NOT AN OFFICIALLY REGISTERED ID ] */ +#define ETH_P_DSA_A5PSW 0xE001 /* A5PSW Tag Value [ NOT AN OFFICIALLY REGISTERED ID ] */ #define ETH_P_IFE 0xED3E /* ForCES inter-FE LFB type */ #define ETH_P_AF_IUCV 0xFBFB /* IBM af_iucv [ NOT AN OFFICIALLY REGISTERED ID ] */ diff --git a/net/dsa/Kconfig b/net/dsa/Kconfig index 8cb87b5067ee..63853fff4e2f 100644 --- a/net/dsa/Kconfig +++ b/net/dsa/Kconfig @@ -132,6 +132,13 @@ config NET_DSA_TAG_RTL8_4 Say Y or M if you want to enable support for tagging frames for Realtek switches with 8 byte protocol 4 tags, such as the Realtek RTL8365MB-VC. +config NET_DSA_TAG_RZN1_A5PSW + tristate "Tag driver for Renesas RZ/N1 A5PSW switch" + help + Say Y or M if you want to enable support for tagging frames for + Renesas RZ/N1 embedded switch that uses an 8 byte tag located after + destination MAC address. + config NET_DSA_TAG_LAN9303 tristate "Tag driver for SMSC/Microchip LAN9303 family of switches" help diff --git a/net/dsa/Makefile b/net/dsa/Makefile index 9f75820e7c98..af28c24ead18 100644 --- a/net/dsa/Makefile +++ b/net/dsa/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_NET_DSA_TAG_OCELOT_8021Q) += tag_ocelot_8021q.o obj-$(CONFIG_NET_DSA_TAG_QCA) += tag_qca.o obj-$(CONFIG_NET_DSA_TAG_RTL4_A) += tag_rtl4_a.o obj-$(CONFIG_NET_DSA_TAG_RTL8_4) += tag_rtl8_4.o +obj-$(CONFIG_NET_DSA_TAG_RZN1_A5PSW) += tag_rzn1_a5psw.o obj-$(CONFIG_NET_DSA_TAG_SJA1105) += tag_sja1105.o obj-$(CONFIG_NET_DSA_TAG_TRAILER) += tag_trailer.o obj-$(CONFIG_NET_DSA_TAG_XRS700X) += tag_xrs700x.o diff --git a/net/dsa/tag_rzn1_a5psw.c b/net/dsa/tag_rzn1_a5psw.c new file mode 100644 index 000000000000..e2a5ee6ae688 --- /dev/null +++ b/net/dsa/tag_rzn1_a5psw.c @@ -0,0 +1,113 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2022 Schneider Electric + * + * Clément Léger + */ + +#include +#include +#include +#include + +#include "dsa_priv.h" + +/* To define the outgoing port and to discover the incoming port a TAG is + * inserted after Src MAC : + * + * Dest MAC Src MAC TAG Type + * ...| 1 2 3 4 5 6 | 1 2 3 4 5 6 | 1 2 3 4 5 6 7 8 | 1 2 |... + * |<--------------->| + * + * See struct a5psw_tag for layout + */ + +#define ETH_P_DSA_A5PSW 0xE001 +#define A5PSW_TAG_LEN 8 +#define A5PSW_CTRL_DATA_FORCE_FORWARD BIT(0) +/* This is both used for xmit tag and rcv tagging */ +#define A5PSW_CTRL_DATA_PORT GENMASK(3, 0) + +struct a5psw_tag { + __be16 ctrl_tag; + __be16 ctrl_data; + __be16 ctrl_data2_hi; + __be16 ctrl_data2_lo; +}; + +static struct sk_buff *a5psw_tag_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct dsa_port *dp = dsa_slave_to_port(dev); + struct a5psw_tag *ptag; + u32 data2_val; + + BUILD_BUG_ON(sizeof(*ptag) != A5PSW_TAG_LEN); + + /* The Ethernet switch we are interfaced with needs packets to be at + * least 60 bytes otherwise they will be discarded when they enter the + * switch port logic. + */ + if (__skb_put_padto(skb, ETH_ZLEN, false)) + return NULL; + + /* provide 'A5PSW_TAG_LEN' bytes additional space */ + skb_push(skb, A5PSW_TAG_LEN); + + /* make room between MACs and Ether-Type to insert tag */ + dsa_alloc_etype_header(skb, A5PSW_TAG_LEN); + + ptag = dsa_etype_header_pos_tx(skb); + + data2_val = FIELD_PREP(A5PSW_CTRL_DATA_PORT, BIT(dp->index)); + ptag->ctrl_tag = htons(ETH_P_DSA_A5PSW); + ptag->ctrl_data = htons(A5PSW_CTRL_DATA_FORCE_FORWARD); + ptag->ctrl_data2_lo = htons(data2_val); + ptag->ctrl_data2_hi = 0; + + return skb; +} + +static struct sk_buff *a5psw_tag_rcv(struct sk_buff *skb, + struct net_device *dev) +{ + struct a5psw_tag *tag; + int port; + + if (unlikely(!pskb_may_pull(skb, A5PSW_TAG_LEN))) { + dev_warn_ratelimited(&dev->dev, + "Dropping packet, cannot pull\n"); + return NULL; + } + + tag = dsa_etype_header_pos_rx(skb); + + if (tag->ctrl_tag != htons(ETH_P_DSA_A5PSW)) { + dev_warn_ratelimited(&dev->dev, "Dropping packet due to invalid TAG marker\n"); + return NULL; + } + + port = FIELD_GET(A5PSW_CTRL_DATA_PORT, ntohs(tag->ctrl_data)); + + skb->dev = dsa_master_find_slave(dev, 0, port); + if (!skb->dev) + return NULL; + + skb_pull_rcsum(skb, A5PSW_TAG_LEN); + dsa_strip_etype_header(skb, A5PSW_TAG_LEN); + + dsa_default_offload_fwd_mark(skb); + + return skb; +} + +static const struct dsa_device_ops a5psw_netdev_ops = { + .name = "a5psw", + .proto = DSA_TAG_PROTO_RZN1_A5PSW, + .xmit = a5psw_tag_xmit, + .rcv = a5psw_tag_rcv, + .needed_headroom = A5PSW_TAG_LEN, +}; + +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_A5PSW); +module_dsa_tag_driver(a5psw_netdev_ops); From patchwork Mon Jun 20 11:08:35 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?b?Q2zDqW1lbnQgTMOpZ2Vy?= X-Patchwork-Id: 583448 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 4D13BC433EF for ; Mon, 20 Jun 2022 11:10:09 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S241374AbiFTLKH (ORCPT ); Mon, 20 Jun 2022 07:10:07 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:59428 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S241352AbiFTLKE (ORCPT ); Mon, 20 Jun 2022 07:10:04 -0400 Received: from relay10.mail.gandi.net (relay10.mail.gandi.net [IPv6:2001:4b98:dc4:8::230]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 4EB4355B9; Mon, 20 Jun 2022 04:10:02 -0700 (PDT) Received: (Authenticated sender: clement.leger@bootlin.com) by mail.gandi.net (Postfix) with ESMTPSA id 9A9F624000B; Mon, 20 Jun 2022 11:09:58 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1655723400; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=ywsUcFT+AmKQedVr/xIG8o4SHWDmoIJPfk298Jklb94=; b=FnkTVHLrKrT8kZmrKa6xxRuN0RXq1kVX4imnyDPJC6e6qWdC/WrcJyWyHla2Ctlx/NJzMm LCMVgnHnuRHQF/UAZiUwptNLfS6ejK4vktCj7UidJwsqYYW8tj99m//Tc07VLYwIgoL4qx AnH4ibXM5libHgergWkprx0VBiB5vCS4zj9XkIcZK/+ckqA13Vs9A8PFsuLs7ze7uAOvYX cAUejw3eFviowX/+VZvvMo10QlpFP/EHx5zi9dbp+5LTRx1bzVKXZAAIAGPnwj6d8xF5Xi YEqCPeTSxQWCEkMeppMTwNTvVV7vUkkj0+4JhCM7df5WQr3wyBAAQByXI+yuPg== From: =?utf-8?b?Q2zDqW1lbnQgTMOpZ2Vy?= To: Andrew Lunn , Vivien Didelot , Florian Fainelli , Vladimir Oltean , "David S . Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Rob Herring , Krzysztof Kozlowski , Geert Uytterhoeven , Magnus Damm , Heiner Kallweit , Russell King , Alexandre Torgue , Giuseppe Cavallaro , Jose Abreu Cc: =?utf-8?b?Q2zDqW1lbnQgTMOpZ2Vy?= , Thomas Petazzoni , Herve Codina , =?utf-8?q?Miqu=C3=A8l_Raynal?= , Milan Stevanovic , Jimmy Lalande , Pascal Eberhard , linux-kernel@vger.kernel.org, devicetree@vger.kernel.org, linux-renesas-soc@vger.kernel.org, netdev@vger.kernel.org Subject: [PATCH net-next v8 05/16] net: pcs: add Renesas MII converter driver Date: Mon, 20 Jun 2022 13:08:35 +0200 Message-Id: <20220620110846.374787-6-clement.leger@bootlin.com> X-Mailer: git-send-email 2.36.1 In-Reply-To: <20220620110846.374787-1-clement.leger@bootlin.com> References: <20220620110846.374787-1-clement.leger@bootlin.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: devicetree@vger.kernel.org Add a PCS driver for the MII converter that is present on the Renesas RZ/N1 SoC. This MII converter is reponsible for converting MII to RMII/RGMII or act as a MII pass-trough. Exposing it as a PCS allows to reuse it in both the switch driver and the stmmac driver. Currently, this driver only allows the PCS to be used by the dual Cortex-A7 subsystem since the register locking system is not used. Signed-off-by: Clément Léger --- drivers/net/pcs/Kconfig | 8 + drivers/net/pcs/Makefile | 1 + drivers/net/pcs/pcs-rzn1-miic.c | 520 ++++++++++++++++++++++++++++++++ include/linux/pcs-rzn1-miic.h | 18 ++ 4 files changed, 547 insertions(+) create mode 100644 drivers/net/pcs/pcs-rzn1-miic.c create mode 100644 include/linux/pcs-rzn1-miic.h diff --git a/drivers/net/pcs/Kconfig b/drivers/net/pcs/Kconfig index 22ba7b0b476d..6a500d30ca4c 100644 --- a/drivers/net/pcs/Kconfig +++ b/drivers/net/pcs/Kconfig @@ -18,4 +18,12 @@ config PCS_LYNX This module provides helpers to phylink for managing the Lynx PCS which is part of the Layerscape and QorIQ Ethernet SERDES. +config PCS_RZN1_MIIC + tristate "Renesas RZ/N1 MII converter" + depends on ARCH_RZN1 || COMPILE_TEST + help + This module provides a driver for the MII converter that is available + on RZ/N1 SoCs. This PCS converts MII to RMII/RGMII or can be set in + pass-through mode for MII. + endmenu diff --git a/drivers/net/pcs/Makefile b/drivers/net/pcs/Makefile index 0603d469bd57..0ff5388fcdea 100644 --- a/drivers/net/pcs/Makefile +++ b/drivers/net/pcs/Makefile @@ -5,3 +5,4 @@ pcs_xpcs-$(CONFIG_PCS_XPCS) := pcs-xpcs.o pcs-xpcs-nxp.o obj-$(CONFIG_PCS_XPCS) += pcs_xpcs.o obj-$(CONFIG_PCS_LYNX) += pcs-lynx.o +obj-$(CONFIG_PCS_RZN1_MIIC) += pcs-rzn1-miic.o diff --git a/drivers/net/pcs/pcs-rzn1-miic.c b/drivers/net/pcs/pcs-rzn1-miic.c new file mode 100644 index 000000000000..6ddc5f8ccaf8 --- /dev/null +++ b/drivers/net/pcs/pcs-rzn1-miic.c @@ -0,0 +1,520 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2022 Schneider Electric + * + * Clément Léger + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MIIC_PRCMD 0x0 +#define MIIC_ESID_CODE 0x4 + +#define MIIC_MODCTRL 0x20 +#define MIIC_MODCTRL_SW_MODE GENMASK(4, 0) + +#define MIIC_CONVCTRL(port) (0x100 + (port) * 4) + +#define MIIC_CONVCTRL_CONV_SPEED GENMASK(1, 0) +#define CONV_MODE_10MBPS 0 +#define CONV_MODE_100MBPS 1 +#define CONV_MODE_1000MBPS 2 + +#define MIIC_CONVCTRL_CONV_MODE GENMASK(3, 2) +#define CONV_MODE_MII 0 +#define CONV_MODE_RMII 1 +#define CONV_MODE_RGMII 2 + +#define MIIC_CONVCTRL_FULLD BIT(8) +#define MIIC_CONVCTRL_RGMII_LINK BIT(12) +#define MIIC_CONVCTRL_RGMII_DUPLEX BIT(13) +#define MIIC_CONVCTRL_RGMII_SPEED GENMASK(15, 14) + +#define MIIC_CONVRST 0x114 +#define MIIC_CONVRST_PHYIF_RST(port) BIT(port) +#define MIIC_CONVRST_PHYIF_RST_MASK GENMASK(4, 0) + +#define MIIC_SWCTRL 0x304 +#define MIIC_SWDUPC 0x308 + +#define MIIC_MAX_NR_PORTS 5 + +#define MIIC_MODCTRL_CONF_CONV_NUM 6 +#define MIIC_MODCTRL_CONF_NONE -1 + +/** + * struct modctrl_match - Matching table entry for convctrl configuration + * See section 8.2.1 of manual. + * @mode_cfg: Configuration value for convctrl + * @conv: Configuration of ethernet port muxes. First index is SWITCH_PORTIN, + * then index 1 - 5 are CONV1 - CONV5. + */ +struct modctrl_match { + u32 mode_cfg; + u8 conv[MIIC_MODCTRL_CONF_CONV_NUM]; +}; + +static struct modctrl_match modctrl_match_table[] = { + {0x0, {MIIC_RTOS_PORT, MIIC_GMAC1_PORT, MIIC_SWITCH_PORTD, + MIIC_SWITCH_PORTC, MIIC_SERCOS_PORTB, MIIC_SERCOS_PORTA}}, + {0x1, {MIIC_RTOS_PORT, MIIC_GMAC1_PORT, MIIC_SWITCH_PORTD, + MIIC_SWITCH_PORTC, MIIC_ETHERCAT_PORTB, MIIC_ETHERCAT_PORTA}}, + {0x2, {MIIC_RTOS_PORT, MIIC_GMAC1_PORT, MIIC_SWITCH_PORTD, + MIIC_ETHERCAT_PORTC, MIIC_ETHERCAT_PORTB, MIIC_ETHERCAT_PORTA}}, + {0x3, {MIIC_RTOS_PORT, MIIC_GMAC1_PORT, MIIC_SWITCH_PORTD, + MIIC_SWITCH_PORTC, MIIC_SWITCH_PORTB, MIIC_SWITCH_PORTA}}, + + {0x8, {MIIC_RTOS_PORT, MIIC_GMAC1_PORT, MIIC_SWITCH_PORTD, + MIIC_SWITCH_PORTC, MIIC_SERCOS_PORTB, MIIC_SERCOS_PORTA}}, + {0x9, {MIIC_RTOS_PORT, MIIC_GMAC1_PORT, MIIC_SWITCH_PORTD, + MIIC_SWITCH_PORTC, MIIC_ETHERCAT_PORTB, MIIC_ETHERCAT_PORTA}}, + {0xA, {MIIC_RTOS_PORT, MIIC_GMAC1_PORT, MIIC_SWITCH_PORTD, + MIIC_ETHERCAT_PORTC, MIIC_ETHERCAT_PORTB, MIIC_ETHERCAT_PORTA}}, + {0xB, {MIIC_RTOS_PORT, MIIC_GMAC1_PORT, MIIC_SWITCH_PORTD, + MIIC_SWITCH_PORTC, MIIC_SWITCH_PORTB, MIIC_SWITCH_PORTA}}, + + {0x10, {MIIC_GMAC2_PORT, MIIC_GMAC1_PORT, MIIC_SWITCH_PORTD, + MIIC_SWITCH_PORTC, MIIC_SERCOS_PORTB, MIIC_SERCOS_PORTA}}, + {0x11, {MIIC_GMAC2_PORT, MIIC_GMAC1_PORT, MIIC_SWITCH_PORTD, + MIIC_SWITCH_PORTC, MIIC_ETHERCAT_PORTB, MIIC_ETHERCAT_PORTA}}, + {0x12, {MIIC_GMAC2_PORT, MIIC_GMAC1_PORT, MIIC_SWITCH_PORTD, + MIIC_ETHERCAT_PORTC, MIIC_ETHERCAT_PORTB, MIIC_ETHERCAT_PORTA}}, + {0x13, {MIIC_GMAC2_PORT, MIIC_GMAC1_PORT, MIIC_SWITCH_PORTD, + MIIC_SWITCH_PORTC, MIIC_SWITCH_PORTB, MIIC_SWITCH_PORTA}} +}; + +static const char * const conf_to_string[] = { + [MIIC_GMAC1_PORT] = "GMAC1_PORT", + [MIIC_GMAC2_PORT] = "GMAC2_PORT", + [MIIC_RTOS_PORT] = "RTOS_PORT", + [MIIC_SERCOS_PORTA] = "SERCOS_PORTA", + [MIIC_SERCOS_PORTB] = "SERCOS_PORTB", + [MIIC_ETHERCAT_PORTA] = "ETHERCAT_PORTA", + [MIIC_ETHERCAT_PORTB] = "ETHERCAT_PORTB", + [MIIC_ETHERCAT_PORTC] = "ETHERCAT_PORTC", + [MIIC_SWITCH_PORTA] = "SWITCH_PORTA", + [MIIC_SWITCH_PORTB] = "SWITCH_PORTB", + [MIIC_SWITCH_PORTC] = "SWITCH_PORTC", + [MIIC_SWITCH_PORTD] = "SWITCH_PORTD", + [MIIC_HSR_PORTA] = "HSR_PORTA", + [MIIC_HSR_PORTB] = "HSR_PORTB", +}; + +static const char *index_to_string[MIIC_MODCTRL_CONF_CONV_NUM] = { + "SWITCH_PORTIN", + "CONV1", + "CONV2", + "CONV3", + "CONV4", + "CONV5", +}; + +/** + * struct miic - MII converter structure + * @base: base address of the MII converter + * @dev: Device associated to the MII converter + * @clks: Clocks used for this device + * @nclk: Number of clocks + * @lock: Lock used for read-modify-write access + */ +struct miic { + void __iomem *base; + struct device *dev; + struct clk_bulk_data *clks; + int nclk; + spinlock_t lock; +}; + +/** + * struct miic_port - Per port MII converter struct + * @miic: backiling to MII converter structure + * @pcs: PCS structure associated to the port + * @port: port number + */ +struct miic_port { + struct miic *miic; + struct phylink_pcs pcs; + int port; +}; + +static struct miic_port *phylink_pcs_to_miic_port(struct phylink_pcs *pcs) +{ + return container_of(pcs, struct miic_port, pcs); +} + +static void miic_reg_writel(struct miic *miic, int offset, u32 value) +{ + writel(value, miic->base + offset); +} + +static u32 miic_reg_readl(struct miic *miic, int offset) +{ + return readl(miic->base + offset); +} + +static void miic_reg_rmw(struct miic *miic, int offset, u32 mask, u32 val) +{ + u32 reg; + + spin_lock(&miic->lock); + + reg = miic_reg_readl(miic, offset); + reg &= ~mask; + reg |= val; + miic_reg_writel(miic, offset, reg); + + spin_unlock(&miic->lock); +} + +static void miic_converter_enable(struct miic *miic, int port, int enable) +{ + u32 val = 0; + + if (enable) + val = MIIC_CONVRST_PHYIF_RST(port); + + miic_reg_rmw(miic, MIIC_CONVRST, MIIC_CONVRST_PHYIF_RST(port), val); +} + +static int miic_config(struct phylink_pcs *pcs, unsigned int mode, + phy_interface_t interface, + const unsigned long *advertising, bool permit) +{ + struct miic_port *miic_port = phylink_pcs_to_miic_port(pcs); + struct miic *miic = miic_port->miic; + int port = miic_port->port; + u32 speed, conv_mode, val; + + switch (interface) { + case PHY_INTERFACE_MODE_RMII: + conv_mode = CONV_MODE_RMII; + speed = CONV_MODE_100MBPS; + break; + case PHY_INTERFACE_MODE_RGMII: + case PHY_INTERFACE_MODE_RGMII_ID: + case PHY_INTERFACE_MODE_RGMII_TXID: + case PHY_INTERFACE_MODE_RGMII_RXID: + conv_mode = CONV_MODE_RGMII; + speed = CONV_MODE_1000MBPS; + break; + case PHY_INTERFACE_MODE_MII: + conv_mode = CONV_MODE_MII; + /* When in MII mode, speed should be set to 0 (which is actually + * CONV_MODE_10MBPS) + */ + speed = CONV_MODE_10MBPS; + break; + default: + return -EOPNOTSUPP; + } + + val = FIELD_PREP(MIIC_CONVCTRL_CONV_MODE, conv_mode) | + FIELD_PREP(MIIC_CONVCTRL_CONV_SPEED, speed); + + miic_reg_rmw(miic, MIIC_CONVCTRL(port), + MIIC_CONVCTRL_CONV_MODE | MIIC_CONVCTRL_CONV_SPEED, val); + miic_converter_enable(miic_port->miic, miic_port->port, 1); + + return 0; +} + +static void miic_link_up(struct phylink_pcs *pcs, unsigned int mode, + phy_interface_t interface, int speed, int duplex) +{ + struct miic_port *miic_port = phylink_pcs_to_miic_port(pcs); + struct miic *miic = miic_port->miic; + u32 conv_speed = 0, val = 0; + int port = miic_port->port; + + if (duplex == DUPLEX_FULL) + val |= MIIC_CONVCTRL_FULLD; + + /* No speed in MII through-mode */ + if (interface != PHY_INTERFACE_MODE_MII) { + switch (speed) { + case SPEED_1000: + conv_speed = CONV_MODE_1000MBPS; + break; + case SPEED_100: + conv_speed = CONV_MODE_100MBPS; + break; + case SPEED_10: + conv_speed = CONV_MODE_10MBPS; + break; + default: + return; + } + } + + val |= FIELD_PREP(MIIC_CONVCTRL_CONV_SPEED, conv_speed); + + miic_reg_rmw(miic, MIIC_CONVCTRL(port), + (MIIC_CONVCTRL_CONV_SPEED | MIIC_CONVCTRL_FULLD), val); +} + +static int miic_validate(struct phylink_pcs *pcs, unsigned long *supported, + const struct phylink_link_state *state) +{ + if (phy_interface_mode_is_rgmii(state->interface) || + state->interface == PHY_INTERFACE_MODE_RMII || + state->interface == PHY_INTERFACE_MODE_MII) + return 1; + + return -EINVAL; +} + +static const struct phylink_pcs_ops miic_phylink_ops = { + .pcs_validate = miic_validate, + .pcs_config = miic_config, + .pcs_link_up = miic_link_up, +}; + +struct phylink_pcs *miic_create(struct device *dev, struct device_node *np) +{ + struct platform_device *pdev; + struct miic_port *miic_port; + struct device_node *pcs_np; + struct miic *miic; + u32 port; + + if (!of_device_is_available(np)) + return ERR_PTR(-ENODEV); + + if (of_property_read_u32(np, "reg", &port)) + return ERR_PTR(-EINVAL); + + if (port > MIIC_MAX_NR_PORTS || port < 1) + return ERR_PTR(-EINVAL); + + /* The PCS pdev is attached to the parent node */ + pcs_np = of_get_parent(np); + if (!pcs_np) + return ERR_PTR(-ENODEV); + + if (!of_device_is_available(pcs_np)) { + of_node_put(pcs_np); + return ERR_PTR(-ENODEV); + } + + pdev = of_find_device_by_node(pcs_np); + of_node_put(pcs_np); + if (!pdev || !platform_get_drvdata(pdev)) + return ERR_PTR(-EPROBE_DEFER); + + miic_port = kzalloc(sizeof(*miic_port), GFP_KERNEL); + if (!miic_port) + return ERR_PTR(-ENOMEM); + + miic = platform_get_drvdata(pdev); + device_link_add(dev, miic->dev, DL_FLAG_AUTOREMOVE_CONSUMER); + + miic_port->miic = miic; + miic_port->port = port - 1; + miic_port->pcs.ops = &miic_phylink_ops; + + return &miic_port->pcs; +} +EXPORT_SYMBOL(miic_create); + +void miic_destroy(struct phylink_pcs *pcs) +{ + struct miic_port *miic_port = phylink_pcs_to_miic_port(pcs); + + miic_converter_enable(miic_port->miic, miic_port->port, 0); + kfree(miic_port); +} +EXPORT_SYMBOL(miic_destroy); + +static int miic_init_hw(struct miic *miic, u32 cfg_mode) +{ + int port; + + /* Unlock write access to accessory registers (cf datasheet). If this + * is going to be used in conjunction with the Cortex-M3, this sequence + * will have to be moved in register write + */ + miic_reg_writel(miic, MIIC_PRCMD, 0x00A5); + miic_reg_writel(miic, MIIC_PRCMD, 0x0001); + miic_reg_writel(miic, MIIC_PRCMD, 0xFFFE); + miic_reg_writel(miic, MIIC_PRCMD, 0x0001); + + miic_reg_writel(miic, MIIC_MODCTRL, + FIELD_PREP(MIIC_MODCTRL_SW_MODE, cfg_mode)); + + for (port = 0; port < MIIC_MAX_NR_PORTS; port++) { + miic_converter_enable(miic, port, 0); + /* Disable speed/duplex control from these registers, datasheet + * says switch registers should be used to setup switch port + * speed and duplex. + */ + miic_reg_writel(miic, MIIC_SWCTRL, 0x0); + miic_reg_writel(miic, MIIC_SWDUPC, 0x0); + } + + return 0; +} + +static bool miic_modctrl_match(s8 table_val[MIIC_MODCTRL_CONF_CONV_NUM], + s8 dt_val[MIIC_MODCTRL_CONF_CONV_NUM]) +{ + int i; + + for (i = 0; i < MIIC_MODCTRL_CONF_CONV_NUM; i++) { + if (dt_val[i] == MIIC_MODCTRL_CONF_NONE) + continue; + + if (dt_val[i] != table_val[i]) + return false; + } + + return true; +} + +static void miic_dump_conf(struct device *dev, + s8 conf[MIIC_MODCTRL_CONF_CONV_NUM]) +{ + const char *conf_name; + int i; + + for (i = 0; i < MIIC_MODCTRL_CONF_CONV_NUM; i++) { + if (conf[i] != MIIC_MODCTRL_CONF_NONE) + conf_name = conf_to_string[conf[i]]; + else + conf_name = "NONE"; + + dev_err(dev, "%s: %s\n", index_to_string[i], conf_name); + } +} + +static int miic_match_dt_conf(struct device *dev, + s8 dt_val[MIIC_MODCTRL_CONF_CONV_NUM], + u32 *mode_cfg) +{ + struct modctrl_match *table_entry; + int i; + + for (i = 0; i < ARRAY_SIZE(modctrl_match_table); i++) { + table_entry = &modctrl_match_table[i]; + + if (miic_modctrl_match(table_entry->conv, dt_val)) { + *mode_cfg = table_entry->mode_cfg; + return 0; + } + } + + dev_err(dev, "Failed to apply requested configuration\n"); + miic_dump_conf(dev, dt_val); + + return -EINVAL; +} + +static int miic_parse_dt(struct device *dev, u32 *mode_cfg) +{ + s8 dt_val[MIIC_MODCTRL_CONF_CONV_NUM]; + struct device_node *np = dev->of_node; + struct device_node *conv; + u32 conf; + int port; + + memset(dt_val, MIIC_MODCTRL_CONF_NONE, sizeof(dt_val)); + + of_property_read_u32(np, "renesas,miic-switch-portin", &conf); + dt_val[0] = conf; + + for_each_child_of_node(np, conv) { + if (of_property_read_u32(conv, "reg", &port)) + continue; + + if (!of_device_is_available(conv)) + continue; + + if (of_property_read_u32(conv, "renesas,miic-input", &conf) == 0) + dt_val[port] = conf; + } + + return miic_match_dt_conf(dev, dt_val, mode_cfg); +} + +static int miic_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct miic *miic; + u32 mode_cfg; + int ret; + + ret = miic_parse_dt(dev, &mode_cfg); + if (ret < 0) + return -EINVAL; + + miic = devm_kzalloc(dev, sizeof(*miic), GFP_KERNEL); + if (!miic) + return -ENOMEM; + + spin_lock_init(&miic->lock); + miic->dev = dev; + miic->base = devm_platform_ioremap_resource(pdev, 0); + if (!miic->base) + return -EINVAL; + + ret = devm_pm_runtime_enable(dev); + if (ret < 0) + return ret; + + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) + return ret; + + ret = miic_init_hw(miic, mode_cfg); + if (ret) + goto disable_runtime_pm; + + /* miic_create() relies on that fact that data are attached to the + * platform device to determine if the driver is ready so this needs to + * be the last thing to be done after everything is initialized + * properly. + */ + platform_set_drvdata(pdev, miic); + + return 0; + +disable_runtime_pm: + pm_runtime_put(dev); + + return ret; +} + +static int miic_remove(struct platform_device *pdev) +{ + pm_runtime_put(&pdev->dev); + + return 0; +} + +static const struct of_device_id miic_of_mtable[] = { + { .compatible = "renesas,rzn1-miic" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, miic_of_mtable); + +static struct platform_driver miic_driver = { + .driver = { + .name = "rzn1_miic", + .suppress_bind_attrs = true, + .of_match_table = of_match_ptr(miic_of_mtable), + }, + .probe = miic_probe, + .remove = miic_remove, +}; +module_platform_driver(miic_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Renesas MII converter PCS driver"); +MODULE_AUTHOR("Clément Léger "); diff --git a/include/linux/pcs-rzn1-miic.h b/include/linux/pcs-rzn1-miic.h new file mode 100644 index 000000000000..56d12b21365d --- /dev/null +++ b/include/linux/pcs-rzn1-miic.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2022 Schneider Electric + * + * Clément Léger + */ + +#ifndef __LINUX_PCS_MIIC_H +#define __LINUX_PCS_MIIC_H + +struct phylink; +struct device_node; + +struct phylink_pcs *miic_create(struct device *dev, struct device_node *np); + +void miic_destroy(struct phylink_pcs *pcs); + +#endif /* __LINUX_PCS_MIIC_H */ From patchwork Mon Jun 20 11:08:37 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?b?Q2zDqW1lbnQgTMOpZ2Vy?= X-Patchwork-Id: 583447 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 14274C43334 for ; Mon, 20 Jun 2022 11:10:46 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S241503AbiFTLKn (ORCPT ); Mon, 20 Jun 2022 07:10:43 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:59776 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S241448AbiFTLKR (ORCPT ); Mon, 20 Jun 2022 07:10:17 -0400 Received: from relay10.mail.gandi.net (relay10.mail.gandi.net [IPv6:2001:4b98:dc4:8::230]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 27FBF15737; Mon, 20 Jun 2022 04:10:09 -0700 (PDT) Received: (Authenticated sender: clement.leger@bootlin.com) by mail.gandi.net (Postfix) with ESMTPSA id 22894240002; Mon, 20 Jun 2022 11:10:04 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1655723407; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=NufiAOdgV0QNPgmwyf+rgME6+MIewSbra40c9n//F6c=; b=iJgxweuhIF5gV3Ffdg6khpU3VrvMVXVQCqGHYuoWR8Pxmlj1SJw4Dor03h0yPfALRhwycW 0oLNWGNQEVcuyfIxG+iFogiQkuAWLHCX0Ei2lcC2M+EnrsX9ijFyJwlpQ+s3qHLCJ7SWJ3 8bXvgmgqfFu+XTuLYA+qdDYOU4ttiMxTr48MVNpOT0X2QNeEREowq7//E1L4OWcLTG97iw WE4ItURCpAlsLVvsCyuyNay/uDEPTKJTxaNExD/Jad9LLryWGQ67r8WV3fKjekJSlZB1ni 1LMAyHUw75wiR9bzOtxF8OYJXs2fiCOHKKcezAGU48+UXPvBubwtrCJa18d74w== From: =?utf-8?b?Q2zDqW1lbnQgTMOpZ2Vy?= To: Andrew Lunn , Vivien Didelot , Florian Fainelli , Vladimir Oltean , "David S . Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Rob Herring , Krzysztof Kozlowski , Geert Uytterhoeven , Magnus Damm , Heiner Kallweit , Russell King , Alexandre Torgue , Giuseppe Cavallaro , Jose Abreu Cc: =?utf-8?b?Q2zDqW1lbnQgTMOpZ2Vy?= , Thomas Petazzoni , Herve Codina , =?utf-8?q?Miqu=C3=A8l_Raynal?= , Milan Stevanovic , Jimmy Lalande , Pascal Eberhard , linux-kernel@vger.kernel.org, devicetree@vger.kernel.org, linux-renesas-soc@vger.kernel.org, netdev@vger.kernel.org, Jean-Pierre Geslin , Phil Edworthy Subject: [PATCH net-next v8 07/16] net: dsa: rzn1-a5psw: add Renesas RZ/N1 advanced 5 port switch driver Date: Mon, 20 Jun 2022 13:08:37 +0200 Message-Id: <20220620110846.374787-8-clement.leger@bootlin.com> X-Mailer: git-send-email 2.36.1 In-Reply-To: <20220620110846.374787-1-clement.leger@bootlin.com> References: <20220620110846.374787-1-clement.leger@bootlin.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: devicetree@vger.kernel.org Add Renesas RZ/N1 advanced 5 port switch driver. This switch handles 5 ports including 1 CPU management port. A MDIO bus is also exposed by this switch and allows to communicate with PHYs connected to the ports. Each switch port (except for the CPU management ports) is connected to the MII converter. This driver includes basic bridging support, more support will be added later (vlan, etc). Suggested-by: Jean-Pierre Geslin Suggested-by: Phil Edworthy Signed-off-by: Clément Léger --- drivers/net/dsa/Kconfig | 9 + drivers/net/dsa/Makefile | 1 + drivers/net/dsa/rzn1_a5psw.c | 720 +++++++++++++++++++++++++++++++++++ drivers/net/dsa/rzn1_a5psw.h | 196 ++++++++++ 4 files changed, 926 insertions(+) create mode 100644 drivers/net/dsa/rzn1_a5psw.c create mode 100644 drivers/net/dsa/rzn1_a5psw.h diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig index 6d1fcb08bba1..c90227cb4bb9 100644 --- a/drivers/net/dsa/Kconfig +++ b/drivers/net/dsa/Kconfig @@ -70,6 +70,15 @@ config NET_DSA_QCA8K source "drivers/net/dsa/realtek/Kconfig" +config NET_DSA_RZN1_A5PSW + tristate "Renesas RZ/N1 A5PSW Ethernet switch support" + depends on ARCH_RZN1 + select NET_DSA_TAG_RZN1_A5PSW + select PCS_RZN1_MIIC + help + This driver supports the A5PSW switch, which is embedded in Renesas + RZ/N1 SoC. + config NET_DSA_SMSC_LAN9303 tristate select NET_DSA_TAG_LAN9303 diff --git a/drivers/net/dsa/Makefile b/drivers/net/dsa/Makefile index e73838c12256..b32907afa702 100644 --- a/drivers/net/dsa/Makefile +++ b/drivers/net/dsa/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_NET_DSA_LANTIQ_GSWIP) += lantiq_gswip.o obj-$(CONFIG_NET_DSA_MT7530) += mt7530.o obj-$(CONFIG_NET_DSA_MV88E6060) += mv88e6060.o obj-$(CONFIG_NET_DSA_QCA8K) += qca8k.o +obj-$(CONFIG_NET_DSA_RZN1_A5PSW) += rzn1_a5psw.o obj-$(CONFIG_NET_DSA_SMSC_LAN9303) += lan9303-core.o obj-$(CONFIG_NET_DSA_SMSC_LAN9303_I2C) += lan9303_i2c.o obj-$(CONFIG_NET_DSA_SMSC_LAN9303_MDIO) += lan9303_mdio.o diff --git a/drivers/net/dsa/rzn1_a5psw.c b/drivers/net/dsa/rzn1_a5psw.c new file mode 100644 index 000000000000..ad9a8073a4d3 --- /dev/null +++ b/drivers/net/dsa/rzn1_a5psw.c @@ -0,0 +1,720 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2022 Schneider-Electric + * + * Clément Léger + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rzn1_a5psw.h" + +static void a5psw_reg_writel(struct a5psw *a5psw, int offset, u32 value) +{ + writel(value, a5psw->base + offset); +} + +static u32 a5psw_reg_readl(struct a5psw *a5psw, int offset) +{ + return readl(a5psw->base + offset); +} + +static void a5psw_reg_rmw(struct a5psw *a5psw, int offset, u32 mask, u32 val) +{ + u32 reg; + + spin_lock(&a5psw->reg_lock); + + reg = a5psw_reg_readl(a5psw, offset); + reg &= ~mask; + reg |= val; + a5psw_reg_writel(a5psw, offset, reg); + + spin_unlock(&a5psw->reg_lock); +} + +static enum dsa_tag_protocol a5psw_get_tag_protocol(struct dsa_switch *ds, + int port, + enum dsa_tag_protocol mp) +{ + return DSA_TAG_PROTO_RZN1_A5PSW; +} + +static void a5psw_port_pattern_set(struct a5psw *a5psw, int port, int pattern, + bool enable) +{ + u32 rx_match = 0; + + if (enable) + rx_match |= A5PSW_RXMATCH_CONFIG_PATTERN(pattern); + + a5psw_reg_rmw(a5psw, A5PSW_RXMATCH_CONFIG(port), + A5PSW_RXMATCH_CONFIG_PATTERN(pattern), rx_match); +} + +static void a5psw_port_mgmtfwd_set(struct a5psw *a5psw, int port, bool enable) +{ + /* Enable "management forward" pattern matching, this will forward + * packets from this port only towards the management port and thus + * isolate the port. + */ + a5psw_port_pattern_set(a5psw, port, A5PSW_PATTERN_MGMTFWD, enable); +} + +static void a5psw_port_enable_set(struct a5psw *a5psw, int port, bool enable) +{ + u32 port_ena = 0; + + if (enable) + port_ena |= A5PSW_PORT_ENA_TX_RX(port); + + a5psw_reg_rmw(a5psw, A5PSW_PORT_ENA, A5PSW_PORT_ENA_TX_RX(port), + port_ena); +} + +static int a5psw_lk_execute_ctrl(struct a5psw *a5psw, u32 *ctrl) +{ + int ret; + + a5psw_reg_writel(a5psw, A5PSW_LK_ADDR_CTRL, *ctrl); + + ret = readl_poll_timeout(a5psw->base + A5PSW_LK_ADDR_CTRL, *ctrl, + !(*ctrl & A5PSW_LK_ADDR_CTRL_BUSY), + A5PSW_LK_BUSY_USEC_POLL, A5PSW_CTRL_TIMEOUT); + if (ret) + dev_err(a5psw->dev, "LK_CTRL timeout waiting for BUSY bit\n"); + + return ret; +} + +static void a5psw_port_fdb_flush(struct a5psw *a5psw, int port) +{ + u32 ctrl = A5PSW_LK_ADDR_CTRL_DELETE_PORT | BIT(port); + + mutex_lock(&a5psw->lk_lock); + a5psw_lk_execute_ctrl(a5psw, &ctrl); + mutex_unlock(&a5psw->lk_lock); +} + +static void a5psw_port_authorize_set(struct a5psw *a5psw, int port, + bool authorize) +{ + u32 reg = a5psw_reg_readl(a5psw, A5PSW_AUTH_PORT(port)); + + if (authorize) + reg |= A5PSW_AUTH_PORT_AUTHORIZED; + else + reg &= ~A5PSW_AUTH_PORT_AUTHORIZED; + + a5psw_reg_writel(a5psw, A5PSW_AUTH_PORT(port), reg); +} + +static void a5psw_port_disable(struct dsa_switch *ds, int port) +{ + struct a5psw *a5psw = ds->priv; + + a5psw_port_authorize_set(a5psw, port, false); + a5psw_port_enable_set(a5psw, port, false); +} + +static int a5psw_port_enable(struct dsa_switch *ds, int port, + struct phy_device *phy) +{ + struct a5psw *a5psw = ds->priv; + + a5psw_port_authorize_set(a5psw, port, true); + a5psw_port_enable_set(a5psw, port, true); + + return 0; +} + +static int a5psw_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu) +{ + struct a5psw *a5psw = ds->priv; + + new_mtu += ETH_HLEN + A5PSW_EXTRA_MTU_LEN + ETH_FCS_LEN; + a5psw_reg_writel(a5psw, A5PSW_FRM_LENGTH(port), new_mtu); + + return 0; +} + +static int a5psw_port_max_mtu(struct dsa_switch *ds, int port) +{ + return A5PSW_MAX_MTU; +} + +static void a5psw_phylink_get_caps(struct dsa_switch *ds, int port, + struct phylink_config *config) +{ + unsigned long *intf = config->supported_interfaces; + + config->mac_capabilities = MAC_1000FD; + + if (dsa_is_cpu_port(ds, port)) { + /* GMII is used internally and GMAC2 is connected to the switch + * using 1000Mbps Full-Duplex mode only (cf ethernet manual) + */ + __set_bit(PHY_INTERFACE_MODE_GMII, intf); + } else { + config->mac_capabilities |= MAC_100 | MAC_10; + phy_interface_set_rgmii(intf); + __set_bit(PHY_INTERFACE_MODE_RMII, intf); + __set_bit(PHY_INTERFACE_MODE_MII, intf); + } +} + +static struct phylink_pcs * +a5psw_phylink_mac_select_pcs(struct dsa_switch *ds, int port, + phy_interface_t interface) +{ + struct dsa_port *dp = dsa_to_port(ds, port); + struct a5psw *a5psw = ds->priv; + + if (!dsa_port_is_cpu(dp) && a5psw->pcs[port]) + return a5psw->pcs[port]; + + return NULL; +} + +static void a5psw_phylink_mac_link_down(struct dsa_switch *ds, int port, + unsigned int mode, + phy_interface_t interface) +{ + struct a5psw *a5psw = ds->priv; + u32 cmd_cfg; + + cmd_cfg = a5psw_reg_readl(a5psw, A5PSW_CMD_CFG(port)); + cmd_cfg &= ~(A5PSW_CMD_CFG_RX_ENA | A5PSW_CMD_CFG_TX_ENA); + a5psw_reg_writel(a5psw, A5PSW_CMD_CFG(port), cmd_cfg); +} + +static void a5psw_phylink_mac_link_up(struct dsa_switch *ds, int port, + unsigned int mode, + phy_interface_t interface, + struct phy_device *phydev, int speed, + int duplex, bool tx_pause, bool rx_pause) +{ + u32 cmd_cfg = A5PSW_CMD_CFG_RX_ENA | A5PSW_CMD_CFG_TX_ENA | + A5PSW_CMD_CFG_TX_CRC_APPEND; + struct a5psw *a5psw = ds->priv; + + if (speed == SPEED_1000) + cmd_cfg |= A5PSW_CMD_CFG_ETH_SPEED; + + if (duplex == DUPLEX_HALF) + cmd_cfg |= A5PSW_CMD_CFG_HD_ENA; + + cmd_cfg |= A5PSW_CMD_CFG_CNTL_FRM_ENA; + + if (!rx_pause) + cmd_cfg &= ~A5PSW_CMD_CFG_PAUSE_IGNORE; + + a5psw_reg_writel(a5psw, A5PSW_CMD_CFG(port), cmd_cfg); +} + +static int a5psw_set_ageing_time(struct dsa_switch *ds, unsigned int msecs) +{ + struct a5psw *a5psw = ds->priv; + unsigned long rate; + u64 max, tmp; + u32 agetime; + + rate = clk_get_rate(a5psw->clk); + max = div64_ul(((u64)A5PSW_LK_AGETIME_MASK * A5PSW_TABLE_ENTRIES * 1024), + rate) * 1000; + if (msecs > max) + return -EINVAL; + + tmp = div_u64(rate, MSEC_PER_SEC); + agetime = div_u64(msecs * tmp, 1024 * A5PSW_TABLE_ENTRIES); + + a5psw_reg_writel(a5psw, A5PSW_LK_AGETIME, agetime); + + return 0; +} + +static void a5psw_flooding_set_resolution(struct a5psw *a5psw, int port, + bool set) +{ + u8 offsets[] = {A5PSW_UCAST_DEF_MASK, A5PSW_BCAST_DEF_MASK, + A5PSW_MCAST_DEF_MASK}; + int i; + + if (set) + a5psw->bridged_ports |= BIT(port); + else + a5psw->bridged_ports &= ~BIT(port); + + for (i = 0; i < ARRAY_SIZE(offsets); i++) + a5psw_reg_writel(a5psw, offsets[i], a5psw->bridged_ports); +} + +static int a5psw_port_bridge_join(struct dsa_switch *ds, int port, + struct dsa_bridge bridge, + bool *tx_fwd_offload, + struct netlink_ext_ack *extack) +{ + struct a5psw *a5psw = ds->priv; + + /* We only support 1 bridge device */ + if (a5psw->br_dev && bridge.dev != a5psw->br_dev) { + NL_SET_ERR_MSG_MOD(extack, + "Forwarding offload supported for a single bridge"); + return -EOPNOTSUPP; + } + + a5psw->br_dev = bridge.dev; + a5psw_flooding_set_resolution(a5psw, port, true); + a5psw_port_mgmtfwd_set(a5psw, port, false); + + return 0; +} + +static void a5psw_port_bridge_leave(struct dsa_switch *ds, int port, + struct dsa_bridge bridge) +{ + struct a5psw *a5psw = ds->priv; + + a5psw_flooding_set_resolution(a5psw, port, false); + a5psw_port_mgmtfwd_set(a5psw, port, true); + + /* No more ports bridged */ + if (a5psw->bridged_ports == BIT(A5PSW_CPU_PORT)) + a5psw->br_dev = NULL; +} + +static void a5psw_port_stp_state_set(struct dsa_switch *ds, int port, u8 state) +{ + u32 mask = A5PSW_INPUT_LEARN_DIS(port) | A5PSW_INPUT_LEARN_BLOCK(port); + struct a5psw *a5psw = ds->priv; + u32 reg = 0; + + switch (state) { + case BR_STATE_DISABLED: + case BR_STATE_BLOCKING: + reg |= A5PSW_INPUT_LEARN_DIS(port); + reg |= A5PSW_INPUT_LEARN_BLOCK(port); + break; + case BR_STATE_LISTENING: + reg |= A5PSW_INPUT_LEARN_DIS(port); + break; + case BR_STATE_LEARNING: + reg |= A5PSW_INPUT_LEARN_BLOCK(port); + break; + case BR_STATE_FORWARDING: + default: + break; + } + + a5psw_reg_rmw(a5psw, A5PSW_INPUT_LEARN, mask, reg); +} + +static void a5psw_port_fast_age(struct dsa_switch *ds, int port) +{ + struct a5psw *a5psw = ds->priv; + + a5psw_port_fdb_flush(a5psw, port); +} + +static int a5psw_setup(struct dsa_switch *ds) +{ + struct a5psw *a5psw = ds->priv; + int port, vlan, ret; + struct dsa_port *dp; + u32 reg; + + /* Validate that there is only 1 CPU port with index A5PSW_CPU_PORT */ + dsa_switch_for_each_cpu_port(dp, ds) { + if (dp->index != A5PSW_CPU_PORT) { + dev_err(a5psw->dev, "Invalid CPU port\n"); + return -EINVAL; + } + } + + /* Configure management port */ + reg = A5PSW_CPU_PORT | A5PSW_MGMT_CFG_DISCARD; + a5psw_reg_writel(a5psw, A5PSW_MGMT_CFG, reg); + + /* Set pattern 0 to forward all frame to mgmt port */ + a5psw_reg_writel(a5psw, A5PSW_PATTERN_CTRL(A5PSW_PATTERN_MGMTFWD), + A5PSW_PATTERN_CTRL_MGMTFWD); + + /* Enable port tagging */ + reg = FIELD_PREP(A5PSW_MGMT_TAG_CFG_TAGFIELD, ETH_P_DSA_A5PSW); + reg |= A5PSW_MGMT_TAG_CFG_ENABLE | A5PSW_MGMT_TAG_CFG_ALL_FRAMES; + a5psw_reg_writel(a5psw, A5PSW_MGMT_TAG_CFG, reg); + + /* Enable normal switch operation */ + reg = A5PSW_LK_ADDR_CTRL_BLOCKING | A5PSW_LK_ADDR_CTRL_LEARNING | + A5PSW_LK_ADDR_CTRL_AGEING | A5PSW_LK_ADDR_CTRL_ALLOW_MIGR | + A5PSW_LK_ADDR_CTRL_CLEAR_TABLE; + a5psw_reg_writel(a5psw, A5PSW_LK_CTRL, reg); + + ret = readl_poll_timeout(a5psw->base + A5PSW_LK_CTRL, reg, + !(reg & A5PSW_LK_ADDR_CTRL_CLEAR_TABLE), + A5PSW_LK_BUSY_USEC_POLL, A5PSW_CTRL_TIMEOUT); + if (ret) { + dev_err(a5psw->dev, "Failed to clear lookup table\n"); + return ret; + } + + /* Reset learn count to 0 */ + reg = A5PSW_LK_LEARNCOUNT_MODE_SET; + a5psw_reg_writel(a5psw, A5PSW_LK_LEARNCOUNT, reg); + + /* Clear VLAN resource table */ + reg = A5PSW_VLAN_RES_WR_PORTMASK | A5PSW_VLAN_RES_WR_TAGMASK; + for (vlan = 0; vlan < A5PSW_VLAN_COUNT; vlan++) + a5psw_reg_writel(a5psw, A5PSW_VLAN_RES(vlan), reg); + + /* Reset all ports */ + dsa_switch_for_each_port(dp, ds) { + port = dp->index; + + /* Reset the port */ + a5psw_reg_writel(a5psw, A5PSW_CMD_CFG(port), + A5PSW_CMD_CFG_SW_RESET); + + /* Enable only CPU port */ + a5psw_port_enable_set(a5psw, port, dsa_port_is_cpu(dp)); + + if (dsa_port_is_unused(dp)) + continue; + + /* Enable egress flooding for CPU port */ + if (dsa_port_is_cpu(dp)) + a5psw_flooding_set_resolution(a5psw, port, true); + + /* Enable management forward only for user ports */ + if (dsa_port_is_user(dp)) + a5psw_port_mgmtfwd_set(a5psw, port, true); + } + + return 0; +} + +static const struct dsa_switch_ops a5psw_switch_ops = { + .get_tag_protocol = a5psw_get_tag_protocol, + .setup = a5psw_setup, + .port_disable = a5psw_port_disable, + .port_enable = a5psw_port_enable, + .phylink_get_caps = a5psw_phylink_get_caps, + .phylink_mac_select_pcs = a5psw_phylink_mac_select_pcs, + .phylink_mac_link_down = a5psw_phylink_mac_link_down, + .phylink_mac_link_up = a5psw_phylink_mac_link_up, + .port_change_mtu = a5psw_port_change_mtu, + .port_max_mtu = a5psw_port_max_mtu, + .set_ageing_time = a5psw_set_ageing_time, + .port_bridge_join = a5psw_port_bridge_join, + .port_bridge_leave = a5psw_port_bridge_leave, + .port_stp_state_set = a5psw_port_stp_state_set, + .port_fast_age = a5psw_port_fast_age, +}; + +static int a5psw_mdio_wait_busy(struct a5psw *a5psw) +{ + u32 status; + int err; + + err = readl_poll_timeout(a5psw->base + A5PSW_MDIO_CFG_STATUS, status, + !(status & A5PSW_MDIO_CFG_STATUS_BUSY), 10, + 1000 * USEC_PER_MSEC); + if (err) + dev_err(a5psw->dev, "MDIO command timeout\n"); + + return err; +} + +static int a5psw_mdio_read(struct mii_bus *bus, int phy_id, int phy_reg) +{ + struct a5psw *a5psw = bus->priv; + u32 cmd, status; + int ret; + + if (phy_reg & MII_ADDR_C45) + return -EOPNOTSUPP; + + cmd = A5PSW_MDIO_COMMAND_READ; + cmd |= FIELD_PREP(A5PSW_MDIO_COMMAND_REG_ADDR, phy_reg); + cmd |= FIELD_PREP(A5PSW_MDIO_COMMAND_PHY_ADDR, phy_id); + + a5psw_reg_writel(a5psw, A5PSW_MDIO_COMMAND, cmd); + + ret = a5psw_mdio_wait_busy(a5psw); + if (ret) + return ret; + + ret = a5psw_reg_readl(a5psw, A5PSW_MDIO_DATA) & A5PSW_MDIO_DATA_MASK; + + status = a5psw_reg_readl(a5psw, A5PSW_MDIO_CFG_STATUS); + if (status & A5PSW_MDIO_CFG_STATUS_READERR) + return -EIO; + + return ret; +} + +static int a5psw_mdio_write(struct mii_bus *bus, int phy_id, int phy_reg, + u16 phy_data) +{ + struct a5psw *a5psw = bus->priv; + u32 cmd; + + if (phy_reg & MII_ADDR_C45) + return -EOPNOTSUPP; + + cmd = FIELD_PREP(A5PSW_MDIO_COMMAND_REG_ADDR, phy_reg); + cmd |= FIELD_PREP(A5PSW_MDIO_COMMAND_PHY_ADDR, phy_id); + + a5psw_reg_writel(a5psw, A5PSW_MDIO_COMMAND, cmd); + a5psw_reg_writel(a5psw, A5PSW_MDIO_DATA, phy_data); + + return a5psw_mdio_wait_busy(a5psw); +} + +static int a5psw_mdio_config(struct a5psw *a5psw, u32 mdio_freq) +{ + unsigned long rate; + unsigned long div; + u32 cfgstatus; + + rate = clk_get_rate(a5psw->hclk); + div = ((rate / mdio_freq) / 2); + if (div > FIELD_MAX(A5PSW_MDIO_CFG_STATUS_CLKDIV) || + div < A5PSW_MDIO_CLK_DIV_MIN) { + dev_err(a5psw->dev, "MDIO clock div %ld out of range\n", div); + return -ERANGE; + } + + cfgstatus = FIELD_PREP(A5PSW_MDIO_CFG_STATUS_CLKDIV, div); + + a5psw_reg_writel(a5psw, A5PSW_MDIO_CFG_STATUS, cfgstatus); + + return 0; +} + +static int a5psw_probe_mdio(struct a5psw *a5psw, struct device_node *node) +{ + struct device *dev = a5psw->dev; + struct mii_bus *bus; + u32 mdio_freq; + int ret; + + if (of_property_read_u32(node, "clock-frequency", &mdio_freq)) + mdio_freq = A5PSW_MDIO_DEF_FREQ; + + ret = a5psw_mdio_config(a5psw, mdio_freq); + if (ret) + return ret; + + bus = devm_mdiobus_alloc(dev); + if (!bus) + return -ENOMEM; + + bus->name = "a5psw_mdio"; + bus->read = a5psw_mdio_read; + bus->write = a5psw_mdio_write; + bus->priv = a5psw; + bus->parent = dev; + snprintf(bus->id, MII_BUS_ID_SIZE, "%s", dev_name(dev)); + + a5psw->mii_bus = bus; + + return devm_of_mdiobus_register(dev, bus, node); +} + +static void a5psw_pcs_free(struct a5psw *a5psw) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(a5psw->pcs); i++) { + if (a5psw->pcs[i]) + miic_destroy(a5psw->pcs[i]); + } +} + +static int a5psw_pcs_get(struct a5psw *a5psw) +{ + struct device_node *ports, *port, *pcs_node; + struct phylink_pcs *pcs; + int ret; + u32 reg; + + ports = of_get_child_by_name(a5psw->dev->of_node, "ethernet-ports"); + if (!ports) + return -EINVAL; + + for_each_available_child_of_node(ports, port) { + pcs_node = of_parse_phandle(port, "pcs-handle", 0); + if (!pcs_node) + continue; + + if (of_property_read_u32(port, "reg", ®)) { + ret = -EINVAL; + goto free_pcs; + } + + if (reg >= ARRAY_SIZE(a5psw->pcs)) { + ret = -ENODEV; + goto free_pcs; + } + + pcs = miic_create(a5psw->dev, pcs_node); + if (IS_ERR(pcs)) { + dev_err(a5psw->dev, "Failed to create PCS for port %d\n", + reg); + ret = PTR_ERR(pcs); + goto free_pcs; + } + + a5psw->pcs[reg] = pcs; + } + of_node_put(ports); + + return 0; + +free_pcs: + of_node_put(port); + of_node_put(ports); + a5psw_pcs_free(a5psw); + + return ret; +} + +static int a5psw_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *mdio; + struct dsa_switch *ds; + struct a5psw *a5psw; + int ret; + + a5psw = devm_kzalloc(dev, sizeof(*a5psw), GFP_KERNEL); + if (!a5psw) + return -ENOMEM; + + a5psw->dev = dev; + mutex_init(&a5psw->lk_lock); + spin_lock_init(&a5psw->reg_lock); + a5psw->base = devm_platform_ioremap_resource(pdev, 0); + if (!a5psw->base) + return -EINVAL; + + ret = a5psw_pcs_get(a5psw); + if (ret) + return ret; + + a5psw->hclk = devm_clk_get(dev, "hclk"); + if (IS_ERR(a5psw->hclk)) { + dev_err(dev, "failed get hclk clock\n"); + ret = PTR_ERR(a5psw->hclk); + goto free_pcs; + } + + a5psw->clk = devm_clk_get(dev, "clk"); + if (IS_ERR(a5psw->clk)) { + dev_err(dev, "failed get clk_switch clock\n"); + ret = PTR_ERR(a5psw->clk); + goto free_pcs; + } + + ret = clk_prepare_enable(a5psw->clk); + if (ret) + goto free_pcs; + + ret = clk_prepare_enable(a5psw->hclk); + if (ret) + goto clk_disable; + + mdio = of_get_child_by_name(dev->of_node, "mdio"); + if (of_device_is_available(mdio)) { + ret = a5psw_probe_mdio(a5psw, mdio); + if (ret) { + of_node_put(mdio); + dev_err(dev, "Failed to register MDIO: %d\n", ret); + goto hclk_disable; + } + } + + of_node_put(mdio); + + ds = &a5psw->ds; + ds->dev = dev; + ds->num_ports = A5PSW_PORTS_NUM; + ds->ops = &a5psw_switch_ops; + ds->priv = a5psw; + + ret = dsa_register_switch(ds); + if (ret) { + dev_err(dev, "Failed to register DSA switch: %d\n", ret); + goto hclk_disable; + } + + return 0; + +hclk_disable: + clk_disable_unprepare(a5psw->hclk); +clk_disable: + clk_disable_unprepare(a5psw->clk); +free_pcs: + a5psw_pcs_free(a5psw); + + return ret; +} + +static int a5psw_remove(struct platform_device *pdev) +{ + struct a5psw *a5psw = platform_get_drvdata(pdev); + + if (!a5psw) + return 0; + + dsa_unregister_switch(&a5psw->ds); + a5psw_pcs_free(a5psw); + clk_disable_unprepare(a5psw->hclk); + clk_disable_unprepare(a5psw->clk); + + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static void a5psw_shutdown(struct platform_device *pdev) +{ + struct a5psw *a5psw = platform_get_drvdata(pdev); + + if (!a5psw) + return; + + dsa_switch_shutdown(&a5psw->ds); + + platform_set_drvdata(pdev, NULL); +} + +static const struct of_device_id a5psw_of_mtable[] = { + { .compatible = "renesas,rzn1-a5psw", }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, a5psw_of_mtable); + +static struct platform_driver a5psw_driver = { + .driver = { + .name = "rzn1_a5psw", + .of_match_table = of_match_ptr(a5psw_of_mtable), + }, + .probe = a5psw_probe, + .remove = a5psw_remove, + .shutdown = a5psw_shutdown, +}; +module_platform_driver(a5psw_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Renesas RZ/N1 Advanced 5-port Switch driver"); +MODULE_AUTHOR("Clément Léger "); diff --git a/drivers/net/dsa/rzn1_a5psw.h b/drivers/net/dsa/rzn1_a5psw.h new file mode 100644 index 000000000000..13397ac02e7c --- /dev/null +++ b/drivers/net/dsa/rzn1_a5psw.h @@ -0,0 +1,196 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2022 Schneider Electric + * + * Clément Léger + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define A5PSW_REVISION 0x0 +#define A5PSW_PORT_OFFSET(port) (0x400 * (port)) + +#define A5PSW_PORT_ENA 0x8 +#define A5PSW_PORT_ENA_RX_SHIFT 16 +#define A5PSW_PORT_ENA_TX_RX(port) (BIT((port) + A5PSW_PORT_ENA_RX_SHIFT) | \ + BIT(port)) +#define A5PSW_UCAST_DEF_MASK 0xC + +#define A5PSW_VLAN_VERIFY 0x10 +#define A5PSW_VLAN_VERI_SHIFT 0 +#define A5PSW_VLAN_DISC_SHIFT 16 + +#define A5PSW_BCAST_DEF_MASK 0x14 +#define A5PSW_MCAST_DEF_MASK 0x18 + +#define A5PSW_INPUT_LEARN 0x1C +#define A5PSW_INPUT_LEARN_DIS(p) BIT((p) + 16) +#define A5PSW_INPUT_LEARN_BLOCK(p) BIT(p) + +#define A5PSW_MGMT_CFG 0x20 +#define A5PSW_MGMT_CFG_DISCARD BIT(7) + +#define A5PSW_MODE_CFG 0x24 +#define A5PSW_MODE_STATS_RESET BIT(31) + +#define A5PSW_VLAN_IN_MODE 0x28 +#define A5PSW_VLAN_IN_MODE_PORT_SHIFT(port) ((port) * 2) +#define A5PSW_VLAN_IN_MODE_PORT(port) (GENMASK(1, 0) << \ + A5PSW_VLAN_IN_MODE_PORT_SHIFT(port)) +#define A5PSW_VLAN_IN_MODE_SINGLE_PASSTHROUGH 0x0 +#define A5PSW_VLAN_IN_MODE_SINGLE_REPLACE 0x1 +#define A5PSW_VLAN_IN_MODE_TAG_ALWAYS 0x2 + +#define A5PSW_VLAN_OUT_MODE 0x2C +#define A5PSW_VLAN_OUT_MODE_PORT(port) (GENMASK(1, 0) << ((port) * 2)) +#define A5PSW_VLAN_OUT_MODE_DIS 0x0 +#define A5PSW_VLAN_OUT_MODE_STRIP 0x1 +#define A5PSW_VLAN_OUT_MODE_TAG_THROUGH 0x2 +#define A5PSW_VLAN_OUT_MODE_TRANSPARENT 0x3 + +#define A5PSW_VLAN_IN_MODE_ENA 0x30 +#define A5PSW_VLAN_TAG_ID 0x34 + +#define A5PSW_SYSTEM_TAGINFO(port) (0x200 + A5PSW_PORT_OFFSET(port)) + +#define A5PSW_AUTH_PORT(port) (0x240 + 4 * (port)) +#define A5PSW_AUTH_PORT_AUTHORIZED BIT(0) + +#define A5PSW_VLAN_RES(entry) (0x280 + 4 * (entry)) +#define A5PSW_VLAN_RES_WR_PORTMASK BIT(30) +#define A5PSW_VLAN_RES_WR_TAGMASK BIT(29) +#define A5PSW_VLAN_RES_RD_TAGMASK BIT(28) +#define A5PSW_VLAN_RES_ID GENMASK(16, 5) +#define A5PSW_VLAN_RES_PORTMASK GENMASK(4, 0) + +#define A5PSW_RXMATCH_CONFIG(port) (0x3e80 + 4 * (port)) +#define A5PSW_RXMATCH_CONFIG_PATTERN(p) BIT(p) + +#define A5PSW_PATTERN_CTRL(p) (0x3eb0 + 4 * (p)) +#define A5PSW_PATTERN_CTRL_MGMTFWD BIT(1) + +#define A5PSW_LK_CTRL 0x400 +#define A5PSW_LK_ADDR_CTRL_BLOCKING BIT(0) +#define A5PSW_LK_ADDR_CTRL_LEARNING BIT(1) +#define A5PSW_LK_ADDR_CTRL_AGEING BIT(2) +#define A5PSW_LK_ADDR_CTRL_ALLOW_MIGR BIT(3) +#define A5PSW_LK_ADDR_CTRL_CLEAR_TABLE BIT(6) + +#define A5PSW_LK_ADDR_CTRL 0x408 +#define A5PSW_LK_ADDR_CTRL_BUSY BIT(31) +#define A5PSW_LK_ADDR_CTRL_DELETE_PORT BIT(30) +#define A5PSW_LK_ADDR_CTRL_CLEAR BIT(29) +#define A5PSW_LK_ADDR_CTRL_LOOKUP BIT(28) +#define A5PSW_LK_ADDR_CTRL_WAIT BIT(27) +#define A5PSW_LK_ADDR_CTRL_READ BIT(26) +#define A5PSW_LK_ADDR_CTRL_WRITE BIT(25) +#define A5PSW_LK_ADDR_CTRL_ADDRESS GENMASK(12, 0) + +#define A5PSW_LK_DATA_LO 0x40C +#define A5PSW_LK_DATA_HI 0x410 +#define A5PSW_LK_DATA_HI_VALID BIT(16) +#define A5PSW_LK_DATA_HI_PORT BIT(16) + +#define A5PSW_LK_LEARNCOUNT 0x418 +#define A5PSW_LK_LEARNCOUNT_COUNT GENMASK(13, 0) +#define A5PSW_LK_LEARNCOUNT_MODE GENMASK(31, 30) +#define A5PSW_LK_LEARNCOUNT_MODE_SET 0x0 +#define A5PSW_LK_LEARNCOUNT_MODE_INC 0x1 +#define A5PSW_LK_LEARNCOUNT_MODE_DEC 0x2 + +#define A5PSW_MGMT_TAG_CFG 0x480 +#define A5PSW_MGMT_TAG_CFG_TAGFIELD GENMASK(31, 16) +#define A5PSW_MGMT_TAG_CFG_ALL_FRAMES BIT(1) +#define A5PSW_MGMT_TAG_CFG_ENABLE BIT(0) + +#define A5PSW_LK_AGETIME 0x41C +#define A5PSW_LK_AGETIME_MASK GENMASK(23, 0) + +#define A5PSW_MDIO_CFG_STATUS 0x700 +#define A5PSW_MDIO_CFG_STATUS_CLKDIV GENMASK(15, 7) +#define A5PSW_MDIO_CFG_STATUS_READERR BIT(1) +#define A5PSW_MDIO_CFG_STATUS_BUSY BIT(0) + +#define A5PSW_MDIO_COMMAND 0x704 +/* Register is named TRAININIT in datasheet and should be set when reading */ +#define A5PSW_MDIO_COMMAND_READ BIT(15) +#define A5PSW_MDIO_COMMAND_PHY_ADDR GENMASK(9, 5) +#define A5PSW_MDIO_COMMAND_REG_ADDR GENMASK(4, 0) + +#define A5PSW_MDIO_DATA 0x708 +#define A5PSW_MDIO_DATA_MASK GENMASK(15, 0) + +#define A5PSW_CMD_CFG(port) (0x808 + A5PSW_PORT_OFFSET(port)) +#define A5PSW_CMD_CFG_CNTL_FRM_ENA BIT(23) +#define A5PSW_CMD_CFG_SW_RESET BIT(13) +#define A5PSW_CMD_CFG_TX_CRC_APPEND BIT(11) +#define A5PSW_CMD_CFG_HD_ENA BIT(10) +#define A5PSW_CMD_CFG_PAUSE_IGNORE BIT(8) +#define A5PSW_CMD_CFG_CRC_FWD BIT(6) +#define A5PSW_CMD_CFG_ETH_SPEED BIT(3) +#define A5PSW_CMD_CFG_RX_ENA BIT(1) +#define A5PSW_CMD_CFG_TX_ENA BIT(0) + +#define A5PSW_FRM_LENGTH(port) (0x814 + A5PSW_PORT_OFFSET(port)) +#define A5PSW_FRM_LENGTH_MASK GENMASK(13, 0) + +#define A5PSW_STATUS(port) (0x840 + A5PSW_PORT_OFFSET(port)) + +#define A5PSW_STATS_HIWORD 0x900 + +#define A5PSW_VLAN_TAG(prio, id) (((prio) << 12) | (id)) +#define A5PSW_PORTS_NUM 5 +#define A5PSW_CPU_PORT (A5PSW_PORTS_NUM - 1) +#define A5PSW_MDIO_DEF_FREQ 2500000 +#define A5PSW_MDIO_TIMEOUT 100 +#define A5PSW_JUMBO_LEN (10 * SZ_1K) +#define A5PSW_MDIO_CLK_DIV_MIN 5 +#define A5PSW_TAG_LEN 8 +#define A5PSW_VLAN_COUNT 32 + +/* Ensure enough space for 2 VLAN tags */ +#define A5PSW_EXTRA_MTU_LEN (A5PSW_TAG_LEN + 8) +#define A5PSW_MAX_MTU (A5PSW_JUMBO_LEN - A5PSW_EXTRA_MTU_LEN) + +#define A5PSW_PATTERN_MGMTFWD 0 + +#define A5PSW_LK_BUSY_USEC_POLL 10 +#define A5PSW_CTRL_TIMEOUT 1000 +#define A5PSW_TABLE_ENTRIES 8192 + +/** + * struct a5psw - switch struct + * @base: Base address of the switch + * @hclk: hclk_switch clock + * @clk: clk_switch clock + * @dev: Device associated to the switch + * @mii_bus: MDIO bus struct + * @mdio_freq: MDIO bus frequency requested + * @pcs: Array of PCS connected to the switch ports (not for the CPU) + * @ds: DSA switch struct + * @lk_lock: Lock for the lookup table + * @reg_lock: Lock for register read-modify-write operation + * @bridged_ports: Mask of ports that are bridged and should be flooded + * @br_dev: Bridge net device + */ +struct a5psw { + void __iomem *base; + struct clk *hclk; + struct clk *clk; + struct device *dev; + struct mii_bus *mii_bus; + struct phylink_pcs *pcs[A5PSW_PORTS_NUM - 1]; + struct dsa_switch ds; + struct mutex lk_lock; + spinlock_t reg_lock; + u32 bridged_ports; + struct net_device *br_dev; +}; From patchwork Mon Jun 20 11:08:39 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?b?Q2zDqW1lbnQgTMOpZ2Vy?= X-Patchwork-Id: 583445 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 245D2C43334 for ; Mon, 20 Jun 2022 11:10:55 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S241428AbiFTLKx (ORCPT ); Mon, 20 Jun 2022 07:10:53 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:60228 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S241278AbiFTLKf (ORCPT ); Mon, 20 Jun 2022 07:10:35 -0400 Received: from relay10.mail.gandi.net (relay10.mail.gandi.net [217.70.178.230]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 4D9C315A2B; Mon, 20 Jun 2022 04:10:14 -0700 (PDT) Received: (Authenticated sender: clement.leger@bootlin.com) by mail.gandi.net (Postfix) with ESMTPSA id 93F9E24000E; Mon, 20 Jun 2022 11:10:10 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1655723413; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=SSefSRe/OnLbKThCn6MkGPJqsyO8MBgggjjINgZSYHI=; b=Ke9QJjhVffVQH7uPn9Mst7AKJANqI+7AJaAqvRGBJTC3y+rCnnmn3mjj/HB2MbX2SWLQcx 7mGEtWLLUCMqEQKhor5rBQE+qABqsrtPL1Tx4PfjACvA4PGqD5ZQ7zdOkL7SXNpdeBYW8v mSSBzT4mHBC6/PN+O/wtuzjnKiVH7xyOXM0bavprK3Y3i/R7uhvlK2mYWm9YlfQrHpzy1G LoFDlXGblog/RbIOV0gBYPRyN6SHxzUeT5ReVjJPHEAYxuxLyJk3WQWUZl8FxnH+RKbrMX A5uDNQKqj7855JgNYWUZUeSB6moYdZtZmSPjQC9EMnD68OkDzlYxrL2jla5PqQ== From: =?utf-8?b?Q2zDqW1lbnQgTMOpZ2Vy?= To: Andrew Lunn , Vivien Didelot , Florian Fainelli , Vladimir Oltean , "David S . Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Rob Herring , Krzysztof Kozlowski , Geert Uytterhoeven , Magnus Damm , Heiner Kallweit , Russell King , Alexandre Torgue , Giuseppe Cavallaro , Jose Abreu Cc: =?utf-8?b?Q2zDqW1lbnQgTMOpZ2Vy?= , Thomas Petazzoni , Herve Codina , =?utf-8?q?Miqu=C3=A8l_Raynal?= , Milan Stevanovic , Jimmy Lalande , Pascal Eberhard , linux-kernel@vger.kernel.org, devicetree@vger.kernel.org, linux-renesas-soc@vger.kernel.org, netdev@vger.kernel.org Subject: [PATCH net-next v8 09/16] net: dsa: rzn1-a5psw: add FDB support Date: Mon, 20 Jun 2022 13:08:39 +0200 Message-Id: <20220620110846.374787-10-clement.leger@bootlin.com> X-Mailer: git-send-email 2.36.1 In-Reply-To: <20220620110846.374787-1-clement.leger@bootlin.com> References: <20220620110846.374787-1-clement.leger@bootlin.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: devicetree@vger.kernel.org This commits add forwarding database support to the driver. It implements fdb_add(), fdb_del() and fdb_dump(). Signed-off-by: Clément Léger --- drivers/net/dsa/rzn1_a5psw.c | 168 +++++++++++++++++++++++++++++++++++ drivers/net/dsa/rzn1_a5psw.h | 17 ++++ 2 files changed, 185 insertions(+) diff --git a/drivers/net/dsa/rzn1_a5psw.c b/drivers/net/dsa/rzn1_a5psw.c index 4c2401fbaf9a..3e910da98ae2 100644 --- a/drivers/net/dsa/rzn1_a5psw.c +++ b/drivers/net/dsa/rzn1_a5psw.c @@ -375,6 +375,171 @@ static void a5psw_port_fast_age(struct dsa_switch *ds, int port) a5psw_port_fdb_flush(a5psw, port); } +static int a5psw_lk_execute_lookup(struct a5psw *a5psw, union lk_data *lk_data, + u16 *entry) +{ + u32 ctrl; + int ret; + + a5psw_reg_writel(a5psw, A5PSW_LK_DATA_LO, lk_data->lo); + a5psw_reg_writel(a5psw, A5PSW_LK_DATA_HI, lk_data->hi); + + ctrl = A5PSW_LK_ADDR_CTRL_LOOKUP; + ret = a5psw_lk_execute_ctrl(a5psw, &ctrl); + if (ret) + return ret; + + *entry = ctrl & A5PSW_LK_ADDR_CTRL_ADDRESS; + + return 0; +} + +static int a5psw_port_fdb_add(struct dsa_switch *ds, int port, + const unsigned char *addr, u16 vid, + struct dsa_db db) +{ + struct a5psw *a5psw = ds->priv; + union lk_data lk_data = {0}; + bool inc_learncount = false; + int ret = 0; + u16 entry; + u32 reg; + + ether_addr_copy(lk_data.entry.mac, addr); + lk_data.entry.port_mask = BIT(port); + + mutex_lock(&a5psw->lk_lock); + + /* Set the value to be written in the lookup table */ + ret = a5psw_lk_execute_lookup(a5psw, &lk_data, &entry); + if (ret) + goto lk_unlock; + + lk_data.hi = a5psw_reg_readl(a5psw, A5PSW_LK_DATA_HI); + if (!lk_data.entry.valid) { + inc_learncount = true; + /* port_mask set to 0x1f when entry is not valid, clear it */ + lk_data.entry.port_mask = 0; + lk_data.entry.prio = 0; + } + + lk_data.entry.port_mask |= BIT(port); + lk_data.entry.is_static = 1; + lk_data.entry.valid = 1; + + a5psw_reg_writel(a5psw, A5PSW_LK_DATA_HI, lk_data.hi); + + reg = A5PSW_LK_ADDR_CTRL_WRITE | entry; + ret = a5psw_lk_execute_ctrl(a5psw, ®); + if (ret) + goto lk_unlock; + + if (inc_learncount) { + reg = A5PSW_LK_LEARNCOUNT_MODE_INC; + a5psw_reg_writel(a5psw, A5PSW_LK_LEARNCOUNT, reg); + } + +lk_unlock: + mutex_unlock(&a5psw->lk_lock); + + return ret; +} + +static int a5psw_port_fdb_del(struct dsa_switch *ds, int port, + const unsigned char *addr, u16 vid, + struct dsa_db db) +{ + struct a5psw *a5psw = ds->priv; + union lk_data lk_data = {0}; + bool clear = false; + u16 entry; + u32 reg; + int ret; + + ether_addr_copy(lk_data.entry.mac, addr); + + mutex_lock(&a5psw->lk_lock); + + ret = a5psw_lk_execute_lookup(a5psw, &lk_data, &entry); + if (ret) + goto lk_unlock; + + lk_data.hi = a5psw_reg_readl(a5psw, A5PSW_LK_DATA_HI); + + /* Our hardware does not associate any VID to the FDB entries so this + * means that if two entries were added for the same mac but for + * different VID, then, on the deletion of the first one, we would also + * delete the second one. Since there is unfortunately nothing we can do + * about that, do not return an error... + */ + if (!lk_data.entry.valid) + goto lk_unlock; + + lk_data.entry.port_mask &= ~BIT(port); + /* If there is no more port in the mask, clear the entry */ + if (lk_data.entry.port_mask == 0) + clear = true; + + a5psw_reg_writel(a5psw, A5PSW_LK_DATA_HI, lk_data.hi); + + reg = entry; + if (clear) + reg |= A5PSW_LK_ADDR_CTRL_CLEAR; + else + reg |= A5PSW_LK_ADDR_CTRL_WRITE; + + ret = a5psw_lk_execute_ctrl(a5psw, ®); + if (ret) + goto lk_unlock; + + /* Decrement LEARNCOUNT */ + if (clear) { + reg = A5PSW_LK_LEARNCOUNT_MODE_DEC; + a5psw_reg_writel(a5psw, A5PSW_LK_LEARNCOUNT, reg); + } + +lk_unlock: + mutex_unlock(&a5psw->lk_lock); + + return ret; +} + +static int a5psw_port_fdb_dump(struct dsa_switch *ds, int port, + dsa_fdb_dump_cb_t *cb, void *data) +{ + struct a5psw *a5psw = ds->priv; + union lk_data lk_data; + int i = 0, ret = 0; + u32 reg; + + mutex_lock(&a5psw->lk_lock); + + for (i = 0; i < A5PSW_TABLE_ENTRIES; i++) { + reg = A5PSW_LK_ADDR_CTRL_READ | A5PSW_LK_ADDR_CTRL_WAIT | i; + + ret = a5psw_lk_execute_ctrl(a5psw, ®); + if (ret) + goto out_unlock; + + lk_data.hi = a5psw_reg_readl(a5psw, A5PSW_LK_DATA_HI); + /* If entry is not valid or does not contain the port, skip */ + if (!lk_data.entry.valid || + !(lk_data.entry.port_mask & BIT(port))) + continue; + + lk_data.lo = a5psw_reg_readl(a5psw, A5PSW_LK_DATA_LO); + + ret = cb(lk_data.entry.mac, 0, lk_data.entry.is_static, data); + if (ret) + goto out_unlock; + } + +out_unlock: + mutex_unlock(&a5psw->lk_lock); + + return ret; +} + static u64 a5psw_read_stat(struct a5psw *a5psw, u32 offset, int port) { u32 reg_lo, reg_hi; @@ -591,6 +756,9 @@ static const struct dsa_switch_ops a5psw_switch_ops = { .port_bridge_leave = a5psw_port_bridge_leave, .port_stp_state_set = a5psw_port_stp_state_set, .port_fast_age = a5psw_port_fast_age, + .port_fdb_add = a5psw_port_fdb_add, + .port_fdb_del = a5psw_port_fdb_del, + .port_fdb_dump = a5psw_port_fdb_dump, }; static int a5psw_mdio_wait_busy(struct a5psw *a5psw) diff --git a/drivers/net/dsa/rzn1_a5psw.h b/drivers/net/dsa/rzn1_a5psw.h index 6b0ce2b15eca..c67abd49c013 100644 --- a/drivers/net/dsa/rzn1_a5psw.h +++ b/drivers/net/dsa/rzn1_a5psw.h @@ -211,6 +211,23 @@ #define A5PSW_CTRL_TIMEOUT 1000 #define A5PSW_TABLE_ENTRIES 8192 +struct fdb_entry { + u8 mac[ETH_ALEN]; + u16 valid:1; + u16 is_static:1; + u16 prio:3; + u16 port_mask:5; + u16 reserved:6; +} __packed; + +union lk_data { + struct { + u32 lo; + u32 hi; + }; + struct fdb_entry entry; +}; + /** * struct a5psw - switch struct * @base: Base address of the switch From patchwork Mon Jun 20 11:08:40 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?b?Q2zDqW1lbnQgTMOpZ2Vy?= X-Patchwork-Id: 583446 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 8F709CCA481 for ; Mon, 20 Jun 2022 11:10:48 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S241585AbiFTLKq (ORCPT ); Mon, 20 Jun 2022 07:10:46 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:60418 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S241336AbiFTLKm (ORCPT ); Mon, 20 Jun 2022 07:10:42 -0400 Received: from relay10.mail.gandi.net (relay10.mail.gandi.net [217.70.178.230]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id D3BCD2677; Mon, 20 Jun 2022 04:10:16 -0700 (PDT) Received: (Authenticated sender: clement.leger@bootlin.com) by mail.gandi.net (Postfix) with ESMTPSA id 30DFD240007; Mon, 20 Jun 2022 11:10:13 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1655723415; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=MRhI3o9vUV9HJ/akGG/sCFg0WByQ0Yk8Z88IwMeWHwE=; b=WjMxQFzxFqM+oZoNhtuxf6ogp+fPcj2Q9Ldq4bYL8le3vWlfi7JZT4PlMvz3XDVk8rzWgR Sct/Gu7zI0dz8bmXUe6cAl6Kmxd8vsBTPRGi/6/jeUxGFcZw3msQpW2pujl+8nfk86PqXT bUSYrCp4awG2V1FwcDpGnSbyseFVJXHyLjPNMmQb8V3JDxL+/7G40k420lqCwnaB27+U97 GDLVSQqvPVUwROyG1bwsk1S1zuT19nT5GVLSidF0yO+Uy385zVHtIfDX2DxPlXUdOqSNlD rua8A9M9QRPqm+k/9t1kDI6X9UMGZxfTutqXI0uQ+CcpmssHMnPNyBEHnxokDA== From: =?utf-8?b?Q2zDqW1lbnQgTMOpZ2Vy?= To: Andrew Lunn , Vivien Didelot , Florian Fainelli , Vladimir Oltean , "David S . Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Rob Herring , Krzysztof Kozlowski , Geert Uytterhoeven , Magnus Damm , Heiner Kallweit , Russell King , Alexandre Torgue , Giuseppe Cavallaro , Jose Abreu Cc: =?utf-8?b?Q2zDqW1lbnQgTMOpZ2Vy?= , Thomas Petazzoni , Herve Codina , =?utf-8?q?Miqu=C3=A8l_Raynal?= , Milan Stevanovic , Jimmy Lalande , Pascal Eberhard , linux-kernel@vger.kernel.org, devicetree@vger.kernel.org, linux-renesas-soc@vger.kernel.org, netdev@vger.kernel.org, Rob Herring Subject: [PATCH net-next v8 10/16] dt-bindings: net: snps,dwmac: add "power-domains" property Date: Mon, 20 Jun 2022 13:08:40 +0200 Message-Id: <20220620110846.374787-11-clement.leger@bootlin.com> X-Mailer: git-send-email 2.36.1 In-Reply-To: <20220620110846.374787-1-clement.leger@bootlin.com> References: <20220620110846.374787-1-clement.leger@bootlin.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: devicetree@vger.kernel.org Since the stmmac driver already uses pm_runtime*() functions, describe "power-domains" property in the binding. Reported-by: Geert Uytterhoeven Signed-off-by: Clément Léger Acked-by: Rob Herring Reviewed-by: Florian Fainelli --- Documentation/devicetree/bindings/net/snps,dwmac.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/net/snps,dwmac.yaml b/Documentation/devicetree/bindings/net/snps,dwmac.yaml index 36c85eb3dc0d..09f97fb5596d 100644 --- a/Documentation/devicetree/bindings/net/snps,dwmac.yaml +++ b/Documentation/devicetree/bindings/net/snps,dwmac.yaml @@ -135,6 +135,9 @@ properties: reset-names: const: stmmaceth + power-domains: + maxItems: 1 + mac-mode: $ref: ethernet-controller.yaml#/properties/phy-connection-type description: From patchwork Mon Jun 20 11:08:43 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?b?Q2zDqW1lbnQgTMOpZ2Vy?= X-Patchwork-Id: 583444 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 07D18C43334 for ; Mon, 20 Jun 2022 11:11:44 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S241668AbiFTLLm (ORCPT ); Mon, 20 Jun 2022 07:11:42 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:59820 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S241389AbiFTLKn (ORCPT ); Mon, 20 Jun 2022 07:10:43 -0400 Received: from relay10.mail.gandi.net (relay10.mail.gandi.net [IPv6:2001:4b98:dc4:8::230]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 3F63315A2E; Mon, 20 Jun 2022 04:10:25 -0700 (PDT) Received: (Authenticated sender: clement.leger@bootlin.com) by mail.gandi.net (Postfix) with ESMTPSA id 2530924000E; Mon, 20 Jun 2022 11:10:21 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1655723423; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=kCft54/wbUztXKT2urhOKH33tJlwwROSYaG1fJeAUws=; b=MFNm7RgnP6YTyHRSSYCEEIEhvj+snogpXl6FCn8UARKr5kkbSP/zK2NKJwjMDWWByadbWB RtehjoWFT2wRX4juOa+fiYtdpZ5UYX/JJpauLVqwfs3EYXIlO4hU/eCa+T4+CUSyz22OPQ yunJHen/XR3LmTcWtCdDqb8e/qdZA8+DKcPp+gYPlVBxCgDmxLDn16dTOxWOCfZqxo/gGB sUVjsT6s7Q0v6aKJ3Ori0EVuOFNJOZqJdkFg3uR7HGiB1MWm9QLe1ZVKM3o81J6qYJiRD5 y2JMkJJFelVxj1hvWTScsfA9Dc5OkMnuvC/bgeQSAvlM9EtotYOLF2VC1Js1Tg== From: =?utf-8?b?Q2zDqW1lbnQgTMOpZ2Vy?= To: Andrew Lunn , Vivien Didelot , Florian Fainelli , Vladimir Oltean , "David S . Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Rob Herring , Krzysztof Kozlowski , Geert Uytterhoeven , Magnus Damm , Heiner Kallweit , Russell King , Alexandre Torgue , Giuseppe Cavallaro , Jose Abreu Cc: =?utf-8?b?Q2zDqW1lbnQgTMOpZ2Vy?= , Thomas Petazzoni , Herve Codina , =?utf-8?q?Miqu=C3=A8l_Raynal?= , Milan Stevanovic , Jimmy Lalande , Pascal Eberhard , linux-kernel@vger.kernel.org, devicetree@vger.kernel.org, linux-renesas-soc@vger.kernel.org, netdev@vger.kernel.org Subject: [PATCH net-next v8 13/16] ARM: dts: r9a06g032: describe GMAC2 Date: Mon, 20 Jun 2022 13:08:43 +0200 Message-Id: <20220620110846.374787-14-clement.leger@bootlin.com> X-Mailer: git-send-email 2.36.1 In-Reply-To: <20220620110846.374787-1-clement.leger@bootlin.com> References: <20220620110846.374787-1-clement.leger@bootlin.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: devicetree@vger.kernel.org RZ/N1 SoC includes two MAC named GMACx that are compatible with the "snps,dwmac" driver. GMAC1 is connected directly to the MII converter port 1. GMAC2 however can be used as the MAC for the switch CPU management port or can be muxed to be connected directly to the MII converter port 2. This commit add description for the GMAC2 which will be used by the switch description. Signed-off-by: Clément Léger --- arch/arm/boot/dts/r9a06g032.dtsi | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/arch/arm/boot/dts/r9a06g032.dtsi b/arch/arm/boot/dts/r9a06g032.dtsi index f6241af33112..42ce02e51e8d 100644 --- a/arch/arm/boot/dts/r9a06g032.dtsi +++ b/arch/arm/boot/dts/r9a06g032.dtsi @@ -304,6 +304,24 @@ dma1: dma-controller@40105000 { data-width = <8>; }; + gmac2: ethernet@44002000 { + compatible = "renesas,r9a06g032-gmac", "renesas,rzn1-gmac", "snps,dwmac"; + reg = <0x44002000 0x2000>; + interrupt-parent = <&gic>; + interrupts = , + , + ; + interrupt-names = "macirq", "eth_wake_irq", "eth_lpi"; + clocks = <&sysctrl R9A06G032_HCLK_GMAC1>; + clock-names = "stmmaceth"; + power-domains = <&sysctrl>; + snps,multicast-filter-bins = <256>; + snps,perfect-filter-entries = <128>; + tx-fifo-depth = <2048>; + rx-fifo-depth = <4096>; + status = "disabled"; + }; + eth_miic: eth-miic@44030000 { compatible = "renesas,r9a06g032-miic", "renesas,rzn1-miic"; #address-cells = <1>; From patchwork Mon Jun 20 11:08:45 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?b?Q2zDqW1lbnQgTMOpZ2Vy?= X-Patchwork-Id: 583443 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 00C51CCA479 for ; Mon, 20 Jun 2022 11:11:54 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S241725AbiFTLLw (ORCPT ); Mon, 20 Jun 2022 07:11:52 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:60524 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S241539AbiFTLKo (ORCPT ); Mon, 20 Jun 2022 07:10:44 -0400 Received: from relay10.mail.gandi.net (relay10.mail.gandi.net [IPv6:2001:4b98:dc4:8::230]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 4EC8B15FEC; Mon, 20 Jun 2022 04:10:30 -0700 (PDT) Received: (Authenticated sender: clement.leger@bootlin.com) by mail.gandi.net (Postfix) with ESMTPSA id 74B0E240005; Mon, 20 Jun 2022 11:10:26 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1655723428; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=ZuP4gcOVQsn/2pRPVhdQXg999qyZbyIWmBlFl1Oa+3w=; b=YAxB1jnQGQz9ioXRx8s/D7C0cCVOZYCl0brYC2b+NmYljEbslaTQYe4S8V1kjzeMlb6bQa qJxnJNnz4O/IoPrVFySu0+OMU+yc75J3WeIWSexEn/UrlzriJTMhmmwuid0N6ek1aAEzIP YNj75eXFz3N4PCpj9Z4cr3gV+VUEzrB9awxQJ5bXiUE1C6bWZ4th+UwHq62tDSZElVTRZO +x9LHNy3IHo7sMoKt+Nbdoy4HHy2aPvuaMMivqq0rgIOartkV/xmf/8BjT4Sm9uUTr4lrR RcejNSwW0aDEKTtHXp9S/ENub2vP2nRIvccStXzxmbp3cSSMuOEdNvBPzW3C+g== From: =?utf-8?b?Q2zDqW1lbnQgTMOpZ2Vy?= To: Andrew Lunn , Vivien Didelot , Florian Fainelli , Vladimir Oltean , "David S . Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Rob Herring , Krzysztof Kozlowski , Geert Uytterhoeven , Magnus Damm , Heiner Kallweit , Russell King , Alexandre Torgue , Giuseppe Cavallaro , Jose Abreu Cc: =?utf-8?b?Q2zDqW1lbnQgTMOpZ2Vy?= , Thomas Petazzoni , Herve Codina , =?utf-8?q?Miqu=C3=A8l_Raynal?= , Milan Stevanovic , Jimmy Lalande , Pascal Eberhard , linux-kernel@vger.kernel.org, devicetree@vger.kernel.org, linux-renesas-soc@vger.kernel.org, netdev@vger.kernel.org Subject: [PATCH net-next v8 15/16] ARM: dts: r9a06g032-rzn1d400-db: add switch description Date: Mon, 20 Jun 2022 13:08:45 +0200 Message-Id: <20220620110846.374787-16-clement.leger@bootlin.com> X-Mailer: git-send-email 2.36.1 In-Reply-To: <20220620110846.374787-1-clement.leger@bootlin.com> References: <20220620110846.374787-1-clement.leger@bootlin.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: devicetree@vger.kernel.org Add description for the switch, GMAC2 and MII converter. With these definitions, the switch port 0 and 1 (MII port 5 and 4) are working on RZ/N1D-DB board. Signed-off-by: Clément Léger --- arch/arm/boot/dts/r9a06g032-rzn1d400-db.dts | 117 ++++++++++++++++++++ 1 file changed, 117 insertions(+) diff --git a/arch/arm/boot/dts/r9a06g032-rzn1d400-db.dts b/arch/arm/boot/dts/r9a06g032-rzn1d400-db.dts index 3f8f3ce87e12..36b898d9f115 100644 --- a/arch/arm/boot/dts/r9a06g032-rzn1d400-db.dts +++ b/arch/arm/boot/dts/r9a06g032-rzn1d400-db.dts @@ -8,6 +8,8 @@ /dts-v1/; +#include +#include #include "r9a06g032.dtsi" / { @@ -31,3 +33,118 @@ &wdt0 { timeout-sec = <60>; status = "okay"; }; + +&gmac2 { + status = "okay"; + phy-mode = "gmii"; + fixed-link { + speed = <1000>; + full-duplex; + }; +}; + +&switch { + status = "okay"; + #address-cells = <1>; + #size-cells = <0>; + + pinctrl-names = "default"; + pinctrl-0 = <&pins_mdio1>, <&pins_eth3>, <&pins_eth4>; + + dsa,member = <0 0>; + + mdio { + clock-frequency = <2500000>; + + #address-cells = <1>; + #size-cells = <0>; + + switch0phy4: ethernet-phy@4{ + reg = <4>; + micrel,led-mode = <1>; + }; + + switch0phy5: ethernet-phy@5{ + reg = <5>; + micrel,led-mode = <1>; + }; + }; +}; + +&switch_port0 { + label = "lan0"; + phy-mode = "mii"; + phy-handle = <&switch0phy5>; + status = "okay"; +}; + +&switch_port1 { + label = "lan1"; + phy-mode = "mii"; + phy-handle = <&switch0phy4>; + status = "okay"; +}; + +&switch_port4 { + status = "okay"; +}; + +ð_miic { + status = "okay"; + renesas,miic-switch-portin = ; +}; + +&mii_conv4 { + renesas,miic-input = ; + status = "okay"; +}; + +&mii_conv5 { + renesas,miic-input = ; + status = "okay"; +}; + +&pinctrl{ + pins_mdio1: pins_mdio1 { + pinmux = < + RZN1_PINMUX(152, RZN1_FUNC_MDIO1_SWITCH) + RZN1_PINMUX(153, RZN1_FUNC_MDIO1_SWITCH) + >; + }; + pins_eth3: pins_eth3 { + pinmux = < + RZN1_PINMUX(36, RZN1_FUNC_CLK_ETH_MII_RGMII_RMII) + RZN1_PINMUX(37, RZN1_FUNC_CLK_ETH_MII_RGMII_RMII) + RZN1_PINMUX(38, RZN1_FUNC_CLK_ETH_MII_RGMII_RMII) + RZN1_PINMUX(39, RZN1_FUNC_CLK_ETH_MII_RGMII_RMII) + RZN1_PINMUX(40, RZN1_FUNC_CLK_ETH_MII_RGMII_RMII) + RZN1_PINMUX(41, RZN1_FUNC_CLK_ETH_MII_RGMII_RMII) + RZN1_PINMUX(42, RZN1_FUNC_CLK_ETH_MII_RGMII_RMII) + RZN1_PINMUX(43, RZN1_FUNC_CLK_ETH_MII_RGMII_RMII) + RZN1_PINMUX(44, RZN1_FUNC_CLK_ETH_MII_RGMII_RMII) + RZN1_PINMUX(45, RZN1_FUNC_CLK_ETH_MII_RGMII_RMII) + RZN1_PINMUX(46, RZN1_FUNC_CLK_ETH_MII_RGMII_RMII) + RZN1_PINMUX(47, RZN1_FUNC_CLK_ETH_MII_RGMII_RMII) + >; + drive-strength = <6>; + bias-disable; + }; + pins_eth4: pins_eth4 { + pinmux = < + RZN1_PINMUX(48, RZN1_FUNC_CLK_ETH_MII_RGMII_RMII) + RZN1_PINMUX(49, RZN1_FUNC_CLK_ETH_MII_RGMII_RMII) + RZN1_PINMUX(50, RZN1_FUNC_CLK_ETH_MII_RGMII_RMII) + RZN1_PINMUX(51, RZN1_FUNC_CLK_ETH_MII_RGMII_RMII) + RZN1_PINMUX(52, RZN1_FUNC_CLK_ETH_MII_RGMII_RMII) + RZN1_PINMUX(53, RZN1_FUNC_CLK_ETH_MII_RGMII_RMII) + RZN1_PINMUX(54, RZN1_FUNC_CLK_ETH_MII_RGMII_RMII) + RZN1_PINMUX(55, RZN1_FUNC_CLK_ETH_MII_RGMII_RMII) + RZN1_PINMUX(56, RZN1_FUNC_CLK_ETH_MII_RGMII_RMII) + RZN1_PINMUX(57, RZN1_FUNC_CLK_ETH_MII_RGMII_RMII) + RZN1_PINMUX(58, RZN1_FUNC_CLK_ETH_MII_RGMII_RMII) + RZN1_PINMUX(59, RZN1_FUNC_CLK_ETH_MII_RGMII_RMII) + >; + drive-strength = <6>; + bias-disable; + }; +};