Loading drivers/clk/clk.c +30 −9 Original line number Diff line number Diff line Loading @@ -1488,7 +1488,7 @@ static struct clk_core *clk_propagate_rate_change(struct clk_core *core, * walk down a subtree and set the new rates notifying the rate * change on the way */ static void clk_change_rate(struct clk_core *core) static int clk_change_rate(struct clk_core *core) { struct clk_core *child; struct hlist_node *tmp; Loading @@ -1497,6 +1497,7 @@ static void clk_change_rate(struct clk_core *core) bool skip_set_rate = false; struct clk_core *old_parent; struct clk_core *parent = NULL; int rc = 0; old_rate = core->rate; Loading Loading @@ -1539,8 +1540,13 @@ static void clk_change_rate(struct clk_core *core) trace_clk_set_rate(core, core->new_rate); if (!skip_set_rate && core->ops->set_rate) core->ops->set_rate(core->hw, core->new_rate, best_parent_rate); if (!skip_set_rate && core->ops->set_rate) { rc = core->ops->set_rate(core->hw, core->new_rate, best_parent_rate); if (rc) goto out; } trace_clk_set_rate_complete(core, core->new_rate); Loading Loading @@ -1572,12 +1578,20 @@ static void clk_change_rate(struct clk_core *core) /* Skip children who will be reparented to another clock */ if (child->new_parent && child->new_parent != core) continue; clk_change_rate(child); rc = clk_change_rate(child); if (rc) return rc; } /* handle the new child who might not be in core->children yet */ if (core->new_child) clk_change_rate(core->new_child); rc = clk_change_rate(core->new_child); return rc; out: trace_clk_set_rate_complete(core, core->new_rate); return rc; } static int clk_core_set_rate_nolock(struct clk_core *core, Loading @@ -1585,6 +1599,7 @@ static int clk_core_set_rate_nolock(struct clk_core *core, { struct clk_core *top, *fail_clk; unsigned long rate = req_rate; int ret = 0; if (!core) return 0; Loading @@ -1604,18 +1619,24 @@ static int clk_core_set_rate_nolock(struct clk_core *core, /* notify that we are about to change rates */ fail_clk = clk_propagate_rate_change(top, PRE_RATE_CHANGE); if (fail_clk) { pr_debug("%s: failed to set %s rate\n", __func__, fail_clk->name); pr_debug("%s: failed to set %s clock to run at %lu\n", __func__, fail_clk->name, req_rate); clk_propagate_rate_change(top, ABORT_RATE_CHANGE); return -EBUSY; } /* change the rates */ clk_change_rate(top); ret = clk_change_rate(top); if (ret) { pr_err("%s: failed to set %s clock to run at %lu\n", __func__, top->name, req_rate); clk_propagate_rate_change(top, ABORT_RATE_CHANGE); return ret; } core->req_rate = req_rate; return 0; return ret; } /** Loading drivers/clk/qcom/clk-branch.c +117 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ #include <linux/err.h> #include <linux/delay.h> #include <linux/export.h> #include <linux/clk.h> #include <linux/clk-provider.h> #include <linux/regmap.h> #include <linux/clk/qcom.h> Loading Loading @@ -182,20 +183,136 @@ const struct clk_ops clk_branch_ops = { }; EXPORT_SYMBOL_GPL(clk_branch_ops); static int clk_branch2_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { struct clk_branch *branch = to_clk_branch(hw); struct clk_hw *parent = clk_hw_get_parent(hw); unsigned long curr_rate, new_rate, other_rate = 0; int ret = 0; if (!parent) return -EPERM; if (!branch->aggr_sibling_rates || !clk_hw_is_prepared(hw)) { branch->rate = rate; return 0; } other_rate = clk_aggregate_rate(hw, parent->core); curr_rate = max(other_rate, branch->rate); new_rate = max(other_rate, rate); if (new_rate != curr_rate) { ret = clk_set_rate(parent->clk, new_rate); if (ret) { pr_err("Failed to scale %s to %lu\n", clk_hw_get_name(parent), new_rate); goto err; } } branch->rate = rate; err: return ret; } static long clk_branch2_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *parent_rate) { struct clk_hw *parent = clk_hw_get_parent(hw); unsigned long rrate = 0; if (!parent) return -EPERM; rrate = clk_hw_round_rate(parent, rate); /* * If the rounded rate that's returned is valid, update the parent_rate * field so that the set_rate() call can be propagated to the parent. */ if (rrate > 0) *parent_rate = rrate; else pr_warn("Failed to get the parent's (%s) rounded rate\n", clk_hw_get_name(parent)); return rrate; } static unsigned long clk_branch2_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { return to_clk_branch(hw)->rate; } static int clk_branch2_enable(struct clk_hw *hw) { return clk_branch_toggle(hw, true, clk_branch2_check_halt); } static int clk_branch2_prepare(struct clk_hw *hw) { struct clk_branch *branch = to_clk_branch(hw); struct clk_hw *parent = clk_hw_get_parent(hw); unsigned long curr_rate, branch_rate = branch->rate; int ret = 0; if (!parent) return -EPERM; /* * Do the rate aggregation and scaling of the RCG in the prepare/ * unprepare functions to avoid potential RPM(/h) communication due to * votes on the voltage rails. */ if (branch->aggr_sibling_rates) { curr_rate = clk_aggregate_rate(hw, parent->core); if (branch_rate > curr_rate) { ret = clk_set_rate(parent->clk, branch_rate); if (ret) { pr_err("Failed to scale %s to %lu\n", clk_hw_get_name(parent), branch_rate); goto exit; } } } exit: return ret; } static void clk_branch2_disable(struct clk_hw *hw) { clk_branch_toggle(hw, false, clk_branch2_check_halt); } static void clk_branch2_unprepare(struct clk_hw *hw) { struct clk_branch *branch = to_clk_branch(hw); struct clk_hw *parent = clk_hw_get_parent(hw); unsigned long curr_rate, new_rate, branch_rate = branch->rate; if (!parent) return; if (branch->aggr_sibling_rates) { new_rate = clk_aggregate_rate(hw, parent->core); curr_rate = max(new_rate, branch_rate); if (new_rate < curr_rate) if (clk_set_rate(parent->clk, new_rate)) pr_err("Failed to scale %s to %lu\n", clk_hw_get_name(parent), new_rate); } } const struct clk_ops clk_branch2_ops = { .prepare = clk_branch2_prepare, .enable = clk_branch2_enable, .unprepare = clk_branch2_unprepare, .disable = clk_branch2_disable, .is_enabled = clk_is_enabled_regmap, .set_rate = clk_branch2_set_rate, .round_rate = clk_branch2_round_rate, .recalc_rate = clk_branch2_recalc_rate, .set_flags = clk_branch_set_flags, }; EXPORT_SYMBOL_GPL(clk_branch2_ops); Loading drivers/clk/qcom/clk-branch.h +4 −0 Original line number Diff line number Diff line Loading @@ -26,6 +26,8 @@ * @halt_reg: halt register * @halt_bit: ANDed with @halt_reg to test for clock halted * @halt_check: type of halt checking to perform * @aggr_sibling_rates: set if the branch clock's parent needs to be scaled * based on an aggregation of its siblings votes. * @clkr: handle between common and hardware-specific interfaces * * Clock which can gate its output. Loading @@ -36,6 +38,8 @@ struct clk_branch { u8 hwcg_bit; u8 halt_bit; u8 halt_check; bool aggr_sibling_rates; unsigned long rate; #define BRANCH_VOTED BIT(7) /* Delay on disable */ #define BRANCH_HALT 0 /* pol: 1 = halt */ #define BRANCH_HALT_VOTED (BRANCH_HALT | BRANCH_VOTED) Loading Loading
drivers/clk/clk.c +30 −9 Original line number Diff line number Diff line Loading @@ -1488,7 +1488,7 @@ static struct clk_core *clk_propagate_rate_change(struct clk_core *core, * walk down a subtree and set the new rates notifying the rate * change on the way */ static void clk_change_rate(struct clk_core *core) static int clk_change_rate(struct clk_core *core) { struct clk_core *child; struct hlist_node *tmp; Loading @@ -1497,6 +1497,7 @@ static void clk_change_rate(struct clk_core *core) bool skip_set_rate = false; struct clk_core *old_parent; struct clk_core *parent = NULL; int rc = 0; old_rate = core->rate; Loading Loading @@ -1539,8 +1540,13 @@ static void clk_change_rate(struct clk_core *core) trace_clk_set_rate(core, core->new_rate); if (!skip_set_rate && core->ops->set_rate) core->ops->set_rate(core->hw, core->new_rate, best_parent_rate); if (!skip_set_rate && core->ops->set_rate) { rc = core->ops->set_rate(core->hw, core->new_rate, best_parent_rate); if (rc) goto out; } trace_clk_set_rate_complete(core, core->new_rate); Loading Loading @@ -1572,12 +1578,20 @@ static void clk_change_rate(struct clk_core *core) /* Skip children who will be reparented to another clock */ if (child->new_parent && child->new_parent != core) continue; clk_change_rate(child); rc = clk_change_rate(child); if (rc) return rc; } /* handle the new child who might not be in core->children yet */ if (core->new_child) clk_change_rate(core->new_child); rc = clk_change_rate(core->new_child); return rc; out: trace_clk_set_rate_complete(core, core->new_rate); return rc; } static int clk_core_set_rate_nolock(struct clk_core *core, Loading @@ -1585,6 +1599,7 @@ static int clk_core_set_rate_nolock(struct clk_core *core, { struct clk_core *top, *fail_clk; unsigned long rate = req_rate; int ret = 0; if (!core) return 0; Loading @@ -1604,18 +1619,24 @@ static int clk_core_set_rate_nolock(struct clk_core *core, /* notify that we are about to change rates */ fail_clk = clk_propagate_rate_change(top, PRE_RATE_CHANGE); if (fail_clk) { pr_debug("%s: failed to set %s rate\n", __func__, fail_clk->name); pr_debug("%s: failed to set %s clock to run at %lu\n", __func__, fail_clk->name, req_rate); clk_propagate_rate_change(top, ABORT_RATE_CHANGE); return -EBUSY; } /* change the rates */ clk_change_rate(top); ret = clk_change_rate(top); if (ret) { pr_err("%s: failed to set %s clock to run at %lu\n", __func__, top->name, req_rate); clk_propagate_rate_change(top, ABORT_RATE_CHANGE); return ret; } core->req_rate = req_rate; return 0; return ret; } /** Loading
drivers/clk/qcom/clk-branch.c +117 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ #include <linux/err.h> #include <linux/delay.h> #include <linux/export.h> #include <linux/clk.h> #include <linux/clk-provider.h> #include <linux/regmap.h> #include <linux/clk/qcom.h> Loading Loading @@ -182,20 +183,136 @@ const struct clk_ops clk_branch_ops = { }; EXPORT_SYMBOL_GPL(clk_branch_ops); static int clk_branch2_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { struct clk_branch *branch = to_clk_branch(hw); struct clk_hw *parent = clk_hw_get_parent(hw); unsigned long curr_rate, new_rate, other_rate = 0; int ret = 0; if (!parent) return -EPERM; if (!branch->aggr_sibling_rates || !clk_hw_is_prepared(hw)) { branch->rate = rate; return 0; } other_rate = clk_aggregate_rate(hw, parent->core); curr_rate = max(other_rate, branch->rate); new_rate = max(other_rate, rate); if (new_rate != curr_rate) { ret = clk_set_rate(parent->clk, new_rate); if (ret) { pr_err("Failed to scale %s to %lu\n", clk_hw_get_name(parent), new_rate); goto err; } } branch->rate = rate; err: return ret; } static long clk_branch2_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *parent_rate) { struct clk_hw *parent = clk_hw_get_parent(hw); unsigned long rrate = 0; if (!parent) return -EPERM; rrate = clk_hw_round_rate(parent, rate); /* * If the rounded rate that's returned is valid, update the parent_rate * field so that the set_rate() call can be propagated to the parent. */ if (rrate > 0) *parent_rate = rrate; else pr_warn("Failed to get the parent's (%s) rounded rate\n", clk_hw_get_name(parent)); return rrate; } static unsigned long clk_branch2_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { return to_clk_branch(hw)->rate; } static int clk_branch2_enable(struct clk_hw *hw) { return clk_branch_toggle(hw, true, clk_branch2_check_halt); } static int clk_branch2_prepare(struct clk_hw *hw) { struct clk_branch *branch = to_clk_branch(hw); struct clk_hw *parent = clk_hw_get_parent(hw); unsigned long curr_rate, branch_rate = branch->rate; int ret = 0; if (!parent) return -EPERM; /* * Do the rate aggregation and scaling of the RCG in the prepare/ * unprepare functions to avoid potential RPM(/h) communication due to * votes on the voltage rails. */ if (branch->aggr_sibling_rates) { curr_rate = clk_aggregate_rate(hw, parent->core); if (branch_rate > curr_rate) { ret = clk_set_rate(parent->clk, branch_rate); if (ret) { pr_err("Failed to scale %s to %lu\n", clk_hw_get_name(parent), branch_rate); goto exit; } } } exit: return ret; } static void clk_branch2_disable(struct clk_hw *hw) { clk_branch_toggle(hw, false, clk_branch2_check_halt); } static void clk_branch2_unprepare(struct clk_hw *hw) { struct clk_branch *branch = to_clk_branch(hw); struct clk_hw *parent = clk_hw_get_parent(hw); unsigned long curr_rate, new_rate, branch_rate = branch->rate; if (!parent) return; if (branch->aggr_sibling_rates) { new_rate = clk_aggregate_rate(hw, parent->core); curr_rate = max(new_rate, branch_rate); if (new_rate < curr_rate) if (clk_set_rate(parent->clk, new_rate)) pr_err("Failed to scale %s to %lu\n", clk_hw_get_name(parent), new_rate); } } const struct clk_ops clk_branch2_ops = { .prepare = clk_branch2_prepare, .enable = clk_branch2_enable, .unprepare = clk_branch2_unprepare, .disable = clk_branch2_disable, .is_enabled = clk_is_enabled_regmap, .set_rate = clk_branch2_set_rate, .round_rate = clk_branch2_round_rate, .recalc_rate = clk_branch2_recalc_rate, .set_flags = clk_branch_set_flags, }; EXPORT_SYMBOL_GPL(clk_branch2_ops); Loading
drivers/clk/qcom/clk-branch.h +4 −0 Original line number Diff line number Diff line Loading @@ -26,6 +26,8 @@ * @halt_reg: halt register * @halt_bit: ANDed with @halt_reg to test for clock halted * @halt_check: type of halt checking to perform * @aggr_sibling_rates: set if the branch clock's parent needs to be scaled * based on an aggregation of its siblings votes. * @clkr: handle between common and hardware-specific interfaces * * Clock which can gate its output. Loading @@ -36,6 +38,8 @@ struct clk_branch { u8 hwcg_bit; u8 halt_bit; u8 halt_check; bool aggr_sibling_rates; unsigned long rate; #define BRANCH_VOTED BIT(7) /* Delay on disable */ #define BRANCH_HALT 0 /* pol: 1 = halt */ #define BRANCH_HALT_VOTED (BRANCH_HALT | BRANCH_VOTED) Loading