summaryrefslogtreecommitdiff
path: root/drivers/phy/phy-qcom-ufs-qmp-14nm.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/phy/phy-qcom-ufs-qmp-14nm.c')
-rw-r--r--drivers/phy/phy-qcom-ufs-qmp-14nm.c146
1 files changed, 129 insertions, 17 deletions
diff --git a/drivers/phy/phy-qcom-ufs-qmp-14nm.c b/drivers/phy/phy-qcom-ufs-qmp-14nm.c
index 56631e77c11d..8cb8c02f4cb8 100644
--- a/drivers/phy/phy-qcom-ufs-qmp-14nm.c
+++ b/drivers/phy/phy-qcom-ufs-qmp-14nm.c
@@ -15,19 +15,49 @@
#include "phy-qcom-ufs-qmp-14nm.h"
#define UFS_PHY_NAME "ufs_phy_qmp_14nm"
-#define UFS_PHY_VDDA_PHY_UV (925000)
static
int ufs_qcom_phy_qmp_14nm_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy,
bool is_rate_B)
{
- int tbl_size_A = ARRAY_SIZE(phy_cal_table_rate_A);
- int tbl_size_B = ARRAY_SIZE(phy_cal_table_rate_B);
int err;
+ int tbl_size_A, tbl_size_B;
+ struct ufs_qcom_phy_calibration *tbl_A, *tbl_B;
+ u8 major = ufs_qcom_phy->host_ctrl_rev_major;
+ u16 minor = ufs_qcom_phy->host_ctrl_rev_minor;
+ u16 step = ufs_qcom_phy->host_ctrl_rev_step;
+
+ tbl_size_B = ARRAY_SIZE(phy_cal_table_rate_B);
+ tbl_B = phy_cal_table_rate_B;
+
+ if ((major == 0x2) && (minor == 0x000) && (step == 0x0000)) {
+ tbl_A = phy_cal_table_rate_A_2_0_0;
+ tbl_size_A = ARRAY_SIZE(phy_cal_table_rate_A_2_0_0);
+ } else if ((major == 0x2) && (minor == 0x001) && (step == 0x0000)) {
+ tbl_A = phy_cal_table_rate_A_2_1_0;
+ tbl_size_A = ARRAY_SIZE(phy_cal_table_rate_A_2_1_0);
+ } else if ((major == 0x2) && (minor == 0x002) && (step == 0x0000)) {
+ tbl_A = phy_cal_table_rate_A_2_2_0;
+ tbl_size_A = ARRAY_SIZE(phy_cal_table_rate_A_2_2_0);
+ tbl_B = phy_cal_table_rate_B_2_2_0;
+ tbl_size_B = ARRAY_SIZE(phy_cal_table_rate_B_2_2_0);
+ } else {
+ dev_err(ufs_qcom_phy->dev,
+ "%s: Unknown UFS-PHY version (major 0x%x minor 0x%x step 0x%x), no calibration values\n",
+ __func__, major, minor, step);
+ err = -ENODEV;
+ goto out;
+ }
- err = ufs_qcom_phy_calibrate(ufs_qcom_phy, phy_cal_table_rate_A,
- tbl_size_A, phy_cal_table_rate_B, tbl_size_B, is_rate_B);
+ err = ufs_qcom_phy_calibrate(ufs_qcom_phy,
+ tbl_A, tbl_size_A,
+ tbl_B, tbl_size_B,
+ is_rate_B);
+ if (ufs_qcom_phy->quirks & UFS_QCOM_PHY_QUIRK_VCO_MANUAL_TUNING)
+ writel_relaxed(ufs_qcom_phy->vco_tune1_mode1,
+ ufs_qcom_phy->mmio + QSERDES_COM_VCO_TUNE1_MODE1);
+out:
if (err)
dev_err(ufs_qcom_phy->dev,
"%s: ufs_qcom_phy_calibrate() failed %d\n",
@@ -38,8 +68,15 @@ int ufs_qcom_phy_qmp_14nm_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy,
static
void ufs_qcom_phy_qmp_14nm_advertise_quirks(struct ufs_qcom_phy *phy_common)
{
- phy_common->quirks =
- UFS_QCOM_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE;
+ u8 major = phy_common->host_ctrl_rev_major;
+ u16 minor = phy_common->host_ctrl_rev_minor;
+ u16 step = phy_common->host_ctrl_rev_step;
+
+ if ((major == 0x2) && (minor == 0x000) && (step == 0x0000))
+ phy_common->quirks =
+ UFS_QCOM_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE |
+ UFS_QCOM_PHY_QUIRK_SVS_MODE |
+ UFS_QCOM_PHY_QUIRK_VCO_MANUAL_TUNING;
}
static int ufs_qcom_phy_qmp_14nm_init(struct phy *generic_phy)
@@ -61,24 +98,66 @@ static int ufs_qcom_phy_qmp_14nm_init(struct phy *generic_phy)
__func__, err);
goto out;
}
- phy_common->vdda_phy.max_uV = UFS_PHY_VDDA_PHY_UV;
- phy_common->vdda_phy.min_uV = UFS_PHY_VDDA_PHY_UV;
ufs_qcom_phy_qmp_14nm_advertise_quirks(phy_common);
+ if (phy_common->quirks & UFS_QCOM_PHY_QUIRK_VCO_MANUAL_TUNING) {
+ phy_common->vco_tune1_mode1 = readl_relaxed(phy_common->mmio +
+ QSERDES_COM_VCO_TUNE1_MODE1);
+ dev_info(phy_common->dev, "%s: vco_tune1_mode1 0x%x\n",
+ __func__, phy_common->vco_tune1_mode1);
+ }
+
out:
return err;
}
static
-void ufs_qcom_phy_qmp_14nm_power_control(struct ufs_qcom_phy *phy, bool val)
+void ufs_qcom_phy_qmp_14nm_power_control(struct ufs_qcom_phy *phy,
+ bool power_ctrl)
{
- writel_relaxed(val ? 0x1 : 0x0, phy->mmio + UFS_PHY_POWER_DOWN_CONTROL);
- /*
- * Before any transactions involving PHY, ensure PHY knows
- * that it's analog rail is powered ON (or OFF).
- */
- mb();
+ bool is_workaround_req = false;
+
+ if (phy->quirks &
+ UFS_QCOM_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE)
+ is_workaround_req = true;
+
+ if (!power_ctrl) {
+ /* apply PHY analog power collapse */
+ if (is_workaround_req) {
+ /* assert common reset before analog power collapse */
+ writel_relaxed(0x1, phy->mmio + QSERDES_COM_SW_RESET);
+ /*
+ * make sure that reset is propogated before analog
+ * power collapse
+ */
+ mb();
+ }
+ /* apply analog power collapse */
+ writel_relaxed(0x0, phy->mmio + UFS_PHY_POWER_DOWN_CONTROL);
+ /*
+ * Make sure that PHY knows its analog rail is going to be
+ * powered OFF.
+ */
+ mb();
+ } else {
+ /* bring PHY out of analog power collapse */
+ 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 (is_workaround_req) {
+ /*
+ * de-assert common reset after coming out of analog
+ * power collapse
+ */
+ writel_relaxed(0x0, phy->mmio + QSERDES_COM_SW_RESET);
+ /* make common reset is de-asserted before proceeding */
+ mb();
+ }
+ }
}
static inline
@@ -90,6 +169,23 @@ void ufs_qcom_phy_qmp_14nm_set_tx_lane_enable(struct ufs_qcom_phy *phy, u32 val)
*/
}
+static
+void ufs_qcom_phy_qmp_14nm_ctrl_rx_linecfg(struct ufs_qcom_phy *phy, bool ctrl)
+{
+ u32 temp;
+
+ temp = readl_relaxed(phy->mmio + UFS_PHY_LINECFG_DISABLE);
+
+ if (ctrl) /* enable RX LineCfg */
+ temp &= ~UFS_PHY_RX_LINECFG_DISABLE_BIT;
+ else /* disable RX LineCfg */
+ temp |= UFS_PHY_RX_LINECFG_DISABLE_BIT;
+
+ writel_relaxed(temp, phy->mmio + UFS_PHY_LINECFG_DISABLE);
+ /* make sure that RX LineCfg config applied before we return */
+ mb();
+}
+
static inline void ufs_qcom_phy_qmp_14nm_start_serdes(struct ufs_qcom_phy *phy)
{
u32 tmp;
@@ -109,9 +205,24 @@ static int ufs_qcom_phy_qmp_14nm_is_pcs_ready(struct ufs_qcom_phy *phy_common)
err = readl_poll_timeout(phy_common->mmio + UFS_PHY_PCS_READY_STATUS,
val, (val & MASK_PCS_READY), 10, 1000000);
- if (err)
+ if (err) {
dev_err(phy_common->dev, "%s: poll for pcs failed err = %d\n",
__func__, err);
+ goto out;
+ }
+
+ if (phy_common->quirks & UFS_QCOM_PHY_QUIRK_SVS_MODE) {
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(phy_svs_mode_config_2_0_0); i++)
+ writel_relaxed(phy_svs_mode_config_2_0_0[i].cfg_value,
+ (phy_common->mmio +
+ phy_svs_mode_config_2_0_0[i].reg_offset));
+ /* apply above configuration immediately */
+ mb();
+ }
+
+out:
return err;
}
@@ -128,6 +239,7 @@ static struct ufs_qcom_phy_specific_ops phy_14nm_ops = {
.start_serdes = ufs_qcom_phy_qmp_14nm_start_serdes,
.is_physical_coding_sublayer_ready = ufs_qcom_phy_qmp_14nm_is_pcs_ready,
.set_tx_lane_enable = ufs_qcom_phy_qmp_14nm_set_tx_lane_enable,
+ .ctrl_rx_linecfg = ufs_qcom_phy_qmp_14nm_ctrl_rx_linecfg,
.power_control = ufs_qcom_phy_qmp_14nm_power_control,
};