Skip to content

Commit

Permalink
sh: clkfwk: support clock remapping.
Browse files Browse the repository at this point in the history
This implements support for ioremapping of register windows that
encapsulate clock control registers used by a struct clk, with
transparent sibling inheritance.

Root clocks at the top of a given topology often encapsulate the entire
register space of all of their sibling clocks, so this mapping can be
done once and handed down. A given clock enable/disable case maps out to
a single bit in a shared register, so this prevents creating multiple
overlapping mappings.

The mapping case breaks down in to a couple of different situations:

	- Sibling clocks without a specific mapping.
	- Root clocks without a specific mapping.
	- Any of sibling/root clocks with a specific mapping.

Sibling clocks with no specified mapping will grovel up the clock chain
and install the root clock mapping unconditionally at registration time.

Root clocks without their own mappings have a dummy BSS-initialized
mapping inserted that is handed down the chain just like any other
mapping. This permits all of the sibling clock ops to read/write using
the mapping offsets without any special configuration, enabling them to
not care whether access ultimately goes through translatable or
untranslatable memory.

Any clock with its own mapping will have the window initialized at
registration time and be ready for use by its clock ops. Failure to
establish the mapping will prevent registration, so no additional sanity
checks are needed. Sibling clocks that double as parents for the moment
will not propagate their mapping down, but this is easily tunable if the
need arises.

All clock mappings are kref refcounted, with each instance of mapping
inheritance incrementing the refcount.

Tested-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Signed-off-by: Paul Mundt <lethal@linux-sh.org>
  • Loading branch information
Paul Mundt committed Oct 15, 2010
1 parent a80be16 commit 28085bc
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 2 deletions.
91 changes: 89 additions & 2 deletions drivers/sh/clk.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
#include <linux/sysdev.h>
#include <linux/seq_file.h>
#include <linux/err.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/debugfs.h>
#include <linux/cpufreq.h>
#include <linux/clk.h>
Expand Down Expand Up @@ -251,8 +251,88 @@ void recalculate_root_clocks(void)
}
}

static struct clk_mapping dummy_mapping;

static struct clk *lookup_root_clock(struct clk *clk)
{
while (clk->parent)
clk = clk->parent;

return clk;
}

static int clk_establish_mapping(struct clk *clk)
{
struct clk_mapping *mapping = clk->mapping;

/*
* Propagate mappings.
*/
if (!mapping) {
struct clk *clkp;

/*
* dummy mapping for root clocks with no specified ranges
*/
if (!clk->parent) {
clk->mapping = &dummy_mapping;
return 0;
}

/*
* If we're on a child clock and it provides no mapping of its
* own, inherit the mapping from its root clock.
*/
clkp = lookup_root_clock(clk);
mapping = clkp->mapping;
BUG_ON(!mapping);
}

/*
* Establish initial mapping.
*/
if (!mapping->base && mapping->phys) {
kref_init(&mapping->ref);

mapping->base = ioremap_nocache(mapping->phys, mapping->len);
if (unlikely(!mapping->base))
return -ENXIO;
} else if (mapping->base) {
/*
* Bump the refcount for an existing mapping
*/
kref_get(&mapping->ref);
}

clk->mapping = mapping;
return 0;
}

static void clk_destroy_mapping(struct kref *kref)
{
struct clk_mapping *mapping;

mapping = container_of(kref, struct clk_mapping, ref);

iounmap(mapping->base);
}

static void clk_teardown_mapping(struct clk *clk)
{
struct clk_mapping *mapping = clk->mapping;

/* Nothing to do */
if (mapping == &dummy_mapping)
return;

kref_put(&mapping->ref, clk_destroy_mapping);
clk->mapping = NULL;
}

int clk_register(struct clk *clk)
{
int ret;

if (clk == NULL || IS_ERR(clk))
return -EINVAL;

Expand All @@ -267,6 +347,10 @@ int clk_register(struct clk *clk)
INIT_LIST_HEAD(&clk->children);
clk->usecount = 0;

ret = clk_establish_mapping(clk);
if (unlikely(ret))
goto out_unlock;

if (clk->parent)
list_add(&clk->sibling, &clk->parent->children);
else
Expand All @@ -275,9 +359,11 @@ int clk_register(struct clk *clk)
list_add(&clk->node, &clock_list);
if (clk->ops && clk->ops->init)
clk->ops->init(clk);

out_unlock:
mutex_unlock(&clock_list_sem);

return 0;
return ret;
}
EXPORT_SYMBOL_GPL(clk_register);

Expand All @@ -286,6 +372,7 @@ void clk_unregister(struct clk *clk)
mutex_lock(&clock_list_sem);
list_del(&clk->sibling);
list_del(&clk->node);
clk_teardown_mapping(clk);
mutex_unlock(&clock_list_sem);
}
EXPORT_SYMBOL_GPL(clk_unregister);
Expand Down
10 changes: 10 additions & 0 deletions include/linux/sh_clk.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,20 @@
#include <linux/list.h>
#include <linux/seq_file.h>
#include <linux/cpufreq.h>
#include <linux/types.h>
#include <linux/kref.h>
#include <linux/clk.h>
#include <linux/err.h>

struct clk;

struct clk_mapping {
phys_addr_t phys;
void __iomem *base;
unsigned long len;
struct kref ref;
};

struct clk_ops {
void (*init)(struct clk *clk);
int (*enable)(struct clk *clk);
Expand Down Expand Up @@ -42,6 +51,7 @@ struct clk {
unsigned long arch_flags;
void *priv;
struct dentry *dentry;
struct clk_mapping *mapping;
struct cpufreq_frequency_table *freq_table;
};

Expand Down

0 comments on commit 28085bc

Please sign in to comment.