aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/base
diff options
context:
space:
mode:
authorMaciej Matraszek <m.matraszek@samsung.com>2014-09-15 07:09:10 -0400
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2014-09-23 17:16:58 -0400
commit2bd5306a8764d9496f3e3d90c4e608c247fcfd31 (patch)
tree9e93a607371760f3f594a9e764982d45df93696a /drivers/base
parent91d66cd27f5fd8a3bca4621a3505c9067925478d (diff)
PM / Domains: add debugfs listing of struct generic_pm_domain-s
Add /sys/kernel/debug/pm_genpd/pm_genpd_summary file, which lists power domains in the system, their statuses and attached devices, resembling /sys/kernel/debug/clk/clk_summary. Currently it is impossible to inspect (from userland) whether a power domain is on or off. And, if it is on, which device blocks it from powering down. This change allows developers working on embedded devices power efficiency to list all necessary information about generic power domains in one place. The content of pm_genpd/pm_genpd_summary file is generated by iterating over all generic power domain in the system, and, for each, over registered devices and over the subdomains, if present. Example output: $ cat /sys/kernel/debug/pm_genpd/pm_genpd_summary domain status slaves /device runtime status ---------------------------------------------------------------------- a4su off a3sg off a3sm on a3sp on /devices/e6600000.pwm suspended /devices/e6c50000.serial active /devices/e6850000.sd suspended /devices/e6bd0000.mmc active a4s on a3sp, a3sm, a3sg /devices/e6900000.irqpin unsupported /devices/e6900004.irqpin unsupported /devices/e6900008.irqpin unsupported /devices/e690000c.irqpin unsupported /devices/e9a00000.ethernet active a3rv off a4r off a3rv /devices/fff20000.i2c suspended a4lc off c5 on a4lc, a4r, a4s, a4su /devices/e6050000.pfc unsupported /devices/e6138000.timer active To enable this feature, compile the kernel with debugfs and CONFIG_PM_ADVANCED_DEBUG enabled. Signed-off-by: Maciej Matraszek <m.matraszek@samsung.com> Tested-by: Geert Uytterhoeven <geert+renesas@glider.be> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Diffstat (limited to 'drivers/base')
-rw-r--r--drivers/base/power/domain.c157
1 files changed, 157 insertions, 0 deletions
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
index a3d41a883d83..cd400575ee0e 100644
--- a/drivers/base/power/domain.c
+++ b/drivers/base/power/domain.c
@@ -2222,3 +2222,160 @@ int genpd_dev_pm_attach(struct device *dev)
2222} 2222}
2223EXPORT_SYMBOL_GPL(genpd_dev_pm_attach); 2223EXPORT_SYMBOL_GPL(genpd_dev_pm_attach);
2224#endif 2224#endif
2225
2226
2227/*** debugfs support ***/
2228
2229#ifdef CONFIG_PM_ADVANCED_DEBUG
2230#include <linux/pm.h>
2231#include <linux/device.h>
2232#include <linux/debugfs.h>
2233#include <linux/seq_file.h>
2234#include <linux/init.h>
2235#include <linux/kobject.h>
2236static struct dentry *pm_genpd_debugfs_dir;
2237
2238/*
2239 * TODO: This function is a slightly modified version of rtpm_status_show
2240 * from sysfs.c, but dependencies between PM_GENERIC_DOMAINS and PM_RUNTIME
2241 * are too loose to generalize it.
2242 */
2243#ifdef CONFIG_PM_RUNTIME
2244static void rtpm_status_str(struct seq_file *s, struct device *dev)
2245{
2246 static const char * const status_lookup[] = {
2247 [RPM_ACTIVE] = "active",
2248 [RPM_RESUMING] = "resuming",
2249 [RPM_SUSPENDED] = "suspended",
2250 [RPM_SUSPENDING] = "suspending"
2251 };
2252 const char *p = "";
2253
2254 if (dev->power.runtime_error)
2255 p = "error";
2256 else if (dev->power.disable_depth)
2257 p = "unsupported";
2258 else if (dev->power.runtime_status < ARRAY_SIZE(status_lookup))
2259 p = status_lookup[dev->power.runtime_status];
2260 else
2261 WARN_ON(1);
2262
2263 seq_puts(s, p);
2264}
2265#else
2266static void rtpm_status_str(struct seq_file *s, struct device *dev)
2267{
2268 seq_puts(s, "active");
2269}
2270#endif
2271
2272static int pm_genpd_summary_one(struct seq_file *s,
2273 struct generic_pm_domain *gpd)
2274{
2275 static const char * const status_lookup[] = {
2276 [GPD_STATE_ACTIVE] = "on",
2277 [GPD_STATE_WAIT_MASTER] = "wait-master",
2278 [GPD_STATE_BUSY] = "busy",
2279 [GPD_STATE_REPEAT] = "off-in-progress",
2280 [GPD_STATE_POWER_OFF] = "off"
2281 };
2282 struct pm_domain_data *pm_data;
2283 const char *kobj_path;
2284 struct gpd_link *link;
2285 int ret;
2286
2287 ret = mutex_lock_interruptible(&gpd->lock);
2288 if (ret)
2289 return -ERESTARTSYS;
2290
2291 if (WARN_ON(gpd->status >= ARRAY_SIZE(status_lookup)))
2292 goto exit;
2293 seq_printf(s, "%-30s %-15s ", gpd->name, status_lookup[gpd->status]);
2294
2295 /*
2296 * Modifications on the list require holding locks on both
2297 * master and slave, so we are safe.
2298 * Also gpd->name is immutable.
2299 */
2300 list_for_each_entry(link, &gpd->master_links, master_node) {
2301 seq_printf(s, "%s", link->slave->name);
2302 if (!list_is_last(&link->master_node, &gpd->master_links))
2303 seq_puts(s, ", ");
2304 }
2305
2306 list_for_each_entry(pm_data, &gpd->dev_list, list_node) {
2307 kobj_path = kobject_get_path(&pm_data->dev->kobj, GFP_KERNEL);
2308 if (kobj_path == NULL)
2309 continue;
2310
2311 seq_printf(s, "\n %-50s ", kobj_path);
2312 rtpm_status_str(s, pm_data->dev);
2313 kfree(kobj_path);
2314 }
2315
2316 seq_puts(s, "\n");
2317exit:
2318 mutex_unlock(&gpd->lock);
2319
2320 return 0;
2321}
2322
2323static int pm_genpd_summary_show(struct seq_file *s, void *data)
2324{
2325 struct generic_pm_domain *gpd;
2326 int ret = 0;
2327
2328 seq_puts(s, " domain status slaves\n");
2329 seq_puts(s, " /device runtime status\n");
2330 seq_puts(s, "----------------------------------------------------------------------\n");
2331
2332 ret = mutex_lock_interruptible(&gpd_list_lock);
2333 if (ret)
2334 return -ERESTARTSYS;
2335
2336 list_for_each_entry(gpd, &gpd_list, gpd_list_node) {
2337 ret = pm_genpd_summary_one(s, gpd);
2338 if (ret)
2339 break;
2340 }
2341 mutex_unlock(&gpd_list_lock);
2342
2343 return ret;
2344}
2345
2346static int pm_genpd_summary_open(struct inode *inode, struct file *file)
2347{
2348 return single_open(file, pm_genpd_summary_show, NULL);
2349}
2350
2351static const struct file_operations pm_genpd_summary_fops = {
2352 .open = pm_genpd_summary_open,
2353 .read = seq_read,
2354 .llseek = seq_lseek,
2355 .release = single_release,
2356};
2357
2358static int __init pm_genpd_debug_init(void)
2359{
2360 struct dentry *d;
2361
2362 pm_genpd_debugfs_dir = debugfs_create_dir("pm_genpd", NULL);
2363
2364 if (!pm_genpd_debugfs_dir)
2365 return -ENOMEM;
2366
2367 d = debugfs_create_file("pm_genpd_summary", S_IRUGO,
2368 pm_genpd_debugfs_dir, NULL, &pm_genpd_summary_fops);
2369 if (!d)
2370 return -ENOMEM;
2371
2372 return 0;
2373}
2374late_initcall(pm_genpd_debug_init);
2375
2376static void __exit pm_genpd_debug_exit(void)
2377{
2378 debugfs_remove_recursive(pm_genpd_debugfs_dir);
2379}
2380__exitcall(pm_genpd_debug_exit);
2381#endif /* CONFIG_PM_ADVANCED_DEBUG */