Merge "msm: clock: Allow registration of multiple MSM clock tables"
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");
+
+}
diff --git a/arch/arm/mach-msm/clock.c b/arch/arm/mach-msm/clock.c
index 27f2405..48cb5be 100644
--- a/arch/arm/mach-msm/clock.c
+++ b/arch/arm/mach-msm/clock.c
@@ -412,7 +412,32 @@
 }
 EXPORT_SYMBOL(clk_set_flags);
 
-static struct clock_init_data __initdata *clk_init_data;
+static struct clock_init_data *clk_init_data;
+
+/**
+ * msm_clock_register() - Register additional clock tables
+ * @table: Table of clocks
+ * @size: Size of @table
+ *
+ * Upon return, clock APIs may be used to control clocks registered using this
+ * function. This API may only be used after msm_clock_init() has completed.
+ * Unlike msm_clock_init(), this function may be called multiple times with
+ * different clock lists and used after the kernel has finished booting.
+ */
+int msm_clock_register(struct clk_lookup *table, size_t size)
+{
+	if (!clk_init_data)
+		return -ENODEV;
+
+	if (!table)
+		return -EINVAL;
+
+	clkdev_add_table(table, size);
+	clock_debug_register(table, size);
+
+	return 0;
+}
+EXPORT_SYMBOL(msm_clock_register);
 
 static enum handoff __init __handoff_clk(struct clk *clk)
 {
@@ -466,13 +491,23 @@
 	return ret;
 }
 
-void __init msm_clock_init(struct clock_init_data *data)
+/**
+ * msm_clock_init() - Register and initialize a clock driver
+ * @data: Driver-specific clock initialization data
+ *
+ * Upon return from this call, clock APIs may be used to control
+ * clocks registered with this API.
+ */
+int __init msm_clock_init(struct clock_init_data *data)
 {
 	unsigned n;
 	struct clk_lookup *clock_tbl;
 	size_t num_clocks;
 	struct clk *clk;
 
+	if (!data)
+		return -EINVAL;
+
 	clk_init_data = data;
 	if (clk_init_data->pre_init)
 		clk_init_data->pre_init();
@@ -499,16 +534,17 @@
 
 	if (clk_init_data->post_init)
 		clk_init_data->post_init();
+
+	clock_debug_init();
+	clock_debug_register(clock_tbl, num_clocks);
+
+	return 0;
 }
 
 static int __init clock_late_init(void)
 {
 	struct handoff_clk *h, *h_temp;
-	int n, ret = 0;
-
-	clock_debug_init(clk_init_data);
-	for (n = 0; n < clk_init_data->size; n++)
-		clock_debug_add(clk_init_data->table[n].clk);
+	int ret = 0;
 
 	pr_info("%s: Removing enables held for handed-off clocks\n", __func__);
 	list_for_each_entry_safe(h, h_temp, &handoff_list, list) {
diff --git a/arch/arm/mach-msm/clock.h b/arch/arm/mach-msm/clock.h
index 45d2f71..ca96ad3 100644
--- a/arch/arm/mach-msm/clock.h
+++ b/arch/arm/mach-msm/clock.h
@@ -171,18 +171,22 @@
 extern struct clock_init_data msm8974_clock_init_data;
 extern struct clock_init_data msm8974_rumi_clock_init_data;
 
-void msm_clock_init(struct clock_init_data *data);
+int msm_clock_init(struct clock_init_data *data);
+int msm_clock_register(struct clk_lookup *table, size_t size);
 int vote_vdd_level(struct clk_vdd_class *vdd_class, int level);
 int unvote_vdd_level(struct clk_vdd_class *vdd_class, int level);
 int find_vdd_level(struct clk *clk, unsigned long rate);
 
 #ifdef CONFIG_DEBUG_FS
-int clock_debug_init(struct clock_init_data *data);
-int clock_debug_add(struct clk *clock);
+int clock_debug_init(void);
+int clock_debug_register(struct clk_lookup *t, size_t s);
 void clock_debug_print_enabled(void);
 #else
-static inline int clock_debug_init(struct clock_init_data *data) { return 0; }
-static inline int clock_debug_add(struct clk *clock) { return 0; }
+static inline int clock_debug_init(void) { return 0; }
+static inline int clock_debug_register(struct clk_lookup *t, size_t s)
+{
+	return 0;
+}
 static inline void clock_debug_print_enabled(void) { return; }
 #endif