Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
+39
View File
@@ -0,0 +1,39 @@
# SPDX-License-Identifier: GPL-2.0-only
menuconfig INTERCONNECT
bool "On-Chip Interconnect management support"
help
Support for management of the on-chip interconnects.
This framework is designed to provide a generic interface for
managing the interconnects in a SoC.
If unsure, say no.
if INTERCONNECT
source "drivers/interconnect/imx/Kconfig"
source "drivers/interconnect/mediatek/Kconfig"
source "drivers/interconnect/qcom/Kconfig"
source "drivers/interconnect/samsung/Kconfig"
config INTERCONNECT_CLK
tristate
depends on COMMON_CLK
help
Support for wrapping clocks into the interconnect nodes.
config INTERCONNECT_KUNIT_TEST
tristate "KUnit tests for Interconnect framework"
depends on KUNIT
default KUNIT_ALL_TESTS
help
This builds the KUnit test suite for the generic system interconnect
framework.
The tests cover the core functionality of the interconnect subsystem,
including provider/consumer APIs, topology management, and bandwidth
aggregation logic.
If unsure, say N.
endif
+14
View File
@@ -0,0 +1,14 @@
# SPDX-License-Identifier: GPL-2.0
CFLAGS_core.o := -I$(src)
icc-core-objs := core.o bulk.o debugfs-client.o
obj-$(CONFIG_INTERCONNECT) += icc-core.o
obj-$(CONFIG_INTERCONNECT_IMX) += imx/
obj-$(CONFIG_INTERCONNECT_MTK) += mediatek/
obj-$(CONFIG_INTERCONNECT_QCOM) += qcom/
obj-$(CONFIG_INTERCONNECT_SAMSUNG) += samsung/
obj-$(CONFIG_INTERCONNECT_CLK) += icc-clk.o
obj-$(CONFIG_INTERCONNECT_KUNIT_TEST) += icc-kunit.o
+159
View File
@@ -0,0 +1,159 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/interconnect-provider.h>
#include <linux/device.h>
#include <linux/export.h>
/**
* of_icc_bulk_get() - get interconnect paths
* @dev: the device requesting the path
* @num_paths: the number of icc_bulk_data
* @paths: the table with the paths we want to get
*
* Returns 0 on success or negative errno otherwise.
*/
int __must_check of_icc_bulk_get(struct device *dev, int num_paths,
struct icc_bulk_data *paths)
{
int ret, i;
for (i = 0; i < num_paths; i++) {
paths[i].path = of_icc_get(dev, paths[i].name);
if (IS_ERR(paths[i].path)) {
ret = PTR_ERR(paths[i].path);
if (ret != -EPROBE_DEFER)
dev_err(dev, "of_icc_get() failed on path %s (%d)\n",
paths[i].name, ret);
paths[i].path = NULL;
goto err;
}
}
return 0;
err:
icc_bulk_put(i, paths);
return ret;
}
EXPORT_SYMBOL_GPL(of_icc_bulk_get);
/**
* icc_bulk_put() - put a list of interconnect paths
* @num_paths: the number of icc_bulk_data
* @paths: the icc_bulk_data table with the paths being put
*/
void icc_bulk_put(int num_paths, struct icc_bulk_data *paths)
{
while (--num_paths >= 0) {
icc_put(paths[num_paths].path);
paths[num_paths].path = NULL;
}
}
EXPORT_SYMBOL_GPL(icc_bulk_put);
/**
* icc_bulk_set_bw() - set bandwidth to a set of paths
* @num_paths: the number of icc_bulk_data
* @paths: the icc_bulk_data table containing the paths and bandwidth
*
* Returns 0 on success or negative errno otherwise.
*/
int icc_bulk_set_bw(int num_paths, const struct icc_bulk_data *paths)
{
int ret = 0;
int i;
for (i = 0; i < num_paths; i++) {
ret = icc_set_bw(paths[i].path, paths[i].avg_bw, paths[i].peak_bw);
if (ret) {
pr_err("icc_set_bw() failed on path %s (%d)\n", paths[i].name, ret);
return ret;
}
}
return ret;
}
EXPORT_SYMBOL_GPL(icc_bulk_set_bw);
/**
* icc_bulk_enable() - enable a previously disabled set of paths
* @num_paths: the number of icc_bulk_data
* @paths: the icc_bulk_data table containing the paths and bandwidth
*
* Returns 0 on success or negative errno otherwise.
*/
int icc_bulk_enable(int num_paths, const struct icc_bulk_data *paths)
{
int ret, i;
for (i = 0; i < num_paths; i++) {
ret = icc_enable(paths[i].path);
if (ret) {
pr_err("icc_enable() failed on path %s (%d)\n", paths[i].name, ret);
goto err;
}
}
return 0;
err:
icc_bulk_disable(i, paths);
return ret;
}
EXPORT_SYMBOL_GPL(icc_bulk_enable);
/**
* icc_bulk_disable() - disable a set of interconnect paths
* @num_paths: the number of icc_bulk_data
* @paths: the icc_bulk_data table containing the paths and bandwidth
*/
void icc_bulk_disable(int num_paths, const struct icc_bulk_data *paths)
{
while (--num_paths >= 0)
icc_disable(paths[num_paths].path);
}
EXPORT_SYMBOL_GPL(icc_bulk_disable);
struct icc_bulk_devres {
struct icc_bulk_data *paths;
int num_paths;
};
static void devm_icc_bulk_release(struct device *dev, void *res)
{
struct icc_bulk_devres *devres = res;
icc_bulk_put(devres->num_paths, devres->paths);
}
/**
* devm_of_icc_bulk_get() - resource managed of_icc_bulk_get
* @dev: the device requesting the path
* @num_paths: the number of icc_bulk_data
* @paths: the table with the paths we want to get
*
* Returns 0 on success or negative errno otherwise.
*/
int devm_of_icc_bulk_get(struct device *dev, int num_paths, struct icc_bulk_data *paths)
{
struct icc_bulk_devres *devres;
int ret;
devres = devres_alloc(devm_icc_bulk_release, sizeof(*devres), GFP_KERNEL);
if (!devres)
return -ENOMEM;
ret = of_icc_bulk_get(dev, num_paths, paths);
if (!ret) {
devres->paths = paths;
devres->num_paths = num_paths;
devres_add(dev, devres);
} else {
devres_free(devres);
}
return ret;
}
EXPORT_SYMBOL_GPL(devm_of_icc_bulk_get);
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,181 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/debugfs.h>
#include <linux/interconnect.h>
#include <linux/platform_device.h>
#include "internal.h"
/*
* This can be dangerous, therefore don't provide any real compile time
* configuration option for this feature.
* People who want to use this will need to modify the source code directly.
*/
#undef INTERCONNECT_ALLOW_WRITE_DEBUGFS
#if defined(INTERCONNECT_ALLOW_WRITE_DEBUGFS) && defined(CONFIG_DEBUG_FS)
static LIST_HEAD(debugfs_paths);
static DEFINE_MUTEX(debugfs_lock);
static struct platform_device *pdev;
static struct icc_path *cur_path;
static char *src_node;
static char *dst_node;
static u32 avg_bw;
static u32 peak_bw;
static u32 tag;
struct debugfs_path {
const char *src;
const char *dst;
struct icc_path *path;
struct list_head list;
};
static struct icc_path *get_path(const char *src, const char *dst)
{
struct debugfs_path *path;
list_for_each_entry(path, &debugfs_paths, list) {
if (!strcmp(path->src, src) && !strcmp(path->dst, dst))
return path->path;
}
return NULL;
}
static int icc_get_set(void *data, u64 val)
{
struct debugfs_path *debugfs_path;
char *src, *dst;
int ret = 0;
mutex_lock(&debugfs_lock);
rcu_read_lock();
src = rcu_dereference(src_node);
dst = rcu_dereference(dst_node);
/*
* If we've already looked up a path, then use the existing one instead
* of calling icc_get() again. This allows for updating previous BW
* votes when "get" is written to multiple times for multiple paths.
*/
cur_path = get_path(src, dst);
if (cur_path) {
rcu_read_unlock();
goto out;
}
src = kstrdup(src, GFP_ATOMIC);
dst = kstrdup(dst, GFP_ATOMIC);
rcu_read_unlock();
if (!src || !dst) {
ret = -ENOMEM;
goto err_free;
}
cur_path = icc_get(&pdev->dev, src, dst);
if (IS_ERR(cur_path)) {
ret = PTR_ERR(cur_path);
goto err_free;
}
debugfs_path = kzalloc_obj(*debugfs_path);
if (!debugfs_path) {
ret = -ENOMEM;
goto err_put;
}
debugfs_path->path = cur_path;
debugfs_path->src = src;
debugfs_path->dst = dst;
list_add_tail(&debugfs_path->list, &debugfs_paths);
goto out;
err_put:
icc_put(cur_path);
err_free:
kfree(src);
kfree(dst);
out:
mutex_unlock(&debugfs_lock);
return ret;
}
DEFINE_DEBUGFS_ATTRIBUTE(icc_get_fops, NULL, icc_get_set, "%llu\n");
static int icc_commit_set(void *data, u64 val)
{
int ret;
mutex_lock(&debugfs_lock);
if (!cur_path) {
ret = -EINVAL;
goto out;
}
if (IS_ERR(cur_path)) {
ret = PTR_ERR(cur_path);
goto out;
}
icc_set_tag(cur_path, tag);
ret = icc_set_bw(cur_path, avg_bw, peak_bw);
out:
mutex_unlock(&debugfs_lock);
return ret;
}
DEFINE_DEBUGFS_ATTRIBUTE(icc_commit_fops, NULL, icc_commit_set, "%llu\n");
int icc_debugfs_client_init(struct dentry *icc_dir)
{
struct dentry *client_dir;
int ret;
pdev = platform_device_alloc("icc-debugfs-client", PLATFORM_DEVID_NONE);
ret = platform_device_add(pdev);
if (ret) {
pr_err("%s: failed to add platform device: %d\n", __func__, ret);
platform_device_put(pdev);
return ret;
}
src_node = kstrdup("", GFP_KERNEL);
dst_node = kstrdup("", GFP_KERNEL);
if (!src_node || !dst_node) {
kfree(dst_node);
kfree(src_node);
return -ENOMEM;
}
client_dir = debugfs_create_dir("test_client", icc_dir);
debugfs_create_str("src_node", 0600, client_dir, &src_node);
debugfs_create_str("dst_node", 0600, client_dir, &dst_node);
debugfs_create_file("get", 0200, client_dir, NULL, &icc_get_fops);
debugfs_create_u32("avg_bw", 0600, client_dir, &avg_bw);
debugfs_create_u32("peak_bw", 0600, client_dir, &peak_bw);
debugfs_create_u32("tag", 0600, client_dir, &tag);
debugfs_create_file("commit", 0200, client_dir, NULL, &icc_commit_fops);
return 0;
}
#else
int icc_debugfs_client_init(struct dentry *icc_dir)
{
return 0;
}
#endif
+203
View File
@@ -0,0 +1,203 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2023, Linaro Ltd.
*/
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/interconnect-clk.h>
#include <linux/interconnect-provider.h>
struct icc_clk_node {
struct clk *clk;
bool enabled;
};
struct icc_clk_provider {
struct icc_provider provider;
int num_clocks;
struct icc_clk_node clocks[] __counted_by(num_clocks);
};
#define to_icc_clk_provider(_provider) \
container_of(_provider, struct icc_clk_provider, provider)
static int icc_clk_set(struct icc_node *src, struct icc_node *dst)
{
struct icc_clk_node *qn = src->data;
int ret;
if (!qn || !qn->clk)
return 0;
if (!src->peak_bw) {
if (qn->enabled)
clk_disable_unprepare(qn->clk);
qn->enabled = false;
return 0;
}
if (!qn->enabled) {
ret = clk_prepare_enable(qn->clk);
if (ret)
return ret;
qn->enabled = true;
}
return clk_set_rate(qn->clk, icc_units_to_bps(src->peak_bw));
}
static int icc_clk_get_bw(struct icc_node *node, u32 *avg, u32 *peak)
{
struct icc_clk_node *qn = node->data;
if (!qn || !qn->clk)
*peak = INT_MAX;
else
*peak = Bps_to_icc(clk_get_rate(qn->clk));
return 0;
}
/**
* icc_clk_register() - register a new clk-based interconnect provider
* @dev: device supporting this provider
* @first_id: an ID of the first provider's node
* @num_clocks: number of instances of struct icc_clk_data
* @data: data for the provider
*
* Registers and returns a clk-based interconnect provider. It is a simple
* wrapper around COMMON_CLK framework, allowing other devices to vote on the
* clock rate.
*
* Return: 0 on success, or an error code otherwise
*/
struct icc_provider *icc_clk_register(struct device *dev,
unsigned int first_id,
unsigned int num_clocks,
const struct icc_clk_data *data)
{
struct icc_clk_provider *qp;
struct icc_provider *provider;
struct icc_onecell_data *onecell;
struct icc_node *node;
int ret, i, j;
onecell = devm_kzalloc(dev, struct_size(onecell, nodes, 2 * num_clocks), GFP_KERNEL);
if (!onecell)
return ERR_PTR(-ENOMEM);
onecell->num_nodes = 2 * num_clocks;
qp = devm_kzalloc(dev, struct_size(qp, clocks, num_clocks), GFP_KERNEL);
if (!qp)
return ERR_PTR(-ENOMEM);
qp->num_clocks = num_clocks;
provider = &qp->provider;
provider->dev = dev;
provider->get_bw = icc_clk_get_bw;
provider->set = icc_clk_set;
provider->aggregate = icc_std_aggregate;
provider->xlate = of_icc_xlate_onecell;
INIT_LIST_HEAD(&provider->nodes);
provider->data = onecell;
icc_provider_init(provider);
for (i = 0, j = 0; i < num_clocks; i++) {
qp->clocks[i].clk = data[i].clk;
node = icc_node_create(first_id + data[i].master_id);
if (IS_ERR(node)) {
ret = PTR_ERR(node);
goto err;
}
node->name = devm_kasprintf(dev, GFP_KERNEL, "%s_master", data[i].name);
if (!node->name) {
icc_node_destroy(node->id);
ret = -ENOMEM;
goto err;
}
node->data = &qp->clocks[i];
icc_node_add(node, provider);
/* link to the next node, slave */
icc_link_create(node, first_id + data[i].slave_id);
onecell->nodes[j++] = node;
node = icc_node_create(first_id + data[i].slave_id);
if (IS_ERR(node)) {
ret = PTR_ERR(node);
goto err;
}
node->name = devm_kasprintf(dev, GFP_KERNEL, "%s_slave", data[i].name);
if (!node->name) {
icc_node_destroy(node->id);
ret = -ENOMEM;
goto err;
}
/* no data for slave node */
icc_node_add(node, provider);
onecell->nodes[j++] = node;
}
ret = icc_provider_register(provider);
if (ret)
goto err;
return provider;
err:
icc_nodes_remove(provider);
return ERR_PTR(ret);
}
EXPORT_SYMBOL_GPL(icc_clk_register);
static void devm_icc_release(void *res)
{
icc_clk_unregister(res);
}
int devm_icc_clk_register(struct device *dev, unsigned int first_id,
unsigned int num_clocks, const struct icc_clk_data *data)
{
struct icc_provider *prov;
prov = icc_clk_register(dev, first_id, num_clocks, data);
if (IS_ERR(prov))
return PTR_ERR(prov);
return devm_add_action_or_reset(dev, devm_icc_release, prov);
}
EXPORT_SYMBOL_GPL(devm_icc_clk_register);
/**
* icc_clk_unregister() - unregister a previously registered clk interconnect provider
* @provider: provider returned by icc_clk_register()
*/
void icc_clk_unregister(struct icc_provider *provider)
{
struct icc_clk_provider *qp = container_of(provider, struct icc_clk_provider, provider);
int i;
icc_provider_deregister(&qp->provider);
icc_nodes_remove(&qp->provider);
for (i = 0; i < qp->num_clocks; i++) {
struct icc_clk_node *qn = &qp->clocks[i];
if (qn->enabled)
clk_disable_unprepare(qn->clk);
}
}
EXPORT_SYMBOL_GPL(icc_clk_unregister);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Interconnect wrapper for clocks");
MODULE_AUTHOR("Dmitry Baryshkov <dmitry.baryshkov@linaro.org>");
@@ -0,0 +1,324 @@
// SPDX-License-Identifier: GPL-2.0
/*
* KUnit tests for the Interconnect framework.
*
* Copyright (c) 2025 Kuan-Wei Chiu <visitorckw@gmail.com>
*
* This suite verifies the behavior of the interconnect core, including
* topology construction, bandwidth aggregation, and path lifecycle.
*/
#include <kunit/platform_device.h>
#include <kunit/test.h>
#include <linux/interconnect-provider.h>
#include <linux/interconnect.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/overflow.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include "internal.h"
enum {
NODE_CPU,
NODE_GPU,
NODE_BUS,
NODE_DDR,
NODE_MAX
};
struct test_node_data {
int id;
const char *name;
int num_links;
int links[2];
};
/*
* Static Topology:
* CPU -\
* -> BUS -> DDR
* GPU -/
*/
static const struct test_node_data test_topology[] = {
{ NODE_CPU, "cpu", 1, { NODE_BUS } },
{ NODE_GPU, "gpu", 1, { NODE_BUS } },
{ NODE_BUS, "bus", 1, { NODE_DDR } },
{ NODE_DDR, "ddr", 0, { } },
};
struct icc_test_priv {
struct icc_provider provider;
struct platform_device *pdev;
struct icc_node *nodes[NODE_MAX];
};
static struct icc_node *get_node(struct icc_test_priv *priv, int id)
{
int idx = id - NODE_CPU;
if (idx < 0 || idx >= ARRAY_SIZE(test_topology))
return NULL;
return priv->nodes[idx];
}
static int icc_test_set(struct icc_node *src, struct icc_node *dst)
{
return 0;
}
static int icc_test_aggregate(struct icc_node *node, u32 tag, u32 avg_bw,
u32 peak_bw, u32 *agg_avg, u32 *agg_peak)
{
return icc_std_aggregate(node, tag, avg_bw, peak_bw, agg_avg, agg_peak);
}
static struct icc_node *icc_test_xlate(const struct of_phandle_args *spec, void *data)
{
return NULL;
}
static int icc_test_get_bw(struct icc_node *node, u32 *avg, u32 *peak)
{
*avg = 0;
*peak = 0;
return 0;
}
static int icc_test_init(struct kunit *test)
{
struct icc_test_priv *priv;
struct icc_node *node;
int i, j, ret;
priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv);
test->priv = priv;
priv->pdev = kunit_platform_device_alloc(test, "icc-test-dev", -1);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->pdev);
KUNIT_ASSERT_EQ(test, kunit_platform_device_add(test, priv->pdev), 0);
priv->provider.set = icc_test_set;
priv->provider.aggregate = icc_test_aggregate;
priv->provider.xlate = icc_test_xlate;
priv->provider.get_bw = icc_test_get_bw;
priv->provider.dev = &priv->pdev->dev;
priv->provider.data = priv;
INIT_LIST_HEAD(&priv->provider.nodes);
ret = icc_provider_register(&priv->provider);
KUNIT_ASSERT_EQ(test, ret, 0);
for (i = 0; i < ARRAY_SIZE(test_topology); i++) {
const struct test_node_data *data = &test_topology[i];
node = icc_node_create(data->id);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, node);
node->name = data->name;
icc_node_add(node, &priv->provider);
priv->nodes[i] = node;
}
for (i = 0; i < ARRAY_SIZE(test_topology); i++) {
const struct test_node_data *data = &test_topology[i];
struct icc_node *src = get_node(priv, data->id);
for (j = 0; j < data->num_links; j++) {
ret = icc_link_create(src, data->links[j]);
KUNIT_ASSERT_EQ_MSG(test, ret, 0, "Failed to link %s->%d",
src->name, data->links[j]);
}
}
icc_sync_state(&priv->pdev->dev);
return 0;
}
static void icc_test_exit(struct kunit *test)
{
struct icc_test_priv *priv = test->priv;
icc_nodes_remove(&priv->provider);
icc_provider_deregister(&priv->provider);
}
/*
* Helper to construct a mock path.
*
* Because we are bypassing icc_get(), we must manually link the requests
* to the nodes' req_list so that icc_std_aggregate() can discover them.
*/
static struct icc_path *icc_test_create_path(struct kunit *test,
struct icc_node **nodes, int num)
{
struct icc_path *path;
int i;
path = kunit_kzalloc(test, struct_size(path, reqs, num), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, path);
path->num_nodes = num;
for (i = 0; i < num; i++) {
path->reqs[i].node = nodes[i];
hlist_add_head(&path->reqs[i].req_node, &nodes[i]->req_list);
}
path->name = "mock-path";
return path;
}
static void icc_test_destroy_path(struct kunit *test, struct icc_path *path)
{
int i;
for (i = 0; i < path->num_nodes; i++)
hlist_del(&path->reqs[i].req_node);
kunit_kfree(test, path);
}
static void icc_test_topology_integrity(struct kunit *test)
{
struct icc_test_priv *priv = test->priv;
struct icc_node *cpu = get_node(priv, NODE_CPU);
struct icc_node *bus = get_node(priv, NODE_BUS);
KUNIT_EXPECT_EQ(test, cpu->num_links, 1);
KUNIT_EXPECT_PTR_EQ(test, cpu->links[0], bus);
KUNIT_EXPECT_PTR_EQ(test, cpu->provider, &priv->provider);
}
static void icc_test_set_bw(struct kunit *test)
{
struct icc_test_priv *priv = test->priv;
struct icc_path *path;
struct icc_node *path_nodes[3];
int ret;
/* Path: CPU -> BUS -> DDR */
path_nodes[0] = get_node(priv, NODE_CPU);
path_nodes[1] = get_node(priv, NODE_BUS);
path_nodes[2] = get_node(priv, NODE_DDR);
path = icc_test_create_path(test, path_nodes, 3);
ret = icc_enable(path);
KUNIT_ASSERT_EQ(test, ret, 0);
ret = icc_set_bw(path, 1000, 2000);
KUNIT_EXPECT_EQ(test, ret, 0);
KUNIT_EXPECT_EQ(test, path_nodes[0]->avg_bw, 1000);
KUNIT_EXPECT_EQ(test, path_nodes[0]->peak_bw, 2000);
KUNIT_EXPECT_EQ(test, path_nodes[1]->avg_bw, 1000);
KUNIT_EXPECT_EQ(test, path_nodes[1]->peak_bw, 2000);
icc_set_tag(path, 0xABC);
KUNIT_EXPECT_EQ(test, path->reqs[0].tag, 0xABC);
icc_disable(path);
KUNIT_EXPECT_EQ(test, path_nodes[0]->avg_bw, 0);
icc_test_destroy_path(test, path);
}
static void icc_test_aggregation(struct kunit *test)
{
struct icc_test_priv *priv = test->priv;
struct icc_path *path_cpu, *path_gpu;
struct icc_node *nodes_cpu[3], *nodes_gpu[2];
struct icc_node *bus = get_node(priv, NODE_BUS);
int ret;
nodes_cpu[0] = get_node(priv, NODE_CPU);
nodes_cpu[1] = bus;
nodes_cpu[2] = get_node(priv, NODE_DDR);
path_cpu = icc_test_create_path(test, nodes_cpu, 3);
nodes_gpu[0] = get_node(priv, NODE_GPU);
nodes_gpu[1] = bus;
path_gpu = icc_test_create_path(test, nodes_gpu, 2);
icc_enable(path_cpu);
icc_enable(path_gpu);
ret = icc_set_bw(path_cpu, 1000, 1000);
KUNIT_EXPECT_EQ(test, ret, 0);
KUNIT_EXPECT_EQ(test, bus->avg_bw, 1000);
ret = icc_set_bw(path_gpu, 2000, 2000);
KUNIT_EXPECT_EQ(test, ret, 0);
/* Bus aggregates: CPU(1000) + GPU(2000) */
KUNIT_EXPECT_EQ(test, bus->avg_bw, 3000);
/* Peak aggregates: max(CPU, GPU) */
KUNIT_EXPECT_EQ(test, bus->peak_bw, 2000);
icc_test_destroy_path(test, path_cpu);
icc_test_destroy_path(test, path_gpu);
}
static void icc_test_bulk_ops(struct kunit *test)
{
struct icc_test_priv *priv = test->priv;
struct icc_node *nodes_cpu[3], *nodes_gpu[2];
struct icc_bulk_data bulk[2];
int ret;
nodes_cpu[0] = get_node(priv, NODE_CPU);
nodes_cpu[1] = get_node(priv, NODE_BUS);
nodes_cpu[2] = get_node(priv, NODE_DDR);
nodes_gpu[0] = get_node(priv, NODE_GPU);
nodes_gpu[1] = get_node(priv, NODE_BUS);
bulk[0].path = icc_test_create_path(test, nodes_cpu, 3);
bulk[0].avg_bw = 500;
bulk[0].peak_bw = 500;
bulk[1].path = icc_test_create_path(test, nodes_gpu, 2);
bulk[1].avg_bw = 600;
bulk[1].peak_bw = 600;
ret = icc_bulk_set_bw(2, bulk);
KUNIT_EXPECT_EQ(test, ret, 0);
/* Paths disabled, bandwidth should be 0 */
KUNIT_EXPECT_EQ(test, get_node(priv, NODE_BUS)->avg_bw, 0);
ret = icc_bulk_enable(2, bulk);
KUNIT_EXPECT_EQ(test, ret, 0);
/* Paths enabled, aggregation applies */
KUNIT_EXPECT_EQ(test, get_node(priv, NODE_BUS)->avg_bw, 1100);
icc_bulk_disable(2, bulk);
KUNIT_EXPECT_EQ(test, get_node(priv, NODE_BUS)->avg_bw, 0);
icc_test_destroy_path(test, bulk[0].path);
icc_test_destroy_path(test, bulk[1].path);
}
static struct kunit_case icc_test_cases[] = {
KUNIT_CASE(icc_test_topology_integrity),
KUNIT_CASE(icc_test_set_bw),
KUNIT_CASE(icc_test_aggregation),
KUNIT_CASE(icc_test_bulk_ops),
{}
};
static struct kunit_suite icc_test_suite = {
.name = "interconnect",
.init = icc_test_init,
.exit = icc_test_exit,
.test_cases = icc_test_cases,
};
kunit_test_suite(icc_test_suite);
MODULE_AUTHOR("Kuan-Wei Chiu <visitorckw@gmail.com>");
MODULE_DESCRIPTION("KUnit tests for the Interconnect framework");
MODULE_LICENSE("GPL");
@@ -0,0 +1,21 @@
config INTERCONNECT_IMX
tristate "i.MX interconnect drivers"
depends on ARCH_MXC || COMPILE_TEST
help
Generic interconnect drivers for i.MX SOCs
config INTERCONNECT_IMX8MM
tristate "i.MX8MM interconnect driver"
depends on INTERCONNECT_IMX
config INTERCONNECT_IMX8MN
tristate "i.MX8MN interconnect driver"
depends on INTERCONNECT_IMX
config INTERCONNECT_IMX8MQ
tristate "i.MX8MQ interconnect driver"
depends on INTERCONNECT_IMX
config INTERCONNECT_IMX8MP
tristate "i.MX8MP interconnect driver"
depends on INTERCONNECT_IMX
@@ -0,0 +1,11 @@
imx-interconnect-objs := imx.o
imx8mm-interconnect-objs := imx8mm.o
imx8mq-interconnect-objs := imx8mq.o
imx8mn-interconnect-objs := imx8mn.o
imx8mp-interconnect-objs := imx8mp.o
obj-$(CONFIG_INTERCONNECT_IMX) += imx-interconnect.o
obj-$(CONFIG_INTERCONNECT_IMX8MM) += imx8mm-interconnect.o
obj-$(CONFIG_INTERCONNECT_IMX8MQ) += imx8mq-interconnect.o
obj-$(CONFIG_INTERCONNECT_IMX8MN) += imx8mn-interconnect.o
obj-$(CONFIG_INTERCONNECT_IMX8MP) += imx8mp-interconnect.o
+338
View File
@@ -0,0 +1,338 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Interconnect framework driver for i.MX SoC
*
* Copyright (c) 2019, BayLibre
* Copyright (c) 2019-2020, NXP
* Author: Alexandre Bailon <abailon@baylibre.com>
* Author: Leonard Crestez <leonard.crestez@nxp.com>
*/
#include <linux/device.h>
#include <linux/interconnect-provider.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/pm_qos.h>
#include "imx.h"
/* private icc_node data */
struct imx_icc_node {
const struct imx_icc_node_desc *desc;
const struct imx_icc_noc_setting *setting;
struct device *qos_dev;
struct dev_pm_qos_request qos_req;
struct imx_icc_provider *imx_provider;
};
static int imx_icc_get_bw(struct icc_node *node, u32 *avg, u32 *peak)
{
*avg = 0;
*peak = 0;
return 0;
}
static int imx_icc_node_set(struct icc_node *node)
{
struct device *dev = node->provider->dev;
struct imx_icc_node *node_data = node->data;
void __iomem *base;
u32 prio;
u64 freq;
if (node_data->setting && node->peak_bw) {
base = node_data->setting->reg + node_data->imx_provider->noc_base;
if (node_data->setting->mode == IMX_NOC_MODE_FIXED) {
prio = node_data->setting->prio_level;
prio = PRIORITY_COMP_MARK | (prio << 8) | prio;
writel(prio, base + IMX_NOC_PRIO_REG);
writel(node_data->setting->mode, base + IMX_NOC_MODE_REG);
writel(node_data->setting->ext_control, base + IMX_NOC_EXT_CTL_REG);
dev_dbg(dev, "%s: mode: 0x%x, prio: 0x%x, ext_control: 0x%x\n",
node_data->desc->name, node_data->setting->mode, prio,
node_data->setting->ext_control);
} else if (node_data->setting->mode == IMX_NOC_MODE_UNCONFIGURED) {
dev_dbg(dev, "%s: mode not unconfigured\n", node_data->desc->name);
} else {
dev_info(dev, "%s: mode: %d not supported\n",
node_data->desc->name, node_data->setting->mode);
return -EOPNOTSUPP;
}
}
if (!node_data->qos_dev)
return 0;
freq = (node->avg_bw + node->peak_bw) * node_data->desc->adj->bw_mul;
do_div(freq, node_data->desc->adj->bw_div);
dev_dbg(dev, "node %s device %s avg_bw %ukBps peak_bw %ukBps min_freq %llukHz\n",
node->name, dev_name(node_data->qos_dev),
node->avg_bw, node->peak_bw, freq);
if (freq > S32_MAX) {
dev_err(dev, "%s can't request more than S32_MAX freq\n",
node->name);
return -ERANGE;
}
dev_pm_qos_update_request(&node_data->qos_req, freq);
return 0;
}
static int imx_icc_set(struct icc_node *src, struct icc_node *dst)
{
int ret;
ret = imx_icc_node_set(src);
if (ret)
return ret;
return imx_icc_node_set(dst);
}
/* imx_icc_node_destroy() - Destroy an imx icc_node, including private data */
static void imx_icc_node_destroy(struct icc_node *node)
{
struct imx_icc_node *node_data = node->data;
int ret;
if (dev_pm_qos_request_active(&node_data->qos_req)) {
ret = dev_pm_qos_remove_request(&node_data->qos_req);
if (ret)
dev_warn(node->provider->dev,
"failed to remove qos request for %s\n",
dev_name(node_data->qos_dev));
}
put_device(node_data->qos_dev);
icc_node_del(node);
icc_node_destroy(node->id);
}
static int imx_icc_node_init_qos(struct icc_provider *provider,
struct icc_node *node)
{
struct imx_icc_node *node_data = node->data;
const struct imx_icc_node_adj_desc *adj = node_data->desc->adj;
struct device *dev = provider->dev;
struct device_node *dn = NULL;
struct platform_device *pdev;
if (adj->main_noc) {
node_data->qos_dev = dev;
dev_dbg(dev, "icc node %s[%d] is main noc itself\n",
node->name, node->id);
} else {
dn = of_parse_phandle(dev->of_node, adj->phandle_name, 0);
if (!dn) {
dev_warn(dev, "Failed to parse %s\n",
adj->phandle_name);
return -ENODEV;
}
/* Allow scaling to be disabled on a per-node basis */
if (!of_device_is_available(dn)) {
dev_warn(dev, "Missing property %s, skip scaling %s\n",
adj->phandle_name, node->name);
of_node_put(dn);
return 0;
}
pdev = of_find_device_by_node(dn);
of_node_put(dn);
if (!pdev) {
dev_warn(dev, "node %s[%d] missing device for %pOF\n",
node->name, node->id, dn);
return -EPROBE_DEFER;
}
node_data->qos_dev = &pdev->dev;
dev_dbg(dev, "node %s[%d] has device node %pOF\n",
node->name, node->id, dn);
}
return dev_pm_qos_add_request(node_data->qos_dev,
&node_data->qos_req,
DEV_PM_QOS_MIN_FREQUENCY, 0);
}
static struct icc_node *imx_icc_node_add(struct imx_icc_provider *imx_provider,
const struct imx_icc_node_desc *node_desc,
const struct imx_icc_noc_setting *setting)
{
struct icc_provider *provider = &imx_provider->provider;
struct device *dev = provider->dev;
struct imx_icc_node *node_data;
struct icc_node *node;
int ret;
node = icc_node_create(node_desc->id);
if (IS_ERR(node)) {
dev_err(dev, "failed to create node %d\n", node_desc->id);
return node;
}
if (node->data) {
dev_err(dev, "already created node %s id=%d\n",
node_desc->name, node_desc->id);
return ERR_PTR(-EEXIST);
}
node_data = devm_kzalloc(dev, sizeof(*node_data), GFP_KERNEL);
if (!node_data) {
icc_node_destroy(node->id);
return ERR_PTR(-ENOMEM);
}
node->name = node_desc->name;
node->data = node_data;
node_data->desc = node_desc;
node_data->setting = setting;
node_data->imx_provider = imx_provider;
icc_node_add(node, provider);
if (node_desc->adj) {
ret = imx_icc_node_init_qos(provider, node);
if (ret < 0) {
imx_icc_node_destroy(node);
return ERR_PTR(ret);
}
}
return node;
}
static void imx_icc_unregister_nodes(struct icc_provider *provider)
{
struct icc_node *node, *tmp;
list_for_each_entry_safe(node, tmp, &provider->nodes, node_list)
imx_icc_node_destroy(node);
}
static int imx_icc_register_nodes(struct imx_icc_provider *imx_provider,
const struct imx_icc_node_desc *descs,
int count,
const struct imx_icc_noc_setting *settings)
{
struct icc_provider *provider = &imx_provider->provider;
struct icc_onecell_data *provider_data = provider->data;
int ret;
int i;
for (i = 0; i < count; i++) {
struct icc_node *node;
const struct imx_icc_node_desc *node_desc = &descs[i];
size_t j;
node = imx_icc_node_add(imx_provider, node_desc,
settings ? &settings[node_desc->id] : NULL);
if (IS_ERR(node)) {
ret = dev_err_probe(provider->dev, PTR_ERR(node),
"failed to add %s\n", node_desc->name);
goto err;
}
provider_data->nodes[node->id] = node;
for (j = 0; j < node_desc->num_links; j++) {
ret = icc_link_create(node, node_desc->links[j]);
if (ret) {
dev_err(provider->dev, "failed to link node %d to %d: %d\n",
node->id, node_desc->links[j], ret);
goto err;
}
}
}
return 0;
err:
imx_icc_unregister_nodes(provider);
return ret;
}
static int get_max_node_id(struct imx_icc_node_desc *nodes, int nodes_count)
{
int i, ret = 0;
for (i = 0; i < nodes_count; ++i)
if (nodes[i].id > ret)
ret = nodes[i].id;
return ret;
}
int imx_icc_register(struct platform_device *pdev,
struct imx_icc_node_desc *nodes, int nodes_count,
struct imx_icc_noc_setting *settings)
{
struct device *dev = &pdev->dev;
struct icc_onecell_data *data;
struct imx_icc_provider *imx_provider;
struct icc_provider *provider;
int num_nodes;
int ret;
/* icc_onecell_data is indexed by node_id, unlike nodes param */
num_nodes = get_max_node_id(nodes, nodes_count) + 1;
data = devm_kzalloc(dev, struct_size(data, nodes, num_nodes),
GFP_KERNEL);
if (!data)
return -ENOMEM;
data->num_nodes = num_nodes;
imx_provider = devm_kzalloc(dev, sizeof(*imx_provider), GFP_KERNEL);
if (!imx_provider)
return -ENOMEM;
provider = &imx_provider->provider;
provider->set = imx_icc_set;
provider->get_bw = imx_icc_get_bw;
provider->aggregate = icc_std_aggregate;
provider->xlate = of_icc_xlate_onecell;
provider->data = data;
provider->dev = dev->parent;
icc_provider_init(provider);
platform_set_drvdata(pdev, imx_provider);
if (settings) {
imx_provider->noc_base = devm_of_iomap(dev, provider->dev->of_node, 0, NULL);
if (IS_ERR(imx_provider->noc_base)) {
ret = PTR_ERR(imx_provider->noc_base);
dev_err(dev, "Error mapping NoC: %d\n", ret);
return ret;
}
}
ret = imx_icc_register_nodes(imx_provider, nodes, nodes_count, settings);
if (ret)
return ret;
ret = icc_provider_register(provider);
if (ret)
goto err_unregister_nodes;
return 0;
err_unregister_nodes:
imx_icc_unregister_nodes(&imx_provider->provider);
return ret;
}
EXPORT_SYMBOL_GPL(imx_icc_register);
void imx_icc_unregister(struct platform_device *pdev)
{
struct imx_icc_provider *imx_provider = platform_get_drvdata(pdev);
icc_provider_deregister(&imx_provider->provider);
imx_icc_unregister_nodes(&imx_provider->provider);
}
EXPORT_SYMBOL_GPL(imx_icc_unregister);
MODULE_DESCRIPTION("Interconnect framework driver for i.MX SoC");
MODULE_LICENSE("GPL v2");
+113
View File
@@ -0,0 +1,113 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Interconnect framework driver for i.MX SoC
*
* Copyright (c) 2019, BayLibre
* Copyright (c) 2019-2020, NXP
* Author: Alexandre Bailon <abailon@baylibre.com>
* Author: Leonard Crestez <leonard.crestez@nxp.com>
*/
#ifndef __DRIVERS_INTERCONNECT_IMX_H
#define __DRIVERS_INTERCONNECT_IMX_H
#include <linux/args.h>
#include <linux/bits.h>
#include <linux/types.h>
#include <linux/interconnect-provider.h>
struct platform_device;
#define IMX_ICC_MAX_LINKS 4
/*
* High throughput priority level in Regulator mode
* Read Priority in Fixed/Limiter mode
*/
#define PRIORITY0_SHIFT 0
/*
* Low throughput priority level in Regulator mode
* Write Priority in Fixed/Limiter mode
*/
#define PRIORITY1_SHIFT 8
#define PRIORITY_MASK 0x7
#define PRIORITY_COMP_MARK BIT(31) /* Must set */
#define IMX_NOC_MODE_FIXED 0
#define IMX_NOC_MODE_LIMITER 1
#define IMX_NOC_MODE_BYPASS 2
#define IMX_NOC_MODE_REGULATOR 3
#define IMX_NOC_MODE_UNCONFIGURED 0xFF
#define IMX_NOC_PRIO_REG 0x8
#define IMX_NOC_MODE_REG 0xC
#define IMX_NOC_BANDWIDTH_REG 0x10
#define IMX_NOC_SATURATION 0x14
#define IMX_NOC_EXT_CTL_REG 0x18
struct imx_icc_provider {
void __iomem *noc_base;
struct icc_provider provider;
};
/*
* struct imx_icc_node_adj - Describe a dynamic adjustable node
*/
struct imx_icc_node_adj_desc {
unsigned int bw_mul, bw_div;
const char *phandle_name;
bool main_noc;
};
/*
* struct imx_icc_node - Describe an interconnect node
* @name: name of the node
* @id: an unique id to identify the node
* @links: an array of slaves' node id
* @num_links: number of id defined in links
*/
struct imx_icc_node_desc {
const char *name;
u16 id;
u16 links[IMX_ICC_MAX_LINKS];
u16 num_links;
const struct imx_icc_node_adj_desc *adj;
};
/*
* struct imx_icc_noc_setting - Describe an interconnect node setting
* @reg: register offset inside the NoC
* @prio_level: priority level
* @mode: functional mode
* @ext_control: external input control
*/
struct imx_icc_noc_setting {
u32 reg;
u32 prio_level;
u32 mode;
u32 ext_control;
};
#define DEFINE_BUS_INTERCONNECT(_name, _id, _adj, ...) \
{ \
.id = _id, \
.name = _name, \
.adj = _adj, \
.num_links = COUNT_ARGS(__VA_ARGS__), \
.links = { __VA_ARGS__ }, \
}
#define DEFINE_BUS_MASTER(_name, _id, _dest_id) \
DEFINE_BUS_INTERCONNECT(_name, _id, NULL, _dest_id)
#define DEFINE_BUS_SLAVE(_name, _id, _adj) \
DEFINE_BUS_INTERCONNECT(_name, _id, _adj)
int imx_icc_register(struct platform_device *pdev,
struct imx_icc_node_desc *nodes,
int nodes_count,
struct imx_icc_noc_setting *noc_settings);
void imx_icc_unregister(struct platform_device *pdev);
#endif /* __DRIVERS_INTERCONNECT_IMX_H */
@@ -0,0 +1,101 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Interconnect framework driver for i.MX8MM SoC
*
* Copyright (c) 2019, BayLibre
* Copyright (c) 2019-2020, NXP
* Author: Alexandre Bailon <abailon@baylibre.com>
* Author: Leonard Crestez <leonard.crestez@nxp.com>
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <dt-bindings/interconnect/imx8mm.h>
#include "imx.h"
static const struct imx_icc_node_adj_desc imx8mm_dram_adj = {
.bw_mul = 1,
.bw_div = 16,
.phandle_name = "fsl,ddrc",
};
static const struct imx_icc_node_adj_desc imx8mm_noc_adj = {
.bw_mul = 1,
.bw_div = 16,
.main_noc = true,
};
/*
* Describe bus masters, slaves and connections between them
*
* This is a simplified subset of the bus diagram, there are several other
* PL301 nics which are skipped/merged into PL301_MAIN
*/
static struct imx_icc_node_desc nodes[] = {
DEFINE_BUS_INTERCONNECT("NOC", IMX8MM_ICN_NOC, &imx8mm_noc_adj,
IMX8MM_ICS_DRAM, IMX8MM_ICN_MAIN),
DEFINE_BUS_SLAVE("DRAM", IMX8MM_ICS_DRAM, &imx8mm_dram_adj),
DEFINE_BUS_SLAVE("OCRAM", IMX8MM_ICS_OCRAM, NULL),
DEFINE_BUS_MASTER("A53", IMX8MM_ICM_A53, IMX8MM_ICN_NOC),
/* VPUMIX */
DEFINE_BUS_MASTER("VPU H1", IMX8MM_ICM_VPU_H1, IMX8MM_ICN_VIDEO),
DEFINE_BUS_MASTER("VPU G1", IMX8MM_ICM_VPU_G1, IMX8MM_ICN_VIDEO),
DEFINE_BUS_MASTER("VPU G2", IMX8MM_ICM_VPU_G2, IMX8MM_ICN_VIDEO),
DEFINE_BUS_INTERCONNECT("PL301_VIDEO", IMX8MM_ICN_VIDEO, NULL, IMX8MM_ICN_NOC),
/* GPUMIX */
DEFINE_BUS_MASTER("GPU 2D", IMX8MM_ICM_GPU2D, IMX8MM_ICN_GPU),
DEFINE_BUS_MASTER("GPU 3D", IMX8MM_ICM_GPU3D, IMX8MM_ICN_GPU),
DEFINE_BUS_INTERCONNECT("PL301_GPU", IMX8MM_ICN_GPU, NULL, IMX8MM_ICN_NOC),
/* DISPLAYMIX */
DEFINE_BUS_MASTER("CSI", IMX8MM_ICM_CSI, IMX8MM_ICN_MIPI),
DEFINE_BUS_MASTER("LCDIF", IMX8MM_ICM_LCDIF, IMX8MM_ICN_MIPI),
DEFINE_BUS_INTERCONNECT("PL301_MIPI", IMX8MM_ICN_MIPI, NULL, IMX8MM_ICN_NOC),
/* HSIO */
DEFINE_BUS_MASTER("USB1", IMX8MM_ICM_USB1, IMX8MM_ICN_HSIO),
DEFINE_BUS_MASTER("USB2", IMX8MM_ICM_USB2, IMX8MM_ICN_HSIO),
DEFINE_BUS_MASTER("PCIE", IMX8MM_ICM_PCIE, IMX8MM_ICN_HSIO),
DEFINE_BUS_INTERCONNECT("PL301_HSIO", IMX8MM_ICN_HSIO, NULL, IMX8MM_ICN_NOC),
/* Audio */
DEFINE_BUS_MASTER("SDMA2", IMX8MM_ICM_SDMA2, IMX8MM_ICN_AUDIO),
DEFINE_BUS_MASTER("SDMA3", IMX8MM_ICM_SDMA3, IMX8MM_ICN_AUDIO),
DEFINE_BUS_INTERCONNECT("PL301_AUDIO", IMX8MM_ICN_AUDIO, NULL, IMX8MM_ICN_MAIN),
/* Ethernet */
DEFINE_BUS_MASTER("ENET", IMX8MM_ICM_ENET, IMX8MM_ICN_ENET),
DEFINE_BUS_INTERCONNECT("PL301_ENET", IMX8MM_ICN_ENET, NULL, IMX8MM_ICN_MAIN),
/* Other */
DEFINE_BUS_MASTER("SDMA1", IMX8MM_ICM_SDMA1, IMX8MM_ICN_MAIN),
DEFINE_BUS_MASTER("NAND", IMX8MM_ICM_NAND, IMX8MM_ICN_MAIN),
DEFINE_BUS_MASTER("USDHC1", IMX8MM_ICM_USDHC1, IMX8MM_ICN_MAIN),
DEFINE_BUS_MASTER("USDHC2", IMX8MM_ICM_USDHC2, IMX8MM_ICN_MAIN),
DEFINE_BUS_MASTER("USDHC3", IMX8MM_ICM_USDHC3, IMX8MM_ICN_MAIN),
DEFINE_BUS_INTERCONNECT("PL301_MAIN", IMX8MM_ICN_MAIN, NULL,
IMX8MM_ICN_NOC, IMX8MM_ICS_OCRAM),
};
static int imx8mm_icc_probe(struct platform_device *pdev)
{
return imx_icc_register(pdev, nodes, ARRAY_SIZE(nodes), NULL);
}
static struct platform_driver imx8mm_icc_driver = {
.probe = imx8mm_icc_probe,
.remove = imx_icc_unregister,
.driver = {
.name = "imx8mm-interconnect",
},
};
module_platform_driver(imx8mm_icc_driver);
MODULE_AUTHOR("Alexandre Bailon <abailon@baylibre.com>");
MODULE_DESCRIPTION("Interconnect framework driver for i.MX8MM SoC");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:imx8mm-interconnect");
@@ -0,0 +1,90 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Interconnect framework driver for i.MX8MN SoC
*
* Copyright (c) 2019-2020, NXP
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <dt-bindings/interconnect/imx8mn.h>
#include "imx.h"
static const struct imx_icc_node_adj_desc imx8mn_dram_adj = {
.bw_mul = 1,
.bw_div = 4,
.phandle_name = "fsl,ddrc",
};
static const struct imx_icc_node_adj_desc imx8mn_noc_adj = {
.bw_mul = 1,
.bw_div = 4,
.main_noc = true,
};
/*
* Describe bus masters, slaves and connections between them
*
* This is a simplified subset of the bus diagram, there are several other
* PL301 nics which are skipped/merged into PL301_MAIN
*/
static struct imx_icc_node_desc nodes[] = {
DEFINE_BUS_INTERCONNECT("NOC", IMX8MN_ICN_NOC, &imx8mn_noc_adj,
IMX8MN_ICS_DRAM, IMX8MN_ICN_MAIN),
DEFINE_BUS_SLAVE("DRAM", IMX8MN_ICS_DRAM, &imx8mn_dram_adj),
DEFINE_BUS_SLAVE("OCRAM", IMX8MN_ICS_OCRAM, NULL),
DEFINE_BUS_MASTER("A53", IMX8MN_ICM_A53, IMX8MN_ICN_NOC),
/* GPUMIX */
DEFINE_BUS_MASTER("GPU", IMX8MN_ICM_GPU, IMX8MN_ICN_GPU),
DEFINE_BUS_INTERCONNECT("PL301_GPU", IMX8MN_ICN_GPU, NULL, IMX8MN_ICN_NOC),
/* DISPLAYMIX */
DEFINE_BUS_MASTER("CSI1", IMX8MN_ICM_CSI1, IMX8MN_ICN_MIPI),
DEFINE_BUS_MASTER("CSI2", IMX8MN_ICM_CSI2, IMX8MN_ICN_MIPI),
DEFINE_BUS_MASTER("ISI", IMX8MN_ICM_ISI, IMX8MN_ICN_MIPI),
DEFINE_BUS_MASTER("LCDIF", IMX8MN_ICM_LCDIF, IMX8MN_ICN_MIPI),
DEFINE_BUS_INTERCONNECT("PL301_MIPI", IMX8MN_ICN_MIPI, NULL, IMX8MN_ICN_NOC),
/* USB goes straight to NOC */
DEFINE_BUS_MASTER("USB", IMX8MN_ICM_USB, IMX8MN_ICN_NOC),
/* Audio */
DEFINE_BUS_MASTER("SDMA2", IMX8MN_ICM_SDMA2, IMX8MN_ICN_AUDIO),
DEFINE_BUS_MASTER("SDMA3", IMX8MN_ICM_SDMA3, IMX8MN_ICN_AUDIO),
DEFINE_BUS_INTERCONNECT("PL301_AUDIO", IMX8MN_ICN_AUDIO, NULL, IMX8MN_ICN_MAIN),
/* Ethernet */
DEFINE_BUS_MASTER("ENET", IMX8MN_ICM_ENET, IMX8MN_ICN_ENET),
DEFINE_BUS_INTERCONNECT("PL301_ENET", IMX8MN_ICN_ENET, NULL, IMX8MN_ICN_MAIN),
/* Other */
DEFINE_BUS_MASTER("SDMA1", IMX8MN_ICM_SDMA1, IMX8MN_ICN_MAIN),
DEFINE_BUS_MASTER("NAND", IMX8MN_ICM_NAND, IMX8MN_ICN_MAIN),
DEFINE_BUS_MASTER("USDHC1", IMX8MN_ICM_USDHC1, IMX8MN_ICN_MAIN),
DEFINE_BUS_MASTER("USDHC2", IMX8MN_ICM_USDHC2, IMX8MN_ICN_MAIN),
DEFINE_BUS_MASTER("USDHC3", IMX8MN_ICM_USDHC3, IMX8MN_ICN_MAIN),
DEFINE_BUS_INTERCONNECT("PL301_MAIN", IMX8MN_ICN_MAIN, NULL,
IMX8MN_ICN_NOC, IMX8MN_ICS_OCRAM),
};
static int imx8mn_icc_probe(struct platform_device *pdev)
{
return imx_icc_register(pdev, nodes, ARRAY_SIZE(nodes), NULL);
}
static struct platform_driver imx8mn_icc_driver = {
.probe = imx8mn_icc_probe,
.remove = imx_icc_unregister,
.driver = {
.name = "imx8mn-interconnect",
},
};
module_platform_driver(imx8mn_icc_driver);
MODULE_ALIAS("platform:imx8mn-interconnect");
MODULE_AUTHOR("Leonard Crestez <leonard.crestez@nxp.com>");
MODULE_DESCRIPTION("Interconnect framework driver for i.MX8MN SoC");
MODULE_LICENSE("GPL v2");
@@ -0,0 +1,254 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Interconnect framework driver for i.MX8MP SoC
*
* Copyright 2022 NXP
* Peng Fan <peng.fan@nxp.com>
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <dt-bindings/interconnect/fsl,imx8mp.h>
#include "imx.h"
static const struct imx_icc_node_adj_desc imx8mp_noc_adj = {
.bw_mul = 1,
.bw_div = 16,
.main_noc = true,
};
static struct imx_icc_noc_setting noc_setting_nodes[] = {
[IMX8MP_ICM_MLMIX] = {
.reg = 0x180,
.mode = IMX_NOC_MODE_FIXED,
.prio_level = 3,
},
[IMX8MP_ICM_DSP] = {
.reg = 0x200,
.mode = IMX_NOC_MODE_FIXED,
.prio_level = 3,
},
[IMX8MP_ICM_SDMA2PER] = {
.reg = 0x280,
.mode = IMX_NOC_MODE_FIXED,
.prio_level = 4,
},
[IMX8MP_ICM_SDMA2BURST] = {
.reg = 0x300,
.mode = IMX_NOC_MODE_FIXED,
.prio_level = 4,
},
[IMX8MP_ICM_SDMA3PER] = {
.reg = 0x380,
.mode = IMX_NOC_MODE_FIXED,
.prio_level = 4,
},
[IMX8MP_ICM_SDMA3BURST] = {
.reg = 0x400,
.mode = IMX_NOC_MODE_FIXED,
.prio_level = 4,
},
[IMX8MP_ICM_EDMA] = {
.reg = 0x480,
.mode = IMX_NOC_MODE_FIXED,
.prio_level = 4,
},
[IMX8MP_ICM_GPU3D] = {
.reg = 0x500,
.mode = IMX_NOC_MODE_FIXED,
.prio_level = 3,
},
[IMX8MP_ICM_GPU2D] = {
.reg = 0x580,
.mode = IMX_NOC_MODE_FIXED,
.prio_level = 3,
},
[IMX8MP_ICM_HRV] = {
.reg = 0x600,
.mode = IMX_NOC_MODE_FIXED,
.prio_level = 2,
.ext_control = 1,
},
[IMX8MP_ICM_LCDIF_HDMI] = {
.reg = 0x680,
.mode = IMX_NOC_MODE_FIXED,
.prio_level = 2,
.ext_control = 1,
},
[IMX8MP_ICM_HDCP] = {
.reg = 0x700,
.mode = IMX_NOC_MODE_FIXED,
.prio_level = 5,
},
[IMX8MP_ICM_NOC_PCIE] = {
.reg = 0x780,
.mode = IMX_NOC_MODE_FIXED,
.prio_level = 3,
},
[IMX8MP_ICM_USB1] = {
.reg = 0x800,
.mode = IMX_NOC_MODE_FIXED,
.prio_level = 3,
},
[IMX8MP_ICM_USB2] = {
.reg = 0x880,
.mode = IMX_NOC_MODE_FIXED,
.prio_level = 3,
},
[IMX8MP_ICM_PCIE] = {
.reg = 0x900,
.mode = IMX_NOC_MODE_FIXED,
.prio_level = 3,
},
[IMX8MP_ICM_LCDIF_RD] = {
.reg = 0x980,
.mode = IMX_NOC_MODE_FIXED,
.prio_level = 2,
.ext_control = 1,
},
[IMX8MP_ICM_LCDIF_WR] = {
.reg = 0xa00,
.mode = IMX_NOC_MODE_FIXED,
.prio_level = 2,
.ext_control = 1,
},
[IMX8MP_ICM_ISI0] = {
.reg = 0xa80,
.mode = IMX_NOC_MODE_FIXED,
.prio_level = 2,
.ext_control = 1,
},
[IMX8MP_ICM_ISI1] = {
.reg = 0xb00,
.mode = IMX_NOC_MODE_FIXED,
.prio_level = 2,
.ext_control = 1,
},
[IMX8MP_ICM_ISI2] = {
.reg = 0xb80,
.mode = IMX_NOC_MODE_FIXED,
.prio_level = 2,
.ext_control = 1,
},
[IMX8MP_ICM_ISP0] = {
.reg = 0xc00,
.mode = IMX_NOC_MODE_FIXED,
.prio_level = 7,
},
[IMX8MP_ICM_ISP1] = {
.reg = 0xc80,
.mode = IMX_NOC_MODE_FIXED,
.prio_level = 7,
},
[IMX8MP_ICM_DWE] = {
.reg = 0xd00,
.mode = IMX_NOC_MODE_FIXED,
.prio_level = 7,
},
[IMX8MP_ICM_VPU_G1] = {
.reg = 0xd80,
.mode = IMX_NOC_MODE_FIXED,
.prio_level = 3,
},
[IMX8MP_ICM_VPU_G2] = {
.reg = 0xe00,
.mode = IMX_NOC_MODE_FIXED,
.prio_level = 3,
},
[IMX8MP_ICM_VPU_H1] = {
.reg = 0xe80,
.mode = IMX_NOC_MODE_FIXED,
.prio_level = 3,
},
[IMX8MP_ICN_MEDIA] = {
.mode = IMX_NOC_MODE_UNCONFIGURED,
},
[IMX8MP_ICN_VIDEO] = {
.mode = IMX_NOC_MODE_UNCONFIGURED,
},
[IMX8MP_ICN_AUDIO] = {
.mode = IMX_NOC_MODE_UNCONFIGURED,
},
[IMX8MP_ICN_HDMI] = {
.mode = IMX_NOC_MODE_UNCONFIGURED,
},
[IMX8MP_ICN_GPU] = {
.mode = IMX_NOC_MODE_UNCONFIGURED,
},
[IMX8MP_ICN_HSIO] = {
.mode = IMX_NOC_MODE_UNCONFIGURED,
},
};
/* Describe bus masters, slaves and connections between them */
static struct imx_icc_node_desc nodes[] = {
DEFINE_BUS_INTERCONNECT("NOC", IMX8MP_ICN_NOC, &imx8mp_noc_adj,
IMX8MP_ICS_DRAM, IMX8MP_ICN_MAIN),
DEFINE_BUS_SLAVE("OCRAM", IMX8MP_ICS_OCRAM, NULL),
DEFINE_BUS_SLAVE("DRAM", IMX8MP_ICS_DRAM, NULL),
DEFINE_BUS_MASTER("A53", IMX8MP_ICM_A53, IMX8MP_ICN_NOC),
DEFINE_BUS_MASTER("SUPERMIX", IMX8MP_ICM_SUPERMIX, IMX8MP_ICN_NOC),
DEFINE_BUS_MASTER("GIC", IMX8MP_ICM_GIC, IMX8MP_ICN_NOC),
DEFINE_BUS_MASTER("MLMIX", IMX8MP_ICM_MLMIX, IMX8MP_ICN_NOC),
DEFINE_BUS_INTERCONNECT("NOC_AUDIO", IMX8MP_ICN_AUDIO, NULL, IMX8MP_ICN_NOC),
DEFINE_BUS_MASTER("DSP", IMX8MP_ICM_DSP, IMX8MP_ICN_AUDIO),
DEFINE_BUS_MASTER("SDMA2PER", IMX8MP_ICM_SDMA2PER, IMX8MP_ICN_AUDIO),
DEFINE_BUS_MASTER("SDMA2BURST", IMX8MP_ICM_SDMA2BURST, IMX8MP_ICN_AUDIO),
DEFINE_BUS_MASTER("SDMA3PER", IMX8MP_ICM_SDMA3PER, IMX8MP_ICN_AUDIO),
DEFINE_BUS_MASTER("SDMA3BURST", IMX8MP_ICM_SDMA3BURST, IMX8MP_ICN_AUDIO),
DEFINE_BUS_MASTER("EDMA", IMX8MP_ICM_EDMA, IMX8MP_ICN_AUDIO),
DEFINE_BUS_INTERCONNECT("NOC_GPU", IMX8MP_ICN_GPU, NULL, IMX8MP_ICN_NOC),
DEFINE_BUS_MASTER("GPU 2D", IMX8MP_ICM_GPU2D, IMX8MP_ICN_GPU),
DEFINE_BUS_MASTER("GPU 3D", IMX8MP_ICM_GPU3D, IMX8MP_ICN_GPU),
DEFINE_BUS_INTERCONNECT("NOC_HDMI", IMX8MP_ICN_HDMI, NULL, IMX8MP_ICN_NOC),
DEFINE_BUS_MASTER("HRV", IMX8MP_ICM_HRV, IMX8MP_ICN_HDMI),
DEFINE_BUS_MASTER("LCDIF_HDMI", IMX8MP_ICM_LCDIF_HDMI, IMX8MP_ICN_HDMI),
DEFINE_BUS_MASTER("HDCP", IMX8MP_ICM_HDCP, IMX8MP_ICN_HDMI),
DEFINE_BUS_INTERCONNECT("NOC_HSIO", IMX8MP_ICN_HSIO, NULL, IMX8MP_ICN_NOC),
DEFINE_BUS_MASTER("NOC_PCIE", IMX8MP_ICM_NOC_PCIE, IMX8MP_ICN_HSIO),
DEFINE_BUS_MASTER("USB1", IMX8MP_ICM_USB1, IMX8MP_ICN_HSIO),
DEFINE_BUS_MASTER("USB2", IMX8MP_ICM_USB2, IMX8MP_ICN_HSIO),
DEFINE_BUS_MASTER("PCIE", IMX8MP_ICM_PCIE, IMX8MP_ICN_HSIO),
DEFINE_BUS_INTERCONNECT("NOC_MEDIA", IMX8MP_ICN_MEDIA, NULL, IMX8MP_ICN_NOC),
DEFINE_BUS_MASTER("LCDIF_RD", IMX8MP_ICM_LCDIF_RD, IMX8MP_ICN_MEDIA),
DEFINE_BUS_MASTER("LCDIF_WR", IMX8MP_ICM_LCDIF_WR, IMX8MP_ICN_MEDIA),
DEFINE_BUS_MASTER("ISI0", IMX8MP_ICM_ISI0, IMX8MP_ICN_MEDIA),
DEFINE_BUS_MASTER("ISI1", IMX8MP_ICM_ISI1, IMX8MP_ICN_MEDIA),
DEFINE_BUS_MASTER("ISI2", IMX8MP_ICM_ISI2, IMX8MP_ICN_MEDIA),
DEFINE_BUS_MASTER("ISP0", IMX8MP_ICM_ISP0, IMX8MP_ICN_MEDIA),
DEFINE_BUS_MASTER("ISP1", IMX8MP_ICM_ISP1, IMX8MP_ICN_MEDIA),
DEFINE_BUS_MASTER("DWE", IMX8MP_ICM_DWE, IMX8MP_ICN_MEDIA),
DEFINE_BUS_INTERCONNECT("NOC_VIDEO", IMX8MP_ICN_VIDEO, NULL, IMX8MP_ICN_NOC),
DEFINE_BUS_MASTER("VPU G1", IMX8MP_ICM_VPU_G1, IMX8MP_ICN_VIDEO),
DEFINE_BUS_MASTER("VPU G2", IMX8MP_ICM_VPU_G2, IMX8MP_ICN_VIDEO),
DEFINE_BUS_MASTER("VPU H1", IMX8MP_ICM_VPU_H1, IMX8MP_ICN_VIDEO),
DEFINE_BUS_INTERCONNECT("PL301_MAIN", IMX8MP_ICN_MAIN, NULL,
IMX8MP_ICN_NOC, IMX8MP_ICS_OCRAM),
};
static int imx8mp_icc_probe(struct platform_device *pdev)
{
return imx_icc_register(pdev, nodes, ARRAY_SIZE(nodes), noc_setting_nodes);
}
static struct platform_driver imx8mp_icc_driver = {
.probe = imx8mp_icc_probe,
.remove = imx_icc_unregister,
.driver = {
.name = "imx8mp-interconnect",
},
};
module_platform_driver(imx8mp_icc_driver);
MODULE_AUTHOR("Peng Fan <peng.fan@nxp.com>");
MODULE_DESCRIPTION("Interconnect framework driver for i.MX8MP SoC");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:imx8mp-interconnect");
@@ -0,0 +1,101 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Interconnect framework driver for i.MX8MQ SoC
*
* Copyright (c) 2019-2020, NXP
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/interconnect-provider.h>
#include <dt-bindings/interconnect/imx8mq.h>
#include "imx.h"
static const struct imx_icc_node_adj_desc imx8mq_dram_adj = {
.bw_mul = 1,
.bw_div = 4,
.phandle_name = "fsl,ddrc",
};
static const struct imx_icc_node_adj_desc imx8mq_noc_adj = {
.bw_mul = 1,
.bw_div = 4,
.main_noc = true,
};
/*
* Describe bus masters, slaves and connections between them
*
* This is a simplified subset of the bus diagram, there are several other
* PL301 nics which are skipped/merged into PL301_MAIN
*/
static struct imx_icc_node_desc nodes[] = {
DEFINE_BUS_INTERCONNECT("NOC", IMX8MQ_ICN_NOC, &imx8mq_noc_adj,
IMX8MQ_ICS_DRAM, IMX8MQ_ICN_MAIN),
DEFINE_BUS_SLAVE("DRAM", IMX8MQ_ICS_DRAM, &imx8mq_dram_adj),
DEFINE_BUS_SLAVE("OCRAM", IMX8MQ_ICS_OCRAM, NULL),
DEFINE_BUS_MASTER("A53", IMX8MQ_ICM_A53, IMX8MQ_ICN_NOC),
/* VPUMIX */
DEFINE_BUS_MASTER("VPU", IMX8MQ_ICM_VPU, IMX8MQ_ICN_VIDEO),
DEFINE_BUS_INTERCONNECT("PL301_VIDEO", IMX8MQ_ICN_VIDEO, NULL, IMX8MQ_ICN_NOC),
/* GPUMIX */
DEFINE_BUS_MASTER("GPU", IMX8MQ_ICM_GPU, IMX8MQ_ICN_GPU),
DEFINE_BUS_INTERCONNECT("PL301_GPU", IMX8MQ_ICN_GPU, NULL, IMX8MQ_ICN_NOC),
/* DISPMIX (only for DCSS) */
DEFINE_BUS_MASTER("DC", IMX8MQ_ICM_DCSS, IMX8MQ_ICN_DCSS),
DEFINE_BUS_INTERCONNECT("PL301_DC", IMX8MQ_ICN_DCSS, NULL, IMX8MQ_ICN_NOC),
/* USBMIX */
DEFINE_BUS_MASTER("USB1", IMX8MQ_ICM_USB1, IMX8MQ_ICN_USB),
DEFINE_BUS_MASTER("USB2", IMX8MQ_ICM_USB2, IMX8MQ_ICN_USB),
DEFINE_BUS_INTERCONNECT("PL301_USB", IMX8MQ_ICN_USB, NULL, IMX8MQ_ICN_NOC),
/* PL301_DISPLAY (IPs other than DCSS, inside SUPERMIX) */
DEFINE_BUS_MASTER("CSI1", IMX8MQ_ICM_CSI1, IMX8MQ_ICN_DISPLAY),
DEFINE_BUS_MASTER("CSI2", IMX8MQ_ICM_CSI2, IMX8MQ_ICN_DISPLAY),
DEFINE_BUS_MASTER("LCDIF", IMX8MQ_ICM_LCDIF, IMX8MQ_ICN_DISPLAY),
DEFINE_BUS_INTERCONNECT("PL301_DISPLAY", IMX8MQ_ICN_DISPLAY, NULL, IMX8MQ_ICN_MAIN),
/* AUDIO */
DEFINE_BUS_MASTER("SDMA2", IMX8MQ_ICM_SDMA2, IMX8MQ_ICN_AUDIO),
DEFINE_BUS_INTERCONNECT("PL301_AUDIO", IMX8MQ_ICN_AUDIO, NULL, IMX8MQ_ICN_DISPLAY),
/* ENET */
DEFINE_BUS_MASTER("ENET", IMX8MQ_ICM_ENET, IMX8MQ_ICN_ENET),
DEFINE_BUS_INTERCONNECT("PL301_ENET", IMX8MQ_ICN_ENET, NULL, IMX8MQ_ICN_MAIN),
/* OTHER */
DEFINE_BUS_MASTER("SDMA1", IMX8MQ_ICM_SDMA1, IMX8MQ_ICN_MAIN),
DEFINE_BUS_MASTER("NAND", IMX8MQ_ICM_NAND, IMX8MQ_ICN_MAIN),
DEFINE_BUS_MASTER("USDHC1", IMX8MQ_ICM_USDHC1, IMX8MQ_ICN_MAIN),
DEFINE_BUS_MASTER("USDHC2", IMX8MQ_ICM_USDHC2, IMX8MQ_ICN_MAIN),
DEFINE_BUS_MASTER("PCIE1", IMX8MQ_ICM_PCIE1, IMX8MQ_ICN_MAIN),
DEFINE_BUS_MASTER("PCIE2", IMX8MQ_ICM_PCIE2, IMX8MQ_ICN_MAIN),
DEFINE_BUS_INTERCONNECT("PL301_MAIN", IMX8MQ_ICN_MAIN, NULL,
IMX8MQ_ICN_NOC, IMX8MQ_ICS_OCRAM),
};
static int imx8mq_icc_probe(struct platform_device *pdev)
{
return imx_icc_register(pdev, nodes, ARRAY_SIZE(nodes), NULL);
}
static struct platform_driver imx8mq_icc_driver = {
.probe = imx8mq_icc_probe,
.remove = imx_icc_unregister,
.driver = {
.name = "imx8mq-interconnect",
.sync_state = icc_sync_state,
},
};
module_platform_driver(imx8mq_icc_driver);
MODULE_ALIAS("platform:imx8mq-interconnect");
MODULE_AUTHOR("Leonard Crestez <leonard.crestez@nxp.com>");
MODULE_DESCRIPTION("Interconnect framework driver for i.MX8MQ SoC");
MODULE_LICENSE("GPL v2");
@@ -0,0 +1,47 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Interconnect framework internal structs
*
* Copyright (c) 2019, Linaro Ltd.
* Author: Georgi Djakov <georgi.djakov@linaro.org>
*/
#ifndef __DRIVERS_INTERCONNECT_INTERNAL_H
#define __DRIVERS_INTERCONNECT_INTERNAL_H
/**
* struct icc_req - constraints that are attached to each node
* @req_node: entry in list of requests for the particular @node
* @node: the interconnect node to which this constraint applies
* @dev: reference to the device that sets the constraints
* @enabled: indicates whether the path with this request is enabled
* @tag: path tag (optional)
* @avg_bw: an integer describing the average bandwidth in kBps
* @peak_bw: an integer describing the peak bandwidth in kBps
*/
struct icc_req {
struct hlist_node req_node;
struct icc_node *node;
struct device *dev;
bool enabled;
u32 tag;
u32 avg_bw;
u32 peak_bw;
};
/**
* struct icc_path - interconnect path structure
* @name: a string name of the path (useful for ftrace)
* @num_nodes: number of hops (nodes)
* @reqs: array of the requests applicable to this path of nodes
*/
struct icc_path {
const char *name;
size_t num_nodes;
struct icc_req reqs[] __counted_by(num_nodes);
};
struct icc_path *icc_get(struct device *dev, const char *src, const char *dst);
int icc_debugfs_client_init(struct dentry *icc_dir);
#endif
@@ -0,0 +1,36 @@
# SPDX-License-Identifier: GPL-2.0-only
config INTERCONNECT_MTK
bool "MediaTek interconnect drivers"
depends on ARCH_MEDIATEK || COMPILE_TEST
help
Support for MediaTek's bus interconnect hardware.
config INTERCONNECT_MTK_DVFSRC_EMI
tristate "MediaTek DVFSRC EMI interconnect driver"
depends on INTERCONNECT_MTK && MTK_DVFSRC
help
This is a driver for the MediaTek External Memory Interface
interconnect on SoCs equipped with the integrated Dynamic
Voltage Frequency Scaling Resource Collector (DVFSRC) MCU
config INTERCONNECT_MTK_MT8183
tristate "MediaTek MT8183 interconnect driver"
depends on INTERCONNECT_MTK_DVFSRC_EMI
help
This is a driver for the MediaTek bus interconnect on MT8183-based
platforms.
config INTERCONNECT_MTK_MT8195
tristate "MediaTek MT8195 interconnect driver"
depends on INTERCONNECT_MTK_DVFSRC_EMI
help
This is a driver for the MediaTek bus interconnect on MT8195-based
platforms.
config INTERCONNECT_MTK_MT8196
tristate "MediaTek MT8196 interconnect driver"
depends on INTERCONNECT_MTK_DVFSRC_EMI
help
This is a driver for the MediaTek bus interconnect on MT8196-based
platforms.
@@ -0,0 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_INTERCONNECT_MTK_DVFSRC_EMI) += icc-emi.o
obj-$(CONFIG_INTERCONNECT_MTK_MT8183) += mt8183.o
obj-$(CONFIG_INTERCONNECT_MTK_MT8195) += mt8195.o
obj-$(CONFIG_INTERCONNECT_MTK_MT8195) += mt8196.o
@@ -0,0 +1,156 @@
// SPDX-License-Identifier: GPL-2.0
/*
* MediaTek External Memory Interface (EMI) Interconnect driver
*
* Copyright (c) 2021 MediaTek Inc.
* Copyright (c) 2024 Collabora Ltd.
* AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
*/
#include <linux/interconnect.h>
#include <linux/interconnect-provider.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/overflow.h>
#include <linux/platform_device.h>
#include <linux/soc/mediatek/dvfsrc.h>
#include "icc-emi.h"
static int mtk_emi_icc_aggregate(struct icc_node *node, u32 tag, u32 avg_bw,
u32 peak_bw, u32 *agg_avg, u32 *agg_peak)
{
struct mtk_icc_node *in = node->data;
if (check_add_overflow(*agg_avg, avg_bw, agg_avg))
*agg_avg = U32_MAX;
*agg_peak = max_t(u32, *agg_peak, peak_bw);
in->sum_avg = *agg_avg;
in->max_peak = *agg_peak;
return 0;
}
static int mtk_emi_icc_set(struct icc_node *src, struct icc_node *dst)
{
struct mtk_icc_node *node = dst->data;
struct device *dev;
int ret;
if (unlikely(!src->provider))
return -EINVAL;
dev = src->provider->dev->parent;
switch (node->ep) {
case 0:
break;
case 1:
ret = mtk_dvfsrc_send_request(dev, MTK_DVFSRC_CMD_PEAK_BW, node->max_peak);
if (ret) {
dev_err(dev, "Cannot send peak bw request: %d\n", ret);
return ret;
}
ret = mtk_dvfsrc_send_request(dev, MTK_DVFSRC_CMD_BW, node->sum_avg);
if (ret) {
dev_err(dev, "Cannot send bw request: %d\n", ret);
return ret;
}
break;
case 2:
ret = mtk_dvfsrc_send_request(dev, MTK_DVFSRC_CMD_HRT_BW, node->sum_avg);
if (ret) {
dev_err(dev, "Cannot send HRT bw request: %d\n", ret);
return ret;
}
break;
default:
dev_err(src->provider->dev, "Unknown endpoint %u\n", node->ep);
return -EINVAL;
}
return 0;
}
int mtk_emi_icc_probe(struct platform_device *pdev)
{
const struct mtk_icc_desc *desc;
struct device *dev = &pdev->dev;
struct icc_node *node;
struct icc_onecell_data *data;
struct icc_provider *provider;
struct mtk_icc_node **mnodes;
int i, j, ret;
desc = of_device_get_match_data(dev);
if (!desc)
return -EINVAL;
mnodes = desc->nodes;
provider = devm_kzalloc(dev, sizeof(*provider), GFP_KERNEL);
if (!provider)
return -ENOMEM;
data = devm_kzalloc(dev, struct_size(data, nodes, desc->num_nodes), GFP_KERNEL);
if (!data)
return -ENOMEM;
provider->dev = dev;
provider->set = mtk_emi_icc_set;
provider->aggregate = mtk_emi_icc_aggregate;
provider->xlate = of_icc_xlate_onecell;
INIT_LIST_HEAD(&provider->nodes);
provider->data = data;
for (i = 0; i < desc->num_nodes; i++) {
if (!mnodes[i])
continue;
node = icc_node_create(mnodes[i]->id);
if (IS_ERR(node)) {
ret = PTR_ERR(node);
goto err;
}
node->name = mnodes[i]->name;
node->data = mnodes[i];
icc_node_add(node, provider);
for (j = 0; j < mnodes[i]->num_links; j++)
icc_link_create(node, mnodes[i]->links[j]);
data->nodes[i] = node;
}
data->num_nodes = desc->num_nodes;
ret = icc_provider_register(provider);
if (ret)
goto err;
platform_set_drvdata(pdev, provider);
return 0;
err:
icc_nodes_remove(provider);
return ret;
}
EXPORT_SYMBOL_GPL(mtk_emi_icc_probe);
void mtk_emi_icc_remove(struct platform_device *pdev)
{
struct icc_provider *provider = platform_get_drvdata(pdev);
icc_provider_deregister(provider);
icc_nodes_remove(provider);
}
EXPORT_SYMBOL_GPL(mtk_emi_icc_remove);
MODULE_AUTHOR("AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>");
MODULE_AUTHOR("Henry Chen <henryc.chen@mediatek.com>");
MODULE_DESCRIPTION("MediaTek External Memory Interface interconnect driver");
MODULE_LICENSE("GPL");
@@ -0,0 +1,40 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2021 MediaTek Inc.
* Copyright (c) 2024 Collabora Ltd.
* AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
*/
#ifndef __DRIVERS_INTERCONNECT_MEDIATEK_ICC_EMI_H
#define __DRIVERS_INTERCONNECT_MEDIATEK_ICC_EMI_H
/**
* struct mtk_icc_node - Mediatek EMI Interconnect Node
* @name: The interconnect node name which is shown in debugfs
* @ep: Type of this endpoint
* @id: Unique node identifier
* @sum_avg: Current sum aggregate value of all average bw requests in kBps
* @max_peak: Current max aggregate value of all peak bw requests in kBps
* @num_links: The total number of @links
* @links: Array of @id linked to this node
*/
struct mtk_icc_node {
unsigned char *name;
int ep;
u16 id;
u64 sum_avg;
u64 max_peak;
u16 num_links;
u16 links[] __counted_by(num_links);
};
struct mtk_icc_desc {
struct mtk_icc_node **nodes;
size_t num_nodes;
};
int mtk_emi_icc_probe(struct platform_device *pdev);
void mtk_emi_icc_remove(struct platform_device *pdev);
#endif /* __DRIVERS_INTERCONNECT_MEDIATEK_ICC_EMI_H */
@@ -0,0 +1,142 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2021 MediaTek Inc.
* Copyright (c) 2024 Collabora Ltd.
* AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
*/
#include <linux/device.h>
#include <linux/interconnect.h>
#include <linux/interconnect-provider.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <dt-bindings/interconnect/mediatek,mt8183.h>
#include "icc-emi.h"
static struct mtk_icc_node ddr_emi = {
.name = "ddr-emi",
.id = SLAVE_DDR_EMI,
.ep = 1,
};
static struct mtk_icc_node mcusys = {
.name = "mcusys",
.id = MASTER_MCUSYS,
.ep = 0,
.num_links = 1,
.links = { SLAVE_DDR_EMI }
};
static struct mtk_icc_node gpu = {
.name = "gpu",
.id = MASTER_MFG,
.ep = 0,
.num_links = 1,
.links = { SLAVE_DDR_EMI }
};
static struct mtk_icc_node mmsys = {
.name = "mmsys",
.id = MASTER_MMSYS,
.ep = 0,
.num_links = 1,
.links = { SLAVE_DDR_EMI }
};
static struct mtk_icc_node mm_vpu = {
.name = "mm-vpu",
.id = MASTER_MM_VPU,
.ep = 0,
.num_links = 1,
.links = { MASTER_MMSYS }
};
static struct mtk_icc_node mm_disp = {
.name = "mm-disp",
.id = MASTER_MM_DISP,
.ep = 0,
.num_links = 1,
.links = { MASTER_MMSYS }
};
static struct mtk_icc_node mm_vdec = {
.name = "mm-vdec",
.id = MASTER_MM_VDEC,
.ep = 0,
.num_links = 1,
.links = { MASTER_MMSYS }
};
static struct mtk_icc_node mm_venc = {
.name = "mm-venc",
.id = MASTER_MM_VENC,
.ep = 0,
.num_links = 1,
.links = { MASTER_MMSYS }
};
static struct mtk_icc_node mm_cam = {
.name = "mm-cam",
.id = MASTER_MM_CAM,
.ep = 0,
.num_links = 1,
.links = { MASTER_MMSYS }
};
static struct mtk_icc_node mm_img = {
.name = "mm-img",
.id = MASTER_MM_IMG,
.ep = 0,
.num_links = 1,
.links = { MASTER_MMSYS }
};
static struct mtk_icc_node mm_mdp = {
.name = "mm-mdp",
.id = MASTER_MM_MDP,
.ep = 0,
.num_links = 1,
.links = { MASTER_MMSYS }
};
static struct mtk_icc_node *mt8183_emi_icc_nodes[] = {
[SLAVE_DDR_EMI] = &ddr_emi,
[MASTER_MCUSYS] = &mcusys,
[MASTER_MFG] = &gpu,
[MASTER_MMSYS] = &mmsys,
[MASTER_MM_VPU] = &mm_vpu,
[MASTER_MM_DISP] = &mm_disp,
[MASTER_MM_VDEC] = &mm_vdec,
[MASTER_MM_VENC] = &mm_venc,
[MASTER_MM_CAM] = &mm_cam,
[MASTER_MM_IMG] = &mm_img,
[MASTER_MM_MDP] = &mm_mdp
};
static const struct mtk_icc_desc mt8183_emi_icc = {
.nodes = mt8183_emi_icc_nodes,
.num_nodes = ARRAY_SIZE(mt8183_emi_icc_nodes),
};
static const struct of_device_id mtk_mt8183_emi_icc_of_match[] = {
{ .compatible = "mediatek,mt8183-emi", .data = &mt8183_emi_icc },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, mtk_mt8183_emi_icc_of_match);
static struct platform_driver mtk_emi_icc_mt8183_driver = {
.driver = {
.name = "emi-icc-mt8183",
.of_match_table = mtk_mt8183_emi_icc_of_match,
.sync_state = icc_sync_state,
},
.probe = mtk_emi_icc_probe,
.remove = mtk_emi_icc_remove,
};
module_platform_driver(mtk_emi_icc_mt8183_driver);
MODULE_AUTHOR("AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>");
MODULE_DESCRIPTION("MediaTek MT8183 EMI ICC driver");
MODULE_LICENSE("GPL");
@@ -0,0 +1,338 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2021 MediaTek Inc.
* Copyright (c) 2024 Collabora Ltd.
* AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
*/
#include <linux/device.h>
#include <linux/interconnect.h>
#include <linux/interconnect-provider.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <dt-bindings/interconnect/mediatek,mt8195.h>
#include "icc-emi.h"
static struct mtk_icc_node ddr_emi = {
.name = "ddr-emi",
.id = SLAVE_DDR_EMI,
.ep = 1,
};
static struct mtk_icc_node mcusys = {
.name = "mcusys",
.id = MASTER_MCUSYS,
.ep = 0,
.num_links = 1,
.links = { SLAVE_DDR_EMI }
};
static struct mtk_icc_node gpu = {
.name = "gpu",
.id = MASTER_GPUSYS,
.ep = 0,
.num_links = 1,
.links = { SLAVE_DDR_EMI }
};
static struct mtk_icc_node mmsys = {
.name = "mmsys",
.id = MASTER_MMSYS,
.ep = 0,
.num_links = 1,
.links = { SLAVE_DDR_EMI }
};
static struct mtk_icc_node mm_vpu = {
.name = "mm-vpu",
.id = MASTER_MM_VPU,
.ep = 0,
.num_links = 1,
.links = { MASTER_MMSYS }
};
static struct mtk_icc_node mm_disp = {
.name = "mm-disp",
.id = MASTER_MM_DISP,
.ep = 0,
.num_links = 1,
.links = { MASTER_MMSYS }
};
static struct mtk_icc_node mm_vdec = {
.name = "mm-vdec",
.id = MASTER_MM_VDEC,
.ep = 0,
.num_links = 1,
.links = { MASTER_MMSYS }
};
static struct mtk_icc_node mm_venc = {
.name = "mm-venc",
.id = MASTER_MM_VENC,
.ep = 0,
.num_links = 1,
.links = { MASTER_MMSYS }
};
static struct mtk_icc_node mm_cam = {
.name = "mm-cam",
.id = MASTER_MM_CAM,
.ep = 0,
.num_links = 1,
.links = { MASTER_MMSYS }
};
static struct mtk_icc_node mm_img = {
.name = "mm-img",
.id = MASTER_MM_IMG,
.ep = 0,
.num_links = 1,
.links = { MASTER_MMSYS }
};
static struct mtk_icc_node mm_mdp = {
.name = "mm-mdp",
.id = MASTER_MM_MDP,
.ep = 0,
.num_links = 1,
.links = { MASTER_MMSYS }
};
static struct mtk_icc_node vpusys = {
.name = "vpusys",
.id = MASTER_VPUSYS,
.ep = 0,
.num_links = 1,
.links = { SLAVE_DDR_EMI }
};
static struct mtk_icc_node vpu_port0 = {
.name = "vpu-port0",
.id = MASTER_VPU_0,
.ep = 0,
.num_links = 1,
.links = { MASTER_VPUSYS }
};
static struct mtk_icc_node vpu_port1 = {
.name = "vpu-port1",
.id = MASTER_VPU_1,
.ep = 0,
.num_links = 1,
.links = { MASTER_VPUSYS }
};
static struct mtk_icc_node mdlasys = {
.name = "mdlasys",
.id = MASTER_MDLASYS,
.ep = 0,
.num_links = 1,
.links = { SLAVE_DDR_EMI }
};
static struct mtk_icc_node mdla_port0 = {
.name = "mdla-port0",
.id = MASTER_MDLA_0,
.ep = 0,
.num_links = 1,
.links = { MASTER_MDLASYS }
};
static struct mtk_icc_node ufs = {
.name = "ufs",
.id = MASTER_UFS,
.ep = 0,
.num_links = 1,
.links = { SLAVE_DDR_EMI }
};
static struct mtk_icc_node pcie0 = {
.name = "pcie0",
.id = MASTER_PCIE_0,
.ep = 0,
.num_links = 1,
.links = { SLAVE_DDR_EMI }
};
static struct mtk_icc_node pcie1 = {
.name = "pcie1",
.id = MASTER_PCIE_1,
.ep = 0,
.num_links = 1,
.links = { SLAVE_DDR_EMI }
};
static struct mtk_icc_node usb = {
.name = "usb",
.id = MASTER_USB,
.ep = 0,
.num_links = 1,
.links = { SLAVE_DDR_EMI }
};
static struct mtk_icc_node wifi = {
.name = "wifi",
.id = MASTER_WIFI,
.ep = 0,
.num_links = 1,
.links = { SLAVE_DDR_EMI }
};
static struct mtk_icc_node bt = {
.name = "bt",
.id = MASTER_BT,
.ep = 0,
.num_links = 1,
.links = { SLAVE_DDR_EMI }
};
static struct mtk_icc_node netsys = {
.name = "netsys",
.id = MASTER_NETSYS,
.ep = 0,
.num_links = 1,
.links = { SLAVE_DDR_EMI }
};
static struct mtk_icc_node dbgif = {
.name = "dbgif",
.id = MASTER_DBGIF,
.ep = 0,
.num_links = 1,
.links = { SLAVE_DDR_EMI }
};
static struct mtk_icc_node hrt_ddr_emi = {
.name = "hrt-ddr-emi",
.id = SLAVE_HRT_DDR_EMI,
.ep = 2,
};
static struct mtk_icc_node hrt_mmsys = {
.name = "hrt-mmsys",
.id = MASTER_HRT_MMSYS,
.ep = 0,
.num_links = 1,
.links = { SLAVE_HRT_DDR_EMI }
};
static struct mtk_icc_node hrt_mm_disp = {
.name = "hrt-mm-disp",
.id = MASTER_HRT_MM_DISP,
.ep = 0,
.num_links = 1,
.links = { MASTER_HRT_MMSYS }
};
static struct mtk_icc_node hrt_mm_vdec = {
.name = "hrt-mm-vdec",
.id = MASTER_HRT_MM_VDEC,
.ep = 0,
.num_links = 1,
.links = { MASTER_HRT_MMSYS }
};
static struct mtk_icc_node hrt_mm_venc = {
.name = "hrt-mm-venc",
.id = MASTER_HRT_MM_VENC,
.ep = 0,
.num_links = 1,
.links = { MASTER_HRT_MMSYS }
};
static struct mtk_icc_node hrt_mm_cam = {
.name = "hrt-mm-cam",
.id = MASTER_HRT_MM_CAM,
.ep = 0,
.num_links = 1,
.links = { MASTER_HRT_MMSYS }
};
static struct mtk_icc_node hrt_mm_img = {
.name = "hrt-mm-img",
.id = MASTER_HRT_MM_IMG,
.ep = 0,
.num_links = 1,
.links = { MASTER_HRT_MMSYS }
};
static struct mtk_icc_node hrt_mm_mdp = {
.name = "hrt-mm-mdp",
.id = MASTER_HRT_MM_MDP,
.ep = 0,
.num_links = 1,
.links = { MASTER_HRT_MMSYS }
};
static struct mtk_icc_node hrt_dbgif = {
.name = "hrt-dbgif",
.id = MASTER_HRT_DBGIF,
.ep = 0,
.num_links = 1,
.links = { SLAVE_HRT_DDR_EMI }
};
static struct mtk_icc_node *mt8195_emi_icc_nodes[] = {
[SLAVE_DDR_EMI] = &ddr_emi,
[MASTER_MCUSYS] = &mcusys,
[MASTER_GPUSYS] = &gpu,
[MASTER_MMSYS] = &mmsys,
[MASTER_MM_VPU] = &mm_vpu,
[MASTER_MM_DISP] = &mm_disp,
[MASTER_MM_VDEC] = &mm_vdec,
[MASTER_MM_VENC] = &mm_venc,
[MASTER_MM_CAM] = &mm_cam,
[MASTER_MM_IMG] = &mm_img,
[MASTER_MM_MDP] = &mm_mdp,
[MASTER_VPUSYS] = &vpusys,
[MASTER_VPU_0] = &vpu_port0,
[MASTER_VPU_1] = &vpu_port1,
[MASTER_MDLASYS] = &mdlasys,
[MASTER_MDLA_0] = &mdla_port0,
[MASTER_UFS] = &ufs,
[MASTER_PCIE_0] = &pcie0,
[MASTER_PCIE_1] = &pcie1,
[MASTER_USB] = &usb,
[MASTER_WIFI] = &wifi,
[MASTER_BT] = &bt,
[MASTER_NETSYS] = &netsys,
[MASTER_DBGIF] = &dbgif,
[SLAVE_HRT_DDR_EMI] = &hrt_ddr_emi,
[MASTER_HRT_MMSYS] = &hrt_mmsys,
[MASTER_HRT_MM_DISP] = &hrt_mm_disp,
[MASTER_HRT_MM_VDEC] = &hrt_mm_vdec,
[MASTER_HRT_MM_VENC] = &hrt_mm_venc,
[MASTER_HRT_MM_CAM] = &hrt_mm_cam,
[MASTER_HRT_MM_IMG] = &hrt_mm_img,
[MASTER_HRT_MM_MDP] = &hrt_mm_mdp,
[MASTER_HRT_DBGIF] = &hrt_dbgif
};
static struct mtk_icc_desc mt8195_emi_icc = {
.nodes = mt8195_emi_icc_nodes,
.num_nodes = ARRAY_SIZE(mt8195_emi_icc_nodes),
};
static const struct of_device_id mtk_mt8195_emi_icc_of_match[] = {
{ .compatible = "mediatek,mt8195-emi", .data = &mt8195_emi_icc },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, mtk_mt8195_emi_icc_of_match);
static struct platform_driver mtk_emi_icc_mt8195_driver = {
.driver = {
.name = "emi-icc-mt8195",
.of_match_table = mtk_mt8195_emi_icc_of_match,
.sync_state = icc_sync_state,
},
.probe = mtk_emi_icc_probe,
.remove = mtk_emi_icc_remove,
};
module_platform_driver(mtk_emi_icc_mt8195_driver);
MODULE_AUTHOR("AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>");
MODULE_DESCRIPTION("MediaTek MT8195 EMI ICC driver");
MODULE_LICENSE("GPL");
@@ -0,0 +1,382 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2025 Collabora Ltd.
* AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
*/
#include <linux/device.h>
#include <linux/interconnect.h>
#include <linux/interconnect-provider.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <dt-bindings/interconnect/mediatek,mt8196.h>
#include "icc-emi.h"
static struct mtk_icc_node ddr_emi = {
.name = "ddr-emi",
.id = SLAVE_DDR_EMI,
.ep = 1,
};
static struct mtk_icc_node mcusys = {
.name = "mcusys",
.id = MASTER_MCUSYS,
.ep = 0,
.num_links = 1,
.links = { SLAVE_DDR_EMI }
};
static struct mtk_icc_node mcu_port0 = {
.name = "mcu-port0",
.id = MASTER_MCU_0,
.ep = 0,
.num_links = 1,
.links = { SLAVE_DDR_EMI }
};
static struct mtk_icc_node mcu_port1 = {
.name = "mcu-port1",
.id = MASTER_MCU_1,
.ep = 0,
.num_links = 1,
.links = { SLAVE_DDR_EMI }
};
static struct mtk_icc_node mcu_port2 = {
.name = "mcu-port2",
.id = MASTER_MCU_2,
.ep = 0,
.num_links = 1,
.links = { SLAVE_DDR_EMI }
};
static struct mtk_icc_node mcu_port3 = {
.name = "mcu-port3",
.id = MASTER_MCU_3,
.ep = 0,
.num_links = 1,
.links = { SLAVE_DDR_EMI }
};
static struct mtk_icc_node mcu_port4 = {
.name = "mcu-port4",
.id = MASTER_MCU_4,
.ep = 0,
.num_links = 1,
.links = { SLAVE_DDR_EMI }
};
static struct mtk_icc_node gpu = {
.name = "gpu",
.id = MASTER_GPUSYS,
.ep = 0,
.num_links = 1,
.links = { SLAVE_DDR_EMI }
};
static struct mtk_icc_node mmsys = {
.name = "mmsys",
.id = MASTER_MMSYS,
.ep = 0,
.num_links = 1,
.links = { SLAVE_DDR_EMI }
};
static struct mtk_icc_node mm_vpu = {
.name = "mm-vpu",
.id = MASTER_MM_VPU,
.ep = 0,
.num_links = 1,
.links = { MASTER_MMSYS }
};
static struct mtk_icc_node mm_disp = {
.name = "mm-disp",
.id = MASTER_MM_DISP,
.ep = 0,
.num_links = 1,
.links = { MASTER_MMSYS }
};
static struct mtk_icc_node mm_vdec = {
.name = "mm-vdec",
.id = MASTER_MM_VDEC,
.ep = 0,
.num_links = 1,
.links = { MASTER_MMSYS }
};
static struct mtk_icc_node mm_venc = {
.name = "mm-venc",
.id = MASTER_MM_VENC,
.ep = 0,
.num_links = 1,
.links = { MASTER_MMSYS }
};
static struct mtk_icc_node mm_cam = {
.name = "mm-cam",
.id = MASTER_MM_CAM,
.ep = 0,
.num_links = 1,
.links = { MASTER_MMSYS }
};
static struct mtk_icc_node mm_img = {
.name = "mm-img",
.id = MASTER_MM_IMG,
.ep = 0,
.num_links = 1,
.links = { MASTER_MMSYS }
};
static struct mtk_icc_node mm_mdp = {
.name = "mm-mdp",
.id = MASTER_MM_MDP,
.ep = 0,
.num_links = 1,
.links = { MASTER_MMSYS }
};
static struct mtk_icc_node vpusys = {
.name = "vpusys",
.id = MASTER_VPUSYS,
.ep = 0,
.num_links = 1,
.links = { SLAVE_DDR_EMI }
};
static struct mtk_icc_node vpu_port0 = {
.name = "vpu-port0",
.id = MASTER_VPU_0,
.ep = 0,
.num_links = 1,
.links = { MASTER_VPUSYS }
};
static struct mtk_icc_node vpu_port1 = {
.name = "vpu-port1",
.id = MASTER_VPU_1,
.ep = 0,
.num_links = 1,
.links = { MASTER_VPUSYS }
};
static struct mtk_icc_node mdlasys = {
.name = "mdlasys",
.id = MASTER_MDLASYS,
.ep = 0,
.num_links = 1,
.links = { SLAVE_DDR_EMI }
};
static struct mtk_icc_node mdla_port0 = {
.name = "mdla-port0",
.id = MASTER_MDLA_0,
.ep = 0,
.num_links = 1,
.links = { MASTER_MDLASYS }
};
static struct mtk_icc_node ufs = {
.name = "ufs",
.id = MASTER_UFS,
.ep = 0,
.num_links = 1,
.links = { SLAVE_DDR_EMI }
};
static struct mtk_icc_node pcie = {
.name = "pcie",
.id = MASTER_PCIE,
.ep = 0,
.num_links = 1,
.links = { SLAVE_DDR_EMI }
};
static struct mtk_icc_node usb = {
.name = "usb",
.id = MASTER_USB,
.ep = 0,
.num_links = 1,
.links = { SLAVE_DDR_EMI }
};
static struct mtk_icc_node wifi = {
.name = "wifi",
.id = MASTER_WIFI,
.ep = 0,
.num_links = 1,
.links = { SLAVE_DDR_EMI }
};
static struct mtk_icc_node bt = {
.name = "bt",
.id = MASTER_BT,
.ep = 0,
.num_links = 1,
.links = { SLAVE_DDR_EMI }
};
static struct mtk_icc_node netsys = {
.name = "netsys",
.id = MASTER_NETSYS,
.ep = 0,
.num_links = 1,
.links = { SLAVE_DDR_EMI }
};
static struct mtk_icc_node dbgif = {
.name = "dbgif",
.id = MASTER_DBGIF,
.ep = 0,
.num_links = 1,
.links = { SLAVE_DDR_EMI }
};
static struct mtk_icc_node hrt_ddr_emi = {
.name = "hrt-ddr-emi",
.id = SLAVE_HRT_DDR_EMI,
.ep = 2,
};
static struct mtk_icc_node hrt_mmsys = {
.name = "hrt-mmsys",
.id = MASTER_HRT_MMSYS,
.ep = 0,
.num_links = 1,
.links = { SLAVE_HRT_DDR_EMI }
};
static struct mtk_icc_node hrt_mm_disp = {
.name = "hrt-mm-disp",
.id = MASTER_HRT_MM_DISP,
.ep = 0,
.num_links = 1,
.links = { MASTER_HRT_MMSYS }
};
static struct mtk_icc_node hrt_mm_vdec = {
.name = "hrt-mm-vdec",
.id = MASTER_HRT_MM_VDEC,
.ep = 0,
.num_links = 1,
.links = { MASTER_HRT_MMSYS }
};
static struct mtk_icc_node hrt_mm_venc = {
.name = "hrt-mm-venc",
.id = MASTER_HRT_MM_VENC,
.ep = 0,
.num_links = 1,
.links = { MASTER_HRT_MMSYS }
};
static struct mtk_icc_node hrt_mm_cam = {
.name = "hrt-mm-cam",
.id = MASTER_HRT_MM_CAM,
.ep = 0,
.num_links = 1,
.links = { MASTER_HRT_MMSYS }
};
static struct mtk_icc_node hrt_mm_img = {
.name = "hrt-mm-img",
.id = MASTER_HRT_MM_IMG,
.ep = 0,
.num_links = 1,
.links = { MASTER_HRT_MMSYS }
};
static struct mtk_icc_node hrt_mm_mdp = {
.name = "hrt-mm-mdp",
.id = MASTER_HRT_MM_MDP,
.ep = 0,
.num_links = 1,
.links = { MASTER_HRT_MMSYS }
};
static struct mtk_icc_node hrt_adsp = {
.name = "hrt-adsp",
.id = MASTER_HRT_ADSP,
.ep = 0,
.num_links = 1,
.links = { SLAVE_HRT_DDR_EMI }
};
static struct mtk_icc_node hrt_dbgif = {
.name = "hrt-dbgif",
.id = MASTER_HRT_DBGIF,
.ep = 0,
.num_links = 1,
.links = { SLAVE_HRT_DDR_EMI }
};
static struct mtk_icc_node *mt8196_emi_icc_nodes[] = {
[SLAVE_DDR_EMI] = &ddr_emi,
[MASTER_MCUSYS] = &mcusys,
[MASTER_MCU_0] = &mcu_port0,
[MASTER_MCU_1] = &mcu_port1,
[MASTER_MCU_2] = &mcu_port2,
[MASTER_MCU_3] = &mcu_port3,
[MASTER_MCU_4] = &mcu_port4,
[MASTER_GPUSYS] = &gpu,
[MASTER_MMSYS] = &mmsys,
[MASTER_MM_VPU] = &mm_vpu,
[MASTER_MM_DISP] = &mm_disp,
[MASTER_MM_VDEC] = &mm_vdec,
[MASTER_MM_VENC] = &mm_venc,
[MASTER_MM_CAM] = &mm_cam,
[MASTER_MM_IMG] = &mm_img,
[MASTER_MM_MDP] = &mm_mdp,
[MASTER_VPUSYS] = &vpusys,
[MASTER_VPU_0] = &vpu_port0,
[MASTER_VPU_1] = &vpu_port1,
[MASTER_MDLASYS] = &mdlasys,
[MASTER_MDLA_0] = &mdla_port0,
[MASTER_UFS] = &ufs,
[MASTER_PCIE] = &pcie,
[MASTER_USB] = &usb,
[MASTER_WIFI] = &wifi,
[MASTER_BT] = &bt,
[MASTER_NETSYS] = &netsys,
[MASTER_DBGIF] = &dbgif,
[SLAVE_HRT_DDR_EMI] = &hrt_ddr_emi,
[MASTER_HRT_MMSYS] = &hrt_mmsys,
[MASTER_HRT_MM_DISP] = &hrt_mm_disp,
[MASTER_HRT_MM_VDEC] = &hrt_mm_vdec,
[MASTER_HRT_MM_VENC] = &hrt_mm_venc,
[MASTER_HRT_MM_CAM] = &hrt_mm_cam,
[MASTER_HRT_MM_IMG] = &hrt_mm_img,
[MASTER_HRT_MM_MDP] = &hrt_mm_mdp,
[MASTER_HRT_ADSP] = &hrt_adsp,
[MASTER_HRT_DBGIF] = &hrt_dbgif
};
static struct mtk_icc_desc mt8196_emi_icc = {
.nodes = mt8196_emi_icc_nodes,
.num_nodes = ARRAY_SIZE(mt8196_emi_icc_nodes),
};
static const struct of_device_id mtk_mt8196_emi_icc_of_match[] = {
{ .compatible = "mediatek,mt8196-emi", .data = &mt8196_emi_icc },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, mtk_mt8196_emi_icc_of_match);
static struct platform_driver mtk_emi_icc_mt8196_driver = {
.driver = {
.name = "emi-icc-mt8196",
.of_match_table = mtk_mt8196_emi_icc_of_match,
.sync_state = icc_sync_state,
},
.probe = mtk_emi_icc_probe,
.remove = mtk_emi_icc_remove,
};
module_platform_driver(mtk_emi_icc_mt8196_driver);
MODULE_AUTHOR("AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>");
MODULE_DESCRIPTION("MediaTek MT8196 EMI ICC driver");
MODULE_LICENSE("GPL");
@@ -0,0 +1,462 @@
# SPDX-License-Identifier: GPL-2.0-only
config INTERCONNECT_QCOM
tristate "Qualcomm Network-on-Chip interconnect drivers"
depends on ARCH_QCOM
help
Support for Qualcomm's Network-on-Chip interconnect hardware.
config INTERCONNECT_QCOM_BCM_VOTER
tristate
config INTERCONNECT_QCOM_ELIZA
tristate "Qualcomm Eliza interconnect driver"
depends on INTERCONNECT_QCOM_RPMH_POSSIBLE
depends on ARM64 || COMPILE_TEST
select INTERCONNECT_QCOM_RPMH
select INTERCONNECT_QCOM_BCM_VOTER
help
This is a driver for the Qualcomm Network-on-Chip on Eliza-based
platforms.
config INTERCONNECT_QCOM_GLYMUR
tristate "Qualcomm Glymur interconnect driver"
depends on INTERCONNECT_QCOM_RPMH_POSSIBLE
depends on ARM64 || COMPILE_TEST
select INTERCONNECT_QCOM_RPMH
select INTERCONNECT_QCOM_BCM_VOTER
help
This is a driver for the Qualcomm Network-on-Chip on glymur-based
platforms.
config INTERCONNECT_QCOM_HAWI
tristate "Qualcomm HAWI interconnect driver"
depends on INTERCONNECT_QCOM_RPMH_POSSIBLE
select INTERCONNECT_QCOM_RPMH
select INTERCONNECT_QCOM_BCM_VOTER
help
This is a driver for the Qualcomm Network-on-Chip on hawi-based
platforms.
config INTERCONNECT_QCOM_KAANAPALI
tristate "Qualcomm Kaanapali interconnect driver"
depends on INTERCONNECT_QCOM_RPMH_POSSIBLE
depends on ARM64 || COMPILE_TEST
select INTERCONNECT_QCOM_RPMH
select INTERCONNECT_QCOM_BCM_VOTER
help
This is a driver for the Qualcomm Network-on-Chip on kaanapali-based
platforms.
config INTERCONNECT_QCOM_MSM8909
tristate "Qualcomm MSM8909 interconnect driver"
depends on INTERCONNECT_QCOM
depends on QCOM_SMD_RPM
depends on ARM || COMPILE_TEST
select INTERCONNECT_QCOM_SMD_RPM
help
This is a driver for the Qualcomm Network-on-Chip on msm8909-based
platforms.
config INTERCONNECT_QCOM_MSM8916
tristate "Qualcomm MSM8916 interconnect driver"
depends on INTERCONNECT_QCOM
depends on QCOM_SMD_RPM
select INTERCONNECT_QCOM_SMD_RPM
help
This is a driver for the Qualcomm Network-on-Chip on msm8916-based
platforms.
config INTERCONNECT_QCOM_MSM8937
tristate "Qualcomm MSM8937 interconnect driver"
depends on INTERCONNECT_QCOM
depends on QCOM_SMD_RPM
select INTERCONNECT_QCOM_SMD_RPM
help
This is a driver for the Qualcomm Network-on-Chip on msm8937-based
platforms.
config INTERCONNECT_QCOM_MSM8939
tristate "Qualcomm MSM8939 interconnect driver"
depends on INTERCONNECT_QCOM
depends on QCOM_SMD_RPM
select INTERCONNECT_QCOM_SMD_RPM
help
This is a driver for the Qualcomm Network-on-Chip on msm8939-based
platforms.
config INTERCONNECT_QCOM_MSM8953
tristate "Qualcomm MSM8953 interconnect driver"
depends on INTERCONNECT_QCOM
depends on QCOM_SMD_RPM
depends on ARM64 || COMPILE_TEST
select INTERCONNECT_QCOM_SMD_RPM
help
This is a driver for the Qualcomm Network-on-Chip on msm8953-based
platforms.
config INTERCONNECT_QCOM_MSM8974
tristate "Qualcomm MSM8974 interconnect driver"
depends on INTERCONNECT_QCOM
depends on QCOM_SMD_RPM
depends on ARM || COMPILE_TEST
select INTERCONNECT_QCOM_SMD_RPM
help
This is a driver for the Qualcomm Network-on-Chip on msm8974-based
platforms.
config INTERCONNECT_QCOM_MSM8976
tristate "Qualcomm MSM8976 interconnect driver"
depends on INTERCONNECT_QCOM
depends on QCOM_SMD_RPM
depends on ARM64 || COMPILE_TEST
select INTERCONNECT_QCOM_SMD_RPM
help
This is a driver for the Qualcomm Network-on-Chip on msm8976-based
platforms.
config INTERCONNECT_QCOM_MSM8996
tristate "Qualcomm MSM8996 interconnect driver"
depends on INTERCONNECT_QCOM
depends on QCOM_SMD_RPM
depends on ARM64 || COMPILE_TEST
select INTERCONNECT_QCOM_SMD_RPM
help
This is a driver for the Qualcomm Network-on-Chip on msm8996-based
platforms.
config INTERCONNECT_QCOM_NORD
tristate "Qualcomm Nord interconnect driver"
depends on INTERCONNECT_QCOM_RPMH_POSSIBLE
select INTERCONNECT_QCOM_RPMH
select INTERCONNECT_QCOM_BCM_VOTER
help
This is a driver for the Qualcomm Network-on-Chip on Nord-based
platforms. The topology consists of several NoCs controlled by
the RPMh hardware and communicates via Bus Clock Manager (BCM)
through the Resource State Coordinator (RSC).
config INTERCONNECT_QCOM_OSM_L3
tristate "Qualcomm OSM L3 interconnect driver"
depends on INTERCONNECT_QCOM || COMPILE_TEST
depends on ARM64 || COMPILE_TEST
help
Say y here to support the Operating State Manager (OSM) interconnect
driver which controls the scaling of L3 caches on Qualcomm SoCs.
config INTERCONNECT_QCOM_QCM2290
tristate "Qualcomm QCM2290 interconnect driver"
depends on INTERCONNECT_QCOM
depends on QCOM_SMD_RPM
depends on ARM64 || COMPILE_TEST
select INTERCONNECT_QCOM_SMD_RPM
help
This is a driver for the Qualcomm Network-on-Chip on qcm2290-based
platforms.
config INTERCONNECT_QCOM_QCS404
tristate "Qualcomm QCS404 interconnect driver"
depends on INTERCONNECT_QCOM
depends on QCOM_SMD_RPM
depends on ARM64 || COMPILE_TEST
select INTERCONNECT_QCOM_SMD_RPM
help
This is a driver for the Qualcomm Network-on-Chip on qcs404-based
platforms.
config INTERCONNECT_QCOM_QCS615
tristate "Qualcomm QCS615 interconnect driver"
depends on INTERCONNECT_QCOM_RPMH_POSSIBLE
depends on ARM64 || COMPILE_TEST
select INTERCONNECT_QCOM_RPMH
select INTERCONNECT_QCOM_BCM_VOTER
help
This is a driver for the Qualcomm Network-on-Chip on qcs615-based
platforms.
config INTERCONNECT_QCOM_QCS8300
tristate "Qualcomm QCS8300 interconnect driver"
depends on INTERCONNECT_QCOM_RPMH_POSSIBLE
depends on ARM64 || COMPILE_TEST
select INTERCONNECT_QCOM_RPMH
select INTERCONNECT_QCOM_BCM_VOTER
help
This is a driver for the Qualcomm Technologies, Inc. Network-on-Chip
on QCS8300-based platforms. The interconnect provider collects and
aggreagates the cosumer bandwidth requests to satisfy constraints
placed on Network-on-Chip performance states.
config INTERCONNECT_QCOM_QDU1000
tristate "Qualcomm QDU1000/QRU1000 interconnect driver"
depends on INTERCONNECT_QCOM_RPMH_POSSIBLE
depends on ARM64 || COMPILE_TEST
select INTERCONNECT_QCOM_RPMH
select INTERCONNECT_QCOM_BCM_VOTER
help
This is a driver for the Qualcomm Network-on-Chip on QDU1000-based
and QRU1000-based platforms.
config INTERCONNECT_QCOM_RPMH_POSSIBLE
tristate
default INTERCONNECT_QCOM
depends on QCOM_RPMH || (COMPILE_TEST && !QCOM_RPMH)
depends on QCOM_COMMAND_DB || (COMPILE_TEST && !QCOM_COMMAND_DB)
depends on OF
help
Compile-testing RPMH drivers is possible on other platforms,
but in order to avoid link failures, drivers must not be built-in
when QCOM_RPMH or QCOM_COMMAND_DB are loadable modules
config INTERCONNECT_QCOM_RPMH
tristate
config INTERCONNECT_QCOM_SA8775P
tristate "Qualcomm SA8775P interconnect driver"
depends on INTERCONNECT_QCOM_RPMH_POSSIBLE
depends on ARM64 || COMPILE_TEST
select INTERCONNECT_QCOM_RPMH
select INTERCONNECT_QCOM_BCM_VOTER
help
This is a driver for the Qualcomm Network-on-Chip on sa8775p-based
platforms.
config INTERCONNECT_QCOM_SAR2130P
tristate "Qualcomm SAR2130P interconnect driver"
depends on INTERCONNECT_QCOM_RPMH_POSSIBLE
depends on ARM64 || COMPILE_TEST
select INTERCONNECT_QCOM_RPMH
select INTERCONNECT_QCOM_BCM_VOTER
help
This is a driver for the Qualcomm Network-on-Chip on SAR2130P-based
platforms.
config INTERCONNECT_QCOM_SC7180
tristate "Qualcomm SC7180 interconnect driver"
depends on INTERCONNECT_QCOM_RPMH_POSSIBLE
depends on ARM64 || COMPILE_TEST
select INTERCONNECT_QCOM_RPMH
select INTERCONNECT_QCOM_BCM_VOTER
help
This is a driver for the Qualcomm Network-on-Chip on sc7180-based
platforms.
config INTERCONNECT_QCOM_SC7280
tristate "Qualcomm SC7280 interconnect driver"
depends on INTERCONNECT_QCOM_RPMH_POSSIBLE
depends on ARM64 || COMPILE_TEST
select INTERCONNECT_QCOM_RPMH
select INTERCONNECT_QCOM_BCM_VOTER
help
This is a driver for the Qualcomm Network-on-Chip on sc7280-based
platforms.
config INTERCONNECT_QCOM_SC8180X
tristate "Qualcomm SC8180X interconnect driver"
depends on INTERCONNECT_QCOM_RPMH_POSSIBLE
depends on ARM64 || COMPILE_TEST
select INTERCONNECT_QCOM_RPMH
select INTERCONNECT_QCOM_BCM_VOTER
help
This is a driver for the Qualcomm Network-on-Chip on sc8180x-based
platforms.
config INTERCONNECT_QCOM_SC8280XP
tristate "Qualcomm SC8280XP interconnect driver"
depends on INTERCONNECT_QCOM_RPMH_POSSIBLE
depends on ARM64 || COMPILE_TEST
select INTERCONNECT_QCOM_RPMH
select INTERCONNECT_QCOM_BCM_VOTER
help
This is a driver for the Qualcomm Network-on-Chip on SC8280XP-based
platforms.
config INTERCONNECT_QCOM_SDM660
tristate "Qualcomm SDM660 interconnect driver"
depends on INTERCONNECT_QCOM
depends on ARM64 || COMPILE_TEST
depends on QCOM_SMD_RPM
select INTERCONNECT_QCOM_SMD_RPM
help
This is a driver for the Qualcomm Network-on-Chip on sdm660-based
platforms.
config INTERCONNECT_QCOM_SDM670
tristate "Qualcomm SDM670 interconnect driver"
depends on INTERCONNECT_QCOM_RPMH_POSSIBLE
depends on ARM64 || COMPILE_TEST
select INTERCONNECT_QCOM_RPMH
select INTERCONNECT_QCOM_BCM_VOTER
help
This is a driver for the Qualcomm Network-on-Chip on sdm670-based
platforms.
config INTERCONNECT_QCOM_SDM845
tristate "Qualcomm SDM845 interconnect driver"
depends on INTERCONNECT_QCOM_RPMH_POSSIBLE
depends on ARM64 || COMPILE_TEST
select INTERCONNECT_QCOM_RPMH
select INTERCONNECT_QCOM_BCM_VOTER
help
This is a driver for the Qualcomm Network-on-Chip on sdm845-based
platforms.
config INTERCONNECT_QCOM_SDX55
tristate "Qualcomm SDX55 interconnect driver"
depends on INTERCONNECT_QCOM_RPMH_POSSIBLE
depends on ARM || COMPILE_TEST
select INTERCONNECT_QCOM_RPMH
select INTERCONNECT_QCOM_BCM_VOTER
help
This is a driver for the Qualcomm Network-on-Chip on sdx55-based
platforms.
config INTERCONNECT_QCOM_SDX65
tristate "Qualcomm SDX65 interconnect driver"
depends on INTERCONNECT_QCOM_RPMH_POSSIBLE
depends on ARM || COMPILE_TEST
select INTERCONNECT_QCOM_RPMH
select INTERCONNECT_QCOM_BCM_VOTER
help
This is a driver for the Qualcomm Network-on-Chip on sdx65-based
platforms.
config INTERCONNECT_QCOM_SDX75
tristate "Qualcomm SDX75 interconnect driver"
depends on INTERCONNECT_QCOM_RPMH_POSSIBLE
depends on ARM64 || COMPILE_TEST
select INTERCONNECT_QCOM_RPMH
select INTERCONNECT_QCOM_BCM_VOTER
help
This is a driver for the Qualcomm Network-on-Chip on sdx75-based
platforms.
config INTERCONNECT_QCOM_SHIKRA
tristate "Qualcomm SHIKRA interconnect driver"
depends on INTERCONNECT_QCOM
depends on QCOM_SMD_RPM
select INTERCONNECT_QCOM_SMD_RPM
help
This is a driver for the Qualcomm Network-on-Chip on shikra-based
platforms.
config INTERCONNECT_QCOM_SM6115
tristate "Qualcomm SM6115 interconnect driver"
depends on INTERCONNECT_QCOM
depends on QCOM_SMD_RPM
depends on ARM64 || COMPILE_TEST
select INTERCONNECT_QCOM_SMD_RPM
help
This is a driver for the Qualcomm Network-on-Chip on sm6115-based
platforms.
config INTERCONNECT_QCOM_SM6350
tristate "Qualcomm SM6350 interconnect driver"
depends on INTERCONNECT_QCOM_RPMH_POSSIBLE
depends on ARM64 || COMPILE_TEST
select INTERCONNECT_QCOM_RPMH
select INTERCONNECT_QCOM_BCM_VOTER
help
This is a driver for the Qualcomm Network-on-Chip on sm6350-based
platforms.
config INTERCONNECT_QCOM_SM7150
tristate "Qualcomm SM7150 interconnect driver"
depends on INTERCONNECT_QCOM_RPMH_POSSIBLE
depends on ARM64 || COMPILE_TEST
select INTERCONNECT_QCOM_RPMH
select INTERCONNECT_QCOM_BCM_VOTER
help
This is a driver for the Qualcomm Network-on-Chip on sm7150-based
platforms.
config INTERCONNECT_QCOM_MILOS
tristate "Qualcomm Milos interconnect driver"
depends on INTERCONNECT_QCOM_RPMH_POSSIBLE
depends on ARM64 || COMPILE_TEST
select INTERCONNECT_QCOM_RPMH
select INTERCONNECT_QCOM_BCM_VOTER
help
This is a driver for the Qualcomm Network-on-Chip on Milos-based
platforms.
config INTERCONNECT_QCOM_SM8150
tristate "Qualcomm SM8150 interconnect driver"
depends on INTERCONNECT_QCOM_RPMH_POSSIBLE
depends on ARM64 || COMPILE_TEST
select INTERCONNECT_QCOM_RPMH
select INTERCONNECT_QCOM_BCM_VOTER
help
This is a driver for the Qualcomm Network-on-Chip on sm8150-based
platforms.
config INTERCONNECT_QCOM_SM8250
tristate "Qualcomm SM8250 interconnect driver"
depends on INTERCONNECT_QCOM_RPMH_POSSIBLE
depends on ARM64 || COMPILE_TEST
select INTERCONNECT_QCOM_RPMH
select INTERCONNECT_QCOM_BCM_VOTER
help
This is a driver for the Qualcomm Network-on-Chip on sm8250-based
platforms.
config INTERCONNECT_QCOM_SM8350
tristate "Qualcomm SM8350 interconnect driver"
depends on INTERCONNECT_QCOM_RPMH_POSSIBLE
depends on ARM64 || COMPILE_TEST
select INTERCONNECT_QCOM_RPMH
select INTERCONNECT_QCOM_BCM_VOTER
help
This is a driver for the Qualcomm Network-on-Chip on SM8350-based
platforms.
config INTERCONNECT_QCOM_SM8450
tristate "Qualcomm SM8450 interconnect driver"
depends on INTERCONNECT_QCOM_RPMH_POSSIBLE
depends on ARM64 || COMPILE_TEST
select INTERCONNECT_QCOM_RPMH
select INTERCONNECT_QCOM_BCM_VOTER
help
This is a driver for the Qualcomm Network-on-Chip on SM8450-based
platforms.
config INTERCONNECT_QCOM_SM8550
tristate "Qualcomm SM8550 interconnect driver"
depends on INTERCONNECT_QCOM_RPMH_POSSIBLE
depends on ARM64 || COMPILE_TEST
select INTERCONNECT_QCOM_RPMH
select INTERCONNECT_QCOM_BCM_VOTER
help
This is a driver for the Qualcomm Network-on-Chip on SM8550-based
platforms.
config INTERCONNECT_QCOM_SM8650
tristate "Qualcomm SM8650 interconnect driver"
depends on INTERCONNECT_QCOM_RPMH_POSSIBLE
depends on ARM64 || COMPILE_TEST
select INTERCONNECT_QCOM_RPMH
select INTERCONNECT_QCOM_BCM_VOTER
help
This is a driver for the Qualcomm Network-on-Chip on SM8650-based
platforms.
config INTERCONNECT_QCOM_SM8750
tristate "Qualcomm SM8750 interconnect driver"
depends on INTERCONNECT_QCOM_RPMH_POSSIBLE
depends on ARM64 || COMPILE_TEST
select INTERCONNECT_QCOM_RPMH
select INTERCONNECT_QCOM_BCM_VOTER
help
This is a driver for the Qualcomm Network-on-Chip on SM8750-based
platforms.
config INTERCONNECT_QCOM_X1E80100
tristate "Qualcomm X1E80100 interconnect driver"
depends on INTERCONNECT_QCOM_RPMH_POSSIBLE
depends on ARM64 || COMPILE_TEST
select INTERCONNECT_QCOM_RPMH
select INTERCONNECT_QCOM_BCM_VOTER
help
This is a driver for the Qualcomm Network-on-Chip on X1E80100-based
platforms.
config INTERCONNECT_QCOM_SMD_RPM
tristate
@@ -0,0 +1,100 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_INTERCONNECT_QCOM) += interconnect_qcom.o
interconnect_qcom-y := icc-common.o
icc-bcm-voter-objs := bcm-voter.o
qnoc-eliza-objs := eliza.o
qnoc-glymur-objs := glymur.o
qnoc-hawi-objs := hawi.o
qnoc-kaanapali-objs := kaanapali.o
qnoc-milos-objs := milos.o
qnoc-msm8909-objs := msm8909.o
qnoc-msm8916-objs := msm8916.o
qnoc-msm8937-objs := msm8937.o
qnoc-msm8939-objs := msm8939.o
qnoc-msm8953-objs := msm8953.o
qnoc-msm8974-objs := msm8974.o
qnoc-msm8976-objs := msm8976.o
qnoc-msm8996-objs := msm8996.o
qnoc-nord-objs := nord.o
icc-osm-l3-objs := osm-l3.o
qnoc-qcm2290-objs := qcm2290.o
qnoc-qcs404-objs := qcs404.o
qnoc-qcs615-objs := qcs615.o
qnoc-qcs8300-objs := qcs8300.o
qnoc-qdu1000-objs := qdu1000.o
icc-rpmh-obj := icc-rpmh.o
qnoc-sa8775p-objs := sa8775p.o
qnoc-sar2130p-objs := sar2130p.o
qnoc-sc7180-objs := sc7180.o
qnoc-sc7280-objs := sc7280.o
qnoc-sc8180x-objs := sc8180x.o
qnoc-sc8280xp-objs := sc8280xp.o
qnoc-sdm660-objs := sdm660.o
qnoc-sdm670-objs := sdm670.o
qnoc-sdm845-objs := sdm845.o
qnoc-sdx55-objs := sdx55.o
qnoc-sdx65-objs := sdx65.o
qnoc-sdx75-objs := sdx75.o
qnoc-shikra-objs := shikra.o
qnoc-sm6115-objs := sm6115.o
qnoc-sm6350-objs := sm6350.o
qnoc-sm7150-objs := sm7150.o
qnoc-sm8150-objs := sm8150.o
qnoc-sm8250-objs := sm8250.o
qnoc-sm8350-objs := sm8350.o
qnoc-sm8450-objs := sm8450.o
qnoc-sm8550-objs := sm8550.o
qnoc-sm8650-objs := sm8650.o
qnoc-sm8750-objs := sm8750.o
qnoc-x1e80100-objs := x1e80100.o
icc-smd-rpm-objs := smd-rpm.o icc-rpm.o icc-rpm-clocks.o
obj-$(CONFIG_INTERCONNECT_QCOM_BCM_VOTER) += icc-bcm-voter.o
obj-$(CONFIG_INTERCONNECT_QCOM_ELIZA) += qnoc-eliza.o
obj-$(CONFIG_INTERCONNECT_QCOM_GLYMUR) += qnoc-glymur.o
obj-$(CONFIG_INTERCONNECT_QCOM_HAWI) += qnoc-hawi.o
obj-$(CONFIG_INTERCONNECT_QCOM_KAANAPALI) += qnoc-kaanapali.o
obj-$(CONFIG_INTERCONNECT_QCOM_MILOS) += qnoc-milos.o
obj-$(CONFIG_INTERCONNECT_QCOM_MSM8909) += qnoc-msm8909.o
obj-$(CONFIG_INTERCONNECT_QCOM_MSM8916) += qnoc-msm8916.o
obj-$(CONFIG_INTERCONNECT_QCOM_MSM8937) += qnoc-msm8937.o
obj-$(CONFIG_INTERCONNECT_QCOM_MSM8939) += qnoc-msm8939.o
obj-$(CONFIG_INTERCONNECT_QCOM_MSM8953) += qnoc-msm8953.o
obj-$(CONFIG_INTERCONNECT_QCOM_MSM8974) += qnoc-msm8974.o
obj-$(CONFIG_INTERCONNECT_QCOM_MSM8976) += qnoc-msm8976.o
obj-$(CONFIG_INTERCONNECT_QCOM_MSM8996) += qnoc-msm8996.o
obj-$(CONFIG_INTERCONNECT_QCOM_NORD) += qnoc-nord.o
obj-$(CONFIG_INTERCONNECT_QCOM_OSM_L3) += icc-osm-l3.o
obj-$(CONFIG_INTERCONNECT_QCOM_QCM2290) += qnoc-qcm2290.o
obj-$(CONFIG_INTERCONNECT_QCOM_QCS404) += qnoc-qcs404.o
obj-$(CONFIG_INTERCONNECT_QCOM_QCS615) += qnoc-qcs615.o
obj-$(CONFIG_INTERCONNECT_QCOM_QCS8300) += qnoc-qcs8300.o
obj-$(CONFIG_INTERCONNECT_QCOM_QDU1000) += qnoc-qdu1000.o
obj-$(CONFIG_INTERCONNECT_QCOM_RPMH) += icc-rpmh.o
obj-$(CONFIG_INTERCONNECT_QCOM_SA8775P) += qnoc-sa8775p.o
obj-$(CONFIG_INTERCONNECT_QCOM_SAR2130P) += qnoc-sar2130p.o
obj-$(CONFIG_INTERCONNECT_QCOM_SC7180) += qnoc-sc7180.o
obj-$(CONFIG_INTERCONNECT_QCOM_SC7280) += qnoc-sc7280.o
obj-$(CONFIG_INTERCONNECT_QCOM_SC8180X) += qnoc-sc8180x.o
obj-$(CONFIG_INTERCONNECT_QCOM_SC8280XP) += qnoc-sc8280xp.o
obj-$(CONFIG_INTERCONNECT_QCOM_SDM660) += qnoc-sdm660.o
obj-$(CONFIG_INTERCONNECT_QCOM_SDM670) += qnoc-sdm670.o
obj-$(CONFIG_INTERCONNECT_QCOM_SDM845) += qnoc-sdm845.o
obj-$(CONFIG_INTERCONNECT_QCOM_SDX55) += qnoc-sdx55.o
obj-$(CONFIG_INTERCONNECT_QCOM_SDX65) += qnoc-sdx65.o
obj-$(CONFIG_INTERCONNECT_QCOM_SDX75) += qnoc-sdx75.o
obj-$(CONFIG_INTERCONNECT_QCOM_SHIKRA) += qnoc-shikra.o
obj-$(CONFIG_INTERCONNECT_QCOM_SM6115) += qnoc-sm6115.o
obj-$(CONFIG_INTERCONNECT_QCOM_SM6350) += qnoc-sm6350.o
obj-$(CONFIG_INTERCONNECT_QCOM_SM7150) += qnoc-sm7150.o
obj-$(CONFIG_INTERCONNECT_QCOM_SM8150) += qnoc-sm8150.o
obj-$(CONFIG_INTERCONNECT_QCOM_SM8250) += qnoc-sm8250.o
obj-$(CONFIG_INTERCONNECT_QCOM_SM8350) += qnoc-sm8350.o
obj-$(CONFIG_INTERCONNECT_QCOM_SM8450) += qnoc-sm8450.o
obj-$(CONFIG_INTERCONNECT_QCOM_SM8550) += qnoc-sm8550.o
obj-$(CONFIG_INTERCONNECT_QCOM_SM8650) += qnoc-sm8650.o
obj-$(CONFIG_INTERCONNECT_QCOM_SM8750) += qnoc-sm8750.o
obj-$(CONFIG_INTERCONNECT_QCOM_X1E80100) += qnoc-x1e80100.o
obj-$(CONFIG_INTERCONNECT_QCOM_SMD_RPM) += icc-smd-rpm.o
@@ -0,0 +1,413 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
*/
#include <asm/div64.h>
#include <linux/interconnect-provider.h>
#include <linux/list_sort.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <soc/qcom/rpmh.h>
#include <soc/qcom/tcs.h>
#include "bcm-voter.h"
#include "icc-rpmh.h"
static LIST_HEAD(bcm_voters);
static DEFINE_MUTEX(bcm_voter_lock);
/**
* struct bcm_voter - Bus Clock Manager voter
* @dev: reference to the device that communicates with the BCM
* @np: reference to the device node to match bcm voters
* @lock: mutex to protect commit and wake/sleep lists in the voter
* @commit_list: list containing bcms to be committed to hardware
* @ws_list: list containing bcms that have different wake/sleep votes
* @voter_node: list of bcm voters
* @tcs_wait: mask for which buckets require TCS completion
*/
struct bcm_voter {
struct device *dev;
struct device_node *np;
struct mutex lock;
struct list_head commit_list;
struct list_head ws_list;
struct list_head voter_node;
u32 tcs_wait;
};
static int cmp_vcd(void *priv, const struct list_head *a, const struct list_head *b)
{
const struct qcom_icc_bcm *bcm_a = list_entry(a, struct qcom_icc_bcm, list);
const struct qcom_icc_bcm *bcm_b = list_entry(b, struct qcom_icc_bcm, list);
return bcm_a->aux_data.vcd - bcm_b->aux_data.vcd;
}
static u64 bcm_div(u64 num, u32 base)
{
/* Ensure that small votes aren't lost. */
if (num && num < base)
return 1;
do_div(num, base);
return num;
}
/* BCMs with enable_mask use one-hot-encoding for on/off signaling */
static void bcm_aggregate_mask(struct qcom_icc_bcm *bcm)
{
struct qcom_icc_node *node;
int bucket, i;
for (bucket = 0; bucket < QCOM_ICC_NUM_BUCKETS; bucket++) {
bcm->vote_x[bucket] = 0;
bcm->vote_y[bucket] = 0;
for (i = 0; i < bcm->num_nodes; i++) {
node = bcm->nodes[i];
/* If any vote in this bucket exists, keep the BCM enabled */
if (node->sum_avg[bucket] || node->max_peak[bucket]) {
bcm->vote_x[bucket] = 0;
bcm->vote_y[bucket] = bcm->enable_mask;
break;
}
}
}
if (bcm->keepalive) {
bcm->vote_x[QCOM_ICC_BUCKET_AMC] = bcm->enable_mask;
bcm->vote_x[QCOM_ICC_BUCKET_WAKE] = bcm->enable_mask;
bcm->vote_y[QCOM_ICC_BUCKET_AMC] = bcm->enable_mask;
bcm->vote_y[QCOM_ICC_BUCKET_WAKE] = bcm->enable_mask;
}
}
static void bcm_aggregate(struct qcom_icc_bcm *bcm)
{
struct qcom_icc_node *node;
size_t i, bucket;
u64 agg_avg[QCOM_ICC_NUM_BUCKETS] = {0};
u64 agg_peak[QCOM_ICC_NUM_BUCKETS] = {0};
u64 temp;
for (bucket = 0; bucket < QCOM_ICC_NUM_BUCKETS; bucket++) {
for (i = 0; i < bcm->num_nodes; i++) {
node = bcm->nodes[i];
temp = bcm_div(node->sum_avg[bucket] * bcm->aux_data.width,
node->buswidth * node->channels);
agg_avg[bucket] = max(agg_avg[bucket], temp);
temp = bcm_div(node->max_peak[bucket] * bcm->aux_data.width,
node->buswidth);
agg_peak[bucket] = max(agg_peak[bucket], temp);
}
temp = agg_avg[bucket] * bcm->vote_scale;
bcm->vote_x[bucket] = bcm_div(temp, bcm->aux_data.unit);
temp = agg_peak[bucket] * bcm->vote_scale;
bcm->vote_y[bucket] = bcm_div(temp, bcm->aux_data.unit);
}
if (bcm->keepalive && bcm->vote_x[QCOM_ICC_BUCKET_AMC] == 0 &&
bcm->vote_y[QCOM_ICC_BUCKET_AMC] == 0) {
bcm->vote_x[QCOM_ICC_BUCKET_AMC] = 1;
bcm->vote_x[QCOM_ICC_BUCKET_WAKE] = 1;
bcm->vote_y[QCOM_ICC_BUCKET_AMC] = 1;
bcm->vote_y[QCOM_ICC_BUCKET_WAKE] = 1;
}
}
static inline void tcs_cmd_gen(struct tcs_cmd *cmd, u64 vote_x, u64 vote_y,
u32 addr, bool commit, bool wait)
{
bool valid = true;
if (!cmd)
return;
memset(cmd, 0, sizeof(*cmd));
if (vote_x == 0 && vote_y == 0)
valid = false;
if (vote_x > BCM_TCS_CMD_VOTE_MASK)
vote_x = BCM_TCS_CMD_VOTE_MASK;
if (vote_y > BCM_TCS_CMD_VOTE_MASK)
vote_y = BCM_TCS_CMD_VOTE_MASK;
cmd->addr = addr;
cmd->data = BCM_TCS_CMD(commit, valid, vote_x, vote_y);
/*
* Set the wait for completion flag on command that need to be completed
* before the next command.
*/
cmd->wait = wait;
}
static void tcs_list_gen(struct bcm_voter *voter, int bucket,
struct tcs_cmd tcs_list[MAX_VCD],
int n[MAX_VCD + 1])
{
struct list_head *bcm_list = &voter->commit_list;
struct qcom_icc_bcm *bcm;
bool commit, wait;
size_t idx = 0, batch = 0, cur_vcd_size = 0;
memset(n, 0, sizeof(int) * (MAX_VCD + 1));
list_for_each_entry(bcm, bcm_list, list) {
commit = false;
cur_vcd_size++;
if ((list_is_last(&bcm->list, bcm_list)) ||
bcm->aux_data.vcd != list_next_entry(bcm, list)->aux_data.vcd) {
commit = true;
cur_vcd_size = 0;
}
wait = commit && (voter->tcs_wait & BIT(bucket));
tcs_cmd_gen(&tcs_list[idx], bcm->vote_x[bucket],
bcm->vote_y[bucket], bcm->addr, commit, wait);
idx++;
n[batch]++;
/*
* Batch the BCMs in such a way that we do not split them in
* multiple payloads when they are under the same VCD. This is
* to ensure that every BCM is committed since we only set the
* commit bit on the last BCM request of every VCD.
*/
if (n[batch] >= MAX_RPMH_PAYLOAD) {
if (!commit) {
n[batch] -= cur_vcd_size;
n[batch + 1] = cur_vcd_size;
}
batch++;
}
}
}
/**
* of_bcm_voter_get - gets a bcm voter handle from DT node
* @dev: device pointer for the consumer device
* @name: name for the bcm voter device
*
* This function will match a device_node pointer for the phandle
* specified in the device DT and return a bcm_voter handle on success.
*
* Returns bcm_voter pointer or ERR_PTR() on error. EPROBE_DEFER is returned
* when matching bcm voter is yet to be found.
*/
struct bcm_voter *of_bcm_voter_get(struct device *dev, const char *name)
{
struct bcm_voter *voter = ERR_PTR(-EPROBE_DEFER);
struct bcm_voter *temp;
struct device_node *np, *node;
int idx = 0;
if (!dev || !dev->of_node)
return ERR_PTR(-ENODEV);
np = dev->of_node;
if (name) {
idx = of_property_match_string(np, "qcom,bcm-voter-names", name);
if (idx < 0)
return ERR_PTR(idx);
}
node = of_parse_phandle(np, "qcom,bcm-voters", idx);
mutex_lock(&bcm_voter_lock);
list_for_each_entry(temp, &bcm_voters, voter_node) {
if (temp->np == node) {
voter = temp;
break;
}
}
mutex_unlock(&bcm_voter_lock);
of_node_put(node);
return voter;
}
EXPORT_SYMBOL_GPL(of_bcm_voter_get);
/**
* qcom_icc_bcm_voter_add - queues up the bcm nodes that require updates
* @voter: voter that the bcms are being added to
* @bcm: bcm to add to the commit and wake sleep list
*/
void qcom_icc_bcm_voter_add(struct bcm_voter *voter, struct qcom_icc_bcm *bcm)
{
if (!voter)
return;
mutex_lock(&voter->lock);
if (list_empty(&bcm->list))
list_add_tail(&bcm->list, &voter->commit_list);
if (list_empty(&bcm->ws_list))
list_add_tail(&bcm->ws_list, &voter->ws_list);
mutex_unlock(&voter->lock);
}
EXPORT_SYMBOL_GPL(qcom_icc_bcm_voter_add);
/**
* qcom_icc_bcm_voter_commit - generates and commits tcs cmds based on bcms
* @voter: voter that needs flushing
*
* This function generates a set of AMC commands and flushes to the BCM device
* associated with the voter. It conditionally generate WAKE and SLEEP commands
* based on deltas between WAKE/SLEEP requirements. The ws_list persists
* through multiple commit requests and bcm nodes are removed only when the
* requirements for WAKE matches SLEEP.
*
* Returns 0 on success, or an appropriate error code otherwise.
*/
int qcom_icc_bcm_voter_commit(struct bcm_voter *voter)
{
struct qcom_icc_bcm *bcm;
struct qcom_icc_bcm *bcm_tmp;
int commit_idx[MAX_VCD + 1];
struct tcs_cmd cmds[MAX_BCMS];
int ret = 0;
if (!voter)
return 0;
mutex_lock(&voter->lock);
list_for_each_entry(bcm, &voter->commit_list, list) {
if (bcm->enable_mask)
bcm_aggregate_mask(bcm);
else
bcm_aggregate(bcm);
}
/*
* Pre sort the BCMs based on VCD for ease of generating a command list
* that groups the BCMs with the same VCD together. VCDs are numbered
* with lowest being the most expensive time wise, ensuring that
* those commands are being sent the earliest in the queue. This needs
* to be sorted every commit since we can't guarantee the order in which
* the BCMs are added to the list.
*/
list_sort(NULL, &voter->commit_list, cmp_vcd);
/*
* Construct the command list based on a pre ordered list of BCMs
* based on VCD.
*/
tcs_list_gen(voter, QCOM_ICC_BUCKET_AMC, cmds, commit_idx);
if (!commit_idx[0])
goto out;
rpmh_invalidate(voter->dev);
ret = rpmh_write_batch(voter->dev, RPMH_ACTIVE_ONLY_STATE,
cmds, commit_idx);
if (ret) {
pr_err("Error sending AMC RPMH requests (%d)\n", ret);
goto out;
}
list_for_each_entry_safe(bcm, bcm_tmp, &voter->commit_list, list)
list_del_init(&bcm->list);
list_for_each_entry_safe(bcm, bcm_tmp, &voter->ws_list, ws_list) {
/*
* Only generate WAKE and SLEEP commands if a resource's
* requirements change as the execution environment transitions
* between different power states.
*/
if (bcm->vote_x[QCOM_ICC_BUCKET_WAKE] !=
bcm->vote_x[QCOM_ICC_BUCKET_SLEEP] ||
bcm->vote_y[QCOM_ICC_BUCKET_WAKE] !=
bcm->vote_y[QCOM_ICC_BUCKET_SLEEP])
list_add_tail(&bcm->list, &voter->commit_list);
else
list_del_init(&bcm->ws_list);
}
if (list_empty(&voter->commit_list))
goto out;
list_sort(NULL, &voter->commit_list, cmp_vcd);
tcs_list_gen(voter, QCOM_ICC_BUCKET_WAKE, cmds, commit_idx);
ret = rpmh_write_batch(voter->dev, RPMH_WAKE_ONLY_STATE, cmds, commit_idx);
if (ret) {
pr_err("Error sending WAKE RPMH requests (%d)\n", ret);
goto out;
}
tcs_list_gen(voter, QCOM_ICC_BUCKET_SLEEP, cmds, commit_idx);
ret = rpmh_write_batch(voter->dev, RPMH_SLEEP_STATE, cmds, commit_idx);
if (ret) {
pr_err("Error sending SLEEP RPMH requests (%d)\n", ret);
goto out;
}
out:
list_for_each_entry_safe(bcm, bcm_tmp, &voter->commit_list, list)
list_del_init(&bcm->list);
mutex_unlock(&voter->lock);
return ret;
}
EXPORT_SYMBOL_GPL(qcom_icc_bcm_voter_commit);
static int qcom_icc_bcm_voter_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct bcm_voter *voter;
voter = devm_kzalloc(&pdev->dev, sizeof(*voter), GFP_KERNEL);
if (!voter)
return -ENOMEM;
voter->dev = &pdev->dev;
voter->np = np;
if (of_property_read_u32(np, "qcom,tcs-wait", &voter->tcs_wait))
voter->tcs_wait = QCOM_ICC_TAG_ACTIVE_ONLY;
mutex_init(&voter->lock);
INIT_LIST_HEAD(&voter->commit_list);
INIT_LIST_HEAD(&voter->ws_list);
mutex_lock(&bcm_voter_lock);
list_add_tail(&voter->voter_node, &bcm_voters);
mutex_unlock(&bcm_voter_lock);
return 0;
}
static const struct of_device_id bcm_voter_of_match[] = {
{ .compatible = "qcom,bcm-voter" },
{ }
};
MODULE_DEVICE_TABLE(of, bcm_voter_of_match);
static struct platform_driver qcom_icc_bcm_voter_driver = {
.probe = qcom_icc_bcm_voter_probe,
.driver = {
.name = "bcm_voter",
.of_match_table = bcm_voter_of_match,
},
};
module_platform_driver(qcom_icc_bcm_voter_driver);
MODULE_AUTHOR("David Dai <daidavid1@codeaurora.org>");
MODULE_DESCRIPTION("Qualcomm BCM Voter interconnect driver");
MODULE_LICENSE("GPL v2");
@@ -0,0 +1,19 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2020, The Linux Foundation. All rights reserved.
*/
#ifndef __DRIVERS_INTERCONNECT_QCOM_BCM_VOTER_H__
#define __DRIVERS_INTERCONNECT_QCOM_BCM_VOTER_H__
#include <soc/qcom/cmd-db.h>
#include <soc/qcom/rpmh.h>
#include <soc/qcom/tcs.h>
#include "icc-rpmh.h"
struct bcm_voter *of_bcm_voter_get(struct device *dev, const char *name);
void qcom_icc_bcm_voter_add(struct bcm_voter *voter, struct qcom_icc_bcm *bcm);
int qcom_icc_bcm_voter_commit(struct bcm_voter *voter);
#endif
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,39 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2022 Linaro Ltd.
*/
#include <linux/of.h>
#include <linux/slab.h>
#include <linux/module.h>
#include "icc-common.h"
struct icc_node_data *qcom_icc_xlate_extended(const struct of_phandle_args *spec,
void *data)
{
struct icc_node_data *ndata;
struct icc_node *node;
node = of_icc_xlate_onecell(spec, data);
if (IS_ERR(node))
return ERR_CAST(node);
ndata = kzalloc_obj(*ndata);
if (!ndata)
return ERR_PTR(-ENOMEM);
ndata->node = node;
if (spec->args_count == 2)
ndata->tag = spec->args[1];
if (spec->args_count > 2)
pr_warn("%pOF: Too many arguments, path tag is not parsed\n", spec->np);
return ndata;
}
EXPORT_SYMBOL_GPL(qcom_icc_xlate_extended);
MODULE_DESCRIPTION("Qualcomm interconnect common functions");
MODULE_LICENSE("GPL");
@@ -0,0 +1,14 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2022 Linaro Ltd.
*/
#ifndef __DRIVERS_INTERCONNECT_QCOM_ICC_COMMON_H__
#define __DRIVERS_INTERCONNECT_QCOM_ICC_COMMON_H__
#include <linux/interconnect-provider.h>
struct icc_node_data *qcom_icc_xlate_extended(const struct of_phandle_args *spec,
void *data);
#endif
@@ -0,0 +1,89 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2023 Linaro Ltd
*/
#include <linux/soc/qcom/smd-rpm.h>
#include "icc-rpm.h"
const struct rpm_clk_resource aggre1_clk = {
.resource_type = QCOM_SMD_RPM_AGGR_CLK,
.clock_id = 1,
};
EXPORT_SYMBOL_GPL(aggre1_clk);
const struct rpm_clk_resource aggre2_clk = {
.resource_type = QCOM_SMD_RPM_AGGR_CLK,
.clock_id = 2,
};
EXPORT_SYMBOL_GPL(aggre2_clk);
const struct rpm_clk_resource bimc_clk = {
.resource_type = QCOM_SMD_RPM_MEM_CLK,
.clock_id = 0,
};
EXPORT_SYMBOL_GPL(bimc_clk);
const struct rpm_clk_resource mem_1_clk = {
.resource_type = QCOM_SMD_RPM_MEM_CLK,
.clock_id = 1,
};
EXPORT_SYMBOL_GPL(mem_1_clk);
const struct rpm_clk_resource gpu_mem_2_clk = {
.resource_type = QCOM_SMD_RPM_MEM_CLK,
.clock_id = 2,
};
EXPORT_SYMBOL_GPL(gpu_mem_2_clk);
const struct rpm_clk_resource bus_0_clk = {
.resource_type = QCOM_SMD_RPM_BUS_CLK,
.clock_id = 0,
};
EXPORT_SYMBOL_GPL(bus_0_clk);
const struct rpm_clk_resource bus_1_clk = {
.resource_type = QCOM_SMD_RPM_BUS_CLK,
.clock_id = 1,
};
EXPORT_SYMBOL_GPL(bus_1_clk);
const struct rpm_clk_resource bus_2_clk = {
.resource_type = QCOM_SMD_RPM_BUS_CLK,
.clock_id = 2,
};
EXPORT_SYMBOL_GPL(bus_2_clk);
const struct rpm_clk_resource mmaxi_0_clk = {
.resource_type = QCOM_SMD_RPM_MMAXI_CLK,
.clock_id = 0,
};
EXPORT_SYMBOL_GPL(mmaxi_0_clk);
const struct rpm_clk_resource mmaxi_1_clk = {
.resource_type = QCOM_SMD_RPM_MMAXI_CLK,
.clock_id = 1,
};
EXPORT_SYMBOL_GPL(mmaxi_1_clk);
const struct rpm_clk_resource qup_clk = {
.resource_type = QCOM_SMD_RPM_QUP_CLK,
.clock_id = 0,
};
EXPORT_SYMBOL_GPL(qup_clk);
/* Branch clocks */
const struct rpm_clk_resource aggre1_branch_clk = {
.resource_type = QCOM_SMD_RPM_AGGR_CLK,
.clock_id = 1,
.branch = true,
};
EXPORT_SYMBOL_GPL(aggre1_branch_clk);
const struct rpm_clk_resource aggre2_branch_clk = {
.resource_type = QCOM_SMD_RPM_AGGR_CLK,
.clock_id = 2,
.branch = true,
};
EXPORT_SYMBOL_GPL(aggre2_branch_clk);
@@ -0,0 +1,642 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2020 Linaro Ltd
*/
#include <linux/device.h>
#include <linux/interconnect-provider.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include "icc-common.h"
#include "icc-rpm.h"
/* QNOC QoS */
#define QNOC_QOS_MCTL_LOWn_ADDR(n) (0x8 + (n * 0x1000))
#define QNOC_QOS_MCTL_DFLT_PRIO_MASK 0x70
#define QNOC_QOS_MCTL_DFLT_PRIO_SHIFT 4
#define QNOC_QOS_MCTL_URGFWD_EN_MASK 0x8
#define QNOC_QOS_MCTL_URGFWD_EN_SHIFT 3
/* BIMC QoS */
#define M_BKE_REG_BASE(n) (0x300 + (0x4000 * n))
#define M_BKE_EN_ADDR(n) (M_BKE_REG_BASE(n))
#define M_BKE_HEALTH_CFG_ADDR(i, n) (M_BKE_REG_BASE(n) + 0x40 + (0x4 * i))
#define M_BKE_HEALTH_CFG_LIMITCMDS_MASK 0x80000000
#define M_BKE_HEALTH_CFG_AREQPRIO_MASK 0x300
#define M_BKE_HEALTH_CFG_PRIOLVL_MASK 0x3
#define M_BKE_HEALTH_CFG_AREQPRIO_SHIFT 0x8
#define M_BKE_HEALTH_CFG_LIMITCMDS_SHIFT 0x1f
#define M_BKE_EN_EN_BMASK 0x1
/* NoC QoS */
#define NOC_QOS_PRIORITYn_ADDR(n) (0x8 + (n * 0x1000))
#define NOC_QOS_PRIORITY_P1_MASK 0xc
#define NOC_QOS_PRIORITY_P0_MASK 0x3
#define NOC_QOS_PRIORITY_P1_SHIFT 0x2
#define NOC_QOS_MODEn_ADDR(n) (0xc + (n * 0x1000))
#define NOC_QOS_MODEn_MASK 0x3
#define NOC_QOS_MODE_FIXED_VAL 0x0
#define NOC_QOS_MODE_BYPASS_VAL 0x2
#define ICC_BUS_CLK_MIN_RATE 19200ULL /* kHz */
static int qcom_icc_set_qnoc_qos(struct icc_node *src)
{
struct icc_provider *provider = src->provider;
struct qcom_icc_provider *qp = to_qcom_provider(provider);
struct qcom_icc_node *qn = src->data;
struct qcom_icc_qos *qos = &qn->qos;
int rc;
rc = regmap_update_bits(qp->regmap,
qp->qos_offset + QNOC_QOS_MCTL_LOWn_ADDR(qos->qos_port),
QNOC_QOS_MCTL_DFLT_PRIO_MASK,
qos->areq_prio << QNOC_QOS_MCTL_DFLT_PRIO_SHIFT);
if (rc)
return rc;
return regmap_update_bits(qp->regmap,
qp->qos_offset + QNOC_QOS_MCTL_LOWn_ADDR(qos->qos_port),
QNOC_QOS_MCTL_URGFWD_EN_MASK,
!!qos->urg_fwd_en << QNOC_QOS_MCTL_URGFWD_EN_SHIFT);
}
static int qcom_icc_bimc_set_qos_health(struct qcom_icc_provider *qp,
struct qcom_icc_qos *qos,
int regnum)
{
u32 val;
u32 mask;
val = qos->prio_level;
mask = M_BKE_HEALTH_CFG_PRIOLVL_MASK;
val |= qos->areq_prio << M_BKE_HEALTH_CFG_AREQPRIO_SHIFT;
mask |= M_BKE_HEALTH_CFG_AREQPRIO_MASK;
/* LIMITCMDS is not present on M_BKE_HEALTH_3 */
if (regnum != 3) {
val |= qos->limit_commands << M_BKE_HEALTH_CFG_LIMITCMDS_SHIFT;
mask |= M_BKE_HEALTH_CFG_LIMITCMDS_MASK;
}
return regmap_update_bits(qp->regmap,
qp->qos_offset + M_BKE_HEALTH_CFG_ADDR(regnum, qos->qos_port),
mask, val);
}
static int qcom_icc_set_bimc_qos(struct icc_node *src)
{
struct qcom_icc_provider *qp;
struct qcom_icc_node *qn;
struct icc_provider *provider;
u32 mode = NOC_QOS_MODE_BYPASS;
u32 val = 0;
int i, rc = 0;
qn = src->data;
provider = src->provider;
qp = to_qcom_provider(provider);
if (qn->qos.qos_mode != NOC_QOS_MODE_INVALID)
mode = qn->qos.qos_mode;
/* QoS Priority: The QoS Health parameters are getting considered
* only if we are NOT in Bypass Mode.
*/
if (mode != NOC_QOS_MODE_BYPASS) {
for (i = 3; i >= 0; i--) {
rc = qcom_icc_bimc_set_qos_health(qp,
&qn->qos, i);
if (rc)
return rc;
}
/* Set BKE_EN to 1 when Fixed, Regulator or Limiter Mode */
val = 1;
}
return regmap_update_bits(qp->regmap,
qp->qos_offset + M_BKE_EN_ADDR(qn->qos.qos_port),
M_BKE_EN_EN_BMASK, val);
}
static int qcom_icc_noc_set_qos_priority(struct qcom_icc_provider *qp,
struct qcom_icc_qos *qos)
{
u32 val;
int rc;
/* Must be updated one at a time, P1 first, P0 last */
val = qos->areq_prio << NOC_QOS_PRIORITY_P1_SHIFT;
rc = regmap_update_bits(qp->regmap,
qp->qos_offset + NOC_QOS_PRIORITYn_ADDR(qos->qos_port),
NOC_QOS_PRIORITY_P1_MASK, val);
if (rc)
return rc;
return regmap_update_bits(qp->regmap,
qp->qos_offset + NOC_QOS_PRIORITYn_ADDR(qos->qos_port),
NOC_QOS_PRIORITY_P0_MASK, qos->prio_level);
}
static int qcom_icc_set_noc_qos(struct icc_node *src)
{
struct qcom_icc_provider *qp;
struct qcom_icc_node *qn;
struct icc_provider *provider;
u32 mode = NOC_QOS_MODE_BYPASS_VAL;
int rc = 0;
qn = src->data;
provider = src->provider;
qp = to_qcom_provider(provider);
if (qn->qos.qos_port < 0) {
dev_dbg(src->provider->dev,
"NoC QoS: Skipping %s: vote aggregated on parent.\n",
qn->name);
return 0;
}
if (qn->qos.qos_mode == NOC_QOS_MODE_FIXED) {
dev_dbg(src->provider->dev, "NoC QoS: %s: Set Fixed mode\n", qn->name);
mode = NOC_QOS_MODE_FIXED_VAL;
rc = qcom_icc_noc_set_qos_priority(qp, &qn->qos);
if (rc)
return rc;
} else if (qn->qos.qos_mode == NOC_QOS_MODE_BYPASS) {
dev_dbg(src->provider->dev, "NoC QoS: %s: Set Bypass mode\n", qn->name);
mode = NOC_QOS_MODE_BYPASS_VAL;
} else {
/* How did we get here? */
}
return regmap_update_bits(qp->regmap,
qp->qos_offset + NOC_QOS_MODEn_ADDR(qn->qos.qos_port),
NOC_QOS_MODEn_MASK, mode);
}
static int qcom_icc_qos_set(struct icc_node *node)
{
struct qcom_icc_provider *qp = to_qcom_provider(node->provider);
struct qcom_icc_node *qn = node->data;
dev_dbg(node->provider->dev, "Setting QoS for %s\n", qn->name);
switch (qp->type) {
case QCOM_ICC_BIMC:
return qcom_icc_set_bimc_qos(node);
case QCOM_ICC_QNOC:
return qcom_icc_set_qnoc_qos(node);
default:
return qcom_icc_set_noc_qos(node);
}
}
static int qcom_icc_rpm_set(struct qcom_icc_node *qn, u64 *bw, bool ignore_enxio)
{
int ret, rpm_ctx = 0;
u64 bw_bps;
if (qn->qos.ap_owned)
return 0;
for (rpm_ctx = 0; rpm_ctx < QCOM_SMD_RPM_STATE_NUM; rpm_ctx++) {
bw_bps = icc_units_to_bps(bw[rpm_ctx]);
if (qn->mas_rpm_id != -1) {
ret = qcom_icc_rpm_smd_send(rpm_ctx,
RPM_BUS_MASTER_REQ,
qn->mas_rpm_id,
bw_bps);
if (ret) {
pr_err("qcom_icc_rpm_smd_send mas %d error %d\n",
qn->mas_rpm_id, ret);
if (ret != -ENXIO || !ignore_enxio)
return ret;
}
}
if (qn->slv_rpm_id != -1) {
ret = qcom_icc_rpm_smd_send(rpm_ctx,
RPM_BUS_SLAVE_REQ,
qn->slv_rpm_id,
bw_bps);
if (ret) {
pr_err("qcom_icc_rpm_smd_send slv %d error %d\n",
qn->slv_rpm_id, ret);
if (ret != -ENXIO || !ignore_enxio)
return ret;
}
}
}
return 0;
}
/**
* qcom_icc_pre_bw_aggregate - cleans up values before re-aggregate requests
* @node: icc node to operate on
*/
static void qcom_icc_pre_bw_aggregate(struct icc_node *node)
{
struct qcom_icc_node *qn;
size_t i;
qn = node->data;
for (i = 0; i < QCOM_SMD_RPM_STATE_NUM; i++) {
qn->sum_avg[i] = 0;
qn->max_peak[i] = 0;
}
}
/**
* qcom_icc_bw_aggregate - aggregate bw for buckets indicated by tag
* @node: node to aggregate
* @tag: tag to indicate which buckets to aggregate
* @avg_bw: new bw to sum aggregate
* @peak_bw: new bw to max aggregate
* @agg_avg: existing aggregate avg bw val
* @agg_peak: existing aggregate peak bw val
*/
static int qcom_icc_bw_aggregate(struct icc_node *node, u32 tag, u32 avg_bw,
u32 peak_bw, u32 *agg_avg, u32 *agg_peak)
{
size_t i;
struct qcom_icc_node *qn;
qn = node->data;
if (!tag)
tag = RPM_ALWAYS_TAG;
for (i = 0; i < QCOM_SMD_RPM_STATE_NUM; i++) {
if (tag & BIT(i)) {
qn->sum_avg[i] += avg_bw;
qn->max_peak[i] = max_t(u32, qn->max_peak[i], peak_bw);
}
}
*agg_avg += avg_bw;
*agg_peak = max_t(u32, *agg_peak, peak_bw);
return 0;
}
static u64 qcom_icc_calc_rate(struct qcom_icc_provider *qp, struct qcom_icc_node *qn, int ctx)
{
u64 agg_avg_rate, agg_peak_rate, agg_rate;
if (qn->channels)
agg_avg_rate = div_u64(qn->sum_avg[ctx], qn->channels);
else
agg_avg_rate = qn->sum_avg[ctx];
if (qn->ab_coeff) {
agg_avg_rate = agg_avg_rate * qn->ab_coeff;
agg_avg_rate = div_u64(agg_avg_rate, 100);
}
if (qn->ib_coeff) {
agg_peak_rate = qn->max_peak[ctx] * 100;
agg_peak_rate = div_u64(agg_peak_rate, qn->ib_coeff);
} else {
agg_peak_rate = qn->max_peak[ctx];
}
agg_rate = max_t(u64, agg_avg_rate, agg_peak_rate);
return div_u64(agg_rate, qn->buswidth);
}
/**
* qcom_icc_bus_aggregate - calculate bus clock rates by traversing all nodes
* @provider: generic interconnect provider
* @agg_clk_rate: array containing the aggregated clock rates in kHz
*/
static void qcom_icc_bus_aggregate(struct icc_provider *provider, u64 *agg_clk_rate)
{
struct qcom_icc_provider *qp = to_qcom_provider(provider);
struct qcom_icc_node *qn;
struct icc_node *node;
int ctx;
/*
* Iterate nodes on the provider, aggregate bandwidth requests for
* every bucket and convert them into bus clock rates.
*/
list_for_each_entry(node, &provider->nodes, node_list) {
qn = node->data;
for (ctx = 0; ctx < QCOM_SMD_RPM_STATE_NUM; ctx++) {
agg_clk_rate[ctx] = max_t(u64, agg_clk_rate[ctx],
qcom_icc_calc_rate(qp, qn, ctx));
}
}
}
static int qcom_icc_set(struct icc_node *src, struct icc_node *dst)
{
struct qcom_icc_node *src_qn = NULL, *dst_qn = NULL;
u64 agg_clk_rate[QCOM_SMD_RPM_STATE_NUM] = { 0 };
struct icc_provider *provider;
struct qcom_icc_provider *qp;
u64 active_rate, sleep_rate;
int ret;
src_qn = src->data;
if (dst)
dst_qn = dst->data;
provider = src->provider;
qp = to_qcom_provider(provider);
qcom_icc_bus_aggregate(provider, agg_clk_rate);
active_rate = agg_clk_rate[QCOM_SMD_RPM_ACTIVE_STATE];
sleep_rate = agg_clk_rate[QCOM_SMD_RPM_SLEEP_STATE];
ret = qcom_icc_rpm_set(src_qn, src_qn->sum_avg, qp->ignore_enxio);
if (ret)
return ret;
if (dst_qn) {
ret = qcom_icc_rpm_set(dst_qn, dst_qn->sum_avg, qp->ignore_enxio);
if (ret)
return ret;
}
/* Some providers don't have a bus clock to scale */
if (!qp->bus_clk_desc && !qp->bus_clk)
return 0;
/*
* Downstream checks whether the requested rate is zero, but it makes little sense
* to vote for a value that's below the lower threshold, so let's not do so.
*/
if (qp->keep_alive)
active_rate = max(ICC_BUS_CLK_MIN_RATE, active_rate);
/* Some providers have a non-RPM-owned bus clock - convert kHz->Hz for the CCF */
if (qp->bus_clk) {
active_rate = max_t(u64, active_rate, sleep_rate);
/* ARM32 caps clk_set_rate arg to u32.. Nothing we can do about that! */
active_rate = min_t(u64, 1000ULL * active_rate, ULONG_MAX);
return clk_set_rate(qp->bus_clk, active_rate);
}
/* RPM only accepts <=INT_MAX rates */
active_rate = min_t(u64, active_rate, INT_MAX);
sleep_rate = min_t(u64, sleep_rate, INT_MAX);
if (active_rate != qp->bus_clk_rate[QCOM_SMD_RPM_ACTIVE_STATE]) {
ret = qcom_icc_rpm_set_bus_rate(qp->bus_clk_desc, QCOM_SMD_RPM_ACTIVE_STATE,
active_rate);
if (ret)
return ret;
/* Cache the rate after we've successfully commited it to RPM */
qp->bus_clk_rate[QCOM_SMD_RPM_ACTIVE_STATE] = active_rate;
}
if (sleep_rate != qp->bus_clk_rate[QCOM_SMD_RPM_SLEEP_STATE]) {
ret = qcom_icc_rpm_set_bus_rate(qp->bus_clk_desc, QCOM_SMD_RPM_SLEEP_STATE,
sleep_rate);
if (ret)
return ret;
/* Cache the rate after we've successfully commited it to RPM */
qp->bus_clk_rate[QCOM_SMD_RPM_SLEEP_STATE] = sleep_rate;
}
/* Handle the node-specific clock */
if (!src_qn->bus_clk_desc)
return 0;
active_rate = qcom_icc_calc_rate(qp, src_qn, QCOM_SMD_RPM_ACTIVE_STATE);
sleep_rate = qcom_icc_calc_rate(qp, src_qn, QCOM_SMD_RPM_SLEEP_STATE);
if (active_rate != src_qn->bus_clk_rate[QCOM_SMD_RPM_ACTIVE_STATE]) {
ret = qcom_icc_rpm_set_bus_rate(src_qn->bus_clk_desc, QCOM_SMD_RPM_ACTIVE_STATE,
active_rate);
if (ret)
return ret;
/* Cache the rate after we've successfully committed it to RPM */
src_qn->bus_clk_rate[QCOM_SMD_RPM_ACTIVE_STATE] = active_rate;
}
if (sleep_rate != src_qn->bus_clk_rate[QCOM_SMD_RPM_SLEEP_STATE]) {
ret = qcom_icc_rpm_set_bus_rate(src_qn->bus_clk_desc, QCOM_SMD_RPM_SLEEP_STATE,
sleep_rate);
if (ret)
return ret;
/* Cache the rate after we've successfully committed it to RPM */
src_qn->bus_clk_rate[QCOM_SMD_RPM_SLEEP_STATE] = sleep_rate;
}
return 0;
}
int qnoc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
const struct qcom_icc_desc *desc;
struct icc_onecell_data *data;
struct icc_provider *provider;
struct qcom_icc_node * const *qnodes;
struct qcom_icc_provider *qp;
struct icc_node *node;
size_t num_nodes, i;
const char * const *cds = NULL;
int cd_num;
int ret;
/* wait for the RPM proxy */
if (!qcom_icc_rpm_smd_available())
return -EPROBE_DEFER;
desc = of_device_get_match_data(dev);
if (!desc)
return -EINVAL;
qnodes = desc->nodes;
num_nodes = desc->num_nodes;
if (desc->num_intf_clocks) {
cds = desc->intf_clocks;
cd_num = desc->num_intf_clocks;
} else {
/* 0 intf clocks is perfectly fine */
cd_num = 0;
}
qp = devm_kzalloc(dev, sizeof(*qp), GFP_KERNEL);
if (!qp)
return -ENOMEM;
qp->intf_clks = devm_kcalloc(dev, cd_num, sizeof(*qp->intf_clks), GFP_KERNEL);
if (!qp->intf_clks)
return -ENOMEM;
if (desc->bus_clk_desc) {
qp->bus_clk_desc = devm_kzalloc(dev, sizeof(*qp->bus_clk_desc),
GFP_KERNEL);
if (!qp->bus_clk_desc)
return -ENOMEM;
qp->bus_clk_desc = desc->bus_clk_desc;
} else {
/* Some older SoCs may have a single non-RPM-owned bus clock. */
qp->bus_clk = devm_clk_get_optional(dev, "bus");
if (IS_ERR(qp->bus_clk))
return PTR_ERR(qp->bus_clk);
}
data = devm_kzalloc(dev, struct_size(data, nodes, num_nodes),
GFP_KERNEL);
if (!data)
return -ENOMEM;
data->num_nodes = num_nodes;
qp->num_intf_clks = cd_num;
for (i = 0; i < cd_num; i++)
qp->intf_clks[i].id = cds[i];
qp->ignore_enxio = desc->ignore_enxio;
qp->keep_alive = desc->keep_alive;
qp->type = desc->type;
qp->qos_offset = desc->qos_offset;
if (desc->regmap_cfg) {
struct resource *res;
void __iomem *mmio;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
/* Try parent's regmap */
qp->regmap = dev_get_regmap(dev->parent, NULL);
if (qp->regmap)
goto regmap_done;
return -ENODEV;
}
mmio = devm_ioremap_resource(dev, res);
if (IS_ERR(mmio))
return PTR_ERR(mmio);
qp->regmap = devm_regmap_init_mmio(dev, mmio, desc->regmap_cfg);
if (IS_ERR(qp->regmap)) {
dev_err(dev, "Cannot regmap interconnect bus resource\n");
return PTR_ERR(qp->regmap);
}
}
regmap_done:
ret = clk_prepare_enable(qp->bus_clk);
if (ret)
return ret;
ret = devm_clk_bulk_get(dev, qp->num_intf_clks, qp->intf_clks);
if (ret)
goto err_disable_unprepare_clk;
provider = &qp->provider;
provider->dev = dev;
provider->set = qcom_icc_set;
provider->pre_aggregate = qcom_icc_pre_bw_aggregate;
provider->aggregate = qcom_icc_bw_aggregate;
provider->xlate_extended = qcom_icc_xlate_extended;
provider->data = data;
provider->get_bw = desc->get_bw;
icc_provider_init(provider);
/* If this fails, bus accesses will crash the platform! */
ret = clk_bulk_prepare_enable(qp->num_intf_clks, qp->intf_clks);
if (ret)
goto err_disable_unprepare_clk;
for (i = 0; i < num_nodes; i++) {
size_t j;
if (!qnodes[i]->ab_coeff)
qnodes[i]->ab_coeff = qp->ab_coeff;
if (!qnodes[i]->ib_coeff)
qnodes[i]->ib_coeff = qp->ib_coeff;
node = icc_node_create(qnodes[i]->id);
if (IS_ERR(node)) {
clk_bulk_disable_unprepare(qp->num_intf_clks,
qp->intf_clks);
ret = PTR_ERR(node);
goto err_remove_nodes;
}
node->name = qnodes[i]->name;
node->data = qnodes[i];
icc_node_add(node, provider);
for (j = 0; j < qnodes[i]->num_links; j++)
icc_link_create(node, qnodes[i]->links[j]);
/* Set QoS registers (we only need to do it once, generally) */
if (qnodes[i]->qos.ap_owned &&
qnodes[i]->qos.qos_mode != NOC_QOS_MODE_INVALID) {
ret = qcom_icc_qos_set(node);
if (ret) {
clk_bulk_disable_unprepare(qp->num_intf_clks,
qp->intf_clks);
goto err_remove_nodes;
}
}
data->nodes[i] = node;
}
clk_bulk_disable_unprepare(qp->num_intf_clks, qp->intf_clks);
ret = icc_provider_register(provider);
if (ret)
goto err_remove_nodes;
platform_set_drvdata(pdev, qp);
/* Populate child NoC devices if any */
if (of_get_child_count(dev->of_node) > 0) {
ret = of_platform_populate(dev->of_node, NULL, NULL, dev);
if (ret)
goto err_deregister_provider;
}
return 0;
err_deregister_provider:
icc_provider_deregister(provider);
err_remove_nodes:
icc_nodes_remove(provider);
err_disable_unprepare_clk:
clk_disable_unprepare(qp->bus_clk);
return ret;
}
EXPORT_SYMBOL(qnoc_probe);
void qnoc_remove(struct platform_device *pdev)
{
struct qcom_icc_provider *qp = platform_get_drvdata(pdev);
icc_provider_deregister(&qp->provider);
icc_nodes_remove(&qp->provider);
clk_disable_unprepare(qp->bus_clk);
}
EXPORT_SYMBOL(qnoc_remove);
@@ -0,0 +1,173 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2020 Linaro Ltd
*/
#ifndef __DRIVERS_INTERCONNECT_QCOM_ICC_RPM_H
#define __DRIVERS_INTERCONNECT_QCOM_ICC_RPM_H
#include <linux/soc/qcom/smd-rpm.h>
#include <dt-bindings/interconnect/qcom,rpm-icc.h>
#include <linux/clk.h>
#include <linux/interconnect-provider.h>
#include <linux/platform_device.h>
#define RPM_BUS_MASTER_REQ 0x73616d62
#define RPM_BUS_SLAVE_REQ 0x766c7362
#define to_qcom_provider(_provider) \
container_of(_provider, struct qcom_icc_provider, provider)
enum qcom_icc_type {
QCOM_ICC_NOC,
QCOM_ICC_BIMC,
QCOM_ICC_QNOC,
};
/**
* struct rpm_clk_resource - RPM bus clock resource
* @resource_type: RPM resource type of the clock resource
* @clock_id: index of the clock resource of a specific resource type
* @branch: whether the resource represents a branch clock
*/
struct rpm_clk_resource {
u32 resource_type;
u32 clock_id;
bool branch;
};
/**
* struct qcom_icc_provider - Qualcomm specific interconnect provider
* @provider: generic interconnect provider
* @num_intf_clks: the total number of intf_clks clk_bulk_data entries
* @type: the ICC provider type
* @regmap: regmap for QoS registers read/write access
* @qos_offset: offset to QoS registers
* @ab_coeff: a percentage-based coefficient for compensating the AB calculations
* @ib_coeff: an inverse-percentage-based coefficient for compensating the IB calculations
* @bus_clk_rate: bus clock rate in Hz
* @bus_clk_desc: a pointer to a rpm_clk_resource description of bus clocks
* @bus_clk: a pointer to a HLOS-owned bus clock
* @intf_clks: a clk_bulk_data array of interface clocks
* @keep_alive: whether to always keep a minimum vote on the bus clocks
* @ignore_enxio: whether to ignore ENXIO errors (for MSM8974)
*/
struct qcom_icc_provider {
struct icc_provider provider;
int num_intf_clks;
enum qcom_icc_type type;
struct regmap *regmap;
unsigned int qos_offset;
u16 ab_coeff;
u16 ib_coeff;
u32 bus_clk_rate[QCOM_SMD_RPM_STATE_NUM];
const struct rpm_clk_resource *bus_clk_desc;
struct clk *bus_clk;
struct clk_bulk_data *intf_clks;
bool keep_alive;
bool ignore_enxio;
};
/**
* struct qcom_icc_qos - Qualcomm specific interconnect QoS parameters
* @areq_prio: node requests priority
* @prio_level: priority level for bus communication
* @limit_commands: activate/deactivate limiter mode during runtime
* @ap_owned: indicates if the node is owned by the AP or by the RPM
* @qos_mode: default qos mode for this node
* @qos_port: qos port number for finding qos registers of this node
* @urg_fwd_en: enable urgent forwarding
*/
struct qcom_icc_qos {
u32 areq_prio;
u32 prio_level;
bool limit_commands;
bool ap_owned;
int qos_mode;
int qos_port;
bool urg_fwd_en;
};
/**
* struct qcom_icc_node - Qualcomm specific interconnect nodes
* @name: the node name used in debugfs
* @id: a unique node identifier
* @links: an array of nodes where we can go next while traversing
* @num_links: the total number of @links
* @channels: number of channels at this node (e.g. DDR channels)
* @buswidth: width of the interconnect between a node and the bus (bytes)
* @bus_clk_desc: a pointer to a rpm_clk_resource description of bus clocks
* @sum_avg: current sum aggregate value of all avg bw requests
* @max_peak: current max aggregate value of all peak bw requests
* @mas_rpm_id: RPM id for devices that are bus masters
* @slv_rpm_id: RPM id for devices that are bus slaves
* @qos: NoC QoS setting parameters
* @ab_coeff: a percentage-based coefficient for compensating the AB calculations
* @ib_coeff: an inverse-percentage-based coefficient for compensating the IB calculations
* @bus_clk_rate: a pointer to an array containing bus clock rates in Hz
*/
struct qcom_icc_node {
unsigned char *name;
u16 id;
const u16 *links;
u16 num_links;
u16 channels;
u16 buswidth;
const struct rpm_clk_resource *bus_clk_desc;
u64 sum_avg[QCOM_SMD_RPM_STATE_NUM];
u64 max_peak[QCOM_SMD_RPM_STATE_NUM];
int mas_rpm_id;
int slv_rpm_id;
struct qcom_icc_qos qos;
u16 ab_coeff;
u16 ib_coeff;
u32 bus_clk_rate[QCOM_SMD_RPM_STATE_NUM];
};
struct qcom_icc_desc {
struct qcom_icc_node * const *nodes;
size_t num_nodes;
const struct rpm_clk_resource *bus_clk_desc;
const char * const *intf_clocks;
size_t num_intf_clocks;
bool keep_alive;
enum qcom_icc_type type;
const struct regmap_config *regmap_cfg;
unsigned int qos_offset;
u16 ab_coeff;
u16 ib_coeff;
int (*get_bw)(struct icc_node *node, u32 *avg, u32 *peak);
bool ignore_enxio;
};
/* Valid for all bus types */
enum qos_mode {
NOC_QOS_MODE_INVALID = 0,
NOC_QOS_MODE_FIXED,
NOC_QOS_MODE_BYPASS,
};
extern const struct rpm_clk_resource aggre1_clk;
extern const struct rpm_clk_resource aggre2_clk;
extern const struct rpm_clk_resource bimc_clk;
extern const struct rpm_clk_resource bus_0_clk;
extern const struct rpm_clk_resource bus_1_clk;
extern const struct rpm_clk_resource bus_2_clk;
extern const struct rpm_clk_resource gpu_mem_2_clk;
extern const struct rpm_clk_resource mem_1_clk;
extern const struct rpm_clk_resource mmaxi_0_clk;
extern const struct rpm_clk_resource mmaxi_1_clk;
extern const struct rpm_clk_resource qup_clk;
extern const struct rpm_clk_resource aggre1_branch_clk;
extern const struct rpm_clk_resource aggre2_branch_clk;
int qnoc_probe(struct platform_device *pdev);
void qnoc_remove(struct platform_device *pdev);
bool qcom_icc_rpm_smd_available(void);
int qcom_icc_rpm_smd_send(int ctx, int rsc_type, int id, u32 val);
int qcom_icc_rpm_set_bus_rate(const struct rpm_clk_resource *clk, int ctx, u32 rate);
#endif
@@ -0,0 +1,375 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2020, The Linux Foundation. All rights reserved.
* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/interconnect.h>
#include <linux/interconnect-provider.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/slab.h>
#include "bcm-voter.h"
#include "icc-common.h"
#include "icc-rpmh.h"
/* QNOC QoS */
#define QOSGEN_MAINCTL_LO(p, qp) (0x8 + (p->port_offsets[qp]))
#define QOS_SLV_URG_MSG_EN_MASK GENMASK(3, 3)
#define QOS_DFLT_PRIO_MASK GENMASK(6, 4)
#define QOS_DISABLE_MASK GENMASK(24, 24)
/**
* qcom_icc_set_qos - initialize static QoS configurations
* @qp: qcom icc provider to which @node belongs
* @node: qcom icc node to operate on
*/
static void qcom_icc_set_qos(struct qcom_icc_provider *qp,
struct qcom_icc_node *node)
{
const struct qcom_icc_qosbox *qos = node->qosbox;
int port;
for (port = 0; port < qos->num_ports; port++) {
regmap_update_bits(qp->regmap, QOSGEN_MAINCTL_LO(qos, port),
QOS_DISABLE_MASK,
FIELD_PREP(QOS_DISABLE_MASK, qos->prio_fwd_disable));
regmap_update_bits(qp->regmap, QOSGEN_MAINCTL_LO(qos, port),
QOS_DFLT_PRIO_MASK,
FIELD_PREP(QOS_DFLT_PRIO_MASK, qos->prio));
regmap_update_bits(qp->regmap, QOSGEN_MAINCTL_LO(qos, port),
QOS_SLV_URG_MSG_EN_MASK,
FIELD_PREP(QOS_SLV_URG_MSG_EN_MASK, qos->urg_fwd));
}
}
/**
* qcom_icc_pre_aggregate - cleans up stale values from prior icc_set
* @node: icc node to operate on
*/
void qcom_icc_pre_aggregate(struct icc_node *node)
{
size_t i;
struct qcom_icc_node *qn;
struct qcom_icc_provider *qp;
qn = node->data;
qp = to_qcom_provider(node->provider);
for (i = 0; i < QCOM_ICC_NUM_BUCKETS; i++) {
qn->sum_avg[i] = 0;
qn->max_peak[i] = 0;
}
for (i = 0; i < qn->num_bcms; i++)
qcom_icc_bcm_voter_add(qp->voter, qn->bcms[i]);
}
EXPORT_SYMBOL_GPL(qcom_icc_pre_aggregate);
/**
* qcom_icc_aggregate - aggregate bw for buckets indicated by tag
* @node: node to aggregate
* @tag: tag to indicate which buckets to aggregate
* @avg_bw: new bw to sum aggregate
* @peak_bw: new bw to max aggregate
* @agg_avg: existing aggregate avg bw val
* @agg_peak: existing aggregate peak bw val
*/
int qcom_icc_aggregate(struct icc_node *node, u32 tag, u32 avg_bw,
u32 peak_bw, u32 *agg_avg, u32 *agg_peak)
{
size_t i;
struct qcom_icc_node *qn;
qn = node->data;
if (!tag)
tag = QCOM_ICC_TAG_ALWAYS;
for (i = 0; i < QCOM_ICC_NUM_BUCKETS; i++) {
if (tag & BIT(i)) {
qn->sum_avg[i] += avg_bw;
qn->max_peak[i] = max_t(u32, qn->max_peak[i], peak_bw);
}
if (node->init_avg || node->init_peak) {
qn->sum_avg[i] = max_t(u64, qn->sum_avg[i], node->init_avg);
qn->max_peak[i] = max_t(u64, qn->max_peak[i], node->init_peak);
}
}
*agg_avg += avg_bw;
*agg_peak = max_t(u32, *agg_peak, peak_bw);
return 0;
}
EXPORT_SYMBOL_GPL(qcom_icc_aggregate);
/**
* qcom_icc_set - set the constraints based on path
* @src: source node for the path to set constraints on
* @dst: destination node for the path to set constraints on
*
* Return: 0 on success, or an error code otherwise
*/
int qcom_icc_set(struct icc_node *src, struct icc_node *dst)
{
struct qcom_icc_provider *qp;
struct icc_node *node;
if (!src)
node = dst;
else
node = src;
qp = to_qcom_provider(node->provider);
qcom_icc_bcm_voter_commit(qp->voter);
return 0;
}
EXPORT_SYMBOL_GPL(qcom_icc_set);
/**
* qcom_icc_bcm_init - populates bcm aux data and connect qnodes
* @bcm: bcm to be initialized
* @dev: associated provider device
*
* Return: 0 on success, or an error code otherwise
*/
int qcom_icc_bcm_init(struct qcom_icc_bcm *bcm, struct device *dev)
{
struct qcom_icc_node *qn;
const struct bcm_db *data;
size_t data_count;
int i;
/* BCM is already initialised*/
if (bcm->addr)
return 0;
bcm->addr = cmd_db_read_addr(bcm->name);
if (!bcm->addr) {
dev_err(dev, "%s could not find RPMh address\n",
bcm->name);
return -EINVAL;
}
data = cmd_db_read_aux_data(bcm->name, &data_count);
if (IS_ERR(data)) {
dev_err(dev, "%s command db read error (%ld)\n",
bcm->name, PTR_ERR(data));
return PTR_ERR(data);
}
if (!data_count) {
dev_err(dev, "%s command db missing or partial aux data\n",
bcm->name);
return -EINVAL;
}
bcm->aux_data.unit = le32_to_cpu(data->unit);
bcm->aux_data.width = le16_to_cpu(data->width);
bcm->aux_data.vcd = data->vcd;
bcm->aux_data.reserved = data->reserved;
INIT_LIST_HEAD(&bcm->list);
INIT_LIST_HEAD(&bcm->ws_list);
if (!bcm->vote_scale)
bcm->vote_scale = 1000;
/* Link Qnodes to their respective BCMs */
for (i = 0; i < bcm->num_nodes; i++) {
qn = bcm->nodes[i];
qn->bcms[qn->num_bcms] = bcm;
qn->num_bcms++;
}
return 0;
}
EXPORT_SYMBOL_GPL(qcom_icc_bcm_init);
/**
* qcom_icc_rpmh_configure_qos - configure QoS parameters
* @qp: qcom icc provider associated with QoS endpoint nodes
*
* Return: 0 on success, or an error code otherwise
*/
static int qcom_icc_rpmh_configure_qos(struct qcom_icc_provider *qp)
{
struct qcom_icc_node *qnode;
size_t i;
int ret;
ret = clk_bulk_prepare_enable(qp->num_clks, qp->clks);
if (ret)
return ret;
for (i = 0; i < qp->num_nodes; i++) {
qnode = qp->nodes[i];
if (!qnode)
continue;
if (qnode->qosbox)
qcom_icc_set_qos(qp, qnode);
}
clk_bulk_disable_unprepare(qp->num_clks, qp->clks);
return ret;
}
int qcom_icc_rpmh_probe(struct platform_device *pdev)
{
const struct qcom_icc_desc *desc;
struct device *dev = &pdev->dev;
struct icc_onecell_data *data;
struct icc_provider *provider;
struct qcom_icc_node * const *qnodes, *qn;
struct qcom_icc_provider *qp;
struct icc_node *node;
size_t num_nodes, i, j;
int ret;
desc = of_device_get_match_data(dev);
if (!desc)
return -EINVAL;
qnodes = desc->nodes;
num_nodes = desc->num_nodes;
qp = devm_kzalloc(dev, sizeof(*qp), GFP_KERNEL);
if (!qp)
return -ENOMEM;
data = devm_kzalloc(dev, struct_size(data, nodes, num_nodes), GFP_KERNEL);
if (!data)
return -ENOMEM;
data->num_nodes = num_nodes;
provider = &qp->provider;
provider->dev = dev;
provider->set = qcom_icc_set;
provider->pre_aggregate = qcom_icc_pre_aggregate;
provider->aggregate = qcom_icc_aggregate;
provider->xlate_extended = qcom_icc_xlate_extended;
provider->data = data;
icc_provider_init(provider);
qp->dev = dev;
qp->bcms = desc->bcms;
qp->nodes = desc->nodes;
qp->num_bcms = desc->num_bcms;
qp->num_nodes = desc->num_nodes;
qp->voter = of_bcm_voter_get(qp->dev, NULL);
if (IS_ERR(qp->voter))
return PTR_ERR(qp->voter);
for (i = 0; i < qp->num_bcms; i++)
qcom_icc_bcm_init(qp->bcms[i], dev);
for (i = 0; i < num_nodes; i++) {
qn = qnodes[i];
if (!qn)
continue;
if (!qn->node)
qn->node = icc_node_create_dyn();
node = qn->node;
if (IS_ERR(node)) {
ret = PTR_ERR(node);
goto err_remove_nodes;
}
ret = icc_node_set_name(node, provider, qn->name);
if (ret) {
icc_node_destroy(node->id);
goto err_remove_nodes;
}
node->data = qn;
icc_node_add(node, provider);
for (j = 0; j < qn->num_links; j++)
icc_link_nodes(node, &qn->link_nodes[j]->node);
data->nodes[i] = node;
}
if (desc->config) {
struct resource *res;
void __iomem *base;
/* Try parent's regmap first */
qp->regmap = dev_get_regmap(dev->parent, NULL);
if (!qp->regmap) {
base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(base))
goto skip_qos_config;
qp->regmap = devm_regmap_init_mmio(dev, base, desc->config);
if (IS_ERR(qp->regmap)) {
dev_info(dev, "Skipping QoS, regmap failed; %ld\n",
PTR_ERR(qp->regmap));
goto skip_qos_config;
}
}
qp->num_clks = devm_clk_bulk_get_all(qp->dev, &qp->clks);
if (qp->num_clks == -EPROBE_DEFER)
return dev_err_probe(dev, qp->num_clks, "Failed to get QoS clocks\n");
if (qp->num_clks < 0 || (!qp->num_clks && desc->qos_requires_clocks)) {
dev_info(dev, "Skipping QoS, failed to get clk: %d\n", qp->num_clks);
goto skip_qos_config;
}
ret = qcom_icc_rpmh_configure_qos(qp);
if (ret)
dev_info(dev, "Failed to program QoS: %d\n", ret);
}
skip_qos_config:
ret = icc_provider_register(provider);
if (ret)
goto err_remove_nodes;
platform_set_drvdata(pdev, qp);
/* Populate child NoC devices if any */
if (of_get_child_count(dev->of_node) > 0) {
ret = of_platform_populate(dev->of_node, NULL, NULL, dev);
if (ret)
goto err_deregister_provider;
}
return 0;
err_deregister_provider:
icc_provider_deregister(provider);
err_remove_nodes:
icc_nodes_remove(provider);
return ret;
}
EXPORT_SYMBOL_GPL(qcom_icc_rpmh_probe);
void qcom_icc_rpmh_remove(struct platform_device *pdev)
{
struct qcom_icc_provider *qp = platform_get_drvdata(pdev);
icc_provider_deregister(&qp->provider);
icc_nodes_remove(&qp->provider);
}
EXPORT_SYMBOL_GPL(qcom_icc_rpmh_remove);
MODULE_DESCRIPTION("Qualcomm RPMh interconnect driver");
MODULE_LICENSE("GPL v2");
@@ -0,0 +1,167 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2020, The Linux Foundation. All rights reserved.
* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef __DRIVERS_INTERCONNECT_QCOM_ICC_RPMH_H__
#define __DRIVERS_INTERCONNECT_QCOM_ICC_RPMH_H__
#include <dt-bindings/interconnect/qcom,icc.h>
#include <linux/regmap.h>
#define to_qcom_provider(_provider) \
container_of(_provider, struct qcom_icc_provider, provider)
/**
* struct qcom_icc_provider - Qualcomm specific interconnect provider
* @provider: generic interconnect provider
* @dev: reference to the NoC device
* @bcms: list of bcms that maps to the provider
* @num_bcms: number of @bcms
* @voter: bcm voter targeted by this provider
* @nodes: list of icc nodes that maps to the provider
* @num_nodes: number of @nodes
* @regmap: used for QoS, register access
* @clks : clks required for register access
* @num_clks: number of @clks
*/
struct qcom_icc_provider {
struct icc_provider provider;
struct device *dev;
struct qcom_icc_bcm * const *bcms;
size_t num_bcms;
struct bcm_voter *voter;
struct qcom_icc_node * const *nodes;
size_t num_nodes;
struct regmap *regmap;
struct clk_bulk_data *clks;
int num_clks;
};
/**
* struct bcm_db - Auxiliary data pertaining to each Bus Clock Manager (BCM)
* @unit: divisor used to convert bytes/sec bw value to an RPMh msg
* @width: multiplier used to convert bytes/sec bw value to an RPMh msg
* @vcd: virtual clock domain that this bcm belongs to
* @reserved: reserved field
*/
struct bcm_db {
__le32 unit;
__le16 width;
u8 vcd;
u8 reserved;
};
#define MAX_PORTS 4
/**
* struct qcom_icc_qosbox - Qualcomm specific QoS config
* @prio: priority value assigned to requests on the node
* @urg_fwd: whether to forward the urgency promotion issued by master
* (endpoint), or discard
* @prio_fwd_disable: whether to forward the priority driven by master, or
* override by @prio
* @num_ports: number of @ports
* @port_offsets: qos register offsets
*/
struct qcom_icc_qosbox {
const u32 prio;
const bool urg_fwd;
const bool prio_fwd_disable;
const u32 num_ports;
const u32 port_offsets[MAX_PORTS];
};
#define MAX_LINKS 128
#define MAX_BCMS 64
#define MAX_BCM_PER_NODE 3
#define MAX_VCD 10
/**
* struct qcom_icc_node - Qualcomm specific interconnect nodes
* @name: the node name used in debugfs
* @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
* @sum_avg: current sum aggregate value of all avg bw requests
* @max_peak: current max aggregate value of all peak bw requests
* @bcms: list of bcms associated with this logical node
* @num_bcms: num of @bcms
* @qosbox: QoS config data associated with node
*/
struct qcom_icc_node {
const char *name;
struct icc_node *node;
u16 num_links;
u16 channels;
u16 buswidth;
u64 sum_avg[QCOM_ICC_NUM_BUCKETS];
u64 max_peak[QCOM_ICC_NUM_BUCKETS];
struct qcom_icc_bcm *bcms[MAX_BCM_PER_NODE];
size_t num_bcms;
const struct qcom_icc_qosbox *qosbox;
struct qcom_icc_node *link_nodes[];
};
/**
* struct qcom_icc_bcm - Qualcomm specific hardware accelerator nodes
* known as Bus Clock Manager (BCM)
* @name: the bcm node name used to fetch BCM data from command db
* @type: latency or bandwidth bcm
* @addr: address offsets used when voting to RPMH
* @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
* @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
* communicating with RPMh
* @list: used to link to other bcms when compiling lists for commit
* @ws_list: used to keep track of bcms that may transition between wake/sleep
* @num_nodes: total number of @num_nodes
* @nodes: list of qcom_icc_nodes that this BCM encapsulates
*/
struct qcom_icc_bcm {
const char *name;
u32 type;
u32 addr;
u64 vote_x[QCOM_ICC_NUM_BUCKETS];
u64 vote_y[QCOM_ICC_NUM_BUCKETS];
u64 vote_scale;
u32 enable_mask;
bool dirty;
bool keepalive;
struct bcm_db aux_data;
struct list_head list;
struct list_head ws_list;
size_t num_nodes;
struct qcom_icc_node *nodes[];
};
struct qcom_icc_fabric {
struct qcom_icc_node **nodes;
size_t num_nodes;
};
struct qcom_icc_desc {
const struct regmap_config *config;
struct qcom_icc_node * const *nodes;
size_t num_nodes;
struct qcom_icc_bcm * const *bcms;
size_t num_bcms;
bool qos_requires_clocks;
};
int qcom_icc_aggregate(struct icc_node *node, u32 tag, u32 avg_bw,
u32 peak_bw, u32 *agg_avg, u32 *agg_peak);
int qcom_icc_set(struct icc_node *src, struct icc_node *dst);
int qcom_icc_bcm_init(struct qcom_icc_bcm *bcm, struct device *dev);
void qcom_icc_pre_aggregate(struct icc_node *node);
int qcom_icc_rpmh_probe(struct platform_device *pdev);
void qcom_icc_rpmh_remove(struct platform_device *pdev);
#endif
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,149 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Qualcomm MSM8996 interconnect IDs
*
* Copyright (c) 2021 Yassine Oudjana <y.oudjana@protonmail.com>
*/
#ifndef __DRIVERS_INTERCONNECT_QCOM_MSM8996_H__
#define __DRIVERS_INTERCONNECT_QCOM_MSM8996_H__
#define MSM8996_MASTER_PCIE_0 1
#define MSM8996_MASTER_PCIE_1 2
#define MSM8996_MASTER_PCIE_2 3
#define MSM8996_MASTER_CNOC_A1NOC 4
#define MSM8996_MASTER_CRYPTO_CORE0 5
#define MSM8996_MASTER_PNOC_A1NOC 6
#define MSM8996_MASTER_USB3 7
#define MSM8996_MASTER_IPA 8
#define MSM8996_MASTER_UFS 9
#define MSM8996_MASTER_AMPSS_M0 10
#define MSM8996_MASTER_GRAPHICS_3D 11
#define MSM8996_MASTER_MNOC_BIMC 12
#define MSM8996_MASTER_SNOC_BIMC 13
#define MSM8996_MASTER_SNOC_CNOC 14
#define MSM8996_MASTER_QDSS_DAP 15
#define MSM8996_MASTER_CNOC_MNOC_MMSS_CFG 16
#define MSM8996_MASTER_CNOC_MNOC_CFG 17
#define MSM8996_MASTER_CPP 18
#define MSM8996_MASTER_JPEG 19
#define MSM8996_MASTER_MDP_PORT0 20
#define MSM8996_MASTER_MDP_PORT1 21
#define MSM8996_MASTER_ROTATOR 22
#define MSM8996_MASTER_VIDEO_P0 23
#define MSM8996_MASTER_VFE 24
#define MSM8996_MASTER_SNOC_VMEM 25
#define MSM8996_MASTER_VIDEO_P0_OCMEM 26
#define MSM8996_MASTER_SNOC_PNOC 27
#define MSM8996_MASTER_SDCC_1 28
#define MSM8996_MASTER_SDCC_2 29
#define MSM8996_MASTER_SDCC_4 30
#define MSM8996_MASTER_USB_HS 31
#define MSM8996_MASTER_BLSP_1 32
#define MSM8996_MASTER_BLSP_2 33
#define MSM8996_MASTER_TSIF 34
#define MSM8996_MASTER_HMSS 35
#define MSM8996_MASTER_QDSS_BAM 36
#define MSM8996_MASTER_SNOC_CFG 37
#define MSM8996_MASTER_BIMC_SNOC_0 38
#define MSM8996_MASTER_BIMC_SNOC_1 39
#define MSM8996_MASTER_A0NOC_SNOC 40
#define MSM8996_MASTER_A1NOC_SNOC 41
#define MSM8996_MASTER_A2NOC_SNOC 42
#define MSM8996_MASTER_QDSS_ETR 43
#define MSM8996_SLAVE_A0NOC_SNOC 44
#define MSM8996_SLAVE_A1NOC_SNOC 45
#define MSM8996_SLAVE_A2NOC_SNOC 46
#define MSM8996_SLAVE_EBI_CH0 47
#define MSM8996_SLAVE_HMSS_L3 48
#define MSM8996_SLAVE_BIMC_SNOC_0 49
#define MSM8996_SLAVE_BIMC_SNOC_1 50
#define MSM8996_SLAVE_CNOC_A1NOC 51
#define MSM8996_SLAVE_CLK_CTL 52
#define MSM8996_SLAVE_TCSR 53
#define MSM8996_SLAVE_TLMM 54
#define MSM8996_SLAVE_CRYPTO_0_CFG 55
#define MSM8996_SLAVE_MPM 56
#define MSM8996_SLAVE_PIMEM_CFG 57
#define MSM8996_SLAVE_IMEM_CFG 58
#define MSM8996_SLAVE_MESSAGE_RAM 59
#define MSM8996_SLAVE_BIMC_CFG 60
#define MSM8996_SLAVE_PMIC_ARB 61
#define MSM8996_SLAVE_PRNG 62
#define MSM8996_SLAVE_DCC_CFG 63
#define MSM8996_SLAVE_RBCPR_MX 64
#define MSM8996_SLAVE_QDSS_CFG 65
#define MSM8996_SLAVE_RBCPR_CX 66
#define MSM8996_SLAVE_QDSS_RBCPR_APU_CFG 67
#define MSM8996_SLAVE_CNOC_MNOC_CFG 68
#define MSM8996_SLAVE_SNOC_CFG 69
#define MSM8996_SLAVE_SNOC_MPU_CFG 70
#define MSM8996_SLAVE_EBI1_PHY_CFG 71
#define MSM8996_SLAVE_A0NOC_CFG 72
#define MSM8996_SLAVE_PCIE_1_CFG 73
#define MSM8996_SLAVE_PCIE_2_CFG 74
#define MSM8996_SLAVE_PCIE_0_CFG 75
#define MSM8996_SLAVE_PCIE20_AHB2PHY 76
#define MSM8996_SLAVE_A0NOC_MPU_CFG 77
#define MSM8996_SLAVE_UFS_CFG 78
#define MSM8996_SLAVE_A1NOC_CFG 79
#define MSM8996_SLAVE_A1NOC_MPU_CFG 80
#define MSM8996_SLAVE_A2NOC_CFG 81
#define MSM8996_SLAVE_A2NOC_MPU_CFG 82
#define MSM8996_SLAVE_SSC_CFG 83
#define MSM8996_SLAVE_A0NOC_SMMU_CFG 84
#define MSM8996_SLAVE_A1NOC_SMMU_CFG 85
#define MSM8996_SLAVE_A2NOC_SMMU_CFG 86
#define MSM8996_SLAVE_LPASS_SMMU_CFG 87
#define MSM8996_SLAVE_CNOC_MNOC_MMSS_CFG 88
#define MSM8996_SLAVE_MMAGIC_CFG 89
#define MSM8996_SLAVE_CPR_CFG 90
#define MSM8996_SLAVE_MISC_CFG 91
#define MSM8996_SLAVE_VENUS_THROTTLE_CFG 92
#define MSM8996_SLAVE_VENUS_CFG 93
#define MSM8996_SLAVE_VMEM_CFG 94
#define MSM8996_SLAVE_DSA_CFG 95
#define MSM8996_SLAVE_MMSS_CLK_CFG 96
#define MSM8996_SLAVE_DSA_MPU_CFG 97
#define MSM8996_SLAVE_MNOC_MPU_CFG 98
#define MSM8996_SLAVE_DISPLAY_CFG 99
#define MSM8996_SLAVE_DISPLAY_THROTTLE_CFG 100
#define MSM8996_SLAVE_CAMERA_CFG 101
#define MSM8996_SLAVE_CAMERA_THROTTLE_CFG 102
#define MSM8996_SLAVE_GRAPHICS_3D_CFG 103
#define MSM8996_SLAVE_SMMU_MDP_CFG 104
#define MSM8996_SLAVE_SMMU_ROTATOR_CFG 105
#define MSM8996_SLAVE_SMMU_VENUS_CFG 106
#define MSM8996_SLAVE_SMMU_CPP_CFG 107
#define MSM8996_SLAVE_SMMU_JPEG_CFG 108
#define MSM8996_SLAVE_SMMU_VFE_CFG 109
#define MSM8996_SLAVE_MNOC_BIMC 110
#define MSM8996_SLAVE_VMEM 111
#define MSM8996_SLAVE_SERVICE_MNOC 112
#define MSM8996_SLAVE_PNOC_A1NOC 113
#define MSM8996_SLAVE_USB_HS 114
#define MSM8996_SLAVE_SDCC_2 115
#define MSM8996_SLAVE_SDCC_4 116
#define MSM8996_SLAVE_TSIF 117
#define MSM8996_SLAVE_BLSP_2 118
#define MSM8996_SLAVE_SDCC_1 119
#define MSM8996_SLAVE_BLSP_1 120
#define MSM8996_SLAVE_PDM 121
#define MSM8996_SLAVE_AHB2PHY 122
#define MSM8996_SLAVE_APPSS 123
#define MSM8996_SLAVE_LPASS 124
#define MSM8996_SLAVE_USB3 125
#define MSM8996_SLAVE_SNOC_BIMC 126
#define MSM8996_SLAVE_SNOC_CNOC 127
#define MSM8996_SLAVE_OCIMEM 128
#define MSM8996_SLAVE_PIMEM 129
#define MSM8996_SLAVE_SNOC_VMEM 130
#define MSM8996_SLAVE_SNOC_PNOC 131
#define MSM8996_SLAVE_QDSS_STM 132
#define MSM8996_SLAVE_PCIE_0 133
#define MSM8996_SLAVE_PCIE_1 134
#define MSM8996_SLAVE_PCIE_2 135
#define MSM8996_SLAVE_SERVICE_SNOC 136
#endif /* __DRIVERS_INTERCONNECT_QCOM_MSM8996_H__ */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,294 @@
// 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>
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/interconnect-provider.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <dt-bindings/interconnect/qcom,osm-l3.h>
#define LUT_MAX_ENTRIES 40U
#define LUT_SRC GENMASK(31, 30)
#define LUT_L_VAL GENMASK(7, 0)
#define CLK_HW_DIV 2
/* OSM Register offsets */
#define REG_ENABLE 0x0
#define OSM_LUT_ROW_SIZE 32
#define OSM_REG_FREQ_LUT 0x110
#define OSM_REG_PERF_STATE 0x920
/* EPSS Register offsets */
#define EPSS_LUT_ROW_SIZE 4
#define EPSS_REG_L3_VOTE 0x90
#define EPSS_REG_FREQ_LUT 0x100
#define EPSS_REG_PERF_STATE 0x320
#define to_osm_l3_provider(_provider) \
container_of(_provider, struct qcom_osm_l3_icc_provider, provider)
struct qcom_osm_l3_icc_provider {
void __iomem *base;
unsigned int max_state;
unsigned int reg_perf_state;
unsigned long lut_tables[LUT_MAX_ENTRIES];
struct icc_provider provider;
};
/**
* struct qcom_osm_l3_node - Qualcomm specific interconnect nodes
* @name: the node name used in debugfs
* @buswidth: width of the interconnect between a node and the bus
*/
struct qcom_osm_l3_node {
const char *name;
u16 buswidth;
};
struct qcom_osm_l3_desc {
const struct qcom_osm_l3_node * const *nodes;
size_t num_nodes;
unsigned int lut_row_size;
unsigned int reg_freq_lut;
unsigned int reg_perf_state;
};
#define DEFINE_QNODE(_name, _buswidth) \
static const struct qcom_osm_l3_node _name = { \
.name = #_name, \
.buswidth = _buswidth, \
}
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_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,
[SLAVE_EPSS_L3_SHARED] = &epss_l3_slave,
};
static const struct qcom_osm_l3_desc osm_l3 = {
.nodes = osm_l3_nodes,
.num_nodes = ARRAY_SIZE(osm_l3_nodes),
.lut_row_size = OSM_LUT_ROW_SIZE,
.reg_freq_lut = OSM_REG_FREQ_LUT,
.reg_perf_state = OSM_REG_PERF_STATE,
};
static const struct qcom_osm_l3_desc epss_l3_perf_state = {
.nodes = epss_l3_nodes,
.num_nodes = ARRAY_SIZE(epss_l3_nodes),
.lut_row_size = EPSS_LUT_ROW_SIZE,
.reg_freq_lut = EPSS_REG_FREQ_LUT,
.reg_perf_state = EPSS_REG_PERF_STATE,
};
static const struct qcom_osm_l3_desc epss_l3_l3_vote = {
.nodes = epss_l3_nodes,
.num_nodes = ARRAY_SIZE(epss_l3_nodes),
.lut_row_size = EPSS_LUT_ROW_SIZE,
.reg_freq_lut = EPSS_REG_FREQ_LUT,
.reg_perf_state = EPSS_REG_L3_VOTE,
};
static int qcom_osm_l3_set(struct icc_node *src, struct icc_node *dst)
{
struct qcom_osm_l3_icc_provider *qp;
struct icc_provider *provider;
const struct qcom_osm_l3_node *qn;
unsigned int index;
u64 rate;
qn = src->data;
provider = src->provider;
qp = to_osm_l3_provider(provider);
rate = icc_units_to_bps(dst->peak_bw);
do_div(rate, qn->buswidth);
for (index = 0; index < qp->max_state - 1; index++) {
if (qp->lut_tables[index] >= rate)
break;
}
writel_relaxed(index, qp->base + qp->reg_perf_state);
return 0;
}
static void qcom_osm_l3_remove(struct platform_device *pdev)
{
struct qcom_osm_l3_icc_provider *qp = platform_get_drvdata(pdev);
icc_provider_deregister(&qp->provider);
icc_nodes_remove(&qp->provider);
}
static int qcom_osm_l3_probe(struct platform_device *pdev)
{
u32 info, src, lval, i, prev_freq = 0, freq;
static unsigned long hw_rate, xo_rate;
struct qcom_osm_l3_icc_provider *qp;
const struct qcom_osm_l3_desc *desc;
struct icc_onecell_data *data;
struct icc_provider *provider;
const struct qcom_osm_l3_node * const *qnodes;
struct icc_node *node;
size_t num_nodes;
struct clk *clk;
int ret;
clk = clk_get(&pdev->dev, "xo");
if (IS_ERR(clk))
return PTR_ERR(clk);
xo_rate = clk_get_rate(clk);
clk_put(clk);
clk = clk_get(&pdev->dev, "alternate");
if (IS_ERR(clk))
return PTR_ERR(clk);
hw_rate = clk_get_rate(clk) / CLK_HW_DIV;
clk_put(clk);
qp = devm_kzalloc(&pdev->dev, sizeof(*qp), GFP_KERNEL);
if (!qp)
return -ENOMEM;
qp->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(qp->base))
return PTR_ERR(qp->base);
/* HW should be in enabled state to proceed */
if (!(readl_relaxed(qp->base + REG_ENABLE) & 0x1)) {
dev_err(&pdev->dev, "error hardware not enabled\n");
return -ENODEV;
}
desc = device_get_match_data(&pdev->dev);
if (!desc)
return -EINVAL;
qp->reg_perf_state = desc->reg_perf_state;
for (i = 0; i < LUT_MAX_ENTRIES; i++) {
info = readl_relaxed(qp->base + desc->reg_freq_lut +
i * desc->lut_row_size);
src = FIELD_GET(LUT_SRC, info);
lval = FIELD_GET(LUT_L_VAL, info);
if (src)
freq = xo_rate * lval;
else
freq = hw_rate;
/* Two of the same frequencies signify end of table */
if (i > 0 && prev_freq == freq)
break;
dev_dbg(&pdev->dev, "index=%d freq=%d\n", i, freq);
qp->lut_tables[i] = freq;
prev_freq = freq;
}
qp->max_state = i;
qnodes = desc->nodes;
num_nodes = desc->num_nodes;
data = devm_kzalloc(&pdev->dev, struct_size(data, nodes, num_nodes), GFP_KERNEL);
if (!data)
return -ENOMEM;
data->num_nodes = num_nodes;
provider = &qp->provider;
provider->dev = &pdev->dev;
provider->set = qcom_osm_l3_set;
provider->aggregate = icc_std_aggregate;
provider->xlate = of_icc_xlate_onecell;
provider->data = data;
icc_provider_init(provider);
/* Create nodes */
for (i = 0; i < num_nodes; i++) {
node = icc_node_create_dyn();
if (IS_ERR(node)) {
ret = PTR_ERR(node);
goto err;
}
ret = icc_node_set_name(node, provider, qnodes[i]->name);
if (ret) {
icc_node_destroy(node->id);
goto err;
}
/* Cast away const and add it back in qcom_osm_l3_set() */
node->data = (void *)qnodes[i];
icc_node_add(node, provider);
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;
platform_set_drvdata(pdev, qp);
return 0;
err:
icc_nodes_remove(provider);
return ret;
}
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 },
{ .compatible = "qcom,sm8150-osm-l3", .data = &osm_l3 },
{ .compatible = "qcom,sc8180x-osm-l3", .data = &osm_l3 },
{ .compatible = "qcom,sm8250-epss-l3", .data = &epss_l3_perf_state },
{ }
};
MODULE_DEVICE_TABLE(of, osm_l3_of_match);
static struct platform_driver osm_l3_driver = {
.probe = qcom_osm_l3_probe,
.remove = qcom_osm_l3_remove,
.driver = {
.name = "osm-l3",
.of_match_table = osm_l3_of_match,
.sync_state = icc_sync_state,
},
};
module_platform_driver(osm_l3_driver);
MODULE_DESCRIPTION("Qualcomm OSM L3 interconnect driver");
MODULE_LICENSE("GPL v2");
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,915 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Qualcomm SDX55 interconnect driver
* Author: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
*
* Copyright (c) 2021, Linaro Ltd.
*
*/
#include <linux/device.h>
#include <linux/interconnect.h>
#include <linux/interconnect-provider.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <dt-bindings/interconnect/qcom,sdx55.h>
#include "bcm-voter.h"
#include "icc-rpmh.h"
static struct qcom_icc_node llcc_mc;
static struct qcom_icc_node acm_tcu;
static struct qcom_icc_node qnm_snoc_gc;
static struct qcom_icc_node xm_apps_rdwr;
static struct qcom_icc_node qhm_audio;
static struct qcom_icc_node qhm_blsp1;
static struct qcom_icc_node qhm_qdss_bam;
static struct qcom_icc_node qhm_qpic;
static struct qcom_icc_node qhm_snoc_cfg;
static struct qcom_icc_node qhm_spmi_fetcher1;
static struct qcom_icc_node qnm_aggre_noc;
static struct qcom_icc_node qnm_ipa;
static struct qcom_icc_node qnm_memnoc;
static struct qcom_icc_node qnm_memnoc_pcie;
static struct qcom_icc_node qxm_crypto;
static struct qcom_icc_node xm_emac;
static struct qcom_icc_node xm_ipa2pcie_slv;
static struct qcom_icc_node xm_pcie;
static struct qcom_icc_node xm_qdss_etr;
static struct qcom_icc_node xm_sdc1;
static struct qcom_icc_node xm_usb3;
static struct qcom_icc_node ebi;
static struct qcom_icc_node qns_llcc;
static struct qcom_icc_node qns_memnoc_snoc;
static struct qcom_icc_node qns_sys_pcie;
static struct qcom_icc_node qhs_aop;
static struct qcom_icc_node qhs_aoss;
static struct qcom_icc_node qhs_apss;
static struct qcom_icc_node qhs_audio;
static struct qcom_icc_node qhs_blsp1;
static struct qcom_icc_node qhs_clk_ctl;
static struct qcom_icc_node qhs_crypto0_cfg;
static struct qcom_icc_node qhs_ddrss_cfg;
static struct qcom_icc_node qhs_ecc_cfg;
static struct qcom_icc_node qhs_emac_cfg;
static struct qcom_icc_node qhs_imem_cfg;
static struct qcom_icc_node qhs_ipa;
static struct qcom_icc_node qhs_mss_cfg;
static struct qcom_icc_node qhs_pcie_parf;
static struct qcom_icc_node qhs_pdm;
static struct qcom_icc_node qhs_prng;
static struct qcom_icc_node qhs_qdss_cfg;
static struct qcom_icc_node qhs_qpic;
static struct qcom_icc_node qhs_sdc1;
static struct qcom_icc_node qhs_snoc_cfg;
static struct qcom_icc_node qhs_spmi_fetcher;
static struct qcom_icc_node qhs_spmi_vgi_coex;
static struct qcom_icc_node qhs_tcsr;
static struct qcom_icc_node qhs_tlmm;
static struct qcom_icc_node qhs_usb3;
static struct qcom_icc_node qhs_usb3_phy;
static struct qcom_icc_node qns_aggre_noc;
static struct qcom_icc_node qns_snoc_memnoc;
static struct qcom_icc_node qxs_imem;
static struct qcom_icc_node srvc_snoc;
static struct qcom_icc_node xs_pcie;
static struct qcom_icc_node xs_qdss_stm;
static struct qcom_icc_node xs_sys_tcu_cfg;
static struct qcom_icc_node llcc_mc = {
.name = "llcc_mc",
.channels = 4,
.buswidth = 4,
.num_links = 1,
.link_nodes = { &ebi },
};
static struct qcom_icc_node acm_tcu = {
.name = "acm_tcu",
.channels = 1,
.buswidth = 8,
.num_links = 3,
.link_nodes = { &qns_llcc,
&qns_memnoc_snoc,
&qns_sys_pcie },
};
static struct qcom_icc_node qnm_snoc_gc = {
.name = "qnm_snoc_gc",
.channels = 1,
.buswidth = 8,
.num_links = 1,
.link_nodes = { &qns_llcc },
};
static struct qcom_icc_node xm_apps_rdwr = {
.name = "xm_apps_rdwr",
.channels = 1,
.buswidth = 16,
.num_links = 3,
.link_nodes = { &qns_llcc,
&qns_memnoc_snoc,
&qns_sys_pcie },
};
static struct qcom_icc_node qhm_audio = {
.name = "qhm_audio",
.channels = 1,
.buswidth = 4,
.num_links = 1,
.link_nodes = { &qns_aggre_noc },
};
static struct qcom_icc_node qhm_blsp1 = {
.name = "qhm_blsp1",
.channels = 1,
.buswidth = 4,
.num_links = 1,
.link_nodes = { &qns_aggre_noc },
};
static struct qcom_icc_node qhm_qdss_bam = {
.name = "qhm_qdss_bam",
.channels = 1,
.buswidth = 4,
.num_links = 28,
.link_nodes = { &qhs_snoc_cfg,
&qhs_emac_cfg,
&qhs_usb3,
&qhs_tlmm,
&qhs_spmi_fetcher,
&qhs_qdss_cfg,
&qhs_pdm,
&qns_snoc_memnoc,
&qhs_tcsr,
&qhs_ddrss_cfg,
&qhs_spmi_vgi_coex,
&qhs_qpic,
&qxs_imem,
&qhs_ipa,
&qhs_usb3_phy,
&qhs_aop,
&qhs_blsp1,
&qhs_sdc1,
&qhs_mss_cfg,
&qhs_pcie_parf,
&qhs_ecc_cfg,
&qhs_audio,
&qhs_aoss,
&qhs_prng,
&qhs_crypto0_cfg,
&xs_sys_tcu_cfg,
&qhs_clk_ctl,
&qhs_imem_cfg },
};
static struct qcom_icc_node qhm_qpic = {
.name = "qhm_qpic",
.channels = 1,
.buswidth = 4,
.num_links = 5,
.link_nodes = { &qhs_aoss,
&qhs_ipa,
&qns_aggre_noc,
&qhs_aop,
&qhs_audio },
};
static struct qcom_icc_node qhm_snoc_cfg = {
.name = "qhm_snoc_cfg",
.channels = 1,
.buswidth = 4,
.num_links = 1,
.link_nodes = { &srvc_snoc },
};
static struct qcom_icc_node qhm_spmi_fetcher1 = {
.name = "qhm_spmi_fetcher1",
.channels = 1,
.buswidth = 4,
.num_links = 3,
.link_nodes = { &qhs_aoss,
&qns_aggre_noc,
&qhs_aop },
};
static struct qcom_icc_node qnm_aggre_noc = {
.name = "qnm_aggre_noc",
.channels = 1,
.buswidth = 8,
.num_links = 30,
.link_nodes = { &xs_pcie,
&qhs_snoc_cfg,
&qhs_sdc1,
&qhs_tlmm,
&qhs_spmi_fetcher,
&qhs_qdss_cfg,
&qhs_pdm,
&qns_snoc_memnoc,
&qhs_tcsr,
&qhs_ddrss_cfg,
&qhs_spmi_vgi_coex,
&xs_qdss_stm,
&qhs_qpic,
&qxs_imem,
&qhs_ipa,
&qhs_usb3_phy,
&qhs_aop,
&qhs_blsp1,
&qhs_usb3,
&qhs_mss_cfg,
&qhs_pcie_parf,
&qhs_ecc_cfg,
&qhs_apss,
&qhs_audio,
&qhs_aoss,
&qhs_prng,
&qhs_crypto0_cfg,
&xs_sys_tcu_cfg,
&qhs_clk_ctl,
&qhs_imem_cfg },
};
static struct qcom_icc_node qnm_ipa = {
.name = "qnm_ipa",
.channels = 1,
.buswidth = 8,
.num_links = 27,
.link_nodes = { &qhs_snoc_cfg,
&qhs_emac_cfg,
&qhs_usb3,
&qhs_aoss,
&qhs_spmi_fetcher,
&qhs_qdss_cfg,
&qhs_pdm,
&qns_snoc_memnoc,
&qhs_tcsr,
&qhs_ddrss_cfg,
&xs_qdss_stm,
&qhs_qpic,
&qxs_imem,
&qhs_ipa,
&qhs_usb3_phy,
&qhs_aop,
&qhs_blsp1,
&qhs_sdc1,
&qhs_mss_cfg,
&qhs_pcie_parf,
&qhs_ecc_cfg,
&qhs_audio,
&qhs_tlmm,
&qhs_prng,
&qhs_crypto0_cfg,
&qhs_clk_ctl,
&qhs_imem_cfg },
};
static struct qcom_icc_node qnm_memnoc = {
.name = "qnm_memnoc",
.channels = 1,
.buswidth = 8,
.num_links = 29,
.link_nodes = { &qhs_snoc_cfg,
&qhs_emac_cfg,
&qhs_usb3,
&qhs_tlmm,
&qhs_spmi_fetcher,
&qhs_qdss_cfg,
&qhs_pdm,
&qhs_tcsr,
&qhs_ddrss_cfg,
&qhs_spmi_vgi_coex,
&xs_qdss_stm,
&qhs_qpic,
&qxs_imem,
&qhs_ipa,
&qhs_usb3_phy,
&qhs_aop,
&qhs_blsp1,
&qhs_sdc1,
&qhs_mss_cfg,
&qhs_pcie_parf,
&qhs_ecc_cfg,
&qhs_apss,
&qhs_audio,
&qhs_aoss,
&qhs_prng,
&qhs_crypto0_cfg,
&xs_sys_tcu_cfg,
&qhs_clk_ctl,
&qhs_imem_cfg },
};
static struct qcom_icc_node qnm_memnoc_pcie = {
.name = "qnm_memnoc_pcie",
.channels = 1,
.buswidth = 8,
.num_links = 1,
.link_nodes = { &xs_pcie },
};
static struct qcom_icc_node qxm_crypto = {
.name = "qxm_crypto",
.channels = 1,
.buswidth = 8,
.num_links = 3,
.link_nodes = { &qhs_aoss,
&qns_aggre_noc,
&qhs_aop },
};
static struct qcom_icc_node xm_emac = {
.name = "xm_emac",
.channels = 1,
.buswidth = 8,
.num_links = 1,
.link_nodes = { &qns_aggre_noc },
};
static struct qcom_icc_node xm_ipa2pcie_slv = {
.name = "xm_ipa2pcie_slv",
.channels = 1,
.buswidth = 8,
.num_links = 1,
.link_nodes = { &xs_pcie },
};
static struct qcom_icc_node xm_pcie = {
.name = "xm_pcie",
.channels = 1,
.buswidth = 8,
.num_links = 1,
.link_nodes = { &qns_aggre_noc },
};
static struct qcom_icc_node xm_qdss_etr = {
.name = "xm_qdss_etr",
.channels = 1,
.buswidth = 8,
.num_links = 28,
.link_nodes = { &qhs_snoc_cfg,
&qhs_emac_cfg,
&qhs_usb3,
&qhs_aoss,
&qhs_spmi_fetcher,
&qhs_qdss_cfg,
&qhs_pdm,
&qns_snoc_memnoc,
&qhs_tcsr,
&qhs_ddrss_cfg,
&qhs_spmi_vgi_coex,
&qhs_qpic,
&qxs_imem,
&qhs_ipa,
&qhs_usb3_phy,
&qhs_aop,
&qhs_blsp1,
&qhs_sdc1,
&qhs_mss_cfg,
&qhs_pcie_parf,
&qhs_ecc_cfg,
&qhs_audio,
&qhs_aoss,
&qhs_prng,
&qhs_crypto0_cfg,
&xs_sys_tcu_cfg,
&qhs_clk_ctl,
&qhs_imem_cfg },
};
static struct qcom_icc_node xm_sdc1 = {
.name = "xm_sdc1",
.channels = 1,
.buswidth = 8,
.num_links = 5,
.link_nodes = { &qhs_aoss,
&qhs_ipa,
&qns_aggre_noc,
&qhs_aop,
&qhs_audio },
};
static struct qcom_icc_node xm_usb3 = {
.name = "xm_usb3",
.channels = 1,
.buswidth = 8,
.num_links = 1,
.link_nodes = { &qns_aggre_noc },
};
static struct qcom_icc_node ebi = {
.name = "ebi",
.channels = 1,
.buswidth = 4,
};
static struct qcom_icc_node qns_llcc = {
.name = "qns_llcc",
.channels = 1,
.buswidth = 16,
.num_links = 1,
.link_nodes = { &ebi },
};
static struct qcom_icc_node qns_memnoc_snoc = {
.name = "qns_memnoc_snoc",
.channels = 1,
.buswidth = 8,
.num_links = 1,
.link_nodes = { &qnm_memnoc },
};
static struct qcom_icc_node qns_sys_pcie = {
.name = "qns_sys_pcie",
.channels = 1,
.buswidth = 8,
.num_links = 1,
.link_nodes = { &qnm_memnoc_pcie },
};
static struct qcom_icc_node qhs_aop = {
.name = "qhs_aop",
.channels = 1,
.buswidth = 4,
};
static struct qcom_icc_node qhs_aoss = {
.name = "qhs_aoss",
.channels = 1,
.buswidth = 4,
};
static struct qcom_icc_node qhs_apss = {
.name = "qhs_apss",
.channels = 1,
.buswidth = 4,
};
static struct qcom_icc_node qhs_audio = {
.name = "qhs_audio",
.channels = 1,
.buswidth = 4,
};
static struct qcom_icc_node qhs_blsp1 = {
.name = "qhs_blsp1",
.channels = 1,
.buswidth = 4,
};
static struct qcom_icc_node qhs_clk_ctl = {
.name = "qhs_clk_ctl",
.channels = 1,
.buswidth = 4,
};
static struct qcom_icc_node qhs_crypto0_cfg = {
.name = "qhs_crypto0_cfg",
.channels = 1,
.buswidth = 4,
};
static struct qcom_icc_node qhs_ddrss_cfg = {
.name = "qhs_ddrss_cfg",
.channels = 1,
.buswidth = 4,
};
static struct qcom_icc_node qhs_ecc_cfg = {
.name = "qhs_ecc_cfg",
.channels = 1,
.buswidth = 4,
};
static struct qcom_icc_node qhs_emac_cfg = {
.name = "qhs_emac_cfg",
.channels = 1,
.buswidth = 4,
};
static struct qcom_icc_node qhs_imem_cfg = {
.name = "qhs_imem_cfg",
.channels = 1,
.buswidth = 4,
};
static struct qcom_icc_node qhs_ipa = {
.name = "qhs_ipa",
.channels = 1,
.buswidth = 4,
};
static struct qcom_icc_node qhs_mss_cfg = {
.name = "qhs_mss_cfg",
.channels = 1,
.buswidth = 4,
};
static struct qcom_icc_node qhs_pcie_parf = {
.name = "qhs_pcie_parf",
.channels = 1,
.buswidth = 4,
};
static struct qcom_icc_node qhs_pdm = {
.name = "qhs_pdm",
.channels = 1,
.buswidth = 4,
};
static struct qcom_icc_node qhs_prng = {
.name = "qhs_prng",
.channels = 1,
.buswidth = 4,
};
static struct qcom_icc_node qhs_qdss_cfg = {
.name = "qhs_qdss_cfg",
.channels = 1,
.buswidth = 4,
};
static struct qcom_icc_node qhs_qpic = {
.name = "qhs_qpic",
.channels = 1,
.buswidth = 4,
};
static struct qcom_icc_node qhs_sdc1 = {
.name = "qhs_sdc1",
.channels = 1,
.buswidth = 4,
};
static struct qcom_icc_node qhs_snoc_cfg = {
.name = "qhs_snoc_cfg",
.channels = 1,
.buswidth = 4,
.num_links = 1,
.link_nodes = { &qhm_snoc_cfg },
};
static struct qcom_icc_node qhs_spmi_fetcher = {
.name = "qhs_spmi_fetcher",
.channels = 1,
.buswidth = 4,
};
static struct qcom_icc_node qhs_spmi_vgi_coex = {
.name = "qhs_spmi_vgi_coex",
.channels = 1,
.buswidth = 4,
};
static struct qcom_icc_node qhs_tcsr = {
.name = "qhs_tcsr",
.channels = 1,
.buswidth = 4,
};
static struct qcom_icc_node qhs_tlmm = {
.name = "qhs_tlmm",
.channels = 1,
.buswidth = 4,
};
static struct qcom_icc_node qhs_usb3 = {
.name = "qhs_usb3",
.channels = 1,
.buswidth = 4,
};
static struct qcom_icc_node qhs_usb3_phy = {
.name = "qhs_usb3_phy",
.channels = 1,
.buswidth = 4,
};
static struct qcom_icc_node qns_aggre_noc = {
.name = "qns_aggre_noc",
.channels = 1,
.buswidth = 8,
.num_links = 1,
.link_nodes = { &qnm_aggre_noc },
};
static struct qcom_icc_node qns_snoc_memnoc = {
.name = "qns_snoc_memnoc",
.channels = 1,
.buswidth = 8,
.num_links = 1,
.link_nodes = { &qnm_snoc_gc },
};
static struct qcom_icc_node qxs_imem = {
.name = "qxs_imem",
.channels = 1,
.buswidth = 8,
};
static struct qcom_icc_node srvc_snoc = {
.name = "srvc_snoc",
.channels = 1,
.buswidth = 4,
};
static struct qcom_icc_node xs_pcie = {
.name = "xs_pcie",
.channels = 1,
.buswidth = 8,
};
static struct qcom_icc_node xs_qdss_stm = {
.name = "xs_qdss_stm",
.channels = 1,
.buswidth = 4,
};
static struct qcom_icc_node xs_sys_tcu_cfg = {
.name = "xs_sys_tcu_cfg",
.channels = 1,
.buswidth = 8,
};
static struct qcom_icc_bcm bcm_mc0 = {
.name = "MC0",
.keepalive = true,
.num_nodes = 1,
.nodes = { &ebi },
};
static struct qcom_icc_bcm bcm_sh0 = {
.name = "SH0",
.keepalive = true,
.num_nodes = 1,
.nodes = { &qns_llcc },
};
static struct qcom_icc_bcm bcm_ce0 = {
.name = "CE0",
.keepalive = false,
.num_nodes = 1,
.nodes = { &qxm_crypto },
};
static struct qcom_icc_bcm bcm_pn0 = {
.name = "PN0",
.keepalive = false,
.num_nodes = 1,
.nodes = { &qhm_snoc_cfg },
};
static struct qcom_icc_bcm bcm_sh3 = {
.name = "SH3",
.keepalive = false,
.num_nodes = 1,
.nodes = { &xm_apps_rdwr },
};
static struct qcom_icc_bcm bcm_sh4 = {
.name = "SH4",
.keepalive = false,
.num_nodes = 2,
.nodes = { &qns_memnoc_snoc, &qns_sys_pcie },
};
static struct qcom_icc_bcm bcm_sn0 = {
.name = "SN0",
.keepalive = true,
.num_nodes = 1,
.nodes = { &qns_snoc_memnoc },
};
static struct qcom_icc_bcm bcm_sn1 = {
.name = "SN1",
.keepalive = false,
.num_nodes = 1,
.nodes = { &qxs_imem },
};
static struct qcom_icc_bcm bcm_pn1 = {
.name = "PN1",
.keepalive = false,
.num_nodes = 1,
.nodes = { &xm_sdc1 },
};
static struct qcom_icc_bcm bcm_pn2 = {
.name = "PN2",
.keepalive = false,
.num_nodes = 2,
.nodes = { &qhm_audio, &qhm_spmi_fetcher1 },
};
static struct qcom_icc_bcm bcm_sn3 = {
.name = "SN3",
.keepalive = false,
.num_nodes = 1,
.nodes = { &xs_qdss_stm },
};
static struct qcom_icc_bcm bcm_pn3 = {
.name = "PN3",
.keepalive = false,
.num_nodes = 2,
.nodes = { &qhm_blsp1, &qhm_qpic },
};
static struct qcom_icc_bcm bcm_sn4 = {
.name = "SN4",
.keepalive = false,
.num_nodes = 1,
.nodes = { &xs_sys_tcu_cfg },
};
static struct qcom_icc_bcm bcm_pn5 = {
.name = "PN5",
.keepalive = false,
.num_nodes = 1,
.nodes = { &qxm_crypto },
};
static struct qcom_icc_bcm bcm_sn6 = {
.name = "SN6",
.keepalive = false,
.num_nodes = 1,
.nodes = { &xs_pcie },
};
static struct qcom_icc_bcm bcm_sn7 = {
.name = "SN7",
.keepalive = false,
.num_nodes = 5,
.nodes = { &qnm_aggre_noc, &xm_emac, &xm_emac, &xm_usb3, &qns_aggre_noc },
};
static struct qcom_icc_bcm bcm_sn8 = {
.name = "SN8",
.keepalive = false,
.num_nodes = 2,
.nodes = { &qhm_qdss_bam, &xm_qdss_etr },
};
static struct qcom_icc_bcm bcm_sn9 = {
.name = "SN9",
.keepalive = false,
.num_nodes = 1,
.nodes = { &qnm_memnoc },
};
static struct qcom_icc_bcm bcm_sn10 = {
.name = "SN10",
.keepalive = false,
.num_nodes = 1,
.nodes = { &qnm_memnoc_pcie },
};
static struct qcom_icc_bcm bcm_sn11 = {
.name = "SN11",
.keepalive = false,
.num_nodes = 2,
.nodes = { &qnm_ipa, &xm_ipa2pcie_slv },
};
static struct qcom_icc_bcm * const mc_virt_bcms[] = {
&bcm_mc0,
};
static struct qcom_icc_node * const mc_virt_nodes[] = {
[MASTER_LLCC] = &llcc_mc,
[SLAVE_EBI_CH0] = &ebi,
};
static const struct qcom_icc_desc sdx55_mc_virt = {
.nodes = mc_virt_nodes,
.num_nodes = ARRAY_SIZE(mc_virt_nodes),
.bcms = mc_virt_bcms,
.num_bcms = ARRAY_SIZE(mc_virt_bcms),
};
static struct qcom_icc_bcm * const mem_noc_bcms[] = {
&bcm_sh0,
&bcm_sh3,
&bcm_sh4,
};
static struct qcom_icc_node * const mem_noc_nodes[] = {
[MASTER_TCU_0] = &acm_tcu,
[MASTER_SNOC_GC_MEM_NOC] = &qnm_snoc_gc,
[MASTER_AMPSS_M0] = &xm_apps_rdwr,
[SLAVE_LLCC] = &qns_llcc,
[SLAVE_MEM_NOC_SNOC] = &qns_memnoc_snoc,
[SLAVE_MEM_NOC_PCIE_SNOC] = &qns_sys_pcie,
};
static const struct qcom_icc_desc sdx55_mem_noc = {
.nodes = mem_noc_nodes,
.num_nodes = ARRAY_SIZE(mem_noc_nodes),
.bcms = mem_noc_bcms,
.num_bcms = ARRAY_SIZE(mem_noc_bcms),
};
static struct qcom_icc_bcm * const system_noc_bcms[] = {
&bcm_ce0,
&bcm_pn0,
&bcm_pn1,
&bcm_pn2,
&bcm_pn3,
&bcm_pn5,
&bcm_sn0,
&bcm_sn1,
&bcm_sn3,
&bcm_sn4,
&bcm_sn6,
&bcm_sn7,
&bcm_sn8,
&bcm_sn9,
&bcm_sn10,
&bcm_sn11,
};
static struct qcom_icc_node * const system_noc_nodes[] = {
[MASTER_AUDIO] = &qhm_audio,
[MASTER_BLSP_1] = &qhm_blsp1,
[MASTER_QDSS_BAM] = &qhm_qdss_bam,
[MASTER_QPIC] = &qhm_qpic,
[MASTER_SNOC_CFG] = &qhm_snoc_cfg,
[MASTER_SPMI_FETCHER] = &qhm_spmi_fetcher1,
[MASTER_ANOC_SNOC] = &qnm_aggre_noc,
[MASTER_IPA] = &qnm_ipa,
[MASTER_MEM_NOC_SNOC] = &qnm_memnoc,
[MASTER_MEM_NOC_PCIE_SNOC] = &qnm_memnoc_pcie,
[MASTER_CRYPTO_CORE_0] = &qxm_crypto,
[MASTER_EMAC] = &xm_emac,
[MASTER_IPA_PCIE] = &xm_ipa2pcie_slv,
[MASTER_PCIE] = &xm_pcie,
[MASTER_QDSS_ETR] = &xm_qdss_etr,
[MASTER_SDCC_1] = &xm_sdc1,
[MASTER_USB3] = &xm_usb3,
[SLAVE_AOP] = &qhs_aop,
[SLAVE_AOSS] = &qhs_aoss,
[SLAVE_APPSS] = &qhs_apss,
[SLAVE_AUDIO] = &qhs_audio,
[SLAVE_BLSP_1] = &qhs_blsp1,
[SLAVE_CLK_CTL] = &qhs_clk_ctl,
[SLAVE_CRYPTO_0_CFG] = &qhs_crypto0_cfg,
[SLAVE_CNOC_DDRSS] = &qhs_ddrss_cfg,
[SLAVE_ECC_CFG] = &qhs_ecc_cfg,
[SLAVE_EMAC_CFG] = &qhs_emac_cfg,
[SLAVE_IMEM_CFG] = &qhs_imem_cfg,
[SLAVE_IPA_CFG] = &qhs_ipa,
[SLAVE_CNOC_MSS] = &qhs_mss_cfg,
[SLAVE_PCIE_PARF] = &qhs_pcie_parf,
[SLAVE_PDM] = &qhs_pdm,
[SLAVE_PRNG] = &qhs_prng,
[SLAVE_QDSS_CFG] = &qhs_qdss_cfg,
[SLAVE_QPIC] = &qhs_qpic,
[SLAVE_SDCC_1] = &qhs_sdc1,
[SLAVE_SNOC_CFG] = &qhs_snoc_cfg,
[SLAVE_SPMI_FETCHER] = &qhs_spmi_fetcher,
[SLAVE_SPMI_VGI_COEX] = &qhs_spmi_vgi_coex,
[SLAVE_TCSR] = &qhs_tcsr,
[SLAVE_TLMM] = &qhs_tlmm,
[SLAVE_USB3] = &qhs_usb3,
[SLAVE_USB3_PHY_CFG] = &qhs_usb3_phy,
[SLAVE_ANOC_SNOC] = &qns_aggre_noc,
[SLAVE_SNOC_MEM_NOC_GC] = &qns_snoc_memnoc,
[SLAVE_OCIMEM] = &qxs_imem,
[SLAVE_SERVICE_SNOC] = &srvc_snoc,
[SLAVE_PCIE_0] = &xs_pcie,
[SLAVE_QDSS_STM] = &xs_qdss_stm,
[SLAVE_TCU] = &xs_sys_tcu_cfg,
};
static const struct qcom_icc_desc sdx55_system_noc = {
.nodes = system_noc_nodes,
.num_nodes = ARRAY_SIZE(system_noc_nodes),
.bcms = system_noc_bcms,
.num_bcms = ARRAY_SIZE(system_noc_bcms),
};
static const struct of_device_id qnoc_of_match[] = {
{ .compatible = "qcom,sdx55-mc-virt",
.data = &sdx55_mc_virt},
{ .compatible = "qcom,sdx55-mem-noc",
.data = &sdx55_mem_noc},
{ .compatible = "qcom,sdx55-system-noc",
.data = &sdx55_system_noc},
{ }
};
MODULE_DEVICE_TABLE(of, qnoc_of_match);
static struct platform_driver qnoc_driver = {
.probe = qcom_icc_rpmh_probe,
.remove = qcom_icc_rpmh_remove,
.driver = {
.name = "qnoc-sdx55",
.of_match_table = qnoc_of_match,
.sync_state = icc_sync_state,
},
};
module_platform_driver(qnoc_driver);
MODULE_DESCRIPTION("Qualcomm SDX55 NoC driver");
MODULE_AUTHOR("Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>");
MODULE_LICENSE("GPL v2");
@@ -0,0 +1,898 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/device.h>
#include <linux/interconnect.h>
#include <linux/interconnect-provider.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <dt-bindings/interconnect/qcom,sdx65.h>
#include "bcm-voter.h"
#include "icc-rpmh.h"
static struct qcom_icc_node llcc_mc;
static struct qcom_icc_node acm_tcu;
static struct qcom_icc_node qnm_snoc_gc;
static struct qcom_icc_node xm_apps_rdwr;
static struct qcom_icc_node qhm_audio;
static struct qcom_icc_node qhm_blsp1;
static struct qcom_icc_node qhm_qdss_bam;
static struct qcom_icc_node qhm_qpic;
static struct qcom_icc_node qhm_snoc_cfg;
static struct qcom_icc_node qhm_spmi_fetcher1;
static struct qcom_icc_node qnm_aggre_noc;
static struct qcom_icc_node qnm_ipa;
static struct qcom_icc_node qnm_memnoc;
static struct qcom_icc_node qnm_memnoc_pcie;
static struct qcom_icc_node qxm_crypto;
static struct qcom_icc_node xm_ipa2pcie_slv;
static struct qcom_icc_node xm_pcie;
static struct qcom_icc_node xm_qdss_etr;
static struct qcom_icc_node xm_sdc1;
static struct qcom_icc_node xm_usb3;
static struct qcom_icc_node ebi;
static struct qcom_icc_node qns_llcc;
static struct qcom_icc_node qns_memnoc_snoc;
static struct qcom_icc_node qns_sys_pcie;
static struct qcom_icc_node qhs_aoss;
static struct qcom_icc_node qhs_apss;
static struct qcom_icc_node qhs_audio;
static struct qcom_icc_node qhs_blsp1;
static struct qcom_icc_node qhs_clk_ctl;
static struct qcom_icc_node qhs_crypto0_cfg;
static struct qcom_icc_node qhs_ddrss_cfg;
static struct qcom_icc_node qhs_ecc_cfg;
static struct qcom_icc_node qhs_imem_cfg;
static struct qcom_icc_node qhs_ipa;
static struct qcom_icc_node qhs_mss_cfg;
static struct qcom_icc_node qhs_pcie_parf;
static struct qcom_icc_node qhs_pdm;
static struct qcom_icc_node qhs_prng;
static struct qcom_icc_node qhs_qdss_cfg;
static struct qcom_icc_node qhs_qpic;
static struct qcom_icc_node qhs_sdc1;
static struct qcom_icc_node qhs_snoc_cfg;
static struct qcom_icc_node qhs_spmi_fetcher;
static struct qcom_icc_node qhs_spmi_vgi_coex;
static struct qcom_icc_node qhs_tcsr;
static struct qcom_icc_node qhs_tlmm;
static struct qcom_icc_node qhs_usb3;
static struct qcom_icc_node qhs_usb3_phy;
static struct qcom_icc_node qns_aggre_noc;
static struct qcom_icc_node qns_snoc_memnoc;
static struct qcom_icc_node qxs_imem;
static struct qcom_icc_node srvc_snoc;
static struct qcom_icc_node xs_pcie;
static struct qcom_icc_node xs_qdss_stm;
static struct qcom_icc_node xs_sys_tcu_cfg;
static struct qcom_icc_node llcc_mc = {
.name = "llcc_mc",
.channels = 1,
.buswidth = 4,
.num_links = 1,
.link_nodes = { &ebi },
};
static struct qcom_icc_node acm_tcu = {
.name = "acm_tcu",
.channels = 1,
.buswidth = 8,
.num_links = 3,
.link_nodes = { &qns_llcc,
&qns_memnoc_snoc,
&qns_sys_pcie },
};
static struct qcom_icc_node qnm_snoc_gc = {
.name = "qnm_snoc_gc",
.channels = 1,
.buswidth = 16,
.num_links = 1,
.link_nodes = { &qns_llcc },
};
static struct qcom_icc_node xm_apps_rdwr = {
.name = "xm_apps_rdwr",
.channels = 1,
.buswidth = 16,
.num_links = 3,
.link_nodes = { &qns_llcc,
&qns_memnoc_snoc,
&qns_sys_pcie },
};
static struct qcom_icc_node qhm_audio = {
.name = "qhm_audio",
.channels = 1,
.buswidth = 4,
.num_links = 1,
.link_nodes = { &qns_aggre_noc },
};
static struct qcom_icc_node qhm_blsp1 = {
.name = "qhm_blsp1",
.channels = 1,
.buswidth = 4,
.num_links = 1,
.link_nodes = { &qns_aggre_noc },
};
static struct qcom_icc_node qhm_qdss_bam = {
.name = "qhm_qdss_bam",
.channels = 1,
.buswidth = 4,
.num_links = 26,
.link_nodes = { &qhs_aoss,
&qhs_audio,
&qhs_blsp1,
&qhs_clk_ctl,
&qhs_crypto0_cfg,
&qhs_ddrss_cfg,
&qhs_ecc_cfg,
&qhs_imem_cfg,
&qhs_ipa,
&qhs_mss_cfg,
&qhs_pcie_parf,
&qhs_pdm,
&qhs_prng,
&qhs_qdss_cfg,
&qhs_qpic,
&qhs_sdc1,
&qhs_snoc_cfg,
&qhs_spmi_fetcher,
&qhs_spmi_vgi_coex,
&qhs_tcsr,
&qhs_tlmm,
&qhs_usb3,
&qhs_usb3_phy,
&qns_snoc_memnoc,
&qxs_imem,
&xs_sys_tcu_cfg },
};
static struct qcom_icc_node qhm_qpic = {
.name = "qhm_qpic",
.channels = 1,
.buswidth = 4,
.num_links = 4,
.link_nodes = { &qhs_aoss,
&qhs_audio,
&qhs_ipa,
&qns_aggre_noc },
};
static struct qcom_icc_node qhm_snoc_cfg = {
.name = "qhm_snoc_cfg",
.channels = 1,
.buswidth = 4,
.num_links = 1,
.link_nodes = { &srvc_snoc },
};
static struct qcom_icc_node qhm_spmi_fetcher1 = {
.name = "qhm_spmi_fetcher1",
.channels = 1,
.buswidth = 4,
.num_links = 2,
.link_nodes = { &qhs_aoss,
&qns_aggre_noc },
};
static struct qcom_icc_node qnm_aggre_noc = {
.name = "qnm_aggre_noc",
.channels = 1,
.buswidth = 8,
.num_links = 29,
.link_nodes = { &qhs_aoss,
&qhs_apss,
&qhs_audio,
&qhs_blsp1,
&qhs_clk_ctl,
&qhs_crypto0_cfg,
&qhs_ddrss_cfg,
&qhs_ecc_cfg,
&qhs_imem_cfg,
&qhs_ipa,
&qhs_mss_cfg,
&qhs_pcie_parf,
&qhs_pdm,
&qhs_prng,
&qhs_qdss_cfg,
&qhs_qpic,
&qhs_sdc1,
&qhs_snoc_cfg,
&qhs_spmi_fetcher,
&qhs_spmi_vgi_coex,
&qhs_tcsr,
&qhs_tlmm,
&qhs_usb3,
&qhs_usb3_phy,
&qns_snoc_memnoc,
&qxs_imem,
&xs_pcie,
&xs_qdss_stm,
&xs_sys_tcu_cfg },
};
static struct qcom_icc_node qnm_ipa = {
.name = "qnm_ipa",
.channels = 1,
.buswidth = 8,
.num_links = 26,
.link_nodes = { &qhs_aoss,
&qhs_audio,
&qhs_blsp1,
&qhs_clk_ctl,
&qhs_crypto0_cfg,
&qhs_ddrss_cfg,
&qhs_ecc_cfg,
&qhs_imem_cfg,
&qhs_ipa,
&qhs_mss_cfg,
&qhs_pcie_parf,
&qhs_pdm,
&qhs_prng,
&qhs_qdss_cfg,
&qhs_qpic,
&qhs_sdc1,
&qhs_snoc_cfg,
&qhs_spmi_fetcher,
&qhs_tcsr,
&qhs_tlmm,
&qhs_usb3,
&qhs_usb3_phy,
&qns_snoc_memnoc,
&qxs_imem,
&xs_pcie,
&xs_qdss_stm },
};
static struct qcom_icc_node qnm_memnoc = {
.name = "qnm_memnoc",
.channels = 1,
.buswidth = 8,
.num_links = 27,
.link_nodes = { &qhs_aoss,
&qhs_apss,
&qhs_audio,
&qhs_blsp1,
&qhs_clk_ctl,
&qhs_crypto0_cfg,
&qhs_ddrss_cfg,
&qhs_ecc_cfg,
&qhs_imem_cfg,
&qhs_ipa,
&qhs_mss_cfg,
&qhs_pcie_parf,
&qhs_pdm,
&qhs_prng,
&qhs_qdss_cfg,
&qhs_qpic,
&qhs_sdc1,
&qhs_snoc_cfg,
&qhs_spmi_fetcher,
&qhs_spmi_vgi_coex,
&qhs_tcsr,
&qhs_tlmm,
&qhs_usb3,
&qhs_usb3_phy,
&qxs_imem,
&xs_qdss_stm,
&xs_sys_tcu_cfg },
};
static struct qcom_icc_node qnm_memnoc_pcie = {
.name = "qnm_memnoc_pcie",
.channels = 1,
.buswidth = 8,
.num_links = 1,
.link_nodes = { &xs_pcie },
};
static struct qcom_icc_node qxm_crypto = {
.name = "qxm_crypto",
.channels = 1,
.buswidth = 8,
.num_links = 2,
.link_nodes = { &qhs_aoss,
&qns_aggre_noc },
};
static struct qcom_icc_node xm_ipa2pcie_slv = {
.name = "xm_ipa2pcie_slv",
.channels = 1,
.buswidth = 8,
.num_links = 1,
.link_nodes = { &xs_pcie },
};
static struct qcom_icc_node xm_pcie = {
.name = "xm_pcie",
.channels = 1,
.buswidth = 8,
.num_links = 1,
.link_nodes = { &qns_aggre_noc },
};
static struct qcom_icc_node xm_qdss_etr = {
.name = "xm_qdss_etr",
.channels = 1,
.buswidth = 8,
.num_links = 26,
.link_nodes = { &qhs_aoss,
&qhs_audio,
&qhs_blsp1,
&qhs_clk_ctl,
&qhs_crypto0_cfg,
&qhs_ddrss_cfg,
&qhs_ecc_cfg,
&qhs_imem_cfg,
&qhs_ipa,
&qhs_mss_cfg,
&qhs_pcie_parf,
&qhs_pdm,
&qhs_prng,
&qhs_qdss_cfg,
&qhs_qpic,
&qhs_sdc1,
&qhs_snoc_cfg,
&qhs_spmi_fetcher,
&qhs_spmi_vgi_coex,
&qhs_tcsr,
&qhs_tlmm,
&qhs_usb3,
&qhs_usb3_phy,
&qns_snoc_memnoc,
&qxs_imem,
&xs_sys_tcu_cfg },
};
static struct qcom_icc_node xm_sdc1 = {
.name = "xm_sdc1",
.channels = 1,
.buswidth = 8,
.num_links = 4,
.link_nodes = { &qhs_aoss,
&qhs_audio,
&qhs_ipa,
&qns_aggre_noc },
};
static struct qcom_icc_node xm_usb3 = {
.name = "xm_usb3",
.channels = 1,
.buswidth = 8,
.num_links = 1,
.link_nodes = { &qns_aggre_noc },
};
static struct qcom_icc_node ebi = {
.name = "ebi",
.channels = 1,
.buswidth = 4,
};
static struct qcom_icc_node qns_llcc = {
.name = "qns_llcc",
.channels = 1,
.buswidth = 16,
.num_links = 1,
.link_nodes = { &llcc_mc },
};
static struct qcom_icc_node qns_memnoc_snoc = {
.name = "qns_memnoc_snoc",
.channels = 1,
.buswidth = 8,
.num_links = 1,
.link_nodes = { &qnm_memnoc },
};
static struct qcom_icc_node qns_sys_pcie = {
.name = "qns_sys_pcie",
.channels = 1,
.buswidth = 8,
.num_links = 1,
.link_nodes = { &qnm_memnoc_pcie },
};
static struct qcom_icc_node qhs_aoss = {
.name = "qhs_aoss",
.channels = 1,
.buswidth = 4,
};
static struct qcom_icc_node qhs_apss = {
.name = "qhs_apss",
.channels = 1,
.buswidth = 4,
};
static struct qcom_icc_node qhs_audio = {
.name = "qhs_audio",
.channels = 1,
.buswidth = 4,
};
static struct qcom_icc_node qhs_blsp1 = {
.name = "qhs_blsp1",
.channels = 1,
.buswidth = 4,
};
static struct qcom_icc_node qhs_clk_ctl = {
.name = "qhs_clk_ctl",
.channels = 1,
.buswidth = 4,
};
static struct qcom_icc_node qhs_crypto0_cfg = {
.name = "qhs_crypto0_cfg",
.channels = 1,
.buswidth = 4,
};
static struct qcom_icc_node qhs_ddrss_cfg = {
.name = "qhs_ddrss_cfg",
.channels = 1,
.buswidth = 4,
};
static struct qcom_icc_node qhs_ecc_cfg = {
.name = "qhs_ecc_cfg",
.channels = 1,
.buswidth = 4,
};
static struct qcom_icc_node qhs_imem_cfg = {
.name = "qhs_imem_cfg",
.channels = 1,
.buswidth = 4,
};
static struct qcom_icc_node qhs_ipa = {
.name = "qhs_ipa",
.channels = 1,
.buswidth = 4,
};
static struct qcom_icc_node qhs_mss_cfg = {
.name = "qhs_mss_cfg",
.channels = 1,
.buswidth = 4,
};
static struct qcom_icc_node qhs_pcie_parf = {
.name = "qhs_pcie_parf",
.channels = 1,
.buswidth = 4,
};
static struct qcom_icc_node qhs_pdm = {
.name = "qhs_pdm",
.channels = 1,
.buswidth = 4,
};
static struct qcom_icc_node qhs_prng = {
.name = "qhs_prng",
.channels = 1,
.buswidth = 4,
};
static struct qcom_icc_node qhs_qdss_cfg = {
.name = "qhs_qdss_cfg",
.channels = 1,
.buswidth = 4,
};
static struct qcom_icc_node qhs_qpic = {
.name = "qhs_qpic",
.channels = 1,
.buswidth = 4,
};
static struct qcom_icc_node qhs_sdc1 = {
.name = "qhs_sdc1",
.channels = 1,
.buswidth = 4,
};
static struct qcom_icc_node qhs_snoc_cfg = {
.name = "qhs_snoc_cfg",
.channels = 1,
.buswidth = 4,
.num_links = 1,
.link_nodes = { &qhm_snoc_cfg },
};
static struct qcom_icc_node qhs_spmi_fetcher = {
.name = "qhs_spmi_fetcher",
.channels = 1,
.buswidth = 4,
};
static struct qcom_icc_node qhs_spmi_vgi_coex = {
.name = "qhs_spmi_vgi_coex",
.channels = 1,
.buswidth = 4,
};
static struct qcom_icc_node qhs_tcsr = {
.name = "qhs_tcsr",
.channels = 1,
.buswidth = 4,
};
static struct qcom_icc_node qhs_tlmm = {
.name = "qhs_tlmm",
.channels = 1,
.buswidth = 4,
};
static struct qcom_icc_node qhs_usb3 = {
.name = "qhs_usb3",
.channels = 1,
.buswidth = 4,
};
static struct qcom_icc_node qhs_usb3_phy = {
.name = "qhs_usb3_phy",
.channels = 1,
.buswidth = 4,
};
static struct qcom_icc_node qns_aggre_noc = {
.name = "qns_aggre_noc",
.channels = 1,
.buswidth = 8,
.num_links = 1,
.link_nodes = { &qnm_aggre_noc },
};
static struct qcom_icc_node qns_snoc_memnoc = {
.name = "qns_snoc_memnoc",
.channels = 1,
.buswidth = 16,
.num_links = 1,
.link_nodes = { &qnm_snoc_gc },
};
static struct qcom_icc_node qxs_imem = {
.name = "qxs_imem",
.channels = 1,
.buswidth = 8,
};
static struct qcom_icc_node srvc_snoc = {
.name = "srvc_snoc",
.channels = 1,
.buswidth = 4,
};
static struct qcom_icc_node xs_pcie = {
.name = "xs_pcie",
.channels = 1,
.buswidth = 8,
};
static struct qcom_icc_node xs_qdss_stm = {
.name = "xs_qdss_stm",
.channels = 1,
.buswidth = 4,
};
static struct qcom_icc_node xs_sys_tcu_cfg = {
.name = "xs_sys_tcu_cfg",
.channels = 1,
.buswidth = 8,
};
static struct qcom_icc_bcm bcm_ce0 = {
.name = "CE0",
.keepalive = false,
.num_nodes = 1,
.nodes = { &qxm_crypto },
};
static struct qcom_icc_bcm bcm_mc0 = {
.name = "MC0",
.keepalive = true,
.num_nodes = 1,
.nodes = { &ebi },
};
static struct qcom_icc_bcm bcm_pn0 = {
.name = "PN0",
.keepalive = true,
.num_nodes = 26,
.nodes = { &qhm_snoc_cfg,
&qhs_aoss,
&qhs_apss,
&qhs_audio,
&qhs_blsp1,
&qhs_clk_ctl,
&qhs_crypto0_cfg,
&qhs_ddrss_cfg,
&qhs_ecc_cfg,
&qhs_imem_cfg,
&qhs_ipa,
&qhs_mss_cfg,
&qhs_pcie_parf,
&qhs_pdm,
&qhs_prng,
&qhs_qdss_cfg,
&qhs_qpic,
&qhs_sdc1,
&qhs_snoc_cfg,
&qhs_spmi_fetcher,
&qhs_spmi_vgi_coex,
&qhs_tcsr,
&qhs_tlmm,
&qhs_usb3,
&qhs_usb3_phy,
&srvc_snoc
},
};
static struct qcom_icc_bcm bcm_pn1 = {
.name = "PN1",
.keepalive = false,
.num_nodes = 1,
.nodes = { &xm_sdc1 },
};
static struct qcom_icc_bcm bcm_pn2 = {
.name = "PN2",
.keepalive = false,
.num_nodes = 2,
.nodes = { &qhm_audio, &qhm_spmi_fetcher1 },
};
static struct qcom_icc_bcm bcm_pn3 = {
.name = "PN3",
.keepalive = false,
.num_nodes = 2,
.nodes = { &qhm_blsp1, &qhm_qpic },
};
static struct qcom_icc_bcm bcm_pn4 = {
.name = "PN4",
.keepalive = false,
.num_nodes = 1,
.nodes = { &qxm_crypto },
};
static struct qcom_icc_bcm bcm_sh0 = {
.name = "SH0",
.keepalive = true,
.num_nodes = 1,
.nodes = { &qns_llcc },
};
static struct qcom_icc_bcm bcm_sh1 = {
.name = "SH1",
.keepalive = false,
.num_nodes = 1,
.nodes = { &qns_memnoc_snoc },
};
static struct qcom_icc_bcm bcm_sh3 = {
.name = "SH3",
.keepalive = false,
.num_nodes = 1,
.nodes = { &xm_apps_rdwr },
};
static struct qcom_icc_bcm bcm_sn0 = {
.name = "SN0",
.keepalive = true,
.num_nodes = 1,
.nodes = { &qns_snoc_memnoc },
};
static struct qcom_icc_bcm bcm_sn1 = {
.name = "SN1",
.keepalive = false,
.num_nodes = 1,
.nodes = { &qxs_imem },
};
static struct qcom_icc_bcm bcm_sn2 = {
.name = "SN2",
.keepalive = false,
.num_nodes = 1,
.nodes = { &xs_qdss_stm },
};
static struct qcom_icc_bcm bcm_sn3 = {
.name = "SN3",
.keepalive = false,
.num_nodes = 1,
.nodes = { &xs_sys_tcu_cfg },
};
static struct qcom_icc_bcm bcm_sn5 = {
.name = "SN5",
.keepalive = false,
.num_nodes = 1,
.nodes = { &xs_pcie },
};
static struct qcom_icc_bcm bcm_sn6 = {
.name = "SN6",
.keepalive = false,
.num_nodes = 2,
.nodes = { &qhm_qdss_bam, &xm_qdss_etr },
};
static struct qcom_icc_bcm bcm_sn7 = {
.name = "SN7",
.keepalive = false,
.num_nodes = 4,
.nodes = { &qnm_aggre_noc, &xm_pcie, &xm_usb3, &qns_aggre_noc },
};
static struct qcom_icc_bcm bcm_sn8 = {
.name = "SN8",
.keepalive = false,
.num_nodes = 1,
.nodes = { &qnm_memnoc },
};
static struct qcom_icc_bcm bcm_sn9 = {
.name = "SN9",
.keepalive = false,
.num_nodes = 1,
.nodes = { &qnm_memnoc_pcie },
};
static struct qcom_icc_bcm bcm_sn10 = {
.name = "SN10",
.keepalive = false,
.num_nodes = 2,
.nodes = { &qnm_ipa, &xm_ipa2pcie_slv },
};
static struct qcom_icc_bcm * const mc_virt_bcms[] = {
&bcm_mc0,
};
static struct qcom_icc_node * const mc_virt_nodes[] = {
[MASTER_LLCC] = &llcc_mc,
[SLAVE_EBI1] = &ebi,
};
static const struct qcom_icc_desc sdx65_mc_virt = {
.nodes = mc_virt_nodes,
.num_nodes = ARRAY_SIZE(mc_virt_nodes),
.bcms = mc_virt_bcms,
.num_bcms = ARRAY_SIZE(mc_virt_bcms),
};
static struct qcom_icc_bcm * const mem_noc_bcms[] = {
&bcm_sh0,
&bcm_sh1,
&bcm_sh3,
};
static struct qcom_icc_node * const mem_noc_nodes[] = {
[MASTER_TCU_0] = &acm_tcu,
[MASTER_SNOC_GC_MEM_NOC] = &qnm_snoc_gc,
[MASTER_APPSS_PROC] = &xm_apps_rdwr,
[SLAVE_LLCC] = &qns_llcc,
[SLAVE_MEM_NOC_SNOC] = &qns_memnoc_snoc,
[SLAVE_MEM_NOC_PCIE_SNOC] = &qns_sys_pcie,
};
static const struct qcom_icc_desc sdx65_mem_noc = {
.nodes = mem_noc_nodes,
.num_nodes = ARRAY_SIZE(mem_noc_nodes),
.bcms = mem_noc_bcms,
.num_bcms = ARRAY_SIZE(mem_noc_bcms),
};
static struct qcom_icc_bcm * const system_noc_bcms[] = {
&bcm_ce0,
&bcm_pn0,
&bcm_pn1,
&bcm_pn2,
&bcm_pn3,
&bcm_pn4,
&bcm_sn0,
&bcm_sn1,
&bcm_sn2,
&bcm_sn3,
&bcm_sn5,
&bcm_sn6,
&bcm_sn7,
&bcm_sn8,
&bcm_sn9,
&bcm_sn10,
};
static struct qcom_icc_node * const system_noc_nodes[] = {
[MASTER_AUDIO] = &qhm_audio,
[MASTER_BLSP_1] = &qhm_blsp1,
[MASTER_QDSS_BAM] = &qhm_qdss_bam,
[MASTER_QPIC] = &qhm_qpic,
[MASTER_SNOC_CFG] = &qhm_snoc_cfg,
[MASTER_SPMI_FETCHER] = &qhm_spmi_fetcher1,
[MASTER_ANOC_SNOC] = &qnm_aggre_noc,
[MASTER_IPA] = &qnm_ipa,
[MASTER_MEM_NOC_SNOC] = &qnm_memnoc,
[MASTER_MEM_NOC_PCIE_SNOC] = &qnm_memnoc_pcie,
[MASTER_CRYPTO] = &qxm_crypto,
[MASTER_IPA_PCIE] = &xm_ipa2pcie_slv,
[MASTER_PCIE_0] = &xm_pcie,
[MASTER_QDSS_ETR] = &xm_qdss_etr,
[MASTER_SDCC_1] = &xm_sdc1,
[MASTER_USB3] = &xm_usb3,
[SLAVE_AOSS] = &qhs_aoss,
[SLAVE_APPSS] = &qhs_apss,
[SLAVE_AUDIO] = &qhs_audio,
[SLAVE_BLSP_1] = &qhs_blsp1,
[SLAVE_CLK_CTL] = &qhs_clk_ctl,
[SLAVE_CRYPTO_0_CFG] = &qhs_crypto0_cfg,
[SLAVE_CNOC_DDRSS] = &qhs_ddrss_cfg,
[SLAVE_ECC_CFG] = &qhs_ecc_cfg,
[SLAVE_IMEM_CFG] = &qhs_imem_cfg,
[SLAVE_IPA_CFG] = &qhs_ipa,
[SLAVE_CNOC_MSS] = &qhs_mss_cfg,
[SLAVE_PCIE_PARF] = &qhs_pcie_parf,
[SLAVE_PDM] = &qhs_pdm,
[SLAVE_PRNG] = &qhs_prng,
[SLAVE_QDSS_CFG] = &qhs_qdss_cfg,
[SLAVE_QPIC] = &qhs_qpic,
[SLAVE_SDCC_1] = &qhs_sdc1,
[SLAVE_SNOC_CFG] = &qhs_snoc_cfg,
[SLAVE_SPMI_FETCHER] = &qhs_spmi_fetcher,
[SLAVE_SPMI_VGI_COEX] = &qhs_spmi_vgi_coex,
[SLAVE_TCSR] = &qhs_tcsr,
[SLAVE_TLMM] = &qhs_tlmm,
[SLAVE_USB3] = &qhs_usb3,
[SLAVE_USB3_PHY_CFG] = &qhs_usb3_phy,
[SLAVE_ANOC_SNOC] = &qns_aggre_noc,
[SLAVE_SNOC_MEM_NOC_GC] = &qns_snoc_memnoc,
[SLAVE_IMEM] = &qxs_imem,
[SLAVE_SERVICE_SNOC] = &srvc_snoc,
[SLAVE_PCIE_0] = &xs_pcie,
[SLAVE_QDSS_STM] = &xs_qdss_stm,
[SLAVE_TCU] = &xs_sys_tcu_cfg,
};
static const struct qcom_icc_desc sdx65_system_noc = {
.nodes = system_noc_nodes,
.num_nodes = ARRAY_SIZE(system_noc_nodes),
.bcms = system_noc_bcms,
.num_bcms = ARRAY_SIZE(system_noc_bcms),
};
static const struct of_device_id qnoc_of_match[] = {
{ .compatible = "qcom,sdx65-mc-virt",
.data = &sdx65_mc_virt},
{ .compatible = "qcom,sdx65-mem-noc",
.data = &sdx65_mem_noc},
{ .compatible = "qcom,sdx65-system-noc",
.data = &sdx65_system_noc},
{ }
};
MODULE_DEVICE_TABLE(of, qnoc_of_match);
static struct platform_driver qnoc_driver = {
.probe = qcom_icc_rpmh_probe,
.remove = qcom_icc_rpmh_remove,
.driver = {
.name = "qnoc-sdx65",
.of_match_table = qnoc_of_match,
.sync_state = icc_sync_state,
},
};
module_platform_driver(qnoc_driver);
MODULE_DESCRIPTION("Qualcomm SDX65 NoC driver");
MODULE_LICENSE("GPL v2");
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,93 @@
// SPDX-License-Identifier: GPL-2.0
/*
* RPM over SMD communication wrapper for interconnects
*
* Copyright (C) 2019 Linaro Ltd
* Author: Georgi Djakov <georgi.djakov@linaro.org>
*/
#include <linux/interconnect-provider.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/soc/qcom/smd-rpm.h>
#include "icc-rpm.h"
#define RPM_KEY_BW 0x00007762
static struct qcom_smd_rpm *icc_smd_rpm;
struct icc_rpm_smd_req {
__le32 key;
__le32 nbytes;
__le32 value;
};
bool qcom_icc_rpm_smd_available(void)
{
return !!icc_smd_rpm;
}
EXPORT_SYMBOL_GPL(qcom_icc_rpm_smd_available);
int qcom_icc_rpm_smd_send(int ctx, int rsc_type, int id, u32 val)
{
struct icc_rpm_smd_req req = {
.key = cpu_to_le32(RPM_KEY_BW),
.nbytes = cpu_to_le32(sizeof(u32)),
.value = cpu_to_le32(val),
};
return qcom_rpm_smd_write(icc_smd_rpm, ctx, rsc_type, id, &req,
sizeof(req));
}
EXPORT_SYMBOL_GPL(qcom_icc_rpm_smd_send);
int qcom_icc_rpm_set_bus_rate(const struct rpm_clk_resource *clk, int ctx, u32 rate)
{
struct clk_smd_rpm_req req = {
.key = cpu_to_le32(QCOM_RPM_SMD_KEY_RATE),
.nbytes = cpu_to_le32(sizeof(u32)),
};
/* Branch clocks are only on/off */
if (clk->branch)
rate = !!rate;
req.value = cpu_to_le32(rate);
return qcom_rpm_smd_write(icc_smd_rpm,
ctx,
clk->resource_type,
clk->clock_id,
&req, sizeof(req));
}
EXPORT_SYMBOL_GPL(qcom_icc_rpm_set_bus_rate);
static void qcom_icc_rpm_smd_remove(struct platform_device *pdev)
{
icc_smd_rpm = NULL;
}
static int qcom_icc_rpm_smd_probe(struct platform_device *pdev)
{
icc_smd_rpm = dev_get_drvdata(pdev->dev.parent);
if (!icc_smd_rpm) {
dev_err(&pdev->dev, "unable to retrieve handle to RPM\n");
return -ENODEV;
}
return 0;
}
static struct platform_driver qcom_interconnect_rpm_smd_driver = {
.driver = {
.name = "icc_smd_rpm",
},
.probe = qcom_icc_rpm_smd_probe,
.remove = qcom_icc_rpm_smd_remove,
};
module_platform_driver(qcom_interconnect_rpm_smd_driver);
MODULE_AUTHOR("Georgi Djakov <georgi.djakov@linaro.org>");
MODULE_DESCRIPTION("Qualcomm SMD RPM interconnect proxy driver");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:icc_smd_rpm");
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,15 @@
# SPDX-License-Identifier: GPL-2.0-only
config INTERCONNECT_SAMSUNG
bool "Samsung SoC interconnect drivers"
depends on ARCH_EXYNOS || COMPILE_TEST
help
Interconnect drivers for Samsung SoCs.
config INTERCONNECT_EXYNOS
tristate "Exynos SoC generic interconnect driver"
depends on INTERCONNECT_SAMSUNG
default y if ARCH_EXYNOS
help
Generic interconnect driver for Samsung Exynos SoCs (e.g. Exynos3250,
Exynos4210, Exynos4412, Exynos542x, Exynos5433).
Choose Y here only if you build for such Samsung SoC.
@@ -0,0 +1,4 @@
# SPDX-License-Identifier: GPL-2.0
exynos-interconnect-objs := exynos.o
obj-$(CONFIG_INTERCONNECT_EXYNOS) += exynos-interconnect.o
@@ -0,0 +1,196 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Exynos generic interconnect provider driver
*
* Copyright (c) 2020 Samsung Electronics Co., Ltd.
*
* Authors: Artur Świgoń <a.swigon@samsung.com>
* Sylwester Nawrocki <s.nawrocki@samsung.com>
*/
#include <linux/device.h>
#include <linux/interconnect-provider.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_qos.h>
#include <linux/slab.h>
#define EXYNOS_ICC_DEFAULT_BUS_CLK_RATIO 8
struct exynos_icc_priv {
struct device *dev;
/* One interconnect node per provider */
struct icc_provider provider;
struct icc_node *node;
struct dev_pm_qos_request qos_req;
u32 bus_clk_ratio;
};
static struct icc_node *exynos_icc_get_parent(struct device_node *np)
{
struct of_phandle_args args;
struct icc_node_data *icc_node_data;
struct icc_node *icc_node;
int num, ret;
num = of_count_phandle_with_args(np, "interconnects",
"#interconnect-cells");
if (num < 1)
return NULL; /* parent nodes are optional */
/* Get the interconnect target node */
ret = of_parse_phandle_with_args(np, "interconnects",
"#interconnect-cells", 0, &args);
if (ret < 0)
return ERR_PTR(ret);
icc_node_data = of_icc_get_from_provider(&args);
of_node_put(args.np);
if (IS_ERR(icc_node_data))
return ERR_CAST(icc_node_data);
icc_node = icc_node_data->node;
kfree(icc_node_data);
return icc_node;
}
static int exynos_generic_icc_set(struct icc_node *src, struct icc_node *dst)
{
struct exynos_icc_priv *src_priv = src->data, *dst_priv = dst->data;
s32 src_freq = max(src->avg_bw, src->peak_bw) / src_priv->bus_clk_ratio;
s32 dst_freq = max(dst->avg_bw, dst->peak_bw) / dst_priv->bus_clk_ratio;
int ret;
ret = dev_pm_qos_update_request(&src_priv->qos_req, src_freq);
if (ret < 0) {
dev_err(src_priv->dev, "failed to update PM QoS of %s (src)\n",
src->name);
return ret;
}
ret = dev_pm_qos_update_request(&dst_priv->qos_req, dst_freq);
if (ret < 0) {
dev_err(dst_priv->dev, "failed to update PM QoS of %s (dst)\n",
dst->name);
return ret;
}
return 0;
}
static struct icc_node *exynos_generic_icc_xlate(const struct of_phandle_args *spec,
void *data)
{
struct exynos_icc_priv *priv = data;
if (spec->np != priv->dev->parent->of_node)
return ERR_PTR(-EINVAL);
return priv->node;
}
static void exynos_generic_icc_remove(struct platform_device *pdev)
{
struct exynos_icc_priv *priv = platform_get_drvdata(pdev);
icc_provider_deregister(&priv->provider);
icc_nodes_remove(&priv->provider);
}
static int exynos_generic_icc_probe(struct platform_device *pdev)
{
struct device *bus_dev = pdev->dev.parent;
struct exynos_icc_priv *priv;
struct icc_provider *provider;
struct icc_node *icc_node, *icc_parent_node;
int ret;
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->dev = &pdev->dev;
platform_set_drvdata(pdev, priv);
provider = &priv->provider;
provider->set = exynos_generic_icc_set;
provider->aggregate = icc_std_aggregate;
provider->xlate = exynos_generic_icc_xlate;
provider->dev = bus_dev;
provider->inter_set = true;
provider->data = priv;
icc_provider_init(provider);
icc_node = icc_node_create(pdev->id);
if (IS_ERR(icc_node))
return PTR_ERR(icc_node);
priv->node = icc_node;
icc_node->name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%pOFn",
bus_dev->of_node);
if (!icc_node->name) {
icc_node_destroy(pdev->id);
return -ENOMEM;
}
if (of_property_read_u32(bus_dev->of_node, "samsung,data-clock-ratio",
&priv->bus_clk_ratio))
priv->bus_clk_ratio = EXYNOS_ICC_DEFAULT_BUS_CLK_RATIO;
icc_node->data = priv;
icc_node_add(icc_node, provider);
/*
* Register a PM QoS request for the parent (devfreq) device.
*/
ret = dev_pm_qos_add_request(bus_dev, &priv->qos_req,
DEV_PM_QOS_MIN_FREQUENCY, 0);
if (ret < 0)
goto err_node_del;
icc_parent_node = exynos_icc_get_parent(bus_dev->of_node);
if (IS_ERR(icc_parent_node)) {
ret = PTR_ERR(icc_parent_node);
goto err_pmqos_del;
}
if (icc_parent_node) {
ret = icc_link_create(icc_node, icc_parent_node->id);
if (ret < 0)
goto err_pmqos_del;
}
ret = icc_provider_register(provider);
if (ret < 0)
goto err_pmqos_del;
return 0;
err_pmqos_del:
dev_pm_qos_remove_request(&priv->qos_req);
err_node_del:
icc_nodes_remove(provider);
return ret;
}
static struct platform_driver exynos_generic_icc_driver = {
.driver = {
.name = "exynos-generic-icc",
.sync_state = icc_sync_state,
},
.probe = exynos_generic_icc_probe,
.remove = exynos_generic_icc_remove,
};
module_platform_driver(exynos_generic_icc_driver);
MODULE_DESCRIPTION("Exynos generic interconnect driver");
MODULE_AUTHOR("Artur Świgoń <a.swigon@samsung.com>");
MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:exynos-generic-icc");
+88
View File
@@ -0,0 +1,88 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Interconnect framework tracepoints
* Copyright (c) 2019, Linaro Ltd.
* Author: Georgi Djakov <georgi.djakov@linaro.org>
*/
#undef TRACE_SYSTEM
#define TRACE_SYSTEM interconnect
#if !defined(_TRACE_INTERCONNECT_H) || defined(TRACE_HEADER_MULTI_READ)
#define _TRACE_INTERCONNECT_H
#include <linux/interconnect.h>
#include <linux/tracepoint.h>
TRACE_EVENT(icc_set_bw,
TP_PROTO(struct icc_path *p, struct icc_node *n, int i,
u32 avg_bw, u32 peak_bw),
TP_ARGS(p, n, i, avg_bw, peak_bw),
TP_STRUCT__entry(
__string(path_name, p->name)
__string(dev, dev_name(p->reqs[i].dev))
__string(node_name, n->name)
__field(u32, avg_bw)
__field(u32, peak_bw)
__field(u32, node_avg_bw)
__field(u32, node_peak_bw)
),
TP_fast_assign(
__assign_str(path_name);
__assign_str(dev);
__assign_str(node_name);
__entry->avg_bw = avg_bw;
__entry->peak_bw = peak_bw;
__entry->node_avg_bw = n->avg_bw;
__entry->node_peak_bw = n->peak_bw;
),
TP_printk("path=%s dev=%s node=%s avg_bw=%u peak_bw=%u agg_avg=%u agg_peak=%u",
__get_str(path_name),
__get_str(dev),
__get_str(node_name),
__entry->avg_bw,
__entry->peak_bw,
__entry->node_avg_bw,
__entry->node_peak_bw)
);
TRACE_EVENT(icc_set_bw_end,
TP_PROTO(struct icc_path *p, int ret),
TP_ARGS(p, ret),
TP_STRUCT__entry(
__string(path_name, p->name)
__string(dev, dev_name(p->reqs[0].dev))
__field(int, ret)
),
TP_fast_assign(
__assign_str(path_name);
__assign_str(dev);
__entry->ret = ret;
),
TP_printk("path=%s dev=%s ret=%d",
__get_str(path_name),
__get_str(dev),
__entry->ret)
);
#endif /* _TRACE_INTERCONNECT_H */
/* This part must be outside protection */
#undef TRACE_INCLUDE_PATH
#define TRACE_INCLUDE_PATH .
#undef TRACE_INCLUDE_FILE
#define TRACE_INCLUDE_FILE trace
#include <trace/define_trace.h>