* [PATCH RESEND net-next 6/8] phy: qcom-sgmii-eth: remove qcom_dwmac_sgmii_phy_interface()
From: Russell King (Oracle) @ 2026-02-26 8:41 UTC (permalink / raw)
To: Andrew Lunn
Cc: Alexandre Torgue, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, linux-arm-kernel, linux-arm-msm, linux-phy,
linux-stm32, Mohd Ayaan Anwar, Neil Armstrong, netdev,
Paolo Abeni, Vinod Koul
In-Reply-To: <aaAHD_wWv2xCKntW@shell.armlinux.org.uk>
Now that qcom_dwmac_sgmii_phy_interface() only serves to validate the
passed interface mode, combine it with qcom_dwmac_sgmii_phy_validate(),
and use qcom_dwmac_sgmii_phy_validate() to validate the mode in
qcom_dwmac_sgmii_phy_set_mode().
Tested-by: Mohd Ayaan Anwar <mohd.anwar@oss.qualcomm.com>
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
---
drivers/phy/qualcomm/phy-qcom-sgmii-eth.c | 27 +++++++++--------------
1 file changed, 10 insertions(+), 17 deletions(-)
diff --git a/drivers/phy/qualcomm/phy-qcom-sgmii-eth.c b/drivers/phy/qualcomm/phy-qcom-sgmii-eth.c
index 58ff15601206..6332ff291fdf 100644
--- a/drivers/phy/qualcomm/phy-qcom-sgmii-eth.c
+++ b/drivers/phy/qualcomm/phy-qcom-sgmii-eth.c
@@ -290,7 +290,9 @@ static int qcom_dwmac_sgmii_phy_power_off(struct phy *phy)
return 0;
}
-static int qcom_dwmac_sgmii_phy_interface(enum phy_mode mode, int submode)
+static int qcom_dwmac_sgmii_phy_validate(struct phy *phy, enum phy_mode mode,
+ int submode,
+ union phy_configure_opts *opts)
{
if (mode != PHY_MODE_ETHERNET)
return -EINVAL;
@@ -298,7 +300,7 @@ static int qcom_dwmac_sgmii_phy_interface(enum phy_mode mode, int submode)
if (submode == PHY_INTERFACE_MODE_SGMII ||
submode == PHY_INTERFACE_MODE_1000BASEX ||
submode == PHY_INTERFACE_MODE_2500BASEX)
- return submode;
+ return 0;
return -EINVAL;
}
@@ -307,27 +309,18 @@ static int qcom_dwmac_sgmii_phy_set_mode(struct phy *phy, enum phy_mode mode,
int submode)
{
struct qcom_dwmac_sgmii_phy_data *data = phy_get_drvdata(phy);
- int interface;
+ int ret;
- interface = qcom_dwmac_sgmii_phy_interface(mode, submode);
- if (interface < 0)
- return interface;
+ ret = qcom_dwmac_sgmii_phy_validate(phy, mode, submode, NULL);
+ if (ret)
+ return ret;
- if (interface != data->interface)
- data->interface = interface;
+ if (submode != data->interface)
+ data->interface = submode;
return qcom_dwmac_sgmii_phy_calibrate(phy);
}
-static int qcom_dwmac_sgmii_phy_validate(struct phy *phy, enum phy_mode mode,
- int submode,
- union phy_configure_opts *opts)
-{
- int ret = qcom_dwmac_sgmii_phy_interface(mode, submode);
-
- return ret < 0 ? ret : 0;
-}
-
static const struct phy_ops qcom_dwmac_sgmii_phy_ops = {
.power_on = qcom_dwmac_sgmii_phy_power_on,
.power_off = qcom_dwmac_sgmii_phy_power_off,
--
2.47.3
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* [PATCH RESEND net-next 7/8] phy: qcom-sgmii-eth: relax order of .power_on() vs .set_mode*()
From: Russell King (Oracle) @ 2026-02-26 8:41 UTC (permalink / raw)
To: Andrew Lunn
Cc: Alexandre Torgue, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, linux-arm-kernel, linux-arm-msm, linux-phy,
linux-stm32, Mohd Ayaan Anwar, Neil Armstrong, netdev,
Paolo Abeni, Vinod Koul
In-Reply-To: <aaAHD_wWv2xCKntW@shell.armlinux.org.uk>
Allow any order of the .power_on() and .set_mode*() methods as per the
recent discussion. This means phy_power_on() with this SerDes will now
restore the previous setup without requiring a subsequent
phy_set_mode*() call.
Tested-by: Mohd Ayaan Anwar <mohd.anwar@oss.qualcomm.com>
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
---
drivers/phy/qualcomm/phy-qcom-sgmii-eth.c | 14 +++++++++++++-
1 file changed, 13 insertions(+), 1 deletion(-)
diff --git a/drivers/phy/qualcomm/phy-qcom-sgmii-eth.c b/drivers/phy/qualcomm/phy-qcom-sgmii-eth.c
index 6332ff291fdf..f48faa2929a6 100644
--- a/drivers/phy/qualcomm/phy-qcom-sgmii-eth.c
+++ b/drivers/phy/qualcomm/phy-qcom-sgmii-eth.c
@@ -271,8 +271,17 @@ static int qcom_dwmac_sgmii_phy_calibrate(struct phy *phy)
static int qcom_dwmac_sgmii_phy_power_on(struct phy *phy)
{
struct qcom_dwmac_sgmii_phy_data *data = phy_get_drvdata(phy);
+ int ret;
+
+ ret = clk_prepare_enable(data->refclk);
+ if (ret < 0)
+ return ret;
- return clk_prepare_enable(data->refclk);
+ ret = qcom_dwmac_sgmii_phy_calibrate(phy);
+ if (ret < 0)
+ clk_disable_unprepare(data->refclk);
+
+ return ret;
}
static int qcom_dwmac_sgmii_phy_power_off(struct phy *phy)
@@ -318,6 +327,9 @@ static int qcom_dwmac_sgmii_phy_set_mode(struct phy *phy, enum phy_mode mode,
if (submode != data->interface)
data->interface = submode;
+ if (phy->power_count == 0)
+ return 0;
+
return qcom_dwmac_sgmii_phy_calibrate(phy);
}
--
2.47.3
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* [PATCH RESEND net-next 8/8] net: stmmac: qcom-ethqos: remove phy_set_mode_ext() after phy_power_on()
From: Russell King (Oracle) @ 2026-02-26 8:42 UTC (permalink / raw)
To: Andrew Lunn
Cc: Alexandre Torgue, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, linux-arm-kernel, linux-arm-msm, linux-phy,
linux-stm32, Mohd Ayaan Anwar, Neil Armstrong, netdev,
Paolo Abeni, Vinod Koul
In-Reply-To: <aaAHD_wWv2xCKntW@shell.armlinux.org.uk>
The call to phy_set_mode_ext() after phy_power_on() was a work-around
for the qcom-sgmii-eth SerDes driver that only re-enabled its clocks on
phy_power_on() but did not configure the PHY. Now that the SerDes driver
fully configures the SerDes at phy_power_on(), there is no need to call
phy_set_mode_ext() immediately afterwards.
This also means we no longer need to record the previous operating mode
of the driver - this is up to the SerDes driver. In any case, the only
thing that we care about is the SerDes provides the necessary clocks to
the stmmac core to allow it to reset at this point. The actual mode is
irrelevant at this point as the correct mode will be configured in
ethqos_mac_finish_serdes() just before the network device is brought
online.
Reviewed-by: Mohd Ayaan Anwar <mohd.anwar@oss.qualcomm.com>
Tested-by: Mohd Ayaan Anwar <mohd.anwar@oss.qualcomm.com>
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
---
.../stmicro/stmmac/dwmac-qcom-ethqos.c | 18 ++----------------
1 file changed, 2 insertions(+), 16 deletions(-)
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c
index 8913f6f02b9e..cb1c074c2053 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c
@@ -105,7 +105,6 @@ struct qcom_ethqos {
struct clk *link_clk;
struct phy *serdes_phy;
- phy_interface_t serdes_mode;
phy_interface_t phy_mode;
const struct ethqos_emac_por *rgmii_por;
@@ -648,17 +647,8 @@ static int qcom_ethqos_serdes_powerup(struct net_device *ndev, void *priv)
return ret;
ret = phy_power_on(ethqos->serdes_phy);
- if (ret) {
- phy_exit(ethqos->serdes_phy);
- return ret;
- }
-
- ret = phy_set_mode_ext(ethqos->serdes_phy, PHY_MODE_ETHERNET,
- ethqos->serdes_mode);
- if (ret) {
- phy_power_off(ethqos->serdes_phy);
+ if (ret)
phy_exit(ethqos->serdes_phy);
- }
return ret;
}
@@ -681,12 +671,9 @@ static int ethqos_mac_finish_serdes(struct net_device *ndev, void *priv,
qcom_ethqos_set_sgmii_loopback(ethqos, false);
if (interface == PHY_INTERFACE_MODE_SGMII ||
- interface == PHY_INTERFACE_MODE_2500BASEX) {
+ interface == PHY_INTERFACE_MODE_2500BASEX)
ret = phy_set_mode_ext(ethqos->serdes_phy, PHY_MODE_ETHERNET,
interface);
- if (ret == 0)
- ethqos->serdes_mode = interface;
- }
return ret;
}
@@ -839,7 +826,6 @@ static int qcom_ethqos_probe(struct platform_device *pdev)
plat_dat->host_dma_width = data->dma_addr_width;
if (ethqos->serdes_phy) {
- ethqos->serdes_mode = PHY_INTERFACE_MODE_SGMII;
plat_dat->serdes_powerup = qcom_ethqos_serdes_powerup;
plat_dat->serdes_powerdown = qcom_ethqos_serdes_powerdown;
}
--
2.47.3
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* Re: [PATCH v2 1/2] dt-bindings: phy: qcom: Add CSI2 C-PHY/DPHY schema
From: Bryan O'Donoghue @ 2026-02-26 9:24 UTC (permalink / raw)
To: Krzysztof Kozlowski, Bryan O'Donoghue
Cc: Vinod Koul, Kishon Vijay Abraham I, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Neil Armstrong,
Vladimir Zapolskiy, linux-arm-msm, linux-phy, linux-media,
devicetree, linux-kernel
In-Reply-To: <20260226-carmine-cockle-of-prosperity-b6baf2@quoll>
On 26/02/2026 07:37, Krzysztof Kozlowski wrote:
> On Wed, Feb 25, 2026 at 02:59:12PM +0000, Bryan O'Donoghue wrote:
>> Add a base schema initially compatible with x1e80100 to describe MIPI CSI2
>> PHY devices.
>>
>> The hardware can support both C-PHY and D-PHY modes. The CSIPHY devices
>> have their own pinouts on the SoC as well as their own individual voltage
>> rails.
>>
>> The need to model voltage rails on a per-PHY basis leads us to define
>> CSIPHY devices as individual nodes.
>>
>> Two nice outcomes in terms of schema and DT arise from this change.
>>
>> 1. The ability to define on a per-PHY basis voltage rails.
>> 2. The ability to require those voltage.
>>
>> We have had a complete bodge upstream for this where a single set of
>> voltage rail for all CSIPHYs has been buried inside of CAMSS.
>>
>> Much like the I2C bus which is dedicated to Camera sensors - the CCI bus in
>> CAMSS parlance, the CSIPHY devices should be individually modelled.
>>
>> Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org>
>
> The entire point of separate CSI PHY was to change new devices. That's
> why I was postponing TWO new bindings. There is little point in change
> existing bindings, it's just a lot of work with little benefit.
Since the x1e dtsi isn't upstream yet though.
> And the beauty is that you did not even had to do the work yourself,
> because the contributors of new device would need to come with CSI PHY
> split.
I mean I'm happy to do that work myself, idle hands make the Devil's work.
?
---
bod
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH v10 8/9] mux: add prompt and help text to CONFIG_MULTIPLEXER making it visible
From: Josua Mayer @ 2026-02-26 9:49 UTC (permalink / raw)
To: Ulf Hansson, Peter Rosin
Cc: Marc Kleine-Budde, Vincent Mailhol, Vinod Koul, Neil Armstrong,
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, Yoshihiro Shimoda,
Yazan Shhady, Jon Nettleton, Vladimir Oltean, 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
In-Reply-To: <CAPDyKFoDZr33tQXofsZzPrJbZM=3VhpeeOk1GqvRA1UK=LO5Ww@mail.gmail.com>
Am 25.02.26 um 13:24 schrieb Ulf Hansson:
> On Wed, 25 Feb 2026 at 12:35, Josua Mayer <josua@solid-run.com> wrote:
>> The multiplexer subsystem was initially designed only for use by drivers
>> that require muxes, and did in particular not consider optional muxes or
>> to compile as a module.
>>
>> Over time several drivers have added a "select MULTIPLEXER" dependency,
>> some of which require a mux and some consider it optional. v7.0-rc1
>> shows 15 such occurrences in Kconfig files, in a variety of subsystems.
>>
>> Further some drivers such as gpio-mux are useful on their own (e.g.
>> through device-tree idle-state property), but can not currently be
>> selected through menuconfig unless another driver selecting MULTIPLEXER
>> symbol was enabled first.
>>
>> The natural step forward to allow enabling mux core and drivers would be
>> adding prompt and help text to the existing symbol.
>>
>> This violates the general kbuild advice to avoid selecting visible
>> symbols.
>>
>> Alternatively addition of a wrapper symbol MUX_CORE was considered,
>> which in turn would "select MULTIPLEXER". This however creates new
>> issues and confusion as MULTIPLEXER and MUX_CORE need to share the same
>> state, i.e. MUX_CORE in menuconfig must not be set to m while
>> MULTIPLEXER was selected builtin. Further confusion occurs with Kconfig
>> "depends on" relationships that could reference either MUX_CORE or
>> MULTIPLEXER.
>>
>> It is common across the tree for subsystem symbols to be both visible
>> and selected, e.g. I2C & SPI. In the same spirit multiplexer needs to
>> ignore this particular kbuild rule.
>>
>> Add prompt and help text to the existing MULTIPLEXER symbol, making it
>> visible in (menu)config without breaking existing "select MULTIPLEXER"
>> occurrences in the tree.
>>
>> Select it by default when COMPILE_TEST is set for better coverage.
>>
>> Signed-off-by: Josua Mayer <josua@solid-run.com>
>> ---
>> drivers/mux/Kconfig | 9 ++++++++-
>> 1 file changed, 8 insertions(+), 1 deletion(-)
>>
>> diff --git a/drivers/mux/Kconfig b/drivers/mux/Kconfig
>> index c68132e38138..4f7c6bb86fc6 100644
>> --- a/drivers/mux/Kconfig
>> +++ b/drivers/mux/Kconfig
>> @@ -4,7 +4,14 @@
>> #
>>
>> config MULTIPLEXER
>> - tristate
>> + tristate "Generic Multiplexer Support"
>> + default m if COMPILE_TEST
> Allowing the core being a module makes things/code a bit unnecessarily
> complicated, I think.
>
> Similar to other subsystems (like regulators/clk/etc), the core is a
> bool and the menu below it for its provider drivers depends on it to
> be configurable, allowing them to be tristate.
This seems reasonable.
Before revising this patch I'd appreciate input from Peter Rosin,
who had strong opinions on v9.
>> + help
>> + This framework is designed to abstract multiplexer handling for
>> + devices via various GPIO-, MMIO/Regmap or specific multiplexer
>> + controller chips.
>> +
>> + If unsure, say no.
>>
> continuing from the above comment, this would instead be:
>
> if MULTIPLEXER
>
> menu "Multiplexer drivers"
>
>
>> menu "Multiplexer drivers"
>> depends on MULTIPLEXER
>>
>> --
>> 2.43.0
>>
> 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 v7 18/23] scsi: ufs: mediatek: Don't acquire dvfsrc-vcore twice
From: AngeloGioacchino Del Regno @ 2026-02-26 10:33 UTC (permalink / raw)
To: Peter Wang (王信友), chu.stanley@gmail.com,
robh@kernel.org, Chunfeng Yun (云春峰),
kishon@kernel.org, James.Bottomley@HansenPartnership.com,
bvanassche@acm.org, Chaotian Jing (井朝天),
conor+dt@kernel.org, lgirdwood@gmail.com,
nicolas.frattaroli@collabora.com, vkoul@kernel.org,
krzk+dt@kernel.org, p.zabel@pengutronix.de,
alim.akhtar@samsung.com, neil.armstrong@linaro.org,
matthias.bgg@gmail.com, avri.altman@wdc.com, broonie@kernel.org,
martin.petersen@oracle.com
Cc: linux-scsi@vger.kernel.org, devicetree@vger.kernel.org,
linux-kernel@vger.kernel.org,
linux-arm-kernel@lists.infradead.org,
linux-phy@lists.infradead.org, linux-mediatek@lists.infradead.org,
Louis-Alexis Eyraud, kernel@collabora.com
In-Reply-To: <c7214effa728b74e753abc0dee8d655e036ca7fb.camel@mediatek.com>
Il 26/02/26 04:45, Peter Wang (王信友) ha scritto:
> On Wed, 2026-02-25 at 13:37 +0100, AngeloGioacchino Del Regno wrote:
>> We need to check both because UFS_MTK_CAP_BOOST_CRYPT_ENGINE depends
>> on:
>> 1. reg_vcore
>> 2. clocks (crypt_mux, crypt_lp, crypt_perf).
>>
>> Failing to check for both ufs_mtk_is_boost_crypt_enabled() and
>> reg_vcore here
>> will introduce a bug that may result in storage corruption.
>>
>> So yes, Nicolas is checking both because it is *required* to check
>> both.
>>
>> Regards,
>> Angelo
>
> Hi AngeloGioacchino,
>
> To clarify, BCE stands for UFS_MTK_CAP_BOOST_CRYPT_ENGINE.
>
> BCE reg_vcore Action
> true true If check is false, continue
> true false This case cannot happen (X)
> false true If check is true, return
> false false If check is true, return
> Therefore, we only need to check whether BCE is
> true (to continue) or false (to return).
>
Thanks for the information.
Now I agree. There's no need to check for that twice, as the cap is set
only when all clocks and when reg_vcore is not NULL - I missed the first
lines of the ufs_mtk_init_boost_crypt_function() from this patch, where
Nicolas is adding a check for that at the very beginning of this function.
This means that the UFS_MTK_CAP_BOOST_CRYPT_ENGINE flag is being set only
when all of the prerequisites (reg_vcore and clocks) are satisfied.
I agree with you, Peter, there's no need to check for both the vreg and
caps, as the cap can only be set if the vreg+clocks are present.
Nicolas, can you please fix that and send a v8 ASAP?
Thanks,
Angelo
> Thanks
> Peter
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH v7 20/23] scsi: ufs: mediatek: Back up idle timer in per-instance struct
From: AngeloGioacchino Del Regno @ 2026-02-26 10:36 UTC (permalink / raw)
To: Peter Wang (王信友), chu.stanley@gmail.com,
robh@kernel.org, Chunfeng Yun (云春峰),
kishon@kernel.org, James.Bottomley@HansenPartnership.com,
bvanassche@acm.org, Chaotian Jing (井朝天),
conor+dt@kernel.org, lgirdwood@gmail.com,
nicolas.frattaroli@collabora.com, vkoul@kernel.org,
krzk+dt@kernel.org, p.zabel@pengutronix.de,
alim.akhtar@samsung.com, neil.armstrong@linaro.org,
matthias.bgg@gmail.com, avri.altman@wdc.com, broonie@kernel.org,
martin.petersen@oracle.com
Cc: linux-scsi@vger.kernel.org, devicetree@vger.kernel.org,
linux-kernel@vger.kernel.org,
linux-arm-kernel@lists.infradead.org,
linux-phy@lists.infradead.org, linux-mediatek@lists.infradead.org,
Louis-Alexis Eyraud, kernel@collabora.com
In-Reply-To: <6297edc9c2d6d1a323f188ef411205701a629d88.camel@mediatek.com>
Il 26/02/26 04:46, Peter Wang (王信友) ha scritto:
> On Wed, 2026-02-25 at 13:40 +0100, AngeloGioacchino Del Regno wrote:
>> In my opinion "ahit" is way less readable than
>> "hibernate_idle_timer".
>>
>> The hibernate_idle_timer member here stores the AUTO HIBERNATE IDLE
>> TIMER, and
>> there is no other possible hibernation state in this driver.
>>
>> Not sure why this could ever be confusing in terms of its intended
>> use: its
>> intended use is to store the (auto) hibern8 idle timer, and the
>> member is called
>> hibernate_idle_timer.
>>
>> In my eyes, that matches 1:1 with its usage. Loud and clear.
>>
>> Regards,
>> Angelo
>>
>>
>
> Hi AngeloGioacchino,
>
> If you want to refer to the AUTO HIBERNATE IDLE TIMER,
> then you cannot omit "auto" because the UFS driver also
> has a manual hibernate method.
> However, "AUTO HIBERNATE IDLE TIMER" is too long,
> and we often use "ahit" instead (which is also used in
> the UFSHCI spec). For example:
> https://elixir.bootlin.com/linux/v6.19.3/source/include/ufs/ufshcd.h#L978
>
> So, if you want to distinguish it from hba->ahit, I suggest
> using backup_ahit or saved_ahit instead.
>
Okay, does "saved_auto_hibern8_idle_tmr" sound good for you instead?
Regards,
Angelo
> Thanks
> Peter
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH v7 16/23] scsi: ufs: mediatek: Clean up logging prints
From: AngeloGioacchino Del Regno @ 2026-02-26 10:45 UTC (permalink / raw)
To: Peter Wang (王信友), chu.stanley@gmail.com,
robh@kernel.org, Chunfeng Yun (云春峰),
kishon@kernel.org, James.Bottomley@hansenpartnership.com,
bvanassche@acm.org, Chaotian Jing (井朝天),
conor+dt@kernel.org, lgirdwood@gmail.com,
nicolas.frattaroli@collabora.com, vkoul@kernel.org,
krzk+dt@kernel.org, p.zabel@pengutronix.de,
alim.akhtar@samsung.com, neil.armstrong@linaro.org,
matthias.bgg@gmail.com, avri.altman@wdc.com, broonie@kernel.org,
martin.petersen@oracle.com
Cc: linux-scsi@vger.kernel.org, devicetree@vger.kernel.org,
linux-kernel@vger.kernel.org,
linux-arm-kernel@lists.infradead.org,
linux-phy@lists.infradead.org, linux-mediatek@lists.infradead.org,
Louis-Alexis Eyraud, kernel@collabora.com
In-Reply-To: <259b24885e5e721ae562d27dd761b02e6a68c971.camel@mediatek.com>
Il 26/02/26 08:00, Peter Wang (王信友) ha scritto:
> On Wed, 2026-02-25 at 14:18 +0100, AngeloGioacchino Del Regno wrote:
>>> Depends on your view of what's useful information for the user.
>>>
>>> I can change both of these back to _info if I have to send out a
>>> next
>>> revision, just to get this through though.
>>>
>>
>> Definitely don't change that back to dev_info() as this is debugging
>> information
>> that spams the kernel log for no reason.
>>
>> This has to be dev_dbg().
>>
>> Regards,
>> Angelo
>>
>>>
>
> Hi AngeloGioacchino, Nicolas,
>
> At least, "device reset done" is important information that
> users would care about, and it should not spam the kernel log.
> You wouldn't expect device resets to occur repeatedly, would you?
>
Sorry Peter, but I'd argue that the users don't care about how much and when
their UFS device resets. Users just want to use a device, without caring
about any implementation detail.
The spirit is: "radio silence as long as everything works good".
Power users might want to check the kernel log in a problematic scenario to
seek for a message that says that "something went horribly wrong", but other
than developers, nobody cares about when UFS resets.
From a developer standpoint, I do agree with you in that we do *not* want to
see device resets occurring repeatedly, but we're talking about a user here.
See it like this... imagine if all of the device drivers in the Linux kernel
would say "device reset done": how many devices are present in one SoC (of
course, ignoring subdevices on a board)?
Of all those many devices, if all of them would print a message saying that
their reset is done (and operation is ok), the kernel log would get quite a
bit clogged, you'd need to have a bigger RAM carveout just for .. well, the
kernel log itself, and then you'd have to grep the log, hoping to find the
one single line that helps you finding an issue that you're having.
This is the reason why keeping any message that is not exactly a *single*
indication of an error (so, an actual issue) as a dev_dbg() is a sensible
thing to do (and of course, with dynamic debug in the kernel, you can always
activate that on-the-fly without recompiling to verify functionality should
you have any immediate doubt).
So while I agree about your reasons, I very strongly disagree about having
this message as a dev_info(), nor anything else that is not dev_dbg() really.
Regards,
Angelo
> Thanks
> Peter
>
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH v10 3/9] mux: Add helper functions for getting optional and selected mux-state
From: Josua Mayer @ 2026-02-26 10:46 UTC (permalink / raw)
To: 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, Wolfram Sang,
Yoshihiro Shimoda
Cc: Yazan Shhady, Jon Nettleton, Vladimir Oltean, 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
In-Reply-To: <20260225-rz-sdio-mux-v10-3-1ee44f2ea112@solid-run.com>
Am 25.02.26 um 12:34 schrieb Josua Mayer:
> In-tree phy-can-transceiver and phy_rcar_gen3_usb2 have already
> implemented local versions 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.
>
> Acked-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
> Signed-off-by: Josua Mayer <josua@solid-run.com>
> ---
> drivers/mux/core.c | 206 ++++++++++++++++++++++++++++++++++++-------
> include/linux/mux/consumer.h | 108 ++++++++++++++++++++++-
> 2 files changed, 279 insertions(+), 35 deletions(-)
>
> diff --git a/drivers/mux/core.c b/drivers/mux/core.c
> index f09ee8782e3d..6033da0a9e17 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,29 @@ 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: Pointer to the mux-state on success, an ERR_PTR with a negative errno on error,
> + * or NULL if mux doesn't exist.
> + */
> +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 +694,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 +710,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;
>
> @@ -685,12 +727,16 @@ static struct mux_state *mux_state_get(struct device *dev, const char *mux_name)
> if (!mstate)
> return ERR_PTR(-ENOMEM);
>
> - mstate->mux = mux_get(dev, mux_name, &mstate->state);
> + mstate->mux = mux_get(dev, mux_name, &mstate->state, optional);
> if (IS_ERR(mstate->mux)) {
> - int err = PTR_ERR(mstate->mux);
> -
> kfree(mstate);
> - return ERR_PTR(err);
> + return ERR_CAST(mstate->mux);
kernel robot found a use-after-free here which I will resolve for v11.
> + } else if (optional && !mstate->mux) {
> + kfree(mstate);
> + return NULL;
> + } else if (!mstate->mux) {
> + kfree(mstate);
> + return ERR_PTR(-ENOENT);
> }
>
> return mstate;
> @@ -710,9 +756,66 @@ static void mux_state_put(struct mux_state *mstate)
>
> static void devm_mux_state_release(struct device *dev, void *res)
> {
> - struct mux_state *mstate = *(struct mux_state **)res;
> + struct devm_mux_state_state *devm_state = res;
>
> + if (devm_state->exit)
> + devm_state->exit(devm_state->mstate);
> +
> + mux_state_put(devm_state->mstate);
> +}
> +
> +/**
> + * __devm_mux_state_get() - Get the optional mux-state for a device,
> + * with resource management.
> + * @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.
> + * @init: Optional function pointer for mux-state object initialisation.
> + * @exit: Optional function pointer for mux-state object cleanup on release.
> + *
> + * 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 *__devm_mux_state_get(struct device *dev, const char *mux_name,
> + bool optional,
> + int (*init)(struct mux_state *mstate),
> + int (*exit)(struct mux_state *mstate))
> +{
> + struct devm_mux_state_state *devm_state;
> + struct mux_state *mstate;
> + int ret;
> +
> + mstate = mux_state_get(dev, mux_name, optional);
> + if (IS_ERR(mstate))
> + return ERR_CAST(mstate);
> + else if (optional && !mstate)
> + return NULL;
> + else if (!mstate)
> + return ERR_PTR(-ENOENT);
> +
> + devm_state = devres_alloc(devm_mux_state_release, sizeof(*devm_state), GFP_KERNEL);
> + if (!devm_state) {
> + ret = -ENOMEM;
> + goto err_devres_alloc;
> + }
> +
> + if (init) {
> + ret = init(mstate);
> + if (ret)
> + goto err_mux_state_init;
> + }
> +
> + devm_state->mstate = mstate;
> + devm_state->exit = exit;
> + devres_add(dev, devm_state);
> +
> + return mstate;
> +
> +err_mux_state_init:
> + devres_free(devm_state);
> +err_devres_alloc:
> mux_state_put(mstate);
> + return ERR_PTR(ret);
> }
>
> /**
> @@ -722,28 +825,69 @@ static void devm_mux_state_release(struct device *dev, void *res)
> * @mux_name: The name identifying the mux-control.
> *
> * Return: Pointer to the mux-state, or an ERR_PTR with a negative errno.
> + *
> + * The mux-state will automatically be freed on release.
> */
> -struct mux_state *devm_mux_state_get(struct device *dev,
> - const char *mux_name)
> +struct mux_state *devm_mux_state_get(struct device *dev, const char *mux_name)
> {
> - struct mux_state **ptr, *mstate;
> -
> - ptr = devres_alloc(devm_mux_state_release, sizeof(*ptr), GFP_KERNEL);
> - if (!ptr)
> - return ERR_PTR(-ENOMEM);
> + return __devm_mux_state_get(dev, mux_name, false, NULL, NULL);
> +}
> +EXPORT_SYMBOL_GPL(devm_mux_state_get);
>
> - mstate = mux_state_get(dev, mux_name);
> - if (IS_ERR(mstate)) {
> - devres_free(ptr);
> - return mstate;
> - }
> +/**
> + * devm_mux_state_get_optional() - Get the optional mux-state for a device,
> + * with resource management.
> + * @dev: The device that needs a mux-state.
> + * @mux_name: The name identifying the mux-state.
> + *
> + * Return: Pointer to the mux-state on success, an ERR_PTR with a negative errno on error,
> + * or NULL if mux doesn't exist.
> + *
> + * The mux-state will automatically be freed on release.
> + */
> +struct mux_state *devm_mux_state_get_optional(struct device *dev, const char *mux_name)
> +{
> + return __devm_mux_state_get(dev, mux_name, true, NULL, NULL);
> +}
> +EXPORT_SYMBOL_GPL(devm_mux_state_get_optional);
>
> - *ptr = mstate;
> - devres_add(dev, ptr);
> +/**
> + * devm_mux_state_get_selected() - Get the mux-state for a device, with
> + * resource management.
> + * @dev: The device that needs a mux-state.
> + * @mux_name: The name identifying the mux-state.
> + *
> + * Return: Pointer to the mux-state, or an ERR_PTR with a negative errno.
> + *
> + * The returned mux-state (if valid) is already selected.
> + *
> + * The mux-state will automatically be deselected and freed on release.
> + */
> +struct mux_state *devm_mux_state_get_selected(struct device *dev, const char *mux_name)
> +{
> + return __devm_mux_state_get(dev, mux_name, false, mux_state_select, mux_state_deselect);
> +}
> +EXPORT_SYMBOL_GPL(devm_mux_state_get_selected);
>
> - return mstate;
> +/**
> + * devm_mux_state_get_optional_selected() - Get the optional mux-state for
> + * a device, with resource management.
> + * @dev: The device that needs a mux-state.
> + * @mux_name: The name identifying the mux-state.
> + *
> + * Return: Pointer to the mux-state on success, an ERR_PTR with a negative errno on error,
> + * or NULL if mux doesn't exist.
> + *
> + * The returned mux-state (if valid) is already selected.
> + *
> + * The mux-state will automatically be deselected and freed on release.
> + */
> +struct mux_state *devm_mux_state_get_optional_selected(struct device *dev,
> + const char *mux_name)
> +{
> + return __devm_mux_state_get(dev, mux_name, true, mux_state_select, mux_state_deselect);
> }
> -EXPORT_SYMBOL_GPL(devm_mux_state_get);
> +EXPORT_SYMBOL_GPL(devm_mux_state_get_optional_selected);
>
> /*
> * Using subsys_initcall instead of module_init here to try to ensure - for
> diff --git a/include/linux/mux/consumer.h b/include/linux/mux/consumer.h
> index 2e25c838f831..a961861a503b 100644
> --- a/include/linux/mux/consumer.h
> +++ b/include/linux/mux/consumer.h
> @@ -16,6 +16,8 @@ struct device;
> struct mux_control;
> struct mux_state;
>
> +#if IS_ENABLED(CONFIG_MULTIPLEXER)
> +
> unsigned int mux_control_states(struct mux_control *mux);
> int __must_check mux_control_select_delay(struct mux_control *mux,
> unsigned int state,
> @@ -54,11 +56,109 @@ int mux_control_deselect(struct mux_control *mux);
> int mux_state_deselect(struct mux_state *mstate);
>
> struct mux_control *mux_control_get(struct device *dev, const char *mux_name);
> +struct mux_control *mux_control_get_optional(struct device *dev, const char *mux_name);
> void mux_control_put(struct mux_control *mux);
>
> -struct mux_control *devm_mux_control_get(struct device *dev,
> - const char *mux_name);
> -struct mux_state *devm_mux_state_get(struct device *dev,
> - const char *mux_name);
> +struct mux_control *devm_mux_control_get(struct device *dev, const char *mux_name);
> +struct mux_state *devm_mux_state_get(struct device *dev, const char *mux_name);
> +struct mux_state *devm_mux_state_get_optional(struct device *dev, const char *mux_name);
> +struct mux_state *devm_mux_state_get_selected(struct device *dev, const char *mux_name);
> +struct mux_state *devm_mux_state_get_optional_selected(struct device *dev, const char *mux_name);
> +
> +#else
> +
> +static inline unsigned int mux_control_states(struct mux_control *mux)
> +{
> + return 0;
> +}
> +static inline int __must_check mux_control_select_delay(struct mux_control *mux,
> + unsigned int state, unsigned int delay_us)
> +{
> + return -EOPNOTSUPP;
> +}
> +static inline int __must_check mux_state_select_delay(struct mux_state *mstate,
> + unsigned int delay_us)
> +{
> + return -EOPNOTSUPP;
> +}
> +static inline int __must_check mux_control_try_select_delay(struct mux_control *mux,
> + unsigned int state,
> + unsigned int delay_us)
> +{
> + return -EOPNOTSUPP;
> +}
> +static inline int __must_check mux_state_try_select_delay(struct mux_state *mstate,
> + unsigned int delay_us)
> +{
> + return -EOPNOTSUPP;
> +}
> +
> +static inline int __must_check mux_control_select(struct mux_control *mux,
> + unsigned int state)
> +{
> + return -EOPNOTSUPP;
> +}
> +
> +static inline int __must_check mux_state_select(struct mux_state *mstate)
> +{
> + return -EOPNOTSUPP;
> +}
> +
> +static inline int __must_check mux_control_try_select(struct mux_control *mux,
> + unsigned int state)
> +{
> + return -EOPNOTSUPP;
> +}
> +
> +static inline int __must_check mux_state_try_select(struct mux_state *mstate)
> +{
> + return -EOPNOTSUPP;
> +}
> +
> +static inline int mux_control_deselect(struct mux_control *mux)
> +{
> + return -EOPNOTSUPP;
> +}
> +static inline int mux_state_deselect(struct mux_state *mstate)
> +{
> + return -EOPNOTSUPP;
> +}
> +
> +static inline struct mux_control *mux_control_get(struct device *dev, const char *mux_name)
> +{
> + return ERR_PTR(-EOPNOTSUPP);
> +}
> +static inline struct mux_control *mux_control_get_optional(struct device *dev,
> + const char *mux_name)
> +{
> + return NULL;
> +}
> +static inline void mux_control_put(struct mux_control *mux) {}
> +
> +static inline struct mux_control *devm_mux_control_get(struct device *dev, const char *mux_name)
> +{
> + return ERR_PTR(-EOPNOTSUPP);
> +}
> +static inline struct mux_state *devm_mux_state_get(struct device *dev, const char *mux_name)
> +{
> + return ERR_PTR(-EOPNOTSUPP);
> +}
> +static inline struct mux_state *devm_mux_state_get_optional(struct device *dev,
> + const char *mux_name)
> +{
> + return NULL;
> +}
> +static inline struct mux_state *devm_mux_state_get_selected(struct device *dev,
> + const char *mux_name)
> +{
> + return ERR_PTR(-EOPNOTSUPP);
> +}
> +static inline struct mux_state *devm_mux_state_get_optional_selected(struct device *dev,
> + const char *mux_name)
> +{
> + return NULL;
> +}
> +
> +#endif /* CONFIG_MULTIPLEXER */
>
> #endif /* _LINUX_MUX_CONSUMER_H */
>
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* [PATCH v3 0/2] phy: qcom-mipi-csi2: Add a CSI2 MIPI DPHY driver
From: Bryan O'Donoghue @ 2026-02-26 12:34 UTC (permalink / raw)
To: Vinod Koul, Kishon Vijay Abraham I, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Neil Armstrong
Cc: Bryan O'Donoghue, Vladimir Zapolskiy, linux-arm-msm,
linux-phy, linux-media, devicetree, linux-kernel,
Bryan O'Donoghue
v3:
- Resending this to make clear this submission is additive to x1e/Hamoa
The existing bindings and code will continue to work
Bindings are added only, nothing is subtracted from existing ABI.
- Link to v2: https://lore.kernel.org/r/20260225-x1e-csi2-phy-v2-0-7756edb67ea9@linaro.org
v2:
In this updated version
- Added operating-point support
The csiphy clock sets the OPP prior to setting the rate
for csiphy and csiphy_timer - Konrad
- Combo mode
Combo mode in CAMSS yaml has been added. Right now
no code has been changed in the PHY driver to support it as
I don't have hardware to test. In principle though it can
be supported. - Vladimir
- CSIPHY init sequences
I left these as their "magic number formats". With my diminished
status as a non-qcom VPN person - I can no longer see what the bits
map to. Moreover this is the situation any non-VPN community member
will be in when submitting CSIPHY sequences derived from downstream.
I think it is perfectly reasonable to take public CSIPHY init sequences
as magic numbers. If someone with bit-level access wants to enumerate
the bits that's fine but, it shouldn't gate in the interim. - Konrad/bod
- Sensor endpoints
I've stuck to the format used by every other CSIPHY in upstream.
Sensor endpoints hit the CAMSS/CSID endpoint not a endpoint in the PHY.
Given the proposed changes to CAMSS though to support "combo mode" I
think this should achieve the same outcome - multiple sensors on the one
PHY without introducing endpoints into the PHY that no other CSIPHY in
upstream currently has.
- Bitmask of enabled lanes
Work needs to be done in the v4l2 layer to really support this.
I propose making a separate series dedicated to non-linear bit
interpretation after merging this so as to contain the scope of the
series to something more bite (byte haha) sized. - Konrad/bod
- Link to v1: https://lore.kernel.org/r/20250710-x1e-csi2-phy-v1-0-74acbb5b162b@linaro.org
v1:
This short series adds a CSI2 MIPI PHY driver, initially supporting D-PHY
mode. The core logic and init sequences come directly from CAMSS and are
working on at least five separate x1e devices.
The rationale to instantiate CSI2 PHYs as standalone devices instead of as
sub-nodes of CAMSS is as follows.
1. Precedence
CAMSS has a dedicated I2C bus called CCI Camera Control Interface.
We model this controller as its own separate device in devicetree.
This makes sense and CCI/I2C is a well defined bus type already modelled
in Linux.
MIPI CSI2 PHY devices similarly fit into a well defined separate
bus/device structure.
Contrast to another CAMSS component such as VFE, CSID or TPG these
components only interact with other CAMSS inputs/outputs unlike CSIPHY
which interacts with non-SoC components.
2. Hardware pinouts and rails
The CSI2 PHY has its own data/clock lanes out from the SoC and indeed
has its own incoming power-rails.
3. Other devicetree schemas
There are several examples throughout the kernel of CSI PHYs modeled as
standalone devices which one assumes follows the same reasoning as given
above.
I've been working on this on-and-off since the end of April:
Link: https://lore.kernel.org/linux-media/c5cf0155-f839-4db9-b865-d39b56bb1e0a@linaro.org
There is another proposal to have the PHYs be subdevices of CAMSS but, I
believe we should go with a "full fat" PHY to match best practices in
drivers/phy/qualcomm/*.
Using the standard PHY API and the parameter passing that goes with it
allows us to move away from custom interfaces in CAMSS and to conform more
clearly to established PHY paradigms such as the QMP combo PHY.
Looking at existing compat strings I settled on
"qcom,x1e80100-mipi-csi2-combo-phy" deliberately omitting reference to the
fact the PHY is built on a four nano-meter process node, which seems to
match recent submissions to QMP PHY.
My first pass at this driver included support for the old two phase
devices:
Link: https://git.codelinaro.org/bryan.odonoghue/kernel/-/commit/a504c28d109296c93470340cfe7281231f573bcb#b6e59ed7db94c9da22e492bb03fcda6a4300983c
I realised that the device tree schema changes required to support a
comprehensive conversion of all CAMSS to this driver would be an
almost certainly be unacceptable ABI break or at the very least an enormous
amount of work and verification so I instead aimed to support just one new
SoC in the submission.
I've retained the callback indirections give us scope to add in another type of
future PHY including potentially adding in the 2PH later on.
This driver is tested and working on x1e/Hamoa and has been tested as not
breaking sc8280xp/Makena and sm8250/Kona.
Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org>
---
Bryan O'Donoghue (2):
dt-bindings: phy: qcom: Add CSI2 C-PHY/DPHY schema
phy: qcom-mipi-csi2: Add a CSI2 MIPI DPHY driver
.../bindings/phy/qcom,x1e80100-csi2-phy.yaml | 114 ++++++
MAINTAINERS | 11 +
drivers/phy/qualcomm/Kconfig | 13 +
drivers/phy/qualcomm/Makefile | 5 +
drivers/phy/qualcomm/phy-qcom-mipi-csi2-3ph-dphy.c | 384 +++++++++++++++++++++
drivers/phy/qualcomm/phy-qcom-mipi-csi2-core.c | 307 ++++++++++++++++
drivers/phy/qualcomm/phy-qcom-mipi-csi2.h | 102 ++++++
7 files changed, 936 insertions(+)
---
base-commit: c824345288d11e269ce41b36c105715bc2286050
change-id: 20250710-x1e-csi2-phy-f6434b651d3a
Best regards,
--
Bryan O'Donoghue <bryan.odonoghue@linaro.org>
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* [PATCH v3 1/2] dt-bindings: phy: qcom: Add CSI2 C-PHY/DPHY schema
From: Bryan O'Donoghue @ 2026-02-26 12:34 UTC (permalink / raw)
To: Vinod Koul, Kishon Vijay Abraham I, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Neil Armstrong
Cc: Bryan O'Donoghue, Vladimir Zapolskiy, linux-arm-msm,
linux-phy, linux-media, devicetree, linux-kernel,
Bryan O'Donoghue
In-Reply-To: <20260226-x1e-csi2-phy-v3-0-11e608759410@linaro.org>
Add a base schema initially compatible with x1e80100 to describe MIPI CSI2
PHY devices.
The hardware can support both C-PHY and D-PHY modes. The CSIPHY devices
have their own pinouts on the SoC as well as their own individual voltage
rails.
The need to model voltage rails on a per-PHY basis leads us to define
CSIPHY devices as individual nodes.
Two nice outcomes in terms of schema and DT arise from this change.
1. The ability to define on a per-PHY basis voltage rails.
2. The ability to require those voltage.
We have had a complete bodge upstream for this where a single set of
voltage rail for all CSIPHYs has been buried inside of CAMSS.
Much like the I2C bus which is dedicated to Camera sensors - the CCI bus in
CAMSS parlance, the CSIPHY devices should be individually modelled.
Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org>
---
.../bindings/phy/qcom,x1e80100-csi2-phy.yaml | 114 +++++++++++++++++++++
1 file changed, 114 insertions(+)
diff --git a/Documentation/devicetree/bindings/phy/qcom,x1e80100-csi2-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,x1e80100-csi2-phy.yaml
new file mode 100644
index 0000000000000..c937d26ccbda9
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/qcom,x1e80100-csi2-phy.yaml
@@ -0,0 +1,114 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/phy/qcom,x1e80100-csi2-phy.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Qualcomm CSI2 PHY
+
+maintainers:
+ - Bryan O'Donoghue <bod@kernel.org>
+
+description:
+ Qualcomm MIPI CSI2 C-PHY/D-PHY combination PHY. Connects MIPI CSI2 sensors
+ to Qualcomm's Camera CSI Decoder. The PHY supports both C-PHY and D-PHY
+ modes.
+
+properties:
+ compatible:
+ const: qcom,x1e80100-csi2-phy
+
+ reg:
+ maxItems: 1
+
+ "#phy-cells":
+ const: 1
+
+ clocks:
+ maxItems: 4
+
+ clock-names:
+ items:
+ - const: csiphy
+ - const: csiphy_timer
+ - const: camnoc_axi
+ - const: cpas_ahb
+
+ interrupts:
+ maxItems: 1
+
+ operating-points-v2:
+ maxItems: 1
+
+ power-domains:
+ maxItems: 1
+
+ vdda-0p8-supply:
+ description: Phandle to a 0.8V regulator supply to a PHY.
+
+ vdda-1p2-supply:
+ description: Phandle to 1.2V regulator supply to a PHY.
+
+required:
+ - compatible
+ - reg
+ - "#phy-cells"
+ - clocks
+ - clock-names
+ - interrupts
+ - operating-points-v2
+ - power-domains
+ - vdda-0p8-supply
+ - vdda-1p2-supply
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/clock/qcom,x1e80100-camcc.h>
+ #include <dt-bindings/clock/qcom,x1e80100-gcc.h>
+ #include <dt-bindings/phy/phy.h>
+
+ csiphy@ace4000 {
+ compatible = "qcom,x1e80100-csi2-phy";
+ reg = <0x0ace4000 0x2000>;
+ #phy-cells = <1>;
+
+ clocks = <&camcc CAM_CC_CSIPHY0_CLK>,
+ <&camcc CAM_CC_CSI0PHYTIMER_CLK>,
+ <&camcc CAM_CC_CAMNOC_AXI_RT_CLK>,
+ <&camcc CAM_CC_CPAS_AHB_CLK>;
+ clock-names = "csiphy",
+ "csiphy_timer",
+ "camnoc_axi",
+ "cpas_ahb";
+
+ operating-points-v2 = <&csiphy_opp_table>;
+
+ interrupts = <GIC_SPI 477 IRQ_TYPE_EDGE_RISING>;
+
+ power-domains = <&camcc CAM_CC_TITAN_TOP_GDSC>;
+
+ vdda-0p8-supply = <&vreg_l2c_0p8>;
+ vdda-1p2-supply = <&vreg_l1c_1p2>;
+ };
+
+ csiphy_opp_table: opp-table-csiphy {
+ compatible = "operating-points-v2";
+
+ opp-300000000 {
+ opp-hz = /bits/ 64 <300000000>;
+ required-opps = <&rpmhpd_opp_low_svs_d1>;
+ };
+
+ opp-400000000 {
+ opp-hz = /bits/ 64 <400000000>;
+ required-opps = <&rpmhpd_opp_low_svs>;
+ };
+
+ opp-480000000 {
+ opp-hz = /bits/ 64 <480000000>;
+ required-opps = <&rpmhpd_opp_low_svs>;
+ };
+ };
--
2.52.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* [PATCH v3 2/2] phy: qcom-mipi-csi2: Add a CSI2 MIPI DPHY driver
From: Bryan O'Donoghue @ 2026-02-26 12:34 UTC (permalink / raw)
To: Vinod Koul, Kishon Vijay Abraham I, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Neil Armstrong
Cc: Bryan O'Donoghue, Vladimir Zapolskiy, linux-arm-msm,
linux-phy, linux-media, devicetree, linux-kernel,
Bryan O'Donoghue
In-Reply-To: <20260226-x1e-csi2-phy-v3-0-11e608759410@linaro.org>
Add a new MIPI CSI2 driver in DPHY mode initially. The entire set of
existing CAMSS CSI PHY init sequences are imported in order to save time
and effort in later patches.
The following devices are supported in this drop:
"qcom,x1e80100-csi2-phy"
In-line with other PHY drivers the process node is included in the name. At
the moment we follow the assignment of lane positions - the bitmap of
physical input lanes to logical lane numbers as a linear list per the
existing DPHY @lanes data-member.
This is fine for us in upstream at the moment since we also map the lanes
contiguously but, our hardware can support different lane mappings so we
should in the future extend out the DPHY structure to capture the mapping.
The Qualcomm 3PH class of PHYs can do both DPHY and CPHY mode. For now only
DPHY is supported.
In porting some of the logic over from camss-csiphy*.c to here its also
possible to rationalise some of the code.
In particular use of regulator_bulk and clk_bulk as well as dropping the
seemingly useless and unused interrupt handler.
The PHY sequences and a lot of the logic that goes with them are well
proven in CAMSS and mature so the main thing to watch out for here is how
to get the right sequencing of regulators, clocks and register-writes.
Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org>
---
MAINTAINERS | 11 +
drivers/phy/qualcomm/Kconfig | 13 +
drivers/phy/qualcomm/Makefile | 5 +
drivers/phy/qualcomm/phy-qcom-mipi-csi2-3ph-dphy.c | 384 +++++++++++++++++++++
drivers/phy/qualcomm/phy-qcom-mipi-csi2-core.c | 307 ++++++++++++++++
drivers/phy/qualcomm/phy-qcom-mipi-csi2.h | 102 ++++++
6 files changed, 822 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 62ccdc72384d4..fe19722355d94 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -21542,6 +21542,17 @@ S: Maintained
F: Documentation/devicetree/bindings/media/qcom,*-iris.yaml
F: drivers/media/platform/qcom/iris/
+QUALCOMM MIPI CSI2 PHY DRIVER
+M: Bryan O'Donoghue <bod@kernel.org>
+L: linux-phy@lists.infradead.org
+L: linux-media@vger.kernel.org
+L: linux-arm-msm@vger.kernel.org
+S: Supported
+F: Documentation/devicetree/bindings/phy/qcom,*-csi2-phy.yaml
+F: drivers/phy/qualcomm/phy-qcom-mipi-csi2*.c
+F: drivers/phy/qualcomm/phy-qcom-mipi-csi2*.h
+F: include/dt-bindings/phy/phy-qcom-mipi-csi2*
+
QUALCOMM NAND CONTROLLER DRIVER
M: Manivannan Sadhasivam <mani@kernel.org>
L: linux-mtd@lists.infradead.org
diff --git a/drivers/phy/qualcomm/Kconfig b/drivers/phy/qualcomm/Kconfig
index 60a0ead127fa9..ea33025a40fd0 100644
--- a/drivers/phy/qualcomm/Kconfig
+++ b/drivers/phy/qualcomm/Kconfig
@@ -28,6 +28,19 @@ config PHY_QCOM_EDP
Enable this driver to support the Qualcomm eDP PHY found in various
Qualcomm chipsets.
+config PHY_QCOM_MIPI_CSI2
+ tristate "Qualcomm MIPI CSI2 PHY driver"
+ depends on ARCH_QCOM || COMPILE_TEST
+ depends on OF
+ depends on COMMON_CLK
+ select GENERIC_PHY
+ select GENERIC_PHY_MIPI_DPHY
+ help
+ Enable this to support the MIPI CSI2 PHY driver found in various
+ Qualcomm chipsets. This PHY is used to connect MIPI CSI2
+ camera sensors to the CSI Decoder in the Qualcomm Camera Subsystem
+ CAMSS.
+
config PHY_QCOM_IPQ4019_USB
tristate "Qualcomm IPQ4019 USB PHY driver"
depends on OF && (ARCH_QCOM || COMPILE_TEST)
diff --git a/drivers/phy/qualcomm/Makefile b/drivers/phy/qualcomm/Makefile
index b71a6a0bed3f1..382cb594b06b6 100644
--- a/drivers/phy/qualcomm/Makefile
+++ b/drivers/phy/qualcomm/Makefile
@@ -6,6 +6,11 @@ obj-$(CONFIG_PHY_QCOM_IPQ4019_USB) += phy-qcom-ipq4019-usb.o
obj-$(CONFIG_PHY_QCOM_IPQ806X_SATA) += phy-qcom-ipq806x-sata.o
obj-$(CONFIG_PHY_QCOM_M31_USB) += phy-qcom-m31.o
obj-$(CONFIG_PHY_QCOM_M31_EUSB) += phy-qcom-m31-eusb2.o
+
+phy-qcom-mipi-csi2-objs += phy-qcom-mipi-csi2-core.o \
+ phy-qcom-mipi-csi2-3ph-dphy.o
+obj-$(CONFIG_PHY_QCOM_MIPI_CSI2) += phy-qcom-mipi-csi2.o
+
obj-$(CONFIG_PHY_QCOM_PCIE2) += phy-qcom-pcie2.o
obj-$(CONFIG_PHY_QCOM_QMP_COMBO) += phy-qcom-qmp-combo.o phy-qcom-qmp-usbc.o
diff --git a/drivers/phy/qualcomm/phy-qcom-mipi-csi2-3ph-dphy.c b/drivers/phy/qualcomm/phy-qcom-mipi-csi2-3ph-dphy.c
new file mode 100644
index 0000000000000..f9f3451e9a5e1
--- /dev/null
+++ b/drivers/phy/qualcomm/phy-qcom-mipi-csi2-3ph-dphy.c
@@ -0,0 +1,384 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * camss-phy_qcom_mipi_csi2-3ph-1-0.c
+ *
+ * Qualcomm MSM Camera Subsystem - CSIPHY Module 3phase v1.0
+ *
+ * Copyright (c) 2011-2015, The Linux Foundation. All rights reserved.
+ * Copyright (C) 2016-2025 Linaro Ltd.
+ */
+
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/time64.h>
+
+#include "phy-qcom-mipi-csi2.h"
+
+#define CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(offset, n) ((offset) + 0x4 * (n))
+#define CSIPHY_3PH_CMN_CSI_COMMON_CTRL0_PHY_SW_RESET BIT(0)
+#define CSIPHY_3PH_CMN_CSI_COMMON_CTRL5_CLK_ENABLE BIT(7)
+#define CSIPHY_3PH_CMN_CSI_COMMON_CTRL6_COMMON_PWRDN_B BIT(0)
+#define CSIPHY_3PH_CMN_CSI_COMMON_CTRL6_SHOW_REV_ID BIT(1)
+#define CSIPHY_3PH_CMN_CSI_COMMON_CTRL10_IRQ_CLEAR_CMD BIT(0)
+#define CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(offset, n) ((offset) + 0xb0 + 0x4 * (n))
+
+/*
+ * 3 phase CSI has 19 common status regs with only 0-10 being used
+ * and 11-18 being reserved.
+ */
+#define CSI_COMMON_STATUS_NUM 11
+/*
+ * There are a number of common control registers
+ * The offset to clear the CSIPHY IRQ status starts @ 22
+ * So to clear CSI_COMMON_STATUS0 this is CSI_COMMON_CONTROL22, STATUS1 is
+ * CONTROL23 and so on
+ */
+#define CSI_CTRL_STATUS_INDEX 22
+
+/*
+ * There are 43 COMMON_CTRL registers with regs after # 33 being reserved
+ */
+#define CSI_CTRL_MAX 33
+
+#define CSIPHY_DEFAULT_PARAMS 0
+#define CSIPHY_LANE_ENABLE 1
+#define CSIPHY_SETTLE_CNT_LOWER_BYTE 2
+#define CSIPHY_SETTLE_CNT_HIGHER_BYTE 3
+#define CSIPHY_DNP_PARAMS 4
+#define CSIPHY_2PH_REGS 5
+#define CSIPHY_3PH_REGS 6
+#define CSIPHY_SKEW_CAL 7
+
+/* 4nm 2PH v 2.1.2 2p5Gbps 4 lane DPHY mode */
+static const struct
+mipi_csi2phy_lane_regs lane_regs_x1e80100[] = {
+ /* Power up lanes 2ph mode */
+ {.reg_addr = 0x1014, .reg_data = 0xD5, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x101C, .reg_data = 0x7A, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x1018, .reg_data = 0x01, .param_type = CSIPHY_DEFAULT_PARAMS},
+
+ {.reg_addr = 0x0094, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x00A0, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0090, .reg_data = 0x0f, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0098, .reg_data = 0x08, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0094, .reg_data = 0x07, .delay_us = 0x01, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0030, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0000, .reg_data = 0x8E, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0038, .reg_data = 0xFE, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x002C, .reg_data = 0x01, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0034, .reg_data = 0x0F, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x001C, .reg_data = 0x0A, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0014, .reg_data = 0x60, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x003C, .reg_data = 0xB8, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0004, .reg_data = 0x0C, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0020, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0008, .reg_data = 0x10, .param_type = CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {.reg_addr = 0x0010, .reg_data = 0x52, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0094, .reg_data = 0xD7, .param_type = CSIPHY_SKEW_CAL},
+ {.reg_addr = 0x005C, .reg_data = 0x00, .param_type = CSIPHY_SKEW_CAL},
+ {.reg_addr = 0x0060, .reg_data = 0xBD, .param_type = CSIPHY_SKEW_CAL},
+ {.reg_addr = 0x0064, .reg_data = 0x7F, .param_type = CSIPHY_SKEW_CAL},
+
+ {.reg_addr = 0x0E94, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0EA0, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0E90, .reg_data = 0x0f, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0E98, .reg_data = 0x08, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0E94, .reg_data = 0x07, .delay_us = 0x01, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0E30, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0E28, .reg_data = 0x04, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0E00, .reg_data = 0x80, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0E0C, .reg_data = 0xFF, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0E38, .reg_data = 0x1F, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0E2C, .reg_data = 0x01, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0E34, .reg_data = 0x0F, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0E1C, .reg_data = 0x0A, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0E14, .reg_data = 0x60, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0E3C, .reg_data = 0xB8, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0E04, .reg_data = 0x0C, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0E20, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0E08, .reg_data = 0x10, .param_type = CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {.reg_addr = 0x0E10, .reg_data = 0x52, .param_type = CSIPHY_DEFAULT_PARAMS},
+
+ {.reg_addr = 0x0494, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x04A0, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0490, .reg_data = 0x0f, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0498, .reg_data = 0x08, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0494, .reg_data = 0x07, .delay_us = 0x01, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0430, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0400, .reg_data = 0x8E, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0438, .reg_data = 0xFE, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x042C, .reg_data = 0x01, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0434, .reg_data = 0x0F, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x041C, .reg_data = 0x0A, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0414, .reg_data = 0x60, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x043C, .reg_data = 0xB8, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0404, .reg_data = 0x0C, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0420, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0408, .reg_data = 0x10, .param_type = CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {.reg_addr = 0x0410, .reg_data = 0x52, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0494, .reg_data = 0xD7, .param_type = CSIPHY_SKEW_CAL},
+ {.reg_addr = 0x045C, .reg_data = 0x00, .param_type = CSIPHY_SKEW_CAL},
+ {.reg_addr = 0x0460, .reg_data = 0xBD, .param_type = CSIPHY_SKEW_CAL},
+ {.reg_addr = 0x0464, .reg_data = 0x7F, .param_type = CSIPHY_SKEW_CAL},
+
+ {.reg_addr = 0x0894, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x08A0, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0890, .reg_data = 0x0f, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0898, .reg_data = 0x08, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0894, .reg_data = 0x07, .delay_us = 0x01, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0830, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0800, .reg_data = 0x8E, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0838, .reg_data = 0xFE, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x082C, .reg_data = 0x01, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0834, .reg_data = 0x0F, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x081C, .reg_data = 0x0A, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0814, .reg_data = 0x60, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x083C, .reg_data = 0xB8, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0804, .reg_data = 0x0C, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0820, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0808, .reg_data = 0x10, .param_type = CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {.reg_addr = 0x0810, .reg_data = 0x52, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0894, .reg_data = 0xD7, .param_type = CSIPHY_SKEW_CAL},
+ {.reg_addr = 0x085C, .reg_data = 0x00, .param_type = CSIPHY_SKEW_CAL},
+ {.reg_addr = 0x0860, .reg_data = 0xBD, .param_type = CSIPHY_SKEW_CAL},
+ {.reg_addr = 0x0864, .reg_data = 0x7F, .param_type = CSIPHY_SKEW_CAL},
+
+ {.reg_addr = 0x0C94, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0CA0, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0C90, .reg_data = 0x0f, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0C98, .reg_data = 0x08, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0C94, .reg_data = 0x07, .delay_us = 0x01, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0C30, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0C00, .reg_data = 0x8E, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0C38, .reg_data = 0xFE, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0C2C, .reg_data = 0x01, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0C34, .reg_data = 0x0F, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0C1C, .reg_data = 0x0A, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0C14, .reg_data = 0x60, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0C3C, .reg_data = 0xB8, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0C04, .reg_data = 0x0C, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0C20, .reg_data = 0x00, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0C08, .reg_data = 0x10, .param_type = CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {.reg_addr = 0x0C10, .reg_data = 0x52, .param_type = CSIPHY_DEFAULT_PARAMS},
+ {.reg_addr = 0x0C94, .reg_data = 0xD7, .param_type = CSIPHY_SKEW_CAL},
+ {.reg_addr = 0x0C5C, .reg_data = 0x00, .param_type = CSIPHY_SKEW_CAL},
+ {.reg_addr = 0x0C60, .reg_data = 0xBD, .param_type = CSIPHY_SKEW_CAL},
+ {.reg_addr = 0x0C64, .reg_data = 0x7F, .param_type = CSIPHY_SKEW_CAL},
+};
+
+static inline const struct mipi_csi2phy_device_regs *
+csi2phy_dev_to_regs(struct mipi_csi2phy_device *csi2phy)
+{
+ return &csi2phy->soc_cfg->reg_info;
+}
+
+static void phy_qcom_mipi_csi2_hw_version_read(struct mipi_csi2phy_device *csi2phy)
+{
+ const struct mipi_csi2phy_device_regs *regs = csi2phy_dev_to_regs(csi2phy);
+ u32 tmp;
+
+ writel(CSIPHY_3PH_CMN_CSI_COMMON_CTRL6_SHOW_REV_ID, csi2phy->base +
+ CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->common_regs_offset, 6));
+
+ tmp = readl_relaxed(csi2phy->base +
+ CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(regs->common_regs_offset, 12));
+ csi2phy->hw_version = tmp;
+
+ tmp = readl_relaxed(csi2phy->base +
+ CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(regs->common_regs_offset, 13));
+ csi2phy->hw_version |= (tmp << 8) & 0xFF00;
+
+ tmp = readl_relaxed(csi2phy->base +
+ CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(regs->common_regs_offset, 14));
+ csi2phy->hw_version |= (tmp << 16) & 0xFF0000;
+
+ tmp = readl_relaxed(csi2phy->base +
+ CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(regs->common_regs_offset, 15));
+ csi2phy->hw_version |= (tmp << 24) & 0xFF000000;
+
+ dev_dbg_once(csi2phy->dev, "CSIPHY 3PH HW Version = 0x%08x\n", csi2phy->hw_version);
+}
+
+/*
+ * phy_qcom_mipi_csi2_reset - Perform software reset on CSIPHY module
+ * @phy_qcom_mipi_csi2: CSIPHY device
+ */
+static void phy_qcom_mipi_csi2_reset(struct mipi_csi2phy_device *csi2phy)
+{
+ const struct mipi_csi2phy_device_regs *regs = csi2phy_dev_to_regs(csi2phy);
+
+ writel(CSIPHY_3PH_CMN_CSI_COMMON_CTRL0_PHY_SW_RESET,
+ csi2phy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->common_regs_offset, 0));
+ usleep_range(5000, 8000);
+ writel(0x0, csi2phy->base +
+ CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->common_regs_offset, 0));
+}
+
+/*
+ * phy_qcom_mipi_csi2_settle_cnt_calc - Calculate settle count value
+ *
+ * Helper function to calculate settle count value. This is
+ * based on the CSI2 T_hs_settle parameter which in turn
+ * is calculated based on the CSI2 transmitter link frequency.
+ *
+ * Return settle count value or 0 if the CSI2 link frequency
+ * is not available
+ */
+static u8 phy_qcom_mipi_csi2_settle_cnt_calc(s64 link_freq, u32 timer_clk_rate)
+{
+ u32 t_hs_prepare_max_ps;
+ u32 timer_period_ps;
+ u32 t_hs_settle_ps;
+ u8 settle_cnt;
+ u32 ui_ps;
+
+ if (link_freq <= 0)
+ return 0;
+
+ ui_ps = div_u64(PSEC_PER_SEC, link_freq);
+ ui_ps /= 2;
+ t_hs_prepare_max_ps = 85000 + 6 * ui_ps;
+ t_hs_settle_ps = t_hs_prepare_max_ps;
+
+ timer_period_ps = div_u64(PSEC_PER_SEC, timer_clk_rate);
+ settle_cnt = t_hs_settle_ps / timer_period_ps - 6;
+
+ return settle_cnt;
+}
+
+static void
+phy_qcom_mipi_csi2_gen2_config_lanes(struct mipi_csi2phy_device *csi2phy,
+ u8 settle_cnt)
+{
+ const struct mipi_csi2phy_device_regs *regs = csi2phy_dev_to_regs(csi2phy);
+ const struct mipi_csi2phy_lane_regs *r = regs->init_seq;
+ int i, array_size = regs->lane_array_size;
+ u32 val;
+
+ for (i = 0; i < array_size; i++, r++) {
+ switch (r->param_type) {
+ case CSIPHY_SETTLE_CNT_LOWER_BYTE:
+ val = settle_cnt & 0xff;
+ break;
+ case CSIPHY_SKEW_CAL:
+ /* TODO: support application of skew from dt flag */
+ continue;
+ default:
+ val = r->reg_data;
+ break;
+ }
+ writel(val, csi2phy->base + r->reg_addr);
+ if (r->delay_us)
+ udelay(r->delay_us);
+ }
+}
+
+static int phy_qcom_mipi_csi2_lanes_enable(struct mipi_csi2phy_device *csi2phy,
+ struct mipi_csi2phy_stream_cfg *cfg)
+{
+ const struct mipi_csi2phy_device_regs *regs = csi2phy_dev_to_regs(csi2phy);
+ struct mipi_csi2phy_lanes_cfg *lane_cfg = &cfg->lane_cfg;
+ u8 settle_cnt;
+ u8 val;
+ int i;
+
+ settle_cnt = phy_qcom_mipi_csi2_settle_cnt_calc(cfg->link_freq, csi2phy->timer_clk_rate);
+
+ val = CSIPHY_3PH_CMN_CSI_COMMON_CTRL5_CLK_ENABLE;
+ for (i = 0; i < cfg->num_data_lanes; i++)
+ val |= BIT(lane_cfg->data[i].pos * 2);
+
+ writel(val, csi2phy->base +
+ CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->common_regs_offset, 5));
+
+ val = CSIPHY_3PH_CMN_CSI_COMMON_CTRL6_COMMON_PWRDN_B;
+ writel(val, csi2phy->base +
+ CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->common_regs_offset, 6));
+
+ val = 0x02;
+ writel(val, csi2phy->base +
+ CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->common_regs_offset, 7));
+
+ val = 0x00;
+ writel(val, csi2phy->base +
+ CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->common_regs_offset, 0));
+
+ phy_qcom_mipi_csi2_gen2_config_lanes(csi2phy, settle_cnt);
+
+ /* IRQ_MASK registers - disable all interrupts */
+ for (i = CSI_COMMON_STATUS_NUM; i < CSI_CTRL_STATUS_INDEX; i++) {
+ writel(0, csi2phy->base +
+ CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->common_regs_offset, i));
+ }
+
+ return 0;
+}
+
+static void
+phy_qcom_mipi_csi2_lanes_disable(struct mipi_csi2phy_device *csi2phy,
+ struct mipi_csi2phy_stream_cfg *cfg)
+{
+ const struct mipi_csi2phy_device_regs *regs = csi2phy_dev_to_regs(csi2phy);
+
+ writel(0, csi2phy->base +
+ CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->common_regs_offset, 5));
+
+ writel(0, csi2phy->base +
+ CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->common_regs_offset, 6));
+}
+
+static const struct mipi_csi2phy_hw_ops phy_qcom_mipi_csi2_ops_3ph_1_0 = {
+ .hw_version_read = phy_qcom_mipi_csi2_hw_version_read,
+ .reset = phy_qcom_mipi_csi2_reset,
+ .lanes_enable = phy_qcom_mipi_csi2_lanes_enable,
+ .lanes_disable = phy_qcom_mipi_csi2_lanes_disable,
+};
+
+static const struct mipi_csi2phy_clk_freq zero = { 0 };
+
+static const struct mipi_csi2phy_clk_freq dphy_4nm_x1e_csiphy = {
+ .freq = {
+ 300000000, 400000000, 480000000
+ },
+ .num_freq = 3,
+};
+
+static const struct mipi_csi2phy_clk_freq dphy_4nm_x1e_csiphy_timer = {
+ .freq = {
+ 266666667, 400000000
+ },
+ .num_freq = 2,
+};
+
+static const char * const x1e_clks[] = {
+ "camnoc_axi",
+ "cpas_ahb",
+ "csiphy",
+ "csiphy_timer"
+};
+
+const struct mipi_csi2phy_soc_cfg mipi_csi2_dphy_4nm_x1e = {
+ .ops = &phy_qcom_mipi_csi2_ops_3ph_1_0,
+ .reg_info = {
+ .init_seq = lane_regs_x1e80100,
+ .lane_array_size = ARRAY_SIZE(lane_regs_x1e80100),
+ .common_regs_offset = 0x1000,
+ .generation = GEN2,
+ },
+ .supply_names = (const char *[]){
+ "vdda-0p8",
+ "vdda-1p2"
+ },
+ .num_supplies = 2,
+ .clk_names = (const char **)x1e_clks,
+ .num_clk = ARRAY_SIZE(x1e_clks),
+ .opp_clk = x1e_clks[2],
+ .timer_clk = x1e_clks[3],
+ .clk_freq = {
+ zero,
+ zero,
+ dphy_4nm_x1e_csiphy,
+ dphy_4nm_x1e_csiphy_timer,
+ },
+};
diff --git a/drivers/phy/qualcomm/phy-qcom-mipi-csi2-core.c b/drivers/phy/qualcomm/phy-qcom-mipi-csi2-core.c
new file mode 100644
index 0000000000000..454144f81b719
--- /dev/null
+++ b/drivers/phy/qualcomm/phy-qcom-mipi-csi2-core.c
@@ -0,0 +1,307 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2025, Linaro Ltd.
+ */
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pm_opp.h>
+#include <linux/phy/phy.h>
+#include <linux/phy/phy-mipi-dphy.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
+#include <linux/slab.h>
+
+#include "phy-qcom-mipi-csi2.h"
+
+#define CAMSS_CLOCK_MARGIN_NUMERATOR 105
+#define CAMSS_CLOCK_MARGIN_DENOMINATOR 100
+
+static inline void phy_qcom_mipi_csi2_add_clock_margin(u64 *rate)
+{
+ *rate *= CAMSS_CLOCK_MARGIN_NUMERATOR;
+ *rate = div_u64(*rate, CAMSS_CLOCK_MARGIN_DENOMINATOR);
+}
+
+static int
+phy_qcom_mipi_csi2_set_clock_rates(struct mipi_csi2phy_device *csi2phy,
+ s64 link_freq)
+{
+ const struct mipi_csi2phy_soc_cfg *soc_cfg = csi2phy->soc_cfg;
+ unsigned long rates[MAX_CSI2PHY_CLKS] = {0};
+ struct device *dev = csi2phy->dev;
+ unsigned long vote_freq = 0;
+ int i, j;
+ int ret;
+
+ for (i = 0; i < soc_cfg->num_clk; i++) {
+ const struct mipi_csi2phy_clk_freq *clk_freq = &soc_cfg->clk_freq[i];
+ const char *clk_name = soc_cfg->clk_names[i];
+ struct clk *clk = csi2phy->clks[i].clk;
+ u64 min_rate = link_freq / 4;
+ long round_rate;
+
+ phy_qcom_mipi_csi2_add_clock_margin(&min_rate);
+
+ /* This clock should be enabled only not set */
+ if (!clk_freq->num_freq)
+ continue;
+
+ for (j = 0; j < clk_freq->num_freq; j++)
+ if (min_rate < clk_freq->freq[j])
+ break;
+
+ if (j == clk_freq->num_freq) {
+ dev_err(dev,
+ "Pixel clock %llu is too high for %s\n",
+ min_rate, clk_name);
+ return -EINVAL;
+ }
+
+ /* if sensor pixel clock is not available
+ * set highest possible CSIPHY clock rate
+ */
+ if (min_rate == 0)
+ j = clk_freq->num_freq - 1;
+
+ round_rate = clk_round_rate(clk, clk_freq->freq[j]);
+ if (round_rate < 0) {
+ dev_err(dev, "clk round rate failed: %ld\n",
+ round_rate);
+ return -EINVAL;
+ }
+
+ rates[i] = round_rate;
+
+ if (!strcmp(clk_name, soc_cfg->timer_clk))
+ csi2phy->timer_clk_rate = round_rate;
+
+ if (!strcmp(clk_name, soc_cfg->opp_clk))
+ vote_freq = round_rate;
+ }
+
+ if (!vote_freq) {
+ dev_err(dev, "Unable to find operating point frequency\n");
+ return -ENODEV;
+ };
+
+ dev_dbg(dev, "OPP freq: %lu Hz\n", vote_freq);
+
+ ret = dev_pm_opp_set_rate(dev, vote_freq);
+ if (ret < 0) {
+ dev_err(dev, "Failed to set OPP rate: %d\n", ret);
+ return ret;
+ }
+
+ for (i = 0; i < soc_cfg->num_clk; i++) {
+ if (rates[i] == 0)
+ continue;
+
+ dev_dbg(dev, "Setting clk %s to %lu Hz\n",
+ soc_cfg->clk_names[i], rates[i]);
+
+ ret = clk_set_rate(csi2phy->clks[i].clk, rates[i]);
+ if (ret < 0) {
+ dev_err(dev, "clk_set_rate failed for %s: %d\n",
+ soc_cfg->clk_names[i], ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int phy_qcom_mipi_csi2_configure(struct phy *phy,
+ union phy_configure_opts *opts)
+{
+ struct mipi_csi2phy_device *csi2phy = phy_get_drvdata(phy);
+ struct phy_configure_opts_mipi_dphy *dphy_cfg_opts = &opts->mipi_dphy;
+ struct mipi_csi2phy_stream_cfg *stream_cfg = &csi2phy->stream_cfg;
+ int ret;
+ int i;
+
+ ret = phy_mipi_dphy_config_validate(dphy_cfg_opts);
+ if (ret)
+ return ret;
+
+ if (dphy_cfg_opts->lanes < 1 || dphy_cfg_opts->lanes > CSI2_MAX_DATA_LANES)
+ return -EINVAL;
+
+ stream_cfg->combo_mode = 0;
+ stream_cfg->link_freq = dphy_cfg_opts->hs_clk_rate;
+ stream_cfg->num_data_lanes = dphy_cfg_opts->lanes;
+
+ /*
+ * phy_configure_opts_mipi_dphy.lanes starts from zero to
+ * the maximum number of enabled lanes.
+ *
+ * TODO: add support for bitmask of enabled lanes and polarities
+ * of those lanes to the phy_configure_opts_mipi_dphy struct.
+ * For now take the polarities as zero and the position as fixed
+ * this is fine as no current upstream implementation maps otherwise.
+ */
+ for (i = 0; i < stream_cfg->num_data_lanes; i++) {
+ stream_cfg->lane_cfg.data[i].pol = 0;
+ stream_cfg->lane_cfg.data[i].pos = i;
+ }
+
+ stream_cfg->lane_cfg.clk.pol = 0;
+ stream_cfg->lane_cfg.clk.pos = 7;
+
+ return 0;
+}
+
+static int phy_qcom_mipi_csi2_power_on(struct phy *phy)
+{
+ struct mipi_csi2phy_device *csi2phy = phy_get_drvdata(phy);
+ const struct mipi_csi2phy_hw_ops *ops = csi2phy->soc_cfg->ops;
+ struct device *dev = &phy->dev;
+ int ret;
+
+ ret = regulator_bulk_enable(csi2phy->soc_cfg->num_supplies,
+ csi2phy->supplies);
+ if (ret)
+ return ret;
+
+ ret = phy_qcom_mipi_csi2_set_clock_rates(csi2phy, csi2phy->stream_cfg.link_freq);
+ if (ret)
+ goto poweroff_phy;
+
+ ret = clk_bulk_prepare_enable(csi2phy->soc_cfg->num_clk,
+ csi2phy->clks);
+ if (ret) {
+ dev_err(dev, "failed to enable clocks, %d\n", ret);
+ goto poweroff_phy;
+ }
+
+ ops->reset(csi2phy);
+
+ ops->hw_version_read(csi2phy);
+
+ return ops->lanes_enable(csi2phy, &csi2phy->stream_cfg);
+
+poweroff_phy:
+ regulator_bulk_disable(csi2phy->soc_cfg->num_supplies,
+ csi2phy->supplies);
+
+ return ret;
+}
+
+static int phy_qcom_mipi_csi2_power_off(struct phy *phy)
+{
+ struct mipi_csi2phy_device *csi2phy = phy_get_drvdata(phy);
+
+ clk_bulk_disable_unprepare(csi2phy->soc_cfg->num_clk,
+ csi2phy->clks);
+ regulator_bulk_disable(csi2phy->soc_cfg->num_supplies,
+ csi2phy->supplies);
+
+ return 0;
+}
+
+static const struct phy_ops phy_qcom_mipi_csi2_ops = {
+ .configure = phy_qcom_mipi_csi2_configure,
+ .power_on = phy_qcom_mipi_csi2_power_on,
+ .power_off = phy_qcom_mipi_csi2_power_off,
+ .owner = THIS_MODULE,
+};
+
+static int phy_qcom_mipi_csi2_probe(struct platform_device *pdev)
+{
+ unsigned int i, num_clk, num_supplies;
+ struct mipi_csi2phy_device *csi2phy;
+ struct phy_provider *phy_provider;
+ struct device *dev = &pdev->dev;
+ struct phy *generic_phy;
+ int ret;
+
+ csi2phy = devm_kzalloc(dev, sizeof(*csi2phy), GFP_KERNEL);
+ if (!csi2phy)
+ return -ENOMEM;
+
+ csi2phy->dev = dev;
+ csi2phy->soc_cfg = device_get_match_data(&pdev->dev);
+
+ if (!csi2phy->soc_cfg)
+ return -EINVAL;
+
+ num_clk = csi2phy->soc_cfg->num_clk;
+ csi2phy->clks = devm_kzalloc(dev, sizeof(*csi2phy->clks) * num_clk, GFP_KERNEL);
+ if (!csi2phy->clks)
+ return -ENOMEM;
+
+ for (i = 0; i < num_clk; i++)
+ csi2phy->clks[i].id = csi2phy->soc_cfg->clk_names[i];
+
+ ret = devm_clk_bulk_get(dev, num_clk, csi2phy->clks);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to get clocks\n");
+
+ ret = devm_pm_opp_set_clkname(dev, csi2phy->soc_cfg->opp_clk);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to set opp clkname\n");
+
+ ret = devm_pm_opp_of_add_table(dev);
+ if (ret && ret != -ENODEV)
+ return dev_err_probe(dev, ret, "invalid OPP table in device tree\n");
+
+ num_supplies = csi2phy->soc_cfg->num_supplies;
+ csi2phy->supplies = devm_kzalloc(dev, sizeof(*csi2phy->supplies) * num_supplies,
+ GFP_KERNEL);
+ if (!csi2phy->supplies)
+ return -ENOMEM;
+
+ for (i = 0; i < num_supplies; i++)
+ csi2phy->supplies[i].supply = csi2phy->soc_cfg->supply_names[i];
+
+ ret = devm_regulator_bulk_get(dev, num_supplies, csi2phy->supplies);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to get regulator supplies\n");
+
+ csi2phy->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(csi2phy->base))
+ return PTR_ERR(csi2phy->base);
+
+ generic_phy = devm_phy_create(dev, NULL, &phy_qcom_mipi_csi2_ops);
+ if (IS_ERR(generic_phy)) {
+ ret = PTR_ERR(generic_phy);
+ return dev_err_probe(dev, ret, "failed to create phy\n");
+ }
+ csi2phy->phy = generic_phy;
+
+ phy_set_drvdata(generic_phy, csi2phy);
+
+ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+ if (!IS_ERR(phy_provider))
+ dev_dbg(dev, "Registered MIPI CSI2 PHY device\n");
+
+ return PTR_ERR_OR_ZERO(phy_provider);
+}
+
+static const struct of_device_id phy_qcom_mipi_csi2_of_match_table[] = {
+ { .compatible = "qcom,x1e80100-csi2-phy", .data = &mipi_csi2_dphy_4nm_x1e },
+ { }
+};
+MODULE_DEVICE_TABLE(of, phy_qcom_mipi_csi2_of_match_table);
+
+static struct platform_driver phy_qcom_mipi_csi2_driver = {
+ .probe = phy_qcom_mipi_csi2_probe,
+ .driver = {
+ .name = "qcom-mipi-csi2-phy",
+ .of_match_table = phy_qcom_mipi_csi2_of_match_table,
+ },
+};
+
+module_platform_driver(phy_qcom_mipi_csi2_driver);
+
+MODULE_DESCRIPTION("Qualcomm MIPI CSI2 PHY driver");
+MODULE_AUTHOR("Bryan O'Donoghue <bryan.odonoghue@linaro.org>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/phy/qualcomm/phy-qcom-mipi-csi2.h b/drivers/phy/qualcomm/phy-qcom-mipi-csi2.h
new file mode 100644
index 0000000000000..4f3a245ba6a53
--- /dev/null
+++ b/drivers/phy/qualcomm/phy-qcom-mipi-csi2.h
@@ -0,0 +1,102 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ *
+ * Qualcomm MIPI CSI2 CPHY/DPHY driver
+ *
+ * Copyright (C) 2025 Linaro Ltd.
+ */
+#ifndef __PHY_QCOM_MIPI_CSI2_H__
+#define __PHY_QCOM_MIPI_CSI2_H__
+
+#include <linux/phy/phy.h>
+
+#define CSI2_MAX_DATA_LANES 4
+
+struct mipi_csi2phy_lane {
+ u8 pos;
+ u8 pol;
+};
+
+struct mipi_csi2phy_lanes_cfg {
+ struct mipi_csi2phy_lane data[CSI2_MAX_DATA_LANES];
+ struct mipi_csi2phy_lane clk;
+};
+
+struct mipi_csi2phy_stream_cfg {
+ u8 combo_mode;
+ s64 link_freq;
+ u8 num_data_lanes;
+ struct mipi_csi2phy_lanes_cfg lane_cfg;
+};
+
+struct mipi_csi2phy_device;
+
+struct mipi_csi2phy_hw_ops {
+ void (*hw_version_read)(struct mipi_csi2phy_device *csi2phy_dev);
+ void (*reset)(struct mipi_csi2phy_device *csi2phy_dev);
+ int (*lanes_enable)(struct mipi_csi2phy_device *csi2phy_dev,
+ struct mipi_csi2phy_stream_cfg *cfg);
+ void (*lanes_disable)(struct mipi_csi2phy_device *csi2phy_dev,
+ struct mipi_csi2phy_stream_cfg *cfg);
+};
+
+struct mipi_csi2phy_lane_regs {
+ const s32 reg_addr;
+ const s32 reg_data;
+ const u32 delay_us;
+ const u32 param_type;
+};
+
+struct mipi_csi2phy_device_regs {
+ const struct mipi_csi2phy_lane_regs *init_seq;
+ const int lane_array_size;
+ const u32 common_regs_offset;
+ enum {
+ GEN1 = 0,
+ GEN1_660,
+ GEN1_670,
+ GEN2,
+ } generation;
+};
+
+#define MAX_CSI2PHY_CLKS 8
+struct mipi_csi2phy_clk_freq {
+ u32 num_freq;
+ u32 freq[MAX_CSI2PHY_CLKS];
+};
+
+struct mipi_csi2phy_soc_cfg {
+ const struct mipi_csi2phy_hw_ops *ops;
+ const struct mipi_csi2phy_device_regs reg_info;
+
+ const char ** const supply_names;
+ const unsigned int num_supplies;
+
+ const char ** const clk_names;
+ const unsigned int num_clk;
+
+ const char * const opp_clk;
+ const char * const timer_clk;
+
+ const struct mipi_csi2phy_clk_freq clk_freq[];
+};
+
+struct mipi_csi2phy_device {
+ struct device *dev;
+
+ struct phy *phy;
+ void __iomem *base;
+
+ struct clk_bulk_data *clks;
+ struct regulator_bulk_data *supplies;
+ u32 timer_clk_rate;
+
+ const struct mipi_csi2phy_soc_cfg *soc_cfg;
+ struct mipi_csi2phy_stream_cfg stream_cfg;
+
+ u32 hw_version;
+};
+
+extern const struct mipi_csi2phy_soc_cfg mipi_csi2_dphy_4nm_x1e;
+
+#endif /* __PHY_QCOM_MIPI_CSI2_H__ */
--
2.52.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* Re: [PATCH v10 8/9] mux: add prompt and help text to CONFIG_MULTIPLEXER making it visible
From: Josua Mayer @ 2026-02-26 13:19 UTC (permalink / raw)
To: Geert Uytterhoeven
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, Wolfram Sang,
Yoshihiro Shimoda, Yazan Shhady, Jon Nettleton, Vladimir Oltean,
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
In-Reply-To: <CAMuHMdUSU8Y9gj5qJ7qNE1UVhp7=HTjAxEsL6uZXPXFgwTd+7Q@mail.gmail.com>
Am 25.02.26 um 13:07 schrieb Geert Uytterhoeven:
> Hi Josua,
>
> On Wed, 25 Feb 2026 at 12:35, Josua Mayer <josua@solid-run.com> wrote:
>> The multiplexer subsystem was initially designed only for use by drivers
>> that require muxes, and did in particular not consider optional muxes or
>> to compile as a module.
>>
>> Over time several drivers have added a "select MULTIPLEXER" dependency,
>> some of which require a mux and some consider it optional. v7.0-rc1
>> shows 15 such occurrences in Kconfig files, in a variety of subsystems.
>>
>> Further some drivers such as gpio-mux are useful on their own (e.g.
>> through device-tree idle-state property), but can not currently be
>> selected through menuconfig unless another driver selecting MULTIPLEXER
>> symbol was enabled first.
>>
>> The natural step forward to allow enabling mux core and drivers would be
>> adding prompt and help text to the existing symbol.
>>
>> This violates the general kbuild advice to avoid selecting visible
>> symbols.
>>
>> Alternatively addition of a wrapper symbol MUX_CORE was considered,
>> which in turn would "select MULTIPLEXER". This however creates new
>> issues and confusion as MULTIPLEXER and MUX_CORE need to share the same
>> state, i.e. MUX_CORE in menuconfig must not be set to m while
>> MULTIPLEXER was selected builtin. Further confusion occurs with Kconfig
>> "depends on" relationships that could reference either MUX_CORE or
>> MULTIPLEXER.
>>
>> It is common across the tree for subsystem symbols to be both visible
>> and selected, e.g. I2C & SPI. In the same spirit multiplexer needs to
>> ignore this particular kbuild rule.
>>
>> Add prompt and help text to the existing MULTIPLEXER symbol, making it
>> visible in (menu)config without breaking existing "select MULTIPLEXER"
>> occurrences in the tree.
>>
>> Select it by default when COMPILE_TEST is set for better coverage.
>>
>> Signed-off-by: Josua Mayer <josua@solid-run.com>
> Thanks for your patch!
>
>> --- a/drivers/mux/Kconfig
>> +++ b/drivers/mux/Kconfig
>> @@ -4,7 +4,14 @@
>> #
>>
>> config MULTIPLEXER
>> - tristate
>> + tristate "Generic Multiplexer Support"
>> + default m if COMPILE_TEST
> Please drop this line. Merely enabling COMPILE_TEST should not
> enable extra functionality.
Acknowledged, thanks!.
>
>> + help
>> + This framework is designed to abstract multiplexer handling for
>> + devices via various GPIO-, MMIO/Regmap or specific multiplexer
>> + controller chips.
>> +
>> + If unsure, say no.
>>
>> menu "Multiplexer drivers"
>> depends on MULTIPLEXER
> Gr{oetje,eeting}s,
>
> Geert
>
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* [PATCH v11 0/9] mmc: host: renesas_sdhi_core: support configuring an optional sdio mux
From: Josua Mayer @ 2026-02-26 13:21 UTC (permalink / raw)
To: 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, Wolfram Sang,
Yoshihiro Shimoda
Cc: Yazan Shhady, Jon Nettleton, Vladimir Oltean, Mikhail Anikin,
linux-can, linux-phy, linux-kernel, linux-omap, linux-i2c,
linux-mmc, devicetree, linux-renesas-soc, Josua Mayer
This series has evolved over time from adding generic mux support for
renesas sdhi driver, to partial rewrite of the mux framework.
Several drivers have started implementing driver-local managed and
unmanaged helper functions for getting and selecting a mux-state object.
mmc maintainers have requested that new code shall intreoduce and use
generic helper functions that can be shared by all drivers, avoiding
code duplication.
This series is structured in 5 parts, each of which is self-sufficient
depending only on the previous patches. This shall allow the first N
patches to be applied even if the last ones need further discussion.
1. Rename driver-local helper functions to avoid name collision with
global version to be introduced later.
2. Implement generic device-managed helper functions in mux core.
3. Convert driver local code from similar patterns to use the newly
added global helpers.
4. Change mux-core Kconfig so that it can be enabled through menuconfig,
without an explicit "select" dependency from other drivers.
5. add dt bindings and driver support for mux in renesas sdhi driver.
Signed-off-by: Josua Mayer <josua@solid-run.com>
---
Changes in v11:
- changed approach to Kconfig making MULTIPLEXER a bool, and adding a
user-visible wrapper for menuconfig.
(Reported-by: Ulf Hansson <ulf.hansson@linaro.org>)
- dropped the "default m if COMPILE_TEST".
(Reported-by: Geert Uytterhoeven <geert@linux-m68k.org>)
- improved kerneldoc line wrapping.
- removed unnecessary changes to original devm_mux_control-get.
- fix "reference preceded by free" in mux_state_get function
- Link to v10: https://lore.kernel.org/r/20260225-rz-sdio-mux-v10-0-1ee44f2ea112@solid-run.com
Changes in v10:
- added renesas-sdhi Kconfig dependency for MULTIPLEXER, avoiding build
errors in case setting SDHI=y and MULTIPLEXER=m is attempted.
Trailers were dropped on this patch to encourage fresh review.
- renamed phy-can-transceiver driver-local helper function to be
consistent with other driver-local functions, and dropped all trailers
for this particular patch.
- reconsidered the Kconfig changes due to inputs from mux core author
- handle newly-added mux usage in rcar-gen3-usb2 introduced with
v7.0-rc1
- streamline patch sequence
- rebase on v7.0-rc1
- Link to v9: https://lore.kernel.org/r/20260208-rz-sdio-mux-v9-0-9a3be13c1280@solid-run.com
Changes in v9:
- compile-tested on x86 with MULTIPLEXER=m/y/unset.
- fixed Kconfig changes so that CONFIG_MULTIPLEXER can be selected.
through menuconfig / .config as intended.
- updated trailers
- document null return value for mux_control_get_optional.
- fix build error for CONFIG_MULTIPLEXER=m, found with x86_64
allmodconfig: replaced ifdef ... with if IS_ENABLED(...).
(Reported-by: Mark Brown <broonie@kernel.org>)
- Link to v8: https://lore.kernel.org/r/20260203-rz-sdio-mux-v8-0-024ea405863e@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 (9):
phy: can-transceiver: rename temporary helper function to avoid conflict
phy: renesas: rcar-gen3-usb2: rename local mux helper to avoid conflict
mux: Add helper functions for getting optional and selected mux-state
phy: can-transceiver: drop temporary helper getting optional mux-state
phy: renesas: rcar-gen3-usb2: drop 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
mux: add visible config symbol to enable multiplexer subsystem
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 | 17 +-
drivers/mux/core.c | 194 ++++++++++++++++++---
drivers/phy/phy-can-transceiver.c | 10 --
drivers/phy/renesas/phy-rcar-gen3-usb2.c | 30 +---
include/linux/mux/consumer.h | 108 +++++++++++-
8 files changed, 305 insertions(+), 90 deletions(-)
---
base-commit: 6de23f81a5e08be8fbf5e8d7e9febc72a5b5f27f
change-id: 20251128-rz-sdio-mux-acc5137f1618
Best regards,
--
Josua Mayer <josua@solid-run.com>
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* [PATCH v11 2/9] phy: renesas: rcar-gen3-usb2: rename local mux helper to avoid conflict
From: Josua Mayer @ 2026-02-26 13:21 UTC (permalink / raw)
To: 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, Wolfram Sang,
Yoshihiro Shimoda
Cc: Yazan Shhady, Jon Nettleton, Vladimir Oltean, Mikhail Anikin,
linux-can, linux-phy, linux-kernel, linux-omap, linux-i2c,
linux-mmc, devicetree, linux-renesas-soc, Josua Mayer
In-Reply-To: <20260226-rz-sdio-mux-v11-0-c2a350f9bbd3@solid-run.com>
Rename the temporary devm_mux_state_get_optional function to avoid
conflict with upcoming implementation in multiplexer subsystem.
Signed-off-by: Josua Mayer <josua@solid-run.com>
---
drivers/phy/renesas/phy-rcar-gen3-usb2.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/phy/renesas/phy-rcar-gen3-usb2.c b/drivers/phy/renesas/phy-rcar-gen3-usb2.c
index cfc2a8d9028d..1155b111420a 100644
--- a/drivers/phy/renesas/phy-rcar-gen3-usb2.c
+++ b/drivers/phy/renesas/phy-rcar-gen3-usb2.c
@@ -941,7 +941,7 @@ static int rcar_gen3_phy_usb2_vbus_regulator_register(struct rcar_gen3_chan *cha
/* Temporary wrapper until the multiplexer subsystem supports optional muxes */
static inline struct mux_state *
-devm_mux_state_get_optional(struct device *dev, const char *mux_name)
+rcar_gen3_phy_mux_state_get_optional(struct device *dev, const char *mux_name)
{
if (!of_property_present(dev->of_node, "mux-states"))
return NULL;
@@ -1036,7 +1036,7 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev)
phy_set_drvdata(channel->rphys[i].phy, &channel->rphys[i]);
}
- mux_state = devm_mux_state_get_optional(dev, NULL);
+ mux_state = rcar_gen3_phy_mux_state_get_optional(dev, NULL);
if (IS_ERR(mux_state))
return PTR_ERR(mux_state);
if (mux_state) {
--
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 v11 1/9] phy: can-transceiver: rename temporary helper function to avoid conflict
From: Josua Mayer @ 2026-02-26 13:21 UTC (permalink / raw)
To: 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, Wolfram Sang,
Yoshihiro Shimoda
Cc: Yazan Shhady, Jon Nettleton, Vladimir Oltean, Mikhail Anikin,
linux-can, linux-phy, linux-kernel, linux-omap, linux-i2c,
linux-mmc, devicetree, linux-renesas-soc, Josua Mayer
In-Reply-To: <20260226-rz-sdio-mux-v11-0-c2a350f9bbd3@solid-run.com>
Rename the temporary devm_mux_state_get_optional function to avoid
conflict with upcoming implementation in multiplexer subsystem.
Signed-off-by: Josua Mayer <josua@solid-run.com>
---
drivers/phy/phy-can-transceiver.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/phy/phy-can-transceiver.c b/drivers/phy/phy-can-transceiver.c
index 330356706ad7..fcbca9d2bded 100644
--- a/drivers/phy/phy-can-transceiver.c
+++ b/drivers/phy/phy-can-transceiver.c
@@ -128,7 +128,7 @@ MODULE_DEVICE_TABLE(of, can_transceiver_phy_ids);
/* Temporary wrapper until the multiplexer subsystem supports optional muxes */
static inline struct mux_state *
-devm_mux_state_get_optional(struct device *dev, const char *mux_name)
+can_transceiver_phy_mux_state_get_optional(struct device *dev, const char *mux_name)
{
if (!of_property_present(dev->of_node, "mux-states"))
return NULL;
@@ -183,7 +183,7 @@ static int can_transceiver_phy_probe(struct platform_device *pdev)
priv->num_ch = num_ch;
platform_set_drvdata(pdev, priv);
- mux_state = devm_mux_state_get_optional(dev, NULL);
+ mux_state = can_transceiver_phy_mux_state_get_optional(dev, NULL);
if (IS_ERR(mux_state))
return PTR_ERR(mux_state);
--
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 v11 4/9] phy: can-transceiver: drop temporary helper getting optional mux-state
From: Josua Mayer @ 2026-02-26 13:21 UTC (permalink / raw)
To: 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, Wolfram Sang,
Yoshihiro Shimoda
Cc: Yazan Shhady, Jon Nettleton, Vladimir Oltean, Mikhail Anikin,
linux-can, linux-phy, linux-kernel, linux-omap, linux-i2c,
linux-mmc, devicetree, linux-renesas-soc, Josua Mayer
In-Reply-To: <20260226-rz-sdio-mux-v11-0-c2a350f9bbd3@solid-run.com>
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>
Reviewed-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
Signed-off-by: Josua Mayer <josua@solid-run.com>
---
drivers/phy/phy-can-transceiver.c | 12 +-----------
1 file changed, 1 insertion(+), 11 deletions(-)
diff --git a/drivers/phy/phy-can-transceiver.c b/drivers/phy/phy-can-transceiver.c
index fcbca9d2bded..2b52e47f247a 100644
--- a/drivers/phy/phy-can-transceiver.c
+++ b/drivers/phy/phy-can-transceiver.c
@@ -126,16 +126,6 @@ static const struct of_device_id can_transceiver_phy_ids[] = {
};
MODULE_DEVICE_TABLE(of, can_transceiver_phy_ids);
-/* Temporary wrapper until the multiplexer subsystem supports optional muxes */
-static inline struct mux_state *
-can_transceiver_phy_mux_state_get_optional(struct device *dev, const char *mux_name)
-{
- if (!of_property_present(dev->of_node, "mux-states"))
- return NULL;
-
- return devm_mux_state_get(dev, mux_name);
-}
-
static struct phy *can_transceiver_phy_xlate(struct device *dev,
const struct of_phandle_args *args)
{
@@ -183,7 +173,7 @@ static int can_transceiver_phy_probe(struct platform_device *pdev)
priv->num_ch = num_ch;
platform_set_drvdata(pdev, priv);
- mux_state = can_transceiver_phy_mux_state_get_optional(dev, NULL);
+ mux_state = devm_mux_state_get_optional(dev, NULL);
if (IS_ERR(mux_state))
return PTR_ERR(mux_state);
--
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 v11 5/9] phy: renesas: rcar-gen3-usb2: drop helper getting optional mux-state
From: Josua Mayer @ 2026-02-26 13:21 UTC (permalink / raw)
To: 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, Wolfram Sang,
Yoshihiro Shimoda
Cc: Yazan Shhady, Jon Nettleton, Vladimir Oltean, Mikhail Anikin,
linux-can, linux-phy, linux-kernel, linux-omap, linux-i2c,
linux-mmc, devicetree, linux-renesas-soc, Josua Mayer
In-Reply-To: <20260226-rz-sdio-mux-v11-0-c2a350f9bbd3@solid-run.com>
Multiplexer subsystem has now added helpers for getting managed optional
mux-state.
Switch to the new devm_mux_state_get_optional_selected helper.
This change is only compile-tested.
Signed-off-by: Josua Mayer <josua@solid-run.com>
---
drivers/phy/renesas/phy-rcar-gen3-usb2.c | 30 ++----------------------------
1 file changed, 2 insertions(+), 28 deletions(-)
diff --git a/drivers/phy/renesas/phy-rcar-gen3-usb2.c b/drivers/phy/renesas/phy-rcar-gen3-usb2.c
index 1155b111420a..79e820e2fe55 100644
--- a/drivers/phy/renesas/phy-rcar-gen3-usb2.c
+++ b/drivers/phy/renesas/phy-rcar-gen3-usb2.c
@@ -939,21 +939,6 @@ static int rcar_gen3_phy_usb2_vbus_regulator_register(struct rcar_gen3_chan *cha
return rcar_gen3_phy_usb2_vbus_regulator_get_exclusive_enable(channel, enable);
}
-/* Temporary wrapper until the multiplexer subsystem supports optional muxes */
-static inline struct mux_state *
-rcar_gen3_phy_mux_state_get_optional(struct device *dev, const char *mux_name)
-{
- if (!of_property_present(dev->of_node, "mux-states"))
- return NULL;
-
- return devm_mux_state_get(dev, mux_name);
-}
-
-static void rcar_gen3_phy_mux_state_deselect(void *data)
-{
- mux_state_deselect(data);
-}
-
static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -1036,20 +1021,9 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev)
phy_set_drvdata(channel->rphys[i].phy, &channel->rphys[i]);
}
- mux_state = rcar_gen3_phy_mux_state_get_optional(dev, NULL);
+ mux_state = devm_mux_state_get_optional_selected(dev, NULL);
if (IS_ERR(mux_state))
- return PTR_ERR(mux_state);
- if (mux_state) {
- ret = mux_state_select(mux_state);
- if (ret)
- return dev_err_probe(dev, ret, "Failed to select USB mux\n");
-
- ret = devm_add_action_or_reset(dev, rcar_gen3_phy_mux_state_deselect,
- mux_state);
- if (ret)
- return dev_err_probe(dev, ret,
- "Failed to register USB mux state deselect\n");
- }
+ return dev_err_probe(dev, PTR_ERR(mux_state), "Failed to get USB mux\n");
if (channel->phy_data->no_adp_ctrl && channel->is_otg_channel) {
ret = rcar_gen3_phy_usb2_vbus_regulator_register(channel);
--
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 v11 6/9] i2c: omap: switch to new generic helper for getting selected mux-state
From: Josua Mayer @ 2026-02-26 13:21 UTC (permalink / raw)
To: 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, Wolfram Sang,
Yoshihiro Shimoda
Cc: Yazan Shhady, Jon Nettleton, Vladimir Oltean, Mikhail Anikin,
linux-can, linux-phy, linux-kernel, linux-omap, linux-i2c,
linux-mmc, devicetree, linux-renesas-soc, Josua Mayer
In-Reply-To: <20260226-rz-sdio-mux-v11-0-c2a350f9bbd3@solid-run.com>
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>
Reviewed-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
Signed-off-by: Josua Mayer <josua@solid-run.com>
---
drivers/i2c/busses/i2c-omap.c | 24 +++++-------------------
1 file changed, 5 insertions(+), 19 deletions(-)
diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c
index d9f590f0c384..f02d294db42a 100644
--- a/drivers/i2c/busses/i2c-omap.c
+++ b/drivers/i2c/busses/i2c-omap.c
@@ -1453,27 +1453,16 @@ omap_i2c_probe(struct platform_device *pdev)
(1000 * omap->speed / 8);
}
- if (of_property_present(node, "mux-states")) {
- struct mux_state *mux_state;
-
- mux_state = devm_mux_state_get(&pdev->dev, NULL);
- if (IS_ERR(mux_state)) {
- r = PTR_ERR(mux_state);
- dev_dbg(&pdev->dev, "failed to get I2C mux: %d\n", r);
- goto err_put_pm;
- }
- omap->mux_state = mux_state;
- r = mux_state_select(omap->mux_state);
- if (r) {
- dev_err(&pdev->dev, "failed to select I2C mux: %d\n", r);
- goto err_put_pm;
- }
+ omap->mux_state = devm_mux_state_get_optional_selected(&pdev->dev, NULL);
+ if (IS_ERR(omap->mux_state)) {
+ r = PTR_ERR(omap->mux_state);
+ goto err_put_pm;
}
/* reset ASAP, clearing any IRQs */
r = omap_i2c_init(omap);
if (r)
- goto err_mux_state_deselect;
+ goto err_put_pm;
if (omap->rev < OMAP_I2C_OMAP1_REV_2)
r = devm_request_irq(&pdev->dev, omap->irq, omap_i2c_omap1_isr,
@@ -1515,9 +1504,6 @@ omap_i2c_probe(struct platform_device *pdev)
err_unuse_clocks:
omap_i2c_write_reg(omap, OMAP_I2C_CON_REG, 0);
-err_mux_state_deselect:
- if (omap->mux_state)
- mux_state_deselect(omap->mux_state);
err_put_pm:
pm_runtime_put_sync(omap->dev);
err_disable_pm:
--
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 v11 3/9] mux: Add helper functions for getting optional and selected mux-state
From: Josua Mayer @ 2026-02-26 13:21 UTC (permalink / raw)
To: 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, Wolfram Sang,
Yoshihiro Shimoda
Cc: Yazan Shhady, Jon Nettleton, Vladimir Oltean, Mikhail Anikin,
linux-can, linux-phy, linux-kernel, linux-omap, linux-i2c,
linux-mmc, devicetree, linux-renesas-soc, Josua Mayer
In-Reply-To: <20260226-rz-sdio-mux-v11-0-c2a350f9bbd3@solid-run.com>
In-tree phy-can-transceiver and phy_rcar_gen3_usb2 have already
implemented local versions 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.
Acked-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
Signed-off-by: Josua Mayer <josua@solid-run.com>
---
drivers/mux/core.c | 194 +++++++++++++++++++++++++++++++++++++------
include/linux/mux/consumer.h | 108 +++++++++++++++++++++++-
2 files changed, 271 insertions(+), 31 deletions(-)
diff --git a/drivers/mux/core.c b/drivers/mux/core.c
index f09ee8782e3d..23538de2c91b 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,29 @@ 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: Pointer to the mux-control on success, an ERR_PTR with a negative
+ * errno on error, or NULL if mux doesn't exist.
+ */
+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.
@@ -670,14 +707,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;
@@ -685,12 +724,15 @@ static struct mux_state *mux_state_get(struct device *dev, const char *mux_name)
if (!mstate)
return ERR_PTR(-ENOMEM);
- mstate->mux = mux_get(dev, mux_name, &mstate->state);
+ mstate->mux = mux_get(dev, mux_name, &mstate->state, optional);
if (IS_ERR(mstate->mux)) {
int err = PTR_ERR(mstate->mux);
kfree(mstate);
return ERR_PTR(err);
+ } else if (!mstate->mux) {
+ kfree(mstate);
+ return optional ? NULL : ERR_PTR(-ENOENT);
}
return mstate;
@@ -710,9 +752,66 @@ static void mux_state_put(struct mux_state *mstate)
static void devm_mux_state_release(struct device *dev, void *res)
{
- struct mux_state *mstate = *(struct mux_state **)res;
+ struct devm_mux_state_state *devm_state = res;
+
+ if (devm_state->exit)
+ devm_state->exit(devm_state->mstate);
+
+ mux_state_put(devm_state->mstate);
+}
+
+/**
+ * __devm_mux_state_get() - Get the optional mux-state for a device,
+ * with resource management.
+ * @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.
+ * @init: Optional function pointer for mux-state object initialisation.
+ * @exit: Optional function pointer for mux-state object cleanup on release.
+ *
+ * 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 *__devm_mux_state_get(struct device *dev, const char *mux_name,
+ bool optional,
+ int (*init)(struct mux_state *mstate),
+ int (*exit)(struct mux_state *mstate))
+{
+ struct devm_mux_state_state *devm_state;
+ struct mux_state *mstate;
+ int ret;
+
+ mstate = mux_state_get(dev, mux_name, optional);
+ if (IS_ERR(mstate))
+ return ERR_CAST(mstate);
+ else if (optional && !mstate)
+ return NULL;
+ else if (!mstate)
+ return ERR_PTR(-ENOENT);
+
+ devm_state = devres_alloc(devm_mux_state_release, sizeof(*devm_state), GFP_KERNEL);
+ if (!devm_state) {
+ ret = -ENOMEM;
+ goto err_devres_alloc;
+ }
+
+ if (init) {
+ ret = init(mstate);
+ if (ret)
+ goto err_mux_state_init;
+ }
+
+ devm_state->mstate = mstate;
+ devm_state->exit = exit;
+ devres_add(dev, devm_state);
+ return mstate;
+
+err_mux_state_init:
+ devres_free(devm_state);
+err_devres_alloc:
mux_state_put(mstate);
+ return ERR_PTR(ret);
}
/**
@@ -722,28 +821,69 @@ static void devm_mux_state_release(struct device *dev, void *res)
* @mux_name: The name identifying the mux-control.
*
* Return: Pointer to the mux-state, or an ERR_PTR with a negative errno.
+ *
+ * The mux-state will automatically be freed on release.
*/
-struct mux_state *devm_mux_state_get(struct device *dev,
- const char *mux_name)
+struct mux_state *devm_mux_state_get(struct device *dev, const char *mux_name)
{
- struct mux_state **ptr, *mstate;
-
- ptr = devres_alloc(devm_mux_state_release, sizeof(*ptr), GFP_KERNEL);
- if (!ptr)
- return ERR_PTR(-ENOMEM);
+ return __devm_mux_state_get(dev, mux_name, false, NULL, NULL);
+}
+EXPORT_SYMBOL_GPL(devm_mux_state_get);
- mstate = mux_state_get(dev, mux_name);
- if (IS_ERR(mstate)) {
- devres_free(ptr);
- return mstate;
- }
+/**
+ * devm_mux_state_get_optional() - Get the optional mux-state for a device,
+ * with resource management.
+ * @dev: The device that needs a mux-state.
+ * @mux_name: The name identifying the mux-state.
+ *
+ * Return: Pointer to the mux-state on success, an ERR_PTR with a negative
+ * errno on error, or NULL if mux doesn't exist.
+ *
+ * The mux-state will automatically be freed on release.
+ */
+struct mux_state *devm_mux_state_get_optional(struct device *dev, const char *mux_name)
+{
+ return __devm_mux_state_get(dev, mux_name, true, NULL, NULL);
+}
+EXPORT_SYMBOL_GPL(devm_mux_state_get_optional);
- *ptr = mstate;
- devres_add(dev, ptr);
+/**
+ * devm_mux_state_get_selected() - Get the mux-state for a device, with
+ * resource management.
+ * @dev: The device that needs a mux-state.
+ * @mux_name: The name identifying the mux-state.
+ *
+ * Return: Pointer to the mux-state, or an ERR_PTR with a negative errno.
+ *
+ * The returned mux-state (if valid) is already selected.
+ *
+ * The mux-state will automatically be deselected and freed on release.
+ */
+struct mux_state *devm_mux_state_get_selected(struct device *dev, const char *mux_name)
+{
+ return __devm_mux_state_get(dev, mux_name, false, mux_state_select, mux_state_deselect);
+}
+EXPORT_SYMBOL_GPL(devm_mux_state_get_selected);
- return mstate;
+/**
+ * devm_mux_state_get_optional_selected() - Get the optional mux-state for
+ * a device, with resource management.
+ * @dev: The device that needs a mux-state.
+ * @mux_name: The name identifying the mux-state.
+ *
+ * Return: Pointer to the mux-state on success, an ERR_PTR with a negative
+ * errno on error, or NULL if mux doesn't exist.
+ *
+ * The returned mux-state (if valid) is already selected.
+ *
+ * The mux-state will automatically be deselected and freed on release.
+ */
+struct mux_state *devm_mux_state_get_optional_selected(struct device *dev,
+ const char *mux_name)
+{
+ return __devm_mux_state_get(dev, mux_name, true, mux_state_select, mux_state_deselect);
}
-EXPORT_SYMBOL_GPL(devm_mux_state_get);
+EXPORT_SYMBOL_GPL(devm_mux_state_get_optional_selected);
/*
* Using subsys_initcall instead of module_init here to try to ensure - for
diff --git a/include/linux/mux/consumer.h b/include/linux/mux/consumer.h
index 2e25c838f831..a961861a503b 100644
--- a/include/linux/mux/consumer.h
+++ b/include/linux/mux/consumer.h
@@ -16,6 +16,8 @@ struct device;
struct mux_control;
struct mux_state;
+#if IS_ENABLED(CONFIG_MULTIPLEXER)
+
unsigned int mux_control_states(struct mux_control *mux);
int __must_check mux_control_select_delay(struct mux_control *mux,
unsigned int state,
@@ -54,11 +56,109 @@ int mux_control_deselect(struct mux_control *mux);
int mux_state_deselect(struct mux_state *mstate);
struct mux_control *mux_control_get(struct device *dev, const char *mux_name);
+struct mux_control *mux_control_get_optional(struct device *dev, const char *mux_name);
void mux_control_put(struct mux_control *mux);
-struct mux_control *devm_mux_control_get(struct device *dev,
- const char *mux_name);
-struct mux_state *devm_mux_state_get(struct device *dev,
- const char *mux_name);
+struct mux_control *devm_mux_control_get(struct device *dev, const char *mux_name);
+struct mux_state *devm_mux_state_get(struct device *dev, const char *mux_name);
+struct mux_state *devm_mux_state_get_optional(struct device *dev, const char *mux_name);
+struct mux_state *devm_mux_state_get_selected(struct device *dev, const char *mux_name);
+struct mux_state *devm_mux_state_get_optional_selected(struct device *dev, const char *mux_name);
+
+#else
+
+static inline unsigned int mux_control_states(struct mux_control *mux)
+{
+ return 0;
+}
+static inline int __must_check mux_control_select_delay(struct mux_control *mux,
+ unsigned int state, unsigned int delay_us)
+{
+ return -EOPNOTSUPP;
+}
+static inline int __must_check mux_state_select_delay(struct mux_state *mstate,
+ unsigned int delay_us)
+{
+ return -EOPNOTSUPP;
+}
+static inline int __must_check mux_control_try_select_delay(struct mux_control *mux,
+ unsigned int state,
+ unsigned int delay_us)
+{
+ return -EOPNOTSUPP;
+}
+static inline int __must_check mux_state_try_select_delay(struct mux_state *mstate,
+ unsigned int delay_us)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int __must_check mux_control_select(struct mux_control *mux,
+ unsigned int state)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int __must_check mux_state_select(struct mux_state *mstate)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int __must_check mux_control_try_select(struct mux_control *mux,
+ unsigned int state)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int __must_check mux_state_try_select(struct mux_state *mstate)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int mux_control_deselect(struct mux_control *mux)
+{
+ return -EOPNOTSUPP;
+}
+static inline int mux_state_deselect(struct mux_state *mstate)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline struct mux_control *mux_control_get(struct device *dev, const char *mux_name)
+{
+ return ERR_PTR(-EOPNOTSUPP);
+}
+static inline struct mux_control *mux_control_get_optional(struct device *dev,
+ const char *mux_name)
+{
+ return NULL;
+}
+static inline void mux_control_put(struct mux_control *mux) {}
+
+static inline struct mux_control *devm_mux_control_get(struct device *dev, const char *mux_name)
+{
+ return ERR_PTR(-EOPNOTSUPP);
+}
+static inline struct mux_state *devm_mux_state_get(struct device *dev, const char *mux_name)
+{
+ return ERR_PTR(-EOPNOTSUPP);
+}
+static inline struct mux_state *devm_mux_state_get_optional(struct device *dev,
+ const char *mux_name)
+{
+ return NULL;
+}
+static inline struct mux_state *devm_mux_state_get_selected(struct device *dev,
+ const char *mux_name)
+{
+ return ERR_PTR(-EOPNOTSUPP);
+}
+static inline struct mux_state *devm_mux_state_get_optional_selected(struct device *dev,
+ const char *mux_name)
+{
+ return NULL;
+}
+
+#endif /* CONFIG_MULTIPLEXER */
#endif /* _LINUX_MUX_CONSUMER_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 v11 7/9] dt-bindings: mmc: renesas,sdhi: Add mux-states property
From: Josua Mayer @ 2026-02-26 13:21 UTC (permalink / raw)
To: 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, Wolfram Sang,
Yoshihiro Shimoda
Cc: Yazan Shhady, Jon Nettleton, Vladimir Oltean, Mikhail Anikin,
linux-can, linux-phy, linux-kernel, linux-omap, linux-i2c,
linux-mmc, devicetree, linux-renesas-soc, Josua Mayer
In-Reply-To: <20260226-rz-sdio-mux-v11-0-c2a350f9bbd3@solid-run.com>
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>
Reviewed-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
Signed-off-by: Josua Mayer <josua@solid-run.com>
---
Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml b/Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml
index c754ea71f51f..64fac0d11329 100644
--- a/Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml
+++ b/Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml
@@ -106,6 +106,11 @@ properties:
iommus:
maxItems: 1
+ mux-states:
+ description:
+ mux controller node to route the SD/SDIO/eMMC signals from SoC to cards.
+ maxItems: 1
+
power-domains:
maxItems: 1
@@ -275,6 +280,7 @@ examples:
max-frequency = <195000000>;
power-domains = <&sysc R8A7790_PD_ALWAYS_ON>;
resets = <&cpg 314>;
+ mux-states = <&mux 0>;
};
sdhi1: mmc@ee120000 {
--
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 v11 9/9] mmc: host: renesas_sdhi_core: support selecting an optional mux
From: Josua Mayer @ 2026-02-26 13:21 UTC (permalink / raw)
To: 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, Wolfram Sang,
Yoshihiro Shimoda
Cc: Yazan Shhady, Jon Nettleton, Vladimir Oltean, Mikhail Anikin,
linux-can, linux-phy, linux-kernel, linux-omap, linux-i2c,
linux-mmc, devicetree, linux-renesas-soc, Josua Mayer
In-Reply-To: <20260226-rz-sdio-mux-v11-0-c2a350f9bbd3@solid-run.com>
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.
Signed-off-by: Josua Mayer <josua@solid-run.com>
---
drivers/mmc/host/renesas_sdhi_core.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/drivers/mmc/host/renesas_sdhi_core.c b/drivers/mmc/host/renesas_sdhi_core.c
index 2a310a145785..f9ec78d699f4 100644
--- a/drivers/mmc/host/renesas_sdhi_core.c
+++ b/drivers/mmc/host/renesas_sdhi_core.c
@@ -26,6 +26,7 @@
#include <linux/mmc/mmc.h>
#include <linux/mmc/slot-gpio.h>
#include <linux/module.h>
+#include <linux/mux/consumer.h>
#include <linux/pinctrl/consumer.h>
#include <linux/pinctrl/pinctrl-state.h>
#include <linux/platform_data/tmio.h>
@@ -1062,6 +1063,7 @@ int renesas_sdhi_probe(struct platform_device *pdev,
struct regulator_dev *rdev;
struct renesas_sdhi_dma *dma_priv;
struct device *dev = &pdev->dev;
+ struct mux_state *mux_state;
struct tmio_mmc_host *host;
struct renesas_sdhi *priv;
int num_irqs, irq, ret, i;
@@ -1116,6 +1118,10 @@ int renesas_sdhi_probe(struct platform_device *pdev,
"state_uhs");
}
+ mux_state = devm_mux_state_get_optional_selected(&pdev->dev, NULL);
+ if (IS_ERR(mux_state))
+ return PTR_ERR(mux_state);
+
host = tmio_mmc_host_alloc(pdev, mmc_data);
if (IS_ERR(host))
return PTR_ERR(host);
--
2.43.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* Re: [PATCH v3 net-next 05/10] phy: add phy_get_rx_polarity() and phy_get_tx_polarity()
From: Geert Uytterhoeven @ 2026-02-26 13:22 UTC (permalink / raw)
To: Vladimir Oltean
Cc: netdev, devicetree, linux-phy, linux-kernel, linux-arm-kernel,
linux-mediatek, Daniel Golle, Horatiu Vultur, Bjørn Mork,
Andrew Lunn, Heiner Kallweit, Russell King, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Vinod Koul, Neil Armstrong,
Matthias Brugger, AngeloGioacchino Del Regno, Eric Woudstra,
Marek Behún, Lee Jones, Patrice Chotard,
open list:KERNEL SELFTEST FRAMEWORK, KUnit Development
In-Reply-To: <20260111093940.975359-6-vladimir.oltean@nxp.com>
Hi Vladimir,
CC kunit
On Sun, 11 Jan 2026 at 10:44, Vladimir Oltean <vladimir.oltean@nxp.com> wrote:
> Add helpers in the generic PHY folder which can be used using 'select
> GENERIC_PHY_COMMON_PROPS' from Kconfig, without otherwise needing to
> enable GENERIC_PHY.
>
> These helpers need to deal with the slight messiness of the fact that
> the polarity properties are arrays per protocol, and with the fact that
> there is no default value mandated by the standard properties, all
> default values depend on driver and protocol (PHY_POL_NORMAL may be a
> good default for SGMII, whereas PHY_POL_AUTO may be a good default for
> PCIe).
>
> Push the supported mask of polarities to these helpers, to simplify
> drivers such that they don't need to validate what's in the device tree
> (or other firmware description).
>
> Add a KUnit test suite to make sure that the API produces the expected
> results. The fact that we use fwnode structures means we can validate
> with software nodes, and as opposed to the device_property API, we can
> bypass the need to have a device structure.
>
> Co-developed-by: Bjørn Mork <bjorn@mork.no>
> Signed-off-by: Bjørn Mork <bjorn@mork.no>
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Thanks for your patch, which is now commit e7556b59ba651796
("phy: add phy_get_rx_polarity() and phy_get_tx_polarity()") in
v7.0-rc1.
> --- a/drivers/phy/Kconfig
> +++ b/drivers/phy/Kconfig
> @@ -5,6 +5,28 @@
>
> menu "PHY Subsystem"
>
> +config PHY_COMMON_PROPS
> + bool
> + help
> + This parses properties common between generic PHYs and Ethernet PHYs.
> +
> + Select this from consumer drivers to gain access to helpers for
> + parsing properties from the
> + Documentation/devicetree/bindings/phy/phy-common-props.yaml schema.
> +
> +config PHY_COMMON_PROPS_TEST
> + tristate "KUnit tests for PHY common props" if !KUNIT_ALL_TESTS
> + select PHY_COMMON_PROPS
This select means that enabling KUNIT_ALL_TESTS also enables extra
functionality, which may not be desirable in a production system.
As PHY_COMMON_PROPS is bool, this extra functionality is even part of
the base kernel if KUNIT_ALL_TESTS=m. Unfortunately PHY_COMMON_PROPS is
invisible, so this cannot just be changed from "select" to "depends on".
But perhaps PHY_COMMON_PROPS can be made visible if KUNIT_ALL_TESTS,
so the select can be turned into a dependency?
> + depends on KUNIT
> + default KUNIT_ALL_TESTS
> + help
> + This builds KUnit tests for the PHY common property API.
> +
> + For more information on KUnit and unit tests in general,
> + please refer to the KUnit documentation in Documentation/dev-tools/kunit/.
> +
> + When in doubt, say N.
> +
> config GENERIC_PHY
> bool "PHY Core"
> help
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
* [PATCH v11 8/9] mux: add visible config symbol to enable multiplexer subsystem
From: Josua Mayer @ 2026-02-26 13:21 UTC (permalink / raw)
To: 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, Wolfram Sang,
Yoshihiro Shimoda
Cc: Yazan Shhady, Jon Nettleton, Vladimir Oltean, Mikhail Anikin,
linux-can, linux-phy, linux-kernel, linux-omap, linux-i2c,
linux-mmc, devicetree, linux-renesas-soc, Josua Mayer
In-Reply-To: <20260226-rz-sdio-mux-v11-0-c2a350f9bbd3@solid-run.com>
The multiplexer subsystem was initially designed to be completely
hidden, relying on consumers to "select MULTIPLEXER" explicitly.
Drivers implementing multiplexers depend on this hidden symbol.
This prevents users from manually enabling both the mux core and any of
the multiplexer drivers.
All multiplexer drivers in drivers/mux/ can operate standalone without a
consumer. This is particularly useful in a device-tree, where a default
state can be set through the idle-state property.
Over time, several drivers have added "select MULTIPLEXER" dependencies,
some of which require a mux and some consider it optional. v7.0-rc1
shows 15 such occurrences in Kconfig files, in a variety of subsystems.
The natural step forward to allow enabling mux core and drivers would be
adding a prompt and help text to the existing symbol.
This violates the general Kbuild advice to avoid selecting visible
symbols for all existing consumers of the mux core.
Add the new config symbol MUX_CORE with a prompt and help text as a
wrapper for users to enable manually. This avoids existing consumers
automatically selecting a visible symbol.
Change the MULTIPLEXER symbol from tristate to bool. This avoids complex
dependencies if users were to attempt a configuration where the mux is a
module but one of its consumers is built-in, as well as difficulties
keeping the state of visible and invisible symbols in sync.
Further convert the "menu ... depends on ..." structure to "if ... menu
... endmenu endif". These are functionally equivalent, but the new
structure is more efficient and can support future source statements
within the conditional block.
Signed-off-by: Josua Mayer <josua@solid-run.com>
---
drivers/mux/Kconfig | 17 +++++++++++++++--
1 file changed, 15 insertions(+), 2 deletions(-)
diff --git a/drivers/mux/Kconfig b/drivers/mux/Kconfig
index c68132e38138..6d17dfa25dad 100644
--- a/drivers/mux/Kconfig
+++ b/drivers/mux/Kconfig
@@ -4,10 +4,21 @@
#
config MULTIPLEXER
- tristate
+ bool
+
+config MUX_CORE
+ bool "Generic Multiplexer Support"
+ select MULTIPLEXER
+ help
+ This framework is designed to abstract multiplexer handling for
+ devices via various GPIO-, MMIO/Regmap or specific multiplexer
+ controller chips.
+
+ If unsure, say no.
+
+if MULTIPLEXER
menu "Multiplexer drivers"
- depends on MULTIPLEXER
config MUX_ADG792A
tristate "Analog Devices ADG792A/ADG792G Multiplexers"
@@ -60,3 +71,5 @@ config MUX_MMIO
be called mux-mmio.
endmenu
+
+endif # MULTIPLEXER
--
2.43.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* Re: [PATCH v3 net-next 05/10] phy: add phy_get_rx_polarity() and phy_get_tx_polarity()
From: Vladimir Oltean @ 2026-02-26 15:10 UTC (permalink / raw)
To: Geert Uytterhoeven
Cc: netdev, devicetree, linux-phy, linux-kernel, linux-arm-kernel,
linux-mediatek, Daniel Golle, Horatiu Vultur, Bjørn Mork,
Andrew Lunn, Heiner Kallweit, Russell King, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Vinod Koul, Neil Armstrong,
Matthias Brugger, AngeloGioacchino Del Regno, Eric Woudstra,
Marek Behún, Lee Jones, Patrice Chotard,
open list:KERNEL SELFTEST FRAMEWORK, KUnit Development
In-Reply-To: <CAMuHMdUBaoYKNj52gn8DQeZFZ42Cvm6xT6fvo0-_twNv1k3Jhg@mail.gmail.com>
Hi Geert,
On Thu, Feb 26, 2026 at 02:22:29PM +0100, Geert Uytterhoeven wrote:
> > +config PHY_COMMON_PROPS
> > + bool
> > + help
> > + This parses properties common between generic PHYs and Ethernet PHYs.
> > +
> > + Select this from consumer drivers to gain access to helpers for
> > + parsing properties from the
> > + Documentation/devicetree/bindings/phy/phy-common-props.yaml schema.
> > +
> > +config PHY_COMMON_PROPS_TEST
> > + tristate "KUnit tests for PHY common props" if !KUNIT_ALL_TESTS
> > + select PHY_COMMON_PROPS
>
> This select means that enabling KUNIT_ALL_TESTS also enables extra
> functionality, which may not be desirable in a production system.
> As PHY_COMMON_PROPS is bool, this extra functionality is even part of
> the base kernel if KUNIT_ALL_TESTS=m. Unfortunately PHY_COMMON_PROPS is
> invisible, so this cannot just be changed from "select" to "depends on".
> But perhaps PHY_COMMON_PROPS can be made visible if KUNIT_ALL_TESTS,
> so the select can be turned into a dependency?
Is this what you're asking for?
-- >8 --
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index 02467dfd4fb0..1875d5b784f6 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -6,7 +6,7 @@
menu "PHY Subsystem"
config PHY_COMMON_PROPS
- bool
+ bool "PHY common properties" if KUNIT_ALL_TESTS
help
This parses properties common between generic PHYs and Ethernet PHYs.
@@ -16,8 +16,7 @@ config PHY_COMMON_PROPS
config PHY_COMMON_PROPS_TEST
tristate "KUnit tests for PHY common props" if !KUNIT_ALL_TESTS
- select PHY_COMMON_PROPS
- depends on KUNIT
+ depends on KUNIT && PHY_COMMON_PROPS
default KUNIT_ALL_TESTS
help
This builds KUnit tests for the PHY common property API.
-- >8 --
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
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