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
>