diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 0d202b99ac94..edbb389e93aa 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -24,6 +24,7 @@ #include #include #include +#include #include "clk.h" @@ -2811,6 +2812,211 @@ static int clk_duty_cycle_show(struct seq_file *s, void *data) } DEFINE_SHOW_ATTRIBUTE(clk_duty_cycle); +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 int clock_available_parent_show(struct seq_file *s, void *data) +{ + struct clk_core *core = (struct clk_core *)s->private; + int i; + + for (i = 0; i < core->num_parents; i++) { + if (!core->parents[i]) + continue; + seq_printf(s, "%s ", core->parents[i]->name); + } + seq_puts(s, "\n"); + + return 0; +} + +static int clock_available_parent_open(struct inode *inode, struct file *file) +{ + return single_open(file, clock_available_parent_show, inode->i_private); +} + +static const struct file_operations clock_available_parent_fops = { + .open = clock_available_parent_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +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->parent; + + snprintf(name, sizeof(name), "%s\n", p ? p->name : "None\n"); + + return simple_read_from_buffer(ubuf, cnt, ppos, name, strlen(name)); +} + +static ssize_t clock_parent_write(struct file *filp, const char __user *buf, + size_t cnt, loff_t *ppos) +{ + char temp[256] = {0}; + char name[256] = {0}; + struct clk_core *core = filp->private_data; + unsigned int ret, i; + + if (copy_from_user(temp, buf, cnt)) + return -EINVAL; + + ret = sscanf(temp, "%s", name); + if (ret != 1) + return -EINVAL; + + for (i = 0; i < core->num_parents; i++) { + if (!core->parents[i]) + continue; + if (!strcmp(core->parents[i]->name, name)) { + if (core->parents[i] != core->parent) + clk_core_set_parent_nolock(core, + core->parents[i]); + break; + } + } + + return cnt; +} + +static const struct file_operations clock_parent_fops = { + .open = simple_open, + .read = clock_parent_read, + .write = clock_parent_write, +}; + +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_DEBUGFS_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) + +static 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_create_one(struct clk_core *core, struct dentry *pdentry) { struct dentry *root; @@ -2821,16 +3027,22 @@ static void clk_debug_create_one(struct clk_core *core, struct dentry *pdentry) root = debugfs_create_dir(core->name, pdentry); core->dentry = root; - debugfs_create_ulong("clk_rate", 0444, root, &core->rate); + debugfs_create_file("clk_rate", 0444, root, core, + &clock_rate_fops); debugfs_create_ulong("clk_accuracy", 0444, root, &core->accuracy); debugfs_create_u32("clk_phase", 0444, root, &core->phase); debugfs_create_file("clk_flags", 0444, root, core, &clk_flags_fops); debugfs_create_u32("clk_prepare_count", 0444, root, &core->prepare_count); - debugfs_create_u32("clk_enable_count", 0444, root, &core->enable_count); + debugfs_create_file("clk_enable_count", 0444, root, core, + &clock_enable_fops); debugfs_create_u32("clk_protect_count", 0444, root, &core->protect_count); debugfs_create_u32("clk_notifier_count", 0444, root, &core->notifier_count); debugfs_create_file("clk_duty_cycle", 0444, root, core, &clk_duty_cycle_fops); + debugfs_create_file("clk_available_parent", 0444, root, core, + &clock_available_parent_fops); + debugfs_create_file("clk_parent", 0444, root, core, + &clock_parent_fops); if (core->num_parents > 1) debugfs_create_file("clk_possible_parents", 0444, root, core, @@ -2898,6 +3110,9 @@ static int __init clk_debug_init(void) debugfs_create_file("clk_orphan_dump", 0444, rootdir, &orphan_list, &clk_dump_fops); + debugfs_create_file("clk_enabled_list", 0444, rootdir, + &clk_debug_list, &clk_enabled_list_fops); + mutex_lock(&clk_debug_lock); hlist_for_each_entry(core, &clk_debug_list, debug_node) clk_debug_create_one(core, rootdir);