summaryrefslogtreecommitdiffstats
path: root/target/linux/brcm2708/patches-4.4/0253-clk-bcm2835-Support-for-clock-parent-selection.patch
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/brcm2708/patches-4.4/0253-clk-bcm2835-Support-for-clock-parent-selection.patch')
-rw-r--r--target/linux/brcm2708/patches-4.4/0253-clk-bcm2835-Support-for-clock-parent-selection.patch188
1 files changed, 188 insertions, 0 deletions
diff --git a/target/linux/brcm2708/patches-4.4/0253-clk-bcm2835-Support-for-clock-parent-selection.patch b/target/linux/brcm2708/patches-4.4/0253-clk-bcm2835-Support-for-clock-parent-selection.patch
new file mode 100644
index 0000000000..68af214691
--- /dev/null
+++ b/target/linux/brcm2708/patches-4.4/0253-clk-bcm2835-Support-for-clock-parent-selection.patch
@@ -0,0 +1,188 @@
+From f875d2424d83a76d4b3942263291917853d56158 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/304] 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
+@@ -1201,16 +1201,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)
+ {
+@@ -1282,13 +1272,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)
+@@ -1304,7 +1356,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,
+@@ -1398,45 +1452,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;
+