Merge branch 'icc-sa8775p' into icc-next

Add Epoch Subsystem (EPSS) L3 provider support on SA8775P SoCs.

Current interconnect framework is based on static IDs for creating node
and registering with framework. This becomes a limitation for topologies
where there are multiple instances of same interconnect provider.
Modified interconnect framework APIs to create and link icc node with
dynamic IDs, this will help to overcome the dependency on static IDs.

* icc-sa8775p
  dt-bindings: interconnect: Add EPSS L3 compatible for SA8775P
  interconnect: core: Add dynamic id allocation support
  interconnect: qcom: Add multidev EPSS L3 support
  interconnect: qcom: icc-rpmh: Add dynamic icc node id support
  interconnect: qcom: sa8775p: Add dynamic icc node id support

Link: https://lore.kernel.org/r/20250415095343.32125-1-quic_rlaggysh@quicinc.com
Signed-off-by: Georgi Djakov <djakov@kernel.org>
pull/1259/head
Georgi Djakov 2025-05-19 17:09:50 +03:00
commit 5fed7fe33c
8 changed files with 475 additions and 635 deletions

View File

@ -28,6 +28,7 @@ properties:
- const: qcom,osm-l3
- items:
- enum:
- qcom,sa8775p-epss-l3
- qcom,sc7280-epss-l3
- qcom,sc8280xp-epss-l3
- qcom,sm6375-cpucp-l3

View File

@ -20,6 +20,8 @@
#include "internal.h"
#define ICC_DYN_ID_START 10000
#define CREATE_TRACE_POINTS
#include "trace.h"
@ -826,7 +828,12 @@ static struct icc_node *icc_node_create_nolock(int id)
if (!node)
return ERR_PTR(-ENOMEM);
id = idr_alloc(&icc_idr, node, id, id + 1, GFP_KERNEL);
/* dynamic id allocation */
if (id == ICC_ALLOC_DYN_ID)
id = idr_alloc(&icc_idr, node, ICC_DYN_ID_START, 0, GFP_KERNEL);
else
id = idr_alloc(&icc_idr, node, id, id + 1, GFP_KERNEL);
if (id < 0) {
WARN(1, "%s: couldn't get idr\n", __func__);
kfree(node);
@ -838,6 +845,25 @@ static struct icc_node *icc_node_create_nolock(int id)
return node;
}
/**
* icc_node_create_dyn() - create a node with dynamic id
*
* Return: icc_node pointer on success, or ERR_PTR() on error
*/
struct icc_node *icc_node_create_dyn(void)
{
struct icc_node *node;
mutex_lock(&icc_lock);
node = icc_node_create_nolock(ICC_ALLOC_DYN_ID);
mutex_unlock(&icc_lock);
return node;
}
EXPORT_SYMBOL_GPL(icc_node_create_dyn);
/**
* icc_node_create() - create a node
* @id: node id
@ -884,6 +910,56 @@ void icc_node_destroy(int id)
}
EXPORT_SYMBOL_GPL(icc_node_destroy);
/**
* icc_link_nodes() - create link between two nodes
* @src_node: source node
* @dst_node: destination node
*
* Create a link between two nodes. The nodes might belong to different
* interconnect providers and the @dst_node might not exist (if the
* provider driver has not probed yet). So just create the @dst_node
* and when the actual provider driver is probed, the rest of the node
* data is filled.
*
* Return: 0 on success, or an error code otherwise
*/
int icc_link_nodes(struct icc_node *src_node, struct icc_node **dst_node)
{
struct icc_node **new;
int ret = 0;
if (!src_node->provider)
return -EINVAL;
mutex_lock(&icc_lock);
if (!*dst_node) {
*dst_node = icc_node_create_nolock(ICC_ALLOC_DYN_ID);
if (IS_ERR(*dst_node)) {
ret = PTR_ERR(*dst_node);
goto out;
}
}
new = krealloc(src_node->links,
(src_node->num_links + 1) * sizeof(*src_node->links),
GFP_KERNEL);
if (!new) {
ret = -ENOMEM;
goto out;
}
src_node->links = new;
src_node->links[src_node->num_links++] = *dst_node;
out:
mutex_unlock(&icc_lock);
return ret;
}
EXPORT_SYMBOL_GPL(icc_link_nodes);
/**
* icc_link_create() - create a link between two nodes
* @node: source node id
@ -962,6 +1038,10 @@ void icc_node_add(struct icc_node *node, struct icc_provider *provider)
node->avg_bw = node->init_avg;
node->peak_bw = node->init_peak;
if (node->id >= ICC_DYN_ID_START)
node->name = devm_kasprintf(provider->dev, GFP_KERNEL, "%s@%s",
node->name, dev_name(provider->dev));
if (node->avg_bw || node->peak_bw) {
if (provider->pre_aggregate)
provider->pre_aggregate(node);

View File

@ -280,7 +280,14 @@ int qcom_icc_rpmh_probe(struct platform_device *pdev)
if (!qn)
continue;
node = icc_node_create(qn->id);
if (desc->alloc_dyn_id) {
if (!qn->node)
qn->node = icc_node_create_dyn();
node = qn->node;
} else {
node = icc_node_create(qn->id);
}
if (IS_ERR(node)) {
ret = PTR_ERR(node);
goto err_remove_nodes;
@ -290,8 +297,12 @@ int qcom_icc_rpmh_probe(struct platform_device *pdev)
node->data = qn;
icc_node_add(node, provider);
for (j = 0; j < qn->num_links; j++)
icc_link_create(node, qn->links[j]);
for (j = 0; j < qn->num_links; j++) {
if (desc->alloc_dyn_id)
icc_link_nodes(node, &qn->link_nodes[j]->node);
else
icc_link_create(node, qn->links[j]);
}
data->nodes[i] = node;
}

View File

@ -83,6 +83,8 @@ struct qcom_icc_qosbox {
* @name: the node name used in debugfs
* @links: an array of nodes where we can go next while traversing
* @id: a unique node identifier
* @link_nodes: links associated with this node
* @node: icc_node associated with this node
* @num_links: the total number of @links
* @channels: num of channels at this node
* @buswidth: width of the interconnect between a node and the bus
@ -96,6 +98,8 @@ struct qcom_icc_node {
const char *name;
u16 links[MAX_LINKS];
u16 id;
struct qcom_icc_node **link_nodes;
struct icc_node *node;
u16 num_links;
u16 channels;
u16 buswidth;
@ -154,6 +158,7 @@ struct qcom_icc_desc {
struct qcom_icc_bcm * const *bcms;
size_t num_bcms;
bool qos_requires_clocks;
bool alloc_dyn_id;
};
int qcom_icc_aggregate(struct icc_node *node, u32 tag, u32 avg_bw,

View File

@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
* Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/args.h>
@ -32,8 +33,6 @@
#define EPSS_REG_FREQ_LUT 0x100
#define EPSS_REG_PERF_STATE 0x320
#define OSM_L3_MAX_LINKS 1
#define to_osm_l3_provider(_provider) \
container_of(_provider, struct qcom_osm_l3_icc_provider, provider)
@ -48,16 +47,10 @@ struct qcom_osm_l3_icc_provider {
/**
* struct qcom_osm_l3_node - Qualcomm specific interconnect nodes
* @name: the node name used in debugfs
* @links: an array of nodes where we can go next while traversing
* @id: a unique node identifier
* @num_links: the total number of @links
* @buswidth: width of the interconnect between a node and the bus
*/
struct qcom_osm_l3_node {
const char *name;
u16 links[OSM_L3_MAX_LINKS];
u16 id;
u16 num_links;
u16 buswidth;
};
@ -69,30 +62,22 @@ struct qcom_osm_l3_desc {
unsigned int reg_perf_state;
};
enum {
OSM_L3_MASTER_NODE = 10000,
OSM_L3_SLAVE_NODE,
};
#define DEFINE_QNODE(_name, _id, _buswidth, ...) \
#define DEFINE_QNODE(_name, _buswidth) \
static const struct qcom_osm_l3_node _name = { \
.name = #_name, \
.id = _id, \
.buswidth = _buswidth, \
.num_links = COUNT_ARGS(__VA_ARGS__), \
.links = { __VA_ARGS__ }, \
}
DEFINE_QNODE(osm_l3_master, OSM_L3_MASTER_NODE, 16, OSM_L3_SLAVE_NODE);
DEFINE_QNODE(osm_l3_slave, OSM_L3_SLAVE_NODE, 16);
DEFINE_QNODE(osm_l3_slave, 16);
DEFINE_QNODE(osm_l3_master, 16);
static const struct qcom_osm_l3_node * const osm_l3_nodes[] = {
[MASTER_OSM_L3_APPS] = &osm_l3_master,
[SLAVE_OSM_L3] = &osm_l3_slave,
};
DEFINE_QNODE(epss_l3_master, OSM_L3_MASTER_NODE, 32, OSM_L3_SLAVE_NODE);
DEFINE_QNODE(epss_l3_slave, OSM_L3_SLAVE_NODE, 32);
DEFINE_QNODE(epss_l3_slave, 32);
DEFINE_QNODE(epss_l3_master, 32);
static const struct qcom_osm_l3_node * const epss_l3_nodes[] = {
[MASTER_EPSS_L3_APPS] = &epss_l3_master,
@ -242,10 +227,10 @@ static int qcom_osm_l3_probe(struct platform_device *pdev)
icc_provider_init(provider);
/* Create nodes */
for (i = 0; i < num_nodes; i++) {
size_t j;
node = icc_node_create_dyn();
node = icc_node_create(qnodes[i]->id);
if (IS_ERR(node)) {
ret = PTR_ERR(node);
goto err;
@ -256,12 +241,12 @@ static int qcom_osm_l3_probe(struct platform_device *pdev)
node->data = (void *)qnodes[i];
icc_node_add(node, provider);
for (j = 0; j < qnodes[i]->num_links; j++)
icc_link_create(node, qnodes[i]->links[j]);
data->nodes[i] = node;
}
/* Create link */
icc_link_nodes(data->nodes[MASTER_OSM_L3_APPS], &data->nodes[SLAVE_OSM_L3]);
ret = icc_provider_register(provider);
if (ret)
goto err;
@ -278,6 +263,7 @@ err:
static const struct of_device_id osm_l3_of_match[] = {
{ .compatible = "qcom,epss-l3", .data = &epss_l3_l3_vote },
{ .compatible = "qcom,osm-l3", .data = &osm_l3 },
{ .compatible = "qcom,sa8775p-epss-l3", .data = &epss_l3_perf_state },
{ .compatible = "qcom,sc7180-osm-l3", .data = &osm_l3 },
{ .compatible = "qcom,sc7280-epss-l3", .data = &epss_l3_perf_state },
{ .compatible = "qcom,sdm845-osm-l3", .data = &osm_l3 },

File diff suppressed because it is too large Load Diff

View File

@ -116,8 +116,10 @@ struct icc_node {
int icc_std_aggregate(struct icc_node *node, u32 tag, u32 avg_bw,
u32 peak_bw, u32 *agg_avg, u32 *agg_peak);
struct icc_node *icc_node_create_dyn(void);
struct icc_node *icc_node_create(int id);
void icc_node_destroy(int id);
int icc_link_nodes(struct icc_node *src_node, struct icc_node **dst_node);
int icc_link_create(struct icc_node *node, const int dst_id);
void icc_node_add(struct icc_node *node, struct icc_provider *provider);
void icc_node_del(struct icc_node *node);
@ -136,6 +138,11 @@ static inline int icc_std_aggregate(struct icc_node *node, u32 tag, u32 avg_bw,
return -ENOTSUPP;
}
static inline struct icc_node *icc_node_create_dyn(void)
{
return ERR_PTR(-EOPNOTSUPP);
}
static inline struct icc_node *icc_node_create(int id)
{
return ERR_PTR(-ENOTSUPP);
@ -145,6 +152,11 @@ static inline void icc_node_destroy(int id)
{
}
static inline int icc_link_nodes(struct icc_node *src_node, struct icc_node **dst_node)
{
return -EOPNOTSUPP;
}
static inline int icc_link_create(struct icc_node *node, const int dst_id)
{
return -ENOTSUPP;

View File

@ -20,6 +20,9 @@
#define Mbps_to_icc(x) ((x) * 1000 / 8)
#define Gbps_to_icc(x) ((x) * 1000 * 1000 / 8)
/* macro to indicate dynamic id allocation */
#define ICC_ALLOC_DYN_ID -1
struct icc_path;
struct device;