* [PATCH 0/6] Clocklib: generic clocks framework
@ 2008-03-31 8:39 Dmitry Baryshkov
2008-03-31 8:44 ` [PATCH 1/6] Clocklib: add generic framework for managing clocks Dmitry Baryshkov
` (5 more replies)
0 siblings, 6 replies; 12+ messages in thread
From: Dmitry Baryshkov @ 2008-03-31 8:39 UTC (permalink / raw)
To: linux-kernel
Cc: akpm, Haavard Skinnemoen, Russell King, Paul Mundt,
pHilipp Zabel, Pavel Machek, tony, paul
Hi,
Here is a new version based upon suggestions from Haavard and Russell.
Changes since last post:
* Split struct clk into clk and clk_ops
* Hide most non-generic things into clock type specific structs wrapping
struct clk.
* Provide at least partial documentation for structures and fields.
* Better /sys/kernel/debug/clock formatting
* Incorporates sa1100 and PXA patches.
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 12+ messages in thread* [PATCH 1/6] Clocklib: add generic framework for managing clocks. 2008-03-31 8:39 [PATCH 0/6] Clocklib: generic clocks framework Dmitry Baryshkov @ 2008-03-31 8:44 ` Dmitry Baryshkov 2008-03-31 14:55 ` Pavel Machek 2008-03-31 8:44 ` [PATCH 2/6] Clocklib: debugfs support Dmitry Baryshkov ` (4 subsequent siblings) 5 siblings, 1 reply; 12+ messages in thread From: Dmitry Baryshkov @ 2008-03-31 8:44 UTC (permalink / raw) To: linux-kernel Cc: akpm, Haavard Skinnemoen, Russell King, Paul Mundt, pHilipp Zabel, Pavel Machek, tony, paul Provide a generic framework that platform may choose to support clocks api. In particular this provides platform-independant struct clk definition, a full implementation of clocks api and a set of functions for registering and unregistering clocks in a safe way. Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com> --- include/linux/clklib.h | 149 ++++++++++++++++++++++ init/Kconfig | 7 + kernel/Makefile | 1 + kernel/clklib.c | 326 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 483 insertions(+), 0 deletions(-) create mode 100644 include/linux/clklib.h create mode 100644 kernel/clklib.c diff --git a/include/linux/clklib.h b/include/linux/clklib.h new file mode 100644 index 0000000..88ab2c1 --- /dev/null +++ b/include/linux/clklib.h @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2008 Dmitry Baryshkov + * + * This file is released under the GPL v2. + */ + +#ifndef CLKLIB_H +#define CLKLIB_H + +#include <linux/list.h> + +struct seq_file; + +/** + * struct clk_ops - generic clock management operations + * @can_get: checks whether passed device can get this clock + * @set_parent: reconfigures the clock to use specified parent + * @set_mode: enable or disable specified clock + * @get_rate: obtain the current clock rate of a specified clock + * @set_rate: set the clock rate for a specified clock + * @round_rate: adjust a reate to the exact rate a clock can provide + * + * This structure specifies clock operations that are used to configure + * specific clock. + */ +struct clk_ops { + int (*can_get)(struct clk *clk, struct device *dev); + int (*set_parent)(struct clk *clk, struct clk *parent); + int (*set_mode) (struct clk *clk, bool enable); + unsigned long (*get_rate) (struct clk *clk); + int (*set_rate) (struct clk *, unsigned long); + long (*round_rate) (struct clk *, unsigned long); +}; + +/** + * struct clk - generic struct clk implementation used in the clocklib + * @node: used to place all clocks in a list + * @parent: the parent clock + * @owner: module holding all the functions + * @name: the name of this clock + * @users: count how many users have enabled this clock + * @ops: a pointer to clock management operations + */ +struct clk { + struct list_head node; + struct clk *parent; + struct module *owner; + + const char *name; + int users; + + const struct clk_ops *ops; +}; + +int __must_check clk_register(struct clk *clk); +void clk_unregister(struct clk *clk); +static void __maybe_unused clks_unregister(struct clk **clks, size_t num) +{ + int i; + for (i = num - 1; i >= 0; i++) { + clk_unregister(clks[i]); + } +} + +static int __must_check __maybe_unused clks_register(struct clk **clks, size_t num) +{ + int i; + int ret; + for (i = 0; i < num; i++) { + ret = clk_register(clks[i]); + if (ret != 0) + goto cleanup; + } + + return 0; + +cleanup: + clks_unregister(clks, i); + + for (i -- ; i >= 0; i--) { + clk_unregister(clks[i]); + } + + return ret; +} + +int __must_check clk_alloc_function(const char *parent, struct clk *clk); + +/* + * struct clk_function - helper that allows easy registration of clock "functions" + * @parent: the name of the parent clock + * @clk: the clock that will be set up and installed + * + * Sometimes single clock will have multiple users or several clocks + * will be bound to similar devices. This allows one to register + * simple wrapper clocks that serve only naming purposes. + */ +struct clk_function { + const char *parent; + void *priv; + struct clk clk; +}; + +#define CLK_FUNC(_clock, _function, _ops, _data) \ + { \ + .parent = _clock, \ + .priv = _data, \ + .clk = { \ + .name= _function, \ + .owner = THIS_MODULE, \ + .ops = _ops, \ + }, \ + } +#define FUNC_TO_CLK(func) &(&(struct clk_function) func)->clk + +static void __maybe_unused clk_free_functions( + struct clk_function *funcs, + int num) +{ + int i; + + for (i = num - 1; i >= 0; i--) { + clk_unregister(&funcs[i].clk); + } +} + +static int __must_check __maybe_unused clk_alloc_functions( + struct clk_function *funcs, + int num) +{ + int i; + int rc; + + for (i = 0; i < num; i++) { + rc = clk_alloc_function(funcs[i].parent, &funcs[i].clk); + + if (rc) { + printk(KERN_ERR "Error allocating %s.%s function.\n", + funcs[i].parent, + funcs[i].clk.name); + clk_free_functions(funcs, i); + return rc; + } + } + + return 0; +} + +#endif diff --git a/init/Kconfig b/init/Kconfig index a97924b..1dd9ce2 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -504,6 +504,13 @@ config CC_OPTIMIZE_FOR_SIZE config SYSCTL bool +config HAVE_CLOCK_LIB + bool + help + Platforms select clocklib if they use this infrastructure + for managing their clocks both built into SoC and provided + by external devices. + menuconfig EMBEDDED bool "Configure standard kernel features (for small systems)" help diff --git a/kernel/Makefile b/kernel/Makefile index 6c584c5..afaed51 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -68,6 +68,7 @@ obj-$(CONFIG_TASK_DELAY_ACCT) += delayacct.o obj-$(CONFIG_TASKSTATS) += taskstats.o tsacct.o obj-$(CONFIG_MARKERS) += marker.o obj-$(CONFIG_LATENCYTOP) += latencytop.o +obj-$(CONFIG_HAVE_CLOCK_LIB) += clklib.o ifneq ($(CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER),y) # According to Alan Modra <alan@linuxcare.com.au>, the -fno-omit-frame-pointer is diff --git a/kernel/clklib.c b/kernel/clklib.c new file mode 100644 index 0000000..012f845 --- /dev/null +++ b/kernel/clklib.c @@ -0,0 +1,326 @@ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/clk.h> +#include <linux/clklib.h> +#include <linux/spinlock.h> +#include <linux/err.h> +#include <linux/delay.h> + +static LIST_HEAD(clocks); +static DEFINE_SPINLOCK(clocks_lock); + +static int __clk_register(struct clk *clk) +{ + if (clk->parent && + !try_module_get(clk->parent->owner)) + return -EINVAL; + + list_add_tail(&clk->node, &clocks); + + return 0; +} + +int __must_check clk_register(struct clk *clk) +{ + unsigned long flags; + int rc; + + spin_lock_irqsave(&clocks_lock, flags); + + rc = __clk_register(clk); + + spin_unlock_irqrestore(&clocks_lock, flags); + + return rc; +} +EXPORT_SYMBOL(clk_register); + +void clk_unregister(struct clk *clk) +{ + unsigned long flags; + + spin_lock_irqsave(&clocks_lock, flags); + list_del(&clk->node); + if (clk->parent) + module_put(clk->parent->owner); + spin_unlock_irqrestore(&clocks_lock, flags); +} +EXPORT_SYMBOL(clk_unregister); + +struct clk *clk_get(struct device *dev, const char *id) +{ + struct clk *p, *clk = ERR_PTR(-ENOENT); + unsigned long flags; + + spin_lock_irqsave(&clocks_lock, flags); + + list_for_each_entry(p, &clocks, node) { + if (strcmp(id, p->name) == 0 && + (p->ops && p->ops->can_get && p->ops->can_get(p, dev)) && + try_module_get(p->owner)) { + clk = p; + break; + } + } + + list_for_each_entry(p, &clocks, node) { + if (strcmp(id, p->name) == 0 && + (!p->ops || !p->ops->can_get) && + try_module_get(p->owner)) { + clk = p; + break; + } + } + + spin_unlock_irqrestore(&clocks_lock, flags); + + return clk; +} +EXPORT_SYMBOL(clk_get); + +void clk_put(struct clk *clk) +{ + unsigned long flags; + + if (!clk || IS_ERR(clk)) + return; + + spin_lock_irqsave(&clocks_lock, flags); + + module_put(clk->owner); + + spin_unlock_irqrestore(&clocks_lock, flags); +} +EXPORT_SYMBOL(clk_put); + +int clk_set_parent(struct clk *clk, struct clk *parent) +{ + int rc; + unsigned long flags; + + if (!clk || IS_ERR(clk) || !clk->ops || !clk->ops->set_parent) + return -EINVAL; + + spin_lock_irqsave(&clocks_lock, flags); + + rc = clk->ops->set_parent(clk, parent); + if (!rc) + clk->parent = parent; + + spin_unlock_irqrestore(&clocks_lock, flags); + + return rc; +} +EXPORT_SYMBOL(clk_set_parent); + +struct clk *clk_get_parent(struct clk *clk) +{ + struct clk* parent; + unsigned long flags; + + spin_lock_irqsave(&clocks_lock, flags); + + parent = clk->parent; + + spin_unlock_irqrestore(&clocks_lock, flags); + + return parent; + +} + +static void __clk_disable(struct clk *clk) +{ + if (clk->users <= 0) { + WARN_ON(1); + return; + } + + if (--clk->users == 0) + if (clk->ops && clk->ops->set_mode) + clk->ops->set_mode(clk, false); + + if (clk->parent) + __clk_disable(clk->parent); +} + +void clk_disable(struct clk *clk) +{ + unsigned long flags; + + if (!clk || IS_ERR(clk)) + return; + + spin_lock_irqsave(&clocks_lock, flags); + + __clk_disable(clk); + + spin_unlock_irqrestore(&clocks_lock, flags); +} +EXPORT_SYMBOL(clk_disable); + +static int __clk_enable(struct clk *clk) +{ + int rc = 0; + + if (clk->parent) { + rc = __clk_enable(clk->parent); + + if (rc) + return rc; + } + + if (clk->users++ != 0) { + return 0; + } + + if (clk->ops && clk->ops->set_mode) { + rc = clk->ops->set_mode(clk, true); + if (rc) { + if (clk->parent) + __clk_disable(clk->parent); + + return rc; + } + } + + return rc; +} + +int clk_enable(struct clk *clk) +{ + unsigned long flags; + int rc; + + if (!clk || IS_ERR(clk)) + return -EINVAL; + + spin_lock_irqsave(&clocks_lock, flags); + + rc = __clk_enable(clk); + + spin_unlock_irqrestore(&clocks_lock, flags); + + return rc; +} +EXPORT_SYMBOL(clk_enable); + +static unsigned long __clk_get_rate(struct clk *clk) +{ + unsigned long rate = 0; + + while (clk) { + if (clk->ops && clk->ops->get_rate) { + rate = clk->ops->get_rate(clk); + break; + } else + clk = clk->parent; + } + + return rate; +} + +unsigned long clk_get_rate(struct clk *clk) +{ + unsigned long rate = 0; + unsigned long flags; + + if (!clk || IS_ERR(clk)) + return -EINVAL; + + spin_lock_irqsave(&clocks_lock, flags); + + rate = __clk_get_rate(clk); + + spin_unlock_irqrestore(&clocks_lock, flags); + + return rate; +} +EXPORT_SYMBOL(clk_get_rate); + +long clk_round_rate(struct clk *clk, unsigned long rate) +{ + long res = 0; + unsigned long flags; + struct clk *pclk; + + if (!clk || IS_ERR(clk)) + return -EINVAL; + + spin_lock_irqsave(&clocks_lock, flags); + + for (pclk = clk; !res && pclk ; pclk = pclk->parent) { + if (pclk->ops && pclk->ops->round_rate) + res = pclk->ops->round_rate(clk, rate); + } + + if (!res) + res = __clk_get_rate(clk); + + spin_unlock_irqrestore(&clocks_lock, flags); + + return res; +} +EXPORT_SYMBOL(clk_round_rate); + +int clk_set_rate(struct clk *clk, unsigned long rate) +{ + int rc = -EINVAL; + unsigned long flags; + + spin_lock_irqsave(&clocks_lock, flags); + + while (clk && !IS_ERR(clk)) { + if (clk->ops && clk->ops->set_rate) { + rc = clk->ops->set_rate(clk, rate); + break; + } + + clk = clk->parent; + } + + spin_unlock_irqrestore(&clocks_lock, flags); + + return rc; +} +EXPORT_SYMBOL(clk_set_rate); + +int clk_alloc_function(const char *parent, struct clk *clk) +{ + int rc = 0; + unsigned long flags; + struct clk *pclk; + bool found = false; + + spin_lock_irqsave(&clocks_lock, flags); + + list_for_each_entry(pclk, &clocks, node) { + if (strcmp(parent, pclk->name) == 0 && + try_module_get(pclk->owner)) { + found = true; + break; + } + } + + if (!found) { + rc = -ENODEV; + goto out; + } + + clk->parent = pclk; + + __clk_register(clk); + /* + * We locked parent owner during search + * and also in __clk_register. Free one reference + */ + module_put(pclk->owner); + +out: + if (rc) { + kfree(clk); + } + spin_unlock_irqrestore(&clocks_lock, flags); + + return rc; +} +EXPORT_SYMBOL(clk_alloc_function); -- 1.5.4.4 -- With best wishes Dmitry ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 1/6] Clocklib: add generic framework for managing clocks. 2008-03-31 8:44 ` [PATCH 1/6] Clocklib: add generic framework for managing clocks Dmitry Baryshkov @ 2008-03-31 14:55 ` Pavel Machek 2008-03-31 16:18 ` Dmitry 0 siblings, 1 reply; 12+ messages in thread From: Pavel Machek @ 2008-03-31 14:55 UTC (permalink / raw) To: Dmitry Baryshkov Cc: linux-kernel, akpm, Haavard Skinnemoen, Russell King, Paul Mundt, pHilipp Zabel, tony, paul > +int __must_check clk_register(struct clk *clk); > +void clk_unregister(struct clk *clk); > +static void __maybe_unused clks_unregister(struct clk **clks, size_t num) > +{ > + int i; > + for (i = num - 1; i >= 0; i++) { > + clk_unregister(clks[i]); > + } > +} > + > +static int __must_check __maybe_unused clks_register(struct clk **clks, size_t num) > +{ > + int i; > + int ret; > + for (i = 0; i < num; i++) { > + ret = clk_register(clks[i]); > + if (ret != 0) > + goto cleanup; > + } > + > + return 0; > + > +cleanup: > + clks_unregister(clks, i); > + > + for (i -- ; i >= 0; i--) { "for (i--;" > + clk_unregister(clks[i]); > + } > + > + return ret; Hmm, clk_unregister is _very_ similar to clks_unregister.... Pavel -- (english) http://www.livejournal.com/~pavelmachek (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 1/6] Clocklib: add generic framework for managing clocks. 2008-03-31 14:55 ` Pavel Machek @ 2008-03-31 16:18 ` Dmitry 0 siblings, 0 replies; 12+ messages in thread From: Dmitry @ 2008-03-31 16:18 UTC (permalink / raw) To: Pavel Machek Cc: linux-kernel, akpm, Haavard Skinnemoen, Russell King, Paul Mundt, pHilipp Zabel, tony, paul 2008/3/31, Pavel Machek <pavel@ucw.cz>: > > > +int __must_check clk_register(struct clk *clk); > > +void clk_unregister(struct clk *clk); > > +static void __maybe_unused clks_unregister(struct clk **clks, size_t num) > > +{ > > + int i; > > + for (i = num - 1; i >= 0; i++) { > > + clk_unregister(clks[i]); > > + } > > +} > > + > > +static int __must_check __maybe_unused clks_register(struct clk **clks, size_t num) > > +{ > > + int i; > > + int ret; > > + for (i = 0; i < num; i++) { > > + ret = clk_register(clks[i]); > > + if (ret != 0) > > + goto cleanup; > > + } > > + > > + return 0; > > + > > +cleanup: > > + clks_unregister(clks, i); > > + > > + for (i -- ; i >= 0; i--) { > > > "for (i--;" ack > > > + clk_unregister(clks[i]); > > + } > > + > > + return ret; > > > Hmm, clk_unregister is _very_ similar to clks_unregister.... oops :) -- With best wishes Dmitry ^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH 2/6] Clocklib: debugfs support 2008-03-31 8:39 [PATCH 0/6] Clocklib: generic clocks framework Dmitry Baryshkov 2008-03-31 8:44 ` [PATCH 1/6] Clocklib: add generic framework for managing clocks Dmitry Baryshkov @ 2008-03-31 8:44 ` Dmitry Baryshkov 2008-03-31 8:44 ` [PATCH 3/6] Clocklib: support sa1100 sub-arch Dmitry Baryshkov ` (3 subsequent siblings) 5 siblings, 0 replies; 12+ messages in thread From: Dmitry Baryshkov @ 2008-03-31 8:44 UTC (permalink / raw) To: linux-kernel Cc: akpm, Haavard Skinnemoen, Russell King, Paul Mundt, pHilipp Zabel, Pavel Machek, tony, paul Provide /sys/kernel/debug/clock to ease debugging. Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com> --- include/linux/clklib.h | 2 + kernel/clklib.c | 74 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 0 deletions(-) diff --git a/include/linux/clklib.h b/include/linux/clklib.h index 88ab2c1..9f1c636 100644 --- a/include/linux/clklib.h +++ b/include/linux/clklib.h @@ -19,6 +19,7 @@ struct seq_file; * @get_rate: obtain the current clock rate of a specified clock * @set_rate: set the clock rate for a specified clock * @round_rate: adjust a reate to the exact rate a clock can provide + * @format: output any additional information for a clock * * This structure specifies clock operations that are used to configure * specific clock. @@ -30,6 +31,7 @@ struct clk_ops { unsigned long (*get_rate) (struct clk *clk); int (*set_rate) (struct clk *, unsigned long); long (*round_rate) (struct clk *, unsigned long); + int (*format) (struct clk *, struct seq_file *); }; /** diff --git a/kernel/clklib.c b/kernel/clklib.c index 012f845..8d872e1 100644 --- a/kernel/clklib.c +++ b/kernel/clklib.c @@ -324,3 +324,77 @@ out: return rc; } EXPORT_SYMBOL(clk_alloc_function); + +#ifdef CONFIG_DEBUG_FS + +#include <linux/debugfs.h> +#include <linux/seq_file.h> + +#define NAME_FIELD_LEN 20 + +static void dump_clocks(struct seq_file *s, struct clk *parent, int nest) +{ + struct clk *clk; + int i; + + list_for_each_entry(clk, &clocks, node) { + if (clk->parent == parent) { + for (i = 0; i < nest; i++) { + seq_putc(s, ' '); + seq_putc(s, ' '); + } + seq_puts(s, clk->name); + + i = 2 * nest + strlen(clk->name); + if (i >= NAME_FIELD_LEN) + i = NAME_FIELD_LEN - 1; + for (; i < NAME_FIELD_LEN; i++) { + seq_putc(s, ' '); + } + seq_printf(s, "%c use=%d rate=%10lu Hz", + clk->ops && clk->ops->set_parent ? '*' : ' ', + clk->users, + __clk_get_rate(clk)); + if (clk->ops && clk->ops->format) + clk->ops->format(clk, s); + seq_putc(s, '\n'); + + dump_clocks(s, clk, nest + 1); + } + } +} + +static int clocklib_show(struct seq_file *s, void *unused) +{ + unsigned long flags; + + spin_lock_irqsave(&clocks_lock, flags); + + dump_clocks(s, NULL, 0); + + spin_unlock_irqrestore(&clocks_lock, flags); + + return 0; +} + +static int clocklib_open(struct inode *inode, struct file *file) +{ + return single_open(file, clocklib_show, NULL); +} + +static struct file_operations clocklib_operations = { + .open = clocklib_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int __init clocklib_debugfs_init(void) +{ + debugfs_create_file("clock", S_IFREG | S_IRUGO, + NULL, NULL, &clocklib_operations); + return 0; +} +subsys_initcall(clocklib_debugfs_init); + +#endif /* DEBUG_FS */ -- 1.5.4.4 -- With best wishes Dmitry ^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH 3/6] Clocklib: support sa1100 sub-arch. 2008-03-31 8:39 [PATCH 0/6] Clocklib: generic clocks framework Dmitry Baryshkov 2008-03-31 8:44 ` [PATCH 1/6] Clocklib: add generic framework for managing clocks Dmitry Baryshkov 2008-03-31 8:44 ` [PATCH 2/6] Clocklib: debugfs support Dmitry Baryshkov @ 2008-03-31 8:44 ` Dmitry Baryshkov 2008-03-31 8:44 ` [PATCH 4/6] Clocklib: support ARM pxa sub-arch Dmitry Baryshkov ` (2 subsequent siblings) 5 siblings, 0 replies; 12+ messages in thread From: Dmitry Baryshkov @ 2008-03-31 8:44 UTC (permalink / raw) To: linux-kernel Cc: akpm, Haavard Skinnemoen, Russell King, Paul Mundt, pHilipp Zabel, Pavel Machek, tony, paul Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com> --- arch/arm/Kconfig | 1 + arch/arm/mach-sa1100/clock.c | 131 ++++++++++------------------------------- 2 files changed, 33 insertions(+), 99 deletions(-) diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 4039a13..6d78a27 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -426,6 +426,7 @@ config ARCH_SA1100 select GENERIC_GPIO select GENERIC_TIME select HAVE_IDE + select HAVE_CLOCK_LIB help Support for StrongARM 11x0 based boards. diff --git a/arch/arm/mach-sa1100/clock.c b/arch/arm/mach-sa1100/clock.c index fc97fe5..8f9b777 100644 --- a/arch/arm/mach-sa1100/clock.c +++ b/arch/arm/mach-sa1100/clock.c @@ -8,127 +8,60 @@ #include <linux/err.h> #include <linux/string.h> #include <linux/clk.h> +#include <linux/clklib.h> #include <linux/spinlock.h> #include <linux/mutex.h> #include <asm/hardware.h> -/* - * Very simple clock implementation - we only have one clock to - * deal with at the moment, so we only match using the "name". - */ -struct clk { - struct list_head node; - unsigned long rate; - const char *name; - unsigned int enabled; - void (*enable)(void); - void (*disable)(void); -}; - -static LIST_HEAD(clocks); -static DEFINE_MUTEX(clocks_mutex); -static DEFINE_SPINLOCK(clocks_lock); - -struct clk *clk_get(struct device *dev, const char *id) -{ - struct clk *p, *clk = ERR_PTR(-ENOENT); - - mutex_lock(&clocks_mutex); - list_for_each_entry(p, &clocks, node) { - if (strcmp(id, p->name) == 0) { - clk = p; - break; - } - } - mutex_unlock(&clocks_mutex); - - return clk; -} -EXPORT_SYMBOL(clk_get); - -void clk_put(struct clk *clk) -{ -} -EXPORT_SYMBOL(clk_put); - -int clk_enable(struct clk *clk) -{ - unsigned long flags; - - spin_lock_irqsave(&clocks_lock, flags); - if (clk->enabled++ == 0) - clk->enable(); - spin_unlock_irqrestore(&clocks_lock, flags); - return 0; -} -EXPORT_SYMBOL(clk_enable); - -void clk_disable(struct clk *clk) -{ - unsigned long flags; - - WARN_ON(clk->enabled == 0); - - spin_lock_irqsave(&clocks_lock, flags); - if (--clk->enabled == 0) - clk->disable(); - spin_unlock_irqrestore(&clocks_lock, flags); -} -EXPORT_SYMBOL(clk_disable); - -unsigned long clk_get_rate(struct clk *clk) -{ - return clk->rate; -} -EXPORT_SYMBOL(clk_get_rate); - - -static void clk_gpio27_enable(void) +static int clk_gpio27_set_enable(struct clk *clk, bool enable) { /* * First, set up the 3.6864MHz clock on GPIO 27 for the SA-1111: * (SA-1110 Developer's Manual, section 9.1.2.1) */ - GAFR |= GPIO_32_768kHz; - GPDR |= GPIO_32_768kHz; - TUCR = TUCR_3_6864MHz; + if (enable) { + GAFR |= GPIO_32_768kHz; + GPDR |= GPIO_32_768kHz; + TUCR = TUCR_3_6864MHz; + } else { + TUCR = 0; + GPDR &= ~GPIO_32_768kHz; + GAFR &= ~GPIO_32_768kHz; + } + + return 0; } -static void clk_gpio27_disable(void) +static unsigned long clk_gpio27_get_rate(struct clk *clk) { - TUCR = 0; - GPDR &= ~GPIO_32_768kHz; - GAFR &= ~GPIO_32_768kHz; + return 3686400; } +static struct clk_ops clk_gpio27_ops = { + .get_rate = clk_gpio27_get_rate, + .set_mode = clk_gpio27_set_enable, +}; + static struct clk clk_gpio27 = { .name = "GPIO27_CLK", - .rate = 3686400, - .enable = clk_gpio27_enable, - .disable = clk_gpio27_disable, + .ops = &clk_gpio27_ops, + .owner = THIS_MODULE, }; -int clk_register(struct clk *clk) -{ - mutex_lock(&clocks_mutex); - list_add(&clk->node, &clocks); - mutex_unlock(&clocks_mutex); - return 0; -} -EXPORT_SYMBOL(clk_register); +static struct clk clk_3_6MHz= { + .name = "3_6MHz_CLK", + .parent = &clk_gpio27, + .owner = THIS_MODULE, +}; -void clk_unregister(struct clk *clk) -{ - mutex_lock(&clocks_mutex); - list_del(&clk->node); - mutex_unlock(&clocks_mutex); -} -EXPORT_SYMBOL(clk_unregister); +static struct clk* clks[] = { + &clk_gpio27, + &clk_3_6MHz, +}; static int __init clk_init(void) { - clk_register(&clk_gpio27); - return 0; + return clks_register(clks, ARRAY_SIZE(clks)); } arch_initcall(clk_init); -- 1.5.4.4 -- With best wishes Dmitry ^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH 4/6] Clocklib: support ARM pxa sub-arch. 2008-03-31 8:39 [PATCH 0/6] Clocklib: generic clocks framework Dmitry Baryshkov ` (2 preceding siblings ...) 2008-03-31 8:44 ` [PATCH 3/6] Clocklib: support sa1100 sub-arch Dmitry Baryshkov @ 2008-03-31 8:44 ` Dmitry Baryshkov 2008-03-31 8:44 ` [PATCH 5/6] Clocklib: Use correct clock for IrDA on pxa Dmitry Baryshkov 2008-03-31 8:45 ` [PATCH 6/6] Clocklib: use correct name for 3,6MHz clock Dmitry Baryshkov 5 siblings, 0 replies; 12+ messages in thread From: Dmitry Baryshkov @ 2008-03-31 8:44 UTC (permalink / raw) To: linux-kernel Cc: akpm, Haavard Skinnemoen, Russell King, Paul Mundt, pHilipp Zabel, Pavel Machek, tony, paul Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com> --- arch/arm/Kconfig | 1 + arch/arm/mach-pxa/clock.c | 152 ++++++++++++++++---------------------------- arch/arm/mach-pxa/clock.h | 64 ++++++++---------- arch/arm/mach-pxa/pxa25x.c | 74 +++++++++++++-------- arch/arm/mach-pxa/pxa27x.c | 67 +++++++++++-------- arch/arm/mach-pxa/pxa3xx.c | 132 +++++++++++++++++++++----------------- 6 files changed, 244 insertions(+), 246 deletions(-) diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 6d78a27..ce2ffe0 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -402,6 +402,7 @@ config ARCH_PXA select GENERIC_TIME select GENERIC_CLOCKEVENTS select TICK_ONESHOT + select HAVE_CLOCK_LIB help Support for Intel/Marvell's PXA2xx/PXA3xx processor line. diff --git a/arch/arm/mach-pxa/clock.c b/arch/arm/mach-pxa/clock.c index df5ae27..ba45ec0 100644 --- a/arch/arm/mach-pxa/clock.c +++ b/arch/arm/mach-pxa/clock.c @@ -8,6 +8,7 @@ #include <linux/err.h> #include <linux/string.h> #include <linux/clk.h> +#include <linux/clklib.h> #include <linux/spinlock.h> #include <linux/platform_device.h> #include <linux/delay.h> @@ -19,135 +20,92 @@ #include "generic.h" #include "clock.h" -static LIST_HEAD(clocks); -static DEFINE_MUTEX(clocks_mutex); -static DEFINE_SPINLOCK(clocks_lock); - -static struct clk *clk_lookup(struct device *dev, const char *id) +static int clk_gpio11_set_mode(struct clk *clk, bool enable) { - struct clk *p; - - list_for_each_entry(p, &clocks, node) - if (strcmp(id, p->name) == 0 && p->dev == dev) - return p; + if (enable) + pxa_gpio_mode(GPIO11_3_6MHz_MD); - return NULL; + return 0; } -struct clk *clk_get(struct device *dev, const char *id) +static unsigned long clk_gpio11_get_rate(struct clk *clk) { - struct clk *p, *clk = ERR_PTR(-ENOENT); + return 3686400; +} - mutex_lock(&clocks_mutex); - p = clk_lookup(dev, id); - if (!p) - p = clk_lookup(NULL, id); - if (p) - clk = p; - mutex_unlock(&clocks_mutex); +static const struct clk_ops clk_gpio11_ops = { + .get_rate = clk_gpio11_get_rate, + .set_mode = clk_gpio11_set_mode, +}; - return clk; -} -EXPORT_SYMBOL(clk_get); +static struct clk clk_gpio11 = { + .name = "GPIO11_CLK", + .ops = &clk_gpio11_ops, + .owner = THIS_MODULE, +}; -void clk_put(struct clk *clk) -{ -} -EXPORT_SYMBOL(clk_put); +static struct clk clk_3_6MHz= { + .name = "3_6MHz_CLK", + .parent = &clk_gpio11, + .owner = THIS_MODULE, +}; -int clk_enable(struct clk *clk) +int clk_cken_set_mode(struct clk *clk, bool enable) { - unsigned long flags; - - spin_lock_irqsave(&clocks_lock, flags); - if (clk->enabled++ == 0) - clk->ops->enable(clk); - spin_unlock_irqrestore(&clocks_lock, flags); + struct clk_cken *cclk = container_of(clk, struct clk_cken, clk); + int cken = cclk->cken; - if (clk->delay) - udelay(clk->delay); + if (enable) { + CKEN |= 1 << cken; + if (cclk->delay) + udelay(cclk->delay); + } else + CKEN &= ~(1 << cken); return 0; } -EXPORT_SYMBOL(clk_enable); - -void clk_disable(struct clk *clk) -{ - unsigned long flags; - WARN_ON(clk->enabled == 0); +unsigned long clk_cken_get_rate(struct clk *clk) { + struct clk_cken *cclk = container_of(clk, struct clk_cken, clk); - spin_lock_irqsave(&clocks_lock, flags); - if (--clk->enabled == 0) - clk->ops->disable(clk); - spin_unlock_irqrestore(&clocks_lock, flags); + return cclk->rate; } -EXPORT_SYMBOL(clk_disable); - -unsigned long clk_get_rate(struct clk *clk) -{ - unsigned long rate; - - rate = clk->rate; - if (clk->ops->getrate) - rate = clk->ops->getrate(clk); - - return rate; -} -EXPORT_SYMBOL(clk_get_rate); +const struct clk_ops clk_cken_ops = { + .set_mode = clk_cken_set_mode, + .get_rate = clk_cken_get_rate, +}; -static void clk_gpio27_enable(struct clk *clk) +static int clk_dev_can_get(struct clk *clk, struct device *dev) { - pxa_gpio_mode(GPIO11_3_6MHz_MD); -} + struct clk_function *cfunc = container_of(clk, struct clk_function, clk); -static void clk_gpio27_disable(struct clk *clk) -{ + return (dev == cfunc->priv); } -static const struct clkops clk_gpio27_ops = { - .enable = clk_gpio27_enable, - .disable = clk_gpio27_disable, -}; - - -void clk_cken_enable(struct clk *clk) +static int clk_dev_format(struct clk *clk, struct seq_file *s) { - CKEN |= 1 << clk->cken; -} + struct clk_function *cfunc = container_of(clk, struct clk_function, clk); -void clk_cken_disable(struct clk *clk) -{ - CKEN &= ~(1 << clk->cken); + BUG_ON(!cfunc->priv); + + seq_puts(s, " for device "); + seq_puts(s, ((struct device *)cfunc->priv)->bus_id); + return 0; } -const struct clkops clk_cken_ops = { - .enable = clk_cken_enable, - .disable = clk_cken_disable, +const struct clk_ops clk_dev_ops = { + .can_get = clk_dev_can_get, + .format = clk_dev_format, }; -static struct clk common_clks[] = { - { - .name = "GPIO27_CLK", - .ops = &clk_gpio27_ops, - .rate = 3686400, - }, +static struct clk * common_clks[] = { + &clk_gpio11, + &clk_3_6MHz, }; -void clks_register(struct clk *clks, size_t num) -{ - int i; - - mutex_lock(&clocks_mutex); - for (i = 0; i < num; i++) - list_add(&clks[i].node, &clocks); - mutex_unlock(&clocks_mutex); -} - static int __init clk_init(void) { - clks_register(common_clks, ARRAY_SIZE(common_clks)); - return 0; + return clks_register(common_clks, ARRAY_SIZE(common_clks)); } arch_initcall(clk_init); diff --git a/arch/arm/mach-pxa/clock.h b/arch/arm/mach-pxa/clock.h index bc6b77e..0b11d13 100644 --- a/arch/arm/mach-pxa/clock.h +++ b/arch/arm/mach-pxa/clock.h @@ -1,43 +1,37 @@ -struct clk; +#include <linux/clk.h> +#include <linux/clklib.h> +#include <linux/seq_file.h> -struct clkops { - void (*enable)(struct clk *); - void (*disable)(struct clk *); - unsigned long (*getrate)(struct clk *); -}; +extern int clk_cken_set_mode(struct clk *clk, bool enable); +extern unsigned long clk_cken_get_rate(struct clk *clk); +extern const struct clk_ops clk_cken_ops; -struct clk { - struct list_head node; - const char *name; - struct device *dev; - const struct clkops *ops; - unsigned long rate; - unsigned int cken; - unsigned int delay; - unsigned int enabled; +struct clk_cken { + unsigned int cken; + unsigned long rate; + int delay; + struct clk clk; }; -#define INIT_CKEN(_name, _cken, _rate, _delay, _dev) \ - { \ - .name = _name, \ - .dev = _dev, \ - .ops = &clk_cken_ops, \ +#define INIT_CKEN(_name, _cken, _rate, _delay) \ + &(&(struct clk_cken) { \ + .cken = CKEN_##_cken, \ .rate = _rate, \ - .cken = CKEN_##_cken, \ .delay = _delay, \ - } - -#define INIT_CK(_name, _cken, _ops, _dev) \ - { \ - .name = _name, \ - .dev = _dev, \ - .ops = _ops, \ - .cken = CKEN_##_cken, \ - } - -extern const struct clkops clk_cken_ops; + .clk = { \ + .name = _name, \ + .ops = &clk_cken_ops, \ + }, \ + })->clk -void clk_cken_enable(struct clk *clk); -void clk_cken_disable(struct clk *clk); +#define INIT_CK(_name, _cken, _ops) \ + &(&(struct clk_cken) { \ + .cken = CKEN_##_cken, \ + .clk = { \ + .name = _name, \ + .ops = _ops, \ + }, \ + })->clk -void clks_register(struct clk *clks, size_t num); +extern const struct clk_ops clk_dev_ops; +extern const struct clk_ops clk_pxa3xx_cken_ops; diff --git a/arch/arm/mach-pxa/pxa25x.c b/arch/arm/mach-pxa/pxa25x.c index 599e53f..5924ef1 100644 --- a/arch/arm/mach-pxa/pxa25x.c +++ b/arch/arm/mach-pxa/pxa25x.c @@ -101,10 +101,9 @@ static unsigned long clk_pxa25x_lcd_getrate(struct clk *clk) return pxa25x_get_memclk_frequency_10khz() * 10000; } -static const struct clkops clk_pxa25x_lcd_ops = { - .enable = clk_cken_enable, - .disable = clk_cken_disable, - .getrate = clk_pxa25x_lcd_getrate, +static const struct clk_ops clk_pxa25x_lcd_ops = { + .set_mode = clk_cken_set_mode, + .get_rate = clk_pxa25x_lcd_getrate, }; /* @@ -112,29 +111,43 @@ static const struct clkops clk_pxa25x_lcd_ops = { * 95.842MHz -> MMC 19.169MHz, I2C 31.949MHz, FICP 47.923MHz, USB 47.923MHz * 147.456MHz -> UART 14.7456MHz, AC97 12.288MHz, I2S 5.672MHz (allegedly) */ -static struct clk pxa25x_hwuart_clk = - INIT_CKEN("UARTCLK", HWUART, 14745600, 1, &pxa_device_hwuart.dev) -; - -static struct clk pxa25x_clks[] = { - INIT_CK("LCDCLK", LCD, &clk_pxa25x_lcd_ops, &pxa_device_fb.dev), - INIT_CKEN("UARTCLK", FFUART, 14745600, 1, &pxa_device_ffuart.dev), - INIT_CKEN("UARTCLK", BTUART, 14745600, 1, &pxa_device_btuart.dev), - INIT_CKEN("UARTCLK", STUART, 14745600, 1, NULL), - INIT_CKEN("UDCCLK", USB, 47923000, 5, &pxa_device_udc.dev), - INIT_CKEN("MMCCLK", MMC, 19169000, 0, &pxa_device_mci.dev), - INIT_CKEN("I2CCLK", I2C, 31949000, 0, &pxa_device_i2c.dev), - - INIT_CKEN("SSPCLK", SSP, 3686400, 0, &pxa25x_device_ssp.dev), - INIT_CKEN("SSPCLK", NSSP, 3686400, 0, &pxa25x_device_nssp.dev), - INIT_CKEN("SSPCLK", ASSP, 3686400, 0, &pxa25x_device_assp.dev), +static struct clk *pxa25x_hwuart_cken_clk = + INIT_CKEN("HWUARTCLK", HWUART, 14745600, 1); + +static struct clk_function pxa25x_hwuart_func = + CLK_FUNC("HWUARTCLK", "UARTCLK", &clk_dev_ops, &pxa_device_hwuart.dev); + + +static struct clk *pxa25x_clks[] = { + INIT_CK("LCDCLK", LCD, &clk_pxa25x_lcd_ops), + INIT_CKEN("FFUARTCLK", FFUART, 14745600, 1), + INIT_CKEN("BTUARTCLK", BTUART, 14745600, 1), + INIT_CKEN("STUARTCLK", STUART, 14745600, 1), + INIT_CKEN("UDCCLK", USB, 47923000, 5), + INIT_CKEN("PXAMMCCLK", MMC, 19169000, 0), + INIT_CKEN("I2CCLK", I2C, 31949000, 0), + + INIT_CKEN("SSP_CLK", SSP, 3686400, 0), + INIT_CKEN("NSSPCLK", NSSP, 3686400, 0), + INIT_CKEN("ASSPCLK", ASSP, 3686400, 0), /* - INIT_CKEN("PWMCLK", PWM0, 3686400, 0, NULL), - INIT_CKEN("PWMCLK", PWM0, 3686400, 0, NULL), - INIT_CKEN("I2SCLK", I2S, 14745600, 0, NULL), + INIT_CKEN("PWMCLK", PWM0, 3686400, 0), + INIT_CKEN("PWMCLK", PWM0, 3686400, 0), + INIT_CKEN("I2SCLK", I2S, 14745600, 0), */ - INIT_CKEN("FICPCLK", FICP, 47923000, 0, NULL), + INIT_CKEN("FICPCLK", FICP, 47923000, 0), +}; + +static struct clk_function pxa25x_clk_funcs[] = { + CLK_FUNC("FFUARTCLK", "UARTCLK", &clk_dev_ops, &pxa_device_ffuart.dev), + CLK_FUNC("BTUARTCLK", "UARTCLK", &clk_dev_ops, &pxa_device_btuart.dev), + CLK_FUNC("STUARTCLK", "UARTCLK", &clk_dev_ops, &pxa_device_stuart.dev), + CLK_FUNC("STUARTCLK", "SIRCLK", NULL, NULL), + CLK_FUNC("PXAMMCCLK", "MMCCLK", &clk_dev_ops, &pxa_device_mci.dev), + CLK_FUNC("SSP_CLK", "SSPCLK", &clk_dev_ops, &pxa25x_device_ssp.dev), + CLK_FUNC("NSSPCLK", "SSPCLK", &clk_dev_ops, &pxa25x_device_nssp.dev), + CLK_FUNC("ASSPCLK", "SSPCLK", &clk_dev_ops, &pxa25x_device_assp.dev), }; #ifdef CONFIG_PM @@ -289,17 +302,22 @@ static struct sys_device pxa25x_sysdev[] = { .cls = &pxa_gpio_sysclass, }, }; - static int __init pxa25x_init(void) { int i, ret = 0; /* Only add HWUART for PXA255/26x; PXA210/250/27x do not have it. */ - if (cpu_is_pxa25x()) - clks_register(&pxa25x_hwuart_clk, 1); + if (cpu_is_pxa25x()) { + ret = clk_register(pxa25x_hwuart_cken_clk); + if (!ret) + ret = clk_alloc_function( + pxa25x_hwuart_func.parent, + &pxa25x_hwuart_func.clk); + } if (cpu_is_pxa21x() || cpu_is_pxa25x()) { - clks_register(pxa25x_clks, ARRAY_SIZE(pxa25x_clks)); + ret = clks_register(pxa25x_clks, ARRAY_SIZE(pxa25x_clks)); + ret = clk_alloc_functions(pxa25x_clk_funcs, ARRAY_SIZE(pxa25x_clk_funcs)); if ((ret = pxa_init_dma(16))) return ret; diff --git a/arch/arm/mach-pxa/pxa27x.c b/arch/arm/mach-pxa/pxa27x.c index 46a951c..11a15a6 100644 --- a/arch/arm/mach-pxa/pxa27x.c +++ b/arch/arm/mach-pxa/pxa27x.c @@ -129,44 +129,54 @@ static unsigned long clk_pxa27x_lcd_getrate(struct clk *clk) return pxa27x_get_lcdclk_frequency_10khz() * 10000; } -static const struct clkops clk_pxa27x_lcd_ops = { - .enable = clk_cken_enable, - .disable = clk_cken_disable, - .getrate = clk_pxa27x_lcd_getrate, +static const struct clk_ops clk_pxa27x_lcd_ops = { + .set_mode = clk_cken_set_mode, + .get_rate = clk_pxa27x_lcd_getrate, }; -static struct clk pxa27x_clks[] = { - INIT_CK("LCDCLK", LCD, &clk_pxa27x_lcd_ops, &pxa_device_fb.dev), - INIT_CK("CAMCLK", CAMERA, &clk_pxa27x_lcd_ops, NULL), +static struct clk *pxa27x_clks[] = { + INIT_CK("LCDCLK", LCD, &clk_pxa27x_lcd_ops), + INIT_CK("CAMCLK", CAMERA, &clk_pxa27x_lcd_ops), - INIT_CKEN("UARTCLK", FFUART, 14857000, 1, &pxa_device_ffuart.dev), - INIT_CKEN("UARTCLK", BTUART, 14857000, 1, &pxa_device_btuart.dev), - INIT_CKEN("UARTCLK", STUART, 14857000, 1, NULL), + INIT_CKEN("FFUARTCLK", FFUART, 14857000, 1), + INIT_CKEN("BTUARTCLK", BTUART, 14857000, 1), + INIT_CKEN("STUARTCLK", STUART, 14857000, 1), - INIT_CKEN("I2SCLK", I2S, 14682000, 0, &pxa_device_i2s.dev), - INIT_CKEN("I2CCLK", I2C, 32842000, 0, &pxa_device_i2c.dev), - INIT_CKEN("UDCCLK", USB, 48000000, 5, &pxa_device_udc.dev), - INIT_CKEN("MMCCLK", MMC, 19500000, 0, &pxa_device_mci.dev), - INIT_CKEN("FICPCLK", FICP, 48000000, 0, &pxa_device_ficp.dev), + INIT_CKEN("I2SCLK", I2S, 14682000, 0), + INIT_CKEN("I2CCLK", I2C, 32842000, 0), + INIT_CKEN("UDCCLK", USB, 48000000, 5), + INIT_CKEN("PXAMMCCLK", MMC, 19500000, 0), + INIT_CKEN("FICPCLK", FICP, 48000000, 0), - INIT_CKEN("USBCLK", USBHOST, 48000000, 0, &pxa27x_device_ohci.dev), - INIT_CKEN("I2CCLK", PWRI2C, 13000000, 0, &pxa27x_device_i2c_power.dev), - INIT_CKEN("KBDCLK", KEYPAD, 32768, 0, NULL), + INIT_CKEN("USBCLK", USBHOST, 48000000, 0), + INIT_CKEN("I2CCLK", PWRI2C, 13000000, 0), + INIT_CKEN("KBDCLK", KEYPAD, 32768, 0), - INIT_CKEN("SSPCLK", SSP1, 13000000, 0, &pxa27x_device_ssp1.dev), - INIT_CKEN("SSPCLK", SSP2, 13000000, 0, &pxa27x_device_ssp2.dev), - INIT_CKEN("SSPCLK", SSP3, 13000000, 0, &pxa27x_device_ssp3.dev), + INIT_CKEN("SSP1CLK", SSP1, 13000000, 0), + INIT_CKEN("SSP2CLK", SSP2, 13000000, 0), + INIT_CKEN("SSP3CLK", SSP3, 13000000, 0), /* - INIT_CKEN("PWMCLK", PWM0, 13000000, 0, NULL), - INIT_CKEN("MSLCLK", MSL, 48000000, 0, NULL), - INIT_CKEN("USIMCLK", USIM, 48000000, 0, NULL), - INIT_CKEN("MSTKCLK", MEMSTK, 19500000, 0, NULL), - INIT_CKEN("IMCLK", IM, 0, 0, NULL), - INIT_CKEN("MEMCLK", MEMC, 0, 0, NULL), + INIT_CKEN("PWMCLK", PWM0, 13000000, 0), + INIT_CKEN("MSLCLK", MSL, 48000000, 0), + INIT_CKEN("USIMCLK", USIM, 48000000, 0), + INIT_CKEN("MSTKCLK", MEMSTK, 19500000, 0), + INIT_CKEN("IMCLK", IM, 0, 0), + INIT_CKEN("MEMCLK", MEMC, 0, 0), */ }; +static struct clk_function pxa27x_clk_funcs[] = { + CLK_FUNC("FFUARTCLK", "UARTCLK", &clk_dev_ops, &pxa_device_ffuart.dev), + CLK_FUNC("BTUARTCLK", "UARTCLK", &clk_dev_ops, &pxa_device_btuart.dev), + CLK_FUNC("STUARTCLK", "UARTCLK", &clk_dev_ops, &pxa_device_stuart.dev), + CLK_FUNC("STUARTCLK", "SIRCLK", NULL, NULL), + CLK_FUNC("PXAMMCCLK", "MMCCLK", &clk_dev_ops, &pxa_device_mci.dev), + CLK_FUNC("SSP1CLK", "SSPCLK", &clk_dev_ops, &pxa27x_device_ssp1.dev), + CLK_FUNC("SSP2CLK", "SSPCLK", &clk_dev_ops, &pxa27x_device_ssp2.dev), + CLK_FUNC("SSP3CLK", "SSPCLK", &clk_dev_ops, &pxa27x_device_ssp3.dev), +}; + #ifdef CONFIG_PM #define SAVE(x) sleep_save[SLEEP_SAVE_##x] = x @@ -401,7 +411,8 @@ static int __init pxa27x_init(void) int i, ret = 0; if (cpu_is_pxa27x()) { - clks_register(pxa27x_clks, ARRAY_SIZE(pxa27x_clks)); + ret = clks_register(pxa27x_clks, ARRAY_SIZE(pxa27x_clks)); + ret = clk_alloc_functions(pxa27x_clk_funcs, ARRAY_SIZE(pxa27x_clk_funcs)); if ((ret = pxa_init_dma(32))) return ret; diff --git a/arch/arm/mach-pxa/pxa3xx.c b/arch/arm/mach-pxa/pxa3xx.c index 35f25fd..7a7cd3c 100644 --- a/arch/arm/mach-pxa/pxa3xx.c +++ b/arch/arm/mach-pxa/pxa3xx.c @@ -125,75 +125,90 @@ static unsigned long clk_pxa3xx_hsio_getrate(struct clk *clk) return hsio_clk; } -static void clk_pxa3xx_cken_enable(struct clk *clk) +static int clk_pxa3xx_cken_set_mode(struct clk *clk, bool enable) { - unsigned long mask = 1ul << (clk->cken & 0x1f); - - if (clk->cken < 32) - CKENA |= mask; + struct clk_cken *cclk = container_of(clk, struct clk_cken, clk); + int cken = cclk->cken; + unsigned long mask = 1ul << (cken & 0x1f); + + if (cken < 32) + if (enable) + CKENA |= mask; + else + CKENA &= ~mask; else - CKENB |= mask; -} - -static void clk_pxa3xx_cken_disable(struct clk *clk) -{ - unsigned long mask = 1ul << (clk->cken & 0x1f); + if (enable) + CKENB |= mask; + else + CKENB &= ~mask; - if (clk->cken < 32) - CKENA &= ~mask; - else - CKENB &= ~mask; + return 0; } -static const struct clkops clk_pxa3xx_cken_ops = { - .enable = clk_pxa3xx_cken_enable, - .disable = clk_pxa3xx_cken_disable, +const struct clk_ops clk_pxa3xx_cken_ops = { + .set_mode = clk_pxa3xx_cken_set_mode, + .get_rate = clk_cken_get_rate, }; -static const struct clkops clk_pxa3xx_hsio_ops = { - .enable = clk_pxa3xx_cken_enable, - .disable = clk_pxa3xx_cken_disable, - .getrate = clk_pxa3xx_hsio_getrate, +static const struct clk_ops clk_pxa3xx_hsio_ops = { + .set_mode = clk_pxa3xx_cken_set_mode, + .get_rate = clk_pxa3xx_hsio_getrate, }; -#define PXA3xx_CKEN(_name, _cken, _rate, _delay, _dev) \ - { \ - .name = _name, \ - .dev = _dev, \ - .ops = &clk_pxa3xx_cken_ops, \ +#define PXA3xx_CKEN(_name, _cken, _rate, _delay) \ + &(&(struct clk_cken) { \ + .cken = CKEN_##_cken, \ .rate = _rate, \ - .cken = CKEN_##_cken, \ .delay = _delay, \ - } - -#define PXA3xx_CK(_name, _cken, _ops, _dev) \ - { \ - .name = _name, \ - .dev = _dev, \ - .ops = _ops, \ - .cken = CKEN_##_cken, \ - } - -static struct clk pxa3xx_clks[] = { - PXA3xx_CK("LCDCLK", LCD, &clk_pxa3xx_hsio_ops, &pxa_device_fb.dev), - PXA3xx_CK("CAMCLK", CAMERA, &clk_pxa3xx_hsio_ops, NULL), - - PXA3xx_CKEN("UARTCLK", FFUART, 14857000, 1, &pxa_device_ffuart.dev), - PXA3xx_CKEN("UARTCLK", BTUART, 14857000, 1, &pxa_device_btuart.dev), - PXA3xx_CKEN("UARTCLK", STUART, 14857000, 1, NULL), - - PXA3xx_CKEN("I2CCLK", I2C, 32842000, 0, &pxa_device_i2c.dev), - PXA3xx_CKEN("UDCCLK", UDC, 48000000, 5, &pxa_device_udc.dev), - PXA3xx_CKEN("USBCLK", USBH, 48000000, 0, &pxa27x_device_ohci.dev), - - PXA3xx_CKEN("SSPCLK", SSP1, 13000000, 0, &pxa27x_device_ssp1.dev), - PXA3xx_CKEN("SSPCLK", SSP2, 13000000, 0, &pxa27x_device_ssp2.dev), - PXA3xx_CKEN("SSPCLK", SSP3, 13000000, 0, &pxa27x_device_ssp3.dev), - PXA3xx_CKEN("SSPCLK", SSP4, 13000000, 0, &pxa3xx_device_ssp4.dev), + .clk = { \ + .name = _name, \ + .ops = &clk_pxa3xx_cken_ops, \ + }, \ + })->clk + +#define PXA3xx_CK(_name, _cken, _ops) \ + &(&(struct clk_cken) { \ + .cken = CKEN_##_cken, \ + .clk = { \ + .name = _name, \ + .ops = _ops, \ + }, \ + })->clk + +static struct clk *pxa3xx_clks[] = { + PXA3xx_CK("LCDCLK", LCD, &clk_pxa3xx_hsio_ops), + PXA3xx_CK("CAMCLK", CAMERA, &clk_pxa3xx_hsio_ops), + + PXA3xx_CKEN("FFUARTCLK", FFUART, 14857000, 1), + PXA3xx_CKEN("BTUARTCLK", BTUART, 14857000, 1), + PXA3xx_CKEN("STUARTCLK", STUART, 14857000, 1), + + PXA3xx_CKEN("I2CCLK", I2C, 32842000, 0), + PXA3xx_CKEN("UDCCLK", UDC, 48000000, 5), + PXA3xx_CKEN("USBCLK", USBH, 48000000, 0), + + PXA3xx_CKEN("SSP1CLK", SSP1, 13000000, 0), + PXA3xx_CKEN("SSP2CLK", SSP2, 13000000, 0), + PXA3xx_CKEN("SSP3CLK", SSP3, 13000000, 0), + PXA3xx_CKEN("SSP4CLK", SSP4, 13000000, 0), + + PXA3xx_CKEN("MMC1CLK", MMC1, 19500000, 0), + PXA3xx_CKEN("MMC2CLK", MMC2, 19500000, 0), + PXA3xx_CKEN("MMC3CLK", MMC3, 19500000, 0), +}; - PXA3xx_CKEN("MMCCLK", MMC1, 19500000, 0, &pxa_device_mci.dev), - PXA3xx_CKEN("MMCCLK", MMC2, 19500000, 0, &pxa3xx_device_mci2.dev), - PXA3xx_CKEN("MMCCLK", MMC3, 19500000, 0, &pxa3xx_device_mci3.dev), +static struct clk_function pxa3xx_clk_funcs[] = { + CLK_FUNC("FFUARTCLK", "UARTCLK", &clk_dev_ops, &pxa_device_ffuart.dev), + CLK_FUNC("BTUARTCLK", "UARTCLK", &clk_dev_ops, &pxa_device_btuart.dev), + CLK_FUNC("STUARTCLK", "UARTCLK", &clk_dev_ops, &pxa_device_stuart.dev), + CLK_FUNC("STUARTCLK", "SIRCLK", NULL, NULL), + CLK_FUNC("SSP1CLK", "SSPCLK", &clk_dev_ops, &pxa27x_device_ssp1.dev), + CLK_FUNC("SSP2CLK", "SSPCLK", &clk_dev_ops, &pxa27x_device_ssp2.dev), + CLK_FUNC("SSP3CLK", "SSPCLK", &clk_dev_ops, &pxa27x_device_ssp3.dev), + CLK_FUNC("SSP4CLK", "SSPCLK", &clk_dev_ops, &pxa3xx_device_ssp4.dev), + CLK_FUNC("MMC1CLK", "MMCCLK", &clk_dev_ops, &pxa_device_mci.dev), + CLK_FUNC("MMC2CLK", "MMCCLK", &clk_dev_ops, &pxa3xx_device_mci2.dev), + CLK_FUNC("MMC3CLK", "MMCCLK", &clk_dev_ops, &pxa3xx_device_mci3.dev), }; #ifdef CONFIG_PM @@ -513,7 +528,8 @@ static int __init pxa3xx_init(void) */ ASCR &= ~(ASCR_RDH | ASCR_D1S | ASCR_D2S | ASCR_D3S); - clks_register(pxa3xx_clks, ARRAY_SIZE(pxa3xx_clks)); + ret = clks_register(pxa3xx_clks, ARRAY_SIZE(pxa3xx_clks)); + ret = clk_alloc_functions(pxa3xx_clk_funcs, ARRAY_SIZE(pxa3xx_clk_funcs)); if ((ret = pxa_init_dma(32))) return ret; -- 1.5.4.4 -- With best wishes Dmitry ^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH 5/6] Clocklib: Use correct clock for IrDA on pxa 2008-03-31 8:39 [PATCH 0/6] Clocklib: generic clocks framework Dmitry Baryshkov ` (3 preceding siblings ...) 2008-03-31 8:44 ` [PATCH 4/6] Clocklib: support ARM pxa sub-arch Dmitry Baryshkov @ 2008-03-31 8:44 ` Dmitry Baryshkov 2008-03-31 8:45 ` [PATCH 6/6] Clocklib: use correct name for 3,6MHz clock Dmitry Baryshkov 5 siblings, 0 replies; 12+ messages in thread From: Dmitry Baryshkov @ 2008-03-31 8:44 UTC (permalink / raw) To: linux-kernel Cc: akpm, Haavard Skinnemoen, Russell King, Paul Mundt, pHilipp Zabel, Pavel Machek, tony, paul Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com> --- drivers/net/irda/pxaficp_ir.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/drivers/net/irda/pxaficp_ir.c b/drivers/net/irda/pxaficp_ir.c index 8c09344..36d2ec0 100644 --- a/drivers/net/irda/pxaficp_ir.c +++ b/drivers/net/irda/pxaficp_ir.c @@ -814,7 +814,7 @@ static int pxa_irda_probe(struct platform_device *pdev) si->dev = &pdev->dev; si->pdata = pdev->dev.platform_data; - si->sir_clk = clk_get(&pdev->dev, "UARTCLK"); + si->sir_clk = clk_get(&pdev->dev, "SIRCLK"); si->fir_clk = clk_get(&pdev->dev, "FICPCLK"); if (IS_ERR(si->sir_clk) || IS_ERR(si->fir_clk)) { err = PTR_ERR(IS_ERR(si->sir_clk) ? si->sir_clk : si->fir_clk); -- 1.5.4.4 -- With best wishes Dmitry ^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH 6/6] Clocklib: use correct name for 3,6MHz clock 2008-03-31 8:39 [PATCH 0/6] Clocklib: generic clocks framework Dmitry Baryshkov ` (4 preceding siblings ...) 2008-03-31 8:44 ` [PATCH 5/6] Clocklib: Use correct clock for IrDA on pxa Dmitry Baryshkov @ 2008-03-31 8:45 ` Dmitry Baryshkov 5 siblings, 0 replies; 12+ messages in thread From: Dmitry Baryshkov @ 2008-03-31 8:45 UTC (permalink / raw) To: linux-kernel Cc: akpm, Haavard Skinnemoen, Russell King, Paul Mundt, pHilipp Zabel, Pavel Machek, tony, paul Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com> --- arch/arm/common/sa1111.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/arch/arm/common/sa1111.c b/arch/arm/common/sa1111.c index eb06d0b..282a4d9 100644 --- a/arch/arm/common/sa1111.c +++ b/arch/arm/common/sa1111.c @@ -627,7 +627,7 @@ __sa1111_probe(struct device *me, struct resource *mem, int irq) if (!sachip) return -ENOMEM; - sachip->clk = clk_get(me, "GPIO27_CLK"); + sachip->clk = clk_get(me, "3_6MHz_CLK"); if (!sachip->clk) { ret = PTR_ERR(sachip->clk); goto err_free; -- 1.5.4.4 -- With best wishes Dmitry ^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH 1/6] Clocklib: add generic framework for managing clocks. @ 2008-03-31 8:42 Dmitry Baryshkov 0 siblings, 0 replies; 12+ messages in thread From: Dmitry Baryshkov @ 2008-03-31 8:42 UTC (permalink / raw) To: linux-kernel Cc: akpm, Haavard Skinnemoen, Russell King, Paul Mundt, pHilipp Zabel, Pavel Machek, tony, paul Provide a generic framework that platform may choose to support clocks api. In particular this provides platform-independant struct clk definition, a full implementation of clocks api and a set of functions for registering and unregistering clocks in a safe way. Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com> --- include/linux/clklib.h | 149 ++++++++++++++++++++++ init/Kconfig | 7 + kernel/Makefile | 1 + kernel/clklib.c | 326 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 483 insertions(+), 0 deletions(-) create mode 100644 include/linux/clklib.h create mode 100644 kernel/clklib.c diff --git a/include/linux/clklib.h b/include/linux/clklib.h new file mode 100644 index 0000000..88ab2c1 --- /dev/null +++ b/include/linux/clklib.h @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2008 Dmitry Baryshkov + * + * This file is released under the GPL v2. + */ + +#ifndef CLKLIB_H +#define CLKLIB_H + +#include <linux/list.h> + +struct seq_file; + +/** + * struct clk_ops - generic clock management operations + * @can_get: checks whether passed device can get this clock + * @set_parent: reconfigures the clock to use specified parent + * @set_mode: enable or disable specified clock + * @get_rate: obtain the current clock rate of a specified clock + * @set_rate: set the clock rate for a specified clock + * @round_rate: adjust a reate to the exact rate a clock can provide + * + * This structure specifies clock operations that are used to configure + * specific clock. + */ +struct clk_ops { + int (*can_get)(struct clk *clk, struct device *dev); + int (*set_parent)(struct clk *clk, struct clk *parent); + int (*set_mode) (struct clk *clk, bool enable); + unsigned long (*get_rate) (struct clk *clk); + int (*set_rate) (struct clk *, unsigned long); + long (*round_rate) (struct clk *, unsigned long); +}; + +/** + * struct clk - generic struct clk implementation used in the clocklib + * @node: used to place all clocks in a list + * @parent: the parent clock + * @owner: module holding all the functions + * @name: the name of this clock + * @users: count how many users have enabled this clock + * @ops: a pointer to clock management operations + */ +struct clk { + struct list_head node; + struct clk *parent; + struct module *owner; + + const char *name; + int users; + + const struct clk_ops *ops; +}; + +int __must_check clk_register(struct clk *clk); +void clk_unregister(struct clk *clk); +static void __maybe_unused clks_unregister(struct clk **clks, size_t num) +{ + int i; + for (i = num - 1; i >= 0; i++) { + clk_unregister(clks[i]); + } +} + +static int __must_check __maybe_unused clks_register(struct clk **clks, size_t num) +{ + int i; + int ret; + for (i = 0; i < num; i++) { + ret = clk_register(clks[i]); + if (ret != 0) + goto cleanup; + } + + return 0; + +cleanup: + clks_unregister(clks, i); + + for (i -- ; i >= 0; i--) { + clk_unregister(clks[i]); + } + + return ret; +} + +int __must_check clk_alloc_function(const char *parent, struct clk *clk); + +/* + * struct clk_function - helper that allows easy registration of clock "functions" + * @parent: the name of the parent clock + * @clk: the clock that will be set up and installed + * + * Sometimes single clock will have multiple users or several clocks + * will be bound to similar devices. This allows one to register + * simple wrapper clocks that serve only naming purposes. + */ +struct clk_function { + const char *parent; + void *priv; + struct clk clk; +}; + +#define CLK_FUNC(_clock, _function, _ops, _data) \ + { \ + .parent = _clock, \ + .priv = _data, \ + .clk = { \ + .name= _function, \ + .owner = THIS_MODULE, \ + .ops = _ops, \ + }, \ + } +#define FUNC_TO_CLK(func) &(&(struct clk_function) func)->clk + +static void __maybe_unused clk_free_functions( + struct clk_function *funcs, + int num) +{ + int i; + + for (i = num - 1; i >= 0; i--) { + clk_unregister(&funcs[i].clk); + } +} + +static int __must_check __maybe_unused clk_alloc_functions( + struct clk_function *funcs, + int num) +{ + int i; + int rc; + + for (i = 0; i < num; i++) { + rc = clk_alloc_function(funcs[i].parent, &funcs[i].clk); + + if (rc) { + printk(KERN_ERR "Error allocating %s.%s function.\n", + funcs[i].parent, + funcs[i].clk.name); + clk_free_functions(funcs, i); + return rc; + } + } + + return 0; +} + +#endif diff --git a/init/Kconfig b/init/Kconfig index a97924b..1dd9ce2 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -504,6 +504,13 @@ config CC_OPTIMIZE_FOR_SIZE config SYSCTL bool +config HAVE_CLOCK_LIB + bool + help + Platforms select clocklib if they use this infrastructure + for managing their clocks both built into SoC and provided + by external devices. + menuconfig EMBEDDED bool "Configure standard kernel features (for small systems)" help diff --git a/kernel/Makefile b/kernel/Makefile index 6c584c5..afaed51 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -68,6 +68,7 @@ obj-$(CONFIG_TASK_DELAY_ACCT) += delayacct.o obj-$(CONFIG_TASKSTATS) += taskstats.o tsacct.o obj-$(CONFIG_MARKERS) += marker.o obj-$(CONFIG_LATENCYTOP) += latencytop.o +obj-$(CONFIG_HAVE_CLOCK_LIB) += clklib.o ifneq ($(CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER),y) # According to Alan Modra <alan@linuxcare.com.au>, the -fno-omit-frame-pointer is diff --git a/kernel/clklib.c b/kernel/clklib.c new file mode 100644 index 0000000..012f845 --- /dev/null +++ b/kernel/clklib.c @@ -0,0 +1,326 @@ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/clk.h> +#include <linux/clklib.h> +#include <linux/spinlock.h> +#include <linux/err.h> +#include <linux/delay.h> + +static LIST_HEAD(clocks); +static DEFINE_SPINLOCK(clocks_lock); + +static int __clk_register(struct clk *clk) +{ + if (clk->parent && + !try_module_get(clk->parent->owner)) + return -EINVAL; + + list_add_tail(&clk->node, &clocks); + + return 0; +} + +int __must_check clk_register(struct clk *clk) +{ + unsigned long flags; + int rc; + + spin_lock_irqsave(&clocks_lock, flags); + + rc = __clk_register(clk); + + spin_unlock_irqrestore(&clocks_lock, flags); + + return rc; +} +EXPORT_SYMBOL(clk_register); + +void clk_unregister(struct clk *clk) +{ + unsigned long flags; + + spin_lock_irqsave(&clocks_lock, flags); + list_del(&clk->node); + if (clk->parent) + module_put(clk->parent->owner); + spin_unlock_irqrestore(&clocks_lock, flags); +} +EXPORT_SYMBOL(clk_unregister); + +struct clk *clk_get(struct device *dev, const char *id) +{ + struct clk *p, *clk = ERR_PTR(-ENOENT); + unsigned long flags; + + spin_lock_irqsave(&clocks_lock, flags); + + list_for_each_entry(p, &clocks, node) { + if (strcmp(id, p->name) == 0 && + (p->ops && p->ops->can_get && p->ops->can_get(p, dev)) && + try_module_get(p->owner)) { + clk = p; + break; + } + } + + list_for_each_entry(p, &clocks, node) { + if (strcmp(id, p->name) == 0 && + (!p->ops || !p->ops->can_get) && + try_module_get(p->owner)) { + clk = p; + break; + } + } + + spin_unlock_irqrestore(&clocks_lock, flags); + + return clk; +} +EXPORT_SYMBOL(clk_get); + +void clk_put(struct clk *clk) +{ + unsigned long flags; + + if (!clk || IS_ERR(clk)) + return; + + spin_lock_irqsave(&clocks_lock, flags); + + module_put(clk->owner); + + spin_unlock_irqrestore(&clocks_lock, flags); +} +EXPORT_SYMBOL(clk_put); + +int clk_set_parent(struct clk *clk, struct clk *parent) +{ + int rc; + unsigned long flags; + + if (!clk || IS_ERR(clk) || !clk->ops || !clk->ops->set_parent) + return -EINVAL; + + spin_lock_irqsave(&clocks_lock, flags); + + rc = clk->ops->set_parent(clk, parent); + if (!rc) + clk->parent = parent; + + spin_unlock_irqrestore(&clocks_lock, flags); + + return rc; +} +EXPORT_SYMBOL(clk_set_parent); + +struct clk *clk_get_parent(struct clk *clk) +{ + struct clk* parent; + unsigned long flags; + + spin_lock_irqsave(&clocks_lock, flags); + + parent = clk->parent; + + spin_unlock_irqrestore(&clocks_lock, flags); + + return parent; + +} + +static void __clk_disable(struct clk *clk) +{ + if (clk->users <= 0) { + WARN_ON(1); + return; + } + + if (--clk->users == 0) + if (clk->ops && clk->ops->set_mode) + clk->ops->set_mode(clk, false); + + if (clk->parent) + __clk_disable(clk->parent); +} + +void clk_disable(struct clk *clk) +{ + unsigned long flags; + + if (!clk || IS_ERR(clk)) + return; + + spin_lock_irqsave(&clocks_lock, flags); + + __clk_disable(clk); + + spin_unlock_irqrestore(&clocks_lock, flags); +} +EXPORT_SYMBOL(clk_disable); + +static int __clk_enable(struct clk *clk) +{ + int rc = 0; + + if (clk->parent) { + rc = __clk_enable(clk->parent); + + if (rc) + return rc; + } + + if (clk->users++ != 0) { + return 0; + } + + if (clk->ops && clk->ops->set_mode) { + rc = clk->ops->set_mode(clk, true); + if (rc) { + if (clk->parent) + __clk_disable(clk->parent); + + return rc; + } + } + + return rc; +} + +int clk_enable(struct clk *clk) +{ + unsigned long flags; + int rc; + + if (!clk || IS_ERR(clk)) + return -EINVAL; + + spin_lock_irqsave(&clocks_lock, flags); + + rc = __clk_enable(clk); + + spin_unlock_irqrestore(&clocks_lock, flags); + + return rc; +} +EXPORT_SYMBOL(clk_enable); + +static unsigned long __clk_get_rate(struct clk *clk) +{ + unsigned long rate = 0; + + while (clk) { + if (clk->ops && clk->ops->get_rate) { + rate = clk->ops->get_rate(clk); + break; + } else + clk = clk->parent; + } + + return rate; +} + +unsigned long clk_get_rate(struct clk *clk) +{ + unsigned long rate = 0; + unsigned long flags; + + if (!clk || IS_ERR(clk)) + return -EINVAL; + + spin_lock_irqsave(&clocks_lock, flags); + + rate = __clk_get_rate(clk); + + spin_unlock_irqrestore(&clocks_lock, flags); + + return rate; +} +EXPORT_SYMBOL(clk_get_rate); + +long clk_round_rate(struct clk *clk, unsigned long rate) +{ + long res = 0; + unsigned long flags; + struct clk *pclk; + + if (!clk || IS_ERR(clk)) + return -EINVAL; + + spin_lock_irqsave(&clocks_lock, flags); + + for (pclk = clk; !res && pclk ; pclk = pclk->parent) { + if (pclk->ops && pclk->ops->round_rate) + res = pclk->ops->round_rate(clk, rate); + } + + if (!res) + res = __clk_get_rate(clk); + + spin_unlock_irqrestore(&clocks_lock, flags); + + return res; +} +EXPORT_SYMBOL(clk_round_rate); + +int clk_set_rate(struct clk *clk, unsigned long rate) +{ + int rc = -EINVAL; + unsigned long flags; + + spin_lock_irqsave(&clocks_lock, flags); + + while (clk && !IS_ERR(clk)) { + if (clk->ops && clk->ops->set_rate) { + rc = clk->ops->set_rate(clk, rate); + break; + } + + clk = clk->parent; + } + + spin_unlock_irqrestore(&clocks_lock, flags); + + return rc; +} +EXPORT_SYMBOL(clk_set_rate); + +int clk_alloc_function(const char *parent, struct clk *clk) +{ + int rc = 0; + unsigned long flags; + struct clk *pclk; + bool found = false; + + spin_lock_irqsave(&clocks_lock, flags); + + list_for_each_entry(pclk, &clocks, node) { + if (strcmp(parent, pclk->name) == 0 && + try_module_get(pclk->owner)) { + found = true; + break; + } + } + + if (!found) { + rc = -ENODEV; + goto out; + } + + clk->parent = pclk; + + __clk_register(clk); + /* + * We locked parent owner during search + * and also in __clk_register. Free one reference + */ + module_put(pclk->owner); + +out: + if (rc) { + kfree(clk); + } + spin_unlock_irqrestore(&clocks_lock, flags); + + return rc; +} +EXPORT_SYMBOL(clk_alloc_function); -- 1.5.4.4 -- With best wishes Dmitry ^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH 0/6] Clocklib: generic clocks framework @ 2008-04-03 13:21 Dmitry Baryshkov 2008-04-03 13:23 ` [PATCH 1/6] Clocklib: add generic framework for managing clocks Dmitry Baryshkov 0 siblings, 1 reply; 12+ messages in thread From: Dmitry Baryshkov @ 2008-04-03 13:21 UTC (permalink / raw) To: linux-kernel Cc: akpm, Haavard Skinnemoen, Russell King, Paul Mundt, pHilipp Zabel, Pavel Machek, tony, paul Hi, Here is an updated version of the clocklib patchset. Changes since last repost: * fix an clks_register error path, as noted by Pavel Machek -- With best wishes Dmitry ^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH 1/6] Clocklib: add generic framework for managing clocks. 2008-04-03 13:21 [PATCH 0/6] Clocklib: generic clocks framework Dmitry Baryshkov @ 2008-04-03 13:23 ` Dmitry Baryshkov 2008-04-07 22:55 ` Andrew Morton 0 siblings, 1 reply; 12+ messages in thread From: Dmitry Baryshkov @ 2008-04-03 13:23 UTC (permalink / raw) To: linux-kernel Cc: akpm, Haavard Skinnemoen, Russell King, Paul Mundt, pHilipp Zabel, Pavel Machek, tony, paul Provide a generic framework that platform may choose to support clocks api. In particular this provides platform-independant struct clk definition, a full implementation of clocks api and a set of functions for registering and unregistering clocks in a safe way. Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com> --- include/linux/clklib.h | 145 +++++++++++++++++++++ init/Kconfig | 7 + kernel/Makefile | 1 + kernel/clklib.c | 326 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 479 insertions(+), 0 deletions(-) create mode 100644 include/linux/clklib.h create mode 100644 kernel/clklib.c diff --git a/include/linux/clklib.h b/include/linux/clklib.h new file mode 100644 index 0000000..3d0ffaf --- /dev/null +++ b/include/linux/clklib.h @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2008 Dmitry Baryshkov + * + * This file is released under the GPL v2. + */ + +#ifndef CLKLIB_H +#define CLKLIB_H + +#include <linux/list.h> + +struct seq_file; + +/** + * struct clk_ops - generic clock management operations + * @can_get: checks whether passed device can get this clock + * @set_parent: reconfigures the clock to use specified parent + * @set_mode: enable or disable specified clock + * @get_rate: obtain the current clock rate of a specified clock + * @set_rate: set the clock rate for a specified clock + * @round_rate: adjust a reate to the exact rate a clock can provide + * + * This structure specifies clock operations that are used to configure + * specific clock. + */ +struct clk_ops { + int (*can_get)(struct clk *clk, struct device *dev); + int (*set_parent)(struct clk *clk, struct clk *parent); + int (*set_mode) (struct clk *clk, bool enable); + unsigned long (*get_rate) (struct clk *clk); + int (*set_rate) (struct clk *, unsigned long); + long (*round_rate) (struct clk *, unsigned long); +}; + +/** + * struct clk - generic struct clk implementation used in the clocklib + * @node: used to place all clocks in a list + * @parent: the parent clock + * @owner: module holding all the functions + * @name: the name of this clock + * @users: count how many users have enabled this clock + * @ops: a pointer to clock management operations + */ +struct clk { + struct list_head node; + struct clk *parent; + struct module *owner; + + const char *name; + int users; + + const struct clk_ops *ops; +}; + +int __must_check clk_register(struct clk *clk); +void clk_unregister(struct clk *clk); +static void __maybe_unused clks_unregister(struct clk **clks, size_t num) +{ + int i; + for (i = num - 1; i >= 0; i++) { + clk_unregister(clks[i]); + } +} + +static int __must_check __maybe_unused clks_register(struct clk **clks, size_t num) +{ + int i; + int ret; + for (i = 0; i < num; i++) { + ret = clk_register(clks[i]); + if (ret != 0) + goto cleanup; + } + + return 0; + +cleanup: + clks_unregister(clks, i); + + return ret; +} + +int __must_check clk_alloc_function(const char *parent, struct clk *clk); + +/* + * struct clk_function - helper that allows easy registration of clock "functions" + * @parent: the name of the parent clock + * @clk: the clock that will be set up and installed + * + * Sometimes single clock will have multiple users or several clocks + * will be bound to similar devices. This allows one to register + * simple wrapper clocks that serve only naming purposes. + */ +struct clk_function { + const char *parent; + void *priv; + struct clk clk; +}; + +#define CLK_FUNC(_clock, _function, _ops, _data) \ + { \ + .parent = _clock, \ + .priv = _data, \ + .clk = { \ + .name= _function, \ + .owner = THIS_MODULE, \ + .ops = _ops, \ + }, \ + } +#define FUNC_TO_CLK(func) &(&(struct clk_function) func)->clk + +static void __maybe_unused clk_free_functions( + struct clk_function *funcs, + int num) +{ + int i; + + for (i = num - 1; i >= 0; i--) { + clk_unregister(&funcs[i].clk); + } +} + +static int __must_check __maybe_unused clk_alloc_functions( + struct clk_function *funcs, + int num) +{ + int i; + int rc; + + for (i = 0; i < num; i++) { + rc = clk_alloc_function(funcs[i].parent, &funcs[i].clk); + + if (rc) { + printk(KERN_ERR "Error allocating %s.%s function.\n", + funcs[i].parent, + funcs[i].clk.name); + clk_free_functions(funcs, i); + return rc; + } + } + + return 0; +} + +#endif diff --git a/init/Kconfig b/init/Kconfig index a97924b..1dd9ce2 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -504,6 +504,13 @@ config CC_OPTIMIZE_FOR_SIZE config SYSCTL bool +config HAVE_CLOCK_LIB + bool + help + Platforms select clocklib if they use this infrastructure + for managing their clocks both built into SoC and provided + by external devices. + menuconfig EMBEDDED bool "Configure standard kernel features (for small systems)" help diff --git a/kernel/Makefile b/kernel/Makefile index 6c584c5..afaed51 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -68,6 +68,7 @@ obj-$(CONFIG_TASK_DELAY_ACCT) += delayacct.o obj-$(CONFIG_TASKSTATS) += taskstats.o tsacct.o obj-$(CONFIG_MARKERS) += marker.o obj-$(CONFIG_LATENCYTOP) += latencytop.o +obj-$(CONFIG_HAVE_CLOCK_LIB) += clklib.o ifneq ($(CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER),y) # According to Alan Modra <alan@linuxcare.com.au>, the -fno-omit-frame-pointer is diff --git a/kernel/clklib.c b/kernel/clklib.c new file mode 100644 index 0000000..012f845 --- /dev/null +++ b/kernel/clklib.c @@ -0,0 +1,326 @@ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/clk.h> +#include <linux/clklib.h> +#include <linux/spinlock.h> +#include <linux/err.h> +#include <linux/delay.h> + +static LIST_HEAD(clocks); +static DEFINE_SPINLOCK(clocks_lock); + +static int __clk_register(struct clk *clk) +{ + if (clk->parent && + !try_module_get(clk->parent->owner)) + return -EINVAL; + + list_add_tail(&clk->node, &clocks); + + return 0; +} + +int __must_check clk_register(struct clk *clk) +{ + unsigned long flags; + int rc; + + spin_lock_irqsave(&clocks_lock, flags); + + rc = __clk_register(clk); + + spin_unlock_irqrestore(&clocks_lock, flags); + + return rc; +} +EXPORT_SYMBOL(clk_register); + +void clk_unregister(struct clk *clk) +{ + unsigned long flags; + + spin_lock_irqsave(&clocks_lock, flags); + list_del(&clk->node); + if (clk->parent) + module_put(clk->parent->owner); + spin_unlock_irqrestore(&clocks_lock, flags); +} +EXPORT_SYMBOL(clk_unregister); + +struct clk *clk_get(struct device *dev, const char *id) +{ + struct clk *p, *clk = ERR_PTR(-ENOENT); + unsigned long flags; + + spin_lock_irqsave(&clocks_lock, flags); + + list_for_each_entry(p, &clocks, node) { + if (strcmp(id, p->name) == 0 && + (p->ops && p->ops->can_get && p->ops->can_get(p, dev)) && + try_module_get(p->owner)) { + clk = p; + break; + } + } + + list_for_each_entry(p, &clocks, node) { + if (strcmp(id, p->name) == 0 && + (!p->ops || !p->ops->can_get) && + try_module_get(p->owner)) { + clk = p; + break; + } + } + + spin_unlock_irqrestore(&clocks_lock, flags); + + return clk; +} +EXPORT_SYMBOL(clk_get); + +void clk_put(struct clk *clk) +{ + unsigned long flags; + + if (!clk || IS_ERR(clk)) + return; + + spin_lock_irqsave(&clocks_lock, flags); + + module_put(clk->owner); + + spin_unlock_irqrestore(&clocks_lock, flags); +} +EXPORT_SYMBOL(clk_put); + +int clk_set_parent(struct clk *clk, struct clk *parent) +{ + int rc; + unsigned long flags; + + if (!clk || IS_ERR(clk) || !clk->ops || !clk->ops->set_parent) + return -EINVAL; + + spin_lock_irqsave(&clocks_lock, flags); + + rc = clk->ops->set_parent(clk, parent); + if (!rc) + clk->parent = parent; + + spin_unlock_irqrestore(&clocks_lock, flags); + + return rc; +} +EXPORT_SYMBOL(clk_set_parent); + +struct clk *clk_get_parent(struct clk *clk) +{ + struct clk* parent; + unsigned long flags; + + spin_lock_irqsave(&clocks_lock, flags); + + parent = clk->parent; + + spin_unlock_irqrestore(&clocks_lock, flags); + + return parent; + +} + +static void __clk_disable(struct clk *clk) +{ + if (clk->users <= 0) { + WARN_ON(1); + return; + } + + if (--clk->users == 0) + if (clk->ops && clk->ops->set_mode) + clk->ops->set_mode(clk, false); + + if (clk->parent) + __clk_disable(clk->parent); +} + +void clk_disable(struct clk *clk) +{ + unsigned long flags; + + if (!clk || IS_ERR(clk)) + return; + + spin_lock_irqsave(&clocks_lock, flags); + + __clk_disable(clk); + + spin_unlock_irqrestore(&clocks_lock, flags); +} +EXPORT_SYMBOL(clk_disable); + +static int __clk_enable(struct clk *clk) +{ + int rc = 0; + + if (clk->parent) { + rc = __clk_enable(clk->parent); + + if (rc) + return rc; + } + + if (clk->users++ != 0) { + return 0; + } + + if (clk->ops && clk->ops->set_mode) { + rc = clk->ops->set_mode(clk, true); + if (rc) { + if (clk->parent) + __clk_disable(clk->parent); + + return rc; + } + } + + return rc; +} + +int clk_enable(struct clk *clk) +{ + unsigned long flags; + int rc; + + if (!clk || IS_ERR(clk)) + return -EINVAL; + + spin_lock_irqsave(&clocks_lock, flags); + + rc = __clk_enable(clk); + + spin_unlock_irqrestore(&clocks_lock, flags); + + return rc; +} +EXPORT_SYMBOL(clk_enable); + +static unsigned long __clk_get_rate(struct clk *clk) +{ + unsigned long rate = 0; + + while (clk) { + if (clk->ops && clk->ops->get_rate) { + rate = clk->ops->get_rate(clk); + break; + } else + clk = clk->parent; + } + + return rate; +} + +unsigned long clk_get_rate(struct clk *clk) +{ + unsigned long rate = 0; + unsigned long flags; + + if (!clk || IS_ERR(clk)) + return -EINVAL; + + spin_lock_irqsave(&clocks_lock, flags); + + rate = __clk_get_rate(clk); + + spin_unlock_irqrestore(&clocks_lock, flags); + + return rate; +} +EXPORT_SYMBOL(clk_get_rate); + +long clk_round_rate(struct clk *clk, unsigned long rate) +{ + long res = 0; + unsigned long flags; + struct clk *pclk; + + if (!clk || IS_ERR(clk)) + return -EINVAL; + + spin_lock_irqsave(&clocks_lock, flags); + + for (pclk = clk; !res && pclk ; pclk = pclk->parent) { + if (pclk->ops && pclk->ops->round_rate) + res = pclk->ops->round_rate(clk, rate); + } + + if (!res) + res = __clk_get_rate(clk); + + spin_unlock_irqrestore(&clocks_lock, flags); + + return res; +} +EXPORT_SYMBOL(clk_round_rate); + +int clk_set_rate(struct clk *clk, unsigned long rate) +{ + int rc = -EINVAL; + unsigned long flags; + + spin_lock_irqsave(&clocks_lock, flags); + + while (clk && !IS_ERR(clk)) { + if (clk->ops && clk->ops->set_rate) { + rc = clk->ops->set_rate(clk, rate); + break; + } + + clk = clk->parent; + } + + spin_unlock_irqrestore(&clocks_lock, flags); + + return rc; +} +EXPORT_SYMBOL(clk_set_rate); + +int clk_alloc_function(const char *parent, struct clk *clk) +{ + int rc = 0; + unsigned long flags; + struct clk *pclk; + bool found = false; + + spin_lock_irqsave(&clocks_lock, flags); + + list_for_each_entry(pclk, &clocks, node) { + if (strcmp(parent, pclk->name) == 0 && + try_module_get(pclk->owner)) { + found = true; + break; + } + } + + if (!found) { + rc = -ENODEV; + goto out; + } + + clk->parent = pclk; + + __clk_register(clk); + /* + * We locked parent owner during search + * and also in __clk_register. Free one reference + */ + module_put(pclk->owner); + +out: + if (rc) { + kfree(clk); + } + spin_unlock_irqrestore(&clocks_lock, flags); + + return rc; +} +EXPORT_SYMBOL(clk_alloc_function); -- 1.5.4.4 -- With best wishes Dmitry ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 1/6] Clocklib: add generic framework for managing clocks. 2008-04-03 13:23 ` [PATCH 1/6] Clocklib: add generic framework for managing clocks Dmitry Baryshkov @ 2008-04-07 22:55 ` Andrew Morton 0 siblings, 0 replies; 12+ messages in thread From: Andrew Morton @ 2008-04-07 22:55 UTC (permalink / raw) To: Dmitry Baryshkov Cc: linux-kernel, haavard.skinnemoen, rmk+lkml, lethal, philipp.zabel, pavel, tony, paul On Thu, 3 Apr 2008 17:23:29 +0400 Dmitry Baryshkov <dbaryshkov@gmail.com> wrote: > Provide a generic framework that platform may choose > to support clocks api. In particular this provides > platform-independant struct clk definition, a full > implementation of clocks api and a set of functions > for registering and unregistering clocks in a safe way. > > +static void __maybe_unused clks_unregister(struct clk **clks, size_t num) > +static int __must_check __maybe_unused clks_register(struct clk **clks, size_t num) > +static void __maybe_unused clk_free_functions( > + struct clk_function *funcs, > + int num) > +static int __must_check __maybe_unused clk_alloc_functions( > + struct clk_function *funcs, > + int num) What are all these __maybe_unused markers doing here? > ... > > +int __must_check clk_alloc_function(const char *parent, struct clk *clk); This should be `static int'. I'm surprised the compiler doesn't get upset. > + for (i = num - 1; i >= 0; i--) { > + clk_unregister(&funcs[i].clk); > + } > > ... > > + if (rc) { > + kfree(clk); > + } There are a few coding-style glitches in here. Please run scripts/checkpatch.pl and consider its reportage. ^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2008-04-07 22:57 UTC | newest] Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2008-03-31 8:39 [PATCH 0/6] Clocklib: generic clocks framework Dmitry Baryshkov 2008-03-31 8:44 ` [PATCH 1/6] Clocklib: add generic framework for managing clocks Dmitry Baryshkov 2008-03-31 14:55 ` Pavel Machek 2008-03-31 16:18 ` Dmitry 2008-03-31 8:44 ` [PATCH 2/6] Clocklib: debugfs support Dmitry Baryshkov 2008-03-31 8:44 ` [PATCH 3/6] Clocklib: support sa1100 sub-arch Dmitry Baryshkov 2008-03-31 8:44 ` [PATCH 4/6] Clocklib: support ARM pxa sub-arch Dmitry Baryshkov 2008-03-31 8:44 ` [PATCH 5/6] Clocklib: Use correct clock for IrDA on pxa Dmitry Baryshkov 2008-03-31 8:45 ` [PATCH 6/6] Clocklib: use correct name for 3,6MHz clock Dmitry Baryshkov 2008-03-31 8:42 [PATCH 1/6] Clocklib: add generic framework for managing clocks Dmitry Baryshkov 2008-04-03 13:21 [PATCH 0/6] Clocklib: generic clocks framework Dmitry Baryshkov 2008-04-03 13:23 ` [PATCH 1/6] Clocklib: add generic framework for managing clocks Dmitry Baryshkov 2008-04-07 22:55 ` Andrew Morton
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®