From d15224492d1aef8b28508f23b889ee0394b07b83 Mon Sep 17 00:00:00 2001 From: Matthew Leung Date: Tue, 7 Jul 2026 20:32:46 +0000 Subject: [PATCH 01/76] dt-bindings: phy: qcom,sc8280xp-qmp-pcie-phy: Add Hawi compatibles Document the compatibles for the Gen3 x2 and Gen4 x1 QMP PCIe PHYs found on the Hawi platform. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Matthew Leung Link: https://patch.msgid.link/20260707-hawi-phy-pcie-v4-1-8d9cc6324947@oss.qualcomm.com Signed-off-by: Vinod Koul --- .../devicetree/bindings/phy/qcom,sc8280xp-qmp-pcie-phy.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-pcie-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-pcie-phy.yaml index 108cf9dc86ea..259ed9678393 100644 --- a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-pcie-phy.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-pcie-phy.yaml @@ -20,6 +20,8 @@ properties: - qcom,eliza-qmp-gen3x2-pcie-phy - qcom,glymur-qmp-gen4x2-pcie-phy - qcom,glymur-qmp-gen5x4-pcie-phy + - qcom,hawi-qmp-gen3x2-pcie-phy + - qcom,hawi-qmp-gen4x1-pcie-phy - qcom,kaanapali-qmp-gen3x2-pcie-phy - qcom,qcs615-qmp-gen3x1-pcie-phy - qcom,qcs8300-qmp-gen4x2-pcie-phy @@ -196,6 +198,8 @@ allOf: - qcom,eliza-qmp-gen3x2-pcie-phy - qcom,glymur-qmp-gen4x2-pcie-phy - qcom,glymur-qmp-gen5x4-pcie-phy + - qcom,hawi-qmp-gen3x2-pcie-phy + - qcom,hawi-qmp-gen4x1-pcie-phy - qcom,qcs8300-qmp-gen4x2-pcie-phy - qcom,sa8775p-qmp-gen4x2-pcie-phy - qcom,sa8775p-qmp-gen4x4-pcie-phy @@ -223,6 +227,8 @@ allOf: - qcom,eliza-qmp-gen3x2-pcie-phy - qcom,glymur-qmp-gen4x2-pcie-phy - qcom,glymur-qmp-gen5x4-pcie-phy + - qcom,hawi-qmp-gen3x2-pcie-phy + - qcom,hawi-qmp-gen4x1-pcie-phy - qcom,kaanapali-qmp-gen3x2-pcie-phy - qcom,sm8550-qmp-gen4x2-pcie-phy - qcom,sm8650-qmp-gen4x2-pcie-phy From 0696cc735dc7ff53297ad2ee649ceed3b1f93d07 Mon Sep 17 00:00:00 2001 From: Matthew Leung Date: Tue, 7 Jul 2026 20:32:47 +0000 Subject: [PATCH 02/76] phy: qcom-qmp: Add v10 register offsets Hawi SoC uses v10 register definitions for PCIe Gen3 x2. Add the new register offset headers for all four sub-blocks: - QSERDES-COM offsets - QSERDES TX/RX offsets - PCS offsets - PCS PCIe-specific offsets Signed-off-by: Matthew Leung Reviewed-by: Dmitry Baryshkov Link: https://patch.msgid.link/20260707-hawi-phy-pcie-v4-2-8d9cc6324947@oss.qualcomm.com Signed-off-by: Vinod Koul --- drivers/phy/qualcomm/phy-qcom-qmp-pcie.c | 1 + .../phy/qualcomm/phy-qcom-qmp-pcs-pcie-v10.h | 18 +++++++ drivers/phy/qualcomm/phy-qcom-qmp-pcs-v10.h | 22 +++++++++ .../qualcomm/phy-qcom-qmp-qserdes-com-v10.h | 49 +++++++++++++++++++ .../qualcomm/phy-qcom-qmp-qserdes-txrx-v10.h | 47 ++++++++++++++++++ drivers/phy/qualcomm/phy-qcom-qmp.h | 5 ++ 6 files changed, 142 insertions(+) create mode 100644 drivers/phy/qualcomm/phy-qcom-qmp-pcs-pcie-v10.h create mode 100644 drivers/phy/qualcomm/phy-qcom-qmp-pcs-v10.h create mode 100644 drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com-v10.h create mode 100644 drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v10.h diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c index d3effad7a074..5a26347d0fc9 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c @@ -40,6 +40,7 @@ #include "phy-qcom-qmp-qserdes-com-v8.h" #include "phy-qcom-qmp-pcs-pcie-v8.h" #include "phy-qcom-qmp-qserdes-txrx-pcie-v8.h" +#include "phy-qcom-qmp-pcs-pcie-v10.h" #define PHY_INIT_COMPLETE_TIMEOUT 10000 diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcs-pcie-v10.h b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-pcie-v10.h new file mode 100644 index 000000000000..2cdcc211bd93 --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-pcie-v10.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef QCOM_PHY_QMP_PCS_PCIE_V10_H_ +#define QCOM_PHY_QMP_PCS_PCIE_V10_H_ + +/* Only for QMP V10 PHY - PCIE PCS registers */ +#define QPHY_PCIE_V10_PCS_POWER_STATE_CONFIG2 0x00c +#define QPHY_PCIE_V10_PCS_POWER_STATE_CONFIG4 0x014 +#define QPHY_PCIE_V10_PCS_ENDPOINT_REFCLK_DRIVE 0x020 +#define QPHY_PCIE_V10_PCS_OSC_DTCT_ACTIONS 0x094 +#define QPHY_PCIE_V10_PCS_EQ_CONFIG1 0x0a4 +#define QPHY_PCIE_V10_PCS_RXEQEVAL_TIME 0x0f4 +#define QPHY_PCIE_V10_PCS_POWER_STATE_CONFIG6 0x0f8 + +#endif diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcs-v10.h b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-v10.h new file mode 100644 index 000000000000..165ce8a28f61 --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-v10.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef QCOM_PHY_QMP_PCS_V10_H_ +#define QCOM_PHY_QMP_PCS_V10_H_ + +/* Only for QMP V10 PHY - PCIe PCS registers */ +#define QPHY_V10_PCS_SW_RESET 0x000 +#define QPHY_V10_PCS_PCS_STATUS1 0x014 +#define QPHY_V10_PCS_POWER_DOWN_CONTROL 0x040 +#define QPHY_V10_PCS_START_CONTROL 0x044 +#define QPHY_V10_PCS_REFGEN_REQ_CONFIG1 0x0dc +#define QPHY_V10_PCS_G12S1_TXDEEMPH_M6DB 0x168 +#define QPHY_V10_PCS_G3S2_PRE_GAIN 0x170 +#define QPHY_V10_PCS_RX_SIGDET_LVL 0x188 +#define QPHY_V10_PCS_RATE_SLEW_CNTRL1 0x198 +#define QPHY_V10_PCS_PCS_TX_RX_CONFIG 0x1d0 +#define QPHY_V10_PCS_EQ_CONFIG2 0x1e4 + +#endif diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com-v10.h b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com-v10.h new file mode 100644 index 000000000000..09199e7b4aac --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com-v10.h @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef QCOM_PHY_QMP_QSERDES_COM_V10_H_ +#define QCOM_PHY_QMP_QSERDES_COM_V10_H_ + +/* Only for QMP V10 PHY - QSERDES COM registers */ +#define QSERDES_V10_COM_SSC_STEP_SIZE1_MODE1 0x00 +#define QSERDES_V10_COM_SSC_STEP_SIZE2_MODE1 0x04 +#define QSERDES_V10_COM_CP_CTRL_MODE1 0x10 +#define QSERDES_V10_COM_PLL_RCTRL_MODE1 0x14 +#define QSERDES_V10_COM_PLL_CCTRL_MODE1 0x18 +#define QSERDES_V10_COM_CORECLK_DIV_MODE1 0x1c +#define QSERDES_V10_COM_LOCK_CMP1_MODE1 0x20 +#define QSERDES_V10_COM_LOCK_CMP2_MODE1 0x24 +#define QSERDES_V10_COM_DEC_START_MODE1 0x28 +#define QSERDES_V10_COM_DIV_FRAC_START1_MODE1 0x30 +#define QSERDES_V10_COM_DIV_FRAC_START2_MODE1 0x34 +#define QSERDES_V10_COM_DIV_FRAC_START3_MODE1 0x38 +#define QSERDES_V10_COM_HSCLK_SEL_1 0x3c +#define QSERDES_V10_COM_SSC_STEP_SIZE1_MODE0 0x60 +#define QSERDES_V10_COM_SSC_STEP_SIZE2_MODE0 0x64 +#define QSERDES_V10_COM_CP_CTRL_MODE0 0x70 +#define QSERDES_V10_COM_PLL_RCTRL_MODE0 0x74 +#define QSERDES_V10_COM_PLL_CCTRL_MODE0 0x78 +#define QSERDES_V10_COM_LOCK_CMP1_MODE0 0x80 +#define QSERDES_V10_COM_LOCK_CMP2_MODE0 0x84 +#define QSERDES_V10_COM_DEC_START_MODE0 0x88 +#define QSERDES_V10_COM_DIV_FRAC_START1_MODE0 0x90 +#define QSERDES_V10_COM_DIV_FRAC_START2_MODE0 0x94 +#define QSERDES_V10_COM_DIV_FRAC_START3_MODE0 0x98 +#define QSERDES_V10_COM_BG_TIMER 0xbc +#define QSERDES_V10_COM_SSC_EN_CENTER 0xc0 +#define QSERDES_V10_COM_SSC_PER1 0xcc +#define QSERDES_V10_COM_SSC_PER2 0xd0 +#define QSERDES_V10_COM_CLK_ENABLE1 0xe0 +#define QSERDES_V10_COM_SYS_CLK_CTRL 0xe4 +#define QSERDES_V10_COM_PLL_IVCO 0xf4 +#define QSERDES_V10_COM_SYSCLK_EN_SEL 0x110 +#define QSERDES_V10_COM_LOCK_CMP_EN 0x120 +#define QSERDES_V10_COM_VCO_TUNE_MAP 0x140 +#define QSERDES_V10_COM_CLK_SELECT 0x164 +#define QSERDES_V10_COM_CORE_CLK_EN 0x170 +#define QSERDES_V10_COM_CMN_CONFIG_1 0x174 +#define QSERDES_V10_COM_ADDITIONAL_MISC_3 0x1bc + +#endif diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v10.h b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v10.h new file mode 100644 index 000000000000..d81ebdde0063 --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v10.h @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef QCOM_PHY_QMP_QSERDES_TXRX_V10_H_ +#define QCOM_PHY_QMP_QSERDES_TXRX_V10_H_ + +#define QSERDES_V10_TX_RES_CODE_LANE_OFFSET_TX 0x03c +#define QSERDES_V10_TX_RES_CODE_LANE_OFFSET_RX 0x040 +#define QSERDES_V10_TX_LANE_MODE_1 0x084 +#define QSERDES_V10_TX_LANE_MODE_3 0x08c +#define QSERDES_V10_TX_LANE_MODE_4 0x090 +#define QSERDES_V10_TX_LANE_MODE_5 0x094 +#define QSERDES_V10_TX_PI_QEC_CTRL 0x0e4 + +#define QSERDES_V10_RX_UCDR_FO_GAIN 0x008 +#define QSERDES_V10_RX_UCDR_SO_GAIN 0x014 +#define QSERDES_V10_RX_UCDR_SB2_THRESH1 0x04c +#define QSERDES_V10_RX_UCDR_SB2_THRESH2 0x050 +#define QSERDES_V10_RX_TX_ADAPT_PRE_THRESH1 0x0c4 +#define QSERDES_V10_RX_TX_ADAPT_PRE_THRESH2 0x0c8 +#define QSERDES_V10_RX_TX_ADAPT_POST_THRESH 0x0cc +#define QSERDES_V10_RX_VGA_CAL_CNTRL2 0x0d8 +#define QSERDES_V10_RX_GM_CAL 0x0dc +#define QSERDES_V10_RX_RX_IDAC_TSETTLE_LOW 0x0f8 +#define QSERDES_V10_RX_SIGDET_ENABLES 0x118 +#define QSERDES_V10_RX_SIGDET_CNTRL 0x11c +#define QSERDES_V10_RX_RX_MODE_00_LOW 0x15c +#define QSERDES_V10_RX_RX_MODE_00_HIGH 0x160 +#define QSERDES_V10_RX_RX_MODE_00_HIGH2 0x164 +#define QSERDES_V10_RX_RX_MODE_00_HIGH3 0x168 +#define QSERDES_V10_RX_RX_MODE_00_HIGH4 0x16c +#define QSERDES_V10_RX_RX_MODE_01_LOW 0x170 +#define QSERDES_V10_RX_RX_MODE_01_HIGH 0x174 +#define QSERDES_V10_RX_RX_MODE_01_HIGH2 0x178 +#define QSERDES_V10_RX_RX_MODE_01_HIGH3 0x17c +#define QSERDES_V10_RX_RX_MODE_01_HIGH4 0x180 +#define QSERDES_V10_RX_RX_MODE_10_LOW 0x184 +#define QSERDES_V10_RX_RX_MODE_10_HIGH 0x188 +#define QSERDES_V10_RX_RX_MODE_10_HIGH2 0x18c +#define QSERDES_V10_RX_RX_MODE_10_HIGH3 0x190 +#define QSERDES_V10_RX_RX_MODE_10_HIGH4 0x194 +#define QSERDES_V10_RX_DFE_CTLE_POST_CAL_OFFSET 0x1a4 +#define QSERDES_V10_RX_SIGDET_CAL_TRIM 0x1f8 + +#endif diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.h b/drivers/phy/qualcomm/phy-qcom-qmp.h index 11b7e03b4fab..e4237e374404 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp.h +++ b/drivers/phy/qualcomm/phy-qcom-qmp.h @@ -36,6 +36,9 @@ #include "phy-qcom-qmp-qserdes-txrx-v8.h" #include "phy-qcom-qmp-qserdes-lalb-v8.h" +#include "phy-qcom-qmp-qserdes-com-v10.h" +#include "phy-qcom-qmp-qserdes-txrx-v10.h" + #include "phy-qcom-qmp-qserdes-pll.h" #include "phy-qcom-qmp-pcs-v2.h" @@ -62,6 +65,8 @@ #include "phy-qcom-qmp-pcs-v8_50.h" +#include "phy-qcom-qmp-pcs-v10.h" + /* QPHY_SW_RESET bit */ #define SW_RESET BIT(0) /* QPHY_POWER_DOWN_CONTROL */ From fdfe6417e2e699890f51bcc0b772734a0431e5d2 Mon Sep 17 00:00:00 2001 From: Matthew Leung Date: Tue, 7 Jul 2026 20:32:48 +0000 Subject: [PATCH 03/76] phy: qcom-qmp: Add v10.60 register offsets Hawi SoC uses v10.60 register definitions for PCIe Gen4 x1. Add the new register offset headers for all four sub-blocks: - QSERDES-COM offsets - QSERDES TX/RX offsets - PCS offsets - PCS PCIe-specific offsets Signed-off-by: Matthew Leung Reviewed-by: Dmitry Baryshkov Link: https://patch.msgid.link/20260707-hawi-phy-pcie-v4-3-8d9cc6324947@oss.qualcomm.com Signed-off-by: Vinod Koul --- drivers/phy/qualcomm/phy-qcom-qmp-pcie.c | 1 + .../qualcomm/phy-qcom-qmp-pcs-pcie-v10_60.h | 26 +++++ .../phy/qualcomm/phy-qcom-qmp-pcs-v10_60.h | 23 ++++ .../phy-qcom-qmp-qserdes-com-v10_60.h | 55 +++++++++ .../phy-qcom-qmp-qserdes-txrx-v10_60.h | 109 ++++++++++++++++++ drivers/phy/qualcomm/phy-qcom-qmp.h | 5 + 6 files changed, 219 insertions(+) create mode 100644 drivers/phy/qualcomm/phy-qcom-qmp-pcs-pcie-v10_60.h create mode 100644 drivers/phy/qualcomm/phy-qcom-qmp-pcs-v10_60.h create mode 100644 drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com-v10_60.h create mode 100644 drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v10_60.h diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c index 5a26347d0fc9..9f933eff3f12 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c @@ -41,6 +41,7 @@ #include "phy-qcom-qmp-pcs-pcie-v8.h" #include "phy-qcom-qmp-qserdes-txrx-pcie-v8.h" #include "phy-qcom-qmp-pcs-pcie-v10.h" +#include "phy-qcom-qmp-pcs-pcie-v10_60.h" #define PHY_INIT_COMPLETE_TIMEOUT 10000 diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcs-pcie-v10_60.h b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-pcie-v10_60.h new file mode 100644 index 000000000000..2df5a45010a4 --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-pcie-v10_60.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef QCOM_PHY_QMP_PCS_PCIE_V10_60_H_ +#define QCOM_PHY_QMP_PCS_PCIE_V10_60_H_ + +/* Only for QMP V10_60 PHY - PCIE PCS registers */ +#define QPHY_PCIE_V10_60_PCS_POWER_STATE_CONFIG2 0x00c +#define QPHY_PCIE_V10_60_PCS_TX_RX_CONFIG 0x018 +#define QPHY_PCIE_V10_60_PCS_ENDPOINT_REFCLK_DRIVE 0x01c +#define QPHY_PCIE_V10_60_PCS_OSC_DTCT_ACTIONS 0x090 +#define QPHY_PCIE_V10_60_PCS_EQ_CONFIG1 0x0a0 +#define QPHY_PCIE_V10_60_PCS_G3_RXEQEVAL_TIME 0x0f0 +#define QPHY_PCIE_V10_60_PCS_G4_RXEQEVAL_TIME 0x0f4 +#define QPHY_PCIE_V10_60_PCS_G4_EQ_CONFIG5 0x108 +#define QPHY_PCIE_V10_60_PCS_G4_PRE_GAIN 0x15c +#define QPHY_PCIE_V10_60_PCS_RX_MARGINING_CONFIG1 0x17c +#define QPHY_PCIE_V10_60_PCS_RX_MARGINING_CONFIG3 0x184 +#define QPHY_PCIE_V10_60_PCS_RX_MARGINING_CONFIG5 0x18c +#define QPHY_PCIE_V10_60_PCS_G3_FOM_EQ_CONFIG5 0x1ac +#define QPHY_PCIE_V10_60_PCS_G4_FOM_EQ_CONFIG5 0x1c0 +#define QPHY_PCIE_V10_60_PCS_POWER_STATE_CONFIG6 0x1d0 + +#endif diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcs-v10_60.h b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-v10_60.h new file mode 100644 index 000000000000..e4558b69489d --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-v10_60.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef QCOM_PHY_QMP_PCS_V10_60_H_ +#define QCOM_PHY_QMP_PCS_V10_60_H_ + +/* Only for QMP V10_60 PHY - PCIe PCS registers */ +#define QPHY_V10_60_PCS_SW_RESET 0x000 +#define QPHY_V10_60_PCS_PCS_STATUS1 0x014 +#define QPHY_V10_60_PCS_POWER_DOWN_CONTROL 0x040 +#define QPHY_V10_60_PCS_START_CONTROL 0x044 +#define QPHY_V10_60_PCS_G12S1_TXDEEMPH_M6DB 0x170 +#define QPHY_V10_60_PCS_G3S2_PRE_GAIN 0x178 +#define QPHY_V10_60_PCS_RX_SIGDET_LVL 0x190 +#define QPHY_V10_60_PCS_ELECIDLE_DLY_SEL 0x1b8 +#define QPHY_V10_60_PCS_PCS_TX_RX_CONFIG1 0x1dc +#define QPHY_V10_60_PCS_PCS_TX_RX_CONFIG2 0x1e0 +#define QPHY_V10_60_PCS_EQ_CONFIG4 0x1f8 +#define QPHY_V10_60_PCS_EQ_CONFIG5 0x1fc + +#endif diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com-v10_60.h b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com-v10_60.h new file mode 100644 index 000000000000..39351bef8b63 --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com-v10_60.h @@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef QCOM_PHY_QMP_QSERDES_COM_V10_60_H_ +#define QCOM_PHY_QMP_QSERDES_COM_V10_60_H_ + +/* Only for QMP V10_60 PHY - QSERDES COM registers */ +#define QSERDES_V10_60_COM_SSC_STEP_SIZE1_MODE1 0x00 +#define QSERDES_V10_60_COM_SSC_STEP_SIZE2_MODE1 0x04 +#define QSERDES_V10_60_COM_CP_CTRL_MODE1 0x10 +#define QSERDES_V10_60_COM_PLL_RCTRL_MODE1 0x14 +#define QSERDES_V10_60_COM_PLL_CCTRL_MODE1 0x18 +#define QSERDES_V10_60_COM_CORECLK_DIV_MODE1 0x1c +#define QSERDES_V10_60_COM_LOCK_CMP1_MODE1 0x20 +#define QSERDES_V10_60_COM_LOCK_CMP2_MODE1 0x24 +#define QSERDES_V10_60_COM_DEC_START_MODE1 0x28 +#define QSERDES_V10_60_COM_DIV_FRAC_START1_MODE1 0x30 +#define QSERDES_V10_60_COM_DIV_FRAC_START2_MODE1 0x34 +#define QSERDES_V10_60_COM_DIV_FRAC_START3_MODE1 0x38 +#define QSERDES_V10_60_COM_HSCLK_SEL_1 0x3c +#define QSERDES_V10_60_COM_SSC_STEP_SIZE1_MODE0 0x60 +#define QSERDES_V10_60_COM_SSC_STEP_SIZE2_MODE0 0x64 +#define QSERDES_V10_60_COM_CP_CTRL_MODE0 0x70 +#define QSERDES_V10_60_COM_PLL_RCTRL_MODE0 0x74 +#define QSERDES_V10_60_COM_PLL_CCTRL_MODE0 0x78 +#define QSERDES_V10_60_COM_CORECLK_DIV_MODE0 0x7c +#define QSERDES_V10_60_COM_LOCK_CMP1_MODE0 0x80 +#define QSERDES_V10_60_COM_LOCK_CMP2_MODE0 0x84 +#define QSERDES_V10_60_COM_DEC_START_MODE0 0x88 +#define QSERDES_V10_60_COM_DIV_FRAC_START1_MODE0 0x90 +#define QSERDES_V10_60_COM_DIV_FRAC_START2_MODE0 0x94 +#define QSERDES_V10_60_COM_DIV_FRAC_START3_MODE0 0x98 +#define QSERDES_V10_60_COM_HSCLK_HS_SWITCH_SEL_1 0x9c +#define QSERDES_V10_60_COM_BG_TIMER 0xbc +#define QSERDES_V10_60_COM_SSC_PER1 0xcc +#define QSERDES_V10_60_COM_SSC_PER2 0xd0 +#define QSERDES_V10_60_COM_BIAS_EN_CLKBUFLR_EN 0xdc +#define QSERDES_V10_60_COM_CLK_ENABLE1 0xe0 +#define QSERDES_V10_60_COM_SYS_CLK_CTRL 0xe4 +#define QSERDES_V10_60_COM_PLL_IVCO 0xf4 +#define QSERDES_V10_60_COM_SYSCLK_EN_SEL 0x110 +#define QSERDES_V10_60_COM_LOCK_CMP_EN 0x120 +#define QSERDES_V10_60_COM_LOCK_CMP_CFG 0x124 +#define QSERDES_V10_60_COM_VCO_TUNE_MAP 0x140 +#define QSERDES_V10_60_COM_CLK_SELECT 0x164 +#define QSERDES_V10_60_COM_CORE_CLK_EN 0x170 +#define QSERDES_V10_60_COM_CMN_CONFIG_1 0x174 +#define QSERDES_V10_60_COM_CMN_MISC1 0x184 +#define QSERDES_V10_60_COM_CMN_MODE 0x188 +#define QSERDES_V10_60_COM_VCO_DC_LEVEL_CTRL 0x198 +#define QSERDES_V10_60_COM_PLL_SPARE_FOR_ECO 0x2b4 + +#endif diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v10_60.h b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v10_60.h new file mode 100644 index 000000000000..3150a494685e --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v10_60.h @@ -0,0 +1,109 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef QCOM_PHY_QMP_QSERDES_TXRX_V10_60_H_ +#define QCOM_PHY_QMP_QSERDES_TXRX_V10_60_H_ + +#define QSERDES_V10_60_TXRX_RES_CODE_LANE_OFFSET_TX 0x034 +#define QSERDES_V10_60_TXRX_RES_CODE_LANE_OFFSET_RX 0x038 +#define QSERDES_V10_60_TXRX_LANE_MODE_1 0x080 +#define QSERDES_V10_60_TXRX_LANE_MODE_2 0x084 +#define QSERDES_V10_60_TXRX_LANE_MODE_3 0x088 +#define QSERDES_V10_60_TXRX_UCDR_FASTLOCK_FO_GAIN_RATE1 0x0c8 +#define QSERDES_V10_60_TXRX_UCDR_FASTLOCK_FO_GAIN_RATE2 0x0cc +#define QSERDES_V10_60_TXRX_UCDR_FASTLOCK_FO_GAIN_RATE3 0x0d0 +#define QSERDES_V10_60_TXRX_UCDR_FASTLOCK_FO_GAIN_RATE4 0x0d4 +#define QSERDES_V10_60_TXRX_UCDR_FASTLOCK_SO_GAIN_RATE1 0x0e0 +#define QSERDES_V10_60_TXRX_UCDR_FASTLOCK_SO_GAIN_RATE2 0x0e4 +#define QSERDES_V10_60_TXRX_UCDR_FASTLOCK_SO_GAIN_RATE3 0x0e8 +#define QSERDES_V10_60_TXRX_UCDR_FASTLOCK_SO_GAIN_RATE4 0x0ec +#define QSERDES_V10_60_TXRX_UCDR_PI_CTRL1 0x12c +#define QSERDES_V10_60_TXRX_UCDR_PI_CTRL2 0x130 +#define QSERDES_V10_60_TXRX_UCDR_PI_CTRL3 0x134 +#define QSERDES_V10_60_TXRX_UCDR_PI_CTRL4 0x138 +#define QSERDES_V10_60_TXRX_SVS_MODE_CTRL 0x19c +#define QSERDES_V10_60_TXRX_RXCLK_DIV2_CTRL 0x1a0 +#define QSERDES_V10_60_TXRX_RX_BAND_CTRL0 0x1a4 +#define QSERDES_V10_60_TXRX_RX_TERM_BW_CTRL0 0x1ac +#define QSERDES_V10_60_TXRX_RX_TERM_BW_CTRL1 0x1b0 +#define QSERDES_V10_60_TXRX_UCDR_FO_GAIN_RATE1 0x1b8 +#define QSERDES_V10_60_TXRX_UCDR_FO_GAIN_RATE2 0x1bc +#define QSERDES_V10_60_TXRX_UCDR_FO_GAIN_RATE3 0x1c0 +#define QSERDES_V10_60_TXRX_UCDR_FO_GAIN_RATE4 0x1c4 +#define QSERDES_V10_60_TXRX_UCDR_SO_GAIN_RATE1 0x1d0 +#define QSERDES_V10_60_TXRX_UCDR_SO_GAIN_RATE2 0x1d4 +#define QSERDES_V10_60_TXRX_UCDR_SO_GAIN_RATE3 0x1d8 +#define QSERDES_V10_60_TXRX_UCDR_SO_GAIN_RATE4 0x1dc +#define QSERDES_V10_60_TXRX_UCDR_PI_CONTROLS 0x1e4 +#define QSERDES_V10_60_TXRX_AUXDATA_BIN_RATE3 0x200 +#define QSERDES_V10_60_TXRX_AUXDATA_BIN_RATE4 0x204 +#define QSERDES_V10_60_TXRX_EOM_MAX_ERR_LIMIT_LSB 0x218 +#define QSERDES_V10_60_TXRX_EOM_MAX_ERR_LIMIT_MSB 0x21c +#define QSERDES_V10_60_TXRX_VGA_CAL_CNTRL1 0x280 +#define QSERDES_V10_60_TXRX_VGA_CAL_MAN_VAL 0x288 +#define QSERDES_V10_60_TXRX_GM_CAL 0x29c +#define QSERDES_V10_60_TXRX_RX_EQU_ADAPTOR_CNTRL6 0x2b8 +#define QSERDES_V10_60_TXRX_SIGDET_ENABLES 0x2d4 +#define QSERDES_V10_60_TXRX_SIGDET_CNTRL 0x2d8 +#define QSERDES_V10_60_TXRX_SIGDET_LVL 0x2dc +#define QSERDES_V10_60_TXRX_SIGDET_DEGLITCH_CNTRL 0x2e0 +#define QSERDES_V10_60_TXRX_RX_MODE_RATE_0_1_B0 0x314 +#define QSERDES_V10_60_TXRX_RX_MODE_RATE_0_1_B1 0x318 +#define QSERDES_V10_60_TXRX_RX_MODE_RATE_0_1_B2 0x31c +#define QSERDES_V10_60_TXRX_RX_MODE_RATE_0_1_B3 0x320 +#define QSERDES_V10_60_TXRX_RX_MODE_RATE_0_1_B4 0x324 +#define QSERDES_V10_60_TXRX_RX_MODE_RATE_0_1_B5 0x328 +#define QSERDES_V10_60_TXRX_RX_MODE_RATE_0_1_B6 0x32c +#define QSERDES_V10_60_TXRX_RX_MODE_RATE_0_1_B7 0x330 +#define QSERDES_V10_60_TXRX_RX_MODE_RATE_0_1_B8 0x334 +#define QSERDES_V10_60_TXRX_RX_MODE_RATE_0_1_B9 0x338 +#define QSERDES_V10_60_TXRX_RX_MODE_RATE_0_1_B10 0x33c +#define QSERDES_V10_60_TXRX_RX_MODE_RATE2_B0 0x340 +#define QSERDES_V10_60_TXRX_RX_MODE_RATE2_B1 0x344 +#define QSERDES_V10_60_TXRX_RX_MODE_RATE2_B2 0x348 +#define QSERDES_V10_60_TXRX_RX_MODE_RATE2_B3 0x34c +#define QSERDES_V10_60_TXRX_RX_MODE_RATE2_B4 0x350 +#define QSERDES_V10_60_TXRX_RX_MODE_RATE2_B5 0x354 +#define QSERDES_V10_60_TXRX_RX_MODE_RATE2_B6 0x358 +#define QSERDES_V10_60_TXRX_RX_MODE_RATE2_B7 0x35c +#define QSERDES_V10_60_TXRX_RX_MODE_RATE2_B8 0x360 +#define QSERDES_V10_60_TXRX_RX_MODE_RATE2_B9 0x364 +#define QSERDES_V10_60_TXRX_RX_MODE_RATE2_B10 0x368 +#define QSERDES_V10_60_TXRX_RX_MODE_RATE3_B0 0x36c +#define QSERDES_V10_60_TXRX_RX_MODE_RATE3_B1 0x370 +#define QSERDES_V10_60_TXRX_RX_MODE_RATE3_B2 0x374 +#define QSERDES_V10_60_TXRX_RX_MODE_RATE3_B3 0x378 +#define QSERDES_V10_60_TXRX_RX_MODE_RATE3_B4 0x37c +#define QSERDES_V10_60_TXRX_RX_MODE_RATE3_B5 0x380 +#define QSERDES_V10_60_TXRX_RX_MODE_RATE3_B6 0x384 +#define QSERDES_V10_60_TXRX_RX_MODE_RATE3_B7 0x388 +#define QSERDES_V10_60_TXRX_RX_MODE_RATE3_B8 0x38c +#define QSERDES_V10_60_TXRX_RX_MODE_RATE3_B9 0x390 +#define QSERDES_V10_60_TXRX_RX_MODE_RATE3_B10 0x394 +#define QSERDES_V10_60_TXRX_RX_MODE_RATE4_SA_B0 0x398 +#define QSERDES_V10_60_TXRX_RX_MODE_RATE4_SA_B1 0x39c +#define QSERDES_V10_60_TXRX_RX_MODE_RATE4_SA_B2 0x3a0 +#define QSERDES_V10_60_TXRX_RX_MODE_RATE4_SA_B3 0x3a4 +#define QSERDES_V10_60_TXRX_RX_MODE_RATE4_SA_B4 0x3a8 +#define QSERDES_V10_60_TXRX_RX_MODE_RATE4_SA_B5 0x3ac +#define QSERDES_V10_60_TXRX_RX_MODE_RATE4_SA_B6 0x3b0 +#define QSERDES_V10_60_TXRX_RX_MODE_RATE4_SA_B7 0x3b4 +#define QSERDES_V10_60_TXRX_RX_MODE_RATE4_SA_B8 0x3b8 +#define QSERDES_V10_60_TXRX_RX_MODE_RATE4_SA_B9 0x3bc +#define QSERDES_V10_60_TXRX_RX_MODE_RATE4_SA_B10 0x3c0 +#define QSERDES_V10_60_TXRX_Q_PI_INTRINSIC_BIAS_RATE32 0x478 +#define QSERDES_V10_60_TXRX_Q_PI_INTRINSIC_BIAS_RATE45 0x47c +#define QSERDES_V10_60_TXRX_SIGDET_CAL_CTRL1 0x4c8 +#define QSERDES_V10_60_TXRX_SIGDET_CAL_CTRL2 0x4cc +#define QSERDES_V10_60_TXRX_SIGDET_CAL_TRIM 0x4d0 +#define QSERDES_V10_60_TXRX_TX_BAND0 0x4e8 +#define QSERDES_V10_60_TXRX_TX_BAND1 0x4ec +#define QSERDES_V10_60_TXRX_SEL_10B_8B 0x4f4 +#define QSERDES_V10_60_TXRX_SEL_20B_10B 0x4f8 +#define QSERDES_V10_60_TXRX_EQ_RCF_CTRL_RATE3 0x53c +#define QSERDES_V10_60_TXRX_EQ_RCF_CTRL_RATE4 0x540 +#define QSERDES_V10_60_TXRX_PHPRE_CTRL 0x5e8 + +#endif diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.h b/drivers/phy/qualcomm/phy-qcom-qmp.h index e4237e374404..10eb97aa8706 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp.h +++ b/drivers/phy/qualcomm/phy-qcom-qmp.h @@ -39,6 +39,9 @@ #include "phy-qcom-qmp-qserdes-com-v10.h" #include "phy-qcom-qmp-qserdes-txrx-v10.h" +#include "phy-qcom-qmp-qserdes-com-v10_60.h" +#include "phy-qcom-qmp-qserdes-txrx-v10_60.h" + #include "phy-qcom-qmp-qserdes-pll.h" #include "phy-qcom-qmp-pcs-v2.h" @@ -67,6 +70,8 @@ #include "phy-qcom-qmp-pcs-v10.h" +#include "phy-qcom-qmp-pcs-v10_60.h" + /* QPHY_SW_RESET bit */ #define SW_RESET BIT(0) /* QPHY_POWER_DOWN_CONTROL */ From 9ef4e520912e4966081e276beda628b69aac85dd Mon Sep 17 00:00:00 2001 From: Matthew Leung Date: Tue, 7 Jul 2026 20:32:49 +0000 Subject: [PATCH 04/76] phy: qcom: qmp-pcie: Add QMP PCIe PHY support for Hawi Add the QMP PCIe PHY support for the Gen3 x2 and Gen4 x1 PHY found on the Hawi platform. Signed-off-by: Matthew Leung Reviewed-by: Dmitry Baryshkov Link: https://patch.msgid.link/20260707-hawi-phy-pcie-v4-4-8d9cc6324947@oss.qualcomm.com Signed-off-by: Vinod Koul --- drivers/phy/qualcomm/phy-qcom-qmp-pcie.c | 373 +++++++++++++++++++++++ 1 file changed, 373 insertions(+) diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c index 9f933eff3f12..357a2e2f8ff6 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c @@ -118,6 +118,20 @@ static const unsigned int pciephy_v8_50_regs_layout[QPHY_LAYOUT_SIZE] = { [QPHY_PCS_POWER_DOWN_CONTROL] = QPHY_V8_50_PCS_POWER_DOWN_CONTROL, }; +static const unsigned int pciephy_v10_regs_layout[QPHY_LAYOUT_SIZE] = { + [QPHY_SW_RESET] = QPHY_V10_PCS_SW_RESET, + [QPHY_START_CTRL] = QPHY_V10_PCS_START_CONTROL, + [QPHY_PCS_STATUS] = QPHY_V10_PCS_PCS_STATUS1, + [QPHY_PCS_POWER_DOWN_CONTROL] = QPHY_V10_PCS_POWER_DOWN_CONTROL, +}; + +static const unsigned int pciephy_v10_60_regs_layout[QPHY_LAYOUT_SIZE] = { + [QPHY_SW_RESET] = QPHY_V10_60_PCS_SW_RESET, + [QPHY_START_CTRL] = QPHY_V10_60_PCS_START_CONTROL, + [QPHY_PCS_STATUS] = QPHY_V10_60_PCS_PCS_STATUS1, + [QPHY_PCS_POWER_DOWN_CONTROL] = QPHY_V10_60_PCS_POWER_DOWN_CONTROL, +}; + static const struct qmp_phy_init_tbl msm8998_pcie_serdes_tbl[] = { QMP_PHY_INIT_CFG(QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN, 0x14), QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_SELECT, 0x30), @@ -3328,6 +3342,287 @@ static const struct qmp_phy_init_tbl kaanapali_qmp_gen3x2_pcie_pcs_misc_tbl[] = QMP_PHY_INIT_CFG(QPHY_PCIE_V8_PCS_POWER_STATE_CONFIG6, 0x1f), }; +static const struct qmp_phy_init_tbl hawi_qmp_gen3x2_pcie_serdes_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V10_COM_SSC_EN_CENTER, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_SSC_PER1, 0x62), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_SSC_PER2, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_SSC_STEP_SIZE1_MODE0, 0xf8), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_SSC_STEP_SIZE2_MODE0, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_SSC_STEP_SIZE1_MODE1, 0x93), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_SSC_STEP_SIZE2_MODE1, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_CLK_ENABLE1, 0x90), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_SYS_CLK_CTRL, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_PLL_IVCO, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_CP_CTRL_MODE0, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_CP_CTRL_MODE1, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_PLL_RCTRL_MODE1, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_PLL_CCTRL_MODE0, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_PLL_CCTRL_MODE1, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_SYSCLK_EN_SEL, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_BG_TIMER, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_LOCK_CMP_EN, 0x42), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_LOCK_CMP1_MODE0, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_LOCK_CMP2_MODE0, 0x0d), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_LOCK_CMP1_MODE1, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_LOCK_CMP2_MODE1, 0x1a), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_DEC_START_MODE0, 0x41), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_DEC_START_MODE1, 0x34), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_DIV_FRAC_START1_MODE0, 0xab), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_DIV_FRAC_START2_MODE0, 0xaa), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_DIV_FRAC_START3_MODE0, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_DIV_FRAC_START1_MODE1, 0x55), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_DIV_FRAC_START2_MODE1, 0x55), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_DIV_FRAC_START3_MODE1, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_VCO_TUNE_MAP, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_CLK_SELECT, 0x34), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_HSCLK_SEL_1, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_CORECLK_DIV_MODE1, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_CMN_CONFIG_1, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_ADDITIONAL_MISC_3, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_CORE_CLK_EN, 0xa0), +}; + +static const struct qmp_phy_init_tbl hawi_qmp_gen3x2_pcie_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V10_RX_DFE_CTLE_POST_CAL_OFFSET, 0x38), + QMP_PHY_INIT_CFG(QSERDES_V10_RX_GM_CAL, 0x11), + QMP_PHY_INIT_CFG(QSERDES_V10_RX_RX_MODE_00_HIGH, 0xbf), + QMP_PHY_INIT_CFG(QSERDES_V10_RX_RX_MODE_00_HIGH2, 0xbf), + QMP_PHY_INIT_CFG(QSERDES_V10_RX_RX_MODE_00_HIGH3, 0xb7), + QMP_PHY_INIT_CFG(QSERDES_V10_RX_RX_MODE_00_HIGH4, 0xec), + QMP_PHY_INIT_CFG(QSERDES_V10_RX_RX_MODE_00_LOW, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V10_RX_RX_MODE_01_HIGH, 0x09), + QMP_PHY_INIT_CFG(QSERDES_V10_RX_RX_MODE_01_HIGH2, 0x49), + QMP_PHY_INIT_CFG(QSERDES_V10_RX_RX_MODE_01_HIGH3, 0x1b), + QMP_PHY_INIT_CFG(QSERDES_V10_RX_RX_MODE_01_HIGH4, 0x9c), + QMP_PHY_INIT_CFG(QSERDES_V10_RX_RX_MODE_01_LOW, 0xd1), + QMP_PHY_INIT_CFG(QSERDES_V10_RX_RX_MODE_10_HIGH, 0x09), + QMP_PHY_INIT_CFG(QSERDES_V10_RX_RX_MODE_10_HIGH2, 0x49), + QMP_PHY_INIT_CFG(QSERDES_V10_RX_RX_MODE_10_HIGH3, 0x1b), + QMP_PHY_INIT_CFG(QSERDES_V10_RX_RX_MODE_10_HIGH4, 0x9c), + QMP_PHY_INIT_CFG(QSERDES_V10_RX_RX_MODE_10_LOW, 0xd1), + QMP_PHY_INIT_CFG(QSERDES_V10_RX_TX_ADAPT_PRE_THRESH1, 0x3e), + QMP_PHY_INIT_CFG(QSERDES_V10_RX_TX_ADAPT_PRE_THRESH2, 0x1e), + QMP_PHY_INIT_CFG(QSERDES_V10_RX_TX_ADAPT_POST_THRESH, 0xd2), + QMP_PHY_INIT_CFG(QSERDES_V10_RX_UCDR_FO_GAIN, 0x09), + QMP_PHY_INIT_CFG(QSERDES_V10_RX_UCDR_SO_GAIN, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V10_RX_UCDR_SB2_THRESH1, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V10_RX_UCDR_SB2_THRESH2, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V10_RX_VGA_CAL_CNTRL2, 0x09), + QMP_PHY_INIT_CFG(QSERDES_V10_RX_SIGDET_ENABLES, 0x1c), + QMP_PHY_INIT_CFG(QSERDES_V10_RX_SIGDET_CNTRL, 0x60), + QMP_PHY_INIT_CFG(QSERDES_V10_RX_RX_IDAC_TSETTLE_LOW, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V10_RX_SIGDET_CAL_TRIM, 0x08), +}; + +static const struct qmp_phy_init_tbl hawi_qmp_gen3x2_pcie_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V10_TX_LANE_MODE_1, 0x25), + QMP_PHY_INIT_CFG(QSERDES_V10_TX_LANE_MODE_3, 0x10), + QMP_PHY_INIT_CFG(QSERDES_V10_TX_LANE_MODE_4, 0x31), + QMP_PHY_INIT_CFG(QSERDES_V10_TX_LANE_MODE_5, 0x7d), + QMP_PHY_INIT_CFG(QSERDES_V10_TX_PI_QEC_CTRL, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V10_TX_RES_CODE_LANE_OFFSET_RX, 0x09), + QMP_PHY_INIT_CFG(QSERDES_V10_TX_RES_CODE_LANE_OFFSET_TX, 0x14), +}; + +static const struct qmp_phy_init_tbl hawi_qmp_gen3x2_pcie_pcs_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V10_PCS_REFGEN_REQ_CONFIG1, 0x05), + QMP_PHY_INIT_CFG(QPHY_V10_PCS_RX_SIGDET_LVL, 0x77), + QMP_PHY_INIT_CFG(QPHY_V10_PCS_RATE_SLEW_CNTRL1, 0x0b), + QMP_PHY_INIT_CFG(QPHY_V10_PCS_EQ_CONFIG2, 0x0f), + QMP_PHY_INIT_CFG(QPHY_V10_PCS_PCS_TX_RX_CONFIG, 0x8c), + QMP_PHY_INIT_CFG(QPHY_V10_PCS_G12S1_TXDEEMPH_M6DB, 0x17), + QMP_PHY_INIT_CFG(QPHY_V10_PCS_G3S2_PRE_GAIN, 0x2e), +}; + +static const struct qmp_phy_init_tbl hawi_qmp_gen3x2_pcie_pcs_misc_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_PCIE_V10_PCS_EQ_CONFIG1, 0x1e), + QMP_PHY_INIT_CFG(QPHY_PCIE_V10_PCS_RXEQEVAL_TIME, 0x27), + QMP_PHY_INIT_CFG(QPHY_PCIE_V10_PCS_POWER_STATE_CONFIG2, 0x1d), + QMP_PHY_INIT_CFG(QPHY_PCIE_V10_PCS_POWER_STATE_CONFIG4, 0x07), + QMP_PHY_INIT_CFG(QPHY_PCIE_V10_PCS_POWER_STATE_CONFIG6, 0x1f), + QMP_PHY_INIT_CFG(QPHY_PCIE_V10_PCS_ENDPOINT_REFCLK_DRIVE, 0xc1), + QMP_PHY_INIT_CFG(QPHY_PCIE_V10_PCS_OSC_DTCT_ACTIONS, 0x00), +}; + +static const struct qmp_phy_init_tbl hawi_qmp_gen4x1_pcie_serdes_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V10_60_COM_SSC_STEP_SIZE1_MODE1, 0x93), + QMP_PHY_INIT_CFG(QSERDES_V10_60_COM_SSC_STEP_SIZE2_MODE1, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V10_60_COM_CP_CTRL_MODE1, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V10_60_COM_PLL_RCTRL_MODE1, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V10_60_COM_PLL_CCTRL_MODE1, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V10_60_COM_CORECLK_DIV_MODE1, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V10_60_COM_LOCK_CMP1_MODE1, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V10_60_COM_LOCK_CMP2_MODE1, 0x1a), + QMP_PHY_INIT_CFG(QSERDES_V10_60_COM_DEC_START_MODE1, 0x34), + QMP_PHY_INIT_CFG(QSERDES_V10_60_COM_DIV_FRAC_START1_MODE1, 0x55), + QMP_PHY_INIT_CFG(QSERDES_V10_60_COM_DIV_FRAC_START2_MODE1, 0x55), + QMP_PHY_INIT_CFG(QSERDES_V10_60_COM_DIV_FRAC_START3_MODE1, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V10_60_COM_HSCLK_SEL_1, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V10_60_COM_SSC_STEP_SIZE1_MODE0, 0xf8), + QMP_PHY_INIT_CFG(QSERDES_V10_60_COM_SSC_STEP_SIZE2_MODE0, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V10_60_COM_CP_CTRL_MODE0, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V10_60_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V10_60_COM_PLL_CCTRL_MODE0, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V10_60_COM_CORECLK_DIV_MODE0, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V10_60_COM_LOCK_CMP1_MODE0, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V10_60_COM_LOCK_CMP2_MODE0, 0x0d), + QMP_PHY_INIT_CFG(QSERDES_V10_60_COM_DEC_START_MODE0, 0x41), + QMP_PHY_INIT_CFG(QSERDES_V10_60_COM_DIV_FRAC_START1_MODE0, 0xab), + QMP_PHY_INIT_CFG(QSERDES_V10_60_COM_DIV_FRAC_START2_MODE0, 0xaa), + QMP_PHY_INIT_CFG(QSERDES_V10_60_COM_DIV_FRAC_START3_MODE0, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V10_60_COM_HSCLK_HS_SWITCH_SEL_1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V10_60_COM_BG_TIMER, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V10_60_COM_SSC_PER1, 0x62), + QMP_PHY_INIT_CFG(QSERDES_V10_60_COM_SSC_PER2, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V10_60_COM_BIAS_EN_CLKBUFLR_EN, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V10_60_COM_CLK_ENABLE1, 0x90), + QMP_PHY_INIT_CFG(QSERDES_V10_60_COM_SYS_CLK_CTRL, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V10_60_COM_PLL_IVCO, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V10_60_COM_SYSCLK_EN_SEL, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V10_60_COM_LOCK_CMP_EN, 0x46), + QMP_PHY_INIT_CFG(QSERDES_V10_60_COM_LOCK_CMP_CFG, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V10_60_COM_VCO_TUNE_MAP, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V10_60_COM_CLK_SELECT, 0x34), + QMP_PHY_INIT_CFG(QSERDES_V10_60_COM_CORE_CLK_EN, 0xa0), + QMP_PHY_INIT_CFG(QSERDES_V10_60_COM_CMN_CONFIG_1, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V10_60_COM_CMN_MISC1, 0x88), + QMP_PHY_INIT_CFG(QSERDES_V10_60_COM_CMN_MODE, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V10_60_COM_VCO_DC_LEVEL_CTRL, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V10_60_COM_PLL_SPARE_FOR_ECO, 0x02), +}; + +static const struct qmp_phy_init_tbl hawi_qmp_gen4x1_pcie_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_RES_CODE_LANE_OFFSET_TX, 0x1a), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_RES_CODE_LANE_OFFSET_RX, 0x12), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_SIGDET_CAL_CTRL1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_SIGDET_CAL_CTRL2, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_SIGDET_CAL_TRIM, 0x66), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_TX_BAND0, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_TX_BAND1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_SEL_10B_8B, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_SEL_20B_10B, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_EQ_RCF_CTRL_RATE3, 0x22), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_EQ_RCF_CTRL_RATE4, 0x22), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_PHPRE_CTRL, 0x20), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_UCDR_FASTLOCK_FO_GAIN_RATE1, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_UCDR_FASTLOCK_FO_GAIN_RATE2, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_UCDR_FASTLOCK_FO_GAIN_RATE3, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_UCDR_FASTLOCK_FO_GAIN_RATE4, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_UCDR_FASTLOCK_SO_GAIN_RATE1, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_UCDR_FASTLOCK_SO_GAIN_RATE2, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_UCDR_FASTLOCK_SO_GAIN_RATE3, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_UCDR_FASTLOCK_SO_GAIN_RATE4, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_UCDR_PI_CTRL1, 0x40), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_UCDR_PI_CTRL2, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_UCDR_PI_CTRL3, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_UCDR_PI_CTRL4, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_SVS_MODE_CTRL, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_RXCLK_DIV2_CTRL, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_RX_BAND_CTRL0, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_RX_TERM_BW_CTRL0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_RX_TERM_BW_CTRL1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_UCDR_FO_GAIN_RATE1, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_UCDR_FO_GAIN_RATE2, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_UCDR_FO_GAIN_RATE3, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_UCDR_FO_GAIN_RATE4, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_UCDR_SO_GAIN_RATE1, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_UCDR_SO_GAIN_RATE2, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_UCDR_SO_GAIN_RATE3, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_UCDR_SO_GAIN_RATE4, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_UCDR_PI_CONTROLS, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_AUXDATA_BIN_RATE3, 0x03), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_AUXDATA_BIN_RATE4, 0x03), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_EOM_MAX_ERR_LIMIT_LSB, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_EOM_MAX_ERR_LIMIT_MSB, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_VGA_CAL_CNTRL1, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_VGA_CAL_MAN_VAL, 0x8e), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_GM_CAL, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_RX_EQU_ADAPTOR_CNTRL6, 0xca), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_SIGDET_ENABLES, 0x1c), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_SIGDET_CNTRL, 0x6f), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_SIGDET_LVL, 0x84), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_SIGDET_DEGLITCH_CNTRL, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_Q_PI_INTRINSIC_BIAS_RATE32, 0x11), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_Q_PI_INTRINSIC_BIAS_RATE45, 0x10), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_RX_MODE_RATE_0_1_B0, 0xc2), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_RX_MODE_RATE_0_1_B1, 0xc2), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_RX_MODE_RATE_0_1_B2, 0xd8), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_RX_MODE_RATE_0_1_B3, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_RX_MODE_RATE_0_1_B4, 0x98), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_RX_MODE_RATE_0_1_B5, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_RX_MODE_RATE_0_1_B6, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_RX_MODE_RATE_0_1_B7, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_RX_MODE_RATE_0_1_B8, 0xc0), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_RX_MODE_RATE_0_1_B9, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_RX_MODE_RATE_0_1_B10, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_RX_MODE_RATE2_B0, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_RX_MODE_RATE2_B1, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_RX_MODE_RATE2_B2, 0xd8), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_RX_MODE_RATE2_B3, 0x1a), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_RX_MODE_RATE2_B4, 0x98), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_RX_MODE_RATE2_B5, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_RX_MODE_RATE2_B6, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_RX_MODE_RATE2_B7, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_RX_MODE_RATE2_B8, 0xc0), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_RX_MODE_RATE2_B9, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_RX_MODE_RATE2_B10, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_RX_MODE_RATE3_B0, 0x13), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_RX_MODE_RATE3_B1, 0xd3), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_RX_MODE_RATE3_B2, 0xc0), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_RX_MODE_RATE3_B3, 0x13), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_RX_MODE_RATE3_B4, 0x13), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_RX_MODE_RATE3_B5, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_RX_MODE_RATE3_B6, 0x4c), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_RX_MODE_RATE3_B7, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_RX_MODE_RATE3_B8, 0xc0), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_RX_MODE_RATE3_B9, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_RX_MODE_RATE3_B10, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_RX_MODE_RATE4_SA_B0, 0x24), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_RX_MODE_RATE4_SA_B1, 0x24), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_RX_MODE_RATE4_SA_B2, 0xc0), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_RX_MODE_RATE4_SA_B3, 0x0b), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_RX_MODE_RATE4_SA_B4, 0x1a), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_RX_MODE_RATE4_SA_B5, 0x24), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_RX_MODE_RATE4_SA_B6, 0x2c), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_RX_MODE_RATE4_SA_B7, 0x86), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_RX_MODE_RATE4_SA_B8, 0x83), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_RX_MODE_RATE4_SA_B9, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_RX_MODE_RATE4_SA_B10, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_LANE_MODE_1, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_LANE_MODE_2, 0xc0), + QMP_PHY_INIT_CFG(QSERDES_V10_60_TXRX_LANE_MODE_3, 0x00), +}; + +static const struct qmp_phy_init_tbl hawi_qmp_gen4x1_pcie_pcs_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V10_60_PCS_G12S1_TXDEEMPH_M6DB, 0x17), + QMP_PHY_INIT_CFG(QPHY_V10_60_PCS_G3S2_PRE_GAIN, 0x2e), + QMP_PHY_INIT_CFG(QPHY_V10_60_PCS_RX_SIGDET_LVL, 0xcc), + QMP_PHY_INIT_CFG(QPHY_V10_60_PCS_ELECIDLE_DLY_SEL, 0x40), + QMP_PHY_INIT_CFG(QPHY_V10_60_PCS_PCS_TX_RX_CONFIG1, 0x04), + QMP_PHY_INIT_CFG(QPHY_V10_60_PCS_PCS_TX_RX_CONFIG2, 0x02), + QMP_PHY_INIT_CFG(QPHY_V10_60_PCS_EQ_CONFIG4, 0x00), + QMP_PHY_INIT_CFG(QPHY_V10_60_PCS_EQ_CONFIG5, 0x22), +}; + +static const struct qmp_phy_init_tbl hawi_qmp_gen4x1_pcie_pcs_misc_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_PCIE_V10_60_PCS_POWER_STATE_CONFIG2, 0x1d), + QMP_PHY_INIT_CFG(QPHY_PCIE_V10_60_PCS_TX_RX_CONFIG, 0xc0), + QMP_PHY_INIT_CFG(QPHY_PCIE_V10_60_PCS_ENDPOINT_REFCLK_DRIVE, 0xc1), + QMP_PHY_INIT_CFG(QPHY_PCIE_V10_60_PCS_OSC_DTCT_ACTIONS, 0x00), + QMP_PHY_INIT_CFG(QPHY_PCIE_V10_60_PCS_EQ_CONFIG1, 0x16), + QMP_PHY_INIT_CFG(QPHY_PCIE_V10_60_PCS_G3_RXEQEVAL_TIME, 0x27), + QMP_PHY_INIT_CFG(QPHY_PCIE_V10_60_PCS_G4_RXEQEVAL_TIME, 0x27), + QMP_PHY_INIT_CFG(QPHY_PCIE_V10_60_PCS_G4_EQ_CONFIG5, 0x02), + QMP_PHY_INIT_CFG(QPHY_PCIE_V10_60_PCS_G4_PRE_GAIN, 0x2e), + QMP_PHY_INIT_CFG(QPHY_PCIE_V10_60_PCS_RX_MARGINING_CONFIG1, 0x03), + QMP_PHY_INIT_CFG(QPHY_PCIE_V10_60_PCS_RX_MARGINING_CONFIG3, 0x28), + QMP_PHY_INIT_CFG(QPHY_PCIE_V10_60_PCS_RX_MARGINING_CONFIG5, 0x0f), + QMP_PHY_INIT_CFG(QPHY_PCIE_V10_60_PCS_G3_FOM_EQ_CONFIG5, 0xf2), + QMP_PHY_INIT_CFG(QPHY_PCIE_V10_60_PCS_G4_FOM_EQ_CONFIG5, 0xf2), + QMP_PHY_INIT_CFG(QPHY_PCIE_V10_60_PCS_POWER_STATE_CONFIG6, 0x1f), +}; + struct qmp_pcie_offsets { u16 serdes; u16 pcs; @@ -3640,6 +3935,24 @@ static const struct qmp_pcie_offsets qmp_pcie_offsets_v8_50 = { .txrxz = 0xd000, }; +static const struct qmp_pcie_offsets qmp_pcie_offsets_v10_0 = { + .serdes = 0x0000, + .pcs = 0x0400, + .pcs_misc = 0x0800, + .tx = 0x1000, + .rx = 0x1200, + .tx2 = 0x1800, + .rx2 = 0x1a00, +}; + +static const struct qmp_pcie_offsets qmp_pcie_offsets_v10_60 = { + .tx = 0x0000, + /* no .rx; tx and rx share the same register block */ + .serdes = 0x1000, + .pcs = 0x1400, + .pcs_misc = 0x1800, +}; + static const struct qmp_phy_cfg eliza_qmp_gen3x1_pciephy_cfg = { .lanes = 1, @@ -4783,6 +5096,60 @@ static const struct qmp_phy_cfg glymur_qmp_gen4x2_pciephy_cfg = { .phy_status = PHYSTATUS_4_20, }; +static const struct qmp_phy_cfg hawi_qmp_gen3x2_pciephy_cfg = { + .lanes = 2, + + .offsets = &qmp_pcie_offsets_v10_0, + + .tbls = { + .serdes = hawi_qmp_gen3x2_pcie_serdes_tbl, + .serdes_num = ARRAY_SIZE(hawi_qmp_gen3x2_pcie_serdes_tbl), + .tx = hawi_qmp_gen3x2_pcie_tx_tbl, + .tx_num = ARRAY_SIZE(hawi_qmp_gen3x2_pcie_tx_tbl), + .rx = hawi_qmp_gen3x2_pcie_rx_tbl, + .rx_num = ARRAY_SIZE(hawi_qmp_gen3x2_pcie_rx_tbl), + .pcs = hawi_qmp_gen3x2_pcie_pcs_tbl, + .pcs_num = ARRAY_SIZE(hawi_qmp_gen3x2_pcie_pcs_tbl), + .pcs_misc = hawi_qmp_gen3x2_pcie_pcs_misc_tbl, + .pcs_misc_num = ARRAY_SIZE(hawi_qmp_gen3x2_pcie_pcs_misc_tbl), + }, + + .reset_list = sdm845_pciephy_reset_l, + .num_resets = ARRAY_SIZE(sdm845_pciephy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = pciephy_v10_regs_layout, + + .pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL, + .phy_status = PHYSTATUS, +}; + +static const struct qmp_phy_cfg hawi_qmp_gen4x1_pciephy_cfg = { + .lanes = 1, + + .offsets = &qmp_pcie_offsets_v10_60, + + .tbls = { + .serdes = hawi_qmp_gen4x1_pcie_serdes_tbl, + .serdes_num = ARRAY_SIZE(hawi_qmp_gen4x1_pcie_serdes_tbl), + .tx = hawi_qmp_gen4x1_pcie_tx_tbl, + .tx_num = ARRAY_SIZE(hawi_qmp_gen4x1_pcie_tx_tbl), + .pcs = hawi_qmp_gen4x1_pcie_pcs_tbl, + .pcs_num = ARRAY_SIZE(hawi_qmp_gen4x1_pcie_pcs_tbl), + .pcs_misc = hawi_qmp_gen4x1_pcie_pcs_misc_tbl, + .pcs_misc_num = ARRAY_SIZE(hawi_qmp_gen4x1_pcie_pcs_misc_tbl), + }, + + .reset_list = sdm845_pciephy_reset_l, + .num_resets = ARRAY_SIZE(sdm845_pciephy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = pciephy_v10_60_regs_layout, + + .pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL, + .phy_status = PHYSTATUS_4_20, +}; + static void qmp_pcie_init_port_b(struct qmp_pcie *qmp, const struct qmp_phy_cfg_tbls *tbls) { const struct qmp_phy_cfg *cfg = qmp->cfg; @@ -5545,6 +5912,12 @@ static const struct of_device_id qmp_pcie_of_match_table[] = { }, { .compatible = "qcom,glymur-qmp-gen5x4-pcie-phy", .data = &glymur_qmp_gen5x4_pciephy_cfg, + }, { + .compatible = "qcom,hawi-qmp-gen3x2-pcie-phy", + .data = &hawi_qmp_gen3x2_pciephy_cfg, + }, { + .compatible = "qcom,hawi-qmp-gen4x1-pcie-phy", + .data = &hawi_qmp_gen4x1_pciephy_cfg, }, { .compatible = "qcom,ipq6018-qmp-pcie-phy", .data = &ipq6018_pciephy_cfg, From f40b0241f3a382e99c14de2f28f14a44973407c1 Mon Sep 17 00:00:00 2001 From: Can Peng Date: Sat, 18 Jul 2026 17:00:53 +0800 Subject: [PATCH 05/76] phy: starfive: Fix runtime PM cleanup in JH7110 DPHY TX probe stf_dphy_probe() enables runtime PM before getting the clock and reset controls, creating the PHY and registering the PHY provider. If any of those steps fails, probe returns with runtime PM still enabled. The driver also has no remove callback, so runtime PM is left enabled on driver unbind after a successful probe. Use devm_pm_runtime_enable() so runtime PM is disabled automatically on later probe failures and on driver unbind. Fixes: d3ab79553308 ("phy: starfive: Add mipi dphy tx support") Signed-off-by: Can Peng Reviewed-by: Changhuang Liang Link: https://patch.msgid.link/20260718090054.444513-2-pengcan@kylinos.cn Signed-off-by: Vinod Koul --- drivers/phy/starfive/phy-jh7110-dphy-tx.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/phy/starfive/phy-jh7110-dphy-tx.c b/drivers/phy/starfive/phy-jh7110-dphy-tx.c index c64d1c91b130..181491a93807 100644 --- a/drivers/phy/starfive/phy-jh7110-dphy-tx.c +++ b/drivers/phy/starfive/phy-jh7110-dphy-tx.c @@ -392,6 +392,7 @@ static int stf_dphy_probe(struct platform_device *pdev) { struct phy_provider *phy_provider; struct stf_dphy *dphy; + int ret; dphy = devm_kzalloc(&pdev->dev, sizeof(*dphy), GFP_KERNEL); if (!dphy) @@ -406,7 +407,9 @@ static int stf_dphy_probe(struct platform_device *pdev) if (IS_ERR(dphy->topsys)) return PTR_ERR(dphy->topsys); - pm_runtime_enable(&pdev->dev); + ret = devm_pm_runtime_enable(&pdev->dev); + if (ret) + return ret; dphy->txesc_clk = devm_clk_get(&pdev->dev, "txesc"); if (IS_ERR(dphy->txesc_clk)) From 97bed336f6a25c9d1115ca95e3aa00e05c3bc271 Mon Sep 17 00:00:00 2001 From: Can Peng Date: Sat, 18 Jul 2026 17:00:54 +0800 Subject: [PATCH 06/76] phy: starfive: Fix runtime PM cleanup in JH7110 DPHY RX probe stf_dphy_probe() enables runtime PM before registering the PHY provider. If devm_of_phy_provider_register() fails, probe returns with runtime PM still enabled. The driver also has no remove callback, so runtime PM is left enabled on driver unbind after a successful probe. Use devm_pm_runtime_enable() so runtime PM is disabled automatically on later probe failures and on driver unbind. Fixes: f8aa660841bc ("phy: starfive: Add mipi dphy rx support") Signed-off-by: Can Peng Reviewed-by: Changhuang Liang Link: https://patch.msgid.link/20260718090054.444513-3-pengcan@kylinos.cn Signed-off-by: Vinod Koul --- drivers/phy/starfive/phy-jh7110-dphy-rx.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/phy/starfive/phy-jh7110-dphy-rx.c b/drivers/phy/starfive/phy-jh7110-dphy-rx.c index 0b039e1f71c5..d06f21ad6332 100644 --- a/drivers/phy/starfive/phy-jh7110-dphy-rx.c +++ b/drivers/phy/starfive/phy-jh7110-dphy-rx.c @@ -150,6 +150,7 @@ static int stf_dphy_probe(struct platform_device *pdev) { struct phy_provider *phy_provider; struct stf_dphy *dphy; + int ret; dphy = devm_kzalloc(&pdev->dev, sizeof(*dphy), GFP_KERNEL); if (!dphy) @@ -190,7 +191,9 @@ static int stf_dphy_probe(struct platform_device *pdev) return PTR_ERR(dphy->phy); } - pm_runtime_enable(&pdev->dev); + ret = devm_pm_runtime_enable(&pdev->dev); + if (ret) + return ret; phy_set_drvdata(dphy->phy, dphy); phy_provider = devm_of_phy_provider_register(&pdev->dev, From 013004374b4da621e292408112413b103dd28b03 Mon Sep 17 00:00:00 2001 From: Inochi Amaoto Date: Fri, 3 Jul 2026 10:10:22 +0800 Subject: [PATCH 07/76] dt-bindings: phy: Add Spacemit K3 USB3/PCIe comb phy support The USB3/PCIe comb PHY on the K3 is a complex PHY group that can provide multiple phy for both PCIe and USB controller. Its mux configuration is controlled by the APMU syscon device. Signed-off-by: Inochi Amaoto Reviewed-by: Rob Herring (Arm) Link: https://patch.msgid.link/20260703021024.495433-2-inochiama@gmail.com Signed-off-by: Vinod Koul --- .../bindings/phy/spacemit,k3-combo-phy.yaml | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 Documentation/devicetree/bindings/phy/spacemit,k3-combo-phy.yaml diff --git a/Documentation/devicetree/bindings/phy/spacemit,k3-combo-phy.yaml b/Documentation/devicetree/bindings/phy/spacemit,k3-combo-phy.yaml new file mode 100644 index 000000000000..ed820cf697b8 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/spacemit,k3-combo-phy.yaml @@ -0,0 +1,68 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/spacemit,k3-combo-phy.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Spacemit K3 PCIE/USB3 Comb PHY + +maintainers: + - Inochi Amaoto + +properties: + compatible: + const: spacemit,k3-combo-phy + + reg: + maxItems: 1 + + "#phy-cells": + const: 2 + description: + The first one is phy id, the second one is phy type. This + only supports types are PCIe and USB . + + spacemit,apb-spare: + $ref: /schemas/types.yaml#/definitions/phandle + description: + Phandle to APB SPARE system controller interface, used for + PHY calibration. + + spacemit,apmu: + $ref: /schemas/types.yaml#/definitions/phandle-array + items: + - items: + - description: phandle of APMU syscon + - description: configuration of the PHY lanes + description: | + Phandle to control PHY mux configuration. The configuration + is described as follows: + bit 4: 0 - PCIe A x8 mode, 1 - PCIe lane share mode + bit 3: 0 - PCIe A x4 mode, 1 - PCIe A x2 and PCIe B x2 mode + bit 2: 0 - PCIe C lane 0 is PCIe mode , 1 - USB mode + bit 1: 0 - PCIe C lane 1 is PCIe mode , 1 - USB mode + bit 0: 0 - PCIe D lane is PCIe mode , 1 - USB mode + + The bit[3:0] is only valid when bit 4 is 1. + + This mux control does not affect the PHY for PCIe E, it is always + in PCIe mode. + +required: + - compatible + - reg + - "#phy-cells" + - spacemit,apb-spare + - spacemit,apmu + +additionalProperties: false + +examples: + - | + phy@81d00000 { + compatible = "spacemit,k3-combo-phy"; + reg = <0x81d00000 0x600000>; + #phy-cells = <2>; + spacemit,apb-spare = <&apb_spare>; + spacemit,apmu = <&apmu 0x00>; + }; From 7a099760ea63ced7b194852b7dbbdcd951585f15 Mon Sep 17 00:00:00 2001 From: Inochi Amaoto Date: Fri, 3 Jul 2026 10:10:23 +0800 Subject: [PATCH 08/76] phy: spacemit: Add USB3/PCIe comb PHY driver for Spacemit K3 The comb PHY on K3 requires to configure a syscon device for the right mux configuration. And it requires calibration before any usage. Add USB3/PCIe comb PHY driver for Spacemit K3. Signed-off-by: Inochi Amaoto Tested-by: Aurelien Jarno Link: https://patch.msgid.link/20260703021024.495433-3-inochiama@gmail.com Signed-off-by: Vinod Koul --- drivers/phy/spacemit/Kconfig | 16 ++ drivers/phy/spacemit/Makefile | 2 + drivers/phy/spacemit/phy-k3-combo.c | 252 +++++++++++++++++ drivers/phy/spacemit/phy-k3-common.c | 391 +++++++++++++++++++++++++++ drivers/phy/spacemit/phy-k3-common.h | 27 ++ 5 files changed, 688 insertions(+) create mode 100644 drivers/phy/spacemit/phy-k3-combo.c create mode 100644 drivers/phy/spacemit/phy-k3-common.c create mode 100644 drivers/phy/spacemit/phy-k3-common.h diff --git a/drivers/phy/spacemit/Kconfig b/drivers/phy/spacemit/Kconfig index 50b0005acf66..5fdf18fce499 100644 --- a/drivers/phy/spacemit/Kconfig +++ b/drivers/phy/spacemit/Kconfig @@ -23,3 +23,19 @@ config PHY_SPACEMIT_K1_USB2 help Enable this to support K1 USB 2.0 PHY driver. This driver takes care of enabling and clock setup and will be used by K1 udc/ehci/otg/xhci driver. + +config PHY_SPACEMIT_K3_COMMON_OPS + tristate + select MFD_SYSCON + select GENERIC_PHY + +config PHY_SPACEMIT_K3_COMBO_PHY + tristate "SpacemiT K3 USB3/PCIe PHY support" + depends on (ARCH_SPACEMIT || COMPILE_TEST) && OF + depends on COMMON_CLK + select PHY_SPACEMIT_K3_COMMON_OPS + help + Enable this to support K3 USB3/PCIe combo PHY driver. This + driver takes care of enabling and clock setup and will be used + by K3 dwc3 driver. + If unsure, say N. diff --git a/drivers/phy/spacemit/Makefile b/drivers/phy/spacemit/Makefile index a821a21d6142..f679c1851b4f 100644 --- a/drivers/phy/spacemit/Makefile +++ b/drivers/phy/spacemit/Makefile @@ -1,3 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_PHY_SPACEMIT_K1_PCIE) += phy-k1-pcie.o obj-$(CONFIG_PHY_SPACEMIT_K1_USB2) += phy-k1-usb2.o +obj-$(CONFIG_PHY_SPACEMIT_K3_COMBO_PHY) += phy-k3-combo.o +obj-$(CONFIG_PHY_SPACEMIT_K3_COMMON_OPS) += phy-k3-common.o diff --git a/drivers/phy/spacemit/phy-k3-combo.c b/drivers/phy/spacemit/phy-k3-combo.c new file mode 100644 index 000000000000..0bc08bb6247a --- /dev/null +++ b/drivers/phy/spacemit/phy-k3-combo.c @@ -0,0 +1,252 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * phy-k3-combo.c - SpacemiT K3 PCIe/USB3 Combo PHY Driver + * + * Copyright (c) 2025 SpacemiT Technology Co. Ltd + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "phy-k3-common.h" + +/* + * The PCIE/USB Subsystem on SpacemiT K3 have 3 single lane PIPE3 PHYs + * (PHY2/3/4) shared by PCIE PortC/D and USB3 PortB/C/D. + * + * PMUA_PCIE_SUBSYS_MGMT[4:0] + * + * bit4 = 0 : PCIe A X8 mode, all 8 lanes dedicated to PCIe Port A + * 1 : PHY lanes shared between PCIe or USB according to [3:0] + * + * All PHY matrix combinations according to [4:0]: + * + * 0x0X : PCIe-A X8 + * 0x10 : PCIe-C x2 (PHY2+PHY3) + PCIe-D x1 (PHY4) + * 0x11 : PCIe-C x2 (PHY2+PHY3) + USB-D (PHY4) + * 0x12 : PCIe-C x1 (PHY2) + USB-C (PHY3) + * 0x13 : PCIe-C x1 (PHY2) + USB-C (PHY3) + USB-D (PHY4) + * 0x14 : PCIe-C x1 (PHY3) + USB-B (PHY2) + * 0x15 : PCIe-C x1 (PHY3) + USB-B (PHY2) + USB-D (PHY4) + * 0x16 : USB-B (PHY2) + USB-C (PHY3) + PCIe D x1 (PHY4) + * 0x17 : USB-B (PHY2) + USB-C (PHY3) + USB-D (PHY4) + * + * So any USB Port B/C/D operation requires PCIe A X8 mode to be disabled. + */ +#define PMUA_PCIE_SUBSYS_MGMT 0x1d8 +#define PU_MATRIX_CONF_MASK GENMASK(4, 0) + +#define COMBPHY_MAX_SUBPHYS 6 + +struct k3_combo_phy { + struct device *dev; + struct k3_lane_group groups[COMBPHY_MAX_SUBPHYS]; + void __iomem *base; + struct regmap *apb_spare; +}; + +static const struct k3_phy_lane_group_data k3_combphy_lane_group0 = { + .lanes = 2, + .config = 0xff, + .mask = 0x00, + .offsets = { + 0x0, 0x400 + }, +}; + +static const struct k3_phy_lane_group_data k3_combphy_lane_group1 = { + .lanes = 2, + .config = 0xff, + .mask = 0x00, + .offsets = { + 0x100000, 0x100400 + }, +}; + +static const struct k3_phy_lane_group_data k3_combphy_lane_group2 = { + .lanes = 1, + .config = 0x14, + .mask = 0x14, + .offsets = { + 0x200000 + }, +}; + +static const struct k3_phy_lane_group_data k3_combphy_lane_group3 = { + .lanes = 1, + .config = 0x12, + .mask = 0x12, + .offsets = { + 0x300000 + }, +}; + +static const struct k3_phy_lane_group_data k3_combphy_lane_group4 = { + .lanes = 1, + .config = 0x11, + .mask = 0x11, + .offsets = { + 0x400000 + }, +}; + +static const struct k3_phy_lane_group_data k3_combphy_lane_group5 = { + .lanes = 1, + .config = 0xff, + .mask = 0x00, + .offsets = { + 0x500000 + }, +}; + +static const struct k3_phy_lane_group_data *k3_combphy_lane_datas[] = { + &k3_combphy_lane_group0, + &k3_combphy_lane_group1, + &k3_combphy_lane_group2, + &k3_combphy_lane_group3, + &k3_combphy_lane_group4, + &k3_combphy_lane_group5, +}; + +static int k3_combo_phy_init_lanes(struct k3_combo_phy *phy, unsigned int config) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(k3_combphy_lane_datas); i++) { + const struct k3_phy_lane_group_data *data = k3_combphy_lane_datas[i]; + struct k3_lane_group *lg = &phy->groups[i]; + const struct phy_ops *ops; + bool is_usb; + + is_usb = (data->mask & config) == data->config; + if (is_usb) + ops = &k3_usb3_phy_ops; + else + ops = &k3_pcie_phy_ops; + + dev_dbg(phy->dev, "phy %d is %s\n", i, is_usb ? "usb" : "pcie"); + + lg->phy = devm_phy_create(phy->dev, NULL, ops); + if (IS_ERR(lg->phy)) + return PTR_ERR(lg->phy); + + lg->is_pcie = !is_usb; + lg->data = data; + lg->base = phy->base; + phy_set_drvdata(lg->phy, lg); + } + + return 0; +} + +static int k3_combo_phy_update_config(struct regmap *apmu, unsigned int config) +{ + if (config & ~PU_MATRIX_CONF_MASK) + return -EINVAL; + + return regmap_update_bits(apmu, PMUA_PCIE_SUBSYS_MGMT, PU_MATRIX_CONF_MASK, config); +} + +static struct phy *k3_combo_phy_xlate(struct device *dev, const struct of_phandle_args *args) +{ + struct k3_combo_phy *phy = dev_get_drvdata(dev); + struct k3_lane_group *lg; + + if (args->args_count != 2) { + dev_err(dev, "Invalid number of arguments\n"); + return ERR_PTR(-EINVAL); + } + + if (args->args[0] >= ARRAY_SIZE(k3_combphy_lane_datas)) { + dev_err(dev, "Invalid PHY id\n"); + return ERR_PTR(-EINVAL); + } + + lg = &phy->groups[args->args[0]]; + + if ((lg->is_pcie && args->args[1] != PHY_TYPE_PCIE) || + (!lg->is_pcie && args->args[1] != PHY_TYPE_USB3)) { + dev_err(dev, "Invalid PHY mode\n"); + return ERR_PTR(-EINVAL); + } + + return lg->phy; +} + +static int k3_combo_phy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *node = dev->of_node; + struct phy_provider *provider; + struct k3_combo_phy *phy; + struct regmap *apmu; + u32 config = 0; + int ret; + + phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); + if (!phy) + return -ENOMEM; + + phy->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(phy->base)) + return PTR_ERR(phy->base); + + phy->apb_spare = syscon_regmap_lookup_by_phandle(node, "spacemit,apb-spare"); + if (IS_ERR(phy->apb_spare)) + return dev_err_probe(dev, PTR_ERR(phy->apb_spare), + "Failed to find APB SPARE syscon"); + + apmu = syscon_regmap_lookup_by_phandle_args(node, "spacemit,apmu", 1, &config); + if (IS_ERR(apmu)) + return dev_err_probe(dev, PTR_ERR(apmu), + "Failed to find APMU syscon"); + + ret = k3_combo_phy_update_config(apmu, config); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to set lane configuration"); + + phy->dev = dev; + platform_set_drvdata(pdev, phy); + + ret = k3_phy_calibrate(phy->apb_spare); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to calibrate phy"); + + ret = k3_combo_phy_init_lanes(phy, config); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to init lanes"); + + provider = devm_of_phy_provider_register(dev, k3_combo_phy_xlate); + if (IS_ERR(provider)) + return dev_err_probe(dev, PTR_ERR(provider), + "Failed to register provider\n"); + + return 0; +} + +static const struct of_device_id k3_combo_phy_of_match[] = { + { .compatible = "spacemit,k3-combo-phy" }, + { }, +}; +MODULE_DEVICE_TABLE(of, k3_combo_phy_of_match); + +static struct platform_driver k3_combo_phy_driver = { + .probe = k3_combo_phy_probe, + .driver = { + .name = "spacemit,k3-combo-phy", + .of_match_table = k3_combo_phy_of_match, + }, +}; +module_platform_driver(k3_combo_phy_driver); + +MODULE_DESCRIPTION("SpacemiT K3 USB3/PCIe combo PHY driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/phy/spacemit/phy-k3-common.c b/drivers/phy/spacemit/phy-k3-common.c new file mode 100644 index 000000000000..0b289e63db99 --- /dev/null +++ b/drivers/phy/spacemit/phy-k3-common.c @@ -0,0 +1,391 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "phy-k3-common.h" + +/* PHY Registers */ +#define PHY_VERSION 0x0 + +#define PHY_RESET_CFG 0x04 + +#define PHY_RESET_RXBUF_RST BIT(0) +#define PHY_RESET_SOFT_RST_PCS BIT(1) +#define PHY_RESET_SOFT_RST_AHB BIT(2) +#define PHY_RESET_EN_SD_AFTER_LOCK BIT(6) + +#define PHY_CLK_CFG 0x08 + +#define PHY_CLK_PLL_READY BIT(0) +#define PHY_CLK_TXCLK_INV BIT(2) +#define PHY_CLK_RXCLK_EN BIT(3) +#define PHY_CLK_TXCLK_EN BIT(4) +#define PHY_CLK_PCLK_EN BIT(5) +#define PHY_CLK_PIPE_PCLK_EN BIT(6) +#define PHY_CLK_REFCLK_FREQ GENMASK(10, 7) +#define PHY_CLK_REFCLK_24M 2 +#define PHY_CLK_SW_INIT_DONE BIT(11) +#define PHY_CLK_PU_SSC_OUT BIT(23) + +#define PHY_MODE_CFG 0x0C + +#define PHY_MODE_PCIE_INT_EN BIT(0) +#define PHY_MODE_LFPS_TPERIOD GENMASK(9, 8) +#define PHY_MODE_LFPS_TPERIOD_USB 3 + +#define PHY_PU_SEL 0x40 + +#define PHY_PU_CFG_STATUS BIT(9) +#define PHY_PU_OVRD_STATUS BIT(10) + +#define PHY_PU_CK_REG 0x54 + +#define PHY_PU_REFCLK_100 BIT(25) + +#define PHY_PLL_REG1 0x58 + +#define PHY_PLL_FREF_SEL GENMASK(15, 13) +#define PHY_PLL_FREF_24M 0x1 +#define PHY_PLL_SSC_DEP_SEL GENMASK(27, 24) +#define PHY_PLL_SSC_5000PPM 0xa +#define PHY_PLL_SSC_MODE GENMASK(29, 28) +#define PHY_PLL_SSC_MODE_CENTER_SPREAD 0 +#define PHY_PLL_SSC_MODE_UP_SPREAD 1 +#define PHY_PLL_SSC_MODE_DOWN_SPREAD 2 +#define PHY_PLL_SSC_MODE_DOWN_SPREAD1 3 + +#define PHY_PLL_REG2 0x5c + +#define PHY_PLL_SEL_REF100 BIT(21) + +/* PHY RX Register Definitions */ +#define PHY_RX_REG_A 0x60 + +#define PHY_RX_REG0_MASK GENMASK(7, 0) +#define PHY_RX_REG1_MASK GENMASK(15, 8) +#define PHY_RX_REG2_MASK GENMASK(23, 16) +#define PHY_RX_REG3_MASK GENMASK(31, 24) + +#define PHY_RX_REG0_RLOAD BIT(4) +#define PHY_RX_REG1_RTERM GENMASK(11, 8) +#define PHY_RX_REG1_RC_CALI GENMASK(15, 12) +#define PHY_RX_REG2_CSEL GENMASK(19, 16) +#define PHY_RX_REG2_FORCE_CSEL BIT(20) +#define PHY_RX_REG2_PSEL GENMASK(23, 21) +#define PHY_RX_REG3_I_LOAD GENMASK(26, 24) +#define PHY_RX_REG3_SEL_CBOOST_CODE BIT(27) +#define PHY_RX_REG3_ADJ_BIAS GENMASK(29, 28) +#define PHY_RX_REG3_RDEG1 GENMASK(31, 30) + +#define PHY_RX_REG_B 0x64 + +#define PHY_RX_REG4_MASK GENMASK(7, 0) +#define PHY_RX_REG5_MASK GENMASK(15, 8) +#define PHY_RX_REG6_MASK GENMASK(23, 16) + +#define PHY_RX_REGB_MASK GENMASK(23, 0) + +#define PHY_RX_REG4_RDEG2 GENMASK(2, 1) +#define PHY_RX_REG4_ENVOS BIT(4) +#define PHY_RX_REG4_RTERM_SEL BIT(5) +#define PHY_RX_REG4_MANUAL_CFG BIT(7) +#define PHY_RX_REG5_RCELL_VCM GENMASK(11, 8) +#define PHY_RX_REG5_RCELL_BIAS GENMASK(15, 12) +#define PHY_RX_REG6_H1_REG GENMASK(19, 16) +#define PHY_RX_REG6_ADAPT_GAIN GENMASK(21, 20) +#define PHY_RX_REG6_BYPASS_ADPT BIT(22) + +#define PHY_ADPT_CFG0 0x140 +#define PHY_ADPT_AFE_RST_OVRD_EN BIT(1) +#define PHY_ADPT_AFE_RST_OVRD_VAL BIT(4) + +#define PHY_RXEQ_TIME 0xb4 +#define PHY_RXEQ_TIME_OVRD_POST_C_SOC BIT(21) +#define PHY_RXEQ_TIME_CFG_AMP_SOC GENMASK(23, 22) +#define PHY_RXEQ_TIME_AMP_SOC_650M 0 +#define PHY_RXEQ_TIME_AMP_SOC_800M 1 +#define PHY_RXEQ_TIME_AMP_SOC_870M 2 +#define PHY_RXEQ_TIME_AMP_SOC_900M 3 +#define PHY_RXEQ_TIME_OVRD_AMP_SOC BIT(24) + +#define PCIE_PU_ADDR_CLK_CFG 0x0008 +#define PHY_CLK_PLL_READY BIT(0) +#define PCIE_INITAL_TIMER GENMASK(6, 3) +#define CFG_INTERNAL_TIMER_ADJ GENMASK(10, 7) +#define CFG_SW_PHY_INIT_DONE BIT(11) + +/* Lane RX/TX configuration (per‑lane, at lane_base) */ +#define PCIE_RX_REG1 0x050 +#define PCIE_RX_REFCLK_MODE GENMASK(1, 0) +#define PCIE_RX_REFCLK_MODE_DRIVER 1 +#define PCIE_RX_SEL_TRI_CODE BIT(2) +#define PCIE_RX_LEGACY GENMASK(15, 8) +#define PCIE_RX_LEGACY_DEFAULT 0x65 + +#define PCIE_TX_REG1 0x064 + +#define PCIE_PLL_TIMEOUT 500000 +#define PCIE_POLL_DELAY 500 + +static int k3_usb3phy_init_single(struct k3_lane_group *lg, void __iomem *base) +{ + struct phy *phy = lg->phy; + u32 val, tmp; + int ret; + + /* Do not wait CDR lock before sampling data */ + val = readl(base + PHY_RESET_CFG); + val = u32_replace_bits(val, 0, PHY_RESET_EN_SD_AFTER_LOCK); + writel(val, base + PHY_RESET_CFG); + + /* Power down 100MHz refclk buffer */ + val = readl(base + PHY_PU_CK_REG); + val = u32_replace_bits(val, 0, PHY_PU_REFCLK_100); + writel(val, base + PHY_PU_CK_REG); + + /* Program PLL REG1 configure the SSC */ + val = FIELD_PREP(PHY_PLL_SSC_MODE, PHY_PLL_SSC_MODE_DOWN_SPREAD1) | + FIELD_PREP(PHY_PLL_SSC_DEP_SEL, PHY_PLL_SSC_5000PPM) | + FIELD_PREP(PHY_PLL_FREF_SEL, PHY_PLL_FREF_24M); + writel(val, base + PHY_PLL_REG1); + + /* Un-select 100MHz PLL reference */ + val = readl(base + PHY_PLL_REG2); + val = u32_replace_bits(val, 0, PHY_PLL_SEL_REF100); + writel(val, base + PHY_PLL_REG2); + + /* USB LFPS period configuration */ + val = readl(base + PHY_MODE_CFG); + val = u32_replace_bits(val, PHY_MODE_LFPS_TPERIOD_USB, PHY_MODE_LFPS_TPERIOD); + writel(val, base + PHY_MODE_CFG); + + /* Force AFE adaptation reset */ + val = readl(base + PHY_ADPT_CFG0); + val |= PHY_ADPT_AFE_RST_OVRD_EN | PHY_ADPT_AFE_RST_OVRD_VAL; + writel(val, base + PHY_ADPT_CFG0); + + /* Override driver amplitude value to 900m */ + val = readl(base + PHY_RXEQ_TIME); + val |= PHY_RXEQ_TIME_OVRD_AMP_SOC; + val = u32_replace_bits(val, PHY_RXEQ_TIME_AMP_SOC_900M, PHY_RXEQ_TIME_CFG_AMP_SOC); + writel(val, base + PHY_RXEQ_TIME); + + /* Configure RX parameters */ + val = PHY_RX_REG0_RLOAD | + FIELD_PREP(PHY_RX_REG1_RTERM, 0x8) | + FIELD_PREP(PHY_RX_REG1_RC_CALI, 0x7) | + FIELD_PREP(PHY_RX_REG2_CSEL, 0x8) | + PHY_RX_REG2_FORCE_CSEL | + FIELD_PREP(PHY_RX_REG2_PSEL, 0x4) | + FIELD_PREP(PHY_RX_REG3_I_LOAD, 0x7) | + PHY_RX_REG3_SEL_CBOOST_CODE | + FIELD_PREP(PHY_RX_REG3_ADJ_BIAS, 0x1) | + FIELD_PREP(PHY_RX_REG3_RDEG1, 0x3); + writel(val, base + PHY_RX_REG_A); + + val = readl(base + PHY_RX_REG_B); + tmp = FIELD_PREP(PHY_RX_REG4_RDEG2, 0x2) | + PHY_RX_REG4_ENVOS | PHY_RX_REG4_RTERM_SEL | PHY_RX_REG4_MANUAL_CFG | + FIELD_PREP(PHY_RX_REG5_RCELL_VCM, 0x8) | + FIELD_PREP(PHY_RX_REG5_RCELL_BIAS, 0x8) | + FIELD_PREP(PHY_RX_REG6_H1_REG, 0x8) | + FIELD_PREP(PHY_RX_REG6_ADAPT_GAIN, 0x2); + val = u32_replace_bits(val, tmp, PHY_RX_REGB_MASK); + writel(val, base + PHY_RX_REG_B); + + /* + * Inform PHY that all PLL-related configuration is done. + * PLL will not start locking until PHY_CLK_SW_INIT_DONE is set. + */ + val = PHY_CLK_SW_INIT_DONE | PHY_CLK_PU_SSC_OUT | + FIELD_PREP(PHY_CLK_REFCLK_FREQ, PHY_CLK_REFCLK_24M) | + PHY_CLK_RXCLK_EN | PHY_CLK_TXCLK_EN | + PHY_CLK_PCLK_EN | PHY_CLK_PIPE_PCLK_EN; + writel(val, base + PHY_CLK_CFG); + + ret = readl_poll_timeout(base + PHY_CLK_CFG, val, + (val & PHY_CLK_PLL_READY), + PCIE_POLL_DELAY, PCIE_PLL_TIMEOUT); + if (ret) { + dev_err(&phy->dev, "PHY PLL polling timeout\n"); + return ret; + } + + return 0; +} + +static int k3_usb3phy_init(struct phy *phy) +{ + struct k3_lane_group *lg = phy_get_drvdata(phy); + int ret, i; + + for (i = 0; i < lg->data->lanes; i++) { + ret = k3_usb3phy_init_single(lg, lg->base + lg->data->offsets[i]); + if (ret < 0) + return ret; + } + + return 0; +} + +const struct phy_ops k3_usb3_phy_ops = { + .init = k3_usb3phy_init, + .owner = THIS_MODULE, +}; +EXPORT_SYMBOL_GPL(k3_usb3_phy_ops); + +static int k3_pcie_phy_init(struct phy *phy) +{ + struct k3_lane_group *lg = phy_get_drvdata(phy); + void __iomem *phy_base = lg->base + lg->data->offsets[0]; + u32 val; + int ret; + int i; + + val = readl(phy_base + PHY_PLL_REG1); + val = u32_replace_bits(val, 0x2, GENMASK(15, 12)); + writel(val, phy_base + PHY_PLL_REG1); + + val = readl(phy_base + PHY_PLL_REG2); + val = u32_replace_bits(val, 0, BIT(21)); + writel(val, phy_base + PHY_PLL_REG2); + + for (i = 0; i < lg->data->lanes; i++) { + void __iomem *lane_base = lg->base + lg->data->offsets[i]; + + val = readl(lane_base + PCIE_RX_REG1); + val = u32_replace_bits(val, 0, 0x3); + writel(val, lane_base + PCIE_RX_REG1); + } + + val = readl(phy_base + PHY_PLL_REG2); + val |= BIT(20); + writel(val, phy_base + PHY_PLL_REG2); + + /* The write is needed as clock requires renegotiation */ + val = FIELD_PREP(PCIE_RX_REFCLK_MODE, PCIE_RX_REFCLK_MODE_DRIVER) | + PCIE_RX_SEL_TRI_CODE | + FIELD_PREP(PCIE_RX_LEGACY, PCIE_RX_LEGACY_DEFAULT); + writel(val, phy_base + PCIE_RX_REG1); + + /* pll_reg1 of lane0, disable SSC: pll[27:24] = 0 */ + val = readl(phy_base + PHY_PLL_REG1); + val = u32_replace_bits(val, 0, GENMASK(27, 24)); + writel(val, phy_base + PHY_PLL_REG1); + + for (i = 0; i < lg->data->lanes; i++) { + void __iomem *lane_base = lg->base + lg->data->offsets[i]; + + /* set cfg_tx_send_dummy_data to be 1'b1 for disable dash data */ + val = readl(lane_base + PHY_PU_SEL); + val = u32_replace_bits(val, 1, BIT(13)); + writel(val, lane_base + PHY_PU_SEL); + + /* disable en_sample_data_after_cdr_locked */ + val = readl(lane_base + PHY_RESET_CFG); + val = u32_replace_bits(val, 0, BIT(6)); + writel(val, lane_base + PHY_RESET_CFG); + + /* Dynamic Lock */ + val = readl(lane_base + PHY_MODE_CFG); + val = u32_replace_bits(val, 1, BIT(2)); + writel(val, lane_base + PHY_MODE_CFG); + + val = FIELD_PREP(PHY_RX_REG0_MASK, 0x10) | + FIELD_PREP(PHY_RX_REG1_MASK, 0x78) | + FIELD_PREP(PHY_RX_REG2_MASK, 0x98) | + FIELD_PREP(PHY_RX_REG3_MASK, 0xdf); + writel(val, lane_base + PHY_RX_REG_A); + + val = readl(lane_base + PHY_RX_REG_B); + val &= ~PHY_RX_REGB_MASK; + val |= FIELD_PREP(PHY_RX_REG4_MASK, 0xb4) | + FIELD_PREP(PHY_RX_REG5_MASK, 0x88) | + FIELD_PREP(PHY_RX_REG6_MASK, 0x28); + writel(val, lane_base + PHY_RX_REG_B); + + /* Set init done */ + val = readl(lane_base + PCIE_PU_ADDR_CLK_CFG); + val = u32_replace_bits(val, 1, CFG_SW_PHY_INIT_DONE); + writel(val, lane_base + PCIE_PU_ADDR_CLK_CFG); + } + + ret = readl_poll_timeout(phy_base + PCIE_PU_ADDR_CLK_CFG, val, + (val & PHY_CLK_PLL_READY), PCIE_POLL_DELAY, + PCIE_PLL_TIMEOUT); + if (ret) { + dev_err(&lg->phy->dev, "PHY PLL lock timeout\n"); + return ret; + } + + return 0; +} + +const struct phy_ops k3_pcie_phy_ops = { + .init = k3_pcie_phy_init, + .owner = THIS_MODULE, +}; +EXPORT_SYMBOL_GPL(k3_pcie_phy_ops); + +/* PHY rcal init requires APB_SPARE regmap access */ + +#define APB_SPARE_PU_CAL 0x178 +#define PU_CAL BIT(17) + +#define APB_SPARE_RCAL_HSIO 0x17c +#define APB_SPARE_PU_CAL_DONE BIT(8) +#define RCAL_OVRD_PTRIM GENMASK(23, 20) +#define RCAL_OVRD_NTRIM GENMASK(27, 24) +#define RCAL_OVRD_PTRIM_EN BIT(28) +#define RCAL_OVRD_NTRIM_EN BIT(29) +#define RCAL_OVRD_STABLE_VAL BIT(30) +#define RCAL_OVRD_STABLE_EN BIT(31) + +#define RCAL_OVRD_TRIM_EN (RCAL_OVRD_NTRIM_EN | RCAL_OVRD_PTRIM_EN) +#define RCAL_OVRD_TRIM_MASK (RCAL_OVRD_NTRIM | RCAL_OVRD_PTRIM) + +#define PU_CAL_TIMEOUT 2000000 + +static DEFINE_MUTEX(calibrate_lock); + +int k3_phy_calibrate(struct regmap *apb_spare) +{ + unsigned int val = 0; + int ret; + + guard(mutex)(&calibrate_lock); + + regmap_read(apb_spare, APB_SPARE_RCAL_HSIO, &val); + if (val & APB_SPARE_PU_CAL_DONE) + return 0; + + regmap_update_bits(apb_spare, APB_SPARE_PU_CAL, PU_CAL, + PU_CAL); + + ret = regmap_read_poll_timeout(apb_spare, APB_SPARE_RCAL_HSIO, + val, (val & APB_SPARE_PU_CAL_DONE), PCIE_POLL_DELAY, + PU_CAL_TIMEOUT); + + if (ret) + regmap_update_bits(apb_spare, APB_SPARE_RCAL_HSIO, + RCAL_OVRD_TRIM_EN | RCAL_OVRD_STABLE_VAL | + RCAL_OVRD_TRIM_MASK | RCAL_OVRD_STABLE_EN, + RCAL_OVRD_TRIM_EN | RCAL_OVRD_STABLE_VAL | + FIELD_PREP(RCAL_OVRD_NTRIM, 0x6) | + FIELD_PREP(RCAL_OVRD_PTRIM, 0xa) | + RCAL_OVRD_STABLE_EN); + + return 0; +} +EXPORT_SYMBOL_GPL(k3_phy_calibrate); + +MODULE_DESCRIPTION("SpacemiT K3 PHY common ops"); +MODULE_LICENSE("GPL"); diff --git a/drivers/phy/spacemit/phy-k3-common.h b/drivers/phy/spacemit/phy-k3-common.h new file mode 100644 index 000000000000..49009c3c313a --- /dev/null +++ b/drivers/phy/spacemit/phy-k3-common.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef _PHY_K3_COMMON_H +#define _PHY_K3_COMMON_H + +#include + +struct k3_phy_lane_group_data { + u32 lanes; + u8 config; + u8 mask; + u32 offsets[] __counted_by(lanes); +}; + +struct k3_lane_group { + const struct k3_phy_lane_group_data *data; + void __iomem *base; + struct phy *phy; + bool is_pcie; +}; + +extern const struct phy_ops k3_pcie_phy_ops; +extern const struct phy_ops k3_usb3_phy_ops; + +int k3_phy_calibrate(struct regmap *apb_spare); + +#endif From 215cd5def8eca1c2aa7ca043483d96155a1c3ed6 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Mon, 13 Jul 2026 12:38:04 +0200 Subject: [PATCH 09/76] dt-bindings: phy: mediatek,dsi-phy: Add support for MT8196 Add support for the MediaTek Kompanio Ultra (MT8196) SoC: this chip features a DSI PHY that is similar to the one found in the MT8183 SoC, but is a new (incremental) revision with a different register layout. Acked-by: Krzysztof Kozlowski Signed-off-by: AngeloGioacchino Del Regno Link: https://patch.msgid.link/20260713103805.23030-2-angelogioacchino.delregno@collabora.com Signed-off-by: Vinod Koul --- Documentation/devicetree/bindings/phy/mediatek,dsi-phy.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/phy/mediatek,dsi-phy.yaml b/Documentation/devicetree/bindings/phy/mediatek,dsi-phy.yaml index c6d0bbdbe0e2..9df1dbe1253b 100644 --- a/Documentation/devicetree/bindings/phy/mediatek,dsi-phy.yaml +++ b/Documentation/devicetree/bindings/phy/mediatek,dsi-phy.yaml @@ -39,6 +39,7 @@ properties: - const: mediatek,mt2701-mipi-tx - const: mediatek,mt8173-mipi-tx - const: mediatek,mt8183-mipi-tx + - const: mediatek,mt8196-mipi-tx reg: maxItems: 1 From 2ec9c210c1f931389709aee403d28e4f32676385 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Mon, 13 Jul 2026 12:38:05 +0200 Subject: [PATCH 10/76] phy: mediatek: Add support for MT8196 MIPI DSI PHY Add support for the MIPI DSI PHY found in the MediaTek MT8196 SoC and its variants. This PHY has a different register layout and provides support for more hardware features compared to the previous generation. This initial driver only adds support for basic functionality that is necessary to drive MIPI DSI displays as a D-PHY. Feature additions like lane-swap, DPHY/CPHY switching, dual-port, and others, may be done in the future. Signed-off-by: AngeloGioacchino Del Regno Link: https://patch.msgid.link/20260713103805.23030-3-angelogioacchino.delregno@collabora.com Signed-off-by: Vinod Koul --- drivers/phy/mediatek/Makefile | 1 + .../phy/mediatek/phy-mtk-mipi-dsi-mt8196.c | 201 ++++++++++++++++++ drivers/phy/mediatek/phy-mtk-mipi-dsi.c | 1 + drivers/phy/mediatek/phy-mtk-mipi-dsi.h | 2 +- 4 files changed, 204 insertions(+), 1 deletion(-) create mode 100644 drivers/phy/mediatek/phy-mtk-mipi-dsi-mt8196.c diff --git a/drivers/phy/mediatek/Makefile b/drivers/phy/mediatek/Makefile index 1b8088df71e8..ed0da708759b 100644 --- a/drivers/phy/mediatek/Makefile +++ b/drivers/phy/mediatek/Makefile @@ -21,4 +21,5 @@ obj-$(CONFIG_PHY_MTK_MIPI_CSI_0_5) += phy-mtk-mipi-csi-0-5.o phy-mtk-mipi-dsi-drv-y := phy-mtk-mipi-dsi.o phy-mtk-mipi-dsi-drv-y += phy-mtk-mipi-dsi-mt8173.o phy-mtk-mipi-dsi-drv-y += phy-mtk-mipi-dsi-mt8183.o +phy-mtk-mipi-dsi-drv-y += phy-mtk-mipi-dsi-mt8196.o obj-$(CONFIG_PHY_MTK_MIPI_DSI) += phy-mtk-mipi-dsi-drv.o diff --git a/drivers/phy/mediatek/phy-mtk-mipi-dsi-mt8196.c b/drivers/phy/mediatek/phy-mtk-mipi-dsi-mt8196.c new file mode 100644 index 000000000000..eac3db9922db --- /dev/null +++ b/drivers/phy/mediatek/phy-mtk-mipi-dsi-mt8196.c @@ -0,0 +1,201 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2019 MediaTek Inc. + * Author: jitao.shi + * + * Copyright (c) 2026 Collabora Ltd. + * AngeloGioacchino Del Regno + */ + +#include "phy-mtk-io.h" +#include "phy-mtk-mipi-dsi.h" + +#define MIPITX_LANE_CON 0x0004 +#define RG_DSI_CPHY_T1DRV_EN BIT(0) +#define RG_DSI_ANA_CK_SEL BIT(1) +#define RG_DSI_PHY_CK_SEL BIT(2) +#define RG_DSI_CPHY_EN BIT(3) +#define RG_DSI_PHYCK_INV_EN BIT(4) +#define RG_DSI_PWR04_EN BIT(5) +#define RG_DSI_BG_LPF_EN BIT(6) +#define RG_DSI_BG_CORE_EN BIT(7) +#define RG_DSI_PAD_TIEL_SEL BIT(8) + +#define MIPITX_VOLTAGE_SEL 0x0008 +#define RG_DSI_HSTX_LDO_REF_SEL GENMASK(9, 6) +#define RG_DSI_PRD_REF_SEL GENMASK(5, 0) +#define RG_DSI_PRD_REF_MINI 0 +#define RG_DSI_PRD_REF_DEF 4 +#define RG_DSI_PRD_REF_MAX 7 + +#define MIPITX_PRESERVED 0x000c +#define MIPITX_PRESERVED_DEF 0xffff0040 +#define MIPITX_PRESERVED_MINI 0xffff00f0 + +#define MIPITX_PLL_PWR 0x0028 +#define AD_DSI_PLL_SDM_PWR_ON BIT(0) +#define AD_DSI_PLL_SDM_ISO_EN BIT(1) +#define MIPITX_PLL_CON0 0x002c +#define MIPITX_PLL_CON1 0x0030 +#define RG_DSI_PLL_EN BIT(0) +#define RG_DSI_PLL_POSDIV GENMASK(10, 8) +#define MIPITX_PLL_CON2 0x0034 +#define MIPITX_PLL_CON3 0x0038 +#define MIPITX_PLL_CON4 0x003c +#define RG_DSI_PLL_IBIAS GENMASK(11, 10) + +#define MIPITX_D2_SW_CTL_EN 0x015c +#define MIPITX_D0_SW_CTL_EN 0x025c +#define MIPITX_CK_CKMODE_EN 0x0320 +#define DSI_CK_CKMODE_EN BIT(0) +#define MIPITX_CK_SW_CTL_EN 0x035c +#define MIPITX_D1_SW_CTL_EN 0x045c +#define MIPITX_D3_SW_CTL_EN 0x055c +#define DSI_SW_CTL_EN BIT(0) + +#define DSI_PHY_XTAL_CLK_HZ 26000000 +#define DSI_PHY_PLL_MIN_RATE_HZ 125000000 +#define DSI_PHY_PLL_MAX_RATE_HZ 2000000000 + +static int mtk_mipi_tx_pll_enable(struct clk_hw *hw) +{ + struct mtk_mipi_tx *mipi_tx = mtk_mipi_tx_from_clk_hw(hw); + void __iomem *base = mipi_tx->regs; + u32 voltage = RG_DSI_PRD_REF_MINI; + u32 pres = MIPITX_PRESERVED_MINI; + unsigned long long pcw_calc; + unsigned int txdiv, txdiv0; + u32 pcw; + + dev_dbg(mipi_tx->dev, "enable: %u bps\n", mipi_tx->data_rate); + + if (mipi_tx->data_rate >= DSI_PHY_PLL_MAX_RATE_HZ) { + /* Select higher signaling voltage for fast data rates */ + voltage = RG_DSI_PRD_REF_DEF; + pres = MIPITX_PRESERVED_DEF; + txdiv = 1; + txdiv0 = 0; + } else if (mipi_tx->data_rate >= 1000000000) { + txdiv = 2; + txdiv0 = 1; + } else if (mipi_tx->data_rate >= 500000000) { + txdiv = 4; + txdiv0 = 2; + } else if (mipi_tx->data_rate > 250000000) { + txdiv = 8; + txdiv0 = 3; + } else if (mipi_tx->data_rate >= 125000000) { + txdiv = 16; + txdiv0 = 4; + } else { + return -EINVAL; + } + + pcw_calc = ((u64)(mipi_tx->data_rate / 2) * txdiv) << 24; + pcw_calc = div_u64(pcw_calc, DSI_PHY_XTAL_CLK_HZ); + + if (pcw_calc > U32_MAX) { + dev_err(mipi_tx->dev, "Calculated PCW=%llu overflow!\n", pcw_calc); + return -EINVAL; + } + pcw = (u32)pcw_calc; + + mtk_phy_update_field(base + MIPITX_VOLTAGE_SEL, RG_DSI_PRD_REF_SEL, voltage); + writel(pres, base + MIPITX_PRESERVED); + + /* Enable the SDM and wait for power to stabilize */ + mtk_phy_set_bits(base + MIPITX_PLL_PWR, AD_DSI_PLL_SDM_PWR_ON); + mtk_phy_clear_bits(base + MIPITX_PLL_CON1, RG_DSI_PLL_EN); + udelay(30); + + /* Disable isolation and program PLL's PCW and dividers */ + mtk_phy_clear_bits(base + MIPITX_PLL_PWR, AD_DSI_PLL_SDM_ISO_EN); + writel(pcw, base + MIPITX_PLL_CON0); + mtk_phy_update_field(base + MIPITX_PLL_CON1, RG_DSI_PLL_POSDIV, txdiv0); + + /* Enable the PLL and wait for the clock output to stabilize */ + mtk_phy_set_bits(base + MIPITX_PLL_CON1, RG_DSI_PLL_EN); + udelay(30); + + return 0; +} + +static void mtk_mipi_tx_pll_disable(struct clk_hw *hw) +{ + struct mtk_mipi_tx *mipi_tx = mtk_mipi_tx_from_clk_hw(hw); + void __iomem *base = mipi_tx->regs; + + mtk_phy_clear_bits(base + MIPITX_PLL_CON1, RG_DSI_PLL_EN); + + mtk_phy_set_bits(base + MIPITX_PLL_PWR, AD_DSI_PLL_SDM_ISO_EN); + mtk_phy_clear_bits(base + MIPITX_PLL_PWR, AD_DSI_PLL_SDM_PWR_ON); +} + +static int mtk_mipi_tx_pll_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + req->rate = clamp_val(req->rate, + DSI_PHY_PLL_MIN_RATE_HZ, DSI_PHY_PLL_MAX_RATE_HZ); + + return 0; +} + +static const struct clk_ops mtk_mipi_tx_pll_ops = { + .enable = mtk_mipi_tx_pll_enable, + .disable = mtk_mipi_tx_pll_disable, + .determine_rate = mtk_mipi_tx_pll_determine_rate, + .set_rate = mtk_mipi_tx_pll_set_rate, + .recalc_rate = mtk_mipi_tx_pll_recalc_rate, +}; + +static void mtk_mipi_tx_power_on_signal(struct phy *phy) +{ + struct mtk_mipi_tx *mipi_tx = phy_get_drvdata(phy); + void __iomem *base = mipi_tx->regs; + + /* BG_LPF_EN / BG_CORE_EN */ + writel(RG_DSI_PAD_TIEL_SEL | RG_DSI_BG_CORE_EN, base + MIPITX_LANE_CON); + /* Wait for MIPI core to enable */ + usleep_range(30, 100); + writel(RG_DSI_BG_CORE_EN | RG_DSI_BG_LPF_EN, base + MIPITX_LANE_CON); + + /* Switch OFF each Lane */ + mtk_phy_clear_bits(base + MIPITX_D0_SW_CTL_EN, DSI_SW_CTL_EN); + mtk_phy_clear_bits(base + MIPITX_D1_SW_CTL_EN, DSI_SW_CTL_EN); + mtk_phy_clear_bits(base + MIPITX_D2_SW_CTL_EN, DSI_SW_CTL_EN); + mtk_phy_clear_bits(base + MIPITX_D3_SW_CTL_EN, DSI_SW_CTL_EN); + mtk_phy_clear_bits(base + MIPITX_CK_SW_CTL_EN, DSI_SW_CTL_EN); + + /* + * The MIPI TX drive strength is in the range of 3000 ~ 6000 microamps: + * RG_DSI_HSTX_LDO_REF_SEL expresses an offset from the minimum drive + * strength (3000uA) and can add a maximum offset of 3000uA, reaching a + * maximum drive strength of 3000+3000=6000uA. + */ + mtk_phy_update_field(base + MIPITX_VOLTAGE_SEL, RG_DSI_HSTX_LDO_REF_SEL, + (mipi_tx->mipitx_drive - 3000) / 200); + + mtk_phy_set_bits(base + MIPITX_CK_CKMODE_EN, DSI_CK_CKMODE_EN); +} + +static void mtk_mipi_tx_power_off_signal(struct phy *phy) +{ + struct mtk_mipi_tx *mipi_tx = phy_get_drvdata(phy); + void __iomem *base = mipi_tx->regs; + + /* Switch ON each lane one by one */ + mtk_phy_set_bits(base + MIPITX_D0_SW_CTL_EN, DSI_SW_CTL_EN); + mtk_phy_set_bits(base + MIPITX_D1_SW_CTL_EN, DSI_SW_CTL_EN); + mtk_phy_set_bits(base + MIPITX_D2_SW_CTL_EN, DSI_SW_CTL_EN); + mtk_phy_set_bits(base + MIPITX_D3_SW_CTL_EN, DSI_SW_CTL_EN); + mtk_phy_set_bits(base + MIPITX_CK_SW_CTL_EN, DSI_SW_CTL_EN); + + writel(RG_DSI_PAD_TIEL_SEL | RG_DSI_BG_CORE_EN, base + MIPITX_LANE_CON); + writel(RG_DSI_PAD_TIEL_SEL, base + MIPITX_LANE_CON); +} + +const struct mtk_mipitx_data mt8196_mipitx_data = { + .mipi_tx_clk_ops = &mtk_mipi_tx_pll_ops, + .mipi_tx_enable_signal = mtk_mipi_tx_power_on_signal, + .mipi_tx_disable_signal = mtk_mipi_tx_power_off_signal, +}; diff --git a/drivers/phy/mediatek/phy-mtk-mipi-dsi.c b/drivers/phy/mediatek/phy-mtk-mipi-dsi.c index 065ea626093a..46f0cb3ac096 100644 --- a/drivers/phy/mediatek/phy-mtk-mipi-dsi.c +++ b/drivers/phy/mediatek/phy-mtk-mipi-dsi.c @@ -183,6 +183,7 @@ static const struct of_device_id mtk_mipi_tx_match[] = { { .compatible = "mediatek,mt2701-mipi-tx", .data = &mt2701_mipitx_data }, { .compatible = "mediatek,mt8173-mipi-tx", .data = &mt8173_mipitx_data }, { .compatible = "mediatek,mt8183-mipi-tx", .data = &mt8183_mipitx_data }, + { .compatible = "mediatek,mt8196-mipi-tx", .data = &mt8196_mipitx_data }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, mtk_mipi_tx_match); diff --git a/drivers/phy/mediatek/phy-mtk-mipi-dsi.h b/drivers/phy/mediatek/phy-mtk-mipi-dsi.h index 5d4876f1dc95..e6f967078e3b 100644 --- a/drivers/phy/mediatek/phy-mtk-mipi-dsi.h +++ b/drivers/phy/mediatek/phy-mtk-mipi-dsi.h @@ -42,5 +42,5 @@ unsigned long mtk_mipi_tx_pll_recalc_rate(struct clk_hw *hw, extern const struct mtk_mipitx_data mt2701_mipitx_data; extern const struct mtk_mipitx_data mt8173_mipitx_data; extern const struct mtk_mipitx_data mt8183_mipitx_data; - +extern const struct mtk_mipitx_data mt8196_mipitx_data; #endif From 910b828b22b7b91054b3bd4be676a017444b0e00 Mon Sep 17 00:00:00 2001 From: Krishna Chaitanya Chundru Date: Thu, 9 Jul 2026 12:05:16 +0530 Subject: [PATCH 11/76] phy: qcom: qmp-pcie: Skip PHY reset if already up If the bootloader has already powered up the PCIe PHY, performing a full reset and waiting for the PHY to come up again adds unnecessary delay during boot. Extend the existing skip_init handling by introducing a skip_reset condition. When skip_init is active and the PHY status indicates that the PHY is already operational, skip asserting and deasserting the no-csr reset while still enabling the required resources during power-on. This allows reusing the bootloader-initialized PHY state and avoids redundant PHY reinitialization and PCIe link retraining, which can add hundred's of milliseconds of delay. This relies on the assumption that when skip_init is enabled and the PHY is reported as up, the bootloader has already configured the PHY correctly and the link is in a usable state. Reviewed-by: Abel Vesa Reviewed-by: Konrad Dybcio Tested-by: Qiang Yu Signed-off-by: Krishna Chaitanya Chundru Link: https://patch.msgid.link/20260709-link_retain-v3-1-81a9d187bb61@oss.qualcomm.com Signed-off-by: Vinod Koul --- drivers/phy/qualcomm/phy-qcom-qmp-pcie.c | 31 ++++++++++++++++-------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c index 357a2e2f8ff6..f54f66429f37 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c @@ -5231,6 +5231,7 @@ static int qmp_pcie_init(struct phy *phy) struct qmp_pcie *qmp = phy_get_drvdata(phy); const struct qmp_phy_cfg *cfg = qmp->cfg; void __iomem *pcs = qmp->pcs; + bool skip_reset; int ret; /* @@ -5246,6 +5247,9 @@ static int qmp_pcie_init(struct phy *phy) qphy_checkbits(pcs, cfg->regs[QPHY_START_CTRL], SERDES_START | PCS_START) && qphy_checkbits(pcs, cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL], cfg->pwrdn_ctrl); + skip_reset = qmp->skip_init && !qphy_checkbits(pcs, cfg->regs[QPHY_PCS_STATUS], + cfg->phy_status); + if (!qmp->skip_init && !cfg->tbls.serdes_num) { dev_err(qmp->dev, "Init sequence not available\n"); return -ENODATA; @@ -5269,13 +5273,15 @@ static int qmp_pcie_init(struct phy *phy) } } - ret = reset_control_assert(qmp->nocsr_reset); - if (ret) { - dev_err(qmp->dev, "no-csr reset assert failed\n"); - goto err_assert_reset; - } + if (!skip_reset) { + ret = reset_control_assert(qmp->nocsr_reset); + if (ret) { + dev_err(qmp->dev, "no-csr reset assert failed\n"); + goto err_assert_reset; + } - usleep_range(200, 300); + usleep_range(200, 300); + } if (!qmp->skip_init) { ret = reset_control_bulk_deassert(cfg->num_resets, qmp->resets); @@ -5325,8 +5331,11 @@ static int qmp_pcie_power_on(struct phy *phy) void __iomem *pcs = qmp->pcs; void __iomem *status; unsigned int mask, val; + bool skip_reset; int ret; + skip_reset = qmp->skip_init && !qphy_checkbits(pcs, cfg->regs[QPHY_PCS_STATUS], + cfg->phy_status); /* * Write CSR register for PHY that doesn't support no_csr reset or has not * been initialized. @@ -5350,10 +5359,12 @@ static int qmp_pcie_power_on(struct phy *phy) if (ret) return ret; - ret = reset_control_deassert(qmp->nocsr_reset); - if (ret) { - dev_err(qmp->dev, "no-csr reset deassert failed\n"); - goto err_disable_pipe_clk; + if (!skip_reset) { + ret = reset_control_deassert(qmp->nocsr_reset); + if (ret) { + dev_err(qmp->dev, "no-csr reset deassert failed\n"); + goto err_disable_pipe_clk; + } } if (qmp->skip_init) From 1e0c8d50663ae2afd2307d617fd94a53f6a1dc02 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Fri, 12 Jun 2026 15:57:31 +0300 Subject: [PATCH 12/76] phy: lynx-10g: fix lynx_10g_pccr_val_enabled() The intention of the code is to extract the PCCR8_SGMIIa_CFG field out of the "pccr" value, not to create a new value with the PCCR8_SGMIIa_CFG field set to the "pccr" value. Since FIELD_GET() is implemented as ((reg) & (mask)) >> __bf_shf(mask) and FIELD_PREP() as (val) << __bf_shf(mask)) & (mask) and since "mask" is GENMASK(2, 0), in practice there is no functional difference between FIELD_GET() and FIELD_PREP(). But FIELD_GET() is logically the correct helper. Signed-off-by: Vladimir Oltean Link: https://patch.msgid.link/20260612125731.133330-1-vladimir.oltean@nxp.com Signed-off-by: Vinod Koul --- drivers/phy/freescale/phy-fsl-lynx-10g.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/phy/freescale/phy-fsl-lynx-10g.c b/drivers/phy/freescale/phy-fsl-lynx-10g.c index 38def160ef1a..1362d1ebb3d4 100644 --- a/drivers/phy/freescale/phy-fsl-lynx-10g.c +++ b/drivers/phy/freescale/phy-fsl-lynx-10g.c @@ -386,7 +386,7 @@ static void lynx_10g_backup_pccr_val(struct lynx_lane *lane) */ static bool lynx_10g_pccr_val_enabled(u32 pccr) { - return FIELD_PREP(PCCR8_SGMIIa_CFG, pccr) != 0; + return FIELD_GET(PCCR8_SGMIIa_CFG, pccr) != 0; } static bool lynx_10g_lane_is_3_125g(struct lynx_lane *lane) From 8d3f1d5ce02152044cd3fbae201e47742b957dfd Mon Sep 17 00:00:00 2001 From: Krishna Kurapati Date: Sun, 12 Jul 2026 20:58:34 +0530 Subject: [PATCH 13/76] dt-bindings: phy: qcom,qusb2: Document QUSB2 Phy for Shikra Update dt-bindings to add Shikra to QUSB2 Phy list. Shikra SoC has two High Speed QUSB2 Phys. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Krishna Kurapati Link: https://patch.msgid.link/20260712-usb-shikra-phy-v6-v6-1-1b3e51bf1541@oss.qualcomm.com Signed-off-by: Vinod Koul --- Documentation/devicetree/bindings/phy/qcom,qusb2-phy.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/phy/qcom,qusb2-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,qusb2-phy.yaml index 449c2a7e5fec..001fd0ccc985 100644 --- a/Documentation/devicetree/bindings/phy/qcom,qusb2-phy.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,qusb2-phy.yaml @@ -28,6 +28,7 @@ properties: - qcom,qcm2290-qusb2-phy - qcom,qcs615-qusb2-phy - qcom,sdm660-qusb2-phy + - qcom,shikra-qusb2-phy - qcom,sm4250-qusb2-phy - qcom,sm6115-qusb2-phy - items: From 49e3105844f8dbcb7f92567b77e9f3a0a802a433 Mon Sep 17 00:00:00 2001 From: Krishna Kurapati Date: Sun, 12 Jul 2026 20:58:35 +0530 Subject: [PATCH 14/76] dt-bindings: phy: qcs615-qmp-usb3dp: Add support for Shikra SoC 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 Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260712-usb-shikra-phy-v6-v6-2-1b3e51bf1541@oss.qualcomm.com Signed-off-by: Vinod Koul --- .../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 From 346121aed2866a62c57395689f021f97a532a749 Mon Sep 17 00:00:00 2001 From: Krishna Kurapati Date: Sun, 12 Jul 2026 20:58:36 +0530 Subject: [PATCH 15/76] phy: qcom-qusb2: Add support for Shikra Add init sequence and phy configuration for Shikra. Since the init sequence is same as that of QCS615, reuse the existing init table in Shikra. Signed-off-by: Krishna Kurapati Reviewed-by: Dmitry Baryshkov Link: https://patch.msgid.link/20260712-usb-shikra-phy-v6-v6-3-1b3e51bf1541@oss.qualcomm.com Signed-off-by: Vinod Koul --- drivers/phy/qualcomm/phy-qcom-qusb2.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/phy/qualcomm/phy-qcom-qusb2.c b/drivers/phy/qualcomm/phy-qcom-qusb2.c index eb93015be841..2e5fdb620cb2 100644 --- a/drivers/phy/qualcomm/phy-qcom-qusb2.c +++ b/drivers/phy/qualcomm/phy-qcom-qusb2.c @@ -381,6 +381,17 @@ static const struct qusb2_phy_cfg sdm660_phy_cfg = { .autoresume_en = BIT(3), }; +static const struct qusb2_phy_cfg shikra_phy_cfg = { + .tbl = qcs615_init_tbl, + .tbl_num = ARRAY_SIZE(qcs615_init_tbl), + .regs = msm8996_regs_layout, + + .has_pll_test = true, + .se_clk_scheme_default = true, + .disable_ctrl = CLAMP_N_EN | FREEZIO_N | POWER_DOWN, + .mask_core_ready = PLL_LOCKED, +}; + static const struct qusb2_phy_cfg sm6115_phy_cfg = { .tbl = sm6115_init_tbl, .tbl_num = ARRAY_SIZE(sm6115_init_tbl), @@ -958,6 +969,9 @@ static const struct of_device_id qusb2_phy_of_match_table[] = { }, { .compatible = "qcom,sdm660-qusb2-phy", .data = &sdm660_phy_cfg, + }, { + .compatible = "qcom,shikra-qusb2-phy", + .data = &shikra_phy_cfg, }, { .compatible = "qcom,sm4250-qusb2-phy", .data = &sm6115_phy_cfg, From edb7772bb3b5ebd5e8efacfaabaade643fc4b77b Mon Sep 17 00:00:00 2001 From: Krishna Kurapati Date: Sun, 12 Jul 2026 20:58:37 +0530 Subject: [PATCH 16/76] phy: qcom: qmp-usbc: Add qmp configuration for Shikra Add init sequence and phy configuration for the Super Speed port on Shikra SoC. Also since Shikra uses 3 resets, add support for the third reset and configure Shikra platform data to use 3 resets. Signed-off-by: Krishna Kurapati Reviewed-by: Dmitry Baryshkov Link: https://patch.msgid.link/20260712-usb-shikra-phy-v6-v6-4-1b3e51bf1541@oss.qualcomm.com Signed-off-by: Vinod Koul --- drivers/phy/qualcomm/phy-qcom-qmp-usbc.c | 53 ++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 3 deletions(-) diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-usbc.c b/drivers/phy/qualcomm/phy-qcom-qmp-usbc.c index ab3055bb5b0c..fa8492b487d7 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-usbc.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-usbc.c @@ -512,10 +512,14 @@ static const char * const usb3phy_reset_l[] = { "phy_phy", "phy", }; -static const char * const usb3dpphy_reset_l[] = { +static const char * const qcs615_usb3dpphy_reset_l[] = { "phy_phy", "dp_phy", }; +static const char * const shikra_usb3dpphy_reset_l[] = { + "phy_phy", "dp_phy", "phy", +}; + static const struct regulator_bulk_data qmp_phy_msm8998_vreg_l[] = { { .supply = "vdda-phy", .init_load_uA = 68600 }, { .supply = "vdda-pll", .init_load_uA = 14200 }, @@ -680,8 +684,48 @@ static const struct qmp_phy_cfg qcs615_usb3dp_phy_cfg = { .configure_dp_phy = qcs615_qmp_configure_dp_phy, .calibrate_dp_phy = qcs615_qmp_calibrate_dp_phy, - .reset_list = usb3dpphy_reset_l, - .num_resets = ARRAY_SIZE(usb3dpphy_reset_l), + .reset_list = qcs615_usb3dpphy_reset_l, + .num_resets = ARRAY_SIZE(qcs615_usb3dpphy_reset_l), + .vreg_list = qmp_phy_qcs615_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_qcs615_vreg_l), +}; + +static const struct qmp_phy_cfg shikra_usb3dp_phy_cfg = { + .offsets = &qmp_usbc_usb3dp_offsets_qcs615, + + .serdes_tbl = qcm2290_usb3_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(qcm2290_usb3_serdes_tbl), + .tx_tbl = qcm2290_usb3_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(qcm2290_usb3_tx_tbl), + .rx_tbl = qcm2290_usb3_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(qcm2290_usb3_rx_tbl), + .pcs_tbl = qcm2290_usb3_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(qcm2290_usb3_pcs_tbl), + + .regs = qmp_v3_usb3phy_regs_layout_qcm2290, + + .dp_serdes_tbl = qcs615_dp_serdes_tbl, + .dp_serdes_tbl_num = ARRAY_SIZE(qcs615_dp_serdes_tbl), + .dp_tx_tbl = qcs615_dp_tx_tbl, + .dp_tx_tbl_num = ARRAY_SIZE(qcs615_dp_tx_tbl), + + .serdes_tbl_rbr = qcs615_dp_serdes_tbl_rbr, + .serdes_tbl_rbr_num = ARRAY_SIZE(qcs615_dp_serdes_tbl_rbr), + .serdes_tbl_hbr = qcs615_dp_serdes_tbl_hbr, + .serdes_tbl_hbr_num = ARRAY_SIZE(qcs615_dp_serdes_tbl_hbr), + .serdes_tbl_hbr2 = qcs615_dp_serdes_tbl_hbr2, + .serdes_tbl_hbr2_num = ARRAY_SIZE(qcs615_dp_serdes_tbl_hbr2), + + .swing_tbl = &qcs615_dp_voltage_swing_hbr2_rbr, + .pre_emphasis_tbl = &qcs615_dp_pre_emphasis_hbr2_rbr, + + .dp_aux_init = qcs615_qmp_dp_aux_init, + .configure_dp_tx = qcs615_qmp_configure_dp_tx, + .configure_dp_phy = qcs615_qmp_configure_dp_phy, + .calibrate_dp_phy = qcs615_qmp_calibrate_dp_phy, + + .reset_list = shikra_usb3dpphy_reset_l, + .num_resets = ARRAY_SIZE(shikra_usb3dpphy_reset_l), .vreg_list = qmp_phy_qcs615_vreg_l, .num_vregs = ARRAY_SIZE(qmp_phy_qcs615_vreg_l), }; @@ -2019,6 +2063,9 @@ static const struct of_device_id qmp_usbc_of_match_table[] = { }, { .compatible = "qcom,sdm660-qmp-usb3-phy", .data = &sdm660_usb3phy_cfg, + }, { + .compatible = "qcom,shikra-qmp-usb3-dp-phy", + .data = &shikra_usb3dp_phy_cfg, }, { .compatible = "qcom,sm6115-qmp-usb3-phy", .data = &qcm2290_usb3phy_cfg, From 9bc73298911064f405e6252ab30ce0ed978ab800 Mon Sep 17 00:00:00 2001 From: Varadarajan Narayanan Date: Tue, 21 Jul 2026 13:05:26 +0530 Subject: [PATCH 17/76] dt-bindings: phy: qcom,ipq8074-qmp-pcie: Document the ipq5210 QMP PCIe PHY The ipq5210 has one dual lane and one single lane PCIe phy. The dual lane phy is similar to the dual lane phy present in ipq9574. Hence qcom,ipq5210-qmp-gen3x2-pcie-phy is documented with ipq9574's dual lane phy as fallback compatible. The single lane phy (qcom,ipq5210-qmp-gen3x1-pcie-phy) is documented as specific compatible as it uses a combination of its own initialization tables and some of the existing tables. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Varadarajan Narayanan Link: https://patch.msgid.link/20260721-pcie-phy-v5-1-8d403ef7e780@oss.qualcomm.com Signed-off-by: Vinod Koul --- .../devicetree/bindings/phy/qcom,ipq8074-qmp-pcie-phy.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/phy/qcom,ipq8074-qmp-pcie-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,ipq8074-qmp-pcie-phy.yaml index f60804687412..fc155ad5fa6d 100644 --- a/Documentation/devicetree/bindings/phy/qcom,ipq8074-qmp-pcie-phy.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,ipq8074-qmp-pcie-phy.yaml @@ -17,6 +17,7 @@ properties: compatible: oneOf: - enum: + - qcom,ipq5210-qmp-gen3x1-pcie-phy - qcom,ipq6018-qmp-pcie-phy - qcom,ipq8074-qmp-gen3-pcie-phy - qcom,ipq8074-qmp-pcie-phy @@ -28,6 +29,7 @@ properties: - const: qcom,ipq9574-qmp-gen3x1-pcie-phy - items: - enum: + - qcom,ipq5210-qmp-gen3x2-pcie-phy - qcom,ipq5424-qmp-gen3x2-pcie-phy - const: qcom,ipq9574-qmp-gen3x2-pcie-phy From 3c25d3d97428a88f451590aeca9e6da3c12e495d Mon Sep 17 00:00:00 2001 From: Varadarajan Narayanan Date: Tue, 21 Jul 2026 13:05:27 +0530 Subject: [PATCH 18/76] phy: qcom-qmp-pcie: Add support for ipq5210 PCIe phys Add support for a PCIe phys found on Qualcomm ipq5210 platform. Reviewed-by: Dmitry Baryshkov Signed-off-by: Varadarajan Narayanan Link: https://patch.msgid.link/20260721-pcie-phy-v5-2-8d403ef7e780@oss.qualcomm.com Signed-off-by: Vinod Koul --- drivers/phy/qualcomm/phy-qcom-qmp-pcie.c | 129 +++++++++++++++++++++++ 1 file changed, 129 insertions(+) diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c index f54f66429f37..f4b25a7b1604 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c @@ -636,6 +636,89 @@ static const struct qmp_phy_init_tbl ipq8074_pcie_gen3_pcs_misc_tbl[] = { QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_ENDPOINT_REFCLK_DRIVE, 0xc1), }; +static const struct qmp_phy_init_tbl ipq5210_gen3x1_pcie_ep_serdes_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_PLL_BG_TIMER, 0x02), + QMP_PHY_INIT_CFG(QSERDES_PLL_BIAS_EN_CLKBUFLR_EN, 0x18), + QMP_PHY_INIT_CFG(QSERDES_PLL_SYSCLK_BUF_ENABLE, 0x07), + QMP_PHY_INIT_CFG(QSERDES_PLL_PLL_IVCO, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_PLL_LOCK_CMP1_MODE0, 0xff), + QMP_PHY_INIT_CFG(QSERDES_PLL_LOCK_CMP2_MODE0, 0x04), + QMP_PHY_INIT_CFG(QSERDES_PLL_LOCK_CMP1_MODE1, 0xff), + QMP_PHY_INIT_CFG(QSERDES_PLL_LOCK_CMP2_MODE1, 0x09), + QMP_PHY_INIT_CFG(QSERDES_PLL_BG_TRIM, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_PLL_CP_CTRL_MODE0, 0x23), + QMP_PHY_INIT_CFG(QSERDES_PLL_CP_CTRL_MODE1, 0x23), + QMP_PHY_INIT_CFG(QSERDES_PLL_PLL_RCTRL_MODE0, 0x10), + QMP_PHY_INIT_CFG(QSERDES_PLL_PLL_RCTRL_MODE1, 0x10), + QMP_PHY_INIT_CFG(QSERDES_PLL_PLL_CCTRL_MODE0, 0x01), + QMP_PHY_INIT_CFG(QSERDES_PLL_PLL_CCTRL_MODE1, 0x01), + QMP_PHY_INIT_CFG(QSERDES_PLL_BIAS_EN_CTRL_BY_PSM, 0x01), + QMP_PHY_INIT_CFG(QSERDES_PLL_SYSCLK_EN_SEL, 0x00), + QMP_PHY_INIT_CFG(QSERDES_PLL_RESETSM_CNTRL, 0x20), + QMP_PHY_INIT_CFG(QSERDES_PLL_LOCK_CMP_EN, 0x42), + QMP_PHY_INIT_CFG(QSERDES_PLL_DEC_START_MODE0, 0x19), + QMP_PHY_INIT_CFG(QSERDES_PLL_DEC_START_MODE1, 0x14), + QMP_PHY_INIT_CFG(QSERDES_PLL_INTEGLOOP_GAIN0_MODE0, 0xfe), + QMP_PHY_INIT_CFG(QSERDES_PLL_INTEGLOOP_GAIN1_MODE0, 0x03), + QMP_PHY_INIT_CFG(QSERDES_PLL_INTEGLOOP_GAIN0_MODE1, 0xfe), + QMP_PHY_INIT_CFG(QSERDES_PLL_INTEGLOOP_GAIN1_MODE1, 0x03), + QMP_PHY_INIT_CFG(QSERDES_PLL_VCO_TUNE1_MODE0, 0x24), + QMP_PHY_INIT_CFG(QSERDES_PLL_VCO_TUNE2_MODE0, 0x02), + QMP_PHY_INIT_CFG(QSERDES_PLL_VCO_TUNE1_MODE1, 0xb4), + QMP_PHY_INIT_CFG(QSERDES_PLL_VCO_TUNE2_MODE1, 0x03), + QMP_PHY_INIT_CFG(QSERDES_PLL_HSCLK_SEL, 0x01), + QMP_PHY_INIT_CFG(QSERDES_PLL_CORE_CLK_EN, 0x00), + QMP_PHY_INIT_CFG(QSERDES_PLL_CMN_CONFIG, 0x06), + QMP_PHY_INIT_CFG(QSERDES_PLL_SVS_MODE_CLK_SEL, 0x05), + QMP_PHY_INIT_CFG(QSERDES_PLL_CORECLK_DIV_MODE1, 0x08), + QMP_PHY_INIT_CFG(QSERDES_PLL_SYS_CLK_CTRL, 0x07), + QMP_PHY_INIT_CFG(QSERDES_PLL_SYSCLK_EN_SEL, 0x10), +}; + +static const struct qmp_phy_init_tbl ipq5210_gen3x1_pcie_ep_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FO_GAIN, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_GAIN, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_PI_CONTROLS, 0x70), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL2, 0x61), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL3, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL4, 0x1e), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_LOW, 0xc0), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_HIGH, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x73), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_ENABLES, 0x03), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_DEGLITCH_CNTRL, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_LOW, 0xf0), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH2, 0x2f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH3, 0xd3), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH4, 0x40), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_LOW, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH2, 0xc8), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH3, 0x09), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH4, 0xb1), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_LOW, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH2, 0xc8), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH3, 0x09), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH4, 0xb1), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_EN_TIMER, 0x04), +}; + +static const struct qmp_phy_init_tbl ipq5210_gen3x1_pcie_ep_pcs_misc_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_POWER_STATE_CONFIG2, 0x04), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_POWER_STATE_CONFIG4, 0x07), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_L1P1_WAKEUP_DLY_TIME_AUXCLK_L, 0x01), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_L1P2_WAKEUP_DLY_TIME_AUXCLK_L, 0x01), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_EQ_CONFIG1, 0x11), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_PRESET_P10_PRE, 0x00), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_PRESET_P10_POST, 0x58), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_OSC_DTCT_CONFIG1, 0x02), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_OSC_DTCT_CONFIG4, 0xff), +}; + static const struct qmp_phy_init_tbl ipq9574_gen3x1_pcie_serdes_tbl[] = { QMP_PHY_INIT_CFG(QSERDES_PLL_BIAS_EN_CLKBUFLR_EN, 0x18), QMP_PHY_INIT_CFG(QSERDES_PLL_BIAS_EN_CTRL_BY_PSM, 0x01), @@ -4061,6 +4144,49 @@ static const struct qmp_phy_cfg ipq6018_pciephy_cfg = { .phy_status = PHYSTATUS, }; +static const struct qmp_phy_cfg ipq5210_gen3x1_pciephy_cfg = { + .lanes = 1, + + .offsets = &qmp_pcie_offsets_v4x1, + + .tbls = { + .serdes = ipq9574_gen3x1_pcie_serdes_tbl, + .serdes_num = ARRAY_SIZE(ipq9574_gen3x1_pcie_serdes_tbl), + .tx = ipq8074_pcie_gen3_tx_tbl, + .tx_num = ARRAY_SIZE(ipq8074_pcie_gen3_tx_tbl), + .rx = ipq9574_pcie_rx_tbl, + .rx_num = ARRAY_SIZE(ipq9574_pcie_rx_tbl), + .pcs = ipq9574_gen3x1_pcie_pcs_tbl, + .pcs_num = ARRAY_SIZE(ipq9574_gen3x1_pcie_pcs_tbl), + .pcs_misc = ipq9574_gen3x1_pcie_pcs_misc_tbl, + .pcs_misc_num = ARRAY_SIZE(ipq9574_gen3x1_pcie_pcs_misc_tbl), + }, + + .tbls_ep = &(const struct qmp_phy_cfg_tbls) { + .serdes = ipq5210_gen3x1_pcie_ep_serdes_tbl, + .serdes_num = ARRAY_SIZE(ipq5210_gen3x1_pcie_ep_serdes_tbl), + .tx = ipq6018_pcie_tx_tbl, + .tx_num = ARRAY_SIZE(ipq6018_pcie_tx_tbl), + .rx = ipq5210_gen3x1_pcie_ep_rx_tbl, + .rx_num = ARRAY_SIZE(ipq5210_gen3x1_pcie_ep_rx_tbl), + .pcs = ipq6018_pcie_pcs_tbl, + .pcs_num = ARRAY_SIZE(ipq6018_pcie_pcs_tbl), + .pcs_misc = ipq5210_gen3x1_pcie_ep_pcs_misc_tbl, + .pcs_misc_num = ARRAY_SIZE(ipq5210_gen3x1_pcie_ep_pcs_misc_tbl), + }, + + .reset_list = ipq8074_pciephy_reset_l, + .num_resets = ARRAY_SIZE(ipq8074_pciephy_reset_l), + .vreg_list = NULL, + .num_vregs = 0, + .regs = pciephy_v4_regs_layout, + + .pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL, + .phy_status = PHYSTATUS, + + .pipe_clock_rate = 250000000, +}; + static const struct qmp_phy_cfg ipq9574_gen3x1_pciephy_cfg = { .lanes = 1, @@ -5929,6 +6055,9 @@ static const struct of_device_id qmp_pcie_of_match_table[] = { }, { .compatible = "qcom,hawi-qmp-gen4x1-pcie-phy", .data = &hawi_qmp_gen4x1_pciephy_cfg, + }, { + .compatible = "qcom,ipq5210-qmp-gen3x1-pcie-phy", + .data = &ipq5210_gen3x1_pciephy_cfg, }, { .compatible = "qcom,ipq6018-qmp-pcie-phy", .data = &ipq6018_pciephy_cfg, From 02f447eabd48a2e843147ca72b0626b6e6428940 Mon Sep 17 00:00:00 2001 From: Radhey Shyam Pandey Date: Sat, 27 Jun 2026 21:52:33 +0530 Subject: [PATCH 19/76] MAINTAINERS: Add Radhey Shyam Pandey as ZynqMP PHY maintainer I am maintaining phy-zynqmp driver in xilinx tree and would like to maintain it in the mainline kernel as well. Hence adding myself as a maintainer. Signed-off-by: Radhey Shyam Pandey Acked-by: Laurent Pinchart Acked-by: Michal Simek Link: https://patch.msgid.link/20260627162233.2803425-1-radhey.shyam.pandey@amd.com Signed-off-by: Vinod Koul --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 15011f5752a9..5b544583e4d8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -29642,6 +29642,7 @@ F: drivers/edac/zynqmp_edac.c XILINX ZYNQMP PSGTR PHY DRIVER M: Tomi Valkeinen +M: Radhey Shyam Pandey L: linux-kernel@vger.kernel.org S: Supported T: git https://github.com/Xilinx/linux-xlnx.git From 7218d17e7613d5949cfc6215101493418fa501c0 Mon Sep 17 00:00:00 2001 From: Kathiravan Thirumoorthy Date: Fri, 10 Jul 2026 23:16:40 +0530 Subject: [PATCH 20/76] dt-bindings: phy: qcom,ipq8074-qmp-pcie: document IPQ9650 QMP PCIe PHYs Document the single-lane and dual-lane QMP PCIe PHYs found on the IPQ9650 SoC. Unlike the PHYs in the other supported IPQ SoCs, the IPQ9650 PHYs require the on-chip refgen supply to power up. Add the refgen-supply property and require it only for the IPQ9650 compatibles. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Kathiravan Thirumoorthy Link: https://patch.msgid.link/20260710-ipq9650_pcie_phy-v3-1-ef6018818d33@oss.qualcomm.com Signed-off-by: Vinod Koul --- .../phy/qcom,ipq8074-qmp-pcie-phy.yaml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/Documentation/devicetree/bindings/phy/qcom,ipq8074-qmp-pcie-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,ipq8074-qmp-pcie-phy.yaml index fc155ad5fa6d..d602588a9178 100644 --- a/Documentation/devicetree/bindings/phy/qcom,ipq8074-qmp-pcie-phy.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,ipq8074-qmp-pcie-phy.yaml @@ -23,6 +23,8 @@ properties: - qcom,ipq8074-qmp-pcie-phy - qcom,ipq9574-qmp-gen3x1-pcie-phy - qcom,ipq9574-qmp-gen3x2-pcie-phy + - qcom,ipq9650-qmp-gen3x1-pcie-phy + - qcom,ipq9650-qmp-gen3x2-pcie-phy - items: - enum: - qcom,ipq5424-qmp-gen3x1-pcie-phy @@ -63,6 +65,8 @@ properties: "#phy-cells": const: 0 + refgen-supply: true + required: - compatible - reg @@ -74,6 +78,21 @@ required: - clock-output-names - "#phy-cells" +allOf: + - if: + properties: + compatible: + contains: + enum: + - qcom,ipq9650-qmp-gen3x1-pcie-phy + - qcom,ipq9650-qmp-gen3x2-pcie-phy + then: + required: + - refgen-supply + else: + properties: + refgen-supply: false + additionalProperties: false examples: From ee6aa0f01d4e8eddf1c7fda49d65c7ef12444e78 Mon Sep 17 00:00:00 2001 From: Kathiravan Thirumoorthy Date: Fri, 10 Jul 2026 23:16:41 +0530 Subject: [PATCH 21/76] phy: qcom: qmp-pcie: Add IPQ9650 PCIe PHY support Add support for the IPQ9650 platform, which includes three Gen3 x2 PCIe controllers and two Gen3 x1 PCIe controllers. The PHY instances require the on-chip refgen supply. Add the IPQ9650 Gen3 x1 and x2 QMP PCIe PHY configurations along with the refgen regulator supply. Note that an on-chip LDO, driven by the SoC CX, supplies the PHY voltages without requiring software control. Note that IPQ9650 does not support CX power collapse or rail scaling. Reviewed-by: Dmitry Baryshkov Signed-off-by: Kathiravan Thirumoorthy Link: https://patch.msgid.link/20260710-ipq9650_pcie_phy-v3-2-ef6018818d33@oss.qualcomm.com Signed-off-by: Vinod Koul --- drivers/phy/qualcomm/phy-qcom-qmp-pcie.c | 220 +++++++++++++++++++++++ 1 file changed, 220 insertions(+) diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c index f4b25a7b1604..27034bb62d0d 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c @@ -956,6 +956,152 @@ static const struct qmp_phy_init_tbl ipq9574_gen3x2_pcie_pcs_misc_tbl[] = { QMP_PHY_INIT_CFG(QPHY_V5_PCS_PCIE_ENDPOINT_REFCLK_DRIVE, 0xc1), }; +static const struct qmp_phy_init_tbl ipq9650_pcie_serdes_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIAS_EN_CLKBUFLR_EN, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIAS_EN_CTRL_BY_PSM, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CLK_SELECT, 0x34), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_IVCO, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CMN_MODE, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CMN_CONFIG, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP_EN, 0x42), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_RESETSM_CNTRL, 0x20), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE_MAP, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE_TIMER1, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE_TIMER2, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE0, 0x68), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START3_MODE0, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START2_MODE0, 0xaa), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START1_MODE0, 0xab), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE0, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE0, 0xd4), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CP_CTRL_MODE0, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_CCTRL_MODE0, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_INTEGLOOP_GAIN1_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_INTEGLOOP_GAIN0_MODE0, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE2_MODE0, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE1_MODE0, 0x24), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SVS_MODE_CLK_SEL, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYS_CLK_CTRL, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYSCLK_BUF_ENABLE, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYSCLK_EN_SEL, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BG_TIMER, 0x0b), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_HSCLK_SEL, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CLK_ENABLE1, 0x90), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE1, 0x53), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START3_MODE1, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START2_MODE1, 0x55), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START1_MODE1, 0x55), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE1, 0x29), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE1, 0xaa), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CP_CTRL_MODE1, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_RCTRL_MODE1, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_CCTRL_MODE1, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_INTEGLOOP_GAIN1_MODE1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_INTEGLOOP_GAIN0_MODE1, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE2_MODE1, 0x03), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE1_MODE1, 0xb4), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SVS_MODE_CLK_SEL, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CORE_CLK_EN, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_EN_CENTER, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_PER2, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_PER1, 0x7d), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE2_MODE0, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE1_MODE0, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE2_MODE1, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE1_MODE1, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CORECLK_DIV_MODE1, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_HSCLK_SEL, 0x11), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE0, 0x18), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE0, 0xa2), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE1, 0x13), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE1, 0xb5), +}; + +static const struct qmp_phy_init_tbl ipq9650_pcie_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_OFFSET_TX, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_OFFSET_RX, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RCV_DETECT_LVL_2, 0x12), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_HIGHZ_DRVR_EN, 0x10), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_LANE_MODE_1, 0xb5), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_PI_QEC_CTRL, 0x00), +}; + +static const struct qmp_phy_init_tbl ipq9650_pcie_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_RX_AUX_DATA_TCOARSE_TFINE, 0x30), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_GM_CAL, 0x11), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FO_GAIN, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_GAIN, 0x03), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_CNTRL, 0x03), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_ENABLES, 0x1c), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_DEGLITCH_CNTRL, 0x1e), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL1, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0e), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4a), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL4, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_VGA_GAIN2_LSB, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_VGA_GAIN2_MSB, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_VGA_CAL_CNTRL1, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_VGA_CAL_CNTRL2, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_DCC_CTRL1, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_CTLE_POST_CAL_OFFSET, 0x38), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_2, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_TX_ADAPT_PRE_THRESH1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_TX_ADAPT_PRE_THRESH2, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_TX_ADAPT_POST_THRESH, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_TX_ADAPT_MAIN_THRESH, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_EN_TIMER, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_PI_CONTROLS, 0x70), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x17), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_LOW, 0xd4), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH, 0x54), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH2, 0xdb), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH3, 0x3b), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_10_HIGH4, 0x31), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_LOW, 0x24), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH, 0xe4), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH2, 0xec), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH3, 0x3b), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH4, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_LOW, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH2, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH3, 0x67), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH4, 0x3a), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_HIGH, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_LOW, 0xc0), +}; + +static const struct qmp_phy_init_tbl ipq9650_pcie_pcs_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V4_PCS_REFGEN_REQ_CONFIG1, 0x25), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_G12S1_TXDEEMPH_M3P5DB, 0x10), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_RATE_SLEW_CNTRL1, 0x03), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG2, 0x0f), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_P2U3_WAKEUP_DLY_TIME_AUXCLK_L, 0x01), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_RX_DCC_CAL_CONFIG, 0x00), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_RX_SIGDET_LVL, 0x77), +}; + +static const struct qmp_phy_init_tbl ipq9650_pcie_pcs_misc_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_POWER_STATE_CONFIG2, 0x0d), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_POWER_STATE_CONFIG4, 0x07), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_ENDPOINT_REFCLK_DRIVE, 0xc1), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_L1P1_WAKEUP_DLY_TIME_AUXCLK_L, 0x01), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_L1P2_WAKEUP_DLY_TIME_AUXCLK_L, 0x01), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_INT_AUX_CLK_CONFIG1, 0x00), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_OSC_DTCT_ACTIONS, 0x00), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_EQ_CONFIG1, 0x1c), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_EQ_CONFIG2, 0x0b), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_PRESET_P2_P3_POST, 0x34), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_PRESET_P6_P7_PRE, 0x33), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_PRESET_P6_P7_POST, 0x40), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_PRESET_P10_PRE, 0x00), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCIE_PRESET_P10_POST, 0x58), +}; + static const struct qmp_phy_init_tbl qcs615_pcie_serdes_tbl[] = { QMP_PHY_INIT_CFG(QSERDES_V2_COM_BIAS_EN_CLKBUFLR_EN, 0x18), QMP_PHY_INIT_CFG(QSERDES_V2_COM_CLK_ENABLE1, 0x10), @@ -3864,6 +4010,10 @@ static const char * const qmp_phy_vreg_l[] = { "vdda-phy", "vdda-pll", }; +static const char * const ipq9650_qmp_phy_vreg_l[] = { + "refgen", +}; + static const char * const sm8550_qmp_phy_vreg_l[] = { "vdda-phy", "vdda-pll", "vdda-qref", }; @@ -3907,6 +4057,14 @@ static const struct qmp_pcie_offsets qmp_pcie_offsets_v4x1 = { .rx = 0x0400, }; +static const struct qmp_pcie_offsets qmp_pcie_offsets_9650_v4x1 = { + .serdes = 0, + .pcs = 0x0600, + .pcs_misc = 0x0a00, + .tx = 0x0200, + .rx = 0x0400, +}; + static const struct qmp_pcie_offsets qmp_pcie_offsets_v4x2 = { .serdes = 0, .pcs = 0x0a00, @@ -4243,6 +4401,62 @@ static const struct qmp_phy_cfg ipq9574_gen3x2_pciephy_cfg = { .pipe_clock_rate = 250000000, }; +static const struct qmp_phy_cfg ipq9650_gen3x1_pciephy_cfg = { + .lanes = 1, + + .offsets = &qmp_pcie_offsets_9650_v4x1, + + .tbls = { + .serdes = ipq9650_pcie_serdes_tbl, + .serdes_num = ARRAY_SIZE(ipq9650_pcie_serdes_tbl), + .tx = ipq9650_pcie_tx_tbl, + .tx_num = ARRAY_SIZE(ipq9650_pcie_tx_tbl), + .rx = ipq9650_pcie_rx_tbl, + .rx_num = ARRAY_SIZE(ipq9650_pcie_rx_tbl), + .pcs = ipq9650_pcie_pcs_tbl, + .pcs_num = ARRAY_SIZE(ipq9650_pcie_pcs_tbl), + .pcs_misc = ipq9650_pcie_pcs_misc_tbl, + .pcs_misc_num = ARRAY_SIZE(ipq9650_pcie_pcs_misc_tbl), + }, + .reset_list = ipq8074_pciephy_reset_l, + .num_resets = ARRAY_SIZE(ipq8074_pciephy_reset_l), + .vreg_list = ipq9650_qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(ipq9650_qmp_phy_vreg_l), + .regs = pciephy_v4_regs_layout, + + .pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL, + .phy_status = PHYSTATUS, + .pipe_clock_rate = 250000000, +}; + +static const struct qmp_phy_cfg ipq9650_gen3x2_pciephy_cfg = { + .lanes = 2, + + .offsets = &qmp_pcie_offsets_v4x2, + + .tbls = { + .serdes = ipq9650_pcie_serdes_tbl, + .serdes_num = ARRAY_SIZE(ipq9650_pcie_serdes_tbl), + .tx = ipq9650_pcie_tx_tbl, + .tx_num = ARRAY_SIZE(ipq9650_pcie_tx_tbl), + .rx = ipq9650_pcie_rx_tbl, + .rx_num = ARRAY_SIZE(ipq9650_pcie_rx_tbl), + .pcs = ipq9650_pcie_pcs_tbl, + .pcs_num = ARRAY_SIZE(ipq9650_pcie_pcs_tbl), + .pcs_misc = ipq9650_pcie_pcs_misc_tbl, + .pcs_misc_num = ARRAY_SIZE(ipq9650_pcie_pcs_misc_tbl), + }, + .reset_list = ipq8074_pciephy_reset_l, + .num_resets = ARRAY_SIZE(ipq8074_pciephy_reset_l), + .vreg_list = ipq9650_qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(ipq9650_qmp_phy_vreg_l), + .regs = pciephy_v4_regs_layout, + + .pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL, + .phy_status = PHYSTATUS, + .pipe_clock_rate = 250000000, +}; + static const struct qmp_phy_cfg qcs615_pciephy_cfg = { .lanes = 1, @@ -6073,6 +6287,12 @@ static const struct of_device_id qmp_pcie_of_match_table[] = { }, { .compatible = "qcom,ipq9574-qmp-gen3x2-pcie-phy", .data = &ipq9574_gen3x2_pciephy_cfg, + }, { + .compatible = "qcom,ipq9650-qmp-gen3x1-pcie-phy", + .data = &ipq9650_gen3x1_pciephy_cfg, + }, { + .compatible = "qcom,ipq9650-qmp-gen3x2-pcie-phy", + .data = &ipq9650_gen3x2_pciephy_cfg, }, { .compatible = "qcom,kaanapali-qmp-gen3x2-pcie-phy", .data = &qmp_v8_gen3x2_pciephy_cfg, From 19d8eca1be11bc5782f9d292d88f96ebf0cdbe7f Mon Sep 17 00:00:00 2001 From: Ronak Raheja Date: Mon, 3 Aug 2026 14:44:38 +0530 Subject: [PATCH 22/76] dt-bindings: phy: qcom,sc8280xp-qmp-usb43dp-phy: Add Hawi QMP PHY Document the Hawi compatible string for the QMP combo PHY. Hawi uses a new QSERDES V10 register layout with a new COM AON module and hardware-specific PHY init sequences compared to previous targets, requiring a dedicated compatible string. Signed-off-by: Ronak Raheja Acked-by: Rob Herring (Arm) Signed-off-by: Mukesh Ojha Link: https://patch.msgid.link/20260803091441.3040830-2-mukesh.ojha@oss.qualcomm.com Signed-off-by: Vinod Koul --- .../devicetree/bindings/phy/qcom,sc8280xp-qmp-usb43dp-phy.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-usb43dp-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-usb43dp-phy.yaml index 4eff92343ce4..ea3f1699eee8 100644 --- a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-usb43dp-phy.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-usb43dp-phy.yaml @@ -26,6 +26,7 @@ properties: - const: qcom,sm8750-qmp-usb3-dp-phy - enum: - qcom,glymur-qmp-usb3-dp-phy + - qcom,hawi-qmp-usb3-dp-phy - qcom,sar2130p-qmp-usb3-dp-phy - qcom,sc7180-qmp-usb3-dp-phy - qcom,sc7280-qmp-usb3-dp-phy @@ -209,6 +210,7 @@ allOf: contains: enum: - qcom,glymur-qmp-usb3-dp-phy + - qcom,hawi-qmp-usb3-dp-phy - qcom,sar2130p-qmp-usb3-dp-phy - qcom,sc8280xp-qmp-usb43dp-phy - qcom,sm6350-qmp-usb3-dp-phy From 7abc2c575d5b5d67349caf73509f069e6bf7445a Mon Sep 17 00:00:00 2001 From: Ronak Raheja Date: Mon, 3 Aug 2026 14:44:39 +0530 Subject: [PATCH 23/76] dt-bindings: phy: qcom,m31-eusb2-phy: Document M31 eUSB2 PHY for Hawi Document the M31 eUSB2 PHY for Hawi which handles the USB2 path. Use fallback to indicate the compatibility of the M31 eUSB2 PHY on the Hawi with that on the SM8750. Signed-off-by: Ronak Raheja Reviewed-by: Krzysztof Kozlowski Signed-off-by: Mukesh Ojha Link: https://patch.msgid.link/20260803091441.3040830-3-mukesh.ojha@oss.qualcomm.com Signed-off-by: Vinod Koul --- Documentation/devicetree/bindings/phy/qcom,m31-eusb2-phy.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/phy/qcom,m31-eusb2-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,m31-eusb2-phy.yaml index cd6b84213a7c..c0e7e2963ce6 100644 --- a/Documentation/devicetree/bindings/phy/qcom,m31-eusb2-phy.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,m31-eusb2-phy.yaml @@ -19,6 +19,7 @@ properties: - items: - enum: - qcom,glymur-m31-eusb2-phy + - qcom,hawi-m31-eusb2-phy - qcom,kaanapali-m31-eusb2-phy - const: qcom,sm8750-m31-eusb2-phy - const: qcom,sm8750-m31-eusb2-phy From eca0e717aab1cc90eb0b7d8c3e8aa63ca89962a5 Mon Sep 17 00:00:00 2001 From: Ronak Raheja Date: Mon, 3 Aug 2026 14:44:41 +0530 Subject: [PATCH 24/76] phy: qualcomm: qmp-combo: Add support for Hawi SoC Add support for the USB3-DP combo PHY found on Hawi platform. The QMP PHY for Hawi uses QSERDES V10 register layouts. Add the required PHY sequences from the hardware programming guide and new V10 register header files. Also add a new v10 offset structure to incorporate the new COM AON register module. Signed-off-by: Ronak Raheja Reviewed-by: Konrad Dybcio Reviewed-by: Abel Vesa Signed-off-by: Mukesh Ojha Link: https://patch.msgid.link/20260803091441.3040830-5-mukesh.ojha@oss.qualcomm.com Signed-off-by: Vinod Koul --- .../phy/qualcomm/phy-qcom-qmp-com-aon-v10.h | 15 ++ drivers/phy/qualcomm/phy-qcom-qmp-combo.c | 231 +++++++++++++++++- .../phy/qualcomm/phy-qcom-qmp-dp-phy-v10.h | 15 ++ .../phy/qualcomm/phy-qcom-qmp-pcs-aon-v10.h | 13 + .../phy/qualcomm/phy-qcom-qmp-pcs-usb-v10.h | 19 ++ drivers/phy/qualcomm/phy-qcom-qmp-pcs-v10.h | 16 +- .../qualcomm/phy-qcom-qmp-qserdes-com-v10.h | 116 ++++++--- .../qualcomm/phy-qcom-qmp-qserdes-txrx-v10.h | 98 ++++++-- 8 files changed, 453 insertions(+), 70 deletions(-) create mode 100644 drivers/phy/qualcomm/phy-qcom-qmp-com-aon-v10.h create mode 100644 drivers/phy/qualcomm/phy-qcom-qmp-dp-phy-v10.h create mode 100644 drivers/phy/qualcomm/phy-qcom-qmp-pcs-aon-v10.h create mode 100644 drivers/phy/qualcomm/phy-qcom-qmp-pcs-usb-v10.h diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-com-aon-v10.h b/drivers/phy/qualcomm/phy-qcom-qmp-com-aon-v10.h new file mode 100644 index 000000000000..6542b586be89 --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-qmp-com-aon-v10.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2026, Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#ifndef QCOM_PHY_QMP_COM_AON_V10_H_ +#define QCOM_PHY_QMP_COM_AON_V10_H_ + +/* Only for QMP V10 PHY - COM AON registers */ + +#define QPHY_V10_COM_AON_USB3_AON_TOGGLE_ENABLE 0x00 +#define QPHY_V10_COM_AON_DP_AON_TOGGLE_ENABLE 0x04 +#define QPHY_V10_COM_AON_DUMMY_STATUS 0x08 + +#endif diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-combo.c b/drivers/phy/qualcomm/phy-qcom-qmp-combo.c index cdcfad2e86b1..ae80eee628c5 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-combo.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-combo.c @@ -32,6 +32,7 @@ #include "phy-qcom-qmp.h" #include "phy-qcom-qmp-pcs-aon-v6.h" #include "phy-qcom-qmp-pcs-aon-v8.h" +#include "phy-qcom-qmp-pcs-aon-v10.h" #include "phy-qcom-qmp-pcs-misc-v3.h" #include "phy-qcom-qmp-pcs-misc-v4.h" #include "phy-qcom-qmp-pcs-misc-v5.h" @@ -40,6 +41,7 @@ #include "phy-qcom-qmp-pcs-usb-v5.h" #include "phy-qcom-qmp-pcs-usb-v6.h" #include "phy-qcom-qmp-pcs-usb-v8.h" +#include "phy-qcom-qmp-pcs-usb-v10.h" #include "phy-qcom-qmp-dp-com-v3.h" @@ -49,9 +51,12 @@ #include "phy-qcom-qmp-dp-phy-v5.h" #include "phy-qcom-qmp-dp-phy-v6.h" #include "phy-qcom-qmp-dp-phy-v8.h" +#include "phy-qcom-qmp-dp-phy-v10.h" #include "phy-qcom-qmp-usb43-pcs-v8.h" +#include "phy-qcom-qmp-com-aon-v10.h" + /* QPHY_V3_DP_COM_RESET_OVRD_CTRL register bits */ /* DP PHY soft reset */ #define SW_DPPHY_RESET BIT(0) @@ -268,6 +273,36 @@ static const unsigned int qmp_v8_usb3phy_regs_layout[QPHY_LAYOUT_SIZE] = { [QPHY_TX_TRANSCEIVER_BIAS_EN] = QSERDES_V8_TX_TRANSCEIVER_BIAS_EN, }; +static const unsigned int qmp_v10_usb3phy_regs_layout[QPHY_LAYOUT_SIZE] = { + [QPHY_SW_RESET] = QPHY_V10_PCS_SW_RESET, + [QPHY_START_CTRL] = QPHY_V10_PCS_START_CONTROL, + [QPHY_PCS_STATUS] = QPHY_V10_PCS_PCS_STATUS1, + [QPHY_PCS_POWER_DOWN_CONTROL] = QPHY_V10_PCS_POWER_DOWN_CONTROL, + + /* In PCS_USB */ + [QPHY_PCS_AUTONOMOUS_MODE_CTRL] = QPHY_V10_PCS_USB3_AUTONOMOUS_MODE_CTRL, + [QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR] = QPHY_V10_PCS_USB3_LFPS_RXTERM_IRQ_CLEAR, + + [QPHY_PCS_CLAMP_ENABLE] = QPHY_V10_PCS_AON_CLAMP_ENABLE, + + [QPHY_AON_TOGGLE_ENABLE] = QPHY_V10_COM_AON_USB3_AON_TOGGLE_ENABLE, + [QPHY_DP_AON_TOGGLE_ENABLE] = QPHY_V10_COM_AON_DP_AON_TOGGLE_ENABLE, + + [QPHY_COM_RESETSM_CNTRL] = QSERDES_V10_COM_RESETSM_CNTRL, + [QPHY_COM_C_READY_STATUS] = QSERDES_V10_COM_C_READY_STATUS, + [QPHY_COM_CMN_STATUS] = QSERDES_V10_COM_CMN_STATUS, + [QPHY_COM_BIAS_EN_CLKBUFLR_EN] = QSERDES_V10_COM_BIAS_EN_CLKBUFLR_EN, + + [QPHY_DP_PHY_STATUS] = QSERDES_V10_DP_PHY_STATUS, + [QPHY_DP_PHY_VCO_DIV] = QSERDES_V10_DP_PHY_VCO_DIV, + + [QPHY_TX_TX_POL_INV] = QSERDES_V10_TX_TX_POL_INV, + [QPHY_TX_TX_DRV_LVL] = QSERDES_V10_TX_TX_DRV_LVL, + [QPHY_TX_TX_EMP_POST1_LVL] = QSERDES_V10_TX_TX_EMP_POST1_LVL, + [QPHY_TX_HIGHZ_DRVR_EN] = QSERDES_V10_TX_HIGHZ_DRVR_EN, + [QPHY_TX_TRANSCEIVER_BIAS_EN] = QSERDES_V10_TX_TRANSCEIVER_BIAS_EN, +}; + static const unsigned int qmp_v8_n3_usb43dpphy_regs_layout[QPHY_LAYOUT_SIZE] = { [QPHY_SW_RESET] = QPHY_V8_USB43_PCS_SW_RESET, [QPHY_START_CTRL] = QPHY_V8_USB43_PCS_START_CONTROL, @@ -2058,6 +2093,141 @@ static const struct qmp_phy_init_tbl x1e80100_usb43dp_pcs_usb_tbl[] = { QMP_PHY_INIT_CFG(QPHY_V6_PCS_USB3_RXEQTRAINING_DFE_TIME_S2, 0x07), }; +static const struct qmp_phy_init_tbl hawi_usb3_serdes_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V10_COM_SSC_STEP_SIZE1_MODE1, 0xc0), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_SSC_STEP_SIZE2_MODE1, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_CP_CTRL_MODE1, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_PLL_RCTRL_MODE1, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_PLL_CCTRL_MODE1, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_CORECLK_DIV_MODE1, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_LOCK_CMP1_MODE1, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_LOCK_CMP2_MODE1, 0x41), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_DEC_START_MODE1, 0x41), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_DEC_START_MSB_MODE1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_DIV_FRAC_START1_MODE1, 0x55), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_DIV_FRAC_START2_MODE1, 0x75), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_DIV_FRAC_START3_MODE1, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_HSCLK_SEL_1, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_VCO_TUNE1_MODE1, 0x25), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_VCO_TUNE2_MODE1, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_BIN_VCOCAL_CMP_CODE1_MODE1, 0x5c), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_BIN_VCOCAL_CMP_CODE2_MODE1, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_BIN_VCOCAL_CMP_CODE1_MODE0, 0x5c), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_BIN_VCOCAL_CMP_CODE2_MODE0, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_SSC_STEP_SIZE1_MODE0, 0xc0), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_SSC_STEP_SIZE2_MODE0, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_CP_CTRL_MODE0, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_PLL_CCTRL_MODE0, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_PLL_CORE_CLK_DIV_MODE0, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_LOCK_CMP1_MODE0, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_LOCK_CMP2_MODE0, 0x1a), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_DEC_START_MODE0, 0x41), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_DEC_START_MSB_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_DIV_FRAC_START1_MODE0, 0x55), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_DIV_FRAC_START2_MODE0, 0x75), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_DIV_FRAC_START3_MODE0, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_VCO_TUNE1_MODE0, 0x25), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_VCO_TUNE2_MODE0, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_BG_TIMER, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_SSC_EN_CENTER, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_SSC_PER1, 0x62), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_SSC_PER2, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_SYSCLK_BUF_ENABLE, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_SYSCLK_EN_SEL, 0x1a), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_LOCK_CMP_CFG, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_VCO_TUNE_MAP, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_CORE_CLK_EN, 0x20), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_CMN_CONFIG_1, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_AUTO_GAIN_ADJ_CTRL_1, 0xb6), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_AUTO_GAIN_ADJ_CTRL_2, 0x4a), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_AUTO_GAIN_ADJ_CTRL_3, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V10_COM_ADDITIONAL_MISC, 0x0c), +}; + +static const struct qmp_phy_init_tbl hawi_usb3_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V10_TX_RES_CODE_LANE_TX, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V10_TX_RES_CODE_LANE_RX, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V10_TX_RES_CODE_LANE_OFFSET_TX, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V10_TX_RES_CODE_LANE_OFFSET_RX, 0x09), + QMP_PHY_INIT_CFG(QSERDES_V10_TX_LANE_MODE_1, 0xe5), + QMP_PHY_INIT_CFG(QSERDES_V10_TX_LANE_MODE_2, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V10_TX_LANE_MODE_3, 0x11), + QMP_PHY_INIT_CFG(QSERDES_V10_TX_LANE_MODE_4, 0x31), + QMP_PHY_INIT_CFG(QSERDES_V10_TX_LANE_MODE_5, 0x5d), + QMP_PHY_INIT_CFG(QSERDES_V10_TX_RCV_DETECT_LVL_2, 0x12), + QMP_PHY_INIT_CFG_LANE(QSERDES_V10_TX_PI_QEC_CTRL, 0x21, 1), + QMP_PHY_INIT_CFG_LANE(QSERDES_V10_TX_PI_QEC_CTRL, 0x05, 2), +}; + +static const struct qmp_phy_init_tbl hawi_usb3_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V10_RX_UCDR_FO_GAIN, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V10_RX_UCDR_SO_GAIN, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V10_RX_UCDR_FASTLOCK_FO_GAIN, 0x2f), + QMP_PHY_INIT_CFG(QSERDES_V10_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V10_RX_UCDR_FASTLOCK_COUNT_LOW, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V10_RX_UCDR_FASTLOCK_COUNT_HIGH, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V10_RX_UCDR_PI_CONTROLS, 0x99), + QMP_PHY_INIT_CFG(QSERDES_V10_RX_UCDR_SB2_THRESH1, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V10_RX_UCDR_SB2_THRESH2, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V10_RX_UCDR_SB2_GAIN1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V10_RX_UCDR_SB2_GAIN2, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V10_RX_AUX_DATA_TCOARSE_TFINE, 0x20), + QMP_PHY_INIT_CFG(QSERDES_V10_RX_VGA_CAL_CNTRL1, 0x54), + QMP_PHY_INIT_CFG(QSERDES_V10_RX_VGA_CAL_CNTRL2, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V10_RX_GM_CAL, 0x13), + QMP_PHY_INIT_CFG(QSERDES_V10_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0e), + QMP_PHY_INIT_CFG(QSERDES_V10_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4a), + QMP_PHY_INIT_CFG(QSERDES_V10_RX_RX_EQU_ADAPTOR_CNTRL4, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V10_RX_RX_IDAC_TSETTLE_LOW, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V10_RX_RX_IDAC_TSETTLE_HIGH, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V10_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x27), + QMP_PHY_INIT_CFG(QSERDES_V10_RX_SIGDET_ENABLES, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V10_RX_SIGDET_CNTRL, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V10_RX_SIGDET_DEGLITCH_CNTRL, 0x0e), + QMP_PHY_INIT_CFG(QSERDES_V10_RX_RX_MODE_00_LOW, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V10_RX_RX_MODE_00_HIGH, 0xbf), + QMP_PHY_INIT_CFG(QSERDES_V10_RX_RX_MODE_00_HIGH2, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V10_RX_RX_MODE_00_HIGH3, 0xdf), + QMP_PHY_INIT_CFG(QSERDES_V10_RX_RX_MODE_00_HIGH4, 0xed), + QMP_PHY_INIT_CFG(QSERDES_V10_RX_RX_MODE_01_LOW, 0x19), + QMP_PHY_INIT_CFG(QSERDES_V10_RX_RX_MODE_01_HIGH, 0x09), + QMP_PHY_INIT_CFG(QSERDES_V10_RX_RX_MODE_01_HIGH2, 0x91), + QMP_PHY_INIT_CFG(QSERDES_V10_RX_RX_MODE_01_HIGH3, 0xb7), + QMP_PHY_INIT_CFG(QSERDES_V10_RX_RX_MODE_01_HIGH4, 0xaa), + QMP_PHY_INIT_CFG(QSERDES_V10_RX_DFE_EN_TIMER, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V10_RX_DFE_CTLE_POST_CAL_OFFSET, 0x38), + QMP_PHY_INIT_CFG(QSERDES_V10_RX_DCC_CTRL1, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V10_RX_VTH_CODE, 0x10), + QMP_PHY_INIT_CFG(QSERDES_V10_RX_SIGDET_CAL_CTRL1, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V10_RX_SIGDET_CAL_TRIM, 0x08), +}; + +static const struct qmp_phy_init_tbl hawi_usb3_pcs_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V10_PCS_LOCK_DETECT_CONFIG1, 0xc4), + QMP_PHY_INIT_CFG(QPHY_V10_PCS_LOCK_DETECT_CONFIG2, 0x89), + QMP_PHY_INIT_CFG(QPHY_V10_PCS_LOCK_DETECT_CONFIG3, 0x20), + QMP_PHY_INIT_CFG(QPHY_V10_PCS_LOCK_DETECT_CONFIG6, 0x13), + QMP_PHY_INIT_CFG(QPHY_V10_PCS_REFGEN_REQ_CONFIG1, 0x21), + QMP_PHY_INIT_CFG(QPHY_V10_PCS_RX_SIGDET_LVL, 0x55), + QMP_PHY_INIT_CFG(QPHY_V10_PCS_RCVR_DTCT_DLY_P1U2_L, 0xe7), + QMP_PHY_INIT_CFG(QPHY_V10_PCS_RCVR_DTCT_DLY_P1U2_H, 0x03), + QMP_PHY_INIT_CFG(QPHY_V10_PCS_RX_CONFIG, 0x0a), + QMP_PHY_INIT_CFG(QPHY_V10_PCS_ALIGN_DETECT_CONFIG1, 0x88), + QMP_PHY_INIT_CFG(QPHY_V10_PCS_ALIGN_DETECT_CONFIG2, 0x13), + QMP_PHY_INIT_CFG(QPHY_V10_PCS_PCS_TX_RX_CONFIG, 0x04), + QMP_PHY_INIT_CFG(QPHY_V10_PCS_PCS_TX_RX_CONFIG2, 0x01), + QMP_PHY_INIT_CFG(QPHY_V10_PCS_EQ_CONFIG1, 0x4b), + QMP_PHY_INIT_CFG(QPHY_V10_PCS_EQ_CONFIG5, 0x10), +}; + +static const struct qmp_phy_init_tbl hawi_usb3_pcs_usb_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V10_PCS_USB3_LFPS_DET_HIGH_COUNT_VAL, 0xf8), + QMP_PHY_INIT_CFG(QPHY_V10_PCS_USB3_RXEQTRAINING_DFE_TIME_S2, 0x07), + QMP_PHY_INIT_CFG(QPHY_V10_PCS_USB3_RCVR_DTCT_DLY_U3_L, 0x40), + QMP_PHY_INIT_CFG(QPHY_V10_PCS_USB3_RCVR_DTCT_DLY_U3_H, 0x00), +}; + /* list of regulators */ static struct regulator_bulk_data qmp_phy_vreg_l[] = { { .supply = "vdda-phy", .init_load_uA = 21800, }, @@ -2198,6 +2368,7 @@ struct qmp_combo_offsets { u16 dp_txa; u16 dp_txb; u16 dp_dp_phy; + u16 aon_toggle; }; struct qmp_phy_cfg { @@ -2270,6 +2441,7 @@ struct qmp_combo { const struct qmp_phy_cfg *cfg; void __iomem *com; + void __iomem *aon_toggle; void __iomem *serdes; void __iomem *tx; @@ -2416,6 +2588,24 @@ static const struct qmp_combo_offsets qmp_combo_offsets_v8 = { .dp_dp_phy = 0x3c00, }; +static const struct qmp_combo_offsets qmp_combo_offsets_v10 = { + .com = 0x0000, + .aon_toggle = 0x0400, + .txa = 0x1400, + .rxa = 0x1600, + .txb = 0x1800, + .rxb = 0x1a00, + .usb3_serdes = 0x1000, + .usb3_pcs_misc = 0x1c00, + .usb3_pcs = 0x1e00, + .usb3_pcs_aon = 0x2000, + .usb3_pcs_usb = 0x2100, + .dp_serdes = 0x3000, + .dp_txa = 0x3400, + .dp_txb = 0x3800, + .dp_dp_phy = 0x3c00, +}; + static const struct qmp_combo_offsets qmp_combo_usb43dp_offsets_v8 = { .com = 0x0000, .usb3_pcs_aon = 0x0100, @@ -2705,6 +2895,27 @@ static const struct qmp_phy_cfg x1e80100_usb3dpphy_cfg = { .regs = qmp_v6_n4_usb3phy_regs_layout, }; +static const struct qmp_phy_cfg hawi_usb3dpphy_cfg = { + .offsets = &qmp_combo_offsets_v10, + + .serdes_tbl = hawi_usb3_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(hawi_usb3_serdes_tbl), + .tx_tbl = hawi_usb3_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(hawi_usb3_tx_tbl), + .rx_tbl = hawi_usb3_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(hawi_usb3_rx_tbl), + .pcs_tbl = hawi_usb3_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(hawi_usb3_pcs_tbl), + .pcs_usb_tbl = hawi_usb3_pcs_usb_tbl, + .pcs_usb_tbl_num = ARRAY_SIZE(hawi_usb3_pcs_usb_tbl), + + .regs = qmp_v10_usb3phy_regs_layout, + .reset_list = msm8996_usb3phy_reset_l, + .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), +}; + static const struct qmp_phy_cfg sm6350_usb3dpphy_cfg = { .offsets = &qmp_combo_offsets_v3, @@ -3662,13 +3873,18 @@ static int qmp_combo_com_init(struct qmp_combo *qmp, bool force) { const struct qmp_phy_cfg *cfg = qmp->cfg; void __iomem *com = qmp->com; - void __iomem *pcs_aon = qmp->pcs_aon; + void __iomem *aon_toggle; int ret; u32 val; if (!force && qmp->init_count++) return 0; + if (qmp->aon_toggle) + aon_toggle = qmp->aon_toggle; + else + aon_toggle = qmp->pcs_aon; + ret = regulator_bulk_enable(cfg->num_vregs, qmp->vregs); if (ret) { dev_err(qmp->dev, "failed to enable regulators, err=%d\n", ret); @@ -3706,9 +3922,9 @@ static int qmp_combo_com_init(struct qmp_combo *qmp, bool force) SW_USB3PHY_RESET_MUX | SW_USB3PHY_RESET); /* override hardware control for reset of qmp phy */ - if (pcs_aon && cfg->regs[QPHY_AON_TOGGLE_ENABLE]) { - qphy_clrbits(pcs_aon, cfg->regs[QPHY_AON_TOGGLE_ENABLE], 0x1); - qphy_clrbits(pcs_aon, cfg->regs[QPHY_DP_AON_TOGGLE_ENABLE], 0x1); + if (aon_toggle && cfg->regs[QPHY_AON_TOGGLE_ENABLE]) { + qphy_clrbits(aon_toggle, cfg->regs[QPHY_AON_TOGGLE_ENABLE], 0x1); + qphy_clrbits(aon_toggle, cfg->regs[QPHY_DP_AON_TOGGLE_ENABLE], 0x1); } /* Use software based port select and switch on typec orientation */ @@ -4735,6 +4951,9 @@ static int qmp_combo_parse_dt(struct qmp_combo *qmp) } qmp->dp_dp_phy = base + offs->dp_dp_phy; + if (offs->aon_toggle) + qmp->aon_toggle = base + offs->aon_toggle; + ret = qmp_combo_clk_init(qmp); if (ret) return ret; @@ -4988,6 +5207,10 @@ static const struct of_device_id qmp_combo_of_match_table[] = { .compatible = "qcom,glymur-qmp-usb3-dp-phy", .data = &glymur_usb3dpphy_cfg, }, + { + .compatible = "qcom,hawi-qmp-usb3-dp-phy", + .data = &hawi_usb3dpphy_cfg, + }, { .compatible = "qcom,sar2130p-qmp-usb3-dp-phy", .data = &sar2130p_usb3dpphy_cfg, diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-dp-phy-v10.h b/drivers/phy/qualcomm/phy-qcom-qmp-dp-phy-v10.h new file mode 100644 index 000000000000..6f3ea7d13556 --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-qmp-dp-phy-v10.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2026, Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#ifndef QCOM_PHY_QMP_DP_PHY_V10_H_ +#define QCOM_PHY_QMP_DP_PHY_V10_H_ + +/* Only for QMP V10 PHY - DP PHY registers */ + +#define QSERDES_V10_DP_PHY_VCO_DIV 0x070 +#define QSERDES_V10_DP_PHY_AUX_INTERRUPT_STATUS 0x0e0 +#define QSERDES_V10_DP_PHY_STATUS 0x0e4 + +#endif diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcs-aon-v10.h b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-aon-v10.h new file mode 100644 index 000000000000..b858381bc238 --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-aon-v10.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2026, Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#ifndef QCOM_PHY_QMP_PCS_AON_V10_H_ +#define QCOM_PHY_QMP_PCS_AON_V10_H_ + +/* Only for QMP V10 PHY - PCS AON registers */ + +#define QPHY_V10_PCS_AON_CLAMP_ENABLE 0x00 + +#endif diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcs-usb-v10.h b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-usb-v10.h new file mode 100644 index 000000000000..0cc25e6acf58 --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-usb-v10.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2026, Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#ifndef QCOM_PHY_QMP_PCS_USB_V10_H_ +#define QCOM_PHY_QMP_PCS_USB_V10_H_ + +/* Only for QMP V10 PHY - USB PCS registers */ + +#define QPHY_V10_PCS_USB3_POWER_STATE_CONFIG1 0x00 +#define QPHY_V10_PCS_USB3_AUTONOMOUS_MODE_CTRL 0x08 +#define QPHY_V10_PCS_USB3_LFPS_RXTERM_IRQ_CLEAR 0x14 +#define QPHY_V10_PCS_USB3_LFPS_DET_HIGH_COUNT_VAL 0x18 +#define QPHY_V10_PCS_USB3_RXEQTRAINING_DFE_TIME_S2 0x3c +#define QPHY_V10_PCS_USB3_RCVR_DTCT_DLY_U3_L 0x40 +#define QPHY_V10_PCS_USB3_RCVR_DTCT_DLY_U3_H 0x44 + +#endif diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcs-v10.h b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-v10.h index 165ce8a28f61..e881fb14650c 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-pcs-v10.h +++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-v10.h @@ -6,17 +6,31 @@ #ifndef QCOM_PHY_QMP_PCS_V10_H_ #define QCOM_PHY_QMP_PCS_V10_H_ -/* Only for QMP V10 PHY - PCIe PCS registers */ +/* Only for QMP V10 PHY - PCS registers */ + #define QPHY_V10_PCS_SW_RESET 0x000 #define QPHY_V10_PCS_PCS_STATUS1 0x014 #define QPHY_V10_PCS_POWER_DOWN_CONTROL 0x040 #define QPHY_V10_PCS_START_CONTROL 0x044 +#define QPHY_V10_PCS_POWER_STATE_CONFIG1 0x090 +#define QPHY_V10_PCS_LOCK_DETECT_CONFIG1 0x0c4 +#define QPHY_V10_PCS_LOCK_DETECT_CONFIG2 0x0c8 +#define QPHY_V10_PCS_LOCK_DETECT_CONFIG3 0x0cc +#define QPHY_V10_PCS_LOCK_DETECT_CONFIG6 0x0d8 #define QPHY_V10_PCS_REFGEN_REQ_CONFIG1 0x0dc #define QPHY_V10_PCS_G12S1_TXDEEMPH_M6DB 0x168 #define QPHY_V10_PCS_G3S2_PRE_GAIN 0x170 #define QPHY_V10_PCS_RX_SIGDET_LVL 0x188 +#define QPHY_V10_PCS_RCVR_DTCT_DLY_P1U2_L 0x190 +#define QPHY_V10_PCS_RCVR_DTCT_DLY_P1U2_H 0x194 #define QPHY_V10_PCS_RATE_SLEW_CNTRL1 0x198 +#define QPHY_V10_PCS_RX_CONFIG 0x1b0 +#define QPHY_V10_PCS_ALIGN_DETECT_CONFIG1 0x1c0 +#define QPHY_V10_PCS_ALIGN_DETECT_CONFIG2 0x1c4 #define QPHY_V10_PCS_PCS_TX_RX_CONFIG 0x1d0 +#define QPHY_V10_PCS_PCS_TX_RX_CONFIG2 0x1d4 +#define QPHY_V10_PCS_EQ_CONFIG1 0x1e0 #define QPHY_V10_PCS_EQ_CONFIG2 0x1e4 +#define QPHY_V10_PCS_EQ_CONFIG5 0x1f0 #endif diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com-v10.h b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com-v10.h index 09199e7b4aac..bbd1f67ea69a 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com-v10.h +++ b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com-v10.h @@ -7,43 +7,83 @@ #define QCOM_PHY_QMP_QSERDES_COM_V10_H_ /* Only for QMP V10 PHY - QSERDES COM registers */ -#define QSERDES_V10_COM_SSC_STEP_SIZE1_MODE1 0x00 -#define QSERDES_V10_COM_SSC_STEP_SIZE2_MODE1 0x04 -#define QSERDES_V10_COM_CP_CTRL_MODE1 0x10 -#define QSERDES_V10_COM_PLL_RCTRL_MODE1 0x14 -#define QSERDES_V10_COM_PLL_CCTRL_MODE1 0x18 -#define QSERDES_V10_COM_CORECLK_DIV_MODE1 0x1c -#define QSERDES_V10_COM_LOCK_CMP1_MODE1 0x20 -#define QSERDES_V10_COM_LOCK_CMP2_MODE1 0x24 -#define QSERDES_V10_COM_DEC_START_MODE1 0x28 -#define QSERDES_V10_COM_DIV_FRAC_START1_MODE1 0x30 -#define QSERDES_V10_COM_DIV_FRAC_START2_MODE1 0x34 -#define QSERDES_V10_COM_DIV_FRAC_START3_MODE1 0x38 -#define QSERDES_V10_COM_HSCLK_SEL_1 0x3c -#define QSERDES_V10_COM_SSC_STEP_SIZE1_MODE0 0x60 -#define QSERDES_V10_COM_SSC_STEP_SIZE2_MODE0 0x64 -#define QSERDES_V10_COM_CP_CTRL_MODE0 0x70 -#define QSERDES_V10_COM_PLL_RCTRL_MODE0 0x74 -#define QSERDES_V10_COM_PLL_CCTRL_MODE0 0x78 -#define QSERDES_V10_COM_LOCK_CMP1_MODE0 0x80 -#define QSERDES_V10_COM_LOCK_CMP2_MODE0 0x84 -#define QSERDES_V10_COM_DEC_START_MODE0 0x88 -#define QSERDES_V10_COM_DIV_FRAC_START1_MODE0 0x90 -#define QSERDES_V10_COM_DIV_FRAC_START2_MODE0 0x94 -#define QSERDES_V10_COM_DIV_FRAC_START3_MODE0 0x98 -#define QSERDES_V10_COM_BG_TIMER 0xbc -#define QSERDES_V10_COM_SSC_EN_CENTER 0xc0 -#define QSERDES_V10_COM_SSC_PER1 0xcc -#define QSERDES_V10_COM_SSC_PER2 0xd0 -#define QSERDES_V10_COM_CLK_ENABLE1 0xe0 -#define QSERDES_V10_COM_SYS_CLK_CTRL 0xe4 -#define QSERDES_V10_COM_PLL_IVCO 0xf4 -#define QSERDES_V10_COM_SYSCLK_EN_SEL 0x110 -#define QSERDES_V10_COM_LOCK_CMP_EN 0x120 -#define QSERDES_V10_COM_VCO_TUNE_MAP 0x140 -#define QSERDES_V10_COM_CLK_SELECT 0x164 -#define QSERDES_V10_COM_CORE_CLK_EN 0x170 -#define QSERDES_V10_COM_CMN_CONFIG_1 0x174 -#define QSERDES_V10_COM_ADDITIONAL_MISC_3 0x1bc + +#define QSERDES_V10_COM_SSC_STEP_SIZE1_MODE1 0x00 +#define QSERDES_V10_COM_SSC_STEP_SIZE2_MODE1 0x04 +#define QSERDES_V10_COM_CP_CTRL_MODE1 0x10 +#define QSERDES_V10_COM_PLL_RCTRL_MODE1 0x14 +#define QSERDES_V10_COM_PLL_CCTRL_MODE1 0x18 +#define QSERDES_V10_COM_CORECLK_DIV_MODE1 0x1c +#define QSERDES_V10_COM_LOCK_CMP1_MODE1 0x20 +#define QSERDES_V10_COM_LOCK_CMP2_MODE1 0x24 +#define QSERDES_V10_COM_DEC_START_MODE1 0x28 +#define QSERDES_V10_COM_DEC_START_MSB_MODE1 0x2c +#define QSERDES_V10_COM_DIV_FRAC_START1_MODE1 0x30 +#define QSERDES_V10_COM_DIV_FRAC_START2_MODE1 0x34 +#define QSERDES_V10_COM_DIV_FRAC_START3_MODE1 0x38 +#define QSERDES_V10_COM_HSCLK_SEL_1 0x3c +#define QSERDES_V10_COM_INTEGLOOP_GAIN0_MODE1 0x40 +#define QSERDES_V10_COM_INTEGLOOP_GAIN1_MODE1 0x44 +#define QSERDES_V10_COM_VCO_TUNE1_MODE1 0x48 +#define QSERDES_V10_COM_VCO_TUNE2_MODE1 0x4c +#define QSERDES_V10_COM_BIN_VCOCAL_CMP_CODE1_MODE1 0x50 +#define QSERDES_V10_COM_BIN_VCOCAL_CMP_CODE2_MODE1 0x54 +#define QSERDES_V10_COM_BIN_VCOCAL_CMP_CODE1_MODE0 0x58 +#define QSERDES_V10_COM_BIN_VCOCAL_CMP_CODE2_MODE0 0x5c +#define QSERDES_V10_COM_SSC_STEP_SIZE1_MODE0 0x60 +#define QSERDES_V10_COM_SSC_STEP_SIZE2_MODE0 0x64 +#define QSERDES_V10_COM_CP_CTRL_MODE0 0x70 +#define QSERDES_V10_COM_PLL_RCTRL_MODE0 0x74 +#define QSERDES_V10_COM_PLL_CCTRL_MODE0 0x78 +#define QSERDES_V10_COM_PLL_CORE_CLK_DIV_MODE0 0x7c +#define QSERDES_V10_COM_LOCK_CMP1_MODE0 0x80 +#define QSERDES_V10_COM_LOCK_CMP2_MODE0 0x84 +#define QSERDES_V10_COM_DEC_START_MODE0 0x88 +#define QSERDES_V10_COM_DEC_START_MSB_MODE0 0x8c +#define QSERDES_V10_COM_DIV_FRAC_START1_MODE0 0x90 +#define QSERDES_V10_COM_DIV_FRAC_START2_MODE0 0x94 +#define QSERDES_V10_COM_DIV_FRAC_START3_MODE0 0x98 +#define QSERDES_V10_COM_HSCLK_HS_SWITCH_SEL_1 0x9c +#define QSERDES_V10_COM_INTEGLOOP_GAIN0_MODE0 0xa0 +#define QSERDES_V10_COM_INTEGLOOP_GAIN1_MODE0 0xa4 +#define QSERDES_V10_COM_VCO_TUNE1_MODE0 0xa8 +#define QSERDES_V10_COM_VCO_TUNE2_MODE0 0xac +#define QSERDES_V10_COM_BG_TIMER 0xbc +#define QSERDES_V10_COM_SSC_EN_CENTER 0xc0 +#define QSERDES_V10_COM_SSC_ADJ_PER1 0xc4 +#define QSERDES_V10_COM_SSC_PER1 0xcc +#define QSERDES_V10_COM_SSC_PER2 0xd0 +#define QSERDES_V10_COM_POST_DIV_MUX 0xd8 +#define QSERDES_V10_COM_BIAS_EN_CLKBUFLR_EN 0xdc +#define QSERDES_V10_COM_CLK_ENABLE1 0xe0 +#define QSERDES_V10_COM_SYS_CLK_CTRL 0xe4 +#define QSERDES_V10_COM_SYSCLK_BUF_ENABLE 0xe8 +#define QSERDES_V10_COM_PLL_IVCO 0xf4 +#define QSERDES_V10_COM_PLL_IVCO_MODE1 0xf8 +#define QSERDES_V10_COM_CMN_IETRIM 0xfc +#define QSERDES_V10_COM_CMN_IPTRIM 0x100 +#define QSERDES_V10_COM_SYSCLK_EN_SEL 0x110 +#define QSERDES_V10_COM_RESETSM_CNTRL 0x118 +#define QSERDES_V10_COM_LOCK_CMP_EN 0x120 +#define QSERDES_V10_COM_LOCK_CMP_CFG 0x124 +#define QSERDES_V10_COM_VCO_TUNE_CTRL 0x13c +#define QSERDES_V10_COM_VCO_TUNE_MAP 0x140 +#define QSERDES_V10_COM_VCO_TUNE_INITVAL2 0x148 +#define QSERDES_V10_COM_VCO_TUNE_MAXVAL2 0x158 +#define QSERDES_V10_COM_CLK_SELECT 0x164 +#define QSERDES_V10_COM_CORE_CLK_EN 0x170 +#define QSERDES_V10_COM_CMN_CONFIG_1 0x174 +#define QSERDES_V10_COM_SVS_MODE_CLK_SEL 0x17c +#define QSERDES_V10_COM_CMN_MISC_1 0x184 +#define QSERDES_V10_COM_CMN_MODE 0x188 +#define QSERDES_V10_COM_PLL_VCO_DC_LEVEL_CTRL 0x198 +#define QSERDES_V10_COM_AUTO_GAIN_ADJ_CTRL_1 0x1a4 +#define QSERDES_V10_COM_AUTO_GAIN_ADJ_CTRL_2 0x1a8 +#define QSERDES_V10_COM_AUTO_GAIN_ADJ_CTRL_3 0x1ac +#define QSERDES_V10_COM_ADDITIONAL_MISC 0x1b4 +#define QSERDES_V10_COM_ADDITIONAL_MISC_2 0x1b8 +#define QSERDES_V10_COM_ADDITIONAL_MISC_3 0x1bc +#define QSERDES_V10_COM_CMN_STATUS 0x2c8 +#define QSERDES_V10_COM_C_READY_STATUS 0x2f0 #endif diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v10.h b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v10.h index d81ebdde0063..edbc26b6d74f 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v10.h +++ b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v10.h @@ -6,42 +6,86 @@ #ifndef QCOM_PHY_QMP_QSERDES_TXRX_V10_H_ #define QCOM_PHY_QMP_QSERDES_TXRX_V10_H_ -#define QSERDES_V10_TX_RES_CODE_LANE_OFFSET_TX 0x03c -#define QSERDES_V10_TX_RES_CODE_LANE_OFFSET_RX 0x040 -#define QSERDES_V10_TX_LANE_MODE_1 0x084 -#define QSERDES_V10_TX_LANE_MODE_3 0x08c -#define QSERDES_V10_TX_LANE_MODE_4 0x090 -#define QSERDES_V10_TX_LANE_MODE_5 0x094 -#define QSERDES_V10_TX_PI_QEC_CTRL 0x0e4 +/* Only for QMP V10 PHY - QSERDES TX/RX registers */ -#define QSERDES_V10_RX_UCDR_FO_GAIN 0x008 -#define QSERDES_V10_RX_UCDR_SO_GAIN 0x014 -#define QSERDES_V10_RX_UCDR_SB2_THRESH1 0x04c -#define QSERDES_V10_RX_UCDR_SB2_THRESH2 0x050 -#define QSERDES_V10_RX_TX_ADAPT_PRE_THRESH1 0x0c4 -#define QSERDES_V10_RX_TX_ADAPT_PRE_THRESH2 0x0c8 -#define QSERDES_V10_RX_TX_ADAPT_POST_THRESH 0x0cc -#define QSERDES_V10_RX_VGA_CAL_CNTRL2 0x0d8 -#define QSERDES_V10_RX_GM_CAL 0x0dc -#define QSERDES_V10_RX_RX_IDAC_TSETTLE_LOW 0x0f8 +/* TX registers */ +#define QSERDES_V10_TX_CLKBUF_ENABLE 0x08 +#define QSERDES_V10_TX_TX_EMP_POST1_LVL 0x0c +#define QSERDES_V10_TX_TX_DRV_LVL 0x14 +#define QSERDES_V10_TX_RESET_TSYNC_EN 0x1c +#define QSERDES_V10_TX_PRE_STALL_LDO_BOOST_EN 0x20 +#define QSERDES_V10_TX_TX_BAND 0x24 +#define QSERDES_V10_TX_INTERFACE_SELECT 0x2c +#define QSERDES_V10_TX_RES_CODE_LANE_TX 0x34 +#define QSERDES_V10_TX_RES_CODE_LANE_RX 0x38 +#define QSERDES_V10_TX_RES_CODE_LANE_OFFSET_TX 0x3c +#define QSERDES_V10_TX_RES_CODE_LANE_OFFSET_RX 0x40 +#define QSERDES_V10_TX_TRANSCEIVER_BIAS_EN 0x54 +#define QSERDES_V10_TX_HIGHZ_DRVR_EN 0x58 +#define QSERDES_V10_TX_TX_POL_INV 0x5c +#define QSERDES_V10_TX_PARRATE_REC_DETECT_IDLE_EN 0x60 +#define QSERDES_V10_TX_BIST_PATTERN7 0x7c +#define QSERDES_V10_TX_LANE_MODE_1 0x84 +#define QSERDES_V10_TX_LANE_MODE_2 0x88 +#define QSERDES_V10_TX_LANE_MODE_3 0x8c +#define QSERDES_V10_TX_LANE_MODE_4 0x90 +#define QSERDES_V10_TX_LANE_MODE_5 0x94 +#define QSERDES_V10_TX_RCV_DETECT_LVL_2 0xa4 +#define QSERDES_V10_TX_TRAN_DRVR_EMP_EN 0xc0 +#define QSERDES_V10_TX_TX_INTERFACE_MODE 0xc4 +#define QSERDES_V10_TX_VMODE_CTRL1 0xc8 +#define QSERDES_V10_TX_PI_QEC_CTRL 0xe4 + +/* RX registers */ +#define QSERDES_V10_RX_UCDR_FO_GAIN 0x08 +#define QSERDES_V10_RX_UCDR_SO_GAIN 0x14 +#define QSERDES_V10_RX_UCDR_FASTLOCK_FO_GAIN 0x30 +#define QSERDES_V10_RX_UCDR_SO_SATURATION_AND_ENABLE 0x34 +#define QSERDES_V10_RX_UCDR_FASTLOCK_COUNT_LOW 0x3c +#define QSERDES_V10_RX_UCDR_FASTLOCK_COUNT_HIGH 0x40 +#define QSERDES_V10_RX_UCDR_PI_CONTROLS 0x44 +#define QSERDES_V10_RX_UCDR_SB2_THRESH1 0x4c +#define QSERDES_V10_RX_UCDR_SB2_THRESH2 0x50 +#define QSERDES_V10_RX_UCDR_SB2_GAIN1 0x54 +#define QSERDES_V10_RX_UCDR_SB2_GAIN2 0x58 +#define QSERDES_V10_RX_AUX_DATA_TCOARSE_TFINE 0x60 +#define QSERDES_V10_RX_TX_ADAPT_PRE_THRESH1 0xc4 +#define QSERDES_V10_RX_TX_ADAPT_PRE_THRESH2 0xc8 +#define QSERDES_V10_RX_TX_ADAPT_POST_THRESH 0xcc +#define QSERDES_V10_RX_VGA_CAL_CNTRL1 0xd4 +#define QSERDES_V10_RX_VGA_CAL_CNTRL2 0xd8 +#define QSERDES_V10_RX_GM_CAL 0xdc +#define QSERDES_V10_RX_RX_EQU_ADAPTOR_CNTRL2 0xec +#define QSERDES_V10_RX_RX_EQU_ADAPTOR_CNTRL3 0xf0 +#define QSERDES_V10_RX_RX_EQU_ADAPTOR_CNTRL4 0xf4 +#define QSERDES_V10_RX_RX_IDAC_TSETTLE_LOW 0xf8 +#define QSERDES_V10_RX_RX_IDAC_TSETTLE_HIGH 0xfc +#define QSERDES_V10_RX_RX_IDAC_ENABLES 0x100 +#define QSERDES_V10_RX_RX_TERM_AC_BYPASS_DC_COUPLE_OFFSET 0x104 +#define QSERDES_V10_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1 0x110 #define QSERDES_V10_RX_SIGDET_ENABLES 0x118 #define QSERDES_V10_RX_SIGDET_CNTRL 0x11c +#define QSERDES_V10_RX_SIGDET_DEGLITCH_CNTRL 0x124 #define QSERDES_V10_RX_RX_MODE_00_LOW 0x15c #define QSERDES_V10_RX_RX_MODE_00_HIGH 0x160 -#define QSERDES_V10_RX_RX_MODE_00_HIGH2 0x164 -#define QSERDES_V10_RX_RX_MODE_00_HIGH3 0x168 -#define QSERDES_V10_RX_RX_MODE_00_HIGH4 0x16c +#define QSERDES_V10_RX_RX_MODE_00_HIGH2 0x164 +#define QSERDES_V10_RX_RX_MODE_00_HIGH3 0x168 +#define QSERDES_V10_RX_RX_MODE_00_HIGH4 0x16c #define QSERDES_V10_RX_RX_MODE_01_LOW 0x170 #define QSERDES_V10_RX_RX_MODE_01_HIGH 0x174 -#define QSERDES_V10_RX_RX_MODE_01_HIGH2 0x178 -#define QSERDES_V10_RX_RX_MODE_01_HIGH3 0x17c -#define QSERDES_V10_RX_RX_MODE_01_HIGH4 0x180 +#define QSERDES_V10_RX_RX_MODE_01_HIGH2 0x178 +#define QSERDES_V10_RX_RX_MODE_01_HIGH3 0x17c +#define QSERDES_V10_RX_RX_MODE_01_HIGH4 0x180 #define QSERDES_V10_RX_RX_MODE_10_LOW 0x184 #define QSERDES_V10_RX_RX_MODE_10_HIGH 0x188 -#define QSERDES_V10_RX_RX_MODE_10_HIGH2 0x18c -#define QSERDES_V10_RX_RX_MODE_10_HIGH3 0x190 -#define QSERDES_V10_RX_RX_MODE_10_HIGH4 0x194 -#define QSERDES_V10_RX_DFE_CTLE_POST_CAL_OFFSET 0x1a4 +#define QSERDES_V10_RX_RX_MODE_10_HIGH2 0x18c +#define QSERDES_V10_RX_RX_MODE_10_HIGH3 0x190 +#define QSERDES_V10_RX_RX_MODE_10_HIGH4 0x194 +#define QSERDES_V10_RX_DFE_EN_TIMER 0x1a0 +#define QSERDES_V10_RX_DFE_CTLE_POST_CAL_OFFSET 0x1a4 +#define QSERDES_V10_RX_DCC_CTRL1 0x1a8 +#define QSERDES_V10_RX_VTH_CODE 0x1b0 +#define QSERDES_V10_RX_SIGDET_CAL_CTRL1 0x1e4 #define QSERDES_V10_RX_SIGDET_CAL_TRIM 0x1f8 #endif From e6c3be558da06062a6a6fcbb8d33986832db3d60 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Thu, 6 Aug 2026 11:22:25 +0100 Subject: [PATCH 25/76] dt-bindings: phy: renesas,usb2-phy: Document RZ/G3L PHY bindings Add device tree binding support for the RZ/G3L (r9a08g046) USB2 PHY. The RZ/G3L USB PHY is almost identical to the RZ/G3S USB PHY, the difference being 2 OTG blocks on RZ/G3L compared to 1 on RZ/G3S. Acked-by: Krzysztof Kozlowski Signed-off-by: Biju Das Link: https://patch.msgid.link/20260806102236.149159-7-biju.das.jz@bp.renesas.com Signed-off-by: Vinod Koul --- Documentation/devicetree/bindings/phy/renesas,usb2-phy.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/phy/renesas,usb2-phy.yaml b/Documentation/devicetree/bindings/phy/renesas,usb2-phy.yaml index 9740e5b335f9..d6b9d08ceec6 100644 --- a/Documentation/devicetree/bindings/phy/renesas,usb2-phy.yaml +++ b/Documentation/devicetree/bindings/phy/renesas,usb2-phy.yaml @@ -16,6 +16,7 @@ properties: - enum: - renesas,usb2-phy-r8a77470 # RZ/G1C - renesas,usb2-phy-r9a08g045 # RZ/G3S + - renesas,usb2-phy-r9a08g046 # RZ/G3L - renesas,usb2-phy-r9a09g057 # RZ/V2H(P) - items: @@ -132,6 +133,7 @@ allOf: enum: - renesas,usb2-phy-r9a09g057 - renesas,usb2-phy-r9a08g045 + - renesas,usb2-phy-r9a08g046 - renesas,rzg2l-usb2-phy then: properties: From 881f6b6f4ccdbdd6b856a574445694bb1f56d79e Mon Sep 17 00:00:00 2001 From: Biju Das Date: Thu, 6 Aug 2026 11:22:26 +0100 Subject: [PATCH 26/76] phy: renesas: phy-rcar-gen3-usb2: Add RZ/G3L support Add renesas,usb2-phy-r9a08g046 to the OF match table, reusing rz_g3s_phy_usb2_data as the PHY configuration is shared with RZ/G3S. While the PHY data is shared, RZ/G3L differs from RZ/G3S in that it has two OTG controllers, OTG interrupts on port 2, and a controllable OTG_PERI bit in COMMCTRL for host/device switching on the port 2 USB controller, which is fixed to host-only on RZ/G3S. Signed-off-by: Biju Das Link: https://patch.msgid.link/20260806102236.149159-8-biju.das.jz@bp.renesas.com Signed-off-by: Vinod Koul --- drivers/phy/renesas/phy-rcar-gen3-usb2.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/phy/renesas/phy-rcar-gen3-usb2.c b/drivers/phy/renesas/phy-rcar-gen3-usb2.c index 9a45d840efeb..d06fb52ed5f1 100644 --- a/drivers/phy/renesas/phy-rcar-gen3-usb2.c +++ b/drivers/phy/renesas/phy-rcar-gen3-usb2.c @@ -721,6 +721,10 @@ static const struct of_device_id rcar_gen3_phy_usb2_match_table[] = { .compatible = "renesas,usb2-phy-r9a08g045", .data = &rz_g3s_phy_usb2_data, }, + { + .compatible = "renesas,usb2-phy-r9a08g046", + .data = &rz_g3s_phy_usb2_data, + }, { .compatible = "renesas,usb2-phy-r9a09g057", .data = &rz_v2h_phy_usb2_data, From 49c9b71b45081e5e5eeb507a2d6edb80d332dc59 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Thu, 6 Aug 2026 11:22:27 +0100 Subject: [PATCH 27/76] phy: renesas: phy-rcar-gen3-usb2: Fix devm action registration for disabled VBUS regulator devm_regulator_get_exclusive() initialises the regulator with enable_count = 1, requiring the consumer to disable it before release. The devm disable action was previously only registered when the caller explicitly requested enable, so when the regulator was left in its initial enabled state without an explicit enable call, the cleanup path skipped decrementing enable_count, triggering a WARN_ON during regulator release on device removal. Fix this by always registering the devm disable action based on the actual enabled state via regulator_is_enabled(), regardless of whether the caller requested an explicit enable. This covers both the explicitly enabled case and the initial state set by devm_regulator_get_exclusive(). Fixes: 24843404efe4 ("phy: renesas: phy-rcar-gen3-usb2: Control VBUS for RZ/G2L SoCs") Signed-off-by: Biju Das Link: https://patch.msgid.link/20260806102236.149159-9-biju.das.jz@bp.renesas.com Signed-off-by: Vinod Koul --- drivers/phy/renesas/phy-rcar-gen3-usb2.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/drivers/phy/renesas/phy-rcar-gen3-usb2.c b/drivers/phy/renesas/phy-rcar-gen3-usb2.c index d06fb52ed5f1..ef38c3b365d4 100644 --- a/drivers/phy/renesas/phy-rcar-gen3-usb2.c +++ b/drivers/phy/renesas/phy-rcar-gen3-usb2.c @@ -905,15 +905,17 @@ static int rcar_gen3_phy_usb2_vbus_regulator_get_exclusive_enable(struct rcar_ge if (IS_ERR(channel->vbus)) return PTR_ERR(channel->vbus); - if (!enable) - return 0; + if (enable) { + ret = regulator_enable(channel->vbus); + if (ret) + return ret; + } - ret = regulator_enable(channel->vbus); - if (ret) - return ret; + if (regulator_is_enabled(channel->vbus)) + return devm_add_action_or_reset(dev, rcar_gen3_phy_usb2_vbus_disable_action, + channel->vbus); - return devm_add_action_or_reset(dev, rcar_gen3_phy_usb2_vbus_disable_action, - channel->vbus); + return 0; } static int rcar_gen3_phy_usb2_vbus_regulator_register(struct rcar_gen3_chan *channel) From f8bb67fa3651d494427194c1e283886ad4cdbde0 Mon Sep 17 00:00:00 2001 From: Esteban Urrutia Date: Tue, 4 Aug 2026 14:06:19 -0400 Subject: [PATCH 28/76] dt-bindings: phy: qcom,sc8280xp-qmp-usb43dp-phy: Add SM8475 QMP PHY SM8450 init sequence for this PHY varies significantly and can't be used in SM8475. Add bindings for the PHY found in this SoC. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Esteban Urrutia Link: https://patch.msgid.link/20260804-sm8475-bup-usbss-v3-1-9a5b9ebf62de@proton.me Signed-off-by: Vinod Koul --- .../devicetree/bindings/phy/qcom,sc8280xp-qmp-usb43dp-phy.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-usb43dp-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-usb43dp-phy.yaml index ea3f1699eee8..8adfc78108fe 100644 --- a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-usb43dp-phy.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-usb43dp-phy.yaml @@ -38,6 +38,7 @@ properties: - qcom,sm8250-qmp-usb3-dp-phy - qcom,sm8350-qmp-usb3-dp-phy - qcom,sm8450-qmp-usb3-dp-phy + - qcom,sm8475-qmp-usb3-dp-phy - qcom,sm8550-qmp-usb3-dp-phy - qcom,sm8650-qmp-usb3-dp-phy - qcom,sm8750-qmp-usb3-dp-phy From 9583243a524c0ea4dd08541900ab2c7da95ce13f Mon Sep 17 00:00:00 2001 From: Esteban Urrutia Date: Tue, 4 Aug 2026 14:06:20 -0400 Subject: [PATCH 29/76] phy: qcom: qmp-combo: Add SM8475 support Has been tested with the following capabilities: - USB Type-C at 10Gb/s - DP Alt Mode, using HBR2 - USB Type-C at 480Mb/s + DP Alt Mode, using HBR2 RX and PCS USB tables had to be added, while serdes, TX and PCS tables were reused from other SoCs. Some SoCs such as SAR2130P, SM7550, SM7635 and SM8475 use 4nm-v1 PLLs. For SM8475, trying to use qmp_v6_dp_serdes_tbl structs (which are for 4nm-v1.1 PLLs) causes link training to fail because of ref clock differences, which in turn makes these structs differ. Add structs for v1 PLLs as well. Signed-off-by: Esteban Urrutia Reviewed-by: Dmitry Baryshkov Link: https://patch.msgid.link/20260804-sm8475-bup-usbss-v3-2-9a5b9ebf62de@proton.me Signed-off-by: Vinod Koul --- drivers/phy/qualcomm/phy-qcom-qmp-combo.c | 169 ++++++++++++++++++++++ 1 file changed, 169 insertions(+) diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-combo.c b/drivers/phy/qualcomm/phy-qcom-qmp-combo.c index ae80eee628c5..322bd5b75e06 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-combo.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-combo.c @@ -1098,6 +1098,55 @@ static const struct qmp_phy_init_tbl sm8350_usb3_pcs_usb_tbl[] = { QMP_PHY_INIT_CFG(QPHY_V5_PCS_USB3_RXEQTRAINING_DFE_TIME_S2, 0x07), }; +static const struct qmp_phy_init_tbl sm8475_usb3_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V6_RX_UCDR_FO_GAIN, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_UCDR_SO_GAIN, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_UCDR_FASTLOCK_FO_GAIN, 0x2f), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_UCDR_FASTLOCK_COUNT_LOW, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_UCDR_FASTLOCK_COUNT_HIGH, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_UCDR_PI_CONTROLS, 0x99), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_UCDR_SB2_THRESH1, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_UCDR_SB2_THRESH2, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_UCDR_SB2_GAIN1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_UCDR_SB2_GAIN2, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_AUX_DATA_TCOARSE_TFINE, 0xa0), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_VGA_CAL_CNTRL1, 0x54), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_VGA_CAL_CNTRL2, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_GM_CAL, 0x13), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4a), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_RX_EQU_ADAPTOR_CNTRL4, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_RX_IDAC_TSETTLE_LOW, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_RX_IDAC_TSETTLE_HIGH, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x47), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_SIGDET_CNTRL, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_SIGDET_DEGLITCH_CNTRL, 0x0e), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_RX_MODE_00_LOW, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_RX_MODE_00_HIGH, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_RX_MODE_00_HIGH2, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_RX_MODE_00_HIGH3, 0xdf), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_RX_MODE_00_HIGH4, 0xfe), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_RX_MODE_01_LOW, 0xdc), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_RX_MODE_01_HIGH, 0x5c), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_RX_MODE_01_HIGH2, 0x9c), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_RX_MODE_01_HIGH3, 0x1d), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_RX_MODE_01_HIGH4, 0x09), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_DFE_EN_TIMER, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_DFE_CTLE_POST_CAL_OFFSET, 0x38), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_DCC_CTRL1, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_VTH_CODE, 0x10), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_SIGDET_CAL_CTRL1, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_SIGDET_CAL_TRIM, 0x08), +}; + +static const struct qmp_phy_init_tbl sm8475_usb3_pcs_usb_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V6_PCS_USB3_LFPS_DET_HIGH_COUNT_VAL, 0xf8), + QMP_PHY_INIT_CFG(QPHY_V6_PCS_USB3_RXEQTRAINING_DFE_TIME_S2, 0x07), + QMP_PHY_INIT_CFG(QPHY_V6_PCS_USB3_RCVR_DTCT_DLY_U3_L, 0x40), + QMP_PHY_INIT_CFG(QPHY_V6_PCS_USB3_RCVR_DTCT_DLY_U3_H, 0x00), +}; + static const struct qmp_phy_init_tbl sm8550_usb3_serdes_tbl[] = { QMP_PHY_INIT_CFG(QSERDES_V6_COM_SSC_STEP_SIZE1_MODE1, 0xc0), QMP_PHY_INIT_CFG(QSERDES_V6_COM_SSC_STEP_SIZE2_MODE1, 0x01), @@ -1372,6 +1421,29 @@ static const struct qmp_phy_init_tbl qmp_v5_5nm_dp_tx_tbl[] = { QMP_PHY_INIT_CFG(QSERDES_V5_5NM_TX_TX_BAND, 0x01), }; +static const struct qmp_phy_init_tbl qmp_v6_dp_v1_serdes_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V6_COM_SVS_MODE_CLK_SEL, 0x15), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_SYSCLK_EN_SEL, 0x3b), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_SYS_CLK_CTRL, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_CLK_ENABLE1, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_SYSCLK_BUF_ENABLE, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_CLK_SELECT, 0x30), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_IVCO, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_CCTRL_MODE0, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_CP_CTRL_MODE0, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_DIV_FRAC_START1_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_CMN_CONFIG_1, 0x12), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_INTEGLOOP_GAIN0_MODE0, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_INTEGLOOP_GAIN1_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_VCO_TUNE_MAP, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_BG_TIMER, 0x0e), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_CORE_CLK_DIV_MODE0, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_VCO_TUNE_CTRL, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_BIAS_EN_CLK_BUFLR_EN, 0x17), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_CORE_CLK_EN, 0x0f), +}; + static const struct qmp_phy_init_tbl qmp_v6_dp_serdes_tbl[] = { QMP_PHY_INIT_CFG(QSERDES_V6_COM_SVS_MODE_CLK_SEL, 0x15), QMP_PHY_INIT_CFG(QSERDES_V6_COM_SYSCLK_EN_SEL, 0x3b), @@ -1506,6 +1578,54 @@ static const struct qmp_phy_init_tbl qmp_v8_n3p_dp_tx_tbl[] = { QMP_PHY_INIT_CFG(QSERDES_V8_LALB_TX1_RESTRIM_POST_CAL_OFFSET, 0x10), }; +static const struct qmp_phy_init_tbl qmp_v6_dp_v1_serdes_tbl_rbr[] = { + QMP_PHY_INIT_CFG(QSERDES_V6_COM_HSCLK_SEL_1, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_DEC_START_MODE0, 0x69), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_DIV_FRAC_START2_MODE0, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_DIV_FRAC_START3_MODE0, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP1_MODE0, 0x6f), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP2_MODE0, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP_EN, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_BIN_VCOCAL_CMP_CODE1_MODE0, 0xe2), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_BIN_VCOCAL_CMP_CODE2_MODE0, 0x18), +}; + +static const struct qmp_phy_init_tbl qmp_v6_dp_v1_serdes_tbl_hbr[] = { + QMP_PHY_INIT_CFG(QSERDES_V6_COM_HSCLK_SEL_1, 0x03), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_DEC_START_MODE0, 0x69), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_DIV_FRAC_START2_MODE0, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_DIV_FRAC_START3_MODE0, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP1_MODE0, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP2_MODE0, 0x0e), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP_EN, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_BIN_VCOCAL_CMP_CODE1_MODE0, 0xe2), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_BIN_VCOCAL_CMP_CODE2_MODE0, 0x18), +}; + +static const struct qmp_phy_init_tbl qmp_v6_dp_v1_serdes_tbl_hbr2[] = { + QMP_PHY_INIT_CFG(QSERDES_V6_COM_HSCLK_SEL_1, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_DEC_START_MODE0, 0x8c), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_DIV_FRAC_START2_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_DIV_FRAC_START3_MODE0, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP1_MODE0, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP2_MODE0, 0x1c), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP_EN, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_BIN_VCOCAL_CMP_CODE1_MODE0, 0x2e), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_BIN_VCOCAL_CMP_CODE2_MODE0, 0x21), +}; + +static const struct qmp_phy_init_tbl qmp_v6_dp_v1_serdes_tbl_hbr3[] = { + QMP_PHY_INIT_CFG(QSERDES_V6_COM_HSCLK_SEL_1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_DEC_START_MODE0, 0x69), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_DIV_FRAC_START2_MODE0, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_DIV_FRAC_START3_MODE0, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP1_MODE0, 0x2f), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP2_MODE0, 0x2a), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP_EN, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_BIN_VCOCAL_CMP_CODE1_MODE0, 0xe2), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_BIN_VCOCAL_CMP_CODE2_MODE0, 0x18), +}; + static const struct qmp_phy_init_tbl qmp_v6_dp_serdes_tbl_rbr[] = { QMP_PHY_INIT_CFG(QSERDES_V6_COM_HSCLK_SEL_1, 0x05), QMP_PHY_INIT_CFG(QSERDES_V6_COM_DEC_START_MODE0, 0x34), @@ -3056,6 +3176,51 @@ static const struct qmp_phy_cfg sm8350_usb3dpphy_cfg = { .has_pwrdn_delay = true, }; +static const struct qmp_phy_cfg sm8475_usb3dpphy_cfg = { + .offsets = &qmp_combo_offsets_v3, + + .serdes_tbl = sar2130p_usb3_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(sar2130p_usb3_serdes_tbl), + .tx_tbl = sm8550_usb3_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(sm8550_usb3_tx_tbl), + .rx_tbl = sm8475_usb3_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(sm8475_usb3_rx_tbl), + .pcs_tbl = sm8550_usb3_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(sm8550_usb3_pcs_tbl), + .pcs_usb_tbl = sm8475_usb3_pcs_usb_tbl, + .pcs_usb_tbl_num = ARRAY_SIZE(sm8475_usb3_pcs_usb_tbl), + + .dp_serdes_tbl = qmp_v6_dp_v1_serdes_tbl, + .dp_serdes_tbl_num = ARRAY_SIZE(qmp_v6_dp_v1_serdes_tbl), + .dp_tx_tbl = qmp_v6_dp_tx_tbl, + .dp_tx_tbl_num = ARRAY_SIZE(qmp_v6_dp_tx_tbl), + + .serdes_tbl_rbr = qmp_v6_dp_v1_serdes_tbl_rbr, + .serdes_tbl_rbr_num = ARRAY_SIZE(qmp_v6_dp_v1_serdes_tbl_rbr), + .serdes_tbl_hbr = qmp_v6_dp_v1_serdes_tbl_hbr, + .serdes_tbl_hbr_num = ARRAY_SIZE(qmp_v6_dp_v1_serdes_tbl_hbr), + .serdes_tbl_hbr2 = qmp_v6_dp_v1_serdes_tbl_hbr2, + .serdes_tbl_hbr2_num = ARRAY_SIZE(qmp_v6_dp_v1_serdes_tbl_hbr2), + .serdes_tbl_hbr3 = qmp_v6_dp_v1_serdes_tbl_hbr3, + .serdes_tbl_hbr3_num = ARRAY_SIZE(qmp_v6_dp_v1_serdes_tbl_hbr3), + + .swing_hbr_rbr = &qmp_dp_v5_voltage_swing_hbr_rbr, + .pre_emphasis_hbr_rbr = &qmp_dp_v6_pre_emphasis_hbr_rbr, + .swing_hbr3_hbr2 = &qmp_dp_v5_voltage_swing_hbr3_hbr2, + .pre_emphasis_hbr3_hbr2 = &qmp_dp_v5_pre_emphasis_hbr3_hbr2, + + .dp_aux_init = qmp_v4_dp_aux_init, + .configure_dp_tx = qmp_v4_configure_dp_tx, + .configure_dp_clocks = qmp_v3_configure_dp_clocks, + .configure_dp_phy = qmp_v4_configure_dp_phy, + + .regs = qmp_v6_usb3phy_regs_layout, + .reset_list = msm8996_usb3phy_reset_l, + .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), +}; + static const struct qmp_phy_cfg sm8550_usb3dpphy_cfg = { .offsets = &qmp_combo_offsets_v3, @@ -5255,6 +5420,10 @@ static const struct of_device_id qmp_combo_of_match_table[] = { .compatible = "qcom,sm8450-qmp-usb3-dp-phy", .data = &sm8350_usb3dpphy_cfg, }, + { + .compatible = "qcom,sm8475-qmp-usb3-dp-phy", + .data = &sm8475_usb3dpphy_cfg, + }, { .compatible = "qcom,sm8550-qmp-usb3-dp-phy", .data = &sm8550_usb3dpphy_cfg, From 87a1805b1c346b092c34f96f3806f207792910e1 Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Fri, 31 Jul 2026 16:11:19 +0800 Subject: [PATCH 30/76] phy: fsl-imx8mq-usb: fix typec switch leak on probe error path If probe fails after imx95_usb_phy_get_tca() succeeds, the typec switch leaks because the only cleanup path was in .remove(), which never runs on probe failure. Use devm_add_action_or_reset() so the switch is cleaned up on both probe failure and driver removal. The imx95_usb_phy_put_tca() is no longer needed, it will be removed in .remove() too. Fixes: b58f0f86fd61 ("phy: fsl-imx8mq-usb: add tca function driver for imx95") Cc: stable@vger.kernel.org Reviewed-by: Frank Li Reviewed-by: Xu Yang Signed-off-by: Felix Gu Signed-off-by: Xu Yang Link: https://patch.msgid.link/20260731-imx8mp-usb-phy-improvement-v8-1-2ec8d6b3854d@nxp.com Signed-off-by: Vinod Koul --- drivers/phy/freescale/phy-fsl-imx8mq-usb.c | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/drivers/phy/freescale/phy-fsl-imx8mq-usb.c b/drivers/phy/freescale/phy-fsl-imx8mq-usb.c index b05d80e849a1..9a33c06d6fc3 100644 --- a/drivers/phy/freescale/phy-fsl-imx8mq-usb.c +++ b/drivers/phy/freescale/phy-fsl-imx8mq-usb.c @@ -173,9 +173,9 @@ static struct typec_switch_dev *tca_blk_get_typec_switch(struct platform_device return sw; } -static void tca_blk_put_typec_switch(struct typec_switch_dev *sw) +static void tca_blk_put_typec_switch(void *data) { - typec_switch_unregister(sw); + typec_switch_unregister(data); } static void tca_blk_orientation_set(struct tca_blk *tca, @@ -248,6 +248,7 @@ static struct tca_blk *imx95_usb_phy_get_tca(struct platform_device *pdev, struct device *dev = &pdev->dev; struct resource *res; struct tca_blk *tca; + int ret; res = platform_get_resource(pdev, IORESOURCE_MEM, 1); if (!res) @@ -266,19 +267,13 @@ static struct tca_blk *imx95_usb_phy_get_tca(struct platform_device *pdev, tca->orientation = TYPEC_ORIENTATION_NORMAL; tca->sw = tca_blk_get_typec_switch(pdev, imx_phy); + ret = devm_add_action_or_reset(&pdev->dev, tca_blk_put_typec_switch, tca->sw); + if (ret) + return ERR_PTR(ret); + return tca; } -static void imx95_usb_phy_put_tca(struct imx8mq_usb_phy *imx_phy) -{ - struct tca_blk *tca = imx_phy->tca; - - if (!tca) - return; - - tca_blk_put_typec_switch(tca->sw); -} - static u32 phy_tx_vref_tune_from_property(u32 percent) { percent = clamp(percent, 94U, 124U); @@ -741,9 +736,7 @@ static int imx8mq_usb_phy_probe(struct platform_device *pdev) static void imx8mq_usb_phy_remove(struct platform_device *pdev) { - struct imx8mq_usb_phy *imx_phy = platform_get_drvdata(pdev); - imx95_usb_phy_put_tca(imx_phy); } static struct platform_driver imx8mq_usb_phy_driver = { From 2602012b8c625e63493647356d7290ee7c8fef2b Mon Sep 17 00:00:00 2001 From: Xu Yang Date: Fri, 31 Jul 2026 16:11:20 +0800 Subject: [PATCH 31/76] phy: fsl-imx8mq-usb: set usb phy to be wakeup capable Set PHY wakeup capable because this PHY supports remote wakeup function. Reviewed-by: Frank Li Signed-off-by: Xu Yang Link: https://patch.msgid.link/20260731-imx8mp-usb-phy-improvement-v8-2-2ec8d6b3854d@nxp.com Signed-off-by: Vinod Koul --- drivers/phy/freescale/phy-fsl-imx8mq-usb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/phy/freescale/phy-fsl-imx8mq-usb.c b/drivers/phy/freescale/phy-fsl-imx8mq-usb.c index 9a33c06d6fc3..3a5788c609e1 100644 --- a/drivers/phy/freescale/phy-fsl-imx8mq-usb.c +++ b/drivers/phy/freescale/phy-fsl-imx8mq-usb.c @@ -728,6 +728,7 @@ static int imx8mq_usb_phy_probe(struct platform_device *pdev) "failed to get tca\n"); imx8m_get_phy_tuning_data(imx_phy); + device_set_wakeup_capable(dev, true); phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); From 56eec5d9665cebe580d41cdf2ea7abb52d23e122 Mon Sep 17 00:00:00 2001 From: Xu Yang Date: Fri, 31 Jul 2026 16:11:21 +0800 Subject: [PATCH 32/76] phy: fsl-imx8mq-usb: add runtime PM support Add runtime PM support to ensure the PHY clocks are properly gated when the PHY is not in use, reducing power consumption. Clock management is moved from power_on()/power_off() callbacks into the runtime_resume()/runtime_suspend() callbacks respectively. The PHY subsystem core already holds a runtime PM reference around init() and power_on/off() calls, so no explicit clock handling is needed there. Use devm_clk_get_enabled() and devm_clk_get_optional_enabled() in probe() to keep clocks enabled initially. This ensures the PHY remains functional when CONFIG_PM is disabled, where runtime suspend/resume callbacks are never invoked. In tca_blk_typec_switch_set(), replace the manual clk_prepare_enable() / clk_disable_unprepare() pair with PM_RUNTIME_ACQUIRE_IF_ENABLED() to guard register access against a concurrently suspended PHY. Move devm_regulator_get() before pm_runtime_enable() to avoid having to clean up runtime PM state on regulator acquisition failure. In remove(), call pm_runtime_get_sync() before pm_runtime_disable() to ensure the PHY is resumed and clocks are enabled before the devres teardown disables them. Signed-off-by: Xu Yang Reviewed-by: Frank Li Link: https://patch.msgid.link/20260731-imx8mp-usb-phy-improvement-v8-3-2ec8d6b3854d@nxp.com Signed-off-by: Vinod Koul --- drivers/phy/freescale/phy-fsl-imx8mq-usb.c | 107 +++++++++++++++------ 1 file changed, 76 insertions(+), 31 deletions(-) diff --git a/drivers/phy/freescale/phy-fsl-imx8mq-usb.c b/drivers/phy/freescale/phy-fsl-imx8mq-usb.c index 3a5788c609e1..42de2cff4d5f 100644 --- a/drivers/phy/freescale/phy-fsl-imx8mq-usb.c +++ b/drivers/phy/freescale/phy-fsl-imx8mq-usb.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -136,17 +137,15 @@ static int tca_blk_typec_switch_set(struct typec_switch_dev *sw, { struct imx8mq_usb_phy *imx_phy = typec_switch_get_drvdata(sw); struct tca_blk *tca = imx_phy->tca; - int ret; if (tca->orientation == orientation) return 0; - ret = clk_prepare_enable(imx_phy->clk); - if (ret) - return ret; + PM_RUNTIME_ACQUIRE_IF_ENABLED(&imx_phy->phy->dev, pm); + if (PM_RUNTIME_ACQUIRE_ERR(&pm)) + return -ENXIO; tca_blk_orientation_set(tca, orientation); - clk_disable_unprepare(imx_phy->clk); return 0; } @@ -620,16 +619,6 @@ static int imx8mq_phy_power_on(struct phy *phy) if (ret) return ret; - ret = clk_prepare_enable(imx_phy->clk); - if (ret) - return ret; - - ret = clk_prepare_enable(imx_phy->alt_clk); - if (ret) { - clk_disable_unprepare(imx_phy->clk); - return ret; - } - /* Disable rx term override */ value = readl(imx_phy->base + PHY_CTRL6); value &= ~PHY_CTRL6_RXTERM_OVERRIDE_SEL; @@ -648,8 +637,6 @@ static int imx8mq_phy_power_off(struct phy *phy) value |= PHY_CTRL6_RXTERM_OVERRIDE_SEL; writel(value, imx_phy->base + PHY_CTRL6); - clk_disable_unprepare(imx_phy->alt_clk); - clk_disable_unprepare(imx_phy->clk); regulator_disable(imx_phy->vbus); return 0; @@ -686,6 +673,7 @@ static int imx8mq_usb_phy_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct imx8mq_usb_phy *imx_phy; const struct phy_ops *phy_ops; + int ret; imx_phy = devm_kzalloc(dev, sizeof(*imx_phy), GFP_KERNEL); if (!imx_phy) @@ -693,13 +681,13 @@ static int imx8mq_usb_phy_probe(struct platform_device *pdev) platform_set_drvdata(pdev, imx_phy); - imx_phy->clk = devm_clk_get(dev, "phy"); + imx_phy->clk = devm_clk_get_enabled(dev, "phy"); if (IS_ERR(imx_phy->clk)) { dev_err(dev, "failed to get imx8mq usb phy clock\n"); return PTR_ERR(imx_phy->clk); } - imx_phy->alt_clk = devm_clk_get_optional(dev, "alt"); + imx_phy->alt_clk = devm_clk_get_optional_enabled(dev, "alt"); if (IS_ERR(imx_phy->alt_clk)) return dev_err_probe(dev, PTR_ERR(imx_phy->alt_clk), "Failed to get alt clk\n"); @@ -708,44 +696,101 @@ static int imx8mq_usb_phy_probe(struct platform_device *pdev) if (IS_ERR(imx_phy->base)) return PTR_ERR(imx_phy->base); - phy_ops = of_device_get_match_data(dev); - if (!phy_ops) - return -EINVAL; - - imx_phy->phy = devm_phy_create(dev, NULL, phy_ops); - if (IS_ERR(imx_phy->phy)) - return PTR_ERR(imx_phy->phy); - imx_phy->vbus = devm_regulator_get(dev, "vbus"); if (IS_ERR(imx_phy->vbus)) return dev_err_probe(dev, PTR_ERR(imx_phy->vbus), "failed to get vbus\n"); + phy_ops = of_device_get_match_data(dev); + if (!phy_ops) + return -EINVAL; + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + + imx_phy->phy = devm_phy_create(dev, NULL, phy_ops); + if (IS_ERR(imx_phy->phy)) { + ret = dev_err_probe(dev, PTR_ERR(imx_phy->phy), + "failed to create PHY\n"); + goto disable_rpm; + } + phy_set_drvdata(imx_phy->phy, imx_phy); imx_phy->tca = imx95_usb_phy_get_tca(pdev, imx_phy); - if (IS_ERR(imx_phy->tca)) - return dev_err_probe(dev, PTR_ERR(imx_phy->tca), - "failed to get tca\n"); + if (IS_ERR(imx_phy->tca)) { + ret = dev_err_probe(dev, PTR_ERR(imx_phy->tca), + "failed to get tca\n"); + goto disable_rpm; + } imx8m_get_phy_tuning_data(imx_phy); device_set_wakeup_capable(dev, true); phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + if (IS_ERR(phy_provider)) { + ret = dev_err_probe(dev, PTR_ERR(phy_provider), + "failed to register phy provider\n"); + goto disable_rpm; + } - return PTR_ERR_OR_ZERO(phy_provider); + return 0; + +disable_rpm: + pm_runtime_disable(dev); + return ret; } static void imx8mq_usb_phy_remove(struct platform_device *pdev) { + struct device *dev = &pdev->dev; + int ret; + ret = pm_runtime_get_sync(dev); + if (ret < 0) + dev_warn(dev, "failed to resume on remove: %d\n", ret); + + pm_runtime_disable(dev); + pm_runtime_put_noidle(dev); } +static int imx8mq_usb_phy_runtime_suspend(struct device *dev) +{ + struct imx8mq_usb_phy *imx_phy = dev_get_drvdata(dev); + + clk_disable_unprepare(imx_phy->alt_clk); + clk_disable_unprepare(imx_phy->clk); + + return 0; +} + +static int imx8mq_usb_phy_runtime_resume(struct device *dev) +{ + struct imx8mq_usb_phy *imx_phy = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(imx_phy->clk); + if (ret) + return ret; + + ret = clk_prepare_enable(imx_phy->alt_clk); + if (ret) { + clk_disable_unprepare(imx_phy->clk); + return ret; + } + + return 0; +} + +static DEFINE_RUNTIME_DEV_PM_OPS(imx8mq_usb_phy_pm_ops, imx8mq_usb_phy_runtime_suspend, + imx8mq_usb_phy_runtime_resume, NULL); + static struct platform_driver imx8mq_usb_phy_driver = { .probe = imx8mq_usb_phy_probe, .remove = imx8mq_usb_phy_remove, .driver = { .name = "imx8mq-usb-phy", .of_match_table = imx8mq_usb_phy_of_match, + .pm = pm_ptr(&imx8mq_usb_phy_pm_ops), .suppress_bind_attrs = true, } }; From 01e9e84a43a1b4e9f67b5bd36be3d127d773a223 Mon Sep 17 00:00:00 2001 From: Xu Yang Date: Fri, 31 Jul 2026 16:11:22 +0800 Subject: [PATCH 33/76] phy: fsl-imx8mq-usb: add control register regmap The CR port is a simple 16-bit data/address parallel port that is accessed through 32-bit MMIO registers for on-chip access to the control registers inside the USB 3.0 femtoPHY. Add control register regmap and export these registers by debugfs to help PHY's diagnostic. Reviewed-by: Frank Li Signed-off-by: Xu Yang Link: https://patch.msgid.link/20260731-imx8mp-usb-phy-improvement-v8-4-2ec8d6b3854d@nxp.com Signed-off-by: Vinod Koul --- drivers/phy/freescale/Kconfig | 1 + drivers/phy/freescale/phy-fsl-imx8mq-usb.c | 27 +++++++++++++++++++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/drivers/phy/freescale/Kconfig b/drivers/phy/freescale/Kconfig index 5bf3864fbe64..7e027755dd8e 100644 --- a/drivers/phy/freescale/Kconfig +++ b/drivers/phy/freescale/Kconfig @@ -7,6 +7,7 @@ config PHY_FSL_IMX8MQ_USB depends on OF && HAS_IOMEM depends on TYPEC || TYPEC=n select GENERIC_PHY + select REGMAP_MMIO default ARCH_MXC && ARM64 config PHY_MIXEL_LVDS_PHY diff --git a/drivers/phy/freescale/phy-fsl-imx8mq-usb.c b/drivers/phy/freescale/phy-fsl-imx8mq-usb.c index 42de2cff4d5f..e03f9dafd69e 100644 --- a/drivers/phy/freescale/phy-fsl-imx8mq-usb.c +++ b/drivers/phy/freescale/phy-fsl-imx8mq-usb.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0+ -/* Copyright (c) 2017 NXP. */ +/* Copyright 2017-2026 NXP. */ #include #include @@ -11,6 +11,7 @@ #include #include #include +#include #include #define PHY_CTRL0 0x0 @@ -56,6 +57,8 @@ #define PHY_CTRL6_ALT_CLK_EN BIT(1) #define PHY_CTRL6_ALT_CLK_SEL BIT(0) +#define PHY_CRCTL 0x30 + #define PHY_TUNE_DEFAULT 0xffffffff #define TCA_CLK_RST 0x00 @@ -119,6 +122,7 @@ struct imx8mq_usb_phy { void __iomem *base; struct regulator *vbus; struct tca_blk *tca; + struct regmap *cr_regmap; u32 pcs_tx_swing_full; u32 pcs_tx_deemph_3p5db; u32 tx_vref_tune; @@ -667,6 +671,14 @@ static const struct of_device_id imx8mq_usb_phy_of_match[] = { }; MODULE_DEVICE_TABLE(of, imx8mq_usb_phy_of_match); +static const struct regmap_config imx_cr_regmap_config = { + .name = "cr", + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = 0x7, +}; + static int imx8mq_usb_phy_probe(struct platform_device *pdev) { struct phy_provider *phy_provider; @@ -696,6 +708,13 @@ static int imx8mq_usb_phy_probe(struct platform_device *pdev) if (IS_ERR(imx_phy->base)) return PTR_ERR(imx_phy->base); + imx_phy->cr_regmap = devm_regmap_init_mmio(dev, imx_phy->base + PHY_CRCTL, + &imx_cr_regmap_config); + if (IS_ERR(imx_phy->cr_regmap)) { + dev_warn(dev, "Fail to init debug register regmap\n"); + imx_phy->cr_regmap = NULL; + } + imx_phy->vbus = devm_regulator_get(dev, "vbus"); if (IS_ERR(imx_phy->vbus)) return dev_err_probe(dev, PTR_ERR(imx_phy->vbus), "failed to get vbus\n"); @@ -757,6 +776,9 @@ static int imx8mq_usb_phy_runtime_suspend(struct device *dev) { struct imx8mq_usb_phy *imx_phy = dev_get_drvdata(dev); + if (imx_phy->cr_regmap) + regcache_cache_only(imx_phy->cr_regmap, true); + clk_disable_unprepare(imx_phy->alt_clk); clk_disable_unprepare(imx_phy->clk); @@ -778,6 +800,9 @@ static int imx8mq_usb_phy_runtime_resume(struct device *dev) return ret; } + if (imx_phy->cr_regmap) + regcache_cache_only(imx_phy->cr_regmap, false); + return 0; } From 2d9dcffd5d3995ba04feeeebe800cbf1c0d4bbbe Mon Sep 17 00:00:00 2001 From: Xu Yang Date: Fri, 31 Jul 2026 16:11:23 +0800 Subject: [PATCH 34/76] phy: fsl-imx8mq-usb: introduce per-variant driver data structure Replace direct use of phy_ops pointer in of_device_id .data with a dedicated imx8mq_usb_phy_drvdata structure. This allows per-variant driver data to be extended in the future without changing the match table. Reviewed-by: Frank Li Signed-off-by: Xu Yang Link: https://patch.msgid.link/20260731-imx8mp-usb-phy-improvement-v8-5-2ec8d6b3854d@nxp.com Signed-off-by: Vinod Koul --- drivers/phy/freescale/phy-fsl-imx8mq-usb.c | 29 ++++++++++++++++------ 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/drivers/phy/freescale/phy-fsl-imx8mq-usb.c b/drivers/phy/freescale/phy-fsl-imx8mq-usb.c index e03f9dafd69e..ba7603a008d8 100644 --- a/drivers/phy/freescale/phy-fsl-imx8mq-usb.c +++ b/drivers/phy/freescale/phy-fsl-imx8mq-usb.c @@ -132,6 +132,9 @@ struct imx8mq_usb_phy { u32 comp_dis_tune; }; +struct imx8mq_usb_phy_drvdata { + const struct phy_ops *ops; +}; static void tca_blk_orientation_set(struct tca_blk *tca, enum typec_orientation orientation); @@ -660,13 +663,25 @@ static const struct phy_ops imx8mp_usb_phy_ops = { .owner = THIS_MODULE, }; +static const struct imx8mq_usb_phy_drvdata imx8mq_usb_phy_data = { + .ops = &imx8mq_usb_phy_ops, +}; + +static const struct imx8mq_usb_phy_drvdata imx8mp_usb_phy_data = { + .ops = &imx8mp_usb_phy_ops, +}; + +static const struct imx8mq_usb_phy_drvdata imx95_usb_phy_data = { + .ops = &imx8mp_usb_phy_ops, +}; + static const struct of_device_id imx8mq_usb_phy_of_match[] = { {.compatible = "fsl,imx8mq-usb-phy", - .data = &imx8mq_usb_phy_ops,}, + .data = &imx8mq_usb_phy_data,}, {.compatible = "fsl,imx8mp-usb-phy", - .data = &imx8mp_usb_phy_ops,}, + .data = &imx8mp_usb_phy_data,}, {.compatible = "fsl,imx95-usb-phy", - .data = &imx8mp_usb_phy_ops,}, + .data = &imx95_usb_phy_data,}, { } }; MODULE_DEVICE_TABLE(of, imx8mq_usb_phy_of_match); @@ -684,7 +699,7 @@ static int imx8mq_usb_phy_probe(struct platform_device *pdev) struct phy_provider *phy_provider; struct device *dev = &pdev->dev; struct imx8mq_usb_phy *imx_phy; - const struct phy_ops *phy_ops; + const struct imx8mq_usb_phy_drvdata *phy_data; int ret; imx_phy = devm_kzalloc(dev, sizeof(*imx_phy), GFP_KERNEL); @@ -719,14 +734,14 @@ static int imx8mq_usb_phy_probe(struct platform_device *pdev) if (IS_ERR(imx_phy->vbus)) return dev_err_probe(dev, PTR_ERR(imx_phy->vbus), "failed to get vbus\n"); - phy_ops = of_device_get_match_data(dev); - if (!phy_ops) + phy_data = of_device_get_match_data(dev); + if (!phy_data) return -EINVAL; pm_runtime_set_active(dev); pm_runtime_enable(dev); - imx_phy->phy = devm_phy_create(dev, NULL, phy_ops); + imx_phy->phy = devm_phy_create(dev, NULL, phy_data->ops); if (IS_ERR(imx_phy->phy)) { ret = dev_err_probe(dev, PTR_ERR(imx_phy->phy), "failed to create PHY\n"); From ee9e2b39ea4890f804b0c75c8a40a0f68ecd8d0a Mon Sep 17 00:00:00 2001 From: Xu Yang Date: Fri, 31 Jul 2026 16:11:24 +0800 Subject: [PATCH 35/76] phy: fsl-imx8mq-usb: keep PHY power domain runtime always-on for i.MX8MP On i.MX8MP, the USB PHY has a dedicated power domain that was previously never powered off at runtime. With the introduction of runtime PM support, the power domain will be powered off if the device is runtime suspended, which breaks USB wakeup functionality. To preserve wakeup functionality, mark the PHY power domain as runtime always-on for i.MX8MP platform. To limit the behavior to i.MX8MP, add a need_genpd_rpm_on to imx8mq_usb_phy_drvdata and set it as true for i.MX8MP. Signed-off-by: Xu Yang Reviewed-by: Frank Li Link: https://patch.msgid.link/20260731-imx8mp-usb-phy-improvement-v8-6-2ec8d6b3854d@nxp.com Signed-off-by: Vinod Koul --- drivers/phy/freescale/phy-fsl-imx8mq-usb.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/phy/freescale/phy-fsl-imx8mq-usb.c b/drivers/phy/freescale/phy-fsl-imx8mq-usb.c index ba7603a008d8..8859835541b1 100644 --- a/drivers/phy/freescale/phy-fsl-imx8mq-usb.c +++ b/drivers/phy/freescale/phy-fsl-imx8mq-usb.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -134,6 +135,7 @@ struct imx8mq_usb_phy { struct imx8mq_usb_phy_drvdata { const struct phy_ops *ops; + bool need_genpd_rpm_on; }; static void tca_blk_orientation_set(struct tca_blk *tca, @@ -669,6 +671,7 @@ static const struct imx8mq_usb_phy_drvdata imx8mq_usb_phy_data = { static const struct imx8mq_usb_phy_drvdata imx8mp_usb_phy_data = { .ops = &imx8mp_usb_phy_ops, + .need_genpd_rpm_on = true, }; static const struct imx8mq_usb_phy_drvdata imx95_usb_phy_data = { @@ -738,6 +741,12 @@ static int imx8mq_usb_phy_probe(struct platform_device *pdev) if (!phy_data) return -EINVAL; + if (phy_data->need_genpd_rpm_on) { + ret = dev_pm_genpd_rpm_always_on(dev, true); + if (ret && ret != -EOPNOTSUPP) + dev_warn(dev, "failed to set genpd rpm always on\n"); + } + pm_runtime_set_active(dev); pm_runtime_enable(dev); From b780b8929c759cfa7a892d58625a5ad46cb1cbd2 Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Mon, 3 Aug 2026 20:43:24 +0800 Subject: [PATCH 36/76] phy: renesas: rcar-gen2: Fix double of_node_put on phy creation failure for_each_child_of_node_scoped() releases the node reference on scope exit, so the explicit of_node_put(np) in the devm_phy_create() error path drops it twice. Drop the redundant of_node_put() and let the scoped cleanup handle it. Fixes: b64b32791fb5 ("phy: renesas: rcar-gen2: Simplify with scoped for each OF child loop") Signed-off-by: Felix Gu Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260803-rcar-gen2-v1-1-9aa35c36d7d7@gmail.com Signed-off-by: Vinod Koul --- drivers/phy/renesas/phy-rcar-gen2.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/phy/renesas/phy-rcar-gen2.c b/drivers/phy/renesas/phy-rcar-gen2.c index 6c671254c625..5a272e25e051 100644 --- a/drivers/phy/renesas/phy-rcar-gen2.c +++ b/drivers/phy/renesas/phy-rcar-gen2.c @@ -405,7 +405,6 @@ static int rcar_gen2_phy_probe(struct platform_device *pdev) data->gen2_phy_ops); if (IS_ERR(phy->phy)) { dev_err(dev, "Failed to create PHY\n"); - of_node_put(np); return PTR_ERR(phy->phy); } phy_set_drvdata(phy->phy, phy); From 8b2683bc4cc18c581b7cd24f227cbe61abca7f4d Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Mon, 3 Aug 2026 23:15:17 +0800 Subject: [PATCH 37/76] phy: sunplus: fix error handling in sp_uphy_init() Fix the error paths of sp_uphy_init() to undo exactly what each stage did: return directly if clk_prepare_enable() fails, release only the clock if reset_control_deassert() fails, and jump to err_reset if update_disc_vol() fails so the clock and reset are not leaked. Fixes: 99d9ccd97385 ("phy: usb: Add USB2.0 phy driver for Sunplus SP7021") Signed-off-by: Felix Gu Reviewed-by: Philipp Zabel Link: https://patch.msgid.link/20260803-sunplus-usb3-v1-1-5a562524c869@gmail.com Signed-off-by: Vinod Koul --- drivers/phy/sunplus/phy-sunplus-usb2.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/phy/sunplus/phy-sunplus-usb2.c b/drivers/phy/sunplus/phy-sunplus-usb2.c index 637a5fbae6d9..0ad4c7160d17 100644 --- a/drivers/phy/sunplus/phy-sunplus-usb2.c +++ b/drivers/phy/sunplus/phy-sunplus-usb2.c @@ -116,11 +116,11 @@ static int sp_uphy_init(struct phy *phy) ret = clk_prepare_enable(usbphy->phy_clk); if (ret) - goto err_clk; + return ret; ret = reset_control_deassert(usbphy->rstc); if (ret) - goto err_reset; + goto err_clk; /* Default value modification */ writel(HIGH_MASK_BITS | 0x4002, usbphy->moon4_regs + UPHY_CONTROL0); @@ -129,7 +129,7 @@ static int sp_uphy_init(struct phy *phy) /* disconnect voltage */ ret = update_disc_vol(usbphy); if (ret < 0) - return ret; + goto err_reset; /* board uphy 0 internal register modification for tid certification */ val = readl(usbphy->phy_regs + CONFIG9); From 4fae43e33a7c10951fbdc6baf409d51bd66aaefb Mon Sep 17 00:00:00 2001 From: Gerald Loacker Date: Sat, 25 Jul 2026 18:56:03 +0200 Subject: [PATCH 38/76] phy: rockchip: phy-rockchip-inno-csidphy: fix rk1808 hsfreq table The rk1808 hsfreq table capped at 2499 Mbps, preventing a data rate of exactly 2500 Mbps. Extend the final entry to 2500 Mbps to support this rate. This is essential for RK3588 reusing this array and fully supporting rates up to 2500 Mbps. Fixes: bd1f775d6027 ("phy/rockchip: add Innosilicon-based CSI dphy") Reviewed-by: Michael Riesch Signed-off-by: Gerald Loacker Link: https://patch.msgid.link/20260725-feature-mipi-csi-dphy-4k60-v4-1-5b2c4626d31e@wolfvision.net Signed-off-by: Vinod Koul --- drivers/phy/rockchip/phy-rockchip-inno-csidphy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/phy/rockchip/phy-rockchip-inno-csidphy.c b/drivers/phy/rockchip/phy-rockchip-inno-csidphy.c index c79fb53d8ee5..5281f8dea0ad 100644 --- a/drivers/phy/rockchip/phy-rockchip-inno-csidphy.c +++ b/drivers/phy/rockchip/phy-rockchip-inno-csidphy.c @@ -170,7 +170,7 @@ static const struct hsfreq_range rk1808_mipidphy_hsfreq_ranges[] = { { 299, 0x06}, { 399, 0x08}, { 499, 0x0b}, { 599, 0x0e}, { 699, 0x10}, { 799, 0x12}, { 999, 0x16}, {1199, 0x1e}, {1399, 0x23}, {1599, 0x2d}, {1799, 0x32}, {1999, 0x37}, - {2199, 0x3c}, {2399, 0x41}, {2499, 0x46} + {2199, 0x3c}, {2399, 0x41}, {2500, 0x46} }; static const struct hsfreq_range rk3326_mipidphy_hsfreq_ranges[] = { From f82ff8609f582ca5105e45f18dfc3e949dfeaef1 Mon Sep 17 00:00:00 2001 From: Gerald Loacker Date: Sat, 25 Jul 2026 18:56:04 +0200 Subject: [PATCH 39/76] dt-bindings: phy: rockchip-inno-csi-dphy: add rockchip,clk-lane-phase property Add support for the optional rockchip,clk-lane-phase device tree property to allow board-specific tuning of the clock lane sampling phase for improved signal integrity across supported data rates. Acked-by: Rob Herring (Arm) Acked-by: Michael Riesch Signed-off-by: Gerald Loacker Link: https://patch.msgid.link/20260725-feature-mipi-csi-dphy-4k60-v4-2-5b2c4626d31e@wolfvision.net Signed-off-by: Vinod Koul --- .../bindings/phy/rockchip-inno-csi-dphy.yaml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Documentation/devicetree/bindings/phy/rockchip-inno-csi-dphy.yaml b/Documentation/devicetree/bindings/phy/rockchip-inno-csi-dphy.yaml index 03950b3cad08..913aa688c0ae 100644 --- a/Documentation/devicetree/bindings/phy/rockchip-inno-csi-dphy.yaml +++ b/Documentation/devicetree/bindings/phy/rockchip-inno-csi-dphy.yaml @@ -56,6 +56,16 @@ properties: description: Some additional phy settings are access through GRF regs. + rockchip,clk-lane-phase: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 7 + default: 3 + description: + Clock lane sampling phase selection (hardware tap index 0–7). Each step + corresponds to an approximately 40 ps delay as described in the hardware + specification. + required: - compatible - reg From 333c23c31696dbddd7dad28e82bd34e37a67d5ff Mon Sep 17 00:00:00 2001 From: Gerald Loacker Date: Sat, 25 Jul 2026 18:56:05 +0200 Subject: [PATCH 40/76] phy: rockchip: phy-rockchip-inno-csidphy: add clock lane phase tuning At high data rates like 4K60 (2500 Mbps), such as when using an LT6911GXD bridge chip on an RK3588 board, fixed default timing parameters can cause signal integrity issues and clock-data recovery failures. The driver currently lacks a mechanism to adjust the clock lane sampling phase to compensate for board-specific trace variations. Resolve this by parsing and applying the optional 'rockchip,clk-lane-phase' device tree property. This enables board-specific tuning of the clock lane sampling phase in ~40 ps steps (range 0-7) to optimize link stability. If the property is absent, the driver falls back to the hardware default. Signed-off-by: Gerald Loacker Reviewed-by: Michael Riesch Link: https://patch.msgid.link/20260725-feature-mipi-csi-dphy-4k60-v4-3-5b2c4626d31e@wolfvision.net Signed-off-by: Vinod Koul --- .../phy/rockchip/phy-rockchip-inno-csidphy.c | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/drivers/phy/rockchip/phy-rockchip-inno-csidphy.c b/drivers/phy/rockchip/phy-rockchip-inno-csidphy.c index 5281f8dea0ad..d989f1649b0e 100644 --- a/drivers/phy/rockchip/phy-rockchip-inno-csidphy.c +++ b/drivers/phy/rockchip/phy-rockchip-inno-csidphy.c @@ -69,6 +69,10 @@ #define RK1808_CSIDPHY_CLK_CALIB_EN 0x168 #define RK3568_CSIDPHY_CLK_CALIB_EN 0x168 +#define CSIDPHY_LANE_CLK_3_PHASE 0x38 +#define CSIDPHY_CLK_PHASE_MASK GENMASK(6, 4) +#define CSIDPHY_CLK_PHASE_DEFAULT 3 + #define RESETS_MAX 2 /* @@ -151,6 +155,7 @@ struct rockchip_inno_csidphy { const struct dphy_drv_data *drv_data; struct phy_configure_opts_mipi_dphy config; u8 hsfreq; + int clk_phase; }; static inline void write_grf_reg(struct rockchip_inno_csidphy *priv, @@ -304,6 +309,13 @@ static int rockchip_inno_csidphy_power_on(struct phy *phy) rockchip_inno_csidphy_ths_settle(priv, priv->hsfreq, CSIDPHY_LANE_THS_SETTLE(i)); + if (priv->clk_phase >= 0) { + val = readl(priv->phy_base + CSIDPHY_LANE_CLK_3_PHASE); + val &= ~CSIDPHY_CLK_PHASE_MASK; + val |= FIELD_PREP(CSIDPHY_CLK_PHASE_MASK, priv->clk_phase); + writel(val, priv->phy_base + CSIDPHY_LANE_CLK_3_PHASE); + } + write_grf_reg(priv, GRF_DPHY_CSIPHY_CLKLANE_EN, 0x1); write_grf_reg(priv, GRF_DPHY_CSIPHY_DATALANE_EN, GENMASK(priv->config.lanes - 1, 0)); @@ -449,6 +461,7 @@ static int rockchip_inno_csidphy_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct phy_provider *phy_provider; struct phy *phy; + u32 phase; int ret; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); @@ -464,6 +477,18 @@ static int rockchip_inno_csidphy_probe(struct platform_device *pdev) return -ENODEV; } + priv->clk_phase = -1; + if (device_property_read_u32(dev, "rockchip,clk-lane-phase", + &phase) == 0) { + if (phase > 7) { + dev_warn(dev, + "invalid rockchip,clk-lane-phase %u, using default %u\n", + phase, CSIDPHY_CLK_PHASE_DEFAULT); + phase = CSIDPHY_CLK_PHASE_DEFAULT; + } + priv->clk_phase = phase; + } + priv->grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,grf"); if (IS_ERR(priv->grf)) { From 58ca6ff31f6e9f5edf2879b5d7dc7485e92a4931 Mon Sep 17 00:00:00 2001 From: Jonas Karlman Date: Mon, 18 May 2026 18:07:20 +0000 Subject: [PATCH 41/76] phy: rockchip: inno-hdmi: Add configure() and validate() ops The commit 10ed34d6eaaf ("phy: Add HDMI configuration options") introduced a way for HDMI PHYs to be configured through the generic phy_configure() function. This driver derives the TMDS character rate from the pixel clock and the PHY bus width setting. However, no in-tree consumer of this PHY has ever called phy_set_bus_width() to change the TMDS character rate as only 8-bit RGB output is supported by the HDMI display driver. Add configure() and validate() ops to allow consumers to configure the TMDS character rate using phy_configure(). Fallback to the deprecated way of using the PHY bus width to configure the TMDS character rate. A typical call chain during DRM modeset on a RK3328 device: dw_hdmi_rockchip_encoder_atomic_check(): - inno_hdmi_phy_validate(): pixclock 148500000 tmdsclock 594000000 dw_hdmi_rockchip_encoder_atomic_mode_set(): - inno_hdmi_phy_configure(): pixclock 148500000 - inno_hdmi_phy_validate(): pixclock 148500000 tmdsclock 594000000 vop_crtc_atomic_enable(): - inno_hdmi_phy_rk3328_clk_set_rate(): rate 594000000 tmdsclk 594000000 inno_hdmi_phy_rk3328_clk_set_rate(): pixclock 594000000 tmdsclock 594000000 - inno_hdmi_phy_rk3328_clk_recalc_rate(): pixclock 594000000 vco 594000000 dw_hdmi_rockchip_encoder_enable(): - inno_hdmi_phy_power_on(): Inno HDMI PHY Power On - inno_hdmi_phy_rk3328_clk_set_rate(): rate 594000000 tmdsclk 594000000 Signed-off-by: Jonas Karlman Reviewed-by: Heiko Stuebner Tested-by: Heiko Stuebner #rk3328 Tested-by: Diederik de Haas # Rock64 Link: https://patch.msgid.link/20260518180722.2480799-2-jonas@kwiboo.se Signed-off-by: Vinod Koul --- drivers/phy/rockchip/phy-rockchip-inno-hdmi.c | 62 ++++++++++++++++++- 1 file changed, 61 insertions(+), 1 deletion(-) diff --git a/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c b/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c index 1483907413fa..1c43ea700f0e 100644 --- a/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c +++ b/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c @@ -245,6 +245,7 @@ struct inno_hdmi_phy { struct clk *phyclk; unsigned long pixclock; unsigned long tmdsclock; + unsigned long opts_tmds_char_rate; }; struct pre_pll_config { @@ -554,7 +555,12 @@ static inline void inno_update_bits(struct inno_hdmi_phy *inno, u8 reg, static unsigned long inno_hdmi_phy_get_tmdsclk(struct inno_hdmi_phy *inno, unsigned long rate) { - int bus_width = phy_get_bus_width(inno->phy); + int bus_width; + + if (inno->opts_tmds_char_rate) + return inno->opts_tmds_char_rate; + + bus_width = phy_get_bus_width(inno->phy); switch (bus_width) { case 4: @@ -602,6 +608,57 @@ static irqreturn_t inno_hdmi_phy_rk3328_irq(int irq, void *dev_id) return IRQ_HANDLED; } +/* + * phy_validate() is expected to be called from encoder atomic_check(), before + * the hdmiphy pixel clock is known. Without knowing the actual pixel clock, we + * cannot do full validation of the configuration. Instead, we do a simple check + * that the pre-pll table contains an entry for the requested TMDS char rate. + */ +static int inno_hdmi_phy_validate(struct phy *phy, enum phy_mode mode, + int submode, union phy_configure_opts *opts) +{ + const struct pre_pll_config *cfg = pre_pll_cfg_table; + unsigned long tmdsclock; + + if (!(mode == PHY_MODE_HDMI && submode == PHY_HDMI_MODE_TMDS)) + return -EINVAL; + + if (!opts) + return -EINVAL; + + if (!opts->hdmi.tmds_char_rate || opts->hdmi.tmds_char_rate > 594000000) + return -EINVAL; + + tmdsclock = opts->hdmi.tmds_char_rate; + for (; cfg->pixclock != 0; cfg++) + if (cfg->tmdsclock == tmdsclock) + return 0; + + return -EINVAL; +} + +/* + * phy_configure() is expected to be called from encoder atomic_set_mode(), + * before the hdmiphy pixel clock is known. Store the requested TMDS character + * rate, so that it can be used later in power_on() and/or set_rate() when the + * pixel clock is known. + */ +static int inno_hdmi_phy_configure(struct phy *phy, + union phy_configure_opts *opts) +{ + struct inno_hdmi_phy *inno = phy_get_drvdata(phy); + int ret; + + ret = inno_hdmi_phy_validate(phy, phy_get_mode(phy), + PHY_HDMI_MODE_TMDS, opts); + if (ret) + return ret; + + inno->opts_tmds_char_rate = opts->hdmi.tmds_char_rate; + + return 0; +} + static int inno_hdmi_phy_power_on(struct phy *phy) { struct inno_hdmi_phy *inno = phy_get_drvdata(phy); @@ -670,6 +727,8 @@ static const struct phy_ops inno_hdmi_phy_ops = { .owner = THIS_MODULE, .power_on = inno_hdmi_phy_power_on, .power_off = inno_hdmi_phy_power_off, + .configure = inno_hdmi_phy_configure, + .validate = inno_hdmi_phy_validate, }; static const @@ -1392,6 +1451,7 @@ static int inno_hdmi_phy_probe(struct platform_device *pdev) } phy_set_drvdata(inno->phy, inno); + phy_set_mode_ext(inno->phy, PHY_MODE_HDMI, PHY_HDMI_MODE_TMDS); phy_set_bus_width(inno->phy, 8); if (inno->plat_data->ops->init) { From 4ba747d3d69b12d1221cea23eec9f468ff221cd6 Mon Sep 17 00:00:00 2001 From: Jonas Karlman Date: Mon, 18 May 2026 18:07:21 +0000 Subject: [PATCH 42/76] phy: rockchip: inno-hdmi: Remove deprecated way to configure TMDS rate The TMDS character rate of this PHY is configured using PHY bus width in downstream vendor kernel and out-of-tree patches, however no in-tree consumer of this PHY has ever called phy_set_bus_width() to change the TMDS character rate as currently only 8-bit RGB output is supported by the HDMI display driver. The series "Split Generic PHY consumer and provider" clarifies that phy_set_bus_width() is intended as a provider-only function. Remove the deprecated unused fallback way to configure TMDS character rate now that this HDMI PHY support using phy_configure() to configure the TMDS character rate. Signed-off-by: Jonas Karlman Reviewed-by: Heiko Stuebner Tested-by: Heiko Stuebner #rk3328 Tested-by: Diederik de Haas # Rock64 Link: https://patch.msgid.link/20260518180722.2480799-3-jonas@kwiboo.se Signed-off-by: Vinod Koul --- drivers/phy/rockchip/phy-rockchip-inno-hdmi.c | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c b/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c index 1c43ea700f0e..c3d257a0f4a6 100644 --- a/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c +++ b/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c @@ -555,24 +555,10 @@ static inline void inno_update_bits(struct inno_hdmi_phy *inno, u8 reg, static unsigned long inno_hdmi_phy_get_tmdsclk(struct inno_hdmi_phy *inno, unsigned long rate) { - int bus_width; - if (inno->opts_tmds_char_rate) return inno->opts_tmds_char_rate; - bus_width = phy_get_bus_width(inno->phy); - - switch (bus_width) { - case 4: - case 5: - case 6: - case 10: - case 12: - case 16: - return (u64)rate * bus_width / 8; - default: - return rate; - } + return rate; } static irqreturn_t inno_hdmi_phy_rk3328_hardirq(int irq, void *dev_id) @@ -1452,7 +1438,6 @@ static int inno_hdmi_phy_probe(struct platform_device *pdev) phy_set_drvdata(inno->phy, inno); phy_set_mode_ext(inno->phy, PHY_MODE_HDMI, PHY_HDMI_MODE_TMDS); - phy_set_bus_width(inno->phy, 8); if (inno->plat_data->ops->init) { ret = inno->plat_data->ops->init(inno); From c3b0bb15ef001703b00d79bc4928e7421e24280d Mon Sep 17 00:00:00 2001 From: Haibo Chen Date: Thu, 30 Jul 2026 12:33:26 +0800 Subject: [PATCH 43/76] phy: phy-can-transceiver: default silent GPIO to high during probe The silent pin of the CAN transceiver is active high, asserting it puts the transceiver into silent (listen-only) mode where the transmitter is disabled. At probe time, and before the PHY is powered on, the transceiver should default to silent mode. This is the correct and lower-power state: the transceiver should not actively drive the CAN bus until the PHY is explicitly powered on. Requesting the silent GPIO as GPIOD_OUT_LOW leaves the transceiver in normal mode by default, which is both incorrect and wastes power. Request the silent GPIO as GPIOD_OUT_HIGH so the transceiver starts in silent mode, and let the power_on/power_off callbacks manage the mode afterwards. Signed-off-by: Haibo Chen Reviewed-by: Marc Kleine-Budde Reviewed-by: Frank Li Link: https://patch.msgid.link/20260730-can-share-silent-v1-1-63fd603f943d@nxp.com Signed-off-by: Vinod Koul --- drivers/phy/phy-can-transceiver.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/phy/phy-can-transceiver.c b/drivers/phy/phy-can-transceiver.c index 75dc49e75ca0..9aa30662105f 100644 --- a/drivers/phy/phy-can-transceiver.c +++ b/drivers/phy/phy-can-transceiver.c @@ -198,7 +198,7 @@ static int can_transceiver_phy_probe(struct platform_device *pdev) if (drvdata->flags & CAN_TRANSCEIVER_SILENT_PRESENT) { silent_gpio = devm_gpiod_get_index_optional(dev, "silent", i, - GPIOD_OUT_LOW); + GPIOD_OUT_HIGH); if (IS_ERR(silent_gpio)) return PTR_ERR(silent_gpio); can_transceiver_phy->silent_gpio = silent_gpio; From bb82e4327a39a17a9338413554b11b328ced0ffd Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 29 Jul 2026 16:28:30 -0300 Subject: [PATCH 44/76] phy: freescale: fsl-samsung-hdmi: Balance runtime PM operations fsl_samsung_hdmi_phy_probe() enables runtime PM, but the remove callback does not disable it. Unbinding and rebinding the device therefore results in the following warning on reprobe: Unbalanced pm_runtime_enable! Disable runtime PM after removing the clock provider. Also undo the runtime PM operations when phy_clk_register() fails. Otherwise, a failed probe leaves runtime PM enabled and the usage counter incremented. Signed-off-by: Fabio Estevam Link: https://patch.msgid.link/20260729192830.487150-1-festevam@gmail.com Signed-off-by: Vinod Koul --- drivers/phy/freescale/phy-fsl-samsung-hdmi.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/phy/freescale/phy-fsl-samsung-hdmi.c b/drivers/phy/freescale/phy-fsl-samsung-hdmi.c index d010fec15671..0f25d81de61b 100644 --- a/drivers/phy/freescale/phy-fsl-samsung-hdmi.c +++ b/drivers/phy/freescale/phy-fsl-samsung-hdmi.c @@ -679,12 +679,15 @@ static int fsl_samsung_hdmi_phy_probe(struct platform_device *pdev) return 0; register_clk_failed: + pm_runtime_disable(phy->dev); + pm_runtime_put_noidle(phy->dev); return ret; } static void fsl_samsung_hdmi_phy_remove(struct platform_device *pdev) { of_clk_del_provider(pdev->dev.of_node); + pm_runtime_disable(&pdev->dev); } static int __maybe_unused fsl_samsung_hdmi_phy_suspend(struct device *dev) From 24d886d9344c7973e202a7f275ff8280b742446c Mon Sep 17 00:00:00 2001 From: Palash Kambar Date: Thu, 6 Aug 2026 21:43:00 +0530 Subject: [PATCH 45/76] dt-bindings: phy: qcom,sc8280xp-qmp-ufs-phy: Add Hawi UFS PHY compatible Document QMP UFS PHY compatible for Hawi SoC. Reviewed-by: Manivannan Sadhasivam Reviewed-by: Krzysztof Kozlowski Signed-off-by: Palash Kambar Link: https://patch.msgid.link/20260806161301.1010876-2-palash.kambar@oss.qualcomm.com Signed-off-by: Vinod Koul --- .../devicetree/bindings/phy/qcom,sc8280xp-qmp-ufs-phy.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-ufs-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-ufs-phy.yaml index b2c5c9a375a3..4efe40c0dc97 100644 --- a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-ufs-phy.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-ufs-phy.yaml @@ -38,6 +38,7 @@ properties: - qcom,kaanapali-qmp-ufs-phy - const: qcom,sm8750-qmp-ufs-phy - enum: + - qcom,hawi-qmp-ufs-phy - qcom,milos-qmp-ufs-phy - qcom,msm8996-qmp-ufs-phy - qcom,msm8998-qmp-ufs-phy @@ -108,6 +109,7 @@ allOf: compatible: contains: enum: + - qcom,hawi-qmp-ufs-phy - qcom,milos-qmp-ufs-phy - qcom,msm8998-qmp-ufs-phy - qcom,sa8775p-qmp-ufs-phy From 2f7b4bc47b1e22c3e510ff6e25985edbe9cd9f97 Mon Sep 17 00:00:00 2001 From: Palash Kambar Date: Thu, 6 Aug 2026 21:43:01 +0530 Subject: [PATCH 46/76] phy: qcom-qmp-ufs: Add UFS PHY support on Hawi Add the init sequence tables and config for the UFS QMP phy found in the Hawi SoC. Reviewed-by: Abel Vesa Reviewed-by: Manivannan Sadhasivam Reviewed-by: Dmitry Baryshkov Signed-off-by: Palash Kambar Link: https://patch.msgid.link/20260806161301.1010876-3-palash.kambar@oss.qualcomm.com Signed-off-by: Vinod Koul --- .../phy/qualcomm/phy-qcom-qmp-pcs-ufs-v7.h | 24 +++ .../qualcomm/phy-qcom-qmp-qserdes-com-v8.h | 13 +- .../phy-qcom-qmp-qserdes-txrx-ufs-v8.h | 37 +++++ drivers/phy/qualcomm/phy-qcom-qmp-ufs.c | 139 ++++++++++++++++++ 4 files changed, 212 insertions(+), 1 deletion(-) create mode 100644 drivers/phy/qualcomm/phy-qcom-qmp-pcs-ufs-v7.h create mode 100644 drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-ufs-v8.h diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcs-ufs-v7.h b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-ufs-v7.h new file mode 100644 index 000000000000..e0e73ac88e8a --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-ufs-v7.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2026, The Linux Foundation. All rights reserved. + */ + +#ifndef QCOM_PHY_QMP_PCS_UFS_V7_H_ +#define QCOM_PHY_QMP_PCS_UFS_V7_H_ + +/* Only for QMP V7 PHY - UFS PCS registers */ +#define QPHY_V7_PCS_UFS_PHY_START 0x000 +#define QPHY_V7_PCS_UFS_POWER_DOWN_CONTROL 0x004 +#define QPHY_V7_PCS_UFS_SW_RESET 0x008 +#define QPHY_V7_PCS_UFS_PCS_CTRL1 0x01c +#define QPHY_V7_PCS_UFS_PLL_CNTL 0x028 +#define QPHY_V7_PCS_UFS_TX_LARGE_AMP_DRV_LVL 0x02c +#define QPHY_V7_PCS_UFS_TX_HSGEAR_CAPABILITY 0x060 +#define QPHY_V7_PCS_UFS_RX_HSGEAR_CAPABILITY 0x094 +#define QPHY_V7_PCS_UFS_LINECFG_DISABLE 0x140 +#define QPHY_V7_PCS_UFS_RX_SIGDET_CTRL2 0x150 +#define QPHY_V7_PCS_UFS_READY_STATUS 0x16c +#define QPHY_V7_PCS_UFS_TX_MID_TERM_CTRL1 0x1b8 +#define QPHY_V7_PCS_UFS_MULTI_LANE_CTRL1 0x1c0 + +#endif diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com-v8.h b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com-v8.h index d8ac4c4a2c31..a3432fa6e38e 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com-v8.h +++ b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com-v8.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2026, The Linux Foundation. All rights reserved. */ #ifndef QCOM_PHY_QMP_QSERDES_COM_V8_H_ @@ -71,5 +71,16 @@ #define QSERDES_V8_COM_ADDITIONAL_MISC 0x1b4 #define QSERDES_V8_COM_CMN_STATUS 0x2c8 #define QSERDES_V8_COM_C_READY_STATUS 0x2f0 +#define QSERDES_V8_COM_PLL_IVCO_MODE1 0xf8 +#define QSERDES_V8_COM_CMN_IETRIM 0xfc +#define QSERDES_V8_COM_CMN_IPTRIM 0x100 +#define QSERDES_V8_COM_VCO_TUNE_CTRL 0x13c +#define QSERDES_V8_COM_ADAPTIVE_ANALOG_CONFIG 0x268 +#define QSERDES_V8_COM_CP_CTRL_ADAPTIVE_MODE0 0x26c +#define QSERDES_V8_COM_PLL_RCCTRL_ADAPTIVE_MODE0 0x270 +#define QSERDES_V8_COM_PLL_CCTRL_ADAPTIVE_MODE0 0x274 +#define QSERDES_V8_COM_CP_CTRL_ADAPTIVE_MODE1 0x278 +#define QSERDES_V8_COM_PLL_RCCTRL_ADAPTIVE_MODE1 0x27c +#define QSERDES_V8_COM_PLL_CCTRL_ADAPTIVE_MODE1 0x280 #endif diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-ufs-v8.h b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-ufs-v8.h new file mode 100644 index 000000000000..1adc6dbc68a0 --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-ufs-v8.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2026, The Linux Foundation. All rights reserved. + */ + +#ifndef QCOM_PHY_QMP_QSERDES_TXRX_UFS_V8_H_ +#define QCOM_PHY_QMP_QSERDES_TXRX_UFS_V8_H_ + +#define QSERDES_UFS_V8_TX_RES_CODE_LANE_OFFSET_TX (0x34) +#define QSERDES_UFS_V8_TX_RES_CODE_LANE_OFFSET_RX (0x38) +#define QSERDES_UFS_V8_TX_LANE_MODE_1 (0x80) +#define QSERDES_UFS_V8_RX_UCDR_FO_GAIN_RATE2 (0x1bc) +#define QSERDES_UFS_V8_RX_UCDR_FO_GAIN_RATE4 (0x1c4) +#define QSERDES_UFS_V8_RX_UCDR_SO_GAIN_RATE4 (0x1dc) +#define QSERDES_UFS_V8_RX_EQ_OFFSET_ADAPTOR_CNTRL1 (0x2c8) +#define QSERDES_UFS_V8_RX_UCDR_PI_CONTROLS (0x1e4) +#define QSERDES_UFS_V8_RX_OFFSET_ADAPTOR_CNTRL3 (0x2d0) +#define QSERDES_UFS_V8_RX_UCDR_FASTLOCK_COUNT_HIGH_RATE4 (0x120) +#define QSERDES_UFS_V8_RX_UCDR_FASTLOCK_FO_GAIN_RATE4 (0xd4) +#define QSERDES_UFS_V8_RX_UCDR_FASTLOCK_SO_GAIN_RATE4 (0xec) +#define QSERDES_UFS_V8_RX_VGA_CAL_MAN_VAL (0x288) +#define QSERDES_UFS_V8_RX_EQU_ADAPTOR_CNTRL4 (0x2b0) +#define QSERDES_UFS_V8_RX_MODE_RATE_0_1_B4 (0x324) +#define QSERDES_UFS_V8_RX_MODE_RATE4_SA_B7 (0x3b4) +#define QSERDES_UFS_V8_RX_MODE_RATE4_SA_B9 (0x3bc) +#define QSERDES_UFS_V8_RX_MODE_RATE4_SB_B7 (0x3e0) +#define QSERDES_UFS_V8_RX_MODE_RATE4_SB_B9 (0x3e8) +#define QSERDES_UFS_V8_RX_MODE_RATE5_SA_B7 (0x40c) +#define QSERDES_UFS_V8_RX_MODE_RATE5_SA_B9 (0x414) +#define QSERDES_UFS_V8_RX_MODE_RATE5_SB_B7 (0x438) +#define QSERDES_UFS_V8_RX_MODE_RATE5_SB_B9 (0x440) +#define QSERDES_UFS_V8_RX_UCDR_SO_SATURATION (0xf4) +#define QSERDES_UFS_V8_RX_TERM_BW_CTRL0 (0x1ac) +#define QSERDES_UFS_V8_RX_DLL0_FTUNE_CTRL (0x498) +#define QSERDES_UFS_V8_RX_SIGDET_CAL_TRIM (0x4d0) + +#endif diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c b/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c index 0f4ad24aa405..d4aca22c181e 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c @@ -29,9 +29,11 @@ #include "phy-qcom-qmp-pcs-ufs-v4.h" #include "phy-qcom-qmp-pcs-ufs-v5.h" #include "phy-qcom-qmp-pcs-ufs-v6.h" +#include "phy-qcom-qmp-pcs-ufs-v7.h" #include "phy-qcom-qmp-qserdes-txrx-ufs-v6.h" #include "phy-qcom-qmp-qserdes-txrx-ufs-v7.h" +#include "phy-qcom-qmp-qserdes-txrx-ufs-v8.h" /* QPHY_PCS_READY_STATUS bit */ #define PCS_READY BIT(0) @@ -84,6 +86,13 @@ static const unsigned int ufsphy_v6_regs_layout[QPHY_LAYOUT_SIZE] = { [QPHY_PCS_POWER_DOWN_CONTROL] = QPHY_V6_PCS_UFS_POWER_DOWN_CONTROL, }; +static const unsigned int ufsphy_v7_regs_layout[QPHY_LAYOUT_SIZE] = { + [QPHY_START_CTRL] = QPHY_V7_PCS_UFS_PHY_START, + [QPHY_PCS_READY_STATUS] = QPHY_V7_PCS_UFS_READY_STATUS, + [QPHY_SW_RESET] = QPHY_V7_PCS_UFS_SW_RESET, + [QPHY_PCS_POWER_DOWN_CONTROL] = QPHY_V7_PCS_UFS_POWER_DOWN_CONTROL, +}; + static const struct qmp_phy_init_tbl milos_ufsphy_serdes[] = { QMP_PHY_INIT_CFG(QSERDES_V6_COM_SYSCLK_EN_SEL, 0xd9), QMP_PHY_INIT_CFG(QSERDES_V6_COM_CMN_CONFIG_1, 0x16), @@ -1307,6 +1316,11 @@ static const struct regulator_bulk_data sm8750_ufsphy_vreg_l[] = { { .supply = "vdda-pll", .init_load_uA = 18300 }, }; +static const struct regulator_bulk_data hawi_ufsphy_vreg_l[] = { + { .supply = "vdda-phy", .init_load_uA = 324000 }, + { .supply = "vdda-pll", .init_load_uA = 27000 }, +}; + static const struct qmp_ufs_offsets qmp_ufs_offsets = { .serdes = 0, .pcs = 0xc00, @@ -1325,6 +1339,15 @@ static const struct qmp_ufs_offsets qmp_ufs_offsets_v6 = { .rx2 = 0x1a00, }; +static const struct qmp_ufs_offsets qmp_ufs_offsets_v7 = { + .serdes = 0, + .pcs = 0x0400, + .tx = 0x2000, + .rx = 0x2000, + .tx2 = 0x3000, + .rx2 = 0x3000, +}; + static const struct qmp_phy_cfg milos_ufsphy_cfg = { .lanes = 2, @@ -1845,6 +1868,119 @@ static const struct qmp_phy_cfg sm8750_ufsphy_cfg = { }; +static const struct qmp_phy_init_tbl hawi_ufsphy_serdes[] = { + QMP_PHY_INIT_CFG(QSERDES_V8_COM_SYSCLK_EN_SEL, 0xd9), + QMP_PHY_INIT_CFG(QSERDES_V8_COM_CMN_CONFIG_1, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V8_COM_HSCLK_SEL_1, 0x11), + QMP_PHY_INIT_CFG(QSERDES_V8_COM_HSCLK_HS_SWITCH_SEL_1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V8_COM_LOCK_CMP_EN, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V8_COM_LOCK_CMP_CFG, 0x60), + QMP_PHY_INIT_CFG(QSERDES_V8_COM_PLL_IVCO, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V8_COM_PLL_IVCO_MODE1, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V8_COM_CMN_IETRIM, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V8_COM_CMN_IPTRIM, 0x20), + QMP_PHY_INIT_CFG(QSERDES_V8_COM_VCO_TUNE_MAP, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V8_COM_VCO_TUNE_CTRL, 0x40), + QMP_PHY_INIT_CFG(QSERDES_V8_COM_ADAPTIVE_ANALOG_CONFIG, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V8_COM_DEC_START_MODE0, 0x41), + QMP_PHY_INIT_CFG(QSERDES_V8_COM_CP_CTRL_MODE0, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V8_COM_PLL_RCTRL_MODE0, 0x18), + QMP_PHY_INIT_CFG(QSERDES_V8_COM_PLL_CCTRL_MODE0, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V8_COM_CP_CTRL_ADAPTIVE_MODE0, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V8_COM_PLL_RCCTRL_ADAPTIVE_MODE0, 0x18), + QMP_PHY_INIT_CFG(QSERDES_V8_COM_PLL_CCTRL_ADAPTIVE_MODE0, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V8_COM_LOCK_CMP1_MODE0, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V8_COM_LOCK_CMP2_MODE0, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V8_COM_BIN_VCOCAL_CMP_CODE1_MODE0, 0x92), + QMP_PHY_INIT_CFG(QSERDES_V8_COM_BIN_VCOCAL_CMP_CODE2_MODE0, 0x1e), + QMP_PHY_INIT_CFG(QSERDES_V8_COM_DEC_START_MODE1, 0x4c), + QMP_PHY_INIT_CFG(QSERDES_V8_COM_CP_CTRL_MODE1, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V8_COM_PLL_RCTRL_MODE1, 0x18), + QMP_PHY_INIT_CFG(QSERDES_V8_COM_PLL_CCTRL_MODE1, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V8_COM_CP_CTRL_ADAPTIVE_MODE1, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V8_COM_PLL_RCCTRL_ADAPTIVE_MODE1, 0x18), + QMP_PHY_INIT_CFG(QSERDES_V8_COM_PLL_CCTRL_ADAPTIVE_MODE1, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V8_COM_LOCK_CMP1_MODE1, 0x99), + QMP_PHY_INIT_CFG(QSERDES_V8_COM_LOCK_CMP2_MODE1, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V8_COM_BIN_VCOCAL_CMP_CODE1_MODE1, 0xbe), + QMP_PHY_INIT_CFG(QSERDES_V8_COM_BIN_VCOCAL_CMP_CODE2_MODE1, 0x23), +}; + +static const struct qmp_phy_init_tbl hawi_ufsphy_tx[] = { + QMP_PHY_INIT_CFG(QSERDES_UFS_V8_TX_LANE_MODE_1, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_UFS_V8_TX_RES_CODE_LANE_OFFSET_TX, 0x07), + QMP_PHY_INIT_CFG(QSERDES_UFS_V8_TX_RES_CODE_LANE_OFFSET_RX, 0x17), +}; + +static const struct qmp_phy_init_tbl hawi_ufsphy_rx[] = { + QMP_PHY_INIT_CFG(QSERDES_UFS_V8_RX_UCDR_FO_GAIN_RATE2, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_UFS_V8_RX_UCDR_FO_GAIN_RATE4, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_UFS_V8_RX_UCDR_SO_GAIN_RATE4, 0x04), + QMP_PHY_INIT_CFG(QSERDES_UFS_V8_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x14), + QMP_PHY_INIT_CFG(QSERDES_UFS_V8_RX_UCDR_PI_CONTROLS, 0x07), + QMP_PHY_INIT_CFG(QSERDES_UFS_V8_RX_OFFSET_ADAPTOR_CNTRL3, 0x0e), + QMP_PHY_INIT_CFG(QSERDES_UFS_V8_RX_UCDR_FASTLOCK_COUNT_HIGH_RATE4, 0x02), + QMP_PHY_INIT_CFG(QSERDES_UFS_V8_RX_UCDR_FASTLOCK_FO_GAIN_RATE4, 0x1c), + QMP_PHY_INIT_CFG(QSERDES_UFS_V8_RX_UCDR_FASTLOCK_SO_GAIN_RATE4, 0x06), + QMP_PHY_INIT_CFG(QSERDES_UFS_V8_RX_VGA_CAL_MAN_VAL, 0x8e), + QMP_PHY_INIT_CFG(QSERDES_UFS_V8_RX_EQU_ADAPTOR_CNTRL4, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_UFS_V8_RX_MODE_RATE_0_1_B4, 0xb8), + QMP_PHY_INIT_CFG(QSERDES_UFS_V8_RX_MODE_RATE4_SA_B7, 0x66), + QMP_PHY_INIT_CFG(QSERDES_UFS_V8_RX_MODE_RATE4_SA_B9, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_UFS_V8_RX_MODE_RATE4_SB_B7, 0x66), + QMP_PHY_INIT_CFG(QSERDES_UFS_V8_RX_MODE_RATE4_SB_B9, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_UFS_V8_RX_MODE_RATE5_SA_B7, 0x66), + QMP_PHY_INIT_CFG(QSERDES_UFS_V8_RX_MODE_RATE5_SA_B9, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_UFS_V8_RX_MODE_RATE5_SB_B7, 0x66), + QMP_PHY_INIT_CFG(QSERDES_UFS_V8_RX_MODE_RATE5_SB_B9, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_UFS_V8_RX_UCDR_SO_SATURATION, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_UFS_V8_RX_TERM_BW_CTRL0, 0xfa), + QMP_PHY_INIT_CFG(QSERDES_UFS_V8_RX_DLL0_FTUNE_CTRL, 0x30), + QMP_PHY_INIT_CFG(QSERDES_UFS_V8_RX_SIGDET_CAL_TRIM, 0x77), +}; + +static const struct qmp_phy_init_tbl hawi_ufsphy_pcs[] = { + QMP_PHY_INIT_CFG(QPHY_V7_PCS_UFS_TX_MID_TERM_CTRL1, 0x43), + QMP_PHY_INIT_CFG(QPHY_V7_PCS_UFS_PCS_CTRL1, 0x42), + QMP_PHY_INIT_CFG(QPHY_V7_PCS_UFS_TX_LARGE_AMP_DRV_LVL, 0x0f), + QMP_PHY_INIT_CFG(QPHY_V7_PCS_UFS_RX_SIGDET_CTRL2, 0x68), + QMP_PHY_INIT_CFG(QPHY_V7_PCS_UFS_MULTI_LANE_CTRL1, 0x02), +}; + +static const struct qmp_phy_init_tbl hawi_ufsphy_g5_pcs[] = { + QMP_PHY_INIT_CFG(QPHY_V7_PCS_UFS_PLL_CNTL, 0x3b), + QMP_PHY_INIT_CFG(QPHY_V7_PCS_UFS_TX_HSGEAR_CAPABILITY, 0x05), + QMP_PHY_INIT_CFG(QPHY_V7_PCS_UFS_RX_HSGEAR_CAPABILITY, 0x05), +}; + +static const struct qmp_phy_cfg hawi_ufsphy_cfg = { + .lanes = 2, + + .offsets = &qmp_ufs_offsets_v7, + .max_supported_gear = UFS_HS_G5, + + .tbls = { + .serdes = hawi_ufsphy_serdes, + .serdes_num = ARRAY_SIZE(hawi_ufsphy_serdes), + .tx = hawi_ufsphy_tx, + .tx_num = ARRAY_SIZE(hawi_ufsphy_tx), + .rx = hawi_ufsphy_rx, + .rx_num = ARRAY_SIZE(hawi_ufsphy_rx), + .pcs = hawi_ufsphy_pcs, + .pcs_num = ARRAY_SIZE(hawi_ufsphy_pcs), + }, + + .tbls_hs_overlay[0] = { + .pcs = hawi_ufsphy_g5_pcs, + .pcs_num = ARRAY_SIZE(hawi_ufsphy_g5_pcs), + .max_gear = UFS_HS_G5, + }, + + .vreg_list = hawi_ufsphy_vreg_l, + .num_vregs = ARRAY_SIZE(hawi_ufsphy_vreg_l), + .regs = ufsphy_v7_regs_layout, +}; + static void qmp_ufs_serdes_init(struct qmp_ufs *qmp, const struct qmp_phy_cfg_tbls *tbls) { void __iomem *serdes = qmp->serdes; @@ -2259,6 +2395,9 @@ static int qmp_ufs_probe(struct platform_device *pdev) static const struct of_device_id qmp_ufs_of_match_table[] = { { + .compatible = "qcom,hawi-qmp-ufs-phy", + .data = &hawi_ufsphy_cfg, + }, { .compatible = "qcom,milos-qmp-ufs-phy", .data = &milos_ufsphy_cfg, }, { From 4b04c8779a50ab27afb0b471ff1ced3061e440cb Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Wed, 22 Jul 2026 13:34:42 +0300 Subject: [PATCH 47/76] phy: qcom-qmp: qserdes-com: drop duplicate v8 DP headers Commit 212cdedcac11 ("phy: qcom-qmp: qserdes-com: Add v8 DP-specific qserdes register offsets") and commit d10736db98d2 ("phy: qualcomm: qmp-combo: Add DP offsets and settings for Glymur platforms") added identical header files for DisplayPort-specific registers on V8 PHYs, having different names. Get rid of one of the copies, reverting commit d10736db98d2 ("phy: qualcomm: qmp-combo: Add DP offsets and settings for Glymur platforms") Signed-off-by: Dmitry Baryshkov Reviewed-by: Konrad Dybcio Reviewed-by: Abel Vesa Link: https://patch.msgid.link/20260722-qmp-drop-duplicate-v8-v1-1-795eb7de0322@oss.qualcomm.com Signed-off-by: Vinod Koul --- .../qualcomm/phy-qcom-qmp-dp-qserdes-com-v8.h | 52 ------------------- 1 file changed, 52 deletions(-) delete mode 100644 drivers/phy/qualcomm/phy-qcom-qmp-dp-qserdes-com-v8.h diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-dp-qserdes-com-v8.h b/drivers/phy/qualcomm/phy-qcom-qmp-dp-qserdes-com-v8.h deleted file mode 100644 index 2bef1eecdc56..000000000000 --- a/drivers/phy/qualcomm/phy-qcom-qmp-dp-qserdes-com-v8.h +++ /dev/null @@ -1,52 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Copyright (C) 2025 Linaro Ltd. - */ - -#ifndef QCOM_PHY_QMP_DP_QSERDES_COM_V8_H_ -#define QCOM_PHY_QMP_DP_QSERDES_COM_V8_H_ - -/* Only for DP QMP V8 PHY - QSERDES COM registers */ -#define DP_QSERDES_V8_COM_HSCLK_SEL_1 0x03c -#define DP_QSERDES_V8_COM_BIN_VCOCAL_CMP_CODE1_MODE0 0x058 -#define DP_QSERDES_V8_COM_BIN_VCOCAL_CMP_CODE2_MODE0 0x05c -#define DP_QSERDES_V8_COM_SSC_STEP_SIZE1_MODE0 0x060 -#define DP_QSERDES_V8_COM_SSC_STEP_SIZE2_MODE0 0x064 -#define DP_QSERDES_V8_COM_CP_CTRL_MODE0 0x070 -#define DP_QSERDES_V8_COM_PLL_RCTRL_MODE0 0x074 -#define DP_QSERDES_V8_COM_PLL_CCTRL_MODE0 0x078 -#define DP_QSERDES_V8_COM_CORECLK_DIV_MODE0 0x07c -#define DP_QSERDES_V8_COM_LOCK_CMP1_MODE0 0x080 -#define DP_QSERDES_V8_COM_LOCK_CMP2_MODE0 0x084 -#define DP_QSERDES_V8_COM_DEC_START_MODE0 0x088 -#define DP_QSERDES_V8_COM_DIV_FRAC_START1_MODE0 0x090 -#define DP_QSERDES_V8_COM_DIV_FRAC_START2_MODE0 0x094 -#define DP_QSERDES_V8_COM_DIV_FRAC_START3_MODE0 0x098 -#define DP_QSERDES_V8_COM_INTEGLOOP_GAIN0_MODE0 0x0a0 -#define DP_QSERDES_V8_COM_VCO_TUNE1_MODE0 0x0a8 -#define DP_QSERDES_V8_COM_INTEGLOOP_GAIN1_MODE0 0x0a4 -#define DP_QSERDES_V8_COM_VCO_TUNE2_MODE0 0x0ac -#define DP_QSERDES_V8_COM_BG_TIMER 0x0bc -#define DP_QSERDES_V8_COM_SSC_EN_CENTER 0x0c0 -#define DP_QSERDES_V8_COM_SSC_ADJ_PER1 0x0c4 -#define DP_QSERDES_V8_COM_SSC_PER1 0x0cc -#define DP_QSERDES_V8_COM_SSC_PER2 0x0d0 -#define DP_QSERDES_V8_COM_BIAS_EN_CLKBUFLR_EN 0x0dc -#define DP_QSERDES_V8_COM_CLK_ENABLE1 0x0e0 -#define DP_QSERDES_V8_COM_SYS_CLK_CTRL 0x0e4 -#define DP_QSERDES_V8_COM_SYSCLK_BUF_ENABLE 0x0e8 -#define DP_QSERDES_V8_COM_PLL_IVCO 0x0f4 -#define DP_QSERDES_V8_COM_SYSCLK_EN_SEL 0x110 -#define DP_QSERDES_V8_COM_RESETSM_CNTRL 0x118 -#define DP_QSERDES_V8_COM_LOCK_CMP_EN 0x120 -#define DP_QSERDES_V8_COM_VCO_TUNE_CTRL 0x13c -#define DP_QSERDES_V8_COM_VCO_TUNE_MAP 0x140 -#define DP_QSERDES_V8_COM_CLK_SELECT 0x164 -#define DP_QSERDES_V8_COM_CORE_CLK_EN 0x170 -#define DP_QSERDES_V8_COM_CMN_CONFIG_1 0x174 -#define DP_QSERDES_V8_COM_SVS_MODE_CLK_SEL 0x180 -#define DP_QSERDES_V8_COM_CLK_FWD_CONFIG_1 0x2f4 -#define DP_QSERDES_V8_COM_CMN_STATUS 0x314 -#define DP_QSERDES_V8_COM_C_READY_STATUS 0x33c - -#endif From 3719850f1b70dd3f01173be830cb0a1d2bc03a4e Mon Sep 17 00:00:00 2001 From: Mohd Ayaan Anwar Date: Tue, 4 Aug 2026 00:57:57 +0530 Subject: [PATCH 48/76] dt-bindings: phy: qcom,sa8775p-dwmac-sgmii-phy: add named voltage rails and deprecate phy-supply The Qualcomm SGMII SerDes PHY has two distinct voltage supply rails, vdda-0p9 and vdda-1p2. The binding incorrectly described only a single generic supply via 'phy-supply'. Deprecate 'phy-supply' and instead add two named supply properties. The 0.9V rail draws a peak current of 46 mA and the 1.2V rail draws 15 mA. Fixes: 97b795125704 ("dt-bindings: phy: describe the Qualcomm SGMII PHY") Signed-off-by: Mohd Ayaan Anwar Reviewed-by: Krzysztof Kozlowski Reviewed-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260804-b4-sgmiieth_serdes_regulator-v2-1-c4bc688177dd@oss.qualcomm.com Signed-off-by: Vinod Koul --- .../bindings/phy/qcom,sa8775p-dwmac-sgmii-phy.yaml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Documentation/devicetree/bindings/phy/qcom,sa8775p-dwmac-sgmii-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,sa8775p-dwmac-sgmii-phy.yaml index 90fc8c039219..e573abcb937d 100644 --- a/Documentation/devicetree/bindings/phy/qcom,sa8775p-dwmac-sgmii-phy.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,sa8775p-dwmac-sgmii-phy.yaml @@ -35,6 +35,17 @@ properties: phy-supply: description: Phandle to a regulator that provides power to the PHY. + deprecated: true + + vdda-0p9-supply: + description: + Phandle to a 0.9V regulator supply to the PHY, which draws a peak + current of 46 mA. + + vdda-1p2-supply: + description: + Phandle to a 1.2V regulator supply to the PHY, which draws a peak + current of 15 mA. "#phy-cells": const: 0 From 4f81684a1d1018d7d0e5579f08d95e7701279358 Mon Sep 17 00:00:00 2001 From: Mohd Ayaan Anwar Date: Tue, 4 Aug 2026 00:57:58 +0530 Subject: [PATCH 49/76] phy: qcom: sgmii-eth: vote for both voltage rails with correct current loads The SerDes PHY has two voltage supply rails, vdda-0p9 and vdda-1p2, that must both be enabled for calibration to succeed. Without them: qcom-dwmac-sgmii-phy 8909000.phy: QSERDES_COM_C_READY_STATUS timed-out qcom-ethqos 23040000.ethernet eth0: __stmmac_open: Serdes powerup failed The driver relied solely on the PHY framework's implicit enable of 'phy-supply', which only voted for a single rail and set no current load. Use devm_regulator_bulk_get_const() to acquire both supplies and set the peak current loads (46 mA for vdda-0p9, 15 mA for vdda-1p2) as required by the hardware. Fixes: 601d06277007 ("phy: qcom: add the SGMII SerDes PHY driver") Signed-off-by: Mohd Ayaan Anwar Reviewed-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260804-b4-sgmiieth_serdes_regulator-v2-2-c4bc688177dd@oss.qualcomm.com Signed-off-by: Vinod Koul --- drivers/phy/qualcomm/phy-qcom-sgmii-eth.c | 37 ++++++++++++++++++++--- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/drivers/phy/qualcomm/phy-qcom-sgmii-eth.c b/drivers/phy/qualcomm/phy-qcom-sgmii-eth.c index f48faa2929a6..7137200c3f31 100644 --- a/drivers/phy/qualcomm/phy-qcom-sgmii-eth.c +++ b/drivers/phy/qualcomm/phy-qcom-sgmii-eth.c @@ -11,6 +11,7 @@ #include #include #include +#include #include "phy-qcom-qmp-pcs-sgmii.h" #include "phy-qcom-qmp-qserdes-com-v5.h" @@ -26,7 +27,15 @@ #define QSERDES_PCS_SGMIIPHY_READY BIT(7) #define QSERDES_COM_C_PLL_LOCKED BIT(1) +static const struct regulator_bulk_data qcom_dwmac_sgmii_phy_vregs[] = { + { .supply = "vdda-0p9", .init_load_uA = 46000 }, + { .supply = "vdda-1p2", .init_load_uA = 15000 }, +}; + +#define QCOM_SGMII_NUM_SUPPLIES ARRAY_SIZE(qcom_dwmac_sgmii_phy_vregs) + struct qcom_dwmac_sgmii_phy_data { + struct regulator_bulk_data *vregs; struct regmap *regmap; struct clk *refclk; phy_interface_t interface; @@ -273,13 +282,24 @@ static int qcom_dwmac_sgmii_phy_power_on(struct phy *phy) struct qcom_dwmac_sgmii_phy_data *data = phy_get_drvdata(phy); int ret; - ret = clk_prepare_enable(data->refclk); - if (ret < 0) + ret = regulator_bulk_enable(QCOM_SGMII_NUM_SUPPLIES, data->vregs); + if (ret) return ret; + ret = clk_prepare_enable(data->refclk); + if (ret) + goto err_disable_regulators; + ret = qcom_dwmac_sgmii_phy_calibrate(phy); - if (ret < 0) - clk_disable_unprepare(data->refclk); + if (ret) + goto err_disable_clk; + + return 0; + +err_disable_clk: + clk_disable_unprepare(data->refclk); +err_disable_regulators: + regulator_bulk_disable(QCOM_SGMII_NUM_SUPPLIES, data->vregs); return ret; } @@ -296,6 +316,8 @@ static int qcom_dwmac_sgmii_phy_power_off(struct phy *phy) clk_disable_unprepare(data->refclk); + regulator_bulk_disable(QCOM_SGMII_NUM_SUPPLIES, data->vregs); + return 0; } @@ -357,6 +379,7 @@ static int qcom_dwmac_sgmii_phy_probe(struct platform_device *pdev) struct phy_provider *provider; void __iomem *base; struct phy *phy; + int ret; data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) @@ -381,6 +404,12 @@ static int qcom_dwmac_sgmii_phy_probe(struct platform_device *pdev) if (IS_ERR(data->refclk)) return PTR_ERR(data->refclk); + ret = devm_regulator_bulk_get_const(dev, QCOM_SGMII_NUM_SUPPLIES, + qcom_dwmac_sgmii_phy_vregs, + &data->vregs); + if (ret) + return ret; + provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); if (IS_ERR(provider)) return PTR_ERR(provider); From 729e0af3c2b938a9405e6245aaab97d025e3f427 Mon Sep 17 00:00:00 2001 From: Loic Poulain Date: Wed, 22 Jul 2026 15:45:14 +0200 Subject: [PATCH 50/76] phy: qcom: qmp-combo: Prevent unnecessary PM runtime suspend at boot Runtime PM has to be enabled before creating the PHYs, since phy_create() only enables runtime PM on the PHY devices if it is already enabled on this parent device. This opens a small window where the device can be runtime suspended after pm_runtime_enable() and before the later pm_runtime_forbid(), causing an unnecessary suspend/resume cycle while the PHYs are not yet registered. Take a runtime PM usage reference with pm_runtime_get_noresume() before enabling runtime PM and release it once the PHYs have been created to prevent the device from being runtime suspended during that window. This also makes the probe path safe independently of pm_runtime_forbid(), which is a good preparation for potentially dropping the forbid() call in the future and letting runtime PM be enabled by default. Reviewed-by: Dmitry Baryshkov Reviewed-by: Abel Vesa Reviewed-by: Konrad Dybcio Signed-off-by: Loic Poulain Link: https://patch.msgid.link/20260722-qcom-usb-phy-fix-null-v6-1-534f7e61b9a6@oss.qualcomm.com Signed-off-by: Vinod Koul --- drivers/phy/qualcomm/phy-qcom-qmp-combo.c | 26 +++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-combo.c b/drivers/phy/qualcomm/phy-qcom-qmp-combo.c index 322bd5b75e06..b75bed1f0744 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-combo.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-combo.c @@ -5318,10 +5318,16 @@ static int qmp_combo_probe(struct platform_device *pdev) if (ret) goto err_node_put; + /* + * Enable runtime PM before creating the PHYs, phy_create() only enables + * it on the PHY devices if already enabled on the parent. Hold a usage + * reference so callbacks cannot run until the PHY is ready. + */ + pm_runtime_get_noresume(dev); pm_runtime_set_active(dev); ret = devm_pm_runtime_enable(dev); if (ret) - goto err_node_put; + goto err_pm_put; /* * Prevent runtime pm from being ON by default. Users can enable * it using power/control in sysfs. @@ -5330,14 +5336,13 @@ static int qmp_combo_probe(struct platform_device *pdev) ret = qmp_combo_register_clocks(qmp, usb_np, dp_np); if (ret) - goto err_node_put; - + goto err_pm_put; qmp->usb_phy = devm_phy_create(dev, usb_np, &qmp_combo_usb_phy_ops); if (IS_ERR(qmp->usb_phy)) { ret = PTR_ERR(qmp->usb_phy); dev_err(dev, "failed to create USB PHY: %d\n", ret); - goto err_node_put; + goto err_pm_put; } phy_set_drvdata(qmp->usb_phy, qmp); @@ -5346,7 +5351,7 @@ static int qmp_combo_probe(struct platform_device *pdev) if (IS_ERR(qmp->dp_phy)) { ret = PTR_ERR(qmp->dp_phy); dev_err(dev, "failed to create DP PHY: %d\n", ret); - goto err_node_put; + goto err_pm_put; } phy_set_drvdata(qmp->dp_phy, qmp); @@ -5356,11 +5361,20 @@ static int qmp_combo_probe(struct platform_device *pdev) else phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + if (IS_ERR(phy_provider)) { + ret = PTR_ERR(phy_provider); + goto err_pm_put; + } + of_node_put(usb_np); of_node_put(dp_np); - return PTR_ERR_OR_ZERO(phy_provider); + pm_runtime_put(dev); + return 0; + +err_pm_put: + pm_runtime_put_noidle(dev); err_node_put: of_node_put(usb_np); of_node_put(dp_np); From 991339420f6ddd299d9f34316866dda83dccd72a Mon Sep 17 00:00:00 2001 From: Loic Poulain Date: Wed, 22 Jul 2026 15:45:15 +0200 Subject: [PATCH 51/76] phy: qcom: qmp-usbc: Prevent unnecessary PM runtime suspend at boot Runtime PM has to be enabled before creating the PHYs, since phy_create() only enables runtime PM on the PHY devices if it is already enabled on this parent device. This opens a small window where the device can be runtime suspended after pm_runtime_enable() and before the later pm_runtime_forbid(), causing an unnecessary suspend/resume cycle while the PHYs are not yet registered. Take a runtime PM usage reference with pm_runtime_get_noresume() before enabling runtime PM and release it once the PHYs have been created to prevent the device from being runtime suspended during that window. This also makes the probe path safe independently of pm_runtime_forbid(), which is a good preparation for potentially dropping the forbid() call in the future and letting runtime PM be enabled by default. Reviewed-by: Konrad Dybcio Reviewed-by: Abel Vesa Reviewed-by: Dmitry Baryshkov Signed-off-by: Loic Poulain Link: https://patch.msgid.link/20260722-qcom-usb-phy-fix-null-v6-2-534f7e61b9a6@oss.qualcomm.com Signed-off-by: Vinod Koul --- drivers/phy/qualcomm/phy-qcom-qmp-usbc.c | 26 ++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-usbc.c b/drivers/phy/qualcomm/phy-qcom-qmp-usbc.c index fa8492b487d7..e3465ab7c80e 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-usbc.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-usbc.c @@ -2003,10 +2003,16 @@ static int qmp_usbc_probe(struct platform_device *pdev) if (ret) goto err_node_put; + /* + * Enable runtime PM before creating the PHYs, phy_create() only enables + * it on the PHY devices if already enabled on the parent. Hold a usage + * reference so callbacks cannot run until the PHY is ready. + */ + pm_runtime_get_noresume(dev); pm_runtime_set_active(dev); ret = devm_pm_runtime_enable(dev); if (ret) - goto err_node_put; + goto err_pm_put; /* * Prevent runtime pm from being ON by default. Users can enable * it using power/control in sysfs. @@ -2015,13 +2021,13 @@ static int qmp_usbc_probe(struct platform_device *pdev) ret = qmp_usbc_register_clocks(qmp, np); if (ret) - goto err_node_put; + goto err_pm_put; qmp->usb_phy = devm_phy_create(dev, np, &qmp_usbc_usb_phy_ops); if (IS_ERR(qmp->usb_phy)) { ret = PTR_ERR(qmp->usb_phy); dev_err(dev, "failed to create PHY: %d\n", ret); - goto err_node_put; + goto err_pm_put; } phy_set_drvdata(qmp->usb_phy, qmp); @@ -2031,17 +2037,25 @@ static int qmp_usbc_probe(struct platform_device *pdev) if (IS_ERR(qmp->dp_phy)) { ret = PTR_ERR(qmp->dp_phy); dev_err(dev, "failed to create PHY: %d\n", ret); - goto err_node_put; + goto err_pm_put; } phy_set_drvdata(qmp->dp_phy, qmp); } + phy_provider = devm_of_phy_provider_register(dev, qmp_usbc_phy_xlate); + if (IS_ERR(phy_provider)) { + ret = PTR_ERR(phy_provider); + goto err_pm_put; + } + of_node_put(np); - phy_provider = devm_of_phy_provider_register(dev, qmp_usbc_phy_xlate); + pm_runtime_put(dev); - return PTR_ERR_OR_ZERO(phy_provider); + return 0; +err_pm_put: + pm_runtime_put_noidle(dev); err_node_put: of_node_put(np); return ret; From 8e3687f7e18fe84372e86875709d56c37e7525a8 Mon Sep 17 00:00:00 2001 From: Loic Poulain Date: Wed, 22 Jul 2026 15:45:16 +0200 Subject: [PATCH 52/76] phy: qcom: qmp-usb-legacy: Fix possible NULL-deref on early runtime suspend There is a small window where the runtime suspend callback may run after pm_runtime_enable() and before pm_runtime_forbid(). In this case, a crash occurs because runtime suspend/resume dereferences qmp->phy pointer, which is not yet initialized: `if (!qmp->phy->init_count) {` This can also happen if user re-enables runtime-pm via the sysfs attribute before qmp phy is initialized. Similarly to other qcom phy drivers, introduce a qmp->phy_initialized variable that can be used to avoid relying on the possibly uninitialized phy pointer. Fixes: e464a3180a43 ("phy: qcom-qmp-usb: split off the legacy USB+dp_com support") Reviewed-by: Abel Vesa Reviewed-by: Dmitry Baryshkov Signed-off-by: Loic Poulain Link: https://patch.msgid.link/20260722-qcom-usb-phy-fix-null-v6-3-534f7e61b9a6@oss.qualcomm.com Signed-off-by: Vinod Koul --- drivers/phy/qualcomm/phy-qcom-qmp-usb-legacy.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-usb-legacy.c b/drivers/phy/qualcomm/phy-qcom-qmp-usb-legacy.c index 8bf951b0490c..fc490589c8e4 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-usb-legacy.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-usb-legacy.c @@ -542,6 +542,8 @@ struct qmp_usb { enum phy_mode mode; + bool phy_initialized; + struct phy *phy; struct clk_fixed_rate pipe_clk_fixed; @@ -895,6 +897,7 @@ static int qmp_usb_legacy_power_off(struct phy *phy) static int qmp_usb_legacy_enable(struct phy *phy) { + struct qmp_usb *qmp = phy_get_drvdata(phy); int ret; ret = qmp_usb_legacy_init(phy); @@ -904,14 +907,19 @@ static int qmp_usb_legacy_enable(struct phy *phy) ret = qmp_usb_legacy_power_on(phy); if (ret) qmp_usb_legacy_exit(phy); + else + qmp->phy_initialized = true; return ret; } static int qmp_usb_legacy_disable(struct phy *phy) { + struct qmp_usb *qmp = phy_get_drvdata(phy); int ret; + qmp->phy_initialized = false; + ret = qmp_usb_legacy_power_off(phy); if (ret) return ret; @@ -988,7 +996,7 @@ static int __maybe_unused qmp_usb_legacy_runtime_suspend(struct device *dev) dev_vdbg(dev, "Suspending QMP phy, mode:%d\n", qmp->mode); - if (!qmp->phy->init_count) { + if (!qmp->phy_initialized) { dev_vdbg(dev, "PHY not initialized, bailing out\n"); return 0; } @@ -1009,7 +1017,7 @@ static int __maybe_unused qmp_usb_legacy_runtime_resume(struct device *dev) dev_vdbg(dev, "Resuming QMP phy, mode:%d\n", qmp->mode); - if (!qmp->phy->init_count) { + if (!qmp->phy_initialized) { dev_vdbg(dev, "PHY not initialized, bailing out\n"); return 0; } From a9b9feb37bfa684f495e3a3553342191d2b30eee Mon Sep 17 00:00:00 2001 From: Loic Poulain Date: Wed, 22 Jul 2026 15:45:17 +0200 Subject: [PATCH 53/76] phy: qcom: qmp-usb-legacy: Prevent unnecessary PM runtime suspend at boot Runtime PM has to be enabled before creating the PHY, since phy_create() only enables runtime PM on the PHY device if it is already enabled on this parent device. This opens a small window where the device can be runtime suspended after pm_runtime_enable() and before the later pm_runtime_forbid(), causing an unnecessary suspend/resume cycle while the PHY is not yet registered. Take a runtime PM usage reference with pm_runtime_get_noresume() before enabling runtime PM and release it once the PHY has been created to prevent the device from being runtime suspended during that window. This also makes the probe path safe independently of pm_runtime_forbid(), which is a good preparation for potentially dropping the forbid() call in the future and letting runtime PM be enabled by default. Reviewed-by: Abel Vesa Reviewed-by: Dmitry Baryshkov Signed-off-by: Loic Poulain Link: https://patch.msgid.link/20260722-qcom-usb-phy-fix-null-v6-4-534f7e61b9a6@oss.qualcomm.com Signed-off-by: Vinod Koul --- .../phy/qualcomm/phy-qcom-qmp-usb-legacy.c | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-usb-legacy.c b/drivers/phy/qualcomm/phy-qcom-qmp-usb-legacy.c index fc490589c8e4..c5160cb89207 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-usb-legacy.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-usb-legacy.c @@ -1285,10 +1285,16 @@ static int qmp_usb_legacy_probe(struct platform_device *pdev) if (ret) goto err_node_put; + /* + * Enable runtime PM before creating the PHY, phy_create() only enables + * it on the PHY device if already enabled on the parent. Hold a usage + * reference so callbacks cannot run before qmp->phy is assigned. + */ + pm_runtime_get_noresume(dev); pm_runtime_set_active(dev); ret = devm_pm_runtime_enable(dev); if (ret) - goto err_node_put; + goto err_pm_put; /* * Prevent runtime pm from being ON by default. Users can enable * it using power/control in sysfs. @@ -1297,23 +1303,31 @@ static int qmp_usb_legacy_probe(struct platform_device *pdev) ret = phy_pipe_clk_register(qmp, np); if (ret) - goto err_node_put; + goto err_pm_put; qmp->phy = devm_phy_create(dev, np, &qmp_usb_legacy_phy_ops); if (IS_ERR(qmp->phy)) { ret = PTR_ERR(qmp->phy); dev_err(dev, "failed to create PHY: %d\n", ret); - goto err_node_put; + goto err_pm_put; } phy_set_drvdata(qmp->phy, qmp); + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + if (IS_ERR(phy_provider)) { + ret = PTR_ERR(phy_provider); + goto err_pm_put; + } + of_node_put(np); - phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + pm_runtime_put(dev); - return PTR_ERR_OR_ZERO(phy_provider); + return 0; +err_pm_put: + pm_runtime_put_noidle(dev); err_node_put: of_node_put(np); return ret; From c271a6926ea7d3c9566b033d63fd4e8c488dc860 Mon Sep 17 00:00:00 2001 From: Loic Poulain Date: Wed, 22 Jul 2026 15:45:18 +0200 Subject: [PATCH 54/76] phy: qcom: snps-femto-v2: Fix possible NULL-deref on early runtime suspend Runtime PM must be enabled before creating the PHY, since phy_create() only enables runtime PM on the PHY device if it is already enabled on this parent device. However, the runtime PM callbacks dereference the hsphy instance, which is not yet ready, leaving a window where a suspend callback may trigger a NULL pointer dereference. Take a runtime PM usage reference with pm_runtime_get_noresume() before enabling runtime PM and release it once the PHY has been created, so that no runtime suspend can run before the PHY is ready. This also prevents a short window where an unnecessary runtime suspend can occur. Use the devres-managed version to ensure PM runtime is symmetrically disabled during driver removal for proper cleanup. Fixes: 0d75f508a9d5 ("phy: qcom-snps: Add runtime suspend and resume handlers") Reviewed-by: Dmitry Baryshkov Reviewed-by: Konrad Dybcio Reviewed-by: Abel Vesa Signed-off-by: Loic Poulain Link: https://patch.msgid.link/20260722-qcom-usb-phy-fix-null-v6-5-534f7e61b9a6@oss.qualcomm.com Signed-off-by: Vinod Koul --- drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c b/drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c index eb0b0f61d98e..980ad1fb1e2e 100644 --- a/drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c +++ b/drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c @@ -599,8 +599,18 @@ static int qcom_snps_hsphy_probe(struct platform_device *pdev) return dev_err_probe(dev, ret, "failed to get regulator supplies\n"); + /* + * Enable runtime PM before creating the PHY, phy_create() only enables + * it on the PHY device if already enabled on the parent. Hold a usage + * reference so callbacks cannot run before the PHY is ready. + */ + pm_runtime_get_noresume(dev); pm_runtime_set_active(dev); - pm_runtime_enable(dev); + ret = devm_pm_runtime_enable(dev); + if (ret) { + pm_runtime_put_noidle(dev); + return ret; + } /* * Prevent runtime pm from being ON by default. Users can enable * it using power/control in sysfs. @@ -611,6 +621,7 @@ static int qcom_snps_hsphy_probe(struct platform_device *pdev) if (IS_ERR(generic_phy)) { ret = PTR_ERR(generic_phy); dev_err(dev, "failed to create phy, %d\n", ret); + pm_runtime_put_noidle(dev); return ret; } hsphy->phy = generic_phy; @@ -620,12 +631,15 @@ static int qcom_snps_hsphy_probe(struct platform_device *pdev) qcom_snps_hsphy_read_override_param_seq(dev); phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); - if (!IS_ERR(phy_provider)) - dev_dbg(dev, "Registered Qcom-SNPS HS phy\n"); - else - pm_runtime_disable(dev); + if (IS_ERR(phy_provider)) { + pm_runtime_put_noidle(dev); + return PTR_ERR(phy_provider); + } - return PTR_ERR_OR_ZERO(phy_provider); + dev_dbg(dev, "Registered Qcom-SNPS HS phy\n"); + pm_runtime_put(dev); + + return 0; } static struct platform_driver qcom_snps_hsphy_driver = { From 142c5593379273264474f31d5956b1a0065cd576 Mon Sep 17 00:00:00 2001 From: Loic Poulain Date: Wed, 22 Jul 2026 15:45:19 +0200 Subject: [PATCH 55/76] phy: qcom: qmp-usb: Fix possible NULL-deref on early runtime suspend There is a small window where the runtime suspend callback may run after pm_runtime_enable() and before pm_runtime_forbid(). In this case, a crash occurs because runtime suspend/resume dereferences qmp->phy pointer, which is not yet initialized: `if (!qmp->phy->init_count) {` This can also happen if user re-enables runtime-pm via the sysfs attribute before qmp phy is initialized. Similarly to other qcom phy drivers, introduce a qmp->phy_initialized variable that can be used to avoid relying on the possibly uninitialized phy pointer. Fixes: e464a3180a43 ("phy: qcom-qmp-usb: split off the legacy USB+dp_com support") Signed-off-by: Loic Poulain Link: https://patch.msgid.link/20260722-qcom-usb-phy-fix-null-v6-6-534f7e61b9a6@oss.qualcomm.com Signed-off-by: Vinod Koul --- drivers/phy/qualcomm/phy-qcom-qmp-usb.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-usb.c b/drivers/phy/qualcomm/phy-qcom-qmp-usb.c index c5507168e135..bb905c437a01 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-usb.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-usb.c @@ -1434,6 +1434,8 @@ struct qmp_usb { enum phy_mode mode; + bool phy_initialized; + struct phy *phy; struct clk_fixed_rate pipe_clk_fixed; @@ -2009,6 +2011,7 @@ static int qmp_usb_power_off(struct phy *phy) static int qmp_usb_enable(struct phy *phy) { + struct qmp_usb *qmp = phy_get_drvdata(phy); int ret; ret = qmp_usb_init(phy); @@ -2018,14 +2021,19 @@ static int qmp_usb_enable(struct phy *phy) ret = qmp_usb_power_on(phy); if (ret) qmp_usb_exit(phy); + else + qmp->phy_initialized = true; return ret; } static int qmp_usb_disable(struct phy *phy) { + struct qmp_usb *qmp = phy_get_drvdata(phy); int ret; + qmp->phy_initialized = false; + ret = qmp_usb_power_off(phy); if (ret) return ret; @@ -2101,7 +2109,7 @@ static int __maybe_unused qmp_usb_runtime_suspend(struct device *dev) dev_vdbg(dev, "Suspending QMP phy, mode:%d\n", qmp->mode); - if (!qmp->phy->init_count) { + if (!qmp->phy_initialized) { dev_vdbg(dev, "PHY not initialized, bailing out\n"); return 0; } @@ -2121,7 +2129,7 @@ static int __maybe_unused qmp_usb_runtime_resume(struct device *dev) dev_vdbg(dev, "Resuming QMP phy, mode:%d\n", qmp->mode); - if (!qmp->phy->init_count) { + if (!qmp->phy_initialized) { dev_vdbg(dev, "PHY not initialized, bailing out\n"); return 0; } From fedcff16a9b60a94e68e2a0478ceda309566d61a Mon Sep 17 00:00:00 2001 From: Loic Poulain Date: Wed, 22 Jul 2026 15:45:20 +0200 Subject: [PATCH 56/76] phy: qcom: qmp-usb: Prevent unnecessary PM runtime suspend at boot Runtime PM has to be enabled before creating the PHY, since phy_create() only enables runtime PM on the PHY device if it is already enabled on this parent device. This opens a small window where the device can be runtime suspended after pm_runtime_enable() and before the later pm_runtime_forbid(), causing an unnecessary suspend/resume cycle while the PHY is not yet registered. Take a runtime PM usage reference with pm_runtime_get_noresume() before enabling runtime PM and release it once the PHY has been created to prevent the device from being runtime suspended during that window. This also makes the probe path safe independently of pm_runtime_forbid(), which is a good preparation for potentially dropping the forbid() call in the future and letting runtime PM be enabled by default. Signed-off-by: Loic Poulain Link: https://patch.msgid.link/20260722-qcom-usb-phy-fix-null-v6-7-534f7e61b9a6@oss.qualcomm.com Signed-off-by: Vinod Koul --- drivers/phy/qualcomm/phy-qcom-qmp-usb.c | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-usb.c b/drivers/phy/qualcomm/phy-qcom-qmp-usb.c index bb905c437a01..b0790bcf0bc8 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-usb.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-usb.c @@ -2419,10 +2419,16 @@ static int qmp_usb_probe(struct platform_device *pdev) if (ret) goto err_node_put; + /* + * Enable runtime PM before creating the PHY, phy_create() only enables + * it on the PHY device if already enabled on the parent. Hold a usage + * reference so callbacks cannot run before qmp->phy is assigned. + */ + pm_runtime_get_noresume(dev); pm_runtime_set_active(dev); ret = devm_pm_runtime_enable(dev); if (ret) - goto err_node_put; + goto err_pm_put; /* * Prevent runtime pm from being ON by default. Users can enable * it using power/control in sysfs. @@ -2431,23 +2437,31 @@ static int qmp_usb_probe(struct platform_device *pdev) ret = phy_pipe_clk_register(qmp, np); if (ret) - goto err_node_put; + goto err_pm_put; qmp->phy = devm_phy_create(dev, np, &qmp_usb_phy_ops); if (IS_ERR(qmp->phy)) { ret = PTR_ERR(qmp->phy); dev_err(dev, "failed to create PHY: %d\n", ret); - goto err_node_put; + goto err_pm_put; } phy_set_drvdata(qmp->phy, qmp); + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + if (IS_ERR(phy_provider)) { + ret = PTR_ERR(phy_provider); + goto err_pm_put; + } + of_node_put(np); - phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + pm_runtime_put(dev); - return PTR_ERR_OR_ZERO(phy_provider); + return 0; +err_pm_put: + pm_runtime_put_noidle(dev); err_node_put: of_node_put(np); return ret; From bae047e91213a7b8f86b70f0f48f38374a73d5ea Mon Sep 17 00:00:00 2001 From: Esteban Urrutia Date: Wed, 15 Jul 2026 02:37:46 -0400 Subject: [PATCH 57/76] dt-bindings: phy: qcom,sc8280xp-qmp-pcie-phy: Add SM8475 QMP PHY SM8450 init sequence for this PHY varies significantly and can't be reused in SM8475. Add bindings for the PHY found in this SoC. Signed-off-by: Esteban Urrutia Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260715-sm8475-bup-pcie-v2-1-48bd91a19abf@proton.me Signed-off-by: Vinod Koul --- .../devicetree/bindings/phy/qcom,sc8280xp-qmp-pcie-phy.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-pcie-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-pcie-phy.yaml index 259ed9678393..b353b7bfb839 100644 --- a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-pcie-phy.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-pcie-phy.yaml @@ -45,6 +45,7 @@ properties: - qcom,sm8350-qmp-gen3x2-pcie-phy - qcom,sm8450-qmp-gen3x1-pcie-phy - qcom,sm8450-qmp-gen4x2-pcie-phy + - qcom,sm8475-qmp-gen3x1-pcie-phy - qcom,sm8550-qmp-gen3x2-pcie-phy - qcom,sm8550-qmp-gen4x2-pcie-phy - qcom,sm8650-qmp-gen3x2-pcie-phy @@ -177,6 +178,7 @@ allOf: - qcom,sm8350-qmp-gen3x2-pcie-phy - qcom,sm8450-qmp-gen3x1-pcie-phy - qcom,sm8450-qmp-gen3x2-pcie-phy + - qcom,sm8475-qmp-gen3x1-pcie-phy - qcom,sm8550-qmp-gen3x2-pcie-phy - qcom,sm8550-qmp-gen4x2-pcie-phy - qcom,sm8650-qmp-gen3x2-pcie-phy From f8b4493d5e7e19682abcf538a9faad012b5ef69e Mon Sep 17 00:00:00 2001 From: Esteban Urrutia Date: Wed, 15 Jul 2026 02:37:47 -0400 Subject: [PATCH 58/76] phy: qcom: qmp-pcie: Add pcs_lane1 offset to V5 offsets Some SoCs such as SM8475 write data to registers using this offset, specifically SW_CTRL2 and MX_CTRL2. Add pcs_lane1 offset to V5 offsets to support this. Signed-off-by: Esteban Urrutia Fixes: 0fd0b31965b0 ("phy: qualcomm: qmp-pcie: add support for SAR2130P") Reviewed-by: Dmitry Baryshkov Link: https://patch.msgid.link/20260715-sm8475-bup-pcie-v2-2-48bd91a19abf@proton.me Signed-off-by: Vinod Koul --- drivers/phy/qualcomm/phy-qcom-qmp-pcie.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c index 27034bb62d0d..5bebc6baadfe 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c @@ -4092,6 +4092,7 @@ static const struct qmp_pcie_offsets qmp_pcie_offsets_v5 = { .pcs_misc = 0x0600, .tx = 0x0e00, .rx = 0x1000, + .pcs_lane1 = 0x1400, .tx2 = 0x1600, .rx2 = 0x1800, }; From 9e953732c3ed02b19698bf4af05396a3573681e1 Mon Sep 17 00:00:00 2001 From: Esteban Urrutia Date: Wed, 15 Jul 2026 02:37:48 -0400 Subject: [PATCH 59/76] phy: qcom: qmp-pcie: Add support for SM8475 Gen3x1 PCIe0 port This gets the port working. Tested with WCN6856 WLAN capabilities. Serdes, RX and PCS Misc tables had to be added, while TX and PCS tables were reused from SM8550, and PCS Lane1 was reused from SAR2130P. Signed-off-by: Esteban Urrutia Reviewed-by: Dmitry Baryshkov Link: https://patch.msgid.link/20260715-sm8475-bup-pcie-v2-3-48bd91a19abf@proton.me Signed-off-by: Vinod Koul --- drivers/phy/qualcomm/phy-qcom-qmp-pcie.c | 108 +++++++++++++++++++++++ 1 file changed, 108 insertions(+) diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c index 5bebc6baadfe..da83358c97ea 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c @@ -2699,6 +2699,82 @@ static const struct qmp_phy_init_tbl sm8450_qmp_gen4x2_pcie_ep_pcs_misc_tbl[] = QMP_PHY_INIT_CFG(QPHY_V5_20_PCS_PCIE_OSC_DTCT_MODE2_CONFIG5, 0x08), }; +static const struct qmp_phy_init_tbl sm8475_qmp_gen3x1_pcie_serdes_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V6_COM_SSC_EN_CENTER, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_SSC_PER1, 0x31), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_SSC_PER2, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_SSC_STEP_SIZE1_MODE0, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_SSC_STEP_SIZE2_MODE0, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_SSC_STEP_SIZE1_MODE1, 0x4c), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_SSC_STEP_SIZE2_MODE1, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_CLK_ENABLE1, 0x90), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_SYS_CLK_CTRL, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_IVCO, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_CP_CTRL_MODE0, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_CP_CTRL_MODE1, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_RCTRL_MODE1, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_CCTRL_MODE0, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_CCTRL_MODE1, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_SYSCLK_EN_SEL, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_BG_TIMER, 0x0e), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP_EN, 0x42), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP1_MODE0, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP2_MODE0, 0x1a), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP1_MODE1, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP2_MODE1, 0x34), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_DEC_START_MODE0, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_DEC_START_MODE1, 0x68), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_DIV_FRAC_START1_MODE0, 0xab), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_DIV_FRAC_START2_MODE0, 0xea), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_DIV_FRAC_START3_MODE0, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_DIV_FRAC_START1_MODE1, 0xab), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_DIV_FRAC_START2_MODE1, 0xaa), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_DIV_FRAC_START3_MODE1, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_VCO_TUNE_MAP, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_CLK_SELECT, 0x34), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_HSCLK_SEL_1, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_CORECLK_DIV_MODE1, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_CMN_CONFIG_1, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_ADDITIONAL_MISC_3, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V6_COM_CORE_CLK_EN, 0xa0), +}; + +static const struct qmp_phy_init_tbl sm8475_qmp_gen3x1_pcie_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V6_RX_DFE_CTLE_POST_CAL_OFFSET, 0x38), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_GM_CAL, 0x11), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_RX_MODE_00_HIGH, 0xbf), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_RX_MODE_00_HIGH2, 0xbf), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_RX_MODE_00_HIGH3, 0xb7), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_RX_MODE_00_HIGH4, 0xea), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_RX_MODE_00_LOW, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_RX_MODE_01_HIGH, 0x5c), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_RX_MODE_01_HIGH2, 0x9c), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_RX_MODE_01_HIGH3, 0x1a), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_RX_MODE_01_HIGH4, 0x89), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_RX_MODE_01_LOW, 0xdc), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_RX_MODE_10_HIGH, 0x94), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_RX_MODE_10_HIGH2, 0x5b), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_RX_MODE_10_HIGH3, 0x1a), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_RX_MODE_10_HIGH4, 0x89), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_TX_ADAPT_POST_THRESH, 0xf0), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_UCDR_FO_GAIN, 0x09), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_UCDR_SO_GAIN, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_UCDR_SB2_THRESH1, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_UCDR_SB2_THRESH2, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_VGA_CAL_CNTRL2, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_SIDGET_ENABLES, 0x1c), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_RX_IDAC_TSETTLE_LOW, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V6_RX_SIGDET_CAL_TRIM, 0x08), +}; + +static const struct qmp_phy_init_tbl sm8475_qmp_gen3x1_pcie_pcs_misc_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_PCIE_V6_PCS_PCIE_POWER_STATE_CONFIG2, 0x1d), + QMP_PHY_INIT_CFG(QPHY_PCIE_V6_PCS_PCIE_POWER_STATE_CONFIG4, 0x07), + QMP_PHY_INIT_CFG(QPHY_PCIE_V6_PCS_PCIE_ENDPOINT_REFCLK_DRIVE, 0xc1), + QMP_PHY_INIT_CFG(QPHY_PCIE_V6_PCS_PCIE_OSC_DTCT_ACTIONS, 0x00), +}; + static const struct qmp_phy_init_tbl sm8550_qmp_gen3x2_pcie_serdes_tbl[] = { QMP_PHY_INIT_CFG(QSERDES_V6_COM_SSC_EN_CENTER, 0x01), QMP_PHY_INIT_CFG(QSERDES_V6_COM_SSC_PER1, 0x62), @@ -5061,6 +5137,35 @@ static const struct qmp_phy_cfg sm8450_qmp_gen4x2_pciephy_cfg = { .aux_clock_rate = 20000000, }; +static const struct qmp_phy_cfg sm8475_qmp_gen3x1_pciephy_cfg = { + .lanes = 1, + + .offsets = &qmp_pcie_offsets_v5, + + .tbls = { + .serdes = sm8475_qmp_gen3x1_pcie_serdes_tbl, + .serdes_num = ARRAY_SIZE(sm8475_qmp_gen3x1_pcie_serdes_tbl), + .tx = sm8550_qmp_gen3x2_pcie_tx_tbl, + .tx_num = ARRAY_SIZE(sm8550_qmp_gen3x2_pcie_tx_tbl), + .rx = sm8475_qmp_gen3x1_pcie_rx_tbl, + .rx_num = ARRAY_SIZE(sm8475_qmp_gen3x1_pcie_rx_tbl), + .pcs = sm8550_qmp_gen3x2_pcie_pcs_tbl, + .pcs_num = ARRAY_SIZE(sm8550_qmp_gen3x2_pcie_pcs_tbl), + .pcs_misc = sm8475_qmp_gen3x1_pcie_pcs_misc_tbl, + .pcs_misc_num = ARRAY_SIZE(sm8475_qmp_gen3x1_pcie_pcs_misc_tbl), + .pcs_lane1 = sar2130p_qmp_gen3x2_pcie_pcs_lane1_tbl, + .pcs_lane1_num = ARRAY_SIZE(sar2130p_qmp_gen3x2_pcie_pcs_lane1_tbl), + }, + .reset_list = sdm845_pciephy_reset_l, + .num_resets = ARRAY_SIZE(sdm845_pciephy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = pciephy_v6_regs_layout, + + .pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL, + .phy_status = PHYSTATUS, +}; + static const struct qmp_phy_cfg sm8550_qmp_gen3x2_pciephy_cfg = { .lanes = 2, @@ -6366,6 +6471,9 @@ static const struct of_device_id qmp_pcie_of_match_table[] = { }, { .compatible = "qcom,sm8450-qmp-gen4x2-pcie-phy", .data = &sm8450_qmp_gen4x2_pciephy_cfg, + }, { + .compatible = "qcom,sm8475-qmp-gen3x1-pcie-phy", + .data = &sm8475_qmp_gen3x1_pciephy_cfg, }, { .compatible = "qcom,sm8550-qmp-gen3x2-pcie-phy", .data = &sm8550_qmp_gen3x2_pciephy_cfg, From 9ea8d49c6cc6e217f5f5818369f659dc25bf89bf Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Thu, 2 Jul 2026 13:58:55 +0100 Subject: [PATCH 60/76] phy: renesas: rcar-gen3-usb2: Ignore missing VBUS regulator Commit b6d7dd157763 ("phy: renesas: rcar-gen3-usb2: Add regulator for OTG VBUS control") introduced support for controlling OTG VBUS through the regulator framework. As part of this change, the driver started requesting an exclusive "vbus" regulator for OTG-capable PHYs with no_adp_ctrl set. The lookup failure was propagated unconditionally, causing probe to fail on platforms where no VBUS regulator is described. On RZ/V2H and RZ/V2N, which do not use a VBUS regulator, this results in the following error: phy_rcar_gen3_usb2 15800200.usb-phy: dummy supplies not allowed for exclusive requests (id=vbus) This failure completely prevents the USB 2.0 interface from initializing. Fix this by allowing the probe to continue if an external VBUS regulator is missing. Only propagate the error if the internal vbus-regulator node is explicitly present, or if the lookup returns -EPROBE_DEFER. For all other missing regulator errors, gracefully assume no external VBUS regulator is available and return 0. Fixes: b6d7dd157763 ("phy: renesas: rcar-gen3-usb2: Add regulator for OTG VBUS control") Signed-off-by: Lad Prabhakar Tested-by: Tommaso Merciai Reviewed-by: Tommaso Merciai Link: https://patch.msgid.link/20260702125855.3157253-1-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Vinod Koul --- drivers/phy/renesas/phy-rcar-gen3-usb2.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/phy/renesas/phy-rcar-gen3-usb2.c b/drivers/phy/renesas/phy-rcar-gen3-usb2.c index ef38c3b365d4..9ae9975d3255 100644 --- a/drivers/phy/renesas/phy-rcar-gen3-usb2.c +++ b/drivers/phy/renesas/phy-rcar-gen3-usb2.c @@ -902,8 +902,17 @@ static int rcar_gen3_phy_usb2_vbus_regulator_get_exclusive_enable(struct rcar_ge int ret; channel->vbus = devm_regulator_get_exclusive(dev, "vbus"); - if (IS_ERR(channel->vbus)) - return PTR_ERR(channel->vbus); + if (IS_ERR(channel->vbus)) { + ret = PTR_ERR(channel->vbus); + /* If vbus-regulator node was present vbus regulator should be available */ + if (channel->otg_internal_reg) + return ret; + + if (ret == -EPROBE_DEFER) + return ret; + + return 0; + } if (enable) { ret = regulator_enable(channel->vbus); From 481807f939077059db278edd0467e7e609b6c9a7 Mon Sep 17 00:00:00 2001 From: Esteban Urrutia Date: Sat, 11 Jul 2026 23:13:17 -0400 Subject: [PATCH 61/76] phy: qcom: qmp-combo: Correct pre-emphasis table for QMP v4 DP PHYs Comparing sm8350 and sm8450 tables, this seems to be a typo. Fixes: ef14aff107bd ("phy: qcom: com-qmp-combo: add SM8350 & SM8450 support") Signed-off-by: Esteban Urrutia Link: https://patch.msgid.link/20260711-sm8450-qol-qmp-v2-1-d14353bd6ff4@proton.me Signed-off-by: Vinod Koul --- drivers/phy/qualcomm/phy-qcom-qmp-combo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-combo.c b/drivers/phy/qualcomm/phy-qcom-qmp-combo.c index b75bed1f0744..f2d2e3feb772 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-combo.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-combo.c @@ -2398,7 +2398,7 @@ static const u8 qmp_dp_v4_pre_emphasis_hbr3_hbr2[4][4] = { static const u8 qmp_dp_v4_pre_emphasis_hbr_rbr[4][4] = { { 0x00, 0x0d, 0x14, 0x1a }, { 0x00, 0x0e, 0x15, 0xff }, - { 0x00, 0x0d, 0xff, 0xff }, + { 0x00, 0x0e, 0xff, 0xff }, { 0x03, 0xff, 0xff, 0xff } }; From 8fd2b30d187fa33e5dd30699d3e3a3662c1610c9 Mon Sep 17 00:00:00 2001 From: Esteban Urrutia Date: Sat, 11 Jul 2026 23:13:18 -0400 Subject: [PATCH 62/76] phy: qcom: qmp-combo: Drop qmp_v4_calibrate_dp_phy There are no downstream device trees that specify five values in the qcom,aux-cfg1-settings array. Plus, after cross referencing both downstream device trees and entries which refer this function, only 0x13 is specified. Since 0x13 is written at initialization time, drop this function as a whole, and remove now unused variable assignations. Fixes: aff188feb5e1 ("phy: qcom-qmp: add support for sm8250-usb3-dp phy") Reviewed-by: Dmitry Baryshkov Signed-off-by: Esteban Urrutia Link: https://patch.msgid.link/20260711-sm8450-qol-qmp-v2-2-d14353bd6ff4@proton.me Signed-off-by: Vinod Koul --- drivers/phy/qualcomm/phy-qcom-qmp-combo.c | 31 ----------------------- 1 file changed, 31 deletions(-) diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-combo.c b/drivers/phy/qualcomm/phy-qcom-qmp-combo.c index f2d2e3feb772..c39ced168d03 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-combo.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-combo.c @@ -2617,7 +2617,6 @@ static int qmp_v3_calibrate_dp_phy(struct qmp_combo *qmp); static void qmp_v4_dp_aux_init(struct qmp_combo *qmp); static void qmp_v4_configure_dp_tx(struct qmp_combo *qmp); static int qmp_v4_configure_dp_phy(struct qmp_combo *qmp); -static int qmp_v4_calibrate_dp_phy(struct qmp_combo *qmp); static void qmp_v8_dp_aux_init(struct qmp_combo *qmp); static int qmp_v8_configure_dp_clocks(struct qmp_combo *qmp); @@ -2776,7 +2775,6 @@ static const struct qmp_phy_cfg sar2130p_usb3dpphy_cfg = { .configure_dp_tx = qmp_v4_configure_dp_tx, .configure_dp_clocks = qmp_v3_configure_dp_clocks, .configure_dp_phy = qmp_v4_configure_dp_phy, - .calibrate_dp_phy = qmp_v4_calibrate_dp_phy, .regs = qmp_v6_usb3phy_regs_layout, .reset_list = msm8996_usb3phy_reset_l, @@ -2913,7 +2911,6 @@ static const struct qmp_phy_cfg sc8180x_usb3dpphy_cfg = { .configure_dp_tx = qmp_v4_configure_dp_tx, .configure_dp_clocks = qmp_v3_configure_dp_clocks, .configure_dp_phy = qmp_v4_configure_dp_phy, - .calibrate_dp_phy = qmp_v4_calibrate_dp_phy, .reset_list = msm8996_usb3phy_reset_l, .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l), @@ -2960,7 +2957,6 @@ static const struct qmp_phy_cfg sc8280xp_usb43dpphy_cfg = { .configure_dp_tx = qmp_v4_configure_dp_tx, .configure_dp_clocks = qmp_v3_configure_dp_clocks, .configure_dp_phy = qmp_v4_configure_dp_phy, - .calibrate_dp_phy = qmp_v4_calibrate_dp_phy, .reset_list = msm8996_usb3phy_reset_l, .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l), @@ -3006,7 +3002,6 @@ static const struct qmp_phy_cfg x1e80100_usb3dpphy_cfg = { .configure_dp_tx = qmp_v4_configure_dp_tx, .configure_dp_clocks = qmp_v3_configure_dp_clocks, .configure_dp_phy = qmp_v4_configure_dp_phy, - .calibrate_dp_phy = qmp_v4_calibrate_dp_phy, .reset_list = msm8996_usb3phy_reset_l, .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l), @@ -3116,7 +3111,6 @@ static const struct qmp_phy_cfg sm8250_usb3dpphy_cfg = { .configure_dp_tx = qmp_v4_configure_dp_tx, .configure_dp_clocks = qmp_v3_configure_dp_clocks, .configure_dp_phy = qmp_v4_configure_dp_phy, - .calibrate_dp_phy = qmp_v4_calibrate_dp_phy, .reset_list = msm8996_usb3phy_reset_l, .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l), @@ -3165,7 +3159,6 @@ static const struct qmp_phy_cfg sm8350_usb3dpphy_cfg = { .configure_dp_tx = qmp_v4_configure_dp_tx, .configure_dp_clocks = qmp_v3_configure_dp_clocks, .configure_dp_phy = qmp_v4_configure_dp_phy, - .calibrate_dp_phy = qmp_v4_calibrate_dp_phy, .reset_list = msm8996_usb3phy_reset_l, .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l), @@ -3258,7 +3251,6 @@ static const struct qmp_phy_cfg sm8550_usb3dpphy_cfg = { .configure_dp_tx = qmp_v4_configure_dp_tx, .configure_dp_clocks = qmp_v3_configure_dp_clocks, .configure_dp_phy = qmp_v4_configure_dp_phy, - .calibrate_dp_phy = qmp_v4_calibrate_dp_phy, .regs = qmp_v6_usb3phy_regs_layout, .reset_list = msm8996_usb3phy_reset_l, @@ -3304,7 +3296,6 @@ static const struct qmp_phy_cfg sm8650_usb3dpphy_cfg = { .configure_dp_tx = qmp_v4_configure_dp_tx, .configure_dp_clocks = qmp_v3_configure_dp_clocks, .configure_dp_phy = qmp_v4_configure_dp_phy, - .calibrate_dp_phy = qmp_v4_calibrate_dp_phy, .regs = qmp_v6_usb3phy_regs_layout, .reset_list = msm8996_usb3phy_reset_l, @@ -3350,7 +3341,6 @@ static const struct qmp_phy_cfg sm8750_usb3dpphy_cfg = { .configure_dp_tx = qmp_v4_configure_dp_tx, .configure_dp_clocks = qmp_v3_configure_dp_clocks, .configure_dp_phy = qmp_v4_configure_dp_phy, - .calibrate_dp_phy = qmp_v4_calibrate_dp_phy, .regs = qmp_v8_usb3phy_regs_layout, .reset_list = msm8996_usb3phy_reset_l, @@ -3396,7 +3386,6 @@ static const struct qmp_phy_cfg glymur_usb3dpphy_cfg = { .configure_dp_tx = qmp_v4_configure_dp_tx, .configure_dp_clocks = qmp_v8_configure_dp_clocks, .configure_dp_phy = qmp_v8_configure_dp_phy, - .calibrate_dp_phy = qmp_v4_calibrate_dp_phy, .regs = qmp_v8_n3_usb43dpphy_regs_layout, .reset_list = msm8996_usb3phy_reset_l, @@ -3692,7 +3681,6 @@ static void qmp_v4_dp_aux_init(struct qmp_combo *qmp) writel(0x03, qmp->dp_dp_phy + QSERDES_DP_PHY_AUX_CFG7); writel(0xb7, qmp->dp_dp_phy + QSERDES_DP_PHY_AUX_CFG8); writel(0x03, qmp->dp_dp_phy + QSERDES_DP_PHY_AUX_CFG9); - qmp->dp_aux_cfg = 0; writel(PHY_AUX_STOP_ERR_MASK | PHY_AUX_DEC_ERR_MASK | PHY_AUX_SYNC_ERR_MASK | PHY_AUX_ALIGN_ERR_MASK | @@ -3721,7 +3709,6 @@ static void qmp_v8_dp_aux_init(struct qmp_combo *qmp) writel(0x03, qmp->dp_dp_phy + QSERDES_DP_PHY_AUX_CFG7); writel(0xb7, qmp->dp_dp_phy + QSERDES_DP_PHY_AUX_CFG8); writel(0x03, qmp->dp_dp_phy + QSERDES_DP_PHY_AUX_CFG9); - qmp->dp_aux_cfg = 0; writel(PHY_AUX_STOP_ERR_MASK | PHY_AUX_DEC_ERR_MASK | PHY_AUX_SYNC_ERR_MASK | PHY_AUX_ALIGN_ERR_MASK | @@ -3981,24 +3968,6 @@ static int qmp_v8_configure_dp_phy(struct qmp_combo *qmp) return 0; } -/* - * We need to calibrate the aux setting here as many times - * as the caller tries - */ -static int qmp_v4_calibrate_dp_phy(struct qmp_combo *qmp) -{ - static const u8 cfg1_settings[] = { 0x20, 0x13, 0x23, 0x1d }; - u8 val; - - qmp->dp_aux_cfg++; - qmp->dp_aux_cfg %= ARRAY_SIZE(cfg1_settings); - val = cfg1_settings[qmp->dp_aux_cfg]; - - writel(val, qmp->dp_dp_phy + QSERDES_DP_PHY_AUX_CFG1); - - return 0; -} - static int qmp_combo_dp_configure(struct phy *phy, union phy_configure_opts *opts) { const struct phy_configure_opts_dp *dp_opts = &opts->dp; From f0c2c9c58a48b621b1cb90f0821da422b31af814 Mon Sep 17 00:00:00 2001 From: Cristian Ciocaltea Date: Tue, 11 Aug 2026 18:44:42 +0300 Subject: [PATCH 63/76] phy: rockchip: samsung-hdptx: Fix rate recalculation for high bpc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Tested-by: Simon Wright Tested-by: Diederik de Haas # NanoPC-T6 LTS Reviewed-by: Andy Yan Reviewed-by: Dmitry Baryshkov Reviewed-by: Manivannan Sadhasivam Signed-off-by: Cristian Ciocaltea Link: https://patch.msgid.link/20260811-hdptx-clk-fixes-v6-1-75bca0ee5753@collabora.com Signed-off-by: Vinod Koul --- drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c index 2d973bc37f07..710603afff86 100644 --- a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c +++ b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c @@ -2168,7 +2168,7 @@ static u64 rk_hdptx_phy_clk_calc_rate_from_pll_cfg(struct rk_hdptx_phy *hdptx) struct lcpll_config lcpll_hw; struct ropll_config ropll_hw; u64 fout, sdm; - u32 mode, val; + u32 mode, bpc, val; int ret, i; ret = regmap_read(hdptx->regmap, CMN_REG(0008), &mode); @@ -2266,6 +2266,7 @@ static u64 rk_hdptx_phy_clk_calc_rate_from_pll_cfg(struct rk_hdptx_phy *hdptx) if (ret) return 0; ropll_hw.pms_sdiv = ((val & PLL_PCG_POSTDIV_SEL_MASK) >> 4) + 1; + bpc = (FIELD_GET(PLL_PCG_CLK_SEL_MASK, val) << 1) + 8; fout = PLL_REF_CLK * ropll_hw.pms_mdiv; if (ropll_hw.sdm_en) { @@ -2280,7 +2281,7 @@ static u64 rk_hdptx_phy_clk_calc_rate_from_pll_cfg(struct rk_hdptx_phy *hdptx) fout = fout + sdm; } - return div_u64(fout * 2, ropll_hw.pms_sdiv * 10); + return DIV_ROUND_CLOSEST_ULL(fout * 2 * 8, ropll_hw.pms_sdiv * 10 * bpc); } static unsigned long rk_hdptx_phy_clk_recalc_rate(struct clk_hw *hw, @@ -2288,19 +2289,13 @@ static unsigned long rk_hdptx_phy_clk_recalc_rate(struct clk_hw *hw, { struct rk_hdptx_phy *hdptx = to_rk_hdptx_phy(hw); u32 status; - u64 rate; int ret; ret = regmap_read(hdptx->grf, GRF_HDPTX_CON0, &status); if (ret || !(status & HDPTX_I_PLL_EN)) return 0; - rate = rk_hdptx_phy_clk_calc_rate_from_pll_cfg(hdptx); - - if (hdptx->hdmi_cfg.mode == PHY_HDMI_MODE_FRL) - return rate; - - return DIV_ROUND_CLOSEST_ULL(rate * 8, hdptx->hdmi_cfg.bpc); + return rk_hdptx_phy_clk_calc_rate_from_pll_cfg(hdptx); } static int rk_hdptx_phy_clk_determine_rate(struct clk_hw *hw, From c4fe5a9f379055ce0ec930f6495ae62aad237b89 Mon Sep 17 00:00:00 2001 From: Cristian Ciocaltea Date: Tue, 11 Aug 2026 18:44:43 +0300 Subject: [PATCH 64/76] phy: rockchip: samsung-hdptx: Prevent divide-by-zero when computing clk rate Calculating 'sdm' fraction in rk_hdptx_phy_clk_calc_rate_from_pll_cfg() could trigger a divide-by-zero, as it uses div_u64() with a denominator read directly from hardware: the values ropll_hw.sdm_deno, ropll_hw.sdc_deno, ropll_hw.sdc_n, and ropll_hw.sdc_num are populated from PLL registers which, in theory, could be left by the bootloader uninitialized/misconfigured. Provide the necessary sanitization to avoid trusting the hardware state. Reported-by: Sashiko Closes: https://sashiko.dev/#/patchset/20260611-hdptx-clk-fixes-v3-0-67b1b0c00e16@collabora.com?part=1 Fixes: 3481fc04d969 ("phy: rockchip: samsung-hdptx: Compute clk rate from PLL config") Tested-by: Diederik de Haas # NanoPC-T6 LTS Reviewed-by: Dmitry Baryshkov Reviewed-by: Andy Yan Signed-off-by: Cristian Ciocaltea Link: https://patch.msgid.link/20260811-hdptx-clk-fixes-v6-2-75bca0ee5753@collabora.com Signed-off-by: Vinod Koul --- .../phy/rockchip/phy-rockchip-samsung-hdptx.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c index 710603afff86..d67ced2bf7b2 100644 --- a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c +++ b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c @@ -2270,10 +2270,20 @@ static u64 rk_hdptx_phy_clk_calc_rate_from_pll_cfg(struct rk_hdptx_phy *hdptx) fout = PLL_REF_CLK * ropll_hw.pms_mdiv; if (ropll_hw.sdm_en) { + val = 16U * ropll_hw.sdm_deno * + (ropll_hw.sdc_deno * ropll_hw.sdc_n - ropll_hw.sdc_num); + if (!val) { + /* + * The PLL config currently stored in hardware can't be + * translated into a rate. The next .set_rate() should + * program a valid configuration and help with recovery. + */ + dev_dbg(hdptx->dev, "Invalid ROPLL hw state: deno == 0\n"); + return 0; + } + sdm = div_u64(PLL_REF_CLK * ropll_hw.sdc_deno * - ropll_hw.pms_mdiv * ropll_hw.sdm_num, - 16 * ropll_hw.sdm_deno * - (ropll_hw.sdc_deno * ropll_hw.sdc_n - ropll_hw.sdc_num)); + ropll_hw.pms_mdiv * ropll_hw.sdm_num, val); if (ropll_hw.sdm_num_sign) fout = fout - sdm; From 229788526ff24e0607726e93b74dd2ef157a99cd Mon Sep 17 00:00:00 2001 From: Cristian Ciocaltea Date: Tue, 11 Aug 2026 18:44:44 +0300 Subject: [PATCH 65/76] phy: rockchip: samsung-hdptx: Guard against clk rate integer underflow The 'fout' subtraction in rk_hdptx_phy_clk_calc_rate_from_pll_cfg() could result in an integer underflow, if the hardware registers are misconfigured or contain uninitialized values, such that the computed sigma-delta modulator offset sdm exceeds the base frequency fout. This might lead to an absurdly high clock rate being returned to the Common Clock Framework, with unpredictable effects on downstream clk consumers. Provide the necessary sanitization to avoid trusting the hardware state. Reported-by: Sashiko Closes: https://lore.kernel.org/all/20260611235702.0E9691F000E9@smtp.kernel.org/ Fixes: 3481fc04d969 ("phy: rockchip: samsung-hdptx: Compute clk rate from PLL config") Signed-off-by: Cristian Ciocaltea Link: https://patch.msgid.link/20260811-hdptx-clk-fixes-v6-3-75bca0ee5753@collabora.com Signed-off-by: Vinod Koul --- drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c index d67ced2bf7b2..3a65c26f3efa 100644 --- a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c +++ b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c @@ -2285,10 +2285,20 @@ static u64 rk_hdptx_phy_clk_calc_rate_from_pll_cfg(struct rk_hdptx_phy *hdptx) sdm = div_u64(PLL_REF_CLK * ropll_hw.sdc_deno * ropll_hw.pms_mdiv * ropll_hw.sdm_num, val); - if (ropll_hw.sdm_num_sign) + if (ropll_hw.sdm_num_sign) { + if (sdm > fout) { + /* + * Similarly to the case above, it is expected + * the next .set_rate() will help with recovery. + */ + dev_dbg(hdptx->dev, "Invalid ROPLL hw state: sdm > fout\n"); + return 0; + } + fout = fout - sdm; - else + } else { fout = fout + sdm; + } } return DIV_ROUND_CLOSEST_ULL(fout * 2 * 8, ropll_hw.pms_sdiv * 10 * bpc); From 9ac3daadb9060360e937a5993a5fd88360d6fdad Mon Sep 17 00:00:00 2001 From: Cristian Ciocaltea Date: Tue, 11 Aug 2026 18:44:45 +0300 Subject: [PATCH 66/76] phy: rockchip: samsung-hdptx: Fix rate recalculation for 3.2GHz FRL rk_hdptx_phy_clk_calc_rate_from_pll_cfg() is currently unable to handle cascade mode for the 3.2GHz FRL operating mode, as it relies solely on LCPLL_LCVCO_MODE_EN_MASK to determinate the rate from the rk_hdptx_frl_lcpll_cfg array. Since there is no entry for this particular rate, the function returns 0. This is the only rate which requires LC_REF_CLK_SEL to be set in GRF_HDPTX_CON0, hence extend the FRL matching accordingly. Reported-by: Sashiko Closes: https://sashiko.dev/#/patchset/20260611-hdptx-clk-fixes-v3-0-67b1b0c00e16@collabora.com?part=1 Fixes: de5dba833118 ("phy: rockchip: samsung-hdptx: Add HDMI 2.1 FRL support") Tested-by: Diederik de Haas # NanoPC-T6 LTS Reviewed-by: Dmitry Baryshkov Reviewed-by: Andy Yan Reviewed-by: Manivannan Sadhasivam Signed-off-by: Cristian Ciocaltea Link: https://patch.msgid.link/20260811-hdptx-clk-fixes-v6-4-75bca0ee5753@collabora.com Signed-off-by: Vinod Koul --- .../phy/rockchip/phy-rockchip-samsung-hdptx.c | 33 ++++++++++++++----- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c index 3a65c26f3efa..d7d8414fdddd 100644 --- a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c +++ b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c @@ -2206,16 +2206,31 @@ static u64 rk_hdptx_phy_clk_calc_rate_from_pll_cfg(struct rk_hdptx_phy *hdptx) return 0; lcpll_hw.sdc_n = (val & LCPLL_SDC_N_MASK) >> 1; - for (i = 0; i < ARRAY_SIZE(rk_hdptx_frl_lcpll_cfg); i++) { - const struct lcpll_config *cfg = &rk_hdptx_frl_lcpll_cfg[i]; + ret = regmap_read(hdptx->grf, GRF_HDPTX_CON0, &val); + if (ret) + return 0; - if (cfg->pms_mdiv == lcpll_hw.pms_mdiv && - cfg->pms_sdiv == lcpll_hw.pms_sdiv && - cfg->sdm_num_sign == lcpll_hw.sdm_num_sign && - cfg->sdm_num == lcpll_hw.sdm_num && - cfg->sdm_deno == lcpll_hw.sdm_deno && - cfg->sdc_n == lcpll_hw.sdc_n) - return cfg->rate; + if (val & LC_REF_CLK_SEL) { + if (lcpll_hw.pms_mdiv == 0x6b && + lcpll_hw.sdm_num_sign == 0x01 && + lcpll_hw.sdm_num == 0x02 && + lcpll_hw.sdm_deno == 0x09 && + lcpll_hw.sdc_n == FIELD_GET(LCPLL_SDC_N_MASK, 0x02)) + return FRL_8G4L_RATE; + } else { + const struct lcpll_config *cfg; + + for (i = 0; i < ARRAY_SIZE(rk_hdptx_frl_lcpll_cfg); i++) { + cfg = &rk_hdptx_frl_lcpll_cfg[i]; + + if (cfg->pms_mdiv == lcpll_hw.pms_mdiv && + cfg->pms_sdiv == lcpll_hw.pms_sdiv && + cfg->sdm_num_sign == lcpll_hw.sdm_num_sign && + cfg->sdm_num == lcpll_hw.sdm_num && + cfg->sdm_deno == lcpll_hw.sdm_deno && + cfg->sdc_n == lcpll_hw.sdc_n) + return cfg->rate; + } } dev_dbg(hdptx->dev, "%s no FRL match found\n", __func__); From aec3e4ce25da4ed37bbbfedcbb7107ae9428b183 Mon Sep 17 00:00:00 2001 From: Cristian Ciocaltea Date: Tue, 11 Aug 2026 18:44:46 +0300 Subject: [PATCH 67/76] phy: rockchip: samsung-hdptx: Handle uncommitted PHY config changes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Any changes to the PHY link rate and/or color depth done via the HDMI PHY configuration API are not immediately programmed into the hardware, but are delayed until the PHY usage count gets incremented from 0 to 1, that is when it is powered on or when the PLL clock exposed through the CCF API is prepared, whichever comes first. Since the clock might remain in prepared state after subsequent PHY config changes, the programming can also be triggered via clk_ops.set_rate(). However, from the clock consumer perspective (i.e. VOP2 display controller), the (pixel) clock rate doesn't vary with bpc, as that is handled internally by the PHY and reflected in the TDMS character rate only. As a consequence, changing the bpc while preserving the modeline may lead to out-of-sync issues between CCF and HDMI PHY config state, because the .set_rate() callback is not invoked when clock rate remains constant. This may also happen when the PHY PLL has been pre-programmed by an external entity, e.g. the bootloader, which is actually a regression introduced by the recent FRL-related changes. Introduce a pll_config_dirty flag to keep track of uncommitted PHY config changes and use it in clk_ops.determine_rate() to invalidate the current clock rate (as known by CCF) and, consequently, ensure those changes are programmed into hardware via clk_ops.set_rate(). Moreover, proceed with a similar fix in phy_ops.power_on() callback, to handle the scenario where the CCF API is not used due to operating in FRL mode, while the clock is still in a prepared state and thus preventing rk_hdptx_phy_consumer_get() to apply the updated PHY configuration. Fixes: de5dba833118 ("phy: rockchip: samsung-hdptx: Add HDMI 2.1 FRL support") Fixes: 9d0ec51d7c22 ("phy: rockchip: samsung-hdptx: Add high color depth management") Tested-by: Thomas Niederprüm Tested-by: Simon Wright Tested-by: Diederik de Haas # NanoPC-T6 LTS Reviewed-by: Andy Yan Reviewed-by: Manivannan Sadhasivam Signed-off-by: Cristian Ciocaltea Link: https://patch.msgid.link/20260811-hdptx-clk-fixes-v6-5-75bca0ee5753@collabora.com Signed-off-by: Vinod Koul --- .../phy/rockchip/phy-rockchip-samsung-hdptx.c | 84 +++++++++++-------- 1 file changed, 48 insertions(+), 36 deletions(-) diff --git a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c index d7d8414fdddd..c24e7f284170 100644 --- a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c +++ b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c @@ -413,6 +413,7 @@ struct rk_hdptx_phy { /* clk provider */ struct clk_hw hw; + bool pll_config_dirty; bool restrict_rate_change; atomic_t usage_count; @@ -1260,13 +1261,19 @@ static int rk_hdptx_tmds_ropll_cmn_config(struct rk_hdptx_phy *hdptx) static int rk_hdptx_pll_cmn_config(struct rk_hdptx_phy *hdptx) { + int ret; + if (hdptx->hdmi_cfg.rate <= HDMI20_MAX_RATE) - return rk_hdptx_tmds_ropll_cmn_config(hdptx); + ret = rk_hdptx_tmds_ropll_cmn_config(hdptx); + else if (hdptx->hdmi_cfg.rate == FRL_8G4L_RATE) + ret = rk_hdptx_frl_lcpll_ropll_cmn_config(hdptx); + else + ret = rk_hdptx_frl_lcpll_cmn_config(hdptx); - if (hdptx->hdmi_cfg.rate == FRL_8G4L_RATE) - return rk_hdptx_frl_lcpll_ropll_cmn_config(hdptx); + if (!ret) + hdptx->pll_config_dirty = false; - return rk_hdptx_frl_lcpll_cmn_config(hdptx); + return ret; } static int rk_hdptx_frl_lcpll_mode_config(struct rk_hdptx_phy *hdptx) @@ -1347,25 +1354,22 @@ static int rk_hdptx_phy_consumer_get(struct rk_hdptx_phy *hdptx) return 0; ret = regmap_read(hdptx->grf, GRF_HDPTX_STATUS, &status); - if (ret) - goto dec_usage; - - if (status & HDPTX_O_PLL_LOCK_DONE) - dev_warn(hdptx->dev, "PLL locked by unknown consumer!\n"); + if (ret) { + atomic_dec(&hdptx->usage_count); + return ret; + } if (mode == PHY_MODE_DP) { rk_hdptx_dp_reset(hdptx); } else { - ret = rk_hdptx_pll_cmn_config(hdptx); - if (ret) - goto dec_usage; + /* + * Ignore PLL config errors at this point as pll_config_dirty + * was not reset and, therefore, operation will be retried. + */ + rk_hdptx_pll_cmn_config(hdptx); } return 0; - -dec_usage: - atomic_dec(&hdptx->usage_count); - return ret; } static int rk_hdptx_phy_consumer_put(struct rk_hdptx_phy *hdptx, bool force) @@ -1700,13 +1704,18 @@ static int rk_hdptx_phy_power_on(struct phy *phy) if (ret) rk_hdptx_phy_consumer_put(hdptx, true); } else { - regmap_write(hdptx->grf, GRF_HDPTX_CON0, - HDPTX_MODE_SEL << 16 | FIELD_PREP(HDPTX_MODE_SEL, 0x0)); + if (hdptx->pll_config_dirty) + ret = rk_hdptx_pll_cmn_config(hdptx); - if (hdptx->hdmi_cfg.mode == PHY_HDMI_MODE_FRL) - ret = rk_hdptx_frl_lcpll_mode_config(hdptx); - else - ret = rk_hdptx_tmds_ropll_mode_config(hdptx); + if (!ret) { + regmap_write(hdptx->grf, GRF_HDPTX_CON0, + HDPTX_MODE_SEL << 16 | FIELD_PREP(HDPTX_MODE_SEL, 0x0)); + + if (hdptx->hdmi_cfg.mode == PHY_HDMI_MODE_FRL) + ret = rk_hdptx_frl_lcpll_mode_config(hdptx); + else + ret = rk_hdptx_tmds_ropll_mode_config(hdptx); + } if (ret) rk_hdptx_phy_consumer_put(hdptx, true); @@ -2081,7 +2090,10 @@ static int rk_hdptx_phy_configure(struct phy *phy, union phy_configure_opts *opt dev_err(hdptx->dev, "invalid hdmi params for phy configure\n"); } else { hdptx->restrict_rate_change = true; - dev_dbg(hdptx->dev, "%s rate=%llu bpc=%u\n", __func__, + hdptx->pll_config_dirty = true; + + dev_dbg(hdptx->dev, "%s %s rate=%llu bpc=%u\n", __func__, + hdptx->hdmi_cfg.mode ? "FRL" : "TMDS", hdptx->hdmi_cfg.rate, hdptx->hdmi_cfg.bpc); } @@ -2338,8 +2350,19 @@ static int rk_hdptx_phy_clk_determine_rate(struct clk_hw *hw, { struct rk_hdptx_phy *hdptx = to_rk_hdptx_phy(hw); - if (hdptx->hdmi_cfg.mode == PHY_HDMI_MODE_FRL) - return hdptx->hdmi_cfg.rate; + /* + * Invalidate current clock rate to ensure rk_hdptx_phy_clk_set_rate() + * will be invoked to commit PLL configuration. + */ + if (hdptx->pll_config_dirty) { + req->rate = 0; + return 0; + } + + if (hdptx->hdmi_cfg.mode == PHY_HDMI_MODE_FRL) { + req->rate = hdptx->hdmi_cfg.rate; + return 0; + } /* * FIXME: Temporarily allow altering TMDS char rate via CCF. @@ -2371,17 +2394,6 @@ static int rk_hdptx_phy_clk_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { struct rk_hdptx_phy *hdptx = to_rk_hdptx_phy(hw); - unsigned long long link_rate = rate; - - if (hdptx->hdmi_cfg.mode != PHY_HDMI_MODE_FRL) - link_rate = DIV_ROUND_CLOSEST_ULL(rate * hdptx->hdmi_cfg.bpc, 8); - - /* Revert any unlikely link rate change since determine_rate() */ - if (hdptx->hdmi_cfg.rate != link_rate) { - dev_warn(hdptx->dev, "Reverting unexpected rate change from %llu to %llu\n", - link_rate, hdptx->hdmi_cfg.rate); - hdptx->hdmi_cfg.rate = link_rate; - } /* * The link rate would be normally programmed in HW during From c2dd42e3ebd76f24bdc21eafe17149bec2ce1394 Mon Sep 17 00:00:00 2001 From: Cristian Ciocaltea Date: Tue, 11 Aug 2026 18:44:47 +0300 Subject: [PATCH 68/76] phy: rockchip: samsung-hdptx: Drop TMDS rate setup workaround MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit ba9c2fe18c17 ("drm/rockchip: dw_hdmi_qp: Switch to phy_configure()") the TMDS rate setup doesn't rely anymore on the unconventional usage of the bus width, instead it is managed exclusively through the HDMI PHY configuration API. Drop the now obsolete workaround to retrieve the TMDS character rate via phy_get_bus_width() during power_on(). Tested-by: Thomas Niederprüm Tested-by: Simon Wright Tested-by: Diederik de Haas # NanoPC-T6 LTS Reviewed-by: Andy Yan Reviewed-by: Manivannan Sadhasivam Signed-off-by: Cristian Ciocaltea Link: https://patch.msgid.link/20260811-hdptx-clk-fixes-v6-6-75bca0ee5753@collabora.com Signed-off-by: Vinod Koul --- .../phy/rockchip/phy-rockchip-samsung-hdptx.c | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c index c24e7f284170..6941dac9deb4 100644 --- a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c +++ b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c @@ -1660,22 +1660,6 @@ static int rk_hdptx_phy_power_on(struct phy *phy) enum phy_mode mode = phy_get_mode(phy); int ret, lane; - if (mode != PHY_MODE_DP) { - if (!hdptx->hdmi_cfg.rate && hdptx->hdmi_cfg.mode != PHY_HDMI_MODE_FRL) { - /* - * FIXME: Temporary workaround to setup TMDS char rate - * from the RK DW HDMI QP bridge driver. - * Will be removed as soon the switch to the HDMI PHY - * configuration API has been completed on both ends. - */ - hdptx->hdmi_cfg.rate = phy_get_bus_width(hdptx->phy) & 0xfffffff; - hdptx->hdmi_cfg.rate *= 100; - } - - dev_dbg(hdptx->dev, "%s rate=%llu bpc=%u\n", __func__, - hdptx->hdmi_cfg.rate, hdptx->hdmi_cfg.bpc); - } - ret = rk_hdptx_phy_consumer_get(hdptx); if (ret) return ret; @@ -1704,6 +1688,9 @@ static int rk_hdptx_phy_power_on(struct phy *phy) if (ret) rk_hdptx_phy_consumer_put(hdptx, true); } else { + dev_dbg(hdptx->dev, "%s rate=%llu bpc=%u\n", __func__, + hdptx->hdmi_cfg.rate, hdptx->hdmi_cfg.bpc); + if (hdptx->pll_config_dirty) ret = rk_hdptx_pll_cmn_config(hdptx); From e49e4bc9f90eeb6b672762c6f089e2fafec104c4 Mon Sep 17 00:00:00 2001 From: Cristian Ciocaltea Date: Tue, 11 Aug 2026 18:44:48 +0300 Subject: [PATCH 69/76] phy: rockchip: samsung-hdptx: Consolidate consumer_put on error path rk_hdptx_phy_consumer_put() is invoked in both branches of the mode check conditional in rk_hdptx_phy_power_on() on the error path. Simplify the code by moving the single call to the end of the function. No functional change intended. Reviewed-by: Manivannan Sadhasivam Signed-off-by: Cristian Ciocaltea Link: https://patch.msgid.link/20260811-hdptx-clk-fixes-v6-7-75bca0ee5753@collabora.com Signed-off-by: Vinod Koul --- drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c index 6941dac9deb4..294484da7085 100644 --- a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c +++ b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c @@ -1685,8 +1685,6 @@ static int rk_hdptx_phy_power_on(struct phy *phy) rk_hdptx_dp_pll_init(hdptx); ret = rk_hdptx_dp_aux_init(hdptx); - if (ret) - rk_hdptx_phy_consumer_put(hdptx, true); } else { dev_dbg(hdptx->dev, "%s rate=%llu bpc=%u\n", __func__, hdptx->hdmi_cfg.rate, hdptx->hdmi_cfg.bpc); @@ -1703,11 +1701,11 @@ static int rk_hdptx_phy_power_on(struct phy *phy) else ret = rk_hdptx_tmds_ropll_mode_config(hdptx); } - - if (ret) - rk_hdptx_phy_consumer_put(hdptx, true); } + if (ret) + rk_hdptx_phy_consumer_put(hdptx, true); + return ret; } From 1bb1b4769e43ecc8ed30bd30b6d4514614753f2c Mon Sep 17 00:00:00 2001 From: Cristian Ciocaltea Date: Tue, 11 Aug 2026 18:44:49 +0300 Subject: [PATCH 70/76] phy: rockchip: samsung-hdptx: Drop restrict_rate_change handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit 6efbd0f46dd8 ("phy: rockchip: samsung-hdptx: Restrict altering TMDS char rate via CCF"), adjusting the rate via the Common Clock Framework API has been disallowed. To avoid breaking existing users until switching to the PHY config API, it introduced a temporary exception to the rule, controlled via the 'restrict_rate_change' flag. As the API transition completed, remove the now deprecated exception logic. Tested-by: Thomas Niederprüm Tested-by: Simon Wright Tested-by: Diederik de Haas # NanoPC-T6 LTS Reviewed-by: Dmitry Baryshkov Reviewed-by: Manivannan Sadhasivam Signed-off-by: Cristian Ciocaltea Link: https://patch.msgid.link/20260811-hdptx-clk-fixes-v6-8-75bca0ee5753@collabora.com Signed-off-by: Vinod Koul --- .../phy/rockchip/phy-rockchip-samsung-hdptx.c | 42 ++++--------------- 1 file changed, 8 insertions(+), 34 deletions(-) diff --git a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c index 294484da7085..cf79bd4752c8 100644 --- a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c +++ b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c @@ -414,7 +414,6 @@ struct rk_hdptx_phy { /* clk provider */ struct clk_hw hw; bool pll_config_dirty; - bool restrict_rate_change; atomic_t usage_count; @@ -2074,7 +2073,6 @@ static int rk_hdptx_phy_configure(struct phy *phy, union phy_configure_opts *opt if (ret) { dev_err(hdptx->dev, "invalid hdmi params for phy configure\n"); } else { - hdptx->restrict_rate_change = true; hdptx->pll_config_dirty = true; dev_dbg(hdptx->dev, "%s %s rate=%llu bpc=%u\n", __func__, @@ -2336,41 +2334,17 @@ static int rk_hdptx_phy_clk_determine_rate(struct clk_hw *hw, struct rk_hdptx_phy *hdptx = to_rk_hdptx_phy(hw); /* - * Invalidate current clock rate to ensure rk_hdptx_phy_clk_set_rate() - * will be invoked to commit PLL configuration. + * For uncommitted PLL configuration, invalidate the current clock rate + * to ensure rk_hdptx_phy_clk_set_rate() will be always invoked. + * Otherwise, restrict the rate according to the PHY link setup. */ - if (hdptx->pll_config_dirty) { + if (hdptx->pll_config_dirty) req->rate = 0; - return 0; - } - - if (hdptx->hdmi_cfg.mode == PHY_HDMI_MODE_FRL) { + else if (hdptx->hdmi_cfg.mode == PHY_HDMI_MODE_FRL) req->rate = hdptx->hdmi_cfg.rate; - return 0; - } - - /* - * FIXME: Temporarily allow altering TMDS char rate via CCF. - * To be dropped as soon as the RK DW HDMI QP bridge driver - * switches to make use of phy_configure(). - */ - if (!hdptx->restrict_rate_change && req->rate != hdptx->hdmi_cfg.rate) { - struct phy_configure_opts_hdmi hdmi = { - .tmds_char_rate = req->rate, - }; - - int ret = rk_hdptx_phy_verify_hdmi_config(hdptx, &hdmi, &hdptx->hdmi_cfg); - - if (ret) - return ret; - } - - /* - * The TMDS char rate shall be adjusted via phy_configure() only, - * hence ensure rk_hdptx_phy_clk_set_rate() won't be invoked with - * a different rate argument. - */ - req->rate = DIV_ROUND_CLOSEST_ULL(hdptx->hdmi_cfg.rate * 8, hdptx->hdmi_cfg.bpc); + else + req->rate = DIV_ROUND_CLOSEST_ULL(hdptx->hdmi_cfg.rate * 8, + hdptx->hdmi_cfg.bpc); return 0; } From 859fd8ae691e8c89c881da5edf0a827531daa372 Mon Sep 17 00:00:00 2001 From: Cristian Ciocaltea Date: Tue, 11 Aug 2026 18:44:50 +0300 Subject: [PATCH 71/76] phy: rockchip: samsung-hdptx: Simplify GRF access with FIELD_PREP_WM16() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 16 most significant bits of the general-purpose register (GRF) are used as a write-enable mask for the remaining 16 bits. Make use of the recently introduced FIELD_PREP_WM16() macro to avoid open-coding the bit shift operations and improve code readability. Tested-by: Thomas Niederprüm Tested-by: Simon Wright Tested-by: Diederik de Haas # NanoPC-T6 LTS Reviewed-by: Dmitry Baryshkov Reviewed-by: Manivannan Sadhasivam Signed-off-by: Cristian Ciocaltea Link: https://patch.msgid.link/20260811-hdptx-clk-fixes-v6-9-75bca0ee5753@collabora.com Signed-off-by: Vinod Koul --- .../phy/rockchip/phy-rockchip-samsung-hdptx.c | 52 +++++++++---------- 1 file changed, 25 insertions(+), 27 deletions(-) diff --git a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c index cf79bd4752c8..d48a20fef93f 100644 --- a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c +++ b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0+ /* * Copyright (c) 2021-2022 Rockchip Electronics Co., Ltd. - * Copyright (c) 2024 Collabora Ltd. + * Copyright (c) 2024-2026 Collabora Ltd. * * Author: Algea Cao * Author: Cristian Ciocaltea @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -949,7 +950,9 @@ static void rk_hdptx_pre_power_up(struct rk_hdptx_phy *hdptx) reset_control_assert(hdptx->rsts[RST_CMN].rstc); reset_control_assert(hdptx->rsts[RST_INIT].rstc); - val = (HDPTX_I_PLL_EN | HDPTX_I_BIAS_EN | HDPTX_I_BGR_EN) << 16; + val = (FIELD_PREP_WM16(HDPTX_I_PLL_EN, 0) | + FIELD_PREP_WM16(HDPTX_I_BIAS_EN, 0) | + FIELD_PREP_WM16(HDPTX_I_BGR_EN, 0)); regmap_write(hdptx->grf, GRF_HDPTX_CON0, val); } @@ -960,8 +963,8 @@ static int rk_hdptx_post_enable_lane(struct rk_hdptx_phy *hdptx) reset_control_deassert(hdptx->rsts[RST_LANE].rstc); - val = (HDPTX_I_BIAS_EN | HDPTX_I_BGR_EN) << 16 | - HDPTX_I_BIAS_EN | HDPTX_I_BGR_EN; + val = (FIELD_PREP_WM16(HDPTX_I_BIAS_EN, 1) | + FIELD_PREP_WM16(HDPTX_I_BGR_EN, 1)); regmap_write(hdptx->grf, GRF_HDPTX_CON0, val); /* 3 lanes FRL mode */ @@ -990,16 +993,15 @@ static int rk_hdptx_post_enable_pll(struct rk_hdptx_phy *hdptx) u32 val; int ret; - val = (HDPTX_I_BIAS_EN | HDPTX_I_BGR_EN) << 16 | - HDPTX_I_BIAS_EN | HDPTX_I_BGR_EN; + val = (FIELD_PREP_WM16(HDPTX_I_BIAS_EN, 1) | + FIELD_PREP_WM16(HDPTX_I_BGR_EN, 1)); regmap_write(hdptx->grf, GRF_HDPTX_CON0, val); usleep_range(10, 15); reset_control_deassert(hdptx->rsts[RST_INIT].rstc); usleep_range(10, 15); - val = HDPTX_I_PLL_EN << 16 | HDPTX_I_PLL_EN; - regmap_write(hdptx->grf, GRF_HDPTX_CON0, val); + regmap_write(hdptx->grf, GRF_HDPTX_CON0, FIELD_PREP_WM16(HDPTX_I_PLL_EN, 1)); usleep_range(10, 15); reset_control_deassert(hdptx->rsts[RST_CMN].rstc); @@ -1037,7 +1039,9 @@ static void rk_hdptx_phy_disable(struct rk_hdptx_phy *hdptx) reset_control_assert(hdptx->rsts[RST_CMN].rstc); reset_control_assert(hdptx->rsts[RST_INIT].rstc); - val = (HDPTX_I_PLL_EN | HDPTX_I_BIAS_EN | HDPTX_I_BGR_EN) << 16; + val = (FIELD_PREP_WM16(HDPTX_I_PLL_EN, 0) | + FIELD_PREP_WM16(HDPTX_I_BIAS_EN, 0) | + FIELD_PREP_WM16(HDPTX_I_BGR_EN, 0)); regmap_write(hdptx->grf, GRF_HDPTX_CON0, val); } @@ -1135,7 +1139,7 @@ static int rk_hdptx_frl_lcpll_cmn_config(struct rk_hdptx_phy *hdptx) rk_hdptx_pre_power_up(hdptx); - regmap_write(hdptx->grf, GRF_HDPTX_CON0, LC_REF_CLK_SEL << 16); + regmap_write(hdptx->grf, GRF_HDPTX_CON0, FIELD_PREP_WM16(LC_REF_CLK_SEL, 0)); rk_hdptx_multi_reg_write(hdptx, rk_hdptx_common_cmn_init_seq); rk_hdptx_multi_reg_write(hdptx, rk_hdptx_frl_lcpll_cmn_init_seq); @@ -1178,8 +1182,7 @@ static int rk_hdptx_frl_lcpll_ropll_cmn_config(struct rk_hdptx_phy *hdptx) rk_hdptx_pre_power_up(hdptx); /* ROPLL input reference clock from LCPLL (cascade mode) */ - regmap_write(hdptx->grf, GRF_HDPTX_CON0, - (LC_REF_CLK_SEL << 16) | LC_REF_CLK_SEL); + regmap_write(hdptx->grf, GRF_HDPTX_CON0, FIELD_PREP_WM16(LC_REF_CLK_SEL, 1)); rk_hdptx_multi_reg_write(hdptx, rk_hdptx_common_cmn_init_seq); rk_hdptx_multi_reg_write(hdptx, rk_hdptx_frl_lcpll_ropll_cmn_init_seq); @@ -1218,7 +1221,7 @@ static int rk_hdptx_tmds_ropll_cmn_config(struct rk_hdptx_phy *hdptx) rk_hdptx_pre_power_up(hdptx); - regmap_write(hdptx->grf, GRF_HDPTX_CON0, LC_REF_CLK_SEL << 16); + regmap_write(hdptx->grf, GRF_HDPTX_CON0, FIELD_PREP_WM16(LC_REF_CLK_SEL, 0)); rk_hdptx_multi_reg_write(hdptx, rk_hdptx_common_cmn_init_seq); rk_hdptx_multi_reg_write(hdptx, rk_hdptx_tmds_cmn_init_seq); @@ -1336,11 +1339,9 @@ static void rk_hdptx_dp_reset(struct rk_hdptx_phy *hdptx) FIELD_PREP(LN_TX_DRV_EI_EN_MASK, 0)); regmap_write(hdptx->grf, GRF_HDPTX_CON0, - HDPTX_I_PLL_EN << 16 | FIELD_PREP(HDPTX_I_PLL_EN, 0x0)); - regmap_write(hdptx->grf, GRF_HDPTX_CON0, - HDPTX_I_BIAS_EN << 16 | FIELD_PREP(HDPTX_I_BIAS_EN, 0x0)); - regmap_write(hdptx->grf, GRF_HDPTX_CON0, - HDPTX_I_BGR_EN << 16 | FIELD_PREP(HDPTX_I_BGR_EN, 0x0)); + FIELD_PREP_WM16(HDPTX_I_PLL_EN, 0) | + FIELD_PREP_WM16(HDPTX_I_BIAS_EN, 0) | + FIELD_PREP_WM16(HDPTX_I_BGR_EN, 0)); } static int rk_hdptx_phy_consumer_get(struct rk_hdptx_phy *hdptx) @@ -1616,9 +1617,8 @@ static int rk_hdptx_dp_aux_init(struct rk_hdptx_phy *hdptx) FIELD_PREP(OVRD_SB_VREG_EN_MASK, 0x1)); regmap_write(hdptx->grf, GRF_HDPTX_CON0, - HDPTX_I_BGR_EN << 16 | FIELD_PREP(HDPTX_I_BGR_EN, 0x1)); - regmap_write(hdptx->grf, GRF_HDPTX_CON0, - HDPTX_I_BIAS_EN << 16 | FIELD_PREP(HDPTX_I_BIAS_EN, 0x1)); + FIELD_PREP_WM16(HDPTX_I_BGR_EN, 1) | + FIELD_PREP_WM16(HDPTX_I_BIAS_EN, 1)); usleep_range(20, 25); reset_control_deassert(hdptx->rsts[RST_INIT].rstc); @@ -1665,7 +1665,7 @@ static int rk_hdptx_phy_power_on(struct phy *phy) if (mode == PHY_MODE_DP) { regmap_write(hdptx->grf, GRF_HDPTX_CON0, - HDPTX_MODE_SEL << 16 | FIELD_PREP(HDPTX_MODE_SEL, 0x1)); + FIELD_PREP_WM16(HDPTX_MODE_SEL, 1)); for (lane = 0; lane < 4; lane++) { regmap_update_bits(hdptx->regmap, LANE_REG(031e) + 0x400 * lane, @@ -1693,7 +1693,7 @@ static int rk_hdptx_phy_power_on(struct phy *phy) if (!ret) { regmap_write(hdptx->grf, GRF_HDPTX_CON0, - HDPTX_MODE_SEL << 16 | FIELD_PREP(HDPTX_MODE_SEL, 0x0)); + FIELD_PREP_WM16(HDPTX_MODE_SEL, 0)); if (hdptx->hdmi_cfg.mode == PHY_HDMI_MODE_FRL) ret = rk_hdptx_frl_lcpll_mode_config(hdptx); @@ -1828,8 +1828,7 @@ static int rk_hdptx_phy_set_rate(struct rk_hdptx_phy *hdptx, u32 bw, status; int ret; - regmap_write(hdptx->grf, GRF_HDPTX_CON0, - HDPTX_I_PLL_EN << 16 | FIELD_PREP(HDPTX_I_PLL_EN, 0x0)); + regmap_write(hdptx->grf, GRF_HDPTX_CON0, FIELD_PREP_WM16(HDPTX_I_PLL_EN, 0)); switch (dp->link_rate) { case 1620: @@ -1885,8 +1884,7 @@ static int rk_hdptx_phy_set_rate(struct rk_hdptx_phy *hdptx, regmap_update_bits(hdptx->regmap, CMN_REG(0095), DP_TX_LINK_BW_MASK, FIELD_PREP(DP_TX_LINK_BW_MASK, bw)); - regmap_write(hdptx->grf, GRF_HDPTX_CON0, - HDPTX_I_PLL_EN << 16 | FIELD_PREP(HDPTX_I_PLL_EN, 0x1)); + regmap_write(hdptx->grf, GRF_HDPTX_CON0, FIELD_PREP_WM16(HDPTX_I_PLL_EN, 1)); ret = regmap_read_poll_timeout(hdptx->grf, GRF_HDPTX_STATUS, status, FIELD_GET(HDPTX_O_PLL_LOCK_DONE, status), From f3a8aba9c36f26b5bb1024653d9083f7e1e6d8c8 Mon Sep 17 00:00:00 2001 From: Cristian Ciocaltea Date: Tue, 11 Aug 2026 18:44:51 +0300 Subject: [PATCH 72/76] phy: rockchip: samsung-hdptx: Consistently use bitfield macros MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make the code more robust and improve readability by using the available bitfield macros (e.g. FIELD_PREP, FIELD_GET) whenever possible, instead of open coding the related bit operations. Tested-by: Thomas Niederprüm Tested-by: Simon Wright Tested-by: Diederik de Haas # NanoPC-T6 LTS Reviewed-by: Dmitry Baryshkov Reviewed-by: Manivannan Sadhasivam Signed-off-by: Cristian Ciocaltea Link: https://patch.msgid.link/20260811-hdptx-clk-fixes-v6-10-75bca0ee5753@collabora.com Signed-off-by: Vinod Koul --- .../phy/rockchip/phy-rockchip-samsung-hdptx.c | 30 ++++++++++++------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c index d48a20fef93f..24ed0d7eb6f8 100644 --- a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c +++ b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c @@ -53,6 +53,12 @@ /* CMN_REG(001e) */ #define LCPLL_PI_EN_MASK BIT(5) #define LCPLL_100M_CLK_EN_MASK BIT(0) +/* CMN_REG(0022) */ +#define ANA_LCPLL_PMS_PDIV_MASK GENMASK(7, 4) +#define ANA_LCPLL_PMS_REFDIV_MASK GENMASK(3, 0) +/* CMN_REG(0023) */ +#define LCPLL_PMS_SDIV_RBR_MASK GENMASK(7, 4) +#define LCPLL_PMS_SDIV_HBR_MASK GENMASK(3, 0) /* CMN_REG(0025) */ #define LCPLL_PMS_IQDIV_RSTN_MASK BIT(4) /* CMN_REG(0028) */ @@ -1157,9 +1163,11 @@ static int rk_hdptx_frl_lcpll_cmn_config(struct rk_hdptx_phy *hdptx) regmap_write(hdptx->regmap, CMN_REG(0020), cfg->pms_mdiv); regmap_write(hdptx->regmap, CMN_REG(0021), cfg->pms_mdiv_afc); regmap_write(hdptx->regmap, CMN_REG(0022), - (cfg->pms_pdiv << 4) | cfg->pms_refdiv); + FIELD_PREP(ANA_LCPLL_PMS_PDIV_MASK, cfg->pms_pdiv) | + FIELD_PREP(ANA_LCPLL_PMS_REFDIV_MASK, cfg->pms_refdiv)); regmap_write(hdptx->regmap, CMN_REG(0023), - (cfg->pms_sdiv << 4) | cfg->pms_sdiv); + FIELD_PREP(LCPLL_PMS_SDIV_RBR_MASK, cfg->pms_sdiv) | + FIELD_PREP(LCPLL_PMS_SDIV_HBR_MASK, cfg->pms_sdiv)); regmap_write(hdptx->regmap, CMN_REG(002a), cfg->sdm_deno); regmap_write(hdptx->regmap, CMN_REG(002b), cfg->sdm_num_sign); regmap_write(hdptx->regmap, CMN_REG(002c), cfg->sdm_num); @@ -1229,8 +1237,10 @@ static int rk_hdptx_tmds_ropll_cmn_config(struct rk_hdptx_phy *hdptx) regmap_write(hdptx->regmap, CMN_REG(0051), cfg->pms_mdiv); regmap_write(hdptx->regmap, CMN_REG(0055), cfg->pms_mdiv_afc); regmap_write(hdptx->regmap, CMN_REG(0059), - (cfg->pms_pdiv << 4) | cfg->pms_refdiv); - regmap_write(hdptx->regmap, CMN_REG(005a), cfg->pms_sdiv << 4); + FIELD_PREP(ANA_ROPLL_PMS_PDIV_MASK, cfg->pms_pdiv) | + FIELD_PREP(ANA_ROPLL_PMS_REFDIV_MASK, cfg->pms_refdiv)); + regmap_write(hdptx->regmap, CMN_REG(005a), + FIELD_PREP(ROPLL_PMS_SDIV_RBR_MASK, cfg->pms_sdiv)); regmap_update_bits(hdptx->regmap, CMN_REG(005e), ROPLL_SDM_EN_MASK, FIELD_PREP(ROPLL_SDM_EN_MASK, cfg->sdm_en)); @@ -2177,7 +2187,7 @@ static u64 rk_hdptx_phy_clk_calc_rate_from_pll_cfg(struct rk_hdptx_phy *hdptx) ret = regmap_read(hdptx->regmap, CMN_REG(0023), &val); if (ret) return 0; - lcpll_hw.pms_sdiv = val & 0xf; + lcpll_hw.pms_sdiv = FIELD_GET(LCPLL_PMS_SDIV_HBR_MASK, val); ret = regmap_read(hdptx->regmap, CMN_REG(002B), &val); if (ret) @@ -2197,7 +2207,7 @@ static u64 rk_hdptx_phy_clk_calc_rate_from_pll_cfg(struct rk_hdptx_phy *hdptx) ret = regmap_read(hdptx->regmap, CMN_REG(002D), &val); if (ret) return 0; - lcpll_hw.sdc_n = (val & LCPLL_SDC_N_MASK) >> 1; + lcpll_hw.sdc_n = FIELD_GET(LCPLL_SDC_N_MASK, val); ret = regmap_read(hdptx->grf, GRF_HDPTX_CON0, &val); if (ret) @@ -2238,12 +2248,12 @@ static u64 rk_hdptx_phy_clk_calc_rate_from_pll_cfg(struct rk_hdptx_phy *hdptx) ret = regmap_read(hdptx->regmap, CMN_REG(005E), &val); if (ret) return 0; - ropll_hw.sdm_en = val & ROPLL_SDM_EN_MASK; + ropll_hw.sdm_en = FIELD_GET(ROPLL_SDM_EN_MASK, val); ret = regmap_read(hdptx->regmap, CMN_REG(0064), &val); if (ret) return 0; - ropll_hw.sdm_num_sign = val & ROPLL_SDM_NUM_SIGN_RBR_MASK; + ropll_hw.sdm_num_sign = FIELD_GET(ROPLL_SDM_NUM_SIGN_RBR_MASK, val); ret = regmap_read(hdptx->regmap, CMN_REG(0065), &val); if (ret) @@ -2258,7 +2268,7 @@ static u64 rk_hdptx_phy_clk_calc_rate_from_pll_cfg(struct rk_hdptx_phy *hdptx) ret = regmap_read(hdptx->regmap, CMN_REG(0069), &val); if (ret) return 0; - ropll_hw.sdc_n = (val & ROPLL_SDC_N_RBR_MASK) + 3; + ropll_hw.sdc_n = FIELD_GET(ROPLL_SDC_N_RBR_MASK, val) + 3; ret = regmap_read(hdptx->regmap, CMN_REG(006c), &val); if (ret) @@ -2273,7 +2283,7 @@ static u64 rk_hdptx_phy_clk_calc_rate_from_pll_cfg(struct rk_hdptx_phy *hdptx) ret = regmap_read(hdptx->regmap, CMN_REG(0086), &val); if (ret) return 0; - ropll_hw.pms_sdiv = ((val & PLL_PCG_POSTDIV_SEL_MASK) >> 4) + 1; + ropll_hw.pms_sdiv = FIELD_GET(PLL_PCG_POSTDIV_SEL_MASK, val) + 1; bpc = (FIELD_GET(PLL_PCG_CLK_SEL_MASK, val) << 1) + 8; fout = PLL_REF_CLK * ropll_hw.pms_mdiv; From 5619458024925b3a488f9046b3ab26647582b99f Mon Sep 17 00:00:00 2001 From: Qiang Yu Date: Fri, 7 Aug 2026 02:51:10 -0700 Subject: [PATCH 73/76] dt-bindings: phy: qcom: Add Glymur QMP PCIe multiple link-mode PHY Add qcom,glymur-qmp-gen5x8-pcie-phy.yaml as a standalone binding for the Glymur Gen5 PCIe PHY hardware block. This block supports two link modes, selected at runtime via a TCSR syscon register: 1. x8 - a single 8-lane PHY instance is exposed 2. x4+x4 - two independent 4-lane PHY instances are exposed Keep this as a separate schema from qcom,sc8280xp-qmp-pcie-phy.yaml rather than folding it into the shared compatible list there, since the two PHY instances active in x8 mode require twice as many clocks, resets, and power-domains as any other entry in that file, and adding Glymur-specific properties like qcom,link-mode and reg-names there would only apply to this one compatible. Document the required clocks, resets, and power-domains for both PHY instances, and use #phy-cells = <1>, where the cell value is the PHY index within the active link mode. Acked-by: Manivannan Sadhasivam Reviewed-by: Krzysztof Kozlowski Signed-off-by: Qiang Yu Link: https://patch.msgid.link/20260807-glymur_linkmode_0807-v9-1-dd1c7e236e29@oss.qualcomm.com Signed-off-by: Vinod Koul --- .../phy/qcom,glymur-qmp-gen5x8-pcie-phy.yaml | 177 ++++++++++++++++++ 1 file changed, 177 insertions(+) create mode 100644 Documentation/devicetree/bindings/phy/qcom,glymur-qmp-gen5x8-pcie-phy.yaml diff --git a/Documentation/devicetree/bindings/phy/qcom,glymur-qmp-gen5x8-pcie-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,glymur-qmp-gen5x8-pcie-phy.yaml new file mode 100644 index 000000000000..759f7f979dc6 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/qcom,glymur-qmp-gen5x8-pcie-phy.yaml @@ -0,0 +1,177 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/qcom,glymur-qmp-gen5x8-pcie-phy.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm QMP PCIe multiple link-mode PHY controller (PCIe, Glymur) + +maintainers: + - Qiang Yu + - Manivannan Sadhasivam + +description: | + The Glymur SoC uses a single PCIe Gen5 PHY hardware block for the + PCIe3a/PCIe3b controllers. This block supports two link modes, selected + at runtime via a TCSR syscon register: + + 1. x8 - a single 8-lane PHY instance is exposed (PCIe3a only) + 2. x4+x4 - two independent 4-lane PHY instances are exposed (PCIe3a and + PCIe3b) + + The node always describes both PHY instances ("port a" and "port b"), + regardless of which link mode is active on the board. + +properties: + compatible: + enum: + - qcom,glymur-qmp-gen5x8-pcie-phy + + reg: + maxItems: 2 + + reg-names: + items: + - const: port_a + - const: port_b + + clocks: + maxItems: 10 + + clock-names: + items: + - const: aux + - const: cfg_ahb + - const: ref + - const: rchng + - const: pipe + - const: phy_b_aux + - const: cfg_ahb_b + - const: rchng_b + - const: pipe_b + - const: pipediv2_b + + power-domains: + maxItems: 2 + + power-domain-names: + items: + - const: port_a + - const: port_b + + resets: + maxItems: 4 + + reset-names: + items: + - const: port_a + - const: port_a_nocsr + - const: port_b + - const: port_b_nocsr + + vdda-phy-supply: true + + vdda-pll-supply: true + + vdda-refgen0p9-supply: true + + vdda-refgen1p2-supply: true + + qcom,link-mode: + description: + Reference to a register in the TCSR syscon that reports the link + mode the PCIe PHY is currently configured for, either a single x8 + link or two independent x4 links. The link mode is programmed by + firmware before Linux boots; this property is only used to read + the active link mode, specified as a phandle to the syscon and + the register offset. + $ref: /schemas/types.yaml#/definitions/phandle-array + items: + - items: + - description: phandle of TCSR syscon + - description: offset of link mode register + + "#clock-cells": + const: 1 + + clock-output-names: + items: + - description: Name of the PHY A pipe clock output. + - description: Name of the PHY B pipe clock output. + + "#phy-cells": + const: 1 + description: + The cell is the index of the sub-PHY within the active link + mode, e.g. on Glymur, 0 for "port a" and 1 for "port b" in + x4x4 mode, and only index 0 (the single wide PHY) is valid in + x8 mode. + +required: + - compatible + - reg + - reg-names + - clocks + - clock-names + - power-domains + - power-domain-names + - resets + - reset-names + - vdda-phy-supply + - vdda-pll-supply + - vdda-refgen0p9-supply + - vdda-refgen1p2-supply + - qcom,link-mode + - "#clock-cells" + - clock-output-names + - "#phy-cells" + +additionalProperties: false + +examples: + - | + #include + #include + + phy@f00000 { + compatible = "qcom,glymur-qmp-gen5x8-pcie-phy"; + reg = <0x00f00000 0x10000>, <0x00f10000 0x10000>; + reg-names = "port_a", "port_b"; + + clocks = <&gcc GCC_PCIE_PHY_3A_AUX_CLK>, + <&gcc GCC_PCIE_3A_CFG_AHB_CLK>, + <&tcsr TCSR_PCIE_3_CLKREF_EN>, + <&gcc GCC_PCIE_3A_PHY_RCHNG_CLK>, + <&gcc GCC_PCIE_3A_PIPE_CLK>, + <&gcc GCC_PCIE_PHY_3B_AUX_CLK>, + <&gcc GCC_PCIE_3B_CFG_AHB_CLK>, + <&gcc GCC_PCIE_3B_PHY_RCHNG_CLK>, + <&gcc GCC_PCIE_3B_PIPE_CLK>, + <&gcc GCC_PCIE_3B_PIPE_DIV2_CLK>; + clock-names = "aux", "cfg_ahb", "ref", "rchng", "pipe", + "phy_b_aux", "cfg_ahb_b", "rchng_b", "pipe_b", + "pipediv2_b"; + + resets = <&gcc GCC_PCIE_3A_PHY_BCR>, + <&gcc GCC_PCIE_3A_NOCSR_COM_PHY_BCR>, + <&gcc GCC_PCIE_3B_PHY_BCR>, + <&gcc GCC_PCIE_3B_NOCSR_COM_PHY_BCR>; + reset-names = "port_a", "port_a_nocsr", "port_b", "port_b_nocsr"; + + power-domains = <&gcc GCC_PCIE_3A_PHY_GDSC>, + <&gcc GCC_PCIE_3B_PHY_GDSC>; + power-domain-names = "port_a", "port_b"; + + vdda-phy-supply = <&vreg_l3c>; + vdda-pll-supply = <&vreg_l2c>; + + vdda-refgen0p9-supply = <&vreg_l3c>; + vdda-refgen1p2-supply = <&vreg_l2c>; + + qcom,link-mode = <&tcsr 0x5000>; + + #clock-cells = <1>; + clock-output-names = "pcie3a_pipe_clk", "pcie3b_pipe_clk"; + + #phy-cells = <1>; + }; From 1781be3c8a5ddf40f5f9e7b9dc32cc577d491062 Mon Sep 17 00:00:00 2001 From: Qiang Yu Date: Fri, 7 Aug 2026 02:51:11 -0700 Subject: [PATCH 74/76] phy: qcom: qmp-pcie: Add QMP PCIe Multi-PHY driver Some QMP PCIe PHY hardware blocks support multiple link topologies (e.g. x8 or x4+x4) selected via a TCSR register. The existing single-instance QMP PCIe PHY driver has no way to model this: it assumes a single cfg per DT node and instantiates exactly one PHY. Add a dedicated driver for this class of PHY. Match data carries a per-mode cfg table; qmp_pcie_multiphy_probe() reads the current link mode from the TCSR register pointed to by "qcom,link-mode", looks up the corresponding cfg array, and instantiates one qmp_pcie per sub-PHY required by that link mode, registering the clock and #phy-cells = <1> phy providers so consumers can address individual sub-PHYs by index. The driver inherits the phy setting and link-mode programmed by firmware, so only the no_csr reset is used and no phy setting tables are provided. Add the first match data and compatible, qcom,glymur-qmp-gen5x8-pcie-phy, for the Glymur Gen5 PCIe PHY that can bifurcate into two x4 links or operate as a single x8 link. Reviewed-by: Manivannan Sadhasivam Reviewed-by: Konrad Dybcio Signed-off-by: Qiang Yu Link: https://patch.msgid.link/20260807-glymur_linkmode_0807-v9-2-dd1c7e236e29@oss.qualcomm.com Signed-off-by: Vinod Koul --- drivers/phy/qualcomm/Kconfig | 10 + drivers/phy/qualcomm/Makefile | 1 + .../phy/qualcomm/phy-qcom-qmp-pcie-multiphy.c | 743 ++++++++++++++++++ 3 files changed, 754 insertions(+) create mode 100644 drivers/phy/qualcomm/phy-qcom-qmp-pcie-multiphy.c diff --git a/drivers/phy/qualcomm/Kconfig b/drivers/phy/qualcomm/Kconfig index 60a0ead127fa..d910a5d1a1ac 100644 --- a/drivers/phy/qualcomm/Kconfig +++ b/drivers/phy/qualcomm/Kconfig @@ -77,6 +77,16 @@ config PHY_QCOM_QMP_PCIE Enable this to support the QMP PCIe PHY transceiver that is used with PCIe controllers on Qualcomm chips. +config PHY_QCOM_QMP_PCIE_MULTIPHY + tristate "Qualcomm QMP PCIe Multi PHY Driver" + depends on PCI || COMPILE_TEST + select GENERIC_PHY + help + Enable this to support the QMP PCIe PHY transceiver that is used + with PCIe controllers on Qualcomm chips. This PHY is a single + multi-lane QMP block that can be configured either as one wide + link or as multiple narrower independent links (bifurcation). + config PHY_QCOM_QMP_PCIE_8996 tristate "Qualcomm QMP PCIe 8996 PHY Driver" depends on PCI || COMPILE_TEST diff --git a/drivers/phy/qualcomm/Makefile b/drivers/phy/qualcomm/Makefile index b71a6a0bed3f..8bf887d58ee4 100644 --- a/drivers/phy/qualcomm/Makefile +++ b/drivers/phy/qualcomm/Makefile @@ -10,6 +10,7 @@ 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 obj-$(CONFIG_PHY_QCOM_QMP_PCIE) += phy-qcom-qmp-pcie.o +obj-$(CONFIG_PHY_QCOM_QMP_PCIE_MULTIPHY) += phy-qcom-qmp-pcie-multiphy.o obj-$(CONFIG_PHY_QCOM_QMP_PCIE_8996) += phy-qcom-qmp-pcie-msm8996.o obj-$(CONFIG_PHY_QCOM_QMP_UFS) += phy-qcom-qmp-ufs.o obj-$(CONFIG_PHY_QCOM_QMP_USB) += phy-qcom-qmp-usb.o 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..e93cba4369fb --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcie-multiphy.c @@ -0,0 +1,743 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "phy-qcom-qmp.h" + +#define PHY_INIT_COMPLETE_TIMEOUT_US 10000 + +enum qmp_pcie_glymur_link_mode { + QMP_PCIE_GLYMUR_MODE_X8, + QMP_PCIE_GLYMUR_MODE_X4X4, +}; + +enum qphy_reg_layout { + QPHY_PCS_STATUS, + QPHY_LAYOUT_SIZE +}; + +static const unsigned int pciephy_v8_50_regs_layout[QPHY_LAYOUT_SIZE] = { + [QPHY_PCS_STATUS] = QPHY_V8_50_PCS_STATUS1, +}; + +struct qmp_pcie_offsets { + u16 pcs; +}; + +struct qmp_phy_cfg { + const struct qmp_pcie_offsets *offsets; + const char * const *reg_names; + int num_regs; + const char * const *pd_names; + int num_pds; + const char * const *nocsr_reset_list; + int num_nocsr_resets; + const char * const *vreg_list; + int num_vregs; + const unsigned int *regs; + unsigned int phy_status; + const char * const *clk_list; + int num_clks; + const char * const *pipe_clk_list; + int num_pipe_clks; +}; + +struct qmp_pcie { + struct device *dev; + const struct qmp_phy_cfg *cfg; + void __iomem **base; + struct clk_bulk_data *clks; + struct clk_bulk_data *pipe_clks; + struct reset_control_bulk_data *nocsr_resets; + struct regulator_bulk_data *vregs; + struct device **pd_devs; +}; + +struct qmp_pcie_multiphy { + struct phy **phys; + const struct qmp_pcie_link_mode_cfg *mode_cfg; + int num_pipe_outputs; + struct clk_fixed_rate *pipe_out_clks; +}; + +struct qmp_pcie_link_mode_cfg { + const struct qmp_phy_cfg * const *cfgs; + u32 num_phys; +}; + +struct qmp_pcie_match_data { + const struct qmp_pcie_link_mode_cfg *mode_cfgs; + u32 num_modes; +}; + +static const char * const glymur_pciephy_clk_l[] = { + "aux", "cfg_ahb", "ref", "rchng", "phy_b_aux", +}; + +static const char * const glymur_pciephy_a_clk_l[] = { + "aux", "cfg_ahb", "ref", "rchng", +}; + +static const char * const glymur_pciephy_b_clk_l[] = { + "phy_b_aux", "cfg_ahb_b", "ref", "rchng_b", +}; + +static const char * const glymur_pciephy_pipeclk_l[] = { + "pipe", +}; + +static const char * const glymur_pipephy_b_pipeclk_l[] = { + "pipe_b", "pipediv2_b", +}; + +static const char * const glymur_vreg_l[] = { + "vdda-phy", "vdda-pll", "vdda-refgen0p9", "vdda-refgen1p2", +}; + +static const char * const glymur_pciephy_a_reg_l[] = { + "port_a", +}; + +static const char * const glymur_pciephy_b_reg_l[] = { + "port_b", +}; + +static const char * const glymur_pciephy_reg_l[] = { + "port_a", "port_b", +}; + +static const char * const glymur_pciephy_a_pd_l[] = { + "port_a", +}; + +static const char * const glymur_pciephy_b_pd_l[] = { + "port_b", +}; + +static const char * const glymur_pciephy_pd_l[] = { + "port_a", "port_b", +}; + +static const char * const glymur_pciephy_a_nocsr_reset_l[] = { + "port_a_nocsr", +}; + +static const char * const glymur_pciephy_nocsr_reset_l[] = { + "port_a_nocsr", "port_b_nocsr", +}; + +static const char * const glymur_pciephy_b_nocsr_reset_l[] = { + "port_b_nocsr", +}; + +static const struct qmp_pcie_offsets glymur_pcie_offsets_v8_50 = { + .pcs = 0x9000, +}; + +static const struct qmp_phy_cfg glymur_qmp_gen5x4_pciephy_a_cfg = { + .offsets = &glymur_pcie_offsets_v8_50, + .reg_names = glymur_pciephy_a_reg_l, + .num_regs = ARRAY_SIZE(glymur_pciephy_a_reg_l), + .pd_names = glymur_pciephy_a_pd_l, + .num_pds = ARRAY_SIZE(glymur_pciephy_a_pd_l), + .nocsr_reset_list = glymur_pciephy_a_nocsr_reset_l, + .num_nocsr_resets = ARRAY_SIZE(glymur_pciephy_a_nocsr_reset_l), + .vreg_list = glymur_vreg_l, + .num_vregs = ARRAY_SIZE(glymur_vreg_l), + .regs = pciephy_v8_50_regs_layout, + .phy_status = PHYSTATUS_4_20, + .pipe_clk_list = glymur_pciephy_pipeclk_l, + .num_pipe_clks = ARRAY_SIZE(glymur_pciephy_pipeclk_l), + .clk_list = glymur_pciephy_a_clk_l, + .num_clks = ARRAY_SIZE(glymur_pciephy_a_clk_l), +}; + +static const struct qmp_phy_cfg glymur_qmp_gen5x4_pciephy_b_cfg = { + .offsets = &glymur_pcie_offsets_v8_50, + .reg_names = glymur_pciephy_b_reg_l, + .num_regs = ARRAY_SIZE(glymur_pciephy_b_reg_l), + .pd_names = glymur_pciephy_b_pd_l, + .num_pds = ARRAY_SIZE(glymur_pciephy_b_pd_l), + .nocsr_reset_list = glymur_pciephy_b_nocsr_reset_l, + .num_nocsr_resets = ARRAY_SIZE(glymur_pciephy_b_nocsr_reset_l), + .vreg_list = glymur_vreg_l, + .num_vregs = ARRAY_SIZE(glymur_vreg_l), + .regs = pciephy_v8_50_regs_layout, + .phy_status = PHYSTATUS_4_20, + .pipe_clk_list = glymur_pipephy_b_pipeclk_l, + .num_pipe_clks = ARRAY_SIZE(glymur_pipephy_b_pipeclk_l), + .clk_list = glymur_pciephy_b_clk_l, + .num_clks = ARRAY_SIZE(glymur_pciephy_b_clk_l), +}; + +static const struct qmp_phy_cfg glymur_qmp_gen5x8_pciephy_cfg = { + .offsets = &glymur_pcie_offsets_v8_50, + .reg_names = glymur_pciephy_reg_l, + .num_regs = ARRAY_SIZE(glymur_pciephy_reg_l), + .pd_names = glymur_pciephy_pd_l, + .num_pds = ARRAY_SIZE(glymur_pciephy_pd_l), + .nocsr_reset_list = glymur_pciephy_nocsr_reset_l, + .num_nocsr_resets = ARRAY_SIZE(glymur_pciephy_nocsr_reset_l), + .vreg_list = glymur_vreg_l, + .num_vregs = ARRAY_SIZE(glymur_vreg_l), + .regs = pciephy_v8_50_regs_layout, + .phy_status = PHYSTATUS_4_20, + .pipe_clk_list = glymur_pciephy_pipeclk_l, + .num_pipe_clks = ARRAY_SIZE(glymur_pciephy_pipeclk_l), + .clk_list = glymur_pciephy_clk_l, + .num_clks = ARRAY_SIZE(glymur_pciephy_clk_l), +}; + +static const struct qmp_phy_cfg * const glymur_qmp_gen5x8_mode_x8_cfgs[] = { + &glymur_qmp_gen5x8_pciephy_cfg, +}; + +static const struct qmp_phy_cfg * const glymur_qmp_gen5x8_mode_x4x4_cfgs[] = { + &glymur_qmp_gen5x4_pciephy_a_cfg, + &glymur_qmp_gen5x4_pciephy_b_cfg, +}; + +static const struct qmp_pcie_link_mode_cfg glymur_qmp_gen5x8_mode_cfgs[] = { + [QMP_PCIE_GLYMUR_MODE_X8] = { + .cfgs = glymur_qmp_gen5x8_mode_x8_cfgs, + .num_phys = ARRAY_SIZE(glymur_qmp_gen5x8_mode_x8_cfgs), + }, + [QMP_PCIE_GLYMUR_MODE_X4X4] = { + .cfgs = glymur_qmp_gen5x8_mode_x4x4_cfgs, + .num_phys = ARRAY_SIZE(glymur_qmp_gen5x8_mode_x4x4_cfgs), + }, +}; + +static const struct qmp_pcie_match_data glymur_qmp_gen5x8_match_data = { + .mode_cfgs = glymur_qmp_gen5x8_mode_cfgs, + .num_modes = ARRAY_SIZE(glymur_qmp_gen5x8_mode_cfgs), +}; + +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_resume_and_get(qmp->pd_devs[i]); + if (ret < 0) { + dev_err(qmp->dev, "failed to power on %s domain: %d\n", + cfg->pd_names[i], ret); + goto err_power_off; + } + } + + return 0; + +err_power_off: + while (--i >= 0) + pm_runtime_put(qmp->pd_devs[i]); + + return ret; +} + +static void qmp_pcie_pd_power_off(struct qmp_pcie *qmp) +{ + const struct qmp_phy_cfg *cfg = qmp->cfg; + int i; + + for (i = cfg->num_pds - 1; i >= 0; i--) + pm_runtime_put(qmp->pd_devs[i]); +} + +static int qmp_pcie_init(struct phy *phy) +{ + struct qmp_pcie *qmp = phy_get_drvdata(phy); + const struct qmp_phy_cfg *cfg = qmp->cfg; + int ret; + + ret = qmp_pcie_pd_power_on(qmp); + if (ret) + return ret; + + ret = regulator_bulk_enable(cfg->num_vregs, qmp->vregs); + if (ret) { + dev_err(qmp->dev, "failed to enable regulators: %d\n", ret); + goto err_pd_power_off; + } + + ret = reset_control_bulk_assert(qmp->cfg->num_nocsr_resets, qmp->nocsr_resets); + if (ret) { + dev_err(qmp->dev, "no-csr reset assert failed: %d\n", ret); + goto err_disable_regulators; + } + + usleep_range(200, 300); + + ret = clk_bulk_prepare_enable(qmp->cfg->num_clks, qmp->clks); + if (ret) + goto err_disable_regulators; + + return 0; + +err_disable_regulators: + regulator_bulk_disable(cfg->num_vregs, qmp->vregs); +err_pd_power_off: + qmp_pcie_pd_power_off(qmp); + + return ret; +} + +static int qmp_pcie_exit(struct phy *phy) +{ + struct qmp_pcie *qmp = phy_get_drvdata(phy); + const struct qmp_phy_cfg *cfg = qmp->cfg; + + reset_control_bulk_assert(qmp->cfg->num_nocsr_resets, qmp->nocsr_resets); + + clk_bulk_disable_unprepare(qmp->cfg->num_clks, qmp->clks); + regulator_bulk_disable(cfg->num_vregs, qmp->vregs); + qmp_pcie_pd_power_off(qmp); + + return 0; +} + +static int qmp_pcie_power_on(struct phy *phy) +{ + struct qmp_pcie *qmp = phy_get_drvdata(phy); + const struct qmp_phy_cfg *cfg = qmp->cfg; + const struct qmp_pcie_offsets *offs = cfg->offsets; + void __iomem *status; + unsigned int val; + int i, ret; + + ret = clk_bulk_prepare_enable(qmp->cfg->num_pipe_clks, qmp->pipe_clks); + if (ret) + return ret; + + ret = reset_control_bulk_deassert(qmp->cfg->num_nocsr_resets, qmp->nocsr_resets); + if (ret) { + dev_err(qmp->dev, "no-csr reset deassert failed: %d\n", ret); + goto err_disable_pipe_clk; + } + + for (i = 0; i < cfg->num_regs; i++) { + status = qmp->base[i] + offs->pcs + cfg->regs[QPHY_PCS_STATUS]; + ret = readl_poll_timeout(status, val, !(val & cfg->phy_status), 200, + PHY_INIT_COMPLETE_TIMEOUT_US); + if (ret) { + dev_err(qmp->dev, "PHY power on timed-out (%s): %d\n", + cfg->reg_names[i], ret); + goto err_disable_pipe_clk; + } + } + + return 0; + +err_disable_pipe_clk: + clk_bulk_disable_unprepare(qmp->cfg->num_pipe_clks, qmp->pipe_clks); + + return ret; +} + +static int qmp_pcie_power_off(struct phy *phy) +{ + struct qmp_pcie *qmp = phy_get_drvdata(phy); + + clk_bulk_disable_unprepare(qmp->cfg->num_pipe_clks, qmp->pipe_clks); + + return 0; +} + +static int qmp_pcie_enable(struct phy *phy) +{ + int ret; + + ret = qmp_pcie_init(phy); + if (ret) + return ret; + + ret = qmp_pcie_power_on(phy); + if (ret) + qmp_pcie_exit(phy); + + return ret; +} + +static int qmp_pcie_disable(struct phy *phy) +{ + int ret; + + ret = qmp_pcie_power_off(phy); + if (ret) + return ret; + + return qmp_pcie_exit(phy); +} + +static const struct phy_ops qmp_pcie_phy_ops = { + .power_on = qmp_pcie_enable, + .power_off = qmp_pcie_disable, + .owner = THIS_MODULE, +}; + +static void qmp_pcie_pd_detach(void *data) +{ + struct qmp_pcie *qmp = data; + const struct qmp_phy_cfg *cfg = qmp->cfg; + int i; + + for (i = 0; i < cfg->num_pds; i++) { + if (!IS_ERR_OR_NULL(qmp->pd_devs[i])) + dev_pm_domain_detach(qmp->pd_devs[i], true); + } +} + +static int qmp_pcie_pd_init(struct qmp_pcie *qmp) +{ + const struct qmp_phy_cfg *cfg = qmp->cfg; + struct device *dev = qmp->dev; + int i, ret; + + if (!cfg->num_pds) + return 0; + + qmp->pd_devs = devm_kcalloc(dev, cfg->num_pds, sizeof(*qmp->pd_devs), + GFP_KERNEL); + if (!qmp->pd_devs) + return -ENOMEM; + + for (i = 0; i < cfg->num_pds; i++) { + qmp->pd_devs[i] = dev_pm_domain_attach_by_name(dev, + cfg->pd_names[i]); + if (IS_ERR_OR_NULL(qmp->pd_devs[i])) { + ret = PTR_ERR(qmp->pd_devs[i]) ? : -ENODATA; + goto err_detach; + } + } + + return devm_add_action_or_reset(dev, qmp_pcie_pd_detach, qmp); + +err_detach: + while (--i >= 0) + dev_pm_domain_detach(qmp->pd_devs[i], false); + + return ret; +} + +static int qmp_pcie_vreg_init(struct qmp_pcie *qmp) +{ + const struct qmp_phy_cfg *cfg = qmp->cfg; + struct device *dev = qmp->dev; + int i; + + qmp->vregs = devm_kcalloc(dev, cfg->num_vregs, sizeof(*qmp->vregs), + GFP_KERNEL); + if (!qmp->vregs) + return -ENOMEM; + + for (i = 0; i < cfg->num_vregs; i++) + qmp->vregs[i].supply = cfg->vreg_list[i]; + + return devm_regulator_bulk_get(dev, cfg->num_vregs, qmp->vregs); +} + +static int qmp_pcie_reset_init(struct qmp_pcie *qmp) +{ + const struct qmp_phy_cfg *cfg = qmp->cfg; + struct device *dev = qmp->dev; + int i, ret; + + qmp->nocsr_resets = devm_kcalloc(dev, cfg->num_nocsr_resets, + sizeof(*qmp->nocsr_resets), + GFP_KERNEL); + if (!qmp->nocsr_resets) + return -ENOMEM; + + for (i = 0; i < cfg->num_nocsr_resets; i++) + qmp->nocsr_resets[i].id = cfg->nocsr_reset_list[i]; + + ret = devm_reset_control_bulk_get_exclusive(dev, + cfg->num_nocsr_resets, + qmp->nocsr_resets); + if (ret) + return dev_err_probe(dev, ret, "failed to get no-csr resets\n"); + + return 0; +} + +static int qmp_pcie_clk_init(struct qmp_pcie *qmp) +{ + const struct qmp_phy_cfg *cfg = qmp->cfg; + struct device *dev = qmp->dev; + int i, ret; + + qmp->clks = devm_kcalloc(dev, cfg->num_clks, sizeof(*qmp->clks), + GFP_KERNEL); + if (!qmp->clks) + return -ENOMEM; + + for (i = 0; i < cfg->num_clks; i++) + qmp->clks[i].id = cfg->clk_list[i]; + + ret = devm_clk_bulk_get_optional(dev, cfg->num_clks, qmp->clks); + if (ret) + return ret; + + qmp->pipe_clks = devm_kcalloc(dev, cfg->num_pipe_clks, + sizeof(*qmp->pipe_clks), GFP_KERNEL); + if (!qmp->pipe_clks) + return -ENOMEM; + + for (i = 0; i < cfg->num_pipe_clks; i++) + qmp->pipe_clks[i].id = cfg->pipe_clk_list[i]; + + return devm_clk_bulk_get_optional(dev, cfg->num_pipe_clks, + qmp->pipe_clks); +} + +static int __phy_pipe_clk_register(struct device *dev, struct device_node *np, + int idx, struct clk_fixed_rate *fixed) +{ + struct clk_init_data init = { }; + int ret; + + ret = of_property_read_string_index(np, "clock-output-names", idx, + &init.name); + if (ret) { + dev_err(dev, "%pOFn: No clock-output-names\n", np); + return ret; + } + + init.ops = &clk_fixed_rate_ops; + + fixed->fixed_rate = 125000000; + + fixed->hw.init = &init; + + return devm_clk_hw_register(dev, &fixed->hw); +} + +static struct clk_hw * +qmp_pcie_multiphy_clk_hw_get(struct of_phandle_args *clkspec, void *data) +{ + struct qmp_pcie_multiphy *qmp_data = data; + unsigned int idx = 0; + + if (clkspec->args_count) + idx = clkspec->args[0]; + + if (idx < (unsigned int)qmp_data->num_pipe_outputs) + return &qmp_data->pipe_out_clks[idx].hw; + + return ERR_PTR(-EINVAL); +} + +static int qmp_pcie_multiphy_register_clocks(struct device *dev, + struct device_node *np, + struct qmp_pcie_multiphy *qmp_data) +{ + int num_pipe_outputs; + int i, ret; + + num_pipe_outputs = of_property_count_strings(np, "clock-output-names"); + + qmp_data->num_pipe_outputs = num_pipe_outputs; + qmp_data->pipe_out_clks = devm_kcalloc(dev, num_pipe_outputs, + sizeof(*qmp_data->pipe_out_clks), + GFP_KERNEL); + if (!qmp_data->pipe_out_clks) + return -ENOMEM; + + for (i = 0; i < num_pipe_outputs; i++) { + ret = __phy_pipe_clk_register(dev, np, i, + &qmp_data->pipe_out_clks[i]); + if (ret) + return ret; + } + + return devm_of_clk_add_hw_provider(dev, qmp_pcie_multiphy_clk_hw_get, qmp_data); +} + +static int qmp_pcie_get_mmio(struct qmp_pcie *qmp) +{ + struct platform_device *pdev = to_platform_device(qmp->dev); + const struct qmp_phy_cfg *cfg = qmp->cfg; + struct device *dev = qmp->dev; + void __iomem *base; + int i; + + qmp->base = devm_kcalloc(dev, cfg->num_regs, sizeof(*qmp->base), + GFP_KERNEL); + if (!qmp->base) + return -ENOMEM; + + for (i = 0; i < cfg->num_regs; i++) { + base = devm_platform_ioremap_resource_byname(pdev, cfg->reg_names[i]); + if (IS_ERR(base)) + return PTR_ERR(base); + + qmp->base[i] = base; + } + + return 0; +} + +static int qmp_pcie_read_link_mode(struct device *dev, unsigned int *link_mode) +{ + struct regmap *map; + unsigned int args[1]; + + map = syscon_regmap_lookup_by_phandle_args(dev->of_node, "qcom,link-mode", + ARRAY_SIZE(args), args); + if (IS_ERR(map)) + return PTR_ERR(map); + + return regmap_read(map, args[0], link_mode); +} + +static struct phy *qmp_pcie_multiphy_xlate(struct device *dev, + const struct of_phandle_args *args) +{ + struct qmp_pcie_multiphy *qmp_data = dev_get_drvdata(dev); + unsigned int idx; + + if (!qmp_data || args->args_count < 1) + return ERR_PTR(-EINVAL); + + idx = args->args[0]; + + if (idx < (unsigned int)qmp_data->mode_cfg->num_phys) + return qmp_data->phys[idx] ?: ERR_PTR(-EINVAL); + + return ERR_PTR(-EINVAL); +} + +static int qmp_pcie_probe_phy(struct qmp_pcie *qmp, struct device_node *np, + struct phy **out_phy) +{ + int ret; + + ret = qmp_pcie_get_mmio(qmp); + if (ret) + return ret; + + ret = qmp_pcie_clk_init(qmp); + if (ret) + return ret; + + ret = qmp_pcie_reset_init(qmp); + if (ret) + return ret; + + ret = qmp_pcie_vreg_init(qmp); + if (ret) + return ret; + + ret = qmp_pcie_pd_init(qmp); + if (ret) + return ret; + + *out_phy = devm_phy_create(qmp->dev, np, &qmp_pcie_phy_ops); + if (IS_ERR(*out_phy)) + return PTR_ERR(*out_phy); + + phy_set_drvdata(*out_phy, qmp); + + return 0; +} + +static int qmp_pcie_multiphy_probe(struct platform_device *pdev) +{ + const struct qmp_pcie_match_data *match_data; + struct qmp_pcie_multiphy *qmp_data; + struct phy_provider *phy_provider; + struct device *dev = &pdev->dev; + unsigned int link_mode; + struct qmp_pcie *qmp; + struct phy **phys; + int phy_index; + int ret; + + qmp_data = devm_kzalloc(dev, sizeof(*qmp_data), GFP_KERNEL); + + match_data = of_device_get_match_data(dev); + if (!match_data) + return -EINVAL; + + if (!qmp_data) + return -ENOMEM; + + ret = qmp_pcie_read_link_mode(dev, &link_mode); + if (ret) + return dev_err_probe(dev, ret, "failed to read qcom,link-mode\n"); + + if (link_mode >= match_data->num_modes) + return dev_err_probe(dev, -EINVAL, "invalid qcom,link-mode: %u\n", + link_mode); + + qmp_data->mode_cfg = &match_data->mode_cfgs[link_mode]; + + qmp = devm_kcalloc(dev, qmp_data->mode_cfg->num_phys, sizeof(*qmp), GFP_KERNEL); + if (!qmp) + return -ENOMEM; + + phys = devm_kcalloc(dev, qmp_data->mode_cfg->num_phys, sizeof(*phys), GFP_KERNEL); + if (!phys) + return -ENOMEM; + + qmp_data->phys = phys; + dev_set_drvdata(dev, qmp_data); + + for (phy_index = 0; phy_index < qmp_data->mode_cfg->num_phys; phy_index++) { + qmp[phy_index].dev = dev; + qmp[phy_index].cfg = qmp_data->mode_cfg->cfgs[phy_index]; + ret = qmp_pcie_probe_phy(&qmp[phy_index], dev->of_node, &phys[phy_index]); + if (ret) + return ret; + } + + ret = qmp_pcie_multiphy_register_clocks(dev, dev->of_node, qmp_data); + if (ret) + return ret; + + phy_provider = devm_of_phy_provider_register(dev, qmp_pcie_multiphy_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static const struct of_device_id qmp_pcie_multiphy_of_match_table[] = { + { + .compatible = "qcom,glymur-qmp-gen5x8-pcie-phy", + .data = &glymur_qmp_gen5x8_match_data, + }, + { } +}; +MODULE_DEVICE_TABLE(of, qmp_pcie_multiphy_of_match_table); + +static struct platform_driver qmp_pcie_multiphy_driver = { + .probe = qmp_pcie_multiphy_probe, + .driver = { + .name = "qcom-qmp-pcie-multiphy", + .of_match_table = qmp_pcie_multiphy_of_match_table, + }, +}; +module_platform_driver(qmp_pcie_multiphy_driver); + +MODULE_AUTHOR("Qiang Yu "); +MODULE_DESCRIPTION("Qualcomm QMP PCIe Multi-PHY driver for Glymur"); +MODULE_LICENSE("GPL"); From 4486e75ba647bd8b98fc1f053101b40caceeed4b Mon Sep 17 00:00:00 2001 From: Jason Yang Date: Tue, 11 Aug 2026 16:10:22 +0800 Subject: [PATCH 75/76] phy: rockchip-samsung-dcphy: fix out-of-range max_register The PHY register block is 64KB, so with a register stride of 4 the last accessible register sits at offset 0xfffc. max_register names 0x10000, one register past the end of the mapping: dumping the registers through the regmap debugfs interface reads beyond the ioremapped region and oopses on the unmapped page. The oops fires with the regmap lock held, so later PHY operations deadlock. Fixes: b2a1a2ae7818 ("phy: rockchip: Add Samsung MIPI D-/C-PHY driver") Cc: stable@vger.kernel.org Signed-off-by: Jason Yang Assisted-by: Claude:claude-opus-5 Reviewed-by: Sebastian Reichel Link: https://patch.msgid.link/20260811-dcphy-maxreg-v1-v1-1-aa63f6a63a64@gmail.com Signed-off-by: Vinod Koul --- drivers/phy/rockchip/phy-rockchip-samsung-dcphy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/phy/rockchip/phy-rockchip-samsung-dcphy.c b/drivers/phy/rockchip/phy-rockchip-samsung-dcphy.c index 0f69060aa5d5..d2db0d2b4a1e 100644 --- a/drivers/phy/rockchip/phy-rockchip-samsung-dcphy.c +++ b/drivers/phy/rockchip/phy-rockchip-samsung-dcphy.c @@ -1527,7 +1527,7 @@ static const struct regmap_config samsung_mipi_dcphy_regmap_config = { .reg_bits = 32, .val_bits = 32, .reg_stride = 4, - .max_register = 0x10000, + .max_register = 0xfffc, }; static struct phy *samsung_mipi_dcphy_xlate(struct device *dev, From ba8376e8c76c6512c98949dffbf4ded0b782a365 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Tue, 11 Aug 2026 16:34:17 +0200 Subject: [PATCH 76/76] MAINTAINERS: Add Manivannan Sadhasivam as the Reviewer for Generic PHY Framework I'm volunteering to review the PHY patches. Hence, add myself as the Reviewer. Signed-off-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260811143417.336664-1-mani@kernel.org Signed-off-by: Vinod Koul --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 5b544583e4d8..80c0cf242887 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -10920,6 +10920,7 @@ F: include/uapi/asm-generic/ GENERIC PHY FRAMEWORK M: Vinod Koul R: Neil Armstrong +R: Manivannan Sadhasivam L: linux-phy@lists.infradead.org S: Supported Q: https://patchwork.kernel.org/project/linux-phy/list/