summaryrefslogtreecommitdiffstats
path: root/drivers/scsi/ufs/ufshcd-pltfrm.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/scsi/ufs/ufshcd-pltfrm.c')
-rw-r--r--drivers/scsi/ufs/ufshcd-pltfrm.c100
1 files changed, 100 insertions, 0 deletions
diff --git a/drivers/scsi/ufs/ufshcd-pltfrm.c b/drivers/scsi/ufs/ufshcd-pltfrm.c
index d727b1a83e95..51e47c429a1f 100644
--- a/drivers/scsi/ufs/ufshcd-pltfrm.c
+++ b/drivers/scsi/ufs/ufshcd-pltfrm.c
@@ -53,6 +53,99 @@ static struct ufs_hba_variant_ops *get_variant_ops(struct device *dev)
return NULL;
}
+#define MAX_PROP_SIZE 32
+static int ufshcd_populate_vreg(struct device *dev, const char *name,
+ struct ufs_vreg **out_vreg)
+{
+ int ret = 0;
+ char prop_name[MAX_PROP_SIZE];
+ struct ufs_vreg *vreg = NULL;
+ struct device_node *np = dev->of_node;
+
+ if (!np) {
+ dev_err(dev, "%s: non DT initialization\n", __func__);
+ goto out;
+ }
+
+ snprintf(prop_name, MAX_PROP_SIZE, "%s-supply", name);
+ if (!of_parse_phandle(np, prop_name, 0)) {
+ dev_info(dev, "%s: Unable to find %s regulator, assuming enabled\n",
+ __func__, prop_name);
+ goto out;
+ }
+
+ vreg = devm_kzalloc(dev, sizeof(*vreg), GFP_KERNEL);
+ if (!vreg) {
+ dev_err(dev, "No memory for %s regulator\n", name);
+ goto out;
+ }
+
+ vreg->name = kstrdup(name, GFP_KERNEL);
+
+ snprintf(prop_name, MAX_PROP_SIZE, "%s-max-microamp", name);
+ ret = of_property_read_u32(np, prop_name, &vreg->max_uA);
+ if (ret) {
+ dev_err(dev, "%s: unable to find %s err %d\n",
+ __func__, prop_name, ret);
+ goto out_free;
+ }
+
+ vreg->min_uA = 0;
+ if (!strcmp(name, "vcc")) {
+ if (of_property_read_bool(np, "vcc-supply-1p8")) {
+ vreg->min_uV = UFS_VREG_VCC_1P8_MIN_UV;
+ vreg->max_uV = UFS_VREG_VCC_1P8_MAX_UV;
+ } else {
+ vreg->min_uV = UFS_VREG_VCC_MIN_UV;
+ vreg->max_uV = UFS_VREG_VCC_MAX_UV;
+ }
+ } else if (!strcmp(name, "vccq")) {
+ vreg->min_uV = UFS_VREG_VCCQ_MIN_UV;
+ vreg->max_uV = UFS_VREG_VCCQ_MAX_UV;
+ } else if (!strcmp(name, "vccq2")) {
+ vreg->min_uV = UFS_VREG_VCCQ2_MIN_UV;
+ vreg->max_uV = UFS_VREG_VCCQ2_MAX_UV;
+ }
+
+ goto out;
+
+out_free:
+ devm_kfree(dev, vreg);
+ vreg = NULL;
+out:
+ if (!ret)
+ *out_vreg = vreg;
+ return ret;
+}
+
+/**
+ * ufshcd_parse_regulator_info - get regulator info from device tree
+ * @hba: per adapter instance
+ *
+ * Get regulator info from device tree for vcc, vccq, vccq2 power supplies.
+ * If any of the supplies are not defined it is assumed that they are always-on
+ * and hence return zero. If the property is defined but parsing is failed
+ * then return corresponding error.
+ */
+static int ufshcd_parse_regulator_info(struct ufs_hba *hba)
+{
+ int err;
+ struct device *dev = hba->dev;
+ struct ufs_vreg_info *info = &hba->vreg_info;
+
+ err = ufshcd_populate_vreg(dev, "vcc", &info->vcc);
+ if (err)
+ goto out;
+
+ err = ufshcd_populate_vreg(dev, "vccq", &info->vccq);
+ if (err)
+ goto out;
+
+ err = ufshcd_populate_vreg(dev, "vccq2", &info->vccq2);
+out:
+ return err;
+}
+
#ifdef CONFIG_PM
/**
* ufshcd_pltfrm_suspend - suspend power management function
@@ -173,6 +266,13 @@ static int ufshcd_pltfrm_probe(struct platform_device *pdev)
hba->vops = get_variant_ops(&pdev->dev);
+ err = ufshcd_parse_regulator_info(hba);
+ if (err) {
+ dev_err(&pdev->dev, "%s: regulator init failed %d\n",
+ __func__, err);
+ goto out;
+ }
+
pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);