You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
188 lines
5.7 KiB
188 lines
5.7 KiB
From 29085f56d20e9c563b183a003f39f7cfc7884b17 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/423] 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;
|
|
|
|
|