devicetree.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 2/3] scsi: ufs: Add regulator enable support
       [not found] <1376391620-13840-1-git-send-email-sthumma@codeaurora.org>
@ 2013-08-13 11:00 ` Sujit Reddy Thumma
  2013-08-13 11:00 ` [PATCH 3/3] scsi: ufs: Add clock initialization support Sujit Reddy Thumma
  1 sibling, 0 replies; 2+ messages in thread
From: Sujit Reddy Thumma @ 2013-08-13 11:00 UTC (permalink / raw)
  To: Vinayak Holikatti, Santosh Y
  Cc: James E.J. Bottomley, linux-scsi, Sujit Reddy Thumma,
	linux-arm-msm, devicetree

UFS devices are powered by at most three external power supplies -
- VCC - The flash memory core power supply, 2.7V to 3.6V or 1.70V to 1.95V
- VCCQ - The controller and I/O power supply, 1.1V to 1.3V
- VCCQ2 - Secondary controller and/or I/O power supply, 1.65V to 1.95V

For some devices VCCQ or VCCQ2 are optional as they can be
generated using internal LDO inside the UFS device.

Add DT bindings for voltage regulators that can be controlled
from host driver.

Signed-off-by: Sujit Reddy Thumma <sthumma@codeaurora.org>
---
 .../devicetree/bindings/ufs/ufshcd-pltfrm.txt      |   18 ++
 drivers/scsi/ufs/ufs.h                             |   23 +++
 drivers/scsi/ufs/ufshcd-pltfrm.c                   |   89 +++++++++
 drivers/scsi/ufs/ufshcd.c                          |  194 +++++++++++++++++++-
 drivers/scsi/ufs/ufshcd.h                          |    3 +
 5 files changed, 325 insertions(+), 2 deletions(-)

diff --git a/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt b/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt
index 20468b2..39ee11f 100644
--- a/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt
+++ b/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt
@@ -8,9 +8,27 @@ Required properties:
 - interrupts        : <interrupt mapping for UFS host controller IRQ>
 - reg               : <registers mapping>
 
+Optional properties:
+- vcc-supply            : phandle to VCC supply regulator node
+- vccq-supply           : phandle to VCCQ supply regulator node
+- vccq2-supply          : phandle to VCCQ2 supply regulator node
+- vcc-max-microamp      : specifies max. load that can be drawn from vcc supply
+- vccq-max-microamp     : specifies max. load that can be drawn from vccq supply
+- vccq2-max-microamp    : specifies max. load that can be drawn from vccq2 supply
+
+Note: If above properties are not defined it can be assumed that the supply
+regulators are always on.
+
 Example:
 	ufshc@0xfc598000 {
 		compatible = "jedec,ufs-1.1";
 		reg = <0xfc598000 0x800>;
 		interrupts = <0 28 0>;
+
+		vcc-supply = <&xxx_reg1>;
+		vccq-supply = <&xxx_reg2>;
+		vccq2-supply = <&xxx_reg3>;
+		vcc-max-microamp = 500000;
+		vccq-max-microamp = 200000;
+		vccq2-max-microamp = 200000;
 	};
diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h
index bce09a6..f8e37bf 100644
--- a/drivers/scsi/ufs/ufs.h
+++ b/drivers/scsi/ufs/ufs.h
@@ -325,4 +325,27 @@ struct ufs_query_res {
 	struct utp_upiu_query upiu_res;
 };
 
+#define UFS_VREG_VCC_MIN	2700000 /* uV */
+#define UFS_VREG_VCC_MAX	3600000 /* uV */
+#define UFS_VREG_VCCQ_MIN	1150000 /* uV */
+#define UFS_VREG_VCCQ_MAX	1300000 /* uV */
+#define UFS_VREG_VCCQ2_MIN	1700000 /* uV */
+#define UFS_VREG_VCCQ2_MAX	1950000 /* uV */
+
+struct ufs_vreg {
+	struct regulator *reg;
+	const char *name;
+	bool enabled;
+	int min_uV;
+	int max_uV;
+	int min_uA;
+	int max_uA;
+};
+
+struct ufs_vreg_info {
+	struct ufs_vreg *vcc;
+	struct ufs_vreg *vccq;
+	struct ufs_vreg *vccq2;
+};
+
 #endif /* End of Header */
diff --git a/drivers/scsi/ufs/ufshcd-pltfrm.c b/drivers/scsi/ufs/ufshcd-pltfrm.c
index 3e0a14f..1c5290d 100644
--- a/drivers/scsi/ufs/ufshcd-pltfrm.c
+++ b/drivers/scsi/ufs/ufshcd-pltfrm.c
@@ -51,6 +51,88 @@ 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;
+	u32 temp = 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__, 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_array(np, prop_name, &temp, 1);
+	if (!ret) {
+		vreg->max_uA = temp;
+	} else {
+		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")) {
+		vreg->min_uV = UFS_VREG_VCC_MIN;
+		vreg->max_uV = UFS_VREG_VCC_MAX;
+	} else if (!strcmp(name, "vccq")) {
+		vreg->min_uV = UFS_VREG_VCCQ_MIN;
+		vreg->max_uV = UFS_VREG_VCCQ_MAX;
+	} else if (!strcmp(name, "vccq2")) {
+		vreg->min_uV = UFS_VREG_VCCQ2_MIN;
+		vreg->max_uV = UFS_VREG_VCCQ2_MAX;
+	}
+
+	goto out;
+
+out_free:
+	devm_kfree(dev, vreg);
+	vreg = NULL;
+out:
+	if (!ret)
+		*out_vreg = vreg;
+	return ret;
+}
+
+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
@@ -171,6 +253,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);
 
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index c539347..ab7b943 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -2826,6 +2826,166 @@ static struct scsi_host_template ufshcd_driver_template = {
 	.can_queue		= UFSHCD_CAN_QUEUE,
 };
 
+static int ufshcd_config_vreg(struct device *dev,
+		struct ufs_vreg *vreg, bool on)
+{
+	int ret = 0;
+	struct regulator *reg = vreg->reg;
+	const char *name = vreg->name;
+	int min_uV, max_uV, uA_load;
+
+	BUG_ON(!vreg);
+
+	min_uV = vreg->min_uV;
+	max_uV = vreg->max_uV;
+
+	if (on)
+		uA_load = vreg->max_uA;
+	else
+		uA_load = 0;
+
+	if (regulator_count_voltages(reg) > 0) {
+		ret = regulator_set_voltage(reg, min_uV, max_uV);
+		if (ret) {
+			dev_err(dev, "%s: %s set voltage failed, err=%d\n",
+					__func__, name, ret);
+			goto out;
+		}
+
+		ret = regulator_set_optimum_mode(reg, uA_load);
+		if (ret < 0) {
+			dev_err(dev, "%s: %s set optimum mode(uA_load=%d) failed, err=%d\n",
+					__func__, name, uA_load, ret);
+			goto out;
+		} else {
+			/*
+			 * regulator_set_optimum_mode() can return non zero
+			 * value even for success case.
+			 */
+			ret = 0;
+		}
+	}
+out:
+	return ret;
+}
+
+static int ufshcd_enable_vreg(struct device *dev, struct ufs_vreg *vreg)
+{
+	int ret = 0;
+
+	if (!vreg || vreg->enabled)
+		goto out;
+
+	ret = ufshcd_config_vreg(dev, vreg, true);
+	if (!ret)
+		ret = regulator_enable(vreg->reg);
+
+	if (!ret)
+		vreg->enabled = true;
+	else
+		dev_err(dev, "%s: %s enable failed, err=%d\n",
+				__func__, vreg->name, ret);
+out:
+	return ret;
+}
+
+static int ufshcd_disable_vreg(struct device *dev, struct ufs_vreg *vreg)
+{
+	int ret = 0;
+
+	if (!vreg || !vreg->enabled)
+		goto out;
+
+	ret = regulator_disable(vreg->reg);
+
+	if (!ret) {
+		/* ignore errors on applying disable config */
+		ufshcd_config_vreg(dev, vreg, false);
+		vreg->enabled = false;
+	} else {
+		dev_err(dev, "%s: %s disable failed, err=%d\n",
+				__func__, vreg->name, ret);
+	}
+out:
+	return ret;
+}
+
+static int ufshcd_setup_vreg(struct ufs_hba *hba, bool on)
+{
+	int ret = 0;
+	struct device *dev = hba->dev;
+	struct ufs_vreg_info *info = &hba->vreg_info;
+
+	if (!info)
+		goto out;
+
+	if (on) {
+		ret = ufshcd_enable_vreg(dev, info->vcc);
+		if (ret)
+			goto out;
+
+		ret = ufshcd_enable_vreg(dev, info->vccq);
+		if (ret)
+			goto out_disable_vcc;
+
+		ret = ufshcd_enable_vreg(dev, info->vccq2);
+		if (ret)
+			goto out_disable_vccq;
+	} else {
+		ufshcd_disable_vreg(dev, info->vcc);
+		ufshcd_disable_vreg(dev, info->vccq);
+		ufshcd_disable_vreg(dev, info->vccq2);
+	}
+
+	goto out;
+
+out_disable_vccq:
+	ufshcd_disable_vreg(dev, info->vccq);
+out_disable_vcc:
+	ufshcd_disable_vreg(dev, info->vcc);
+out:
+	return ret;
+}
+
+static int ufshcd_get_vreg(struct device *dev, struct ufs_vreg *vreg)
+{
+	int ret = 0;
+
+	if (!vreg)
+		goto out;
+
+	vreg->reg = devm_regulator_get(dev, vreg->name);
+	if (IS_ERR(vreg->reg)) {
+		ret = PTR_ERR(vreg->reg);
+		dev_err(dev, "%s: %s get failed, err=%d\n",
+				__func__, vreg->name, ret);
+	}
+out:
+	return ret;
+}
+
+static int ufshcd_init_vreg(struct ufs_hba *hba)
+{
+	int ret = 0;
+	struct device *dev = hba->dev;
+	struct ufs_vreg_info *info = &hba->vreg_info;
+
+	if (!info)
+		goto out;
+
+	ret = ufshcd_get_vreg(dev, info->vcc);
+	if (ret)
+		goto out;
+
+	ret = ufshcd_get_vreg(dev, info->vccq);
+	if (ret)
+		goto out;
+
+	ret = ufshcd_get_vreg(dev, info->vccq2);
+out:
+	return ret;
+}
+
 static int ufshcd_variant_hba_init(struct ufs_hba *hba)
 {
 	int err = 0;
@@ -2881,6 +3041,36 @@ static void ufshcd_variant_hba_exit(struct ufs_hba *hba)
 		hba->vops->exit(hba);
 }
 
+static int ufs_hba_init(struct ufs_hba *hba)
+{
+	int err;
+
+	err = ufshcd_init_vreg(hba);
+	if (err)
+		goto out;
+
+	err = ufshcd_setup_vreg(hba, true);
+	if (err)
+		goto out;
+
+	err = ufshcd_variant_hba_init(hba);
+	if (err)
+		goto out_disable_vreg;
+
+	goto out;
+
+out_disable_vreg:
+	ufshcd_setup_vreg(hba, false);
+out:
+	return err;
+}
+
+static void ufs_hba_exit(struct ufs_hba *hba)
+{
+	ufshcd_variant_hba_exit(hba);
+	ufshcd_setup_vreg(hba, false);
+}
+
 /**
  * ufshcd_suspend - suspend power management function
  * @hba: per adapter instance
@@ -2966,7 +3156,7 @@ void ufshcd_remove(struct ufs_hba *hba)
 
 	scsi_host_put(hba->host);
 
-	ufshcd_variant_hba_exit(hba);
+	ufs_hba_exit(hba);
 }
 EXPORT_SYMBOL_GPL(ufshcd_remove);
 
@@ -3029,7 +3219,7 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)
 	hba->mmio_base = mmio_base;
 	hba->irq = irq;
 
-	err = ufshcd_variant_hba_init(hba);
+	err = ufs_hba_init(hba);
 	if (err)
 		goto out_disable;
 
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index 72acbc7..c137ca4 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -61,6 +61,7 @@
 #include <scsi/scsi_tcq.h>
 #include <scsi/scsi_dbg.h>
 #include <scsi/scsi_eh.h>
+#include <linux/regulator/consumer.h>
 
 #include "ufs.h"
 #include "ufshci.h"
@@ -218,6 +219,7 @@ struct ufs_hba_variant_ops {
  * @saved_uic_err: sticky UIC error mask
  * @dev_cmd: ufs device management command information
  * @auto_bkops_enabled: to track whether bkops is enabled in device
+ * @vreg_info: UFS device voltage regulator information
  */
 struct ufs_hba {
 	void __iomem *mmio_base;
@@ -276,6 +278,7 @@ struct ufs_hba {
 	struct ufs_dev_cmd dev_cmd;
 
 	bool auto_bkops_enabled;
+	struct ufs_vreg_info vreg_info;
 };
 
 #define ufshcd_writel(hba, val, reg)	\
-- 
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member
of Code Aurora Forum, hosted by The Linux Foundation.


^ permalink raw reply related	[flat|nested] 2+ messages in thread

* [PATCH 3/3] scsi: ufs: Add clock initialization support
       [not found] <1376391620-13840-1-git-send-email-sthumma@codeaurora.org>
  2013-08-13 11:00 ` [PATCH 2/3] scsi: ufs: Add regulator enable support Sujit Reddy Thumma
@ 2013-08-13 11:00 ` Sujit Reddy Thumma
  1 sibling, 0 replies; 2+ messages in thread
From: Sujit Reddy Thumma @ 2013-08-13 11:00 UTC (permalink / raw)
  To: Vinayak Holikatti, Santosh Y
  Cc: James E.J. Bottomley, linux-scsi, Sujit Reddy Thumma,
	linux-arm-msm, devicetree

Add generic clock initialization support for UFSHCD platform
driver. The clock info is read from device tree using standard
clock bindings. A generic max-clock-frequency-hz property is
defined to save information on maximum operating clock frequency
the h/w supports.

Signed-off-by: Sujit Reddy Thumma <sthumma@codeaurora.org>
---
 .../devicetree/bindings/ufs/ufshcd-pltfrm.txt      |   15 +++-
 drivers/scsi/ufs/ufshcd-pltfrm.c                   |   64 ++++++++++++++
 drivers/scsi/ufs/ufshcd.c                          |   90 +++++++++++++++++++-
 drivers/scsi/ufs/ufshcd.h                          |   18 ++++
 4 files changed, 184 insertions(+), 3 deletions(-)

diff --git a/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt b/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt
index 39ee11f..d82c54c 100644
--- a/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt
+++ b/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt
@@ -16,8 +16,17 @@ Optional properties:
 - vccq-max-microamp     : specifies max. load that can be drawn from vccq supply
 - vccq2-max-microamp    : specifies max. load that can be drawn from vccq2 supply
 
+- clocks                : List of phandle and clock specifier pairs
+- clock-names           : List of clock input name strings sorted in the same
+                          order as the clocks property.
+- max-clock-frequency-hz : List of maximum operating frequency stored in the same
+                           order as the clocks property. If this property is not
+			   defined or a value in the array is "0" then it is assumed
+			   that the frequency is set by the parent clock or a
+			   fixed rate clock source.
+
 Note: If above properties are not defined it can be assumed that the supply
-regulators are always on.
+regulators or clocks are always on.
 
 Example:
 	ufshc@0xfc598000 {
@@ -31,4 +40,8 @@ Example:
 		vcc-max-microamp = 500000;
 		vccq-max-microamp = 200000;
 		vccq2-max-microamp = 200000;
+
+		clocks = <&core 0>, <&ref 0>, <&iface 0>;
+		clock-names = "core_clk", "ref_clk", "iface_clk";
+		max-clock-frequency-hz = <100000000 19200000 0>;
 	};
diff --git a/drivers/scsi/ufs/ufshcd-pltfrm.c b/drivers/scsi/ufs/ufshcd-pltfrm.c
index 1c5290d..9bbcbcb 100644
--- a/drivers/scsi/ufs/ufshcd-pltfrm.c
+++ b/drivers/scsi/ufs/ufshcd-pltfrm.c
@@ -38,6 +38,7 @@
 #include <linux/of.h>
 
 #include "ufshcd.h"
+#define MAX_CLK_CNT	100
 
 static const struct of_device_id ufs_of_match[];
 static struct ufs_hba_variant_ops *get_variant_ops(struct device *dev)
@@ -51,6 +52,63 @@ static struct ufs_hba_variant_ops *get_variant_ops(struct device *dev)
 	return NULL;
 }
 
+static int ufshcd_parse_clock_info(struct ufs_hba *hba)
+{
+	int ret = 0;
+	int cnt;
+	int i;
+	struct device *dev = hba->dev;
+	struct device_node *np = dev->of_node;
+	char *name;
+	u32 clkfreq[MAX_CLK_CNT] = {0};
+	struct ufs_clk_info *clki;
+
+	if (!np)
+		goto out;
+
+	INIT_LIST_HEAD(&hba->clk_list_head);
+
+	cnt = of_property_count_strings(np, "clock-names");
+	if (!cnt || (cnt == -EINVAL)) {
+		dev_info(dev, "%s: Unable to find clocks, assuming enabled\n",
+				__func__);
+	} else if (cnt < 0) {
+		dev_err(dev, "%s: count clock strings failed, err %d\n",
+				__func__, cnt);
+		ret = cnt;
+	}
+
+	if (cnt <= 0)
+		goto out;
+
+	ret = of_property_read_u32_array(np,
+			"max-clock-frequency-hz", clkfreq, cnt);
+	if (ret && (ret != -EINVAL)) {
+		dev_err(dev, "%s: invalid max-clock-frequency-hz property, %d\n",
+				__func__, ret);
+		goto out;
+	}
+
+	for (i = 0; i < cnt; i++) {
+		ret = of_property_read_string_index(np,
+				"clock-names", i, (const char **)&name);
+		if (ret)
+			goto out;
+
+		clki = devm_kzalloc(dev, sizeof(*clki), GFP_KERNEL);
+		if (!clki) {
+			ret = -ENOMEM;
+			goto out;
+		}
+
+		clki->max_freq = clkfreq[i];
+		clki->name = kstrdup(name, GFP_KERNEL);
+		list_add_tail(&clki->list, &hba->clk_list_head);
+	}
+out:
+	return ret;
+}
+
 #define MAX_PROP_SIZE 32
 static int ufshcd_populate_vreg(struct device *dev, const char *name,
 		struct ufs_vreg **out_vreg)
@@ -253,6 +311,12 @@ static int ufshcd_pltfrm_probe(struct platform_device *pdev)
 
 	hba->vops = get_variant_ops(&pdev->dev);
 
+	err = ufshcd_parse_clock_info(hba);
+	if (err) {
+		dev_err(&pdev->dev, "%s: clock parse failed %d\n",
+				__func__, err);
+		goto out;
+	}
 	err = ufshcd_parse_regulator_info(hba);
 	if (err) {
 		dev_err(&pdev->dev, "%s: regulator init failed %d\n",
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index ab7b943..0001941 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -2986,6 +2986,81 @@ out:
 	return ret;
 }
 
+static int ufshcd_setup_clocks(struct ufs_hba *hba, bool on)
+{
+	int ret = 0;
+	struct ufs_clk_info *clki;
+	struct list_head *head = &hba->clk_list_head;
+
+	if (!head)
+		goto out;
+
+	list_for_each_entry(clki, head, list) {
+		if (!IS_ERR_OR_NULL(clki->clk)) {
+			if (on && !clki->enabled) {
+				ret = clk_prepare_enable(clki->clk);
+				if (ret) {
+					dev_err(hba->dev, "%s: %s prepare enable failed, %d\n",
+						__func__, clki->name, ret);
+					goto out;
+				}
+				clki->enabled = true;
+			} else if (!on && clki->enabled) {
+				clk_disable_unprepare(clki->clk);
+				clki->enabled = false;
+			}
+			dev_dbg(hba->dev, "%s: clk: %s %sabled\n", __func__,
+					clki->name, on ? "en" : "dis");
+		}
+	}
+out:
+	if (ret) {
+		list_for_each_entry(clki, head, list) {
+			if (!IS_ERR_OR_NULL(clki->clk) && clki->enabled)
+				clk_disable_unprepare(clki->clk);
+		}
+	}
+	return ret;
+}
+
+static int ufshcd_init_clocks(struct ufs_hba *hba)
+{
+	int ret = 0;
+	struct ufs_clk_info *clki;
+	struct device *dev = hba->dev;
+	struct list_head *head = &hba->clk_list_head;
+
+	if (!head)
+		goto out;
+
+	list_for_each_entry(clki, head, list) {
+		if (!clki->name)
+			continue;
+
+		clki->clk = devm_clk_get(dev, clki->name);
+		if (IS_ERR(clki->clk)) {
+			ret = PTR_ERR(clki->clk);
+			dev_err(dev, "%s: %s clk get failed, %d\n",
+					__func__, clki->name, ret);
+			goto out;
+		}
+
+		if (clki->max_freq) {
+			ret = clk_set_rate(clki->clk, clki->max_freq);
+			if (ret) {
+				dev_err(hba->dev, "%s: %s clk set rate(%dHz) failed, %d\n",
+					__func__, clki->name,
+					clki->max_freq, ret);
+				goto out;
+			}
+		}
+		dev_dbg(dev, "%s: clk: %s, rate: %lu\n", __func__,
+				clki->name, clk_get_rate(clki->clk));
+	}
+out:
+	return ret;
+}
+
 static int ufshcd_variant_hba_init(struct ufs_hba *hba)
 {
 	int err = 0;
@@ -3045,14 +3120,22 @@ static int ufs_hba_init(struct ufs_hba *hba)
 {
 	int err;
 
-	err = ufshcd_init_vreg(hba);
+	err = ufshcd_init_clocks(hba);
 	if (err)
 		goto out;
 
-	err = ufshcd_setup_vreg(hba, true);
+	err = ufshcd_setup_clocks(hba, true);
 	if (err)
 		goto out;
 
+	err = ufshcd_init_vreg(hba);
+	if (err)
+		goto out_disable_clks;
+
+	err = ufshcd_setup_vreg(hba, true);
+	if (err)
+		goto out_disable_clks;
+
 	err = ufshcd_variant_hba_init(hba);
 	if (err)
 		goto out_disable_vreg;
@@ -3061,6 +3144,8 @@ static int ufs_hba_init(struct ufs_hba *hba)
 
 out_disable_vreg:
 	ufshcd_setup_vreg(hba, false);
+out_disable_clks:
+	ufshcd_setup_clocks(hba, false);
 out:
 	return err;
 }
@@ -3069,6 +3154,7 @@ static void ufs_hba_exit(struct ufs_hba *hba)
 {
 	ufshcd_variant_hba_exit(hba);
 	ufshcd_setup_vreg(hba, false);
+	ufshcd_setup_clocks(hba, false);
 }
 
 /**
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index c137ca4..8773e2b 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -155,6 +155,22 @@ struct ufs_dev_cmd {
 	struct ufs_query query;
 };
 
+/**
+ * struct ufs_clk_info - UFS clock related info
+ * @list: list headed by hba->clk_list_head
+ * @clk: clock node
+ * @name: clock name
+ * @max_freq: maximum frequency supported by the clock
+ * @enabled: variable to check against multiple enable/disable
+ */
+struct ufs_clk_info {
+	struct list_head list;
+	struct clk *clk;
+	const char *name;
+	u32 max_freq;
+	bool enabled;
+};
+
 #define PRE_CHANGE      0
 #define POST_CHANGE     1
 /**
@@ -220,6 +236,7 @@ struct ufs_hba_variant_ops {
  * @dev_cmd: ufs device management command information
  * @auto_bkops_enabled: to track whether bkops is enabled in device
  * @vreg_info: UFS device voltage regulator information
+ * @clk_list_head: UFS host controller clocks list node head
  */
 struct ufs_hba {
 	void __iomem *mmio_base;
@@ -279,6 +296,7 @@ struct ufs_hba {
 
 	bool auto_bkops_enabled;
 	struct ufs_vreg_info vreg_info;
+	struct list_head clk_list_head;
 };
 
 #define ufshcd_writel(hba, val, reg)	\
-- 
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member
of Code Aurora Forum, hosted by The Linux Foundation.


^ permalink raw reply related	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2013-08-13 11:00 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
     [not found] <1376391620-13840-1-git-send-email-sthumma@codeaurora.org>
2013-08-13 11:00 ` [PATCH 2/3] scsi: ufs: Add regulator enable support Sujit Reddy Thumma
2013-08-13 11:00 ` [PATCH 3/3] scsi: ufs: Add clock initialization support Sujit Reddy Thumma

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).