diff options
| author | Subhash Jadavani <subhashj@codeaurora.org> | 2016-11-10 12:05:10 -0800 |
|---|---|---|
| committer | Subhash Jadavani <subhashj@codeaurora.org> | 2016-11-24 07:55:03 -0800 |
| commit | 0c82737188e2d63a08196e078e411032dbbc3b89 (patch) | |
| tree | 306273bd39551254746ccdbbbd925afdcedc277e | |
| parent | 1f92a7048e6dd82a77506babc1fa06887c4cad03 (diff) | |
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>
| -rw-r--r-- | Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt | 4 | ||||
| -rw-r--r-- | drivers/scsi/ufs/ufshcd-pltfrm.c | 21 | ||||
| -rw-r--r-- | drivers/scsi/ufs/ufshcd.c | 68 | ||||
| -rw-r--r-- | drivers/scsi/ufs/ufshcd.h | 1 |
4 files changed, 94 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt b/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt index bceee5e1747d..a25961c6e7de 100644 --- a/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt +++ b/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt @@ -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. diff --git a/drivers/scsi/ufs/ufshcd-pltfrm.c b/drivers/scsi/ufs/ufshcd-pltfrm.c index 5a9564326099..41684dca6baa 100644 --- a/drivers/scsi/ufs/ufshcd-pltfrm.c +++ b/drivers/scsi/ufs/ufshcd-pltfrm.c @@ -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) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index e52aed51f67d..6a5c9ca74818 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -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) { diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index 9f2b04c8ff82..c5eb21d8a0fe 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -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) |
