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