* Re: [PATCH net-next v2 00/14] net: stmmac: SerDes, PCS, BASE-X, and inband goodies
From: Mohd Ayaan Anwar @ 2026-01-27 14:57 UTC (permalink / raw)
To: Russell King (Oracle)
Cc: Andrew Lunn, Heiner Kallweit, Alexandre Torgue, Andrew Lunn,
David S. Miller, Eric Dumazet, Jakub Kicinski, Konrad Dybcio,
linux-arm-kernel, linux-arm-msm, linux-phy, linux-stm32,
Maxime Coquelin, Neil Armstrong, netdev, Paolo Abeni, Vinod Koul
In-Reply-To: <aXPo5R1Q-qWG3r3l@shell.armlinux.org.uk>
On Fri, Jan 23, 2026 at 09:32:21PM +0000, Russell King (Oracle) wrote:
>
> and the failing store is the one for that last line of C code - in
> other words, pcs = NULL.
>
> This means that mac_select_pcs() returned NULL when being asked
> "which PCS should be used for 2500base-X" ?
>
> This suggests that the SerDes detection of support for 2500BASE-X
> isn't working, meaning that stmmac_mac_select_pcs() ends up returning
> NULL, rather than &priv->integrated_pcs->pcs.
>
> That would only happen if:
>
> /* Only allow 2500Base-X if the SerDes has support. */
> ret = dwmac_serdes_validate(priv, PHY_INTERFACE_MODE_2500BASEX);
> if (ret == 0)
> __set_bit(PHY_INTERFACE_MODE_2500BASEX,
> spcs->pcs.supported_interfaces);
>
> fails, meaning we don't set that interface mode for the PCS.
> dwmac_serdes_validate() calls phy_validate() for PHY_MODE_ETHERNET
> with the PHY interface mode as the sub mode.
>
> Patch 3 adds the required methods to phy-qcom-sgmii-eth.c to allow
> phy_validate() to indicate whether this is supported or not:
>
> .validate = qcom_dwmac_sgmii_phy_validate,
>
> and its implementation is:
>
> int ret = qcom_dwmac_sgmii_phy_speed(mode, submode);
>
> return ret < 0 ? ret : 0;
>
> where qcom_dwmac_sgmii_phy_speed() is:
>
> if (mode != PHY_MODE_ETHERNET)
> return -EINVAL;
>
> if (submode == PHY_INTERFACE_MODE_SGMII ||
> submode == PHY_INTERFACE_MODE_1000BASEX)
> return SPEED_1000;
>
> if (submode == PHY_INTERFACE_MODE_2500BASEX)
> return SPEED_2500;
>
> return -EINVAL;
>
> So, this should be returning a positive integer (SPEED_2500), which
> should cause phy_validate(serdes, PHY_MODE_ETHERNET,
> PHY_INTERFACE_MODE_2500BASEX, NULL) to return success (zero). That
> should result in PHY_INTERFACE_MODE_2500BASEX being set in
> spcs->pcs.supported_interfaces, and thus &priv->integrated_pcs->pcs
> being returned for PHY_INTERFACE_MODE_2500BASEX.
>
> Is the particular hardware you're running this oopsing test on not
> using a SerDes PHY? If that's the case, how does it switch between
> 2.5Gbps and 1Gbps data rate on the SerDes?
>
It is using the same SerDes PHY (qcom_dwmac_sgmii_phy_driver).
I added additional debug prints, and I think the crash is due to
BMSR_ESTATEN not being set in GMAC_AN_STATUS.
During pcs_init, BIT(8) of GMAC_AN_STATUS is 0:
[ 7.985913] [DBG] GMAC_AN_STATUS = 8
Therefore, this check:
if (readl(spcs->base + GMAC_AN_STATUS) & BMSR_ESTATEN) {
__set_bit(PHY_INTERFACE_MODE_1000BASEX,
spcs->pcs.supported_interfaces);
/* Only allow 2500Base-X if the SerDes has support. */
ret = dwmac_serdes_validate(priv, PHY_INTERFACE_MODE_2500BASEX);
if (ret == 0)
__set_bit(PHY_INTERFACE_MODE_2500BASEX,
spcs->pcs.supported_interfaces);
}
fails, and PHY_INTERFACE_MODE_2500BASEX never gets set in
pcs.supported_interfaces. Pardon my naivete, but does the
BMSR_ESTATEN bit not being set break some standard?
If I remove the check, the NULL pointer dereference is not observed
anymore. Although the SerDes link is still unstable.
I also tried enabling comma detect during dwmac_integrated_pcs_config,
but I am still seeing the Tx timeouts. I remember that when I had
tested the patches in October (without the SerDes driver changes),
the link state used to flap, but the data path became functional
after the link stabilized.
Ayaan
---
Full Logs (Speed Change: 1G -> 2.5G)
[ 244.817499] qcom-ethqos 23040000.ethernet eth1: pcs link down
[ 257.066210] dwmac: PCS configuration changed from phylink by glue, please report: 0x00040000 -> 0x00041000
[ 257.076143] dwmac: ANE 0 -> 1
[ 257.079668] qcom-ethqos 23040000.ethernet eth1: Link is Up - 1Gbps/Full - flow control off
[ 264.260852] qcom-ethqos 23040000.ethernet eth1: NETDEV WATCHDOG: CPU: 7: transmit queue 3 timed out 5472 ms
[ 264.271394] qcom-ethqos 23040000.ethernet eth1: Reset adapter.
[ 264.280493] qcom-ethqos 23040000.ethernet eth1: phy link down 2500base-x/Unknown/Unknown/none/off/nolpi
[ 264.842309] qcom-ethqos 23040000.ethernet eth1: Timeout accessing MAC_VLAN_Tag_Filter
[ 264.850391] qcom-ethqos 23040000.ethernet eth1: failed to kill vid 0081/0
[ 264.857547] qcom-ethqos 23040000.ethernet eth1: Register MEM_TYPE_PAGE_POOL RxQ-0
[ 264.865795] qcom-ethqos 23040000.ethernet eth1: Register MEM_TYPE_PAGE_POOL RxQ-1
[ 264.873939] qcom-ethqos 23040000.ethernet eth1: Register MEM_TYPE_PAGE_POOL RxQ-2
[ 264.882111] qcom-ethqos 23040000.ethernet eth1: Register MEM_TYPE_PAGE_POOL RxQ-3
[ 265.792807] qcom-ethqos 23040000.ethernet eth1: PHY stmmac-0:08 uses interfaces 4,23,27, validating 23
[ 265.802389] [DBG] stmmac_mac_select_pcs - testing for 23 (2500base-x) on priv->integrated_pcs->pcs.supported_interfaces = 4
[ 265.802399] qcom-ethqos 23040000.ethernet eth1: interface 23 (2500base-x) rate match pause supports 0-7,9,13-14,47
[ 265.824572] qcom-ethqos 23040000.ethernet eth1: PHY [stmmac-0:08] driver [Aquantia AQR115C] (irq=334)
[ 265.834055] qcom-ethqos 23040000.ethernet eth1: phy: sgmii setting supported 00000000,00000000,00008000,000062ff advertising 00000000,00000000,00008000,000062ff
[ 265.852828] [DBG] qcom_dwmac_sgmii_phy_speed called with mode=15, submode=4
[ 265.852837] [DBG] qcom_dwmac_sgmii_phy_validate - qcom_dwmac_sgmii_phy_speed returned 1000
[ 265.868580] qcom-ethqos 23040000.ethernet eth1: Enabling Safety Features
[ 265.884237] qcom-ethqos 23040000.ethernet eth1: IEEE 1588-2008 Advanced Timestamp supported
[ 265.893946] qcom-ethqos 23040000.ethernet eth1: registered PTP clock
[ 265.900561] qcom-ethqos 23040000.ethernet eth1: configuring for phy/sgmii link mode
[ 265.908451] qcom-ethqos 23040000.ethernet eth1: major config, requested phy/sgmii
[ 265.916159] [DBG] stmmac_mac_select_pcs - testing for 4 (sgmii) on priv->integrated_pcs->pcs.supported_interfaces = 4
[ 265.916166] qcom-ethqos 23040000.ethernet eth1: interface sgmii inband modes: pcs=03 phy=03
[ 265.935652] qcom-ethqos 23040000.ethernet eth1: major config, active phy/outband/sgmii
[ 265.943795] qcom-ethqos 23040000.ethernet eth1: phylink_mac_config: mode=phy/sgmii/none adv=00000000,00000000,00000000,00000000 pause=00
[ 265.956407] [DBG] qcom_dwmac_sgmii_phy_speed called with mode=15, submode=4
[ 265.956408] [DBG] qcom_dwmac_sgmii_phy_set_mode - qcom_dwmac_sgmii_phy_speed returned 1000
[ 265.976997] qcom-ethqos 23040000.ethernet eth1: phy link down 2500base-x/Unknown/Unknown/none/off/nolpi
[ 270.556001] qcom-ethqos 23040000.ethernet eth1: phy link up 2500base-x/2.5Gbps/Full/none/off/nolpi
[ 270.567649] qcom-ethqos 23040000.ethernet eth1: major config, requested phy/2500base-x
[ 270.575823] [DBG] stmmac_mac_select_pcs - testing for 23 (2500base-x) on priv->integrated_pcs->pcs.supported_interfaces = 4
[ 270.575831] qcom-ethqos 23040000.ethernet eth1: mac_select_pcs returned NULL
[ 270.594521] qcom-ethqos 23040000.ethernet eth1: interface 2500base-x inband modes: pcs=00 phy=00
[ 270.603554] qcom-ethqos 23040000.ethernet eth1: major config, active phy/outband/2500base-x
[ 270.612286] Unable to handle kernel NULL pointer dereference at virtual address 0000000000000010
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH net-next v2 00/14] net: stmmac: SerDes, PCS, BASE-X, and inband goodies
From: Mohd Ayaan Anwar @ 2026-01-27 13:45 UTC (permalink / raw)
To: Russell King (Oracle)
Cc: Andrew Lunn, Heiner Kallweit, Alexandre Torgue, Andrew Lunn,
David S. Miller, Eric Dumazet, Jakub Kicinski, Konrad Dybcio,
linux-arm-kernel, linux-arm-msm, linux-phy, linux-stm32,
Maxime Coquelin, Neil Armstrong, netdev, Paolo Abeni, Vinod Koul
In-Reply-To: <aXOvV-XRmn_Pe-ca@shell.armlinux.org.uk>
On Fri, Jan 23, 2026 at 05:26:47PM +0000, Russell King (Oracle) wrote:
> Sorry to hear that, but if it's any consolation, you're not alone. On
> new year's eve, I had three teeth extracted, including one that was
> laying horizontally in the palate of the mouth buried in bone, and
> needed in bone graft (modern bone grafts are quite different from what
> you'd expect btw.) It's been quite sore/painful as it heals.
>
That sounds like a nightmare. I hope you get better soon.
> Given the results you've given, my suggestion would be that the
> following patches are probably the most risky:
>
> Patch 2 "net: stmmac: qcom-ethqos: convert to set_clk_tx_rate() method"
>
> This changes the way the clock is configured. It would be worth
> testing that and giving a tested-by for the first two patches if
> that's successful.
Right, this new sequence would move the clock rate setting to after the
custom core is configured. Unfortunately, I don't have a device with an
RGMII interface. I am checking if I can get my hands on one.
I have given a t-b for the first patch.
Ayaan
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH net-next v2 01/14] net: stmmac: qcom-ethqos: remove mac_base
From: Mohd Ayaan Anwar @ 2026-01-27 12:06 UTC (permalink / raw)
To: Russell King (Oracle)
Cc: Andrew Lunn, Heiner Kallweit, Alexandre Torgue, Andrew Lunn,
David S. Miller, Eric Dumazet, Jakub Kicinski, linux-arm-kernel,
linux-arm-msm, linux-phy, linux-stm32, Maxime Chevallier,
Maxime Coquelin, Neil Armstrong, netdev, Paolo Abeni, Vinod Koul
In-Reply-To: <E1vjDr1-00000005fQ3-2rcn@rmk-PC.armlinux.org.uk>
On Fri, Jan 23, 2026 at 09:53:23AM +0000, Russell King (Oracle) wrote:
> In commit 9b443e58a896 ("net: stmmac: qcom-ethqos: remove MAC_CTRL_REG
> modification"), ethqos->mac_base is only written, never read. Let's
> remove it.
>
> Reviewed-by: Andrew Lunn <andrew@lunn.ch>
> Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
> ---
> drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c | 3 ---
> 1 file changed, 3 deletions(-)
>
> diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c
> index 0826a7bd32ff..869f924f3cde 100644
> --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c
> +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c
> @@ -100,7 +100,6 @@ struct ethqos_emac_driver_data {
> struct qcom_ethqos {
> struct platform_device *pdev;
> void __iomem *rgmii_base;
> - void __iomem *mac_base;
> int (*configure_func)(struct qcom_ethqos *ethqos, int speed);
>
> unsigned int link_clk_rate;
> @@ -772,8 +771,6 @@ static int qcom_ethqos_probe(struct platform_device *pdev)
> return dev_err_probe(dev, PTR_ERR(ethqos->rgmii_base),
> "Failed to map rgmii resource\n");
>
> - ethqos->mac_base = stmmac_res.addr;
> -
> data = of_device_get_match_data(dev);
> ethqos->por = data->por;
> ethqos->num_por = data->num_por;
> --
> 2.47.3
>
Validated this patch on the QCS9100 Ride R3 board, so:
Tested-by: Mohd Ayaan Anwar <mohd.anwar@oss.qualcomm.com>
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH 2/4] phy: s32g: Add serdes subsystem phy
From: Vincent Guittot @ 2026-01-27 10:07 UTC (permalink / raw)
To: Philipp Zabel
Cc: vkoul, neil.armstrong, krzk+dt, conor+dt, ciprianmarian.costea,
s32, linux, ghennadi.procopciuc, bogdan-gabriel.roman,
Ionut.Vicovan, alexandru-catalin.ionita, linux-phy, devicetree,
linux-kernel, linux-arm-kernel, netdev, Frank.li
In-Reply-To: <69592713b7bd419e23df458734ca78070a83e8ac.camel@pengutronix.de>
On Mon, 26 Jan 2026 at 14:11, Philipp Zabel <p.zabel@pengutronix.de> wrote:
>
> On Mo, 2026-01-26 at 10:21 +0100, Vincent Guittot wrote:
> > 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 <ciprianmarian.costea@oss.nxp.com>
> > Signed-off-by: Ciprian Marian Costea <ciprianmarian.costea@oss.nxp.com>
> > Co-developed-by: Alexandru-Catalin Ionita <alexandru-catalin.ionita@nxp.com>
> > Signed-off-by: Alexandru-Catalin Ionita <alexandru-catalin.ionita@nxp.com>
> > Co-developed-by: Ghennadi Procopciuc <ghennadi.procopciuc@nxp.com>
> > Signed-off-by: Ghennadi Procopciuc <ghennadi.procopciuc@nxp.com>
> > Co-developed-by: Ionut Vicovan <Ionut.Vicovan@nxp.com>
> > Signed-off-by: Ionut Vicovan <Ionut.Vicovan@nxp.com>
> > Co-developed-by: Bogdan Roman <bogdan-gabriel.roman@nxp.com>
> > Signed-off-by: Bogdan Roman <bogdan-gabriel.roman@nxp.com>
> > Signed-off-by: Vincent Guittot <vincent.guittot@linaro.org>
> > ---
> > 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/phy-nxp-s32g-serdes.c b/drivers/phy/freescale/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 @@
> [...]
> > +static int s32g_serdes_get_ctrl_resources(struct platform_device *pdev, struct s32g_serdes *serdes)
> > +{
> [...]
> > + ctrl->rst = devm_reset_control_get(dev, "serdes");
>
> Please use devm_reset_control_get_exclusive() directly.
Okay
>
> [...]
> > +static int s32g_serdes_get_pcie_resources(struct platform_device *pdev, struct s32g_serdes *serdes)
> > +{
> [...]
> > + pcie->rst = devm_reset_control_get(dev, "pcie");
>
> Same here.
>
> regards
> Philipp
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH next] phy: google: fix build dependency for Google Tensor USB PHY
From: Roy Luo @ 2026-01-27 2:34 UTC (permalink / raw)
To: André Draszik
Cc: Vinod Koul, Neil Armstrong, Peter Griffin, Tudor Ambarus,
Joy Chakraborty, Naveen Kumar, linux-phy, linux-kernel,
linux-arm-kernel, linux-samsung-soc, kernel test robot
In-Reply-To: <eebefae5273bccce2d0fcd97afe3ea28bedf27cd.camel@linaro.org>
On Fri, Jan 23, 2026 at 10:52 PM André Draszik <andre.draszik@linaro.org> wrote:
>
> Hi Roy,
>
> On Fri, 2026-01-23 at 15:51 -0800, Roy Luo wrote:
> > On Thu, Jan 22, 2026 at 10:28 PM André Draszik <andre.draszik@linaro.org> wrote:
> > >
> > >
> > > COMPILE_TEST is not limited to ARCH_xxx. It allows drivers to be
> > > compile tested even if the current build doesn't enable whatever
> > > option (like TYPEC).
> > >
> > > See also https://www.kernel.org/doc/html/latest/kbuild/kconfig-language.html#compile-testing
> >
> > "If a config symbol has a dependency, but the code controlled by the
> > config symbol can still be compiled if the dependency is not met",
> > TYPEC doesn't fall into this category as it's mandatory for compiling
> > PHY_GOOGLE_USB.
> > With "depends on TYPEC || COMPILE_TEST", this very build error
> > would resurface when TYPEC=n, COMPILE_TEST=y and
> > PHY_GOOGLE_USB=y. Please let me know if I missed anything.
>
> No it wouldn't. If you look at the build log & .config from the reported
> failure, the error was because:
>
> CONFIG_PHY_GOOGLE_USB=y
> CONFIG_TYPEC=m
You're right about the cause of the build error and sorry I gave
the wrong example. Let's look at this build error scenario with
"depends on TYPEC || COMPILE_TEST":
Given COMPILE_TEST=y and CONFIG_TYPEC=m, the
"depends on" would be evaluated as max(y,m) = y.
Hence, CONFIG_PHY_GOOGLE_USB is allowed to be
set to y, m or n. When it's set to y, the exact build error
would be introduced. I also tested this locally and saw
the build error.
Thanks,
Roy
>
> built-in code can not link against code from a module.
>
> The typec APIs are all stubbed out when TYPEC=n.
>
> Cheers,
> Andre'
>
>
>
> >
> > Thanks,
> > Roy
> >
> > >
> > > Cheers,
> > > Andre'
> > >
> > >
> > > >
> > > > [1] https://lore.kernel.org/all/CA+zupgwgfKwPYqj8G2tNf4pEXNEWA+vL2WYJPhJ16xExgko7Dw@mail.gmail.com/
> > > >
> > > > Regards,
> > > > Roy
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH v5 15/22] arm64: dts: renesas: r9a09g057: Add USB2.0 VBUS_SEL mux-controller support
From: Tommaso Merciai @ 2026-01-26 17:49 UTC (permalink / raw)
To: Geert Uytterhoeven
Cc: tomm.merciai, linux-renesas-soc, biju.das.jz, Peter Rosin,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Vinod Koul,
Kishon Vijay Abraham I, Fabrizio Castro, Lad Prabhakar,
Philipp Zabel, Yoshihiro Shimoda, Magnus Damm, Guenter Roeck,
Mark Brown, Jonathan Cameron, Georgi Djakov, Bartosz Golaszewski,
Arnd Bergmann, Greg Kroah-Hartman, devicetree, linux-kernel,
linux-phy
In-Reply-To: <CAMuHMdVfsO1NVv6+N37C8ss3thKz+sANCtO00PRhgnD5M1cs0Q@mail.gmail.com>
Hi Geert,
Thanks for your review.
On Mon, Jan 26, 2026 at 05:59:02PM +0100, Geert Uytterhoeven wrote:
> Hi Tommaso,
>
> On Thu, 27 Nov 2025 at 12:51, Tommaso Merciai
> <tommaso.merciai.xr@bp.renesas.com> wrote:
> > Enable control of USB2.0 VBUSEN via the VBUS_SEL bit in the VBENCTL
> > register. According to the RZ/V2H(P) SoC hardware manual, OTG channels
> > require VBUS_SEL set, while HOST-only channels require it cleared.
> >
> > Add `#mux-state-cell` to the usb20phyrst and usb21phyrst reset
> > nodes to expose them as mux controllers.
> >
> > Set the required mux-states in usb2_phy0 (OTG: state 1) and usb2_phy1
> > (HOST: state 0) nodes.
> >
> > This enables proper VBUSEN management for OTG and HOST-only USB2.0
> > channels.
> >
> > Signed-off-by: Tommaso Merciai <tommaso.merciai.xr@bp.renesas.com>
>
> Thanks for your patch!
>
> > --- a/arch/arm64/boot/dts/renesas/r9a09g057.dtsi
> > +++ b/arch/arm64/boot/dts/renesas/r9a09g057.dtsi
> > @@ -1034,6 +1034,7 @@ usb2_phy0: usb-phy@15800200 {
> > resets = <&usb20phyrst>;
> > #phy-cells = <1>;
> > power-domains = <&cpg>;
> > + mux-states = <&usb20phyrst 1>;
>
> I am no USB expert, and didn't really follow the USB specifics of
> this series, but isn't the selection of host (VBUSEN = 1) or function
> (VBUSEN = 0) mode decided at runtime?
usb2_phy0 -> OTG channels -> VBUS_SEL = 1
For peripheral/function will be used (VBOUT bit):
usb2_phy0_vbus_otg: vbus-regulator {
regulator-name = "USB2PHY0-VBUS-OTG";
status = "disabled";
};
[0] Will drive the VBUSEN signal at runtime using [1].
>
> > status = "disabled";
> > };
> >
> > @@ -1047,6 +1048,7 @@ usb2_phy1: usb-phy@15810200 {
> > resets = <&usb21phyrst>;
> > #phy-cells = <1>;
> > power-domains = <&cpg>;
> > + mux-states = <&usb21phyrst 0>;
>
> The second controller is always used in host mode, so 0 is correct.
>
Right in this way we will have:
usb2_phy1 -> HOST only channel -> VBUS_SEL = 0
and not VBOUT bit.
[0] https://patchwork.kernel.org/project/linux-renesas-soc/patch/c67fcdbc2c6d1694b785d7240d368490037a82fa.1764241212.git.tommaso.merciai.xr@bp.renesas.com/
[1] https://patchwork.kernel.org/project/linux-renesas-soc/patch/4618b939734fcfe1f153e725ac178844b44d9a3f.1764241212.git.tommaso.merciai.xr@bp.renesas.com/
Thanks & Regards,
Tommaso
> > status = "disabled";
> > };
> >
>
> Gr{oetje,eeting}s,
>
> Geert
>
> --
> Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
>
> In personal conversations with technical people, I call myself a hacker. But
> when I'm talking to journalists I just say "programmer" or something like that.
> -- Linus Torvalds
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH v5 15/22] arm64: dts: renesas: r9a09g057: Add USB2.0 VBUS_SEL mux-controller support
From: Geert Uytterhoeven @ 2026-01-26 16:59 UTC (permalink / raw)
To: Tommaso Merciai
Cc: tomm.merciai, linux-renesas-soc, biju.das.jz, Peter Rosin,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Vinod Koul,
Kishon Vijay Abraham I, Fabrizio Castro, Lad Prabhakar,
Philipp Zabel, Yoshihiro Shimoda, Magnus Damm, Guenter Roeck,
Mark Brown, Jonathan Cameron, Georgi Djakov, Bartosz Golaszewski,
Arnd Bergmann, Greg Kroah-Hartman, devicetree, linux-kernel,
linux-phy
In-Reply-To: <63e8022438eb0d485505c262cac383d76c804403.1764241212.git.tommaso.merciai.xr@bp.renesas.com>
Hi Tommaso,
On Thu, 27 Nov 2025 at 12:51, Tommaso Merciai
<tommaso.merciai.xr@bp.renesas.com> wrote:
> Enable control of USB2.0 VBUSEN via the VBUS_SEL bit in the VBENCTL
> register. According to the RZ/V2H(P) SoC hardware manual, OTG channels
> require VBUS_SEL set, while HOST-only channels require it cleared.
>
> Add `#mux-state-cell` to the usb20phyrst and usb21phyrst reset
> nodes to expose them as mux controllers.
>
> Set the required mux-states in usb2_phy0 (OTG: state 1) and usb2_phy1
> (HOST: state 0) nodes.
>
> This enables proper VBUSEN management for OTG and HOST-only USB2.0
> channels.
>
> Signed-off-by: Tommaso Merciai <tommaso.merciai.xr@bp.renesas.com>
Thanks for your patch!
> --- a/arch/arm64/boot/dts/renesas/r9a09g057.dtsi
> +++ b/arch/arm64/boot/dts/renesas/r9a09g057.dtsi
> @@ -1034,6 +1034,7 @@ usb2_phy0: usb-phy@15800200 {
> resets = <&usb20phyrst>;
> #phy-cells = <1>;
> power-domains = <&cpg>;
> + mux-states = <&usb20phyrst 1>;
I am no USB expert, and didn't really follow the USB specifics of
this series, but isn't the selection of host (VBUSEN = 1) or function
(VBUSEN = 0) mode decided at runtime?
> status = "disabled";
> };
>
> @@ -1047,6 +1048,7 @@ usb2_phy1: usb-phy@15810200 {
> resets = <&usb21phyrst>;
> #phy-cells = <1>;
> power-domains = <&cpg>;
> + mux-states = <&usb21phyrst 0>;
The second controller is always used in host mode, so 0 is correct.
> status = "disabled";
> };
>
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH v4 2/3] dt-bindings: phy: ti,phy-usb3: convert to DT schema
From: Rob Herring (Arm) @ 2026-01-26 15:43 UTC (permalink / raw)
To: Charan Pedumuru
Cc: Conor Dooley, Kishon Vijay Abraham I, Andreas Kemnade,
Roger Quadros, Kevin Hilman, Tony Lindgren, Vinod Koul,
Neil Armstrong, Aaro Koskinen, Roger Quadros, linux-omap,
linux-phy, Krzysztof Kozlowski, devicetree, linux-kernel
In-Reply-To: <20260123-ti-phy-v4-2-b557e2c46e6f@gmail.com>
On Fri, 23 Jan 2026 15:39:03 +0000, Charan Pedumuru wrote:
> Convert TI PIPE3 PHY binding to DT schema.
> Changes during conversion:
> - Define a new pattern 'pcie-phy' to match nodes defined in DT.
> - Drop obsolete "id" property from the schema.
>
> Signed-off-by: Charan Pedumuru <charan.pedumuru@gmail.com>
> ---
> .../devicetree/bindings/phy/ti,phy-usb3.yaml | 138 +++++++++++++++++++++
> 1 file changed, 138 insertions(+)
>
Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH v6 23/24] scsi: ufs: mediatek: Remove undocumented "clk-scale-up-vcore-min"
From: AngeloGioacchino Del Regno @ 2026-01-26 14:53 UTC (permalink / raw)
To: Nicolas Frattaroli, Alim Akhtar, Avri Altman, Bart Van Assche,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Matthias Brugger,
Chunfeng Yun, Vinod Koul, Kishon Vijay Abraham I, Peter Wang,
Stanley Jhu, James E.J. Bottomley, Martin K. Petersen,
Philipp Zabel, Liam Girdwood, Mark Brown, Chaotian Jing,
Neil Armstrong
Cc: Louis-Alexis Eyraud, kernel, linux-scsi, devicetree, linux-kernel,
linux-arm-kernel, linux-mediatek, linux-phy
In-Reply-To: <20260124-mt8196-ufs-v6-23-e7c005b60028@collabora.com>
Il 24/01/26 13:01, Nicolas Frattaroli ha scritto:
> The MediaTek UFS driver contains support for an undocumented,
> non-vendor-prefixed u32 property named "clk-scale-up-vcore-min".
>
> Since it is not part of any binding, and would not pass a bindings
> review in its current form, remove it.
>
> To return this functionality, it needs to be resubmitted in a series
> that also introduces it to the binding, and justifies what it is used
> for. Compatibility with downstream device trees is not a valid
> justification for its existence.
>
> Signed-off-by: Nicolas Frattaroli <nicolas.frattaroli@collabora.com>
Not sure what this is used for, because then UFS DVFS for gears should be managed
with OPPs (either dynamic or static) anyway, so the vcore (I guess this is the scp
vcore in dvfsrc regulators) should be scaled like so, without ugly init hacks like
the one that you just removed.
Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH 2/4] phy: s32g: Add serdes subsystem phy
From: Philipp Zabel @ 2026-01-26 13:11 UTC (permalink / raw)
To: Vincent Guittot, vkoul, neil.armstrong, krzk+dt, conor+dt,
ciprianmarian.costea, s32, linux, ghennadi.procopciuc,
bogdan-gabriel.roman, Ionut.Vicovan, alexandru-catalin.ionita,
linux-phy, devicetree, linux-kernel, linux-arm-kernel, netdev
Cc: Frank.li
In-Reply-To: <20260126092159.815968-3-vincent.guittot@linaro.org>
On Mo, 2026-01-26 at 10:21 +0100, Vincent Guittot wrote:
> 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 <ciprianmarian.costea@oss.nxp.com>
> Signed-off-by: Ciprian Marian Costea <ciprianmarian.costea@oss.nxp.com>
> Co-developed-by: Alexandru-Catalin Ionita <alexandru-catalin.ionita@nxp.com>
> Signed-off-by: Alexandru-Catalin Ionita <alexandru-catalin.ionita@nxp.com>
> Co-developed-by: Ghennadi Procopciuc <ghennadi.procopciuc@nxp.com>
> Signed-off-by: Ghennadi Procopciuc <ghennadi.procopciuc@nxp.com>
> Co-developed-by: Ionut Vicovan <Ionut.Vicovan@nxp.com>
> Signed-off-by: Ionut Vicovan <Ionut.Vicovan@nxp.com>
> Co-developed-by: Bogdan Roman <bogdan-gabriel.roman@nxp.com>
> Signed-off-by: Bogdan Roman <bogdan-gabriel.roman@nxp.com>
> Signed-off-by: Vincent Guittot <vincent.guittot@linaro.org>
> ---
> 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/phy-nxp-s32g-serdes.c b/drivers/phy/freescale/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 @@
[...]
> +static int s32g_serdes_get_ctrl_resources(struct platform_device *pdev, struct s32g_serdes *serdes)
> +{
[...]
> + ctrl->rst = devm_reset_control_get(dev, "serdes");
Please use devm_reset_control_get_exclusive() directly.
[...]
> +static int s32g_serdes_get_pcie_resources(struct platform_device *pdev, struct s32g_serdes *serdes)
> +{
[...]
> + pcie->rst = devm_reset_control_get(dev, "pcie");
Same here.
regards
Philipp
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH v2 6/6] phy: qcom: snps-femto-v2: Fix possible NULL-deref on early runtime suspend
From: Abel Vesa @ 2026-01-26 12:03 UTC (permalink / raw)
To: Loic Poulain
Cc: vkoul, kishon, linux-arm-msm, linux-phy, dmitry.baryshkov,
neil.armstrong, konrad.dybcio
In-Reply-To: <20260121142827.2583-7-loic.poulain@oss.qualcomm.com>
On 26-01-21 15:28:27, Loic Poulain wrote:
> Enabling runtime PM before attaching the hsphy instance as driver data
> can lead to a NULL pointer dereference in runtime PM callbacks that
> expect valid driver data. There is a small window where the suspend
> callback may run after PM runtime enabling and before runtime forbid.
>
> Attach the hsphy instance as driver data before enabling runtime PM to
> prevent NULL pointer dereference in runtime PM callbacks.
>
> Reorder pm_runtime_enable() and pm_runtime_forbid() to prevent a
> short window where an unnecessary runtime suspend can occur.
>
> Use the devres-managed version to ensure PM runtime is symmetrically
> disabled during driver removal for proper cleanup.
>
> Fixes: 0d75f508a9d5 ("phy: qcom-snps: Add runtime suspend and resume handlers")
> Signed-off-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
> Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
Reviewed-by: Abel Vesa <abel.vesa@oss.qualcomm.com>
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH v2 5/6] phy: qcom: qmp-usb-legacy: Prevent unnecessary PM runtime suspend at boot
From: Abel Vesa @ 2026-01-26 12:03 UTC (permalink / raw)
To: Loic Poulain
Cc: vkoul, kishon, linux-arm-msm, linux-phy, dmitry.baryshkov,
neil.armstrong, konrad.dybcio
In-Reply-To: <20260121142827.2583-6-loic.poulain@oss.qualcomm.com>
On 26-01-21 15:28:26, Loic Poulain wrote:
> There is a small window where the device can suspend after
> pm_runtime_enable() and before pm_runtime_forbid(), causing an
> unnecessary suspend/resume cycle while the PHY is not yet registered.
>
> Move pm_runtime_forbid() before pm_runtime_enable() to eliminate
> this race.
>
> Signed-off-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
Reviewed-by: Abel Vesa <abel.vesa@oss.qualcomm.com>
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH v2 4/6] phy: qcom: qmp-usb-legacy: Fix possible NULL-deref on early runtime suspend
From: Abel Vesa @ 2026-01-26 12:02 UTC (permalink / raw)
To: Loic Poulain
Cc: vkoul, kishon, linux-arm-msm, linux-phy, dmitry.baryshkov,
neil.armstrong, konrad.dybcio
In-Reply-To: <20260121142827.2583-5-loic.poulain@oss.qualcomm.com>
On 26-01-21 15:28:25, Loic Poulain wrote:
> There is a small window where the runtime suspend callback may run
> after pm_runtime_enable() and before pm_runtime_forbid(). In this
> case, a crash occurs because runtime suspend/resume dereferences
> qmp->phy pointer, which is not yet initialized:
> `if (!qmp->phy->init_count) {`
>
> This can also happen if user re-enables runtime-pm via the sysfs
> attribute before qmp phy is initialized.
>
> Similarly to other qcom phy drivers, introduce a qmp->phy_initialized
> variable that can be used to avoid relying on the possibly uninitialized
> phy pointer.
>
> Fixes: e464a3180a43 ("phy: qcom-qmp-usb: split off the legacy USB+dp_com support")
> Signed-off-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
Reviewed-by: Abel Vesa <abel.vesa@oss.qualcomm.com>
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH v2 3/6] phy: qcom: qmp-usbc: Prevent unnecessary PM runtime suspend at boot
From: Abel Vesa @ 2026-01-26 12:02 UTC (permalink / raw)
To: Loic Poulain
Cc: vkoul, kishon, linux-arm-msm, linux-phy, dmitry.baryshkov,
neil.armstrong, konrad.dybcio
In-Reply-To: <20260121142827.2583-4-loic.poulain@oss.qualcomm.com>
On 26-01-21 15:28:24, Loic Poulain wrote:
> There is a small window where the device can suspend after
> pm_runtime_enable() and before pm_runtime_forbid(), causing an
> unnecessary suspend/resume cycle while the PHY is not yet registered.
>
> Move pm_runtime_forbid() before pm_runtime_enable() to eliminate
> this race.
>
> Signed-off-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
Reviewed-by: Abel Vesa <abel.vesa@oss.qualcomm.com>
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH v2 1/6] phy: qcom: qmp-combo: Prevent unnecessary PM runtime suspend at boot
From: Abel Vesa @ 2026-01-26 12:01 UTC (permalink / raw)
To: Loic Poulain
Cc: vkoul, kishon, linux-arm-msm, linux-phy, dmitry.baryshkov,
neil.armstrong, konrad.dybcio
In-Reply-To: <20260121142827.2583-2-loic.poulain@oss.qualcomm.com>
On 26-01-21 15:28:22, Loic Poulain wrote:
> There is a small window where the device can suspend after
> pm_runtime_enable() and before pm_runtime_forbid(), causing an
> unnecessary suspend/resume cycle while the PHY is not yet registered.
>
> Move pm_runtime_forbid() before pm_runtime_enable() to eliminate
> this race.
>
> Signed-off-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
> Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
Reviewed-by: Abel Vesa <abel.vesa@oss.qualcomm.com>
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* [PATCH 4/4] MAINTAINERS: Add MAINTAINER for NXP S32G Serdes driver
From: Vincent Guittot @ 2026-01-26 9:21 UTC (permalink / raw)
To: vkoul, neil.armstrong, krzk+dt, conor+dt, ciprianmarian.costea,
s32, p.zabel, linux, ghennadi.procopciuc, bogdan-gabriel.roman,
Ionut.Vicovan, alexandru-catalin.ionita, linux-phy, devicetree,
linux-kernel, linux-arm-kernel, netdev
Cc: Frank.li
In-Reply-To: <20260126092159.815968-1-vincent.guittot@linaro.org>
Add a new entry for S32G Serdes driver.
Signed-off-by: Vincent Guittot <vincent.guittot@linaro.org>
---
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
+ARM/NXP S32G SERDES DRIVER
+M: Ciprian Marian Costea <ciprianmarian.costea@oss.nxp.com>
+R: NXP S32 Linux Team <s32@nxp.com>
+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 <alex@digriz.org.uk>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
--
2.43.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* [PATCH 3/4] phy: s32g: Add serdes xpcs subsystem
From: Vincent Guittot @ 2026-01-26 9:21 UTC (permalink / raw)
To: vkoul, neil.armstrong, krzk+dt, conor+dt, ciprianmarian.costea,
s32, p.zabel, linux, ghennadi.procopciuc, bogdan-gabriel.roman,
Ionut.Vicovan, alexandru-catalin.ionita, linux-phy, devicetree,
linux-kernel, linux-arm-kernel, netdev
Cc: Frank.li
In-Reply-To: <20260126092159.815968-1-vincent.guittot@linaro.org>
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 <ciprianmarian.costea@oss.nxp.com>
Signed-off-by: Ciprian Marian Costea <ciprianmarian.costea@oss.nxp.com>
Co-developed-by: Alexandru-Catalin Ionita <alexandru-catalin.ionita@nxp.com>
Signed-off-by: Alexandru-Catalin Ionita <alexandru-catalin.ionita@nxp.com>
Co-developed-by: Ghennadi Procopciuc <ghennadi.procopciuc@nxp.com>
Signed-off-by: Ghennadi Procopciuc <ghennadi.procopciuc@nxp.com>
Co-developed-by: Ionut Vicovan <Ionut.Vicovan@nxp.com>
Signed-off-by: Ionut Vicovan <Ionut.Vicovan@nxp.com>
Co-developed-by: Bogdan Roman <bogdan-gabriel.roman@nxp.com>
Signed-off-by: Bogdan Roman <bogdan-gabriel.roman@nxp.com>
Signed-off-by: Vincent Guittot <vincent.guittot@linaro.org>
---
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) += phy-fsl-imx8m-pcie.o
obj-$(CONFIG_PHY_FSL_IMX8QM_HSIO) += phy-fsl-imx8qm-hsio.o
obj-$(CONFIG_PHY_FSL_LYNX_28G) += phy-fsl-lynx-28g.o
obj-$(CONFIG_PHY_FSL_SAMSUNG_HDMI_PHY) += phy-fsl-samsung-hdmi.o
-obj-$(CONFIG_PHY_S32G_SERDES) += phy-nxp-s32g-serdes.o
+obj-$(CONFIG_PHY_S32G_SERDES) += phy-nxp-s32g-serdes.o phy-nxp-s32g-xpcs.o
diff --git a/drivers/phy/freescale/phy-nxp-s32g-serdes.c b/drivers/phy/freescale/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 <linux/module.h>
#include <linux/of_platform.h>
#include <linux/of_address.h>
+#include <linux/pcs/pcs-nxp-xpcs.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/processor.h>
#include <linux/reset.h>
#include <linux/units.h>
+#include "phy-nxp-s32g-xpcs.h"
+
+#define S32G_SERDES_XPCS_MAX 2
#define S32G_SERDES_MODE_MAX 5
#define EXTERNAL_CLK_NAME "ext"
@@ -31,6 +35,52 @@
#define S32G_PCIE_PHY_MPLLA_CTRL 0x10
#define MPLL_STATE BIT(30)
+#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 @@
#define S32G_PHY_REG_DATA 0x4
+#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
@@ -75,16 +128,33 @@ struct s32g_pcie_ctrl {
bool powered_on;
};
+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;
};
/* PCIe phy subsystem */
#define S32G_SERDES_PCIE_FREQ (100 * HZ_PER_MHZ)
+static void s32g_pcie_phy_reset(struct s32g_serdes *serdes)
+{
+ u32 val;
+
+ val = 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 = &serdes->ctrl;
@@ -263,6 +333,187 @@ static struct phy *s32g_serdes_phy_xlate(struct device *dev,
return phy;
}
+/* XPCS subsystem */
+
+static int s32g_serdes_xpcs_init(struct s32g_serdes *serdes, int id)
+{
+ struct s32g_serdes_ctrl *ctrl = &serdes->ctrl;
+ struct s32g_xpcs_ctrl *xpcs = &serdes->xpcs;
+ enum pcie_xpcs_mode shared = NOT_SHARED;
+ unsigned long rate = ctrl->ref_clk_rate;
+ struct device *dev = serdes->dev;
+ void __iomem *base;
+
+ if (!id)
+ base = xpcs->base0;
+ else
+ base = xpcs->base1;
+
+ if (ctrl->ss_mode == 1 || ctrl->ss_mode == 2)
+ shared = PCIE_XPCS_1G;
+ else if (ctrl->ss_mode == 5)
+ shared = 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 = readl(serdes->ctrl.ss_base + S32G_PCIE_PHY_EXT_MISC_CTRL_2);
+ val &= ~(EXT_TX_VBOOST_LVL_MASK | EXT_TX_TERM_CTRL_MASK);
+ val |= 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 = readl(serdes->ctrl.ss_base + S32G_PCIE_PHY_EXT_CTRL_SEL);
+ val |= EXT_PHY_CTRL_SEL;
+ writel(val, serdes->ctrl.ss_base + S32G_PCIE_PHY_EXT_CTRL_SEL);
+
+ /* Configure ref range, disable PLLB/ref div2 */
+ val = readl(serdes->ctrl.ss_base + S32G_PCIE_PHY_REF_CLK_CTRL);
+ val &= ~(REF_CLK_DIV2_EN | REF_CLK_MPLLB_DIV2_EN | EXT_REF_RANGE_MASK);
+ val |= FIELD_PREP(EXT_REF_RANGE_MASK, 0x3);
+ writel(val, serdes->ctrl.ss_base + S32G_PCIE_PHY_REF_CLK_CTRL);
+
+ /* Configure multiplier */
+ val = readl(serdes->ctrl.ss_base + S32G_PCIE_PHY_EXT_MPLLB_CTRL_2);
+ val &= ~(MPLLB_MULTIPLIER_MASK | EXT_MPLLB_FRACN_CTRL_MASK | BIT(24) | BIT(28));
+ val |= 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 = readl(serdes->ctrl.ss_base + S32G_PCIE_PHY_MPLLB_CTRL);
+ val &= ~MPLLB_SSC_EN;
+ writel(val, serdes->ctrl.ss_base + S32G_PCIE_PHY_MPLLB_CTRL);
+
+ /* Configure tx lane division, disable word clock div2*/
+ val = readl(serdes->ctrl.ss_base + S32G_PCIE_PHY_EXT_MPLLB_CTRL_3);
+ val &= ~(EXT_MPLLB_WORD_DIV2_EN | EXT_MPLLB_TX_CLK_DIV_MASK);
+ val |= 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 = readl(serdes->ctrl.ss_base + S32G_PCIE_PHY_EXT_MPLLB_CTRL_1);
+ val &= ~(EXT_MPLLB_BANDWIDTH_MASK | EXT_MPLLB_DIV_CLK_EN |
+ EXT_MPLLB_DIV8_CLK_EN | EXT_MPLLB_DIV_MULTIPLIER_MASK);
+ val |= 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 = readl(serdes->ctrl.ss_base + S32G_PCIE_PHY_EXT_MPLLA_CTRL_1);
+ val &= ~(EXT_MPLLA_BANDWIDTH_MASK);
+ val |= FIELD_PREP(EXT_MPLLA_BANDWIDTH_MASK, 0xc5);
+ writel(val, serdes->ctrl.ss_base + S32G_PCIE_PHY_EXT_MPLLA_CTRL_1);
+
+ /* Configure VCO */
+ val = readl(serdes->ctrl.ss_base + S32G_PCIE_PHY_XPCS1_RX_OVRD_CTRL);
+ val &= ~(XPCS1_RX_VCO_LD_VAL_MASK | XPCS1_RX_REF_LD_VAL_MASK);
+ val |= 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 = readl(serdes->ctrl.ss_base + S32G_PCIE_PHY_EXT_BS_CTRL);
+ val &= ~(EXT_BS_RX_LEVEL_MASK | EXT_BS_TX_LOWSWING);
+ val |= 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 = readl(serdes->ctrl.ss_base + S32G_PCIE_PHY_EXT_MISC_CTRL_1);
+ val &= ~(EXT_RX_LOS_THRESHOLD_MASK | EXT_RX_VREF_CTRL_MASK);
+ val |= 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 = &serdes->ctrl;
+ struct s32g_xpcs_ctrl *xpcs = &serdes->xpcs;
+ int ret, order[2], xpcs_id;
+ size_t i;
+
+ switch (ctrl->ss_mode) {
+ case 0:
+ return 0;
+ case 1:
+ order[0] = 0;
+ order[1] = XPCS_DISABLED;
+ break;
+ case 2:
+ case 5:
+ order[0] = 1;
+ order[1] = XPCS_DISABLED;
+ break;
+ case 3:
+ order[0] = 1;
+ order[1] = 0;
+ break;
+ case 4:
+ order[0] = 0;
+ order[1] = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(order); i++) {
+ xpcs_id = order[i];
+
+ if (xpcs_id == XPCS_DISABLED)
+ continue;
+
+ ret = s32g_xpcs_init_plls(xpcs->phys[xpcs_id]);
+ if (ret)
+ return ret;
+ }
+
+ if (ctrl->ss_mode == 5) {
+ s32g_serdes_prepare_pma_mode5(serdes);
+
+ ret = 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 = 0; i < ARRAY_SIZE(order); i++) {
+ xpcs_id = order[i];
+
+ if (xpcs_id == XPCS_DISABLED)
+ continue;
+
+ ret = s32g_xpcs_vreset(xpcs->phys[xpcs_id]);
+ if (ret)
+ return ret;
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(order); i++) {
+ xpcs_id = order[i];
+
+ if (xpcs_id == XPCS_DISABLED)
+ continue;
+
+ ret = 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 */
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;
}
+ /*
+ * 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 = 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);
- return 0;
+ ret = s32g_serdes_init_clks(serdes);
+ if (ret) {
+ dev_err(serdes->dev, "XPCS init failed\n");
+ goto disable_clks;
+ }
+
+ return ret;
disable_clks:
clk_bulk_disable_unprepare(serdes->ctrl.nclks,
@@ -433,12 +694,32 @@ static int s32g_serdes_get_pcie_resources(struct platform_device *pdev, struct s
return 0;
}
+static int s32g_serdes_get_xpcs_resources(struct platform_device *pdev, struct s32g_serdes *serdes)
+{
+ struct s32g_xpcs_ctrl *xpcs = &serdes->xpcs;
+ struct device *dev = &pdev->dev;
+
+ xpcs->base0 = 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 = 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 device_node *child_node)
{
struct s32g_serdes_ctrl *ctrl = &serdes->ctrl;
struct phy_provider *phy_provider;
struct device *dev = serdes->dev;
- int ss_mode = ctrl->ss_mode;
+ int ret, ss_mode = ctrl->ss_mode;
struct phy *phy;
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);
+ } else if (of_device_is_compatible(child_node, "nxp,s32g2-serdes-xpcs")) {
+ struct s32g_xpcs_ctrl *xpcs_ctrl = &serdes->xpcs;
+ struct s32g_xpcs *xpcs;
+ int port;
+
+ /* no Ethernet phy lane */
+ if (ss_mode == 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 == 1 && port == 1)
+ return -EINVAL;
+
+ /* XPCS0 is not used */
+ if (ss_mode == 2 && port == 0)
+ return -EINVAL;
+
+ xpcs = 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] = xpcs;
+
+ xpcs->an = of_property_read_bool(dev->of_node, "nxp,xpcs_an");
+
+ ret = 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 *pdev)
if (ret)
return ret;
+ ret = s32g_serdes_get_xpcs_resources(pdev, serdes);
+ if (ret)
+ return ret;
+
ret = s32g_serdes_parse_lanes(dev, serdes);
if (ret)
return ret;
@@ -541,6 +861,43 @@ static int __maybe_unused s32g_serdes_resume(struct device *device)
return ret;
}
+struct phylink_pcs *s32g_serdes_pcs_create(struct device *dev, struct device_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 = 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 = 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 = 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[] = {
{
.compatible = "nxp,s32g2-serdes",
diff --git a/drivers/phy/freescale/phy-nxp-s32g-xpcs.c b/drivers/phy/freescale/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 <linux/device.h>
+#include <linux/errno.h>
+#include <linux/units.h>
+#include <linux/io.h>
+#include <linux/processor.h>
+#include <linux/regmap.h>
+#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[] = {
+ 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 = {
+ .yes_ranges = s32g_xpcs_wr_ranges,
+ .n_yes_ranges = ARRAY_SIZE(s32g_xpcs_wr_ranges),
+};
+
+static const struct regmap_range s32g_xpcs_rd_ranges[] = {
+ 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 = {
+ .yes_ranges = s32g_xpcs_rd_ranges,
+ .n_yes_ranges = 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 = context;
+ u16 ofsleft = (reg >> 8) & 0xffffU;
+ u16 ofsright = (reg & 0xffU);
+
+ writew(ofsleft, xpcs->base + ADDR1_OFS);
+ *result = 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 = context;
+ u16 ofsleft = (reg >> 8) & 0xffffU;
+ u16 ofsright = (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 = {
+ .reg_bits = 16,
+ .val_bits = 16,
+ .reg_read = s32g_xpcs_regmap_reg_read,
+ .reg_write = s32g_xpcs_regmap_reg_write,
+ .wr_table = &s32g_xpcs_wr_table,
+ .rd_table = &s32g_xpcs_rd_table,
+ .max_register = 0x1F80E1,
+};
+
+static void s32g_xpcs_write_bits(struct s32g_xpcs *xpcs, unsigned int reg,
+ unsigned int mask, unsigned int value)
+{
+ int ret = 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 = 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 reg)
+{
+ unsigned int val = 0;
+ int ret;
+
+ ret = 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 = 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 = 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 = 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 = ktime_add_ms(ktime_get(), XPCS_TIMEOUT_MS);
+
+ spin_until_cond((cur = ktime_get(),
+ (s32g_xpcs_read(xpcs, reg) & mask) == bits ||
+ ktime_after(cur, timeout)));
+ if ((s32g_xpcs_read(xpcs, reg) & mask) != 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) == 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 = 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 = 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 = 0;
+
+ ret = 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] = 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 = 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 = 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 vco_pll)
+{
+ unsigned int vco_ld = 0;
+ unsigned int vco_ref = 0;
+ unsigned int rx_baud = 0;
+ unsigned int tx_baud = 0;
+
+ switch (vco_pll) {
+ case XPCS_PLLA:
+ if (xpcs->mhz125) {
+ vco_ld = FIELD_PREP(VCO_LD_VAL_0_MASK, 1360);
+ vco_ref = FIELD_PREP(VCO_REF_LD_0_MASK, 17);
+ } else {
+ vco_ld = FIELD_PREP(VCO_LD_VAL_0_MASK, 1350);
+ vco_ref = FIELD_PREP(VCO_REF_LD_0_MASK, 27);
+ }
+
+ rx_baud = FIELD_PREP(RX0_RATE_MASK, RX0_BAUD_DIV_8);
+ tx_baud = FIELD_PREP(TX0_RATE_MASK, TX0_BAUD_DIV_4);
+ break;
+ case XPCS_PLLB:
+ if (xpcs->mhz125) {
+ vco_ld = FIELD_PREP(VCO_LD_VAL_0_MASK, 1350);
+ vco_ref = FIELD_PREP(VCO_REF_LD_0_MASK, 27);
+ } else {
+ vco_ld = FIELD_PREP(VCO_LD_VAL_0_MASK, 1344);
+ vco_ref = FIELD_PREP(VCO_REF_LD_0_MASK, 43);
+ }
+
+ rx_baud = FIELD_PREP(RX0_RATE_MASK, RX0_BAUD_DIV_2);
+ tx_baud = 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 == 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 = 0;
+ if (xpcs->ext_clk)
+ val |= REF_USE_PAD;
+
+ if (xpcs->mhz125) {
+ val |= REF_MPLLA_DIV2;
+ val |= REF_CLK_DIV2;
+ val |= FIELD_PREP(REF_RANGE_MASK, RANGE_52_78_MHZ);
+ } else {
+ val |= 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 = FIELD_PREP(MLLA_MULTIPLIER_MASK, 80);
+ else
+ val = 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 = FIELD_PREP(MPLLA_BANDWIDTH_MASK, 43);
+ else
+ val = 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 = 0;
+ if (xpcs->ext_clk)
+ val |= REF_USE_PAD;
+
+ if (xpcs->mhz125) {
+ val |= REF_MPLLB_DIV2;
+ val |= REF_CLK_DIV2;
+ val |= FIELD_PREP(REF_RANGE_MASK, RANGE_52_78_MHZ);
+ } else {
+ val |= 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 = 125 << MLLB_MULTIPLIER_OFF;
+ else
+ val = 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 = FIELD_PREP(MPLLB_FRACN_CTRL_MASK, 0);
+ else
+ val = 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 = FIELD_PREP(MPLLB_BANDWIDTH_MASK, 68);
+ else
+ val = 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_CTL,
+ 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 = 0;
+ struct device *dev = xpcs->dev;
+
+ /* Configure XPCS speed and VCO */
+ if (target_pll == 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 == 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 = 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 = 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 = 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 = xpcs->dev;
+ unsigned int mii_ctrl, val, ss;
+ bool ss6, ss13, an_enabled, intr_en;
+
+ mii_ctrl = s32g_xpcs_read(xpcs, SR_MII_CTRL);
+ an_enabled = !!(mii_ctrl & AN_ENABLE);
+ intr_en = !!(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 = SPEED_UNKNOWN;
+ state->link = 0;
+ state->duplex = DUPLEX_UNKNOWN;
+ state->an_complete = 0;
+ state->pause = MLO_PAUSE_NONE;
+ val = 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 = 1;
+ if (val & CL37_ANSGM_STS_LINK)
+ state->link = 1;
+ else
+ return 0;
+ if (val & CL37_ANSGM_STS_DUPLEX)
+ state->duplex = DUPLEX_FULL;
+ else
+ state->duplex = DUPLEX_HALF;
+ ss = FIELD_GET(CL37_ANSGM_STS_SPEED_MASK, val);
+ } else {
+ return 0;
+ }
+
+ } else {
+ /* MLO_AN_FIXED, MLO_AN_PHY */
+ val = s32g_xpcs_read(xpcs, SR_MII_STS);
+ state->link = !!(val & LINK_STS);
+ state->an_complete = 0;
+ state->pause = MLO_PAUSE_NONE;
+
+ if (mii_ctrl & DUPLEX_MODE)
+ state->duplex = DUPLEX_FULL;
+ else
+ state->duplex = 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 = !!(mii_ctrl & SS6);
+ ss13 = !!(mii_ctrl & SS13);
+ ss = ss6 << 1 | ss13;
+ }
+
+ switch (ss) {
+ case CL37_ANSGM_10MBPS:
+ state->speed = SPEED_10;
+ break;
+ case CL37_ANSGM_100MBPS:
+ state->speed = SPEED_100;
+ break;
+ case CL37_ANSGM_1000MBPS:
+ state->speed = SPEED_1000;
+ break;
+ default:
+ dev_err(dev, "Failed to interpret the value of SR_MII_CTRL\n");
+ break;
+ }
+
+ val = s32g_xpcs_read(xpcs, VR_MII_DIG_CTRL1);
+ if ((val & EN_2_5G_MODE) && state->speed == SPEED_1000)
+ state->speed = SPEED_2500;
+
+ /* Cover SGMII AN inability to distigunish between 1G and 2.5G */
+ if ((val & EN_2_5G_MODE) &&
+ state->speed != 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 = false;
+
+ an_enabled = 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 = xpcs->dev;
+ unsigned int val = 0, duplex = 0;
+ int ret = 0;
+ int speed = state.speed;
+ bool an_enabled;
+
+ /* Configure adaptive MII width */
+ s32g_xpcs_write_bits(xpcs, VR_MII_AN_CTRL, MII_CTRL, 0);
+
+ an_enabled = !!(s32g_xpcs_read(xpcs, SR_MII_CTRL) & AN_ENABLE);
+
+ dev_dbg(dev, "xpcs_%d: speed=%u duplex=%d an=%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 = 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 = SS13;
+ break;
+ case SPEED_1000:
+ val = SS6;
+ break;
+ case SPEED_2500:
+ val = SS6;
+ break;
+ default:
+ dev_err(dev, "Speed not supported\n");
+ break;
+ }
+
+ if (state.duplex == DUPLEX_FULL)
+ duplex = DUPLEX_MODE;
+
+ s32g_xpcs_write_bits(xpcs, SR_MII_CTRL, DUPLEX_MODE, duplex);
+
+ if (speed == SPEED_2500) {
+ ret = s32g_serdes_bifurcation_pll_transit(xpcs, XPCS_PLLB);
+ if (ret)
+ dev_err(dev, "Switch to PLLB failed\n");
+ } else {
+ ret = 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 = 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 = phylink_pcs_to_s32g_xpcs(pcs);
+ struct phylink_link_state state = { 0 };
+
+ if (!(neg_mode == 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 = phylink_pcs_to_s32g_xpcs(pcs);
+ struct phylink_link_state state = { 0 };
+
+ state.speed = speed;
+ state.duplex = duplex;
+ state.an_complete = false;
+
+ s32g_xpcs_config(xpcs, state);
+}
+
+static const struct phylink_pcs_ops s32cc_phylink_pcs_ops = {
+ .pcs_get_state = s32cc_phylink_pcs_get_state,
+ .pcs_config = s32cc_phylink_pcs_config,
+ .pcs_an_restart = s32cc_phylink_pcs_restart_an,
+ .pcs_link_up = 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 = 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 = 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 == NOT_SHARED) {
+ ret = s32g_xpcs_wait_power_good_state(xpcs);
+ if (ret)
+ return ret;
+ } else if (xpcs->pcie_shared == PCIE_XPCS_2G5) {
+ ret = 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 = s32g_xpcs_init_mplla(xpcs);
+ if (ret) {
+ dev_err(dev, "Failed to initialize PLLA\n");
+ return ret;
+ }
+ ret = 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 = (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 != (125 * HZ_PER_MHZ) && rate != (100 * HZ_PER_MHZ)) {
+ dev_err(dev, "XPCS cannot operate @%lu HZ\n", rate);
+ return -EINVAL;
+ }
+
+ xpcs->base = base;
+ xpcs->ext_clk = ext_clk;
+ xpcs->id = id;
+ xpcs->dev = dev;
+ xpcs->pcie_shared = pcie_shared;
+
+ if (rate == (125 * HZ_PER_MHZ))
+ xpcs->mhz125 = true;
+ else
+ xpcs->mhz125 = false;
+
+ conf = s32g_xpcs_regmap_config;
+
+ if (!id)
+ conf.name = "xpcs0";
+ else
+ conf.name = "xpcs1";
+
+ xpcs->regmap = 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 = &s32cc_phylink_pcs_ops;
+ xpcs->pcs.poll = 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/freescale/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 <linux/phy.h>
+#include <linux/phy/phy.h>
+#include <linux/types.h>
+#include <linux/phylink.h>
+
+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-xpcs.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 <linux/phylink.h>
+
+struct phylink_pcs *s32g_serdes_pcs_create(struct device *dev, struct device_node *np);
+
+#endif /* __LINUX_PCS_XPCS_H */
--
2.43.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* [PATCH 2/4] phy: s32g: Add serdes subsystem phy
From: Vincent Guittot @ 2026-01-26 9:21 UTC (permalink / raw)
To: vkoul, neil.armstrong, krzk+dt, conor+dt, ciprianmarian.costea,
s32, p.zabel, linux, ghennadi.procopciuc, bogdan-gabriel.roman,
Ionut.Vicovan, alexandru-catalin.ionita, linux-phy, devicetree,
linux-kernel, linux-arm-kernel, netdev
Cc: Frank.li
In-Reply-To: <20260126092159.815968-1-vincent.guittot@linaro.org>
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 <ciprianmarian.costea@oss.nxp.com>
Signed-off-by: Ciprian Marian Costea <ciprianmarian.costea@oss.nxp.com>
Co-developed-by: Alexandru-Catalin Ionita <alexandru-catalin.ionita@nxp.com>
Signed-off-by: Alexandru-Catalin Ionita <alexandru-catalin.ionita@nxp.com>
Co-developed-by: Ghennadi Procopciuc <ghennadi.procopciuc@nxp.com>
Signed-off-by: Ghennadi Procopciuc <ghennadi.procopciuc@nxp.com>
Co-developed-by: Ionut Vicovan <Ionut.Vicovan@nxp.com>
Signed-off-by: Ionut Vicovan <Ionut.Vicovan@nxp.com>
Co-developed-by: Bogdan Roman <bogdan-gabriel.roman@nxp.com>
Signed-off-by: Bogdan Roman <bogdan-gabriel.roman@nxp.com>
Signed-off-by: Vincent Guittot <vincent.guittot@linaro.org>
---
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) += phy-fsl-imx8m-pcie.o
obj-$(CONFIG_PHY_FSL_IMX8QM_HSIO) += phy-fsl-imx8qm-hsio.o
obj-$(CONFIG_PHY_FSL_LYNX_28G) += phy-fsl-lynx-28g.o
obj-$(CONFIG_PHY_FSL_SAMSUNG_HDMI_PHY) += phy-fsl-samsung-hdmi.o
+obj-$(CONFIG_PHY_S32G_SERDES) += phy-nxp-s32g-serdes.o
diff --git a/drivers/phy/freescale/phy-nxp-s32g-serdes.c b/drivers/phy/freescale/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 <dt-bindings/phy/phy.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/of_address.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/processor.h>
+#include <linux/reset.h>
+#include <linux/units.h>
+
+#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 = 0 mode.
+ */
+enum pcie_phy_mode {
+ CRNS = 0, /* Common Reference Clock, No Spread Spectrum */
+ CRSS = 1, /* Common Reference Clock, Spread Spectrum */
+ SRNS = 2, /* Separate Reference Clock, No Spread Spectrum */
+ SRIS = 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 = &serdes->ctrl;
+ unsigned long rate = sctrl->ref_clk_rate;
+
+ if (rate != 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 = readl(serdes->ctrl.ss_base + S32G_PCIE_PHY_MPLLA_CTRL);
+ const u32 mask = MPLL_STATE;
+
+ return (mplla & mask) == 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 = &serdes->ctrl;
+ struct s32g_pcie_ctrl *pcie = &serdes->pcie;
+ u32 reg;
+ int val, ret = 0;
+
+ ret = s32g_pcie_check_clk(serdes);
+ if (ret)
+ return ret;
+
+ reg = readl(sctrl->ss_base + S32G_PCIE_PHY_GEN_CTRL);
+
+ /* if PCIE PHY is in SRIS mode */
+ if (pcie->phy_mode == SRIS)
+ reg |= RX_SRIS_MODE;
+
+ if (sctrl->ext_clk)
+ reg |= REF_USE_PAD;
+ else
+ reg &= ~REF_USE_PAD;
+
+ writel(reg, sctrl->ss_base + S32G_PCIE_PHY_GEN_CTRL);
+
+ /* Monitor Serdes MPLL state */
+ ret = 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 = readl(sctrl->ss_base + S32G_SS_RW_REG_0);
+ reg |= 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 val)
+{
+ 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 = &serdes->pcie;
+ struct s32g_serdes_ctrl *ctrl = &serdes->ctrl;
+ u32 iq_ovrd_in;
+ int ret = 0;
+
+ ret = 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 = 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 == 0) {
+ iq_ovrd_in = 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 = true;
+
+ return 0;
+}
+
+/* PCIe phy ops function */
+
+static int s32g_serdes_phy_power_on(struct phy *p)
+{
+ struct s32g_serdes *serdes = 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 = phy_get_drvdata(p);
+
+ if (mode == 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 == CRSS || submode == SRIS) &&
+ serdes->ctrl.ss_mode != 0)
+ return -EINVAL;
+
+ /*
+ * Internal reference clock cannot be used with either Common clock
+ * or Spread spectrum, leaving only SRNSS
+ */
+ if (submode != SRNS && !serdes->ctrl.ext_clk)
+ return -EINVAL;
+
+ serdes->pcie.phy_mode = submode;
+
+ return 0;
+}
+
+static const struct phy_ops serdes_pcie_ops = {
+ .power_on = s32g_serdes_phy_power_on,
+ .set_mode = 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 = dev_get_drvdata(dev);
+ if (!serdes)
+ return ERR_PTR(-EINVAL);
+
+ phy = serdes->pcie.phy;
+
+ return phy;
+}
+
+/* Serdes subsystem */
+
+static int s32g_serdes_assert_reset(struct s32g_serdes *serdes)
+{
+ struct device *dev = serdes->dev;
+ int ret;
+
+ ret = reset_control_assert(serdes->pcie.rst);
+ if (ret) {
+ dev_err(dev, "Failed to assert PCIE reset: %d\n", ret);
+ return ret;
+ }
+
+ ret = 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 = serdes->dev;
+ int ret;
+
+ ret = reset_control_deassert(serdes->pcie.rst);
+ if (ret) {
+ dev_err(dev, "Failed to assert PCIE reset: %d\n", ret);
+ return ret;
+ }
+
+ ret = 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 = &serdes->ctrl;
+ u32 reg0;
+ int ret;
+
+ ret = clk_bulk_prepare_enable(ctrl->nclks, ctrl->clks);
+ if (ret) {
+ dev_err(serdes->dev, "Failed to enable SerDes clocks\n");
+ return ret;
+ }
+
+ ret = s32g_serdes_assert_reset(serdes);
+ if (ret)
+ goto disable_clks;
+
+ /* Set serdes mode */
+ reg0 = readl(ctrl->ss_base + S32G_SS_RW_REG_0);
+ reg0 &= ~SUBMODE_MASK;
+ if (ctrl->ss_mode == 5)
+ reg0 |= 2;
+ else
+ reg0 |= ctrl->ss_mode;
+ writel(reg0, ctrl->ss_base + S32G_SS_RW_REG_0);
+
+ /* Set Clock source: internal or external */
+ reg0 = readl(ctrl->ss_base + S32G_SS_RW_REG_0);
+ if (ctrl->ext_clk)
+ reg0 &= ~CLKEN_MASK;
+ else
+ reg0 |= 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 = 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, struct s32g_serdes *serdes)
+{
+ struct s32g_serdes_ctrl *ctrl = &serdes->ctrl;
+ struct device *dev = &pdev->dev;
+ int ret, idx;
+
+ ret = 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 = 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 = 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 = 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 = of_property_match_string(dev->of_node, "clock-names", EXTERNAL_CLK_NAME);
+ if (idx < 0)
+ idx = of_property_match_string(dev->of_node, "clock-names", INTERNAL_CLK_NAME);
+ else
+ ctrl->ext_clk = true;
+
+ if (idx < 0) {
+ dev_err(dev, "Failed to get Phy reference clock source\n");
+ return -EINVAL;
+ }
+
+ ctrl->ref_clk_rate = 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, struct s32g_serdes *serdes)
+{
+ struct s32g_pcie_ctrl *pcie = &serdes->pcie;
+ struct device *dev = &pdev->dev;
+
+ pcie->phy_base = 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 = 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 device_node *child_node)
+{
+ struct s32g_serdes_ctrl *ctrl = &serdes->ctrl;
+ struct phy_provider *phy_provider;
+ struct device *dev = serdes->dev;
+ int ss_mode = 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 = 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 = PHY_MODE_PCIE;
+ phy->id = 0;
+ serdes->pcie.phy = phy;
+
+ phy_provider = devm_of_phy_provider_register(&phy->dev, s32g_serdes_phy_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 = 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 = &pdev->dev;
+ int ret;
+
+ serdes = devm_kzalloc(dev, sizeof(*serdes), GFP_KERNEL);
+ if (!serdes)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, serdes);
+ serdes->dev = dev;
+
+ ret = s32g_serdes_get_ctrl_resources(pdev, serdes);
+ if (ret)
+ return ret;
+
+ ret = s32g_serdes_get_pcie_resources(pdev, serdes);
+ if (ret)
+ return ret;
+
+ ret = s32g_serdes_parse_lanes(dev, serdes);
+ if (ret)
+ return ret;
+
+ ret = s32g_serdes_init(serdes);
+
+ return ret;
+}
+
+static int __maybe_unused s32g_serdes_suspend(struct device *device)
+{
+ struct s32g_serdes *serdes = 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 = dev_get_drvdata(device);
+ struct s32g_pcie_ctrl *pcie = &serdes->pcie;
+ int ret;
+
+ ret = s32g_serdes_init(serdes);
+ if (ret) {
+ dev_err(device, "Failed to initialize\n");
+ return ret;
+ }
+
+ /* Restore PCIe phy power */
+ if (pcie->powered_on) {
+ ret = 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[] = {
+ {
+ .compatible = "nxp,s32g2-serdes",
+ },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, s32g_serdes_match);
+
+static const struct dev_pm_ops s32g_serdes_pm_ops = {
+ NOIRQ_SYSTEM_SLEEP_PM_OPS(s32g_serdes_suspend,
+ s32g_serdes_resume)
+};
+
+static struct platform_driver s32g_serdes_driver = {
+ .probe = s32g_serdes_probe,
+ .driver = {
+ .name = "phy-s32g-serdes",
+ .of_match_table = s32g_serdes_match,
+ .pm = &s32g_serdes_pm_ops,
+ },
+};
+module_platform_driver(s32g_serdes_driver);
+
+MODULE_AUTHOR("Ghennadi Procopciuc <ghennadi.procopciuc@nxp.com>");
+MODULE_DESCRIPTION("S32CC SerDes driver");
+MODULE_LICENSE("GPL");
--
2.43.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* [PATCH 1/4] dt-bindings: serdes: s32g: Add NXP serdes subsystem
From: Vincent Guittot @ 2026-01-26 9:21 UTC (permalink / raw)
To: vkoul, neil.armstrong, krzk+dt, conor+dt, ciprianmarian.costea,
s32, p.zabel, linux, ghennadi.procopciuc, bogdan-gabriel.roman,
Ionut.Vicovan, alexandru-catalin.ionita, linux-phy, devicetree,
linux-kernel, linux-arm-kernel, netdev
Cc: Frank.li
In-Reply-To: <20260126092159.815968-1-vincent.guittot@linaro.org>
Describe the serdes subsystem available on the S32G platforms.
Signed-off-by: Vincent Guittot <vincent.guittot@linaro.org>
---
.../bindings/phy/nxp,s32g-serdes.yaml | 154 ++++++++++++++++++
1 file changed, 154 insertions(+)
create mode 100644 Documentation/devicetree/bindings/phy/nxp,s32g-serdes.yaml
diff --git a/Documentation/devicetree/bindings/phy/nxp,s32g-serdes.yaml b/Documentation/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 <ghennadi.procopciuc@nxp.com>
+
+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 frequency
+ 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 = <2>;
+ #size-cells = <2>;
+ serdes0: serdes@40480000 {
+ compatible = "nxp,s32g3-serdes", "nxp,s32g2-serdes";
+ reg = <0x0 0x40480000 0x0 0x108>,
+ <0x0 0x40483008 0x0 0x10>,
+ <0x0 0x40482000 0x0 0x800>,
+ <0x0 0x40482800 0x0 0x800>;
+ reg-names = "ss_pcie", "pcie_phy", "xpcs0", "xpcs1";
+ clocks = <&clks 1>,
+ <&clks 2>,
+ <&clks 3>,
+ <&clks 4>,
+ <&serdes_100_ext>;
+ clock-names = "axi", "aux", "apb", "ref", "ext";
+ resets = <&reset 9>,
+ <&reset 8>;
+ reset-names = "serdes", "pcie";
+ nxp,sys-mode = <1>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ phy_pcie0: serdes0_lane@0 {
+ compatible = "nxp,s32g2-serdes-pcie-phy";
+ #phy-cells = <0>;
+ reg = <0>;
+ };
+ phy_xpcs0_0: serdes0_lane@1 {
+ compatible = "nxp,s32g2-serdes-xpcs";
+ reg = <0>;
+ };
+ };
+ };
--
2.43.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* [PATCH 0/4] Serdes: s32g: Add support for serdes subsystem
From: Vincent Guittot @ 2026-01-26 9:21 UTC (permalink / raw)
To: vkoul, neil.armstrong, krzk+dt, conor+dt, ciprianmarian.costea,
s32, p.zabel, linux, ghennadi.procopciuc, bogdan-gabriel.roman,
Ionut.Vicovan, alexandru-catalin.ionita, linux-phy, devicetree,
linux-kernel, linux-arm-kernel, netdev
Cc: Frank.li
s32g SoC family includes 2 serdes subsystems which are made of one PCIe
controller, 2 XPCS and a shared Phy. The Phy got 2 lanes that can be
configured to output PCIe lanes and/or SGMII.
Implement PCIe phy and XPCS support.
Vincent Guittot (4):
dt-bindings: serdes: s32g: Add NXP serdes subsystem
phy: s32g: Add serdes subsystem phy
phy: s32g: Add serdes xpcs subsystem
MAINTAINERS: Add MAINTAINER for NXP S32G Serdes driver
.../bindings/phy/nxp,s32g-serdes.yaml | 154 +++
MAINTAINERS | 9 +
drivers/phy/freescale/Kconfig | 10 +
drivers/phy/freescale/Makefile | 1 +
drivers/phy/freescale/phy-nxp-s32g-serdes.c | 926 ++++++++++++++
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 +
8 files changed, 2242 insertions(+)
create mode 100644 Documentation/devicetree/bindings/phy/nxp,s32g-serdes.yaml
create mode 100644 drivers/phy/freescale/phy-nxp-s32g-serdes.c
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
--
2.43.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* [PATCH] phy: fsl-imx8mq-usb: add control register regmap
From: Xu Yang @ 2026-01-26 6:03 UTC (permalink / raw)
To: vkoul, neil.armstrong, shawnguo, kernel, festevam, jun.li,
Frank.Li
Cc: linux-phy, imx, linux-arm-kernel, linux-kernel
The CR port is a simple 16-bit data/address parallel port that is
provided for on-chip access to the control registers inside the
USB 3.0 femtoPHY. Add control register regmap and export these
registers by debugfs to help PHY's diagnostic.
Signed-off-by: Xu Yang <xu.yang_2@nxp.com>
---
drivers/phy/freescale/phy-fsl-imx8mq-usb.c | 19 ++++++++++++++++++-
1 file changed, 18 insertions(+), 1 deletion(-)
diff --git a/drivers/phy/freescale/phy-fsl-imx8mq-usb.c b/drivers/phy/freescale/phy-fsl-imx8mq-usb.c
index b05d80e849a1..958d114b0c83 100644
--- a/drivers/phy/freescale/phy-fsl-imx8mq-usb.c
+++ b/drivers/phy/freescale/phy-fsl-imx8mq-usb.c
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0+
-/* Copyright (c) 2017 NXP. */
+/* Copyright 2017-2026 NXP. */
#include <linux/bitfield.h>
#include <linux/clk.h>
@@ -9,6 +9,7 @@
#include <linux/of.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
+#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/usb/typec_mux.h>
@@ -55,6 +56,8 @@
#define PHY_CTRL6_ALT_CLK_EN BIT(1)
#define PHY_CTRL6_ALT_CLK_SEL BIT(0)
+#define PHY_CRCTL 0x30
+
#define PHY_TUNE_DEFAULT 0xffffffff
#define TCA_CLK_RST 0x00
@@ -118,6 +121,7 @@ struct imx8mq_usb_phy {
void __iomem *base;
struct regulator *vbus;
struct tca_blk *tca;
+ struct regmap *cr_regmap;
u32 pcs_tx_swing_full;
u32 pcs_tx_deemph_3p5db;
u32 tx_vref_tune;
@@ -685,6 +689,14 @@ static const struct of_device_id imx8mq_usb_phy_of_match[] = {
};
MODULE_DEVICE_TABLE(of, imx8mq_usb_phy_of_match);
+static const struct regmap_config imx_cr_regmap_config = {
+ .name = "cr",
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = 0x7,
+};
+
static int imx8mq_usb_phy_probe(struct platform_device *pdev)
{
struct phy_provider *phy_provider;
@@ -713,6 +725,11 @@ static int imx8mq_usb_phy_probe(struct platform_device *pdev)
if (IS_ERR(imx_phy->base))
return PTR_ERR(imx_phy->base);
+ imx_phy->cr_regmap = devm_regmap_init_mmio(dev, imx_phy->base + PHY_CRCTL,
+ &imx_cr_regmap_config);
+ if (IS_ERR(imx_phy->cr_regmap))
+ return PTR_ERR(imx_phy->cr_regmap);
+
phy_ops = of_device_get_match_data(dev);
if (!phy_ops)
return -EINVAL;
--
2.34.1
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* [PATCH] phy: marvell: phy-mvebu-cp110-comphy: Remove unnecessary NULL check before clk_disable_unprepare()
From: Chen Ni @ 2026-01-26 3:55 UTC (permalink / raw)
To: vkoul, neil.armstrong; +Cc: linux-phy, linux-kernel, Chen Ni
clk_disable_unprepare() already checks NULL by using IS_ERR_OR_NULL.
Remove unneeded NULL check for clk here.
Signed-off-by: Chen Ni <nichen@iscas.ac.cn>
---
drivers/phy/marvell/phy-mvebu-cp110-comphy.c | 9 +++------
1 file changed, 3 insertions(+), 6 deletions(-)
diff --git a/drivers/phy/marvell/phy-mvebu-cp110-comphy.c b/drivers/phy/marvell/phy-mvebu-cp110-comphy.c
index 71f9c14fb50d..990c0dd94ad6 100644
--- a/drivers/phy/marvell/phy-mvebu-cp110-comphy.c
+++ b/drivers/phy/marvell/phy-mvebu-cp110-comphy.c
@@ -984,14 +984,11 @@ static int mvebu_comphy_init_clks(struct mvebu_comphy_priv *priv)
static void mvebu_comphy_disable_unprepare_clks(struct mvebu_comphy_priv *priv)
{
- if (priv->axi_clk)
- clk_disable_unprepare(priv->axi_clk);
+ clk_disable_unprepare(priv->axi_clk);
- if (priv->mg_core_clk)
- clk_disable_unprepare(priv->mg_core_clk);
+ clk_disable_unprepare(priv->mg_core_clk);
- if (priv->mg_domain_clk)
- clk_disable_unprepare(priv->mg_domain_clk);
+ clk_disable_unprepare(priv->mg_domain_clk);
}
static int mvebu_comphy_probe(struct platform_device *pdev)
--
2.25.1
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* Re: [PATCH v14 0/9] Add Type-C DP support for RK3399 EVB IND board
From: Chaoyi Chen @ 2026-01-26 3:22 UTC (permalink / raw)
To: Hugh Cole-Baker
Cc: Chaoyi Chen, Heikki Krogerus, Greg Kroah-Hartman,
Dmitry Baryshkov, Peter Chen, Luca Ceresoli, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Vinod Koul,
Kishon Vijay Abraham I, Heiko Stuebner, Sandy Huang, Andy Yan,
Yubing Zhang, Frank Wang, Andrzej Hajda, Neil Armstrong,
Robert Foss, Laurent Pinchart, Jonas Karlman, Jernej Skrabec,
Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Amit Sunil Dhamne, Dragan Simic, Johan Jonker,
Diederik de Haas, Peter Robinson, linux-usb, devicetree,
linux-kernel, linux-phy, linux-arm-kernel, linux-rockchip,
dri-devel
In-Reply-To: <c9dd365b-ad8f-410e-96f8-f914f03634b2@gmail.com>
Hello,
On 1/26/2026 3:42 AM, Hugh Cole-Baker wrote:
> On 19/01/2026 07:30, Chaoyi Chen wrote:
>> From: Chaoyi Chen <chaoyi.chen@rock-chips.com>
>>
>> This series focuses on adding Type-C DP support for USBDP PHY and DP
>> driver. The USBDP PHY and DP will perceive the changes in cable status
>> based on the USB PD and Type-C state machines provided by TCPM. Before
>> this, the USBDP PHY and DP controller of RK3399 sensed cable state
>> changes through extcon, and devices such as the RK3399 Gru-Chromebook
>> rely on them. This series should not break them.
>>
>> ====
>> 1. DisplayPort HPD status notify
>>
>> Before v7, I implemented a variety of DP HPD status notify. However,
>> they all had various problems and it was difficult to become a generic
>> solution.
>>
>> Under the guidance of Heikki and Dmitry, a decoupled notification
>> method between the TypeC and DRM subsystems was introduced in v7.
>> First, a notification is sent when TypeC registers a new altmode.
>> Then, a generic DP AUX HPD bridge is implemented on the DRM side.
>>
>> During v7-v10, we added a new notifier in typec to notify the altmode
>> device register event. With the help of Greg and Heikki, we implemented
>> the reuse of notifiers for the type bus itself in patch1 of v11.
>>
>> The USB subsystem related parts have already been merged into the
>> usb-next branch in v13 [0][1]. Therefore, this series no longer includes
>> these patches starting from v14. Thanks to Greg and Heikki!
>>
>> [0]: https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb.git/commit/?h=usb-next&id=67ab45426215c7fdccb65aecd4cac15bbe4dfcbb
>> [1]: https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb.git/commit/?h=usb-next&id=4dee13db29de6dd869af9b3827e1ff569644e838
>>
>> That makes it redundant for each Type-C controller driver to implement
>> a similar DP AUX HPD bridge in embedded scenarios.
>>
>> ====
>> 2. Altmode switching and orientation switching for USBDP PHY
>>
>> For USB Type-C interfaces, an external Type-C controller chip assists
>> by detecting cable attachment, determining plug orientation, and
>> reporting USB PD message. The USB/DP combo PHY supports software
>> configurable pin mapping and DisplayPort lane assignment. Based on
>> these message, the combo PHY can perform both altmode switching and
>> orientation switching via software.
>>
>> The RK3399 EVB IND board has a Type-C interface DisplayPort. It use
>> fusb302 chip as Type-C controller. The connection diagram is shown below:
>>
>> fusb302 chip +---> USB2.0 PHY ----> DWC3 USB controller
>> |
>> +---> USB/DP PHY0 +--> CDN-DP controller
>> |
>> +--> DWC3 USB controller
>>
>> ====
>> 3. Multiple bridge model for RK3399 CDN-DP
>>
>> The RK3399 has two USB/DP combo PHY and one CDN-DP controller. And
>> the CDN-DP can be switched to output to one of the PHYs.
>>
>> USB/DP PHY0 ---+
>> | <----> CDN-DP controller
>> USB/DP PHY1 ---+
>>
>> In previous versions, if both PHY ports were connected to DP,
>> the CDN-DP driver would select the first PHY port for output.
>>
>> On Dmitry's suggestion, we introduced a multi-bridge model to support
>> flexible selection of the output PHY port. For each PHY port, a
>> separate encoder and bridge are registered.
>>
>> The change is based on the DRM AUX HPD bridge, rather than the
>> extcon approach. This requires the DT to correctly describe the
>> connections between the first bridge in bridge chain and DP
>> controller. And Once the first bridge is obtained, we can get the
>> last bridge corresponding to the USB-C connector, and then set the
>> DRM connector's fwnode to the corresponding one to enable HPD
>> notification.
>
> With a similar dts patch [1] on top of this series I tested a type-C to
> DP adapter/cable for display output on the ROCKPro64 board, which also
> pairs a FUSB302 with RK3399. Booting it up with the cable plugged in
> works, as does hotplugging the cable after booting in both orientations.
> The correct mode for the display is detected. I wasn't able to test
> audio, only video output, as this display doesn't have speakers.
>
> I did once, after unplugging and reconnecting the cable a few times,
> see it get into a state where it didn't detect the attached display.
> Logs from that unplug/reconnect attempt are here [2] if of interest.
> Nevertheless, hotplug seems to work the majority of the time, so
>
> Tested-by: Hugh Cole-Baker <sigmaris@gmail.com>
>
> [1]: https://github.com/sigmaris/linux/commit/91724088b19bee7d248946442a801423e8cd0634
> [2]: https://gist.github.com/sigmaris/fa107384a7492583ceee1c2962f5030a
>
Thank you for the test. I also have the same board, and I will
try it later :)
--
Best,
Chaoyi
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH v14 0/9] Add Type-C DP support for RK3399 EVB IND board
From: Hugh Cole-Baker @ 2026-01-25 19:42 UTC (permalink / raw)
To: Chaoyi Chen, Heikki Krogerus, Greg Kroah-Hartman,
Dmitry Baryshkov, Peter Chen, Luca Ceresoli, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Vinod Koul,
Kishon Vijay Abraham I, Heiko Stuebner, Sandy Huang, Andy Yan,
Yubing Zhang, Frank Wang, Andrzej Hajda, Neil Armstrong,
Robert Foss, Laurent Pinchart, Jonas Karlman, Jernej Skrabec,
Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Amit Sunil Dhamne, Chaoyi Chen, Dragan Simic,
Johan Jonker, Diederik de Haas, Peter Robinson
Cc: linux-usb, devicetree, linux-kernel, linux-phy, linux-arm-kernel,
linux-rockchip, dri-devel
In-Reply-To: <20260119073100.143-1-kernel@airkyi.com>
On 19/01/2026 07:30, Chaoyi Chen wrote:
> From: Chaoyi Chen <chaoyi.chen@rock-chips.com>
>
> This series focuses on adding Type-C DP support for USBDP PHY and DP
> driver. The USBDP PHY and DP will perceive the changes in cable status
> based on the USB PD and Type-C state machines provided by TCPM. Before
> this, the USBDP PHY and DP controller of RK3399 sensed cable state
> changes through extcon, and devices such as the RK3399 Gru-Chromebook
> rely on them. This series should not break them.
>
> ====
> 1. DisplayPort HPD status notify
>
> Before v7, I implemented a variety of DP HPD status notify. However,
> they all had various problems and it was difficult to become a generic
> solution.
>
> Under the guidance of Heikki and Dmitry, a decoupled notification
> method between the TypeC and DRM subsystems was introduced in v7.
> First, a notification is sent when TypeC registers a new altmode.
> Then, a generic DP AUX HPD bridge is implemented on the DRM side.
>
> During v7-v10, we added a new notifier in typec to notify the altmode
> device register event. With the help of Greg and Heikki, we implemented
> the reuse of notifiers for the type bus itself in patch1 of v11.
>
> The USB subsystem related parts have already been merged into the
> usb-next branch in v13 [0][1]. Therefore, this series no longer includes
> these patches starting from v14. Thanks to Greg and Heikki!
>
> [0]: https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb.git/commit/?h=usb-next&id=67ab45426215c7fdccb65aecd4cac15bbe4dfcbb
> [1]: https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb.git/commit/?h=usb-next&id=4dee13db29de6dd869af9b3827e1ff569644e838
>
> That makes it redundant for each Type-C controller driver to implement
> a similar DP AUX HPD bridge in embedded scenarios.
>
> ====
> 2. Altmode switching and orientation switching for USBDP PHY
>
> For USB Type-C interfaces, an external Type-C controller chip assists
> by detecting cable attachment, determining plug orientation, and
> reporting USB PD message. The USB/DP combo PHY supports software
> configurable pin mapping and DisplayPort lane assignment. Based on
> these message, the combo PHY can perform both altmode switching and
> orientation switching via software.
>
> The RK3399 EVB IND board has a Type-C interface DisplayPort. It use
> fusb302 chip as Type-C controller. The connection diagram is shown below:
>
> fusb302 chip +---> USB2.0 PHY ----> DWC3 USB controller
> |
> +---> USB/DP PHY0 +--> CDN-DP controller
> |
> +--> DWC3 USB controller
>
> ====
> 3. Multiple bridge model for RK3399 CDN-DP
>
> The RK3399 has two USB/DP combo PHY and one CDN-DP controller. And
> the CDN-DP can be switched to output to one of the PHYs.
>
> USB/DP PHY0 ---+
> | <----> CDN-DP controller
> USB/DP PHY1 ---+
>
> In previous versions, if both PHY ports were connected to DP,
> the CDN-DP driver would select the first PHY port for output.
>
> On Dmitry's suggestion, we introduced a multi-bridge model to support
> flexible selection of the output PHY port. For each PHY port, a
> separate encoder and bridge are registered.
>
> The change is based on the DRM AUX HPD bridge, rather than the
> extcon approach. This requires the DT to correctly describe the
> connections between the first bridge in bridge chain and DP
> controller. And Once the first bridge is obtained, we can get the
> last bridge corresponding to the USB-C connector, and then set the
> DRM connector's fwnode to the corresponding one to enable HPD
> notification.
With a similar dts patch [1] on top of this series I tested a type-C to
DP adapter/cable for display output on the ROCKPro64 board, which also
pairs a FUSB302 with RK3399. Booting it up with the cable plugged in
works, as does hotplugging the cable after booting in both orientations.
The correct mode for the display is detected. I wasn't able to test
audio, only video output, as this display doesn't have speakers.
I did once, after unplugging and reconnecting the cable a few times,
see it get into a state where it didn't detect the attached display.
Logs from that unplug/reconnect attempt are here [2] if of interest.
Nevertheless, hotplug seems to work the majority of the time, so
Tested-by: Hugh Cole-Baker <sigmaris@gmail.com>
[1]: https://github.com/sigmaris/linux/commit/91724088b19bee7d248946442a801423e8cd0634
[2]: https://gist.github.com/sigmaris/fa107384a7492583ceee1c2962f5030a
> ====
> Patch1 add generic USB Type-C DP HPD bridge (Dmitry, Heikki).
> Patch2 add new API drm_aux_bridge_register_from_node() (Neil).
> Patch3 add new Type-C mode switch for RK3399 USBDP phy binding (Krzysztof).
> Patch4 add typec_mux and typec_switch for RK3399 USBDP PHY.
> Patch5 add DRM AUX bridge support for RK3399 USBDP PHY (Neil).
> Patch6 drops CDN-DP's extcon dependency when Type-C is present (Dmitry).
> Patch7 add multiple bridges to support PHY port selection (Dmitry, Luca).
> Patch8 add missing dp_out port for RK3399 CDN-DP.
> Patch9 add Type-C DP support for RK3399 EVB IND board (Diederik, Peter).
>
> Changes in v14:
> - Link to V13: https://lore.kernel.org/all/20251208015500.94-1-kernel@airkyi.com/
> - Drop the patches for the USB Type-C subsusytem part, as they have
> already been merged into usb-next.
>
> Changes in v13:
> - Link to V12: https://lore.kernel.org/all/20251204063109.104-1-kernel@airkyi.com/
> - Only register drm dp hpd bridge for typec port altmode device.
>
> Changes in v12:
> - Link to V11: https://lore.kernel.org/all/20251128020405.90-1-kernel@airkyi.com/
> - Add missing Signed-off-by line.
>
> Changes in v11:
> - Link to V10: https://lore.kernel.org/all/20251120022343.250-1-kernel@airkyi.com/
> - Switch to using typec bus notifiers.
>
> Changes in v10:
> - Link to V9: https://lore.kernel.org/all/20251111105040.94-1-kernel@airkyi.com/
> - Notify TYPEC_ALTMODE_UNREGISTERED when altmode removed.
> - Add drm_aux_bridge_register_from_node().
> - Fix refcount usage of drm_bridge.
>
> Changes in v9:
> - Link to V8: https://lore.kernel.org/all/20251029071435.88-1-kernel@airkyi.com/
> - Remove the exposed DRM_AUX_HPD_BRIDGE option, and select
> DRM_AUX_HPD_TYPEC_BRIDGE when it is available.
> - Add usb role switch for Type-C.
> - Remove USB2 PHY in Type-C connection.
> - ...
>
> Changes in v8:
> - Link to V7: https://lore.kernel.org/all/20251023033009.90-1-kernel@airkyi.com/
> - Export all typec device types for identification.
> - Merge generic DP HPD bridge into one module.
> - Fix coding style.
>
> Changes in v7:
> - Link to V6: https://lore.kernel.org/all/20251016022741.91-1-kernel@airkyi.com/
> - Add notifier functions for Type-C core.
> - Add generic USB Type-C DP HPD bridge.
>
> Changes in v6:
> - Link to V5: https://lore.kernel.org/all/20251011033233.97-1-kernel@airkyi.com/
> - Fix depend in Kconfig.
> - Check DP svid in tcphy_typec_mux_set().
> - Remove mode setting in tcphy_orien_sw_set().
> - Rename some variable names.
> - Attach the DP bridge to the next bridge.
>
> Changes in v5:
> - Link to V4: https://lore.kernel.org/all/20250922012039.323-1-kernel@airkyi.com/
> - Remove the calls related to `drm_aux_hpd_bridge_notify()`.
> - Place the helper functions in the same compilation unit.
> - Add more comments about parent device.
> - Add DRM AUX bridge support for RK3399 USBDP PHY
> - By parsing the HPD bridge chain, set the connector's of_node to the
> of_node corresponding to the USB-C connector.
> - Return EDID cache when other port is already enabled.
>
> Changes in v4:
> - Link to V3: https://lore.kernel.org/all/20250729090032.97-1-kernel@airkyi.com/
> - Add default HPD device for DisplayPort altmode.
> - Introduce multiple bridges for CDN-DP.
> - ...
>
> Changes in v3:
> - Link to V2: https://lore.kernel.org/all/20250718062619.99-1-kernel@airkyi.com/
> - Add more descriptions to clarify the role of the PHY in switching.
> - Fix wrong vdo value.
> - Fix port node in usb-c-connector.
>
> Changes in v2:
> - Link to V1: https://lore.kernel.org/all/20250715112456.101-1-kernel@airkyi.com/
> - Reuse dp-port/usb3-port in rk3399-typec-phy binding.
> - Fix compile error when CONFIG_TYPEC is not enabled.
> - Notify DP HPD state by USB/DP PHY.
> - Ignore duplicate HPD events.
> - Add endpoint to link DP PHY and DP controller.
> - Fix devicetree coding style.
>
> Chaoyi Chen (9):
> drm/bridge: Implement generic USB Type-C DP HPD bridge
> drm/bridge: aux: Add drm_aux_bridge_register_from_node()
> dt-bindings: phy: rockchip: rk3399-typec-phy: Support mode-switch
> phy: rockchip: phy-rockchip-typec: Add typec_mux/typec_switch support
> phy: rockchip: phy-rockchip-typec: Add DRM AUX bridge
> drm/rockchip: cdn-dp: Support handle lane info without extcon
> drm/rockchip: cdn-dp: Add multiple bridges to support PHY port
> selection
> arm64: dts: rockchip: Add missing dp_out port for RK3399 CDN-DP
> arm64: dts: rockchip: rk3399-evb-ind: Add support for DisplayPort
>
> .../phy/rockchip,rk3399-typec-phy.yaml | 6 +
> arch/arm64/boot/dts/rockchip/rk3399-base.dtsi | 10 +-
> .../boot/dts/rockchip/rk3399-evb-ind.dts | 147 +++++++
> drivers/gpu/drm/bridge/Kconfig | 10 +
> drivers/gpu/drm/bridge/Makefile | 1 +
> drivers/gpu/drm/bridge/aux-bridge.c | 24 +-
> .../gpu/drm/bridge/aux-hpd-typec-dp-bridge.c | 49 +++
> drivers/gpu/drm/rockchip/Kconfig | 1 +
> drivers/gpu/drm/rockchip/cdn-dp-core.c | 350 +++++++++++++---
> drivers/gpu/drm/rockchip/cdn-dp-core.h | 18 +-
> drivers/phy/rockchip/Kconfig | 3 +
> drivers/phy/rockchip/phy-rockchip-typec.c | 373 +++++++++++++++++-
> include/drm/bridge/aux-bridge.h | 6 +
> 13 files changed, 914 insertions(+), 84 deletions(-)
> create mode 100644 drivers/gpu/drm/bridge/aux-hpd-typec-dp-bridge.c
>
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH v6 14/24] scsi: ufs: mediatek: Switch to newer PM ops helpers
From: kernel test robot @ 2026-01-24 22:41 UTC (permalink / raw)
To: Nicolas Frattaroli, Alim Akhtar, Avri Altman, Bart Van Assche,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Matthias Brugger,
AngeloGioacchino Del Regno, Chunfeng Yun, Vinod Koul,
Kishon Vijay Abraham I, Peter Wang, Stanley Jhu,
James E.J. Bottomley, Martin K. Petersen, Philipp Zabel,
Liam Girdwood, Mark Brown, Chaotian Jing, Neil Armstrong
Cc: llvm, oe-kbuild-all, Louis-Alexis Eyraud, kernel, linux-scsi,
devicetree, linux-kernel, linux-arm-kernel, linux-mediatek,
linux-phy, Nicolas Frattaroli
In-Reply-To: <20260124-mt8196-ufs-v6-14-e7c005b60028@collabora.com>
Hi Nicolas,
kernel test robot noticed the following build errors:
[auto build test ERROR on 4af4e95edc37ae54f64cbd75b46f16ce15f3a6b8]
url: https://github.com/intel-lab-lkp/linux/commits/Nicolas-Frattaroli/dt-bindings-phy-Add-mediatek-mt8196-ufsphy-variant/20260124-201226
base: 4af4e95edc37ae54f64cbd75b46f16ce15f3a6b8
patch link: https://lore.kernel.org/r/20260124-mt8196-ufs-v6-14-e7c005b60028%40collabora.com
patch subject: [PATCH v6 14/24] scsi: ufs: mediatek: Switch to newer PM ops helpers
config: arm64-randconfig-004-20260125 (https://download.01.org/0day-ci/archive/20260125/202601250638.kCl6evPM-lkp@intel.com/config)
compiler: clang version 22.0.0git (https://github.com/llvm/llvm-project 9b8addffa70cee5b2acc5454712d9cf78ce45710)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260125/202601250638.kCl6evPM-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202601250638.kCl6evPM-lkp@intel.com/
All errors (new ones prefixed by >>):
>> drivers/ufs/host/ufs-mediatek.c:2339:8: error: call to undeclared function 'ufshcd_system_suspend'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
2339 | ret = ufshcd_system_suspend(dev);
| ^
drivers/ufs/host/ufs-mediatek.c:2339:8: note: did you mean 'ufs_mtk_system_suspend'?
drivers/ufs/host/ufs-mediatek.c:2328:12: note: 'ufs_mtk_system_suspend' declared here
2328 | static int ufs_mtk_system_suspend(struct device *dev)
| ^
2329 | {
2330 | struct ufs_hba *hba = dev_get_drvdata(dev);
2331 | struct arm_smccc_res res;
2332 | int ret;
2333 |
2334 | if (hba->shutting_down) {
2335 | ret = -EBUSY;
2336 | goto out;
2337 | }
2338 |
2339 | ret = ufshcd_system_suspend(dev);
| ~~~~~~~~~~~~~~~~~~~~~
| ufs_mtk_system_suspend
>> drivers/ufs/host/ufs-mediatek.c:2370:8: error: call to undeclared function 'ufshcd_system_resume'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
2370 | ret = ufshcd_system_resume(dev);
| ^
drivers/ufs/host/ufs-mediatek.c:2370:8: note: did you mean 'ufs_mtk_system_resume'?
drivers/ufs/host/ufs-mediatek.c:2355:12: note: 'ufs_mtk_system_resume' declared here
2355 | static int ufs_mtk_system_resume(struct device *dev)
| ^
2356 | {
2357 | int ret = 0;
2358 | struct ufs_hba *hba = dev_get_drvdata(dev);
2359 | struct arm_smccc_res res;
2360 |
2361 | if (pm_runtime_suspended(hba->dev))
2362 | goto out;
2363 |
2364 | if (ufs_mtk_is_rtff_mtcmos(hba))
2365 | ufs_mtk_mtcmos_ctrl(true, res);
2366 |
2367 | ufs_mtk_dev_vreg_set_lpm(hba, false);
2368 |
2369 | out:
2370 | ret = ufshcd_system_resume(dev);
| ~~~~~~~~~~~~~~~~~~~~
| ufs_mtk_system_resume
2 errors generated.
vim +/ufshcd_system_suspend +2339 drivers/ufs/host/ufs-mediatek.c
ddd90623ce26ea drivers/scsi/ufs/ufs-mediatek.c Stanley Chu 2019-03-16 2327
e7bf1d50063ce0 drivers/ufs/host/ufs-mediatek.c Stanley Chu 2022-06-23 2328 static int ufs_mtk_system_suspend(struct device *dev)
3fd23b8dfb54d9 drivers/ufs/host/ufs-mediatek.c Po-Wen Kao 2022-06-16 2329 {
3fd23b8dfb54d9 drivers/ufs/host/ufs-mediatek.c Po-Wen Kao 2022-06-16 2330 struct ufs_hba *hba = dev_get_drvdata(dev);
a6888d623eae6d drivers/ufs/host/ufs-mediatek.c Alice Chao 2024-03-15 2331 struct arm_smccc_res res;
3fd23b8dfb54d9 drivers/ufs/host/ufs-mediatek.c Po-Wen Kao 2022-06-16 2332 int ret;
3fd23b8dfb54d9 drivers/ufs/host/ufs-mediatek.c Po-Wen Kao 2022-06-16 2333
014de20bb36ba0 drivers/ufs/host/ufs-mediatek.c Peter Wang 2025-09-24 2334 if (hba->shutting_down) {
014de20bb36ba0 drivers/ufs/host/ufs-mediatek.c Peter Wang 2025-09-24 2335 ret = -EBUSY;
014de20bb36ba0 drivers/ufs/host/ufs-mediatek.c Peter Wang 2025-09-24 2336 goto out;
014de20bb36ba0 drivers/ufs/host/ufs-mediatek.c Peter Wang 2025-09-24 2337 }
014de20bb36ba0 drivers/ufs/host/ufs-mediatek.c Peter Wang 2025-09-24 2338
3fd23b8dfb54d9 drivers/ufs/host/ufs-mediatek.c Po-Wen Kao 2022-06-16 @2339 ret = ufshcd_system_suspend(dev);
3fd23b8dfb54d9 drivers/ufs/host/ufs-mediatek.c Po-Wen Kao 2022-06-16 2340 if (ret)
77b96ef70b6ba4 drivers/ufs/host/ufs-mediatek.c Peter Wang 2025-09-03 2341 goto out;
77b96ef70b6ba4 drivers/ufs/host/ufs-mediatek.c Peter Wang 2025-09-03 2342
77b96ef70b6ba4 drivers/ufs/host/ufs-mediatek.c Peter Wang 2025-09-03 2343 if (pm_runtime_suspended(hba->dev))
77b96ef70b6ba4 drivers/ufs/host/ufs-mediatek.c Peter Wang 2025-09-03 2344 goto out;
3fd23b8dfb54d9 drivers/ufs/host/ufs-mediatek.c Po-Wen Kao 2022-06-16 2345
42b1928360a32e drivers/ufs/host/ufs-mediatek.c Stanley Chu 2022-06-16 2346 ufs_mtk_dev_vreg_set_lpm(hba, true);
3fd23b8dfb54d9 drivers/ufs/host/ufs-mediatek.c Po-Wen Kao 2022-06-16 2347
a6888d623eae6d drivers/ufs/host/ufs-mediatek.c Alice Chao 2024-03-15 2348 if (ufs_mtk_is_rtff_mtcmos(hba))
a6888d623eae6d drivers/ufs/host/ufs-mediatek.c Alice Chao 2024-03-15 2349 ufs_mtk_mtcmos_ctrl(false, res);
a6888d623eae6d drivers/ufs/host/ufs-mediatek.c Alice Chao 2024-03-15 2350
77b96ef70b6ba4 drivers/ufs/host/ufs-mediatek.c Peter Wang 2025-09-03 2351 out:
77b96ef70b6ba4 drivers/ufs/host/ufs-mediatek.c Peter Wang 2025-09-03 2352 return ret;
3fd23b8dfb54d9 drivers/ufs/host/ufs-mediatek.c Po-Wen Kao 2022-06-16 2353 }
3fd23b8dfb54d9 drivers/ufs/host/ufs-mediatek.c Po-Wen Kao 2022-06-16 2354
e7bf1d50063ce0 drivers/ufs/host/ufs-mediatek.c Stanley Chu 2022-06-23 2355 static int ufs_mtk_system_resume(struct device *dev)
3fd23b8dfb54d9 drivers/ufs/host/ufs-mediatek.c Po-Wen Kao 2022-06-16 2356 {
77b96ef70b6ba4 drivers/ufs/host/ufs-mediatek.c Peter Wang 2025-09-03 2357 int ret = 0;
3fd23b8dfb54d9 drivers/ufs/host/ufs-mediatek.c Po-Wen Kao 2022-06-16 2358 struct ufs_hba *hba = dev_get_drvdata(dev);
a6888d623eae6d drivers/ufs/host/ufs-mediatek.c Alice Chao 2024-03-15 2359 struct arm_smccc_res res;
3fd23b8dfb54d9 drivers/ufs/host/ufs-mediatek.c Po-Wen Kao 2022-06-16 2360
77b96ef70b6ba4 drivers/ufs/host/ufs-mediatek.c Peter Wang 2025-09-03 2361 if (pm_runtime_suspended(hba->dev))
77b96ef70b6ba4 drivers/ufs/host/ufs-mediatek.c Peter Wang 2025-09-03 2362 goto out;
3fd23b8dfb54d9 drivers/ufs/host/ufs-mediatek.c Po-Wen Kao 2022-06-16 2363
a6888d623eae6d drivers/ufs/host/ufs-mediatek.c Alice Chao 2024-03-15 2364 if (ufs_mtk_is_rtff_mtcmos(hba))
a6888d623eae6d drivers/ufs/host/ufs-mediatek.c Alice Chao 2024-03-15 2365 ufs_mtk_mtcmos_ctrl(true, res);
a6888d623eae6d drivers/ufs/host/ufs-mediatek.c Alice Chao 2024-03-15 2366
b2f8abadabea32 drivers/ufs/host/ufs-mediatek.c Alice Chao 2025-09-03 2367 ufs_mtk_dev_vreg_set_lpm(hba, false);
b2f8abadabea32 drivers/ufs/host/ufs-mediatek.c Alice Chao 2025-09-03 2368
77b96ef70b6ba4 drivers/ufs/host/ufs-mediatek.c Peter Wang 2025-09-03 2369 out:
77b96ef70b6ba4 drivers/ufs/host/ufs-mediatek.c Peter Wang 2025-09-03 @2370 ret = ufshcd_system_resume(dev);
77b96ef70b6ba4 drivers/ufs/host/ufs-mediatek.c Peter Wang 2025-09-03 2371
77b96ef70b6ba4 drivers/ufs/host/ufs-mediatek.c Peter Wang 2025-09-03 2372 return ret;
3fd23b8dfb54d9 drivers/ufs/host/ufs-mediatek.c Po-Wen Kao 2022-06-16 2373 }
3fd23b8dfb54d9 drivers/ufs/host/ufs-mediatek.c Po-Wen Kao 2022-06-16 2374
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox