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