Linux Audio

Check our new training course

Loading...
Note: File does not exist in v6.2.
  1/*
  2 * Generic OPP debugfs interface
  3 *
  4 * Copyright (C) 2015-2016 Viresh Kumar <viresh.kumar@linaro.org>
  5 *
  6 * This program is free software; you can redistribute it and/or modify
  7 * it under the terms of the GNU General Public License version 2 as
  8 * published by the Free Software Foundation.
  9 */
 10
 11#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 12
 13#include <linux/debugfs.h>
 14#include <linux/device.h>
 15#include <linux/err.h>
 16#include <linux/init.h>
 17#include <linux/limits.h>
 18
 19#include "opp.h"
 20
 21static struct dentry *rootdir;
 22
 23static void opp_set_dev_name(const struct device *dev, char *name)
 24{
 25	if (dev->parent)
 26		snprintf(name, NAME_MAX, "%s-%s", dev_name(dev->parent),
 27			 dev_name(dev));
 28	else
 29		snprintf(name, NAME_MAX, "%s", dev_name(dev));
 30}
 31
 32void opp_debug_remove_one(struct dev_pm_opp *opp)
 33{
 34	debugfs_remove_recursive(opp->dentry);
 35}
 36
 37int opp_debug_create_one(struct dev_pm_opp *opp, struct opp_table *opp_table)
 38{
 39	struct dentry *pdentry = opp_table->dentry;
 40	struct dentry *d;
 41	char name[25];	/* 20 chars for 64 bit value + 5 (opp:\0) */
 42
 43	/* Rate is unique to each OPP, use it to give opp-name */
 44	snprintf(name, sizeof(name), "opp:%lu", opp->rate);
 45
 46	/* Create per-opp directory */
 47	d = debugfs_create_dir(name, pdentry);
 48	if (!d)
 49		return -ENOMEM;
 50
 51	if (!debugfs_create_bool("available", S_IRUGO, d, &opp->available))
 52		return -ENOMEM;
 53
 54	if (!debugfs_create_bool("dynamic", S_IRUGO, d, &opp->dynamic))
 55		return -ENOMEM;
 56
 57	if (!debugfs_create_bool("turbo", S_IRUGO, d, &opp->turbo))
 58		return -ENOMEM;
 59
 60	if (!debugfs_create_bool("suspend", S_IRUGO, d, &opp->suspend))
 61		return -ENOMEM;
 62
 63	if (!debugfs_create_ulong("rate_hz", S_IRUGO, d, &opp->rate))
 64		return -ENOMEM;
 65
 66	if (!debugfs_create_ulong("u_volt_target", S_IRUGO, d, &opp->u_volt))
 67		return -ENOMEM;
 68
 69	if (!debugfs_create_ulong("u_volt_min", S_IRUGO, d, &opp->u_volt_min))
 70		return -ENOMEM;
 71
 72	if (!debugfs_create_ulong("u_volt_max", S_IRUGO, d, &opp->u_volt_max))
 73		return -ENOMEM;
 74
 75	if (!debugfs_create_ulong("u_amp", S_IRUGO, d, &opp->u_amp))
 76		return -ENOMEM;
 77
 78	if (!debugfs_create_ulong("clock_latency_ns", S_IRUGO, d,
 79				  &opp->clock_latency_ns))
 80		return -ENOMEM;
 81
 82	opp->dentry = d;
 83	return 0;
 84}
 85
 86static int opp_list_debug_create_dir(struct opp_device *opp_dev,
 87				     struct opp_table *opp_table)
 88{
 89	const struct device *dev = opp_dev->dev;
 90	struct dentry *d;
 91
 92	opp_set_dev_name(dev, opp_table->dentry_name);
 93
 94	/* Create device specific directory */
 95	d = debugfs_create_dir(opp_table->dentry_name, rootdir);
 96	if (!d) {
 97		dev_err(dev, "%s: Failed to create debugfs dir\n", __func__);
 98		return -ENOMEM;
 99	}
100
101	opp_dev->dentry = d;
102	opp_table->dentry = d;
103
104	return 0;
105}
106
107static int opp_list_debug_create_link(struct opp_device *opp_dev,
108				      struct opp_table *opp_table)
109{
110	const struct device *dev = opp_dev->dev;
111	char name[NAME_MAX];
112	struct dentry *d;
113
114	opp_set_dev_name(opp_dev->dev, name);
115
116	/* Create device specific directory link */
117	d = debugfs_create_symlink(name, rootdir, opp_table->dentry_name);
118	if (!d) {
119		dev_err(dev, "%s: Failed to create link\n", __func__);
120		return -ENOMEM;
121	}
122
123	opp_dev->dentry = d;
124
125	return 0;
126}
127
128/**
129 * opp_debug_register - add a device opp node to the debugfs 'opp' directory
130 * @opp_dev: opp-dev pointer for device
131 * @opp_table: the device-opp being added
132 *
133 * Dynamically adds device specific directory in debugfs 'opp' directory. If the
134 * device-opp is shared with other devices, then links will be created for all
135 * devices except the first.
136 *
137 * Return: 0 on success, otherwise negative error.
138 */
139int opp_debug_register(struct opp_device *opp_dev, struct opp_table *opp_table)
140{
141	if (!rootdir) {
142		pr_debug("%s: Uninitialized rootdir\n", __func__);
143		return -EINVAL;
144	}
145
146	if (opp_table->dentry)
147		return opp_list_debug_create_link(opp_dev, opp_table);
148
149	return opp_list_debug_create_dir(opp_dev, opp_table);
150}
151
152static void opp_migrate_dentry(struct opp_device *opp_dev,
153			       struct opp_table *opp_table)
154{
155	struct opp_device *new_dev;
156	const struct device *dev;
157	struct dentry *dentry;
158
159	/* Look for next opp-dev */
160	list_for_each_entry(new_dev, &opp_table->dev_list, node)
161		if (new_dev != opp_dev)
162			break;
163
164	/* new_dev is guaranteed to be valid here */
165	dev = new_dev->dev;
166	debugfs_remove_recursive(new_dev->dentry);
167
168	opp_set_dev_name(dev, opp_table->dentry_name);
169
170	dentry = debugfs_rename(rootdir, opp_dev->dentry, rootdir,
171				opp_table->dentry_name);
172	if (!dentry) {
173		dev_err(dev, "%s: Failed to rename link from: %s to %s\n",
174			__func__, dev_name(opp_dev->dev), dev_name(dev));
175		return;
176	}
177
178	new_dev->dentry = dentry;
179	opp_table->dentry = dentry;
180}
181
182/**
183 * opp_debug_unregister - remove a device opp node from debugfs opp directory
184 * @opp_dev: opp-dev pointer for device
185 * @opp_table: the device-opp being removed
186 *
187 * Dynamically removes device specific directory from debugfs 'opp' directory.
188 */
189void opp_debug_unregister(struct opp_device *opp_dev,
190			  struct opp_table *opp_table)
191{
192	if (opp_dev->dentry == opp_table->dentry) {
193		/* Move the real dentry object under another device */
194		if (!list_is_singular(&opp_table->dev_list)) {
195			opp_migrate_dentry(opp_dev, opp_table);
196			goto out;
197		}
198		opp_table->dentry = NULL;
199	}
200
201	debugfs_remove_recursive(opp_dev->dentry);
202
203out:
204	opp_dev->dentry = NULL;
205}
206
207static int __init opp_debug_init(void)
208{
209	/* Create /sys/kernel/debug/opp directory */
210	rootdir = debugfs_create_dir("opp", NULL);
211	if (!rootdir) {
212		pr_err("%s: Failed to create root directory\n", __func__);
213		return -ENOMEM;
214	}
215
216	return 0;
217}
218core_initcall(opp_debug_init);