1 // SPDX-License-Identifier: GPL-2.0-only
3 * Generic OPP debugfs interface
5 * Copyright (C) 2015-2016 Viresh Kumar <viresh.kumar@linaro.org>
8 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
10 #include <linux/debugfs.h>
11 #include <linux/device.h>
12 #include <linux/err.h>
13 #include <linux/init.h>
14 #include <linux/limits.h>
15 #include <linux/slab.h>
19 static struct dentry
*rootdir
;
21 static void opp_set_dev_name(const struct device
*dev
, char *name
)
24 snprintf(name
, NAME_MAX
, "%s-%s", dev_name(dev
->parent
),
27 snprintf(name
, NAME_MAX
, "%s", dev_name(dev
));
30 void opp_debug_remove_one(struct dev_pm_opp
*opp
)
32 debugfs_remove_recursive(opp
->dentry
);
35 static ssize_t
bw_name_read(struct file
*fp
, char __user
*userbuf
,
36 size_t count
, loff_t
*ppos
)
38 struct icc_path
*path
= fp
->private_data
;
42 i
= scnprintf(buf
, sizeof(buf
), "%.62s\n", icc_get_name(path
));
44 return simple_read_from_buffer(userbuf
, count
, ppos
, buf
, i
);
47 static const struct file_operations bw_name_fops
= {
50 .llseek
= default_llseek
,
53 static void opp_debug_create_bw(struct dev_pm_opp
*opp
,
54 struct opp_table
*opp_table
,
55 struct dentry
*pdentry
)
61 for (i
= 0; i
< opp_table
->path_count
; i
++) {
62 snprintf(name
, sizeof(name
), "icc-path-%.1d", i
);
64 /* Create per-path directory */
65 d
= debugfs_create_dir(name
, pdentry
);
67 debugfs_create_file("name", S_IRUGO
, d
, opp_table
->paths
[i
],
69 debugfs_create_u32("peak_bw", S_IRUGO
, d
,
70 &opp
->bandwidth
[i
].peak
);
71 debugfs_create_u32("avg_bw", S_IRUGO
, d
,
72 &opp
->bandwidth
[i
].avg
);
76 static void opp_debug_create_supplies(struct dev_pm_opp
*opp
,
77 struct opp_table
*opp_table
,
78 struct dentry
*pdentry
)
83 for (i
= 0; i
< opp_table
->regulator_count
; i
++) {
86 snprintf(name
, sizeof(name
), "supply-%d", i
);
88 /* Create per-opp directory */
89 d
= debugfs_create_dir(name
, pdentry
);
91 debugfs_create_ulong("u_volt_target", S_IRUGO
, d
,
92 &opp
->supplies
[i
].u_volt
);
94 debugfs_create_ulong("u_volt_min", S_IRUGO
, d
,
95 &opp
->supplies
[i
].u_volt_min
);
97 debugfs_create_ulong("u_volt_max", S_IRUGO
, d
,
98 &opp
->supplies
[i
].u_volt_max
);
100 debugfs_create_ulong("u_amp", S_IRUGO
, d
,
101 &opp
->supplies
[i
].u_amp
);
105 void opp_debug_create_one(struct dev_pm_opp
*opp
, struct opp_table
*opp_table
)
107 struct dentry
*pdentry
= opp_table
->dentry
;
110 char name
[25]; /* 20 chars for 64 bit value + 5 (opp:\0) */
113 * Get directory name for OPP.
115 * - Normally rate is unique to each OPP, use it to get unique opp-name.
116 * - For some devices rate isn't available, use index instead.
118 if (likely(opp
->rate
))
121 id
= _get_opp_count(opp_table
);
123 snprintf(name
, sizeof(name
), "opp:%lu", id
);
125 /* Create per-opp directory */
126 d
= debugfs_create_dir(name
, pdentry
);
128 debugfs_create_bool("available", S_IRUGO
, d
, &opp
->available
);
129 debugfs_create_bool("dynamic", S_IRUGO
, d
, &opp
->dynamic
);
130 debugfs_create_bool("turbo", S_IRUGO
, d
, &opp
->turbo
);
131 debugfs_create_bool("suspend", S_IRUGO
, d
, &opp
->suspend
);
132 debugfs_create_u32("performance_state", S_IRUGO
, d
, &opp
->pstate
);
133 debugfs_create_ulong("rate_hz", S_IRUGO
, d
, &opp
->rate
);
134 debugfs_create_ulong("clock_latency_ns", S_IRUGO
, d
,
135 &opp
->clock_latency_ns
);
137 opp_debug_create_supplies(opp
, opp_table
, d
);
138 opp_debug_create_bw(opp
, opp_table
, d
);
143 static void opp_list_debug_create_dir(struct opp_device
*opp_dev
,
144 struct opp_table
*opp_table
)
146 const struct device
*dev
= opp_dev
->dev
;
149 opp_set_dev_name(dev
, opp_table
->dentry_name
);
151 /* Create device specific directory */
152 d
= debugfs_create_dir(opp_table
->dentry_name
, rootdir
);
155 opp_table
->dentry
= d
;
158 static void opp_list_debug_create_link(struct opp_device
*opp_dev
,
159 struct opp_table
*opp_table
)
163 opp_set_dev_name(opp_dev
->dev
, name
);
165 /* Create device specific directory link */
166 opp_dev
->dentry
= debugfs_create_symlink(name
, rootdir
,
167 opp_table
->dentry_name
);
171 * opp_debug_register - add a device opp node to the debugfs 'opp' directory
172 * @opp_dev: opp-dev pointer for device
173 * @opp_table: the device-opp being added
175 * Dynamically adds device specific directory in debugfs 'opp' directory. If the
176 * device-opp is shared with other devices, then links will be created for all
177 * devices except the first.
179 void opp_debug_register(struct opp_device
*opp_dev
, struct opp_table
*opp_table
)
181 if (opp_table
->dentry
)
182 opp_list_debug_create_link(opp_dev
, opp_table
);
184 opp_list_debug_create_dir(opp_dev
, opp_table
);
187 static void opp_migrate_dentry(struct opp_device
*opp_dev
,
188 struct opp_table
*opp_table
)
190 struct opp_device
*new_dev
;
191 const struct device
*dev
;
192 struct dentry
*dentry
;
194 /* Look for next opp-dev */
195 list_for_each_entry(new_dev
, &opp_table
->dev_list
, node
)
196 if (new_dev
!= opp_dev
)
199 /* new_dev is guaranteed to be valid here */
201 debugfs_remove_recursive(new_dev
->dentry
);
203 opp_set_dev_name(dev
, opp_table
->dentry_name
);
205 dentry
= debugfs_rename(rootdir
, opp_dev
->dentry
, rootdir
,
206 opp_table
->dentry_name
);
208 dev_err(dev
, "%s: Failed to rename link from: %s to %s\n",
209 __func__
, dev_name(opp_dev
->dev
), dev_name(dev
));
213 new_dev
->dentry
= dentry
;
214 opp_table
->dentry
= dentry
;
218 * opp_debug_unregister - remove a device opp node from debugfs opp directory
219 * @opp_dev: opp-dev pointer for device
220 * @opp_table: the device-opp being removed
222 * Dynamically removes device specific directory from debugfs 'opp' directory.
224 void opp_debug_unregister(struct opp_device
*opp_dev
,
225 struct opp_table
*opp_table
)
227 if (opp_dev
->dentry
== opp_table
->dentry
) {
228 /* Move the real dentry object under another device */
229 if (!list_is_singular(&opp_table
->dev_list
)) {
230 opp_migrate_dentry(opp_dev
, opp_table
);
233 opp_table
->dentry
= NULL
;
236 debugfs_remove_recursive(opp_dev
->dentry
);
239 opp_dev
->dentry
= NULL
;
242 static int __init
opp_debug_init(void)
244 /* Create /sys/kernel/debug/opp directory */
245 rootdir
= debugfs_create_dir("opp", NULL
);
249 core_initcall(opp_debug_init
);