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