diff options
author | Viresh Kumar <viresh.kumar@linaro.org> | 2018-02-02 13:00:59 +0530 |
---|---|---|
committer | Viresh Kumar <viresh.kumar@linaro.org> | 2018-02-02 13:00:59 +0530 |
commit | 78c72960f7d7487c4628eb8c80ddeba7f39839b2 (patch) | |
tree | 283ceaee70c849ef752f912c50b822ca0a3a1178 | |
parent | 88a0e65049e8e66073b0c8fb3dfb79621dda29ec (diff) |
updates
Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
-rw-r--r-- | boot_constraint/boot_constraint.html | 26 | ||||
-rw-r--r-- | boot_constraint/boot_constraint.txt | 24 | ||||
-rw-r--r-- | boot_constraint/boot_constraint_orig.txt | 372 |
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. |