diff options
Diffstat (limited to 'arch/arm/mach-ns9xxx/clock.c')
-rw-r--r-- | arch/arm/mach-ns9xxx/clock.c | 128 |
1 files changed, 111 insertions, 17 deletions
diff --git a/arch/arm/mach-ns9xxx/clock.c b/arch/arm/mach-ns9xxx/clock.c index 44ed20d4a388..0344a519707b 100644 --- a/arch/arm/mach-ns9xxx/clock.c +++ b/arch/arm/mach-ns9xxx/clock.c @@ -1,7 +1,7 @@ /* * arch/arm/mach-ns9xxx/clock.c * - * Copyright (C) 2007 by Digi International Inc. + * Copyright (C) 2007-2008 by Digi International Inc. * All rights reserved. * * This program is free software; you can redistribute it and/or modify it @@ -61,22 +61,77 @@ EXPORT_SYMBOL(clk_get); void clk_put(struct clk *clk) { + unsigned long flags; + + spin_lock_irqsave(&clk_lock, flags); + module_put(clk->owner); --clk->refcount; + + spin_unlock_irqrestore(&clk_lock, flags); } EXPORT_SYMBOL(clk_put); -static int clk_enable_unlocked(struct clk *clk) +static int clk_enable_haslock(struct clk *clk); +static int clk_disable_haslock(struct clk *clk); + +static void clk_disable_parent_haslock(struct clk *clk) { + struct clk *parent = clk->parent; int ret = 0; - if (clk->parent) { - ret = clk_enable_unlocked(clk->parent); - if (ret) - return ret; + + if (parent) + ret = clk_disable_haslock(parent); + + if (unlikely(ret)) + pr_warning("failed to disable %s.%d clk -> %d\n", + parent->name, parent->id, ret); +} + +static int clk_enable_haslocknchange(struct clk *clk) +{ + int ret = 0; + + assert_spin_locked(&clk_lock); + BUG_ON(!test_bit(CLK_FLAG_CHANGESTATE, &clk->flags)); + + if (clk->usage++ == 0) { + if (clk->parent) { + ret = clk_enable_haslock(clk->parent); + if (ret) + goto err_enable_parent; + } + + spin_unlock(&clk_lock); + + if (clk->endisable) + ret = clk->endisable(clk, 1); + + spin_lock(&clk_lock); + + if (ret) { + clk_disable_parent_haslock(clk); +err_enable_parent: + + clk->usage = 0; + } + } - if (clk->usage++ == 0 && clk->endisable) - ret = clk->endisable(clk, 1); + return ret; +} + +static int clk_enable_haslock(struct clk *clk) +{ + int ret; + + assert_spin_locked(&clk_lock); + if (__test_and_set_bit(CLK_FLAG_CHANGESTATE, &clk->flags)) + return -EBUSY; + + ret = clk_enable_haslocknchange(clk); + + clear_bit(CLK_FLAG_CHANGESTATE, &clk->flags); return ret; } @@ -88,7 +143,7 @@ int clk_enable(struct clk *clk) spin_lock_irqsave(&clk_lock, flags); - ret = clk_enable_unlocked(clk); + ret = clk_enable_haslock(clk); spin_unlock_irqrestore(&clk_lock, flags); @@ -96,24 +151,60 @@ int clk_enable(struct clk *clk) } EXPORT_SYMBOL(clk_enable); -static void clk_disable_unlocked(struct clk *clk) +static int clk_disable_haslocknchange(struct clk *clk) { - if (--clk->usage == 0 && clk->endisable) - clk->endisable(clk, 0); + int ret = 0; - if (clk->parent) - clk_disable_unlocked(clk->parent); + assert_spin_locked(&clk_lock); + BUG_ON(!test_bit(CLK_FLAG_CHANGESTATE, &clk->flags)); + + BUG_ON(clk->usage == 0); + + if (--clk->usage == 0) { + spin_unlock(&clk_lock); + + if (clk->endisable) + ret = clk->endisable(clk, 0); + + spin_lock(&clk_lock); + + if (ret == 0) + clk_disable_parent_haslock(clk); + else + clk->usage = 1; + } + + return ret; +} + +static int clk_disable_haslock(struct clk *clk) +{ + int ret; + + if (__test_and_set_bit(CLK_FLAG_CHANGESTATE, &clk->flags)) + return -EBUSY; + + ret = clk_disable_haslocknchange(clk); + + clear_bit(CLK_FLAG_CHANGESTATE, &clk->flags); + + return ret; } void clk_disable(struct clk *clk) { + int ret; unsigned long flags; spin_lock_irqsave(&clk_lock, flags); - clk_disable_unlocked(clk); + ret = clk_disable_haslock(clk); spin_unlock_irqrestore(&clk_lock, flags); + + if (unlikely(ret)) + pr_warning("failed to disable %s.%d clk -> %d\n", + clk->name, clk->id, ret); } EXPORT_SYMBOL(clk_disable); @@ -138,6 +229,8 @@ int clk_register(struct clk *clk) spin_lock_irqsave(&clk_lock, flags); + BUG_ON(clk->flags); + list_add(&clk->node, &clocks); if (clk->parent) @@ -181,9 +274,10 @@ static int clk_debugfs_show(struct seq_file *s, void *null) spin_lock_irqsave(&clk_lock, flags); list_for_each_entry(p, &clocks, node) - seq_printf(s, "%s.%d: usage=%lu refcount=%lu rate=%lu\n", + seq_printf(s, "%s.%d: usage=%lu refcount=%lu rate=%lu parent=%s\n", p->name, p->id, p->usage, p->refcount, - p->usage ? clk_get_rate(p) : 0); + p->usage ? clk_get_rate(p) : 0, + p->parent ? p->parent->name : "<nil>"); spin_unlock_irqrestore(&clk_lock, flags); |