Hi,
This is again a set of patches to unify the management of clocks and
allow easy registration and unregistration of them. This is neccessary
to cleanly support such devices as toshiba mobile companion chips,
sa1111 companion, etc. Also it brings code unification, especially for a
lot of arm sub-arches which share nearly the same code, etc.
This is the "version 3" approach. Given the negative response to
kobjects, I've redesigned it to use plain krefs.
Debugfs support is merged into main clocklib patch. Documentation
for it's interface will come later. For now it provides tree structure
with single file per each clock directory.
--
With best wishes
Dmitry
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 <[email protected]>
---
include/linux/clocklib.h | 58 ++++++++
lib/Kconfig | 3 +
lib/Makefile | 1 +
lib/clocklib.c | 353 ++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 415 insertions(+), 0 deletions(-)
create mode 100644 include/linux/clocklib.h
create mode 100644 lib/clocklib.c
diff --git a/include/linux/clocklib.h b/include/linux/clocklib.h
new file mode 100644
index 0000000..cf2b41e
--- /dev/null
+++ b/include/linux/clocklib.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2008 Dmitry Baryshkov
+ *
+ * This file is released under the GPL v2.
+ */
+
+#ifndef CLKLIB_H
+#define CLKLIB_H
+
+#include <linux/spinlock.h>
+#include <linux/kref.h>
+
+struct clk;
+
+/**
+ * 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 (*enable)(struct clk *clk);
+ void (*disable)(struct clk *clk);
+ unsigned long (*get_rate)(struct clk *clk);
+ long (*round_rate)(struct clk *clk, unsigned long hz, bool apply);
+};
+
+
+struct clk {
+ struct list_head node;
+ spinlock_t *lock;
+ struct kref ref;
+ int usage;
+#ifdef CONFIG_DEBUG_FS
+ struct dentry *dir;
+ struct dentry *info;
+#endif
+
+ const char *name;
+ struct clk *parent;
+ struct clk_ops *ops;
+ void (*release)(struct clk *clk);
+};
+
+int clk_register(struct clk *clk);
+void clk_unregister(struct clk *clk);
+int clks_register(struct clk **clk, size_t num);
+void clks_unregister(struct clk **clk, size_t num);
+
+#endif
diff --git a/lib/Kconfig b/lib/Kconfig
index 8cc8e87..592f5e1 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -13,6 +13,9 @@ config GENERIC_FIND_FIRST_BIT
config GENERIC_FIND_NEXT_BIT
def_bool n
+config HAVE_CLOCKLIB
+ tristate
+
config CRC_CCITT
tristate "CRC-CCITT functions"
help
diff --git a/lib/Makefile b/lib/Makefile
index 74b0cfb..cee74e1 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -37,6 +37,7 @@ obj-$(CONFIG_PLIST) += plist.o
obj-$(CONFIG_DEBUG_PREEMPT) += smp_processor_id.o
obj-$(CONFIG_DEBUG_LIST) += list_debug.o
obj-$(CONFIG_DEBUG_OBJECTS) += debugobjects.o
+obj-$(CONFIG_HAVE_CLOCKLIB) += clocklib.o
ifneq ($(CONFIG_HAVE_DEC_LOCK),y)
lib-y += dec_and_lock.o
diff --git a/lib/clocklib.c b/lib/clocklib.c
new file mode 100644
index 0000000..590a665
--- /dev/null
+++ b/lib/clocklib.c
@@ -0,0 +1,353 @@
+/*
+ * Generic clocks API implementation
+ *
+ * Copyright (c) 2008 Dmitry Baryshkov
+ *
+ * This file is released under the GPL v2.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/clocklib.h>
+#include <linux/spinlock.h>
+
+static LIST_HEAD(clocks);
+static DEFINE_SPINLOCK(clocks_lock);
+
+#ifdef CONFIG_DEBUG_FS
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+static struct dentry *clkdir;
+
+static int clocklib_show(struct seq_file *s, void *data)
+{
+ struct clk *clk = s->private;
+
+ BUG_ON(!clk);
+
+ seq_printf(s, "set_parent=%savailable\nusage=%d/%d\nrate=%10lu Hz\n",
+ clk->ops && clk->ops->set_parent ? "not " : "",
+ clk->usage, atomic_read(&clk->ref.refcount),
+ clk_get_rate(clk));
+// if (clk->ops && clk->ops->format)
+// clk->ops->format(clk, s);
+
+ return 0;
+}
+
+static int clocklib_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, clocklib_show, inode->i_private);
+}
+
+static struct file_operations clocklib_operations = {
+ .open = clocklib_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int clk_debugfs_init(struct clk *clk)
+{
+ struct dentry *dir;
+ struct dentry *info;
+
+ if (!clkdir)
+ dump_stack();
+
+ dir = debugfs_create_dir(clk->name,
+ clk->parent ? clk->parent->dir : clkdir);
+
+ if (IS_ERR(dir))
+ return PTR_ERR(dir);
+
+ info = debugfs_create_file("info", S_IFREG | S_IRUGO,
+ dir, clk, &clocklib_operations);
+
+ if (IS_ERR(info)) {
+ debugfs_remove(dir);
+ return PTR_ERR(info);
+ }
+
+ clk->dir = dir;
+ clk->info = info;
+
+ return 0;
+}
+
+static void clk_debugfs_clean(struct clk *clk)
+{
+ if (clk->info)
+ debugfs_remove(clk->info);
+ clk->info = NULL;
+
+ if (clk->dir)
+ debugfs_remove(clk->dir);
+ clk->dir = NULL;
+}
+
+static void clk_debugfs_reparent(struct clk *clk, struct clk *old, struct clk *new)
+{
+ struct dentry *oldd = old ? old->dir : clkdir;
+ struct dentry *newd = new ? new->dir : clkdir;
+ struct dentry *dir = debugfs_rename(oldd, clk->dir, newd, clk->name);
+
+ if (IS_ERR(dir))
+ WARN_ON(1);
+ else
+ clk->dir = dir;
+}
+
+static int __init clocklib_debugfs_init(void)
+{
+ clkdir = debugfs_create_dir("clocks", NULL);
+ return 0;
+}
+core_initcall(clocklib_debugfs_init);
+#else
+#define clk_debugfs_init(clk) ({0;})
+#define clk_debugfs_clean(clk) do {} while (0);
+#define clk_debugfs_reparent(clk, old, new) do {} while (0);
+#endif
+
+static int clk_can_get_def(struct clk *clk, struct device *dev)
+{
+ return 1;
+}
+
+static unsigned long clk_get_rate_def(struct clk *clk)
+{
+ return 0;
+}
+
+static long clk_round_rate_def(struct clk *clk, unsigned long hz, bool apply)
+{
+ long rate = clk->ops->get_rate(clk);
+
+ if (apply && hz != rate)
+ return -EINVAL;
+
+ return rate;
+}
+
+static void clk_release(struct kref *ref)
+{
+ struct clk *clk = container_of(ref, struct clk, ref);
+
+ BUG_ON(!clk->release);
+
+ if (clk->parent)
+ kref_get(&clk->parent->ref);
+
+ clk->release(clk);
+}
+EXPORT_SYMBOL(clk_round_rate);
+
+struct clk* clk_get_parent(struct clk *clk)
+{
+ struct clk *parent;
+
+ spin_lock(clk->lock);
+
+ parent = clk->parent;
+ kref_get(&parent->ref);
+
+ spin_unlock(clk->lock);
+
+ return parent;
+}
+EXPORT_SYMBOL(clk_get_parent);
+
+int clk_set_parent(struct clk *clk, struct clk *parent)
+{
+ int rc = -EINVAL;
+ struct clk *old;
+
+ spin_lock(clk->lock);
+
+ if (!clk->ops->set_parent)
+ goto out;
+
+ old = clk->parent;
+
+ rc = clk->ops->set_parent(clk, parent);
+ if (rc)
+ goto out;
+
+ kref_get(&parent->ref);
+ clk->parent = parent;
+
+ clk_debugfs_reparent(clk, old, parent);
+
+ kref_put(&old->ref, clk_release);
+
+out:
+ spin_unlock(clk->lock);
+
+ return rc;
+}
+EXPORT_SYMBOL(clk_set_parent);
+
+int clk_register(struct clk *clk)
+{
+ int rc;
+
+ BUG_ON(!clk->ops);
+ BUG_ON(!clk->ops->enable || !clk->ops->disable);
+
+ if (!clk->ops->can_get)
+ clk->ops->can_get = clk_can_get_def;
+ if (!clk->ops->get_rate)
+ clk->ops->get_rate = clk_get_rate_def;
+ if (!clk->ops->round_rate)
+ clk->ops->round_rate = clk_round_rate_def;
+
+ kref_init(&clk->ref);
+
+ spin_lock(&clocks_lock);
+ if (clk->parent)
+ kref_get(&clk->parent->ref);
+ list_add_tail(&clk->node, &clocks);
+
+ rc = clk_debugfs_init(clk);
+ if (rc) {
+ list_del(&clk->node);
+ kref_put(&clk->ref, clk_release);
+ }
+
+ spin_unlock(&clocks_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(clk_register);
+
+void clk_unregister(struct clk *clk)
+{
+ spin_lock(&clocks_lock);
+ clk_debugfs_clean(clk);
+ list_del(&clk->node);
+ kref_put(&clk->ref, clk_release);
+ spin_unlock(&clocks_lock);
+}
+EXPORT_SYMBOL(clk_unregister);
+
+int clks_register(struct clk **clk, size_t num)
+{
+ int i;
+ int rc;
+ for (i = 0; i < num; i++) {
+ rc = clk_register(clk[i]);
+ if (rc)
+ return rc;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(clks_register);
+
+void clks_unregister(struct clk **clk, size_t num)
+{
+ int i;
+ for (i = 0; i < num; i++)
+ clk_unregister(clk[i]);
+}
+EXPORT_SYMBOL(clks_unregister);
+
+struct clk *clk_get(struct device *dev, const char *id)
+{
+ struct clk *clk = NULL, *p;
+
+ spin_lock(&clocks_lock);
+ list_for_each_entry(p, &clocks, node)
+ if (strcmp(id, p->name) == 0 && p->ops->can_get(p, dev)) {
+ clk = p;
+ kref_get(&clk->ref);
+ break;
+ }
+
+ spin_unlock(&clocks_lock);
+
+ return clk;
+}
+EXPORT_SYMBOL(clk_get);
+
+void clk_put(struct clk *clk)
+{
+ kref_put(&clk->ref, clk_release);
+}
+EXPORT_SYMBOL(clk_put);
+
+int clk_enable(struct clk *clk)
+{
+ int rc = 0;
+
+ spin_lock(clk->lock);
+
+ clk->usage++;
+ if (clk->usage == 1)
+ rc = clk->ops->enable(clk);
+
+ if (rc)
+ clk->usage--;
+
+ spin_unlock(clk->lock);
+
+ return rc;
+}
+EXPORT_SYMBOL(clk_enable);
+
+void clk_disable(struct clk *clk)
+{
+ spin_lock(clk->lock);
+
+ WARN_ON(clk->usage <= 0);
+
+ clk->usage--;
+ if (clk->usage == 0)
+ clk->ops->disable(clk);
+
+ spin_unlock(clk->lock);
+}
+EXPORT_SYMBOL(clk_disable);
+
+unsigned long clk_get_rate(struct clk *clk)
+{
+ unsigned long hz;
+
+ spin_lock(clk->lock);
+
+ hz = clk->ops->get_rate(clk);
+
+ spin_unlock(clk->lock);
+
+ return hz;
+}
+EXPORT_SYMBOL(clk_get_rate);
+
+int clk_set_rate(struct clk *clk, unsigned long hz)
+{
+ long rc;
+
+ spin_lock(clk->lock);
+
+ rc = clk->ops->round_rate(clk, hz, 1);
+
+ spin_unlock(clk->lock);
+
+ return rc < 0 ? rc : 0;
+}
+EXPORT_SYMBOL(clk_set_rate);
+
+long clk_round_rate(struct clk *clk, unsigned long hz)
+{
+ long rc;
+
+ spin_lock(clk->lock);
+
+ rc = clk->ops->round_rate(clk, hz, 0);
+
+ spin_unlock(clk->lock);
+
+ return rc;
+}
+
--
1.5.5.4
--
With best wishes
Dmitry
Make PXA use clocklib for clocks support.
Signed-off-by: Dmitry Baryshkov <[email protected]>
---
arch/arm/Kconfig | 1 +
arch/arm/mach-pxa/clock.c | 157 ++++++++++++++++++--------------------------
arch/arm/mach-pxa/clock.h | 89 ++++++++++++++++---------
arch/arm/mach-pxa/pxa25x.c | 69 ++++++++++++-------
arch/arm/mach-pxa/pxa27x.c | 78 +++++++++++++---------
arch/arm/mach-pxa/pxa3xx.c | 154 ++++++++++++++++++++++++++----------------
6 files changed, 307 insertions(+), 241 deletions(-)
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index fe1455e..e7d0343 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -393,6 +393,7 @@ config ARCH_PXA
select GENERIC_TIME
select GENERIC_CLOCKEVENTS
select TICK_ONESHOT
+ select HAVE_CLOCKLIB
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 180c8bb..f326b80 100644
--- a/arch/arm/mach-pxa/clock.c
+++ b/arch/arm/mach-pxa/clock.c
@@ -3,152 +3,121 @@
*/
#include <linux/module.h>
#include <linux/kernel.h>
-#include <linux/list.h>
-#include <linux/errno.h>
-#include <linux/err.h>
-#include <linux/string.h>
#include <linux/clk.h>
-#include <linux/spinlock.h>
-#include <linux/platform_device.h>
+#include <linux/clocklib.h>
#include <linux/delay.h>
#include <asm/arch/pxa2xx-regs.h>
#include <asm/arch/pxa2xx-gpio.h>
#include <asm/hardware.h>
-#include "devices.h"
-#include "generic.h"
+//#include "devices.h"
+//#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_enable(struct clk *clk)
{
- struct clk *p;
-
- list_for_each_entry(p, &clocks, node)
- if (strcmp(id, p->name) == 0 && p->dev == dev)
- return p;
-
- return NULL;
+ pxa_gpio_mode(GPIO11_3_6MHz_MD);
+ return 0;
}
-struct clk *clk_get(struct device *dev, const char *id)
+static void clk_gpio11_disable(struct clk *clk)
{
- struct clk *p, *clk = ERR_PTR(-ENOENT);
-
- mutex_lock(&clocks_mutex);
- p = clk_lookup(dev, id);
- if (!p)
- p = clk_lookup(NULL, id);
- if (p)
- clk = p;
- mutex_unlock(&clocks_mutex);
-
- return clk;
+ /* do nothing */
}
-EXPORT_SYMBOL(clk_get);
-void clk_put(struct clk *clk)
+static unsigned long clk_gpio11_get_rate(struct clk *clk)
{
+ return 3686400;
}
-EXPORT_SYMBOL(clk_put);
-int clk_enable(struct clk *clk)
+static struct clk_ops clk_gpio11_ops = {
+ .enable = clk_gpio11_enable,
+ .disable = clk_gpio11_disable,
+ .get_rate = clk_gpio11_get_rate,
+};
+
+static struct clk clk_gpio11 = {
+ .name = "GPIO27_CLK",
+ .ops = &clk_gpio11_ops,
+ .release = clk_static_release,
+};
+
+int clk_cken_enable(struct clk *clk)
{
- unsigned long flags;
+ struct clk_cken *priv = container_of(clk, struct clk_cken, clk);
- spin_lock_irqsave(&clocks_lock, flags);
- if (clk->enabled++ == 0)
- clk->ops->enable(clk);
- spin_unlock_irqrestore(&clocks_lock, flags);
+ CKEN |= 1 << priv->cken;
- if (clk->delay)
- udelay(clk->delay);
+ if (priv->delay)
+ udelay(priv->delay);
return 0;
}
-EXPORT_SYMBOL(clk_enable);
-void clk_disable(struct clk *clk)
+void clk_cken_disable(struct clk *clk)
{
- unsigned long flags;
+ struct clk_cken *priv = container_of(clk, struct clk_cken, clk);
- WARN_ON(clk->enabled == 0);
-
- spin_lock_irqsave(&clocks_lock, flags);
- if (--clk->enabled == 0)
- clk->ops->disable(clk);
- spin_unlock_irqrestore(&clocks_lock, flags);
+ CKEN &= ~(1 << priv->cken);
}
-EXPORT_SYMBOL(clk_disable);
-unsigned long clk_get_rate(struct clk *clk)
+unsigned long clk_cken_get_rate(struct clk *clk)
{
- unsigned long rate;
+ struct clk_cken *priv = container_of(clk, struct clk_cken, clk);
- rate = clk->rate;
- if (clk->ops->getrate)
- rate = clk->ops->getrate(clk);
+ return priv->rate;
- return rate;
}
-EXPORT_SYMBOL(clk_get_rate);
+struct clk_ops clk_cken_ops = {
+ .enable = clk_cken_enable,
+ .disable = clk_cken_disable,
+ .get_rate = clk_cken_get_rate,
+};
-static void clk_gpio27_enable(struct clk *clk)
+static inline int clk_enable_parent(struct clk *clk)
{
- pxa_gpio_mode(GPIO11_3_6MHz_MD);
+ BUG_ON(!clk->parent);
+ return clk_enable(clk->parent);
}
-static void clk_gpio27_disable(struct clk *clk)
+static inline void clk_disable_parent(struct clk *clk)
{
+ BUG_ON(!clk->parent);
+ clk_disable(clk->parent);
}
-static const struct clkops clk_gpio27_ops = {
- .enable = clk_gpio27_enable,
- .disable = clk_gpio27_disable,
-};
-
-
-void clk_cken_enable(struct clk *clk)
+static inline unsigned long clk_get_rate_parent(struct clk *clk)
{
- CKEN |= 1 << clk->cken;
+ BUG_ON(!clk->parent);
+ return clk_get_rate(clk->parent);
}
-void clk_cken_disable(struct clk *clk)
+static inline long clk_round_rate_parent(struct clk *clk, unsigned long hz, bool apply)
{
- CKEN &= ~(1 << clk->cken);
+ BUG_ON(!clk->parent);
+ return apply ? clk_set_rate(clk->parent, hz) :
+ clk_round_rate(clk->parent, hz);
}
-const struct clkops clk_cken_ops = {
- .enable = clk_cken_enable,
- .disable = clk_cken_disable,
-};
-
-static struct clk common_clks[] = {
- {
- .name = "GPIO27_CLK",
- .ops = &clk_gpio27_ops,
- .rate = 3686400,
- },
-};
-
-void clks_register(struct clk *clks, size_t num)
+static inline int clk_devck_can_get(struct clk *clk, struct device *dev)
{
- int i;
+ struct clk_devck *dc = container_of(clk, struct clk_devck, clk);
- mutex_lock(&clocks_mutex);
- for (i = 0; i < num; i++)
- list_add(&clks[i].node, &clocks);
- mutex_unlock(&clocks_mutex);
+ return dc->dev == dev;
}
+struct clk_ops clk_devck_ops = {
+ .can_get = clk_devck_can_get,
+ .enable = clk_enable_parent,
+ .disable = clk_disable_parent,
+ .get_rate = clk_get_rate_parent,
+ .round_rate = clk_round_rate_parent,
+};
+
static int __init clk_init(void)
{
- clks_register(common_clks, ARRAY_SIZE(common_clks));
- return 0;
+ return clk_register(&clk_gpio11);
}
arch_initcall(clk_init);
diff --git a/arch/arm/mach-pxa/clock.h b/arch/arm/mach-pxa/clock.h
index bc6b77e..b787bbe 100644
--- a/arch/arm/mach-pxa/clock.h
+++ b/arch/arm/mach-pxa/clock.h
@@ -1,43 +1,70 @@
-struct clk;
+#include <linux/clk.h>
+#include <linux/clocklib.h>
-struct clkops {
- void (*enable)(struct clk *);
- void (*disable)(struct clk *);
- unsigned long (*getrate)(struct clk *);
+struct clk_devck {
+ struct clk clk;
+ const char *parent;
+ struct device *dev;
};
-struct clk {
- struct list_head node;
- const char *name;
- struct device *dev;
- const struct clkops *ops;
+extern struct clk_ops clk_devck_ops;
+
+struct clk_cken {
+ struct clk clk;
unsigned long rate;
unsigned int cken;
unsigned int delay;
- unsigned int enabled;
};
-#define INIT_CKEN(_name, _cken, _rate, _delay, _dev) \
- { \
- .name = _name, \
- .dev = _dev, \
- .ops = &clk_cken_ops, \
- .rate = _rate, \
- .cken = CKEN_##_cken, \
- .delay = _delay, \
- }
+int clk_cken_enable(struct clk *clk);
+void clk_cken_disable(struct clk *clk);
+unsigned long clk_cken_get_rate(struct clk *clk);
-#define INIT_CK(_name, _cken, _ops, _dev) \
- { \
- .name = _name, \
- .dev = _dev, \
- .ops = _ops, \
- .cken = CKEN_##_cken, \
- }
+extern struct clk_ops clk_cken_ops;
-extern const struct clkops clk_cken_ops;
+static inline void clk_static_release(struct clk *clk)
+{
+ printk(KERN_ERR "Can't release static clock: %s!!!\n", clk->name);
+ BUG();
+}
-void clk_cken_enable(struct clk *clk);
-void clk_cken_disable(struct clk *clk);
+#define INIT_CKEN(_name, _cken, _rate, _delay) \
+ &(struct clk_cken) { \
+ .clk.name = _name, \
+ .clk.ops = &clk_cken_ops, \
+ .clk.release = clk_static_release, \
+ .cken = CKEN_##_cken, \
+ .rate = _rate, \
+ .delay = _delay, \
+ } .clk
-void clks_register(struct clk *clks, size_t num);
+#define INIT_CK(_name, _cken, _ops) \
+ &(struct clk_cken) { \
+ .clk.name = _name, \
+ .clk.ops = _ops, \
+ .clk.release = clk_static_release, \
+ .cken = CKEN_##_cken, \
+ } .clk
+
+#define INIT_DEVCK(_parent_name, _name, _dev) \
+ { \
+ .clk.name = _name, \
+ .clk.ops = &clk_devck_ops, \
+ .clk.release = clk_static_release, \
+ .parent = _parent_name, \
+ .dev = _dev, \
+ }
+
+static inline void clks_devck_register(struct clk_devck *clks, int size)
+{
+ int i;
+ for (i = 0; i < size; i++) {
+ struct clk *parent = clk_get(NULL, clks[i].parent);
+ WARN_ON(!parent);
+ clks[i].clk.parent = parent;
+ if (parent) {
+ clk_register(&clks[i].clk);
+ clk_put(parent);
+ }
+ }
+}
diff --git a/arch/arm/mach-pxa/pxa25x.c b/arch/arm/mach-pxa/pxa25x.c
index 3920d15..de7f0b0 100644
--- a/arch/arm/mach-pxa/pxa25x.c
+++ b/arch/arm/mach-pxa/pxa25x.c
@@ -103,10 +103,10 @@ 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 = {
+static struct clk_ops clk_pxa25x_lcd_ops = {
.enable = clk_cken_enable,
.disable = clk_cken_disable,
- .getrate = clk_pxa25x_lcd_getrate,
+ .get_rate = clk_pxa25x_lcd_getrate,
};
/*
@@ -114,31 +114,44 @@ 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),
- INIT_CKEN("PWMCLK", PWM0, 3686400, 0, &pxa25x_device_pwm0.dev),
- INIT_CKEN("PWMCLK", PWM1, 3686400, 0, &pxa25x_device_pwm1.dev),
-
- INIT_CKEN("AC97CLK", AC97, 24576000, 0, NULL),
+static struct clk *pxa25x_hwuart_clk =
+ INIT_CKEN("HWUARTCLK", HWUART, 14745600, 1);
+static struct clk_devck pxa25x_hwuart_devclk =
+ INIT_DEVCK("HWUARTCLK", "UARTCLK", &pxa_device_hwuart.dev);
+
+static struct clk *pxa25x_clks[] = {
+ INIT_CK("LCDCLK", LCD, &clk_pxa25x_lcd_ops), /* &pxa_device_fb.dev */
+ INIT_CKEN("FFUARTCLK", FFUART, 14745600, 1),
+ INIT_CKEN("BTUARTCLK", BTUART, 14745600, 1),
+ INIT_CKEN("STUARTCLK", STUART, 14745600, 1),
+ 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("SSP_CLK", SSP, 3686400, 0),
+ INIT_CKEN("NSSPCLK", NSSP, 3686400, 0),
+ INIT_CKEN("ASSPCLK", ASSP, 3686400, 0),
+ INIT_CKEN("PWM0CLK", PWM0, 3686400, 0),
+ INIT_CKEN("PWM1CLK", PWM1, 3686400, 0),
+
+ INIT_CKEN("AC97CLK", AC97, 24576000, 0),
/*
- INIT_CKEN("I2SCLK", I2S, 14745600, 0, NULL),
+ INIT_CKEN("I2SCLK", I2S, 14745600, 0),
*/
- INIT_CKEN("FICPCLK", FICP, 47923000, 0, NULL),
+ INIT_CKEN("FICPCLK", FICP, 47923000, 0),
+};
+
+static struct clk_devck clk_children[] = {
+ INIT_DEVCK("FFUARTCLK", "UARTCLK", &pxa_device_ffuart.dev),
+ INIT_DEVCK("BTUARTCLK", "UARTCLK", &pxa_device_btuart.dev),
+ INIT_DEVCK("STUARTCLK", "UARTCLK", &pxa_device_stuart.dev),
+
+ INIT_DEVCK("SSP_CLK", "SSPCLK", &pxa25x_device_ssp.dev),
+ INIT_DEVCK("NSSPCLK", "SSPCLK", &pxa25x_device_nssp.dev),
+ INIT_DEVCK("ASSPCLK", "SSPCLK", &pxa25x_device_assp.dev),
+ INIT_DEVCK("PWM0CLK", "PWMCLK", &pxa25x_device_pwm0.dev),
+ INIT_DEVCK("PWM1CLK", "PWMCLK", &pxa25x_device_pwm1.dev),
};
#ifdef CONFIG_PM
@@ -287,11 +300,15 @@ 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()) {
+ clk_register(pxa25x_hwuart_clk);
+ pxa25x_hwuart_devclk.clk.parent = pxa25x_hwuart_clk;
+ clk_register(&pxa25x_hwuart_devclk.clk);
+ }
if (cpu_is_pxa21x() || cpu_is_pxa25x()) {
clks_register(pxa25x_clks, ARRAY_SIZE(pxa25x_clks));
+ clks_devck_register(ARRAY_AND_SIZE(clk_children));
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 4d7afae..a88be7a 100644
--- a/arch/arm/mach-pxa/pxa27x.c
+++ b/arch/arm/mach-pxa/pxa27x.c
@@ -45,7 +45,7 @@ unsigned int pxa27x_get_clk_frequency_khz(int info)
{
unsigned long ccsr, clkcfg;
unsigned int l, L, m, M, n2, N, S;
- int cccr_a, t, ht, b;
+ int cccr_a, t, ht, b;
ccsr = CCSR;
cccr_a = CCCR & (1 << 25);
@@ -88,7 +88,7 @@ unsigned int pxa27x_get_memclk_frequency_10khz(void)
{
unsigned long ccsr, clkcfg;
unsigned int l, L, m, M;
- int cccr_a, b;
+ int cccr_a, b;
ccsr = CCSR;
cccr_a = CCCR & (1 << 25);
@@ -130,48 +130,63 @@ 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 = {
+static struct clk_ops clk_pxa27x_lcd_ops = {
.enable = clk_cken_enable,
.disable = clk_cken_disable,
- .getrate = clk_pxa27x_lcd_getrate,
+ .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), /* &pxa_device_fb.dev */
+ 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), /* &pxa_device_i2s.dev */
+ INIT_CKEN("I2C_CLK", I2C, 32842000, 0),
+ 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("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, &pxa27x_device_keypad.dev),
+ INIT_CKEN("USBCLK", USBHOST, 48000000, 0), /* &pxa27x_device_ohci.dev */
+ INIT_CKEN("PWRI2CCLK", PWRI2C, 13000000, 0),
+ INIT_CKEN("KBDCLK", KEYPAD, 32768, 0), /* &pxa27x_device_keypad.dev */
- 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("PWMCLK", PWM0, 13000000, 0, &pxa27x_device_pwm0.dev),
- INIT_CKEN("PWMCLK", PWM1, 13000000, 0, &pxa27x_device_pwm1.dev),
+ INIT_CKEN("SSP1CLK", SSP1, 13000000, 0),
+ INIT_CKEN("SSP2CLK", SSP2, 13000000, 0),
+ INIT_CKEN("SSP3CLK", SSP3, 13000000, 0),
+ INIT_CKEN("PWM0CLK", PWM0, 13000000, 0),
+ INIT_CKEN("PWM1CLK", PWM1, 13000000, 0),
- INIT_CKEN("AC97CLK", AC97, 24576000, 0, NULL),
- INIT_CKEN("AC97CONFCLK", AC97CONF, 24576000, 0, NULL),
+ INIT_CKEN("AC97CLK", AC97, 24576000, 0),
+ INIT_CKEN("AC97CONFCLK", AC97CONF, 24576000, 0),
/*
- 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("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_devck clk_children[] = {
+ INIT_DEVCK("FFUARTCLK", "UARTCLK", &pxa_device_ffuart.dev),
+ INIT_DEVCK("BTUARTCLK", "UARTCLK", &pxa_device_btuart.dev),
+ INIT_DEVCK("STUARTCLK", "UARTCLK", &pxa_device_stuart.dev),
+
+ INIT_DEVCK("I2C_CLK", "I2CCLK", &pxa_device_i2c.dev),
+ INIT_DEVCK("PWRI2CCLK", "I2CCLK", &pxa27x_device_i2c_power.dev),
+
+ INIT_DEVCK("SSP1CLK", "SSPCLK", &pxa27x_device_ssp1.dev),
+ INIT_DEVCK("SSP2CLK", "SSPCLK", &pxa27x_device_ssp2.dev),
+ INIT_DEVCK("SSP3CLK", "SSPCLK", &pxa27x_device_ssp3.dev),
+ INIT_DEVCK("PWM0CLK", "PWMCLK", &pxa27x_device_pwm0.dev),
+ INIT_DEVCK("PWM1CLK", "PWMCLK", &pxa27x_device_pwm1.dev),
+};
+
#ifdef CONFIG_PM
#define SAVE(x) sleep_save[SLEEP_SAVE_##x] = x
@@ -384,7 +399,8 @@ static int __init pxa27x_init(void)
int i, ret = 0;
if (cpu_is_pxa27x()) {
- clks_register(pxa27x_clks, ARRAY_SIZE(pxa27x_clks));
+ clks_register(ARRAY_AND_SIZE(pxa27x_clks));
+ clks_devck_register(ARRAY_AND_SIZE(clk_children));
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 d26a9b0..ad78a05 100644
--- a/arch/arm/mach-pxa/pxa3xx.c
+++ b/arch/arm/mach-pxa/pxa3xx.c
@@ -21,6 +21,7 @@
#include <linux/irq.h>
#include <linux/io.h>
#include <linux/sysdev.h>
+#include <linux/delay.h>
#include <asm/hardware.h>
#include <asm/arch/pxa3xx-regs.h>
@@ -144,46 +145,58 @@ 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_enable(struct clk *clk)
{
- unsigned long mask = 1ul << (clk->cken & 0x1f);
+ struct clk_cken *priv = container_of(clk, struct clk_cken, clk);
+ unsigned long mask = 1ul << (priv->cken & 0x1f);
- if (clk->cken < 32)
+ if (priv->cken < 32)
CKENA |= mask;
else
CKENB |= mask;
+
+ if (priv->delay)
+ udelay(priv->delay);
+
+ return 0;
}
static void clk_pxa3xx_cken_disable(struct clk *clk)
{
- unsigned long mask = 1ul << (clk->cken & 0x1f);
+ struct clk_cken *priv = container_of(clk, struct clk_cken, clk);
+ unsigned long mask = 1ul << (priv->cken & 0x1f);
- if (clk->cken < 32)
+ if (priv->cken < 32)
CKENA &= ~mask;
else
CKENB &= ~mask;
}
-static const struct clkops clk_pxa3xx_cken_ops = {
+static struct clk_ops clk_pxa3xx_cken_ops = {
.enable = clk_pxa3xx_cken_enable,
.disable = clk_pxa3xx_cken_disable,
+ .get_rate = clk_cken_get_rate,
};
-static const struct clkops clk_pxa3xx_hsio_ops = {
+static struct clk_ops clk_pxa3xx_hsio_ops = {
.enable = clk_pxa3xx_cken_enable,
.disable = clk_pxa3xx_cken_disable,
- .getrate = clk_pxa3xx_hsio_getrate,
+ .get_rate = clk_pxa3xx_hsio_getrate,
};
-static const struct clkops clk_pxa3xx_ac97_ops = {
+static struct clk_ops clk_pxa3xx_ac97_ops = {
.enable = clk_pxa3xx_cken_enable,
.disable = clk_pxa3xx_cken_disable,
- .getrate = clk_pxa3xx_ac97_getrate,
+ .get_rate = clk_pxa3xx_ac97_getrate,
};
-static void clk_pout_enable(struct clk *clk)
+static int clk_pout_enable(struct clk *clk)
{
- OSCC |= OSCC_PEN;
+ OSCC &= ~OSCC_PEN;
+
+ udelay(70);
+
+ return 0;
}
static void clk_pout_disable(struct clk *clk)
@@ -191,60 +204,82 @@ static void clk_pout_disable(struct clk *clk)
OSCC &= ~OSCC_PEN;
}
-static const struct clkops clk_pout_ops = {
+static unsigned long clk_pout_get_rate(struct clk *clk)
+{
+ return 13000000;
+}
+
+static struct clk_ops clk_pout_ops = {
.enable = clk_pout_enable,
.disable = clk_pout_disable,
+ .get_rate = clk_pout_get_rate,
};
-#define PXA3xx_CKEN(_name, _cken, _rate, _delay, _dev) \
- { \
- .name = _name, \
- .dev = _dev, \
- .ops = &clk_pxa3xx_cken_ops, \
- .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[] = {
- {
+#define PXA3xx_CKEN(_name, _cken, _rate, _delay) \
+ &(struct clk_cken) { \
+ .clk.name = _name, \
+ .clk.ops = &clk_pxa3xx_cken_ops, \
+ .clk.release = clk_static_release, \
+ .cken = CKEN_##_cken, \
+ .rate = _rate, \
+ .delay = _delay, \
+ }.clk
+
+#define PXA3xx_CK(_name, _cken, _ops) \
+ &(struct clk_cken) { \
+ .clk.name = _name, \
+ .clk.ops = _ops, \
+ .clk.release = clk_static_release, \
+ .cken = CKEN_##_cken, \
+ }.clk
+
+static struct clk *pxa3xx_clks[] = {
+ &(struct clk) {
.name = "CLK_POUT",
.ops = &clk_pout_ops,
- .rate = 13000000,
- .delay = 70,
+ .release = clk_static_release,
},
- PXA3xx_CK("LCDCLK", LCD, &clk_pxa3xx_hsio_ops, &pxa_device_fb.dev),
- PXA3xx_CK("CAMCLK", CAMERA, &clk_pxa3xx_hsio_ops, NULL),
- PXA3xx_CK("AC97CLK", AC97, &clk_pxa3xx_ac97_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("KBDCLK", KEYPAD, 32768, 0, &pxa27x_device_keypad.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),
- PXA3xx_CKEN("PWMCLK", PWM0, 13000000, 0, &pxa27x_device_pwm0.dev),
- PXA3xx_CKEN("PWMCLK", PWM1, 13000000, 0, &pxa27x_device_pwm1.dev),
-
- 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),
+ PXA3xx_CK("LCDCLK", LCD, &clk_pxa3xx_hsio_ops), /* &pxa_device_fb.dev */
+ PXA3xx_CK("CAMCLK", CAMERA, &clk_pxa3xx_hsio_ops),
+ PXA3xx_CK("AC97CLK", AC97, &clk_pxa3xx_ac97_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), /* &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("KBDCLK", KEYPAD, 32768, 0), /* &pxa27x_device_keypad.dev */
+
+ 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("PWM0CLK", PWM0, 13000000, 0),
+ PXA3xx_CKEN("PWM1CLK", PWM1, 13000000, 0),
+
+ PXA3xx_CKEN("MMC1CLK", MMC1, 19500000, 0),
+ PXA3xx_CKEN("MMC2CLK", MMC2, 19500000, 0),
+ PXA3xx_CKEN("MMC3CLK", MMC3, 19500000, 0),
+};
+
+static struct clk_devck clk_children[] = {
+ INIT_DEVCK("FFUARTCLK", "UARTCLK", &pxa_device_ffuart.dev),
+ INIT_DEVCK("BTUARTCLK", "UARTCLK", &pxa_device_btuart.dev),
+ INIT_DEVCK("STUARTCLK", "UARTCLK", &pxa_device_stuart.dev),
+
+ INIT_DEVCK("SSP1CLK", "SSPCLK", &pxa27x_device_ssp1.dev),
+ INIT_DEVCK("SSP2CLK", "SSPCLK", &pxa27x_device_ssp2.dev),
+ INIT_DEVCK("SSP3CLK", "SSPCLK", &pxa27x_device_ssp3.dev),
+ INIT_DEVCK("SSP4CLK", "SSPCLK", &pxa3xx_device_ssp4.dev),
+ INIT_DEVCK("PWM0CLK", "PWMCLK", &pxa27x_device_pwm0.dev),
+ INIT_DEVCK("PWM1CLK", "PWMCLK", &pxa27x_device_pwm1.dev),
+
+ INIT_DEVCK("MMC1CLK", "MMCCLK", &pxa_device_mci.dev),
+ INIT_DEVCK("MMC2CLK", "MMCCLK", &pxa3xx_device_mci2.dev),
+ INIT_DEVCK("MMC3CLK", "MMCCLK", &pxa3xx_device_mci3.dev),
};
#ifdef CONFIG_PM
@@ -559,7 +594,8 @@ static int __init pxa3xx_init(void)
*/
ASCR &= ~(ASCR_RDH | ASCR_D1S | ASCR_D2S | ASCR_D3S);
- clks_register(pxa3xx_clks, ARRAY_SIZE(pxa3xx_clks));
+ clks_register(ARRAY_AND_SIZE(pxa3xx_clks));
+ clks_devck_register(ARRAY_AND_SIZE(clk_children));
if ((ret = pxa_init_dma(32)))
return ret;
--
1.5.5.4
--
With best wishes
Dmitry
Make SA-1100 use clocklib for clocks support.
Signed-off-by: Dmitry Baryshkov <[email protected]>
---
arch/arm/Kconfig | 1 +
arch/arm/mach-sa1100/clock.c | 114 ++++++------------------------------------
2 files changed, 17 insertions(+), 98 deletions(-)
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index e7d0343..fffd99a 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -421,6 +421,7 @@ config ARCH_SA1100
select GENERIC_CLOCKEVENTS
select TICK_ONESHOT
select HAVE_GPIO_LIB
+ select HAVE_CLOCKLIB
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..61760b7 100644
--- a/arch/arm/mach-sa1100/clock.c
+++ b/arch/arm/mach-sa1100/clock.c
@@ -3,88 +3,12 @@
*/
#include <linux/module.h>
#include <linux/kernel.h>
-#include <linux/list.h>
-#include <linux/errno.h>
-#include <linux/err.h>
-#include <linux/string.h>
#include <linux/clk.h>
-#include <linux/spinlock.h>
-#include <linux/mutex.h>
+#include <linux/clocklib.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_enable(struct clk *clk)
{
/*
* First, set up the 3.6864MHz clock on GPIO 27 for the SA-1111:
@@ -93,38 +17,32 @@ static void clk_gpio27_enable(void)
GAFR |= GPIO_32_768kHz;
GPDR |= GPIO_32_768kHz;
TUCR = TUCR_3_6864MHz;
+
+ return 0;
}
-static void clk_gpio27_disable(void)
+static void clk_gpio27_disable(struct clk *clk)
{
TUCR = 0;
GPDR &= ~GPIO_32_768kHz;
GAFR &= ~GPIO_32_768kHz;
}
-static struct clk clk_gpio27 = {
- .name = "GPIO27_CLK",
- .rate = 3686400,
+static unsigned long clk_gpio27_get_rate(struct clk *clk)
+{
+ return 3686400;
+}
+
+static struct clk_ops clk_gpio27_ops = {
.enable = clk_gpio27_enable,
.disable = clk_gpio27_disable,
+ .get_rate = clk_gpio27_get_rate,
};
-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);
-
-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 clk_gpio27 = {
+ .name = "GPIO27_CLK",
+ .ops = &clk_gpio27_ops,
+};
static int __init clk_init(void)
{
--
1.5.5.4
--
With best wishes
Dmitry
On Thu, Jun 26, 2008 at 2:51 PM, Dmitry Baryshkov <[email protected]> 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.
>
> Signed-off-by: Dmitry Baryshkov <[email protected]>
> ---
> include/linux/clocklib.h | 58 ++++++++
> lib/Kconfig | 3 +
> lib/Makefile | 1 +
> lib/clocklib.c | 353 ++++++++++++++++++++++++++++++++++++++++++++++
> 4 files changed, 415 insertions(+), 0 deletions(-)
> create mode 100644 include/linux/clocklib.h
> create mode 100644 lib/clocklib.c
>
> diff --git a/include/linux/clocklib.h b/include/linux/clocklib.h
> new file mode 100644
> index 0000000..cf2b41e
> --- /dev/null
> +++ b/include/linux/clocklib.h
> @@ -0,0 +1,58 @@
> +/*
> + * Copyright (C) 2008 Dmitry Baryshkov
> + *
> + * This file is released under the GPL v2.
> + */
> +
> +#ifndef CLKLIB_H
> +#define CLKLIB_H
> +
> +#include <linux/spinlock.h>
> +#include <linux/kref.h>
> +
> +struct clk;
> +
> +/**
> + * 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 (*enable)(struct clk *clk);
> + void (*disable)(struct clk *clk);
> + unsigned long (*get_rate)(struct clk *clk);
> + long (*round_rate)(struct clk *clk, unsigned long hz, bool apply);
> +};
> +
> +
> +struct clk {
> + struct list_head node;
> + spinlock_t *lock;
> + struct kref ref;
> + int usage;
> +#ifdef CONFIG_DEBUG_FS
> + struct dentry *dir;
> + struct dentry *info;
> +#endif
> +
> + const char *name;
> + struct clk *parent;
> + struct clk_ops *ops;
> + void (*release)(struct clk *clk);
> +};
> +
> +int clk_register(struct clk *clk);
> +void clk_unregister(struct clk *clk);
> +int clks_register(struct clk **clk, size_t num);
> +void clks_unregister(struct clk **clk, size_t num);
> +
> +#endif
> diff --git a/lib/Kconfig b/lib/Kconfig
> index 8cc8e87..592f5e1 100644
> --- a/lib/Kconfig
> +++ b/lib/Kconfig
> @@ -13,6 +13,9 @@ config GENERIC_FIND_FIRST_BIT
> config GENERIC_FIND_NEXT_BIT
> def_bool n
>
> +config HAVE_CLOCKLIB
> + tristate
> +
> config CRC_CCITT
> tristate "CRC-CCITT functions"
> help
> diff --git a/lib/Makefile b/lib/Makefile
> index 74b0cfb..cee74e1 100644
> --- a/lib/Makefile
> +++ b/lib/Makefile
> @@ -37,6 +37,7 @@ obj-$(CONFIG_PLIST) += plist.o
> obj-$(CONFIG_DEBUG_PREEMPT) += smp_processor_id.o
> obj-$(CONFIG_DEBUG_LIST) += list_debug.o
> obj-$(CONFIG_DEBUG_OBJECTS) += debugobjects.o
> +obj-$(CONFIG_HAVE_CLOCKLIB) += clocklib.o
>
> ifneq ($(CONFIG_HAVE_DEC_LOCK),y)
> lib-y += dec_and_lock.o
> diff --git a/lib/clocklib.c b/lib/clocklib.c
> new file mode 100644
> index 0000000..590a665
> --- /dev/null
> +++ b/lib/clocklib.c
> @@ -0,0 +1,353 @@
> +/*
> + * Generic clocks API implementation
> + *
> + * Copyright (c) 2008 Dmitry Baryshkov
> + *
> + * This file is released under the GPL v2.
> + */
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/clk.h>
> +#include <linux/clocklib.h>
> +#include <linux/spinlock.h>
> +
> +static LIST_HEAD(clocks);
> +static DEFINE_SPINLOCK(clocks_lock);
> +
> +#ifdef CONFIG_DEBUG_FS
> +#include <linux/debugfs.h>
> +#include <linux/seq_file.h>
> +static struct dentry *clkdir;
> +
> +static int clocklib_show(struct seq_file *s, void *data)
> +{
> + struct clk *clk = s->private;
> +
> + BUG_ON(!clk);
> +
> + seq_printf(s, "set_parent=%savailable\nusage=%d/%d\nrate=%10lu Hz\n",
> + clk->ops && clk->ops->set_parent ? "not " : "",
> + clk->usage, atomic_read(&clk->ref.refcount),
> + clk_get_rate(clk));
> +// if (clk->ops && clk->ops->format)
> +// clk->ops->format(clk, s);
What about those?
> +
> + return 0;
> +}
> +
> +static int clocklib_open(struct inode *inode, struct file *file)
> +{
> + return single_open(file, clocklib_show, inode->i_private);
> +}
> +
> +static struct file_operations clocklib_operations = {
> + .open = clocklib_open,
> + .read = seq_read,
> + .llseek = seq_lseek,
> + .release = single_release,
> +};
> +
> +static int clk_debugfs_init(struct clk *clk)
> +{
> + struct dentry *dir;
> + struct dentry *info;
> +
> + if (!clkdir)
> + dump_stack();
> +
> + dir = debugfs_create_dir(clk->name,
> + clk->parent ? clk->parent->dir : clkdir);
> +
> + if (IS_ERR(dir))
> + return PTR_ERR(dir);
> +
> + info = debugfs_create_file("info", S_IFREG | S_IRUGO,
> + dir, clk, &clocklib_operations);
> +
> + if (IS_ERR(info)) {
> + debugfs_remove(dir);
> + return PTR_ERR(info);
> + }
> +
> + clk->dir = dir;
> + clk->info = info;
> +
> + return 0;
> +}
> +
> +static void clk_debugfs_clean(struct clk *clk)
> +{
> + if (clk->info)
> + debugfs_remove(clk->info);
> + clk->info = NULL;
> +
> + if (clk->dir)
> + debugfs_remove(clk->dir);
> + clk->dir = NULL;
> +}
> +
> +static void clk_debugfs_reparent(struct clk *clk, struct clk *old, struct clk *new)
> +{
> + struct dentry *oldd = old ? old->dir : clkdir;
> + struct dentry *newd = new ? new->dir : clkdir;
> + struct dentry *dir = debugfs_rename(oldd, clk->dir, newd, clk->name);
> +
> + if (IS_ERR(dir))
> + WARN_ON(1);
> + else
> + clk->dir = dir;
> +}
> +
> +static int __init clocklib_debugfs_init(void)
> +{
> + clkdir = debugfs_create_dir("clocks", NULL);
> + return 0;
> +}
> +core_initcall(clocklib_debugfs_init);
> +#else
> +#define clk_debugfs_init(clk) ({0;})
> +#define clk_debugfs_clean(clk) do {} while (0);
> +#define clk_debugfs_reparent(clk, old, new) do {} while (0);
> +#endif
> +
> +static int clk_can_get_def(struct clk *clk, struct device *dev)
> +{
> + return 1;
> +}
> +
> +static unsigned long clk_get_rate_def(struct clk *clk)
> +{
> + return 0;
> +}
> +
> +static long clk_round_rate_def(struct clk *clk, unsigned long hz, bool apply)
> +{
> + long rate = clk->ops->get_rate(clk);
> +
> + if (apply && hz != rate)
> + return -EINVAL;
> +
> + return rate;
> +}
> +
> +static void clk_release(struct kref *ref)
> +{
> + struct clk *clk = container_of(ref, struct clk, ref);
> +
> + BUG_ON(!clk->release);
> +
> + if (clk->parent)
> + kref_get(&clk->parent->ref);
> +
> + clk->release(clk);
> +}
> +EXPORT_SYMBOL(clk_round_rate);
> +
> +struct clk* clk_get_parent(struct clk *clk)
> +{
> + struct clk *parent;
> +
> + spin_lock(clk->lock);
> +
> + parent = clk->parent;
> + kref_get(&parent->ref);
> +
> + spin_unlock(clk->lock);
> +
> + return parent;
> +}
> +EXPORT_SYMBOL(clk_get_parent);
> +
> +int clk_set_parent(struct clk *clk, struct clk *parent)
> +{
> + int rc = -EINVAL;
> + struct clk *old;
> +
> + spin_lock(clk->lock);
> +
> + if (!clk->ops->set_parent)
> + goto out;
> +
> + old = clk->parent;
> +
> + rc = clk->ops->set_parent(clk, parent);
> + if (rc)
> + goto out;
> +
> + kref_get(&parent->ref);
> + clk->parent = parent;
> +
> + clk_debugfs_reparent(clk, old, parent);
> +
> + kref_put(&old->ref, clk_release);
> +
> +out:
> + spin_unlock(clk->lock);
> +
> + return rc;
> +}
> +EXPORT_SYMBOL(clk_set_parent);
> +
> +int clk_register(struct clk *clk)
> +{
> + int rc;
> +
> + BUG_ON(!clk->ops);
> + BUG_ON(!clk->ops->enable || !clk->ops->disable);
> +
> + if (!clk->ops->can_get)
> + clk->ops->can_get = clk_can_get_def;
> + if (!clk->ops->get_rate)
> + clk->ops->get_rate = clk_get_rate_def;
> + if (!clk->ops->round_rate)
> + clk->ops->round_rate = clk_round_rate_def;
> +
> + kref_init(&clk->ref);
> +
> + spin_lock(&clocks_lock);
> + if (clk->parent)
> + kref_get(&clk->parent->ref);
> + list_add_tail(&clk->node, &clocks);
> +
> + rc = clk_debugfs_init(clk);
> + if (rc) {
> + list_del(&clk->node);
> + kref_put(&clk->ref, clk_release);
> + }
> +
> + spin_unlock(&clocks_lock);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL(clk_register);
> +
> +void clk_unregister(struct clk *clk)
> +{
> + spin_lock(&clocks_lock);
> + clk_debugfs_clean(clk);
> + list_del(&clk->node);
> + kref_put(&clk->ref, clk_release);
> + spin_unlock(&clocks_lock);
> +}
> +EXPORT_SYMBOL(clk_unregister);
> +
> +int clks_register(struct clk **clk, size_t num)
> +{
> + int i;
> + int rc;
> + for (i = 0; i < num; i++) {
> + rc = clk_register(clk[i]);
> + if (rc)
> + return rc;
> + }
> +
> + return 0;
> +}
> +EXPORT_SYMBOL(clks_register);
> +
> +void clks_unregister(struct clk **clk, size_t num)
> +{
> + int i;
> + for (i = 0; i < num; i++)
> + clk_unregister(clk[i]);
> +}
> +EXPORT_SYMBOL(clks_unregister);
> +
> +struct clk *clk_get(struct device *dev, const char *id)
> +{
> + struct clk *clk = NULL, *p;
> +
> + spin_lock(&clocks_lock);
> + list_for_each_entry(p, &clocks, node)
> + if (strcmp(id, p->name) == 0 && p->ops->can_get(p, dev)) {
> + clk = p;
> + kref_get(&clk->ref);
> + break;
> + }
> +
> + spin_unlock(&clocks_lock);
> +
> + return clk;
> +}
> +EXPORT_SYMBOL(clk_get);
> +
> +void clk_put(struct clk *clk)
> +{
> + kref_put(&clk->ref, clk_release);
> +}
> +EXPORT_SYMBOL(clk_put);
> +
> +int clk_enable(struct clk *clk)
> +{
> + int rc = 0;
> +
> + spin_lock(clk->lock);
> +
> + clk->usage++;
> + if (clk->usage == 1)
> + rc = clk->ops->enable(clk);
> +
> + if (rc)
> + clk->usage--;
> +
> + spin_unlock(clk->lock);
> +
> + return rc;
> +}
> +EXPORT_SYMBOL(clk_enable);
> +
> +void clk_disable(struct clk *clk)
> +{
> + spin_lock(clk->lock);
> +
> + WARN_ON(clk->usage <= 0);
> +
> + clk->usage--;
> + if (clk->usage == 0)
> + clk->ops->disable(clk);
> +
> + spin_unlock(clk->lock);
> +}
> +EXPORT_SYMBOL(clk_disable);
> +
> +unsigned long clk_get_rate(struct clk *clk)
> +{
> + unsigned long hz;
> +
> + spin_lock(clk->lock);
> +
> + hz = clk->ops->get_rate(clk);
> +
> + spin_unlock(clk->lock);
> +
> + return hz;
> +}
> +EXPORT_SYMBOL(clk_get_rate);
> +
> +int clk_set_rate(struct clk *clk, unsigned long hz)
> +{
> + long rc;
> +
> + spin_lock(clk->lock);
> +
> + rc = clk->ops->round_rate(clk, hz, 1);
> +
> + spin_unlock(clk->lock);
> +
> + return rc < 0 ? rc : 0;
> +}
> +EXPORT_SYMBOL(clk_set_rate);
> +
> +long clk_round_rate(struct clk *clk, unsigned long hz)
> +{
> + long rc;
> +
> + spin_lock(clk->lock);
> +
> + rc = clk->ops->round_rate(clk, hz, 0);
> +
> + spin_unlock(clk->lock);
> +
> + return rc;
> +}
> +
> --
> 1.5.5.4
>
>
> --
> With best wishes
> Dmitry
>
>
regards
Philipp
On Thu, Jun 26, 2008 at 2:51 PM, Dmitry Baryshkov <[email protected]> wrote:
> Make PXA use clocklib for clocks support.
>
> Signed-off-by: Dmitry Baryshkov <[email protected]>
> ---
> arch/arm/Kconfig | 1 +
> arch/arm/mach-pxa/clock.c | 157 ++++++++++++++++++--------------------------
> arch/arm/mach-pxa/clock.h | 89 ++++++++++++++++---------
> arch/arm/mach-pxa/pxa25x.c | 69 ++++++++++++-------
> arch/arm/mach-pxa/pxa27x.c | 78 +++++++++++++---------
> arch/arm/mach-pxa/pxa3xx.c | 154 ++++++++++++++++++++++++++----------------
> 6 files changed, 307 insertions(+), 241 deletions(-)
>
> diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
> index fe1455e..e7d0343 100644
> --- a/arch/arm/Kconfig
> +++ b/arch/arm/Kconfig
> @@ -393,6 +393,7 @@ config ARCH_PXA
> select GENERIC_TIME
> select GENERIC_CLOCKEVENTS
> select TICK_ONESHOT
> + select HAVE_CLOCKLIB
> 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 180c8bb..f326b80 100644
> --- a/arch/arm/mach-pxa/clock.c
> +++ b/arch/arm/mach-pxa/clock.c
> @@ -3,152 +3,121 @@
> */
> #include <linux/module.h>
> #include <linux/kernel.h>
> -#include <linux/list.h>
> -#include <linux/errno.h>
> -#include <linux/err.h>
> -#include <linux/string.h>
> #include <linux/clk.h>
> -#include <linux/spinlock.h>
> -#include <linux/platform_device.h>
> +#include <linux/clocklib.h>
> #include <linux/delay.h>
>
> #include <asm/arch/pxa2xx-regs.h>
> #include <asm/arch/pxa2xx-gpio.h>
> #include <asm/hardware.h>
>
> -#include "devices.h"
> -#include "generic.h"
> +//#include "devices.h"
> +//#include "generic.h"
And again.
> #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_enable(struct clk *clk)
> {
> - struct clk *p;
> -
> - list_for_each_entry(p, &clocks, node)
> - if (strcmp(id, p->name) == 0 && p->dev == dev)
> - return p;
> -
> - return NULL;
> + pxa_gpio_mode(GPIO11_3_6MHz_MD);
> + return 0;
> }
>
> -struct clk *clk_get(struct device *dev, const char *id)
> +static void clk_gpio11_disable(struct clk *clk)
> {
> - struct clk *p, *clk = ERR_PTR(-ENOENT);
> -
> - mutex_lock(&clocks_mutex);
> - p = clk_lookup(dev, id);
> - if (!p)
> - p = clk_lookup(NULL, id);
> - if (p)
> - clk = p;
> - mutex_unlock(&clocks_mutex);
> -
> - return clk;
> + /* do nothing */
> }
> -EXPORT_SYMBOL(clk_get);
>
> -void clk_put(struct clk *clk)
> +static unsigned long clk_gpio11_get_rate(struct clk *clk)
> {
> + return 3686400;
> }
> -EXPORT_SYMBOL(clk_put);
>
> -int clk_enable(struct clk *clk)
> +static struct clk_ops clk_gpio11_ops = {
> + .enable = clk_gpio11_enable,
> + .disable = clk_gpio11_disable,
> + .get_rate = clk_gpio11_get_rate,
> +};
> +
> +static struct clk clk_gpio11 = {
> + .name = "GPIO27_CLK",
> + .ops = &clk_gpio11_ops,
> + .release = clk_static_release,
> +};
> +
> +int clk_cken_enable(struct clk *clk)
> {
> - unsigned long flags;
> + struct clk_cken *priv = container_of(clk, struct clk_cken, clk);
>
> - spin_lock_irqsave(&clocks_lock, flags);
> - if (clk->enabled++ == 0)
> - clk->ops->enable(clk);
> - spin_unlock_irqrestore(&clocks_lock, flags);
> + CKEN |= 1 << priv->cken;
>
> - if (clk->delay)
> - udelay(clk->delay);
> + if (priv->delay)
> + udelay(priv->delay);
>
> return 0;
> }
> -EXPORT_SYMBOL(clk_enable);
>
> -void clk_disable(struct clk *clk)
> +void clk_cken_disable(struct clk *clk)
> {
> - unsigned long flags;
> + struct clk_cken *priv = container_of(clk, struct clk_cken, clk);
>
> - WARN_ON(clk->enabled == 0);
> -
> - spin_lock_irqsave(&clocks_lock, flags);
> - if (--clk->enabled == 0)
> - clk->ops->disable(clk);
> - spin_unlock_irqrestore(&clocks_lock, flags);
> + CKEN &= ~(1 << priv->cken);
> }
> -EXPORT_SYMBOL(clk_disable);
>
> -unsigned long clk_get_rate(struct clk *clk)
> +unsigned long clk_cken_get_rate(struct clk *clk)
> {
> - unsigned long rate;
> + struct clk_cken *priv = container_of(clk, struct clk_cken, clk);
>
> - rate = clk->rate;
> - if (clk->ops->getrate)
> - rate = clk->ops->getrate(clk);
> + return priv->rate;
>
> - return rate;
> }
> -EXPORT_SYMBOL(clk_get_rate);
>
> +struct clk_ops clk_cken_ops = {
> + .enable = clk_cken_enable,
> + .disable = clk_cken_disable,
> + .get_rate = clk_cken_get_rate,
> +};
>
> -static void clk_gpio27_enable(struct clk *clk)
> +static inline int clk_enable_parent(struct clk *clk)
> {
> - pxa_gpio_mode(GPIO11_3_6MHz_MD);
> + BUG_ON(!clk->parent);
> + return clk_enable(clk->parent);
> }
>
> -static void clk_gpio27_disable(struct clk *clk)
> +static inline void clk_disable_parent(struct clk *clk)
> {
> + BUG_ON(!clk->parent);
> + clk_disable(clk->parent);
> }
>
> -static const struct clkops clk_gpio27_ops = {
> - .enable = clk_gpio27_enable,
> - .disable = clk_gpio27_disable,
> -};
> -
> -
> -void clk_cken_enable(struct clk *clk)
> +static inline unsigned long clk_get_rate_parent(struct clk *clk)
> {
> - CKEN |= 1 << clk->cken;
> + BUG_ON(!clk->parent);
> + return clk_get_rate(clk->parent);
> }
>
> -void clk_cken_disable(struct clk *clk)
> +static inline long clk_round_rate_parent(struct clk *clk, unsigned long hz, bool apply)
> {
> - CKEN &= ~(1 << clk->cken);
> + BUG_ON(!clk->parent);
> + return apply ? clk_set_rate(clk->parent, hz) :
> + clk_round_rate(clk->parent, hz);
> }
>
> -const struct clkops clk_cken_ops = {
> - .enable = clk_cken_enable,
> - .disable = clk_cken_disable,
> -};
> -
> -static struct clk common_clks[] = {
> - {
> - .name = "GPIO27_CLK",
> - .ops = &clk_gpio27_ops,
> - .rate = 3686400,
> - },
> -};
> -
> -void clks_register(struct clk *clks, size_t num)
> +static inline int clk_devck_can_get(struct clk *clk, struct device *dev)
> {
> - int i;
> + struct clk_devck *dc = container_of(clk, struct clk_devck, clk);
>
> - mutex_lock(&clocks_mutex);
> - for (i = 0; i < num; i++)
> - list_add(&clks[i].node, &clocks);
> - mutex_unlock(&clocks_mutex);
> + return dc->dev == dev;
> }
>
> +struct clk_ops clk_devck_ops = {
> + .can_get = clk_devck_can_get,
> + .enable = clk_enable_parent,
> + .disable = clk_disable_parent,
> + .get_rate = clk_get_rate_parent,
> + .round_rate = clk_round_rate_parent,
> +};
> +
> static int __init clk_init(void)
> {
> - clks_register(common_clks, ARRAY_SIZE(common_clks));
> - return 0;
> + return clk_register(&clk_gpio11);
> }
> arch_initcall(clk_init);
> diff --git a/arch/arm/mach-pxa/clock.h b/arch/arm/mach-pxa/clock.h
> index bc6b77e..b787bbe 100644
> --- a/arch/arm/mach-pxa/clock.h
> +++ b/arch/arm/mach-pxa/clock.h
> @@ -1,43 +1,70 @@
> -struct clk;
> +#include <linux/clk.h>
> +#include <linux/clocklib.h>
>
> -struct clkops {
> - void (*enable)(struct clk *);
> - void (*disable)(struct clk *);
> - unsigned long (*getrate)(struct clk *);
> +struct clk_devck {
> + struct clk clk;
> + const char *parent;
> + struct device *dev;
> };
>
> -struct clk {
> - struct list_head node;
> - const char *name;
> - struct device *dev;
> - const struct clkops *ops;
> +extern struct clk_ops clk_devck_ops;
> +
> +struct clk_cken {
> + struct clk clk;
> unsigned long rate;
> unsigned int cken;
> unsigned int delay;
> - unsigned int enabled;
> };
>
> -#define INIT_CKEN(_name, _cken, _rate, _delay, _dev) \
> - { \
> - .name = _name, \
> - .dev = _dev, \
> - .ops = &clk_cken_ops, \
> - .rate = _rate, \
> - .cken = CKEN_##_cken, \
> - .delay = _delay, \
> - }
> +int clk_cken_enable(struct clk *clk);
> +void clk_cken_disable(struct clk *clk);
> +unsigned long clk_cken_get_rate(struct clk *clk);
>
> -#define INIT_CK(_name, _cken, _ops, _dev) \
> - { \
> - .name = _name, \
> - .dev = _dev, \
> - .ops = _ops, \
> - .cken = CKEN_##_cken, \
> - }
> +extern struct clk_ops clk_cken_ops;
>
> -extern const struct clkops clk_cken_ops;
> +static inline void clk_static_release(struct clk *clk)
> +{
> + printk(KERN_ERR "Can't release static clock: %s!!!\n", clk->name);
> + BUG();
> +}
>
> -void clk_cken_enable(struct clk *clk);
> -void clk_cken_disable(struct clk *clk);
> +#define INIT_CKEN(_name, _cken, _rate, _delay) \
> + &(struct clk_cken) { \
> + .clk.name = _name, \
> + .clk.ops = &clk_cken_ops, \
> + .clk.release = clk_static_release, \
> + .cken = CKEN_##_cken, \
> + .rate = _rate, \
> + .delay = _delay, \
> + } .clk
>
> -void clks_register(struct clk *clks, size_t num);
> +#define INIT_CK(_name, _cken, _ops) \
> + &(struct clk_cken) { \
> + .clk.name = _name, \
> + .clk.ops = _ops, \
> + .clk.release = clk_static_release, \
> + .cken = CKEN_##_cken, \
> + } .clk
> +
> +#define INIT_DEVCK(_parent_name, _name, _dev) \
> + { \
> + .clk.name = _name, \
> + .clk.ops = &clk_devck_ops, \
> + .clk.release = clk_static_release, \
> + .parent = _parent_name, \
> + .dev = _dev, \
> + }
> +
> +static inline void clks_devck_register(struct clk_devck *clks, int size)
> +{
> + int i;
> + for (i = 0; i < size; i++) {
> + struct clk *parent = clk_get(NULL, clks[i].parent);
> + WARN_ON(!parent);
> + clks[i].clk.parent = parent;
> + if (parent) {
> + clk_register(&clks[i].clk);
> + clk_put(parent);
> + }
> + }
> +}
> diff --git a/arch/arm/mach-pxa/pxa25x.c b/arch/arm/mach-pxa/pxa25x.c
> index 3920d15..de7f0b0 100644
> --- a/arch/arm/mach-pxa/pxa25x.c
> +++ b/arch/arm/mach-pxa/pxa25x.c
> @@ -103,10 +103,10 @@ 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 = {
> +static struct clk_ops clk_pxa25x_lcd_ops = {
> .enable = clk_cken_enable,
> .disable = clk_cken_disable,
> - .getrate = clk_pxa25x_lcd_getrate,
> + .get_rate = clk_pxa25x_lcd_getrate,
> };
>
> /*
> @@ -114,31 +114,44 @@ 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),
> - INIT_CKEN("PWMCLK", PWM0, 3686400, 0, &pxa25x_device_pwm0.dev),
> - INIT_CKEN("PWMCLK", PWM1, 3686400, 0, &pxa25x_device_pwm1.dev),
> -
> - INIT_CKEN("AC97CLK", AC97, 24576000, 0, NULL),
> +static struct clk *pxa25x_hwuart_clk =
> + INIT_CKEN("HWUARTCLK", HWUART, 14745600, 1);
> +static struct clk_devck pxa25x_hwuart_devclk =
> + INIT_DEVCK("HWUARTCLK", "UARTCLK", &pxa_device_hwuart.dev);
> +
> +static struct clk *pxa25x_clks[] = {
> + INIT_CK("LCDCLK", LCD, &clk_pxa25x_lcd_ops), /* &pxa_device_fb.dev */
> + INIT_CKEN("FFUARTCLK", FFUART, 14745600, 1),
> + INIT_CKEN("BTUARTCLK", BTUART, 14745600, 1),
> + INIT_CKEN("STUARTCLK", STUART, 14745600, 1),
> + 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("SSP_CLK", SSP, 3686400, 0),
> + INIT_CKEN("NSSPCLK", NSSP, 3686400, 0),
> + INIT_CKEN("ASSPCLK", ASSP, 3686400, 0),
> + INIT_CKEN("PWM0CLK", PWM0, 3686400, 0),
> + INIT_CKEN("PWM1CLK", PWM1, 3686400, 0),
> +
> + INIT_CKEN("AC97CLK", AC97, 24576000, 0),
>
> /*
> - INIT_CKEN("I2SCLK", I2S, 14745600, 0, NULL),
> + INIT_CKEN("I2SCLK", I2S, 14745600, 0),
> */
> - INIT_CKEN("FICPCLK", FICP, 47923000, 0, NULL),
> + INIT_CKEN("FICPCLK", FICP, 47923000, 0),
> +};
> +
> +static struct clk_devck clk_children[] = {
> + INIT_DEVCK("FFUARTCLK", "UARTCLK", &pxa_device_ffuart.dev),
> + INIT_DEVCK("BTUARTCLK", "UARTCLK", &pxa_device_btuart.dev),
> + INIT_DEVCK("STUARTCLK", "UARTCLK", &pxa_device_stuart.dev),
> +
> + INIT_DEVCK("SSP_CLK", "SSPCLK", &pxa25x_device_ssp.dev),
> + INIT_DEVCK("NSSPCLK", "SSPCLK", &pxa25x_device_nssp.dev),
> + INIT_DEVCK("ASSPCLK", "SSPCLK", &pxa25x_device_assp.dev),
> + INIT_DEVCK("PWM0CLK", "PWMCLK", &pxa25x_device_pwm0.dev),
> + INIT_DEVCK("PWM1CLK", "PWMCLK", &pxa25x_device_pwm1.dev),
> };
>
> #ifdef CONFIG_PM
> @@ -287,11 +300,15 @@ 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()) {
> + clk_register(pxa25x_hwuart_clk);
> + pxa25x_hwuart_devclk.clk.parent = pxa25x_hwuart_clk;
> + clk_register(&pxa25x_hwuart_devclk.clk);
> + }
>
> if (cpu_is_pxa21x() || cpu_is_pxa25x()) {
> clks_register(pxa25x_clks, ARRAY_SIZE(pxa25x_clks));
> + clks_devck_register(ARRAY_AND_SIZE(clk_children));
>
> 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 4d7afae..a88be7a 100644
> --- a/arch/arm/mach-pxa/pxa27x.c
> +++ b/arch/arm/mach-pxa/pxa27x.c
> @@ -45,7 +45,7 @@ unsigned int pxa27x_get_clk_frequency_khz(int info)
> {
> unsigned long ccsr, clkcfg;
> unsigned int l, L, m, M, n2, N, S;
> - int cccr_a, t, ht, b;
> + int cccr_a, t, ht, b;
>
> ccsr = CCSR;
> cccr_a = CCCR & (1 << 25);
> @@ -88,7 +88,7 @@ unsigned int pxa27x_get_memclk_frequency_10khz(void)
> {
> unsigned long ccsr, clkcfg;
> unsigned int l, L, m, M;
> - int cccr_a, b;
> + int cccr_a, b;
>
> ccsr = CCSR;
> cccr_a = CCCR & (1 << 25);
> @@ -130,48 +130,63 @@ 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 = {
> +static struct clk_ops clk_pxa27x_lcd_ops = {
> .enable = clk_cken_enable,
> .disable = clk_cken_disable,
> - .getrate = clk_pxa27x_lcd_getrate,
> + .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), /* &pxa_device_fb.dev */
> + 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), /* &pxa_device_i2s.dev */
> + INIT_CKEN("I2C_CLK", I2C, 32842000, 0),
> + 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("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, &pxa27x_device_keypad.dev),
> + INIT_CKEN("USBCLK", USBHOST, 48000000, 0), /* &pxa27x_device_ohci.dev */
> + INIT_CKEN("PWRI2CCLK", PWRI2C, 13000000, 0),
> + INIT_CKEN("KBDCLK", KEYPAD, 32768, 0), /* &pxa27x_device_keypad.dev */
>
> - 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("PWMCLK", PWM0, 13000000, 0, &pxa27x_device_pwm0.dev),
> - INIT_CKEN("PWMCLK", PWM1, 13000000, 0, &pxa27x_device_pwm1.dev),
> + INIT_CKEN("SSP1CLK", SSP1, 13000000, 0),
> + INIT_CKEN("SSP2CLK", SSP2, 13000000, 0),
> + INIT_CKEN("SSP3CLK", SSP3, 13000000, 0),
> + INIT_CKEN("PWM0CLK", PWM0, 13000000, 0),
> + INIT_CKEN("PWM1CLK", PWM1, 13000000, 0),
>
> - INIT_CKEN("AC97CLK", AC97, 24576000, 0, NULL),
> - INIT_CKEN("AC97CONFCLK", AC97CONF, 24576000, 0, NULL),
> + INIT_CKEN("AC97CLK", AC97, 24576000, 0),
> + INIT_CKEN("AC97CONFCLK", AC97CONF, 24576000, 0),
>
> /*
> - 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("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_devck clk_children[] = {
> + INIT_DEVCK("FFUARTCLK", "UARTCLK", &pxa_device_ffuart.dev),
> + INIT_DEVCK("BTUARTCLK", "UARTCLK", &pxa_device_btuart.dev),
> + INIT_DEVCK("STUARTCLK", "UARTCLK", &pxa_device_stuart.dev),
> +
> + INIT_DEVCK("I2C_CLK", "I2CCLK", &pxa_device_i2c.dev),
> + INIT_DEVCK("PWRI2CCLK", "I2CCLK", &pxa27x_device_i2c_power.dev),
> +
> + INIT_DEVCK("SSP1CLK", "SSPCLK", &pxa27x_device_ssp1.dev),
> + INIT_DEVCK("SSP2CLK", "SSPCLK", &pxa27x_device_ssp2.dev),
> + INIT_DEVCK("SSP3CLK", "SSPCLK", &pxa27x_device_ssp3.dev),
> + INIT_DEVCK("PWM0CLK", "PWMCLK", &pxa27x_device_pwm0.dev),
> + INIT_DEVCK("PWM1CLK", "PWMCLK", &pxa27x_device_pwm1.dev),
> +};
> +
> #ifdef CONFIG_PM
>
> #define SAVE(x) sleep_save[SLEEP_SAVE_##x] = x
> @@ -384,7 +399,8 @@ static int __init pxa27x_init(void)
> int i, ret = 0;
>
> if (cpu_is_pxa27x()) {
> - clks_register(pxa27x_clks, ARRAY_SIZE(pxa27x_clks));
> + clks_register(ARRAY_AND_SIZE(pxa27x_clks));
> + clks_devck_register(ARRAY_AND_SIZE(clk_children));
>
> 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 d26a9b0..ad78a05 100644
> --- a/arch/arm/mach-pxa/pxa3xx.c
> +++ b/arch/arm/mach-pxa/pxa3xx.c
> @@ -21,6 +21,7 @@
> #include <linux/irq.h>
> #include <linux/io.h>
> #include <linux/sysdev.h>
> +#include <linux/delay.h>
>
> #include <asm/hardware.h>
> #include <asm/arch/pxa3xx-regs.h>
> @@ -144,46 +145,58 @@ 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_enable(struct clk *clk)
> {
> - unsigned long mask = 1ul << (clk->cken & 0x1f);
> + struct clk_cken *priv = container_of(clk, struct clk_cken, clk);
> + unsigned long mask = 1ul << (priv->cken & 0x1f);
>
> - if (clk->cken < 32)
> + if (priv->cken < 32)
> CKENA |= mask;
> else
> CKENB |= mask;
> +
> + if (priv->delay)
> + udelay(priv->delay);
> +
> + return 0;
> }
>
> static void clk_pxa3xx_cken_disable(struct clk *clk)
> {
> - unsigned long mask = 1ul << (clk->cken & 0x1f);
> + struct clk_cken *priv = container_of(clk, struct clk_cken, clk);
> + unsigned long mask = 1ul << (priv->cken & 0x1f);
>
> - if (clk->cken < 32)
> + if (priv->cken < 32)
> CKENA &= ~mask;
> else
> CKENB &= ~mask;
> }
>
> -static const struct clkops clk_pxa3xx_cken_ops = {
> +static struct clk_ops clk_pxa3xx_cken_ops = {
> .enable = clk_pxa3xx_cken_enable,
> .disable = clk_pxa3xx_cken_disable,
> + .get_rate = clk_cken_get_rate,
> };
>
> -static const struct clkops clk_pxa3xx_hsio_ops = {
> +static struct clk_ops clk_pxa3xx_hsio_ops = {
> .enable = clk_pxa3xx_cken_enable,
> .disable = clk_pxa3xx_cken_disable,
> - .getrate = clk_pxa3xx_hsio_getrate,
> + .get_rate = clk_pxa3xx_hsio_getrate,
> };
>
> -static const struct clkops clk_pxa3xx_ac97_ops = {
> +static struct clk_ops clk_pxa3xx_ac97_ops = {
> .enable = clk_pxa3xx_cken_enable,
> .disable = clk_pxa3xx_cken_disable,
> - .getrate = clk_pxa3xx_ac97_getrate,
> + .get_rate = clk_pxa3xx_ac97_getrate,
> };
>
> -static void clk_pout_enable(struct clk *clk)
> +static int clk_pout_enable(struct clk *clk)
> {
> - OSCC |= OSCC_PEN;
> + OSCC &= ~OSCC_PEN;
> +
> + udelay(70);
> +
> + return 0;
> }
>
> static void clk_pout_disable(struct clk *clk)
> @@ -191,60 +204,82 @@ static void clk_pout_disable(struct clk *clk)
> OSCC &= ~OSCC_PEN;
> }
>
> -static const struct clkops clk_pout_ops = {
> +static unsigned long clk_pout_get_rate(struct clk *clk)
> +{
> + return 13000000;
> +}
> +
> +static struct clk_ops clk_pout_ops = {
> .enable = clk_pout_enable,
> .disable = clk_pout_disable,
> + .get_rate = clk_pout_get_rate,
> };
>
> -#define PXA3xx_CKEN(_name, _cken, _rate, _delay, _dev) \
> - { \
> - .name = _name, \
> - .dev = _dev, \
> - .ops = &clk_pxa3xx_cken_ops, \
> - .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[] = {
> - {
> +#define PXA3xx_CKEN(_name, _cken, _rate, _delay) \
> + &(struct clk_cken) { \
> + .clk.name = _name, \
> + .clk.ops = &clk_pxa3xx_cken_ops, \
> + .clk.release = clk_static_release, \
> + .cken = CKEN_##_cken, \
> + .rate = _rate, \
> + .delay = _delay, \
> + }.clk
> +
> +#define PXA3xx_CK(_name, _cken, _ops) \
> + &(struct clk_cken) { \
> + .clk.name = _name, \
> + .clk.ops = _ops, \
> + .clk.release = clk_static_release, \
> + .cken = CKEN_##_cken, \
> + }.clk
> +
> +static struct clk *pxa3xx_clks[] = {
> + &(struct clk) {
> .name = "CLK_POUT",
> .ops = &clk_pout_ops,
> - .rate = 13000000,
> - .delay = 70,
> + .release = clk_static_release,
> },
>
> - PXA3xx_CK("LCDCLK", LCD, &clk_pxa3xx_hsio_ops, &pxa_device_fb.dev),
> - PXA3xx_CK("CAMCLK", CAMERA, &clk_pxa3xx_hsio_ops, NULL),
> - PXA3xx_CK("AC97CLK", AC97, &clk_pxa3xx_ac97_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("KBDCLK", KEYPAD, 32768, 0, &pxa27x_device_keypad.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),
> - PXA3xx_CKEN("PWMCLK", PWM0, 13000000, 0, &pxa27x_device_pwm0.dev),
> - PXA3xx_CKEN("PWMCLK", PWM1, 13000000, 0, &pxa27x_device_pwm1.dev),
> -
> - 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),
> + PXA3xx_CK("LCDCLK", LCD, &clk_pxa3xx_hsio_ops), /* &pxa_device_fb.dev */
> + PXA3xx_CK("CAMCLK", CAMERA, &clk_pxa3xx_hsio_ops),
> + PXA3xx_CK("AC97CLK", AC97, &clk_pxa3xx_ac97_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), /* &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("KBDCLK", KEYPAD, 32768, 0), /* &pxa27x_device_keypad.dev */
> +
> + 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("PWM0CLK", PWM0, 13000000, 0),
> + PXA3xx_CKEN("PWM1CLK", PWM1, 13000000, 0),
> +
> + PXA3xx_CKEN("MMC1CLK", MMC1, 19500000, 0),
> + PXA3xx_CKEN("MMC2CLK", MMC2, 19500000, 0),
> + PXA3xx_CKEN("MMC3CLK", MMC3, 19500000, 0),
> +};
> +
> +static struct clk_devck clk_children[] = {
> + INIT_DEVCK("FFUARTCLK", "UARTCLK", &pxa_device_ffuart.dev),
> + INIT_DEVCK("BTUARTCLK", "UARTCLK", &pxa_device_btuart.dev),
> + INIT_DEVCK("STUARTCLK", "UARTCLK", &pxa_device_stuart.dev),
> +
> + INIT_DEVCK("SSP1CLK", "SSPCLK", &pxa27x_device_ssp1.dev),
> + INIT_DEVCK("SSP2CLK", "SSPCLK", &pxa27x_device_ssp2.dev),
> + INIT_DEVCK("SSP3CLK", "SSPCLK", &pxa27x_device_ssp3.dev),
> + INIT_DEVCK("SSP4CLK", "SSPCLK", &pxa3xx_device_ssp4.dev),
> + INIT_DEVCK("PWM0CLK", "PWMCLK", &pxa27x_device_pwm0.dev),
> + INIT_DEVCK("PWM1CLK", "PWMCLK", &pxa27x_device_pwm1.dev),
> +
> + INIT_DEVCK("MMC1CLK", "MMCCLK", &pxa_device_mci.dev),
> + INIT_DEVCK("MMC2CLK", "MMCCLK", &pxa3xx_device_mci2.dev),
> + INIT_DEVCK("MMC3CLK", "MMCCLK", &pxa3xx_device_mci3.dev),
> };
>
> #ifdef CONFIG_PM
> @@ -559,7 +594,8 @@ static int __init pxa3xx_init(void)
> */
> ASCR &= ~(ASCR_RDH | ASCR_D1S | ASCR_D2S | ASCR_D3S);
>
> - clks_register(pxa3xx_clks, ARRAY_SIZE(pxa3xx_clks));
> + clks_register(ARRAY_AND_SIZE(pxa3xx_clks));
> + clks_devck_register(ARRAY_AND_SIZE(clk_children));
>
> if ((ret = pxa_init_dma(32)))
> return ret;
> --
> 1.5.5.4
>
>
> --
> With best wishes
> Dmitry
>
>
regards
Philipp
On Thu, Jun 26, 2008 at 05:00:26PM +0200, pHilipp Zabel wrote:
> On Thu, Jun 26, 2008 at 2:51 PM, Dmitry Baryshkov <[email protected]> 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.
> >
> > Signed-off-by: Dmitry Baryshkov <[email protected]>
> > ---
> > include/linux/clocklib.h | 58 ++++++++
> > lib/Kconfig | 3 +
> > lib/Makefile | 1 +
> > lib/clocklib.c | 353 ++++++++++++++++++++++++++++++++++++++++++++++
> > 4 files changed, 415 insertions(+), 0 deletions(-)
> > create mode 100644 include/linux/clocklib.h
> > create mode 100644 lib/clocklib.c
> >
> > diff --git a/include/linux/clocklib.h b/include/linux/clocklib.h
> > new file mode 100644
> > index 0000000..cf2b41e
> > --- /dev/null
> > +++ b/include/linux/clocklib.h
> > @@ -0,0 +1,58 @@
> > +/*
> > + * Copyright (C) 2008 Dmitry Baryshkov
> > + *
> > + * This file is released under the GPL v2.
> > + */
> > +
> > +#ifndef CLKLIB_H
> > +#define CLKLIB_H
> > +
> > +#include <linux/spinlock.h>
> > +#include <linux/kref.h>
> > +
> > +struct clk;
> > +
> > +/**
> > + * 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 (*enable)(struct clk *clk);
> > + void (*disable)(struct clk *clk);
> > + unsigned long (*get_rate)(struct clk *clk);
> > + long (*round_rate)(struct clk *clk, unsigned long hz, bool apply);
> > +};
> > +
> > +
> > +struct clk {
> > + struct list_head node;
> > + spinlock_t *lock;
> > + struct kref ref;
> > + int usage;
> > +#ifdef CONFIG_DEBUG_FS
> > + struct dentry *dir;
> > + struct dentry *info;
> > +#endif
> > +
> > + const char *name;
> > + struct clk *parent;
> > + struct clk_ops *ops;
> > + void (*release)(struct clk *clk);
> > +};
> > +
> > +int clk_register(struct clk *clk);
> > +void clk_unregister(struct clk *clk);
> > +int clks_register(struct clk **clk, size_t num);
> > +void clks_unregister(struct clk **clk, size_t num);
> > +
> > +#endif
> > diff --git a/lib/Kconfig b/lib/Kconfig
> > index 8cc8e87..592f5e1 100644
> > --- a/lib/Kconfig
> > +++ b/lib/Kconfig
> > @@ -13,6 +13,9 @@ config GENERIC_FIND_FIRST_BIT
> > config GENERIC_FIND_NEXT_BIT
> > def_bool n
> >
> > +config HAVE_CLOCKLIB
> > + tristate
> > +
> > config CRC_CCITT
> > tristate "CRC-CCITT functions"
> > help
> > diff --git a/lib/Makefile b/lib/Makefile
> > index 74b0cfb..cee74e1 100644
> > --- a/lib/Makefile
> > +++ b/lib/Makefile
> > @@ -37,6 +37,7 @@ obj-$(CONFIG_PLIST) += plist.o
> > obj-$(CONFIG_DEBUG_PREEMPT) += smp_processor_id.o
> > obj-$(CONFIG_DEBUG_LIST) += list_debug.o
> > obj-$(CONFIG_DEBUG_OBJECTS) += debugobjects.o
> > +obj-$(CONFIG_HAVE_CLOCKLIB) += clocklib.o
> >
> > ifneq ($(CONFIG_HAVE_DEC_LOCK),y)
> > lib-y += dec_and_lock.o
> > diff --git a/lib/clocklib.c b/lib/clocklib.c
> > new file mode 100644
> > index 0000000..590a665
> > --- /dev/null
> > +++ b/lib/clocklib.c
> > @@ -0,0 +1,353 @@
> > +/*
> > + * Generic clocks API implementation
> > + *
> > + * Copyright (c) 2008 Dmitry Baryshkov
> > + *
> > + * This file is released under the GPL v2.
> > + */
> > +#include <linux/kernel.h>
> > +#include <linux/module.h>
> > +#include <linux/clk.h>
> > +#include <linux/clocklib.h>
> > +#include <linux/spinlock.h>
> > +
> > +static LIST_HEAD(clocks);
> > +static DEFINE_SPINLOCK(clocks_lock);
> > +
> > +#ifdef CONFIG_DEBUG_FS
> > +#include <linux/debugfs.h>
> > +#include <linux/seq_file.h>
> > +static struct dentry *clkdir;
> > +
> > +static int clocklib_show(struct seq_file *s, void *data)
> > +{
> > + struct clk *clk = s->private;
> > +
> > + BUG_ON(!clk);
> > +
> > + seq_printf(s, "set_parent=%savailable\nusage=%d/%d\nrate=%10lu Hz\n",
> > + clk->ops && clk->ops->set_parent ? "not " : "",
> > + clk->usage, atomic_read(&clk->ref.refcount),
> > + clk_get_rate(clk));
> > +// if (clk->ops && clk->ops->format)
> > +// clk->ops->format(clk, s);
>
> What about those?
That was an idea to be able to supply some additional info from a clock.
For now commented, but can (and probably will) be restored later.
>
> > +
> > + return 0;
> > +}
> > +
> > +static int clocklib_open(struct inode *inode, struct file *file)
> > +{
> > + return single_open(file, clocklib_show, inode->i_private);
> > +}
> > +
> > +static struct file_operations clocklib_operations = {
> > + .open = clocklib_open,
> > + .read = seq_read,
> > + .llseek = seq_lseek,
> > + .release = single_release,
> > +};
> > +
> > +static int clk_debugfs_init(struct clk *clk)
> > +{
> > + struct dentry *dir;
> > + struct dentry *info;
> > +
> > + if (!clkdir)
> > + dump_stack();
> > +
> > + dir = debugfs_create_dir(clk->name,
> > + clk->parent ? clk->parent->dir : clkdir);
> > +
> > + if (IS_ERR(dir))
> > + return PTR_ERR(dir);
> > +
> > + info = debugfs_create_file("info", S_IFREG | S_IRUGO,
> > + dir, clk, &clocklib_operations);
> > +
> > + if (IS_ERR(info)) {
> > + debugfs_remove(dir);
> > + return PTR_ERR(info);
> > + }
> > +
> > + clk->dir = dir;
> > + clk->info = info;
> > +
> > + return 0;
> > +}
> > +
> > +static void clk_debugfs_clean(struct clk *clk)
> > +{
> > + if (clk->info)
> > + debugfs_remove(clk->info);
> > + clk->info = NULL;
> > +
> > + if (clk->dir)
> > + debugfs_remove(clk->dir);
> > + clk->dir = NULL;
> > +}
> > +
> > +static void clk_debugfs_reparent(struct clk *clk, struct clk *old, struct clk *new)
> > +{
> > + struct dentry *oldd = old ? old->dir : clkdir;
> > + struct dentry *newd = new ? new->dir : clkdir;
> > + struct dentry *dir = debugfs_rename(oldd, clk->dir, newd, clk->name);
> > +
> > + if (IS_ERR(dir))
> > + WARN_ON(1);
> > + else
> > + clk->dir = dir;
> > +}
> > +
> > +static int __init clocklib_debugfs_init(void)
> > +{
> > + clkdir = debugfs_create_dir("clocks", NULL);
> > + return 0;
> > +}
> > +core_initcall(clocklib_debugfs_init);
> > +#else
> > +#define clk_debugfs_init(clk) ({0;})
> > +#define clk_debugfs_clean(clk) do {} while (0);
> > +#define clk_debugfs_reparent(clk, old, new) do {} while (0);
> > +#endif
> > +
> > +static int clk_can_get_def(struct clk *clk, struct device *dev)
> > +{
> > + return 1;
> > +}
> > +
> > +static unsigned long clk_get_rate_def(struct clk *clk)
> > +{
> > + return 0;
> > +}
> > +
> > +static long clk_round_rate_def(struct clk *clk, unsigned long hz, bool apply)
> > +{
> > + long rate = clk->ops->get_rate(clk);
> > +
> > + if (apply && hz != rate)
> > + return -EINVAL;
> > +
> > + return rate;
> > +}
> > +
> > +static void clk_release(struct kref *ref)
> > +{
> > + struct clk *clk = container_of(ref, struct clk, ref);
> > +
> > + BUG_ON(!clk->release);
> > +
> > + if (clk->parent)
> > + kref_get(&clk->parent->ref);
> > +
> > + clk->release(clk);
> > +}
> > +EXPORT_SYMBOL(clk_round_rate);
> > +
> > +struct clk* clk_get_parent(struct clk *clk)
> > +{
> > + struct clk *parent;
> > +
> > + spin_lock(clk->lock);
> > +
> > + parent = clk->parent;
> > + kref_get(&parent->ref);
> > +
> > + spin_unlock(clk->lock);
> > +
> > + return parent;
> > +}
> > +EXPORT_SYMBOL(clk_get_parent);
> > +
> > +int clk_set_parent(struct clk *clk, struct clk *parent)
> > +{
> > + int rc = -EINVAL;
> > + struct clk *old;
> > +
> > + spin_lock(clk->lock);
> > +
> > + if (!clk->ops->set_parent)
> > + goto out;
> > +
> > + old = clk->parent;
> > +
> > + rc = clk->ops->set_parent(clk, parent);
> > + if (rc)
> > + goto out;
> > +
> > + kref_get(&parent->ref);
> > + clk->parent = parent;
> > +
> > + clk_debugfs_reparent(clk, old, parent);
> > +
> > + kref_put(&old->ref, clk_release);
> > +
> > +out:
> > + spin_unlock(clk->lock);
> > +
> > + return rc;
> > +}
> > +EXPORT_SYMBOL(clk_set_parent);
> > +
> > +int clk_register(struct clk *clk)
> > +{
> > + int rc;
> > +
> > + BUG_ON(!clk->ops);
> > + BUG_ON(!clk->ops->enable || !clk->ops->disable);
> > +
> > + if (!clk->ops->can_get)
> > + clk->ops->can_get = clk_can_get_def;
> > + if (!clk->ops->get_rate)
> > + clk->ops->get_rate = clk_get_rate_def;
> > + if (!clk->ops->round_rate)
> > + clk->ops->round_rate = clk_round_rate_def;
> > +
> > + kref_init(&clk->ref);
> > +
> > + spin_lock(&clocks_lock);
> > + if (clk->parent)
> > + kref_get(&clk->parent->ref);
> > + list_add_tail(&clk->node, &clocks);
> > +
> > + rc = clk_debugfs_init(clk);
> > + if (rc) {
> > + list_del(&clk->node);
> > + kref_put(&clk->ref, clk_release);
> > + }
> > +
> > + spin_unlock(&clocks_lock);
> > +
> > + return 0;
> > +}
> > +EXPORT_SYMBOL(clk_register);
> > +
> > +void clk_unregister(struct clk *clk)
> > +{
> > + spin_lock(&clocks_lock);
> > + clk_debugfs_clean(clk);
> > + list_del(&clk->node);
> > + kref_put(&clk->ref, clk_release);
> > + spin_unlock(&clocks_lock);
> > +}
> > +EXPORT_SYMBOL(clk_unregister);
> > +
> > +int clks_register(struct clk **clk, size_t num)
> > +{
> > + int i;
> > + int rc;
> > + for (i = 0; i < num; i++) {
> > + rc = clk_register(clk[i]);
> > + if (rc)
> > + return rc;
> > + }
> > +
> > + return 0;
> > +}
> > +EXPORT_SYMBOL(clks_register);
> > +
> > +void clks_unregister(struct clk **clk, size_t num)
> > +{
> > + int i;
> > + for (i = 0; i < num; i++)
> > + clk_unregister(clk[i]);
> > +}
> > +EXPORT_SYMBOL(clks_unregister);
> > +
> > +struct clk *clk_get(struct device *dev, const char *id)
> > +{
> > + struct clk *clk = NULL, *p;
> > +
> > + spin_lock(&clocks_lock);
> > + list_for_each_entry(p, &clocks, node)
> > + if (strcmp(id, p->name) == 0 && p->ops->can_get(p, dev)) {
> > + clk = p;
> > + kref_get(&clk->ref);
> > + break;
> > + }
> > +
> > + spin_unlock(&clocks_lock);
> > +
> > + return clk;
> > +}
> > +EXPORT_SYMBOL(clk_get);
> > +
> > +void clk_put(struct clk *clk)
> > +{
> > + kref_put(&clk->ref, clk_release);
> > +}
> > +EXPORT_SYMBOL(clk_put);
> > +
> > +int clk_enable(struct clk *clk)
> > +{
> > + int rc = 0;
> > +
> > + spin_lock(clk->lock);
> > +
> > + clk->usage++;
> > + if (clk->usage == 1)
> > + rc = clk->ops->enable(clk);
> > +
> > + if (rc)
> > + clk->usage--;
> > +
> > + spin_unlock(clk->lock);
> > +
> > + return rc;
> > +}
> > +EXPORT_SYMBOL(clk_enable);
> > +
> > +void clk_disable(struct clk *clk)
> > +{
> > + spin_lock(clk->lock);
> > +
> > + WARN_ON(clk->usage <= 0);
> > +
> > + clk->usage--;
> > + if (clk->usage == 0)
> > + clk->ops->disable(clk);
> > +
> > + spin_unlock(clk->lock);
> > +}
> > +EXPORT_SYMBOL(clk_disable);
> > +
> > +unsigned long clk_get_rate(struct clk *clk)
> > +{
> > + unsigned long hz;
> > +
> > + spin_lock(clk->lock);
> > +
> > + hz = clk->ops->get_rate(clk);
> > +
> > + spin_unlock(clk->lock);
> > +
> > + return hz;
> > +}
> > +EXPORT_SYMBOL(clk_get_rate);
> > +
> > +int clk_set_rate(struct clk *clk, unsigned long hz)
> > +{
> > + long rc;
> > +
> > + spin_lock(clk->lock);
> > +
> > + rc = clk->ops->round_rate(clk, hz, 1);
> > +
> > + spin_unlock(clk->lock);
> > +
> > + return rc < 0 ? rc : 0;
> > +}
> > +EXPORT_SYMBOL(clk_set_rate);
> > +
> > +long clk_round_rate(struct clk *clk, unsigned long hz)
> > +{
> > + long rc;
> > +
> > + spin_lock(clk->lock);
> > +
> > + rc = clk->ops->round_rate(clk, hz, 0);
> > +
> > + spin_unlock(clk->lock);
> > +
> > + return rc;
> > +}
> > +
> > --
> > 1.5.5.4
> >
> >
> > --
> > With best wishes
> > Dmitry
> >
> >
>
> regards
> Philipp
--
With best wishes
Dmitry
On Thu, Jun 26, 2008 at 04:50:33PM +0400, Dmitry Baryshkov wrote:
> Hi,
>
> This is again a set of patches to unify the management of clocks and
> allow easy registration and unregistration of them. This is neccessary
> to cleanly support such devices as toshiba mobile companion chips,
> sa1111 companion, etc. Also it brings code unification, especially for a
> lot of arm sub-arches which share nearly the same code, etc.
>
> This is the "version 3" approach. Given the negative response to
> kobjects, I've redesigned it to use plain krefs.
>
> Debugfs support is merged into main clocklib patch. Documentation
> for it's interface will come later. For now it provides tree structure
> with single file per each clock directory.
I'd prefer not to see this called clocklib, it isn't of any use
outside of the kernel, and once it is in there's little point in
anyone not using it.
--
Ben ([email protected], http://www.fluff.org/)
'a smiley only costs 4 bytes'
On Thu, Jun 26, 2008 at 04:51:38PM +0400, Dmitry Baryshkov 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.
>
> Signed-off-by: Dmitry Baryshkov <[email protected]>
> ---
> include/linux/clocklib.h | 58 ++++++++
> lib/Kconfig | 3 +
> lib/Makefile | 1 +
> lib/clocklib.c | 353 ++++++++++++++++++++++++++++++++++++++++++++++
why under lib? drivers/clk might be a better place for this.
> 4 files changed, 415 insertions(+), 0 deletions(-)
> create mode 100644 include/linux/clocklib.h
> create mode 100644 lib/clocklib.c
>
> diff --git a/include/linux/clocklib.h b/include/linux/clocklib.h
> new file mode 100644
> index 0000000..cf2b41e
> --- /dev/null
> +++ b/include/linux/clocklib.h
> @@ -0,0 +1,58 @@
> +/*
> + * Copyright (C) 2008 Dmitry Baryshkov
> + *
> + * This file is released under the GPL v2.
> + */
> +
> +#ifndef CLKLIB_H
> +#define CLKLIB_H
> +
> +#include <linux/spinlock.h>
> +#include <linux/kref.h>
> +
> +struct clk;
> +
> +/**
> + * 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 (*enable)(struct clk *clk);
> + void (*disable)(struct clk *clk);
> + unsigned long (*get_rate)(struct clk *clk);
> + long (*round_rate)(struct clk *clk, unsigned long hz, bool apply);
> +};
I'd much prefer to see the following changes:
1) enable and disable merged into one, and pass an enable
parameter, ie:
int (*set_enable)(struct clk *clk, bool on);
as most code I see is of the following:
int myclk_enable(struct clk *clk, bool on)
{
clkbit = bit_for_clk(clk);
reg = read_reg(reg_for_clk(clk));
if (on)
reg |= clkbit;
else
reg &= ~clkbit;
write_reg(reg, reg_for_clk(clk));
return 0;
}
whereas if you have seperate enable and disable methods you
end up duplicating quite a lot of that code, as so:
int myclk_enable(struct clk *clk)
{
clkbit = bit_for_clk(clk);
reg = read_reg(reg_for_clk(clk));
reg |= clkbit;
write_reg(reg, reg_for_clk(clk));
return 0;
}
int myclk_disable(struct clk *clk)
{
clkbit = bit_for_clk(clk);
reg = read_reg(reg_for_clk(clk));
reg &= ~clkbit;
write_reg(reg, reg_for_clk(clk));
return 0;
}
> +
> +
> +struct clk {
> + struct list_head node;
> + spinlock_t *lock;
> + struct kref ref;
> + int usage;
> +#ifdef CONFIG_DEBUG_FS
> + struct dentry *dir;
> + struct dentry *info;
> +#endif
Can't you hide this in the code, say by wrappering the
struct with something else when it is registered?
> +
> + const char *name;
> + struct clk *parent;
> + struct clk_ops *ops;
> + void (*release)(struct clk *clk);
> +};
> +
> +int clk_register(struct clk *clk);
> +void clk_unregister(struct clk *clk);
> +int clks_register(struct clk **clk, size_t num);
> +void clks_unregister(struct clk **clk, size_t num);
> +
> +#endif
> diff --git a/lib/Kconfig b/lib/Kconfig
> index 8cc8e87..592f5e1 100644
> --- a/lib/Kconfig
> +++ b/lib/Kconfig
> @@ -13,6 +13,9 @@ config GENERIC_FIND_FIRST_BIT
> config GENERIC_FIND_NEXT_BIT
> def_bool n
>
> +config HAVE_CLOCKLIB
> + tristate
> +
> config CRC_CCITT
> tristate "CRC-CCITT functions"
> help
> diff --git a/lib/Makefile b/lib/Makefile
> index 74b0cfb..cee74e1 100644
> --- a/lib/Makefile
> +++ b/lib/Makefile
> @@ -37,6 +37,7 @@ obj-$(CONFIG_PLIST) += plist.o
> obj-$(CONFIG_DEBUG_PREEMPT) += smp_processor_id.o
> obj-$(CONFIG_DEBUG_LIST) += list_debug.o
> obj-$(CONFIG_DEBUG_OBJECTS) += debugobjects.o
> +obj-$(CONFIG_HAVE_CLOCKLIB) += clocklib.o
why not call this CONFIG_GENERIC_CLK ?
> ifneq ($(CONFIG_HAVE_DEC_LOCK),y)
> lib-y += dec_and_lock.o
> diff --git a/lib/clocklib.c b/lib/clocklib.c
> new file mode 100644
> index 0000000..590a665
> --- /dev/null
> +++ b/lib/clocklib.c
> @@ -0,0 +1,353 @@
> +/*
> + * Generic clocks API implementation
> + *
> + * Copyright (c) 2008 Dmitry Baryshkov
> + *
> + * This file is released under the GPL v2.
> + */
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/clk.h>
> +#include <linux/clocklib.h>
> +#include <linux/spinlock.h>
> +
> +static LIST_HEAD(clocks);
> +static DEFINE_SPINLOCK(clocks_lock);
> +
> +#ifdef CONFIG_DEBUG_FS
> +#include <linux/debugfs.h>
> +#include <linux/seq_file.h>
> +static struct dentry *clkdir;
possibly seperate the code out into a seperate file
> +
> +static int clocklib_show(struct seq_file *s, void *data)
> +{
> + struct clk *clk = s->private;
> +
> + BUG_ON(!clk);
> +
> + seq_printf(s, "set_parent=%savailable\nusage=%d/%d\nrate=%10lu Hz\n",
> + clk->ops && clk->ops->set_parent ? "not " : "",
> + clk->usage, atomic_read(&clk->ref.refcount),
> + clk_get_rate(clk));
> +// if (clk->ops && clk->ops->format)
> +// clk->ops->format(clk, s);
> +
> + return 0;
> +}
> +
> +static int clocklib_open(struct inode *inode, struct file *file)
> +{
> + return single_open(file, clocklib_show, inode->i_private);
> +}
> +
> +static struct file_operations clocklib_operations = {
> + .open = clocklib_open,
> + .read = seq_read,
> + .llseek = seq_lseek,
> + .release = single_release,
> +};
> +
> +static int clk_debugfs_init(struct clk *clk)
> +{
> + struct dentry *dir;
> + struct dentry *info;
> +
> + if (!clkdir)
> + dump_stack();
> +
> + dir = debugfs_create_dir(clk->name,
> + clk->parent ? clk->parent->dir : clkdir);
> +
> + if (IS_ERR(dir))
> + return PTR_ERR(dir);
> +
> + info = debugfs_create_file("info", S_IFREG | S_IRUGO,
> + dir, clk, &clocklib_operations);
> +
> + if (IS_ERR(info)) {
> + debugfs_remove(dir);
> + return PTR_ERR(info);
> + }
> +
> + clk->dir = dir;
> + clk->info = info;
> +
> + return 0;
> +}
> +
> +static void clk_debugfs_clean(struct clk *clk)
> +{
> + if (clk->info)
> + debugfs_remove(clk->info);
> + clk->info = NULL;
> +
> + if (clk->dir)
> + debugfs_remove(clk->dir);
> + clk->dir = NULL;
> +}
> +
> +static void clk_debugfs_reparent(struct clk *clk, struct clk *old, struct clk *new)
> +{
> + struct dentry *oldd = old ? old->dir : clkdir;
> + struct dentry *newd = new ? new->dir : clkdir;
> + struct dentry *dir = debugfs_rename(oldd, clk->dir, newd, clk->name);
> +
> + if (IS_ERR(dir))
> + WARN_ON(1);
> + else
> + clk->dir = dir;
> +}
> +
> +static int __init clocklib_debugfs_init(void)
> +{
> + clkdir = debugfs_create_dir("clocks", NULL);
> + return 0;
> +}
> +core_initcall(clocklib_debugfs_init);
> +#else
> +#define clk_debugfs_init(clk) ({0;})
> +#define clk_debugfs_clean(clk) do {} while (0);
> +#define clk_debugfs_reparent(clk, old, new) do {} while (0);
> +#endif
> +
> +static int clk_can_get_def(struct clk *clk, struct device *dev)
> +{
> + return 1;
> +}
> +
> +static unsigned long clk_get_rate_def(struct clk *clk)
> +{
> + return 0;
> +}
> +
> +static long clk_round_rate_def(struct clk *clk, unsigned long hz, bool apply)
> +{
> + long rate = clk->ops->get_rate(clk);
> +
> + if (apply && hz != rate)
> + return -EINVAL;
> +
> + return rate;
> +}
> +
> +static void clk_release(struct kref *ref)
> +{
> + struct clk *clk = container_of(ref, struct clk, ref);
> +
> + BUG_ON(!clk->release);
> +
> + if (clk->parent)
> + kref_get(&clk->parent->ref);
> +
> + clk->release(clk);
> +}
> +EXPORT_SYMBOL(clk_round_rate);
> +
> +struct clk* clk_get_parent(struct clk *clk)
> +{
> + struct clk *parent;
> +
> + spin_lock(clk->lock);
> +
> + parent = clk->parent;
> + kref_get(&parent->ref);
> +
> + spin_unlock(clk->lock);
> +
> + return parent;
> +}
> +EXPORT_SYMBOL(clk_get_parent);
> +
> +int clk_set_parent(struct clk *clk, struct clk *parent)
> +{
> + int rc = -EINVAL;
> + struct clk *old;
> +
> + spin_lock(clk->lock);
> +
> + if (!clk->ops->set_parent)
> + goto out;
> +
> + old = clk->parent;
> +
> + rc = clk->ops->set_parent(clk, parent);
> + if (rc)
> + goto out;
> +
> + kref_get(&parent->ref);
> + clk->parent = parent;
> +
> + clk_debugfs_reparent(clk, old, parent);
> +
> + kref_put(&old->ref, clk_release);
> +
> +out:
> + spin_unlock(clk->lock);
> +
> + return rc;
> +}
> +EXPORT_SYMBOL(clk_set_parent);
> +
> +int clk_register(struct clk *clk)
> +{
> + int rc;
> +
> + BUG_ON(!clk->ops);
> + BUG_ON(!clk->ops->enable || !clk->ops->disable);
> +
> + if (!clk->ops->can_get)
> + clk->ops->can_get = clk_can_get_def;
> + if (!clk->ops->get_rate)
> + clk->ops->get_rate = clk_get_rate_def;
> + if (!clk->ops->round_rate)
> + clk->ops->round_rate = clk_round_rate_def;
> +
> + kref_init(&clk->ref);
> +
> + spin_lock(&clocks_lock);
> + if (clk->parent)
> + kref_get(&clk->parent->ref);
> + list_add_tail(&clk->node, &clocks);
> +
> + rc = clk_debugfs_init(clk);
> + if (rc) {
> + list_del(&clk->node);
> + kref_put(&clk->ref, clk_release);
> + }
> +
> + spin_unlock(&clocks_lock);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL(clk_register);
> +
> +void clk_unregister(struct clk *clk)
> +{
> + spin_lock(&clocks_lock);
> + clk_debugfs_clean(clk);
> + list_del(&clk->node);
> + kref_put(&clk->ref, clk_release);
> + spin_unlock(&clocks_lock);
> +}
> +EXPORT_SYMBOL(clk_unregister);
> +
> +int clks_register(struct clk **clk, size_t num)
> +{
> + int i;
> + int rc;
> + for (i = 0; i < num; i++) {
> + rc = clk_register(clk[i]);
> + if (rc)
> + return rc;
> + }
> +
> + return 0;
> +}
> +EXPORT_SYMBOL(clks_register);
> +
> +void clks_unregister(struct clk **clk, size_t num)
> +{
> + int i;
> + for (i = 0; i < num; i++)
> + clk_unregister(clk[i]);
> +}
> +EXPORT_SYMBOL(clks_unregister);
> +
> +struct clk *clk_get(struct device *dev, const char *id)
> +{
> + struct clk *clk = NULL, *p;
> +
> + spin_lock(&clocks_lock);
> + list_for_each_entry(p, &clocks, node)
> + if (strcmp(id, p->name) == 0 && p->ops->can_get(p, dev)) {
> + clk = p;
> + kref_get(&clk->ref);
> + break;
> + }
> +
> + spin_unlock(&clocks_lock);
> +
> + return clk;
> +}
> +EXPORT_SYMBOL(clk_get);
> +
> +void clk_put(struct clk *clk)
> +{
> + kref_put(&clk->ref, clk_release);
> +}
> +EXPORT_SYMBOL(clk_put);
> +
> +int clk_enable(struct clk *clk)
> +{
> + int rc = 0;
> +
> + spin_lock(clk->lock);
> +
> + clk->usage++;
> + if (clk->usage == 1)
> + rc = clk->ops->enable(clk);
> +
> + if (rc)
> + clk->usage--;
clk->usage = 0; is the same, maybe also easier to encase
the whole lot in the same if block:
if (clk->usage == 1) {
rc = clk->ops->enable(clk);
if (rc)
clk->usage = 0;
}
> +
> + spin_unlock(clk->lock);
> +
> + return rc;
> +}
> +EXPORT_SYMBOL(clk_enable);
> +
> +void clk_disable(struct clk *clk)
> +{
> + spin_lock(clk->lock);
> +
> + WARN_ON(clk->usage <= 0);
> +
> + clk->usage--;
> + if (clk->usage == 0)
> + clk->ops->disable(clk);
> +
> + spin_unlock(clk->lock);
> +}
> +EXPORT_SYMBOL(clk_disable);
> +
> +unsigned long clk_get_rate(struct clk *clk)
> +{
> + unsigned long hz;
> +
> + spin_lock(clk->lock);
> +
> + hz = clk->ops->get_rate(clk);
> +
> + spin_unlock(clk->lock);
> +
> + return hz;
> +}
> +EXPORT_SYMBOL(clk_get_rate);
> +
> +int clk_set_rate(struct clk *clk, unsigned long hz)
> +{
> + long rc;
> +
> + spin_lock(clk->lock);
> +
> + rc = clk->ops->round_rate(clk, hz, 1);
> +
> + spin_unlock(clk->lock);
> +
> + return rc < 0 ? rc : 0;
> +}
> +EXPORT_SYMBOL(clk_set_rate);
> +
> +long clk_round_rate(struct clk *clk, unsigned long hz)
> +{
> + long rc;
> +
> + spin_lock(clk->lock);
> +
> + rc = clk->ops->round_rate(clk, hz, 0);
> +
> + spin_unlock(clk->lock);
> +
> + return rc;
> +}
--
Ben ([email protected], http://www.fluff.org/)
'a smiley only costs 4 bytes'
On Thu, Jul 03, 2008 at 09:31:57PM +0100, Ben Dooks wrote:
> On Thu, Jun 26, 2008 at 04:51:38PM +0400, Dmitry Baryshkov 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.
> >
> > Signed-off-by: Dmitry Baryshkov <[email protected]>
> > ---
> > include/linux/clocklib.h | 58 ++++++++
> > lib/Kconfig | 3 +
> > lib/Makefile | 1 +
> > lib/clocklib.c | 353 ++++++++++++++++++++++++++++++++++++++++++++++
>
> why under lib? drivers/clk might be a better place for this.
Because there will be probably no "clock only" drivers. Probably it
should go into kernel/, but definitely not into drivers/clk/
>
> > 4 files changed, 415 insertions(+), 0 deletions(-)
> > create mode 100644 include/linux/clocklib.h
> > create mode 100644 lib/clocklib.c
> >
> > diff --git a/include/linux/clocklib.h b/include/linux/clocklib.h
> > new file mode 100644
> > index 0000000..cf2b41e
> > --- /dev/null
> > +++ b/include/linux/clocklib.h
> > @@ -0,0 +1,58 @@
> > +/*
> > + * Copyright (C) 2008 Dmitry Baryshkov
> > + *
> > + * This file is released under the GPL v2.
> > + */
> > +
> > +#ifndef CLKLIB_H
> > +#define CLKLIB_H
> > +
> > +#include <linux/spinlock.h>
> > +#include <linux/kref.h>
> > +
> > +struct clk;
> > +
> > +/**
> > + * 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 (*enable)(struct clk *clk);
> > + void (*disable)(struct clk *clk);
> > + unsigned long (*get_rate)(struct clk *clk);
> > + long (*round_rate)(struct clk *clk, unsigned long hz, bool apply);
> > +};
>
>
> I'd much prefer to see the following changes:
>
> 1) enable and disable merged into one, and pass an enable
> parameter, ie:
>
> int (*set_enable)(struct clk *clk, bool on);
>
> as most code I see is of the following:
>
> int myclk_enable(struct clk *clk, bool on)
> {
> clkbit = bit_for_clk(clk);
> reg = read_reg(reg_for_clk(clk));
>
> if (on)
> reg |= clkbit;
> else
> reg &= ~clkbit;
>
> write_reg(reg, reg_for_clk(clk));
> return 0;
> }
It was discussed before. Andrew Morton specifically said, that he
doesn't want set_enable-like functions with bool param.
> whereas if you have seperate enable and disable methods you
> end up duplicating quite a lot of that code, as so:
>
> int myclk_enable(struct clk *clk)
> {
> clkbit = bit_for_clk(clk);
> reg = read_reg(reg_for_clk(clk));
>
> reg |= clkbit;
>
> write_reg(reg, reg_for_clk(clk));
> return 0;
> }
>
> int myclk_disable(struct clk *clk)
> {
> clkbit = bit_for_clk(clk);
> reg = read_reg(reg_for_clk(clk));
>
> reg &= ~clkbit;
>
> write_reg(reg, reg_for_clk(clk));
> return 0;
> }
>
> > +
> > +
> > +struct clk {
> > + struct list_head node;
> > + spinlock_t *lock;
> > + struct kref ref;
> > + int usage;
> > +#ifdef CONFIG_DEBUG_FS
> > + struct dentry *dir;
> > + struct dentry *info;
> > +#endif
>
> Can't you hide this in the code, say by wrappering the
> struct with something else when it is registered?
It is allocated dynamically by drivers. I can move this to
struct clk_private to specify that it's private, but it should be
visible outside
>
> > +
> > + const char *name;
> > + struct clk *parent;
> > + struct clk_ops *ops;
> > + void (*release)(struct clk *clk);
> > +};
> > +
> > +int clk_register(struct clk *clk);
> > +void clk_unregister(struct clk *clk);
> > +int clks_register(struct clk **clk, size_t num);
> > +void clks_unregister(struct clk **clk, size_t num);
> > +
> > +#endif
> > diff --git a/lib/Kconfig b/lib/Kconfig
> > index 8cc8e87..592f5e1 100644
> > --- a/lib/Kconfig
> > +++ b/lib/Kconfig
> > @@ -13,6 +13,9 @@ config GENERIC_FIND_FIRST_BIT
> > config GENERIC_FIND_NEXT_BIT
> > def_bool n
> >
> > +config HAVE_CLOCKLIB
> > + tristate
> > +
> > config CRC_CCITT
> > tristate "CRC-CCITT functions"
> > help
> > diff --git a/lib/Makefile b/lib/Makefile
> > index 74b0cfb..cee74e1 100644
> > --- a/lib/Makefile
> > +++ b/lib/Makefile
> > @@ -37,6 +37,7 @@ obj-$(CONFIG_PLIST) += plist.o
> > obj-$(CONFIG_DEBUG_PREEMPT) += smp_processor_id.o
> > obj-$(CONFIG_DEBUG_LIST) += list_debug.o
> > obj-$(CONFIG_DEBUG_OBJECTS) += debugobjects.o
> > +obj-$(CONFIG_HAVE_CLOCKLIB) += clocklib.o
>
> why not call this CONFIG_GENERIC_CLK ?
it was modelled after CONFIG_HAVE_GPIO_LIB
>
> > ifneq ($(CONFIG_HAVE_DEC_LOCK),y)
> > lib-y += dec_and_lock.o
> > diff --git a/lib/clocklib.c b/lib/clocklib.c
> > new file mode 100644
> > index 0000000..590a665
> > --- /dev/null
> > +++ b/lib/clocklib.c
> > @@ -0,0 +1,353 @@
> > +/*
> > + * Generic clocks API implementation
> > + *
> > + * Copyright (c) 2008 Dmitry Baryshkov
> > + *
> > + * This file is released under the GPL v2.
> > + */
> > +#include <linux/kernel.h>
> > +#include <linux/module.h>
> > +#include <linux/clk.h>
> > +#include <linux/clocklib.h>
> > +#include <linux/spinlock.h>
> > +
> > +static LIST_HEAD(clocks);
> > +static DEFINE_SPINLOCK(clocks_lock);
> > +
> > +#ifdef CONFIG_DEBUG_FS
> > +#include <linux/debugfs.h>
> > +#include <linux/seq_file.h>
> > +static struct dentry *clkdir;
>
> possibly seperate the code out into a seperate file
fine
>
> > +
> > +static int clocklib_show(struct seq_file *s, void *data)
> > +{
> > + struct clk *clk = s->private;
> > +
> > + BUG_ON(!clk);
> > +
> > + seq_printf(s, "set_parent=%savailable\nusage=%d/%d\nrate=%10lu Hz\n",
> > + clk->ops && clk->ops->set_parent ? "not " : "",
> > + clk->usage, atomic_read(&clk->ref.refcount),
> > + clk_get_rate(clk));
> > +// if (clk->ops && clk->ops->format)
> > +// clk->ops->format(clk, s);
> > +
> > + return 0;
> > +}
> > +
> > +static int clocklib_open(struct inode *inode, struct file *file)
> > +{
> > + return single_open(file, clocklib_show, inode->i_private);
> > +}
> > +
> > +static struct file_operations clocklib_operations = {
> > + .open = clocklib_open,
> > + .read = seq_read,
> > + .llseek = seq_lseek,
> > + .release = single_release,
> > +};
> > +
> > +static int clk_debugfs_init(struct clk *clk)
> > +{
> > + struct dentry *dir;
> > + struct dentry *info;
> > +
> > + if (!clkdir)
> > + dump_stack();
> > +
> > + dir = debugfs_create_dir(clk->name,
> > + clk->parent ? clk->parent->dir : clkdir);
> > +
> > + if (IS_ERR(dir))
> > + return PTR_ERR(dir);
> > +
> > + info = debugfs_create_file("info", S_IFREG | S_IRUGO,
> > + dir, clk, &clocklib_operations);
> > +
> > + if (IS_ERR(info)) {
> > + debugfs_remove(dir);
> > + return PTR_ERR(info);
> > + }
> > +
> > + clk->dir = dir;
> > + clk->info = info;
> > +
> > + return 0;
> > +}
> > +
> > +static void clk_debugfs_clean(struct clk *clk)
> > +{
> > + if (clk->info)
> > + debugfs_remove(clk->info);
> > + clk->info = NULL;
> > +
> > + if (clk->dir)
> > + debugfs_remove(clk->dir);
> > + clk->dir = NULL;
> > +}
> > +
> > +static void clk_debugfs_reparent(struct clk *clk, struct clk *old, struct clk *new)
> > +{
> > + struct dentry *oldd = old ? old->dir : clkdir;
> > + struct dentry *newd = new ? new->dir : clkdir;
> > + struct dentry *dir = debugfs_rename(oldd, clk->dir, newd, clk->name);
> > +
> > + if (IS_ERR(dir))
> > + WARN_ON(1);
> > + else
> > + clk->dir = dir;
> > +}
> > +
> > +static int __init clocklib_debugfs_init(void)
> > +{
> > + clkdir = debugfs_create_dir("clocks", NULL);
> > + return 0;
> > +}
> > +core_initcall(clocklib_debugfs_init);
> > +#else
> > +#define clk_debugfs_init(clk) ({0;})
> > +#define clk_debugfs_clean(clk) do {} while (0);
> > +#define clk_debugfs_reparent(clk, old, new) do {} while (0);
> > +#endif
> > +
> > +static int clk_can_get_def(struct clk *clk, struct device *dev)
> > +{
> > + return 1;
> > +}
> > +
> > +static unsigned long clk_get_rate_def(struct clk *clk)
> > +{
> > + return 0;
> > +}
> > +
> > +static long clk_round_rate_def(struct clk *clk, unsigned long hz, bool apply)
> > +{
> > + long rate = clk->ops->get_rate(clk);
> > +
> > + if (apply && hz != rate)
> > + return -EINVAL;
> > +
> > + return rate;
> > +}
> > +
> > +static void clk_release(struct kref *ref)
> > +{
> > + struct clk *clk = container_of(ref, struct clk, ref);
> > +
> > + BUG_ON(!clk->release);
> > +
> > + if (clk->parent)
> > + kref_get(&clk->parent->ref);
> > +
> > + clk->release(clk);
> > +}
> > +EXPORT_SYMBOL(clk_round_rate);
> > +
> > +struct clk* clk_get_parent(struct clk *clk)
> > +{
> > + struct clk *parent;
> > +
> > + spin_lock(clk->lock);
> > +
> > + parent = clk->parent;
> > + kref_get(&parent->ref);
> > +
> > + spin_unlock(clk->lock);
> > +
> > + return parent;
> > +}
> > +EXPORT_SYMBOL(clk_get_parent);
> > +
> > +int clk_set_parent(struct clk *clk, struct clk *parent)
> > +{
> > + int rc = -EINVAL;
> > + struct clk *old;
> > +
> > + spin_lock(clk->lock);
> > +
> > + if (!clk->ops->set_parent)
> > + goto out;
> > +
> > + old = clk->parent;
> > +
> > + rc = clk->ops->set_parent(clk, parent);
> > + if (rc)
> > + goto out;
> > +
> > + kref_get(&parent->ref);
> > + clk->parent = parent;
> > +
> > + clk_debugfs_reparent(clk, old, parent);
> > +
> > + kref_put(&old->ref, clk_release);
> > +
> > +out:
> > + spin_unlock(clk->lock);
> > +
> > + return rc;
> > +}
> > +EXPORT_SYMBOL(clk_set_parent);
> > +
> > +int clk_register(struct clk *clk)
> > +{
> > + int rc;
> > +
> > + BUG_ON(!clk->ops);
> > + BUG_ON(!clk->ops->enable || !clk->ops->disable);
> > +
> > + if (!clk->ops->can_get)
> > + clk->ops->can_get = clk_can_get_def;
> > + if (!clk->ops->get_rate)
> > + clk->ops->get_rate = clk_get_rate_def;
> > + if (!clk->ops->round_rate)
> > + clk->ops->round_rate = clk_round_rate_def;
> > +
> > + kref_init(&clk->ref);
> > +
> > + spin_lock(&clocks_lock);
> > + if (clk->parent)
> > + kref_get(&clk->parent->ref);
> > + list_add_tail(&clk->node, &clocks);
> > +
> > + rc = clk_debugfs_init(clk);
> > + if (rc) {
> > + list_del(&clk->node);
> > + kref_put(&clk->ref, clk_release);
> > + }
> > +
> > + spin_unlock(&clocks_lock);
> > +
> > + return 0;
> > +}
> > +EXPORT_SYMBOL(clk_register);
> > +
> > +void clk_unregister(struct clk *clk)
> > +{
> > + spin_lock(&clocks_lock);
> > + clk_debugfs_clean(clk);
> > + list_del(&clk->node);
> > + kref_put(&clk->ref, clk_release);
> > + spin_unlock(&clocks_lock);
> > +}
> > +EXPORT_SYMBOL(clk_unregister);
> > +
> > +int clks_register(struct clk **clk, size_t num)
> > +{
> > + int i;
> > + int rc;
> > + for (i = 0; i < num; i++) {
> > + rc = clk_register(clk[i]);
> > + if (rc)
> > + return rc;
> > + }
> > +
> > + return 0;
> > +}
> > +EXPORT_SYMBOL(clks_register);
> > +
> > +void clks_unregister(struct clk **clk, size_t num)
> > +{
> > + int i;
> > + for (i = 0; i < num; i++)
> > + clk_unregister(clk[i]);
> > +}
> > +EXPORT_SYMBOL(clks_unregister);
> > +
> > +struct clk *clk_get(struct device *dev, const char *id)
> > +{
> > + struct clk *clk = NULL, *p;
> > +
> > + spin_lock(&clocks_lock);
> > + list_for_each_entry(p, &clocks, node)
> > + if (strcmp(id, p->name) == 0 && p->ops->can_get(p, dev)) {
> > + clk = p;
> > + kref_get(&clk->ref);
> > + break;
> > + }
> > +
> > + spin_unlock(&clocks_lock);
> > +
> > + return clk;
> > +}
> > +EXPORT_SYMBOL(clk_get);
> > +
> > +void clk_put(struct clk *clk)
> > +{
> > + kref_put(&clk->ref, clk_release);
> > +}
> > +EXPORT_SYMBOL(clk_put);
> > +
> > +int clk_enable(struct clk *clk)
> > +{
> > + int rc = 0;
> > +
> > + spin_lock(clk->lock);
> > +
> > + clk->usage++;
> > + if (clk->usage == 1)
> > + rc = clk->ops->enable(clk);
> > +
> > + if (rc)
> > + clk->usage--;
>
> clk->usage = 0; is the same, maybe also easier to encase
> the whole lot in the same if block:
>
> if (clk->usage == 1) {
> rc = clk->ops->enable(clk);
>
> if (rc)
> clk->usage = 0;
> }
ok
>
> > +
> > + spin_unlock(clk->lock);
> > +
> > + return rc;
> > +}
> > +EXPORT_SYMBOL(clk_enable);
> > +
> > +void clk_disable(struct clk *clk)
> > +{
> > + spin_lock(clk->lock);
> > +
> > + WARN_ON(clk->usage <= 0);
> > +
> > + clk->usage--;
> > + if (clk->usage == 0)
> > + clk->ops->disable(clk);
> > +
> > + spin_unlock(clk->lock);
> > +}
> > +EXPORT_SYMBOL(clk_disable);
> > +
> > +unsigned long clk_get_rate(struct clk *clk)
> > +{
> > + unsigned long hz;
> > +
> > + spin_lock(clk->lock);
> > +
> > + hz = clk->ops->get_rate(clk);
> > +
> > + spin_unlock(clk->lock);
> > +
> > + return hz;
> > +}
> > +EXPORT_SYMBOL(clk_get_rate);
> > +
> > +int clk_set_rate(struct clk *clk, unsigned long hz)
> > +{
> > + long rc;
> > +
> > + spin_lock(clk->lock);
> > +
> > + rc = clk->ops->round_rate(clk, hz, 1);
> > +
> > + spin_unlock(clk->lock);
> > +
> > + return rc < 0 ? rc : 0;
> > +}
> > +EXPORT_SYMBOL(clk_set_rate);
> > +
> > +long clk_round_rate(struct clk *clk, unsigned long hz)
> > +{
> > + long rc;
> > +
> > + spin_lock(clk->lock);
> > +
> > + rc = clk->ops->round_rate(clk, hz, 0);
> > +
> > + spin_unlock(clk->lock);
> > +
> > + return rc;
> > +}
>
> --
> Ben ([email protected], http://www.fluff.org/)
>
> 'a smiley only costs 4 bytes'
--
With best wishes
Dmitry
On Thu, Jul 03, 2008 at 09:22:50PM +0100, Ben Dooks wrote:
> On Thu, Jun 26, 2008 at 04:50:33PM +0400, Dmitry Baryshkov wrote:
> > Hi,
> >
> > This is again a set of patches to unify the management of clocks and
> > allow easy registration and unregistration of them. This is neccessary
> > to cleanly support such devices as toshiba mobile companion chips,
> > sa1111 companion, etc. Also it brings code unification, especially for a
> > lot of arm sub-arches which share nearly the same code, etc.
> >
> > This is the "version 3" approach. Given the negative response to
> > kobjects, I've redesigned it to use plain krefs.
> >
> > Debugfs support is merged into main clocklib patch. Documentation
> > for it's interface will come later. For now it provides tree structure
> > with single file per each clock directory.
>
> I'd prefer not to see this called clocklib, it isn't of any use
> outside of the kernel, and once it is in there's little point in
> anyone not using it.
Please propose better name.
>
> --
> Ben ([email protected], http://www.fluff.org/)
>
> 'a smiley only costs 4 bytes'
--
With best wishes
Dmitry
Dmitry Baryshkov <[email protected]> wrote:
> > > +#ifdef CONFIG_DEBUG_FS
> > > + struct dentry *dir;
> > > + struct dentry *info;
> > > +#endif
> >
> > Can't you hide this in the code, say by wrappering the
> > struct with something else when it is registered?
>
> It is allocated dynamically by drivers. I can move this to
> struct clk_private to specify that it's private, but it should be
> visible outside
Actually, I don't think it _should_ be private. Low-level clock drivers
might want to provide debugfs nodes on their own, and those nodes
naturally belong in the same directory as the clklib ones. So the
debugfs root node must be exposed somehow.
You can get rid of the "info" field if you apply this patch:
http://www.kernel.org/pub/linux/kernel/people/gregkh/gregkh-2.6/patches/driver-core/debugfs-implement-debugfs_remove_recursive.patch
Haavard
On Thu 2008-07-03 21:31:57, Ben Dooks wrote:
> On Thu, Jun 26, 2008 at 04:51:38PM +0400, Dmitry Baryshkov 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.
> >
> > Signed-off-by: Dmitry Baryshkov <[email protected]>
> > ---
> > include/linux/clocklib.h | 58 ++++++++
> > lib/Kconfig | 3 +
> > lib/Makefile | 1 +
> > lib/clocklib.c | 353 ++++++++++++++++++++++++++++++++++++++++++++++
>
> why under lib? drivers/clk might be a better place for this.
This does not belong into lib/, really. drivers/clk seems ok.
Pavel
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html