X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=arch%2Fsh%2Fkernel%2Fcpu%2Fclock.c;h=0a06df8cde2b58ace6b741ba3f917083b468374e;hb=b1f6cfe48c3cb1dfa77db3d2f42f765febaef9bc;hp=1dc896483b591169bcf1254b70e4961ed3e88337;hpb=50fbe56c127efde659c686495eb1f925fd84ff0a;p=~andy%2Flinux diff --git a/arch/sh/kernel/cpu/clock.c b/arch/sh/kernel/cpu/clock.c index 1dc896483b5..0a06df8cde2 100644 --- a/arch/sh/kernel/cpu/clock.c +++ b/arch/sh/kernel/cpu/clock.c @@ -1,11 +1,11 @@ /* * arch/sh/kernel/cpu/clock.c - SuperH clock framework * - * Copyright (C) 2005, 2006, 2007 Paul Mundt + * Copyright (C) 2005 - 2009 Paul Mundt * * This clock framework is derived from the OMAP version by: * - * Copyright (C) 2004 - 2005 Nokia Corporation + * Copyright (C) 2004 - 2008 Nokia Corporation * Written by Tuukka Tikkanen * * Modified for omap shared clock framework by Tony Lindgren @@ -19,7 +19,6 @@ #include #include #include -#include #include #include #include @@ -44,20 +43,20 @@ static DEFINE_MUTEX(clock_list_sem); */ static struct clk master_clk = { .name = "master_clk", - .flags = CLK_ALWAYS_ENABLED | CLK_RATE_PROPAGATES, + .flags = CLK_ALWAYS_ENABLED, .rate = CONFIG_SH_PCLK_FREQ, }; static struct clk module_clk = { .name = "module_clk", .parent = &master_clk, - .flags = CLK_ALWAYS_ENABLED | CLK_RATE_PROPAGATES, + .flags = CLK_ALWAYS_ENABLED, }; static struct clk bus_clk = { .name = "bus_clk", .parent = &master_clk, - .flags = CLK_ALWAYS_ENABLED | CLK_RATE_PROPAGATES, + .flags = CLK_ALWAYS_ENABLED, }; static struct clk cpu_clk = { @@ -76,21 +75,25 @@ static struct clk *onchip_clocks[] = { &cpu_clk, }; -static void propagate_rate(struct clk *clk) +/* Used for clocks that always have same value as the parent clock */ +unsigned long followparent_recalc(struct clk *clk) +{ + return clk->parent->rate; +} + +/* Propagate rate to children */ +void propagate_rate(struct clk *tclk) { struct clk *clkp; - list_for_each_entry(clkp, &clock_list, node) { - if (likely(clkp->parent != clk)) - continue; - if (likely(clkp->ops && clkp->ops->recalc)) - clkp->ops->recalc(clkp); - if (unlikely(clkp->flags & CLK_RATE_PROPAGATES)) - propagate_rate(clkp); + list_for_each_entry(clkp, &tclk->children, sibling) { + if (clkp->ops->recalc) + clkp->rate = clkp->ops->recalc(clkp); + propagate_rate(clkp); } } -static int __clk_enable(struct clk *clk) +static void __clk_init(struct clk *clk) { /* * See if this is the first time we're enabling the clock, some @@ -99,17 +102,34 @@ static int __clk_enable(struct clk *clk) * changes and the clock needs to hunt for the proper set of * divisors to use before it can effectively recalc. */ - if (unlikely(atomic_read(&clk->kref.refcount) == 1)) + + if (clk->flags & CLK_NEEDS_INIT) { if (clk->ops && clk->ops->init) clk->ops->init(clk); - kref_get(&clk->kref); + clk->flags &= ~CLK_NEEDS_INIT; + } +} + +static int __clk_enable(struct clk *clk) +{ + if (!clk) + return -EINVAL; + + clk->usecount++; + /* nothing to do if always enabled */ if (clk->flags & CLK_ALWAYS_ENABLED) return 0; - if (likely(clk->ops && clk->ops->enable)) - clk->ops->enable(clk); + if (clk->usecount == 1) { + __clk_init(clk); + + __clk_enable(clk->parent); + + if (clk->ops && clk->ops->enable) + clk->ops->enable(clk); + } return 0; } @@ -119,11 +139,6 @@ int clk_enable(struct clk *clk) unsigned long flags; int ret; - if (!clk) - return -EINVAL; - - clk_enable(clk->parent); - spin_lock_irqsave(&clock_lock, flags); ret = __clk_enable(clk); spin_unlock_irqrestore(&clock_lock, flags); @@ -132,21 +147,23 @@ int clk_enable(struct clk *clk) } EXPORT_SYMBOL_GPL(clk_enable); -static void clk_kref_release(struct kref *kref) -{ - /* Nothing to do */ -} - static void __clk_disable(struct clk *clk) { - int count = kref_put(&clk->kref, clk_kref_release); + if (!clk) + return; + + clk->usecount--; + + WARN_ON(clk->usecount < 0); if (clk->flags & CLK_ALWAYS_ENABLED) return; - if (!count) { /* count reaches zero, disable the clock */ + if (clk->usecount == 0) { if (likely(clk->ops && clk->ops->disable)) clk->ops->disable(clk); + + __clk_disable(clk->parent); } } @@ -154,30 +171,61 @@ void clk_disable(struct clk *clk) { unsigned long flags; - if (!clk) - return; - spin_lock_irqsave(&clock_lock, flags); __clk_disable(clk); spin_unlock_irqrestore(&clock_lock, flags); - - clk_disable(clk->parent); } EXPORT_SYMBOL_GPL(clk_disable); +static LIST_HEAD(root_clks); + +/** + * recalculate_root_clocks - recalculate and propagate all root clocks + * + * Recalculates all root clocks (clocks with no parent), which if the + * clock's .recalc is set correctly, should also propagate their rates. + * Called at init. + */ +void recalculate_root_clocks(void) +{ + struct clk *clkp; + + list_for_each_entry(clkp, &root_clks, sibling) { + if (clkp->ops->recalc) + clkp->rate = clkp->ops->recalc(clkp); + propagate_rate(clkp); + } +} + int clk_register(struct clk *clk) { + if (clk == NULL || IS_ERR(clk)) + return -EINVAL; + + /* + * trap out already registered clocks + */ + if (clk->node.next || clk->node.prev) + return 0; + mutex_lock(&clock_list_sem); + INIT_LIST_HEAD(&clk->children); + + if (clk->parent) + list_add(&clk->sibling, &clk->parent->children); + else + list_add(&clk->sibling, &root_clks); + list_add(&clk->node, &clock_list); - kref_init(&clk->kref); + clk->usecount = 0; + clk->flags |= CLK_NEEDS_INIT; mutex_unlock(&clock_list_sem); if (clk->flags & CLK_ALWAYS_ENABLED) { + __clk_init(clk); pr_debug( "Clock '%s' is ALWAYS_ENABLED\n", clk->name); - if (clk->ops && clk->ops->init) - clk->ops->init(clk); if (clk->ops && clk->ops->enable) clk->ops->enable(clk); pr_debug( "Enabled."); @@ -190,6 +238,7 @@ EXPORT_SYMBOL_GPL(clk_register); void clk_unregister(struct clk *clk) { mutex_lock(&clock_list_sem); + list_del(&clk->sibling); list_del(&clk->node); mutex_unlock(&clock_list_sem); } @@ -216,50 +265,53 @@ int clk_set_rate_ex(struct clk *clk, unsigned long rate, int algo_id) spin_lock_irqsave(&clock_lock, flags); ret = clk->ops->set_rate(clk, rate, algo_id); + if (ret == 0) { + if (clk->ops->recalc) + clk->rate = clk->ops->recalc(clk); + propagate_rate(clk); + } spin_unlock_irqrestore(&clock_lock, flags); } - if (unlikely(clk->flags & CLK_RATE_PROPAGATES)) - propagate_rate(clk); - return ret; } EXPORT_SYMBOL_GPL(clk_set_rate_ex); void clk_recalc_rate(struct clk *clk) { - if (likely(clk->ops && clk->ops->recalc)) { - unsigned long flags; + unsigned long flags; - spin_lock_irqsave(&clock_lock, flags); - clk->ops->recalc(clk); - spin_unlock_irqrestore(&clock_lock, flags); - } + if (!clk->ops->recalc) + return; - if (unlikely(clk->flags & CLK_RATE_PROPAGATES)) - propagate_rate(clk); + spin_lock_irqsave(&clock_lock, flags); + clk->rate = clk->ops->recalc(clk); + propagate_rate(clk); + spin_unlock_irqrestore(&clock_lock, flags); } EXPORT_SYMBOL_GPL(clk_recalc_rate); int clk_set_parent(struct clk *clk, struct clk *parent) { + unsigned long flags; int ret = -EINVAL; - struct clk *old; if (!parent || !clk) return ret; - old = clk->parent; - if (likely(clk->ops && clk->ops->set_parent)) { - unsigned long flags; - spin_lock_irqsave(&clock_lock, flags); - ret = clk->ops->set_parent(clk, parent); - spin_unlock_irqrestore(&clock_lock, flags); - clk->parent = (ret ? old : parent); - } + spin_lock_irqsave(&clock_lock, flags); + if (clk->usecount == 0) { + if (clk->ops->set_parent) + ret = clk->ops->set_parent(clk, parent); + if (ret == 0) { + if (clk->ops->recalc) + clk->rate = clk->ops->recalc(clk); + propagate_rate(clk); + } + } else + ret = -EBUSY; + spin_unlock_irqrestore(&clock_lock, flags); - if (unlikely(clk->flags & CLK_RATE_PROPAGATES)) - propagate_rate(clk); return ret; } EXPORT_SYMBOL_GPL(clk_set_parent); @@ -353,7 +405,7 @@ static int show_clocks(char *buf, char **start, off_t off, p += sprintf(p, "%-12s\t: %ld.%02ldMHz\t%s\n", clk->name, rate / 1000000, (rate % 1000000) / 10000, ((clk->flags & CLK_ALWAYS_ENABLED) || - (atomic_read(&clk->kref.refcount) != 1)) ? + clk->usecount > 0) ? "enabled" : "disabled"); } @@ -369,20 +421,22 @@ static int clks_sysdev_suspend(struct sys_device *dev, pm_message_t state) switch (state.event) { case PM_EVENT_ON: /* Resumeing from hibernation */ - if (prev_state.event == PM_EVENT_FREEZE) { - list_for_each_entry(clkp, &clock_list, node) - if (likely(clkp->ops)) { - unsigned long rate = clkp->rate; - - if (likely(clkp->ops->set_parent)) - clkp->ops->set_parent(clkp, - clkp->parent); - if (likely(clkp->ops->set_rate)) - clkp->ops->set_rate(clkp, - rate, NO_CHANGE); - else if (likely(clkp->ops->recalc)) - clkp->ops->recalc(clkp); - } + if (prev_state.event != PM_EVENT_FREEZE) + break; + + list_for_each_entry(clkp, &clock_list, node) { + if (likely(clkp->ops)) { + unsigned long rate = clkp->rate; + + if (likely(clkp->ops->set_parent)) + clkp->ops->set_parent(clkp, + clkp->parent); + if (likely(clkp->ops->set_rate)) + clkp->ops->set_rate(clkp, + rate, NO_CHANGE); + else if (likely(clkp->ops->recalc)) + clkp->rate = clkp->ops->recalc(clkp); + } } break; case PM_EVENT_FREEZE: @@ -440,8 +494,7 @@ int __init clk_init(void) ret |= arch_clk_init(); /* Kick the child clocks.. */ - propagate_rate(&master_clk); - propagate_rate(&bus_clk); + recalculate_root_clocks(); return ret; }