diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2023-08-30 16:59:03 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2023-08-30 16:59:03 -0700 |
commit | 8f447694c23a432b2e9cfe67fb2651f8f6655bfd (patch) | |
tree | 044233dffe03ab7136f4166eea1b8783efbfed03 /drivers/of | |
parent | 0e72db77672ff4758a31fb5259c754a7bb229751 (diff) | |
parent | 75cc186739805a5e8abe133be04692b36e7a5257 (diff) | |
download | linux-8f447694c23a432b2e9cfe67fb2651f8f6655bfd.tar.gz linux-8f447694c23a432b2e9cfe67fb2651f8f6655bfd.tar.bz2 linux-8f447694c23a432b2e9cfe67fb2651f8f6655bfd.zip |
Merge tag 'devicetree-for-6.6' of git://git.kernel.org/pub/scm/linux/kernel/git/robh/linux
Pull devicetree updates from Rob Herring:
"DT core:
- Add support for generating DT nodes for PCI devices. This is the
groundwork for applying overlays to PCI devices containing
non-discoverable downstream devices.
- DT unittest additions to check reverted changesets, to test for
refcount issues, and to test unresolved symbols. Also, various
clean-ups of the unittest along the way.
- Refactor node and property manipulation functions to better share
code with old API and changeset API
- Refactor changeset print functions to a common implementation
- Move some platform_device specific functions into of_platform.c
Bindings:
- Treewide fixing of typos
- Treewide clean-up of SPDX tags to use 'OR' consistently
- Last chunk of dropping unnecessary quotes. With that, the check for
unnecessary quotes is enabled in yamllint.
- Convert ftgmac100, zynqmp-genpd, pps-gpio, syna,rmi4, and qcom,ssbi
bindings to DT schema format
- Add Allwinner V3s xHCI USB, Saef SF-TC154B display, QCom SM8450
Inline Crypto Engine, QCom SM6115 UFS, QCom SDM670 PDC interrupt
controller, Arm 2022 Cortex cores, and QCom IPQ9574 Crypto bindings
- Fixes for Rockchip DWC PCI binding
- Ensure all properties are evaluated on USB connector schema
- Fix dt-check-compatible script to find of_device_id instances with
compiler annotations"
* tag 'devicetree-for-6.6' of git://git.kernel.org/pub/scm/linux/kernel/git/robh/linux: (64 commits)
dt-bindings: usb: Add V3s compatible string for OHCI
dt-bindings: usb: Add V3s compatible string for EHCI
dt-bindings: display: panel: mipi-dbi-spi: add Saef SF-TC154B
dt-bindings: vendor-prefixes: document Saef Technology
dt-bindings: thermal: lmh: update maintainer address
of: unittest: Fix of_unittest_pci_node() kconfig dependencies
dt-bindings: crypto: ice: Document sm8450 inline crypto engine
dt-bindings: ufs: qcom: Add ICE to sm8450 example
dt-bindings: ufs: qcom: Add sm6115 binding
dt-bindings: ufs: qcom: Add reg-names property for ICE
dt-bindings: yamllint: Enable quoted string check
dt-bindings: Drop remaining unneeded quotes
of: unittest-data: Fix whitespace - angular brackets
of: unittest-data: Fix whitespace - indentation
of: unittest-data: Fix whitespace - blank lines
of: unittest-data: Convert remaining overlay DTS files to sugar syntax
of: overlay: unittest: Add test for unresolved symbol
of: unittest: Add separators to of_unittest_overlay_high_level()
of: unittest: Cleanup partially-applied overlays
of: unittest: Merge of_unittest_apply{,_revert}_overlay_check()
...
Diffstat (limited to 'drivers/of')
33 files changed, 790 insertions, 366 deletions
diff --git a/drivers/of/base.c b/drivers/of/base.c index 166fb7d75337..8d93cb6ea9cd 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -167,6 +167,7 @@ void __init of_core_init(void) { struct device_node *np; + of_platform_register_reconfig_notifier(); /* Create the kset, and register existing nodes */ mutex_lock(&of_mutex); @@ -1529,6 +1530,20 @@ int of_count_phandle_with_args(const struct device_node *np, const char *list_na } EXPORT_SYMBOL(of_count_phandle_with_args); +static struct property *__of_remove_property_from_list(struct property **list, struct property *prop) +{ + struct property **next; + + for (next = list; *next; next = &(*next)->next) { + if (*next == prop) { + *next = prop->next; + prop->next = NULL; + return prop; + } + } + return NULL; +} + /** * __of_add_property - Add a property to a node without lock operations * @np: Caller's Device Node @@ -1536,19 +1551,32 @@ EXPORT_SYMBOL(of_count_phandle_with_args); */ int __of_add_property(struct device_node *np, struct property *prop) { + int rc = 0; + unsigned long flags; struct property **next; + raw_spin_lock_irqsave(&devtree_lock, flags); + + __of_remove_property_from_list(&np->deadprops, prop); + prop->next = NULL; next = &np->properties; while (*next) { - if (strcmp(prop->name, (*next)->name) == 0) + if (strcmp(prop->name, (*next)->name) == 0) { /* duplicate ! don't insert it */ - return -EEXIST; - + rc = -EEXIST; + goto out_unlock; + } next = &(*next)->next; } *next = prop; +out_unlock: + raw_spin_unlock_irqrestore(&devtree_lock, flags); + if (rc) + return rc; + + __of_add_property_sysfs(np, prop); return 0; } @@ -1559,18 +1587,10 @@ int __of_add_property(struct device_node *np, struct property *prop) */ int of_add_property(struct device_node *np, struct property *prop) { - unsigned long flags; int rc; mutex_lock(&of_mutex); - - raw_spin_lock_irqsave(&devtree_lock, flags); rc = __of_add_property(np, prop); - raw_spin_unlock_irqrestore(&devtree_lock, flags); - - if (!rc) - __of_add_property_sysfs(np, prop); - mutex_unlock(&of_mutex); if (!rc) @@ -1582,20 +1602,23 @@ EXPORT_SYMBOL_GPL(of_add_property); int __of_remove_property(struct device_node *np, struct property *prop) { - struct property **next; + unsigned long flags; + int rc = -ENODEV; - for (next = &np->properties; *next; next = &(*next)->next) { - if (*next == prop) - break; + raw_spin_lock_irqsave(&devtree_lock, flags); + + if (__of_remove_property_from_list(&np->properties, prop)) { + /* Found the property, add it to deadprops list */ + prop->next = np->deadprops; + np->deadprops = prop; + rc = 0; } - if (*next == NULL) - return -ENODEV; - /* found the node */ - *next = prop->next; - prop->next = np->deadprops; - np->deadprops = prop; + raw_spin_unlock_irqrestore(&devtree_lock, flags); + if (rc) + return rc; + __of_remove_property_sysfs(np, prop); return 0; } @@ -1611,21 +1634,13 @@ int __of_remove_property(struct device_node *np, struct property *prop) */ int of_remove_property(struct device_node *np, struct property *prop) { - unsigned long flags; int rc; if (!prop) return -ENODEV; mutex_lock(&of_mutex); - - raw_spin_lock_irqsave(&devtree_lock, flags); rc = __of_remove_property(np, prop); - raw_spin_unlock_irqrestore(&devtree_lock, flags); - - if (!rc) - __of_remove_property_sysfs(np, prop); - mutex_unlock(&of_mutex); if (!rc) @@ -1639,6 +1654,11 @@ int __of_update_property(struct device_node *np, struct property *newprop, struct property **oldpropp) { struct property **next, *oldprop; + unsigned long flags; + + raw_spin_lock_irqsave(&devtree_lock, flags); + + __of_remove_property_from_list(&np->deadprops, newprop); for (next = &np->properties; *next; next = &(*next)->next) { if (of_prop_cmp((*next)->name, newprop->name) == 0) @@ -1658,6 +1678,10 @@ int __of_update_property(struct device_node *np, struct property *newprop, *next = newprop; } + raw_spin_unlock_irqrestore(&devtree_lock, flags); + + __of_update_property_sysfs(np, newprop, oldprop); + return 0; } @@ -1673,21 +1697,13 @@ int __of_update_property(struct device_node *np, struct property *newprop, int of_update_property(struct device_node *np, struct property *newprop) { struct property *oldprop; - unsigned long flags; int rc; if (!newprop->name) return -EINVAL; mutex_lock(&of_mutex); - - raw_spin_lock_irqsave(&devtree_lock, flags); rc = __of_update_property(np, newprop, &oldprop); - raw_spin_unlock_irqrestore(&devtree_lock, flags); - - if (!rc) - __of_update_property_sysfs(np, newprop, oldprop); - mutex_unlock(&of_mutex); if (!rc) diff --git a/drivers/of/device.c b/drivers/of/device.c index 90131de6d75b..1ca42ad9dd15 100644 --- a/drivers/of/device.c +++ b/drivers/of/device.c @@ -32,25 +32,6 @@ const struct of_device_id *of_match_device(const struct of_device_id *matches, } EXPORT_SYMBOL(of_match_device); -int of_device_add(struct platform_device *ofdev) -{ - BUG_ON(ofdev->dev.of_node == NULL); - - /* name and id have to be set so that the platform bus doesn't get - * confused on matching */ - ofdev->name = dev_name(&ofdev->dev); - ofdev->id = PLATFORM_DEVID_NONE; - - /* - * If this device has not binding numa node in devicetree, that is - * of_node_to_nid returns NUMA_NO_NODE. device_add will assume that this - * device is on the same node as the parent. - */ - set_dev_node(&ofdev->dev, of_node_to_nid(ofdev->dev.of_node)); - - return device_add(&ofdev->dev); -} - static void of_dma_set_restricted_buffer(struct device *dev, struct device_node *np) { @@ -221,19 +202,6 @@ int of_dma_configure_id(struct device *dev, struct device_node *np, } EXPORT_SYMBOL_GPL(of_dma_configure_id); -int of_device_register(struct platform_device *pdev) -{ - device_initialize(&pdev->dev); - return of_device_add(pdev); -} -EXPORT_SYMBOL(of_device_register); - -void of_device_unregister(struct platform_device *ofdev) -{ - device_unregister(&ofdev->dev); -} -EXPORT_SYMBOL(of_device_unregister); - const void *of_device_get_match_data(const struct device *dev) { const struct of_device_id *match; diff --git a/drivers/of/dynamic.c b/drivers/of/dynamic.c index 4999636eaa92..0a3483e247a8 100644 --- a/drivers/of/dynamic.c +++ b/drivers/of/dynamic.c @@ -72,27 +72,21 @@ static const char *action_names[] = { [OF_RECONFIG_UPDATE_PROPERTY] = "UPDATE_PROPERTY", }; +#define _do_print(func, prefix, action, node, prop, ...) ({ \ + func("changeset: " prefix "%-15s %pOF%s%s\n", \ + ##__VA_ARGS__, action_names[action], node, \ + prop ? ":" : "", prop ? prop->name : ""); \ +}) +#define of_changeset_action_err(...) _do_print(pr_err, __VA_ARGS__) +#define of_changeset_action_debug(...) _do_print(pr_debug, __VA_ARGS__) + int of_reconfig_notify(unsigned long action, struct of_reconfig_data *p) { int rc; -#ifdef DEBUG struct of_reconfig_data *pr = p; - switch (action) { - case OF_RECONFIG_ATTACH_NODE: - case OF_RECONFIG_DETACH_NODE: - pr_debug("notify %-15s %pOF\n", action_names[action], - pr->dn); - break; - case OF_RECONFIG_ADD_PROPERTY: - case OF_RECONFIG_REMOVE_PROPERTY: - case OF_RECONFIG_UPDATE_PROPERTY: - pr_debug("notify %-15s %pOF:%s\n", action_names[action], - pr->dn, pr->prop->name); - break; + of_changeset_action_debug("notify: ", action, pr->dn, pr->prop); - } -#endif rc = blocking_notifier_call_chain(&of_reconfig_chain, action, p); return notifier_to_errno(rc); } @@ -204,6 +198,9 @@ static void __of_attach_node(struct device_node *np) { const __be32 *phandle; int sz; + unsigned long flags; + + raw_spin_lock_irqsave(&devtree_lock, flags); if (!of_node_check_flag(np, OF_OVERLAY)) { np->name = __of_get_property(np, "name", NULL); @@ -226,6 +223,10 @@ static void __of_attach_node(struct device_node *np) np->parent->child = np; of_node_clear_flag(np, OF_DETACHED); np->fwnode.flags |= FWNODE_FLAG_NOT_DEVICE; + + raw_spin_unlock_irqrestore(&devtree_lock, flags); + + __of_attach_node_sysfs(np); } /** @@ -235,17 +236,12 @@ static void __of_attach_node(struct device_node *np) int of_attach_node(struct device_node *np) { struct of_reconfig_data rd; - unsigned long flags; memset(&rd, 0, sizeof(rd)); rd.dn = np; mutex_lock(&of_mutex); - raw_spin_lock_irqsave(&devtree_lock, flags); __of_attach_node(np); - raw_spin_unlock_irqrestore(&devtree_lock, flags); - - __of_attach_node_sysfs(np); mutex_unlock(&of_mutex); of_reconfig_notify(OF_RECONFIG_ATTACH_NODE, &rd); @@ -256,13 +252,15 @@ int of_attach_node(struct device_node *np) void __of_detach_node(struct device_node *np) { struct device_node *parent; + unsigned long flags; - if (WARN_ON(of_node_check_flag(np, OF_DETACHED))) - return; + raw_spin_lock_irqsave(&devtree_lock, flags); parent = np->parent; - if (WARN_ON(!parent)) + if (WARN_ON(of_node_check_flag(np, OF_DETACHED) || !parent)) { + raw_spin_unlock_irqrestore(&devtree_lock, flags); return; + } if (parent->child == np) parent->child = np->sibling; @@ -279,6 +277,10 @@ void __of_detach_node(struct device_node *np) /* race with of_find_node_by_phandle() prevented by devtree_lock */ __of_phandle_cache_inv_entry(np->phandle); + + raw_spin_unlock_irqrestore(&devtree_lock, flags); + + __of_detach_node_sysfs(np); } /** @@ -288,17 +290,12 @@ void __of_detach_node(struct device_node *np) int of_detach_node(struct device_node *np) { struct of_reconfig_data rd; - unsigned long flags; memset(&rd, 0, sizeof(rd)); rd.dn = np; mutex_lock(&of_mutex); - raw_spin_lock_irqsave(&devtree_lock, flags); __of_detach_node(np); - raw_spin_unlock_irqrestore(&devtree_lock, flags); - - __of_detach_node_sysfs(np); mutex_unlock(&of_mutex); of_reconfig_notify(OF_RECONFIG_DETACH_NODE, &rd); @@ -486,6 +483,38 @@ struct device_node *__of_node_dup(const struct device_node *np, return NULL; } +/** + * of_changeset_create_node - Dynamically create a device node and attach to + * a given changeset. + * + * @ocs: Pointer to changeset + * @parent: Pointer to parent device node + * @full_name: Node full name + * + * Return: Pointer to the created device node or NULL in case of an error. + */ +struct device_node *of_changeset_create_node(struct of_changeset *ocs, + struct device_node *parent, + const char *full_name) +{ + struct device_node *np; + int ret; + + np = __of_node_dup(NULL, full_name); + if (!np) + return NULL; + np->parent = parent; + + ret = of_changeset_attach_node(ocs, np); + if (ret) { + of_node_put(np); + return NULL; + } + + return np; +} +EXPORT_SYMBOL(of_changeset_create_node); + static void __of_changeset_entry_destroy(struct of_changeset_entry *ce) { if (ce->action == OF_RECONFIG_ATTACH_NODE && @@ -503,30 +532,6 @@ static void __of_changeset_entry_destroy(struct of_changeset_entry *ce) kfree(ce); } -#ifdef DEBUG -static void __of_changeset_entry_dump(struct of_changeset_entry *ce) -{ - switch (ce->action) { - case OF_RECONFIG_ADD_PROPERTY: - case OF_RECONFIG_REMOVE_PROPERTY: - case OF_RECONFIG_UPDATE_PROPERTY: - pr_debug("cset<%p> %-15s %pOF/%s\n", ce, action_names[ce->action], - ce->np, ce->prop->name); - break; - case OF_RECONFIG_ATTACH_NODE: - case OF_RECONFIG_DETACH_NODE: - pr_debug("cset<%p> %-15s %pOF\n", ce, action_names[ce->action], - ce->np); - break; - } -} -#else -static inline void __of_changeset_entry_dump(struct of_changeset_entry *ce) -{ - /* empty */ -} -#endif - static void __of_changeset_entry_invert(struct of_changeset_entry *ce, struct of_changeset_entry *rce) { @@ -594,13 +599,10 @@ static int __of_changeset_entry_notify(struct of_changeset_entry *ce, static int __of_changeset_entry_apply(struct of_changeset_entry *ce) { - struct property *old_prop, **propp; - unsigned long flags; int ret = 0; - __of_changeset_entry_dump(ce); + of_changeset_action_debug("apply: ", ce->action, ce->np, ce->prop); - raw_spin_lock_irqsave(&devtree_lock, flags); switch (ce->action) { case OF_RECONFIG_ATTACH_NODE: __of_attach_node(ce->np); @@ -609,15 +611,6 @@ static int __of_changeset_entry_apply(struct of_changeset_entry *ce) __of_detach_node(ce->np); break; case OF_RECONFIG_ADD_PROPERTY: - /* If the property is in deadprops then it must be removed */ - for (propp = &ce->np->deadprops; *propp; propp = &(*propp)->next) { - if (*propp == ce->prop) { - *propp = ce->prop->next; - ce->prop->next = NULL; - break; - } - } - ret = __of_add_property(ce->np, ce->prop); break; case OF_RECONFIG_REMOVE_PROPERTY: @@ -625,47 +618,17 @@ static int __of_changeset_entry_apply(struct of_changeset_entry *ce) break; case OF_RECONFIG_UPDATE_PROPERTY: - /* If the property is in deadprops then it must be removed */ - for (propp = &ce->np->deadprops; *propp; propp = &(*propp)->next) { - if (*propp == ce->prop) { - *propp = ce->prop->next; - ce->prop->next = NULL; - break; - } - } - - ret = __of_update_property(ce->np, ce->prop, &old_prop); + ret = __of_update_property(ce->np, ce->prop, &ce->old_prop); break; default: ret = -EINVAL; } - raw_spin_unlock_irqrestore(&devtree_lock, flags); if (ret) { - pr_err("changeset: apply failed: %-15s %pOF:%s\n", - action_names[ce->action], ce->np, ce->prop->name); + of_changeset_action_err("apply failed: ", ce->action, ce->np, ce->prop); return ret; } - switch (ce->action) { - case OF_RECONFIG_ATTACH_NODE: - __of_attach_node_sysfs(ce->np); - break; - case OF_RECONFIG_DETACH_NODE: - __of_detach_node_sysfs(ce->np); - break; - case OF_RECONFIG_ADD_PROPERTY: - /* ignore duplicate names */ - __of_add_property_sysfs(ce->np, ce->prop); - break; - case OF_RECONFIG_REMOVE_PROPERTY: - __of_remove_property_sysfs(ce->np, ce->prop); - break; - case OF_RECONFIG_UPDATE_PROPERTY: - __of_update_property_sysfs(ce->np, ce->prop, ce->old_prop); - break; - } - return 0; } @@ -939,11 +902,140 @@ int of_changeset_action(struct of_changeset *ocs, unsigned long action, ce->np = of_node_get(np); ce->prop = prop; - if (action == OF_RECONFIG_UPDATE_PROPERTY && prop) - ce->old_prop = of_find_property(np, prop->name, NULL); - /* add it to the list */ list_add_tail(&ce->node, &ocs->entries); return 0; } EXPORT_SYMBOL_GPL(of_changeset_action); + +static int of_changeset_add_prop_helper(struct of_changeset *ocs, + struct device_node *np, + const struct property *pp) +{ + struct property *new_pp; + int ret; + + new_pp = __of_prop_dup(pp, GFP_KERNEL); + if (!new_pp) + return -ENOMEM; + + ret = of_changeset_add_property(ocs, np, new_pp); + if (ret) { + kfree(new_pp->name); + kfree(new_pp->value); + kfree(new_pp); + } + + return ret; +} + +/** + * of_changeset_add_prop_string - Add a string property to a changeset + * + * @ocs: changeset pointer + * @np: device node pointer + * @prop_name: name of the property to be added + * @str: pointer to null terminated string + * + * Create a string property and add it to a changeset. + * + * Return: 0 on success, a negative error value in case of an error. + */ +int of_changeset_add_prop_string(struct of_changeset *ocs, + struct device_node *np, + const char *prop_name, const char *str) +{ + struct property prop; + + prop.name = (char *)prop_name; + prop.length = strlen(str) + 1; + prop.value = (void *)str; + + return of_changeset_add_prop_helper(ocs, np, &prop); +} +EXPORT_SYMBOL_GPL(of_changeset_add_prop_string); + +/** + * of_changeset_add_prop_string_array - Add a string list property to + * a changeset + * + * @ocs: changeset pointer + * @np: device node pointer + * @prop_name: name of the property to be added + * @str_array: pointer to an array of null terminated strings + * @sz: number of string array elements + * + * Create a string list property and add it to a changeset. + * + * Return: 0 on success, a negative error value in case of an error. + */ +int of_changeset_add_prop_string_array(struct of_changeset *ocs, + struct device_node *np, + const char *prop_name, + const char **str_array, size_t sz) +{ + struct property prop; + int i, ret; + char *vp; + + prop.name = (char *)prop_name; + + prop.length = 0; + for (i = 0; i < sz; i++) + prop.length += strlen(str_array[i]) + 1; + + prop.value = kmalloc(prop.length, GFP_KERNEL); + if (!prop.value) + return -ENOMEM; + + vp = prop.value; + for (i = 0; i < sz; i++) { + vp += snprintf(vp, (char *)prop.value + prop.length - vp, "%s", + str_array[i]) + 1; + } + ret = of_changeset_add_prop_helper(ocs, np, &prop); + kfree(prop.value); + + return ret; +} +EXPORT_SYMBOL_GPL(of_changeset_add_prop_string_array); + +/** + * of_changeset_add_prop_u32_array - Add a property of 32 bit integers + * property to a changeset + * + * @ocs: changeset pointer + * @np: device node pointer + * @prop_name: name of the property to be added + * @array: pointer to an array of 32 bit integers + * @sz: number of array elements + * + * Create a property of 32 bit integers and add it to a changeset. + * + * Return: 0 on success, a negative error value in case of an error. + */ +int of_changeset_add_prop_u32_array(struct of_changeset *ocs, + struct device_node *np, + const char *prop_name, + const u32 *array, size_t sz) +{ + struct property prop; + __be32 *val; + int i, ret; + + val = kcalloc(sz, sizeof(__be32), GFP_KERNEL); + if (!val) + return -ENOMEM; + + for (i = 0; i < sz; i++) + val[i] = cpu_to_be32(array[i]); + prop.name = (char *)prop_name; + prop.length = sizeof(u32) * sz; + prop.value = (void *)val; + + ret = of_changeset_add_prop_helper(ocs, np, &prop); + kfree(val); + + return ret; +} +EXPORT_SYMBOL_GPL(of_changeset_add_prop_u32_array); diff --git a/drivers/of/of_private.h b/drivers/of/of_private.h index b57f1014e419..f38397c7b582 100644 --- a/drivers/of/of_private.h +++ b/drivers/of/of_private.h @@ -60,6 +60,12 @@ static inline int of_property_notify(int action, struct device_node *np, } #endif /* CONFIG_OF_DYNAMIC */ +#if defined(CONFIG_OF_DYNAMIC) && defined(CONFIG_OF_ADDRESS) +void of_platform_register_reconfig_notifier(void); +#else +static inline void of_platform_register_reconfig_notifier(void) { } +#endif + #if defined(CONFIG_OF_KOBJ) int of_node_is_attached(const struct device_node *node); int __of_add_property_sysfs(struct device_node *np, struct property *pp); diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c index 7feb643f1370..dfb6fb962fc7 100644 --- a/drivers/of/overlay.c +++ b/drivers/of/overlay.c @@ -682,9 +682,11 @@ static int build_changeset(struct overlay_changeset *ovcs) * 1) "target" property containing the phandle of the target * 2) "target-path" property containing the path of the target */ -static struct device_node *find_target(struct device_node *info_node) +static struct device_node *find_target(struct device_node *info_node, + struct device_node *target_base) { struct device_node *node; + char *target_path; const char *path; u32 val; int ret; @@ -700,10 +702,23 @@ static struct device_node *find_target(struct device_node *info_node) ret = of_property_read_string(info_node, "target-path", &path); if (!ret) { - node = of_find_node_by_path(path); - if (!node) - pr_err("find target, node: %pOF, path '%s' not found\n", - info_node, path); + if (target_base) { + target_path = kasprintf(GFP_KERNEL, "%pOF%s", target_base, path); + if (!target_path) + return NULL; + node = of_find_node_by_path(target_path); + if (!node) { + pr_err("find target, node: %pOF, path '%s' not found\n", + info_node, target_path); + } + kfree(target_path); + } else { + node = of_find_node_by_path(path); + if (!node) { + pr_err("find target, node: %pOF, path '%s' not found\n", + info_node, path); + } + } return node; } @@ -715,6 +730,7 @@ static struct device_node *find_target(struct device_node *info_node) /** * init_overlay_changeset() - initialize overlay changeset from overlay tree * @ovcs: Overlay changeset to build + * @target_base: Point to the target node to apply overlay * * Initialize @ovcs. Populate @ovcs->fragments with node information from * the top level of @overlay_root. The relevant top level nodes are the @@ -725,7 +741,8 @@ static struct device_node *find_target(struct device_node *info_node) * detected in @overlay_root. On error return, the caller of * init_overlay_changeset() must call free_overlay_changeset(). */ -static int init_overlay_changeset(struct overlay_changeset *ovcs) +static int init_overlay_changeset(struct overlay_changeset *ovcs, + struct device_node *target_base) { struct device_node *node, *overlay_node; struct fragment *fragment; @@ -752,8 +769,6 @@ static int init_overlay_changeset(struct overlay_changeset *ovcs) if (!of_node_is_root(ovcs->overlay_root)) pr_debug("%s() ovcs->overlay_root is not root\n", __func__); - of_changeset_init(&ovcs->cset); - cnt = 0; /* fragment nodes */ @@ -786,7 +801,7 @@ static int init_overlay_changeset(struct overlay_changeset *ovcs) fragment = &fragments[cnt]; fragment->overlay = overlay_node; - fragment->target = find_target(node); + fragment->target = find_target(node, target_base); if (!fragment->target) { of_node_put(fragment->overlay); ret = -EINVAL; @@ -877,6 +892,7 @@ static void free_overlay_changeset(struct overlay_changeset *ovcs) * * of_overlay_apply() - Create and apply an overlay changeset * @ovcs: overlay changeset + * @base: point to the target node to apply overlay * * Creates and applies an overlay changeset. * @@ -900,7 +916,8 @@ static void free_overlay_changeset(struct overlay_changeset *ovcs) * the caller of of_overlay_apply() must call free_overlay_changeset(). */ -static int of_overlay_apply(struct overlay_changeset *ovcs) +static int of_overlay_apply(struct overlay_changeset *ovcs, + struct device_node *base) { int ret = 0, ret_revert, ret_tmp; @@ -908,7 +925,7 @@ static int of_overlay_apply(struct overlay_changeset *ovcs) if (ret) goto out; - ret = init_overlay_changeset(ovcs); + ret = init_overlay_changeset(ovcs, base); if (ret) goto out; @@ -952,6 +969,7 @@ out: * @overlay_fdt: pointer to overlay FDT * @overlay_fdt_size: number of bytes in @overlay_fdt * @ret_ovcs_id: pointer for returning created changeset id + * @base: pointer for the target node to apply overlay * * Creates and applies an overlay changeset. * @@ -967,7 +985,7 @@ out: */ int of_overlay_fdt_apply(const void *overlay_fdt, u32 overlay_fdt_size, - int *ret_ovcs_id) + int *ret_ovcs_id, struct device_node *base) { void *new_fdt; void *new_fdt_align; @@ -1013,6 +1031,7 @@ int of_overlay_fdt_apply(const void *overlay_fdt, u32 overlay_fdt_size, INIT_LIST_HEAD(&ovcs->ovcs_list); list_add_tail(&ovcs->ovcs_list, &ovcs_list); + of_changeset_init(&ovcs->cset); /* * Must create permanent copy of FDT because of_fdt_unflatten_tree() @@ -1037,7 +1056,7 @@ int of_overlay_fdt_apply(const void *overlay_fdt, u32 overlay_fdt_size, } ovcs->overlay_mem = overlay_mem; - ret = of_overlay_apply(ovcs); + ret = of_overlay_apply(ovcs, base); /* * If of_overlay_apply() error, calling free_overlay_changeset() may * result in a memory leak if the apply partly succeeded, so do NOT diff --git a/drivers/of/platform.c b/drivers/of/platform.c index 6a557eb866d0..f235ab55b91e 100644 --- a/drivers/of/platform.c +++ b/drivers/of/platform.c @@ -21,6 +21,8 @@ #include <linux/of_platform.h> #include <linux/platform_device.h> +#include "of_private.h" + const struct of_device_id of_default_bus_match_table[] = { { .compatible = "simple-bus", }, { .compatible = "simple-mfd", }, @@ -31,11 +33,6 @@ const struct of_device_id of_default_bus_match_table[] = { {} /* Empty terminated list */ }; -static const struct of_device_id of_skipped_node_table[] = { - { .compatible = "operating-points-v2", }, - {} /* Empty terminated list */ -}; - /** * of_find_device_by_node - Find the platform_device associated with a node * @np: Pointer to device tree node @@ -54,7 +51,44 @@ struct platform_device *of_find_device_by_node(struct device_node *np) } EXPORT_SYMBOL(of_find_device_by_node); +int of_device_add(struct platform_device *ofdev) +{ + BUG_ON(ofdev->dev.of_node == NULL); + + /* name and id have to be set so that the platform bus doesn't get + * confused on matching */ + ofdev->name = dev_name(&ofdev->dev); + ofdev->id = PLATFORM_DEVID_NONE; + + /* + * If this device has not binding numa node in devicetree, that is + * of_node_to_nid returns NUMA_NO_NODE. device_add will assume that this + * device is on the same node as the parent. + */ + set_dev_node(&ofdev->dev, of_node_to_nid(ofdev->dev.of_node)); + + return device_add(&ofdev->dev); +} + +int of_device_register(struct platform_device *pdev) +{ + device_initialize(&pdev->dev); + return of_device_add(pdev); +} +EXPORT_SYMBOL(of_device_register); + +void of_device_unregister(struct platform_device *ofdev) +{ + device_unregister(&ofdev->dev); +} +EXPORT_SYMBOL(of_device_unregister); + #ifdef CONFIG_OF_ADDRESS +static const struct of_device_id of_skipped_node_table[] = { + { .compatible = "operating-points-v2", }, + {} /* Empty terminated list */ +}; + /* * The following routines scan a subtree and registers a device for * each applicable node. diff --git a/drivers/of/unittest-data/Makefile b/drivers/of/unittest-data/Makefile index ea5f4da68e23..01a966e39f23 100644 --- a/drivers/of/unittest-data/Makefile +++ b/drivers/of/unittest-data/Makefile @@ -32,7 +32,9 @@ obj-$(CONFIG_OF_OVERLAY) += overlay.dtbo.o \ overlay_gpio_02b.dtbo.o \ overlay_gpio_03.dtbo.o \ overlay_gpio_04a.dtbo.o \ - overlay_gpio_04b.dtbo.o + overlay_gpio_04b.dtbo.o \ + overlay_pci_node.dtbo.o \ + overlay_bad_unresolved.dtbo.o # enable creation of __symbols__ node DTC_FLAGS_overlay += -@ diff --git a/drivers/of/unittest-data/overlay.dtso b/drivers/of/unittest-data/overlay.dtso index 3bbc59e922fe..b3e807b99852 100644 --- a/drivers/of/unittest-data/overlay.dtso +++ b/drivers/of/unittest-data/overlay.dtso @@ -3,13 +3,12 @@ /plugin/; &electric_1 { - status = "okay"; hvac_2: hvac-large-1 { compatible = "ot,hvac-large"; - heat-range = < 40 75 >; - cool-range = < 65 80 >; + heat-range = <40 75>; + cool-range = <65 80>; }; }; @@ -24,11 +23,11 @@ #size-cells = <1>; track@30 { - incline-up = < 48 32 16 >; + incline-up = <48 32 16>; }; track@40 { - incline-up = < 47 31 15 >; + incline-up = <47 31 15>; }; }; @@ -36,29 +35,28 @@ #address-cells = <1>; #size-cells = <1>; compatible = "ot,ferris-wheel"; - reg = < 0x00000200 0x100 >; - hvac-provider = < &hvac_2 >; - hvac-thermostat = < 27 32 > ; - hvac-zones = < 12 5 >; + reg = <0x00000200 0x100>; + hvac-provider = <&hvac_2>; + hvac-thermostat = <27 32> ; + hvac-zones = <12 5>; hvac-zone-names = "operator", "snack-bar"; - spin-controller = < &spin_ctrl_1 3 >; - spin-rph = < 30 >; - gondolas = < 16 >; - gondola-capacity = < 6 >; + spin-controller = <&spin_ctrl_1 3>; + spin-rph = <30>; + gondolas = <16>; + gondola-capacity = <6>; ride_200_left: track@10 { - reg = < 0x00000010 0x10 >; + reg = <0x00000010 0x10>; }; ride_200_right: track@20 { - reg = < 0x00000020 0x10 >; + reg = <0x00000020 0x10>; }; }; }; &lights_2 { - status = "okay"; color = "purple", "white", "red", "green"; - rate = < 3 256 >; + rate = <3 256>; }; diff --git a/drivers/of/unittest-data/overlay_0.dtso b/drivers/of/unittest-data/overlay_0.dtso index ac0f9e0fe65f..bb46582e0485 100644 --- a/drivers/of/unittest-data/overlay_0.dtso +++ b/drivers/of/unittest-data/overlay_0.dtso @@ -2,13 +2,8 @@ /dts-v1/; /plugin/; -/ { - /* overlay_0 - enable using absolute target path */ +/* overlay_0 - enable using absolute target path */ - fragment@0 { - target-path = "/testcase-data/overlay-node/test-bus/test-unittest0"; - __overlay__ { - status = "okay"; - }; - }; +&{/testcase-data/overlay-node/test-bus/test-unittest0} { + status = "okay"; }; diff --git a/drivers/of/unittest-data/overlay_1.dtso b/drivers/of/unittest-data/overlay_1.dtso index e92a626e2948..9c0fc8ffa4a1 100644 --- a/drivers/of/unittest-data/overlay_1.dtso +++ b/drivers/of/unittest-data/overlay_1.dtso @@ -2,13 +2,8 @@ /dts-v1/; /plugin/; -/ { - /* overlay_1 - disable using absolute target path */ +/* overlay_1 - disable using absolute target path */ - fragment@0 { - target-path = "/testcase-data/overlay-node/test-bus/test-unittest1"; - __overlay__ { - status = "disabled"; - }; - }; +&{/testcase-data/overlay-node/test-bus/test-unittest1} { + status = "disabled"; }; diff --git a/drivers/of/unittest-data/overlay_11.dtso b/drivers/of/unittest-data/overlay_11.dtso index 9a79b253a809..7d04ff503a18 100644 --- a/drivers/of/unittest-data/overlay_11.dtso +++ b/drivers/of/unittest-data/overlay_11.dtso @@ -23,6 +23,5 @@ status = "okay"; reg = <1>; }; - }; }; diff --git a/drivers/of/unittest-data/overlay_12.dtso b/drivers/of/unittest-data/overlay_12.dtso index ca3441e2cbec..8d5087793eb4 100644 --- a/drivers/of/unittest-data/overlay_12.dtso +++ b/drivers/of/unittest-data/overlay_12.dtso @@ -2,13 +2,8 @@ /dts-v1/; /plugin/; -/ { - /* overlay_12 - enable using absolute target path (i2c) */ +/* overlay_12 - enable using absolute target path (i2c) */ - fragment@0 { - target-path = "/testcase-data/overlay-node/test-bus/i2c-test-bus/test-unittest12"; - __overlay__ { - status = "okay"; - }; - }; +&{/testcase-data/overlay-node/test-bus/i2c-test-bus/test-unittest12} { + status = "okay"; }; diff --git a/drivers/of/unittest-data/overlay_13.dtso b/drivers/of/unittest-data/overlay_13.dtso index 3c30dec63894..da200ae94f45 100644 --- a/drivers/of/unittest-data/overlay_13.dtso +++ b/drivers/of/unittest-data/overlay_13.dtso @@ -2,13 +2,8 @@ /dts-v1/; /plugin/; -/ { - /* overlay_13 - disable using absolute target path (i2c) */ +/* overlay_13 - disable using absolute target path (i2c) */ - fragment@0 { - target-path = "/testcase-data/overlay-node/test-bus/i2c-test-bus/test-unittest13"; - __overlay__ { - status = "disabled"; - }; - }; +&{/testcase-data/overlay-node/test-bus/i2c-test-bus/test-unittest13} { + status = "disabled"; }; diff --git a/drivers/of/unittest-data/overlay_15.dtso b/drivers/of/unittest-data/overlay_15.dtso index 5728490474f6..ba02ae1fed38 100644 --- a/drivers/of/unittest-data/overlay_15.dtso +++ b/drivers/of/unittest-data/overlay_15.dtso @@ -7,6 +7,7 @@ &unittest_i2c_test_bus { #address-cells = <1>; #size-cells = <0>; + test-unittest15 { reg = <11>; compatible = "unittest-i2c-mux"; diff --git a/drivers/of/unittest-data/overlay_4.dtso b/drivers/of/unittest-data/overlay_4.dtso index a8a77ddf9abe..9b9eadddb4a0 100644 --- a/drivers/of/unittest-data/overlay_4.dtso +++ b/drivers/of/unittest-data/overlay_4.dtso @@ -5,7 +5,6 @@ /* overlay_4 - test insertion of a full node */ &unittest_test_bus { - /* suppress DTC warning */ #address-cells = <1>; #size-cells = <0>; diff --git a/drivers/of/unittest-data/overlay_bad_add_dup_node.dtso b/drivers/of/unittest-data/overlay_bad_add_dup_node.dtso index 145dfc3b1024..9b53412b2079 100644 --- a/drivers/of/unittest-data/overlay_bad_add_dup_node.dtso +++ b/drivers/of/unittest-data/overlay_bad_add_dup_node.dtso @@ -13,16 +13,15 @@ */ &electric_1 { - motor-1 { controller { - power_bus = < 0x1 0x2 >; + power_bus = <0x1 0x2>; }; }; }; &spin_ctrl_1 { - controller { - power_bus_emergency = < 0x101 0x102 >; - }; + controller { + power_bus_emergency = <0x101 0x102>; + }; }; diff --git a/drivers/of/unittest-data/overlay_bad_add_dup_prop.dtso b/drivers/of/unittest-data/overlay_bad_add_dup_prop.dtso index 6327d1ffb963..e03f791655b0 100644 --- a/drivers/of/unittest-data/overlay_bad_add_dup_prop.dtso +++ b/drivers/of/unittest-data/overlay_bad_add_dup_prop.dtso @@ -24,16 +24,15 @@ */ &electric_1 { - motor-1 { electric { - rpm_avail = < 100 >; + rpm_avail = <100>; }; }; }; &spin_ctrl_1 { - electric { - rpm_avail = < 100 200 >; - }; + electric { + rpm_avail = <100 200>; + }; }; diff --git a/drivers/of/unittest-data/overlay_bad_phandle.dtso b/drivers/of/unittest-data/overlay_bad_phandle.dtso index 83b797360318..a61ffc0738e3 100644 --- a/drivers/of/unittest-data/overlay_bad_phandle.dtso +++ b/drivers/of/unittest-data/overlay_bad_phandle.dtso @@ -3,12 +3,11 @@ /plugin/; &electric_1 { - // This label should cause an error when the overlay // is applied. There is already a phandle value // in the base tree for motor-1. spin_ctrl_1_conflict: motor-1 { - accelerate = < 3 >; - decelerate = < 5 >; + accelerate = <3>; + decelerate = <5>; }; }; diff --git a/drivers/of/unittest-data/overlay_bad_symbol.dtso b/drivers/of/unittest-data/overlay_bad_symbol.dtso index 98c6d1de144a..07f730384cdd 100644 --- a/drivers/of/unittest-data/overlay_bad_symbol.dtso +++ b/drivers/of/unittest-data/overlay_bad_symbol.dtso @@ -3,14 +3,13 @@ /plugin/; &electric_1 { - // This label should cause an error when the overlay // is applied. There is already a symbol hvac_1 // in the base tree hvac_1: hvac-medium-2 { compatible = "ot,hvac-medium"; - heat-range = < 50 75 >; - cool-range = < 60 80 >; + heat-range = <50 75>; + cool-range = <60 80>; }; }; diff --git a/drivers/of/unittest-data/overlay_bad_unresolved.dtso b/drivers/of/unittest-data/overlay_bad_unresolved.dtso new file mode 100644 index 000000000000..3b75a53ae8a4 --- /dev/null +++ b/drivers/of/unittest-data/overlay_bad_unresolved.dtso @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0 +/dts-v1/; +/plugin/; + +&this_label_does_not_exist { + status = "ok"; +}; diff --git a/drivers/of/unittest-data/overlay_common.dtsi b/drivers/of/unittest-data/overlay_common.dtsi index 08874a72556e..a9d7cdbd5ddc 100644 --- a/drivers/of/unittest-data/overlay_common.dtsi +++ b/drivers/of/unittest-data/overlay_common.dtsi @@ -16,19 +16,19 @@ electric_1: substation@100 { compatible = "ot,big-volts-control"; - reg = < 0x00000100 0x100 >; + reg = <0x00000100 0x100>; status = "disabled"; hvac_1: hvac-medium-1 { compatible = "ot,hvac-medium"; - heat-range = < 50 75 >; - cool-range = < 60 80 >; + heat-range = <50 75>; + cool-range = <60 80>; }; spin_ctrl_1: motor-1 { compatible = "ot,ferris-wheel-motor"; spin = "clockwise"; - rpm_avail = < 50 >; + rpm_avail = <50>; }; spin_ctrl_2: motor-8 { @@ -41,27 +41,27 @@ #size-cells = <1>; compatible = "ot,rides"; status = "disabled"; - orientation = < 127 >; + orientation = <127>; ride@100 { #address-cells = <1>; #size-cells = <1>; compatible = "ot,roller-coaster"; - reg = < 0x00000100 0x100 >; - hvac-provider = < &hvac_1 >; - hvac-thermostat = < 29 > ; - hvac-zones = < 14 >; + reg = <0x00000100 0x100>; + hvac-provider = <&hvac_1>; + hvac-thermostat = <29> ; + hvac-zones = <14>; hvac-zone-names = "operator"; - spin-controller = < &spin_ctrl_2 5 &spin_ctrl_2 7 >; + spin-controller = <&spin_ctrl_2 5 &spin_ctrl_2 7>; spin-controller-names = "track_1", "track_2"; - queues = < 2 >; + queues = <2>; track@30 { - reg = < 0x00000030 0x10 >; + reg = <0x00000030 0x10>; }; track@40 { - reg = < 0x00000040 0x10 >; + reg = <0x00000040 0x10>; }; }; @@ -69,23 +69,21 @@ lights_1: lights@30000 { compatible = "ot,work-lights"; - reg = < 0x00030000 0x1000 >; + reg = <0x00030000 0x1000>; status = "disabled"; }; lights_2: lights@40000 { compatible = "ot,show-lights"; - reg = < 0x00040000 0x1000 >; + reg = <0x00040000 0x1000>; status = "disabled"; - rate = < 13 138 >; + rate = <13 138>; }; retail_1: vending@50000 { - reg = < 0x00050000 0x1000 >; + reg = <0x00050000 0x1000>; compatible = "ot,tickets"; status = "disabled"; }; - }; }; - diff --git a/drivers/of/unittest-data/overlay_gpio_01.dtso b/drivers/of/unittest-data/overlay_gpio_01.dtso index 699ff104ae10..bb3a31a2137a 100644 --- a/drivers/of/unittest-data/overlay_gpio_01.dtso +++ b/drivers/of/unittest-data/overlay_gpio_01.dtso @@ -5,6 +5,7 @@ &unittest_test_bus { #address-cells = <1>; #size-cells = <0>; + gpio@0 { compatible = "unittest-gpio"; reg = <0>; diff --git a/drivers/of/unittest-data/overlay_gpio_02a.dtso b/drivers/of/unittest-data/overlay_gpio_02a.dtso index ec59aff6ed47..da955537df74 100644 --- a/drivers/of/unittest-data/overlay_gpio_02a.dtso +++ b/drivers/of/unittest-data/overlay_gpio_02a.dtso @@ -5,6 +5,7 @@ &unittest_test_bus { #address-cells = <1>; #size-cells = <0>; + gpio@2 { compatible = "unittest-gpio"; reg = <2>; diff --git a/drivers/of/unittest-data/overlay_gpio_02b.dtso b/drivers/of/unittest-data/overlay_gpio_02b.dtso index 43ce111d41ce..79503965d3d7 100644 --- a/drivers/of/unittest-data/overlay_gpio_02b.dtso +++ b/drivers/of/unittest-data/overlay_gpio_02b.dtso @@ -5,6 +5,7 @@ &unittest_test_bus { #address-cells = <1>; #size-cells = <0>; + gpio@2 { line-a { gpio-hog; diff --git a/drivers/of/unittest-data/overlay_gpio_03.dtso b/drivers/of/unittest-data/overlay_gpio_03.dtso index 6e0312340a1b..d8c709616029 100644 --- a/drivers/of/unittest-data/overlay_gpio_03.dtso +++ b/drivers/of/unittest-data/overlay_gpio_03.dtso @@ -5,6 +5,7 @@ &unittest_test_bus { #address-cells = <1>; #size-cells = <0>; + gpio@3 { compatible = "unittest-gpio"; reg = <3>; diff --git a/drivers/of/unittest-data/overlay_gpio_04a.dtso b/drivers/of/unittest-data/overlay_gpio_04a.dtso index 7b1e04ebfa7a..de86511972c2 100644 --- a/drivers/of/unittest-data/overlay_gpio_04a.dtso +++ b/drivers/of/unittest-data/overlay_gpio_04a.dtso @@ -5,6 +5,7 @@ &unittest_test_bus { #address-cells = <1>; #size-cells = <0>; + gpio@4 { compatible = "unittest-gpio"; reg = <4>; diff --git a/drivers/of/unittest-data/overlay_gpio_04b.dtso b/drivers/of/unittest-data/overlay_gpio_04b.dtso index a14e95c6699a..dc6eff22f927 100644 --- a/drivers/of/unittest-data/overlay_gpio_04b.dtso +++ b/drivers/of/unittest-data/overlay_gpio_04b.dtso @@ -5,6 +5,7 @@ &unittest_test_bus { #address-cells = <1>; #size-cells = <0>; + gpio@4 { line-c { gpio-hog; diff --git a/drivers/of/unittest-data/overlay_pci_node.dtso b/drivers/of/unittest-data/overlay_pci_node.dtso new file mode 100644 index 000000000000..c05e52e9e44a --- /dev/null +++ b/drivers/of/unittest-data/overlay_pci_node.dtso @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: GPL-2.0 +/dts-v1/; +/ { + fragment@0 { + target-path=""; + __overlay__ { + #address-cells = <3>; + #size-cells = <2>; + pci-ep-bus@0 { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0x0 0x0 0x0 0x1000>; + reg = <0 0 0 0 0>; + unittest-pci@100 { + compatible = "unittest-pci"; + reg = <0x100 0x200>; + }; + }; + }; + }; +}; diff --git a/drivers/of/unittest-data/testcases_common.dtsi b/drivers/of/unittest-data/testcases_common.dtsi index e7887f2301c1..1c2cdf353ae3 100644 --- a/drivers/of/unittest-data/testcases_common.dtsi +++ b/drivers/of/unittest-data/testcases_common.dtsi @@ -5,6 +5,7 @@ changeset { prop-update = "hello"; prop-remove = "world"; + node-remove { }; }; diff --git a/drivers/of/unittest-data/tests-interrupts.dtsi b/drivers/of/unittest-data/tests-interrupts.dtsi index ecc74dbcc373..7c9f31cc131b 100644 --- a/drivers/of/unittest-data/tests-interrupts.dtsi +++ b/drivers/of/unittest-data/tests-interrupts.dtsi @@ -5,6 +5,7 @@ interrupts { #address-cells = <1>; #size-cells = <1>; + test_intc0: intc0 { interrupt-controller; #interrupt-cells = <1>; diff --git a/drivers/of/unittest-data/tests-overlay.dtsi b/drivers/of/unittest-data/tests-overlay.dtsi index 4ea024d908ee..eb35e8aa5d5a 100644 --- a/drivers/of/unittest-data/tests-overlay.dtsi +++ b/drivers/of/unittest-data/tests-overlay.dtsi @@ -3,7 +3,6 @@ / { testcase-data { overlay-node { - /* test bus */ unittest_test_bus: test-bus { compatible = "simple-bus"; diff --git a/drivers/of/unittest-data/tests-phandle.dtsi b/drivers/of/unittest-data/tests-phandle.dtsi index 6b33be4c4416..d01f92f0f0db 100644 --- a/drivers/of/unittest-data/tests-phandle.dtsi +++ b/drivers/of/unittest-data/tests-phandle.dtsi @@ -8,7 +8,9 @@ testcase: testcase-data { security-password = "password"; duplicate-name = "duplicate"; + duplicate-name { }; + phandle-tests { provider0: provider0 { #phandle-cells = <0>; diff --git a/drivers/of/unittest.c b/drivers/of/unittest.c index b545fcb22536..ad2b7879cc67 100644 --- a/drivers/of/unittest.c +++ b/drivers/of/unittest.c @@ -22,6 +22,7 @@ #include <linux/slab.h> #include <linux/device.h> #include <linux/platform_device.h> +#include <linux/pci.h> #include <linux/kernel.h> #include <linux/i2c.h> @@ -77,7 +78,7 @@ static void __init of_unittest_find_node_by_name(void) np = of_find_node_by_path("/testcase-data"); name = kasprintf(GFP_KERNEL, "%pOF", np); - unittest(np && !strcmp("/testcase-data", name), + unittest(np && name && !strcmp("/testcase-data", name), "find /testcase-data failed\n"); of_node_put(np); kfree(name); @@ -88,14 +89,14 @@ static void __init of_unittest_find_node_by_name(void) np = of_find_node_by_path("/testcase-data/phandle-tests/consumer-a"); name = kasprintf(GFP_KERNEL, "%pOF", np); - unittest(np && !strcmp("/testcase-data/phandle-tests/consumer-a", name), + unittest(np && name && !strcmp("/testcase-data/phandle-tests/consumer-a", name), "find /testcase-data/phandle-tests/consumer-a failed\n"); of_node_put(np); kfree(name); np = of_find_node_by_path("testcase-alias"); name = kasprintf(GFP_KERNEL, "%pOF", np); - unittest(np && !strcmp("/testcase-data", name), + unittest(np && name && !strcmp("/testcase-data", name), "find testcase-alias failed\n"); of_node_put(np); kfree(name); @@ -106,7 +107,7 @@ static void __init of_unittest_find_node_by_name(void) np = of_find_node_by_path("testcase-alias/phandle-tests/consumer-a"); name = kasprintf(GFP_KERNEL, "%pOF", np); - unittest(np && !strcmp("/testcase-data/phandle-tests/consumer-a", name), + unittest(np && name && !strcmp("/testcase-data/phandle-tests/consumer-a", name), "find testcase-alias/phandle-tests/consumer-a failed\n"); of_node_put(np); kfree(name); @@ -796,14 +797,18 @@ static void __init of_unittest_property_copy(void) static void __init of_unittest_changeset(void) { #ifdef CONFIG_OF_DYNAMIC + int ret; struct property *ppadd, padd = { .name = "prop-add", .length = 1, .value = "" }; struct property *ppname_n1, pname_n1 = { .name = "name", .length = 3, .value = "n1" }; struct property *ppname_n2, pname_n2 = { .name = "name", .length = 3, .value = "n2" }; struct property *ppname_n21, pname_n21 = { .name = "name", .length = 3, .value = "n21" }; struct property *ppupdate, pupdate = { .name = "prop-update", .length = 5, .value = "abcd" }; struct property *ppremove; - struct device_node *n1, *n2, *n21, *nchangeset, *nremove, *parent, *np; + struct device_node *n1, *n2, *n21, *n22, *nchangeset, *nremove, *parent, *np; + static const char * const str_array[] = { "str1", "str2", "str3" }; + const u32 u32_array[] = { 1, 2, 3 }; struct of_changeset chgset; + const char *propstr = NULL; n1 = __of_node_dup(NULL, "n1"); unittest(n1, "testcase setup failure\n"); @@ -857,6 +862,17 @@ static void __init of_unittest_changeset(void) unittest(!of_changeset_add_property(&chgset, parent, ppadd), "fail add prop prop-add\n"); unittest(!of_changeset_update_property(&chgset, parent, ppupdate), "fail update prop\n"); unittest(!of_changeset_remove_property(&chgset, parent, ppremove), "fail remove prop\n"); + n22 = of_changeset_create_node(&chgset, n2, "n22"); + unittest(n22, "fail create n22\n"); + unittest(!of_changeset_add_prop_string(&chgset, n22, "prop-str", "abcd"), + "fail add prop prop-str"); + unittest(!of_changeset_add_prop_string_array(&chgset, n22, "prop-str-array", + (const char **)str_array, + ARRAY_SIZE(str_array)), + "fail add prop prop-str-array"); + unittest(!of_changeset_add_prop_u32_array(&chgset, n22, "prop-u32-array", + u32_array, ARRAY_SIZE(u32_array)), + "fail add prop prop-u32-array"); unittest(!of_changeset_apply(&chgset), "apply failed\n"); @@ -866,14 +882,29 @@ static void __init of_unittest_changeset(void) unittest((np = of_find_node_by_path("/testcase-data/changeset/n2/n21")), "'%pOF' not added\n", n21); of_node_put(np); + unittest((np = of_find_node_by_path("/testcase-data/changeset/n2/n22")), + "'%pOF' not added\n", n22); + of_node_put(np); unittest(!of_changeset_revert(&chgset), "revert failed\n"); + unittest(!of_find_node_by_path("/testcase-data/changeset/n2/n21"), + "'%pOF' still present after revert\n", n21); + + ppremove = of_find_property(parent, "prop-remove", NULL); + unittest(ppremove, "failed to find removed prop after revert\n"); + + ret = of_property_read_string(parent, "prop-update", &propstr); + unittest(!ret, "failed to find updated prop after revert\n"); + if (!ret) + unittest(strcmp(propstr, "hello") == 0, "original value not in updated property after revert"); + of_changeset_destroy(&chgset); of_node_put(n1); of_node_put(n2); of_node_put(n21); + of_node_put(n22); #endif } @@ -1533,6 +1564,8 @@ static void attach_node_and_children(struct device_node *np) const char *full_name; full_name = kasprintf(GFP_KERNEL, "%pOF", np); + if (!full_name) + return; if (!strcmp(full_name, "/__local_fixups__") || !strcmp(full_name, "/__fixups__")) { @@ -1694,7 +1727,7 @@ static struct platform_driver unittest_driver = { .remove_new = unittest_remove, .driver = { .name = "unittest", - .of_match_table = of_match_ptr(unittest_match), + .of_match_table = unittest_match, }, }; @@ -1795,7 +1828,7 @@ static struct platform_driver unittest_gpio_driver = { .remove_new = unittest_gpio_remove, .driver = { .name = "unittest-gpio", - .of_match_table = of_match_ptr(unittest_gpio_id), + .of_match_table = unittest_gpio_id, }, }; @@ -2101,14 +2134,13 @@ static int __init of_unittest_apply_overlay(int overlay_nr, int *ovcs_id) return 0; } -/* apply an overlay while checking before and after states */ -static int __init of_unittest_apply_overlay_check(int overlay_nr, +static int __init __of_unittest_apply_overlay_check(int overlay_nr, int unittest_nr, int before, int after, enum overlay_type ovtype) { int ret, ovcs_id; - /* unittest device must not be in before state */ + /* unittest device must be in before state */ if (of_unittest_device_exists(unittest_nr, ovtype) != before) { unittest(0, "%s with device @\"%s\" %s\n", overlay_name_from_nr(overlay_nr), @@ -2117,6 +2149,7 @@ static int __init of_unittest_apply_overlay_check(int overlay_nr, return -EINVAL; } + /* apply the overlay */ ovcs_id = 0; ret = of_unittest_apply_overlay(overlay_nr, &ovcs_id); if (ret != 0) { @@ -2124,15 +2157,28 @@ static int __init of_unittest_apply_overlay_check(int overlay_nr, return ret; } - /* unittest device must be to set to after state */ + /* unittest device must be in after state */ if (of_unittest_device_exists(unittest_nr, ovtype) != after) { - unittest(0, "%s failed to create @\"%s\" %s\n", + unittest(0, "%s with device @\"%s\" %s\n", overlay_name_from_nr(overlay_nr), unittest_path(unittest_nr, ovtype), !after ? "enabled" : "disabled"); return -EINVAL; } + return ovcs_id; +} + +/* apply an overlay while checking before and after states */ +static int __init of_unittest_apply_overlay_check(int overlay_nr, + int unittest_nr, int before, int after, + enum overlay_type ovtype) +{ + int ovcs_id = __of_unittest_apply_overlay_check(overlay_nr, + unittest_nr, before, after, ovtype); + if (ovcs_id < 0) + return ovcs_id; + return 0; } @@ -2143,32 +2189,12 @@ static int __init of_unittest_apply_revert_overlay_check(int overlay_nr, { int ret, ovcs_id, save_ovcs_id; - /* unittest device must be in before state */ - if (of_unittest_device_exists(unittest_nr, ovtype) != before) { - unittest(0, "%s with device @\"%s\" %s\n", - overlay_name_from_nr(overlay_nr), - unittest_path(unittest_nr, ovtype), - !before ? "enabled" : "disabled"); - return -EINVAL; - } - - /* apply the overlay */ - ovcs_id = 0; - ret = of_unittest_apply_overlay(overlay_nr, &ovcs_id); - if (ret != 0) { - /* of_unittest_apply_overlay already called unittest() */ - return ret; - } - - /* unittest device must be in after state */ - if (of_unittest_device_exists(unittest_nr, ovtype) != after) { - unittest(0, "%s failed to create @\"%s\" %s\n", - overlay_name_from_nr(overlay_nr), - unittest_path(unittest_nr, ovtype), - !after ? "enabled" : "disabled"); - return -EINVAL; - } + ovcs_id = __of_unittest_apply_overlay_check(overlay_nr, unittest_nr, + before, after, ovtype); + if (ovcs_id < 0) + return ovcs_id; + /* remove the overlay */ save_ovcs_id = ovcs_id; ret = of_overlay_remove(&ovcs_id); if (ret != 0) { @@ -2180,7 +2206,7 @@ static int __init of_unittest_apply_revert_overlay_check(int overlay_nr, of_unittest_untrack_overlay(save_ovcs_id); /* unittest device must be again in before state */ - if (of_unittest_device_exists(unittest_nr, PDEV_OVERLAY) != before) { + if (of_unittest_device_exists(unittest_nr, ovtype) != before) { unittest(0, "%s with device @\"%s\" %s\n", overlay_name_from_nr(overlay_nr), unittest_path(unittest_nr, ovtype), @@ -2622,7 +2648,7 @@ static struct platform_driver unittest_i2c_bus_driver = { .remove_new = unittest_i2c_bus_remove, .driver = { .name = "unittest-i2c-bus", - .of_match_table = of_match_ptr(unittest_i2c_bus_match), + .of_match_table = unittest_i2c_bus_match, }, }; @@ -2971,12 +2997,6 @@ static void __init of_unittest_overlay_notify(void) unittest(ovcs_id, "ovcs_id not created for overlay_17\n"); - if (ovcs_id) { - ret = of_overlay_remove(&ovcs_id); - unittest(!ret, - "overlay_17 of_overlay_remove(), ret = %d\n", ret); - } - /* --- overlay 18 --- */ unittest(overlay_data_apply("overlay_18", &ovcs_id), @@ -3046,6 +3066,7 @@ static void __init of_unittest_overlay_notify(void) static void __init of_unittest_overlay(void) { struct device_node *bus_np = NULL; + unsigned int i; if (platform_driver_register(&unittest_driver)) { unittest(0, "could not register unittest driver\n"); @@ -3083,7 +3104,8 @@ static void __init of_unittest_overlay(void) of_unittest_overlay_2(); of_unittest_overlay_3(); of_unittest_overlay_4(); - of_unittest_overlay_5(); + for (i = 0; i < 3; i++) + of_unittest_overlay_5(); of_unittest_overlay_6(); of_unittest_overlay_8(); @@ -3264,17 +3286,19 @@ out_skip_tests: extern uint8_t __dtbo_##overlay_name##_begin[]; \ extern uint8_t __dtbo_##overlay_name##_end[] -#define OVERLAY_INFO(overlay_name, expected) \ -{ .dtbo_begin = __dtbo_##overlay_name##_begin, \ - .dtbo_end = __dtbo_##overlay_name##_end, \ - .expected_result = expected, \ - .name = #overlay_name, \ +#define OVERLAY_INFO(overlay_name, expected, expected_remove) \ +{ .dtbo_begin = __dtbo_##overlay_name##_begin, \ + .dtbo_end = __dtbo_##overlay_name##_end, \ + .expected_result = expected, \ + .expected_result_remove = expected_remove, \ + .name = #overlay_name, \ } struct overlay_info { uint8_t *dtbo_begin; uint8_t *dtbo_end; int expected_result; + int expected_result_remove; /* if apply failed */ int ovcs_id; char *name; }; @@ -3307,47 +3331,51 @@ OVERLAY_INFO_EXTERN(overlay_gpio_02b); OVERLAY_INFO_EXTERN(overlay_gpio_03); OVERLAY_INFO_EXTERN(overlay_gpio_04a); OVERLAY_INFO_EXTERN(overlay_gpio_04b); +OVERLAY_INFO_EXTERN(overlay_pci_node); OVERLAY_INFO_EXTERN(overlay_bad_add_dup_node); OVERLAY_INFO_EXTERN(overlay_bad_add_dup_prop); OVERLAY_INFO_EXTERN(overlay_bad_phandle); OVERLAY_INFO_EXTERN(overlay_bad_symbol); +OVERLAY_INFO_EXTERN(overlay_bad_unresolved); /* entries found by name */ static struct overlay_info overlays[] = { - OVERLAY_INFO(overlay_base, -9999), - OVERLAY_INFO(overlay, 0), - OVERLAY_INFO(overlay_0, 0), - OVERLAY_INFO(overlay_1, 0), - OVERLAY_INFO(overlay_2, 0), - OVERLAY_INFO(overlay_3, 0), - OVERLAY_INFO(overlay_4, 0), - OVERLAY_INFO(overlay_5, 0), - OVERLAY_INFO(overlay_6, 0), - OVERLAY_INFO(overlay_7, 0), - OVERLAY_INFO(overlay_8, 0), - OVERLAY_INFO(overlay_9, 0), - OVERLAY_INFO(overlay_10, 0), - OVERLAY_INFO(overlay_11, 0), - OVERLAY_INFO(overlay_12, 0), - OVERLAY_INFO(overlay_13, 0), - OVERLAY_INFO(overlay_15, 0), - OVERLAY_INFO(overlay_16, -EBUSY), - OVERLAY_INFO(overlay_17, -EEXIST), - OVERLAY_INFO(overlay_18, 0), - OVERLAY_INFO(overlay_19, 0), - OVERLAY_INFO(overlay_20, 0), - OVERLAY_INFO(overlay_gpio_01, 0), - OVERLAY_INFO(overlay_gpio_02a, 0), - OVERLAY_INFO(overlay_gpio_02b, 0), - OVERLAY_INFO(overlay_gpio_03, 0), - OVERLAY_INFO(overlay_gpio_04a, 0), - OVERLAY_INFO(overlay_gpio_04b, 0), - OVERLAY_INFO(overlay_bad_add_dup_node, -EINVAL), - OVERLAY_INFO(overlay_bad_add_dup_prop, -EINVAL), - OVERLAY_INFO(overlay_bad_phandle, -EINVAL), - OVERLAY_INFO(overlay_bad_symbol, -EINVAL), + OVERLAY_INFO(overlay_base, -9999, 0), + OVERLAY_INFO(overlay, 0, 0), + OVERLAY_INFO(overlay_0, 0, 0), + OVERLAY_INFO(overlay_1, 0, 0), + OVERLAY_INFO(overlay_2, 0, 0), + OVERLAY_INFO(overlay_3, 0, 0), + OVERLAY_INFO(overlay_4, 0, 0), + OVERLAY_INFO(overlay_5, 0, 0), + OVERLAY_INFO(overlay_6, 0, 0), + OVERLAY_INFO(overlay_7, 0, 0), + OVERLAY_INFO(overlay_8, 0, 0), + OVERLAY_INFO(overlay_9, 0, 0), + OVERLAY_INFO(overlay_10, 0, 0), + OVERLAY_INFO(overlay_11, 0, 0), + OVERLAY_INFO(overlay_12, 0, 0), + OVERLAY_INFO(overlay_13, 0, 0), + OVERLAY_INFO(overlay_15, 0, 0), + OVERLAY_INFO(overlay_16, -EBUSY, 0), + OVERLAY_INFO(overlay_17, -EEXIST, 0), + OVERLAY_INFO(overlay_18, 0, 0), + OVERLAY_INFO(overlay_19, 0, 0), + OVERLAY_INFO(overlay_20, 0, 0), + OVERLAY_INFO(overlay_gpio_01, 0, 0), + OVERLAY_INFO(overlay_gpio_02a, 0, 0), + OVERLAY_INFO(overlay_gpio_02b, 0, 0), + OVERLAY_INFO(overlay_gpio_03, 0, 0), + OVERLAY_INFO(overlay_gpio_04a, 0, 0), + OVERLAY_INFO(overlay_gpio_04b, 0, 0), + OVERLAY_INFO(overlay_pci_node, 0, 0), + OVERLAY_INFO(overlay_bad_add_dup_node, -EINVAL, -ENODEV), + OVERLAY_INFO(overlay_bad_add_dup_prop, -EINVAL, -ENODEV), + OVERLAY_INFO(overlay_bad_phandle, -EINVAL, 0), + OVERLAY_INFO(overlay_bad_symbol, -EINVAL, -ENODEV), + OVERLAY_INFO(overlay_bad_unresolved, -EINVAL, 0), /* end marker */ - {.dtbo_begin = NULL, .dtbo_end = NULL, .expected_result = 0, .name = NULL} + { } }; static struct device_node *overlay_base_root; @@ -3442,8 +3470,9 @@ void __init unittest_unflatten_overlay_base(void) static int __init overlay_data_apply(const char *overlay_name, int *ovcs_id) { struct overlay_info *info; + int passed = 1; int found = 0; - int ret; + int ret, ret2; u32 size; for (info = overlays; info && info->name; info++) { @@ -3461,7 +3490,8 @@ static int __init overlay_data_apply(const char *overlay_name, int *ovcs_id) if (!size) pr_err("no overlay data for %s\n", overlay_name); - ret = of_overlay_fdt_apply(info->dtbo_begin, size, &info->ovcs_id); + ret = of_overlay_fdt_apply(info->dtbo_begin, size, &info->ovcs_id, + NULL); if (ovcs_id) *ovcs_id = info->ovcs_id; if (ret < 0) @@ -3470,11 +3500,24 @@ static int __init overlay_data_apply(const char *overlay_name, int *ovcs_id) pr_debug("%s applied\n", overlay_name); out: - if (ret != info->expected_result) + if (ret != info->expected_result) { pr_err("of_overlay_fdt_apply() expected %d, ret=%d, %s\n", info->expected_result, ret, overlay_name); + passed = 0; + } - return (ret == info->expected_result); + if (ret < 0) { + /* changeset may be partially applied */ + ret2 = of_overlay_remove(&info->ovcs_id); + if (ret2 != info->expected_result_remove) { + pr_err("of_overlay_remove() expected %d, ret=%d, %s\n", + info->expected_result_remove, ret2, + overlay_name); + passed = 0; + } + } + + return passed; } /* @@ -3613,6 +3656,8 @@ static __init void of_unittest_overlay_high_level(void) /* now do the normal overlay usage test */ + /* --- overlay --- */ + EXPECT_BEGIN(KERN_ERR, "OF: overlay: WARNING: memory leak will occur if overlay removed, property: /testcase-data-2/substation@100/status"); EXPECT_BEGIN(KERN_ERR, @@ -3663,51 +3708,283 @@ static __init void of_unittest_overlay_high_level(void) unittest(ret, "Adding overlay 'overlay' failed\n"); + /* --- overlay_bad_add_dup_node --- */ + EXPECT_BEGIN(KERN_ERR, "OF: overlay: ERROR: multiple fragments add and/or delete node /testcase-data-2/substation@100/motor-1/controller"); EXPECT_BEGIN(KERN_ERR, "OF: overlay: ERROR: multiple fragments add, update, and/or delete property /testcase-data-2/substation@100/motor-1/controller/name"); + EXPECT_BEGIN(KERN_ERR, + "OF: changeset: apply failed: REMOVE_PROPERTY /testcase-data-2/substation@100/motor-1/controller:name"); + EXPECT_BEGIN(KERN_ERR, + "OF: Error reverting changeset (-19)"); unittest(overlay_data_apply("overlay_bad_add_dup_node", NULL), "Adding overlay 'overlay_bad_add_dup_node' failed\n"); EXPECT_END(KERN_ERR, + "OF: Error reverting changeset (-19)"); + EXPECT_END(KERN_ERR, + "OF: changeset: apply failed: REMOVE_PROPERTY /testcase-data-2/substation@100/motor-1/controller:name"); + EXPECT_END(KERN_ERR, "OF: overlay: ERROR: multiple fragments add, update, and/or delete property /testcase-data-2/substation@100/motor-1/controller/name"); EXPECT_END(KERN_ERR, "OF: overlay: ERROR: multiple fragments add and/or delete node /testcase-data-2/substation@100/motor-1/controller"); + /* --- overlay_bad_add_dup_prop --- */ + EXPECT_BEGIN(KERN_ERR, "OF: overlay: ERROR: multiple fragments add and/or delete node /testcase-data-2/substation@100/motor-1/electric"); EXPECT_BEGIN(KERN_ERR, "OF: overlay: ERROR: multiple fragments add, update, and/or delete property /testcase-data-2/substation@100/motor-1/electric/rpm_avail"); EXPECT_BEGIN(KERN_ERR, "OF: overlay: ERROR: multiple fragments add, update, and/or delete property /testcase-data-2/substation@100/motor-1/electric/name"); + EXPECT_BEGIN(KERN_ERR, + "OF: changeset: apply failed: REMOVE_PROPERTY /testcase-data-2/substation@100/motor-1/electric:name"); + EXPECT_BEGIN(KERN_ERR, + "OF: Error reverting changeset (-19)"); unittest(overlay_data_apply("overlay_bad_add_dup_prop", NULL), "Adding overlay 'overlay_bad_add_dup_prop' failed\n"); EXPECT_END(KERN_ERR, - "OF: overlay: ERROR: multiple fragments add, update, and/or delete property /testcase-data-2/substation@100/motor-1/electric/name"); + "OF: Error reverting changeset (-19)"); EXPECT_END(KERN_ERR, - "OF: overlay: ERROR: multiple fragments add, update, and/or delete property /testcase-data-2/substation@100/motor-1/electric/rpm_avail"); + "OF: changeset: apply failed: REMOVE_PROPERTY /testcase-data-2/substation@100/motor-1/electric:name"); EXPECT_END(KERN_ERR, - "OF: overlay: ERROR: multiple fragments add and/or delete node /testcase-data-2/substation@100/motor-1/electric"); + "OF: overlay: ERROR: multiple fragments add, update, and/or delete property /testcase-data-2/substation@100/motor-1/electric/name"); + EXPECT_END(KERN_ERR, + "OF: overlay: ERROR: multiple fragments add, update, and/or delete property /testcase-data-2/substation@100/motor-1/electric/rpm_avail"); + EXPECT_END(KERN_ERR, + "OF: overlay: ERROR: multiple fragments add and/or delete node /testcase-data-2/substation@100/motor-1/electric"); + + /* --- overlay_bad_phandle --- */ unittest(overlay_data_apply("overlay_bad_phandle", NULL), "Adding overlay 'overlay_bad_phandle' failed\n"); + /* --- overlay_bad_symbol --- */ + + EXPECT_BEGIN(KERN_ERR, + "OF: changeset: apply failed: REMOVE_PROPERTY /testcase-data-2/substation@100/hvac-medium-2:name"); + EXPECT_BEGIN(KERN_ERR, + "OF: Error reverting changeset (-19)"); + unittest(overlay_data_apply("overlay_bad_symbol", NULL), "Adding overlay 'overlay_bad_symbol' failed\n"); + EXPECT_END(KERN_ERR, + "OF: Error reverting changeset (-19)"); + EXPECT_END(KERN_ERR, + "OF: changeset: apply failed: REMOVE_PROPERTY /testcase-data-2/substation@100/hvac-medium-2:name"); + + /* --- overlay_bad_unresolved --- */ + + EXPECT_BEGIN(KERN_ERR, + "OF: resolver: node label 'this_label_does_not_exist' not found in live devicetree symbols table"); + EXPECT_BEGIN(KERN_ERR, + "OF: resolver: overlay phandle fixup failed: -22"); + + unittest(overlay_data_apply("overlay_bad_unresolved", NULL), + "Adding overlay 'overlay_bad_unresolved' failed\n"); + + EXPECT_END(KERN_ERR, + "OF: resolver: overlay phandle fixup failed: -22"); + EXPECT_END(KERN_ERR, + "OF: resolver: node label 'this_label_does_not_exist' not found in live devicetree symbols table"); + return; err_unlock: mutex_unlock(&of_mutex); } +static int of_unittest_pci_dev_num; +static int of_unittest_pci_child_num; + +/* + * PCI device tree node test driver + */ +static const struct pci_device_id testdrv_pci_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_REDHAT, 0x5), }, /* PCI_VENDOR_ID_REDHAT */ + { 0, } +}; + +static int testdrv_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct overlay_info *info; + struct device_node *dn; + int ret, ovcs_id; + u32 size; + + dn = pdev->dev.of_node; + if (!dn) { + dev_err(&pdev->dev, "does not find bus endpoint"); + return -EINVAL; + } + + for (info = overlays; info && info->name; info++) { + if (!strcmp(info->name, "overlay_pci_node")) + break; + } + if (!info || !info->name) { + dev_err(&pdev->dev, "no overlay data for overlay_pci_node"); + return -ENODEV; + } + + size = info->dtbo_end - info->dtbo_begin; + ret = of_overlay_fdt_apply(info->dtbo_begin, size, &ovcs_id, dn); + of_node_put(dn); + if (ret) + return ret; + + of_platform_default_populate(dn, NULL, &pdev->dev); + pci_set_drvdata(pdev, (void *)(uintptr_t)ovcs_id); + + return 0; +} + +static void testdrv_remove(struct pci_dev *pdev) +{ + int ovcs_id = (int)(uintptr_t)pci_get_drvdata(pdev); + + of_platform_depopulate(&pdev->dev); + of_overlay_remove(&ovcs_id); +} + +static struct pci_driver testdrv_driver = { + .name = "pci_dt_testdrv", + .id_table = testdrv_pci_ids, + .probe = testdrv_probe, + .remove = testdrv_remove, +}; + +static int unittest_pci_probe(struct platform_device *pdev) +{ + struct resource *res; + struct device *dev; + u64 exp_addr; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + + dev = &pdev->dev; + while (dev && !dev_is_pci(dev)) + dev = dev->parent; + if (!dev) { + pr_err("unable to find parent device\n"); + return -ENODEV; + } + + exp_addr = pci_resource_start(to_pci_dev(dev), 0) + 0x100; + unittest(res->start == exp_addr, "Incorrect translated address %llx, expected %llx\n", + (u64)res->start, exp_addr); + + of_unittest_pci_child_num++; + + return 0; +} + +static const struct of_device_id unittest_pci_of_match[] = { + { .compatible = "unittest-pci" }, + { } +}; + +static struct platform_driver unittest_pci_driver = { + .probe = unittest_pci_probe, + .driver = { + .name = "unittest-pci", + .of_match_table = unittest_pci_of_match, + }, +}; + +static int of_unittest_pci_node_verify(struct pci_dev *pdev, bool add) +{ + struct device_node *pnp, *np = NULL; + struct device *child_dev; + char *path = NULL; + const __be32 *reg; + int rc = 0; + + pnp = pdev->dev.of_node; + unittest(pnp, "Failed creating PCI dt node\n"); + if (!pnp) + return -ENODEV; + + if (add) { + path = kasprintf(GFP_KERNEL, "%pOF/pci-ep-bus@0/unittest-pci@100", pnp); + np = of_find_node_by_path(path); + unittest(np, "Failed to get unittest-pci node under PCI node\n"); + if (!np) { + rc = -ENODEV; + goto failed; + } + + reg = of_get_property(np, "reg", NULL); + unittest(reg, "Failed to get reg property\n"); + if (!reg) + rc = -ENODEV; + } else { + path = kasprintf(GFP_KERNEL, "%pOF/pci-ep-bus@0", pnp); + np = of_find_node_by_path(path); + unittest(!np, "Child device tree node is not removed\n"); + child_dev = device_find_any_child(&pdev->dev); + unittest(!child_dev, "Child device is not removed\n"); + } + +failed: + kfree(path); + if (np) + of_node_put(np); + + return rc; +} + +static void __init of_unittest_pci_node(void) +{ + struct pci_dev *pdev = NULL; + int rc; + + if (!IS_ENABLED(CONFIG_PCI_DYNAMIC_OF_NODES)) + return; + + rc = pci_register_driver(&testdrv_driver); + unittest(!rc, "Failed to register pci test driver; rc = %d\n", rc); + if (rc) + return; + + rc = platform_driver_register(&unittest_pci_driver); + if (unittest(!rc, "Failed to register unittest pci driver\n")) { + pci_unregister_driver(&testdrv_driver); + return; + } + + while ((pdev = pci_get_device(PCI_VENDOR_ID_REDHAT, 0x5, pdev)) != NULL) { + of_unittest_pci_node_verify(pdev, true); + of_unittest_pci_dev_num++; + } + if (pdev) + pci_dev_put(pdev); + + unittest(of_unittest_pci_dev_num, + "No test PCI device been found. Please run QEMU with '-device pci-testdev'\n"); + unittest(of_unittest_pci_dev_num == of_unittest_pci_child_num, + "Child device number %d is not expected %d", of_unittest_pci_child_num, + of_unittest_pci_dev_num); + + platform_driver_unregister(&unittest_pci_driver); + pci_unregister_driver(&testdrv_driver); + + while ((pdev = pci_get_device(PCI_VENDOR_ID_REDHAT, 0x5, pdev)) != NULL) + of_unittest_pci_node_verify(pdev, false); + if (pdev) + pci_dev_put(pdev); +} #else static inline __init void of_unittest_overlay_high_level(void) {} +static inline __init void of_unittest_pci_node(void) { } #endif @@ -3761,6 +4038,7 @@ static int __init of_unittest(void) of_unittest_platform_populate(); of_unittest_overlay(); of_unittest_lifecycle(); + of_unittest_pci_node(); /* Double check linkage after removing testcase data */ of_unittest_check_tree_linkage(); |