diff options
Diffstat (limited to 'drivers/clk/qcom/clk-rcg2.c')
| -rw-r--r-- | drivers/clk/qcom/clk-rcg2.c | 73 |
1 files changed, 73 insertions, 0 deletions
diff --git a/drivers/clk/qcom/clk-rcg2.c b/drivers/clk/qcom/clk-rcg2.c index c9ba7f97eebe..ff0c8327fabe 100644 --- a/drivers/clk/qcom/clk-rcg2.c +++ b/drivers/clk/qcom/clk-rcg2.c @@ -580,6 +580,9 @@ static int __clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate) rcg->curr_index = 0; else { f = qcom_find_freq(rcg->freq_tbl, rcg->current_freq); + if (!f) + return -EINVAL; + rcg->curr_index = qcom_find_src_index(hw, rcg->parent_map, f->src); @@ -1346,3 +1349,73 @@ const struct clk_ops clk_gfx3d_src_ops = { .list_registers = clk_rcg2_list_registers, }; EXPORT_SYMBOL_GPL(clk_gfx3d_src_ops); + +static int clk_esc_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + struct clk_rcg2 *rcg = to_clk_rcg2(hw); + unsigned long parent_rate, div; + u32 mask = BIT(rcg->hid_width) - 1; + struct clk_hw *p; + unsigned long rate = req->rate; + + if (rate == 0) + return -EINVAL; + + p = req->best_parent_hw; + req->best_parent_rate = parent_rate = clk_hw_round_rate(p, rate); + + div = ((2 * parent_rate) / rate) - 1; + div = min_t(u32, div, mask); + + req->rate = calc_rate(parent_rate, 0, 0, 0, div); + + return 0; +} + +static int clk_esc_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_rcg2 *rcg = to_clk_rcg2(hw); + struct freq_tbl f = { 0 }; + unsigned long div; + int i, num_parents = clk_hw_get_num_parents(hw); + u32 mask = BIT(rcg->hid_width) - 1; + u32 cfg; + + div = ((2 * parent_rate) / rate) - 1; + div = min_t(u32, div, mask); + + f.pre_div = div; + + regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, &cfg); + cfg &= CFG_SRC_SEL_MASK; + cfg >>= CFG_SRC_SEL_SHIFT; + + for (i = 0; i < num_parents; i++) { + if (cfg == rcg->parent_map[i].cfg) { + f.src = rcg->parent_map[i].src; + return clk_rcg2_configure(rcg, &f); + } + } + + return -EINVAL; +} + +static int clk_esc_set_rate_and_parent(struct clk_hw *hw, + unsigned long rate, unsigned long parent_rate, u8 index) +{ + return clk_esc_set_rate(hw, rate, parent_rate); +} + +const struct clk_ops clk_esc_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, + .determine_rate = clk_esc_determine_rate, + .set_rate = clk_esc_set_rate, + .set_rate_and_parent = clk_esc_set_rate_and_parent, + .list_registers = clk_rcg2_list_registers, +}; +EXPORT_SYMBOL(clk_esc_ops); |
