mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-28 10:41:38 -04:00
DeviceTree for 4.15:
- kbuild cleanups and improvements for dtbs
- Code clean-up of overlay code and fixing for some long standing memory
leak and race condition in applying overlays
- Improvements to DT memory usage making sysfs/kobjects optional and
skipping unflattening of disabled nodes. This is part of kernel
tinification efforts.
- Final piece of removing storing the full path for every DT node. The
prerequisite conversion of printk's to use device_node format
specifier happened in 4.14.
- Sync with current upstream dtc. This brings additional checks to dtb
compiling.
- Binding doc tree wide removal of leading 0s from examples
- RTC binding documentation adding missing devices and some
consolidation of duplicated bindings
- Vendor prefix documentation for nutsboard, Silicon Storage Technology,
shimafuji, Tecon Microprocessor Technologies, DH electronics GmbH,
Opal Kelly, and Next Thing
-----BEGIN PGP SIGNATURE-----
iQItBAABCAAXBQJaCwaSEBxyb2JoQGtlcm5lbC5vcmcACgkQ+vtdtY28YcNzeA/8
C8uQhSsX2+UQZvFzcEA8KQAMGT3kYdrcf+gidRKwCEUWg1qscUEpTb3n3Rm5NUbU
RPD1s6GSlh6fJCMHDTQ6Tti/T59L7nZa2/AIGmUishGu4x4q1o18AobpFJmYP/EM
SJPwnmm5RV9WcZFao1y+sY3Xtn8DStxHO4cS+dyF5/EvPN9D8nbLJfu7bgTBAZww
HktIMB9kx+GTipRQZBvBwXoy5MJjthIZub4XwzesA4tGananj4cXlc0xaVxpdYy3
5bO6q5F7cbrZ2uyrF+oIChpCENK4VaXh80m0WHc8EzaG++shzEkR4he1vYkwnV+I
OYo4vsUg9dP8rBksUG1eYhS8fJKPvEBRNP7ETT5utVBy5I/tDEbo/crmQZRTIDIC
hZbhcdZlISZj0DzkMK2ZHQV9UYtRWzXrJbZHFIPP12GCyvXVxYJUIWb9iYnUYSon
KugygsFSpZHMWmfAhemw5/ctJZ19qhM5UIl2KZk5tMBHAf466ILmZjg0me6fYkOp
eADfwHJ1dLMdK79CVMHSfp+vArcZXp35B16c3sWpJB36Il97Mc/9siEufCL4GKX7
IBBnQBlbpSBKBejWVyI7Ip/Xp5u4qAQD+ZMJ9oLqBRqfWerHbDuOERlEOgwGqJYr
9v4HvP7V8eVUvAdqXka4EBfCyAgUzXDAxG2Dfmv9vGU=
=jgpN
-----END PGP SIGNATURE-----
Merge tag 'devicetree-for-4.15' of git://git.kernel.org/pub/scm/linux/kernel/git/robh/linux
Pull DeviceTree updates from Rob Herring:
"A bigger diffstat than usual with the kbuild changes and a tree wide
fix in the binding documentation.
Summary:
- kbuild cleanups and improvements for dtbs
- Code clean-up of overlay code and fixing for some long standing
memory leak and race condition in applying overlays
- Improvements to DT memory usage making sysfs/kobjects optional and
skipping unflattening of disabled nodes. This is part of kernel
tinification efforts.
- Final piece of removing storing the full path for every DT node.
The prerequisite conversion of printk's to use device_node format
specifier happened in 4.14.
- Sync with current upstream dtc. This brings additional checks to
dtb compiling.
- Binding doc tree wide removal of leading 0s from examples
- RTC binding documentation adding missing devices and some
consolidation of duplicated bindings
- Vendor prefix documentation for nutsboard, Silicon Storage
Technology, shimafuji, Tecon Microprocessor Technologies, DH
electronics GmbH, Opal Kelly, and Next Thing"
* tag 'devicetree-for-4.15' of git://git.kernel.org/pub/scm/linux/kernel/git/robh/linux: (55 commits)
dt-bindings: usb: add #phy-cells to usb-nop-xceiv
dt-bindings: Remove leading zeros from bindings notation
kbuild: handle dtb-y and CONFIG_OF_ALL_DTBS natively in Makefile.lib
MIPS: dts: remove bogus bcm96358nb4ser.dtb from dtb-y entry
kbuild: clean up *.dtb and *.dtb.S patterns from top-level Makefile
.gitignore: move *.dtb and *.dtb.S patterns to the top-level .gitignore
.gitignore: sort normal pattern rules alphabetically
dt-bindings: add vendor prefix for Next Thing Co.
scripts/dtc: Update to upstream version v1.4.5-6-gc1e55a5513e9
of: dynamic: fix memory leak related to properties of __of_node_dup
of: overlay: make pr_err() string unique
of: overlay: pr_err from return NOTIFY_OK to overlay apply/remove
of: overlay: remove unneeded check for NULL kbasename()
of: overlay: remove a dependency on device node full_name
of: overlay: simplify applying symbols from an overlay
of: overlay: avoid race condition between applying multiple overlays
of: overlay: loosen overly strict phandle clash check
of: overlay: expand check of whether overlay changeset can be removed
of: overlay: detect cases where device tree may become corrupt
of: overlay: minor restructuring
...
This commit is contained in:
commit
37cb8e1f8e
237 changed files with 3136 additions and 1439 deletions
|
|
@ -46,10 +46,14 @@ config OF_EARLY_FLATTREE
|
|||
config OF_PROMTREE
|
||||
bool
|
||||
|
||||
config OF_KOBJ
|
||||
def_bool SYSFS
|
||||
|
||||
# Hardly any platforms need this. It is safe to select, but only do so if you
|
||||
# need it.
|
||||
config OF_DYNAMIC
|
||||
bool "Support for dynamic device trees" if OF_UNITTEST
|
||||
select OF_KOBJ
|
||||
help
|
||||
On some platforms, the device tree can be manipulated at runtime.
|
||||
While this option is selected automatically on such platforms, you
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
obj-y = base.o device.o platform.o property.o
|
||||
obj-$(CONFIG_OF_KOBJ) += kobj.o
|
||||
obj-$(CONFIG_OF_DYNAMIC) += dynamic.o
|
||||
obj-$(CONFIG_OF_FLATTREE) += fdt.o
|
||||
obj-$(CONFIG_OF_EARLY_FLATTREE) += fdt_address.o
|
||||
|
|
|
|||
|
|
@ -95,108 +95,6 @@ int __weak of_node_to_nid(struct device_node *np)
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_OF_DYNAMIC
|
||||
static void of_node_release(struct kobject *kobj)
|
||||
{
|
||||
/* Without CONFIG_OF_DYNAMIC, no nodes gets freed */
|
||||
}
|
||||
#endif /* CONFIG_OF_DYNAMIC */
|
||||
|
||||
struct kobj_type of_node_ktype = {
|
||||
.release = of_node_release,
|
||||
};
|
||||
|
||||
static ssize_t of_node_property_read(struct file *filp, struct kobject *kobj,
|
||||
struct bin_attribute *bin_attr, char *buf,
|
||||
loff_t offset, size_t count)
|
||||
{
|
||||
struct property *pp = container_of(bin_attr, struct property, attr);
|
||||
return memory_read_from_buffer(buf, count, &offset, pp->value, pp->length);
|
||||
}
|
||||
|
||||
/* always return newly allocated name, caller must free after use */
|
||||
static const char *safe_name(struct kobject *kobj, const char *orig_name)
|
||||
{
|
||||
const char *name = orig_name;
|
||||
struct kernfs_node *kn;
|
||||
int i = 0;
|
||||
|
||||
/* don't be a hero. After 16 tries give up */
|
||||
while (i < 16 && (kn = sysfs_get_dirent(kobj->sd, name))) {
|
||||
sysfs_put(kn);
|
||||
if (name != orig_name)
|
||||
kfree(name);
|
||||
name = kasprintf(GFP_KERNEL, "%s#%i", orig_name, ++i);
|
||||
}
|
||||
|
||||
if (name == orig_name) {
|
||||
name = kstrdup(orig_name, GFP_KERNEL);
|
||||
} else {
|
||||
pr_warn("Duplicate name in %s, renamed to \"%s\"\n",
|
||||
kobject_name(kobj), name);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
int __of_add_property_sysfs(struct device_node *np, struct property *pp)
|
||||
{
|
||||
int rc;
|
||||
|
||||
/* Important: Don't leak passwords */
|
||||
bool secure = strncmp(pp->name, "security-", 9) == 0;
|
||||
|
||||
if (!IS_ENABLED(CONFIG_SYSFS))
|
||||
return 0;
|
||||
|
||||
if (!of_kset || !of_node_is_attached(np))
|
||||
return 0;
|
||||
|
||||
sysfs_bin_attr_init(&pp->attr);
|
||||
pp->attr.attr.name = safe_name(&np->kobj, pp->name);
|
||||
pp->attr.attr.mode = secure ? 0400 : 0444;
|
||||
pp->attr.size = secure ? 0 : pp->length;
|
||||
pp->attr.read = of_node_property_read;
|
||||
|
||||
rc = sysfs_create_bin_file(&np->kobj, &pp->attr);
|
||||
WARN(rc, "error adding attribute %s to node %pOF\n", pp->name, np);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int __of_attach_node_sysfs(struct device_node *np)
|
||||
{
|
||||
const char *name;
|
||||
struct kobject *parent;
|
||||
struct property *pp;
|
||||
int rc;
|
||||
|
||||
if (!IS_ENABLED(CONFIG_SYSFS))
|
||||
return 0;
|
||||
|
||||
if (!of_kset)
|
||||
return 0;
|
||||
|
||||
np->kobj.kset = of_kset;
|
||||
if (!np->parent) {
|
||||
/* Nodes without parents are new top level trees */
|
||||
name = safe_name(&of_kset->kobj, "base");
|
||||
parent = NULL;
|
||||
} else {
|
||||
name = safe_name(&np->parent->kobj, kbasename(np->full_name));
|
||||
parent = &np->parent->kobj;
|
||||
}
|
||||
if (!name)
|
||||
return -ENOMEM;
|
||||
rc = kobject_add(&np->kobj, parent, "%s", name);
|
||||
kfree(name);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
for_each_property_of_node(np, pp)
|
||||
__of_add_property_sysfs(np, pp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __init of_core_init(void)
|
||||
{
|
||||
struct device_node *np;
|
||||
|
|
@ -760,7 +658,7 @@ struct device_node *of_get_child_by_name(const struct device_node *node,
|
|||
}
|
||||
EXPORT_SYMBOL(of_get_child_by_name);
|
||||
|
||||
static struct device_node *__of_find_node_by_path(struct device_node *parent,
|
||||
struct device_node *__of_find_node_by_path(struct device_node *parent,
|
||||
const char *path)
|
||||
{
|
||||
struct device_node *child;
|
||||
|
|
@ -1504,22 +1402,6 @@ int __of_remove_property(struct device_node *np, struct property *prop)
|
|||
return 0;
|
||||
}
|
||||
|
||||
void __of_sysfs_remove_bin_file(struct device_node *np, struct property *prop)
|
||||
{
|
||||
sysfs_remove_bin_file(&np->kobj, &prop->attr);
|
||||
kfree(prop->attr.attr.name);
|
||||
}
|
||||
|
||||
void __of_remove_property_sysfs(struct device_node *np, struct property *prop)
|
||||
{
|
||||
if (!IS_ENABLED(CONFIG_SYSFS))
|
||||
return;
|
||||
|
||||
/* at early boot, bail here and defer setup to of_init() */
|
||||
if (of_kset && of_node_is_attached(np))
|
||||
__of_sysfs_remove_bin_file(np, prop);
|
||||
}
|
||||
|
||||
/**
|
||||
* of_remove_property - Remove a property from a node.
|
||||
*
|
||||
|
|
@ -1579,21 +1461,6 @@ int __of_update_property(struct device_node *np, struct property *newprop,
|
|||
return 0;
|
||||
}
|
||||
|
||||
void __of_update_property_sysfs(struct device_node *np, struct property *newprop,
|
||||
struct property *oldprop)
|
||||
{
|
||||
if (!IS_ENABLED(CONFIG_SYSFS))
|
||||
return;
|
||||
|
||||
/* At early boot, bail out and defer setup to of_init() */
|
||||
if (!of_kset)
|
||||
return;
|
||||
|
||||
if (oldprop)
|
||||
__of_sysfs_remove_bin_file(np, oldprop);
|
||||
__of_add_property_sysfs(np, newprop);
|
||||
}
|
||||
|
||||
/*
|
||||
* of_update_property - Update a property in a node, if the property does
|
||||
* not exist, add it.
|
||||
|
|
|
|||
|
|
@ -16,6 +16,11 @@
|
|||
|
||||
#include "of_private.h"
|
||||
|
||||
static struct device_node *kobj_to_device_node(struct kobject *kobj)
|
||||
{
|
||||
return container_of(kobj, struct device_node, kobj);
|
||||
}
|
||||
|
||||
/**
|
||||
* of_node_get() - Increment refcount of a node
|
||||
* @node: Node to inc refcount, NULL is supported to simplify writing of
|
||||
|
|
@ -43,28 +48,6 @@ void of_node_put(struct device_node *node)
|
|||
}
|
||||
EXPORT_SYMBOL(of_node_put);
|
||||
|
||||
void __of_detach_node_sysfs(struct device_node *np)
|
||||
{
|
||||
struct property *pp;
|
||||
|
||||
if (!IS_ENABLED(CONFIG_SYSFS))
|
||||
return;
|
||||
|
||||
BUG_ON(!of_node_is_initialized(np));
|
||||
if (!of_kset)
|
||||
return;
|
||||
|
||||
/* only remove properties if on sysfs */
|
||||
if (of_node_is_attached(np)) {
|
||||
for_each_property_of_node(np, pp)
|
||||
__of_sysfs_remove_bin_file(np, pp);
|
||||
kobject_del(&np->kobj);
|
||||
}
|
||||
|
||||
/* finally remove the kobj_init ref */
|
||||
of_node_put(np);
|
||||
}
|
||||
|
||||
static BLOCKING_NOTIFIER_HEAD(of_reconfig_chain);
|
||||
|
||||
int of_reconfig_notifier_register(struct notifier_block *nb)
|
||||
|
|
@ -315,6 +298,18 @@ int of_detach_node(struct device_node *np)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(of_detach_node);
|
||||
|
||||
static void property_list_free(struct property *prop_list)
|
||||
{
|
||||
struct property *prop, *next;
|
||||
|
||||
for (prop = prop_list; prop != NULL; prop = next) {
|
||||
next = prop->next;
|
||||
kfree(prop->name);
|
||||
kfree(prop->value);
|
||||
kfree(prop);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* of_node_release() - release a dynamically allocated node
|
||||
* @kref: kref element of the node to be released
|
||||
|
|
@ -324,7 +319,6 @@ EXPORT_SYMBOL_GPL(of_detach_node);
|
|||
void of_node_release(struct kobject *kobj)
|
||||
{
|
||||
struct device_node *node = kobj_to_device_node(kobj);
|
||||
struct property *prop = node->properties;
|
||||
|
||||
/* We should never be releasing nodes that haven't been detached. */
|
||||
if (!of_node_check_flag(node, OF_DETACHED)) {
|
||||
|
|
@ -335,18 +329,9 @@ void of_node_release(struct kobject *kobj)
|
|||
if (!of_node_check_flag(node, OF_DYNAMIC))
|
||||
return;
|
||||
|
||||
while (prop) {
|
||||
struct property *next = prop->next;
|
||||
kfree(prop->name);
|
||||
kfree(prop->value);
|
||||
kfree(prop);
|
||||
prop = next;
|
||||
property_list_free(node->properties);
|
||||
property_list_free(node->deadprops);
|
||||
|
||||
if (!prop) {
|
||||
prop = node->deadprops;
|
||||
node->deadprops = NULL;
|
||||
}
|
||||
}
|
||||
kfree(node->full_name);
|
||||
kfree(node->data);
|
||||
kfree(node);
|
||||
|
|
@ -508,11 +493,12 @@ static void __of_changeset_entry_invert(struct of_changeset_entry *ce,
|
|||
}
|
||||
}
|
||||
|
||||
static void __of_changeset_entry_notify(struct of_changeset_entry *ce, bool revert)
|
||||
static int __of_changeset_entry_notify(struct of_changeset_entry *ce,
|
||||
bool revert)
|
||||
{
|
||||
struct of_reconfig_data rd;
|
||||
struct of_changeset_entry ce_inverted;
|
||||
int ret;
|
||||
int ret = 0;
|
||||
|
||||
if (revert) {
|
||||
__of_changeset_entry_invert(ce, &ce_inverted);
|
||||
|
|
@ -534,11 +520,12 @@ static void __of_changeset_entry_notify(struct of_changeset_entry *ce, bool reve
|
|||
default:
|
||||
pr_err("invalid devicetree changeset action: %i\n",
|
||||
(int)ce->action);
|
||||
return;
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
if (ret)
|
||||
pr_err("changeset notifier error @%pOF\n", ce->np);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __of_changeset_entry_apply(struct of_changeset_entry *ce)
|
||||
|
|
@ -672,32 +659,82 @@ void of_changeset_destroy(struct of_changeset *ocs)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(of_changeset_destroy);
|
||||
|
||||
int __of_changeset_apply(struct of_changeset *ocs)
|
||||
/*
|
||||
* Apply the changeset entries in @ocs.
|
||||
* If apply fails, an attempt is made to revert the entries that were
|
||||
* successfully applied.
|
||||
*
|
||||
* If multiple revert errors occur then only the final revert error is reported.
|
||||
*
|
||||
* Returns 0 on success, a negative error value in case of an error.
|
||||
* If a revert error occurs, it is returned in *ret_revert.
|
||||
*/
|
||||
int __of_changeset_apply_entries(struct of_changeset *ocs, int *ret_revert)
|
||||
{
|
||||
struct of_changeset_entry *ce;
|
||||
int ret;
|
||||
int ret, ret_tmp;
|
||||
|
||||
/* perform the rest of the work */
|
||||
pr_debug("changeset: applying...\n");
|
||||
list_for_each_entry(ce, &ocs->entries, node) {
|
||||
ret = __of_changeset_entry_apply(ce);
|
||||
if (ret) {
|
||||
pr_err("Error applying changeset (%d)\n", ret);
|
||||
list_for_each_entry_continue_reverse(ce, &ocs->entries, node)
|
||||
__of_changeset_entry_revert(ce);
|
||||
list_for_each_entry_continue_reverse(ce, &ocs->entries,
|
||||
node) {
|
||||
ret_tmp = __of_changeset_entry_revert(ce);
|
||||
if (ret_tmp)
|
||||
*ret_revert = ret_tmp;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
pr_debug("changeset: applied, emitting notifiers.\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns 0 on success, a negative error value in case of an error.
|
||||
*
|
||||
* If multiple changset entry notification errors occur then only the
|
||||
* final notification error is reported.
|
||||
*/
|
||||
int __of_changeset_apply_notify(struct of_changeset *ocs)
|
||||
{
|
||||
struct of_changeset_entry *ce;
|
||||
int ret = 0, ret_tmp;
|
||||
|
||||
pr_debug("changeset: emitting notifiers.\n");
|
||||
|
||||
/* drop the global lock while emitting notifiers */
|
||||
mutex_unlock(&of_mutex);
|
||||
list_for_each_entry(ce, &ocs->entries, node)
|
||||
__of_changeset_entry_notify(ce, 0);
|
||||
list_for_each_entry(ce, &ocs->entries, node) {
|
||||
ret_tmp = __of_changeset_entry_notify(ce, 0);
|
||||
if (ret_tmp)
|
||||
ret = ret_tmp;
|
||||
}
|
||||
mutex_lock(&of_mutex);
|
||||
pr_debug("changeset: notifiers sent.\n");
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns 0 on success, a negative error value in case of an error.
|
||||
*
|
||||
* If a changeset entry apply fails, an attempt is made to revert any
|
||||
* previous entries in the changeset. If any of the reverts fails,
|
||||
* that failure is not reported. Thus the state of the device tree
|
||||
* is unknown if an apply error occurs.
|
||||
*/
|
||||
static int __of_changeset_apply(struct of_changeset *ocs)
|
||||
{
|
||||
int ret, ret_revert = 0;
|
||||
|
||||
ret = __of_changeset_apply_entries(ocs, &ret_revert);
|
||||
if (!ret)
|
||||
ret = __of_changeset_apply_notify(ocs);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -724,31 +761,74 @@ int of_changeset_apply(struct of_changeset *ocs)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(of_changeset_apply);
|
||||
|
||||
int __of_changeset_revert(struct of_changeset *ocs)
|
||||
/*
|
||||
* Revert the changeset entries in @ocs.
|
||||
* If revert fails, an attempt is made to re-apply the entries that were
|
||||
* successfully removed.
|
||||
*
|
||||
* If multiple re-apply errors occur then only the final apply error is
|
||||
* reported.
|
||||
*
|
||||
* Returns 0 on success, a negative error value in case of an error.
|
||||
* If an apply error occurs, it is returned in *ret_apply.
|
||||
*/
|
||||
int __of_changeset_revert_entries(struct of_changeset *ocs, int *ret_apply)
|
||||
{
|
||||
struct of_changeset_entry *ce;
|
||||
int ret;
|
||||
int ret, ret_tmp;
|
||||
|
||||
pr_debug("changeset: reverting...\n");
|
||||
list_for_each_entry_reverse(ce, &ocs->entries, node) {
|
||||
ret = __of_changeset_entry_revert(ce);
|
||||
if (ret) {
|
||||
pr_err("Error reverting changeset (%d)\n", ret);
|
||||
list_for_each_entry_continue(ce, &ocs->entries, node)
|
||||
__of_changeset_entry_apply(ce);
|
||||
list_for_each_entry_continue(ce, &ocs->entries, node) {
|
||||
ret_tmp = __of_changeset_entry_apply(ce);
|
||||
if (ret_tmp)
|
||||
*ret_apply = ret_tmp;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
pr_debug("changeset: reverted, emitting notifiers.\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* If multiple changset entry notification errors occur then only the
|
||||
* final notification error is reported.
|
||||
*/
|
||||
int __of_changeset_revert_notify(struct of_changeset *ocs)
|
||||
{
|
||||
struct of_changeset_entry *ce;
|
||||
int ret = 0, ret_tmp;
|
||||
|
||||
pr_debug("changeset: emitting notifiers.\n");
|
||||
|
||||
/* drop the global lock while emitting notifiers */
|
||||
mutex_unlock(&of_mutex);
|
||||
list_for_each_entry_reverse(ce, &ocs->entries, node)
|
||||
__of_changeset_entry_notify(ce, 1);
|
||||
list_for_each_entry_reverse(ce, &ocs->entries, node) {
|
||||
ret_tmp = __of_changeset_entry_notify(ce, 1);
|
||||
if (ret_tmp)
|
||||
ret = ret_tmp;
|
||||
}
|
||||
mutex_lock(&of_mutex);
|
||||
pr_debug("changeset: notifiers sent.\n");
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __of_changeset_revert(struct of_changeset *ocs)
|
||||
{
|
||||
int ret, ret_reply;
|
||||
|
||||
ret_reply = 0;
|
||||
ret = __of_changeset_revert_entries(ocs, &ret_reply);
|
||||
|
||||
if (!ret)
|
||||
ret = __of_changeset_revert_notify(ocs);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -775,7 +855,7 @@ int of_changeset_revert(struct of_changeset *ocs)
|
|||
EXPORT_SYMBOL_GPL(of_changeset_revert);
|
||||
|
||||
/**
|
||||
* of_changeset_action - Perform a changeset action
|
||||
* of_changeset_action - Add an action to the tail of the changeset list
|
||||
*
|
||||
* @ocs: changeset pointer
|
||||
* @action: action to perform
|
||||
|
|
|
|||
|
|
@ -132,6 +132,19 @@ bool of_fdt_is_big_endian(const void *blob, unsigned long node)
|
|||
return false;
|
||||
}
|
||||
|
||||
static bool of_fdt_device_is_available(const void *blob, unsigned long node)
|
||||
{
|
||||
const char *status = fdt_getprop(blob, node, "status", NULL);
|
||||
|
||||
if (!status)
|
||||
return true;
|
||||
|
||||
if (!strcmp(status, "ok") || !strcmp(status, "okay"))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* of_fdt_match - Return true if node matches a list of compatible values
|
||||
*/
|
||||
|
|
@ -266,74 +279,32 @@ static void populate_properties(const void *blob,
|
|||
*pprev = NULL;
|
||||
}
|
||||
|
||||
static unsigned int populate_node(const void *blob,
|
||||
int offset,
|
||||
void **mem,
|
||||
struct device_node *dad,
|
||||
unsigned int fpsize,
|
||||
struct device_node **pnp,
|
||||
bool dryrun)
|
||||
static bool populate_node(const void *blob,
|
||||
int offset,
|
||||
void **mem,
|
||||
struct device_node *dad,
|
||||
struct device_node **pnp,
|
||||
bool dryrun)
|
||||
{
|
||||
struct device_node *np;
|
||||
const char *pathp;
|
||||
unsigned int l, allocl;
|
||||
int new_format = 0;
|
||||
|
||||
pathp = fdt_get_name(blob, offset, &l);
|
||||
if (!pathp) {
|
||||
*pnp = NULL;
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
allocl = ++l;
|
||||
|
||||
/* version 0x10 has a more compact unit name here instead of the full
|
||||
* path. we accumulate the full path size using "fpsize", we'll rebuild
|
||||
* it later. We detect this because the first character of the name is
|
||||
* not '/'.
|
||||
*/
|
||||
if ((*pathp) != '/') {
|
||||
new_format = 1;
|
||||
if (fpsize == 0) {
|
||||
/* root node: special case. fpsize accounts for path
|
||||
* plus terminating zero. root node only has '/', so
|
||||
* fpsize should be 2, but we want to avoid the first
|
||||
* level nodes to have two '/' so we use fpsize 1 here
|
||||
*/
|
||||
fpsize = 1;
|
||||
allocl = 2;
|
||||
l = 1;
|
||||
pathp = "";
|
||||
} else {
|
||||
/* account for '/' and path size minus terminal 0
|
||||
* already in 'l'
|
||||
*/
|
||||
fpsize += l;
|
||||
allocl = fpsize;
|
||||
}
|
||||
}
|
||||
|
||||
np = unflatten_dt_alloc(mem, sizeof(struct device_node) + allocl,
|
||||
__alignof__(struct device_node));
|
||||
if (!dryrun) {
|
||||
char *fn;
|
||||
of_node_init(np);
|
||||
np->full_name = fn = ((char *)np) + sizeof(*np);
|
||||
if (new_format) {
|
||||
/* rebuild full path for new format */
|
||||
if (dad && dad->parent) {
|
||||
strcpy(fn, dad->full_name);
|
||||
#ifdef DEBUG
|
||||
if ((strlen(fn) + l + 1) != allocl) {
|
||||
pr_debug("%s: p: %d, l: %d, a: %d\n",
|
||||
pathp, (int)strlen(fn),
|
||||
l, allocl);
|
||||
}
|
||||
#endif
|
||||
fn += strlen(fn);
|
||||
}
|
||||
*(fn++) = '/';
|
||||
}
|
||||
|
||||
memcpy(fn, pathp, l);
|
||||
|
||||
if (dad != NULL) {
|
||||
|
|
@ -355,7 +326,7 @@ static unsigned int populate_node(const void *blob,
|
|||
}
|
||||
|
||||
*pnp = np;
|
||||
return fpsize;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void reverse_nodes(struct device_node *parent)
|
||||
|
|
@ -399,7 +370,6 @@ static int unflatten_dt_nodes(const void *blob,
|
|||
struct device_node *root;
|
||||
int offset = 0, depth = 0, initial_depth = 0;
|
||||
#define FDT_MAX_DEPTH 64
|
||||
unsigned int fpsizes[FDT_MAX_DEPTH];
|
||||
struct device_node *nps[FDT_MAX_DEPTH];
|
||||
void *base = mem;
|
||||
bool dryrun = !base;
|
||||
|
|
@ -418,7 +388,6 @@ static int unflatten_dt_nodes(const void *blob,
|
|||
depth = initial_depth = 1;
|
||||
|
||||
root = dad;
|
||||
fpsizes[depth] = dad ? strlen(of_node_full_name(dad)) : 0;
|
||||
nps[depth] = dad;
|
||||
|
||||
for (offset = 0;
|
||||
|
|
@ -427,11 +396,12 @@ static int unflatten_dt_nodes(const void *blob,
|
|||
if (WARN_ON_ONCE(depth >= FDT_MAX_DEPTH))
|
||||
continue;
|
||||
|
||||
fpsizes[depth+1] = populate_node(blob, offset, &mem,
|
||||
nps[depth],
|
||||
fpsizes[depth],
|
||||
&nps[depth+1], dryrun);
|
||||
if (!fpsizes[depth+1])
|
||||
if (!IS_ENABLED(CONFIG_OF_KOBJ) &&
|
||||
!of_fdt_device_is_available(blob, offset))
|
||||
continue;
|
||||
|
||||
if (!populate_node(blob, offset, &mem, nps[depth],
|
||||
&nps[depth+1], dryrun))
|
||||
return mem - base;
|
||||
|
||||
if (!dryrun && nodepp && !*nodepp)
|
||||
|
|
@ -467,6 +437,7 @@ static int unflatten_dt_nodes(const void *blob,
|
|||
* @mynodes: The device_node tree created by the call
|
||||
* @dt_alloc: An allocator that provides a virtual address to memory
|
||||
* for the resulting tree
|
||||
* @detached: if true set OF_DETACHED on @mynodes
|
||||
*
|
||||
* Returns NULL on failure or the memory chunk containing the unflattened
|
||||
* device tree on success.
|
||||
|
|
@ -652,7 +623,6 @@ static int __init __fdt_scan_reserved_mem(unsigned long node, const char *uname,
|
|||
int depth, void *data)
|
||||
{
|
||||
static int found;
|
||||
const char *status;
|
||||
int err;
|
||||
|
||||
if (!found && depth == 1 && strcmp(uname, "reserved-memory") == 0) {
|
||||
|
|
@ -672,8 +642,7 @@ static int __init __fdt_scan_reserved_mem(unsigned long node, const char *uname,
|
|||
return 1;
|
||||
}
|
||||
|
||||
status = of_get_flat_dt_prop(node, "status", NULL);
|
||||
if (status && strcmp(status, "okay") != 0 && strcmp(status, "ok") != 0)
|
||||
if (!of_fdt_device_is_available(initial_boot_params, node))
|
||||
return 0;
|
||||
|
||||
err = __reserved_mem_reserve_reg(node, uname);
|
||||
|
|
|
|||
164
drivers/of/kobj.c
Normal file
164
drivers/of/kobj.c
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
#include <linux/of.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "of_private.h"
|
||||
|
||||
/* true when node is initialized */
|
||||
static int of_node_is_initialized(struct device_node *node)
|
||||
{
|
||||
return node && node->kobj.state_initialized;
|
||||
}
|
||||
|
||||
/* true when node is attached (i.e. present on sysfs) */
|
||||
int of_node_is_attached(struct device_node *node)
|
||||
{
|
||||
return node && node->kobj.state_in_sysfs;
|
||||
}
|
||||
|
||||
|
||||
#ifndef CONFIG_OF_DYNAMIC
|
||||
static void of_node_release(struct kobject *kobj)
|
||||
{
|
||||
/* Without CONFIG_OF_DYNAMIC, no nodes gets freed */
|
||||
}
|
||||
#endif /* CONFIG_OF_DYNAMIC */
|
||||
|
||||
struct kobj_type of_node_ktype = {
|
||||
.release = of_node_release,
|
||||
};
|
||||
|
||||
static ssize_t of_node_property_read(struct file *filp, struct kobject *kobj,
|
||||
struct bin_attribute *bin_attr, char *buf,
|
||||
loff_t offset, size_t count)
|
||||
{
|
||||
struct property *pp = container_of(bin_attr, struct property, attr);
|
||||
return memory_read_from_buffer(buf, count, &offset, pp->value, pp->length);
|
||||
}
|
||||
|
||||
/* always return newly allocated name, caller must free after use */
|
||||
static const char *safe_name(struct kobject *kobj, const char *orig_name)
|
||||
{
|
||||
const char *name = orig_name;
|
||||
struct kernfs_node *kn;
|
||||
int i = 0;
|
||||
|
||||
/* don't be a hero. After 16 tries give up */
|
||||
while (i < 16 && (kn = sysfs_get_dirent(kobj->sd, name))) {
|
||||
sysfs_put(kn);
|
||||
if (name != orig_name)
|
||||
kfree(name);
|
||||
name = kasprintf(GFP_KERNEL, "%s#%i", orig_name, ++i);
|
||||
}
|
||||
|
||||
if (name == orig_name) {
|
||||
name = kstrdup(orig_name, GFP_KERNEL);
|
||||
} else {
|
||||
pr_warn("Duplicate name in %s, renamed to \"%s\"\n",
|
||||
kobject_name(kobj), name);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
int __of_add_property_sysfs(struct device_node *np, struct property *pp)
|
||||
{
|
||||
int rc;
|
||||
|
||||
/* Important: Don't leak passwords */
|
||||
bool secure = strncmp(pp->name, "security-", 9) == 0;
|
||||
|
||||
if (!IS_ENABLED(CONFIG_SYSFS))
|
||||
return 0;
|
||||
|
||||
if (!of_kset || !of_node_is_attached(np))
|
||||
return 0;
|
||||
|
||||
sysfs_bin_attr_init(&pp->attr);
|
||||
pp->attr.attr.name = safe_name(&np->kobj, pp->name);
|
||||
pp->attr.attr.mode = secure ? 0400 : 0444;
|
||||
pp->attr.size = secure ? 0 : pp->length;
|
||||
pp->attr.read = of_node_property_read;
|
||||
|
||||
rc = sysfs_create_bin_file(&np->kobj, &pp->attr);
|
||||
WARN(rc, "error adding attribute %s to node %pOF\n", pp->name, np);
|
||||
return rc;
|
||||
}
|
||||
|
||||
void __of_sysfs_remove_bin_file(struct device_node *np, struct property *prop)
|
||||
{
|
||||
if (!IS_ENABLED(CONFIG_SYSFS))
|
||||
return;
|
||||
|
||||
sysfs_remove_bin_file(&np->kobj, &prop->attr);
|
||||
kfree(prop->attr.attr.name);
|
||||
}
|
||||
|
||||
void __of_remove_property_sysfs(struct device_node *np, struct property *prop)
|
||||
{
|
||||
/* at early boot, bail here and defer setup to of_init() */
|
||||
if (of_kset && of_node_is_attached(np))
|
||||
__of_sysfs_remove_bin_file(np, prop);
|
||||
}
|
||||
|
||||
void __of_update_property_sysfs(struct device_node *np, struct property *newprop,
|
||||
struct property *oldprop)
|
||||
{
|
||||
/* At early boot, bail out and defer setup to of_init() */
|
||||
if (!of_kset)
|
||||
return;
|
||||
|
||||
if (oldprop)
|
||||
__of_sysfs_remove_bin_file(np, oldprop);
|
||||
__of_add_property_sysfs(np, newprop);
|
||||
}
|
||||
|
||||
int __of_attach_node_sysfs(struct device_node *np)
|
||||
{
|
||||
const char *name;
|
||||
struct kobject *parent;
|
||||
struct property *pp;
|
||||
int rc;
|
||||
|
||||
if (!of_kset)
|
||||
return 0;
|
||||
|
||||
np->kobj.kset = of_kset;
|
||||
if (!np->parent) {
|
||||
/* Nodes without parents are new top level trees */
|
||||
name = safe_name(&of_kset->kobj, "base");
|
||||
parent = NULL;
|
||||
} else {
|
||||
name = safe_name(&np->parent->kobj, kbasename(np->full_name));
|
||||
parent = &np->parent->kobj;
|
||||
}
|
||||
if (!name)
|
||||
return -ENOMEM;
|
||||
rc = kobject_add(&np->kobj, parent, "%s", name);
|
||||
kfree(name);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
for_each_property_of_node(np, pp)
|
||||
__of_add_property_sysfs(np, pp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __of_detach_node_sysfs(struct device_node *np)
|
||||
{
|
||||
struct property *pp;
|
||||
|
||||
BUG_ON(!of_node_is_initialized(np));
|
||||
if (!of_kset)
|
||||
return;
|
||||
|
||||
/* only remove properties if on sysfs */
|
||||
if (of_node_is_attached(np)) {
|
||||
for_each_property_of_node(np, pp)
|
||||
__of_sysfs_remove_bin_file(np, pp);
|
||||
kobject_del(&np->kobj);
|
||||
}
|
||||
|
||||
/* finally remove the kobj_init ref */
|
||||
of_node_put(np);
|
||||
}
|
||||
|
||||
|
|
@ -35,18 +35,16 @@ extern struct mutex of_mutex;
|
|||
extern struct list_head aliases_lookup;
|
||||
extern struct kset *of_kset;
|
||||
|
||||
|
||||
static inline struct device_node *kobj_to_device_node(struct kobject *kobj)
|
||||
{
|
||||
return container_of(kobj, struct device_node, kobj);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_OF_DYNAMIC)
|
||||
extern int of_property_notify(int action, struct device_node *np,
|
||||
struct property *prop, struct property *old_prop);
|
||||
extern void of_node_release(struct kobject *kobj);
|
||||
extern int __of_changeset_apply(struct of_changeset *ocs);
|
||||
extern int __of_changeset_revert(struct of_changeset *ocs);
|
||||
extern int __of_changeset_apply_entries(struct of_changeset *ocs,
|
||||
int *ret_revert);
|
||||
extern int __of_changeset_apply_notify(struct of_changeset *ocs);
|
||||
extern int __of_changeset_revert_entries(struct of_changeset *ocs,
|
||||
int *ret_apply);
|
||||
extern int __of_changeset_revert_notify(struct of_changeset *ocs);
|
||||
#else /* CONFIG_OF_DYNAMIC */
|
||||
static inline int of_property_notify(int action, struct device_node *np,
|
||||
struct property *prop, struct property *old_prop)
|
||||
|
|
@ -55,6 +53,41 @@ static inline int of_property_notify(int action, struct device_node *np,
|
|||
}
|
||||
#endif /* CONFIG_OF_DYNAMIC */
|
||||
|
||||
#if defined(CONFIG_OF_KOBJ)
|
||||
int of_node_is_attached(struct device_node *node);
|
||||
int __of_add_property_sysfs(struct device_node *np, struct property *pp);
|
||||
void __of_remove_property_sysfs(struct device_node *np, struct property *prop);
|
||||
void __of_update_property_sysfs(struct device_node *np, struct property *newprop,
|
||||
struct property *oldprop);
|
||||
int __of_attach_node_sysfs(struct device_node *np);
|
||||
void __of_detach_node_sysfs(struct device_node *np);
|
||||
#else
|
||||
static inline int __of_add_property_sysfs(struct device_node *np, struct property *pp)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline void __of_remove_property_sysfs(struct device_node *np, struct property *prop) {}
|
||||
static inline void __of_update_property_sysfs(struct device_node *np,
|
||||
struct property *newprop, struct property *oldprop) {}
|
||||
static inline int __of_attach_node_sysfs(struct device_node *np)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline void __of_detach_node_sysfs(struct device_node *np) {}
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_OF_RESOLVE)
|
||||
int of_resolve_phandles(struct device_node *tree);
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_OF_OVERLAY)
|
||||
void of_overlay_mutex_lock(void);
|
||||
void of_overlay_mutex_unlock(void);
|
||||
#else
|
||||
static inline void of_overlay_mutex_lock(void) {};
|
||||
static inline void of_overlay_mutex_unlock(void) {};
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_OF_UNITTEST) && defined(CONFIG_OF_OVERLAY)
|
||||
extern void __init unittest_unflatten_overlay_base(void);
|
||||
#else
|
||||
|
|
@ -77,6 +110,8 @@ extern void *__unflatten_device_tree(const void *blob,
|
|||
struct property *__of_prop_dup(const struct property *prop, gfp_t allocflags);
|
||||
__printf(2, 3) struct device_node *__of_node_dup(const struct device_node *np, const char *fmt, ...);
|
||||
|
||||
struct device_node *__of_find_node_by_path(struct device_node *parent,
|
||||
const char *path);
|
||||
struct device_node *__of_find_node_by_full_path(struct device_node *node,
|
||||
const char *path);
|
||||
|
||||
|
|
|
|||
1053
drivers/of/overlay.c
1053
drivers/of/overlay.c
File diff suppressed because it is too large
Load diff
|
|
@ -84,10 +84,9 @@ static int update_usages_of_a_phandle_reference(struct device_node *overlay,
|
|||
int offset, len;
|
||||
int err = 0;
|
||||
|
||||
value = kmalloc(prop_fixup->length, GFP_KERNEL);
|
||||
value = kmemdup(prop_fixup->value, prop_fixup->length, GFP_KERNEL);
|
||||
if (!value)
|
||||
return -ENOMEM;
|
||||
memcpy(value, prop_fixup->value, prop_fixup->length);
|
||||
|
||||
/* prop_fixup contains a list of tuples of path:property_name:offset */
|
||||
end = value + prop_fixup->length;
|
||||
|
|
@ -165,7 +164,6 @@ static int adjust_local_phandle_references(struct device_node *local_fixups,
|
|||
struct property *prop_fix, *prop;
|
||||
int err, i, count;
|
||||
unsigned int off;
|
||||
phandle phandle;
|
||||
|
||||
if (!local_fixups)
|
||||
return 0;
|
||||
|
|
@ -195,9 +193,7 @@ static int adjust_local_phandle_references(struct device_node *local_fixups,
|
|||
if ((off + 4) > prop->length)
|
||||
return -EINVAL;
|
||||
|
||||
phandle = be32_to_cpu(*(__be32 *)(prop->value + off));
|
||||
phandle += phandle_delta;
|
||||
*(__be32 *)(prop->value + off) = cpu_to_be32(phandle);
|
||||
be32_add_cpu(prop->value + off, phandle_delta);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -275,11 +271,18 @@ int of_resolve_phandles(struct device_node *overlay)
|
|||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
#if 0
|
||||
Temporarily disable check so that old style overlay unittests
|
||||
do not fail when of_resolve_phandles() is moved into
|
||||
of_overlay_apply().
|
||||
|
||||
if (!of_node_check_flag(overlay, OF_DETACHED)) {
|
||||
pr_err("overlay not detached\n");
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
#endif
|
||||
|
||||
phandle_delta = live_tree_max_phandle() + 1;
|
||||
adjust_overlay_phandles(overlay, phandle_delta);
|
||||
|
|
|
|||
2
drivers/of/unittest-data/.gitignore
vendored
2
drivers/of/unittest-data/.gitignore
vendored
|
|
@ -1,2 +0,0 @@
|
|||
testcases.dtb
|
||||
testcases.dtb.S
|
||||
|
|
@ -994,10 +994,17 @@ static int __init unittest_data_add(void)
|
|||
pr_warn("%s: No tree to attach; not running tests\n", __func__);
|
||||
return -ENODATA;
|
||||
}
|
||||
of_node_set_flag(unittest_data_node, OF_DETACHED);
|
||||
|
||||
/*
|
||||
* This lock normally encloses of_overlay_apply() as well as
|
||||
* of_resolve_phandles().
|
||||
*/
|
||||
of_overlay_mutex_lock();
|
||||
|
||||
rc = of_resolve_phandles(unittest_data_node);
|
||||
if (rc) {
|
||||
pr_err("%s: Failed to resolve phandles (rc=%i)\n", __func__, rc);
|
||||
of_overlay_mutex_unlock();
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
|
@ -1007,6 +1014,7 @@ static int __init unittest_data_add(void)
|
|||
__of_attach_node_sysfs(np);
|
||||
of_aliases = of_find_node_by_path("/aliases");
|
||||
of_chosen = of_find_node_by_path("/chosen");
|
||||
of_overlay_mutex_unlock();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1019,6 +1027,9 @@ static int __init unittest_data_add(void)
|
|||
attach_node_and_children(np);
|
||||
np = next;
|
||||
}
|
||||
|
||||
of_overlay_mutex_unlock();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1219,7 +1230,7 @@ static void of_unittest_untrack_overlay(int id)
|
|||
|
||||
static void of_unittest_destroy_tracked_overlays(void)
|
||||
{
|
||||
int id, ret, defers;
|
||||
int id, ret, defers, ovcs_id;
|
||||
|
||||
if (overlay_first_id < 0)
|
||||
return;
|
||||
|
|
@ -1232,7 +1243,8 @@ static void of_unittest_destroy_tracked_overlays(void)
|
|||
if (!(overlay_id_bits[BIT_WORD(id)] & BIT_MASK(id)))
|
||||
continue;
|
||||
|
||||
ret = of_overlay_destroy(id + overlay_first_id);
|
||||
ovcs_id = id + overlay_first_id;
|
||||
ret = of_overlay_remove(&ovcs_id);
|
||||
if (ret == -ENODEV) {
|
||||
pr_warn("%s: no overlay to destroy for #%d\n",
|
||||
__func__, id + overlay_first_id);
|
||||
|
|
@ -1254,7 +1266,7 @@ static int of_unittest_apply_overlay(int overlay_nr, int unittest_nr,
|
|||
int *overlay_id)
|
||||
{
|
||||
struct device_node *np = NULL;
|
||||
int ret, id = -1;
|
||||
int ret;
|
||||
|
||||
np = of_find_node_by_path(overlay_path(overlay_nr));
|
||||
if (np == NULL) {
|
||||
|
|
@ -1264,23 +1276,20 @@ static int of_unittest_apply_overlay(int overlay_nr, int unittest_nr,
|
|||
goto out;
|
||||
}
|
||||
|
||||
ret = of_overlay_create(np);
|
||||
*overlay_id = 0;
|
||||
ret = of_overlay_apply(np, overlay_id);
|
||||
if (ret < 0) {
|
||||
unittest(0, "could not create overlay from \"%s\"\n",
|
||||
overlay_path(overlay_nr));
|
||||
goto out;
|
||||
}
|
||||
id = ret;
|
||||
of_unittest_track_overlay(id);
|
||||
of_unittest_track_overlay(*overlay_id);
|
||||
|
||||
ret = 0;
|
||||
|
||||
out:
|
||||
of_node_put(np);
|
||||
|
||||
if (overlay_id)
|
||||
*overlay_id = id;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -1288,7 +1297,7 @@ out:
|
|||
static int of_unittest_apply_overlay_check(int overlay_nr, int unittest_nr,
|
||||
int before, int after, enum overlay_type ovtype)
|
||||
{
|
||||
int ret;
|
||||
int ret, ovcs_id;
|
||||
|
||||
/* unittest device must not be in before state */
|
||||
if (of_unittest_device_exists(unittest_nr, ovtype) != before) {
|
||||
|
|
@ -1299,7 +1308,8 @@ static int of_unittest_apply_overlay_check(int overlay_nr, int unittest_nr,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = of_unittest_apply_overlay(overlay_nr, unittest_nr, NULL);
|
||||
ovcs_id = 0;
|
||||
ret = of_unittest_apply_overlay(overlay_nr, unittest_nr, &ovcs_id);
|
||||
if (ret != 0) {
|
||||
/* of_unittest_apply_overlay already called unittest() */
|
||||
return ret;
|
||||
|
|
@ -1322,7 +1332,7 @@ static int of_unittest_apply_revert_overlay_check(int overlay_nr,
|
|||
int unittest_nr, int before, int after,
|
||||
enum overlay_type ovtype)
|
||||
{
|
||||
int ret, ov_id;
|
||||
int ret, ovcs_id;
|
||||
|
||||
/* unittest device must be in before state */
|
||||
if (of_unittest_device_exists(unittest_nr, ovtype) != before) {
|
||||
|
|
@ -1334,7 +1344,8 @@ static int of_unittest_apply_revert_overlay_check(int overlay_nr,
|
|||
}
|
||||
|
||||
/* apply the overlay */
|
||||
ret = of_unittest_apply_overlay(overlay_nr, unittest_nr, &ov_id);
|
||||
ovcs_id = 0;
|
||||
ret = of_unittest_apply_overlay(overlay_nr, unittest_nr, &ovcs_id);
|
||||
if (ret != 0) {
|
||||
/* of_unittest_apply_overlay already called unittest() */
|
||||
return ret;
|
||||
|
|
@ -1349,7 +1360,7 @@ static int of_unittest_apply_revert_overlay_check(int overlay_nr,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = of_overlay_destroy(ov_id);
|
||||
ret = of_overlay_remove(&ovcs_id);
|
||||
if (ret != 0) {
|
||||
unittest(0, "overlay @\"%s\" failed to be destroyed @\"%s\"\n",
|
||||
overlay_path(overlay_nr),
|
||||
|
|
@ -1451,7 +1462,7 @@ static void of_unittest_overlay_5(void)
|
|||
static void of_unittest_overlay_6(void)
|
||||
{
|
||||
struct device_node *np;
|
||||
int ret, i, ov_id[2];
|
||||
int ret, i, ov_id[2], ovcs_id;
|
||||
int overlay_nr = 6, unittest_nr = 6;
|
||||
int before = 0, after = 1;
|
||||
|
||||
|
|
@ -1478,13 +1489,14 @@ static void of_unittest_overlay_6(void)
|
|||
return;
|
||||
}
|
||||
|
||||
ret = of_overlay_create(np);
|
||||
ovcs_id = 0;
|
||||
ret = of_overlay_apply(np, &ovcs_id);
|
||||
if (ret < 0) {
|
||||
unittest(0, "could not create overlay from \"%s\"\n",
|
||||
overlay_path(overlay_nr + i));
|
||||
return;
|
||||
}
|
||||
ov_id[i] = ret;
|
||||
ov_id[i] = ovcs_id;
|
||||
of_unittest_track_overlay(ov_id[i]);
|
||||
}
|
||||
|
||||
|
|
@ -1502,7 +1514,8 @@ static void of_unittest_overlay_6(void)
|
|||
}
|
||||
|
||||
for (i = 1; i >= 0; i--) {
|
||||
ret = of_overlay_destroy(ov_id[i]);
|
||||
ovcs_id = ov_id[i];
|
||||
ret = of_overlay_remove(&ovcs_id);
|
||||
if (ret != 0) {
|
||||
unittest(0, "overlay @\"%s\" failed destroy @\"%s\"\n",
|
||||
overlay_path(overlay_nr + i),
|
||||
|
|
@ -1533,7 +1546,7 @@ static void of_unittest_overlay_6(void)
|
|||
static void of_unittest_overlay_8(void)
|
||||
{
|
||||
struct device_node *np;
|
||||
int ret, i, ov_id[2];
|
||||
int ret, i, ov_id[2], ovcs_id;
|
||||
int overlay_nr = 8, unittest_nr = 8;
|
||||
|
||||
/* we don't care about device state in this test */
|
||||
|
|
@ -1548,18 +1561,20 @@ static void of_unittest_overlay_8(void)
|
|||
return;
|
||||
}
|
||||
|
||||
ret = of_overlay_create(np);
|
||||
ovcs_id = 0;
|
||||
ret = of_overlay_apply(np, &ovcs_id);
|
||||
if (ret < 0) {
|
||||
unittest(0, "could not create overlay from \"%s\"\n",
|
||||
overlay_path(overlay_nr + i));
|
||||
return;
|
||||
}
|
||||
ov_id[i] = ret;
|
||||
ov_id[i] = ovcs_id;
|
||||
of_unittest_track_overlay(ov_id[i]);
|
||||
}
|
||||
|
||||
/* now try to remove first overlay (it should fail) */
|
||||
ret = of_overlay_destroy(ov_id[0]);
|
||||
ovcs_id = ov_id[0];
|
||||
ret = of_overlay_remove(&ovcs_id);
|
||||
if (ret == 0) {
|
||||
unittest(0, "overlay @\"%s\" was destroyed @\"%s\"\n",
|
||||
overlay_path(overlay_nr + 0),
|
||||
|
|
@ -1570,7 +1585,8 @@ static void of_unittest_overlay_8(void)
|
|||
|
||||
/* removing them in order should work */
|
||||
for (i = 1; i >= 0; i--) {
|
||||
ret = of_overlay_destroy(ov_id[i]);
|
||||
ovcs_id = ov_id[i];
|
||||
ret = of_overlay_remove(&ovcs_id);
|
||||
if (ret != 0) {
|
||||
unittest(0, "overlay @\"%s\" not destroyed @\"%s\"\n",
|
||||
overlay_path(overlay_nr + i),
|
||||
|
|
@ -2144,21 +2160,13 @@ static int __init overlay_data_add(int onum)
|
|||
ret = 0;
|
||||
goto out_free_data;
|
||||
}
|
||||
of_node_set_flag(info->np_overlay, OF_DETACHED);
|
||||
|
||||
ret = of_resolve_phandles(info->np_overlay);
|
||||
if (ret) {
|
||||
pr_err("resolve ot phandles (ret=%d), %d\n", ret, onum);
|
||||
goto out_free_np_overlay;
|
||||
}
|
||||
|
||||
ret = of_overlay_create(info->np_overlay);
|
||||
info->overlay_id = 0;
|
||||
ret = of_overlay_apply(info->np_overlay, &info->overlay_id);
|
||||
if (ret < 0) {
|
||||
pr_err("of_overlay_create() (ret=%d), %d\n", ret, onum);
|
||||
pr_err("of_overlay_apply() (ret=%d), %d\n", ret, onum);
|
||||
of_overlay_mutex_unlock();
|
||||
goto out_free_np_overlay;
|
||||
} else {
|
||||
info->overlay_id = ret;
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
pr_debug("__dtb_overlay_begin applied, overlay id %d\n", ret);
|
||||
|
|
@ -2207,7 +2215,10 @@ static __init void of_unittest_overlay_high_level(void)
|
|||
* Could not fixup phandles in unittest_unflatten_overlay_base()
|
||||
* because kmalloc() was not yet available.
|
||||
*/
|
||||
of_overlay_mutex_lock();
|
||||
of_resolve_phandles(overlay_base_root);
|
||||
of_overlay_mutex_unlock();
|
||||
|
||||
|
||||
/*
|
||||
* do not allow overlay_base to duplicate any node already in
|
||||
|
|
|
|||
Loading…
Reference in a new issue