summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinux Build Service Account <lnxbuild@localhost>2016-11-28 14:56:38 -0800
committerGerrit - the friendly Code Review server <code-review@localhost>2016-11-28 14:56:37 -0800
commitd664e19659647a995cf3a12b884e3d1a245de555 (patch)
tree7e99e4597635b9438b653704eac78a8dc5584896
parent1c699f1930f6274a959625ad563f192a7b105928 (diff)
parent00e8c4542a6fbbdfad334ce8a032cc40c5c0d302 (diff)
Merge "ARM: dts: msm: add UFS_RESET pin ctrl data for msm8998"
-rw-r--r--Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt4
-rw-r--r--arch/arm/boot/dts/qcom/msm8998-pinctrl.dtsi46
-rw-r--r--arch/arm/boot/dts/qcom/msm8998.dtsi4
-rw-r--r--drivers/scsi/ufs/ufshcd-pltfrm.c21
-rw-r--r--drivers/scsi/ufs/ufshcd.c68
-rw-r--r--drivers/scsi/ufs/ufshcd.h1
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)