Loading drivers/clk/clk.c +130 −1 Original line number Original line Diff line number Diff line Loading @@ -2513,6 +2513,14 @@ int clock_debug_print_clock(struct clk_core *c, struct seq_file *s) clock_debug_output(s, 0, "\t"); clock_debug_output(s, 0, "\t"); do { do { 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, clock_debug_output(s, 1, "%s%s:%u:%u [%ld]", start, clk->core->name, clk->core->name, clk->core->prepare_count, clk->core->prepare_count, Loading Loading @@ -2604,6 +2612,117 @@ static const struct file_operations clock_print_hw_fops = { .release = seq_release, .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) static int clk_debug_create_one(struct clk_core *core, struct dentry *pdentry) { { struct dentry *d; struct dentry *d; Loading @@ -2625,6 +2744,16 @@ static int clk_debug_create_one(struct clk_core *core, struct dentry *pdentry) if (!d) if (!d) goto err_out; goto err_out; if (core->ops->list_rate) { if (!debugfs_create_file("clk_list_rates", 0444, core->dentry, core, &list_rates_fops)) goto err_out; } if (core->vdd_class && !debugfs_create_file("clk_rate_max", 0444, core->dentry, core, &rate_max_fops)) goto err_out; d = debugfs_create_u32("clk_accuracy", 0444, core->dentry, d = debugfs_create_u32("clk_accuracy", 0444, core->dentry, (u32 *)&core->accuracy); (u32 *)&core->accuracy); if (!d) if (!d) Loading include/linux/clk-provider.h +6 −0 Original line number Original line Diff line number Diff line Loading @@ -184,6 +184,10 @@ struct clk_rate_request { * @list_registers: Queries the hardware to get the current register contents. * @list_registers: Queries the hardware to get the current register contents. * This callback is optional. * This callback is optional. * * * @list_rate: On success, return the nth supported frequency for a given * clock that is below rate_max. Return -ENXIO in case there is * no frequency table. * * The clk_enable/clk_disable and clk_prepare/clk_unprepare pairs allow * The clk_enable/clk_disable and clk_prepare/clk_unprepare pairs allow * implementations to split any work between atomic (enable) and sleepable * implementations to split any work between atomic (enable) and sleepable * (prepare) contexts. If enabling a clock requires code that might sleep, * (prepare) contexts. If enabling a clock requires code that might sleep, Loading Loading @@ -226,6 +230,8 @@ struct clk_ops { int (*set_flags)(struct clk_hw *hw, unsigned int flags); int (*set_flags)(struct clk_hw *hw, unsigned int flags); void (*list_registers)(struct seq_file *f, void (*list_registers)(struct seq_file *f, struct clk_hw *hw); struct clk_hw *hw); long (*list_rate)(struct clk_hw *hw, unsigned int n, unsigned long rate_max); }; }; /** /** Loading Loading
drivers/clk/clk.c +130 −1 Original line number Original line Diff line number Diff line Loading @@ -2513,6 +2513,14 @@ int clock_debug_print_clock(struct clk_core *c, struct seq_file *s) clock_debug_output(s, 0, "\t"); clock_debug_output(s, 0, "\t"); do { do { 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, clock_debug_output(s, 1, "%s%s:%u:%u [%ld]", start, clk->core->name, clk->core->name, clk->core->prepare_count, clk->core->prepare_count, Loading Loading @@ -2604,6 +2612,117 @@ static const struct file_operations clock_print_hw_fops = { .release = seq_release, .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) static int clk_debug_create_one(struct clk_core *core, struct dentry *pdentry) { { struct dentry *d; struct dentry *d; Loading @@ -2625,6 +2744,16 @@ static int clk_debug_create_one(struct clk_core *core, struct dentry *pdentry) if (!d) if (!d) goto err_out; goto err_out; if (core->ops->list_rate) { if (!debugfs_create_file("clk_list_rates", 0444, core->dentry, core, &list_rates_fops)) goto err_out; } if (core->vdd_class && !debugfs_create_file("clk_rate_max", 0444, core->dentry, core, &rate_max_fops)) goto err_out; d = debugfs_create_u32("clk_accuracy", 0444, core->dentry, d = debugfs_create_u32("clk_accuracy", 0444, core->dentry, (u32 *)&core->accuracy); (u32 *)&core->accuracy); if (!d) if (!d) Loading
include/linux/clk-provider.h +6 −0 Original line number Original line Diff line number Diff line Loading @@ -184,6 +184,10 @@ struct clk_rate_request { * @list_registers: Queries the hardware to get the current register contents. * @list_registers: Queries the hardware to get the current register contents. * This callback is optional. * This callback is optional. * * * @list_rate: On success, return the nth supported frequency for a given * clock that is below rate_max. Return -ENXIO in case there is * no frequency table. * * The clk_enable/clk_disable and clk_prepare/clk_unprepare pairs allow * The clk_enable/clk_disable and clk_prepare/clk_unprepare pairs allow * implementations to split any work between atomic (enable) and sleepable * implementations to split any work between atomic (enable) and sleepable * (prepare) contexts. If enabling a clock requires code that might sleep, * (prepare) contexts. If enabling a clock requires code that might sleep, Loading Loading @@ -226,6 +230,8 @@ struct clk_ops { int (*set_flags)(struct clk_hw *hw, unsigned int flags); int (*set_flags)(struct clk_hw *hw, unsigned int flags); void (*list_registers)(struct seq_file *f, void (*list_registers)(struct seq_file *f, struct clk_hw *hw); struct clk_hw *hw); long (*list_rate)(struct clk_hw *hw, unsigned int n, unsigned long rate_max); }; }; /** /** Loading