diff options
| author | Subhash Jadavani <subhashj@codeaurora.org> | 2014-09-05 18:12:09 -0700 |
|---|---|---|
| committer | David Keitel <dkeitel@codeaurora.org> | 2016-03-22 10:57:22 -0700 |
| commit | 279f4d7c9d841baa9756ebeb157fe17a32cd699d (patch) | |
| tree | 728268e8079ae950063b934bd5ef48842d8794e6 | |
| parent | ad03d1f91beecc0b2a9fb198bfac7ff65f067d9b (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.c | 7 | ||||
| -rw-r--r-- | drivers/scsi/ufs/ufshcd.c | 78 | ||||
| -rw-r--r-- | include/linux/phy/phy-qcom-ufs.h~HEAD | 59 | ||||
| -rw-r--r-- | include/linux/scsi/ufs/ufshcd.h | 2 |
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 */ |
