summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/interconnect/core.c149
-rw-r--r--include/linux/interconnect-provider.h17
-rw-r--r--include/linux/interconnect.h7
3 files changed, 173 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
diff --git a/include/linux/interconnect-provider.h b/include/linux/interconnect-provider.h
index 78208a754181..63caccadc2db 100644
--- a/include/linux/interconnect-provider.h
+++ b/include/linux/interconnect-provider.h
@@ -12,6 +12,21 @@
12#define icc_units_to_bps(bw) ((bw) * 1000ULL) 12#define icc_units_to_bps(bw) ((bw) * 1000ULL)
13 13
14struct icc_node; 14struct icc_node;
15struct of_phandle_args;
16
17/**
18 * struct icc_onecell_data - driver data for onecell interconnect providers
19 *
20 * @num_nodes: number of nodes in this device
21 * @nodes: array of pointers to the nodes in this device
22 */
23struct icc_onecell_data {
24 unsigned int num_nodes;
25 struct icc_node *nodes[];
26};
27
28struct icc_node *of_icc_xlate_onecell(struct of_phandle_args *spec,
29 void *data);
15 30
16/** 31/**
17 * struct icc_provider - interconnect provider (controller) entity that might 32 * struct icc_provider - interconnect provider (controller) entity that might
@@ -21,6 +36,7 @@ struct icc_node;
21 * @nodes: internal list of the interconnect provider nodes 36 * @nodes: internal list of the interconnect provider nodes
22 * @set: pointer to device specific set operation function 37 * @set: pointer to device specific set operation function
23 * @aggregate: pointer to device specific aggregate operation function 38 * @aggregate: pointer to device specific aggregate operation function
39 * @xlate: provider-specific callback for mapping nodes from phandle arguments
24 * @dev: the device this interconnect provider belongs to 40 * @dev: the device this interconnect provider belongs to
25 * @users: count of active users 41 * @users: count of active users
26 * @data: pointer to private data 42 * @data: pointer to private data
@@ -31,6 +47,7 @@ struct icc_provider {
31 int (*set)(struct icc_node *src, struct icc_node *dst); 47 int (*set)(struct icc_node *src, struct icc_node *dst);
32 int (*aggregate)(struct icc_node *node, u32 avg_bw, u32 peak_bw, 48 int (*aggregate)(struct icc_node *node, u32 avg_bw, u32 peak_bw,
33 u32 *agg_avg, u32 *agg_peak); 49 u32 *agg_avg, u32 *agg_peak);
50 struct icc_node* (*xlate)(struct of_phandle_args *spec, void *data);
34 struct device *dev; 51 struct device *dev;
35 int users; 52 int users;
36 void *data; 53 void *data;
diff --git a/include/linux/interconnect.h b/include/linux/interconnect.h
index c331afb3a2c8..dc25864755ba 100644
--- a/include/linux/interconnect.h
+++ b/include/linux/interconnect.h
@@ -27,6 +27,7 @@ struct device;
27 27
28struct icc_path *icc_get(struct device *dev, const int src_id, 28struct icc_path *icc_get(struct device *dev, const int src_id,
29 const int dst_id); 29 const int dst_id);
30struct icc_path *of_icc_get(struct device *dev, const char *name);
30void icc_put(struct icc_path *path); 31void icc_put(struct icc_path *path);
31int icc_set_bw(struct icc_path *path, u32 avg_bw, u32 peak_bw); 32int icc_set_bw(struct icc_path *path, u32 avg_bw, u32 peak_bw);
32 33
@@ -38,6 +39,12 @@ static inline struct icc_path *icc_get(struct device *dev, const int src_id,
38 return NULL; 39 return NULL;
39} 40}
40 41
42static inline struct icc_path *of_icc_get(struct device *dev,
43 const char *name)
44{
45 return NULL;
46}
47
41static inline void icc_put(struct icc_path *path) 48static inline void icc_put(struct icc_path *path)
42{ 49{
43} 50}