diff options
-rw-r--r-- | Documentation/devicetree/overlay-notes.txt | 133 | ||||
-rw-r--r-- | drivers/of/Kconfig | 7 | ||||
-rw-r--r-- | drivers/of/Makefile | 1 | ||||
-rw-r--r-- | drivers/of/overlay.c | 562 | ||||
-rw-r--r-- | include/linux/of.h | 31 |
5 files changed, 734 insertions, 0 deletions
diff --git a/Documentation/devicetree/overlay-notes.txt b/Documentation/devicetree/overlay-notes.txt new file mode 100644 index 000000000000..30ae758e3eef --- /dev/null +++ b/Documentation/devicetree/overlay-notes.txt | |||
@@ -0,0 +1,133 @@ | |||
1 | Device Tree Overlay Notes | ||
2 | ------------------------- | ||
3 | |||
4 | This document describes the implementation of the in-kernel | ||
5 | device tree overlay functionality residing in drivers/of/overlay.c and is a | ||
6 | companion document to Documentation/devicetree/dt-object-internal.txt[1] & | ||
7 | Documentation/devicetree/dynamic-resolution-notes.txt[2] | ||
8 | |||
9 | How overlays work | ||
10 | ----------------- | ||
11 | |||
12 | A Device Tree's overlay purpose is to modify the kernel's live tree, and | ||
13 | have the modification affecting the state of the the kernel in a way that | ||
14 | is reflecting the changes. | ||
15 | Since the kernel mainly deals with devices, any new device node that result | ||
16 | in an active device should have it created while if the device node is either | ||
17 | disabled or removed all together, the affected device should be deregistered. | ||
18 | |||
19 | Lets take an example where we have a foo board with the following base tree | ||
20 | which is taken from [1]. | ||
21 | |||
22 | ---- foo.dts ----------------------------------------------------------------- | ||
23 | /* FOO platform */ | ||
24 | / { | ||
25 | compatible = "corp,foo"; | ||
26 | |||
27 | /* shared resources */ | ||
28 | res: res { | ||
29 | }; | ||
30 | |||
31 | /* On chip peripherals */ | ||
32 | ocp: ocp { | ||
33 | /* peripherals that are always instantiated */ | ||
34 | peripheral1 { ... }; | ||
35 | } | ||
36 | }; | ||
37 | ---- foo.dts ----------------------------------------------------------------- | ||
38 | |||
39 | The overlay bar.dts, when loaded (and resolved as described in [2]) should | ||
40 | |||
41 | ---- bar.dts ----------------------------------------------------------------- | ||
42 | /plugin/; /* allow undefined label references and record them */ | ||
43 | / { | ||
44 | .... /* various properties for loader use; i.e. part id etc. */ | ||
45 | fragment@0 { | ||
46 | target = <&ocp>; | ||
47 | __overlay__ { | ||
48 | /* bar peripheral */ | ||
49 | bar { | ||
50 | compatible = "corp,bar"; | ||
51 | ... /* various properties and child nodes */ | ||
52 | } | ||
53 | }; | ||
54 | }; | ||
55 | }; | ||
56 | ---- bar.dts ----------------------------------------------------------------- | ||
57 | |||
58 | result in foo+bar.dts | ||
59 | |||
60 | ---- foo+bar.dts ------------------------------------------------------------- | ||
61 | /* FOO platform + bar peripheral */ | ||
62 | / { | ||
63 | compatible = "corp,foo"; | ||
64 | |||
65 | /* shared resources */ | ||
66 | res: res { | ||
67 | }; | ||
68 | |||
69 | /* On chip peripherals */ | ||
70 | ocp: ocp { | ||
71 | /* peripherals that are always instantiated */ | ||
72 | peripheral1 { ... }; | ||
73 | |||
74 | /* bar peripheral */ | ||
75 | bar { | ||
76 | compatible = "corp,bar"; | ||
77 | ... /* various properties and child nodes */ | ||
78 | } | ||
79 | } | ||
80 | }; | ||
81 | ---- foo+bar.dts ------------------------------------------------------------- | ||
82 | |||
83 | As a result of the the overlay, a new device node (bar) has been created | ||
84 | so a bar platform device will be registered and if a matching device driver | ||
85 | is loaded the device will be created as expected. | ||
86 | |||
87 | Overlay in-kernel API | ||
88 | -------------------------------- | ||
89 | |||
90 | The API is quite easy to use. | ||
91 | |||
92 | 1. Call of_overlay_create() to create and apply an overlay. The return value | ||
93 | is a cookie identifying this overlay. | ||
94 | |||
95 | 2. Call of_overlay_destroy() to remove and cleanup the overlay previously | ||
96 | created via the call to of_overlay_create(). Removal of an overlay that | ||
97 | is stacked by another will not be permitted. | ||
98 | |||
99 | Finally, if you need to remove all overlays in one-go, just call | ||
100 | of_overlay_destroy_all() which will remove every single one in the correct | ||
101 | order. | ||
102 | |||
103 | Overlay DTS Format | ||
104 | ------------------ | ||
105 | |||
106 | The DTS of an overlay should have the following format: | ||
107 | |||
108 | { | ||
109 | /* ignored properties by the overlay */ | ||
110 | |||
111 | fragment@0 { /* first child node */ | ||
112 | |||
113 | target=<phandle>; /* phandle target of the overlay */ | ||
114 | or | ||
115 | target-path="/path"; /* target path of the overlay */ | ||
116 | |||
117 | __overlay__ { | ||
118 | property-a; /* add property-a to the target */ | ||
119 | node-a { /* add to an existing, or create a node-a */ | ||
120 | ... | ||
121 | }; | ||
122 | }; | ||
123 | } | ||
124 | fragment@1 { /* second child node */ | ||
125 | ... | ||
126 | }; | ||
127 | /* more fragments follow */ | ||
128 | } | ||
129 | |||
130 | Using the non-phandle based target method allows one to use a base DT which does | ||
131 | not contain a __symbols__ node, i.e. it was not compiled with the -@ option. | ||
132 | The __symbols__ node is only required for the target=<phandle> method, since it | ||
133 | contains the information required to map from a phandle to a tree location. | ||
diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig index fbe8f8d418f7..18b2e2539f84 100644 --- a/drivers/of/Kconfig +++ b/drivers/of/Kconfig | |||
@@ -84,4 +84,11 @@ config OF_RESERVED_MEM | |||
84 | config OF_RESOLVE | 84 | config OF_RESOLVE |
85 | bool | 85 | bool |
86 | 86 | ||
87 | config OF_OVERLAY | ||
88 | bool | ||
89 | depends on OF | ||
90 | select OF_DYNAMIC | ||
91 | select OF_DEVICE | ||
92 | select OF_RESOLVE | ||
93 | |||
87 | endmenu # OF | 94 | endmenu # OF |
diff --git a/drivers/of/Makefile b/drivers/of/Makefile index d90553fcd37f..7563f36c71db 100644 --- a/drivers/of/Makefile +++ b/drivers/of/Makefile | |||
@@ -14,6 +14,7 @@ obj-$(CONFIG_OF_PCI_IRQ) += of_pci_irq.o | |||
14 | obj-$(CONFIG_OF_MTD) += of_mtd.o | 14 | obj-$(CONFIG_OF_MTD) += of_mtd.o |
15 | obj-$(CONFIG_OF_RESERVED_MEM) += of_reserved_mem.o | 15 | obj-$(CONFIG_OF_RESERVED_MEM) += of_reserved_mem.o |
16 | obj-$(CONFIG_OF_RESOLVE) += resolver.o | 16 | obj-$(CONFIG_OF_RESOLVE) += resolver.o |
17 | obj-$(CONFIG_OF_OVERLAY) += overlay.o | ||
17 | 18 | ||
18 | CFLAGS_fdt.o = -I$(src)/../../scripts/dtc/libfdt | 19 | CFLAGS_fdt.o = -I$(src)/../../scripts/dtc/libfdt |
19 | CFLAGS_fdt_address.o = -I$(src)/../../scripts/dtc/libfdt | 20 | CFLAGS_fdt_address.o = -I$(src)/../../scripts/dtc/libfdt |
diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c new file mode 100644 index 000000000000..ea63fbd228ed --- /dev/null +++ b/drivers/of/overlay.c | |||
@@ -0,0 +1,562 @@ | |||
1 | /* | ||
2 | * Functions for working with device tree overlays | ||
3 | * | ||
4 | * Copyright (C) 2012 Pantelis Antoniou <panto@antoniou-consulting.com> | ||
5 | * Copyright (C) 2012 Texas Instruments Inc. | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU General Public License | ||
9 | * version 2 as published by the Free Software Foundation. | ||
10 | */ | ||
11 | #undef DEBUG | ||
12 | #include <linux/kernel.h> | ||
13 | #include <linux/module.h> | ||
14 | #include <linux/of.h> | ||
15 | #include <linux/of_device.h> | ||
16 | #include <linux/string.h> | ||
17 | #include <linux/ctype.h> | ||
18 | #include <linux/errno.h> | ||
19 | #include <linux/string.h> | ||
20 | #include <linux/slab.h> | ||
21 | #include <linux/err.h> | ||
22 | |||
23 | #include "of_private.h" | ||
24 | |||
25 | /** | ||
26 | * struct of_overlay_info - Holds a single overlay info | ||
27 | * @target: target of the overlay operation | ||
28 | * @overlay: pointer to the overlay contents node | ||
29 | * | ||
30 | * Holds a single overlay state, including all the overlay logs & | ||
31 | * records. | ||
32 | */ | ||
33 | struct of_overlay_info { | ||
34 | struct device_node *target; | ||
35 | struct device_node *overlay; | ||
36 | }; | ||
37 | |||
38 | /** | ||
39 | * struct of_overlay - Holds a complete overlay transaction | ||
40 | * @node: List on which we are located | ||
41 | * @count: Count of ovinfo structures | ||
42 | * @ovinfo_tab: Overlay info table (count sized) | ||
43 | * @cset: Changeset to be used | ||
44 | * | ||
45 | * Holds a complete overlay transaction | ||
46 | */ | ||
47 | struct of_overlay { | ||
48 | int id; | ||
49 | struct list_head node; | ||
50 | int count; | ||
51 | struct of_overlay_info *ovinfo_tab; | ||
52 | struct of_changeset cset; | ||
53 | }; | ||
54 | |||
55 | static int of_overlay_apply_one(struct of_overlay *ov, | ||
56 | struct device_node *target, const struct device_node *overlay); | ||
57 | |||
58 | static int of_overlay_apply_single_property(struct of_overlay *ov, | ||
59 | struct device_node *target, struct property *prop) | ||
60 | { | ||
61 | struct property *propn, *tprop; | ||
62 | |||
63 | /* NOTE: Multiple changes of single properties not supported */ | ||
64 | tprop = of_find_property(target, prop->name, NULL); | ||
65 | |||
66 | /* special properties are not meant to be updated (silent NOP) */ | ||
67 | if (of_prop_cmp(prop->name, "name") == 0 || | ||
68 | of_prop_cmp(prop->name, "phandle") == 0 || | ||
69 | of_prop_cmp(prop->name, "linux,phandle") == 0) | ||
70 | return 0; | ||
71 | |||
72 | propn = __of_prop_dup(prop, GFP_KERNEL); | ||
73 | if (propn == NULL) | ||
74 | return -ENOMEM; | ||
75 | |||
76 | /* not found? add */ | ||
77 | if (tprop == NULL) | ||
78 | return of_changeset_add_property(&ov->cset, target, propn); | ||
79 | |||
80 | /* found? update */ | ||
81 | return of_changeset_update_property(&ov->cset, target, propn); | ||
82 | } | ||
83 | |||
84 | static int of_overlay_apply_single_device_node(struct of_overlay *ov, | ||
85 | struct device_node *target, struct device_node *child) | ||
86 | { | ||
87 | const char *cname; | ||
88 | struct device_node *tchild, *grandchild; | ||
89 | int ret = 0; | ||
90 | |||
91 | cname = kbasename(child->full_name); | ||
92 | if (cname == NULL) | ||
93 | return -ENOMEM; | ||
94 | |||
95 | /* NOTE: Multiple mods of created nodes not supported */ | ||
96 | tchild = of_get_child_by_name(target, cname); | ||
97 | if (tchild != NULL) { | ||
98 | /* apply overlay recursively */ | ||
99 | ret = of_overlay_apply_one(ov, tchild, child); | ||
100 | of_node_put(tchild); | ||
101 | } else { | ||
102 | /* create empty tree as a target */ | ||
103 | tchild = __of_node_dup(child, "%s/%s", target->full_name, cname); | ||
104 | if (!tchild) | ||
105 | return -ENOMEM; | ||
106 | |||
107 | /* point to parent */ | ||
108 | tchild->parent = target; | ||
109 | |||
110 | ret = of_changeset_attach_node(&ov->cset, tchild); | ||
111 | if (ret) | ||
112 | return ret; | ||
113 | |||
114 | ret = of_overlay_apply_one(ov, tchild, child); | ||
115 | if (ret) | ||
116 | return ret; | ||
117 | |||
118 | /* The properties are already copied, now do the child nodes */ | ||
119 | for_each_child_of_node(child, grandchild) { | ||
120 | ret = of_overlay_apply_single_device_node(ov, tchild, grandchild); | ||
121 | if (ret) { | ||
122 | pr_err("%s: Failed to apply single node @%s/%s\n", | ||
123 | __func__, tchild->full_name, | ||
124 | grandchild->name); | ||
125 | return ret; | ||
126 | } | ||
127 | } | ||
128 | } | ||
129 | |||
130 | return ret; | ||
131 | } | ||
132 | |||
133 | /* | ||
134 | * Apply a single overlay node recursively. | ||
135 | * | ||
136 | * Note that the in case of an error the target node is left | ||
137 | * in a inconsistent state. Error recovery should be performed | ||
138 | * by using the changeset. | ||
139 | */ | ||
140 | static int of_overlay_apply_one(struct of_overlay *ov, | ||
141 | struct device_node *target, const struct device_node *overlay) | ||
142 | { | ||
143 | struct device_node *child; | ||
144 | struct property *prop; | ||
145 | int ret; | ||
146 | |||
147 | for_each_property_of_node(overlay, prop) { | ||
148 | ret = of_overlay_apply_single_property(ov, target, prop); | ||
149 | if (ret) { | ||
150 | pr_err("%s: Failed to apply prop @%s/%s\n", | ||
151 | __func__, target->full_name, prop->name); | ||
152 | return ret; | ||
153 | } | ||
154 | } | ||
155 | |||
156 | for_each_child_of_node(overlay, child) { | ||
157 | ret = of_overlay_apply_single_device_node(ov, target, child); | ||
158 | if (ret != 0) { | ||
159 | pr_err("%s: Failed to apply single node @%s/%s\n", | ||
160 | __func__, target->full_name, | ||
161 | child->name); | ||
162 | return ret; | ||
163 | } | ||
164 | } | ||
165 | |||
166 | return 0; | ||
167 | } | ||
168 | |||
169 | /** | ||
170 | * of_overlay_apply() - Apply @count overlays pointed at by @ovinfo_tab | ||
171 | * @ov: Overlay to apply | ||
172 | * | ||
173 | * Applies the overlays given, while handling all error conditions | ||
174 | * appropriately. Either the operation succeeds, or if it fails the | ||
175 | * live tree is reverted to the state before the attempt. | ||
176 | * Returns 0, or an error if the overlay attempt failed. | ||
177 | */ | ||
178 | static int of_overlay_apply(struct of_overlay *ov) | ||
179 | { | ||
180 | int i, err; | ||
181 | |||
182 | /* first we apply the overlays atomically */ | ||
183 | for (i = 0; i < ov->count; i++) { | ||
184 | struct of_overlay_info *ovinfo = &ov->ovinfo_tab[i]; | ||
185 | |||
186 | err = of_overlay_apply_one(ov, ovinfo->target, ovinfo->overlay); | ||
187 | if (err != 0) { | ||
188 | pr_err("%s: overlay failed '%s'\n", | ||
189 | __func__, ovinfo->target->full_name); | ||
190 | return err; | ||
191 | } | ||
192 | } | ||
193 | |||
194 | return 0; | ||
195 | } | ||
196 | |||
197 | /* | ||
198 | * Find the target node using a number of different strategies | ||
199 | * in order of preference | ||
200 | * | ||
201 | * "target" property containing the phandle of the target | ||
202 | * "target-path" property containing the path of the target | ||
203 | */ | ||
204 | static struct device_node *find_target_node(struct device_node *info_node) | ||
205 | { | ||
206 | const char *path; | ||
207 | u32 val; | ||
208 | int ret; | ||
209 | |||
210 | /* first try to go by using the target as a phandle */ | ||
211 | ret = of_property_read_u32(info_node, "target", &val); | ||
212 | if (ret == 0) | ||
213 | return of_find_node_by_phandle(val); | ||
214 | |||
215 | /* now try to locate by path */ | ||
216 | ret = of_property_read_string(info_node, "target-path", &path); | ||
217 | if (ret == 0) | ||
218 | return of_find_node_by_path(path); | ||
219 | |||
220 | pr_err("%s: Failed to find target for node %p (%s)\n", __func__, | ||
221 | info_node, info_node->name); | ||
222 | |||
223 | return NULL; | ||
224 | } | ||
225 | |||
226 | /** | ||
227 | * of_fill_overlay_info() - Fill an overlay info structure | ||
228 | * @ov Overlay to fill | ||
229 | * @info_node: Device node containing the overlay | ||
230 | * @ovinfo: Pointer to the overlay info structure to fill | ||
231 | * | ||
232 | * Fills an overlay info structure with the overlay information | ||
233 | * from a device node. This device node must have a target property | ||
234 | * which contains a phandle of the overlay target node, and an | ||
235 | * __overlay__ child node which has the overlay contents. | ||
236 | * Both ovinfo->target & ovinfo->overlay have their references taken. | ||
237 | * | ||
238 | * Returns 0 on success, or a negative error value. | ||
239 | */ | ||
240 | static int of_fill_overlay_info(struct of_overlay *ov, | ||
241 | struct device_node *info_node, struct of_overlay_info *ovinfo) | ||
242 | { | ||
243 | ovinfo->overlay = of_get_child_by_name(info_node, "__overlay__"); | ||
244 | if (ovinfo->overlay == NULL) | ||
245 | goto err_fail; | ||
246 | |||
247 | ovinfo->target = find_target_node(info_node); | ||
248 | if (ovinfo->target == NULL) | ||
249 | goto err_fail; | ||
250 | |||
251 | return 0; | ||
252 | |||
253 | err_fail: | ||
254 | of_node_put(ovinfo->target); | ||
255 | of_node_put(ovinfo->overlay); | ||
256 | |||
257 | memset(ovinfo, 0, sizeof(*ovinfo)); | ||
258 | return -EINVAL; | ||
259 | } | ||
260 | |||
261 | /** | ||
262 | * of_build_overlay_info() - Build an overlay info array | ||
263 | * @ov Overlay to build | ||
264 | * @tree: Device node containing all the overlays | ||
265 | * | ||
266 | * Helper function that given a tree containing overlay information, | ||
267 | * allocates and builds an overlay info array containing it, ready | ||
268 | * for use using of_overlay_apply. | ||
269 | * | ||
270 | * Returns 0 on success with the @cntp @ovinfop pointers valid, | ||
271 | * while on error a negative error value is returned. | ||
272 | */ | ||
273 | static int of_build_overlay_info(struct of_overlay *ov, | ||
274 | struct device_node *tree) | ||
275 | { | ||
276 | struct device_node *node; | ||
277 | struct of_overlay_info *ovinfo; | ||
278 | int cnt, err; | ||
279 | |||
280 | /* worst case; every child is a node */ | ||
281 | cnt = 0; | ||
282 | for_each_child_of_node(tree, node) | ||
283 | cnt++; | ||
284 | |||
285 | ovinfo = kcalloc(cnt, sizeof(*ovinfo), GFP_KERNEL); | ||
286 | if (ovinfo == NULL) | ||
287 | return -ENOMEM; | ||
288 | |||
289 | cnt = 0; | ||
290 | for_each_child_of_node(tree, node) { | ||
291 | memset(&ovinfo[cnt], 0, sizeof(*ovinfo)); | ||
292 | err = of_fill_overlay_info(ov, node, &ovinfo[cnt]); | ||
293 | if (err == 0) | ||
294 | cnt++; | ||
295 | } | ||
296 | |||
297 | /* if nothing filled, return error */ | ||
298 | if (cnt == 0) { | ||
299 | kfree(ovinfo); | ||
300 | return -ENODEV; | ||
301 | } | ||
302 | |||
303 | ov->count = cnt; | ||
304 | ov->ovinfo_tab = ovinfo; | ||
305 | |||
306 | return 0; | ||
307 | } | ||
308 | |||
309 | /** | ||
310 | * of_free_overlay_info() - Free an overlay info array | ||
311 | * @ov Overlay to free the overlay info from | ||
312 | * @ovinfo_tab: Array of overlay_info's to free | ||
313 | * | ||
314 | * Releases the memory of a previously allocated ovinfo array | ||
315 | * by of_build_overlay_info. | ||
316 | * Returns 0, or an error if the arguments are bogus. | ||
317 | */ | ||
318 | static int of_free_overlay_info(struct of_overlay *ov) | ||
319 | { | ||
320 | struct of_overlay_info *ovinfo; | ||
321 | int i; | ||
322 | |||
323 | /* do it in reverse */ | ||
324 | for (i = ov->count - 1; i >= 0; i--) { | ||
325 | ovinfo = &ov->ovinfo_tab[i]; | ||
326 | |||
327 | of_node_put(ovinfo->target); | ||
328 | of_node_put(ovinfo->overlay); | ||
329 | } | ||
330 | kfree(ov->ovinfo_tab); | ||
331 | |||
332 | return 0; | ||
333 | } | ||
334 | |||
335 | static LIST_HEAD(ov_list); | ||
336 | static DEFINE_IDR(ov_idr); | ||
337 | |||
338 | /** | ||
339 | * of_overlay_create() - Create and apply an overlay | ||
340 | * @tree: Device node containing all the overlays | ||
341 | * | ||
342 | * Creates and applies an overlay while also keeping track | ||
343 | * of the overlay in a list. This list can be used to prevent | ||
344 | * illegal overlay removals. | ||
345 | * | ||
346 | * Returns the id of the created overlay, or an negative error number | ||
347 | */ | ||
348 | int of_overlay_create(struct device_node *tree) | ||
349 | { | ||
350 | struct of_overlay *ov; | ||
351 | int err, id; | ||
352 | |||
353 | /* allocate the overlay structure */ | ||
354 | ov = kzalloc(sizeof(*ov), GFP_KERNEL); | ||
355 | if (ov == NULL) | ||
356 | return -ENOMEM; | ||
357 | ov->id = -1; | ||
358 | |||
359 | INIT_LIST_HEAD(&ov->node); | ||
360 | |||
361 | of_changeset_init(&ov->cset); | ||
362 | |||
363 | mutex_lock(&of_mutex); | ||
364 | |||
365 | id = idr_alloc(&ov_idr, ov, 0, 0, GFP_KERNEL); | ||
366 | if (id < 0) { | ||
367 | pr_err("%s: idr_alloc() failed for tree@%s\n", | ||
368 | __func__, tree->full_name); | ||
369 | err = id; | ||
370 | goto err_destroy_trans; | ||
371 | } | ||
372 | ov->id = id; | ||
373 | |||
374 | /* build the overlay info structures */ | ||
375 | err = of_build_overlay_info(ov, tree); | ||
376 | if (err) { | ||
377 | pr_err("%s: of_build_overlay_info() failed for tree@%s\n", | ||
378 | __func__, tree->full_name); | ||
379 | goto err_free_idr; | ||
380 | } | ||
381 | |||
382 | /* apply the overlay */ | ||
383 | err = of_overlay_apply(ov); | ||
384 | if (err) { | ||
385 | pr_err("%s: of_overlay_apply() failed for tree@%s\n", | ||
386 | __func__, tree->full_name); | ||
387 | goto err_abort_trans; | ||
388 | } | ||
389 | |||
390 | /* apply the changeset */ | ||
391 | err = of_changeset_apply(&ov->cset); | ||
392 | if (err) { | ||
393 | pr_err("%s: of_changeset_apply() failed for tree@%s\n", | ||
394 | __func__, tree->full_name); | ||
395 | goto err_revert_overlay; | ||
396 | } | ||
397 | |||
398 | /* add to the tail of the overlay list */ | ||
399 | list_add_tail(&ov->node, &ov_list); | ||
400 | |||
401 | mutex_unlock(&of_mutex); | ||
402 | |||
403 | return id; | ||
404 | |||
405 | err_revert_overlay: | ||
406 | err_abort_trans: | ||
407 | of_free_overlay_info(ov); | ||
408 | err_free_idr: | ||
409 | idr_remove(&ov_idr, ov->id); | ||
410 | err_destroy_trans: | ||
411 | of_changeset_destroy(&ov->cset); | ||
412 | kfree(ov); | ||
413 | mutex_unlock(&of_mutex); | ||
414 | |||
415 | return err; | ||
416 | } | ||
417 | EXPORT_SYMBOL_GPL(of_overlay_create); | ||
418 | |||
419 | /* check whether the given node, lies under the given tree */ | ||
420 | static int overlay_subtree_check(struct device_node *tree, | ||
421 | struct device_node *dn) | ||
422 | { | ||
423 | struct device_node *child; | ||
424 | |||
425 | /* match? */ | ||
426 | if (tree == dn) | ||
427 | return 1; | ||
428 | |||
429 | for_each_child_of_node(tree, child) { | ||
430 | if (overlay_subtree_check(child, dn)) | ||
431 | return 1; | ||
432 | } | ||
433 | |||
434 | return 0; | ||
435 | } | ||
436 | |||
437 | /* check whether this overlay is the topmost */ | ||
438 | static int overlay_is_topmost(struct of_overlay *ov, struct device_node *dn) | ||
439 | { | ||
440 | struct of_overlay *ovt; | ||
441 | struct of_changeset_entry *ce; | ||
442 | |||
443 | list_for_each_entry_reverse(ovt, &ov_list, node) { | ||
444 | /* if we hit ourselves, we're done */ | ||
445 | if (ovt == ov) | ||
446 | break; | ||
447 | |||
448 | /* check against each subtree affected by this overlay */ | ||
449 | list_for_each_entry(ce, &ovt->cset.entries, node) { | ||
450 | if (overlay_subtree_check(ce->np, dn)) { | ||
451 | pr_err("%s: #%d clashes #%d @%s\n", | ||
452 | __func__, ov->id, ovt->id, | ||
453 | dn->full_name); | ||
454 | return 0; | ||
455 | } | ||
456 | } | ||
457 | } | ||
458 | |||
459 | /* overlay is topmost */ | ||
460 | return 1; | ||
461 | } | ||
462 | |||
463 | /* | ||
464 | * We can safely remove the overlay only if it's the top-most one. | ||
465 | * Newly applied overlays are inserted at the tail of the overlay list, | ||
466 | * so a top most overlay is the one that is closest to the tail. | ||
467 | * | ||
468 | * The topmost check is done by exploiting this property. For each | ||
469 | * affected device node in the log list we check if this overlay is | ||
470 | * the one closest to the tail. If another overlay has affected this | ||
471 | * device node and is closest to the tail, then removal is not permited. | ||
472 | */ | ||
473 | static int overlay_removal_is_ok(struct of_overlay *ov) | ||
474 | { | ||
475 | struct of_changeset_entry *ce; | ||
476 | |||
477 | list_for_each_entry(ce, &ov->cset.entries, node) { | ||
478 | if (!overlay_is_topmost(ov, ce->np)) { | ||
479 | pr_err("%s: overlay #%d is not topmost\n", | ||
480 | __func__, ov->id); | ||
481 | return 0; | ||
482 | } | ||
483 | } | ||
484 | |||
485 | return 1; | ||
486 | } | ||
487 | |||
488 | /** | ||
489 | * of_overlay_destroy() - Removes an overlay | ||
490 | * @id: Overlay id number returned by a previous call to of_overlay_create | ||
491 | * | ||
492 | * Removes an overlay if it is permissible. | ||
493 | * | ||
494 | * Returns 0 on success, or an negative error number | ||
495 | */ | ||
496 | int of_overlay_destroy(int id) | ||
497 | { | ||
498 | struct of_overlay *ov; | ||
499 | int err; | ||
500 | |||
501 | mutex_lock(&of_mutex); | ||
502 | |||
503 | ov = idr_find(&ov_idr, id); | ||
504 | if (ov == NULL) { | ||
505 | err = -ENODEV; | ||
506 | pr_err("%s: Could not find overlay #%d\n", | ||
507 | __func__, id); | ||
508 | goto out; | ||
509 | } | ||
510 | |||
511 | /* check whether the overlay is safe to remove */ | ||
512 | if (!overlay_removal_is_ok(ov)) { | ||
513 | err = -EBUSY; | ||
514 | pr_err("%s: removal check failed for overlay #%d\n", | ||
515 | __func__, id); | ||
516 | goto out; | ||
517 | } | ||
518 | |||
519 | |||
520 | list_del(&ov->node); | ||
521 | of_changeset_revert(&ov->cset); | ||
522 | of_free_overlay_info(ov); | ||
523 | idr_remove(&ov_idr, id); | ||
524 | of_changeset_destroy(&ov->cset); | ||
525 | kfree(ov); | ||
526 | |||
527 | err = 0; | ||
528 | |||
529 | out: | ||
530 | mutex_unlock(&of_mutex); | ||
531 | |||
532 | return err; | ||
533 | } | ||
534 | EXPORT_SYMBOL_GPL(of_overlay_destroy); | ||
535 | |||
536 | /** | ||
537 | * of_overlay_destroy_all() - Removes all overlays from the system | ||
538 | * | ||
539 | * Removes all overlays from the system in the correct order. | ||
540 | * | ||
541 | * Returns 0 on success, or an negative error number | ||
542 | */ | ||
543 | int of_overlay_destroy_all(void) | ||
544 | { | ||
545 | struct of_overlay *ov, *ovn; | ||
546 | |||
547 | mutex_lock(&of_mutex); | ||
548 | |||
549 | /* the tail of list is guaranteed to be safe to remove */ | ||
550 | list_for_each_entry_safe_reverse(ov, ovn, &ov_list, node) { | ||
551 | list_del(&ov->node); | ||
552 | of_changeset_revert(&ov->cset); | ||
553 | of_free_overlay_info(ov); | ||
554 | idr_remove(&ov_idr, ov->id); | ||
555 | kfree(ov); | ||
556 | } | ||
557 | |||
558 | mutex_unlock(&of_mutex); | ||
559 | |||
560 | return 0; | ||
561 | } | ||
562 | EXPORT_SYMBOL_GPL(of_overlay_destroy_all); | ||
diff --git a/include/linux/of.h b/include/linux/of.h index fe1dec87fd68..aa01cf5852f8 100644 --- a/include/linux/of.h +++ b/include/linux/of.h | |||
@@ -23,6 +23,7 @@ | |||
23 | #include <linux/spinlock.h> | 23 | #include <linux/spinlock.h> |
24 | #include <linux/topology.h> | 24 | #include <linux/topology.h> |
25 | #include <linux/notifier.h> | 25 | #include <linux/notifier.h> |
26 | #include <linux/list.h> | ||
26 | 27 | ||
27 | #include <asm/byteorder.h> | 28 | #include <asm/byteorder.h> |
28 | #include <asm/errno.h> | 29 | #include <asm/errno.h> |
@@ -957,4 +958,34 @@ static inline int of_reconfig_get_state_change(unsigned long action, | |||
957 | /* CONFIG_OF_RESOLVE api */ | 958 | /* CONFIG_OF_RESOLVE api */ |
958 | extern int of_resolve_phandles(struct device_node *tree); | 959 | extern int of_resolve_phandles(struct device_node *tree); |
959 | 960 | ||
961 | /** | ||
962 | * Overlay support | ||
963 | */ | ||
964 | |||
965 | #ifdef CONFIG_OF_OVERLAY | ||
966 | |||
967 | /* ID based overlays; the API for external users */ | ||
968 | int of_overlay_create(struct device_node *tree); | ||
969 | int of_overlay_destroy(int id); | ||
970 | int of_overlay_destroy_all(void); | ||
971 | |||
972 | #else | ||
973 | |||
974 | static inline int of_overlay_create(struct device_node *tree) | ||
975 | { | ||
976 | return -ENOTSUPP; | ||
977 | } | ||
978 | |||
979 | static inline int of_overlay_destroy(int id) | ||
980 | { | ||
981 | return -ENOTSUPP; | ||
982 | } | ||
983 | |||
984 | static inline int of_overlay_destroy_all(void) | ||
985 | { | ||
986 | return -ENOTSUPP; | ||
987 | } | ||
988 | |||
989 | #endif | ||
990 | |||
960 | #endif /* _LINUX_OF_H */ | 991 | #endif /* _LINUX_OF_H */ |