summaryrefslogtreecommitdiff
path: root/drivers/base/dd.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/base/dd.c')
-rw-r--r--drivers/base/dd.c61
1 files changed, 49 insertions, 12 deletions
diff --git a/drivers/base/dd.c b/drivers/base/dd.c
index 6ea4f45dae12..cc6bf344b5c4 100644
--- a/drivers/base/dd.c
+++ b/drivers/base/dd.c
@@ -25,6 +25,7 @@
#include <linux/async.h>
#include <linux/pm_runtime.h>
#include <linux/pinctrl/devinfo.h>
+#include <linux/platform_device.h>
#include "base.h"
#include "power/power.h"
@@ -171,26 +172,49 @@ static void driver_deferred_probe_trigger(void)
queue_work(deferred_wq, &deferred_probe_work);
}
+static void enable_trigger_defer_cycle(void)
+{
+ driver_deferred_probe_enable = true;
+ driver_deferred_probe_trigger();
+ /*
+ * Sort as many dependencies as possible before the next initcall
+ * level
+ */
+ flush_workqueue(deferred_wq);
+}
+
/**
* deferred_probe_initcall() - Enable probing of deferred devices
*
* We don't want to get in the way when the bulk of drivers are getting probed.
* Instead, this initcall makes sure that deferred probing is delayed until
- * late_initcall time.
+ * all the registered initcall functions at a particular level are completed.
+ * This function is invoked at every *_initcall_sync level.
*/
static int deferred_probe_initcall(void)
{
- deferred_wq = create_singlethread_workqueue("deferwq");
- if (WARN_ON(!deferred_wq))
- return -ENOMEM;
+ if (!deferred_wq) {
+ deferred_wq = create_singlethread_workqueue("deferwq");
+ if (WARN_ON(!deferred_wq))
+ return -ENOMEM;
+ }
- driver_deferred_probe_enable = true;
- driver_deferred_probe_trigger();
- /* Sort as many dependencies as possible before exiting initcalls */
- flush_workqueue(deferred_wq);
+ enable_trigger_defer_cycle();
+ driver_deferred_probe_enable = false;
return 0;
}
-late_initcall(deferred_probe_initcall);
+arch_initcall_sync(deferred_probe_initcall);
+subsys_initcall_sync(deferred_probe_initcall);
+fs_initcall_sync(deferred_probe_initcall);
+device_initcall_sync(deferred_probe_initcall);
+
+static int deferred_probe_enable_fn(void)
+{
+ /* Enable deferred probing for all time */
+ enable_trigger_defer_cycle();
+ return 0;
+}
+late_initcall(deferred_probe_enable_fn);
static void driver_bound(struct device *dev)
{
@@ -615,6 +639,20 @@ void device_initial_probe(struct device *dev)
{
__device_attach(dev, true);
}
+#ifdef CONFIG_PLATFORM_AUTO
+static inline int lock_parent(struct device *dev)
+{
+ if (!dev->parent || dev->bus == &platform_bus_type)
+ return 0;
+
+ return 1;
+}
+#else
+static inline int lock_parent(struct device *dev)
+{
+ return dev->parent ? 1 : 0;
+}
+#endif
static int __driver_attach(struct device *dev, void *data)
{
@@ -632,14 +670,13 @@ static int __driver_attach(struct device *dev, void *data)
if (!driver_match_device(drv, dev))
return 0;
-
- if (dev->parent) /* Needed for USB */
+ if (lock_parent(dev))
device_lock(dev->parent);
device_lock(dev);
if (!dev->driver)
driver_probe_device(drv, dev);
device_unlock(dev);
- if (dev->parent)
+ if (lock_parent(dev))
device_unlock(dev->parent);
return 0;