Commit b4529379 authored by AngeloGioacchino Del Regno's avatar AngeloGioacchino Del Regno Committed by Georgi Djakov
Browse files

interconnect: mediatek: Add MediaTek MT8183/8195 EMI Interconnect driver



Add an interconnect driver for the External Memory Interface (EMI),
voting for bus bandwidth over the Dynamic Voltage and Frequency Scaling
Resource Collector (DVFSRC).

             ICC provider         ICC Nodes
                              ----          ----
             ---------       |CPU |   |--- |VPU |
    -----   |         |-----  ----    |     ----
   |DRAM |--|DRAM     |       ----    |     ----
   |     |--|scheduler|----- |GPU |   |--- |DISP|
   |     |--|(EMI)    |       ----    |     ----
   |     |--|         |       -----   |     ----
    -----   |         |----- |MMSYS|--|--- |VDEC|
             ---------        -----   |     ----
               /|\                    |     ----
                |change DRAM freq     |--- |VENC|
             ----------               |     ----
            |  DVFSR   |              |
            |          |              |     ----
             ----------               |--- |IMG |
                                      |     ----
                                      |     ----
                                      |--- |CAM |
                                            ----

Signed-off-by: default avatarAngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Link: https://lore.kernel.org/r/20240610085735.147134-8-angelogioacchino.delregno@collabora.com


Signed-off-by: default avatarGeorgi Djakov <djakov@kernel.org>
parent 1a8009e1
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -12,6 +12,7 @@ menuconfig INTERCONNECT
if INTERCONNECT

source "drivers/interconnect/imx/Kconfig"
source "drivers/interconnect/mediatek/Kconfig"
source "drivers/interconnect/qcom/Kconfig"
source "drivers/interconnect/samsung/Kconfig"

+1 −0
Original line number Diff line number Diff line
@@ -5,6 +5,7 @@ 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/

+29 −0
Original line number Diff line number Diff line
# 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.
+5 −0
Original line number Diff line number Diff line
# 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
+153 −0
Original line number Diff line number Diff line
// 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/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;

	*agg_avg += avg_bw;
	*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;

	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 = pdev->dev.parent;
	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");
Loading