xref: /linux/drivers/interconnect/mediatek/icc-emi.c (revision a1ff5a7d78a036d6c2178ee5acd6ba4946243800)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * MediaTek External Memory Interface (EMI) Interconnect driver
4  *
5  * Copyright (c) 2021 MediaTek Inc.
6  * Copyright (c) 2024 Collabora Ltd.
7  *                    AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
8  */
9 
10 #include <linux/interconnect.h>
11 #include <linux/interconnect-provider.h>
12 #include <linux/module.h>
13 #include <linux/of.h>
14 #include <linux/of_platform.h>
15 #include <linux/platform_device.h>
16 #include <linux/soc/mediatek/dvfsrc.h>
17 
18 #include "icc-emi.h"
19 
mtk_emi_icc_aggregate(struct icc_node * node,u32 tag,u32 avg_bw,u32 peak_bw,u32 * agg_avg,u32 * agg_peak)20 static int mtk_emi_icc_aggregate(struct icc_node *node, u32 tag, u32 avg_bw,
21 				 u32 peak_bw, u32 *agg_avg, u32 *agg_peak)
22 {
23 	struct mtk_icc_node *in = node->data;
24 
25 	*agg_avg += avg_bw;
26 	*agg_peak = max_t(u32, *agg_peak, peak_bw);
27 
28 	in->sum_avg = *agg_avg;
29 	in->max_peak = *agg_peak;
30 
31 	return 0;
32 }
33 
mtk_emi_icc_set(struct icc_node * src,struct icc_node * dst)34 static int mtk_emi_icc_set(struct icc_node *src, struct icc_node *dst)
35 {
36 	struct mtk_icc_node *node = dst->data;
37 	struct device *dev;
38 	int ret;
39 
40 	if (unlikely(!src->provider))
41 		return -EINVAL;
42 
43 	dev = src->provider->dev;
44 
45 	switch (node->ep) {
46 	case 0:
47 		break;
48 	case 1:
49 		ret = mtk_dvfsrc_send_request(dev, MTK_DVFSRC_CMD_PEAK_BW, node->max_peak);
50 		if (ret) {
51 			dev_err(dev, "Cannot send peak bw request: %d\n", ret);
52 			return ret;
53 		}
54 
55 		ret = mtk_dvfsrc_send_request(dev, MTK_DVFSRC_CMD_BW, node->sum_avg);
56 		if (ret) {
57 			dev_err(dev, "Cannot send bw request: %d\n", ret);
58 			return ret;
59 		}
60 		break;
61 	case 2:
62 		ret = mtk_dvfsrc_send_request(dev, MTK_DVFSRC_CMD_HRT_BW, node->sum_avg);
63 		if (ret) {
64 			dev_err(dev, "Cannot send HRT bw request: %d\n", ret);
65 			return ret;
66 		}
67 		break;
68 	default:
69 		dev_err(src->provider->dev, "Unknown endpoint %u\n", node->ep);
70 		return -EINVAL;
71 	}
72 
73 	return 0;
74 }
75 
mtk_emi_icc_probe(struct platform_device * pdev)76 int mtk_emi_icc_probe(struct platform_device *pdev)
77 {
78 	const struct mtk_icc_desc *desc;
79 	struct device *dev = &pdev->dev;
80 	struct icc_node *node;
81 	struct icc_onecell_data *data;
82 	struct icc_provider *provider;
83 	struct mtk_icc_node **mnodes;
84 	int i, j, ret;
85 
86 	desc = of_device_get_match_data(dev);
87 	if (!desc)
88 		return -EINVAL;
89 
90 	mnodes = desc->nodes;
91 
92 	provider = devm_kzalloc(dev, sizeof(*provider), GFP_KERNEL);
93 	if (!provider)
94 		return -ENOMEM;
95 
96 	data = devm_kzalloc(dev, struct_size(data, nodes, desc->num_nodes), GFP_KERNEL);
97 	if (!data)
98 		return -ENOMEM;
99 
100 	provider->dev = pdev->dev.parent;
101 	provider->set = mtk_emi_icc_set;
102 	provider->aggregate = mtk_emi_icc_aggregate;
103 	provider->xlate = of_icc_xlate_onecell;
104 	INIT_LIST_HEAD(&provider->nodes);
105 	provider->data = data;
106 
107 	for (i = 0; i < desc->num_nodes; i++) {
108 		if (!mnodes[i])
109 			continue;
110 
111 		node = icc_node_create(mnodes[i]->id);
112 		if (IS_ERR(node)) {
113 			ret = PTR_ERR(node);
114 			goto err;
115 		}
116 
117 		node->name = mnodes[i]->name;
118 		node->data = mnodes[i];
119 		icc_node_add(node, provider);
120 
121 		for (j = 0; j < mnodes[i]->num_links; j++)
122 			icc_link_create(node, mnodes[i]->links[j]);
123 
124 		data->nodes[i] = node;
125 	}
126 	data->num_nodes = desc->num_nodes;
127 
128 	ret = icc_provider_register(provider);
129 	if (ret)
130 		goto err;
131 
132 	platform_set_drvdata(pdev, provider);
133 
134 	return 0;
135 err:
136 	icc_nodes_remove(provider);
137 	return ret;
138 }
139 EXPORT_SYMBOL_GPL(mtk_emi_icc_probe);
140 
mtk_emi_icc_remove(struct platform_device * pdev)141 void mtk_emi_icc_remove(struct platform_device *pdev)
142 {
143 	struct icc_provider *provider = platform_get_drvdata(pdev);
144 
145 	icc_provider_deregister(provider);
146 	icc_nodes_remove(provider);
147 }
148 EXPORT_SYMBOL_GPL(mtk_emi_icc_remove);
149 
150 MODULE_AUTHOR("AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>");
151 MODULE_AUTHOR("Henry Chen <henryc.chen@mediatek.com>");
152 MODULE_DESCRIPTION("MediaTek External Memory Interface interconnect driver");
153 MODULE_LICENSE("GPL");
154