summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorLinux Build Service Account <lnxbuild@localhost>2016-10-06 01:07:14 -0700
committerGerrit - the friendly Code Review server <code-review@localhost>2016-10-06 01:07:14 -0700
commit3ea0533e231fd8426736b9e0043c8f2546237b2b (patch)
treebfeeab3749adc871cebab56d6af41b7db38e59c4 /drivers
parentb3cc3d0447c082f6402ce9a6ac81b67ac8237830 (diff)
parentd3d0687504251c0abfc4557c7b530e94a4f6f466 (diff)
Merge "clk: qcom: Add support for gfx clock to ping pong PLLs"
Diffstat (limited to 'drivers')
-rw-r--r--drivers/clk/qcom/clk-rcg.h2
-rw-r--r--drivers/clk/qcom/clk-rcg2.c78
2 files changed, 80 insertions, 0 deletions
diff --git a/drivers/clk/qcom/clk-rcg.h b/drivers/clk/qcom/clk-rcg.h
index e3760969848d..da02ab499bff 100644
--- a/drivers/clk/qcom/clk-rcg.h
+++ b/drivers/clk/qcom/clk-rcg.h
@@ -23,6 +23,7 @@ struct freq_tbl {
u8 pre_div;
u16 m;
u16 n;
+ unsigned long src_freq;
};
/**
@@ -184,5 +185,6 @@ extern const struct clk_ops clk_byte_ops;
extern const struct clk_ops clk_byte2_ops;
extern const struct clk_ops clk_pixel_ops;
extern const struct clk_ops clk_gfx3d_ops;
+extern const struct clk_ops clk_gfx3d_src_ops;
#endif
diff --git a/drivers/clk/qcom/clk-rcg2.c b/drivers/clk/qcom/clk-rcg2.c
index 4d5081c2b6d1..a075859771d3 100644
--- a/drivers/clk/qcom/clk-rcg2.c
+++ b/drivers/clk/qcom/clk-rcg2.c
@@ -16,6 +16,7 @@
#include <linux/err.h>
#include <linux/bug.h>
#include <linux/export.h>
+#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/delay.h>
#include <linux/regmap.h>
@@ -865,3 +866,80 @@ const struct clk_ops clk_gfx3d_ops = {
.determine_rate = clk_gfx3d_determine_rate,
};
EXPORT_SYMBOL_GPL(clk_gfx3d_ops);
+
+static int clk_gfx3d_src_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct clk_rate_request parent_req = { };
+ struct clk_hw *p1, *p3, *xo, *curr_p;
+ const struct freq_tbl *f;
+ int ret;
+
+ xo = clk_hw_get_parent_by_index(hw, 0);
+ if (req->rate == clk_hw_get_rate(xo)) {
+ req->best_parent_hw = xo;
+ req->best_parent_rate = req->rate;
+ return 0;
+ }
+
+ f = qcom_find_freq(rcg->freq_tbl, req->rate);
+ if (!f || (req->rate != f->freq))
+ return -EINVAL;
+
+ /* Indexes of source from the parent map */
+ p1 = clk_hw_get_parent_by_index(hw, 1);
+ p3 = clk_hw_get_parent_by_index(hw, 2);
+
+ curr_p = clk_hw_get_parent(hw);
+ parent_req.rate = f->src_freq;
+
+ if (curr_p == xo || curr_p == p3)
+ req->best_parent_hw = p1;
+ else if (curr_p == p1)
+ req->best_parent_hw = p3;
+
+ parent_req.best_parent_hw = req->best_parent_hw;
+
+ ret = __clk_determine_rate(req->best_parent_hw, &parent_req);
+ if (ret)
+ return ret;
+
+ req->best_parent_rate = parent_req.rate;
+
+ return 0;
+}
+
+static int clk_gfx3d_src_set_rate_and_parent(struct clk_hw *hw,
+ unsigned long rate, unsigned long parent_rate, u8 index)
+{
+ struct clk_rcg2 *rcg = to_clk_rcg2(hw);
+ const struct freq_tbl *f;
+ u32 cfg;
+ int ret;
+
+ cfg = rcg->parent_map[index].cfg << CFG_SRC_SEL_SHIFT;
+
+ f = qcom_find_freq(rcg->freq_tbl, rate);
+ if (!f)
+ return -EINVAL;
+
+ /* Update the RCG-DIV */
+ cfg |= f->pre_div << CFG_SRC_DIV_SHIFT;
+
+ ret = regmap_write(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, cfg);
+ if (ret)
+ return ret;
+
+ return update_config(rcg);
+}
+
+const struct clk_ops clk_gfx3d_src_ops = {
+ .is_enabled = clk_rcg2_is_enabled,
+ .get_parent = clk_rcg2_get_parent,
+ .set_parent = clk_rcg2_set_parent,
+ .recalc_rate = clk_rcg2_recalc_rate,
+ .set_rate = clk_gfx3d_set_rate,
+ .set_rate_and_parent = clk_gfx3d_src_set_rate_and_parent,
+ .determine_rate = clk_gfx3d_src_determine_rate,
+};
+EXPORT_SYMBOL_GPL(clk_gfx3d_src_ops);