All,

This patch is dependent and come on top of draviv 16 patch-set
In this patch we introduce 8 new ufs-msm files that we are working with,
using msm chips.
Please review and comment

Thanks,
Yaniv

-----Original Message-----
From: linux-scsi-ow...@vger.kernel.org
[mailto:linux-scsi-ow...@vger.kernel.org] On Behalf Of Yaniv Gardi
Sent: Thursday, August 14, 2014 5:22 PM
To: james.bottom...@hansenpartnership.com; h...@infradead.org
Cc: linux-ker...@vger.kernel.org; linux-scsi@vger.kernel.org;
linux-arm-...@vger.kernel.org; santos...@gmail.com;
linux-scsi-ow...@vger.kernel.org; subha...@codeaurora.org;
yga...@codeaurora.org; n...@codeaurora.org; dra...@codeaurora.org; Rob
Herring; Pawel Moll; Mark Rutland; Ian Campbell; Kumar Gala; Vinayak
Holikatti; James E.J. Bottomley; Grant Likely; Sujit Reddy Thumma; Sahitya
Tummala; open list:OPEN FIRMWARE AND...
Subject: [PATCH v3] scsi: ufs-msm: add UFS controller support for Qualcomm
MSM chips

The files in this change implement the UFS HW (controller & PHY) specific
behavior in Qualcomm MSM chips.

Signed-off-by: Yaniv Gardi <yga...@codeaurora.org>
---
 Documentation/devicetree/bindings/ufs/ufs-msm.txt  |   37 +
 .../devicetree/bindings/ufs/ufshcd-pltfrm.txt      |    4 +
 drivers/scsi/ufs/Kconfig                           |   12 +
 drivers/scsi/ufs/Makefile                          |    4 +
 drivers/scsi/ufs/ufs-msm-phy-qmp-20nm.c            |  254 +++++
 drivers/scsi/ufs/ufs-msm-phy-qmp-20nm.h            |  216 ++++
 drivers/scsi/ufs/ufs-msm-phy-qmp-28nm.c            |  368 +++++++
 drivers/scsi/ufs/ufs-msm-phy-qmp-28nm.h            |  735 +++++++++++++
 drivers/scsi/ufs/ufs-msm-phy.c                     |  646 ++++++++++++
 drivers/scsi/ufs/ufs-msm-phy.h                     |  193 ++++
 drivers/scsi/ufs/ufs-msm.c                         | 1105
++++++++++++++++++++
 drivers/scsi/ufs/ufs-msm.h                         |  158 +++
 12 files changed, 3732 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/ufs/ufs-msm.txt
 create mode 100644 drivers/scsi/ufs/ufs-msm-phy-qmp-20nm.c
 create mode 100644 drivers/scsi/ufs/ufs-msm-phy-qmp-20nm.h
 create mode 100644 drivers/scsi/ufs/ufs-msm-phy-qmp-28nm.c
 create mode 100644 drivers/scsi/ufs/ufs-msm-phy-qmp-28nm.h
 create mode 100644 drivers/scsi/ufs/ufs-msm-phy.c
 create mode 100644 drivers/scsi/ufs/ufs-msm-phy.h
 create mode 100644 drivers/scsi/ufs/ufs-msm.c
 create mode 100644 drivers/scsi/ufs/ufs-msm.h

diff --git a/Documentation/devicetree/bindings/ufs/ufs-msm.txt
b/Documentation/devicetree/bindings/ufs/ufs-msm.txt
new file mode 100644
index 0000000..b5caace
--- /dev/null
+++ b/Documentation/devicetree/bindings/ufs/ufs-msm.txt
@@ -0,0 +1,37 @@
+* MSM Universal Flash Storage (UFS) PHY
+
+UFSPHY nodes are defined to describe on-chip UFS PHY hardware macro.
+Each UFS PHY node should have its own node.
+
+To bind UFS PHY with UFS host controller, the controller node should
+contain a phandle reference to UFS PHY node.
+
+Required properties:
+- compatible        : compatible list, contains "qcom,ufs-msm-phy-qmp-28nm"
+                      or "qcom,ufs-msm-phy-qmp-20nm" according to the
relevant
+                      phy in use
+- reg               : <registers mapping>
+- #phy-cells         : This property shall be set to 0
+- vdda-phy-supply   : phandle to main PHY supply for analog domain
+- vdda-pll-supply   : phandle to PHY PLL and Power-Gen block power supply
+
+Optional properties:
+- vdda-phy-max-microamp : specifies max. load that can be drawn from phy
supply
+- vdda-pll-max-microamp : specifies max. load that can be drawn from pll
supply
+
+Example:
+
+       ufsphy1: ufsphy@0xfc597000 {
+               compatible = "qcom,ufs-msm-phy-qmp-28nm";
+               reg = <0xfc597000 0x800>;
+               #phy-cells = <0>;
+               vdda-phy-supply = <&pma8084_l4>;
+               vdda-pll-supply = <&pma8084_l12>;
+               vdda-phy-max-microamp = <50000>;
+               vdda-pll-max-microamp = <1000>;
+       };
+
+       ufshc@0xfc598000 {
+               ...
+               phys = <&ufsphy1>;
+       };
diff --git a/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt
b/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt
index e73a619..378585c 100644
--- a/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt
+++ b/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt
@@ -9,6 +9,9 @@ Required properties:
 - reg               : <registers mapping>
 
 Optional properties:
+- phys                 : phandle to UFS PHY node
+- phy-names            : the string "ufs_msm_phy" when is found in a node,
along
+                         with "phys" attribute, provides phandle to UFS PHY
node
 - vcc-supply            : phandle to VCC supply regulator node
 - vccq-supply           : phandle to VCCQ supply regulator node
 - vccq2-supply          : phandle to VCCQ2 supply regulator node
@@ -39,6 +42,7 @@ Example:
                reg = <0xfc598000 0x800>;
                interrupts = <0 28 0>;
 
+               ufs-phy = <&ufsphy>;
                vcc-supply = <&xxx_reg1>;
                vcc-supply-1p8;
                vccq-supply = <&xxx_reg2>;
diff --git a/drivers/scsi/ufs/Kconfig b/drivers/scsi/ufs/Kconfig
index 6e07b2a..a8259e0 100644
--- a/drivers/scsi/ufs/Kconfig
+++ b/drivers/scsi/ufs/Kconfig
@@ -70,3 +70,15 @@ config SCSI_UFSHCD_PLATFORM
        If you have a controller with this interface, say Y or M here.
 
          If unsure, say N.
+
+config SCSI_UFS_MSM
+       bool "MSM specific hooks to UFS controller platform driver"
+       depends on SCSI_UFSHCD_PLATFORM && ARCH_MSM
+       help
+         This selects the MSM specific additions to UFSHCD platform driver.
+         UFS host on MSM needs some vendor specific configuration before
+         accessing the hardware which includes PHY configuration and vendor
+         specific registers.
+
+         Select this if you have UFS controller on MSM chipset.
+         If unsure, say N.
diff --git a/drivers/scsi/ufs/Makefile b/drivers/scsi/ufs/Makefile
index 1e5bd48..35b6e4d 100644
--- a/drivers/scsi/ufs/Makefile
+++ b/drivers/scsi/ufs/Makefile
@@ -1,4 +1,8 @@
 # UFSHCD makefile
+obj-$(CONFIG_SCSI_UFS_MSM) += ufs-msm.o
+obj-$(CONFIG_SCSI_UFS_MSM) += ufs-msm-phy.o
+obj-$(CONFIG_SCSI_UFS_MSM) += ufs-msm-phy-qmp-28nm.o
+obj-$(CONFIG_SCSI_UFS_MSM) += ufs-msm-phy-qmp-20nm.o
 obj-$(CONFIG_SCSI_UFSHCD) += ufshcd.o
 obj-$(CONFIG_SCSI_UFSHCD_PCI) += ufshcd-pci.o
 obj-$(CONFIG_SCSI_UFSHCD_PLATFORM) += ufshcd-pltfrm.o
diff --git a/drivers/scsi/ufs/ufs-msm-phy-qmp-20nm.c
b/drivers/scsi/ufs/ufs-msm-phy-qmp-20nm.c
new file mode 100644
index 0000000..2b48bac
--- /dev/null
+++ b/drivers/scsi/ufs/ufs-msm-phy-qmp-20nm.c
@@ -0,0 +1,254 @@
+/*
+ * Copyright (c) 2013-2014, Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/time.h>
+#include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/iopoll.h>
+#include <linux/platform_device.h>
+#include <linux/msm-bus.h>
+
+#include "ufshcd.h"
+#include "unipro.h"
+#include "ufs-msm.h"
+#include "ufs-msm-phy.h"
+#include "ufs-msm-phy-qmp-20nm.h"
+
+#define UFS_PHY_NAME "ufs_msm_phy_qmp_20nm"
+
+static int ufs_msm_phy_qmp_20nm_phy_calibrate(struct ufs_msm_phy
*ufs_msm_phy)
+{
+       struct ufs_msm_phy_calibration *tbl_A, *tbl_B;
+       int tbl_size_A, tbl_size_B;
+       int rate = UFS_MSM_LIMIT_HS_RATE;
+       int err;
+
+       tbl_size_A = ARRAY_SIZE(phy_cal_table_rate_A);
+       tbl_A = phy_cal_table_rate_A;
+
+       tbl_size_B = ARRAY_SIZE(phy_cal_table_rate_B);
+       tbl_B = phy_cal_table_rate_B;
+
+       err = ufs_msm_phy_calibrate(ufs_msm_phy, tbl_A, tbl_size_A,
+                                               tbl_B, tbl_size_B, rate);
+
+       if (err)
+               dev_err(ufs_msm_phy->dev, "%s: ufs_msm_phy_calibrate()
failed %d\n",
+                       __func__, err);
+
+       return err;
+}
+
+static int ufs_msm_phy_qmp_20nm_init(struct phy *generic_phy)
+{
+       struct ufs_msm_phy_qmp_20nm *phy = phy_get_drvdata(generic_phy);
+       struct ufs_msm_phy *phy_common = &phy->common_cfg;
+       int err = 0;
+
+       err = ufs_msm_phy_init_clks(generic_phy, phy_common);
+       if (err) {
+               dev_err(phy_common->dev, "%s: ufs_msm_phy_init_clks() failed
%d\n",
+                       __func__, err);
+               goto out;
+       }
+
+       err = ufs_msm_phy_init_vregulators(generic_phy, phy_common);
+       if (err)
+               dev_err(phy_common->dev, "%s: ufs_msm_phy_init_vregulators()
failed %d\n",
+                       __func__, err);
+
+out:
+       return err;
+}
+
+static
+void ufs_msm_phy_qmp_20nm_power_control(struct ufs_msm_phy *phy, bool val)
+{
+       if (val) {
+               writel_relaxed(0x1, phy->mmio + UFS_PHY_POWER_DOWN_CONTROL);
+               /*
+                * Before any transactions involving PHY, ensure PHY knows
+                * that it's analog rail is powered ON.
+                */
+               mb();
+
+               if (phy->quirks &
+
MSM_UFS_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE) {
+                       /*
+                        * Give atleast 1us delay after restoring PHY analog
+                        * power.
+                        */
+                       usleep_range(1, 2);
+                       writel_relaxed(0x0A, phy->mmio +
+                                      QSERDES_COM_SYSCLK_EN_SEL_TXBAND);
+                       writel_relaxed(0x08, phy->mmio +
+                                      QSERDES_COM_SYSCLK_EN_SEL_TXBAND);
+                       /*
+                        * Make sure workaround is deactivated before
proceeding
+                        * with normal PHY operations.
+                        */
+                       mb();
+               }
+       } else {
+               if (phy->quirks &
+
MSM_UFS_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE) {
+                       writel_relaxed(0x0A, phy->mmio +
+                                      QSERDES_COM_SYSCLK_EN_SEL_TXBAND);
+                       writel_relaxed(0x02, phy->mmio +
+                                      QSERDES_COM_SYSCLK_EN_SEL_TXBAND);
+                       /*
+                        * Make sure that above workaround is activated
before
+                        * PHY analog power collapse.
+                        */
+                       mb();
+               }
+
+               writel_relaxed(0x0, phy->mmio + UFS_PHY_POWER_DOWN_CONTROL);
+               /*
+                * ensure that PHY knows its PHY analog rail is going
+                * to be powered down
+                */
+               mb();
+       }
+}
+
+static
+void ufs_msm_phy_qmp_20nm_set_tx_lane_enable(struct ufs_msm_phy *phy, u32
val)
+{
+       writel_relaxed(val & UFS_PHY_TX_LANE_ENABLE_MASK,
+                       phy->mmio + UFS_PHY_TX_LANE_ENABLE);
+       mb();
+}
+
+static inline void ufs_msm_phy_qmp_20nm_start_serdes(struct ufs_msm_phy
*phy)
+{
+       u32 tmp;
+
+       tmp = readl_relaxed(phy->mmio + UFS_PHY_PHY_START);
+       tmp &= ~MASK_SERDES_START;
+       tmp |= (1 << OFFSET_SERDES_START);
+       writel_relaxed(tmp, phy->mmio + UFS_PHY_PHY_START);
+       mb();
+}
+
+static int ufs_msm_phy_qmp_20nm_is_pcs_ready(struct ufs_msm_phy
*phy_common)
+{
+       int err = 0;
+       u32 val;
+
+       err = readl_poll_timeout(phy_common->mmio +
UFS_PHY_PCS_READY_STATUS,
+                       val, (val & MASK_PCS_READY), 10, 1000000);
+       if (err)
+               dev_err(phy_common->dev, "%s: poll for pcs failed err =
%d\n",
+                       __func__, err);
+       return err;
+}
+
+static void ufs_msm_phy_qmp_20nm_advertise_quirks(struct phy *generic_phy)
+{
+       struct ufs_msm_phy_qmp_20nm *phy =  phy_get_drvdata(generic_phy);
+       struct ufs_msm_phy *phy_common = &(phy->common_cfg);
+
+       phy_common->quirks =
+               MSM_UFS_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE;
+}
+
+struct phy_ops ufs_msm_phy_qmp_20nm_phy_ops = {
+       .init           = ufs_msm_phy_qmp_20nm_init,
+       .exit           = ufs_msm_phy_exit,
+       .power_on       = ufs_msm_phy_power_on,
+       .power_off      = ufs_msm_phy_power_off,
+       .advertise_quirks = ufs_msm_phy_qmp_20nm_advertise_quirks,
+       .owner          = THIS_MODULE,
+};
+
+struct ufs_msm_phy_specific_ops phy_20nm_ops = {
+       .calibrate_phy          = ufs_msm_phy_qmp_20nm_phy_calibrate,
+       .start_serdes           = ufs_msm_phy_qmp_20nm_start_serdes,
+       .is_physical_coding_sublayer_ready =
ufs_msm_phy_qmp_20nm_is_pcs_ready,
+       .set_tx_lane_enable     = ufs_msm_phy_qmp_20nm_set_tx_lane_enable,
+       .power_control          = ufs_msm_phy_qmp_20nm_power_control,
+};
+
+static int ufs_msm_phy_qmp_20nm_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct phy *generic_phy;
+       struct ufs_msm_phy_qmp_20nm *phy;
+       int err = 0;
+
+       phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
+       if (!phy) {
+               dev_err(dev, "%s: failed to allocate phy\n", __func__);
+               err = -ENOMEM;
+               goto out;
+       }
+
+       generic_phy = ufs_msm_phy_generic_probe(pdev, &phy->common_cfg,
+                               &ufs_msm_phy_qmp_20nm_phy_ops,
&phy_20nm_ops);
+
+       if (!generic_phy) {
+               dev_err(dev, "%s: ufs_msm_phy_generic_probe() failed\n",
+                       __func__);
+               err = -EIO;
+               goto out;
+       }
+
+       phy_set_drvdata(generic_phy, phy);
+
+       strlcpy(phy->common_cfg.name, UFS_PHY_NAME,
+                       sizeof(phy->common_cfg.name));
+
+out:
+       return err;
+}
+
+static int ufs_msm_phy_qmp_20nm_remove(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct phy *generic_phy = to_phy(dev);
+       struct ufs_msm_phy *ufs_msm_phy = get_ufs_msm_phy(generic_phy);
+       int err = 0;
+
+       err = ufs_msm_phy_remove(generic_phy, ufs_msm_phy);
+       if (err)
+               dev_err(dev, "%s: ufs_msm_phy_remove failed = %d\n",
+                       __func__, err);
+
+       return err;
+}
+
+static const struct of_device_id ufs_msm_phy_qmp_20nm_of_match[] = {
+       {.compatible = "qcom,ufs-msm-phy-qmp-20nm"},
+       {},
+};
+MODULE_DEVICE_TABLE(of, ufs_msm_phy_qmp_20nm_of_match);
+
+static struct platform_driver ufs_msm_phy_qmp_20nm_driver = {
+       .probe = ufs_msm_phy_qmp_20nm_probe,
+       .remove = ufs_msm_phy_qmp_20nm_remove,
+       .driver = {
+               .of_match_table = ufs_msm_phy_qmp_20nm_of_match,
+               .name = "ufs_msm_phy_qmp_20nm",
+               .owner = THIS_MODULE,
+       },
+};
+
+module_platform_driver(ufs_msm_phy_qmp_20nm_driver);
+
+MODULE_DESCRIPTION("Universal Flash Storage (UFS) MSM PHY QMP 20nm");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/scsi/ufs/ufs-msm-phy-qmp-20nm.h
b/drivers/scsi/ufs/ufs-msm-phy-qmp-20nm.h
new file mode 100644
index 0000000..13bf2f7
--- /dev/null
+++ b/drivers/scsi/ufs/ufs-msm-phy-qmp-20nm.h
@@ -0,0 +1,216 @@
+/*
+ * Copyright (c) 2013-2014, Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef UFS_MSM_PHY_QMP_20NM_H_
+#define UFS_MSM_PHY_QMP_20NM_H_
+
+#include "ufs-msm-phy.h"
+
+/* MSM UFS PHY control registers */
+
+#define COM_OFF(x)     (0x000 + x)
+#define PHY_OFF(x)     (0xC00 + x)
+#define TX_OFF(n, x)   (0x400 + (0x400 * n) + x)
+#define RX_OFF(n, x)   (0x600 + (0x400 * n) + x)
+
+/* UFS PHY PLL block registers */
+#define QSERDES_COM_SYS_CLK_CTRL               COM_OFF(0x0)
+#define QSERDES_COM_PLL_VCOTAIL_EN             COM_OFF(0x04)
+#define QSERDES_COM_PLL_CNTRL                  COM_OFF(0x14)
+#define QSERDES_COM_PLL_IP_SETI                        COM_OFF(0x24)
+#define QSERDES_COM_CORE_CLK_IN_SYNC_SEL       COM_OFF(0x28)
+#define QSERDES_COM_PLL_CP_SETI                        COM_OFF(0x34)
+#define QSERDES_COM_PLL_IP_SETP                        COM_OFF(0x38)
+#define QSERDES_COM_PLL_CP_SETP                        COM_OFF(0x3C)
+#define QSERDES_COM_SYSCLK_EN_SEL_TXBAND       COM_OFF(0x48)
+#define QSERDES_COM_RESETSM_CNTRL              COM_OFF(0x4C)
+#define QSERDES_COM_RESETSM_CNTRL2             COM_OFF(0x50)
+#define QSERDES_COM_PLLLOCK_CMP1               COM_OFF(0x90)
+#define QSERDES_COM_PLLLOCK_CMP2               COM_OFF(0x94)
+#define QSERDES_COM_PLLLOCK_CMP3               COM_OFF(0x98)
+#define QSERDES_COM_PLLLOCK_CMP_EN             COM_OFF(0x9C)
+#define QSERDES_COM_BGTC                       COM_OFF(0xA0)
+#define QSERDES_COM_DEC_START1                 COM_OFF(0xAC)
+#define QSERDES_COM_PLL_AMP_OS                 COM_OFF(0xB0)
+#define QSERDES_COM_DIV_FRAC_START1            COM_OFF(0x100)
+#define QSERDES_COM_DIV_FRAC_START2            COM_OFF(0x104)
+#define QSERDES_COM_DIV_FRAC_START3            COM_OFF(0x108)
+#define QSERDES_COM_DEC_START2                 COM_OFF(0x10C)
+#define QSERDES_COM_PLL_RXTXEPCLK_EN           COM_OFF(0x110)
+#define QSERDES_COM_PLL_CRCTRL                 COM_OFF(0x114)
+#define QSERDES_COM_PLL_CLKEPDIV               COM_OFF(0x118)
+
+/* TX LANE n (0, 1) registers */
+#define QSERDES_TX_EMP_POST1_LVL(n)            TX_OFF(n, 0x08)
+#define QSERDES_TX_DRV_LVL(n)                  TX_OFF(n, 0x0C)
+#define QSERDES_TX_LANE_MODE(n)                        TX_OFF(n, 0x54)
+
+/* RX LANE n (0, 1) registers */
+#define QSERDES_RX_CDR_CONTROL1(n)             RX_OFF(n, 0x0)
+#define QSERDES_RX_CDR_CONTROL_HALF(n)         RX_OFF(n, 0x8)
+#define QSERDES_RX_RX_EQ_GAIN1_LSB(n)          RX_OFF(n, 0xA8)
+#define QSERDES_RX_RX_EQ_GAIN1_MSB(n)          RX_OFF(n, 0xAC)
+#define QSERDES_RX_RX_EQ_GAIN2_LSB(n)          RX_OFF(n, 0xB0)
+#define QSERDES_RX_RX_EQ_GAIN2_MSB(n)          RX_OFF(n, 0xB4)
+#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2(n)    RX_OFF(n, 0xBC)
+#define QSERDES_RX_CDR_CONTROL_QUARTER(n)      RX_OFF(n, 0xC)
+#define QSERDES_RX_SIGDET_CNTRL(n)             RX_OFF(n, 0x100)
+
+/* UFS PHY registers */
+#define UFS_PHY_PHY_START                      PHY_OFF(0x00)
+#define UFS_PHY_POWER_DOWN_CONTROL             PHY_OFF(0x4)
+#define UFS_PHY_TX_LANE_ENABLE                 PHY_OFF(0x44)
+#define UFS_PHY_PWM_G1_CLK_DIVIDER             PHY_OFF(0x08)
+#define UFS_PHY_PWM_G2_CLK_DIVIDER             PHY_OFF(0x0C)
+#define UFS_PHY_PWM_G3_CLK_DIVIDER             PHY_OFF(0x10)
+#define UFS_PHY_PWM_G4_CLK_DIVIDER             PHY_OFF(0x14)
+#define UFS_PHY_CORECLK_PWM_G1_CLK_DIVIDER     PHY_OFF(0x34)
+#define UFS_PHY_CORECLK_PWM_G2_CLK_DIVIDER     PHY_OFF(0x38)
+#define UFS_PHY_CORECLK_PWM_G3_CLK_DIVIDER     PHY_OFF(0x3C)
+#define UFS_PHY_CORECLK_PWM_G4_CLK_DIVIDER     PHY_OFF(0x40)
+#define UFS_PHY_OMC_STATUS_RDVAL               PHY_OFF(0x68)
+#define UFS_PHY_LINE_RESET_TIME                        PHY_OFF(0x28)
+#define UFS_PHY_LINE_RESET_GRANULARITY         PHY_OFF(0x2C)
+#define UFS_PHY_TSYNC_RSYNC_CNTL               PHY_OFF(0x48)
+#define UFS_PHY_PLL_CNTL                       PHY_OFF(0x50)
+#define UFS_PHY_TX_LARGE_AMP_DRV_LVL           PHY_OFF(0x54)
+#define UFS_PHY_TX_SMALL_AMP_DRV_LVL           PHY_OFF(0x5C)
+#define UFS_PHY_TX_LARGE_AMP_POST_EMP_LVL      PHY_OFF(0x58)
+#define UFS_PHY_TX_SMALL_AMP_POST_EMP_LVL      PHY_OFF(0x60)
+#define UFS_PHY_CFG_CHANGE_CNT_VAL             PHY_OFF(0x64)
+#define UFS_PHY_RX_SYNC_WAIT_TIME              PHY_OFF(0x6C)
+#define UFS_PHY_TX_MIN_SLEEP_NOCONFIG_TIME_CAPABILITY  PHY_OFF(0xB4)
+#define UFS_PHY_RX_MIN_SLEEP_NOCONFIG_TIME_CAPABILITY  PHY_OFF(0xE0)
+#define UFS_PHY_TX_MIN_STALL_NOCONFIG_TIME_CAPABILITY  PHY_OFF(0xB8)
+#define UFS_PHY_RX_MIN_STALL_NOCONFIG_TIME_CAPABILITY  PHY_OFF(0xE4)
+#define UFS_PHY_TX_MIN_SAVE_CONFIG_TIME_CAPABILITY     PHY_OFF(0xBC)
+#define UFS_PHY_RX_MIN_SAVE_CONFIG_TIME_CAPABILITY     PHY_OFF(0xE8)
+#define UFS_PHY_RX_PWM_BURST_CLOSURE_LENGTH_CAPABILITY PHY_OFF(0xFC)
+#define UFS_PHY_RX_MIN_ACTIVATETIME_CAPABILITY         PHY_OFF(0x100)
+#define UFS_PHY_RMMI_ATTR_CTRL                 PHY_OFF(0x160)
+#define UFS_PHY_RMMI_RX_CFGUPDT_L1     (1 << 7)
+#define UFS_PHY_RMMI_TX_CFGUPDT_L1     (1 << 6)
+#define UFS_PHY_RMMI_CFGWR_L1          (1 << 5)
+#define UFS_PHY_RMMI_CFGRD_L1          (1 << 4)
+#define UFS_PHY_RMMI_RX_CFGUPDT_L0     (1 << 3)
+#define UFS_PHY_RMMI_TX_CFGUPDT_L0     (1 << 2)
+#define UFS_PHY_RMMI_CFGWR_L0          (1 << 1)
+#define UFS_PHY_RMMI_CFGRD_L0          (1 << 0)
+#define UFS_PHY_RMMI_ATTRID                    PHY_OFF(0x164)
+#define UFS_PHY_RMMI_ATTRWRVAL                 PHY_OFF(0x168)
+#define UFS_PHY_RMMI_ATTRRDVAL_L0_STATUS       PHY_OFF(0x16C)
+#define UFS_PHY_RMMI_ATTRRDVAL_L1_STATUS       PHY_OFF(0x170)
+#define UFS_PHY_PCS_READY_STATUS               PHY_OFF(0x174)
+
+#define UFS_PHY_TX_LANE_ENABLE_MASK            0x3
+
+/*
+ * This structure represents the 20nm specific phy.
+ * common_cfg MUST remain the first field in this structure
+ * in case extra fields are added. This way, when calling
+ * get_ufs_msm_phy() of generic phy, we can extract the
+ * common phy structure (struct ufs_msm_phy) out of it
+ * regardless of the relevant specific phy.
+ */
+struct ufs_msm_phy_qmp_20nm {
+       struct ufs_msm_phy common_cfg;
+};
+
+static struct ufs_msm_phy_calibration phy_cal_table_rate_A[] = {
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_POWER_DOWN_CONTROL, 0x01),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_PLL_VCOTAIL_EN, 0xe1),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CRCTRL, 0xcc),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_SYSCLK_EN_SEL_TXBAND, 0x08),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CLKEPDIV, 0x03),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_PLL_RXTXEPCLK_EN, 0x10),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START1, 0x82),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START2, 0x03),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START1, 0x80),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START2, 0x80),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START3, 0x40),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP1, 0xff),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP2, 0x19),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP3, 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP_EN, 0x03),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_RESETSM_CNTRL, 0x90),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_RESETSM_CNTRL2, 0x03),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL1(0), 0xf2),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_HALF(0), 0x0c),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_QUARTER(0), 0x12),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL1(1), 0xf2),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_HALF(1), 0x0c),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_QUARTER(1), 0x12),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_LSB(0), 0xff),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_MSB(0), 0xff),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_LSB(0), 0xff),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_MSB(0), 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_LSB(1), 0xff),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_MSB(1), 0xff),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_LSB(1), 0xff),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_MSB(1), 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CP_SETI, 0x3f),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_PLL_IP_SETP, 0x1b),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CP_SETP, 0x0f),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_PLL_IP_SETI, 0x01),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_TX_EMP_POST1_LVL(0), 0x2F),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_TX_DRV_LVL(0), 0x20),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_TX_EMP_POST1_LVL(1), 0x2F),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_TX_DRV_LVL(1), 0x20),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_TX_LANE_MODE(0), 0x68),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_TX_LANE_MODE(1), 0x68),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2(1), 0xdc),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2(0), 0xdc),
+};
+
+static struct ufs_msm_phy_calibration phy_cal_table_rate_B[] = {
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_POWER_DOWN_CONTROL, 0x01),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CRCTRL, 0xcc),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_SYSCLK_EN_SEL_TXBAND, 0x08),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CLKEPDIV, 0x03),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_PLL_RXTXEPCLK_EN, 0x10),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START1, 0x98),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START2, 0x03),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START1, 0x80),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START2, 0x80),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START3, 0x40),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP1, 0x65),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP2, 0x1e),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP3, 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP_EN, 0x03),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_RESETSM_CNTRL, 0x90),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL1(0), 0xf2),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_HALF(0), 0x0c),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_QUARTER(0), 0x12),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL1(1), 0xf2),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_HALF(1), 0x0c),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_QUARTER(1), 0x12),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_LSB(0), 0xff),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_MSB(0), 0xff),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_LSB(0), 0xff),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_MSB(0), 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_LSB(1), 0xff),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_MSB(1), 0xff),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_LSB(1), 0xff),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_MSB(1), 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CP_SETI, 0x3f),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_PLL_IP_SETP, 0x1b),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CP_SETP, 0x0f),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_PLL_IP_SETI, 0x01),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_TX_LANE_MODE(0), 0x68),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_TX_LANE_MODE(1), 0x68),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2(1), 0xdc),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2(0), 0xdc),
+};
+
+#endif
diff --git a/drivers/scsi/ufs/ufs-msm-phy-qmp-28nm.c
b/drivers/scsi/ufs/ufs-msm-phy-qmp-28nm.c
new file mode 100644
index 0000000..6c82a59
--- /dev/null
+++ b/drivers/scsi/ufs/ufs-msm-phy-qmp-28nm.c
@@ -0,0 +1,368 @@
+/*
+ * Copyright (c) 2013-2014, Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/time.h>
+#include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/iopoll.h>
+#include <linux/platform_device.h>
+#include <linux/msm-bus.h>
+#include <linux/phy/phy.h>
+
+#include "ufshcd.h"
+#include "unipro.h"
+#include "ufs-msm.h"
+#include "ufs-msm-phy.h"
+#include "ufs-msm-phy-qmp-28nm.h"
+
+#define UFS_PHY_NAME "ufs_msm_phy_qmp_28nm"
+
+static
+void ufs_msm_phy_qmp_28nm_power_control(struct ufs_msm_phy *phy, bool val)
+{
+       if (val) {
+               writel_relaxed(0x1, phy->mmio + UFS_PHY_POWER_DOWN_CONTROL);
+               /*
+                * Before any transactions involving PHY, ensure PHY knows
+                * that it's analog rail is powered ON. This also ensures
+                * that PHY is out of power collapse before enabling the
+                * SIGDET.
+                */
+               mb();
+               if (phy->quirks &
MSM_UFS_PHY_DIS_SIGDET_BEFORE_PWR_COLLAPSE) {
+                       writel_relaxed(0xC0,
+                               phy->mmio + QSERDES_RX_SIGDET_CNTRL(0));
+                       writel_relaxed(0xC0,
+                               phy->mmio + QSERDES_RX_SIGDET_CNTRL(1));
+                       /*
+                        * make sure that SIGDET is enabled before
proceeding
+                        * further.
+                        */
+                        mb();
+               }
+       } else {
+                if (phy->quirks &
MSM_UFS_PHY_DIS_SIGDET_BEFORE_PWR_COLLAPSE) {
+                       writel_relaxed(0x0,
+                               phy->mmio + QSERDES_RX_SIGDET_CNTRL(0));
+                       writel_relaxed(0x0,
+                               phy->mmio + QSERDES_RX_SIGDET_CNTRL(1));
+                       /*
+                        * Ensure that SIGDET is disabled before PHY power
+                        * collapse
+                        */
+                       mb();
+               }
+               writel_relaxed(0x0, phy->mmio + UFS_PHY_POWER_DOWN_CONTROL);
+               /*
+                * ensure that PHY knows its PHY analog rail is going
+                * to be powered down
+                */
+               mb();
+       }
+}
+
+static int ufs_msm_phy_qmp_28nm_init(struct phy *generic_phy)
+{
+       struct ufs_msm_phy_qmp_28nm *phy = phy_get_drvdata(generic_phy);
+       struct ufs_msm_phy *phy_common = &phy->common_cfg;
+       int err = 0;
+
+       err = ufs_msm_phy_init_clks(generic_phy, phy_common);
+       if (err) {
+               dev_err(phy_common->dev, "%s: ufs_msm_phy_init_clks() failed
%d\n",
+                       __func__, err);
+               goto out;
+       }
+
+       err = ufs_msm_phy_init_vregulators(generic_phy, phy_common);
+       if (err)
+               dev_err(phy_common->dev, "%s: ufs_msm_phy_init_vregulators()
failed %d\n",
+                       __func__, err);
+
+out:
+       return err;
+}
+
+static int ufs_msm_phy_qmp_28nm_calibrate(struct ufs_msm_phy *ufs_msm_phy)
+{
+       struct ufs_msm_phy_calibration *tbl_A, *tbl_B;
+       int tbl_size_A, tbl_size_B;
+       int rate = UFS_MSM_LIMIT_HS_RATE;
+       u8 major = ufs_msm_phy->host_ctrl_rev_major;
+       u16 minor = ufs_msm_phy->host_ctrl_rev_minor;
+       u16 step = ufs_msm_phy->host_ctrl_rev_step;
+       int err;
+
+       if ((major == 0x1) && (minor == 0x001) && (step == 0x0000)) {
+               tbl_size_A = ARRAY_SIZE(phy_cal_table_ctrl_1_1_0_rate_A);
+               tbl_A = phy_cal_table_ctrl_1_1_0_rate_A;
+       } else if ((major == 0x1) && (minor == 0x001) && (step == 0x0001)) {
+               tbl_size_A = ARRAY_SIZE(phy_cal_table_ctrl_1_1_1_rate_A);
+               tbl_A = phy_cal_table_ctrl_1_1_1_rate_A;
+       }
+
+       tbl_B = phy_cal_table_rate_B;
+       tbl_size_B = ARRAY_SIZE(phy_cal_table_rate_B);
+
+       err = ufs_msm_phy_calibrate(ufs_msm_phy, tbl_A, tbl_size_A,
+                             tbl_B, tbl_size_B, rate);
+       if (err)
+               dev_err(ufs_msm_phy->dev, "%s: ufs_msm_phy_calibrate()
failed %d\n",
+                       __func__, err);
+
+       return err;
+}
+
+static
+u32 ufs_msm_phy_qmp_28nm_read_attr(struct ufs_msm_phy *phy_common, u32
attr)
+
+{
+       u32 l0, l1;
+
+       writel_relaxed(attr, phy_common->mmio + UFS_PHY_RMMI_ATTRID);
+       /* Read attribute value for both lanes */
+       writel_relaxed((UFS_PHY_RMMI_CFGRD_L0 | UFS_PHY_RMMI_CFGRD_L1),
+                      phy_common->mmio + UFS_PHY_RMMI_ATTR_CTRL);
+
+       l0 = readl_relaxed(phy_common->mmio +
UFS_PHY_RMMI_ATTRRDVAL_L0_STATUS);
+       l1 = readl_relaxed(phy_common->mmio +
UFS_PHY_RMMI_ATTRRDVAL_L1_STATUS);
+       /* Both lanes should have the same value for same attribute type */
+       if (unlikely(l0 != l1))
+               dev_warn(phy_common->dev, "%s: attr 0x%x values are not same
for Lane-0 and Lane-1, l0=0x%x, l1=0x%x",
+                               __func__, attr, l0, l1);
+
+       /* must clear now */
+       writel_relaxed(0x00, phy_common->mmio + UFS_PHY_RMMI_ATTR_CTRL);
+
+       return l0;
+}
+
+static
+void ufs_msm_phy_qmp_28nm_save_configuration(struct ufs_msm_phy
*phy_common)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(cached_phy_regs); i++)
+               cached_phy_regs[i].cfg_value =
+                       readl_relaxed(phy_common->mmio +
+                                     cached_phy_regs[i].reg_offset);
+
+       for (i = 0; i < ARRAY_SIZE(cached_phy_attr); i++)
+               cached_phy_attr[i].value =
+                       ufs_msm_phy_qmp_28nm_read_attr(phy_common,
+                                       cached_phy_attr[i].att);
+}
+
+static
+void ufs_msm_phy_qmp_28nm_set_tx_lane_enable(struct ufs_msm_phy *phy, u32
val)
+{
+       writel_relaxed(val & UFS_PHY_TX_LANE_ENABLE_MASK,
+                       phy->mmio + UFS_PHY_TX_LANE_ENABLE);
+       mb();
+}
+
+static inline void ufs_msm_phy_qmp_28nm_start_serdes(struct ufs_msm_phy
*phy)
+{
+       u32 tmp;
+
+       tmp = readl_relaxed(phy->mmio + UFS_PHY_PHY_START);
+       tmp &= ~MASK_SERDES_START;
+       tmp |= (1 << OFFSET_SERDES_START);
+       writel_relaxed(tmp, phy->mmio + UFS_PHY_PHY_START);
+       mb();
+}
+
+static void
+ufs_msm_phy_qmp_28nm_write_attr(struct phy *generic_phy, u32 attr, u32 val)
+{
+       struct ufs_msm_phy_qmp_28nm *phy =  phy_get_drvdata(generic_phy);
+       struct ufs_msm_phy *phy_common = &(phy->common_cfg);
+
+       writel_relaxed(attr, phy_common->mmio + UFS_PHY_RMMI_ATTRID);
+       writel_relaxed(val, phy_common->mmio + UFS_PHY_RMMI_ATTRWRVAL);
+       /* update attribute for both lanes */
+       writel_relaxed((UFS_PHY_RMMI_CFGWR_L0 | UFS_PHY_RMMI_CFGWR_L1),
+                      phy_common->mmio + UFS_PHY_RMMI_ATTR_CTRL);
+       if (is_mphy_tx_attr(attr))
+               writel_relaxed((UFS_PHY_RMMI_TX_CFGUPDT_L0 |
+                               UFS_PHY_RMMI_TX_CFGUPDT_L1),
+                              phy_common->mmio + UFS_PHY_RMMI_ATTR_CTRL);
+       else
+               writel_relaxed((UFS_PHY_RMMI_RX_CFGUPDT_L0 |
+                               UFS_PHY_RMMI_RX_CFGUPDT_L1),
+                              phy_common->mmio + UFS_PHY_RMMI_ATTR_CTRL);
+
+       writel_relaxed(0x00, phy_common->mmio + UFS_PHY_RMMI_ATTR_CTRL);
+}
+
+static void ufs_msm_phy_qmp_28nm_restore_attrs(struct phy *generic_phy)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(cached_phy_attr); i++)
+               ufs_msm_phy_qmp_28nm_write_attr(generic_phy,
+                       cached_phy_attr[i].att, cached_phy_attr[i].value);
+}
+
+static int ufs_msm_phy_qmp_28nm_is_pcs_ready(struct ufs_msm_phy
*phy_common)
+{
+       int err = 0;
+       u32 val;
+
+       err = readl_poll_timeout(phy_common->mmio +
UFS_PHY_PCS_READY_STATUS,
+                       val, (val & MASK_PCS_READY), 10, 1000000);
+       if (err)
+               dev_err(phy_common->dev, "%s: phy init failed, %d\n",
+                       __func__, err);
+
+       return err;
+}
+
+static void ufs_msm_phy_qmp_28nm_advertise_quirks(struct phy *generic_phy)
+{
+       struct ufs_msm_phy_qmp_28nm *phy =  phy_get_drvdata(generic_phy);
+       struct ufs_msm_phy *phy_common = &(phy->common_cfg);
+
+       phy_common->quirks = MSM_UFS_PHY_QUIRK_CFG_RESTORE
+                               |
MSM_UFS_PHY_DIS_SIGDET_BEFORE_PWR_COLLAPSE;
+}
+
+static int ufs_msm_phy_qmp_28nm_suspend(struct phy *generic_phy)
+{
+       struct ufs_msm_phy_qmp_28nm *phy =  phy_get_drvdata(generic_phy);
+       struct ufs_msm_phy *phy_common = &(phy->common_cfg);
+
+       ufs_msm_phy_disable_ref_clk(generic_phy);
+       ufs_msm_phy_qmp_28nm_power_control(phy_common, false);
+
+       ufs_msm_phy_disable_vreg(generic_phy, &phy_common->vdda_phy);
+       ufs_msm_phy_disable_vreg(generic_phy, &phy_common->vdda_pll);
+
+       return 0;
+}
+
+static int ufs_msm_phy_qmp_28nm_resume(struct phy *generic_phy)
+{
+       struct ufs_msm_phy_qmp_28nm *phy = phy_get_drvdata(generic_phy);
+       struct ufs_msm_phy *phy_common = &phy->common_cfg;
+       int err = 0;
+
+       ufs_msm_phy_qmp_28nm_start_serdes(phy_common);
+
+       ufs_msm_phy_qmp_28nm_restore_attrs(generic_phy);
+
+       err = ufs_msm_phy_qmp_28nm_is_pcs_ready(phy_common);
+       if (err)
+               dev_err(phy_common->dev, "%s: failed to init phy = %d\n",
+                       __func__, err);
+
+       return err;
+}
+
+struct phy_ops ufs_msm_phy_qmp_28nm_phy_ops = {
+       .init           = ufs_msm_phy_qmp_28nm_init,
+       .exit           = ufs_msm_phy_exit,
+       .power_on       = ufs_msm_phy_power_on,
+       .power_off      = ufs_msm_phy_power_off,
+       .advertise_quirks = ufs_msm_phy_qmp_28nm_advertise_quirks,
+       .suspend        = ufs_msm_phy_qmp_28nm_suspend,
+       .resume         = ufs_msm_phy_qmp_28nm_resume,
+       .owner          = THIS_MODULE,
+};
+
+struct ufs_msm_phy_specific_ops phy_28nm_ops = {
+       .calibrate_phy          = ufs_msm_phy_qmp_28nm_calibrate,
+       .start_serdes           = ufs_msm_phy_qmp_28nm_start_serdes,
+       .save_configuration     = ufs_msm_phy_qmp_28nm_save_configuration,
+       .is_physical_coding_sublayer_ready =
ufs_msm_phy_qmp_28nm_is_pcs_ready,
+       .set_tx_lane_enable     = ufs_msm_phy_qmp_28nm_set_tx_lane_enable,
+       .power_control          = ufs_msm_phy_qmp_28nm_power_control,
+};
+
+static int ufs_msm_phy_qmp_28nm_probe(struct platform_device *pdev)
+{
+       struct ufs_msm_phy_qmp_28nm *phy;
+       struct device *dev = &pdev->dev;
+       int err = 0;
+       struct phy *generic_phy;
+
+       phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
+       if (!phy) {
+               err = -ENOMEM;
+               dev_err(dev, "%s: failed to allocate phy\n", __func__);
+               goto out;
+       }
+
+       phy->common_cfg.cached_regs =
+                       (struct ufs_msm_phy_calibration *)cached_phy_regs;
+       phy->common_cfg.cached_regs_table_size =
+                               ARRAY_SIZE(cached_phy_regs);
+
+       generic_phy = ufs_msm_phy_generic_probe(pdev, &phy->common_cfg,
+                               &ufs_msm_phy_qmp_28nm_phy_ops,
&phy_28nm_ops);
+
+       if (!generic_phy) {
+               dev_err(dev, "%s: ufs_msm_phy_generic_probe() failed\n",
+                       __func__);
+               err = -EIO;
+               goto out;
+       }
+
+       phy_set_drvdata(generic_phy, phy);
+
+       strlcpy(phy->common_cfg.name, UFS_PHY_NAME,
+                       sizeof(phy->common_cfg.name));
+
+out:
+       return err;
+}
+
+static int ufs_msm_phy_qmp_28nm_remove(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct phy *generic_phy = to_phy(dev);
+       struct ufs_msm_phy *ufs_msm_phy = get_ufs_msm_phy(generic_phy);
+       int err = 0;
+
+       err = ufs_msm_phy_remove(generic_phy, ufs_msm_phy);
+       if (err)
+               dev_err(dev, "%s: ufs_msm_phy_remove failed = %d\n",
+                       __func__, err);
+
+       return err;
+}
+
+static const struct of_device_id ufs_msm_phy_qmp_28nm_of_match[] = {
+       {.compatible = "qcom,ufs-msm-phy-qmp-28nm"},
+       {},
+};
+MODULE_DEVICE_TABLE(of, ufs_msm_phy_qmp_28nm_of_match);
+
+static struct platform_driver ufs_msm_phy_qmp_28nm_driver = {
+       .probe = ufs_msm_phy_qmp_28nm_probe,
+       .remove = ufs_msm_phy_qmp_28nm_remove,
+       .driver = {
+               .of_match_table = ufs_msm_phy_qmp_28nm_of_match,
+               .name = "ufs_msm_phy_qmp_28nm",
+               .owner = THIS_MODULE,
+       },
+};
+
+module_platform_driver(ufs_msm_phy_qmp_28nm_driver);
+
+MODULE_DESCRIPTION("Universal Flash Storage (UFS) MSM PHY QMP 28nm");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/scsi/ufs/ufs-msm-phy-qmp-28nm.h
b/drivers/scsi/ufs/ufs-msm-phy-qmp-28nm.h
new file mode 100644
index 0000000..edb2892
--- /dev/null
+++ b/drivers/scsi/ufs/ufs-msm-phy-qmp-28nm.h
@@ -0,0 +1,735 @@
+/*
+ * Copyright (c) 2013-2014, Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef UFS_MSM_PHY_QMP_28NM_H_
+#define UFS_MSM_PHY_QMP_28NM_H_
+
+#include "ufs-msm-phy.h"
+
+/* MSM UFS PHY control registers */
+
+#define COM_OFF(x)     (0x000 + x)
+#define PHY_OFF(x)     (0x700 + x)
+#define TX_OFF(n, x)   (0x100 + (0x400 * n) + x)
+#define RX_OFF(n, x)   (0x200 + (0x400 * n) + x)
+
+/* UFS PHY PLL block registers */
+#define QSERDES_COM_SYS_CLK_CTRL                            COM_OFF(0x00)
+#define QSERDES_COM_PLL_VCOTAIL_EN                          COM_OFF(0x04)
+#define QSERDES_COM_CMN_MODE                                COM_OFF(0x08)
+#define QSERDES_COM_IE_TRIM                                 COM_OFF(0x0C)
+#define QSERDES_COM_IP_TRIM                                 COM_OFF(0x10)
+#define QSERDES_COM_PLL_CNTRL                               COM_OFF(0x14)
+#define QSERDES_COM_PLL_IP_SETI                             COM_OFF(0x18)
+#define QSERDES_COM_CORE_CLK_IN_SYNC_SEL                    COM_OFF(0x1C)
+#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN                     COM_OFF(0x20)
+#define QSERDES_COM_PLL_CP_SETI                             COM_OFF(0x24)
+#define QSERDES_COM_PLL_IP_SETP                             COM_OFF(0x28)
+#define QSERDES_COM_PLL_CP_SETP                             COM_OFF(0x2C)
+#define QSERDES_COM_ATB_SEL1                                COM_OFF(0x30)
+#define QSERDES_COM_ATB_SEL2                                COM_OFF(0x34)
+#define QSERDES_COM_SYSCLK_EN_SEL                           COM_OFF(0x38)
+#define QSERDES_COM_RES_CODE_TXBAND                         COM_OFF(0x3C)
+#define QSERDES_COM_RESETSM_CNTRL                           COM_OFF(0x40)
+#define QSERDES_COM_PLLLOCK_CMP1                            COM_OFF(0x44)
+#define QSERDES_COM_PLLLOCK_CMP2                            COM_OFF(0x48)
+#define QSERDES_COM_PLLLOCK_CMP3                            COM_OFF(0x4C)
+#define QSERDES_COM_PLLLOCK_CMP_EN                          COM_OFF(0x50)
+#define QSERDES_COM_RES_TRIM_OFFSET                         COM_OFF(0x54)
+#define QSERDES_COM_BGTC                                    COM_OFF(0x58)
+#define QSERDES_COM_PLL_TEST_UPDN_RESTRIMSTEP               COM_OFF(0x5C)
+#define QSERDES_COM_PLL_VCO_TUNE                            COM_OFF(0x60)
+#define QSERDES_COM_DEC_START1                              COM_OFF(0x64)
+#define QSERDES_COM_PLL_AMP_OS                              COM_OFF(0x68)
+#define QSERDES_COM_SSC_EN_CENTER                           COM_OFF(0x6C)
+#define QSERDES_COM_SSC_ADJ_PER1                            COM_OFF(0x70)
+#define QSERDES_COM_SSC_ADJ_PER2                            COM_OFF(0x74)
+#define QSERDES_COM_SSC_PER1                                COM_OFF(0x78)
+#define QSERDES_COM_SSC_PER2                                COM_OFF(0x7C)
+#define QSERDES_COM_SSC_STEP_SIZE1                          COM_OFF(0x80)
+#define QSERDES_COM_SSC_STEP_SIZE2                          COM_OFF(0x84)
+#define QSERDES_COM_RES_TRIM_SEARCH                         COM_OFF(0x88)
+#define QSERDES_COM_RES_TRIM_FREEZE                         COM_OFF(0x8C)
+#define QSERDES_COM_RES_TRIM_EN_VCOCALDONE                  COM_OFF(0x90)
+#define QSERDES_COM_FAUX_EN                                 COM_OFF(0x94)
+#define QSERDES_COM_DIV_FRAC_START1                         COM_OFF(0x98)
+#define QSERDES_COM_DIV_FRAC_START2                         COM_OFF(0x9C)
+#define QSERDES_COM_DIV_FRAC_START3                         COM_OFF(0xA0)
+#define QSERDES_COM_DEC_START2                              COM_OFF(0xA4)
+#define QSERDES_COM_PLL_RXTXEPCLK_EN                        COM_OFF(0xA8)
+#define QSERDES_COM_PLL_CRCTRL                              COM_OFF(0xAC)
+#define QSERDES_COM_PLL_CLKEPDIV                            COM_OFF(0xB0)
+#define QSERDES_COM_PLL_FREQUPDATE                          COM_OFF(0xB4)
+#define QSERDES_COM_PLL_VCO_HIGH                            COM_OFF(0xB8)
+#define QSERDES_COM_RESET_SM                                COM_OFF(0xBC)
+
+/* UFS PHY registers */
+#define UFS_PHY_PHY_START                                   PHY_OFF(0x00)
+#define UFS_PHY_POWER_DOWN_CONTROL                          PHY_OFF(0x04)
+#define UFS_PHY_PWM_G1_CLK_DIVIDER                          PHY_OFF(0x08)
+#define UFS_PHY_PWM_G2_CLK_DIVIDER                          PHY_OFF(0x0C)
+#define UFS_PHY_PWM_G3_CLK_DIVIDER                          PHY_OFF(0x10)
+#define UFS_PHY_PWM_G4_CLK_DIVIDER                          PHY_OFF(0x14)
+#define UFS_PHY_TIMER_100US_SYSCLK_STEPS_MSB                PHY_OFF(0x18)
+#define UFS_PHY_TIMER_100US_SYSCLK_STEPS_LSB                PHY_OFF(0x1C)
+#define UFS_PHY_TIMER_20US_CORECLK_STEPS_MSB                PHY_OFF(0x20)
+#define UFS_PHY_TIMER_20US_CORECLK_STEPS_LSB                PHY_OFF(0x24)
+#define UFS_PHY_LINE_RESET_TIME                             PHY_OFF(0x28)
+#define UFS_PHY_LINE_RESET_GRANULARITY                      PHY_OFF(0x2C)
+#define UFS_PHY_CONTROLSYM_ONE_HOT_DISABLE                  PHY_OFF(0x30)
+#define UFS_PHY_CORECLK_PWM_G1_CLK_DIVIDER                  PHY_OFF(0x34)
+#define UFS_PHY_CORECLK_PWM_G2_CLK_DIVIDER                  PHY_OFF(0x38)
+#define UFS_PHY_CORECLK_PWM_G3_CLK_DIVIDER                  PHY_OFF(0x3C)
+#define UFS_PHY_CORECLK_PWM_G4_CLK_DIVIDER                  PHY_OFF(0x40)
+#define UFS_PHY_TX_LANE_ENABLE                              PHY_OFF(0x44)
+#define UFS_PHY_TSYNC_RSYNC_CNTL                            PHY_OFF(0x48)
+#define UFS_PHY_RETIME_BUFFER_EN                            PHY_OFF(0x4C)
+#define UFS_PHY_PLL_CNTL                                    PHY_OFF(0x50)
+#define UFS_PHY_TX_LARGE_AMP_DRV_LVL                        PHY_OFF(0x54)
+#define UFS_PHY_TX_LARGE_AMP_POST_EMP_LVL                   PHY_OFF(0x58)
+#define UFS_PHY_TX_SMALL_AMP_DRV_LVL                        PHY_OFF(0x5C)
+#define UFS_PHY_TX_SMALL_AMP_POST_EMP_LVL                   PHY_OFF(0x60)
+#define UFS_PHY_CFG_CHANGE_CNT_VAL                          PHY_OFF(0x64)
+#define UFS_PHY_OMC_STATUS_RDVAL                            PHY_OFF(0x68)
+#define UFS_PHY_RX_SYNC_WAIT_TIME                           PHY_OFF(0x6C)
+#define UFS_PHY_L0_BIST_CTRL                                PHY_OFF(0x70)
+#define UFS_PHY_L1_BIST_CTRL                                PHY_OFF(0x74)
+#define UFS_PHY_BIST_PRBS_POLY0                             PHY_OFF(0x78)
+#define UFS_PHY_BIST_PRBS_POLY1                             PHY_OFF(0x7C)
+#define UFS_PHY_BIST_PRBS_SEED0                             PHY_OFF(0x80)
+#define UFS_PHY_BIST_PRBS_SEED1                             PHY_OFF(0x84)
+#define UFS_PHY_BIST_FIXED_PAT_CTRL                         PHY_OFF(0x88)
+#define UFS_PHY_BIST_FIXED_PAT0_DATA                        PHY_OFF(0x8C)
+#define UFS_PHY_BIST_FIXED_PAT1_DATA                        PHY_OFF(0x90)
+#define UFS_PHY_BIST_FIXED_PAT2_DATA                        PHY_OFF(0x94)
+#define UFS_PHY_BIST_FIXED_PAT3_DATA                        PHY_OFF(0x98)
+#define UFS_PHY_TX_HSGEAR_CAPABILITY                        PHY_OFF(0x9C)
+#define UFS_PHY_TX_PWMGEAR_CAPABILITY                       PHY_OFF(0xA0)
+#define UFS_PHY_TX_AMPLITUDE_CAPABILITY                     PHY_OFF(0xA4)
+#define UFS_PHY_TX_EXTERNALSYNC_CAPABILITY                  PHY_OFF(0xA8)
+#define UFS_PHY_TX_HS_UNTERMINATED_LINE_DRIVE_CAPABILITY    PHY_OFF(0xAC)
+#define UFS_PHY_TX_LS_TERMINATED_LINE_DRIVE_CAPABILITY      PHY_OFF(0xB0)
+#define UFS_PHY_TX_MIN_SLEEP_NOCONFIG_TIME_CAPABILITY       PHY_OFF(0xB4)
+#define UFS_PHY_TX_MIN_STALL_NOCONFIG_TIME_CAPABILITY       PHY_OFF(0xB8)
+#define UFS_PHY_TX_MIN_SAVE_CONFIG_TIME_CAPABILITY          PHY_OFF(0xBC)
+#define UFS_PHY_TX_REF_CLOCK_SHARED_CAPABILITY              PHY_OFF(0xC0)
+#define UFS_PHY_TX_PHY_MAJORMINOR_RELEASE_CAPABILITY        PHY_OFF(0xC4)
+#define UFS_PHY_TX_PHY_EDITORIAL_RELEASE_CAPABILITY         PHY_OFF(0xC8)
+#define UFS_PHY_TX_HIBERN8TIME_CAPABILITY                   PHY_OFF(0xCC)
+#define UFS_PHY_RX_HSGEAR_CAPABILITY                        PHY_OFF(0xD0)
+#define UFS_PHY_RX_PWMGEAR_CAPABILITY                       PHY_OFF(0xD4)
+#define UFS_PHY_RX_HS_UNTERMINATED_CAPABILITY               PHY_OFF(0xD8)
+#define UFS_PHY_RX_LS_TERMINATED_CAPABILITY                 PHY_OFF(0xDC)
+#define UFS_PHY_RX_MIN_SLEEP_NOCONFIG_TIME_CAPABILITY       PHY_OFF(0xE0)
+#define UFS_PHY_RX_MIN_STALL_NOCONFIG_TIME_CAPABILITY       PHY_OFF(0xE4)
+#define UFS_PHY_RX_MIN_SAVE_CONFIG_TIME_CAPABILITY          PHY_OFF(0xE8)
+#define UFS_PHY_RX_REF_CLOCK_SHARED_CAPABILITY              PHY_OFF(0xEC)
+#define UFS_PHY_RX_HS_G1_SYNC_LENGTH_CAPABILITY             PHY_OFF(0xF0)
+#define UFS_PHY_RX_HS_G1_PREPARE_LENGTH_CAPABILITY          PHY_OFF(0xF4)
+#define UFS_PHY_RX_LS_PREPARE_LENGTH_CAPABILITY             PHY_OFF(0xF8)
+#define UFS_PHY_RX_PWM_BURST_CLOSURE_LENGTH_CAPABILITY      PHY_OFF(0xFC)
+#define UFS_PHY_RX_MIN_ACTIVATETIME_CAPABILITY              PHY_OFF(0x100)
+#define UFS_PHY_RX_PHY_MAJORMINOR_RELEASE_CAPABILITY        PHY_OFF(0x104)
+#define UFS_PHY_RX_PHY_EDITORIAL_RELEASE_CAPABILITY         PHY_OFF(0x108)
+#define UFS_PHY_RX_HIBERN8TIME_CAPABILITY                   PHY_OFF(0x10C)
+#define UFS_PHY_RX_HS_G2_SYNC_LENGTH_CAPABILITY             PHY_OFF(0x110)
+#define UFS_PHY_RX_HS_G3_SYNC_LENGTH_CAPABILITY             PHY_OFF(0x114)
+#define UFS_PHY_RX_HS_G2_PREPARE_LENGTH_CAPABILITY          PHY_OFF(0x118)
+#define UFS_PHY_RX_HS_G3_PREPARE_LENGTH_CAPABILITY          PHY_OFF(0x11C)
+#define UFS_PHY_DEBUG_BUS_SEL                               PHY_OFF(0x120)
+#define UFS_PHY_DEBUG_BUS_0_STATUS_CHK                      PHY_OFF(0x124)
+#define UFS_PHY_DEBUG_BUS_1_STATUS_CHK                      PHY_OFF(0x128)
+#define UFS_PHY_DEBUG_BUS_2_STATUS_CHK                      PHY_OFF(0x12C)
+#define UFS_PHY_DEBUG_BUS_3_STATUS_CHK                      PHY_OFF(0x130)
+#define UFS_PHY_PCS_READY_STATUS                            PHY_OFF(0x134)
+#define UFS_PHY_L0_BIST_CHK_ERR_CNT_L_STATUS                PHY_OFF(0x138)
+#define UFS_PHY_L0_BIST_CHK_ERR_CNT_H_STATUS                PHY_OFF(0x13C)
+#define UFS_PHY_L1_BIST_CHK_ERR_CNT_L_STATUS                PHY_OFF(0x140)
+#define UFS_PHY_L1_BIST_CHK_ERR_CNT_H_STATUS                PHY_OFF(0x144)
+#define UFS_PHY_L0_BIST_CHK_STATUS                          PHY_OFF(0x148)
+#define UFS_PHY_L1_BIST_CHK_STATUS                          PHY_OFF(0x14C)
+#define UFS_PHY_DEBUG_BUS_0_STATUS                          PHY_OFF(0x150)
+#define UFS_PHY_DEBUG_BUS_1_STATUS                          PHY_OFF(0x154)
+#define UFS_PHY_DEBUG_BUS_2_STATUS                          PHY_OFF(0x158)
+#define UFS_PHY_DEBUG_BUS_3_STATUS                          PHY_OFF(0x15C)
+#define UFS_PHY_RMMI_ATTR_CTRL                              PHY_OFF(0x16C)
+#define UFS_PHY_RMMI_RX_CFGUPDT_L1     (1 << 7)
+#define UFS_PHY_RMMI_TX_CFGUPDT_L1     (1 << 6)
+#define UFS_PHY_RMMI_CFGWR_L1          (1 << 5)
+#define UFS_PHY_RMMI_CFGRD_L1          (1 << 4)
+#define UFS_PHY_RMMI_RX_CFGUPDT_L0     (1 << 3)
+#define UFS_PHY_RMMI_TX_CFGUPDT_L0     (1 << 2)
+#define UFS_PHY_RMMI_CFGWR_L0          (1 << 1)
+#define UFS_PHY_RMMI_CFGRD_L0          (1 << 0)
+#define UFS_PHY_RMMI_ATTRID                                PHY_OFF(0x170)
+#define UFS_PHY_RMMI_ATTRWRVAL                             PHY_OFF(0x174)
+#define UFS_PHY_RMMI_ATTRRDVAL_L0_STATUS                   PHY_OFF(0x178)
+#define UFS_PHY_RMMI_ATTRRDVAL_L1_STATUS                   PHY_OFF(0x17C)
+
+/* TX LANE n (0, 1) registers */
+#define QSERDES_TX_BIST_MODE_LANENO(n)                      TX_OFF(n, 0x00)
+#define QSERDES_TX_CLKBUF_ENABLE(n)                         TX_OFF(n, 0x04)
+#define QSERDES_TX_TX_EMP_POST1_LVL(n)                      TX_OFF(n, 0x08)
+#define QSERDES_TX_TX_DRV_LVL(n)                            TX_OFF(n, 0x0C)
+#define QSERDES_TX_RESET_TSYNC_EN(n)                        TX_OFF(n, 0x10)
+#define QSERDES_TX_LPB_EN(n)                                TX_OFF(n, 0x14)
+#define QSERDES_TX_RES_CODE(n)                              TX_OFF(n, 0x18)
+#define QSERDES_TX_PERL_LENGTH1(n)                          TX_OFF(n, 0x1C)
+#define QSERDES_TX_PERL_LENGTH2(n)                          TX_OFF(n, 0x20)
+#define QSERDES_TX_SERDES_BYP_EN_OUT(n)                     TX_OFF(n, 0x24)
+#define QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_EN(n)           TX_OFF(n, 0x28)
+#define QSERDES_TX_PARRATE_REC_DETECT_IDLE_EN(n)            TX_OFF(n, 0x2C)
+#define QSERDES_TX_BIST_PATTERN1(n)                         TX_OFF(n, 0x30)
+#define QSERDES_TX_BIST_PATTERN2(n)                         TX_OFF(n, 0x34)
+#define QSERDES_TX_BIST_PATTERN3(n)                         TX_OFF(n, 0x38)
+#define QSERDES_TX_BIST_PATTERN4(n)                         TX_OFF(n, 0x3C)
+#define QSERDES_TX_BIST_PATTERN5(n)                         TX_OFF(n, 0x40)
+#define QSERDES_TX_BIST_PATTERN6(n)                         TX_OFF(n, 0x44)
+#define QSERDES_TX_BIST_PATTERN7(n)                         TX_OFF(n, 0x48)
+#define QSERDES_TX_BIST_PATTERN8(n)                         TX_OFF(n, 0x4C)
+#define QSERDES_TX_LANE_MODE(n)                             TX_OFF(n, 0x50)
+#define QSERDES_TX_ATB_SEL(n)                               TX_OFF(n, 0x54)
+#define QSERDES_TX_REC_DETECT_LVL(n)                        TX_OFF(n, 0x58)
+#define QSERDES_TX_PRBS_SEED1(n)                            TX_OFF(n, 0x5C)
+#define QSERDES_TX_PRBS_SEED2(n)                            TX_OFF(n, 0x60)
+#define QSERDES_TX_PRBS_SEED3(n)                            TX_OFF(n, 0x64)
+#define QSERDES_TX_PRBS_SEED4(n)                            TX_OFF(n, 0x68)
+#define QSERDES_TX_RESET_GEN(n)                             TX_OFF(n, 0x6C)
+#define QSERDES_TX_TRAN_DRVR_EMP_EN(n)                      TX_OFF(n, 0x70)
+#define QSERDES_TX_TX_INTERFACE_MODE(n)                     TX_OFF(n, 0x74)
+#define QSERDES_TX_BIST_STATUS(n)                           TX_OFF(n, 0x78)
+#define QSERDES_TX_BIST_ERROR_COUNT1(n)                     TX_OFF(n, 0x7C)
+#define QSERDES_TX_BIST_ERROR_COUNT2(n)                     TX_OFF(n, 0x80)
+
+/* RX LANE n (0, 1) registers */
+#define QSERDES_RX_CDR_CONTROL(n)                           RX_OFF(n, 0x00)
+#define QSERDES_RX_AUX_CONTROL(n)                           RX_OFF(n, 0x04)
+#define QSERDES_RX_AUX_DATA_TCODE(n)                        RX_OFF(n, 0x08)
+#define QSERDES_RX_RCLK_AUXDATA_SEL(n)                      RX_OFF(n, 0x0C)
+#define QSERDES_RX_EQ_CONTROL(n)                            RX_OFF(n, 0x10)
+#define QSERDES_RX_RX_EQ_GAIN2(n)                           RX_OFF(n, 0x14)
+#define QSERDES_RX_AC_JTAG_INIT(n)                          RX_OFF(n, 0x18)
+#define QSERDES_RX_AC_JTAG_LVL_EN(n)                        RX_OFF(n, 0x1C)
+#define QSERDES_RX_AC_JTAG_MODE(n)                          RX_OFF(n, 0x20)
+#define QSERDES_RX_AC_JTAG_RESET(n)                         RX_OFF(n, 0x24)
+#define QSERDES_RX_RX_IQ_RXDET_EN(n)                        RX_OFF(n, 0x28)
+#define QSERDES_RX_RX_TERM_HIGHZ_CM_AC_COUPLE(n)            RX_OFF(n, 0x2C)
+#define QSERDES_RX_RX_EQ_GAIN1(n)                           RX_OFF(n, 0x30)
+#define QSERDES_RX_SIGDET_CNTRL(n)                          RX_OFF(n, 0x34)
+#define QSERDES_RX_RX_BAND(n)                               RX_OFF(n, 0x38)
+#define QSERDES_RX_CDR_FREEZE_UP_DN(n)                      RX_OFF(n, 0x3C)
+#define QSERDES_RX_RX_INTERFACE_MODE(n)                     RX_OFF(n, 0x40)
+#define QSERDES_RX_JITTER_GEN_MODE(n)                       RX_OFF(n, 0x44)
+#define QSERDES_RX_BUJ_AMP(n)                               RX_OFF(n, 0x48)
+#define QSERDES_RX_SJ_AMP1(n)                               RX_OFF(n, 0x4C)
+#define QSERDES_RX_SJ_AMP2(n)                               RX_OFF(n, 0x50)
+#define QSERDES_RX_SJ_PER1(n)                               RX_OFF(n, 0x54)
+#define QSERDES_RX_SJ_PER2(n)                               RX_OFF(n, 0x58)
+#define QSERDES_RX_BUJ_STEP_FREQ1(n)                        RX_OFF(n, 0x5C)
+#define QSERDES_RX_BUJ_STEP_FREQ2(n)                        RX_OFF(n, 0x60)
+#define QSERDES_RX_PPM_OFFSET1(n)                           RX_OFF(n, 0x64)
+#define QSERDES_RX_PPM_OFFSET2(n)                           RX_OFF(n, 0x68)
+#define QSERDES_RX_SIGN_PPM_PERIOD1(n)                      RX_OFF(n, 0x6C)
+#define QSERDES_RX_SIGN_PPM_PERIOD2(n)                      RX_OFF(n, 0x70)
+#define QSERDES_RX_SSC_CTRL(n)                              RX_OFF(n, 0x74)
+#define QSERDES_RX_SSC_COUNT1(n)                            RX_OFF(n, 0x78)
+#define QSERDES_RX_SSC_COUNT2(n)                            RX_OFF(n, 0x7C)
+#define QSERDES_RX_PWM_CNTRL1(n)                            RX_OFF(n, 0x80)
+#define QSERDES_RX_PWM_CNTRL2(n)                            RX_OFF(n, 0x84)
+#define QSERDES_RX_PWM_NDIV(n)                              RX_OFF(n, 0x88)
+#define QSERDES_RX_SIGDET_CNTRL2(n)                         RX_OFF(n, 0x8C)
+#define QSERDES_RX_UFS_CNTRL(n)                             RX_OFF(n, 0x90)
+#define QSERDES_RX_CDR_CONTROL3(n)                          RX_OFF(n, 0x94)
+#define QSERDES_RX_CDR_CONTROL_HALF(n)                      RX_OFF(n, 0x98)
+#define QSERDES_RX_CDR_CONTROL_QUARTER(n)                   RX_OFF(n, 0x9C)
+#define QSERDES_RX_CDR_CONTROL_EIGHTH(n)                    RX_OFF(n, 0xA0)
+#define QSERDES_RX_UCDR_FO_GAIN(n)                          RX_OFF(n, 0xA4)
+#define QSERDES_RX_UCDR_SO_GAIN(n)                          RX_OFF(n, 0xA8)
+#define QSERDES_RX_UCDR_SO_SATURATION_AND_ENABLE(n)         RX_OFF(n, 0xAC)
+#define QSERDES_RX_UCDR_FO_TO_SO_DELAY(n)                   RX_OFF(n, 0xB0)
+#define QSERDES_RX_PI_CTRL1(n)                              RX_OFF(n, 0xB4)
+#define QSERDES_RX_PI_CTRL2(n)                              RX_OFF(n, 0xB8)
+#define QSERDES_RX_PI_QUAD(n)                               RX_OFF(n, 0xBC)
+#define QSERDES_RX_IDATA1(n)                                RX_OFF(n, 0xC0)
+#define QSERDES_RX_IDATA2(n)                                RX_OFF(n, 0xC4)
+#define QSERDES_RX_AUX_DATA1(n)                             RX_OFF(n, 0xC8)
+#define QSERDES_RX_AUX_DATA2(n)                             RX_OFF(n, 0xCC)
+#define QSERDES_RX_AC_JTAG_OUTP(n)                          RX_OFF(n, 0xD0)
+#define QSERDES_RX_AC_JTAG_OUTN(n)                          RX_OFF(n, 0xD4)
+#define QSERDES_RX_RX_SIGDET_PWMDECSTATUS(n)                RX_OFF(n, 0xD8)
+
+#define UFS_PHY_TX_LANE_ENABLE_MASK            0x3
+
+/*
+ * This structure represents the 28nm specific phy.
+ * common_cfg MUST remain the first field in this structure
+ * in case extra fields are added. This way, when calling
+ * get_ufs_msm_phy() of generic phy, we can extract the
+ * common phy structure (struct ufs_msm_phy) out of it
+ * regardless of the relevant specific phy.
+ */
+struct ufs_msm_phy_qmp_28nm {
+       struct ufs_msm_phy common_cfg;
+};
+
+static struct ufs_msm_phy_calibration phy_cal_table_ctrl_1_1_0_rate_A[] = {
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_POWER_DOWN_CONTROL, 0x01),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CRCTRL, 0xFF),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CNTRL, 0x24),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_SYSCLK_EN_SEL, 0x08),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_SYS_CLK_CTRL, 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CLKEPDIV, 0x03),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START1, 0x82),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START2, 0x03),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START1, 0x80),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START2, 0x80),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START3, 0x10),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP1, 0xff),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP2, 0x67),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP3, 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP_EN, 0x01),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_RESETSM_CNTRL, 0x10),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_PLL_RXTXEPCLK_EN, 0x13),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_PWM_CNTRL1(0), 0x43),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_PWM_CNTRL1(1), 0x43),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL(0), 0x22),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_HALF(0), 0x12),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_QUARTER(0), 0x2a),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL(1), 0x22),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_HALF(1), 0x12),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_QUARTER(1), 0x2a),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_SIGDET_CNTRL(0), 0xC0),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_SIGDET_CNTRL(1), 0xC0),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_SIGDET_CNTRL2(0), 0x07),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_SIGDET_CNTRL2(1), 0x07),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_PWM_G1_CLK_DIVIDER, 0x50),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_PWM_G2_CLK_DIVIDER, 0x28),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_PWM_G3_CLK_DIVIDER, 0x10),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_PWM_G4_CLK_DIVIDER, 0x08),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_CORECLK_PWM_G1_CLK_DIVIDER, 0xa8),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_CORECLK_PWM_G2_CLK_DIVIDER, 0x54),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_CORECLK_PWM_G3_CLK_DIVIDER, 0x2a),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_CORECLK_PWM_G4_CLK_DIVIDER, 0x15),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_OMC_STATUS_RDVAL, 0xff),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_LINE_RESET_TIME, 0x1f),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_LINE_RESET_GRANULARITY, 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_TSYNC_RSYNC_CNTL, 0x03),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_PLL_CNTL, 0x01),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_TX_LARGE_AMP_DRV_LVL, 0x0f),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_TX_SMALL_AMP_DRV_LVL, 0x1a),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_TX_LARGE_AMP_POST_EMP_LVL, 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_TX_SMALL_AMP_POST_EMP_LVL, 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_CFG_CHANGE_CNT_VAL, 0x09),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_RX_SYNC_WAIT_TIME, 0x30),
+       UFS_MSM_PHY_CAL_ENTRY(
+               UFS_PHY_TX_MIN_SLEEP_NOCONFIG_TIME_CAPABILITY, 0x01),
+       UFS_MSM_PHY_CAL_ENTRY(
+               UFS_PHY_RX_MIN_SLEEP_NOCONFIG_TIME_CAPABILITY, 0x08),
+       UFS_MSM_PHY_CAL_ENTRY(
+               UFS_PHY_TX_MIN_STALL_NOCONFIG_TIME_CAPABILITY, 0x01),
+       UFS_MSM_PHY_CAL_ENTRY(
+               UFS_PHY_RX_MIN_STALL_NOCONFIG_TIME_CAPABILITY, 0x0f),
+       UFS_MSM_PHY_CAL_ENTRY(
+               UFS_PHY_TX_MIN_SAVE_CONFIG_TIME_CAPABILITY, 0x04),
+       UFS_MSM_PHY_CAL_ENTRY(
+               UFS_PHY_RX_MIN_SAVE_CONFIG_TIME_CAPABILITY, 0xc8),
+       UFS_MSM_PHY_CAL_ENTRY(
+               UFS_PHY_RX_PWM_BURST_CLOSURE_LENGTH_CAPABILITY, 0x10),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_RX_MIN_ACTIVATETIME_CAPABILITY, 0x01),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1(0), 0x07),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2(0), 0x07),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1(1), 0x07),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2(1), 0x07),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL3(0), 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL3(1), 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_PLL_IP_SETI, 0x01),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CP_SETI, 0x3f),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_PLL_IP_SETP, 0x01),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CP_SETP, 0x01),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_RES_TRIM_OFFSET, 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_BGTC, 0x0f),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_PLL_AMP_OS, 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_TX_TX_DRV_LVL(0), 0x0f),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_TX_TX_DRV_LVL(1), 0x0f),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_TX_BIST_MODE_LANENO(0), 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_TX_BIST_MODE_LANENO(1), 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_TX_TX_EMP_POST1_LVL(0), 0x04),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_TX_TX_EMP_POST1_LVL(1), 0x04),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_EN(0),
0x05),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_EN(1),
0x05),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_TIMER_100US_SYSCLK_STEPS_MSB, 0x07),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_TIMER_100US_SYSCLK_STEPS_LSB, 0x80),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_TIMER_20US_CORECLK_STEPS_MSB, 0x27),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_TIMER_20US_CORECLK_STEPS_LSB, 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_CONTROLSYM_ONE_HOT_DISABLE, 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_RETIME_BUFFER_EN, 0x01),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_TX_HSGEAR_CAPABILITY, 0x03),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_TX_PWMGEAR_CAPABILITY, 0x04),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_TX_AMPLITUDE_CAPABILITY, 0x03),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_TX_EXTERNALSYNC_CAPABILITY, 0x01),
+       UFS_MSM_PHY_CAL_ENTRY(
+               UFS_PHY_TX_HS_UNTERMINATED_LINE_DRIVE_CAPABILITY, 0x01),
+       UFS_MSM_PHY_CAL_ENTRY(
+               UFS_PHY_TX_LS_TERMINATED_LINE_DRIVE_CAPABILITY, 0x01),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_TX_REF_CLOCK_SHARED_CAPABILITY, 0x01),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_TX_HIBERN8TIME_CAPABILITY, 0x01),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_RX_HSGEAR_CAPABILITY, 0x03),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_RX_PWMGEAR_CAPABILITY, 0x04),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_RX_HS_UNTERMINATED_CAPABILITY, 0x01),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_RX_LS_TERMINATED_CAPABILITY, 0x01),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_RX_REF_CLOCK_SHARED_CAPABILITY, 0x01),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_RX_HS_G1_SYNC_LENGTH_CAPABILITY,
0x48),
+       UFS_MSM_PHY_CAL_ENTRY(
+               UFS_PHY_RX_HS_G1_PREPARE_LENGTH_CAPABILITY, 0x0f),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_RX_LS_PREPARE_LENGTH_CAPABILITY,
0x09),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_RX_HIBERN8TIME_CAPABILITY, 0x01),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_RX_HS_G2_SYNC_LENGTH_CAPABILITY,
0x48),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_RX_HS_G3_SYNC_LENGTH_CAPABILITY,
0x48),
+       UFS_MSM_PHY_CAL_ENTRY(
+               UFS_PHY_RX_HS_G2_PREPARE_LENGTH_CAPABILITY, 0x0f),
+       UFS_MSM_PHY_CAL_ENTRY(
+               UFS_PHY_RX_HS_G3_PREPARE_LENGTH_CAPABILITY, 0x0f),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_TX_CLKBUF_ENABLE(0), 0x09),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_TX_RESET_TSYNC_EN(0), 0x01),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_TX_RES_CODE(0), 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_TX_SERDES_BYP_EN_OUT(0), 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_TX_REC_DETECT_LVL(0), 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_TX_PARRATE_REC_DETECT_IDLE_EN(0),
0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_TX_TRAN_DRVR_EMP_EN(0), 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_AUX_CONTROL(0), 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_AUX_DATA_TCODE(0), 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_RCLK_AUXDATA_SEL(0), 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_EQ_CONTROL(0), 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_RX_IQ_RXDET_EN(0), 0x73),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_RX_TERM_HIGHZ_CM_AC_COUPLE(0),
0x05),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_CDR_FREEZE_UP_DN(0), 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_UFS_CNTRL(0), 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_EIGHTH(0), 0x22),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_UCDR_FO_GAIN(0), 0x0a),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_UCDR_SO_GAIN(0), 0x06),
+       UFS_MSM_PHY_CAL_ENTRY(
+               QSERDES_RX_UCDR_SO_SATURATION_AND_ENABLE(0), 0x35),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_UCDR_FO_TO_SO_DELAY(0), 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_TX_CLKBUF_ENABLE(1), 0x09),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_TX_RESET_TSYNC_EN(1), 0x01),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_TX_RES_CODE(1), 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_TX_SERDES_BYP_EN_OUT(1), 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_TX_REC_DETECT_LVL(1), 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_TX_PARRATE_REC_DETECT_IDLE_EN(1),
0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_TX_TRAN_DRVR_EMP_EN(1), 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_AUX_CONTROL(1), 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_AUX_DATA_TCODE(1), 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_RCLK_AUXDATA_SEL(1), 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_EQ_CONTROL(1), 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_RX_IQ_RXDET_EN(1), 0x73),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_RX_TERM_HIGHZ_CM_AC_COUPLE(1),
0x05),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_CDR_FREEZE_UP_DN(1), 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_UFS_CNTRL(1), 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_EIGHTH(1), 0x22),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_UCDR_FO_GAIN(1), 0x0a),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_UCDR_SO_GAIN(1), 0x06),
+       UFS_MSM_PHY_CAL_ENTRY(
+               QSERDES_RX_UCDR_SO_SATURATION_AND_ENABLE(1), 0x35),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_UCDR_FO_TO_SO_DELAY(1), 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_CMN_MODE, 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_IE_TRIM, 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_IP_TRIM, 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_CORE_CLK_IN_SYNC_SEL, 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_PLL_TEST_UPDN_RESTRIMSTEP, 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_FAUX_EN, 0x00),
+};
+
+static struct ufs_msm_phy_calibration phy_cal_table_ctrl_1_1_1_rate_A[] = {
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_POWER_DOWN_CONTROL, 0x01),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CRCTRL, 0x43),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CNTRL, 0x24),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_SYSCLK_EN_SEL, 0x08),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_SYS_CLK_CTRL, 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CLKEPDIV, 0x03),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START1, 0x82),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START2, 0x03),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START1, 0x80),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START2, 0x80),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START3, 0x10),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP1, 0xff),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP2, 0x19),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP3, 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP_EN, 0x03),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_RESETSM_CNTRL, 0x10),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_PLL_RXTXEPCLK_EN, 0x10),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_PWM_CNTRL1(0), 0x43),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_PWM_CNTRL1(1), 0x43),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL(0), 0x40),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_HALF(0), 0x0c),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_QUARTER(0), 0x12),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL(1), 0x40),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_HALF(1), 0x0c),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_QUARTER(1), 0x12),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_SIGDET_CNTRL(0), 0xC0),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_SIGDET_CNTRL(1), 0xC0),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_SIGDET_CNTRL2(0), 0x07),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_SIGDET_CNTRL2(1), 0x07),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_PWM_G1_CLK_DIVIDER, 0x30),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_PWM_G2_CLK_DIVIDER, 0x18),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_PWM_G3_CLK_DIVIDER, 0x0c),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_PWM_G4_CLK_DIVIDER, 0x06),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_CORECLK_PWM_G1_CLK_DIVIDER, 0xa8),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_CORECLK_PWM_G2_CLK_DIVIDER, 0x54),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_CORECLK_PWM_G3_CLK_DIVIDER, 0x2a),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_CORECLK_PWM_G4_CLK_DIVIDER, 0x15),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_OMC_STATUS_RDVAL, 0xff),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_LINE_RESET_TIME, 0x1f),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_LINE_RESET_GRANULARITY, 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_TSYNC_RSYNC_CNTL, 0x03),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_PLL_CNTL, 0x01),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_TX_LARGE_AMP_DRV_LVL, 0x0f),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_TX_SMALL_AMP_DRV_LVL, 0x1a),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_TX_LARGE_AMP_POST_EMP_LVL, 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_TX_SMALL_AMP_POST_EMP_LVL, 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_CFG_CHANGE_CNT_VAL, 0x09),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_RX_SYNC_WAIT_TIME, 0x30),
+       UFS_MSM_PHY_CAL_ENTRY(
+               UFS_PHY_TX_MIN_SLEEP_NOCONFIG_TIME_CAPABILITY, 0x01),
+       UFS_MSM_PHY_CAL_ENTRY(
+               UFS_PHY_RX_MIN_SLEEP_NOCONFIG_TIME_CAPABILITY, 0x08),
+       UFS_MSM_PHY_CAL_ENTRY(
+               UFS_PHY_TX_MIN_STALL_NOCONFIG_TIME_CAPABILITY, 0x01),
+       UFS_MSM_PHY_CAL_ENTRY(
+               UFS_PHY_RX_MIN_STALL_NOCONFIG_TIME_CAPABILITY, 0x0f),
+       UFS_MSM_PHY_CAL_ENTRY(
+               UFS_PHY_TX_MIN_SAVE_CONFIG_TIME_CAPABILITY, 0x04),
+       UFS_MSM_PHY_CAL_ENTRY(
+               UFS_PHY_RX_MIN_SAVE_CONFIG_TIME_CAPABILITY, 0xc8),
+       UFS_MSM_PHY_CAL_ENTRY(
+               UFS_PHY_RX_PWM_BURST_CLOSURE_LENGTH_CAPABILITY, 0x10),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_RX_MIN_ACTIVATETIME_CAPABILITY, 0x01),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1(0), 0x1f),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2(0), 0x17),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1(1), 0x1f),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2(1), 0x17),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL3(0), 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL3(1), 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_PLL_IP_SETI, 0x07),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CP_SETI, 0x0f),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_PLL_IP_SETP, 0x07),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CP_SETP, 0x01),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_RES_TRIM_OFFSET, 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_BGTC, 0x0f),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_PLL_AMP_OS, 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_TX_TX_DRV_LVL(0), 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_TX_TX_DRV_LVL(1), 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_TX_BIST_MODE_LANENO(0), 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_TX_BIST_MODE_LANENO(1), 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_TX_TX_EMP_POST1_LVL(0), 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_TX_TX_EMP_POST1_LVL(1), 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_EN(0),
0x05),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_EN(1),
0x05),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_TIMER_100US_SYSCLK_STEPS_MSB, 0x07),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_TIMER_100US_SYSCLK_STEPS_LSB, 0x80),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_TIMER_20US_CORECLK_STEPS_MSB, 0x27),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_TIMER_20US_CORECLK_STEPS_LSB, 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_CONTROLSYM_ONE_HOT_DISABLE, 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_RETIME_BUFFER_EN, 0x01),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_TX_HSGEAR_CAPABILITY, 0x03),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_TX_PWMGEAR_CAPABILITY, 0x04),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_TX_AMPLITUDE_CAPABILITY, 0x03),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_TX_EXTERNALSYNC_CAPABILITY, 0x01),
+       UFS_MSM_PHY_CAL_ENTRY(
+               UFS_PHY_TX_HS_UNTERMINATED_LINE_DRIVE_CAPABILITY, 0x01),
+       UFS_MSM_PHY_CAL_ENTRY(
+               UFS_PHY_TX_LS_TERMINATED_LINE_DRIVE_CAPABILITY, 0x01),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_TX_REF_CLOCK_SHARED_CAPABILITY, 0x01),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_TX_HIBERN8TIME_CAPABILITY, 0x01),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_RX_HSGEAR_CAPABILITY, 0x03),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_RX_PWMGEAR_CAPABILITY, 0x04),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_RX_HS_UNTERMINATED_CAPABILITY, 0x01),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_RX_LS_TERMINATED_CAPABILITY, 0x01),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_RX_REF_CLOCK_SHARED_CAPABILITY, 0x01),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_RX_HS_G1_SYNC_LENGTH_CAPABILITY,
0x48),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_RX_HS_G1_PREPARE_LENGTH_CAPABILITY,
0x0f),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_RX_LS_PREPARE_LENGTH_CAPABILITY,
0x0a),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_RX_HIBERN8TIME_CAPABILITY, 0x01),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_RX_HS_G2_SYNC_LENGTH_CAPABILITY,
0x48),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_RX_HS_G3_SYNC_LENGTH_CAPABILITY,
0x48),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_RX_HS_G2_PREPARE_LENGTH_CAPABILITY,
0x0f),
+       UFS_MSM_PHY_CAL_ENTRY(UFS_PHY_RX_HS_G3_PREPARE_LENGTH_CAPABILITY,
0x0f),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_TX_CLKBUF_ENABLE(0), 0x09),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_TX_RESET_TSYNC_EN(0), 0x01),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_TX_RES_CODE(0), 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_TX_SERDES_BYP_EN_OUT(0), 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_TX_REC_DETECT_LVL(0), 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_TX_PARRATE_REC_DETECT_IDLE_EN(0),
0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_TX_TRAN_DRVR_EMP_EN(0), 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_AUX_CONTROL(0), 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_AUX_DATA_TCODE(0), 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_RCLK_AUXDATA_SEL(0), 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_EQ_CONTROL(0), 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_RX_IQ_RXDET_EN(0), 0x51),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_RX_TERM_HIGHZ_CM_AC_COUPLE(0),
0x05),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_CDR_FREEZE_UP_DN(0), 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_UFS_CNTRL(0), 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_EIGHTH(0), 0x22),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_UCDR_FO_GAIN(0), 0x0a),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_UCDR_SO_GAIN(0), 0x06),
+       UFS_MSM_PHY_CAL_ENTRY(
+               QSERDES_RX_UCDR_SO_SATURATION_AND_ENABLE(0), 0x35),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_UCDR_FO_TO_SO_DELAY(0), 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_TX_CLKBUF_ENABLE(1), 0x09),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_TX_RESET_TSYNC_EN(1), 0x01),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_TX_RES_CODE(1), 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_TX_SERDES_BYP_EN_OUT(1), 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_TX_REC_DETECT_LVL(1), 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_TX_PARRATE_REC_DETECT_IDLE_EN(1),
0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_TX_TRAN_DRVR_EMP_EN(1), 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_AUX_CONTROL(1), 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_AUX_DATA_TCODE(1), 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_RCLK_AUXDATA_SEL(1), 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_EQ_CONTROL(1), 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_RX_IQ_RXDET_EN(1), 0x51),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_RX_TERM_HIGHZ_CM_AC_COUPLE(1),
0x05),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_CDR_FREEZE_UP_DN(1), 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_UFS_CNTRL(1), 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_EIGHTH(1), 0x22),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_UCDR_FO_GAIN(1), 0x0a),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_UCDR_SO_GAIN(1), 0x06),
+       UFS_MSM_PHY_CAL_ENTRY(
+               QSERDES_RX_UCDR_SO_SATURATION_AND_ENABLE(1), 0x35),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_RX_UCDR_FO_TO_SO_DELAY(1), 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_CMN_MODE, 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_IE_TRIM, 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_IP_TRIM, 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_CORE_CLK_IN_SYNC_SEL, 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_PLL_TEST_UPDN_RESTRIMSTEP, 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_FAUX_EN, 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_TX_LANE_MODE(0), 0x08),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_TX_LANE_MODE(1), 0x08),
+};
+
+static struct ufs_msm_phy_calibration phy_cal_table_rate_B[] = {
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CLKEPDIV, 0x03),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START1, 0x98),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START2, 0x03),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START1, 0x80),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START2, 0x80),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START3, 0x10),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP1, 0x65),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP2, 0x1E),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP3, 0x00),
+       UFS_MSM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP_EN, 0x03),
+};
+
+static struct ufs_msm_phy_calibration cached_phy_regs[] = {
+       {QSERDES_COM_PLL_CRCTRL},
+       {QSERDES_COM_PLL_CNTRL},
+       {QSERDES_COM_SYSCLK_EN_SEL},
+       {QSERDES_COM_SYS_CLK_CTRL},
+       {QSERDES_COM_PLL_CLKEPDIV},
+       {QSERDES_COM_DEC_START1},
+       {QSERDES_COM_DEC_START2},
+       {QSERDES_COM_DIV_FRAC_START1},
+       {QSERDES_COM_DIV_FRAC_START2},
+       {QSERDES_COM_DIV_FRAC_START3},
+       {QSERDES_COM_PLLLOCK_CMP1},
+       {QSERDES_COM_PLLLOCK_CMP2},
+       {QSERDES_COM_PLLLOCK_CMP3},
+       {QSERDES_COM_PLLLOCK_CMP_EN},
+       {QSERDES_COM_RESETSM_CNTRL},
+       {QSERDES_COM_PLL_RXTXEPCLK_EN},
+       {QSERDES_RX_PWM_CNTRL1(0)},
+       {QSERDES_RX_PWM_CNTRL1(1)},
+       {QSERDES_RX_CDR_CONTROL(0)},
+       {QSERDES_RX_CDR_CONTROL_HALF(0)},
+       {QSERDES_RX_CDR_CONTROL_QUARTER(0)},
+       {QSERDES_RX_CDR_CONTROL(1)},
+       {QSERDES_RX_CDR_CONTROL_HALF(1)},
+       {QSERDES_RX_CDR_CONTROL_QUARTER(1)},
+       {QSERDES_RX_SIGDET_CNTRL(0)},
+       {QSERDES_RX_SIGDET_CNTRL(1)},
+       {QSERDES_RX_SIGDET_CNTRL2(0)},
+       {QSERDES_RX_SIGDET_CNTRL2(1)},
+       {QSERDES_RX_RX_EQ_GAIN1(0)},
+       {QSERDES_RX_RX_EQ_GAIN2(0)},
+       {QSERDES_RX_RX_EQ_GAIN1(1)},
+       {QSERDES_RX_RX_EQ_GAIN2(1)},
+       {QSERDES_COM_PLL_IP_SETI},
+       {QSERDES_COM_PLL_CP_SETI},
+       {QSERDES_COM_PLL_IP_SETP},
+       {QSERDES_COM_PLL_CP_SETP},
+       {UFS_PHY_PWM_G1_CLK_DIVIDER},
+       {UFS_PHY_PWM_G2_CLK_DIVIDER},
+       {UFS_PHY_PWM_G3_CLK_DIVIDER},
+       {UFS_PHY_PWM_G4_CLK_DIVIDER},
+       {UFS_PHY_CORECLK_PWM_G1_CLK_DIVIDER},
+       {UFS_PHY_CORECLK_PWM_G2_CLK_DIVIDER},
+       {UFS_PHY_CORECLK_PWM_G3_CLK_DIVIDER},
+       {UFS_PHY_CORECLK_PWM_G4_CLK_DIVIDER},
+       {UFS_PHY_OMC_STATUS_RDVAL},
+       {UFS_PHY_LINE_RESET_TIME},
+       {UFS_PHY_LINE_RESET_GRANULARITY},
+       {UFS_PHY_TSYNC_RSYNC_CNTL},
+       {UFS_PHY_PLL_CNTL},
+       {UFS_PHY_TX_LARGE_AMP_DRV_LVL},
+       {UFS_PHY_TX_SMALL_AMP_DRV_LVL},
+       {UFS_PHY_TX_LARGE_AMP_POST_EMP_LVL},
+       {UFS_PHY_TX_SMALL_AMP_POST_EMP_LVL},
+       {UFS_PHY_CFG_CHANGE_CNT_VAL},
+       {UFS_PHY_RX_SYNC_WAIT_TIME},
+       {UFS_PHY_TX_MIN_SLEEP_NOCONFIG_TIME_CAPABILITY},
+       {UFS_PHY_RX_MIN_SLEEP_NOCONFIG_TIME_CAPABILITY},
+       {UFS_PHY_TX_MIN_STALL_NOCONFIG_TIME_CAPABILITY},
+       {UFS_PHY_RX_MIN_STALL_NOCONFIG_TIME_CAPABILITY},
+       {UFS_PHY_TX_MIN_SAVE_CONFIG_TIME_CAPABILITY},
+       {UFS_PHY_RX_MIN_SAVE_CONFIG_TIME_CAPABILITY},
+       {UFS_PHY_RX_PWM_BURST_CLOSURE_LENGTH_CAPABILITY},
+       {UFS_PHY_RX_MIN_ACTIVATETIME_CAPABILITY},
+       {QSERDES_RX_CDR_CONTROL3(0)},
+       {QSERDES_RX_CDR_CONTROL3(1)},
+       {QSERDES_COM_RES_TRIM_OFFSET},
+       {QSERDES_COM_BGTC},
+       {QSERDES_COM_PLL_AMP_OS},
+};
+
+static struct ufs_msm_phy_stored_attributes cached_phy_attr[] = {
+       {TX_MODE},
+       {TX_HSRATE_SERIES},
+       {TX_HSGEAR},
+       {TX_PWMGEAR},
+       {TX_AMPLITUDE},
+       {TX_HS_SLEWRATE},
+       {TX_SYNC_SOURCE},
+       {TX_HS_PREPARE_LENGTH},
+       {TX_LS_PREPARE_LENGTH},
+       {TX_LCC_ENABLE},
+       {TX_PWM_BURST_CLOSURE_EXTENSION},
+       {TX_BYPASS_8B10B_ENABLE},
+       {TX_DRIVER_POLARITY},
+       {TX_HS_UNTERMINATED_LINE_DRIVE_ENABLE},
+       {TX_LS_TERMINATED_LINE_DRIVE_ENABLE},
+       {TX_LCC_SEQUENCER},
+       {TX_MIN_ACTIVATETIME},
+       {TX_PWM_G6_G7_SYNC_LENGTH},
+       {RX_MODE},
+       {RX_HSRATE_SERIES},
+       {RX_HSGEAR},
+       {RX_PWMGEAR},
+       {RX_LS_TERMINATED_ENABLE},
+       {RX_HS_UNTERMINATED_ENABLE},
+       {RX_ENTER_HIBERN8},
+       {RX_BYPASS_8B10B_ENABLE},
+       {RX_TERMINATION_FORCE_ENABLE},
+};
+
+#endif
diff --git a/drivers/scsi/ufs/ufs-msm-phy.c b/drivers/scsi/ufs/ufs-msm-phy.c
new file mode 100644
index 0000000..82486b2
--- /dev/null
+++ b/drivers/scsi/ufs/ufs-msm-phy.c
@@ -0,0 +1,646 @@
+/*
+ * Copyright (c) 2013-2014, Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/time.h>
+#include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/iopoll.h>
+#include <linux/platform_device.h>
+#include <linux/msm-bus.h>
+
+#include "ufshcd.h"
+#include "unipro.h"
+#include "ufs-msm.h"
+#include "ufs-msm-phy.h"
+
+int ufs_msm_phy_calibrate(struct ufs_msm_phy *ufs_msm_phy,
+                       struct ufs_msm_phy_calibration *tbl_A, int
tbl_size_A,
+                       struct ufs_msm_phy_calibration *tbl_B, int
tbl_size_B,
+                       int rate)
+{
+       int i;
+       int ret = 0;
+
+       if (!tbl_A) {
+               dev_err(ufs_msm_phy->dev, "%s: tbl_A is NULL", __func__);
+               ret = EINVAL;
+               goto out;
+       }
+
+       for (i = 0; i < tbl_size_A; i++)
+               writel_relaxed(tbl_A[i].cfg_value,
+                               ufs_msm_phy->mmio + tbl_A[i].reg_offset);
+
+       /*
+        * In case we would like to work in rate B, we need
+        * to override a registers that were configured in rate A table
+        * with registers of rate B table.
+        * table.
+        */
+       if (rate == PA_HS_MODE_B) {
+               if (!tbl_B) {
+                       dev_err(ufs_msm_phy->dev, "%s: tbl_B is NULL",
+                               __func__);
+                       ret = EINVAL;
+                       goto out;
+               }
+
+               for (i = 0; i < tbl_size_B; i++)
+                       writel_relaxed(tbl_B[i].cfg_value,
+                               ufs_msm_phy->mmio + tbl_B[i].reg_offset);
+       }
+
+       /* flush buffered writes */
+       mb();
+
+out:
+       return ret;
+}
+
+struct phy *ufs_msm_phy_generic_probe(struct platform_device *pdev,
+                         struct ufs_msm_phy *common_cfg,
+                         struct phy_ops *ufs_msm_phy_gen_ops,
+                         struct ufs_msm_phy_specific_ops *phy_spec_ops)
+{
+       int err;
+       struct device *dev = &pdev->dev;
+       struct phy *generic_phy = NULL;
+       struct phy_provider *phy_provider;
+
+       err = ufs_msm_phy_base_init(pdev, common_cfg);
+       if (err) {
+               dev_err(dev, "%s: phy base init failed %d\n", __func__,
err);
+               goto out;
+       }
+
+       phy_provider = devm_of_phy_provider_register(dev,
of_phy_simple_xlate);
+       if (IS_ERR(phy_provider)) {
+               err = PTR_ERR(phy_provider);
+               dev_err(dev, "%s: failed to register phy %d\n", __func__,
err);
+               goto out;
+       }
+
+       generic_phy = devm_phy_create(dev, ufs_msm_phy_gen_ops, NULL);
+       if (IS_ERR(generic_phy)) {
+               err =  PTR_ERR(generic_phy);
+               dev_err(dev, "%s: failed to create phy %d\n", __func__,
err);
+               goto out;
+       }
+
+       common_cfg->phy_spec_ops = phy_spec_ops;
+       common_cfg->dev = dev;
+
+out:
+       return generic_phy;
+}
+
+/*
+ * This assumes the embedded phy structure inside generic_phy is of type
+ * struct ufs_msm_phy. In order to function properly it's crucial
+ * to keep the embedded struct "struct ufs_msm_phy common_cfg"
+ * as the first inside generic_phy.
+ */
+struct ufs_msm_phy *get_ufs_msm_phy(struct phy *generic_phy)
+{
+       return (struct ufs_msm_phy *)phy_get_drvdata(generic_phy);
+}
+
+int ufs_msm_phy_base_init(struct platform_device *pdev,
+               struct ufs_msm_phy *phy_common)
+{
+       struct device *dev = &pdev->dev;
+       struct resource *res;
+       int err = 0;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res) {
+               dev_err(dev, "%s: platform_get_resource() failed. returned
NULL\n",
+                       __func__);
+               err = -ENOMEM;
+               goto out;
+       }
+
+       phy_common->mmio = devm_ioremap_resource(dev, res);
+       if (IS_ERR(phy_common->mmio)) {
+               err = PTR_ERR(phy_common->mmio);
+               dev_err(dev, "ioremap resource failed %d\n", err);
+       }
+
+out:
+       return err;
+}
+
+int ufs_msm_phy_clk_get(struct phy *phy,
+               const char *name, struct clk **clk_out)
+{
+       struct clk *clk;
+       int err = 0;
+       struct ufs_msm_phy *ufs_msm_phy = get_ufs_msm_phy(phy);
+       struct device *dev = ufs_msm_phy->dev;
+
+       clk = devm_clk_get(dev, name);
+       if (IS_ERR(clk)) {
+               err = PTR_ERR(clk);
+               dev_err(dev, "failed to get %s err %d", name, err);
+       } else {
+               *clk_out = clk;
+       }
+
+       return err;
+}
+
+int
+ufs_msm_phy_init_clks(struct phy *generic_phy, struct ufs_msm_phy
*phy_common)
+{
+       int err;
+
+       err = ufs_msm_phy_clk_get(generic_phy, "tx_iface_clk",
+               &phy_common->tx_iface_clk);
+       if (err)
+               goto out;
+
+       err = ufs_msm_phy_clk_get(generic_phy, "rx_iface_clk",
+               &phy_common->rx_iface_clk);
+       if (err)
+               goto out;
+
+       err = ufs_msm_phy_clk_get(generic_phy, "ref_clk_src",
+               &phy_common->ref_clk_src);
+       if (err)
+               goto out;
+
+       err = ufs_msm_phy_clk_get(generic_phy, "ref_clk_parent",
+               &phy_common->ref_clk_parent);
+       if (err)
+               goto out;
+
+       err = ufs_msm_phy_clk_get(generic_phy, "ref_clk",
+               &phy_common->ref_clk);
+
+out:
+       return err;
+}
+
+int
+ufs_msm_phy_init_vregulators(struct phy *generic_phy,
+                            struct ufs_msm_phy *phy_common)
+{
+       int err;
+
+       err = ufs_msm_phy_init_vreg(generic_phy, &phy_common->vdda_pll,
+               "vdda-pll");
+       if (err)
+               goto out;
+
+       err = ufs_msm_phy_init_vreg(generic_phy, &phy_common->vdda_phy,
+               "vdda-phy");
+out:
+       return err;
+}
+
+int ufs_msm_phy_init_vreg(struct phy *phy,
+               struct ufs_msm_phy_vreg *vreg, const char *name)
+{
+       int err = 0;
+       struct ufs_msm_phy *ufs_msm_phy = get_ufs_msm_phy(phy);
+       struct device *dev = ufs_msm_phy->dev;
+
+       char prop_name[MAX_PROP_NAME];
+
+       vreg->name = kstrdup(name, GFP_KERNEL);
+       if (!vreg->name) {
+               err = -ENOMEM;
+               goto out;
+       }
+
+       vreg->reg = devm_regulator_get(dev, name);
+       if (IS_ERR(vreg->reg)) {
+               err = PTR_ERR(vreg->reg);
+               dev_err(dev, "failed to get %s, %d\n", name, err);
+               goto out;
+       }
+
+       if (dev->of_node) {
+               snprintf(prop_name, MAX_PROP_NAME, "%s-max-microamp", name);
+               err = of_property_read_u32(dev->of_node,
+                                       prop_name, &vreg->max_uA);
+               if (err && err != -EINVAL) {
+                       dev_err(dev, "%s: failed to read %s\n",
+                                       __func__, prop_name);
+                       goto out;
+               } else if (err == -EINVAL || !vreg->max_uA) {
+                       if (regulator_count_voltages(vreg->reg) > 0) {
+                               dev_err(dev, "%s: %s is mandatory\n",
+                                               __func__, prop_name);
+                               goto out;
+                       }
+                       err = 0;
+               }
+       }
+
+       if (!strcmp(name, "vdda-pll")) {
+               vreg->max_uV = VDDA_PLL_MAX_UV;
+               vreg->min_uV = VDDA_PLL_MIN_UV;
+       } else if (!strcmp(name, "vdda-phy")) {
+               vreg->max_uV = VDDA_PHY_MAX_UV;
+               vreg->min_uV = VDDA_PHY_MIN_UV;
+       }
+
+out:
+       if (err)
+               kfree(vreg->name);
+       return err;
+}
+
+int ufs_msm_phy_cfg_vreg(struct phy *phy,
+                       struct ufs_msm_phy_vreg *vreg, bool on)
+{
+       int ret = 0;
+       struct regulator *reg = vreg->reg;
+       const char *name = vreg->name;
+       int min_uV;
+       int uA_load;
+       struct ufs_msm_phy *ufs_msm_phy = get_ufs_msm_phy(phy);
+       struct device *dev = ufs_msm_phy->dev;
+
+       BUG_ON(!vreg);
+
+       if (regulator_count_voltages(reg) > 0) {
+               min_uV = on ? vreg->min_uV : 0;
+               ret = regulator_set_voltage(reg, min_uV, vreg->max_uV);
+               if (ret) {
+                       dev_err(dev, "%s: %s set voltage failed, err=%d\n",
+                                       __func__, name, ret);
+                       goto out;
+               }
+               uA_load = on ? vreg->max_uA : 0;
+               ret = regulator_set_optimum_mode(reg, uA_load);
+               if (ret >= 0) {
+                       /*
+                        * regulator_set_optimum_mode() returns new
regulator
+                        * mode upon success.
+                        */
+                       ret = 0;
+               } else {
+                       dev_err(dev, "%s: %s set optimum mode(uA_load=%d)
failed, err=%d\n",
+                                       __func__, name, uA_load, ret);
+                       goto out;
+               }
+       }
+out:
+       return ret;
+}
+
+int ufs_msm_phy_enable_vreg(struct phy *phy,
+                       struct ufs_msm_phy_vreg *vreg)
+{
+       struct ufs_msm_phy *ufs_msm_phy = get_ufs_msm_phy(phy);
+       struct device *dev = ufs_msm_phy->dev;
+       int ret = 0;
+
+       if (!vreg || vreg->enabled)
+               goto out;
+
+       ret = ufs_msm_phy_cfg_vreg(phy, vreg, true);
+       if (ret) {
+               dev_err(dev, "%s: ufs_msm_phy_cfg_vreg() failed, err=%d\n",
+                               __func__, ret);
+               goto out;
+       }
+
+       ret = regulator_enable(vreg->reg);
+       if (ret) {
+               dev_err(dev, "%s: enable failed, err=%d\n",
+                               __func__, ret);
+               goto out;
+       }
+
+       vreg->enabled = true;
+out:
+       return ret;
+}
+
+int ufs_msm_phy_enable_ref_clk(struct phy *generic_phy)
+{
+       int ret = 0;
+       struct ufs_msm_phy *phy = get_ufs_msm_phy(generic_phy);
+       if (phy->is_ref_clk_enabled)
+               goto out;
+
+       /*
+        * reference clock is propagated in a daisy-chained manner from
+        * source to phy, so ungate them at each stage.
+        */
+       ret = clk_prepare_enable(phy->ref_clk_src);
+       if (ret) {
+               dev_err(phy->dev, "%s: ref_clk_src enable failed %d\n",
+                               __func__, ret);
+               goto out;
+       }
+
+       ret = clk_prepare_enable(phy->ref_clk_parent);
+       if (ret) {
+               dev_err(phy->dev, "%s: ref_clk_parent enable failed %d\n",
+                               __func__, ret);
+               goto out_disable_src;
+       }
+
+       ret = clk_prepare_enable(phy->ref_clk);
+       if (ret) {
+               dev_err(phy->dev, "%s: ref_clk enable failed %d\n",
+                               __func__, ret);
+               goto out_disable_parent;
+       }
+
+       phy->is_ref_clk_enabled = true;
+       goto out;
+
+out_disable_parent:
+       clk_disable_unprepare(phy->ref_clk_parent);
+out_disable_src:
+       clk_disable_unprepare(phy->ref_clk_src);
+out:
+       return ret;
+}
+
+int ufs_msm_phy_disable_vreg(struct phy *phy,
+                            struct ufs_msm_phy_vreg *vreg)
+{
+       struct ufs_msm_phy *ufs_msm_phy = get_ufs_msm_phy(phy);
+       struct device *dev = ufs_msm_phy->dev;
+       int ret = 0;
+
+       if (!vreg || !vreg->enabled)
+               goto out;
+
+       ret = regulator_disable(vreg->reg);
+
+       if (!ret) {
+               /* ignore errors on applying disable config */
+               ufs_msm_phy_cfg_vreg(phy, vreg, false);
+               vreg->enabled = false;
+       } else {
+               dev_err(dev, "%s: %s disable failed, err=%d\n",
+                               __func__, vreg->name, ret);
+       }
+out:
+       return ret;
+}
+
+void ufs_msm_phy_disable_ref_clk(struct phy *generic_phy)
+{
+       struct ufs_msm_phy *phy = get_ufs_msm_phy(generic_phy);
+
+       if (phy->is_ref_clk_enabled) {
+               clk_disable_unprepare(phy->ref_clk);
+               clk_disable_unprepare(phy->ref_clk_parent);
+               clk_disable_unprepare(phy->ref_clk_src);
+               phy->is_ref_clk_enabled = false;
+       }
+}
+
+void ufs_msm_phy_restore_swi_regs(struct phy *generic_phy)
+{
+       int i;
+       struct ufs_msm_phy *phy = get_ufs_msm_phy(generic_phy);
+
+       for (i = 0; i < phy->cached_regs_table_size; i++) {
+               struct ufs_msm_phy_calibration *table =
+                       (struct ufs_msm_phy_calibration *)phy->cached_regs;
+               writel_relaxed(table[i].cfg_value, phy->mmio +
+                               table[i].reg_offset);
+       }
+
+       /* flush buffered writes */
+       mb();
+}
+
+/* Turn ON M-PHY RMMI interface clocks */
+int ufs_msm_phy_enable_iface_clk(struct phy *generic_phy)
+{
+       struct ufs_msm_phy *phy = get_ufs_msm_phy(generic_phy);
+       int ret = 0;
+       if (phy->is_iface_clk_enabled)
+               goto out;
+
+       ret = clk_prepare_enable(phy->tx_iface_clk);
+       if (ret) {
+               dev_err(phy->dev, "%s: tx_iface_clk enable failed %d\n",
+                               __func__, ret);
+               goto out;
+       }
+       ret = clk_prepare_enable(phy->rx_iface_clk);
+       if (ret) {
+               clk_disable_unprepare(phy->tx_iface_clk);
+               dev_err(phy->dev, "%s: rx_iface_clk enable failed %d.
disabling also tx_iface_clk\n",
+                               __func__, ret);
+               goto out;
+       }
+       phy->is_iface_clk_enabled = true;
+
+out:
+       return ret;
+}
+
+/* Turn OFF M-PHY RMMI interface clocks */
+void ufs_msm_phy_disable_iface_clk(struct phy *generic_phy)
+{
+       struct ufs_msm_phy *phy = get_ufs_msm_phy(generic_phy);
+
+       if (phy->is_iface_clk_enabled) {
+               clk_disable_unprepare(phy->tx_iface_clk);
+               clk_disable_unprepare(phy->rx_iface_clk);
+               phy->is_iface_clk_enabled = false;
+       }
+}
+
+int ufs_msm_phy_is_cfg_restore_quirk_enabled(struct phy *phy)
+{
+       struct ufs_msm_phy *ufs_msm_phy = get_ufs_msm_phy(phy);
+
+       return ufs_msm_phy->quirks & MSM_UFS_PHY_QUIRK_CFG_RESTORE;
+}
+
+int ufs_msm_phy_start_serdes(struct phy *generic_phy)
+{
+       struct ufs_msm_phy *ufs_msm_phy = get_ufs_msm_phy(generic_phy);
+       int ret = 0;
+
+       if (!ufs_msm_phy->phy_spec_ops->start_serdes) {
+               dev_err(ufs_msm_phy->dev, "%s: start_serdes() callback is
not supported\n",
+                       __func__);
+               ret = -ENOTSUPP;
+       } else {
+               ufs_msm_phy->phy_spec_ops->start_serdes(ufs_msm_phy);
+       }
+
+       return ret;
+}
+
+int ufs_msm_phy_set_tx_lane_enable(struct phy *generic_phy, u32 tx_lanes)
+{
+       struct ufs_msm_phy *ufs_msm_phy = get_ufs_msm_phy(generic_phy);
+       int ret = 0;
+
+       if (!ufs_msm_phy->phy_spec_ops->set_tx_lane_enable) {
+               dev_err(ufs_msm_phy->dev, "%s: set_tx_lane_enable() callback
is not supported\n",
+                       __func__);
+               ret = -ENOTSUPP;
+       } else {
+               ufs_msm_phy->phy_spec_ops->set_tx_lane_enable(ufs_msm_phy,
+                                                               tx_lanes);
+       }
+
+       return ret;
+}
+
+void ufs_msm_phy_save_controller_version(struct phy *generic_phy,
+                                               u8 major, u16 minor, u16
step)
+{
+       struct ufs_msm_phy *ufs_msm_phy = get_ufs_msm_phy(generic_phy);
+
+       ufs_msm_phy->host_ctrl_rev_major = major;
+       ufs_msm_phy->host_ctrl_rev_minor = minor;
+       ufs_msm_phy->host_ctrl_rev_step = step;
+}
+
+int ufs_msm_phy_calibrate_phy(struct phy *generic_phy)
+{
+       struct ufs_msm_phy *ufs_msm_phy = get_ufs_msm_phy(generic_phy);
+       int ret = 0;
+
+       if (!ufs_msm_phy->phy_spec_ops->calibrate_phy) {
+               dev_err(ufs_msm_phy->dev, "%s: calibrate_phy() callback is
not supported\n",
+                       __func__);
+               ret = -ENOTSUPP;
+       } else {
+               ret = ufs_msm_phy->phy_spec_ops->
+                               calibrate_phy(ufs_msm_phy);
+               if (ret)
+                       dev_err(ufs_msm_phy->dev, "%s: calibrate_phy()
failed %d\n",
+                               __func__, ret);
+       }
+
+       return ret;
+}
+
+int ufs_msm_phy_remove(struct phy *generic_phy,
+                      struct ufs_msm_phy *ufs_msm_phy)
+{
+       phy_power_off(generic_phy);
+
+       kfree(ufs_msm_phy->vdda_pll.name);
+       kfree(ufs_msm_phy->vdda_phy.name);
+
+       return 0;
+}
+
+int ufs_msm_phy_exit(struct phy *generic_phy)
+{
+       struct ufs_msm_phy *ufs_msm_phy = get_ufs_msm_phy(generic_phy);
+
+       if (ufs_msm_phy->is_powered_on)
+               phy_power_off(generic_phy);
+
+       return 0;
+}
+
+int ufs_msm_phy_is_pcs_ready(struct phy *generic_phy)
+{
+       struct ufs_msm_phy *ufs_msm_phy = get_ufs_msm_phy(generic_phy);
+
+       if (!ufs_msm_phy->phy_spec_ops->is_physical_coding_sublayer_ready) {
+               dev_err(ufs_msm_phy->dev, "%s:
is_physical_coding_sublayer_ready() callback is not supported\n",
+                       __func__);
+               return -ENOTSUPP;
+       }
+
+       return ufs_msm_phy->phy_spec_ops->
+                       is_physical_coding_sublayer_ready(ufs_msm_phy);
+}
+
+int ufs_msm_phy_save_configuration(struct phy *generic_phy)
+{
+       struct ufs_msm_phy *ufs_msm_phy = get_ufs_msm_phy(generic_phy);
+       int ret = 0;
+
+       if (!ufs_msm_phy->phy_spec_ops->save_configuration) {
+               dev_err(ufs_msm_phy->dev, "%s: save_configuration() callback
is not supported\n",
+                       __func__);
+               ret = -ENOTSUPP;
+       } else {
+               ufs_msm_phy->phy_spec_ops->save_configuration(ufs_msm_phy);
+       }
+
+       return ret;
+}
+
+int ufs_msm_phy_power_on(struct phy *generic_phy)
+{
+       struct ufs_msm_phy *phy_common = get_ufs_msm_phy(generic_phy);
+       struct device *dev = phy_common->dev;
+       int err;
+
+       err = ufs_msm_phy_enable_vreg(generic_phy, &phy_common->vdda_phy);
+       if (err) {
+               dev_err(dev, "%s enable vdda_phy failed, err=%d\n",
+                       __func__, err);
+               goto out;
+       }
+
+       phy_common->phy_spec_ops->power_control(phy_common, true);
+
+       /* vdda_pll also enables ref clock LDOs so enable it first */
+       err = ufs_msm_phy_enable_vreg(generic_phy, &phy_common->vdda_pll);
+       if (err) {
+               dev_err(dev, "%s enable vdda_pll failed, err=%d\n",
+                       __func__, err);
+               goto out_disable_phy;
+       }
+
+       err = ufs_msm_phy_enable_ref_clk(generic_phy);
+       if (err) {
+               dev_err(dev, "%s enable phy ref clock failed, err=%d\n",
+                       __func__, err);
+               goto out_disable_pll;
+       }
+
+       phy_common->is_powered_on = true;
+       goto out;
+
+out_disable_pll:
+       ufs_msm_phy_disable_vreg(generic_phy, &phy_common->vdda_pll);
+out_disable_phy:
+       ufs_msm_phy_disable_vreg(generic_phy, &phy_common->vdda_phy);
+out:
+       return err;
+}
+
+int ufs_msm_phy_power_off(struct phy *generic_phy)
+{
+       struct ufs_msm_phy *phy_common = get_ufs_msm_phy(generic_phy);
+
+       phy_common->phy_spec_ops->power_control(phy_common, false);
+
+       ufs_msm_phy_disable_ref_clk(generic_phy);
+
+       ufs_msm_phy_disable_vreg(generic_phy, &phy_common->vdda_pll);
+       ufs_msm_phy_disable_vreg(generic_phy, &phy_common->vdda_phy);
+       phy_common->is_powered_on = false;
+
+       return 0;
+}
diff --git a/drivers/scsi/ufs/ufs-msm-phy.h b/drivers/scsi/ufs/ufs-msm-phy.h
new file mode 100644
index 0000000..e6f2b92
--- /dev/null
+++ b/drivers/scsi/ufs/ufs-msm-phy.h
@@ -0,0 +1,193 @@
+/*
+ * Copyright (c) 2013-2014, Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef UFS_MSM_PHY_H_
+#define UFS_MSM_PHY_H_
+
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/time.h>
+#include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/iopoll.h>
+#include <linux/platform_device.h>
+#include <linux/phy/phy.h>
+#include <linux/msm-bus.h>
+
+#include "ufshcd.h"
+#include "unipro.h"
+#include "ufs-msm.h"
+
+#define UFS_MSM_PHY_CAL_ENTRY(reg, val)        \
+       {                               \
+               .reg_offset = reg,      \
+               .cfg_value = val,       \
+       }
+
+#define UFS_MSM_PHY_NAME_LEN   30
+
+struct ufs_msm_phy_stored_attributes {
+       u32 att;
+       u32 value;
+};
+
+struct ufs_msm_phy_calibration {
+       u32 reg_offset;
+       u32 cfg_value;
+};
+
+struct ufs_msm_phy {
+       struct list_head list;
+       struct device *dev;
+       void __iomem *mmio;
+       struct clk *tx_iface_clk;
+       struct clk *rx_iface_clk;
+       bool is_iface_clk_enabled;
+       struct clk *ref_clk_src;
+       struct clk *ref_clk_parent;
+       struct clk *ref_clk;
+       bool is_ref_clk_enabled;
+       struct ufs_msm_phy_vreg vdda_pll;
+       struct ufs_msm_phy_vreg vdda_phy;
+       unsigned int quirks;
+       u8 host_ctrl_rev_major;
+       u16 host_ctrl_rev_minor;
+       u16 host_ctrl_rev_step;
+
+       /*
+        * As part of UFS power management, UFS link would be put in
hibernate
+        * and UFS device would be put in SLEEP mode as part of
runtime/system
+        * suspend callback. But when system goes into suspend with VDD
+        * minimization, UFS PHY states are being reset which means UFS link
+        * hibernate exit command on system resume would fail.
+        * If this quirk is enabled then above issue is workaround by saving
+        * the UFS PHY state information before system goes into suspend and
+        * restoring the saved state information during system resume but
+        * before executing the hibern8 exit command.
+        * Note that this quirk will help restoring the PHY state if even
when
+        * link in not kept in hibern8 during suspend.
+        *
+        * Here is the list of steps to save/restore the configuration:
+        * Before entering into system suspend:
+        *      1. Read Critical PCS SWI Registers  + less critical PHY CSR
+        *      2. Read RMMI Attributes
+        * Enter into system suspend
+        * After exiting from system suspend:
+        *      1. Set UFS_PHY_SOFT_RESET bit in UFS_CFG1 register of the
UFS
+        *         Controller
+        *      2. Write 0x01 to the UFS_PHY_POWER_DOWN_CONTROL register in
the
+        *         UFS PHY
+        *      3. Write back the values of the PHY SWI registers
+        *      4. Clear UFS_PHY_SOFT_RESET bit in UFS_CFG1 register of the
UFS
+        *         Controller
+        *      5. Write 0x01 to the UFS_PHY_PHY_START in the UFS PHY. This
will
+        *         start the PLL calibration and bring-up of the PHY.
+        *      6. Write back the values to the PHY RMMI Attributes
+        *      7. Wait for UFS_PHY_PCS_READY_STATUS[0] to be '1'
+        */
+       #define MSM_UFS_PHY_QUIRK_CFG_RESTORE           (1 << 0)
+
+       /*
+       * If UFS PHY power down is deasserted and power is restored to
analog
+       * circuits, the rx_sigdet can glitch. If the glitch is wide enough,
+       * it can trigger the digital logic to think it saw a DIF-N and cause
+       * it to exit Hibern8. Disabling the rx_sigdet during power-up masks
+       * the glitch.
+       */
+       #define MSM_UFS_PHY_DIS_SIGDET_BEFORE_PWR_COLLAPSE      (1 << 1)
+
+       /*
+       * If UFS link is put into Hibern8 and if UFS PHY analog hardware is
+       * power collapsed (by clearing UFS_PHY_POWER_DOWN_CONTROL), Hibern8
+       * exit might fail even after powering on UFS PHY analog hardware.
+       * Enabling this quirk will help to solve above issue by doing
+       * custom PHY settings just before PHY analog power collapse.
+       */
+       #define MSM_UFS_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE   (1
<< 2)
+
+       char name[UFS_MSM_PHY_NAME_LEN];
+       struct ufs_msm_phy_calibration *cached_regs;
+       int cached_regs_table_size;
+       bool is_powered_on;
+       struct ufs_msm_phy_specific_ops *phy_spec_ops;
+};
+
+/**
+ * struct ufs_msm_phy_specific_ops - set of pointers to functions which
have a
+ * specific implementation per phy. Each UFS phy, should implement
+ * those functions according to its spec and requirements
+ * @calibrate_phy: pointer to a function that calibrate the phy
+ * @start_serdes: pointer to a function that starts the serdes
+ * @save_configuration: pointer to a function that saves phy
+ * configuration
+ * @is_physical_coding_sublayer_ready: pointer to a function that
+ * checks pcs readiness
+ * @set_tx_lane_enable: pointer to a function that enable tx lanes
+ * @power_control: pointer to a function that controls analog rail of phy
+ * and writes to QSERDES_RX_SIGDET_CNTRL attribute
+ */
+struct ufs_msm_phy_specific_ops {
+       int (*calibrate_phy) (struct ufs_msm_phy *phy);
+       void (*start_serdes) (struct ufs_msm_phy *phy);
+       void (*save_configuration)(struct ufs_msm_phy *phy);
+       int (*is_physical_coding_sublayer_ready) (struct ufs_msm_phy *phy);
+       void (*set_tx_lane_enable) (struct ufs_msm_phy *phy, u32 val);
+       void (*power_control) (struct ufs_msm_phy *phy, bool val);
+};
+
+int ufs_msm_phy_init_vreg(struct phy *phy,
+               struct ufs_msm_phy_vreg *vreg, const char *name);
+int ufs_msm_phy_cfg_vreg(struct phy *phy,
+                       struct ufs_msm_phy_vreg *vreg, bool on);
+int ufs_msm_phy_enable_vreg(struct phy *phy,
+                       struct ufs_msm_phy_vreg *vreg);
+int ufs_msm_phy_disable_vreg(struct phy *phy,
+                       struct ufs_msm_phy_vreg *vreg);
+int ufs_msm_phy_enable_ref_clk(struct phy *phy);
+void ufs_msm_phy_disable_ref_clk(struct phy *phy);
+int ufs_msm_phy_enable_iface_clk(struct phy *phy);
+void ufs_msm_phy_disable_iface_clk(struct phy *phy);
+void ufs_msm_phy_restore_swi_regs(struct phy *phy);
+int ufs_msm_phy_link_startup_post_change(struct phy *phy,
+                       struct ufs_hba *hba);
+int ufs_msm_phy_base_init(struct platform_device *pdev,
+                       struct ufs_msm_phy *ufs_msm_phy_ops);
+int ufs_msm_phy_is_cfg_restore_quirk_enabled(struct phy *phy);
+struct ufs_msm_phy *get_ufs_msm_phy(struct phy *generic_phy);
+int ufs_msm_phy_start_serdes(struct phy *generic_phy);
+int ufs_msm_phy_set_tx_lane_enable(struct phy *generic_phy, u32 tx_lanes);
+int ufs_msm_phy_calibrate_phy(struct phy *generic_phy);
+int ufs_msm_phy_is_pcs_ready(struct phy *generic_phy);
+int ufs_msm_phy_save_configuration(struct phy *generic_phy);
+void ufs_msm_phy_save_controller_version(struct phy *generic_phy,
+                       u8 major, u16 minor, u16 step);
+int ufs_msm_phy_power_on(struct phy *generic_phy);
+int ufs_msm_phy_power_off(struct phy *generic_phy);
+int ufs_msm_phy_exit(struct phy *generic_phy);
+int ufs_msm_phy_init_clks(struct phy *generic_phy,
+                       struct ufs_msm_phy *phy_common);
+int ufs_msm_phy_init_vregulators(struct phy *generic_phy,
+                       struct ufs_msm_phy *phy_common);
+int ufs_msm_phy_remove(struct phy *generic_phy,
+                      struct ufs_msm_phy *ufs_msm_phy);
+struct phy *ufs_msm_phy_generic_probe(struct platform_device *pdev,
+                       struct ufs_msm_phy *common_cfg,
+                       struct phy_ops *ufs_msm_phy_gen_ops,
+                       struct ufs_msm_phy_specific_ops *phy_spec_ops);
+int ufs_msm_phy_calibrate(struct ufs_msm_phy *ufs_msm_phy,
+                       struct ufs_msm_phy_calibration *tbl_A, int
tbl_size_A,
+                       struct ufs_msm_phy_calibration *tbl_B, int
tbl_size_B,
+                       int rate);
+#endif
diff --git a/drivers/scsi/ufs/ufs-msm.c b/drivers/scsi/ufs/ufs-msm.c
new file mode 100644
index 0000000..a9cbc1b
--- /dev/null
+++ b/drivers/scsi/ufs/ufs-msm.c
@@ -0,0 +1,1105 @@
+/*
+ * Copyright (c) 2013-2014, Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/time.h>
+#include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/iopoll.h>
+#include <linux/platform_device.h>
+
+#include <linux/msm-bus.h>
+
+#include "ufshcd.h"
+#include "unipro.h"
+#include "ufs-msm.h"
+#include "ufs-msm-phy.h"
+
+static int ufs_msm_get_speed_mode(struct ufs_pa_layer_attr *p, char
*result);
+static int ufs_msm_get_bus_vote(struct ufs_msm_host *host,
+               const char *speed_mode);
+static int ufs_msm_set_bus_vote(struct ufs_msm_host *host, int vote);
+
+static int ufs_msm_get_connected_tx_lanes(struct ufs_hba *hba, u32
*tx_lanes)
+{
+       int err = 0;
+
+       err = ufshcd_dme_get(hba,
+                       UIC_ARG_MIB(PA_CONNECTEDTXDATALANES), tx_lanes);
+       if (err)
+               dev_err(hba->dev, "%s: couldn't read PA_CONNECTEDTXDATALANES
%d\n",
+                               __func__, err);
+
+       return err;
+}
+
+static int ufs_msm_host_clk_get(struct device *dev,
+               const char *name, struct clk **clk_out)
+{
+       struct clk *clk;
+       int err = 0;
+
+       clk = devm_clk_get(dev, name);
+       if (IS_ERR(clk)) {
+               err = PTR_ERR(clk);
+               dev_err(dev, "%s: failed to get %s err %d",
+                               __func__, name, err);
+       } else {
+               *clk_out = clk;
+       }
+
+       return err;
+}
+
+static int ufs_msm_host_clk_enable(struct device *dev,
+               const char *name, struct clk *clk)
+{
+       int err = 0;
+
+       err = clk_prepare_enable(clk);
+       if (err)
+               dev_err(dev, "%s: %s enable failed %d\n", __func__, name,
err);
+
+       return err;
+}
+
+static void ufs_msm_disable_lane_clks(struct ufs_msm_host *host)
+{
+       if (!host->is_lane_clks_enabled)
+               return;
+
+       clk_disable_unprepare(host->tx_l1_sync_clk);
+       clk_disable_unprepare(host->tx_l0_sync_clk);
+       clk_disable_unprepare(host->rx_l1_sync_clk);
+       clk_disable_unprepare(host->rx_l0_sync_clk);
+
+       host->is_lane_clks_enabled = false;
+}
+
+static int ufs_msm_enable_lane_clks(struct ufs_msm_host *host)
+{
+       int err = 0;
+       struct device *dev = host->hba->dev;
+
+       if (host->is_lane_clks_enabled)
+               return 0;
+
+       err = ufs_msm_host_clk_enable(dev,
+                       "rx_lane0_sync_clk", host->rx_l0_sync_clk);
+       if (err)
+               goto out;
+
+       err = ufs_msm_host_clk_enable(dev,
+                       "rx_lane1_sync_clk", host->rx_l1_sync_clk);
+       if (err)
+               goto disable_rx_l0;
+
+       err = ufs_msm_host_clk_enable(dev,
+                       "tx_lane0_sync_clk", host->tx_l0_sync_clk);
+       if (err)
+               goto disable_rx_l1;
+
+       err = ufs_msm_host_clk_enable(dev,
+                       "tx_lane1_sync_clk", host->tx_l1_sync_clk);
+       if (err)
+               goto disable_tx_l0;
+
+       host->is_lane_clks_enabled = true;
+       goto out;
+
+disable_tx_l0:
+       clk_disable_unprepare(host->tx_l0_sync_clk);
+disable_rx_l1:
+       clk_disable_unprepare(host->rx_l1_sync_clk);
+disable_rx_l0:
+       clk_disable_unprepare(host->rx_l0_sync_clk);
+out:
+       return err;
+}
+
+static int ufs_msm_init_lane_clks(struct ufs_msm_host *host)
+{
+       int err = 0;
+       struct device *dev = host->hba->dev;
+
+       err = ufs_msm_host_clk_get(dev,
+                       "rx_lane0_sync_clk", &host->rx_l0_sync_clk);
+       if (err)
+               goto out;
+
+       err = ufs_msm_host_clk_get(dev,
+                       "rx_lane1_sync_clk", &host->rx_l1_sync_clk);
+       if (err)
+               goto out;
+
+       err = ufs_msm_host_clk_get(dev,
+                       "tx_lane0_sync_clk", &host->tx_l0_sync_clk);
+       if (err)
+               goto out;
+
+       err = ufs_msm_host_clk_get(dev,
+                       "tx_lane1_sync_clk", &host->tx_l1_sync_clk);
+out:
+       return err;
+}
+
+static int ufs_msm_link_startup_post_change(struct ufs_hba *hba)
+{
+       struct ufs_msm_host *host = hba->priv;
+       struct phy *phy = host->generic_phy;
+       u32 tx_lanes;
+       int err = 0;
+
+       err = ufs_msm_get_connected_tx_lanes(hba, &tx_lanes);
+       if (err)
+               goto out;
+
+       err = ufs_msm_phy_set_tx_lane_enable(phy, tx_lanes);
+       if (err)
+               dev_err(hba->dev, "%s: ufs_msm_phy_set_tx_lane_enable
failed\n",
+                       __func__);
+
+out:
+       return err;
+}
+
+static int ufs_msm_check_hibern8(struct ufs_hba *hba)
+{
+       int err;
+       u32 tx_fsm_val = 0;
+       unsigned long timeout = jiffies +
msecs_to_jiffies(HBRN8_POLL_TOUT_MS);
+
+       do {
+               err = ufshcd_dme_get(hba,
+                       UIC_ARG_MIB(MPHY_TX_FSM_STATE), &tx_fsm_val);
+               if (err || tx_fsm_val == TX_FSM_HIBERN8)
+                       break;
+
+               /* sleep for max. 200us */
+               usleep_range(100, 200);
+       } while (time_before(jiffies, timeout));
+
+       /*
+        * we might have scheduled out for long during polling so
+        * check the state again.
+        */
+       if (time_after(jiffies, timeout))
+               err = ufshcd_dme_get(hba,
+                               UIC_ARG_MIB(MPHY_TX_FSM_STATE),
&tx_fsm_val);
+
+       if (err) {
+               dev_err(hba->dev, "%s: unable to get TX_FSM_STATE, err
%d\n",
+                               __func__, err);
+       } else if (tx_fsm_val != TX_FSM_HIBERN8) {
+               err = tx_fsm_val;
+               dev_err(hba->dev, "%s: invalid TX_FSM_STATE = %d\n",
+                               __func__, err);
+       }
+
+       return err;
+}
+
+static int ufs_msm_power_up_sequence(struct ufs_hba *hba)
+{
+       struct ufs_msm_host *host = hba->priv;
+       struct phy *phy = host->generic_phy;
+       int ret = 0;
+       u8 major;
+       u16 minor, step;
+
+       /* Assert PHY reset and apply PHY calibration values */
+       ufs_msm_assert_reset(hba);
+       /* provide 1ms delay to let the reset pulse propagate */
+       usleep_range(1000, 1100);
+
+       ufs_msm_get_controller_revision(hba, &major, &minor, &step);
+       ufs_msm_phy_save_controller_version(phy, major, minor, step);
+       ret = ufs_msm_phy_calibrate_phy(phy);
+       if (ret) {
+               dev_err(hba->dev, "%s: ufs_msm_phy_calibrate_phy() failed,
ret = %d\n",
+                       __func__, ret);
+               goto out;
+       }
+
+       /* De-assert PHY reset and start serdes */
+       ufs_msm_deassert_reset(hba);
+
+       /*
+        * after reset deassertion, phy will need all ref clocks,
+        * voltage, current to settle down before starting serdes.
+        */
+       usleep_range(1000, 1100);
+       if (ufs_msm_phy_start_serdes(phy)) {
+               dev_err(hba->dev, "%s: ufs_msm_phy_start_serdes() failed,
ret = %d\n",
+                       __func__, ret);
+               goto out;
+       }
+
+       ret = ufs_msm_phy_is_pcs_ready(phy);
+       if (ret)
+               dev_err(hba->dev, "%s: is_physical_coding_sublayer_ready()
failed, ret = %d\n",
+                       __func__, ret);
+
+out:
+       return ret;
+}
+
+static int ufs_msm_hce_enable_notify(struct ufs_hba *hba, bool status)
+{
+       struct ufs_msm_host *host = hba->priv;
+       int err = 0;
+
+       switch (status) {
+       case PRE_CHANGE:
+               ufs_msm_power_up_sequence(hba);
+               /*
+                * The PHY PLL output is the source of tx/rx lane symbol
+                * clocks, hence, enable the lane clocks only after PHY
+                * is initialized.
+                */
+               err = ufs_msm_enable_lane_clks(host);
+               break;
+       case POST_CHANGE:
+               /* check if UFS PHY moved from DISABLED to HIBERN8 */
+               err = ufs_msm_check_hibern8(hba);
+               break;
+       default:
+               dev_err(hba->dev, "%s: invalid status %d\n", __func__,
status);
+               err = -EINVAL;
+               break;
+       }
+       return err;
+}
+
+/**
+ * Returns non-zero for success (which rate of core_clk) and 0
+ * in case of a failure
+ */
+static unsigned long
+ufs_msm_cfg_timers(struct ufs_hba *hba, u32 gear, u32 hs, u32 rate)
+{
+       struct ufs_clk_info *clki;
+       u32 core_clk_period_in_ns;
+       u32 tx_clk_cycles_per_us = 0;
+       unsigned long core_clk_rate = 0;
+       u32 core_clk_cycles_per_us = 0;
+
+       static u32 pwm_fr_table[][2] = {
+               {UFS_PWM_G1, 0x1},
+               {UFS_PWM_G2, 0x1},
+               {UFS_PWM_G3, 0x1},
+               {UFS_PWM_G4, 0x1},
+       };
+
+       static u32 hs_fr_table_rA[][2] = {
+               {UFS_HS_G1, 0x1F},
+               {UFS_HS_G2, 0x3e},
+       };
+
+       static u32 hs_fr_table_rB[][2] = {
+               {UFS_HS_G1, 0x24},
+               {UFS_HS_G2, 0x49},
+       };
+
+       if (gear == 0) {
+               dev_err(hba->dev, "%s: invalid gear = %d\n", __func__,
gear);
+               goto out_error;
+       }
+
+       list_for_each_entry(clki, &hba->clk_list_head, list) {
+               if (!strcmp(clki->name, "core_clk"))
+                       core_clk_rate = clk_get_rate(clki->clk);
+       }
+
+       /* If frequency is smaller than 1MHz, set to 1MHz */
+       if (core_clk_rate < DEFAULT_CLK_RATE_HZ)
+               core_clk_rate = DEFAULT_CLK_RATE_HZ;
+
+       core_clk_cycles_per_us = core_clk_rate / USEC_PER_SEC;
+       ufshcd_writel(hba, core_clk_cycles_per_us, REG_UFS_SYS1CLK_1US);
+
+       core_clk_period_in_ns = NSEC_PER_SEC / core_clk_rate;
+       core_clk_period_in_ns <<= OFFSET_CLK_NS_REG;
+       core_clk_period_in_ns &= MASK_CLK_NS_REG;
+
+       switch (hs) {
+       case FASTAUTO_MODE:
+       case FAST_MODE:
+               if (rate == PA_HS_MODE_A) {
+                       if (gear > ARRAY_SIZE(hs_fr_table_rA)) {
+                               dev_err(hba->dev,
+                                       "%s: index %d exceeds table size
%zu\n",
+                                       __func__, gear,
+                                       ARRAY_SIZE(hs_fr_table_rA));
+                               goto out_error;
+                       }
+                       tx_clk_cycles_per_us = hs_fr_table_rA[gear-1][1];
+               } else if (rate == PA_HS_MODE_B) {
+                       if (gear > ARRAY_SIZE(hs_fr_table_rB)) {
+                               dev_err(hba->dev,
+                                       "%s: index %d exceeds table size
%zu\n",
+                                       __func__, gear,
+                                       ARRAY_SIZE(hs_fr_table_rB));
+                               goto out_error;
+                       }
+                       tx_clk_cycles_per_us = hs_fr_table_rB[gear-1][1];
+               } else {
+                       dev_err(hba->dev, "%s: invalid rate = %d\n",
+                               __func__, rate);
+                       goto out_error;
+               }
+               break;
+       case SLOWAUTO_MODE:
+       case SLOW_MODE:
+               if (gear > ARRAY_SIZE(pwm_fr_table)) {
+                       dev_err(hba->dev,
+                                       "%s: index %d exceeds table size
%zu\n",
+                                       __func__, gear,
+                                       ARRAY_SIZE(pwm_fr_table));
+                       goto out_error;
+               }
+               tx_clk_cycles_per_us = pwm_fr_table[gear-1][1];
+               break;
+       case UNCHANGED:
+       default:
+               dev_err(hba->dev, "%s: invalid mode = %d\n", __func__, hs);
+               goto out_error;
+       }
+
+       /* this register 2 fields shall be written at once */
+       ufshcd_writel(hba, core_clk_period_in_ns | tx_clk_cycles_per_us,
+
REG_UFS_TX_SYMBOL_CLK_NS_US);
+       goto out;
+
+out_error:
+       core_clk_rate = 0;
+out:
+       return core_clk_rate;
+}
+
+static int ufs_msm_link_startup_notify(struct ufs_hba *hba, bool status)
+{
+       unsigned long core_clk_rate = 0;
+       u32 core_clk_cycles_per_100ms;
+
+       switch (status) {
+       case PRE_CHANGE:
+               core_clk_rate = ufs_msm_cfg_timers(hba, UFS_PWM_G1,
+                                                  SLOWAUTO_MODE, 0);
+               if (!core_clk_rate) {
+                       dev_err(hba->dev, "%s: ufs_msm_cfg_timers()
failed\n",
+                               __func__);
+                       return -EINVAL;
+               }
+               core_clk_cycles_per_100ms =
+                       (core_clk_rate / MSEC_PER_SEC) * 100;
+               ufshcd_writel(hba, core_clk_cycles_per_100ms,
+                                       REG_UFS_PA_LINK_STARTUP_TIMER);
+               break;
+       case POST_CHANGE:
+               ufs_msm_link_startup_post_change(hba);
+               break;
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+static int ufs_msm_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op)
+{
+       struct ufs_msm_host *host = hba->priv;
+       struct phy *phy = host->generic_phy;
+       int ret = 0;
+
+       if (ufs_msm_is_link_off(hba)) {
+               /*
+                * Disable the tx/rx lane symbol clocks before PHY is
+                * powered down as the PLL source should be disabled
+                * after downstream clocks are disabled.
+                */
+               ufs_msm_disable_lane_clks(host);
+               phy_power_off(phy);
+
+               goto out;
+       }
+
+       /*
+        * If UniPro link is not active, PHY ref_clk, main PHY analog power
+        * rail and low noise analog power rail for PLL can be switched off.
+        */
+       if (!ufs_msm_is_link_active(hba)) {
+               if (ufs_msm_phy_is_cfg_restore_quirk_enabled(phy) &&
+                   ufs_msm_is_link_hibern8(hba)) {
+                       ret = ufs_msm_phy_save_configuration(phy);
+                       if (ret)
+                               dev_err(hba->dev, "%s: failed
ufs_msm_phy_save_configuration %d\n",
+                                       __func__, ret);
+               }
+               phy_power_off(phy);
+       }
+
+out:
+       return ret;
+}
+
+static bool ufs_msm_is_phy_config_restore_required(struct ufs_hba *hba)
+{
+       struct ufs_msm_host *host = hba->priv;
+       struct phy *phy = host->generic_phy;
+
+       return ufs_msm_phy_is_cfg_restore_quirk_enabled(phy)
+               && ufshcd_is_link_hibern8(hba)
+               && hba->is_sys_suspended;
+}
+
+static int ufs_msm_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op)
+{
+       struct ufs_msm_host *host = hba->priv;
+       struct phy *phy = host->generic_phy;
+       int err;
+
+       if (ufs_msm_is_phy_config_restore_required(hba)) {
+               ufs_msm_assert_reset(hba);
+               /* provide 1ms delay to let the reset pulse propagate */
+               usleep_range(1000, 1100);
+       }
+
+       err = phy_power_on(phy);
+       if (err) {
+               dev_err(hba->dev, "%s: failed enabling regs, err = %d\n",
+                       __func__, err);
+               goto out;
+       }
+
+       if (ufs_msm_is_phy_config_restore_required(hba)) {
+               ufs_msm_phy_restore_swi_regs(phy);
+
+               /* De-assert PHY reset and start serdes */
+               ufs_msm_deassert_reset(hba);
+
+               /*
+                * after reset deassertion, phy will need all ref clocks,
+                * voltage, current to settle down before starting serdes.
+                */
+               usleep_range(1000, 1100);
+
+               err = phy_resume(host->generic_phy);
+       }
+
+       hba->is_sys_suspended = false;
+out:
+       return err;
+}
+
+struct ufs_msm_dev_params {
+       u32 pwm_rx_gear;        /* pwm rx gear to work in */
+       u32 pwm_tx_gear;        /* pwm tx gear to work in */
+       u32 hs_rx_gear;         /* hs rx gear to work in */
+       u32 hs_tx_gear;         /* hs tx gear to work in */
+       u32 rx_lanes;           /* number of rx lanes */
+       u32 tx_lanes;           /* number of tx lanes */
+       u32 rx_pwr_pwm;         /* rx pwm working pwr */
+       u32 tx_pwr_pwm;         /* tx pwm working pwr */
+       u32 rx_pwr_hs;          /* rx hs working pwr */
+       u32 tx_pwr_hs;          /* tx hs working pwr */
+       u32 hs_rate;            /* rate A/B to work in HS */
+       u32 desired_working_mode;
+};
+
+/**
+ * as every power mode, according to the UFS spec, have a defined
+ * number that are not corresponed to their order or power
+ * consumption (i.e 5, 2, 4, 1 respectively from low to high),
+ * we need to map them into array, so we can scan it easily
+ * in order to find the minimum required power mode.
+ * also, we can use this routine to go the other way around,
+ * and from array index, the fetch the correspond power mode.
+ */
+static int map_unmap_pwr_mode(u32 mode, bool is_pwr_to_arr)
+{
+       enum {SL_MD = 0, SLA_MD = 1, FS_MD = 2, FSA_MD = 3, UNDEF = 4};
+       int ret = -EINVAL;
+
+       if (is_pwr_to_arr) {
+               switch (mode) {
+               case SLOW_MODE:
+                       ret = SL_MD;
+                       break;
+               case SLOWAUTO_MODE:
+                       ret = SLA_MD;
+                       break;
+               case FAST_MODE:
+                       ret = FS_MD;
+                       break;
+               case FASTAUTO_MODE:
+                       ret = FSA_MD;
+                       break;
+               default:
+                       ret = UNDEF;
+                       break;
+               }
+       } else {
+               switch (mode) {
+               case SL_MD:
+                       ret = SLOW_MODE;
+                       break;
+               case SLA_MD:
+                       ret = SLOWAUTO_MODE;
+                       break;
+               case FS_MD:
+                       ret = FAST_MODE;
+                       break;
+               case FSA_MD:
+                       ret = FASTAUTO_MODE;
+                       break;
+               default:
+                       ret = -EINVAL;
+                       break;
+               }
+       }
+
+       return ret;
+}
+
+#define NUM_OF_SUPPORTED_MODES 5
+static int get_pwr_dev_param(struct ufs_msm_dev_params *msm_param,
+                               struct ufs_pa_layer_attr *dev_max,
+                               struct ufs_pa_layer_attr *dev_req)
+{
+       int arr[NUM_OF_SUPPORTED_MODES] = {0};
+       int i;
+       int min_power;
+       int min_msm_gear;
+       int min_dev_gear;
+       bool is_max_dev_hs;
+       bool is_max_msm_hs;
+
+       /**
+        * mapping the max. supported power mode of the device
+        * and the max. pre-defined support power mode of the vendor
+        * in order to scan them easily
+        */
+       arr[map_unmap_pwr_mode(dev_max->pwr_rx, true)]++;
+       arr[map_unmap_pwr_mode(dev_max->pwr_tx, true)]++;
+
+       if (msm_param->desired_working_mode == SLOW) {
+               arr[map_unmap_pwr_mode(msm_param->rx_pwr_pwm, true)]++;
+               arr[map_unmap_pwr_mode(msm_param->tx_pwr_pwm, true)]++;
+       } else {
+               arr[map_unmap_pwr_mode(msm_param->rx_pwr_hs, true)]++;
+               arr[map_unmap_pwr_mode(msm_param->tx_pwr_hs, true)]++;
+       }
+
+       for (i = 0; i < NUM_OF_SUPPORTED_MODES; ++i) {
+               if (arr[i] != 0)
+                       break;
+       }
+
+       /* no supported power mode found */
+       if (i == NUM_OF_SUPPORTED_MODES) {
+               return -EINVAL;
+       } else {
+               min_power = map_unmap_pwr_mode(i, false);
+               if (min_power >= 0)
+                       dev_req->pwr_rx = dev_req->pwr_tx = min_power;
+               else
+                       return -EINVAL;
+       }
+
+       /**
+        * we would like tx to work in the minimum number of lanes
+        * between device capability and vendor preferences.
+        * the same decision will be made for rx.
+        */
+       dev_req->lane_tx = min_t(u32, dev_max->lane_tx,
msm_param->tx_lanes);
+       dev_req->lane_rx = min_t(u32, dev_max->lane_rx,
msm_param->rx_lanes);
+
+       if (dev_max->pwr_rx == SLOW_MODE ||
+           dev_max->pwr_rx == SLOWAUTO_MODE)
+               is_max_dev_hs = false;
+       else
+               is_max_dev_hs = true;
+
+       /* setting the device maximum gear */
+       min_dev_gear = min_t(u32, dev_max->gear_rx, dev_max->gear_tx);
+
+       /**
+        * setting the desired gear to be the minimum according to the
desired
+        * power mode
+        */
+       if (msm_param->desired_working_mode == SLOW) {
+               is_max_msm_hs = false;
+               min_msm_gear = min_t(u32, msm_param->pwm_rx_gear,
+                                               msm_param->pwm_tx_gear);
+       } else {
+               is_max_msm_hs = true;
+               min_msm_gear = min_t(u32, msm_param->hs_rx_gear,
+                                               msm_param->hs_tx_gear);
+       }
+
+       /**
+        * if both device capabilities and vendor pre-defined preferences
are
+        * both HS or both PWM then set the minimum gear to be the
+        * chosen working gear.
+        * if one is PWM and one is HS then the one that is PWM get to
decide
+        * what the gear, as he is the one that also decided previously what
+        * pwr the device will be configured to.
+        */
+       if ((is_max_dev_hs && is_max_msm_hs) ||
+           (!is_max_dev_hs && !is_max_msm_hs)) {
+               dev_req->gear_rx = dev_req->gear_tx =
+                       min_t(u32, min_dev_gear, min_msm_gear);
+       } else if (!is_max_dev_hs) {
+               dev_req->gear_rx = dev_req->gear_tx = min_dev_gear;
+       } else {
+               dev_req->gear_rx = dev_req->gear_tx = min_msm_gear;
+       }
+
+       dev_req->hs_rate = msm_param->hs_rate;
+
+       return 0;
+}
+
+static int ufs_msm_update_bus_bw_vote(struct ufs_msm_host *host)
+{
+       int vote;
+       int err = 0;
+       char mode[BUS_VECTOR_NAME_LEN];
+
+       err = ufs_msm_get_speed_mode(&host->dev_req_params, mode);
+       if (err)
+               goto out;
+
+       vote = ufs_msm_get_bus_vote(host, mode);
+       if (vote >= 0)
+               err = ufs_msm_set_bus_vote(host, vote);
+       else
+               err = vote;
+
+out:
+       if (err)
+               dev_err(host->hba->dev, "%s: failed %d\n", __func__, err);
+       else
+               host->bus_vote.saved_vote = vote;
+       return err;
+}
+
+static int ufs_msm_pwr_change_notify(struct ufs_hba *hba,
+                                    bool status,
+                                    struct ufs_pa_layer_attr
*dev_max_params,
+                                    struct ufs_pa_layer_attr
*dev_req_params)
+{
+       u32 val;
+       struct ufs_msm_host *host = hba->priv;
+       struct phy *phy = host->generic_phy;
+       struct ufs_msm_dev_params ufs_msm_cap;
+       int ret = 0;
+       int res = 0;
+
+       if (!dev_req_params) {
+               pr_err("%s: incoming dev_req_params is NULL\n", __func__);
+               ret = -EINVAL;
+               goto out;
+       }
+
+       switch (status) {
+       case PRE_CHANGE:
+               ufs_msm_cap.tx_lanes = UFS_MSM_LIMIT_NUM_LANES_TX;
+               ufs_msm_cap.rx_lanes = UFS_MSM_LIMIT_NUM_LANES_RX;
+               ufs_msm_cap.hs_rx_gear = UFS_MSM_LIMIT_HSGEAR_RX;
+               ufs_msm_cap.hs_tx_gear = UFS_MSM_LIMIT_HSGEAR_TX;
+               ufs_msm_cap.pwm_rx_gear = UFS_MSM_LIMIT_PWMGEAR_RX;
+               ufs_msm_cap.pwm_tx_gear = UFS_MSM_LIMIT_PWMGEAR_TX;
+               ufs_msm_cap.rx_pwr_pwm = UFS_MSM_LIMIT_RX_PWR_PWM;
+               ufs_msm_cap.tx_pwr_pwm = UFS_MSM_LIMIT_TX_PWR_PWM;
+               ufs_msm_cap.rx_pwr_hs = UFS_MSM_LIMIT_RX_PWR_HS;
+               ufs_msm_cap.tx_pwr_hs = UFS_MSM_LIMIT_TX_PWR_HS;
+               ufs_msm_cap.hs_rate = UFS_MSM_LIMIT_HS_RATE;
+               ufs_msm_cap.desired_working_mode =
+                                       UFS_MSM_LIMIT_DESIRED_MODE;
+
+               ret = get_pwr_dev_param(&ufs_msm_cap, dev_max_params,
+                                                       dev_req_params);
+               if (ret) {
+                       pr_err("%s: failed to determine capabilities\n",
+                                       __func__);
+                       goto out;
+               }
+
+               break;
+       case POST_CHANGE:
+               if (!ufs_msm_cfg_timers(hba, dev_req_params->gear_rx,
+                                       dev_req_params->pwr_rx,
+                                       dev_req_params->hs_rate)) {
+                       dev_err(hba->dev, "%s: ufs_msm_cfg_timers()
failed\n",
+                               __func__);
+                       /*
+                        * we return error code at the end of the routine,
+                        * but continue to configure UFS_PHY_TX_LANE_ENABLE
+                        * and bus voting as usual
+                        */
+                       ret = -EINVAL;
+               }
+
+               val = ~(MAX_U32 << dev_req_params->lane_tx);
+               res = ufs_msm_phy_set_tx_lane_enable(phy, val);
+               if (res) {
+                       dev_err(hba->dev, "%s:
ufs_msm_phy_set_tx_lane_enable() failed res = %d\n",
+                               __func__, res);
+                       ret = res;
+               }
+
+               /* cache the power mode parameters to use internally */
+               memcpy(&host->dev_req_params,
+                               dev_req_params, sizeof(*dev_req_params));
+               ufs_msm_update_bus_bw_vote(host);
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+out:
+       return ret;
+}
+
+/**
+ * ufs_msm_advertise_quirks - advertise the known MSM UFS controller quirks
+ * @hba: host controller instance
+ *
+ * MSM UFS host controller might have some non standard behaviours (quirks)
+ * than what is specified by UFSHCI specification. Advertise all such
+ * quirks to standard UFS host controller driver so standard takes them
into
+ * account.
+ */
+static void ufs_msm_advertise_quirks(struct ufs_hba *hba)
+{
+       struct ufs_msm_host *host = hba->priv;
+
+       phy_advertise_quirks(host->generic_phy);
+}
+
+static int ufs_msm_get_bus_vote(struct ufs_msm_host *host,
+               const char *speed_mode)
+{
+       struct device *dev = host->hba->dev;
+       struct device_node *np = dev->of_node;
+       int err;
+       const char *key = "qcom,bus-vector-names";
+
+       if (!speed_mode) {
+               err = -EINVAL;
+               goto out;
+       }
+
+       if (host->bus_vote.is_max_bw_needed && !!strcmp(speed_mode, "MIN"))
+               err = of_property_match_string(np, key, "MAX");
+       else
+               err = of_property_match_string(np, key, speed_mode);
+
+out:
+       if (err < 0)
+               dev_err(dev, "%s: Invalid %s mode %d\n",
+                               __func__, speed_mode, err);
+       return err;
+}
+
+static int ufs_msm_set_bus_vote(struct ufs_msm_host *host, int vote)
+{
+       int err = 0;
+
+       if (vote != host->bus_vote.curr_vote) {
+               err = msm_bus_scale_client_update_request(
+                               host->bus_vote.client_handle, vote);
+               if (err) {
+                       dev_err(host->hba->dev,
+                               "%s: msm_bus_scale_client_update_request()
failed: bus_client_handle=0x%x, vote=%d, err=%d\n",
+                               __func__, host->bus_vote.client_handle,
+                               vote, err);
+                       goto out;
+               }
+
+               host->bus_vote.curr_vote = vote;
+       }
+out:
+       return err;
+}
+
+static int ufs_msm_get_speed_mode(struct ufs_pa_layer_attr *p, char
*result)
+{
+       int err = 0;
+       int gear = max_t(u32, p->gear_rx, p->gear_tx);
+       int lanes = max_t(u32, p->lane_rx, p->lane_tx);
+       int pwr = max_t(u32, map_unmap_pwr_mode(p->pwr_rx, true),
+                       map_unmap_pwr_mode(p->pwr_tx, true));
+
+       /* default to PWM Gear 1, Lane 1 if power mode is not initialized */
+       if (!gear)
+               gear = 1;
+
+       if (!lanes)
+               lanes = 1;
+
+       if (!p->pwr_rx && !p->pwr_tx)
+               pwr = 0;
+
+       pwr = map_unmap_pwr_mode(pwr, false);
+       if (pwr < 0) {
+               err = pwr;
+               goto out;
+       }
+
+       if (pwr == FAST_MODE || pwr == FASTAUTO_MODE)
+               snprintf(result, BUS_VECTOR_NAME_LEN, "%s_R%s_G%d_L%d",
"HS",
+                               p->hs_rate == PA_HS_MODE_B ? "B" : "A",
+                               gear, lanes);
+       else
+               snprintf(result, BUS_VECTOR_NAME_LEN, "%s_G%d_L%d",
+                               "PWM", gear, lanes);
+out:
+       return err;
+}
+
+
+
+static int ufs_msm_setup_clocks(struct ufs_hba *hba, bool on)
+{
+       struct ufs_msm_host *host = hba->priv;
+       int err;
+       int vote = 0;
+
+       /*
+        * In case ufs_msm_init() is not yet done, simply ignore.
+        * This ufs_msm_setup_clocks() shall be called from
+        * ufs_msm_init() after init is done.
+        */
+       if (!host)
+               return 0;
+
+       if (on) {
+               err = ufs_msm_phy_enable_iface_clk(host->generic_phy);
+               if (err)
+                       goto out;
+
+               vote = host->bus_vote.saved_vote;
+               if (vote == host->bus_vote.min_bw_vote)
+                       ufs_msm_update_bus_bw_vote(host);
+       } else {
+               /* M-PHY RMMI interface clocks can be turned off */
+               ufs_msm_phy_disable_iface_clk(host->generic_phy);
+               vote = host->bus_vote.min_bw_vote;
+       }
+
+       err = ufs_msm_set_bus_vote(host, vote);
+       if (err)
+               dev_err(hba->dev, "%s: set bus vote failed %d\n",
+                               __func__, err);
+
+out:
+       return err;
+}
+
+static ssize_t
+show_ufs_to_mem_max_bus_bw(struct device *dev, struct device_attribute
*attr,
+                       char *buf)
+{
+       struct ufs_hba *hba = dev_get_drvdata(dev);
+       struct ufs_msm_host *host = hba->priv;
+
+       return snprintf(buf, PAGE_SIZE, "%u\n",
+                       host->bus_vote.is_max_bw_needed);
+}
+
+static ssize_t
+store_ufs_to_mem_max_bus_bw(struct device *dev, struct device_attribute
*attr,
+               const char *buf, size_t count)
+{
+       struct ufs_hba *hba = dev_get_drvdata(dev);
+       struct ufs_msm_host *host = hba->priv;
+       uint32_t value;
+
+       if (!kstrtou32(buf, 0, &value)) {
+               host->bus_vote.is_max_bw_needed = !!value;
+               ufs_msm_update_bus_bw_vote(host);
+       }
+
+       return count;
+}
+
+static int ufs_msm_bus_register(struct ufs_msm_host *host)
+{
+       int err;
+       struct msm_bus_scale_pdata *bus_pdata;
+       struct device *dev = host->hba->dev;
+       struct platform_device *pdev = to_platform_device(dev);
+       struct device_node *np = dev->of_node;
+
+       bus_pdata = msm_bus_cl_get_pdata(pdev);
+       if (!bus_pdata) {
+               dev_err(dev, "%s: failed to get bus vectors\n", __func__);
+               err = -ENODATA;
+               goto out;
+       }
+
+       err = of_property_count_strings(np, "qcom,bus-vector-names");
+       if (err < 0 || err != bus_pdata->num_usecases) {
+               dev_err(dev, "%s: qcom,bus-vector-names not specified
correctly %d\n",
+                               __func__, err);
+               goto out;
+       }
+
+       host->bus_vote.client_handle =
msm_bus_scale_register_client(bus_pdata);
+       if (!host->bus_vote.client_handle) {
+               dev_err(dev, "%s: msm_bus_scale_register_client failed\n",
+                               __func__);
+               err = -EFAULT;
+               goto out;
+       }
+
+       /* cache the vote index for minimum and maximum bandwidth */
+       host->bus_vote.min_bw_vote = ufs_msm_get_bus_vote(host, "MIN");
+       host->bus_vote.max_bw_vote = ufs_msm_get_bus_vote(host, "MAX");
+
+       host->bus_vote.max_bus_bw.show = show_ufs_to_mem_max_bus_bw;
+       host->bus_vote.max_bus_bw.store = store_ufs_to_mem_max_bus_bw;
+       sysfs_attr_init(&host->bus_vote.max_bus_bw.attr);
+       host->bus_vote.max_bus_bw.attr.name = "max_bus_bw";
+       host->bus_vote.max_bus_bw.attr.mode = S_IRUGO | S_IWUSR;
+       err = device_create_file(dev, &host->bus_vote.max_bus_bw);
+out:
+       return err;
+}
+
+#define        ANDROID_BOOT_DEV_MAX    30
+static char android_boot_dev[ANDROID_BOOT_DEV_MAX];
+static int get_android_boot_dev(char *str)
+{
+       strlcpy(android_boot_dev, str, ANDROID_BOOT_DEV_MAX);
+       return 1;
+}
+__setup("androidboot.bootdevice=", get_android_boot_dev);
+
+/**
+ * ufs_msm_init - bind phy with controller
+ * @hba: host controller instance
+ *
+ * Binds PHY with controller and powers up PHY enabling clocks
+ * and regulators.
+ *
+ * Returns -EPROBE_DEFER if binding fails, returns negative error
+ * on phy power up failure and returns zero on success.
+ */
+static int ufs_msm_init(struct ufs_hba *hba)
+{
+       int err;
+       struct device *dev = hba->dev;
+       struct ufs_msm_host *host;
+
+       if (strlen(android_boot_dev) && strcmp(android_boot_dev,
dev_name(dev)))
+               return -ENODEV;
+
+       host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL);
+       if (!host) {
+               err = -ENOMEM;
+               dev_err(dev, "%s: no memory for msm ufs host\n", __func__);
+               goto out;
+       }
+
+       host->hba = hba;
+       host->generic_phy = devm_phy_get(dev, "ufs_msm_phy");
+
+       if (IS_ERR(host->generic_phy)) {
+               err = PTR_ERR(host->generic_phy);
+               dev_err(dev, "PHY get failed %d\n", err);
+               goto out;
+       }
+
+       hba->priv = (void *)host;
+
+       err = ufs_msm_bus_register(host);
+       if (err)
+               goto out_host_free;
+
+       phy_init(host->generic_phy);
+       err = phy_power_on(host->generic_phy);
+       if (err)
+               goto out_unregister_bus;
+
+       err = ufs_msm_init_lane_clks(host);
+       if (err)
+               goto out_disable_phy;
+
+       ufs_msm_advertise_quirks(hba);
+
+       hba->caps |= UFSHCD_CAP_CLK_GATING | UFSHCD_CAP_CLK_SCALING;
+       hba->caps |= UFSHCD_CAP_AUTO_BKOPS_SUSPEND;
+       ufs_msm_setup_clocks(hba, true);
+       goto out;
+
+out_disable_phy:
+       phy_power_off(host->generic_phy);
+out_unregister_bus:
+       phy_exit(host->generic_phy);
+       msm_bus_scale_unregister_client(host->bus_vote.client_handle);
+out_host_free:
+       devm_kfree(dev, host);
+       hba->priv = NULL;
+out:
+       return err;
+}
+
+static void ufs_msm_exit(struct ufs_hba *hba)
+{
+       struct ufs_msm_host *host = hba->priv;
+
+       msm_bus_scale_unregister_client(host->bus_vote.client_handle);
+       ufs_msm_disable_lane_clks(host);
+       phy_power_off(host->generic_phy);
+}
+
+
+void ufs_msm_clk_scale_notify(struct ufs_hba *hba)
+{
+       struct ufs_msm_host *host = hba->priv;
+       struct ufs_pa_layer_attr *dev_req_params = &host->dev_req_params;
+
+       if (!dev_req_params)
+               return;
+
+       ufs_msm_cfg_timers(hba, dev_req_params->gear_rx,
+                               dev_req_params->pwr_rx,
+                               dev_req_params->hs_rate);
+       ufs_msm_update_bus_bw_vote(host);
+}
+/**
+ * struct ufs_hba_msm_vops - UFS MSM specific variant operations
+ *
+ * The variant operations configure the necessary controller and PHY
+ * handshake during initializaiton.
+ */
+const struct ufs_hba_variant_ops ufs_hba_msm_vops = {
+       .name                   = "msm",
+       .init                   = ufs_msm_init,
+       .exit                   = ufs_msm_exit,
+       .clk_scale_notify       = ufs_msm_clk_scale_notify,
+       .setup_clocks           = ufs_msm_setup_clocks,
+       .hce_enable_notify      = ufs_msm_hce_enable_notify,
+       .link_startup_notify    = ufs_msm_link_startup_notify,
+       .pwr_change_notify      = ufs_msm_pwr_change_notify,
+       .suspend                = ufs_msm_suspend,
+       .resume                 = ufs_msm_resume,
+};
+EXPORT_SYMBOL(ufs_hba_msm_vops);
diff --git a/drivers/scsi/ufs/ufs-msm.h b/drivers/scsi/ufs/ufs-msm.h
new file mode 100644
index 0000000..6e93f1e
--- /dev/null
+++ b/drivers/scsi/ufs/ufs-msm.h
@@ -0,0 +1,158 @@
+/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef UFS_MSM_H_
+#define UFS_MSM_H_
+
+#include <linux/phy/phy.h>
+
+#define MAX_U32                 (~(u32)0)
+#define MPHY_TX_FSM_STATE       0x41
+#define TX_FSM_HIBERN8          0x1
+#define HBRN8_POLL_TOUT_MS      100
+#define DEFAULT_CLK_RATE_HZ     1000000
+#define BUS_VECTOR_NAME_LEN     32
+
+#define UFS_HW_VER_MAJOR_SHFT  (28)
+#define UFS_HW_VER_MAJOR_MASK  (0x000F << UFS_HW_VER_MAJOR_SHFT)
+#define UFS_HW_VER_MINOR_SHFT  (16)
+#define UFS_HW_VER_MINOR_MASK  (0x0FFF << UFS_HW_VER_MINOR_SHFT)
+#define UFS_HW_VER_STEP_SHFT   (0)
+#define UFS_HW_VER_STEP_MASK   (0xFFFF << UFS_HW_VER_STEP_SHFT)
+
+/* vendor specific pre-defined parameters */
+#define SLOW 1
+#define FAST 2
+
+#define UFS_MSM_LIMIT_NUM_LANES_RX     2
+#define UFS_MSM_LIMIT_NUM_LANES_TX     2
+#define UFS_MSM_LIMIT_HSGEAR_RX        UFS_HS_G2
+#define UFS_MSM_LIMIT_HSGEAR_TX        UFS_HS_G2
+#define UFS_MSM_LIMIT_PWMGEAR_RX       UFS_PWM_G4
+#define UFS_MSM_LIMIT_PWMGEAR_TX       UFS_PWM_G4
+#define UFS_MSM_LIMIT_RX_PWR_PWM       SLOW_MODE
+#define UFS_MSM_LIMIT_TX_PWR_PWM       SLOW_MODE
+#define UFS_MSM_LIMIT_RX_PWR_HS        FAST_MODE
+#define UFS_MSM_LIMIT_TX_PWR_HS        FAST_MODE
+#define UFS_MSM_LIMIT_HS_RATE          PA_HS_MODE_A
+#define UFS_MSM_LIMIT_DESIRED_MODE     FAST
+
+/* MSM UFS host controller vendor specific registers */
+enum {
+       REG_UFS_SYS1CLK_1US                 = 0xC0,
+       REG_UFS_TX_SYMBOL_CLK_NS_US         = 0xC4,
+       REG_UFS_LOCAL_PORT_ID_REG           = 0xC8,
+       REG_UFS_PA_ERR_CODE                 = 0xCC,
+       REG_UFS_RETRY_TIMER_REG             = 0xD0,
+       REG_UFS_PA_LINK_STARTUP_TIMER       = 0xD8,
+       REG_UFS_CFG1                        = 0xDC,
+       REG_UFS_CFG2                        = 0xE0,
+       REG_UFS_HW_VERSION                  = 0xE4,
+};
+
+/* bit offset */
+enum {
+       OFFSET_UFS_PHY_SOFT_RESET           = 1,
+       OFFSET_CLK_NS_REG                   = 10,
+};
+
+/* bit masks */
+enum {
+       MASK_UFS_PHY_SOFT_RESET             = 0x2,
+       MASK_TX_SYMBOL_CLK_1US_REG          = 0x3FF,
+       MASK_CLK_NS_REG                     = 0xFFFC00,
+};
+
+static LIST_HEAD(phy_list);
+
+enum ufs_msm_phy_init_type {
+       UFS_PHY_INIT_FULL,
+       UFS_PHY_INIT_CFG_RESTORE,
+};
+
+struct ufs_msm_phy_vreg {
+       const char *name;
+       struct regulator *reg;
+       int max_uA;
+       int min_uV;
+       int max_uV;
+       bool enabled;
+};
+
+static inline void
+ufs_msm_get_controller_revision(struct ufs_hba *hba,
+                                u8 *major, u16 *minor, u16 *step)
+{
+       u32 ver = ufshcd_readl(hba, REG_UFS_HW_VERSION);
+
+       *major = (ver & UFS_HW_VER_MAJOR_MASK) >> UFS_HW_VER_MAJOR_SHFT;
+       *minor = (ver & UFS_HW_VER_MINOR_MASK) >> UFS_HW_VER_MINOR_SHFT;
+       *step = (ver & UFS_HW_VER_STEP_MASK) >> UFS_HW_VER_STEP_SHFT;
+};
+
+static inline void ufs_msm_assert_reset(struct ufs_hba *hba)
+{
+       ufshcd_rmwl(hba, MASK_UFS_PHY_SOFT_RESET,
+                       1 << OFFSET_UFS_PHY_SOFT_RESET, REG_UFS_CFG1);
+       mb();
+}
+
+static inline void ufs_msm_deassert_reset(struct ufs_hba *hba)
+{
+       ufshcd_rmwl(hba, MASK_UFS_PHY_SOFT_RESET,
+                       0 << OFFSET_UFS_PHY_SOFT_RESET, REG_UFS_CFG1);
+       mb();
+}
+
+struct ufs_msm_bus_vote {
+       uint32_t client_handle;
+       uint32_t curr_vote;
+       int min_bw_vote;
+       int max_bw_vote;
+       int saved_vote;
+       bool is_max_bw_needed;
+       struct device_attribute max_bus_bw;
+};
+
+struct ufs_msm_host {
+       struct phy *generic_phy;
+       struct ufs_hba *hba;
+       struct ufs_msm_bus_vote bus_vote;
+       struct ufs_pa_layer_attr dev_req_params;
+       struct clk *rx_l0_sync_clk;
+       struct clk *tx_l0_sync_clk;
+       struct clk *rx_l1_sync_clk;
+       struct clk *tx_l1_sync_clk;
+       bool is_lane_clks_enabled;
+};
+
+#define ufs_msm_is_link_off(hba) ufshcd_is_link_off(hba)
+#define ufs_msm_is_link_active(hba) ufshcd_is_link_active(hba)
+#define ufs_msm_is_link_hibern8(hba) ufshcd_is_link_hibern8(hba)
+
+enum {
+       MASK_SERDES_START       = 0x1,
+       MASK_PCS_READY          = 0x1,
+};
+
+enum {
+       OFFSET_SERDES_START     = 0x0,
+};
+
+#define MAX_PROP_NAME              32
+#define VDDA_PHY_MIN_UV            1000000
+#define VDDA_PHY_MAX_UV            1000000
+#define VDDA_PLL_MIN_UV            1800000
+#define VDDA_PLL_MAX_UV            1800000
+
+#endif /* UFS_MSM_H_ */
-- 
1.8.5.2
-- 
QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member
of Code Aurora Forum, hosted by The Linux Foundation
--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to