// 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 */ #include #include #include #include #include #include #include #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 "); MODULE_AUTHOR("Henry Chen "); MODULE_DESCRIPTION("MediaTek External Memory Interface interconnect driver"); MODULE_LICENSE("GPL");