scsi: ufs: add UFS device reset support

Some platforms may have a SOC pin/gpio connected to UFS device's
RST_n to allow the UFS device reset. This change adds support to
trigger device reset on such platforms.

Change-Id: Ie4faa47fb76837dba909f9a3d0dfe11bc69659e1
Signed-off-by: Subhash Jadavani <subhashj@codeaurora.org>
This commit is contained in:
Subhash Jadavani 2016-11-10 12:05:10 -08:00
parent 1f92a7048e
commit 0c82737188
4 changed files with 94 additions and 0 deletions

View file

@ -55,6 +55,10 @@ Optional properties:
- lanes-per-direction: number of lanes available per direction - either 1 or 2.
Note that it is assume same number of lanes is used both directions at once.
If not specified, default is 2 lanes per direction.
- pinctrl-names, pinctrl-0, pinctrl-1,.. pinctrl-n: Refer to "Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt"
for these optional properties
Note: If above properties are not defined it can be assumed that the supply
regulators or clocks are always on.

View file

@ -250,6 +250,20 @@ static void ufshcd_parse_pm_levels(struct ufs_hba *hba)
}
}
static int ufshcd_parse_pinctrl_info(struct ufs_hba *hba)
{
int ret = 0;
/* Try to obtain pinctrl handle */
hba->pctrl = devm_pinctrl_get(hba->dev);
if (IS_ERR(hba->pctrl)) {
ret = PTR_ERR(hba->pctrl);
hba->pctrl = NULL;
}
return ret;
}
#ifdef CONFIG_SMP
/**
* ufshcd_pltfrm_suspend - suspend power management function
@ -361,6 +375,13 @@ int ufshcd_pltfrm_init(struct platform_device *pdev,
goto dealloc_host;
}
err = ufshcd_parse_pinctrl_info(hba);
if (err) {
dev_dbg(&pdev->dev, "%s: unable to parse pinctrl data %d\n",
__func__, err);
/* let's not fail the probe */
}
ufshcd_parse_pm_levels(hba);
if (!dev->dma_mask)

View file

@ -446,6 +446,63 @@ void ufshcd_scsi_block_requests(struct ufs_hba *hba)
}
EXPORT_SYMBOL(ufshcd_scsi_block_requests);
static int ufshcd_device_reset_ctrl(struct ufs_hba *hba, bool ctrl)
{
int ret = 0;
if (!hba->pctrl)
return 0;
/* Assert reset if ctrl == true */
if (ctrl)
ret = pinctrl_select_state(hba->pctrl,
pinctrl_lookup_state(hba->pctrl, "dev-reset-assert"));
else
ret = pinctrl_select_state(hba->pctrl,
pinctrl_lookup_state(hba->pctrl, "dev-reset-deassert"));
if (ret < 0)
dev_err(hba->dev, "%s: %s failed with err %d\n",
__func__, ctrl ? "Assert" : "Deassert", ret);
return ret;
}
static inline int ufshcd_assert_device_reset(struct ufs_hba *hba)
{
return ufshcd_device_reset_ctrl(hba, true);
}
static inline int ufshcd_deassert_device_reset(struct ufs_hba *hba)
{
return ufshcd_device_reset_ctrl(hba, false);
}
static int ufshcd_reset_device(struct ufs_hba *hba)
{
int ret;
/* reset the connected UFS device */
ret = ufshcd_assert_device_reset(hba);
if (ret)
goto out;
/*
* The reset signal is active low.
* The UFS device shall detect more than or equal to 1us of positive
* or negative RST_n pulse width.
* To be on safe side, keep the reset low for atleast 10us.
*/
usleep_range(10, 15);
ret = ufshcd_deassert_device_reset(hba);
if (ret)
goto out;
/* same as assert, wait for atleast 10us after deassert */
usleep_range(10, 15);
out:
return ret;
}
/* replace non-printable or non-ASCII characters with spaces */
static inline void ufshcd_remove_non_printable(char *val)
{
@ -6520,6 +6577,11 @@ static int ufshcd_reset_and_restore(struct ufs_hba *hba)
dev_warn(hba->dev, "%s: full reset returned %d\n",
__func__, err);
err = ufshcd_reset_device(hba);
if (err)
dev_warn(hba->dev, "%s: device reset failed. err %d\n",
__func__, err);
err = ufshcd_host_reset_and_restore(hba);
} while (err && --retries);
@ -9422,6 +9484,12 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)
/* Reset controller to power on reset (POR) state */
ufshcd_vops_full_reset(hba);
/* reset connected UFS device */
err = ufshcd_reset_device(hba);
if (err)
dev_warn(hba->dev, "%s: device reset failed. err %d\n",
__func__, err);
/* Host controller enable */
err = ufshcd_hba_enable(hba);
if (err) {

View file

@ -907,6 +907,7 @@ struct ufs_hba {
int scsi_block_reqs_cnt;
bool full_init_linereset;
struct pinctrl *pctrl;
};
static inline void ufshcd_mark_shutdown_ongoing(struct ufs_hba *hba)