From nobody Wed Apr 1 08:15:25 2026 Received: from mail-vk1-f182.google.com (mail-vk1-f182.google.com [209.85.221.182]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 98D0C421EED for ; Tue, 31 Mar 2026 23:00:18 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.182 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774998019; cv=none; b=TQHfilRAq0Xfm446CLSdxUXpWzADQKyV4vmIOAQkIN8oTcL4iV+Kjp3QmrDBTYfA4sTTMqbKQyWgK658BqlLyeT3uJ5nYxg4HHKdg5tqJiNtbgaFZl7cBSua/VapNeCnZcfB0oDpy0+tc1r9Hiyx7aHJodVLiAomX9aCFvHJGT8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774998019; c=relaxed/simple; bh=uH+S+6nZMoETKffQbWnB0xVUO8zOVAC/LAftpPnzPcc=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=YtkKHVK1QHUYzpmx7oB6LxnX6tleRhunstXMrMyh+TR4zkfMQAh88kzGuFkhdDO5AVtv0YEumR2FGvb/mSui2VwAMm9hpgVk9juiQcTD5SRKzcUzjiyyk/sISx+tVoDQKrJa0vbjd5Bylb2a0Bzh84TlfbRLQHt6ISsF8sDbzW4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=CFWLqiUH; arc=none smtp.client-ip=209.85.221.182 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="CFWLqiUH" Received: by mail-vk1-f182.google.com with SMTP id 71dfb90a1353d-56739adfa1aso5048509e0c.0 for ; Tue, 31 Mar 2026 16:00:18 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1774998017; x=1775602817; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=vyMo3bZqBoLxHWSj1zUWxXXoAGw/imiy/MTfskhCFgQ=; b=CFWLqiUHa5jzuCVT368EtwA+4h7XKMcCawNmXidbmDYf8N8mLAzUbeyRMZOhVMY3Jo 8Dk4M8MmCPP/gzn/vB7p1fHbPq5UAPrOQcSO5mH5wOr93arTG0qHIFjRs4n62GtOQ+A9 wj2XqLsgEL/UbgZPRXmwQSdoIb9vwCb/hXyd63vhMgfnvufO0HYCPgv7oCcseohS5RxE JuvPKptc4hdYgULoyGh/MaiTRK8639aHiyfCINLEkrtZ3912YkVMz7YNMDZQkTLPQJvh ek8Iwl+/dS0JhwdiZhCsvQwM+beWz0sXzRM0Oo/EA8s3UP8oYxjf1MCjQY5fHZCw3Cz/ sOXA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1774998017; x=1775602817; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=vyMo3bZqBoLxHWSj1zUWxXXoAGw/imiy/MTfskhCFgQ=; b=nuwUctzNhG5iz1Zqu5gMt+9+ZfSHfxFiH8L+HecOht80JgnnQW5Px6Pk9EMf3dnN2j 1txP5uy9P2V2oSfYSW9xbxaOezhQ3Pq3wEnQ5AaT8yseo5iVJ+5LsaAIife/qwK2OGNN 34o1bsU/bsNYnfmVASY//hH9JSuhqtyISfO/xtAC2CRp3TuxnYh11uVkcafwCEkSZdta pyAxHMNr9vMiS+o7uvcksTxB2S9G9XYlFzxFUcC8AydUu51YWYbWzbNXBOT4l+qXlQub Px5PcdXo1WEJf7+sbejuIIPVX85u/vKofC6RODGJCoLlfJ1FkjLPlKZk+pDl+pSZwojt UdeA== X-Forwarded-Encrypted: i=1; AJvYcCVroXK8RUDkqsz45lTtkjuWFO0d3a/VvXbQLx0XoANuyiV3JXmR6jXHZ0HUQy0BbEtWmahRwQWc21rzKRI=@vger.kernel.org X-Gm-Message-State: AOJu0YwmofAcXM1c0/zWrMFJD34TGs220pV/qq+/2+kGVYdON8FE8ErK tR5j8h2ZJIuEqcBUo8SA5ZQxlxGU9e8mBfFa4cg1obEb5GQXqcEyGiiu X-Gm-Gg: ATEYQzyxQ1MH7txGlPYhvsiG0IEC0yrMZplryFGdQIisq8s4ejSeSLbOYcRcXv6MwVc CCdXyyqIJ+5meh+GmiCnqmi6NeqxXlP2rfgKrvXbIaEj6qatYgAXbHK8jIkmbROv+KVl8bdcnbO ZQ4ZYtWyBMbm/XO4FhXSd+LEwGoPf1MeApl7mWGIKA5rmt9NySlpswfS6rJPnotmwhCusiMIPQH O+OQFPQDQ7Q9NzeoKiBKd2hdn/RKRHsLmwwT/TASrkZKLlTvuGVG3SpCqNE7PxKIiIvG0LNTYNx EtJ0ZHcwuEqOWwws13AFnMKkCYllIv/7zayGB1L+NqtrxBTKpyG8eonQqvhrx0F3Aa/s1B5vkcm YZMeUgvN52MACfN5S479DDdVvgFIyimB9xlTiGKKycYbJPdgJpNxOoj/3VzXOWCrP6TNU/VKtqE WgX8aAxd5p8xQA56MyxG3OO6plnNVjFOJ1X+gKPg== X-Received: by 2002:a05:6122:4f8a:b0:56b:6adf:150b with SMTP id 71dfb90a1353d-56d8a94a490mr860276e0c.13.1774998017543; Tue, 31 Mar 2026 16:00:17 -0700 (PDT) Received: from tresc054937.tre-sc.gov.br ([187.65.210.13]) by smtp.gmail.com with ESMTPSA id 71dfb90a1353d-56d58a7ba96sm13948214e0c.17.2026.03.31.16.00.12 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 31 Mar 2026 16:00:16 -0700 (PDT) From: Luiz Angelo Daros de Luca Date: Tue, 31 Mar 2026 20:00:01 -0300 Subject: [net-next PATCH 01/10] net: dsa: tag_rtl8_4: update format description Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260331-realtek_forward-v1-1-44fb63033b7e@gmail.com> References: <20260331-realtek_forward-v1-0-44fb63033b7e@gmail.com> In-Reply-To: <20260331-realtek_forward-v1-0-44fb63033b7e@gmail.com> To: Andrew Lunn , Vladimir Oltean , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , Linus Walleij , =?utf-8?q?Alvin_=C5=A0ipraga?= , Yury Norov , Rasmus Villemoes , Russell King Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org, Luiz Angelo Daros de Luca X-Mailer: b4 0.15.1 From: Alvin =C5=A0ipraga Document the updated tag layout fields (EFID, VSEL/VIDX) and clarify which bits are set/cleared when emitting tags. Co-developed-by: Alvin =C5=A0ipraga Signed-off-by: Alvin =C5=A0ipraga Signed-off-by: Luiz Angelo Daros de Luca --- net/dsa/tag_rtl8_4.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/net/dsa/tag_rtl8_4.c b/net/dsa/tag_rtl8_4.c index 2464545da4d2..b7ed39c5419f 100644 --- a/net/dsa/tag_rtl8_4.c +++ b/net/dsa/tag_rtl8_4.c @@ -17,8 +17,8 @@ * | (8-bit) | (8-bit) = | * | Protocol [0x04] | REASON = | b * |-----------------------------------+---------------------------------= --| y - * | (1) | (1) | (2) | (1) | (3) | (1) | (1) | (1) | (5) = | t - * | FID_EN | X | FID | PRI_EN | PRI | KEEP | X | LEARN_DIS | X = | e + * | (1) | (3) | (1) | (3) | (1) | (1) | (1) | (5) = | t + * | EFID_EN | EFID | PRI_EN | PRI | KEEP | VSEL | LEARN_DIS | VIDX= | e * |-----------------------------------+---------------------------------= --| s * | (1) | (15-bit) = | | * | ALLOW | TX/RX = | v @@ -32,19 +32,22 @@ * EtherType | note that Realtek uses the same EtherType for * | other incompatible tag formats (e.g. tag_rtl4_a= .c) * Protocol | 0x04: indicates that this tag conforms to this format - * X | reserved * ------------+------------- * REASON | reason for forwarding packet to CPU * | 0: packet was forwarded or flooded to CPU * | 80: packet was trapped to CPU - * FID_EN | 1: packet has an FID - * | 0: no FID - * FID | FID of packet (if FID_EN=3D1) + * EFID_EN | 1: packet has an EFID + * | 0: no EFID + * EFID | Extended filter ID (EFID) of packet (if EFID_EN=3D1) * PRI_EN | 1: force priority of packet * | 0: don't force priority * PRI | priority of packet (if PRI_EN=3D1) * KEEP | preserve packet VLAN tag format + * VSEL | 0: switch should classify packet according to VLAN tag + * | 1: switch should classify packet according to VLAN memb= ership + * | configuration with index VIDX * LEARN_DIS | don't learn the source MAC address of the packet + * VIDX | index of a VLAN membership configuration to use with VS= EL * ALLOW | 1: treat TX/RX field as an allowance port mask, meaning= the * | packet may only be forwarded to ports specified in t= he * | mask @@ -111,7 +114,7 @@ static void rtl8_4_write_tag(struct sk_buff *skb, struc= t net_device *dev, /* Set Protocol; zero REASON */ tag16[1] =3D htons(FIELD_PREP(RTL8_4_PROTOCOL, RTL8_4_PROTOCOL_RTL8365MB)= ); =20 - /* Zero FID_EN, FID, PRI_EN, PRI, KEEP; set LEARN_DIS */ + /* Zero EFID_EN, EFID, PRI_EN, PRI, VSEL, VIDX, KEEP; set LEARN_DIS */ tag16[2] =3D htons(FIELD_PREP(RTL8_4_LEARN_DIS, 1)); =20 /* Zero ALLOW; set RX (CPU->switch) forwarding port mask */ --=20 2.53.0 From nobody Wed Apr 1 08:15:25 2026 Received: from mail-vk1-f175.google.com (mail-vk1-f175.google.com [209.85.221.175]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 28F82346766 for ; Tue, 31 Mar 2026 23:00:23 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.175 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774998025; cv=none; b=NSDHRYz2xTTxC6d8njZLL7LBiiM/4zot5Zg7X1LXSatbl+4Nj+o037IoCs7TWa43ue9dI2lvxh72RClynoYPu1DaidYLDYvra66fRC9ydaBSPBLF2jEMrk+vF0q7TfQx6VsTNWhafVVEFKMOjQmRWdy6ibxelkdXbHffy2+HaJw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774998025; c=relaxed/simple; bh=jpPmDvg4WiegEQM0IYSX0CZC2jP6oj/2V+56GqkivfI=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=DVOTnDdc3PStWqAUTWlT56VWVKW3LObNySkJlYZ05ULaLm868FHI0EHFqWjbRFQsOmOn5GFbpjtQtmOAnckvCTtbsnBEy6Uvh0qhGqCULV2aryfzbKQ7YPisMuloCGHHw+E12d4WCdl8BFEvy5/abvvqBGMIZ2Phh5YO175qwZ8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=NYMVcQjJ; arc=none smtp.client-ip=209.85.221.175 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="NYMVcQjJ" Received: by mail-vk1-f175.google.com with SMTP id 71dfb90a1353d-56d85e76d73so602430e0c.2 for ; Tue, 31 Mar 2026 16:00:23 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1774998023; x=1775602823; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=MiThgiHUNvzIUgWjS4Gk6rzIcaAngyxuVqiXsTkuGBA=; b=NYMVcQjJHVojMEkbzkZ0G7vfDXtyOBSS5JshHwUc0YFt131V9cKB7TY/qrfpVXckfg 4VJrh5YnPzHNTXQXq7xRhYuFP+taslUje9hRuCfANRgbputhR+PK1E6Qx+s5cNO3Ehfa JivpgZBYI3iTB4ryAaMyIuTQ+wuU5IQdaMTOPQsqBywfTQsT/J7dERykOzqVAd4H/JpL G0PlaGekkIp312lvrowpr7vuG9XdiLmHFjCbve5GUwu/XLX+/VTmXM9OBcIr40SrWnPy 4FTO0PyYLmgsmcXyaneFd/E0ImF+whDhKhb8CQMvaWDIFb4PTVpX2Tnou/3icJtIyLMO bhog== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1774998023; x=1775602823; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=MiThgiHUNvzIUgWjS4Gk6rzIcaAngyxuVqiXsTkuGBA=; b=kBr6K7HSx2KhvRIjRWom0amkHT/oFzlwyOyllFfmTqCWWxVb+8fgSe1ZI1fJFWGJdg umVfQSPsv/8P3faaIFLA6pc+lyhSe3V+l6W9cToytTLNfRef1+anRY69NPxTS47W1+ZY rowJRy9BSe+DMblPf6dbLPvxovWMLSvYfQx83wHJmPVtCjysjevF3ohHvupr0LtBp6ul 5SX0CpqepqOBVQdcW10CiuRXaRcRzMoKrCQRWoGXxQn7Vdlukdke1xYnyTopgpA2n45s kXG0NGrV6OWcxwnjltlLDsJVH1VNrXwgKx5eFZGQ9Rb/5k4gF/HXy69zBEwVRHSRivQO gcfg== X-Forwarded-Encrypted: i=1; AJvYcCWsuxGWCWiT9zIQ/3k48j9GcAHZolhnHDKhy/Nu8zXOPUuQJT/5gdORViBHbENVSSSw/V8HtRJyKSXXSy0=@vger.kernel.org X-Gm-Message-State: AOJu0YzRVQlUBxk/nGrd1+OnIV/TVz+jaua6YaP1Y9BXHkfKyNLzvECJ 73dgGfvNysz8Msco6QYOui8hhEmITO4+6YnoXJHnuWeasc3rUFONfkVO X-Gm-Gg: ATEYQzw+r7icNmOV0SiyPi/rxJ2ItzAR61yMZlZoKCIaKMqFTO5xulzPrPQYpqaqD/0 qETCi+IbTiP4pw9/NAjSDamoiWsBLxH4WXVuR6QwGk1Jqul8NI5aDvRtXraBuPBWFuQwO148Vqq FNQMp+0sojtfoi+d8KYpa2Sa2WIFFXI5qSL10fIX42SpI3TiwNfMNLgGAF9fC90t887V5i6CBnX qMWJ85FjKpcllAEgH9nDO/rlX1QQWisJCwAAw4k/+AFVHRL0k0sSYaPz9fITtiYhY5fj+RJWMsV EX+rozXzSYEMB7bJ0TfF1zsQNhtr4FEL3bkOUBbS2XOduGnROqvheM5YDiLUPzH2syN/XO+cxWc hPAPCmMHH+iFqMsxSwcBrTXPkb1xG1sHb/QBQ2FhCi6iYUKo6OXf71sj/7w6hQxJkHUzX1Tuql5 F4kacUhM7a6/8pZJBj+UhnJdeVulkCpf0Cvu2ABi0x6ZPhG9as X-Received: by 2002:a05:6122:4588:b0:56b:9b7b:83e7 with SMTP id 71dfb90a1353d-56d8a89484cmr792432e0c.7.1774998022785; Tue, 31 Mar 2026 16:00:22 -0700 (PDT) Received: from tresc054937.tre-sc.gov.br ([187.65.210.13]) by smtp.gmail.com with ESMTPSA id 71dfb90a1353d-56d58a7ba96sm13948214e0c.17.2026.03.31.16.00.17 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 31 Mar 2026 16:00:22 -0700 (PDT) From: Luiz Angelo Daros de Luca Date: Tue, 31 Mar 2026 20:00:02 -0300 Subject: [net-next PATCH 02/10] net: dsa: realtek: rtl8365mb: set STP state to disabled early Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260331-realtek_forward-v1-2-44fb63033b7e@gmail.com> References: <20260331-realtek_forward-v1-0-44fb63033b7e@gmail.com> In-Reply-To: <20260331-realtek_forward-v1-0-44fb63033b7e@gmail.com> To: Andrew Lunn , Vladimir Oltean , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , Linus Walleij , =?utf-8?q?Alvin_=C5=A0ipraga?= , Yury Norov , Rasmus Villemoes , Russell King Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org, Luiz Angelo Daros de Luca X-Mailer: b4 0.15.1 From: Alvin =C5=A0ipraga Since we are going around and manipulating a number of port settings during setup, it's safer to assume that the port is disabled to begin with. This is purely defensive. Co-developed-by: Alvin =C5=A0ipraga Signed-off-by: Alvin =C5=A0ipraga Signed-off-by: Luiz Angelo Daros de Luca --- drivers/net/dsa/realtek/rtl8365mb.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/net/dsa/realtek/rtl8365mb.c b/drivers/net/dsa/realtek/= rtl8365mb.c index 31fa94dac627..530ff5503543 100644 --- a/drivers/net/dsa/realtek/rtl8365mb.c +++ b/drivers/net/dsa/realtek/rtl8365mb.c @@ -1989,6 +1989,12 @@ static int rtl8365mb_setup(struct dsa_switch *ds) if (dsa_is_unused_port(ds, i)) continue; =20 + /* Set the initial STP state of all ports to DISABLED, otherwise + * ports will still forward frames to the CPU despite being + * administratively down by default. + */ + rtl8365mb_port_stp_state_set(ds, i, BR_STATE_DISABLED); + /* Forward only to the CPU */ ret =3D rtl8365mb_port_set_isolation(priv, i, cpu->mask); if (ret) @@ -1999,12 +2005,6 @@ static int rtl8365mb_setup(struct dsa_switch *ds) if (ret) goto out_teardown_irq; =20 - /* Set the initial STP state of all ports to DISABLED, otherwise - * ports will still forward frames to the CPU despite being - * administratively down by default. - */ - rtl8365mb_port_stp_state_set(ds, i, BR_STATE_DISABLED); - /* Set up per-port private data */ p->priv =3D priv; p->index =3D i; --=20 2.53.0 From nobody Wed Apr 1 08:15:25 2026 Received: from mail-vk1-f172.google.com (mail-vk1-f172.google.com [209.85.221.172]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 2C36E2FD7D3 for ; Tue, 31 Mar 2026 23:00:29 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774998030; cv=none; b=iQssOeLOX8G+0CPp5riBWgu0eg5kGR8LwYyKJKeMz0kJ4QhTfXwbRjqK5xS3fnXTXjEEw9UfJZk3Ko3ouEJSX17nWB+LQpXGHUP8uTx+RugU8OUieMpwpHNZV3gZ9KY6VbAv3mZk9+TA8ZevBr3jF2m59EH95HZape6fVydmVl8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774998030; c=relaxed/simple; bh=WPNJNWrBYnAojkkcHRpxEx5f5nMRyycsOuMu5ztrywo=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=N5WiKLjXDZWDoxn6D7qKiUEmpF/izxh+YjTpAFuRhQ4ih/K6+N+KTmTRz2M2mXV0C7TW2gyw2QU7lmiMsLaTBHwo/huVYuDxzy1IbQmgm6f/qbq0VggRuRcusPNxd2oSg02SorqAS5w9F9Y/Z+5rVcVYcgKSSkToRRq814A+gJE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=WU0sbQ5B; arc=none smtp.client-ip=209.85.221.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="WU0sbQ5B" Received: by mail-vk1-f172.google.com with SMTP id 71dfb90a1353d-56ce54c8c82so1986639e0c.2 for ; Tue, 31 Mar 2026 16:00:29 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1774998028; x=1775602828; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=KZrJJG+BUOXCgvao7M4HPDJpbagyCTvtNjqLqKAeBdA=; b=WU0sbQ5Bzv3PCz2QpNUfUn44OnIRh7xR72ijqk3z3kD6JA+/uqWlZ2upkJM8GAZt4A s5ma9egrWfVfKw4qx9G13dL1eN65vay6IFfBzfpIlQQ7ZqtbongB4CuwHsCPGoEQMWCi /bTq52+XjmohwTk68+jJgth61hkS9HNUwDYrBtigWblodK9KbYu9PAu5aCAXrwKoXR11 HhHsyLryoj1SuxpyArCR2u5BQY4Fzf3B5sUhbTPvrJfBVXYkjCgtmtJuW0iE42aBoQXc GkTsbx8gdzOrgxvnGdC5yIP2c/TAofmB4EMLddkw0qnWC96hKlbjheLpxHEJ/kjH1OxH dQoQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1774998028; x=1775602828; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=KZrJJG+BUOXCgvao7M4HPDJpbagyCTvtNjqLqKAeBdA=; b=BLr3CgGKoZ6OhTh43laQ4zD306JMe4VNmdNw70i4IwU8teFH0MA26DxOibqfPk5OJx bDIkxckR6mchivz5ukRovRQTSrvLSqj3uTr4RXHE9CIWdnxD3BJ9NvPO/HEvxJ5/87jf GPsz58kfxA81DGIlb5YOf6vjrJLdC5IEin9hFq7KZixI2ODaP1csC52Hr++kkXHMXJOQ L+y7xQv6V7Q+xNFaOchanTh0VWQgit0nQduaNOQDWXvT1sL9EFLsgWCwX4dD+45ifg40 XiqZE6zXYk48aZvo/ZSx09b5N91rXMK68JMg3nOXl0APLb6359xyc51TQFMY/K35fdyN AiQw== X-Forwarded-Encrypted: i=1; AJvYcCV4Nn+hkiv4w1JtVnD9BPk2rrYY4yKlvEoix3PNP0mR/ei7lFJ0ppBpWFK260LiuvQ+hJVWThxW9+d/1YI=@vger.kernel.org X-Gm-Message-State: AOJu0YxT61VtjLz1QHhO5/W5Qwiqv1EbYFsk6IjHt3QTIi2YmhBv6z2Z CWAQDBgt6gLZhiWnxAJxQd1Mt7OY+laND8Xc+qjS6m6r/nz9frBcCopO X-Gm-Gg: ATEYQzwwDID2XjTyh4gYBMmLQjzQtTJuXdjeQqEcRqbPn3wvDAdrK6eUNJYmVWt8Ep7 oemvvLVWS5Xt9HXw9o/74tHgBeXzw4NlzrDtFBT3wpH1axu/0bnmmAQ53hW0ayrs+U+QAh/ew04 BmaQ1/9HAf/MDqxmBYjG9smC5AJWddqymiYVF/U7aZCLWBgyVapG0UpgsohI+/nomCMsT4VAUU9 7lQJcf6deMWyp1XVeBU53n4FrG0JXn9qWqXddcCmQkHQvveA/edY1A8qz0Q26l2nd7q71Vo8R4l FACU8qFLpm7KoyHB21/iZ4nqbAR1FFRdmDe12SDVWMna985mqjnxaOK7updMgd0nz52LfUzc48s jPypW0zb0NIhTRsguuGdP/Z3b7+qihZEBfxQQgrrGIMXcB0av+D8fLSmpjl09USh7HM30rab1R+ YV4PW7Aw4bOEtVJPWIXd7J+zo+RvsyeCAHKB+Cxw== X-Received: by 2002:a05:6122:3288:b0:56a:e46f:2cfa with SMTP id 71dfb90a1353d-56d8a916d32mr621207e0c.12.1774998028157; Tue, 31 Mar 2026 16:00:28 -0700 (PDT) Received: from tresc054937.tre-sc.gov.br ([187.65.210.13]) by smtp.gmail.com with ESMTPSA id 71dfb90a1353d-56d58a7ba96sm13948214e0c.17.2026.03.31.16.00.23 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 31 Mar 2026 16:00:27 -0700 (PDT) From: Luiz Angelo Daros de Luca Date: Tue, 31 Mar 2026 20:00:03 -0300 Subject: [net-next PATCH 03/10] net: dsa: realtek: rtl8365mb: prepare for multiple source files Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260331-realtek_forward-v1-3-44fb63033b7e@gmail.com> References: <20260331-realtek_forward-v1-0-44fb63033b7e@gmail.com> In-Reply-To: <20260331-realtek_forward-v1-0-44fb63033b7e@gmail.com> To: Andrew Lunn , Vladimir Oltean , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , Linus Walleij , =?utf-8?q?Alvin_=C5=A0ipraga?= , Yury Norov , Rasmus Villemoes , Russell King Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org, Luiz Angelo Daros de Luca X-Mailer: b4 0.15.1 From: Alvin =C5=A0ipraga Rename rtl8365mb.c to rtl8365mb_main.c in preparation for subsequent commits which add additional source files to the driver. The trailing backslash in the Makefile is deliberate. It allows for new files to be added without clobbering git history. Co-developed-by: Alvin =C5=A0ipraga Signed-off-by: Alvin =C5=A0ipraga Signed-off-by: Luiz Angelo Daros de Luca --- drivers/net/dsa/realtek/Makefile | 1 + drivers/net/dsa/realtek/{rtl8365mb.c =3D> rtl8365mb_main.c} | 0 2 files changed, 1 insertion(+) diff --git a/drivers/net/dsa/realtek/Makefile b/drivers/net/dsa/realtek/Mak= efile index 17367bcba496..3f986e04912f 100644 --- a/drivers/net/dsa/realtek/Makefile +++ b/drivers/net/dsa/realtek/Makefile @@ -16,3 +16,4 @@ ifdef CONFIG_NET_DSA_REALTEK_RTL8366RB_LEDS rtl8366-objs +=3D rtl8366rb-leds.o endif obj-$(CONFIG_NET_DSA_REALTEK_RTL8365MB) +=3D rtl8365mb.o +rtl8365mb-objs :=3D rtl8365mb_main.o \ diff --git a/drivers/net/dsa/realtek/rtl8365mb.c b/drivers/net/dsa/realtek/= rtl8365mb_main.c similarity index 100% rename from drivers/net/dsa/realtek/rtl8365mb.c rename to drivers/net/dsa/realtek/rtl8365mb_main.c --=20 2.53.0 From nobody Wed Apr 1 08:15:25 2026 Received: from mail-vk1-f169.google.com (mail-vk1-f169.google.com [209.85.221.169]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8E644346766 for ; Tue, 31 Mar 2026 23:00:34 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.169 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774998035; cv=none; b=gUvf0PUNFwmQrwyjHkR7xRPGteWcyPAX9loCNVXOC7ubi7ApLe+bRPeqq0HDTyRQxnFHo1r3m0evJ43/hPnF4Y6PjcGYwFW5zpm7o19WtOBtn+70X8D9GXhd3i6DfkUN0pLlJC0w3PVlgU9Lmho4CU1j/9S0svb02uaZrxWo+PU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774998035; c=relaxed/simple; bh=ebB3tFe7st0ChlV9KEmkVCHkBQRl1ZMa/WdScimdd9g=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=O3p/bNXgA7Q5xrAtVB4ooZ1aynpHkH2y5hzNa46iBwFcxZ+X95FQHFHKL7YZGMdONjln0OXr9uWBgMtxxf9/d4yZeBhThGZdSyCqBa6SYYXg6dt2yOul/z7DG2Iu9L9mWBxyp/0/XkNWG+bI3i1TI1Yh+VpIK/RSONswK8NVtKw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=S/UTOowP; arc=none smtp.client-ip=209.85.221.169 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="S/UTOowP" Received: by mail-vk1-f169.google.com with SMTP id 71dfb90a1353d-56d8365c1efso255399e0c.0 for ; Tue, 31 Mar 2026 16:00:34 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1774998033; x=1775602833; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=Ya7/v/C2ic5WGsD3LIjfz1NrbBwmNrzBq94qY22p7kA=; b=S/UTOowPngScjHxsuMUEcHpY/ce8RmIUfVRw3cDeaqvtLQ/tX4xyZ8J8AJVX7U5GJs mJ2sjb6nTe2BanqMOanOyewVitFdXUGvK4a3azlWuBOhgSfjAigvkczxTKNS+bafpaDR xO/lhZLsEHlomJ47l6m9/Z5oiCiYADGMt/RdeofbMcSVMvwN+a/g3l6uP1foZ44XLch3 znzecaGkW08tHm4odtLNTo3V2uK0HC7H09tcdogRqxc2pjdtSEOVkBfwbPBRWumAbCQ+ 3/I+LFbEEQRwN8eIhcSK1+D9anaiKDDBp9KBok4FqVa8yeQy8MWAIIGH3fl9ysnXiX4b dBXw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1774998033; x=1775602833; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=Ya7/v/C2ic5WGsD3LIjfz1NrbBwmNrzBq94qY22p7kA=; b=OpnYGAWNFsCAvnJ9Xey7KYfnBBj2YCytAxyw6PTClqfKXltCL+SGRbQsKEr0DlLa6+ 6k77dYvgMJ8UYXBi8no6ARBC+WrAY1WQbROeCL7bblN5dGp/y4iYz8xYDTBP8rHGN0kP btJGlO86YZwYmJIcRkXyu0AiL6KFS2UuyBJc1fQdYR/9WSFy/S8BnNDx+seuTYTvf6tj 6mt/QZ1OS/p14zi1NYVSVc+O0v3RDRubCx6Yl5cgnBMcaSxZR6beG1jX7QZVQf+7Wo/X nLQ0UoTSeurM/vPwUJn5EZERdsUrYkBo9DdbQx3v2DAiymucz1zLYNRmltbk5uWuGcaY Elzw== X-Forwarded-Encrypted: i=1; AJvYcCXKylAh/QfSBS36OY8Lf7g2qYA9mvIbcAkr2cMTrvUUP4t1wEptMmsAvQ5vhNDo9G/ujOedxXNDvXZCO/0=@vger.kernel.org X-Gm-Message-State: AOJu0YyX0h4PEV5fQ1RyASrZqtvtF3bGp6W4uT5JuD69JLv3WgbZY6ve oVsVC5qhCeHvQFrLZG+W7GsVuYDvLshaRNy9kmg8GeCTcS5D4fn5Yun+mU8A6blU X-Gm-Gg: ATEYQzxrIEOO9XPrMT7Up1dRNvdQEpOAeL8M4a0CeGUGiVQXQAuhsM2MRc9MGawLrJN BtG81Bx6EsDk/krclKRLLYF0u6PxTRcziZ+mouiEn7nIdvkb+BmkRrgfm1hoSzRQdTu46pK1QcC i99AdwFfsiBylzlv8CkWqmLcDuKkwGcOFvqAGIqFZLRrIzqYjYPTwLNpTcLMvCgeBf/DoNCR0I8 3P43r2l1Dz2PG+tlGSfPOq/jNPfRpAxKfRoxWQ4+3+o9hprjJVc2Nv/aVndlxaChEBGiUQvDMsj TzwOrqnpygit89hhMA0zR9mVaunyXExi4q/N/a+hu97qGVUhwFyORuNzn4ykYAo4Hm6UIyI/nZg heqKkXYeVud8qq0eu+h2PW+cIqaH8A8TJp8dyrUvS1BxhmQSKr1/46fRnF7fXvjWqOyoWEYeO0p eBI35rTfsEdJK5YxdqSZ4eEXCBl6MxLLAunW8FGg== X-Received: by 2002:a05:6122:2224:b0:566:ec03:4683 with SMTP id 71dfb90a1353d-56d8ae13b72mr522364e0c.2.1774998033459; Tue, 31 Mar 2026 16:00:33 -0700 (PDT) Received: from tresc054937.tre-sc.gov.br ([187.65.210.13]) by smtp.gmail.com with ESMTPSA id 71dfb90a1353d-56d58a7ba96sm13948214e0c.17.2026.03.31.16.00.28 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 31 Mar 2026 16:00:32 -0700 (PDT) From: Luiz Angelo Daros de Luca Date: Tue, 31 Mar 2026 20:00:04 -0300 Subject: [net-next PATCH 04/10] bitfield.h: add FIELD_WIDTH() Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260331-realtek_forward-v1-4-44fb63033b7e@gmail.com> References: <20260331-realtek_forward-v1-0-44fb63033b7e@gmail.com> In-Reply-To: <20260331-realtek_forward-v1-0-44fb63033b7e@gmail.com> To: Andrew Lunn , Vladimir Oltean , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , Linus Walleij , =?utf-8?q?Alvin_=C5=A0ipraga?= , Yury Norov , Rasmus Villemoes , Russell King Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org, Luiz Angelo Daros de Luca X-Mailer: b4 0.15.1 Some hardware stores logical fields split across multiple registers, requiring partial values to be reassembled using shifts and masks. Add FIELD_WIDTH(), a helper that returns the number of contiguous bits covered by a bitfield mask. This allows callers to determine how much a partial value needs to be shifted when reconstructing a field. This complements FIELD_MAX() and FIELD_FIT(), and avoids reimplementing bit counting logic in drivers. Signed-off-by: Luiz Angelo Daros de Luca --- include/linux/bitfield.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/include/linux/bitfield.h b/include/linux/bitfield.h index 54aeeef1f0ec..8d6c1c309c3b 100644 --- a/include/linux/bitfield.h +++ b/include/linux/bitfield.h @@ -111,6 +111,19 @@ (typeof(_mask))((_mask) >> __bf_shf(_mask)); \ }) =20 +/** + * FIELD_WIDTH() - return the width of a bitfield + * @_mask: shifted mask defining the field's length and position + * + * Returns the number of contiguous bits covered by @_mask. + * This corresponds to the bit width of FIELD_MAX(@_mask). + */ +#define FIELD_WIDTH(_mask) \ + ({ \ + __BF_FIELD_CHECK(_mask, 0ULL, 0ULL, "FIELD_WIDTH: "); \ + __bf_shf(~FIELD_MAX(_mask)); \ + }) + /** * FIELD_FIT() - check if value fits in the field * @_mask: shifted mask defining the field's length and position --=20 2.53.0 From nobody Wed Apr 1 08:15:25 2026 Received: from mail-vk1-f174.google.com (mail-vk1-f174.google.com [209.85.221.174]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 1B6874611CE for ; Tue, 31 Mar 2026 23:00:39 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.174 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774998041; cv=none; b=l6uA1T1VWwL6GEfLMVMNfC8MILhblcAnj7oiAWRbfxa2v2eTWxLm43NX+3w+RGvHWv3kiLtOl0+1GzkV98w7LIv69mf6YlxDUa30RtoWEv/gDnTk5KqFInqcfQ5i+B/VQnrAp0TaBkf8+Xbfk7iGCcfDAuQPx9W/VvhQCnQdi6M= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774998041; c=relaxed/simple; bh=qFAUFb2gfC1QG1XIoe8EoAN6Yv/7RceDpbGATS8CNkY=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=F3ymSklrJN+Eg70fO1mygrb9D8etkObie840+3yLU/CDtwtLssuis4qzUthN0v3kRnFvCcdnCiJ75k1hFZTdy+Y+nwmsG3G3/wln0jufIHpvS8MxiYHj5wETKETVTM0aPn5Ear1b8i7gEuOmy/dvL1oCBNpkai0iSjnT0qJqUTk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=Yvvo1gkd; arc=none smtp.client-ip=209.85.221.174 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="Yvvo1gkd" Received: by mail-vk1-f174.google.com with SMTP id 71dfb90a1353d-56a857578a8so2563594e0c.3 for ; Tue, 31 Mar 2026 16:00:39 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1774998039; x=1775602839; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=eaQ9peV6MKpS65VqKmDafQwn6JmBvtInEx/V01jlh+Q=; b=Yvvo1gkdcVM8hWECai6xs4sbTIF1zoAHqE+S/BNYZhm6JmcuJwsHT8+CmZ4HMmsXwC w0N06hEkAQ2EDwCD7mhPzyDNS5krAJu2bSZsTJQlQVTwGe77Xhr3bcUpagerxcRm7hpR tJBmlb8C2f7R4Z9gMy4J3xuiXJT73PIfbfbc4GTSWLUdzuMvAMgZge+YuiZ4Ry4ezdo1 FMZdpQxH5x6J3+kwssIblgoEAaAXIUMM5Urr0Azye6e7Ue2UGKQEPPrMizUNlFlwkM49 6D8wwDUpzV5SPMPZC32bJUihIh9GI46+iemty8g/p9nQv+UYDKz68X9i68bD1dDtNrDr h8GA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1774998039; x=1775602839; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=eaQ9peV6MKpS65VqKmDafQwn6JmBvtInEx/V01jlh+Q=; b=ilHfxImgMSkVsdrIZu5/6vcXIfs8AE12UpRys1zoAG/+XV+wmKjjzmu9hG1KsCtmsU gsVjQVTJWohAa6LzOt9C31Byc2wvFBRHtbnNNjs2cH30Jb68FOfSNgsSdOffrupEwQP2 zZMCOURYyBKAJ6elZCPduR2Jg4XVcrTQtQCRmEQcyFDoJuHb2Vu5mUQ6R1AUtZeMBfpN yFpghNbpCfeV+0CMCtGs2SX8hT3+d3TsWmMaxtr1DeVvDnvjmRSwJFos1OY9qHDoJCrR +o+PCuJVXgSQK5cUUfUXpBynIeK5hiMagZDyYnxZ+dBgu4Ej9w/yMKhV55Q00dQr0BqJ b14w== X-Forwarded-Encrypted: i=1; AJvYcCWigl8INZFbgAEtqDmAupqIhvpFJA4+okaRXhNcQfq63rU2i3180eLIRCOeJlIqFwvPlinYPQ1MC73PiTg=@vger.kernel.org X-Gm-Message-State: AOJu0Yxu+aIkUxQsKsTmuR5JWCq5RpDl7+6wGmlQJN317k8GwSu6lYJn LRHskg6GP5kBQNWliPohu8YK6/3M9E+nT0b6Oo1qbm4kOHiD4M21XGSA X-Gm-Gg: ATEYQzwTm1AvgblltoR/5VgBGqeDh+W4XjVntjyVfbr0RC77gW0dUoTGn7DkJqMI3bK 3LSHx4yve/iS+adKu3PfWHR27M9nq7DOzK8TqYJESZTQLTuRqyXG2SfcL6ujunGUuPiiR9CJUyv mReVlb4r3pIf1Q41ZHsNDdhhDYjhBkBlsKrOJN+eyZN46Y7pyFc4VMpXTs5RxRnqH4uxkyr92ME AR7Af0zIVegvcFCV+FtRsxE049M9o6gbITzVH+BbYKs6VHqqD6lqa0Pj39qXz08xN+O6JWIIyGK hF9jf85CJNx44SMhsWXfVwWKLOF2oxftjPDhu8mpbb2+AsuOj1GRff5NkhZbpXcslH4maXI7zZ1 R6o0WetW/25v6zlPZJLZy3jgXfNXf5uFh0glONGsqxH96yJ6TYgWZNLoa6HXV3Wi/PwrBW48Olf Glf0VQJ8VOTZHKGb0hfRRiHc+lDy4zU4gFwXMe2Q== X-Received: by 2002:a05:6122:4b89:b0:559:5ef5:b196 with SMTP id 71dfb90a1353d-56d8a9636f4mr693678e0c.13.1774998038989; Tue, 31 Mar 2026 16:00:38 -0700 (PDT) Received: from tresc054937.tre-sc.gov.br ([187.65.210.13]) by smtp.gmail.com with ESMTPSA id 71dfb90a1353d-56d58a7ba96sm13948214e0c.17.2026.03.31.16.00.33 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 31 Mar 2026 16:00:38 -0700 (PDT) From: Luiz Angelo Daros de Luca Date: Tue, 31 Mar 2026 20:00:05 -0300 Subject: [net-next PATCH 05/10] net: dsa: realtek: rtl8365mb: add table lookup interface Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260331-realtek_forward-v1-5-44fb63033b7e@gmail.com> References: <20260331-realtek_forward-v1-0-44fb63033b7e@gmail.com> In-Reply-To: <20260331-realtek_forward-v1-0-44fb63033b7e@gmail.com> To: Andrew Lunn , Vladimir Oltean , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , Linus Walleij , =?utf-8?q?Alvin_=C5=A0ipraga?= , Yury Norov , Rasmus Villemoes , Russell King Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org, Luiz Angelo Daros de Luca X-Mailer: b4 0.15.1 From: Alvin =C5=A0ipraga Add a generic table lookup interface to centralize access to the RTL8365MB internal tables. This interface abstracts the low-level table access logic and will be used by subsequent commits to implement FDB and VLAN operations. Co-developed-by: Alvin =C5=A0ipraga Signed-off-by: Alvin =C5=A0ipraga Signed-off-by: Luiz Angelo Daros de Luca --- drivers/net/dsa/realtek/Makefile | 1 + drivers/net/dsa/realtek/rtl8365mb_table.c | 255 ++++++++++++++++++++++++++= ++++ drivers/net/dsa/realtek/rtl8365mb_table.h | 133 ++++++++++++++++ 3 files changed, 389 insertions(+) diff --git a/drivers/net/dsa/realtek/Makefile b/drivers/net/dsa/realtek/Mak= efile index 3f986e04912f..99654c4c5a3d 100644 --- a/drivers/net/dsa/realtek/Makefile +++ b/drivers/net/dsa/realtek/Makefile @@ -17,3 +17,4 @@ rtl8366-objs +=3D rtl8366rb-leds.o endif obj-$(CONFIG_NET_DSA_REALTEK_RTL8365MB) +=3D rtl8365mb.o rtl8365mb-objs :=3D rtl8365mb_main.o \ + rtl8365mb_table.o \ diff --git a/drivers/net/dsa/realtek/rtl8365mb_table.c b/drivers/net/dsa/re= altek/rtl8365mb_table.c new file mode 100644 index 000000000000..e706ea2ccb85 --- /dev/null +++ b/drivers/net/dsa/realtek/rtl8365mb_table.c @@ -0,0 +1,255 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Look-up table query interface for the rtl8365mb switch family + * + * Copyright (C) 2022 Alvin =C5=A0ipraga + */ + +#include "rtl8365mb_table.h" +#include + +/* Table access control register */ +#define RTL8365MB_TABLE_CTRL_REG 0x0500 +/* Should be one of rtl8365mb_table enum members */ +#define RTL8365MB_TABLE_CTRL_TABLE_MASK GENMASK(2, 0) +/* Should be one of rtl8365mb_table_op enum members */ +#define RTL8365MB_TABLE_CTRL_OP_MASK GENMASK(3, 3) +/* Should be one of rtl8365mb_table_l2_method enum members */ +#define RTL8365MB_TABLE_CTRL_METHOD_MASK GENMASK(7, 4) +/* NOTE: PORT_MASK is only 4 bit, which suggests that port-based + * look-up of the L2 table only works for physical port addresses + * 0~4. It could be that the Realtek driver is out-of-date and + * actually the mask is something like 0xFF00, but this is + * unconfirmed. + */ +#define RTL8365MB_TABLE_CTRL_PORT_MASK GENMASK(11, 8) + +/* Table access address register */ +#define RTL8365MB_TABLE_ACCESS_ADDR_REG 0x0501 +#define RTL8365MB_TABLE_ADDR_MASK GENMASK(13, 0) + +/* Table status register */ +#define RTL8365MB_TABLE_STATUS_REG 0x0502 +#define RTL8365MB_TABLE_STATUS_ADDRESS_MASK GENMASK(10, 0) +/* set for L3, unset for L2 */ +#define RTL8365MB_TABLE_STATUS_ADDR_TYPE_MASK GENMASK(11, 11) +#define RTL8365MB_TABLE_STATUS_HIT_STATUS_MASK GENMASK(12, 12) +#define RTL8365MB_TABLE_STATUS_BUSY_FLAG_MASK GENMASK(13, 13) +#define RTL8365MB_TABLE_STATUS_ADDRESS_EXT_MASK GENMASK(14, 14) + +/* Table read/write registers */ +#define RTL8365MB_TABLE_WRITE_BASE 0x0510 +#define RTL8365MB_TABLE_WRITE_REG(_x) \ + (RTL8365MB_TABLE_WRITE_BASE + (_x)) +#define RTL8365MB_TABLE_READ_BASE 0x0520 +#define RTL8365MB_TABLE_READ_REG(_x) \ + (RTL8365MB_TABLE_READ_BASE + (_x)) +#define RTL8365MB_TABLE_ENTRY_MAX_SIZE 10 +#define RTL8365MB_TABLE_10TH_DATA_MASK GENMASK(3, 0) +#define RTL8365MB_TABLE_WRITE_10TH_REG \ + RTL8365MB_TABLE_WRITE_REG(RTL8365MB_TABLE_ENTRY_MAX_SIZE - 1) + +static int rtl8365mb_table_poll_busy(struct realtek_priv *priv) +{ + u32 val; + + return regmap_read_poll_timeout(priv->map_nolock, + RTL8365MB_TABLE_STATUS_REG, val, + !FIELD_GET(RTL8365MB_TABLE_STATUS_BUSY_FLAG_MASK, val), + 10, 100); +} + +int rtl8365mb_table_query(struct realtek_priv *priv, + enum rtl8365mb_table table, + enum rtl8365mb_table_op op, u16 *addr, + enum rtl8365mb_table_l2_method method, + u16 port, u16 *data, size_t size) +{ + bool addr_as_input =3D true; + bool write_data =3D false; + int ret =3D 0; + u32 cmd; + u32 val; + u32 hit; + + if (!addr) { + dev_err(priv->dev, "%s: addr is NULL\n", __func__); + return -EINVAL; + } + + if (!data) { + dev_err(priv->dev, "%s: data is NULL\n", __func__); + return -EINVAL; + } + + if (size > RTL8365MB_TABLE_ENTRY_MAX_SIZE) { + dev_err(priv->dev, "%s: size too big: %zu\n", __func__, size); + return -E2BIG; + } + + if (size =3D=3D 0) { + dev_err(priv->dev, "%s: size is 0\n", __func__); + return -EINVAL; + } + + if (!FIELD_FIT(RTL8365MB_TABLE_CTRL_TABLE_MASK, table)) { + dev_err(priv->dev, "%s: table %d does not fit in MASK\n", + __func__, table); + return -EINVAL; + } + + /* Prepare target table and operation (read or write) */ + cmd =3D 0; + cmd |=3D FIELD_PREP(RTL8365MB_TABLE_CTRL_TABLE_MASK, table); + cmd |=3D FIELD_PREP(RTL8365MB_TABLE_CTRL_OP_MASK, op); + if (op =3D=3D RTL8365MB_TABLE_OP_READ && table =3D=3D RTL8365MB_TABLE_L2)= { + if (!FIELD_FIT(RTL8365MB_TABLE_CTRL_METHOD_MASK, method)) + return -EINVAL; + + cmd |=3D FIELD_PREP(RTL8365MB_TABLE_CTRL_METHOD_MASK, method); + switch (method) { + case RTL8365MB_TABLE_L2_METHOD_MAC: + /* + * Method MAC requires as input the same L2 table format + * you'll get as result. However, it might only use mac + * address and FID/VID fields. + */ + write_data =3D true; + + /* METHOD_MAC does not use addr as input, but may return + * the matched index. + */ + addr_as_input =3D false; + + break; + case RTL8365MB_TABLE_L2_METHOD_ADDR: + case RTL8365MB_TABLE_L2_METHOD_ADDR_NEXT: + case RTL8365MB_TABLE_L2_METHOD_ADDR_NEXT_UC: + case RTL8365MB_TABLE_L2_METHOD_ADDR_NEXT_MC: + break; + case RTL8365MB_TABLE_L2_METHOD_ADDR_NEXT_UC_PORT: + if (!FIELD_FIT(RTL8365MB_TABLE_CTRL_PORT_MASK, port)) + return -EINVAL; + + cmd |=3D FIELD_PREP(RTL8365MB_TABLE_CTRL_PORT_MASK, port); + break; + default: + return -EINVAL; + } + } else if (op =3D=3D RTL8365MB_TABLE_OP_WRITE) { + write_data =3D true; + + /* Writing to L2 does not use addr as input, as the table index + * is derived from key fields. + */ + if (table =3D=3D RTL8365MB_TABLE_L2) + addr_as_input =3D false; + } + + /* Validate addr only when used as an input */ + if (addr_as_input) { + if (!FIELD_FIT(RTL8365MB_TABLE_ADDR_MASK, *addr)) { + dev_err(priv->dev, "%s: addr %u does not fit in MASK\n", + __func__, *addr); + return -EINVAL; + } + } + + /* To prevent concurrent access to the look-up tables, take the regmap + * lock manually and access via the map_nolock regmap. + */ + mutex_lock(&priv->map_lock); + + /* Write entry data if writing to the table (or L2_METHOD_MAC) */ + if (write_data) { + /* bulk write data up to 9th byte */ + ret =3D regmap_bulk_write(priv->map_nolock, + RTL8365MB_TABLE_WRITE_BASE, + data, + min_t(size_t, size, + RTL8365MB_TABLE_ENTRY_MAX_SIZE - + 1)); + if (ret) + goto out; + + /* 10th register uses only 4 less significant bits */ + if (size =3D=3D RTL8365MB_TABLE_ENTRY_MAX_SIZE) { + val =3D FIELD_PREP(RTL8365MB_TABLE_10TH_DATA_MASK, + data[size - 1]); + ret =3D regmap_update_bits(priv->map_nolock, + RTL8365MB_TABLE_WRITE_10TH_REG, + RTL8365MB_TABLE_10TH_DATA_MASK, + val); + } + + if (ret) + goto out; + } + + /* Write address (if needed) */ + if (addr_as_input) { + ret =3D regmap_write(priv->map_nolock, + RTL8365MB_TABLE_ACCESS_ADDR_REG, + FIELD_PREP(RTL8365MB_TABLE_ADDR_MASK, + *addr)); + if (ret) + goto out; + } + + /* Execute */ + ret =3D regmap_write(priv->map_nolock, RTL8365MB_TABLE_CTRL_REG, cmd); + if (ret) + goto out; + + /* Poll for completion */ + ret =3D rtl8365mb_table_poll_busy(priv); + if (ret) + goto out; + + /* For both reads and writes to the L2 table, check status */ + if (table =3D=3D RTL8365MB_TABLE_L2) { + ret =3D regmap_read(priv->map_nolock, RTL8365MB_TABLE_STATUS_REG, + &val); + if (ret) + goto out; + + /* Did the query find an entry? */ + hit =3D FIELD_GET(RTL8365MB_TABLE_STATUS_HIT_STATUS_MASK, val); + if (!hit) { + ret =3D -ENOENT; + goto out; + } + + /* If so, extract the address */ + *addr =3D 0; + *addr |=3D FIELD_GET(RTL8365MB_TABLE_STATUS_ADDRESS_MASK, val); + *addr |=3D FIELD_GET(RTL8365MB_TABLE_STATUS_ADDRESS_EXT_MASK, val) + << 11; + /* only set if it is a L3 address */ + *addr |=3D FIELD_GET(RTL8365MB_TABLE_STATUS_ADDR_TYPE_MASK, val) + << 12; + } + + /* Finally, get the table entry if we were reading */ + if (op =3D=3D RTL8365MB_TABLE_OP_READ) { + ret =3D regmap_bulk_read(priv->map_nolock, + RTL8365MB_TABLE_READ_BASE, + data, size); + + /* For the biggest table entries, the uppermost table + * entry register has space for only one nibble. Mask + * out the remainder bits. Empirically I saw nothing + * wrong with omitting this mask, but it may prevent + * unwanted behaviour. FYI. + */ + if (size =3D=3D RTL8365MB_TABLE_ENTRY_MAX_SIZE) { + val =3D FIELD_GET(RTL8365MB_TABLE_10TH_DATA_MASK, + data[size - 1]); + data[size - 1] =3D val; + } + } + +out: + mutex_unlock(&priv->map_lock); + + return ret; +} diff --git a/drivers/net/dsa/realtek/rtl8365mb_table.h b/drivers/net/dsa/re= altek/rtl8365mb_table.h new file mode 100644 index 000000000000..0b1a89bd81f1 --- /dev/null +++ b/drivers/net/dsa/realtek/rtl8365mb_table.h @@ -0,0 +1,133 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Look-up table query interface for the rtl8365mb switch family + * + * Copyright (C) 2022 Alvin =C5=A0ipraga + */ + +#ifndef _REALTEK_RTL8365MB_TABLE_H +#define _REALTEK_RTL8365MB_TABLE_H + +#include +#include + +#include "realtek.h" + +/** + * struct rtl8365mb_table - available switch tables + * + * @RTL8365MB_TABLE_ACL_RULE - ACL rules + * @RTL8365MB_TABLE_ACL_ACTION - ACL actions + * @RTL8365MB_TABLE_CVLAN - VLAN4k configurations + * @RTL8365MB_TABLE_L2 - filtering database (2K hash table) + * @RTL8365MB_TABLE_IGMP_GROUP - IGMP group database (readonly) + * + * NOTE: Don't change the enum values. They must concur with the field + * described by @RTL8365MB_TABLE_CTRL_TABLE_MASK. + */ +enum rtl8365mb_table { + RTL8365MB_TABLE_ACL_RULE =3D 1, + RTL8365MB_TABLE_ACL_ACTION =3D 2, + RTL8365MB_TABLE_CVLAN =3D 3, + RTL8365MB_TABLE_L2 =3D 4, + RTL8365MB_TABLE_IGMP_GROUP =3D 5, +}; + +/** + * enum rtl8365mb_table_op - table query operation + * + * @RTL8365MB_TABLE_OP_READ: read an entry from the target table + * @RTL8365MB_TABLE_OP_WRITE: write an entry to the target table + * + * NOTE: Don't change the enum values. They must concur with the field + * described by @RTL8365MB_TABLE_CTRL_OP_MASK. + */ +enum rtl8365mb_table_op { + RTL8365MB_TABLE_OP_READ =3D 0, + RTL8365MB_TABLE_OP_WRITE =3D 1, +}; + +/** + * enum rtl8365mb_table_l2_method - look-up method for read queries of L2 = table + * + * @RTL8365MB_TABLE_L2_METHOD_MAC: look-up by source MAC address and FID (= or + * VID) + * @RTL8365MB_TABLE_L2_METHOD_ADDR: look-up by entry address + * @RTL8365MB_TABLE_L2_METHOD_ADDR_NEXT: look-up next entry starting from = the + * supplied address + * @RTL8365MB_TABLE_L2_METHOD_ADDR_NEXT_UC: same as ADDR_NEXT but search o= nly + * unicast addresses + * @RTL8365MB_TABLE_L2_METHOD_ADDR_NEXT_MC: same as ADDR_NEXT but search o= nly + * multicast addresses + * @RTL8365MB_TABLE_L2_METHOD_ADDR_NEXT_UC_PORT: same as ADDR_NEXT_UC but + * search only entries with matching source port + * + * NOTE: Don't change the enum values. They must concur with the field + * described by @RTL8365MB_TABLE_CTRL_METHOD_MASK + */ +enum rtl8365mb_table_l2_method { + RTL8365MB_TABLE_L2_METHOD_MAC =3D 0, + RTL8365MB_TABLE_L2_METHOD_ADDR =3D 1, + RTL8365MB_TABLE_L2_METHOD_ADDR_NEXT =3D 2, + RTL8365MB_TABLE_L2_METHOD_ADDR_NEXT_UC =3D 3, + RTL8365MB_TABLE_L2_METHOD_ADDR_NEXT_MC =3D 4, + /* + * RTL8365MB_TABLE_L2_METHOD_ADDR_NEXT_MC_L3 =3D 5, + * RTL8365MB_TABLE_L2_METHOD_ADDR_NEXT_MC_L2L3 =3D 6, + */ + RTL8365MB_TABLE_L2_METHOD_ADDR_NEXT_UC_PORT =3D 7, +}; + +/** + * rtl8365mb_table_query() - read from or write to a switch table + * @priv: driver context + * @table: target table, see &enum rtl8365mb_table + * @op: read or write operation, see &enum rtl8365mb_table_op + * @addr: table address. For indexed tables, this selects the entry to acc= ess. + * For L2 read queries, it is ignored as input for MAC-based lookup + * methods and used as input for address-based lookup methods. On + * successful L2 queries, it is updated with the matched entry addr= ess. + * @method: L2 table lookup method, see &enum rtl8365mb_table_l2_method. + * Ignored for non-L2 tables. + * @port: for L2 read queries using method + * %RTL8365MB_TABLE_L2_METHOD_ADDR_NEXT_UC_PORT, restrict the search + * to entries associated with this source port. Ignored otherwise. + * @data: data buffer used to read from or write to the table. For L2 MAC + * lookups, this buffer provides the lookup key and receives the + * matched entry contents on success. + * @size: size of @data in 16-bit words + * + * This function provides unified access to the internal tables of the swi= tch. + * All tables except the L2 table are simple indexed tables, where @addr + * selects the entry and @op determines whether the access is a read or a + * write operation. + * + * The L2 table is a hash table and supports multiple lookup methods. For + * %RTL8365MB_TABLE_L2_METHOD_MAC, an entry is searched based on the MAC + * address and FID/VID fields provided in @data, using the same format as + * an L2 table entry. Address-based methods either read a specific entry + * (%RTL8365MB_TABLE_L2_METHOD_ADDR) or iterate over valid entries starting + * from @addr (%RTL8365MB_TABLE_L2_METHOD_ADDR_NEXT and variants). When us= ing + * %RTL8365MB_TABLE_L2_METHOD_ADDR_NEXT_UC_PORT, only entries associated w= ith + * the specified @port are considered. + * + * On successful L2 lookups, @addr is updated with the matched table addre= ss + * and @data contains the corresponding table entry. If no matching entry + * is found, -ENOENT is returned. + * + * The contents of @data are used as input when writing to tables or when + * specifying the lookup key for L2 MAC searches, and as output for all + * successful read operations. If an error occurs, the contents of @addr + * and @data are undefined. + * + * @size must match the size of the target table entry, expressed in 16-bit + * words. This function only validates that it is non-zero and fits in the + * available register space. + * + */ +int rtl8365mb_table_query(struct realtek_priv *priv, + enum rtl8365mb_table table, + enum rtl8365mb_table_op op, u16 *addr, + enum rtl8365mb_table_l2_method method, + u16 port, u16 *data, size_t size); + +#endif /* _REALTEK_RTL8365MB_TABLE_H */ --=20 2.53.0 From nobody Wed Apr 1 08:15:25 2026 Received: from mail-vk1-f182.google.com (mail-vk1-f182.google.com [209.85.221.182]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id F3057371895 for ; Tue, 31 Mar 2026 23:00:45 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.182 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774998049; cv=none; b=sHkCao8tAoVQksAEgnor6l23BLauMpByrHPwcDIKle6p7qBWrprT+g+7IMX0s5LMrWtuoDhsCaHiOnwLBVHVO9eGrnPxLopC/y6wCkc5Qv4o34Mvel5eh/szcMbCw57NtIk50zK+LEeEN4N77R3zIyib01Bh/d1jKH9trnFEgjY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774998049; c=relaxed/simple; bh=zS+mNc9c+Neu61UQzalBRkHFbhe85rZ871tygwe+gVg=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=Pn4JyE+l+Uf0+NJxAlSXywv1BZ5b/+sQ0x3SM0o04qiJBf4nysvO4SPBtNmGSfL24kUoyXbe283Nxj5OoGgTAbnSEsk4CfKTOMwHMbO9Vh9TnLXb8J0y2pHztIl53R/1VUAnq+m6CmhKmlsicb1myJc/N87RMbWoRiBvUGxhPyg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=UQYK4Gmh; arc=none smtp.client-ip=209.85.221.182 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="UQYK4Gmh" Received: by mail-vk1-f182.google.com with SMTP id 71dfb90a1353d-56b7043c97eso2475039e0c.1 for ; Tue, 31 Mar 2026 16:00:45 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1774998045; x=1775602845; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=LGLY/T0HFLxBmugFFgKJly1nixAlzC5XR0iq2QbIvpM=; b=UQYK4Gmhse+l/scA9mcNIyH1m41+I+vRcUXo6MSyyhYxD3F7O134HLDXhlYUkONSSJ J3nNOII+ExKKnoqGRnU46PfiaTe5SZ5RC8uBSgdjKGCxlVaSAUM/Y/ksx5MRhM/iPcdn XqIWtB9N9KQQd1h1O4O7v5Cs5j0zdWo1BJJduvbxmYMu21ue7Fd06TOedAYvIwUQeZcw /bfArglF9VkdWYWaFyYhITXyfdkyvEnrf0Ep/5dVh4f4D0+nNgEy+UUs2gty1OYFk+vK iuPW3bSRyDVP6aCKh7cUxnporbMICjc334bK6h4iXwzV0IATVwH99zlZVRyrzptr5NRJ y2YA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1774998045; x=1775602845; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=LGLY/T0HFLxBmugFFgKJly1nixAlzC5XR0iq2QbIvpM=; b=YyJxfckkWBJ4oBOZRgJ/CTut39Ip1ugFDN2HJYjENlZd3LED4VC2qEXlcnZ6+7h6xX CM01wqYgAtjfN4gVOxc0dsegyeLzrL40ANmCb+fDQhla4XQjKOySU6Z8t4Og4ta6scfF G+X2QogCNFI0yfNDeinx4YvxVNKp7zi+hmbiYQ1mFfS4j4APREo5h0iMWDj3gBbO5ml2 JknwD51fpvZRVLdkKnvhnHaRx3tIEhvYl8rKNMVam6aSwLJN0QEX3p94Ci4AcbRuWohM VsqMz3ZXhiBLdXK+qjfRReRDRr5G/qnfeJZxSFW53OD5B+kc0o7LmhxlArN8zExMq31k +YaA== X-Forwarded-Encrypted: i=1; AJvYcCWzYaWh05iJkv8zzvo5TQTddrn++zBtmTDC2pDDunfKlbb/0QPu1EoaMjScVANtEW4sLy4Hd2nvJgvh9pk=@vger.kernel.org X-Gm-Message-State: AOJu0Yw+Z9En8bbEQV1lBK7/XZKK2FeS3lyvwePwzwwmAbZSniHgP7BE veEm0dI3IPNtgNY3/MEaJCnqjOjSNn5LNsC+nMmyT0WpBWryXDATbVh8 X-Gm-Gg: ATEYQzzUhuzh4R5BwAv7Lh3xlJlqaG42N/gK4s5UQszEZYP+Nkvl9LenlGzyQ8hgZcm Ll0InuzFASpX2FYOZkBcmpq+T+yvGx5Kh9Aamd9jlr9znE3rsWaZXCImQBZ8TH2yq+AY+Qwgu9s Q1THBkNy4OUCVMlJnmvkYdGfOCPq7QmmQkNZJynrtlZJshNmC1s62jC+Q2Rsekcn22hxqMFcC+K Mf5Y4DXr9KHODW8rwIK/HfFY0E6i2SfhCgcAB2usV6wEd0Im8cA8d+SKFXnSSBu37SXOOF9K4gT 3CJxUI0YiyN3HLbShuaOFyTJTQ8zqW8Pqwls7i+wNrjkOa2U9NuZ8/DNrIFr+kYyMJJADoFAm2r ynFghS6qAGuQYKYSTNq9VFcf5C2mSJBi7gIHSvnx7bk0euaq9TlX/ykGDz6DDkPltsQf0bvdaFx TvyUWR1PM0MNWstUnXyqxOsC6fVA+ID/TVik1Ccds/Mm1SHuZo X-Received: by 2002:a05:6122:3b83:b0:56b:9083:4331 with SMTP id 71dfb90a1353d-56d8a97849bmr675853e0c.12.1774998044562; Tue, 31 Mar 2026 16:00:44 -0700 (PDT) Received: from tresc054937.tre-sc.gov.br ([187.65.210.13]) by smtp.gmail.com with ESMTPSA id 71dfb90a1353d-56d58a7ba96sm13948214e0c.17.2026.03.31.16.00.39 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 31 Mar 2026 16:00:43 -0700 (PDT) From: Luiz Angelo Daros de Luca Date: Tue, 31 Mar 2026 20:00:06 -0300 Subject: [net-next PATCH 06/10] net: dsa: realtek: rtl8365mb: add VLAN support Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260331-realtek_forward-v1-6-44fb63033b7e@gmail.com> References: <20260331-realtek_forward-v1-0-44fb63033b7e@gmail.com> In-Reply-To: <20260331-realtek_forward-v1-0-44fb63033b7e@gmail.com> To: Andrew Lunn , Vladimir Oltean , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , Linus Walleij , =?utf-8?q?Alvin_=C5=A0ipraga?= , Yury Norov , Rasmus Villemoes , Russell King Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org, Luiz Angelo Daros de Luca X-Mailer: b4 0.15.1 From: Alvin =C5=A0ipraga Realtek RTL8365MB switches (a.k.a. RTL8367C family) use two different structures for VLANs: - VLAN4K: A full table with 4096 entries defining port membership and tagging. - VLANMC: A smaller table with 32 entries used primarily for PVID assignment. In this hardware, a port's PVID must point to an index in the VLANMC table rather than a VID directly. Since the VLANMC table is limited to 32 entries, the driver implements a dynamic allocation scheme to maximize resource usage: - VLAN4K is treated by the driver as the source of truth for membership. - A VLANMC entry is only allocated when a port is configured to use a specific VID as its PVID. - VLANMC entries are deleted when no longer needed as a PVID by any port. Although VLANMC has a members field, the switch only checks membership in the VLAN4K table. However, when a corresponding VLAN entry also exists in VLANMC, this driver keeps both membership configurations in sync. VLANMC index 0, although a valid entry, is reserved in this driver as a neutral PVID value for ports not using a specific PVID. In the subsequent RTL8367D switch family, VLANMC table was removed and PVID assignment was delegated to a dedicated set of registers. All ports start isolated, forwarding exclusively to CPU ports, and with VLAN transparent, ignoring VLAN membership. Once a member in a bridge, the port isolation is expanded to include the bridge members. When that bridge enables VLAN filtering, the VLAN transparent feature is disabled, letting the switch filter based on VLAN setup. Co-developed-by: Alvin =C5=A0ipraga Signed-off-by: Alvin =C5=A0ipraga Signed-off-by: Luiz Angelo Daros de Luca --- drivers/net/dsa/realtek/Makefile | 1 + drivers/net/dsa/realtek/rtl8365mb_main.c | 256 ++++++++++ drivers/net/dsa/realtek/rtl8365mb_vlan.c | 805 +++++++++++++++++++++++++++= ++++ drivers/net/dsa/realtek/rtl8365mb_vlan.h | 30 ++ 4 files changed, 1092 insertions(+) diff --git a/drivers/net/dsa/realtek/Makefile b/drivers/net/dsa/realtek/Mak= efile index 99654c4c5a3d..b7fc4e852fd8 100644 --- a/drivers/net/dsa/realtek/Makefile +++ b/drivers/net/dsa/realtek/Makefile @@ -18,3 +18,4 @@ endif obj-$(CONFIG_NET_DSA_REALTEK_RTL8365MB) +=3D rtl8365mb.o rtl8365mb-objs :=3D rtl8365mb_main.o \ rtl8365mb_table.o \ + rtl8365mb_vlan.o \ diff --git a/drivers/net/dsa/realtek/rtl8365mb_main.c b/drivers/net/dsa/rea= ltek/rtl8365mb_main.c index 530ff5503543..c604bd744d38 100644 --- a/drivers/net/dsa/realtek/rtl8365mb_main.c +++ b/drivers/net/dsa/realtek/rtl8365mb_main.c @@ -104,6 +104,7 @@ #include "realtek-smi.h" #include "realtek-mdio.h" #include "rtl83xx.h" +#include "rtl8365mb_vlan.h" =20 /* Family-specific data and limits */ #define RTL8365MB_PHYADDRMAX 7 @@ -292,6 +293,67 @@ #define RTL8365MB_MSTI_CTRL_PORT_STATE_MASK(_physport) \ (0x3 << RTL8365MB_MSTI_CTRL_PORT_STATE_OFFSET((_physport))) =20 +/* Miscellaneous port configuration register, incl. VLAN egress mode */ +#define RTL8365MB_PORT_MISC_CFG_REG_BASE 0x000E +#define RTL8365MB_PORT_MISC_CFG_REG(_p) \ + (RTL8365MB_PORT_MISC_CFG_REG_BASE + ((_p) << 5)) +#define RTL8365MB_PORT_MISC_CFG_SMALL_TAG_IPG_MASK 0x8000 +#define RTL8365MB_PORT_MISC_CFG_TX_ITFSP_MODE_MASK 0x4000 +#define RTL8365MB_PORT_MISC_CFG_FLOWCTRL_INDEP_MASK 0x2000 +#define RTL8365MB_PORT_MISC_CFG_DOT1Q_REMARK_ENABLE_MASK 0x1000 +#define RTL8365MB_PORT_MISC_CFG_INGRESSBW_FLOWCTRL_MASK 0x0800 +#define RTL8365MB_PORT_MISC_CFG_INGRESSBW_IFG_MASK 0x0400 +#define RTL8365MB_PORT_MISC_CFG_RX_SPC_MASK 0x0200 +#define RTL8365MB_PORT_MISC_CFG_CRC_SKIP_MASK 0x0100 +#define RTL8365MB_PORT_MISC_CFG_PKTGEN_TX_FIRST_MASK 0x0080 +#define RTL8365MB_PORT_MISC_CFG_MAC_LOOPBACK_MASK 0x0040 +/* See &rtl8365mb_vlan_egress_mode */ +#define RTL8365MB_PORT_MISC_CFG_VLAN_EGRESS_MODE_MASK 0x0030 +#define RTL8365MB_PORT_MISC_CFG_CONGESTION_SUSTAIN_TIME_MASK 0x000F + +/** + * enum rtl8365mb_vlan_egress_mode - port VLAN engress mode + * @RTL8365MB_VLAN_EGRESS_MODE_ORIGINAL: follow untag mask in VLAN4k table= entry + * @RTL8365MB_VLAN_EGRESS_MODE_KEEP: the VLAN tag format of egressed packe= ts + * will remain the same as their ingressed format, but the priority and VID + * fields may be altered + * @RTL8365MB_VLAN_EGRESS_MODE_PRI_TAG: always egress with priority tag + * @RTL8365MB_VLAN_EGRESS_MODE_REAL_KEEP: the VLAN tag format of egressed + * packets will remain the same as their ingressed format, and neither the + * priority nor VID fields can be altered + */ +enum rtl8365mb_vlan_egress_mode { + RTL8365MB_VLAN_EGRESS_MODE_ORIGINAL =3D 0, + RTL8365MB_VLAN_EGRESS_MODE_KEEP =3D 1, + RTL8365MB_VLAN_EGRESS_MODE_PRI_TAG =3D 2, + RTL8365MB_VLAN_EGRESS_MODE_REAL_KEEP =3D 3, +}; + +/* VLAN control register */ +#define RTL8365MB_VLAN_CTRL_REG 0x07A8 +#define RTL8365MB_VLAN_CTRL_EN_MASK 0x0001 + +/* VLAN ingress filter register */ +#define RTL8365MB_VLAN_INGRESS_REG 0x07A9 +#define RTL8365MB_VLAN_INGRESS_MASK GENMASK(10, 0) +#define RTL8365MB_VLAN_INGRESS_FILTER_PORT_EN_OFFSET(_p) (_p) +#define RTL8365MB_VLAN_INGRESS_FILTER_PORT_EN_MASK(_p) BIT(_p) + +/* VLAN "transparent" setting registers */ +#define RTL8365MB_VLAN_EGRESS_TRANSPARENT_REG_BASE 0x09D0 +#define RTL8365MB_VLAN_EGRESS_TRANSPARENT_REG(_p) \ + (RTL8365MB_VLAN_EGRESS_TRANSPARENT_REG_BASE + (_p)) + +/* Frame type filtering registers */ +#define RTL8365MB_VLAN_ACCEPT_FRAME_TYPE_BASE 0x07aa +#define RTL8365MB_VLAN_ACCEPT_FRAME_TYPE_REG(port) \ + (RTL8365MB_VLAN_ACCEPT_FRAME_TYPE_BASE + ((port) >> 3)) +/* required as FIELD_PREP cannot use non-constant masks */ +#define RTL8365MB_VLAN_ACCEPT_FRAME_TYPE_MASK(port) \ + (0x3 << RTL8365MB_VLAN_ACCEPT_FRAME_TYPE_OFFSET(port)) +#define RTL8365MB_VLAN_ACCEPT_FRAME_TYPE_OFFSET(port) \ + (((port) & 0x7) << 1) + /* MIB counter value registers */ #define RTL8365MB_MIB_COUNTER_BASE 0x1000 #define RTL8365MB_MIB_COUNTER_REG(_x) (RTL8365MB_MIB_COUNTER_BASE + (_x)) @@ -1196,6 +1258,183 @@ static void rtl8365mb_port_stp_state_set(struct dsa= _switch *ds, int port, val << RTL8365MB_MSTI_CTRL_PORT_STATE_OFFSET(port)); } =20 +static int rtl8365mb_port_set_transparent(struct realtek_priv *priv, + int igr_port, int egr_port, + bool enable) +{ + dev_dbg(priv->dev, "%s transparent VLAN from %d to %d\n", + enable ? "Enable" : "Disable", igr_port, egr_port); + + /* "Transparent" between the two ports means that packets forwarded by + * igr_port and egressed on egr_port will not be filtered by the usual + * VLAN membership settings. + */ + return regmap_update_bits(priv->map, + RTL8365MB_VLAN_EGRESS_TRANSPARENT_REG(egr_port), + BIT(igr_port), enable ? BIT(igr_port) : 0); +} + +static int rtl8365mb_port_set_ingress_filtering(struct realtek_priv *priv, + int port, bool enable) +{ + /* Ingress filtering enabled: Discard VLAN-tagged frames if the port is + * not a member of the VLAN with which the packet is associated. + * Untagged packets will also be discarded unless the port has a PVID + * programmed. Priority-tagged frames are treated as untagged frames. + * + * Ingress filtering disabled: Accept all tagged and untagged frames. + */ + return regmap_update_bits(priv->map, RTL8365MB_VLAN_INGRESS_REG, + RTL8365MB_VLAN_INGRESS_FILTER_PORT_EN_MASK(port), + enable ? + RTL8365MB_VLAN_INGRESS_FILTER_PORT_EN_MASK(port) : + 0); +} + +static int rtl8365mb_port_vlan_filtering(struct dsa_switch *ds, int port, + bool vlan_filtering, + struct netlink_ext_ack *extack) +{ + struct realtek_priv *priv =3D ds->priv; + struct dsa_port *dp; + int ret; + + dev_dbg(priv->dev, "port %d: %s VLAN filtering\n", port, + vlan_filtering ? "enable" : "disable"); + + /* (En|dis)able incoming packets filter, i.e. ignore VLAN membership */ + dsa_switch_for_each_available_port(dp, ds) { + /* after considering port isolation, if not filtering + * allow forwarding from port to dp->index ignoring + * VLAN membership. + */ + ret =3D rtl8365mb_port_set_transparent(priv, port, dp->index, + !vlan_filtering); + if (ret) + return ret; + } + + /* If the port is not in the member set, the frame will be dropped */ + return rtl8365mb_port_set_ingress_filtering(priv, port, + vlan_filtering); +} + +static int rtl8365mb_port_vlan_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan, + struct netlink_ext_ack *extack) +{ + bool untagged =3D !!(vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED); + bool pvid =3D !!(vlan->flags & BRIDGE_VLAN_INFO_PVID); + struct realtek_priv *priv =3D ds->priv; + int ret; + + dev_dbg(priv->dev, "add VLAN %d on port %d, %s, %s\n", + vlan->vid, port, untagged ? "untagged" : "tagged", + pvid ? "PVID" : "no PVID"); + /* add port to vlan4k. It knows nothing about PVID */ + ret =3D rtl8365mb_vlan_4k_port_add(ds, port, vlan, extack); + if (ret) + return ret; + + /* VlanMC knows nothing about untagged but it is required for PVID */ + ret =3D rtl8365mb_vlan_mc_port_add(ds, port, vlan, extack); + if (ret) + goto undo_vlan_4k; + + /* Set PVID if needed */ + if (pvid) { + ret =3D rtl8365mb_vlan_pvid_port_add(ds, port, vlan, extack); + if (ret) + goto undo_vlan_mc; + } + + return 0; + +undo_vlan_mc: + (void)rtl8365mb_vlan_mc_port_del(ds, port, vlan); + +undo_vlan_4k: + (void)rtl8365mb_vlan_4k_port_del(ds, port, vlan); + return ret; +} + +static int rtl8365mb_port_vlan_del(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan) +{ + bool untagged =3D !!(vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED); + bool pvid =3D !!(vlan->flags & BRIDGE_VLAN_INFO_PVID); + struct realtek_priv *priv =3D ds->priv; + int ret1, ret2, ret3; + + dev_dbg(priv->dev, "del VLAN %d on port %d, %s, %s\n", + vlan->vid, port, untagged ? "untagged" : "tagged", + pvid ? "PVID" : "no PVID"); + + ret1 =3D rtl8365mb_vlan_pvid_port_del(ds, port, vlan); + ret2 =3D rtl8365mb_vlan_mc_port_del(ds, port, vlan); + ret3 =3D rtl8365mb_vlan_4k_port_del(ds, port, vlan); + + return ret1 ?: ret2 ?: ret3; +} + +static int +rtl8365mb_port_set_vlan_egress_mode(struct realtek_priv *priv, int port, + enum rtl8365mb_vlan_egress_mode mode) +{ + u32 val; + + val =3D FIELD_PREP(RTL8365MB_PORT_MISC_CFG_VLAN_EGRESS_MODE_MASK, mode); + return regmap_update_bits(priv->map, + RTL8365MB_PORT_MISC_CFG_REG(port), + RTL8365MB_PORT_MISC_CFG_VLAN_EGRESS_MODE_MASK, val); +} + +/* VLAN support is always enabled in the switch. + * + * All ports starts with vlan-unaware state, letting non-bridge port forwa= rd + * to CPU. + * + */ +static int rtl8365mb_vlan_setup(struct dsa_switch *ds) +{ + struct realtek_priv *priv =3D ds->priv; + enum rtl8365mb_vlan_egress_mode mode; + struct dsa_port *dp; + int ret; + + dsa_switch_for_each_user_port(dp, ds) { + /* Disable vlan-filtering for all ports */ + ret =3D rtl8365mb_port_vlan_filtering(ds, dp->index, false, NULL); + if (ret) { + dev_err(priv->dev, + "Failed to disable vlan filtering on port %d\n", + dp->index); + return ret; + } + + /* The switch default is RTL8365MB_VLAN_EGRESS_MODE_REAL_KEEP, + * that forwards the packet as it was received. However, + * different untag settings will require the switch to update + * the tag. + */ + mode =3D RTL8365MB_VLAN_EGRESS_MODE_ORIGINAL; + ret =3D rtl8365mb_port_set_vlan_egress_mode(priv, dp->index, + mode); + if (ret) { + dev_err(priv->dev, + "Failed to set port %d egress mode\n", + dp->index); + return ret; + } + } + + /* VLAN is always enabled. */ + ret =3D regmap_update_bits(priv->map, RTL8365MB_VLAN_CTRL_REG, + RTL8365MB_VLAN_CTRL_EN_MASK, + FIELD_PREP(RTL8365MB_VLAN_CTRL_EN_MASK, 1)); + return ret; +} + static int rtl8365mb_port_set_learning(struct realtek_priv *priv, int port, bool enable) { @@ -2014,6 +2253,20 @@ static int rtl8365mb_setup(struct dsa_switch *ds) if (ret) goto out_teardown_irq; =20 + /* vlan config will only be effective for ports with vlan filtering */ + ds->configure_vlan_while_not_filtering =3D true; + /* Set up VLAN */ + ret =3D rtl8365mb_vlan_setup(ds); + if (ret) + goto out_teardown_irq; + + /* Set maximum packet length to 1536 bytes */ + ret =3D regmap_update_bits(priv->map, RTL8365MB_CFG0_MAX_LEN_REG, + RTL8365MB_CFG0_MAX_LEN_MASK, + FIELD_PREP(RTL8365MB_CFG0_MAX_LEN_MASK, 1536)); + if (ret) + goto out_teardown_irq; + ret =3D rtl83xx_setup_user_mdio(ds); if (ret) { dev_err(priv->dev, "could not set up MDIO bus\n"); @@ -2124,6 +2377,9 @@ static const struct dsa_switch_ops rtl8365mb_switch_o= ps =3D { .teardown =3D rtl8365mb_teardown, .phylink_get_caps =3D rtl8365mb_phylink_get_caps, .port_stp_state_set =3D rtl8365mb_port_stp_state_set, + .port_vlan_add =3D rtl8365mb_port_vlan_add, + .port_vlan_del =3D rtl8365mb_port_vlan_del, + .port_vlan_filtering =3D rtl8365mb_port_vlan_filtering, .get_strings =3D rtl8365mb_get_strings, .get_ethtool_stats =3D rtl8365mb_get_ethtool_stats, .get_sset_count =3D rtl8365mb_get_sset_count, diff --git a/drivers/net/dsa/realtek/rtl8365mb_vlan.c b/drivers/net/dsa/rea= ltek/rtl8365mb_vlan.c new file mode 100644 index 000000000000..1ac36c06dcf7 --- /dev/null +++ b/drivers/net/dsa/realtek/rtl8365mb_vlan.c @@ -0,0 +1,805 @@ +// SPDX-License-Identifier: GPL-2.0 +/* VLAN configuration interface for the rtl8365mb switch family + * + * Copyright (C) 2022 Alvin =C5=A0ipraga + * + * VLAN configuration takes place in two separate domains of the switch: t= he + * VLAN4k table and the VLAN membership configuration (MC) database. While= the + * VLAN4k table is exhaustive and can be fully populated with 4096 VLAN + * configurations, the same does not hold for the VLAN membership configur= ation + * database, which is limited to 32 entries. + * + * The switch will normally only use the VLAN4k table when making forwardi= ng + * decisions. The VLAN membership configuration database is a vestigial AS= IC + * design and is only used for a few specific features in the rtl8365mb + * family. This means that the limit of 32 entries should not hinder us in + * programming a huge number of VLANs into the switch. + * + * One necessary use of the VLAN membership configuration database is for = the + * programming of a port-based VLAN ID (PVID). The PVID is programmed on a + * per-port basis via register field, which refers to a specific VLAN memb= ership + * configuration via an index 0~31. In order to maintain coherent behaviou= r on a + * port with a PVID, it is necessary to keep the VLAN configuration synchr= onized + * between the VLAN4k table and the VLAN membership configuration database. + * + * Since VLAN membership configs are a scarce resource, it will only be us= ed + * when strictly needed (i.e. a VLAN with members using PVID). Otherwise, = the + * VLAN4k will be enough. + * + * With some exceptions, the entries in both the VLAN4k table and the VLAN + * membership configuration database offer the same configuration options.= The + * differences are as follows: + * + * 1. VLAN4k entries can specify whether to use Independent or Shared VLAN + * Learning (IVL or SVL respectively). VLAN membership config entries + * cannot. This underscores the fact that VLAN membership configs are n= ot + * involved in the learning process of the ASIC. + * + * 2. VLAN membership config entries use an "enhanced VLAN ID" (efid), whi= ch has + * a range 0~8191 compared with the standard 0~4095 range of the VLAN4k + * table. This underscores the fact that VLAN membership configs can be= used + * to group ports on a layer beyond the standard VLAN configuration, wh= ich + * may be useful for ACL rules which specify alternative forwarding + * decisions. + * + * VLANMC index 0 is reserved as a neutral PVID, used for vlan-unaware por= ts. + * + */ + +#include "rtl8365mb_vlan.h" +#include "rtl8365mb_table.h" +#include +#include + +/* CVLAN (i.e. VLAN4k) table entry layout, u16[3] */ +#define RTL8365MB_CVLAN_ENTRY_SIZE 3 /* 48-bits */ +#define RTL8365MB_CVLAN_ENTRY_D0_MBR_MASK GENMASK(7, 0) +#define RTL8365MB_CVLAN_ENTRY_D0_UNTAG_MASK GENMASK(15, 8) +#define RTL8365MB_CVLAN_ENTRY_D1_FID_MASK GENMASK(3, 0) +#define RTL8365MB_CVLAN_ENTRY_D1_VBPEN_MASK GENMASK(4, 4) +#define RTL8365MB_CVLAN_ENTRY_D1_VBPRI_MASK GENMASK(7, 5) +#define RTL8365MB_CVLAN_ENTRY_D1_ENVLANPOL_MASK GENMASK(8, 8) +#define RTL8365MB_CVLAN_ENTRY_D1_METERIDX_MASK GENMASK(13, 9) +#define RTL8365MB_CVLAN_ENTRY_D1_IVL_SVL_MASK GENMASK(14, 14) +/* extends RTL8365MB_CVLAN_ENTRY_D0_MBR_MASK */ +#define RTL8365MB_CVLAN_ENTRY_D2_MBR_EXT_MASK GENMASK(2, 0) +/* extends RTL8365MB_CVLAN_ENTRY_D0_UNTAG_MASK */ +#define RTL8365MB_CVLAN_ENTRY_D2_UNTAG_EXT_MASK GENMASK(5, 3) +/* extends RTL8365MB_CVLAN_ENTRY_D1_METERIDX_MASK */ +#define RTL8365MB_CVLAN_ENTRY_D2_METERIDX_EXT_MASK GENMASK(6, 6) + +/* VLAN member configuration registers 0~31, u16[3] */ +#define RTL8365MB_VLAN_MC_BASE 0x0728 +#define RTL8365MB_VLAN_MC_ENTRY_SIZE 4 /* 64-bit */ +#define RTL8365MB_VLAN_MC_REG(index) \ + (RTL8365MB_VLAN_MC_BASE + \ + (RTL8365MB_VLAN_MC_ENTRY_SIZE * (index))) +#define RTL8365MB_VLAN_MC_D0_MBR_MASK GENMASK(10, 0) +#define RTL8365MB_VLAN_MC_D1_FID_MASK GENMASK(3, 0) + +#define RTL8365MB_VLAN_MC_D2_VBPEN_MASK GENMASK(0, 0) +#define RTL8365MB_VLAN_MC_D2_VBPRI_MASK GENMASK(3, 1) +#define RTL8365MB_VLAN_MC_D2_ENVLANPOL_MASK GENMASK(4, 4) +#define RTL8365MB_VLAN_MC_D2_METERIDX_MASK GENMASK(10, 5) +#define RTL8365MB_VLAN_MC_D3_EVID_MASK GENMASK(12, 0) + +/* Some limits for VLAN4k/VLAN membership config entries */ +#define RTL8365MB_PRIORITYMAX 7 +#define RTL8365MB_FIDMAX 15 +#define RTL8365MB_METERMAX 63 +#define RTL8365MB_VLAN_MCMAX 31 + +/* RTL8367S supports 4k vlans (vid<=3D4095) and 32 enhanced vlans + * for VIDs up to 8191 + */ +#define RTL8365MB_MAX_4K_VID 0x0FFF /* 4095 */ +#define RTL8365MB_MAX_MC_VID 0x1FFF /* 8191 */ + + /* Port-based VID registers 0~5 - each one holds an MC index for two port= s */ +#define RTL8365MB_VLAN_PVID_CTRL_BASE 0x0700 +#define RTL8365MB_VLAN_PVID_CTRL_REG(_p) \ + (RTL8365MB_VLAN_PVID_CTRL_BASE + ((_p) >> 1)) +#define RTL8365MB_VLAN_PVID_CTRL_PORT0_MCIDX_MASK 0x001F +#define RTL8365MB_VLAN_PVID_CTRL_PORT1_MCIDX_MASK 0x1F00 +#define RTL8365MB_VLAN_PVID_CTRL_PORT_MCIDX_OFFSET(_p) \ + (((_p) & 1) << 3) +#define RTL8365MB_VLAN_PVID_CTRL_PORT_MCIDX_MASK(_p) \ + (0x1F << RTL8365MB_VLAN_PVID_CTRL_PORT_MCIDX_OFFSET(_p)) + +/* Frame type filtering registers */ +#define RTL8365MB_VLAN_ACCEPT_FRAME_TYPE_BASE 0x07aa +#define RTL8365MB_VLAN_ACCEPT_FRAME_TYPE_REG(port) \ + (RTL8365MB_VLAN_ACCEPT_FRAME_TYPE_BASE + ((port) >> 3)) +/* required as FIELD_PREP cannot use non-constant masks */ +#define RTL8365MB_VLAN_ACCEPT_FRAME_TYPE_MASK(port) \ + (0x3 << RTL8365MB_VLAN_ACCEPT_FRAME_TYPE_OFFSET(port)) +#define RTL8365MB_VLAN_ACCEPT_FRAME_TYPE_OFFSET(port) \ + (((port) & 0x7) << 1) + +/** + * struct rtl8365mb_vlan4k - VLAN4k table entry + * @vid: VLAN ID (0~4095) + * @member: port mask of ports in this VLAN + * @untag: port mask of ports which untag on egress + * @fid: filter ID - only used with SVL (unused) + * @priority: priority classification (unused) + * @priority_en: enable priority (unused) + * @policing_en: enable policing (unused) + * @ivl_en: enable IVL instead of default SVL + * @meteridx: metering index (unused) + * + * This structure is used to get/set entries in the VLAN4k table. The + * VLAN4k table dictates the VLAN configuration for the switch for the + * vast majority of features. + */ +struct rtl8365mb_vlan4k { + u16 vid; + u16 member; + u16 untag; + u8 fid : 4; + u8 priority : 3; + u8 priority_en : 1; + u8 policing_en : 1; + u8 ivl_en : 1; + u8 meteridx : 6; +}; + +/** + * struct rtl8365mb_vlanmc - VLAN membership config + * @evid: Enhanced VLAN ID (0~8191) + * @member: port mask of ports in this VLAN + * @fid: filter ID - only used with SVL (unused) + * @priority: priority classification (unused) + * @priority_en: enable priority (unused) + * @policing_en: enable policing (unused) + * @meteridx: metering index (unused) + * + * This structure is used to get/set entries in the VLAN membership + * configuration database. This feature is largely vestigial, but + * still needed for at least the following features: + * - PVID configuration + * - ACL configuration + * - selection of VLAN by the CPU tag when VSEL=3D1, although the switch + * can also select VLAN based on the VLAN tag if VSEL=3D0 + * + * This is a low-level structure and it is recommended to interface with + * the VLAN membership config database via &struct rtl8365mb_vlanmc_entry. + */ +struct rtl8365mb_vlanmc { + u16 evid; + u16 member; + u8 fid : 4; + u8 priority : 3; + u8 priority_en : 1; + u8 policing_en : 1; + u8 meteridx : 6; +}; + +enum rtl8365mb_frame_ingress { + RTL8365MB_FRAME_TYPE_ANY_FRAME =3D 0, + RTL8365MB_FRAME_TYPE_TAGGED_ONLY, + RTL8365MB_FRAME_TYPE_UNTAGGED_ONLY, +}; + +static int rtl8365mb_vlan_4k_read(struct realtek_priv *priv, u16 vid, + struct rtl8365mb_vlan4k *vlan4k) +{ + u16 data[RTL8365MB_CVLAN_ENTRY_SIZE]; + int ret; + + ret =3D rtl8365mb_table_query(priv, RTL8365MB_TABLE_CVLAN, + RTL8365MB_TABLE_OP_READ, &vid, 0, 0, + data, ARRAY_SIZE(data)); + if (ret) + return ret; + + /* Unpack table entry */ + memset(vlan4k, 0, sizeof(*vlan4k)); + vlan4k->vid =3D vid; + vlan4k->member =3D + FIELD_GET(RTL8365MB_CVLAN_ENTRY_D0_MBR_MASK, data[0]) | + (FIELD_GET(RTL8365MB_CVLAN_ENTRY_D2_MBR_EXT_MASK, data[2]) + << FIELD_WIDTH(RTL8365MB_CVLAN_ENTRY_D0_MBR_MASK)); + vlan4k->untag =3D + FIELD_GET(RTL8365MB_CVLAN_ENTRY_D0_UNTAG_MASK, data[0]) | + (FIELD_GET(RTL8365MB_CVLAN_ENTRY_D2_UNTAG_EXT_MASK, data[2]) + << FIELD_WIDTH(RTL8365MB_CVLAN_ENTRY_D0_UNTAG_MASK)); + vlan4k->fid =3D FIELD_GET(RTL8365MB_CVLAN_ENTRY_D1_FID_MASK, data[1]); + vlan4k->priority_en =3D + FIELD_GET(RTL8365MB_CVLAN_ENTRY_D1_VBPEN_MASK, data[1]); + vlan4k->priority =3D + FIELD_GET(RTL8365MB_CVLAN_ENTRY_D1_VBPRI_MASK, data[1]); + vlan4k->policing_en =3D + FIELD_GET(RTL8365MB_CVLAN_ENTRY_D1_ENVLANPOL_MASK, data[1]); + vlan4k->meteridx =3D + FIELD_GET(RTL8365MB_CVLAN_ENTRY_D1_METERIDX_MASK, data[1]) | + (FIELD_GET(RTL8365MB_CVLAN_ENTRY_D2_METERIDX_EXT_MASK, data[2]) + << FIELD_WIDTH(RTL8365MB_CVLAN_ENTRY_D1_METERIDX_MASK)); + vlan4k->ivl_en =3D + FIELD_GET(RTL8365MB_CVLAN_ENTRY_D1_IVL_SVL_MASK, data[1]); + + return 0; +} + +static int rtl8365mb_vlan_4k_write(struct realtek_priv *priv, + const struct rtl8365mb_vlan4k *vlan4k) +{ + u16 data[RTL8365MB_CVLAN_ENTRY_SIZE] =3D { 0 }; + u16 val; + u16 vid; + + if (vlan4k->fid > RTL8365MB_FIDMAX || + vlan4k->priority > RTL8365MB_PRIORITYMAX || + vlan4k->meteridx > RTL8365MB_METERMAX) + return -EINVAL; + + /* Pack table entry value */ + data[0] |=3D + FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D0_MBR_MASK, vlan4k->member); + data[0] |=3D + FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D0_UNTAG_MASK, vlan4k->untag); + + data[1] |=3D FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D1_FID_MASK, vlan4k->fid); + data[1] |=3D FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D1_VBPEN_MASK, + vlan4k->priority_en); + data[1] |=3D FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D1_VBPRI_MASK, + vlan4k->priority); + data[1] |=3D FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D1_ENVLANPOL_MASK, + vlan4k->policing_en); + data[1] |=3D FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D1_METERIDX_MASK, + vlan4k->meteridx); + data[1] |=3D FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D1_IVL_SVL_MASK, + vlan4k->ivl_en); + + data[2] |=3D FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D2_MBR_EXT_MASK, + vlan4k->member >> + FIELD_WIDTH(RTL8365MB_CVLAN_ENTRY_D0_MBR_MASK)); + data[2] |=3D FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D2_UNTAG_EXT_MASK, + vlan4k->untag >> + FIELD_WIDTH(RTL8365MB_CVLAN_ENTRY_D0_UNTAG_MASK)); + val =3D FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D2_METERIDX_EXT_MASK, + vlan4k->meteridx >> + FIELD_WIDTH(RTL8365MB_CVLAN_ENTRY_D1_METERIDX_MASK)); + data[2] |=3D val; + + vid =3D vlan4k->vid; + return rtl8365mb_table_query(priv, RTL8365MB_TABLE_CVLAN, + RTL8365MB_TABLE_OP_WRITE, &vid, 0, 0, + data, ARRAY_SIZE(data)); +} + +#define RTL_VLAN_ERR(msg) \ + do { \ + const char *__msg =3D (msg); \ + \ + if (extack) \ + NL_SET_ERR_MSG_FMT_MOD(extack, "%s", __msg); \ + dev_err(priv->dev, "%s", __msg); \ + } while (0) + +static int +rtl8365mb_vlan_4k_port_set(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan, + struct netlink_ext_ack *extack, + bool include) +{ + struct realtek_priv *priv =3D ds->priv; + struct rtl8365mb_vlan4k vlan4k =3D {0}; + int ret; + + dev_dbg(priv->dev, "%s VLAN %d 4K on port %d\n", + include ? "add" : "del", + vlan->vid, port); + + if (vlan->vid > RTL8365MB_MAX_4K_VID) { + RTL_VLAN_ERR("VLAN ID greater than " + __stringify(RTL8365MB_MAX_4K_VID)); + return -EINVAL; + } + + ret =3D rtl8365mb_vlan_4k_read(priv, vlan->vid, &vlan4k); + if (ret) { + RTL_VLAN_ERR("Failed to read VLAN 4k table"); + return ret; + } + + if (include) + vlan4k.member |=3D BIT(port); + else + vlan4k.member &=3D ~BIT(port); + + if (include && (vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED)) + vlan4k.untag |=3D BIT(port); + else + vlan4k.untag &=3D ~BIT(port); + vlan4k.ivl_en =3D true; /* always use Independent VLAN Learning */ + + ret =3D rtl8365mb_vlan_4k_write(priv, &vlan4k); + if (ret) { + RTL_VLAN_ERR("Failed to write VLAN 4k table"); + return ret; + } + + return 0; +} + +int rtl8365mb_vlan_4k_port_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan, + struct netlink_ext_ack *extack) +{ + return rtl8365mb_vlan_4k_port_set(ds, port, vlan, extack, true); +} + +int rtl8365mb_vlan_4k_port_del(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan) +{ + return rtl8365mb_vlan_4k_port_set(ds, port, vlan, NULL, false); +} + +/** + * rtl8365mb_vlan_mc_find() - find VLANMC index by VID or the first free i= ndex + * + * @priv: realtek_priv pointer + * @vid: VLAN ID + * @index: found index + * @first_free: found free index + * + * If a VLAN MC entry using @vid was found, @index will return the matched= index + * and @first_free is undefined. If not found, @index will return 0 and + * @first_free will return the first found free index in VLAN MC or 0 if t= he + * table is full. + * + * Although 0 is a valid VLAN MC index, it is reserved for ports without P= VID, + * including standalone, non-member ports. + * + * Both @index and @first_free will be in the * 1..@RTL8365MB_VLAN_MCMAX r= ange. + * + * Return: Returns 0 on success, a negative error on failure. + * + */ +static int rtl8365mb_vlan_mc_find(struct realtek_priv *priv, u16 vid, + u8 *index, u8 *first_free) +{ + u32 vlan_entry_d3; + u8 vlanmc_idx; + u16 evid; + int ret; + + if (!index) + return -EINVAL; + if (!first_free) + return -EINVAL; + if (!vid) + return -EINVAL; + + *index =3D 0; + *first_free =3D 0; + + /* look for existing entry or an empty one */ + /* vlanmc index 0 is reserved as a neutral PVID value. + * Non-PVID ports can still reach the CPU via VLAN + * transparent mode. + **/ + for (vlanmc_idx =3D 1; vlanmc_idx <=3D RTL8365MB_VLAN_MCMAX; vlanmc_idx++= ) { + /* just read the 4th word, where the evid is */ + ret =3D regmap_read(priv->map, + RTL8365MB_VLAN_MC_REG(vlanmc_idx) + 3, + &vlan_entry_d3); + if (ret) + return ret; + + evid =3D FIELD_GET(RTL8365MB_VLAN_MC_D3_EVID_MASK, vlan_entry_d3); + + if (evid =3D=3D vid) { + *index =3D vlanmc_idx; + return 0; + } + + if (evid =3D=3D 0x0 && *first_free < 1) + *first_free =3D vlanmc_idx; + } + return 0; +} + +static int rtl8365mb_vlan_port_set_pvid(struct realtek_priv *priv, + int port, u16 vlanmc_idx) +{ + int ret; + u32 val; + + val =3D vlanmc_idx << RTL8365MB_VLAN_PVID_CTRL_PORT_MCIDX_OFFSET(port); + ret =3D regmap_update_bits(priv->map, + RTL8365MB_VLAN_PVID_CTRL_REG(port), + RTL8365MB_VLAN_PVID_CTRL_PORT_MCIDX_MASK(port), + val); + if (ret) + return ret; + + return 0; +} + +static int rtl8365mb_vlan_port_get_pvid(struct realtek_priv *priv, + int port, u8 *vlanmc_idx) +{ + u32 data; + int ret; + + ret =3D regmap_read(priv->map, RTL8365MB_VLAN_PVID_CTRL_REG(port), &data); + if (ret) + return ret; + + *vlanmc_idx =3D (data & RTL8365MB_VLAN_PVID_CTRL_PORT_MCIDX_MASK(port)) + >> RTL8365MB_VLAN_PVID_CTRL_PORT_MCIDX_OFFSET(port); + + return 0; +} + +/** + * rtl8365mb_vlan_mc_pvid_members() - Get a bitmap of vlan PVID members + * + * @ds: DSA switch + * @vlanmc_idx: the index of a VLAN in VLAN MC table + * @members: the returned bitmap of members that have PVID status + * + * This function iterates over DSA ports and creates a bitmap representati= on of + * those ports that have PVID pointing to this VLAN (identified by its tab= le + * index and not VID). If you need to get the table index from VID, see + * rtl8365mb_vlan_mc_find() + * + * Return: Returns 0 on success, a negative error on failure. + **/ +static int rtl8365mb_vlan_mc_pvid_members(struct dsa_switch *ds, + u8 vlanmc_idx, u16 *members) +{ + struct realtek_priv *priv =3D ds->priv; + struct dsa_port *dp; + u8 _vlanmc_idx; + int ret; + + if (!members) + return -EINVAL; + + *members =3D 0; + + dsa_switch_for_each_port(dp, ds) { + ret =3D rtl8365mb_vlan_port_get_pvid(priv, dp->index, + &_vlanmc_idx); + if (ret) + return ret; + + if (_vlanmc_idx =3D=3D vlanmc_idx) + *members |=3D BIT(dp->index); + } + + return 0; +} + +static int +rtl8365mb_vlan_port_set_framefilter(struct realtek_priv *priv, + int port, + enum rtl8365mb_frame_ingress accepted_frame) +{ + /* Even if ACCEPT_FRAME_TYPE_ANY, the switch will still check if the + * port is a member of vlan PVID + */ + accepted_frame =3D accepted_frame + << RTL8365MB_VLAN_ACCEPT_FRAME_TYPE_OFFSET(port); + + return regmap_update_bits(priv->map, + RTL8365MB_VLAN_ACCEPT_FRAME_TYPE_REG(port), + RTL8365MB_VLAN_ACCEPT_FRAME_TYPE_MASK(port), + accepted_frame); +} + +static int rtl8365mb_vlan_mc_read(struct realtek_priv *priv, u32 index, + struct rtl8365mb_vlanmc *vlanmc) +{ + u16 data[RTL8365MB_VLAN_MC_ENTRY_SIZE]; + int ret; + + if (index > RTL8365MB_VLAN_MCMAX) + return -EINVAL; + + ret =3D regmap_bulk_read(priv->map, RTL8365MB_VLAN_MC_REG(index), &data, + RTL8365MB_VLAN_MC_ENTRY_SIZE); + if (ret) + return ret; + + vlanmc->member =3D FIELD_GET(RTL8365MB_VLAN_MC_D0_MBR_MASK, data[0]); + vlanmc->fid =3D FIELD_GET(RTL8365MB_VLAN_MC_D1_FID_MASK, data[1]); + vlanmc->priority =3D FIELD_GET(RTL8365MB_VLAN_MC_D2_VBPRI_MASK, data[2]); + vlanmc->evid =3D FIELD_GET(RTL8365MB_VLAN_MC_D3_EVID_MASK, data[3]); + + return 0; +} + +int rtl8365mb_vlan_pvid_port_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan, + struct netlink_ext_ack *extack) +{ + bool pvid =3D !!(vlan->flags & BRIDGE_VLAN_INFO_PVID); + enum rtl8365mb_frame_ingress accepted_frame; + struct realtek_priv *priv =3D ds->priv; + u8 _unused_first_free_idx; + u8 vlanmc_idx; + int ret; + + if (!pvid) + return 0; + + /* look for existing entry */ + ret =3D rtl8365mb_vlan_mc_find(priv, vlan->vid, &vlanmc_idx, + &_unused_first_free_idx); + if (ret) { + RTL_VLAN_ERR("Failed to find a VLAN MC table index"); + return ret; + } + + if (!vlanmc_idx) { + RTL_VLAN_ERR("VLAN should already exist in VLAN MC"); + return ret; + } + + ret =3D rtl8365mb_vlan_port_set_pvid(priv, port, vlanmc_idx); + if (ret) { + RTL_VLAN_ERR("Failed to set port PVID"); + return ret; + } + + /* Changing accept frame is what enables PVID (if not enabled before) */ + accepted_frame =3D RTL8365MB_FRAME_TYPE_ANY_FRAME; + ret =3D rtl8365mb_vlan_port_set_framefilter(priv, port, accepted_frame); + if (ret) { + RTL_VLAN_ERR("Failed to set port frame filter"); + return ret; + } + + return 0; +} + +int rtl8365mb_vlan_pvid_port_del(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan) +{ + enum rtl8365mb_frame_ingress accepted_frame; + struct netlink_ext_ack *extack =3D NULL; + struct realtek_priv *priv =3D ds->priv; + struct rtl8365mb_vlanmc vlanmc =3D {0}; + u8 vlanmc_idx; + int ret; + + ret =3D rtl8365mb_vlan_port_get_pvid(priv, port, &vlanmc_idx); + if (ret) + return ret; + + /* Port is not using PVID. Nothing to remove. */ + if (!vlanmc_idx) + return 0; + + ret =3D rtl8365mb_vlan_mc_read(priv, vlanmc_idx, &vlanmc); + if (ret) { + RTL_VLAN_ERR("Failed to read VLAN MC table"); + return ret; + } + + /* We are leaving a non PVID vlan, Nothing to remove. */ + if (vlanmc.evid !=3D vlan->vid) + return 0; + + /* Changing accept frame is what really removes PVID */ + accepted_frame =3D RTL8365MB_FRAME_TYPE_TAGGED_ONLY; + ret =3D rtl8365mb_vlan_port_set_framefilter(priv, port, accepted_frame); + if (ret) { + RTL_VLAN_ERR("Failed to set port frame filter"); + return ret; + } + + ret =3D rtl8365mb_vlan_port_set_pvid(priv, port, 0); + if (ret) { + RTL_VLAN_ERR("Failed to set port PVID to 0"); + return ret; + } + + return 0; +} + +static int rtl8365mb_vlan_mc_write(struct realtek_priv *priv, u32 index, + const struct rtl8365mb_vlanmc *vlanmc) +{ + u16 data[4] =3D { 0 }; + int ret; + + if (index > RTL8365MB_VLAN_MCMAX || + vlanmc->fid > RTL8365MB_FIDMAX || + vlanmc->priority > RTL8365MB_PRIORITYMAX || + vlanmc->meteridx > RTL8365MB_METERMAX) + return -EINVAL; + + data[0] |=3D FIELD_PREP(RTL8365MB_VLAN_MC_D0_MBR_MASK, vlanmc->member); + data[1] |=3D FIELD_PREP(RTL8365MB_VLAN_MC_D1_FID_MASK, vlanmc->fid); + data[2] |=3D FIELD_PREP(RTL8365MB_VLAN_MC_D2_METERIDX_MASK, + vlanmc->meteridx); + data[2] |=3D FIELD_PREP(RTL8365MB_VLAN_MC_D2_ENVLANPOL_MASK, + vlanmc->policing_en); + data[2] |=3D + FIELD_PREP(RTL8365MB_VLAN_MC_D2_VBPRI_MASK, vlanmc->priority); + data[2] |=3D FIELD_PREP(RTL8365MB_VLAN_MC_D2_VBPEN_MASK, + vlanmc->priority_en); + data[3] |=3D FIELD_PREP(RTL8365MB_VLAN_MC_D3_EVID_MASK, vlanmc->evid); + + ret =3D regmap_bulk_write(priv->map, RTL8365MB_VLAN_MC_REG(index), &data, + RTL8365MB_VLAN_MC_ENTRY_SIZE); + + return ret; +} + +static int rtl8365mb_vlan_mc_erase(struct realtek_priv *priv, u32 index) +{ + u16 data[4] =3D { 0 }; + int ret; + + if (index > RTL8365MB_VLAN_MCMAX) + return -EINVAL; + + ret =3D regmap_bulk_write(priv->map, RTL8365MB_VLAN_MC_REG(index), &data, + RTL8365MB_VLAN_MC_ENTRY_SIZE); + + return ret; +} + +/** rtl8365mb_vlanmc_port_set() - include or exclude a port from vlanMC + * @ds: dsa switch + * @port: the port number + * @vlan: the vlan to include/exclude @port + * @extack: optional extack to return errors + * @include: whether to include or exclude @port + * + * This function is used to include/exclude ports to the vlanMC table. + * + * VlanMC stands for VLAN membership config and it is used exclusively for + * PVID. If @vlan members are not using PVID, this function will either + * remove or not create a new vlanMC entry. + * + * vlanMC members are kept in sync with vlan4k, although the switch only + * checks membership in vlan4k table. + * + * Port PVID and accepted frame type are updated as well. + * + * Return: Returns 0 on success, a negative error on failure. + * + */ +static +int rtl8365mb_vlan_mc_port_set(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan, + struct netlink_ext_ack *extack, + bool include) +{ + bool pvid =3D !!(vlan->flags & BRIDGE_VLAN_INFO_PVID); + struct realtek_priv *priv =3D ds->priv; + struct rtl8365mb_vlan4k vlan4k =3D {0}; + struct rtl8365mb_vlanmc vlanmc =3D {0}; + u16 pvid_members =3D 0; + u8 first_unused =3D 0; + u8 vlanmc_idx =3D 0; + int ret; + + dev_dbg(priv->dev, "%s VLAN %d MC on port %d\n", + include ? "add" : "del", + vlan->vid, port); + + if (vlan->vid > RTL8365MB_MAX_MC_VID) { + RTL_VLAN_ERR("VLAN ID greater than " + __stringify(RTL8365MB_MAX_MC_VID)); + return -EINVAL; + } + + /* look for existing entry or an empty slot */ + ret =3D rtl8365mb_vlan_mc_find(priv, vlan->vid, &vlanmc_idx, + &first_unused); + if (ret) { + RTL_VLAN_ERR("Failed to find a VLAN MC table index"); + return ret; + } + + if (vlanmc_idx) { + ret =3D rtl8365mb_vlan_mc_read(priv, vlanmc_idx, &vlanmc); + if (ret) { + RTL_VLAN_ERR("Failed to read VLAN MC table"); + return ret; + } + } else if (include) { + /* for now, vlan_mc is only required for PVID. Defer allocation + * until at least one port uses PVID. + */ + if (!pvid) { + dev_dbg(priv->dev, + "Not creating VlanMC for vlan %d until a port uses PVID (%d does not)\= n", + vlan->vid, port); + return 0; + } + + if (!first_unused) { + RTL_VLAN_ERR("All VLAN MC entries (" + __stringify(RTL8365MB_VLAN_MCMAX + 1) + ") are in use."); + return -E2BIG; + } + + /* Retrieve vlan4k members as we might have deferred VlanMC + * before. + */ + if (vlan->vid <=3D RTL8365MB_MAX_4K_VID) { + ret =3D rtl8365mb_vlan_4k_read(priv, vlan->vid, &vlan4k); + if (ret) { + RTL_VLAN_ERR("Failed to read VLAN 4k table"); + return ret; + } + } + + vlanmc_idx =3D first_unused; + vlanmc.evid =3D vlan->vid; + + /* for new vlan_mc, sync current vlan4k members, + * although only vlan4k members matter. + */ + vlanmc.member |=3D vlan4k.member; + } else /* excluding and VLANMC not found */ { + return 0; + } + + ret =3D rtl8365mb_vlan_mc_pvid_members(ds, vlanmc_idx, + &pvid_members); + if (ret) { + RTL_VLAN_ERR("Failed to read VLANMC PVID members"); + return ret; + } + dev_dbg(priv->dev, + "VLAN %d (idx: %d) PVID curr members: %08x\n", + vlan->vid, vlanmc_idx, pvid_members); + + /* here we either have an existing VLANMC (with PVID members) or the + * added port is using this VLAN as PVID + */ + if (include) { + vlanmc.member |=3D BIT(port); + if (pvid) + pvid_members |=3D BIT(port); + } else { + vlanmc.member &=3D ~BIT(port); + pvid_members &=3D ~BIT(port); + } + + /* just like we don't need to create a VLAN_MC when there is no port + * using it as PVID, we can erase it when there is no more port using + * it as PVID. + */ + if (!pvid_members) { + dev_dbg(priv->dev, + "Clearing VlanMC index %d previously used by VID %d\n", + vlanmc_idx, vlan->vid); + ret =3D rtl8365mb_vlan_mc_erase(priv, vlanmc_idx); + } else { + dev_dbg(priv->dev, + "Saving VlanMC index %d with VID %d\n", + vlanmc_idx, vlan->vid); + ret =3D rtl8365mb_vlan_mc_write(priv, vlanmc_idx, &vlanmc); + } + if (ret) { + RTL_VLAN_ERR("Failed to write vlan MC entry"); + return ret; + } + + return 0; +} + +int rtl8365mb_vlan_mc_port_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan, + struct netlink_ext_ack *extack) +{ + return rtl8365mb_vlan_mc_port_set(ds, port, vlan, extack, true); +} + +int rtl8365mb_vlan_mc_port_del(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan) +{ + return rtl8365mb_vlan_mc_port_set(ds, port, vlan, NULL, false); +} diff --git a/drivers/net/dsa/realtek/rtl8365mb_vlan.h b/drivers/net/dsa/rea= ltek/rtl8365mb_vlan.h new file mode 100644 index 000000000000..bd0cf4c804c8 --- /dev/null +++ b/drivers/net/dsa/realtek/rtl8365mb_vlan.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* VLAN configuration interface for the rtl8365mb switch family + * + * Copyright (C) 2022 Alvin =C5=A0ipraga + * + */ + +#ifndef _REALTEK_RTL8365MB_VLAN_H +#define _REALTEK_RTL8365MB_VLAN_H + +#include + +#include "realtek.h" + +int rtl8365mb_vlan_4k_port_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan, + struct netlink_ext_ack *extack); +int rtl8365mb_vlan_4k_port_del(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan); +int rtl8365mb_vlan_mc_port_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan, + struct netlink_ext_ack *extack); +int rtl8365mb_vlan_mc_port_del(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan); +int rtl8365mb_vlan_pvid_port_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan, + struct netlink_ext_ack *extack); +int rtl8365mb_vlan_pvid_port_del(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan); +#endif /* _REALTEK_RTL8365MB_VLAN_H */ --=20 2.53.0 From nobody Wed Apr 1 08:15:25 2026 Received: from mail-vk1-f170.google.com (mail-vk1-f170.google.com [209.85.221.170]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 2D9C04611CE for ; Tue, 31 Mar 2026 23:00:52 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.170 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774998053; cv=none; b=udl0+C4JqOrXyJedJ26RQCH9xhjiL975Wmc09Gvp3ujiF9UPXy8a4HqpOwu4fcO3sEtn0+vFxJ3f6bgzTt5fdrEmfuaPE6IDbU+F7bPt37Kd2xeyRcgb4LSaHqI//BZVk4Ahk178tT2sEWnTFu/72BWxlQlkRXrjWX1Oa3+vOck= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774998053; c=relaxed/simple; bh=tN8N0/Pfy9DaP4Rgpo4ivkxeOwe/BNzpIiyJBucuD/0=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=ZZR9LGkZEKh2fpqxg0mziqQn+IgnIJmFZjOXI2SQZjrXburAUbpQZLPzUBa/laj4yGLaHrDEAW9b7744dd3te8Ob17O8EmNG946bZbgZiNUOaNO4znRFJcniQ7rQOYcuepUQEKh+qBPukt6iMEYMhwAonabOA3KmPOw5XFuNcss= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=C/Uwdjtq; arc=none smtp.client-ip=209.85.221.170 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="C/Uwdjtq" Received: by mail-vk1-f170.google.com with SMTP id 71dfb90a1353d-56d85e76d73so602738e0c.2 for ; Tue, 31 Mar 2026 16:00:52 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1774998051; x=1775602851; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=pJDO8dNemPTipqN+44lXNB8MtKgUhVoWcKpvwCy+p/c=; b=C/UwdjtqPZwwBslKS0bh73EBRmsI1Up8v24uvDO3cRMWaqYl3RADT3SzeXfnjLv/3t LdIwnTsgF8/6jVfd4EVCaz5sqOdgjPZYwF4LV1fp6R7MHlQ/QaPckbyLH9SOzNHnjdsn U2/mYcwMKmYry73XwogR3p/66akfsaJfMlasHrqNkoHzO61sEcFeR5prdY+KjPO8gJff 9MZMk8cAVkyGTrVfqAw1JCt8cfgouexpyPpTbKQP/TvC+MY0KLO9asCjCRsuoqNTkFBB F2p6gDlKfDGsqnFBRZ1myetyS/ThnQPMTdm+300YNy3Ex9Lb2xXzmR8l3RmXi/028q6V oWOQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1774998051; x=1775602851; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=pJDO8dNemPTipqN+44lXNB8MtKgUhVoWcKpvwCy+p/c=; b=GhTgIb6Vc4lU6TxBqxOseoxNKF0DVk1C/WloFMMabu7mILGovkl7cGKa3NFEv3uakE WRkdrN9jzCNlrAuFWMlS4vLlWFqQedKDOev1K1Lv36jX0QvP2IG5Y0Hc6E/AP7YXnzRf n+z6SZZgwGH3cEixoyadEYJZCMbEm9bgNWiSLpNQ4o9PxHP3afQ4q80pE34Xvpqz5JcO KyCX1GjAzh8uH6i9st9rTR5jBIVqum5EN+bv16gjE6IMmjdzcY0cIPS5WQxm9PUUrfwJ gdpZR4DhRhTUrwhhVUmevldQwJBjzfG92Zd1dx/MC9DSw1Cci53UhT7oURyv7XmU97Kw kXag== X-Forwarded-Encrypted: i=1; AJvYcCWlgRRCBHiIWNCAjBXafGs92D3wfFyWkoXZIbi0//RrUIU402jqexQ7KwzYohr+/rhCI7F96awhPZ+H5bg=@vger.kernel.org X-Gm-Message-State: AOJu0YxUDHlLNfyOr2oYDHESmqgvO6Pnhw6MW0AxTG+rE5c4cgAQjBTe Vveswt4CoFQugsL1opsliTfkAM+lhAaSEigNjjlhOh4Lgo6jx27orMQ7 X-Gm-Gg: ATEYQzxxa3+GiwUnvxb46ElmC8a3oGqtSEFDvgd+g+IN8giuNASLp+uBVwciG9BLpBM U8VSmFRky4Q4A1dNADk7uVdwSV3Fpr9kVK71Qp6ylTzzcGr4cSiXFaRmwWEyOIxlfhKPcpyPnfH icERkMLTH6JwMx6dMe3rvmNJysnqROgrHX9c4rYBXrQaL1LBfBq5C33TJcIl8a2D8KphI6bCLu3 Bd+syweOEpRHw260BC5jwlw/ozaPAt+qIeBSHTrbWfJ79C/T/9ufO2WtXsbSbyzCiWTwwwRvQS/ S2SnPjjMylJwS0chP0yzkZ3tmbjCfNBWQwX/x58ozQ88LQmkTXGvAKV7Z7WN0VUh3iR6mxZTS/j 92YiShJQXSy4zhIEXWMi1yRA7I5BNT67RVJuBdTCmTMOjeWdRd1VpdTNbvNEs9AxR8GyphNmKye H8MBE/IaIiHUV0JH3kL3pI1lvQAxbhlYy/wIadNw== X-Received: by 2002:a05:6122:e170:b0:56b:579c:82e with SMTP id 71dfb90a1353d-56d8a843538mr792707e0c.5.1774998050757; Tue, 31 Mar 2026 16:00:50 -0700 (PDT) Received: from tresc054937.tre-sc.gov.br ([187.65.210.13]) by smtp.gmail.com with ESMTPSA id 71dfb90a1353d-56d58a7ba96sm13948214e0c.17.2026.03.31.16.00.44 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 31 Mar 2026 16:00:49 -0700 (PDT) From: Luiz Angelo Daros de Luca Date: Tue, 31 Mar 2026 20:00:07 -0300 Subject: [net-next PATCH 07/10] net: dsa: realtek: rtl8365mb: add port_bridge_{join,leave} Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260331-realtek_forward-v1-7-44fb63033b7e@gmail.com> References: <20260331-realtek_forward-v1-0-44fb63033b7e@gmail.com> In-Reply-To: <20260331-realtek_forward-v1-0-44fb63033b7e@gmail.com> To: Andrew Lunn , Vladimir Oltean , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , Linus Walleij , =?utf-8?q?Alvin_=C5=A0ipraga?= , Yury Norov , Rasmus Villemoes , Russell King Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org, Luiz Angelo Daros de Luca X-Mailer: b4 0.15.1 From: Alvin =C5=A0ipraga Implement hardware offloading of bridge functionality. This is achieved by using the per-port isolation registers, which contain a forwarding port mask. The switch will refuse to forward packets ingressed on a given port to a port which is not in its forwarding mask. For each bridge that is offloaded, use the DSA-provided bridge number for the Extended Filtering ID (EFID). When using Independent VLAN Learning (IVL), the forwarding database is keyed with the tuple {VID, MAC, EFID}. There are 8 EFIDs available (0~7), but we reserve the default EFID 0 for standalone ports where learning is disabled. This fits nicely because DSA indexes the bridge number starting from 1. Because of the limited number of EFIDs, we have to set the max_num_bridges property of our switch to 7: we can't offload more than that or we will fail to offer IVL as at least two bridges would end up having to share an EFID. Co-developed-by: Alvin =C5=A0ipraga Signed-off-by: Alvin =C5=A0ipraga Signed-off-by: Luiz Angelo Daros de Luca --- drivers/net/dsa/realtek/rtl8365mb_main.c | 123 +++++++++++++++++++++++++++= +++- 1 file changed, 122 insertions(+), 1 deletion(-) diff --git a/drivers/net/dsa/realtek/rtl8365mb_main.c b/drivers/net/dsa/rea= ltek/rtl8365mb_main.c index c604bd744d38..c955864f17b3 100644 --- a/drivers/net/dsa/realtek/rtl8365mb_main.c +++ b/drivers/net/dsa/realtek/rtl8365mb_main.c @@ -285,6 +285,15 @@ (RTL8365MB_PORT_ISOLATION_REG_BASE + (_physport)) #define RTL8365MB_PORT_ISOLATION_MASK 0x07FF =20 +/* Extended filter ID registers - used to key forwarding database with IVL= */ +#define RTL8365MB_EFID_MASK GENMASK(2, 0) +#define RTL8365MB_PORT_EFID_REG_BASE 0x0A32 +#define RTL8365MB_PORT_EFID_REG(_p) \ + (RTL8365MB_PORT_EFID_REG_BASE + ((_p) >> 2)) +#define RTL8365MB_PORT_EFID_OFFSET(_p) (((_p) & 0x3) << 2) +#define RTL8365MB_PORT_EFID_MASK(_p) \ + (RTL8365MB_EFID_MASK << RTL8365MB_PORT_EFID_OFFSET(_p)) + /* MSTP port state registers - indexed by tree instance */ #define RTL8365MB_MSTI_CTRL_BASE 0x0A00 #define RTL8365MB_MSTI_CTRL_REG(_msti, _physport) \ @@ -1447,10 +1456,117 @@ static int rtl8365mb_port_set_learning(struct real= tek_priv *priv, int port, enable ? RTL8365MB_LEARN_LIMIT_MAX : 0); } =20 +static int rtl8365mb_port_set_efid(struct realtek_priv *priv, int port, + u32 efid) +{ + return regmap_update_bits(priv->map, RTL8365MB_PORT_EFID_REG(port), + RTL8365MB_PORT_EFID_MASK(port), + efid << RTL8365MB_PORT_EFID_OFFSET(port)); +} + +/* Port isolation manipulation functions. + * + * The port isolation register controls the forwarding mask of a given + * port. The switch will not forward packets ingressed on a given port + * to ports which are not enabled in its forwarding mask. + * + * The port forwarding mask has the highest priority in forwarding + * decisions. The only exception to this rule is when the switch + * receives a packet on its CPU port with ALLOW=3D0. In that case the TX + * field of the CPU tag will override the forwarding port mask. + */ static int rtl8365mb_port_set_isolation(struct realtek_priv *priv, int por= t, u32 mask) { - return regmap_write(priv->map, RTL8365MB_PORT_ISOLATION_REG(port), mask); + return regmap_write(priv->map, RTL8365MB_PORT_ISOLATION_REG(port), + mask); +} + +static int rtl8365mb_port_add_isolation(struct realtek_priv *priv, int por= t, + u32 mask) +{ + return regmap_update_bits(priv->map, RTL8365MB_PORT_ISOLATION_REG(port), + mask, mask); +} + +static int rtl8365mb_port_remove_isolation(struct realtek_priv *priv, int = port, + u32 mask) +{ + return regmap_update_bits(priv->map, RTL8365MB_PORT_ISOLATION_REG(port), + mask, 0); +} + +static int rtl8365mb_port_bridge_join(struct dsa_switch *ds, int port, + struct dsa_bridge bridge, + bool *tx_forward_offload, + struct netlink_ext_ack *extack) +{ + struct realtek_priv *priv =3D ds->priv; + u32 mask =3D 0; + int ret; + int i; + + /* Add this port to the isolation group of every other port + * offloading this bridge. + */ + for (i =3D 0; i < priv->num_ports; i++) { + /* Handle this port after */ + if (i =3D=3D port) + continue; + + /* Skip ports that are not in this bridge */ + if (!dsa_port_offloads_bridge(dsa_to_port(ds, i), &bridge)) + continue; + + ret =3D rtl8365mb_port_add_isolation(priv, i, BIT(port)); + if (ret) + return ret; + + mask |=3D BIT(i); + } + + /* Add those ports to the isolation group of this port */ + ret =3D rtl8365mb_port_add_isolation(priv, port, mask); + if (ret) + return ret; + + /* Use the bridge number as the EFID for this port */ + ret =3D rtl8365mb_port_set_efid(priv, port, bridge.num); + if (ret) + return ret; + + return 0; +} + +static void rtl8365mb_port_bridge_leave(struct dsa_switch *ds, int port, + struct dsa_bridge bridge) +{ + struct realtek_priv *priv =3D ds->priv; + u32 mask =3D 0; + int i; + + /* Remove this port from the isolation group of every other + * port offloading this bridge. + */ + for (i =3D 0; i < priv->num_ports; i++) { + /* Handle this port after */ + if (i =3D=3D port) + continue; + + /* Skip ports that are not in this bridge */ + if (!dsa_port_offloads_bridge(dsa_to_port(ds, i), &bridge)) + continue; + + rtl8365mb_port_remove_isolation(priv, i, BIT(port)); + + mask |=3D BIT(i); + } + + /* Remove those ports from the isolation group of this port */ + rtl8365mb_port_remove_isolation(priv, port, mask); + + /* Revert to the default EFID 0 for standalone mode */ + rtl8365mb_port_set_efid(priv, port, 0); } =20 static int rtl8365mb_mib_counter_read(struct realtek_priv *priv, int port, @@ -2253,6 +2369,9 @@ static int rtl8365mb_setup(struct dsa_switch *ds) if (ret) goto out_teardown_irq; =20 + /* The EFID is 3 bits, but EFID 0 is reserved for standalone ports */ + ds->max_num_bridges =3D FIELD_MAX(RTL8365MB_EFID_MASK); + /* vlan config will only be effective for ports with vlan filtering */ ds->configure_vlan_while_not_filtering =3D true; /* Set up VLAN */ @@ -2376,6 +2495,8 @@ static const struct dsa_switch_ops rtl8365mb_switch_o= ps =3D { .setup =3D rtl8365mb_setup, .teardown =3D rtl8365mb_teardown, .phylink_get_caps =3D rtl8365mb_phylink_get_caps, + .port_bridge_join =3D rtl8365mb_port_bridge_join, + .port_bridge_leave =3D rtl8365mb_port_bridge_leave, .port_stp_state_set =3D rtl8365mb_port_stp_state_set, .port_vlan_add =3D rtl8365mb_port_vlan_add, .port_vlan_del =3D rtl8365mb_port_vlan_del, --=20 2.53.0 From nobody Wed Apr 1 08:15:25 2026 Received: from mail-vk1-f172.google.com (mail-vk1-f172.google.com [209.85.221.172]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7EFC5466B7D for ; Tue, 31 Mar 2026 23:00:57 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774998059; cv=none; b=lRIKeLnJQyqFCcZjH+f2ZBG5gaMpiWLuHI3ewz3+pkQ6DwkG6HkRHOHX9udI8YhHdSHw/6eu4R6fPwu8LpDb+5jNbjct79oR9kozvHBWkKHGWLoAnuePijs3ozynY798QRXA4Rqc909ej2M3BtDBbTzobisREyWs++wUx7745R0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774998059; c=relaxed/simple; bh=opbPqYr59ykM4mi1Qec+Y7KtTsf3ls1hHO7GW0cHaZ8=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=sbmCDu7TziUgeBG2il+1tsPla5YRUIOiH4lkjFAYdV+NCAIi8G5KRMv8d7UBrUojsx0+DYTzqgmtq3zk5JGL5MqumQA6h+GAnmI40H8ND/UKMYZTt9OU8Q//d/zqweLvKmBzoWAu8gTKKhhEZpeEKCSSrG+Ad+wPXUlbeS0KCUI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=n87niXXn; arc=none smtp.client-ip=209.85.221.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="n87niXXn" Received: by mail-vk1-f172.google.com with SMTP id 71dfb90a1353d-5673804da95so2252131e0c.0 for ; Tue, 31 Mar 2026 16:00:57 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1774998056; x=1775602856; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=LVSxqrmft82CW5zhffxaBSlaB2KPtvzEqUsxhFVjkdI=; b=n87niXXnP9RAlz0GjkGJ4UD1MZ5sMybOjAjL6Tflyxp+0RAswhu1OWv91Pc3PIwfFm tW0Qte63JEBqIVGrkvqn8AuDAzgI0UKHKTTpSOxt8Cq7POJ/DcD9uruOvfCRaY6KP3J1 8E8LKgwLRN/60RKId3dzR4IeMtUTtMS1x3iGLbXgCNXndQuAiV6rmIQ7SB9pFQG0i4fa bAzGUjO/UE+0gkHz6R7uzKCZRGqOhrBtUXP/pJ1Lv6UU+tcppRJANPY78BCAZEDmTd/b euN7/zXBSW5qY2mmpVby691zOhDpH2s3o5kbt9IyZqSWKSGZps5kAPxBAmZ7hwQg4Yhi SmbA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1774998056; x=1775602856; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=LVSxqrmft82CW5zhffxaBSlaB2KPtvzEqUsxhFVjkdI=; b=VkkoBqeAegNsREe0pwm9JmMbbbEkNiV0nqq/qnuTagBzUtWW9aX8YFCHlL4RjNl4QO cAaX3W9tfga3bFEuPxCtUrxyrBxcq24a3KDTlj0JDaZewTLOCQZliswdSynxSuYXHNEA j3E8jk+NJLcMW8LY/BGeFuuxtWL05W4gJqdmfQwnklWouyWoYMJcDZU6/bINRhZXsTik ROJ7cvReWqRnEf3v1/pMBMQNj/pomASdX0qw6FTorJIcGiX8uHdCNhjszJL6ucrESfo+ ck4e9lq8ntOJFgF0xqY7G44bAwhybxS40wrorksqXlLx7Eh/XFiIViCr5ngo5JN9zlI3 F2HQ== X-Forwarded-Encrypted: i=1; AJvYcCUFdUKv80/6hUZ11YltgGn2Lmz9WE1RlcMpckNBxZxoVJMVpY+O0s7lVLj6vBsTJ75DuIZJI6rT7ZPT0qo=@vger.kernel.org X-Gm-Message-State: AOJu0Yw34NoUvHGaWjeT+ZM/Msf6t0YHvUZDSdyxmvp2l7Yf0z4emP+c 6De3gkRQGHGGyYhgg/DXGZbAOSdqNE2QBIf2YnMCGk5FP61NGWLVniqt X-Gm-Gg: ATEYQzxGwPfR0hNw95G5jaUBQMEMoozGowN9RR2y/89XKZID/vdS0/le6aIvrdYNfZ7 dN/9mfH3Kdt4OijX73XIMv+Cf1Wn+6Ne1tliB0UVcQI/lmiZoMdf5SIO7E3dIhkfpPUlu/NGScH 1bJag2D3OaRZ9fCDUlpenw8qoQcbsvvSDwZyqmgza8tZf6It1UZb8xVrxTWExPB4+1F9VGLva4j 8PLaKqYEZ74Y5LX/EtG5R89SmPf5SkXrrKOD5m1jOOOF7Lhhcmgo8+BYp45/6gcYqpIYqdenTfb eNgW3bryMOy1iIpXDN00GWyDDAQ4anUVjZdjlR0qMcZ3FJlkNmyWtUEtgqnOsXLoEwZ7xzIYwmw gWXkyybWxv41xLRGCWfCeh04StjfvQy+YnvJxBovCUzQeOBOdMnJs1dSFcLfdFviwGmWXisKif/ tiPKo0vt42fLcbhe2KIrln09FDAGGr8ceAS+dFmQ== X-Received: by 2002:a05:6122:ca1:b0:56a:ef89:34fc with SMTP id 71dfb90a1353d-56d8a81bd48mr662957e0c.6.1774998056172; Tue, 31 Mar 2026 16:00:56 -0700 (PDT) Received: from tresc054937.tre-sc.gov.br ([187.65.210.13]) by smtp.gmail.com with ESMTPSA id 71dfb90a1353d-56d58a7ba96sm13948214e0c.17.2026.03.31.16.00.51 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 31 Mar 2026 16:00:55 -0700 (PDT) From: Luiz Angelo Daros de Luca Date: Tue, 31 Mar 2026 20:00:08 -0300 Subject: [net-next PATCH 08/10] net: dsa: realtek: rtl8365mb: add FDB support Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260331-realtek_forward-v1-8-44fb63033b7e@gmail.com> References: <20260331-realtek_forward-v1-0-44fb63033b7e@gmail.com> In-Reply-To: <20260331-realtek_forward-v1-0-44fb63033b7e@gmail.com> To: Andrew Lunn , Vladimir Oltean , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , Linus Walleij , =?utf-8?q?Alvin_=C5=A0ipraga?= , Yury Norov , Rasmus Villemoes , Russell King Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org, Luiz Angelo Daros de Luca X-Mailer: b4 0.15.1 From: Alvin =C5=A0ipraga Add support for forwarding database operations, including unicast and multicast entry handling as well as fast aging support. The driver implements Independent VLAN Learning (IVL) by keying the forwarding database with the {VID, MAC, EFID} tuple. The Extended Filtering ID (EFID) is 3 bits wide, providing 8 unique filtering domains. Since EFID 0 is reserved for standalone ports where learning is disabled, the hardware is limited to offloading a maximum of 7 bridges. The driver implements only the subset of L2 table accessors needed to support these DSA operations. Other hardware capabilities are left untouched and can be added incrementally if needed. Co-developed-by: Alvin =C5=A0ipraga Signed-off-by: Alvin =C5=A0ipraga Signed-off-by: Luiz Angelo Daros de Luca --- drivers/net/dsa/realtek/Makefile | 1 + drivers/net/dsa/realtek/rtl8365mb_l2.c | 465 +++++++++++++++++++++++++++= ++++ drivers/net/dsa/realtek/rtl8365mb_l2.h | 59 ++++ drivers/net/dsa/realtek/rtl8365mb_main.c | 197 ++++++++++++- 4 files changed, 721 insertions(+), 1 deletion(-) diff --git a/drivers/net/dsa/realtek/Makefile b/drivers/net/dsa/realtek/Mak= efile index b7fc4e852fd8..6c329e046d0b 100644 --- a/drivers/net/dsa/realtek/Makefile +++ b/drivers/net/dsa/realtek/Makefile @@ -19,3 +19,4 @@ obj-$(CONFIG_NET_DSA_REALTEK_RTL8365MB) +=3D rtl8365mb.o rtl8365mb-objs :=3D rtl8365mb_main.o \ rtl8365mb_table.o \ rtl8365mb_vlan.o \ + rtl8365mb_l2.o \ diff --git a/drivers/net/dsa/realtek/rtl8365mb_l2.c b/drivers/net/dsa/realt= ek/rtl8365mb_l2.c new file mode 100644 index 000000000000..be5825f8fe7b --- /dev/null +++ b/drivers/net/dsa/realtek/rtl8365mb_l2.c @@ -0,0 +1,465 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Forwarding and multicast database interface for the rtl8365mb switch fa= mily + * + * Copyright (C) 2022 Alvin =C5=A0ipraga + */ + +#include + +#include "rtl8365mb_l2.h" +#include "rtl8365mb_table.h" +#include + +#define RTL8365MB_L2_ENTRY_SIZE 6 + +#define RTL8365MB_L2_UC_D0_MAC5_MASK GENMASK(7, 0) +#define RTL8365MB_L2_UC_D0_MAC4_MASK GENMASK(15, 8) +#define RTL8365MB_L2_UC_D1_MAC3_MASK GENMASK(7, 0) +#define RTL8365MB_L2_UC_D1_MAC2_MASK GENMASK(15, 8) +#define RTL8365MB_L2_UC_D2_MAC1_MASK GENMASK(7, 0) +#define RTL8365MB_L2_UC_D2_MAC0_MASK GENMASK(15, 8) +#define RTL8365MB_L2_UC_D3_VID_MASK GENMASK(11, 0) +#define RTL8365MB_L2_UC_D3_IVL_MASK GENMASK(13, 13) +#define RTL8365MB_L2_UC_D3_PORT_EXT_MASK GENMASK(15, 15) +#define RTL8365MB_L2_UC_D4_EFID_MASK GENMASK(2, 0) +#define RTL8365MB_L2_UC_D4_FID_MASK GENMASK(6, 3) +#define RTL8365MB_L2_UC_D4_SA_PRI_MASK GENMASK(7, 7) +#define RTL8365MB_L2_UC_D4_PORT_MASK GENMASK(10, 8) +#define RTL8365MB_L2_UC_D4_AGE_MASK GENMASK(13, 11) +#define RTL8365MB_L2_UC_D4_AUTH_MASK GENMASK(14, 14) +#define RTL8365MB_L2_UC_D4_SA_BLOCK_MASK GENMASK(15, 15) +#define RTL8365MB_L2_UC_D5_DA_BLOCK_MASK GENMASK(0, 0) +#define RTL8365MB_L2_UC_D5_PRIORITY_MASK GENMASK(3, 1) +#define RTL8365MB_L2_UC_D5_FWD_PRI_MASK GENMASK(4, 4) +#define RTL8365MB_L2_UC_D5_STATIC_MASK GENMASK(5, 5) + +#define RTL8365MB_L2_MC_MAC5_MASK GENMASK(7, 0) /* D0 */ +#define RTL8365MB_L2_MC_MAC4_MASK GENMASK(15, 8) /* D0 */ +#define RTL8365MB_L2_MC_MAC3_MASK GENMASK(7, 0) /* D1 */ +#define RTL8365MB_L2_MC_MAC2_MASK GENMASK(15, 8) /* D1 */ +#define RTL8365MB_L2_MC_MAC1_MASK GENMASK(7, 0) /* D2 */ +#define RTL8365MB_L2_MC_MAC0_MASK GENMASK(15, 8) /* D2 */ +#define RTL8365MB_L2_MC_VID_MASK GENMASK(11, 0) /* D3 */ +#define RTL8365MB_L2_MC_IVL_MASK GENMASK(13, 13) /* D3 */ +#define RTL8365MB_L2_MC_MBR_EXT1_MASK GENMASK(15, 14) /* D3 */ + +#define RTL8365MB_L2_MC_MBR_MASK GENMASK(7, 0) /* D4 */ +#define RTL8365MB_L2_MC_IGMPIDX_MASK GENMASK(15, 8) /* D4 */ + +#define RTL8365MB_L2_MC_IGMP_ASIC_MASK GENMASK(0, 0) /* D5 */ +#define RTL8365MB_L2_MC_PRIORITY_MASK GENMASK(3, 1) /* D5 */ +#define RTL8365MB_L2_MC_FWD_PRI_MASK GENMASK(4, 4) /* D5 */ +#define RTL8365MB_L2_MC_STATIC_MASK GENMASK(5, 5) /* D5 */ +#define RTL8365MB_L2_MC_MBR_EXT2_MASK GENMASK(7, 7) /* D5 */ + +/* Port flush command registers - writing a 1 to the port's MASK bit will + * initiate the flush procedure. Completion is signalled when the correspo= nding + * BUSY bit is 0. + */ +#define RTL8365MB_L2_FLUSH_PORT_REG 0x0A36 +#define RTL8365MB_L2_FLUSH_PORT_MASK_MASK GENMASK(7, 0) +#define RTL8365MB_L2_FLUSH_PORT_BUSY_MASK GENMASK(15, 8) + +#define RTL8365MB_L2_FLUSH_PORT_EXT_REG 0x0A35 +#define RTL8365MB_L2_FLUSH_PORT_EXT_MASK_MASK GENMASK(2, 0) +#define RTL8365MB_L2_FLUSH_PORT_EXT_BUSY_MASK GENMASK(5, 3) + +#define RTL8365MB_L2_FLUSH_CTRL1_REG 0x0A37 +#define RTL8365MB_L2_FLUSH_CTRL1_VID_MASK GENMASK(11, 0) +#define RTL8365MB_L2_FLUSH_CTRL1_FID_MASK GENMASK(15, 12) + +#define RTL8365MB_L2_FLUSH_CTRL2_REG 0x0A38 +#define RTL8365MB_L2_FLUSH_CTRL2_MODE_MASK GENMASK(1, 0) +#define RTL8365MB_L2_FLUSH_CTRL2_MODE_PORT 0 +#define RTL8365MB_L2_FLUSH_CTRL2_MODE_PORT_VID 1 +#define RTL8365MB_L2_FLUSH_CTRL2_MODE_PORT_FID 2 +#define RTL8365MB_L2_FLUSH_CTRL2_TYPE_MASK GENMASK(2, 2) +#define RTL8365MB_L2_FLUSH_CTRL2_TYPE_DYNAMIC 0 +#define RTL8365MB_L2_FLUSH_CTRL2_TYPE_BOTH 0 + +/* This flushes the entire LUT, reading it back it will turn 0 when the + * operation is complete + */ +#define RTL8365MB_L2_FLUSH_CTRL3_REG 0x0A39 +#define RTL8365MB_L2_FLUSH_CTRL3_MASK GENMASK(0, 0) + +struct rtl8365mb_l2_mc_key { + u8 mac_addr[ETH_ALEN]; + union { + u16 vid; /* IVL */ + u16 fid; /* SVL */ + }; + bool ivl; +}; + +struct rtl8365mb_l2_mc { + struct rtl8365mb_l2_mc_key key; + u16 member; + u8 priority; + u8 igmpidx; + + bool is_static; + bool fwd_pri; + bool igmp_asic; +}; + +static void rtl8365mb_l2_data_to_uc(const u16 *data, struct rtl8365mb_l2_u= c *uc) +{ + uc->key.mac_addr[5] =3D FIELD_GET(RTL8365MB_L2_UC_D0_MAC5_MASK, data[0]); + uc->key.mac_addr[4] =3D FIELD_GET(RTL8365MB_L2_UC_D0_MAC4_MASK, data[0]); + uc->key.mac_addr[3] =3D FIELD_GET(RTL8365MB_L2_UC_D1_MAC3_MASK, data[1]); + uc->key.mac_addr[2] =3D FIELD_GET(RTL8365MB_L2_UC_D1_MAC2_MASK, data[1]); + uc->key.mac_addr[1] =3D FIELD_GET(RTL8365MB_L2_UC_D2_MAC1_MASK, data[2]); + uc->key.mac_addr[0] =3D FIELD_GET(RTL8365MB_L2_UC_D2_MAC0_MASK, data[2]); + uc->key.efid =3D FIELD_GET(RTL8365MB_L2_UC_D4_EFID_MASK, data[4]); + uc->key.vid =3D FIELD_GET(RTL8365MB_L2_UC_D3_VID_MASK, data[3]); + uc->key.ivl =3D FIELD_GET(RTL8365MB_L2_UC_D3_IVL_MASK, data[3]); + uc->key.fid =3D FIELD_GET(RTL8365MB_L2_UC_D4_FID_MASK, data[4]); + uc->age =3D FIELD_GET(RTL8365MB_L2_UC_D4_AGE_MASK, data[4]); + uc->auth =3D FIELD_GET(RTL8365MB_L2_UC_D4_AUTH_MASK, data[4]); + uc->port =3D FIELD_GET(RTL8365MB_L2_UC_D4_PORT_MASK, data[4]) | + (FIELD_GET(RTL8365MB_L2_UC_D3_PORT_EXT_MASK, data[3]) << 3); + uc->sa_pri =3D FIELD_GET(RTL8365MB_L2_UC_D4_SA_PRI_MASK, data[4]); + uc->fwd_pri =3D FIELD_GET(RTL8365MB_L2_UC_D5_FWD_PRI_MASK, data[5]); + uc->sa_block =3D FIELD_GET(RTL8365MB_L2_UC_D4_SA_BLOCK_MASK, data[4]); + uc->da_block =3D FIELD_GET(RTL8365MB_L2_UC_D5_DA_BLOCK_MASK, data[5]); + uc->priority =3D FIELD_GET(RTL8365MB_L2_UC_D5_PRIORITY_MASK, data[5]); + uc->is_static =3D FIELD_GET(RTL8365MB_L2_UC_D5_STATIC_MASK, data[5]); +} + +static void rtl8365mb_l2_uc_to_data(const struct rtl8365mb_l2_uc *uc, u16 = *data) +{ + memset(data, 0, RTL8365MB_L2_ENTRY_SIZE * 2); + data[0] |=3D + FIELD_PREP(RTL8365MB_L2_UC_D0_MAC5_MASK, uc->key.mac_addr[5]); + data[0] |=3D + FIELD_PREP(RTL8365MB_L2_UC_D0_MAC4_MASK, uc->key.mac_addr[4]); + data[1] |=3D + FIELD_PREP(RTL8365MB_L2_UC_D1_MAC3_MASK, uc->key.mac_addr[3]); + data[1] |=3D + FIELD_PREP(RTL8365MB_L2_UC_D1_MAC2_MASK, uc->key.mac_addr[2]); + data[2] |=3D + FIELD_PREP(RTL8365MB_L2_UC_D2_MAC1_MASK, uc->key.mac_addr[1]); + data[2] |=3D + FIELD_PREP(RTL8365MB_L2_UC_D2_MAC0_MASK, uc->key.mac_addr[0]); + data[3] |=3D FIELD_PREP(RTL8365MB_L2_UC_D3_VID_MASK, uc->key.vid); + data[3] |=3D FIELD_PREP(RTL8365MB_L2_UC_D3_IVL_MASK, uc->key.ivl); + data[3] |=3D FIELD_PREP(RTL8365MB_L2_UC_D3_PORT_EXT_MASK, uc->port >> 3); + data[4] |=3D FIELD_PREP(RTL8365MB_L2_UC_D4_FID_MASK, uc->key.fid); + data[4] |=3D FIELD_PREP(RTL8365MB_L2_UC_D4_EFID_MASK, uc->key.efid); + data[4] |=3D FIELD_PREP(RTL8365MB_L2_UC_D4_AGE_MASK, uc->age); + data[4] |=3D FIELD_PREP(RTL8365MB_L2_UC_D4_AUTH_MASK, uc->auth); + data[4] |=3D FIELD_PREP(RTL8365MB_L2_UC_D4_PORT_MASK, uc->port); + data[4] |=3D FIELD_PREP(RTL8365MB_L2_UC_D4_SA_PRI_MASK, uc->sa_pri); + data[4] |=3D FIELD_PREP(RTL8365MB_L2_UC_D4_SA_BLOCK_MASK, uc->sa_block); + data[5] |=3D FIELD_PREP(RTL8365MB_L2_UC_D5_FWD_PRI_MASK, uc->fwd_pri); + data[5] |=3D FIELD_PREP(RTL8365MB_L2_UC_D5_DA_BLOCK_MASK, uc->da_block); + data[5] |=3D FIELD_PREP(RTL8365MB_L2_UC_D5_PRIORITY_MASK, uc->priority); + data[5] |=3D FIELD_PREP(RTL8365MB_L2_UC_D5_STATIC_MASK, uc->is_static); +} + +static void rtl8365mb_l2_data_to_mc(const u16 *data, struct rtl8365mb_l2_m= c *mc) +{ + mc->key.mac_addr[5] =3D FIELD_GET(RTL8365MB_L2_MC_MAC5_MASK, data[0]); + mc->key.mac_addr[4] =3D FIELD_GET(RTL8365MB_L2_MC_MAC4_MASK, data[0]); + mc->key.mac_addr[3] =3D FIELD_GET(RTL8365MB_L2_MC_MAC3_MASK, data[1]); + mc->key.mac_addr[2] =3D FIELD_GET(RTL8365MB_L2_MC_MAC2_MASK, data[1]); + mc->key.mac_addr[1] =3D FIELD_GET(RTL8365MB_L2_MC_MAC1_MASK, data[2]); + mc->key.mac_addr[0] =3D FIELD_GET(RTL8365MB_L2_MC_MAC0_MASK, data[2]); + mc->key.vid =3D FIELD_GET(RTL8365MB_L2_MC_VID_MASK, data[3]); + mc->key.ivl =3D FIELD_GET(RTL8365MB_L2_MC_IVL_MASK, data[3]); + mc->priority =3D FIELD_GET(RTL8365MB_L2_MC_PRIORITY_MASK, data[5]); + mc->fwd_pri =3D FIELD_GET(RTL8365MB_L2_MC_FWD_PRI_MASK, data[5]); + mc->is_static =3D FIELD_GET(RTL8365MB_L2_MC_STATIC_MASK, data[5]); + mc->member =3D FIELD_GET(RTL8365MB_L2_MC_MBR_MASK, data[4]) | + (FIELD_GET(RTL8365MB_L2_MC_MBR_EXT1_MASK, data[3]) << 8) | + (FIELD_GET(RTL8365MB_L2_MC_MBR_EXT2_MASK, data[5]) << 8); + mc->igmpidx =3D FIELD_GET(RTL8365MB_L2_MC_IGMPIDX_MASK, data[4]); + mc->igmp_asic =3D FIELD_GET(RTL8365MB_L2_MC_IGMP_ASIC_MASK, data[5]); +} + +static void rtl8365mb_l2_mc_to_data(const struct rtl8365mb_l2_mc *mc, u16 = *data) +{ + memset(data, 0, 12); + data[0] |=3D FIELD_PREP(RTL8365MB_L2_MC_MAC5_MASK, mc->key.mac_addr[5]); + data[0] |=3D FIELD_PREP(RTL8365MB_L2_MC_MAC4_MASK, mc->key.mac_addr[4]); + data[1] |=3D FIELD_PREP(RTL8365MB_L2_MC_MAC3_MASK, mc->key.mac_addr[3]); + data[1] |=3D FIELD_PREP(RTL8365MB_L2_MC_MAC2_MASK, mc->key.mac_addr[2]); + data[2] |=3D FIELD_PREP(RTL8365MB_L2_MC_MAC1_MASK, mc->key.mac_addr[1]); + data[2] |=3D FIELD_PREP(RTL8365MB_L2_MC_MAC0_MASK, mc->key.mac_addr[0]); + data[3] |=3D FIELD_PREP(RTL8365MB_L2_MC_VID_MASK, mc->key.vid); + data[3] |=3D FIELD_PREP(RTL8365MB_L2_MC_IVL_MASK, mc->key.ivl); + data[3] |=3D FIELD_PREP(RTL8365MB_L2_MC_MBR_EXT1_MASK, mc->member >> 8); + data[4] |=3D FIELD_PREP(RTL8365MB_L2_MC_MBR_MASK, mc->member); + data[4] |=3D FIELD_PREP(RTL8365MB_L2_MC_IGMPIDX_MASK, mc->igmpidx); + data[5] |=3D FIELD_PREP(RTL8365MB_L2_MC_IGMP_ASIC_MASK, mc->igmp_asic); + data[5] |=3D FIELD_PREP(RTL8365MB_L2_MC_PRIORITY_MASK, mc->priority); + data[5] |=3D FIELD_PREP(RTL8365MB_L2_MC_FWD_PRI_MASK, mc->fwd_pri); + data[5] |=3D FIELD_PREP(RTL8365MB_L2_MC_STATIC_MASK, mc->is_static); + data[5] |=3D FIELD_PREP(RTL8365MB_L2_MC_MBR_EXT2_MASK, mc->member >> 10); +} + +/** + * rtl8365mb_l2_get_next_uc() - get the next Unicast L2 entry + * + * @priv: realtek_priv pointer + * @addr: as input, the table index to start the walk + * as output, the found table index + * @port: restrict the walk on entries related to port + * @uc: returned L2 Unicast table entry + * + * This function get the next unicast L2 table entry starting from @addr + * and checking exclusively entries related to @port. If no more entries + * were found, the output @addr will be lower than the input @addr and @uc + * will not be overwritten. + * + * Return: Returns 0 on success, a negative error on failure. + **/ +int rtl8365mb_l2_get_next_uc(struct realtek_priv *priv, u16 *addr, u16 por= t, + struct rtl8365mb_l2_uc *uc) +{ + u16 data[RTL8365MB_L2_ENTRY_SIZE] =3D { 0 }; + int ret; + + ret =3D rtl8365mb_table_query(priv, RTL8365MB_TABLE_L2, + RTL8365MB_TABLE_OP_READ, addr, + RTL8365MB_TABLE_L2_METHOD_ADDR_NEXT_UC_PORT, + port, data, RTL8365MB_L2_ENTRY_SIZE); + if (ret) + return ret; + + rtl8365mb_l2_data_to_uc(data, uc); + + return 0; +} + +int rtl8365mb_l2_add_uc(struct realtek_priv *priv, u16 port, + const unsigned char mac_addr[static ETH_ALEN], + u16 efid, u16 vid) +{ + u16 data[RTL8365MB_L2_ENTRY_SIZE] =3D { 0 }; + struct rtl8365mb_l2_uc uc =3D { 0 }; + u16 addr; + int ret; + + memcpy(uc.key.mac_addr, mac_addr, ETH_ALEN); + uc.key.efid =3D efid; + uc.key.ivl =3D true; + uc.key.vid =3D vid; + uc.port =3D port; + /* do not let HW decrease age */ + uc.is_static =3D true; + /* age greater than 0 adds/updates entries */ + uc.age =3D 1; + rtl8365mb_l2_uc_to_data(&uc, data); + + /* add the new entry or update an existing one */ + ret =3D rtl8365mb_table_query(priv, RTL8365MB_TABLE_L2, + RTL8365MB_TABLE_OP_WRITE, &addr, + 0, 0, + data, RTL8365MB_L2_ENTRY_SIZE); + /* addr will hold the table index, but it is not used here */ + if (ret =3D=3D -ENOENT) { + /* -ENOENT means the just added entry was not found (and @addr + * does not hold the table index. Although any error will be + * treated equally by the caller, assume that the missing entry + * means the table is full (tested in real HW). + */ + return -ENOSPC; + } + return ret; +} + +int rtl8365mb_l2_del_uc(struct realtek_priv *priv, u16 port, + const unsigned char mac_addr[static ETH_ALEN], + u16 efid, u16 vid) +{ + u16 data[RTL8365MB_L2_ENTRY_SIZE] =3D { 0 }; + struct rtl8365mb_l2_uc uc =3D { 0 }; + u16 addr; + int ret; + + memcpy(uc.key.mac_addr, mac_addr, ETH_ALEN); + uc.key.efid =3D efid; + uc.key.ivl =3D true; + uc.key.vid =3D vid; + /* age 0 deletes the entry */ + uc.age =3D 0; + rtl8365mb_l2_uc_to_data(&uc, data); + + /* it looks like the switch will always add/update the entry, + * even when age is 0 or uc.key did not match an existing entry, + * just to immediately drop it because age is zero. You can still + * get the added/updated address from @addr + */ + ret =3D rtl8365mb_table_query(priv, RTL8365MB_TABLE_L2, + RTL8365MB_TABLE_OP_WRITE, &addr, + 0, 0, + data, RTL8365MB_L2_ENTRY_SIZE); + /* addr will hold the table index, but it is not used here */ + return ret; +} + +int rtl8365mb_l2_flush(struct realtek_priv *priv, int port, u16 vid) +{ + int mode =3D vid ? RTL8365MB_L2_FLUSH_CTRL2_MODE_PORT_VID : + RTL8365MB_L2_FLUSH_CTRL2_MODE_PORT; + u32 val, mask; + int ret; + + mutex_lock(&priv->map_lock); + + /* Configure flushing mode; only flush dynamic entries */ + ret =3D regmap_write(priv->map_nolock, RTL8365MB_L2_FLUSH_CTRL2_REG, + FIELD_PREP(RTL8365MB_L2_FLUSH_CTRL2_MODE_MASK, + mode) | + FIELD_PREP(RTL8365MB_L2_FLUSH_CTRL2_TYPE_MASK, + RTL8365MB_L2_FLUSH_CTRL2_TYPE_DYNAMIC)); + if (ret) + goto out; + + ret =3D regmap_write(priv->map_nolock, RTL8365MB_L2_FLUSH_CTRL1_REG, + FIELD_PREP(RTL8365MB_L2_FLUSH_CTRL1_VID_MASK, vid)); + + if (ret) + goto out; + /* Now issue the flush command and wait for its completion. There are + * two registers for this purpose, and which one to use depends on the + * port number. The _EXT register is for ports 8 or higher. + */ + if (port < 8) { + val =3D FIELD_PREP(RTL8365MB_L2_FLUSH_PORT_MASK_MASK, + BIT(port) & 0xFF); + ret =3D regmap_write(priv->map_nolock, + RTL8365MB_L2_FLUSH_PORT_REG, val); + if (ret) + goto out; + + mask =3D FIELD_PREP(RTL8365MB_L2_FLUSH_PORT_BUSY_MASK, + BIT(port) & 0xFF); + ret =3D regmap_read_poll_timeout(priv->map_nolock, + RTL8365MB_L2_FLUSH_PORT_REG, + val, !(val & mask), 10, 100); + if (ret) + goto out; + } else { + val =3D FIELD_PREP(RTL8365MB_L2_FLUSH_PORT_EXT_MASK_MASK, + BIT(port) >> 8); + ret =3D regmap_write(priv->map_nolock, + RTL8365MB_L2_FLUSH_PORT_EXT_REG, val); + if (ret) + goto out; + + mask =3D FIELD_PREP(RTL8365MB_L2_FLUSH_PORT_EXT_BUSY_MASK, + BIT(port) >> 8); + ret =3D regmap_read_poll_timeout(priv->map_nolock, + RTL8365MB_L2_FLUSH_PORT_EXT_REG, + val, !(val & mask), 10, 100); + if (ret) + goto out; + } + +out: + mutex_unlock(&priv->map_lock); + + return ret; +} + +int rtl8365mb_l2_add_mc(struct realtek_priv *priv, u16 port, + const unsigned char mac_addr[static ETH_ALEN], + u16 vid) +{ + u16 data[RTL8365MB_L2_ENTRY_SIZE] =3D { 0 }; + struct rtl8365mb_l2_mc mc =3D { 0 }; + u16 addr; + int ret; + + memcpy(mc.key.mac_addr, mac_addr, ETH_ALEN); + mc.key.vid =3D vid; + mc.key.ivl =3D true; + /* Already set the port and is_static, although not used in OP_READ, + * data will be ready for OP_WRITE if it is a new entry. + */ + mc.member |=3D BIT(port); + mc.is_static =3D 1; + rtl8365mb_l2_mc_to_data(&mc, data); + + /* First look for an existing entry (to get existing port members) */ + ret =3D rtl8365mb_table_query(priv, RTL8365MB_TABLE_L2, + RTL8365MB_TABLE_OP_READ, &addr, + RTL8365MB_TABLE_L2_METHOD_MAC, 0, + data, RTL8365MB_L2_ENTRY_SIZE); + if (!ret) { + /* There is already an entry... */ + rtl8365mb_l2_data_to_mc(data, &mc); + /* the port must be added as a member */ + mc.member |=3D BIT(port); + rtl8365mb_l2_mc_to_data(&mc, data); + } else if (ret =3D=3D -ENOENT) { + /* New entry, no need to update data again as it already + * includes the member + */ + } else { + return ret; + } + + /* add the new entry or update an existing one */ + ret =3D rtl8365mb_table_query(priv, RTL8365MB_TABLE_L2, + RTL8365MB_TABLE_OP_WRITE, &addr, + 0, 0, + data, RTL8365MB_L2_ENTRY_SIZE); + /* addr will hold the table index, but it is not used here */ + if (ret =3D=3D -ENOENT) { + /* -ENOENT means the just added entry was not found (and @addr + * does not hold the table index. Although any error will be + * treated equally by the caller, assume that the missing entry + * means the table is full (tested in real HW). + */ + return -ENOSPC; + } + + return ret; +} + +int rtl8365mb_l2_del_mc(struct realtek_priv *priv, u16 port, + const unsigned char mac_addr[static ETH_ALEN], + u16 vid) +{ + u16 data[RTL8365MB_L2_ENTRY_SIZE] =3D { 0 }; + struct rtl8365mb_l2_mc mc =3D { 0 }; + u16 addr; + int ret; + + memcpy(mc.key.mac_addr, mac_addr, ETH_ALEN); + mc.key.vid =3D vid; + mc.key.ivl =3D true; + rtl8365mb_l2_mc_to_data(&mc, data); + + /* First look for an existing entry (to get existing port members) */ + ret =3D rtl8365mb_table_query(priv, RTL8365MB_TABLE_L2, + RTL8365MB_TABLE_OP_READ, &addr, + RTL8365MB_TABLE_L2_METHOD_MAC, 0, + data, RTL8365MB_L2_ENTRY_SIZE); + if (ret) + /* Any error, including -ENOENT is unexpected */ + return ret; + + rtl8365mb_l2_data_to_mc(data, &mc); + /* the port must be removed as a member */ + mc.member &=3D ~BIT(port); + if (!mc.member) { + /* With no members, zero all non-key fields to delete the + * entry. However is_static is everything else we wrote. + * (and probably all that is needed by the HW) + */ + mc.is_static =3D 0; + } + rtl8365mb_l2_mc_to_data(&mc, data); + + /* update the existing entry. */ + ret =3D rtl8365mb_table_query(priv, RTL8365MB_TABLE_L2, + RTL8365MB_TABLE_OP_WRITE, &addr, + 0, 0, + data, RTL8365MB_L2_ENTRY_SIZE); + return ret; +} diff --git a/drivers/net/dsa/realtek/rtl8365mb_l2.h b/drivers/net/dsa/realt= ek/rtl8365mb_l2.h new file mode 100644 index 000000000000..b60d8617969d --- /dev/null +++ b/drivers/net/dsa/realtek/rtl8365mb_l2.h @@ -0,0 +1,59 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Forwarding and multicast database interface for the rtl8365mb switch fa= mily + * + * Copyright (C) 2022 Alvin =C5=A0ipraga + */ + +#ifndef _REALTEK_RTL8365MB_L2_H +#define _REALTEK_RTL8365MB_L2_H + +#include +#include + +#include "realtek.h" + +/* It's valid for all family but RTL8370B, which has 4160 */ +#define RTL8365MB_LEARN_LIMIT_MAX 2112 + +struct rtl8365mb_l2_uc_key { + u8 mac_addr[ETH_ALEN]; + union { + u16 vid; /* IVL */ + u16 fid; /* SVL */ + }; + bool ivl; + u16 efid; +}; + +struct rtl8365mb_l2_uc { + struct rtl8365mb_l2_uc_key key; + u8 port; + u8 age; + u8 priority; + + bool sa_block; + bool da_block; + bool auth; + bool is_static; + bool sa_pri; + bool fwd_pri; +}; + +int rtl8365mb_l2_get_next_uc(struct realtek_priv *priv, u16 *addr, u16 por= t, + struct rtl8365mb_l2_uc *uc); +int rtl8365mb_l2_add_uc(struct realtek_priv *priv, u16 port, + const unsigned char addr[static ETH_ALEN], + u16 efid, u16 vid); +int rtl8365mb_l2_del_uc(struct realtek_priv *priv, u16 port, + const unsigned char addr[static ETH_ALEN], + u16 efid, u16 vid); +int rtl8365mb_l2_flush(struct realtek_priv *priv, int port, u16 vid); + +int rtl8365mb_l2_add_mc(struct realtek_priv *priv, u16 port, + const unsigned char mac_addr[static ETH_ALEN], + u16 vid); +int rtl8365mb_l2_del_mc(struct realtek_priv *priv, u16 port, + const unsigned char mac_addr[static ETH_ALEN], + u16 vid); + +#endif /* _REALTEK_RTL8365MB_L2_H */ diff --git a/drivers/net/dsa/realtek/rtl8365mb_main.c b/drivers/net/dsa/rea= ltek/rtl8365mb_main.c index c955864f17b3..0767b2704ad7 100644 --- a/drivers/net/dsa/realtek/rtl8365mb_main.c +++ b/drivers/net/dsa/realtek/rtl8365mb_main.c @@ -104,6 +104,7 @@ #include "realtek-smi.h" #include "realtek-mdio.h" #include "rtl83xx.h" +#include "rtl8365mb_l2.h" #include "rtl8365mb_vlan.h" =20 /* Family-specific data and limits */ @@ -112,7 +113,6 @@ #define RTL8365MB_PHYREGMAX (RTL8365MB_NUM_PHYREGS - 1) #define RTL8365MB_MAX_NUM_PORTS 11 #define RTL8365MB_MAX_NUM_EXTINTS 3 -#define RTL8365MB_LEARN_LIMIT_MAX 2112 =20 /* Chip identification registers */ #define RTL8365MB_CHIP_ID_REG 0x1300 @@ -705,6 +705,7 @@ struct rtl8365mb_port { * @chip_info: chip-specific info about the attached switch * @cpu: CPU tagging and CPU port configuration for this chip * @mib_lock: prevent concurrent reads of MIB counters + * @l2_lock: prevent concurrent access to L2 look-up table * @ports: per-port data * * Private data for this driver. @@ -715,6 +716,15 @@ struct rtl8365mb { const struct rtl8365mb_chip_info *chip_info; struct rtl8365mb_cpu cpu; struct mutex mib_lock; + /* l2_lock is used to prevent concurrent modifications of L2 table + * entries while another function is reading it. l2_(add,del)_mc + * is an example that first read current table entry and then + * create/update it. l2_(add|del)_uc uses a single table op and, + * internally, it might not need this lock. However, altering FDB + * may still collide, as well as l2_flush, with fdb_dump iterating + * over FDB. + */ + struct mutex l2_lock; struct rtl8365mb_port ports[RTL8365MB_MAX_NUM_PORTS]; }; =20 @@ -1267,6 +1277,22 @@ static void rtl8365mb_port_stp_state_set(struct dsa_= switch *ds, int port, val << RTL8365MB_MSTI_CTRL_PORT_STATE_OFFSET(port)); } =20 +static void rtl8365mb_port_fast_age(struct dsa_switch *ds, int port) +{ + struct realtek_priv *priv =3D ds->priv; + struct rtl8365mb *mb =3D priv->chip_data; + int ret; + + dev_dbg(priv->dev, "fast_age port %d\n", port); + + mutex_lock(&mb->l2_lock); + ret =3D rtl8365mb_l2_flush(priv, port, 0); + mutex_unlock(&mb->l2_lock); + if (ret) + dev_err(priv->dev, "failed to fast age on port %d: %d\n", port, + ret); +} + static int rtl8365mb_port_set_transparent(struct realtek_priv *priv, int igr_port, int egr_port, bool enable) @@ -1444,6 +1470,165 @@ static int rtl8365mb_vlan_setup(struct dsa_switch *= ds) return ret; } =20 +static int rtl8365mb_port_fdb_add(struct dsa_switch *ds, int port, + const unsigned char *addr, u16 vid, + struct dsa_db db) +{ + struct realtek_priv *priv =3D ds->priv; + struct rtl8365mb *mb; + int efid; + int ret; + + mb =3D priv->chip_data; + + if (db.type !=3D DSA_DB_PORT && db.type !=3D DSA_DB_BRIDGE) + return -EOPNOTSUPP; + + /* + * DSA_DB_BRIDGE ports use bridge number [1..N] as EFID, while + * DSA_DB_PORT use the default EFID (0), not used by any bridge. + */ + efid =3D db.type =3D=3D DSA_DB_BRIDGE ? db.bridge.num : 0; + + dev_dbg(priv->dev, "fdb_add port %d addr %pM efid %d vid %d\n", + port, addr, efid, vid); + + mutex_lock(&mb->l2_lock); + ret =3D rtl8365mb_l2_add_uc(priv, port, addr, efid, vid); + mutex_unlock(&mb->l2_lock); + + if (ret) + dev_err(priv->dev, "fdb_add ERROR %pe\n", ERR_PTR(ret)); + return ret; +} + +static int rtl8365mb_port_fdb_del(struct dsa_switch *ds, int port, + const unsigned char *addr, u16 vid, + struct dsa_db db) +{ + struct realtek_priv *priv =3D ds->priv; + struct rtl8365mb *mb; + int efid; + int ret; + + mb =3D priv->chip_data; + + if (db.type !=3D DSA_DB_PORT && db.type !=3D DSA_DB_BRIDGE) + return -EOPNOTSUPP; + + /* + * DSA_DB_BRIDGE ports use bridge number [1..N] as EFID, while + * DSA_DB_PORT use the default EFID (0), not used by any bridge. + */ + efid =3D db.type =3D=3D DSA_DB_BRIDGE ? db.bridge.num : 0; + + dev_dbg(priv->dev, "fdb_del port %d addr %pM efid %d vid %d\n", + port, addr, efid, vid); + + mutex_lock(&mb->l2_lock); + ret =3D rtl8365mb_l2_del_uc(priv, port, addr, efid, vid); + mutex_unlock(&mb->l2_lock); + + if (ret) + dev_err(priv->dev, "fdb_del ERROR %pe\n", ERR_PTR(ret)); + return ret; +} + +static int rtl8365mb_port_fdb_dump(struct dsa_switch *ds, int port, + dsa_fdb_dump_cb_t *cb, void *data) +{ + struct realtek_priv *priv =3D ds->priv; + struct rtl8365mb_l2_uc uc =3D {0}; + u16 start_addr, addr =3D 0; + struct rtl8365mb *mb; + int ret =3D 0; + + mb =3D priv->chip_data; + + mutex_lock(&mb->l2_lock); + while (true) { + start_addr =3D addr; + + dev_dbg(priv->dev, "l2_get_next_uc, addr:%d, port:%d\n", + addr, port); + ret =3D rtl8365mb_l2_get_next_uc(priv, &addr, port, &uc); + dev_dbg(priv->dev, + "l2_get_next_uc addr:%d mac:%pM vid:%d static:%d ret:%pe\n", + addr, uc.key.mac_addr, uc.key.vid, uc.is_static, + ERR_PTR(ret)); + + /* table is empty for that port */ + if (ret =3D=3D -ENOENT) + break; + if (ret) + break; + + /* overflow: reached the end */ + if (addr < start_addr) + break; + + cb(uc.key.mac_addr, uc.key.vid, uc.is_static, data); + + addr++; + + /* Avoid -ETIMEDOUT in rtl8365mb_l2_get_next_uc() */ + if (addr > RTL8365MB_LEARN_LIMIT_MAX) + break; + } + mutex_unlock(&mb->l2_lock); + + return ret; +} + +static int rtl8365mb_port_mdb_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_mdb *mdb, + struct dsa_db db) +{ + struct realtek_priv *priv =3D ds->priv; + struct rtl8365mb *mb; + int ret; + + mb =3D priv->chip_data; + + if (db.type !=3D DSA_DB_PORT && db.type !=3D DSA_DB_BRIDGE) + return -EOPNOTSUPP; + + dev_dbg(priv->dev, "mdb_add port %d addr %pM vid %d\n", + port, mdb->addr, mdb->vid); + + mutex_lock(&mb->l2_lock); + ret =3D rtl8365mb_l2_add_mc(priv, port, mdb->addr, mdb->vid); + mutex_unlock(&mb->l2_lock); + + if (ret) + dev_err(priv->dev, "mdb_add ERROR %pe\n", ERR_PTR(ret)); + return ret; +} + +static int rtl8365mb_port_mdb_del(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_mdb *mdb, + struct dsa_db db) +{ + struct realtek_priv *priv =3D ds->priv; + struct rtl8365mb *mb; + int ret; + + mb =3D priv->chip_data; + + if (db.type !=3D DSA_DB_PORT && db.type !=3D DSA_DB_BRIDGE) + return -EOPNOTSUPP; + + dev_dbg(priv->dev, "mdb_del port %d addr %pM vid %d\n", + port, mdb->addr, mdb->vid); + + mutex_lock(&mb->l2_lock); + ret =3D rtl8365mb_l2_del_mc(priv, port, mdb->addr, mdb->vid); + mutex_unlock(&mb->l2_lock); + + if (ret) + dev_err(priv->dev, "mdb_del ERROR %pe\n", ERR_PTR(ret)); + return ret; +} static int rtl8365mb_port_set_learning(struct realtek_priv *priv, int port, bool enable) { @@ -2305,6 +2490,8 @@ static int rtl8365mb_setup(struct dsa_switch *ds) mb =3D priv->chip_data; cpu =3D &mb->cpu; =20 + mutex_init(&mb->l2_lock); + ret =3D rtl8365mb_reset_chip(priv); if (ret) { dev_err(priv->dev, "failed to reset chip: %d\n", ret); @@ -2369,6 +2556,8 @@ static int rtl8365mb_setup(struct dsa_switch *ds) if (ret) goto out_teardown_irq; =20 + ds->assisted_learning_on_cpu_port =3D true; + ds->fdb_isolation =3D true; /* The EFID is 3 bits, but EFID 0 is reserved for standalone ports */ ds->max_num_bridges =3D FIELD_MAX(RTL8365MB_EFID_MASK); =20 @@ -2498,6 +2687,12 @@ static const struct dsa_switch_ops rtl8365mb_switch_= ops =3D { .port_bridge_join =3D rtl8365mb_port_bridge_join, .port_bridge_leave =3D rtl8365mb_port_bridge_leave, .port_stp_state_set =3D rtl8365mb_port_stp_state_set, + .port_fast_age =3D rtl8365mb_port_fast_age, + .port_fdb_add =3D rtl8365mb_port_fdb_add, + .port_fdb_del =3D rtl8365mb_port_fdb_del, + .port_fdb_dump =3D rtl8365mb_port_fdb_dump, + .port_mdb_add =3D rtl8365mb_port_mdb_add, + .port_mdb_del =3D rtl8365mb_port_mdb_del, .port_vlan_add =3D rtl8365mb_port_vlan_add, .port_vlan_del =3D rtl8365mb_port_vlan_del, .port_vlan_filtering =3D rtl8365mb_port_vlan_filtering, --=20 2.53.0 From nobody Wed Apr 1 08:15:25 2026 Received: from mail-vk1-f180.google.com (mail-vk1-f180.google.com [209.85.221.180]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id F1562346766 for ; Tue, 31 Mar 2026 23:01:02 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.180 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774998064; cv=none; b=Js+1Bg8Ex6IZEGEpThUm7XQk3W06j/+I9N+Y2i8b2pwdOmZLPut2ZDfCRcUu6WFY/mTWSQKlRiv/+N1opgbnId2W9EglkNB6Vbc91bK2giRca1S4QHKKDtYfrYx0xZwP7p3VhDhW0LnL+kknXBX8KprXNOALklpV5a7ITqVTho0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774998064; c=relaxed/simple; bh=saMCRLvcAfVJd1CcnzEV9fSwr/on3STCPFcEkdYn+28=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=mIR1vLK46jtJGW6claeDj/Qo1vY7runuXdmYP1FKECgrXjVlscBm+Xjbp686oNaglHF6TgQAQ1R7+EfdVvSJ31wAM4Xx+gaCMa+bq3dtU0iltKPzDoMLR9Pb9ukAdYX1AsS/1qTOe8wAeqXePOSVLH2H285jXDFXSsBYob3dsSQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=bKzTr1+i; arc=none smtp.client-ip=209.85.221.180 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="bKzTr1+i" Received: by mail-vk1-f180.google.com with SMTP id 71dfb90a1353d-56d89f35940so192261e0c.2 for ; Tue, 31 Mar 2026 16:01:02 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1774998062; x=1775602862; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=RLQtIV9wOsG2KyNPG3MRoTNeEqvzgHfhMrQmDCa9Iq0=; b=bKzTr1+iGxzBXzne44yv6OtYSqnH5xgI/kw4aC0fX03CBUTEuGttZ3H3tUFtcZVvqq dZ3Aw/0dYO9awf8FHhenQ86I9L/3fKiTfpQH0m+DVGEIojZZpHydBRKIGQxAhUAAZ5XS ePr/Vy6qy65zN4LIYEvlU1OcWIGgzpstJEiDVfZfGQI2w9jtljDd+DefaiUJz1zN2XCH 3xCVqQiZlxHWpsVBTD9wxu6qt4w29kocM0QM29a9tztphmfOQ5o+nARKLBX7t1xtkXRA pXWwLS3Rjhu0z1iq4MtmtS702SG4cvDHdMVSIUdM+a12zTZujBgdSkWK590TVRDHS11J yODQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1774998062; x=1775602862; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=RLQtIV9wOsG2KyNPG3MRoTNeEqvzgHfhMrQmDCa9Iq0=; b=lyhJqHK+yUzzvgWgNJmywd0Kpl4qvfKExIKqmYTHf9wYiCXON+XfTsnTDBB5J9YWry 8FjzaZhm/tWSVidIrlm4x/ZTwnLce3CUfUBMmy9qFFKXzBSNjGpSOKYnlL6PxdWAmPT8 jyJYlhxcunCB5DjnHm/fVSJp8Ea/V1fUmpIM0kgJWwX8xhpQfAmT29nsLXA2EYlbYUY8 Ds417tvAxi4qwr6c+N1TCXo03ITW6z0DaVdiLz1QUVNziEiCYr7fgSv1ApIwm9Dw17IY 5m2Dku68fgumjMYI36uatv1eVJJVDkpRSBpnWfaO2HfXpRMs5t9tfvzuEYAkpo6hrsjf N8Jw== X-Forwarded-Encrypted: i=1; AJvYcCUhInmtmHZ1Xq13O7XGWTRNetlCRT3TtrfvNv5CQCwtusOMTmcS//QrS1LQQxELCxlQMMuIubBc3o+5AU0=@vger.kernel.org X-Gm-Message-State: AOJu0YwEpSwjCuYAkxcUxZGUJukxwBqBe7phl4TqY6E+If4zgE+eu5Uh kTuXb01PIg/kZIaI8v3TFhg9a81muRIG0TU4C0UeFxEt9Bl5JfHBF/Kg X-Gm-Gg: ATEYQzym0fjvGNJ5NxDUHhohkHhNyNYoi0hRVav7KDx3LdU2osnjDr9eShSs8r0QY5T HwRXO6sFcTuciacNKDgc0AAPDvgp4jicuxy9Yt1LII3fTUSaIoUwvBEKgetGiRgSYPQS8JwwvzG sTU1qMqc+2GSSNjw9uxADigQ2WFKIIMMNjP6VWKmZR2Zjyso+hdJre5b8rcHIhqF59CiBO1HiPF Wj4Jma5lqOZbtWKMryOhqaa/UhO8/zOdzeaUVVpRqkmx1tL2FHKsDZs9wLKo7gAcoUYaOkSYw6D 24DWqJgZ/PzMwnInkHxZjD/LRFlPFIUw2IvkFx5l0I1/Q8wnOkyrXYYA2/Yhn9tqpti532y5fl+ XLNhl5zKAosNMqM+zXroHJOVmi9mZz8xnjilDKZsakfsjaMikPhP7dc4PPN85GghwMxttBN7lar 6aqBcd84DLdyDH2E0Z160ZsKqz+m9caLYcxeYTqw== X-Received: by 2002:ac5:c9a8:0:b0:56b:8665:9da9 with SMTP id 71dfb90a1353d-56d8a93571fmr502585e0c.9.1774998061501; Tue, 31 Mar 2026 16:01:01 -0700 (PDT) Received: from tresc054937.tre-sc.gov.br ([187.65.210.13]) by smtp.gmail.com with ESMTPSA id 71dfb90a1353d-56d58a7ba96sm13948214e0c.17.2026.03.31.16.00.56 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 31 Mar 2026 16:01:00 -0700 (PDT) From: Luiz Angelo Daros de Luca Date: Tue, 31 Mar 2026 20:00:09 -0300 Subject: [net-next PATCH 09/10] net: dsa: realtek: rtl8365mb: add bridge port flags Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260331-realtek_forward-v1-9-44fb63033b7e@gmail.com> References: <20260331-realtek_forward-v1-0-44fb63033b7e@gmail.com> In-Reply-To: <20260331-realtek_forward-v1-0-44fb63033b7e@gmail.com> To: Andrew Lunn , Vladimir Oltean , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , Linus Walleij , =?utf-8?q?Alvin_=C5=A0ipraga?= , Yury Norov , Rasmus Villemoes , Russell King Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org, Luiz Angelo Daros de Luca X-Mailer: b4 0.15.1 From: Alvin =C5=A0ipraga Add bridge port flags for: - BR_LEARNING - BR_FLOOD - BR_MCAST_FLOOD - BR_BCAST_FLOOD Co-developed-by: Alvin =C5=A0ipraga Signed-off-by: Alvin =C5=A0ipraga Signed-off-by: Luiz Angelo Daros de Luca --- drivers/net/dsa/realtek/rtl8365mb_main.c | 125 +++++++++++++++++++++++++++= ++++ 1 file changed, 125 insertions(+) diff --git a/drivers/net/dsa/realtek/rtl8365mb_main.c b/drivers/net/dsa/rea= ltek/rtl8365mb_main.c index 0767b2704ad7..b26335c5b27e 100644 --- a/drivers/net/dsa/realtek/rtl8365mb_main.c +++ b/drivers/net/dsa/realtek/rtl8365mb_main.c @@ -302,6 +302,21 @@ #define RTL8365MB_MSTI_CTRL_PORT_STATE_MASK(_physport) \ (0x3 << RTL8365MB_MSTI_CTRL_PORT_STATE_OFFSET((_physport))) =20 +/* Unknown unicast DA flooding port mask */ +#define RTL8365MB_UNKNOWN_UNICAST_FLOODING_PMASK_REG 0x0890 +#define RTL8365MB_UNKNOWN_UNICAST_FLOODING_PMASK_MASK 0x07FF + +/* Unknown multicast DA flooding port mask */ +#define RTL8365MB_UNKNOWN_MULTICAST_FLOODING_PMASK_REG 0x0891 +#define RTL8365MB_UNKNOWN_MULTICAST_FLOODING_PMASK_MASK 0x07FF + +/* Broadcast flooding port mask */ +#define RTL8365MB_UNKNOWN_BROADCAST_FLOODING_PMASK_REG 0x0892 +#define RTL8365MB_UNKNOWN_BROADCAST_FLOODING_PMASK_MASK 0x07FF + +#define RTL8365MB_SUPPORTED_BRIDGE_FLAGS \ + (BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD) + /* Miscellaneous port configuration register, incl. VLAN egress mode */ #define RTL8365MB_PORT_MISC_CFG_REG_BASE 0x000E #define RTL8365MB_PORT_MISC_CFG_REG(_p) \ @@ -1641,6 +1656,97 @@ static int rtl8365mb_port_set_learning(struct realte= k_priv *priv, int port, enable ? RTL8365MB_LEARN_LIMIT_MAX : 0); } =20 +static int rtl8365mb_port_set_ucast_flood(struct realtek_priv *priv, int p= ort, + bool enable) +{ + /* Frames with unknown unicast DA will be flooded to a programmable + * port mask that by default includes all ports. Add or remove + * the specified port from this port mask accordingly. + */ + return regmap_update_bits(priv->map, + RTL8365MB_UNKNOWN_UNICAST_FLOODING_PMASK_REG, + BIT(port), enable ? BIT(port) : 0); +} + +static int rtl8365mb_port_set_mcast_flood(struct realtek_priv *priv, int p= ort, + bool enable) +{ + return regmap_update_bits(priv->map, + RTL8365MB_UNKNOWN_MULTICAST_FLOODING_PMASK_REG, + BIT(port), enable ? BIT(port) : 0); +} + +static int rtl8365mb_port_set_bcast_flood(struct realtek_priv *priv, int p= ort, + bool enable) +{ + return regmap_update_bits(priv->map, + RTL8365MB_UNKNOWN_BROADCAST_FLOODING_PMASK_REG, + BIT(port), enable ? BIT(port) : 0); +} + +static int rtl8365mb_port_pre_bridge_flags(struct dsa_switch *ds, int port, + struct switchdev_brport_flags flags, + struct netlink_ext_ack *extack) +{ + struct realtek_priv *priv =3D ds->priv; + + dev_dbg(priv->dev, "pre_bridge_flags port:%d flags:%lx supported:%lx", + port, flags.mask, RTL8365MB_SUPPORTED_BRIDGE_FLAGS); + + if (flags.mask & ~RTL8365MB_SUPPORTED_BRIDGE_FLAGS) + return -EINVAL; + + return 0; +} + +static int rtl8365mb_port_bridge_flags(struct dsa_switch *ds, int port, + struct switchdev_brport_flags flags, + struct netlink_ext_ack *exack) +{ + struct realtek_priv *priv =3D ds->priv; + int ret; + + dev_dbg(priv->dev, "port_bridge_flags port:%d flags:%lx supported:%lx", + port, flags.mask, RTL8365MB_SUPPORTED_BRIDGE_FLAGS); + + if (flags.mask & BR_LEARNING) { + bool learning_en =3D !!(flags.val & BR_LEARNING); + + ret =3D rtl8365mb_port_set_learning(priv, port, learning_en); + if (ret) + return ret; + } + + if (flags.mask & BR_FLOOD) { + bool ucast_flood_en =3D !!(flags.val & BR_FLOOD); + + ret =3D rtl8365mb_port_set_ucast_flood(priv, port, + ucast_flood_en); + if (ret) + return ret; + } + + if (flags.mask & BR_MCAST_FLOOD) { + bool mcast_flood_en =3D !!(flags.val & BR_MCAST_FLOOD); + + ret =3D rtl8365mb_port_set_mcast_flood(priv, port, + mcast_flood_en); + if (ret) + return ret; + } + + if (flags.mask & BR_BCAST_FLOOD) { + bool bcast_flood_en =3D !!(flags.val & BR_BCAST_FLOOD); + + ret =3D rtl8365mb_port_set_bcast_flood(priv, port, + bcast_flood_en); + if (ret) + return ret; + } + + return 0; +} + static int rtl8365mb_port_set_efid(struct realtek_priv *priv, int port, u32 efid) { @@ -1691,6 +1797,8 @@ static int rtl8365mb_port_bridge_join(struct dsa_swit= ch *ds, int port, int ret; int i; =20 + dev_dbg(priv->dev, "bridge %d join port %d\n", port, bridge.num); + /* Add this port to the isolation group of every other port * offloading this bridge. */ @@ -1730,6 +1838,8 @@ static void rtl8365mb_port_bridge_leave(struct dsa_sw= itch *ds, int port, u32 mask =3D 0; int i; =20 + dev_dbg(priv->dev, "bridge %d leave port %d\n", port, bridge.num); + /* Remove this port from the isolation group of every other * port offloading this bridge. */ @@ -2547,6 +2657,19 @@ static int rtl8365mb_setup(struct dsa_switch *ds) if (ret) goto out_teardown_irq; =20 + /* Enable all types of flooding */ + ret =3D rtl8365mb_port_set_ucast_flood(priv, i, true); + if (ret) + goto out_teardown_irq; + + ret =3D rtl8365mb_port_set_mcast_flood(priv, i, true); + if (ret) + goto out_teardown_irq; + + ret =3D rtl8365mb_port_set_bcast_flood(priv, i, true); + if (ret) + goto out_teardown_irq; + /* Set up per-port private data */ p->priv =3D priv; p->index =3D i; @@ -2686,6 +2809,8 @@ static const struct dsa_switch_ops rtl8365mb_switch_o= ps =3D { .phylink_get_caps =3D rtl8365mb_phylink_get_caps, .port_bridge_join =3D rtl8365mb_port_bridge_join, .port_bridge_leave =3D rtl8365mb_port_bridge_leave, + .port_pre_bridge_flags =3D rtl8365mb_port_pre_bridge_flags, + .port_bridge_flags =3D rtl8365mb_port_bridge_flags, .port_stp_state_set =3D rtl8365mb_port_stp_state_set, .port_fast_age =3D rtl8365mb_port_fast_age, .port_fdb_add =3D rtl8365mb_port_fdb_add, --=20 2.53.0 From nobody Wed Apr 1 08:15:25 2026 Received: from mail-vk1-f180.google.com (mail-vk1-f180.google.com [209.85.221.180]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 50682346766 for ; Tue, 31 Mar 2026 23:01:09 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.180 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774998070; cv=none; b=PUsaiztoZeJSjEqehaS8dwN758Ys5qZBsUPadVf7XsohxUQJBlnLIyhjdsxVuacvDrm0+VmMtXNdZGRbcH9lv/9w4vrPr7APBj8Ysyl+purE74Xcfas3WYnFVJRYdNzPB8p61V3kiIzeo8bTPc31E60OukGVYczLfWR0AufuCxY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774998070; c=relaxed/simple; bh=0rRRB1pnJMSltJnfuPdC7c14m71gUrKIm3lMrUNlwxc=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=oWE3RBBisZLtIGw7JC1wxvMzQ/7Hw/yHUm4i2L+6hnZ82mrF/fF4ooeFLfIdEpBH2Q46wb5N1bNqh3+UEQOMREHUfbIQTbd80ja5yaJGlXiwZLKRP8GsikwR9CvywKU8Q9rTBofZms2H/0W/khRgMVuwMbH7bkY4lVW/acp6ywA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=Dg5dNi6T; arc=none smtp.client-ip=209.85.221.180 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="Dg5dNi6T" Received: by mail-vk1-f180.google.com with SMTP id 71dfb90a1353d-5637886c92aso2971736e0c.0 for ; Tue, 31 Mar 2026 16:01:09 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1774998068; x=1775602868; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=nCcRDNWf/MJLY2UxfN+Xcd+3k+QnkQJ/sDWLTVG516k=; b=Dg5dNi6Tv/8f+lnqDF13xW5zFdyCc6T20FUlLh4Xy7fP8XbD6f7B57LeMqshSdHidl 5orKPH5ZpejoAaL4JqqEQ3v27tGY9+uwlGEUE5qYls6vFhwB7fNvALXVb7cdRL0yB3q2 YYVmDrFQy0NocvAyL18RuEAxWcJxQyp791uHxPBZodIwQezQFy62tmyPoGYEjyJRoMVN a8KUZZEsQ6stLxjKTOpI4lEvIaS64F/jjfqKtcj6E4Kr6idAT4YNTx8bY9l0HAhNdjna pv1t7087B0X5cuyItabM2eG9KLJ0nfl/vOWKsbtZjGP+r5uwMocbT8Uav4mP7EABoJTJ J9xA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1774998068; x=1775602868; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=nCcRDNWf/MJLY2UxfN+Xcd+3k+QnkQJ/sDWLTVG516k=; b=cNzv+bkndh/T4Nt4QPdnvLthhIBqYy5y+15bCfqHjfQTnR+67ye8Vl608ToN17ASPh sy8GmSHtQsR7YWT1XCBzAk/+utnoEuk0qWKKFsfeDef/LatMUb1WpSXihNORdjZBTAYK 2/mk3fLjIxKrQccX43023oLVpbExcg51eaLm2JkyFLT0ZS1iga9zn44cD9SztdzMbVqU j2VlpDuJknOjiCzyPlCw0/qv9YfM35DeSvWS78NAhdvyYZ0FUSvz5TBjFKW4M2A+ibiL MF2DHAaWrdZLahBUWhlpigP4Zwu8ZTncjaa33VJaH9AVL4eD9XPpvHRXDCppihMPD5D5 UUbw== X-Forwarded-Encrypted: i=1; AJvYcCVUAnDc1FkjgfCpobpEfDHQ1FuDp5k6aG+OFvtMqRa9KCAXLgVexA4UCdvTdBwS8oL4ozf9uChDYW+0KBw=@vger.kernel.org X-Gm-Message-State: AOJu0YyGIBsDv6eWty0im1WVwJvW0nPeWkmD+i7jOlt9+wvIaSaXxOOQ BD4FO2mm1PKaFkiVLR2kG0sY8aI0kTZJ8yBG3OG5hOr4Om/TDwuPVgAd X-Gm-Gg: ATEYQzy2cMe+7Jju2yMm06HYKBCz2kXXF1graHeRs8QX1YZdGlu9I8yshAYgpi4lFX/ 8MiIjW/joKIZSIOAsXRSdd1R+O0XpagljJh9nYnwd/Wmg0yMNjT6IdN7uIknONkv1GT9ogc0yLt w32IiOnRwV6499pqVV3sEb05BBJbWotWacYfGGDazDkr+oqhK3EsZ+FaqhmWhlornyR9CbYy4cE 2CN3BqkqwZU7ga+b0t6khb+mE7ZmXf9EMuMw6MzSUpyv+HVP7/V6sS+NlI+d7+zaHqb7dR1YDD7 VAAcmq2YTmJOZPgcA2HA109Cem639eOyg3p57oHYdSKekn7arQ0uMKBrFrKqOhBQxcz0vFgbAFt fsby2Wc2Bot4hyY93LukQDWEMpGPNSOcosiCGC9TduxowZGBkL/myHfXH+8xzI2cKqWnZJoJSmK 7u97vUkTye6b7Ex+dHPhpkWg4U2/zDGC/jg8iuzA== X-Received: by 2002:a05:6122:469a:b0:56a:f576:cfca with SMTP id 71dfb90a1353d-56d8a8184fbmr695578e0c.2.1774998068199; Tue, 31 Mar 2026 16:01:08 -0700 (PDT) Received: from tresc054937.tre-sc.gov.br ([187.65.210.13]) by smtp.gmail.com with ESMTPSA id 71dfb90a1353d-56d58a7ba96sm13948214e0c.17.2026.03.31.16.01.02 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 31 Mar 2026 16:01:06 -0700 (PDT) From: Luiz Angelo Daros de Luca Date: Tue, 31 Mar 2026 20:00:10 -0300 Subject: [net-next PATCH 10/10] net: dsa: tag_rtl8_4: set KEEP flag Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260331-realtek_forward-v1-10-44fb63033b7e@gmail.com> References: <20260331-realtek_forward-v1-0-44fb63033b7e@gmail.com> In-Reply-To: <20260331-realtek_forward-v1-0-44fb63033b7e@gmail.com> To: Andrew Lunn , Vladimir Oltean , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , Linus Walleij , =?utf-8?q?Alvin_=C5=A0ipraga?= , Yury Norov , Rasmus Villemoes , Russell King Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org, Luiz Angelo Daros de Luca X-Mailer: b4 0.15.1 KEEP=3D1 is needed because we should respect the format of the packet as the kernel sends it to us. Unless tx forward offloading is used, the kernel is giving us the packet exactly as it should leave the specified port on the wire. Until now this was not needed because the ports were always functioning in a standalone mode in a VLAN-unaware way, so the switch would not tag or untag frames anyway. But arguably it should have been KEEP=3D1 all along. Co-developed-by: Alvin =C5=A0ipraga Signed-off-by: Alvin =C5=A0ipraga Signed-off-by: Luiz Angelo Daros de Luca --- net/dsa/tag_rtl8_4.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/net/dsa/tag_rtl8_4.c b/net/dsa/tag_rtl8_4.c index b7ed39c5419f..852c6b88079a 100644 --- a/net/dsa/tag_rtl8_4.c +++ b/net/dsa/tag_rtl8_4.c @@ -99,6 +99,7 @@ #define RTL8_4_REASON_TRAP 80 =20 #define RTL8_4_LEARN_DIS BIT(5) +#define RTL8_4_KEEP BIT(7) =20 #define RTL8_4_TX GENMASK(3, 0) #define RTL8_4_RX GENMASK(10, 0) @@ -114,8 +115,9 @@ static void rtl8_4_write_tag(struct sk_buff *skb, struc= t net_device *dev, /* Set Protocol; zero REASON */ tag16[1] =3D htons(FIELD_PREP(RTL8_4_PROTOCOL, RTL8_4_PROTOCOL_RTL8365MB)= ); =20 - /* Zero EFID_EN, EFID, PRI_EN, PRI, VSEL, VIDX, KEEP; set LEARN_DIS */ - tag16[2] =3D htons(FIELD_PREP(RTL8_4_LEARN_DIS, 1)); + /* Zero EFID_EN, EFID, PRI_EN, PRI, VSEL, VIDX; set KEEP, LEARN_DIS */ + tag16[2] =3D htons(FIELD_PREP(RTL8_4_LEARN_DIS, 1) | + FIELD_PREP(RTL8_4_KEEP, 1)); =20 /* Zero ALLOW; set RX (CPU->switch) forwarding port mask */ tag16[3] =3D htons(FIELD_PREP(RTL8_4_RX, dsa_xmit_port_mask(skb, dev))); --=20 2.53.0