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 | |
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 | |
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, cmd: MTK_DVFSRC_CMD_PEAK_BW, data: 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, cmd: MTK_DVFSRC_CMD_BW, data: 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, cmd: MTK_DVFSRC_CMD_HRT_BW, data: 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 | |
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, size: 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(list: &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(id: mnodes[i]->id); |
112 | if (IS_ERR(ptr: node)) { |
113 | ret = PTR_ERR(ptr: 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, dst_id: 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, data: 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 | |
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 | |