msm: clock: Allow registration of multiple MSM clock tables
If multiple clock drivers wish to use the framework in mach-msm/clock.c,
then it is necessary for additional clock tables to be registered after
msm_clock_init(). Add the msm_clock_register(table, size) API to
accomplish this. Unlike msm_clock_init(), msm_clock_register() can be
used multiple times and called after the kernel has finished booting.
To support debugfs features for clocks registered with the new API,
changes are required to debugfs initialization, which is now done as
part of msm_clock_init() rather than clock_late_init(). A linked list
is used to chain together the tables of clocks so their state can be
printed by clock_debug_print_enabled().
Change-Id: I55f1b804513d5d343f4a4ecf8858577db3759c9c
Signed-off-by: Matt Wagantall <mattw@codeaurora.org>
Signed-off-by: Neha Pandey <nehap@codeaurora.org>
diff --git a/arch/arm/mach-msm/clock-debug.c b/arch/arm/mach-msm/clock-debug.c
index c996ff4..e9b89f6 100644
--- a/arch/arm/mach-msm/clock-debug.c
+++ b/arch/arm/mach-msm/clock-debug.c
@@ -145,74 +145,39 @@
DEFINE_SIMPLE_ATTRIBUTE(clock_hwcg_fops, clock_debug_hwcg_get,
NULL, "%llu\n");
-static struct dentry *debugfs_base;
-static u32 debug_suspend;
-static struct clk_lookup *msm_clocks;
-static size_t num_msm_clocks;
-
-int __init clock_debug_init(struct clock_init_data *data)
+static int fmax_rates_show(struct seq_file *m, void *unused)
{
- debugfs_base = debugfs_create_dir("clk", NULL);
- if (!debugfs_base)
- return -ENOMEM;
- if (!debugfs_create_u32("debug_suspend", S_IRUGO | S_IWUSR,
- debugfs_base, &debug_suspend)) {
- debugfs_remove_recursive(debugfs_base);
- return -ENOMEM;
- }
- msm_clocks = data->table;
- num_msm_clocks = data->size;
+ struct clk *clock = m->private;
+ int level = 0;
- measure = clk_get_sys("debug", "measure");
- if (IS_ERR(measure))
- measure = NULL;
+ int vdd_level = find_vdd_level(clock, clock->rate);
+ if (vdd_level < 0) {
+ seq_printf(m, "could not find_vdd_level for %s, %ld\n",
+ clock->dbg_name, clock->rate);
+ return 0;
+ }
+ for (level = 0; level < ARRAY_SIZE(clock->fmax); level++) {
+ if (vdd_level == level)
+ seq_printf(m, "[%lu] ", clock->fmax[level]);
+ else
+ seq_printf(m, "%lu ", clock->fmax[level]);
+ }
+ seq_printf(m, "\n");
return 0;
}
-
-static int clock_debug_print_clock(struct clk *c)
+static int fmax_rates_open(struct inode *inode, struct file *file)
{
- char *start = "";
-
- if (!c || !c->prepare_count)
- return 0;
-
- pr_info("\t");
- do {
- if (c->vdd_class)
- pr_cont("%s%s:%u:%u [%ld, %lu]", start, c->dbg_name,
- c->prepare_count, c->count, c->rate,
- c->vdd_class->cur_level);
- else
- pr_cont("%s%s:%u:%u [%ld]", start, c->dbg_name,
- c->prepare_count, c->count, c->rate);
- start = " -> ";
- } while ((c = clk_get_parent(c)));
-
- pr_cont("\n");
-
- return 1;
+ return single_open(file, fmax_rates_show, inode->i_private);
}
-void clock_debug_print_enabled(void)
-{
- unsigned i;
- int cnt = 0;
-
- if (likely(!debug_suspend))
- return;
-
- pr_info("Enabled clocks:\n");
- for (i = 0; i < num_msm_clocks; i++)
- cnt += clock_debug_print_clock(msm_clocks[i].clk);
-
- if (cnt)
- pr_info("Enabled clock count: %d\n", cnt);
- else
- pr_info("No clocks enabled.\n");
-
-}
+static const struct file_operations fmax_rates_fops = {
+ .open = fmax_rates_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
static int list_rates_show(struct seq_file *m, void *unused)
{
@@ -252,41 +217,16 @@
.release = seq_release,
};
-static int fmax_rates_show(struct seq_file *m, void *unused)
-{
- struct clk *clock = m->private;
- int level = 0;
+static struct dentry *debugfs_base;
+static u32 debug_suspend;
- int vdd_level = find_vdd_level(clock, clock->rate);
- if (vdd_level < 0) {
- seq_printf(m, "could not find_vdd_level for %s, %ld\n",
- clock->dbg_name, clock->rate);
- return 0;
- }
- for (level = 0; level < ARRAY_SIZE(clock->fmax); level++) {
- if (vdd_level == level)
- seq_printf(m, "[%lu] ", clock->fmax[level]);
- else
- seq_printf(m, "%lu ", clock->fmax[level]);
- }
- seq_printf(m, "\n");
-
- return 0;
-}
-
-static int fmax_rates_open(struct inode *inode, struct file *file)
-{
- return single_open(file, fmax_rates_show, inode->i_private);
-}
-
-static const struct file_operations fmax_rates_fops = {
- .open = fmax_rates_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = seq_release,
+struct clk_table {
+ struct list_head node;
+ struct clk_lookup *clocks;
+ size_t num_clocks;
};
-int __init clock_debug_add(struct clk *clock)
+static int clock_debug_add(struct clk *clock)
{
char temp[50], *ptr;
struct dentry *clk_dir;
@@ -333,9 +273,118 @@
S_IRUGO, clk_dir, clock, &fmax_rates_fops))
goto error;
-
return 0;
error:
debugfs_remove_recursive(clk_dir);
return -ENOMEM;
}
+static LIST_HEAD(clk_list);
+static DEFINE_SPINLOCK(clk_list_lock);
+
+/**
+ * clock_debug_register() - Add additional clocks to clock debugfs hierarchy
+ * @table: Table of clocks to create debugfs nodes for
+ * @size: Size of @table
+ *
+ * Use this function to register additional clocks in debugfs. The clock debugfs
+ * hierarchy must have already been initialized with clock_debug_init() prior to
+ * calling this function. Unlike clock_debug_init(), this may be called multiple
+ * times with different clock lists and can be used after the kernel has
+ * finished booting.
+ */
+int clock_debug_register(struct clk_lookup *table, size_t size)
+{
+ struct clk_table *clk_table;
+ unsigned long flags;
+ int i;
+
+ clk_table = kmalloc(sizeof(*clk_table), GFP_KERNEL);
+ if (!clk_table)
+ return -ENOMEM;
+
+ clk_table->clocks = table;
+ clk_table->num_clocks = size;
+
+ spin_lock_irqsave(&clk_list_lock, flags);
+ list_add_tail(&clk_table->node, &clk_list);
+ spin_unlock_irqrestore(&clk_list_lock, flags);
+
+ for (i = 0; i < size; i++)
+ clock_debug_add(table[i].clk);
+
+ return 0;
+}
+
+/**
+ * clock_debug_init() - Initialize clock debugfs
+ */
+int __init clock_debug_init(void)
+{
+ debugfs_base = debugfs_create_dir("clk", NULL);
+ if (!debugfs_base)
+ return -ENOMEM;
+ if (!debugfs_create_u32("debug_suspend", S_IRUGO | S_IWUSR,
+ debugfs_base, &debug_suspend)) {
+ debugfs_remove_recursive(debugfs_base);
+ return -ENOMEM;
+ }
+
+ measure = clk_get_sys("debug", "measure");
+ if (IS_ERR(measure))
+ measure = NULL;
+
+ return 0;
+}
+
+static int clock_debug_print_clock(struct clk *c)
+{
+ char *start = "";
+
+ if (!c || !c->prepare_count)
+ return 0;
+
+ pr_info("\t");
+ do {
+ if (c->vdd_class)
+ pr_cont("%s%s:%u:%u [%ld, %lu]", start, c->dbg_name,
+ c->prepare_count, c->count, c->rate,
+ c->vdd_class->cur_level);
+ else
+ pr_cont("%s%s:%u:%u [%ld]", start, c->dbg_name,
+ c->prepare_count, c->count, c->rate);
+ start = " -> ";
+ } while ((c = clk_get_parent(c)));
+
+ pr_cont("\n");
+
+ return 1;
+}
+
+/**
+ * clock_debug_print_enabled() - Print names of enabled clocks for suspend debug
+ *
+ * Print the names of enabled clocks and their parents if debug_suspend is set
+ */
+void clock_debug_print_enabled(void)
+{
+ struct clk_table *table;
+ unsigned long flags;
+ int i, cnt = 0;
+
+ if (likely(!debug_suspend))
+ return;
+
+ pr_info("Enabled clocks:\n");
+ spin_lock_irqsave(&clk_list_lock, flags);
+ list_for_each_entry(table, &clk_list, node) {
+ for (i = 0; i < table->num_clocks; i++)
+ cnt += clock_debug_print_clock(table->clocks[i].clk);
+ }
+ spin_unlock_irqrestore(&clk_list_lock, flags);
+
+ if (cnt)
+ pr_info("Enabled clock count: %d\n", cnt);
+ else
+ pr_info("No clocks enabled.\n");
+
+}