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:
parent
1f92a7048e
commit
0c82737188
4 changed files with 94 additions and 0 deletions
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Add table
Reference in a new issue