* [PATCH v3 1/5] phy: fsl-imx8mq-usb: fix typec switch leak on probe error path
From: Xu Yang @ 2026-06-03 5:37 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Frank Li, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Jun Li
Cc: linux-phy, imx, linux-arm-kernel, linux-kernel, Felix Gu, stable,
Xu Yang
In-Reply-To: <20260603-imx8mp-usb-phy-improvement-v3-0-7afb8f89abc6@nxp.com>
From: Felix Gu <ustc.gu@gmail.com>
If probe fails after imx95_usb_phy_get_tca() succeeds, the typec
switch leaks because the only cleanup path was in .remove, which
never runs on probe failure.
Use devm_add_action_or_reset() so the switch is cleaned up on both
probe failure and driver removal. The .remove callback and
imx95_usb_phy_put_tca() are no longer needed.
Fixes: b58f0f86fd61 ("phy: fsl-imx8mq-usb: add tca function driver for imx95")
Cc: stable@vger.kernel.org
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Reviewed-by: Xu Yang <xu.yang_2@nxp.com>
Signed-off-by: Felix Gu <ustc.gu@gmail.com>
---
Changes in v3:
- add R-b tag
- cc statble
- drop "sw = data" conversion
---
drivers/phy/freescale/phy-fsl-imx8mq-usb.c | 27 +++++++--------------------
1 file changed, 7 insertions(+), 20 deletions(-)
diff --git a/drivers/phy/freescale/phy-fsl-imx8mq-usb.c b/drivers/phy/freescale/phy-fsl-imx8mq-usb.c
index b05d80e849a1..88b804b2c982 100644
--- a/drivers/phy/freescale/phy-fsl-imx8mq-usb.c
+++ b/drivers/phy/freescale/phy-fsl-imx8mq-usb.c
@@ -173,9 +173,9 @@ static struct typec_switch_dev *tca_blk_get_typec_switch(struct platform_device
return sw;
}
-static void tca_blk_put_typec_switch(struct typec_switch_dev *sw)
+static void tca_blk_put_typec_switch(void *data)
{
- typec_switch_unregister(sw);
+ typec_switch_unregister(data);
}
static void tca_blk_orientation_set(struct tca_blk *tca,
@@ -248,6 +248,7 @@ static struct tca_blk *imx95_usb_phy_get_tca(struct platform_device *pdev,
struct device *dev = &pdev->dev;
struct resource *res;
struct tca_blk *tca;
+ int ret;
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
if (!res)
@@ -266,17 +267,11 @@ static struct tca_blk *imx95_usb_phy_get_tca(struct platform_device *pdev,
tca->orientation = TYPEC_ORIENTATION_NORMAL;
tca->sw = tca_blk_get_typec_switch(pdev, imx_phy);
- return tca;
-}
-
-static void imx95_usb_phy_put_tca(struct imx8mq_usb_phy *imx_phy)
-{
- struct tca_blk *tca = imx_phy->tca;
-
- if (!tca)
- return;
+ ret = devm_add_action_or_reset(&pdev->dev, tca_blk_put_typec_switch, tca->sw);
+ if (ret)
+ return ERR_PTR(ret);
- tca_blk_put_typec_switch(tca->sw);
+ return tca;
}
static u32 phy_tx_vref_tune_from_property(u32 percent)
@@ -739,16 +734,8 @@ static int imx8mq_usb_phy_probe(struct platform_device *pdev)
return PTR_ERR_OR_ZERO(phy_provider);
}
-static void imx8mq_usb_phy_remove(struct platform_device *pdev)
-{
- struct imx8mq_usb_phy *imx_phy = platform_get_drvdata(pdev);
-
- imx95_usb_phy_put_tca(imx_phy);
-}
-
static struct platform_driver imx8mq_usb_phy_driver = {
.probe = imx8mq_usb_phy_probe,
- .remove = imx8mq_usb_phy_remove,
.driver = {
.name = "imx8mq-usb-phy",
.of_match_table = imx8mq_usb_phy_of_match,
--
2.34.1
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* [PATCH v3 0/5] phy: fsl-imx8mq-usb: few improvements
From: Xu Yang @ 2026-06-03 5:37 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Frank Li, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Jun Li
Cc: linux-phy, imx, linux-arm-kernel, linux-kernel, Felix Gu, stable,
Xu Yang
This patchset is a continuous of v2, it mainly resolves some concerns
reported by sashiko-bot.
Patch #1 fix Type-C switch resource leak if probe() fails.
Patch #3 add runtime PM support to avoid register access issue if the
USB controller enters into runtime suspended state, in this state
accessing USB PHY register may lack some resources. This will also
avoid regulator leak if power_on() fails.
Patch #4 add debug control register regmap
Patch #5 correct i.MX8MP USB runtime wakeup issue after introduce runtime
PM support.
Link to v2:
- https://lore.kernel.org/linux-phy/20260512101046.1498096-1-xu.yang_2@nxp.com/
- https://lore.kernel.org/linux-phy/20260512101212.1498223-1-xu.yang_2@nxp.com/
---
Felix Gu (1):
phy: fsl-imx8mq-usb: fix typec switch leak on probe error path
Xu Yang (4):
phy: fsl-imx8mq-usb: set usb phy to be wakeup capable
phy: fsl-imx8mq-usb: add runtime PM support
phy: fsl-imx8mq-usb: add control register regmap
phy: fsl-imx8mq-usb: keep PHY power domain runtime always-on for i.MX8MP
drivers/phy/freescale/phy-fsl-imx8mq-usb.c | 125 ++++++++++++++++++++---------
1 file changed, 88 insertions(+), 37 deletions(-)
---
base-commit: 08484c504b55a98bd100527fbe10a3caf55ff3ff
change-id: 20260602-imx8mp-usb-phy-improvement-4272d308d862
Best regards,
--
Xu Yang <xu.yang_2@nxp.com>
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH] phy: fsl-imx8mq-usb: fix typec switch leak on probe error path
From: Xu Yang @ 2026-06-03 3:09 UTC (permalink / raw)
To: Felix Gu
Cc: Vinod Koul, Neil Armstrong, Frank Li, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Xu Yang, Jun Li,
linux-phy, imx, linux-arm-kernel, linux-kernel
In-Reply-To: <20260602-imx8mq-typec-v1-1-3debe9bc77d9@gmail.com>
On Tue, Jun 02, 2026 at 10:12:41PM +0800, Felix Gu wrote:
> If probe fails after imx95_usb_phy_get_tca() succeeds, the typec
> switch leaks because the only cleanup path was in .remove, which
> never runs on probe failure.
>
> Use devm_add_action_or_reset() so the switch is cleaned up on both
> probe failure and driver removal. The .remove callback and
> imx95_usb_phy_put_tca() are no longer needed.
>
> Fixes: b58f0f86fd61 ("phy: fsl-imx8mq-usb: add tca function driver for imx95")
> Signed-off-by: Felix Gu <ustc.gu@gmail.com>
> ---
> drivers/phy/freescale/phy-fsl-imx8mq-usb.c | 27 ++++++++-------------------
> 1 file changed, 8 insertions(+), 19 deletions(-)
>
> diff --git a/drivers/phy/freescale/phy-fsl-imx8mq-usb.c b/drivers/phy/freescale/phy-fsl-imx8mq-usb.c
> index b05d80e849a1..8af5a4f85698 100644
> --- a/drivers/phy/freescale/phy-fsl-imx8mq-usb.c
> +++ b/drivers/phy/freescale/phy-fsl-imx8mq-usb.c
> @@ -173,8 +173,10 @@ static struct typec_switch_dev *tca_blk_get_typec_switch(struct platform_device
> return sw;
> }
>
> -static void tca_blk_put_typec_switch(struct typec_switch_dev *sw)
> +static void tca_blk_put_typec_switch(void *data)
> {
> + struct typec_switch_dev *sw = data;
> +
> typec_switch_unregister(sw);
> }
Nit: You can simply call typec_switch_unregister(data)
Reviewed-by: Xu Yang <xu.yang_2@nxp.com>
>
> @@ -248,6 +250,7 @@ static struct tca_blk *imx95_usb_phy_get_tca(struct platform_device *pdev,
> struct device *dev = &pdev->dev;
> struct resource *res;
> struct tca_blk *tca;
> + int ret;
>
> res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> if (!res)
> @@ -266,17 +269,11 @@ static struct tca_blk *imx95_usb_phy_get_tca(struct platform_device *pdev,
> tca->orientation = TYPEC_ORIENTATION_NORMAL;
> tca->sw = tca_blk_get_typec_switch(pdev, imx_phy);
>
> - return tca;
> -}
> -
> -static void imx95_usb_phy_put_tca(struct imx8mq_usb_phy *imx_phy)
> -{
> - struct tca_blk *tca = imx_phy->tca;
> -
> - if (!tca)
> - return;
> + ret = devm_add_action_or_reset(&pdev->dev, tca_blk_put_typec_switch, tca->sw);
> + if (ret)
> + return ERR_PTR(ret);
>
> - tca_blk_put_typec_switch(tca->sw);
> + return tca;
> }
>
> static u32 phy_tx_vref_tune_from_property(u32 percent)
> @@ -739,16 +736,8 @@ static int imx8mq_usb_phy_probe(struct platform_device *pdev)
> return PTR_ERR_OR_ZERO(phy_provider);
> }
>
> -static void imx8mq_usb_phy_remove(struct platform_device *pdev)
> -{
> - struct imx8mq_usb_phy *imx_phy = platform_get_drvdata(pdev);
> -
> - imx95_usb_phy_put_tca(imx_phy);
> -}
> -
> static struct platform_driver imx8mq_usb_phy_driver = {
> .probe = imx8mq_usb_phy_probe,
> - .remove = imx8mq_usb_phy_remove,
> .driver = {
> .name = "imx8mq-usb-phy",
> .of_match_table = imx8mq_usb_phy_of_match,
>
> ---
> base-commit: 08484c504b55a98bd100527fbe10a3caf55ff3ff
> change-id: 20260602-imx8mq-typec-20a6f1d1900f
>
> Best regards,
> --
> Felix Gu <ustc.gu@gmail.com>
>
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH v8 1/2] dt-bindings: phy: qcom: Add CSI2 C-PHY/DPHY schema
From: Bryan O'Donoghue @ 2026-06-02 22:51 UTC (permalink / raw)
To: Vladimir Zapolskiy, Bryan O'Donoghue, Vinod Koul,
Kishon Vijay Abraham I, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Neil Armstrong
Cc: linux-arm-msm, linux-phy, linux-media, devicetree, linux-kernel
In-Reply-To: <dda32577-04e0-4507-acaf-a5694f4f31b3@linaro.org>
On 02/06/2026 22:59, Vladimir Zapolskiy wrote:
> On 5/23/26 05:48, Bryan O'Donoghue wrote:
>> Add a base schema initially compatible with x1e80100 to describe MIPI CSI2
>> PHY devices.
>>
>> The hardware can support both CPHY, DPHY and a special split-mode DPHY.
>>
>> The schema here defines three ports:
>>
>> port@0:
>> The first input port where a sensor is always required.
>>
>> port@1:
>> A second optional input port which if present implies DPHY split-mode.
>>
>> port@2:
>> A third always required output port which connects to the controller.
>>
>
> This port numeration is imperfect, because port@0 and port@2 are required,
> while middle port@1 is optional.
>
> Like it was stated before a number of times, it seems natural to operate
> with two ports, where input port may have two endpoints rather than 3 ports,
> also that approach solves the problem of a hole in the port numeration.
Can you confirm this is what you are after ?
port@0 {
#address-cells = <1>;
#size-cells = <0>;
endpoint@0 { /* primary sensor */
reg = <0>;
data-lanes = <0 1 2 3>;
remote-endpoint = <&sensor0_out>;
};
endpoint@1 { /* split-mode second sensor, optional */
reg = <1>;
data-lanes = <0>;
remote-endpoint = <&sensor1_out>;
};
};
port@1 { /* output to CAMSS, was port@2 */
endpoint { remote-endpoint = <&controller_in>; };
};
This works for me BTW.
>> 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 | 209 +++++++++++++++++++++
>> 1 file changed, 209 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..270375f949880
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/phy/qcom,x1e80100-csi2-phy.yaml
>> @@ -0,0 +1,209 @@
>> +# 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
>> + description:
>> + The single cell specifies the PHY operating mode.
>
> #phy-cells should be 0, because the PHY operating mode is well defined
> by 'bus-type' property of an endpoint on the sensor side, the opposite
> side of CAMSS/CSID as a CSIPHY "consumer" should not dictate the PHY type.
Rob said consumer but, I'm also not very bothered about that. bus-type
is perfectly acceptable to me.
>> +
>> + clocks:
>> + maxItems: 2
>> +
>> + clock-names:
>> + items:
>> + - const: core
>> + - const: timer
>> +
>> + interrupts:
>> + maxItems: 1
>> +
>> + operating-points-v2:
>> + maxItems: 1
>> +
>> + power-domains:
>> + items:
>> + - description: MMCX voltage rail
>> + - description: MXC or MXA voltage rail
>
> Only "qcom,x1e80100-csi2-phy" device is supported so far, unlikely it's
> the case that "MXC or MXA voltage rail" should be specified, it'd be
> just one of two or both.
Hmm. I'm not being clear here if this is your take, I will reword it to
make it clearer this generation of PHY _must_ have either
- MMCX and MXC
or
- MMCX and MXA
>> +
>> + power-domain-names:
>> + items:
>> + - const: mmcx
>> + - const: mx
>> +
>> + vdda-0p9-supply:
>> + description: Phandle to a 0.9V regulator supply to a PHY.
>> +
>> + vdda-1p2-supply:
>> + description: Phandle to 1.2V regulator supply to a PHY.
>> +
>> + ports:
>> + $ref: /schemas/graph.yaml#/properties/ports
>> +
>> + properties:
>> + port@0:
>> + $ref: /schemas/graph.yaml#/$defs/port-base
>> + description: Sensor input. Always present.
>> + unevaluatedProperties: false
>> +
>> + properties:
>> + endpoint:
>> + $ref: /schemas/media/video-interfaces.yaml#
>> + unevaluatedProperties: false
>> + properties:
>> + data-lanes:
>> + minItems: 1
>> + maxItems: 4
>> + clock-lanes:
>> + maxItems: 1
>> + remote-endpoint: true
>> + required:
>> + - data-lanes
>> + - remote-endpoint
>> +
>> + port@1:
>> + $ref: /schemas/graph.yaml#/$defs/port-base
>> + description:
>> + Second sensor input. When present, indicates DPHY split mode.
>> + unevaluatedProperties: false
>> +
>> + properties:
>> + endpoint:
>> + $ref: /schemas/media/video-interfaces.yaml#
>> + unevaluatedProperties: false
>> + properties:
>> + data-lanes:
>> + maxItems: 1
>> + clock-lanes:
>> + maxItems: 1
>> + remote-endpoint: true
>> + required:
>> + - data-lanes
>> + - clock-lanes
>> + - remote-endpoint
>
> As it's stated above, it should be converted to a single port with two
> endpoints, it'd be done in accordance to video-interfaces.yaml.
>
>> +
>> + port@2:
>> + $ref: /schemas/graph.yaml#/$defs/port-base
>> + description: Output to CAMSS controller.
>> + unevaluatedProperties: false
>> +
>> + properties:
>> + endpoint:
>> + $ref: /schemas/graph.yaml#/$defs/endpoint-base
>> + unevaluatedProperties: false
>> + properties:
>> + remote-endpoint: true
>> + required:
>> + - remote-endpoint
>> +
>> + required:
>> + - port@0
>> + - port@2
>> +
>> +required:
>> + - compatible
>> + - reg
>> + - "#phy-cells"
>> + - clocks
>> + - clock-names
>> + - interrupts
>> + - operating-points-v2
>> + - power-domains
>> + - power-domain-names
>> + - vdda-0p9-supply
>> + - vdda-1p2-supply
>> + - ports
>> +
>> +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/power/qcom,rpmhpd.h>
>> +
>> + csiphy4: csiphy@ace4000 {
>> + compatible = "qcom,x1e80100-csi2-phy";
>> + reg = <0x0ace4000 0x2000>;
>> + #phy-cells = <1>;
>> +
>> + clocks = <&camcc CAM_CC_CSIPHY0_CLK>,
>> + <&camcc CAM_CC_CSI0PHYTIMER_CLK>;
>> + clock-names = "core",
>> + "timer";
>> +
>> + operating-points-v2 = <&csiphy_opp_table>;
>> +
>> + interrupts = <GIC_SPI 477 IRQ_TYPE_EDGE_RISING>;
>> +
>> + power-domains = <&rpmhpd RPMHPD_MMCX>,
>> + <&rpmhpd RPMHPD_MX>;
>> + power-domain-names = "mmcx",
>> + "mx";
>> +
>> + vdda-0p9-supply = <&vreg_l2c_0p8>;
>> + vdda-1p2-supply = <&vreg_l1c_1p2>;
>> +
>> + ports {
>> + #address-cells = <1>;
>> + #size-cells = <0>;
>> +
>> + port@0 {
>> + reg = <0>;
>> + csiphy0_in_ep: endpoint {
>> + data-lanes = <0 1>;
>> + clock-lanes = <2>;
>> + remote-endpoint = <&sensor_out>;
>> + };
>> + };
>> +
>> + port@2 {
>> + reg = <2>;
>> + csiphy0_out_ep: endpoint {
>> + remote-endpoint = <&controller_in>;
>> + };
>> + };
>> + };
>> + };
>> +
>> + csiphy_opp_table: opp-table {
>> + compatible = "operating-points-v2";
>> +
>> + opp-300000000 {
>> + opp-hz = /bits/ 64 <300000000>;
>> + required-opps = <&rpmhpd_opp_low_svs_d1>,
>> + <&rpmhpd_opp_low_svs_d1>;
>> + };
>> +
>> + opp-400000000 {
>> + opp-hz = /bits/ 64 <400000000>;
>> + required-opps = <&rpmhpd_opp_low_svs>,
>> + <&rpmhpd_opp_low_svs_d1>;
>> + };
>> +
>> + opp-480000000 {
>> + opp-hz = /bits/ 64 <480000000>;
>> + required-opps = <&rpmhpd_opp_low_svs>,
>> + <&rpmhpd_opp_low_svs_d1>;
>> + };
>> + };
>>
>
> --
> Best wishes,
> Vladimir
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH v8 2/2] phy: qcom-mipi-csi2: Add a CSI2 MIPI DPHY driver
From: Bryan O'Donoghue @ 2026-06-02 22:22 UTC (permalink / raw)
To: Vladimir Zapolskiy, Vinod Koul, Kishon Vijay Abraham I,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Neil Armstrong
Cc: Bryan O'Donoghue, linux-arm-msm, linux-phy, linux-media,
devicetree, linux-kernel
In-Reply-To: <54904b61-222d-4600-ad4c-c03a9952d337@linaro.org>
On 02/06/2026 23:07, Vladimir Zapolskiy wrote:
>> + ret = fwnode_property_read_u32(ep, "clock-lanes", &clock_lane);
>> + if (ret) {
>> + clock_lane = CSI2_DEFAULT_CLK_LN;
>> + dev_info(dev, "Using default clock-lane %d\n",
>> + CSI2_DEFAULT_CLK_LN);
>
> Why CSI2_DEFAULT_CLK_LN is set to 7, what does it mean and how is it used?
>
> Since "7" is a meaningless number in the context, I believe it's
> practically
> not used at all, and if so, 'clock-lanes' property should be just removed.
Documentation shows clock lane at lane 7.
Truthfully it makes no sense that the clock lane would genuinely be
locked to lane 7 but the documentation does seem to suggest it.
Yes in fact I agree. clock-lanes can be reintroduced if someone can show
hardware that supports/depends on it.
---
bod
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH v8 2/2] phy: qcom-mipi-csi2: Add a CSI2 MIPI DPHY driver
From: Vladimir Zapolskiy @ 2026-06-02 22:07 UTC (permalink / raw)
To: Bryan O'Donoghue, Vinod Koul, Kishon Vijay Abraham I,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Neil Armstrong
Cc: Bryan O'Donoghue, linux-arm-msm, linux-phy, linux-media,
devicetree, linux-kernel
In-Reply-To: <20260523-x1e-csi2-phy-v8-2-a85668459521@linaro.org>
On 5/23/26 05:48, Bryan O'Donoghue wrote:
> 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.
> Data-lane and clock lane positioning and polarity selection via newly
> amended struct phy_configure_opts_mipi_dphy{} is supported.
>
> 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.
>
> The register init sequence table is imported verbatim from the existing
> CAMSS csiphy driver. A follow-up series will rework the table to extract
> the repetitive per-lane pattern into a loop.
>
> Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org>
<>
> +static int phy_qcom_mipi_csi2_parse_routing(struct mipi_csi2phy_device *csi2phy)
> +{
> + struct mipi_csi2phy_stream_cfg *stream_cfg = &csi2phy->stream_cfg;
> + u32 lane_polarities[CSI2_MAX_DATA_LANES + 1];
> + u32 data_lanes[CSI2_MAX_DATA_LANES];
> + struct device *dev = csi2phy->dev;
> + struct fwnode_handle *ep;
> + int num_polarities;
> + int num_data_lanes;
> + u32 clock_lane;
> + int i, ret;
> +
> + ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 1, 0,
> + FWNODE_GRAPH_ENDPOINT_NEXT);
> + if (ep) {
> + fwnode_handle_put(ep);
> + dev_err(dev, "DPHY split mode is not supported\n");
> + return -EOPNOTSUPP;
> + }
> +
> + ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0, 0);
> + if (!ep) {
> + dev_err(dev, "Missing port@0\n");
> + return -ENODEV;
> + }
> +
> + num_data_lanes = fwnode_property_count_u32(ep, "data-lanes");
> + if (num_data_lanes < 1 || num_data_lanes > CSI2_MAX_DATA_LANES) {
> + ret = -EINVAL;
> + dev_err(dev, "Invalid data-lanes count: %d\n", num_data_lanes);
> + goto out_put;
> + }
> + stream_cfg->num_data_lanes = num_data_lanes;
> +
> + ret = fwnode_property_read_u32_array(ep, "data-lanes", data_lanes,
> + stream_cfg->num_data_lanes);
> + if (ret) {
> + dev_err(dev, "Failed to read data-lanes: %d\n", ret);
> + goto out_put;
> + }
> +
> + ret = fwnode_property_read_u32(ep, "clock-lanes", &clock_lane);
> + if (ret) {
> + clock_lane = CSI2_DEFAULT_CLK_LN;
> + dev_info(dev, "Using default clock-lane %d\n",
> + CSI2_DEFAULT_CLK_LN);
Why CSI2_DEFAULT_CLK_LN is set to 7, what does it mean and how is it used?
Since "7" is a meaningless number in the context, I believe it's practically
not used at all, and if so, 'clock-lanes' property should be just removed.
> + }
--
Best wishes,
Vladimir
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH v8 1/2] dt-bindings: phy: qcom: Add CSI2 C-PHY/DPHY schema
From: Vladimir Zapolskiy @ 2026-06-02 21:59 UTC (permalink / raw)
To: Bryan O'Donoghue, Vinod Koul, Kishon Vijay Abraham I,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Neil Armstrong
Cc: Bryan O'Donoghue, linux-arm-msm, linux-phy, linux-media,
devicetree, linux-kernel
In-Reply-To: <20260523-x1e-csi2-phy-v8-1-a85668459521@linaro.org>
On 5/23/26 05:48, Bryan O'Donoghue wrote:
> Add a base schema initially compatible with x1e80100 to describe MIPI CSI2
> PHY devices.
>
> The hardware can support both CPHY, DPHY and a special split-mode DPHY.
>
> The schema here defines three ports:
>
> port@0:
> The first input port where a sensor is always required.
>
> port@1:
> A second optional input port which if present implies DPHY split-mode.
>
> port@2:
> A third always required output port which connects to the controller.
>
This port numeration is imperfect, because port@0 and port@2 are required,
while middle port@1 is optional.
Like it was stated before a number of times, it seems natural to operate
with two ports, where input port may have two endpoints rather than 3 ports,
also that approach solves the problem of a hole in the port numeration.
> 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 | 209 +++++++++++++++++++++
> 1 file changed, 209 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..270375f949880
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/phy/qcom,x1e80100-csi2-phy.yaml
> @@ -0,0 +1,209 @@
> +# 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
> + description:
> + The single cell specifies the PHY operating mode.
#phy-cells should be 0, because the PHY operating mode is well defined
by 'bus-type' property of an endpoint on the sensor side, the opposite
side of CAMSS/CSID as a CSIPHY "consumer" should not dictate the PHY type.
> +
> + clocks:
> + maxItems: 2
> +
> + clock-names:
> + items:
> + - const: core
> + - const: timer
> +
> + interrupts:
> + maxItems: 1
> +
> + operating-points-v2:
> + maxItems: 1
> +
> + power-domains:
> + items:
> + - description: MMCX voltage rail
> + - description: MXC or MXA voltage rail
Only "qcom,x1e80100-csi2-phy" device is supported so far, unlikely it's
the case that "MXC or MXA voltage rail" should be specified, it'd be
just one of two or both.
> +
> + power-domain-names:
> + items:
> + - const: mmcx
> + - const: mx
> +
> + vdda-0p9-supply:
> + description: Phandle to a 0.9V regulator supply to a PHY.
> +
> + vdda-1p2-supply:
> + description: Phandle to 1.2V regulator supply to a PHY.
> +
> + ports:
> + $ref: /schemas/graph.yaml#/properties/ports
> +
> + properties:
> + port@0:
> + $ref: /schemas/graph.yaml#/$defs/port-base
> + description: Sensor input. Always present.
> + unevaluatedProperties: false
> +
> + properties:
> + endpoint:
> + $ref: /schemas/media/video-interfaces.yaml#
> + unevaluatedProperties: false
> + properties:
> + data-lanes:
> + minItems: 1
> + maxItems: 4
> + clock-lanes:
> + maxItems: 1
> + remote-endpoint: true
> + required:
> + - data-lanes
> + - remote-endpoint
> +
> + port@1:
> + $ref: /schemas/graph.yaml#/$defs/port-base
> + description:
> + Second sensor input. When present, indicates DPHY split mode.
> + unevaluatedProperties: false
> +
> + properties:
> + endpoint:
> + $ref: /schemas/media/video-interfaces.yaml#
> + unevaluatedProperties: false
> + properties:
> + data-lanes:
> + maxItems: 1
> + clock-lanes:
> + maxItems: 1
> + remote-endpoint: true
> + required:
> + - data-lanes
> + - clock-lanes
> + - remote-endpoint
As it's stated above, it should be converted to a single port with two
endpoints, it'd be done in accordance to video-interfaces.yaml.
> +
> + port@2:
> + $ref: /schemas/graph.yaml#/$defs/port-base
> + description: Output to CAMSS controller.
> + unevaluatedProperties: false
> +
> + properties:
> + endpoint:
> + $ref: /schemas/graph.yaml#/$defs/endpoint-base
> + unevaluatedProperties: false
> + properties:
> + remote-endpoint: true
> + required:
> + - remote-endpoint
> +
> + required:
> + - port@0
> + - port@2
> +
> +required:
> + - compatible
> + - reg
> + - "#phy-cells"
> + - clocks
> + - clock-names
> + - interrupts
> + - operating-points-v2
> + - power-domains
> + - power-domain-names
> + - vdda-0p9-supply
> + - vdda-1p2-supply
> + - ports
> +
> +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/power/qcom,rpmhpd.h>
> +
> + csiphy4: csiphy@ace4000 {
> + compatible = "qcom,x1e80100-csi2-phy";
> + reg = <0x0ace4000 0x2000>;
> + #phy-cells = <1>;
> +
> + clocks = <&camcc CAM_CC_CSIPHY0_CLK>,
> + <&camcc CAM_CC_CSI0PHYTIMER_CLK>;
> + clock-names = "core",
> + "timer";
> +
> + operating-points-v2 = <&csiphy_opp_table>;
> +
> + interrupts = <GIC_SPI 477 IRQ_TYPE_EDGE_RISING>;
> +
> + power-domains = <&rpmhpd RPMHPD_MMCX>,
> + <&rpmhpd RPMHPD_MX>;
> + power-domain-names = "mmcx",
> + "mx";
> +
> + vdda-0p9-supply = <&vreg_l2c_0p8>;
> + vdda-1p2-supply = <&vreg_l1c_1p2>;
> +
> + ports {
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + port@0 {
> + reg = <0>;
> + csiphy0_in_ep: endpoint {
> + data-lanes = <0 1>;
> + clock-lanes = <2>;
> + remote-endpoint = <&sensor_out>;
> + };
> + };
> +
> + port@2 {
> + reg = <2>;
> + csiphy0_out_ep: endpoint {
> + remote-endpoint = <&controller_in>;
> + };
> + };
> + };
> + };
> +
> + csiphy_opp_table: opp-table {
> + compatible = "operating-points-v2";
> +
> + opp-300000000 {
> + opp-hz = /bits/ 64 <300000000>;
> + required-opps = <&rpmhpd_opp_low_svs_d1>,
> + <&rpmhpd_opp_low_svs_d1>;
> + };
> +
> + opp-400000000 {
> + opp-hz = /bits/ 64 <400000000>;
> + required-opps = <&rpmhpd_opp_low_svs>,
> + <&rpmhpd_opp_low_svs_d1>;
> + };
> +
> + opp-480000000 {
> + opp-hz = /bits/ 64 <480000000>;
> + required-opps = <&rpmhpd_opp_low_svs>,
> + <&rpmhpd_opp_low_svs_d1>;
> + };
> + };
>
--
Best wishes,
Vladimir
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH v8 1/2] dt-bindings: phy: qcom: Add CSI2 C-PHY/DPHY schema
From: Bryan O'Donoghue @ 2026-06-02 21:00 UTC (permalink / raw)
To: Frank Li
Cc: Vinod Koul, Kishon Vijay Abraham I, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Neil Armstrong,
Bryan O'Donoghue, Vladimir Zapolskiy, linux-arm-msm,
linux-phy, linux-media, devicetree, linux-kernel
In-Reply-To: <ah9DMPcKUaTm324I@lizhi-Precision-Tower-5810>
On 02/06/2026 21:55, Frank Li wrote:
> phy {
> ...
> }
>
> csi2 {
> phy=<&phy>;
>
> port@0: {
> }
> ...
> }
Agreed.
However, we also want to support "split-mode" which is a special mode of
this PHY where two sensors can be connected to one CSIPHY.
After a lot of debate adding ports/endpoints to the CSIPHY itself has
been pretty much settled on.
---
bod
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH v8 1/2] dt-bindings: phy: qcom: Add CSI2 C-PHY/DPHY schema
From: Frank Li @ 2026-06-02 20:55 UTC (permalink / raw)
To: Bryan O'Donoghue
Cc: Vinod Koul, Kishon Vijay Abraham I, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Neil Armstrong,
Bryan O'Donoghue, Vladimir Zapolskiy, linux-arm-msm,
linux-phy, linux-media, devicetree, linux-kernel
In-Reply-To: <20260523-x1e-csi2-phy-v8-1-a85668459521@linaro.org>
On Sat, May 23, 2026 at 03:48:47AM +0100, Bryan O'Donoghue wrote:
> Add a base schema initially compatible with x1e80100 to describe MIPI CSI2
> PHY devices.
>
> The hardware can support both CPHY, DPHY and a special split-mode DPHY.
>
> The schema here defines three ports:
>
> port@0:
> The first input port where a sensor is always required.
>
> port@1:
> A second optional input port which if present implies DPHY split-mode.
>
> port@2:
> A third always required output port which connects to the controller.
Most likely CSI controller binding to descript connect graph. PHY should
be simple and straigh forward to change digial signal to analog signal.
most system work like
phy {
...
}
csi2 {
phy=<&phy>;
port@0: {
}
...
}
Frank
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH] phy: fsl-imx8mq-usb: fix typec switch leak on probe error path
From: Frank Li @ 2026-06-02 20:28 UTC (permalink / raw)
To: Felix Gu
Cc: Vinod Koul, Neil Armstrong, Sascha Hauer, Pengutronix Kernel Team,
Fabio Estevam, Xu Yang, Jun Li, linux-phy, imx, linux-arm-kernel,
linux-kernel
In-Reply-To: <20260602-imx8mq-typec-v1-1-3debe9bc77d9@gmail.com>
On Tue, Jun 02, 2026 at 10:12:41PM +0800, Felix Gu wrote:
> If probe fails after imx95_usb_phy_get_tca() succeeds, the typec
> switch leaks because the only cleanup path was in .remove, which
> never runs on probe failure.
>
> Use devm_add_action_or_reset() so the switch is cleaned up on both
> probe failure and driver removal. The .remove callback and
> imx95_usb_phy_put_tca() are no longer needed.
>
> Fixes: b58f0f86fd61 ("phy: fsl-imx8mq-usb: add tca function driver for imx95")
> Signed-off-by: Felix Gu <ustc.gu@gmail.com>
> ---
Reviewed-by: Frank Li <Frank.Li@nxp.com>
> drivers/phy/freescale/phy-fsl-imx8mq-usb.c | 27 ++++++++-------------------
> 1 file changed, 8 insertions(+), 19 deletions(-)
>
> diff --git a/drivers/phy/freescale/phy-fsl-imx8mq-usb.c b/drivers/phy/freescale/phy-fsl-imx8mq-usb.c
> index b05d80e849a1..8af5a4f85698 100644
> --- a/drivers/phy/freescale/phy-fsl-imx8mq-usb.c
> +++ b/drivers/phy/freescale/phy-fsl-imx8mq-usb.c
> @@ -173,8 +173,10 @@ static struct typec_switch_dev *tca_blk_get_typec_switch(struct platform_device
> return sw;
> }
>
> -static void tca_blk_put_typec_switch(struct typec_switch_dev *sw)
> +static void tca_blk_put_typec_switch(void *data)
> {
> + struct typec_switch_dev *sw = data;
> +
> typec_switch_unregister(sw);
> }
>
> @@ -248,6 +250,7 @@ static struct tca_blk *imx95_usb_phy_get_tca(struct platform_device *pdev,
> struct device *dev = &pdev->dev;
> struct resource *res;
> struct tca_blk *tca;
> + int ret;
>
> res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> if (!res)
> @@ -266,17 +269,11 @@ static struct tca_blk *imx95_usb_phy_get_tca(struct platform_device *pdev,
> tca->orientation = TYPEC_ORIENTATION_NORMAL;
> tca->sw = tca_blk_get_typec_switch(pdev, imx_phy);
>
> - return tca;
> -}
> -
> -static void imx95_usb_phy_put_tca(struct imx8mq_usb_phy *imx_phy)
> -{
> - struct tca_blk *tca = imx_phy->tca;
> -
> - if (!tca)
> - return;
> + ret = devm_add_action_or_reset(&pdev->dev, tca_blk_put_typec_switch, tca->sw);
> + if (ret)
> + return ERR_PTR(ret);
>
> - tca_blk_put_typec_switch(tca->sw);
> + return tca;
> }
>
> static u32 phy_tx_vref_tune_from_property(u32 percent)
> @@ -739,16 +736,8 @@ static int imx8mq_usb_phy_probe(struct platform_device *pdev)
> return PTR_ERR_OR_ZERO(phy_provider);
> }
>
> -static void imx8mq_usb_phy_remove(struct platform_device *pdev)
> -{
> - struct imx8mq_usb_phy *imx_phy = platform_get_drvdata(pdev);
> -
> - imx95_usb_phy_put_tca(imx_phy);
> -}
> -
> static struct platform_driver imx8mq_usb_phy_driver = {
> .probe = imx8mq_usb_phy_probe,
> - .remove = imx8mq_usb_phy_remove,
> .driver = {
> .name = "imx8mq-usb-phy",
> .of_match_table = imx8mq_usb_phy_of_match,
>
> ---
> base-commit: 08484c504b55a98bd100527fbe10a3caf55ff3ff
> change-id: 20260602-imx8mq-typec-20a6f1d1900f
>
> Best regards,
> --
> Felix Gu <ustc.gu@gmail.com>
>
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH v2 phy-next 13/15] dt-bindings: phy: lynx-10g: initial document
From: Conor Dooley @ 2026-06-02 17:10 UTC (permalink / raw)
To: Vladimir Oltean
Cc: linux-phy, Ioana Ciornei, Vinod Koul, Neil Armstrong,
Tanjeff Moos, linux-kernel, devicetree, Conor Dooley,
Krzysztof Kozlowski, Rob Herring
In-Reply-To: <20260529171509.1163787-14-vladimir.oltean@nxp.com>
[-- Attachment #1.1: Type: text/plain, Size: 5088 bytes --]
On Fri, May 29, 2026 at 08:15:07PM +0300, Vladimir Oltean wrote:
> Add a schema for the 10G Lynx SerDes. This is very similar to the modern
> form of the 28G Lynx SerDes, which is very much the intention.
>
> We allow both forms of #phy-cells = <1> in the top-level provider
> and #phy-cells = <0> in the per-lane provider for more flexibility to
> consumers, and because the kernel code is shared with the 28G Lynx which
> already has that support for compatibility reasons.
>
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> ---
> Cc: devicetree@vger.kernel.org
> Cc: Conor Dooley <conor+dt@kernel.org>
> Cc: Krzysztof Kozlowski <krzk+dt@kernel.org>
> Cc: Rob Herring <robh@kernel.org>
>
> v1->v2:
> - move patch later in series, right before driver
> - deliberately ignoring this Sashiko feedback:
> https://lore.kernel.org/linux-phy/20260529125017.ifqunh52gdzhthdg@skbuf/
> ---
> .../devicetree/bindings/phy/fsl,lynx-10g.yaml | 131 ++++++++++++++++++
> 1 file changed, 131 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/phy/fsl,lynx-10g.yaml
>
> diff --git a/Documentation/devicetree/bindings/phy/fsl,lynx-10g.yaml b/Documentation/devicetree/bindings/phy/fsl,lynx-10g.yaml
> new file mode 100644
> index 000000000000..993f076bba4e
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/phy/fsl,lynx-10g.yaml
> @@ -0,0 +1,131 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/phy/fsl,lynx-10g.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Freescale Lynx 10G SerDes PHY
> +
> +maintainers:
> + - Vladimir Oltean <vladimir.oltean@nxp.com>
> +
> +description:
> + The 10G Lynx is a multi-protocol SerDes block which handles networking, PCIe,
> + SATA and other high-speed interfaces. It is present on most QorIQ and
> + Layerscape SoCs. The register map is common, but the integration is
> + SoC-specific, with the differences consisting in register endianness, the
> + number of lanes, protocol converters available per lane and their location in
> + the PCCR registers. Some SoCs have multiple SerDes blocks and those differ in
> + their protocol capabilities per lane.
> +
> +properties:
> + compatible:
> + description:
> + There is intentionally no generic fsl,lynx-10g compatible string due to
> + the hardware inability to report its capabilities, despite having a
> + common register map.
I think you can probably drop this from the diff, and put it in the
commit message.
> + enum:
> + - fsl,ls1028a-serdes
> + - fsl,ls1046a-serdes1
> + - fsl,ls1046a-serdes2
> + - fsl,ls1088a-serdes1
> + - fsl,ls1088a-serdes2
> + - fsl,ls2088a-serdes1
> + - fsl,ls2088a-serdes2
> +
> + reg:
> + maxItems: 1
> +
> + big-endian: true
This isn't a required property, but should it be made required for the
specific compatibles that are big endian? Or are we not that lucky, and
devices can be either?
Cheers,
Conor.
> +
> + "#phy-cells":
> + const: 1
> +
> + "#address-cells":
> + const: 1
> +
> + "#size-cells":
> + const: 0
> +
> +patternProperties:
> + "^phy@[0-7]$":
> + type: object
> + description: SerDes lane (single RX/TX differential pair)
> +
> + properties:
> + reg:
> + minimum: 0
> + maximum: 7
> + description: Lane index as seen in register map
> +
> + "#phy-cells":
> + const: 0
> +
> + required:
> + - reg
> + - "#phy-cells"
> +
> + additionalProperties: false
> +
> +required:
> + - compatible
> + - reg
> + - "#phy-cells"
> + - "#address-cells"
> + - "#size-cells"
> +
> +allOf:
> + - if:
> + properties:
> + compatible:
> + contains:
> + enum:
> + - fsl,ls1028a-serdes
> + - fsl,ls1046a-serdes1
> + - fsl,ls1046a-serdes2
> + - fsl,ls1088a-serdes1
> + - fsl,ls1088a-serdes2
> + then:
> + patternProperties:
> + "^phy@[0-7]$":
> + properties:
> + reg:
> + minimum: 0
> + maximum: 3
> +
> +additionalProperties: false
> +
> +examples:
> + - |
> + soc {
> + #address-cells = <2>;
> + #size-cells = <2>;
> +
> + serdes@1ea0000 {
> + compatible = "fsl,ls1028a-serdes";
> + reg = <0x0 0x1ea0000 0x0 0xffff>;
> + #address-cells = <1>;
> + #size-cells = <0>;
> + #phy-cells = <1>;
> +
> + phy@0 {
> + reg = <0>;
> + #phy-cells = <0>;
> + };
> +
> + phy@1 {
> + reg = <1>;
> + #phy-cells = <0>;
> + };
> +
> + phy@2 {
> + reg = <2>;
> + #phy-cells = <0>;
> + };
> +
> + phy@3 {
> + reg = <3>;
> + #phy-cells = <0>;
> + };
> + };
> + };
> --
> 2.34.1
>
[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
[-- Attachment #2: Type: text/plain, Size: 112 bytes --]
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Incorrect reply address with b4 relay [was Re: [PATCH v6 2/2] phy: add basic support for NXPs TJA1145 CAN transceiver]
From: Rob Herring @ 2026-06-02 16:02 UTC (permalink / raw)
To: sashiko-reviews
Cc: Dimitri Fedrau via B4 Relay, olteanv, vkoul, neil.armstrong,
linux-phy, devicetree, conor+dt
In-Reply-To: <20260602083856.E92081F00898@smtp.kernel.org>
Sashiko devs,
It looks like sashiko replies to b4 relay submissions don't get sent to
the author's email and instead go to the relay's email (devnull). Here's
the header of the reply:
> Date: Tue, 02 Jun 2026 08:38:56 +0000
> From: sashiko-bot@kernel.org
> To: Dimitri Fedrau via B4 Relay <devnull+dimitri.fedrau.liebherr.com@kernel.org>
> Cc: olteanv@gmail.com, vkoul@kernel.org, neil.armstrong@linaro.org, linux-phy@lists.infradead.org, devicetree@vger.kernel.org, conor+dt@kernel.org, robh@kernel.org
> Subject: Re: [PATCH v6 2/2] phy: add basic support for NXPs TJA1145 CAN transceiver
> Message-Id: <20260602083856.E92081F00898@smtp.kernel.org>
On Tue, Jun 02, 2026 at 08:38:56AM +0000, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
> - [Medium] If setting the CAN operating mode fails during `tja1145_phy_power_on`, the PHY hardware is left in normal operating mode without rolling back to standby mode.
> --
>
> commit 4b999361552fc4af356fbf9fbd3b1ca5f2dd59bf
> Author: Dimitri Fedrau <dimitri.fedrau@liebherr.com>
>
> phy: add basic support for NXPs TJA1145 CAN transceiver
>
> This commit adds basic driver support for the NXP TJA1145 CAN transceiver.
> It enables bringing the PHY up or down by switching between normal and
> standby modes using SPI commands.
>
> > diff --git a/drivers/phy/phy-nxp-tja1145.c b/drivers/phy/phy-nxp-tja1145.c
> > new file mode 100644
> > index 0000000000000..1e8bd169743ab
> > --- /dev/null
> > +++ b/drivers/phy/phy-nxp-tja1145.c
>
> [ ... ]
>
> > +static int tja1145_phy_power_on(struct phy *phy)
> > +{
> > + struct regmap *map = phy_get_drvdata(phy);
> > + int ret;
> > +
> > + /*
> > + * Switch operating mode to normal which is the active operating mode.
> > + * In this mode, the device is fully operational.
> > + */
> > + ret = regmap_update_bits(map, TJA1145_MODE_CTRL, TJA1145_MODE_CTRL_MC,
> > + TJA1145_MODE_CTRL_NORMAL);
> > + if (ret)
> > + return ret;
> > +
> > + /*
> > + * Switch to CAN operating mode active where the PHY can transmit and
> > + * receive data.
> > + */
> > + return regmap_update_bits(map, TJA1145_CAN_CTRL, TJA1145_CAN_CTRL_CMC,
> > + TJA1145_CAN_CTRL_ACTIVE);
> > +}
>
> [Severity: Medium]
> If the second regmap_update_bits call fails to set TJA1145_CAN_CTRL, does
> this leave the hardware in normal mode without reverting to standby?
>
> Because tja1145_phy_power_on would return an error here, the PHY framework
> will not consider the PHY powered on and will not invoke
> tja1145_phy_power_off later. Could this result in the hardware being
> permanently left in a higher-power state?
>
> Should the driver intercept this error and attempt to roll back
> TJA1145_MODE_CTRL to TJA1145_MODE_CTRL_STBY before returning?
>
> --
> Sashiko AI review · https://sashiko.dev/#/patchset/20260602-tja1145-support-v6-0-0e0ffc8ee63d@liebherr.com?part=2
>
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH v2 phy-next 01/15] phy: lynx-28g: reject probing on devices with unsupported OF nodes
From: Vladimir Oltean @ 2026-06-02 14:52 UTC (permalink / raw)
To: sashiko-reviews; +Cc: vkoul, linux-phy, olteanv, neil.armstrong
In-Reply-To: <20260529173544.908FE1F00893@smtp.kernel.org>
On Fri, May 29, 2026 at 05:35:43PM +0000, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
>
> Pre-existing issues:
> - [High] The lynx_28g_xlate callback can return NULL for uninitialized PHY lanes, leading to a NULL pointer dereference in the PHY framework.
> - [High] The PHY device retains a pointer to an OF child node after its reference count has been dropped, leading to a potential Use-After-Free.
> --
>
> commit 74be4f3f82272eac244d345f53d8340e9a230084
> Author: Vladimir Oltean <vladimir.oltean@nxp.com>
>
> phy: lynx-28g: reject probing on devices with unsupported OF nodes
>
> This patch fixes a crash triggered via the driver_override mechanism when
> of_device_get_match_data() returns NULL for a device with a different
> compatible string. It adds a check to return -ENODEV if the match data
> is missing.
>
> > diff --git a/drivers/phy/freescale/phy-fsl-lynx-28g.c b/drivers/phy/freescale/phy-fsl-lynx-28g.c
> > index 92bfc5f65e0b8..4461b47a16ad6 100644
> > --- a/drivers/phy/freescale/phy-fsl-lynx-28g.c
> > +++ b/drivers/phy/freescale/phy-fsl-lynx-28g.c
> > @@ -1477,6 +1477,9 @@ static int lynx_28g_probe(struct platform_device *pdev)
> >
> > priv->dev = dev;
> > priv->info = of_device_get_match_data(dev);
> > + if (!priv->info)
> > + return -ENODEV;
> > +
> > dev_set_drvdata(dev, priv);
> > spin_lock_init(&priv->pcc_lock);
> > INIT_DELAYED_WORK(&priv->cdr_check, lynx_28g_cdr_lock_check);
>
> [Severity: High]
> This isn't a bug introduced by this patch, but does this function leave the
> PHY device with a pointer to a freed child node later in the probe process?
>
> Further down in lynx_28g_probe():
>
> for_each_available_child_of_node(dn, child) {
> ...
> err = lynx_28g_probe_lane(priv, reg, child);
> ...
>
> The for_each_available_child_of_node() macro automatically handles
> reference counting for the child node.
>
> When lynx_28g_probe_lane() creates the PHY, phy_create() stores this
> child node in phy->dev.of_node, but it does not call of_node_get() to
> acquire an additional reference.
>
> Once the loop advances, the iterator automatically calls of_node_put(),
> dropping the reference. If the node is later dynamically freed, such as
> during a DT overlay unload, could this cause a use-after-free when
> phy->dev.of_node is accessed?
>
This is a problem within phy_create() not calling of_node_get() when
assigning phy->dev.of_node. It is out of scope for this series.
> [Severity: High]
> This is a pre-existing issue, but can the xlate callback return NULL for
> uninitialized PHY lanes and cause a crash in the PHY framework?
>
> If a requested lane index is within bounds but the lane was not probed
> (due to a missing subnode in the device tree), priv->lane[idx].phy remains
> NULL and is returned directly by lynx_28g_xlate().
>
> When this NULL is passed to of_phy_get() by _of_phy_get():
>
> drivers/phy/phy-core.c:of_phy_get() {
> ...
> if (IS_ERR(phy))
> return phy;
>
> if (!try_module_get(phy->ops->owner))
> ...
> }
>
> Since IS_ERR(NULL) evaluates to false, it proceeds to dereference the NULL
> pointer via phy->ops->owner. Should the callback return ERR_PTR(-ENODEV)
> instead for missing lanes?
Yeah, ok, _of_phy_get() does not work when phy_provider->of_xlate()
returns NULL, it needs to be an error-encoded pointer instead.
Will add one patch for v3.
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH v2 phy-next 02/15] phy: lynx-28g: move lane mode helpers to new core module
From: Vladimir Oltean @ 2026-06-02 14:38 UTC (permalink / raw)
To: sashiko-reviews; +Cc: vkoul, olteanv, linux-phy, neil.armstrong
In-Reply-To: <20260529172706.50EC21F00893@smtp.kernel.org>
On Fri, May 29, 2026 at 05:27:05PM +0000, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
> - [Low] The enum values for `lynx_lane_mode` are globally exposed without proper namespacing, risking build failures due to collisions.
> - [Low] Invisible Kconfig symbol contains useless 'help' text.
> --
>
> commit afcf6c44814cd194adbadc7748b22b52095d7d42
> Author: Vladimir Oltean <vladimir.oltean@nxp.com>
>
> phy: lynx-28g: move lane mode helpers to new core module
>
> This commit extracts lane mode helpers and shared structures from the
> lynx-28g driver into a new core module. The lane modes are exported to
> a public header to allow coordination with the upcoming lynx-10g driver.
>
> > diff --git a/drivers/phy/freescale/Kconfig b/drivers/phy/freescale/Kconfig
> > index 81f53564ee156..a87429f634ea8 100644
> > --- a/drivers/phy/freescale/Kconfig
> > +++ b/drivers/phy/freescale/Kconfig
> > @@ -51,11 +51,18 @@ config PHY_FSL_SAMSUNG_HDMI_PHY
> > Enable this to add support for the Samsung HDMI PHY in i.MX8MP.
> > endif
> >
> > +config PHY_FSL_LYNX_CORE
> > + tristate
> > + help
> > + Enable this to add common support code for NXP Lynx 10G and Lynx 28G
> > + SerDes blocks.
>
> [Severity: Low]
> This isn't a bug, but does this invisible Kconfig symbol need a help text?
>
> Since this tristate doesn't have a prompt string, it won't be visible in
> menuconfig, making the help text inaccessible to users.
Will remove help text.
> > diff --git a/include/soc/fsl/phy-fsl-lynx.h b/include/soc/fsl/phy-fsl-lynx.h
> > new file mode 100644
> > index 0000000000000..92e8272d5ae15
> > --- /dev/null
> > +++ b/include/soc/fsl/phy-fsl-lynx.h
> > @@ -0,0 +1,16 @@
> > +/* SPDX-License-Identifier: GPL-2.0+ */
> > +/* Copyright 2023-2026 NXP */
> > +
> > +#ifndef __PHY_FSL_LYNX_H_
> > +#define __PHY_FSL_LYNX_H_
> > +
> > +enum lynx_lane_mode {
> > + LANE_MODE_UNKNOWN,
> > + LANE_MODE_1000BASEX_SGMII,
> > + LANE_MODE_10GBASER,
> > + LANE_MODE_USXGMII,
> > + LANE_MODE_25GBASER,
> > + LANE_MODE_MAX,
> > +};
>
> [Severity: Low]
> Since this header is now globally accessible under include/soc/, could these
> generic enum values cause namespace collisions?
>
> Values like LANE_MODE_UNKNOWN and LANE_MODE_MAX lack a subsystem or
> driver-specific prefix (like LYNX_ or FSL_). If another subsystem defines
> a macro or enum with the same generic name, it might lead to build failures.
In a vacuum this is a valid point, but unless I am being told by a human
to make this refactoring, the risk of namespacing conflicts will have to
be there. The churn resulting from renaming these will be painful for me
to manage and will result in additional patches too. At least we can go
with the lane mode variables named this way and rename them later, if it
ever becomes a practical problem (hopefully I can upstream the rest of
the code by then, so I don't have to resolve merge conflicts in tens of
patches).
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH] phy: fsl-imx8mq-usb: fix typec switch leak on probe error path
From: sashiko-bot @ 2026-06-02 14:24 UTC (permalink / raw)
To: Felix Gu; +Cc: neil.armstrong, linux-phy, vkoul, Frank.Li, olteanv, imx
In-Reply-To: <20260602-imx8mq-typec-v1-1-3debe9bc77d9@gmail.com>
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
Pre-existing issues:
- [High] Potential NULL pointer dereference if the Type-C switch's `.set()` callback is invoked concurrently during probe.
- [High] Potential Use-After-Free if a typec port driver holds a reference to the switch and calls `.set()` after the PHY driver unbinds.
--
commit 546b07c54e0077d325cf3fd20b1dd95d1c123a9d
Author: Felix Gu <ustc.gu@gmail.com>
phy: fsl-imx8mq-usb: fix typec switch leak on probe error path
If probe fails after imx95_usb_phy_get_tca() succeeds, the typec
switch leaks because the only cleanup path was in .remove, which
never runs on probe failure.
Use devm_add_action_or_reset() so the switch is cleaned up on both
probe failure and driver removal.
> diff --git a/drivers/phy/freescale/phy-fsl-imx8mq-usb.c b/drivers/phy/freescale/phy-fsl-imx8mq-usb.c
> index b05d80e849a14..8af5a4f856987 100644
> --- a/drivers/phy/freescale/phy-fsl-imx8mq-usb.c
> +++ b/drivers/phy/freescale/phy-fsl-imx8mq-usb.c
> @@ -173,8 +173,10 @@ static struct typec_switch_dev *tca_blk_get_typec_switch(struct platform_device
> return sw;
> }
>
> -static void tca_blk_put_typec_switch(struct typec_switch_dev *sw)
> +static void tca_blk_put_typec_switch(void *data)
> {
> + struct typec_switch_dev *sw = data;
> +
> typec_switch_unregister(sw);
> }
[Severity: High]
This is a pre-existing issue, but could this cleanup sequence lead to a
use-after-free during unbind?
When the PHY driver unbinds or fails probe late, the devres framework will
automatically free the imx_phy and tca structures since they are allocated
with devm_kzalloc. While typec_switch_unregister() drops the initial
reference to the switch, it doesn't force consumers to drop their references.
If a consumer typec port driver still holds a reference to the switch and
calls its .set callback after the PHY driver unbinds:
tca_blk_typec_switch_set()
struct imx8mq_usb_phy *imx_phy = typec_switch_get_drvdata(sw);
struct tca_blk *tca = imx_phy->tca;
won't this callback access the freed imx_phy and tca memory?
[ ... ]
> @@ -266,17 +269,11 @@ static struct tca_blk *imx95_usb_phy_get_tca(struct platform_device *pdev,
> tca->orientation = TYPEC_ORIENTATION_NORMAL;
> tca->sw = tca_blk_get_typec_switch(pdev, imx_phy);
[Severity: High]
This isn't a bug introduced by this patch, but does registering the switch
here expose it to consumers before imx_phy->tca is assigned?
Once registered, the switch becomes globally discoverable via fwnode matching.
If a consumer discovers it and immediately invokes the .set callback
(tca_blk_typec_switch_set), that callback expects imx_phy->tca to be
fully initialized:
tca_blk_typec_switch_set()
struct tca_blk *tca = imx_phy->tca;
int ret;
if (tca->orientation == orientation)
Since the assignment of imx_phy->tca happens later in the probe sequence
after this function returns, wouldn't this concurrent access dereference a
NULL pointer?
> - return tca;
> -}
> -
> -static void imx95_usb_phy_put_tca(struct imx8mq_usb_phy *imx_phy)
> -{
> - struct tca_blk *tca = imx_phy->tca;
> -
> - if (!tca)
> - return;
> + ret = devm_add_action_or_reset(&pdev->dev, tca_blk_put_typec_switch, tca->sw);
> + if (ret)
> + return ERR_PTR(ret);
>
> - tca_blk_put_typec_switch(tca->sw);
> + return tca;
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260602-imx8mq-typec-v1-1-3debe9bc77d9@gmail.com?part=1
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* [PATCH] phy: fsl-imx8mq-usb: fix typec switch leak on probe error path
From: Felix Gu @ 2026-06-02 14:12 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Frank Li, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Xu Yang, Jun Li
Cc: linux-phy, imx, linux-arm-kernel, linux-kernel, Felix Gu
If probe fails after imx95_usb_phy_get_tca() succeeds, the typec
switch leaks because the only cleanup path was in .remove, which
never runs on probe failure.
Use devm_add_action_or_reset() so the switch is cleaned up on both
probe failure and driver removal. The .remove callback and
imx95_usb_phy_put_tca() are no longer needed.
Fixes: b58f0f86fd61 ("phy: fsl-imx8mq-usb: add tca function driver for imx95")
Signed-off-by: Felix Gu <ustc.gu@gmail.com>
---
drivers/phy/freescale/phy-fsl-imx8mq-usb.c | 27 ++++++++-------------------
1 file changed, 8 insertions(+), 19 deletions(-)
diff --git a/drivers/phy/freescale/phy-fsl-imx8mq-usb.c b/drivers/phy/freescale/phy-fsl-imx8mq-usb.c
index b05d80e849a1..8af5a4f85698 100644
--- a/drivers/phy/freescale/phy-fsl-imx8mq-usb.c
+++ b/drivers/phy/freescale/phy-fsl-imx8mq-usb.c
@@ -173,8 +173,10 @@ static struct typec_switch_dev *tca_blk_get_typec_switch(struct platform_device
return sw;
}
-static void tca_blk_put_typec_switch(struct typec_switch_dev *sw)
+static void tca_blk_put_typec_switch(void *data)
{
+ struct typec_switch_dev *sw = data;
+
typec_switch_unregister(sw);
}
@@ -248,6 +250,7 @@ static struct tca_blk *imx95_usb_phy_get_tca(struct platform_device *pdev,
struct device *dev = &pdev->dev;
struct resource *res;
struct tca_blk *tca;
+ int ret;
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
if (!res)
@@ -266,17 +269,11 @@ static struct tca_blk *imx95_usb_phy_get_tca(struct platform_device *pdev,
tca->orientation = TYPEC_ORIENTATION_NORMAL;
tca->sw = tca_blk_get_typec_switch(pdev, imx_phy);
- return tca;
-}
-
-static void imx95_usb_phy_put_tca(struct imx8mq_usb_phy *imx_phy)
-{
- struct tca_blk *tca = imx_phy->tca;
-
- if (!tca)
- return;
+ ret = devm_add_action_or_reset(&pdev->dev, tca_blk_put_typec_switch, tca->sw);
+ if (ret)
+ return ERR_PTR(ret);
- tca_blk_put_typec_switch(tca->sw);
+ return tca;
}
static u32 phy_tx_vref_tune_from_property(u32 percent)
@@ -739,16 +736,8 @@ static int imx8mq_usb_phy_probe(struct platform_device *pdev)
return PTR_ERR_OR_ZERO(phy_provider);
}
-static void imx8mq_usb_phy_remove(struct platform_device *pdev)
-{
- struct imx8mq_usb_phy *imx_phy = platform_get_drvdata(pdev);
-
- imx95_usb_phy_put_tca(imx_phy);
-}
-
static struct platform_driver imx8mq_usb_phy_driver = {
.probe = imx8mq_usb_phy_probe,
- .remove = imx8mq_usb_phy_remove,
.driver = {
.name = "imx8mq-usb-phy",
.of_match_table = imx8mq_usb_phy_of_match,
---
base-commit: 08484c504b55a98bd100527fbe10a3caf55ff3ff
change-id: 20260602-imx8mq-typec-20a6f1d1900f
Best regards,
--
Felix Gu <ustc.gu@gmail.com>
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* Re: [PATCH v8 2/2] phy: qcom-mipi-csi2: Add a CSI2 MIPI DPHY driver
From: Bryan O'Donoghue @ 2026-06-02 13:58 UTC (permalink / raw)
To: Loic Poulain, 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: <CAFEp6-13Nr7GGB2_pXDsonFRq2Afd1oLna_MvXqkswrH5o2g+w@mail.gmail.com>
On 02/06/2026 09:18, Loic Poulain wrote:
> Hi Bryan,
>
> On Sat, May 23, 2026 at 4:53 AM Bryan O'Donoghue
> <bryan.odonoghue@linaro.org> wrote:
>>
>> 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.
>> Data-lane and clock lane positioning and polarity selection via newly
>> amended struct phy_configure_opts_mipi_dphy{} is supported.
>>
>> 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.
>>
>> The register init sequence table is imported verbatim from the existing
>> CAMSS csiphy driver. A follow-up series will rework the table to extract
>> the repetitive per-lane pattern into a loop.
>>
>> Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org>
>> ---
>> MAINTAINERS | 10 +
>> drivers/phy/qualcomm/Kconfig | 14 +
>> drivers/phy/qualcomm/Makefile | 5 +
>> drivers/phy/qualcomm/phy-qcom-mipi-csi2-3ph-dphy.c | 376 +++++++++++++++++++
>> drivers/phy/qualcomm/phy-qcom-mipi-csi2-core.c | 402 +++++++++++++++++++++
>> drivers/phy/qualcomm/phy-qcom-mipi-csi2.h | 95 +++++
>> 6 files changed, 902 insertions(+)
>>
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index 63389fea5d150..3b5da8a40383f 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -22018,6 +22018,16 @@ 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: Maintained
>> +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
>> +
>> 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..779a3511ba852 100644
>> --- a/drivers/phy/qualcomm/Kconfig
>> +++ b/drivers/phy/qualcomm/Kconfig
>> @@ -28,6 +28,20 @@ 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 PM
>> + 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..86ec405820e62
>> --- /dev/null
>> +++ b/drivers/phy/qualcomm/phy-qcom-mipi-csi2-3ph-dphy.c
>> @@ -0,0 +1,376 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * 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))
>> +
>> +#define CSIPHY_2PH_LN_CSI_2PHASE_CTRL9n(n) ((0x200 * (n)) + 0x24)
>> +
>> +/*
>> + * 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_SETTLE_CNT_LOWER_BYTE 2
>> +#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);
>> +
>> + /* Lane position enable in common reg offset */
>> + val = BIT(lane_cfg->clk.pos);
>> + for (i = 0; i < cfg->num_data_lanes; i++)
>> + val |= BIT(lane_cfg->data[i].pos);
>> +
>> + writel(val, csi2phy->base +
>> + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->common_regs_offset, 5));
>> +
>> + /* Lane configuration for polarity @ CSIPHY-base + CTRL9 */
>> + for (i = 0; i < cfg->num_data_lanes; i++) {
>> + if (lane_cfg->data[i].pol) {
>> + u8 pos = lane_cfg->data[i].pos;
>> +
>> + writel(BIT(2), csi2phy->base + CSIPHY_2PH_LN_CSI_2PHASE_CTRL9n(pos));
>> + }
>> + }
>> +
>> + if (lane_cfg->clk.pol)
>> + writel(BIT(2), csi2phy->base + CSIPHY_2PH_LN_CSI_2PHASE_CTRL9n(lane_cfg->clk.pos));
>> +
>> + 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 char * const x1e_clks[] = {
>> + "core",
>> + "timer"
>> +};
>> +
>> +static const char * const x1e_supplies[] = {
>> + "vdda-0p9",
>> + "vdda-1p2"
>> +};
>> +
>> +static const char * const x1e_genpd_names[] = {
>> + "mmcx",
>> + "mx",
>> +};
>> +
>> +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,
>> + },
>> + .supply_names = (const char **)x1e_supplies,
>> + .num_supplies = ARRAY_SIZE(x1e_supplies),
>> + .clk_names = (const char **)x1e_clks,
>> + .num_clk = ARRAY_SIZE(x1e_clks),
>> + .opp_clk = x1e_clks[0],
>> + .timer_clk = x1e_clks[1],
>> + .genpd_names = (const char **)x1e_genpd_names,
>> + .num_genpd_names = ARRAY_SIZE(x1e_genpd_names),
>> +};
>> 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..dfeff863a406f
>> --- /dev/null
>> +++ b/drivers/phy/qualcomm/phy-qcom-mipi-csi2-core.c
>> @@ -0,0 +1,402 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Copyright (c) 2025, Linaro Ltd.
>> + */
>> +#include <dt-bindings/phy/phy.h>
>> +#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_domain.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"
>> +
>> +static int
>> +phy_qcom_mipi_csi2_set_clock_rates(struct mipi_csi2phy_device *csi2phy,
>> + s64 link_freq)
>> +{
>> + struct device *dev = csi2phy->dev;
>> + unsigned long opp_rate = link_freq / 4;
>> + struct dev_pm_opp *opp;
>> + long timer_rate;
>> + int i, ret;
>> +
>> + opp = dev_pm_opp_find_freq_ceil(dev, &opp_rate);
>> + if (IS_ERR(opp)) {
>> + dev_err(csi2phy->dev, "Couldn't find ceiling for %lld Hz\n",
>> + link_freq);
>> + return PTR_ERR(opp);
>> + }
>> +
>> + for (i = 0; i < csi2phy->pd_list->num_pds; i++) {
>> + unsigned int perf = dev_pm_opp_get_required_pstate(opp, i);
>> +
>> + ret = dev_pm_genpd_set_performance_state(csi2phy->pd_list->pd_devs[i], perf);
>> + if (ret) {
>> + dev_err(csi2phy->dev, "Couldn't set perf state %u\n",
>> + perf);
>> + dev_pm_opp_put(opp);
>> + goto unset_pstate;
>> + }
>> + }
>> + dev_pm_opp_put(opp);
>> +
>> + ret = dev_pm_opp_set_rate(dev, opp_rate);
>> + if (ret) {
>> + dev_err(csi2phy->dev, "dev_pm_opp_set_rate() fail\n");
>> + goto unset_opp_rate;
>> + }
>> +
>> + timer_rate = clk_round_rate(csi2phy->timer_clk, link_freq / 4);
>> + if (timer_rate <= 0) {
>> + ret = -ENODEV;
>> + goto unset_opp_rate;
>> + }
>> +
>> + ret = clk_set_rate(csi2phy->timer_clk, timer_rate);
>> + if (ret)
>> + goto unset_opp_rate;
>> +
>> + csi2phy->timer_clk_rate = timer_rate;
>> +
>> + return 0;
>> +
>> +unset_opp_rate:
>> + dev_pm_opp_set_rate(dev, 0);
>> +
>> +unset_pstate:
>> + while (i--)
>> + dev_pm_genpd_set_performance_state(csi2phy->pd_list->pd_devs[i], 0);
>> +
>> + return ret;
>> +}
>> +
>> +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->mipi_dphy;
>> + struct mipi_csi2phy_stream_cfg *stream_cfg = &csi2phy->stream_cfg;
>> + int ret;
>> +
>> + ret = phy_mipi_dphy_config_validate(dphy_cfg);
>> + if (ret)
>> + return ret;
>> +
>> + if (dphy_cfg->lanes < 1 || dphy_cfg->lanes > CSI2_MAX_DATA_LANES)
>> + return -EINVAL;
>> +
>> + stream_cfg->link_freq = dphy_cfg->hs_clk_rate;
>> +
>> + 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 i, ret;
>> +
>> + ret = regulator_bulk_enable(csi2phy->soc_cfg->num_supplies,
>> + csi2phy->supplies);
>> + if (ret)
>> + return ret;
>> +
>> + ret = pm_runtime_resume_and_get(csi2phy->dev);
>> + if (ret < 0)
>> + goto disable_regulators;
>> +
>> + 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 unset_rate;
>> + }
>> +
>> + ops->reset(csi2phy);
>> +
>> + ops->hw_version_read(csi2phy);
>> +
>> + return ops->lanes_enable(csi2phy, &csi2phy->stream_cfg);
>> +
>> +unset_rate:
>> + for (i = 0; i < csi2phy->pd_list->num_pds; i++)
>> + dev_pm_genpd_set_performance_state(csi2phy->pd_list->pd_devs[i], 0);
>> +
>> + dev_pm_opp_set_rate(csi2phy->dev, 0);
>> +
>> +poweroff_phy:
>> + pm_runtime_put_sync(csi2phy->dev);
>> +
>> +disable_regulators:
>> + 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);
>> + const struct mipi_csi2phy_hw_ops *ops = csi2phy->soc_cfg->ops;
>> + int i;
>> +
>> + ops->lanes_disable(csi2phy, &csi2phy->stream_cfg);
>> +
>> + clk_bulk_disable_unprepare(csi2phy->soc_cfg->num_clk,
>> + csi2phy->clks);
>> +
>> + for (i = 0; i < csi2phy->pd_list->num_pds; i++)
>> + dev_pm_genpd_set_performance_state(csi2phy->pd_list->pd_devs[i], 0);
>> +
>> + dev_pm_opp_set_rate(csi2phy->dev, 0);
>> +
>> + pm_runtime_put_sync(csi2phy->dev);
>> +
>> + 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 struct phy *qcom_csi2_phy_xlate(struct device *dev,
>> + const struct of_phandle_args *args)
>> +{
>> + struct mipi_csi2phy_device *csi2phy = dev_get_drvdata(dev);
>> +
>> + if (args->args[0] != PHY_TYPE_DPHY) {
>> + dev_err(csi2phy->dev, "mode %d -EOPNOTSUPP\n", args->args[0]);
>> + return ERR_PTR(-EOPNOTSUPP);
>> + }
>> +
>> + csi2phy->phy_mode = args->args[0];
>> +
>> + return csi2phy->phy;
>> +}
>> +
>> +static int phy_qcom_mipi_csi2_attach_pm_domains(struct mipi_csi2phy_device *csi2phy)
>> +{
>> + const struct dev_pm_domain_attach_data pd_data = {
>> + .pd_names = csi2phy->soc_cfg->genpd_names,
>> + .num_pd_names = csi2phy->soc_cfg->num_genpd_names,
>> + };
>> +
>> + return devm_pm_domain_attach_list(csi2phy->dev, &pd_data, &csi2phy->pd_list);
>
> If strict domain/name checking isn’t required (is there a reason it
> would be?), we could simplify the soc_cfg struct and pass NULL instead
> of pd_data in the above call.
Naming is a nice feature as it means you can mix RPMPD and GDSC
power-domains attaching opps to RPMPD only.
>
>> +}
>> +
>> +static int phy_qcom_mipi_csi2_parse_routing(struct mipi_csi2phy_device *csi2phy)
>> +{
>> + struct mipi_csi2phy_stream_cfg *stream_cfg = &csi2phy->stream_cfg;
>> + u32 lane_polarities[CSI2_MAX_DATA_LANES + 1];
>> + u32 data_lanes[CSI2_MAX_DATA_LANES];
>> + struct device *dev = csi2phy->dev;
>> + struct fwnode_handle *ep;
>> + int num_polarities;
>> + int num_data_lanes;
>> + u32 clock_lane;
>> + int i, ret;
>> +
>> + ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 1, 0,
>> + FWNODE_GRAPH_ENDPOINT_NEXT);
>> + if (ep) {
>> + fwnode_handle_put(ep);
>> + dev_err(dev, "DPHY split mode is not supported\n");
>> + return -EOPNOTSUPP;
>> + }
>> +
>> + ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0, 0);
>> + if (!ep) {
>> + dev_err(dev, "Missing port@0\n");
>> + return -ENODEV;
>> + }
>> +
>> + num_data_lanes = fwnode_property_count_u32(ep, "data-lanes");
>> + if (num_data_lanes < 1 || num_data_lanes > CSI2_MAX_DATA_LANES) {
>> + ret = -EINVAL;
>> + dev_err(dev, "Invalid data-lanes count: %d\n", num_data_lanes);
>> + goto out_put;
>> + }
>> + stream_cfg->num_data_lanes = num_data_lanes;
>> +
>> + ret = fwnode_property_read_u32_array(ep, "data-lanes", data_lanes,
>> + stream_cfg->num_data_lanes);
>> + if (ret) {
>> + dev_err(dev, "Failed to read data-lanes: %d\n", ret);
>> + goto out_put;
>> + }
>> +
>> + ret = fwnode_property_read_u32(ep, "clock-lanes", &clock_lane);
>> + if (ret) {
>> + clock_lane = CSI2_DEFAULT_CLK_LN;
>> + dev_info(dev, "Using default clock-lane %d\n",
>> + CSI2_DEFAULT_CLK_LN);
>> + }
>> +
>> + /* lane-polarities: optional, up to num_data_lanes + 1 entries */
>> + memset(lane_polarities, 0x00, sizeof(lane_polarities));
>> + num_polarities = fwnode_property_count_u32(ep, "lane-polarities");
>> + if (num_polarities > 0) {
>> + if (num_polarities != stream_cfg->num_data_lanes + 1) {
>> + ret = -EINVAL;
>> + dev_err(dev, "clock+data-lane %d/polarities %d mismatch\n",
>> + stream_cfg->num_data_lanes + 1, num_polarities);
>> + goto out_put;
>> + }
>> +
>> + ret = fwnode_property_read_u32_array(ep, "lane-polarities", lane_polarities,
>> + num_polarities);
>> + if (ret) {
>> + dev_err(dev, "Failed to read lane-polarities: %d\n", ret);
>> + goto out_put;
>> + }
>> + }
>> +
>> + for (i = 0; i < csi2phy->stream_cfg.num_data_lanes; i++) {
>> + csi2phy->stream_cfg.lane_cfg.data[i].pos = data_lanes[i];
>> + csi2phy->stream_cfg.lane_cfg.data[i].pol = lane_polarities[i + 1];
>> + }
>> + csi2phy->stream_cfg.lane_cfg.clk.pos = clock_lane;
>> + csi2phy->stream_cfg.lane_cfg.clk.pol = lane_polarities[0];
>> +
>> + ret = 0;
>> +
>> +out_put:
>> + fwnode_handle_put(ep);
>> +
>> + return ret;
>> +}
>> +
>> +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;
>> + dev_set_drvdata(dev, csi2phy);
>> +
>> + 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;
>> +
>> + ret = phy_qcom_mipi_csi2_parse_routing(csi2phy);
>> + if (ret)
>> + return ret;
>> +
>> + ret = phy_qcom_mipi_csi2_attach_pm_domains(csi2phy);
>> + if (ret < 0)
>> + return dev_err_probe(dev, ret, "Failed to attach power-domain list\n");
>> +
>> + devm_pm_runtime_enable(dev);
>> +
>> + 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");
>
> Maybe it would be simpler to use devm_pm_clk_create +
> of_pm_clk_add_clks ? then the clocks would be automatically handled
> from the PM core on suspend/resume. And you wouldn't have to specify
> and handle per-platform specific clock names/count (if such strict
> checking is not necessary).
I think TBH, I'd rather keep control of the ordering of voting /
clock-enables in the driver.
>> +
>> + csi2phy->timer_clk = devm_clk_get(dev, csi2phy->soc_cfg->timer_clk);
>> + if (IS_ERR(csi2phy->timer_clk)) {
>> + return dev_err_probe(dev, PTR_ERR(csi2phy->timer_clk),
>> + "Failed to get timer clock\n");
>> + }
>> +
>> + ret = devm_pm_opp_set_clkname(dev, csi2phy->soc_cfg->opp_clk);
>
> Is there any reason for the clock name to differ from "core"? Since
> you're introducing a fresh driver and binding, it might be better to
> avoid making the clock naming explicitly dependent on the SoC or
> platform.
This is the correct call though. The YAML mandates the name, so
hard-coding in the driver is just an expression of that mandate or
rather a restatement of it.
I'll use core and timer directly.
>> + 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, qcom_csi2_phy_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..e7c1ce00916e3
>> --- /dev/null
>> +++ b/drivers/phy/qualcomm/phy-qcom-mipi-csi2.h
>> @@ -0,0 +1,95 @@
>> +/* 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
>> +#define CSI2_DEFAULT_CLK_LN 7
>> +
>> +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 {
>> + 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;
>> +};
>> +
>> +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 char ** const genpd_names;
>> + const unsigned int num_genpd_names;
>> +};
>> +
>> +struct mipi_csi2phy_device {
>> + struct device *dev;
>> + u8 phy_mode;
>> +
>> + struct phy *phy;
>> + void __iomem *base;
>> +
>> + struct clk_bulk_data *clks;
>> + struct clk *timer_clk;
>> + u32 timer_clk_rate;
>> +
>> + struct regulator_bulk_data *supplies;
>> + struct dev_pm_domain_list *pd_list;
>> +
>> + 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.54.0
>>
>>
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH] phy: fsl-imx8mq-usb: fix vbus regulator leak on error paths
From: Frank Li @ 2026-06-02 13:22 UTC (permalink / raw)
To: Felix Gu
Cc: Vinod Koul, Neil Armstrong, Sascha Hauer, Pengutronix Kernel Team,
Fabio Estevam, Xu Yang, linux-phy, imx, linux-arm-kernel,
linux-kernel
In-Reply-To: <20260602-imx9mq-v1-1-e58b912923d2@gmail.com>
On Tue, Jun 02, 2026 at 09:14:33PM +0800, Felix Gu wrote:
> In imx8mq_phy_power_on(), if clk_prepare_enable() fails after
> regulator_enable() has succeeded, the vbus regulator is left
> enabled. The same issue exists for the alt_clk error path.
>
> Fix both by converting to the standard goto-based cleanup
> pattern, ensuring the regulator is disabled when any
> subsequent step fails.
>
> Fixes: 3b64ea4768e7 ("phy: fsl-imx8mq-usb: support alternate reference clock")
> Signed-off-by: Felix Gu <ustc.gu@gmail.com>
> ---
Reviewed-by: Frank Li <Frank.Li@nxp.com>
> drivers/phy/freescale/phy-fsl-imx8mq-usb.c | 15 ++++++++++-----
> 1 file changed, 10 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/phy/freescale/phy-fsl-imx8mq-usb.c b/drivers/phy/freescale/phy-fsl-imx8mq-usb.c
> index b05d80e849a1..e45e4f16057e 100644
> --- a/drivers/phy/freescale/phy-fsl-imx8mq-usb.c
> +++ b/drivers/phy/freescale/phy-fsl-imx8mq-usb.c
> @@ -627,13 +627,11 @@ static int imx8mq_phy_power_on(struct phy *phy)
>
> ret = clk_prepare_enable(imx_phy->clk);
> if (ret)
> - return ret;
> + goto disable_vbus;
>
> ret = clk_prepare_enable(imx_phy->alt_clk);
> - if (ret) {
> - clk_disable_unprepare(imx_phy->clk);
> - return ret;
> - }
> + if (ret)
> + goto disable_clk;
>
> /* Disable rx term override */
> value = readl(imx_phy->base + PHY_CTRL6);
> @@ -641,6 +639,13 @@ static int imx8mq_phy_power_on(struct phy *phy)
> writel(value, imx_phy->base + PHY_CTRL6);
>
> return 0;
> +
> +disable_clk:
> + clk_disable_unprepare(imx_phy->clk);
> +disable_vbus:
> + regulator_disable(imx_phy->vbus);
> +
> + return ret;
> }
>
> static int imx8mq_phy_power_off(struct phy *phy)
>
> ---
> base-commit: 08484c504b55a98bd100527fbe10a3caf55ff3ff
> change-id: 20260602-imx9mq-30169239b590
>
> Best regards,
> --
> Felix Gu <ustc.gu@gmail.com>
>
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* [PATCH] phy: fsl-imx8mq-usb: fix vbus regulator leak on error paths
From: Felix Gu @ 2026-06-02 13:14 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Frank Li, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Xu Yang
Cc: linux-phy, imx, linux-arm-kernel, linux-kernel, Felix Gu
In imx8mq_phy_power_on(), if clk_prepare_enable() fails after
regulator_enable() has succeeded, the vbus regulator is left
enabled. The same issue exists for the alt_clk error path.
Fix both by converting to the standard goto-based cleanup
pattern, ensuring the regulator is disabled when any
subsequent step fails.
Fixes: 3b64ea4768e7 ("phy: fsl-imx8mq-usb: support alternate reference clock")
Signed-off-by: Felix Gu <ustc.gu@gmail.com>
---
drivers/phy/freescale/phy-fsl-imx8mq-usb.c | 15 ++++++++++-----
1 file changed, 10 insertions(+), 5 deletions(-)
diff --git a/drivers/phy/freescale/phy-fsl-imx8mq-usb.c b/drivers/phy/freescale/phy-fsl-imx8mq-usb.c
index b05d80e849a1..e45e4f16057e 100644
--- a/drivers/phy/freescale/phy-fsl-imx8mq-usb.c
+++ b/drivers/phy/freescale/phy-fsl-imx8mq-usb.c
@@ -627,13 +627,11 @@ static int imx8mq_phy_power_on(struct phy *phy)
ret = clk_prepare_enable(imx_phy->clk);
if (ret)
- return ret;
+ goto disable_vbus;
ret = clk_prepare_enable(imx_phy->alt_clk);
- if (ret) {
- clk_disable_unprepare(imx_phy->clk);
- return ret;
- }
+ if (ret)
+ goto disable_clk;
/* Disable rx term override */
value = readl(imx_phy->base + PHY_CTRL6);
@@ -641,6 +639,13 @@ static int imx8mq_phy_power_on(struct phy *phy)
writel(value, imx_phy->base + PHY_CTRL6);
return 0;
+
+disable_clk:
+ clk_disable_unprepare(imx_phy->clk);
+disable_vbus:
+ regulator_disable(imx_phy->vbus);
+
+ return ret;
}
static int imx8mq_phy_power_off(struct phy *phy)
---
base-commit: 08484c504b55a98bd100527fbe10a3caf55ff3ff
change-id: 20260602-imx9mq-30169239b590
Best regards,
--
Felix Gu <ustc.gu@gmail.com>
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* Re: [PATCH 1/2] dt-bindings: phy: econet: Document EN751221 USB PHY
From: Caleb James DeLisle @ 2026-06-02 12:40 UTC (permalink / raw)
To: Rob Herring
Cc: linux-mips, vkoul, neil.armstrong, krzk+dt, conor+dt, linux-phy,
devicetree, linux-kernel
In-Reply-To: <20260601225805.GA129257-robh@kernel.org>
On 02/06/2026 00:58, Rob Herring wrote:
> On Mon, May 18, 2026 at 02:13:42PM +0000, Caleb James DeLisle wrote:
>> Document the USB PHY devices which appear in EcoNet EN751221, EN751627,
>> and EN7528 based SoCs.
>>
>> Signed-off-by: Caleb James DeLisle <cjd@cjdns.fr>
>> ---
>> .../bindings/phy/econet,en751221-usb-phy.yaml | 128 ++++++++++++++++++
>> MAINTAINERS | 6 +
>> 2 files changed, 134 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/phy/econet,en751221-usb-phy.yaml
>>
>> diff --git a/Documentation/devicetree/bindings/phy/econet,en751221-usb-phy.yaml b/Documentation/devicetree/bindings/phy/econet,en751221-usb-phy.yaml
>> new file mode 100644
>> index 000000000000..a44f59601747
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/phy/econet,en751221-usb-phy.yaml
>> @@ -0,0 +1,128 @@
>> +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
>> +# Copyright (C) 2024 EcoNet
>> +%YAML 1.2
>> +---
>> +$id: http://devicetree.org/schemas/phy/econet,en751221-usb-phy.yaml#
>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>> +
>> +title: EcoNet EN751221 USB PHY
>> +
>> +maintainers:
>> + - Caleb James DeLisle <cjd@cjdns.fr>
>> +
>> +description: |
> Don't need '|' unless there is formatting to preserve.
>
>> + USB PHY controller found on EcoNet EN751221 SoCs as well as on EN751627 and
>> + EN7528. These devices generally have two ports, one of which is a USB 3.0,
>> + and the other is USB 2.0. The USB 3.0 port is driven by one of two PHY
>> + blocks, depending on whether the connected device has negotiated USB 3.0 or
>> + 2.0. These PHYs are also used on other EcoNet silicon in varying
>> + configurations, such as only port 0 (the USB 3.0 port), or only port 1 (the
>> + USB 2.0 port).
>> +
>> +properties:
>> + compatible:
>> + enum:
>> + - econet,en751221-usb-phy
>> + - econet,en751627-usb-phy
>> + - econet,en7528-usb-phy
>> +
>> + reg:
>> + maxItems: 1
>> +
>> + "#address-cells": true
>> + "#size-cells": true
>> + ranges: true
>> +
>> + clocks:
>> + maxItems: 1
>> + description: |
>> + Crystal oscillator clock source. EcoNet devices run at either 20Mhz or
>> + 25Mhz. 25Mhz devices require additional tuning in the USB 3.0 PHY.
>> +
>> + clock-names:
>> + items:
>> + - const: xtal
>> +
>> +patternProperties:
>> + "^usb-phy@[0-9a-f]+$":
>> + type: object
>> + description: USB 2.0 or 3.0 PHY sub-node.
>> +
>> + properties:
>> + compatible:
>> + enum:
>> + - econet,usb2-phy
>> + - econet,usb3-phy
>> +
>> + reg:
>> + maxItems: 1
>> +
>> + resets:
>> + maxItems: 1
>> +
>> + econet,usb-port-id:
>> + $ref: /schemas/types.yaml#/definitions/uint32
>> + enum: [0, 1]
>> + description: |
>> + Physical port number. Since USB 3.0 requires a second PHY for the 2.0
>> + fallback, multiple PHYs can map to the same physical port.
> What is special about this platform needing this property.
For measuring the frequency to set the slew rate, we need to know which
port we're working with. Also the hardware revision has to be set
differently on port 1 than port 0.
> Lots of
> platforms have 2 phys for USB 2.0 and 3.0 yet don't need a property
> like this.
I copied the idea from google,extcon-usbc-cros-ec which uses
google,usb-port-id.
> Can't you figure out which phys are the same USB port by the
> USB controller 'phys' property which would define that?
Unfortunately the Mediatek driver handles all of the ports and only uses
one DT node. I suppose I could encode the information in the compatible
of the subnode, something like:
usb-phy@1fa80000 {
compatible = "econet,en751221-usb-phy";
...
usb-phy@1fa80800 {
compatible = "econet,usb2-port0-phy";
...
};
...
};
I'm not really sure which way is more correct. If you have an opinion on this, let me know.
Thanks,
Caleb
>
> Rob
>
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* [PATCH 2/2] phy: qcom: qmp-pcie: Add IPQ9650 PCIe PHY support
From: Kathiravan Thirumoorthy @ 2026-06-02 9:10 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
Conor Dooley
Cc: linux-arm-msm, linux-phy, devicetree, linux-kernel,
Kathiravan Thirumoorthy
In-Reply-To: <20260602-ipq9650_pcie_phy-v1-0-d8c32a36dbd9@oss.qualcomm.com>
The IPQ9650 platform has three Gen3 2-lane PCIe controllers and two Gen3
1-lane PCIe controllers. The PHY instances also require the on-chip refgen
supply.
Add the IPQ9650 Gen3 x1 and x2 QMP PCIe PHY configurations, including the
refgen regulator supply.
Signed-off-by: Kathiravan Thirumoorthy <kathiravan.thirumoorthy@oss.qualcomm.com>
---
drivers/phy/qualcomm/phy-qcom-qmp-pcie.c | 220 +++++++++++++++++++++++++++++++
1 file changed, 220 insertions(+)
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c
index 75afbd15aaf4..459e54c2b60d 100644
--- a/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c
@@ -751,6 +751,152 @@ static const struct qmp_phy_init_tbl ipq9574_gen3x2_pcie_pcs_misc_tbl[] = {
QMP_PHY_INIT_CFG(QPHY_V5_PCS_PCIE_ENDPOINT_REFCLK_DRIVE, 0xc1),
};
+static const struct qmp_phy_init_tbl ipq9650_pcie_serdes_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIAS_EN_CLKBUFLR_EN, 0x14),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIAS_EN_CTRL_BY_PSM, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_CLK_SELECT, 0x34),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_IVCO, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_CMN_MODE, 0x14),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_CMN_CONFIG, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP_EN, 0x42),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_RESETSM_CNTRL, 0x20),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE_MAP, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE_TIMER1, 0xff),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE_TIMER2, 0x3f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE0, 0x68),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START3_MODE0, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START2_MODE0, 0xaa),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START1_MODE0, 0xab),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE0, 0x14),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE0, 0xd4),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_CP_CTRL_MODE0, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_RCTRL_MODE0, 0x16),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_CCTRL_MODE0, 0x36),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_INTEGLOOP_GAIN1_MODE0, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_INTEGLOOP_GAIN0_MODE0, 0x3f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE2_MODE0, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE1_MODE0, 0x24),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SVS_MODE_CLK_SEL, 0x05),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYS_CLK_CTRL, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYSCLK_BUF_ENABLE, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYSCLK_EN_SEL, 0x08),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_BG_TIMER, 0x0b),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_HSCLK_SEL, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_CLK_ENABLE1, 0x90),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE1, 0x53),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START3_MODE1, 0x05),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START2_MODE1, 0x55),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START1_MODE1, 0x55),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE1, 0x29),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE1, 0xaa),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_CP_CTRL_MODE1, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_RCTRL_MODE1, 0x16),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_CCTRL_MODE1, 0x36),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_INTEGLOOP_GAIN1_MODE1, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_INTEGLOOP_GAIN0_MODE1, 0x3f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE2_MODE1, 0x03),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE1_MODE1, 0xb4),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SVS_MODE_CLK_SEL, 0x05),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_CORE_CLK_EN, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_EN_CENTER, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_PER2, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_PER1, 0x7d),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE2_MODE0, 0x05),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE1_MODE0, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE2_MODE1, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE1_MODE1, 0x08),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_CORECLK_DIV_MODE1, 0x08),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_HSCLK_SEL, 0x11),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE0, 0x18),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE0, 0xa2),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE1, 0x13),
+ QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE1, 0xb5),
+};
+
+static const struct qmp_phy_init_tbl ipq9650_pcie_tx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_OFFSET_TX, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_OFFSET_RX, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_RCV_DETECT_LVL_2, 0x12),
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_HIGHZ_DRVR_EN, 0x10),
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_LANE_MODE_1, 0xb5),
+ QMP_PHY_INIT_CFG(QSERDES_V4_TX_PI_QEC_CTRL, 0x00),
+};
+
+static const struct qmp_phy_init_tbl ipq9650_pcie_rx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_AUX_DATA_TCOARSE_TFINE, 0x30),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_GM_CAL, 0x11),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FO_GAIN, 0x0c),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_GAIN, 0x03),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_CNTRL, 0x03),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_ENABLES, 0x1c),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_DEGLITCH_CNTRL, 0x1e),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL1, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0e),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4a),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL4, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_VGA_GAIN2_LSB, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_VGA_GAIN2_MSB, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_VGA_CAL_CNTRL1, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_VGA_CAL_CNTRL2, 0x07),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_DCC_CTRL1, 0x0c),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_CTLE_POST_CAL_OFFSET, 0x38),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_1, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_2, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_TX_ADAPT_PRE_THRESH1, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_TX_ADAPT_PRE_THRESH2, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_TX_ADAPT_POST_THRESH, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_TX_ADAPT_MAIN_THRESH, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_EN_TIMER, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x7f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_PI_CONTROLS, 0x70),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x17),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_LOW, 0xd4),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH, 0x54),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH2, 0xdb),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH3, 0x3b),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH4, 0x31),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_LOW, 0x24),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH, 0xe4),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH2, 0xec),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH3, 0x3b),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH4, 0x36),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_LOW, 0xff),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH, 0x3f),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH2, 0xff),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH3, 0x67),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH4, 0x35),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_HIGH, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_LOW, 0xc0),
+};
+
+static const struct qmp_phy_init_tbl ipq9650_pcie_pcs_tbl[] = {
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_REFGEN_REQ_CONFIG1, 0x25),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_G12S1_TXDEEMPH_M3P5DB, 0x10),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_RATE_SLEW_CNTRL1, 0x03),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG2, 0x0f),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_P2U3_WAKEUP_DLY_TIME_AUXCLK_L, 0x01),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_RX_DCC_CAL_CONFIG, 0x00),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_RX_SIGDET_LVL, 0x77),
+};
+
+static const struct qmp_phy_init_tbl ipq9650_pcie_pcs_misc_tbl[] = {
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_POWER_STATE_CONFIG2, 0x0d),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_POWER_STATE_CONFIG4, 0x07),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_ENDPOINT_REFCLK_DRIVE, 0xc1),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_L1P1_WAKEUP_DLY_TIME_AUXCLK_L, 0x01),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_L1P2_WAKEUP_DLY_TIME_AUXCLK_L, 0x01),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_INT_AUX_CLK_CONFIG1, 0x00),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_OSC_DTCT_ACTIONS, 0x00),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_EQ_CONFIG1, 0x1c),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_EQ_CONFIG2, 0x0b),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_PRESET_P2_P3_POST, 0x34),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_PRESET_P6_P7_PRE, 0x33),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_PRESET_P6_P7_POST, 0x40),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_PRESET_P10_PRE, 0x00),
+ QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_PRESET_P10_POST, 0x58),
+};
+
static const struct qmp_phy_init_tbl qcs615_pcie_serdes_tbl[] = {
QMP_PHY_INIT_CFG(QSERDES_V2_COM_BIAS_EN_CLKBUFLR_EN, 0x18),
QMP_PHY_INIT_CFG(QSERDES_V2_COM_CLK_ENABLE1, 0x10),
@@ -3378,6 +3524,10 @@ static const char * const qmp_phy_vreg_l[] = {
"vdda-phy", "vdda-pll",
};
+static const char * const ipq9650_qmp_phy_vreg_l[] = {
+ "refgen",
+};
+
static const char * const sm8550_qmp_phy_vreg_l[] = {
"vdda-phy", "vdda-pll", "vdda-qref",
};
@@ -3421,6 +3571,14 @@ static const struct qmp_pcie_offsets qmp_pcie_offsets_v4x1 = {
.rx = 0x0400,
};
+static const struct qmp_pcie_offsets qmp_pcie_offsets_9650_v4x1 = {
+ .serdes = 0,
+ .pcs = 0x0600,
+ .pcs_misc = 0x0a00,
+ .tx = 0x0200,
+ .rx = 0x0400,
+};
+
static const struct qmp_pcie_offsets qmp_pcie_offsets_v4x2 = {
.serdes = 0,
.pcs = 0x0a00,
@@ -3669,6 +3827,62 @@ static const struct qmp_phy_cfg ipq9574_gen3x2_pciephy_cfg = {
.pipe_clock_rate = 250000000,
};
+static const struct qmp_phy_cfg ipq9650_gen3x1_pciephy_cfg = {
+ .lanes = 1,
+
+ .offsets = &qmp_pcie_offsets_9650_v4x1,
+
+ .tbls = {
+ .serdes = ipq9650_pcie_serdes_tbl,
+ .serdes_num = ARRAY_SIZE(ipq9650_pcie_serdes_tbl),
+ .tx = ipq9650_pcie_tx_tbl,
+ .tx_num = ARRAY_SIZE(ipq9650_pcie_tx_tbl),
+ .rx = ipq9650_pcie_rx_tbl,
+ .rx_num = ARRAY_SIZE(ipq9650_pcie_rx_tbl),
+ .pcs = ipq9650_pcie_pcs_tbl,
+ .pcs_num = ARRAY_SIZE(ipq9650_pcie_pcs_tbl),
+ .pcs_misc = ipq9650_pcie_pcs_misc_tbl,
+ .pcs_misc_num = ARRAY_SIZE(ipq9650_pcie_pcs_misc_tbl),
+ },
+ .reset_list = ipq8074_pciephy_reset_l,
+ .num_resets = ARRAY_SIZE(ipq8074_pciephy_reset_l),
+ .vreg_list = ipq9650_qmp_phy_vreg_l,
+ .num_vregs = ARRAY_SIZE(ipq9650_qmp_phy_vreg_l),
+ .regs = pciephy_v4_regs_layout,
+
+ .pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL,
+ .phy_status = PHYSTATUS,
+ .pipe_clock_rate = 250000000,
+};
+
+static const struct qmp_phy_cfg ipq9650_gen3x2_pciephy_cfg = {
+ .lanes = 2,
+
+ .offsets = &qmp_pcie_offsets_v4x2,
+
+ .tbls = {
+ .serdes = ipq9650_pcie_serdes_tbl,
+ .serdes_num = ARRAY_SIZE(ipq9650_pcie_serdes_tbl),
+ .tx = ipq9650_pcie_tx_tbl,
+ .tx_num = ARRAY_SIZE(ipq9650_pcie_tx_tbl),
+ .rx = ipq9650_pcie_rx_tbl,
+ .rx_num = ARRAY_SIZE(ipq9650_pcie_rx_tbl),
+ .pcs = ipq9650_pcie_pcs_tbl,
+ .pcs_num = ARRAY_SIZE(ipq9650_pcie_pcs_tbl),
+ .pcs_misc = ipq9650_pcie_pcs_misc_tbl,
+ .pcs_misc_num = ARRAY_SIZE(ipq9650_pcie_pcs_misc_tbl),
+ },
+ .reset_list = ipq8074_pciephy_reset_l,
+ .num_resets = ARRAY_SIZE(ipq8074_pciephy_reset_l),
+ .vreg_list = ipq9650_qmp_phy_vreg_l,
+ .num_vregs = ARRAY_SIZE(ipq9650_qmp_phy_vreg_l),
+ .regs = pciephy_v4_regs_layout,
+
+ .pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL,
+ .phy_status = PHYSTATUS,
+ .pipe_clock_rate = 250000000,
+};
+
static const struct qmp_phy_cfg qcs615_pciephy_cfg = {
.lanes = 1,
@@ -5419,6 +5633,12 @@ static const struct of_device_id qmp_pcie_of_match_table[] = {
}, {
.compatible = "qcom,ipq9574-qmp-gen3x2-pcie-phy",
.data = &ipq9574_gen3x2_pciephy_cfg,
+ }, {
+ .compatible = "qcom,ipq9650-qmp-gen3x1-pcie-phy",
+ .data = &ipq9650_gen3x1_pciephy_cfg,
+ }, {
+ .compatible = "qcom,ipq9650-qmp-gen3x2-pcie-phy",
+ .data = &ipq9650_gen3x2_pciephy_cfg,
}, {
.compatible = "qcom,kaanapali-qmp-gen3x2-pcie-phy",
.data = &qmp_v8_gen3x2_pciephy_cfg,
--
2.34.1
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* [PATCH 1/2] dt-bindings: phy: qcom,ipq8074-qmp-pcie: document IPQ9650 QMP PCIe PHYs
From: Kathiravan Thirumoorthy @ 2026-06-02 9:10 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
Conor Dooley
Cc: linux-arm-msm, linux-phy, devicetree, linux-kernel,
Kathiravan Thirumoorthy
In-Reply-To: <20260602-ipq9650_pcie_phy-v1-0-d8c32a36dbd9@oss.qualcomm.com>
Document the single-lane and dual-lane QMP PCIe PHYs found on the
IPQ9650 SoC.
Unlike the PHYs in the other supported IPQ SoCs, the IPQ9650 PHYs require
the on-chip refgen supply to power up. Add the refgen-supply property
and require it only for the IPQ9650 compatibles.
Signed-off-by: Kathiravan Thirumoorthy <kathiravan.thirumoorthy@oss.qualcomm.com>
---
.../bindings/phy/qcom,ipq8074-qmp-pcie-phy.yaml | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/Documentation/devicetree/bindings/phy/qcom,ipq8074-qmp-pcie-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,ipq8074-qmp-pcie-phy.yaml
index f60804687412..048b2e3ff0ef 100644
--- a/Documentation/devicetree/bindings/phy/qcom,ipq8074-qmp-pcie-phy.yaml
+++ b/Documentation/devicetree/bindings/phy/qcom,ipq8074-qmp-pcie-phy.yaml
@@ -22,6 +22,8 @@ properties:
- qcom,ipq8074-qmp-pcie-phy
- qcom,ipq9574-qmp-gen3x1-pcie-phy
- qcom,ipq9574-qmp-gen3x2-pcie-phy
+ - qcom,ipq9650-qmp-gen3x1-pcie-phy
+ - qcom,ipq9650-qmp-gen3x2-pcie-phy
- items:
- enum:
- qcom,ipq5424-qmp-gen3x1-pcie-phy
@@ -61,6 +63,8 @@ properties:
"#phy-cells":
const: 0
+ refgen-supply: true
+
required:
- compatible
- reg
@@ -72,6 +76,21 @@ required:
- clock-output-names
- "#phy-cells"
+allOf:
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - qcom,ipq9650-qmp-gen3x1-pcie-phy
+ - qcom,ipq9650-qmp-gen3x2-pcie-phy
+ then:
+ required:
+ - refgen-supply
+ else:
+ properties:
+ refgen-supply: false
+
additionalProperties: false
examples:
--
2.34.1
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* [PATCH 0/2] Add support for the QMP PCIe PHYs in Qualcomm IPQ9650
From: Kathiravan Thirumoorthy @ 2026-06-02 9:10 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
Conor Dooley
Cc: linux-arm-msm, linux-phy, devicetree, linux-kernel,
Kathiravan Thirumoorthy
Qualcomm's IPQ9650 SoC has 3 Gen3 dual lane and 2 Gen3 single lane
controllers with the QMP PHYs. Unlike the PHYs in the other IPQ SoC,
refgen supply is needed to bringup the PHYs. Both single and dual lane
shares the same HW init sequence. So reuse the tables.
Document the compatible along with refgen supply and add the phy driver
support for it.
Signed-off-by: Kathiravan Thirumoorthy <kathiravan.thirumoorthy@oss.qualcomm.com>
---
Kathiravan Thirumoorthy (2):
dt-bindings: phy: qcom,ipq8074-qmp-pcie: document IPQ9650 QMP PCIe PHYs
phy: qcom: qmp-pcie: Add IPQ9650 PCIe PHY support
.../bindings/phy/qcom,ipq8074-qmp-pcie-phy.yaml | 19 ++
drivers/phy/qualcomm/phy-qcom-qmp-pcie.c | 220 +++++++++++++++++++++
2 files changed, 239 insertions(+)
---
base-commit: 08484c504b55a98bd100527fbe10a3caf55ff3ff
change-id: 20260521-ipq9650_pcie_phy-60d7df32581c
Best regards,
--
Kathiravan Thirumoorthy <kathiravan.thirumoorthy@oss.qualcomm.com>
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH v2 phy-next 13/15] dt-bindings: phy: lynx-10g: initial document
From: Vladimir Oltean @ 2026-06-02 9:03 UTC (permalink / raw)
To: Alexander Stein
Cc: linux-phy, Ioana Ciornei, Vinod Koul, Neil Armstrong,
Tanjeff Moos, linux-kernel, devicetree, Conor Dooley,
Krzysztof Kozlowski, Rob Herring
In-Reply-To: <2842502.mvXUDI8C0e@steina-w>
Hi Alexander,
On Mon, Jun 01, 2026 at 08:34:25AM +0200, Alexander Stein wrote:
> Hi,
>
> Am Freitag, 29. Mai 2026, 19:15:07 CEST schrieb Vladimir Oltean:
> > Add a schema for the 10G Lynx SerDes. This is very similar to the modern
> > form of the 28G Lynx SerDes, which is very much the intention.
> >
> > We allow both forms of #phy-cells = <1> in the top-level provider
> > and #phy-cells = <0> in the per-lane provider for more flexibility to
> > consumers, and because the kernel code is shared with the 28G Lynx which
> > already has that support for compatibility reasons.
> >
> > Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> > ---
> > Cc: devicetree@vger.kernel.org
> > Cc: Conor Dooley <conor+dt@kernel.org>
> > Cc: Krzysztof Kozlowski <krzk+dt@kernel.org>
> > Cc: Rob Herring <robh@kernel.org>
> >
> > v1->v2:
> > - move patch later in series, right before driver
> > - deliberately ignoring this Sashiko feedback:
> > https://lore.kernel.org/linux-phy/20260529125017.ifqunh52gdzhthdg@skbuf/
> > ---
> > .../devicetree/bindings/phy/fsl,lynx-10g.yaml | 131 ++++++++++++++++++
> > 1 file changed, 131 insertions(+)
> > create mode 100644 Documentation/devicetree/bindings/phy/fsl,lynx-10g.yaml
> >
> > diff --git a/Documentation/devicetree/bindings/phy/fsl,lynx-10g.yaml b/Documentation/devicetree/bindings/phy/fsl,lynx-10g.yaml
> > new file mode 100644
> > index 000000000000..993f076bba4e
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/phy/fsl,lynx-10g.yaml
> > @@ -0,0 +1,131 @@
> > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > +%YAML 1.2
> > +---
> > +$id: http://devicetree.org/schemas/phy/fsl,lynx-10g.yaml
> > +$schema: http://devicetree.org/meta-schemas/core.yaml
> > +
> > +title: Freescale Lynx 10G SerDes PHY
> > +
> > +maintainers:
> > + - Vladimir Oltean <vladimir.oltean@nxp.com>
> > +
> > +description:
> > + The 10G Lynx is a multi-protocol SerDes block which handles networking, PCIe,
> > + SATA and other high-speed interfaces. It is present on most QorIQ and
> > + Layerscape SoCs. The register map is common, but the integration is
> > + SoC-specific, with the differences consisting in register endianness, the
> > + number of lanes, protocol converters available per lane and their location in
> > + the PCCR registers. Some SoCs have multiple SerDes blocks and those differ in
> > + their protocol capabilities per lane.
> > +
> > +properties:
> > + compatible:
> > + description:
> > + There is intentionally no generic fsl,lynx-10g compatible string due to
> > + the hardware inability to report its capabilities, despite having a
> > + common register map.
> > + enum:
> > + - fsl,ls1028a-serdes
> > + - fsl,ls1046a-serdes1
> > + - fsl,ls1046a-serdes2
> > + - fsl,ls1088a-serdes1
> > + - fsl,ls1088a-serdes2
> > + - fsl,ls2088a-serdes1
> > + - fsl,ls2088a-serdes2
>
> Silly question: What about LS1043A? AFAIK it has a single serdes block.
>
> Best regards
> Alexander
My understanding is that hardware validation for LS1043A was not
budgeted for the main two features why the lynx-10g driver is necessary:
RCW override for 1G <-> 10G dynamic protocol switching and KR link
training. As such, this SoC isn't supported by the SerDes driver in the
NXP BSP either. With the exception of 1G <-> 2.5G minor protocol
switching, having a lynx-10g driver would not be very useful for the
LS1043A as is, without a procedure from h/w validation to do RCW
override.
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH v6 2/2] phy: add basic support for NXPs TJA1145 CAN transceiver
From: sashiko-bot @ 2026-06-02 8:38 UTC (permalink / raw)
To: Dimitri Fedrau via B4 Relay
Cc: olteanv, vkoul, neil.armstrong, linux-phy, devicetree, conor+dt,
robh
In-Reply-To: <20260602-tja1145-support-v6-2-0e0ffc8ee63d@liebherr.com>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] If setting the CAN operating mode fails during `tja1145_phy_power_on`, the PHY hardware is left in normal operating mode without rolling back to standby mode.
--
commit 4b999361552fc4af356fbf9fbd3b1ca5f2dd59bf
Author: Dimitri Fedrau <dimitri.fedrau@liebherr.com>
phy: add basic support for NXPs TJA1145 CAN transceiver
This commit adds basic driver support for the NXP TJA1145 CAN transceiver.
It enables bringing the PHY up or down by switching between normal and
standby modes using SPI commands.
> diff --git a/drivers/phy/phy-nxp-tja1145.c b/drivers/phy/phy-nxp-tja1145.c
> new file mode 100644
> index 0000000000000..1e8bd169743ab
> --- /dev/null
> +++ b/drivers/phy/phy-nxp-tja1145.c
[ ... ]
> +static int tja1145_phy_power_on(struct phy *phy)
> +{
> + struct regmap *map = phy_get_drvdata(phy);
> + int ret;
> +
> + /*
> + * Switch operating mode to normal which is the active operating mode.
> + * In this mode, the device is fully operational.
> + */
> + ret = regmap_update_bits(map, TJA1145_MODE_CTRL, TJA1145_MODE_CTRL_MC,
> + TJA1145_MODE_CTRL_NORMAL);
> + if (ret)
> + return ret;
> +
> + /*
> + * Switch to CAN operating mode active where the PHY can transmit and
> + * receive data.
> + */
> + return regmap_update_bits(map, TJA1145_CAN_CTRL, TJA1145_CAN_CTRL_CMC,
> + TJA1145_CAN_CTRL_ACTIVE);
> +}
[Severity: Medium]
If the second regmap_update_bits call fails to set TJA1145_CAN_CTRL, does
this leave the hardware in normal mode without reverting to standby?
Because tja1145_phy_power_on would return an error here, the PHY framework
will not consider the PHY powered on and will not invoke
tja1145_phy_power_off later. Could this result in the hardware being
permanently left in a higher-power state?
Should the driver intercept this error and attempt to roll back
TJA1145_MODE_CTRL to TJA1145_MODE_CTRL_STBY before returning?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260602-tja1145-support-v6-0-0e0ffc8ee63d@liebherr.com?part=2
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox