Sekhar Nori <[email protected]> writes:
> Currently, the spinlock in DaVinci clock framework is being
> used to:
>
> 1) Protect clock structure variables usecount and rate against
> concurrent modification.
>
> 2) Protect against simultaneous PSC enables/disables ie.
> serialize davinci_psc_config().
>
> 3) Serialize clk_set_rate():
> i. Prevent simultaneous setting of clock rates
> ii. Ensure clock list remains sane during rate
> propagation (also in clk_set_parent).
>
> Remove the spinlock usage in clock framework by:
>
> 1) Making clock rate and usecount as atomic variables.
>
> 2) Making davinci_psc_config() protect itself instead of
> relying on serialization by caller.
>
> 3) (i) Allowing the clk->set_rate to serialize itself. There
> should be no need to serialize all clock rate settings.
> Currently the only user of rate setting is cpufreq driver on
> DA850. cpufreq naturally serializes the calls to set CPU rate.
> Also, there appears no need to lock IRQs during CPU rate
> transtitions. If required, IRQs can be locked in the actual
> set_rate function.
>
> 3) (ii) Use the mutex already in place for serialzing clock list
> manipulation for serializing clock rate propagation as well.
>
> Apart from the general benefit of reducing locking granurlarity,
> the main motivation behind this change is to enable usage of
> clock framework for off-chip clock synthesizers. One such
> synthesizer, CDCE949, is present on DM6467 EVM. Access to the
> synthesizer happens through I2C bus - accessing which can lead to
> CPU sleep. Having IRQs locked in clk_set_rate prevents the
> clk->set_rate function from sleeping.
>
> Signed-off-by: Sekhar Nori <[email protected]>
I like this cleanup a lot. Applying patches 1-3 to davinci git and queued for
2.6.34.
Kevin
> ---
> arch/arm/mach-davinci/board-dm646x-evm.c | 4 +-
> arch/arm/mach-davinci/clock.c | 74 ++++++++++++-----------------
> arch/arm/mach-davinci/clock.h | 4 +-
> arch/arm/mach-davinci/da830.c | 2 +-
> arch/arm/mach-davinci/da850.c | 4 +-
> arch/arm/mach-davinci/dm355.c | 4 +-
> arch/arm/mach-davinci/dm365.c | 4 +-
> arch/arm/mach-davinci/dm644x.c | 8 ++--
> arch/arm/mach-davinci/dm646x.c | 8 ++--
> arch/arm/mach-davinci/psc.c | 6 ++
> 10 files changed, 56 insertions(+), 62 deletions(-)
>
> diff --git a/arch/arm/mach-davinci/board-dm646x-evm.c
> b/arch/arm/mach-davinci/board-dm646x-evm.c
> index 542bfdb..6ff3411 100644
> --- a/arch/arm/mach-davinci/board-dm646x-evm.c
> +++ b/arch/arm/mach-davinci/board-dm646x-evm.c
> @@ -722,9 +722,9 @@ static __init void davinci_dm646x_evm_irq_init(void)
> void __init dm646x_board_setup_refclk(struct clk *clk)
> {
> if (machine_is_davinci_dm6467tevm())
> - clk->rate = DM6467T_EVM_REF_FREQ;
> + atomic_set(&clk->rate, DM6467T_EVM_REF_FREQ);
> else
> - clk->rate = DM646X_EVM_REF_FREQ;
> + atomic_set(&clk->rate, DM646X_EVM_REF_FREQ);
> }
>
> MACHINE_START(DAVINCI_DM6467_EVM, "DaVinci DM646x EVM")
> diff --git a/arch/arm/mach-davinci/clock.c b/arch/arm/mach-davinci/clock.c
> index baece65..df884c8 100644
> --- a/arch/arm/mach-davinci/clock.c
> +++ b/arch/arm/mach-davinci/clock.c
> @@ -28,7 +28,6 @@
>
> static LIST_HEAD(clocks);
> static DEFINE_MUTEX(clocks_mutex);
> -static DEFINE_SPINLOCK(clockfw_lock);
>
> static unsigned psc_domain(struct clk *clk)
> {
> @@ -41,15 +40,16 @@ static void __clk_enable(struct clk *clk)
> {
> if (clk->parent)
> __clk_enable(clk->parent);
> - if (clk->usecount++ == 0 && (clk->flags & CLK_PSC))
> + if (atomic_read(&clk->usecount) == 0 && (clk->flags & CLK_PSC))
> davinci_psc_config(psc_domain(clk), clk->gpsc, clk->lpsc, 1);
> + atomic_inc(&clk->usecount);
> }
>
> static void __clk_disable(struct clk *clk)
> {
> - if (WARN_ON(clk->usecount == 0))
> + if (WARN_ON(atomic_read(&clk->usecount) == 0))
> return;
> - if (--clk->usecount == 0 && !(clk->flags & CLK_PLL))
> + if (atomic_dec_and_test(&clk->usecount) && !(clk->flags & CLK_PLL))
> davinci_psc_config(psc_domain(clk), clk->gpsc, clk->lpsc, 0);
> if (clk->parent)
> __clk_disable(clk->parent);
> @@ -57,14 +57,10 @@ static void __clk_disable(struct clk *clk)
>
> int clk_enable(struct clk *clk)
> {
> - unsigned long flags;
> -
> if (clk == NULL || IS_ERR(clk))
> return -EINVAL;
>
> - spin_lock_irqsave(&clockfw_lock, flags);
> __clk_enable(clk);
> - spin_unlock_irqrestore(&clockfw_lock, flags);
>
> return 0;
> }
> @@ -72,14 +68,10 @@ EXPORT_SYMBOL(clk_enable);
>
> void clk_disable(struct clk *clk)
> {
> - unsigned long flags;
> -
> if (clk == NULL || IS_ERR(clk))
> return;
>
> - spin_lock_irqsave(&clockfw_lock, flags);
> __clk_disable(clk);
> - spin_unlock_irqrestore(&clockfw_lock, flags);
> }
> EXPORT_SYMBOL(clk_disable);
>
> @@ -88,7 +80,7 @@ unsigned long clk_get_rate(struct clk *clk)
> if (clk == NULL || IS_ERR(clk))
> return -EINVAL;
>
> - return clk->rate;
> + return atomic_read(&clk->rate);
> }
> EXPORT_SYMBOL(clk_get_rate);
>
> @@ -100,7 +92,7 @@ long clk_round_rate(struct clk *clk, unsigned long rate)
> if (clk->round_rate)
> return clk->round_rate(clk, rate);
>
> - return clk->rate;
> + return atomic_read(&clk->rate);
> }
> EXPORT_SYMBOL(clk_round_rate);
>
> @@ -111,28 +103,27 @@ static void propagate_rate(struct clk *root)
>
> list_for_each_entry(clk, &root->children, childnode) {
> if (clk->recalc)
> - clk->rate = clk->recalc(clk);
> + atomic_set(&clk->rate, clk->recalc(clk));
> propagate_rate(clk);
> }
> }
>
> int clk_set_rate(struct clk *clk, unsigned long rate)
> {
> - unsigned long flags;
> int ret = -EINVAL;
>
> if (clk == NULL || IS_ERR(clk))
> return ret;
>
> - spin_lock_irqsave(&clockfw_lock, flags);
> if (clk->set_rate)
> ret = clk->set_rate(clk, rate);
> if (ret == 0) {
> if (clk->recalc)
> - clk->rate = clk->recalc(clk);
> + atomic_set(&clk->rate, clk->recalc(clk));
> + mutex_lock(&clocks_mutex);
> propagate_rate(clk);
> + mutex_unlock(&clocks_mutex);
> }
> - spin_unlock_irqrestore(&clockfw_lock, flags);
>
> return ret;
> }
> @@ -140,26 +131,22 @@ EXPORT_SYMBOL(clk_set_rate);
>
> int clk_set_parent(struct clk *clk, struct clk *parent)
> {
> - unsigned long flags;
> -
> if (clk == NULL || IS_ERR(clk))
> return -EINVAL;
>
> /* Cannot change parent on enabled clock */
> - if (WARN_ON(clk->usecount))
> + if (WARN_ON(atomic_read(&clk->usecount)))
> return -EINVAL;
>
> mutex_lock(&clocks_mutex);
> clk->parent = parent;
> list_del_init(&clk->childnode);
> list_add(&clk->childnode, &clk->parent->children);
> - mutex_unlock(&clocks_mutex);
>
> - spin_lock_irqsave(&clockfw_lock, flags);
> if (clk->recalc)
> - clk->rate = clk->recalc(clk);
> + atomic_set(&clk->rate, clk->recalc(clk));
> propagate_rate(clk);
> - spin_unlock_irqrestore(&clockfw_lock, flags);
> + mutex_unlock(&clocks_mutex);
>
> return 0;
> }
> @@ -170,7 +157,7 @@ int clk_register(struct clk *clk)
> if (clk == NULL || IS_ERR(clk))
> return -EINVAL;
>
> - if (WARN(clk->parent && !clk->parent->rate,
> + if (WARN(clk->parent && !atomic_read(&clk->parent->rate),
> "CLK: %s parent %s has no rate!\n",
> clk->name, clk->parent->name))
> return -EINVAL;
> @@ -184,16 +171,16 @@ int clk_register(struct clk *clk)
> mutex_unlock(&clocks_mutex);
>
> /* If rate is already set, use it */
> - if (clk->rate)
> + if (atomic_read(&clk->rate))
> return 0;
>
> /* Else, see if there is a way to calculate it */
> if (clk->recalc)
> - clk->rate = clk->recalc(clk);
> + atomic_set(&clk->rate, clk->recalc(clk));
>
> /* Otherwise, default to parent rate */
> else if (clk->parent)
> - clk->rate = clk->parent->rate;
> + atomic_set(&clk->rate, atomic_read(&clk->parent->rate));
>
> return 0;
> }
> @@ -219,9 +206,9 @@ static int __init clk_disable_unused(void)
> {
> struct clk *ck;
>
> - spin_lock_irq(&clockfw_lock);
> + mutex_lock(&clocks_mutex);
> list_for_each_entry(ck, &clocks, node) {
> - if (ck->usecount > 0)
> + if (atomic_read(&ck->usecount) > 0)
> continue;
> if (!(ck->flags & CLK_PSC))
> continue;
> @@ -233,7 +220,7 @@ static int __init clk_disable_unused(void)
> pr_info("Clocks: disable unused %s\n", ck->name);
> davinci_psc_config(psc_domain(ck), ck->gpsc, ck->lpsc, 0);
> }
> - spin_unlock_irq(&clockfw_lock);
> + mutex_unlock(&clocks_mutex);
>
> return 0;
> }
> @@ -244,7 +231,7 @@ static unsigned long clk_sysclk_recalc(struct clk *clk)
> {
> u32 v, plldiv;
> struct pll_data *pll;
> - unsigned long rate = clk->rate;
> + unsigned long rate = atomic_read(&clk->rate);
>
> /* If this is the PLL base clock, no more calculations needed */
> if (clk->pll_data)
> @@ -253,7 +240,7 @@ static unsigned long clk_sysclk_recalc(struct clk *clk)
> if (WARN_ON(!clk->parent))
> return rate;
>
> - rate = clk->parent->rate;
> + rate = atomic_read(&clk->parent->rate);
>
> /* Otherwise, the parent must be a PLL */
> if (WARN_ON(!clk->parent->pll_data))
> @@ -281,9 +268,9 @@ static unsigned long clk_sysclk_recalc(struct clk *clk)
> static unsigned long clk_leafclk_recalc(struct clk *clk)
> {
> if (WARN_ON(!clk->parent))
> - return clk->rate;
> + return atomic_read(&clk->rate);
>
> - return clk->parent->rate;
> + return atomic_read(&clk->parent->rate);
> }
>
> static unsigned long clk_pllclk_recalc(struct clk *clk)
> @@ -291,11 +278,11 @@ static unsigned long clk_pllclk_recalc(struct clk *clk)
> u32 ctrl, mult = 1, prediv = 1, postdiv = 1;
> u8 bypass;
> struct pll_data *pll = clk->pll_data;
> - unsigned long rate = clk->rate;
> + unsigned long rate = atomic_read(&clk->rate);
>
> pll->base = IO_ADDRESS(pll->phys_base);
> ctrl = __raw_readl(pll->base + PLLCTL);
> - rate = pll->input_rate = clk->parent->rate;
> + rate = pll->input_rate = atomic_read(&clk->parent->rate);
>
> if (ctrl & PLLCTL_PLLEN) {
> bypass = 0;
> @@ -333,8 +320,8 @@ static unsigned long clk_pllclk_recalc(struct clk *clk)
> rate /= postdiv;
> }
>
> - pr_debug("PLL%d: input = %lu MHz [ ",
> - pll->num, clk->parent->rate / 1000000);
> + pr_debug("PLL%d: input = %u MHz [ ",
> + pll->num, atomic_read(&clk->parent->rate) / 1000000);
> if (bypass)
> pr_debug("bypass ");
> if (prediv > 1)
> @@ -452,7 +439,7 @@ int __init davinci_clk_init(struct davinci_clk *clocks)
> }
>
> if (clk->recalc)
> - clk->rate = clk->recalc(clk);
> + atomic_set(&clk->rate, clk->recalc(clk));
>
> if (clk->lpsc)
> clk->flags |= CLK_PSC;
> @@ -514,7 +501,8 @@ dump_clock(struct seq_file *s, unsigned nest, struct clk
> *parent)
> min(i, (unsigned)(sizeof(buf) - 1 - nest)));
>
> seq_printf(s, "%s users=%2d %-3s %9ld Hz\n",
> - buf, parent->usecount, state, clk_get_rate(parent));
> + buf, atomic_read(&parent->usecount), state,
> + clk_get_rate(parent));
> /* REVISIT show device associations too */
>
> /* cost is now small, but not linear... */
> diff --git a/arch/arm/mach-davinci/clock.h b/arch/arm/mach-davinci/clock.h
> index c92d77a..796a568 100644
> --- a/arch/arm/mach-davinci/clock.h
> +++ b/arch/arm/mach-davinci/clock.h
> @@ -67,8 +67,8 @@ struct clk {
> struct list_head node;
> struct module *owner;
> const char *name;
> - unsigned long rate;
> - u8 usecount;
> + atomic_t rate;
> + atomic_t usecount;
> u8 lpsc;
> u8 gpsc;
> u32 flags;
> diff --git a/arch/arm/mach-davinci/da830.c b/arch/arm/mach-davinci/da830.c
> index b22b5cf..d1961d2 100644
> --- a/arch/arm/mach-davinci/da830.c
> +++ b/arch/arm/mach-davinci/da830.c
> @@ -43,7 +43,7 @@ static struct pll_data pll0_data = {
>
> static struct clk ref_clk = {
> .name = "ref_clk",
> - .rate = DA830_REF_FREQ,
> + .rate = ATOMIC_INIT(DA830_REF_FREQ),
> };
>
> static struct clk pll0_clk = {
> diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c
> index 717806c..62f8c22 100644
> --- a/arch/arm/mach-davinci/da850.c
> +++ b/arch/arm/mach-davinci/da850.c
> @@ -54,7 +54,7 @@ static struct pll_data pll0_data = {
>
> static struct clk ref_clk = {
> .name = "ref_clk",
> - .rate = DA850_REF_FREQ,
> + .rate = ATOMIC_INIT(DA850_REF_FREQ),
> };
>
> static struct clk pll0_clk = {
> @@ -1024,7 +1024,7 @@ static int da850_set_pll0rate(struct clk *clk, unsigned
> long armrate)
>
> static int da850_round_armrate(struct clk *clk, unsigned long rate)
> {
> - return clk->rate;
> + return atomic_read(&clk->rate);
> }
> #endif
>
> diff --git a/arch/arm/mach-davinci/dm355.c b/arch/arm/mach-davinci/dm355.c
> index dedf4d4..2244e8c 100644
> --- a/arch/arm/mach-davinci/dm355.c
> +++ b/arch/arm/mach-davinci/dm355.c
> @@ -55,7 +55,7 @@ static struct pll_data pll2_data = {
> static struct clk ref_clk = {
> .name = "ref_clk",
> /* FIXME -- crystal rate is board-specific */
> - .rate = DM355_REF_FREQ,
> + .rate = ATOMIC_INIT(DM355_REF_FREQ),
> };
>
> static struct clk pll1_clk = {
> @@ -314,7 +314,7 @@ static struct clk timer2_clk = {
> .name = "timer2",
> .parent = &pll1_aux_clk,
> .lpsc = DAVINCI_LPSC_TIMER2,
> - .usecount = 1, /* REVISIT: why cant' this be disabled? */
> + .usecount = ATOMIC_INIT(1), /* REVISIT: why cant' this be disabled? */
> };
>
> static struct clk timer3_clk = {
> diff --git a/arch/arm/mach-davinci/dm365.c b/arch/arm/mach-davinci/dm365.c
> index 2ec619e..cc3bae4 100644
> --- a/arch/arm/mach-davinci/dm365.c
> +++ b/arch/arm/mach-davinci/dm365.c
> @@ -52,7 +52,7 @@ static struct pll_data pll2_data = {
>
> static struct clk ref_clk = {
> .name = "ref_clk",
> - .rate = DM365_REF_FREQ,
> + .rate = ATOMIC_INIT(DM365_REF_FREQ),
> };
>
> static struct clk pll1_clk = {
> @@ -358,7 +358,7 @@ static struct clk timer2_clk = {
> .name = "timer2",
> .parent = &pll1_aux_clk,
> .lpsc = DAVINCI_LPSC_TIMER2,
> - .usecount = 1,
> + .usecount = ATOMIC_INIT(1),
> };
>
> static struct clk timer3_clk = {
> diff --git a/arch/arm/mach-davinci/dm644x.c b/arch/arm/mach-davinci/dm644x.c
> index 2cd0081..e65e29e 100644
> --- a/arch/arm/mach-davinci/dm644x.c
> +++ b/arch/arm/mach-davinci/dm644x.c
> @@ -47,7 +47,7 @@ static struct pll_data pll2_data = {
>
> static struct clk ref_clk = {
> .name = "ref_clk",
> - .rate = DM644X_REF_FREQ,
> + .rate = ATOMIC_INIT(DM644X_REF_FREQ),
> };
>
> static struct clk pll1_clk = {
> @@ -131,7 +131,7 @@ static struct clk dsp_clk = {
> .parent = &pll1_sysclk1,
> .lpsc = DAVINCI_LPSC_GEM,
> .flags = PSC_DSP,
> - .usecount = 1, /* REVISIT how to disable? */
> + .usecount = ATOMIC_INIT(1), /* REVISIT how to disable? */
> };
>
> static struct clk arm_clk = {
> @@ -146,7 +146,7 @@ static struct clk vicp_clk = {
> .parent = &pll1_sysclk2,
> .lpsc = DAVINCI_LPSC_IMCOP,
> .flags = PSC_DSP,
> - .usecount = 1, /* REVISIT how to disable? */
> + .usecount = ATOMIC_INIT(1), /* REVISIT how to disable? */
> };
>
> static struct clk vpss_master_clk = {
> @@ -274,7 +274,7 @@ static struct clk timer2_clk = {
> .name = "timer2",
> .parent = &pll1_aux_clk,
> .lpsc = DAVINCI_LPSC_TIMER2,
> - .usecount = 1, /* REVISIT: why cant' this be disabled? */
> + .usecount = ATOMIC_INIT(1), /* REVISIT: why cant' this be disabled? */
> };
>
> struct davinci_clk dm644x_clks[] = {
> diff --git a/arch/arm/mach-davinci/dm646x.c b/arch/arm/mach-davinci/dm646x.c
> index 515d3ed..6f80616 100644
> --- a/arch/arm/mach-davinci/dm646x.c
> +++ b/arch/arm/mach-davinci/dm646x.c
> @@ -60,7 +60,7 @@ static struct clk ref_clk = {
>
> static struct clk aux_clkin = {
> .name = "aux_clkin",
> - .rate = DM646X_AUX_FREQ,
> + .rate = ATOMIC_INIT(DM646X_AUX_FREQ),
> };
>
> static struct clk pll1_clk = {
> @@ -158,7 +158,7 @@ static struct clk dsp_clk = {
> .parent = &pll1_sysclk1,
> .lpsc = DM646X_LPSC_C64X_CPU,
> .flags = PSC_DSP,
> - .usecount = 1, /* REVISIT how to disable? */
> + .usecount = ATOMIC_INIT(1), /* REVISIT how to disable? */
> };
>
> static struct clk arm_clk = {
> @@ -262,14 +262,14 @@ static struct clk pwm0_clk = {
> .name = "pwm0",
> .parent = &pll1_sysclk3,
> .lpsc = DM646X_LPSC_PWM0,
> - .usecount = 1, /* REVIST: disabling hangs system */
> + .usecount = ATOMIC_INIT(1), /* REVIST: disabling hangs system */
> };
>
> static struct clk pwm1_clk = {
> .name = "pwm1",
> .parent = &pll1_sysclk3,
> .lpsc = DM646X_LPSC_PWM1,
> - .usecount = 1, /* REVIST: disabling hangs system */
> + .usecount = ATOMIC_INIT(1), /* REVIST: disabling hangs system */
> };
>
> static struct clk timer0_clk = {
> diff --git a/arch/arm/mach-davinci/psc.c b/arch/arm/mach-davinci/psc.c
> index 04a3cb7..a9a4d08 100644
> --- a/arch/arm/mach-davinci/psc.c
> +++ b/arch/arm/mach-davinci/psc.c
> @@ -21,6 +21,7 @@
> #include <linux/kernel.h>
> #include <linux/init.h>
> #include <linux/io.h>
> +#include <linux/spinlock.h>
>
> #include <mach/cputype.h>
> #include <mach/psc.h>
> @@ -64,6 +65,9 @@ void davinci_psc_config(unsigned int domain, unsigned int
> ctlr,
> void __iomem *psc_base;
> struct davinci_soc_info *soc_info = &davinci_soc_info;
> u32 next_state = enable ? 0x3 : 0x2; /* 0x3 enables, 0x2 disables */
> + /* Protect against simultaneous enable/disable of PSCs */
> + DEFINE_SPINLOCK(lock);
> + unsigned long flags;
>
> if (!soc_info->psc_bases || (ctlr >= soc_info->psc_bases_num)) {
> pr_warning("PSC: Bad psc data: 0x%x[%d]\n",
> @@ -73,6 +77,7 @@ void davinci_psc_config(unsigned int domain, unsigned int
> ctlr,
>
> psc_base = soc_info->psc_bases[ctlr];
>
> + spin_lock_irqsave(&lock, flags);
> mdctl = __raw_readl(psc_base + MDCTL + 4 * id);
> mdctl &= ~MDSTAT_STATE_MASK;
> mdctl |= next_state;
> @@ -111,4 +116,5 @@ void davinci_psc_config(unsigned int domain, unsigned int
> ctlr,
> do {
> mdstat = __raw_readl(psc_base + MDSTAT + 4 * id);
> } while (!((mdstat & MDSTAT_STATE_MASK) == next_state));
> + spin_unlock_irqrestore(&lock, flags);
> }
> --
> 1.6.2.4
>
> _______________________________________________
> Davinci-linux-open-source mailing list
> [email protected]
> http://linux.davincidsp.com/mailman/listinfo/davinci-linux-open-source
_______________________________________________
Davinci-linux-open-source mailing list
[email protected]
http://linux.davincidsp.com/mailman/listinfo/davinci-linux-open-source