diff options
author | Andreas Kemnade <andreas@kemnade.info> | 2019-01-16 23:04:27 +0100 |
---|---|---|
committer | Tero Kristo <t-kristo@ti.com> | 2019-02-15 16:47:55 +0200 |
commit | d277ce2d3a75c6c116a6119c3745694f5941eff5 (patch) | |
tree | cec5a936cac3b1f07daaf4cead9a3d1feae37a3a | |
parent | ead478250b950f1f082d4cb57bed3adeacfe53c3 (diff) | |
download | linux-d277ce2d3a75c6c116a6119c3745694f5941eff5.tar.gz linux-d277ce2d3a75c6c116a6119c3745694f5941eff5.tar.bz2 linux-d277ce2d3a75c6c116a6119c3745694f5941eff5.zip |
clk: ti: add a usecount for autoidle
Multiple users might deny autoidle on a clock. So we should have some
counting here, also according to the comment in _setup_iclk_autoidle().
Also setting autoidle regs is not atomic, so there is another reason
for locking.
Signed-off-by: Andreas Kemnade <andreas@kemnade.info>
Acked-by: Tony Lindgren <tony@atomide.com>
Tested-by: Keerthy <j-keerthy@ti.com>
Signed-off-by: Tero Kristo <t-kristo@ti.com>
-rw-r--r-- | drivers/clk/ti/autoidle.c | 32 | ||||
-rw-r--r-- | include/linux/clk/ti.h | 1 |
2 files changed, 29 insertions, 4 deletions
diff --git a/drivers/clk/ti/autoidle.c b/drivers/clk/ti/autoidle.c index a129b4b36ea3..964e97b5478a 100644 --- a/drivers/clk/ti/autoidle.c +++ b/drivers/clk/ti/autoidle.c @@ -36,17 +36,41 @@ struct clk_ti_autoidle { static LIST_HEAD(autoidle_clks); +/* + * we have some non-atomic read/write + * operations behind it, so lets + * take one lock for handling autoidle + * of all clocks + */ +static DEFINE_SPINLOCK(autoidle_spinlock); + static int _omap2_clk_deny_idle(struct clk_hw_omap *clk) { - if (clk->ops && clk->ops->deny_idle) - clk->ops->deny_idle(clk); + if (clk->ops && clk->ops->deny_idle) { + unsigned long irqflags; + + spin_lock_irqsave(&autoidle_spinlock, irqflags); + clk->autoidle_count++; + if (clk->autoidle_count == 1) + clk->ops->deny_idle(clk); + + spin_unlock_irqrestore(&autoidle_spinlock, irqflags); + } return 0; } static int _omap2_clk_allow_idle(struct clk_hw_omap *clk) { - if (clk->ops && clk->ops->allow_idle) - clk->ops->allow_idle(clk); + if (clk->ops && clk->ops->allow_idle) { + unsigned long irqflags; + + spin_lock_irqsave(&autoidle_spinlock, irqflags); + clk->autoidle_count--; + if (clk->autoidle_count == 0) + clk->ops->allow_idle(clk); + + spin_unlock_irqrestore(&autoidle_spinlock, irqflags); + } return 0; } diff --git a/include/linux/clk/ti.h b/include/linux/clk/ti.h index eacc5df57b99..78872efc7be0 100644 --- a/include/linux/clk/ti.h +++ b/include/linux/clk/ti.h @@ -160,6 +160,7 @@ struct clk_hw_omap { struct clockdomain *clkdm; const struct clk_hw_omap_ops *ops; u32 context; + int autoidle_count; }; /* |