/*
* 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 <tuukka.tikkanen@elektrobit.com>
*
* Modified for omap shared clock framework by Tony Lindgren <tony@atomide.com>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/list.h>
-#include <linux/kref.h>
#include <linux/kobject.h>
#include <linux/sysdev.h>
#include <linux/seq_file.h>
*/
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 = {
&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
* 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;
}
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);
}
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);
}
}
{
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.");
void clk_unregister(struct clk *clk)
{
mutex_lock(&clock_list_sem);
+ list_del(&clk->sibling);
list_del(&clk->node);
mutex_unlock(&clock_list_sem);
}
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);
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");
}
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:
ret |= arch_clk_init();
/* Kick the child clocks.. */
- propagate_rate(&master_clk);
- propagate_rate(&bus_clk);
+ recalculate_root_clocks();
return ret;
}