aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/scsi/ufs/ufshcd.c
diff options
context:
space:
mode:
authorSujit Reddy Thumma <sthumma@codeaurora.org>2014-09-25 15:32:23 +0300
committerChristoph Hellwig <hch@lst.de>2014-10-01 13:11:19 +0200
commitc6e79dacd86fd7ddd452fa52b3f4ca996db31e49 (patch)
treea1482fd92a23e1c34eaaaaf3a70c1339f1b81eb6 /drivers/scsi/ufs/ufshcd.c
parentufs: Add regulator enable support (diff)
downloadlinux-dev-c6e79dacd86fd7ddd452fa52b3f4ca996db31e49.tar.xz
linux-dev-c6e79dacd86fd7ddd452fa52b3f4ca996db31e49.zip
ufs: Add clock initialization support
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> Signed-off-by: Dolev Raviv <draviv@codeaurora.org> Signed-off-by: Christoph Hellwig <hch@lst.de>
Diffstat (limited to 'drivers/scsi/ufs/ufshcd.c')
-rw-r--r--drivers/scsi/ufs/ufshcd.c89
1 files changed, 87 insertions, 2 deletions
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index ef8519e70e97..b03370292070 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -3350,6 +3350,80 @@ 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 || list_empty(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;
+ }
+ } else if (!on && clki->enabled) {
+ clk_disable_unprepare(clki->clk);
+ }
+ clki->enabled = on;
+ 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 || list_empty(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;
@@ -3409,14 +3483,22 @@ static int ufshcd_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;
@@ -3425,6 +3507,8 @@ static int ufshcd_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;
}
@@ -3433,6 +3517,7 @@ static void ufshcd_hba_exit(struct ufs_hba *hba)
{
ufshcd_variant_hba_exit(hba);
ufshcd_setup_vreg(hba, false);
+ ufshcd_setup_clocks(hba, false);
}
/**