* Re: [PATCH v5 2/3] phy: qcom: qmp-pcie: Add QMP PCIe Multi-PHY driver
From: Qiang Yu @ 2026-07-20 3:36 UTC (permalink / raw)
To: Bjorn Andersson
Cc: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Manivannan Sadhasivam, Philipp Zabel, Konrad Dybcio,
linux-arm-msm, linux-phy, devicetree, linux-kernel
In-Reply-To: <alqrbpi3EbvV9RnC@baldur>
On Fri, Jul 17, 2026 at 05:45:45PM -0500, Bjorn Andersson wrote:
> On Fri, Jul 17, 2026 at 02:58:34AM -0700, Qiang Yu wrote:
> [..]
> > diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcie-multiphy.c b/drivers/phy/qualcomm/phy-qcom-qmp-pcie-multiphy.c
> > new file mode 100644
> > index 000000000000..b79edf2b7d5f
> > --- /dev/null
> > +++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcie-multiphy.c
> > @@ -0,0 +1,770 @@
> > +// SPDX-License-Identifier: GPL-2.0-only
> > +/*
> > + * Copyright (c) 2026, Qualcomm Technologies, Inc. and/or its subsidiaries.
>
> No, there should not be a year in there.
Ack, will drop the year:
Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
>
> > + */
> > +
> [..]
> > +static int qmp_pcie_pd_power_on(struct qmp_pcie *qmp)
> > +{
> > + const struct qmp_phy_cfg *cfg = qmp->cfg;
> > + int i, ret;
> > +
> > + for (i = 0; i < cfg->num_pds; i++) {
> > + ret = pm_runtime_get_sync(qmp->pd_devs[i]);
>
> In the error path you will put pd_devs[0..i-1], but
> pm_runtime_get_sync() requires that you put pd_devs[i] as well.
>
> Please use pm_runtime_resume_and_get() instead.
Okay, will switch to pm_runtime_resume_and_get() to avoid this imbalance.
- Qiang Yu
>
> > + if (ret < 0) {
> > + dev_err(qmp->dev, "failed to power on %s domain\n",
> > + cfg->pd_names[i]);
> > + goto err_power_off;
> > + }
> > + }
> > +
> > + return 0;
> > +
> > +err_power_off:
> > + while (--i >= 0)
> > + pm_runtime_put(qmp->pd_devs[i]);
> > +
> > + return ret;
> > +}
>
> Regards,
> Bjorn
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH v5 3/3] arm64: dts: qcom: glymur: Wire PCIe3a/3b to shared Gen5x8 PHY
From: Qiang Yu @ 2026-07-20 3:44 UTC (permalink / raw)
To: Konrad Dybcio
Cc: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Manivannan Sadhasivam, Philipp Zabel,
Bjorn Andersson, Konrad Dybcio, linux-arm-msm, linux-phy,
devicetree, linux-kernel
In-Reply-To: <5f20426c-d34e-4d68-ad80-d01e2fc4e33d@oss.qualcomm.com>
On Fri, Jul 17, 2026 at 06:50:23PM +0200, Konrad Dybcio wrote:
> On 7/17/26 11:58 AM, Qiang Yu wrote:
> > Glymur's PCIe3a and PCIe3b controllers share a single Gen5x8 QMP PHY block
> > that can be bifurcated into two independent x4 links, rather than each
> > controller owning its own dedicated PHY.
> >
> > Add a pcie3_phy node describing the shared PHY block, add the missing
> > PCIe3a controller node, and point both PCIe3a's and PCIe3b's port phys
> > at &pcie3_phy (index 0 and 1 respectively) so each controller picks up
> > its half of the bifurcated PHY. Update the GCC pipe clock parent array
> > to reference the new PHY's clock outputs instead of the placeholders.
> >
> > Signed-off-by: Qiang Yu <qiang.yu@oss.qualcomm.com>
> > ---
> > arch/arm64/boot/dts/qcom/glymur-crd.dtsi | 8 +
> > arch/arm64/boot/dts/qcom/glymur.dtsi | 336 ++++++++++++++++++++++++++++++-
>
> These changes really shouldn't be in the same commit
Ack, will split this into two patches: one for the glymur.dtsi PHY node
and GCC pipe clock changes, and a separate one for the glymur-crd.dtsi
board-specific supply wiring.
>
> > 2 files changed, 342 insertions(+), 2 deletions(-)
> >
> > diff --git a/arch/arm64/boot/dts/qcom/glymur-crd.dtsi b/arch/arm64/boot/dts/qcom/glymur-crd.dtsi
> > index c12808abbfe1..e8e81bf9a6a1 100644
> > --- a/arch/arm64/boot/dts/qcom/glymur-crd.dtsi
> > +++ b/arch/arm64/boot/dts/qcom/glymur-crd.dtsi
> > @@ -750,6 +750,14 @@ &pcie3b {
> > pinctrl-names = "default";
> > };
> >
> > +&pcie3_phy {
> > + vdda-phy-supply = <&vreg_l3c_e1_0p89>;
> > + vdda-pll-supply = <&vreg_l2c_e1_1p14>;
> > +
> > + vdda-refgen0p9-supply = <&vreg_l1c_e1_0p82>;
> > + vdda-refgen1p2-supply = <&vreg_l4f_e1_1p08>;
> > +};
>
> This node is disabled
Yes, it is disabled because pcie3b is not enabled by default.
See the fake pcie3b_phy be dropped
https://lore.kernel.org/all/20260420133616.88740-2-krzysztof.kozlowski@oss.qualcomm.com/
- Qiang Yu
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH v13 2/2] phy: qcom-mipi-csi2: Add a CSI2 MIPI DPHY driver
From: Loic Poulain @ 2026-07-20 8:21 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: <20260720-x1e-csi2-phy-v13-2-160c31958863@linaro.org>
On Mon, Jul 20, 2026 at 3:14 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>
Reviewed-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
> ---
> MAINTAINERS | 10 +
> drivers/phy/qualcomm/Kconfig | 15 +
> drivers/phy/qualcomm/Makefile | 5 +
> drivers/phy/qualcomm/phy-qcom-mipi-csi2-3ph-dphy.c | 385 ++++++++++++++++++
> drivers/phy/qualcomm/phy-qcom-mipi-csi2-core.c | 449 +++++++++++++++++++++
> drivers/phy/qualcomm/phy-qcom-mipi-csi2.h | 97 +++++
> 6 files changed, 961 insertions(+)
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 15011f5752a99..a203b41475ea4 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -22296,6 +22296,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..cd6959e69bef7 100644
> --- a/drivers/phy/qualcomm/Kconfig
> +++ b/drivers/phy/qualcomm/Kconfig
> @@ -28,6 +28,21 @@ 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 PM_OPP
> + 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..966d79c98f9b2
> --- /dev/null
> +++ b/drivers/phy/qualcomm/phy-qcom-mipi-csi2-3ph-dphy.c
> @@ -0,0 +1,385 @@
> +// 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-2026 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 = 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.
> + */
> +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;
> +
> + ui_ps = div64_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);
> +
> + if ((t_hs_settle_ps / timer_period_ps) < 6)
> + return 0;
> +
> + 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;
> +
> + if (cfg->link_freq <= 0)
> + return -EINVAL;
> +
> + settle_cnt = phy_qcom_mipi_csi2_settle_cnt_calc(cfg->link_freq, csi2phy->timer_clk_rate);
> + if (!settle_cnt)
> + return -ENODEV;
> +
> + /*
> + * CSI_COMMON_CTRL5 is a physical lane power-up bitmap:
> + * - Bits [0,2,4,6] → D-PHY data lanes(LN0, LN2, LN4, LN6)
> + * - Bits [1,3,5] → C-PHY trio lanes(LN1, LN3, LN5)
> + * - Bit [7] → D-PHY clock lane(LNCK) dedicated clock enable
> + */
> + val = BIT(lane_cfg->clk.pos);
> + for (i = 0; i < cfg->num_data_lanes; i++)
> + val |= BIT(lane_cfg->data[i].pos * 2);
> +
> + writel(val, csi2phy->base +
> + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->common_regs_offset, 5));
> +
> + /* 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 * 2));
> + }
> + }
> +
> + 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",
> + "ahb"
> +};
> +
> +static const char * const x1e_supplies[] = {
> + "vdda-0p9",
> + "vdda-1p2"
> +};
> +
> +static struct mipi_csi2_genpd x1e_genpds[] = {
> + { .name = "top", .scaled = false },
> + { .name = "mmcx", .scaled = true },
> + { .name = "mx", .scaled = true },
> +};
> +
> +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),
> + .genpds = x1e_genpds,
> + .num_genpds = ARRAY_SIZE(x1e_genpds),
> +};
> 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..aae049f0ec160
> --- /dev/null
> +++ b/drivers/phy/qualcomm/phy-qcom-mipi-csi2-core.c
> @@ -0,0 +1,449 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2026, 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, pstate;
> + int 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);
> + }
> +
> + pstate = 0;
> + for (i = 0; i < csi2phy->pd_list->num_pds; i++) {
> + unsigned int perf;
> +
> + if (!csi2phy->soc_cfg->genpds[i].scaled)
> + continue;
> +
> + perf = dev_pm_opp_get_required_pstate(opp, pstate);
> + pstate += 1;
> +
> + 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--) {
> + if (!csi2phy->soc_cfg->genpds[i].scaled)
> + continue;
> +
> + 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;
> + 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(csi2phy->dev, "failed to enable clocks, %d\n", ret);
> + goto unset_rate;
> + }
> +
> + ops->reset(csi2phy);
> +
> + ops->hw_version_read(csi2phy);
> +
> + ret = ops->lanes_enable(csi2phy, &csi2phy->stream_cfg);
> + if (ret)
> + goto unset_clocks;
> +
> + return 0;
> +
> +unset_clocks:
> + clk_bulk_disable_unprepare(csi2phy->soc_cfg->num_clk,
> + csi2phy->clks);
> +
> +unset_rate:
> + dev_pm_opp_set_rate(csi2phy->dev, 0);
> +
> + for (i = 0; i < csi2phy->pd_list->num_pds; i++) {
> + if (!csi2phy->soc_cfg->genpds[i].scaled)
> + continue;
> +
> + dev_pm_genpd_set_performance_state(csi2phy->pd_list->pd_devs[i], 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);
> +
> + dev_pm_opp_set_rate(csi2phy->dev, 0);
> +
> + for (i = 0; i < csi2phy->pd_list->num_pds; i++) {
> + if (!csi2phy->soc_cfg->genpds[i].scaled)
> + continue;
> +
> + dev_pm_genpd_set_performance_state(csi2phy->pd_list->pd_devs[i], 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_count < 1 || args->args[0] != PHY_TYPE_DPHY) {
> + dev_err(csi2phy->dev, "invalid phy mode in DTB\n");
> + 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)
> +{
> + struct dev_pm_domain_attach_data pd_data = { 0 };
> + const char **pd_names;
> + int i;
> +
> + pd_names = devm_kzalloc(csi2phy->dev,
> + sizeof(char *) * csi2phy->soc_cfg->num_genpds,
> + GFP_KERNEL);
> + if (!pd_names)
> + return -ENOMEM;
> +
> + for (i = 0; i < csi2phy->soc_cfg->num_genpds; i++)
> + pd_names[i] = csi2phy->soc_cfg->genpds[i].name;
> +
> + pd_data.pd_names = pd_names;
> + pd_data.num_pd_names = csi2phy->soc_cfg->num_genpds;
> +
> + return devm_pm_domain_attach_list(csi2phy->dev, &pd_data,
> + &csi2phy->pd_list);
> +}
> +
> +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;
> + int i, ret;
> +
> + ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 1, 0);
> + 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;
> + }
> +
> + /* 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;
> + }
> + }
> +
> + csi2phy->stream_cfg.lane_cfg.clk.pos = CSI2_DEFAULT_CLK_LANE;
> + csi2phy->stream_cfg.lane_cfg.clk.pol = lane_polarities[0];
> +
> + for (i = 0; i < csi2phy->stream_cfg.num_data_lanes; i++) {
> + if (data_lanes[i] >= CSI2_MAX_DATA_LANES) {
> + dev_err(dev, "Invalid lane %d\n", data_lanes[i]);
> + ret = -EINVAL;
> + goto out_put;
> + }
> + csi2phy->stream_cfg.lane_cfg.data[i].pos = data_lanes[i];
> + csi2phy->stream_cfg.lane_cfg.data[i].pol = lane_polarities[i + 1];
> + }
> +
> + 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;
> +
> + ret = phy_qcom_mipi_csi2_parse_routing(csi2phy);
> + if (ret)
> + return ret;
> +
> + ret = phy_qcom_mipi_csi2_attach_pm_domains(csi2phy);
> + if (ret < 0 || csi2phy->pd_list == NULL) {
> + if (ret == 0)
> + ret = -ENODEV;
> + return dev_err_probe(dev, ret, "Failed to attach power-domain list\n");
> + }
> +
> + ret = devm_pm_runtime_enable(dev);
> + if (ret < 0)
> + return dev_err_probe(dev, ret, "Failed to enable pm runtime\n");
> +
> + ret = devm_clk_bulk_get_all(dev, &csi2phy->clks);
> + if (ret < 0)
> + return dev_err_probe(dev, ret, "Failed to get clocks\n");
> +
> + if (num_clk != ret) {
> + return dev_err_probe(dev, -ENODEV, "clock count %d expected %d\n",
> + ret, num_clk);
> + }
> +
> + for (i = 0; i < num_clk; i++) {
> + if (!csi2phy->clks[i].id)
> + return dev_err_probe(dev, -EINVAL, "Missing clock-names\n");
> +
> + if (!strcmp(csi2phy->clks[i].id, "timer")) {
> + csi2phy->timer_clk = csi2phy->clks[i].clk;
> + break;
> + }
> + }
> + if (!csi2phy->timer_clk)
> + return dev_err_probe(dev, -ENODEV, "no timer clock\n");
> +
> + ret = devm_pm_opp_set_clkname(dev, "core");
> + if (ret)
> + return dev_err_probe(dev, ret, "Failed to set opp clkname\n");
> +
> + ret = devm_pm_opp_of_add_table(dev);
> + if (ret)
> + 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 <bod@kernel.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..7e55ae0073704
> --- /dev/null
> +++ b/drivers/phy/qualcomm/phy-qcom-mipi-csi2.h
> @@ -0,0 +1,97 @@
> +/* 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_LANE 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_csi2_genpd {
> + const char *name;
> + bool scaled;
> +};
> +
> +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 struct mipi_csi2_genpd *genpds;
> + const unsigned int num_genpds;
> +};
> +
> +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
* [PATCH] phy: fsl-lynx-10g: propagate PCVT enable errors
From: kr494167 @ 2026-07-20 10:19 UTC (permalink / raw)
To: ioana.ciornei, vladimir.oltean, vkoul
Cc: neil.armstrong, netdev, linux-phy, linux-kernel, surendra
From: surendra <kr494167@gmail.com>
lynx_10g_set_mode() currently ignores failures from
lynx_10g_lane_enable_pcvt(). It then updates the lane mode and reports
success even though the protocol converter may remain disabled.
Propagate the error and leave the previous lane mode intact so the caller
can handle the failed reconfiguration.
Signed-off-by: surendra <kr494167@gmail.com>
---
drivers/phy/freescale/phy-fsl-lynx-10g.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/phy/freescale/phy-fsl-lynx-10g.c b/drivers/phy/freescale/phy-fsl-lynx-10g.c
index 38def160ef1a..9740b08700c4 100644
--- a/drivers/phy/freescale/phy-fsl-lynx-10g.c
+++ b/drivers/phy/freescale/phy-fsl-lynx-10g.c
@@ -1231,7 +1231,9 @@ static int lynx_10g_set_mode(struct phy *phy, enum phy_mode mode, int submode)
lynx_10g_lane_change_proto_conf(lane, lane_mode);
lynx_10g_lane_remap_pll(lane, lane_mode);
- WARN_ON(lynx_10g_lane_enable_pcvt(lane, lane_mode));
+ err = lynx_10g_lane_enable_pcvt(lane, lane_mode);
+ if (err)
+ goto out;
lane->mode = lane_mode;
--
2.55.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* [PATCH v4 0/3] phy: nuvoton: extend MA35D1 USB2 PHY driver for dual-port OTG support
From: Joey Lu @ 2026-07-20 10:22 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong
Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Arnd Bergmann,
Catalin Marinas, Jacky Huang, Shan-Chun Hung, Hui-Ping Chen,
Joey Lu, linux-phy, devicetree, linux-arm-kernel, linux-kernel,
Joey Lu
The MA35D1 SoC has two USB PHY ports managed by a shared hardware block:
- PHY0 (USB0): OTG port shared between the DWC2 gadget controller and
the EHCI0/OHCI0 host controllers. A hardware mux automatically routes
USB0 signals to the correct controller based on the USB ID pin state.
- PHY1 (USB1): dedicated host-only port for EHCI1/OHCI1.
This series extends the existing phy-ma35d1-usb2.c driver and its binding
to cover both ports and add OTG role-switch support, while keeping full
backward compatibility with existing device trees.
Changes since v3:
- dt-bindings: add nuvoton,rcalcode = <7 7> example to illustrate the
optional resistor calibration property; revert redundant clock example
change (back to USBD_GATE as in the original binding).
- driver: handle -ENODEV from usb_role_switch_register() gracefully so
that probe succeeds when CONFIG_USB_ROLE_SWITCH is not enabled.
Joey Lu (3):
dt-bindings: phy: nuvoton,ma35d1-usb2-phy: extend for dual-port and
OTG
arm64: dts: nuvoton: ma35d1: add USB controllers and dual-port PHY
node
phy: nuvoton: phy-ma35d1-usb2: extend to dual-port with OTG support
.../bindings/phy/nuvoton,ma35d1-usb2-phy.yaml | 44 ++-
.../boot/dts/nuvoton/ma35d1-iot-512m.dts | 36 +++
.../boot/dts/nuvoton/ma35d1-som-256m.dts | 36 +++
arch/arm64/boot/dts/nuvoton/ma35d1.dtsi | 65 ++++
drivers/phy/nuvoton/phy-ma35d1-usb2.c | 284 +++++++++++++-----
5 files changed, 389 insertions(+), 76 deletions(-)
base-commit: dc59e4fea9d83f03bad6bddf3fa2e52491777482
--
2.43.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* [PATCH v4 1/3] dt-bindings: phy: nuvoton,ma35d1-usb2-phy: extend for dual-port and OTG
From: Joey Lu @ 2026-07-20 10:22 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong
Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Arnd Bergmann,
Catalin Marinas, Jacky Huang, Shan-Chun Hung, Hui-Ping Chen,
Joey Lu, linux-phy, devicetree, linux-arm-kernel, linux-kernel,
Joey Lu, Krzysztof Kozlowski
In-Reply-To: <20260720102222.251179-1-a0987203069@gmail.com>
The MA35D1 exposes two USB PHY ports (PHY0 and PHY1) managed by the
same hardware block (USBPMISCR register in the system-management syscon).
PHY0 is shared between the DWC2 gadget controller and EHCI0/OHCI0
through an automatic hardware mux that follows the USB ID pin; PHY1 is
dedicated to EHCI1/OHCI1. Because both ports share the same register
topology a single binding and driver should cover both.
Changing '#phy-cells' from const: 0 to enum: [0, 1] lets consumers name
the port they need while preserving backward compatibility: boards already
using '#phy-cells = <0>' continue to validate and function unchanged.
The two new optional properties:
- nuvoton,rcalcode: the resistor calibration trim code is determined at
board design time to match the PCB trace impedance.
- nuvoton,oc-active-high: the over-current detect polarity depends on
the choice of VBUS power switch.
Signed-off-by: Joey Lu <a0987203069@gmail.com>
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
---
.../bindings/phy/nuvoton,ma35d1-usb2-phy.yaml | 44 ++++++++++++++++++-
1 file changed, 42 insertions(+), 2 deletions(-)
diff --git a/Documentation/devicetree/bindings/phy/nuvoton,ma35d1-usb2-phy.yaml b/Documentation/devicetree/bindings/phy/nuvoton,ma35d1-usb2-phy.yaml
index fff858c909a0..1bd22dd0e5fc 100644
--- a/Documentation/devicetree/bindings/phy/nuvoton,ma35d1-usb2-phy.yaml
+++ b/Documentation/devicetree/bindings/phy/nuvoton,ma35d1-usb2-phy.yaml
@@ -8,6 +8,18 @@ title: Nuvoton MA35D1 USB2 phy
maintainers:
- Hui-Ping Chen <hpchen0nvt@gmail.com>
+ - Joey Lu <yclu4@nuvoton.com>
+
+description:
+ USB 2.0 PHY for the Nuvoton MA35D1 SoC. The PHY node is a standalone
+ platform device that accesses the USB PHY control registers inside the
+ system-management syscon block via the nuvoton,sys phandle.
+
+ PHY0 is the OTG port whose signals are routed to either the DWC2 gadget
+ controller or the EHCI0/OHCI0 host controller by a hardware mux that
+ follows the USB ID pin automatically.
+
+ PHY1 is a dedicated host-only port used by EHCI1/OHCI1.
properties:
compatible:
@@ -15,7 +27,12 @@ properties:
- nuvoton,ma35d1-usb2-phy
"#phy-cells":
- const: 0
+ enum: [0, 1]
+ description:
+ When 0, the node exposes PHY0 only and consumers reference it
+ with no cell argument. When 1, the single cell selects the port,
+ with 0 for the OTG port (USB0, shared with DWC2 gadget controller)
+ and 1 for the host-only port (USB1).
clocks:
maxItems: 1
@@ -23,7 +40,29 @@ properties:
nuvoton,sys:
$ref: /schemas/types.yaml#/definitions/phandle
description:
- phandle to syscon for checking the PHY clock status.
+ Phandle to the system-management syscon node providing access to the
+ USB PHY control registers.
+
+ nuvoton,rcalcode:
+ $ref: /schemas/types.yaml#/definitions/uint32-array
+ minItems: 2
+ maxItems: 2
+ items:
+ minimum: 0
+ maximum: 15
+ description:
+ Resistor calibration trim codes for PHY0 and PHY1 respectively.
+ Each 4-bit value is written to the RCALCODE field in USBPMISCR and
+ adjusts the PHY's internal termination resistance. Both entries must
+ be supplied when this property is present; when absent the hardware
+ reset default is used for each port.
+
+ nuvoton,oc-active-high:
+ type: boolean
+ description:
+ When present, the over-current detect input from the VBUS power switch
+ is treated as active-high. The default (property absent) is active-low.
+ This setting is shared by both USB host ports.
required:
- compatible
@@ -42,4 +81,5 @@ examples:
clocks = <&clk USBD_GATE>;
nuvoton,sys = <&sys>;
#phy-cells = <0>;
+ nuvoton,rcalcode = <7 7>;
};
--
2.43.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* [PATCH v4 2/3] arm64: dts: nuvoton: ma35d1: add USB controllers and dual-port PHY node
From: Joey Lu @ 2026-07-20 10:22 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong
Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Arnd Bergmann,
Catalin Marinas, Jacky Huang, Shan-Chun Hung, Hui-Ping Chen,
Joey Lu, linux-phy, devicetree, linux-arm-kernel, linux-kernel,
Joey Lu
In-Reply-To: <20260720102222.251179-1-a0987203069@gmail.com>
Add the USB subsystem nodes for the MA35D1 SoC and boards:
- usb_phy: standalone PHY node with #phy-cells = <1>, covering both
the OTG port (PHY0, index 0) and the host-only port (PHY1, index 1).
Uses nuvoton,sys phandle and HUSBH0_GATE clock per the binding.
- usb (DWC2 gadget, USB0): phys = <&usb_phy 0>, dr_mode = peripheral.
- ehci0/ohci0 (USB0 host): phys = <&usb_phy 0>, companion pairing.
- ehci1/ohci1 (USB1 host): phys = <&usb_phy 1>.
Board files (ma35d1-som-256m.dts, ma35d1-iot-512m.dts) add pinctrl for
VBUSVLD, PWREN and OVC signals and enable all USB nodes.
Signed-off-by: Joey Lu <a0987203069@gmail.com>
---
.../boot/dts/nuvoton/ma35d1-iot-512m.dts | 36 ++++++++++
.../boot/dts/nuvoton/ma35d1-som-256m.dts | 36 ++++++++++
arch/arm64/boot/dts/nuvoton/ma35d1.dtsi | 65 +++++++++++++++++++
3 files changed, 137 insertions(+)
diff --git a/arch/arm64/boot/dts/nuvoton/ma35d1-iot-512m.dts b/arch/arm64/boot/dts/nuvoton/ma35d1-iot-512m.dts
index 78534d613486..0a068abda6e0 100644
--- a/arch/arm64/boot/dts/nuvoton/ma35d1-iot-512m.dts
+++ b/arch/arm64/boot/dts/nuvoton/ma35d1-iot-512m.dts
@@ -97,6 +97,16 @@ pinctrl_uart14: uart14-pins {
power-source = <1>;
};
};
+
+ hsusb {
+ pinctrl_hsusb: hsusb-pins {
+ nuvoton,pins = <5 15 1>, /* VBUSVLD */
+ <11 12 9>, /* PWREN */
+ <11 13 9>; /* OVC */
+ bias-disable;
+ power-source = <1>;
+ };
+ };
};
&uart0 {
@@ -151,3 +161,29 @@ eth_phy1: ethernet-phy@1 {
reg = <1>;
};
};
+
+&usb_phy {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_hsusb>;
+ status = "okay";
+};
+
+&usb {
+ status = "okay";
+};
+
+&ehci0 {
+ status = "okay";
+};
+
+&ehci1 {
+ status = "okay";
+};
+
+&ohci0 {
+ status = "okay";
+};
+
+&ohci1 {
+ status = "okay";
+};
diff --git a/arch/arm64/boot/dts/nuvoton/ma35d1-som-256m.dts b/arch/arm64/boot/dts/nuvoton/ma35d1-som-256m.dts
index a029b660e8dc..7f1e6d2a1fc2 100644
--- a/arch/arm64/boot/dts/nuvoton/ma35d1-som-256m.dts
+++ b/arch/arm64/boot/dts/nuvoton/ma35d1-som-256m.dts
@@ -100,6 +100,16 @@ pinctrl_uart16: uart16-pins {
power-source = <1>;
};
};
+
+ hsusb {
+ pinctrl_hsusb: hsusb-pins {
+ nuvoton,pins = <5 15 1>, /* VBUSVLD */
+ <11 12 9>, /* PWREN */
+ <11 13 9>; /* OVC */
+ bias-disable;
+ power-source = <1>;
+ };
+ };
};
&uart0 {
@@ -153,3 +163,29 @@ eth_phy1: ethernet-phy@1 {
reg = <1>;
};
};
+
+&usb_phy {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_hsusb>;
+ status = "okay";
+};
+
+&usb {
+ status = "okay";
+};
+
+&ehci0 {
+ status = "okay";
+};
+
+&ehci1 {
+ status = "okay";
+};
+
+&ohci0 {
+ status = "okay";
+};
+
+&ohci1 {
+ status = "okay";
+};
diff --git a/arch/arm64/boot/dts/nuvoton/ma35d1.dtsi b/arch/arm64/boot/dts/nuvoton/ma35d1.dtsi
index 7228ad4735b5..5a7a39b1b514 100644
--- a/arch/arm64/boot/dts/nuvoton/ma35d1.dtsi
+++ b/arch/arm64/boot/dts/nuvoton/ma35d1.dtsi
@@ -88,6 +88,14 @@ sys: system-management@40460000 {
#reset-cells = <1>;
};
+ usb_phy: usb-phy {
+ compatible = "nuvoton,ma35d1-usb2-phy";
+ clocks = <&clk HUSBH0_GATE>;
+ nuvoton,sys = <&sys>;
+ #phy-cells = <1>;
+ status = "disabled";
+ };
+
clk: clock-controller@40460200 {
compatible = "nuvoton,ma35d1-clk";
reg = <0x00000000 0x40460200 0x0 0x100>;
@@ -431,5 +439,62 @@ mdio1: mdio {
#size-cells = <0>;
};
};
+
+ usb: usb@40200000 {
+ compatible = "snps,dwc2";
+ reg = <0x0 0x40200000 0x0 0x1000>;
+ interrupts = <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clk USBD_GATE>;
+ clock-names = "otg";
+ phys = <&usb_phy 0>;
+ phy-names = "usb2-phy";
+ dr_mode = "peripheral";
+ g-np-tx-fifo-size = <16>;
+ g-rx-fifo-size = <0x100>;
+ g-tx-fifo-size = <256 256 64 64 64 32 32 32>;
+ status = "disabled";
+ };
+
+ ehci0: usb@40140000 {
+ compatible = "generic-ehci";
+ reg = <0x0 0x40140000 0x0 0x1000>;
+ interrupts = <GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clk HUSBH0_GATE>;
+ phys = <&usb_phy 0>;
+ phy-names = "usb";
+ companion = <&ohci0>;
+ status = "disabled";
+ };
+
+ ehci1: usb@401c0000 {
+ compatible = "generic-ehci";
+ reg = <0x0 0x401c0000 0x0 0x1000>;
+ interrupts = <GIC_SPI 34 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clk HUSBH1_GATE>;
+ phys = <&usb_phy 1>;
+ phy-names = "usb";
+ companion = <&ohci1>;
+ status = "disabled";
+ };
+
+ ohci0: usb@40150000 {
+ compatible = "generic-ohci";
+ reg = <0x0 0x40150000 0x0 0x1000>;
+ interrupts = <GIC_SPI 35 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clk HUSBH0_GATE>;
+ phys = <&usb_phy 0>;
+ phy-names = "usb";
+ status = "disabled";
+ };
+
+ ohci1: usb@401d0000 {
+ compatible = "generic-ohci";
+ reg = <0x0 0x401d0000 0x0 0x1000>;
+ interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clk HUSBH1_GATE>;
+ phys = <&usb_phy 1>;
+ phy-names = "usb";
+ status = "disabled";
+ };
};
};
--
2.43.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* [PATCH v4 3/3] phy: nuvoton: phy-ma35d1-usb2: extend to dual-port with OTG support
From: Joey Lu @ 2026-07-20 10:22 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong
Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Arnd Bergmann,
Catalin Marinas, Jacky Huang, Shan-Chun Hung, Hui-Ping Chen,
Joey Lu, linux-phy, devicetree, linux-arm-kernel, linux-kernel,
Joey Lu
In-Reply-To: <20260720102222.251179-1-a0987203069@gmail.com>
PHY0 and PHY1 use the same power-on/reset sequence in USBPMISCR, with
PHY1 control bits shifted 16 positions relative to PHY0. A separate
driver for PHY1 would duplicate this logic, so the existing driver is
extended to manage both ports.
The original driver polled only DEVCKSTB after releasing PHY0 from
reset. When USB0 operates in host mode (USB ID pin floating or tied
high) only HSTCKSTB and CK12MSTB assert; DEVCKSTB never sets. Polling
exclusively for DEVCKSTB in host mode causes a 1 ms timeout on every
phy_init() call from the EHCI driver. The init callback is changed to
accept either host-mode or device-mode clock stability, whichever
asserts first.
The power_on and power_off callbacks are replaced by a single init
callback that handles PHY reset and clock-stable polling, because
there is no PHY-specific clock gate on MA35D1; the PHY analog block
derives its reference from the HXT crystal.
A read-only USB role switch is registered for PHY0 to expose the USB0
role to userspace via the standard role-switch sysfs interface. The
.set callback returns -EOPNOTSUPP because the hardware mux is fully
automatic with no software override path. When CONFIG_USB_ROLE_SWITCH
is not enabled, the registration returns -ENODEV and the driver skips
the role switch gracefully without failing probe.
Two new optional device-tree properties are implemented:
- nuvoton,rcalcode: writes per-port 4-bit resistor calibration trim
codes to the RCALCODE field in USBPMISCR.
- nuvoton,oc-active-high: sets the UHOVRCURH bit in MISCFCR0 to treat
the over-current detect input as active-high.
Signed-off-by: Joey Lu <a0987203069@gmail.com>
---
drivers/phy/nuvoton/phy-ma35d1-usb2.c | 284 +++++++++++++++++++-------
1 file changed, 210 insertions(+), 74 deletions(-)
diff --git a/drivers/phy/nuvoton/phy-ma35d1-usb2.c b/drivers/phy/nuvoton/phy-ma35d1-usb2.c
index 9a459b700ed4..2c60be0ee50f 100644
--- a/drivers/phy/nuvoton/phy-ma35d1-usb2.c
+++ b/drivers/phy/nuvoton/phy-ma35d1-usb2.c
@@ -1,11 +1,16 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * Copyright (C) 2024 Nuvoton Technology Corp.
+ * Nuvoton MA35D1 USB 2.0 PHY driver
+ *
+ * Supports PHY0 (USB0 OTG port, shared between DWC2 gadget and EHCI0/OHCI0)
+ * and PHY1 (USB1 host-only port, used by EHCI1/OHCI1). The hardware mux on
+ * PHY0 switches automatically via the USB ID pin.
+ *
+ * Copyright (C) 2026 Nuvoton Technology Corp.
*/
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/delay.h>
-#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
@@ -13,131 +18,262 @@
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
+#include <linux/usb/role.h>
-/* USB PHY Miscellaneous Control Register */
-#define MA35_SYS_REG_USBPMISCR 0x60
-#define PHY0POR BIT(0) /* PHY Power-On Reset Control Bit */
-#define PHY0SUSPEND BIT(1) /* PHY Suspend; 0: suspend, 1: operaion */
-#define PHY0COMN BIT(2) /* PHY Common Block Power-Down Control */
-#define PHY0DEVCKSTB BIT(10) /* PHY 60 MHz UTMI clock stable bit */
+#define MA35_SYS_PWRONOTP 0x04
+#define PWRONOTP_USBP0ID BIT(16)
+
+#define MA35_SYS_USBPMISCR 0x60
+#define USBPMISCR_PHY_POR(n) BIT(0 + (n) * 16)
+#define USBPMISCR_PHY_SUSPEND(n) BIT(1 + (n) * 16)
+#define USBPMISCR_PHY_COMN(n) BIT(2 + (n) * 16)
+#define USBPMISCR_PHY_HSTCKSTB(n) BIT(8 + (n) * 16)
+#define USBPMISCR_PHY_CK12MSTB(n) BIT(9 + (n) * 16)
+#define USBPMISCR_PHY_DEVCKSTB(n) BIT(10 + (n) * 16)
+/* Mask for control bits (POR, SUSPEND, COMN) */
+#define USBPMISCR_PHY_CTL_MASK(n) (0x7u << ((n) * 16))
+/* Host-mode ready */
+#define USBPMISCR_PHY_HOST_READY(n) (USBPMISCR_PHY_SUSPEND(n) | \
+ USBPMISCR_PHY_HSTCKSTB(n) | \
+ USBPMISCR_PHY_CK12MSTB(n))
+/* Device-mode ready */
+#define USBPMISCR_PHY_DEV_READY(n) (USBPMISCR_PHY_SUSPEND(n) | \
+ USBPMISCR_PHY_DEVCKSTB(n))
+/* RCALCODE: 4-bit resistor trim */
+#define USBPMISCR_RCAL_SHIFT(n) (12 + (n) * 16)
+#define USBPMISCR_RCAL_MASK(n) GENMASK(USBPMISCR_RCAL_SHIFT(n) + 3, \
+ USBPMISCR_RCAL_SHIFT(n))
+
+#define MA35_SYS_MISCFCR0 0x70
+/* USB host over-current detect polarity (shared, both ports) */
+#define MISCFCR0_UHOVRCURH BIT(12)
+
+#define MA35_PHY_NUM 2
+
+struct ma35_phy_port {
+ struct phy *phy;
+ unsigned int idx;
+};
struct ma35_usb_phy {
- struct clk *clk;
struct device *dev;
struct regmap *sysreg;
+ struct ma35_phy_port port[MA35_PHY_NUM];
+ struct usb_role_switch *role_sw;
};
-static int ma35_usb_phy_power_on(struct phy *phy)
+static int ma35_usb_phy_init(struct phy *phy)
{
- struct ma35_usb_phy *p_phy = phy_get_drvdata(phy);
+ struct ma35_phy_port *port = phy_get_drvdata(phy);
+ struct ma35_usb_phy *p = container_of(port - port->idx,
+ struct ma35_usb_phy, port[0]);
+ unsigned int n = port->idx;
unsigned int val;
int ret;
- ret = clk_prepare_enable(p_phy->clk);
- if (ret < 0) {
- dev_err(p_phy->dev, "Failed to enable PHY clock: %d\n", ret);
- return ret;
- }
+ regmap_read(p->sysreg, MA35_SYS_USBPMISCR, &val);
- regmap_read(p_phy->sysreg, MA35_SYS_REG_USBPMISCR, &val);
- if (val & PHY0SUSPEND) {
- /*
- * USB PHY0 is in operation mode already
- * make sure USB PHY 60 MHz UTMI Interface Clock ready
- */
- ret = regmap_read_poll_timeout(p_phy->sysreg, MA35_SYS_REG_USBPMISCR, val,
- val & PHY0DEVCKSTB, 10, 1000);
- if (ret == 0)
- return 0;
- }
+ if (val & USBPMISCR_PHY_SUSPEND(n))
+ return 0;
- /*
- * reset USB PHY0.
- * wait until USB PHY0 60 MHz UTMI Interface Clock ready
- */
- regmap_update_bits(p_phy->sysreg, MA35_SYS_REG_USBPMISCR, 0x7, (PHY0POR | PHY0SUSPEND));
+ regmap_update_bits(p->sysreg, MA35_SYS_USBPMISCR,
+ USBPMISCR_PHY_CTL_MASK(n),
+ USBPMISCR_PHY_POR(n) | USBPMISCR_PHY_SUSPEND(n));
udelay(20);
- /* make USB PHY0 enter operation mode */
- regmap_update_bits(p_phy->sysreg, MA35_SYS_REG_USBPMISCR, 0x7, PHY0SUSPEND);
+ regmap_update_bits(p->sysreg, MA35_SYS_USBPMISCR,
+ USBPMISCR_PHY_CTL_MASK(n),
+ USBPMISCR_PHY_SUSPEND(n));
- /* make sure USB PHY 60 MHz UTMI Interface Clock ready */
- ret = regmap_read_poll_timeout(p_phy->sysreg, MA35_SYS_REG_USBPMISCR, val,
- val & PHY0DEVCKSTB, 10, 1000);
- if (ret == -ETIMEDOUT) {
- dev_err(p_phy->dev, "Check PHY clock, Timeout: %d\n", ret);
- clk_disable_unprepare(p_phy->clk);
+ if (n == 0) {
+ ret = regmap_read_poll_timeout(p->sysreg, MA35_SYS_USBPMISCR,
+ val,
+ ((val & USBPMISCR_PHY_HOST_READY(0)) ==
+ USBPMISCR_PHY_HOST_READY(0)) ||
+ ((val & USBPMISCR_PHY_DEV_READY(0)) ==
+ USBPMISCR_PHY_DEV_READY(0)),
+ 10, 1000);
+ } else {
+ ret = regmap_read_poll_timeout(p->sysreg, MA35_SYS_USBPMISCR,
+ val,
+ (val & USBPMISCR_PHY_HOST_READY(n)) ==
+ USBPMISCR_PHY_HOST_READY(n),
+ 10, 1000);
+ }
+
+ if (ret) {
+ dev_err(p->dev, "USB PHY%u clock not stable (USBPMISCR=0x%08x)\n",
+ n, val);
return ret;
}
return 0;
}
-static int ma35_usb_phy_power_off(struct phy *phy)
+static const struct phy_ops ma35_usb_phy_ops = {
+ .init = ma35_usb_phy_init,
+ .owner = THIS_MODULE,
+};
+
+static int ma35_role_sw_set(struct usb_role_switch *sw, enum usb_role role)
+{
+ return -EOPNOTSUPP;
+}
+
+static enum usb_role ma35_role_sw_get(struct usb_role_switch *sw)
+{
+ struct ma35_usb_phy *p = usb_role_switch_get_drvdata(sw);
+ u32 val;
+
+ regmap_read(p->sysreg, MA35_SYS_PWRONOTP, &val);
+
+ return (val & PWRONOTP_USBP0ID) ? USB_ROLE_HOST : USB_ROLE_DEVICE;
+}
+
+static int ma35_role_switch_init(struct platform_device *pdev,
+ struct ma35_usb_phy *p)
{
- struct ma35_usb_phy *p_phy = phy_get_drvdata(phy);
+ struct usb_role_switch_desc sw_desc = {0};
+
+ sw_desc.set = ma35_role_sw_set;
+ sw_desc.get = ma35_role_sw_get;
+ sw_desc.allow_userspace_control = true;
+ sw_desc.driver_data = p;
+ sw_desc.fwnode = dev_fwnode(&pdev->dev);
+
+ p->role_sw = usb_role_switch_register(&pdev->dev, &sw_desc);
+ if (IS_ERR(p->role_sw)) {
+ if (PTR_ERR(p->role_sw) == -ENODEV) {
+ p->role_sw = NULL;
+ return 0;
+ }
+ return dev_err_probe(&pdev->dev, PTR_ERR(p->role_sw),
+ "failed to register role switch\n");
+ }
- clk_disable_unprepare(p_phy->clk);
return 0;
}
-static const struct phy_ops ma35_usb_phy_ops = {
- .power_on = ma35_usb_phy_power_on,
- .power_off = ma35_usb_phy_power_off,
- .owner = THIS_MODULE,
-};
+static void ma35_role_switch_exit(struct ma35_usb_phy *p)
+{
+ if (p->role_sw) {
+ usb_role_switch_unregister(p->role_sw);
+ p->role_sw = NULL;
+ }
+}
+
+static struct phy *ma35_usb_phy_xlate(struct device *dev,
+ const struct of_phandle_args *args)
+{
+ struct ma35_usb_phy *p = dev_get_drvdata(dev);
+ unsigned int idx;
+
+ if (args->args_count == 0)
+ idx = 0;
+ else
+ idx = args->args[0];
+
+ if (idx >= MA35_PHY_NUM)
+ return ERR_PTR(-EINVAL);
+
+ return p->port[idx].phy;
+}
static int ma35_usb_phy_probe(struct platform_device *pdev)
{
struct phy_provider *provider;
- struct ma35_usb_phy *p_phy;
- struct phy *phy;
+ struct ma35_usb_phy *p;
+ struct clk *clk;
+ int n, ret;
+ u32 code;
- p_phy = devm_kzalloc(&pdev->dev, sizeof(*p_phy), GFP_KERNEL);
- if (!p_phy)
+ p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL);
+ if (!p)
return -ENOMEM;
- p_phy->dev = &pdev->dev;
- platform_set_drvdata(pdev, p_phy);
+ p->dev = &pdev->dev;
+ platform_set_drvdata(pdev, p);
+
+ p->sysreg = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
+ "nuvoton,sys");
+ if (IS_ERR(p->sysreg))
+ return dev_err_probe(&pdev->dev, PTR_ERR(p->sysreg),
+ "failed to get SYS regmap\n");
- p_phy->sysreg = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "nuvoton,sys");
- if (IS_ERR(p_phy->sysreg))
- return dev_err_probe(&pdev->dev, PTR_ERR(p_phy->sysreg),
- "Failed to get SYS registers\n");
+ clk = devm_clk_get_enabled(&pdev->dev, NULL);
+ if (IS_ERR(clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(clk),
+ "failed to get clock\n");
- p_phy->clk = of_clk_get(pdev->dev.of_node, 0);
- if (IS_ERR(p_phy->clk))
- return dev_err_probe(&pdev->dev, PTR_ERR(p_phy->clk),
- "failed to find usb_phy clock\n");
+ for (n = 0; n < MA35_PHY_NUM; n++) {
+ if (of_property_read_u32_index(pdev->dev.of_node,
+ "nuvoton,rcalcode", n, &code))
+ continue;
- phy = devm_phy_create(&pdev->dev, NULL, &ma35_usb_phy_ops);
- if (IS_ERR(phy))
- return dev_err_probe(&pdev->dev, PTR_ERR(phy), "Failed to create PHY\n");
+ if (code > 15)
+ return dev_err_probe(&pdev->dev, -EINVAL,
+ "rcalcode[%d] %u out of range (0-15)\n",
+ n, code);
- phy_set_drvdata(phy, p_phy);
+ regmap_update_bits(p->sysreg, MA35_SYS_USBPMISCR,
+ USBPMISCR_RCAL_MASK(n),
+ code << USBPMISCR_RCAL_SHIFT(n));
+ }
+
+ if (of_property_read_bool(pdev->dev.of_node, "nuvoton,oc-active-high"))
+ regmap_update_bits(p->sysreg, MA35_SYS_MISCFCR0,
+ MISCFCR0_UHOVRCURH, MISCFCR0_UHOVRCURH);
+
+ for (n = 0; n < MA35_PHY_NUM; n++) {
+ p->port[n].idx = n;
+
+ p->port[n].phy = devm_phy_create(&pdev->dev, pdev->dev.of_node,
+ &ma35_usb_phy_ops);
+ if (IS_ERR(p->port[n].phy))
+ return dev_err_probe(&pdev->dev, PTR_ERR(p->port[n].phy),
+ "failed to create PHY%d\n", n);
+
+ phy_set_drvdata(p->port[n].phy, &p->port[n]);
+ }
+
+ ret = ma35_role_switch_init(pdev, p);
+ if (ret)
+ return ret;
- provider = devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate);
- if (IS_ERR(provider))
+ provider = devm_of_phy_provider_register(&pdev->dev, ma35_usb_phy_xlate);
+ if (IS_ERR(provider)) {
+ ma35_role_switch_exit(p);
return dev_err_probe(&pdev->dev, PTR_ERR(provider),
- "Failed to register PHY provider\n");
+ "failed to register PHY provider\n");
+ }
+
return 0;
}
+static void ma35_usb_phy_remove(struct platform_device *pdev)
+{
+ struct ma35_usb_phy *p = platform_get_drvdata(pdev);
+
+ ma35_role_switch_exit(p);
+}
+
static const struct of_device_id ma35_usb_phy_of_match[] = {
- { .compatible = "nuvoton,ma35d1-usb2-phy", },
- { },
+ { .compatible = "nuvoton,ma35d1-usb2-phy" },
+ { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, ma35_usb_phy_of_match);
static struct platform_driver ma35_usb_phy_driver = {
.probe = ma35_usb_phy_probe,
- .driver = {
- .name = "ma35d1-usb2-phy",
- .of_match_table = ma35_usb_phy_of_match,
+ .remove = ma35_usb_phy_remove,
+ .driver = {
+ .name = "ma35d1-usb2-phy",
+ .of_match_table = ma35_usb_phy_of_match,
},
};
module_platform_driver(ma35_usb_phy_driver);
MODULE_DESCRIPTION("Nuvoton ma35d1 USB2.0 PHY driver");
MODULE_AUTHOR("Hui-Ping Chen <hpchen0nvt@gmail.com>");
+MODULE_AUTHOR("Joey Lu <a0987203069@gmail.com>");
MODULE_LICENSE("GPL");
--
2.43.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* Re: [PATCH v4 1/3] dt-bindings: phy: nuvoton,ma35d1-usb2-phy: extend for dual-port and OTG
From: sashiko-bot @ 2026-07-20 10:28 UTC (permalink / raw)
To: Joey Lu
Cc: robh, neil.armstrong, conor+dt, vkoul, olteanv, linux-phy,
devicetree
In-Reply-To: <20260720102222.251179-2-a0987203069@gmail.com>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] The `#phy-cells` property uses an `enum` constraint instead of a `const` constraint, violating Device Tree Bindings guidelines.
--
--- Patch [1]: [PATCH v4 1/3] dt-bindings: phy: nuvoton,ma35d1-usb2-phy: extend for dual-port and OTG ---
commit 676c30318df404b9fb25b710326b359f41a5cf10
Author: Joey Lu <a0987203069@gmail.com>
dt-bindings: phy: nuvoton,ma35d1-usb2-phy: extend for dual-port and OTG
This commit updates the Nuvoton MA35D1 USB2 PHY binding to support dual ports
managed by the same hardware block. It modifies the #phy-cells property to
allow values of 0 or 1 for port selection, while preserving backward
compatibility. It also introduces optional properties for resistor calibration
trim codes and over-current detect polarity.
> diff --git a/Documentation/devicetree/bindings/phy/nuvoton,ma35d1-usb2-phy.yaml b/Documentation/devicetree/bindings/phy/nuvoton,ma35d1-usb2-phy.yaml
> index fff858c909a06..1bd22dd0e5fc5 100644
> --- a/Documentation/devicetree/bindings/phy/nuvoton,ma35d1-usb2-phy.yaml
> +++ b/Documentation/devicetree/bindings/phy/nuvoton,ma35d1-usb2-phy.yaml
[ ... ]
> @@ -15,7 +27,12 @@ properties:
> - nuvoton,ma35d1-usb2-phy
>
> "#phy-cells":
> - const: 0
> + enum: [0, 1]
[Severity: Low]
Does this violate the device tree binding subsystem rules for cell-count
properties? The guidelines specify that each cell-count property must have
a const constraint matching the hardware, rather than an enum constraint.
If the hardware requires different configurations depending on the variant
or backward compatibility, should this be enforced using conditional if-then
blocks instead of allowing either value unconditionally?
> + description:
> + When 0, the node exposes PHY0 only and consumers reference it
> + with no cell argument. When 1, the single cell selects the port,
> + with 0 for the OTG port (USB0, shared with DWC2 gadget controller)
> + and 1 for the host-only port (USB1).
[ ... ]
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260720102222.251179-1-a0987203069@gmail.com?part=1
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH v4 2/3] arm64: dts: nuvoton: ma35d1: add USB controllers and dual-port PHY node
From: sashiko-bot @ 2026-07-20 10:32 UTC (permalink / raw)
To: Joey Lu
Cc: olteanv, linux-phy, vkoul, robh, devicetree, neil.armstrong,
conor+dt
In-Reply-To: <20260720102222.251179-3-a0987203069@gmail.com>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] The `usb_phy` node is placed inside a `simple-bus` without a `reg` property, triggering a `simple_bus_reg` dtc warning.
--
--- Patch [2]: [PATCH v4 2/3] arm64: dts: nuvoton: ma35d1: add USB controllers and dual-port PHY node ---
commit cf3aea78de55506203f48556a640406a6dccb9bc
Author: Joey Lu <a0987203069@gmail.com>
arm64: dts: nuvoton: ma35d1: add USB controllers and dual-port PHY node
This commit adds the USB subsystem nodes for the MA35D1 SoC, including the
DWC2 gadget, EHCI/OHCI hosts, and a dual-port PHY node. It also updates
the board files to configure pinctrl and enable the nodes.
> diff --git a/arch/arm64/boot/dts/nuvoton/ma35d1.dtsi b/arch/arm64/boot/dts/nuvoton/ma35d1.dtsi
> index 7228ad4735b5c..5a7a39b1b5143 100644
> --- a/arch/arm64/boot/dts/nuvoton/ma35d1.dtsi
> +++ b/arch/arm64/boot/dts/nuvoton/ma35d1.dtsi
> @@ -88,6 +88,14 @@ sys: system-management@40460000 {
> #reset-cells = <1>;
> };
>
> + usb_phy: usb-phy {
[Severity: Low]
Does placing this node inside the soc simple-bus without a reg property
trigger a simple_bus_reg dtc compiler warning?
The Device Tree specification states that children of a simple-bus must
represent memory-mapped devices, so nodes without a reg or ranges property
usually trigger a warning when placed directly under a simple-bus.
> + compatible = "nuvoton,ma35d1-usb2-phy";
> + clocks = <&clk HUSBH0_GATE>;
> + nuvoton,sys = <&sys>;
> + #phy-cells = <1>;
> + status = "disabled";
> + };
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260720102222.251179-1-a0987203069@gmail.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 v4 3/3] phy: nuvoton: phy-ma35d1-usb2: extend to dual-port with OTG support
From: sashiko-bot @ 2026-07-20 10:37 UTC (permalink / raw)
To: Joey Lu
Cc: olteanv, neil.armstrong, linux-phy, conor+dt, devicetree, robh,
vkoul
In-Reply-To: <20260720102222.251179-4-a0987203069@gmail.com>
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] The driver fails to clear the MISCFCR0_UHOVRCURH hardware bit when the `nuvoton,oc-active-high` device tree property is absent, inheriting potentially incorrect bootloader state.
- [Medium] The driver ignores the return value of regmap_read(), potentially evaluating uninitialized stack memory if the read fails.
--
--- Patch [3]: [PATCH v4 3/3] phy: nuvoton: phy-ma35d1-usb2: extend to dual-port with OTG support ---
Note: The format of this review is altered due to strict recitation restrictions. Instead of inline patch quoting, a free-form summary of the findings is provided below.
Commit: 678e65678c8fb5e2363eb1d51e7a9a2159ca8dd9
Author: Joey Lu <a0987203069@gmail.com>
Subject: phy: nuvoton: phy-ma35d1-usb2: extend to dual-port with OTG support
This commit extends the driver to manage both USB ports and updates the init
polling logic to handle host-mode and device-mode clock stability. It also
registers a read-only USB role switch and adds support for the rcalcode and
oc-active-high device tree properties.
[Severity: Medium]
In ma35_usb_phy_init() and ma35_role_sw_get(), the return value of
regmap_read() is not checked before evaluating the 'val' stack variable. If
the register read fails, might the driver evaluate uninitialized memory,
causing it to return an unpredictable USB role or falsely bypass the PHY
initialization?
[Severity: High]
In ma35_usb_phy_probe(), the driver sets the MISCFCR0_UHOVRCURH bit if the
"nuvoton,oc-active-high" property is present, but lacks an else path to clear
it if the property is absent. Does this allow the hardware to incorrectly
inherit an inverted over-current polarity from the bootloader or a prior
kernel instance?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260720102222.251179-1-a0987203069@gmail.com?part=3
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH v3 0/4] phy: qcom: Introduce USB support for Hawi
From: Vinod Koul @ 2026-07-20 11:18 UTC (permalink / raw)
To: Manivannan Sadhasivam
Cc: Ronak Raheja, krzk+dt, conor+dt, robh, neil.armstrong, gregkh,
dmitry.baryshkov, konrad.dybcio, abel.vesa, wesley.cheng,
krzysztof.kozlowski, linux-arm-msm, linux-phy, devicetree,
linux-usb, linux-kernel
In-Reply-To: <chf57j2dkhhjzigpguldadfld5utepa4sxwbo7fjxk5nq37bhp@yci5agzh46ly>
On 13-07-26, 13:12, Manivannan Sadhasivam wrote:
> On Fri, May 08, 2026 at 02:32:30PM -0700, Ronak Raheja wrote:
> > This series adds USB PHY support for the Hawi SoC. It documents the
> > QMP USB3-DP combo PHY and M31 eUSB2 PHY bindings, adds the Hawi
> > compatible to the DWC3 USB binding, and adds the PHY initialization
> > sequences to the qmp-combo driver.
> >
>
> Vinod, does this series look good to you?
It doesnt apply for me, can you folks rebase and resend
--
~Vinod
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH v6 2/4] dt-bindings: phy: qcs615-qmp-usb3dp: Add support for Shikra SoC
From: Vinod Koul @ 2026-07-20 11:23 UTC (permalink / raw)
To: Krishna Kurapati, Krzysztof Kozlowski
Cc: Neil Armstrong, Rob Herring, Conor Dooley, Wesley Cheng,
Xiangxu Yin, linux-arm-msm, linux-phy, devicetree, linux-kernel
In-Reply-To: <20260712-usb-shikra-phy-v6-v6-2-1b3e51bf1541@oss.qualcomm.com>
On 12-07-26, 20:58, Krishna Kurapati wrote:
Krzysztof okay with this..?
> Declare the USB-C QMP PHY present on the Qualcomm Shikra SoC. Shikra uses
> 3 resets to be programmed before initialising the phy. As per the hardware
> documentation, the third reset is PHY_PRIM_SP0_BCR, hence naming it "phy".
>
> Also, add remote endpoints and orientation switch support for getting
> Type-C orientation information.
>
> Signed-off-by: Krishna Kurapati <krishna.kurapati@oss.qualcomm.com>
> ---
> .../bindings/phy/qcom,qcs615-qmp-usb3dp-phy.yaml | 49 +++++++++++++++++++++-
> 1 file changed, 48 insertions(+), 1 deletion(-)
>
> diff --git a/Documentation/devicetree/bindings/phy/qcom,qcs615-qmp-usb3dp-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,qcs615-qmp-usb3dp-phy.yaml
> index efb465c71c1b..98397006fa62 100644
> --- a/Documentation/devicetree/bindings/phy/qcom,qcs615-qmp-usb3dp-phy.yaml
> +++ b/Documentation/devicetree/bindings/phy/qcom,qcs615-qmp-usb3dp-phy.yaml
> @@ -18,6 +18,7 @@ properties:
> compatible:
> enum:
> - qcom,qcs615-qmp-usb3-dp-phy
> + - qcom,shikra-qmp-usb3-dp-phy
>
> reg:
> maxItems: 1
> @@ -33,12 +34,15 @@ properties:
> - const: pipe
>
> resets:
> - maxItems: 2
> + minItems: 2
> + maxItems: 3
>
> reset-names:
> + minItems: 2
> items:
> - const: phy_phy
> - const: dp_phy
> + - const: phy
>
> vdda-phy-supply: true
>
> @@ -63,6 +67,22 @@ properties:
> - description: offset of the PHY mode register
> description: Clamp and PHY mode register present in the TCSR
>
> + orientation-switch:
> + description:
> + Flag the PHY as possible handler of USB Type-C orientation switching
> + type: boolean
> +
> + ports:
> + $ref: /schemas/graph.yaml#/properties/ports
> + properties:
> + port@0:
> + $ref: /schemas/graph.yaml#/properties/port
> + description: Output endpoint of the PHY
> +
> + port@1:
> + $ref: /schemas/graph.yaml#/properties/port
> + description: Incoming endpoint from the USB controller
> +
> required:
> - compatible
> - reg
> @@ -78,6 +98,33 @@ required:
>
> additionalProperties: false
>
> +allOf:
> + - if:
> + properties:
> + compatible:
> + contains:
> + enum:
> + - qcom,shikra-qmp-usb3-dp-phy
> + then:
> + properties:
> + resets:
> + minItems: 3
> + reset-names:
> + minItems: 3
> +
> + - if:
> + properties:
> + compatible:
> + contains:
> + enum:
> + - qcom,qcs615-qmp-usb3-dp-phy
> + then:
> + properties:
> + resets:
> + maxItems: 2
> + reset-names:
> + maxItems: 2
> +
> examples:
> - |
> #include <dt-bindings/clock/qcom,qcs615-gcc.h>
>
> --
> 2.34.1
--
~Vinod
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH] phy: qcom: m31-eusb2: Fix return value of init call
From: Vinod Koul @ 2026-07-20 11:36 UTC (permalink / raw)
To: Neil Armstrong, Wesley Cheng, Melody Olvera, Dmitry Baryshkov,
Krishna Kurapati
Cc: linux-arm-msm, linux-phy, linux-kernel
In-Reply-To: <20260718-m31-eusb2-fix-v1-1-8588a1b94d76@oss.qualcomm.com>
On Sat, 18 Jul 2026 15:01:31 +0530, Krishna Kurapati wrote:
> The init call currently returns success irrespective of any failures
> during repeater init or clock enablement. Return appropriate error value
> in the init call failure path.
>
>
Applied, thanks!
[1/1] phy: qcom: m31-eusb2: Fix return value of init call
commit: 361f533a2dce2c2841fe4dc0c9d85a67117edf95
Best regards,
--
~Vinod
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: (subset) [PATCH] phy: rockchip: naneng-combphy: Always configure SSC spread direction
From: Vinod Koul @ 2026-07-20 11:36 UTC (permalink / raw)
To: Neil Armstrong, Heiko Stuebner, Shawn Lin, Alexey Charkov
Cc: linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, stable
In-Reply-To: <20260714-naneng-ssc-fix-v1-1-1c40a58061ae@flipper.net>
On Tue, 14 Jul 2026 19:41:20 +0400, Alexey Charkov wrote:
> Commit 0b31f297557f ("phy: rockchip: naneng-combphy: Consolidate SSC
> configuration") moved the SSC spread spectrum direction setup into the
> new rk_combphy_common_cfg_ssc() helper. That helper returns early when
> the 'rockchip,enable-ssc' property is absent, whereas the equivalent
> RK3568_PHYREG32 direction writes previously ran unconditionally in the
> per-type switch statements, independent of whether SSC modulation was
> actually enabled.
>
> [...]
Applied, thanks!
[1/1] phy: rockchip: naneng-combphy: Always configure SSC spread direction
commit: be2b5b17b7053fee142939076746d26b2d6c9702
Best regards,
--
~Vinod
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH v4 0/4] phy: qcom: qmp-pcie: Add PCIe PHY support for Hawi
From: Vinod Koul @ 2026-07-20 11:43 UTC (permalink / raw)
To: Neil Armstrong, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Matthew Leung
Cc: linux-arm-msm, linux-phy, devicetree, linux-kernel,
Krzysztof Kozlowski
In-Reply-To: <20260707-hawi-phy-pcie-v4-0-8d9cc6324947@oss.qualcomm.com>
On Tue, 07 Jul 2026 20:32:45 +0000, Matthew Leung wrote:
> This series adds QMP PCIe PHY support for the Qualcomm Hawi SoC. The Hawi
> platform features two PCIe PHY configurations: Gen3 x2 and Gen4 x1.
>
> The Gen3 x2 PHY uses v10 register definitions, while the Gen4 x1 PHY uses
> v10.60 register definitions.
>
> The series adds:
> - device tree bindings
> - v10 register offset headers
> - v10.60 register offset headers
> - driver support with PHY initialization tables for both configurations
>
> [...]
Applied, thanks!
[1/4] dt-bindings: phy: qcom,sc8280xp-qmp-pcie-phy: Add Hawi compatibles
commit: d15224492d1aef8b28508f23b889ee0394b07b83
[2/4] phy: qcom-qmp: Add v10 register offsets
commit: 0696cc735dc7ff53297ad2ee649ceed3b1f93d07
[3/4] phy: qcom-qmp: Add v10.60 register offsets
commit: fdfe6417e2e699890f51bcc0b772734a0431e5d2
[4/4] phy: qcom: qmp-pcie: Add QMP PCIe PHY support for Hawi
commit: 9ef4e520912e4966081e276beda628b69aac85dd
Best regards,
--
~Vinod
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH v2 0/2] phy: starfive: Fix JH7110 DPHY runtime PM cleanup
From: Vinod Koul @ 2026-07-20 11:43 UTC (permalink / raw)
To: jack.zhu, changhuang.liang, keith.zhao, neil.armstrong, Can Peng
Cc: linux-phy, linux-kernel
In-Reply-To: <20260718090054.444513-1-pengcan@kylinos.cn>
On Sat, 18 Jul 2026 17:00:52 +0800, Can Peng wrote:
> The JH7110 DPHY TX and RX drivers both enable runtime PM during probe
> without arranging for it to be disabled on later probe failures or on
> driver unbind.
>
> Use devm_pm_runtime_enable() in both drivers so the runtime PM state is
> cleaned up automatically when probe fails after runtime PM has been
> enabled and when the driver is unbound.
>
> [...]
Applied, thanks!
[1/2] phy: starfive: Fix runtime PM cleanup in JH7110 DPHY TX probe
commit: f40b0241f3a382e99c14de2f28f14a44973407c1
[2/2] phy: starfive: Fix runtime PM cleanup in JH7110 DPHY RX probe
commit: 97bed336f6a25c9d1115ca95e3aa00e05c3bc271
Best regards,
--
~Vinod
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH v2 0/2] riscv: spacemit: Add K3 PCIe/USB comb phy support
From: Vinod Koul @ 2026-07-20 11:43 UTC (permalink / raw)
To: Neil Armstrong, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Yixun Lan, Kees Cook, Gustavo A. R. Silva, Paul Walmsley,
Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Alex Elder, Ze Huang,
Inochi Amaoto
Cc: linux-phy, devicetree, linux-riscv, spacemit, linux-kernel,
linux-hardening, Longbin Li, Yixun Lan
In-Reply-To: <20260703021024.495433-1-inochiama@gmail.com>
On Fri, 03 Jul 2026 10:10:21 +0800, Inochi Amaoto wrote:
> The PCIe/USB comb phy on K3 is a big phy that contains multiple
> standalone phys for each PCIe and USB controllers. This phy is
> required to configure a syscon device for mux configuration and
> calibration.
>
> Changed from v1:
> patch 1:
> 1. Add extra description for "#phy-cells" and "spacemit,apmu"
> patch 2:
> 1. Fix multiple spelling errors and copy-paste errors.
> 2. Make some magic number as meaningful macros.
> 3. Remove USB3 set_speed() callback as it is not needed.
> 4. Fix the wrong land data config and mask.
>
> [...]
Applied, thanks!
[1/2] dt-bindings: phy: Add Spacemit K3 USB3/PCIe comb phy support
commit: 013004374b4da621e292408112413b103dd28b03
[2/2] phy: spacemit: Add USB3/PCIe comb PHY driver for Spacemit K3
commit: 7a099760ea63ced7b194852b7dbbdcd951585f15
Best regards,
--
~Vinod
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH v3 0/2] PHY: Add support for MT8196 DSI PHY
From: Vinod Koul @ 2026-07-20 11:43 UTC (permalink / raw)
To: chunfeng.yun, AngeloGioacchino Del Regno
Cc: neil.armstrong, robh, krzk+dt, conor+dt, chunkuang.hu, p.zabel,
matthias.bgg, justin.yeh, linux-arm-kernel, linux-mediatek,
linux-phy, devicetree, linux-kernel, dri-devel, kernel
In-Reply-To: <20260713103805.23030-1-angelogioacchino.delregno@collabora.com>
On Mon, 13 Jul 2026 12:38:03 +0200, AngeloGioacchino Del Regno wrote:
> Changes in v3:
> - Actually sent the correct patch series (sorry, I had messed up v2)
> - Added definitions for minimum and maximum PLL rates
>
> Changes in v2:
> - Added a few comments and fix scheduling while atomic issue in clk
> enable ops by waiting with udelay() instead
> - Removed one unnecessary delay after clk/pwr isolation disablement
>
> [...]
Applied, thanks!
[1/2] dt-bindings: phy: mediatek,dsi-phy: Add support for MT8196
commit: 215cd5def8eca1c2aa7ca043483d96155a1c3ed6
[2/2] phy: mediatek: Add support for MT8196 MIPI DSI PHY
commit: 2ec9c210c1f931389709aee403d28e4f32676385
Best regards,
--
~Vinod
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: (subset) [PATCH v3 0/4] PCI: qcom: Add link retention support
From: Vinod Koul @ 2026-07-20 11:43 UTC (permalink / raw)
To: Neil Armstrong, Philipp Zabel, Jingoo Han, Manivannan Sadhasivam,
Lorenzo Pieralisi, Krzysztof Wilczyński, Rob Herring,
Bjorn Helgaas, Krishna Chaitanya Chundru
Cc: linux-arm-msm, linux-phy, linux-kernel, linux-pci, Abel Vesa,
Konrad Dybcio, Qiang Yu
In-Reply-To: <20260709-link_retain-v3-0-81a9d187bb61@oss.qualcomm.com>
On Thu, 09 Jul 2026 12:05:15 +0530, Krishna Chaitanya Chundru wrote:
> This patch series introduces support for retaining the PCIe link across
> bootloader and kernel handoff on Qualcomm platforms, specifically
> X1E80100. The goal is to reduce boot time and avoid unnecessary link
> reinitialization when the link is already up.
>
> We are not enabling link retantion support for all the targets, as there
> is no guarantee that the bootloader on all targets has initialized the
> PCIe link in max supported speed. So we are enabling for hamoa & glymur
> target only for now based on the config flag.
>
> [...]
Applied, thanks!
[1/4] phy: qcom: qmp-pcie: Skip PHY reset if already up
commit: 910b828b22b7b91054b3bd4be676a017444b0e00
Best regards,
--
~Vinod
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH] phy: fsl-lynx-10g: propagate PCVT enable errors
From: Breno Leitao @ 2026-07-20 12:14 UTC (permalink / raw)
To: kr494167
Cc: ioana.ciornei, vladimir.oltean, vkoul, neil.armstrong, netdev,
linux-phy, linux-kernel
In-Reply-To: <20260720101906.80584-1-kr494167@gmail.com>
On Mon, Jul 20, 2026 at 03:49:06PM +0530, kr494167@gmail.com wrote:
> Propagate the error and leave the previous lane mode intact so the caller
> can handle the failed reconfiguration.
Do you need to undo these operations that were done to the lane earlier?
lynx_10g_lane_change_proto_conf(lane, lane_mode);
lynx_10g_lane_remap_pll(lane, lane_mode);
--breno
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH] phy: fsl-lynx-10g: propagate PCVT enable errors
From: Vladimir Oltean @ 2026-07-20 12:16 UTC (permalink / raw)
To: kr494167
Cc: ioana.ciornei, vkoul, neil.armstrong, netdev, linux-phy,
linux-kernel
In-Reply-To: <20260720101906.80584-1-kr494167@gmail.com>
Hi Surendra,
On Mon, Jul 20, 2026 at 03:49:06PM +0530, kr494167@gmail.com wrote:
> From: surendra <kr494167@gmail.com>
>
> lynx_10g_set_mode() currently ignores failures from
> lynx_10g_lane_enable_pcvt(). It then updates the lane mode and reports
> success even though the protocol converter may remain disabled.
>
> Propagate the error and leave the previous lane mode intact so the caller
> can handle the failed reconfiguration.
>
> Signed-off-by: surendra <kr494167@gmail.com>
> ---
> drivers/phy/freescale/phy-fsl-lynx-10g.c | 4 +++-
> 1 file changed, 3 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/phy/freescale/phy-fsl-lynx-10g.c b/drivers/phy/freescale/phy-fsl-lynx-10g.c
> index 38def160ef1a..9740b08700c4 100644
> --- a/drivers/phy/freescale/phy-fsl-lynx-10g.c
> +++ b/drivers/phy/freescale/phy-fsl-lynx-10g.c
> @@ -1231,7 +1231,9 @@ static int lynx_10g_set_mode(struct phy *phy, enum phy_mode mode, int submode)
>
> lynx_10g_lane_change_proto_conf(lane, lane_mode);
> lynx_10g_lane_remap_pll(lane, lane_mode);
> - WARN_ON(lynx_10g_lane_enable_pcvt(lane, lane_mode));
> + err = lynx_10g_lane_enable_pcvt(lane, lane_mode);
> + if (err)
> + goto out;
>
> lane->mode = lane_mode;
>
> --
> 2.55.0
>
Thank you for the patch, however you are fixing a situation which will
never occur.
Assume lynx_10g_lane_enable_pcvt(lane, lane_mode) will fail.
It means one of the following functions inside of it returned a negative
return code:
- lynx_pcvt_rmw(lane, mode, ...)
- lynx_pccr_write(lane, mode, ...)
Let's take them one by one.
lynx_pcvt_rmw() fails if priv->info->get_pcvt_offset(lane->id, mode)
returns negative.
lynx_pccr_write() fails if priv->info->get_pccr(mode, lane->id, ...)
returns negative.
Had any of these two functions returned a negative error code, the
execution would not have reached the lynx_10g_lane_enable_pcvt() stage!
The following code path:
lynx_10g_set_mode()
-> lynx_10g_validate()
-> lynx_phy_mode_to_lane_mode()
-> lynx_lane_supports_mode()
-> priv->info->lane_supports_mode() // this is provided for lynx-28g
OR
-> lynx_lane_supports_mode_default() // all lynx-10g instances use this
-> checks priv->info->get_pccr() and
priv->info->get_pcvt_offset()
already fails much earlier if priv->info->get_pccr() or
priv->info->get_pcvt_offset() return negative (i.e. we don't know how to
handle the requested protocol).
The suggested placement of your error handling is extremely late anyway.
lynx_10g_lane_change_proto_conf() and lynx_10g_lane_remap_pll() have
already been executed, so the lane is reconfigured for the new protocol
and remapped to the new PLL. The "goto out" label doesn't undo that.
This is why I am reluctant to agree with your proposed change - it
doesn't make the code any more correct, it just complicates the handling
and actually makes that impossible case handled *incorrectly* (on
phy_set_mode_ext() failure, you don't expect the PHY to be configured
half way).
It would be very strange to proceed thus far if we didn't first make
sure that the protocol we are switching to is fully supported.
This is why the code has the WARN_ON(). The function _can_ fail, but we
don't need to check more than once for failures on the same mode with
the same protocol, since they are time-invariant.
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH v4 1/8] phy: rockchip: samsung-hdptx: Fix rate recalculation for high bpc
From: Dmitry Baryshkov @ 2026-07-20 13:24 UTC (permalink / raw)
To: Cristian Ciocaltea
Cc: Vinod Koul, Neil Armstrong, Heiko Stuebner, Algea Cao,
Dmitry Baryshkov, kernel, linux-phy, linux-arm-kernel,
linux-rockchip, linux-kernel, Thomas Niederprüm,
Simon Wright
In-Reply-To: <20260612-hdptx-clk-fixes-v4-1-ce5e1d456cda@collabora.com>
On Fri, Jun 12, 2026 at 02:45:20AM +0300, Cristian Ciocaltea wrote:
> The PHY PLL can be programmed by an external component, e.g. the
> bootloader, just before the recalc_rate() callback is invoked during
> devm_clk_hw_register() in the probe path.
>
> Therefore rk_hdptx_phy_clk_recalc_rate() finds the PLL enabled and
> attempts to compute the clock rate, while making use of the bpc value
> from the HDMI PHY configuration, which always defaults to 8 because
> phy_configure() was not run at that point. As a consequence, the
> (re)calculated rate is incorrect when the actual bpc was higher than 8.
>
> Do not rely on any of the hdmi_cfg members when computing the clock rate
> and, instead, read the required input data (i.e. bpc), directly from the
> hardware registers.
>
> Fixes: 3481fc04d969 ("phy: rockchip: samsung-hdptx: Compute clk rate from PLL config")
> Tested-by: Thomas Niederprüm <dubito@online.de>
> Tested-by: Simon Wright <simon@symple.nz>
> Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
> ---
> drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c | 13 ++++---------
> 1 file changed, 4 insertions(+), 9 deletions(-)
>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
--
With best wishes
Dmitry
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* [PATCH v3 phy-next 0/8] RCW override for 10G Lynx dynamic protocol reconfiguration
From: Vladimir Oltean @ 2026-07-20 13:36 UTC (permalink / raw)
To: linux-phy
Cc: devicetree, linuxppc-dev, linux-arm-kernel, Ioana Ciornei,
Vinod Koul, Neil Armstrong, Tanjeff Moos,
Christophe Leroy (CS GROUP), Michael Walle, Shawn Guo, Frank Li,
linux-kernel, Conor Dooley, Krzysztof Kozlowski, Rob Herring
Previous set "New Generic PHY driver for Lynx 10G SerDes":
https://lore.kernel.org/linux-phy/20260610151952.2141019-1-vladimir.oltean@nxp.com/
introduced the 10G Lynx SerDes driver with a reduced functionality set.
Namely, only minor protocol changes are supported (1GbE <-> 2.5GbE).
The major protocol changes need a procedure named RCW override,
explained in more detail in commits 6/8 and 7/8.
This series adds kernel and device tree binding support for RCW
override, completing the SerDes PHY driver functionality.
Two components are involved:
- drivers/soc/fsl/guts.c (binding is fsl,layerscape-dcfg.yaml) - Device
Configuration Unit, this is API provider for the SerDes driver to
request RCW override depending on SoC
- drivers/phy/freescale/phy-fsl-lynx-10g.c - SerDes PHY driver, this is
API consumer
The guts driver probes on DCFG blocks from multiple Freescale SoC
generations:
- MPC85xx, BSC and QorIQ (PowerPC) are all covered by the
Documentation/devicetree/bindings/soc/fsl/guts.txt schema
- Layerscape (Arm) is covered by
Documentation/devicetree/bindings/soc/fsl/fsl,layerscape-dcfg.yaml
It is ultimately the same hardware block, just that (from what I can
tell) the Layerscape nodes are also compatible with syscon, and PowerPC
aren't.
RCW override has only been validated on select Layerscape SoCs, so
converting guts.txt to a PowerPC schema is out of scope for this
series - we don't even touch that (just in case it gets asked).
Using syscon to map the DCFG_DCSR register block in the Lynx SerDes
driver instead of creating this guts <-> lynx API was considered, but
because the RCW procedure is SoC-specific, it was ruled out for
polluting the SerDes driver. The guts driver is all about SoC awareness
anyway, and it offers some abstraction of all the gory details.
Changes since v2:
- fix error handling in fsl_guts_init() and make sure that all newly
introduced RCW override API functions fail if fsl_guts_init() failed.
- replace __bf_shf() use with __ffs()
- use read_poll_timeout_atomic() in fsl_guts_rcw_rmw() to clarify that
DCFG_DCSR writes are supposed to reflect back in DCFG_CCSR
- only operate on lanes on which fsl_guts_lane_init() was called in
ls2088a_serdes_init_rcwcr()
- put parentheses around (lane) in LS1088A_RCWSR29_SRDS_PRTCL_S1_LNn()
and LS1088A_RCWSR30_SRDS_PRTCL_S2_LNn() macro expressions
v2 at:
https://lore.kernel.org/linux-phy/20260612210859.266759-8-vladimir.oltean@nxp.com/
Changes since v1:
- add Conor's review tag on 6/8
- update email addresses of DT maintainers
- drop DT maintainers from explicit CC on patch 7/8
- keep devicetree@vger.kernel.org CCed on entire series
- include missing <linux/bitfield.h> in patch 7/8
- namespace SRDS_PRTCL values for LS1046A and LS1088A, even if they are
the same. For LS1028A (not covered here) they are not.
- prefix SRDS_CLK_SEL_{GMII,XGMII} with LS2088A_
- reorder alphanumerically (LS1046A should come before LS1088A)
Change logs also in individual patches.
v1 at:
https://lore.kernel.org/linux-phy/20260611193940.44416-1-vladimir.oltean@nxp.com/
Cc: Conor Dooley <conor+dt@kernel.org>
Cc: Krzysztof Kozlowski <krzk+dt@kernel.org>
Cc: Rob Herring <robh@kernel.org>
Ioana Ciornei (5):
soc: fsl: guts: use a macro to encode the DCFG CCSR space
soc: fsl: guts: add a global structure to hold state
soc: fsl: guts: add a central fsl_guts_read() function
soc: fsl: guts: make it easier to determine on which SoC we are
running
soc: fsl: guts: implement the RCW override procedure
Vladimir Oltean (3):
soc: fsl: guts: make fsl_soc_data available after fsl_guts_init()
dt-bindings: fsl: layerscape-dcfg: define DCFG_DCSR region
phy: lynx-10g: use RCW override procedure for dynamic protocol change
.../bindings/soc/fsl/fsl,layerscape-dcfg.yaml | 15 +-
drivers/phy/freescale/Kconfig | 1 +
drivers/phy/freescale/phy-fsl-lynx-10g.c | 24 +-
drivers/soc/fsl/guts.c | 438 ++++++++++++++++--
include/linux/fsl/guts.h | 20 +-
5 files changed, 459 insertions(+), 39 deletions(-)
--
2.34.1
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* [PATCH v3 phy-next 1/8] soc: fsl: guts: use a macro to encode the DCFG CCSR space
From: Vladimir Oltean @ 2026-07-20 13:36 UTC (permalink / raw)
To: linux-phy
Cc: devicetree, linuxppc-dev, linux-arm-kernel, Ioana Ciornei,
Vinod Koul, Neil Armstrong, Tanjeff Moos,
Christophe Leroy (CS GROUP), Michael Walle, Shawn Guo, Frank Li,
linux-kernel
In-Reply-To: <20260720133642.136324-1-vladimir.oltean@nxp.com>
From: Ioana Ciornei <ioana.ciornei@nxp.com>
Instead of using a hardcoded value when iomapping the DCFG CCSR space,
add a new macro for it. The code will be easier to follow this way,
especially when we add support for the DCFG DCSR space as well.
Signed-off-by: Ioana Ciornei <ioana.ciornei@nxp.com>
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
---
v1->v3: none
---
drivers/soc/fsl/guts.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/soc/fsl/guts.c b/drivers/soc/fsl/guts.c
index 9bee7baec2b9..f87ee47c1503 100644
--- a/drivers/soc/fsl/guts.c
+++ b/drivers/soc/fsl/guts.c
@@ -14,6 +14,8 @@
#include <linux/platform_device.h>
#include <linux/fsl/guts.h>
+#define DCFG_CCSR 0
+
struct fsl_soc_die_attr {
char *die;
u32 svr;
@@ -197,7 +199,7 @@ static int __init fsl_guts_init(void)
return 0;
soc_data = match->data;
- regs = of_iomap(np, 0);
+ regs = of_iomap(np, DCFG_CCSR);
if (!regs) {
of_node_put(np);
return -ENOMEM;
--
2.34.1
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox