Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 37cb6a6d authored by Kyle Yan's avatar Kyle Yan Committed by Gerrit - the friendly Code Review server
Browse files

Merge "clk: add/modify debugfs support for clocks" into msm-4.8

parents ce81e103 2dd2572c
Loading
Loading
Loading
Loading
+212 −13
Original line number Diff line number Diff line
@@ -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;
@@ -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)
@@ -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);
+5 −0
Original line number Diff line number Diff line
@@ -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,
@@ -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);
};

/**