From nobody Thu Oct 2 05:03:34 2025 Received: from mail-pf1-f172.google.com (mail-pf1-f172.google.com [209.85.210.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 53BB130CB33 for ; Mon, 22 Sep 2025 13:12:58 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1758546779; cv=none; b=FPRBEswZc2pGcmdX3ZJCtWflIQZsjn6Mj++6CPcYXDGnBtx/5xVZ4KWC1ybleKY+ARHvX7hB8rn/Xolco/B5ITpzJlTBTzUDCj/ikC1bH4mbx386bi0ksYlUPjvq44sW7IPk7D9sbhTi/Cnk60oyE1Y6FgCU2HDohODYr0sN29g= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1758546779; c=relaxed/simple; bh=SAyUnJhCwMKSbhb7U5p01vudm4sy13VmR4oxIBmj0nM=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=UZQdRmrlcDUzOj3vn9+Oa1PmPRpctGlkVfQu2oScwfMLmfMRKyPzFw/HGCb7A9pCrCOC94QBwyxIiIK9/6FlAAMhVC8IsHBmIPFF9uz53Yhb40fF9juakESOONVUcm/8bx7eXmVLQ4Js068Uvb34Ux5THiogPL73eHH9t0xTxoM= 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=QdVfyIXV; arc=none smtp.client-ip=209.85.210.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="QdVfyIXV" Received: by mail-pf1-f172.google.com with SMTP id d2e1a72fcca58-77f2e960728so1517506b3a.2 for ; Mon, 22 Sep 2025 06:12:58 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1758546778; x=1759151578; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=P2cvRp1weqffRPvbmfOkQaVMZbMjAj/UHkPXTSQ25jI=; b=QdVfyIXVQgD8V8yNhNeo9v6DKyeuWl+MgNnxdDCSczYNxeL/ujp2p8PjCvFb+z9BB+ RU8S21IyMa1tELKR2JYFTyhZiveOq0wt5vxuJh7n9agTjeBjntrIo3YcGfuhFqL6R14w eKunBNbsYuhzmkoPdsWEHDU4ccRKK+DGBweWYAxd4gW4OBNH85s+m+UJq5QEYCXVYTUN zbz1EP7XEiLxGiyfyJGuopoeg/6hC4v69Xf7Svxg2D3YlXHVbdU+4u+2I/nKkZXKFABB 0BXbhY32YHxRzEEh+5MfKbdankBKagm68E2tPIPCbo7/p3F1KE9KhXXATjAVJXoiMDNN tlig== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1758546778; x=1759151578; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=P2cvRp1weqffRPvbmfOkQaVMZbMjAj/UHkPXTSQ25jI=; b=lCxO1KPPujUyIWAk2XxBaze40WqpC2U/YIvD6TYhzai7bGYIUwfM7PcqAXY/2B7wik qg1u+8WBD2W7dSmzY/RRLAUcD27hm/mHnGe/7Uaw180/XEAAp7lvEYRZIDnWd8/caJ5a l4HdmsEaJ42ZYkLzIjOiwDjQ7+KJE3EVw42pnSfneuZx9B1ZkBw361mZsKF6PASbbfUx 8sOpz2gWhXijRbe4M+qd60Dj5it4BYnbzkB455MT1EQyBEAQBA7BdyZsABlF3GAuCq97 2LIBuSUIsQTx5bQeplP6iHk3svnpDb/+QyZveI2xv97tCpE6AGcs2BfLfMs6o4vsdr5r NFZQ== X-Forwarded-Encrypted: i=1; AJvYcCX1Vs7mUg3h3ouMismNbOLIzxJ9ZaDeYcezB0CTAlvu0lLwnR07rG3mjGEHfHd1fWVdfsGLKaAd7BdocG8=@vger.kernel.org X-Gm-Message-State: AOJu0YzVA0c6hGUWeVZsq79/Fs2OdriadYenF1dVxd1jrwzEEsdY+k+5 FV7wkIs8KsRO7tEeOsRpp0NZ4sYnkUvpNxbxk6ss93OyRLlkEIxTV539 X-Gm-Gg: ASbGncunM3xjpJEZ1uTl/Wdx5lYhtdLS4H8RcTtnyfPIy0zHfUpK3CR3h9DlCfKE5ZB 2W4I2u6FczON2B97QrcqIp4QGuQ6ckfVhiqsqgu2XpQ4umg+M91NK2erncKn1lhgczsHHnU3i8m AUEE/w0by5YiaJC+gyUGqNxYuj4w46liPSflroTU9Phlfnh8F9O88/GVRbqenYKYIjDuY0x17rh 3Bd0RjVAqzTt60CwO0yDUyaK3Hg/cWzS7saqWptW0cgek4RIh7TJohsogTB1LYrDsi/Q20rqbUC IB4wTRhVPW9IWWdwmbT1J+TQk3pmICnTlWbh7MdDlo48CHo9uq9oKGG2VKzrlnvwcEefjuG616T qsbm9JK6QXCMVSXkPbrLE7SOHeCqOAA== X-Google-Smtp-Source: AGHT+IH6Ot1LK9QjB6FeC20glUfGDL/Gnk3tPXUiEAOeZtENxIjiU3TXsAP1et3k8LlFBwpL34RbSw== X-Received: by 2002:a17:903:1a0d:b0:264:f3ed:ee10 with SMTP id d9443c01a7336-269ba40abc9mr164331725ad.11.1758546777656; Mon, 22 Sep 2025 06:12:57 -0700 (PDT) Received: from d.home.yangfl.dn42 ([45.32.227.231]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2698016bff2sm130200055ad.35.2025.09.22.06.12.53 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 22 Sep 2025 06:12:57 -0700 (PDT) From: David Yang To: netdev@vger.kernel.org Cc: David Yang , Andrew Lunn , Vladimir Oltean , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Heiner Kallweit , Russell King , Simon Horman , devicetree@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH net-next v11 1/5] dt-bindings: ethernet-phy: add reverse SGMII phy interface type Date: Mon, 22 Sep 2025 21:11:39 +0800 Message-ID: <20250922131148.1917856-2-mmyangfl@gmail.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20250922131148.1917856-1-mmyangfl@gmail.com> References: <20250922131148.1917856-1-mmyangfl@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The "reverse SGMII" protocol name is a personal invention, derived from "reverse MII" and "reverse RMII", this means: "behave like an SGMII PHY". Signed-off-by: David Yang Acked-by: Rob Herring (Arm) --- Documentation/devicetree/bindings/net/ethernet-controller.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/net/ethernet-controller.yaml= b/Documentation/devicetree/bindings/net/ethernet-controller.yaml index 2c924d296a8f..8f190fe2208a 100644 --- a/Documentation/devicetree/bindings/net/ethernet-controller.yaml +++ b/Documentation/devicetree/bindings/net/ethernet-controller.yaml @@ -42,6 +42,7 @@ properties: - mii-lite - gmii - sgmii + - rev-sgmii - psgmii - qsgmii - qusgmii --=20 2.51.0 From nobody Thu Oct 2 05:03:34 2025 Received: from mail-pl1-f181.google.com (mail-pl1-f181.google.com [209.85.214.181]) (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 37BB030DD1B for ; Mon, 22 Sep 2025 13:13:02 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.181 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1758546783; cv=none; b=pKErkeV6n1A7vZXFlikEdv2kTLV2UZ+18fkS3+eYPvChehv2xxyKYzhLQ08ZPhKcC/1C06WZ0VKqvJtSmmrT32W1etDzcSnZX4YFcTIvhxMHX1egdlMVwCCWxmWHm30Z6XB6V33J4okcYxZfDi8cc8S5ggEKnOJNLW9UOCBkBlo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1758546783; c=relaxed/simple; bh=unxmjxm6h4F/9t72n3Zmx6ePZef1LSzn6zgM64G59vU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=JmUtZ53fYVRlamXBHwO96kW7Sf9YZfivUjkmUazKQxTFEZXm6OXQrT+/b9gp4bWrK9A7BcJLKdn/4duNRNox09MYOLZZxf3JgKvHsJaW6diT+aTqADOqg6col7rO1R7KNNEbQASBHZz8wrecCEHNfzzFQ/Y8lIYaWOxx1HN+t6M= 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=lO4nwvJH; arc=none smtp.client-ip=209.85.214.181 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="lO4nwvJH" Received: by mail-pl1-f181.google.com with SMTP id d9443c01a7336-2445826fd9dso51665505ad.3 for ; Mon, 22 Sep 2025 06:13:02 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1758546781; x=1759151581; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=6BICoezk7Aqii5uI8fB/6RDKEXOngB1t0y1HJi8V6fU=; b=lO4nwvJHnSTArmDFKn2QFzlyq2ZF2B+3hE3Fqe+IUO41INfwT46rH9RQYjeLkbGdwL AJNKmy6sfpOQnWFezfusoiwyi7jST+PvEBIZIlKd4SnyXdO8vQTRYpewQmkymiJpU/ln OqhPXOOkVFQ4nkUoqyuKlidgVYG5ZyJaXS3ZX4b3elQHARTK+5ytuTuWZnOvdfdvEZwH YEpl72tScxM7eNuRZ0tcnTere7tZfpn58ZtJhLxZcwUO2SXX/v6IXHfnhPmmL21LKgQr lANdHipf1y449d5OVb6eD8bPxw/hR3fDO/qZ1X98DkrhpnkSyMtpe0uWbd4yV0jayNQi dZ+Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1758546781; x=1759151581; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=6BICoezk7Aqii5uI8fB/6RDKEXOngB1t0y1HJi8V6fU=; b=cfZypAJWh2xHNvGbD8Sr1h9OBegi7kD1SKxaZ9Uogjfew5PaiZ5MFD9WVAySdMfYic oVregoIHF1itLEf86JVVLXbYQ4M/RNp2uekkTx4BBJ0lf6tYw2j+7sjVaLCwt6dlbW7e +NH8Xwa+khkdehtdR6GsQfLaARmYm2PuhsvOlfPcnydl8Pijr12Z2vHwcf8waxngc6WR Co76JvaILNCbMnQcVSVGLUjII5BOtp9NL+tt8OssY0l18qfF0x1otCuLIXgDA2Jm1OA2 cO6jMjPXiWxaaQ36WOqvE5u6zjs/Fu9iKxic+4SveQJGHlwgJVjxTMJUBg+DQXVXR6Xc yQ+w== X-Forwarded-Encrypted: i=1; AJvYcCWrnvF5icsids5Y4FPE58sEV/tYQFUCcyZv0thzd//5ucuBGfhy/bQliQAHxajhddxEmoImhT+bce//Gp8=@vger.kernel.org X-Gm-Message-State: AOJu0YxXVR4jYLnUNT/lbGbU+n826Zj2Xs/J3U9+Qyn9LKBI08lNfAHj FvEaolD4/AENs6IyEyFlmmXRyVITYhGbeSGjz31LpKP43G/FaQdTcR34 X-Gm-Gg: ASbGncuiUOG6HmZKxuqviL+7HoTRypddt1PPypwbfUwg6LxR5FgKjdmPKzp00QPqnL7 jCHeg4x107lgebjTJDJSWvqN/183JnlCVOh6pCGT3DDY65YY+AbpNh6ex4c7GxTNutAUYkuXeq/ tHdyHZQQGa8LbTC2JjnS4aZs6bjWOwPN9djV6D0tTsZ4WbMMGKwZQr71AIwkSRnU2176fBKJffd Q7FEHdLo5bCQLA9m4A1AEsn8A7H/M0zuUjaQO6avf/GSNzFNCuMbUYSjPX9JQEYzkT4sUjTUZ5O qi3rh3SNaD8a1Ajm184oFUa+PKSUjhp804d3275YvOnsNi11mEWBJG4FXSzeH9XzoLZ65hHKckr f7M6yyAFnMYBbPF7yOa6KhqQKCbF+9w== X-Google-Smtp-Source: AGHT+IFCaMWxLEAqaH9OP6YhIlxxfOIJkeoBdqxz5qfmubo3eJBUEdY3PUmuBFtbQg3xg9PFsFXEEA== X-Received: by 2002:a17:903:ac3:b0:274:9dae:6a6d with SMTP id d9443c01a7336-2749dae6c18mr64125005ad.34.1758546781613; Mon, 22 Sep 2025 06:13:01 -0700 (PDT) Received: from d.home.yangfl.dn42 ([45.32.227.231]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2698016bff2sm130200055ad.35.2025.09.22.06.12.57 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 22 Sep 2025 06:13:01 -0700 (PDT) From: David Yang To: netdev@vger.kernel.org Cc: David Yang , Andrew Lunn , Vladimir Oltean , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Heiner Kallweit , Russell King , Simon Horman , devicetree@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH net-next v11 2/5] net: phy: introduce PHY_INTERFACE_MODE_REVSGMII Date: Mon, 22 Sep 2025 21:11:40 +0800 Message-ID: <20250922131148.1917856-3-mmyangfl@gmail.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20250922131148.1917856-1-mmyangfl@gmail.com> References: <20250922131148.1917856-1-mmyangfl@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The "reverse SGMII" protocol name is a personal invention, derived from "reverse MII" and "reverse RMII", this means: "behave like an SGMII PHY". Signed-off-by: David Yang Reviewed-by: Maxime Chevallier --- drivers/net/phy/phy-core.c | 1 + drivers/net/phy/phy_caps.c | 1 + drivers/net/phy/phylink.c | 1 + include/linux/phy.h | 4 ++++ 4 files changed, 7 insertions(+) diff --git a/drivers/net/phy/phy-core.c b/drivers/net/phy/phy-core.c index 605ca20ae192..074645840cd5 100644 --- a/drivers/net/phy/phy-core.c +++ b/drivers/net/phy/phy-core.c @@ -132,6 +132,7 @@ int phy_interface_num_ports(phy_interface_t interface) case PHY_INTERFACE_MODE_TRGMII: case PHY_INTERFACE_MODE_USXGMII: case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_REVSGMII: case PHY_INTERFACE_MODE_SMII: case PHY_INTERFACE_MODE_1000BASEX: case PHY_INTERFACE_MODE_2500BASEX: diff --git a/drivers/net/phy/phy_caps.c b/drivers/net/phy/phy_caps.c index 2cc9ee97e867..9a9a8afc056f 100644 --- a/drivers/net/phy/phy_caps.c +++ b/drivers/net/phy/phy_caps.c @@ -299,6 +299,7 @@ unsigned long phy_caps_from_interface(phy_interface_t i= nterface) case PHY_INTERFACE_MODE_PSGMII: case PHY_INTERFACE_MODE_QSGMII: case PHY_INTERFACE_MODE_QUSGMII: + case PHY_INTERFACE_MODE_REVSGMII: case PHY_INTERFACE_MODE_SGMII: case PHY_INTERFACE_MODE_GMII: link_caps |=3D BIT(LINK_CAPA_1000HD) | BIT(LINK_CAPA_1000FD); diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index 1b06805f1bd7..e8e237fb9d35 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -255,6 +255,7 @@ static int phylink_interface_max_speed(phy_interface_t = interface) case PHY_INTERFACE_MODE_PSGMII: case PHY_INTERFACE_MODE_QSGMII: case PHY_INTERFACE_MODE_QUSGMII: + case PHY_INTERFACE_MODE_REVSGMII: case PHY_INTERFACE_MODE_SGMII: case PHY_INTERFACE_MODE_GMII: return SPEED_1000; diff --git a/include/linux/phy.h b/include/linux/phy.h index 7da9e19471c9..42d5c1f4d8ad 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -107,6 +107,7 @@ extern const int phy_basic_ports_array[3]; * @PHY_INTERFACE_MODE_LAUI: 50 Gigabit Attachment Unit Interface * @PHY_INTERFACE_MODE_100GBASEP: 100GBase-P - with Clause 134 FEC * @PHY_INTERFACE_MODE_MIILITE: MII-Lite - MII without RXER TXER CRS COL + * @PHY_INTERFACE_MODE_REVSGMII: Serial gigabit media-independent interfac= e in PHY role * @PHY_INTERFACE_MODE_MAX: Book keeping * * Describes the interface between the MAC and PHY. @@ -152,6 +153,7 @@ typedef enum { PHY_INTERFACE_MODE_LAUI, PHY_INTERFACE_MODE_100GBASEP, PHY_INTERFACE_MODE_MIILITE, + PHY_INTERFACE_MODE_REVSGMII, PHY_INTERFACE_MODE_MAX, } phy_interface_t; =20 @@ -281,6 +283,8 @@ static inline const char *phy_modes(phy_interface_t int= erface) return "100gbase-p"; case PHY_INTERFACE_MODE_MIILITE: return "mii-lite"; + case PHY_INTERFACE_MODE_REVSGMII: + return "rev-sgmii"; default: return "unknown"; } --=20 2.51.0 From nobody Thu Oct 2 05:03:34 2025 Received: from mail-pl1-f178.google.com (mail-pl1-f178.google.com [209.85.214.178]) (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 15E5930E82C for ; Mon, 22 Sep 2025 13:13:06 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.178 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1758546788; cv=none; b=XhtJF3fc0wT0vxMWdRZuIRz56FNev8Uo81g+0g45URVE4/ueEqfQSVTw8/YmkCN18apbuL7vBbSLvqQA5lIxs8YcdCPZfn+AGsSb4aRb9feZu+vlmzpLs/1vlgrUpEP2reWMcwCdkou4IX6YVdv81oUMpZTlLIt160/jW6GYRjA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1758546788; c=relaxed/simple; bh=60sKW7i1vbbtY0FuzPNlPEuK/DTNRLiRrb1M3U5kNUc=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Uitin39wHqx5OWyjKTUNtcCjsSNLaDomimoD7GMms2oV6oP1D5eJk40haRhs4w/+bfwYX6rDS4GO4vITJE3dRVAQhEMSpofwZ3uJo6tK+6dM+h0pUOIhYMSsv8mmCaNbzrFPfd85hXPMm8Zc1/8PMkLEVEbiHWJL6qJ63/vj6RE= 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=gNFItJxS; arc=none smtp.client-ip=209.85.214.178 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="gNFItJxS" Received: by mail-pl1-f178.google.com with SMTP id d9443c01a7336-2698384978dso36694195ad.0 for ; Mon, 22 Sep 2025 06:13:06 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1758546786; x=1759151586; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=hcC9XUY8FVCgBMwswacD7pUTLZ7aCAKIG4tpodOwdGI=; b=gNFItJxSCyvH0CpUxO9BR9dJ1CP8WL8cPSehUt2rsMkqUUXjZDB4+WqsAosmUVBw0H ra0QdJcEUEG/l6tazFgeHJDpuNg4bA+YVIKYQJ5RxCgL5MLuV55QZ/mWLPT6lpNhAnmA ZdqjBCINixkZZCm2QrRpGjwVcYfrU834dS2WE2jHw2WcDNUTATeH7Jy1ZEobrqBc0/DZ ibyfSZFX7r8CLj6bNmZ8Jx4ycueKTseIukcgGqEHf0myaWJQ1bh8kaRQjPfxKaZvzoKK 56b1oJf351MRDZbtIRmG7uGyl7yeaqDTz45200PqI5i4zcpD/waopdZRdmY88S2JhJ3f hsTg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1758546786; x=1759151586; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=hcC9XUY8FVCgBMwswacD7pUTLZ7aCAKIG4tpodOwdGI=; b=qszoRkSM5GqJ5L/3SHYppnqKf+1Pk6CabDPB7fMInWvtPECyPYuP5Q24sbkEq6IMyQ /bvb3j1QtPTLl14A1RGj3GBq8Z479usmm+Xx1kMbHWjiZfj0W61/nyAjmdM1HPq9P72X HxUwIsLsX0DOO42QaX5wdISHJqkKxo/+9yJYU20c4/YALKkU9TVZ212+LVk9wpHqR/Y2 Gcg8F7UNMAovLJucKiZnO0o/0pSI2usvByn8Vmz23bNZrci1aHWINMsULAE/p/LAsn5P wvGr0o6cXWeIYrltJWgOi6+kfK1Bv6/DR8pBOjKqWRWooD2GRy9LiTyszYr8k7wa5Rne impQ== X-Forwarded-Encrypted: i=1; AJvYcCWRD+Ns1WCXaCg2mPmYGaacZ596GF0cRxGJDWyoFd9WvrEPJyh6tw6VVcD1TmviBMSLI/+KE8RZAGOMIrI=@vger.kernel.org X-Gm-Message-State: AOJu0YywOoJqVnZ3ygl4UjoVRrantSP4XYTWW4lPtv9UfSUPkPu5pQrS +EdZERNcl3G03njYW4NgavXiQDOiEiHQBUyrA8vWaqMpkEcOo53naF/5 X-Gm-Gg: ASbGncsb+58uszAzYkBaahcWCzN5dkwZRcwa7qc/ahwqIlnhwtViOBBWDAZSIBNYvVb GIISAQ11cSJGVze0WNuLH1XZufL1Da1pSRHqYwfW7l7Qx/ehAcyyeigqLUW4doMh/j1ngJEs3Ry h6rXzdOWoQMLRULZTrEfP+01waXGnKxY6ay8p82Iccyo1pUam+b12TruCKHhoHRMmXerVWLcSFD d86qD3ZxMqxI4RMS15W2EhBqP6JkCQP3hhJOI1PRe8ZJLDIQYHTX7gP7Fk173xMkJsjYCV7Ym3t 38xgiscR8AqGbmrA+L3wslgQ9LEYGGrHCDGY2CpTUMkVjmp/VgYlKXyE6YSBSOqY9nop2SdmoaS 8S1zmqtXI84e6KpSQ50dpnPXGkO04eQ== X-Google-Smtp-Source: AGHT+IGFTJtSRX8IY/TBdGJa+aIaflKvJYjPnVoFytK6InI2+78W4kFt4SIKFk6Fxg0L8uAVRulAFA== X-Received: by 2002:a17:903:1b10:b0:24d:64bc:1495 with SMTP id d9443c01a7336-269ba528961mr145240865ad.41.1758546786207; Mon, 22 Sep 2025 06:13:06 -0700 (PDT) Received: from d.home.yangfl.dn42 ([45.32.227.231]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2698016bff2sm130200055ad.35.2025.09.22.06.13.01 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 22 Sep 2025 06:13:05 -0700 (PDT) From: David Yang To: netdev@vger.kernel.org Cc: David Yang , Andrew Lunn , Vladimir Oltean , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Heiner Kallweit , Russell King , Simon Horman , devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, Krzysztof Kozlowski Subject: [PATCH net-next v11 3/5] dt-bindings: net: dsa: yt921x: Add Motorcomm YT921x switch support Date: Mon, 22 Sep 2025 21:11:41 +0800 Message-ID: <20250922131148.1917856-4-mmyangfl@gmail.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20250922131148.1917856-1-mmyangfl@gmail.com> References: <20250922131148.1917856-1-mmyangfl@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The Motorcomm YT921x series is a family of Ethernet switches with up to 8 internal GbE PHYs and up to 2 GMACs. Signed-off-by: David Yang Reviewed-by: Krzysztof Kozlowski --- .../bindings/net/dsa/motorcomm,yt921x.yaml | 169 ++++++++++++++++++ 1 file changed, 169 insertions(+) create mode 100644 Documentation/devicetree/bindings/net/dsa/motorcomm,yt9= 21x.yaml diff --git a/Documentation/devicetree/bindings/net/dsa/motorcomm,yt921x.yam= l b/Documentation/devicetree/bindings/net/dsa/motorcomm,yt921x.yaml new file mode 100644 index 000000000000..ff03bff0be4f --- /dev/null +++ b/Documentation/devicetree/bindings/net/dsa/motorcomm,yt921x.yaml @@ -0,0 +1,169 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/dsa/motorcomm,yt921x.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Motorcomm YT921x Ethernet switch family + +maintainers: + - David Yang + +description: | + The Motorcomm YT921x series is a family of Ethernet switches with up to 8 + internal GbE PHYs and up to 2 GMACs, including: + + - YT9215S / YT9215RB / YT9215SC: 5 GbE PHYs (Port 0-4) + 2 GMACs (Port= 8-9) + - YT9213NB: 2 GbE PHYs (Port 1/3) + 1 GMAC (Port 9) + - YT9214NB: 2 GbE PHYs (Port 1/3) + 2 GMACs (Port 8-9) + - YT9218N: 8 GbE PHYs (Port 0-7) + - YT9218MB: 8 GbE PHYs (Port 0-7) + 2 GMACs (Port 8-9) + + Any port can be used as the CPU port. + +properties: + compatible: + const: motorcomm,yt9215 + + reg: + enum: [0x0, 0x1d] + + reset-gpios: + maxItems: 1 + + mdio: + $ref: /schemas/net/mdio.yaml# + unevaluatedProperties: false + description: + Internal MDIO bus for the internal GbE PHYs. PHY 0-7 are used for Po= rt + 0-7 respectively. + + mdio-external: + $ref: /schemas/net/mdio.yaml# + unevaluatedProperties: false + description: + External MDIO bus to access external components. External PHYs for G= MACs + (Port 8-9) are expected to be connected to the external MDIO bus in + vendor's reference design, but that is not a hard limitation from the + chip. + +required: + - compatible + - reg + +allOf: + - $ref: dsa.yaml#/$defs/ethernet-ports + +unevaluatedProperties: false + +examples: + - | + #include + + mdio { + #address-cells =3D <1>; + #size-cells =3D <0>; + + switch@1d { + compatible =3D "motorcomm,yt9215"; + /* default 0x1d, alternate 0x0 */ + reg =3D <0x1d>; + reset-gpios =3D <&tlmm 39 GPIO_ACTIVE_LOW>; + + mdio { + #address-cells =3D <1>; + #size-cells =3D <0>; + + sw_phy0: phy@0 { + reg =3D <0x0>; + }; + + sw_phy1: phy@1 { + reg =3D <0x1>; + }; + + sw_phy2: phy@2 { + reg =3D <0x2>; + }; + + sw_phy3: phy@3 { + reg =3D <0x3>; + }; + + sw_phy4: phy@4 { + reg =3D <0x4>; + }; + }; + + mdio-external { + #address-cells =3D <1>; + #size-cells =3D <0>; + + phy1: phy@b { + reg =3D <0xb>; + }; + }; + + ethernet-ports { + #address-cells =3D <1>; + #size-cells =3D <0>; + + ethernet-port@0 { + reg =3D <0>; + label =3D "lan1"; + phy-mode =3D "internal"; + phy-handle =3D <&sw_phy0>; + }; + + ethernet-port@1 { + reg =3D <1>; + label =3D "lan2"; + phy-mode =3D "internal"; + phy-handle =3D <&sw_phy1>; + }; + + ethernet-port@2 { + reg =3D <2>; + label =3D "lan3"; + phy-mode =3D "internal"; + phy-handle =3D <&sw_phy2>; + }; + + ethernet-port@3 { + reg =3D <3>; + label =3D "lan4"; + phy-mode =3D "internal"; + phy-handle =3D <&sw_phy3>; + }; + + ethernet-port@4 { + reg =3D <4>; + label =3D "lan5"; + phy-mode =3D "internal"; + phy-handle =3D <&sw_phy4>; + }; + + /* CPU port */ + ethernet-port@8 { + reg =3D <8>; + phy-mode =3D "rev-sgmii"; + ethernet =3D <ð0>; + + fixed-link { + speed =3D <1000>; + full-duplex; + pause; + asym-pause; + }; + }; + + /* if external phy is connected to a MAC */ + ethernet-port@9 { + reg =3D <9>; + label =3D "wan"; + phy-mode =3D "rgmii-id"; + phy-handle =3D <&phy1>; + }; + }; + }; + }; --=20 2.51.0 From nobody Thu Oct 2 05:03:34 2025 Received: from mail-pg1-f177.google.com (mail-pg1-f177.google.com [209.85.215.177]) (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 7132730EF6B for ; Mon, 22 Sep 2025 13:13:11 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.177 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1758546793; cv=none; b=GwRBPYXjdwKoHStcshkzSfWajRfDM0MNXghPBr4XqIk8309Y8IjfCBeEikbK753JtT+5sNexvd4m+24J8OSyGYCAEpE6k4vHhbii01EFrfo5qtCOrVJjvnmWHyukZF148582EqTibBw0oV3ilGUdM9+0udUcATNnt3cNyvBqtBs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1758546793; c=relaxed/simple; bh=SZ2v3HFrpzfTIKrJkGYFsVPTstS5wi/CPHBzBdXSC5Y=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=GlelS+0UQEbYDcWoJyO7iW2AVwsmfqcGsIoEgKyJxGH9hy/2wsdD4ve+UsHY5yQ10F5EsDMdZyYZq2MTIJ084KOMpXVxD/mIQd8E4N8MgNqsJsV1J8IcFTxbuSYbgqFJTDK9g19S6mY8zQma90BlDNsqCvhDjaq4V/yl0POmFO0= 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=cQcd9cCC; arc=none smtp.client-ip=209.85.215.177 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="cQcd9cCC" Received: by mail-pg1-f177.google.com with SMTP id 41be03b00d2f7-b554bb615dcso624596a12.1 for ; Mon, 22 Sep 2025 06:13:11 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1758546790; x=1759151590; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=8JgmLAllM6GJ7LAy07OgoFPN7Fl/16NMS6tmzU8OBXw=; b=cQcd9cCCQxxU1Ym62rleQoANazSRw0hg6SGsV+Cu3BJhNqr1G6icbt42NuG3dfB29K iEtKS/XLJlT/2pV1o0duTriW8yoxei9z4+j0MXt/sT6WkqxSeTWjtRLpArEEVoT77MuS VWZQRFEd7FPsP1RdGrC+q5GVAH4SiC1URS8Fz8MlPKlJW/n0tAvfzTVe/Bk9jLhozfWN hzdBF6hWTUgbNiA8OJrkJ/VMCKRd+vwutB2A5jFS1gDFlWVlivQgZVW2sekNnKygmyiD FAAsWx30rWF9zCQbs03xb4mURGKYlIaSydBIISrrPJmXnf9cxdirxzw4b5jmnKegIMRv YxBA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1758546790; x=1759151590; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=8JgmLAllM6GJ7LAy07OgoFPN7Fl/16NMS6tmzU8OBXw=; b=BDJqNV/BsYQXbal0Wrhlo7uJ4BM2JqwrPSxscY8mzCrrQ1Itl/J16WsJRVVvY6bdlK f3hTgfJzHkc4A5vHvvbZOVdODeRappuuMJIPDDGkijLGi+qHGyu17jiqJ1PI7hGgyoT9 WoOIDOHvNGtL1AXGTSl+CLWpzR9xL4wN04dt1P9OpoEuQQnmMlGHEGv6AazKb4Im6qMK X+xlwHGkrzLVJ1X+x0/7lxEvTu552Hpk8QgCbjqdHTwSjJqhmwbxm0u3wHFD9RFy9ND3 28sYp2z3wrRJhzLGuImCVqWbUv/jCtPHFK+pSn/bbrSeIqznVa02JY9qiar4vNWYJq6X EjkQ== X-Forwarded-Encrypted: i=1; AJvYcCVpwCGM3vi4hCinytQT/fcgAqwb6+MdqOp4FU774OWDcYQxgO7S+9dT2JCoR18XzjiuHHnHNZLaJtRz148=@vger.kernel.org X-Gm-Message-State: AOJu0Yx06NSH5Fq23S4jW1o2HcoeezbZWCIxyx4DGfX70zKBHMQKOuGg sknyXVpvxR19ifhpjNm0OYzt55tBS55FYW+wyPpmB1ljyJbv8w+BtuQV X-Gm-Gg: ASbGncsw1Ckyl1xywuqcZtu6Aq9WCjHFhYcUb1biLzH5/mqnAJdWlhwkpqKEUBlgMOS IWw3Kzafs/fR1sJU44lz1SLzf6qApzEZFHDBYQzw3UcuSiklhnoQuY9/MganabjTvF/MtAhIyhv uLG7e+kJJnbc/V6N9M0A3W1q2+yT1vQd5ObF0YiDbN2toaBEiWuAT5J7xztN3UQ9aCL5vnPk6e0 ZBrQeoHZbAsTdpbjqt7GEdQkd9EJFSuy3KNkoyOkq1lXHdlrrYD6gFtJzTeXs+0n2bAEOT3+5eB slW+4Zn7lf5msciy4/6n5L69xFEIak7Xw1xIwoyr72Yqx/ofTe2ZSoGFev/3gKpSO/qxoIvjov8 2eb7YbiKOUZLcWpnNh1VpXE7GWyLuwg== X-Google-Smtp-Source: AGHT+IEek/VOljtauc5TH7hc6cPocWNBiK8C+Y884wsUqhxJwXcfOujKjSs2LfzV9IGUPK1XBs7/Qg== X-Received: by 2002:a17:902:8e83:b0:246:e1b6:f9b0 with SMTP id d9443c01a7336-269ba4279d6mr125188015ad.18.1758546790517; Mon, 22 Sep 2025 06:13:10 -0700 (PDT) Received: from d.home.yangfl.dn42 ([45.32.227.231]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2698016bff2sm130200055ad.35.2025.09.22.06.13.06 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 22 Sep 2025 06:13:10 -0700 (PDT) From: David Yang To: netdev@vger.kernel.org Cc: David Yang , Andrew Lunn , Vladimir Oltean , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Heiner Kallweit , Russell King , Simon Horman , devicetree@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH net-next v11 4/5] net: dsa: tag_yt921x: add support for Motorcomm YT921x tags Date: Mon, 22 Sep 2025 21:11:42 +0800 Message-ID: <20250922131148.1917856-5-mmyangfl@gmail.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20250922131148.1917856-1-mmyangfl@gmail.com> References: <20250922131148.1917856-1-mmyangfl@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add support for Motorcomm YT921x tags, which includes a proper configurable ethertype field (default to 0x9988). Signed-off-by: David Yang Reviewed-by: Andrew Lunn --- include/net/dsa.h | 2 + include/uapi/linux/if_ether.h | 1 + net/dsa/Kconfig | 6 ++ net/dsa/Makefile | 1 + net/dsa/tag_yt921x.c | 141 ++++++++++++++++++++++++++++++++++ 5 files changed, 151 insertions(+) create mode 100644 net/dsa/tag_yt921x.c diff --git a/include/net/dsa.h b/include/net/dsa.h index d73ea0880066..67762fdaf3c7 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -55,6 +55,7 @@ struct tc_action; #define DSA_TAG_PROTO_LAN937X_VALUE 27 #define DSA_TAG_PROTO_VSC73XX_8021Q_VALUE 28 #define DSA_TAG_PROTO_BRCM_LEGACY_FCS_VALUE 29 +#define DSA_TAG_PROTO_YT921X_VALUE 30 =20 enum dsa_tag_protocol { DSA_TAG_PROTO_NONE =3D DSA_TAG_PROTO_NONE_VALUE, @@ -87,6 +88,7 @@ enum dsa_tag_protocol { DSA_TAG_PROTO_RZN1_A5PSW =3D DSA_TAG_PROTO_RZN1_A5PSW_VALUE, DSA_TAG_PROTO_LAN937X =3D DSA_TAG_PROTO_LAN937X_VALUE, DSA_TAG_PROTO_VSC73XX_8021Q =3D DSA_TAG_PROTO_VSC73XX_8021Q_VALUE, + DSA_TAG_PROTO_YT921X =3D DSA_TAG_PROTO_YT921X_VALUE, }; =20 struct dsa_switch; diff --git a/include/uapi/linux/if_ether.h b/include/uapi/linux/if_ether.h index 69e0457eb200..cfd200c87e5e 100644 --- a/include/uapi/linux/if_ether.h +++ b/include/uapi/linux/if_ether.h @@ -114,6 +114,7 @@ #define ETH_P_QINQ1 0x9100 /* deprecated QinQ VLAN [ NOT AN OFFICIALLY RE= GISTERED ID ] */ #define ETH_P_QINQ2 0x9200 /* deprecated QinQ VLAN [ NOT AN OFFICIALLY RE= GISTERED ID ] */ #define ETH_P_QINQ3 0x9300 /* deprecated QinQ VLAN [ NOT AN OFFICIALLY RE= GISTERED ID ] */ +#define ETH_P_YT921X 0x9988 /* Motorcomm YT921x DSA [ NOT AN OFFICIALLY R= EGISTERED ID ] */ #define ETH_P_EDSA 0xDADA /* Ethertype DSA [ NOT AN OFFICIALLY REGISTERED= ID ] */ #define ETH_P_DSA_8021Q 0xDADB /* Fake VLAN Header for DSA [ NOT AN OFFIC= IALLY REGISTERED ID ] */ #define ETH_P_DSA_A5PSW 0xE001 /* A5PSW Tag Value [ NOT AN OFFICIALLY REG= ISTERED ID ] */ diff --git a/net/dsa/Kconfig b/net/dsa/Kconfig index 869cbe57162f..6b94028b1fcc 100644 --- a/net/dsa/Kconfig +++ b/net/dsa/Kconfig @@ -190,4 +190,10 @@ config NET_DSA_TAG_XRS700X Say Y or M if you want to enable support for tagging frames for Arrow SpeedChips XRS700x switches that use a single byte tag trailer. =20 +config NET_DSA_TAG_YT921X + tristate "Tag driver for Motorcomm YT921x switches" + help + Say Y or M if you want to enable support for tagging frames for + Motorcomm YT921x switches. + endif diff --git a/net/dsa/Makefile b/net/dsa/Makefile index 555c07cfeb71..4b011a1d5c87 100644 --- a/net/dsa/Makefile +++ b/net/dsa/Makefile @@ -39,6 +39,7 @@ obj-$(CONFIG_NET_DSA_TAG_SJA1105) +=3D tag_sja1105.o obj-$(CONFIG_NET_DSA_TAG_TRAILER) +=3D tag_trailer.o obj-$(CONFIG_NET_DSA_TAG_VSC73XX_8021Q) +=3D tag_vsc73xx_8021q.o obj-$(CONFIG_NET_DSA_TAG_XRS700X) +=3D tag_xrs700x.o +obj-$(CONFIG_NET_DSA_TAG_YT921X) +=3D tag_yt921x.o =20 # for tracing framework to find trace.h CFLAGS_trace.o :=3D -I$(src) diff --git a/net/dsa/tag_yt921x.c b/net/dsa/tag_yt921x.c new file mode 100644 index 000000000000..995da44f0a2a --- /dev/null +++ b/net/dsa/tag_yt921x.c @@ -0,0 +1,141 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Motorcomm YT921x Switch Extended CPU Port Tagging + * + * Copyright (c) 2025 David Yang + * + * +----+----+-------+-----+----+--------- + * | DA | SA | TagET | Tag | ET | Payload ... + * +----+----+-------+-----+----+--------- + * 6 6 2 6 2 N + * + * Tag Ethertype: CPU_TAG_TPID_TPID (default: ETH_P_YT921X =3D 0x9988) + * * Hardcoded for the moment, but still configurable. Discuss it if the= re + * are conflicts somewhere and/or you want to change it for some reaso= n. + * Tag: + * 2: VLAN Tag + * 2: Rx Port + * 15b: Rx Port Valid + * 14b-11b: Rx Port + * 10b-0b: Cmd? + * 2: Tx Port(s) + * 15b: Tx Port(s) Valid + * 10b-0b: Tx Port(s) Mask + */ + +#include + +#include "tag.h" + +#define YT921X_TAG_NAME "yt921x" + +#define YT921X_TAG_LEN 8 + +#define YT921X_TAG_PORT_EN BIT(15) +#define YT921X_TAG_RX_PORT_M GENMASK(14, 11) +#define YT921X_TAG_RX_CMD_M GENMASK(10, 0) +#define YT921X_TAG_RX_CMD(x) FIELD_PREP(YT921X_TAG_RX_CMD_M, (x)) +#define YT921X_TAG_RX_CMD_FORWARDED 0x80 +#define YT921X_TAG_RX_CMD_UNK_UCAST 0xb2 +#define YT921X_TAG_RX_CMD_UNK_MCAST 0xb4 +#define YT921X_TAG_TX_PORTS_M GENMASK(10, 0) +#define YT921X_TAG_TX_PORTn(port) BIT(port) + +static struct sk_buff * +yt921x_tag_xmit(struct sk_buff *skb, struct net_device *netdev) +{ + struct dsa_port *dp =3D dsa_user_to_port(netdev); + unsigned int port =3D dp->index; + __be16 *tag; + u16 tx; + + skb_push(skb, YT921X_TAG_LEN); + dsa_alloc_etype_header(skb, YT921X_TAG_LEN); + + tag =3D dsa_etype_header_pos_tx(skb); + + tag[0] =3D htons(ETH_P_YT921X); + /* VLAN tag unrelated when TX */ + tag[1] =3D 0; + tag[2] =3D 0; + tx =3D YT921X_TAG_PORT_EN | YT921X_TAG_TX_PORTn(port); + tag[3] =3D htons(tx); + + return skb; +} + +static struct sk_buff * +yt921x_tag_rcv(struct sk_buff *skb, struct net_device *netdev) +{ + unsigned int port; + __be16 *tag; + u16 cmd; + u16 rx; + + if (unlikely(!pskb_may_pull(skb, YT921X_TAG_LEN))) + return NULL; + + tag =3D dsa_etype_header_pos_rx(skb); + + if (unlikely(tag[0] !=3D htons(ETH_P_YT921X))) { + dev_warn_ratelimited(&netdev->dev, + "Unexpected EtherType 0x%04x\n", + ntohs(tag[0])); + return NULL; + } + + /* Locate which port this is coming from */ + rx =3D ntohs(tag[2]); + if (unlikely((rx & YT921X_TAG_PORT_EN) =3D=3D 0)) { + dev_warn_ratelimited(&netdev->dev, + "Unexpected rx tag 0x%04x\n", rx); + return NULL; + } + + port =3D FIELD_GET(YT921X_TAG_RX_PORT_M, rx); + skb->dev =3D dsa_conduit_find_user(netdev, 0, port); + if (unlikely(!skb->dev)) { + dev_warn_ratelimited(&netdev->dev, + "Couldn't decode source port %u\n", port); + return NULL; + } + + cmd =3D FIELD_GET(YT921X_TAG_RX_CMD_M, rx); + switch (cmd) { + case YT921X_TAG_RX_CMD_FORWARDED: + /* Already forwarded by hardware */ + dsa_default_offload_fwd_mark(skb); + break; + case YT921X_TAG_RX_CMD_UNK_UCAST: + case YT921X_TAG_RX_CMD_UNK_MCAST: + /* NOTE: hardware doesn't distinguish between TRAP (copy to CPU + * only) and COPY (forward and copy to CPU). In order to perform + * a soft switch, NEVER use COPY action in the switch driver. + */ + break; + default: + dev_warn_ratelimited(&netdev->dev, + "Unexpected rx cmd 0x%02x\n", cmd); + break; + } + + /* Remove YT921x tag and update checksum */ + skb_pull_rcsum(skb, YT921X_TAG_LEN); + dsa_strip_etype_header(skb, YT921X_TAG_LEN); + + return skb; +} + +static const struct dsa_device_ops yt921x_netdev_ops =3D { + .name =3D YT921X_TAG_NAME, + .proto =3D DSA_TAG_PROTO_YT921X, + .xmit =3D yt921x_tag_xmit, + .rcv =3D yt921x_tag_rcv, + .needed_headroom =3D YT921X_TAG_LEN, +}; + +MODULE_DESCRIPTION("DSA tag driver for Motorcomm YT921x switches"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_YT921X, YT921X_TAG_NAME); + +module_dsa_tag_driver(yt921x_netdev_ops); --=20 2.51.0 From nobody Thu Oct 2 05:03:34 2025 Received: from mail-pl1-f179.google.com (mail-pl1-f179.google.com [209.85.214.179]) (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 BA56B30F524 for ; Mon, 22 Sep 2025 13:13:16 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.179 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1758546801; cv=none; b=upnm9IUOfMLKw09wqtCJiVLckLXQugWKJ6rFljTgXLO1SO+3/Kx9dxir7bKZ5NU5wqlTWVAdNB0hUhGPeHbM83n7T0aSwfn2Gg/+LAwhm6sBo4cYO6l4nQLCOtPGq52ImCKo6KWHx1P0y/v1RUWeVNIYuTrxJSHcYwZg65iWJb8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1758546801; c=relaxed/simple; bh=ilVGCQM9AmLtgy41t7cEBAve3mXsD7XYTiAq/WEPYew=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=l9xNyDUovPAGNxB+/rpxs4oypgKa29cI2vgLxQx8HrIulWoz4/I0FqYUNjxza1aRQS9Cc7z7B1MR6TKsxbeiJK+RcCbuNy5+Toyy6jmRfOslplwKfR4z5l1qIJBa/eOwjrxQ4LXfdIoHYHwBmGEHmT8B1i450qY2VKfOewZeoHg= 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=gkup1hgJ; arc=none smtp.client-ip=209.85.214.179 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="gkup1hgJ" Received: by mail-pl1-f179.google.com with SMTP id d9443c01a7336-26c209802c0so27121845ad.0 for ; Mon, 22 Sep 2025 06:13:16 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1758546796; x=1759151596; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=AkZGqkC4SRW8zrF0kG17UYKvbLecZnrKv5gqbEWOOfE=; b=gkup1hgJrzpVh3Du8Rx89HKv/Zxa5/axD6ak/WinIFB5xCoB0LLUienDJGEYLWJBXt TwPfVKKKhfDA7OFqCNSZpbe4sgwGPi1iIoFdHellzoqsa1wqHL4vtUJ95fJoFaQ0N6hu 3Z+jV5ENgg26ndXsDU79KZQnPEfLPGBe+Nyi42Aioy00BA5gUjc0eRH2SB9/msfY9ppr AgFeBZ2CAnIbLla3fVUpMgUbb4eU/2ldw9rw7SOy63a0WhMbag6wXSi50JdzJg9xnf1d VvAjv9cXif/MNBjKlCA3OvmY7FkilXwFk0POJmwqQCmgB2NoyDum5T2yso3rhk8GrLC/ ZWmA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1758546796; x=1759151596; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=AkZGqkC4SRW8zrF0kG17UYKvbLecZnrKv5gqbEWOOfE=; b=bfx5zkrwfBq1+nGDaGnzo7O3FlFJUKkLuRmMvtZ1+gBzNCNnjtEI6blbRpbnP975WA xmqIK1BiTZurNlZzr8JZy0qehvK2TCLKrDkHbq/HmW8X3TSykl9e8E6aWY7JZcZg2QB4 lPtJ9Xen7eWitAQtwQ55tMt+ohR4ug8zGrJSajQczj22AeKd0KV0G2TwHYmLa0qL8gy7 qkb5c3pdGQvOt8g9mmhjty3XbnUbU3wfz9EHJAvZnltmQiLpaBhGhvHDIf0O6V03HkWi wFeSk5hDLFaObhzuaYuA6r6kwH5Q7+EKtuI0SsFSTdrV+weloRkLOVKgdrdh7QbDGOds jFyg== X-Forwarded-Encrypted: i=1; AJvYcCWco/ixnHBrQoy2Q8q4Y22/WhafUyN7jaaHTbaRi2LWBmJsHYBPBSr798LH4FV8VK74tfBY5aACbkTEIuY=@vger.kernel.org X-Gm-Message-State: AOJu0Yxvv0tWCeCXjDSf3XjktyNPfHm6V6ckLaubfsiiCgI4SneXcqJJ bCKO6+ogrVyG0ckBbK6WZaNKbBxqs4ZfLzzhoWemyzfBbqRW7ad1wYX4 X-Gm-Gg: ASbGnctUNzY57K/Wr7z+KBvSiIFmkC/X2NWDRlhnJqb/Hs4Y4zytSEFwzLROk753tQ+ KYmS7coB/sC+MpeFbEjqNoRCo6Wmexl1xfsFkr06cqQ6XvxIi5JltD4fh3dV+cEbfhMac934i4E niqXcandlXb5GmkZamn3Na6CHmwPgWoO8Z628rafe/8G/am41cDymPHs2uWZR+nmhRRDzSdPl2A Uu/uOSBBE6oMEd4PmhjX+PHl79tUOzJBPjtEIolodLuZMwgB/8zmxzCOe/Vrr03zZysWTw4nTpZ GywaHS16z2Oq0EKkyECjXbSL6yL5DRTtNdwp8GnLlAjKWk2kIDdl7Qbu6wWVfVRiTfn8k+lCSsV 3myX2EHqqTVMm3QdeMZdsSFaKQ8jUbmgiJnTjycVB X-Google-Smtp-Source: AGHT+IF75HzXl1dCJEf98eAOZUwSQvdHjlDfXx7X8h6Uz39nXh5fNhz/d89XY1pfSWIRPMITeNJO7A== X-Received: by 2002:a17:903:110c:b0:25d:d848:1cca with SMTP id d9443c01a7336-269ba5040e0mr154231705ad.35.1758546795223; Mon, 22 Sep 2025 06:13:15 -0700 (PDT) Received: from d.home.yangfl.dn42 ([45.32.227.231]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2698016bff2sm130200055ad.35.2025.09.22.06.13.10 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 22 Sep 2025 06:13:14 -0700 (PDT) From: David Yang To: netdev@vger.kernel.org Cc: David Yang , Andrew Lunn , Vladimir Oltean , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Heiner Kallweit , Russell King , Simon Horman , devicetree@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH net-next v11 5/5] net: dsa: yt921x: Add support for Motorcomm YT921x Date: Mon, 22 Sep 2025 21:11:43 +0800 Message-ID: <20250922131148.1917856-6-mmyangfl@gmail.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20250922131148.1917856-1-mmyangfl@gmail.com> References: <20250922131148.1917856-1-mmyangfl@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Motorcomm YT921x is a series of ethernet switches developed by Shanghai Motorcomm Electronic Technology, including: - YT9215S / YT9215RB / YT9215SC: 5 GbE PHYs - YT9213NB / YT9214NB: 2 GbE PHYs - YT9218N / YT9218MB: 8 GbE PHYs and up to 2 GMACs. Driver verified on a stock wireless router with IPQ5018 + YT9215S. Signed-off-by: David Yang --- drivers/net/dsa/Kconfig | 7 + drivers/net/dsa/Makefile | 1 + drivers/net/dsa/yt921x.c | 2900 ++++++++++++++++++++++++++++++++++++++ drivers/net/dsa/yt921x.h | 505 +++++++ 4 files changed, 3413 insertions(+) create mode 100644 drivers/net/dsa/yt921x.c create mode 100644 drivers/net/dsa/yt921x.h diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig index 4d9af691b989..7eb301fd987d 100644 --- a/drivers/net/dsa/Kconfig +++ b/drivers/net/dsa/Kconfig @@ -154,4 +154,11 @@ config NET_DSA_VITESSE_VSC73XX_PLATFORM This enables support for the Vitesse VSC7385, VSC7388, VSC7395 and VSC7398 SparX integrated ethernet switches, connected over a CPU-attached address bus and work in memory-mapped I/O mode. + +config NET_DSA_YT921X + tristate "Motorcomm YT9215 ethernet switch chip support" + select NET_DSA_TAG_YT921X + help + This enables support for the Motorcomm YT9215 ethernet switch + chip. endmenu diff --git a/drivers/net/dsa/Makefile b/drivers/net/dsa/Makefile index 0f8ff4a1a313..16de4ba3fa38 100644 --- a/drivers/net/dsa/Makefile +++ b/drivers/net/dsa/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_NET_DSA_SMSC_LAN9303_MDIO) +=3D lan9303_mdio= .o obj-$(CONFIG_NET_DSA_VITESSE_VSC73XX) +=3D vitesse-vsc73xx-core.o obj-$(CONFIG_NET_DSA_VITESSE_VSC73XX_PLATFORM) +=3D vitesse-vsc73xx-platfo= rm.o obj-$(CONFIG_NET_DSA_VITESSE_VSC73XX_SPI) +=3D vitesse-vsc73xx-spi.o +obj-$(CONFIG_NET_DSA_YT921X) +=3D yt921x.o obj-y +=3D b53/ obj-y +=3D hirschmann/ obj-y +=3D lantiq/ diff --git a/drivers/net/dsa/yt921x.c b/drivers/net/dsa/yt921x.c new file mode 100644 index 000000000000..1412e037823f --- /dev/null +++ b/drivers/net/dsa/yt921x.c @@ -0,0 +1,2900 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Driver for Motorcomm YT921x Switch + * + * Should work on YT9213/YT9214/YT9215/YT9218, but only tested on YT9215+S= GMII, + * be sure to do your own checks before porting to another chip. + * + * Copyright (c) 2025 David Yang + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "yt921x.h" + +struct yt921x_mib_desc { + unsigned int size; + unsigned int offset; + const char *name; + bool unstructured; +}; + +#define MIB_DESC(_size, _offset, _name, _unstructured) \ + {_size, _offset, _name, _unstructured} + +/* Must agree with yt921x_mib */ +static const struct yt921x_mib_desc yt921x_mib_descs[] =3D { + MIB_DESC(1, 0x00, "RxBroadcast", false), /* rx broadcast pkts */ + MIB_DESC(1, 0x04, "RxPause", false), /* rx pause pkts */ + MIB_DESC(1, 0x08, "RxMulticast", false), /* rx multicast pkts, excluding = pause and OAM */ + MIB_DESC(1, 0x0c, "RxCrcErr", false), /* rx crc err pkts, len >=3D 64B */ + + MIB_DESC(1, 0x10, "RxAlignErr", false), /* rx pkts with odd number of by= tes */ + MIB_DESC(1, 0x14, "RxUnderSizeErr", false), /* rx crc ok pkts, len < 64B = */ + MIB_DESC(1, 0x18, "RxFragErr", false), /* rx crc err pkts, len < 64B */ + MIB_DESC(1, 0x1c, "RxPktSz64", false), /* rx pkts, len =3D=3D 64B */ + + MIB_DESC(1, 0x20, "RxPktSz65To127", false), /* rx pkts, len >=3D 65B and = <=3D 127B */ + MIB_DESC(1, 0x24, "RxPktSz128To255", false), /* rx pkts, len >=3D 128B an= d <=3D 255B */ + MIB_DESC(1, 0x28, "RxPktSz256To511", false), /* rx pkts, len >=3D 256B an= d <=3D 511B */ + MIB_DESC(1, 0x2c, "RxPktSz512To1023", false), /* rx pkts, len >=3D 512B a= nd <=3D 1023B */ + + MIB_DESC(1, 0x30, "RxPktSz1024To1518", false), /* rx pkts, len >=3D 1024B= and <=3D 1518B */ + MIB_DESC(1, 0x34, "RxPktSz1519ToMax", false), /* rx pkts, len >=3D 1519B = */ + MIB_DESC(2, 0x38, "RxGoodBytes", false), /* total bytes of rx ok pkts */ + /* 0x3c */ + + MIB_DESC(2, 0x40, "RxBadBytes", true), /* total bytes of rx err pkts */ + /* 0x44 */ + MIB_DESC(2, 0x48, "RxOverSzErr", false), /* rx pkts, len > mac frame size= */ + /* 0x4c */ + + MIB_DESC(1, 0x50, "RxDropped", false), /* rx dropped pkts, excluding crc= err and pause */ + MIB_DESC(1, 0x54, "TxBroadcast", false), /* tx broadcast pkts */ + MIB_DESC(1, 0x58, "TxPause", false), /* tx pause pkts */ + MIB_DESC(1, 0x5c, "TxMulticast", false), /* tx multicast pkts, excluding = pause and OAM */ + + MIB_DESC(1, 0x60, "TxUnderSizeErr", false), /* tx pkts, len < 64B */ + MIB_DESC(1, 0x64, "TxPktSz64", false), /* tx pkts, len =3D=3D 64B */ + MIB_DESC(1, 0x68, "TxPktSz65To127", false), /* tx pkts, len >=3D 65B and = <=3D 127B */ + MIB_DESC(1, 0x6c, "TxPktSz128To255", false), /* tx pkts, len >=3D 128B an= d <=3D 255B */ + + MIB_DESC(1, 0x70, "TxPktSz256To511", false), /* tx pkts, len >=3D 256B an= d <=3D 511B */ + MIB_DESC(1, 0x74, "TxPktSz512To1023", false), /* tx pkts, len >=3D 512B a= nd <=3D 1023B */ + MIB_DESC(1, 0x78, "TxPktSz1024To1518", false), /* tx pkts, len >=3D 1024B= and <=3D 1518B */ + MIB_DESC(1, 0x7c, "TxPktSz1519ToMax", false), /* tx pkts, len >=3D 1519B = */ + + MIB_DESC(2, 0x80, "TxGoodBytes", false), /* total bytes of tx ok pkts */ + /* 0x84 */ + MIB_DESC(2, 0x88, "TxCollision", false), /* collisions before 64B */ + /* 0x8c */ + + MIB_DESC(1, 0x90, "TxExcessiveCollistion", false), /* aborted pkts due to= too many colls */ + MIB_DESC(1, 0x94, "TxMultipleCollision", false), /* multiple collision fo= r one pkt */ + MIB_DESC(1, 0x98, "TxSingleCollision", false), /* one collision for one p= kt */ + MIB_DESC(1, 0x9c, "TxPkt", false), /* tx ok pkts */ + + MIB_DESC(1, 0xa0, "TxDeferred", false), /* delayed pkts due to defer sig= nal */ + MIB_DESC(1, 0xa4, "TxLateCollision", false), /* collisions after 64B */ + MIB_DESC(1, 0xa8, "RxOAM", true), /* rx OAM pkts */ + MIB_DESC(1, 0xac, "TxOAM", true), /* tx OAM pkts */ +}; + +struct yt921x_info { + const char *name; + u16 major; + /* Unknown, seems to be plain enumeration */ + u8 mode; + u8 extmode; + /* Ports with integral GbE PHYs, not including MCU Port 10 */ + u16 internal_mask; + /* TODO: see comments in yt921x_dsa_phylink_get_caps() */ + u16 external_mask; +}; + +#define YT921X_PORT_MASK_INTn(port) BIT(port) +#define YT921X_PORT_MASK_INT0_n(n) GENMASK((n) - 1, 0) +#define YT921X_PORT_MASK_EXT0 BIT(8) +#define YT921X_PORT_MASK_EXT1 BIT(9) + +static const struct yt921x_info yt921x_infos[] =3D { + { + "YT9215SC", YT9215_MAJOR, 1, 0, + YT921X_PORT_MASK_INT0_n(5), + YT921X_PORT_MASK_EXT0 | YT921X_PORT_MASK_EXT1, + }, + { + "YT9215S", YT9215_MAJOR, 2, 0, + YT921X_PORT_MASK_INT0_n(5), + YT921X_PORT_MASK_EXT0 | YT921X_PORT_MASK_EXT1, + }, + { + "YT9215RB", YT9215_MAJOR, 3, 0, + YT921X_PORT_MASK_INT0_n(5), + YT921X_PORT_MASK_EXT0 | YT921X_PORT_MASK_EXT1, + }, + { + "YT9214NB", YT9215_MAJOR, 3, 2, + YT921X_PORT_MASK_INTn(1) | YT921X_PORT_MASK_INTn(3), + YT921X_PORT_MASK_EXT0 | YT921X_PORT_MASK_EXT1, + }, + { + "YT9213NB", YT9215_MAJOR, 3, 3, + YT921X_PORT_MASK_INTn(1) | YT921X_PORT_MASK_INTn(3), + YT921X_PORT_MASK_EXT1, + }, + { + "YT9218N", YT9218_MAJOR, 0, 0, + YT921X_PORT_MASK_INT0_n(8), + 0, + }, + { + "YT9218MB", YT9218_MAJOR, 1, 0, + YT921X_PORT_MASK_INT0_n(8), + YT921X_PORT_MASK_EXT0 | YT921X_PORT_MASK_EXT1, + }, + {} +}; + +#define YT921X_NAME "yt921x" + +#define YT921X_VID_UNWARE 4095 + +#define YT921X_POLL_SLEEP_US 10000 +#define YT921X_POLL_TIMEOUT_US 100000 + +/* The interval should be small enough to avoid overflow of 32bit MIBs. + * + * Until we can read MIBs from stats64 call directly (i.e. sleep + * there), we have to poll stats more frequently then it is actually neede= d. + * For overflow protection, normally, 100 sec interval should have been OK. + */ +#define YT921X_STATS_INTERVAL_JIFFIES (3 * HZ) + +struct yt921x_reg_mdio { + struct mii_bus *bus; + int addr; + /* SWITCH_ID_1 / SWITCH_ID_0 of the device + * + * This is a way to multiplex multiple devices on the same MII phyaddr + * and should be configurable in DT. However, MDIO core simply doesn't + * allow multiple devices over one reg addr, so this is a fixed value + * for now until a solution is found. + * + * Keep this because we need switchid to form MII regaddrs anyway. + */ + unsigned char switchid; +}; + +/* TODO: SPI/I2C */ + +#define to_yt921x_priv(_ds) container_of_const(_ds, struct yt921x_priv, ds) +#define to_device(priv) ((priv)->ds.dev) + +static int yt921x_reg_read(struct yt921x_priv *priv, u32 reg, u32 *valp) +{ + WARN_ON(!mutex_is_locked(&priv->reg_lock)); + + return priv->reg_ops->read(priv->reg_ctx, reg, valp); +} + +static int yt921x_reg_write(struct yt921x_priv *priv, u32 reg, u32 val) +{ + WARN_ON(!mutex_is_locked(&priv->reg_lock)); + + return priv->reg_ops->write(priv->reg_ctx, reg, val); +} + +static int +yt921x_reg_wait(struct yt921x_priv *priv, u32 reg, u32 mask, u32 *valp) +{ + u32 val; + int res; + int ret; + + ret =3D read_poll_timeout(yt921x_reg_read, res, + res || (val & mask) =3D=3D *valp, + YT921X_POLL_SLEEP_US, YT921X_POLL_TIMEOUT_US, + false, priv, reg, &val); + if (ret) + return ret; + if (res) + return res; + + *valp =3D val; + return 0; +} + +static int +yt921x_reg_update_bits(struct yt921x_priv *priv, u32 reg, u32 mask, u32 va= l) +{ + int res; + u32 v; + u32 u; + + res =3D yt921x_reg_read(priv, reg, &v); + if (res) + return res; + + u =3D v; + u &=3D ~mask; + u |=3D val; + if (u =3D=3D v) + return 0; + + return yt921x_reg_write(priv, reg, u); +} + +static int yt921x_reg_set_bits(struct yt921x_priv *priv, u32 reg, u32 mask) +{ + return yt921x_reg_update_bits(priv, reg, 0, mask); +} + +static int yt921x_reg_clear_bits(struct yt921x_priv *priv, u32 reg, u32 ma= sk) +{ + return yt921x_reg_update_bits(priv, reg, mask, 0); +} + +static int +yt921x_reg_toggle_bits(struct yt921x_priv *priv, u32 reg, u32 mask, bool s= et) +{ + return yt921x_reg_update_bits(priv, reg, mask, !set ? 0 : mask); +} + +/* Some registers, like VLANn_CTRL, should always be written in 64-bit, ev= en if + * you are to write only the lower / upper 32 bits. + * + * There is no such restriction for reading, but we still provide 64-bit r= ead + * wrappers so that we always handle u64 values. + */ + +static int yt921x_reg64_read(struct yt921x_priv *priv, u32 reg, u64 *valp) +{ + u32 lo; + u32 hi; + int res; + + res =3D yt921x_reg_read(priv, reg, &lo); + if (res) + return res; + res =3D yt921x_reg_read(priv, reg + 4, &hi); + if (res) + return res; + + *valp =3D ((u64)hi << 32) | lo; + return 0; +} + +static int yt921x_reg64_write(struct yt921x_priv *priv, u32 reg, u64 val) +{ + int res; + + res =3D yt921x_reg_write(priv, reg, (u32)val); + if (res) + return res; + return yt921x_reg_write(priv, reg + 4, (u32)(val >> 32)); +} + +static int +yt921x_reg64_update_bits(struct yt921x_priv *priv, u32 reg, u64 mask, u64 = val) +{ + int res; + u64 v; + u64 u; + + res =3D yt921x_reg64_read(priv, reg, &v); + if (res) + return res; + + u =3D v; + u &=3D ~mask; + u |=3D val; + if (u =3D=3D v) + return 0; + + return yt921x_reg64_write(priv, reg, u); +} + +static int yt921x_reg64_clear_bits(struct yt921x_priv *priv, u32 reg, u64 = mask) +{ + return yt921x_reg64_update_bits(priv, reg, mask, 0); +} + +static int yt921x_reg_mdio_read(void *context, u32 reg, u32 *valp) +{ + struct yt921x_reg_mdio *mdio =3D context; + struct mii_bus *bus =3D mdio->bus; + int addr =3D mdio->addr; + u32 reg_addr; + u32 reg_data; + u32 val; + int res; + + /* Hold the mdio bus lock to avoid (un)locking for 4 times */ + mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); + + reg_addr =3D YT921X_SMI_SWITCHID(mdio->switchid) | YT921X_SMI_ADDR | + YT921X_SMI_READ; + res =3D __mdiobus_write(bus, addr, reg_addr, (u16)(reg >> 16)); + if (res) + goto end; + res =3D __mdiobus_write(bus, addr, reg_addr, (u16)reg); + if (res) + goto end; + + reg_data =3D YT921X_SMI_SWITCHID(mdio->switchid) | YT921X_SMI_DATA | + YT921X_SMI_READ; + res =3D __mdiobus_read(bus, addr, reg_data); + if (res < 0) + goto end; + val =3D (u16)res; + res =3D __mdiobus_read(bus, addr, reg_data); + if (res < 0) + goto end; + val =3D (val << 16) | (u16)res; + + *valp =3D val; + res =3D 0; + +end: + mutex_unlock(&bus->mdio_lock); + return res; +} + +static int yt921x_reg_mdio_write(void *context, u32 reg, u32 val) +{ + struct yt921x_reg_mdio *mdio =3D context; + struct mii_bus *bus =3D mdio->bus; + int addr =3D mdio->addr; + u32 reg_addr; + u32 reg_data; + int res; + + mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); + + reg_addr =3D YT921X_SMI_SWITCHID(mdio->switchid) | YT921X_SMI_ADDR | + YT921X_SMI_WRITE; + res =3D __mdiobus_write(bus, addr, reg_addr, (u16)(reg >> 16)); + if (res) + goto end; + res =3D __mdiobus_write(bus, addr, reg_addr, (u16)reg); + if (res) + goto end; + + reg_data =3D YT921X_SMI_SWITCHID(mdio->switchid) | YT921X_SMI_DATA | + YT921X_SMI_WRITE; + res =3D __mdiobus_write(bus, addr, reg_data, (u16)(val >> 16)); + if (res) + goto end; + res =3D __mdiobus_write(bus, addr, reg_data, (u16)val); + if (res) + goto end; + + res =3D 0; + +end: + mutex_unlock(&bus->mdio_lock); + return res; +} + +static const struct yt921x_reg_ops yt921x_reg_ops_mdio =3D { + .read =3D yt921x_reg_mdio_read, + .write =3D yt921x_reg_mdio_write, +}; + +/* TODO: SPI/I2C */ + +static int yt921x_intif_wait(struct yt921x_priv *priv) +{ + u32 val =3D 0; + + return yt921x_reg_wait(priv, YT921X_INT_MBUS_OP, YT921X_MBUS_OP_START, + &val); +} + +static int +yt921x_intif_read(struct yt921x_priv *priv, int port, int reg, u16 *valp) +{ + struct device *dev =3D to_device(priv); + u32 mask; + u32 ctrl; + u32 val; + int res; + + res =3D yt921x_intif_wait(priv); + if (res) + return res; + + mask =3D YT921X_MBUS_CTRL_PORT_M | YT921X_MBUS_CTRL_REG_M | + YT921X_MBUS_CTRL_OP_M; + ctrl =3D YT921X_MBUS_CTRL_PORT(port) | YT921X_MBUS_CTRL_REG(reg) | + YT921X_MBUS_CTRL_READ; + res =3D yt921x_reg_update_bits(priv, YT921X_INT_MBUS_CTRL, mask, ctrl); + if (res) + return res; + res =3D yt921x_reg_write(priv, YT921X_INT_MBUS_OP, YT921X_MBUS_OP_START); + if (res) + return res; + + res =3D yt921x_intif_wait(priv); + if (res) + return res; + res =3D yt921x_reg_read(priv, YT921X_INT_MBUS_DIN, &val); + if (res) + return res; + + if ((u16)val !=3D val) + dev_info(dev, + "%s: port %d, reg 0x%x: Expected u16, got 0x%08x\n", + __func__, port, reg, val); + *valp =3D (u16)val; + return 0; +} + +static int +yt921x_intif_write(struct yt921x_priv *priv, int port, int reg, u16 val) +{ + u32 mask; + u32 ctrl; + int res; + + res =3D yt921x_intif_wait(priv); + if (res) + return res; + + mask =3D YT921X_MBUS_CTRL_PORT_M | YT921X_MBUS_CTRL_REG_M | + YT921X_MBUS_CTRL_OP_M; + ctrl =3D YT921X_MBUS_CTRL_PORT(port) | YT921X_MBUS_CTRL_REG(reg) | + YT921X_MBUS_CTRL_WRITE; + res =3D yt921x_reg_update_bits(priv, YT921X_INT_MBUS_CTRL, mask, ctrl); + if (res) + return res; + res =3D yt921x_reg_write(priv, YT921X_INT_MBUS_DOUT, val); + if (res) + return res; + res =3D yt921x_reg_write(priv, YT921X_INT_MBUS_OP, YT921X_MBUS_OP_START); + if (res) + return res; + + return yt921x_intif_wait(priv); +} + +static int yt921x_mbus_int_read(struct mii_bus *mbus, int port, int reg) +{ + struct yt921x_priv *priv =3D mbus->priv; + u16 val; + int res; + + if (port >=3D YT921X_PORT_NUM) + return U16_MAX; + + mutex_lock(&priv->reg_lock); + res =3D yt921x_intif_read(priv, port, reg, &val); + mutex_unlock(&priv->reg_lock); + + if (res) + return res; + return val; +} + +static int +yt921x_mbus_int_write(struct mii_bus *mbus, int port, int reg, u16 data) +{ + struct yt921x_priv *priv =3D mbus->priv; + int res; + + if (port >=3D YT921X_PORT_NUM) + return -ENODEV; + + mutex_lock(&priv->reg_lock); + res =3D yt921x_intif_write(priv, port, reg, data); + mutex_unlock(&priv->reg_lock); + + return res; +} + +static int +yt921x_mbus_int_init(struct yt921x_priv *priv, struct device_node *mnp) +{ + struct device *dev =3D to_device(priv); + struct mii_bus *mbus; + int res; + + mbus =3D devm_mdiobus_alloc(dev); + if (!mbus) + return -ENOMEM; + + mbus->name =3D "YT921x internal MDIO bus"; + snprintf(mbus->id, MII_BUS_ID_SIZE, "%s", dev_name(dev)); + mbus->priv =3D priv; + mbus->read =3D yt921x_mbus_int_read; + mbus->write =3D yt921x_mbus_int_write; + mbus->parent =3D dev; + mbus->phy_mask =3D (u32)~GENMASK(YT921X_PORT_NUM - 1, 0); + + res =3D devm_of_mdiobus_register(dev, mbus, mnp); + if (res) + return res; + + priv->mbus_int =3D mbus; + + return 0; +} + +static int yt921x_extif_wait(struct yt921x_priv *priv) +{ + u32 val =3D 0; + + return yt921x_reg_wait(priv, YT921X_EXT_MBUS_OP, YT921X_MBUS_OP_START, + &val); +} + +static int +yt921x_extif_read(struct yt921x_priv *priv, int port, int reg, u16 *valp) +{ + struct device *dev =3D to_device(priv); + u32 mask; + u32 ctrl; + u32 val; + int res; + + res =3D yt921x_extif_wait(priv); + if (res) + return res; + + mask =3D YT921X_MBUS_CTRL_PORT_M | YT921X_MBUS_CTRL_REG_M | + YT921X_MBUS_CTRL_TYPE_M | YT921X_MBUS_CTRL_OP_M; + ctrl =3D YT921X_MBUS_CTRL_PORT(port) | YT921X_MBUS_CTRL_REG(reg) | + YT921X_MBUS_CTRL_TYPE_C22 | YT921X_MBUS_CTRL_READ; + res =3D yt921x_reg_update_bits(priv, YT921X_EXT_MBUS_CTRL, mask, ctrl); + if (res) + return res; + res =3D yt921x_reg_write(priv, YT921X_EXT_MBUS_OP, YT921X_MBUS_OP_START); + if (res) + return res; + + res =3D yt921x_extif_wait(priv); + if (res) + return res; + res =3D yt921x_reg_read(priv, YT921X_EXT_MBUS_DIN, &val); + if (res) + return res; + + if ((u16)val !=3D val) + dev_info(dev, + "%s: port %d, reg 0x%x: Expected u16, got 0x%08x\n", + __func__, port, reg, val); + *valp =3D (u16)val; + return 0; +} + +static int +yt921x_extif_write(struct yt921x_priv *priv, int port, int reg, u16 val) +{ + u32 mask; + u32 ctrl; + int res; + + res =3D yt921x_extif_wait(priv); + if (res) + return res; + + mask =3D YT921X_MBUS_CTRL_PORT_M | YT921X_MBUS_CTRL_REG_M | + YT921X_MBUS_CTRL_TYPE_M | YT921X_MBUS_CTRL_OP_M; + ctrl =3D YT921X_MBUS_CTRL_PORT(port) | YT921X_MBUS_CTRL_REG(reg) | + YT921X_MBUS_CTRL_TYPE_C22 | YT921X_MBUS_CTRL_WRITE; + res =3D yt921x_reg_update_bits(priv, YT921X_EXT_MBUS_CTRL, mask, ctrl); + if (res) + return res; + res =3D yt921x_reg_write(priv, YT921X_EXT_MBUS_DOUT, val); + if (res) + return res; + res =3D yt921x_reg_write(priv, YT921X_EXT_MBUS_OP, YT921X_MBUS_OP_START); + if (res) + return res; + + return yt921x_extif_wait(priv); +} + +static int yt921x_mbus_ext_read(struct mii_bus *mbus, int port, int reg) +{ + struct yt921x_priv *priv =3D mbus->priv; + u16 val; + int res; + + mutex_lock(&priv->reg_lock); + res =3D yt921x_extif_read(priv, port, reg, &val); + mutex_unlock(&priv->reg_lock); + + if (res) + return res; + return val; +} + +static int +yt921x_mbus_ext_write(struct mii_bus *mbus, int port, int reg, u16 data) +{ + struct yt921x_priv *priv =3D mbus->priv; + int res; + + mutex_lock(&priv->reg_lock); + res =3D yt921x_extif_write(priv, port, reg, data); + mutex_unlock(&priv->reg_lock); + + return res; +} + +static int +yt921x_mbus_ext_init(struct yt921x_priv *priv, struct device_node *mnp) +{ + struct device *dev =3D to_device(priv); + struct mii_bus *mbus; + int res; + + mbus =3D devm_mdiobus_alloc(dev); + if (!mbus) + return -ENOMEM; + + mbus->name =3D "YT921x external MDIO bus"; + snprintf(mbus->id, MII_BUS_ID_SIZE, "%s@ext", dev_name(dev)); + mbus->priv =3D priv; + /* TODO: c45? */ + mbus->read =3D yt921x_mbus_ext_read; + mbus->write =3D yt921x_mbus_ext_write; + mbus->parent =3D dev; + + res =3D devm_of_mdiobus_register(dev, mbus, mnp); + if (res) + return res; + + priv->mbus_ext =3D mbus; + + return 0; +} + +/* Read and handle overflow of 32bit MIBs. MIB buffer must be zeroed befor= e. */ +static int yt921x_read_mib(struct yt921x_priv *priv, int port) +{ + struct yt921x_port *pp =3D &priv->ports[port]; + struct device *dev =3D to_device(priv); + struct yt921x_mib *mib =3D &pp->mib; + int res =3D 0; + + /* It's vain to keep consistency of yt921x_port::mib, since we have to + * read them one by one and there is no magic operation to make a + * snapshot of MIB stats. + * + * Our best hope is the consistency of each attribute, which can be done + * without a lock. + */ + for (size_t i =3D 0; i < ARRAY_SIZE(yt921x_mib_descs); i++) { + const struct yt921x_mib_desc *desc =3D &yt921x_mib_descs[i]; + u32 reg =3D YT921X_MIBn_DATA0(port) + desc->offset; + u64 *valp =3D &((u64 *)mib)[i]; + u64 val =3D *valp; + u32 val0; + u32 val1; + + res =3D yt921x_reg_read(priv, reg, &val0); + if (res) + break; + + if (desc->size <=3D 1) { + if (val < (u32)val) + /* overflow */ + val +=3D (u64)U32_MAX + 1; + val &=3D ~U32_MAX; + val |=3D val0; + } else { + res =3D yt921x_reg_read(priv, reg + 4, &val1); + if (res) + break; + val =3D ((u64)val0 << 32) | val1; + } + + WRITE_ONCE(*valp, val); + } + + if (res) { + smp_wmb(); + dev_err(dev, "Failed to %s port %d: %i\n", "read stats for", + port, res); + return res; + } + + pp->rx_frames =3D mib->rx_64byte + mib->rx_65_127byte + + mib->rx_128_255byte + mib->rx_256_511byte + + mib->rx_512_1023byte + mib->rx_1024_1518byte + + mib->rx_jumbo; + pp->tx_frames =3D mib->tx_64byte + mib->tx_65_127byte + + mib->tx_128_255byte + mib->tx_256_511byte + + mib->tx_512_1023byte + mib->tx_1024_1518byte + + mib->tx_jumbo; + + /* Flush all writes */ + smp_wmb(); + return 0; +} + +static void yt921x_poll_mib(struct work_struct *work) +{ + struct yt921x_port *pp =3D container_of_const(work, struct yt921x_port, + mib_read.work); + struct yt921x_priv *priv =3D (void *)(pp - pp->index) - + offsetof(struct yt921x_priv, ports); + unsigned long delay =3D YT921X_STATS_INTERVAL_JIFFIES; + int port =3D pp->index; + int res; + + mutex_lock(&priv->reg_lock); + res =3D yt921x_read_mib(priv, port); + mutex_unlock(&priv->reg_lock); + if (res) + delay *=3D 4; + + schedule_delayed_work(&pp->mib_read, delay); +} + +static void +yt921x_dsa_get_strings(struct dsa_switch *ds, int port, u32 stringset, + uint8_t *data) +{ + if (stringset !=3D ETH_SS_STATS) + return; + + for (size_t i =3D 0; i < ARRAY_SIZE(yt921x_mib_descs); i++) { + const struct yt921x_mib_desc *desc =3D &yt921x_mib_descs[i]; + + if (desc->unstructured) + ethtool_puts(&data, desc->name); + } +} + +static void +yt921x_dsa_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *da= ta) +{ + struct yt921x_priv *priv =3D to_yt921x_priv(ds); + struct yt921x_port *pp =3D &priv->ports[port]; + struct yt921x_mib *mib =3D &pp->mib; + size_t j; + + mutex_lock(&priv->reg_lock); + yt921x_read_mib(priv, port); + mutex_unlock(&priv->reg_lock); + + j =3D 0; + for (size_t i =3D 0; i < ARRAY_SIZE(yt921x_mib_descs); i++) { + const struct yt921x_mib_desc *desc =3D &yt921x_mib_descs[i]; + + if (!desc->unstructured) + continue; + + data[j] =3D ((u64 *)mib)[i]; + j++; + } +} + +static int yt921x_dsa_get_sset_count(struct dsa_switch *ds, int port, int = sset) +{ + int cnt =3D 0; + + if (sset !=3D ETH_SS_STATS) + return 0; + + for (size_t i =3D 0; i < ARRAY_SIZE(yt921x_mib_descs); i++) { + const struct yt921x_mib_desc *desc =3D &yt921x_mib_descs[i]; + + if (desc->unstructured) + cnt++; + } + + return cnt; +} + +static void +yt921x_dsa_get_eth_mac_stats(struct dsa_switch *ds, int port, + struct ethtool_eth_mac_stats *mac_stats) +{ + struct yt921x_priv *priv =3D to_yt921x_priv(ds); + struct yt921x_port *pp =3D &priv->ports[port]; + struct yt921x_mib *mib =3D &pp->mib; + + mutex_lock(&priv->reg_lock); + yt921x_read_mib(priv, port); + mutex_unlock(&priv->reg_lock); + + mac_stats->FramesTransmittedOK =3D pp->tx_frames; + mac_stats->SingleCollisionFrames =3D mib->tx_single_collisions; + mac_stats->MultipleCollisionFrames =3D mib->tx_multiple_collisions; + mac_stats->FramesReceivedOK =3D pp->rx_frames; + mac_stats->FrameCheckSequenceErrors =3D mib->rx_crc_errors; + mac_stats->AlignmentErrors =3D mib->rx_alignment_errors; + mac_stats->OctetsTransmittedOK =3D mib->tx_good_bytes; + mac_stats->FramesWithDeferredXmissions =3D mib->tx_deferred; + mac_stats->LateCollisions =3D mib->tx_late_collisions; + mac_stats->FramesAbortedDueToXSColls =3D mib->tx_aborted_errors; + /* mac_stats->FramesLostDueToIntMACXmitError */ + /* mac_stats->CarrierSenseErrors */ + mac_stats->OctetsReceivedOK =3D mib->rx_good_bytes; + /* mac_stats->FramesLostDueToIntMACRcvError */ + mac_stats->MulticastFramesXmittedOK =3D mib->tx_multicast; + mac_stats->BroadcastFramesXmittedOK =3D mib->tx_broadcast; + /* mac_stats->FramesWithExcessiveDeferral */ + mac_stats->MulticastFramesReceivedOK =3D mib->rx_multicast; + mac_stats->BroadcastFramesReceivedOK =3D mib->rx_broadcast; + /* mac_stats->InRangeLengthErrors */ + /* mac_stats->OutOfRangeLengthField */ + mac_stats->FrameTooLongErrors =3D mib->rx_oversize_errors; +} + +static void +yt921x_dsa_get_eth_ctrl_stats(struct dsa_switch *ds, int port, + struct ethtool_eth_ctrl_stats *ctrl_stats) +{ + struct yt921x_priv *priv =3D to_yt921x_priv(ds); + struct yt921x_port *pp =3D &priv->ports[port]; + struct yt921x_mib *mib =3D &pp->mib; + + mutex_lock(&priv->reg_lock); + yt921x_read_mib(priv, port); + mutex_unlock(&priv->reg_lock); + + ctrl_stats->MACControlFramesTransmitted =3D mib->tx_pause; + ctrl_stats->MACControlFramesReceived =3D mib->rx_pause; + /* ctrl_stats->UnsupportedOpcodesReceived */ +} + +static const struct ethtool_rmon_hist_range yt921x_rmon_ranges[] =3D { + { 0, 64 }, + { 65, 127 }, + { 128, 255 }, + { 256, 511 }, + { 512, 1023 }, + { 1024, 1518 }, + { 1519, YT921X_FRAME_SIZE_MAX }, + {} +}; + +static void +yt921x_dsa_get_rmon_stats(struct dsa_switch *ds, int port, + struct ethtool_rmon_stats *rmon_stats, + const struct ethtool_rmon_hist_range **ranges) +{ + struct yt921x_priv *priv =3D to_yt921x_priv(ds); + struct yt921x_port *pp =3D &priv->ports[port]; + struct yt921x_mib *mib =3D &pp->mib; + + mutex_lock(&priv->reg_lock); + yt921x_read_mib(priv, port); + mutex_unlock(&priv->reg_lock); + + *ranges =3D yt921x_rmon_ranges; + + rmon_stats->undersize_pkts =3D mib->rx_undersize_errors; + rmon_stats->oversize_pkts =3D mib->rx_oversize_errors; + rmon_stats->fragments =3D mib->rx_alignment_errors; + /* rmon_stats->jabbers */ + + rmon_stats->hist[0] =3D mib->rx_64byte; + rmon_stats->hist[1] =3D mib->rx_65_127byte; + rmon_stats->hist[2] =3D mib->rx_128_255byte; + rmon_stats->hist[3] =3D mib->rx_256_511byte; + rmon_stats->hist[4] =3D mib->rx_512_1023byte; + rmon_stats->hist[5] =3D mib->rx_1024_1518byte; + rmon_stats->hist[6] =3D mib->rx_jumbo; + + rmon_stats->hist_tx[0] =3D mib->tx_64byte; + rmon_stats->hist_tx[1] =3D mib->tx_65_127byte; + rmon_stats->hist_tx[2] =3D mib->tx_128_255byte; + rmon_stats->hist_tx[3] =3D mib->tx_256_511byte; + rmon_stats->hist_tx[4] =3D mib->tx_512_1023byte; + rmon_stats->hist_tx[5] =3D mib->tx_1024_1518byte; + rmon_stats->hist_tx[6] =3D mib->tx_jumbo; +} + +static void +yt921x_dsa_get_stats64(struct dsa_switch *ds, int port, + struct rtnl_link_stats64 *stats) +{ + struct yt921x_priv *priv =3D to_yt921x_priv(ds); + struct yt921x_port *pp =3D &priv->ports[port]; + struct yt921x_mib *mib =3D &pp->mib; + + stats->rx_length_errors =3D mib->rx_undersize_errors + + mib->rx_fragment_errors; + stats->rx_over_errors =3D mib->rx_oversize_errors; + stats->rx_crc_errors =3D mib->rx_crc_errors; + stats->rx_frame_errors =3D mib->rx_alignment_errors; + /* stats->rx_fifo_errors */ + /* stats->rx_missed_errors */ + + stats->tx_aborted_errors =3D mib->tx_aborted_errors; + /* stats->tx_carrier_errors */ + stats->tx_fifo_errors =3D mib->tx_undersize_errors; + /* stats->tx_heartbeat_errors */ + stats->tx_window_errors =3D mib->tx_late_collisions; + + stats->rx_packets =3D pp->rx_frames; + stats->tx_packets =3D pp->tx_frames; + stats->rx_bytes =3D mib->rx_good_bytes - ETH_FCS_LEN * stats->rx_packets; + stats->tx_bytes =3D mib->tx_good_bytes - ETH_FCS_LEN * stats->tx_packets; + stats->rx_errors =3D stats->rx_length_errors + stats->rx_over_errors + + stats->rx_crc_errors + stats->rx_frame_errors; + stats->tx_errors =3D stats->tx_aborted_errors + stats->tx_fifo_errors + + stats->tx_window_errors; + stats->rx_dropped =3D mib->rx_dropped; + /* stats->tx_dropped */ + stats->multicast =3D mib->rx_multicast; + stats->collisions =3D mib->tx_collisions; +} + +static void +yt921x_dsa_get_pause_stats(struct dsa_switch *ds, int port, + struct ethtool_pause_stats *pause_stats) +{ + struct yt921x_priv *priv =3D to_yt921x_priv(ds); + struct yt921x_port *pp =3D &priv->ports[port]; + struct yt921x_mib *mib =3D &pp->mib; + + mutex_lock(&priv->reg_lock); + yt921x_read_mib(priv, port); + mutex_unlock(&priv->reg_lock); + + pause_stats->tx_pause_frames =3D mib->tx_pause; + pause_stats->rx_pause_frames =3D mib->rx_pause; +} + +static int +yt921x_set_eee(struct yt921x_priv *priv, int port, struct ethtool_keee *e) +{ + /* Poor datasheet for EEE operations; don't ask if you are confused */ + + bool enable =3D e->eee_enabled; + u16 new_mask; + int res; + + /* Enable / disable global EEE */ + new_mask =3D priv->eee_ports_mask; + new_mask &=3D ~BIT(port); + new_mask |=3D !enable ? 0 : BIT(port); + + if (!!new_mask !=3D !!priv->eee_ports_mask) { + res =3D yt921x_reg_toggle_bits(priv, YT921X_PON_STRAP_FUNC, + YT921X_PON_STRAP_EEE, !!new_mask); + if (res) + return res; + res =3D yt921x_reg_toggle_bits(priv, YT921X_PON_STRAP_VAL, + YT921X_PON_STRAP_EEE, !!new_mask); + if (res) + return res; + } + + priv->eee_ports_mask =3D new_mask; + + /* Enable / disable port EEE */ + res =3D yt921x_reg_toggle_bits(priv, YT921X_EEE_CTRL, + YT921X_EEE_CTRL_ENn(port), enable); + if (res) + return res; + res =3D yt921x_reg_toggle_bits(priv, YT921X_EEEn_VAL(port), + YT921X_EEE_VAL_DATA, enable); + if (res) + return res; + + return 0; +} + +static int +yt921x_dsa_set_mac_eee(struct dsa_switch *ds, int port, struct ethtool_kee= e *e) +{ + struct yt921x_priv *priv =3D to_yt921x_priv(ds); + int res; + + mutex_lock(&priv->reg_lock); + res =3D yt921x_set_eee(priv, port, e); + mutex_unlock(&priv->reg_lock); + + return res; +} + +static int +yt921x_dsa_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu) +{ + /* Only serves as packet filter, since the frame size is always set to + * maximum after reset + */ + + struct yt921x_priv *priv =3D to_yt921x_priv(ds); + struct dsa_port *dp =3D dsa_to_port(ds, port); + int frame_size; + int res; + + frame_size =3D new_mtu + ETH_HLEN + ETH_FCS_LEN; + if (dsa_port_is_cpu(dp)) + frame_size +=3D YT921X_TAG_LEN; + + mutex_lock(&priv->reg_lock); + res =3D yt921x_reg_update_bits(priv, YT921X_MACn_FRAME(port), + YT921X_MAC_FRAME_SIZE_M, + YT921X_MAC_FRAME_SIZE(frame_size)); + mutex_unlock(&priv->reg_lock); + + return res; +} + +static int yt921x_dsa_port_max_mtu(struct dsa_switch *ds, int port) +{ + /* Only called for user ports, exclude tag len here */ + return YT921X_FRAME_SIZE_MAX - ETH_HLEN - ETH_FCS_LEN - YT921X_TAG_LEN; +} + +static int +yt921x_mirror_del(struct yt921x_priv *priv, int port, bool ingress) +{ + u32 mask; + + if (ingress) + mask =3D YT921X_MIRROR_IGR_PORTn(port); + else + mask =3D YT921X_MIRROR_EGR_PORTn(port); + return yt921x_reg_clear_bits(priv, YT921X_MIRROR, mask); +} + +static int +yt921x_mirror_add(struct yt921x_priv *priv, int port, bool ingress, + int to_local_port, struct netlink_ext_ack *extack) +{ + u32 srcs; + u32 ctrl; + u32 val; + u32 dst; + int res; + + if (ingress) + srcs =3D YT921X_MIRROR_IGR_PORTn(port); + else + srcs =3D YT921X_MIRROR_EGR_PORTn(port); + dst =3D YT921X_MIRROR_PORT(to_local_port); + + res =3D yt921x_reg_read(priv, YT921X_MIRROR, &val); + if (res) + return res; + + /* other mirror tasks & different dst port -> conflict */ + if ((val & ~srcs & (YT921X_MIRROR_EGR_PORTS_M | + YT921X_MIRROR_IGR_PORTS_M)) !=3D 0 && + (val & YT921X_MIRROR_PORT_M) !=3D dst) { + NL_SET_ERR_MSG_MOD(extack, + "Sniffer port is already configured, " + "delete existing rules & retry"); + return -EBUSY; + } + + ctrl =3D val & ~YT921X_MIRROR_PORT_M; + ctrl |=3D srcs; + ctrl |=3D dst; + + if (ctrl =3D=3D val) + return 0; + + return yt921x_reg_write(priv, YT921X_MIRROR, ctrl); +} + +static void +yt921x_dsa_port_mirror_del(struct dsa_switch *ds, int port, + struct dsa_mall_mirror_tc_entry *mirror) +{ + struct yt921x_priv *priv =3D to_yt921x_priv(ds); + struct device *dev =3D to_device(priv); + int res; + + mutex_lock(&priv->reg_lock); + res =3D yt921x_mirror_del(priv, port, mirror->ingress); + mutex_unlock(&priv->reg_lock); + + if (res) + dev_err(dev, "Failed to %s port %d: %i\n", "unmirror", + port, res); +} + +static int +yt921x_dsa_port_mirror_add(struct dsa_switch *ds, int port, + struct dsa_mall_mirror_tc_entry *mirror, + bool ingress, struct netlink_ext_ack *extack) +{ + struct yt921x_priv *priv =3D to_yt921x_priv(ds); + int res; + + mutex_lock(&priv->reg_lock); + res =3D yt921x_mirror_add(priv, port, ingress, + mirror->to_local_port, extack); + mutex_unlock(&priv->reg_lock); + + return res; +} + +static int yt921x_fdb_wait(struct yt921x_priv *priv, u32 *valp) +{ + struct device *dev =3D to_device(priv); + u32 val =3D YT921X_FDB_RESULT_DONE; + int res; + + res =3D yt921x_reg_wait(priv, YT921X_FDB_RESULT, YT921X_FDB_RESULT_DONE, + &val); + if (res) { + dev_err(dev, "FDB probably stucked\n"); + return res; + } + + *valp =3D val; + return 0; +} + +static int +yt921x_fdb_in01(struct yt921x_priv *priv, const unsigned char *addr, + u16 vid, u32 ctrl1) +{ + u32 ctrl; + int res; + + ctrl =3D (addr[0] << 24) | (addr[1] << 16) | (addr[2] << 8) | addr[3]; + res =3D yt921x_reg_write(priv, YT921X_FDB_IN0, ctrl); + if (res) + return res; + + ctrl =3D ctrl1 | YT921X_FDB_IO1_FID(vid) | (addr[4] << 8) | addr[5]; + return yt921x_reg_write(priv, YT921X_FDB_IN1, ctrl); +} + +static int +yt921x_fdb_has(struct yt921x_priv *priv, const unsigned char *addr, u16 vi= d, + u16 *indexp) +{ + u32 ctrl; + u32 val; + int res; + + res =3D yt921x_fdb_in01(priv, addr, vid, 0); + if (res) + return res; + + ctrl =3D 0; + res =3D yt921x_reg_write(priv, YT921X_FDB_IN2, ctrl); + if (res) + return res; + + ctrl =3D YT921X_FDB_OP_OP_GET_ONE | YT921X_FDB_OP_START; + res =3D yt921x_reg_write(priv, YT921X_FDB_OP, ctrl); + if (res) + return res; + + res =3D yt921x_fdb_wait(priv, &val); + if (res) + return res; + if ((val & YT921X_FDB_RESULT_NOTFOUND) !=3D 0) { + *indexp =3D YT921X_FDB_NUM; + return 0; + } + + *indexp =3D FIELD_GET(YT921X_FDB_RESULT_INDEX_M, val); + return 0; +} + +static int +yt921x_fdb_read(struct yt921x_priv *priv, unsigned char *addr, u16 *vidp, + u16 *ports_maskp, u16 *indexp, u8 *statusp) +{ + struct device *dev =3D to_device(priv); + u16 index; + u32 data0; + u32 data1; + u32 data2; + u32 val; + int res; + + res =3D yt921x_fdb_wait(priv, &val); + if (res) + return res; + if ((val & YT921X_FDB_RESULT_NOTFOUND) !=3D 0) { + *ports_maskp =3D 0; + return 0; + } + index =3D FIELD_GET(YT921X_FDB_RESULT_INDEX_M, val); + + res =3D yt921x_reg_read(priv, YT921X_FDB_OUT1, &data1); + if (res) + return res; + if ((data1 & YT921X_FDB_IO1_STATUS_M) =3D=3D + YT921X_FDB_IO1_STATUS_INVALID) { + *ports_maskp =3D 0; + return 0; + } + + res =3D yt921x_reg_read(priv, YT921X_FDB_OUT0, &data0); + if (res) + return res; + res =3D yt921x_reg_read(priv, YT921X_FDB_OUT2, &data2); + if (res) + return res; + + addr[0] =3D data0 >> 24; + addr[1] =3D data0 >> 16; + addr[2] =3D data0 >> 8; + addr[3] =3D data0; + addr[4] =3D data1 >> 8; + addr[5] =3D data1; + *vidp =3D FIELD_GET(YT921X_FDB_IO1_FID_M, data1); + *indexp =3D index; + *ports_maskp =3D FIELD_GET(YT921X_FDB_IO2_EGR_PORTS_M, data2); + *statusp =3D FIELD_GET(YT921X_FDB_IO1_STATUS_M, data1); + + dev_dbg(dev, + "%s: index 0x%x, mac %02x:%02x:%02x:%02x:%02x:%02x, " + "vid %d, ports 0x%x, status %d\n", + __func__, *indexp, addr[0], addr[1], addr[2], addr[3], + addr[4], addr[5], *vidp, *ports_maskp, *statusp); + return 0; +} + +static int +yt921x_fdb_dump(struct yt921x_priv *priv, u16 ports_mask, + dsa_fdb_dump_cb_t *cb, void *data) +{ + unsigned char addr[ETH_ALEN]; + u8 status; + u16 pmask; + u16 index; + u32 ctrl; + u16 vid; + int res; + + ctrl =3D YT921X_FDB_OP_INDEX(0) | YT921X_FDB_OP_MODE_INDEX | + YT921X_FDB_OP_OP_GET_ONE | YT921X_FDB_OP_START; + res =3D yt921x_reg_write(priv, YT921X_FDB_OP, ctrl); + if (res) + return res; + res =3D yt921x_fdb_read(priv, addr, &vid, &pmask, &index, &status); + if (res) + return res; + if ((pmask & ports_mask) !=3D 0 && !is_multicast_ether_addr(addr)) { + res =3D cb(addr, vid, + status =3D=3D YT921X_FDB_ENTRY_STATUS_STATIC, data); + if (res) + return res; + } + + ctrl =3D YT921X_FDB_IO2_EGR_PORTS(ports_mask); + res =3D yt921x_reg_write(priv, YT921X_FDB_IN2, ctrl); + if (res) + return res; + + index =3D 0; + do { + ctrl =3D YT921X_FDB_OP_INDEX(index) | YT921X_FDB_OP_MODE_INDEX | + YT921X_FDB_OP_NEXT_TYPE_UCAST_PORT | + YT921X_FDB_OP_OP_GET_NEXT | YT921X_FDB_OP_START; + res =3D yt921x_reg_write(priv, YT921X_FDB_OP, ctrl); + if (res) + return res; + + res =3D yt921x_fdb_read(priv, addr, &vid, &pmask, &index, + &status); + if (res) + return res; + if (!pmask) + break; + + if ((pmask & ports_mask) !=3D 0 && + !is_multicast_ether_addr(addr)) { + res =3D cb(addr, vid, + status =3D=3D YT921X_FDB_ENTRY_STATUS_STATIC, + data); + if (res) + return res; + } + + /* Never call GET_NEXT with 4095, otherwise it will hang + * forever until a reset! + */ + } while (index < YT921X_FDB_NUM - 1); + + return 0; +} + +static int +yt921x_fdb_flush_raw(struct yt921x_priv *priv, u16 ports_mask, u16 vid, + bool flush_static) +{ + u32 ctrl; + u32 val; + int res; + + if (vid < 4096) { + ctrl =3D YT921X_FDB_IO1_FID(vid); + res =3D yt921x_reg_write(priv, YT921X_FDB_IN1, ctrl); + if (res) + return res; + } + + ctrl =3D YT921X_FDB_IO2_EGR_PORTS(ports_mask); + res =3D yt921x_reg_write(priv, YT921X_FDB_IN2, ctrl); + if (res) + return res; + + ctrl =3D YT921X_FDB_OP_OP_FLUSH | YT921X_FDB_OP_START; + if (vid >=3D 4096) + ctrl |=3D YT921X_FDB_OP_FLUSH_PORT; + else + ctrl |=3D YT921X_FDB_OP_FLUSH_PORT_VID; + if (flush_static) + ctrl |=3D YT921X_FDB_OP_FLUSH_STATIC; + res =3D yt921x_reg_write(priv, YT921X_FDB_OP, ctrl); + if (res) + return res; + + res =3D yt921x_fdb_wait(priv, &val); + if (res) + return res; + + return 0; +} + +static int +yt921x_fdb_flush_port(struct yt921x_priv *priv, int port, bool flush_stati= c) +{ + return yt921x_fdb_flush_raw(priv, BIT(port), 4096, flush_static); +} + +static int +yt921x_fdb_add_index_in12(struct yt921x_priv *priv, u16 index, u16 ctrl1, + u16 ctrl2) +{ + u32 ctrl; + u32 val; + int res; + + res =3D yt921x_reg_write(priv, YT921X_FDB_IN1, ctrl1); + if (res) + return res; + res =3D yt921x_reg_write(priv, YT921X_FDB_IN2, ctrl2); + if (res) + return res; + + ctrl =3D YT921X_FDB_OP_INDEX(index) | YT921X_FDB_OP_MODE_INDEX | + YT921X_FDB_OP_OP_ADD | YT921X_FDB_OP_START; + res =3D yt921x_reg_write(priv, YT921X_FDB_OP, ctrl); + if (res) + return res; + + return yt921x_fdb_wait(priv, &val); +} + +static int +yt921x_fdb_add(struct yt921x_priv *priv, const unsigned char *addr, u16 vi= d, + u16 ports_mask) +{ + u32 ctrl; + u32 val; + int res; + + ctrl =3D YT921X_FDB_IO1_STATUS_STATIC; + res =3D yt921x_fdb_in01(priv, addr, vid, ctrl); + if (res) + return res; + + ctrl =3D YT921X_FDB_IO2_EGR_PORTS(ports_mask); + res =3D yt921x_reg_write(priv, YT921X_FDB_IN2, ctrl); + if (res) + return res; + + ctrl =3D YT921X_FDB_OP_OP_ADD | YT921X_FDB_OP_START; + res =3D yt921x_reg_write(priv, YT921X_FDB_OP, ctrl); + if (res) + return res; + + return yt921x_fdb_wait(priv, &val); +} + +static int +yt921x_fdb_leave(struct yt921x_priv *priv, const unsigned char *addr, + u16 vid, u16 ports_mask) +{ + u16 index; + u32 ctrl1; + u32 ctrl2; + u32 ctrl; + u32 val2; + u32 val; + int res; + + /* Check for presence */ + res =3D yt921x_fdb_has(priv, addr, vid, &index); + if (res) + return res; + if (index >=3D YT921X_FDB_NUM) + return 0; + + /* Check if action required */ + res =3D yt921x_reg_read(priv, YT921X_FDB_OUT2, &val2); + if (res) + return res; + + ctrl2 =3D val2 & ~YT921X_FDB_IO2_EGR_PORTS(ports_mask); + if (ctrl2 =3D=3D val2) + return 0; + if ((ctrl2 & YT921X_FDB_IO2_EGR_PORTS_M) =3D=3D 0) { + ctrl =3D YT921X_FDB_OP_OP_DEL | YT921X_FDB_OP_START; + res =3D yt921x_reg_write(priv, YT921X_FDB_OP, ctrl); + if (res) + return res; + + return yt921x_fdb_wait(priv, &val); + } + + res =3D yt921x_reg_read(priv, YT921X_FDB_OUT1, &ctrl1); + if (res) + return res; + + return yt921x_fdb_add_index_in12(priv, index, ctrl1, ctrl2); +} + +static int +yt921x_fdb_join(struct yt921x_priv *priv, const unsigned char *addr, u16 v= id, + u16 ports_mask) +{ + u16 index; + u32 ctrl1; + u32 ctrl2; + u32 val1; + u32 val2; + int res; + + /* Check for presence */ + res =3D yt921x_fdb_has(priv, addr, vid, &index); + if (res) + return res; + if (index >=3D YT921X_FDB_NUM) + return yt921x_fdb_add(priv, addr, vid, ports_mask); + + /* Check if action required */ + res =3D yt921x_reg_read(priv, YT921X_FDB_OUT1, &val1); + if (res) + return res; + res =3D yt921x_reg_read(priv, YT921X_FDB_OUT2, &val2); + if (res) + return res; + + ctrl1 =3D val1 & ~YT921X_FDB_IO1_STATUS_M; + ctrl1 |=3D YT921X_FDB_IO1_STATUS_STATIC; + ctrl2 =3D val2 | YT921X_FDB_IO2_EGR_PORTS(ports_mask); + if (ctrl1 =3D=3D val1 && ctrl2 =3D=3D val2) + return 0; + + return yt921x_fdb_add_index_in12(priv, index, ctrl1, ctrl2); +} + +static int +yt921x_dsa_port_fdb_dump(struct dsa_switch *ds, int port, + dsa_fdb_dump_cb_t *cb, void *data) +{ + struct yt921x_priv *priv =3D to_yt921x_priv(ds); + int res; + + mutex_lock(&priv->reg_lock); + /* Hardware FDB is shared for fdb and mdb, "bridge fdb show" + * only wants to see unicast + */ + res =3D yt921x_fdb_dump(priv, BIT(port), cb, data); + mutex_unlock(&priv->reg_lock); + + return res; +} + +static void yt921x_dsa_port_fast_age(struct dsa_switch *ds, int port) +{ + struct yt921x_priv *priv =3D to_yt921x_priv(ds); + struct device *dev =3D to_device(priv); + int res; + + mutex_lock(&priv->reg_lock); + res =3D yt921x_fdb_flush_port(priv, port, false); + mutex_unlock(&priv->reg_lock); + + if (res) + dev_err(dev, "Failed to %s port %d: %i\n", "clear FDB for", + port, res); +} + +static int +yt921x_dsa_set_ageing_time(struct dsa_switch *ds, unsigned int msecs) +{ + struct yt921x_priv *priv =3D to_yt921x_priv(ds); + u32 ctrl; + int res; + + /* AGEING reg is set in 5s step */ + ctrl =3D clamp(msecs / 5000, 1, U16_MAX); + + mutex_lock(&priv->reg_lock); + res =3D yt921x_reg_write(priv, YT921X_AGEING, ctrl); + mutex_unlock(&priv->reg_lock); + + return res; +} + +static int +yt921x_dsa_port_fdb_del(struct dsa_switch *ds, int port, + const unsigned char *addr, u16 vid, struct dsa_db db) +{ + struct yt921x_priv *priv =3D to_yt921x_priv(ds); + int res; + + mutex_lock(&priv->reg_lock); + res =3D yt921x_fdb_leave(priv, addr, vid, BIT(port)); + mutex_unlock(&priv->reg_lock); + + return res; +} + +static int +yt921x_dsa_port_fdb_add(struct dsa_switch *ds, int port, + const unsigned char *addr, u16 vid, struct dsa_db db) +{ + struct yt921x_priv *priv =3D to_yt921x_priv(ds); + int res; + + mutex_lock(&priv->reg_lock); + res =3D yt921x_fdb_join(priv, addr, vid, BIT(port)); + mutex_unlock(&priv->reg_lock); + + return res; +} + +static int +yt921x_dsa_port_mdb_del(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_mdb *mdb, + struct dsa_db db) +{ + struct yt921x_priv *priv =3D to_yt921x_priv(ds); + const unsigned char *addr =3D mdb->addr; + u16 vid =3D mdb->vid; + int res; + + mutex_lock(&priv->reg_lock); + res =3D yt921x_fdb_leave(priv, addr, vid, BIT(port)); + mutex_unlock(&priv->reg_lock); + + return res; +} + +static int +yt921x_dsa_port_mdb_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_mdb *mdb, + struct dsa_db db) +{ + struct yt921x_priv *priv =3D to_yt921x_priv(ds); + const unsigned char *addr =3D mdb->addr; + u16 vid =3D mdb->vid; + int res; + + mutex_lock(&priv->reg_lock); + res =3D yt921x_fdb_join(priv, addr, vid, BIT(port)); + mutex_unlock(&priv->reg_lock); + + return res; +} + +static int +yt921x_port_set_pvid(struct yt921x_priv *priv, int port, u16 vid) +{ + u32 mask; + u32 ctrl; + + mask =3D YT921X_PORT_VLAN_CTRL_CVID_M; + ctrl =3D YT921X_PORT_VLAN_CTRL_CVID(vid); + return yt921x_reg_update_bits(priv, YT921X_PORTn_VLAN_CTRL(port), + mask, ctrl); +} + +static int +yt921x_vlan_filtering(struct yt921x_priv *priv, int port, bool vlan_filter= ing) +{ + struct dsa_port *dp =3D dsa_to_port(&priv->ds, port); + struct net_device *bdev; + u16 pvid; + u32 mask; + u32 ctrl; + int res; + + bdev =3D dsa_port_bridge_dev_get(dp); + + if (!bdev || !vlan_filtering) + pvid =3D YT921X_VID_UNWARE; + else + br_vlan_get_pvid(bdev, &pvid); + res =3D yt921x_port_set_pvid(priv, port, pvid); + if (res) + return res; + + mask =3D YT921X_PORT_VLAN_CTRL1_CVLAN_DROP_TAGGED | + YT921X_PORT_VLAN_CTRL1_CVLAN_DROP_UNTAGGED; + ctrl =3D 0; + /* Do not drop tagged frames here; let VLAN_IGR_FILTER do it */ + if (vlan_filtering && !pvid) + ctrl |=3D YT921X_PORT_VLAN_CTRL1_CVLAN_DROP_UNTAGGED; + res =3D yt921x_reg_update_bits(priv, YT921X_PORTn_VLAN_CTRL1(port), + mask, ctrl); + if (res) + return res; + + res =3D yt921x_reg_toggle_bits(priv, YT921X_VLAN_IGR_FILTER, + YT921X_VLAN_IGR_FILTER_PORTn(port), + vlan_filtering); + if (res) + return res; + + /* Turn on / off VLAN awareness */ + mask =3D YT921X_PORT_IGR_TPIDn_CTAG_M; + if (!vlan_filtering) + ctrl =3D 0; + else + ctrl =3D YT921X_PORT_IGR_TPIDn_CTAG(0); + res =3D yt921x_reg_update_bits(priv, YT921X_PORTn_IGR_TPID(port), + mask, ctrl); + if (res) + return res; + + return 0; +} + +static int +yt921x_vlan_del(struct yt921x_priv *priv, int port, u16 vid) +{ + u64 mask64; + + mask64 =3D YT921X_VLAN_CTRL_PORTS(port) | + YT921X_VLAN_CTRL_UNTAG_PORTn(port); + + return yt921x_reg64_clear_bits(priv, YT921X_VLANn_CTRL(vid), mask64); +} + +static int +yt921x_vlan_add(struct yt921x_priv *priv, int port, u16 vid, bool untagged) +{ + u64 mask64; + u64 ctrl64; + + mask64 =3D YT921X_VLAN_CTRL_PORTn(port) | + YT921X_VLAN_CTRL_PORTS(priv->cpu_ports_mask); + ctrl64 =3D mask64; + + mask64 |=3D YT921X_VLAN_CTRL_UNTAG_PORTn(port); + if (untagged) + ctrl64 |=3D YT921X_VLAN_CTRL_UNTAG_PORTn(port); + + return yt921x_reg64_update_bits(priv, YT921X_VLANn_CTRL(vid), + mask64, ctrl64); +} + +static int +yt921x_pvid_clear(struct yt921x_priv *priv, int port) +{ + struct dsa_port *dp =3D dsa_to_port(&priv->ds, port); + bool vlan_filtering; + u32 mask; + int res; + + vlan_filtering =3D dsa_port_is_vlan_filtering(dp); + + res =3D yt921x_port_set_pvid(priv, port, + vlan_filtering ? 0 : YT921X_VID_UNWARE); + if (res) + return res; + + if (vlan_filtering) { + mask =3D YT921X_PORT_VLAN_CTRL1_CVLAN_DROP_UNTAGGED; + res =3D yt921x_reg_set_bits(priv, YT921X_PORTn_VLAN_CTRL1(port), + mask); + if (res) + return res; + } + + return 0; +} + +static int +yt921x_pvid_set(struct yt921x_priv *priv, int port, u16 vid) +{ + struct dsa_port *dp =3D dsa_to_port(&priv->ds, port); + bool vlan_filtering; + u32 mask; + int res; + + vlan_filtering =3D dsa_port_is_vlan_filtering(dp); + + if (vlan_filtering) { + res =3D yt921x_port_set_pvid(priv, port, vid); + if (res) + return res; + } + + mask =3D YT921X_PORT_VLAN_CTRL1_CVLAN_DROP_UNTAGGED; + res =3D yt921x_reg_clear_bits(priv, YT921X_PORTn_VLAN_CTRL1(port), mask); + if (res) + return res; + + return 0; +} + +static int +yt921x_dsa_port_vlan_filtering(struct dsa_switch *ds, int port, + bool vlan_filtering, + struct netlink_ext_ack *extack) +{ + struct yt921x_priv *priv =3D to_yt921x_priv(ds); + int res; + + if (dsa_is_cpu_port(ds, port)) + return 0; + + mutex_lock(&priv->reg_lock); + res =3D yt921x_vlan_filtering(priv, port, vlan_filtering); + mutex_unlock(&priv->reg_lock); + + return res; +} + +static int +yt921x_dsa_port_vlan_del(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan) +{ + struct yt921x_priv *priv =3D to_yt921x_priv(ds); + u16 vid =3D vlan->vid; + u16 pvid; + int res; + + if (dsa_is_cpu_port(ds, port)) + return 0; + + mutex_lock(&priv->reg_lock); + do { + struct dsa_port *dp =3D dsa_to_port(ds, port); + struct net_device *bdev; + + res =3D yt921x_vlan_del(priv, port, vid); + if (res) + break; + + bdev =3D dsa_port_bridge_dev_get(dp); + if (bdev) { + br_vlan_get_pvid(bdev, &pvid); + if (pvid =3D=3D vid) + res =3D yt921x_pvid_clear(priv, port); + } + } while (0); + mutex_unlock(&priv->reg_lock); + + return res; +} + +static int +yt921x_dsa_port_vlan_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan, + struct netlink_ext_ack *extack) +{ + struct yt921x_priv *priv =3D to_yt921x_priv(ds); + u16 vid =3D vlan->vid; + u16 pvid; + int res; + + /* CPU port is supposed to be a member of every VLAN; see + * yt921x_vlan_add() and yt921x_port_setup() + */ + if (dsa_is_cpu_port(ds, port)) + return 0; + + mutex_lock(&priv->reg_lock); + do { + struct dsa_port *dp =3D dsa_to_port(ds, port); + struct net_device *bdev; + + res =3D yt921x_vlan_add(priv, port, vid, + (vlan->flags & + BRIDGE_VLAN_INFO_UNTAGGED) !=3D 0); + if (res) + break; + + bdev =3D dsa_port_bridge_dev_get(dp); + if (bdev) { + if ((vlan->flags & BRIDGE_VLAN_INFO_PVID) !=3D 0) { + res =3D yt921x_pvid_set(priv, port, vid); + } else { + br_vlan_get_pvid(bdev, &pvid); + if (pvid =3D=3D vid) + res =3D yt921x_pvid_clear(priv, port); + } + } + } while (0); + mutex_unlock(&priv->reg_lock); + + return res; +} + +static int yt921x_userport_standalone(struct yt921x_priv *priv, int port) +{ + u32 mask; + u32 ctrl; + int res; + + ctrl =3D ~priv->cpu_ports_mask; + res =3D yt921x_reg_write(priv, YT921X_PORTn_ISOLATION(port), ctrl); + if (res) + return res; + + /* Turn off FDB learning to prevent FDB pollution */ + mask =3D YT921X_PORT_LEARN_DIS; + res =3D yt921x_reg_set_bits(priv, YT921X_PORTn_LEARN(port), mask); + if (res) + return res; + + /* Turn off VLAN awareness */ + mask =3D YT921X_PORT_IGR_TPIDn_CTAG_M; + res =3D yt921x_reg_clear_bits(priv, YT921X_PORTn_IGR_TPID(port), mask); + if (res) + return res; + + /* Unrelated since learning is off and all packets are trapped; + * set it anyway + */ + res =3D yt921x_port_set_pvid(priv, port, YT921X_VID_UNWARE); + if (res) + return res; + + return 0; +} + +static int yt921x_userport_bridge(struct yt921x_priv *priv, int port) +{ + u32 mask; + int res; + + mask =3D YT921X_PORT_LEARN_DIS; + res =3D yt921x_reg_clear_bits(priv, YT921X_PORTn_LEARN(port), mask); + if (res) + return res; + + return 0; +} + +static int yt921x_isolate(struct yt921x_priv *priv, int port) +{ + u32 mask; + int res; + + mask =3D BIT(port); + for (int i =3D 0; i < YT921X_PORT_NUM; i++) { + if ((BIT(i) & priv->cpu_ports_mask) !=3D 0 || i =3D=3D port) + continue; + + res =3D yt921x_reg_set_bits(priv, YT921X_PORTn_ISOLATION(i), + mask); + if (res) + return res; + } + + return 0; +} + +/* Make sure to include the CPU port in ports_mask, or your bridge will + * not have it. + */ +static int yt921x_bridge(struct yt921x_priv *priv, u16 ports_mask) +{ + unsigned long targets_mask =3D ports_mask & ~priv->cpu_ports_mask; + u32 isolated_mask; + u32 ctrl; + int port; + int res; + + isolated_mask =3D 0; + for_each_set_bit(port, &targets_mask, YT921X_PORT_NUM) { + struct yt921x_port *pp =3D &priv->ports[port]; + + if (pp->isolated) + isolated_mask |=3D BIT(port); + } + + /* Block from non-cpu bridge ports ... */ + for_each_set_bit(port, &targets_mask, YT921X_PORT_NUM) { + struct yt921x_port *pp =3D &priv->ports[port]; + + /* to non-bridge ports */ + ctrl =3D ~ports_mask; + /* to isolated ports when isolated */ + if (pp->isolated) + ctrl |=3D isolated_mask; + /* to itself when non-hairpin */ + if (!pp->hairpin) + ctrl |=3D BIT(port); + else + ctrl &=3D ~BIT(port); + + res =3D yt921x_reg_write(priv, YT921X_PORTn_ISOLATION(port), + ctrl); + if (res) + return res; + } + + return 0; +} + +static int yt921x_bridge_leave(struct yt921x_priv *priv, int port) +{ + int res; + + res =3D yt921x_userport_standalone(priv, port); + if (res) + return res; + + res =3D yt921x_isolate(priv, port); + if (res) + return res; + + return 0; +} + +static int +yt921x_bridge_join(struct yt921x_priv *priv, int port, u16 ports_mask) +{ + int res; + + res =3D yt921x_userport_bridge(priv, port); + if (res) + return res; + + res =3D yt921x_bridge(priv, ports_mask); + if (res) + return res; + + return 0; +} + +static u32 +dsa_bridge_ports(struct dsa_switch *ds, const struct net_device *bdev) +{ + struct dsa_port *dp; + u32 mask =3D 0; + + dsa_switch_for_each_user_port(dp, ds) + if (dsa_port_offloads_bridge_dev(dp, bdev)) + mask |=3D BIT(dp->index); + + return mask; +} + +static int +yt921x_bridge_flags(struct yt921x_priv *priv, int port, + struct switchdev_brport_flags flags) +{ + struct yt921x_port *pp =3D &priv->ports[port]; + bool do_flush; + u32 mask; + int res; + + if ((flags.mask & BR_LEARNING) !=3D 0) { + bool learning =3D (flags.val & BR_LEARNING) !=3D 0; + + mask =3D YT921X_PORT_LEARN_DIS; + res =3D yt921x_reg_toggle_bits(priv, YT921X_PORTn_LEARN(port), + mask, !learning); + if (res) + return res; + } + + /* BR_FLOOD, BR_MCAST_FLOOD: see the comment where ACT_UNK_ACTn_TRAP + * is set + */ + + /* BR_BCAST_FLOOD: we can filter bcast, but cannot trap them */ + + do_flush =3D false; + if ((flags.mask & BR_HAIRPIN_MODE) !=3D 0) { + pp->hairpin =3D (flags.val & BR_HAIRPIN_MODE) !=3D 0; + do_flush =3D true; + } + if ((flags.mask & BR_ISOLATED) !=3D 0) { + pp->isolated =3D (flags.val & BR_ISOLATED) !=3D 0; + do_flush =3D true; + } + if (do_flush) { + struct dsa_switch *ds =3D &priv->ds; + struct dsa_port *dp =3D dsa_to_port(ds, port); + struct net_device *bdev; + + bdev =3D dsa_port_bridge_dev_get(dp); + if (bdev) { + u32 ports_mask; + + ports_mask =3D dsa_bridge_ports(ds, bdev); + ports_mask |=3D priv->cpu_ports_mask; + res =3D yt921x_bridge(priv, ports_mask); + if (res) + return res; + } + } + + return 0; +} + +static int +yt921x_dsa_port_pre_bridge_flags(struct dsa_switch *ds, int port, + struct switchdev_brport_flags flags, + struct netlink_ext_ack *extack) +{ + if ((flags.mask & ~(BR_HAIRPIN_MODE | BR_LEARNING | BR_FLOOD | + BR_MCAST_FLOOD | BR_ISOLATED)) !=3D 0) + return -EINVAL; + return 0; +} + +static int +yt921x_dsa_port_bridge_flags(struct dsa_switch *ds, int port, + struct switchdev_brport_flags flags, + struct netlink_ext_ack *extack) +{ + struct yt921x_priv *priv =3D to_yt921x_priv(ds); + int res; + + if (dsa_is_cpu_port(ds, port)) + return 0; + + mutex_lock(&priv->reg_lock); + res =3D yt921x_bridge_flags(priv, port, flags); + mutex_unlock(&priv->reg_lock); + + return res; +} + +static void +yt921x_dsa_port_bridge_leave(struct dsa_switch *ds, int port, + struct dsa_bridge bridge) +{ + struct yt921x_priv *priv =3D to_yt921x_priv(ds); + struct device *dev =3D to_device(priv); + int res; + + if (dsa_is_cpu_port(ds, port)) + return; + + mutex_lock(&priv->reg_lock); + res =3D yt921x_bridge_leave(priv, port); + mutex_unlock(&priv->reg_lock); + + if (res) + dev_err(dev, "Failed to %s port %d: %i\n", "unbridge", + port, res); +} + +static int +yt921x_dsa_port_bridge_join(struct dsa_switch *ds, int port, + struct dsa_bridge bridge, bool *tx_fwd_offload, + struct netlink_ext_ack *extack) +{ + struct yt921x_priv *priv =3D to_yt921x_priv(ds); + u16 ports_mask; + int res; + + if (dsa_is_cpu_port(ds, port)) + return 0; + + ports_mask =3D dsa_bridge_ports(ds, bridge.dev); + ports_mask |=3D priv->cpu_ports_mask; + + mutex_lock(&priv->reg_lock); + res =3D yt921x_bridge_join(priv, port, ports_mask); + mutex_unlock(&priv->reg_lock); + + return res; +} + +static int yt921x_port_down(struct yt921x_priv *priv, int port) +{ + u32 mask; + int res; + + mask =3D YT921X_PORT_LINK | YT921X_PORT_RX_MAC_EN | YT921X_PORT_TX_MAC_EN; + res =3D yt921x_reg_clear_bits(priv, YT921X_PORTn_CTRL(port), mask); + if (res) + return res; + + if (yt921x_port_is_external(port)) { + mask =3D YT921X_SERDES_LINK; + res =3D yt921x_reg_clear_bits(priv, YT921X_SERDESn(port), mask); + if (res) + return res; + + mask =3D YT921X_XMII_LINK; + res =3D yt921x_reg_clear_bits(priv, YT921X_XMIIn(port), mask); + if (res) + return res; + } + + return 0; +} + +static int +yt921x_port_up(struct yt921x_priv *priv, int port, unsigned int mode, + phy_interface_t interface, int speed, int duplex, + bool tx_pause, bool rx_pause) +{ + u32 mask; + u32 ctrl; + int res; + + switch (speed) { + case SPEED_10: + ctrl =3D YT921X_PORT_SPEED_10; + break; + case SPEED_100: + ctrl =3D YT921X_PORT_SPEED_100; + break; + case SPEED_1000: + ctrl =3D YT921X_PORT_SPEED_1000; + break; + case SPEED_2500: + ctrl =3D YT921X_PORT_SPEED_2500; + break; + case SPEED_10000: + ctrl =3D YT921X_PORT_SPEED_10000; + break; + default: + return -EINVAL; + } + if (duplex =3D=3D DUPLEX_FULL) + ctrl |=3D YT921X_PORT_DUPLEX_FULL; + if (tx_pause) + ctrl |=3D YT921X_PORT_TX_PAUSE; + if (rx_pause) + ctrl |=3D YT921X_PORT_RX_PAUSE; + ctrl |=3D YT921X_PORT_RX_MAC_EN | YT921X_PORT_TX_MAC_EN; + res =3D yt921x_reg_write(priv, YT921X_PORTn_CTRL(port), ctrl); + if (res) + return res; + + if (yt921x_port_is_external(port)) { + mask =3D YT921X_SERDES_SPEED_M; + switch (speed) { + case SPEED_10: + ctrl =3D YT921X_SERDES_SPEED_10; + break; + case SPEED_100: + ctrl =3D YT921X_SERDES_SPEED_100; + break; + case SPEED_1000: + ctrl =3D YT921X_SERDES_SPEED_1000; + break; + case SPEED_2500: + ctrl =3D YT921X_SERDES_SPEED_2500; + break; + case SPEED_10000: + ctrl =3D YT921X_SERDES_SPEED_10000; + break; + default: + return -EINVAL; + } + mask |=3D YT921X_SERDES_DUPLEX_FULL; + if (duplex =3D=3D DUPLEX_FULL) + ctrl |=3D YT921X_SERDES_DUPLEX_FULL; + mask |=3D YT921X_SERDES_TX_PAUSE; + if (tx_pause) + ctrl |=3D YT921X_SERDES_TX_PAUSE; + mask |=3D YT921X_SERDES_RX_PAUSE; + if (rx_pause) + ctrl |=3D YT921X_SERDES_RX_PAUSE; + mask |=3D YT921X_SERDES_LINK; + ctrl |=3D YT921X_SERDES_LINK; + res =3D yt921x_reg_update_bits(priv, YT921X_SERDESn(port), + mask, ctrl); + if (res) + return res; + + mask =3D YT921X_XMII_LINK; + res =3D yt921x_reg_set_bits(priv, YT921X_XMIIn(port), mask); + if (res) + return res; + + switch (speed) { + case SPEED_10: + ctrl =3D YT921X_MDIO_POLLING_SPEED_10; + break; + case SPEED_100: + ctrl =3D YT921X_MDIO_POLLING_SPEED_100; + break; + case SPEED_1000: + ctrl =3D YT921X_MDIO_POLLING_SPEED_1000; + break; + case SPEED_2500: + ctrl =3D YT921X_MDIO_POLLING_SPEED_2500; + break; + case SPEED_10000: + ctrl =3D YT921X_MDIO_POLLING_SPEED_10000; + break; + default: + return -EINVAL; + } + if (duplex =3D=3D DUPLEX_FULL) + ctrl |=3D YT921X_MDIO_POLLING_DUPLEX_FULL; + ctrl |=3D YT921X_MDIO_POLLING_LINK; + res =3D yt921x_reg_write(priv, YT921X_MDIO_POLLINGn(port), ctrl); + if (res) + return res; + } + + return 0; +} + +static int +yt921x_port_config(struct yt921x_priv *priv, int port, unsigned int mode, + phy_interface_t interface) +{ + struct device *dev =3D to_device(priv); + u32 mask; + u32 ctrl; + int res; + + if (!yt921x_port_is_external(port)) { + if (interface !=3D PHY_INTERFACE_MODE_INTERNAL) { + dev_err(dev, "Wrong mode %d on port %d\n", + interface, port); + return -EINVAL; + } + return 0; + } + + switch (interface) { + /* SERDES */ + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_REVSGMII: + case PHY_INTERFACE_MODE_100BASEX: + case PHY_INTERFACE_MODE_1000BASEX: + case PHY_INTERFACE_MODE_2500BASEX: + mask =3D YT921X_SERDES_CTRL_PORTn(port); + res =3D yt921x_reg_set_bits(priv, YT921X_SERDES_CTRL, mask); + if (res) + return res; + + mask =3D YT921X_XMII_CTRL_PORTn(port); + res =3D yt921x_reg_clear_bits(priv, YT921X_XMII_CTRL, mask); + if (res) + return res; + + mask =3D YT921X_SERDES_MODE_M; + switch (interface) { + case PHY_INTERFACE_MODE_SGMII: + ctrl =3D YT921X_SERDES_MODE_SGMII; + break; + case PHY_INTERFACE_MODE_REVSGMII: + ctrl =3D YT921X_SERDES_MODE_REVSGMII; + break; + case PHY_INTERFACE_MODE_100BASEX: + ctrl =3D YT921X_SERDES_MODE_100BASEX; + break; + case PHY_INTERFACE_MODE_1000BASEX: + ctrl =3D YT921X_SERDES_MODE_1000BASEX; + break; + case PHY_INTERFACE_MODE_2500BASEX: + ctrl =3D YT921X_SERDES_MODE_2500BASEX; + break; + default: + return -EINVAL; + } + res =3D yt921x_reg_update_bits(priv, YT921X_SERDESn(port), + mask, ctrl); + if (res) + return res; + + break; + /* add XMII support here */ + default: + return -EINVAL; + } + + return 0; +} + +static void +yt921x_phylink_mac_link_down(struct phylink_config *config, unsigned int m= ode, + phy_interface_t interface) +{ + struct dsa_port *dp =3D dsa_phylink_to_port(config); + struct yt921x_priv *priv =3D to_yt921x_priv(dp->ds); + int port =3D dp->index; + int res; + + /* No need to sync; port control block is hold until device remove */ + cancel_delayed_work(&priv->ports[port].mib_read); + + mutex_lock(&priv->reg_lock); + res =3D yt921x_port_down(priv, port); + mutex_unlock(&priv->reg_lock); + + if (res) + dev_err(dp->ds->dev, "Failed to %s port %d: %i\n", "bring down", + port, res); +} + +static void +yt921x_phylink_mac_link_up(struct phylink_config *config, + struct phy_device *phydev, unsigned int mode, + phy_interface_t interface, int speed, int duplex, + bool tx_pause, bool rx_pause) +{ + struct dsa_port *dp =3D dsa_phylink_to_port(config); + struct yt921x_priv *priv =3D to_yt921x_priv(dp->ds); + int port =3D dp->index; + int res; + + mutex_lock(&priv->reg_lock); + res =3D yt921x_port_up(priv, port, mode, interface, speed, duplex, + tx_pause, rx_pause); + mutex_unlock(&priv->reg_lock); + + if (res) + dev_err(dp->ds->dev, "Failed to %s port %d: %i\n", "bring up", + port, res); + + schedule_delayed_work(&priv->ports[port].mib_read, 0); +} + +static void +yt921x_phylink_mac_config(struct phylink_config *config, unsigned int mode, + const struct phylink_link_state *state) +{ + struct dsa_port *dp =3D dsa_phylink_to_port(config); + struct yt921x_priv *priv =3D to_yt921x_priv(dp->ds); + int port =3D dp->index; + int res; + + mutex_lock(&priv->reg_lock); + res =3D yt921x_port_config(priv, port, mode, state->interface); + mutex_unlock(&priv->reg_lock); + + if (res) + dev_err(dp->ds->dev, "Failed to %s port %d: %i\n", "config", + port, res); +} + +static void +yt921x_dsa_phylink_get_caps(struct dsa_switch *ds, int port, + struct phylink_config *config) +{ + struct yt921x_priv *priv =3D to_yt921x_priv(ds); + const struct yt921x_info *info =3D priv->info; + + config->mac_capabilities =3D MAC_ASYM_PAUSE | MAC_SYM_PAUSE | + MAC_10 | MAC_100 | MAC_1000; + + if ((info->internal_mask & BIT(port)) !=3D 0) { + /* Port 10 for MCU should probably go here too. But since that + * is untested yet, turn it down for the moment by letting it + * fall to the default branch. + */ + __set_bit(PHY_INTERFACE_MODE_INTERNAL, + config->supported_interfaces); + } else if ((info->external_mask & BIT(port)) !=3D 0) { + /* TODO: external ports may support SERDES only, XMII only, or + * SERDES + XMII depending on the chip. However, we can't get + * the accurate config table due to lack of document, thus + * we simply declare SERDES + XMII and rely on the correctness + * of devicetree for now. + */ + + /* SERDES */ + __set_bit(PHY_INTERFACE_MODE_SGMII, + config->supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_REVSGMII, + config->supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_100BASEX, + config->supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_1000BASEX, + config->supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_2500BASEX, + config->supported_interfaces); + config->mac_capabilities |=3D MAC_2500FD; + + /* XMII */ + + /* Not tested. To add support for XMII: + * - Add proper interface modes below + * - Handle them in yt921x_port_config() + */ + } + /* no such port: empty supported_interfaces causes phylink to turn it + * down + */ +} + +static int yt921x_port_setup(struct yt921x_priv *priv, int port) +{ + struct dsa_switch *ds =3D &priv->ds; + u32 ctrl; + int res; + + res =3D yt921x_userport_standalone(priv, port); + if (res) + return res; + + if (dsa_is_cpu_port(ds, port)) { + /* CPU port egress is supposed to be completely controlled via + * tagging, so set to oneway isolated (drop all packets without + * tag). + */ + ctrl =3D ~(u32)0; + res =3D yt921x_reg_write(priv, YT921X_PORTn_ISOLATION(port), + ctrl); + if (res) + return res; + + /* To simplify FDB "isolation" simulation, we also disable + * learning on the CPU port, and let software identify packets + * towarding CPU (either trapped or a static FDB entry is + * matched, no matter which bridge that entry is for), which is + * already done by yt921x_userport_standalone(). As a result, + * VLAN-awareness becomes unrelated on the CPU port (set to + * VLAN-unaware by the way). + */ + } + + return 0; +} + +static enum dsa_tag_protocol +yt921x_dsa_get_tag_protocol(struct dsa_switch *ds, int port, + enum dsa_tag_protocol m) +{ + return DSA_TAG_PROTO_YT921X; +} + +static int yt921x_dsa_port_setup(struct dsa_switch *ds, int port) +{ + struct yt921x_priv *priv =3D to_yt921x_priv(ds); + int res; + + mutex_lock(&priv->reg_lock); + res =3D yt921x_port_setup(priv, port); + mutex_unlock(&priv->reg_lock); + + return res; +} + +static int yt921x_edata_wait(struct yt921x_priv *priv, u32 *valp) +{ + u32 val =3D YT921X_EDATA_DATA_IDLE; + int res; + + res =3D yt921x_reg_wait(priv, YT921X_EDATA_DATA, + YT921X_EDATA_DATA_STATUS_M, &val); + if (res) + return res; + + *valp =3D val; + return 0; +} + +static int +yt921x_edata_read_cont(struct yt921x_priv *priv, u8 addr, u8 *valp) +{ + u32 ctrl; + u32 val; + int res; + + ctrl =3D YT921X_EDATA_CTRL_ADDR(addr) | YT921X_EDATA_CTRL_READ; + res =3D yt921x_reg_write(priv, YT921X_EDATA_CTRL, ctrl); + if (res) + return res; + res =3D yt921x_edata_wait(priv, &val); + if (res) + return res; + + *valp =3D FIELD_GET(YT921X_EDATA_DATA_DATA_M, val); + return 0; +} + +static int yt921x_edata_read(struct yt921x_priv *priv, u8 addr, u8 *valp) +{ + u32 val; + int res; + + res =3D yt921x_edata_wait(priv, &val); + if (res) + return res; + return yt921x_edata_read_cont(priv, addr, valp); +} + +static int yt921x_chip_detect(struct yt921x_priv *priv) +{ + struct device *dev =3D to_device(priv); + const struct yt921x_info *info; + u8 extmode; + u32 chipid; + u32 major; + u32 mode; + int res; + + res =3D yt921x_reg_read(priv, YT921X_CHIP_ID, &chipid); + if (res) + return res; + + major =3D FIELD_GET(YT921X_CHIP_ID_MAJOR, chipid); + + for (info =3D yt921x_infos; info->name; info++) + if (info->major =3D=3D major) + break; + if (!info->name) { + dev_err(dev, "Unexpected chipid 0x%x\n", chipid); + return -ENODEV; + } + + res =3D yt921x_reg_read(priv, YT921X_CHIP_MODE, &mode); + if (res) + return res; + res =3D yt921x_edata_read(priv, YT921X_EDATA_EXTMODE, &extmode); + if (res) + return res; + + for (; info->name; info++) + if (info->major =3D=3D major && info->mode =3D=3D mode && + info->extmode =3D=3D extmode) + break; + if (!info->name) { + dev_err(dev, + "Unsupported chipid 0x%x with chipmode 0x%x 0x%x\n", + chipid, mode, extmode); + return -ENODEV; + } + + /* Print chipid here since we are interested in lower 16 bits */ + dev_info(dev, + "Motorcomm %s ethernet switch, chipid: 0x%x, " + "chipmode: 0x%x 0x%x\n", + info->name, chipid, mode, extmode); + + priv->info =3D info; + return 0; +} + +static int yt921x_chip_reset(struct yt921x_priv *priv) +{ + struct device *dev =3D to_device(priv); + u16 eth_p_tag; + u32 val; + int res; + + res =3D yt921x_chip_detect(priv); + if (res) + return res; + + /* Reset */ + res =3D yt921x_reg_write(priv, YT921X_RST, YT921X_RST_HW); + if (res) + return res; + + /* RST_HW is almost same as GPIO hard reset, so we need this delay. */ + fsleep(YT921X_RST_DELAY_US); + + val =3D 0; + res =3D yt921x_reg_wait(priv, YT921X_RST, ~0, &val); + if (res) + return res; + + /* Check for tag EtherType; do it after reset in case you messed it up + * before. + */ + res =3D yt921x_reg_read(priv, YT921X_CPU_TAG_TPID, &val); + if (res) + return res; + eth_p_tag =3D FIELD_GET(YT921X_CPU_TAG_TPID_TPID_M, val); + if (eth_p_tag !=3D ETH_P_YT921X) { + dev_err(dev, "Tag type 0x%x !=3D 0x%x\n", eth_p_tag, + ETH_P_YT921X); + /* Despite being possible, we choose not to set CPU_TAG_TPID, + * since there is no way it can be different unless you have the + * wrong chip. + */ + return -EINVAL; + } + + return 0; +} + +static int yt921x_chip_setup(struct yt921x_priv *priv) +{ + struct dsa_switch *ds =3D &priv->ds; + unsigned long cpu_ports_mask; + u64 ctrl64; + u32 ctrl; + int port; + int res; + + /* Enable DSA */ + priv->cpu_ports_mask =3D dsa_cpu_ports(ds); + + ctrl =3D YT921X_EXT_CPU_PORT_TAG_EN | YT921X_EXT_CPU_PORT_PORT_EN | + YT921X_EXT_CPU_PORT_PORT(__ffs(priv->cpu_ports_mask)); + res =3D yt921x_reg_write(priv, YT921X_EXT_CPU_PORT, ctrl); + if (res) + return res; + + /* Enable and clear MIB */ + res =3D yt921x_reg_set_bits(priv, YT921X_FUNC, YT921X_FUNC_MIB); + if (res) + return res; + + ctrl =3D YT921X_MIB_CTRL_CLEAN | YT921X_MIB_CTRL_ALL_PORT; + res =3D yt921x_reg_write(priv, YT921X_MIB_CTRL, ctrl); + if (res) + return res; + + /* Setup software switch */ + ctrl =3D YT921X_CPU_COPY_TO_EXT_CPU; + res =3D yt921x_reg_write(priv, YT921X_CPU_COPY, ctrl); + if (res) + return res; + + ctrl =3D GENMASK(10, 0); + res =3D yt921x_reg_write(priv, YT921X_FILTER_UNK_UCAST, ctrl); + if (res) + return res; + res =3D yt921x_reg_write(priv, YT921X_FILTER_UNK_MCAST, ctrl); + if (res) + return res; + + /* YT921x does not support native DSA port bridging, so we use port + * isolation to emulate it. However, be especially careful that port + * isolation takes _after_ FDB lookups, i.e. if an FDB entry (from + * another bridge) is matched and the destination port (in another + * bridge) is blocked, the packet will be dropped instead of flooding to + * the "bridged" ports, thus we need to trap and handle those packets by + * software. + * + * If there is no more than one bridge, we might be able to drop them + * directly given some conditions are met, but we trap them in all cases + * for now. + */ + ctrl =3D 0; + for (int i =3D 0; i < YT921X_PORT_NUM; i++) + ctrl |=3D YT921X_ACT_UNK_ACTn_TRAP(i); + /* Except for CPU ports, if any packets are sent via CPU ports without + * tag, they should be dropped. + */ + cpu_ports_mask =3D priv->cpu_ports_mask; + for_each_set_bit(port, &cpu_ports_mask, YT921X_PORT_NUM) { + ctrl &=3D ~YT921X_ACT_UNK_ACTn_M(port); + ctrl |=3D YT921X_ACT_UNK_ACTn_DROP(port); + } + res =3D yt921x_reg_write(priv, YT921X_ACT_UNK_UCAST, ctrl); + if (res) + return res; + res =3D yt921x_reg_write(priv, YT921X_ACT_UNK_MCAST, ctrl); + if (res) + return res; + + /* Tagged VID 0 should be treated as untagged, which confuses the + * hardware a lot + */ + ctrl64 =3D YT921X_VLAN_CTRL_LEARN_DIS | YT921X_VLAN_CTRL_PORTS_M; + res =3D yt921x_reg64_write(priv, YT921X_VLANn_CTRL(0), ctrl64); + if (res) + return res; + + /* Miscellaneous */ + res =3D yt921x_reg_set_bits(priv, YT921X_SENSOR, YT921X_SENSOR_TEMP); + if (res) + return res; + + return 0; +} + +static int yt921x_dsa_setup(struct dsa_switch *ds) +{ + struct yt921x_priv *priv =3D to_yt921x_priv(ds); + struct device *dev =3D to_device(priv); + struct device_node *np =3D dev->of_node; + struct device_node *child; + int res; + + mutex_lock(&priv->reg_lock); + res =3D yt921x_chip_reset(priv); + mutex_unlock(&priv->reg_lock); + + if (res) + return res; + + /* Register the internal mdio bus. Nodes for internal ports should have + * proper phy-handle pointing to their PHYs. Not enabling the internal + * bus is possible, though pretty wired, if internal ports are not used. + */ + child =3D of_get_child_by_name(np, "mdio"); + if (child) { + res =3D yt921x_mbus_int_init(priv, child); + of_node_put(child); + if (res) + return res; + } + + /* External mdio bus is optional */ + child =3D of_get_child_by_name(np, "mdio-external"); + if (child) { + res =3D yt921x_mbus_ext_init(priv, child); + of_node_put(child); + if (res) + return res; + + dev_err(dev, "Untested external mdio bus\n"); + return -ENODEV; + } + + mutex_lock(&priv->reg_lock); + res =3D yt921x_chip_setup(priv); + mutex_unlock(&priv->reg_lock); + + if (res) + return res; + + return 0; +} + +static const struct phylink_mac_ops yt921x_phylink_mac_ops =3D { + .mac_link_down =3D yt921x_phylink_mac_link_down, + .mac_link_up =3D yt921x_phylink_mac_link_up, + .mac_config =3D yt921x_phylink_mac_config, +}; + +static const struct dsa_switch_ops yt921x_dsa_switch_ops =3D { + /* mib */ + .get_strings =3D yt921x_dsa_get_strings, + .get_ethtool_stats =3D yt921x_dsa_get_ethtool_stats, + .get_sset_count =3D yt921x_dsa_get_sset_count, + .get_eth_mac_stats =3D yt921x_dsa_get_eth_mac_stats, + .get_eth_ctrl_stats =3D yt921x_dsa_get_eth_ctrl_stats, + .get_rmon_stats =3D yt921x_dsa_get_rmon_stats, + .get_stats64 =3D yt921x_dsa_get_stats64, + .get_pause_stats =3D yt921x_dsa_get_pause_stats, + /* eee */ + .support_eee =3D dsa_supports_eee, + .set_mac_eee =3D yt921x_dsa_set_mac_eee, + /* mtu */ + .port_change_mtu =3D yt921x_dsa_port_change_mtu, + .port_max_mtu =3D yt921x_dsa_port_max_mtu, + /* mirror */ + .port_mirror_del =3D yt921x_dsa_port_mirror_del, + .port_mirror_add =3D yt921x_dsa_port_mirror_add, + /* fdb */ + .port_fdb_dump =3D yt921x_dsa_port_fdb_dump, + .port_fast_age =3D yt921x_dsa_port_fast_age, + .set_ageing_time =3D yt921x_dsa_set_ageing_time, + .port_fdb_del =3D yt921x_dsa_port_fdb_del, + .port_fdb_add =3D yt921x_dsa_port_fdb_add, + .port_mdb_del =3D yt921x_dsa_port_mdb_del, + .port_mdb_add =3D yt921x_dsa_port_mdb_add, + /* vlan */ + .port_vlan_filtering =3D yt921x_dsa_port_vlan_filtering, + .port_vlan_del =3D yt921x_dsa_port_vlan_del, + .port_vlan_add =3D yt921x_dsa_port_vlan_add, + /* bridge */ + .port_pre_bridge_flags =3D yt921x_dsa_port_pre_bridge_flags, + .port_bridge_flags =3D yt921x_dsa_port_bridge_flags, + .port_bridge_leave =3D yt921x_dsa_port_bridge_leave, + .port_bridge_join =3D yt921x_dsa_port_bridge_join, + /* port */ + .get_tag_protocol =3D yt921x_dsa_get_tag_protocol, + .phylink_get_caps =3D yt921x_dsa_phylink_get_caps, + .port_setup =3D yt921x_dsa_port_setup, + /* chip */ + .setup =3D yt921x_dsa_setup, +}; + +static void yt921x_mdio_shutdown(struct mdio_device *mdiodev) +{ + struct yt921x_priv *priv =3D mdiodev_get_drvdata(mdiodev); + + if (!priv) + return; + + dsa_switch_shutdown(&priv->ds); +} + +static void yt921x_mdio_remove(struct mdio_device *mdiodev) +{ + struct yt921x_priv *priv =3D mdiodev_get_drvdata(mdiodev); + + if (!priv) + return; + + for (size_t i =3D ARRAY_SIZE(priv->ports); i-- > 0; ) { + struct yt921x_port *pp =3D &priv->ports[i]; + + cancel_delayed_work_sync(&pp->mib_read); + } + + dsa_unregister_switch(&priv->ds); + + mutex_destroy(&priv->reg_lock); +} + +static int yt921x_mdio_probe(struct mdio_device *mdiodev) +{ + struct device *dev =3D &mdiodev->dev; + struct yt921x_reg_mdio *mdio; + struct yt921x_priv *priv; + struct dsa_switch *ds; + + priv =3D devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + mdio =3D devm_kzalloc(dev, sizeof(*mdio), GFP_KERNEL); + if (!mdio) + return -ENOMEM; + + mdio->bus =3D mdiodev->bus; + mdio->addr =3D mdiodev->addr; + mdio->switchid =3D 0; + + mutex_init(&priv->reg_lock); + + priv->reg_ops =3D &yt921x_reg_ops_mdio; + priv->reg_ctx =3D mdio; + + for (size_t i =3D 0; i < ARRAY_SIZE(priv->ports); i++) { + struct yt921x_port *pp =3D &priv->ports[i]; + + pp->index =3D i; + INIT_DELAYED_WORK(&pp->mib_read, yt921x_poll_mib); + } + + ds =3D &priv->ds; + ds->dev =3D dev; + ds->assisted_learning_on_cpu_port =3D true; + ds->priv =3D priv; + ds->ops =3D &yt921x_dsa_switch_ops; + ds->phylink_mac_ops =3D &yt921x_phylink_mac_ops; + ds->num_ports =3D YT921X_PORT_NUM; + + mdiodev_set_drvdata(mdiodev, priv); + + return dsa_register_switch(ds); +} + +static const struct of_device_id yt921x_of_match[] =3D { + { .compatible =3D "motorcomm,yt9215" }, + {} +}; +MODULE_DEVICE_TABLE(of, yt921x_of_match); + +static struct mdio_driver yt921x_mdio_driver =3D { + .probe =3D yt921x_mdio_probe, + .remove =3D yt921x_mdio_remove, + .shutdown =3D yt921x_mdio_shutdown, + .mdiodrv.driver =3D { + .name =3D YT921X_NAME, + .of_match_table =3D yt921x_of_match, + }, +}; + +mdio_module_driver(yt921x_mdio_driver); + +MODULE_AUTHOR("David Yang "); +MODULE_DESCRIPTION("Driver for Motorcomm YT921x Switch"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/dsa/yt921x.h b/drivers/net/dsa/yt921x.h new file mode 100644 index 000000000000..a269b313d2a8 --- /dev/null +++ b/drivers/net/dsa/yt921x.h @@ -0,0 +1,505 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2025 David Yang + */ + +#ifndef __YT921X_H +#define __YT921X_H + +#include + +#define YT921X_SMI_SWITCHID_M GENMASK(3, 2) +#define YT921X_SMI_SWITCHID(x) FIELD_PREP(YT921X_SMI_SWITCHID_M, (x)) +#define YT921X_SMI_AD BIT(1) +#define YT921X_SMI_ADDR 0 +#define YT921X_SMI_DATA YT921X_SMI_AD +#define YT921X_SMI_RW BIT(0) +#define YT921X_SMI_WRITE 0 +#define YT921X_SMI_READ YT921X_SMI_RW + +#define YT921X_SWITCHID_NUM 4 + +#define YT921X_RST 0x80000 +#define YT921X_RST_HW BIT(31) +#define YT921X_RST_SW BIT(1) +#define YT921X_FUNC 0x80004 +#define YT921X_FUNC_MIB BIT(1) +#define YT921X_CHIP_ID 0x80008 +#define YT921X_CHIP_ID_MAJOR GENMASK(31, 16) +#define YT921X_EXT_CPU_PORT 0x8000c +#define YT921X_EXT_CPU_PORT_TAG_EN BIT(15) +#define YT921X_EXT_CPU_PORT_PORT_EN BIT(14) +#define YT921X_EXT_CPU_PORT_PORT_M GENMASK(3, 0) +#define YT921X_EXT_CPU_PORT_PORT(x) FIELD_PREP(YT921X_EXT_CPU_PORT_POR= T_M, (x)) +#define YT921X_CPU_TAG_TPID 0x80010 +#define YT921X_CPU_TAG_TPID_TPID_M GENMASK(15, 0) +/* Same as ETH_P_YT921X, but this represents the true HW default, while the + * former is a local convention chosen by us. + */ +#define YT921X_CPU_TAG_TPID_TPID_DEFAULT 0x9988 +#define YT921X_PVID_SEL 0x80014 +#define YT921X_PVID_SEL_SVID_PORTn(port) BIT(port) +#define YT921X_SERDES_CTRL 0x80028 +#define YT921X_SERDES_CTRL_PORTn_TEST(port) BIT((port) - 3) +#define YT921X_SERDES_CTRL_PORTn(port) BIT((port) - 8) +#define YT921X_IO_LEVEL 0x80030 +#define YT9215_IO_LEVEL_NORMAL_M GENMASK(5, 4) +#define YT9215_IO_LEVEL_NORMAL(x) FIELD_PREP(YT9215_IO_LEVEL_NORMAL_M,= (x)) +#define YT9215_IO_LEVEL_NORMAL_3V3 YT9215_IO_LEVEL_NORMAL(0) +#define YT9215_IO_LEVEL_NORMAL_1V8 YT9215_IO_LEVEL_NORMAL(3) +#define YT9215_IO_LEVEL_RGMII1_M GENMASK(3, 2) +#define YT9215_IO_LEVEL_RGMII1(x) FIELD_PREP(YT9215_IO_LEVEL_RGMII1_M,= (x)) +#define YT9215_IO_LEVEL_RGMII1_3V3 YT9215_IO_LEVEL_RGMII1(0) +#define YT9215_IO_LEVEL_RGMII1_2V5 YT9215_IO_LEVEL_RGMII1(1) +#define YT9215_IO_LEVEL_RGMII1_1V8 YT9215_IO_LEVEL_RGMII1(2) +#define YT9215_IO_LEVEL_RGMII0_M GENMASK(1, 0) +#define YT9215_IO_LEVEL_RGMII0(x) FIELD_PREP(YT9215_IO_LEVEL_RGMII0_M,= (x)) +#define YT9215_IO_LEVEL_RGMII0_3V3 YT9215_IO_LEVEL_RGMII0(0) +#define YT9215_IO_LEVEL_RGMII0_2V5 YT9215_IO_LEVEL_RGMII0(1) +#define YT9215_IO_LEVEL_RGMII0_1V8 YT9215_IO_LEVEL_RGMII0(2) +#define YT9218_IO_LEVEL_RGMII1_M GENMASK(5, 4) +#define YT9218_IO_LEVEL_RGMII1(x) FIELD_PREP(YT9218_IO_LEVEL_RGMII1_M,= (x)) +#define YT9218_IO_LEVEL_RGMII1_3V3 YT9218_IO_LEVEL_RGMII1(0) +#define YT9218_IO_LEVEL_RGMII1_2V5 YT9218_IO_LEVEL_RGMII1(1) +#define YT9218_IO_LEVEL_RGMII1_1V8 YT9218_IO_LEVEL_RGMII1(2) +#define YT9218_IO_LEVEL_RGMII0_M GENMASK(3, 2) +#define YT9218_IO_LEVEL_RGMII0(x) FIELD_PREP(YT9218_IO_LEVEL_RGMII0_M,= (x)) +#define YT9218_IO_LEVEL_RGMII0_3V3 YT9218_IO_LEVEL_RGMII0(0) +#define YT9218_IO_LEVEL_RGMII0_2V5 YT9218_IO_LEVEL_RGMII0(1) +#define YT9218_IO_LEVEL_RGMII0_1V8 YT9218_IO_LEVEL_RGMII0(2) +#define YT9218_IO_LEVEL_NORMAL_M GENMASK(1, 0) +#define YT9218_IO_LEVEL_NORMAL(x) FIELD_PREP(YT9218_IO_LEVEL_NORMAL_M,= (x)) +#define YT9218_IO_LEVEL_NORMAL_3V3 YT9218_IO_LEVEL_NORMAL(0) +#define YT9218_IO_LEVEL_NORMAL_1V8 YT9218_IO_LEVEL_NORMAL(3) +#define YT921X_MAC_ADDR_HI2 0x80080 +#define YT921X_MAC_ADDR_LO4 0x80084 +#define YT921X_SERDESn(port) (0x8008c + 4 * ((port) - 8)) +#define YT921X_SERDES_MODE_M GENMASK(9, 7) +#define YT921X_SERDES_MODE(x) FIELD_PREP(YT921X_SERDES_MODE_M, (x)) +#define YT921X_SERDES_MODE_SGMII YT921X_SERDES_MODE(0) +#define YT921X_SERDES_MODE_REVSGMII YT921X_SERDES_MODE(1) +#define YT921X_SERDES_MODE_1000BASEX YT921X_SERDES_MODE(2) +#define YT921X_SERDES_MODE_100BASEX YT921X_SERDES_MODE(3) +#define YT921X_SERDES_MODE_2500BASEX YT921X_SERDES_MODE(4) +#define YT921X_SERDES_MODE_BASEX YT921X_SERDES_MODE(5) +#define YT921X_SERDES_RX_PAUSE BIT(6) +#define YT921X_SERDES_TX_PAUSE BIT(5) +#define YT921X_SERDES_LINK BIT(4) /* force link */ +#define YT921X_SERDES_DUPLEX_FULL BIT(3) +#define YT921X_SERDES_SPEED_M GENMASK(2, 0) +#define YT921X_SERDES_SPEED(x) FIELD_PREP(YT921X_SERDES_SPEED_M, (x)) +#define YT921X_SERDES_SPEED_10 YT921X_SERDES_SPEED(0) +#define YT921X_SERDES_SPEED_100 YT921X_SERDES_SPEED(1) +#define YT921X_SERDES_SPEED_1000 YT921X_SERDES_SPEED(2) +#define YT921X_SERDES_SPEED_10000 YT921X_SERDES_SPEED(3) +#define YT921X_SERDES_SPEED_2500 YT921X_SERDES_SPEED(4) +#define YT921X_PORTn_CTRL(port) (0x80100 + 4 * (port)) +#define YT921X_PORT_CTRL_PAUSE_AN BIT(10) +#define YT921X_PORTn_STATUS(port) (0x80200 + 4 * (port)) +#define YT921X_PORT_LINK BIT(9) /* CTRL: auto negotiation */ +#define YT921X_PORT_HALF_PAUSE BIT(8) /* Half-duplex back pressure mod= e */ +#define YT921X_PORT_DUPLEX_FULL BIT(7) +#define YT921X_PORT_RX_PAUSE BIT(6) +#define YT921X_PORT_TX_PAUSE BIT(5) +#define YT921X_PORT_RX_MAC_EN BIT(4) +#define YT921X_PORT_TX_MAC_EN BIT(3) +#define YT921X_PORT_SPEED_M GENMASK(2, 0) +#define YT921X_PORT_SPEED(x) FIELD_PREP(YT921X_PORT_SPEED_M, (x)) +#define YT921X_PORT_SPEED_10 YT921X_PORT_SPEED(0) +#define YT921X_PORT_SPEED_100 YT921X_PORT_SPEED(1) +#define YT921X_PORT_SPEED_1000 YT921X_PORT_SPEED(2) +#define YT921X_PORT_SPEED_10000 YT921X_PORT_SPEED(3) +#define YT921X_PORT_SPEED_2500 YT921X_PORT_SPEED(4) +#define YT921X_PON_STRAP_FUNC 0x80320 +#define YT921X_PON_STRAP_VAL 0x80324 +#define YT921X_PON_STRAP_CAP 0x80328 +#define YT921X_PON_STRAP_EEE BIT(16) +#define YT921X_PON_STRAP_LOOP_DETECT BIT(7) +#define YT921X_MDIO_POLLINGn(port) (0x80364 + 4 * ((port) - 8)) +#define YT921X_MDIO_POLLING_DUPLEX_FULL BIT(4) +#define YT921X_MDIO_POLLING_LINK BIT(3) +#define YT921X_MDIO_POLLING_SPEED_M GENMASK(2, 0) +#define YT921X_MDIO_POLLING_SPEED(x) FIELD_PREP(YT921X_MDIO_POLLING_SP= EED_M, (x)) +#define YT921X_MDIO_POLLING_SPEED_10 YT921X_MDIO_POLLING_SPEED(0) +#define YT921X_MDIO_POLLING_SPEED_100 YT921X_MDIO_POLLING_SPEED(1) +#define YT921X_MDIO_POLLING_SPEED_1000 YT921X_MDIO_POLLING_SPEED(2) +#define YT921X_MDIO_POLLING_SPEED_10000 YT921X_MDIO_POLLING_SPEED(3) +#define YT921X_MDIO_POLLING_SPEED_2500 YT921X_MDIO_POLLING_SPEED(4) +#define YT921X_SENSOR 0x8036c +#define YT921X_SENSOR_TEMP BIT(18) +#define YT921X_TEMP 0x80374 +#define YT921X_CHIP_MODE 0x80388 +#define YT921X_CHIP_MODE_MODE GENMASK(1, 0) +#define YT921X_XMII_CTRL 0x80394 +#define YT921X_XMII_CTRL_PORTn(port) BIT(9 - (port)) /* Yes, it's rever= sed */ +#define YT921X_XMIIn(port) (0x80400 + 8 * ((port) - 8)) +#define YT921X_XMII_MODE_M GENMASK(31, 29) +#define YT921X_XMII_MODE(x) FIELD_PREP(YT921X_XMII_MODE_M, (x)) +#define YT921X_XMII_MODE_MII YT921X_XMII_MODE(0) +#define YT921X_XMII_MODE_REVMII YT921X_XMII_MODE(1) +#define YT921X_XMII_MODE_RMII YT921X_XMII_MODE(2) +#define YT921X_XMII_MODE_REVRMII YT921X_XMII_MODE(3) +#define YT921X_XMII_MODE_RGMII YT921X_XMII_MODE(4) +#define YT921X_XMII_MODE_DISABLE YT921X_XMII_MODE(5) +#define YT921X_XMII_LINK BIT(19) /* force link */ +#define YT921X_XMII_EN BIT(18) +#define YT921X_XMII_SOFT_RST BIT(17) +#define YT921X_XMII_RGMII_TX_DELAY_150PS_M GENMASK(16, 13) +#define YT921X_XMII_RGMII_TX_DELAY_150PS(x) FIELD_PREP(YT921X_XMII_RGMI= I_TX_DELAY_150PS_M, (x)) +#define YT921X_XMII_TX_CLK_IN BIT(11) +#define YT921X_XMII_RX_CLK_IN BIT(10) +#define YT921X_XMII_RGMII_TX_DELAY_2NS BIT(8) +#define YT921X_XMII_RGMII_TX_CLK_OUT BIT(7) +#define YT921X_XMII_RGMII_RX_DELAY_150PS_M GENMASK(6, 3) +#define YT921X_XMII_RGMII_RX_DELAY_150PS(x) FIELD_PREP(YT921X_XMII_RGMI= I_RX_DELAY_150PS_M, (x)) +#define YT921X_XMII_RMII_PHY_TX_CLK_OUT BIT(2) +#define YT921X_XMII_REVMII_TX_CLK_OUT BIT(1) +#define YT921X_XMII_REVMII_RX_CLK_OUT BIT(0) + +#define YT921X_MACn_FRAME(port) (0x81008 + 0x1000 * (port)) +#define YT921X_MAC_FRAME_SIZE_M GENMASK(21, 8) +#define YT921X_MAC_FRAME_SIZE(x) FIELD_PREP(YT921X_MAC_FRAME_SIZE_M, (= x)) + +#define YT921X_EEEn_VAL(port) (0xa0000 + 0x40 * (port)) +#define YT921X_EEE_VAL_DATA BIT(1) + +#define YT921X_EEE_CTRL 0xb0000 +#define YT921X_EEE_CTRL_ENn(port) BIT(port) + +#define YT921X_MIB_CTRL 0xc0004 +#define YT921X_MIB_CTRL_CLEAN BIT(30) +#define YT921X_MIB_CTRL_PORT_M GENMASK(6, 3) +#define YT921X_MIB_CTRL_PORT(x) FIELD_PREP(YT921X_MIB_CTRL_PORT_M, (x)) +#define YT921X_MIB_CTRL_ONE_PORT BIT(1) +#define YT921X_MIB_CTRL_ALL_PORT BIT(0) +#define YT921X_MIBn_DATA0(port) (0xc0100 + 0x100 * (port)) +#define YT921X_MIBn_DATAm(port, x) (YT921X_MIBn_DATA0(port) + 4 * (x)) + +#define YT921X_EDATA_CTRL 0xe0000 +#define YT921X_EDATA_CTRL_ADDR_M GENMASK(15, 8) +#define YT921X_EDATA_CTRL_ADDR(x) FIELD_PREP(YT921X_EDATA_CTRL_ADDR_M,= (x)) +#define YT921X_EDATA_CTRL_OP_M GENMASK(3, 0) +#define YT921X_EDATA_CTRL_OP(x) FIELD_PREP(YT921X_EDATA_CTRL_OP_M, (x)) +#define YT921X_EDATA_CTRL_READ YT921X_EDATA_CTRL_OP(5) +#define YT921X_EDATA_DATA 0xe0004 +#define YT921X_EDATA_DATA_DATA_M GENMASK(31, 24) +#define YT921X_EDATA_DATA_STATUS_M GENMASK(3, 0) +#define YT921X_EDATA_DATA_STATUS(x) FIELD_PREP(YT921X_EDATA_DATA_STATU= S_M, (x)) +#define YT921X_EDATA_DATA_IDLE YT921X_EDATA_DATA_STATUS(3) + +#define YT921X_EXT_MBUS_OP 0x6a000 +#define YT921X_INT_MBUS_OP 0xf0000 +#define YT921X_MBUS_OP_START BIT(0) +#define YT921X_EXT_MBUS_CTRL 0x6a004 +#define YT921X_INT_MBUS_CTRL 0xf0004 +#define YT921X_MBUS_CTRL_PORT_M GENMASK(25, 21) +#define YT921X_MBUS_CTRL_PORT(x) FIELD_PREP(YT921X_MBUS_CTRL_PORT_M, (= x)) +#define YT921X_MBUS_CTRL_REG_M GENMASK(20, 16) +#define YT921X_MBUS_CTRL_REG(x) FIELD_PREP(YT921X_MBUS_CTRL_REG_M, (x)) +#define YT921X_MBUS_CTRL_TYPE_M GENMASK(11, 8) /* wild guess */ +#define YT921X_MBUS_CTRL_TYPE(x) FIELD_PREP(YT921X_MBUS_CTRL_TYPE_M, (= x)) +#define YT921X_MBUS_CTRL_TYPE_C22 YT921X_MBUS_CTRL_TYPE(4) +#define YT921X_MBUS_CTRL_OP_M GENMASK(3, 2) /* wild guess */ +#define YT921X_MBUS_CTRL_OP(x) FIELD_PREP(YT921X_MBUS_CTRL_OP_M, (x)) +#define YT921X_MBUS_CTRL_WRITE YT921X_MBUS_CTRL_OP(1) +#define YT921X_MBUS_CTRL_READ YT921X_MBUS_CTRL_OP(2) +#define YT921X_EXT_MBUS_DOUT 0x6a008 +#define YT921X_INT_MBUS_DOUT 0xf0008 +#define YT921X_EXT_MBUS_DIN 0x6a00c +#define YT921X_INT_MBUS_DIN 0xf000c + +#define YT921X_PORTn_EGR(port) (0x100000 + 4 * (port)) +#define YT921X_PORT_EGR_TPID_CTAG_M GENMASK(5, 4) +#define YT921X_PORT_EGR_TPID_CTAG(x) FIELD_PREP(YT921X_PORT_EGR_TPID_C= TAG_M, (x)) +#define YT921X_PORT_EGR_TPID_STAG_M GENMASK(3, 2) +#define YT921X_PORT_EGR_TPID_STAG(x) FIELD_PREP(YT921X_PORT_EGR_TPID_S= TAG_M, (x)) +#define YT921X_TPID_EGRn(x) (0x100300 + 4 * (x)) /* [0, 3] */ +#define YT921X_TPID_EGR_TPID_M GENMASK(15, 0) + +#define YT921X_VLAN_IGR_FILTER 0x180280 +#define YT921X_VLAN_IGR_FILTER_PORTn_BYPASS_IGMP(port) BIT((port) + 11) +#define YT921X_VLAN_IGR_FILTER_PORTn(port) BIT(port) +#define YT921X_PORTn_ISOLATION(port) (0x180294 + 4 * (port)) +#define YT921X_PORT_ISOLATION_BLOCKn(port) BIT(port) +#define YT921X_PORTn_LEARN(port) (0x1803d0 + 4 * (port)) +#define YT921X_PORT_LEARN_VID_LEARN_MULTI_EN BIT(22) +#define YT921X_PORT_LEARN_VID_LEARN_MODE BIT(21) +#define YT921X_PORT_LEARN_VID_LEARN_EN BIT(20) +#define YT921X_PORT_LEARN_SUSPEND_COPY_EN BIT(19) +#define YT921X_PORT_LEARN_SUSPEND_DROP_EN BIT(18) +#define YT921X_PORT_LEARN_DIS BIT(17) +#define YT921X_PORT_LEARN_LIMIT_EN BIT(16) +#define YT921X_PORT_LEARN_LIMIT_M GENMASK(15, 8) +#define YT921X_PORT_LEARN_LIMIT(x) FIELD_PREP(YT921X_PORT_LEARN_LIMIT_= M, (x)) +#define YT921X_PORT_LEARN_DROP_ON_EXCEEDED BIT(2) +#define YT921X_PORT_LEARN_MODE_M GENMASK(1, 0) +#define YT921X_PORT_LEARN_MODE(x) FIELD_PREP(YT921X_PORT_LEARN_MODE_M,= (x)) +#define YT921X_PORT_LEARN_MODE_AUTO YT921X_PORT_LEARN_MODE(0) +#define YT921X_PORT_LEARN_MODE_AUTO_AND_COPY YT921X_PORT_LEARN_MODE(1) +#define YT921X_PORT_LEARN_MODE_CPU_CONTROL YT921X_PORT_LEARN_MODE(2) +#define YT921X_AGEING 0x180440 +#define YT921X_AGEING_INTERVAL_M GENMASK(15, 0) +#define YT921X_FDB_IN0 0x180454 +#define YT921X_FDB_IN1 0x180458 +#define YT921X_FDB_IN2 0x18045c +#define YT921X_FDB_OP 0x180460 +#define YT921X_FDB_OP_INDEX_M GENMASK(22, 11) +#define YT921X_FDB_OP_INDEX(x) FIELD_PREP(YT921X_FDB_OP_INDEX_M, (x)) +#define YT921X_FDB_OP_MODE_INDEX BIT(10) /* mac+fid / index */ +#define YT921X_FDB_OP_FLUSH_MCAST BIT(9) /* ucast / mcast */ +#define YT921X_FDB_OP_FLUSH_M GENMASK(8, 7) +#define YT921X_FDB_OP_FLUSH(x) FIELD_PREP(YT921X_FDB_OP_FLUSH_M, (x)) +#define YT921X_FDB_OP_FLUSH_ALL YT921X_FDB_OP_FLUSH(0) +#define YT921X_FDB_OP_FLUSH_PORT YT921X_FDB_OP_FLUSH(1) +#define YT921X_FDB_OP_FLUSH_PORT_VID YT921X_FDB_OP_FLUSH(2) +#define YT921X_FDB_OP_FLUSH_VID YT921X_FDB_OP_FLUSH(3) +#define YT921X_FDB_OP_FLUSH_STATIC BIT(6) +#define YT921X_FDB_OP_NEXT_TYPE_M GENMASK(5, 4) +#define YT921X_FDB_OP_NEXT_TYPE(x) FIELD_PREP(YT921X_FDB_OP_NEXT_TYPE_= M, (x)) +#define YT921X_FDB_OP_NEXT_TYPE_UCAST_PORT YT921X_FDB_OP_NEXT_TYPE(0) +#define YT921X_FDB_OP_NEXT_TYPE_UCAST_VID YT921X_FDB_OP_NEXT_TYPE(1) +#define YT921X_FDB_OP_NEXT_TYPE_UCAST YT921X_FDB_OP_NEXT_TYPE(2) +#define YT921X_FDB_OP_NEXT_TYPE_MCAST YT921X_FDB_OP_NEXT_TYPE(3) +#define YT921X_FDB_OP_OP_M GENMASK(3, 1) +#define YT921X_FDB_OP_OP(x) FIELD_PREP(YT921X_FDB_OP_OP_M, (x)) +#define YT921X_FDB_OP_OP_ADD YT921X_FDB_OP_OP(0) +#define YT921X_FDB_OP_OP_DEL YT921X_FDB_OP_OP(1) +#define YT921X_FDB_OP_OP_GET_ONE YT921X_FDB_OP_OP(2) +#define YT921X_FDB_OP_OP_GET_NEXT YT921X_FDB_OP_OP(3) +#define YT921X_FDB_OP_OP_FLUSH YT921X_FDB_OP_OP(4) +#define YT921X_FDB_OP_START BIT(0) +#define YT921X_FDB_RESULT 0x180464 +#define YT921X_FDB_RESULT_DONE BIT(15) +#define YT921X_FDB_RESULT_NOTFOUND BIT(14) +#define YT921X_FDB_RESULT_OVERWRITED BIT(13) +#define YT921X_FDB_RESULT_INDEX_M GENMASK(11, 0) +#define YT921X_FDB_RESULT_INDEX(x) FIELD_PREP(YT921X_FDB_RESULT_INDEX_= M, (x)) +#define YT921X_FDB_OUT0 0x1804b0 +#define YT921X_FDB_IO0_ADDR_HI4_M GENMASK(31, 0) +#define YT921X_FDB_OUT1 0x1804b4 +#define YT921X_FDB_IO1_EGR_INT_PRI_EN BIT(31) +#define YT921X_FDB_IO1_STATUS_M GENMASK(30, 28) +#define YT921X_FDB_IO1_STATUS(x) FIELD_PREP(YT921X_FDB_IO1_STATUS_M, (= x)) +#define YT921X_FDB_IO1_STATUS_INVALID YT921X_FDB_IO1_STATUS(0) +#define YT921X_FDB_IO1_STATUS_MIN_TIME YT921X_FDB_IO1_STATUS(1) +#define YT921X_FDB_IO1_STATUS_MOVE_AGING_MAX_TIME YT921X_FDB_IO1_STATUS(= 3) +#define YT921X_FDB_IO1_STATUS_MAX_TIME YT921X_FDB_IO1_STATUS(5) +#define YT921X_FDB_IO1_STATUS_PENDING YT921X_FDB_IO1_STATUS(6) +#define YT921X_FDB_IO1_STATUS_STATIC YT921X_FDB_IO1_STATUS(7) +#define YT921X_FDB_IO1_FID_M GENMASK(27, 16) /* filtering ID (VID) */ +#define YT921X_FDB_IO1_FID(x) FIELD_PREP(YT921X_FDB_IO1_FID_M, (x)) +#define YT921X_FDB_IO1_ADDR_LO2_M GENMASK(15, 0) +#define YT921X_FDB_OUT2 0x1804b8 +#define YT921X_FDB_IO2_MOVE_AGING_STATUS_M GENMASK(31, 30) +#define YT921X_FDB_IO2_IGR_DROP BIT(29) +#define YT921X_FDB_IO2_EGR_PORTS_M GENMASK(28, 18) +#define YT921X_FDB_IO2_EGR_PORTS(x) FIELD_PREP(YT921X_FDB_IO2_EGR_PORT= S_M, (x)) +#define YT921X_FDB_IO2_EGR_DROP BIT(17) +#define YT921X_FDB_IO2_COPY_TO_CPU BIT(16) +#define YT921X_FDB_IO2_IGR_INT_PRI_EN BIT(15) +#define YT921X_FDB_IO2_INT_PRI_M GENMASK(14, 12) +#define YT921X_FDB_IO2_INT_PRI(x) FIELD_PREP(YT921X_FDB_IO2_INT_PRI_M,= (x)) +#define YT921X_FDB_IO2_NEW_VID_M GENMASK(11, 0) +#define YT921X_FDB_IO2_NEW_VID(x) FIELD_PREP(YT921X_FDB_IO2_NEW_VID_M,= (x)) +#define YT921X_FILTER_UNK_UCAST 0x180508 +#define YT921X_FILTER_UNK_MCAST 0x18050c +#define YT921X_FILTER_MCAST 0x180510 +#define YT921X_FILTER_BCAST 0x180514 +#define YT921X_FILTER_PORTS_M GENMASK(10, 0) +#define YT921X_FILTER_PORTS(x) FIELD_PREP(YT921X_FILTER_PORTS_M, (x)) +#define YT921X_FILTER_PORTn(port) BIT(port) +#define YT921X_VLAN_EGR_FILTER 0x180598 +#define YT921X_VLAN_EGR_FILTER_PORTn(port) BIT(port) +#define YT921X_CPU_COPY 0x180690 +#define YT921X_CPU_COPY_FORCE_INT_PORT BIT(2) +#define YT921X_CPU_COPY_TO_INT_CPU BIT(1) +#define YT921X_CPU_COPY_TO_EXT_CPU BIT(0) +#define YT921X_ACT_UNK_UCAST 0x180734 +#define YT921X_ACT_UNK_MCAST 0x180738 +#define YT921X_ACT_UNK_MCAST_BYPASS_DROP_RMA BIT(23) +#define YT921X_ACT_UNK_MCAST_BYPASS_DROP_IGMP BIT(22) +#define YT921X_ACT_UNK_ACTn_M(port) GENMASK(2 * (port) + 1, 2 * (port)) +#define YT921X_ACT_UNK_ACTn(port, x) ((x) << (2 * (port))) +#define YT921X_ACT_UNK_ACTn_FORWARD(port) YT921X_ACT_UNK_ACTn(port, 0) = /* flood */ +#define YT921X_ACT_UNK_ACTn_TRAP(port) YT921X_ACT_UNK_ACTn(port, 1) /*= steer to CPU */ +#define YT921X_ACT_UNK_ACTn_DROP(port) YT921X_ACT_UNK_ACTn(port, 2) /*= discard */ +/* NEVER use this action; see comments in the tag driver */ +#define YT921X_ACT_UNK_ACTn_COPY(port) YT921X_ACT_UNK_ACTn(port, 3) /*= flood and copy */ +#define YT921X_FDB_HW_FLUSH 0x180958 +#define YT921X_FDB_HW_FLUSH_ON_LINKDOWN BIT(0) + +#define YT921X_VLANn_CTRL(vlan) (0x188000 + 8 * (vlan)) +#define YT921X_VLAN_CTRL_UNTAG_PORTS_M GENMASK(50, 40) +#define YT921X_VLAN_CTRL_UNTAG_PORTS(x) FIELD_PREP(YT921X_VLAN_CTRL_UNT= AG_PORTS_M, (x)) +#define YT921X_VLAN_CTRL_UNTAG_PORTn(port) BIT((port) + 40) +#define YT921X_VLAN_CTRL_STP_ID_M GENMASK(39, 36) +#define YT921X_VLAN_CTRL_STP_ID(x) FIELD_PREP(YT921X_VLAN_CTRL_STP_ID_= M, (x)) +#define YT921X_VLAN_CTRL_SVLAN_EN BIT(35) +#define YT921X_VLAN_CTRL_FID_M GENMASK(34, 23) +#define YT921X_VLAN_CTRL_FID(x) FIELD_PREP(YT921X_VLAN_CTRL_FID_M, (x)) +#define YT921X_VLAN_CTRL_LEARN_DIS BIT(22) +#define YT921X_VLAN_CTRL_INT_PRI_EN BIT(21) +#define YT921X_VLAN_CTRL_INT_PRI_M GENMASK(20, 18) +#define YT921X_VLAN_CTRL_PORTS_M GENMASK(17, 7) +#define YT921X_VLAN_CTRL_PORTS(x) FIELD_PREP(YT921X_VLAN_CTRL_PORTS_M,= (x)) +#define YT921X_VLAN_CTRL_PORTn(port) BIT((port) + 7) +#define YT921X_VLAN_CTRL_BYPASS_1X_AC BIT(6) +#define YT921X_VLAN_CTRL_METER_EN BIT(5) +#define YT921X_VLAN_CTRL_METER_ID_M GENMASK(4, 0) + +#define YT921X_TPID_IGRn(x) (0x210000 + 4 * (x)) /* [0, 3] */ +#define YT921X_TPID_IGR_TPID_M GENMASK(15, 0) +#define YT921X_PORTn_IGR_TPID(port) (0x210010 + 4 * (port)) +#define YT921X_PORT_IGR_TPIDn_STAG_M GENMASK(7, 4) +#define YT921X_PORT_IGR_TPIDn_STAG(x) BIT((x) + 4) +#define YT921X_PORT_IGR_TPIDn_CTAG_M GENMASK(3, 0) +#define YT921X_PORT_IGR_TPIDn_CTAG(x) BIT(x) + +#define YT921X_PORTn_VLAN_CTRL(port) (0x230010 + 4 * (port)) +#define YT921X_PORT_VLAN_CTRL_SVLAN_PRI_EN BIT(31) +#define YT921X_PORT_VLAN_CTRL_CVLAN_PRI_EN BIT(30) +#define YT921X_PORT_VLAN_CTRL_SVID_M GENMASK(29, 18) +#define YT921X_PORT_VLAN_CTRL_SVID(x) FIELD_PREP(YT921X_PORT_VLAN_CTRL= _SVID_M, (x)) +#define YT921X_PORT_VLAN_CTRL_CVID_M GENMASK(17, 6) +#define YT921X_PORT_VLAN_CTRL_CVID(x) FIELD_PREP(YT921X_PORT_VLAN_CTRL= _CVID_M, (x)) +#define YT921X_PORT_VLAN_CTRL_SVLAN_PRI_M GENMASK(5, 3) +#define YT921X_PORT_VLAN_CTRL_CVLAN_PRI_M GENMASK(2, 0) +#define YT921X_PORTn_VLAN_CTRL1(port) (0x230080 + 4 * (port)) +#define YT921X_PORT_VLAN_CTRL1_VLAN_RANGE_EN BIT(8) +#define YT921X_PORT_VLAN_CTRL1_VLAN_RANGE_PROFILE_ID_M GENMASK(7, 4) +#define YT921X_PORT_VLAN_CTRL1_SVLAN_DROP_TAGGED BIT(3) +#define YT921X_PORT_VLAN_CTRL1_SVLAN_DROP_UNTAGGED BIT(2) +#define YT921X_PORT_VLAN_CTRL1_CVLAN_DROP_TAGGED BIT(1) +#define YT921X_PORT_VLAN_CTRL1_CVLAN_DROP_UNTAGGED BIT(0) + +#define YT921X_MIRROR 0x300300 +#define YT921X_MIRROR_IGR_PORTS_M GENMASK(26, 16) +#define YT921X_MIRROR_IGR_PORTS(x) FIELD_PREP(YT921X_MIRROR_IGR_PORTS_= M, (x)) +#define YT921X_MIRROR_IGR_PORTn(port) BIT((port) + 16) +#define YT921X_MIRROR_EGR_PORTS_M GENMASK(14, 4) +#define YT921X_MIRROR_EGR_PORTS(x) FIELD_PREP(YT921X_MIRROR_EGR_PORTS_= M, (x)) +#define YT921X_MIRROR_EGR_PORTn(port) BIT((port) + 4) +#define YT921X_MIRROR_PORT_M GENMASK(3, 0) +#define YT921X_MIRROR_PORT(x) FIELD_PREP(YT921X_MIRROR_PORT_M, (x)) + +#define YT921X_EDATA_EXTMODE 0xfb +#define YT921X_EDATA_LEN 0x100 + +#define YT921X_FDB_NUM 4096 + +enum yt921x_fdb_entry_status { + YT921X_FDB_ENTRY_STATUS_INVALID =3D 0, + YT921X_FDB_ENTRY_STATUS_MIN_TIME =3D 1, + YT921X_FDB_ENTRY_STATUS_MOVE_AGING_MAX_TIME =3D 3, + YT921X_FDB_ENTRY_STATUS_MAX_TIME =3D 5, + YT921X_FDB_ENTRY_STATUS_PENDING =3D 6, + YT921X_FDB_ENTRY_STATUS_STATIC =3D 7, +}; + +#define YT9215_MAJOR 0x9002 +#define YT9218_MAJOR 0x9001 + +/* required for a hard reset */ +#define YT921X_RST_DELAY_US 10000 + +#define YT921X_FRAME_SIZE_MAX 0x2400 /* 9216 */ + +#define YT921X_TAG_LEN 8 + +/* 8 internal + 2 external + 1 mcu */ +#define YT921X_PORT_NUM 11 + +#define yt921x_port_is_internal(port) ((port) < 8) +#define yt921x_port_is_external(port) (8 <=3D (port) && (port) < 9) + +struct yt921x_mib { + u64 rx_broadcast; + u64 rx_pause; + u64 rx_multicast; + u64 rx_crc_errors; + + u64 rx_alignment_errors; + u64 rx_undersize_errors; + u64 rx_fragment_errors; + u64 rx_64byte; + + u64 rx_65_127byte; + u64 rx_128_255byte; + u64 rx_256_511byte; + u64 rx_512_1023byte; + + u64 rx_1024_1518byte; + u64 rx_jumbo; + u64 rx_good_bytes; + + u64 rx_bad_bytes; + u64 rx_oversize_errors; + + u64 rx_dropped; + u64 tx_broadcast; + u64 tx_pause; + u64 tx_multicast; + + u64 tx_undersize_errors; + u64 tx_64byte; + u64 tx_65_127byte; + u64 tx_128_255byte; + + u64 tx_256_511byte; + u64 tx_512_1023byte; + u64 tx_1024_1518byte; + u64 tx_jumbo; + + u64 tx_good_bytes; + u64 tx_collisions; + + u64 tx_aborted_errors; + u64 tx_multiple_collisions; + u64 tx_single_collisions; + u64 tx_good; + + u64 tx_deferred; + u64 tx_late_collisions; + u64 rx_oam; + u64 tx_oam; +}; + +struct yt921x_port { + unsigned char index; + + bool hairpin; + bool isolated; + + struct delayed_work mib_read; + struct yt921x_mib mib; + u64 rx_frames; + u64 tx_frames; +}; + +struct yt921x_reg_ops { + int (*read)(void *context, u32 reg, u32 *valp); + int (*write)(void *context, u32 reg, u32 val); +}; + +struct yt921x_priv { + struct dsa_switch ds; + + const struct yt921x_info *info; + /* cache of dsa_cpu_ports(ds) */ + u16 cpu_ports_mask; + + /* protect the access to the switch registers */ + struct mutex reg_lock; + const struct yt921x_reg_ops *reg_ops; + void *reg_ctx; + + /* mdio master bus */ + struct mii_bus *mbus_int; + struct mii_bus *mbus_ext; + + struct yt921x_port ports[YT921X_PORT_NUM]; + + u16 eee_ports_mask; +}; + +#endif --=20 2.51.0