diff options
| author | Linux Build Service Account <lnxbuild@localhost> | 2016-11-28 14:56:38 -0800 |
|---|---|---|
| committer | Gerrit - the friendly Code Review server <code-review@localhost> | 2016-11-28 14:56:37 -0800 |
| commit | d664e19659647a995cf3a12b884e3d1a245de555 (patch) | |
| tree | 7e99e4597635b9438b653704eac78a8dc5584896 | |
| parent | 1c699f1930f6274a959625ad563f192a7b105928 (diff) | |
| parent | 00e8c4542a6fbbdfad334ce8a032cc40c5c0d302 (diff) | |
Merge "ARM: dts: msm: add UFS_RESET pin ctrl data for msm8998"
| -rw-r--r-- | Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt | 4 | ||||
| -rw-r--r-- | arch/arm/boot/dts/qcom/msm8998-pinctrl.dtsi | 46 | ||||
| -rw-r--r-- | arch/arm/boot/dts/qcom/msm8998.dtsi | 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 |
6 files changed, 144 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/arch/arm/boot/dts/qcom/msm8998-pinctrl.dtsi b/arch/arm/boot/dts/qcom/msm8998-pinctrl.dtsi index 1f5facd5cde5..5685e9041fe4 100644 --- a/arch/arm/boot/dts/qcom/msm8998-pinctrl.dtsi +++ b/arch/arm/boot/dts/qcom/msm8998-pinctrl.dtsi @@ -1624,6 +1624,52 @@ }; }; + ufs_dev_reset_assert: ufs_dev_reset_assert { + config { + pins = "ufs_reset"; + bias-pull-down; /* default: pull down */ + /* + * UFS_RESET driver strengths are having + * different values/steps compared to typical + * GPIO drive strengths. + * + * Following table clarifies: + * + * HDRV value | UFS_RESET | Typical GPIO + * (dec) | (mA) | (mA) + * 0 | 0.8 | 2 + * 1 | 1.55 | 4 + * 2 | 2.35 | 6 + * 3 | 3.1 | 8 + * 4 | 3.9 | 10 + * 5 | 4.65 | 12 + * 6 | 5.4 | 14 + * 7 | 6.15 | 16 + * + * POR value for UFS_RESET HDRV is 3 which means + * 3.1mA and we want to use that. Hence just + * specify 8mA to "drive-strength" binding and + * that should result into writing 3 to HDRV + * field. + */ + drive-strength = <8>; /* default: 3.1 mA */ + output-low; /* active low reset */ + }; + }; + + ufs_dev_reset_deassert: ufs_dev_reset_deassert { + config { + pins = "ufs_reset"; + bias-pull-down; /* default: pull down */ + /* + * default: 3.1 mA + * check comments under ufs_dev_reset_assert + */ + drive-strength = <8>; + output-high; /* active low reset */ + }; + }; + sdc2_clk_on: sdc2_clk_on { config { pins = "sdc2_clk"; diff --git a/arch/arm/boot/dts/qcom/msm8998.dtsi b/arch/arm/boot/dts/qcom/msm8998.dtsi index 7f7f2f65deee..e95bc8597043 100644 --- a/arch/arm/boot/dts/qcom/msm8998.dtsi +++ b/arch/arm/boot/dts/qcom/msm8998.dtsi @@ -1660,6 +1660,10 @@ qcom,pm-qos-cpu-group-latency-us = <70 70>; qcom,pm-qos-default-cpu = <0>; + pinctrl-names = "dev-reset-assert", "dev-reset-deassert"; + pinctrl-0 = <&ufs_dev_reset_assert>; + pinctrl-1 = <&ufs_dev_reset_deassert>; + resets = <&clock_gcc UFS_BCR>; reset-names = "core_reset"; 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) |
