summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorViresh Kumar <viresh.kumar@linaro.org>2018-02-02 13:00:59 +0530
committerViresh Kumar <viresh.kumar@linaro.org>2018-02-02 13:00:59 +0530
commit78c72960f7d7487c4628eb8c80ddeba7f39839b2 (patch)
tree283ceaee70c849ef752f912c50b822ca0a3a1178
parent88a0e65049e8e66073b0c8fb3dfb79621dda29ec (diff)
updates
Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
-rw-r--r--boot_constraint/boot_constraint.html26
-rw-r--r--boot_constraint/boot_constraint.txt24
-rw-r--r--boot_constraint/boot_constraint_orig.txt372
3 files changed, 399 insertions, 23 deletions
diff --git a/boot_constraint/boot_constraint.html b/boot_constraint/boot_constraint.html
index fc27371..dd71bd1 100644
--- a/boot_constraint/boot_constraint.html
+++ b/boot_constraint/boot_constraint.html
@@ -10,9 +10,11 @@
<p>
</p>
<a name="preamble"></a>
-<p>The Linux kernel <a href="">4.16-rc1</a> release includes support for the "Boot
-Constraint" subsystem. The purpose of this subsystem is to honor the constraints
-put on the devices by the
+<p>The
+<a href="https://lkml.kernel.org/r/cover.1513264961.git.viresh.kumar@linaro.org">fifth
+version</a> of the patch series adding the Boot constraint subsystem is getting
+reviewed over the Linux kernel mailing list. The purpose of this subsystem is to
+honor the constraints put on the devices by the
<a href="https://en.wikipedia.org/wiki/Booting">bootloader</a> before the devices are
handed over to the operating system (OS), Linux in our case. In may cases we
want these devices to continue working as is until the time a Linux driver
@@ -328,19 +330,19 @@ static int __init foo_constraints_init(void)
}
subsys_initcall(foo_constraints_init);</code></pre>
</td></tr></table>
-<p>Few platforms are already updated to use the boot constraint core and its time
-for others to adapt to it to make their code better and fix such complex
-dependencies in a proper way. There is a lot of work on the cards going forward,
-getting rid of relatively slow boot time with deferred constraints, as described
-earlier, is of utmost priority. We may also want to get the constraint specific
-information from the device tree instead and reduce some platform specific code.
-It would also be interesting to use this work for other firmware interfaces like
-ACPI as these are common problems across architectures.</p>
+<p>The proposed series updates few platforms to use the boot constraint core and
+its time for others to adapt to it to make their code better and fix such
+complex dependencies in a proper way. There is a lot of work on the cards going
+forward, getting rid of relatively slow boot time with deferred constraints, as
+described earlier, is of utmost priority. We may also want to get the constraint
+specific information from the device tree instead and reduce some platform
+specific code. It would also be interesting to use this work for other firmware
+interfaces like ACPI as these are common problems across architectures.</p>
<p></p>
<p></p>
<hr><p><small>
Last updated
- 2018-01-10 14:57:25 IST
+ 2018-02-01 15:40:28 IST
</small></p>
</body>
</html>
diff --git a/boot_constraint/boot_constraint.txt b/boot_constraint/boot_constraint.txt
index 8b08d4a..4329d0b 100644
--- a/boot_constraint/boot_constraint.txt
+++ b/boot_constraint/boot_constraint.txt
@@ -1,9 +1,11 @@
The Boot Constraint Subsystem
=============================
-The Linux kernel link:[4.16-rc1] release includes support for the "Boot
-Constraint" subsystem. The purpose of this subsystem is to honor the constraints
-put on the devices by the
+The
+link:https://lkml.kernel.org/r/cover.1513264961.git.viresh.kumar@linaro.org[fifth
+version] of the patch series adding the Boot constraint subsystem is getting
+reviewed over the Linux kernel mailing list. The purpose of this subsystem is to
+honor the constraints put on the devices by the
link:https://en.wikipedia.org/wiki/Booting[bootloader] before the devices are
handed over to the operating system (OS), Linux in our case. In may cases we
want these devices to continue working as is until the time a Linux driver
@@ -362,11 +364,11 @@ static int __init foo_constraints_init(void)
subsys_initcall(foo_constraints_init);
----
-Few platforms are already updated to use the boot constraint core and its time
-for others to adapt to it to make their code better and fix such complex
-dependencies in a proper way. There is a lot of work on the cards going forward,
-getting rid of relatively slow boot time with deferred constraints, as described
-earlier, is of utmost priority. We may also want to get the constraint specific
-information from the device tree instead and reduce some platform specific code.
-It would also be interesting to use this work for other firmware interfaces like
-ACPI as these are common problems across architectures.
+The proposed series updates few platforms to use the boot constraint core and
+its time for others to adapt to it to make their code better and fix such
+complex dependencies in a proper way. There is a lot of work on the cards going
+forward, getting rid of relatively slow boot time with deferred constraints, as
+described earlier, is of utmost priority. We may also want to get the constraint
+specific information from the device tree instead and reduce some platform
+specific code. It would also be interesting to use this work for other firmware
+interfaces like ACPI as these are common problems across architectures.
diff --git a/boot_constraint/boot_constraint_orig.txt b/boot_constraint/boot_constraint_orig.txt
new file mode 100644
index 0000000..8b08d4a
--- /dev/null
+++ b/boot_constraint/boot_constraint_orig.txt
@@ -0,0 +1,372 @@
+The Boot Constraint Subsystem
+=============================
+
+The Linux kernel link:[4.16-rc1] release includes support for the "Boot
+Constraint" subsystem. The purpose of this subsystem is to honor the constraints
+put on the devices by the
+link:https://en.wikipedia.org/wiki/Booting[bootloader] before the devices are
+handed over to the operating system (OS), Linux in our case. In may cases we
+want these devices to continue working as is until the time a Linux driver
+reconfigures them.
+
+The bootloader is a piece of code that loads the operating system, normally
+after initializing a bunch of
+link:https://en.wikipedia.org/wiki/Semiconductor_intellectual_property_core[IP
+blocks] which are required during the boot process, e.g. flash memory
+controller. More than one bootloader may take part in booting the OS, wherein
+the first stage bootloader loads the second stage bootloader and the second
+state bootloader loads the OS. Some of the most common bootloaders used with
+Linux are LILO (LInux LOader), LOADLIN (LOAD LINux), GRUB (GRand Unified
+Bootloader), U-BOOT (Universal Bootloader) and UEFI (Unified Extensible Firmware
+Interface).
+
+The bootloaders enable (power ON) and configure lot of devices before passing
+their control to Linux and it is important to hand them over to Linux in a
+glitch-free manner for various reasons, for example to provide a good user
+experience.
+
+A typical example of that can be the display or
+link:https://en.wikipedia.org/wiki/Liquid-crystal_display[LCD] controller, which
+is used by the bootloaders to show image(s) while the platform boots Linux. The
+LCD controller may be using multiple resources, like clk, regulators, etc, that
+are shared with other devices. These shared resources must be configured in such
+a way that they satisfy need of all the devices that use them. If another
+device’s (X) driver gets probed before the LCD controller driver, then it may
+end up disabling or re-configuring those resources to ranges satisfying only the
+probed device (X) and that may make the LCD screen unstable and present a bad
+user experience. Another very common use case is of the debug serial port,
+enabled by the bootloader, which may be used by the kernel to debug early kernel
+oops with the `earlycon` command line parameter.
+
+Of course we can have more complex cases where the same resource is getting used
+by multiple devices and the order in which these devices get probed wouldn't
+matter as the device which gets probed first, may end up breaking rest of the
+devices that share the resources with it. We can also have a case where the
+resources are not shared, but the kernel disables them forcefully if no users
+appear until a certain point in the kernel boot process. An example of that is
+the clock framework which disables unused clocks from `late_initcall_sync()`
+initcall level.
+
+The boot constraint core solves these complex boot order dependencies between
+otherwise unrelated devices by setting the constraints on the shared resources,
+on behalf of the bootloader, before any of these devices are probed.
+
+For example, if the multimedia controller (MMC) and the LCD controller share the
+clock and regulator resources and both are enabled by the bootloader, then the
+boot constraint core adds the constraint on those resources before LCD or MMC
+controllers are probed by their kernel device drivers. The constraints are set
+differently for each resource type; It can be a simple `clk_enable()` operation
+for the clock constraint for example. The MMC and LCD controller can get probed
+in any order later on and the constraints added by the boot constraint core will
+be honored by the resources until the time the constraints are removed.
+
+The boot constraints for a device are added by the platform specific code
+currently. These constraints remain set until the time a driver tries to probe
+the device; The constraints are removed automatically by the drivers core
+after the device is probed successfully or unsuccessfully, except for the case
+where probe has deferred by returning `-EPROBE_DEFER` from the driver as some of
+the required resources to probe the device weren't available. The constraints
+are removed even for cases where probing of the device failed because the
+kernel will not retry probing the device by again by itself and there is no
+point keeping the constraint set in this case. This behavior can be modified in
+future if we have a use case where it makes sense to keep the constraint set
+even after failing to probe the device.
+
+Adding boot constraints
+-----------------------
+
+A boot constraint defines a configuration requirement for the device, set by the
+bootloader. For example, if the clock is enabled for a device by the bootloader
+and we want the device to be working as is until the time the device is probed
+by its driver, then keeping this clock enabled is one of the boot constraint.
+
+The boot constraint core supports three type of boot constraints currently,
+based on resource types and they are represented by the below enumeration:
+
+----
+enum dev_boot_constraint_type {
+ DEV_BOOT_CONSTRAINT_CLK,
+ DEV_BOOT_CONSTRAINT_PM,
+ DEV_BOOT_CONSTRAINT_SUPPLY,
+};
+----
+
+`DEV_BOOT_CONSTRAINT_CLK` represents the clock boot constraint,
+`DEV_BOOT_CONSTRAINT_PM` represents the power-domain boot constraint and
+`DEV_BOOT_CONSTRAINT_SUPPLY` represents the power-supply boot constraint. This
+list can be expanded in future and basically any resource type can have
+constraints set for it.
+
+A single boot constraint, of any type, can be added for a device using the below
+helper. This must be called before the device is probed by its driver, otherwise
+the boot constraint will never get removed and may result in unwanted behavior
+of the hardware.
+
+----
+int dev_boot_constraint_add(struct device *dev, struct dev_boot_constraint_info *info);
+----
+
+Here, `dev` represents the device for which the boot constraint is getting
+added, and `info` defines the boot constraint. This returns 0 on success, and
+a negative error number otherwise.
+
+The `struct dev_boot_constraint_info` contains an instance of the `struct
+dev_boot_constraint` and the optional `free_resources()` callback and its
+parameter. The boot constraint core calls the `free_resources()` callback, if
+available, with `free_resources_data` as its argument right after the constraint
+is removed. This callback can be implemented by platforms if they want to free
+some resources after the constraint is removed.
+
+----
+struct dev_boot_constraint_info {
+ struct dev_boot_constraint constraint;
+
+ /* This will be called just before the constraint is removed */
+ void (*free_resources)(void *data);
+ void *free_resources_data;
+};
+----
+
+The `struct dev_boot_constraint` represents the actual constraint. It contains
+`type` of the boot constraint and constraint specific `data` as each constraint
+type have different requirements. The `data` is mandatory for clock and
+power-supply boot constraints, while it is not required for the PM boot
+constraint.
+
+----
+struct dev_boot_constraint {
+ enum dev_boot_constraint_type type;
+ void *data;
+};
+----
+
+The clock boot constraint is represented by the `struct
+dev_boot_constraint_clk_info`, which contains only the `name` string. This
+string must match with the `connection-id` of the device's clock, otherwise
+the call to routine `clk_get(dev, name)`, called internally by the boot
+constraint core, will fail. The boot constraint core adds the clock constraint
+by performing the `clk_prepare_enable()` operation for the device's clock and
+removes it by performing the `clk_disable_unprepare()` operation.
+
+----
+struct dev_boot_constraint_clk_info {
+ const char *name;
+};
+----
+
+The power-supply constraint is represented by the `struct
+dev_boot_constraint_supply_info`, which contains the `name` string, `u_volt_min`
+and `u_volt_max`. The `name` string must match with the power-supply name as the
+boot constraint core uses it as an argument to the `regulator_get(dev, name)`
+helper. `u_volt_min` and `u_volt_max` define the minimum and maximum voltage
+allowed by the device (for which the constraint is added) under this constraint.
+The boot constraint core adds the power-supply constraint by first performing
+the `regulator_set_voltage()` operation (only if both `u_volt_min` and
+`u_volt_max` have non zero values), followed by the `regulator_enable()`
+operation. The constraint is removed by performing the `regulator_disable()`
+operation followed by the `regulator_set_voltage(reg, 0, INT_MAX)` operation,
+only if `regulator_set_voltage()` was called earlier.
+
+----
+struct dev_boot_constraint_supply_info {
+ const char *name;
+ unsigned int u_volt_min;
+ unsigned int u_volt_max;
+};
+----
+
+The PM boot constraint doesn't need any data and the boot constraint core adds
+the constraint by attaching the power-domain to the device by performing the
+`dev_pm_domain_attach()` operation, which internally enables the power-domain.
+The power-domain is normally attached to the device by the driver core while it
+binds a driver to the device, which wouldn't happen in this case as the
+power-domain is already attached by the boot constraint core. For this reason,
+the boot constraint core does not detach the power-domain while removing the
+constraint, as that will be done by the driver core once the driver gets
+detached from the device at a later point of time.
+
+Once the boot constraints are added, they will be honored by the boot constraint
+core until the time a driver tries to probe the device. As mentioned earlier,
+the constraints for a device are removed automatically by the driver core. It
+does so with the help of following helper, which must not be called directly by
+the platforms:
+
+----
+void dev_boot_constraints_remove(struct device *dev);
+----
+
+Here, `dev` is the device for which the boot constraints are getting removed.
+
+
+There is a limitation with the `dev_boot_constraint_add()` helper though. What
+if the resources, like clk or regulator, required to set the constraint for the
+device aren't available when the `dev_boot_constraint_add()` helper is called ?
+How do we make sure that the boot constraint core gets a chance to set the
+constraints before the device is probed by its driver, if the resources are
+going to be available at a later point of time ?
+
+The solution of these problems is twofold; First we have to make the process
+of adding constraints deferrable, so that the driver core tries adding
+constraints again at a later point of time; And second we have to make sure that
+the boot constraint core gets a chance to add the constraints right
+after the resources are made available and before the device (to which we are
+adding the constraints) is probed by its driver.
+
+The first solution is implemented with the help of (virtual) device-driver
+pairs; The boot constraint core implements an internal platform driver with the
+name `"boot-constraints-dev"` and its `boot_constraint_probe()` callback gets
+called for every device registered with the same name as the driver. An internal
+routine `dev_boot_constraint_add_deferrable()`, which is called by one of the
+exported helper described later, creates a platform device
+(with name `"boot-constraints-dev"`) for every device constraint we want to add.
+The `boot_constraint_probe()` callback gets called for each of these platform
+devices (or individual constraints) and tries to add the respective constraint.
+If the resources required for adding the constraint aren't available yet, the
+probe callback returns `-EPROBE_DEFER` and the driver core moves the virtual
+device to the list of deferred devices, which are re-probed at a later point of
+time. On successful probing of these virtual devices, the constraints are added
+with the boot constraints core. The `dev_boot_constraint_add_deferrable()`
+helper uses the `free_resources()` callback and its argument, present in the
+`struct dev_boot_constraint_info`, to unregister the virtual device once the
+constraint is removed.
+
+The second problem of the race with the actual device getting probed before the
+constraints are set once the resources are made available, is solved by
+requesting the driver core to start probing of the deferred devices immediately
+after `boot_constraint_probe()` returned `-EPROBE_DEFER` for any of the
+constraints. Normally the driver core allows re-probing of the deferred devices
+from an internal routine registered with `late_initcall()`. But if the boot
+constraint core finds that it failed to add one of the constraint because its
+resources weren't available, then it requests the drivers core to enable the
+re-probing of deferred devices from that point itself. With that the driver core
+tries to re-probe the deferred devices after every new device is registered with
+it and so the constraint specific platform device gets a chance to get probed
+before the actual device for which constraints are getting added. Yes, this puts
+extra burden on the system during boot process and may make the kernel boot
+process slightly longer, but it is the best we could do as an initial solution.
+Later on we may want the boot constraint core to register with the resource
+specific subsystems, so that they can inform the boot constraint core once the
+resource is available. This way the boot constraint core can get a chance to set
+the constraint before the actual device gets probed. But that is future work.
+
+In order to support deferrable constraints and simplify adding multiple boot
+constraints for a platform, the boot constraints core provides another helper
+which calls the internal routine `dev_boot_constraint_add_deferrable()`
+described earlier.
+
+----
+void dev_boot_constraint_add_deferrable_of(struct dev_boot_constraint_of *oconst, int count);
+----
+
+Here, `oconst` represents an array of
+link:https://en.wikipedia.org/wiki/Device_tree[device tree] (DT) constraints and
+`count` represents the number of entries in the array.
+
+The `dev_boot_constraint_add_deferrable_of()` helper provides an easy way to add
+one or more deferrable constraints for one or more devices. Just like the
+`dev_boot_constraint_add()` helper, this must be called before the devices (to
+which we want to add constraints) are probed by their drivers. This adds the
+boot constraints in a deferrable way and the caller need not worry about the
+availability of the resources required by the constraint. This routine returns
+immediately even if resources for some of the constraints weren't available
+then.
+
+`struct dev_boot_constraint_of` represents one or more constraints corresponding
+to one or more devices with the same DT `compatible` string. The DT `compatible`
+string allow a device to express its compatibility with a family of similar
+devices, in some cases, allowing a single driver to match against several
+devices regardless of their actual names.
+
+----
+struct dev_boot_constraint_of {
+ const char *compat;
+ struct dev_boot_constraint *constraints;
+ unsigned int count;
+
+ const char * const *dev_names;
+ unsigned int dev_names_count;
+};
+----
+
+The `compat` string must match with the DT compatible property of the devices to
+which we want to apply constraints, `constraints` is an array of one or more
+boot constraints and `count` is the array size. The constraints will get added
+for every device that matches with the `compat` string, unless the optional
+`dev_names` field is set to a non NULL value. The `dev_names` field is used to
+confine the boot constraints to only a subset of devices that matches the
+`compat` string; `dev_names` is an array containing names (e.g.
+`"serial@fff02000"`) of the DT nodes where the constraints must be applied and
+`dev_names_count` is the array size.
+
+Example
+-------
+
+Here is an example of how boot constraints are set for multiple devices for a
+platform. A clock constraint is set for the devices with compatible property
+`"foo,serial"`, but is limited only to a subset of such devices:
+`"serial@fff02000"` and `"serial@fff32000"`. A power-supply and PM constraint is
+set for every device whose compatible property matches `"foo,display"`.
+
+----
+static struct dev_boot_constraint_clk_info uart_clk_info = {
+ .name = "uartclk",
+};
+
+static struct dev_boot_constraint foo_uart_constraints[] = {
+ {
+ .type = DEV_BOOT_CONSTRAINT_CLK,
+ .data = &uart_clk_info,
+ },
+};
+
+static const char * const uart_subset[] = {
+ "serial@fff02000", /* UART 3 */
+ "serial@fff32000", /* UART 6 */
+};
+
+static struct dev_boot_constraint_supply_info vdda_info = {
+ .name = "vdda"
+};
+
+static struct dev_boot_constraint foo_display_constraints[] = {
+ {
+ .type = DEV_BOOT_CONSTRAINT_SUPPLY,
+ .data = &vdda_info,
+ }, {
+ .type = DEV_BOOT_CONSTRAINT_PM,
+ .data = NULL,
+ },
+};
+
+static struct dev_boot_constraint_of foo_constraints[] = {
+ {
+ .compat = "foo,serial",
+ .constraints = foo_uart_constraints,
+ .count = ARRAY_SIZE(foo_uart_constraints),
+
+ .dev_names = uart_subset,
+ .dev_names_count = ARRAY_SIZE(uart_subset),
+ }, {
+ .compat = "foo,display",
+ .constraints = foo_display_constraints,
+ .count = ARRAY_SIZE(foo_display_constraints),
+ },
+};
+
+static int __init foo_constraints_init(void)
+{
+ dev_boot_constraint_add_deferrable_of(foo_constraints, ARRAY_SIZE(foo_constraints));
+
+ return 0;
+}
+subsys_initcall(foo_constraints_init);
+----
+
+Few platforms are already updated to use the boot constraint core and its time
+for others to adapt to it to make their code better and fix such complex
+dependencies in a proper way. There is a lot of work on the cards going forward,
+getting rid of relatively slow boot time with deferred constraints, as described
+earlier, is of utmost priority. We may also want to get the constraint specific
+information from the device tree instead and reduce some platform specific code.
+It would also be interesting to use this work for other firmware interfaces like
+ACPI as these are common problems across architectures.