scsi: ufs-qcom: Implement device_reset vops
authorBjorn Andersson <bjorn.andersson@linaro.org>
Wed, 28 Aug 2019 19:17:55 +0000 (12:17 -0700)
committerMartin K. Petersen <martin.petersen@oracle.com>
Sat, 7 Sep 2019 20:36:03 +0000 (16:36 -0400)
The UFS_RESET pin on Qualcomm SoCs are controlled by TLMM and exposed
through the GPIO framework. Acquire the device-reset GPIO and use this to
implement the device_reset vops, to allow resetting the attached memory.

Based on downstream support implemented by Subhash Jadavani
<subhashj@codeaurora.org>.

Link: https://lore.kernel.org/r/20190828191756.24312-3-bjorn.andersson@linaro.org
Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
Acked-by: Rob Herring <robh@kernel.org>
Acked-by: Avri Altman <Avri.Altman@wdc.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt
drivers/scsi/ufs/ufs-qcom.c
drivers/scsi/ufs/ufs-qcom.h

index a74720486ee29d775170715ac9f8497feab7a0b5..d78ef63935f9e09bb1ec3fbc3cf4efcfac263563 100644 (file)
@@ -54,6 +54,8 @@ Optional properties:
                          PHY reset from the UFS controller.
 - resets            : reset node register
 - reset-names       : describe reset node register, the "rst" corresponds to reset the whole UFS IP.
+- reset-gpios       : A phandle and gpio specifier denoting the GPIO connected
+                     to the RESET pin of the UFS memory device.
 
 Note: If above properties are not defined it can be assumed that the supply
 regulators or clocks are always on.
index 02cdcefb81acdee4e02665341a7ef2c24ed197ea..a5b71487a2065636960efcafa34133225e288f42 100644 (file)
@@ -8,6 +8,7 @@
 #include <linux/of.h>
 #include <linux/platform_device.h>
 #include <linux/phy/phy.h>
+#include <linux/gpio/consumer.h>
 #include <linux/reset-controller.h>
 
 #include "ufshcd.h"
@@ -1137,6 +1138,15 @@ static int ufs_qcom_init(struct ufs_hba *hba)
                }
        }
 
+       host->device_reset = devm_gpiod_get_optional(dev, "reset",
+                                                    GPIOD_OUT_HIGH);
+       if (IS_ERR(host->device_reset)) {
+               err = PTR_ERR(host->device_reset);
+               if (err != -EPROBE_DEFER)
+                       dev_err(dev, "failed to acquire reset gpio: %d\n", err);
+               goto out_variant_clear;
+       }
+
        err = ufs_qcom_bus_register(host);
        if (err)
                goto out_variant_clear;
@@ -1542,6 +1552,31 @@ static void ufs_qcom_dump_dbg_regs(struct ufs_hba *hba)
        usleep_range(1000, 1100);
 }
 
+/**
+ * ufs_qcom_device_reset() - toggle the (optional) device reset line
+ * @hba: per-adapter instance
+ *
+ * Toggles the (optional) reset line to reset the attached device.
+ */
+static void ufs_qcom_device_reset(struct ufs_hba *hba)
+{
+       struct ufs_qcom_host *host = ufshcd_get_variant(hba);
+
+       /* reset gpio is optional */
+       if (!host->device_reset)
+               return;
+
+       /*
+        * The UFS device shall detect reset pulses of 1us, sleep for 10us to
+        * be on the safe side.
+        */
+       gpiod_set_value_cansleep(host->device_reset, 1);
+       usleep_range(10, 15);
+
+       gpiod_set_value_cansleep(host->device_reset, 0);
+       usleep_range(10, 15);
+}
+
 /**
  * struct ufs_hba_qcom_vops - UFS QCOM specific variant operations
  *
@@ -1562,6 +1597,7 @@ static const struct ufs_hba_variant_ops ufs_hba_qcom_vops = {
        .suspend                = ufs_qcom_suspend,
        .resume                 = ufs_qcom_resume,
        .dbg_register_dump      = ufs_qcom_dump_dbg_regs,
+       .device_reset           = ufs_qcom_device_reset,
 };
 
 /**
index 001915d1e0e4d373e433dca3b955fc587a545d63..d401f174bb70b44932c4f2f20aedf288bc11fbcd 100644 (file)
@@ -195,6 +195,8 @@ struct ufs_qcom_testbus {
        u8 select_minor;
 };
 
+struct gpio_desc;
+
 struct ufs_qcom_host {
        /*
         * Set this capability if host controller supports the QUniPro mode
@@ -232,6 +234,8 @@ struct ufs_qcom_host {
        struct ufs_qcom_testbus testbus;
 
        struct reset_controller_dev rcdev;
+
+       struct gpio_desc *device_reset;
 };
 
 static inline u32