On Nov 21, 2011 6:43 PM, "Mike Turquette" <mturque...@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 <mturque...@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 <mturque...@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 >
_______________________________________________ linaro-dev mailing list linaro-dev@lists.linaro.org http://lists.linaro.org/mailman/listinfo/linaro-dev