From patchwork Sat Apr 3 11:48:40 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Oleksij Rempel X-Patchwork-Id: 12181899 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=-16.7 required=3.0 tests=BAYES_00, 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 B0371C43461 for ; Sat, 3 Apr 2021 11:49:13 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 8B0F86127A for ; Sat, 3 Apr 2021 11:49:13 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236752AbhDCLtO (ORCPT ); Sat, 3 Apr 2021 07:49:14 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:38600 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236718AbhDCLtM (ORCPT ); Sat, 3 Apr 2021 07:49:12 -0400 Received: from metis.ext.pengutronix.de (metis.ext.pengutronix.de [IPv6:2001:67c:670:201:290:27ff:fe1d:cc33]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 9E04CC061788 for ; Sat, 3 Apr 2021 04:49:09 -0700 (PDT) Received: from dude.hi.pengutronix.de ([2001:67c:670:100:1d::7]) by metis.ext.pengutronix.de with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1lSelp-0000eq-6s; Sat, 03 Apr 2021 13:48:53 +0200 Received: from ore by dude.hi.pengutronix.de with local (Exim 4.92) (envelope-from ) id 1lSell-0007xX-ML; Sat, 03 Apr 2021 13:48:49 +0200 From: Oleksij Rempel To: Andrew Lunn , Vivien Didelot , Florian Fainelli , Vladimir Oltean , "David S. Miller" , Jakub Kicinski , Russell King Cc: Oleksij Rempel , Pengutronix Kernel Team , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mips@vger.kernel.org Subject: [PATCH net-next v1 1/9] net: dsa: add rcv_post call back Date: Sat, 3 Apr 2021 13:48:40 +0200 Message-Id: <20210403114848.30528-2-o.rempel@pengutronix.de> X-Mailer: git-send-email 2.29.2 In-Reply-To: <20210403114848.30528-1-o.rempel@pengutronix.de> References: <20210403114848.30528-1-o.rempel@pengutronix.de> MIME-Version: 1.0 X-SA-Exim-Connect-IP: 2001:67c:670:100:1d::7 X-SA-Exim-Mail-From: ore@pengutronix.de X-SA-Exim-Scanned: No (on metis.ext.pengutronix.de); SAEximRunCond expanded to false X-PTX-Original-Recipient: linux-mips@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-mips@vger.kernel.org Some switches (for example ar9331) do not provide enough information about forwarded packets. If the switch decision was made based on IPv4 or IPv6 header, we need to analyze it and set proper flag. Potentially we can do it in existing rcv path, on other hand we can avoid part of duplicated work and let the dsa framework set skb header pointers and then use preprocessed skb one step later withing the rcv_post call back. This patch is needed for ar9331 switch. Signed-off-by: Oleksij Rempel --- include/net/dsa.h | 2 ++ net/dsa/dsa.c | 4 ++++ net/dsa/port.c | 1 + 3 files changed, 7 insertions(+) diff --git a/include/net/dsa.h b/include/net/dsa.h index 57b2c49f72f4..f1a7aa4303a7 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -84,6 +84,7 @@ struct dsa_device_ops { struct sk_buff *(*xmit)(struct sk_buff *skb, struct net_device *dev); struct sk_buff *(*rcv)(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt); + void (*rcv_post)(struct sk_buff *skb); void (*flow_dissect)(const struct sk_buff *skb, __be16 *proto, int *offset); /* Used to determine which traffic should match the DSA filter in @@ -247,6 +248,7 @@ struct dsa_port { struct dsa_switch_tree *dst; struct sk_buff *(*rcv)(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt); + void (*rcv_post)(struct sk_buff *skb); bool (*filter)(const struct sk_buff *skb, struct net_device *dev); enum { diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c index 84cad1be9ce4..fa3e7201e760 100644 --- a/net/dsa/dsa.c +++ b/net/dsa/dsa.c @@ -249,6 +249,10 @@ static int dsa_switch_rcv(struct sk_buff *skb, struct net_device *dev, skb->pkt_type = PACKET_HOST; skb->protocol = eth_type_trans(skb, skb->dev); + + if (cpu_dp->rcv_post) + cpu_dp->rcv_post(skb); + if (unlikely(!dsa_slave_dev_check(skb->dev))) { /* Packet is to be injected directly on an upper * device, e.g. a team/bond, so skip all DSA-port diff --git a/net/dsa/port.c b/net/dsa/port.c index 01e30264b25b..859957688c62 100644 --- a/net/dsa/port.c +++ b/net/dsa/port.c @@ -720,6 +720,7 @@ void dsa_port_set_tag_protocol(struct dsa_port *cpu_dp, { cpu_dp->filter = tag_ops->filter; cpu_dp->rcv = tag_ops->rcv; + cpu_dp->rcv_post = tag_ops->rcv_post; cpu_dp->tag_ops = tag_ops; } From patchwork Sat Apr 3 11:48:41 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Oleksij Rempel X-Patchwork-Id: 12181913 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=-16.7 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT autolearn=unavailable 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 080D1C433ED for ; Sat, 3 Apr 2021 11:50:16 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id D7AAE61244 for ; Sat, 3 Apr 2021 11:50:15 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236727AbhDCLuR (ORCPT ); Sat, 3 Apr 2021 07:50:17 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:38616 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236730AbhDCLtN (ORCPT ); Sat, 3 Apr 2021 07:49:13 -0400 Received: from metis.ext.pengutronix.de (metis.ext.pengutronix.de [IPv6:2001:67c:670:201:290:27ff:fe1d:cc33]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id EE923C0617AA for ; Sat, 3 Apr 2021 04:49:09 -0700 (PDT) Received: from dude.hi.pengutronix.de ([2001:67c:670:100:1d::7]) by metis.ext.pengutronix.de with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1lSelp-0000er-6s; Sat, 03 Apr 2021 13:48:53 +0200 Received: from ore by dude.hi.pengutronix.de with local (Exim 4.92) (envelope-from ) id 1lSell-0007xg-Nc; Sat, 03 Apr 2021 13:48:49 +0200 From: Oleksij Rempel To: Andrew Lunn , Vivien Didelot , Florian Fainelli , Vladimir Oltean , "David S. Miller" , Jakub Kicinski , Russell King Cc: Oleksij Rempel , Pengutronix Kernel Team , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mips@vger.kernel.org Subject: [PATCH net-next v1 2/9] net: dsa: tag_ar9331: detect IGMP and MLD packets Date: Sat, 3 Apr 2021 13:48:41 +0200 Message-Id: <20210403114848.30528-3-o.rempel@pengutronix.de> X-Mailer: git-send-email 2.29.2 In-Reply-To: <20210403114848.30528-1-o.rempel@pengutronix.de> References: <20210403114848.30528-1-o.rempel@pengutronix.de> MIME-Version: 1.0 X-SA-Exim-Connect-IP: 2001:67c:670:100:1d::7 X-SA-Exim-Mail-From: ore@pengutronix.de X-SA-Exim-Scanned: No (on metis.ext.pengutronix.de); SAEximRunCond expanded to false X-PTX-Original-Recipient: linux-mips@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-mips@vger.kernel.org The ar9331 switch is not forwarding IGMP and MLD packets if IGMP snooping is enabled. This patch is trying to mimic the HW heuristic to take same decisions as this switch would do to be able to tell the linux bridge if some packet was prabably forwarded or not. Signed-off-by: Oleksij Rempel --- net/dsa/tag_ar9331.c | 130 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) diff --git a/net/dsa/tag_ar9331.c b/net/dsa/tag_ar9331.c index 002cf7f952e2..0ba90e0f91bb 100644 --- a/net/dsa/tag_ar9331.c +++ b/net/dsa/tag_ar9331.c @@ -6,6 +6,8 @@ #include #include +#include +#include #include "dsa_priv.h" @@ -24,6 +26,69 @@ #define AR9331_HDR_RESERVED_MASK GENMASK(5, 4) #define AR9331_HDR_PORT_NUM_MASK GENMASK(3, 0) +/* + * This code replicated MLD detection more or less in the same way as the + * switch is doing it + */ +static int ipv6_mc_check_ip6hdr(struct sk_buff *skb) +{ + const struct ipv6hdr *ip6h; + unsigned int offset; + + offset = skb_network_offset(skb) + sizeof(*ip6h); + + if (!pskb_may_pull(skb, offset)) + return -EINVAL; + + ip6h = ipv6_hdr(skb); + + if (ip6h->version != 6) + return -EINVAL; + + skb_set_transport_header(skb, offset); + + return 0; +} + +static int ipv6_mc_check_exthdrs(struct sk_buff *skb) +{ + const struct ipv6hdr *ip6h; + int offset; + u8 nexthdr; + __be16 frag_off; + + ip6h = ipv6_hdr(skb); + + if (ip6h->nexthdr != IPPROTO_HOPOPTS) + return -ENOMSG; + + nexthdr = ip6h->nexthdr; + offset = skb_network_offset(skb) + sizeof(*ip6h); + offset = ipv6_skip_exthdr(skb, offset, &nexthdr, &frag_off); + + if (offset < 0) + return -EINVAL; + + if (nexthdr != IPPROTO_ICMPV6) + return -ENOMSG; + + skb_set_transport_header(skb, offset); + + return 0; +} + +static int my_ipv6_mc_check_mld(struct sk_buff *skb) +{ + int ret; + + ret = ipv6_mc_check_ip6hdr(skb); + if (ret < 0) + return ret; + + return ipv6_mc_check_exthdrs(skb); +} + + static struct sk_buff *ar9331_tag_xmit(struct sk_buff *skb, struct net_device *dev) { @@ -31,6 +96,13 @@ static struct sk_buff *ar9331_tag_xmit(struct sk_buff *skb, __le16 *phdr; u16 hdr; + if (dp->stp_state == BR_STATE_BLOCKING) { + /* TODO: should we reflect it in the stats? */ + netdev_warn_once(dev, "%s:%i dropping blocking packet\n", + __func__, __LINE__); + return NULL; + } + phdr = skb_push(skb, AR9331_HDR_LEN); hdr = FIELD_PREP(AR9331_HDR_VERSION_MASK, AR9331_HDR_VERSION); @@ -80,11 +152,69 @@ static struct sk_buff *ar9331_tag_rcv(struct sk_buff *skb, return skb; } +static void ar9331_tag_rcv_post(struct sk_buff *skb) +{ + const struct iphdr *iph; + unsigned char *dest; + int ret; + + /* + * Since the switch do not tell us which packets was offloaded we assume + * that all of them did. Except: + * - port is not configured for forwarding to any other ports + * - igmp/mld snooping is enabled + * - unicast or multicast flood is disabled on some of bridged ports + * - if we have two port bridge and one is not in forwarding state. + * - packet was dropped on the output port.. + * - any other reasons? + */ + skb->offload_fwd_mark = true; + + dest = eth_hdr(skb)->h_dest; + /* + * Complete not multicast traffic seems to be forwarded automatically, + * as long as multicast and unicast flood are enabled + */ + if (!(is_multicast_ether_addr(dest) && !is_broadcast_ether_addr(dest))) + return; + + + /* + * Documentation do not providing any usable information on how the + * igmp/mld snooping is implemented on this switch. Following + * implementation is based on testing, by sending correct and malformed + * packets to the switch. + * It is not trying to find sane and properly formated packets. Instead + * it is trying to be as close to the switch behavior as possible. + */ + skb_reset_network_header(skb); + switch (ntohs(skb->protocol)) { + case ETH_P_IP: + + if (!pskb_network_may_pull(skb, sizeof(*iph))) + break; + + iph = ip_hdr(skb); + if (iph->protocol == IPPROTO_IGMP) + skb->offload_fwd_mark = false; + + break; + case ETH_P_IPV6: + ret = my_ipv6_mc_check_mld(skb); + if (!ret) + skb->offload_fwd_mark = false; + + break; + } +} + + static const struct dsa_device_ops ar9331_netdev_ops = { .name = "ar9331", .proto = DSA_TAG_PROTO_AR9331, .xmit = ar9331_tag_xmit, .rcv = ar9331_tag_rcv, + .rcv_post = ar9331_tag_rcv_post, .overhead = AR9331_HDR_LEN, }; From patchwork Sat Apr 3 11:48:42 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Oleksij Rempel X-Patchwork-Id: 12181901 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=-16.7 required=3.0 tests=BAYES_00, 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 D6123C43462 for ; Sat, 3 Apr 2021 11:49:13 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 9E8046127B for ; Sat, 3 Apr 2021 11:49:13 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236762AbhDCLtO (ORCPT ); Sat, 3 Apr 2021 07:49:14 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:38608 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236736AbhDCLtN (ORCPT ); Sat, 3 Apr 2021 07:49:13 -0400 Received: from metis.ext.pengutronix.de (metis.ext.pengutronix.de [IPv6:2001:67c:670:201:290:27ff:fe1d:cc33]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id EB8C9C061793 for ; Sat, 3 Apr 2021 04:49:09 -0700 (PDT) Received: from dude.hi.pengutronix.de ([2001:67c:670:100:1d::7]) by metis.ext.pengutronix.de with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1lSelp-0000es-7B; Sat, 03 Apr 2021 13:48:53 +0200 Received: from ore by dude.hi.pengutronix.de with local (Exim 4.92) (envelope-from ) id 1lSell-0007xp-OX; Sat, 03 Apr 2021 13:48:49 +0200 From: Oleksij Rempel To: Andrew Lunn , Vivien Didelot , Florian Fainelli , Vladimir Oltean , "David S. Miller" , Jakub Kicinski , Russell King Cc: Oleksij Rempel , Pengutronix Kernel Team , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mips@vger.kernel.org Subject: [PATCH net-next v1 3/9] net: dsa: qca: ar9331: reorder MDIO write sequence Date: Sat, 3 Apr 2021 13:48:42 +0200 Message-Id: <20210403114848.30528-4-o.rempel@pengutronix.de> X-Mailer: git-send-email 2.29.2 In-Reply-To: <20210403114848.30528-1-o.rempel@pengutronix.de> References: <20210403114848.30528-1-o.rempel@pengutronix.de> MIME-Version: 1.0 X-SA-Exim-Connect-IP: 2001:67c:670:100:1d::7 X-SA-Exim-Mail-From: ore@pengutronix.de X-SA-Exim-Scanned: No (on metis.ext.pengutronix.de); SAEximRunCond expanded to false X-PTX-Original-Recipient: linux-mips@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-mips@vger.kernel.org In case of this switch we work with 32bit registers on top of 16bit bus. Some registers (for example access to forwarding database) have trigger bit on the first 16bit half of request and the result + configuration of request in the second half. Without this this patch, we would trigger database operation and overwrite result in one run. To make it work properly, we should do the second part of transfer before the first one is done. So far, this rule seems to work for all registers on this switch. Signed-off-by: Oleksij Rempel Reviewed-by: Andrew Lunn --- drivers/net/dsa/qca/ar9331.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/net/dsa/qca/ar9331.c b/drivers/net/dsa/qca/ar9331.c index ca2ad77b71f1..9a5035b2f0ff 100644 --- a/drivers/net/dsa/qca/ar9331.c +++ b/drivers/net/dsa/qca/ar9331.c @@ -837,16 +837,17 @@ static int ar9331_mdio_write(void *ctx, u32 reg, u32 val) return 0; } - ret = __ar9331_mdio_write(sbus, AR9331_SW_MDIO_PHY_MODE_REG, reg, val); + ret = __ar9331_mdio_write(sbus, AR9331_SW_MDIO_PHY_MODE_REG, reg + 2, + val >> 16); if (ret < 0) goto error; - ret = __ar9331_mdio_write(sbus, AR9331_SW_MDIO_PHY_MODE_REG, reg + 2, - val >> 16); + ret = __ar9331_mdio_write(sbus, AR9331_SW_MDIO_PHY_MODE_REG, reg, val); if (ret < 0) goto error; return 0; + error: dev_err_ratelimited(&sbus->dev, "Bus error. Failed to write register.\n"); return ret; From patchwork Sat Apr 3 11:48:43 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Oleksij Rempel X-Patchwork-Id: 12181905 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=-16.7 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT autolearn=unavailable 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 C7B4BC433ED for ; Sat, 3 Apr 2021 11:49:16 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 9C6FD6124B for ; Sat, 3 Apr 2021 11:49:16 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236807AbhDCLtR (ORCPT ); Sat, 3 Apr 2021 07:49:17 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:38614 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236739AbhDCLtN (ORCPT ); Sat, 3 Apr 2021 07:49:13 -0400 Received: from metis.ext.pengutronix.de (metis.ext.pengutronix.de [IPv6:2001:67c:670:201:290:27ff:fe1d:cc33]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id EE7E8C0617A9 for ; Sat, 3 Apr 2021 04:49:09 -0700 (PDT) Received: from dude.hi.pengutronix.de ([2001:67c:670:100:1d::7]) by metis.ext.pengutronix.de with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1lSelp-0000et-6x; Sat, 03 Apr 2021 13:48:53 +0200 Received: from ore by dude.hi.pengutronix.de with local (Exim 4.92) (envelope-from ) id 1lSell-0007xy-PW; Sat, 03 Apr 2021 13:48:49 +0200 From: Oleksij Rempel To: Andrew Lunn , Vivien Didelot , Florian Fainelli , Vladimir Oltean , "David S. Miller" , Jakub Kicinski , Russell King Cc: Oleksij Rempel , Pengutronix Kernel Team , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mips@vger.kernel.org Subject: [PATCH net-next v1 4/9] net: dsa: qca: ar9331: make proper initial port defaults Date: Sat, 3 Apr 2021 13:48:43 +0200 Message-Id: <20210403114848.30528-5-o.rempel@pengutronix.de> X-Mailer: git-send-email 2.29.2 In-Reply-To: <20210403114848.30528-1-o.rempel@pengutronix.de> References: <20210403114848.30528-1-o.rempel@pengutronix.de> MIME-Version: 1.0 X-SA-Exim-Connect-IP: 2001:67c:670:100:1d::7 X-SA-Exim-Mail-From: ore@pengutronix.de X-SA-Exim-Scanned: No (on metis.ext.pengutronix.de); SAEximRunCond expanded to false X-PTX-Original-Recipient: linux-mips@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-mips@vger.kernel.org Make sure that all external port are actually isolated from each other, so no packets are leaked. Signed-off-by: Oleksij Rempel --- drivers/net/dsa/qca/ar9331.c | 145 ++++++++++++++++++++++++++++++++++- 1 file changed, 143 insertions(+), 2 deletions(-) diff --git a/drivers/net/dsa/qca/ar9331.c b/drivers/net/dsa/qca/ar9331.c index 9a5035b2f0ff..a3de3598fbf5 100644 --- a/drivers/net/dsa/qca/ar9331.c +++ b/drivers/net/dsa/qca/ar9331.c @@ -60,10 +60,19 @@ #define AR9331_SW_REG_FLOOD_MASK 0x2c #define AR9331_SW_FLOOD_MASK_BROAD_TO_CPU BIT(26) +#define AR9331_SW_FLOOD_MASK_MULTI_FLOOD_DP GENMASK(20, 16) +#define AR9331_SW_FLOOD_MASK_UNI_FLOOD_DP GENMASK(4, 0) #define AR9331_SW_REG_GLOBAL_CTRL 0x30 #define AR9331_SW_GLOBAL_CTRL_MFS_M GENMASK(13, 0) +#define AR9331_SW_REG_ADDR_TABLE_CTRL 0x5c +#define AR9331_SW_AT_ARP_EN BIT(20) +#define AR9331_SW_AT_LEARN_CHANGE_EN BIT(18) +#define AR9331_SW_AT_AGE_EN BIT(17) +#define AR9331_SW_AT_AGE_TIME GENMASK(15, 0) +#define AR9331_SW_AT_AGE_TIME_COEF 6900 /* Not documented */ + #define AR9331_SW_REG_MDIO_CTRL 0x98 #define AR9331_SW_MDIO_CTRL_BUSY BIT(31) #define AR9331_SW_MDIO_CTRL_MASTER_EN BIT(30) @@ -101,6 +110,46 @@ AR9331_SW_PORT_STATUS_RX_FLOW_EN | AR9331_SW_PORT_STATUS_TX_FLOW_EN | \ AR9331_SW_PORT_STATUS_SPEED_M) +#define AR9331_SW_REG_PORT_CTRL(_port) (0x104 + (_port) * 0x100) +#define AR9331_SW_PORT_CTRL_ING_MIRROR_EN BIT(17) +#define AR9331_SW_PORT_CTRL_EG_MIRROR_EN BIT(16) +#define AR9331_SW_PORT_CTRL_DOUBLE_TAG_VLAN BIT(15) +#define AR9331_SW_PORT_CTRL_LEARN_EN BIT(14) +#define AR9331_SW_PORT_CTRL_SINGLE_VLAN_EN BIT(13) +#define AR9331_SW_PORT_CTRL_MAC_LOOP_BACK BIT(12) +#define AR9331_SW_PORT_CTRL_HEAD_EN BIT(11) +#define AR9331_SW_PORT_CTRL_IGMP_MLD_EN BIT(10) +#define AR9331_SW_PORT_CTRL_EG_VLAN_MODE GENMASK(9, 8) +#define AR9331_SW_PORT_CTRL_EG_VLAN_MODE_KEEP 0 +#define AR9331_SW_PORT_CTRL_EG_VLAN_MODE_STRIP 1 +#define AR9331_SW_PORT_CTRL_EG_VLAN_MODE_ADD 2 +#define AR9331_SW_PORT_CTRL_EG_VLAN_MODE_DOUBLE 3 +#define AR9331_SW_PORT_CTRL_LEARN_ONE_LOCK BIT(7) +#define AR9331_SW_PORT_CTRL_PORT_LOCK_EN BIT(6) +#define AR9331_SW_PORT_CTRL_LOCK_DROP_EN BIT(5) +#define AR9331_SW_PORT_CTRL_PORT_STATE GENMASK(2, 0) +#define AR9331_SW_PORT_CTRL_PORT_STATE_DISABLED 0 +#define AR9331_SW_PORT_CTRL_PORT_STATE_BLOCKING 1 +#define AR9331_SW_PORT_CTRL_PORT_STATE_LISTENING 2 +#define AR9331_SW_PORT_CTRL_PORT_STATE_LEARNING 3 +#define AR9331_SW_PORT_CTRL_PORT_STATE_FORWARD 4 + +#define AR9331_SW_REG_PORT_VLAN(_port) (0x108 + (_port) * 0x100) +#define AR9331_SW_PORT_VLAN_8021Q_MODE GENMASK(31, 30) +#define AR9331_SW_8021Q_MODE_SECURE 3 +#define AR9331_SW_8021Q_MODE_CHECK 2 +#define AR9331_SW_8021Q_MODE_FALLBACK 1 +#define AR9331_SW_8021Q_MODE_NONE 0 +#define AR9331_SW_PORT_VLAN_ING_PORT_PRI GENMASK(29, 27) +#define AR9331_SW_PORT_VLAN_FORCE_PORT_VLAN_EN BIT(26) +#define AR9331_SW_PORT_VLAN_PORT_VID_MEMBER GENMASK(25, 16) +#define AR9331_SW_PORT_VLAN_ARP_LEAKY_EN BIT(15) +#define AR9331_SW_PORT_VLAN_UNI_LEAKY_EN BIT(14) +#define AR9331_SW_PORT_VLAN_MULTI_LEAKY_EN BIT(13) +#define AR9331_SW_PORT_VLAN_FORCE_DEFALUT_VID_EN BIT(12) +#define AR9331_SW_PORT_VLAN_PORT_VID GENMASK(11, 0) +#define AR9331_SW_PORT_VLAN_PORT_VID_DEF 1 + /* MIB registers */ #define AR9331_MIB_COUNTER(x) (0x20000 + ((x) * 0x100)) @@ -229,6 +278,7 @@ struct ar9331_sw_priv { struct regmap *regmap; struct reset_control *sw_reset; struct ar9331_sw_port port[AR9331_SW_PORTS]; + int cpu_port; }; static struct ar9331_sw_priv *ar9331_sw_port_to_priv(struct ar9331_sw_port *port) @@ -371,12 +421,72 @@ static int ar9331_sw_mbus_init(struct ar9331_sw_priv *priv) return 0; } -static int ar9331_sw_setup(struct dsa_switch *ds) +static int ar9331_sw_setup_port(struct dsa_switch *ds, int port) { struct ar9331_sw_priv *priv = (struct ar9331_sw_priv *)ds->priv; struct regmap *regmap = priv->regmap; + u32 port_mask, port_ctrl, val; int ret; + /* Generate default port settings */ + port_ctrl = FIELD_PREP(AR9331_SW_PORT_CTRL_PORT_STATE, + AR9331_SW_PORT_CTRL_PORT_STATE_DISABLED); + + if (dsa_is_cpu_port(ds, port)) { + /* + * CPU port should be allowed to communicate with all user + * ports. + */ + //port_mask = dsa_user_ports(ds); + port_mask = 0; + /* + * Enable atheros header on CPU port. This will allow us + * communicate with each port separately + */ + port_ctrl |= AR9331_SW_PORT_CTRL_HEAD_EN; + port_ctrl |= AR9331_SW_PORT_CTRL_LEARN_EN; + } else if (dsa_is_user_port(ds, port)) { + /* + * User ports should communicate only with the CPU port. + */ + port_mask = BIT(priv->cpu_port); + /* Enable unicast address learning by default */ + port_ctrl |= AR9331_SW_PORT_CTRL_LEARN_EN + /* IGMP snooping seems to work correctly, let's use it */ + | AR9331_SW_PORT_CTRL_IGMP_MLD_EN + | AR9331_SW_PORT_CTRL_SINGLE_VLAN_EN; + } else { + /* Other ports do not need to communicate at all */ + port_mask = 0; + } + + val = FIELD_PREP(AR9331_SW_PORT_VLAN_8021Q_MODE, + AR9331_SW_8021Q_MODE_NONE) | + FIELD_PREP(AR9331_SW_PORT_VLAN_PORT_VID_MEMBER, port_mask) | + FIELD_PREP(AR9331_SW_PORT_VLAN_PORT_VID, + AR9331_SW_PORT_VLAN_PORT_VID_DEF); + + ret = regmap_write(regmap, AR9331_SW_REG_PORT_VLAN(port), val); + if (ret) + goto error; + + ret = regmap_write(regmap, AR9331_SW_REG_PORT_CTRL(port), port_ctrl); + if (ret) + goto error; + + return 0; +error: + dev_err_ratelimited(priv->dev, "%s: error: %i\n", __func__, ret); + + return ret; +} + +static int ar9331_sw_setup(struct dsa_switch *ds) +{ + struct ar9331_sw_priv *priv = (struct ar9331_sw_priv *)ds->priv; + struct regmap *regmap = priv->regmap; + int ret, i; + ret = ar9331_sw_reset(priv); if (ret) return ret; @@ -390,7 +500,8 @@ static int ar9331_sw_setup(struct dsa_switch *ds) /* Do not drop broadcast frames */ ret = regmap_write_bits(regmap, AR9331_SW_REG_FLOOD_MASK, - AR9331_SW_FLOOD_MASK_BROAD_TO_CPU, + AR9331_SW_FLOOD_MASK_BROAD_TO_CPU + | AR9331_SW_FLOOD_MASK_MULTI_FLOOD_DP, AR9331_SW_FLOOD_MASK_BROAD_TO_CPU); if (ret) goto error; @@ -402,6 +513,36 @@ static int ar9331_sw_setup(struct dsa_switch *ds) if (ret) goto error; + /* + * Configure the ARL: + * AR9331_SW_AT_ARP_EN - why? + * AR9331_SW_AT_LEARN_CHANGE_EN - why? + */ + ret = regmap_set_bits(regmap, AR9331_SW_REG_ADDR_TABLE_CTRL, + AR9331_SW_AT_ARP_EN | + AR9331_SW_AT_LEARN_CHANGE_EN); + if (ret) + goto error; + + /* find the CPU port */ + priv->cpu_port = -1; + for (i = 0; i < ds->num_ports; i++) { + if (!dsa_is_cpu_port(ds, i)) + continue; + + if (priv->cpu_port != -1) + dev_err_ratelimited(priv->dev, "%s: more then one CPU port. Already set: %i, trying to add: %i\n", + __func__, priv->cpu_port, i); + else + priv->cpu_port = i; + } + + for (i = 0; i < ds->num_ports; i++) { + ret = ar9331_sw_setup_port(ds, i); + if (ret) + goto error; + } + ds->configure_vlan_while_not_filtering = false; return 0; From patchwork Sat Apr 3 11:48:44 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Oleksij Rempel X-Patchwork-Id: 12181911 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=-16.7 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT autolearn=unavailable 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 4CFC5C43461 for ; Sat, 3 Apr 2021 11:50:15 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 1A3FA61243 for ; Sat, 3 Apr 2021 11:50:15 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236824AbhDCLuQ (ORCPT ); Sat, 3 Apr 2021 07:50:16 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:38608 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236758AbhDCLtO (ORCPT ); Sat, 3 Apr 2021 07:49:14 -0400 Received: from metis.ext.pengutronix.de (metis.ext.pengutronix.de [IPv6:2001:67c:670:201:290:27ff:fe1d:cc33]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 0DE0AC0617AB for ; Sat, 3 Apr 2021 04:49:10 -0700 (PDT) Received: from dude.hi.pengutronix.de ([2001:67c:670:100:1d::7]) by metis.ext.pengutronix.de with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1lSelp-0000eu-6z; Sat, 03 Apr 2021 13:48:53 +0200 Received: from ore by dude.hi.pengutronix.de with local (Exim 4.92) (envelope-from ) id 1lSell-0007y7-QX; Sat, 03 Apr 2021 13:48:49 +0200 From: Oleksij Rempel To: Andrew Lunn , Vivien Didelot , Florian Fainelli , Vladimir Oltean , "David S. Miller" , Jakub Kicinski , Russell King Cc: Oleksij Rempel , Pengutronix Kernel Team , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mips@vger.kernel.org Subject: [PATCH net-next v1 5/9] net: dsa: qca: ar9331: add forwarding database support Date: Sat, 3 Apr 2021 13:48:44 +0200 Message-Id: <20210403114848.30528-6-o.rempel@pengutronix.de> X-Mailer: git-send-email 2.29.2 In-Reply-To: <20210403114848.30528-1-o.rempel@pengutronix.de> References: <20210403114848.30528-1-o.rempel@pengutronix.de> MIME-Version: 1.0 X-SA-Exim-Connect-IP: 2001:67c:670:100:1d::7 X-SA-Exim-Mail-From: ore@pengutronix.de X-SA-Exim-Scanned: No (on metis.ext.pengutronix.de); SAEximRunCond expanded to false X-PTX-Original-Recipient: linux-mips@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-mips@vger.kernel.org This switch provides simple address resolution table, without VLAN or multicast specific information. With this patch we are able now to read, modify unicast and mulicast addresses. Signed-off-by: Oleksij Rempel --- drivers/net/dsa/qca/ar9331.c | 356 +++++++++++++++++++++++++++++++++++ 1 file changed, 356 insertions(+) diff --git a/drivers/net/dsa/qca/ar9331.c b/drivers/net/dsa/qca/ar9331.c index a3de3598fbf5..4a98f14f31f4 100644 --- a/drivers/net/dsa/qca/ar9331.c +++ b/drivers/net/dsa/qca/ar9331.c @@ -66,6 +66,47 @@ #define AR9331_SW_REG_GLOBAL_CTRL 0x30 #define AR9331_SW_GLOBAL_CTRL_MFS_M GENMASK(13, 0) +/* Size of the address resolution table (ARL) */ +#define AR9331_SW_NUM_ARL_RECORDS 1024 + +#define AR9331_SW_REG_ADDR_TABLE_FUNCTION0 0x50 +#define AR9331_SW_AT_ADDR_BYTES4 GENMASK(31, 24) +#define AR9331_SW_AT_ADDR_BYTES5 GENMASK(23, 16) +#define AR9331_SW_AT_FULL_VIO BIT(12) +#define AR9331_SW_AT_PORT_NUM GENMASK(11, 8) +#define AR9331_SW_AT_FLUSH_STATIC_EN BIT(4) +#define AR9331_SW_AT_BUSY BIT(3) +#define AR9331_SW_AT_FUNC GENMASK(2, 0) +#define AR9331_SW_AT_FUNC_NOP 0 +#define AR9331_SW_AT_FUNC_FLUSH_ALL 1 +#define AR9331_SW_AT_FUNC_LOAD_ENTRY 2 +#define AR9331_SW_AT_FUNC_PURGE_ENTRY 3 +#define AR9331_SW_AT_FUNC_FLUSH_ALL_UNLOCKED 4 +#define AR9331_SW_AT_FUNC_FLUSH_PORT 5 +#define AR9331_SW_AT_FUNC_GET_NEXT 6 +#define AR9331_SW_AT_FUNC_FIND_MAC 7 + +#define AR9331_SW_REG_ADDR_TABLE_FUNCTION1 0x54 +#define AR9331_SW_AT_ADDR_BYTES0 GENMASK(31, 24) +#define AR9331_SW_AT_ADDR_BYTES1 GENMASK(23, 16) +#define AR9331_SW_AT_ADDR_BYTES2 GENMASK(15, 8) +#define AR9331_SW_AT_ADDR_BYTES3 GENMASK(7, 0) + +#define AR9331_SW_REG_ADDR_TABLE_FUNCTION2 0x58 +#define AR9331_SW_AT_COPY_TO_CPU BIT(26) +#define AR9331_SW_AT_REDIRECT_TOCPU BIT(25) +#define AR9331_SW_AT_LEAKY_EN BIT(24) +#define AR9331_SW_AT_STATUS GENMASK(19, 16) +#define AR9331_SW_AT_STATUS_EMPTY 0 +/* STATUS values from 7 to 1 are different aging levels */ +#define AR9331_SW_AT_STATUS_STATIC 0xf + +#define AR9331_SW_AT_SA_DROP_EN BIT(14) +#define AR9331_SW_AT_MIRROR_EN BIT(13) +#define AR9331_SW_AT_PRIORITY_EN BIT(12) +#define AR9331_SW_AT_PRIORITY GENMASK(11, 10) +#define AR9331_SW_AT_DES_PORT GENMASK(5, 0) + #define AR9331_SW_REG_ADDR_TABLE_CTRL 0x5c #define AR9331_SW_AT_ARP_EN BIT(20) #define AR9331_SW_AT_LEARN_CHANGE_EN BIT(18) @@ -266,6 +307,12 @@ struct ar9331_sw_port { struct spinlock stats_lock; }; +struct ar9331_sw_fdb { + u8 port_mask; + u8 aging; + u8 mac[ETH_ALEN]; +}; + struct ar9331_sw_priv { struct device *dev; struct dsa_switch ds; @@ -765,6 +812,309 @@ static void ar9331_get_stats64(struct dsa_switch *ds, int port, spin_unlock(&p->stats_lock); } +static int ar9331_sw_fdb_wait(struct ar9331_sw_priv *priv, u32 *f0) +{ + struct regmap *regmap = priv->regmap; + + return regmap_read_poll_timeout(regmap, + AR9331_SW_REG_ADDR_TABLE_FUNCTION0, + *f0, !(*f0 & AR9331_SW_AT_BUSY), + 10, 2000); +} + +static int ar9331_sw_port_fdb_write(struct ar9331_sw_priv *priv, + u32 f0, u32 f1, u32 f2) +{ + struct regmap *regmap = priv->regmap; + int ret; + + ret = regmap_write(regmap, AR9331_SW_REG_ADDR_TABLE_FUNCTION2, f2); + if (ret) + return ret; + + ret = regmap_write(regmap, AR9331_SW_REG_ADDR_TABLE_FUNCTION1, f1); + if (ret) + return ret; + + return regmap_write(regmap, AR9331_SW_REG_ADDR_TABLE_FUNCTION0, f0); +} + +static int ar9331_sw_fdb_next(struct ar9331_sw_priv *priv, + struct ar9331_sw_fdb *fdb, int port) +{ + struct regmap *regmap = priv->regmap; + unsigned int status, ports; + u32 f0, f1, f2; + int ret; + + /* Keep AT_ADDR_BYTES4/5 to search next entry after current */ + ret = regmap_update_bits(regmap, AR9331_SW_REG_ADDR_TABLE_FUNCTION0, + AR9331_SW_AT_FUNC | AR9331_SW_AT_BUSY, + AR9331_SW_AT_BUSY | + FIELD_PREP(AR9331_SW_AT_FUNC, + AR9331_SW_AT_FUNC_GET_NEXT)); + if (ret) + return ret; + + ret = ar9331_sw_fdb_wait(priv, &f0); + if (ret) + return ret; + + ret = regmap_read(regmap, AR9331_SW_REG_ADDR_TABLE_FUNCTION2, &f2); + if (ret) + return ret; + + /* + * If the hardware returns an MAC != 0 and the AT_STATUS is zero, there + * is no next valid entry in the address table. + */ + status = FIELD_GET(AR9331_SW_AT_STATUS, f2); + fdb->aging = status; + if (!status) + return 0; + + ret = regmap_read(regmap, AR9331_SW_REG_ADDR_TABLE_FUNCTION1, &f1); + if (ret) + return ret; + + fdb->mac[0] = FIELD_GET(AR9331_SW_AT_ADDR_BYTES0, f1); + fdb->mac[1] = FIELD_GET(AR9331_SW_AT_ADDR_BYTES1, f1); + fdb->mac[2] = FIELD_GET(AR9331_SW_AT_ADDR_BYTES2, f1); + fdb->mac[3] = FIELD_GET(AR9331_SW_AT_ADDR_BYTES3, f1); + fdb->mac[4] = FIELD_GET(AR9331_SW_AT_ADDR_BYTES4, f0); + fdb->mac[5] = FIELD_GET(AR9331_SW_AT_ADDR_BYTES5, f0); + + ports = FIELD_GET(AR9331_SW_AT_DES_PORT, f2); + if (!(ports & BIT(port))) + return -EAGAIN; + + return 0; +} + +static void ar9331_sw_port_fdb_prepare(const unsigned char *mac, u32 *f0, + u32 *f1, unsigned int func) +{ + *f1 = FIELD_PREP(AR9331_SW_AT_ADDR_BYTES0, mac[0]) | + FIELD_PREP(AR9331_SW_AT_ADDR_BYTES1, mac[1]) | + FIELD_PREP(AR9331_SW_AT_ADDR_BYTES2, mac[2]) | + FIELD_PREP(AR9331_SW_AT_ADDR_BYTES3, mac[3]); + *f0 = FIELD_PREP(AR9331_SW_AT_ADDR_BYTES4, mac[4]) | + FIELD_PREP(AR9331_SW_AT_ADDR_BYTES5, mac[5]) | + FIELD_PREP(AR9331_SW_AT_FUNC, func) | AR9331_SW_AT_BUSY; +} + +static int ar9331_sw_port_fdb_dump(struct dsa_switch *ds, int port, + dsa_fdb_dump_cb_t *cb, void *data) +{ + struct ar9331_sw_priv *priv = (struct ar9331_sw_priv *)ds->priv; + int cnt = AR9331_SW_NUM_ARL_RECORDS; + struct ar9331_sw_fdb _fdb = { 0 }; + bool is_static; + int ret; + u32 f0; + + /* + * Make sure no pending operation is in progress. Since no timeout and + * interval values are documented, we use here "seems to be sane, works + * for me" values. + */ + ret = ar9331_sw_fdb_wait(priv, &f0); + if (ret) + return ret; + + /* + * If the address and the AT_STATUS are both zero, the hardware will + * search the first valid entry from entry0. + * If the address is set to zero and the AT_STATUS is not zero, the + * hardware will discover the next valid entry which has an address + * of 0x0. + */ + ret = ar9331_sw_port_fdb_write(priv, 0, 0, 0); + if (ret) + return ret; + + while (cnt--) { + ret = ar9331_sw_fdb_next(priv, &_fdb, port); + if (ret == -EAGAIN) + continue; + else if (ret) + return ret; + + if (!_fdb.aging) + break; + + is_static = (_fdb.aging == AR9331_SW_AT_STATUS_STATIC); + ret = cb(_fdb.mac, 0, is_static, data); + if (ret) + break; + } + + return ret; +} + +static int ar9331_sw_port_fdb_rmw(struct ar9331_sw_priv *priv, + const unsigned char *mac, + u8 port_mask_set, + u8 port_mask_clr) +{ + struct regmap *regmap = priv->regmap; + u32 f0, f1, f2; + u8 port_mask, port_mask_new, status, func; + int ret; + + ret = ar9331_sw_fdb_wait(priv, &f0); + if (ret) + return ret; + + ar9331_sw_port_fdb_prepare(mac, &f0, &f1, AR9331_SW_AT_FUNC_FIND_MAC); + + ret = ar9331_sw_port_fdb_write(priv, f0, f1, f2); + if (ret) + return ret; + + ret = ar9331_sw_fdb_wait(priv, &f0); + if (ret) + return ret; + + ret = regmap_read(regmap, AR9331_SW_REG_ADDR_TABLE_FUNCTION2, &f2); + if (ret) + return ret; + + port_mask = FIELD_GET(AR9331_SW_AT_DES_PORT, f2); + status = FIELD_GET(AR9331_SW_AT_STATUS, f2); + if (status > 0 && status < AR9331_SW_AT_STATUS_STATIC) { + dev_err_ratelimited(priv->dev, "%s: found existing dynamic entry on %x\n", + __func__, port_mask); + + if (port_mask_set && port_mask_set != port_mask) + dev_err_ratelimited(priv->dev, "%s: found existing dynamic entry on %x, replacing it with static on %x\n", + __func__, port_mask, port_mask_set); + port_mask = 0; + } else if (!status && !port_mask_set) { + return 0; + } + + port_mask_new = port_mask & ~port_mask_clr; + port_mask_new |= port_mask_set; + + if (port_mask_new == port_mask && + status == AR9331_SW_AT_STATUS_STATIC) { + dev_info(priv->dev, "%s: no need to overwrite existing valid entry on %x\n", + __func__, port_mask_new); + return 0; + } + + if (port_mask_new) { + func = AR9331_SW_AT_FUNC_LOAD_ENTRY; + } else { + func = AR9331_SW_AT_FUNC_PURGE_ENTRY; + port_mask_new = port_mask; + } + + f2 = FIELD_PREP(AR9331_SW_AT_DES_PORT, port_mask_new) | + FIELD_PREP(AR9331_SW_AT_STATUS, AR9331_SW_AT_STATUS_STATIC); + + ar9331_sw_port_fdb_prepare(mac, &f0, &f1, func); + + ret = ar9331_sw_port_fdb_write(priv, f0, f1, f2); + if (ret) + return ret; + + ret = ar9331_sw_fdb_wait(priv, &f0); + if (ret) + return ret; + + if (f0 & AR9331_SW_AT_FULL_VIO) { + /* cleanup error status */ + regmap_write(regmap, AR9331_SW_REG_ADDR_TABLE_FUNCTION0, 0); + dev_err_ratelimited(priv->dev, "%s: can't add new entry, ATU is full\n", __func__); + return -ENOMEM; + } + + return 0; +} + + + +static int ar9331_sw_port_fdb_add(struct dsa_switch *ds, int port, + const unsigned char *mac, u16 vid) +{ + struct ar9331_sw_priv *priv = (struct ar9331_sw_priv *)ds->priv; + u16 port_mask = BIT(port); + + dev_info(priv->dev, "%s(%pM, %x)\n", __func__, mac, port); + + if (vid) + return -EINVAL; + + return ar9331_sw_port_fdb_rmw(priv, mac, port_mask, 0); +} + +static int ar9331_sw_port_fdb_del(struct dsa_switch *ds, int port, + const unsigned char *mac, u16 vid) +{ + struct ar9331_sw_priv *priv = (struct ar9331_sw_priv *)ds->priv; + u16 port_mask = BIT(port); + + if (vid) + return -EINVAL; + + return ar9331_sw_port_fdb_rmw(priv, mac, 0, port_mask); +} + +static int ar9331_sw_port_mdb_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_mdb *mdb) +{ + struct ar9331_sw_priv *priv = (struct ar9331_sw_priv *)ds->priv; + u16 port_mask = BIT(port); + + if (mdb->vid) + return -EOPNOTSUPP; + + return ar9331_sw_port_fdb_rmw(priv, mdb->addr, port_mask, 0); +} + +static int ar9331_sw_port_mdb_del(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_mdb *mdb) +{ + struct ar9331_sw_priv *priv = (struct ar9331_sw_priv *)ds->priv; + u16 port_mask = BIT(port); + + if (mdb->vid) + return -EOPNOTSUPP; + + return ar9331_sw_port_fdb_rmw(priv, mdb->addr, 0, port_mask); +} + +static void ar9331_sw_port_fast_age(struct dsa_switch *ds, int port) +{ + struct ar9331_sw_priv *priv = (struct ar9331_sw_priv *)ds->priv; + struct regmap *regmap = priv->regmap; + int ret; + u32 f0; + + ret = ar9331_sw_fdb_wait(priv, &f0); + if (ret) + goto error; + + /* Flush all non static unicast address on a given port */ + f0 = FIELD_PREP(AR9331_SW_AT_PORT_NUM, port) | + FIELD_PREP(AR9331_SW_AT_FUNC, AR9331_SW_AT_FUNC_FLUSH_PORT) | + AR9331_SW_AT_BUSY; + + ret = regmap_write(regmap, AR9331_SW_REG_ADDR_TABLE_FUNCTION0, f0); + if (ret) + goto error; + + ret = ar9331_sw_fdb_wait(priv, &f0); + if (ret) + goto error; + + return; +error: + dev_err_ratelimited(priv->dev, "%s: error: %i\n", __func__, ret); +} + static const struct dsa_switch_ops ar9331_sw_ops = { .get_tag_protocol = ar9331_sw_get_tag_protocol, .setup = ar9331_sw_setup, @@ -774,6 +1124,12 @@ static const struct dsa_switch_ops ar9331_sw_ops = { .phylink_mac_link_down = ar9331_sw_phylink_mac_link_down, .phylink_mac_link_up = ar9331_sw_phylink_mac_link_up, .get_stats64 = ar9331_get_stats64, + .port_fast_age = ar9331_sw_port_fast_age, + .port_fdb_del = ar9331_sw_port_fdb_del, + .port_fdb_add = ar9331_sw_port_fdb_add, + .port_fdb_dump = ar9331_sw_port_fdb_dump, + .port_mdb_add = ar9331_sw_port_mdb_add, + .port_mdb_del = ar9331_sw_port_mdb_del, }; static irqreturn_t ar9331_sw_irq(int irq, void *data) From patchwork Sat Apr 3 11:48:45 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Oleksij Rempel X-Patchwork-Id: 12181907 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=-16.7 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT autolearn=unavailable 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 8F7F1C433ED for ; Sat, 3 Apr 2021 11:50:12 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 5C60561244 for ; Sat, 3 Apr 2021 11:50:12 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236777AbhDCLtQ (ORCPT ); Sat, 3 Apr 2021 07:49:16 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:38604 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236737AbhDCLtN (ORCPT ); Sat, 3 Apr 2021 07:49:13 -0400 Received: from metis.ext.pengutronix.de (metis.ext.pengutronix.de [IPv6:2001:67c:670:201:290:27ff:fe1d:cc33]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id DE0F7C06178C for ; Sat, 3 Apr 2021 04:49:09 -0700 (PDT) Received: from dude.hi.pengutronix.de ([2001:67c:670:100:1d::7]) by metis.ext.pengutronix.de with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1lSelp-0000ev-77; Sat, 03 Apr 2021 13:48:53 +0200 Received: from ore by dude.hi.pengutronix.de with local (Exim 4.92) (envelope-from ) id 1lSell-0007yG-RT; Sat, 03 Apr 2021 13:48:49 +0200 From: Oleksij Rempel To: Andrew Lunn , Vivien Didelot , Florian Fainelli , Vladimir Oltean , "David S. Miller" , Jakub Kicinski , Russell King Cc: Oleksij Rempel , Pengutronix Kernel Team , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mips@vger.kernel.org Subject: [PATCH net-next v1 6/9] net: dsa: qca: ar9331: add ageing time support Date: Sat, 3 Apr 2021 13:48:45 +0200 Message-Id: <20210403114848.30528-7-o.rempel@pengutronix.de> X-Mailer: git-send-email 2.29.2 In-Reply-To: <20210403114848.30528-1-o.rempel@pengutronix.de> References: <20210403114848.30528-1-o.rempel@pengutronix.de> MIME-Version: 1.0 X-SA-Exim-Connect-IP: 2001:67c:670:100:1d::7 X-SA-Exim-Mail-From: ore@pengutronix.de X-SA-Exim-Scanned: No (on metis.ext.pengutronix.de); SAEximRunCond expanded to false X-PTX-Original-Recipient: linux-mips@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-mips@vger.kernel.org This switch provides global ageing time configuration, so let DSA use it. Signed-off-by: Oleksij Rempel Reviewed-by: Andrew Lunn Reviewed-by: Florian Fainelli --- drivers/net/dsa/qca/ar9331.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/drivers/net/dsa/qca/ar9331.c b/drivers/net/dsa/qca/ar9331.c index 4a98f14f31f4..b2c22ba924f0 100644 --- a/drivers/net/dsa/qca/ar9331.c +++ b/drivers/net/dsa/qca/ar9331.c @@ -1115,6 +1115,25 @@ static void ar9331_sw_port_fast_age(struct dsa_switch *ds, int port) dev_err_ratelimited(priv->dev, "%s: error: %i\n", __func__, ret); } +static int ar9331_sw_set_ageing_time(struct dsa_switch *ds, + unsigned int ageing_time) +{ + struct ar9331_sw_priv *priv = (struct ar9331_sw_priv *)ds->priv; + struct regmap *regmap = priv->regmap; + u32 time, val; + + time = DIV_ROUND_UP(ageing_time, AR9331_SW_AT_AGE_TIME_COEF); + if (!time) + time = 1; + else if (time > U16_MAX) + time = U16_MAX; + + val = FIELD_PREP(AR9331_SW_AT_AGE_TIME, time) | AR9331_SW_AT_AGE_EN; + return regmap_update_bits(regmap, AR9331_SW_REG_ADDR_TABLE_CTRL, + AR9331_SW_AT_AGE_EN | AR9331_SW_AT_AGE_TIME, + val); +} + static const struct dsa_switch_ops ar9331_sw_ops = { .get_tag_protocol = ar9331_sw_get_tag_protocol, .setup = ar9331_sw_setup, @@ -1130,6 +1149,7 @@ static const struct dsa_switch_ops ar9331_sw_ops = { .port_fdb_dump = ar9331_sw_port_fdb_dump, .port_mdb_add = ar9331_sw_port_mdb_add, .port_mdb_del = ar9331_sw_port_mdb_del, + .set_ageing_time = ar9331_sw_set_ageing_time, }; static irqreturn_t ar9331_sw_irq(int irq, void *data) @@ -1476,6 +1496,8 @@ static int ar9331_sw_probe(struct mdio_device *mdiodev) priv->ops = ar9331_sw_ops; ds->ops = &priv->ops; dev_set_drvdata(&mdiodev->dev, priv); + ds->ageing_time_min = AR9331_SW_AT_AGE_TIME_COEF; + ds->ageing_time_max = AR9331_SW_AT_AGE_TIME_COEF * U16_MAX; for (i = 0; i < ARRAY_SIZE(priv->port); i++) { struct ar9331_sw_port *port = &priv->port[i]; From patchwork Sat Apr 3 11:48:46 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Oleksij Rempel X-Patchwork-Id: 12181909 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=-16.7 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT autolearn=unavailable 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 709A2C43462 for ; Sat, 3 Apr 2021 11:50:15 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 4979561245 for ; Sat, 3 Apr 2021 11:50:15 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236673AbhDCLuR (ORCPT ); Sat, 3 Apr 2021 07:50:17 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:38610 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236727AbhDCLtN (ORCPT ); Sat, 3 Apr 2021 07:49:13 -0400 Received: from metis.ext.pengutronix.de (metis.ext.pengutronix.de [IPv6:2001:67c:670:201:290:27ff:fe1d:cc33]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id EBFABC061794 for ; Sat, 3 Apr 2021 04:49:09 -0700 (PDT) Received: from dude.hi.pengutronix.de ([2001:67c:670:100:1d::7]) by metis.ext.pengutronix.de with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1lSelp-0000ew-7A; Sat, 03 Apr 2021 13:48:53 +0200 Received: from ore by dude.hi.pengutronix.de with local (Exim 4.92) (envelope-from ) id 1lSell-0007yP-SO; Sat, 03 Apr 2021 13:48:49 +0200 From: Oleksij Rempel To: Andrew Lunn , Vivien Didelot , Florian Fainelli , Vladimir Oltean , "David S. Miller" , Jakub Kicinski , Russell King Cc: Oleksij Rempel , Pengutronix Kernel Team , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mips@vger.kernel.org Subject: [PATCH net-next v1 7/9] net: dsa: qca: ar9331: add bridge support Date: Sat, 3 Apr 2021 13:48:46 +0200 Message-Id: <20210403114848.30528-8-o.rempel@pengutronix.de> X-Mailer: git-send-email 2.29.2 In-Reply-To: <20210403114848.30528-1-o.rempel@pengutronix.de> References: <20210403114848.30528-1-o.rempel@pengutronix.de> MIME-Version: 1.0 X-SA-Exim-Connect-IP: 2001:67c:670:100:1d::7 X-SA-Exim-Mail-From: ore@pengutronix.de X-SA-Exim-Scanned: No (on metis.ext.pengutronix.de); SAEximRunCond expanded to false X-PTX-Original-Recipient: linux-mips@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-mips@vger.kernel.org This switch is providing forwarding matrix, with it we can configure individual bridges. Potentially we can configure more then one not VLAN based bridge on this HW. Signed-off-by: Oleksij Rempel Reviewed-by: Florian Fainelli --- drivers/net/dsa/qca/ar9331.c | 73 ++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/drivers/net/dsa/qca/ar9331.c b/drivers/net/dsa/qca/ar9331.c index b2c22ba924f0..bf9588574205 100644 --- a/drivers/net/dsa/qca/ar9331.c +++ b/drivers/net/dsa/qca/ar9331.c @@ -40,6 +40,7 @@ */ #include +#include #include #include #include @@ -1134,6 +1135,76 @@ static int ar9331_sw_set_ageing_time(struct dsa_switch *ds, val); } +static int ar9331_sw_port_bridge_join(struct dsa_switch *ds, int port, + struct net_device *br) +{ + struct ar9331_sw_priv *priv = (struct ar9331_sw_priv *)ds->priv; + struct regmap *regmap = priv->regmap; + int port_mask = BIT(priv->cpu_port); + int i, ret; + u32 val; + + for (i = 0; i < ds->num_ports; i++) { + if (dsa_to_port(ds, i)->bridge_dev != br) + continue; + + if (!dsa_is_user_port(ds, port)) + continue; + + val = FIELD_PREP(AR9331_SW_PORT_VLAN_PORT_VID_MEMBER, BIT(port)); + ret = regmap_set_bits(regmap, AR9331_SW_REG_PORT_VLAN(i), val); + if (ret) + goto error; + + if (i != port) + port_mask |= BIT(i); + } + + val = FIELD_PREP(AR9331_SW_PORT_VLAN_PORT_VID_MEMBER, port_mask); + ret = regmap_update_bits(regmap, AR9331_SW_REG_PORT_VLAN(port), + AR9331_SW_PORT_VLAN_PORT_VID_MEMBER, val); + if (ret) + goto error; + + return 0; +error: + dev_err_ratelimited(priv->dev, "%s: error: %i\n", __func__, ret); + + return ret; +} + +static void ar9331_sw_port_bridge_leave(struct dsa_switch *ds, int port, + struct net_device *br) +{ + struct ar9331_sw_priv *priv = (struct ar9331_sw_priv *)ds->priv; + struct regmap *regmap = priv->regmap; + int i, ret; + u32 val; + + for (i = 0; i < ds->num_ports; i++) { + if (dsa_to_port(ds, i)->bridge_dev != br) + continue; + + if (!dsa_is_user_port(ds, port)) + continue; + + val = FIELD_PREP(AR9331_SW_PORT_VLAN_PORT_VID_MEMBER, BIT(port)); + ret = regmap_clear_bits(regmap, AR9331_SW_REG_PORT_VLAN(i), val); + if (ret) + goto error; + } + + val = FIELD_PREP(AR9331_SW_PORT_VLAN_PORT_VID_MEMBER, BIT(priv->cpu_port)); + ret = regmap_update_bits(regmap, AR9331_SW_REG_PORT_VLAN(port), + AR9331_SW_PORT_VLAN_PORT_VID_MEMBER, val); + if (ret) + goto error; + + return; +error: + dev_err_ratelimited(priv->dev, "%s: error: %i\n", __func__, ret); +} + static const struct dsa_switch_ops ar9331_sw_ops = { .get_tag_protocol = ar9331_sw_get_tag_protocol, .setup = ar9331_sw_setup, @@ -1150,6 +1221,8 @@ static const struct dsa_switch_ops ar9331_sw_ops = { .port_mdb_add = ar9331_sw_port_mdb_add, .port_mdb_del = ar9331_sw_port_mdb_del, .set_ageing_time = ar9331_sw_set_ageing_time, + .port_bridge_join = ar9331_sw_port_bridge_join, + .port_bridge_leave = ar9331_sw_port_bridge_leave, }; static irqreturn_t ar9331_sw_irq(int irq, void *data) From patchwork Sat Apr 3 11:48:47 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Oleksij Rempel X-Patchwork-Id: 12181903 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=-16.7 required=3.0 tests=BAYES_00, 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 88C92C43460 for ; Sat, 3 Apr 2021 11:49:15 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 6645C61243 for ; Sat, 3 Apr 2021 11:49:14 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236782AbhDCLtP (ORCPT ); Sat, 3 Apr 2021 07:49:15 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:38612 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236732AbhDCLtN (ORCPT ); Sat, 3 Apr 2021 07:49:13 -0400 Received: from metis.ext.pengutronix.de (metis.ext.pengutronix.de [IPv6:2001:67c:670:201:290:27ff:fe1d:cc33]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id EE0D9C0617A7 for ; Sat, 3 Apr 2021 04:49:09 -0700 (PDT) Received: from dude.hi.pengutronix.de ([2001:67c:670:100:1d::7]) by metis.ext.pengutronix.de with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1lSelp-0000ex-76; Sat, 03 Apr 2021 13:48:53 +0200 Received: from ore by dude.hi.pengutronix.de with local (Exim 4.92) (envelope-from ) id 1lSell-0007yY-TJ; Sat, 03 Apr 2021 13:48:49 +0200 From: Oleksij Rempel To: Andrew Lunn , Vivien Didelot , Florian Fainelli , Vladimir Oltean , "David S. Miller" , Jakub Kicinski , Russell King Cc: Oleksij Rempel , Pengutronix Kernel Team , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mips@vger.kernel.org Subject: [PATCH net-next v1 8/9] net: dsa: qca: ar9331: add STP support Date: Sat, 3 Apr 2021 13:48:47 +0200 Message-Id: <20210403114848.30528-9-o.rempel@pengutronix.de> X-Mailer: git-send-email 2.29.2 In-Reply-To: <20210403114848.30528-1-o.rempel@pengutronix.de> References: <20210403114848.30528-1-o.rempel@pengutronix.de> MIME-Version: 1.0 X-SA-Exim-Connect-IP: 2001:67c:670:100:1d::7 X-SA-Exim-Mail-From: ore@pengutronix.de X-SA-Exim-Scanned: No (on metis.ext.pengutronix.de); SAEximRunCond expanded to false X-PTX-Original-Recipient: linux-mips@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-mips@vger.kernel.org According to the datasheet, this switch has configurable STP port states. Suddenly LISTENING and BLOCKING states didn't forwarded packets to the CPU and linux bridge continuously re enabled ports even if a loop was detected. To make it work, I reused bridge functionality to isolate port in LISTENING and BLOCKING states. Signed-off-by: Oleksij Rempel --- drivers/net/dsa/qca/ar9331.c | 69 ++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/drivers/net/dsa/qca/ar9331.c b/drivers/net/dsa/qca/ar9331.c index bf9588574205..83b59e771a5f 100644 --- a/drivers/net/dsa/qca/ar9331.c +++ b/drivers/net/dsa/qca/ar9331.c @@ -327,6 +327,7 @@ struct ar9331_sw_priv { struct reset_control *sw_reset; struct ar9331_sw_port port[AR9331_SW_PORTS]; int cpu_port; + u32 isolated_ports; }; static struct ar9331_sw_priv *ar9331_sw_port_to_priv(struct ar9331_sw_port *port) @@ -1151,6 +1152,10 @@ static int ar9331_sw_port_bridge_join(struct dsa_switch *ds, int port, if (!dsa_is_user_port(ds, port)) continue; + /* part of the bridge but should be isolated for now */ + if (priv->isolated_ports & BIT(i)) + continue; + val = FIELD_PREP(AR9331_SW_PORT_VLAN_PORT_VID_MEMBER, BIT(port)); ret = regmap_set_bits(regmap, AR9331_SW_REG_PORT_VLAN(i), val); if (ret) @@ -1205,6 +1210,69 @@ static void ar9331_sw_port_bridge_leave(struct dsa_switch *ds, int port, dev_err_ratelimited(priv->dev, "%s: error: %i\n", __func__, ret); } +static void ar9331_sw_port_stp_state_set(struct dsa_switch *ds, int port, + u8 state) +{ + struct ar9331_sw_priv *priv = (struct ar9331_sw_priv *)ds->priv; + struct net_device *br = dsa_to_port(ds, port)->bridge_dev; + struct regmap *regmap = priv->regmap; + u32 port_ctrl = 0, port_state = 0; + bool join = false; + int ret; + + /* + * STP hw support is buggy or I didn't understood it. So, it seems to + * be easier to make hand crafted implementation by using bridge + * functionality. Similar implementation can be found on ksz9477 switch + * and may be we need some generic code to so for all related devices + */ + switch (state) { + case BR_STATE_FORWARDING: + join = true; + fallthrough; + case BR_STATE_LEARNING: + port_ctrl = AR9331_SW_PORT_CTRL_LEARN_EN; + fallthrough; + case BR_STATE_LISTENING: + case BR_STATE_BLOCKING: + port_state = AR9331_SW_PORT_CTRL_PORT_STATE_FORWARD; + break; + case BR_STATE_DISABLED: + default: + port_state = AR9331_SW_PORT_CTRL_PORT_STATE_DISABLED; + break; + } + + port_ctrl |= FIELD_PREP(AR9331_SW_PORT_CTRL_PORT_STATE, port_state); + + ret = regmap_update_bits(regmap, AR9331_SW_REG_PORT_CTRL(port), + AR9331_SW_PORT_CTRL_LEARN_EN | + AR9331_SW_PORT_CTRL_PORT_STATE, port_ctrl); + if (ret) + goto error; + + if (!dsa_is_user_port(ds, port)) + return; + + /* + * here we care only about user ports. CPU port do not need this + * configuration + */ + if (join) { + priv->isolated_ports &= ~BIT(port); + if (br) + ar9331_sw_port_bridge_join(ds, port, br); + } else { + priv->isolated_ports |= BIT(port); + if (br) + ar9331_sw_port_bridge_leave(ds, port, br); + } + + return; +error: + dev_err_ratelimited(priv->dev, "%s: error: %i\n", __func__, ret); +} + static const struct dsa_switch_ops ar9331_sw_ops = { .get_tag_protocol = ar9331_sw_get_tag_protocol, .setup = ar9331_sw_setup, @@ -1223,6 +1291,7 @@ static const struct dsa_switch_ops ar9331_sw_ops = { .set_ageing_time = ar9331_sw_set_ageing_time, .port_bridge_join = ar9331_sw_port_bridge_join, .port_bridge_leave = ar9331_sw_port_bridge_leave, + .port_stp_state_set = ar9331_sw_port_stp_state_set, }; static irqreturn_t ar9331_sw_irq(int irq, void *data) From patchwork Sat Apr 3 11:48:48 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Oleksij Rempel X-Patchwork-Id: 12181895 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=-16.7 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT autolearn=unavailable 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 889AEC433ED for ; Sat, 3 Apr 2021 11:49:12 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 59D2B61243 for ; Sat, 3 Apr 2021 11:49:12 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236744AbhDCLtN (ORCPT ); Sat, 3 Apr 2021 07:49:13 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:38598 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236716AbhDCLtM (ORCPT ); Sat, 3 Apr 2021 07:49:12 -0400 Received: from metis.ext.pengutronix.de (metis.ext.pengutronix.de [IPv6:2001:67c:670:201:290:27ff:fe1d:cc33]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 972EFC0613E6 for ; Sat, 3 Apr 2021 04:49:09 -0700 (PDT) Received: from dude.hi.pengutronix.de ([2001:67c:670:100:1d::7]) by metis.ext.pengutronix.de with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1lSelp-0000ey-7B; Sat, 03 Apr 2021 13:48:53 +0200 Received: from ore by dude.hi.pengutronix.de with local (Exim 4.92) (envelope-from ) id 1lSell-0007yg-UG; Sat, 03 Apr 2021 13:48:49 +0200 From: Oleksij Rempel To: Andrew Lunn , Vivien Didelot , Florian Fainelli , Vladimir Oltean , "David S. Miller" , Jakub Kicinski , Russell King Cc: Oleksij Rempel , Pengutronix Kernel Team , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mips@vger.kernel.org Subject: [PATCH net-next v1 9/9] net: dsa: qca: ar9331: add vlan support Date: Sat, 3 Apr 2021 13:48:48 +0200 Message-Id: <20210403114848.30528-10-o.rempel@pengutronix.de> X-Mailer: git-send-email 2.29.2 In-Reply-To: <20210403114848.30528-1-o.rempel@pengutronix.de> References: <20210403114848.30528-1-o.rempel@pengutronix.de> MIME-Version: 1.0 X-SA-Exim-Connect-IP: 2001:67c:670:100:1d::7 X-SA-Exim-Mail-From: ore@pengutronix.de X-SA-Exim-Scanned: No (on metis.ext.pengutronix.de); SAEximRunCond expanded to false X-PTX-Original-Recipient: linux-mips@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-mips@vger.kernel.org This switch provides simple VLAN resolution database for 16 entries (VLANs). With this database we can cover typical functionalities as port based VLANs, untagged and tagged egress. Port based ingress filtering. The VLAN database is working on top of forwarding database. So, potentially, we can have multiple VLANs on top of multiple bridges. Hawing one VLAN on top of multiple bridges will fail on different levels, most probably DSA framework should warn if some one wont to make something likes this. Signed-off-by: Oleksij Rempel --- drivers/net/dsa/qca/ar9331.c | 255 +++++++++++++++++++++++++++++++++++ 1 file changed, 255 insertions(+) diff --git a/drivers/net/dsa/qca/ar9331.c b/drivers/net/dsa/qca/ar9331.c index 83b59e771a5f..40062388d4a7 100644 --- a/drivers/net/dsa/qca/ar9331.c +++ b/drivers/net/dsa/qca/ar9331.c @@ -67,6 +67,27 @@ #define AR9331_SW_REG_GLOBAL_CTRL 0x30 #define AR9331_SW_GLOBAL_CTRL_MFS_M GENMASK(13, 0) +#define AR9331_SW_NUM_VLAN_RECORDS 16 + +#define AR9331_SW_REG_VLAN_TABLE_FUNCTION0 0x40 +#define AR9331_SW_VT0_PRI_EN BIT(31) +#define AR9331_SW_VT0_PRI GENMASK(30, 28) +#define AR9331_SW_VT0_VID GENMASK(27, 16) +#define AR9331_SW_VT0_PORT_NUM GENMASK(11, 8) +#define AR9331_SW_VT0_FULL_VIO BIT(4) +#define AR9331_SW_VT0_BUSY BIT(3) +#define AR9331_SW_VT0_FUNC GENMASK(2, 0) +#define AR9331_SW_VT0_FUNC_NOP 0 +#define AR9331_SW_VT0_FUNC_FLUSH_ALL 1 +#define AR9331_SW_VT0_FUNC_LOAD_ENTRY 2 +#define AR9331_SW_VT0_FUNC_PURGE_ENTRY 3 +#define AR9331_SW_VT0_FUNC_DEL_PORT 4 +#define AR9331_SW_VT0_FUNC_GET_NEXT 5 + +#define AR9331_SW_REG_VLAN_TABLE_FUNCTION1 0x44 +#define AR9331_SW_VT1_VALID BIT(11) +#define AR9331_SW_VT1_VID_MEM GENMASK(9, 0) + /* Size of the address resolution table (ARL) */ #define AR9331_SW_NUM_ARL_RECORDS 1024 @@ -308,6 +329,11 @@ struct ar9331_sw_port { struct spinlock stats_lock; }; +struct ar9331_sw_vlan_db { + u16 vid; + u8 port_mask; +}; + struct ar9331_sw_fdb { u8 port_mask; u8 aging; @@ -328,6 +354,7 @@ struct ar9331_sw_priv { struct ar9331_sw_port port[AR9331_SW_PORTS]; int cpu_port; u32 isolated_ports; + struct ar9331_sw_vlan_db vdb[AR9331_SW_NUM_VLAN_RECORDS]; }; static struct ar9331_sw_priv *ar9331_sw_port_to_priv(struct ar9331_sw_port *port) @@ -1273,6 +1300,231 @@ static void ar9331_sw_port_stp_state_set(struct dsa_switch *ds, int port, dev_err_ratelimited(priv->dev, "%s: error: %i\n", __func__, ret); } +static int ar9331_port_vlan_filtering(struct dsa_switch *ds, int port, + bool vlan_filtering, + struct netlink_ext_ack *extack) +{ + struct ar9331_sw_priv *priv = (struct ar9331_sw_priv *)ds->priv; + struct regmap *regmap = priv->regmap; + u32 mode; + int ret; + + if (vlan_filtering) + mode = AR9331_SW_8021Q_MODE_SECURE; + else + mode = AR9331_SW_8021Q_MODE_NONE; + + ret = regmap_update_bits(regmap, AR9331_SW_REG_PORT_VLAN(port), + AR9331_SW_PORT_VLAN_8021Q_MODE, + FIELD_PREP(AR9331_SW_PORT_VLAN_8021Q_MODE, + mode)); + if (ret) + dev_err_ratelimited(priv->dev, "%s: error: %pe\n", + __func__, ERR_PTR(ret)); + + return ret; +} + +static int ar9331_sw_vt_wait(struct ar9331_sw_priv *priv, u32 *f0) +{ + struct regmap *regmap = priv->regmap; + + return regmap_read_poll_timeout(regmap, + AR9331_SW_REG_VLAN_TABLE_FUNCTION0, + *f0, !(*f0 & AR9331_SW_VT0_BUSY), + 100, 2000); +} + +static int ar9331_sw_port_vt_rmw(struct ar9331_sw_priv *priv, u16 vid, + u8 port_mask_set, u8 port_mask_clr) +{ + struct regmap *regmap = priv->regmap; + u32 f0, f1, port_mask = 0, port_mask_new, func; + struct ar9331_sw_vlan_db *vdb = NULL; + int ret, i; + + if (!vid) + return 0; + + ret = ar9331_sw_vt_wait(priv, &f0); + if (ret) + return ret; + + ret = regmap_write(regmap, AR9331_SW_REG_VLAN_TABLE_FUNCTION0, 0); + if (ret) + goto error; + + ret = regmap_write(regmap, AR9331_SW_REG_VLAN_TABLE_FUNCTION1, 0); + if (ret) + goto error; + + for (i = 0; i < ARRAY_SIZE(priv->vdb); i++) { + if (priv->vdb[i].vid == vid) { + vdb = &priv->vdb[i]; + break; + } + } + + ret = regmap_read(regmap, AR9331_SW_REG_VLAN_TABLE_FUNCTION1, &f1); + if (ret) + return ret; + + if (vdb) { + port_mask = vdb->port_mask; + } + + port_mask_new = port_mask & ~port_mask_clr; + port_mask_new |= port_mask_set; + + if (port_mask_new && port_mask_new == port_mask) { + dev_info(priv->dev, "%s: no need to overwrite existing valid entry on %x\n", + __func__, port_mask_new); + return 0; + } + + if (port_mask_new) { + func = AR9331_SW_VT0_FUNC_LOAD_ENTRY; + } else { + func = AR9331_SW_VT0_FUNC_PURGE_ENTRY; + port_mask_new = port_mask; + } + + if (vdb) { + vdb->port_mask = port_mask_new; + + if (!port_mask_new) + vdb->vid = 0; + } else { + for (i = 0; i < ARRAY_SIZE(priv->vdb); i++) { + if (!priv->vdb[i].vid) { + vdb = &priv->vdb[i]; + break; + } + } + + if (!vdb) { + dev_err_ratelimited(priv->dev, "Local VDB is full\n"); + return -ENOMEM; + } + vdb->vid = vid; + vdb->port_mask = port_mask_new; + } + + f0 = FIELD_PREP(AR9331_SW_VT0_VID, vid) | + FIELD_PREP(AR9331_SW_VT0_FUNC, func) | + AR9331_SW_VT0_BUSY; + f1 = FIELD_PREP(AR9331_SW_VT1_VID_MEM, port_mask_new) | + AR9331_SW_VT1_VALID; + + ret = regmap_write(regmap, AR9331_SW_REG_VLAN_TABLE_FUNCTION1, f1); + if (ret) + return ret; + + ret = regmap_write(regmap, AR9331_SW_REG_VLAN_TABLE_FUNCTION0, f0); + if (ret) + return ret; + + ret = ar9331_sw_vt_wait(priv, &f0); + if (ret) + return ret; + + if (f0 & AR9331_SW_VT0_FULL_VIO) { + /* cleanup error status */ + regmap_write(regmap, AR9331_SW_REG_VLAN_TABLE_FUNCTION0, 0); + dev_err_ratelimited(priv->dev, "%s: can't add new entry, VT is full\n", __func__); + return -ENOMEM; + } + + return 0; + +error: + dev_err_ratelimited(priv->dev, "%s: error: %pe\n", __func__, + ERR_PTR(ret)); + + return ret; +} + +static int ar9331_port_vlan_set_pvid(struct ar9331_sw_priv *priv, int port, + u16 pvid) +{ + struct regmap *regmap = priv->regmap; + int ret; + u32 mask, val; + + mask = AR9331_SW_PORT_VLAN_8021Q_MODE | + AR9331_SW_PORT_VLAN_FORCE_DEFALUT_VID_EN | + AR9331_SW_PORT_VLAN_FORCE_PORT_VLAN_EN; + val = AR9331_SW_PORT_VLAN_FORCE_DEFALUT_VID_EN | + AR9331_SW_PORT_VLAN_FORCE_PORT_VLAN_EN | + FIELD_PREP(AR9331_SW_PORT_VLAN_8021Q_MODE, + AR9331_SW_8021Q_MODE_FALLBACK); + + ret = regmap_update_bits(regmap, AR9331_SW_REG_PORT_VLAN(port), + mask, val); + if (ret) + return ret; + + return regmap_update_bits(regmap, AR9331_SW_REG_PORT_VLAN(port), + AR9331_SW_PORT_VLAN_PORT_VID, + FIELD_PREP(AR9331_SW_PORT_VLAN_PORT_VID, + pvid)); +} + +static int ar9331_port_vlan_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan, + struct netlink_ext_ack *extack) +{ + struct ar9331_sw_priv *priv = (struct ar9331_sw_priv *)ds->priv; + struct regmap *regmap = priv->regmap; + int ret, mode; + + ret = ar9331_sw_port_vt_rmw(priv, vlan->vid, BIT(port), 0); + if (ret) + goto error; + + if (vlan->flags & BRIDGE_VLAN_INFO_PVID) + ret = ar9331_port_vlan_set_pvid(priv, port, vlan->vid); + + if (ret) + goto error; + + if (vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED) + mode = AR9331_SW_PORT_CTRL_EG_VLAN_MODE_STRIP; + else + mode = AR9331_SW_PORT_CTRL_EG_VLAN_MODE_ADD; + + ret = regmap_update_bits(regmap, AR9331_SW_REG_PORT_CTRL(port), + AR9331_SW_PORT_CTRL_EG_VLAN_MODE, mode); + if (ret) + goto error; + + return 0; +error: + dev_err_ratelimited(priv->dev, "%s: error: %pe\n", __func__, + ERR_PTR(ret)); + + return ret; +} + +static int ar9331_port_vlan_del(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan) +{ + struct ar9331_sw_priv *priv = (struct ar9331_sw_priv *)ds->priv; + int ret; + + ret = ar9331_sw_port_vt_rmw(priv, vlan->vid, 0, BIT(port)); + if (ret) + goto error; + + return 0; + +error: + dev_err_ratelimited(priv->dev, "%s: error: %pe\n", __func__, + ERR_PTR(ret)); + + return ret; +} + static const struct dsa_switch_ops ar9331_sw_ops = { .get_tag_protocol = ar9331_sw_get_tag_protocol, .setup = ar9331_sw_setup, @@ -1292,6 +1544,9 @@ static const struct dsa_switch_ops ar9331_sw_ops = { .port_bridge_join = ar9331_sw_port_bridge_join, .port_bridge_leave = ar9331_sw_port_bridge_leave, .port_stp_state_set = ar9331_sw_port_stp_state_set, + .port_vlan_filtering = ar9331_port_vlan_filtering, + .port_vlan_add = ar9331_port_vlan_add, + .port_vlan_del = ar9331_port_vlan_del, }; static irqreturn_t ar9331_sw_irq(int irq, void *data)