Merge "interconnect: qcom: qnoc-qos: Don't skip writing urg_fwd when it's zero"

This commit is contained in:
qctecmdr 2022-05-06 21:19:38 -07:00 committed by Gerrit - the friendly Code Review server
commit 22c17bcabb
9 changed files with 266 additions and 24 deletions

View File

@ -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.

View File

@ -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

View File

@ -66,6 +66,7 @@ static void bcm_aggregate(struct qcom_icc_bcm *bcm, bool init)
size_t i, bucket;
u64 agg_avg[QCOM_ICC_NUM_BUCKETS] = {0};
u64 agg_peak[QCOM_ICC_NUM_BUCKETS] = {0};
bool perf_mode[QCOM_ICC_NUM_BUCKETS] = {0};
u64 temp;
for (bucket = 0; bucket < QCOM_ICC_NUM_BUCKETS; bucket++) {
@ -78,6 +79,8 @@ static void bcm_aggregate(struct qcom_icc_bcm *bcm, bool init)
temp = bcm_div(node->max_peak[bucket] * bcm->aux_data.width,
node->buswidth);
agg_peak[bucket] = max(agg_peak[bucket], temp);
perf_mode[bucket] |= node->perf_mode[bucket];
}
temp = agg_avg[bucket] * bcm->vote_scale;
@ -85,6 +88,13 @@ static void bcm_aggregate(struct qcom_icc_bcm *bcm, bool init)
temp = agg_peak[bucket] * bcm->vote_scale;
bcm->vote_y[bucket] = bcm_div(temp, bcm->aux_data.unit);
if (bcm->enable_mask && (bcm->vote_x[bucket] || bcm->vote_y[bucket])) {
bcm->vote_x[bucket] = 0;
bcm->vote_y[bucket] = bcm->enable_mask;
if (perf_mode[bucket])
bcm->vote_y[bucket] |= bcm->perf_mode_mask;
}
}
if (bcm->keepalive) {

View 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");

View 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

View File

@ -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"
@ -35,6 +36,7 @@ void qcom_icc_pre_aggregate(struct icc_node *node)
for (i = 0; i < QCOM_ICC_NUM_BUCKETS; i++) {
qn->sum_avg[i] = 0;
qn->max_peak[i] = 0;
qn->perf_mode[i] = false;
}
for (i = 0; i < qn->num_bcms; i++)
@ -67,6 +69,8 @@ int qcom_icc_aggregate(struct icc_node *node, u32 tag, u32 avg_bw,
if (tag & BIT(i)) {
qn->sum_avg[i] += avg_bw;
qn->max_peak[i] = max_t(u32, qn->max_peak[i], peak_bw);
if (tag & QCOM_ICC_TAG_PERF_MODE && (avg_bw || peak_bw))
qn->perf_mode[i] = true;
}
if (node->init_avg || node->init_peak) {
@ -122,6 +126,15 @@ int qcom_icc_set_stub(struct icc_node *src, struct icc_node *dst)
}
EXPORT_SYMBOL(qcom_icc_set_stub);
int qcom_icc_get_bw_stub(struct icc_node *node, u32 *avg, u32 *peak)
{
*avg = 0;
*peak = 0;
return 0;
}
EXPORT_SYMBOL(qcom_icc_get_bw_stub);
struct icc_node_data *qcom_icc_xlate_extended(struct of_phandle_args *spec, void *data)
{
struct icc_node_data *ndata;
@ -184,8 +197,8 @@ int qcom_icc_bcm_init(struct qcom_icc_bcm *bcm, struct device *dev)
return -EINVAL;
}
bcm->aux_data.unit = le32_to_cpu(data->unit);
bcm->aux_data.width = le16_to_cpu(data->width);
bcm->aux_data.unit = max_t(u32, 1, le32_to_cpu(data->unit));
bcm->aux_data.width = max_t(u16, 1, le16_to_cpu(data->width));
bcm->aux_data.vcd = data->vcd;
bcm->aux_data.reserved = data->reserved;
INIT_LIST_HEAD(&bcm->list);
@ -250,6 +263,8 @@ int qcom_icc_rpmh_probe(struct platform_device *pdev)
if (!data)
return -ENOMEM;
qp->stub = of_property_read_bool(pdev->dev.of_node, "qcom,stub");
provider = &qp->provider;
provider->dev = dev;
provider->set = qcom_icc_set_stub;
@ -258,22 +273,26 @@ int qcom_icc_rpmh_probe(struct platform_device *pdev)
provider->xlate_extended = qcom_icc_xlate_extended;
INIT_LIST_HEAD(&provider->nodes);
provider->data = data;
provider->get_bw = qcom_icc_get_bw_stub;
qp->dev = dev;
qp->bcms = desc->bcms;
qp->num_bcms = desc->num_bcms;
qp->num_voters = desc->num_voters;
qp->voters = devm_kcalloc(&pdev->dev, qp->num_voters,
sizeof(*qp->voters), GFP_KERNEL);
if (!qp->stub) {
qp->num_bcms = desc->num_bcms;
qp->num_voters = desc->num_voters;
if (!qp->voters)
return -ENOMEM;
qp->voters = devm_kcalloc(&pdev->dev, qp->num_voters,
sizeof(*qp->voters), GFP_KERNEL);
for (i = 0; i < qp->num_voters; i++) {
qp->voters[i] = of_bcm_voter_get(qp->dev, desc->voters[i]);
if (IS_ERR(qp->voters[i]))
return PTR_ERR(qp->voters[i]);
if (!qp->voters)
return -ENOMEM;
for (i = 0; i < qp->num_voters; i++) {
qp->voters[i] = of_bcm_voter_get(qp->dev, desc->voters[i]);
if (IS_ERR(qp->voters[i]))
return PTR_ERR(qp->voters[i]);
}
}
qp->regmap = qcom_icc_rpmh_map(pdev, desc);
@ -326,8 +345,12 @@ int qcom_icc_rpmh_probe(struct platform_device *pdev)
data->num_nodes = num_nodes;
platform_set_drvdata(pdev, qp);
provider->set = qcom_icc_set;
provider->aggregate = qcom_icc_aggregate;
if (!qp->stub) {
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);
@ -347,6 +370,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);

View File

@ -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.
*/
#ifndef __DRIVERS_INTERCONNECT_QCOM_ICC_RPMH_H__
@ -32,6 +32,7 @@ struct qcom_icc_provider {
int num_clks;
struct bcm_voter **voters;
size_t num_voters;
bool stub;
};
/**
@ -63,6 +64,7 @@ struct bcm_db {
* @buswidth: width of the interconnect between a node and the bus
* @sum_avg: current sum aggregate value of all avg bw requests
* @max_peak: current max aggregate value of all peak bw requests
* @perf_mode: current OR aggregate value of all QCOM_ICC_TAG_PERF_MODE votes
* @bcms: list of bcms associated with this logical node
* @num_bcms: num of @bcms
*/
@ -75,6 +77,7 @@ struct qcom_icc_node {
u16 buswidth;
u64 sum_avg[QCOM_ICC_NUM_BUCKETS];
u64 max_peak[QCOM_ICC_NUM_BUCKETS];
bool perf_mode[QCOM_ICC_NUM_BUCKETS];
struct qcom_icc_bcm *bcms[MAX_BCM_PER_NODE];
size_t num_bcms;
struct regmap *regmap;
@ -91,6 +94,8 @@ struct qcom_icc_node {
* @vote_x: aggregated threshold values, represents sum_bw when @type is bw bcm
* @vote_y: aggregated threshold values, represents peak_bw when @type is bw bcm
* @vote_scale: scaling factor for vote_x and vote_y
* @enable_mask: optional mask to send as vote instead of vote_x/vote_y
* @perf_mode_mask: mask to OR with enable_mask when QCOM_ICC_TAG_PERF_MODE is set
* @dirty: flag used to indicate whether the bcm needs to be committed
* @keepalive: flag used to indicate whether a keepalive is required
* @aux_data: auxiliary data used when calculating threshold values and
@ -107,6 +112,8 @@ struct qcom_icc_bcm {
u64 vote_x[QCOM_ICC_NUM_BUCKETS];
u64 vote_y[QCOM_ICC_NUM_BUCKETS];
u64 vote_scale;
u32 enable_mask;
u32 perf_mode_mask;
bool dirty;
bool keepalive;
struct bcm_db aux_data;
@ -154,4 +161,5 @@ void qcom_icc_pre_aggregate(struct icc_node *node);
int qcom_icc_rpmh_probe(struct platform_device *pdev);
int qcom_icc_rpmh_remove(struct platform_device *pdev);
void qcom_icc_rpmh_sync_state(struct device *dev);
int qcom_icc_get_bw_stub(struct icc_node *node, u32 *avg, u32 *peak);
#endif

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2020, The Linux Foundation. All rights reserved.
* Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
*
*/
@ -13,7 +13,7 @@
#define QOSGEN_MAINCTL_LO(p, qp) ((p)->offsets[qp] + \
(p)->regs[QOSGEN_OFF_MAINCTL_LO])
# define QOS_SLV_URG_MSG_EN BIT(3)
#define QOS_SLV_URG_MSG_EN_SHFT 3
# define QOS_DFLT_PRIO_MASK 0x7
# define QOS_DFLT_PRIO_SHFT 4
@ -49,11 +49,9 @@ static void qcom_icc_set_qos(struct qcom_icc_node *node)
QOS_DFLT_PRIO_MASK << QOS_DFLT_PRIO_SHFT,
qos->config->prio << QOS_DFLT_PRIO_SHFT);
if (qos->config->urg_fwd)
regmap_update_bits(node->regmap,
QOSGEN_MAINCTL_LO(qos, port),
QOS_SLV_URG_MSG_EN,
QOS_SLV_URG_MSG_EN);
regmap_update_bits(node->regmap, QOSGEN_MAINCTL_LO(qos, port),
BIT(QOS_SLV_URG_MSG_EN_SHFT),
qos->config->urg_fwd << QOS_SLV_URG_MSG_EN_SHFT);
}
}

View File

@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2020, The Linux Foundation. All rights reserved.
* Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
*/
#ifndef __DT_BINDINGS_INTERCONNECT_QCOM_ICC_H
@ -23,4 +23,10 @@
#define QCOM_ICC_TAG_ALWAYS (QCOM_ICC_TAG_AMC | QCOM_ICC_TAG_WAKE |\
QCOM_ICC_TAG_SLEEP)
/*
* PERF_MODE indicates that each node in the requested path should use
* performance-optimized settings if supported by the node.
*/
#define QCOM_ICC_TAG_PERF_MODE (1 << 3)
#endif