clk: add/modify debugfs support for clocks

Update clock debugfs to support the below functionalities.
 - Allow enable/disable a clock.
 - Allow set_rate on a clock.
 - Display available parent of a clock.
 - Allow set_parent on a clock.
 - Display the list of enabled_clocks along with prepare_count,
   enable_count and rate.

Change-Id: Ib67b3a3409c9e7d8adb710bb524f54f543abf712
Signed-off-by: Taniya Das <tdas@codeaurora.org>
Signed-off-by: Tao Huang <huangtao@rock-chips.com>
Signed-off-by: Finley Xiao <finley.xiao@rock-chips.com>
Signed-off-by: Elaine Zhang <zhangqing@rock-chips.com>
This commit is contained in:
Taniya Das
2016-11-14 11:26:02 +05:30
committed by Tao Huang
parent 531fc5e280
commit ac2b539a8f

View File

@@ -24,6 +24,7 @@
#include <linux/pm_runtime.h>
#include <linux/sched.h>
#include <linux/clkdev.h>
#include <linux/uaccess.h>
#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);