diff options
author | Olof Johansson <olof@lixom.net> | 2019-12-05 13:16:58 -0800 |
---|---|---|
committer | Olof Johansson <olof@lixom.net> | 2019-12-05 13:18:54 -0800 |
commit | 942e6f8a8314e5550e254519dfba4ccd5170421d (patch) | |
tree | 75ec655b440fbc1c454247af38b5596dd8c78de9 /drivers/of | |
parent | ARM: pxa: Fix resource properties (diff) | |
parent | Merge tag 'armsoc-defconfig' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc (diff) | |
download | linux-dev-942e6f8a8314e5550e254519dfba4ccd5170421d.tar.xz linux-dev-942e6f8a8314e5550e254519dfba4ccd5170421d.zip |
Merge mainline/master into arm/fixes
This brings in the mainline tree right after armsoc contents was merged
this release cycle, so that we can re-run savedefconfig, etc.
Signed-off-by: Olof Johansson <olof@lixom.net>
Diffstat (limited to 'drivers/of')
-rw-r--r-- | drivers/of/address.c | 103 | ||||
-rw-r--r-- | drivers/of/base.c | 32 | ||||
-rw-r--r-- | drivers/of/device.c | 9 | ||||
-rw-r--r-- | drivers/of/fdt.c | 24 | ||||
-rw-r--r-- | drivers/of/of_mdio.c | 4 | ||||
-rw-r--r-- | drivers/of/of_net.c | 16 | ||||
-rw-r--r-- | drivers/of/of_private.h | 14 | ||||
-rw-r--r-- | drivers/of/of_reserved_mem.c | 4 | ||||
-rw-r--r-- | drivers/of/overlay.c | 37 | ||||
-rw-r--r-- | drivers/of/platform.c | 12 | ||||
-rw-r--r-- | drivers/of/property.c | 340 | ||||
-rw-r--r-- | drivers/of/unittest-data/testcases.dts | 1 | ||||
-rw-r--r-- | drivers/of/unittest-data/tests-address.dtsi | 48 | ||||
-rw-r--r-- | drivers/of/unittest.c | 97 |
14 files changed, 632 insertions, 109 deletions
diff --git a/drivers/of/address.c b/drivers/of/address.c index 978427a9d5e6..99c1b8058559 100644 --- a/drivers/of/address.c +++ b/drivers/of/address.c @@ -14,6 +14,8 @@ #include <linux/slab.h> #include <linux/string.h> +#include "of_private.h" + /* Max address size we deal with */ #define OF_MAX_ADDR_CELLS 4 #define OF_CHECK_ADDR_COUNT(na) ((na) > 0 && (na) <= OF_MAX_ADDR_CELLS) @@ -241,6 +243,7 @@ static int parser_init(struct of_pci_range_parser *parser, parser->node = node; parser->pna = of_n_addr_cells(node); parser->np = parser->pna + na + ns; + parser->dma = !strcmp(name, "dma-ranges"); parser->range = of_get_property(node, name, &rlen); if (parser->range == NULL) @@ -279,7 +282,11 @@ struct of_pci_range *of_pci_range_parser_one(struct of_pci_range_parser *parser, range->pci_space = be32_to_cpup(parser->range); range->flags = of_bus_pci_get_flags(parser->range); range->pci_addr = of_read_number(parser->range + 1, ns); - range->cpu_addr = of_translate_address(parser->node, + if (parser->dma) + range->cpu_addr = of_translate_dma_address(parser->node, + parser->range + na); + else + range->cpu_addr = of_translate_address(parser->node, parser->range + na); range->size = of_read_number(parser->range + parser->pna + na, ns); @@ -292,8 +299,12 @@ struct of_pci_range *of_pci_range_parser_one(struct of_pci_range_parser *parser, flags = of_bus_pci_get_flags(parser->range); pci_addr = of_read_number(parser->range + 1, ns); - cpu_addr = of_translate_address(parser->node, - parser->range + na); + if (parser->dma) + cpu_addr = of_translate_dma_address(parser->node, + parser->range + na); + else + cpu_addr = of_translate_address(parser->node, + parser->range + na); size = of_read_number(parser->range + parser->pna + na, ns); if (flags != range->flags) @@ -517,9 +528,13 @@ static int of_translate_one(struct device_node *parent, struct of_bus *bus, * * As far as we know, this damage only exists on Apple machines, so * This code is only enabled on powerpc. --gcl + * + * This quirk also applies for 'dma-ranges' which frequently exist in + * child nodes without 'dma-ranges' in the parent nodes. --RobH */ ranges = of_get_property(parent, rprop, &rlen); - if (ranges == NULL && !of_empty_ranges_quirk(parent)) { + if (ranges == NULL && !of_empty_ranges_quirk(parent) && + strcmp(rprop, "dma-ranges")) { pr_debug("no ranges; cannot translate\n"); return 1; } @@ -695,6 +710,16 @@ static struct device_node *__of_get_dma_parent(const struct device_node *np) return of_node_get(args.np); } +static struct device_node *of_get_next_dma_parent(struct device_node *np) +{ + struct device_node *parent; + + parent = __of_get_dma_parent(np); + of_node_put(np); + + return parent; +} + u64 of_translate_dma_address(struct device_node *dev, const __be32 *in_addr) { struct device_node *host; @@ -826,25 +851,6 @@ int of_address_to_resource(struct device_node *dev, int index, } EXPORT_SYMBOL_GPL(of_address_to_resource); -struct device_node *of_find_matching_node_by_address(struct device_node *from, - const struct of_device_id *matches, - u64 base_address) -{ - struct device_node *dn = of_find_matching_node(from, matches); - struct resource res; - - while (dn) { - if (!of_address_to_resource(dn, 0, &res) && - res.start == base_address) - return dn; - - dn = of_find_matching_node(dn, matches); - } - - return NULL; -} - - /** * of_iomap - Maps the memory mapped IO for a given device_node * @device: the device whose io range will be mapped @@ -924,47 +930,39 @@ int of_dma_get_range(struct device_node *np, u64 *dma_addr, u64 *paddr, u64 *siz const __be32 *ranges = NULL; int len, naddr, nsize, pna; int ret = 0; + bool found_dma_ranges = false; u64 dmaaddr; - if (!node) - return -EINVAL; - - while (1) { - struct device_node *parent; - - naddr = of_n_addr_cells(node); - nsize = of_n_size_cells(node); - - parent = __of_get_dma_parent(node); - of_node_put(node); - - node = parent; - if (!node) - break; - + while (node) { ranges = of_get_property(node, "dma-ranges", &len); /* Ignore empty ranges, they imply no translation required */ if (ranges && len > 0) break; - /* - * At least empty ranges has to be defined for parent node if - * DMA is supported - */ - if (!ranges) - break; + /* Once we find 'dma-ranges', then a missing one is an error */ + if (found_dma_ranges && !ranges) { + ret = -ENODEV; + goto out; + } + found_dma_ranges = true; + + node = of_get_next_dma_parent(node); } - if (!ranges) { + if (!node || !ranges) { pr_debug("no dma-ranges found for node(%pOF)\n", np); ret = -ENODEV; goto out; } - len /= sizeof(u32); - + naddr = of_bus_n_addr_cells(node); + nsize = of_bus_n_size_cells(node); pna = of_n_addr_cells(node); + if ((len / sizeof(__be32)) % (pna + naddr + nsize)) { + ret = -EINVAL; + goto out; + } /* dma-ranges format: * DMA addr : naddr cells @@ -972,10 +970,10 @@ int of_dma_get_range(struct device_node *np, u64 *dma_addr, u64 *paddr, u64 *siz * size : nsize cells */ dmaaddr = of_read_number(ranges, naddr); - *paddr = of_translate_dma_address(np, ranges); + *paddr = of_translate_dma_address(node, ranges + naddr); if (*paddr == OF_BAD_ADDR) { - pr_err("translation of DMA address(%pad) to CPU address failed node(%pOF)\n", - dma_addr, np); + pr_err("translation of DMA address(%llx) to CPU address failed node(%pOF)\n", + dmaaddr, np); ret = -EINVAL; goto out; } @@ -991,7 +989,6 @@ out: return ret; } -EXPORT_SYMBOL_GPL(of_dma_get_range); /** * of_dma_is_coherent - Check if device is coherent @@ -1009,7 +1006,7 @@ bool of_dma_is_coherent(struct device_node *np) of_node_put(node); return true; } - node = of_get_next_parent(node); + node = of_get_next_dma_parent(node); } of_node_put(node); return false; diff --git a/drivers/of/base.c b/drivers/of/base.c index 1d667eb730e1..db7fbc0c0893 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -86,34 +86,46 @@ static bool __of_node_is_type(const struct device_node *np, const char *type) return np && match && type && !strcmp(match, type); } -int of_n_addr_cells(struct device_node *np) +int of_bus_n_addr_cells(struct device_node *np) { u32 cells; - do { - if (np->parent) - np = np->parent; + for (; np; np = np->parent) if (!of_property_read_u32(np, "#address-cells", &cells)) return cells; - } while (np->parent); + /* No #address-cells property for the root node */ return OF_ROOT_NODE_ADDR_CELLS_DEFAULT; } + +int of_n_addr_cells(struct device_node *np) +{ + if (np->parent) + np = np->parent; + + return of_bus_n_addr_cells(np); +} EXPORT_SYMBOL(of_n_addr_cells); -int of_n_size_cells(struct device_node *np) +int of_bus_n_size_cells(struct device_node *np) { u32 cells; - do { - if (np->parent) - np = np->parent; + for (; np; np = np->parent) if (!of_property_read_u32(np, "#size-cells", &cells)) return cells; - } while (np->parent); + /* No #size-cells property for the root node */ return OF_ROOT_NODE_SIZE_CELLS_DEFAULT; } + +int of_n_size_cells(struct device_node *np) +{ + if (np->parent) + np = np->parent; + + return of_bus_n_size_cells(np); +} EXPORT_SYMBOL(of_n_size_cells); #ifdef CONFIG_NUMA diff --git a/drivers/of/device.c b/drivers/of/device.c index da8158392010..e9127db7b067 100644 --- a/drivers/of/device.c +++ b/drivers/of/device.c @@ -93,7 +93,7 @@ int of_dma_configure(struct device *dev, struct device_node *np, bool force_dma) bool coherent; unsigned long offset; const struct iommu_ops *iommu; - u64 mask; + u64 mask, end; ret = of_dma_get_range(np, &dma_addr, &paddr, &size); if (ret < 0) { @@ -148,12 +148,13 @@ int of_dma_configure(struct device *dev, struct device_node *np, bool force_dma) * Limit coherent and dma mask based on size and default mask * set by the driver. */ - mask = DMA_BIT_MASK(ilog2(dma_addr + size - 1) + 1); + end = dma_addr + size - 1; + mask = DMA_BIT_MASK(ilog2(end) + 1); dev->coherent_dma_mask &= mask; *dev->dma_mask &= mask; - /* ...but only set bus mask if we found valid dma-ranges earlier */ + /* ...but only set bus limit if we found valid dma-ranges earlier */ if (!ret) - dev->bus_dma_mask = mask; + dev->bus_dma_limit = end; coherent = of_dma_is_coherent(np); dev_dbg(dev, "device is%sdma coherent\n", diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index 223d617ecfe1..2cdf64d2456f 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -412,8 +412,8 @@ void *__unflatten_device_tree(const void *blob, /* Second pass, do actual unflattening */ unflatten_dt_nodes(blob, mem, dad, mynodes); if (be32_to_cpup(mem + size) != 0xdeadbeef) - pr_warning("End of tree marker overwritten: %08x\n", - be32_to_cpup(mem + size)); + pr_warn("End of tree marker overwritten: %08x\n", + be32_to_cpup(mem + size)); if (detached && mynodes) { of_node_set_flag(*mynodes, OF_DETACHED); @@ -947,8 +947,8 @@ int __init early_init_dt_scan_chosen_stdout(void) if (fdt_node_check_compatible(fdt, offset, match->compatible)) continue; - of_setup_earlycon(match, offset, options); - return 0; + if (of_setup_earlycon(match, offset, options) == 0) + return 0; } return -ENODEV; } @@ -1120,25 +1120,25 @@ void __init __weak early_init_dt_add_memory_arch(u64 base, u64 size) size &= PAGE_MASK; if (base > MAX_MEMBLOCK_ADDR) { - pr_warning("Ignoring memory block 0x%llx - 0x%llx\n", - base, base + size); + pr_warn("Ignoring memory block 0x%llx - 0x%llx\n", + base, base + size); return; } if (base + size - 1 > MAX_MEMBLOCK_ADDR) { - pr_warning("Ignoring memory range 0x%llx - 0x%llx\n", - ((u64)MAX_MEMBLOCK_ADDR) + 1, base + size); + pr_warn("Ignoring memory range 0x%llx - 0x%llx\n", + ((u64)MAX_MEMBLOCK_ADDR) + 1, base + size); size = MAX_MEMBLOCK_ADDR - base + 1; } if (base + size < phys_offset) { - pr_warning("Ignoring memory block 0x%llx - 0x%llx\n", - base, base + size); + pr_warn("Ignoring memory block 0x%llx - 0x%llx\n", + base, base + size); return; } if (base < phys_offset) { - pr_warning("Ignoring memory range 0x%llx - 0x%llx\n", - base, phys_offset); + pr_warn("Ignoring memory range 0x%llx - 0x%llx\n", + base, phys_offset); size -= phys_offset - base; base = phys_offset; } diff --git a/drivers/of/of_mdio.c b/drivers/of/of_mdio.c index bd6129db6417..c6b87ce2b0cc 100644 --- a/drivers/of/of_mdio.c +++ b/drivers/of/of_mdio.c @@ -361,8 +361,8 @@ struct phy_device *of_phy_get_and_connect(struct net_device *dev, struct phy_device *phy; int ret; - iface = of_get_phy_mode(np); - if ((int)iface < 0) + ret = of_get_phy_mode(np, &iface); + if (ret) return NULL; if (of_phy_is_fixed_link(np)) { ret = of_phy_register_fixed_link(np); diff --git a/drivers/of/of_net.c b/drivers/of/of_net.c index b02734aff8c1..6e411821583e 100644 --- a/drivers/of/of_net.c +++ b/drivers/of/of_net.c @@ -15,16 +15,20 @@ /** * of_get_phy_mode - Get phy mode for given device_node * @np: Pointer to the given device_node + * @interface: Pointer to the result * * The function gets phy interface string from property 'phy-mode' or - * 'phy-connection-type', and return its index in phy_modes table, or errno in - * error case. + * 'phy-connection-type'. The index in phy_modes table is set in + * interface and 0 returned. In case of error interface is set to + * PHY_INTERFACE_MODE_NA and an errno is returned, e.g. -ENODEV. */ -int of_get_phy_mode(struct device_node *np) +int of_get_phy_mode(struct device_node *np, phy_interface_t *interface) { const char *pm; int err, i; + *interface = PHY_INTERFACE_MODE_NA; + err = of_property_read_string(np, "phy-mode", &pm); if (err < 0) err = of_property_read_string(np, "phy-connection-type", &pm); @@ -32,8 +36,10 @@ int of_get_phy_mode(struct device_node *np) return err; for (i = 0; i < PHY_INTERFACE_MODE_MAX; i++) - if (!strcasecmp(pm, phy_modes(i))) - return i; + if (!strcasecmp(pm, phy_modes(i))) { + *interface = i; + return 0; + } return -ENODEV; } diff --git a/drivers/of/of_private.h b/drivers/of/of_private.h index 24786818e32e..66294d29942a 100644 --- a/drivers/of/of_private.h +++ b/drivers/of/of_private.h @@ -158,4 +158,18 @@ extern void __of_sysfs_remove_bin_file(struct device_node *np, #define for_each_transaction_entry_reverse(_oft, _te) \ list_for_each_entry_reverse(_te, &(_oft)->te_list, node) +extern int of_bus_n_addr_cells(struct device_node *np); +extern int of_bus_n_size_cells(struct device_node *np); + +#ifdef CONFIG_OF_ADDRESS +extern int of_dma_get_range(struct device_node *np, u64 *dma_addr, + u64 *paddr, u64 *size); +#else +static inline int of_dma_get_range(struct device_node *np, u64 *dma_addr, + u64 *paddr, u64 *size) +{ + return -ENODEV; +} +#endif + #endif /* _LINUX_OF_PRIVATE_H */ diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c index 7989703b883c..6bd610ee2cd7 100644 --- a/drivers/of/of_reserved_mem.c +++ b/drivers/of/of_reserved_mem.c @@ -324,8 +324,10 @@ int of_reserved_mem_device_init_by_idx(struct device *dev, if (!target) return -ENODEV; - if (!of_device_is_available(target)) + if (!of_device_is_available(target)) { + of_node_put(target); return 0; + } rmem = __find_rmem(target); of_node_put(target); diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c index c423e94baf0f..9617b7df7c4d 100644 --- a/drivers/of/overlay.c +++ b/drivers/of/overlay.c @@ -305,7 +305,6 @@ static int add_changeset_property(struct overlay_changeset *ovcs, { struct property *new_prop = NULL, *prop; int ret = 0; - bool check_for_non_overlay_node = false; if (target->in_livetree) if (!of_prop_cmp(overlay_prop->name, "name") || @@ -318,6 +317,25 @@ static int add_changeset_property(struct overlay_changeset *ovcs, else prop = NULL; + if (prop) { + if (!of_prop_cmp(prop->name, "#address-cells")) { + if (!of_prop_val_eq(prop, overlay_prop)) { + pr_err("ERROR: changing value of #address-cells is not allowed in %pOF\n", + target->np); + ret = -EINVAL; + } + return ret; + + } else if (!of_prop_cmp(prop->name, "#size-cells")) { + if (!of_prop_val_eq(prop, overlay_prop)) { + pr_err("ERROR: changing value of #size-cells is not allowed in %pOF\n", + target->np); + ret = -EINVAL; + } + return ret; + } + } + if (is_symbols_prop) { if (prop) return -EINVAL; @@ -330,33 +348,18 @@ static int add_changeset_property(struct overlay_changeset *ovcs, return -ENOMEM; if (!prop) { - check_for_non_overlay_node = true; if (!target->in_livetree) { new_prop->next = target->np->deadprops; target->np->deadprops = new_prop; } ret = of_changeset_add_property(&ovcs->cset, target->np, new_prop); - } else if (!of_prop_cmp(prop->name, "#address-cells")) { - if (!of_prop_val_eq(prop, new_prop)) { - pr_err("ERROR: changing value of #address-cells is not allowed in %pOF\n", - target->np); - ret = -EINVAL; - } - } else if (!of_prop_cmp(prop->name, "#size-cells")) { - if (!of_prop_val_eq(prop, new_prop)) { - pr_err("ERROR: changing value of #size-cells is not allowed in %pOF\n", - target->np); - ret = -EINVAL; - } } else { - check_for_non_overlay_node = true; ret = of_changeset_update_property(&ovcs->cset, target->np, new_prop); } - if (check_for_non_overlay_node && - !of_node_check_flag(target->np, OF_OVERLAY)) + if (!of_node_check_flag(target->np, OF_OVERLAY)) pr_err("WARNING: memory leak will occur if overlay removed, property: %pOF/%s\n", target->np, new_prop->name); diff --git a/drivers/of/platform.c b/drivers/of/platform.c index b47a2292fe8e..d93891a05f60 100644 --- a/drivers/of/platform.c +++ b/drivers/of/platform.c @@ -480,6 +480,7 @@ int of_platform_populate(struct device_node *root, pr_debug("%s()\n", __func__); pr_debug(" starting at: %pOF\n", root); + device_links_supplier_sync_state_pause(); for_each_child_of_node(root, child) { rc = of_platform_bus_create(child, matches, lookup, parent, true); if (rc) { @@ -487,6 +488,8 @@ int of_platform_populate(struct device_node *root, break; } } + device_links_supplier_sync_state_resume(); + of_node_set_flag(root, OF_POPULATED_BUS); of_node_put(root); @@ -518,6 +521,7 @@ static int __init of_platform_default_populate_init(void) if (!of_have_populated_dt()) return -ENODEV; + device_links_supplier_sync_state_pause(); /* * Handle certain compatibles explicitly, since we don't want to create * platform_devices for every node in /reserved-memory with a @@ -538,6 +542,14 @@ static int __init of_platform_default_populate_init(void) return 0; } arch_initcall_sync(of_platform_default_populate_init); + +static int __init of_platform_sync_state_init(void) +{ + if (of_have_populated_dt()) + device_links_supplier_sync_state_resume(); + return 0; +} +late_initcall_sync(of_platform_sync_state_init); #endif int of_platform_device_destroy(struct device *dev, void *data) diff --git a/drivers/of/property.c b/drivers/of/property.c index d7fa75e31f22..e851c57a15b0 100644 --- a/drivers/of/property.c +++ b/drivers/of/property.c @@ -25,6 +25,7 @@ #include <linux/of_device.h> #include <linux/of_graph.h> #include <linux/string.h> +#include <linux/moduleparam.h> #include "of_private.h" @@ -164,7 +165,7 @@ EXPORT_SYMBOL_GPL(of_property_read_u64_index); * * @np: device node from which the property value is to be read. * @propname: name of the property to be searched. - * @out_values: pointer to return value, modified only if return value is 0. + * @out_values: pointer to found values. * @sz_min: minimum number of array elements to read * @sz_max: maximum number of array elements to read, if zero there is no * upper limit on the number of elements in the dts entry but only @@ -212,7 +213,7 @@ EXPORT_SYMBOL_GPL(of_property_read_variable_u8_array); * * @np: device node from which the property value is to be read. * @propname: name of the property to be searched. - * @out_values: pointer to return value, modified only if return value is 0. + * @out_values: pointer to found values. * @sz_min: minimum number of array elements to read * @sz_max: maximum number of array elements to read, if zero there is no * upper limit on the number of elements in the dts entry but only @@ -260,7 +261,7 @@ EXPORT_SYMBOL_GPL(of_property_read_variable_u16_array); * * @np: device node from which the property value is to be read. * @propname: name of the property to be searched. - * @out_values: pointer to return value, modified only if return value is 0. + * @out_values: pointer to return found values. * @sz_min: minimum number of array elements to read * @sz_max: maximum number of array elements to read, if zero there is no * upper limit on the number of elements in the dts entry but only @@ -334,7 +335,7 @@ EXPORT_SYMBOL_GPL(of_property_read_u64); * * @np: device node from which the property value is to be read. * @propname: name of the property to be searched. - * @out_values: pointer to return value, modified only if return value is 0. + * @out_values: pointer to found values. * @sz_min: minimum number of array elements to read * @sz_max: maximum number of array elements to read, if zero there is no * upper limit on the number of elements in the dts entry but only @@ -872,6 +873,20 @@ of_fwnode_property_read_string_array(const struct fwnode_handle *fwnode, of_property_count_strings(node, propname); } +static const char *of_fwnode_get_name(const struct fwnode_handle *fwnode) +{ + return kbasename(to_of_node(fwnode)->full_name); +} + +static const char *of_fwnode_get_name_prefix(const struct fwnode_handle *fwnode) +{ + /* Root needs no prefix here (its name is "/"). */ + if (!to_of_node(fwnode)->parent) + return ""; + + return "/"; +} + static struct fwnode_handle * of_fwnode_get_parent(const struct fwnode_handle *fwnode) { @@ -985,6 +1000,320 @@ of_fwnode_device_get_match_data(const struct fwnode_handle *fwnode, return of_device_get_match_data(dev); } +static bool of_is_ancestor_of(struct device_node *test_ancestor, + struct device_node *child) +{ + of_node_get(child); + while (child) { + if (child == test_ancestor) { + of_node_put(child); + return true; + } + child = of_get_next_parent(child); + } + return false; +} + +/** + * of_link_to_phandle - Add device link to supplier from supplier phandle + * @dev: consumer device + * @sup_np: phandle to supplier device tree node + * + * Given a phandle to a supplier device tree node (@sup_np), this function + * finds the device that owns the supplier device tree node and creates a + * device link from @dev consumer device to the supplier device. This function + * doesn't create device links for invalid scenarios such as trying to create a + * link with a parent device as the consumer of its child device. In such + * cases, it returns an error. + * + * Returns: + * - 0 if link successfully created to supplier + * - -EAGAIN if linking to the supplier should be reattempted + * - -EINVAL if the supplier link is invalid and should not be created + * - -ENODEV if there is no device that corresponds to the supplier phandle + */ +static int of_link_to_phandle(struct device *dev, struct device_node *sup_np, + u32 dl_flags) +{ + struct device *sup_dev; + int ret = 0; + struct device_node *tmp_np = sup_np; + int is_populated; + + of_node_get(sup_np); + /* + * Find the device node that contains the supplier phandle. It may be + * @sup_np or it may be an ancestor of @sup_np. + */ + while (sup_np && !of_find_property(sup_np, "compatible", NULL)) + sup_np = of_get_next_parent(sup_np); + if (!sup_np) { + dev_dbg(dev, "Not linking to %pOFP - No device\n", tmp_np); + return -ENODEV; + } + + /* + * Don't allow linking a device node as a consumer of one of its + * descendant nodes. By definition, a child node can't be a functional + * dependency for the parent node. + */ + if (of_is_ancestor_of(dev->of_node, sup_np)) { + dev_dbg(dev, "Not linking to %pOFP - is descendant\n", sup_np); + of_node_put(sup_np); + return -EINVAL; + } + sup_dev = get_dev_from_fwnode(&sup_np->fwnode); + is_populated = of_node_check_flag(sup_np, OF_POPULATED); + of_node_put(sup_np); + if (!sup_dev && is_populated) { + /* Early device without struct device. */ + dev_dbg(dev, "Not linking to %pOFP - No struct device\n", + sup_np); + return -ENODEV; + } else if (!sup_dev) { + return -EAGAIN; + } + if (!device_link_add(dev, sup_dev, dl_flags)) + ret = -EAGAIN; + put_device(sup_dev); + return ret; +} + +/** + * parse_prop_cells - Property parsing function for suppliers + * + * @np: Pointer to device tree node containing a list + * @prop_name: Name of property to be parsed. Expected to hold phandle values + * @index: For properties holding a list of phandles, this is the index + * into the list. + * @list_name: Property name that is known to contain list of phandle(s) to + * supplier(s) + * @cells_name: property name that specifies phandles' arguments count + * + * This is a helper function to parse properties that have a known fixed name + * and are a list of phandles and phandle arguments. + * + * Returns: + * - phandle node pointer with refcount incremented. Caller must of_node_put() + * on it when done. + * - NULL if no phandle found at index + */ +static struct device_node *parse_prop_cells(struct device_node *np, + const char *prop_name, int index, + const char *list_name, + const char *cells_name) +{ + struct of_phandle_args sup_args; + + if (strcmp(prop_name, list_name)) + return NULL; + + if (of_parse_phandle_with_args(np, list_name, cells_name, index, + &sup_args)) + return NULL; + + return sup_args.np; +} + +#define DEFINE_SIMPLE_PROP(fname, name, cells) \ +static struct device_node *parse_##fname(struct device_node *np, \ + const char *prop_name, int index) \ +{ \ + return parse_prop_cells(np, prop_name, index, name, cells); \ +} + +static int strcmp_suffix(const char *str, const char *suffix) +{ + unsigned int len, suffix_len; + + len = strlen(str); + suffix_len = strlen(suffix); + if (len <= suffix_len) + return -1; + return strcmp(str + len - suffix_len, suffix); +} + +/** + * parse_suffix_prop_cells - Suffix property parsing function for suppliers + * + * @np: Pointer to device tree node containing a list + * @prop_name: Name of property to be parsed. Expected to hold phandle values + * @index: For properties holding a list of phandles, this is the index + * into the list. + * @suffix: Property suffix that is known to contain list of phandle(s) to + * supplier(s) + * @cells_name: property name that specifies phandles' arguments count + * + * This is a helper function to parse properties that have a known fixed suffix + * and are a list of phandles and phandle arguments. + * + * Returns: + * - phandle node pointer with refcount incremented. Caller must of_node_put() + * on it when done. + * - NULL if no phandle found at index + */ +static struct device_node *parse_suffix_prop_cells(struct device_node *np, + const char *prop_name, int index, + const char *suffix, + const char *cells_name) +{ + struct of_phandle_args sup_args; + + if (strcmp_suffix(prop_name, suffix)) + return NULL; + + if (of_parse_phandle_with_args(np, prop_name, cells_name, index, + &sup_args)) + return NULL; + + return sup_args.np; +} + +#define DEFINE_SUFFIX_PROP(fname, suffix, cells) \ +static struct device_node *parse_##fname(struct device_node *np, \ + const char *prop_name, int index) \ +{ \ + return parse_suffix_prop_cells(np, prop_name, index, suffix, cells); \ +} + +/** + * struct supplier_bindings - Property parsing functions for suppliers + * + * @parse_prop: function name + * parse_prop() finds the node corresponding to a supplier phandle + * @parse_prop.np: Pointer to device node holding supplier phandle property + * @parse_prop.prop_name: Name of property holding a phandle value + * @parse_prop.index: For properties holding a list of phandles, this is the + * index into the list + * + * Returns: + * parse_prop() return values are + * - phandle node pointer with refcount incremented. Caller must of_node_put() + * on it when done. + * - NULL if no phandle found at index + */ +struct supplier_bindings { + struct device_node *(*parse_prop)(struct device_node *np, + const char *prop_name, int index); +}; + +DEFINE_SIMPLE_PROP(clocks, "clocks", "#clock-cells") +DEFINE_SIMPLE_PROP(interconnects, "interconnects", "#interconnect-cells") +DEFINE_SIMPLE_PROP(iommus, "iommus", "#iommu-cells") +DEFINE_SIMPLE_PROP(mboxes, "mboxes", "#mbox-cells") +DEFINE_SIMPLE_PROP(io_channels, "io-channel", "#io-channel-cells") +DEFINE_SIMPLE_PROP(interrupt_parent, "interrupt-parent", NULL) +DEFINE_SIMPLE_PROP(dmas, "dmas", "#dma-cells") +DEFINE_SUFFIX_PROP(regulators, "-supply", NULL) +DEFINE_SUFFIX_PROP(gpio, "-gpio", "#gpio-cells") +DEFINE_SUFFIX_PROP(gpios, "-gpios", "#gpio-cells") + +static struct device_node *parse_iommu_maps(struct device_node *np, + const char *prop_name, int index) +{ + if (strcmp(prop_name, "iommu-map")) + return NULL; + + return of_parse_phandle(np, prop_name, (index * 4) + 1); +} + +static const struct supplier_bindings of_supplier_bindings[] = { + { .parse_prop = parse_clocks, }, + { .parse_prop = parse_interconnects, }, + { .parse_prop = parse_iommus, }, + { .parse_prop = parse_iommu_maps, }, + { .parse_prop = parse_mboxes, }, + { .parse_prop = parse_io_channels, }, + { .parse_prop = parse_interrupt_parent, }, + { .parse_prop = parse_dmas, }, + { .parse_prop = parse_regulators, }, + { .parse_prop = parse_gpio, }, + { .parse_prop = parse_gpios, }, + {} +}; + +/** + * of_link_property - Create device links to suppliers listed in a property + * @dev: Consumer device + * @con_np: The consumer device tree node which contains the property + * @prop_name: Name of property to be parsed + * + * This function checks if the property @prop_name that is present in the + * @con_np device tree node is one of the known common device tree bindings + * that list phandles to suppliers. If @prop_name isn't one, this function + * doesn't do anything. + * + * If @prop_name is one, this function attempts to create device links from the + * consumer device @dev to all the devices of the suppliers listed in + * @prop_name. + * + * Any failed attempt to create a device link will NOT result in an immediate + * return. of_link_property() must create links to all the available supplier + * devices even when attempts to create a link to one or more suppliers fail. + */ +static int of_link_property(struct device *dev, struct device_node *con_np, + const char *prop_name) +{ + struct device_node *phandle; + const struct supplier_bindings *s = of_supplier_bindings; + unsigned int i = 0; + bool matched = false; + int ret = 0; + u32 dl_flags; + + if (dev->of_node == con_np) + dl_flags = DL_FLAG_AUTOPROBE_CONSUMER; + else + dl_flags = DL_FLAG_SYNC_STATE_ONLY; + + /* Do not stop at first failed link, link all available suppliers. */ + while (!matched && s->parse_prop) { + while ((phandle = s->parse_prop(con_np, prop_name, i))) { + matched = true; + i++; + if (of_link_to_phandle(dev, phandle, dl_flags) + == -EAGAIN) + ret = -EAGAIN; + of_node_put(phandle); + } + s++; + } + return ret; +} + +static int of_link_to_suppliers(struct device *dev, + struct device_node *con_np) +{ + struct device_node *child; + struct property *p; + int ret = 0; + + for_each_property_of_node(con_np, p) + if (of_link_property(dev, con_np, p->name)) + ret = -ENODEV; + + for_each_child_of_node(con_np, child) + if (of_link_to_suppliers(dev, child) && !ret) + ret = -EAGAIN; + + return ret; +} + +static bool of_devlink; +core_param(of_devlink, of_devlink, bool, 0); + +static int of_fwnode_add_links(const struct fwnode_handle *fwnode, + struct device *dev) +{ + if (!of_devlink) + return 0; + + if (unlikely(!is_of_node(fwnode))) + return 0; + + return of_link_to_suppliers(dev, to_of_node(fwnode)); +} + const struct fwnode_operations of_fwnode_ops = { .get = of_fwnode_get, .put = of_fwnode_put, @@ -993,6 +1322,8 @@ const struct fwnode_operations of_fwnode_ops = { .property_present = of_fwnode_property_present, .property_read_int_array = of_fwnode_property_read_int_array, .property_read_string_array = of_fwnode_property_read_string_array, + .get_name = of_fwnode_get_name, + .get_name_prefix = of_fwnode_get_name_prefix, .get_parent = of_fwnode_get_parent, .get_next_child_node = of_fwnode_get_next_child_node, .get_named_child_node = of_fwnode_get_named_child_node, @@ -1001,5 +1332,6 @@ const struct fwnode_operations of_fwnode_ops = { .graph_get_remote_endpoint = of_fwnode_graph_get_remote_endpoint, .graph_get_port_parent = of_fwnode_graph_get_port_parent, .graph_parse_endpoint = of_fwnode_graph_parse_endpoint, + .add_links = of_fwnode_add_links, }; EXPORT_SYMBOL_GPL(of_fwnode_ops); diff --git a/drivers/of/unittest-data/testcases.dts b/drivers/of/unittest-data/testcases.dts index 55fe0ee20109..a85b5e1c381a 100644 --- a/drivers/of/unittest-data/testcases.dts +++ b/drivers/of/unittest-data/testcases.dts @@ -15,5 +15,6 @@ #include "tests-phandle.dtsi" #include "tests-interrupts.dtsi" #include "tests-match.dtsi" +#include "tests-address.dtsi" #include "tests-platform.dtsi" #include "tests-overlay.dtsi" diff --git a/drivers/of/unittest-data/tests-address.dtsi b/drivers/of/unittest-data/tests-address.dtsi new file mode 100644 index 000000000000..3fe5d3987beb --- /dev/null +++ b/drivers/of/unittest-data/tests-address.dtsi @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: GPL-2.0 + +/ { + #address-cells = <1>; + #size-cells = <1>; + + testcase-data { + #address-cells = <1>; + #size-cells = <1>; + ranges; + + address-tests { + #address-cells = <1>; + #size-cells = <1>; + /* ranges here is to make sure we don't use it for + * dma-ranges translation */ + ranges = <0x70000000 0x70000000 0x40000000>, + <0x00000000 0xd0000000 0x20000000>; + dma-ranges = <0x0 0x20000000 0x40000000>; + + device@70000000 { + reg = <0x70000000 0x1000>; + }; + + bus@80000000 { + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0x80000000 0x100000>; + dma-ranges = <0x10000000 0x0 0x40000000>; + + device@1000 { + reg = <0x1000 0x1000>; + }; + }; + + pci@90000000 { + device_type = "pci"; + #address-cells = <3>; + #size-cells = <2>; + reg = <0x90000000 0x1000>; + ranges = <0x42000000 0x0 0x40000000 0x40000000 0x0 0x10000000>; + dma-ranges = <0x42000000 0x0 0x80000000 0x00000000 0x0 0x10000000>, + <0x42000000 0x0 0xc0000000 0x20000000 0x0 0x10000000>; + }; + + }; + }; +}; diff --git a/drivers/of/unittest.c b/drivers/of/unittest.c index 480a21e2ed39..68b87587b2ef 100644 --- a/drivers/of/unittest.c +++ b/drivers/of/unittest.c @@ -12,6 +12,7 @@ #include <linux/hashtable.h> #include <linux/libfdt.h> #include <linux/of.h> +#include <linux/of_address.h> #include <linux/of_fdt.h> #include <linux/of_irq.h> #include <linux/of_platform.h> @@ -779,6 +780,95 @@ static void __init of_unittest_changeset(void) #endif } +static void __init of_unittest_dma_ranges_one(const char *path, + u64 expect_dma_addr, u64 expect_paddr, u64 expect_size) +{ + struct device_node *np; + u64 dma_addr, paddr, size; + int rc; + + np = of_find_node_by_path(path); + if (!np) { + pr_err("missing testcase data\n"); + return; + } + + rc = of_dma_get_range(np, &dma_addr, &paddr, &size); + + unittest(!rc, "of_dma_get_range failed on node %pOF rc=%i\n", np, rc); + if (!rc) { + unittest(size == expect_size, + "of_dma_get_range wrong size on node %pOF size=%llx\n", np, size); + unittest(paddr == expect_paddr, + "of_dma_get_range wrong phys addr (%llx) on node %pOF", paddr, np); + unittest(dma_addr == expect_dma_addr, + "of_dma_get_range wrong DMA addr (%llx) on node %pOF", dma_addr, np); + } + of_node_put(np); +} + +static void __init of_unittest_parse_dma_ranges(void) +{ + of_unittest_dma_ranges_one("/testcase-data/address-tests/device@70000000", + 0x0, 0x20000000, 0x40000000); + of_unittest_dma_ranges_one("/testcase-data/address-tests/bus@80000000/device@1000", + 0x10000000, 0x20000000, 0x40000000); + of_unittest_dma_ranges_one("/testcase-data/address-tests/pci@90000000", + 0x80000000, 0x20000000, 0x10000000); +} + +static void __init of_unittest_pci_dma_ranges(void) +{ + struct device_node *np; + struct of_pci_range range; + struct of_pci_range_parser parser; + int i = 0; + + if (!IS_ENABLED(CONFIG_PCI)) + return; + + np = of_find_node_by_path("/testcase-data/address-tests/pci@90000000"); + if (!np) { + pr_err("missing testcase data\n"); + return; + } + + if (of_pci_dma_range_parser_init(&parser, np)) { + pr_err("missing dma-ranges property\n"); + return; + } + + /* + * Get the dma-ranges from the device tree + */ + for_each_of_pci_range(&parser, &range) { + if (!i) { + unittest(range.size == 0x10000000, + "for_each_of_pci_range wrong size on node %pOF size=%llx\n", + np, range.size); + unittest(range.cpu_addr == 0x20000000, + "for_each_of_pci_range wrong CPU addr (%llx) on node %pOF", + range.cpu_addr, np); + unittest(range.pci_addr == 0x80000000, + "for_each_of_pci_range wrong DMA addr (%llx) on node %pOF", + range.pci_addr, np); + } else { + unittest(range.size == 0x10000000, + "for_each_of_pci_range wrong size on node %pOF size=%llx\n", + np, range.size); + unittest(range.cpu_addr == 0x40000000, + "for_each_of_pci_range wrong CPU addr (%llx) on node %pOF", + range.cpu_addr, np); + unittest(range.pci_addr == 0xc0000000, + "for_each_of_pci_range wrong DMA addr (%llx) on node %pOF", + range.pci_addr, np); + } + i++; + } + + of_node_put(np); +} + static void __init of_unittest_parse_interrupts(void) { struct device_node *np; @@ -1146,8 +1236,10 @@ static void attach_node_and_children(struct device_node *np) full_name = kasprintf(GFP_KERNEL, "%pOF", np); if (!strcmp(full_name, "/__local_fixups__") || - !strcmp(full_name, "/__fixups__")) + !strcmp(full_name, "/__fixups__")) { + kfree(full_name); return; + } dup = of_find_node_by_path(full_name); kfree(full_name); @@ -1207,6 +1299,7 @@ static int __init unittest_data_add(void) of_fdt_unflatten_tree(unittest_data, NULL, &unittest_data_node); if (!unittest_data_node) { pr_warn("%s: No tree to attach; not running tests\n", __func__); + kfree(unittest_data); return -ENODATA; } @@ -2554,6 +2647,8 @@ static int __init of_unittest(void) of_unittest_changeset(); of_unittest_parse_interrupts(); of_unittest_parse_interrupts_extended(); + of_unittest_parse_dma_ranges(); + of_unittest_pci_dma_ranges(); of_unittest_match_node(); of_unittest_platform_populate(); of_unittest_overlay(); |