From 372a67c0c5ef63f55bd1eb480d9555328d8ec0f2 Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Wed, 4 Sep 2019 14:11:20 -0700 Subject: driver core: Add fwnode_to_dev() to look up device from fwnode It's often useful to look up a device that corresponds to a fwnode. So add an API to do that irrespective of the bus on which the device has been added to. Signed-off-by: Saravana Kannan Link: https://lore.kernel.org/r/20190904211126.47518-2-saravanak@google.com Signed-off-by: Greg Kroah-Hartman --- include/linux/fwnode.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include/linux/fwnode.h') diff --git a/include/linux/fwnode.h b/include/linux/fwnode.h index ababd6bc82f3..d8c6d231d577 100644 --- a/include/linux/fwnode.h +++ b/include/linux/fwnode.h @@ -17,6 +17,7 @@ struct device; struct fwnode_handle { struct fwnode_handle *secondary; const struct fwnode_operations *ops; + struct device *dev; }; /** @@ -123,5 +124,6 @@ struct fwnode_operations { if (fwnode_has_op(fwnode, op)) \ (fwnode)->ops->op(fwnode, ## __VA_ARGS__); \ } while (false) +#define get_dev_from_fwnode(fwnode) get_device((fwnode)->dev) #endif -- cgit v1.2.3 From e2ae9bcc4aaacda04edb75c4eea93384719efaa5 Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Wed, 4 Sep 2019 14:11:21 -0700 Subject: driver core: Add support for linking devices during device addition The firmware corresponding to a device (dev.fwnode) might be able to provide functional dependency information between a device and its supplier and consumer devices. Tracking this functional dependency allows optimizing device probe order and informing a supplier when all its consumers have probed (and thereby actively managing their resources). The existing device links feature allows tracking and using supplier-consumer relationships. So, this patch adds the add_links() fwnode callback to allow firmware to create device links for each device as the device is added. However, when consumer devices are added, they might not have a supplier device to link to despite needing mandatory resources/functionality from one or more suppliers. A waiting_for_suppliers list is created to track such consumers and retry linking them when new devices get added. Signed-off-by: Saravana Kannan Link: https://lore.kernel.org/r/20190904211126.47518-3-saravanak@google.com Signed-off-by: Greg Kroah-Hartman --- Documentation/driver-api/device_link.rst | 3 +- drivers/base/core.c | 88 ++++++++++++++++++++++++++++++++ include/linux/device.h | 2 + include/linux/fwnode.h | 17 ++++++ 4 files changed, 109 insertions(+), 1 deletion(-) (limited to 'include/linux/fwnode.h') diff --git a/Documentation/driver-api/device_link.rst b/Documentation/driver-api/device_link.rst index 1b5020ec6517..bc2d89af88ce 100644 --- a/Documentation/driver-api/device_link.rst +++ b/Documentation/driver-api/device_link.rst @@ -281,7 +281,8 @@ State machine :c:func:`driver_bound()`.) * Before a consumer device is probed, presence of supplier drivers is - verified by checking that links to suppliers are in ``DL_STATE_AVAILABLE`` + verified by checking the consumer device is not in the wait_for_suppliers + list and by checking that links to suppliers are in ``DL_STATE_AVAILABLE`` state. The state of the links is updated to ``DL_STATE_CONSUMER_PROBE``. (Call to :c:func:`device_links_check_suppliers()` from :c:func:`really_probe()`.) diff --git a/drivers/base/core.c b/drivers/base/core.c index 510fabf8918c..b3896da73b3d 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -44,6 +44,8 @@ early_param("sysfs.deprecated", sysfs_deprecated_setup); #endif /* Device links support. */ +static LIST_HEAD(wait_for_suppliers); +static DEFINE_MUTEX(wfs_lock); #ifdef CONFIG_SRCU static DEFINE_MUTEX(device_links_lock); @@ -430,6 +432,58 @@ struct device_link *device_link_add(struct device *consumer, } EXPORT_SYMBOL_GPL(device_link_add); +/** + * device_link_wait_for_supplier - Add device to wait_for_suppliers list + * @consumer: Consumer device + * + * Marks the @consumer device as waiting for suppliers to become available by + * adding it to the wait_for_suppliers list. The consumer device will never be + * probed until it's removed from the wait_for_suppliers list. + * + * The caller is responsible for adding the links to the supplier devices once + * they are available and removing the @consumer device from the + * wait_for_suppliers list once links to all the suppliers have been created. + * + * This function is NOT meant to be called from the probe function of the + * consumer but rather from code that creates/adds the consumer device. + */ +static void device_link_wait_for_supplier(struct device *consumer) +{ + mutex_lock(&wfs_lock); + list_add_tail(&consumer->links.needs_suppliers, &wait_for_suppliers); + mutex_unlock(&wfs_lock); +} + +/** + * device_link_add_missing_supplier_links - Add links from consumer devices to + * supplier devices, leaving any + * consumer with inactive suppliers on + * the wait_for_suppliers list + * + * Loops through all consumers waiting on suppliers and tries to add all their + * supplier links. If that succeeds, the consumer device is removed from + * wait_for_suppliers list. Otherwise, they are left in the wait_for_suppliers + * list. Devices left on the wait_for_suppliers list will not be probed. + * + * The fwnode add_links callback is expected to return 0 if it has found and + * added all the supplier links for the consumer device. It should return an + * error if it isn't able to do so. + * + * The caller of device_link_wait_for_supplier() is expected to call this once + * it's aware of potential suppliers becoming available. + */ +static void device_link_add_missing_supplier_links(void) +{ + struct device *dev, *tmp; + + mutex_lock(&wfs_lock); + list_for_each_entry_safe(dev, tmp, &wait_for_suppliers, + links.needs_suppliers) + if (!fwnode_call_int_op(dev->fwnode, add_links, dev)) + list_del_init(&dev->links.needs_suppliers); + mutex_unlock(&wfs_lock); +} + static void device_link_free(struct device_link *link) { while (refcount_dec_not_one(&link->rpm_active)) @@ -564,6 +618,17 @@ int device_links_check_suppliers(struct device *dev) struct device_link *link; int ret = 0; + /* + * Device waiting for supplier to become available is not allowed to + * probe. + */ + mutex_lock(&wfs_lock); + if (!list_empty(&dev->links.needs_suppliers)) { + mutex_unlock(&wfs_lock); + return -EPROBE_DEFER; + } + mutex_unlock(&wfs_lock); + device_links_write_lock(); list_for_each_entry(link, &dev->links.suppliers, c_node) { @@ -848,6 +913,10 @@ static void device_links_purge(struct device *dev) { struct device_link *link, *ln; + mutex_lock(&wfs_lock); + list_del(&dev->links.needs_suppliers); + mutex_unlock(&wfs_lock); + /* * Delete all of the remaining links from this device to any other * devices (either consumers or suppliers). @@ -1712,6 +1781,7 @@ void device_initialize(struct device *dev) #endif INIT_LIST_HEAD(&dev->links.consumers); INIT_LIST_HEAD(&dev->links.suppliers); + INIT_LIST_HEAD(&dev->links.needs_suppliers); dev->links.status = DL_DEV_NO_DRIVER; } EXPORT_SYMBOL_GPL(device_initialize); @@ -2202,6 +2272,24 @@ int device_add(struct device *dev) if (dev->fwnode && !dev->fwnode->dev) dev->fwnode->dev = dev; + /* + * Check if any of the other devices (consumers) have been waiting for + * this device (supplier) to be added so that they can create a device + * link to it. + * + * This needs to happen after device_pm_add() because device_link_add() + * requires the supplier be registered before it's called. + * + * But this also needs to happe before bus_probe_device() to make sure + * waiting consumers can link to it before the driver is bound to the + * device and the driver sync_state callback is called for this device. + */ + device_link_add_missing_supplier_links(); + + if (fwnode_has_op(dev->fwnode, add_links) + && fwnode_call_int_op(dev->fwnode, add_links, dev)) + device_link_wait_for_supplier(dev); + bus_probe_device(dev); if (parent) klist_add_tail(&dev->p->knode_parent, diff --git a/include/linux/device.h b/include/linux/device.h index 297239a08bb7..c6fb5b3431b7 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -1135,11 +1135,13 @@ enum dl_dev_state { * struct dev_links_info - Device data related to device links. * @suppliers: List of links to supplier devices. * @consumers: List of links to consumer devices. + * @needs_suppliers: Hook to global list of devices waiting for suppliers. * @status: Driver status information. */ struct dev_links_info { struct list_head suppliers; struct list_head consumers; + struct list_head needs_suppliers; enum dl_dev_state status; }; diff --git a/include/linux/fwnode.h b/include/linux/fwnode.h index d8c6d231d577..6ae05b9ce359 100644 --- a/include/linux/fwnode.h +++ b/include/linux/fwnode.h @@ -66,6 +66,21 @@ struct fwnode_reference_args { * endpoint node. * @graph_get_port_parent: Return the parent node of a port node. * @graph_parse_endpoint: Parse endpoint for port and endpoint id. + * @add_links: Called after the device corresponding to the fwnode is added + * using device_add(). The function is expected to create device + * links to all the suppliers of the device that are available at + * the time this function is called. The function must NOT stop + * at the first failed device link if other unlinked supplier + * devices are present in the system. If some suppliers are not + * yet available, this function will be called again when other + * devices are added to allow creating device links to any newly + * available suppliers. + * + * Return 0 if device links have been successfully created to all + * the suppliers of this device or if the supplier information is + * not known. Return an error if and only if the supplier + * information is known but some of the suppliers are not yet + * available to create device links to. */ struct fwnode_operations { struct fwnode_handle *(*get)(struct fwnode_handle *fwnode); @@ -103,6 +118,8 @@ struct fwnode_operations { (*graph_get_port_parent)(struct fwnode_handle *fwnode); int (*graph_parse_endpoint)(const struct fwnode_handle *fwnode, struct fwnode_endpoint *endpoint); + int (*add_links)(const struct fwnode_handle *fwnode, + struct device *dev); }; #define fwnode_has_op(fwnode, op) \ -- cgit v1.2.3 From bc0500c1e43d95cca5352d2345fb0769f314ba22 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 3 Oct 2019 15:32:12 +0300 Subject: device property: Add fwnode_get_name for returning the name of a node The fwnode framework did not have means to obtain the name of a node. Add that now, in form of the fwnode_get_name() function and a corresponding get_name fwnode op. OF and ACPI support is included. Signed-off-by: Sakari Ailus Acked-by: Rob Herring (for OF) Reviewed-by: Andy Shevchenko Signed-off-by: Rafael J. Wysocki --- drivers/acpi/property.c | 26 ++++++++++++++++++++++++++ drivers/base/property.c | 11 +++++++++++ drivers/base/swnode.c | 12 ++++++++++++ drivers/of/property.c | 6 ++++++ include/linux/fwnode.h | 2 ++ include/linux/property.h | 1 + 6 files changed, 58 insertions(+) (limited to 'include/linux/fwnode.h') diff --git a/drivers/acpi/property.c b/drivers/acpi/property.c index 3eacf474e1e3..a501c1c584b5 100644 --- a/drivers/acpi/property.c +++ b/drivers/acpi/property.c @@ -1317,6 +1317,31 @@ acpi_fwnode_get_reference_args(const struct fwnode_handle *fwnode, args_count, args); } +static const char *acpi_fwnode_get_name(const struct fwnode_handle *fwnode) +{ + const struct acpi_device *adev; + struct fwnode_handle *parent; + + /* Is this the root node? */ + parent = fwnode_get_parent(fwnode); + if (!parent) + return "\\"; + + fwnode_handle_put(parent); + + if (is_acpi_data_node(fwnode)) { + const struct acpi_data_node *dn = to_acpi_data_node(fwnode); + + return dn->name; + } + + adev = to_acpi_device_node(fwnode); + if (WARN_ON(!adev)) + return NULL; + + return acpi_device_bid(adev); +} + static struct fwnode_handle * acpi_fwnode_get_parent(struct fwnode_handle *fwnode) { @@ -1357,6 +1382,7 @@ acpi_fwnode_device_get_match_data(const struct fwnode_handle *fwnode, .get_parent = acpi_node_get_parent, \ .get_next_child_node = acpi_get_next_subnode, \ .get_named_child_node = acpi_fwnode_get_named_child_node, \ + .get_name = acpi_fwnode_get_name, \ .get_reference_args = acpi_fwnode_get_reference_args, \ .graph_get_next_endpoint = \ acpi_graph_get_next_endpoint, \ diff --git a/drivers/base/property.c b/drivers/base/property.c index f2e555e68b56..9b5ec88e72d8 100644 --- a/drivers/base/property.c +++ b/drivers/base/property.c @@ -556,6 +556,17 @@ int device_add_properties(struct device *dev, } EXPORT_SYMBOL_GPL(device_add_properties); +/** + * fwnode_get_name - Return the name of a node + * @fwnode: The firmware node + * + * Returns a pointer to the node name. + */ +const char *fwnode_get_name(const struct fwnode_handle *fwnode) +{ + return fwnode_call_ptr_op(fwnode, get_name); +} + /** * fwnode_get_parent - Return parent firwmare node * @fwnode: Firmware whose parent is retrieved diff --git a/drivers/base/swnode.c b/drivers/base/swnode.c index 9cde143201f5..eacca6a6626b 100644 --- a/drivers/base/swnode.c +++ b/drivers/base/swnode.c @@ -515,6 +515,17 @@ static int software_node_read_string_array(const struct fwnode_handle *fwnode, propname, val, nval); } +static const char * +software_node_get_name(const struct fwnode_handle *fwnode) +{ + const struct swnode *swnode = to_swnode(fwnode); + + if (!swnode) + return "(null)"; + + return kobject_name(&swnode->kobj); +} + static struct fwnode_handle * software_node_get_parent(const struct fwnode_handle *fwnode) { @@ -615,6 +626,7 @@ static const struct fwnode_operations software_node_ops = { .property_present = software_node_property_present, .property_read_int_array = software_node_read_int_array, .property_read_string_array = software_node_read_string_array, + .get_name = software_node_get_name, .get_parent = software_node_get_parent, .get_next_child_node = software_node_get_next_child, .get_named_child_node = software_node_get_named_child_node, diff --git a/drivers/of/property.c b/drivers/of/property.c index d7fa75e31f22..5bed634551ea 100644 --- a/drivers/of/property.c +++ b/drivers/of/property.c @@ -872,6 +872,11 @@ 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 struct fwnode_handle * of_fwnode_get_parent(const struct fwnode_handle *fwnode) { @@ -993,6 +998,7 @@ 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_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, diff --git a/include/linux/fwnode.h b/include/linux/fwnode.h index ababd6bc82f3..2bbf55739a57 100644 --- a/include/linux/fwnode.h +++ b/include/linux/fwnode.h @@ -56,6 +56,7 @@ struct fwnode_reference_args { * otherwise. * @property_read_string_array: Read an array of string properties. Return zero * on success, a negative error code otherwise. + * @get_name: Return the name of an fwnode. * @get_parent: Return the parent of an fwnode. * @get_next_child_node: Return the next child node in an iteration. * @get_named_child_node: Return a child node with a given name. @@ -82,6 +83,7 @@ struct fwnode_operations { (*property_read_string_array)(const struct fwnode_handle *fwnode_handle, const char *propname, const char **val, size_t nval); + const char *(*get_name)(const struct fwnode_handle *fwnode); struct fwnode_handle *(*get_parent)(const struct fwnode_handle *fwnode); struct fwnode_handle * (*get_next_child_node)(const struct fwnode_handle *fwnode, diff --git a/include/linux/property.h b/include/linux/property.h index ea27c5811e1b..afa84c47bf16 100644 --- a/include/linux/property.h +++ b/include/linux/property.h @@ -80,6 +80,7 @@ struct fwnode_handle *fwnode_find_reference(const struct fwnode_handle *fwnode, const char *name, unsigned int index); +const char *fwnode_get_name(const struct fwnode_handle *fwnode); struct fwnode_handle *fwnode_get_parent(const struct fwnode_handle *fwnode); struct fwnode_handle *fwnode_get_next_parent( struct fwnode_handle *fwnode); -- cgit v1.2.3 From e7e242bccb209b5f73455b33928b8680cc6e3319 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 3 Oct 2019 15:32:13 +0300 Subject: device property: Add a function to obtain a node's prefix The prefix is used for printing purpose before a node, and it also works as a separator between two nodes. Signed-off-by: Sakari Ailus Acked-by: Rob Herring (for OF) Reviewed-by: Andy Shevchenko Signed-off-by: Rafael J. Wysocki --- drivers/acpi/property.c | 22 ++++++++++++++++++++++ drivers/base/property.c | 12 ++++++++++++ drivers/base/swnode.c | 22 ++++++++++++++++++++++ drivers/of/property.c | 10 ++++++++++ include/linux/fwnode.h | 2 ++ include/linux/property.h | 1 + 6 files changed, 69 insertions(+) (limited to 'include/linux/fwnode.h') diff --git a/drivers/acpi/property.c b/drivers/acpi/property.c index a501c1c584b5..e601c4511a8b 100644 --- a/drivers/acpi/property.c +++ b/drivers/acpi/property.c @@ -1342,6 +1342,27 @@ static const char *acpi_fwnode_get_name(const struct fwnode_handle *fwnode) return acpi_device_bid(adev); } +static const char * +acpi_fwnode_get_name_prefix(const struct fwnode_handle *fwnode) +{ + struct fwnode_handle *parent; + + /* Is this the root node? */ + parent = fwnode_get_parent(fwnode); + if (!parent) + return ""; + + /* Is this 2nd node from the root? */ + parent = fwnode_get_next_parent(parent); + if (!parent) + return ""; + + fwnode_handle_put(parent); + + /* ACPI device or data node. */ + return "."; +} + static struct fwnode_handle * acpi_fwnode_get_parent(struct fwnode_handle *fwnode) { @@ -1383,6 +1404,7 @@ acpi_fwnode_device_get_match_data(const struct fwnode_handle *fwnode, .get_next_child_node = acpi_get_next_subnode, \ .get_named_child_node = acpi_fwnode_get_named_child_node, \ .get_name = acpi_fwnode_get_name, \ + .get_name_prefix = acpi_fwnode_get_name_prefix, \ .get_reference_args = acpi_fwnode_get_reference_args, \ .graph_get_next_endpoint = \ acpi_graph_get_next_endpoint, \ diff --git a/drivers/base/property.c b/drivers/base/property.c index 9b5ec88e72d8..511f6d7acdfe 100644 --- a/drivers/base/property.c +++ b/drivers/base/property.c @@ -567,6 +567,18 @@ const char *fwnode_get_name(const struct fwnode_handle *fwnode) return fwnode_call_ptr_op(fwnode, get_name); } +/** + * fwnode_get_name_prefix - Return the prefix of node for printing purposes + * @fwnode: The firmware node + * + * Returns the prefix of a node, intended to be printed right before the node. + * The prefix works also as a separator between the nodes. + */ +const char *fwnode_get_name_prefix(const struct fwnode_handle *fwnode) +{ + return fwnode_call_ptr_op(fwnode, get_name_prefix); +} + /** * fwnode_get_parent - Return parent firwmare node * @fwnode: Firmware whose parent is retrieved diff --git a/drivers/base/swnode.c b/drivers/base/swnode.c index eacca6a6626b..12ca439bab30 100644 --- a/drivers/base/swnode.c +++ b/drivers/base/swnode.c @@ -526,6 +526,27 @@ software_node_get_name(const struct fwnode_handle *fwnode) return kobject_name(&swnode->kobj); } +static const char * +software_node_get_name_prefix(const struct fwnode_handle *fwnode) +{ + struct fwnode_handle *parent; + const char *prefix; + + parent = fwnode_get_parent(fwnode); + if (!parent) + return ""; + + /* Figure out the prefix from the parents. */ + while (is_software_node(parent)) + parent = fwnode_get_next_parent(parent); + + prefix = fwnode_get_name_prefix(parent); + fwnode_handle_put(parent); + + /* Guess something if prefix was NULL. */ + return prefix ?: "/"; +} + static struct fwnode_handle * software_node_get_parent(const struct fwnode_handle *fwnode) { @@ -627,6 +648,7 @@ static const struct fwnode_operations software_node_ops = { .property_read_int_array = software_node_read_int_array, .property_read_string_array = software_node_read_string_array, .get_name = software_node_get_name, + .get_name_prefix = software_node_get_name_prefix, .get_parent = software_node_get_parent, .get_next_child_node = software_node_get_next_child, .get_named_child_node = software_node_get_named_child_node, diff --git a/drivers/of/property.c b/drivers/of/property.c index 5bed634551ea..e8202f61a5d9 100644 --- a/drivers/of/property.c +++ b/drivers/of/property.c @@ -877,6 +877,15 @@ 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) { @@ -999,6 +1008,7 @@ const struct fwnode_operations of_fwnode_ops = { .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, diff --git a/include/linux/fwnode.h b/include/linux/fwnode.h index 2bbf55739a57..a5673c4674cf 100644 --- a/include/linux/fwnode.h +++ b/include/linux/fwnode.h @@ -57,6 +57,7 @@ struct fwnode_reference_args { * @property_read_string_array: Read an array of string properties. Return zero * on success, a negative error code otherwise. * @get_name: Return the name of an fwnode. + * @get_name_prefix: Get a prefix for a node (for printing purposes). * @get_parent: Return the parent of an fwnode. * @get_next_child_node: Return the next child node in an iteration. * @get_named_child_node: Return a child node with a given name. @@ -84,6 +85,7 @@ struct fwnode_operations { const char *propname, const char **val, size_t nval); const char *(*get_name)(const struct fwnode_handle *fwnode); + const char *(*get_name_prefix)(const struct fwnode_handle *fwnode); struct fwnode_handle *(*get_parent)(const struct fwnode_handle *fwnode); struct fwnode_handle * (*get_next_child_node)(const struct fwnode_handle *fwnode, diff --git a/include/linux/property.h b/include/linux/property.h index afa84c47bf16..054661109661 100644 --- a/include/linux/property.h +++ b/include/linux/property.h @@ -81,6 +81,7 @@ struct fwnode_handle *fwnode_find_reference(const struct fwnode_handle *fwnode, unsigned int index); const char *fwnode_get_name(const struct fwnode_handle *fwnode); +const char *fwnode_get_name_prefix(const struct fwnode_handle *fwnode); struct fwnode_handle *fwnode_get_parent(const struct fwnode_handle *fwnode); struct fwnode_handle *fwnode_get_next_parent( struct fwnode_handle *fwnode); -- cgit v1.2.3 From 8ed61d36050c57d9eba09511f53d683fd63b04d1 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Tue, 8 Oct 2019 16:26:06 +0300 Subject: device property: Fix the description of struct fwnode_operations Adding description for the device_is_available member which was missing, and fixing the description of the member property_read_int_array. Signed-off-by: Heikki Krogerus Reviewed-by: Andy Shevchenko Signed-off-by: Rafael J. Wysocki --- include/linux/fwnode.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'include/linux/fwnode.h') diff --git a/include/linux/fwnode.h b/include/linux/fwnode.h index a5673c4674cf..9d9dc444d787 100644 --- a/include/linux/fwnode.h +++ b/include/linux/fwnode.h @@ -49,11 +49,11 @@ struct fwnode_reference_args { * struct fwnode_operations - Operations for fwnode interface * @get: Get a reference to an fwnode. * @put: Put a reference to an fwnode. + * @device_is_available: Return true if the device is available. * @device_get_match_data: Return the device driver match data. * @property_present: Return true if a property is present. - * @property_read_integer_array: Read an array of integer properties. Return - * zero on success, a negative error code - * otherwise. + * @property_read_int_array: Read an array of integer properties. Return zero on + * success, a negative error code otherwise. * @property_read_string_array: Read an array of string properties. Return zero * on success, a negative error code otherwise. * @get_name: Return the name of an fwnode. -- cgit v1.2.3 From 92df01e3601fe29eb3727a82705eafa6209053f5 Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Fri, 11 Oct 2019 12:15:20 -0700 Subject: driver: core: Improve documentation for fwnode_operations.add_links() The add_links() ops shouldn't return on the first failed device link add. It needs to continue trying to add device links to other suppliers that are available. The documentation didn't explain WHY this behavior is necessary. So, update the documentation with an example that explains why this is necessary. Signed-off-by: Saravana Kannan Link: https://lore.kernel.org/r/20191011191521.179614-3-saravanak@google.com Signed-off-by: Greg Kroah-Hartman --- include/linux/fwnode.h | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) (limited to 'include/linux/fwnode.h') diff --git a/include/linux/fwnode.h b/include/linux/fwnode.h index 6ae05b9ce359..97223e2410bd 100644 --- a/include/linux/fwnode.h +++ b/include/linux/fwnode.h @@ -71,8 +71,25 @@ struct fwnode_reference_args { * links to all the suppliers of the device that are available at * the time this function is called. The function must NOT stop * at the first failed device link if other unlinked supplier - * devices are present in the system. If some suppliers are not - * yet available, this function will be called again when other + * devices are present in the system. This is necessary for the + * driver/bus sync_state() callbacks to work correctly. + * + * For example, say Device-C depends on suppliers Device-S1 and + * Device-S2 and the dependency is listed in that order in the + * firmware. Say, S1 gets populated from the firmware after + * late_initcall_sync(). Say S2 is populated and probed way + * before that in device_initcall(). When C is populated, if this + * add_links() function doesn't continue past a "failed linking to + * S1" and continue linking C to S2, then S2 will get a + * sync_state() callback before C is probed. This is because from + * the perspective of S2, C was never a consumer when its + * sync_state() evaluation is done. To avoid this, the add_links() + * function has to go through all available suppliers of the + * device (that corresponds to this fwnode) and link to them + * before returning. + * + * If some suppliers are not yet available (indicated by an error + * return value), this function will be called again when other * devices are added to allow creating device links to any newly * available suppliers. * -- cgit v1.2.3 From 03324507e66c7664c754b1ef92c5c3be24c78aa2 Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Mon, 28 Oct 2019 15:00:24 -0700 Subject: driver core: Allow fwnode_operations.add_links to differentiate errors When add_links() still has suppliers that it needs to link to in the future, this patch allows it to differentiate between suppliers that are needed for probing vs suppliers that are needed for sync_state() correctness. Signed-off-by: Saravana Kannan Link: https://lore.kernel.org/r/20191028220027.251605-4-saravanak@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 12 ++++++++---- include/linux/fwnode.h | 13 +++++++++---- 2 files changed, 17 insertions(+), 8 deletions(-) (limited to 'include/linux/fwnode.h') diff --git a/drivers/base/core.c b/drivers/base/core.c index 48cd43a91ce6..e6d3e6d485da 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -2297,7 +2297,7 @@ int device_add(struct device *dev) struct device *parent; struct kobject *kobj; struct class_interface *class_intf; - int error = -EINVAL; + int error = -EINVAL, fw_ret; struct kobject *glue_dir = NULL; dev = get_device(dev); @@ -2413,9 +2413,13 @@ int device_add(struct device *dev) */ device_link_add_missing_supplier_links(); - if (fwnode_has_op(dev->fwnode, add_links) - && fwnode_call_int_op(dev->fwnode, add_links, dev)) - device_link_wait_for_mandatory_supplier(dev, true); + if (fwnode_has_op(dev->fwnode, add_links)) { + fw_ret = fwnode_call_int_op(dev->fwnode, add_links, dev); + if (fw_ret == -ENODEV) + device_link_wait_for_mandatory_supplier(dev); + else if (fw_ret) + device_link_wait_for_optional_supplier(dev); + } bus_probe_device(dev); if (parent) diff --git a/include/linux/fwnode.h b/include/linux/fwnode.h index 97223e2410bd..766ff9bb5876 100644 --- a/include/linux/fwnode.h +++ b/include/linux/fwnode.h @@ -94,10 +94,15 @@ struct fwnode_reference_args { * available suppliers. * * Return 0 if device links have been successfully created to all - * the suppliers of this device or if the supplier information is - * not known. Return an error if and only if the supplier - * information is known but some of the suppliers are not yet - * available to create device links to. + * the suppliers this device needs to create device links to or if + * the supplier information is not known. + * + * Return -ENODEV if and only if the suppliers needed for probing + * the device are not yet available to create device links to. + * + * Return -EAGAIN if there are suppliers that need to be linked to + * that are not yet available but none of those suppliers are + * necessary for probing this device. */ struct fwnode_operations { struct fwnode_handle *(*get)(struct fwnode_handle *fwnode); -- cgit v1.2.3 From bee3bbe616a2c8de641a64d874f9206835bd4401 Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Tue, 12 Nov 2019 18:35:59 -0800 Subject: driver core: Clarify documentation for fwnode_operations.add_links() The wording was a bit ambiguous. So update it to make it clear. Signed-off-by: Saravana Kannan Reviewed-by: Rafael J. Wysocki Link: https://lore.kernel.org/r/20191113023559.62295-2-saravanak@google.com Signed-off-by: Greg Kroah-Hartman --- include/linux/fwnode.h | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'include/linux/fwnode.h') diff --git a/include/linux/fwnode.h b/include/linux/fwnode.h index 766ff9bb5876..23df37f85398 100644 --- a/include/linux/fwnode.h +++ b/include/linux/fwnode.h @@ -94,15 +94,16 @@ struct fwnode_reference_args { * available suppliers. * * Return 0 if device links have been successfully created to all - * the suppliers this device needs to create device links to or if - * the supplier information is not known. + * the known suppliers of this device or if the supplier + * information is not known. * - * Return -ENODEV if and only if the suppliers needed for probing - * the device are not yet available to create device links to. + * Return -ENODEV if the suppliers needed for probing this device + * have not been registered yet (because device links can only be + * created to devices registered with the driver core). * - * Return -EAGAIN if there are suppliers that need to be linked to - * that are not yet available but none of those suppliers are - * necessary for probing this device. + * Return -EAGAIN if some of the suppliers of this device have not + * been registered yet, but none of those suppliers are necessary + * for probing the device. */ struct fwnode_operations { struct fwnode_handle *(*get)(struct fwnode_handle *fwnode); -- cgit v1.2.3