diff options
Diffstat (limited to 'arch/arm/common/clkdev.c')
| -rw-r--r-- | arch/arm/common/clkdev.c | 128 | 
1 files changed, 128 insertions, 0 deletions
diff --git a/arch/arm/common/clkdev.c b/arch/arm/common/clkdev.c new file mode 100644 index 00000000000..17a17b49a45 --- /dev/null +++ b/arch/arm/common/clkdev.c @@ -0,0 +1,128 @@ +/* + *  arch/arm/common/clkdev.c + * + *  Copyright (C) 2008 Russell King. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Helper for the clk API to assist looking up a struct clk. + */ +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/device.h> +#include <linux/list.h> +#include <linux/errno.h> +#include <linux/err.h> +#include <linux/string.h> +#include <linux/mutex.h> + +#include <asm/clkdev.h> +#include <mach/clkdev.h> + +static LIST_HEAD(clocks); +static DEFINE_MUTEX(clocks_mutex); + +static struct clk *clk_find(const char *dev_id, const char *con_id) +{ +	struct clk_lookup *p; +	struct clk *clk = NULL; +	int match, best = 0; + +	list_for_each_entry(p, &clocks, node) { +		if ((p->dev_id && !dev_id) || (p->con_id && !con_id)) +			continue; +		match = 0; +		if (p->dev_id) +			match += 2 * (strcmp(p->dev_id, dev_id) == 0); +		if (p->con_id) +			match += 1 * (strcmp(p->con_id, con_id) == 0); +		if (match == 0) +			continue; + +		if (match > best) { +			clk = p->clk; +			best = match; +		} +	} +	return clk; +} + +struct clk *clk_get(struct device *dev, const char *con_id) +{ +	const char *dev_id = dev ? dev_name(dev) : NULL; +	struct clk *clk; + +	mutex_lock(&clocks_mutex); +	clk = clk_find(dev_id, con_id); +	if (clk && !__clk_get(clk)) +		clk = NULL; +	mutex_unlock(&clocks_mutex); + +	return clk ? clk : ERR_PTR(-ENOENT); +} +EXPORT_SYMBOL(clk_get); + +void clk_put(struct clk *clk) +{ +	__clk_put(clk); +} +EXPORT_SYMBOL(clk_put); + +void clkdev_add(struct clk_lookup *cl) +{ +	mutex_lock(&clocks_mutex); +	list_add_tail(&cl->node, &clocks); +	mutex_unlock(&clocks_mutex); +} +EXPORT_SYMBOL(clkdev_add); + +#define MAX_DEV_ID	20 +#define MAX_CON_ID	16 + +struct clk_lookup_alloc { +	struct clk_lookup cl; +	char	dev_id[MAX_DEV_ID]; +	char	con_id[MAX_CON_ID]; +}; + +struct clk_lookup *clkdev_alloc(struct clk *clk, const char *con_id, +	const char *dev_fmt, ...) +{ +	struct clk_lookup_alloc *cla; + +	cla = kzalloc(sizeof(*cla), GFP_KERNEL); +	if (!cla) +		return NULL; + +	cla->cl.clk = clk; +	if (con_id) { +		strlcpy(cla->con_id, con_id, sizeof(cla->con_id)); +		cla->cl.con_id = cla->con_id; +	} + +	if (dev_fmt) { +		va_list ap; + +		va_start(ap, dev_fmt); +		vscnprintf(cla->dev_id, sizeof(cla->dev_id), dev_fmt, ap); +		cla->cl.dev_id = cla->dev_id; +		va_end(ap); +	} + +	return &cla->cl; +} +EXPORT_SYMBOL(clkdev_alloc); + +/* + * clkdev_drop - remove a clock dynamically allocated + */ +void clkdev_drop(struct clk_lookup *cl) +{ +	mutex_lock(&clocks_mutex); +	list_del(&cl->node); +	mutex_unlock(&clocks_mutex); +	kfree(cl); +} +EXPORT_SYMBOL(clkdev_drop);  | 
