lkml.org 
[lkml]   [2011]   [Nov]   [21]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH v3 5/5] clk: export tree topology and clk data via sysfs
    Date
    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.

    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


    \
     
     \ /
      Last update: 2011-11-22 02:47    [W:0.036 / U:0.732 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site