mirror of
https://github.com/torvalds/linux.git
synced 2026-07-31 03:27:03 +02:00
Merge "interconnect: qcom: qnoc-qos: Don't skip writing urg_fwd when it's zero"
This commit is contained in:
commit
22c17bcabb
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
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"
|
||||
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user