summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorSubhash Jadavani <subhashj@codeaurora.org>2015-01-21 17:17:21 -0800
committerDavid Keitel <dkeitel@codeaurora.org>2016-03-22 10:58:39 -0700
commite77d1e1a1dd2421fad02ddc7cac054bd53f0461e (patch)
treeff54690813e0bb3aba2b479ced518b9955ee55cb /drivers
parentaa161ba54d4d002c0ca5f44fadfd7c090c00a670 (diff)
phy: ufs-qcom: disable RX LineCfg
Some UFS devices send incorrect LineCfg data as part of power mode change sequence which may cause host PHY to go into bad state. Currently we workaround this issue by disabling the device's TX LCC but disabling TX LCC is much more complicated if both host and device supports UniPro 1.6 specification. To simplify the workaround, this change disables the host PHY's RX LineCfg to skip processing incorrect LineCfg from device. Change-Id: I1eac56c11dd001eb0c53ba8e16aa512a656ab9ea Signed-off-by: Subhash Jadavani <subhashj@codeaurora.org> [venkatg@codeaurora.org: resolved trivial merge conflicts] Signed-off-by: Venkat Gopalakrishnan <venkatg@codeaurora.org>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/phy/phy-qcom-ufs-i.h3
-rw-r--r--drivers/phy/phy-qcom-ufs-qmp-14nm.c18
-rw-r--r--drivers/phy/phy-qcom-ufs-qmp-14nm.h4
-rw-r--r--drivers/phy/phy-qcom-ufs-qmp-20nm.c20
-rw-r--r--drivers/phy/phy-qcom-ufs-qmp-20nm.h5
-rw-r--r--drivers/phy/phy-qcom-ufs.c16
-rw-r--r--drivers/scsi/ufs/ufs-qcom.c21
7 files changed, 82 insertions, 5 deletions
diff --git a/drivers/phy/phy-qcom-ufs-i.h b/drivers/phy/phy-qcom-ufs-i.h
index 2bd5ce43a724..f0ed60f3bc9e 100644
--- a/drivers/phy/phy-qcom-ufs-i.h
+++ b/drivers/phy/phy-qcom-ufs-i.h
@@ -127,6 +127,8 @@ struct ufs_qcom_phy {
* @is_physical_coding_sublayer_ready: pointer to a function that
* checks pcs readiness. returns 0 for success and non-zero for error.
* @set_tx_lane_enable: pointer to a function that enable tx lanes
+ * @ctrl_rx_linecfg: pointer to a function that controls the Host Rx LineCfg
+ * state.
* @power_control: pointer to a function that controls analog rail of phy
* and writes to QSERDES_RX_SIGDET_CNTRL attribute
*/
@@ -135,6 +137,7 @@ struct ufs_qcom_phy_specific_ops {
void (*start_serdes)(struct ufs_qcom_phy *phy);
int (*is_physical_coding_sublayer_ready)(struct ufs_qcom_phy *phy);
void (*set_tx_lane_enable)(struct ufs_qcom_phy *phy, u32 val);
+ void (*ctrl_rx_linecfg)(struct ufs_qcom_phy *phy, bool ctrl);
void (*power_control)(struct ufs_qcom_phy *phy, bool val);
};
diff --git a/drivers/phy/phy-qcom-ufs-qmp-14nm.c b/drivers/phy/phy-qcom-ufs-qmp-14nm.c
index 56631e77c11d..79756ec074c5 100644
--- a/drivers/phy/phy-qcom-ufs-qmp-14nm.c
+++ b/drivers/phy/phy-qcom-ufs-qmp-14nm.c
@@ -90,6 +90,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;
@@ -128,6 +145,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,
};
diff --git a/drivers/phy/phy-qcom-ufs-qmp-14nm.h b/drivers/phy/phy-qcom-ufs-qmp-14nm.h
index 3aefdbacbcd0..e3e2f3a10b92 100644
--- a/drivers/phy/phy-qcom-ufs-qmp-14nm.h
+++ b/drivers/phy/phy-qcom-ufs-qmp-14nm.h
@@ -74,6 +74,8 @@
/* UFS PHY registers */
#define UFS_PHY_PHY_START PHY_OFF(0x00)
#define UFS_PHY_POWER_DOWN_CONTROL PHY_OFF(0x04)
+#define UFS_PHY_LINECFG_DISABLE PHY_OFF(0x138)
+#define UFS_PHY_RX_PWM_GEAR_BAND PHY_OFF(0x154)
#define UFS_PHY_PCS_READY_STATUS PHY_OFF(0x168)
/* UFS PHY TX registers */
@@ -93,6 +95,8 @@
#define QSERDES_RX_SIGDET_DEGLITCH_CNTRL RX_OFF(0, 0x11C)
#define QSERDES_RX_RX_INTERFACE_MODE RX_OFF(0, 0x12C)
+#define UFS_PHY_RX_LINECFG_DISABLE_BIT BIT(1)
+
/*
* This structure represents the 14nm specific phy.
* common_cfg MUST remain the first field in this structure
diff --git a/drivers/phy/phy-qcom-ufs-qmp-20nm.c b/drivers/phy/phy-qcom-ufs-qmp-20nm.c
index f3e0774a63e3..65612c5187d3 100644
--- a/drivers/phy/phy-qcom-ufs-qmp-20nm.c
+++ b/drivers/phy/phy-qcom-ufs-qmp-20nm.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013-2014, Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2015, 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
@@ -133,6 +133,23 @@ void ufs_qcom_phy_qmp_20nm_set_tx_lane_enable(struct ufs_qcom_phy *phy, u32 val)
mb();
}
+static
+void ufs_qcom_phy_qmp_20nm_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_20nm_start_serdes(struct ufs_qcom_phy *phy)
{
u32 tmp;
@@ -170,6 +187,7 @@ struct ufs_qcom_phy_specific_ops phy_20nm_ops = {
.start_serdes = ufs_qcom_phy_qmp_20nm_start_serdes,
.is_physical_coding_sublayer_ready = ufs_qcom_phy_qmp_20nm_is_pcs_ready,
.set_tx_lane_enable = ufs_qcom_phy_qmp_20nm_set_tx_lane_enable,
+ .ctrl_rx_linecfg = ufs_qcom_phy_qmp_20nm_ctrl_rx_linecfg,
.power_control = ufs_qcom_phy_qmp_20nm_power_control,
};
diff --git a/drivers/phy/phy-qcom-ufs-qmp-20nm.h b/drivers/phy/phy-qcom-ufs-qmp-20nm.h
index 3e1f471643f0..bd33c88c2435 100644
--- a/drivers/phy/phy-qcom-ufs-qmp-20nm.h
+++ b/drivers/phy/phy-qcom-ufs-qmp-20nm.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013-2014, Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2015, 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
@@ -98,6 +98,8 @@
#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_LINECFG_DISABLE PHY_OFF(0x134)
+#define UFS_PHY_RX_SIGDET_CTRL3 PHY_OFF(0x14c)
#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)
@@ -114,6 +116,7 @@
#define UFS_PHY_PCS_READY_STATUS PHY_OFF(0x174)
#define UFS_PHY_TX_LANE_ENABLE_MASK 0x3
+#define UFS_PHY_RX_LINECFG_DISABLE_BIT BIT(1)
/*
* This structure represents the 20nm specific phy.
diff --git a/drivers/phy/phy-qcom-ufs.c b/drivers/phy/phy-qcom-ufs.c
index 1e6d1835f44d..8e93bf22fe68 100644
--- a/drivers/phy/phy-qcom-ufs.c
+++ b/drivers/phy/phy-qcom-ufs.c
@@ -517,6 +517,22 @@ int ufs_qcom_phy_set_tx_lane_enable(struct phy *generic_phy, u32 tx_lanes)
return ret;
}
+int ufs_qcom_phy_ctrl_rx_linecfg(struct phy *generic_phy, bool ctrl)
+{
+ struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy);
+ int ret = 0;
+
+ if (!ufs_qcom_phy->phy_spec_ops->ctrl_rx_linecfg) {
+ dev_err(ufs_qcom_phy->dev, "%s: ctrl_rx_linecfg() callback is not supported\n",
+ __func__);
+ ret = -ENOTSUPP;
+ } else {
+ ufs_qcom_phy->phy_spec_ops->ctrl_rx_linecfg(ufs_qcom_phy, ctrl);
+ }
+
+ return ret;
+}
+
void ufs_qcom_phy_save_controller_version(struct phy *generic_phy,
u8 major, u16 minor, u16 step)
{
diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c
index adf9b02e0765..f53aba35b57b 100644
--- a/drivers/scsi/ufs/ufs-qcom.c
+++ b/drivers/scsi/ufs/ufs-qcom.c
@@ -221,6 +221,13 @@ static int ufs_qcom_link_startup_post_change(struct ufs_hba *hba)
dev_err(hba->dev, "%s: ufs_qcom_phy_set_tx_lane_enable failed\n",
__func__);
+ /*
+ * Some UFS devices send incorrect LineCfg data as part of power mode
+ * change sequence which may cause host PHY to go into bad state.
+ * Disabling Rx LineCfg of host PHY should help avoid this.
+ */
+ err = ufs_qcom_phy_ctrl_rx_linecfg(phy, false);
+
out:
return err;
}
@@ -523,14 +530,22 @@ out:
static int ufs_qcom_link_startup_notify(struct ufs_hba *hba, bool status)
{
+ int err = 0;
+ struct ufs_qcom_host *host = hba->priv;
+ struct phy *phy = host->generic_phy;
+
switch (status) {
case PRE_CHANGE:
if (ufs_qcom_cfg_timers(hba, UFS_PWM_G1, SLOWAUTO_MODE,
0, true)) {
dev_err(hba->dev, "%s: ufs_qcom_cfg_timers() failed\n",
__func__);
- return -EINVAL;
+ err = -EINVAL;
+ goto out;
}
+
+ /* make sure RX LineCfg is enabled before link startup */
+ err = ufs_qcom_phy_ctrl_rx_linecfg(phy, true);
break;
case POST_CHANGE:
ufs_qcom_link_startup_post_change(hba);
@@ -539,7 +554,8 @@ static int ufs_qcom_link_startup_notify(struct ufs_hba *hba, bool status)
break;
}
- return 0;
+out:
+ return err;
}
static int ufs_qcom_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op)
@@ -958,7 +974,6 @@ static void ufs_qcom_advertise_quirks(struct ufs_hba *hba)
if (host->hw_ver.major == 0x1) {
hba->quirks |= (UFSHCD_QUIRK_DELAY_BEFORE_DME_CMDS
| UFSHCD_QUIRK_BROKEN_PA_RXHSUNTERMCAP
- | UFSHCD_QUIRK_BROKEN_LCC
| UFSHCD_QUIRK_DME_PEER_ACCESS_AUTO_MODE);
if (host->hw_ver.minor == 0x001 && host->hw_ver.step == 0x0001)