linux/drivers/opp/debugfs.c
<<
>>
Prefs
   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
  19static struct dentry *rootdir;
  20
  21static void opp_set_dev_name(const struct device *dev, char *name)
  22{
  23        if (dev->parent)
  24                snprintf(name, NAME_MAX, "%s-%s", dev_name(dev->parent),
  25                         dev_name(dev));
  26        else
  27                snprintf(name, NAME_MAX, "%s", dev_name(dev));
  28}
  29
  30void opp_debug_remove_one(struct dev_pm_opp *opp)
  31{
  32        debugfs_remove_recursive(opp->dentry);
  33}
  34
  35static ssize_t bw_name_read(struct file *fp, char __user *userbuf,
  36                            size_t count, loff_t *ppos)
  37{
  38        struct icc_path *path = fp->private_data;
  39        char buf[64];
  40        int i;
  41
  42        i = scnprintf(buf, sizeof(buf), "%.62s\n", icc_get_name(path));
  43
  44        return simple_read_from_buffer(userbuf, count, ppos, buf, i);
  45}
  46
  47static const struct file_operations bw_name_fops = {
  48        .open = simple_open,
  49        .read = bw_name_read,
  50        .llseek = default_llseek,
  51};
  52
  53static void opp_debug_create_bw(struct dev_pm_opp *opp,
  54                                struct opp_table *opp_table,
  55                                struct dentry *pdentry)
  56{
  57        struct dentry *d;
  58        char name[11];
  59        int i;
  60
  61        for (i = 0; i < opp_table->path_count; i++) {
  62                snprintf(name, sizeof(name), "icc-path-%.1d", i);
  63
  64                /* Create per-path directory */
  65                d = debugfs_create_dir(name, pdentry);
  66
  67                debugfs_create_file("name", S_IRUGO, d, opp_table->paths[i],
  68                                    &bw_name_fops);
  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);
  73        }
  74}
  75
  76static void opp_debug_create_supplies(struct dev_pm_opp *opp,
  77                                      struct opp_table *opp_table,
  78                                      struct dentry *pdentry)
  79{
  80        struct dentry *d;
  81        int i;
  82
  83        for (i = 0; i < opp_table->regulator_count; i++) {
  84                char name[15];
  85
  86                snprintf(name, sizeof(name), "supply-%d", i);
  87
  88                /* Create per-opp directory */
  89                d = debugfs_create_dir(name, pdentry);
  90
  91                debugfs_create_ulong("u_volt_target", S_IRUGO, d,
  92                                     &opp->supplies[i].u_volt);
  93
  94                debugfs_create_ulong("u_volt_min", S_IRUGO, d,
  95                                     &opp->supplies[i].u_volt_min);
  96
  97                debugfs_create_ulong("u_volt_max", S_IRUGO, d,
  98                                     &opp->supplies[i].u_volt_max);
  99
 100                debugfs_create_ulong("u_amp", S_IRUGO, d,
 101                                     &opp->supplies[i].u_amp);
 102        }
 103}
 104
 105void opp_debug_create_one(struct dev_pm_opp *opp, struct opp_table *opp_table)
 106{
 107        struct dentry *pdentry = opp_table->dentry;
 108        struct dentry *d;
 109        unsigned long id;
 110        char name[25];  /* 20 chars for 64 bit value + 5 (opp:\0) */
 111
 112        /*
 113         * Get directory name for OPP.
 114         *
 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.
 117         */
 118        if (likely(opp->rate))
 119                id = opp->rate;
 120        else
 121                id = _get_opp_count(opp_table);
 122
 123        snprintf(name, sizeof(name), "opp:%lu", id);
 124
 125        /* Create per-opp directory */
 126        d = debugfs_create_dir(name, pdentry);
 127
 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);
 136
 137        opp_debug_create_supplies(opp, opp_table, d);
 138        opp_debug_create_bw(opp, opp_table, d);
 139
 140        opp->dentry = d;
 141}
 142
 143static void opp_list_debug_create_dir(struct opp_device *opp_dev,
 144                                      struct opp_table *opp_table)
 145{
 146        const struct device *dev = opp_dev->dev;
 147        struct dentry *d;
 148
 149        opp_set_dev_name(dev, opp_table->dentry_name);
 150
 151        /* Create device specific directory */
 152        d = debugfs_create_dir(opp_table->dentry_name, rootdir);
 153
 154        opp_dev->dentry = d;
 155        opp_table->dentry = d;
 156}
 157
 158static void opp_list_debug_create_link(struct opp_device *opp_dev,
 159                                       struct opp_table *opp_table)
 160{
 161        char name[NAME_MAX];
 162
 163        opp_set_dev_name(opp_dev->dev, name);
 164
 165        /* Create device specific directory link */
 166        opp_dev->dentry = debugfs_create_symlink(name, rootdir,
 167                                                 opp_table->dentry_name);
 168}
 169
 170/**
 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
 174 *
 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.
 178 */
 179void opp_debug_register(struct opp_device *opp_dev, struct opp_table *opp_table)
 180{
 181        if (opp_table->dentry)
 182                opp_list_debug_create_link(opp_dev, opp_table);
 183        else
 184                opp_list_debug_create_dir(opp_dev, opp_table);
 185}
 186
 187static void opp_migrate_dentry(struct opp_device *opp_dev,
 188                               struct opp_table *opp_table)
 189{
 190        struct opp_device *new_dev;
 191        const struct device *dev;
 192        struct dentry *dentry;
 193
 194        /* Look for next opp-dev */
 195        list_for_each_entry(new_dev, &opp_table->dev_list, node)
 196                if (new_dev != opp_dev)
 197                        break;
 198
 199        /* new_dev is guaranteed to be valid here */
 200        dev = new_dev->dev;
 201        debugfs_remove_recursive(new_dev->dentry);
 202
 203        opp_set_dev_name(dev, opp_table->dentry_name);
 204
 205        dentry = debugfs_rename(rootdir, opp_dev->dentry, rootdir,
 206                                opp_table->dentry_name);
 207        if (!dentry) {
 208                dev_err(dev, "%s: Failed to rename link from: %s to %s\n",
 209                        __func__, dev_name(opp_dev->dev), dev_name(dev));
 210                return;
 211        }
 212
 213        new_dev->dentry = dentry;
 214        opp_table->dentry = dentry;
 215}
 216
 217/**
 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
 221 *
 222 * Dynamically removes device specific directory from debugfs 'opp' directory.
 223 */
 224void opp_debug_unregister(struct opp_device *opp_dev,
 225                          struct opp_table *opp_table)
 226{
 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);
 231                        goto out;
 232                }
 233                opp_table->dentry = NULL;
 234        }
 235
 236        debugfs_remove_recursive(opp_dev->dentry);
 237
 238out:
 239        opp_dev->dentry = NULL;
 240}
 241
 242static int __init opp_debug_init(void)
 243{
 244        /* Create /sys/kernel/debug/opp directory */
 245        rootdir = debugfs_create_dir("opp", NULL);
 246
 247        return 0;
 248}
 249core_initcall(opp_debug_init);
 250