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 22 static struct dentry *rootdir; 23 24 static 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 33 void opp_debug_remove_one(struct dev_pm_opp *opp) 34 { 35 debugfs_remove_recursive(opp->dentry); 36 } 37 38 static void 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 45 for (i = 0; i < opp_table->regulator_count; i++) { 46 char name[15]; 47 48 snprintf(name, sizeof(name), "supply-%d", i); 49 50 /* Create per-opp directory */ 51 d = debugfs_create_dir(name, pdentry); 52 53 debugfs_create_ulong("u_volt_target", S_IRUGO, d, 54 &opp->supplies[i].u_volt); 55 56 debugfs_create_ulong("u_volt_min", S_IRUGO, d, 57 &opp->supplies[i].u_volt_min); 58 59 debugfs_create_ulong("u_volt_max", S_IRUGO, d, 60 &opp->supplies[i].u_volt_max); 61 62 debugfs_create_ulong("u_amp", S_IRUGO, d, 63 &opp->supplies[i].u_amp); 64 } 65 } 66 67 void opp_debug_create_one(struct dev_pm_opp *opp, struct opp_table *opp_table) 68 { 69 struct dentry *pdentry = opp_table->dentry; 70 struct dentry *d; 71 unsigned long id; 72 char name[25]; /* 20 chars for 64 bit value + 5 (opp:\0) */ 73 74 /* 75 * Get directory name for OPP. 76 * 77 * - Normally rate is unique to each OPP, use it to get unique opp-name. 78 * - For some devices rate isn't available, use index instead. 79 */ 80 if (likely(opp->rate)) 81 id = opp->rate; 82 else 83 id = _get_opp_count(opp_table); 84 85 snprintf(name, sizeof(name), "opp:%lu", id); 86 87 /* Create per-opp directory */ 88 d = debugfs_create_dir(name, pdentry); 89 90 debugfs_create_bool("available", S_IRUGO, d, &opp->available); 91 debugfs_create_bool("dynamic", S_IRUGO, d, &opp->dynamic); 92 debugfs_create_bool("turbo", S_IRUGO, d, &opp->turbo); 93 debugfs_create_bool("suspend", S_IRUGO, d, &opp->suspend); 94 debugfs_create_u32("performance_state", S_IRUGO, d, &opp->pstate); 95 debugfs_create_ulong("rate_hz", S_IRUGO, d, &opp->rate); 96 debugfs_create_ulong("clock_latency_ns", S_IRUGO, d, 97 &opp->clock_latency_ns); 98 99 opp_debug_create_supplies(opp, opp_table, d); 100 101 opp->dentry = d; 102 } 103 104 static void opp_list_debug_create_dir(struct opp_device *opp_dev, 105 struct opp_table *opp_table) 106 { 107 const struct device *dev = opp_dev->dev; 108 struct dentry *d; 109 110 opp_set_dev_name(dev, opp_table->dentry_name); 111 112 /* Create device specific directory */ 113 d = debugfs_create_dir(opp_table->dentry_name, rootdir); 114 115 opp_dev->dentry = d; 116 opp_table->dentry = d; 117 } 118 119 static void opp_list_debug_create_link(struct opp_device *opp_dev, 120 struct opp_table *opp_table) 121 { 122 char name[NAME_MAX]; 123 124 opp_set_dev_name(opp_dev->dev, name); 125 126 /* Create device specific directory link */ 127 opp_dev->dentry = debugfs_create_symlink(name, rootdir, 128 opp_table->dentry_name); 129 } 130 131 /** 132 * opp_debug_register - add a device opp node to the debugfs 'opp' directory 133 * @opp_dev: opp-dev pointer for device 134 * @opp_table: the device-opp being added 135 * 136 * Dynamically adds device specific directory in debugfs 'opp' directory. If the 137 * device-opp is shared with other devices, then links will be created for all 138 * devices except the first. 139 */ 140 void opp_debug_register(struct opp_device *opp_dev, struct opp_table *opp_table) 141 { 142 if (opp_table->dentry) 143 opp_list_debug_create_link(opp_dev, opp_table); 144 else 145 opp_list_debug_create_dir(opp_dev, opp_table); 146 } 147 148 static void opp_migrate_dentry(struct opp_device *opp_dev, 149 struct opp_table *opp_table) 150 { 151 struct opp_device *new_dev; 152 const struct device *dev; 153 struct dentry *dentry; 154 155 /* Look for next opp-dev */ 156 list_for_each_entry(new_dev, &opp_table->dev_list, node) 157 if (new_dev != opp_dev) 158 break; 159 160 /* new_dev is guaranteed to be valid here */ 161 dev = new_dev->dev; 162 debugfs_remove_recursive(new_dev->dentry); 163 164 opp_set_dev_name(dev, opp_table->dentry_name); 165 166 dentry = debugfs_rename(rootdir, opp_dev->dentry, rootdir, 167 opp_table->dentry_name); 168 if (!dentry) { 169 dev_err(dev, "%s: Failed to rename link from: %s to %s\n", 170 __func__, dev_name(opp_dev->dev), dev_name(dev)); 171 return; 172 } 173 174 new_dev->dentry = dentry; 175 opp_table->dentry = dentry; 176 } 177 178 /** 179 * opp_debug_unregister - remove a device opp node from debugfs opp directory 180 * @opp_dev: opp-dev pointer for device 181 * @opp_table: the device-opp being removed 182 * 183 * Dynamically removes device specific directory from debugfs 'opp' directory. 184 */ 185 void opp_debug_unregister(struct opp_device *opp_dev, 186 struct opp_table *opp_table) 187 { 188 if (opp_dev->dentry == opp_table->dentry) { 189 /* Move the real dentry object under another device */ 190 if (!list_is_singular(&opp_table->dev_list)) { 191 opp_migrate_dentry(opp_dev, opp_table); 192 goto out; 193 } 194 opp_table->dentry = NULL; 195 } 196 197 debugfs_remove_recursive(opp_dev->dentry); 198 199 out: 200 opp_dev->dentry = NULL; 201 } 202 203 static int __init opp_debug_init(void) 204 { 205 /* Create /sys/kernel/debug/opp directory */ 206 rootdir = debugfs_create_dir("opp", NULL); 207 208 return 0; 209 } 210 core_initcall(opp_debug_init); 211