Merge tag 'driver-core-5.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git...
authorLinus Torvalds <torvalds@linux-foundation.org>
Mon, 26 Apr 2021 18:05:36 +0000 (11:05 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Mon, 26 Apr 2021 18:05:36 +0000 (11:05 -0700)
Pull driver core updates from Greg KH:
 "Here is the "big" set of driver core changes for 5.13-rc1.

  Nothing major, just lots of little core changes and cleanups, notable
  things are:

   - finally set 'fw_devlink=on' by default.

     All reported issues with this have been shaken out over the past 9
     months or so, but we will be paying attention to any fallout here
     in case we need to revert this as the default boot value (symptoms
     of problems are a simple lack of booting)

   - fixes found to be needed by fw_devlink=on value in some subsystems
     (like clock).

   - delayed work initialization cleanup

   - driver core cleanups and minor updates

   - software node cleanups and tweaks

   - devtmpfs cleanups

   - minor debugfs cleanups

  All of these have been in linux-next for a while with no reported
  issues"

* tag 'driver-core-5.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core: (53 commits)
  devm-helpers: Fix devm_delayed_work_autocancel() kerneldoc
  PM / wakeup: use dev_set_name() directly
  software node: Allow node addition to already existing device
  kunit: software node: adhear to KUNIT formatting standard
  node: fix device cleanups in error handling code
  kobject_uevent: remove warning in init_uevent_argv()
  debugfs: Make debugfs_allow RO after init
  Revert "driver core: platform: Make platform_get_irq_optional() optional"
  media: ipu3-cio2: Switch to use SOFTWARE_NODE_REFERENCE()
  software node: Introduce SOFTWARE_NODE_REFERENCE() helper macro
  software node: Imply kobj_to_swnode() to be no-op
  software node: Deduplicate code in fwnode_create_software_node()
  software node: Introduce software_node_alloc()/software_node_free()
  software node: Free resources explicitly when swnode_register() fails
  debugfs: drop pointless nul-termination in debugfs_read_file_bool()
  driver core: add helper for deferred probe reason setting
  driver core: Improve fw_devlink & deferred_probe_timeout interaction
  of: property: fw_devlink: Add support for remote-endpoint
  driver core: platform: Make platform_get_irq_optional() optional
  driver core: Replace printf() specifier and drop unneeded casting
  ...

42 files changed:
MAINTAINERS
drivers/base/attribute_container.c
drivers/base/auxiliary.c
drivers/base/base.h
drivers/base/component.c
drivers/base/core.c
drivers/base/cpu.c
drivers/base/dd.c
drivers/base/devcoredump.c
drivers/base/devres.c
drivers/base/devtmpfs.c
drivers/base/node.c
drivers/base/platform-msi.c
drivers/base/platform.c
drivers/base/power/wakeup_stats.c
drivers/base/swnode.c
drivers/base/test/Kconfig
drivers/base/test/Makefile
drivers/base/test/property-entry-test.c
drivers/clk/clk.c
drivers/extcon/extcon-gpio.c
drivers/extcon/extcon-intel-int3496.c
drivers/extcon/extcon-palmas.c
drivers/extcon/extcon-qcom-spmi-misc.c
drivers/hwmon/raspberrypi-hwmon.c
drivers/media/pci/intel/ipu3/cio2-bridge.c
drivers/of/property.c
drivers/platform/x86/gpd-pocket-fan.c
drivers/power/supply/axp20x_usb_power.c
drivers/power/supply/bq24735-charger.c
drivers/power/supply/ltc2941-battery-gauge.c
drivers/power/supply/sbs-battery.c
drivers/regulator/qcom_spmi-regulator.c
drivers/watchdog/retu_wdt.c
fs/debugfs/file.c
fs/debugfs/inode.c
include/linux/device.h
include/linux/devm-helpers.h [new file with mode: 0644]
include/linux/platform_device.h
include/linux/property.h
lib/kobject_uevent.c
tools/testing/selftests/firmware/fw_namespace.c

index 56defe56d421a3cdde26ca0d2069950b0ea1437c..502a7d570d117b57befc6f420e05f5eefecf65e6 100644 (file)
@@ -5197,6 +5197,12 @@ M:       Torben Mathiasen <device@lanana.org>
 S:     Maintained
 W:     http://lanana.org/docs/device-list/index.html
 
+DEVICE RESOURCE MANAGEMENT HELPERS
+M:     Hans de Goede <hdegoede@redhat.com>
+R:     Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
+S:     Maintained
+F:     include/linux/devm-helpers.h
+
 DEVICE-MAPPER  (LVM)
 M:     Alasdair Kergon <agk@redhat.com>
 M:     Mike Snitzer <snitzer@redhat.com>
index f7bd0f4db13d832788e839d965c7f0bd7c8efb4e..9c00d203d61e3d1444011e7c7a83728813a70aea 100644 (file)
@@ -461,6 +461,10 @@ attribute_container_add_class_device(struct device *classdev)
 /**
  * attribute_container_add_class_device_adapter - simple adapter for triggers
  *
+ * @cont: the container to register.
+ * @dev:  the generic device to activate the trigger for
+ * @classdev:  the class device to add
+ *
  * This function is identical to attribute_container_add_class_device except
  * that it is designed to be called from the triggers
  */
index d8b314e7d0fdc88cfa33f37f2ccefdd329cb286e..adc199dfba3cb3ff3e03ea3c98ae4645799fa442 100644 (file)
@@ -265,8 +265,3 @@ void __init auxiliary_bus_init(void)
 {
        WARN_ON(bus_register(&auxiliary_bus_type));
 }
-
-MODULE_LICENSE("GPL v2");
-MODULE_DESCRIPTION("Auxiliary Bus");
-MODULE_AUTHOR("David Ertman <david.m.ertman@intel.com>");
-MODULE_AUTHOR("Kiran Patil <kiran.patil@intel.com>");
index 52b3d7b75c2750e85c0b5f1b238964dd42cb5fa9..e5f9b7e656c34b385ef2aa2a309a702c587b6388 100644 (file)
@@ -185,11 +185,13 @@ extern int device_links_read_lock(void);
 extern void device_links_read_unlock(int idx);
 extern int device_links_read_lock_held(void);
 extern int device_links_check_suppliers(struct device *dev);
+extern void device_links_force_bind(struct device *dev);
 extern void device_links_driver_bound(struct device *dev);
 extern void device_links_driver_cleanup(struct device *dev);
 extern void device_links_no_driver(struct device *dev);
 extern bool device_links_busy(struct device *dev);
 extern void device_links_unbind_consumers(struct device *dev);
+extern void fw_devlink_drivers_done(void);
 
 /* device pm support */
 void device_pm_move_to_tail(struct device *dev);
index dcfbe7251dc43b86edd347c371c306d70d32e39d..272ba42392f0c0d953ea593dd26421f9f2f1e5a6 100644 (file)
@@ -65,7 +65,6 @@ struct master {
        const struct component_master_ops *ops;
        struct device *dev;
        struct component_match *match;
-       struct dentry *dentry;
 };
 
 struct component {
@@ -125,15 +124,13 @@ core_initcall(component_debug_init);
 
 static void component_master_debugfs_add(struct master *m)
 {
-       m->dentry = debugfs_create_file(dev_name(m->dev), 0444,
-                                       component_debugfs_dir,
-                                       m, &component_devices_fops);
+       debugfs_create_file(dev_name(m->dev), 0444, component_debugfs_dir, m,
+                           &component_devices_fops);
 }
 
 static void component_master_debugfs_del(struct master *m)
 {
-       debugfs_remove(m->dentry);
-       m->dentry = NULL;
+       debugfs_remove(debugfs_lookup(dev_name(m->dev), component_debugfs_dir));
 }
 
 #else
index f29839382f8166b4106e96eb9a0cc41f1e2d7b34..4a8bf8cda52bc77243fcffb8af554309aeed01bb 100644 (file)
@@ -51,6 +51,7 @@ static LIST_HEAD(deferred_sync);
 static unsigned int defer_sync_state_count = 1;
 static DEFINE_MUTEX(fwnode_link_lock);
 static bool fw_devlink_is_permissive(void);
+static bool fw_devlink_drv_reg_done;
 
 /**
  * fwnode_link_add - Create a link between two fwnode_handles.
@@ -1153,6 +1154,41 @@ static ssize_t waiting_for_supplier_show(struct device *dev,
 }
 static DEVICE_ATTR_RO(waiting_for_supplier);
 
+/**
+ * device_links_force_bind - Prepares device to be force bound
+ * @dev: Consumer device.
+ *
+ * device_bind_driver() force binds a device to a driver without calling any
+ * driver probe functions. So the consumer really isn't going to wait for any
+ * supplier before it's bound to the driver. We still want the device link
+ * states to be sensible when this happens.
+ *
+ * In preparation for device_bind_driver(), this function goes through each
+ * supplier device links and checks if the supplier is bound. If it is, then
+ * the device link status is set to CONSUMER_PROBE. Otherwise, the device link
+ * is dropped. Links without the DL_FLAG_MANAGED flag set are ignored.
+ */
+void device_links_force_bind(struct device *dev)
+{
+       struct device_link *link, *ln;
+
+       device_links_write_lock();
+
+       list_for_each_entry_safe(link, ln, &dev->links.suppliers, c_node) {
+               if (!(link->flags & DL_FLAG_MANAGED))
+                       continue;
+
+               if (link->status != DL_STATE_AVAILABLE) {
+                       device_link_drop_managed(link);
+                       continue;
+               }
+               WRITE_ONCE(link->status, DL_STATE_CONSUMER_PROBE);
+       }
+       dev->links.status = DL_DEV_PROBING;
+
+       device_links_write_unlock();
+}
+
 /**
  * device_links_driver_bound - Update device links after probing its driver.
  * @dev: Device to update the links for.
@@ -1503,7 +1539,7 @@ static void device_links_purge(struct device *dev)
 #define FW_DEVLINK_FLAGS_RPM           (FW_DEVLINK_FLAGS_ON | \
                                         DL_FLAG_PM_RUNTIME)
 
-static u32 fw_devlink_flags = FW_DEVLINK_FLAGS_PERMISSIVE;
+static u32 fw_devlink_flags = FW_DEVLINK_FLAGS_ON;
 static int __init fw_devlink_setup(char *arg)
 {
        if (!arg)
@@ -1563,6 +1599,52 @@ static void fw_devlink_parse_fwtree(struct fwnode_handle *fwnode)
                fw_devlink_parse_fwtree(child);
 }
 
+static void fw_devlink_relax_link(struct device_link *link)
+{
+       if (!(link->flags & DL_FLAG_INFERRED))
+               return;
+
+       if (link->flags == (DL_FLAG_MANAGED | FW_DEVLINK_FLAGS_PERMISSIVE))
+               return;
+
+       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));
+}
+
+static int fw_devlink_no_driver(struct device *dev, void *data)
+{
+       struct device_link *link = to_devlink(dev);
+
+       if (!link->supplier->can_match)
+               fw_devlink_relax_link(link);
+
+       return 0;
+}
+
+void fw_devlink_drivers_done(void)
+{
+       fw_devlink_drv_reg_done = true;
+       device_links_write_lock();
+       class_for_each_device(&devlink_class, NULL, NULL,
+                             fw_devlink_no_driver);
+       device_links_write_unlock();
+}
+
+static void fw_devlink_unblock_consumers(struct device *dev)
+{
+       struct device_link *link;
+
+       if (!fw_devlink_flags || fw_devlink_is_permissive())
+               return;
+
+       device_links_write_lock();
+       list_for_each_entry(link, &dev->links.consumers, s_node)
+               fw_devlink_relax_link(link);
+       device_links_write_unlock();
+}
+
 /**
  * fw_devlink_relax_cycle - Convert cyclic links to SYNC_STATE_ONLY links
  * @con: Device to check dependencies for.
@@ -1599,21 +1681,16 @@ static int fw_devlink_relax_cycle(struct device *con, void *sup)
 
                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));
+               fw_devlink_relax_link(link);
        }
        return ret;
 }
 
 /**
  * fw_devlink_create_devlink - Create a device link from a consumer to fwnode
- * @con - Consumer device for the device link
- * @sup_handle - fwnode handle of supplier
+ * @con: consumer device for the device link
+ * @sup_handle: fwnode handle of supplier
+ * @flags: devlink flags
  *
  * This function will try to create a device link between the consumer device
  * @con and the supplier device represented by @sup_handle.
@@ -1709,7 +1786,7 @@ out:
 
 /**
  * __fw_devlink_link_to_consumers - Create device links to consumers of a device
- * @dev - Device that needs to be linked to its consumers
+ * @dev: Device that needs to be linked to its consumers
  *
  * This function looks at all the consumer fwnodes of @dev and creates device
  * links between the consumer device and @dev (supplier).
@@ -1779,8 +1856,8 @@ static void __fw_devlink_link_to_consumers(struct device *dev)
 
 /**
  * __fw_devlink_link_to_suppliers - Create device links to suppliers of a device
- * @dev - The consumer device that needs to be linked to its suppliers
- * @fwnode - Root of the fwnode tree that is used to create device links
+ * @dev: The consumer device that needs to be linked to its suppliers
+ * @fwnode: Root of the fwnode tree that is used to create device links
  *
  * This function looks at all the supplier fwnodes of fwnode tree rooted at
  * @fwnode and creates device links between @dev (consumer) and all the
@@ -3240,6 +3317,15 @@ int device_add(struct device *dev)
        }
 
        bus_probe_device(dev);
+
+       /*
+        * If all driver registration is done and a newly added device doesn't
+        * match with any driver, don't block its consumers from probing in
+        * case the consumer device is able to operate without this supplier.
+        */
+       if (dev->fwnode && fw_devlink_drv_reg_done && !dev->can_match)
+               fw_devlink_unblock_consumers(dev);
+
        if (parent)
                klist_add_tail(&dev->p->knode_parent,
                               &parent->p->klist_children);
index 8f1d6569564c4099deea31b1ebfd2371481ee36a..2b9e41377a0705a5743775ba6d8837b3a190da13 100644 (file)
@@ -409,13 +409,11 @@ __cpu_device_create(struct device *parent, void *drvdata,
                    const char *fmt, va_list args)
 {
        struct device *dev = NULL;
-       int retval = -ENODEV;
+       int retval = -ENOMEM;
 
        dev = kzalloc(sizeof(*dev), GFP_KERNEL);
-       if (!dev) {
-               retval = -ENOMEM;
+       if (!dev)
                goto error;
-       }
 
        device_initialize(dev);
        dev->parent = parent;
index 37a5e5f8b2219e3a50d7d587a6d94c9d8da35971..ecd7cf848daff7ea3522cb18f568f2203c5a522b 100644 (file)
@@ -55,7 +55,6 @@ static DEFINE_MUTEX(deferred_probe_mutex);
 static LIST_HEAD(deferred_probe_pending_list);
 static LIST_HEAD(deferred_probe_active_list);
 static atomic_t deferred_trigger_count = ATOMIC_INIT(0);
-static struct dentry *deferred_devices;
 static bool initcalls_done;
 
 /* Save the async probe drivers' name from kernel cmdline */
@@ -69,6 +68,12 @@ static char async_probe_drv_names[ASYNC_DRV_NAMES_MAX_LEN];
  */
 static bool defer_all_probes;
 
+static void __device_set_deferred_probe_reason(const struct device *dev, char *reason)
+{
+       kfree(dev->p->deferred_probe_reason);
+       dev->p->deferred_probe_reason = reason;
+}
+
 /*
  * deferred_probe_work_func() - Retry probing devices in the active list.
  */
@@ -97,8 +102,7 @@ static void deferred_probe_work_func(struct work_struct *work)
 
                get_device(dev);
 
-               kfree(dev->p->deferred_probe_reason);
-               dev->p->deferred_probe_reason = NULL;
+               __device_set_deferred_probe_reason(dev, NULL);
 
                /*
                 * Drop the mutex while probing each device; the probe path may
@@ -126,6 +130,9 @@ static DECLARE_WORK(deferred_probe_work, deferred_probe_work_func);
 
 void driver_deferred_probe_add(struct device *dev)
 {
+       if (!dev->can_match)
+               return;
+
        mutex_lock(&deferred_probe_mutex);
        if (list_empty(&dev->p->deferred_probe)) {
                dev_dbg(dev, "Added to deferred list\n");
@@ -140,8 +147,7 @@ void driver_deferred_probe_del(struct device *dev)
        if (!list_empty(&dev->p->deferred_probe)) {
                dev_dbg(dev, "Removed from deferred list\n");
                list_del_init(&dev->p->deferred_probe);
-               kfree(dev->p->deferred_probe_reason);
-               dev->p->deferred_probe_reason = NULL;
+               __device_set_deferred_probe_reason(dev, NULL);
        }
        mutex_unlock(&deferred_probe_mutex);
 }
@@ -185,7 +191,7 @@ static void driver_deferred_probe_trigger(void)
         * Kick the re-probe thread.  It may already be scheduled, but it is
         * safe to kick it again.
         */
-       schedule_work(&deferred_probe_work);
+       queue_work(system_unbound_wq, &deferred_probe_work);
 }
 
 /**
@@ -220,11 +226,12 @@ void device_unblock_probing(void)
 void device_set_deferred_probe_reason(const struct device *dev, struct va_format *vaf)
 {
        const char *drv = dev_driver_string(dev);
+       char *reason;
 
        mutex_lock(&deferred_probe_mutex);
 
-       kfree(dev->p->deferred_probe_reason);
-       dev->p->deferred_probe_reason = kasprintf(GFP_KERNEL, "%s: %pV", drv, vaf);
+       reason = kasprintf(GFP_KERNEL, "%s: %pV", drv, vaf);
+       __device_set_deferred_probe_reason(dev, reason);
 
        mutex_unlock(&deferred_probe_mutex);
 }
@@ -294,6 +301,8 @@ static void deferred_probe_timeout_work_func(struct work_struct *work)
 {
        struct device_private *p;
 
+       fw_devlink_drivers_done();
+
        driver_deferred_probe_timeout = 0;
        driver_deferred_probe_trigger();
        flush_work(&deferred_probe_work);
@@ -315,8 +324,8 @@ static DECLARE_DELAYED_WORK(deferred_probe_timeout_work, deferred_probe_timeout_
  */
 static int deferred_probe_initcall(void)
 {
-       deferred_devices = debugfs_create_file("devices_deferred", 0444, NULL,
-                                              NULL, &deferred_devs_fops);
+       debugfs_create_file("devices_deferred", 0444, NULL, NULL,
+                           &deferred_devs_fops);
 
        driver_deferred_probe_enable = true;
        driver_deferred_probe_trigger();
@@ -324,6 +333,9 @@ static int deferred_probe_initcall(void)
        flush_work(&deferred_probe_work);
        initcalls_done = true;
 
+       if (!IS_ENABLED(CONFIG_MODULES))
+               fw_devlink_drivers_done();
+
        /*
         * Trigger deferred probe again, this time we won't defer anything
         * that is optional
@@ -341,7 +353,7 @@ late_initcall(deferred_probe_initcall);
 
 static void __exit deferred_probe_exit(void)
 {
-       debugfs_remove_recursive(deferred_devices);
+       debugfs_remove_recursive(debugfs_lookup("devices_deferred", NULL));
 }
 __exitcall(deferred_probe_exit);
 
@@ -418,8 +430,11 @@ static int driver_sysfs_add(struct device *dev)
        if (ret)
                goto rm_dev;
 
-       if (!IS_ENABLED(CONFIG_DEV_COREDUMP) || !dev->driver->coredump ||
-           !device_create_file(dev, &dev_attr_coredump))
+       if (!IS_ENABLED(CONFIG_DEV_COREDUMP) || !dev->driver->coredump)
+               return 0;
+
+       ret = device_create_file(dev, &dev_attr_coredump);
+       if (!ret)
                return 0;
 
        sysfs_remove_link(&dev->kobj, "driver");
@@ -462,8 +477,10 @@ int device_bind_driver(struct device *dev)
        int ret;
 
        ret = driver_sysfs_add(dev);
-       if (!ret)
+       if (!ret) {
+               device_links_force_bind(dev);
                driver_bound(dev);
+       }
        else if (dev->bus)
                blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
                                             BUS_NOTIFY_DRIVER_NOT_BOUND, dev);
@@ -731,6 +748,7 @@ static int driver_probe_device(struct device_driver *drv, struct device *dev)
        if (!device_is_registered(dev))
                return -ENODEV;
 
+       dev->can_match = true;
        pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
                 drv->bus->name, __func__, dev_name(dev), drv->name);
 
@@ -834,6 +852,7 @@ static int __device_attach_driver(struct device_driver *drv, void *_data)
                return 0;
        } else if (ret == -EPROBE_DEFER) {
                dev_dbg(dev, "Device match requests probe deferral\n");
+               dev->can_match = true;
                driver_deferred_probe_add(dev);
        } else if (ret < 0) {
                dev_dbg(dev, "Bus failed to match device: %d\n", ret);
@@ -1069,6 +1088,7 @@ static int __driver_attach(struct device *dev, void *data)
                return 0;
        } else if (ret == -EPROBE_DEFER) {
                dev_dbg(dev, "Device match requests probe deferral\n");
+               dev->can_match = true;
                driver_deferred_probe_add(dev);
        } else if (ret < 0) {
                dev_dbg(dev, "Bus failed to match device: %d\n", ret);
index 9243468e2c99fd7b28541fe34d860eb0b0c4da23..8eec0e0ddff7b1ec40b585daed4977bf13914425 100644 (file)
@@ -202,7 +202,7 @@ static int devcd_match_failing(struct device *dev, const void *failing)
  * NOTE: if two tables allocated with devcd_alloc_sgtable and then chained
  * using the sg_chain function then that function should be called only once
  * on the chained table
- * @table: pointer to sg_table to free
+ * @data: pointer to sg_table to free
  */
 static void devcd_free_sgtable(void *data)
 {
@@ -210,7 +210,7 @@ static void devcd_free_sgtable(void *data)
 }
 
 /**
- * devcd_read_from_table - copy data from sg_table to a given buffer
+ * devcd_read_from_sgtable - copy data from sg_table to a given buffer
  * and return the number of bytes read
  * @buffer: the buffer to copy the data to it
  * @buf_len: the length of the buffer
@@ -292,13 +292,16 @@ void dev_coredumpm(struct device *dev, struct module *owner,
        if (device_add(&devcd->devcd_dev))
                goto put_device;
 
+       /*
+        * These should normally not fail, but there is no problem
+        * continuing without the links, so just warn instead of
+        * failing.
+        */
        if (sysfs_create_link(&devcd->devcd_dev.kobj, &dev->kobj,
-                             "failing_device"))
-               /* nothing - symlink will be missing */;
-
-       if (sysfs_create_link(&dev->kobj, &devcd->devcd_dev.kobj,
-                             "devcoredump"))
-               /* nothing - symlink will be missing */;
+                             "failing_device") ||
+           sysfs_create_link(&dev->kobj, &devcd->devcd_dev.kobj,
+                             "devcoredump"))
+               dev_warn(dev, "devcoredump create_link failed\n");
 
        INIT_DELAYED_WORK(&devcd->del_wk, devcd_del);
        schedule_delayed_work(&devcd->del_wk, DEVCD_TIMEOUT);
index fb9d5289a620338e42cf14e9f40046794c9f2d7c..8746f2212781b5153afa2307aee5dc6493248250 100644 (file)
@@ -58,8 +58,8 @@ static void devres_log(struct device *dev, struct devres_node *node,
                       const char *op)
 {
        if (unlikely(log_devres))
-               dev_err(dev, "DEVRES %3s %p %s (%lu bytes)\n",
-                       op, node, node->name, (unsigned long)node->size);
+               dev_err(dev, "DEVRES %3s %p %s (%zu bytes)\n",
+                       op, node, node->name, node->size);
 }
 #else /* CONFIG_DEBUG_DEVRES */
 #define set_node_dbginfo(node, n, s)   do {} while (0)
@@ -1228,6 +1228,6 @@ EXPORT_SYMBOL_GPL(__devm_alloc_percpu);
 void devm_free_percpu(struct device *dev, void __percpu *pdata)
 {
        WARN_ON(devres_destroy(dev, devm_percpu_release, devm_percpu_match,
-                              (void *)pdata));
+                              (__force void *)pdata));
 }
 EXPORT_SYMBOL_GPL(devm_free_percpu);
index 653c8c6ac7a73a2137598065b79a078fdaaf1841..8be352ab4ddbfdcfdda4885dde97c39b5477cf32 100644 (file)
@@ -371,7 +371,7 @@ int __init devtmpfs_mount(void)
        return err;
 }
 
-static DECLARE_COMPLETION(setup_done);
+static __initdata DECLARE_COMPLETION(setup_done);
 
 static int handle(const char *name, umode_t mode, kuid_t uid, kgid_t gid,
                  struct device *dev)
@@ -405,7 +405,7 @@ static void __noreturn devtmpfs_work_loop(void)
        }
 }
 
-static int __init devtmpfs_setup(void *p)
+static noinline int __init devtmpfs_setup(void *p)
 {
        int err;
 
@@ -419,7 +419,6 @@ static int __init devtmpfs_setup(void *p)
        init_chroot(".");
 out:
        *(int *)p = err;
-       complete(&setup_done);
        return err;
 }
 
@@ -432,6 +431,7 @@ static int __ref devtmpfsd(void *p)
 {
        int err = devtmpfs_setup(p);
 
+       complete(&setup_done);
        if (err)
                return err;
        devtmpfs_work_loop();
index f449dbb2c74666a83caabb55b39e8c9de31a622c..2c36f61d30bcb0d7c6b315c6dfcd8e1ee492722e 100644 (file)
@@ -268,21 +268,20 @@ static void node_init_cache_dev(struct node *node)
        if (!dev)
                return;
 
+       device_initialize(dev);
        dev->parent = &node->dev;
        dev->release = node_cache_release;
        if (dev_set_name(dev, "memory_side_cache"))
-               goto free_dev;
+               goto put_device;
 
-       if (device_register(dev))
-               goto free_name;
+       if (device_add(dev))
+               goto put_device;
 
        pm_runtime_no_callbacks(dev);
        node->cache_dev = dev;
        return;
-free_name:
-       kfree_const(dev->kobj.name);
-free_dev:
-       kfree(dev);
+put_device:
+       put_device(dev);
 }
 
 /**
@@ -319,25 +318,24 @@ void node_add_cache(unsigned int nid, struct node_cache_attrs *cache_attrs)
                return;
 
        dev = &info->dev;
+       device_initialize(dev);
        dev->parent = node->cache_dev;
        dev->release = node_cacheinfo_release;
        dev->groups = cache_groups;
        if (dev_set_name(dev, "index%d", cache_attrs->level))
-               goto free_cache;
+               goto put_device;
 
        info->cache_attrs = *cache_attrs;
-       if (device_register(dev)) {
+       if (device_add(dev)) {
                dev_warn(&node->dev, "failed to add cache level:%d\n",
                         cache_attrs->level);
-               goto free_name;
+               goto put_device;
        }
        pm_runtime_no_callbacks(dev);
        list_add_tail(&info->node, &node->cache_attrs);
        return;
-free_name:
-       kfree_const(dev->kobj.name);
-free_cache:
-       kfree(info);
+put_device:
+       put_device(dev);
 }
 
 static void node_remove_caches(struct node *node)
index 2c1e2e0c1a59cc5f037f57aec963c8fcfa04a8e2..0b72b134a3048971345c96f0aedbe1fdbac5f671 100644 (file)
@@ -316,10 +316,11 @@ void *platform_msi_get_host_data(struct irq_domain *domain)
 }
 
 /**
- * platform_msi_create_device_domain - Create a platform-msi domain
+ * __platform_msi_create_device_domain - Create a platform-msi domain
  *
  * @dev:               The device generating the MSIs
  * @nvec:              The number of MSIs that need to be allocated
+ * @is_tree:           flag to indicate tree hierarchy
  * @write_msi_msg:     Callback to write an interrupt message for @dev
  * @ops:               The hierarchy domain operations to use
  * @host_data:         Private data associated to this domain
index 6e1f8e0b661cef101dc6d4a01ad56beea41345d5..9cd34def2237b8a27eb827863826dbe51cca6709 100644 (file)
@@ -192,7 +192,7 @@ int platform_get_irq_optional(struct platform_device *dev, unsigned int num)
 #ifdef CONFIG_SPARC
        /* sparc does not have irqs represented as IORESOURCE_IRQ resources */
        if (!dev || num >= dev->archdata.num_irqs)
-               return -ENXIO;
+               goto out_not_found;
        ret = dev->archdata.irqs[num];
        goto out;
 #else
@@ -223,10 +223,8 @@ int platform_get_irq_optional(struct platform_device *dev, unsigned int num)
                struct irq_data *irqd;
 
                irqd = irq_get_irq_data(r->start);
-               if (!irqd) {
-                       ret = -ENXIO;
-                       goto out;
-               }
+               if (!irqd)
+                       goto out_not_found;
                irqd_set_trigger_type(irqd, r->flags & IORESOURCE_BITS);
        }
 
@@ -249,8 +247,9 @@ int platform_get_irq_optional(struct platform_device *dev, unsigned int num)
                        goto out;
        }
 
-       ret = -ENXIO;
 #endif
+out_not_found:
+       ret = -ENXIO;
 out:
        WARN(ret == 0, "0 is an invalid IRQ number\n");
        return ret;
index d638259b829a5ef24303f874cbe2405722fbeae6..5ade7539ac0244beb20e5e71e10439ed0c5118ef 100644 (file)
@@ -154,7 +154,7 @@ static struct device *wakeup_source_device_create(struct device *parent,
        dev_set_drvdata(dev, ws);
        device_set_pm_not_required(dev);
 
-       retval = kobject_set_name(&dev->kobj, "wakeup%d", ws->id);
+       retval = dev_set_name(dev, "wakeup%d", ws->id);
        if (retval)
                goto error;
 
index fa3719ef80e4d12b93655189ed4cec22075a04e0..3cc11b813f28ca8ff6cb508b83cd3d1d2c996cce 100644 (file)
 #include <linux/slab.h>
 
 struct swnode {
-       int id;
        struct kobject kobj;
        struct fwnode_handle fwnode;
        const struct software_node *node;
+       int id;
 
        /* hierarchy */
        struct ida child_ids;
@@ -720,19 +720,30 @@ software_node_find_by_name(const struct software_node *parent, const char *name)
 }
 EXPORT_SYMBOL_GPL(software_node_find_by_name);
 
-static int
-software_node_register_properties(struct software_node *node,
-                                 const struct property_entry *properties)
+static struct software_node *software_node_alloc(const struct property_entry *properties)
 {
        struct property_entry *props;
+       struct software_node *node;
 
        props = property_entries_dup(properties);
        if (IS_ERR(props))
-               return PTR_ERR(props);
+               return ERR_CAST(props);
+
+       node = kzalloc(sizeof(*node), GFP_KERNEL);
+       if (!node) {
+               property_entries_free(props);
+               return ERR_PTR(-ENOMEM);
+       }
 
        node->properties = props;
 
-       return 0;
+       return node;
+}
+
+static void software_node_free(const struct software_node *node)
+{
+       property_entries_free(node->properties);
+       kfree(node);
 }
 
 static void software_node_release(struct kobject *kobj)
@@ -746,10 +757,9 @@ static void software_node_release(struct kobject *kobj)
                ida_simple_remove(&swnode_root_ids, swnode->id);
        }
 
-       if (swnode->allocated) {
-               property_entries_free(swnode->node->properties);
-               kfree(swnode->node);
-       }
+       if (swnode->allocated)
+               software_node_free(swnode->node);
+
        ida_destroy(&swnode->child_ids);
        kfree(swnode);
 }
@@ -767,22 +777,19 @@ swnode_register(const struct software_node *node, struct swnode *parent,
        int ret;
 
        swnode = kzalloc(sizeof(*swnode), GFP_KERNEL);
-       if (!swnode) {
-               ret = -ENOMEM;
-               goto out_err;
-       }
+       if (!swnode)
+               return ERR_PTR(-ENOMEM);
 
        ret = ida_simple_get(parent ? &parent->child_ids : &swnode_root_ids,
                             0, 0, GFP_KERNEL);
        if (ret < 0) {
                kfree(swnode);
-               goto out_err;
+               return ERR_PTR(ret);
        }
 
        swnode->id = ret;
        swnode->node = node;
        swnode->parent = parent;
-       swnode->allocated = allocated;
        swnode->kobj.kset = swnode_kset;
        fwnode_init(&swnode->fwnode, &software_node_ops);
 
@@ -803,16 +810,17 @@ swnode_register(const struct software_node *node, struct swnode *parent,
                return ERR_PTR(ret);
        }
 
+       /*
+        * Assign the flag only in the successful case, so
+        * the above kobject_put() won't mess up with properties.
+        */
+       swnode->allocated = allocated;
+
        if (parent)
                list_add_tail(&swnode->entry, &parent->children);
 
        kobject_uevent(&swnode->kobj, KOBJ_ADD);
        return &swnode->fwnode;
-
-out_err:
-       if (allocated)
-               property_entries_free(node->properties);
-       return ERR_PTR(ret);
 }
 
 /**
@@ -880,7 +888,11 @@ EXPORT_SYMBOL_GPL(software_node_unregister_nodes);
  * software_node_register_node_group - Register a group of software nodes
  * @node_group: NULL terminated array of software node pointers to be registered
  *
- * Register multiple software nodes at once.
+ * Register multiple software nodes at once. If any node in the array
+ * has its .parent pointer set (which can only be to another software_node),
+ * then its parent **must** have been registered before it is; either outside
+ * of this function or by ordering the array such that parent comes before
+ * child.
  */
 int software_node_register_node_group(const struct software_node **node_group)
 {
@@ -906,10 +918,14 @@ EXPORT_SYMBOL_GPL(software_node_register_node_group);
  * software_node_unregister_node_group - Unregister a group of software nodes
  * @node_group: NULL terminated array of software node pointers to be unregistered
  *
- * Unregister multiple software nodes at once. The array will be unwound in
- * reverse order (i.e. last entry first) and thus if any members of the array are
- * children of another member then the children must appear later in the list such
- * that they are unregistered first.
+ * Unregister multiple software nodes at once. If parent pointers are set up
+ * in any of the software nodes then the array **must** be ordered such that
+ * parents come before their children.
+ *
+ * NOTE: If you are uncertain whether the array is ordered such that
+ * parents will be unregistered before their children, it is wiser to
+ * remove the nodes individually, in the correct order (child before
+ * parent).
  */
 void software_node_unregister_node_group(
                const struct software_node **node_group)
@@ -963,31 +979,28 @@ struct fwnode_handle *
 fwnode_create_software_node(const struct property_entry *properties,
                            const struct fwnode_handle *parent)
 {
+       struct fwnode_handle *fwnode;
        struct software_node *node;
-       struct swnode *p = NULL;
-       int ret;
+       struct swnode *p;
 
-       if (parent) {
-               if (IS_ERR(parent))
-                       return ERR_CAST(parent);
-               if (!is_software_node(parent))
-                       return ERR_PTR(-EINVAL);
-               p = to_swnode(parent);
-       }
+       if (IS_ERR(parent))
+               return ERR_CAST(parent);
 
-       node = kzalloc(sizeof(*node), GFP_KERNEL);
-       if (!node)
-               return ERR_PTR(-ENOMEM);
+       p = to_swnode(parent);
+       if (parent && !p)
+               return ERR_PTR(-EINVAL);
 
-       ret = software_node_register_properties(node, properties);
-       if (ret) {
-               kfree(node);
-               return ERR_PTR(ret);
-       }
+       node = software_node_alloc(properties);
+       if (IS_ERR(node))
+               return ERR_CAST(node);
 
        node->parent = p ? p->node : NULL;
 
-       return swnode_register(node, p, 1);
+       fwnode = swnode_register(node, p, 1);
+       if (IS_ERR(fwnode))
+               software_node_free(node);
+
+       return fwnode;
 }
 EXPORT_SYMBOL_GPL(fwnode_create_software_node);
 
@@ -1032,6 +1045,7 @@ int device_add_software_node(struct device *dev, const struct software_node *nod
        }
 
        set_secondary_fwnode(dev, &swnode->fwnode);
+       software_node_notify(dev, KOBJ_ADD);
 
        return 0;
 }
@@ -1105,8 +1119,8 @@ int software_node_notify(struct device *dev, unsigned long action)
 
        switch (action) {
        case KOBJ_ADD:
-               ret = sysfs_create_link(&dev->kobj, &swnode->kobj,
-                                       "software_node");
+               ret = sysfs_create_link_nowarn(&dev->kobj, &swnode->kobj,
+                                              "software_node");
                if (ret)
                        break;
 
index ba225eb1b7615fb6a56996acd3c18cbba773afe8..2f3fa31a948e2e86f6e9753a304443110a29fcc6 100644 (file)
@@ -8,7 +8,7 @@ config TEST_ASYNC_DRIVER_PROBE
          The module name will be test_async_driver_probe.ko
 
          If unsure say N.
-config KUNIT_DRIVER_PE_TEST
+config DRIVER_PE_KUNIT_TEST
        bool "KUnit Tests for property entry API" if !KUNIT_ALL_TESTS
        depends on KUNIT=y
        default KUNIT_ALL_TESTS
index 2f15fae8625f191d42fd80f594d86fcb5a8fe4c5..64b2f3d744d51aa91d98d64012adb8fce6bae54b 100644 (file)
@@ -1,5 +1,5 @@
 # SPDX-License-Identifier: GPL-2.0
 obj-$(CONFIG_TEST_ASYNC_DRIVER_PROBE)  += test_async_driver_probe.o
 
-obj-$(CONFIG_KUNIT_DRIVER_PE_TEST) += property-entry-test.o
+obj-$(CONFIG_DRIVER_PE_KUNIT_TEST) += property-entry-test.o
 CFLAGS_REMOVE_property-entry-test.o += -fplugin-arg-structleak_plugin-byref -fplugin-arg-structleak_plugin-byref-all
index abe03315180ff9a2d77c25b541a6b08e07d498bb..1106fedcceed88aad2e6fdb83eeff7c846b5a577 100644 (file)
@@ -27,6 +27,9 @@ static void pe_test_uints(struct kunit *test)
        node = fwnode_create_software_node(entries, NULL);
        KUNIT_ASSERT_NOT_ERR_OR_NULL(test, node);
 
+       error = fwnode_property_count_u8(node, "prop-u8");
+       KUNIT_EXPECT_EQ(test, error, 1);
+
        error = fwnode_property_read_u8(node, "prop-u8", &val_u8);
        KUNIT_EXPECT_EQ(test, error, 0);
        KUNIT_EXPECT_EQ(test, (int)val_u8, 8);
@@ -48,6 +51,9 @@ static void pe_test_uints(struct kunit *test)
        KUNIT_EXPECT_EQ(test, error, 0);
        KUNIT_EXPECT_EQ(test, (int)val_u16, 16);
 
+       error = fwnode_property_count_u16(node, "prop-u16");
+       KUNIT_EXPECT_EQ(test, error, 1);
+
        error = fwnode_property_read_u16_array(node, "prop-u16", array_u16, 1);
        KUNIT_EXPECT_EQ(test, error, 0);
        KUNIT_EXPECT_EQ(test, (int)array_u16[0], 16);
@@ -65,6 +71,9 @@ static void pe_test_uints(struct kunit *test)
        KUNIT_EXPECT_EQ(test, error, 0);
        KUNIT_EXPECT_EQ(test, (int)val_u32, 32);
 
+       error = fwnode_property_count_u32(node, "prop-u32");
+       KUNIT_EXPECT_EQ(test, error, 1);
+
        error = fwnode_property_read_u32_array(node, "prop-u32", array_u32, 1);
        KUNIT_EXPECT_EQ(test, error, 0);
        KUNIT_EXPECT_EQ(test, (int)array_u32[0], 32);
@@ -82,6 +91,9 @@ static void pe_test_uints(struct kunit *test)
        KUNIT_EXPECT_EQ(test, error, 0);
        KUNIT_EXPECT_EQ(test, (int)val_u64, 64);
 
+       error = fwnode_property_count_u64(node, "prop-u64");
+       KUNIT_EXPECT_EQ(test, error, 1);
+
        error = fwnode_property_read_u64_array(node, "prop-u64", array_u64, 1);
        KUNIT_EXPECT_EQ(test, error, 0);
        KUNIT_EXPECT_EQ(test, (int)array_u64[0], 64);
@@ -95,15 +107,19 @@ static void pe_test_uints(struct kunit *test)
        error = fwnode_property_read_u64_array(node, "no-prop-u64", array_u64, 1);
        KUNIT_EXPECT_NE(test, error, 0);
 
+       /* Count 64-bit values as 16-bit */
+       error = fwnode_property_count_u16(node, "prop-u64");
+       KUNIT_EXPECT_EQ(test, error, 4);
+
        fwnode_remove_software_node(node);
 }
 
 static void pe_test_uint_arrays(struct kunit *test)
 {
-       static const u8 a_u8[16] = { 8, 9 };
-       static const u16 a_u16[16] = { 16, 17 };
-       static const u32 a_u32[16] = { 32, 33 };
-       static const u64 a_u64[16] = { 64, 65 };
+       static const u8 a_u8[10] = { 8, 9 };
+       static const u16 a_u16[10] = { 16, 17 };
+       static const u32 a_u32[10] = { 32, 33 };
+       static const u64 a_u64[10] = { 64, 65 };
        static const struct property_entry entries[] = {
                PROPERTY_ENTRY_U8_ARRAY("prop-u8", a_u8),
                PROPERTY_ENTRY_U16_ARRAY("prop-u16", a_u16),
@@ -126,6 +142,9 @@ static void pe_test_uint_arrays(struct kunit *test)
        KUNIT_EXPECT_EQ(test, error, 0);
        KUNIT_EXPECT_EQ(test, (int)val_u8, 8);
 
+       error = fwnode_property_count_u8(node, "prop-u8");
+       KUNIT_EXPECT_EQ(test, error, 10);
+
        error = fwnode_property_read_u8_array(node, "prop-u8", array_u8, 1);
        KUNIT_EXPECT_EQ(test, error, 0);
        KUNIT_EXPECT_EQ(test, (int)array_u8[0], 8);
@@ -148,6 +167,9 @@ static void pe_test_uint_arrays(struct kunit *test)
        KUNIT_EXPECT_EQ(test, error, 0);
        KUNIT_EXPECT_EQ(test, (int)val_u16, 16);
 
+       error = fwnode_property_count_u16(node, "prop-u16");
+       KUNIT_EXPECT_EQ(test, error, 10);
+
        error = fwnode_property_read_u16_array(node, "prop-u16", array_u16, 1);
        KUNIT_EXPECT_EQ(test, error, 0);
        KUNIT_EXPECT_EQ(test, (int)array_u16[0], 16);
@@ -170,6 +192,9 @@ static void pe_test_uint_arrays(struct kunit *test)
        KUNIT_EXPECT_EQ(test, error, 0);
        KUNIT_EXPECT_EQ(test, (int)val_u32, 32);
 
+       error = fwnode_property_count_u32(node, "prop-u32");
+       KUNIT_EXPECT_EQ(test, error, 10);
+
        error = fwnode_property_read_u32_array(node, "prop-u32", array_u32, 1);
        KUNIT_EXPECT_EQ(test, error, 0);
        KUNIT_EXPECT_EQ(test, (int)array_u32[0], 32);
@@ -192,6 +217,9 @@ static void pe_test_uint_arrays(struct kunit *test)
        KUNIT_EXPECT_EQ(test, error, 0);
        KUNIT_EXPECT_EQ(test, (int)val_u64, 64);
 
+       error = fwnode_property_count_u64(node, "prop-u64");
+       KUNIT_EXPECT_EQ(test, error, 10);
+
        error = fwnode_property_read_u64_array(node, "prop-u64", array_u64, 1);
        KUNIT_EXPECT_EQ(test, error, 0);
        KUNIT_EXPECT_EQ(test, (int)array_u64[0], 64);
@@ -210,6 +238,14 @@ static void pe_test_uint_arrays(struct kunit *test)
        error = fwnode_property_read_u64_array(node, "no-prop-u64", array_u64, 1);
        KUNIT_EXPECT_NE(test, error, 0);
 
+       /* Count 64-bit values as 16-bit */
+       error = fwnode_property_count_u16(node, "prop-u64");
+       KUNIT_EXPECT_EQ(test, error, 40);
+
+       /* Other way around */
+       error = fwnode_property_count_u64(node, "prop-u16");
+       KUNIT_EXPECT_EQ(test, error, 2);
+
        fwnode_remove_software_node(node);
 }
 
@@ -239,6 +275,9 @@ static void pe_test_strings(struct kunit *test)
        KUNIT_EXPECT_EQ(test, error, 0);
        KUNIT_EXPECT_STREQ(test, str, "single");
 
+       error = fwnode_property_string_array_count(node, "str");
+       KUNIT_EXPECT_EQ(test, error, 1);
+
        error = fwnode_property_read_string_array(node, "str", strs, 1);
        KUNIT_EXPECT_EQ(test, error, 1);
        KUNIT_EXPECT_STREQ(test, strs[0], "single");
@@ -258,6 +297,9 @@ static void pe_test_strings(struct kunit *test)
        KUNIT_EXPECT_EQ(test, error, 0);
        KUNIT_EXPECT_STREQ(test, str, "");
 
+       error = fwnode_property_string_array_count(node, "strs");
+       KUNIT_EXPECT_EQ(test, error, 2);
+
        error = fwnode_property_read_string_array(node, "strs", strs, 3);
        KUNIT_EXPECT_EQ(test, error, 2);
        KUNIT_EXPECT_STREQ(test, strs[0], "string-a");
@@ -370,15 +412,8 @@ static void pe_test_reference(struct kunit *test)
        };
 
        static const struct software_node_ref_args refs[] = {
-               {
-                       .node = &nodes[0],
-                       .nargs = 0,
-               },
-               {
-                       .node = &nodes[1],
-                       .nargs = 2,
-                       .args = { 3, 4 },
-               },
+               SOFTWARE_NODE_REFERENCE(&nodes[0]),
+               SOFTWARE_NODE_REFERENCE(&nodes[1], 3, 4),
        };
 
        const struct property_entry entries[] = {
index 39cfc6c6a8d239660120c11a7d597d419e3940f2..a3b30f7de2ef3ee84348ea28b78a115535b652b1 100644 (file)
@@ -4610,6 +4610,8 @@ int of_clk_add_hw_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_hw_provider);
index c211222f5d0ccf6166a394a6b7ff02a16bcb5327..4105df74f2b06f74dee219d86d9f390af58a3144 100644 (file)
@@ -9,6 +9,7 @@
  * (originally switch class is supported)
  */
 
+#include <linux/devm-helpers.h>
 #include <linux/extcon-provider.h>
 #include <linux/gpio/consumer.h>
 #include <linux/init.h>
@@ -112,7 +113,9 @@ static int gpio_extcon_probe(struct platform_device *pdev)
        if (ret < 0)
                return ret;
 
-       INIT_DELAYED_WORK(&data->work, gpio_extcon_work);
+       ret = devm_delayed_work_autocancel(dev, &data->work, gpio_extcon_work);
+       if (ret)
+               return ret;
 
        /*
         * Request the interrupt of gpio to detect whether external connector
@@ -131,15 +134,6 @@ static int gpio_extcon_probe(struct platform_device *pdev)
        return 0;
 }
 
-static int gpio_extcon_remove(struct platform_device *pdev)
-{
-       struct gpio_extcon_data *data = platform_get_drvdata(pdev);
-
-       cancel_delayed_work_sync(&data->work);
-
-       return 0;
-}
-
 #ifdef CONFIG_PM_SLEEP
 static int gpio_extcon_resume(struct device *dev)
 {
@@ -158,7 +152,6 @@ static SIMPLE_DEV_PM_OPS(gpio_extcon_pm_ops, NULL, gpio_extcon_resume);
 
 static struct platform_driver gpio_extcon_driver = {
        .probe          = gpio_extcon_probe,
-       .remove         = gpio_extcon_remove,
        .driver         = {
                .name   = "extcon-gpio",
                .pm     = &gpio_extcon_pm_ops,
index 80c9abcc3f97874d4323930c7090b9ae9f60e2e6..fb527c23639e80f689b0f7d2120042d6b62906bb 100644 (file)
@@ -11,6 +11,7 @@
  */
 
 #include <linux/acpi.h>
+#include <linux/devm-helpers.h>
 #include <linux/extcon-provider.h>
 #include <linux/gpio/consumer.h>
 #include <linux/interrupt.h>
@@ -101,7 +102,9 @@ static int int3496_probe(struct platform_device *pdev)
                return -ENOMEM;
 
        data->dev = dev;
-       INIT_DELAYED_WORK(&data->work, int3496_do_usb_id);
+       ret = devm_delayed_work_autocancel(dev, &data->work, int3496_do_usb_id);
+       if (ret)
+               return ret;
 
        data->gpio_usb_id = devm_gpiod_get(dev, "id", GPIOD_IN);
        if (IS_ERR(data->gpio_usb_id)) {
@@ -155,16 +158,6 @@ static int int3496_probe(struct platform_device *pdev)
        return 0;
 }
 
-static int int3496_remove(struct platform_device *pdev)
-{
-       struct int3496_data *data = platform_get_drvdata(pdev);
-
-       devm_free_irq(&pdev->dev, data->usb_id_irq, data);
-       cancel_delayed_work_sync(&data->work);
-
-       return 0;
-}
-
 static const struct acpi_device_id int3496_acpi_match[] = {
        { "INT3496" },
        { }
@@ -177,7 +170,6 @@ static struct platform_driver int3496_driver = {
                .acpi_match_table = int3496_acpi_match,
        },
        .probe = int3496_probe,
-       .remove = int3496_remove,
 };
 
 module_platform_driver(int3496_driver);
index a2852bcc5f0d5e215a6230262c01053fd395202e..d2c1a8b89c085449eb16ca6a168fc8ec4dabbc1a 100644 (file)
@@ -9,6 +9,7 @@
  * Author: Hema HK <hemahk@ti.com>
  */
 
+#include <linux/devm-helpers.h>
 #include <linux/module.h>
 #include <linux/interrupt.h>
 #include <linux/platform_device.h>
@@ -237,7 +238,11 @@ static int palmas_usb_probe(struct platform_device *pdev)
                        palmas_usb->sw_debounce_jiffies = msecs_to_jiffies(debounce);
        }
 
-       INIT_DELAYED_WORK(&palmas_usb->wq_detectid, palmas_gpio_id_detect);
+       status = devm_delayed_work_autocancel(&pdev->dev,
+                                             &palmas_usb->wq_detectid,
+                                             palmas_gpio_id_detect);
+       if (status)
+               return status;
 
        palmas->usb = palmas_usb;
        palmas_usb->palmas = palmas;
@@ -359,15 +364,6 @@ static int palmas_usb_probe(struct platform_device *pdev)
        return 0;
 }
 
-static int palmas_usb_remove(struct platform_device *pdev)
-{
-       struct palmas_usb *palmas_usb = platform_get_drvdata(pdev);
-
-       cancel_delayed_work_sync(&palmas_usb->wq_detectid);
-
-       return 0;
-}
-
 #ifdef CONFIG_PM_SLEEP
 static int palmas_usb_suspend(struct device *dev)
 {
@@ -422,7 +418,6 @@ static const struct of_device_id of_palmas_match_tbl[] = {
 
 static struct platform_driver palmas_usb_driver = {
        .probe = palmas_usb_probe,
-       .remove = palmas_usb_remove,
        .driver = {
                .name = "palmas-usb",
                .of_match_table = of_palmas_match_tbl,
index 9e8ccfbea026945a1c446c1ef24de9046d0ed673..eb02cb962b5e18585ef4be6266351314527a8493 100644 (file)
@@ -7,6 +7,7 @@
  * Stephen Boyd <stephen.boyd@linaro.org>
  */
 
+#include <linux/devm-helpers.h>
 #include <linux/extcon-provider.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
@@ -116,7 +117,11 @@ static int qcom_usb_extcon_probe(struct platform_device *pdev)
        }
 
        info->debounce_jiffies = msecs_to_jiffies(USB_ID_DEBOUNCE_MS);
-       INIT_DELAYED_WORK(&info->wq_detcable, qcom_usb_extcon_detect_cable);
+
+       ret = devm_delayed_work_autocancel(dev, &info->wq_detcable,
+                                          qcom_usb_extcon_detect_cable);
+       if (ret)
+               return ret;
 
        info->id_irq = platform_get_irq_byname(pdev, "usb_id");
        if (info->id_irq > 0) {
@@ -158,15 +163,6 @@ static int qcom_usb_extcon_probe(struct platform_device *pdev)
        return 0;
 }
 
-static int qcom_usb_extcon_remove(struct platform_device *pdev)
-{
-       struct qcom_usb_extcon_info *info = platform_get_drvdata(pdev);
-
-       cancel_delayed_work_sync(&info->wq_detcable);
-
-       return 0;
-}
-
 #ifdef CONFIG_PM_SLEEP
 static int qcom_usb_extcon_suspend(struct device *dev)
 {
@@ -210,7 +206,6 @@ MODULE_DEVICE_TABLE(of, qcom_usb_extcon_dt_match);
 
 static struct platform_driver qcom_usb_extcon_driver = {
        .probe          = qcom_usb_extcon_probe,
-       .remove         = qcom_usb_extcon_remove,
        .driver         = {
                .name   = "extcon-pm8941-misc",
                .pm     = &qcom_usb_extcon_pm_ops,
index d3a64a35f7a9af2d11fe31de0d37b5abb7c2301a..805d396aa81b0feba50850521b1288d757545a03 100644 (file)
@@ -7,6 +7,7 @@
  * Copyright (C) 2018 Stefan Wahren <stefan.wahren@i2se.com>
  */
 #include <linux/device.h>
+#include <linux/devm-helpers.h>
 #include <linux/err.h>
 #include <linux/hwmon.h>
 #include <linux/module.h>
@@ -106,6 +107,7 @@ static int rpi_hwmon_probe(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
        struct rpi_hwmon_data *data;
+       int ret;
 
        data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
        if (!data)
@@ -119,7 +121,10 @@ static int rpi_hwmon_probe(struct platform_device *pdev)
                                                               &rpi_chip_info,
                                                               NULL);
 
-       INIT_DELAYED_WORK(&data->get_values_poll_work, get_values_poll);
+       ret = devm_delayed_work_autocancel(dev, &data->get_values_poll_work,
+                                          get_values_poll);
+       if (ret)
+               return ret;
        platform_set_drvdata(pdev, data);
 
        if (!PTR_ERR_OR_ZERO(data->hwmon_dev))
@@ -128,18 +133,8 @@ static int rpi_hwmon_probe(struct platform_device *pdev)
        return PTR_ERR_OR_ZERO(data->hwmon_dev);
 }
 
-static int rpi_hwmon_remove(struct platform_device *pdev)
-{
-       struct rpi_hwmon_data *data = platform_get_drvdata(pdev);
-
-       cancel_delayed_work_sync(&data->get_values_poll_work);
-
-       return 0;
-}
-
 static struct platform_driver rpi_hwmon_driver = {
        .probe = rpi_hwmon_probe,
-       .remove = rpi_hwmon_remove,
        .driver = {
                .name = "raspberrypi-hwmon",
        },
index c2199042d3db593b8d4f6bf045b791cfb6055f28..e8511787c1e439a72588e9f8609a965372e20bec 100644 (file)
@@ -79,8 +79,8 @@ static void cio2_bridge_create_fwnode_properties(
 {
        sensor->prop_names = prop_names;
 
-       sensor->local_ref[0].node = &sensor->swnodes[SWNODE_CIO2_ENDPOINT];
-       sensor->remote_ref[0].node = &sensor->swnodes[SWNODE_SENSOR_ENDPOINT];
+       sensor->local_ref[0] = SOFTWARE_NODE_REFERENCE(&sensor->swnodes[SWNODE_CIO2_ENDPOINT]);
+       sensor->remote_ref[0] = SOFTWARE_NODE_REFERENCE(&sensor->swnodes[SWNODE_SENSOR_ENDPOINT]);
 
        sensor->dev_properties[0] = PROPERTY_ENTRY_U32(
                                        sensor->prop_names.clock_frequency,
index 78427c85bef38737d850905a1adb143dbfbb930b..aab6383f02198928f47319f9e70ef8b076dd2c00 100644 (file)
@@ -1038,6 +1038,25 @@ static bool of_is_ancestor_of(struct device_node *test_ancestor,
        return false;
 }
 
+static struct device_node *of_get_compat_node(struct device_node *np)
+{
+       of_node_get(np);
+
+       while (np) {
+               if (!of_device_is_available(np)) {
+                       of_node_put(np);
+                       np = NULL;
+               }
+
+               if (of_find_property(np, "compatible", NULL))
+                       break;
+
+               np = of_get_next_parent(np);
+       }
+
+       return np;
+}
+
 /**
  * of_link_to_phandle - Add fwnode link to supplier from supplier phandle
  * @con_np: consumer device tree node
@@ -1061,25 +1080,11 @@ static int of_link_to_phandle(struct device_node *con_np,
        struct device *sup_dev;
        struct device_node *tmp_np = sup_np;
 
-       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) {
-
-               /* Don't allow linking to a disabled supplier */
-               if (!of_device_is_available(sup_np)) {
-                       of_node_put(sup_np);
-                       sup_np = NULL;
-               }
-
-               if (of_find_property(sup_np, "compatible", NULL))
-                       break;
-
-               sup_np = of_get_next_parent(sup_np);
-       }
-
+       sup_np = of_get_compat_node(sup_np);
        if (!sup_np) {
                pr_debug("Not linking %pOFP to %pOFP - No device\n",
                         con_np, tmp_np);
@@ -1225,6 +1230,8 @@ static struct device_node *parse_##fname(struct device_node *np,       \
  * @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
+ * @optional: The property can be an optional dependency.
+ * @node_not_dev: The consumer node containing the property is never a device.
  *
  * Returns:
  * parse_prop() return values are
@@ -1236,6 +1243,7 @@ struct supplier_bindings {
        struct device_node *(*parse_prop)(struct device_node *np,
                                          const char *prop_name, int index);
        bool optional;
+       bool node_not_dev;
 };
 
 DEFINE_SIMPLE_PROP(clocks, "clocks", "#clock-cells")
@@ -1260,6 +1268,7 @@ DEFINE_SIMPLE_PROP(pinctrl5, "pinctrl-5", NULL)
 DEFINE_SIMPLE_PROP(pinctrl6, "pinctrl-6", NULL)
 DEFINE_SIMPLE_PROP(pinctrl7, "pinctrl-7", NULL)
 DEFINE_SIMPLE_PROP(pinctrl8, "pinctrl-8", NULL)
+DEFINE_SIMPLE_PROP(remote_endpoint, "remote-endpoint", NULL)
 DEFINE_SUFFIX_PROP(regulators, "-supply", NULL)
 DEFINE_SUFFIX_PROP(gpio, "-gpio", "#gpio-cells")
 
@@ -1343,6 +1352,7 @@ static const struct supplier_bindings of_supplier_bindings[] = {
        { .parse_prop = parse_pinctrl6, },
        { .parse_prop = parse_pinctrl7, },
        { .parse_prop = parse_pinctrl8, },
+       { .parse_prop = parse_remote_endpoint, .node_not_dev = true, },
        { .parse_prop = parse_gpio_compat, },
        { .parse_prop = parse_interrupts, },
        { .parse_prop = parse_regulators, },
@@ -1387,10 +1397,16 @@ 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;
+
+                       con_dev_np = s->node_not_dev
+                                       ? of_get_compat_node(con_np)
+                                       : of_node_get(con_np);
                        matched = true;
                        i++;
-                       of_link_to_phandle(con_np, phandle);
+                       of_link_to_phandle(con_dev_np, phandle);
                        of_node_put(phandle);
+                       of_node_put(con_dev_np);
                }
                s++;
        }
index 5b516e4c2bfbe2ac53aa68a7bbf99365d1c391a4..7a20f68ae20611f74d6621da1a89601d838cde7d 100644 (file)
@@ -6,6 +6,7 @@
  */
 
 #include <linux/acpi.h>
+#include <linux/devm-helpers.h>
 #include <linux/gpio/consumer.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
@@ -124,7 +125,7 @@ static void gpd_pocket_fan_force_update(struct gpd_pocket_fan_data *fan)
 static int gpd_pocket_fan_probe(struct platform_device *pdev)
 {
        struct gpd_pocket_fan_data *fan;
-       int i;
+       int i, ret;
 
        for (i = 0; i < ARRAY_SIZE(temp_limits); i++) {
                if (temp_limits[i] < 20000 || temp_limits[i] > 90000) {
@@ -152,7 +153,10 @@ static int gpd_pocket_fan_probe(struct platform_device *pdev)
                return -ENOMEM;
 
        fan->dev = &pdev->dev;
-       INIT_DELAYED_WORK(&fan->work, gpd_pocket_fan_worker);
+       ret = devm_delayed_work_autocancel(&pdev->dev, &fan->work,
+                                          gpd_pocket_fan_worker);
+       if (ret)
+               return ret;
 
        /* Note this returns a "weak" reference which we don't need to free */
        fan->dts0 = thermal_zone_get_zone_by_name("soc_dts0");
@@ -177,14 +181,6 @@ static int gpd_pocket_fan_probe(struct platform_device *pdev)
        return 0;
 }
 
-static int gpd_pocket_fan_remove(struct platform_device *pdev)
-{
-       struct gpd_pocket_fan_data *fan = platform_get_drvdata(pdev);
-
-       cancel_delayed_work_sync(&fan->work);
-       return 0;
-}
-
 #ifdef CONFIG_PM_SLEEP
 static int gpd_pocket_fan_suspend(struct device *dev)
 {
@@ -215,7 +211,6 @@ MODULE_DEVICE_TABLE(acpi, gpd_pocket_fan_acpi_match);
 
 static struct platform_driver gpd_pocket_fan_driver = {
        .probe  = gpd_pocket_fan_probe,
-       .remove = gpd_pocket_fan_remove,
        .driver = {
                .name                   = "gpd_pocket_fan",
                .acpi_match_table       = gpd_pocket_fan_acpi_match,
index 8933ae26c3d6934c07df37c09f5e1d706f3f20b3..e954970b50e698a35a2783558380b827a2a7077e 100644 (file)
@@ -8,6 +8,7 @@
 
 #include <linux/bitops.h>
 #include <linux/device.h>
+#include <linux/devm-helpers.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/kernel.h>
@@ -593,7 +594,11 @@ static int axp20x_usb_power_probe(struct platform_device *pdev)
        power->axp20x_id = axp_data->axp20x_id;
        power->regmap = axp20x->regmap;
        power->num_irqs = axp_data->num_irq_names;
-       INIT_DELAYED_WORK(&power->vbus_detect, axp20x_usb_power_poll_vbus);
+
+       ret = devm_delayed_work_autocancel(&pdev->dev, &power->vbus_detect,
+                                          axp20x_usb_power_poll_vbus);
+       if (ret)
+               return ret;
 
        if (power->axp20x_id == AXP202_ID) {
                /* Enable vbus valid checking */
@@ -652,15 +657,6 @@ static int axp20x_usb_power_probe(struct platform_device *pdev)
        return 0;
 }
 
-static int axp20x_usb_power_remove(struct platform_device *pdev)
-{
-       struct axp20x_usb_power *power = platform_get_drvdata(pdev);
-
-       cancel_delayed_work_sync(&power->vbus_detect);
-
-       return 0;
-}
-
 static const struct of_device_id axp20x_usb_power_match[] = {
        {
                .compatible = "x-powers,axp202-usb-power-supply",
@@ -680,7 +676,6 @@ MODULE_DEVICE_TABLE(of, axp20x_usb_power_match);
 
 static struct platform_driver axp20x_usb_power_driver = {
        .probe = axp20x_usb_power_probe,
-       .remove = axp20x_usb_power_remove,
        .driver = {
                .name           = DRVNAME,
                .of_match_table = axp20x_usb_power_match,
index ab2f4bf8f603ffa947458be3b209ae66be22f757..b5d619db79f6be6ccb5978c682c07358d5c16e82 100644 (file)
@@ -17,6 +17,7 @@
  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 
+#include <linux/devm-helpers.h>
 #include <linux/err.h>
 #include <linux/i2c.h>
 #include <linux/init.h>
@@ -473,7 +474,11 @@ static int bq24735_charger_probe(struct i2c_client *client,
                if (!charger->poll_interval)
                        return 0;
 
-               INIT_DELAYED_WORK(&charger->poll, bq24735_poll);
+               ret = devm_delayed_work_autocancel(&client->dev, &charger->poll,
+                                                  bq24735_poll);
+               if (ret)
+                       return ret;
+
                schedule_delayed_work(&charger->poll,
                                      msecs_to_jiffies(charger->poll_interval));
        }
@@ -481,16 +486,6 @@ static int bq24735_charger_probe(struct i2c_client *client,
        return 0;
 }
 
-static int bq24735_charger_remove(struct i2c_client *client)
-{
-       struct bq24735 *charger = i2c_get_clientdata(client);
-
-       if (charger->poll_interval)
-               cancel_delayed_work_sync(&charger->poll);
-
-       return 0;
-}
-
 static const struct i2c_device_id bq24735_charger_id[] = {
        { "bq24735-charger", 0 },
        {}
@@ -509,7 +504,6 @@ static struct i2c_driver bq24735_charger_driver = {
                .of_match_table = bq24735_match_ids,
        },
        .probe = bq24735_charger_probe,
-       .remove = bq24735_charger_remove,
        .id_table = bq24735_charger_id,
 };
 
index 10cd617516ec26168270aff95af4524aec3014e7..09f3e78af4e01fe2bdf9e2d828a7c01659e87b19 100644 (file)
@@ -8,6 +8,7 @@
  * Author: Auryn Verwegen
  * Author: Mike Looijmans
  */
+#include <linux/devm-helpers.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/of_device.h>
@@ -445,15 +446,6 @@ static enum power_supply_property ltc294x_properties[] = {
        POWER_SUPPLY_PROP_CURRENT_NOW,
 };
 
-static int ltc294x_i2c_remove(struct i2c_client *client)
-{
-       struct ltc294x_info *info = i2c_get_clientdata(client);
-
-       cancel_delayed_work_sync(&info->work);
-       power_supply_unregister(info->supply);
-       return 0;
-}
-
 static int ltc294x_i2c_probe(struct i2c_client *client,
        const struct i2c_device_id *id)
 {
@@ -547,7 +539,10 @@ static int ltc294x_i2c_probe(struct i2c_client *client,
 
        psy_cfg.drv_data = info;
 
-       INIT_DELAYED_WORK(&info->work, ltc294x_work);
+       ret = devm_delayed_work_autocancel(&client->dev, &info->work,
+                                          ltc294x_work);
+       if (ret)
+               return ret;
 
        ret = ltc294x_reset(info, prescaler_exp);
        if (ret < 0) {
@@ -555,8 +550,8 @@ static int ltc294x_i2c_probe(struct i2c_client *client,
                return ret;
        }
 
-       info->supply = power_supply_register(&client->dev, &info->supply_desc,
-                                            &psy_cfg);
+       info->supply = devm_power_supply_register(&client->dev,
+                                                 &info->supply_desc, &psy_cfg);
        if (IS_ERR(info->supply)) {
                dev_err(&client->dev, "failed to register ltc2941\n");
                return PTR_ERR(info->supply);
@@ -655,7 +650,6 @@ static struct i2c_driver ltc294x_driver = {
                .pm     = LTC294X_PM_OPS,
        },
        .probe          = ltc294x_i2c_probe,
-       .remove         = ltc294x_i2c_remove,
        .shutdown       = ltc294x_i2c_shutdown,
        .id_table       = ltc294x_i2c_id,
 };
index b6a538ebb378fcde62a42b9d76ac78555af8450f..70ea404b2a36767ecfc313e0df5af4231f61bf81 100644 (file)
@@ -7,6 +7,7 @@
 
 #include <linux/bits.h>
 #include <linux/delay.h>
+#include <linux/devm-helpers.h>
 #include <linux/err.h>
 #include <linux/gpio/consumer.h>
 #include <linux/i2c.h>
@@ -1165,7 +1166,10 @@ skip_gpio:
                }
        }
 
-       INIT_DELAYED_WORK(&chip->work, sbs_delayed_work);
+       rc = devm_delayed_work_autocancel(&client->dev, &chip->work,
+                                         sbs_delayed_work);
+       if (rc)
+               return rc;
 
        chip->power_supply = devm_power_supply_register(&client->dev, sbs_desc,
                                                   &psy_cfg);
@@ -1185,15 +1189,6 @@ exit_psupply:
        return rc;
 }
 
-static int sbs_remove(struct i2c_client *client)
-{
-       struct sbs_info *chip = i2c_get_clientdata(client);
-
-       cancel_delayed_work_sync(&chip->work);
-
-       return 0;
-}
-
 #if defined CONFIG_PM_SLEEP
 
 static int sbs_suspend(struct device *dev)
@@ -1248,7 +1243,6 @@ MODULE_DEVICE_TABLE(of, sbs_dt_ids);
 
 static struct i2c_driver sbs_battery_driver = {
        .probe_new      = sbs_probe,
-       .remove         = sbs_remove,
        .alert          = sbs_alert,
        .id_table       = sbs_id,
        .driver = {
index e62e1d72d94390f0843dbc06fcd2b09aa71915a8..c2442d7798ab73ac01918373031b90c304d61ec2 100644 (file)
@@ -5,6 +5,7 @@
 
 #include <linux/module.h>
 #include <linux/delay.h>
+#include <linux/devm-helpers.h>
 #include <linux/err.h>
 #include <linux/kernel.h>
 #include <linux/interrupt.h>
@@ -1842,7 +1843,10 @@ static int spmi_regulator_of_parse(struct device_node *node,
                        return ret;
                }
 
-               INIT_DELAYED_WORK(&vreg->ocp_work, spmi_regulator_vs_ocp_work);
+               ret = devm_delayed_work_autocancel(dev, &vreg->ocp_work,
+                                                  spmi_regulator_vs_ocp_work);
+               if (ret)
+                       return ret;
        }
 
        return 0;
@@ -2157,10 +2161,8 @@ static int qcom_spmi_regulator_probe(struct platform_device *pdev)
                vreg->regmap = regmap;
                if (reg->ocp) {
                        vreg->ocp_irq = platform_get_irq_byname(pdev, reg->ocp);
-                       if (vreg->ocp_irq < 0) {
-                               ret = vreg->ocp_irq;
-                               goto err;
-                       }
+                       if (vreg->ocp_irq < 0)
+                               return vreg->ocp_irq;
                }
                vreg->desc.id = -1;
                vreg->desc.owner = THIS_MODULE;
@@ -2203,8 +2205,7 @@ static int qcom_spmi_regulator_probe(struct platform_device *pdev)
                rdev = devm_regulator_register(dev, &vreg->desc, &config);
                if (IS_ERR(rdev)) {
                        dev_err(dev, "failed to register %s\n", name);
-                       ret = PTR_ERR(rdev);
-                       goto err;
+                       return PTR_ERR(rdev);
                }
 
                INIT_LIST_HEAD(&vreg->node);
@@ -2212,24 +2213,6 @@ static int qcom_spmi_regulator_probe(struct platform_device *pdev)
        }
 
        return 0;
-
-err:
-       list_for_each_entry(vreg, vreg_list, node)
-               if (vreg->ocp_irq)
-                       cancel_delayed_work_sync(&vreg->ocp_work);
-       return ret;
-}
-
-static int qcom_spmi_regulator_remove(struct platform_device *pdev)
-{
-       struct spmi_regulator *vreg;
-       struct list_head *vreg_list = platform_get_drvdata(pdev);
-
-       list_for_each_entry(vreg, vreg_list, node)
-               if (vreg->ocp_irq)
-                       cancel_delayed_work_sync(&vreg->ocp_work);
-
-       return 0;
 }
 
 static struct platform_driver qcom_spmi_regulator_driver = {
@@ -2238,7 +2221,6 @@ static struct platform_driver qcom_spmi_regulator_driver = {
                .of_match_table = qcom_spmi_regulator_match,
        },
        .probe          = qcom_spmi_regulator_probe,
-       .remove         = qcom_spmi_regulator_remove,
 };
 module_platform_driver(qcom_spmi_regulator_driver);
 
index 258dfcf9cbdada75e1b4073d1dc818ca9d913c22..2b9017e1cd91f8db5f70d26d0bacf887acac18a6 100644 (file)
@@ -8,6 +8,7 @@
  * Rewritten by Aaro Koskinen.
  */
 
+#include <linux/devm-helpers.h>
 #include <linux/slab.h>
 #include <linux/errno.h>
 #include <linux/device.h>
@@ -127,9 +128,12 @@ static int retu_wdt_probe(struct platform_device *pdev)
        wdev->rdev              = rdev;
        wdev->dev               = &pdev->dev;
 
-       INIT_DELAYED_WORK(&wdev->ping_work, retu_wdt_ping_work);
+       ret = devm_delayed_work_autocancel(&pdev->dev, &wdev->ping_work,
+                                          retu_wdt_ping_work);
+       if (ret)
+               return ret;
 
-       ret = watchdog_register_device(retu_wdt);
+       ret = devm_watchdog_register_device(&pdev->dev, retu_wdt);
        if (ret < 0)
                return ret;
 
@@ -138,25 +142,11 @@ static int retu_wdt_probe(struct platform_device *pdev)
        else
                retu_wdt_ping_enable(wdev);
 
-       platform_set_drvdata(pdev, retu_wdt);
-
-       return 0;
-}
-
-static int retu_wdt_remove(struct platform_device *pdev)
-{
-       struct watchdog_device *wdog = platform_get_drvdata(pdev);
-       struct retu_wdt_dev *wdev = watchdog_get_drvdata(wdog);
-
-       watchdog_unregister_device(wdog);
-       cancel_delayed_work_sync(&wdev->ping_work);
-
        return 0;
 }
 
 static struct platform_driver retu_wdt_driver = {
        .probe          = retu_wdt_probe,
-       .remove         = retu_wdt_remove,
        .driver         = {
                .name   = "retu-wdt",
        },
index 686e0ad287880c15406a479a3acc209a8fa1396f..9979d981e9beb8f8ad8b84e6a08feaee43c0e786 100644 (file)
@@ -773,7 +773,7 @@ EXPORT_SYMBOL_GPL(debugfs_create_atomic_t);
 ssize_t debugfs_read_file_bool(struct file *file, char __user *user_buf,
                               size_t count, loff_t *ppos)
 {
-       char buf[3];
+       char buf[2];
        bool val;
        int r;
        struct dentry *dentry = F_DENTRY(file);
@@ -789,7 +789,6 @@ ssize_t debugfs_read_file_bool(struct file *file, char __user *user_buf,
        else
                buf[0] = 'N';
        buf[1] = '\n';
-       buf[2] = 0x00;
        return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
 }
 EXPORT_SYMBOL_GPL(debugfs_read_file_bool);
index 22e86ae4dd5a829ebbef4fbdf346d18315446601..1d252164d97b6f989f1836dc27e69e3dc698abdf 100644 (file)
@@ -35,7 +35,7 @@
 static struct vfsmount *debugfs_mount;
 static int debugfs_mount_count;
 static bool debugfs_registered;
-static unsigned int debugfs_allow = DEFAULT_DEBUGFS_ALLOW_BITS;
+static unsigned int debugfs_allow __ro_after_init = DEFAULT_DEBUGFS_ALLOW_BITS;
 
 /*
  * Don't allow access attributes to be changed whilst the kernel is locked down
index ba660731bd258279d8c9b5513a90015b22fad3e0..38a2071cf77685a3b8c520f4d4eafb50ee080669 100644 (file)
@@ -49,7 +49,7 @@ struct dev_iommu;
 /**
  * struct subsys_interface - interfaces to device functions
  * @name:       name of the device function
- * @subsys:     subsytem of the devices to attach to
+ * @subsys:     subsystem of the devices to attach to
  * @node:       the list of functions registered at the subsystem
  * @add_dev:    device hookup to device function handler
  * @remove_dev: device hookup to device function handler
@@ -439,6 +439,9 @@ struct dev_links_info {
  * @state_synced: The hardware state of this device has been synced to match
  *               the software state of this device by calling the driver/bus
  *               sync_state() callback.
+ * @can_match: The device has matched with a driver at least once or it is in
+ *             a bus (like AMBA) which can't check for matching drivers until
+ *             other devices probe successfully.
  * @dma_coherent: this particular device is dma coherent, even if the
  *             architecture supports non-coherent devices.
  * @dma_ops_bypass: If set to %true then the dma_ops are bypassed for the
@@ -545,6 +548,7 @@ struct device {
        bool                    offline:1;
        bool                    of_node_reused:1;
        bool                    state_synced:1;
+       bool                    can_match:1;
 #if defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_DEVICE) || \
     defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU) || \
     defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU_ALL)
diff --git a/include/linux/devm-helpers.h b/include/linux/devm-helpers.h
new file mode 100644 (file)
index 0000000..f40f777
--- /dev/null
@@ -0,0 +1,54 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef __LINUX_DEVM_HELPERS_H
+#define __LINUX_DEVM_HELPERS_H
+
+/*
+ * Functions which do automatically cancel operations or release resources upon
+ * driver detach.
+ *
+ * These should be helpful to avoid mixing the manual and devm-based resource
+ * management which can be source of annoying, rarely occurring,
+ * hard-to-reproduce bugs.
+ *
+ * Please take into account that devm based cancellation may be performed some
+ * time after the remove() is ran.
+ *
+ * Thus mixing devm and manual resource management can easily cause problems
+ * when unwinding operations with dependencies. IRQ scheduling a work in a queue
+ * is typical example where IRQs are often devm-managed and WQs are manually
+ * cleaned at remove(). If IRQs are not manually freed at remove() (and this is
+ * often the case when we use devm for IRQs) we have a period of time after
+ * remove() - and before devm managed IRQs are freed - where new IRQ may fire
+ * and schedule a work item which won't be cancelled because remove() was
+ * already ran.
+ */
+
+#include <linux/device.h>
+#include <linux/workqueue.h>
+
+static inline void devm_delayed_work_drop(void *res)
+{
+       cancel_delayed_work_sync(res);
+}
+
+/**
+ * devm_delayed_work_autocancel - Resource-managed delayed work allocation
+ * @dev:       Device which lifetime work is bound to
+ * @w:         Work item to be queued
+ * @worker:    Worker function
+ *
+ * Initialize delayed work which is automatically cancelled when driver is
+ * detached. A few drivers need delayed work which must be cancelled before
+ * driver is detached to avoid accessing removed resources.
+ * devm_delayed_work_autocancel() can be used to omit the explicit
+ * cancelleation when driver is detached.
+ */
+static inline int devm_delayed_work_autocancel(struct device *dev,
+                                              struct delayed_work *w,
+                                              work_func_t worker)
+{
+       INIT_DELAYED_WORK(w, worker);
+       return devm_add_action(dev, devm_delayed_work_drop, w);
+}
+
+#endif
index 3f23f6e430bfa514d7866a941413ea0d274587f7..cd81e060863c95bbb048468db2020aeb3a312aea 100644 (file)
@@ -359,4 +359,7 @@ static inline int is_sh_early_platform_device(struct platform_device *pdev)
 }
 #endif /* CONFIG_SUPERH */
 
+/* For now only SuperH uses it */
+void early_platform_cleanup(void);
+
 #endif /* _PLATFORM_DEVICE_H_ */
index dd4687b562393120ebd029ecc2fb977c9535f0c3..0d876316e61d5df0f574e1b007f7f6f5fab42302 100644 (file)
@@ -254,6 +254,13 @@ struct software_node_ref_args {
        u64 args[NR_FWNODE_REFERENCE_ARGS];
 };
 
+#define SOFTWARE_NODE_REFERENCE(_ref_, ...)                    \
+(const struct software_node_ref_args) {                                \
+       .node = _ref_,                                          \
+       .nargs = ARRAY_SIZE(((u64[]){ 0, ##__VA_ARGS__ })) - 1, \
+       .args = { __VA_ARGS__ },                                \
+}
+
 /**
  * struct property_entry - "Built-in" device property representation.
  * @name: Name of the property.
@@ -362,11 +369,7 @@ struct property_entry {
        .name = _name_,                                                 \
        .length = sizeof(struct software_node_ref_args),                \
        .type = DEV_PROP_REF,                                           \
-       { .pointer = &(const struct software_node_ref_args) {           \
-               .node = _ref_,                                          \
-               .nargs = ARRAY_SIZE(((u64[]){ 0, ##__VA_ARGS__ })) - 1, \
-               .args = { __VA_ARGS__ },                                \
-       } },                                                            \
+       { .pointer = &SOFTWARE_NODE_REFERENCE(_ref_, ##__VA_ARGS__), }, \
 }
 
 struct property_entry *
index 7998affa45d49a27b8613931ae3eb81499eef926..c87d5b6a8a55a3c02719ebfde3761f948ec2c35b 100644 (file)
@@ -251,12 +251,13 @@ static int kobj_usermode_filter(struct kobject *kobj)
 
 static int init_uevent_argv(struct kobj_uevent_env *env, const char *subsystem)
 {
+       int buffer_size = sizeof(env->buf) - env->buflen;
        int len;
 
-       len = strlcpy(&env->buf[env->buflen], subsystem,
-                     sizeof(env->buf) - env->buflen);
-       if (len >= (sizeof(env->buf) - env->buflen)) {
-               WARN(1, KERN_ERR "init_uevent_argv: buffer size too small\n");
+       len = strlcpy(&env->buf[env->buflen], subsystem, buffer_size);
+       if (len >= buffer_size) {
+               pr_warn("init_uevent_argv: buffer size of %d too small, needed %d\n",
+                       buffer_size, len);
                return -ENOMEM;
        }
 
index 5ebc1aec7923b41b3d8697957883d1c358ffd53d..0e393cb5f42de42d9d93a4b954b32b16881bb7e6 100644 (file)
@@ -95,7 +95,7 @@ static bool test_fw_in_ns(const char *fw_name, const char *sys_path, bool block_
                }
                if (block_fw_in_parent_ns)
                        umount("/lib/firmware");
-               return WEXITSTATUS(status) == EXIT_SUCCESS ? true : false;
+               return WEXITSTATUS(status) == EXIT_SUCCESS;
        }
 
        if (unshare(CLONE_NEWNS) != 0) {