@@ -6,6 +6,9 @@ obj-y := component.o core.o bus.o dd.o syscore.o \
attribute_container.o transport_class.o \
topology.o container.o property.o cacheinfo.o
obj-$(CONFIG_BOOT_CONSTRAINTS) += boot_constraint.o
+ifeq ($(CONFIG_OF),y)
+obj-$(CONFIG_BOOT_CONSTRAINTS) += boot_constraint_of.o
+endif
obj-$(CONFIG_DEVTMPFS) += devtmpfs.o
obj-$(CONFIG_DMA_CMA) += dma-contiguous.o
obj-y += power/
@@ -136,6 +136,7 @@ extern char *make_class_name(const char *name, struct kobject *kobj);
extern int devres_release_all(struct device *dev);
extern void device_block_probing(void);
extern void device_unblock_probing(void);
+extern void driver_enable_deferred_probe(void);
/* /sys/devices directory */
extern struct kset *devices_kset;
@@ -50,7 +50,7 @@ static DEFINE_MUTEX(constraint_devices_mutex);
static int constraint_supply_add(struct constraint *constraint, void *data);
static void constraint_supply_remove(struct constraint *constraint);
-static bool boot_constraints_disabled;
+bool boot_constraints_disabled;
static int __init constraints_disable(char *str)
{
new file mode 100644
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2017 Linaro.
+ * Viresh Kumar <viresh.kumar@linaro.org>
+ *
+ * This file is released under the GPLv2.
+ */
+
+#define pr_fmt(fmt) "Boot Constraints: " fmt
+
+#include <linux/boot_constraint.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+
+#include "base.h"
+
+extern bool boot_constraints_disabled;
+
+struct boot_constraint_of_pdata {
+ struct device *dev;
+ enum boot_constraint_type type;
+ struct boot_constraint_supply_info info;
+};
+
+static void boot_constraint_add_supply(struct device *dev, int index)
+{
+ struct boot_constraint_of_pdata pdata = {
+ .dev = dev,
+ .type = BOOT_CONSTRAINT_SUPPLY,
+ };
+ struct boot_constraint_supply_info *info;
+ struct device_node *np = dev->of_node;
+ struct platform_device *pdev;
+ int ret;
+
+ info = &pdata.info;
+
+ ret = of_property_read_string_index(np, "boot-constraint-supplies",
+ index, &info->name);
+ if (ret < 0) {
+ dev_err(dev, "%s: Failed to read supply at index %d (%d)\n",
+ __func__, index, ret);
+ return;
+ }
+
+ of_property_read_u32_index(np, "boot-constraint-uV", 2 * index,
+ &info->u_volt_min);
+
+ of_property_read_u32_index(np, "boot-constraint-uV", 2 * index + 1,
+ &info->u_volt_max);
+
+ pdev = platform_device_register_data(NULL, "boot-constraints-of", index,
+ &pdata, sizeof(pdata));
+ if (IS_ERR(pdev)) {
+ dev_err(dev, "%s: Failed to create pdev for index %d (%d)\n",
+ __func__, index, ret);
+ }
+}
+
+static void boot_constraint_add_supplies(struct device *dev)
+{
+ struct device_node *np = dev->of_node;
+ int i, count;
+
+ count = of_property_count_strings(np, "boot-constraint-supplies");
+ if (count <= 0)
+ return;
+
+ for (i = 0; i < count; i++)
+ boot_constraint_add_supply(dev, i);
+}
+
+void of_boot_constraint_init(struct device *dev)
+{
+ if (boot_constraints_disabled)
+ return;
+
+ BUG_ON(!dev->of_node);
+
+ boot_constraint_add_supplies(dev);
+}
+
+static void boot_constraint_remove_of(void *data)
+{
+ platform_device_unregister(data);
+}
+
+/*
+ * A platform device is added for each and every constraint, to handle
+ * -EPROBE_DEFER properly.
+ */
+static int boot_constraint_of_probe(struct platform_device *pdev)
+{
+ struct boot_constraint_of_pdata *pdata = dev_get_platdata(&pdev->dev);
+ struct boot_constraint_info info;
+ int ret;
+
+ BUG_ON(!pdata);
+
+ info.free_resources = boot_constraint_remove_of;
+ info.free_resources_data = pdev;
+ info.constraint_info = &pdata->info;
+
+ ret = boot_constraint_add(pdata->dev, pdata->type, &info);
+ if (ret == -EPROBE_DEFER)
+ driver_enable_deferred_probe();
+
+ return ret;
+}
+
+static struct platform_driver boot_constraint_of_driver = {
+ .driver = {
+ .name = "boot-constraints-of",
+ },
+ .probe = boot_constraint_of_probe,
+};
+
+static int __init boot_constraint_of_init(void)
+{
+ return platform_driver_register(&boot_constraint_of_driver);
+}
+core_initcall(boot_constraint_of_init);
@@ -204,6 +204,18 @@ void device_unblock_probing(void)
}
/**
+ * driver_enable_deferred_probe() - Enable probing of deferred devices
+ *
+ * We don't want to get in the way when the bulk of drivers are getting probed
+ * and so deferred probe is disabled in the beginning. Enable it now because we
+ * need it.
+ */
+void driver_enable_deferred_probe(void)
+{
+ driver_deferred_probe_enable = true;
+}
+
+/**
* 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.
@@ -17,6 +17,7 @@
#include <linux/errno.h>
#include <linux/module.h>
#include <linux/amba/bus.h>
+#include <linux/boot_constraint.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>
@@ -194,6 +195,8 @@ static struct platform_device *of_platform_device_create_pdata(
goto err_clear_flag;
}
+ of_boot_constraint_init(&dev->dev);
+
return dev;
err_clear_flag:
@@ -271,6 +274,7 @@ static struct amba_device *of_amba_device_create(struct device_node *node,
goto err_free;
}
+ of_boot_constraint_init(&dev->dev);
return dev;
err_free:
@@ -33,10 +33,12 @@ struct boot_constraint_info {
int boot_constraint_add(struct device *dev, enum boot_constraint_type type,
struct boot_constraint_info *info);
void boot_constraints_remove(struct device *dev);
+void of_boot_constraint_init(struct device *dev);
#else
static inline int boot_constraint_add(struct device *dev,
enum boot_constraint_type type,
struct boot_constraint_info *info)
{ return -EINVAL; }
static inline void boot_constraints_remove(struct device *dev) {}
+static inline void of_boot_constraint_init(struct device *dev) {}
#endif
This implements the device tree dependent part of the boot constraints. The supply constraints are added automatically for the platform and AMBA devices created from DT and will be removed after the driver is registered for the devices. It is possible that some of the resources aren't available at the time when the devices are added and so the boot constraints core will return -EPROBE_DEFER for them. In order to retry adding the constraints at a later point of time (after the resource is added and before any of its users come up), this patch proposes two things: - Each constraint is represented by a virtual platform device, so that it is re-probed again until the time all the dependencies aren't met. The platform device is removed along with the constraint, with help of the free_resources() callback. - Enable early defer probing support by calling driver_enable_deferred_probe(), so that the core retries probing deferred devices every time any device is bound to a driver. This makes sure that the constraint is set before any of the users of the resources come up. This is tested on ARM64 Hikey board where probe was deferred for a device. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> --- drivers/base/Makefile | 3 + drivers/base/base.h | 1 + drivers/base/boot_constraint.c | 2 +- drivers/base/boot_constraint_of.c | 122 ++++++++++++++++++++++++++++++++++++++ drivers/base/dd.c | 12 ++++ drivers/of/platform.c | 4 ++ include/linux/boot_constraint.h | 2 + 7 files changed, 145 insertions(+), 1 deletion(-) create mode 100644 drivers/base/boot_constraint_of.c -- 2.13.0.71.gd7076ec9c9cb