Message ID | 20171004023006.31763-1-linus.walleij@linaro.org (mailing list archive) |
---|---|
State | Rejected, archived |
Headers | show |
On Wed, Oct 4, 2017 at 4:30 AM, Linus Walleij <linus.walleij@linaro.org> wrote: > From: Stephen Boyd <sboyd@codeaurora.org> > > If a clock is on and we call clk_set_rate() on it we may get into > a situation where the clock temporarily increases in rate > dramatically while we walk the tree and call .set_rate() ops. For > example, consider a case where a PLL feeds into a divider. > Initially the divider is set to divide by 1 and the PLL is > running fairly slow (100MHz). The downstream consumer of the > divider output can only handle rates =< 400 MHz, but the divider > can only choose between divisors of 1 and 4. > > +-----+ +----------------+ > | PLL |-->| div 1 or div 4 |---> consumer device > +-----+ +----------------+ > > To achieve a rate of 400MHz on the output of the divider, we > would have to set the rate of the PLL to 1.6 GHz and then divide > it by 4. The current code would set the PLL to 1.6GHz first while > the divider is still set to 1, thus causing the downstream > consumer of the clock to receive a few clock cycles of 1.6GHz > clock (far beyond it's maximum acceptable rate). We should be > changing the divider first before increasing the PLL rate to > avoid this problem. > > Therefore, set the rate of any child clocks that are increasing > in rate from their current rate so that they can increase their > dividers if necessary. We assume that there isn't such a thing as > minimum rate requirements. > > Cc: Opera <montvid@gmail.com> > Cc: John Stultz <john.stultz@linaro.org> > Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> > --- > Stephen: just wanted to check what's up with this patch. > When I apply it on my kernel I get graphics on the Nexus7, > when I don't, I don't. > > OpenWRT has started to carry the patch in their tree I noticed. > I found it in John Stultz patch stack. > > Is there some similar patch floating in some other series, is > it fundamentally wrong or something else? > > Just wanted to reboot the discussion so we know where this is > standing. Gentle ping on this patch too. Sorry for pushing, it's just so nice to have graphics working out-of-the-box on Nexus 7. Yours, Linus Walleij -- To unsubscribe from this list: send the line "unsubscribe linux-clk" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On 10/04, Linus Walleij wrote: > From: Stephen Boyd <sboyd@codeaurora.org> > > If a clock is on and we call clk_set_rate() on it we may get into > a situation where the clock temporarily increases in rate > dramatically while we walk the tree and call .set_rate() ops. For > example, consider a case where a PLL feeds into a divider. > Initially the divider is set to divide by 1 and the PLL is > running fairly slow (100MHz). The downstream consumer of the > divider output can only handle rates =< 400 MHz, but the divider > can only choose between divisors of 1 and 4. > > +-----+ +----------------+ > | PLL |-->| div 1 or div 4 |---> consumer device > +-----+ +----------------+ > > To achieve a rate of 400MHz on the output of the divider, we > would have to set the rate of the PLL to 1.6 GHz and then divide > it by 4. The current code would set the PLL to 1.6GHz first while > the divider is still set to 1, thus causing the downstream > consumer of the clock to receive a few clock cycles of 1.6GHz > clock (far beyond it's maximum acceptable rate). We should be > changing the divider first before increasing the PLL rate to > avoid this problem. > > Therefore, set the rate of any child clocks that are increasing > in rate from their current rate so that they can increase their > dividers if necessary. We assume that there isn't such a thing as > minimum rate requirements. > > Cc: Opera <montvid@gmail.com> > Cc: John Stultz <john.stultz@linaro.org> > Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> > --- > Stephen: just wanted to check what's up with this patch. > When I apply it on my kernel I get graphics on the Nexus7, > when I don't, I don't. > > OpenWRT has started to carry the patch in their tree I noticed. > I found it in John Stultz patch stack. > > Is there some similar patch floating in some other series, is > it fundamentally wrong or something else? > > Just wanted to reboot the discussion so we know where this is > standing. This patch wants to go away and be replaced with coordinated clk rates code. So far, nothing has appeared for that though, so this problem is stuck in limbo, blocking Krait CPUfreq. I have no idea why it matters for your Nexus7 graphics stuff. As far as I know the GPU clk on Nexus7 is a plain old dynamic rcg that can switch rates glitch free and we don't do anything odd like reprogramming the parent PLL of the rcg to use faster rates, which is mostly what this patch is for. I wrote this patch for Krait CPUfreq originally. In that case we reprogram PLLs higher up in tree and then have to adjust dividers below that and doing that in the wrong order causes high frequencies to come down the tree into the CPU that we can't turn off. I suspect OpenWRT has pulled in the patch for Krait CPUfreq. It could be that you also need it for that. Or it could be that odd line where we don't look at the result of clk_recalc, and just blast in the calculated new rate into core->rate somehow fixes something.
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index c8d83acda006..324e4fa11802 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -1468,12 +1468,12 @@ 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 void +clk_change_rate(struct clk_core *core, unsigned long best_parent_rate) { struct clk_core *child; struct hlist_node *tmp; unsigned long old_rate; - unsigned long best_parent_rate = 0; bool skip_set_rate = false; struct clk_core *old_parent; struct clk_core *parent = NULL; @@ -1525,6 +1525,7 @@ static void clk_change_rate(struct clk_core *core) trace_clk_set_rate_complete(core, core->new_rate); core->rate = clk_recalc(core, best_parent_rate); + core->rate = core->new_rate; if (core->flags & CLK_SET_RATE_UNGATE) { unsigned long flags; @@ -1552,12 +1553,13 @@ 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); + if (child->new_rate != child->rate) + clk_change_rate(child, core->new_rate); } - /* handle the new child who might not be in core->children yet */ - if (core->new_child) - clk_change_rate(core->new_child); + /* handle the new child who might not be in clk->children yet */ + if (core->new_child && core->new_child->new_rate != core->new_child->rate) + clk_change_rate(core->new_child, core->new_rate); } static int clk_core_set_rate_nolock(struct clk_core *core, @@ -1565,6 +1567,7 @@ static int clk_core_set_rate_nolock(struct clk_core *core, { struct clk_core *top, *fail_clk; unsigned long rate = req_rate; + unsigned long parent_rate; if (!core) return 0; @@ -1590,8 +1593,13 @@ static int clk_core_set_rate_nolock(struct clk_core *core, return -EBUSY; } + if (top->parent) + parent_rate = top->parent->rate; + else + parent_rate = 0; + /* change the rates */ - clk_change_rate(top); + clk_change_rate(top, parent_rate); core->req_rate = req_rate;