Commit 081ba802 authored by Rajendra Nayak's avatar Rajendra Nayak Committed by Stephen Boyd

clk: qcom: Add rcg ops to return floor value closest to the requested rate

The default behaviour with clk_rcg2_ops is for the
clk_round_rate()/clk_set_rate() to return/set a ceil clock
rate closest to the requested rate by looking up the corresponding
frequency table.
However, we do have some instances (mainly sdcc on various platforms)
of clients expecting a clk_set_rate() to set a floor value instead.
Add a new clk_rcg2_floor_ops to handle this for such specific
rcg instances
Signed-off-by: default avatarRajendra Nayak <rnayak@codeaurora.org>
Signed-off-by: default avatarRitesh Harjani <riteshh@codeaurora.org>
Signed-off-by: default avatarStephen Boyd <sboyd@codeaurora.org>
parent c705d22b
...@@ -173,6 +173,7 @@ struct clk_rcg2 { ...@@ -173,6 +173,7 @@ struct clk_rcg2 {
#define to_clk_rcg2(_hw) container_of(to_clk_regmap(_hw), struct clk_rcg2, clkr) #define to_clk_rcg2(_hw) container_of(to_clk_regmap(_hw), struct clk_rcg2, clkr)
extern const struct clk_ops clk_rcg2_ops; extern const struct clk_ops clk_rcg2_ops;
extern const struct clk_ops clk_rcg2_floor_ops;
extern const struct clk_ops clk_rcg2_shared_ops; extern const struct clk_ops clk_rcg2_shared_ops;
extern const struct clk_ops clk_edp_pixel_ops; extern const struct clk_ops clk_edp_pixel_ops;
extern const struct clk_ops clk_byte_ops; extern const struct clk_ops clk_byte_ops;
......
...@@ -47,6 +47,11 @@ ...@@ -47,6 +47,11 @@
#define N_REG 0xc #define N_REG 0xc
#define D_REG 0x10 #define D_REG 0x10
enum freq_policy {
FLOOR,
CEIL,
};
static int clk_rcg2_is_enabled(struct clk_hw *hw) static int clk_rcg2_is_enabled(struct clk_hw *hw)
{ {
struct clk_rcg2 *rcg = to_clk_rcg2(hw); struct clk_rcg2 *rcg = to_clk_rcg2(hw);
...@@ -176,15 +181,26 @@ clk_rcg2_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) ...@@ -176,15 +181,26 @@ clk_rcg2_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
return calc_rate(parent_rate, m, n, mode, hid_div); return calc_rate(parent_rate, m, n, mode, hid_div);
} }
static int _freq_tbl_determine_rate(struct clk_hw *hw, static int _freq_tbl_determine_rate(struct clk_hw *hw, const struct freq_tbl *f,
const struct freq_tbl *f, struct clk_rate_request *req) struct clk_rate_request *req,
enum freq_policy policy)
{ {
unsigned long clk_flags, rate = req->rate; unsigned long clk_flags, rate = req->rate;
struct clk_hw *p; struct clk_hw *p;
struct clk_rcg2 *rcg = to_clk_rcg2(hw); struct clk_rcg2 *rcg = to_clk_rcg2(hw);
int index; int index;
f = qcom_find_freq(f, rate); switch (policy) {
case FLOOR:
f = qcom_find_freq_floor(f, rate);
break;
case CEIL:
f = qcom_find_freq(f, rate);
break;
default:
return -EINVAL;
};
if (!f) if (!f)
return -EINVAL; return -EINVAL;
...@@ -221,7 +237,15 @@ static int clk_rcg2_determine_rate(struct clk_hw *hw, ...@@ -221,7 +237,15 @@ static int clk_rcg2_determine_rate(struct clk_hw *hw,
{ {
struct clk_rcg2 *rcg = to_clk_rcg2(hw); struct clk_rcg2 *rcg = to_clk_rcg2(hw);
return _freq_tbl_determine_rate(hw, rcg->freq_tbl, req); return _freq_tbl_determine_rate(hw, rcg->freq_tbl, req, CEIL);
}
static int clk_rcg2_determine_floor_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
struct clk_rcg2 *rcg = to_clk_rcg2(hw);
return _freq_tbl_determine_rate(hw, rcg->freq_tbl, req, FLOOR);
} }
static int clk_rcg2_configure(struct clk_rcg2 *rcg, const struct freq_tbl *f) static int clk_rcg2_configure(struct clk_rcg2 *rcg, const struct freq_tbl *f)
...@@ -265,12 +289,23 @@ static int clk_rcg2_configure(struct clk_rcg2 *rcg, const struct freq_tbl *f) ...@@ -265,12 +289,23 @@ static int clk_rcg2_configure(struct clk_rcg2 *rcg, const struct freq_tbl *f)
return update_config(rcg); return update_config(rcg);
} }
static int __clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate) static int __clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate,
enum freq_policy policy)
{ {
struct clk_rcg2 *rcg = to_clk_rcg2(hw); struct clk_rcg2 *rcg = to_clk_rcg2(hw);
const struct freq_tbl *f; const struct freq_tbl *f;
f = qcom_find_freq(rcg->freq_tbl, rate); switch (policy) {
case FLOOR:
f = qcom_find_freq_floor(rcg->freq_tbl, rate);
break;
case CEIL:
f = qcom_find_freq(rcg->freq_tbl, rate);
break;
default:
return -EINVAL;
};
if (!f) if (!f)
return -EINVAL; return -EINVAL;
...@@ -280,13 +315,25 @@ static int __clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate) ...@@ -280,13 +315,25 @@ static int __clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate)
static int clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate, static int clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate) unsigned long parent_rate)
{ {
return __clk_rcg2_set_rate(hw, rate); return __clk_rcg2_set_rate(hw, rate, CEIL);
}
static int clk_rcg2_set_floor_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
return __clk_rcg2_set_rate(hw, rate, FLOOR);
} }
static int clk_rcg2_set_rate_and_parent(struct clk_hw *hw, static int clk_rcg2_set_rate_and_parent(struct clk_hw *hw,
unsigned long rate, unsigned long parent_rate, u8 index) unsigned long rate, unsigned long parent_rate, u8 index)
{ {
return __clk_rcg2_set_rate(hw, rate); return __clk_rcg2_set_rate(hw, rate, CEIL);
}
static int clk_rcg2_set_floor_rate_and_parent(struct clk_hw *hw,
unsigned long rate, unsigned long parent_rate, u8 index)
{
return __clk_rcg2_set_rate(hw, rate, FLOOR);
} }
const struct clk_ops clk_rcg2_ops = { const struct clk_ops clk_rcg2_ops = {
...@@ -300,6 +347,17 @@ const struct clk_ops clk_rcg2_ops = { ...@@ -300,6 +347,17 @@ const struct clk_ops clk_rcg2_ops = {
}; };
EXPORT_SYMBOL_GPL(clk_rcg2_ops); EXPORT_SYMBOL_GPL(clk_rcg2_ops);
const struct clk_ops clk_rcg2_floor_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_rcg2_determine_floor_rate,
.set_rate = clk_rcg2_set_floor_rate,
.set_rate_and_parent = clk_rcg2_set_floor_rate_and_parent,
};
EXPORT_SYMBOL_GPL(clk_rcg2_floor_ops);
static int clk_rcg2_shared_force_enable(struct clk_hw *hw, unsigned long rate) static int clk_rcg2_shared_force_enable(struct clk_hw *hw, unsigned long rate)
{ {
struct clk_rcg2 *rcg = to_clk_rcg2(hw); struct clk_rcg2 *rcg = to_clk_rcg2(hw);
...@@ -323,7 +381,7 @@ static int clk_rcg2_shared_force_enable(struct clk_hw *hw, unsigned long rate) ...@@ -323,7 +381,7 @@ static int clk_rcg2_shared_force_enable(struct clk_hw *hw, unsigned long rate)
pr_err("%s: RCG did not turn on\n", name); pr_err("%s: RCG did not turn on\n", name);
/* set clock rate */ /* set clock rate */
ret = __clk_rcg2_set_rate(hw, rate); ret = __clk_rcg2_set_rate(hw, rate, CEIL);
if (ret) if (ret)
return ret; return ret;
......
...@@ -46,6 +46,22 @@ struct freq_tbl *qcom_find_freq(const struct freq_tbl *f, unsigned long rate) ...@@ -46,6 +46,22 @@ struct freq_tbl *qcom_find_freq(const struct freq_tbl *f, unsigned long rate)
} }
EXPORT_SYMBOL_GPL(qcom_find_freq); EXPORT_SYMBOL_GPL(qcom_find_freq);
const struct freq_tbl *qcom_find_freq_floor(const struct freq_tbl *f,
unsigned long rate)
{
const struct freq_tbl *best = NULL;
for ( ; f->freq; f++) {
if (rate >= f->freq)
best = f;
else
break;
}
return best;
}
EXPORT_SYMBOL_GPL(qcom_find_freq_floor);
int qcom_find_src_index(struct clk_hw *hw, const struct parent_map *map, u8 src) int qcom_find_src_index(struct clk_hw *hw, const struct parent_map *map, u8 src)
{ {
int i, num_parents = clk_hw_get_num_parents(hw); int i, num_parents = clk_hw_get_num_parents(hw);
......
...@@ -41,6 +41,8 @@ struct qcom_cc_desc { ...@@ -41,6 +41,8 @@ struct qcom_cc_desc {
extern const struct freq_tbl *qcom_find_freq(const struct freq_tbl *f, extern const struct freq_tbl *qcom_find_freq(const struct freq_tbl *f,
unsigned long rate); unsigned long rate);
extern const struct freq_tbl *qcom_find_freq_floor(const struct freq_tbl *f,
unsigned long rate);
extern void extern void
qcom_pll_set_fsm_mode(struct regmap *m, u32 reg, u8 bias_count, u8 lock_count); qcom_pll_set_fsm_mode(struct regmap *m, u32 reg, u8 bias_count, u8 lock_count);
extern int qcom_find_src_index(struct clk_hw *hw, const struct parent_map *map, extern int qcom_find_src_index(struct clk_hw *hw, const struct parent_map *map,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment