diff mbox series

scsi: ufs-qcom: initialize PHY for ACPI boot

Message ID 20210301082609.21051-1-shawn.guo@linaro.org
State New
Headers show
Series scsi: ufs-qcom: initialize PHY for ACPI boot | expand

Commit Message

Shawn Guo March 1, 2021, 8:26 a.m. UTC
When booting Lenovo Flex 5G laptop (powered by Qualcomm SC8180x SoC)
from ACPI, UFS is not working, because the generic QMP PHY driver that
takes care of UFS PHY init only works with DT.  It adds a specialized
PHY init function to get UFS work with ACPI boot on this platform,
where UFS 3.0 and QMP V4 PHY is combined to support UFS.

Signed-off-by: Shawn Guo <shawn.guo@linaro.org>

---
 drivers/scsi/ufs/Makefile       |   1 +
 drivers/scsi/ufs/ufs-qcom-phy.c | 349 ++++++++++++++++++++++++++++++++
 drivers/scsi/ufs/ufs-qcom.c     |   4 +
 drivers/scsi/ufs/ufs-qcom.h     |  11 +
 drivers/scsi/ufs/ufshci.h       |   1 +
 5 files changed, 366 insertions(+)
 create mode 100644 drivers/scsi/ufs/ufs-qcom-phy.c

-- 
2.17.1
diff mbox series

Patch

diff --git a/drivers/scsi/ufs/Makefile b/drivers/scsi/ufs/Makefile
index 06f3a3fe4a44..15f561de0cbf 100644
--- a/drivers/scsi/ufs/Makefile
+++ b/drivers/scsi/ufs/Makefile
@@ -15,6 +15,7 @@  obj-$(CONFIG_SCSI_UFS_CDNS_PLATFORM) += cdns-pltfrm.o
 obj-$(CONFIG_SCSI_UFS_QCOM) += ufs_qcom.o
 ufs_qcom-y += ufs-qcom.o
 ufs_qcom-$(CONFIG_SCSI_UFS_CRYPTO) += ufs-qcom-ice.o
+ufs_qcom-$(CONFIG_ACPI) += ufs-qcom-phy.o
 obj-$(CONFIG_SCSI_UFS_EXYNOS) += ufs-exynos.o
 obj-$(CONFIG_SCSI_UFSHCD_PCI) += ufshcd-pci.o
 obj-$(CONFIG_SCSI_UFSHCD_PLATFORM) += ufshcd-pltfrm.o
diff --git a/drivers/scsi/ufs/ufs-qcom-phy.c b/drivers/scsi/ufs/ufs-qcom-phy.c
new file mode 100644
index 000000000000..113292f9637d
--- /dev/null
+++ b/drivers/scsi/ufs/ufs-qcom-phy.c
@@ -0,0 +1,349 @@ 
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021, Linaro Limited
+ */
+
+#include <linux/iopoll.h>
+
+#include "ufshcd.h"
+
+/* Only for QMP V4 PHY - QSERDES COM registers */
+#define QSERDES_V4_COM_SSC_EN_CENTER			0x010
+#define QSERDES_V4_COM_SSC_PER1				0x01c
+#define QSERDES_V4_COM_SSC_PER2				0x020
+#define QSERDES_V4_COM_SSC_STEP_SIZE1_MODE0		0x024
+#define QSERDES_V4_COM_SSC_STEP_SIZE2_MODE0		0x028
+#define QSERDES_V4_COM_SSC_STEP_SIZE1_MODE1		0x030
+#define QSERDES_V4_COM_SSC_STEP_SIZE2_MODE1		0x034
+#define QSERDES_V4_COM_CLK_ENABLE1			0x048
+#define QSERDES_V4_COM_SYSCLK_BUF_ENABLE		0x050
+#define QSERDES_V4_COM_PLL_IVCO				0x058
+#define QSERDES_V4_COM_CMN_IPTRIM			0x060
+#define QSERDES_V4_COM_CP_CTRL_MODE0			0x074
+#define QSERDES_V4_COM_CP_CTRL_MODE1			0x078
+#define QSERDES_V4_COM_PLL_RCTRL_MODE0			0x07c
+#define QSERDES_V4_COM_PLL_RCTRL_MODE1			0x080
+#define QSERDES_V4_COM_PLL_CCTRL_MODE0			0x084
+#define QSERDES_V4_COM_PLL_CCTRL_MODE1			0x088
+#define QSERDES_V4_COM_SYSCLK_EN_SEL			0x094
+#define QSERDES_V4_COM_LOCK_CMP_EN			0x0a4
+#define QSERDES_V4_COM_LOCK_CMP1_MODE0			0x0ac
+#define QSERDES_V4_COM_LOCK_CMP2_MODE0			0x0b0
+#define QSERDES_V4_COM_LOCK_CMP1_MODE1			0x0b4
+#define QSERDES_V4_COM_DEC_START_MODE0			0x0bc
+#define QSERDES_V4_COM_LOCK_CMP2_MODE1			0x0b8
+#define QSERDES_V4_COM_DEC_START_MODE1			0x0c4
+#define QSERDES_V4_COM_DIV_FRAC_START1_MODE0		0x0cc
+#define QSERDES_V4_COM_DIV_FRAC_START2_MODE0		0x0d0
+#define QSERDES_V4_COM_DIV_FRAC_START3_MODE0		0x0d4
+#define QSERDES_V4_COM_DIV_FRAC_START1_MODE1		0x0d8
+#define QSERDES_V4_COM_DIV_FRAC_START2_MODE1		0x0dc
+#define QSERDES_V4_COM_DIV_FRAC_START3_MODE1		0x0e0
+#define QSERDES_V4_COM_VCO_TUNE_MAP			0x10c
+#define QSERDES_V4_COM_VCO_TUNE1_MODE0			0x110
+#define QSERDES_V4_COM_VCO_TUNE2_MODE0			0x114
+#define QSERDES_V4_COM_VCO_TUNE1_MODE1			0x118
+#define QSERDES_V4_COM_VCO_TUNE2_MODE1			0x11c
+#define QSERDES_V4_COM_VCO_TUNE_INITVAL2		0x124
+#define QSERDES_V4_COM_CLK_SELECT			0x154
+#define QSERDES_V4_COM_HSCLK_SEL			0x158
+#define QSERDES_V4_COM_HSCLK_HS_SWITCH_SEL		0x15c
+#define QSERDES_V4_COM_CORECLK_DIV_MODE1		0x16c
+#define QSERDES_V4_COM_SVS_MODE_CLK_SEL			0x184
+#define QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE0	0x1ac
+#define QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE0	0x1b0
+#define QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE1	0x1b4
+#define QSERDES_V4_COM_BIN_VCOCAL_HSCLK_SEL		0x1bc
+#define QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE1	0x1b8
+
+/* Only for QMP V4 PHY - TX registers */
+#define QSERDES_V4_TX_RES_CODE_LANE_TX			0x34
+#define QSERDES_V4_TX_RES_CODE_LANE_RX			0x38
+#define QSERDES_V4_TX_RES_CODE_LANE_OFFSET_TX		0x3c
+#define QSERDES_V4_TX_RES_CODE_LANE_OFFSET_RX		0x40
+#define QSERDES_V4_TX_LANE_MODE_1			0x84
+#define QSERDES_V4_TX_LANE_MODE_2			0x88
+#define QSERDES_V4_TX_RCV_DETECT_LVL_2			0x9c
+#define QSERDES_V4_TX_PWM_GEAR_1_DIVIDER_BAND0_1	0xd8
+#define QSERDES_V4_TX_PWM_GEAR_2_DIVIDER_BAND0_1	0xdC
+#define QSERDES_V4_TX_PWM_GEAR_3_DIVIDER_BAND0_1	0xe0
+#define QSERDES_V4_TX_PWM_GEAR_4_DIVIDER_BAND0_1	0xe4
+#define QSERDES_V4_TX_TRAN_DRVR_EMP_EN			0xb8
+#define QSERDES_V4_TX_PI_QEC_CTRL			0x104
+
+/* Only for QMP V4 PHY - RX registers */
+#define QSERDES_V4_RX_UCDR_FO_GAIN			0x008
+#define QSERDES_V4_RX_UCDR_SO_GAIN			0x014
+#define QSERDES_V4_RX_UCDR_FASTLOCK_FO_GAIN		0x030
+#define QSERDES_V4_RX_UCDR_SO_SATURATION_AND_ENABLE	0x034
+#define QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_LOW		0x03c
+#define QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_HIGH		0x040
+#define QSERDES_V4_RX_UCDR_PI_CONTROLS			0x044
+#define QSERDES_V4_RX_UCDR_PI_CTRL2			0x048
+#define QSERDES_V4_RX_UCDR_SB2_THRESH1			0x04c
+#define QSERDES_V4_RX_UCDR_SB2_THRESH2			0x050
+#define QSERDES_V4_RX_UCDR_SB2_GAIN1			0x054
+#define QSERDES_V4_RX_UCDR_SB2_GAIN2			0x058
+#define QSERDES_V4_RX_AUX_DATA_TCOARSE_TFINE		0x060
+#define QSERDES_V4_RX_RCLK_AUXDATA_SEL			0x064
+#define QSERDES_V4_RX_AC_JTAG_ENABLE			0x068
+#define QSERDES_V4_RX_AC_JTAG_MODE			0x078
+#define QSERDES_V4_RX_RX_TERM_BW			0x080
+#define QSERDES_V4_RX_VGA_CAL_CNTRL1			0x0d4
+#define QSERDES_V4_RX_VGA_CAL_CNTRL2			0x0d8
+#define QSERDES_V4_RX_GM_CAL				0x0dc
+#define QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL1		0x0e8
+#define QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL2		0x0ec
+#define QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL3		0x0f0
+#define QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL4		0x0f4
+#define QSERDES_V4_RX_RX_IDAC_TSETTLE_LOW		0x0f8
+#define QSERDES_V4_RX_RX_IDAC_TSETTLE_HIGH		0x0fc
+#define QSERDES_V4_RX_RX_IDAC_MEASURE_TIME		0x100
+#define QSERDES_V4_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1	0x110
+#define QSERDES_V4_RX_RX_OFFSET_ADAPTOR_CNTRL2		0x114
+#define QSERDES_V4_RX_SIGDET_ENABLES			0x118
+#define QSERDES_V4_RX_SIGDET_CNTRL			0x11c
+#define QSERDES_V4_RX_SIGDET_LVL			0x120
+#define QSERDES_V4_RX_SIGDET_DEGLITCH_CNTRL		0x124
+#define QSERDES_V4_RX_RX_BAND				0x128
+#define QSERDES_V4_RX_RX_MODE_00_LOW			0x170
+#define QSERDES_V4_RX_RX_MODE_00_HIGH			0x174
+#define QSERDES_V4_RX_RX_MODE_00_HIGH2			0x178
+#define QSERDES_V4_RX_RX_MODE_00_HIGH3			0x17c
+#define QSERDES_V4_RX_RX_MODE_00_HIGH4			0x180
+#define QSERDES_V4_RX_RX_MODE_01_LOW			0x184
+#define QSERDES_V4_RX_RX_MODE_01_HIGH			0x188
+#define QSERDES_V4_RX_RX_MODE_01_HIGH2			0x18c
+#define QSERDES_V4_RX_RX_MODE_01_HIGH3			0x190
+#define QSERDES_V4_RX_RX_MODE_01_HIGH4			0x194
+#define QSERDES_V4_RX_RX_MODE_10_LOW			0x198
+#define QSERDES_V4_RX_RX_MODE_10_HIGH			0x19c
+#define QSERDES_V4_RX_RX_MODE_10_HIGH2			0x1a0
+#define QSERDES_V4_RX_RX_MODE_10_HIGH3			0x1a4
+#define QSERDES_V4_RX_RX_MODE_10_HIGH4			0x1a8
+#define QSERDES_V4_RX_DFE_EN_TIMER			0x1b4
+#define QSERDES_V4_RX_DFE_CTLE_POST_CAL_OFFSET		0x1b8
+#define QSERDES_V4_RX_DCC_CTRL1				0x1bc
+#define QSERDES_V4_RX_VTH_CODE				0x1c4
+
+/* Only for QMP V4 PHY - UFS PCS registers */
+#define QPHY_V4_PCS_UFS_PHY_START			0x000
+#define QPHY_V4_PCS_UFS_POWER_DOWN_CONTROL		0x004
+#define QPHY_V4_PCS_UFS_SW_RESET			0x008
+#define QPHY_V4_PCS_UFS_TIMER_20US_CORECLK_STEPS_MSB	0x00c
+#define QPHY_V4_PCS_UFS_TIMER_20US_CORECLK_STEPS_LSB	0x010
+#define QPHY_V4_PCS_UFS_PLL_CNTL			0x02c
+#define QPHY_V4_PCS_UFS_TX_LARGE_AMP_DRV_LVL		0x030
+#define QPHY_V4_PCS_UFS_TX_SMALL_AMP_DRV_LVL		0x038
+#define QPHY_V4_PCS_UFS_BIST_FIXED_PAT_CTRL		0x060
+#define QPHY_V4_PCS_UFS_TX_HSGEAR_CAPABILITY		0x074
+#define QPHY_V4_PCS_UFS_RX_HSGEAR_CAPABILITY		0x0b4
+#define QPHY_V4_PCS_UFS_DEBUG_BUS_CLKSEL		0x124
+#define QPHY_V4_PCS_UFS_LINECFG_DISABLE			0x148
+#define QPHY_V4_PCS_UFS_RX_MIN_HIBERN8_TIME		0x150
+#define QPHY_V4_PCS_UFS_RX_SIGDET_CTRL2			0x158
+#define QPHY_V4_PCS_UFS_TX_PWM_GEAR_BAND		0x160
+#define QPHY_V4_PCS_UFS_TX_HS_GEAR_BAND			0x168
+#define QPHY_V4_PCS_UFS_READY_STATUS			0x180
+#define QPHY_V4_PCS_UFS_TX_MID_TERM_CTRL1		0x1d8
+#define QPHY_V4_PCS_UFS_MULTI_LANE_CTRL1		0x1e0
+
+struct ufsphy_init_tbl {
+	unsigned int offset;
+	unsigned int val;
+};
+
+static const struct ufsphy_init_tbl ufsphy_serdes_tbl[] = {
+	{ QSERDES_V4_COM_SYSCLK_EN_SEL, 0xd9 },
+	{ QSERDES_V4_COM_HSCLK_SEL, 0x11 },
+	{ QSERDES_V4_COM_HSCLK_HS_SWITCH_SEL, 0x00 },
+	{ QSERDES_V4_COM_LOCK_CMP_EN, 0x01 },
+	{ QSERDES_V4_COM_VCO_TUNE_MAP, 0x02 },
+	{ QSERDES_V4_COM_PLL_IVCO, 0x0f },
+	{ QSERDES_V4_COM_VCO_TUNE_INITVAL2, 0x00 },
+	{ QSERDES_V4_COM_BIN_VCOCAL_HSCLK_SEL, 0x11 },
+	{ QSERDES_V4_COM_DEC_START_MODE0, 0x82 },
+	{ QSERDES_V4_COM_CP_CTRL_MODE0, 0x06 },
+	{ QSERDES_V4_COM_PLL_RCTRL_MODE0, 0x16 },
+	{ QSERDES_V4_COM_PLL_CCTRL_MODE0, 0x36 },
+	{ QSERDES_V4_COM_LOCK_CMP1_MODE0, 0xff },
+	{ QSERDES_V4_COM_LOCK_CMP2_MODE0, 0x0c },
+	{ QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE0, 0xac },
+	{ QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE0, 0x1e },
+	{ QSERDES_V4_COM_DEC_START_MODE1, 0x98 },
+	{ QSERDES_V4_COM_CP_CTRL_MODE1, 0x06 },
+	{ QSERDES_V4_COM_PLL_RCTRL_MODE1, 0x16 },
+	{ QSERDES_V4_COM_PLL_CCTRL_MODE1, 0x36 },
+	{ QSERDES_V4_COM_LOCK_CMP1_MODE1, 0x32 },
+	{ QSERDES_V4_COM_LOCK_CMP2_MODE1, 0x0f },
+	{ QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE1, 0xdd },
+	{ QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE1, 0x23 },
+	/* Rate B */
+	{ QSERDES_V4_COM_VCO_TUNE_MAP, 0x06 },
+};
+
+static const struct ufsphy_init_tbl ufsphy_tx_tbl[] = {
+	{ QSERDES_V4_TX_PWM_GEAR_1_DIVIDER_BAND0_1, 0x06 },
+	{ QSERDES_V4_TX_PWM_GEAR_2_DIVIDER_BAND0_1, 0x03 },
+	{ QSERDES_V4_TX_PWM_GEAR_3_DIVIDER_BAND0_1, 0x01 },
+	{ QSERDES_V4_TX_PWM_GEAR_4_DIVIDER_BAND0_1, 0x00 },
+	{ QSERDES_V4_TX_LANE_MODE_1, 0x05 },
+	{ QSERDES_V4_TX_TRAN_DRVR_EMP_EN, 0x0c },
+};
+
+static const struct ufsphy_init_tbl ufsphy_rx_tbl[] = {
+	{ QSERDES_V4_RX_SIGDET_LVL, 0x24 },
+	{ QSERDES_V4_RX_SIGDET_CNTRL, 0x0f },
+	{ QSERDES_V4_RX_SIGDET_DEGLITCH_CNTRL, 0x1e },
+	{ QSERDES_V4_RX_RX_BAND, 0x18 },
+	{ QSERDES_V4_RX_UCDR_FASTLOCK_FO_GAIN, 0x0a },
+	{ QSERDES_V4_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x4b },
+	{ QSERDES_V4_RX_UCDR_PI_CONTROLS, 0xf1 },
+	{ QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_LOW, 0x80 },
+	{ QSERDES_V4_RX_UCDR_PI_CTRL2, 0x80 },
+	{ QSERDES_V4_RX_UCDR_FO_GAIN, 0x0c },
+	{ QSERDES_V4_RX_UCDR_SO_GAIN, 0x04 },
+	{ QSERDES_V4_RX_RX_TERM_BW, 0x1b },
+	{ QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL2, 0x06 },
+	{ QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL3, 0x04 },
+	{ QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL4, 0x1d },
+	{ QSERDES_V4_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x00 },
+	{ QSERDES_V4_RX_RX_IDAC_MEASURE_TIME, 0x10 },
+	{ QSERDES_V4_RX_RX_IDAC_TSETTLE_LOW, 0xc0 },
+	{ QSERDES_V4_RX_RX_IDAC_TSETTLE_HIGH, 0x00 },
+	{ QSERDES_V4_RX_RX_MODE_00_LOW, 0x36 },
+	{ QSERDES_V4_RX_RX_MODE_00_HIGH, 0x36 },
+	{ QSERDES_V4_RX_RX_MODE_00_HIGH2, 0xf6 },
+	{ QSERDES_V4_RX_RX_MODE_00_HIGH3, 0x3b },
+	{ QSERDES_V4_RX_RX_MODE_00_HIGH4, 0x3d },
+	{ QSERDES_V4_RX_RX_MODE_01_LOW, 0xe0 },
+	{ QSERDES_V4_RX_RX_MODE_01_HIGH, 0xc8 },
+	{ QSERDES_V4_RX_RX_MODE_01_HIGH2, 0xc8 },
+	{ QSERDES_V4_RX_RX_MODE_01_HIGH3, 0x3b },
+	{ QSERDES_V4_RX_RX_MODE_01_HIGH4, 0xb1 },
+	{ QSERDES_V4_RX_RX_MODE_10_LOW, 0xe0 },
+	{ QSERDES_V4_RX_RX_MODE_10_HIGH, 0xc8 },
+	{ QSERDES_V4_RX_RX_MODE_10_HIGH2, 0xc8 },
+	{ QSERDES_V4_RX_RX_MODE_10_HIGH3, 0x3b },
+	{ QSERDES_V4_RX_RX_MODE_10_HIGH4, 0xb1 },
+};
+
+static const struct ufsphy_init_tbl ufsphy_pcs_tbl[] = {
+	{ QPHY_V4_PCS_UFS_RX_SIGDET_CTRL2, 0x6d },
+	{ QPHY_V4_PCS_UFS_TX_LARGE_AMP_DRV_LVL, 0x0a },
+	{ QPHY_V4_PCS_UFS_TX_SMALL_AMP_DRV_LVL, 0x02 },
+	{ QPHY_V4_PCS_UFS_TX_MID_TERM_CTRL1, 0x43 },
+	{ QPHY_V4_PCS_UFS_DEBUG_BUS_CLKSEL, 0x1f },
+	{ QPHY_V4_PCS_UFS_RX_MIN_HIBERN8_TIME, 0xff },
+	{ QPHY_V4_PCS_UFS_MULTI_LANE_CTRL1, 0x02 },
+};
+
+#define UFSPHY_SERDES_OFFSET		0x3000
+#define UFSPHY_TX_OFFSET		0x3400
+#define UFSPHY_RX_OFFSET		0x3600
+#define UFSPHY_TX2_OFFSET		0x3800
+#define UFSPHY_RX2_OFFSET		0x3a00
+#define UFSPHY_PCS_OFFSET		0x3c00
+
+#define SW_RESET			BIT(0)
+#define SW_PWRDN			BIT(0)
+#define SERDES_START			BIT(0)
+#define PCS_READY			BIT(0)
+
+#define GCC_UFS_PHY_BCR_ADDR		0x177000
+#define BCR_RESET			BIT(0)
+
+#define PHY_INIT_COMPLETE_TIMEOUT	10000
+
+static int ufs_qcom_phy_reset(void)
+{
+	void __iomem *reg;
+	u32 val;
+
+	reg = ioremap(GCC_UFS_PHY_BCR_ADDR, 4);
+	if (!reg)
+		return -ENOMEM;
+
+	/* assert */
+	val = readl(reg);
+	val |= BCR_RESET;
+	writel(val, reg);
+
+	/*
+	 * The hardware requirement for delay between assert/deassert
+	 * is at least 3-4 sleep clock (32.7KHz) cycles, which comes to
+	 * ~125us (4/32768). To be on the safe side add 200us delay.
+	 */
+	usleep_range(200, 210);
+
+	/* deassert */
+	val = readl(reg);
+	val &= ~BCR_RESET;
+	writel(val, reg);
+
+	usleep_range(1000, 1100);
+
+	iounmap(reg);
+	return 0;
+}
+
+static void ufsphy_configure_hw(void __iomem *base,
+				const struct ufsphy_init_tbl tbl[], int num)
+{
+	const struct ufsphy_init_tbl *t = tbl;
+	int i;
+
+	for (i = 0; i < num; i++, t++)
+		writel(t->val, base + t->offset);
+}
+
+int ufs_qcom_phy_init(struct ufs_hba *hba)
+{
+	void __iomem *pcs = hba->mmio_base + UFSPHY_PCS_OFFSET;
+	u32 val;
+	int ret;
+
+	ret = ufs_qcom_phy_reset();
+	if (ret)
+		return ret;
+
+	val = readl(pcs + QPHY_V4_PCS_UFS_POWER_DOWN_CONTROL);
+	val |= SW_PWRDN;
+	writel(val, pcs + QPHY_V4_PCS_UFS_POWER_DOWN_CONTROL);
+
+	ufsphy_configure_hw(hba->mmio_base + UFSPHY_SERDES_OFFSET,
+			    ufsphy_serdes_tbl, ARRAY_SIZE(ufsphy_serdes_tbl));
+	ufsphy_configure_hw(hba->mmio_base + UFSPHY_TX_OFFSET,
+			    ufsphy_tx_tbl, ARRAY_SIZE(ufsphy_tx_tbl));
+	ufsphy_configure_hw(hba->mmio_base + UFSPHY_TX2_OFFSET,
+			    ufsphy_tx_tbl, ARRAY_SIZE(ufsphy_tx_tbl));
+	ufsphy_configure_hw(hba->mmio_base + UFSPHY_RX_OFFSET,
+			    ufsphy_rx_tbl, ARRAY_SIZE(ufsphy_rx_tbl));
+	ufsphy_configure_hw(hba->mmio_base + UFSPHY_RX2_OFFSET,
+			    ufsphy_rx_tbl, ARRAY_SIZE(ufsphy_rx_tbl));
+	ufsphy_configure_hw(pcs, ufsphy_pcs_tbl, ARRAY_SIZE(ufsphy_pcs_tbl));
+
+	/*
+	 * Pull out PHY from POWER DOWN state.
+	 * This is active low enable signal to power-down PHY.
+	 */
+	/* Pull PHY out of reset state */
+	val = readl(pcs + QPHY_V4_PCS_UFS_SW_RESET);
+	val &= ~SW_RESET;
+	writel(val, pcs + QPHY_V4_PCS_UFS_SW_RESET);
+
+	/* Start SerDes and Phy-Coding-Sublayer */
+	val = readl(pcs + QPHY_V4_PCS_UFS_PHY_START);
+	val |= SERDES_START;
+	writel(val, pcs + QPHY_V4_PCS_UFS_PHY_START);
+
+	ret = readl_poll_timeout(pcs + QPHY_V4_PCS_UFS_READY_STATUS,
+				 val, (val & PCS_READY) == PCS_READY,
+				 10, PHY_INIT_COMPLETE_TIMEOUT);
+	if (ret) {
+		dev_err(hba->dev, "ufsphy initialization timed-out\n");
+		return ret;
+	}
+
+	return 0;
+}
diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c
index f97d7b0ae3b6..1b45157d9dc9 100644
--- a/drivers/scsi/ufs/ufs-qcom.c
+++ b/drivers/scsi/ufs/ufs-qcom.c
@@ -292,6 +292,10 @@  static int ufs_qcom_power_up_sequence(struct ufs_hba *hba)
 	bool is_rate_B = (UFS_QCOM_LIMIT_HS_RATE == PA_HS_MODE_B)
 							? true : false;
 
+	if (has_acpi_companion(hba->dev) &&
+	    hba->ufs_version == UFSHCI_VERSION_30)
+		return ufs_qcom_phy_init(hba);
+
 	/* Reset UFS Host Controller and PHY */
 	ret = ufs_qcom_host_reset(hba);
 	if (ret)
diff --git a/drivers/scsi/ufs/ufs-qcom.h b/drivers/scsi/ufs/ufs-qcom.h
index 8208e3a3ef59..69a26f0f05ce 100644
--- a/drivers/scsi/ufs/ufs-qcom.h
+++ b/drivers/scsi/ufs/ufs-qcom.h
@@ -269,4 +269,15 @@  static inline int ufs_qcom_ice_resume(struct ufs_qcom_host *host)
 #define ufs_qcom_ice_program_key NULL
 #endif /* !CONFIG_SCSI_UFS_CRYPTO */
 
+/* ufs-qcom-phy.c */
+
+#ifdef CONFIG_ACPI
+int ufs_qcom_phy_init(struct ufs_hba *hba);
+#else
+static inline int ufs_qcom_phy_init(struct ufs_hba *hba)
+{
+	return 0;
+}
+#endif
+
 #endif /* UFS_QCOM_H_ */
diff --git a/drivers/scsi/ufs/ufshci.h b/drivers/scsi/ufs/ufshci.h
index 6795e1f0e8f8..48f6c191f26b 100644
--- a/drivers/scsi/ufs/ufshci.h
+++ b/drivers/scsi/ufs/ufshci.h
@@ -80,6 +80,7 @@  enum {
 	UFSHCI_VERSION_11 = 0x00010100, /* 1.1 */
 	UFSHCI_VERSION_20 = 0x00000200, /* 2.0 */
 	UFSHCI_VERSION_21 = 0x00000210, /* 2.1 */
+	UFSHCI_VERSION_30 = 0x00000300, /* 3.0 */
 };
 
 /*