[PATCH v3 07/13] phy: qcom: add QMP USB3 PHY driver for Shikra

Aswin Murugan via U-Boot <[email protected]>
Newsgroups gmane.comp.boot-loaders.u-boot.general,gmane.comp.boot-loaders.u-boot
Message-ID <[email protected]>
Shikra USB3 QMP PHY uses a single-PHY DT binding (#phy-cells = <0>),
which is not supported by the existing QMP combo PHY driver.

Add a dedicated USB3-only QMP PHY driver for Shikra, porting the PHY
initialization sequence and configuration tables from Linux
drivers/phy/qualcomm/phy-qcom-qmp-usbc.c. The driver is selected
through PHY_QCOM_QMP_USB3_LEGACY and enables USB3 PHY bring-up on
Shikra platforms.

Signed-off-by: Aswin Murugan <[email protected]>
---
 drivers/phy/qcom/Kconfig                    |   7 +
 drivers/phy/qcom/Makefile                   |   1 +
 drivers/phy/qcom/phy-qcom-qmp-usb3-legacy.c | 534 ++++++++++++++++++++
 3 files changed, 542 insertions(+)
 create mode 100644 drivers/phy/qcom/phy-qcom-qmp-usb3-legacy.c

diff --git a/drivers/phy/qcom/Kconfig b/drivers/phy/qcom/Kconfig
index 1fdadaccb12..ffb523fffbb 100644
--- a/drivers/phy/qcom/Kconfig
+++ b/drivers/phy/qcom/Kconfig
@@ -32,6 +32,13 @@ config PHY_QCOM_QMP_UFS
 	help
 	  Enable this to support the UFS QMP PHY on various Qualcomm chipsets.
 
+config PHY_QCOM_QMP_USB3_LEGACY
+	bool "Qualcomm QMP USB3-only legacy PHY driver"
+	depends on PHY && ARCH_SNAPDRAGON
+	help
+	  Enable this to support the legacy USB3-only QMP PHY (v3) found on
+	  older Qualcomm chipsets such as QCM2290/QCS615 and derivatives.
+
 config PHY_QCOM_QUSB2
 	bool "Qualcomm USB QUSB2 PHY driver"
 	depends on PHY && ARCH_SNAPDRAGON
diff --git a/drivers/phy/qcom/Makefile b/drivers/phy/qcom/Makefile
index 714013dc572..dfad1f1e56a 100644
--- a/drivers/phy/qcom/Makefile
+++ b/drivers/phy/qcom/Makefile
@@ -3,6 +3,7 @@ obj-$(CONFIG_MSM8916_USB_PHY) += msm8916-usbh-phy.o
 obj-$(CONFIG_PHY_QCOM_QMP_COMBO) += phy-qcom-qmp-combo.o
 obj-$(CONFIG_PHY_QCOM_QMP_PCIE) += phy-qcom-qmp-pcie.o
 obj-$(CONFIG_PHY_QCOM_QMP_UFS) += phy-qcom-qmp-ufs.o
+obj-$(CONFIG_PHY_QCOM_QMP_USB3_LEGACY) += phy-qcom-qmp-usb3-legacy.o
 obj-$(CONFIG_PHY_QCOM_QUSB2) += phy-qcom-qusb2.o
 obj-$(CONFIG_PHY_QCOM_USB_SNPS_FEMTO_V2) += phy-qcom-snps-femto-v2.o
 obj-$(CONFIG_PHY_QCOM_SNPS_EUSB2) += phy-qcom-snps-eusb2.o
diff --git a/drivers/phy/qcom/phy-qcom-qmp-usb3-legacy.c b/drivers/phy/qcom/phy-qcom-qmp-usb3-legacy.c
new file mode 100644
index 00000000000..d1aad6e7952
--- /dev/null
+++ b/drivers/phy/qcom/phy-qcom-qmp-usb3-legacy.c
@@ -0,0 +1,534 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#include <clk.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <dm/devres.h>
+#include <dm/uclass.h>
+#include <generic-phy.h>
+#include <reset.h>
+#include <power/regulator.h>
+#include <usb/tcpm.h>
+#include <asm/io.h>
+#include <linux/bitops.h>
+#include <linux/compat.h>
+#include <linux/delay.h>
+#include <linux/iopoll.h>
+#include <linux/err.h>
+
+#include "phy-qcom-qmp-common.h"
+
+#include "phy-qcom-qmp.h"
+#include "phy-qcom-qmp-pcs-misc-v3.h"
+
+/* QPHY_V3_PCS_MISC_TYPEC_CTRL-equivalent bit, legacy USB3-only PHY */
+#define SW_PORTSELECT_MUX			BIT(1)
+#define SW_PORTSELECT_VAL			BIT(0)
+
+#define PHY_INIT_COMPLETE_TIMEOUT		10000
+
+#define RESET_SETTLE_DELAY_US			10
+
+struct qmp_usb3_legacy_offsets {
+	u16 serdes;
+	u16 tx;
+	u16 rx;
+	u16 tx2;
+	u16 rx2;
+	u16 pcs;
+	u16 pcs_misc;
+};
+
+static const struct qmp_usb3_legacy_offsets qmp_usb3_legacy_offsets_qcm2290 = {
+	.serdes		= 0x000,
+	.tx		= 0x200,
+	.rx		= 0x400,
+	.tx2		= 0x600,
+	.rx2		= 0x800,
+	.pcs_misc	= 0xa00,
+	.pcs		= 0xc00,
+};
+
+static const struct qmp_phy_init_tbl qcm2290_usb3_serdes_tbl[] = {
+	QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_EN_SEL, 0x14),
+	QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x08),
+	QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x30),
+	QMP_PHY_INIT_CFG(QSERDES_COM_SYS_CLK_CTRL, 0x06),
+	QMP_PHY_INIT_CFG(QSERDES_COM_RESETSM_CNTRL, 0x00),
+	QMP_PHY_INIT_CFG(QSERDES_COM_RESETSM_CNTRL2, 0x08),
+	QMP_PHY_INIT_CFG(QSERDES_COM_BG_TRIM, 0x0f),
+	QMP_PHY_INIT_CFG(QSERDES_COM_SVS_MODE_CLK_SEL, 0x01),
+	QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x00),
+	QMP_PHY_INIT_CFG(QSERDES_COM_DEC_START_MODE0, 0x82),
+	QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START1_MODE0, 0x55),
+	QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START2_MODE0, 0x55),
+	QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x03),
+	QMP_PHY_INIT_CFG(QSERDES_COM_CP_CTRL_MODE0, 0x0b),
+	QMP_PHY_INIT_CFG(QSERDES_COM_PLL_RCTRL_MODE0, 0x16),
+	QMP_PHY_INIT_CFG(QSERDES_COM_PLL_CCTRL_MODE0, 0x28),
+	QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x80),
+	QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN1_MODE0, 0x00),
+	QMP_PHY_INIT_CFG(QSERDES_COM_CORECLK_DIV, 0x0a),
+	QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP1_MODE0, 0x15),
+	QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP2_MODE0, 0x34),
+	QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP3_MODE0, 0x00),
+	QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP_EN, 0x00),
+	QMP_PHY_INIT_CFG(QSERDES_COM_CORE_CLK_EN, 0x00),
+	QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP_CFG, 0x00),
+	QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_MAP, 0x00),
+	QMP_PHY_INIT_CFG(QSERDES_COM_BG_TIMER, 0x0a),
+	QMP_PHY_INIT_CFG(QSERDES_COM_SSC_EN_CENTER, 0x01),
+	QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER1, 0x31),
+	QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER2, 0x01),
+	QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER1, 0x00),
+	QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER2, 0x00),
+	QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE1, 0xde),
+	QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE2, 0x07),
+	QMP_PHY_INIT_CFG(QSERDES_COM_PLL_IVCO, 0x0f),
+	QMP_PHY_INIT_CFG(QSERDES_COM_CMN_CONFIG, 0x06),
+	QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_INITVAL, 0x80),
+	QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CTRL_BY_PSM, 0x01),
+};
+
+static const struct qmp_phy_init_tbl qcm2290_usb3_tx_tbl[] = {
+	QMP_PHY_INIT_CFG(QSERDES_V3_TX_HIGHZ_DRVR_EN, 0x10),
+	QMP_PHY_INIT_CFG(QSERDES_V3_TX_RCV_DETECT_LVL_2, 0x12),
+	QMP_PHY_INIT_CFG(QSERDES_V3_TX_LANE_MODE_1, 0xc6),
+	QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_TX, 0x00),
+	QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_RX, 0x00),
+};
+
+static const struct qmp_phy_init_tbl qcm2290_usb3_rx_tbl[] = {
+	QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_FO_GAIN, 0x0b),
+	QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_PI_CONTROLS, 0x80),
+	QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_COUNT_LOW, 0x00),
+	QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_COUNT_HIGH, 0x00),
+	QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FO_GAIN, 0x0a),
+	QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_GAIN, 0x06),
+	QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x75),
+	QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL2, 0x02),
+	QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4e),
+	QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL4, 0x18),
+	QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x77),
+	QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80),
+	QMP_PHY_INIT_CFG(QSERDES_V3_RX_VGA_CAL_CNTRL2, 0x0a),
+	QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_CNTRL, 0x03),
+	QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_DEGLITCH_CNTRL, 0x16),
+	QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_ENABLES, 0x00),
+	QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_MODE_00, 0x00),
+};
+
+static const struct qmp_phy_init_tbl qcm2290_usb3_pcs_tbl[] = {
+	QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V0, 0x9f),
+	QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V0, 0x17),
+	QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V0, 0x0f),
+	QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL2, 0x83),
+	QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL1, 0x02),
+	QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_L, 0x09),
+	QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_H_TOL, 0xa2),
+	QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_MAN_CODE, 0x85),
+	QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG1, 0xd1),
+	QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG2, 0x1f),
+	QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG3, 0x47),
+	QMP_PHY_INIT_CFG(QPHY_V3_PCS_RXEQTRAINING_WAIT_TIME, 0x75),
+	QMP_PHY_INIT_CFG(QPHY_V3_PCS_RXEQTRAINING_RUN_TIME, 0x13),
+	QMP_PHY_INIT_CFG(QPHY_V3_PCS_LFPS_TX_ECSTART_EQTLOCK, 0x86),
+	QMP_PHY_INIT_CFG(QPHY_V3_PCS_PWRUP_RESET_DLY_TIME_AUXCLK, 0x04),
+	QMP_PHY_INIT_CFG(QPHY_V3_PCS_TSYNC_RSYNC_TIME, 0x44),
+	QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_L, 0xe7),
+	QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_H, 0x03),
+	QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_U3_L, 0x40),
+	QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_U3_H, 0x00),
+	QMP_PHY_INIT_CFG(QPHY_V3_PCS_RX_SIGDET_LVL, 0x88),
+};
+
+/* Per-compatible config; add a new instance here to support another SoC */
+struct qmp_usb3_legacy_cfg {
+	const struct qmp_usb3_legacy_offsets *offsets;
+
+	const struct qmp_phy_init_tbl *serdes_tbl;
+	int serdes_tbl_num;
+	const struct qmp_phy_init_tbl *tx_tbl;
+	int tx_tbl_num;
+	const struct qmp_phy_init_tbl *rx_tbl;
+	int rx_tbl_num;
+	const struct qmp_phy_init_tbl *pcs_tbl;
+	int pcs_tbl_num;
+
+	/* clock names fetched by clk_get_by_name(); "pipe" is fetched separately */
+	const char * const *clk_list;
+	int num_clks;
+
+	const char * const *vreg_list;
+	int num_vregs;
+};
+
+static const char * const shikra_usb3_clk_l[] = {
+	"cfg_ahb", "ref", "com_aux",
+};
+
+static const struct qmp_usb3_legacy_cfg shikra_usb3phy_cfg = {
+	.offsets	= &qmp_usb3_legacy_offsets_qcm2290,
+	.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),
+	.clk_list	= shikra_usb3_clk_l,
+	.num_clks	= ARRAY_SIZE(shikra_usb3_clk_l),
+	.vreg_list	= NULL,
+	.num_vregs	= 0,
+};
+
+static const char * const talos_usb3_clk_l[] = {
+	"aux", "ref", "cfg_ahb",
+};
+
+static const char * const talos_usb3_vreg_l[] = {
+	"vdda-phy-supply", "vdda-pll-supply",
+};
+
+static const struct qmp_usb3_legacy_cfg talos_usb3phy_cfg = {
+	.offsets	= &qmp_usb3_legacy_offsets_qcm2290,
+	.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),
+	.clk_list	= talos_usb3_clk_l,
+	.num_clks	= ARRAY_SIZE(talos_usb3_clk_l),
+	.vreg_list	= talos_usb3_vreg_l,
+	.num_vregs	= ARRAY_SIZE(talos_usb3_vreg_l),
+};
+
+struct qmp_usb3_legacy {
+	struct udevice *dev;
+	void __iomem *serdes;
+	void __iomem *tx;
+	void __iomem *rx;
+	void __iomem *tx2;
+	void __iomem *rx2;
+	void __iomem *pcs;
+	void __iomem *pcs_misc;
+	struct clk *clks;
+	struct clk pipe_clk;
+	int num_clks;
+	struct reset_ctl_bulk resets;
+	struct udevice **vregs;
+	int num_vregs;
+	const struct qmp_usb3_legacy_cfg *cfg;
+};
+
+static inline void qphy_setbits(void __iomem *base, u32 offset, u32 val)
+{
+	u32 reg;
+
+	reg = readl(base + offset);
+	reg |= val;
+	writel(reg, base + offset);
+	readl(base + offset);
+}
+
+static inline void qphy_clrbits(void __iomem *base, u32 offset, u32 val)
+{
+	u32 reg;
+
+	reg = readl(base + offset);
+	reg &= ~val;
+	writel(reg, base + offset);
+	readl(base + offset);
+}
+
+/* Quiesce the PHY; shared by power_off() and power_on()'s error path */
+static void qmp_usb3_legacy_quiesce(struct qmp_usb3_legacy *qmp)
+{
+	qphy_clrbits(qmp->pcs, QPHY_V3_PCS_START_CONTROL, SERDES_START | PCS_START);
+	qphy_setbits(qmp->pcs, QPHY_V3_PCS_SW_RESET, SW_RESET);
+	qphy_clrbits(qmp->pcs, QPHY_V3_PCS_POWER_DOWN_CONTROL, SW_PWRDN);
+}
+
+/* Fixed orientation by default; use TCPM orientation if available */
+static u32 qmp_usb3_legacy_get_portselect(struct qmp_usb3_legacy *qmp)
+{
+	u32 val = SW_PORTSELECT_MUX;
+
+	if (IS_ENABLED(CONFIG_TYPEC_TCPM)) {
+		struct udevice *tcpm_dev;
+		enum typec_orientation orientation;
+		int ret;
+
+		ret = uclass_first_device_check(UCLASS_TCPM, &tcpm_dev);
+		if (!ret && tcpm_dev) {
+			orientation = tcpm_get_orientation(tcpm_dev);
+			if (orientation == TYPEC_ORIENTATION_REVERSE)
+				val |= SW_PORTSELECT_VAL;
+		}
+	}
+
+	return val;
+}
+
+static int qmp_usb3_legacy_power_on(struct phy *phy)
+{
+	struct qmp_usb3_legacy *qmp = dev_get_priv(phy->dev);
+	const struct qmp_usb3_legacy_cfg *cfg = qmp->cfg;
+	void __iomem *status;
+	u32 val;
+	int ret;
+
+	qphy_setbits(qmp->pcs, QPHY_V3_PCS_POWER_DOWN_CONTROL, SW_PWRDN);
+
+	writel(qmp_usb3_legacy_get_portselect(qmp), qmp->pcs_misc);
+
+	qmp_configure(qmp->dev, qmp->serdes, cfg->serdes_tbl, cfg->serdes_tbl_num);
+
+	ret = clk_enable(&qmp->pipe_clk);
+	if (ret) {
+		dev_err(qmp->dev, "pipe_clk enable failed err=%d\n", ret);
+		return ret;
+	}
+
+	qmp_configure_lane(qmp->dev, qmp->tx, cfg->tx_tbl, cfg->tx_tbl_num, 1);
+	qmp_configure_lane(qmp->dev, qmp->rx, cfg->rx_tbl, cfg->rx_tbl_num, 1);
+
+	qmp_configure_lane(qmp->dev, qmp->tx2, cfg->tx_tbl, cfg->tx_tbl_num, 2);
+	qmp_configure_lane(qmp->dev, qmp->rx2, cfg->rx_tbl, cfg->rx_tbl_num, 2);
+
+	qmp_configure(qmp->dev, qmp->pcs, cfg->pcs_tbl, cfg->pcs_tbl_num);
+
+	qphy_clrbits(qmp->pcs, QPHY_V3_PCS_SW_RESET, SW_RESET);
+
+	qphy_setbits(qmp->pcs, QPHY_V3_PCS_START_CONTROL, SERDES_START | PCS_START);
+
+	status = qmp->pcs + QPHY_V3_PCS_PCS_STATUS;
+	ret = readl_poll_timeout(status, val, !(val & PHYSTATUS),
+				  PHY_INIT_COMPLETE_TIMEOUT);
+	if (ret) {
+		dev_err(qmp->dev, "phy initialization timed-out\n");
+		clk_disable(&qmp->pipe_clk);
+		qmp_usb3_legacy_quiesce(qmp);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int qmp_usb3_legacy_power_off(struct phy *phy)
+{
+	struct qmp_usb3_legacy *qmp = dev_get_priv(phy->dev);
+
+	clk_disable(&qmp->pipe_clk);
+	qmp_usb3_legacy_quiesce(qmp);
+
+	return 0;
+}
+
+static void qmp_usb3_legacy_clk_disable_upto(struct qmp_usb3_legacy *qmp, int upto)
+{
+	int i;
+
+	for (i = 0; i < upto; i++)
+		clk_disable(&qmp->clks[i]);
+}
+
+static int qmp_usb3_legacy_clk_init(struct qmp_usb3_legacy *qmp)
+{
+	const struct qmp_usb3_legacy_cfg *cfg = qmp->cfg;
+	struct udevice *dev = qmp->dev;
+	int num = cfg->num_clks;
+	int i, ret;
+
+	qmp->clks = devm_kcalloc(dev, num, sizeof(*qmp->clks), GFP_KERNEL);
+	if (!qmp->clks)
+		return -ENOMEM;
+
+	qmp->num_clks = 0;
+
+	for (i = 0; i < num; i++) {
+		ret = clk_get_by_name(dev, cfg->clk_list[i], &qmp->clks[i]);
+		if (ret) {
+			dev_err(dev, "failed to get %s clock: %d\n",
+				cfg->clk_list[i], ret);
+			qmp_usb3_legacy_clk_disable_upto(qmp, i);
+			return ret;
+		}
+
+		ret = clk_enable(&qmp->clks[i]);
+		if (ret) {
+			dev_err(dev, "failed to enable %s clock: %d\n",
+				cfg->clk_list[i], ret);
+			qmp_usb3_legacy_clk_disable_upto(qmp, i);
+			return ret;
+		}
+	}
+	qmp->num_clks = num;
+
+	ret = clk_get_by_name(dev, "pipe", &qmp->pipe_clk);
+	if (ret) {
+		dev_err(dev, "failed to get pipe clock: %d\n", ret);
+		qmp_usb3_legacy_clk_disable_upto(qmp, qmp->num_clks);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int qmp_usb3_legacy_vreg_init(struct qmp_usb3_legacy *qmp)
+{
+	const struct qmp_usb3_legacy_cfg *cfg = qmp->cfg;
+	struct udevice *dev = qmp->dev;
+	int i, ret;
+
+	qmp->num_vregs = 0;
+
+	if (!cfg->num_vregs)
+		return 0;
+
+	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++) {
+		ret = device_get_supply_regulator(dev, cfg->vreg_list[i], &qmp->vregs[i]);
+		if (ret) {
+			dev_err(dev, "failed to get regulator %s: %d\n",
+				cfg->vreg_list[i], ret);
+			return ret;
+		}
+	}
+
+	qmp->num_vregs = cfg->num_vregs;
+	return 0;
+}
+
+static int qmp_usb3_legacy_vreg_enable(struct qmp_usb3_legacy *qmp)
+{
+	int i, ret;
+
+	for (i = 0; i < qmp->num_vregs; i++) {
+		ret = regulator_set_enable(qmp->vregs[i], true);
+		if (ret) {
+			dev_err(qmp->dev, "failed to enable regulator %d: %d\n", i, ret);
+			while (--i >= 0)
+				regulator_set_enable(qmp->vregs[i], false);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static void qmp_usb3_legacy_vreg_disable(struct qmp_usb3_legacy *qmp)
+{
+	int i;
+
+	for (i = qmp->num_vregs - 1; i >= 0; i--)
+		regulator_set_enable(qmp->vregs[i], false);
+}
+
+static int qmp_usb3_legacy_parse_dt(struct qmp_usb3_legacy *qmp)
+{
+	const struct qmp_usb3_legacy_cfg *cfg = qmp->cfg;
+	const struct qmp_usb3_legacy_offsets *offs = cfg->offsets;
+	struct udevice *dev = qmp->dev;
+	void __iomem *base;
+	int ret;
+
+	base = (void __iomem *)dev_read_addr(dev);
+	if (IS_ERR(base))
+		return PTR_ERR(base);
+
+	qmp->serdes = base + offs->serdes;
+	qmp->tx = base + offs->tx;
+	qmp->rx = base + offs->rx;
+	qmp->tx2 = base + offs->tx2;
+	qmp->rx2 = base + offs->rx2;
+	qmp->pcs = base + offs->pcs;
+	qmp->pcs_misc = base + offs->pcs_misc;
+
+	ret = qmp_usb3_legacy_vreg_init(qmp);
+	if (ret)
+		return ret;
+
+	ret = qmp_usb3_legacy_vreg_enable(qmp);
+	if (ret)
+		return ret;
+
+	ret = reset_get_bulk(dev, &qmp->resets);
+	if (ret) {
+		dev_err(dev, "failed to get resets: %d\n", ret);
+		goto err_disable_vregs;
+	}
+
+	ret = reset_assert_bulk(&qmp->resets);
+	if (ret) {
+		dev_err(dev, "failed to assert resets: %d\n", ret);
+		goto err_disable_vregs;
+	}
+
+	udelay(RESET_SETTLE_DELAY_US);
+
+	ret = reset_deassert_bulk(&qmp->resets);
+	if (ret) {
+		dev_err(dev, "failed to deassert resets: %d\n", ret);
+		goto err_assert_resets;
+	}
+
+	ret = qmp_usb3_legacy_clk_init(qmp);
+	if (ret)
+		goto err_assert_resets;
+
+	return 0;
+
+err_assert_resets:
+	reset_assert_bulk(&qmp->resets);
+err_disable_vregs:
+	qmp_usb3_legacy_vreg_disable(qmp);
+
+	return ret;
+}
+
+static int qmp_usb3_legacy_probe(struct udevice *dev)
+{
+	struct qmp_usb3_legacy *qmp = dev_get_priv(dev);
+
+	qmp->dev = dev;
+	qmp->cfg = (const struct qmp_usb3_legacy_cfg *)dev_get_driver_data(dev);
+	if (!qmp->cfg) {
+		dev_err(dev, "failed to get PHY configuration\n");
+		return -EINVAL;
+	}
+
+	return qmp_usb3_legacy_parse_dt(qmp);
+}
+
+static struct phy_ops qmp_usb3_legacy_ops = {
+	.power_on = qmp_usb3_legacy_power_on,
+	.power_off = qmp_usb3_legacy_power_off,
+};
+
+static const struct udevice_id qmp_usb3_legacy_ids[] = {
+	{
+		.compatible = "qcom,shikra-qmp-usb3-phy",
+		.data = (ulong)&shikra_usb3phy_cfg,
+	},
+	{ }
+};
+
+U_BOOT_DRIVER(qcom_qmp_usb3_legacy_phy) = {
+	.name = "qcom-qmp-usb3-legacy-phy",
+	.id = UCLASS_PHY,
+	.of_match = qmp_usb3_legacy_ids,
+	.ops = &qmp_usb3_legacy_ops,
+	.probe = qmp_usb3_legacy_probe,
+	.priv_auto = sizeof(struct qmp_usb3_legacy),
+};
-- 
2.34.1
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.