Linux-PHY Archive on lore.kernel.org
 help / color / mirror / Atom feed
* Re: [PATCH] phy: GOOGLE_USB: add TYPEC dependency
From: Neil Armstrong @ 2026-02-04 12:41 UTC (permalink / raw)
  To: Arnd Bergmann, Vinod Koul, Joy Chakraborty, Naveen Kumar, Roy Luo
  Cc: Arnd Bergmann, Alex Elder, Ivaylo Ivanov, Dmitry Baryshkov,
	Inochi Amaoto, Sven Peter, Vladimir Oltean, linux-phy,
	linux-kernel
In-Reply-To: <20260202095655.1289973-1-arnd@kernel.org>

On 2/2/26 10:56, Arnd Bergmann wrote:
> From: Arnd Bergmann <arnd@arndb.de>
> 
> With CONFIG_TYPEC=m, this driver cannot be built-in:
> 
> arm-linux-gnueabi/bin/arm-linux-gnueabi-ld: drivers/phy/phy-google-usb.o: in function `google_usb_phy_remove':
> phy-google-usb.c:(.text+0x24): undefined reference to `typec_switch_unregister'
> 
> Add CONFIG_TYPEC as a hard dependency here to force a clean build.
> In theory, compile-testing with CONFIG_TYPEC=n would also work, but
> that seems pointless.
> 
> Fixes: cbce66669c82 ("phy: Add Google Tensor SoC USB PHY driver")
> Signed-off-by: Arnd Bergmann <arnd@arndb.de>
> ---
>   drivers/phy/Kconfig | 1 +
>   1 file changed, 1 insertion(+)
> 
> diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
> index 142e7b0ef2ef..02467dfd4fb0 100644
> --- a/drivers/phy/Kconfig
> +++ b/drivers/phy/Kconfig
> @@ -50,6 +50,7 @@ config GENERIC_PHY_MIPI_DPHY
>   config PHY_GOOGLE_USB
>   	tristate "Google Tensor SoC USB PHY driver"
>   	select GENERIC_PHY
> +	depends on TYPEC
>   	help
>   	  Enable support for the USB PHY on Google Tensor SoCs, starting with
>   	  the G5 generation (Laguna). This driver provides the PHY interfaces

Reviewed-by: Neil Armstrong <neil.armstrong@linaro.org>

Thanks,
Neil

-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply

* Re: [PATCH v8 2/7] mux: Add helper functions for getting optional and selected mux-state
From: Ulf Hansson @ 2026-02-04 14:21 UTC (permalink / raw)
  To: Josua Mayer, Wolfram Sang
  Cc: Marc Kleine-Budde, Vincent Mailhol, Vinod Koul, Neil Armstrong,
	Peter Rosin, Aaro Koskinen, Andreas Kemnade, Kevin Hilman,
	Roger Quadros, Tony Lindgren, Janusz Krzysztofik, Vignesh R,
	Andi Shyti, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Geert Uytterhoeven, Magnus Damm, Yazan Shhady, Jon Nettleton,
	Mikhail Anikin, linux-can@vger.kernel.org,
	linux-phy@lists.infradead.org, linux-kernel@vger.kernel.org,
	linux-omap@vger.kernel.org, linux-i2c@vger.kernel.org,
	linux-mmc@vger.kernel.org, devicetree@vger.kernel.org,
	linux-renesas-soc@vger.kernel.org, Dan Carpenter
In-Reply-To: <e6bccab9-79ce-4b9b-942e-01c504228d7b@solid-run.com>

On Tue, 3 Feb 2026 at 16:35, Josua Mayer <josua@solid-run.com> wrote:
>
> On 03/02/2026 15:01, Josua Mayer wrote:
> > In-tree phy-can-transceiver driver has already implemented a local
> > version of devm_mux_state_get_optional.
> >
> > The omap-i2c driver gets and selects an optional mux in its probe
> > function without using any helper.
> >
> > Add new helper functions covering both aforementioned use-cases:
> >
> > - mux_control_get_optional:
> >    Get a mux-control if specified in dt, return NULL otherwise.
> > - devm_mux_state_get_optional:
> >    Get a mux-state if specified in dt, return NULL otherwise.
> > - devm_mux_state_get_selected:
> >    Get and select a mux-state specified in dt, return error otherwise.
> > - devm_mux_state_get_optional_selected:
> >    Get and select a mux-state if specified in dt, return error or NULL.
> >
> > Existing mux_get helper function is changed to take an extra argument
> > indicating whether the mux is optional.
> > In this case no error is printed, and NULL returned in case of ENOENT.
> >
> > Calling code is adapted to handle NULL return case, and to pass optional
> > argument as required.
> >
> > To support automatic deselect for _selected helper, a new structure is
> > created storing an exit pointer similar to clock core which is called on
> > release.
> >
> > To facilitate code sharing between optional/mandatory/selected helpers,
> > a new internal helper function is added to handle quiet (optional) and
> > verbose (mandatory) errors, as well as storing the correct callback for
> > devm release: __devm_mux_state_get
> >
> > Due to this structure devm_mux_state_get_*_selected can no longer print
> > a useful error message when select fails. Instead callers should print
> > errors where needed.
> >
> > Commit e153fdea9db04 ("phy: can-transceiver: Re-instate "mux-states"
> > property presence check") noted that "mux_get() always prints an error
> > message in case of an error, including when the property is not present,
> > confusing the user."
> >
> > The first error message covers the case that a mux name is not matched
> > in dt. The second error message is based on of_parse_phandle_with_args
> > return value.
> >
> > In optional case no error is printed and NULL is returned.
> > This ensures that the new helper functions will not confuse the user
> > either.
> >
> > With the addition of optional helper functions it became clear that
> > drivers should compile and link even if CONFIG_MULTIPLEXER was not enabled.
> > Add stubs for all symbols exported by mux core.
> >
> > Signed-off-by: Josua Mayer <josua@solid-run.com>
> > ---
> >   drivers/mux/core.c           | 205 ++++++++++++++++++++++++++++++++++++-------
> >   include/linux/mux/consumer.h | 108 ++++++++++++++++++++++-
> >   2 files changed, 278 insertions(+), 35 deletions(-)
> >
> > diff --git a/drivers/mux/core.c b/drivers/mux/core.c
> > index a3840fe0995f..2e8295e3aabe 100644
> > --- a/drivers/mux/core.c
> > +++ b/drivers/mux/core.c
> > @@ -46,6 +46,16 @@ static const struct class mux_class = {
> >       .name = "mux",
> >   };
> >
> > +/**
> > + * struct devm_mux_state_state -     Tracks managed resources for mux-state objects.
> > + * @mstate:                          Pointer to a mux state.
> > + * @exit:                            An optional callback to execute before free.
> > + */
> > +struct devm_mux_state_state {
> > +     struct mux_state *mstate;
> > +     int (*exit)(struct mux_state *mstate);
> > +};
> > +
> >   static DEFINE_IDA(mux_ida);
> >
> >   static int __init mux_init(void)
> > @@ -516,17 +526,19 @@ static struct mux_chip *of_find_mux_chip_by_node(struct device_node *np)
> >       return dev ? to_mux_chip(dev) : NULL;
> >   }
> >
> > -/*
> > +/**
> >    * mux_get() - Get the mux-control for a device.
> >    * @dev: The device that needs a mux-control.
> >    * @mux_name: The name identifying the mux-control.
> >    * @state: Pointer to where the requested state is returned, or NULL when
> >    *         the required multiplexer states are handled by other means.
> > + * @optional: Whether to return NULL and silence errors when mux doesn't exist.
> >    *
> > - * Return: A pointer to the mux-control, or an ERR_PTR with a negative errno.
> > + * Return: Pointer to the mux-control on success, an ERR_PTR with a negative errno on error,
> > + * or NULL if optional is true and mux doesn't exist.
> >    */
> >   static struct mux_control *mux_get(struct device *dev, const char *mux_name,
> > -                                unsigned int *state)
> > +                                unsigned int *state, bool optional)
> >   {
> >       struct device_node *np = dev->of_node;
> >       struct of_phandle_args args;
> > @@ -542,7 +554,9 @@ static struct mux_control *mux_get(struct device *dev, const char *mux_name,
> >               else
> >                       index = of_property_match_string(np, "mux-control-names",
> >                                                        mux_name);
> > -             if (index < 0) {
> > +             if (index < 0 && optional) {
> > +                     return NULL;
> > +             } else if (index < 0) {
> >                       dev_err(dev, "mux controller '%s' not found\n",
> >                               mux_name);
> >                       return ERR_PTR(index);
> > @@ -558,8 +572,12 @@ static struct mux_control *mux_get(struct device *dev, const char *mux_name,
> >                                                "mux-controls", "#mux-control-cells",
> >                                                index, &args);
> >       if (ret) {
> > +             if (optional && ret == -ENOENT)
> > +                     return NULL;
> > +
> >               dev_err(dev, "%pOF: failed to get mux-%s %s(%i)\n",
> > -                     np, state ? "state" : "control", mux_name ?: "", index);
> > +                     np, state ? "state" : "control",
> > +                     mux_name ?: "", index);
> >               return ERR_PTR(ret);
> >       }
> >
> > @@ -617,10 +635,28 @@ static struct mux_control *mux_get(struct device *dev, const char *mux_name,
> >    */
> >   struct mux_control *mux_control_get(struct device *dev, const char *mux_name)
> >   {
> > -     return mux_get(dev, mux_name, NULL);
> > +     struct mux_control *mux = mux_get(dev, mux_name, NULL, false);
> > +
> > +     if (!mux)
> > +             return ERR_PTR(-ENOENT);
> > +
> > +     return mux;
> >   }
> >   EXPORT_SYMBOL_GPL(mux_control_get);
> >
> > +/**
> > + * mux_control_get_optional() - Get the optional mux-control for a device.
> > + * @dev: The device that needs a mux-control.
> > + * @mux_name: The name identifying the mux-control.
> > + *
> > + * Return: A pointer to the mux-control, an ERR_PTR with a negative errno.
>   * Return: Pointer to the mux-state on success, an ERR_PTR with a
> negative errno on error,
>   * or NULL if mux doesn't exist.
>
> If there will be v9, I shall change this to be more precise similar to
> other functions.

The series looks good to me!

I can queue the series for v7.0 via my mmc tree and amend the change
according to above when applying, but I need the ack from Wolfram on
the i2c patch (patch5) first.

If this doesn't make it for v7.0, I suggest you re-spin a v9 after the
merge window.

Kind regards
Uffe



> > + */
> > +struct mux_control *mux_control_get_optional(struct device *dev, const char *mux_name)
> > +{
> > +     return mux_get(dev, mux_name, NULL, true);
> > +}
> > +EXPORT_SYMBOL_GPL(mux_control_get_optional);
> > +
> >   /**
> >    * mux_control_put() - Put away the mux-control for good.
> >    * @mux: The mux-control to put away.
> > @@ -657,10 +693,13 @@ struct mux_control *devm_mux_control_get(struct device *dev,
> >       if (!ptr)
> >               return ERR_PTR(-ENOMEM);
> >
> > -     mux = mux_control_get(dev, mux_name);
> > +     mux = mux_get(dev, mux_name, NULL, false);
> >       if (IS_ERR(mux)) {
> >               devres_free(ptr);
> >               return mux;
> > +     } else if (!mux) {
> > +             devres_free(ptr);
> > +             return ERR_PTR(-ENOENT);
> >       }
> >
> >       *ptr = mux;
> > @@ -670,14 +709,16 @@ struct mux_control *devm_mux_control_get(struct device *dev,
> >   }
> >   EXPORT_SYMBOL_GPL(devm_mux_control_get);
> >
> > -/*
> > +/**
> >    * mux_state_get() - Get the mux-state for a device.
> >    * @dev: The device that needs a mux-state.
> >    * @mux_name: The name identifying the mux-state.
> > + * @optional: Whether to return NULL and silence errors when mux doesn't exist.
> >    *
> > - * Return: A pointer to the mux-state, or an ERR_PTR with a negative errno.
> > + * Return: Pointer to the mux-state on success, an ERR_PTR with a negative errno on error,
> > + * or NULL if optional is true and mux doesn't exist.
> >    */
> > -static struct mux_state *mux_state_get(struct device *dev, const char *mux_name)
> > +static struct mux_state *mux_state_get(struct device *dev, const char *mux_name, bool optional)
> >   {
> >       struct mux_state *mstate;
> >
> ...

-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply

* Re: [PATCH next v2] phy: google: fix build dependency for Google Tensor USB PHY
From: Vinod Koul @ 2026-02-04 15:18 UTC (permalink / raw)
  To: Roy Luo
  Cc: Neil Armstrong, Peter Griffin, André Draszik, Tudor Ambarus,
	Joy Chakraborty, Naveen Kumar, linux-phy, linux-kernel,
	linux-arm-kernel, linux-samsung-soc, kernel test robot
In-Reply-To: <20260128-next-v2-1-624bdae8e6d0@google.com>

On 28-01-26, 21:22, Roy Luo wrote:
> The Google Tensor USB PHY driver uses the Type-C switch framework to
> handle orientation changes. However, the Kconfig did not specify a
> dependency on the TYPEC framework, leading to undefined reference
> errors when building for architectures or configurations where
> CONFIG_TYPEC is configured as a module while CONFIG_PHY_GOOGLE_USB
> is configured as built-in.
> 
> Add 'depends on TYPEC' to the PHY_GOOGLE_USB entry to ensure all
> required symbols are available during linking, and 'COMPILE_TEST'
> to expand test coverage.

Thanks for the fix, looking at three patches for this, I like Arnd's fix
better and have applied that now

-- 
~Vinod

-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply

* Re: [PATCH v8 1/7] phy: can-transceiver: rename temporary helper function to avoid conflict
From: Wolfram Sang @ 2026-02-04 15:18 UTC (permalink / raw)
  To: Josua Mayer
  Cc: Marc Kleine-Budde, Vincent Mailhol, Vinod Koul, Neil Armstrong,
	Peter Rosin, Aaro Koskinen, Andreas Kemnade, Kevin Hilman,
	Roger Quadros, Tony Lindgren, Janusz Krzysztofik, Vignesh R,
	Andi Shyti, Ulf Hansson, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Geert Uytterhoeven, Magnus Damm, Yazan Shhady,
	Jon Nettleton, Mikhail Anikin, linux-can, linux-phy, linux-kernel,
	linux-omap, linux-i2c, linux-mmc, devicetree, linux-renesas-soc
In-Reply-To: <20260203-rz-sdio-mux-v8-1-024ea405863e@solid-run.com>

On Tue, Feb 03, 2026 at 03:01:34PM +0200, Josua Mayer wrote:
> Rename the temporary devm_mux_state_get_optional function to avoid
> conflict with upcoming implementation in multiplexer subsystem.
> 
> Acked-by: Vinod Koul <vkoul@kernel.org>
> Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
> Signed-off-by: Josua Mayer <josua@solid-run.com>

Reviewed-by: Wolfram Sang <wsa+renesas@sang-engineering.com>


-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply

* Re: [PATCH v8 2/7] mux: Add helper functions for getting optional and selected mux-state
From: Wolfram Sang @ 2026-02-04 15:19 UTC (permalink / raw)
  To: Josua Mayer
  Cc: Marc Kleine-Budde, Vincent Mailhol, Vinod Koul, Neil Armstrong,
	Peter Rosin, Aaro Koskinen, Andreas Kemnade, Kevin Hilman,
	Roger Quadros, Tony Lindgren, Janusz Krzysztofik, Vignesh R,
	Andi Shyti, Ulf Hansson, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Geert Uytterhoeven, Magnus Damm, Yazan Shhady,
	Jon Nettleton, Mikhail Anikin, linux-can, linux-phy, linux-kernel,
	linux-omap, linux-i2c, linux-mmc, devicetree, linux-renesas-soc
In-Reply-To: <20260203-rz-sdio-mux-v8-2-024ea405863e@solid-run.com>

On Tue, Feb 03, 2026 at 03:01:35PM +0200, Josua Mayer wrote:
> In-tree phy-can-transceiver driver has already implemented a local
> version of devm_mux_state_get_optional.
> 
> The omap-i2c driver gets and selects an optional mux in its probe
> function without using any helper.
> 
> Add new helper functions covering both aforementioned use-cases:
> 
> - mux_control_get_optional:
>   Get a mux-control if specified in dt, return NULL otherwise.
> - devm_mux_state_get_optional:
>   Get a mux-state if specified in dt, return NULL otherwise.
> - devm_mux_state_get_selected:
>   Get and select a mux-state specified in dt, return error otherwise.
> - devm_mux_state_get_optional_selected:
>   Get and select a mux-state if specified in dt, return error or NULL.
> 
> Existing mux_get helper function is changed to take an extra argument
> indicating whether the mux is optional.
> In this case no error is printed, and NULL returned in case of ENOENT.
> 
> Calling code is adapted to handle NULL return case, and to pass optional
> argument as required.
> 
> To support automatic deselect for _selected helper, a new structure is
> created storing an exit pointer similar to clock core which is called on
> release.
> 
> To facilitate code sharing between optional/mandatory/selected helpers,
> a new internal helper function is added to handle quiet (optional) and
> verbose (mandatory) errors, as well as storing the correct callback for
> devm release: __devm_mux_state_get
> 
> Due to this structure devm_mux_state_get_*_selected can no longer print
> a useful error message when select fails. Instead callers should print
> errors where needed.
> 
> Commit e153fdea9db04 ("phy: can-transceiver: Re-instate "mux-states"
> property presence check") noted that "mux_get() always prints an error
> message in case of an error, including when the property is not present,
> confusing the user."
> 
> The first error message covers the case that a mux name is not matched
> in dt. The second error message is based on of_parse_phandle_with_args
> return value.
> 
> In optional case no error is printed and NULL is returned.
> This ensures that the new helper functions will not confuse the user
> either.
> 
> With the addition of optional helper functions it became clear that
> drivers should compile and link even if CONFIG_MULTIPLEXER was not enabled.
> Add stubs for all symbols exported by mux core.
> 
> Signed-off-by: Josua Mayer <josua@solid-run.com>

I trust you guys with this one. I think the interfaces are useful,
though, so maybe like this?

Acked-by: Wolfram Sang <wsa+renesas@sang-engineering.com>


-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply

* Re: [PATCH v8 3/7] mux: add help text for MULTIPLEXER config option
From: Wolfram Sang @ 2026-02-04 15:19 UTC (permalink / raw)
  To: Josua Mayer
  Cc: Marc Kleine-Budde, Vincent Mailhol, Vinod Koul, Neil Armstrong,
	Peter Rosin, Aaro Koskinen, Andreas Kemnade, Kevin Hilman,
	Roger Quadros, Tony Lindgren, Janusz Krzysztofik, Vignesh R,
	Andi Shyti, Ulf Hansson, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Geert Uytterhoeven, Magnus Damm, Yazan Shhady,
	Jon Nettleton, Mikhail Anikin, linux-can, linux-phy, linux-kernel,
	linux-omap, linux-i2c, linux-mmc, devicetree, linux-renesas-soc
In-Reply-To: <20260203-rz-sdio-mux-v8-3-024ea405863e@solid-run.com>

On Tue, Feb 03, 2026 at 03:01:36PM +0200, Josua Mayer wrote:
> Add prompt and help text for CONFIG_MULTIPLEXER to allow enabling this
> option thorugh the kernel configuration without explicit "select" driver
> dependencies.
> 
> Select it by default when COMPILE_TEST is set for better coverage.
> 
> Signed-off-by: Josua Mayer <josua@solid-run.com>

Reviewed-by: Wolfram Sang <wsa+renesas@sang-engineering.com>


-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply

* Re: [PATCH] phy: PHY_GOOGLE_USB should depend on ARCH_GOOGLE
From: Vinod Koul @ 2026-02-04 15:19 UTC (permalink / raw)
  To: Geert Uytterhoeven
  Cc: Neil Armstrong, Peter Griffin, André Draszik, Tudor Ambarus,
	Naveen Kumar, Roy Luo, Joy Chakraborty, Arnd Bergmann, linux-phy,
	linux-arm-kernel, linux-samsung-soc, linux-kernel
In-Reply-To: <0caa3c449b0c3d64944da3f1003d9389bdc13f98.1769541083.git.geert+renesas@glider.be>

Hi Geert,

On 27-01-26, 20:12, Geert Uytterhoeven wrote:
> The Google Tensor SoC USB PHY is only present on Google Tensor G5
> (Laguna) SoCs.  Hence add a dependency on ARCH_GOOGLE, to prevent asking
> the user about this driver when configuring a kernel without Google
> Tensor SoC support.

I have three patches for this :-) Looking at it, I prefer Arnd's
approach and not to depend on unknown symbol.

-- 
~Vinod

-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply

* Re: [PATCH v8 4/7] phy: can-transceiver: drop temporary helper getting optional mux-state
From: Wolfram Sang @ 2026-02-04 15:19 UTC (permalink / raw)
  To: Josua Mayer
  Cc: Marc Kleine-Budde, Vincent Mailhol, Vinod Koul, Neil Armstrong,
	Peter Rosin, Aaro Koskinen, Andreas Kemnade, Kevin Hilman,
	Roger Quadros, Tony Lindgren, Janusz Krzysztofik, Vignesh R,
	Andi Shyti, Ulf Hansson, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Geert Uytterhoeven, Magnus Damm, Yazan Shhady,
	Jon Nettleton, Mikhail Anikin, linux-can, linux-phy, linux-kernel,
	linux-omap, linux-i2c, linux-mmc, devicetree, linux-renesas-soc
In-Reply-To: <20260203-rz-sdio-mux-v8-4-024ea405863e@solid-run.com>

On Tue, Feb 03, 2026 at 03:01:37PM +0200, Josua Mayer wrote:
> Multiplexer subsystem has now added helpers for getting managed optional
> mux-state.
> 
> Switch to the new devm_mux_state_get_optional helper.
> 
> This change is only compile-tested.
> 
> Acked-by: Vinod Koul <vkoul@kernel.org>
> Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
> Signed-off-by: Josua Mayer <josua@solid-run.com>

Reviewed-by: Wolfram Sang <wsa+renesas@sang-engineering.com>


-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply

* Re: [PATCH v8 5/7] i2c: omap: switch to new generic helper for getting selected mux-state
From: Wolfram Sang @ 2026-02-04 15:20 UTC (permalink / raw)
  To: Josua Mayer
  Cc: Marc Kleine-Budde, Vincent Mailhol, Vinod Koul, Neil Armstrong,
	Peter Rosin, Aaro Koskinen, Andreas Kemnade, Kevin Hilman,
	Roger Quadros, Tony Lindgren, Janusz Krzysztofik, Vignesh R,
	Andi Shyti, Ulf Hansson, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Geert Uytterhoeven, Magnus Damm, Yazan Shhady,
	Jon Nettleton, Mikhail Anikin, linux-can, linux-phy, linux-kernel,
	linux-omap, linux-i2c, linux-mmc, devicetree, linux-renesas-soc
In-Reply-To: <20260203-rz-sdio-mux-v8-5-024ea405863e@solid-run.com>

On Tue, Feb 03, 2026 at 03:01:38PM +0200, Josua Mayer wrote:
> Multiplexer subsystem has added generic helper functions for getting an
> already selected mux-state object.
> 
> Replace existing logic in probe with the equivalent helper function.
> 
> There is a functional difference in that the mux is now automatically
> deselected on release, replacing the explicit mux_state_deselect call.
> 
> This change is only compile-tested.
> 
> Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
> Reviewed-by: Andreas Kemnade <andreas@kemnade.info>
> Signed-off-by: Josua Mayer <josua@solid-run.com>

Reviewed-by: Wolfram Sang <wsa+renesas@sang-engineering.com>


-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply

* Re: [PATCH v8 6/7] dt-bindings: mmc: renesas,sdhi: Add mux-states property
From: Wolfram Sang @ 2026-02-04 15:20 UTC (permalink / raw)
  To: Josua Mayer
  Cc: Marc Kleine-Budde, Vincent Mailhol, Vinod Koul, Neil Armstrong,
	Peter Rosin, Aaro Koskinen, Andreas Kemnade, Kevin Hilman,
	Roger Quadros, Tony Lindgren, Janusz Krzysztofik, Vignesh R,
	Andi Shyti, Ulf Hansson, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Geert Uytterhoeven, Magnus Damm, Yazan Shhady,
	Jon Nettleton, Mikhail Anikin, linux-can, linux-phy, linux-kernel,
	linux-omap, linux-i2c, linux-mmc, devicetree, linux-renesas-soc
In-Reply-To: <20260203-rz-sdio-mux-v8-6-024ea405863e@solid-run.com>

On Tue, Feb 03, 2026 at 03:01:39PM +0200, Josua Mayer wrote:
> Add mux controller support for data or control lines that are muxed
> between a host and multiple cards.
> 
> There are several devices supporting a choice of eMMC or SD on a single
> board by both dip switch and gpio, e.g. Renesas RZ/G2L SMARC SoM and
> SolidRun RZ/G2L SoM.
> 
> In-tree dts for the Renesas boards currently rely on preprocessor macros
> and gpio hogs to describe the respective cards.
> 
> By adding mux-states property to sdhi controller description, boards can
> correctly describe the mux that already exists in hardware - and drivers
> can coordinate between mux selection and probing for cards.
> 
> Acked-by: Rob Herring (Arm) <robh@kernel.org>
> Signed-off-by: Josua Mayer <josua@solid-run.com>

Reviewed-by: Wolfram Sang <wsa+renesas@sang-engineering.com>


-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply

* Re: [PATCH v8 7/7] mmc: host: renesas_sdhi_core: support selecting an optional mux
From: Wolfram Sang @ 2026-02-04 15:20 UTC (permalink / raw)
  To: Josua Mayer
  Cc: Marc Kleine-Budde, Vincent Mailhol, Vinod Koul, Neil Armstrong,
	Peter Rosin, Aaro Koskinen, Andreas Kemnade, Kevin Hilman,
	Roger Quadros, Tony Lindgren, Janusz Krzysztofik, Vignesh R,
	Andi Shyti, Ulf Hansson, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Geert Uytterhoeven, Magnus Damm, Yazan Shhady,
	Jon Nettleton, Mikhail Anikin, linux-can, linux-phy, linux-kernel,
	linux-omap, linux-i2c, linux-mmc, devicetree, linux-renesas-soc
In-Reply-To: <20260203-rz-sdio-mux-v8-7-024ea405863e@solid-run.com>

On Tue, Feb 03, 2026 at 03:01:40PM +0200, Josua Mayer wrote:
> Some hardware designs route data or control signals through a mux to
> support multiple devices on a single sdhi controller.
> 
> In particular SolidRun RZ/G2L/G2LC/V2L System on Module use a mux for
> switching between soldered eMMC and an optional microSD on a carrier
> board, e.g. for development or provisioning.
> 
> SD/SDIO/eMMC are not well suited for runtime switching between different
> cards, however boot-time selection is possible and useful - in
> particular considering dt overlays.
> 
> Add support for an optional SD/SDIO/eMMC mux defined in dt, and select
> it during probe.
> 
> Similar functionality already exists in other places, e.g. i2c-omap.
> 
> Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
> Signed-off-by: Josua Mayer <josua@solid-run.com>

Reviewed-by: Wolfram Sang <wsa+renesas@sang-engineering.com>


-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply

* Re: [PATCH 3/4 v2] phy: s32g: Add serdes xpcs subsystem
From: Russell King (Oracle) @ 2026-02-04 15:29 UTC (permalink / raw)
  To: Vincent Guittot
  Cc: vkoul, neil.armstrong, krzk+dt, conor+dt, ciprianmarian.costea,
	s32, p.zabel, ghennadi.procopciuc, Ionut.Vicovan, linux-phy,
	devicetree, linux-kernel, linux-arm-kernel, netdev, horms,
	Frank.li
In-Reply-To: <20260203161917.1666696-4-vincent.guittot@linaro.org>

Sorry, I don't have time to finish this review, nor cut down the context
as I normally would do... I'm being bothered on company Slack, which now
has the really bloody annoying feature of popping up a window rather than
using KDE's notification subsystem, and that steals keyboard focus away
from whatever one is trying to do at the time.

On Tue, Feb 03, 2026 at 05:19:16PM +0100, Vincent Guittot wrote:
> +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;

XPCS_TIMEOUT_MS is 300ms. spin_until_cond() spins until the condition is
true. Do you need to tie up this CPU for up to 300ms? That seems
excessive. What is the reason that read_poll_timeout() or similar
couldn't be used?

The advantage of read_poll_timeout() is that it will correctly handle
the timeout vs condition being satisfied witout need for special code.

> +
> +	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;

Same here:

	return read_poll_timeout(s32g_xpcs_read, val, (val & mask) == bits,
				 0, XPCS_TIMEOUT_MS, false,
				 xpcs, reg);

> +
> +	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);

This could be:
	return read_poll_timeout(s32g_xpcs_read, val,
				 FIELD_GET(PSEQ_STATE_MASK, val) == POWER_GOOD_STATE,
				 0, XPCS_TIMEOUT_MS, false,
				 xpcs, VR_MII_DIG_STS);

eliminating the need for s32g_xpcs_digital_status().

> +}
> +
> +void s32g_xpcs_vreset(struct s32g_xpcs *xpcs)
> +{
> +	/* Step 19 */
> +	s32g_xpcs_write_bits(xpcs, VR_MII_DIG_CTRL1, VR_RST, VR_RST);
> +}
> +
> +static bool s32g_xpcs_is_not_in_reset(struct s32g_xpcs *xpcs)
> +{
> +	unsigned int val;
> +
> +	val = 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));

Prefer hex numbers to be lower case.

> +
> +	/* 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);
> +	}

I am really not happy with this driver being
PHY_INTERFACE_MODE_SGMII-only but supporting running that at 2.5Gbps.
In the kernel, PHY_INTERFACE_MODE_SGMII is strictly _Cisco_ SGMII only,
which means the version of it clocked at 1.25GHz, not 3.125GHz.

OCSGMII or whatever random name you call it tends to be only supported
without inband AN, and we have pushed everyone to adopt
PHY_INTERFACE_MODE_2500BASEX for that. Please do the same.

Should this SerDes be connected to a SFP cage, it will need to support
dynamically switching between Cisco SGMII and 2500BASE-X.

> +
> +	/* 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;
> +}
> +
> +/*
> + * phylink_pcs_ops
> + */
> +
> +static unsigned int s32cc_phylink_pcs_inband_caps(struct phylink_pcs *pcs,
> +						  phy_interface_t interface)
> +{
> +	switch (interface) {
> +	case PHY_INTERFACE_MODE_SGMII:
> +		return LINK_INBAND_DISABLE | LINK_INBAND_ENABLE;
> +
> +	default:
> +		return 0;
> +	}
> +}
> +
> +static int s32cc_phylink_pcs_config(struct phylink_pcs *pcs,
> +				    unsigned int neg_mode,
> +				    phy_interface_t interface,
> +				    const unsigned long *advertising,
> +				    bool permit_pause_to_mac)
> +{
> +	struct s32g_xpcs *xpcs = phylink_pcs_to_s32g_xpcs(pcs);
> +
> +	/* Step 1: Disable SGMII AN */
> +	s32g_xpcs_write_bits(xpcs, SR_MII_CTRL, AN_ENABLE, 0);
> +
> +	s32g_xpcs_write_bits(xpcs, VR_MII_AN_CTRL,
> +			     MII_AN_INTR_EN,
> +			     0);
> +
> +	if (!(neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED))
> +		return 0;
> +
> +	/* Step 2  */
> +	s32g_xpcs_write_bits(xpcs, VR_MII_AN_CTRL,
> +			     PCS_MODE_MASK,
> +			     FIELD_PREP(PCS_MODE_MASK, PCS_MODE_SGMII));
> +
> +	/* Step 3 */
> +	s32g_xpcs_write_bits(xpcs, SR_MII_CTRL,
> +			     SS13 | SS6,
> +			     SS6);
> +
> +	/* Step 4  */
> +	s32g_xpcs_write_bits(xpcs, VR_MII_AN_CTRL,
> +			     MII_CTRL,
> +			     0);
> +	/* Step 5 and 8 */
> +	if (xpcs->pcie_shared == PCIE_XPCS_2G5) {
> +		s32g_xpcs_write(xpcs, VR_MII_LINK_TIMER_CTRL, 0x2faf);
> +		s32g_xpcs_write_bits(xpcs, VR_MII_DIG_CTRL1,
> +				     MAC_AUTO_SW, MAC_AUTO_SW);
> +	} else {
> +		s32g_xpcs_write(xpcs, VR_MII_LINK_TIMER_CTRL, 0x7a1);
> +		s32g_xpcs_write_bits(xpcs, VR_MII_DIG_CTRL1, MAC_AUTO_SW, 0);
> +	}
> +
> +	/* Step 6 */
> +	s32g_xpcs_write_bits(xpcs, VR_MII_DIG_CTRL1,
> +			     CL37_TMR_OVRRIDE, CL37_TMR_OVRRIDE);
> +
> +	/* Step 7 */
> +	s32g_xpcs_write_bits(xpcs, VR_MII_AN_CTRL,
> +			     MII_AN_INTR_EN,
> +			     MII_AN_INTR_EN);
> +
> +	/* Step 9: Enable SGMII AN */
> +	s32g_xpcs_write_bits(xpcs, SR_MII_CTRL, AN_ENABLE, AN_ENABLE);
> +
> +	return 0;
> +}
> +
> +static void s32cc_phylink_pcs_get_state(struct phylink_pcs *pcs, unsigned int neg_mode,
> +					struct phylink_link_state *state)
> +{
> +	struct s32g_xpcs *xpcs = phylink_pcs_to_s32g_xpcs(pcs);
> +	bool ss6, ss13, an_enabled;
> +	struct device *dev = xpcs->dev;
> +	unsigned int val, ss;
> +
> +	an_enabled = (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED);
> +
> +	if (an_enabled) {
> +		state->link = 0;
> +		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;
> +			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;
> +		}
> +
> +	} else {
> +		val = s32g_xpcs_read(xpcs, SR_MII_STS);
> +		state->link = !!(val & LINK_STS);
> +		state->an_complete = 0;
> +		state->pause = MLO_PAUSE_NONE;
> +
> +		val = s32g_xpcs_read(xpcs, SR_MII_CTRL);
> +		if (val & 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 = !!(val & SS6);
> +		ss13 = !!(val & 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");
> +	}
> +}
> +
> +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 device *dev = xpcs->dev;
> +	bool an_enabled = (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED);
> +	unsigned int val;
> +	int ret;
> +
> +	dev_dbg(dev, "xpcs_%d: speed=%u duplex=%d an=%d\n", xpcs->id,
> +		speed, duplex, an_enabled);
> +
> +	if (an_enabled)
> +		return;
> +
> +	s32g_xpcs_write_bits(xpcs, SR_MII_CTRL, AN_ENABLE, 0);
> +	s32g_xpcs_write_bits(xpcs, VR_MII_AN_CTRL, MII_AN_INTR_EN, 0);
> +	s32g_xpcs_write_bits(xpcs, VR_MII_AN_CTRL, MII_CTRL, 0);

Haven't you already disabled AN in .pcs_config() ? This method doesn't
change the AN enable state, the only time that happens is when
.pcs_config() will be called. All other cases of passing neg_mode are
merely informational.

> +
> +	if (duplex == DUPLEX_FULL)
> +		val = DUPLEX_MODE;
> +	else
> +		val = 0;
> +
> +	s32g_xpcs_write_bits(xpcs, SR_MII_CTRL, DUPLEX_MODE, val);
> +
> +	switch (speed) {
> +	case SPEED_10:
> +		val = 0;
> +		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 (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");
> +	}

This is a protocol transition, and isn't something that can be handled
here. Cisco SGMII (PHY_INTERFACE_MODE_SGMII) does not support 2500Mbps
and phylink will not allow it.

See my comments for s32g_serdes_bifurcation_pll_transit().

> +
> +	s32g_xpcs_write_bits(xpcs, SR_MII_CTRL, SS6 | SS13, val);
> +}
> +
> +static const struct phylink_pcs_ops s32cc_phylink_pcs_ops = {
> +	.pcs_inband_caps = s32cc_phylink_pcs_inband_caps,
> +	.pcs_get_state = s32cc_phylink_pcs_get_state,
> +	.pcs_config = s32cc_phylink_pcs_config,
> +	.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;
> +}
> +
> +void s32g_xpcs_disable_an(struct s32g_xpcs *xpcs)
> +{
> +	s32g_xpcs_write_bits(xpcs, SR_MII_CTRL, DUPLEX_MODE, DUPLEX_MODE);
> +	s32g_xpcs_write_bits(xpcs, SR_MII_CTRL, AN_ENABLE, 0);
> +}

Sorry, but why? You should never override phylink's requests.

> +
> +int s32g_xpcs_init(struct s32g_xpcs *xpcs, struct device *dev,
> +		   unsigned char id, void __iomem *base, bool ext_clk,
> +		   unsigned long rate, enum s32g_xpcs_shared pcie_shared)
> +{
> +	struct regmap_config conf;
> +
> +	if (rate != (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/Kconfig b/drivers/phy/freescale/Kconfig
> index 45184a3cdd69..bb7f59897faf 100644
> --- a/drivers/phy/freescale/Kconfig
> +++ b/drivers/phy/freescale/Kconfig
> @@ -66,6 +66,7 @@ config PHY_S32G_SERDES
>  	tristate "NXP S32G SERDES support"
>  	depends on ARCH_S32 || COMPILE_TEST
>  	select GENERIC_PHY
> +	select REGMAP
>  	help
>  	  This option enables support for S23G SerDes PHY used for
>  	  PCIe & Ethernet
> diff --git a/drivers/phy/freescale/phy-nxp-s32g-serdes.c b/drivers/phy/freescale/phy-nxp-s32g-serdes.c
> index 321a80c02be5..f2f7eb5aa327 100644
> --- a/drivers/phy/freescale/phy-nxp-s32g-serdes.c
> +++ b/drivers/phy/freescale/phy-nxp-s32g-serdes.c
> @@ -12,12 +12,14 @@
>  #include <linux/module.h>
>  #include <linux/of_platform.h>
>  #include <linux/of_address.h>
> +#include <linux/pcs/pcs-nxp-s32g-xpcs.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_XPCS_MAX			2
>  #define S32G_SERDES_MODE_MAX			5
>  
>  #define EXTERNAL_CLK_NAME			"ext"
> @@ -32,6 +34,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)
> @@ -44,6 +92,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
>  
> @@ -76,16 +127,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;
> @@ -277,6 +345,192 @@ 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 s32g_xpcs_shared 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);
> +}
> +
> +static int s32g_serdes_enable_mode5(struct s32g_serdes *serdes, struct s32g_xpcs *xpcs)
> +{
> +	int ret;
> +
> +	s32g_serdes_prepare_pma_mode5(serdes);
> +
> +	ret = s32g_xpcs_pre_pcie_2g5(xpcs);
> +	if (ret) {
> +		dev_err(serdes->dev,
> +			"Failed to prepare SerDes for PCIE & XPCS @ 2G5 mode\n");
> +		return ret;
> +	}
> +
> +	s32g_pcie_phy_reset(serdes);
> +
> +	return 0;
> +}
> +
> +static int s32g_serdes_init_clks(struct s32g_serdes *serdes)
> +{
> +	struct s32g_serdes_ctrl *ctrl = &serdes->ctrl;
> +	struct s32g_xpcs_ctrl *xpcs = &serdes->xpcs;
> +	struct s32g_xpcs *order[2];
> +	size_t i;
> +	int ret;
> +
> +	switch (ctrl->ss_mode) {
> +	case 0:
> +		return 0;
> +	case 1:
> +		order[0] = xpcs->phys[0];
> +		order[1] = NULL;
> +		break;
> +	case 2:
> +	case 5:
> +		order[0] = xpcs->phys[1];
> +		order[1] = NULL;
> +		break;
> +	case 3:
> +		order[0] = xpcs->phys[1];
> +		order[1] = xpcs->phys[0];
> +		break;
> +	case 4:
> +		order[0] = xpcs->phys[0];
> +		order[1] = xpcs->phys[1];
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	for (i = 0; i < ARRAY_SIZE(order); i++) {
> +		if (!order[i])
> +			continue;
> +
> +		ret = s32g_xpcs_init_plls(order[i]);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	for (i = 0; i < ARRAY_SIZE(order); i++) {
> +		if (!order[i])
> +			continue;
> +
> +		if (ctrl->ss_mode == 5) {
> +			ret = s32g_serdes_enable_mode5(serdes, order[i]);
> +			if (ret)
> +				return ret;
> +		} else {
> +			s32g_xpcs_vreset(order[i]);
> +		}
> +	}
> +
> +	for (i = 0; i < ARRAY_SIZE(order); i++) {
> +		if (!order[i])
> +			continue;
> +
> +		ret = s32g_xpcs_wait_vreset(order[i]);
> +		if (ret)
> +			return ret;
> +
> +		ret = s32g_xpcs_reset_rx(order[i]);
> +		if (ret)
> +			return ret;
> +
> +		s32g_xpcs_disable_an(order[i]);
> +	}
> +
> +	return 0;
> +}
> +
>  /* Serdes subsystem */
>  
>  static int s32g_serdes_assert_reset(struct s32g_serdes *serdes)
> @@ -331,6 +585,10 @@ static int s32g_serdes_init(struct s32g_serdes *serdes)
>  		return ret;
>  	}
>  
> +	/*
> +	 * 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;
> @@ -363,7 +621,13 @@ static int s32g_serdes_init(struct s32g_serdes *serdes)
>  	dev_info(serdes->dev, "Using mode %d for SerDes subsystem\n",
>  		 ctrl->ss_mode);
>  
> -	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,
> @@ -449,12 +713,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")) {
> @@ -476,6 +760,37 @@ static int s32g2_serdes_create_phy(struct s32g_serdes *serdes, struct device_nod
>  		if (IS_ERR(phy_provider))
>  			return PTR_ERR(phy_provider);
>  
> +	} 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 (!xpcs)
> +			return -ENOMEM;
> +
> +		xpcs_ctrl->phys[port] = xpcs;
> +
> +		ret = s32g_serdes_xpcs_init(serdes, port);
> +		if (ret)
> +			return ret;
> +
>  	} else {
>  		dev_warn(dev, "Skipping unknown child node %pOFn\n", child_node);
>  	}
> @@ -517,6 +832,10 @@ static int s32g_serdes_probe(struct platform_device *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;
> @@ -555,6 +874,57 @@ 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)
> +		return ERR_PTR(-EPROBE_DEFER);
> +
> +	serdes = platform_get_drvdata(pdev);
> +	if (!serdes) {
> +		put_device(&pdev->dev);
> +		return ERR_PTR(-EPROBE_DEFER);
> +	}
> +
> +	if (!serdes->xpcs.phys[port]) {
> +		put_device(&pdev->dev);
> +		return ERR_PTR(-EPROBE_DEFER);
> +	}
> +
> +	return &serdes->xpcs.phys[port]->pcs;
> +}
> +EXPORT_SYMBOL(s32g_serdes_pcs_create);
> +
> +void s32g_serdes_pcs_destroy(struct phylink_pcs *pcs)
> +{
> +	struct s32g_xpcs *xpcs = phylink_pcs_to_s32g_xpcs(pcs);
> +
> +	put_device(xpcs->dev);
> +}
> +EXPORT_SYMBOL(s32g_serdes_pcs_destroy);
> +
>  static const struct of_device_id s32g_serdes_match[] = {
>  	{
>  		.compatible = "nxp,s32g2-serdes",
> diff --git a/include/linux/pcs/pcs-nxp-s32g-xpcs.h b/include/linux/pcs/pcs-nxp-s32g-xpcs.h
> new file mode 100644
> index 000000000000..96a0049b93a6
> --- /dev/null
> +++ b/include/linux/pcs/pcs-nxp-s32g-xpcs.h
> @@ -0,0 +1,50 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/**
> + * Copyright 2021-2026 NXP
> + */
> +#ifndef PCS_NXP_S32G_XPCS_H
> +#define PCS_NXP_S32G_XPCS_H
> +
> +#include <linux/phylink.h>
> +
> +enum s32g_xpcs_shared {
> +	NOT_SHARED,
> +	PCIE_XPCS_1G,
> +	PCIE_XPCS_2G5,
> +};
> +
> +enum s32g_xpcs_pll {
> +	XPCS_PLLA,	/* Slow PLL */
> +	XPCS_PLLB,	/* Fast PLL */
> +};
> +
> +struct s32g_xpcs {
> +	void __iomem *base;
> +	struct device *dev;
> +	unsigned char id;
> +	struct regmap *regmap;
> +	enum s32g_xpcs_pll ref;
> +	bool ext_clk;
> +	bool mhz125;
> +	enum s32g_xpcs_shared pcie_shared;
> +	struct phylink_pcs pcs;
> +};
> +
> +#define phylink_pcs_to_s32g_xpcs(pl_pcs) \
> +	container_of((pl_pcs), struct s32g_xpcs, pcs)
> +
> +int s32g_xpcs_init(struct s32g_xpcs *xpcs, struct device *dev,
> +		   unsigned char id, void __iomem *base, bool ext_clk,
> +		   unsigned long rate, enum s32g_xpcs_shared pcie_shared);
> +int s32g_xpcs_init_plls(struct s32g_xpcs *xpcs);
> +int s32g_xpcs_pre_pcie_2g5(struct s32g_xpcs *xpcs);
> +void s32g_xpcs_vreset(struct s32g_xpcs *xpcs);
> +int s32g_xpcs_wait_vreset(struct s32g_xpcs *xpcs);
> +int s32g_xpcs_reset_rx(struct s32g_xpcs *xpcs);
> +void s32g_xpcs_disable_an(struct s32g_xpcs *xpcs);
> +
> +struct phylink_pcs *s32g_serdes_pcs_create(struct device *dev, struct device_node *np);
> +void s32g_serdes_pcs_destroy(struct phylink_pcs *pcs);
> +
> +#endif /* PCS_NXP_S32G_XPCS_H */
> +
> -- 
> 2.43.0
> 
> 


-- 
RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
FTTP is here! 80Mbps down 10Mbps up. Decent connectivity at last!

-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply

* Re: [PATCH v8 0/7] mmc: host: renesas_sdhi_core: support configuring an optional sdio mux
From: Ulf Hansson @ 2026-02-04 15:33 UTC (permalink / raw)
  To: Josua Mayer
  Cc: Marc Kleine-Budde, Vincent Mailhol, Vinod Koul, Neil Armstrong,
	Peter Rosin, Aaro Koskinen, Andreas Kemnade, Kevin Hilman,
	Roger Quadros, Tony Lindgren, Janusz Krzysztofik, Vignesh R,
	Andi Shyti, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Geert Uytterhoeven, Magnus Damm, Wolfram Sang, Yazan Shhady,
	Jon Nettleton, Mikhail Anikin, linux-can, linux-phy, linux-kernel,
	linux-omap, linux-i2c, linux-mmc, devicetree, linux-renesas-soc
In-Reply-To: <20260203-rz-sdio-mux-v8-0-024ea405863e@solid-run.com>

On Tue, 3 Feb 2026 at 14:01, Josua Mayer <josua@solid-run.com> wrote:
>
> Some Renesas SoC based boards mux SD and eMMC on a single sdio
> controller, exposing user control by dip switch and software control by
> gpio.
>
> Purpose is to simplify development and provisioning by selecting boot
> media at power-on, and again before starting linux.
>
> Add binding and driver support for linking a (gpio) mux to renesas sdio
> controller.
>
> Introduce generic helper functions for getting managed and selected
> mux-state objects, and switch i2c-omap and phy-can-transceiver drivers.
>
> Signed-off-by: Josua Mayer <josua@solid-run.com>
> ---
> Changes in v8:
> - Add defensive null checks for all non-optional calls to internal
>   mux_get function.
> - Document NULL return value on applicable functions.
> - Avoid IS_ERR_OR_NULL and ERR_PTR(0) to disarm smatch errors.
> - Link to v7: https://lore.kernel.org/r/20260128-rz-sdio-mux-v7-0-92ebb6da0df8@solid-run.com
>
> Changes in v7:
> - picked up reviewed-tags
> - fix Kconfig change to add the missing prompt for CONFIG_MULTIPLEXER,
>   and enable it by default when COMPILE_TEST is set.
>   (Reported-by: Geert Uytterhoeven <geert@linux-m68k.org>)
> - fix another kernel build robot warning: undocumented C struct member
> - Link to v6: https://lore.kernel.org/r/20260121-rz-sdio-mux-v6-0-38aa39527928@solid-run.com
>
> Changes in v6:
> - replaced /* with /** for devm_mux_state_state function description.
> - collected review tags.
> - fixed checkpatch warnings (space-before-tab, void-return).
>   (Reported-by: Geert Uytterhoeven)
> - fixed use-after-free in mux core mux_get function.
>   (Reported-by: Geert Uytterhoeven)
> - fix mux helper error path uninitialised return code variable.
>   (Reported-by: kernel test robot <lkp@intel.com>)
> - Link to v5: https://lore.kernel.org/r/20260118-rz-sdio-mux-v5-0-3c37e8872683@solid-run.com
>
> Changes in v5:
> - implemented automatic mux deselect for devm_*_selected.
>   (Reported-by: Wolfram Sang <wsa+renesas@sang-engineering.com>)
> - because of semantic changes I dropped reviewed and acks from omap-i2c
>   patch (Andreas Kemnade / Wolfram Sang).
> - fix invalid return value in void function for mux helper stubs
>   (Reported-by: kernel test robot <lkp@intel.com>)
> - Link to v4: https://lore.kernel.org/r/20251229-rz-sdio-mux-v4-0-a023e55758fe@solid-run.com
>
> Changes in v4:
> - added MULTIPLEXER Kconfig help text.
> - removed "select MULTIPLEXER" from renesas sdhi Kconfig, as it is
>   not required for all devices using this driver.
> - added stubs for all symbols exported by mux core.
>   (Reported-by: Geert Uytterhoeven <geert@linux-m68k.org>)
> - refactored mux core logic to silence ENOENT errors only on optional
>   code paths, keeping error printing unchanged otherwise.
>   (Reported-by: Geert Uytterhoeven <geert@linux-m68k.org>)
> - picked up various reviewed- and acked-by tags
> - Link to v3: https://lore.kernel.org/r/20251210-rz-sdio-mux-v3-0-ca628db56d60@solid-run.com
>
> Changes in v3:
> - updated omap-i2c and phy-can-transceiver to use new helpers.
> - created generic helper functions for getting managed optional mux-state.
>   (Reported-by: Rob Herring <robh@kernel.org>)
> - picked up binding ack by Rob Herring.
> - replaced use of "SDIO" with "SD/SDIO/eMMC" in binding document and
>   commit descriptions.
>   (Reported-by: Ulf Hansson <ulf.hansson@linaro.org>)
> - Link to v2: https://lore.kernel.org/r/20251201-rz-sdio-mux-v2-0-bcb581b88dd7@solid-run.com
>
> Changes in v2:
> - dropped mux-controller node from dt binding example
>   (Reported-by: Conor Dooley <conor@kernel.org>
>    Reported-by: Krzysztof Kozlowski <krzk@kernel.org>)
> - Link to v1: https://lore.kernel.org/r/20251128-rz-sdio-mux-v1-0-1ede318d160f@solid-run.com
>
> ---
> Josua Mayer (7):
>       phy: can-transceiver: rename temporary helper function to avoid conflict
>       mux: Add helper functions for getting optional and selected mux-state
>       mux: add help text for MULTIPLEXER config option
>       phy: can-transceiver: drop temporary helper getting optional mux-state
>       i2c: omap: switch to new generic helper for getting selected mux-state
>       dt-bindings: mmc: renesas,sdhi: Add mux-states property
>       mmc: host: renesas_sdhi_core: support selecting an optional mux
>
>  .../devicetree/bindings/mmc/renesas,sdhi.yaml      |   6 +
>  drivers/i2c/busses/i2c-omap.c                      |  24 +--
>  drivers/mmc/host/renesas_sdhi_core.c               |   6 +
>  drivers/mux/Kconfig                                |   8 +-
>  drivers/mux/core.c                                 | 205 +++++++++++++++++----
>  drivers/phy/phy-can-transceiver.c                  |  10 -
>  include/linux/mux/consumer.h                       | 108 ++++++++++-
>  7 files changed, 302 insertions(+), 65 deletions(-)
> ---
> base-commit: 8f0b4cce4481fb22653697cced8d0d04027cb1e8
> change-id: 20251128-rz-sdio-mux-acc5137f1618
>
> Best regards,
> --
> Josua Mayer <josua@solid-run.com>
>
>

The series applied for next, thanks!

Kind regards
Uffe

-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply

* Re: [PATCH phy] phy: enter drivers/phy/Makefile even without CONFIG_GENERIC_PHY
From: Vinod Koul @ 2026-02-04 15:44 UTC (permalink / raw)
  To: linux-phy, Neil Armstrong, Vladimir Oltean
  Cc: netdev, Paolo Abeni, Bjørn Mork, linux-kernel,
	Venkat Rao Bagalkote, Christophe Leroy (CS GROUP)
In-Reply-To: <20260123110600.3118561-1-vladimir.oltean@nxp.com>


On Fri, 23 Jan 2026 13:06:00 +0200, Vladimir Oltean wrote:
> Kconfig option CONFIG_PHY_COMMON_PROPS, which builds
> drivers/phy/phy-common-props.c, was intended to be selectable
> independently of CONFIG_GENERIC_PHY. Yet it lives in drivers/phy/, which
> is entered by the Makefile only if CONFIG_GENERIC_PHY is set.
> 
> Allow the Makefile to enter one level deeper, but stop at drivers/phy/
> if CONFIG_GENERIC_PHY is unselected (i.e. do not enter vendor folders).
> The other stuff from drivers/phy/Makefile except for CONFIG_PHY_COMMON_PROPS,
> like CONFIG_PHY_NXP_PTN3222, all depends on CONFIG_GENERIC_PHY.
> 
> [...]

Applied, thanks!

[1/1] phy: enter drivers/phy/Makefile even without CONFIG_GENERIC_PHY
      commit: 3ddcd24b4d8454b2b9b2d013a0d61986ae8bbbe7

Best regards,
-- 
~Vinod



-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply

* Re: [PATCH] phy: GOOGLE_USB: add TYPEC dependency
From: Vinod Koul @ 2026-02-04 15:44 UTC (permalink / raw)
  To: Joy Chakraborty, Naveen Kumar, Roy Luo, Arnd Bergmann
  Cc: Arnd Bergmann, Neil Armstrong, Alex Elder, Ivaylo Ivanov,
	Dmitry Baryshkov, Inochi Amaoto, Sven Peter, Vladimir Oltean,
	linux-phy, linux-kernel
In-Reply-To: <20260202095655.1289973-1-arnd@kernel.org>


On Mon, 02 Feb 2026 10:56:52 +0100, Arnd Bergmann wrote:
> With CONFIG_TYPEC=m, this driver cannot be built-in:
> 
> arm-linux-gnueabi/bin/arm-linux-gnueabi-ld: drivers/phy/phy-google-usb.o: in function `google_usb_phy_remove':
> phy-google-usb.c:(.text+0x24): undefined reference to `typec_switch_unregister'
> 
> Add CONFIG_TYPEC as a hard dependency here to force a clean build.
> In theory, compile-testing with CONFIG_TYPEC=n would also work, but
> that seems pointless.
> 
> [...]

Applied, thanks!

[1/1] phy: GOOGLE_USB: add TYPEC dependency
      commit: 8a13968460004d21dd0ca457b06fccfdfdfafd6c

Best regards,
-- 
~Vinod



-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply

* Re: [PATCH] phy: renesas: rcar-gen3-usb2: add regulator dependency
From: Vinod Koul @ 2026-02-04 15:44 UTC (permalink / raw)
  To: Tommaso Merciai, Arnd Bergmann
  Cc: Arnd Bergmann, Neil Armstrong, Biju Das, linux-phy, linux-kernel
In-Reply-To: <20260202095118.1233046-1-arnd@kernel.org>


On Mon, 02 Feb 2026 10:51:14 +0100, Arnd Bergmann wrote:
> The driver start registering a regulator, but can still be
> enabled even when it is unable to call into the regulator
> subsystem:
> 
> aarch64-linux-ld: drivers/phy/renesas/phy-rcar-gen3-usb2.o: in function `rcar_gen3_phy_usb2_probe':
> phy-rcar-gen3-usb2.c:(.text+0x2884): undefined reference to `devm_regulator_register'
> 
> [...]

Applied, thanks!

[1/1] phy: renesas: rcar-gen3-usb2: add regulator dependency
      commit: 3a03a0e47cf2e0ec7ce7ca9e0bf4c59ec537ad09

Best regards,
-- 
~Vinod



-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply

* Re: [PATCH] phy: tegra: xusb: Remove unused powered_on variable
From: Vinod Koul @ 2026-02-04 15:44 UTC (permalink / raw)
  To: JC Kuo, Thierry Reding, Jon Hunter; +Cc: linux-phy, linux-tegra
In-Reply-To: <20260202153314.1634145-1-jonathanh@nvidia.com>


On Mon, 02 Feb 2026 15:33:14 +0000, Jon Hunter wrote:
> Commit bbf711682cd5 ("phy: tegra: xusb: Add Tegra186 support") added the
> variable 'powered_on' to the structure 'tegra_xusb_usb2_lane' but it has
> never been used. Therefore, remove this unused variable.
> 
> 

Applied, thanks!

[1/1] phy: tegra: xusb: Remove unused powered_on variable
      commit: eeca25fe13a2f690f659a6b43ebbb270b073a6c4

Best regards,
-- 
~Vinod



-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply

* Re: (subset) [PATCH v4 0/3] dt-bindings: phy: Convert TI OMAP control and PIPE3 PHY to DT schema
From: Vinod Koul @ 2026-02-04 15:44 UTC (permalink / raw)
  To: Neil Armstrong, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Kishon Vijay Abraham I, Aaro Koskinen, Andreas Kemnade,
	Kevin Hilman, Roger Quadros, Tony Lindgren, Roger Quadros,
	Charan Pedumuru
  Cc: linux-phy, devicetree, linux-kernel, linux-omap
In-Reply-To: <20260123-ti-phy-v4-0-b557e2c46e6f@gmail.com>


On Fri, 23 Jan 2026 15:39:01 +0000, Charan Pedumuru wrote:
> This series converts the old text-based DeviceTree bindings for TI OMAP
> Control PHY and TI PIPE3 PHY to modern JSON-schema (YAML) format.
> 
> 

Applied, thanks!

[2/3] dt-bindings: phy: ti,phy-usb3: convert to DT schema
      commit: 62c9ff8fc20d23c0dc36be1330734fdafb3e8585
[3/3] dt-bindings: phy: ti,control-phy-otghs: convert to DT schema
      commit: 7878306d182a1750583a325a29e5ccab9ce0235b

Best regards,
-- 
~Vinod



-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply

* Re: (subset) [PATCH 00/27] clk: remove deprecated API divider_round_rate() and friends
From: Vinod Koul @ 2026-02-04 15:44 UTC (permalink / raw)
  To: Michael Turquette, Stephen Boyd, Brian Masney
  Cc: linux-clk, linux-kernel, Chen Wang, Inochi Amaoto, sophgo,
	Chen-Yu Tsai, Maxime Ripard, Jernej Skrabec, Samuel Holland,
	linux-arm-kernel, linux-sunxi, Alexandre Belloni, linux-rtc,
	Andreas Färber, Manivannan Sadhasivam, linux-actions,
	Keguang Zhang, linux-mips, Taichi Sugaya, Takao Orito,
	Jacky Huang, Shan-Chun Hung, Vladimir Zapolskiy,
	Piotr Wojtaszczyk, Bjorn Andersson, linux-arm-msm, Orson Zhai,
	Baolin Wang, Chunyan Zhang, Maxime Coquelin, Alexandre Torgue,
	linux-stm32, Michal Simek, Rob Clark, Dmitry Baryshkov,
	David Airlie, Simona Vetter, Abhinav Kumar, Jessica Zhang,
	Sean Paul, Marijn Suijten, dri-devel, freedreno, Neil Armstrong,
	linux-phy
In-Reply-To: <20260108-clk-divider-round-rate-v1-0-535a3ed73bf3@redhat.com>


On Thu, 08 Jan 2026 16:16:18 -0500, Brian Masney wrote:
> Here's a series that gets rid of the deprecated APIs
> divider_round_rate(), divider_round_rate_parent(), and
> divider_ro_round_rate_parent() since these functions are just wrappers
> for the determine_rate variant.
> 
> Note that when I converted some of these drivers from round_rate to
> determine_rate, this was mistakenly converted to the following in some
> cases:
> 
> [...]

Applied, thanks!

[25/27] phy: ti: phy-j721e-wiz: convert from divider_round_rate() to divider_determine_rate()
        commit: dbeea86fecef7cf2b93aded4525d74f6277376ef

Best regards,
-- 
~Vinod



-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply

* Re: [PATCH v8 7/7] mmc: host: renesas_sdhi_core: support selecting an optional mux
From: Wolfram Sang @ 2026-02-04 15:47 UTC (permalink / raw)
  To: Josua Mayer
  Cc: Marc Kleine-Budde, Vincent Mailhol, Vinod Koul, Neil Armstrong,
	Peter Rosin, Aaro Koskinen, Andreas Kemnade, Kevin Hilman,
	Roger Quadros, Tony Lindgren, Janusz Krzysztofik, Vignesh R,
	Andi Shyti, Ulf Hansson, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Geert Uytterhoeven, Magnus Damm, Yazan Shhady,
	Jon Nettleton, Mikhail Anikin, linux-can, linux-phy, linux-kernel,
	linux-omap, linux-i2c, linux-mmc, devicetree, linux-renesas-soc
In-Reply-To: <20260203-rz-sdio-mux-v8-7-024ea405863e@solid-run.com>

On Tue, Feb 03, 2026 at 03:01:40PM +0200, Josua Mayer wrote:
> Some hardware designs route data or control signals through a mux to
> support multiple devices on a single sdhi controller.
> 
> In particular SolidRun RZ/G2L/G2LC/V2L System on Module use a mux for
> switching between soldered eMMC and an optional microSD on a carrier
> board, e.g. for development or provisioning.
> 
> SD/SDIO/eMMC are not well suited for runtime switching between different
> cards, however boot-time selection is possible and useful - in
> particular considering dt overlays.
> 
> Add support for an optional SD/SDIO/eMMC mux defined in dt, and select
> it during probe.
> 
> Similar functionality already exists in other places, e.g. i2c-omap.
> 
> Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
> Signed-off-by: Josua Mayer <josua@solid-run.com>

And to confirm the obvious, no regression found on a Renesas SparrowHawk
(R-Car V4H) not using muxes:

Tested-by: Wolfram Sang <wsa+renesas@sang-engineering.com>


-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply

* Re: [PATCH 5/8] dt-bindings: mmc: axiado: Add axiado eMMC variant
From: Tzu-Hao Wei @ 2026-02-05  3:35 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  Cc: SriNavmani A, Prasad Bolisetty, Vinod Koul, Neil Armstrong,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Ulf Hansson,
	Adrian Hunter, Michal Simek, linux-phy, devicetree,
	linux-arm-kernel, linux-kernel, linux-mmc
In-Reply-To: <20251223-ludicrous-carmine-mushroom-983c69@quoll>

On 12/23/2025 10:43 PM, Krzysztof Kozlowski wrote:
> 
> No need for a new example. Please writing bindings and writing schema
> docs first.
> 
> Best regards,
> Krzysztof
> 
Thanks. Removed.


Best regards,
TH

-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply

* Re: [PATCH 5/8] dt-bindings: mmc: axiado: Add axiado eMMC variant
From: Tzu-Hao Wei @ 2026-02-05  3:35 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  Cc: SriNavmani A, Prasad Bolisetty, Vinod Koul, Neil Armstrong,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Harshit Shah,
	Ulf Hansson, Adrian Hunter, Michal Simek, linux-phy, devicetree,
	linux-arm-kernel, linux-kernel, linux-mmc
In-Reply-To: <20251227-industrious-mammoth-of-culture-ff9dbe@quoll>

On 12/27/2025 8:50 PM, Krzysztof Kozlowski wrote:
> CAUTION: This email originated from outside of the organization. Do not click links or open attachments unless you recognize the sender and know the content is safe.
> 
> 
> On Mon, Dec 22, 2025 at 04:45:04PM +0800, Tzu-Hao Wei wrote:
>> From: SriNavmani A <srinavmani@axiado.com>
>>
>> Add device tree binding variant for the Axiado AX3000 Arasan eMMC
>> controller. This variant identifies the specific eMMC controller
>> implementation used on the AX3000 SoC and enables the appropriate
>> driver configuration.
>>
>> Signed-off-by: Tzu-Hao Wei <twei@axiado.com>
> 
> Incorrect DCO chain.
> 
> 
> Best regards,
> Krzysztof
> 
Fixed DOC chain in the new patchset.

Best regards,
TH

-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply

* Re: [PATCH 6/8] mmc: host: axiado: add AX3000 eMMC PHY support to sdhci-of-arasan
From: Tzu-Hao Wei @ 2026-02-05  3:36 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  Cc: SriNavmani A, Prasad Bolisetty, Vinod Koul, Neil Armstrong,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Harshit Shah,
	Ulf Hansson, Adrian Hunter, Michal Simek, linux-phy, devicetree,
	linux-arm-kernel, linux-kernel, linux-mmc
In-Reply-To: <20251223-optimal-ochre-kestrel-a8cb1c@quoll>

On 12/23/2025 10:34 PM, Krzysztof Kozlowski wrote:
>> @@ -2019,15 +2023,17 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
>>                       goto unreg_clk;
>>               }
>>
>> -             host->mmc_host_ops.hs400_enhanced_strobe =
>> +             if (!of_device_is_compatible(np, "axiado,ax3000-sdhci-5.1-emmc")) {
> 
> No, don't sprinkle compatibles all around. You should use driver match
> data for this.
> 
> Best regards,
> Krzysztof
> 
Thanks for suggestion. The new patch uses driver match data instead of sprinkling compatibles.

Best regards,
TH

-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply

* Re: [PATCH 2/8] phy: axiado: add Arasan eMMC-PHY for Axiado
From: Tzu-Hao Wei @ 2026-02-05  3:36 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  Cc: SriNavmani A, Prasad Bolisetty, Vinod Koul, Neil Armstrong,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Harshit Shah,
	Ulf Hansson, Adrian Hunter, Michal Simek, linux-phy, devicetree,
	linux-arm-kernel, linux-kernel, linux-mmc
In-Reply-To: <20251223-grumpy-daft-loon-6a6186@quoll>

On 12/23/2025 10:32 PM, Krzysztof Kozlowski wrote:
> CAUTION: This email originated from outside of the organization. Do not click links or open attachments unless you recognize the sender and know the content is safe.
> 
> 
> On Mon, Dec 22, 2025 at 04:45:01PM +0800, Tzu-Hao Wei wrote:
>> @@ -15,6 +15,7 @@ obj-$(CONFIG_PHY_AIROHA_PCIE)               += phy-airoha-pcie.o
>>  obj-$(CONFIG_PHY_NXP_PTN3222)                += phy-nxp-ptn3222.o
> 
> 
> Where is maintainers file update in this patch? Why shall we take
> unmaintained code?
> 
It's in this series 4/8.

>>  obj-y                                        += allwinner/   \
>>                                          amlogic/     \
>> +                                        axiado/      \
>>                                          broadcom/    \
>>                                          cadence/     \
>>                                          freescale/   \
>> diff --git a/drivers/phy/axiado/Kconfig b/drivers/phy/axiado/Kconfig
>> new file mode 100644
>> index 0000000000000000000000000000000000000000..824114e6068da327308321b9884552ad33db9efc
>> --- /dev/null
>> +++ b/drivers/phy/axiado/Kconfig
>> @@ -0,0 +1,15 @@
>> +#
>> +# PHY drivers for Axiado platforms
>> +#
>> +
> 
> Missing menuconfig or other if-block for groupping this with your ARCH
> and COMPILE_TEST dependency.
> 
Updated in the next version with
depends on OF && (ARCH_AXIADO || COMPILE_TEST)

> Look how other NEW and MAINTAINED platforms did it.
> 
>> +config PHY_AX3000_EMMC
>> +     tristate "Axiado eMMC PHY driver"
>> +     select GENERIC_PHY
>> +     help
>> +       This enables support for the eMMC PHY block found on the
>> +       Axiado AX3000 SoCs. The PHY provides the physical layer
>> +       interface used by the Arasan SDHCI host controller for emmc
>> +       signaling and timing adjustment.
>> +
>> +       If you are building a kernel for AX3000 platform with
>> +       eMMC storage, say Y or N.
> 
> ...
> 
> 
Clean up the description in the new patch.

>> +static void arasan_emmc_phy_write(struct axiado_emmc_phy *ax_phy, u32 offset, u32 data)
>> +{
>> +     writel(data, ax_phy->reg_base + offset);
>> +}
>> +
>> +static int arasan_emmc_phy_read(struct axiado_emmc_phy *ax_phy, u32 offset)
> 
> Useless wrappers. Just use readl/writel directly. You are not making
> code more readable.
> 
Removed wrappers and use readl/writel directly.

>> +
>> +     while (1) {
> 
> You proper read_poll loop.
Removed while() and use readl_poll_timeout()

>> +             bool timedout = ktime_after(ktime_get(), timeout);
>> +
>> +             if (arasan_emmc_phy_read(ax_phy, STATUS) & DLL_RDY_MASK)
>> +                     break;
>> +
>> +             if (timedout) {
>> +                     dev_err(&phy->dev, "DLL_RDY_MASK bit is not cleared.");
>> +                     return -ETIMEDOUT;
>> +             }
>> +             udelay(TIMEOUT_DELAY);
> 
> ...
> 
Removed.

>> +static int axiado_emmc_phy_probe(struct platform_device *pdev)
>> +{
>> +     struct axiado_emmc_phy *ax_phy;
>> +     struct phy_provider *phy_provider;
>> +     struct device *dev = &pdev->dev;
>> +     const struct of_device_id *id;
>> +     struct phy *generic_phy;
>> +     struct resource *res;
>> +
>> +     if (!dev->of_node)
>> +             return -ENODEV;
>> +
>> +     ax_phy = devm_kzalloc(dev, sizeof(*ax_phy), GFP_KERNEL);
>> +     if (!ax_phy)
>> +             return -ENOMEM;
>> +
>> +     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +
> 
> Use proper wrapper to combine get resource and ioremap.
> 
Fixed.
Calling devm_platform_ioremap_resource(pdev, 0) now.

>> +     ax_phy->reg_base = devm_ioremap_resource(&pdev->dev, res);
>> +
> 
> Drop blank line, there's never such.
> 
Removed
>> +     if (IS_ERR(ax_phy->reg_base))
>> +             return PTR_ERR(ax_phy->reg_base);
>> +
>> +     id = of_match_node(axiado_emmc_phy_of_match, pdev->dev.of_node);
>> +     if (!id) {
>> +             dev_err(dev, "failed to get match_node\n");
> 
> What is the point of this? You do not use this match at all, no other
> devices. How can your device bind and still fail the match?
> 
> Drop
> 
Removed.

>> +             return -EINVAL;
>> +     }
>> +
>> +     generic_phy = devm_phy_create(dev, dev->of_node, &axiado_emmc_phy_ops);
>> +     if (IS_ERR(generic_phy)) {
>> +             dev_err(dev, "failed to create PHY\n");
>> +             return PTR_ERR(generic_phy);
> 
> Syntax is - return dev_err_probe.
> 
Fixed.

> Didn't Axiado receive this feedback before? Are you sure that you have
> procedures set inside to avoid repeating same mistakes?
> 
> Best regards,
> Krzysztof
> 
Thanks Krzysztof.

I have checked the comments one-by-one and make sure every comments
are adopted.

Best regards,
TH

-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply

* Re: [PATCH 1/8] dt-bindings: phy: axiado,ax3000-emmc-phy: add Axiado eMMC PHY document
From: Tzu-Hao Wei @ 2026-02-05  3:36 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  Cc: SriNavmani A, Prasad Bolisetty, Vinod Koul, Neil Armstrong,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Harshit Shah,
	Ulf Hansson, Adrian Hunter, Michal Simek, linux-phy, devicetree,
	linux-arm-kernel, linux-kernel, linux-mmc
In-Reply-To: <20251223-adder-of-imminent-recreation-ea90c0@quoll>

On 12/23/2025 10:23 PM, Krzysztof Kozlowski wrote:
> CAUTION: This email originated from outside of the organization. Do not click links or open attachments unless you recognize the sender and know the content is safe.
> 
> 
> On Mon, Dec 22, 2025 at 04:45:00PM +0800, Tzu-Hao Wei wrote:
>> From: SriNavmani A <srinavmani@axiado.com>
>>
>> Add device tree bindings for the Axiado AX3000 eMMC PHY. The bindings
>> define the required properties for configuring the external Arasan
> 
> Ah, and also:
> 
> A nit, subject: drop second/last, redundant "document". The
> "dt-bindings" prefix is already stating that these are documents.
> See also:
> https://elixir.bootlin.com/linux/v6.17-rc3/source/Documentation/devicetree/bindings/submitting-patches.rst#L18
> 
> Best regards,
> Krzysztof
> 
Revised the subject in the new patch

Best regards,
TH

-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox