Skip to content

Commit

Permalink
clk: qcom: clk-rcg: Add customized clk_ops for DSI RCGs
Browse files Browse the repository at this point in the history
DSI specific RCG clocks required customized clk_ops. There are
a total of 4 RCGs per DSI block: DSI, BYTE, ESC and PIXEL.

There are a total of 2 clocks coming from the DSI PLL, which serve as
inputs to these RCGs. The BYTE and ESC RCGs are fed by one of the
post dividers of DSI1 or DSI2 PLLs, and the DSI and PIXEL RCGs are fed by
another divider of the PLL.

In each of the 2 groups above, only one of the clocks sets its parent.
These are BYTE RCG and DSI RCG for each of the groups respectively, as
shown in the diagram below.

The DSI and BYTE RCGs serve as bypass clocks. We create a new set of ops
clk_rcg_bypass2_ops, which are like the regular bypass ops, but don't
take in a freq table, since the DSI driver using these clocks is
parent-able.

The PIXEL RCG needs to derive the required pixel clock using dsixpll.
It parses a m/n frac table to retrieve the correct clock.

The ESC RCG doesn't have a frac M/N block, it can just apply a pre-
divider. Its ops simply check if the required clock rate can be
achieved by the pre-divider.

      +-------------------+
      |                   |---dsixpllbyte---o---> To byte RCG
      |                   |                 | (sets parent rate)
      |                   |                 |
      |                   |                 |
      |    DSI 1/2 PLL    |                 |
      |                   |                 o---> To esc RCG
      |                   |                 (doesn't set parent rate)
      |                   |
      |                   |----dsixpll-----o---> To dsi RCG
      +-------------------+                | (sets parent rate)
                             ( x = 1, 2 )  |
                                           |
                                           o---> To pixel rcg
                                           (doesn't set parent rate)

Signed-off-by: Archit Taneja <architt@codeaurora.org>
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
  • Loading branch information
boddob authored and bebarino committed Oct 16, 2015
1 parent b1a0eeb commit d8aa2be
Show file tree
Hide file tree
Showing 2 changed files with 233 additions and 0 deletions.
230 changes: 230 additions & 0 deletions drivers/clk/qcom/clk-rcg.c
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,200 @@ static int clk_rcg_bypass_set_rate(struct clk_hw *hw, unsigned long rate,
return __clk_rcg_set_rate(rcg, rcg->freq_tbl);
}

static int clk_rcg_bypass2_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
struct clk_hw *p;

p = req->best_parent_hw;
req->best_parent_rate = clk_hw_round_rate(p, req->rate);
req->rate = req->best_parent_rate;

return 0;
}

static int clk_rcg_bypass2_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct clk_rcg *rcg = to_clk_rcg(hw);
struct freq_tbl f = { 0 };
u32 ns, src;
int i, ret, num_parents = clk_hw_get_num_parents(hw);

ret = regmap_read(rcg->clkr.regmap, rcg->ns_reg, &ns);
if (ret)
return ret;

src = ns_to_src(&rcg->s, ns);
f.pre_div = ns_to_pre_div(&rcg->p, ns) + 1;

for (i = 0; i < num_parents; i++) {
if (src == rcg->s.parent_map[i].cfg) {
f.src = rcg->s.parent_map[i].src;
return __clk_rcg_set_rate(rcg, &f);
}
}

return -EINVAL;
}

static int clk_rcg_bypass2_set_rate_and_parent(struct clk_hw *hw,
unsigned long rate, unsigned long parent_rate, u8 index)
{
/* Read the hardware to determine parent during set_rate */
return clk_rcg_bypass2_set_rate(hw, rate, parent_rate);
}

struct frac_entry {
int num;
int den;
};

static const struct frac_entry pixel_table[] = {
{ 1, 2 },
{ 1, 3 },
{ 3, 16 },
{ }
};

static int clk_rcg_pixel_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
int delta = 100000;
const struct frac_entry *frac = pixel_table;
unsigned long request, src_rate;

for (; frac->num; frac++) {
request = (req->rate * frac->den) / frac->num;

src_rate = clk_hw_round_rate(req->best_parent_hw, request);

if ((src_rate < (request - delta)) ||
(src_rate > (request + delta)))
continue;

req->best_parent_rate = src_rate;
req->rate = (src_rate * frac->num) / frac->den;
return 0;
}

return -EINVAL;
}

static int clk_rcg_pixel_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct clk_rcg *rcg = to_clk_rcg(hw);
int delta = 100000;
const struct frac_entry *frac = pixel_table;
unsigned long request;
struct freq_tbl f = { 0 };
u32 ns, src;
int i, ret, num_parents = clk_hw_get_num_parents(hw);

ret = regmap_read(rcg->clkr.regmap, rcg->ns_reg, &ns);
if (ret)
return ret;

src = ns_to_src(&rcg->s, ns);
f.pre_div = ns_to_pre_div(&rcg->p, ns) + 1;

for (i = 0; i < num_parents; i++) {
if (src == rcg->s.parent_map[i].cfg) {
f.src = rcg->s.parent_map[i].src;
break;
}
}

/* let us find appropriate m/n values for this */
for (; frac->num; frac++) {
request = (rate * frac->den) / frac->num;

if ((parent_rate < (request - delta)) ||
(parent_rate > (request + delta)))
continue;

f.m = frac->num;
f.n = frac->den;

return __clk_rcg_set_rate(rcg, &f);
}

return -EINVAL;
}

static int clk_rcg_pixel_set_rate_and_parent(struct clk_hw *hw,
unsigned long rate, unsigned long parent_rate, u8 index)
{
return clk_rcg_pixel_set_rate(hw, rate, parent_rate);
}

static int clk_rcg_esc_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
struct clk_rcg *rcg = to_clk_rcg(hw);
int pre_div_max = BIT(rcg->p.pre_div_width);
int div;
unsigned long src_rate;

if (req->rate == 0)
return -EINVAL;

src_rate = clk_hw_get_rate(req->best_parent_hw);

div = src_rate / req->rate;

if (div >= 1 && div <= pre_div_max) {
req->best_parent_rate = src_rate;
req->rate = src_rate / div;
return 0;
}

return -EINVAL;
}

static int clk_rcg_esc_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct clk_rcg *rcg = to_clk_rcg(hw);
struct freq_tbl f = { 0 };
int pre_div_max = BIT(rcg->p.pre_div_width);
int div;
u32 ns;
int i, ret, num_parents = clk_hw_get_num_parents(hw);

if (rate == 0)
return -EINVAL;

ret = regmap_read(rcg->clkr.regmap, rcg->ns_reg, &ns);
if (ret)
return ret;

ns = ns_to_src(&rcg->s, ns);

for (i = 0; i < num_parents; i++) {
if (ns == rcg->s.parent_map[i].cfg) {
f.src = rcg->s.parent_map[i].src;
break;
}
}

div = parent_rate / rate;

if (div >= 1 && div <= pre_div_max) {
f.pre_div = div;
return __clk_rcg_set_rate(rcg, &f);
}

return -EINVAL;
}

static int clk_rcg_esc_set_rate_and_parent(struct clk_hw *hw,
unsigned long rate, unsigned long parent_rate, u8 index)
{
return clk_rcg_esc_set_rate(hw, rate, parent_rate);
}

/*
* This type of clock has a glitch-free mux that switches between the output of
* the M/N counter and an always on clock source (XO). When clk_set_rate() is
Expand Down Expand Up @@ -639,6 +833,42 @@ const struct clk_ops clk_rcg_bypass_ops = {
};
EXPORT_SYMBOL_GPL(clk_rcg_bypass_ops);

const struct clk_ops clk_rcg_bypass2_ops = {
.enable = clk_enable_regmap,
.disable = clk_disable_regmap,
.get_parent = clk_rcg_get_parent,
.set_parent = clk_rcg_set_parent,
.recalc_rate = clk_rcg_recalc_rate,
.determine_rate = clk_rcg_bypass2_determine_rate,
.set_rate = clk_rcg_bypass2_set_rate,
.set_rate_and_parent = clk_rcg_bypass2_set_rate_and_parent,
};
EXPORT_SYMBOL_GPL(clk_rcg_bypass2_ops);

const struct clk_ops clk_rcg_pixel_ops = {
.enable = clk_enable_regmap,
.disable = clk_disable_regmap,
.get_parent = clk_rcg_get_parent,
.set_parent = clk_rcg_set_parent,
.recalc_rate = clk_rcg_recalc_rate,
.determine_rate = clk_rcg_pixel_determine_rate,
.set_rate = clk_rcg_pixel_set_rate,
.set_rate_and_parent = clk_rcg_pixel_set_rate_and_parent,
};
EXPORT_SYMBOL_GPL(clk_rcg_pixel_ops);

const struct clk_ops clk_rcg_esc_ops = {
.enable = clk_enable_regmap,
.disable = clk_disable_regmap,
.get_parent = clk_rcg_get_parent,
.set_parent = clk_rcg_set_parent,
.recalc_rate = clk_rcg_recalc_rate,
.determine_rate = clk_rcg_esc_determine_rate,
.set_rate = clk_rcg_esc_set_rate,
.set_rate_and_parent = clk_rcg_esc_set_rate_and_parent,
};
EXPORT_SYMBOL_GPL(clk_rcg_esc_ops);

const struct clk_ops clk_rcg_lcc_ops = {
.enable = clk_rcg_lcc_enable,
.disable = clk_rcg_lcc_disable,
Expand Down
3 changes: 3 additions & 0 deletions drivers/clk/qcom/clk-rcg.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,9 @@ struct clk_rcg {

extern const struct clk_ops clk_rcg_ops;
extern const struct clk_ops clk_rcg_bypass_ops;
extern const struct clk_ops clk_rcg_bypass2_ops;
extern const struct clk_ops clk_rcg_pixel_ops;
extern const struct clk_ops clk_rcg_esc_ops;
extern const struct clk_ops clk_rcg_lcc_ops;

#define to_clk_rcg(_hw) container_of(to_clk_regmap(_hw), struct clk_rcg, clkr)
Expand Down

0 comments on commit d8aa2be

Please sign in to comment.