diff options
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.patch | 188 |
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 0000000..68af214 --- /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; + |