From nobody Sun Feb 8 19:56:23 2026 Received: from mail-wm1-f48.google.com (mail-wm1-f48.google.com [209.85.128.48]) (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 7C12A3064BA for ; Mon, 26 Jan 2026 09:22:04 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.48 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769419326; cv=none; b=GmW51CHOlVCzDfbMO+MOHzsn45t9xReTsKGDrza4fuwaKfWue8vKvyb+5WwFhLbbkwPRQAd5I5KTsWbOAwQiNv2IjD9+K28Uo6VWhtQ/VeOTMKXM2eUE1uG9FS1STTwY/TsXGQzrn1Y9skLew2g9kwaNDOsfsO2f8BV6KRZ4glk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769419326; c=relaxed/simple; bh=HzBu2NbGKcOQwsBSue+2sXgdaUYPXA/eSDYmPrHSRrc=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=AZomd1epYSzo/3U8lGnXqjG2yMUafkgFhWRXNibe0NvBzPeKQEFTuop7UEp5AQ8hCslV9UFjdAbgn6OHUCAk1fqkmdNewZFpA+odKAE87g29Ualp7Ahwt9TmmkYcCfaEMLCVJzZmMqmGuHgbb8MOgTErBOQixKPW0ahHwKXbjOY= 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=A6LA8ssg; arc=none smtp.client-ip=209.85.128.48 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="A6LA8ssg" Received: by mail-wm1-f48.google.com with SMTP id 5b1f17b1804b1-48049955f7fso35991375e9.0 for ; Mon, 26 Jan 2026 01:22:04 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1769419323; x=1770024123; 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=A6LA8ssgTyZ23s7ItqrjdXKyy0kKFQmZE47uDIl8IhELHnUBU7QA78F1iq1E0KF+G8 /gLQZIBP/Q8wHBisCXHqqAmVwdXpD/DNT4j4F3OWwx+G/Y8rKH4kkFjB4qKWfrtpib5C WIbb9UFr2FvAUAYU/RuJudpPAFN+oSiz+EyWJt7bz2aXkylvsMikxt4z0E8KCkS5kzDE NUg8o6cm02pmDv04MKWkAKNwnEAcSa9BeXVgqCSMkRIPJ3feZOBMz7pL5yWdJyO+aY9f YIcWL6NVCpofqGni5QywUl8akcWgcrcmxyl0SxWZp70aEIppCqV4cPN7UKPTL3KWXqnz U+rA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1769419323; x=1770024123; 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=m9ulFkEPv1h1NxPgSPXPv0xsQp5RdO9HsVSPLqHsA/DfwskTFfpIaYtGMZPn/nyH/k Bvzam9NeQcrQvwABAAmV7q63cZiwcFJCDKT+UbN1XpbovUU9rwdieb31zQd68jDb0zce bvnrFl6e7T6FPkIe1MXUzu8J7N/X6hdhLclIMRZIYlh7J4syXiVTt6KsZ+NmjnW4eI77 aDGuLhyAalnmieEijYUmUT6uSkdqy/M1o/hTsrRSLDtVD7OEx7h9KlynGhFzkDvqiuEx ih1yY8fKKzxsEy7xiQKnYbb9b//GBJ6FWFl01lucsm5S1A6TPXQ1FeD18gWQ4z6KTd8n XdDQ== X-Forwarded-Encrypted: i=1; AJvYcCWbygrYFagodMKZU2bgC3orRZ7ScQspWalxepnRLFWGK+XutSqZ8WyXzvph+WcP9G+6awWZLzpGtp8l+l0=@vger.kernel.org X-Gm-Message-State: AOJu0Yzy3SguXJDvsSk6ZzCTjh26pT6iS83HVxMj9jfUGP6F3XFruNCT O1rSp1+WvsJsuNNc5iELQsluf6Nze8pl/tvLy1dkR0ZH0fm+c0g4bdJZMEwTQS+ETJE= X-Gm-Gg: AZuq6aL2YObZXGAv0KX28SI8pNRaaqo4XeVddtb9v/tjl0BokQa5DNPGoTAtXW8YfTb 5dCUnL2B89RBRtULGXipZqUYrbTRZ1rYI9Yyjwag9ozYy8mly7uLn/lVKS25nbqJQnSDdupWyB0 YQa0dG3MIzSE3IC6JMCU1sQJ+9L1yiXSVez/eLAs3remKlFYuz+aKAbeQWiPJMin4oX/01e2XJq JszEAWgPxgr5UNlFKAbvgtguLkAb/cyBuxvi11M3zBItBW7lJjyXMpzeXKVkKS1pdd/DxrpL/Zb 8Gtd4TzLT3tNT/zhBQ/Epejqs9ZQjMu7c/NKdYw+6H9cwFEnI4H+un8KEmIBA05uY+dXDtZLSYZ q+y+sd0q7tJ4WL4mQbvebh8zO13hDB4xRbi/gH1b4jjSmk9iDXmkD/flSk3W1S6o4B9VQjXX/er xAWPdFi609tpXAEV0LbjU= X-Received: by 2002:a05:600c:3ba8:b0:47d:264e:b435 with SMTP id 5b1f17b1804b1-4805cf5ecb7mr68589285e9.22.1769419322750; Mon, 26 Jan 2026 01:22:02 -0800 (PST) Received: from vingu-cube.. ([2a01:e0a:f:6020:e270:a43a:f2fa:900a]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-480470cf1acsm346669855e9.14.2026.01.26.01.22.01 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 26 Jan 2026 01:22:01 -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, bogdan-gabriel.roman@nxp.com, Ionut.Vicovan@nxp.com, alexandru-catalin.ionita@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 Cc: Frank.li@nxp.com Subject: [PATCH 1/4] dt-bindings: serdes: s32g: Add NXP serdes subsystem Date: Mon, 26 Jan 2026 10:21:56 +0100 Message-ID: <20260126092159.815968-2-vincent.guittot@linaro.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260126092159.815968-1-vincent.guittot@linaro.org> References: <20260126092159.815968-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 Sun Feb 8 19:56:23 2026 Received: from mail-wm1-f54.google.com (mail-wm1-f54.google.com [209.85.128.54]) (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 253E63081A4 for ; Mon, 26 Jan 2026 09:22:06 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.54 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769419328; cv=none; b=Qu0geEDbNxfsu1EAbfq5YBAYAiHZQJ0nlLyoLTOW5Lm+PUw/GOdL8aytQ3ZNTvLDpcr2xz93MGx3bjWcVThssvrjHeQGyxjPWnke7vL8RV+nFDtt5SjF5F3PofeAy7t+TRkhLBSR8AbgOAKQ6GBioWIGc2hFKgNfYaHGUylCCWY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769419328; c=relaxed/simple; bh=Nj5xoIHsFwz4mwN35mQYGJFp7jFOthXPcVMX2Svf5Lc=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=XRPBvsuapsFABPpU5aVcuF/5XRK122AtiKL7NN3fR658XBNw8xG+3Djug9VdbuDG3tJWgMh4abP1ZJbx4eziq8wONNPl4xde+2n7QR+3mOFpmmlBQkqgyUwCPFFNbJC6zaGLD9IDVQiO0EvKi3HtQyWLsRPufubiY6NCNGB4bdc= 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=rvPufoni; arc=none smtp.client-ip=209.85.128.54 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="rvPufoni" Received: by mail-wm1-f54.google.com with SMTP id 5b1f17b1804b1-4801bc32725so32997625e9.0 for ; Mon, 26 Jan 2026 01:22:05 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1769419324; x=1770024124; 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=DTHA/4uN9KLXd+UFlDqR2tTJgWE1rHWO6ROsUhtjyd0=; b=rvPufonipOKGGezOLhRnSJmz2Msnb2QVeSDazVJLBzQ+hd94Q9dCk4L3Utx7gTFhGh SdReeNe2Ws68cDqKsr3KvgWbQONEw+R5u1v6QWrN1v8C9nNzLyvDXIKole7uPUGY135/ tPLOxuc/gUrGUpFDUOyDAmVDhh6rLGXtYHbZip7oZu65heM6H1C/Tckq6ehaux+qRW3Y 6o8T4J7Hqs1odRjm6cYoOFLF6vlMFxben/P1Dazatw+hxb7M973gV0lkTzoQ7ugq7C6O OgrXK02Mu9SITz2TXQFSmCeJf63j3rpr3ipbl2NI7YlcDbVfLJ7yTtEwDjr60gyI4FG5 r3cg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1769419324; x=1770024124; 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=DTHA/4uN9KLXd+UFlDqR2tTJgWE1rHWO6ROsUhtjyd0=; b=wXVoYc8owH3xu7QWmf7UNNCSXeAqt5yvtpOQZWnl6Sg4sJqD70F5VzTEB8+xh+Y4kL 4hRDMmfYEy/0TP8Ty5wwqMgp9YrTYGFEaXoIi2NJAJgaIzA6C1GNwgteUDYy8FwHLJy0 AVoMO0lVTyosYSHgIZlNokNqIHkoWC465lWN6QlEJujUGGQEbv6rUMKJXZ1JuPQ2SecV mJNlFSlHAOhl/hpkEYAF38Rhu7coy1fZz/+cRWqYiB/CP7OA0SgA+QkvjZ4xsHi5NiRT TGIdOUPRMCirTSAbajy6Zy860pNrcV+O/+WTNUctc0UqCdJ/1LGp7kss9lGardYNbNwk a+yA== X-Forwarded-Encrypted: i=1; AJvYcCWin44xV/RRxcc/P3k4eM3cpEveGECi70gMGRpSVFDbn8+8OOGMIZNQq/GgRnlDk1Xl/aitpCEksp+Q+m8=@vger.kernel.org X-Gm-Message-State: AOJu0Yy+ds3Kkpu1Q26ijTRnHLaMTTIkRQ3/qsvwwqKvyIE0sKB9QR99 TrTU9FFHEwIRt4O65I3QAns3LiNM7hT+ItRSuebQtd/mw3gM8DSFWADx7IXARvPHqkU= X-Gm-Gg: AZuq6aKuxCT0VHnnqyNi6qrXg1SZHOMQ8eEck1IeD96HmrBYaSxR8hNBdkI9NMdh7Da fpGJmoZEiub/oCcJiFSxmD9sZMPh34V/n4XXdrRlZfwZPuLnuo4BPYWNKABR7wotFIZ28wZw/Zl MP+xtfuA5zHe4JKhZ5e+8ATmNwfl3AVOeiRGvLYeuWfycvKQNM7Stho0gJRZM19p7l5YTgrJI99 9iS/bZIu3GMt2yEgzNSZJVYt3YEZbU1KoiWEQJ3hree1ZYbbyctLh52uXCfRkYTnZqQZAsziSkw kHsLXkKg8gSxrRW/uQcC/uUVHzCbjp0cjPJ85MMVm7mMTnOM/395NbKEK+soIRQ7czm4cARVe3M G61rNWSpg8Il4kiY8y1pLv5DkTfTkvFhR5BAcaCVln2VAgpxa/rqDIYMCysejc7jWEIOttM+jaF 24cfuJPOhhj62StoQv3Mk= X-Received: by 2002:a05:600c:1e2a:b0:47e:e952:86c9 with SMTP id 5b1f17b1804b1-4805ccd1198mr65140045e9.0.1769419324298; Mon, 26 Jan 2026 01:22:04 -0800 (PST) Received: from vingu-cube.. ([2a01:e0a:f:6020:e270:a43a:f2fa:900a]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-480470cf1acsm346669855e9.14.2026.01.26.01.22.02 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 26 Jan 2026 01:22:03 -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, bogdan-gabriel.roman@nxp.com, Ionut.Vicovan@nxp.com, alexandru-catalin.ionita@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 Cc: Frank.li@nxp.com Subject: [PATCH 2/4] phy: s32g: Add serdes subsystem phy Date: Mon, 26 Jan 2026 10:21:57 +0100 Message-ID: <20260126092159.815968-3-vincent.guittot@linaro.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260126092159.815968-1-vincent.guittot@linaro.org> References: <20260126092159.815968-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 | 569 ++++++++++++++++++++ 3 files changed, 579 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..8336c868c8dc --- /dev/null +++ b/drivers/phy/freescale/phy-nxp-s32g-serdes.c @@ -0,0 +1,569 @@ +// 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 + +#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.2ms 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 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=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; + + 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, + .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(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) + return dev_err_probe(dev, ctrl->nclks, + "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 ret; +} + +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(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) + break; + } + + return ret; +} + +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; + + ret =3D s32g_serdes_init(serdes); + + return ret; +} + +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 Sun Feb 8 19:56:23 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 6EE083093C4 for ; Mon, 26 Jan 2026 09:22:08 +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=1769419332; cv=none; b=Q4SFh6K50hqVfrzzt/n1jakQECURNoh9DeLnTNdIzoRDxQBoAZcCn/S5VBxnbCmd4yJZX6XvWIO3DLJxbG8d9AIXQnjGd/iFV4wI1+fGwkX2iAmy8z36A8jdczb5+DkMEIHe9Yl33Xqq8LHAdSix+VPSwWX/ggdhuJxPDiGemsc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769419332; c=relaxed/simple; bh=FGgH6X95XdSgyz1tkvVLK6j6gwCyVERhZRDVHgjgKxg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Cjh+mhykrxPO/fX7l1IolfNdYlxiewUtZGQ333zPdIfVI/5z4aolkwlhVV/r9H8Xz9sgcfL2amntMaUSF+E0e+uYvaWKm7OVcoTOlN79nk6DqOCrVircTl3UER24taX+5wyojbxpETpLOS7G2qjYfq9QoXy8CUdfrAu5RUavskk= 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=JObhvvjz; 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="JObhvvjz" Received: by mail-wm1-f45.google.com with SMTP id 5b1f17b1804b1-47fedb7c68dso42436715e9.2 for ; Mon, 26 Jan 2026 01:22:08 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1769419327; x=1770024127; 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=vSce4mO8UziDynZb3AXNH2TtP9oKk4P3oblHDyCxtXs=; b=JObhvvjzT6ycvqAsWEVUtqEkUB5tWROrBmHBLxW7nxpZt5CISbgSrRji+0RuecZMLU juSBIfo2Tyc3lF7vapILkQuUtTPce0nCzFd2A6+tAW3geOS89G+2NirmEmyAOzjaCwm0 yPxKeTvyPTlZRP8G7mWY8uTq0OEOaaPNApOVOWzAWor/F4zAUMY/DHMHf7c7HmIXyw/3 KlNlKATytkwsb0cGw3xWmp0tFONehE0zBXDs4nvu0MioMsRrqxwUSZn5pqajhg2bMoeg 4ujRBbfVhdGW74KTs8tJXH42XoMgYjHzLWLEu0XjJaFMkovvd3QsRnCC5GayAwr3RNlC RlRQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1769419327; x=1770024127; 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=vSce4mO8UziDynZb3AXNH2TtP9oKk4P3oblHDyCxtXs=; b=qrptUqxv4kE6iPm+y1FnQ/+u7RrtCeyhVBQpuQNvlQO+cwRJ8ogDYqEw3qmyZTGmA5 JOCBeYrJ/YA6y3HqaX9XiQVXuNqDKD18mqs9p/Dj/yzw8cR0oqkL6OdrcCKY2l6l6vve nwH133ocfu5OmKZQ506//P5QckMiQzcWISJ/+KQagGmOHEJLdTPKIHtIJuGgtRtc/fIQ M+C2IpR0PG00aFNFV7tNHD6WqIy2qws/xQiYU3A09dWcqJIb5MGMCKJisdh+vvgIGeV5 Yau+mSG8zB2OWWW32aaKTK6rQhX8jhs5e3GUEZycTLxcMMkuJHnTcZZ6O3VyulFb3tiq yXew== X-Forwarded-Encrypted: i=1; AJvYcCWajLUuV3fsOR5HkanYv440mqQVMrakwqBLx+q/MfqehjaFPY5K+MybRaNUfn/cMrPAVz5EPmUHyzKnmM0=@vger.kernel.org X-Gm-Message-State: AOJu0Yzat8GK/DzyanbhPa9tfxBbv21RQhrCfZT1HAhv+dJq0Xnhpkbv XCt9o1Z6leHBoYFJhMGzkaDEo5uPpMbUJo7S2Kly0vgE/b1CJ6fLEB39CS3nyxZ690Y= X-Gm-Gg: AZuq6aKNq/dLj1iR6peeL7amn1lHaagDAUNhhIpluAiYJyThOyT43U49l+ptlF94kki JcqowLRRuwd80/SqRFpwpS/GNmt8cj0UkAVB/opDr0S30TOkCurmfYaU2w0/MRTd5ukc46aUUXc uKbWX3a3dsBuxI5xUU+e2f5HFa/q738hIPbAqPSvM6L0OEjnHaL/NePo+uJUVp3Dh6QOMRlxok4 kk44n9l/BLAj3VbcKyLrCFvUu/mtwgY+lxTNq/ABv843ww+F+PEoFtNZIpGITqTu+Izc0JVLW9H QMARAt+aCT0M5e8kmRaKqsCxm92Unz5ftwTnrICNMWCk2/rjA15++pF2iYNEc15ENhYuO6nL7Oo k7iHXw8ezlNbQRyqGF7UtVi/DBzcmlNXQsQd3EcyiJtl6AdgAkLsp9fFybEOCXJwXWeMD4uLSS+ z8/lI+wS4G5BAP9iThl8I= X-Received: by 2002:a05:600c:8287:b0:47e:e8c2:905f with SMTP id 5b1f17b1804b1-4805cd407edmr60213865e9.8.1769419326481; Mon, 26 Jan 2026 01:22:06 -0800 (PST) Received: from vingu-cube.. ([2a01:e0a:f:6020:e270:a43a:f2fa:900a]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-480470cf1acsm346669855e9.14.2026.01.26.01.22.04 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 26 Jan 2026 01:22:04 -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, bogdan-gabriel.roman@nxp.com, Ionut.Vicovan@nxp.com, alexandru-catalin.ionita@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 Cc: Frank.li@nxp.com Subject: [PATCH 3/4] phy: s32g: Add serdes xpcs subsystem Date: Mon, 26 Jan 2026 10:21:58 +0100 Message-ID: <20260126092159.815968-4-vincent.guittot@linaro.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260126092159.815968-1-vincent.guittot@linaro.org> References: <20260126092159.815968-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/phy/freescale/Kconfig | 1 + drivers/phy/freescale/Makefile | 2 +- drivers/phy/freescale/phy-nxp-s32g-serdes.c | 361 ++++++- drivers/phy/freescale/phy-nxp-s32g-xpcs.c | 1082 +++++++++++++++++++ drivers/phy/freescale/phy-nxp-s32g-xpcs.h | 47 + include/linux/pcs/pcs-nxp-xpcs.h | 13 + 6 files changed, 1503 insertions(+), 3 deletions(-) create mode 100644 drivers/phy/freescale/phy-nxp-s32g-xpcs.c create mode 100644 drivers/phy/freescale/phy-nxp-s32g-xpcs.h create mode 100644 include/linux/pcs/pcs-nxp-xpcs.h 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/Makefile b/drivers/phy/freescale/Makefile index 86d948417252..2a1231cd466b 100644 --- a/drivers/phy/freescale/Makefile +++ b/drivers/phy/freescale/Makefile @@ -6,4 +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 +obj-$(CONFIG_PHY_S32G_SERDES) +=3D phy-nxp-s32g-serdes.o phy-nxp-s32g-xpc= s.o diff --git a/drivers/phy/freescale/phy-nxp-s32g-serdes.c b/drivers/phy/free= scale/phy-nxp-s32g-serdes.c index 8336c868c8dc..e0065c61a994 100644 --- a/drivers/phy/freescale/phy-nxp-s32g-serdes.c +++ b/drivers/phy/freescale/phy-nxp-s32g-serdes.c @@ -11,12 +11,16 @@ #include #include #include +#include #include #include #include #include #include =20 +#include "phy-nxp-s32g-xpcs.h" + +#define S32G_SERDES_XPCS_MAX 2 #define S32G_SERDES_MODE_MAX 5 =20 #define EXTERNAL_CLK_NAME "ext" @@ -31,6 +35,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) @@ -43,6 +93,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 @@ -75,16 +128,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; @@ -263,6 +333,187 @@ 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 pcie_xpcs_mode 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); +} + +#define XPCS_DISABLED -1 + +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; + int ret, order[2], xpcs_id; + size_t i; + + switch (ctrl->ss_mode) { + case 0: + return 0; + case 1: + order[0] =3D 0; + order[1] =3D XPCS_DISABLED; + break; + case 2: + case 5: + order[0] =3D 1; + order[1] =3D XPCS_DISABLED; + break; + case 3: + order[0] =3D 1; + order[1] =3D 0; + break; + case 4: + order[0] =3D 0; + order[1] =3D 1; + break; + default: + return -EINVAL; + } + + for (i =3D 0; i < ARRAY_SIZE(order); i++) { + xpcs_id =3D order[i]; + + if (xpcs_id =3D=3D XPCS_DISABLED) + continue; + + ret =3D s32g_xpcs_init_plls(xpcs->phys[xpcs_id]); + if (ret) + return ret; + } + + if (ctrl->ss_mode =3D=3D 5) { + s32g_serdes_prepare_pma_mode5(serdes); + + ret =3D s32g_xpcs_pre_pcie_2g5(xpcs->phys[1]); + if (ret) { + dev_err(serdes->dev, + "Failed to prepare SerDes for PCIE & XPCS @ 2G5 mode\n"); + return ret; + } + + s32g_pcie_phy_reset(serdes); + } else { + for (i =3D 0; i < ARRAY_SIZE(order); i++) { + xpcs_id =3D order[i]; + + if (xpcs_id =3D=3D XPCS_DISABLED) + continue; + + ret =3D s32g_xpcs_vreset(xpcs->phys[xpcs_id]); + if (ret) + return ret; + } + } + + for (i =3D 0; i < ARRAY_SIZE(order); i++) { + xpcs_id =3D order[i]; + + if (xpcs_id =3D=3D XPCS_DISABLED) + continue; + + ret =3D s32g_xpcs_wait_vreset(xpcs->phys[xpcs_id]); + if (ret) + return ret; + + s32g_xpcs_reset_rx(xpcs->phys[xpcs_id]); + s32g_xpcs_disable_an(xpcs->phys[xpcs_id]); + } + + return 0; +} + /* Serdes subsystem */ =20 static int s32g_serdes_assert_reset(struct s32g_serdes *serdes) @@ -317,6 +568,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; @@ -349,7 +604,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, @@ -433,12 +694,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")) { @@ -460,6 +741,41 @@ 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 (IS_ERR(xpcs)) { + dev_err(dev, "Failed to allocate xpcs\n"); + return -ENOMEM; + } + + xpcs_ctrl->phys[port] =3D xpcs; + + xpcs->an =3D of_property_read_bool(dev->of_node, "nxp,xpcs_an"); + + ret =3D s32g_serdes_xpcs_init(serdes, port); + if (ret) + return ret; + } else { dev_warn(dev, "Skipping unknown child node %pOFn\n", child_node); } @@ -501,6 +817,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; @@ -541,6 +861,43 @@ 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 > 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 || !platform_get_drvdata(pdev)) { + if (pdev) + put_device(&pdev->dev); + return ERR_PTR(-EPROBE_DEFER); + } + + serdes =3D platform_get_drvdata(pdev); + + return &serdes->xpcs.phys[port]->pcs; +} +EXPORT_SYMBOL(s32g_serdes_pcs_create); + static const struct of_device_id s32g_serdes_match[] =3D { { .compatible =3D "nxp,s32g2-serdes", diff --git a/drivers/phy/freescale/phy-nxp-s32g-xpcs.c b/drivers/phy/freesc= ale/phy-nxp-s32g-xpcs.c new file mode 100644 index 000000000000..c49bdaecc034 --- /dev/null +++ b/drivers/phy/freescale/phy-nxp-s32g-xpcs.c @@ -0,0 +1,1082 @@ +// SPDX-License-Identifier: GPL-2.0 +/** + * Copyright 2021-2026 NXP + */ + +#include +#include +#include +#include +#include +#include +#include "phy-nxp-s32g-xpcs.h" + +#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 0xFF +#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) + +#define phylink_pcs_to_s32g_xpcs(pl_pcs) \ + container_of((pl_pcs), struct s32g_xpcs, pcs) + +typedef bool (*xpcs_poll_func_t)(struct s32g_xpcs *); + +/* + * XPCS registers can't be access 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 unsigned int s32g_xpcs_get_an(struct s32g_xpcs *xpcs) +{ + unsigned int val =3D s32g_xpcs_read(xpcs, VR_MII_AN_INTR_STS); + + return !!(val & CL37_ANCMPLT_INTR); +}; + +static int s32g_xpcs_wait_an_done(struct s32g_xpcs *xpcs) +{ + unsigned int val; + + return read_poll_timeout(s32g_xpcs_get_an, val, + !!(val & CL37_ANCMPLT_INTR), + 0, + XPCS_TIMEOUT_MS, false, xpcs); +}; + +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); +} + +int s32g_xpcs_vreset(struct s32g_xpcs *xpcs) +{ + if (!xpcs) + return -EINVAL; + + /* Step 19 */ + s32g_xpcs_write_bits(xpcs, VR_MII_DIG_CTRL1, VR_RST, VR_RST); + + return 0; +} + +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; +} + +/* + * Note: This function should be compatible with phylink. + * That means it should only modify link, duplex, speed + * an_complete, pause. + */ +static int s32g_xpcs_get_state(struct s32g_xpcs *xpcs, + struct phylink_link_state *state) +{ + struct device *dev =3D xpcs->dev; + unsigned int mii_ctrl, val, ss; + bool ss6, ss13, an_enabled, intr_en; + + mii_ctrl =3D s32g_xpcs_read(xpcs, SR_MII_CTRL); + an_enabled =3D !!(mii_ctrl & AN_ENABLE); + intr_en =3D !!(s32g_xpcs_read(xpcs, VR_MII_AN_CTRL) & MII_AN_INTR_EN); + + /* Check this important condition */ + if (an_enabled && !intr_en) { + dev_err(dev, "Invalid SGMII AN config interrupt is disabled\n"); + return -EINVAL; + } + + if (an_enabled) { + /* MLO_AN_INBAND */ + state->speed =3D SPEED_UNKNOWN; + state->link =3D 0; + state->duplex =3D DUPLEX_UNKNOWN; + state->an_complete =3D 0; + state->pause =3D MLO_PAUSE_NONE; + 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 0; + 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 0; + } + + } else { + /* MLO_AN_FIXED, MLO_AN_PHY */ + 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; + + if (mii_ctrl & 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 !!(mii_ctrl & SS6); + ss13 =3D !!(mii_ctrl & 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"); + return -EINVAL; + } + + return 0; +} + +static int s32g_xpcs_config_an(struct s32g_xpcs *xpcs, + const struct phylink_link_state state) +{ + bool an_enabled =3D false; + + an_enabled =3D linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, + state.advertising); + if (!an_enabled) + return 0; + + s32g_xpcs_write_bits(xpcs, VR_MII_DIG_CTRL1, + CL37_TMR_OVRRIDE, CL37_TMR_OVRRIDE); + + s32g_xpcs_write_bits(xpcs, VR_MII_AN_CTRL, + PCS_MODE_MASK | MII_AN_INTR_EN, + FIELD_PREP(PCS_MODE_MASK, PCS_MODE_SGMII) | MII_AN_INTR_EN); + /* Enable SGMII AN */ + s32g_xpcs_write_bits(xpcs, SR_MII_CTRL, AN_ENABLE, AN_ENABLE); + /* Enable SGMII AUTO SW */ + s32g_xpcs_write_bits(xpcs, VR_MII_DIG_CTRL1, + MAC_AUTO_SW, MAC_AUTO_SW); + + return 0; +} + +static int s32g_xpcs_config(struct s32g_xpcs *xpcs, + const struct phylink_link_state state) +{ + struct device *dev =3D xpcs->dev; + unsigned int val =3D 0, duplex =3D 0; + int ret =3D 0; + int speed =3D state.speed; + bool an_enabled; + + /* Configure adaptive MII width */ + s32g_xpcs_write_bits(xpcs, VR_MII_AN_CTRL, MII_CTRL, 0); + + an_enabled =3D !!(s32g_xpcs_read(xpcs, SR_MII_CTRL) & AN_ENABLE); + + dev_dbg(dev, "xpcs_%d: speed=3D%u duplex=3D%d an=3D%d\n", xpcs->id, + speed, state.duplex, an_enabled); + + if (an_enabled) { + switch (speed) { + case SPEED_10: + case SPEED_100: + case SPEED_1000: + s32g_xpcs_write(xpcs, VR_MII_LINK_TIMER_CTRL, 0x2faf); + break; + case SPEED_2500: + s32g_xpcs_write(xpcs, VR_MII_LINK_TIMER_CTRL, 0x7a1); + s32g_xpcs_write_bits(xpcs, VR_MII_DIG_CTRL1, MAC_AUTO_SW, 0); + break; + default: + dev_err(dev, "Speed not recognized. Can't setup xpcs\n"); + return -EINVAL; + } + + s32g_xpcs_write_bits(xpcs, SR_MII_CTRL, RESTART_AN, RESTART_AN); + + ret =3D s32g_xpcs_wait_an_done(xpcs); + if (ret) + dev_warn(dev, "AN did not finish for XPCS%d", xpcs->id); + + /* Clear the AN CMPL intr */ + s32g_xpcs_write_bits(xpcs, VR_MII_AN_INTR_STS, CL37_ANCMPLT_INTR, 0); + } else { + 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); + + switch (speed) { + case SPEED_10: + 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 (state.duplex =3D=3D DUPLEX_FULL) + duplex =3D DUPLEX_MODE; + + s32g_xpcs_write_bits(xpcs, SR_MII_CTRL, DUPLEX_MODE, duplex); + + 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); + } + + return 0; +} + +/* + * phylink_pcs_ops fops + */ + +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); + + s32g_xpcs_get_state(xpcs, state); +} + +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); + struct phylink_link_state state =3D { 0 }; + + if (!(neg_mode =3D=3D PHYLINK_PCS_NEG_INBAND_ENABLED)) + return 0; + + linkmode_copy(state.advertising, advertising); + + return s32g_xpcs_config_an(xpcs, state); +} + +static void s32cc_phylink_pcs_restart_an(struct phylink_pcs *pcs) +{ + /* Not yet */ +} + +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 phylink_link_state state =3D { 0 }; + + state.speed =3D speed; + state.duplex =3D duplex; + state.an_complete =3D false; + + s32g_xpcs_config(xpcs, state); +} + +static const struct phylink_pcs_ops s32cc_phylink_pcs_ops =3D { + .pcs_get_state =3D s32cc_phylink_pcs_get_state, + .pcs_config =3D s32cc_phylink_pcs_config, + .pcs_an_restart =3D s32cc_phylink_pcs_restart_an, + .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; +} + +int s32g_xpcs_disable_an(struct s32g_xpcs *xpcs) +{ + int ret; + + ret =3D (s32g_xpcs_read(xpcs, SR_MII_CTRL) & AN_ENABLE); + + s32g_xpcs_write_bits(xpcs, SR_MII_CTRL, DUPLEX_MODE, DUPLEX_MODE); + s32g_xpcs_write_bits(xpcs, SR_MII_CTRL, AN_ENABLE, 0); + + return ret; +} + +int s32g_xpcs_init(struct s32g_xpcs *xpcs, struct device *dev, + unsigned char id, void __iomem *base, bool ext_clk, + unsigned long rate, enum pcie_xpcs_mode 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/phy-nxp-s32g-xpcs.h b/drivers/phy/freesc= ale/phy-nxp-s32g-xpcs.h new file mode 100644 index 000000000000..07cdd292fd5e --- /dev/null +++ b/drivers/phy/freescale/phy-nxp-s32g-xpcs.h @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/** + * Copyright 2021-2026 NXP + */ +#ifndef NXP_S32G_XPCS_H +#define NXP_S32G_XPCS_H + +#include +#include +#include +#include + +enum pcie_xpcs_mode { + 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; + bool an; + enum pcie_xpcs_mode pcie_shared; + struct phylink_pcs 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 pcie_xpcs_mode pcie_shared); +int s32g_xpcs_init_plls(struct s32g_xpcs *xpcs); +int s32g_xpcs_pre_pcie_2g5(struct s32g_xpcs *xpcs); +int 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); +int s32g_xpcs_disable_an(struct s32g_xpcs *xpcs); +#endif + diff --git a/include/linux/pcs/pcs-nxp-xpcs.h b/include/linux/pcs/pcs-nxp-x= pcs.h new file mode 100644 index 000000000000..41f424e8ff5b --- /dev/null +++ b/include/linux/pcs/pcs-nxp-xpcs.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/** + * Copyright 2026 NXP + */ + +#ifndef __LINUX_PCS_NXP_XPCS_H +#define __LINUX_PCS_NXP_XPCS_H + +#include + +struct phylink_pcs *s32g_serdes_pcs_create(struct device *dev, struct devi= ce_node *np); + +#endif /* __LINUX_PCS_XPCS_H */ --=20 2.43.0 From nobody Sun Feb 8 19:56:23 2026 Received: from mail-wm1-f54.google.com (mail-wm1-f54.google.com [209.85.128.54]) (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 1FA58309DCB for ; Mon, 26 Jan 2026 09:22:09 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.54 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769419331; cv=none; b=n85JL6nIvsDDRUTiMi9tx7K9LxefTYnBaGBxYor9Qwmsl6ZSAHMycPfkGszMVysXSPlDbvPFVAy+7OjXmuqiqxgPksZmxxHvy1tvzzZRSeCL34+kibtGbd5lDjb7PdPwBjc+U6dt+h/UmyGVRpGZVtbkOigtrZZ5mpeTD8a//Lc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769419331; c=relaxed/simple; bh=rRZrBOTEL98igZrxjeyvohslWGlOhGCZd+W/KF9qYh0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=P/VyOjbDsUmfq+vCmfeBJ0auWPM6HTArrwzatHEzrlVgO5K3SOuo10zAuZc0ISiCH1TCSZXTeX3aLgvN+bPubm4AIpcFHAh3FxTEkNVIIceKhC/On6GROodyvsBl+oTAw6IsZvujMew83uM580GEtjWNn2hFE6Bq2/c1qVuLfuY= 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=TTSG/xoX; arc=none smtp.client-ip=209.85.128.54 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="TTSG/xoX" Received: by mail-wm1-f54.google.com with SMTP id 5b1f17b1804b1-47ee0291921so35835735e9.3 for ; Mon, 26 Jan 2026 01:22:09 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1769419327; x=1770024127; 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=6l0bRB7VVDjDIq+ulMsBVmhmzBjYiR54K3+u8tiEuOs=; b=TTSG/xoXgiDAwLo/45en78JlxyD3JJLNcG/NzSxknEfQBGXffkbLy6Cv4Brx28JKLu mcZz7M4rMjAlXl9K2YM6FGfsZ3zkqWGFWLmeoYoYv9VCYCspQ9azT96CfQmZQXFQ1Y32 DH9c1vwMeWr328YoYIMyrU3FDxAakG5R+FBWeYylFs8BxCp51TFN6rN9y/XrX0/i9QDJ yoKwTDkyez2FfVVaZhK46OQHx8eVc6BSZFotQRIZiuT6otJoCZNS4FnQRo6BsZWLr6Oe e++9HTLlZIxUmI10nQ9WgwJYmE7vPZzucmF4u26L4rHGeGYBiBJtPWe4YKJwkJnFAsOZ 5c/w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1769419327; x=1770024127; 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=6l0bRB7VVDjDIq+ulMsBVmhmzBjYiR54K3+u8tiEuOs=; b=i0ERzpaUPFhg7IntkSIfQzPOAxvxv5OYn9wcrvcl5P0zDRFNlhDm4WbxVJQp4FFppV 1tbViRjIjQbm60dE23Unk7hf4kgZpyJR/eoHqTaK/W9jmqYrQRtazgjkz+oV48C2PBll 8l6525nzjPY75Y+5+NVTvfJMuTXji37G3ZmtJO0SMysSmjedibdXwwf3TSVu2ScToO2+ tKnNpxBi9gMGoyXK94oeixugJbf61HEAzViUHf18xyeDhuEgnVOmvg+W59e9WUacabt4 +rI2ibvnfZcUILKPVxx9xx1myj7jCN7lZ9ivnMDwONzw7i98GbncLzLxcbkUZ09pg88r KGKw== X-Forwarded-Encrypted: i=1; AJvYcCWzwYHnBUEaTUj3jEbcTt+gsDalJU1pKmBLinHY44EVIk8Wr6QLmjX13YSzxt4vY+q1ADPv4paPUFJbP08=@vger.kernel.org X-Gm-Message-State: AOJu0YyFiDjMCqXFqtaKq6zk337ALedgJlrhLZFKqKeyl5zDEqTZXkaF dtou0AAwT8ozSgKXMmWt0o1L1ArAyAttRJ1H5a0OO4ZUIZVFgbnTDej6BtN238Xpo1Y= X-Gm-Gg: AZuq6aLDD8hE7qYkEUmu0ZYqgjMF9cnGeOOZ2gGE2b5lzABYDJUHrlD0q+BagFff5U9 sFRNay12wvYVmuiWe83I1YQ9vgOv3joAOVYR6r6mbDamlVz/E8ESyE4paI5nahqjM7abL/T/O6M 7mht5LbvfCw5rsSfuDcm3KsjkqXotExigQCCdhfLFPXGJ5agKHo1kvlwrg1g4eMTOodjM+aEX+W TBsAnvFRombumqbjThpuw48nej3ZPWOymtINggu+5Fhz+jcMSbED6/d6RoNE7gH1ywpZG+Ok+9i htcIYDGra+nIQfo7otdkI2mhz0EAAcUpI4cbzwTze2b7mHaTv9H3jUGul4SwX/vWFSsH0otdTIm fGUx4/rZg3F2Vj9/tgQ84ZctDaiTX7KfduhRYIr42CNk9yUqKDMZzft7TTpox10vK1fKY5G8UO4 W3sCf7C3gcnVc2HikMWLg= X-Received: by 2002:a05:600c:3b9d:b0:47e:de23:dd6f with SMTP id 5b1f17b1804b1-4805ce42a6dmr67097725e9.12.1769419327486; Mon, 26 Jan 2026 01:22:07 -0800 (PST) Received: from vingu-cube.. ([2a01:e0a:f:6020:e270:a43a:f2fa:900a]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-480470cf1acsm346669855e9.14.2026.01.26.01.22.06 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 26 Jan 2026 01:22:06 -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, bogdan-gabriel.roman@nxp.com, Ionut.Vicovan@nxp.com, alexandru-catalin.ionita@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 Cc: Frank.li@nxp.com Subject: [PATCH 4/4] MAINTAINERS: Add MAINTAINER for NXP S32G Serdes driver Date: Mon, 26 Jan 2026 10:21:59 +0100 Message-ID: <20260126092159.815968-5-vincent.guittot@linaro.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260126092159.815968-1-vincent.guittot@linaro.org> References: <20260126092159.815968-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 | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 765ad2daa218..888674a308a5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3202,6 +3202,15 @@ 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/pci/nxp,s32g-serdes.yaml +F: drivers/phy/freescale/phy-nxp-s32g-* +F: include/linux/pcs/pcs-nxp-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