Commit b1f6cfe4 authored by Paul Mundt's avatar Paul Mundt

sh: clkfwk: refactor rate propagation.

This resyncs the rate propagation strategy with the scheme used by the
OMAP clock framework. Child clocks are tracked on a list under each
parent and propagation happens there specifically rather than constantly
iterating over the global clock list.
Signed-off-by: default avatarPaul Mundt <lethal@linux-sh.org>
parent a02cb230
...@@ -27,6 +27,9 @@ struct clk { ...@@ -27,6 +27,9 @@ struct clk {
struct clk *parent; struct clk *parent;
struct clk_ops *ops; struct clk_ops *ops;
struct list_head children;
struct list_head sibling; /* node for children */
int usecount; int usecount;
unsigned long rate; unsigned long rate;
...@@ -35,7 +38,6 @@ struct clk { ...@@ -35,7 +38,6 @@ struct clk {
}; };
#define CLK_ALWAYS_ENABLED (1 << 0) #define CLK_ALWAYS_ENABLED (1 << 0)
#define CLK_RATE_PROPAGATES (1 << 1)
#define CLK_NEEDS_INIT (1 << 2) #define CLK_NEEDS_INIT (1 << 2)
/* Should be defined by processor-specific code */ /* Should be defined by processor-specific code */
...@@ -44,9 +46,10 @@ int __init arch_clk_init(void); ...@@ -44,9 +46,10 @@ int __init arch_clk_init(void);
/* arch/sh/kernel/cpu/clock.c */ /* arch/sh/kernel/cpu/clock.c */
int clk_init(void); int clk_init(void);
unsigned long followparent_recalc(struct clk *clk); unsigned long followparent_recalc(struct clk *);
void recalculate_root_clocks(void);
void propagate_rate(struct clk *);
void clk_recalc_rate(struct clk *); void clk_recalc_rate(struct clk *);
int clk_register(struct clk *); int clk_register(struct clk *);
void clk_unregister(struct clk *); void clk_unregister(struct clk *);
......
/* /*
* arch/sh/kernel/cpu/clock.c - SuperH clock framework * 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: * 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> * Written by Tuukka Tikkanen <tuukka.tikkanen@elektrobit.com>
* *
* Modified for omap shared clock framework by Tony Lindgren <tony@atomide.com> * Modified for omap shared clock framework by Tony Lindgren <tony@atomide.com>
...@@ -43,20 +43,20 @@ static DEFINE_MUTEX(clock_list_sem); ...@@ -43,20 +43,20 @@ static DEFINE_MUTEX(clock_list_sem);
*/ */
static struct clk master_clk = { static struct clk master_clk = {
.name = "master_clk", .name = "master_clk",
.flags = CLK_ALWAYS_ENABLED | CLK_RATE_PROPAGATES, .flags = CLK_ALWAYS_ENABLED,
.rate = CONFIG_SH_PCLK_FREQ, .rate = CONFIG_SH_PCLK_FREQ,
}; };
static struct clk module_clk = { static struct clk module_clk = {
.name = "module_clk", .name = "module_clk",
.parent = &master_clk, .parent = &master_clk,
.flags = CLK_ALWAYS_ENABLED | CLK_RATE_PROPAGATES, .flags = CLK_ALWAYS_ENABLED,
}; };
static struct clk bus_clk = { static struct clk bus_clk = {
.name = "bus_clk", .name = "bus_clk",
.parent = &master_clk, .parent = &master_clk,
.flags = CLK_ALWAYS_ENABLED | CLK_RATE_PROPAGATES, .flags = CLK_ALWAYS_ENABLED,
}; };
static struct clk cpu_clk = { static struct clk cpu_clk = {
...@@ -75,27 +75,24 @@ static struct clk *onchip_clocks[] = { ...@@ -75,27 +75,24 @@ static struct clk *onchip_clocks[] = {
&cpu_clk, &cpu_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 */ /* Propagate rate to children */
static void propagate_rate(struct clk *clk) void propagate_rate(struct clk *tclk)
{ {
struct clk *clkp; struct clk *clkp;
list_for_each_entry(clkp, &clock_list, node) { list_for_each_entry(clkp, &tclk->children, sibling) {
if (likely(clkp->parent != clk)) if (clkp->ops->recalc)
continue;
if (likely(clkp->ops && clkp->ops->recalc))
clkp->rate = clkp->ops->recalc(clkp); clkp->rate = clkp->ops->recalc(clkp);
if (unlikely(clkp->flags & CLK_RATE_PROPAGATES)) propagate_rate(clkp);
propagate_rate(clkp);
} }
} }
/* Used for clocks that always have same value as the parent clock */
unsigned long followparent_recalc(struct clk *clk)
{
return clk->parent->rate;
}
static void __clk_init(struct clk *clk) static void __clk_init(struct clk *clk)
{ {
/* /*
...@@ -180,10 +177,46 @@ void clk_disable(struct clk *clk) ...@@ -180,10 +177,46 @@ void clk_disable(struct clk *clk)
} }
EXPORT_SYMBOL_GPL(clk_disable); 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) 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); 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); list_add(&clk->node, &clock_list);
clk->usecount = 0; clk->usecount = 0;
clk->flags |= CLK_NEEDS_INIT; clk->flags |= CLK_NEEDS_INIT;
...@@ -205,6 +238,7 @@ EXPORT_SYMBOL_GPL(clk_register); ...@@ -205,6 +238,7 @@ EXPORT_SYMBOL_GPL(clk_register);
void clk_unregister(struct clk *clk) void clk_unregister(struct clk *clk)
{ {
mutex_lock(&clock_list_sem); mutex_lock(&clock_list_sem);
list_del(&clk->sibling);
list_del(&clk->node); list_del(&clk->node);
mutex_unlock(&clock_list_sem); mutex_unlock(&clock_list_sem);
} }
...@@ -231,50 +265,53 @@ int clk_set_rate_ex(struct clk *clk, unsigned long rate, int algo_id) ...@@ -231,50 +265,53 @@ int clk_set_rate_ex(struct clk *clk, unsigned long rate, int algo_id)
spin_lock_irqsave(&clock_lock, flags); spin_lock_irqsave(&clock_lock, flags);
ret = clk->ops->set_rate(clk, rate, algo_id); 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); spin_unlock_irqrestore(&clock_lock, flags);
} }
if (unlikely(clk->flags & CLK_RATE_PROPAGATES))
propagate_rate(clk);
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(clk_set_rate_ex); EXPORT_SYMBOL_GPL(clk_set_rate_ex);
void clk_recalc_rate(struct clk *clk) 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); if (!clk->ops->recalc)
clk->rate = clk->ops->recalc(clk); return;
spin_unlock_irqrestore(&clock_lock, flags);
}
if (unlikely(clk->flags & CLK_RATE_PROPAGATES)) spin_lock_irqsave(&clock_lock, flags);
propagate_rate(clk); clk->rate = clk->ops->recalc(clk);
propagate_rate(clk);
spin_unlock_irqrestore(&clock_lock, flags);
} }
EXPORT_SYMBOL_GPL(clk_recalc_rate); EXPORT_SYMBOL_GPL(clk_recalc_rate);
int clk_set_parent(struct clk *clk, struct clk *parent) int clk_set_parent(struct clk *clk, struct clk *parent)
{ {
unsigned long flags;
int ret = -EINVAL; int ret = -EINVAL;
struct clk *old;
if (!parent || !clk) if (!parent || !clk)
return ret; return ret;
old = clk->parent; spin_lock_irqsave(&clock_lock, flags);
if (likely(clk->ops && clk->ops->set_parent)) { if (clk->usecount == 0) {
unsigned long flags; if (clk->ops->set_parent)
spin_lock_irqsave(&clock_lock, flags); ret = clk->ops->set_parent(clk, parent);
ret = clk->ops->set_parent(clk, parent); if (ret == 0) {
spin_unlock_irqrestore(&clock_lock, flags); if (clk->ops->recalc)
clk->parent = (ret ? old : parent); 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; return ret;
} }
EXPORT_SYMBOL_GPL(clk_set_parent); EXPORT_SYMBOL_GPL(clk_set_parent);
...@@ -457,8 +494,7 @@ int __init clk_init(void) ...@@ -457,8 +494,7 @@ int __init clk_init(void)
ret |= arch_clk_init(); ret |= arch_clk_init();
/* Kick the child clocks.. */ /* Kick the child clocks.. */
propagate_rate(&master_clk); recalculate_root_clocks();
propagate_rate(&bus_clk);
return ret; return ret;
} }
......
...@@ -161,9 +161,7 @@ static unsigned long master_clk_recalc(struct clk *clk) ...@@ -161,9 +161,7 @@ static unsigned long master_clk_recalc(struct clk *clk)
static void master_clk_init(struct clk *clk) static void master_clk_init(struct clk *clk)
{ {
clk->parent = NULL; clk->parent = NULL;
clk->flags |= CLK_RATE_PROPAGATES; clk->rate = master_clk_recalc(clk);
clk->rate = CONFIG_SH_PCLK_FREQ;
master_clk_recalc(clk);
} }
static unsigned long module_clk_recalc(struct clk *clk) static unsigned long module_clk_recalc(struct clk *clk)
...@@ -541,19 +539,16 @@ static struct clk_ops sh7722_video_clk_ops = { ...@@ -541,19 +539,16 @@ static struct clk_ops sh7722_video_clk_ops = {
static struct clk sh7722_umem_clock = { static struct clk sh7722_umem_clock = {
.name = "umem_clk", .name = "umem_clk",
.ops = &sh7722_frqcr_clk_ops, .ops = &sh7722_frqcr_clk_ops,
.flags = CLK_RATE_PROPAGATES,
}; };
static struct clk sh7722_sh_clock = { static struct clk sh7722_sh_clock = {
.name = "sh_clk", .name = "sh_clk",
.ops = &sh7722_frqcr_clk_ops, .ops = &sh7722_frqcr_clk_ops,
.flags = CLK_RATE_PROPAGATES,
}; };
static struct clk sh7722_peripheral_clock = { static struct clk sh7722_peripheral_clock = {
.name = "peripheral_clk", .name = "peripheral_clk",
.ops = &sh7722_frqcr_clk_ops, .ops = &sh7722_frqcr_clk_ops,
.flags = CLK_RATE_PROPAGATES,
}; };
static struct clk sh7722_sdram_clock = { static struct clk sh7722_sdram_clock = {
...@@ -564,7 +559,6 @@ static struct clk sh7722_sdram_clock = { ...@@ -564,7 +559,6 @@ static struct clk sh7722_sdram_clock = {
static struct clk sh7722_r_clock = { static struct clk sh7722_r_clock = {
.name = "r_clk", .name = "r_clk",
.rate = 32768, .rate = 32768,
.flags = CLK_RATE_PROPAGATES,
}; };
#if !defined(CONFIG_CPU_SUBTYPE_SH7343) &&\ #if !defined(CONFIG_CPU_SUBTYPE_SH7343) &&\
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment