diff options
Diffstat (limited to 'drivers/of/property.c')
-rw-r--r-- | drivers/of/property.c | 291 |
1 files changed, 221 insertions, 70 deletions
diff --git a/drivers/of/property.c b/drivers/of/property.c index fa8cd33be131..208d922cc24c 100644 --- a/drivers/of/property.c +++ b/drivers/of/property.c @@ -32,6 +32,32 @@ #include "of_private.h" /** + * of_property_read_bool - Find a property + * @np: device node from which the property value is to be read. + * @propname: name of the property to be searched. + * + * Search for a boolean property in a device node. Usage on non-boolean + * property types is deprecated. + * + * Return: true if the property exists false otherwise. + */ +bool of_property_read_bool(const struct device_node *np, const char *propname) +{ + struct property *prop = of_find_property(np, propname, NULL); + + /* + * Boolean properties should not have a value. Testing for property + * presence should either use of_property_present() or just read the + * property value and check the returned error code. + */ + if (prop && prop->length) + pr_warn("%pOF: Read of boolean property '%s' with a value.\n", np, propname); + + return prop ? true : false; +} +EXPORT_SYMBOL(of_property_read_bool); + +/** * of_graph_is_present() - check graph's presence * @node: pointer to device_node containing graph port * @@ -40,15 +66,12 @@ */ bool of_graph_is_present(const struct device_node *node) { - struct device_node *ports, *port; + struct device_node *ports __free(device_node) = of_get_child_by_name(node, "ports"); - ports = of_get_child_by_name(node, "ports"); if (ports) node = ports; - port = of_get_child_by_name(node, "port"); - of_node_put(ports); - of_node_put(port); + struct device_node *port __free(device_node) = of_get_child_by_name(node, "port"); return !!port; } @@ -71,7 +94,7 @@ EXPORT_SYMBOL(of_graph_is_present); int of_property_count_elems_of_size(const struct device_node *np, const char *propname, int elem_size) { - struct property *prop = of_find_property(np, propname, NULL); + const struct property *prop = of_find_property(np, propname, NULL); if (!prop) return -EINVAL; @@ -107,7 +130,7 @@ EXPORT_SYMBOL_GPL(of_property_count_elems_of_size); static void *of_find_property_value_of_size(const struct device_node *np, const char *propname, u32 min, u32 max, size_t *len) { - struct property *prop = of_find_property(np, propname, NULL); + const struct property *prop = of_find_property(np, propname, NULL); if (!prop) return ERR_PTR(-EINVAL); @@ -455,12 +478,17 @@ EXPORT_SYMBOL_GPL(of_property_read_string); /** * of_property_match_string() - Find string in a list and return index - * @np: pointer to node containing string list property + * @np: pointer to the node containing the string list property * @propname: string list property name - * @string: pointer to string to search for in string list + * @string: pointer to the string to search for in the string list * - * This function searches a string list property and returns the index - * of a specific string value. + * Search for an exact match of string in a device node property which is a + * string of lists. + * + * Return: the index of the first occurrence of the string on success, -EINVAL + * if the property does not exist, -ENODATA if the property does not have a + * value, and -EILSEQ if the string is not null-terminated within the length of + * the property data. */ int of_property_match_string(const struct device_node *np, const char *propname, const char *string) @@ -528,7 +556,7 @@ int of_property_read_string_helper(const struct device_node *np, } EXPORT_SYMBOL_GPL(of_property_read_string_helper); -const __be32 *of_prop_next_u32(struct property *prop, const __be32 *cur, +const __be32 *of_prop_next_u32(const struct property *prop, const __be32 *cur, u32 *pu) { const void *curv = cur; @@ -551,7 +579,7 @@ out_val: } EXPORT_SYMBOL_GPL(of_prop_next_u32); -const char *of_prop_next_string(struct property *prop, const char *cur) +const char *of_prop_next_string(const struct property *prop, const char *cur) { const void *curv = cur; @@ -579,7 +607,8 @@ EXPORT_SYMBOL_GPL(of_prop_next_string); int of_graph_parse_endpoint(const struct device_node *node, struct of_endpoint *endpoint) { - struct device_node *port_node = of_get_parent(node); + struct device_node *port_node __free(device_node) = + of_get_parent(node); WARN_ONCE(!port_node, "%s(): endpoint %pOF has no parent node\n", __func__, node); @@ -594,8 +623,6 @@ int of_graph_parse_endpoint(const struct device_node *node, of_property_read_u32(port_node, "reg", &endpoint->port); of_property_read_u32(node, "reg", &endpoint->id); - of_node_put(port_node); - return 0; } EXPORT_SYMBOL(of_graph_parse_endpoint); @@ -610,29 +637,90 @@ EXPORT_SYMBOL(of_graph_parse_endpoint); */ struct device_node *of_graph_get_port_by_id(struct device_node *parent, u32 id) { - struct device_node *node, *port; + struct device_node *node __free(device_node) = of_get_child_by_name(parent, "ports"); - node = of_get_child_by_name(parent, "ports"); if (node) parent = node; - for_each_child_of_node(parent, port) { + for_each_child_of_node_scoped(parent, port) { u32 port_id = 0; if (!of_node_name_eq(port, "port")) continue; of_property_read_u32(port, "reg", &port_id); if (id == port_id) - break; + return_ptr(port); } - of_node_put(node); - - return port; + return NULL; } EXPORT_SYMBOL(of_graph_get_port_by_id); /** + * of_graph_get_next_port() - get next port node. + * @parent: pointer to the parent device node, or parent ports node + * @prev: previous port node, or NULL to get first + * + * Parent device node can be used as @parent whether device node has ports node + * or not. It will work same as ports@0 node. + * + * Return: A 'port' node pointer with refcount incremented. Refcount + * of the passed @prev node is decremented. + */ +struct device_node *of_graph_get_next_port(const struct device_node *parent, + struct device_node *prev) +{ + if (!parent) + return NULL; + + if (!prev) { + struct device_node *node __free(device_node) = + of_get_child_by_name(parent, "ports"); + + if (node) + parent = node; + + return of_get_child_by_name(parent, "port"); + } + + do { + prev = of_get_next_child(parent, prev); + if (!prev) + break; + } while (!of_node_name_eq(prev, "port")); + + return prev; +} +EXPORT_SYMBOL(of_graph_get_next_port); + +/** + * of_graph_get_next_port_endpoint() - get next endpoint node in port. + * If it reached to end of the port, it will return NULL. + * @port: pointer to the target port node + * @prev: previous endpoint node, or NULL to get first + * + * Return: An 'endpoint' node pointer with refcount incremented. Refcount + * of the passed @prev node is decremented. + */ +struct device_node *of_graph_get_next_port_endpoint(const struct device_node *port, + struct device_node *prev) +{ + while (1) { + prev = of_get_next_child(port, prev); + if (!prev) + break; + if (WARN(!of_node_name_eq(prev, "endpoint"), + "non endpoint node is used (%pOF)", prev)) + continue; + + break; + } + + return prev; +} +EXPORT_SYMBOL(of_graph_get_next_port_endpoint); + +/** * of_graph_get_next_endpoint() - get next endpoint node * @parent: pointer to the parent device node * @prev: previous endpoint node, or NULL to get first @@ -655,17 +743,9 @@ struct device_node *of_graph_get_next_endpoint(const struct device_node *parent, * parent port node. */ if (!prev) { - struct device_node *node; - - node = of_get_child_by_name(parent, "ports"); - if (node) - parent = node; - - port = of_get_child_by_name(parent, "port"); - of_node_put(node); - + port = of_graph_get_next_port(parent, NULL); if (!port) { - pr_err("graph: no port node found in %pOF\n", parent); + pr_debug("graph: no port node found in %pOF\n", parent); return NULL; } } else { @@ -681,7 +761,7 @@ struct device_node *of_graph_get_next_endpoint(const struct device_node *parent, * getting the next child. If the previous endpoint is NULL this * will return the first child. */ - endpoint = of_get_next_child(port, prev); + endpoint = of_graph_get_next_port_endpoint(port, prev); if (endpoint) { of_node_put(port); return endpoint; @@ -690,11 +770,9 @@ struct device_node *of_graph_get_next_endpoint(const struct device_node *parent, /* No more endpoints under this port, try the next one. */ prev = NULL; - do { - port = of_get_next_child(parent, port); - if (!port) - return NULL; - } while (!of_node_name_eq(port, "port")); + port = of_graph_get_next_port(parent, port); + if (!port) + return NULL; } } EXPORT_SYMBOL(of_graph_get_next_endpoint); @@ -782,16 +860,11 @@ EXPORT_SYMBOL(of_graph_get_port_parent); struct device_node *of_graph_get_remote_port_parent( const struct device_node *node) { - struct device_node *np, *pp; - /* Get remote endpoint node. */ - np = of_graph_get_remote_endpoint(node); + struct device_node *np __free(device_node) = + of_graph_get_remote_endpoint(node); - pp = of_graph_get_port_parent(np); - - of_node_put(np); - - return pp; + return of_graph_get_port_parent(np); } EXPORT_SYMBOL(of_graph_get_remote_port_parent); @@ -814,10 +887,16 @@ struct device_node *of_graph_get_remote_port(const struct device_node *node) } EXPORT_SYMBOL(of_graph_get_remote_port); -int of_graph_get_endpoint_count(const struct device_node *np) +/** + * of_graph_get_endpoint_count() - get the number of endpoints in a device node + * @np: parent device node containing ports and endpoints + * + * Return: count of endpoint of this device node + */ +unsigned int of_graph_get_endpoint_count(const struct device_node *np) { struct device_node *endpoint; - int num = 0; + unsigned int num = 0; for_each_endpoint_of_node(np, endpoint) num++; @@ -827,6 +906,23 @@ int of_graph_get_endpoint_count(const struct device_node *np) EXPORT_SYMBOL(of_graph_get_endpoint_count); /** + * of_graph_get_port_count() - get the number of port in a device or ports node + * @np: pointer to the device or ports node + * + * Return: count of port of this device or ports node + */ +unsigned int of_graph_get_port_count(struct device_node *np) +{ + unsigned int num = 0; + + for_each_of_graph_port(np, port) + num++; + + return num; +} +EXPORT_SYMBOL(of_graph_get_port_count); + +/** * of_graph_get_remote_node() - get remote parent device_node for given port/endpoint * @node: pointer to parent device_node containing graph port/endpoint * @port: identifier (value of reg property) of the parent port node @@ -896,6 +992,12 @@ of_fwnode_device_get_dma_attr(const struct fwnode_handle *fwnode) static bool of_fwnode_property_present(const struct fwnode_handle *fwnode, const char *propname) { + return of_property_present(to_of_node(fwnode), propname); +} + +static bool of_fwnode_property_read_bool(const struct fwnode_handle *fwnode, + const char *propname) +{ return of_property_read_bool(to_of_node(fwnode), propname); } @@ -1046,15 +1148,13 @@ static int of_fwnode_graph_parse_endpoint(const struct fwnode_handle *fwnode, struct fwnode_endpoint *endpoint) { const struct device_node *node = to_of_node(fwnode); - struct device_node *port_node = of_get_parent(node); + struct device_node *port_node __free(device_node) = of_get_parent(node); endpoint->local_fwnode = fwnode; of_property_read_u32(port_node, "reg", &endpoint->port); of_property_read_u32(node, "reg", &endpoint->id); - of_node_put(port_node); - return 0; } @@ -1066,26 +1166,23 @@ of_fwnode_device_get_match_data(const struct fwnode_handle *fwnode, } static void of_link_to_phandle(struct device_node *con_np, - struct device_node *sup_np) + struct device_node *sup_np, + u8 flags) { - struct device_node *tmp_np = of_node_get(sup_np); + struct device_node *tmp_np __free(device_node) = of_node_get(sup_np); /* Check that sup_np and its ancestors are available. */ while (tmp_np) { - if (of_fwnode_handle(tmp_np)->dev) { - of_node_put(tmp_np); + if (of_fwnode_handle(tmp_np)->dev) break; - } - if (!of_device_is_available(tmp_np)) { - of_node_put(tmp_np); + if (!of_device_is_available(tmp_np)) return; - } tmp_np = of_get_next_parent(tmp_np); } - fwnode_link_add(of_fwnode_handle(con_np), of_fwnode_handle(sup_np)); + fwnode_link_add(of_fwnode_handle(con_np), of_fwnode_handle(sup_np), flags); } /** @@ -1198,6 +1295,8 @@ static struct device_node *parse_##fname(struct device_node *np, \ * to a struct device, implement this ops so fw_devlink can use it * to find the true consumer. * @optional: Describes whether a supplier is mandatory or not + * @fwlink_flags: Optional fwnode link flags to use when creating a fwnode link + * for this property. * * Returns: * parse_prop() return values are @@ -1210,6 +1309,7 @@ struct supplier_bindings { const char *prop_name, int index); struct device_node *(*get_con_dev)(struct device_node *np); bool optional; + u8 fwlink_flags; }; DEFINE_SIMPLE_PROP(clocks, "clocks", "#clock-cells") @@ -1217,7 +1317,7 @@ 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-channels", "#io-channel-cells") -DEFINE_SIMPLE_PROP(interrupt_parent, "interrupt-parent", NULL) +DEFINE_SIMPLE_PROP(io_backends, "io-backends", "#io-backend-cells") DEFINE_SIMPLE_PROP(dmas, "dmas", "#dma-cells") DEFINE_SIMPLE_PROP(power_domains, "power-domains", "#power-domain-cells") DEFINE_SIMPLE_PROP(hwlocks, "hwlocks", "#hwlock-cells") @@ -1240,6 +1340,10 @@ DEFINE_SIMPLE_PROP(leds, "leds", NULL) DEFINE_SIMPLE_PROP(backlight, "backlight", NULL) DEFINE_SIMPLE_PROP(panel, "panel", NULL) DEFINE_SIMPLE_PROP(msi_parent, "msi-parent", "#msi-cells") +DEFINE_SIMPLE_PROP(post_init_providers, "post-init-providers", NULL) +DEFINE_SIMPLE_PROP(access_controllers, "access-controllers", "#access-controller-cells") +DEFINE_SIMPLE_PROP(pses, "pses", "#pse-cells") +DEFINE_SIMPLE_PROP(power_supplies, "power-supplies", NULL) DEFINE_SUFFIX_PROP(regulators, "-supply", NULL) DEFINE_SUFFIX_PROP(gpio, "-gpio", "#gpio-cells") @@ -1299,6 +1403,47 @@ static struct device_node *parse_interrupts(struct device_node *np, return of_irq_parse_one(np, index, &sup_args) ? NULL : sup_args.np; } +static struct device_node *parse_interrupt_map(struct device_node *np, + const char *prop_name, int index) +{ + const __be32 *imap, *imap_end; + struct of_phandle_args sup_args; + u32 addrcells, intcells; + int imaplen; + + if (!IS_ENABLED(CONFIG_OF_IRQ)) + return NULL; + + if (strcmp(prop_name, "interrupt-map")) + return NULL; + + if (of_property_read_u32(np, "#interrupt-cells", &intcells)) + return NULL; + addrcells = of_bus_n_addr_cells(np); + + imap = of_get_property(np, "interrupt-map", &imaplen); + if (!imap) + return NULL; + imaplen /= sizeof(*imap); + + imap_end = imap + imaplen; + + for (int i = 0; imap + addrcells + intcells + 1 < imap_end; i++) { + imap += addrcells + intcells; + + imap = of_irq_parse_imap_parent(imap, imap_end - imap, &sup_args); + if (!imap) + return NULL; + + if (i == index) + return sup_args.np; + + of_node_put(sup_args.np); + } + + return NULL; +} + static struct device_node *parse_remote_endpoint(struct device_node *np, const char *prop_name, int index) @@ -1317,7 +1462,7 @@ static const struct supplier_bindings of_supplier_bindings[] = { { .parse_prop = parse_iommu_maps, .optional = true, }, { .parse_prop = parse_mboxes, }, { .parse_prop = parse_io_channels, }, - { .parse_prop = parse_interrupt_parent, }, + { .parse_prop = parse_io_backends, }, { .parse_prop = parse_dmas, .optional = true, }, { .parse_prop = parse_power_domains, }, { .parse_prop = parse_hwlocks, }, @@ -1344,11 +1489,19 @@ static const struct supplier_bindings of_supplier_bindings[] = { { .parse_prop = parse_backlight, }, { .parse_prop = parse_panel, }, { .parse_prop = parse_msi_parent, }, + { .parse_prop = parse_pses, }, + { .parse_prop = parse_power_supplies, }, { .parse_prop = parse_gpio_compat, }, { .parse_prop = parse_interrupts, }, + { .parse_prop = parse_interrupt_map, }, + { .parse_prop = parse_access_controllers, }, { .parse_prop = parse_regulators, }, { .parse_prop = parse_gpio, }, { .parse_prop = parse_gpios, }, + { + .parse_prop = parse_post_init_providers, + .fwlink_flags = FWLINK_FLAG_IGNORE, + }, {} }; @@ -1386,16 +1539,13 @@ static int of_link_property(struct device_node *con_np, const char *prop_name) } while ((phandle = s->parse_prop(con_np, prop_name, i))) { - struct device_node *con_dev_np; + struct device_node *con_dev_np __free(device_node) = + s->get_con_dev ? s->get_con_dev(con_np) : of_node_get(con_np); - con_dev_np = s->get_con_dev - ? s->get_con_dev(con_np) - : of_node_get(con_np); matched = true; i++; - of_link_to_phandle(con_dev_np, phandle); + of_link_to_phandle(con_dev_np, phandle, s->fwlink_flags); of_node_put(phandle); - of_node_put(con_dev_np); } s++; } @@ -1419,7 +1569,7 @@ static int of_fwnode_irq_get(const struct fwnode_handle *fwnode, static int of_fwnode_add_links(struct fwnode_handle *fwnode) { - struct property *p; + const struct property *p; struct device_node *con_np = to_of_node(fwnode); if (IS_ENABLED(CONFIG_X86)) @@ -1442,6 +1592,7 @@ const struct fwnode_operations of_fwnode_ops = { .device_dma_supported = of_fwnode_device_dma_supported, .device_get_dma_attr = of_fwnode_device_get_dma_attr, .property_present = of_fwnode_property_present, + .property_read_bool = of_fwnode_property_read_bool, .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, |