123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188 |
- From 8578eeecb147439b7286e14cd0ce8a5078851f56 Mon Sep 17 00:00:00 2001
- From: Remi Pommarel <repk@triplefau.lt>
- Date: Sun, 6 Dec 2015 17:22:47 +0100
- Subject: [PATCH 253/381] clk: bcm2835: Support for clock parent selection
- Some bcm2835 clocks used by hardware (like "PWM" or "H264") can have multiple
- parent clocks. These clocks divide the rate of a parent which can be selected by
- setting the proper bits in the clock control register.
- Previously all these parents where handled by a mux clock. But a mux clock
- cannot be used because updating clock control register to select parent needs a
- password to be xor'd with the parent index.
- This patch get rid of mux clock and make these clocks handle their own parent,
- allowing them to select the one to use.
- Signed-off-by: Remi Pommarel <repk@triplefau.lt>
- Reviewed-by: Eric Anholt <eric@anholt.net>
- Signed-off-by: Michael Turquette <mturquette@baylibre.com>
- (cherry picked from commit 6d18b8adbe679b5947aa822b676efff230acc5f6)
- ---
- drivers/clk/bcm/clk-bcm2835.c | 122 ++++++++++++++++++++++++++----------------
- 1 file changed, 77 insertions(+), 45 deletions(-)
- --- a/drivers/clk/bcm/clk-bcm2835.c
- +++ b/drivers/clk/bcm/clk-bcm2835.c
- @@ -1216,16 +1216,6 @@ static long bcm2835_clock_rate_from_divi
- return temp;
- }
-
- -static long bcm2835_clock_round_rate(struct clk_hw *hw,
- - unsigned long rate,
- - unsigned long *parent_rate)
- -{
- - struct bcm2835_clock *clock = bcm2835_clock_from_hw(hw);
- - u32 div = bcm2835_clock_choose_div(hw, rate, *parent_rate, false);
- -
- - return bcm2835_clock_rate_from_divisor(clock, *parent_rate, div);
- -}
- -
- static unsigned long bcm2835_clock_get_rate(struct clk_hw *hw,
- unsigned long parent_rate)
- {
- @@ -1297,13 +1287,75 @@ static int bcm2835_clock_set_rate(struct
- return 0;
- }
-
- +static int bcm2835_clock_determine_rate(struct clk_hw *hw,
- + struct clk_rate_request *req)
- +{
- + struct bcm2835_clock *clock = bcm2835_clock_from_hw(hw);
- + struct clk_hw *parent, *best_parent = NULL;
- + unsigned long rate, best_rate = 0;
- + unsigned long prate, best_prate = 0;
- + size_t i;
- + u32 div;
- +
- + /*
- + * Select parent clock that results in the closest but lower rate
- + */
- + for (i = 0; i < clk_hw_get_num_parents(hw); ++i) {
- + parent = clk_hw_get_parent_by_index(hw, i);
- + if (!parent)
- + continue;
- + prate = clk_hw_get_rate(parent);
- + div = bcm2835_clock_choose_div(hw, req->rate, prate, true);
- + rate = bcm2835_clock_rate_from_divisor(clock, prate, div);
- + if (rate > best_rate && rate <= req->rate) {
- + best_parent = parent;
- + best_prate = prate;
- + best_rate = rate;
- + }
- + }
- +
- + if (!best_parent)
- + return -EINVAL;
- +
- + req->best_parent_hw = best_parent;
- + req->best_parent_rate = best_prate;
- +
- + req->rate = best_rate;
- +
- + return 0;
- +}
- +
- +static int bcm2835_clock_set_parent(struct clk_hw *hw, u8 index)
- +{
- + struct bcm2835_clock *clock = bcm2835_clock_from_hw(hw);
- + struct bcm2835_cprman *cprman = clock->cprman;
- + const struct bcm2835_clock_data *data = clock->data;
- + u8 src = (index << CM_SRC_SHIFT) & CM_SRC_MASK;
- +
- + cprman_write(cprman, data->ctl_reg, src);
- + return 0;
- +}
- +
- +static u8 bcm2835_clock_get_parent(struct clk_hw *hw)
- +{
- + struct bcm2835_clock *clock = bcm2835_clock_from_hw(hw);
- + struct bcm2835_cprman *cprman = clock->cprman;
- + const struct bcm2835_clock_data *data = clock->data;
- + u32 src = cprman_read(cprman, data->ctl_reg);
- +
- + return (src & CM_SRC_MASK) >> CM_SRC_SHIFT;
- +}
- +
- +
- static const struct clk_ops bcm2835_clock_clk_ops = {
- .is_prepared = bcm2835_clock_is_on,
- .prepare = bcm2835_clock_on,
- .unprepare = bcm2835_clock_off,
- .recalc_rate = bcm2835_clock_get_rate,
- .set_rate = bcm2835_clock_set_rate,
- - .round_rate = bcm2835_clock_round_rate,
- + .determine_rate = bcm2835_clock_determine_rate,
- + .set_parent = bcm2835_clock_set_parent,
- + .get_parent = bcm2835_clock_get_parent,
- };
-
- static int bcm2835_vpu_clock_is_on(struct clk_hw *hw)
- @@ -1319,7 +1371,9 @@ static const struct clk_ops bcm2835_vpu_
- .is_prepared = bcm2835_vpu_clock_is_on,
- .recalc_rate = bcm2835_clock_get_rate,
- .set_rate = bcm2835_clock_set_rate,
- - .round_rate = bcm2835_clock_round_rate,
- + .determine_rate = bcm2835_clock_determine_rate,
- + .set_parent = bcm2835_clock_set_parent,
- + .get_parent = bcm2835_clock_get_parent,
- };
-
- static struct clk *bcm2835_register_pll(struct bcm2835_cprman *cprman,
- @@ -1413,45 +1467,23 @@ static struct clk *bcm2835_register_cloc
- {
- struct bcm2835_clock *clock;
- struct clk_init_data init;
- - const char *parent;
- + const char *parents[1 << CM_SRC_BITS];
- + size_t i;
-
- /*
- - * Most of the clock generators have a mux field, so we
- - * instantiate a generic mux as our parent to handle it.
- + * Replace our "xosc" references with the oscillator's
- + * actual name.
- */
- - if (data->num_mux_parents) {
- - const char *parents[1 << CM_SRC_BITS];
- - int i;
- -
- - parent = devm_kasprintf(cprman->dev, GFP_KERNEL,
- - "mux_%s", data->name);
- - if (!parent)
- - return NULL;
- -
- - /*
- - * Replace our "xosc" references with the oscillator's
- - * actual name.
- - */
- - for (i = 0; i < data->num_mux_parents; i++) {
- - if (strcmp(data->parents[i], "xosc") == 0)
- - parents[i] = cprman->osc_name;
- - else
- - parents[i] = data->parents[i];
- - }
- -
- - clk_register_mux(cprman->dev, parent,
- - parents, data->num_mux_parents,
- - CLK_SET_RATE_PARENT,
- - cprman->regs + data->ctl_reg,
- - CM_SRC_SHIFT, CM_SRC_BITS,
- - 0, &cprman->regs_lock);
- - } else {
- - parent = data->parents[0];
- + for (i = 0; i < data->num_mux_parents; i++) {
- + if (strcmp(data->parents[i], "xosc") == 0)
- + parents[i] = cprman->osc_name;
- + else
- + parents[i] = data->parents[i];
- }
-
- memset(&init, 0, sizeof(init));
- - init.parent_names = &parent;
- - init.num_parents = 1;
- + init.parent_names = parents;
- + init.num_parents = data->num_mux_parents;
- init.name = data->name;
- init.flags = CLK_IGNORE_UNUSED;
-
|