Loading drivers/clk/clk.c +86 −20 Original line number Diff line number Diff line /* * Copyright (C) 2010-2011 Canonical Ltd <jeremy.kerr@canonical.com> * Copyright (C) 2011-2012 Linaro Ltd <mturquette@linaro.org> * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as Loading Loading @@ -67,7 +67,9 @@ struct clk_core { unsigned long rate; unsigned long req_rate; unsigned long new_rate; unsigned long old_rate; struct clk_core *new_parent; struct clk_core *old_parent; struct clk_core *new_child; unsigned long flags; bool orphan; Loading Loading @@ -2081,7 +2083,7 @@ static int clk_change_rate(struct clk_core *core) struct clk_core *parent = NULL; int rc = 0; old_rate = core->rate; core->old_rate = old_rate = core->rate; if (core->new_parent) { parent = core->new_parent; Loading @@ -2095,6 +2097,8 @@ static int clk_change_rate(struct clk_core *core) if (rc) return rc; core->old_parent = core->parent; if (core->flags & CLK_SET_RATE_UNGATE) { unsigned long flags; Loading @@ -2106,15 +2110,9 @@ static int clk_change_rate(struct clk_core *core) trace_clk_set_rate(core, core->new_rate); /* Enforce vdd requirements for new frequency. */ if (core->prepare_count) { rc = clk_vote_rate_vdd(core, core->new_rate); if (rc) goto out; } if (core->new_parent && core->new_parent != core->parent) { old_parent = __clk_set_parent_before(core, core->new_parent); core->old_parent = old_parent; trace_clk_set_parent(core, core->new_parent); if (core->ops->set_rate_and_parent) { Loading Loading @@ -2144,10 +2142,6 @@ static int clk_change_rate(struct clk_core *core) trace_clk_set_rate_complete(core, core->new_rate); /* Release vdd requirements for old frequency. */ if (core->prepare_count) clk_unvote_rate_vdd(core, old_rate); core->rate = clk_recalc(core, best_parent_rate); if (core->flags & CLK_SET_RATE_UNGATE) { Loading Loading @@ -2185,13 +2179,7 @@ static int clk_change_rate(struct clk_core *core) if (core->new_child) rc = clk_change_rate(core->new_child); clk_pm_runtime_put(core); return rc; err_set_rate: if (core->prepare_count) clk_unvote_rate_vdd(core, core->new_rate); out: clk_pm_runtime_put(core); return rc; } Loading Loading @@ -2223,6 +2211,74 @@ static unsigned long clk_core_req_round_rate_nolock(struct clk_core *core, return ret ? 0 : req.rate; } static int vote_vdd_up(struct clk_core *core) { struct clk_core *parent = NULL; int ret, cur_level, next_level; /* sanity */ if (IS_ERR_OR_NULL(core)) return 0; if (core->vdd_class) { cur_level = clk_find_vdd_level(core, core->rate); next_level = clk_find_vdd_level(core, core->new_rate); if (cur_level == next_level) return 0; } /* save parent rate, if it exists */ if (core->new_parent) parent = core->new_parent; else if (core->parent) parent = core->parent; if (core->prepare_count && core->new_rate) { ret = clk_vote_rate_vdd(core, core->new_rate); if (ret) return ret; } vote_vdd_up(parent); return 0; } static int vote_vdd_down(struct clk_core *core) { struct clk_core *parent; unsigned long rate; int cur_level, old_level; /* sanity */ if (IS_ERR_OR_NULL(core)) return 0; rate = core->old_rate; /* New rate set was a failure */ if (DIV_ROUND_CLOSEST(core->rate, 1000) != DIV_ROUND_CLOSEST(core->new_rate, 1000)) rate = core->new_rate; if (core->vdd_class) { cur_level = clk_find_vdd_level(core, core->rate); old_level = clk_find_vdd_level(core, core->old_rate); if ((cur_level == old_level) || !core->vdd_class->level_votes[old_level]) return 0; } parent = core->old_parent; if (core->prepare_count && rate) clk_unvote_rate_vdd(core, rate); vote_vdd_down(parent); return 0; } static int clk_core_set_rate_nolock(struct clk_core *core, unsigned long req_rate) { Loading Loading @@ -2262,16 +2318,26 @@ static int clk_core_set_rate_nolock(struct clk_core *core, goto err; } /* Enforce the VDD for new frequency */ ret = vote_vdd_up(core); if (ret) goto err; /* change the rates */ 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; /* Release vdd requirements for new frequency. */ vote_vdd_down(core); goto err; } core->req_rate = req_rate; /* Release vdd requirements for old frequency. */ vote_vdd_down(core); err: clk_pm_runtime_put(core); Loading Loading
drivers/clk/clk.c +86 −20 Original line number Diff line number Diff line /* * Copyright (C) 2010-2011 Canonical Ltd <jeremy.kerr@canonical.com> * Copyright (C) 2011-2012 Linaro Ltd <mturquette@linaro.org> * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as Loading Loading @@ -67,7 +67,9 @@ struct clk_core { unsigned long rate; unsigned long req_rate; unsigned long new_rate; unsigned long old_rate; struct clk_core *new_parent; struct clk_core *old_parent; struct clk_core *new_child; unsigned long flags; bool orphan; Loading Loading @@ -2081,7 +2083,7 @@ static int clk_change_rate(struct clk_core *core) struct clk_core *parent = NULL; int rc = 0; old_rate = core->rate; core->old_rate = old_rate = core->rate; if (core->new_parent) { parent = core->new_parent; Loading @@ -2095,6 +2097,8 @@ static int clk_change_rate(struct clk_core *core) if (rc) return rc; core->old_parent = core->parent; if (core->flags & CLK_SET_RATE_UNGATE) { unsigned long flags; Loading @@ -2106,15 +2110,9 @@ static int clk_change_rate(struct clk_core *core) trace_clk_set_rate(core, core->new_rate); /* Enforce vdd requirements for new frequency. */ if (core->prepare_count) { rc = clk_vote_rate_vdd(core, core->new_rate); if (rc) goto out; } if (core->new_parent && core->new_parent != core->parent) { old_parent = __clk_set_parent_before(core, core->new_parent); core->old_parent = old_parent; trace_clk_set_parent(core, core->new_parent); if (core->ops->set_rate_and_parent) { Loading Loading @@ -2144,10 +2142,6 @@ static int clk_change_rate(struct clk_core *core) trace_clk_set_rate_complete(core, core->new_rate); /* Release vdd requirements for old frequency. */ if (core->prepare_count) clk_unvote_rate_vdd(core, old_rate); core->rate = clk_recalc(core, best_parent_rate); if (core->flags & CLK_SET_RATE_UNGATE) { Loading Loading @@ -2185,13 +2179,7 @@ static int clk_change_rate(struct clk_core *core) if (core->new_child) rc = clk_change_rate(core->new_child); clk_pm_runtime_put(core); return rc; err_set_rate: if (core->prepare_count) clk_unvote_rate_vdd(core, core->new_rate); out: clk_pm_runtime_put(core); return rc; } Loading Loading @@ -2223,6 +2211,74 @@ static unsigned long clk_core_req_round_rate_nolock(struct clk_core *core, return ret ? 0 : req.rate; } static int vote_vdd_up(struct clk_core *core) { struct clk_core *parent = NULL; int ret, cur_level, next_level; /* sanity */ if (IS_ERR_OR_NULL(core)) return 0; if (core->vdd_class) { cur_level = clk_find_vdd_level(core, core->rate); next_level = clk_find_vdd_level(core, core->new_rate); if (cur_level == next_level) return 0; } /* save parent rate, if it exists */ if (core->new_parent) parent = core->new_parent; else if (core->parent) parent = core->parent; if (core->prepare_count && core->new_rate) { ret = clk_vote_rate_vdd(core, core->new_rate); if (ret) return ret; } vote_vdd_up(parent); return 0; } static int vote_vdd_down(struct clk_core *core) { struct clk_core *parent; unsigned long rate; int cur_level, old_level; /* sanity */ if (IS_ERR_OR_NULL(core)) return 0; rate = core->old_rate; /* New rate set was a failure */ if (DIV_ROUND_CLOSEST(core->rate, 1000) != DIV_ROUND_CLOSEST(core->new_rate, 1000)) rate = core->new_rate; if (core->vdd_class) { cur_level = clk_find_vdd_level(core, core->rate); old_level = clk_find_vdd_level(core, core->old_rate); if ((cur_level == old_level) || !core->vdd_class->level_votes[old_level]) return 0; } parent = core->old_parent; if (core->prepare_count && rate) clk_unvote_rate_vdd(core, rate); vote_vdd_down(parent); return 0; } static int clk_core_set_rate_nolock(struct clk_core *core, unsigned long req_rate) { Loading Loading @@ -2262,16 +2318,26 @@ static int clk_core_set_rate_nolock(struct clk_core *core, goto err; } /* Enforce the VDD for new frequency */ ret = vote_vdd_up(core); if (ret) goto err; /* change the rates */ 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; /* Release vdd requirements for new frequency. */ vote_vdd_down(core); goto err; } core->req_rate = req_rate; /* Release vdd requirements for old frequency. */ vote_vdd_down(core); err: clk_pm_runtime_put(core); Loading