Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Generic OPP debugfs interface
0004  *
0005  * Copyright (C) 2015-2016 Viresh Kumar <viresh.kumar@linaro.org>
0006  */
0007 
0008 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0009 
0010 #include <linux/debugfs.h>
0011 #include <linux/device.h>
0012 #include <linux/err.h>
0013 #include <linux/of.h>
0014 #include <linux/init.h>
0015 #include <linux/limits.h>
0016 #include <linux/slab.h>
0017 
0018 #include "opp.h"
0019 
0020 static struct dentry *rootdir;
0021 
0022 static void opp_set_dev_name(const struct device *dev, char *name)
0023 {
0024     if (dev->parent)
0025         snprintf(name, NAME_MAX, "%s-%s", dev_name(dev->parent),
0026              dev_name(dev));
0027     else
0028         snprintf(name, NAME_MAX, "%s", dev_name(dev));
0029 }
0030 
0031 void opp_debug_remove_one(struct dev_pm_opp *opp)
0032 {
0033     debugfs_remove_recursive(opp->dentry);
0034 }
0035 
0036 static ssize_t bw_name_read(struct file *fp, char __user *userbuf,
0037                 size_t count, loff_t *ppos)
0038 {
0039     struct icc_path *path = fp->private_data;
0040     char buf[64];
0041     int i;
0042 
0043     i = scnprintf(buf, sizeof(buf), "%.62s\n", icc_get_name(path));
0044 
0045     return simple_read_from_buffer(userbuf, count, ppos, buf, i);
0046 }
0047 
0048 static const struct file_operations bw_name_fops = {
0049     .open = simple_open,
0050     .read = bw_name_read,
0051     .llseek = default_llseek,
0052 };
0053 
0054 static void opp_debug_create_bw(struct dev_pm_opp *opp,
0055                 struct opp_table *opp_table,
0056                 struct dentry *pdentry)
0057 {
0058     struct dentry *d;
0059     char name[11];
0060     int i;
0061 
0062     for (i = 0; i < opp_table->path_count; i++) {
0063         snprintf(name, sizeof(name), "icc-path-%.1d", i);
0064 
0065         /* Create per-path directory */
0066         d = debugfs_create_dir(name, pdentry);
0067 
0068         debugfs_create_file("name", S_IRUGO, d, opp_table->paths[i],
0069                     &bw_name_fops);
0070         debugfs_create_u32("peak_bw", S_IRUGO, d,
0071                    &opp->bandwidth[i].peak);
0072         debugfs_create_u32("avg_bw", S_IRUGO, d,
0073                    &opp->bandwidth[i].avg);
0074     }
0075 }
0076 
0077 static void opp_debug_create_clks(struct dev_pm_opp *opp,
0078                   struct opp_table *opp_table,
0079                   struct dentry *pdentry)
0080 {
0081     char name[12];
0082     int i;
0083 
0084     if (opp_table->clk_count == 1) {
0085         debugfs_create_ulong("rate_hz", S_IRUGO, pdentry, &opp->rates[0]);
0086         return;
0087     }
0088 
0089     for (i = 0; i < opp_table->clk_count; i++) {
0090         snprintf(name, sizeof(name), "rate_hz_%d", i);
0091         debugfs_create_ulong(name, S_IRUGO, pdentry, &opp->rates[i]);
0092     }
0093 }
0094 
0095 static void opp_debug_create_supplies(struct dev_pm_opp *opp,
0096                       struct opp_table *opp_table,
0097                       struct dentry *pdentry)
0098 {
0099     struct dentry *d;
0100     int i;
0101 
0102     for (i = 0; i < opp_table->regulator_count; i++) {
0103         char name[15];
0104 
0105         snprintf(name, sizeof(name), "supply-%d", i);
0106 
0107         /* Create per-opp directory */
0108         d = debugfs_create_dir(name, pdentry);
0109 
0110         debugfs_create_ulong("u_volt_target", S_IRUGO, d,
0111                      &opp->supplies[i].u_volt);
0112 
0113         debugfs_create_ulong("u_volt_min", S_IRUGO, d,
0114                      &opp->supplies[i].u_volt_min);
0115 
0116         debugfs_create_ulong("u_volt_max", S_IRUGO, d,
0117                      &opp->supplies[i].u_volt_max);
0118 
0119         debugfs_create_ulong("u_amp", S_IRUGO, d,
0120                      &opp->supplies[i].u_amp);
0121 
0122         debugfs_create_ulong("u_watt", S_IRUGO, d,
0123                      &opp->supplies[i].u_watt);
0124     }
0125 }
0126 
0127 void opp_debug_create_one(struct dev_pm_opp *opp, struct opp_table *opp_table)
0128 {
0129     struct dentry *pdentry = opp_table->dentry;
0130     struct dentry *d;
0131     unsigned long id;
0132     char name[25];  /* 20 chars for 64 bit value + 5 (opp:\0) */
0133 
0134     /*
0135      * Get directory name for OPP.
0136      *
0137      * - Normally rate is unique to each OPP, use it to get unique opp-name.
0138      * - For some devices rate isn't available or there are multiple, use
0139      *   index instead for them.
0140      */
0141     if (likely(opp_table->clk_count == 1 && opp->rates[0]))
0142         id = opp->rates[0];
0143     else
0144         id = _get_opp_count(opp_table);
0145 
0146     snprintf(name, sizeof(name), "opp:%lu", id);
0147 
0148     /* Create per-opp directory */
0149     d = debugfs_create_dir(name, pdentry);
0150 
0151     debugfs_create_bool("available", S_IRUGO, d, &opp->available);
0152     debugfs_create_bool("dynamic", S_IRUGO, d, &opp->dynamic);
0153     debugfs_create_bool("turbo", S_IRUGO, d, &opp->turbo);
0154     debugfs_create_bool("suspend", S_IRUGO, d, &opp->suspend);
0155     debugfs_create_u32("performance_state", S_IRUGO, d, &opp->pstate);
0156     debugfs_create_u32("level", S_IRUGO, d, &opp->level);
0157     debugfs_create_ulong("clock_latency_ns", S_IRUGO, d,
0158                  &opp->clock_latency_ns);
0159 
0160     opp->of_name = of_node_full_name(opp->np);
0161     debugfs_create_str("of_name", S_IRUGO, d, (char **)&opp->of_name);
0162 
0163     opp_debug_create_clks(opp, opp_table, d);
0164     opp_debug_create_supplies(opp, opp_table, d);
0165     opp_debug_create_bw(opp, opp_table, d);
0166 
0167     opp->dentry = d;
0168 }
0169 
0170 static void opp_list_debug_create_dir(struct opp_device *opp_dev,
0171                       struct opp_table *opp_table)
0172 {
0173     const struct device *dev = opp_dev->dev;
0174     struct dentry *d;
0175 
0176     opp_set_dev_name(dev, opp_table->dentry_name);
0177 
0178     /* Create device specific directory */
0179     d = debugfs_create_dir(opp_table->dentry_name, rootdir);
0180 
0181     opp_dev->dentry = d;
0182     opp_table->dentry = d;
0183 }
0184 
0185 static void opp_list_debug_create_link(struct opp_device *opp_dev,
0186                        struct opp_table *opp_table)
0187 {
0188     char name[NAME_MAX];
0189 
0190     opp_set_dev_name(opp_dev->dev, name);
0191 
0192     /* Create device specific directory link */
0193     opp_dev->dentry = debugfs_create_symlink(name, rootdir,
0194                          opp_table->dentry_name);
0195 }
0196 
0197 /**
0198  * opp_debug_register - add a device opp node to the debugfs 'opp' directory
0199  * @opp_dev: opp-dev pointer for device
0200  * @opp_table: the device-opp being added
0201  *
0202  * Dynamically adds device specific directory in debugfs 'opp' directory. If the
0203  * device-opp is shared with other devices, then links will be created for all
0204  * devices except the first.
0205  */
0206 void opp_debug_register(struct opp_device *opp_dev, struct opp_table *opp_table)
0207 {
0208     if (opp_table->dentry)
0209         opp_list_debug_create_link(opp_dev, opp_table);
0210     else
0211         opp_list_debug_create_dir(opp_dev, opp_table);
0212 }
0213 
0214 static void opp_migrate_dentry(struct opp_device *opp_dev,
0215                    struct opp_table *opp_table)
0216 {
0217     struct opp_device *new_dev = NULL, *iter;
0218     const struct device *dev;
0219     struct dentry *dentry;
0220 
0221     /* Look for next opp-dev */
0222     list_for_each_entry(iter, &opp_table->dev_list, node)
0223         if (iter != opp_dev) {
0224             new_dev = iter;
0225             break;
0226         }
0227 
0228     BUG_ON(!new_dev);
0229 
0230     /* new_dev is guaranteed to be valid here */
0231     dev = new_dev->dev;
0232     debugfs_remove_recursive(new_dev->dentry);
0233 
0234     opp_set_dev_name(dev, opp_table->dentry_name);
0235 
0236     dentry = debugfs_rename(rootdir, opp_dev->dentry, rootdir,
0237                 opp_table->dentry_name);
0238     if (!dentry) {
0239         dev_err(dev, "%s: Failed to rename link from: %s to %s\n",
0240             __func__, dev_name(opp_dev->dev), dev_name(dev));
0241         return;
0242     }
0243 
0244     new_dev->dentry = dentry;
0245     opp_table->dentry = dentry;
0246 }
0247 
0248 /**
0249  * opp_debug_unregister - remove a device opp node from debugfs opp directory
0250  * @opp_dev: opp-dev pointer for device
0251  * @opp_table: the device-opp being removed
0252  *
0253  * Dynamically removes device specific directory from debugfs 'opp' directory.
0254  */
0255 void opp_debug_unregister(struct opp_device *opp_dev,
0256               struct opp_table *opp_table)
0257 {
0258     if (opp_dev->dentry == opp_table->dentry) {
0259         /* Move the real dentry object under another device */
0260         if (!list_is_singular(&opp_table->dev_list)) {
0261             opp_migrate_dentry(opp_dev, opp_table);
0262             goto out;
0263         }
0264         opp_table->dentry = NULL;
0265     }
0266 
0267     debugfs_remove_recursive(opp_dev->dentry);
0268 
0269 out:
0270     opp_dev->dentry = NULL;
0271 }
0272 
0273 static int __init opp_debug_init(void)
0274 {
0275     /* Create /sys/kernel/debug/opp directory */
0276     rootdir = debugfs_create_dir("opp", NULL);
0277 
0278     return 0;
0279 }
0280 core_initcall(opp_debug_init);