summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSubhash Jadavani <subhashj@codeaurora.org>2014-09-05 18:12:09 -0700
committerDavid Keitel <dkeitel@codeaurora.org>2016-03-22 10:57:22 -0700
commit279f4d7c9d841baa9756ebeb157fe17a32cd699d (patch)
tree728268e8079ae950063b934bd5ef48842d8794e6
parentad03d1f91beecc0b2a9fb198bfac7ff65f067d9b (diff)
scsi: ufs: remove unwanted checks from hibern8 sequence
We are unnecessarily checking for the request/task doorbell status during hibern8 enter/exit path but it's very important to have the minimal latencies for hibern8 enter/exit in order to achieve agressive power management strategies for UFS. So these unecessary checks are moved out of this hot path. Change-Id: Ibaeddca7bd516d71eb03b02a1fc1a86f05038f08 Signed-off-by: Subhash Jadavani <subhashj@codeaurora.org> [venkatg@codeaurora.org: resolved trivial merge conflicts] Signed-off-by: Venkat Gopalakrishnan <venkatg@codeaurora.org>
-rw-r--r--drivers/scsi/ufs/debugfs.c7
-rw-r--r--drivers/scsi/ufs/ufshcd.c78
-rw-r--r--include/linux/phy/phy-qcom-ufs.h~HEAD59
-rw-r--r--include/linux/scsi/ufs/ufshcd.h2
4 files changed, 113 insertions, 33 deletions
diff --git a/drivers/scsi/ufs/debugfs.c b/drivers/scsi/ufs/debugfs.c
index 8790f7a80682..c9479e979a8c 100644
--- a/drivers/scsi/ufs/debugfs.c
+++ b/drivers/scsi/ufs/debugfs.c
@@ -668,6 +668,7 @@ static ssize_t ufsdbg_power_mode_write(struct file *file,
loff_t buff_pos = 0;
int ret;
int idx = 0;
+ #define DOORBELL_CLR_TOUT_US (1000 * 1000) /* 1 sec */
ret = simple_write_to_buffer(pwr_mode_str, BUFF_LINE_CAPACITY,
&buff_pos, ubuf, cnt);
@@ -695,7 +696,11 @@ static ssize_t ufsdbg_power_mode_write(struct file *file,
pwr_mode.lane_tx, pwr_mode.pwr_rx, pwr_mode.pwr_tx);
pm_runtime_get_sync(hba->dev);
- ret = ufshcd_config_pwr_mode(hba, &pwr_mode);
+ scsi_block_requests(hba->host);
+ ret = ufshcd_wait_for_doorbell_clr(hba, DOORBELL_CLR_TOUT_US);
+ if (!ret)
+ ret = ufshcd_config_pwr_mode(hba, &pwr_mode);
+ scsi_unblock_requests(hba->host);
pm_runtime_put_sync(hba->dev);
if (ret == -EBUSY)
dev_err(hba->dev,
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index cdc434ea1287..7fb477d4f97e 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -2715,43 +2715,13 @@ static int ufshcd_uic_pwr_ctrl(struct ufs_hba *hba, struct uic_command *cmd)
unsigned long flags;
u8 status;
int ret;
- u32 tm_doorbell;
- u32 tr_doorbell;
- bool uic_ready;
- int retries = POWER_MODE_RETRIES;
mutex_lock(&hba->uic_cmd_mutex);
init_completion(&uic_async_done);
ufshcd_add_delay_before_dme_cmd(hba);
- /*
- * Before changing the power mode there should be no outstanding
- * tasks/transfer requests. Verify by checking the doorbell registers
- * are clear.
- */
- do {
- spin_lock_irqsave(hba->host->host_lock, flags);
- uic_ready = ufshcd_ready_for_uic_cmd(hba);
- tm_doorbell = ufshcd_readl(hba, REG_UTP_TASK_REQ_DOOR_BELL);
- tr_doorbell = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL);
- if (!tm_doorbell && !tr_doorbell && uic_ready)
- break;
-
- spin_unlock_irqrestore(hba->host->host_lock, flags);
- schedule();
- retries--;
- } while (retries && (tm_doorbell || tr_doorbell || !uic_ready));
-
- if (!retries) {
- dev_err(hba->dev,
- "%s: too many retries waiting for doorbell to clear (tm=0x%x, tr=0x%x, uicrdy=%d)\n",
- __func__, tm_doorbell, tr_doorbell, uic_ready);
- ret = -EBUSY;
- goto out;
- }
-
+ spin_lock_irqsave(hba->host->host_lock, flags);
hba->uic_async_done = &uic_async_done;
-
ret = __ufshcd_send_uic_cmd(hba, cmd);
spin_unlock_irqrestore(hba->host->host_lock, flags);
if (ret) {
@@ -2792,6 +2762,52 @@ out:
return ret;
}
+int ufshcd_wait_for_doorbell_clr(struct ufs_hba *hba, u64 wait_timeout_us)
+{
+ unsigned long flags;
+ int ret = 0;
+ u32 tm_doorbell;
+ u32 tr_doorbell;
+ bool timeout = false;
+ ktime_t start = ktime_get();
+
+ spin_lock_irqsave(hba->host->host_lock, flags);
+ if (hba->ufshcd_state != UFSHCD_STATE_OPERATIONAL) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ /*
+ * Wait for all the outstanding tasks/transfer requests.
+ * Verify by checking the doorbell registers are clear.
+ */
+ do {
+ tm_doorbell = ufshcd_readl(hba, REG_UTP_TASK_REQ_DOOR_BELL);
+ tr_doorbell = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL);
+ if (!tm_doorbell && !tr_doorbell) {
+ timeout = false;
+ break;
+ }
+
+ spin_unlock_irqrestore(hba->host->host_lock, flags);
+ schedule();
+ if (ktime_to_us(ktime_sub(ktime_get(), start)) >
+ wait_timeout_us)
+ timeout = true;
+ spin_lock_irqsave(hba->host->host_lock, flags);
+ } while (tm_doorbell || tr_doorbell);
+
+ if (timeout) {
+ dev_err(hba->dev,
+ "%s: timedout waiting for doorbell to clear (tm=0x%x, tr=0x%x)\n",
+ __func__, tm_doorbell, tr_doorbell);
+ ret = -EBUSY;
+ }
+out:
+ spin_unlock_irqrestore(hba->host->host_lock, flags);
+ return ret;
+}
+
/**
* ufshcd_uic_change_pwr_mode - Perform the UIC power mode chage
* using DME_SET primitives.
diff --git a/include/linux/phy/phy-qcom-ufs.h~HEAD b/include/linux/phy/phy-qcom-ufs.h~HEAD
new file mode 100644
index 000000000000..9d18e9f948e9
--- /dev/null
+++ b/include/linux/phy/phy-qcom-ufs.h~HEAD
@@ -0,0 +1,59 @@
+/*
+ * 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
+ * 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 PHY_QCOM_UFS_H_
+#define PHY_QCOM_UFS_H_
+
+#include "phy.h"
+
+/**
+ * ufs_qcom_phy_enable_ref_clk() - Enable the phy
+ * ref clock.
+ * @phy: reference to a generic phy
+ *
+ * returns 0 for success, and non-zero for error.
+ */
+int ufs_qcom_phy_enable_ref_clk(struct phy *phy);
+
+/**
+ * ufs_qcom_phy_disable_ref_clk() - Disable the phy
+ * ref clock.
+ * @phy: reference to a generic phy.
+ */
+void ufs_qcom_phy_disable_ref_clk(struct phy *phy);
+
+/**
+ * ufs_qcom_phy_enable_dev_ref_clk() - Enable the device
+ * ref clock.
+ * @phy: reference to a generic phy.
+ */
+void ufs_qcom_phy_enable_dev_ref_clk(struct phy *phy);
+
+/**
+ * ufs_qcom_phy_disable_dev_ref_clk() - Disable the device
+ * ref clock.
+ * @phy: reference to a generic phy.
+ */
+void ufs_qcom_phy_disable_dev_ref_clk(struct phy *phy);
+
+int ufs_qcom_phy_enable_iface_clk(struct phy *phy);
+void ufs_qcom_phy_disable_iface_clk(struct phy *phy);
+int ufs_qcom_phy_start_serdes(struct phy *phy);
+int ufs_qcom_phy_set_tx_lane_enable(struct phy *phy, u32 tx_lanes);
+int ufs_qcom_phy_calibrate_phy(struct phy *phy, bool is_rate_B);
+int ufs_qcom_phy_is_pcs_ready(struct phy *phy);
+void ufs_qcom_phy_save_controller_version(struct phy *phy,
+ u8 major, u16 minor, u16 step);
+
+#endif /* PHY_QCOM_UFS_H_ */
diff --git a/include/linux/scsi/ufs/ufshcd.h b/include/linux/scsi/ufs/ufshcd.h
index bb24bcb3bf30..9a35a4cd170a 100644
--- a/include/linux/scsi/ufs/ufshcd.h
+++ b/include/linux/scsi/ufs/ufshcd.h
@@ -771,6 +771,7 @@ int ufshcd_query_descriptor(struct ufs_hba *hba, enum query_opcode opcode,
int ufshcd_hold(struct ufs_hba *hba, bool async);
void ufshcd_release(struct ufs_hba *hba);
+int ufshcd_wait_for_doorbell_clr(struct ufs_hba *hba, u64 wait_timeout_us);
/* Wrapper functions for safely calling variant operations */
static inline const char *ufshcd_get_var_name(struct ufs_hba *hba)
@@ -875,5 +876,4 @@ static inline void ufshcd_vops_dbg_register_dump(struct ufs_hba *hba)
if (hba->vops && hba->vops->dbg_register_dump)
hba->vops->dbg_register_dump(hba);
}
-
#endif /* End of Header */