summaryrefslogtreecommitdiffstats
path: root/drivers/interconnect
diff options
context:
space:
mode:
authorGeorgi Djakov <georgi.djakov@linaro.org>2019-01-16 11:10:58 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2019-01-22 07:37:25 -0500
commit87e3031b6fbd83ea83adf1bf9602bcce313ee787 (patch)
treea4c5607d1858e2459239003d3a205fb57d7bf056 /drivers/interconnect
parent06b3773b5038a618c578f823d5622481e96c0b56 (diff)
interconnect: Allow endpoints translation via DT
Currently we support only platform data for specifying the interconnect endpoints. As now the endpoints are hard-coded into the consumer driver this may lead to complications when a single driver is used by multiple SoCs, which may have different interconnect topology. To avoid cluttering the consumer drivers, introduce a translation function to help us get the board specific interconnect data from device-tree. Reviewed-by: Evan Green <evgreen@chromium.org> Signed-off-by: Georgi Djakov <georgi.djakov@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/interconnect')
-rw-r--r--drivers/interconnect/core.c149
1 files changed, 149 insertions, 0 deletions
diff --git a/drivers/interconnect/core.c b/drivers/interconnect/core.c
index 2b937b4f43c4..a8c2bd35197f 100644
--- a/drivers/interconnect/core.c
+++ b/drivers/interconnect/core.c
@@ -15,6 +15,7 @@
15#include <linux/module.h> 15#include <linux/module.h>
16#include <linux/mutex.h> 16#include <linux/mutex.h>
17#include <linux/slab.h> 17#include <linux/slab.h>
18#include <linux/of.h>
18#include <linux/overflow.h> 19#include <linux/overflow.h>
19 20
20static DEFINE_IDR(icc_idr); 21static DEFINE_IDR(icc_idr);
@@ -194,6 +195,152 @@ out:
194 return ret; 195 return ret;
195} 196}
196 197
198/* of_icc_xlate_onecell() - Translate function using a single index.
199 * @spec: OF phandle args to map into an interconnect node.
200 * @data: private data (pointer to struct icc_onecell_data)
201 *
202 * This is a generic translate function that can be used to model simple
203 * interconnect providers that have one device tree node and provide
204 * multiple interconnect nodes. A single cell is used as an index into
205 * an array of icc nodes specified in the icc_onecell_data struct when
206 * registering the provider.
207 */
208struct icc_node *of_icc_xlate_onecell(struct of_phandle_args *spec,
209 void *data)
210{
211 struct icc_onecell_data *icc_data = data;
212 unsigned int idx = spec->args[0];
213
214 if (idx >= icc_data->num_nodes) {
215 pr_err("%s: invalid index %u\n", __func__, idx);
216 return ERR_PTR(-EINVAL);
217 }
218
219 return icc_data->nodes[idx];
220}
221EXPORT_SYMBOL_GPL(of_icc_xlate_onecell);
222
223/**
224 * of_icc_get_from_provider() - Look-up interconnect node
225 * @spec: OF phandle args to use for look-up
226 *
227 * Looks for interconnect provider under the node specified by @spec and if
228 * found, uses xlate function of the provider to map phandle args to node.
229 *
230 * Returns a valid pointer to struct icc_node on success or ERR_PTR()
231 * on failure.
232 */
233static struct icc_node *of_icc_get_from_provider(struct of_phandle_args *spec)
234{
235 struct icc_node *node = ERR_PTR(-EPROBE_DEFER);
236 struct icc_provider *provider;
237
238 if (!spec || spec->args_count != 1)
239 return ERR_PTR(-EINVAL);
240
241 mutex_lock(&icc_lock);
242 list_for_each_entry(provider, &icc_providers, provider_list) {
243 if (provider->dev->of_node == spec->np)
244 node = provider->xlate(spec, provider->data);
245 if (!IS_ERR(node))
246 break;
247 }
248 mutex_unlock(&icc_lock);
249
250 return node;
251}
252
253/**
254 * of_icc_get() - get a path handle from a DT node based on name
255 * @dev: device pointer for the consumer device
256 * @name: interconnect path name
257 *
258 * This function will search for a path between two endpoints and return an
259 * icc_path handle on success. Use icc_put() to release constraints when they
260 * are not needed anymore.
261 * If the interconnect API is disabled, NULL is returned and the consumer
262 * drivers will still build. Drivers are free to handle this specifically,
263 * but they don't have to.
264 *
265 * Return: icc_path pointer on success or ERR_PTR() on error. NULL is returned
266 * when the API is disabled or the "interconnects" DT property is missing.
267 */
268struct icc_path *of_icc_get(struct device *dev, const char *name)
269{
270 struct icc_path *path = ERR_PTR(-EPROBE_DEFER);
271 struct icc_node *src_node, *dst_node;
272 struct device_node *np = NULL;
273 struct of_phandle_args src_args, dst_args;
274 int idx = 0;
275 int ret;
276
277 if (!dev || !dev->of_node)
278 return ERR_PTR(-ENODEV);
279
280 np = dev->of_node;
281
282 /*
283 * When the consumer DT node do not have "interconnects" property
284 * return a NULL path to skip setting constraints.
285 */
286 if (!of_find_property(np, "interconnects", NULL))
287 return NULL;
288
289 /*
290 * We use a combination of phandle and specifier for endpoint. For now
291 * lets support only global ids and extend this in the future if needed
292 * without breaking DT compatibility.
293 */
294 if (name) {
295 idx = of_property_match_string(np, "interconnect-names", name);
296 if (idx < 0)
297 return ERR_PTR(idx);
298 }
299
300 ret = of_parse_phandle_with_args(np, "interconnects",
301 "#interconnect-cells", idx * 2,
302 &src_args);
303 if (ret)
304 return ERR_PTR(ret);
305
306 of_node_put(src_args.np);
307
308 ret = of_parse_phandle_with_args(np, "interconnects",
309 "#interconnect-cells", idx * 2 + 1,
310 &dst_args);
311 if (ret)
312 return ERR_PTR(ret);
313
314 of_node_put(dst_args.np);
315
316 src_node = of_icc_get_from_provider(&src_args);
317
318 if (IS_ERR(src_node)) {
319 if (PTR_ERR(src_node) != -EPROBE_DEFER)
320 dev_err(dev, "error finding src node: %ld\n",
321 PTR_ERR(src_node));
322 return ERR_CAST(src_node);
323 }
324
325 dst_node = of_icc_get_from_provider(&dst_args);
326
327 if (IS_ERR(dst_node)) {
328 if (PTR_ERR(dst_node) != -EPROBE_DEFER)
329 dev_err(dev, "error finding dst node: %ld\n",
330 PTR_ERR(dst_node));
331 return ERR_CAST(dst_node);
332 }
333
334 mutex_lock(&icc_lock);
335 path = path_find(dev, src_node, dst_node);
336 if (IS_ERR(path))
337 dev_err(dev, "%s: invalid path=%ld\n", __func__, PTR_ERR(path));
338 mutex_unlock(&icc_lock);
339
340 return path;
341}
342EXPORT_SYMBOL_GPL(of_icc_get);
343
197/** 344/**
198 * icc_set_bw() - set bandwidth constraints on an interconnect path 345 * icc_set_bw() - set bandwidth constraints on an interconnect path
199 * @path: reference to the path returned by icc_get() 346 * @path: reference to the path returned by icc_get()
@@ -519,6 +666,8 @@ int icc_provider_add(struct icc_provider *provider)
519{ 666{
520 if (WARN_ON(!provider->set)) 667 if (WARN_ON(!provider->set))
521 return -EINVAL; 668 return -EINVAL;
669 if (WARN_ON(!provider->xlate))
670 return -EINVAL;
522 671
523 mutex_lock(&icc_lock); 672 mutex_lock(&icc_lock);
524 673