summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGilad Broner <gbroner@codeaurora.org>2014-12-21 10:48:42 +0200
committerDavid Keitel <dkeitel@codeaurora.org>2016-03-22 10:58:26 -0700
commit5cdbfe11ca75af09b97123498075b56a7fa02b8d (patch)
treefbd8d7c3d3806f5db17665296553d38a8f06128e
parentf6786d1fec861506b3e3339cd4f6066c75cdb203 (diff)
scsi: ufs-qcom: set device ref. clk bit
As of HW major version 2, a new bit 'UFS_DEV_REF_CLK_EN' was added to UFS_CFG1 register which needs to be set as part of hibernate enter/exit sequences during suspend/resume. Change-Id: I66a6a75dc5a1cf130b1cee90ae20f9f950edfb3a Signed-off-by: Gilad Broner <gbroner@codeaurora.org> [imaund@codeaurora.org: Resolved context conflicts and updated a conditional in ufs_qcom_advertise_quirks to use the ufs_qcom_host struct when querying major hw versions] Signed-off-by: Ian Maund <imaund@codeaurora.org> [subhashj@codeaurora.org: resolved trivial merge conflicts] Signed-off-by: Subhash Jadavani <subhashj@codeaurora.org> [venkatg@codeaurora.org: resolved trivial merge conflicts, drop changes to include/linux/phy/phy-qcom-ufs.h] Signed-off-by: Venkat Gopalakrishnan <venkatg@codeaurora.org>
-rw-r--r--drivers/phy/phy-qcom-ufs.c33
-rw-r--r--drivers/scsi/ufs/ufs-qcom.c83
-rw-r--r--drivers/scsi/ufs/ufshcd.c1
-rw-r--r--include/linux/scsi/ufs/ufs-qcom.h10
-rw-r--r--include/linux/scsi/ufs/ufshcd.h9
5 files changed, 92 insertions, 44 deletions
diff --git a/drivers/phy/phy-qcom-ufs.c b/drivers/phy/phy-qcom-ufs.c
index 2a053c3cd30b..1e6d1835f44d 100644
--- a/drivers/phy/phy-qcom-ufs.c
+++ b/drivers/phy/phy-qcom-ufs.c
@@ -144,7 +144,6 @@ int ufs_qcom_phy_base_init(struct platform_device *pdev,
phy_common->mmio = NULL;
dev_err(dev, "%s: ioremap for phy_mem resource failed %d\n",
__func__, err);
- goto out;
}
/* "dev_ref_clk_ctrl_mem" is optional resource */
@@ -445,38 +444,6 @@ void ufs_qcom_phy_disable_ref_clk(struct phy *generic_phy)
}
}
-#define UFS_REF_CLK_EN (1 << 5)
-
-static void ufs_qcom_phy_dev_ref_clk_ctrl(struct phy *generic_phy, bool enable)
-{
- struct ufs_qcom_phy *phy = get_ufs_qcom_phy(generic_phy);
-
- if (phy->dev_ref_clk_ctrl_mmio &&
- (enable ^ phy->is_dev_ref_clk_enabled)) {
- u32 temp = readl_relaxed(phy->dev_ref_clk_ctrl_mmio);
-
- if (enable)
- temp |= UFS_REF_CLK_EN;
- else
- temp &= ~UFS_REF_CLK_EN;
-
- writel_relaxed(temp, phy->dev_ref_clk_ctrl_mmio);
- /* ensure that ref_clk is enabled/disabled before we return */
- wmb();
- phy->is_dev_ref_clk_enabled = enable;
- }
-}
-
-void ufs_qcom_phy_enable_dev_ref_clk(struct phy *generic_phy)
-{
- ufs_qcom_phy_dev_ref_clk_ctrl(generic_phy, true);
-}
-
-void ufs_qcom_phy_disable_dev_ref_clk(struct phy *generic_phy)
-{
- ufs_qcom_phy_dev_ref_clk_ctrl(generic_phy, false);
-}
-
/* Turn ON M-PHY RMMI interface clocks */
int ufs_qcom_phy_enable_iface_clk(struct phy *generic_phy)
{
diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c
index 9cbbc51793f2..1d47641a02f7 100644
--- a/drivers/scsi/ufs/ufs-qcom.c
+++ b/drivers/scsi/ufs/ufs-qcom.c
@@ -774,6 +774,44 @@ static int ufs_qcom_update_bus_bw_vote(struct ufs_qcom_host *host)
return err;
}
+#define UFS_REF_CLK_EN (1 << 5)
+static void ufs_qcom_enable_dev_ref_clk(struct ufs_qcom_host *host, bool enable)
+{
+ if (host->dev_ref_clk_ctrl_mmio &&
+ (enable ^ host->is_dev_ref_clk_enabled)) {
+ u32 temp = readl_relaxed(host->dev_ref_clk_ctrl_mmio);
+
+ if (enable)
+ temp |= UFS_REF_CLK_EN;
+ else
+ temp &= ~UFS_REF_CLK_EN;
+
+ /*
+ * If we are here to disable this clock it might be immediately
+ * after entering into hibern8 in which case we need to make
+ * sure that device ref_clk is active at least 1us after the
+ * hibern8 enter.
+ */
+ if (!enable)
+ udelay(1);
+
+ writel_relaxed(temp, host->dev_ref_clk_ctrl_mmio);
+
+ /* ensure that ref_clk is enabled/disabled before we return */
+ wmb();
+
+ /*
+ * If we call hibern8 exit after this, we need to make sure that
+ * device ref_clk is stable for at least 1us before the hibern8
+ * exit command.
+ */
+ if (enable)
+ udelay(1);
+
+ host->is_dev_ref_clk_enabled = enable;
+ }
+}
+
static int ufs_qcom_pwr_change_notify(struct ufs_hba *hba,
bool status,
struct ufs_pa_layer_attr *dev_max_params,
@@ -817,6 +855,10 @@ static int ufs_qcom_pwr_change_notify(struct ufs_hba *hba,
goto out;
}
+ /* enable the device ref clock before changing to HS mode */
+ if (!ufshcd_is_hs_mode(&hba->pwr_info) &&
+ ufshcd_is_hs_mode(dev_req_params))
+ ufs_qcom_enable_dev_ref_clk(host, true);
break;
case POST_CHANGE:
if (ufs_qcom_cfg_timers(hba, dev_req_params->gear_rx,
@@ -844,6 +886,11 @@ static int ufs_qcom_pwr_change_notify(struct ufs_hba *hba,
memcpy(&host->dev_req_params,
dev_req_params, sizeof(*dev_req_params));
ufs_qcom_update_bus_bw_vote(host);
+
+ /* disable the device ref clock if entered PWM mode */
+ if (ufshcd_is_hs_mode(&hba->pwr_info) &&
+ !ufshcd_is_hs_mode(dev_req_params))
+ ufs_qcom_enable_dev_ref_clk(host, false);
break;
default:
ret = -EINVAL;
@@ -864,23 +911,19 @@ out:
*/
static void ufs_qcom_advertise_quirks(struct ufs_hba *hba)
{
- u8 major;
- u16 minor, step;
struct ufs_qcom_host *host = hba->priv;
- ufs_qcom_get_controller_revision(hba, &major, &minor, &step);
-
- if (major == 0x1) {
+ 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 ((minor == 0x001) && (step == 0x0001))
+ if (host->hw_ver.minor == 0x001 && host->hw_ver.step == 0x0001)
hba->quirks |= UFSHCD_QUIRK_BROKEN_INTR_AGGR;
}
- if (major >= 0x2) {
+ if (host->hw_ver.major >= 0x2) {
hba->quirks |= UFSHCD_QUIRK_BROKEN_LCC;
if (!ufs_qcom_cap_qunipro(host))
@@ -1003,8 +1046,9 @@ static int ufs_qcom_setup_clocks(struct ufs_hba *hba, bool on)
ufs_qcom_phy_disable_iface_clk(host->generic_phy);
goto out;
}
- /* enable the device ref clock */
- ufs_qcom_phy_enable_dev_ref_clk(host->generic_phy);
+ /* enable the device ref clock for HS mode*/
+ if (ufshcd_is_hs_mode(&hba->pwr_info))
+ ufs_qcom_enable_dev_ref_clk(host, true);
vote = host->bus_vote.saved_vote;
if (vote == host->bus_vote.min_bw_vote)
ufs_qcom_update_bus_bw_vote(host);
@@ -1015,7 +1059,7 @@ static int ufs_qcom_setup_clocks(struct ufs_hba *hba, bool on)
/* turn off UFS local PHY ref_clk */
ufs_qcom_phy_disable_ref_clk(host->generic_phy);
/* disable device ref_clk */
- ufs_qcom_phy_disable_dev_ref_clk(host->generic_phy);
+ ufs_qcom_enable_dev_ref_clk(host, false);
}
vote = host->bus_vote.min_bw_vote;
}
@@ -1123,9 +1167,11 @@ static int ufs_qcom_init(struct ufs_hba *hba)
{
int err;
struct device *dev = hba->dev;
+ struct platform_device *pdev = to_platform_device(dev);
struct ufs_qcom_host *host;
u8 major;
u16 minor, step;
+ struct resource *res;
if (strlen(android_boot_dev) && strcmp(android_boot_dev, dev_name(dev)))
return -ENODEV;
@@ -1194,6 +1240,8 @@ static int ufs_qcom_init(struct ufs_hba *hba)
goto out_disable_phy;
ufs_qcom_set_caps(hba);
+ ufs_qcom_get_controller_revision(hba, &host->hw_ver.major,
+ &host->hw_ver.minor, &host->hw_ver.step);
ufs_qcom_advertise_quirks(hba);
hba->caps |= UFSHCD_CAP_CLK_GATING |
@@ -1202,6 +1250,21 @@ static int ufs_qcom_init(struct ufs_hba *hba)
hba->caps |= UFSHCD_CAP_HIBERN8_ENTER_ON_IDLE;
ufs_qcom_setup_clocks(hba, true);
+ /* "dev_ref_clk_ctrl_mem" is optional resource */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!res) {
+ dev_dbg(dev, "%s: dev_ref_clk_ctrl_mem resource not found\n",
+ __func__);
+ } else {
+ host->dev_ref_clk_ctrl_mmio = devm_ioremap_resource(dev, res);
+ if (IS_ERR(host->dev_ref_clk_ctrl_mmio)) {
+ dev_warn(dev,
+ "%s: could not map dev_ref_clk_ctrl_mmio, err %ld\n",
+ __func__, PTR_ERR(host->dev_ref_clk_ctrl_mmio));
+ host->dev_ref_clk_ctrl_mmio = NULL;
+ }
+ }
+
if (hba->dev->id < MAX_UFS_QCOM_HOSTS)
ufs_qcom_hosts[hba->dev->id] = host;
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 6945e41e864d..40ebaf2e25e9 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -43,7 +43,6 @@
#include <linux/nls.h>
#include <linux/of.h>
#include <linux/scsi/ufs/ufshcd.h>
-#include <linux/scsi/ufs/unipro.h>
#include "ufshci.h"
#include "ufs_quirks.h"
#include "debugfs.h"
diff --git a/include/linux/scsi/ufs/ufs-qcom.h b/include/linux/scsi/ufs/ufs-qcom.h
index 047d38987f85..dee92b9ee54f 100644
--- a/include/linux/scsi/ufs/ufs-qcom.h
+++ b/include/linux/scsi/ufs/ufs-qcom.h
@@ -182,6 +182,13 @@ struct ufs_qcom_ice_data {
bool crypto_engine_err;
};
+/* Host controller hardware version: major.minor.step */
+struct ufs_hw_version {
+ u16 step;
+ u16 minor;
+ u8 major;
+};
+
struct ufs_qcom_host {
/*
* Set this capability if host controller supports the QUniPro mode
@@ -203,6 +210,9 @@ struct ufs_qcom_host {
bool is_lane_clks_enabled;
bool sec_cfg_updated;
struct ufs_qcom_ice_data ice;
+ void __iomem *dev_ref_clk_ctrl_mmio;
+ bool is_dev_ref_clk_enabled;
+ struct ufs_hw_version hw_ver;
};
#define ufs_qcom_is_link_off(hba) ufshcd_is_link_off(hba)
diff --git a/include/linux/scsi/ufs/ufshcd.h b/include/linux/scsi/ufs/ufshcd.h
index e3a5c23f39ea..b4d7b5f60995 100644
--- a/include/linux/scsi/ufs/ufshcd.h
+++ b/include/linux/scsi/ufs/ufshcd.h
@@ -55,6 +55,7 @@
#include <linux/completion.h>
#include <linux/regulator/consumer.h>
#include <linux/pm_qos.h>
+#include <linux/scsi/ufs/unipro.h>
#include <asm/irq.h>
#include <asm/byteorder.h>
@@ -940,6 +941,14 @@ static inline int ufshcd_dme_peer_get(struct ufs_hba *hba,
int ufshcd_read_device_desc(struct ufs_hba *hba, u8 *buf, u32 size);
+static inline bool ufshcd_is_hs_mode(struct ufs_pa_layer_attr *pwr_info)
+{
+ return (pwr_info->pwr_rx == FAST_MODE ||
+ pwr_info->pwr_rx == FASTAUTO_MODE) &&
+ (pwr_info->pwr_tx == FAST_MODE ||
+ pwr_info->pwr_tx == FASTAUTO_MODE);
+}
+
#define ASCII_STD true
#define UTF16_STD false
int ufshcd_read_string_desc(struct ufs_hba *hba, int desc_index, u8 *buf,