Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
at v4.11 250 lines 6.2 kB view raw
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#include <linux/slab.h> 19 20#include "opp.h" 21 22static struct dentry *rootdir; 23 24static void opp_set_dev_name(const struct device *dev, char *name) 25{ 26 if (dev->parent) 27 snprintf(name, NAME_MAX, "%s-%s", dev_name(dev->parent), 28 dev_name(dev)); 29 else 30 snprintf(name, NAME_MAX, "%s", dev_name(dev)); 31} 32 33void opp_debug_remove_one(struct dev_pm_opp *opp) 34{ 35 debugfs_remove_recursive(opp->dentry); 36} 37 38static bool opp_debug_create_supplies(struct dev_pm_opp *opp, 39 struct opp_table *opp_table, 40 struct dentry *pdentry) 41{ 42 struct dentry *d; 43 int i = 0; 44 char *name; 45 46 /* Always create at least supply-0 directory */ 47 do { 48 name = kasprintf(GFP_KERNEL, "supply-%d", i); 49 50 /* Create per-opp directory */ 51 d = debugfs_create_dir(name, pdentry); 52 53 kfree(name); 54 55 if (!d) 56 return false; 57 58 if (!debugfs_create_ulong("u_volt_target", S_IRUGO, d, 59 &opp->supplies[i].u_volt)) 60 return false; 61 62 if (!debugfs_create_ulong("u_volt_min", S_IRUGO, d, 63 &opp->supplies[i].u_volt_min)) 64 return false; 65 66 if (!debugfs_create_ulong("u_volt_max", S_IRUGO, d, 67 &opp->supplies[i].u_volt_max)) 68 return false; 69 70 if (!debugfs_create_ulong("u_amp", S_IRUGO, d, 71 &opp->supplies[i].u_amp)) 72 return false; 73 } while (++i < opp_table->regulator_count); 74 75 return true; 76} 77 78int opp_debug_create_one(struct dev_pm_opp *opp, struct opp_table *opp_table) 79{ 80 struct dentry *pdentry = opp_table->dentry; 81 struct dentry *d; 82 char name[25]; /* 20 chars for 64 bit value + 5 (opp:\0) */ 83 84 /* Rate is unique to each OPP, use it to give opp-name */ 85 snprintf(name, sizeof(name), "opp:%lu", opp->rate); 86 87 /* Create per-opp directory */ 88 d = debugfs_create_dir(name, pdentry); 89 if (!d) 90 return -ENOMEM; 91 92 if (!debugfs_create_bool("available", S_IRUGO, d, &opp->available)) 93 return -ENOMEM; 94 95 if (!debugfs_create_bool("dynamic", S_IRUGO, d, &opp->dynamic)) 96 return -ENOMEM; 97 98 if (!debugfs_create_bool("turbo", S_IRUGO, d, &opp->turbo)) 99 return -ENOMEM; 100 101 if (!debugfs_create_bool("suspend", S_IRUGO, d, &opp->suspend)) 102 return -ENOMEM; 103 104 if (!debugfs_create_ulong("rate_hz", S_IRUGO, d, &opp->rate)) 105 return -ENOMEM; 106 107 if (!opp_debug_create_supplies(opp, opp_table, d)) 108 return -ENOMEM; 109 110 if (!debugfs_create_ulong("clock_latency_ns", S_IRUGO, d, 111 &opp->clock_latency_ns)) 112 return -ENOMEM; 113 114 opp->dentry = d; 115 return 0; 116} 117 118static int opp_list_debug_create_dir(struct opp_device *opp_dev, 119 struct opp_table *opp_table) 120{ 121 const struct device *dev = opp_dev->dev; 122 struct dentry *d; 123 124 opp_set_dev_name(dev, opp_table->dentry_name); 125 126 /* Create device specific directory */ 127 d = debugfs_create_dir(opp_table->dentry_name, rootdir); 128 if (!d) { 129 dev_err(dev, "%s: Failed to create debugfs dir\n", __func__); 130 return -ENOMEM; 131 } 132 133 opp_dev->dentry = d; 134 opp_table->dentry = d; 135 136 return 0; 137} 138 139static int opp_list_debug_create_link(struct opp_device *opp_dev, 140 struct opp_table *opp_table) 141{ 142 const struct device *dev = opp_dev->dev; 143 char name[NAME_MAX]; 144 struct dentry *d; 145 146 opp_set_dev_name(opp_dev->dev, name); 147 148 /* Create device specific directory link */ 149 d = debugfs_create_symlink(name, rootdir, opp_table->dentry_name); 150 if (!d) { 151 dev_err(dev, "%s: Failed to create link\n", __func__); 152 return -ENOMEM; 153 } 154 155 opp_dev->dentry = d; 156 157 return 0; 158} 159 160/** 161 * opp_debug_register - add a device opp node to the debugfs 'opp' directory 162 * @opp_dev: opp-dev pointer for device 163 * @opp_table: the device-opp being added 164 * 165 * Dynamically adds device specific directory in debugfs 'opp' directory. If the 166 * device-opp is shared with other devices, then links will be created for all 167 * devices except the first. 168 * 169 * Return: 0 on success, otherwise negative error. 170 */ 171int opp_debug_register(struct opp_device *opp_dev, struct opp_table *opp_table) 172{ 173 if (!rootdir) { 174 pr_debug("%s: Uninitialized rootdir\n", __func__); 175 return -EINVAL; 176 } 177 178 if (opp_table->dentry) 179 return opp_list_debug_create_link(opp_dev, opp_table); 180 181 return opp_list_debug_create_dir(opp_dev, opp_table); 182} 183 184static void opp_migrate_dentry(struct opp_device *opp_dev, 185 struct opp_table *opp_table) 186{ 187 struct opp_device *new_dev; 188 const struct device *dev; 189 struct dentry *dentry; 190 191 /* Look for next opp-dev */ 192 list_for_each_entry(new_dev, &opp_table->dev_list, node) 193 if (new_dev != opp_dev) 194 break; 195 196 /* new_dev is guaranteed to be valid here */ 197 dev = new_dev->dev; 198 debugfs_remove_recursive(new_dev->dentry); 199 200 opp_set_dev_name(dev, opp_table->dentry_name); 201 202 dentry = debugfs_rename(rootdir, opp_dev->dentry, rootdir, 203 opp_table->dentry_name); 204 if (!dentry) { 205 dev_err(dev, "%s: Failed to rename link from: %s to %s\n", 206 __func__, dev_name(opp_dev->dev), dev_name(dev)); 207 return; 208 } 209 210 new_dev->dentry = dentry; 211 opp_table->dentry = dentry; 212} 213 214/** 215 * opp_debug_unregister - remove a device opp node from debugfs opp directory 216 * @opp_dev: opp-dev pointer for device 217 * @opp_table: the device-opp being removed 218 * 219 * Dynamically removes device specific directory from debugfs 'opp' directory. 220 */ 221void opp_debug_unregister(struct opp_device *opp_dev, 222 struct opp_table *opp_table) 223{ 224 if (opp_dev->dentry == opp_table->dentry) { 225 /* Move the real dentry object under another device */ 226 if (!list_is_singular(&opp_table->dev_list)) { 227 opp_migrate_dentry(opp_dev, opp_table); 228 goto out; 229 } 230 opp_table->dentry = NULL; 231 } 232 233 debugfs_remove_recursive(opp_dev->dentry); 234 235out: 236 opp_dev->dentry = NULL; 237} 238 239static int __init opp_debug_init(void) 240{ 241 /* Create /sys/kernel/debug/opp directory */ 242 rootdir = debugfs_create_dir("opp", NULL); 243 if (!rootdir) { 244 pr_err("%s: Failed to create root directory\n", __func__); 245 return -ENOMEM; 246 } 247 248 return 0; 249} 250core_initcall(opp_debug_init);