On Nov 21, 2011 6:43 PM, "Mike Turquette" mturquette@ti.com wrote:
Introduces kobject support for the common struct clk, exports per-clk data via read-only callbacks and models the clk tree topology in sysfs.
Also adds support for generating the clk tree in clk_init and migrating nodes when input sources are switches in clk_set_parent.
I'm not convinced this is a good idea. What is the use case for exporting the clock tree? If it is debug, then I suggest using debugfs to avoid abi issues.
g.
Signed-off-by: Mike Turquette mturquette@linaro.org
drivers/clk/Kconfig | 10 +++ drivers/clk/Makefile | 1 + drivers/clk/clk-sysfs.c | 199
+++++++++++++++++++++++++++++++++++++++++++++++
drivers/clk/clk.c | 5 +- include/linux/clk.h | 36 ++++++++- 5 files changed, 248 insertions(+), 3 deletions(-) create mode 100644 drivers/clk/clk-sysfs.c
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index ba7eb8c..8f8e7ac 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -19,3 +19,13 @@ config GENERIC_CLK_BASIC help Allow use of basic, single-function clock types. These common definitions can be used across many platforms.
+config GENERIC_CLK_SYSFS
bool "Clock tree topology and debug info"
depends on EXPERIMENTAL && GENERIC_CLK
help
Creates clock tree represenation in sysfs. Directory names
and hierarchy represent clock names and tree structure,
respectively. Each directory exports clock rate, flags,
prepare_count and enable_count info as read-only for debug
purposes.
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 68b20a1..806a9999 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -2,3 +2,4 @@ obj-$(CONFIG_CLKDEV_LOOKUP) += clkdev.o obj-$(CONFIG_GENERIC_CLK) += clk.o obj-$(CONFIG_GENERIC_CLK_BASIC) += clk-basic.o +obj-$(CONFIG_GENERIC_CLK_SYSFS) += clk-sysfs.o diff --git a/drivers/clk/clk-sysfs.c b/drivers/clk/clk-sysfs.c new file mode 100644 index 0000000..8ccf9e3 --- /dev/null +++ b/drivers/clk/clk-sysfs.c @@ -0,0 +1,199 @@ +/*
- Copyright (C) 2011 Linaro Ltd mturquette@linaro.org
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License version 2 as
- published by the Free Software Foundation.
- Clock tree topology and debug info for the common clock framework
- */
+#include <linux/clk.h> +#include <linux/kobject.h> +#include <linux/sysfs.h> +#include <linux/err.h>
+#define MAX_STRING_LENGTH 32
+static struct kobject *clk_kobj; +LIST_HEAD(kobj_list);
+struct clk_attribute {
struct attribute attr;
ssize_t (*show)(struct clk *clk, char *buf);
+};
+static ssize_t clk_rate_show(struct clk *clk, char *buf) +{
if (IS_ERR_OR_NULL(clk))
return -ENODEV;
return snprintf(buf, MAX_STRING_LENGTH, "%lu\n", clk->rate);
+}
+static ssize_t clk_flags_show(struct clk *clk, char *buf) +{
if (IS_ERR_OR_NULL(clk))
return -ENODEV;
return snprintf(buf, MAX_STRING_LENGTH, "0x%lX\n", clk->flags);
+}
+static ssize_t clk_prepare_count_show(struct clk *clk, char *buf) +{
if (IS_ERR_OR_NULL(clk))
return -ENODEV;
return snprintf(buf, MAX_STRING_LENGTH, "%d\n",
clk->prepare_count);
+}
+static ssize_t clk_enable_count_show(struct clk *clk, char *buf) +{
if (IS_ERR_OR_NULL(clk))
return -ENODEV;
return snprintf(buf, MAX_STRING_LENGTH, "%d\n",
clk->enable_count);
+}
+static ssize_t clk_show(struct kobject *kobj, struct attribute *attr,
char *buf)
+{
struct clk *clk;
struct clk_attribute *clk_attr;
ssize_t ret = -EINVAL;
clk = container_of(kobj, struct clk, kobj);
clk_attr = container_of(attr, struct clk_attribute, attr);
if (!clk || !clk_attr)
goto out;
/* we don't do any locking for debug operations */
/* refcount++ */
kobject_get(&clk->kobj);
if (clk_attr->show)
ret = clk_attr->show(clk, buf);
else
ret = -EIO;
/* refcount-- */
kobject_put(&clk->kobj);
+out:
return ret;
+}
+static struct clk_attribute clk_rate = __ATTR_RO(clk_rate); +static struct clk_attribute clk_flags = __ATTR_RO(clk_flags); +static struct clk_attribute clk_prepare_count =
__ATTR_RO(clk_prepare_count);
+static struct clk_attribute clk_enable_count =
__ATTR_RO(clk_enable_count);
+static struct attribute *clk_default_attrs[] = {
&clk_rate.attr,
&clk_flags.attr,
&clk_prepare_count.attr,
&clk_enable_count.attr,
NULL,
+};
+static const struct sysfs_ops clk_ops = {
.show = clk_show,
+};
+static void clk_release(struct kobject *kobj) +{
struct clk *clk;
clk = container_of(kobj, struct clk, kobj);
complete(&clk->kobj_unregister);
+}
+static struct kobj_type clk_ktype = {
.sysfs_ops = &clk_ops,
.default_attrs = clk_default_attrs,
.release = clk_release,
+};
+int clk_kobj_add(struct clk *clk) +{
if (IS_ERR(clk))
return -EINVAL;
/*
* Some kobject trickery!
*
* We want to (ab)use the kobject infrastructure to track our
* tree topology for us, specifically the root clocks (which are
* otherwise not remembered in a global list).
*
* Unfortunately we might not be able to allocate memory yet
* when this path is hit. This pretty much rules out anything
* that looks or smells like kobject_add, since there are
* allocations for kobject->name and a dependency on sysfs being
* initialized.
*
* To get around this we initialize the kobjects and (ab)use
* struct kobject's list_head member, "entry". Later on we walk
* this list in clk_sysfs_tree_create() to make proper
* kobject_add calls once it is safe to do so.
*
* FIXME - this is starting to smell alot like clkdev (i.e.
* tracking the clocks in a list)
*/
kobject_init(&clk->kobj, &clk_ktype);
list_add_tail(&clk->kobj.entry, &kobj_list);
return 0;
+}
+int clk_kobj_reparent(struct clk *clk, struct clk *parent) +{
int ret;
if (!clk || !parent)
return -EINVAL;
ret = kobject_move(&clk->kobj, &parent->kobj);
if (ret)
pr_warning("%s: failed to reparent %s to %s in sysfs\n",
__func__, clk->name, parent->name);
return ret;
+}
+static int __init clk_sysfs_init(void) +{
struct list_head *tmp;
clk_kobj = kobject_create_and_add("clk", NULL);
WARN_ON(!clk_kobj);
list_for_each(tmp, &kobj_list) {
struct kobject *kobj;
struct clk *clk;
struct kobject *parent_kobj = NULL;
int ret;
kobj = container_of(tmp, struct kobject, entry);
clk = container_of(kobj, struct clk, kobj);
/* assumes list is ordered */
if (clk->parent)
parent_kobj = &clk->parent->kobj;
else
parent_kobj = clk_kobj;
ret = kobject_add(kobj, parent_kobj, clk->name);
if (ret)
pr_warning("%s: failed to create sysfs entry for
%s\n",
__func__, clk->name);
}
return 0;
+} +late_initcall(clk_sysfs_init); diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 12c9994..85dabdb 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -436,7 +436,8 @@ void __clk_reparent(struct clk *clk, struct clk
*new_parent)
clk->parent = new_parent;
/* FIXME update sysfs clock topology */
/* update sysfs clock topology */
clk_kobj_reparent(clk, clk->parent);
}
/** @@ -531,6 +532,8 @@ void clk_init(struct device *dev, struct clk *clk) else clk->rate = 0;
clk_kobj_add(clk);
mutex_unlock(&prepare_lock); return;
diff --git a/include/linux/clk.h b/include/linux/clk.h index 8ed354a..99337ca 100644 --- a/include/linux/clk.h +++ b/include/linux/clk.h @@ -14,8 +14,8 @@ #define __LINUX_CLK_H
#include <linux/kernel.h>
-#include <linux/kernel.h> +#include <linux/kobject.h> +#include <linux/completion.h> #include <linux/errno.h>
struct device; @@ -46,6 +46,10 @@ struct clk { unsigned int prepare_count; struct hlist_head children; struct hlist_node child_node; +#ifdef CONFIG_GENERIC_CLK_SYSFS
struct kobject kobj;
struct completion kobj_unregister;
+#endif };
/** @@ -177,6 +181,34 @@ int clk_register_gate(struct device *dev, const char
*name, unsigned long flags,
*/ void clk_init(struct device *dev, struct clk *clk);
+#ifdef CONFIG_GENERIC_CLK_SYSFS +/**
- clk_kobj_add - create a clk entry in sysfs
- @clk: clk to model in sysfs
- Create a directory in sysfs with the same name as clk. Also creates
- read-only entries for the common struct clk members (rate, flags,
- prepare_count & enable_count). The topology of the tree is
- represented by the sysfs directory structure itself.
- */
+int clk_kobj_add(struct clk *clk);
+/**
- clk_kobj_reparent - reparent a clk entry in sysfs
- @clk: the child clk that is switching parents
- @parent: the new parent clk
- Simple call to kobject_move to keep sysfs up to date with the
- hardware clock topology
- */
+int clk_kobj_reparent(struct clk *clk, struct clk *parent); +#else +static inline int clk_kobj_add(struct clk *clk) +{ return 0; } +static inline int clk_kobj_reparent(struct clk *clk, struct clk *parent) +{ return 0; } +#endif
#endif /* !CONFIG_GENERIC_CLK */
/**
1.7.4.1