interconnect: qcom: Add debug library
Add a debug library that provides QCOM-specific interconnect debug features. This includes support for printing all enabled interconnect votes to console when entering suspend. Change-Id: I61302f95c94176f833bd1c88ddc76b88121b3f79 Signed-off-by: Mike Tipton <mdtipton@codeaurora.org>
This commit is contained in:
parent
428497d097
commit
98b202c507
@ -178,3 +178,11 @@ config INTERCONNECT_QCOM_SMD_RPM
|
||||
|
||||
config INTERCONNECT_QCOM_QOS
|
||||
tristate
|
||||
|
||||
config INTERCONNECT_QCOM_DEBUG
|
||||
tristate "QCOM-specific interconnect debug features"
|
||||
depends on INTERCONNECT_QCOM
|
||||
help
|
||||
This driver provides QCOM-specific interconnect debug features. These
|
||||
features include optionally printing all enabled interconnect votes
|
||||
when entering suspend.
|
||||
|
@ -43,3 +43,4 @@ obj-$(CONFIG_INTERCONNECT_QCOM_SM8350) += qnoc-sm8350.o
|
||||
obj-$(CONFIG_INTERCONNECT_QCOM_SM8450) += qnoc-sm8450.o
|
||||
obj-$(CONFIG_INTERCONNECT_QCOM_SMD_RPM) += icc-smd-rpm.o
|
||||
obj-$(CONFIG_INTERCONNECT_QCOM_QOS) += qnoc-qos.o
|
||||
obj-$(CONFIG_INTERCONNECT_QCOM_DEBUG) += icc-debug.o
|
||||
|
175
drivers/interconnect/qcom/icc-debug.c
Normal file
175
drivers/interconnect/qcom/icc-debug.c
Normal file
@ -0,0 +1,175 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/* Copyright (c) 2021, The Linux Foundation. All rights reserved. */
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/interconnect-provider.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/slab.h>
|
||||
#include <trace/events/power.h>
|
||||
|
||||
#include "../internal.h"
|
||||
|
||||
static LIST_HEAD(icc_providers);
|
||||
static DEFINE_MUTEX(debug_lock);
|
||||
static struct dentry *dentry_suspend;
|
||||
static bool debug_suspend;
|
||||
|
||||
struct qcom_icc_debug_provider {
|
||||
struct list_head list;
|
||||
struct icc_provider *provider;
|
||||
};
|
||||
|
||||
static int icc_print_enabled(void)
|
||||
{
|
||||
struct qcom_icc_debug_provider *dp;
|
||||
struct icc_provider *provider;
|
||||
struct icc_node *n;
|
||||
struct icc_req *r;
|
||||
u32 avg_bw, peak_bw;
|
||||
|
||||
pr_info(" node tag avg peak\n");
|
||||
pr_info("--------------------------------------------------------------------\n");
|
||||
|
||||
list_for_each_entry(dp, &icc_providers, list) {
|
||||
provider = dp->provider;
|
||||
|
||||
list_for_each_entry(n, &provider->nodes, node_list) {
|
||||
if (!n->avg_bw && !n->peak_bw)
|
||||
continue;
|
||||
|
||||
pr_info("%-42s %12u %12u\n",
|
||||
n->name, n->avg_bw, n->peak_bw);
|
||||
|
||||
hlist_for_each_entry(r, &n->req_list, req_node) {
|
||||
if (!r->dev)
|
||||
continue;
|
||||
|
||||
if (r->enabled) {
|
||||
avg_bw = r->avg_bw;
|
||||
peak_bw = r->peak_bw;
|
||||
} else {
|
||||
avg_bw = 0;
|
||||
peak_bw = 0;
|
||||
}
|
||||
|
||||
if (avg_bw || peak_bw)
|
||||
pr_info(" %-27s %12u %12u %12u\n",
|
||||
dev_name(r->dev), r->tag, avg_bw, peak_bw);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int icc_debug_suspend_get(void *data, u64 *val)
|
||||
{
|
||||
*val = debug_suspend;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void icc_debug_suspend_trace_probe(void *unused, const char *action,
|
||||
int val, bool start)
|
||||
{
|
||||
if (start && val > 0 && !strcmp("machine_suspend", action)) {
|
||||
pr_info("Enabled interconnect votes:\n");
|
||||
icc_print_enabled();
|
||||
}
|
||||
}
|
||||
|
||||
static int icc_debug_suspend_set(void *data, u64 val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
val = !!val;
|
||||
if (val == debug_suspend)
|
||||
return 0;
|
||||
|
||||
if (val)
|
||||
ret = register_trace_suspend_resume(icc_debug_suspend_trace_probe, NULL);
|
||||
else
|
||||
ret = unregister_trace_suspend_resume(icc_debug_suspend_trace_probe, NULL);
|
||||
|
||||
if (ret) {
|
||||
pr_err("%s: Failed to %sregister suspend trace callback, ret=%d\n",
|
||||
__func__, val ? "" : "un", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
debug_suspend = val;
|
||||
|
||||
return 0;
|
||||
}
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(icc_debug_suspend_fops, icc_debug_suspend_get,
|
||||
icc_debug_suspend_set, "%llu\n");
|
||||
|
||||
int qcom_icc_debug_register(struct icc_provider *provider)
|
||||
{
|
||||
struct qcom_icc_debug_provider *dp;
|
||||
|
||||
dp = kzalloc(sizeof(*dp), GFP_KERNEL);
|
||||
if (!dp)
|
||||
return -ENOMEM;
|
||||
|
||||
dp->provider = provider;
|
||||
|
||||
mutex_lock(&debug_lock);
|
||||
list_add_tail(&dp->list, &icc_providers);
|
||||
mutex_unlock(&debug_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(qcom_icc_debug_register);
|
||||
|
||||
int qcom_icc_debug_unregister(struct icc_provider *provider)
|
||||
{
|
||||
struct qcom_icc_debug_provider *dp, *temp;
|
||||
|
||||
mutex_lock(&debug_lock);
|
||||
|
||||
list_for_each_entry_safe(dp, temp, &icc_providers, list) {
|
||||
if (dp->provider == provider) {
|
||||
list_del(&dp->list);
|
||||
kfree(dp);
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&debug_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(qcom_icc_debug_unregister);
|
||||
|
||||
static int __init qcom_icc_debug_init(void)
|
||||
{
|
||||
static struct dentry *dir;
|
||||
int ret;
|
||||
|
||||
dir = debugfs_lookup("interconnect", NULL);
|
||||
if (IS_ERR_OR_NULL(dir)) {
|
||||
ret = PTR_ERR(dir);
|
||||
pr_err("%s: unable to find root interconnect debugfs directory, ret=%d\n",
|
||||
__func__, ret);
|
||||
return 0;
|
||||
}
|
||||
|
||||
dentry_suspend = debugfs_create_file_unsafe("debug_suspend",
|
||||
0644, dir, NULL,
|
||||
&icc_debug_suspend_fops);
|
||||
return 0;
|
||||
}
|
||||
module_init(qcom_icc_debug_init);
|
||||
|
||||
static void __exit qcom_icc_debug_exit(void)
|
||||
{
|
||||
debugfs_remove(dentry_suspend);
|
||||
if (debug_suspend)
|
||||
unregister_trace_suspend_resume(icc_debug_suspend_trace_probe, NULL);
|
||||
}
|
||||
module_exit(qcom_icc_debug_exit);
|
||||
|
||||
MODULE_DESCRIPTION("QCOM ICC debug library");
|
||||
MODULE_LICENSE("GPL v2");
|
12
drivers/interconnect/qcom/icc-debug.h
Normal file
12
drivers/interconnect/qcom/icc-debug.h
Normal file
@ -0,0 +1,12 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* Copyright (c) 2021, The Linux Foundation. All rights reserved. */
|
||||
|
||||
#ifndef __QCOM_ICC_DEBUG_H__
|
||||
#define __QCOM_ICC_DEBUG_H__
|
||||
|
||||
#include <linux/interconnect-provider.h>
|
||||
|
||||
int qcom_icc_debug_register(struct icc_provider *provider);
|
||||
int qcom_icc_debug_unregister(struct icc_provider *provider);
|
||||
|
||||
#endif
|
@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2020, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
@ -13,6 +13,7 @@
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "bcm-voter.h"
|
||||
#include "icc-debug.h"
|
||||
#include "icc-rpmh.h"
|
||||
#include "qnoc-qos.h"
|
||||
|
||||
@ -329,6 +330,8 @@ int qcom_icc_rpmh_probe(struct platform_device *pdev)
|
||||
provider->set = qcom_icc_set;
|
||||
provider->aggregate = qcom_icc_aggregate;
|
||||
|
||||
qcom_icc_debug_register(provider);
|
||||
|
||||
mutex_lock(&probe_list_lock);
|
||||
list_add_tail(&qp->probe_list, &qnoc_probe_list);
|
||||
mutex_unlock(&probe_list_lock);
|
||||
@ -347,6 +350,7 @@ int qcom_icc_rpmh_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct qcom_icc_provider *qp = platform_get_drvdata(pdev);
|
||||
|
||||
qcom_icc_debug_unregister(&qp->provider);
|
||||
clk_bulk_put_all(qp->num_clks, qp->clks);
|
||||
|
||||
icc_nodes_remove(&qp->provider);
|
||||
|
Loading…
Reference in New Issue
Block a user