diff options
author | Linux Build Service Account <lnxbuild@localhost> | 2016-10-06 01:07:14 -0700 |
---|---|---|
committer | Gerrit - the friendly Code Review server <code-review@localhost> | 2016-10-06 01:07:14 -0700 |
commit | 3ea0533e231fd8426736b9e0043c8f2546237b2b (patch) | |
tree | bfeeab3749adc871cebab56d6af41b7db38e59c4 /drivers | |
parent | b3cc3d0447c082f6402ce9a6ac81b67ac8237830 (diff) | |
parent | d3d0687504251c0abfc4557c7b530e94a4f6f466 (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.h | 2 | ||||
-rw-r--r-- | drivers/clk/qcom/clk-rcg2.c | 78 |
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); |