aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYaniv Gardi <ygardi@codeaurora.org>2015-03-31 10:37:14 -0400
committerJames Bottomley <JBottomley@Odin.com>2015-04-10 11:53:56 -0400
commitcad2e03d8607793fd71a830b4b41fc8e9e9995ea (patch)
tree4ffd08cf1f701eb08fab9c4c01d9d8a84386dae4
parentbfdbe8ba21291c024afcec95b7bd515b5bf69689 (diff)
ufs: add support to allow non standard behaviours (quirks)
Some implementation of UFS host controller HW might have some non-standard behaviours (quirks) when compared to behaviour specified by UFSHCI specification. This patch add support to allow specifying all such quirks to standard UFS host controller driver so standard driver takes them into account. In this change a UFSHCD_QUIRK_DELAY_BEFORE_DME_CMDS is introduced, where a minimum delay of 1ms is required before DME commands for stability purposes. Signed-off-by: Yaniv Gardi <ygardi@codeaurora.org> Reviewed-by: Gilad Broner <gbroner@codeaurora.org> Signed-off-by: James Bottomley <JBottomley@Odin.com>
-rw-r--r--drivers/scsi/ufs/ufs-qcom.c22
-rw-r--r--drivers/scsi/ufs/ufs-qcom.h18
-rw-r--r--drivers/scsi/ufs/ufshcd.c35
-rw-r--r--drivers/scsi/ufs/ufshcd.h9
4 files changed, 79 insertions, 5 deletions
diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c
index d7cb8435b697..6652a8171de6 100644
--- a/drivers/scsi/ufs/ufs-qcom.c
+++ b/drivers/scsi/ufs/ufs-qcom.c
@@ -694,13 +694,24 @@ out:
694 */ 694 */
695static void ufs_qcom_advertise_quirks(struct ufs_hba *hba) 695static void ufs_qcom_advertise_quirks(struct ufs_hba *hba)
696{ 696{
697 struct ufs_qcom_host *host = hba->priv;
697 698
699 if (host->hw_ver.major == 0x1)
700 hba->quirks |= UFSHCD_QUIRK_DELAY_BEFORE_DME_CMDS;
698 701
699 /* 702 if (host->hw_ver.major >= 0x2) {
700 * TBD 703 if (!ufs_qcom_cap_qunipro(host))
701 * here we should be advertising controller quirks according to 704 /* Legacy UniPro mode still need following quirks */
702 * controller version. 705 hba->quirks |= UFSHCD_QUIRK_DELAY_BEFORE_DME_CMDS;
703 */ 706 }
707}
708
709static void ufs_qcom_set_caps(struct ufs_hba *hba)
710{
711 struct ufs_qcom_host *host = hba->priv;
712
713 if (host->hw_ver.major >= 0x2)
714 host->caps = UFS_QCOM_CAP_QUNIPRO;
704} 715}
705 716
706static int ufs_qcom_get_bus_vote(struct ufs_qcom_host *host, 717static int ufs_qcom_get_bus_vote(struct ufs_qcom_host *host,
@@ -938,6 +949,7 @@ static int ufs_qcom_init(struct ufs_hba *hba)
938 if (err) 949 if (err)
939 goto out_disable_phy; 950 goto out_disable_phy;
940 951
952 ufs_qcom_set_caps(hba);
941 ufs_qcom_advertise_quirks(hba); 953 ufs_qcom_advertise_quirks(hba);
942 954
943 hba->caps |= UFSHCD_CAP_CLK_GATING | UFSHCD_CAP_CLK_SCALING; 955 hba->caps |= UFSHCD_CAP_CLK_GATING | UFSHCD_CAP_CLK_SCALING;
diff --git a/drivers/scsi/ufs/ufs-qcom.h b/drivers/scsi/ufs/ufs-qcom.h
index 903739668228..db2c0a00e846 100644
--- a/drivers/scsi/ufs/ufs-qcom.h
+++ b/drivers/scsi/ufs/ufs-qcom.h
@@ -158,6 +158,16 @@ struct ufs_hw_version {
158 u8 major; 158 u8 major;
159}; 159};
160struct ufs_qcom_host { 160struct ufs_qcom_host {
161
162 /*
163 * Set this capability if host controller supports the QUniPro mode
164 * and if driver wants the Host controller to operate in QUniPro mode.
165 * Note: By default this capability will be kept enabled if host
166 * controller supports the QUniPro mode.
167 */
168 #define UFS_QCOM_CAP_QUNIPRO UFS_BIT(0)
169 u32 caps;
170
161 struct phy *generic_phy; 171 struct phy *generic_phy;
162 struct ufs_hba *hba; 172 struct ufs_hba *hba;
163 struct ufs_qcom_bus_vote bus_vote; 173 struct ufs_qcom_bus_vote bus_vote;
@@ -175,4 +185,12 @@ struct ufs_qcom_host {
175#define ufs_qcom_is_link_active(hba) ufshcd_is_link_active(hba) 185#define ufs_qcom_is_link_active(hba) ufshcd_is_link_active(hba)
176#define ufs_qcom_is_link_hibern8(hba) ufshcd_is_link_hibern8(hba) 186#define ufs_qcom_is_link_hibern8(hba) ufshcd_is_link_hibern8(hba)
177 187
188static inline bool ufs_qcom_cap_qunipro(struct ufs_qcom_host *host)
189{
190 if (host->caps & UFS_QCOM_CAP_QUNIPRO)
191 return true;
192 else
193 return false;
194}
195
178#endif /* UFS_QCOM_H_ */ 196#endif /* UFS_QCOM_H_ */
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 5d60a868830d..dfd72af28f06 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -183,6 +183,7 @@ static int __ufshcd_setup_clocks(struct ufs_hba *hba, bool on,
183static int ufshcd_setup_clocks(struct ufs_hba *hba, bool on); 183static int ufshcd_setup_clocks(struct ufs_hba *hba, bool on);
184static int ufshcd_uic_hibern8_exit(struct ufs_hba *hba); 184static int ufshcd_uic_hibern8_exit(struct ufs_hba *hba);
185static int ufshcd_uic_hibern8_enter(struct ufs_hba *hba); 185static int ufshcd_uic_hibern8_enter(struct ufs_hba *hba);
186static inline void ufshcd_add_delay_before_dme_cmd(struct ufs_hba *hba);
186static int ufshcd_host_reset_and_restore(struct ufs_hba *hba); 187static int ufshcd_host_reset_and_restore(struct ufs_hba *hba);
187static irqreturn_t ufshcd_intr(int irq, void *__hba); 188static irqreturn_t ufshcd_intr(int irq, void *__hba);
188static int ufshcd_config_pwr_mode(struct ufs_hba *hba, 189static int ufshcd_config_pwr_mode(struct ufs_hba *hba,
@@ -972,6 +973,8 @@ ufshcd_send_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd)
972 973
973 ufshcd_hold(hba, false); 974 ufshcd_hold(hba, false);
974 mutex_lock(&hba->uic_cmd_mutex); 975 mutex_lock(&hba->uic_cmd_mutex);
976 ufshcd_add_delay_before_dme_cmd(hba);
977
975 spin_lock_irqsave(hba->host->host_lock, flags); 978 spin_lock_irqsave(hba->host->host_lock, flags);
976 ret = __ufshcd_send_uic_cmd(hba, uic_cmd); 979 ret = __ufshcd_send_uic_cmd(hba, uic_cmd);
977 spin_unlock_irqrestore(hba->host->host_lock, flags); 980 spin_unlock_irqrestore(hba->host->host_lock, flags);
@@ -2058,6 +2061,37 @@ static int ufshcd_dme_link_startup(struct ufs_hba *hba)
2058 return ret; 2061 return ret;
2059} 2062}
2060 2063
2064static inline void ufshcd_add_delay_before_dme_cmd(struct ufs_hba *hba)
2065{
2066 #define MIN_DELAY_BEFORE_DME_CMDS_US 1000
2067 unsigned long min_sleep_time_us;
2068
2069 if (!(hba->quirks & UFSHCD_QUIRK_DELAY_BEFORE_DME_CMDS))
2070 return;
2071
2072 /*
2073 * last_dme_cmd_tstamp will be 0 only for 1st call to
2074 * this function
2075 */
2076 if (unlikely(!ktime_to_us(hba->last_dme_cmd_tstamp))) {
2077 min_sleep_time_us = MIN_DELAY_BEFORE_DME_CMDS_US;
2078 } else {
2079 unsigned long delta =
2080 (unsigned long) ktime_to_us(
2081 ktime_sub(ktime_get(),
2082 hba->last_dme_cmd_tstamp));
2083
2084 if (delta < MIN_DELAY_BEFORE_DME_CMDS_US)
2085 min_sleep_time_us =
2086 MIN_DELAY_BEFORE_DME_CMDS_US - delta;
2087 else
2088 return; /* no more delay required */
2089 }
2090
2091 /* allow sleep for extra 50us if needed */
2092 usleep_range(min_sleep_time_us, min_sleep_time_us + 50);
2093}
2094
2061/** 2095/**
2062 * ufshcd_dme_set_attr - UIC command for DME_SET, DME_PEER_SET 2096 * ufshcd_dme_set_attr - UIC command for DME_SET, DME_PEER_SET
2063 * @hba: per adapter instance 2097 * @hba: per adapter instance
@@ -2157,6 +2191,7 @@ static int ufshcd_uic_pwr_ctrl(struct ufs_hba *hba, struct uic_command *cmd)
2157 2191
2158 mutex_lock(&hba->uic_cmd_mutex); 2192 mutex_lock(&hba->uic_cmd_mutex);
2159 init_completion(&uic_async_done); 2193 init_completion(&uic_async_done);
2194 ufshcd_add_delay_before_dme_cmd(hba);
2160 2195
2161 spin_lock_irqsave(hba->host->host_lock, flags); 2196 spin_lock_irqsave(hba->host->host_lock, flags);
2162 hba->uic_async_done = &uic_async_done; 2197 hba->uic_async_done = &uic_async_done;
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index 4a574aa45855..b47ff07698e8 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -366,6 +366,7 @@ struct ufs_init_prefetch {
366 * @saved_err: sticky error mask 366 * @saved_err: sticky error mask
367 * @saved_uic_err: sticky UIC error mask 367 * @saved_uic_err: sticky UIC error mask
368 * @dev_cmd: ufs device management command information 368 * @dev_cmd: ufs device management command information
369 * @last_dme_cmd_tstamp: time stamp of the last completed DME command
369 * @auto_bkops_enabled: to track whether bkops is enabled in device 370 * @auto_bkops_enabled: to track whether bkops is enabled in device
370 * @vreg_info: UFS device voltage regulator information 371 * @vreg_info: UFS device voltage regulator information
371 * @clk_list_head: UFS host controller clocks list node head 372 * @clk_list_head: UFS host controller clocks list node head
@@ -416,6 +417,13 @@ struct ufs_hba {
416 unsigned int irq; 417 unsigned int irq;
417 bool is_irq_enabled; 418 bool is_irq_enabled;
418 419
420 /*
421 * delay before each dme command is required as the unipro
422 * layer has shown instabilities
423 */
424 #define UFSHCD_QUIRK_DELAY_BEFORE_DME_CMDS UFS_BIT(0)
425
426 unsigned int quirks; /* Deviations from standard UFSHCI spec. */
419 427
420 wait_queue_head_t tm_wq; 428 wait_queue_head_t tm_wq;
421 wait_queue_head_t tm_tag_wq; 429 wait_queue_head_t tm_tag_wq;
@@ -446,6 +454,7 @@ struct ufs_hba {
446 454
447 /* Device management request data */ 455 /* Device management request data */
448 struct ufs_dev_cmd dev_cmd; 456 struct ufs_dev_cmd dev_cmd;
457 ktime_t last_dme_cmd_tstamp;
449 458
450 /* Keeps information of the UFS device connected to this host */ 459 /* Keeps information of the UFS device connected to this host */
451 struct ufs_dev_info dev_info; 460 struct ufs_dev_info dev_info;