diff options
Diffstat (limited to 'arch/sh/kernel/clkdev.c')
| -rw-r--r-- | arch/sh/kernel/clkdev.c | 169 | 
1 files changed, 169 insertions, 0 deletions
diff --git a/arch/sh/kernel/clkdev.c b/arch/sh/kernel/clkdev.c new file mode 100644 index 00000000000..defdd6e3090 --- /dev/null +++ b/arch/sh/kernel/clkdev.c @@ -0,0 +1,169 @@ +/* + * arch/sh/kernel/clkdev.c + * + * Cloned from 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 <linux/clk.h> +#include <linux/slab.h> +#include <linux/bootmem.h> +#include <linux/mm.h> +#include <asm/clock.h> +#include <asm/clkdev.h> + +static LIST_HEAD(clocks); +static DEFINE_MUTEX(clocks_mutex); + +/* + * Find the correct struct clk for the device and connection ID. + * We do slightly fuzzy matching here: + *  An entry with a NULL ID is assumed to be a wildcard. + *  If an entry has a device ID, it must match + *  If an entry has a connection ID, it must match + * Then we take the most specific entry - with the following + * order of precidence: dev+con > dev only > con only. + */ +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) { +		match = 0; +		if (p->dev_id) { +			if (!dev_id || strcmp(p->dev_id, dev_id)) +				continue; +			match += 2; +		} +		if (p->con_id) { +			if (!con_id || strcmp(p->con_id, con_id)) +				continue; +			match += 1; +		} +		if (match == 0) +			continue; + +		if (match > best) { +			clk = p->clk; +			best = match; +		} +	} +	return clk; +} + +struct clk *clk_get_sys(const char *dev_id, const char *con_id) +{ +	struct clk *clk; + +	mutex_lock(&clocks_mutex); +	clk = clk_find(dev_id, con_id); +	mutex_unlock(&clocks_mutex); + +	return clk ? clk : ERR_PTR(-ENOENT); +} +EXPORT_SYMBOL(clk_get_sys); + +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); + +void __init clkdev_add_table(struct clk_lookup *cl, size_t num) +{ +	mutex_lock(&clocks_mutex); +	while (num--) { +		list_add_tail(&cl->node, &clocks); +		cl++; +	} +	mutex_unlock(&clocks_mutex); +} + +#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 * __init_refok +clkdev_alloc(struct clk *clk, const char *con_id, const char *dev_fmt, ...) +{ +	struct clk_lookup_alloc *cla; + +	if (!slab_is_available()) +		cla = alloc_bootmem_low_pages(sizeof(*cla)); +	else +		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); + +int clk_add_alias(const char *alias, const char *alias_dev_name, char *id, +	struct device *dev) +{ +	struct clk *r = clk_get(dev, id); +	struct clk_lookup *l; + +	if (IS_ERR(r)) +		return PTR_ERR(r); + +	l = clkdev_alloc(r, alias, alias_dev_name); +	clk_put(r); +	if (!l) +		return -ENODEV; +	clkdev_add(l); +	return 0; +} +EXPORT_SYMBOL(clk_add_alias); + +/* + * 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);  |