Skip to content

Commit

Permalink
sh: clkfwk: refactor rate propagation.
Browse files Browse the repository at this point in the history
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: Paul Mundt <lethal@linux-sh.org>
  • Loading branch information
Paul Mundt committed May 11, 2009
1 parent a02cb23 commit b1f6cfe
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 52 deletions.
9 changes: 6 additions & 3 deletions arch/sh/include/asm/clock.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ struct clk {
struct clk *parent;
struct clk_ops *ops;

struct list_head children;
struct list_head sibling; /* node for children */

int usecount;

unsigned long rate;
Expand All @@ -35,7 +38,6 @@ struct clk {
};

#define CLK_ALWAYS_ENABLED (1 << 0)
#define CLK_RATE_PROPAGATES (1 << 1)
#define CLK_NEEDS_INIT (1 << 2)

/* Should be defined by processor-specific code */
Expand All @@ -44,9 +46,10 @@ int __init arch_clk_init(void);

/* arch/sh/kernel/cpu/clock.c */
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 *);

int clk_register(struct clk *);
void clk_unregister(struct clk *);

Expand Down
120 changes: 78 additions & 42 deletions arch/sh/kernel/cpu/clock.c
Original file line number Diff line number Diff line change
@@ -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 <tuukka.tikkanen@elektrobit.com>
*
* Modified for omap shared clock framework by Tony Lindgren <tony@atomide.com>
Expand Down Expand Up @@ -43,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 = {
Expand All @@ -75,27 +75,24 @@ static struct clk *onchip_clocks[] = {
&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 */
static void propagate_rate(struct clk *clk)
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))
list_for_each_entry(clkp, &tclk->children, sibling) {
if (clkp->ops->recalc)
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)
{
/*
Expand Down Expand Up @@ -180,10 +177,46 @@ void clk_disable(struct clk *clk)
}
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);
clk->usecount = 0;
clk->flags |= CLK_NEEDS_INIT;
Expand All @@ -205,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);
}
Expand All @@ -231,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->rate = 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);
Expand Down Expand Up @@ -457,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;
}
Expand Down
8 changes: 1 addition & 7 deletions arch/sh/kernel/cpu/sh4a/clock-sh7722.c
Original file line number Diff line number Diff line change
Expand Up @@ -161,9 +161,7 @@ static unsigned long master_clk_recalc(struct clk *clk)
static void master_clk_init(struct clk *clk)
{
clk->parent = NULL;
clk->flags |= CLK_RATE_PROPAGATES;
clk->rate = CONFIG_SH_PCLK_FREQ;
master_clk_recalc(clk);
clk->rate = master_clk_recalc(clk);
}

static unsigned long module_clk_recalc(struct clk *clk)
Expand Down Expand Up @@ -541,19 +539,16 @@ static struct clk_ops sh7722_video_clk_ops = {
static struct clk sh7722_umem_clock = {
.name = "umem_clk",
.ops = &sh7722_frqcr_clk_ops,
.flags = CLK_RATE_PROPAGATES,
};

static struct clk sh7722_sh_clock = {
.name = "sh_clk",
.ops = &sh7722_frqcr_clk_ops,
.flags = CLK_RATE_PROPAGATES,
};

static struct clk sh7722_peripheral_clock = {
.name = "peripheral_clk",
.ops = &sh7722_frqcr_clk_ops,
.flags = CLK_RATE_PROPAGATES,
};

static struct clk sh7722_sdram_clock = {
Expand All @@ -564,7 +559,6 @@ static struct clk sh7722_sdram_clock = {
static struct clk sh7722_r_clock = {
.name = "r_clk",
.rate = 32768,
.flags = CLK_RATE_PROPAGATES,
};

#if !defined(CONFIG_CPU_SUBTYPE_SH7343) &&\
Expand Down

0 comments on commit b1f6cfe

Please sign in to comment.