Merge tag 'driver-core-5.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git...
authorLinus Torvalds <torvalds@linux-foundation.org>
Wed, 24 Feb 2021 18:13:55 +0000 (10:13 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 24 Feb 2021 18:13:55 +0000 (10:13 -0800)
Pull driver core / debugfs update from Greg KH:
 "Here is the "big" driver core and debugfs update for 5.12-rc1

  This set of driver core patches caused a bunch of problems in
  linux-next for the past few weeks, when Saravana tried to set
  fw_devlink=on as the default functionality. This caused a number of
  systems to stop booting, and lots of bugs were fixed in this area for
  almost all of the reported systems, but this option is not ready to be
  turned on just yet for the default operation based on this testing, so
  I've reverted that change at the very end so we don't have to worry
  about regressions in 5.12

  We will try to turn this on for 5.13 if testing goes better over the
  next few months.

  Other than the fixes caused by the fw_devlink testing in here, there's
  not much more:

   - debugfs fixes for invalid input into debugfs_lookup()

   - kerneldoc cleanups

   - warn message if platform drivers return an error on their remove
     callback (a futile effort, but good to catch).

  All of these have been in linux-next for a while now, and the
  regressions have gone away with the revert of the fw_devlink change"

* tag 'driver-core-5.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core: (35 commits)
  Revert "driver core: Set fw_devlink=on by default"
  of: property: fw_devlink: Ignore interrupts property for some configs
  debugfs: do not attempt to create a new file before the filesystem is initalized
  debugfs: be more robust at handling improper input in debugfs_lookup()
  driver core: auxiliary bus: Fix calling stage for auxiliary bus init
  of: irq: Fix the return value for of_irq_parse_one() stub
  of: irq: make a stub for of_irq_parse_one()
  clk: Mark fwnodes when their clock provider is added/removed
  PM: domains: Mark fwnodes when their powerdomain is added/removed
  irqdomain: Mark fwnodes when their irqdomain is added/removed
  driver core: fw_devlink: Handle suppliers that don't use driver core
  of: property: Add fw_devlink support for optional properties
  driver core: Add fw_devlink.strict kernel param
  of: property: Don't add links to absent suppliers
  driver core: fw_devlink: Detect supplier devices that will never be added
  driver core: platform: Emit a warning if a remove callback returned non-zero
  of: property: Fix fw_devlink handling of interrupts/interrupts-extended
  gpiolib: Don't probe gpio_device if it's not the primary device
  device.h: Remove bogus "the" in kerneldoc
  gpiolib: Bind gpio_device to a driver to enable fw_devlink=on by default
  ...

21 files changed:
Documentation/admin-guide/kernel-parameters.txt
drivers/base/Kconfig
drivers/base/auxiliary.c
drivers/base/base.h
drivers/base/bus.c
drivers/base/core.c
drivers/base/init.c
drivers/base/platform.c
drivers/base/power/domain.c
drivers/base/test/Makefile
drivers/clk/clk.c
drivers/gpio/gpiolib-of.c
drivers/gpio/gpiolib-of.h
drivers/gpio/gpiolib.c
drivers/of/property.c
fs/debugfs/inode.c
include/linux/device.h
include/linux/device/driver.h
include/linux/fwnode.h
include/linux/of_irq.h
kernel/irq/irqdomain.c

index 0ac8837773184da227b9779b506a62ed6bcf5b04..da949b20503713044f9bcadff9f3eb08eaa7e777 100644 (file)
                                to enforce probe and suspend/resume ordering.
                        rpm --  Like "on", but also use to order runtime PM.
 
+       fw_devlink.strict=<bool>
+                       [KNL] Treat all inferred dependencies as mandatory
+                       dependencies. This only applies for fw_devlink=on|rpm.
+                       Format: <bool>
+
        gamecon.map[2|3]=
                        [HW,JOY] Multisystem joystick and NES/SNES/PSX pad
                        support via parallel port (up to 5 devices per port)
index 040be48ce046643672fc846899ee78d55a6dde31..324aa03fed3cf597249a107e48dfe54b6f77f60a 100644 (file)
@@ -161,7 +161,7 @@ config HMEM_REPORTING
        default n
        depends on NUMA
        help
-         Enable reporting for heterogenous memory access attributes under
+         Enable reporting for heterogeneous memory access attributes under
          their non-uniform memory nodes.
 
 source "drivers/base/test/Kconfig"
index 8336535f1e110219992b2c765a6b81cfdfdbb53e..d8b314e7d0fdc88cfa33f37f2ccefdd329cb286e 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/pm_runtime.h>
 #include <linux/string.h>
 #include <linux/auxiliary_bus.h>
+#include "base.h"
 
 static const struct auxiliary_device_id *auxiliary_match_id(const struct auxiliary_device_id *id,
                                                            const struct auxiliary_device *auxdev)
@@ -260,19 +261,11 @@ void auxiliary_driver_unregister(struct auxiliary_driver *auxdrv)
 }
 EXPORT_SYMBOL_GPL(auxiliary_driver_unregister);
 
-static int __init auxiliary_bus_init(void)
+void __init auxiliary_bus_init(void)
 {
-       return bus_register(&auxiliary_bus_type);
+       WARN_ON(bus_register(&auxiliary_bus_type));
 }
 
-static void __exit auxiliary_bus_exit(void)
-{
-       bus_unregister(&auxiliary_bus_type);
-}
-
-module_init(auxiliary_bus_init);
-module_exit(auxiliary_bus_exit);
-
 MODULE_LICENSE("GPL v2");
 MODULE_DESCRIPTION("Auxiliary Bus");
 MODULE_AUTHOR("David Ertman <david.m.ertman@intel.com>");
index f5600a83124fafa376826260e8bd04d57ae13531..52b3d7b75c2750e85c0b5f1b238964dd42cb5fa9 100644 (file)
@@ -119,6 +119,11 @@ static inline int hypervisor_init(void) { return 0; }
 extern int platform_bus_init(void);
 extern void cpu_dev_init(void);
 extern void container_dev_init(void);
+#ifdef CONFIG_AUXILIARY_BUS
+extern void auxiliary_bus_init(void);
+#else
+static inline void auxiliary_bus_init(void) { }
+#endif
 
 struct kobject *virtual_device_parent(struct device *dev);
 
index a9c23ecebc7c8de3e1060ed4243cf75d431f8099..36d0c654ea6124c94f41851261b43c4f933c9e01 100644 (file)
@@ -633,7 +633,7 @@ int bus_add_driver(struct device_driver *drv)
        error = driver_add_groups(drv, bus->drv_groups);
        if (error) {
                /* How the hell do we get out of this pickle? Give up */
-               printk(KERN_ERR "%s: driver_create_groups(%s) failed\n",
+               printk(KERN_ERR "%s: driver_add_groups(%s) failed\n",
                        __func__, drv->name);
        }
 
@@ -729,23 +729,6 @@ int device_reprobe(struct device *dev)
 }
 EXPORT_SYMBOL_GPL(device_reprobe);
 
-/**
- * find_bus - locate bus by name.
- * @name: name of bus.
- *
- * Call kset_find_obj() to iterate over list of buses to
- * find a bus by name. Return bus if found.
- *
- * Note that kset_find_obj increments bus' reference count.
- */
-#if 0
-struct bus_type *find_bus(char *name)
-{
-       struct kobject *k = kset_find_obj(bus_kset, name);
-       return k ? to_bus(k) : NULL;
-}
-#endif  /*  0  */
-
 static int bus_add_groups(struct bus_type *bus,
                          const struct attribute_group **groups)
 {
index 7c0406e675e98fcd71c9f62ea66d03dfd71b8932..f29839382f8166b4106e96eb9a0cc41f1e2d7b34 100644 (file)
@@ -149,6 +149,21 @@ void fwnode_links_purge(struct fwnode_handle *fwnode)
        fwnode_links_purge_consumers(fwnode);
 }
 
+static void fw_devlink_purge_absent_suppliers(struct fwnode_handle *fwnode)
+{
+       struct fwnode_handle *child;
+
+       /* Don't purge consumer links of an added child */
+       if (fwnode->dev)
+               return;
+
+       fwnode->flags |= FWNODE_FLAG_NOT_DEVICE;
+       fwnode_links_purge_consumers(fwnode);
+
+       fwnode_for_each_available_child_node(fwnode, child)
+               fw_devlink_purge_absent_suppliers(child);
+}
+
 #ifdef CONFIG_SRCU
 static DEFINE_MUTEX(device_links_lock);
 DEFINE_STATIC_SRCU(device_links_srcu);
@@ -245,7 +260,8 @@ int device_is_dependent(struct device *dev, void *target)
                return ret;
 
        list_for_each_entry(link, &dev->links.consumers, s_node) {
-               if (link->flags == (DL_FLAG_SYNC_STATE_ONLY | DL_FLAG_MANAGED))
+               if ((link->flags & ~DL_FLAG_INFERRED) ==
+                   (DL_FLAG_SYNC_STATE_ONLY | DL_FLAG_MANAGED))
                        continue;
 
                if (link->consumer == target)
@@ -318,7 +334,8 @@ static int device_reorder_to_tail(struct device *dev, void *not_used)
 
        device_for_each_child(dev, NULL, device_reorder_to_tail);
        list_for_each_entry(link, &dev->links.consumers, s_node) {
-               if (link->flags == (DL_FLAG_SYNC_STATE_ONLY | DL_FLAG_MANAGED))
+               if ((link->flags & ~DL_FLAG_INFERRED) ==
+                   (DL_FLAG_SYNC_STATE_ONLY | DL_FLAG_MANAGED))
                        continue;
                device_reorder_to_tail(link->consumer, NULL);
        }
@@ -566,7 +583,8 @@ postcore_initcall(devlink_class_init);
 #define DL_MANAGED_LINK_FLAGS (DL_FLAG_AUTOREMOVE_CONSUMER | \
                               DL_FLAG_AUTOREMOVE_SUPPLIER | \
                               DL_FLAG_AUTOPROBE_CONSUMER  | \
-                              DL_FLAG_SYNC_STATE_ONLY)
+                              DL_FLAG_SYNC_STATE_ONLY | \
+                              DL_FLAG_INFERRED)
 
 #define DL_ADD_VALID_FLAGS (DL_MANAGED_LINK_FLAGS | DL_FLAG_STATELESS | \
                            DL_FLAG_PM_RUNTIME | DL_FLAG_RPM_ACTIVE)
@@ -635,7 +653,7 @@ struct device_link *device_link_add(struct device *consumer,
        if (!consumer || !supplier || flags & ~DL_ADD_VALID_FLAGS ||
            (flags & DL_FLAG_STATELESS && flags & DL_MANAGED_LINK_FLAGS) ||
            (flags & DL_FLAG_SYNC_STATE_ONLY &&
-            flags != DL_FLAG_SYNC_STATE_ONLY) ||
+            (flags & ~DL_FLAG_INFERRED) != DL_FLAG_SYNC_STATE_ONLY) ||
            (flags & DL_FLAG_AUTOPROBE_CONSUMER &&
             flags & (DL_FLAG_AUTOREMOVE_CONSUMER |
                      DL_FLAG_AUTOREMOVE_SUPPLIER)))
@@ -691,6 +709,10 @@ struct device_link *device_link_add(struct device *consumer,
                if (link->consumer != consumer)
                        continue;
 
+               if (link->flags & DL_FLAG_INFERRED &&
+                   !(flags & DL_FLAG_INFERRED))
+                       link->flags &= ~DL_FLAG_INFERRED;
+
                if (flags & DL_FLAG_PM_RUNTIME) {
                        if (!(link->flags & DL_FLAG_PM_RUNTIME)) {
                                pm_runtime_new_link(consumer);
@@ -950,6 +972,10 @@ int device_links_check_suppliers(struct device *dev)
        mutex_lock(&fwnode_link_lock);
        if (dev->fwnode && !list_empty(&dev->fwnode->suppliers) &&
            !fw_devlink_is_permissive()) {
+               dev_dbg(dev, "probe deferral - wait for supplier %pfwP\n",
+                       list_first_entry(&dev->fwnode->suppliers,
+                       struct fwnode_link,
+                       c_hook)->supplier);
                mutex_unlock(&fwnode_link_lock);
                return -EPROBE_DEFER;
        }
@@ -964,6 +990,8 @@ int device_links_check_suppliers(struct device *dev)
                if (link->status != DL_STATE_AVAILABLE &&
                    !(link->flags & DL_FLAG_SYNC_STATE_ONLY)) {
                        device_links_missing_supplier(dev);
+                       dev_dbg(dev, "probe deferral - supplier %s not ready\n",
+                               dev_name(link->supplier));
                        ret = -EPROBE_DEFER;
                        break;
                }
@@ -1142,12 +1170,22 @@ void device_links_driver_bound(struct device *dev)
        LIST_HEAD(sync_list);
 
        /*
-        * If a device probes successfully, it's expected to have created all
+        * If a device binds successfully, it's expected to have created all
         * the device links it needs to or make new device links as it needs
-        * them. So, it no longer needs to wait on any suppliers.
+        * them. So, fw_devlink no longer needs to create device links to any
+        * of the device's suppliers.
+        *
+        * Also, if a child firmware node of this bound device is not added as
+        * a device by now, assume it is never going to be added and make sure
+        * other devices don't defer probe indefinitely by waiting for such a
+        * child device.
         */
-       if (dev->fwnode && dev->fwnode->dev == dev)
+       if (dev->fwnode && dev->fwnode->dev == dev) {
+               struct fwnode_handle *child;
                fwnode_links_purge_suppliers(dev->fwnode);
+               fwnode_for_each_available_child_node(dev->fwnode, child)
+                       fw_devlink_purge_absent_suppliers(child);
+       }
        device_remove_file(dev, &dev_attr_waiting_for_supplier);
 
        device_links_write_lock();
@@ -1458,7 +1496,14 @@ static void device_links_purge(struct device *dev)
        device_links_write_unlock();
 }
 
-static u32 fw_devlink_flags = DL_FLAG_SYNC_STATE_ONLY;
+#define FW_DEVLINK_FLAGS_PERMISSIVE    (DL_FLAG_INFERRED | \
+                                        DL_FLAG_SYNC_STATE_ONLY)
+#define FW_DEVLINK_FLAGS_ON            (DL_FLAG_INFERRED | \
+                                        DL_FLAG_AUTOPROBE_CONSUMER)
+#define FW_DEVLINK_FLAGS_RPM           (FW_DEVLINK_FLAGS_ON | \
+                                        DL_FLAG_PM_RUNTIME)
+
+static u32 fw_devlink_flags = FW_DEVLINK_FLAGS_PERMISSIVE;
 static int __init fw_devlink_setup(char *arg)
 {
        if (!arg)
@@ -1467,17 +1512,23 @@ static int __init fw_devlink_setup(char *arg)
        if (strcmp(arg, "off") == 0) {
                fw_devlink_flags = 0;
        } else if (strcmp(arg, "permissive") == 0) {
-               fw_devlink_flags = DL_FLAG_SYNC_STATE_ONLY;
+               fw_devlink_flags = FW_DEVLINK_FLAGS_PERMISSIVE;
        } else if (strcmp(arg, "on") == 0) {
-               fw_devlink_flags = DL_FLAG_AUTOPROBE_CONSUMER;
+               fw_devlink_flags = FW_DEVLINK_FLAGS_ON;
        } else if (strcmp(arg, "rpm") == 0) {
-               fw_devlink_flags = DL_FLAG_AUTOPROBE_CONSUMER |
-                                  DL_FLAG_PM_RUNTIME;
+               fw_devlink_flags = FW_DEVLINK_FLAGS_RPM;
        }
        return 0;
 }
 early_param("fw_devlink", fw_devlink_setup);
 
+static bool fw_devlink_strict;
+static int __init fw_devlink_strict_setup(char *arg)
+{
+       return strtobool(arg, &fw_devlink_strict);
+}
+early_param("fw_devlink.strict", fw_devlink_strict_setup);
+
 u32 fw_devlink_get_flags(void)
 {
        return fw_devlink_flags;
@@ -1485,7 +1536,12 @@ u32 fw_devlink_get_flags(void)
 
 static bool fw_devlink_is_permissive(void)
 {
-       return fw_devlink_flags == DL_FLAG_SYNC_STATE_ONLY;
+       return fw_devlink_flags == FW_DEVLINK_FLAGS_PERMISSIVE;
+}
+
+bool fw_devlink_is_strict(void)
+{
+       return fw_devlink_strict && !fw_devlink_is_permissive();
 }
 
 static void fw_devlink_parse_fwnode(struct fwnode_handle *fwnode)
@@ -1507,6 +1563,53 @@ static void fw_devlink_parse_fwtree(struct fwnode_handle *fwnode)
                fw_devlink_parse_fwtree(child);
 }
 
+/**
+ * fw_devlink_relax_cycle - Convert cyclic links to SYNC_STATE_ONLY links
+ * @con: Device to check dependencies for.
+ * @sup: Device to check against.
+ *
+ * Check if @sup depends on @con or any device dependent on it (its child or
+ * its consumer etc).  When such a cyclic dependency is found, convert all
+ * device links created solely by fw_devlink into SYNC_STATE_ONLY device links.
+ * This is the equivalent of doing fw_devlink=permissive just between the
+ * devices in the cycle. We need to do this because, at this point, fw_devlink
+ * can't tell which of these dependencies is not a real dependency.
+ *
+ * Return 1 if a cycle is found. Otherwise, return 0.
+ */
+static int fw_devlink_relax_cycle(struct device *con, void *sup)
+{
+       struct device_link *link;
+       int ret;
+
+       if (con == sup)
+               return 1;
+
+       ret = device_for_each_child(con, sup, fw_devlink_relax_cycle);
+       if (ret)
+               return ret;
+
+       list_for_each_entry(link, &con->links.consumers, s_node) {
+               if ((link->flags & ~DL_FLAG_INFERRED) ==
+                   (DL_FLAG_SYNC_STATE_ONLY | DL_FLAG_MANAGED))
+                       continue;
+
+               if (!fw_devlink_relax_cycle(link->consumer, sup))
+                       continue;
+
+               ret = 1;
+
+               if (!(link->flags & DL_FLAG_INFERRED))
+                       continue;
+
+               pm_runtime_drop_link(link);
+               link->flags = DL_FLAG_MANAGED | FW_DEVLINK_FLAGS_PERMISSIVE;
+               dev_dbg(link->consumer, "Relaxing link with %s\n",
+                       dev_name(link->supplier));
+       }
+       return ret;
+}
+
 /**
  * fw_devlink_create_devlink - Create a device link from a consumer to fwnode
  * @con - Consumer device for the device link
@@ -1534,16 +1637,40 @@ static int fw_devlink_create_devlink(struct device *con,
 
        sup_dev = get_dev_from_fwnode(sup_handle);
        if (sup_dev) {
+               /*
+                * If it's one of those drivers that don't actually bind to
+                * their device using driver core, then don't wait on this
+                * supplier device indefinitely.
+                */
+               if (sup_dev->links.status == DL_DEV_NO_DRIVER &&
+                   sup_handle->flags & FWNODE_FLAG_INITIALIZED) {
+                       ret = -EINVAL;
+                       goto out;
+               }
+
                /*
                 * If this fails, it is due to cycles in device links.  Just
                 * give up on this link and treat it as invalid.
                 */
-               if (!device_link_add(con, sup_dev, flags))
+               if (!device_link_add(con, sup_dev, flags) &&
+                   !(flags & DL_FLAG_SYNC_STATE_ONLY)) {
+                       dev_info(con, "Fixing up cyclic dependency with %s\n",
+                                dev_name(sup_dev));
+                       device_links_write_lock();
+                       fw_devlink_relax_cycle(con, sup_dev);
+                       device_links_write_unlock();
+                       device_link_add(con, sup_dev,
+                                       FW_DEVLINK_FLAGS_PERMISSIVE);
                        ret = -EINVAL;
+               }
 
                goto out;
        }
 
+       /* Supplier that's already initialized without a struct device. */
+       if (sup_handle->flags & FWNODE_FLAG_INITIALIZED)
+               return -EINVAL;
+
        /*
         * DL_FLAG_SYNC_STATE_ONLY doesn't block probing and supports
         * cycles. So cycle detection isn't necessary and shouldn't be
@@ -1632,7 +1759,7 @@ static void __fw_devlink_link_to_consumers(struct device *dev)
                                con_dev = NULL;
                        } else {
                                own_link = false;
-                               dl_flags = DL_FLAG_SYNC_STATE_ONLY;
+                               dl_flags = FW_DEVLINK_FLAGS_PERMISSIVE;
                        }
                }
 
@@ -1687,7 +1814,7 @@ static void __fw_devlink_link_to_suppliers(struct device *dev,
        if (own_link)
                dl_flags = fw_devlink_get_flags();
        else
-               dl_flags = DL_FLAG_SYNC_STATE_ONLY;
+               dl_flags = FW_DEVLINK_FLAGS_PERMISSIVE;
 
        list_for_each_entry_safe(link, tmp, &fwnode->suppliers, c_hook) {
                int ret;
index 908e6520e804b2967ab029ca6a617cc0d1902f16..a9f57c22fb9e2f128e9d8708483ca1f631a8fa7e 100644 (file)
@@ -32,6 +32,7 @@ void __init driver_init(void)
         */
        of_core_init();
        platform_bus_init();
+       auxiliary_bus_init();
        cpu_dev_init();
        memory_dev_init();
        container_dev_init();
index ac68328b0acada084d1dbaa280b707d270335d8f..6e1f8e0b661cef101dc6d4a01ad56beea41345d5 100644 (file)
@@ -1463,13 +1463,16 @@ static int platform_remove(struct device *_dev)
 {
        struct platform_driver *drv = to_platform_driver(_dev->driver);
        struct platform_device *dev = to_platform_device(_dev);
-       int ret = 0;
 
-       if (drv->remove)
-               ret = drv->remove(dev);
+       if (drv->remove) {
+               int ret = drv->remove(dev);
+
+               if (ret)
+                       dev_warn(_dev, "remove callback returned a non-zero value. This will be ignored.\n");
+       }
        dev_pm_domain_detach(_dev, true);
 
-       return ret;
+       return 0;
 }
 
 static void platform_shutdown(struct device *_dev)
index aaf6c83b5cf616364ed33bfb2884498293b67fdd..78c310d3179d36b67c59a62e6ceda8d2f0f774d4 100644 (file)
@@ -2196,6 +2196,7 @@ static int genpd_add_provider(struct device_node *np, genpd_xlate_t xlate,
        cp->node = of_node_get(np);
        cp->data = data;
        cp->xlate = xlate;
+       fwnode_dev_initialized(&np->fwnode, true);
 
        mutex_lock(&of_genpd_mutex);
        list_add(&cp->link, &of_genpd_providers);
@@ -2385,6 +2386,7 @@ void of_genpd_del_provider(struct device_node *np)
                                }
                        }
 
+                       fwnode_dev_initialized(&cp->node->fwnode, false);
                        list_del(&cp->link);
                        of_node_put(cp->node);
                        kfree(cp);
index 3ca56367c84b729a7b7f3d0e7df7b61c9b652a46..2f15fae8625f191d42fd80f594d86fcb5a8fe4c5 100644 (file)
@@ -2,3 +2,4 @@
 obj-$(CONFIG_TEST_ASYNC_DRIVER_PROBE)  += test_async_driver_probe.o
 
 obj-$(CONFIG_KUNIT_DRIVER_PE_TEST) += property-entry-test.o
+CFLAGS_REMOVE_property-entry-test.o += -fplugin-arg-structleak_plugin-byref -fplugin-arg-structleak_plugin-byref-all
index 3d751ae5bc703280eb2918afea367277641cf933..5052541a0986658248867a20a531cbac673dd1d1 100644 (file)
@@ -4576,6 +4576,8 @@ int of_clk_add_provider(struct device_node *np,
        if (ret < 0)
                of_clk_del_provider(np);
 
+       fwnode_dev_initialized(&np->fwnode, true);
+
        return ret;
 }
 EXPORT_SYMBOL_GPL(of_clk_add_provider);
@@ -4693,6 +4695,7 @@ void of_clk_del_provider(struct device_node *np)
        list_for_each_entry(cp, &of_clk_providers, link) {
                if (cp->node == np) {
                        list_del(&cp->link);
+                       fwnode_dev_initialized(&np->fwnode, false);
                        of_node_put(cp->node);
                        kfree(cp);
                        break;
index b4a71119a4b07c24d24f3329678d9940609fb543..baf0153b7bca3f150af5b129453b5f6e284f6d2c 100644 (file)
@@ -1039,3 +1039,14 @@ void of_gpiochip_remove(struct gpio_chip *chip)
 {
        of_node_put(chip->of_node);
 }
+
+void of_gpio_dev_init(struct gpio_chip *gc, struct gpio_device *gdev)
+{
+       /* If the gpiochip has an assigned OF node this takes precedence */
+       if (gc->of_node)
+               gdev->dev.of_node = gc->of_node;
+       else
+               gc->of_node = gdev->dev.of_node;
+       if (gdev->dev.of_node)
+               gdev->dev.fwnode = of_fwnode_handle(gdev->dev.of_node);
+}
index ed26664f153782fc6670a242b4fb60a6a74b1364..8af2bc899aab251082f11441beb1618183a4b1d4 100644 (file)
@@ -15,6 +15,7 @@ int of_gpiochip_add(struct gpio_chip *gc);
 void of_gpiochip_remove(struct gpio_chip *gc);
 int of_gpio_get_count(struct device *dev, const char *con_id);
 bool of_gpio_need_valid_mask(const struct gpio_chip *gc);
+void of_gpio_dev_init(struct gpio_chip *gc, struct gpio_device *gdev);
 #else
 static inline struct gpio_desc *of_find_gpio(struct device *dev,
                                             const char *con_id,
@@ -33,6 +34,10 @@ static inline bool of_gpio_need_valid_mask(const struct gpio_chip *gc)
 {
        return false;
 }
+static inline void of_gpio_dev_init(struct gpio_chip *gc,
+                                   struct gpio_device *gdev)
+{
+}
 #endif /* CONFIG_OF_GPIO */
 
 extern struct notifier_block gpio_of_notifier;
index 844198cb4e317839838eb9fe763578d0731052bb..adf55db080d86f1d3c07832972e2a468e53380e3 100644 (file)
 static DEFINE_IDA(gpio_ida);
 static dev_t gpio_devt;
 #define GPIO_DEV_MAX 256 /* 256 GPIO chip devices supported */
+static int gpio_bus_match(struct device *dev, struct device_driver *drv);
 static struct bus_type gpio_bus_type = {
        .name = "gpio",
+       .match = gpio_bus_match,
 };
 
 /*
@@ -590,13 +592,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
                gdev->dev.of_node = gc->parent->of_node;
        }
 
-#ifdef CONFIG_OF_GPIO
-       /* If the gpiochip has an assigned OF node this takes precedence */
-       if (gc->of_node)
-               gdev->dev.of_node = gc->of_node;
-       else
-               gc->of_node = gdev->dev.of_node;
-#endif
+       of_gpio_dev_init(gc, gdev);
 
        gdev->id = ida_alloc(&gpio_ida, GFP_KERNEL);
        if (gdev->id < 0) {
@@ -4215,6 +4211,41 @@ void gpiod_put_array(struct gpio_descs *descs)
 }
 EXPORT_SYMBOL_GPL(gpiod_put_array);
 
+
+static int gpio_bus_match(struct device *dev, struct device_driver *drv)
+{
+       /*
+        * Only match if the fwnode doesn't already have a proper struct device
+        * created for it.
+        */
+       if (dev->fwnode && dev->fwnode->dev != dev)
+               return 0;
+       return 1;
+}
+
+static int gpio_stub_drv_probe(struct device *dev)
+{
+       /*
+        * The DT node of some GPIO chips have a "compatible" property, but
+        * never have a struct device added and probed by a driver to register
+        * the GPIO chip with gpiolib. In such cases, fw_devlink=on will cause
+        * the consumers of the GPIO chip to get probe deferred forever because
+        * they will be waiting for a device associated with the GPIO chip
+        * firmware node to get added and bound to a driver.
+        *
+        * To allow these consumers to probe, we associate the struct
+        * gpio_device of the GPIO chip with the firmware node and then simply
+        * bind it to this stub driver.
+        */
+       return 0;
+}
+
+static struct device_driver gpio_stub_drv = {
+       .name = "gpio_stub_drv",
+       .bus = &gpio_bus_type,
+       .probe = gpio_stub_drv_probe,
+};
+
 static int __init gpiolib_dev_init(void)
 {
        int ret;
@@ -4226,9 +4257,16 @@ static int __init gpiolib_dev_init(void)
                return ret;
        }
 
+       if (driver_register(&gpio_stub_drv) < 0) {
+               pr_err("gpiolib: could not register GPIO stub driver\n");
+               bus_unregister(&gpio_bus_type);
+               return ret;
+       }
+
        ret = alloc_chrdev_region(&gpio_devt, 0, GPIO_DEV_MAX, GPIOCHIP_NAME);
        if (ret < 0) {
                pr_err("gpiolib: failed to allocate char dev region\n");
+               driver_unregister(&gpio_stub_drv);
                bus_unregister(&gpio_bus_type);
                return ret;
        }
index 5f9eed79a8aaf84f42e1a4cd1ce7a00f1da82cdf..5036a362f52e711ff0a3381b280a2d37e4262022 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/of.h>
 #include <linux/of_device.h>
 #include <linux/of_graph.h>
+#include <linux/of_irq.h>
 #include <linux/string.h>
 #include <linux/moduleparam.h>
 
@@ -1102,7 +1103,9 @@ static int of_link_to_phandle(struct device_node *con_np,
         * created for them.
         */
        sup_dev = get_dev_from_fwnode(&sup_np->fwnode);
-       if (!sup_dev && of_node_check_flag(sup_np, OF_POPULATED)) {
+       if (!sup_dev &&
+           (of_node_check_flag(sup_np, OF_POPULATED) ||
+            sup_np->fwnode.flags & FWNODE_FLAG_NOT_DEVICE)) {
                pr_debug("Not linking %pOFP to %pOFP - No struct device\n",
                         con_np, sup_np);
                of_node_put(sup_np);
@@ -1232,6 +1235,7 @@ static struct device_node *parse_##fname(struct device_node *np,       \
 struct supplier_bindings {
        struct device_node *(*parse_prop)(struct device_node *np,
                                          const char *prop_name, int index);
+       bool optional;
 };
 
 DEFINE_SIMPLE_PROP(clocks, "clocks", "#clock-cells")
@@ -1244,8 +1248,6 @@ DEFINE_SIMPLE_PROP(dmas, "dmas", "#dma-cells")
 DEFINE_SIMPLE_PROP(power_domains, "power-domains", "#power-domain-cells")
 DEFINE_SIMPLE_PROP(hwlocks, "hwlocks", "#hwlock-cells")
 DEFINE_SIMPLE_PROP(extcon, "extcon", NULL)
-DEFINE_SIMPLE_PROP(interrupts_extended, "interrupts-extended",
-                                       "#interrupt-cells")
 DEFINE_SIMPLE_PROP(nvmem_cells, "nvmem-cells", NULL)
 DEFINE_SIMPLE_PROP(phys, "phys", "#phy-cells")
 DEFINE_SIMPLE_PROP(wakeup_parent, "wakeup-parent", NULL)
@@ -1271,19 +1273,55 @@ static struct device_node *parse_iommu_maps(struct device_node *np,
        return of_parse_phandle(np, prop_name, (index * 4) + 1);
 }
 
+static struct device_node *parse_gpio_compat(struct device_node *np,
+                                            const char *prop_name, int index)
+{
+       struct of_phandle_args sup_args;
+
+       if (strcmp(prop_name, "gpio") && strcmp(prop_name, "gpios"))
+               return NULL;
+
+       /*
+        * Ignore node with gpio-hog property since its gpios are all provided
+        * by its parent.
+        */
+       if (of_find_property(np, "gpio-hog", NULL))
+               return NULL;
+
+       if (of_parse_phandle_with_args(np, prop_name, "#gpio-cells", index,
+                                      &sup_args))
+               return NULL;
+
+       return sup_args.np;
+}
+
+static struct device_node *parse_interrupts(struct device_node *np,
+                                           const char *prop_name, int index)
+{
+       struct of_phandle_args sup_args;
+
+       if (!IS_ENABLED(CONFIG_OF_IRQ) || IS_ENABLED(CONFIG_PPC))
+               return NULL;
+
+       if (strcmp(prop_name, "interrupts") &&
+           strcmp(prop_name, "interrupts-extended"))
+               return NULL;
+
+       return of_irq_parse_one(np, index, &sup_args) ? NULL : sup_args.np;
+}
+
 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_iommus, .optional = true, },
+       { .parse_prop = parse_iommu_maps, .optional = true, },
        { .parse_prop = parse_mboxes, },
        { .parse_prop = parse_io_channels, },
        { .parse_prop = parse_interrupt_parent, },
-       { .parse_prop = parse_dmas, },
+       { .parse_prop = parse_dmas, .optional = true, },
        { .parse_prop = parse_power_domains, },
        { .parse_prop = parse_hwlocks, },
        { .parse_prop = parse_extcon, },
-       { .parse_prop = parse_interrupts_extended, },
        { .parse_prop = parse_nvmem_cells, },
        { .parse_prop = parse_phys, },
        { .parse_prop = parse_wakeup_parent, },
@@ -1296,6 +1334,8 @@ static const struct supplier_bindings of_supplier_bindings[] = {
        { .parse_prop = parse_pinctrl6, },
        { .parse_prop = parse_pinctrl7, },
        { .parse_prop = parse_pinctrl8, },
+       { .parse_prop = parse_gpio_compat, },
+       { .parse_prop = parse_interrupts, },
        { .parse_prop = parse_regulators, },
        { .parse_prop = parse_gpio, },
        { .parse_prop = parse_gpios, },
@@ -1332,6 +1372,11 @@ static int of_link_property(struct device_node *con_np, const char *prop_name)
 
        /* Do not stop at first failed link, link all available suppliers. */
        while (!matched && s->parse_prop) {
+               if (s->optional && !fw_devlink_is_strict()) {
+                       s++;
+                       continue;
+               }
+
                while ((phandle = s->parse_prop(con_np, prop_name, i))) {
                        matched = true;
                        i++;
index c35249497b9b81302d16d474310d192cdccd9fe3..22e86ae4dd5a829ebbef4fbdf346d18315446601 100644 (file)
@@ -298,7 +298,7 @@ struct dentry *debugfs_lookup(const char *name, struct dentry *parent)
 {
        struct dentry *dentry;
 
-       if (IS_ERR(parent))
+       if (!debugfs_initialized() || IS_ERR_OR_NULL(name) || IS_ERR(parent))
                return NULL;
 
        if (!parent)
@@ -319,6 +319,9 @@ static struct dentry *start_creating(const char *name, struct dentry *parent)
        if (!(debugfs_allow & DEBUGFS_ALLOW_API))
                return ERR_PTR(-EPERM);
 
+       if (!debugfs_initialized())
+               return ERR_PTR(-ENOENT);
+
        pr_debug("creating file '%s'\n", name);
 
        if (IS_ERR(parent))
index 1779f90eeb4cb4b3daf2a99b2246148de0c407a7..7619a84f8ce4abef94579bb1881bb95b8c08e834 100644 (file)
@@ -323,6 +323,7 @@ enum device_link_state {
  * AUTOPROBE_CONSUMER: Probe consumer driver automatically after supplier binds.
  * MANAGED: The core tracks presence of supplier/consumer drivers (internal).
  * SYNC_STATE_ONLY: Link only affects sync_state() behavior.
+ * INFERRED: Inferred from data (eg: firmware) and not from driver actions.
  */
 #define DL_FLAG_STATELESS              BIT(0)
 #define DL_FLAG_AUTOREMOVE_CONSUMER    BIT(1)
@@ -332,6 +333,7 @@ enum device_link_state {
 #define DL_FLAG_AUTOPROBE_CONSUMER     BIT(5)
 #define DL_FLAG_MANAGED                        BIT(6)
 #define DL_FLAG_SYNC_STATE_ONLY                BIT(7)
+#define DL_FLAG_INFERRED               BIT(8)
 
 /**
  * enum dl_dev_state - Device driver presence tracking information.
index ee7ba5b5417e510580f8444d4c77d9ca38e93b96..a498ebcf49933d309fff25b724d1a9244b807b3d 100644 (file)
@@ -75,7 +75,7 @@ enum probe_type {
  * @resume:    Called to bring a device from sleep mode.
  * @groups:    Default attributes that get created by the driver core
  *             automatically.
- * @dev_groups:        Additional attributes attached to device instance once the
+ * @dev_groups:        Additional attributes attached to device instance once
  *             it is bound to the driver.
  * @pm:                Power management operations of the device which matched
  *             this driver.
index 77414e431e89b5ecf360f188c44ca92fa4f9dd11..ed4e67a7ff1c4bfe9992dee723190cd0a2719fe4 100644 (file)
@@ -11,6 +11,7 @@
 
 #include <linux/types.h>
 #include <linux/list.h>
+#include <linux/err.h>
 
 struct fwnode_operations;
 struct device;
@@ -18,9 +19,13 @@ struct device;
 /*
  * fwnode link flags
  *
- * LINKS_ADDED: The fwnode has already be parsed to add fwnode links.
+ * LINKS_ADDED:        The fwnode has already be parsed to add fwnode links.
+ * NOT_DEVICE: The fwnode will never be populated as a struct device.
+ * INITIALIZED: The hardware corresponding to fwnode has been initialized.
  */
 #define FWNODE_FLAG_LINKS_ADDED                BIT(0)
+#define FWNODE_FLAG_NOT_DEVICE         BIT(1)
+#define FWNODE_FLAG_INITIALIZED                BIT(2)
 
 struct fwnode_handle {
        struct fwnode_handle *secondary;
@@ -166,7 +171,20 @@ static inline void fwnode_init(struct fwnode_handle *fwnode,
        INIT_LIST_HEAD(&fwnode->suppliers);
 }
 
+static inline void fwnode_dev_initialized(struct fwnode_handle *fwnode,
+                                         bool initialized)
+{
+       if (IS_ERR_OR_NULL(fwnode))
+               return;
+
+       if (initialized)
+               fwnode->flags |= FWNODE_FLAG_INITIALIZED;
+       else
+               fwnode->flags &= ~FWNODE_FLAG_INITIALIZED;
+}
+
 extern u32 fw_devlink_get_flags(void);
+extern bool fw_devlink_is_strict(void);
 int fwnode_link_add(struct fwnode_handle *con, struct fwnode_handle *sup);
 void fwnode_links_purge(struct fwnode_handle *fwnode);
 
index e8b78139f78c5a949f23782ecc7ea30252e6731b..aaf219bd0354c9b9336ecd18cfd90d418508807c 100644 (file)
@@ -33,8 +33,6 @@ static inline int of_irq_parse_oldworld(struct device_node *device, int index,
 #endif /* CONFIG_PPC32 && CONFIG_PPC_PMAC */
 
 extern int of_irq_parse_raw(const __be32 *addr, struct of_phandle_args *out_irq);
-extern int of_irq_parse_one(struct device_node *device, int index,
-                         struct of_phandle_args *out_irq);
 extern unsigned int irq_create_of_mapping(struct of_phandle_args *irq_data);
 extern int of_irq_to_resource(struct device_node *dev, int index,
                              struct resource *r);
@@ -42,6 +40,8 @@ extern int of_irq_to_resource(struct device_node *dev, int index,
 extern void of_irq_init(const struct of_device_id *matches);
 
 #ifdef CONFIG_OF_IRQ
+extern int of_irq_parse_one(struct device_node *device, int index,
+                         struct of_phandle_args *out_irq);
 extern int of_irq_count(struct device_node *dev);
 extern int of_irq_get(struct device_node *dev, int index);
 extern int of_irq_get_byname(struct device_node *dev, const char *name);
@@ -57,6 +57,11 @@ extern struct irq_domain *of_msi_map_get_device_domain(struct device *dev,
 extern void of_msi_configure(struct device *dev, struct device_node *np);
 u32 of_msi_map_id(struct device *dev, struct device_node *msi_np, u32 id_in);
 #else
+static inline int of_irq_parse_one(struct device_node *device, int index,
+                                  struct of_phandle_args *out_irq)
+{
+       return -EINVAL;
+}
 static inline int of_irq_count(struct device_node *dev)
 {
        return 0;
index 6aacd342cd14cb88a6e305f4d2473df1bcc5de3b..288151393a0653978d38facd20b30e7b28632372 100644 (file)
@@ -205,6 +205,7 @@ struct irq_domain *__irq_domain_add(struct fwnode_handle *fwnode, int size,
        }
 
        fwnode_handle_get(fwnode);
+       fwnode_dev_initialized(fwnode, true);
 
        /* Fill structure */
        INIT_RADIX_TREE(&domain->revmap_tree, GFP_KERNEL);
@@ -253,6 +254,7 @@ void irq_domain_remove(struct irq_domain *domain)
 
        pr_debug("Removed domain %s\n", domain->name);
 
+       fwnode_dev_initialized(domain->fwnode, false);
        fwnode_handle_put(domain->fwnode);
        if (domain->flags & IRQ_DOMAIN_NAME_ALLOCATED)
                kfree(domain->name);