Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0-only
2 : : /*
3 : : * Generic OPP debugfs interface
4 : : *
5 : : * Copyright (C) 2015-2016 Viresh Kumar <viresh.kumar@linaro.org>
6 : : */
7 : :
8 : : #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
9 : :
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>
16 : :
17 : : #include "opp.h"
18 : :
19 : : static struct dentry *rootdir;
20 : :
21 : 0 : static void opp_set_dev_name(const struct device *dev, char *name)
22 : : {
23 [ # # ]: 0 : if (dev->parent)
24 : 0 : snprintf(name, NAME_MAX, "%s-%s", dev_name(dev->parent),
25 : : dev_name(dev));
26 : : else
27 : 0 : snprintf(name, NAME_MAX, "%s", dev_name(dev));
28 : 0 : }
29 : :
30 : 0 : void opp_debug_remove_one(struct dev_pm_opp *opp)
31 : : {
32 : 0 : debugfs_remove_recursive(opp->dentry);
33 : 0 : }
34 : :
35 : 0 : static void opp_debug_create_supplies(struct dev_pm_opp *opp,
36 : : struct opp_table *opp_table,
37 : : struct dentry *pdentry)
38 : : {
39 : : struct dentry *d;
40 : : int i;
41 : :
42 [ # # ]: 0 : for (i = 0; i < opp_table->regulator_count; i++) {
43 : : char name[15];
44 : :
45 : 0 : snprintf(name, sizeof(name), "supply-%d", i);
46 : :
47 : : /* Create per-opp directory */
48 : 0 : d = debugfs_create_dir(name, pdentry);
49 : :
50 : 0 : debugfs_create_ulong("u_volt_target", S_IRUGO, d,
51 : 0 : &opp->supplies[i].u_volt);
52 : :
53 : 0 : debugfs_create_ulong("u_volt_min", S_IRUGO, d,
54 : 0 : &opp->supplies[i].u_volt_min);
55 : :
56 : 0 : debugfs_create_ulong("u_volt_max", S_IRUGO, d,
57 : 0 : &opp->supplies[i].u_volt_max);
58 : :
59 : 0 : debugfs_create_ulong("u_amp", S_IRUGO, d,
60 : 0 : &opp->supplies[i].u_amp);
61 : : }
62 : 0 : }
63 : :
64 : 0 : void opp_debug_create_one(struct dev_pm_opp *opp, struct opp_table *opp_table)
65 : : {
66 : 0 : struct dentry *pdentry = opp_table->dentry;
67 : : struct dentry *d;
68 : : unsigned long id;
69 : : char name[25]; /* 20 chars for 64 bit value + 5 (opp:\0) */
70 : :
71 : : /*
72 : : * Get directory name for OPP.
73 : : *
74 : : * - Normally rate is unique to each OPP, use it to get unique opp-name.
75 : : * - For some devices rate isn't available, use index instead.
76 : : */
77 [ # # ]: 0 : if (likely(opp->rate))
78 : : id = opp->rate;
79 : : else
80 : 0 : id = _get_opp_count(opp_table);
81 : :
82 : 0 : snprintf(name, sizeof(name), "opp:%lu", id);
83 : :
84 : : /* Create per-opp directory */
85 : 0 : d = debugfs_create_dir(name, pdentry);
86 : :
87 : 0 : debugfs_create_bool("available", S_IRUGO, d, &opp->available);
88 : 0 : debugfs_create_bool("dynamic", S_IRUGO, d, &opp->dynamic);
89 : 0 : debugfs_create_bool("turbo", S_IRUGO, d, &opp->turbo);
90 : 0 : debugfs_create_bool("suspend", S_IRUGO, d, &opp->suspend);
91 : 0 : debugfs_create_u32("performance_state", S_IRUGO, d, &opp->pstate);
92 : 0 : debugfs_create_ulong("rate_hz", S_IRUGO, d, &opp->rate);
93 : 0 : debugfs_create_ulong("clock_latency_ns", S_IRUGO, d,
94 : : &opp->clock_latency_ns);
95 : :
96 : 0 : opp_debug_create_supplies(opp, opp_table, d);
97 : :
98 : 0 : opp->dentry = d;
99 : 0 : }
100 : :
101 : 0 : static void opp_list_debug_create_dir(struct opp_device *opp_dev,
102 : : struct opp_table *opp_table)
103 : : {
104 : 0 : const struct device *dev = opp_dev->dev;
105 : : struct dentry *d;
106 : :
107 : 0 : opp_set_dev_name(dev, opp_table->dentry_name);
108 : :
109 : : /* Create device specific directory */
110 : 0 : d = debugfs_create_dir(opp_table->dentry_name, rootdir);
111 : :
112 : 0 : opp_dev->dentry = d;
113 : 0 : opp_table->dentry = d;
114 : 0 : }
115 : :
116 : 0 : static void opp_list_debug_create_link(struct opp_device *opp_dev,
117 : : struct opp_table *opp_table)
118 : : {
119 : : char name[NAME_MAX];
120 : :
121 : 0 : opp_set_dev_name(opp_dev->dev, name);
122 : :
123 : : /* Create device specific directory link */
124 : 0 : opp_dev->dentry = debugfs_create_symlink(name, rootdir,
125 : 0 : opp_table->dentry_name);
126 : 0 : }
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 : 0 : void opp_debug_register(struct opp_device *opp_dev, struct opp_table *opp_table)
138 : : {
139 [ # # ]: 0 : if (opp_table->dentry)
140 : 0 : opp_list_debug_create_link(opp_dev, opp_table);
141 : : else
142 : 0 : opp_list_debug_create_dir(opp_dev, opp_table);
143 : 0 : }
144 : :
145 : 0 : static void opp_migrate_dentry(struct opp_device *opp_dev,
146 : : struct opp_table *opp_table)
147 : : {
148 : : struct opp_device *new_dev;
149 : : const struct device *dev;
150 : : struct dentry *dentry;
151 : :
152 : : /* Look for next opp-dev */
153 [ # # ]: 0 : list_for_each_entry(new_dev, &opp_table->dev_list, node)
154 [ # # ]: 0 : if (new_dev != opp_dev)
155 : : break;
156 : :
157 : : /* new_dev is guaranteed to be valid here */
158 : 0 : dev = new_dev->dev;
159 : 0 : debugfs_remove_recursive(new_dev->dentry);
160 : :
161 : 0 : opp_set_dev_name(dev, opp_table->dentry_name);
162 : :
163 : 0 : dentry = debugfs_rename(rootdir, opp_dev->dentry, rootdir,
164 : : opp_table->dentry_name);
165 [ # # ]: 0 : if (!dentry) {
166 : 0 : dev_err(dev, "%s: Failed to rename link from: %s to %s\n",
167 : : __func__, dev_name(opp_dev->dev), dev_name(dev));
168 : 0 : return;
169 : : }
170 : :
171 : 0 : new_dev->dentry = dentry;
172 : 0 : opp_table->dentry = dentry;
173 : : }
174 : :
175 : : /**
176 : : * opp_debug_unregister - remove a device opp node from debugfs opp directory
177 : : * @opp_dev: opp-dev pointer for device
178 : : * @opp_table: the device-opp being removed
179 : : *
180 : : * Dynamically removes device specific directory from debugfs 'opp' directory.
181 : : */
182 : 0 : void opp_debug_unregister(struct opp_device *opp_dev,
183 : : struct opp_table *opp_table)
184 : : {
185 [ # # ]: 0 : if (opp_dev->dentry == opp_table->dentry) {
186 : : /* Move the real dentry object under another device */
187 [ # # ]: 0 : if (!list_is_singular(&opp_table->dev_list)) {
188 : 0 : opp_migrate_dentry(opp_dev, opp_table);
189 : 0 : goto out;
190 : : }
191 : 0 : opp_table->dentry = NULL;
192 : : }
193 : :
194 : 0 : debugfs_remove_recursive(opp_dev->dentry);
195 : :
196 : : out:
197 : 0 : opp_dev->dentry = NULL;
198 : 0 : }
199 : :
200 : 404 : static int __init opp_debug_init(void)
201 : : {
202 : : /* Create /sys/kernel/debug/opp directory */
203 : 404 : rootdir = debugfs_create_dir("opp", NULL);
204 : :
205 : 404 : return 0;
206 : : }
207 : : core_initcall(opp_debug_init);
|