Hi Jeremy,

In power management group of linaro, we want to export debug information of
clock to user space and make a common interface to all the SOC platform.
Currently, each SOC platform has their own way to export clock information,
like freesale and TI, which is not ideal.

To do so, I need to make somethings done, and some of them are related to
common clock code, for which I am more than happy to hear your comments.

1. Create clock information based on common clock device, more specific,
based on struct clk_lookup. Since platform drivers are supposed to register
their clock
 using 'void clkdev_add(struct clk_lookup *cl)', clk_lookup should contain
enough information for clock display, like clock name and platform clk
definition 'struck clk'. An extra field need to be added into clk_lookup is
a 'struct dentry *' to store a pointer of the dentry node which will be
exported in debug fs, like below:
struct clk_lookup {
        struct list_head        node;
        const char              *dev_id;
        const char              *con_id;
        struct clk              *clk;
+
+#if defined(CONFIG_PM_DEBUG) && defined(CONFIG_DEBUG_FS)
+       struct dentry           *dent;
+#endif
};

2. clock names is a little bit fuzzy, cause there are two names in the
clk_lookup, they are dev_id and con_id. And there is no name checking in
clkdev_add, which means that two clock with the same name could be register
into system and it is not reasonable. Also, it is impossible to create clock
information with two clocks having the same name, since user will be
confused about that. So, I plan to
 add name checking to avoid name confilt in clkdev_add.
To avoid confusion, I am going to use the combination of dev name and
connection name togather to display clock. It goes like dev-id_con-id.

3. Currently, 'struct clk' is defined in platform related code, common
clk  interface did not impose any assumption on how each platform define
their own 'struct clk', therefore, clock debug interface can not make any
assumption on how much information the platform defined clock contains as
well. So far, only clk_get_rate is there as a common defined interface which
I can use to export clock rate information, and I plan to add some optional
interface like clk_get_count, clk_get_flag, so more useful information can
be displayed.

I also attached a draft patch and the clock information displayed on the
console is a tree pattern like below(generated by powerdebug, a user
space application which can display clock information based on the debug
interface I made in the kernel).

|-- NULL-osc <flags=0x0:rate=24000000:usecount=0>

|   |-- NULL-spdif_xtal_clk <flags=0x0:rate=24000000:usecount=0>

|   |   |-- mxc_alsa_spdif.0-NULL <flags=0x0:rate=24000000:usecount=0>

|   |-- NULL-usb_phy1_clk <flags=0x0:rate=24000000:usecount=0>

|   |-- NULL-lp_apm <flags=0x0:rate=24000000:usecount=0>

|   |   |-- NULL-ssi_lp_apm_clk <flags=0x0:rate=24000000:usecount=0>

|   |   |   |-- mxc_ssi.1-NULL <flags=0x0:rate=12000000:usecount=0>

|   |   |   |   |-- NULL-ssi_ext2_clk <flags=0x0:rate=12000000:usecount=0>

|   |   |   |-- mxc_ssi.0-NULL <flags=0x0:rate=12000000:usecount=0>

|   |   |   |   |-- NULL-ssi_ext1_clk <flags=0x0:rate=12000000:usecount=0>



Yong
diff --git a/arch/arm/common/clkdev.c b/arch/arm/common/clkdev.c
index e2b2bb6..ad747b2 100644
--- a/arch/arm/common/clkdev.c
+++ b/arch/arm/common/clkdev.c
@@ -19,6 +19,7 @@
 #include <linux/mutex.h>
 #include <linux/clk.h>
 #include <linux/slab.h>
+#include <linux/debugfs.h>
 
 #include <asm/clkdev.h>
 #include <mach/clkdev.h>
@@ -95,6 +96,21 @@ EXPORT_SYMBOL(clk_put);
 
 void clkdev_add(struct clk_lookup *cl)
 {
+	struct clk_lookup *p;
+	struct clk *clk = NULL;
+
+	list_for_each_entry(p, &clocks, node) {
+		if (p->dev_id) {
+			if (!cl->dev_id || strcmp(p->dev_id, cl->dev_id))
+				continue;
+		}
+		if (p->con_id) {
+			if (!cl->con_id || strcmp(p->con_id, cl->con_id))
+				continue;
+		}
+
+		return;
+	}
 	mutex_lock(&clocks_mutex);
 	list_add_tail(&cl->node, &clocks);
 	mutex_unlock(&clocks_mutex);
@@ -177,3 +193,139 @@ void clkdev_drop(struct clk_lookup *cl)
 	kfree(cl);
 }
 EXPORT_SYMBOL(clkdev_drop);
+
+#if defined(CONFIG_PM_DEBUG) && defined(CONFIG_DEBUG_FS)
+/*
+ *	debugfs support to trace clock tree hierarchy and attributes
+ */
+
+static struct clk_lookup *clk_lookup_get_parent(struct clk *clk)
+{
+	struct clk_lookup *cl;
+
+	list_for_each_entry(cl, &clocks, node) {
+		if (cl->clk == clk_get_parent(clk))
+			return cl;
+	}
+
+	return NULL;
+}
+static int open_file(struct inode *inode, struct file *file)
+{
+	        file->private_data = inode->i_private;
+		        return 0;
+}
+static ssize_t read_file(struct file *file, char __user *user_buf,
+		                                size_t count, loff_t *ppos)
+{
+	struct clk *clk = file->private_data;
+	unsigned long rate;
+	char buf[32];
+	ssize_t ret;
+
+	rate = clk_get_rate(clk);
+
+	ret = snprintf(buf, 32, "%d\n", rate);
+
+	ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+
+	return ret;
+}
+
+static const struct file_operations rate_fops = {
+	.open = open_file,
+	.read = read_file,
+};
+
+static struct dentry *clk_debugfs_root;
+
+static int clk_debugfs_register_one(struct clk_lookup *cl)
+{
+	int err;
+	struct dentry *d, *child, *child_tmp;
+	struct clk_lookup *pa = clk_lookup_get_parent(cl->clk);
+	char s[255];
+	char *p = s;
+
+	if (cl->dev_id && cl->con_id)
+		p += sprintf(p, "%s-%s", cl->dev_id, cl->con_id);
+	else if (cl->dev_id)
+		p += sprintf(p, "%s-NULL", cl->dev_id);
+	else if (cl->con_id)
+		p += sprintf(p, "NULL-%s", cl->con_id);
+	else
+		p += sprintf(p, "%s", "unknown");
+
+	printk("create dir %s, %s....\n", s, pa? "nonroot" : "root");
+	d = debugfs_create_dir(s, pa ? pa->dent : clk_debugfs_root);
+	if (!d) {
+		printk("failed to create dir....\n");
+		return -ENOMEM;
+	}
+
+	cl->dent = d;
+
+	d = debugfs_create_file("rate", S_IRUGO, cl->dent, (void *)cl->clk, &rate_fops);
+	if (!d) {
+		err = -ENOMEM;
+		goto err_out;
+	}
+
+	return 0;
+
+err_out:
+	printk("err out....\n");
+	return -ENOMEM;
+	d = cl->dent;
+	list_for_each_entry_safe(child, child_tmp, &d->d_subdirs, d_u.d_child)
+		debugfs_remove(child);
+	debugfs_remove(cl->dent);
+	return err;
+}
+
+static int clk_debugfs_register(struct clk_lookup *cl)
+{
+	int err;
+	struct clk_lookup *pa = clk_lookup_get_parent(cl->clk);
+
+	if (pa && !pa->dent) {
+		err = clk_debugfs_register(pa);
+		if (err)
+			return err;
+	}
+
+	if (!cl->dent) {
+		err = clk_debugfs_register_one(cl);
+		if (err)
+			return err;
+	}
+	return 0;
+}
+
+static int __init clk_debugfs_init(void)
+{
+	struct clk_lookup *cl;
+	struct dentry *d;
+	int err;
+
+	d = debugfs_create_dir("clock", NULL);
+	if (!d)
+		return -ENOMEM;
+	clk_debugfs_root = d;
+
+	list_for_each_entry(cl, &clocks, node) {
+		printk("dev name %s, con name %s\n",\
+			cl->dev_id ? cl->dev_id : NULL,\
+		       	cl->con_id ? cl->con_id : NULL);
+		err = clk_debugfs_register(cl);
+		if (err)
+			goto err_out;
+	}
+	return 0;
+err_out:
+	debugfs_remove_recursive(clk_debugfs_root);
+	return err;
+}
+late_initcall(clk_debugfs_init);
+
+#endif /* defined(CONFIG_PM_DEBUG) && defined(CONFIG_DEBUG_FS) */
diff --git a/arch/arm/include/asm/clkdev.h b/arch/arm/include/asm/clkdev.h
index b56c138..81bc118 100644
--- a/arch/arm/include/asm/clkdev.h
+++ b/arch/arm/include/asm/clkdev.h
@@ -20,6 +20,10 @@ struct clk_lookup {
 	const char		*dev_id;
 	const char		*con_id;
 	struct clk		*clk;
+
+#if defined(CONFIG_PM_DEBUG) && defined(CONFIG_DEBUG_FS)
+	struct dentry           *dent;
+#endif
 };
 
 struct clk_lookup *clkdev_alloc(struct clk *clk, const char *con_id,
_______________________________________________
linaro-dev mailing list
linaro-dev@lists.linaro.org
http://lists.linaro.org/mailman/listinfo/linaro-dev

Reply via email to