| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Generic OPP debugfs interface |
|---|
| 3 | 4 | * |
|---|
| 4 | 5 | * 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 | 6 | */ |
|---|
| 10 | 7 | |
|---|
| 11 | 8 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
|---|
| .. | .. |
|---|
| 35 | 32 | debugfs_remove_recursive(opp->dentry); |
|---|
| 36 | 33 | } |
|---|
| 37 | 34 | |
|---|
| 38 | | -static bool opp_debug_create_supplies(struct dev_pm_opp *opp, |
|---|
| 35 | +static 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 | + |
|---|
| 47 | +static const struct file_operations bw_name_fops = { |
|---|
| 48 | + .open = simple_open, |
|---|
| 49 | + .read = bw_name_read, |
|---|
| 50 | + .llseek = default_llseek, |
|---|
| 51 | +}; |
|---|
| 52 | + |
|---|
| 53 | +static 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 | + |
|---|
| 76 | +static void opp_debug_create_supplies(struct dev_pm_opp *opp, |
|---|
| 39 | 77 | struct opp_table *opp_table, |
|---|
| 40 | 78 | struct dentry *pdentry) |
|---|
| 41 | 79 | { |
|---|
| .. | .. |
|---|
| 50 | 88 | /* Create per-opp directory */ |
|---|
| 51 | 89 | d = debugfs_create_dir(name, pdentry); |
|---|
| 52 | 90 | |
|---|
| 53 | | - if (!d) |
|---|
| 54 | | - return false; |
|---|
| 91 | + debugfs_create_ulong("u_volt_target", S_IRUGO, d, |
|---|
| 92 | + &opp->supplies[i].u_volt); |
|---|
| 55 | 93 | |
|---|
| 56 | | - if (!debugfs_create_ulong("u_volt_target", S_IRUGO, d, |
|---|
| 57 | | - &opp->supplies[i].u_volt)) |
|---|
| 58 | | - return false; |
|---|
| 94 | + debugfs_create_ulong("u_volt_min", S_IRUGO, d, |
|---|
| 95 | + &opp->supplies[i].u_volt_min); |
|---|
| 59 | 96 | |
|---|
| 60 | | - if (!debugfs_create_ulong("u_volt_min", S_IRUGO, d, |
|---|
| 61 | | - &opp->supplies[i].u_volt_min)) |
|---|
| 62 | | - return false; |
|---|
| 97 | + debugfs_create_ulong("u_volt_max", S_IRUGO, d, |
|---|
| 98 | + &opp->supplies[i].u_volt_max); |
|---|
| 63 | 99 | |
|---|
| 64 | | - if (!debugfs_create_ulong("u_volt_max", S_IRUGO, d, |
|---|
| 65 | | - &opp->supplies[i].u_volt_max)) |
|---|
| 66 | | - return false; |
|---|
| 67 | | - |
|---|
| 68 | | - if (!debugfs_create_ulong("u_amp", S_IRUGO, d, |
|---|
| 69 | | - &opp->supplies[i].u_amp)) |
|---|
| 70 | | - return false; |
|---|
| 100 | + debugfs_create_ulong("u_amp", S_IRUGO, d, |
|---|
| 101 | + &opp->supplies[i].u_amp); |
|---|
| 71 | 102 | } |
|---|
| 72 | | - |
|---|
| 73 | | - return true; |
|---|
| 74 | 103 | } |
|---|
| 75 | 104 | |
|---|
| 76 | | -int opp_debug_create_one(struct dev_pm_opp *opp, struct opp_table *opp_table) |
|---|
| 105 | +void opp_debug_create_one(struct dev_pm_opp *opp, struct opp_table *opp_table) |
|---|
| 77 | 106 | { |
|---|
| 78 | 107 | struct dentry *pdentry = opp_table->dentry; |
|---|
| 79 | 108 | struct dentry *d; |
|---|
| .. | .. |
|---|
| 95 | 124 | |
|---|
| 96 | 125 | /* Create per-opp directory */ |
|---|
| 97 | 126 | d = debugfs_create_dir(name, pdentry); |
|---|
| 98 | | - if (!d) |
|---|
| 99 | | - return -ENOMEM; |
|---|
| 100 | 127 | |
|---|
| 101 | | - if (!debugfs_create_bool("available", S_IRUGO, d, &opp->available)) |
|---|
| 102 | | - return -ENOMEM; |
|---|
| 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); |
|---|
| 103 | 136 | |
|---|
| 104 | | - if (!debugfs_create_bool("dynamic", S_IRUGO, d, &opp->dynamic)) |
|---|
| 105 | | - return -ENOMEM; |
|---|
| 106 | | - |
|---|
| 107 | | - if (!debugfs_create_bool("turbo", S_IRUGO, d, &opp->turbo)) |
|---|
| 108 | | - return -ENOMEM; |
|---|
| 109 | | - |
|---|
| 110 | | - if (!debugfs_create_bool("suspend", S_IRUGO, d, &opp->suspend)) |
|---|
| 111 | | - return -ENOMEM; |
|---|
| 112 | | - |
|---|
| 113 | | - if (!debugfs_create_u32("performance_state", S_IRUGO, d, &opp->pstate)) |
|---|
| 114 | | - return -ENOMEM; |
|---|
| 115 | | - |
|---|
| 116 | | - if (!debugfs_create_ulong("rate_hz", S_IRUGO, d, &opp->rate)) |
|---|
| 117 | | - return -ENOMEM; |
|---|
| 118 | | - |
|---|
| 119 | | - if (!opp_debug_create_supplies(opp, opp_table, d)) |
|---|
| 120 | | - return -ENOMEM; |
|---|
| 121 | | - |
|---|
| 122 | | - if (!debugfs_create_ulong("clock_latency_ns", S_IRUGO, d, |
|---|
| 123 | | - &opp->clock_latency_ns)) |
|---|
| 124 | | - return -ENOMEM; |
|---|
| 137 | + opp_debug_create_supplies(opp, opp_table, d); |
|---|
| 138 | + opp_debug_create_bw(opp, opp_table, d); |
|---|
| 125 | 139 | |
|---|
| 126 | 140 | opp->dentry = d; |
|---|
| 127 | | - return 0; |
|---|
| 128 | 141 | } |
|---|
| 129 | 142 | |
|---|
| 130 | | -static int opp_list_debug_create_dir(struct opp_device *opp_dev, |
|---|
| 131 | | - struct opp_table *opp_table) |
|---|
| 143 | +static void opp_list_debug_create_dir(struct opp_device *opp_dev, |
|---|
| 144 | + struct opp_table *opp_table) |
|---|
| 132 | 145 | { |
|---|
| 133 | 146 | const struct device *dev = opp_dev->dev; |
|---|
| 134 | 147 | struct dentry *d; |
|---|
| .. | .. |
|---|
| 137 | 150 | |
|---|
| 138 | 151 | /* Create device specific directory */ |
|---|
| 139 | 152 | d = debugfs_create_dir(opp_table->dentry_name, rootdir); |
|---|
| 140 | | - if (!d) { |
|---|
| 141 | | - dev_err(dev, "%s: Failed to create debugfs dir\n", __func__); |
|---|
| 142 | | - return -ENOMEM; |
|---|
| 143 | | - } |
|---|
| 144 | 153 | |
|---|
| 145 | 154 | opp_dev->dentry = d; |
|---|
| 146 | 155 | opp_table->dentry = d; |
|---|
| 147 | | - |
|---|
| 148 | | - return 0; |
|---|
| 149 | 156 | } |
|---|
| 150 | 157 | |
|---|
| 151 | | -static int opp_list_debug_create_link(struct opp_device *opp_dev, |
|---|
| 152 | | - struct opp_table *opp_table) |
|---|
| 158 | +static void opp_list_debug_create_link(struct opp_device *opp_dev, |
|---|
| 159 | + struct opp_table *opp_table) |
|---|
| 153 | 160 | { |
|---|
| 154 | | - const struct device *dev = opp_dev->dev; |
|---|
| 155 | 161 | char name[NAME_MAX]; |
|---|
| 156 | | - struct dentry *d; |
|---|
| 157 | 162 | |
|---|
| 158 | 163 | opp_set_dev_name(opp_dev->dev, name); |
|---|
| 159 | 164 | |
|---|
| 160 | 165 | /* Create device specific directory link */ |
|---|
| 161 | | - d = debugfs_create_symlink(name, rootdir, opp_table->dentry_name); |
|---|
| 162 | | - if (!d) { |
|---|
| 163 | | - dev_err(dev, "%s: Failed to create link\n", __func__); |
|---|
| 164 | | - return -ENOMEM; |
|---|
| 165 | | - } |
|---|
| 166 | | - |
|---|
| 167 | | - opp_dev->dentry = d; |
|---|
| 168 | | - |
|---|
| 169 | | - return 0; |
|---|
| 166 | + opp_dev->dentry = debugfs_create_symlink(name, rootdir, |
|---|
| 167 | + opp_table->dentry_name); |
|---|
| 170 | 168 | } |
|---|
| 171 | 169 | |
|---|
| 172 | 170 | /** |
|---|
| .. | .. |
|---|
| 177 | 175 | * Dynamically adds device specific directory in debugfs 'opp' directory. If the |
|---|
| 178 | 176 | * device-opp is shared with other devices, then links will be created for all |
|---|
| 179 | 177 | * devices except the first. |
|---|
| 180 | | - * |
|---|
| 181 | | - * Return: 0 on success, otherwise negative error. |
|---|
| 182 | 178 | */ |
|---|
| 183 | | -int opp_debug_register(struct opp_device *opp_dev, struct opp_table *opp_table) |
|---|
| 179 | +void opp_debug_register(struct opp_device *opp_dev, struct opp_table *opp_table) |
|---|
| 184 | 180 | { |
|---|
| 185 | | - if (!rootdir) { |
|---|
| 186 | | - pr_debug("%s: Uninitialized rootdir\n", __func__); |
|---|
| 187 | | - return -EINVAL; |
|---|
| 188 | | - } |
|---|
| 189 | | - |
|---|
| 190 | 181 | if (opp_table->dentry) |
|---|
| 191 | | - return opp_list_debug_create_link(opp_dev, opp_table); |
|---|
| 192 | | - |
|---|
| 193 | | - return opp_list_debug_create_dir(opp_dev, opp_table); |
|---|
| 182 | + opp_list_debug_create_link(opp_dev, opp_table); |
|---|
| 183 | + else |
|---|
| 184 | + opp_list_debug_create_dir(opp_dev, opp_table); |
|---|
| 194 | 185 | } |
|---|
| 195 | 186 | |
|---|
| 196 | 187 | static void opp_migrate_dentry(struct opp_device *opp_dev, |
|---|
| .. | .. |
|---|
| 213 | 204 | |
|---|
| 214 | 205 | dentry = debugfs_rename(rootdir, opp_dev->dentry, rootdir, |
|---|
| 215 | 206 | opp_table->dentry_name); |
|---|
| 216 | | - if (!dentry) { |
|---|
| 207 | + if (IS_ERR(dentry)) { |
|---|
| 217 | 208 | dev_err(dev, "%s: Failed to rename link from: %s to %s\n", |
|---|
| 218 | 209 | __func__, dev_name(opp_dev->dev), dev_name(dev)); |
|---|
| 219 | 210 | return; |
|---|
| .. | .. |
|---|
| 248 | 239 | opp_dev->dentry = NULL; |
|---|
| 249 | 240 | } |
|---|
| 250 | 241 | |
|---|
| 242 | +static int opp_summary_show(struct seq_file *s, void *data) |
|---|
| 243 | +{ |
|---|
| 244 | + struct list_head *lists = (struct list_head *)s->private; |
|---|
| 245 | + struct opp_table *opp_table; |
|---|
| 246 | + struct dev_pm_opp *opp; |
|---|
| 247 | + |
|---|
| 248 | + mutex_lock(&opp_table_lock); |
|---|
| 249 | + |
|---|
| 250 | + seq_puts(s, " device rate(Hz) target(uV) min(uV) max(uV)\n"); |
|---|
| 251 | + seq_puts(s, "-------------------------------------------------------------------\n"); |
|---|
| 252 | + |
|---|
| 253 | + list_for_each_entry(opp_table, lists, node) { |
|---|
| 254 | + seq_printf(s, " %s\n", opp_table->dentry_name); |
|---|
| 255 | + mutex_lock(&opp_table->lock); |
|---|
| 256 | + list_for_each_entry(opp, &opp_table->opp_list, node) { |
|---|
| 257 | + if (!opp->available) |
|---|
| 258 | + continue; |
|---|
| 259 | + seq_printf(s, "%31lu %12lu %11lu %11lu\n", |
|---|
| 260 | + opp->rate, |
|---|
| 261 | + opp->supplies[0].u_volt, |
|---|
| 262 | + opp->supplies[0].u_volt_min, |
|---|
| 263 | + opp->supplies[0].u_volt_max); |
|---|
| 264 | + if (opp_table->regulator_count > 1) |
|---|
| 265 | + seq_printf(s, "%44lu %11lu %11lu\n", |
|---|
| 266 | + opp->supplies[1].u_volt, |
|---|
| 267 | + opp->supplies[1].u_volt_min, |
|---|
| 268 | + opp->supplies[1].u_volt_max); |
|---|
| 269 | + } |
|---|
| 270 | + mutex_unlock(&opp_table->lock); |
|---|
| 271 | + } |
|---|
| 272 | + |
|---|
| 273 | + mutex_unlock(&opp_table_lock); |
|---|
| 274 | + |
|---|
| 275 | + return 0; |
|---|
| 276 | +} |
|---|
| 277 | + |
|---|
| 278 | +static int opp_summary_open(struct inode *inode, struct file *file) |
|---|
| 279 | +{ |
|---|
| 280 | + return single_open(file, opp_summary_show, inode->i_private); |
|---|
| 281 | +} |
|---|
| 282 | + |
|---|
| 283 | +static const struct file_operations opp_summary_fops = { |
|---|
| 284 | + .open = opp_summary_open, |
|---|
| 285 | + .read = seq_read, |
|---|
| 286 | + .llseek = seq_lseek, |
|---|
| 287 | + .release = single_release, |
|---|
| 288 | +}; |
|---|
| 289 | + |
|---|
| 251 | 290 | static int __init opp_debug_init(void) |
|---|
| 252 | 291 | { |
|---|
| 253 | 292 | /* Create /sys/kernel/debug/opp directory */ |
|---|
| 254 | 293 | rootdir = debugfs_create_dir("opp", NULL); |
|---|
| 255 | | - if (!rootdir) { |
|---|
| 256 | | - pr_err("%s: Failed to create root directory\n", __func__); |
|---|
| 257 | | - return -ENOMEM; |
|---|
| 258 | | - } |
|---|
| 294 | + |
|---|
| 295 | + debugfs_create_file("opp_summary", 0444, rootdir, &opp_tables, |
|---|
| 296 | + &opp_summary_fops); |
|---|
| 259 | 297 | |
|---|
| 260 | 298 | return 0; |
|---|
| 261 | 299 | } |
|---|
| 262 | | -#ifdef CONFIG_ROCKCHIP_THUNDER_BOOT |
|---|
| 263 | | -core_initcall_sync(opp_debug_init); |
|---|
| 264 | | -#else |
|---|
| 265 | 300 | core_initcall(opp_debug_init); |
|---|
| 266 | | -#endif |
|---|