From dba9af81b8765aa65fed6488b29eb4bbfe90267d Mon Sep 17 00:00:00 2001 From: Taniya Das Date: Mon, 14 Nov 2016 11:54:02 +0530 Subject: [PATCH] clk: Add support for list_rates ops for clocks Add support for clocks debugfs to be able to display - rates_max which indicates the frequency to voltage mapping of a clock. - list_rates, the list of clock frequencies supported by root clocks. - Also display the rate_max associated with enabled clocks list. Change-Id: I0a202af6f46c7cf164036d65487db5c40aab4063 Signed-off-by: Taniya Das --- drivers/clk/clk.c | 131 ++++++++++++++++++++++++++++++++++- include/linux/clk-provider.h | 6 ++ 2 files changed, 136 insertions(+), 1 deletion(-) diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 620b1c16ab21..deb935ce6a11 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -2502,7 +2502,15 @@ int clock_debug_print_clock(struct clk_core *c, struct seq_file *s) clock_debug_output(s, 0, "\t"); do { - clock_debug_output(s, 1, "%s%s:%u:%u [%ld]", start, + if (clk->core->vdd_class) + clock_debug_output(s, 1, "%s%s:%u:%u [%ld, %d]", start, + clk->core->name, + clk->core->prepare_count, + clk->core->enable_count, + clk->core->rate, + clk_find_vdd_level(clk->core, clk->core->rate)); + else + clock_debug_output(s, 1, "%s%s:%u:%u [%ld]", start, clk->core->name, clk->core->prepare_count, clk->core->enable_count, @@ -2593,6 +2601,117 @@ static const struct file_operations clock_print_hw_fops = { .release = seq_release, }; +static int list_rates_show(struct seq_file *s, void *unused) +{ + struct clk_core *core = s->private; + int level = 0, i = 0; + unsigned long rate, rate_max = 0; + + /* Find max frequency supported within voltage constraints. */ + if (!core->vdd_class) { + rate_max = ULONG_MAX; + } else { + for (level = 0; level < core->num_rate_max; level++) + if (core->rate_max[level]) + rate_max = core->rate_max[level]; + } + + /* + * List supported frequencies <= rate_max. Higher frequencies may + * appear in the frequency table, but are not valid and should not + * be listed. + */ + while (!IS_ERR_VALUE(rate = + core->ops->list_rate(core->hw, i++, rate_max))) { + if (rate <= 0) + break; + if (rate <= rate_max) + seq_printf(s, "%lu\n", rate); + } + + return 0; +} + +static int list_rates_open(struct inode *inode, struct file *file) +{ + return single_open(file, list_rates_show, inode->i_private); +} + +static const struct file_operations list_rates_fops = { + .open = list_rates_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +static void clock_print_rate_max_by_level(struct seq_file *s, int level) +{ + struct clk_core *core = s->private; + struct clk_vdd_class *vdd_class = core->vdd_class; + int off, i, vdd_level, nregs = vdd_class->num_regulators; + + vdd_level = clk_find_vdd_level(core, core->rate); + + seq_printf(s, "%2s%10lu", vdd_level == level ? "[" : "", + core->rate_max[level]); + + for (i = 0; i < nregs; i++) { + off = nregs*level + i; + if (vdd_class->vdd_uv) + seq_printf(s, "%10u", vdd_class->vdd_uv[off]); + } + + if (vdd_level == level) + seq_puts(s, "]"); + + seq_puts(s, "\n"); +} + +static int rate_max_show(struct seq_file *s, void *unused) +{ + struct clk_core *core = s->private; + struct clk_vdd_class *vdd_class = core->vdd_class; + int level = 0, i, nregs = vdd_class->num_regulators; + char reg_name[10]; + + int vdd_level = clk_find_vdd_level(core, core->rate); + + if (vdd_level < 0) { + seq_printf(s, "could not find_vdd_level for %s, %ld\n", + core->name, core->rate); + return 0; + } + + seq_printf(s, "%12s", ""); + for (i = 0; i < nregs; i++) { + snprintf(reg_name, ARRAY_SIZE(reg_name), "reg %d", i); + seq_printf(s, "%10s", reg_name); + } + + seq_printf(s, "\n%12s", "freq"); + for (i = 0; i < nregs; i++) + seq_printf(s, "%10s", "uV"); + + seq_puts(s, "\n"); + + for (level = 0; level < core->num_rate_max; level++) + clock_print_rate_max_by_level(s, level); + + return 0; +} + +static int rate_max_open(struct inode *inode, struct file *file) +{ + return single_open(file, rate_max_show, inode->i_private); +} + +static const struct file_operations rate_max_fops = { + .open = rate_max_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + static int clk_debug_create_one(struct clk_core *core, struct dentry *pdentry) { struct dentry *d; @@ -2614,6 +2733,16 @@ static int clk_debug_create_one(struct clk_core *core, struct dentry *pdentry) if (!d) goto err_out; + if (core->ops->list_rate) { + if (!debugfs_create_file("clk_list_rates", + S_IRUGO, core->dentry, core, &list_rates_fops)) + goto err_out; + } + + if (core->vdd_class && !debugfs_create_file("clk_rate_max", + S_IRUGO, core->dentry, core, &rate_max_fops)) + goto err_out; + d = debugfs_create_u32("clk_accuracy", S_IRUGO, core->dentry, (u32 *)&core->accuracy); if (!d) diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 2a4047c049a8..fd2eb059b991 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -181,6 +181,10 @@ struct clk_rate_request { * This callback is optional and required clocks could * add this callback. * + * @list_rate: Return the nth supported frequency for a given clock which is + * below rate_max on success and -ENXIO in case of no frequency + * table. + * * The clk_enable/clk_disable and clk_prepare/clk_unprepare pairs allow * implementations to split any work between atomic (enable) and sleepable * (prepare) contexts. If enabling a clock requires code that might sleep, @@ -223,6 +227,8 @@ struct clk_ops { int (*set_flags)(struct clk_hw *hw, unsigned flags); void (*list_registers)(struct seq_file *f, struct clk_hw *hw); + long (*list_rate)(struct clk_hw *hw, unsigned n, + unsigned long rate_max); }; /**