1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Interconnect framework driver for i.MX SoC
4 *
5 * Copyright (c) 2019, BayLibre
6 * Copyright (c) 2019-2020, NXP
7 * Author: Alexandre Bailon <abailon@baylibre.com>
8 * Author: Leonard Crestez <leonard.crestez@nxp.com>
9 */
10
11 #include <linux/device.h>
12 #include <linux/interconnect-provider.h>
13 #include <linux/module.h>
14 #include <linux/of.h>
15 #include <linux/of_platform.h>
16 #include <linux/platform_device.h>
17 #include <linux/pm_qos.h>
18
19 #include "imx.h"
20
21 /* private icc_node data */
22 struct imx_icc_node {
23 const struct imx_icc_node_desc *desc;
24 struct device *qos_dev;
25 struct dev_pm_qos_request qos_req;
26 };
27
imx_icc_node_set(struct icc_node * node)28 static int imx_icc_node_set(struct icc_node *node)
29 {
30 struct device *dev = node->provider->dev;
31 struct imx_icc_node *node_data = node->data;
32 u64 freq;
33
34 if (!node_data->qos_dev)
35 return 0;
36
37 freq = (node->avg_bw + node->peak_bw) * node_data->desc->adj->bw_mul;
38 do_div(freq, node_data->desc->adj->bw_div);
39 dev_dbg(dev, "node %s device %s avg_bw %ukBps peak_bw %ukBps min_freq %llukHz\n",
40 node->name, dev_name(node_data->qos_dev),
41 node->avg_bw, node->peak_bw, freq);
42
43 if (freq > S32_MAX) {
44 dev_err(dev, "%s can't request more than S32_MAX freq\n",
45 node->name);
46 return -ERANGE;
47 }
48
49 dev_pm_qos_update_request(&node_data->qos_req, freq);
50
51 return 0;
52 }
53
imx_icc_set(struct icc_node * src,struct icc_node * dst)54 static int imx_icc_set(struct icc_node *src, struct icc_node *dst)
55 {
56 return imx_icc_node_set(dst);
57 }
58
59 /* imx_icc_node_destroy() - Destroy an imx icc_node, including private data */
imx_icc_node_destroy(struct icc_node * node)60 static void imx_icc_node_destroy(struct icc_node *node)
61 {
62 struct imx_icc_node *node_data = node->data;
63 int ret;
64
65 if (dev_pm_qos_request_active(&node_data->qos_req)) {
66 ret = dev_pm_qos_remove_request(&node_data->qos_req);
67 if (ret)
68 dev_warn(node->provider->dev,
69 "failed to remove qos request for %s\n",
70 dev_name(node_data->qos_dev));
71 }
72
73 put_device(node_data->qos_dev);
74 icc_node_del(node);
75 icc_node_destroy(node->id);
76 }
77
imx_icc_node_init_qos(struct icc_provider * provider,struct icc_node * node)78 static int imx_icc_node_init_qos(struct icc_provider *provider,
79 struct icc_node *node)
80 {
81 struct imx_icc_node *node_data = node->data;
82 const struct imx_icc_node_adj_desc *adj = node_data->desc->adj;
83 struct device *dev = provider->dev;
84 struct device_node *dn = NULL;
85 struct platform_device *pdev;
86
87 if (adj->main_noc) {
88 node_data->qos_dev = dev;
89 dev_dbg(dev, "icc node %s[%d] is main noc itself\n",
90 node->name, node->id);
91 } else {
92 dn = of_parse_phandle(dev->of_node, adj->phandle_name, 0);
93 if (!dn) {
94 dev_warn(dev, "Failed to parse %s\n",
95 adj->phandle_name);
96 return -ENODEV;
97 }
98 /* Allow scaling to be disabled on a per-node basis */
99 if (!of_device_is_available(dn)) {
100 dev_warn(dev, "Missing property %s, skip scaling %s\n",
101 adj->phandle_name, node->name);
102 of_node_put(dn);
103 return 0;
104 }
105
106 pdev = of_find_device_by_node(dn);
107 of_node_put(dn);
108 if (!pdev) {
109 dev_warn(dev, "node %s[%d] missing device for %pOF\n",
110 node->name, node->id, dn);
111 return -EPROBE_DEFER;
112 }
113 node_data->qos_dev = &pdev->dev;
114 dev_dbg(dev, "node %s[%d] has device node %pOF\n",
115 node->name, node->id, dn);
116 }
117
118 return dev_pm_qos_add_request(node_data->qos_dev,
119 &node_data->qos_req,
120 DEV_PM_QOS_MIN_FREQUENCY, 0);
121 }
122
imx_icc_node_add(struct icc_provider * provider,const struct imx_icc_node_desc * node_desc)123 static struct icc_node *imx_icc_node_add(struct icc_provider *provider,
124 const struct imx_icc_node_desc *node_desc)
125 {
126 struct device *dev = provider->dev;
127 struct imx_icc_node *node_data;
128 struct icc_node *node;
129 int ret;
130
131 node = icc_node_create(node_desc->id);
132 if (IS_ERR(node)) {
133 dev_err(dev, "failed to create node %d\n", node_desc->id);
134 return node;
135 }
136
137 if (node->data) {
138 dev_err(dev, "already created node %s id=%d\n",
139 node_desc->name, node_desc->id);
140 return ERR_PTR(-EEXIST);
141 }
142
143 node_data = devm_kzalloc(dev, sizeof(*node_data), GFP_KERNEL);
144 if (!node_data) {
145 icc_node_destroy(node->id);
146 return ERR_PTR(-ENOMEM);
147 }
148
149 node->name = node_desc->name;
150 node->data = node_data;
151 node_data->desc = node_desc;
152 icc_node_add(node, provider);
153
154 if (node_desc->adj) {
155 ret = imx_icc_node_init_qos(provider, node);
156 if (ret < 0) {
157 imx_icc_node_destroy(node);
158 return ERR_PTR(ret);
159 }
160 }
161
162 return node;
163 }
164
imx_icc_unregister_nodes(struct icc_provider * provider)165 static void imx_icc_unregister_nodes(struct icc_provider *provider)
166 {
167 struct icc_node *node, *tmp;
168
169 list_for_each_entry_safe(node, tmp, &provider->nodes, node_list)
170 imx_icc_node_destroy(node);
171 }
172
imx_icc_register_nodes(struct icc_provider * provider,const struct imx_icc_node_desc * descs,int count)173 static int imx_icc_register_nodes(struct icc_provider *provider,
174 const struct imx_icc_node_desc *descs,
175 int count)
176 {
177 struct icc_onecell_data *provider_data = provider->data;
178 int ret;
179 int i;
180
181 for (i = 0; i < count; i++) {
182 struct icc_node *node;
183 const struct imx_icc_node_desc *node_desc = &descs[i];
184 size_t j;
185
186 node = imx_icc_node_add(provider, node_desc);
187 if (IS_ERR(node)) {
188 ret = dev_err_probe(provider->dev, PTR_ERR(node),
189 "failed to add %s\n", node_desc->name);
190 goto err;
191 }
192 provider_data->nodes[node->id] = node;
193
194 for (j = 0; j < node_desc->num_links; j++) {
195 ret = icc_link_create(node, node_desc->links[j]);
196 if (ret) {
197 dev_err(provider->dev, "failed to link node %d to %d: %d\n",
198 node->id, node_desc->links[j], ret);
199 goto err;
200 }
201 }
202 }
203
204 return 0;
205
206 err:
207 imx_icc_unregister_nodes(provider);
208
209 return ret;
210 }
211
get_max_node_id(struct imx_icc_node_desc * nodes,int nodes_count)212 static int get_max_node_id(struct imx_icc_node_desc *nodes, int nodes_count)
213 {
214 int i, ret = 0;
215
216 for (i = 0; i < nodes_count; ++i)
217 if (nodes[i].id > ret)
218 ret = nodes[i].id;
219
220 return ret;
221 }
222
imx_icc_register(struct platform_device * pdev,struct imx_icc_node_desc * nodes,int nodes_count)223 int imx_icc_register(struct platform_device *pdev,
224 struct imx_icc_node_desc *nodes, int nodes_count)
225 {
226 struct device *dev = &pdev->dev;
227 struct icc_onecell_data *data;
228 struct icc_provider *provider;
229 int max_node_id;
230 int ret;
231
232 /* icc_onecell_data is indexed by node_id, unlike nodes param */
233 max_node_id = get_max_node_id(nodes, nodes_count);
234 data = devm_kzalloc(dev, struct_size(data, nodes, max_node_id),
235 GFP_KERNEL);
236 if (!data)
237 return -ENOMEM;
238 data->num_nodes = max_node_id;
239
240 provider = devm_kzalloc(dev, sizeof(*provider), GFP_KERNEL);
241 if (!provider)
242 return -ENOMEM;
243 provider->set = imx_icc_set;
244 provider->aggregate = icc_std_aggregate;
245 provider->xlate = of_icc_xlate_onecell;
246 provider->data = data;
247 provider->dev = dev->parent;
248 platform_set_drvdata(pdev, provider);
249
250 ret = icc_provider_add(provider);
251 if (ret) {
252 dev_err(dev, "error adding interconnect provider: %d\n", ret);
253 return ret;
254 }
255
256 ret = imx_icc_register_nodes(provider, nodes, nodes_count);
257 if (ret)
258 goto provider_del;
259
260 return 0;
261
262 provider_del:
263 icc_provider_del(provider);
264 return ret;
265 }
266 EXPORT_SYMBOL_GPL(imx_icc_register);
267
imx_icc_unregister(struct platform_device * pdev)268 int imx_icc_unregister(struct platform_device *pdev)
269 {
270 struct icc_provider *provider = platform_get_drvdata(pdev);
271
272 imx_icc_unregister_nodes(provider);
273
274 return icc_provider_del(provider);
275 }
276 EXPORT_SYMBOL_GPL(imx_icc_unregister);
277
278 MODULE_LICENSE("GPL v2");
279