Commit 5fed7fe3 authored by Georgi Djakov's avatar Georgi Djakov
Browse files

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: default avatarGeorgi Djakov <djakov@kernel.org>
parents b0779ea4 b1244645
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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
+81 −1
Original line number Diff line number Diff line
@@ -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);

	/* 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);
+14 −3
Original line number Diff line number Diff line
@@ -280,7 +280,14 @@ int qcom_icc_rpmh_probe(struct platform_device *pdev)
		if (!qn)
			continue;

		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++)
		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;
	}
+5 −0
Original line number Diff line number Diff line
@@ -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,
+12 −26
Original line number Diff line number Diff line
// 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 @@ static int qcom_osm_l3_probe(struct platform_device *pdev)
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 },
Loading