From nobody Mon Feb 9 01:02:11 2026 Received: from mail-wm1-f68.google.com (mail-wm1-f68.google.com [209.85.128.68]) (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 EBF563C1985 for ; Tue, 3 Feb 2026 16:19:21 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.68 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770135563; cv=none; b=E/5/4ZI48DONUaLik4OaKvNZAy5DPUeikSNYJamWeUt+4XnSPgYJtbp0zv4P/gBSy+Ryh8ZtXNBzhTjCdvE7kZmADBEI4wD/LoSkwnS78ZZ1U2JSAGQgJrqyZ0jk43xFfaKUzTUklMg111ibZzJPgi9KigFnU9GN4UVLzyn5z2Q= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770135563; c=relaxed/simple; bh=HzBu2NbGKcOQwsBSue+2sXgdaUYPXA/eSDYmPrHSRrc=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=sLx6YDk5A0Q1aHfoIZyK7/34w5mXOK+fg9wUHlDCCugMTcY6uaTyQpqE0XxEOlIk9+Q2QCJoMzdgv+JSpvncFzIF1CQR/DJuCLGtVT0iPv6CKAwGeEi0HL57v2k7ii3C2tr1s4elwnSS+SyCDdGT/7Lpg4ozbe/jiZGMSJwBkss= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linaro.org; spf=pass smtp.mailfrom=linaro.org; dkim=pass (2048-bit key) header.d=linaro.org header.i=@linaro.org header.b=h4Rt6+Rs; arc=none smtp.client-ip=209.85.128.68 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linaro.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linaro.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=linaro.org header.i=@linaro.org header.b="h4Rt6+Rs" Received: by mail-wm1-f68.google.com with SMTP id 5b1f17b1804b1-4806e0f6b69so42808825e9.3 for ; Tue, 03 Feb 2026 08:19:21 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1770135560; x=1770740360; 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=j3Z4Yd2fRXfRsRi8a3YRl2niiXC4DwI7ABHuLbV15ig=; b=h4Rt6+RsjtNKfurCPZcgm8jMatIpGD/b/WzNU3lr1gs5EztHor5LYpiE4YB9Eg8T6J Mf56lEsFZMihGcPZ4edGVn74syYMKbgFS2NpEYpLaxC7s/Sn5nXFZjShm+E/wAY4I+1H 1Pr1Dgpyyaqm1+se3QvcENj8RekbnO/tpb8bFqGtOITMuDzSQ69RoOMrE8LW9J6o+i7k dEG/6rzkhrIvAmsQOyX96rUYCKod0Z3kqrZjpOrxZCo/iniw8P10fP4lvrg9WzYTmsmI fmnLNJyNipkOHOHdNSWTCt2UDL4kFiy/xJ+Tuju8NyZIkfe5I4ZEEo9tSwYcnk/6oSIP 4Wfg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1770135560; x=1770740360; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=j3Z4Yd2fRXfRsRi8a3YRl2niiXC4DwI7ABHuLbV15ig=; b=gXEu/zg9LXsi9lRHqZCUq+j7owS8zCTghYkMkgbeRMm5yhDC96GJhoTDiTGcbe9cr4 QdRWuv6qXAHvlxd914vCfiWjYp+By/lJue93a278E+zR585GSAC2XJ8vGpZe9M5pjRev sTbR7lZ+11G3fZjxgaCtm/eRs+/UvXiDfFO49+XOSi3HqQxCGUPq6njmS+gUGvjNPgmu V2zPnleoZOoY74hF9kSU9O24fTDoM75vW9DHm8qc6M+pacBkvlJRH0Vh+TrP32UvT+BG rU4MYdPqvJxlZgv/LwX4MLPMFAU2fI105eA3+QGQINZLAr2CewNwfZJeg1PDWU+xJCPK V5tA== X-Forwarded-Encrypted: i=1; AJvYcCVNbWfvjeRzNEE+GTVD5egzmFTgQFW5bDcetEoK8CaFawk52zE6WGay0y+fc5MWVSE0s3WY+XFzGnoaqHo=@vger.kernel.org X-Gm-Message-State: AOJu0YxyzEjbAtkT3z4NWknpLxI/Ajv7a4dfdIuXBKyWxj1Zk42d9NsD yB27mZ8Q5mfB7FbzAmhnNwv5QHHH1QiLyngDEVPohtvWOWb5wx1AERFsUIorXcqYxcU= X-Gm-Gg: AZuq6aIVwBPPkxYcOf/vCt8tiEz4tbSB7BKPR79SySnKapkE6W5ACOTihUDBsI7wFFO 5E9jzcQwzHOr1igy+TLsi/EaG6H3huuBI5pQeKITJghdN3fzPhy/5X93iiPwLcaa6evRP8+I7rd bLakUUUoMeEnADxogjYFAbwGWBWwcHUtcBFdJmjEV7MePez/0y4El/kzSs14VhXOUAM1Zshk3cY GO+LOBaYgPxCRc9CPjXVG+xZGQgp2DqPddqpgvwrE1la33Ou7lUxx4bwf4/zFfB28Iw0VIdBE2y kt6hsGB0QKadJvN0O7MFFmEZs1EEe+XPBriopT6+sQGLk8pann9mjcXdb9CSfG+xASSkSYPwHJA h107nRs1z2OF1znqOLkRCFBql19iWTQbP32KTEUvhF6YDdNRlfiYoxlOVa0e6UzT3KwfwQUrC+w iV3Vwbg76fN8AfVX+yFkM= X-Received: by 2002:a05:600c:8489:b0:483:ea6:8767 with SMTP id 5b1f17b1804b1-4830ea687b8mr681395e9.36.1770135560329; Tue, 03 Feb 2026 08:19:20 -0800 (PST) Received: from vingu-cube.. ([2a01:e0a:f:6020:81d1:b874:c1dc:42e5]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-482dbd21f5dsm144120785e9.8.2026.02.03.08.19.18 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 03 Feb 2026 08:19:19 -0800 (PST) From: Vincent Guittot To: vkoul@kernel.org, neil.armstrong@linaro.org, krzk+dt@kernel.org, conor+dt@kernel.org, ciprianmarian.costea@oss.nxp.com, s32@nxp.com, p.zabel@pengutronix.de, linux@armlinux.org.uk, ghennadi.procopciuc@nxp.com, Ionut.Vicovan@nxp.com, linux-phy@lists.infradead.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, netdev@vger.kernel.org, horms@kernel.org Cc: Frank.li@nxp.com Subject: [PATCH 1/4 v2] dt-bindings: serdes: s32g: Add NXP serdes subsystem Date: Tue, 3 Feb 2026 17:19:14 +0100 Message-ID: <20260203161917.1666696-2-vincent.guittot@linaro.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260203161917.1666696-1-vincent.guittot@linaro.org> References: <20260203161917.1666696-1-vincent.guittot@linaro.org> 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" Describe the serdes subsystem available on the S32G platforms. Signed-off-by: Vincent Guittot --- .../bindings/phy/nxp,s32g-serdes.yaml | 154 ++++++++++++++++++ 1 file changed, 154 insertions(+) create mode 100644 Documentation/devicetree/bindings/phy/nxp,s32g-serdes.y= aml diff --git a/Documentation/devicetree/bindings/phy/nxp,s32g-serdes.yaml b/D= ocumentation/devicetree/bindings/phy/nxp,s32g-serdes.yaml new file mode 100644 index 000000000000..fad34bee2a4f --- /dev/null +++ b/Documentation/devicetree/bindings/phy/nxp,s32g-serdes.yaml @@ -0,0 +1,154 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/nxp,s32g-serdes.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NXP S32G2xxx/S32G3xxx SerDes PHY subsystem + +maintainers: + - Ghennadi Procopciuc + +description: | + The SerDes subsystem on S32G SoC Family includes two types of PHYs: + - One PCIe PHY: Supports various PCIe operation modes + - Two Ethernet Physical Coding Sublayer (XPCS) controllers + + SerDes operation mode selects the enabled PHYs and speeds. Clock frequen= cy + must be adapted accordingly. Below table describes all possible operation + modes. + + Mode PCIe XPCS0 XPCS1 PHY clock Description + SGMII SGMII (MHz) + ------------------------------------------------------------------------- + 0 Gen3 N/A N/A 100 Single PCIe + 1 Gen2 1.25Gbps N/A 100 PCIe/SGMII + 2 Gen2 N/A 1.25Gbps 100 PCIe/SGMII + 3 N/A 1.25Gbps 1.25Gbps 100,125 SGMII + 4 N/A 3.125/1.25Gbps 3.125/1.25Gbps 125 SGMII + 5 Gen2 N/A 3.125Gbps 100 PCIe/SGMII + +properties: + compatible: + oneOf: + - enum: + - nxp,s32g2-serdes + - items: + - const: nxp,s32g3-serdes + - const: nxp,s32g2-serdes + + reg: + maxItems: 4 + + reg-names: + items: + - const: ss_pcie + - const: pcie_phy + - const: xpcs0 + - const: xpcs1 + + clocks: + minItems: 4 + maxItems: 5 + + clock-names: + items: + - const: axi + - const: aux + - const: apb + - const: ref + - const: ext + minItems: 4 + + resets: + maxItems: 2 + + reset-names: + items: + - const: serdes + - const: pcie + + nxp,sys-mode: + $ref: /schemas/types.yaml#/definitions/uint32 + description: | + SerDes operational mode. See above table for possible values. + + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + +patternProperties: + '^serdes[0,1]_lane@[0,1]$': + description: + Describe a serdes lane. + type: object + + properties: + compatible: + enum: + - nxp,s32g2-serdes-pcie-phy + - nxp,s32g2-serdes-xpcs + + reg: + maxItems: 1 + + '#phy-cells': + const: 0 + + required: + - reg + - compatible + + unevaluatedProperties: false + +required: + - compatible + - reg + - reg-names + - clocks + - clock-names + - resets + - reset-names + - nxp,sys-mode + - '#address-cells' + - '#size-cells' + +additionalProperties: false + +examples: + - | + bus { + #address-cells =3D <2>; + #size-cells =3D <2>; + serdes0: serdes@40480000 { + compatible =3D "nxp,s32g3-serdes", "nxp,s32g2-serdes"; + reg =3D <0x0 0x40480000 0x0 0x108>, + <0x0 0x40483008 0x0 0x10>, + <0x0 0x40482000 0x0 0x800>, + <0x0 0x40482800 0x0 0x800>; + reg-names =3D "ss_pcie", "pcie_phy", "xpcs0", "xpcs1"; + clocks =3D <&clks 1>, + <&clks 2>, + <&clks 3>, + <&clks 4>, + <&serdes_100_ext>; + clock-names =3D "axi", "aux", "apb", "ref", "ext"; + resets =3D <&reset 9>, + <&reset 8>; + reset-names =3D "serdes", "pcie"; + nxp,sys-mode =3D <1>; + #address-cells =3D <1>; + #size-cells =3D <0>; + phy_pcie0: serdes0_lane@0 { + compatible =3D "nxp,s32g2-serdes-pcie-phy"; + #phy-cells =3D <0>; + reg =3D <0>; + }; + phy_xpcs0_0: serdes0_lane@1 { + compatible =3D "nxp,s32g2-serdes-xpcs"; + reg =3D <0>; + }; + }; + }; --=20 2.43.0 From nobody Mon Feb 9 01:02:11 2026 Received: from mail-wm1-f45.google.com (mail-wm1-f45.google.com [209.85.128.45]) (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 59CBC3D3006 for ; Tue, 3 Feb 2026 16:19:24 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.45 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770135566; cv=none; b=qX7Lo/L3IU6BQm7Lowt2Xc3tLsJlUuIxtqqLroc128M4j/f6GUPqpYzpjj4tLHW0qCPbl+6hV7eL1v8b8CwSds4Dnu4IrhuTqabtLYGJt9qg4DxbBCIS2l6cdNdpPoJc3g0wUA1myTi9le1cX/aqSiULvLvC7ZICGxL6iPjMBAg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770135566; c=relaxed/simple; bh=nqUbNXxpO98Rzb7BnzJgmK6NB2w6MSkQyH4/cQM1d2k=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=PLA1vBcAWMr2LsbbL9aukwWZmKGtTnhyxhJr7O8uNDpmixMeMMtrFH9QAnRWR2+5HA7EBaGYJ+17OYksymH87LTRADLL+VISYBfu+7OTI+qy1JduC8E58b7TmnH7hvVtu1cBY07DNyypllQWZt+AvtyJUg0JIbiilQyAT0jYwGg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linaro.org; spf=pass smtp.mailfrom=linaro.org; dkim=pass (2048-bit key) header.d=linaro.org header.i=@linaro.org header.b=w1qFnfQj; arc=none smtp.client-ip=209.85.128.45 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linaro.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linaro.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=linaro.org header.i=@linaro.org header.b="w1qFnfQj" Received: by mail-wm1-f45.google.com with SMTP id 5b1f17b1804b1-48069a48629so60404995e9.0 for ; Tue, 03 Feb 2026 08:19:24 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1770135563; x=1770740363; 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=yMWMFGgY8hFgLjdTl2Yu6amxW1e7tik9P3AEAeibj40=; b=w1qFnfQjTaYvdNso1+yqj6b9EUZh8ffmcBjYZv7mrIbRs1B9J6Fg5quU9tUGVGg9B/ T0AMHtpYUM+rWHyr2ybciBXYx9lPacRAOrYfy5yqvJGusENVY8os0YMPGm8bospRHVup FrxUSC69yM2k4wxZkECnSoONuds5zwdW7mNYY+fnU+y3mh6wX7/93mmCqgAiWQeAnS0y ew5BER+ZVrN6eOTYXLOYzNtz2O9qepLj43n10CGr4IiWbCQ8XbLsDJnyvM1cWojBtptM jJ1Lq+n1kKn6iNjuGHczqRlmMpOtf4W2QpN8poLyVRn8RDeXv5VGVcPxsAScd9Vxpv5S FiuA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1770135563; x=1770740363; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=yMWMFGgY8hFgLjdTl2Yu6amxW1e7tik9P3AEAeibj40=; b=RFTjKOcHUhk/MMivjE9siF3J5YGCDn+PAcO2GNq3gFI9xPHA8X5YlL/PdFR/4qG7Xq 2OZl/jE6hb1vDnJaNX7tl4jDWzwV9Rh6NKCX7IBUo9vZrP1ICemVsUM3zNA24Qrf8oCQ A9D1v1liLDZ+/KoNZka6aYuirXFmhSnd6qV5/1rRZQIvNCN0UQ1RQdMS6p2I7/8ahoYo U1N5PWW/lQDs1WqxBixeOP+s+VI4+K01fOHkj3iE1ByeAOT5dmnKpoRUYI4WSLxKkJew jO83/SB2xHjfjMWtODaZ4tLKx2roxRhx9usJNUZ2cwjtiOoo1RA9wPxsDjC9e/bz8DfJ Y5NA== X-Forwarded-Encrypted: i=1; AJvYcCUnI2pQN+8OGVPZ7GhsINLW75MVEIy1oN0UxwnewZuR17rRCANzqS5sN2pCRa4OrRWhWm6QKKNKJVMpy9M=@vger.kernel.org X-Gm-Message-State: AOJu0YywvCuNU5J++xwxxEHTOXdOMbv872klCnRvYDCy8lBfhxoz2Ars E1HQ/3wDsaAaYTvy4P1XX5lcCRq6OCaa5Ai+2a0qsAT6yoQcfECav4sEjmZRzzqvAcA= X-Gm-Gg: AZuq6aLMUtydm5MCTNCIsI6J+GMpMJJetIB0cG+fbGcROCkaEkGuU1O9Px2iHS3ZOJI K5XVRGQmHB/J/dXv4qvcbqjSEvqudwJZ8i3DgxRiPHASHYCgWWhOqY+NdcYxhMiJzRauWojArjw dkrFQp/3ffU/ZcISTS0y1kW+sqsBYxkJHzW4DYdIVExIAEBPgl8beX2rKV8mszo/6IQ23QCWBWu fxydfLyV4q/xYHwHobnQgwIFViOKis0ndgW+Jwcwc7+vVbq3swgsEQjxaul0aEsDTe6SHXsn51W zc8UGDefTTe4pOrYC/eATAyCGW7fB++tpvaRQ3xQllIXT7meYjPAgVRwRIqXdX/LjXAdYUnCy7+ xILMRNyIvJnH9JkNAmiSh/kX3Zb3D/XU78v2H4gCMf+QDAPS4LKBh619Q6BTjFW94yRRRlCnVbM QUp0WIGN+1AEijxlUbmCeOl92RmZdKmw== X-Received: by 2002:a05:600c:1e8b:b0:479:1ac2:f9b8 with SMTP id 5b1f17b1804b1-4830e9794a8mr1995565e9.21.1770135562397; Tue, 03 Feb 2026 08:19:22 -0800 (PST) Received: from vingu-cube.. ([2a01:e0a:f:6020:81d1:b874:c1dc:42e5]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-482dbd21f5dsm144120785e9.8.2026.02.03.08.19.20 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 03 Feb 2026 08:19:20 -0800 (PST) From: Vincent Guittot To: vkoul@kernel.org, neil.armstrong@linaro.org, krzk+dt@kernel.org, conor+dt@kernel.org, ciprianmarian.costea@oss.nxp.com, s32@nxp.com, p.zabel@pengutronix.de, linux@armlinux.org.uk, ghennadi.procopciuc@nxp.com, Ionut.Vicovan@nxp.com, linux-phy@lists.infradead.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, netdev@vger.kernel.org, horms@kernel.org Cc: Frank.li@nxp.com Subject: [PATCH 2/4 v2] phy: s32g: Add serdes subsystem phy Date: Tue, 3 Feb 2026 17:19:15 +0100 Message-ID: <20260203161917.1666696-3-vincent.guittot@linaro.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260203161917.1666696-1-vincent.guittot@linaro.org> References: <20260203161917.1666696-1-vincent.guittot@linaro.org> 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" s32g SoC family includes 2 serdes subsystems which are made of one PCIe controller, 2 XPCS and one Phy. The Phy got 2 lanes that can be configure to output PCIe lanes and/or SGMII. Implement PCIe phy support Co-developed-by: Ciprian Marian Costea Signed-off-by: Ciprian Marian Costea Co-developed-by: Alexandru-Catalin Ionita Signed-off-by: Alexandru-Catalin Ionita Co-developed-by: Ghennadi Procopciuc Signed-off-by: Ghennadi Procopciuc Co-developed-by: Ionut Vicovan Signed-off-by: Ionut Vicovan Co-developed-by: Bogdan Roman Signed-off-by: Bogdan Roman Signed-off-by: Vincent Guittot --- drivers/phy/freescale/Kconfig | 9 + drivers/phy/freescale/Makefile | 1 + drivers/phy/freescale/phy-nxp-s32g-serdes.c | 583 ++++++++++++++++++++ 3 files changed, 593 insertions(+) create mode 100644 drivers/phy/freescale/phy-nxp-s32g-serdes.c diff --git a/drivers/phy/freescale/Kconfig b/drivers/phy/freescale/Kconfig index 81f53564ee15..45184a3cdd69 100644 --- a/drivers/phy/freescale/Kconfig +++ b/drivers/phy/freescale/Kconfig @@ -61,3 +61,12 @@ config PHY_FSL_LYNX_28G found on NXP's Layerscape platforms such as LX2160A. Used to change the protocol running on SerDes lanes at runtime. Only useful for a restricted set of Ethernet protocols. + +config PHY_S32G_SERDES + tristate "NXP S32G SERDES support" + depends on ARCH_S32 || COMPILE_TEST + select GENERIC_PHY + help + This option enables support for S23G SerDes PHY used for + PCIe & Ethernet + diff --git a/drivers/phy/freescale/Makefile b/drivers/phy/freescale/Makefile index 658eac7d0a62..86d948417252 100644 --- a/drivers/phy/freescale/Makefile +++ b/drivers/phy/freescale/Makefile @@ -6,3 +6,4 @@ obj-$(CONFIG_PHY_FSL_IMX8M_PCIE) +=3D phy-fsl-imx8m-pcie.o obj-$(CONFIG_PHY_FSL_IMX8QM_HSIO) +=3D phy-fsl-imx8qm-hsio.o obj-$(CONFIG_PHY_FSL_LYNX_28G) +=3D phy-fsl-lynx-28g.o obj-$(CONFIG_PHY_FSL_SAMSUNG_HDMI_PHY) +=3D phy-fsl-samsung-hdmi.o +obj-$(CONFIG_PHY_S32G_SERDES) +=3D phy-nxp-s32g-serdes.o diff --git a/drivers/phy/freescale/phy-nxp-s32g-serdes.c b/drivers/phy/free= scale/phy-nxp-s32g-serdes.c new file mode 100644 index 000000000000..321a80c02be5 --- /dev/null +++ b/drivers/phy/freescale/phy-nxp-s32g-serdes.c @@ -0,0 +1,583 @@ +// SPDX-License-Identifier: GPL-2.0 +/** + * SerDes driver for S32G SoCs + * + * Copyright 2021-2026 NXP + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define S32G_SERDES_MODE_MAX 5 + +#define EXTERNAL_CLK_NAME "ext" +#define INTERNAL_CLK_NAME "ref" + +/* Serdes Sub system registers */ + +#define S32G_PCIE_PHY_GEN_CTRL 0x0 +#define REF_USE_PAD BIT(17) +#define RX_SRIS_MODE BIT(9) + +#define S32G_PCIE_PHY_MPLLA_CTRL 0x10 +#define MPLL_STATE BIT(30) + +#define S32G_SS_RW_REG_0 0xF0 +#define SUBMODE_MASK GENMASK(3, 0) +#define CLKEN_MASK BIT(23) +#define PHY0_CR_PARA_SEL BIT(9) + +/* PCIe phy subsystem registers */ + +#define S32G_PHY_REG_ADDR 0x0 +#define PHY_REG_EN BIT(31) + +#define S32G_PHY_REG_DATA 0x4 + +#define RAWLANE0_DIG_PCS_XF_RX_EQ_DELTA_IQ_OVRD_IN 0x3019 +#define RAWLANE1_DIG_PCS_XF_RX_EQ_DELTA_IQ_OVRD_IN 0x3119 + +/* + * Until now, there is no generic way to describe and set PCIe clock mode. + * PCIe controller uses the default CRNS =3D 0 mode. + */ +enum pcie_phy_mode { + CRNS =3D 0, /* Common Reference Clock, No Spread Spectrum */ + CRSS =3D 1, /* Common Reference Clock, Spread Spectrum */ + SRNS =3D 2, /* Separate Reference Clock, No Spread Spectrum */ + SRIS =3D 3 /* Separate Reference Clock, Spread Spectrum */ +}; + +struct s32g_serdes_ctrl { + void __iomem *ss_base; + struct reset_control *rst; + struct clk_bulk_data *clks; + int nclks; + u32 ss_mode; + unsigned long ref_clk_rate; + bool ext_clk; +}; + +struct s32g_pcie_ctrl { + void __iomem *phy_base; + struct reset_control *rst; + struct phy *phy; + enum pcie_phy_mode phy_mode; + bool powered_on; +}; + +struct s32g_serdes { + struct s32g_serdes_ctrl ctrl; + struct s32g_pcie_ctrl pcie; + struct device *dev; +}; + +/* PCIe phy subsystem */ + +#define S32G_SERDES_PCIE_FREQ (100 * HZ_PER_MHZ) + +static int s32g_pcie_check_clk(struct s32g_serdes *serdes) +{ + struct s32g_serdes_ctrl *sctrl =3D &serdes->ctrl; + unsigned long rate =3D sctrl->ref_clk_rate; + + if (rate !=3D S32G_SERDES_PCIE_FREQ) { + dev_err(serdes->dev, "PCIe PHY cannot operate at %lu HZ\n", rate); + return -EINVAL; + } + + return 0; +} + +static bool s32g_pcie_phy_is_locked(struct s32g_serdes *serdes) +{ + u32 mplla =3D readl(serdes->ctrl.ss_base + S32G_PCIE_PHY_MPLLA_CTRL); + const u32 mask =3D MPLL_STATE; + + return (mplla & mask) =3D=3D mask; +} + +/* Serdes RFM says between 3.4 and 5.2 ms depending of pll */ +#define S32G_SERDES_LOCK_TIMEOUT_MS 6 + +static int s32g_pcie_phy_power_on_common(struct s32g_serdes *serdes) +{ + struct s32g_serdes_ctrl *sctrl =3D &serdes->ctrl; + struct s32g_pcie_ctrl *pcie =3D &serdes->pcie; + u32 reg; + int val, ret =3D 0; + + ret =3D s32g_pcie_check_clk(serdes); + if (ret) + return ret; + + reg =3D readl(sctrl->ss_base + S32G_PCIE_PHY_GEN_CTRL); + + /* if PCIE PHY is in SRIS mode */ + if (pcie->phy_mode =3D=3D SRIS) + reg |=3D RX_SRIS_MODE; + + if (sctrl->ext_clk) + reg |=3D REF_USE_PAD; + else + reg &=3D ~REF_USE_PAD; + + writel(reg, sctrl->ss_base + S32G_PCIE_PHY_GEN_CTRL); + + /* Monitor Serdes MPLL state */ + ret =3D read_poll_timeout(s32g_pcie_phy_is_locked, val, + (val), + 0, + S32G_SERDES_LOCK_TIMEOUT_MS, false, serdes); + if (ret) { + dev_err(serdes->dev, "Failed to lock PCIE phy\n"); + return -ETIMEDOUT; + } + + /* Set PHY register access to CR interface */ + reg =3D readl(sctrl->ss_base + S32G_SS_RW_REG_0); + reg |=3D PHY0_CR_PARA_SEL; + writel(reg, sctrl->ss_base + S32G_SS_RW_REG_0); + + return ret; +} + +static void s32g_pcie_phy_write(struct s32g_serdes *serdes, u32 reg, u32 v= al) +{ + writel(PHY_REG_EN, serdes->pcie.phy_base + S32G_PHY_REG_ADDR); + writel(reg | PHY_REG_EN, serdes->pcie.phy_base + S32G_PHY_REG_ADDR); + usleep_range(100, 110); + writel(val, serdes->pcie.phy_base + S32G_PHY_REG_DATA); + usleep_range(100, 110); + writel(0, serdes->pcie.phy_base + S32G_PHY_REG_ADDR); +} + +static int s32g_pcie_phy_power_on(struct s32g_serdes *serdes) +{ + struct s32g_pcie_ctrl *pcie =3D &serdes->pcie; + struct s32g_serdes_ctrl *ctrl =3D &serdes->ctrl; + u32 iq_ovrd_in; + int ret =3D 0; + + ret =3D s32g_pcie_phy_power_on_common(serdes); + if (ret) + return ret; + + /* RX_EQ_DELTA_IQ_OVRD enable and override value for PCIe lanes */ + iq_ovrd_in =3D RAWLANE0_DIG_PCS_XF_RX_EQ_DELTA_IQ_OVRD_IN; + + s32g_pcie_phy_write(serdes, iq_ovrd_in, 0x3); + s32g_pcie_phy_write(serdes, iq_ovrd_in, 0x13); + + if (ctrl->ss_mode =3D=3D 0) { + iq_ovrd_in =3D RAWLANE1_DIG_PCS_XF_RX_EQ_DELTA_IQ_OVRD_IN; + + s32g_pcie_phy_write(serdes, iq_ovrd_in, 0x3); + s32g_pcie_phy_write(serdes, iq_ovrd_in, 0x13); + } + + pcie->powered_on =3D true; + + return 0; +} + +/* PCIe phy ops function */ + +static int s32g_serdes_phy_power_on(struct phy *p) +{ + struct s32g_serdes *serdes =3D phy_get_drvdata(p); + + return s32g_pcie_phy_power_on(serdes); +} + +static int s32g_serdes_phy_power_off(struct phy *p) +{ + struct s32g_serdes *serdes =3D phy_get_drvdata(p); + + serdes->pcie.powered_on =3D false; + + return 0; +} + +static inline bool is_pcie_phy_mode_valid(int mode) +{ + switch (mode) { + case CRNS: + case CRSS: + case SRNS: + case SRIS: + return true; + default: + return false; + } +} + +static int s32g_serdes_phy_set_mode_ext(struct phy *p, + enum phy_mode mode, int submode) +{ + struct s32g_serdes *serdes =3D phy_get_drvdata(p); + + if (mode !=3D PHY_MODE_PCIE) + return -EINVAL; + + if (!is_pcie_phy_mode_valid(submode)) + return -EINVAL; + + /* + * Do not configure SRIS or CRSS PHY MODE in conjunction + * with any SGMII mode on the same SerDes subsystem + */ + if ((submode =3D=3D CRSS || submode =3D=3D SRIS) && + serdes->ctrl.ss_mode !=3D 0) + return -EINVAL; + + /* + * Internal reference clock cannot be used with either Common clock + * or Spread spectrum, leaving only SRNSS + */ + if (submode !=3D SRNS && !serdes->ctrl.ext_clk) + return -EINVAL; + + if (serdes->pcie.powered_on) + dev_warn(serdes->dev, "The phy is already powered on.\n"); + + serdes->pcie.phy_mode =3D submode; + + return 0; +} + +static const struct phy_ops serdes_pcie_ops =3D { + .power_on =3D s32g_serdes_phy_power_on, + .power_off =3D s32g_serdes_phy_power_off, + .set_mode =3D s32g_serdes_phy_set_mode_ext, +}; + +static struct phy *s32g_serdes_phy_xlate(struct device *dev, + const struct of_phandle_args *args) +{ + struct s32g_serdes *serdes; + struct phy *phy; + + serdes =3D dev_get_drvdata(dev); + if (!serdes) + return ERR_PTR(-EINVAL); + + phy =3D serdes->pcie.phy; + + return phy; +} + +/* Serdes subsystem */ + +static int s32g_serdes_assert_reset(struct s32g_serdes *serdes) +{ + struct device *dev =3D serdes->dev; + int ret; + + ret =3D reset_control_assert(serdes->pcie.rst); + if (ret) { + dev_err(dev, "Failed to assert PCIE reset: %d\n", ret); + return ret; + } + + ret =3D reset_control_assert(serdes->ctrl.rst); + if (ret) { + dev_err(dev, "Failed to assert SerDes reset: %d\n", ret); + return ret; + } + + return 0; +} + +static int s32g_serdes_deassert_reset(struct s32g_serdes *serdes) +{ + struct device *dev =3D serdes->dev; + int ret; + + ret =3D reset_control_deassert(serdes->pcie.rst); + if (ret) { + dev_err(dev, "Failed to assert PCIE reset: %d\n", ret); + return ret; + } + + ret =3D reset_control_deassert(serdes->ctrl.rst); + if (ret) { + dev_err(dev, "Failed to assert SerDes reset: %d\n", ret); + return ret; + } + + return ret; +} + +static int s32g_serdes_init(struct s32g_serdes *serdes) +{ + struct s32g_serdes_ctrl *ctrl =3D &serdes->ctrl; + u32 reg0; + int ret; + + ret =3D clk_bulk_prepare_enable(ctrl->nclks, ctrl->clks); + if (ret) { + dev_err(serdes->dev, "Failed to enable SerDes clocks\n"); + return ret; + } + + ret =3D s32g_serdes_assert_reset(serdes); + if (ret) + goto disable_clks; + + /* Set serdes mode */ + reg0 =3D readl(ctrl->ss_base + S32G_SS_RW_REG_0); + reg0 &=3D ~SUBMODE_MASK; + if (ctrl->ss_mode =3D=3D 5) + reg0 |=3D 2; + else + reg0 |=3D ctrl->ss_mode; + writel(reg0, ctrl->ss_base + S32G_SS_RW_REG_0); + + /* Set Clock source: internal or external */ + reg0 =3D readl(ctrl->ss_base + S32G_SS_RW_REG_0); + if (ctrl->ext_clk) + reg0 &=3D ~CLKEN_MASK; + else + reg0 |=3D CLKEN_MASK; + + writel(reg0, ctrl->ss_base + S32G_SS_RW_REG_0); + + /* Wait for the selection of working mode (as per the manual specs) */ + usleep_range(100, 110); + + ret =3D s32g_serdes_deassert_reset(serdes); + if (ret) + goto disable_clks; + + dev_info(serdes->dev, "Using mode %d for SerDes subsystem\n", + ctrl->ss_mode); + + return 0; + +disable_clks: + clk_bulk_disable_unprepare(serdes->ctrl.nclks, + serdes->ctrl.clks); + + return ret; +} + +static int s32g_serdes_get_ctrl_resources(struct platform_device *pdev, st= ruct s32g_serdes *serdes) +{ + struct s32g_serdes_ctrl *ctrl =3D &serdes->ctrl; + struct device *dev =3D &pdev->dev; + int ret, idx; + + ret =3D of_property_read_u32(dev->of_node, "nxp,sys-mode", + &ctrl->ss_mode); + if (ret) { + dev_err(dev, "Failed to get SerDes subsystem mode\n"); + return -EINVAL; + } + + if (ctrl->ss_mode > S32G_SERDES_MODE_MAX) { + dev_err(dev, "Invalid SerDes subsystem mode %u\n", + ctrl->ss_mode); + return -EINVAL; + } + + ctrl->ss_base =3D devm_platform_ioremap_resource_byname(pdev, "ss_pcie"); + if (IS_ERR(ctrl->ss_base)) { + dev_err(dev, "Failed to map 'ss_pcie'\n"); + return PTR_ERR(ctrl->ss_base); + } + + ctrl->rst =3D devm_reset_control_get_exclusive(dev, "serdes"); + if (IS_ERR(ctrl->rst)) + return dev_err_probe(dev, PTR_ERR(ctrl->rst), + "Failed to get 'serdes' reset control\n"); + + ctrl->nclks =3D devm_clk_bulk_get_all(dev, &ctrl->clks); + if (ctrl->nclks < 1) { + ret =3D ctrl->nclks ? : -EINVAL; + return dev_err_probe(dev, ret, + "Failed to get SerDes clocks\n"); + } + + idx =3D of_property_match_string(dev->of_node, "clock-names", EXTERNAL_CL= K_NAME); + if (idx < 0) + idx =3D of_property_match_string(dev->of_node, "clock-names", INTERNAL_C= LK_NAME); + else + ctrl->ext_clk =3D true; + + if (idx < 0) { + dev_err(dev, "Failed to get Phy reference clock source\n"); + return -EINVAL; + } + + ctrl->ref_clk_rate =3D clk_get_rate(ctrl->clks[idx].clk); + if (!ctrl->ref_clk_rate) { + dev_err(dev, "Failed to get Phy reference clock rate\n"); + return -EINVAL; + } + + return 0; +} + +static int s32g_serdes_get_pcie_resources(struct platform_device *pdev, st= ruct s32g_serdes *serdes) +{ + struct s32g_pcie_ctrl *pcie =3D &serdes->pcie; + struct device *dev =3D &pdev->dev; + + pcie->phy_base =3D devm_platform_ioremap_resource_byname(pdev, + "pcie_phy"); + if (IS_ERR(pcie->phy_base)) { + dev_err(dev, "Failed to map 'pcie_phy'\n"); + return PTR_ERR(pcie->phy_base); + } + + pcie->rst =3D devm_reset_control_get_exclusive(dev, "pcie"); + if (IS_ERR(pcie->rst)) + return dev_err_probe(dev, IS_ERR(pcie->rst), + "Failed to get 'pcie' reset control\n"); + + return 0; +} + +static int s32g2_serdes_create_phy(struct s32g_serdes *serdes, struct devi= ce_node *child_node) +{ + struct s32g_serdes_ctrl *ctrl =3D &serdes->ctrl; + struct phy_provider *phy_provider; + struct device *dev =3D serdes->dev; + int ss_mode =3D ctrl->ss_mode; + struct phy *phy; + + if (of_device_is_compatible(child_node, "nxp,s32g2-serdes-pcie-phy")) { + /* no PCIe phy lane */ + if (ss_mode > 2) + return 0; + + phy =3D devm_phy_create(dev, child_node, &serdes_pcie_ops); + if (IS_ERR(phy)) + return PTR_ERR(phy); + + phy_set_drvdata(phy, serdes); + + phy->attrs.mode =3D PHY_MODE_PCIE; + phy->id =3D 0; + serdes->pcie.phy =3D phy; + + phy_provider =3D devm_of_phy_provider_register(&phy->dev, s32g_serdes_ph= y_xlate); + if (IS_ERR(phy_provider)) + return PTR_ERR(phy_provider); + + } else { + dev_warn(dev, "Skipping unknown child node %pOFn\n", child_node); + } + + return 0; +} + +static int s32g_serdes_parse_lanes(struct device *dev, struct s32g_serdes = *serdes) +{ + int ret; + + for_each_available_child_of_node_scoped(dev->of_node, of_port) { + ret =3D s32g2_serdes_create_phy(serdes, of_port); + if (ret) + return ret; + } + + return 0; +} + +static int s32g_serdes_probe(struct platform_device *pdev) +{ + struct s32g_serdes *serdes; + struct device *dev =3D &pdev->dev; + int ret; + + serdes =3D devm_kzalloc(dev, sizeof(*serdes), GFP_KERNEL); + if (!serdes) + return -ENOMEM; + + platform_set_drvdata(pdev, serdes); + serdes->dev =3D dev; + + ret =3D s32g_serdes_get_ctrl_resources(pdev, serdes); + if (ret) + return ret; + + ret =3D s32g_serdes_get_pcie_resources(pdev, serdes); + if (ret) + return ret; + + ret =3D s32g_serdes_parse_lanes(dev, serdes); + if (ret) + return ret; + + return s32g_serdes_init(serdes); +} + +static int __maybe_unused s32g_serdes_suspend(struct device *device) +{ + struct s32g_serdes *serdes =3D dev_get_drvdata(device); + + clk_bulk_disable_unprepare(serdes->ctrl.nclks, serdes->ctrl.clks); + + return 0; +} + +static int __maybe_unused s32g_serdes_resume(struct device *device) +{ + struct s32g_serdes *serdes =3D dev_get_drvdata(device); + struct s32g_pcie_ctrl *pcie =3D &serdes->pcie; + int ret; + + ret =3D s32g_serdes_init(serdes); + if (ret) { + dev_err(device, "Failed to initialize\n"); + return ret; + } + + /* Restore PCIe phy power */ + if (pcie->powered_on) { + ret =3D s32g_pcie_phy_power_on(serdes); + if (ret) + dev_err(device, "Failed to power-on PCIe phy\n"); + } + + return ret; +} + +static const struct of_device_id s32g_serdes_match[] =3D { + { + .compatible =3D "nxp,s32g2-serdes", + }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, s32g_serdes_match); + +static const struct dev_pm_ops s32g_serdes_pm_ops =3D { + NOIRQ_SYSTEM_SLEEP_PM_OPS(s32g_serdes_suspend, + s32g_serdes_resume) +}; + +static struct platform_driver s32g_serdes_driver =3D { + .probe =3D s32g_serdes_probe, + .driver =3D { + .name =3D "phy-s32g-serdes", + .of_match_table =3D s32g_serdes_match, + .pm =3D &s32g_serdes_pm_ops, + }, +}; +module_platform_driver(s32g_serdes_driver); + +MODULE_AUTHOR("Ghennadi Procopciuc "); +MODULE_DESCRIPTION("S32CC SerDes driver"); +MODULE_LICENSE("GPL"); --=20 2.43.0 From nobody Mon Feb 9 01:02:11 2026 Received: from mail-wm1-f43.google.com (mail-wm1-f43.google.com [209.85.128.43]) (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 511063D3300 for ; Tue, 3 Feb 2026 16:19:26 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.43 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770135570; cv=none; b=vDA3Nj6j2NGcqO9eKs1q7CNQnpALaDCj0ZeuSF+QLL6iDZtvR+bDg4nW6Q2uHkyjyzu+8zt6wa3yOo+vez2COdyjLxc7Y5u9rO6Y26/A9utPvjknoPCRJ1amzAylmTpR1T6QR72firmhHVjoZh5S5B7UUbbWQ3OpxYPOhtBZAo8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770135570; c=relaxed/simple; bh=ZQbYMt/urwbjfEqsKR25v+W+m2VsUcljpKeOrhzfNHU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=rV3jPeeMe9wRRIec3tnyQhcnVKBvrWjJv9zzotgKGnYPwsq4mfGUBNtjSWx72qJNWn2Zpgyv83fkpq1voyx46tr5AYBElMDX2kAbVPdSMgBZnQLvG8ZvnWExo1v5n1Buj9kZ7w1aGAymwby6xTcVCu2uf/wDe40fa3/PSLEM7I0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linaro.org; spf=pass smtp.mailfrom=linaro.org; dkim=pass (2048-bit key) header.d=linaro.org header.i=@linaro.org header.b=yI3TRWxw; arc=none smtp.client-ip=209.85.128.43 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linaro.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linaro.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=linaro.org header.i=@linaro.org header.b="yI3TRWxw" Received: by mail-wm1-f43.google.com with SMTP id 5b1f17b1804b1-47edd6111b4so61925125e9.1 for ; Tue, 03 Feb 2026 08:19:26 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1770135565; x=1770740365; 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=Ld5W7O5+DZjm6MAnTZUl+YMvXmBb7jCpcTxIrIErWHI=; b=yI3TRWxwv9Vvg1Ey9v7f+qNigyooKnTefgMO38lzTC5PChigQ2igLOHXBQky8jkIDU bwnL+PMtnb0iwmrIR77kNB6YI2DDUHv3A4/en85rb7/r0gG29NlEq6G4Ckwm7BGndD0q co/Li18VGZVkJKYHJDXmifHHkPcEmTdSMCjn4MLZG+TGnSXXLmceUKzcY81dW81gjIlN mzdGfa4EjLpuQScEKvEKkkcfDUIIM2+fQswFBgpP9tEIh5fysTISMlXa/wuHt1tXD+Nc i/nCyH1zcjGf8S549sQZ2q0FKcwszELoBjYxrH+3Areio96B4QBuEvSWim34dQAjexv9 gJCw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1770135565; x=1770740365; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=Ld5W7O5+DZjm6MAnTZUl+YMvXmBb7jCpcTxIrIErWHI=; b=pCiv+VWPiCosO8ykYnX2KCEhbM3UfxVKADf3STsymyDGrV7soxu7wM+LbxZ5XBegO3 15nnAmIro+SOn20jRZFyInmOjnqdcJlspzYW5+255yQs8hHUhpUun/FHz9Pe0uK/erYd maanrBnljQgUgRn3XGOOGqgkOgMl81+JxcmBwIIFB7rk0Hne7R0c7TESCqaCizQS8acf EhZ8aY/wFEUK8GsZlpvfHPaU+VP9+Uui1jyOjNRGYA5oLoIPz2SEqZy3p/67Hp1aT1FN A/gMeGny+moTkyAKc3GDrSF/zf3W7RJzn5rt6hXxuvM6jsBW5pcnu1UfZVyiyJ4Z+KWK qBYw== X-Forwarded-Encrypted: i=1; AJvYcCVPZ167zh/khdmpKntWPHdwR76wPsO0rZI2tR2hOEOfLoGJRzIfTwQJhEMxrHRzvJTzk4n7HXc/GBTuOUQ=@vger.kernel.org X-Gm-Message-State: AOJu0Yzgvg3dgetFOCCN/kNkOinnXxwZbWzJ/bgvBIS4CiDEDLgvlAHP 3GY/YdhbtIu41Rh+/ldMyaYdakUVRhbY3cfYI7mzP63pO6Y9s6Io30u0dHxv4bkM3LA= X-Gm-Gg: AZuq6aIsHwjJFDeMRwI7GE/0cKzGv3YWRKauFRjirZYaSjELxy8LMPOqSr0rJ9lZexY ZIlIFonN/dA7lzGYmRMFsOxglpTCKSmGm1VOMDSdvGRbYmqCr+MYVKnweKbkjvl6L5wpYwfbS5W ouaGnQ8xqKvlr6ZueB6rYswUclVGhLgo4k5a+0m/QsLMMmsESR5Xx2lIyqHNlzEq+t0rJXcSa7e 9xFMSePUOJ5UrgaeRmqd39MPkCjUjMTJPLRr36Brd0G2ij8Fr1Cia4vRA4tTl7l1oMgDpxLWKzZ mB5i1acjqP5sPbq9GHvpVAnlZvocl+8jY/LgnRnOK4nJv6Un5h1UX7GujW7E+dhwgC/x9qcLBQg yGB5x8xFFzqpUOn/P+oippgATNSBtpnVqw6M0g2ARfMY3/08r6TXZ6gDSlRszYDBx9HRAQKlFfe RSGhkEaIVpEHnWn9N8RUGeU6tvETWseA== X-Received: by 2002:a05:600c:4513:b0:47d:6856:9bd9 with SMTP id 5b1f17b1804b1-4830e97ae1emr2031415e9.23.1770135564315; Tue, 03 Feb 2026 08:19:24 -0800 (PST) Received: from vingu-cube.. ([2a01:e0a:f:6020:81d1:b874:c1dc:42e5]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-482dbd21f5dsm144120785e9.8.2026.02.03.08.19.22 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 03 Feb 2026 08:19:22 -0800 (PST) From: Vincent Guittot To: vkoul@kernel.org, neil.armstrong@linaro.org, krzk+dt@kernel.org, conor+dt@kernel.org, ciprianmarian.costea@oss.nxp.com, s32@nxp.com, p.zabel@pengutronix.de, linux@armlinux.org.uk, ghennadi.procopciuc@nxp.com, Ionut.Vicovan@nxp.com, linux-phy@lists.infradead.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, netdev@vger.kernel.org, horms@kernel.org Cc: Frank.li@nxp.com Subject: [PATCH 3/4 v2] phy: s32g: Add serdes xpcs subsystem Date: Tue, 3 Feb 2026 17:19:16 +0100 Message-ID: <20260203161917.1666696-4-vincent.guittot@linaro.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260203161917.1666696-1-vincent.guittot@linaro.org> References: <20260203161917.1666696-1-vincent.guittot@linaro.org> 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" s32g SoC family includes 2 serdes subsystems which are made of one PCIe controller, 2 XPCS and one Phy. The Phy got 2 lanes that can be configure to output PCIe lanes and/or SGMII. Add XPCS and SGMII support. Co-developed-by: Ciprian Marian Costea Signed-off-by: Ciprian Marian Costea Co-developed-by: Alexandru-Catalin Ionita Signed-off-by: Alexandru-Catalin Ionita Co-developed-by: Ghennadi Procopciuc Signed-off-by: Ghennadi Procopciuc Co-developed-by: Ionut Vicovan Signed-off-by: Ionut Vicovan Co-developed-by: Bogdan Roman Signed-off-by: Bogdan Roman Signed-off-by: Vincent Guittot --- drivers/net/pcs/Makefile | 1 + drivers/net/pcs/pcs-nxp-s32g-xpcs.c | 1006 +++++++++++++++++++ drivers/phy/freescale/Kconfig | 1 + drivers/phy/freescale/phy-nxp-s32g-serdes.c | 374 ++++++- include/linux/pcs/pcs-nxp-s32g-xpcs.h | 50 + 5 files changed, 1430 insertions(+), 2 deletions(-) create mode 100644 drivers/net/pcs/pcs-nxp-s32g-xpcs.c create mode 100644 include/linux/pcs/pcs-nxp-s32g-xpcs.h diff --git a/drivers/net/pcs/Makefile b/drivers/net/pcs/Makefile index 4f7920618b90..55107cbaa6d5 100644 --- a/drivers/net/pcs/Makefile +++ b/drivers/net/pcs/Makefile @@ -8,3 +8,4 @@ obj-$(CONFIG_PCS_XPCS) +=3D pcs_xpcs.o obj-$(CONFIG_PCS_LYNX) +=3D pcs-lynx.o obj-$(CONFIG_PCS_MTK_LYNXI) +=3D pcs-mtk-lynxi.o obj-$(CONFIG_PCS_RZN1_MIIC) +=3D pcs-rzn1-miic.o +obj-$(CONFIG_PHY_S32G_SERDES) +=3D pcs-nxp-s32g-xpcs.o diff --git a/drivers/net/pcs/pcs-nxp-s32g-xpcs.c b/drivers/net/pcs/pcs-nxp-= s32g-xpcs.c new file mode 100644 index 000000000000..5636440ed38d --- /dev/null +++ b/drivers/net/pcs/pcs-nxp-s32g-xpcs.c @@ -0,0 +1,1006 @@ +// SPDX-License-Identifier: GPL-2.0 +/** + * Copyright 2021-2026 NXP + */ + +#include +#include +#include +#include +#include +#include +#include + +#define XPCS_TIMEOUT_MS 300 + +#define ADDR1_OFS 0x3FC + +#define SR_MII_CTRL 0x1F0000 +#define SR_RST BIT(15) +#define SS13 BIT(13) +#define AN_ENABLE BIT(12) +#define RESTART_AN BIT(9) +#define DUPLEX_MODE BIT(8) +#define SS6 BIT(6) +#define SR_MII_STS 0x1F0001 +#define LINK_STS BIT(2) +#define AN_ABL BIT(3) +#define AN_CMPL BIT(5) +#define SR_MII_DEV_ID1 0x1F0002 +#define SR_MII_DEV_ID2 0x1F0003 +#define SR_MII_EXT_STS 0x1F000F +#define CAP_1G_T_FD BIT(13) +#define CAP_1G_T_HD BIT(12) +#define VR_MII_DIG_CTRL1 0x1F8000 +#define BYP_PWRUP BIT(1) +#define EN_2_5G_MODE BIT(2) +#define CL37_TMR_OVRRIDE BIT(3) +#define INIT BIT(8) +#define MAC_AUTO_SW BIT(9) +#define CS_EN BIT(10) +#define PWRSV BIT(11) +#define EN_VSMMD1 BIT(13) +#define R2TLBE BIT(14) +#define VR_RST BIT(15) +#define VR_MII_AN_CTRL 0x1F8001 +#define MII_AN_INTR_EN BIT(0) +#define PCS_MODE_MASK GENMASK(2, 1) +#define PCS_MODE_SGMII 2 +#define MII_CTRL BIT(8) +#define VR_MII_AN_INTR_STS 0x1F8002 +#define CL37_ANCMPLT_INTR BIT(0) +#define CL37_ANSGM_STS_DUPLEX BIT(1) +#define CL37_ANSGM_STS_SPEED_MASK GENMASK(3, 2) +#define CL37_ANSGM_10MBPS 0 +#define CL37_ANSGM_100MBPS 1 +#define CL37_ANSGM_1000MBPS 2 +#define CL37_ANSGM_STS_LINK BIT(4) +#define VR_MII_DBG_CTRL 0x1F8005 +#define SUPPRESS_LOS_DET BIT(4) +#define RX_DT_EN_CTL BIT(6) +#define VR_MII_LINK_TIMER_CTRL 0x1F800A +#define VR_MII_DIG_STS 0x1F8010 +#define PSEQ_STATE_MASK GENMASK(4, 2) +#define POWER_GOOD_STATE 0x4 +#define VR_MII_GEN5_12G_16G_TX_GENCTRL1 0x1F8031 +#define VBOOST_EN_0 BIT(4) +#define TX_CLK_RDY_0 BIT(12) +#define VR_MII_GEN5_12G_16G_TX_GENCTRL2 0x1F8032 +#define TX_REQ_0 BIT(0) +#define VR_MII_GEN5_12G_16G_TX_RATE_CTRL 0x1F8034 +#define TX0_RATE_MASK GENMASK(2, 0) +#define TX0_BAUD_DIV_1 0 +#define TX0_BAUD_DIV_4 2 +#define VR_MII_GEN5_12G_16G_TX_EQ_CTRL0 0x1F8036 +#define TX_EQ_MAIN_MASK GENMASK(13, 8) +#define VR_MII_GEN5_12G_16G_TX_EQ_CTRL1 0x1F8037 +#define TX_EQ_OVR_RIDE BIT(6) +#define VR_MII_CONSUMER_10G_TX_TERM_CTRL 0x1F803C +#define TX0_TERM_MASK GENMASK(2, 0) +#define VR_MII_GEN5_12G_16G_RX_GENCTRL1 0x1F8051 +#define RX_RST_0 BIT(4) +#define VR_MII_GEN5_12G_16G_RX_GENCTRL2 0x1F8052 +#define RX_REQ_0 BIT(0) +#define VR_MII_GEN5_12G_16G_RX_RATE_CTRL 0x1F8054 +#define RX0_RATE_MASK GENMASK(1, 0) +#define RX0_BAUD_DIV_2 0x1 +#define RX0_BAUD_DIV_8 0x3 +#define VR_MII_GEN5_12G_16G_CDR_CTRL 0x1F8056 +#define CDR_SSC_EN_0 BIT(4) +#define VCO_LOW_FREQ_0 BIT(8) +#define VR_MII_GEN5_12G_16G_MPLL_CMN_CTRL 0x1F8070 +#define MPLLB_SEL_0 BIT(4) +#define VR_MII_GEN5_12G_16G_MPLLA_CTRL0 0x1F8071 +#define MPLLA_CAL_DISABLE BIT(15) +#define MLLA_MULTIPLIER_MASK GENMASK(7, 0) +#define VR_MII_GEN5_12G_MPLLA_CTRL1 0x1F8072 +#define MPLLA_FRACN_CTRL_MASK GENMASK(15, 5) +#define VR_MII_GEN5_12G_16G_MPLLA_CTRL2 0x1F8073 +#define MPLLA_TX_CLK_DIV_MASK GENMASK(13, 11) +#define MPLLA_DIV10_CLK_EN BIT(9) +#define VR_MII_GEN5_12G_16G_MPLLB_CTRL0 0x1F8074 +#define MPLLB_CAL_DISABLE BIT(15) +#define MLLB_MULTIPLIER_OFF 0 +#define MLLB_MULTIPLIER_MASK GENMASK(7, 0) +#define VR_MII_GEN5_12G_MPLLB_CTRL1 0x1F8075 +#define MPLLB_FRACN_CTRL_MASK GENMASK(15, 5) +#define VR_MII_GEN5_12G_16G_MPLLB_CTRL2 0x1F8076 +#define MPLLB_TX_CLK_DIV_MASK GENMASK(13, 11) +#define MPLLB_DIV10_CLK_EN BIT(9) +#define VR_MII_RX_LSTS 0x1F8020 +#define RX_VALID_0 BIT(12) +#define VR_MII_GEN5_12G_MPLLA_CTRL3 0x1F8077 +#define MPLLA_BANDWIDTH_MASK GENMASK(15, 0) +#define VR_MII_GEN5_12G_MPLLB_CTRL3 0x1F8078 +#define MPLLB_BANDWIDTH_MASK GENMASK(15, 0) +#define VR_MII_GEN5_12G_16G_MISC_CTRL0 0x1F8090 +#define PLL_CTRL BIT(15) +#define VR_MII_GEN5_12G_16G_REF_CLK_CTRL 0x1F8091 +#define REF_CLK_EN BIT(0) +#define REF_USE_PAD BIT(1) +#define REF_CLK_DIV2 BIT(2) +#define REF_RANGE_MASK GENMASK(5, 3) +#define RANGE_26_53_MHZ 0x1 +#define RANGE_52_78_MHZ 0x2 +#define RANGE_78_104_MHZ 0x3 +#define REF_MPLLA_DIV2 BIT(6) +#define REF_MPLLB_DIV2 BIT(7) +#define REF_RPT_CLK_EN BIT(8) +#define VR_MII_GEN5_12G_16G_VCO_CAL_LD0 0x1F8092 +#define VCO_LD_VAL_0_MASK GENMASK(12, 0) +#define VR_MII_GEN5_12G_VCO_CAL_REF0 0x1F8096 +#define VCO_REF_LD_0_MASK GENMASK(5, 0) + +typedef bool (*xpcs_poll_func_t)(struct s32g_xpcs *); + +/* + * XPCS registers can't be accessed directly and an indirect address method + * must be used instead. + */ + +static const struct regmap_range s32g_xpcs_wr_ranges[] =3D { + regmap_reg_range(0x1F0000, 0x1F0000), + regmap_reg_range(0x1F0004, 0x1F0004), + regmap_reg_range(0x1F8000, 0x1F8003), + regmap_reg_range(0x1F8005, 0x1F8005), + regmap_reg_range(0x1F800A, 0x1F800A), + regmap_reg_range(0x1F8012, 0x1F8012), + regmap_reg_range(0x1F8015, 0x1F8015), + regmap_reg_range(0x1F8030, 0x1F8037), + regmap_reg_range(0x1F803C, 0x1F803E), + regmap_reg_range(0x1F8050, 0x1F8058), + regmap_reg_range(0x1F805C, 0x1F805E), + regmap_reg_range(0x1F8064, 0x1F8064), + regmap_reg_range(0x1F806B, 0x1F806B), + regmap_reg_range(0x1F8070, 0x1F8078), + regmap_reg_range(0x1F8090, 0x1F8092), + regmap_reg_range(0x1F8096, 0x1F8096), + regmap_reg_range(0x1F8099, 0x1F8099), + regmap_reg_range(0x1F80A0, 0x1F80A2), + regmap_reg_range(0x1F80E1, 0x1F80E1), +}; + +static const struct regmap_access_table s32g_xpcs_wr_table =3D { + .yes_ranges =3D s32g_xpcs_wr_ranges, + .n_yes_ranges =3D ARRAY_SIZE(s32g_xpcs_wr_ranges), +}; + +static const struct regmap_range s32g_xpcs_rd_ranges[] =3D { + regmap_reg_range(0x1F0000, 0x1F0006), + regmap_reg_range(0x1F000F, 0x1F000F), + regmap_reg_range(0x1F0708, 0x1F0710), + regmap_reg_range(0x1F8000, 0x1F8003), + regmap_reg_range(0x1F8005, 0x1F8005), + regmap_reg_range(0x1F800A, 0x1F800A), + regmap_reg_range(0x1F8010, 0x1F8012), + regmap_reg_range(0x1F8015, 0x1F8015), + regmap_reg_range(0x1F8018, 0x1F8018), + regmap_reg_range(0x1F8020, 0x1F8020), + regmap_reg_range(0x1F8030, 0x1F8037), + regmap_reg_range(0x1F803C, 0x1F803C), + regmap_reg_range(0x1F8040, 0x1F8040), + regmap_reg_range(0x1F8050, 0x1F8058), + regmap_reg_range(0x1F805C, 0x1F805E), + regmap_reg_range(0x1F8060, 0x1F8060), + regmap_reg_range(0x1F8064, 0x1F8064), + regmap_reg_range(0x1F806B, 0x1F806B), + regmap_reg_range(0x1F8070, 0x1F8078), + regmap_reg_range(0x1F8090, 0x1F8092), + regmap_reg_range(0x1F8096, 0x1F8096), + regmap_reg_range(0x1F8098, 0x1F8099), + regmap_reg_range(0x1F80A0, 0x1F80A2), + regmap_reg_range(0x1F80E1, 0x1F80E1), +}; + +static const struct regmap_access_table s32g_xpcs_rd_table =3D { + .yes_ranges =3D s32g_xpcs_rd_ranges, + .n_yes_ranges =3D ARRAY_SIZE(s32g_xpcs_rd_ranges), +}; + +static int s32g_xpcs_regmap_reg_read(void *context, unsigned int reg, + unsigned int *result) +{ + struct s32g_xpcs *xpcs =3D context; + u16 ofsleft =3D (reg >> 8) & 0xffffU; + u16 ofsright =3D (reg & 0xffU); + + writew(ofsleft, xpcs->base + ADDR1_OFS); + *result =3D readw(xpcs->base + (ofsright * 4)); + + return 0; +} + +static int s32g_xpcs_regmap_reg_write(void *context, unsigned int reg, + unsigned int val) +{ + struct s32g_xpcs *xpcs =3D context; + u16 ofsleft =3D (reg >> 8) & 0xffffU; + u16 ofsright =3D (reg & 0xffU); + + writew(ofsleft, xpcs->base + ADDR1_OFS); + writew(val, xpcs->base + (ofsright * 4)); + + return 0; +} + +static const struct regmap_config s32g_xpcs_regmap_config =3D { + .reg_bits =3D 16, + .val_bits =3D 16, + .reg_read =3D s32g_xpcs_regmap_reg_read, + .reg_write =3D s32g_xpcs_regmap_reg_write, + .wr_table =3D &s32g_xpcs_wr_table, + .rd_table =3D &s32g_xpcs_rd_table, + .max_register =3D 0x1F80E1, +}; + +static void s32g_xpcs_write_bits(struct s32g_xpcs *xpcs, unsigned int reg, + unsigned int mask, unsigned int value) +{ + int ret =3D regmap_write_bits(xpcs->regmap, reg, mask, value); + + if (ret) + dev_err(xpcs->dev, "Failed to write bits of XPCS reg: 0x%x\n", reg); +} + +static void s32g_xpcs_write(struct s32g_xpcs *xpcs, unsigned int reg, + unsigned int value) +{ + int ret =3D regmap_write(xpcs->regmap, reg, value); + + if (ret) + dev_err(xpcs->dev, "Failed to write XPCS reg: 0x%x\n", reg); +} + +static unsigned int s32g_xpcs_read(struct s32g_xpcs *xpcs, unsigned int re= g) +{ + unsigned int val =3D 0; + int ret; + + ret =3D regmap_read(xpcs->regmap, reg, &val); + if (ret) + dev_err(xpcs->dev, "Failed to read XPCS reg: 0x%x\n", reg); + + return val; +} + +/* + * Internal XPCS function + */ + +static bool s32g_xpcs_poll_timeout(struct s32g_xpcs *xpcs, xpcs_poll_func_= t func, + ktime_t timeout) +{ + ktime_t cur =3D ktime_get(); + + return func(xpcs) || ktime_after(cur, timeout); +} + +static int s32g_xpcs_wait(struct s32g_xpcs *xpcs, xpcs_poll_func_t func) +{ + ktime_t timeout =3D ktime_add_ms(ktime_get(), XPCS_TIMEOUT_MS); + + spin_until_cond(s32g_xpcs_poll_timeout(xpcs, func, timeout)); + if (!func(xpcs)) + return -ETIMEDOUT; + + return 0; +} + +static int s32g_xpcs_wait_bits(struct s32g_xpcs *xpcs, unsigned int reg, + unsigned int mask, unsigned int bits) +{ + ktime_t cur; + ktime_t timeout =3D ktime_add_ms(ktime_get(), XPCS_TIMEOUT_MS); + + spin_until_cond((cur =3D ktime_get(), + (s32g_xpcs_read(xpcs, reg) & mask) =3D=3D bits || + ktime_after(cur, timeout))); + if ((s32g_xpcs_read(xpcs, reg) & mask) !=3D bits) + return -ETIMEDOUT; + + return 0; +} + +static unsigned int s32g_xpcs_digital_status(struct s32g_xpcs *xpcs) +{ + return s32g_xpcs_read(xpcs, VR_MII_DIG_STS); +} + +static int s32g_xpcs_wait_power_good_state(struct s32g_xpcs *xpcs) +{ + unsigned int val; + + return read_poll_timeout(s32g_xpcs_digital_status, val, + FIELD_GET(PSEQ_STATE_MASK, val) =3D=3D POWER_GOOD_STATE, + 0, + XPCS_TIMEOUT_MS, false, xpcs); +} + +void s32g_xpcs_vreset(struct s32g_xpcs *xpcs) +{ + /* Step 19 */ + s32g_xpcs_write_bits(xpcs, VR_MII_DIG_CTRL1, VR_RST, VR_RST); +} + +static bool s32g_xpcs_is_not_in_reset(struct s32g_xpcs *xpcs) +{ + unsigned int val; + + val =3D s32g_xpcs_read(xpcs, VR_MII_DIG_CTRL1); + + return !(val & VR_RST); +} + +int s32g_xpcs_wait_vreset(struct s32g_xpcs *xpcs) +{ + int ret; + + /* Step 20 */ + ret =3D s32g_xpcs_wait(xpcs, s32g_xpcs_is_not_in_reset); + if (ret) + dev_err(xpcs->dev, "XPCS%d is in reset\n", xpcs->id); + + return ret; +} + +int s32g_xpcs_reset_rx(struct s32g_xpcs *xpcs) +{ + int ret =3D 0; + + ret =3D s32g_xpcs_wait_power_good_state(xpcs); + if (ret) { + dev_err(xpcs->dev, "Failed to enter in PGOOD state after vendor reset\n"= ); + return ret; + } + + /* Step 21 */ + s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_RX_GENCTRL1, + RX_RST_0, RX_RST_0); + + /* Step 22 */ + s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_RX_GENCTRL1, + RX_RST_0, 0); + + /* Step 23 */ + /* Wait until SR_MII_STS[LINK_STS] =3D 1 */ + + return ret; +} + +static int s32g_xpcs_ref_clk_sel(struct s32g_xpcs *xpcs, + enum s32g_xpcs_pll ref_pll) +{ + switch (ref_pll) { + case XPCS_PLLA: + s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_MPLL_CMN_CTRL, + MPLLB_SEL_0, 0); + xpcs->ref =3D XPCS_PLLA; + break; + case XPCS_PLLB: + s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_MPLL_CMN_CTRL, + MPLLB_SEL_0, MPLLB_SEL_0); + xpcs->ref =3D XPCS_PLLB; + break; + default: + return -EINVAL; + } + + return 0; +} + +static void s32g_xpcs_electrical_configure(struct s32g_xpcs *xpcs) +{ + /* Step 2 */ + s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_TX_EQ_CTRL0, + TX_EQ_MAIN_MASK, FIELD_PREP(TX_EQ_MAIN_MASK, 0xC)); + + /* Step 3 */ + s32g_xpcs_write_bits(xpcs, VR_MII_CONSUMER_10G_TX_TERM_CTRL, + TX0_TERM_MASK, FIELD_PREP(TX0_TERM_MASK, 0x4)); +} + +static int s32g_xpcs_vco_cfg(struct s32g_xpcs *xpcs, enum s32g_xpcs_pll vc= o_pll) +{ + unsigned int vco_ld =3D 0; + unsigned int vco_ref =3D 0; + unsigned int rx_baud =3D 0; + unsigned int tx_baud =3D 0; + + switch (vco_pll) { + case XPCS_PLLA: + if (xpcs->mhz125) { + vco_ld =3D FIELD_PREP(VCO_LD_VAL_0_MASK, 1360); + vco_ref =3D FIELD_PREP(VCO_REF_LD_0_MASK, 17); + } else { + vco_ld =3D FIELD_PREP(VCO_LD_VAL_0_MASK, 1350); + vco_ref =3D FIELD_PREP(VCO_REF_LD_0_MASK, 27); + } + + rx_baud =3D FIELD_PREP(RX0_RATE_MASK, RX0_BAUD_DIV_8); + tx_baud =3D FIELD_PREP(TX0_RATE_MASK, TX0_BAUD_DIV_4); + break; + case XPCS_PLLB: + if (xpcs->mhz125) { + vco_ld =3D FIELD_PREP(VCO_LD_VAL_0_MASK, 1350); + vco_ref =3D FIELD_PREP(VCO_REF_LD_0_MASK, 27); + } else { + vco_ld =3D FIELD_PREP(VCO_LD_VAL_0_MASK, 1344); + vco_ref =3D FIELD_PREP(VCO_REF_LD_0_MASK, 43); + } + + rx_baud =3D FIELD_PREP(RX0_RATE_MASK, RX0_BAUD_DIV_2); + tx_baud =3D FIELD_PREP(TX0_RATE_MASK, TX0_BAUD_DIV_1); + break; + default: + return -EINVAL; + } + + s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_VCO_CAL_LD0, + VCO_LD_VAL_0_MASK, vco_ld); + + s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_VCO_CAL_REF0, + VCO_REF_LD_0_MASK, vco_ref); + + s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_TX_RATE_CTRL, + TX0_RATE_MASK, tx_baud); + s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_RX_RATE_CTRL, + RX0_RATE_MASK, rx_baud); + + if (vco_pll =3D=3D XPCS_PLLB) { + s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_CDR_CTRL, + VCO_LOW_FREQ_0, VCO_LOW_FREQ_0); + } else { + s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_CDR_CTRL, + VCO_LOW_FREQ_0, 0); + } + + return 0; +} + +static int s32g_xpcs_init_mplla(struct s32g_xpcs *xpcs) +{ + unsigned int val; + + if (!xpcs) + return -EINVAL; + + /* Step 7 */ + val =3D 0; + if (xpcs->ext_clk) + val |=3D REF_USE_PAD; + + if (xpcs->mhz125) { + val |=3D REF_MPLLA_DIV2; + val |=3D REF_CLK_DIV2; + val |=3D FIELD_PREP(REF_RANGE_MASK, RANGE_52_78_MHZ); + } else { + val |=3D FIELD_PREP(REF_RANGE_MASK, RANGE_26_53_MHZ); + } + + s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_REF_CLK_CTRL, + REF_MPLLA_DIV2 | REF_USE_PAD | REF_RANGE_MASK | + REF_CLK_DIV2, val); + + /* Step 8 */ + if (xpcs->mhz125) + val =3D FIELD_PREP(MLLA_MULTIPLIER_MASK, 80); + else + val =3D FIELD_PREP(MLLA_MULTIPLIER_MASK, 25); + + s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_MPLLA_CTRL0, + MPLLA_CAL_DISABLE | MLLA_MULTIPLIER_MASK, + val); + + /* Step 9 */ + s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_MPLLA_CTRL1, + MPLLA_FRACN_CTRL_MASK, 0); + + /* Step 10 */ + s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_MPLLA_CTRL2, + MPLLA_TX_CLK_DIV_MASK | MPLLA_DIV10_CLK_EN, + FIELD_PREP(MPLLA_TX_CLK_DIV_MASK, 1) | MPLLA_DIV10_CLK_EN); + + /* Step 11 */ + if (xpcs->mhz125) + val =3D FIELD_PREP(MPLLA_BANDWIDTH_MASK, 43); + else + val =3D FIELD_PREP(MPLLA_BANDWIDTH_MASK, 357); + + s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_MPLLA_CTRL3, + MPLLA_BANDWIDTH_MASK, val); + + return 0; +} + +static int s32g_xpcs_init_mpllb(struct s32g_xpcs *xpcs) +{ + unsigned int val; + + if (!xpcs) + return -EINVAL; + + /* Step 7 */ + val =3D 0; + if (xpcs->ext_clk) + val |=3D REF_USE_PAD; + + if (xpcs->mhz125) { + val |=3D REF_MPLLB_DIV2; + val |=3D REF_CLK_DIV2; + val |=3D FIELD_PREP(REF_RANGE_MASK, RANGE_52_78_MHZ); + } else { + val |=3D FIELD_PREP(REF_RANGE_MASK, RANGE_26_53_MHZ); + } + + s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_REF_CLK_CTRL, + REF_MPLLB_DIV2 | REF_USE_PAD | REF_RANGE_MASK | + REF_CLK_DIV2, val); + + /* Step 8 */ + if (xpcs->mhz125) + val =3D 125 << MLLB_MULTIPLIER_OFF; + else + val =3D 39 << MLLB_MULTIPLIER_OFF; + + s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_MPLLB_CTRL0, + MPLLB_CAL_DISABLE | MLLB_MULTIPLIER_MASK, + val); + + /* Step 9 */ + if (xpcs->mhz125) + val =3D FIELD_PREP(MPLLB_FRACN_CTRL_MASK, 0); + else + val =3D FIELD_PREP(MPLLB_FRACN_CTRL_MASK, 1044); + + s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_MPLLB_CTRL1, + MPLLB_FRACN_CTRL_MASK, val); + + /* Step 10 */ + s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_MPLLB_CTRL2, + MPLLB_TX_CLK_DIV_MASK | MPLLB_DIV10_CLK_EN, + FIELD_PREP(MPLLB_TX_CLK_DIV_MASK, 5) | MPLLB_DIV10_CLK_EN); + + /* Step 11 */ + if (xpcs->mhz125) + val =3D FIELD_PREP(MPLLB_BANDWIDTH_MASK, 68); + else + val =3D FIELD_PREP(MPLLB_BANDWIDTH_MASK, 102); + + s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_MPLLB_CTRL3, + MPLLB_BANDWIDTH_MASK, val); + + return 0; +} + +static void s32g_serdes_pma_high_freq_recovery(struct s32g_xpcs *xpcs) +{ + /* PCS signal protection, PLL railout recovery */ + s32g_xpcs_write_bits(xpcs, VR_MII_DBG_CTRL, SUPPRESS_LOS_DET | RX_DT_EN_C= TL, + SUPPRESS_LOS_DET | RX_DT_EN_CTL); + s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_MISC_CTRL0, + PLL_CTRL, PLL_CTRL); +} + +static void s32g_serdes_pma_configure_tx_eq_post(struct s32g_xpcs *xpcs) +{ + s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_TX_EQ_CTRL1, + TX_EQ_OVR_RIDE, TX_EQ_OVR_RIDE); +} + +static int s32g_serdes_bifurcation_pll_transit(struct s32g_xpcs *xpcs, + enum s32g_xpcs_pll target_pll) +{ + int ret =3D 0; + struct device *dev =3D xpcs->dev; + + /* Configure XPCS speed and VCO */ + if (target_pll =3D=3D XPCS_PLLA) { + s32g_xpcs_write_bits(xpcs, VR_MII_DIG_CTRL1, EN_2_5G_MODE, 0); + s32g_xpcs_vco_cfg(xpcs, XPCS_PLLA); + } else { + s32g_xpcs_write_bits(xpcs, VR_MII_DIG_CTRL1, + EN_2_5G_MODE, EN_2_5G_MODE); + s32g_xpcs_vco_cfg(xpcs, XPCS_PLLB); + } + + /* Signal that clock are not available */ + s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_TX_GENCTRL1, + TX_CLK_RDY_0, 0); + + /* Select PLL reference */ + if (target_pll =3D=3D XPCS_PLLA) + s32g_xpcs_ref_clk_sel(xpcs, XPCS_PLLA); + else + s32g_xpcs_ref_clk_sel(xpcs, XPCS_PLLB); + + /* Initiate transmitter TX reconfiguration request */ + s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_TX_GENCTRL2, + TX_REQ_0, TX_REQ_0); + + /* Wait for transmitter to reconfigure */ + ret =3D s32g_xpcs_wait_bits(xpcs, VR_MII_GEN5_12G_16G_TX_GENCTRL2, + TX_REQ_0, 0); + if (ret) { + dev_err(dev, "Switch to TX_REQ_0 failed\n"); + return ret; + } + + /* Initiate transmitter RX reconfiguration request */ + s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_RX_GENCTRL2, + RX_REQ_0, RX_REQ_0); + + /* Wait for receiver to reconfigure */ + ret =3D s32g_xpcs_wait_bits(xpcs, VR_MII_GEN5_12G_16G_RX_GENCTRL2, + RX_REQ_0, 0); + if (ret) { + dev_err(dev, "Switch to RX_REQ_0 failed\n"); + return ret; + } + + /* Signal that clock are available */ + s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_TX_GENCTRL1, + TX_CLK_RDY_0, TX_CLK_RDY_0); + + /* Flush internal logic */ + s32g_xpcs_write_bits(xpcs, VR_MII_DIG_CTRL1, INIT, INIT); + + /* Wait for init */ + ret =3D s32g_xpcs_wait_bits(xpcs, VR_MII_DIG_CTRL1, INIT, 0); + if (ret) { + dev_err(dev, "XPCS INIT failed\n"); + return ret; + } + + return ret; +} + +/* + * phylink_pcs_ops + */ + +static unsigned int s32cc_phylink_pcs_inband_caps(struct phylink_pcs *pcs, + phy_interface_t interface) +{ + switch (interface) { + case PHY_INTERFACE_MODE_SGMII: + return LINK_INBAND_DISABLE | LINK_INBAND_ENABLE; + + default: + return 0; + } +} + +static int s32cc_phylink_pcs_config(struct phylink_pcs *pcs, + unsigned int neg_mode, + phy_interface_t interface, + const unsigned long *advertising, + bool permit_pause_to_mac) +{ + struct s32g_xpcs *xpcs =3D phylink_pcs_to_s32g_xpcs(pcs); + + /* Step 1: Disable SGMII AN */ + s32g_xpcs_write_bits(xpcs, SR_MII_CTRL, AN_ENABLE, 0); + + s32g_xpcs_write_bits(xpcs, VR_MII_AN_CTRL, + MII_AN_INTR_EN, + 0); + + if (!(neg_mode =3D=3D PHYLINK_PCS_NEG_INBAND_ENABLED)) + return 0; + + /* Step 2 */ + s32g_xpcs_write_bits(xpcs, VR_MII_AN_CTRL, + PCS_MODE_MASK, + FIELD_PREP(PCS_MODE_MASK, PCS_MODE_SGMII)); + + /* Step 3 */ + s32g_xpcs_write_bits(xpcs, SR_MII_CTRL, + SS13 | SS6, + SS6); + + /* Step 4 */ + s32g_xpcs_write_bits(xpcs, VR_MII_AN_CTRL, + MII_CTRL, + 0); + /* Step 5 and 8 */ + if (xpcs->pcie_shared =3D=3D PCIE_XPCS_2G5) { + s32g_xpcs_write(xpcs, VR_MII_LINK_TIMER_CTRL, 0x2faf); + s32g_xpcs_write_bits(xpcs, VR_MII_DIG_CTRL1, + MAC_AUTO_SW, MAC_AUTO_SW); + } else { + s32g_xpcs_write(xpcs, VR_MII_LINK_TIMER_CTRL, 0x7a1); + s32g_xpcs_write_bits(xpcs, VR_MII_DIG_CTRL1, MAC_AUTO_SW, 0); + } + + /* Step 6 */ + s32g_xpcs_write_bits(xpcs, VR_MII_DIG_CTRL1, + CL37_TMR_OVRRIDE, CL37_TMR_OVRRIDE); + + /* Step 7 */ + s32g_xpcs_write_bits(xpcs, VR_MII_AN_CTRL, + MII_AN_INTR_EN, + MII_AN_INTR_EN); + + /* Step 9: Enable SGMII AN */ + s32g_xpcs_write_bits(xpcs, SR_MII_CTRL, AN_ENABLE, AN_ENABLE); + + return 0; +} + +static void s32cc_phylink_pcs_get_state(struct phylink_pcs *pcs, unsigned = int neg_mode, + struct phylink_link_state *state) +{ + struct s32g_xpcs *xpcs =3D phylink_pcs_to_s32g_xpcs(pcs); + bool ss6, ss13, an_enabled; + struct device *dev =3D xpcs->dev; + unsigned int val, ss; + + an_enabled =3D (neg_mode =3D=3D PHYLINK_PCS_NEG_INBAND_ENABLED); + + if (an_enabled) { + state->link =3D 0; + val =3D s32g_xpcs_read(xpcs, VR_MII_AN_INTR_STS); + + /* Interrupt is raised with each SGMII AN that is in cases + * Link down - Every SGMII link timer expire + * Link up - Once before link goes up + * So either linkup or raised interrupt mean AN was completed + */ + if ((val & CL37_ANCMPLT_INTR) || (val & CL37_ANSGM_STS_LINK)) { + state->an_complete =3D 1; + if (val & CL37_ANSGM_STS_LINK) + state->link =3D 1; + else + return; + if (val & CL37_ANSGM_STS_DUPLEX) + state->duplex =3D DUPLEX_FULL; + else + state->duplex =3D DUPLEX_HALF; + ss =3D FIELD_GET(CL37_ANSGM_STS_SPEED_MASK, val); + } else { + return; + } + + } else { + val =3D s32g_xpcs_read(xpcs, SR_MII_STS); + state->link =3D !!(val & LINK_STS); + state->an_complete =3D 0; + state->pause =3D MLO_PAUSE_NONE; + + val =3D s32g_xpcs_read(xpcs, SR_MII_CTRL); + if (val & DUPLEX_MODE) + state->duplex =3D DUPLEX_FULL; + else + state->duplex =3D DUPLEX_HALF; + + /* + * Build similar value as CL37_ANSGM_STS_SPEED with + * SS6 and SS13 of SR_MII_CTRL: + * - 0 for 10 Mbps + * - 1 for 100 Mbps + * - 2 for 1000 Mbps + */ + ss6 =3D !!(val & SS6); + ss13 =3D !!(val & SS13); + ss =3D ss6 << 1 | ss13; + } + + switch (ss) { + case CL37_ANSGM_10MBPS: + state->speed =3D SPEED_10; + break; + case CL37_ANSGM_100MBPS: + state->speed =3D SPEED_100; + break; + case CL37_ANSGM_1000MBPS: + state->speed =3D SPEED_1000; + break; + default: + dev_err(dev, "Failed to interpret the value of SR_MII_CTRL\n"); + break; + } + + val =3D s32g_xpcs_read(xpcs, VR_MII_DIG_CTRL1); + if ((val & EN_2_5G_MODE) && state->speed =3D=3D SPEED_1000) + state->speed =3D SPEED_2500; + + /* Cover SGMII AN inability to distigunish between 1G and 2.5G */ + if ((val & EN_2_5G_MODE) && + state->speed !=3D SPEED_2500 && an_enabled) { + dev_err(dev, "Speed not supported in SGMII AN mode\n"); + } +} + +static void s32cc_phylink_pcs_link_up(struct phylink_pcs *pcs, + unsigned int neg_mode, + phy_interface_t interface, int speed, + int duplex) +{ + struct s32g_xpcs *xpcs =3D phylink_pcs_to_s32g_xpcs(pcs); + struct device *dev =3D xpcs->dev; + bool an_enabled =3D (neg_mode =3D=3D PHYLINK_PCS_NEG_INBAND_ENABLED); + unsigned int val; + int ret; + + dev_dbg(dev, "xpcs_%d: speed=3D%u duplex=3D%d an=3D%d\n", xpcs->id, + speed, duplex, an_enabled); + + if (an_enabled) + return; + + s32g_xpcs_write_bits(xpcs, SR_MII_CTRL, AN_ENABLE, 0); + s32g_xpcs_write_bits(xpcs, VR_MII_AN_CTRL, MII_AN_INTR_EN, 0); + s32g_xpcs_write_bits(xpcs, VR_MII_AN_CTRL, MII_CTRL, 0); + + if (duplex =3D=3D DUPLEX_FULL) + val =3D DUPLEX_MODE; + else + val =3D 0; + + s32g_xpcs_write_bits(xpcs, SR_MII_CTRL, DUPLEX_MODE, val); + + switch (speed) { + case SPEED_10: + val =3D 0; + break; + case SPEED_100: + val =3D SS13; + break; + case SPEED_1000: + val =3D SS6; + break; + case SPEED_2500: + val =3D SS6; + break; + default: + dev_err(dev, "Speed not supported\n"); + break; + } + + if (speed =3D=3D SPEED_2500) { + ret =3D s32g_serdes_bifurcation_pll_transit(xpcs, XPCS_PLLB); + if (ret) + dev_err(dev, "Switch to PLLB failed\n"); + } else { + ret =3D s32g_serdes_bifurcation_pll_transit(xpcs, XPCS_PLLA); + if (ret) + dev_err(dev, "Switch to PLLA failed\n"); + } + + s32g_xpcs_write_bits(xpcs, SR_MII_CTRL, SS6 | SS13, val); +} + +static const struct phylink_pcs_ops s32cc_phylink_pcs_ops =3D { + .pcs_inband_caps =3D s32cc_phylink_pcs_inband_caps, + .pcs_get_state =3D s32cc_phylink_pcs_get_state, + .pcs_config =3D s32cc_phylink_pcs_config, + .pcs_link_up =3D s32cc_phylink_pcs_link_up, +}; + +/* + * Serdes functions for initializing/configuring/releasing the xpcs + */ + +int s32g_xpcs_pre_pcie_2g5(struct s32g_xpcs *xpcs) +{ + int ret; + + /* Enable voltage boost */ + s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_TX_GENCTRL1, VBOOST_EN_0, + VBOOST_EN_0); + + /* TX rate baud */ + s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_TX_RATE_CTRL, 0x7, 0x0U); + + /* Rx rate baud/2 */ + s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_RX_RATE_CTRL, 0x3U, 0x1U); + + /* Set low-frequency operating band */ + s32g_xpcs_write_bits(xpcs, VR_MII_GEN5_12G_16G_CDR_CTRL, CDR_SSC_EN_0, + VCO_LOW_FREQ_0); + + ret =3D s32g_serdes_bifurcation_pll_transit(xpcs, XPCS_PLLB); + if (ret) + dev_err(xpcs->dev, "Switch to PLLB failed\n"); + + return ret; +} + +int s32g_xpcs_init_plls(struct s32g_xpcs *xpcs) +{ + int ret; + struct device *dev =3D xpcs->dev; + + if (!xpcs->ext_clk) { + /* Step 1 */ + s32g_xpcs_write_bits(xpcs, VR_MII_DIG_CTRL1, BYP_PWRUP, BYP_PWRUP); + } else if (xpcs->pcie_shared =3D=3D NOT_SHARED) { + ret =3D s32g_xpcs_wait_power_good_state(xpcs); + if (ret) + return ret; + } else if (xpcs->pcie_shared =3D=3D PCIE_XPCS_2G5) { + ret =3D s32g_xpcs_wait_power_good_state(xpcs); + if (ret) + return ret; + /* Configure equalization */ + s32g_serdes_pma_configure_tx_eq_post(xpcs); + s32g_xpcs_electrical_configure(xpcs); + + /* Enable receiver recover */ + s32g_serdes_pma_high_freq_recovery(xpcs); + return 0; + } + + s32g_xpcs_electrical_configure(xpcs); + + s32g_xpcs_ref_clk_sel(xpcs, XPCS_PLLA); + ret =3D s32g_xpcs_init_mplla(xpcs); + if (ret) { + dev_err(dev, "Failed to initialize PLLA\n"); + return ret; + } + ret =3D s32g_xpcs_init_mpllb(xpcs); + if (ret) { + dev_err(dev, "Failed to initialize PLLB\n"); + return ret; + } + s32g_xpcs_vco_cfg(xpcs, XPCS_PLLA); + + /* Step 18 */ + if (!xpcs->ext_clk) + s32g_xpcs_write_bits(xpcs, VR_MII_DIG_CTRL1, BYP_PWRUP, 0); + + /* Will be cleared by Step 19 Vreset */ + s32g_xpcs_write_bits(xpcs, SR_MII_CTRL, AN_ENABLE, 0); + s32g_xpcs_write_bits(xpcs, SR_MII_CTRL, DUPLEX_MODE, DUPLEX_MODE); + + return ret; +} + +void s32g_xpcs_disable_an(struct s32g_xpcs *xpcs) +{ + s32g_xpcs_write_bits(xpcs, SR_MII_CTRL, DUPLEX_MODE, DUPLEX_MODE); + s32g_xpcs_write_bits(xpcs, SR_MII_CTRL, AN_ENABLE, 0); +} + +int s32g_xpcs_init(struct s32g_xpcs *xpcs, struct device *dev, + unsigned char id, void __iomem *base, bool ext_clk, + unsigned long rate, enum s32g_xpcs_shared pcie_shared) +{ + struct regmap_config conf; + + if (rate !=3D (125 * HZ_PER_MHZ) && rate !=3D (100 * HZ_PER_MHZ)) { + dev_err(dev, "XPCS cannot operate @%lu HZ\n", rate); + return -EINVAL; + } + + xpcs->base =3D base; + xpcs->ext_clk =3D ext_clk; + xpcs->id =3D id; + xpcs->dev =3D dev; + xpcs->pcie_shared =3D pcie_shared; + + if (rate =3D=3D (125 * HZ_PER_MHZ)) + xpcs->mhz125 =3D true; + else + xpcs->mhz125 =3D false; + + conf =3D s32g_xpcs_regmap_config; + + if (!id) + conf.name =3D "xpcs0"; + else + conf.name =3D "xpcs1"; + + xpcs->regmap =3D devm_regmap_init(dev, NULL, xpcs, &conf); + if (IS_ERR(xpcs->regmap)) + return dev_err_probe(dev, PTR_ERR(xpcs->regmap), + "Failed to init register amp\n"); + + /* Phylink PCS */ + xpcs->pcs.ops =3D &s32cc_phylink_pcs_ops; + xpcs->pcs.poll =3D true; + __set_bit(PHY_INTERFACE_MODE_SGMII, xpcs->pcs.supported_interfaces); + + return 0; +} diff --git a/drivers/phy/freescale/Kconfig b/drivers/phy/freescale/Kconfig index 45184a3cdd69..bb7f59897faf 100644 --- a/drivers/phy/freescale/Kconfig +++ b/drivers/phy/freescale/Kconfig @@ -66,6 +66,7 @@ config PHY_S32G_SERDES tristate "NXP S32G SERDES support" depends on ARCH_S32 || COMPILE_TEST select GENERIC_PHY + select REGMAP help This option enables support for S23G SerDes PHY used for PCIe & Ethernet diff --git a/drivers/phy/freescale/phy-nxp-s32g-serdes.c b/drivers/phy/free= scale/phy-nxp-s32g-serdes.c index 321a80c02be5..f2f7eb5aa327 100644 --- a/drivers/phy/freescale/phy-nxp-s32g-serdes.c +++ b/drivers/phy/freescale/phy-nxp-s32g-serdes.c @@ -12,12 +12,14 @@ #include #include #include +#include #include #include #include #include #include =20 +#define S32G_SERDES_XPCS_MAX 2 #define S32G_SERDES_MODE_MAX 5 =20 #define EXTERNAL_CLK_NAME "ext" @@ -32,6 +34,52 @@ #define S32G_PCIE_PHY_MPLLA_CTRL 0x10 #define MPLL_STATE BIT(30) =20 +#define S32G_PCIE_PHY_MPLLB_CTRL 0x14 +#define MPLLB_SSC_EN BIT(1) + +#define S32G_PCIE_PHY_EXT_CTRL_SEL 0x18 +#define EXT_PHY_CTRL_SEL BIT(0) + +#define S32G_PCIE_PHY_EXT_BS_CTRL 0x1C +#define EXT_BS_TX_LOWSWING BIT(6) +#define EXT_BS_RX_BIGSWING BIT(5) +#define EXT_BS_RX_LEVEL_MASK GENMASK(4, 0) + +#define S32G_PCIE_PHY_REF_CLK_CTRL 0x20 +#define EXT_REF_RANGE_MASK GENMASK(5, 3) +#define REF_CLK_DIV2_EN BIT(2) +#define REF_CLK_MPLLB_DIV2_EN BIT(1) + +#define S32G_PCIE_PHY_EXT_MPLLA_CTRL_1 0x30 +#define EXT_MPLLA_BANDWIDTH_MASK GENMASK(15, 0) + +#define S32G_PCIE_PHY_EXT_MPLLB_CTRL_1 0x40 +#define EXT_MPLLB_DIV_MULTIPLIER_MASK GENMASK(31, 24) +#define EXT_MPLLB_DIV_CLK_EN BIT(19) +#define EXT_MPLLB_DIV8_CLK_EN BIT(18) +#define EXT_MPLLB_DIV10_CLK_EN BIT(16) +#define EXT_MPLLB_BANDWIDTH_MASK GENMASK(15, 0) + +#define S32G_PCIE_PHY_EXT_MPLLB_CTRL_2 0x44 +#define EXT_MPLLB_FRACN_CTRL_MASK GENMASK(22, 12) +#define MPLLB_MULTIPLIER_MASK GENMASK(8, 0) + +#define S32G_PCIE_PHY_EXT_MPLLB_CTRL_3 0x48 +#define EXT_MPLLB_WORD_DIV2_EN BIT(31) +#define EXT_MPLLB_TX_CLK_DIV_MASK GENMASK(30, 28) + +#define S32G_PCIE_PHY_EXT_MISC_CTRL_1 0xA0 +#define EXT_RX_LOS_THRESHOLD_MASK GENMASK(7, 1) +#define EXT_RX_VREF_CTRL_MASK GENMASK(28, 24) + +#define S32G_PCIE_PHY_EXT_MISC_CTRL_2 0xA4 +#define EXT_TX_VBOOST_LVL_MASK GENMASK(18, 16) +#define EXT_TX_TERM_CTRL_MASK GENMASK(26, 24) + +#define S32G_PCIE_PHY_XPCS1_RX_OVRD_CTRL 0xD0 +#define XPCS1_RX_VCO_LD_VAL_MASK GENMASK(28, 16) +#define XPCS1_RX_REF_LD_VAL_MASK GENMASK(14, 8) + #define S32G_SS_RW_REG_0 0xF0 #define SUBMODE_MASK GENMASK(3, 0) #define CLKEN_MASK BIT(23) @@ -44,6 +92,9 @@ =20 #define S32G_PHY_REG_DATA 0x4 =20 +#define S32G_PHY_RST_CTRL 0x8 +#define WARM_RST BIT(1) + #define RAWLANE0_DIG_PCS_XF_RX_EQ_DELTA_IQ_OVRD_IN 0x3019 #define RAWLANE1_DIG_PCS_XF_RX_EQ_DELTA_IQ_OVRD_IN 0x3119 =20 @@ -76,16 +127,33 @@ struct s32g_pcie_ctrl { bool powered_on; }; =20 +struct s32g_xpcs_ctrl { + struct s32g_xpcs *phys[2]; + void __iomem *base0, *base1; +}; + struct s32g_serdes { struct s32g_serdes_ctrl ctrl; struct s32g_pcie_ctrl pcie; + struct s32g_xpcs_ctrl xpcs; struct device *dev; + u8 lanes_status; }; =20 /* PCIe phy subsystem */ =20 #define S32G_SERDES_PCIE_FREQ (100 * HZ_PER_MHZ) =20 +static void s32g_pcie_phy_reset(struct s32g_serdes *serdes) +{ + u32 val; + + val =3D readl(serdes->pcie.phy_base + S32G_PHY_RST_CTRL); + writel(val | WARM_RST, serdes->pcie.phy_base + S32G_PHY_RST_CTRL); + usleep_range(1000, 1100); + writel(val, serdes->pcie.phy_base + S32G_PHY_RST_CTRL); +} + static int s32g_pcie_check_clk(struct s32g_serdes *serdes) { struct s32g_serdes_ctrl *sctrl =3D &serdes->ctrl; @@ -277,6 +345,192 @@ static struct phy *s32g_serdes_phy_xlate(struct devic= e *dev, return phy; } =20 +/* XPCS subsystem */ + +static int s32g_serdes_xpcs_init(struct s32g_serdes *serdes, int id) +{ + struct s32g_serdes_ctrl *ctrl =3D &serdes->ctrl; + struct s32g_xpcs_ctrl *xpcs =3D &serdes->xpcs; + enum s32g_xpcs_shared shared =3D NOT_SHARED; + unsigned long rate =3D ctrl->ref_clk_rate; + struct device *dev =3D serdes->dev; + void __iomem *base; + + if (!id) + base =3D xpcs->base0; + else + base =3D xpcs->base1; + + if (ctrl->ss_mode =3D=3D 1 || ctrl->ss_mode =3D=3D 2) + shared =3D PCIE_XPCS_1G; + else if (ctrl->ss_mode =3D=3D 5) + shared =3D PCIE_XPCS_2G5; + + return s32g_xpcs_init(xpcs->phys[id], dev, id, base, + ctrl->ext_clk, rate, shared); +} + +static void s32g_serdes_prepare_pma_mode5(struct s32g_serdes *serdes) +{ + u32 val; + /* Configure TX_VBOOST_LVL and TX_TERM_CTRL */ + val =3D readl(serdes->ctrl.ss_base + S32G_PCIE_PHY_EXT_MISC_CTRL_2); + val &=3D ~(EXT_TX_VBOOST_LVL_MASK | EXT_TX_TERM_CTRL_MASK); + val |=3D FIELD_PREP(EXT_TX_VBOOST_LVL_MASK, 0x3) | + FIELD_PREP(EXT_TX_TERM_CTRL_MASK, 0x4); + writel(val, serdes->ctrl.ss_base + S32G_PCIE_PHY_EXT_MISC_CTRL_2); + + /* Enable phy external control */ + val =3D readl(serdes->ctrl.ss_base + S32G_PCIE_PHY_EXT_CTRL_SEL); + val |=3D EXT_PHY_CTRL_SEL; + writel(val, serdes->ctrl.ss_base + S32G_PCIE_PHY_EXT_CTRL_SEL); + + /* Configure ref range, disable PLLB/ref div2 */ + val =3D readl(serdes->ctrl.ss_base + S32G_PCIE_PHY_REF_CLK_CTRL); + val &=3D ~(REF_CLK_DIV2_EN | REF_CLK_MPLLB_DIV2_EN | EXT_REF_RANGE_MASK); + val |=3D FIELD_PREP(EXT_REF_RANGE_MASK, 0x3); + writel(val, serdes->ctrl.ss_base + S32G_PCIE_PHY_REF_CLK_CTRL); + + /* Configure multiplier */ + val =3D readl(serdes->ctrl.ss_base + S32G_PCIE_PHY_EXT_MPLLB_CTRL_2); + val &=3D ~(MPLLB_MULTIPLIER_MASK | EXT_MPLLB_FRACN_CTRL_MASK | BIT(24) | = BIT(28)); + val |=3D FIELD_PREP(MPLLB_MULTIPLIER_MASK, 0x27U) | + FIELD_PREP(EXT_MPLLB_FRACN_CTRL_MASK, 0x414); + writel(val, serdes->ctrl.ss_base + S32G_PCIE_PHY_EXT_MPLLB_CTRL_2); + + val =3D readl(serdes->ctrl.ss_base + S32G_PCIE_PHY_MPLLB_CTRL); + val &=3D ~MPLLB_SSC_EN; + writel(val, serdes->ctrl.ss_base + S32G_PCIE_PHY_MPLLB_CTRL); + + /* Configure tx lane division, disable word clock div2*/ + val =3D readl(serdes->ctrl.ss_base + S32G_PCIE_PHY_EXT_MPLLB_CTRL_3); + val &=3D ~(EXT_MPLLB_WORD_DIV2_EN | EXT_MPLLB_TX_CLK_DIV_MASK); + val |=3D FIELD_PREP(EXT_MPLLB_TX_CLK_DIV_MASK, 0x5); + writel(val, serdes->ctrl.ss_base + S32G_PCIE_PHY_EXT_MPLLB_CTRL_3); + + /* Configure bandwidth for filtering and div10*/ + val =3D readl(serdes->ctrl.ss_base + S32G_PCIE_PHY_EXT_MPLLB_CTRL_1); + val &=3D ~(EXT_MPLLB_BANDWIDTH_MASK | EXT_MPLLB_DIV_CLK_EN | + EXT_MPLLB_DIV8_CLK_EN | EXT_MPLLB_DIV_MULTIPLIER_MASK); + val |=3D FIELD_PREP(EXT_MPLLB_BANDWIDTH_MASK, 0x5f) | EXT_MPLLB_DIV10_CLK= _EN; + writel(val, serdes->ctrl.ss_base + S32G_PCIE_PHY_EXT_MPLLB_CTRL_1); + + val =3D readl(serdes->ctrl.ss_base + S32G_PCIE_PHY_EXT_MPLLA_CTRL_1); + val &=3D ~(EXT_MPLLA_BANDWIDTH_MASK); + val |=3D FIELD_PREP(EXT_MPLLA_BANDWIDTH_MASK, 0xc5); + writel(val, serdes->ctrl.ss_base + S32G_PCIE_PHY_EXT_MPLLA_CTRL_1); + + /* Configure VCO */ + val =3D readl(serdes->ctrl.ss_base + S32G_PCIE_PHY_XPCS1_RX_OVRD_CTRL); + val &=3D ~(XPCS1_RX_VCO_LD_VAL_MASK | XPCS1_RX_REF_LD_VAL_MASK); + val |=3D FIELD_PREP(XPCS1_RX_VCO_LD_VAL_MASK, 0x540) | + FIELD_PREP(XPCS1_RX_REF_LD_VAL_MASK, 0x2b); + writel(val, serdes->ctrl.ss_base + S32G_PCIE_PHY_XPCS1_RX_OVRD_CTRL); + + /* Boundary scan control */ + val =3D readl(serdes->ctrl.ss_base + S32G_PCIE_PHY_EXT_BS_CTRL); + val &=3D ~(EXT_BS_RX_LEVEL_MASK | EXT_BS_TX_LOWSWING); + val |=3D FIELD_PREP(EXT_BS_RX_LEVEL_MASK, 0xB) | EXT_BS_RX_BIGSWING; + writel(val, serdes->ctrl.ss_base + S32G_PCIE_PHY_EXT_BS_CTRL); + + /* Rx loss threshold */ + val =3D readl(serdes->ctrl.ss_base + S32G_PCIE_PHY_EXT_MISC_CTRL_1); + val &=3D ~(EXT_RX_LOS_THRESHOLD_MASK | EXT_RX_VREF_CTRL_MASK); + val |=3D FIELD_PREP(EXT_RX_LOS_THRESHOLD_MASK, 0x3U) | + FIELD_PREP(EXT_RX_VREF_CTRL_MASK, 0x11U); + writel(val, serdes->ctrl.ss_base + S32G_PCIE_PHY_EXT_MISC_CTRL_1); +} + +static int s32g_serdes_enable_mode5(struct s32g_serdes *serdes, struct s32= g_xpcs *xpcs) +{ + int ret; + + s32g_serdes_prepare_pma_mode5(serdes); + + ret =3D s32g_xpcs_pre_pcie_2g5(xpcs); + if (ret) { + dev_err(serdes->dev, + "Failed to prepare SerDes for PCIE & XPCS @ 2G5 mode\n"); + return ret; + } + + s32g_pcie_phy_reset(serdes); + + return 0; +} + +static int s32g_serdes_init_clks(struct s32g_serdes *serdes) +{ + struct s32g_serdes_ctrl *ctrl =3D &serdes->ctrl; + struct s32g_xpcs_ctrl *xpcs =3D &serdes->xpcs; + struct s32g_xpcs *order[2]; + size_t i; + int ret; + + switch (ctrl->ss_mode) { + case 0: + return 0; + case 1: + order[0] =3D xpcs->phys[0]; + order[1] =3D NULL; + break; + case 2: + case 5: + order[0] =3D xpcs->phys[1]; + order[1] =3D NULL; + break; + case 3: + order[0] =3D xpcs->phys[1]; + order[1] =3D xpcs->phys[0]; + break; + case 4: + order[0] =3D xpcs->phys[0]; + order[1] =3D xpcs->phys[1]; + break; + default: + return -EINVAL; + } + + for (i =3D 0; i < ARRAY_SIZE(order); i++) { + if (!order[i]) + continue; + + ret =3D s32g_xpcs_init_plls(order[i]); + if (ret) + return ret; + } + + for (i =3D 0; i < ARRAY_SIZE(order); i++) { + if (!order[i]) + continue; + + if (ctrl->ss_mode =3D=3D 5) { + ret =3D s32g_serdes_enable_mode5(serdes, order[i]); + if (ret) + return ret; + } else { + s32g_xpcs_vreset(order[i]); + } + } + + for (i =3D 0; i < ARRAY_SIZE(order); i++) { + if (!order[i]) + continue; + + ret =3D s32g_xpcs_wait_vreset(order[i]); + if (ret) + return ret; + + ret =3D s32g_xpcs_reset_rx(order[i]); + if (ret) + return ret; + + s32g_xpcs_disable_an(order[i]); + } + + return 0; +} + /* Serdes subsystem */ =20 static int s32g_serdes_assert_reset(struct s32g_serdes *serdes) @@ -331,6 +585,10 @@ static int s32g_serdes_init(struct s32g_serdes *serdes) return ret; } =20 + /* + * We have a tight timing for the init sequence and any delay linked to + * printk as an example can fail the init after reset + */ ret =3D s32g_serdes_assert_reset(serdes); if (ret) goto disable_clks; @@ -363,7 +621,13 @@ static int s32g_serdes_init(struct s32g_serdes *serdes) dev_info(serdes->dev, "Using mode %d for SerDes subsystem\n", ctrl->ss_mode); =20 - return 0; + ret =3D s32g_serdes_init_clks(serdes); + if (ret) { + dev_err(serdes->dev, "XPCS init failed\n"); + goto disable_clks; + } + + return ret; =20 disable_clks: clk_bulk_disable_unprepare(serdes->ctrl.nclks, @@ -449,12 +713,32 @@ static int s32g_serdes_get_pcie_resources(struct plat= form_device *pdev, struct s return 0; } =20 +static int s32g_serdes_get_xpcs_resources(struct platform_device *pdev, st= ruct s32g_serdes *serdes) +{ + struct s32g_xpcs_ctrl *xpcs =3D &serdes->xpcs; + struct device *dev =3D &pdev->dev; + + xpcs->base0 =3D devm_platform_ioremap_resource_byname(pdev, "xpcs0"); + if (IS_ERR(xpcs->base0)) { + dev_err(dev, "Failed to map 'xpcs0'\n"); + return PTR_ERR(xpcs->base0); + } + + xpcs->base1 =3D devm_platform_ioremap_resource_byname(pdev, "xpcs1"); + if (IS_ERR(xpcs->base1)) { + dev_err(dev, "Failed to map 'xpcs1'\n"); + return PTR_ERR(xpcs->base1); + } + + return 0; +} + static int s32g2_serdes_create_phy(struct s32g_serdes *serdes, struct devi= ce_node *child_node) { struct s32g_serdes_ctrl *ctrl =3D &serdes->ctrl; struct phy_provider *phy_provider; struct device *dev =3D serdes->dev; - int ss_mode =3D ctrl->ss_mode; + int ret, ss_mode =3D ctrl->ss_mode; struct phy *phy; =20 if (of_device_is_compatible(child_node, "nxp,s32g2-serdes-pcie-phy")) { @@ -476,6 +760,37 @@ static int s32g2_serdes_create_phy(struct s32g_serdes = *serdes, struct device_nod if (IS_ERR(phy_provider)) return PTR_ERR(phy_provider); =20 + } else if (of_device_is_compatible(child_node, "nxp,s32g2-serdes-xpcs")) { + struct s32g_xpcs_ctrl *xpcs_ctrl =3D &serdes->xpcs; + struct s32g_xpcs *xpcs; + int port; + + /* no Ethernet phy lane */ + if (ss_mode =3D=3D 0) + return 0; + + /* Get XPCS port number connected to the lane */ + if (of_property_read_u32(child_node, "reg", &port)) + return -EINVAL; + + /* XPCS1 is not used */ + if (ss_mode =3D=3D 1 && port =3D=3D 1) + return -EINVAL; + + /* XPCS0 is not used */ + if (ss_mode =3D=3D 2 && port =3D=3D 0) + return -EINVAL; + + xpcs =3D devm_kmalloc(dev, sizeof(*xpcs), GFP_KERNEL); + if (!xpcs) + return -ENOMEM; + + xpcs_ctrl->phys[port] =3D xpcs; + + ret =3D s32g_serdes_xpcs_init(serdes, port); + if (ret) + return ret; + } else { dev_warn(dev, "Skipping unknown child node %pOFn\n", child_node); } @@ -517,6 +832,10 @@ static int s32g_serdes_probe(struct platform_device *p= dev) if (ret) return ret; =20 + ret =3D s32g_serdes_get_xpcs_resources(pdev, serdes); + if (ret) + return ret; + ret =3D s32g_serdes_parse_lanes(dev, serdes); if (ret) return ret; @@ -555,6 +874,57 @@ static int __maybe_unused s32g_serdes_resume(struct de= vice *device) return ret; } =20 +struct phylink_pcs *s32g_serdes_pcs_create(struct device *dev, struct devi= ce_node *np) +{ + struct platform_device *pdev; + struct device_node *pcs_np; + struct s32g_serdes *serdes; + u32 port; + + if (of_property_read_u32(np, "reg", &port)) + return ERR_PTR(-EINVAL); + + if (port >=3D S32G_SERDES_XPCS_MAX) + return ERR_PTR(-EINVAL); + + /* The PCS pdev is attached to the parent node */ + pcs_np =3D of_get_parent(np); + if (!pcs_np) + return ERR_PTR(-ENODEV); + + if (!of_device_is_available(pcs_np)) { + of_node_put(pcs_np); + return ERR_PTR(-ENODEV); + } + + pdev =3D of_find_device_by_node(pcs_np); + of_node_put(pcs_np); + if (!pdev) + return ERR_PTR(-EPROBE_DEFER); + + serdes =3D platform_get_drvdata(pdev); + if (!serdes) { + put_device(&pdev->dev); + return ERR_PTR(-EPROBE_DEFER); + } + + if (!serdes->xpcs.phys[port]) { + put_device(&pdev->dev); + return ERR_PTR(-EPROBE_DEFER); + } + + return &serdes->xpcs.phys[port]->pcs; +} +EXPORT_SYMBOL(s32g_serdes_pcs_create); + +void s32g_serdes_pcs_destroy(struct phylink_pcs *pcs) +{ + struct s32g_xpcs *xpcs =3D phylink_pcs_to_s32g_xpcs(pcs); + + put_device(xpcs->dev); +} +EXPORT_SYMBOL(s32g_serdes_pcs_destroy); + static const struct of_device_id s32g_serdes_match[] =3D { { .compatible =3D "nxp,s32g2-serdes", diff --git a/include/linux/pcs/pcs-nxp-s32g-xpcs.h b/include/linux/pcs/pcs-= nxp-s32g-xpcs.h new file mode 100644 index 000000000000..96a0049b93a6 --- /dev/null +++ b/include/linux/pcs/pcs-nxp-s32g-xpcs.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/** + * Copyright 2021-2026 NXP + */ +#ifndef PCS_NXP_S32G_XPCS_H +#define PCS_NXP_S32G_XPCS_H + +#include + +enum s32g_xpcs_shared { + NOT_SHARED, + PCIE_XPCS_1G, + PCIE_XPCS_2G5, +}; + +enum s32g_xpcs_pll { + XPCS_PLLA, /* Slow PLL */ + XPCS_PLLB, /* Fast PLL */ +}; + +struct s32g_xpcs { + void __iomem *base; + struct device *dev; + unsigned char id; + struct regmap *regmap; + enum s32g_xpcs_pll ref; + bool ext_clk; + bool mhz125; + enum s32g_xpcs_shared pcie_shared; + struct phylink_pcs pcs; +}; + +#define phylink_pcs_to_s32g_xpcs(pl_pcs) \ + container_of((pl_pcs), struct s32g_xpcs, pcs) + +int s32g_xpcs_init(struct s32g_xpcs *xpcs, struct device *dev, + unsigned char id, void __iomem *base, bool ext_clk, + unsigned long rate, enum s32g_xpcs_shared pcie_shared); +int s32g_xpcs_init_plls(struct s32g_xpcs *xpcs); +int s32g_xpcs_pre_pcie_2g5(struct s32g_xpcs *xpcs); +void s32g_xpcs_vreset(struct s32g_xpcs *xpcs); +int s32g_xpcs_wait_vreset(struct s32g_xpcs *xpcs); +int s32g_xpcs_reset_rx(struct s32g_xpcs *xpcs); +void s32g_xpcs_disable_an(struct s32g_xpcs *xpcs); + +struct phylink_pcs *s32g_serdes_pcs_create(struct device *dev, struct devi= ce_node *np); +void s32g_serdes_pcs_destroy(struct phylink_pcs *pcs); + +#endif /* PCS_NXP_S32G_XPCS_H */ + --=20 2.43.0 From nobody Mon Feb 9 01:02:11 2026 Received: from mail-wm1-f53.google.com (mail-wm1-f53.google.com [209.85.128.53]) (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 18F9E3D330D for ; Tue, 3 Feb 2026 16:19:26 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.53 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770135568; cv=none; b=oJSb7p5PGmNB9cb1SvPfD3SUvbtzeXXuq+tPu/7tN+Hb0Ddpi9UDE+/GRgWaVScxfwcP6BFUKDjdNmWv2UuSIIIYVPoxeZrQ/C+ws5eL9jM1VOPGnJX9FtHfXS2we1RUcLJapTIocNQwvt5q4c9KKG5rh6orx1ydnI9lGl73/0I= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770135568; c=relaxed/simple; bh=qQXywwliKiapdNiYt6dNWT6x/lDJ9YflmLKRdBQeGyw=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=S82VYkfM7C+g8PnIdyvOWyeMqcjEUNyvTAO67bQ1bvXHfgcjUvX0p/dsK86hP0M5/j/9Yfr8l0hNZ3Z9OqZkOpSbZ+zSMaJLFAVws2unyTMuVX2tNJ2dFMGSO6IE2OgwvorYS0PwxRdSzTwDHOASFGbZM00hsLbAfbqKNox0b10= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linaro.org; spf=pass smtp.mailfrom=linaro.org; dkim=pass (2048-bit key) header.d=linaro.org header.i=@linaro.org header.b=sz0cHxFf; arc=none smtp.client-ip=209.85.128.53 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linaro.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linaro.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=linaro.org header.i=@linaro.org header.b="sz0cHxFf" Received: by mail-wm1-f53.google.com with SMTP id 5b1f17b1804b1-481188b7760so39636025e9.0 for ; Tue, 03 Feb 2026 08:19:26 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1770135565; x=1770740365; 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=L5M1ZbJBKUwOtAuomtIJGP1xLtlDmn08RXJLiC0W3qw=; b=sz0cHxFfNl/3CGqWQVtv34vGdT2jimV0+aTwD3AuaVg1FFvAPiqQUiEf/N0I/9e+hp bU4z+9HBhmbZix6aYQJjyrzLknhXdACxUYJ0I3uP2bZJhT0sqayVq51jbsnuYJpGndVO swKwarzj32vbel7EDr01cytgi/Fc4zCKceG3HJw1Y4nIorwhFaLjc8rAwB/BIpBLLb9f +rov7jZCe5YeA+UhXseF6Mc53ZxmCvIO9Ma6VmzHZ0dnNkJcC2vgjG6/d13sNaw0dql1 U89uKLjtqJQ9hOW4PKYsL4ZIW2zoOG32Rg/i6xUfUD5xD9nj6J/wVk+Uw3PC6Wr8WELe TlYw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1770135565; x=1770740365; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=L5M1ZbJBKUwOtAuomtIJGP1xLtlDmn08RXJLiC0W3qw=; b=vz2Z0nl4ZwIhiuEvOVkl45MiygNCuGyzKKI6E/EGqF18P4i/L97eyPZxynlmF3Fgt0 bqrSy/3wMRrsi/yObcoPVLsWUA7LBh+v6WXeCX0HIRoVxhfBqZPvAPG4eiX2SRW+I9+c m1fsRlJsS8JrJ/xNGrFYBxx/fNFKugRIHTElCjmt2SBvAeB+IJBVHRZcbNlQp1QqAmJ5 Wk+VlYswg37aY9gkSP3LEfY9/nibLdHDExOVW1fOdIqbuvY/77Ae1SP+vhEQJAiSKrHc mxxfyD3GRSrG+XR/RLzW16pMAYdVr0MNVEolU4SEl4Bh6GUv4CIGo1YMPV8iLp9kaUA+ JXZw== X-Forwarded-Encrypted: i=1; AJvYcCWu40OaWHhKSVb9IFLTGBkOmd2ciAHRrdTGIGkeohaoaiEO6zYTT3u2Pnr9YHdD0BZJBsqPzPkIFeF2xYc=@vger.kernel.org X-Gm-Message-State: AOJu0YwkYMEaaqgVvBTGHvbPqH52zbEq0wbxMrIKtQvYD2w72PLUY3yu l1boVwobyvpYt8AH5nZrFxlLjona3PMtW622lql+iZGTLGFgZRtxnfdyIjDyca7gYwY= X-Gm-Gg: AZuq6aLSsdY/Shn7N/ULMTZnKfZk5mDWwd2lNRRnJYp93Z01JWq2Zg76DCwrbVYUDjN GgoqtVre9JjLpGjKBb/P2S5x+dbm5olxM5Uh7tZsOms4hfAUI9rYo1RuIFVFyOlaR+zwdueO8+U 6kAffLqenqwNOk8heN8pgr9njX0v2RS2sII6/jFzyiaPWB4agbAq2g9X2yUSeYi7kkJM90o1ktB 96lajIIIZggJBKlJAlRvKxp6TCWZMg8NA1GjVby7Z9/V4aKJeOCEy6GUUSk5B02IO2HNElznOYz lOHmBr+mHxXhQvABKm53wptuuxMLEgG13PWdnYwvKZ7Js2utuyjF1cY3Rzg3tYUTCBIcAcz3Pv5 At2Z4VX1x1IwKSe4clXLgJIg7w3zXy4VGu5aazAFRLLK9Z+zfdbg8XvOgS96tsW3CCjl6ihBCvn zg7ySIQOtGKHaOeoBO/t0= X-Received: by 2002:a05:600c:820e:b0:477:df7:b020 with SMTP id 5b1f17b1804b1-4830e96abaemr2199595e9.18.1770135565342; Tue, 03 Feb 2026 08:19:25 -0800 (PST) Received: from vingu-cube.. ([2a01:e0a:f:6020:81d1:b874:c1dc:42e5]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-482dbd21f5dsm144120785e9.8.2026.02.03.08.19.24 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 03 Feb 2026 08:19:24 -0800 (PST) From: Vincent Guittot To: vkoul@kernel.org, neil.armstrong@linaro.org, krzk+dt@kernel.org, conor+dt@kernel.org, ciprianmarian.costea@oss.nxp.com, s32@nxp.com, p.zabel@pengutronix.de, linux@armlinux.org.uk, ghennadi.procopciuc@nxp.com, Ionut.Vicovan@nxp.com, linux-phy@lists.infradead.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, netdev@vger.kernel.org, horms@kernel.org Cc: Frank.li@nxp.com Subject: [PATCH 4/4 v2] MAINTAINERS: Add MAINTAINER for NXP S32G Serdes driver Date: Tue, 3 Feb 2026 17:19:17 +0100 Message-ID: <20260203161917.1666696-5-vincent.guittot@linaro.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260203161917.1666696-1-vincent.guittot@linaro.org> References: <20260203161917.1666696-1-vincent.guittot@linaro.org> 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 a new entry for S32G Serdes driver. Signed-off-by: Vincent Guittot --- MAINTAINERS | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 67db88b04537..fab748292821 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3206,6 +3206,16 @@ S: Maintained F: Documentation/devicetree/bindings/net/nxp,s32-dwmac.yaml F: drivers/net/ethernet/stmicro/stmmac/dwmac-s32.c =20 +ARM/NXP S32G SERDES DRIVER +M: Ciprian Marian Costea +R: NXP S32 Linux Team +L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) +S: Maintained +F: Documentation/devicetree/bindings/phy/nxp,s32g-serdes.yaml +F: drivers/net/pcs/pcs-nxp-s32g-xpcs.c +F: drivers/phy/freescale/phy-nxp-s32g-serdes.c +F: include/linux/pcs/pcs-nxp-s32g-xpcs.h + ARM/Orion SoC/Technologic Systems TS-78xx platform support M: Alexander Clouter L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) --=20 2.43.0