Loading drivers/clk/clk.c +212 −13 Original line number Diff line number Diff line Loading @@ -2420,6 +2420,190 @@ static const struct file_operations clk_dump_fops = { .release = single_release, }; static int clock_debug_rate_set(void *data, u64 val) { struct clk_core *core = data; int ret; ret = clk_set_rate(core->hw->clk, val); if (ret) pr_err("clk_set_rate(%lu) failed (%d)\n", (unsigned long)val, ret); return ret; } static int clock_debug_rate_get(void *data, u64 *val) { struct clk_core *core = data; *val = core->hw->core->rate; return 0; } DEFINE_SIMPLE_ATTRIBUTE(clock_rate_fops, clock_debug_rate_get, clock_debug_rate_set, "%llu\n"); static ssize_t clock_parent_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos) { char name[256] = {0}; struct clk_core *core = filp->private_data; struct clk_core *p = core->hw->core->parent; snprintf(name, sizeof(name), "%s\n", p ? p->name : "None\n"); return simple_read_from_buffer(ubuf, cnt, ppos, name, strlen(name)); } static const struct file_operations clock_parent_fops = { .open = simple_open, .read = clock_parent_read, }; static int clock_debug_enable_set(void *data, u64 val) { struct clk_core *core = data; int rc = 0; if (val) rc = clk_prepare_enable(core->hw->clk); else clk_disable_unprepare(core->hw->clk); return rc; } static int clock_debug_enable_get(void *data, u64 *val) { struct clk_core *core = data; int enabled = 0; enabled = core->enable_count; *val = enabled; return 0; } DEFINE_SIMPLE_ATTRIBUTE(clock_enable_fops, clock_debug_enable_get, clock_debug_enable_set, "%lld\n"); #define clock_debug_output(m, c, fmt, ...) \ do { \ if (m) \ seq_printf(m, fmt, ##__VA_ARGS__); \ else if (c) \ pr_cont(fmt, ##__VA_ARGS__); \ else \ pr_info(fmt, ##__VA_ARGS__); \ } while (0) int clock_debug_print_clock(struct clk_core *c, struct seq_file *s) { char *start = ""; struct clk *clk; if (!c || !c->prepare_count) return 0; clk = c->hw->clk; clock_debug_output(s, 0, "\t"); do { clock_debug_output(s, 1, "%s%s:%u:%u [%ld]", start, clk->core->name, clk->core->prepare_count, clk->core->enable_count, clk->core->rate); start = " -> "; } while ((clk = clk_get_parent(clk))); clock_debug_output(s, 1, "\n"); return 1; } /* * clock_debug_print_enabled_clocks() - Print names of enabled clocks */ static void clock_debug_print_enabled_clocks(struct seq_file *s) { struct clk_core *core; int cnt = 0; clock_debug_output(s, 0, "Enabled clocks:\n"); mutex_lock(&clk_debug_lock); hlist_for_each_entry(core, &clk_debug_list, debug_node) cnt += clock_debug_print_clock(core, s); mutex_unlock(&clk_debug_lock); if (cnt) clock_debug_output(s, 0, "Enabled clock count: %d\n", cnt); else clock_debug_output(s, 0, "No clocks enabled.\n"); } static int enabled_clocks_show(struct seq_file *s, void *unused) { clock_debug_print_enabled_clocks(s); return 0; } static int enabled_clocks_open(struct inode *inode, struct file *file) { return single_open(file, enabled_clocks_show, inode->i_private); } static const struct file_operations clk_enabled_list_fops = { .open = enabled_clocks_open, .read = seq_read, .llseek = seq_lseek, .release = seq_release, }; static void clk_debug_print_hw(struct clk_core *clk, struct seq_file *f) { if (IS_ERR_OR_NULL(clk)) return; clk_debug_print_hw(clk->parent, f); clock_debug_output(f, false, "%s\n", clk->name); if (!clk->ops->list_registers) return; clk->ops->list_registers(f, clk->hw); } static int print_hw_show(struct seq_file *m, void *unused) { struct clk_core *c = m->private; clk_debug_print_hw(c, m); return 0; } static int print_hw_open(struct inode *inode, struct file *file) { return single_open(file, print_hw_show, inode->i_private); } static const struct file_operations clock_print_hw_fops = { .open = print_hw_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; Loading @@ -2436,41 +2620,51 @@ static int clk_debug_create_one(struct clk_core *core, struct dentry *pdentry) core->dentry = d; d = debugfs_create_u32("clk_rate", S_IRUGO, core->dentry, (u32 *)&core->rate); d = debugfs_create_file("clk_rate", 0444, core->dentry, core, &clock_rate_fops); if (!d) goto err_out; d = debugfs_create_u32("clk_accuracy", S_IRUGO, core->dentry, d = debugfs_create_u32("clk_accuracy", 0444, core->dentry, (u32 *)&core->accuracy); if (!d) goto err_out; d = debugfs_create_u32("clk_phase", S_IRUGO, core->dentry, d = debugfs_create_u32("clk_phase", 0444, core->dentry, (u32 *)&core->phase); if (!d) goto err_out; d = debugfs_create_x32("clk_flags", S_IRUGO, core->dentry, d = debugfs_create_x32("clk_flags", 0444, core->dentry, (u32 *)&core->flags); if (!d) goto err_out; d = debugfs_create_u32("clk_prepare_count", S_IRUGO, core->dentry, d = debugfs_create_u32("clk_prepare_count", 0444, core->dentry, (u32 *)&core->prepare_count); if (!d) goto err_out; d = debugfs_create_u32("clk_enable_count", S_IRUGO, core->dentry, (u32 *)&core->enable_count); d = debugfs_create_file("clk_enable_count", 0444, core->dentry, core, &clock_enable_fops); if (!d) goto err_out; d = debugfs_create_u32("clk_notifier_count", S_IRUGO, core->dentry, d = debugfs_create_u32("clk_notifier_count", 0444, core->dentry, (u32 *)&core->notifier_count); if (!d) goto err_out; d = debugfs_create_file("clk_parent", 0444, core->dentry, core, &clock_parent_fops); if (!d) goto err_out; d = debugfs_create_file("clk_print_regs", 0444, core->dentry, core, &clock_print_hw_fops); if (!d) goto err_out; if (core->ops->debug_init) { ret = core->ops->debug_init(core->hw, core->dentry); if (ret) Loading Loading @@ -2561,26 +2755,31 @@ static int __init clk_debug_init(void) if (!rootdir) return -ENOMEM; d = debugfs_create_file("clk_summary", S_IRUGO, rootdir, &all_lists, d = debugfs_create_file("clk_summary", 0444, rootdir, &all_lists, &clk_summary_fops); if (!d) return -ENOMEM; d = debugfs_create_file("clk_dump", S_IRUGO, rootdir, &all_lists, d = debugfs_create_file("clk_dump", 0444, rootdir, &all_lists, &clk_dump_fops); if (!d) return -ENOMEM; d = debugfs_create_file("clk_orphan_summary", S_IRUGO, rootdir, d = debugfs_create_file("clk_orphan_summary", 0444, rootdir, &orphan_list, &clk_summary_fops); if (!d) return -ENOMEM; d = debugfs_create_file("clk_orphan_dump", S_IRUGO, rootdir, d = debugfs_create_file("clk_orphan_dump", 0444, rootdir, &orphan_list, &clk_dump_fops); if (!d) return -ENOMEM; d = debugfs_create_file("clk_enabled_list", 0444, rootdir, &clk_debug_list, &clk_enabled_list_fops); if (!d) return -ENOMEM; mutex_lock(&clk_debug_lock); hlist_for_each_entry(core, &clk_debug_list, debug_node) clk_debug_create_one(core, rootdir); Loading include/linux/clk-provider.h +5 −0 Original line number Diff line number Diff line Loading @@ -181,6 +181,9 @@ struct clk_rate_request { * @set_flags: Set custom flags which deal with hardware specifics. Returns 0 * on success, -EERROR otherwise. * * @list_registers: Queries the hardware to get the current register contents. * This callback is optional. * * 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, Loading Loading @@ -221,6 +224,8 @@ struct clk_ops { void (*init)(struct clk_hw *hw); int (*debug_init)(struct clk_hw *hw, struct dentry *dentry); int (*set_flags)(struct clk_hw *hw, unsigned int flags); void (*list_registers)(struct seq_file *f, struct clk_hw *hw); }; /** Loading Loading
drivers/clk/clk.c +212 −13 Original line number Diff line number Diff line Loading @@ -2420,6 +2420,190 @@ static const struct file_operations clk_dump_fops = { .release = single_release, }; static int clock_debug_rate_set(void *data, u64 val) { struct clk_core *core = data; int ret; ret = clk_set_rate(core->hw->clk, val); if (ret) pr_err("clk_set_rate(%lu) failed (%d)\n", (unsigned long)val, ret); return ret; } static int clock_debug_rate_get(void *data, u64 *val) { struct clk_core *core = data; *val = core->hw->core->rate; return 0; } DEFINE_SIMPLE_ATTRIBUTE(clock_rate_fops, clock_debug_rate_get, clock_debug_rate_set, "%llu\n"); static ssize_t clock_parent_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos) { char name[256] = {0}; struct clk_core *core = filp->private_data; struct clk_core *p = core->hw->core->parent; snprintf(name, sizeof(name), "%s\n", p ? p->name : "None\n"); return simple_read_from_buffer(ubuf, cnt, ppos, name, strlen(name)); } static const struct file_operations clock_parent_fops = { .open = simple_open, .read = clock_parent_read, }; static int clock_debug_enable_set(void *data, u64 val) { struct clk_core *core = data; int rc = 0; if (val) rc = clk_prepare_enable(core->hw->clk); else clk_disable_unprepare(core->hw->clk); return rc; } static int clock_debug_enable_get(void *data, u64 *val) { struct clk_core *core = data; int enabled = 0; enabled = core->enable_count; *val = enabled; return 0; } DEFINE_SIMPLE_ATTRIBUTE(clock_enable_fops, clock_debug_enable_get, clock_debug_enable_set, "%lld\n"); #define clock_debug_output(m, c, fmt, ...) \ do { \ if (m) \ seq_printf(m, fmt, ##__VA_ARGS__); \ else if (c) \ pr_cont(fmt, ##__VA_ARGS__); \ else \ pr_info(fmt, ##__VA_ARGS__); \ } while (0) int clock_debug_print_clock(struct clk_core *c, struct seq_file *s) { char *start = ""; struct clk *clk; if (!c || !c->prepare_count) return 0; clk = c->hw->clk; clock_debug_output(s, 0, "\t"); do { clock_debug_output(s, 1, "%s%s:%u:%u [%ld]", start, clk->core->name, clk->core->prepare_count, clk->core->enable_count, clk->core->rate); start = " -> "; } while ((clk = clk_get_parent(clk))); clock_debug_output(s, 1, "\n"); return 1; } /* * clock_debug_print_enabled_clocks() - Print names of enabled clocks */ static void clock_debug_print_enabled_clocks(struct seq_file *s) { struct clk_core *core; int cnt = 0; clock_debug_output(s, 0, "Enabled clocks:\n"); mutex_lock(&clk_debug_lock); hlist_for_each_entry(core, &clk_debug_list, debug_node) cnt += clock_debug_print_clock(core, s); mutex_unlock(&clk_debug_lock); if (cnt) clock_debug_output(s, 0, "Enabled clock count: %d\n", cnt); else clock_debug_output(s, 0, "No clocks enabled.\n"); } static int enabled_clocks_show(struct seq_file *s, void *unused) { clock_debug_print_enabled_clocks(s); return 0; } static int enabled_clocks_open(struct inode *inode, struct file *file) { return single_open(file, enabled_clocks_show, inode->i_private); } static const struct file_operations clk_enabled_list_fops = { .open = enabled_clocks_open, .read = seq_read, .llseek = seq_lseek, .release = seq_release, }; static void clk_debug_print_hw(struct clk_core *clk, struct seq_file *f) { if (IS_ERR_OR_NULL(clk)) return; clk_debug_print_hw(clk->parent, f); clock_debug_output(f, false, "%s\n", clk->name); if (!clk->ops->list_registers) return; clk->ops->list_registers(f, clk->hw); } static int print_hw_show(struct seq_file *m, void *unused) { struct clk_core *c = m->private; clk_debug_print_hw(c, m); return 0; } static int print_hw_open(struct inode *inode, struct file *file) { return single_open(file, print_hw_show, inode->i_private); } static const struct file_operations clock_print_hw_fops = { .open = print_hw_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; Loading @@ -2436,41 +2620,51 @@ static int clk_debug_create_one(struct clk_core *core, struct dentry *pdentry) core->dentry = d; d = debugfs_create_u32("clk_rate", S_IRUGO, core->dentry, (u32 *)&core->rate); d = debugfs_create_file("clk_rate", 0444, core->dentry, core, &clock_rate_fops); if (!d) goto err_out; d = debugfs_create_u32("clk_accuracy", S_IRUGO, core->dentry, d = debugfs_create_u32("clk_accuracy", 0444, core->dentry, (u32 *)&core->accuracy); if (!d) goto err_out; d = debugfs_create_u32("clk_phase", S_IRUGO, core->dentry, d = debugfs_create_u32("clk_phase", 0444, core->dentry, (u32 *)&core->phase); if (!d) goto err_out; d = debugfs_create_x32("clk_flags", S_IRUGO, core->dentry, d = debugfs_create_x32("clk_flags", 0444, core->dentry, (u32 *)&core->flags); if (!d) goto err_out; d = debugfs_create_u32("clk_prepare_count", S_IRUGO, core->dentry, d = debugfs_create_u32("clk_prepare_count", 0444, core->dentry, (u32 *)&core->prepare_count); if (!d) goto err_out; d = debugfs_create_u32("clk_enable_count", S_IRUGO, core->dentry, (u32 *)&core->enable_count); d = debugfs_create_file("clk_enable_count", 0444, core->dentry, core, &clock_enable_fops); if (!d) goto err_out; d = debugfs_create_u32("clk_notifier_count", S_IRUGO, core->dentry, d = debugfs_create_u32("clk_notifier_count", 0444, core->dentry, (u32 *)&core->notifier_count); if (!d) goto err_out; d = debugfs_create_file("clk_parent", 0444, core->dentry, core, &clock_parent_fops); if (!d) goto err_out; d = debugfs_create_file("clk_print_regs", 0444, core->dentry, core, &clock_print_hw_fops); if (!d) goto err_out; if (core->ops->debug_init) { ret = core->ops->debug_init(core->hw, core->dentry); if (ret) Loading Loading @@ -2561,26 +2755,31 @@ static int __init clk_debug_init(void) if (!rootdir) return -ENOMEM; d = debugfs_create_file("clk_summary", S_IRUGO, rootdir, &all_lists, d = debugfs_create_file("clk_summary", 0444, rootdir, &all_lists, &clk_summary_fops); if (!d) return -ENOMEM; d = debugfs_create_file("clk_dump", S_IRUGO, rootdir, &all_lists, d = debugfs_create_file("clk_dump", 0444, rootdir, &all_lists, &clk_dump_fops); if (!d) return -ENOMEM; d = debugfs_create_file("clk_orphan_summary", S_IRUGO, rootdir, d = debugfs_create_file("clk_orphan_summary", 0444, rootdir, &orphan_list, &clk_summary_fops); if (!d) return -ENOMEM; d = debugfs_create_file("clk_orphan_dump", S_IRUGO, rootdir, d = debugfs_create_file("clk_orphan_dump", 0444, rootdir, &orphan_list, &clk_dump_fops); if (!d) return -ENOMEM; d = debugfs_create_file("clk_enabled_list", 0444, rootdir, &clk_debug_list, &clk_enabled_list_fops); if (!d) return -ENOMEM; mutex_lock(&clk_debug_lock); hlist_for_each_entry(core, &clk_debug_list, debug_node) clk_debug_create_one(core, rootdir); Loading
include/linux/clk-provider.h +5 −0 Original line number Diff line number Diff line Loading @@ -181,6 +181,9 @@ struct clk_rate_request { * @set_flags: Set custom flags which deal with hardware specifics. Returns 0 * on success, -EERROR otherwise. * * @list_registers: Queries the hardware to get the current register contents. * This callback is optional. * * 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, Loading Loading @@ -221,6 +224,8 @@ struct clk_ops { void (*init)(struct clk_hw *hw); int (*debug_init)(struct clk_hw *hw, struct dentry *dentry); int (*set_flags)(struct clk_hw *hw, unsigned int flags); void (*list_registers)(struct seq_file *f, struct clk_hw *hw); }; /** Loading