summaryrefslogtreecommitdiffstats
path: root/configs/platform-v7a/patches/barebox-2016.05.0/0001-bootstate-add-framework-for-redundant-boot-scenarios.patch
diff options
context:
space:
mode:
Diffstat (limited to 'configs/platform-v7a/patches/barebox-2016.05.0/0001-bootstate-add-framework-for-redundant-boot-scenarios.patch')
-rw-r--r--configs/platform-v7a/patches/barebox-2016.05.0/0001-bootstate-add-framework-for-redundant-boot-scenarios.patch1467
1 files changed, 0 insertions, 1467 deletions
diff --git a/configs/platform-v7a/patches/barebox-2016.05.0/0001-bootstate-add-framework-for-redundant-boot-scenarios.patch b/configs/platform-v7a/patches/barebox-2016.05.0/0001-bootstate-add-framework-for-redundant-boot-scenarios.patch
deleted file mode 100644
index d665772..0000000
--- a/configs/platform-v7a/patches/barebox-2016.05.0/0001-bootstate-add-framework-for-redundant-boot-scenarios.patch
+++ /dev/null
@@ -1,1467 +0,0 @@
-From: Marc Kleine-Budde <mkl@pengutronix.de>
-Date: Fri, 20 Feb 2015 09:19:14 +0100
-Subject: [PATCH] bootstate: add framework for redundant boot scenarios
-
-There are several use cases where a redundant Linux system is needed. The
-barebox,bootstate framework provides the building blocks to model different
-use cases without the need to start from the scratch over and over again.
-
-Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
----
- .../bindings/barebox/barebox,bootstate.rst | 236 +++++++
- arch/sandbox/dts/sandbox.dts | 83 +++
- commands/Kconfig | 5 +
- commands/Makefile | 1 +
- commands/bootchooser.c | 101 +++
- common/Kconfig | 8 +
- common/Makefile | 1 +
- common/bootstate.c | 781 +++++++++++++++++++++
- drivers/misc/Kconfig | 5 +
- drivers/misc/Makefile | 1 +
- drivers/misc/bootstate.c | 79 +++
- include/bootstate.h | 39 +
- 12 files changed, 1340 insertions(+)
- create mode 100644 Documentation/devicetree/bindings/barebox/barebox,bootstate.rst
- create mode 100644 commands/bootchooser.c
- create mode 100644 common/bootstate.c
- create mode 100644 drivers/misc/bootstate.c
- create mode 100644 include/bootstate.h
-
-diff --git a/Documentation/devicetree/bindings/barebox/barebox,bootstate.rst b/Documentation/devicetree/bindings/barebox/barebox,bootstate.rst
-new file mode 100644
-index 000000000000..0f7131536798
---- /dev/null
-+++ b/Documentation/devicetree/bindings/barebox/barebox,bootstate.rst
-@@ -0,0 +1,236 @@
-+barebox bootstate
-+=================
-+
-+Overview
-+--------
-+
-+There are several use cases where a redundant Linux system is needed.
-+The ``barebox,bootstate`` framework provides the building blocks to
-+model different use cases without the need to start from the scratch
-+over and over again.
-+
-+The ``barebox,bootstate`` works on abstract boot targets, each with a
-+set of properties and implements an algorithm which selects the
-+highest priority target to boot.
-+
-+A set of boot targets can be described in a devicetree node. This
-+node could be part of the regular devicetree blob or it could be an
-+extra devicetree for the bootstate.
-+
-+A bootstate node contains a description of a set of boot targets along
-+with a place where to store the mutable state. Currently implemented
-+backends are :ref:`barebox,state` and ``nv`` (:ref:`command_nv`)
-+variables.
-+
-+Required properties:
-+
-+* ``compatible``: should be ``barebox,bootstate``;
-+* ``backend-type``: should be ``state`` or ``nv``.
-+
-+Optional properties:
-+
-+* ``backend``: phandle to the :ref:`barebox,state` backend
-+
-+
-+boot target nodes - immutable description
-+-----------------------------------------
-+
-+These are subnodes of a bootstate node, each describing a boot
-+target. The node name may end with ``@<ADDRESS>``, but the suffix is
-+stripped from the target name.
-+
-+Optional properties:
-+
-+* ``default_attempts``: If the boot attempts counter is reset, this
-+ value is used.
-+
-+Example::
-+
-+ bootstate: bootstate {
-+ compatible = "barebox,bootstate";
-+ backend-type = "state";
-+ backend = <&state>;
-+
-+ system0 {
-+ default_attempts = <3>;
-+ };
-+
-+ system1 {
-+ default_attempts = <3>;
-+ };
-+ };
-+
-+In this example a bootstate, using a :ref:`barebox,state` backend with
-+two boot target nodes ``system0`` and ``system1`` is defined. When the
-+boot attempts counter is reset, the default value of ``3`` is used for
-+both targets.
-+
-+
-+boot target nodes - mutable state
-+---------------------------------
-+
-+The above example uses a :ref:`barebox,state` backend, which requires
-+some additional configuration to hold the mutable
-+state. :ref:`barebox,state` has to be explicidly configured, while
-+``nv`` (:ref:`command_nv`) variables are created on the fly.
-+
-+The state of each boot target consists of the following ``uint32``
-+varibles:
-+
-+* ``remaining_attempts``: holds the number of remaining boot attempts.
-+ This variable is changed by the bootstate algorithm during boot.
-+* ``priority``: defines the priority of the boot target. Higher number
-+ indicate a higher priority, If two boot targets have the same
-+ priority the one defined first in the device tree has precedence.
-+ The ``priority`` can optionally be changed by the algorithm to 0, if
-+ the boot target is decremented to ``0`` remaining boot attempts. A
-+ ``priority`` of ``0`` means the boot target is **deactivated** and
-+ will not be considered a valid target during further boots. If the
-+ remaining attempts counter is reset, a target with priority 0 is
-+ **not** changed.
-+* ``ok``: this is an opaque value, it's not accessed by the bootstate
-+ algorithm. It can be used be the Linux system to track the first
-+ boot after an update.
-+
-+The bootstate can also hold a default watchdog timeout (in seconds),
-+which can be activated by the bootstate algorithm.
-+
-+Example::
-+
-+ state: state {
-+ magic = <0x4d433230>;
-+ compatible = "barebox,state";
-+ backend-type = "raw";
-+ backend = <&backend_state>;
-+ #address-cells = <1>;
-+ #size-cells = <1>;
-+
-+ bootstate {
-+ #address-cells = <1>;
-+ #size-cells = <1>;
-+
-+ system0 {
-+ #address-cells = <1>;
-+ #size-cells = <1>;
-+
-+ remaining_attempts {
-+ reg = <0x0 0x4>;
-+ type = "uint32";
-+ };
-+ priority {
-+ reg = <0x4 0x4>;
-+ type = "uint32";
-+ };
-+ ok {
-+ reg = <0x8 0x4>;
-+ type = "uint32";
-+ };
-+ };
-+
-+ system1 {
-+ #address-cells = <1>;
-+ #size-cells = <1>;
-+
-+ remaining_attempts {
-+ reg = <0x10 0x4>;
-+ type = "uint32";
-+ };
-+ priority {
-+ reg = <0x14 0x4>;
-+ type = "uint32";
-+ };
-+ ok {
-+ reg = <0x18 0x4>;
-+ type = "uint32";
-+ };
-+ };
-+
-+ watchdog_timeout {
-+ reg = <0x20 0x4>;
-+ type = "uint32";
-+ default = <60>;
-+ };
-+ };
-+ };
-+
-+This example defines two boot targets (``system0`` and ``system1``) and
-+a watchdog timeout of ``60`` seconds.
-+
-+
-+Backends
-+--------
-+
-+Currently two backends exist. The :ref:`barebox,state` backend is a
-+bit more complicated to setup, as all boot target have to be described
-+in the referenced :ref:`barebox,state` in the device tree. On the
-+upside, the advantages of the (possible redundant storage, etc...) of
-+the :ref:`barebox,state` is gained for free.
-+
-+The :ref:`command_nv` backend is a lot simpler, no special setup is
-+needed, it should run on every board, which already implements a
-+read/writeable barebox environment.
-+
-+
-+Algorithm
-+---------
-+
-+The low level algorithm is implemented by the
-+``bootstate_get_target()`` function. Its job is to iterate over all
-+boot sources and return the name (as a string) of the choosen boot
-+target.
-+
-+The algorithm iterates over all boot targets defined under the
-+associated device tree node and picks the one with the highest
-+``priority`` (higher number have a higher priority) where the
-+``remaining_attempts`` is greater than zero. A pointer to the name of
-+the boot target is returned, the string should be freed via ``free()``.
-+
-+The behaviour can be modified with the flags paramter. The following
-+flags are currently supported:
-+
-+* ``BOOTCHOOSER_FLAG_ATTEMPTS_KEEP``: the ``remaining_attempts``
-+ counter of the choosen boot target is not changed.
-+* ``BOOTCHOOSER_FLAG_ATTEMPTS_DEC``: the ``remaining_attempts``
-+ counter of the choosen boot target is decremented by one.
-+* ``BOOTCHOOSER_FLAG_ATTEMPTS_RESET``: the ``remaining_attempts``
-+ counter of all *active* boot targets (those with ``priority > 0``)
-+ are reset to their default values as defined in the immutable
-+ description by ``default_attempts``.
-+* ``BOOTCHOOSER_FLAG_DEACTIVATE_ON_ZERO_ATTEMPTS``: if used together
-+ with ``BOOTCHOOSER_FLAG_ATTEMPTS_DEC`` and the
-+ ``remaining_attempts`` counter of the choosen boot target is
-+ decremented to ``0``, the boot target is deactivated for further
-+ boot attempts (although *this* boot is attemped as usual). This is
-+ done by setting the ``priority`` to ``0``.
-+* ``BOOTCHOOSER_FLAG_VERBOSE``: increases the verbosity of the output
-+
-+
-+Frontend
-+--------
-+
-+The shell command ``bootchooser`` (:ref:`command_bootchooser`) can be
-+used to choose and start a boot target by a shell one-liner. The
-+command picks the boot target with the highest priority and calls the
-+``boot`` (:ref:`command_boot`) command with the selected boot target
-+as its first and only parameter.
-+
-+The ``bootchooser`` command implements command line paramter versions
-+of the above described flags:
-+
-+* ``-k``: keep boot attempts
-+* ``-d``: decrement boot attempts
-+* ``-r``: reset boot attempts
-+* ``-z``: deactivate on zero remaining attempts
-+* ``-v``: verbose output
-+
-+Next to the standard parameters, these additional options are
-+implemented:
-+
-+* ``-D``: dryrun - do not boot (all other functionality is active) - a
-+ specified watchdog timeout will be activated.
-+* ``-R``: retry - if booting fails, then chose next target, but
-+ decrement its attempts. Note: if the current target has still the
-+ highest priority and remaining attemts, it will be selected again.
-+* ``-w <TIMEOUT_IN_SEC>``: activate watchdog - if no parameter is
-+ given, the timeout from the device tree is used. A given parameter
-+ overwrites the device tree default.
-diff --git a/arch/sandbox/dts/sandbox.dts b/arch/sandbox/dts/sandbox.dts
-index 2595aa13fa62..e2bc8f76c2e3 100644
---- a/arch/sandbox/dts/sandbox.dts
-+++ b/arch/sandbox/dts/sandbox.dts
-@@ -3,5 +3,88 @@
- #include "skeleton.dtsi"
-
- / {
-+ aliases {
-+ state = &state;
-+ };
-
-+ state: state {
-+ magic = <0x4d433230>;
-+ compatible = "barebox,state";
-+ backend-type = "dtb";
-+ backend = "/fd0";
-+
-+ bootstate {
-+ system0 {
-+ #address-cells = <1>;
-+ #size-cells = <1>;
-+
-+ remaining_attempts {
-+ reg = <0x0 0x4>;
-+ type = "uint32";
-+ };
-+ priority {
-+ reg = <0x4 0x4>;
-+ type = "uint32";
-+ };
-+ ok {
-+ reg = <0x8 0x4>;
-+ type = "uint32";
-+ };
-+ };
-+
-+ system1 {
-+ #address-cells = <1>;
-+ #size-cells = <1>;
-+
-+ remaining_attempts {
-+ reg = <0x10 0x4>;
-+ type = "uint32";
-+ };
-+ priority {
-+ reg = <0x14 0x4>;
-+ type = "uint32";
-+ };
-+ ok {
-+ reg = <0x18 0x4>;
-+ type = "uint32";
-+ };
-+ };
-+
-+ factory {
-+ #address-cells = <1>;
-+ #size-cells = <1>;
-+
-+ remaining_attempts {
-+ reg = <0x20 0x4>;
-+ type = "uint32";
-+ };
-+ priority {
-+ reg = <0x24 0x4>;
-+ type = "uint32";
-+ };
-+ ok {
-+ reg = <0x28 0x4>;
-+ type = "uint32";
-+ };
-+ };
-+ };
-+ };
-+
-+ bootstate: bootstate {
-+ compatible = "barebox,bootstate";
-+ backend-type = "state"; // or "nv", or "efivar"
-+ backend = <&state>;
-+
-+ system0 {
-+ default_attempts = <3>;
-+ };
-+
-+ system1 {
-+ default_attempts = <3>;
-+ };
-+
-+ factory {
-+ default_attempts = <3>;
-+ };
-+ };
- };
-diff --git a/commands/Kconfig b/commands/Kconfig
-index 875c5f4f01ed..603e887b15ec 100644
---- a/commands/Kconfig
-+++ b/commands/Kconfig
-@@ -2189,6 +2189,11 @@ config CMD_SPD_DECODE
- help
- decode spd eeprom
-
-+config CMD_BOOTCHOOSER
-+ tristate
-+ depends on BOOTSTATE
-+ prompt "bootchooser"
-+
- # end Miscellaneous commands
- endmenu
-
-diff --git a/commands/Makefile b/commands/Makefile
-index f1b482f04934..bb3b40809af5 100644
---- a/commands/Makefile
-+++ b/commands/Makefile
-@@ -117,3 +117,4 @@ obj-$(CONFIG_CMD_DHRYSTONE) += dhrystone.o
- obj-$(CONFIG_CMD_SPD_DECODE) += spd_decode.o
- obj-$(CONFIG_CMD_MMC_EXTCSD) += mmc_extcsd.o
- obj-$(CONFIG_CMD_NAND_BITFLIP) += nand-bitflip.o
-+obj-$(CONFIG_CMD_BOOTCHOOSER) += bootchooser.o
-diff --git a/commands/bootchooser.c b/commands/bootchooser.c
-new file mode 100644
-index 000000000000..06ca5e41d527
---- /dev/null
-+++ b/commands/bootchooser.c
-@@ -0,0 +1,101 @@
-+/*
-+ * Copyright (C) 2012 Jan Luebbe <j.luebbe@pengutronix.de>
-+ * Copyright (C) 2015 Marc Kleine-Budde <mkl@pengutronix.de>
-+ *
-+ * This program is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-+ * GNU General Public License for more details.
-+ */
-+
-+#include <bootstate.h>
-+#include <command.h>
-+#include <common.h>
-+#include <getopt.h>
-+#include <malloc.h>
-+#include <stdio.h>
-+
-+static int do_bootchooser(int argc, char *argv[])
-+{
-+ unsigned flags = 0, timeout = 0;
-+ char *name = NULL;
-+ int opt, ret;
-+
-+ while ((opt = getopt(argc, argv, "kdrzvDRw::")) > 0) {
-+ switch (opt) {
-+ case 'k':
-+ flags |= BOOTCHOOSER_FLAG_ATTEMPTS_KEEP;
-+ break;
-+ case 'd':
-+ flags |= BOOTCHOOSER_FLAG_ATTEMPTS_DEC;
-+ break;
-+ case 'r':
-+ flags |= BOOTCHOOSER_FLAG_ATTEMPTS_RESET;
-+ break;
-+ case 'z':
-+ flags |= BOOTCHOOSER_FLAG_DEACTIVATE_ON_ZERO_ATTEMPTS;
-+ break;
-+ case 'v':
-+ flags |= BOOTCHOOSER_FLAG_VERBOSE;
-+ break;
-+ case 'D':
-+ flags |= BOOTCHOOSER_FLAG_DRYRUN;
-+ break;
-+ case 'R':
-+ flags |= BOOTCHOOSER_FLAG_RETRY_WITH_DEC;
-+ break;
-+ case 'w':
-+ if (optarg)
-+ timeout = simple_strtoul(optarg, NULL, 0);
-+ else
-+ flags |= BOOTCHOOSER_FLAG_WATCHDOG_TIMEOUT_FROM_STATE;
-+ flags |= BOOTCHOOSER_FLAG_WATCHDOG_ENABLE;
-+ break;
-+ default:
-+ return COMMAND_ERROR_USAGE;
-+ }
-+ }
-+
-+ if (optind < argc)
-+ name = argv[optind];
-+
-+ if (!(flags & (BOOTCHOOSER_FLAG_ATTEMPTS_KEEP |
-+ BOOTCHOOSER_FLAG_ATTEMPTS_DEC |
-+ BOOTCHOOSER_FLAG_ATTEMPTS_RESET))) {
-+ bootstate_info();
-+ return 0;
-+ }
-+
-+ if ((flags & BOOTCHOOSER_FLAG_ATTEMPTS_KEEP) &&
-+ (flags & (BOOTCHOOSER_FLAG_ATTEMPTS_DEC | BOOTCHOOSER_FLAG_ATTEMPTS_RESET)))
-+ return COMMAND_ERROR_USAGE;
-+
-+ ret = bootstate_bootchooser(name, flags, timeout);
-+
-+ return ret ? COMMAND_ERROR : COMMAND_SUCCESS;
-+}
-+
-+BAREBOX_CMD_HELP_START(bootchooser)
-+BAREBOX_CMD_HELP_TEXT("Options:")
-+BAREBOX_CMD_HELP_OPT ("-k","keep - boot, don't modify attempts counter")
-+BAREBOX_CMD_HELP_OPT ("-d","decrement - boot, but decrement attempts counter by one")
-+BAREBOX_CMD_HELP_OPT ("-r","reset - boot, but reset _all_ attempts counters to default")
-+BAREBOX_CMD_HELP_OPT ("-z","deactivate chosen target on zero remaining boot attempts")
-+BAREBOX_CMD_HELP_OPT ("-v","verbose output")
-+BAREBOX_CMD_HELP_OPT ("-D","dryrun - do not boot, but handle watchdog and reset")
-+BAREBOX_CMD_HELP_OPT ("-R","retry - boot, retry next boot target and decrement attempts")
-+BAREBOX_CMD_HELP_OPT ("-w","activate watchdog, use timeout specified in <BOOTSTATE>.watchdog_timeout")
-+BAREBOX_CMD_HELP_END
-+
-+BAREBOX_CMD_START(bootchooser)
-+ .cmd = do_bootchooser,
-+ BAREBOX_CMD_DESC("automatically select a boot target and boot")
-+ BAREBOX_CMD_OPTS("[-kdrzvDR] -w <TIMEOUT> [BOOTSTATE]")
-+ BAREBOX_CMD_GROUP(CMD_GRP_MISC)
-+ BAREBOX_CMD_HELP(cmd_bootchooser_help)
-+BAREBOX_CMD_END
-diff --git a/common/Kconfig b/common/Kconfig
-index 7c09e8c117f1..f17769661ee6 100644
---- a/common/Kconfig
-+++ b/common/Kconfig
-@@ -792,6 +792,14 @@ config STATE_CRYPTO
- See Documentation/devicetree/bindings/barebox/barebox,state.rst
- for more information.
-
-+config BOOTSTATE
-+ bool "bootstate infrastructure"
-+ depends on OF_BAREBOX_DRIVERS
-+ select ENVIRONMENT_VARIABLES
-+ select OFTREE
-+ select PARAMETER
-+ select BOOTSTATE_DRV
-+
- config RESET_SOURCE
- bool "detect Reset cause"
- depends on GLOBALVAR
-diff --git a/common/Makefile b/common/Makefile
-index d99ca7b7ac74..5505449874ad 100644
---- a/common/Makefile
-+++ b/common/Makefile
-@@ -46,6 +46,7 @@ obj-$(CONFIG_SHELL_HUSH) += hush.o
- obj-$(CONFIG_SHELL_SIMPLE) += parser.o
- obj-$(CONFIG_STATE) += state.o
- obj-$(CONFIG_RATP) += ratp.o
-+obj-$(CONFIG_BOOTSTATE) += bootstate.o
- obj-$(CONFIG_UIMAGE) += image.o uimage.o
- obj-$(CONFIG_FITIMAGE) += image-fit.o
- obj-$(CONFIG_MENUTREE) += menutree.o
-diff --git a/common/bootstate.c b/common/bootstate.c
-new file mode 100644
-index 000000000000..3dc18813064d
---- /dev/null
-+++ b/common/bootstate.c
-@@ -0,0 +1,781 @@
-+/*
-+ * Copyright (C) 2012 Jan Luebbe <j.luebbe@pengutronix.de>
-+ * Copyright (C) 2015 Marc Kleine-Budde <mkl@pengutronix.de>
-+ *
-+ * This program is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-+ * GNU General Public License for more details.
-+ */
-+
-+#include <bootstate.h>
-+#include <common.h>
-+#include <envfs.h>
-+#include <environment.h>
-+#include <errno.h>
-+#include <fcntl.h>
-+#include <fs.h>
-+#include <globalvar.h>
-+#include <init.h>
-+#include <ioctl.h>
-+#include <libbb.h>
-+#include <libfile.h>
-+#include <malloc.h>
-+#include <net.h>
-+#include <printk.h>
-+#include <state.h>
-+#include <stdio.h>
-+#include <watchdog.h>
-+#include <xfuncs.h>
-+
-+#include <linux/err.h>
-+#include <linux/kernel.h>
-+#include <linux/list.h>
-+#include <linux/mtd/mtd-abi.h>
-+#include <linux/mtd/mtd.h>
-+
-+#include <asm/unaligned.h>
-+
-+/* list of all registered bootstate instances */
-+static LIST_HEAD(bootstate_list);
-+
-+struct state_backend;
-+
-+struct bootstate {
-+ struct device_d dev;
-+ const char *name;
-+ struct list_head list;
-+ struct list_head targets;
-+ struct list_head targets_unsorted;
-+ struct bootstate_backend *backend;
-+ bool dirty;
-+};
-+
-+struct bootstate_backend {
-+ int (*load)(struct bootstate_backend *backend, struct bootstate *bootstate);
-+ int (*save)(struct bootstate_backend *backend, struct bootstate *bootstate);
-+ const char *name;
-+ const char *path;
-+};
-+
-+struct bootstate_target {
-+ struct list_head list;
-+ struct list_head list_unsorted;
-+
-+ /* state */
-+ unsigned int priority;
-+ unsigned int remaining_attempts;
-+ bool ok;
-+
-+ /* spec */
-+ const char *name;
-+ unsigned int default_attempts;
-+};
-+
-+static void pr_target(struct bootstate *bootstate, struct bootstate_target *target)
-+{
-+ printf("%s: target: name=%s prio=%u, ok=%d, rem=%u, def=%u\n",
-+ bootstate->name, target->name, target->priority, target->ok,
-+ target->remaining_attempts, target->default_attempts);
-+}
-+
-+static struct bootstate *bootstate_new(const char *name)
-+{
-+ struct bootstate *bootstate;
-+ int ret;
-+
-+ bootstate = xzalloc(sizeof(*bootstate));
-+ safe_strncpy(bootstate->dev.name, name, MAX_DRIVER_NAME);
-+ bootstate->name = bootstate->dev.name;
-+ bootstate->dev.id = DEVICE_ID_DYNAMIC;
-+ INIT_LIST_HEAD(&bootstate->targets);
-+ INIT_LIST_HEAD(&bootstate->targets_unsorted);
-+
-+ ret = register_device(&bootstate->dev);
-+ if (ret) {
-+ free(bootstate);
-+ return ERR_PTR(ret);
-+ }
-+
-+ list_add_tail(&bootstate->list, &bootstate_list);
-+
-+ return bootstate;
-+}
-+
-+void bootstate_release(struct bootstate *bootstate)
-+{
-+ list_del(&bootstate->list);
-+ unregister_device(&bootstate->dev);
-+ free(bootstate);
-+}
-+
-+static int bootstate_target_compare(struct list_head *a, struct list_head *b)
-+{
-+ struct bootstate_target *bootstate_a = list_entry(a, struct bootstate_target, list);
-+ struct bootstate_target *bootstate_b = list_entry(b, struct bootstate_target, list);
-+
-+ /* order descending */
-+ return bootstate_a->priority >= bootstate_b->priority ? -1 : 1;
-+}
-+
-+static void bootstate_target_add(struct bootstate *bootstate, struct bootstate_target *target)
-+{
-+ list_del(&target->list);
-+ list_add_sort(&target->list, &bootstate->targets, bootstate_target_compare);
-+}
-+
-+static int bootstate_variable_read_u32(const struct bootstate *bootstate,
-+ const char *name, uint32_t *out_val)
-+{
-+ char *var;
-+ int ret;
-+
-+ var = asprintf("%s.%s.%s", bootstate->backend->path, bootstate->name, name);
-+ ret = getenv_uint(var, out_val);
-+ free(var);
-+
-+ return ret;
-+}
-+
-+static int bootstate_backend_variable_read_target_u32(const struct bootstate_backend *backend,
-+ const struct bootstate *bootstate,
-+ const struct bootstate_target *target,
-+ const char *name, uint32_t *out_val)
-+{
-+ char *var;
-+ int ret;
-+
-+ var = asprintf("%s.%s.%s.%s", backend->path, bootstate->name,
-+ target->name, name);
-+ ret = getenv_uint(var, out_val);
-+ free(var);
-+
-+ return ret;
-+}
-+
-+static int bootstate_backend_variable_write_target_u32(const struct bootstate_backend *backend,
-+ const struct bootstate *bootstate,
-+ const struct bootstate_target *target,
-+ const char *name, uint32_t in_val)
-+{
-+ char *var;
-+ char *val;
-+ int ret;
-+
-+ var = asprintf("%s.%s.%s.%s", backend->path, bootstate->name,
-+ target->name, name);
-+ val = asprintf("%d", in_val);
-+ ret = setenv(var, val);
-+ free(val);
-+ free(var);
-+
-+ return ret;
-+}
-+
-+static int bootstate_variable_nv_init_u32(const struct bootstate_backend *backend,
-+ const struct bootstate *bootstate,
-+ const struct bootstate_target *target,
-+ const char *name)
-+{
-+ char *var;
-+ int ret;
-+
-+ var = asprintf("%s.%s.%s", bootstate->name, target->name, name);
-+ ret = nvvar_add(var, "0");
-+ free(var);
-+
-+ return ret;
-+}
-+
-+static struct bootstate_target *bootstate_target_find(const struct bootstate *bootstate,
-+ const char *name)
-+{
-+ struct bootstate_target *target;
-+
-+ list_for_each_entry(target, &bootstate->targets, list) {
-+ if (!strcmp(target->name, name))
-+ return target;
-+ }
-+
-+ return ERR_PTR(-ENOENT);
-+}
-+
-+static int bootstate_target_from_node(struct bootstate *bootstate, const struct device_node *node, bool create)
-+{
-+ struct bootstate_target *target;
-+ char *name, *indexs;
-+ int ret;
-+
-+ name = xstrdup(node->name);
-+ indexs = strchr(name, '@');
-+ if (indexs)
-+ *indexs++ = 0;
-+
-+ if (create) {
-+ /* create*/
-+ target = xzalloc(sizeof(*target));
-+
-+ target->name = xstrdup(name);
-+ list_add_tail(&target->list, &bootstate->targets);
-+ list_add_tail(&target->list_unsorted,
-+ &bootstate->targets_unsorted);
-+ } else {
-+ target = bootstate_target_find(bootstate, name);
-+ if (IS_ERR(target)) {
-+ int ret = PTR_ERR(target);
-+ pr_err("no such boot target: %s: %s\n",
-+ name, strerror(-ret));
-+ return ret;
-+ }
-+ }
-+
-+ /* init */
-+ ret = of_property_read_u32(node, "default_attempts",
-+ &target->default_attempts);
-+ if (ret)
-+ return ret;
-+
-+ free(name);
-+
-+ return 0;
-+}
-+
-+static int bootstate_from_node(struct bootstate *bootstate,
-+ const struct device_node *node, bool create)
-+{
-+ struct device_node *child;
-+ int ret;
-+
-+ for_each_child_of_node(node, child) {
-+ ret = bootstate_target_from_node(bootstate, child, create);
-+ if (ret)
-+ return ret;
-+ }
-+
-+ return 0;
-+}
-+
-+static int bootstate_backend_load_one(const struct bootstate_backend *backend,
-+ const struct bootstate *bootstate,
-+ struct bootstate_target *target)
-+{
-+ uint32_t tmp;
-+ int ret;
-+
-+ ret = bootstate_backend_variable_read_target_u32(backend, bootstate, target,
-+ "remaining_attempts",
-+ &target->remaining_attempts);
-+ if (ret)
-+ return ret;
-+
-+ ret = bootstate_backend_variable_read_target_u32(backend, bootstate, target,
-+ "priority", &target->priority);
-+ if (ret)
-+ return ret;
-+
-+ ret = bootstate_backend_variable_read_target_u32(backend, bootstate, target,
-+ "ok", &tmp);
-+ if (ret)
-+ return ret;
-+
-+ target->ok = !!tmp;
-+
-+ return ret;
-+}
-+
-+static int bootstate_backend_load(struct bootstate_backend *backend,
-+ struct bootstate *bootstate)
-+{
-+ struct bootstate_target *target;
-+ int ret;
-+
-+ list_for_each_entry(target, &bootstate->targets_unsorted, list_unsorted) {
-+ ret = bootstate_backend_load_one(backend, bootstate, target);
-+ if (ret)
-+ return ret;
-+ bootstate_target_add(bootstate, target);
-+ }
-+
-+ return 0;
-+}
-+
-+static int bootstate_backend_save_one(const struct bootstate_backend *backend,
-+ const struct bootstate *bootstate,
-+ struct bootstate_target *target)
-+{
-+ int ret;
-+
-+ ret = bootstate_backend_variable_write_target_u32(backend, bootstate, target,
-+ "remaining_attempts",
-+ target->remaining_attempts);
-+ if (ret)
-+ return ret;
-+
-+ ret = bootstate_backend_variable_write_target_u32(backend, bootstate, target,
-+ "priority", target->priority);
-+ if (ret)
-+ return ret;
-+
-+ ret = bootstate_backend_variable_write_target_u32(backend, bootstate, target,
-+ "ok", target->ok);
-+ if (ret)
-+ return ret;
-+
-+ return 0;
-+}
-+
-+static int bootstate_backend_save(const struct bootstate_backend *backend,
-+ const struct bootstate *bootstate)
-+{
-+ struct bootstate_target *target;
-+ int ret;
-+
-+ list_for_each_entry(target, &bootstate->targets, list) {
-+ ret = bootstate_backend_save_one(backend, bootstate, target);
-+ if (ret)
-+ return ret;
-+ }
-+
-+ return 0;
-+}
-+
-+static int bootstate_backend_nv_init_one(const struct bootstate_backend *backend,
-+ const struct bootstate *bootstate,
-+ struct bootstate_target *target)
-+{
-+ int ret;
-+
-+ ret = bootstate_variable_nv_init_u32(backend, bootstate, target,
-+ "remaining_attempts");
-+ if (ret)
-+ return ret;
-+
-+ ret = bootstate_variable_nv_init_u32(backend, bootstate, target,
-+ "priority");
-+ if (ret)
-+ return ret;
-+
-+ ret = bootstate_variable_nv_init_u32(backend, bootstate, target,
-+ "ok");
-+ if (ret)
-+ return ret;
-+
-+ return 0;
-+}
-+
-+static int bootstate_backend_nv_init(struct bootstate_backend *backend,
-+ struct bootstate *bootstate)
-+{
-+ struct bootstate_target *target;
-+ int ret;
-+
-+ list_for_each_entry(target, &bootstate->targets_unsorted, list_unsorted) {
-+ ret = bootstate_backend_nv_init_one(backend, bootstate, target);
-+ if (ret)
-+ return ret;
-+ }
-+
-+ return 0;
-+}
-+
-+static int bootstate_backend_nv_save(struct bootstate_backend *backend,
-+ struct bootstate *bootstate)
-+{
-+ int ret;
-+
-+ ret = bootstate_backend_save(backend, bootstate);
-+ if (ret)
-+ return ret;
-+
-+ return envfs_save(NULL, NULL, 0);
-+}
-+
-+static int bootstate_backend_nv_load(struct bootstate_backend *backend,
-+ struct bootstate *bootstate)
-+{
-+ return bootstate_backend_load(backend, bootstate);
-+}
-+
-+struct bootstate_backend_nv {
-+ struct bootstate_backend backend;
-+};
-+
-+int bootstate_backend_nv(struct bootstate *bootstate)
-+{
-+ struct bootstate_backend_nv *backend_nv;
-+ struct bootstate_backend *backend;
-+
-+ if (bootstate->backend)
-+ return -EBUSY;
-+
-+ backend_nv = xzalloc(sizeof(*backend_nv));
-+ backend = &backend_nv->backend;
-+
-+ backend->load = bootstate_backend_nv_load;
-+ backend->save = bootstate_backend_nv_save;
-+ backend->name = "nv";
-+ backend->path = "nv";
-+
-+ bootstate->backend = backend;
-+
-+ return bootstate_backend_nv_init(backend, bootstate);
-+}
-+
-+struct bootstate_backend_state {
-+ struct bootstate_backend backend;
-+ struct state *state;
-+};
-+
-+static int bootstate_backend_state_save(struct bootstate_backend *backend,
-+ struct bootstate *bootstate)
-+{
-+ struct bootstate_backend_state *backend_state =
-+ container_of(backend, struct bootstate_backend_state, backend);
-+ int ret;
-+
-+ ret = bootstate_backend_save(backend, bootstate);
-+ if (ret)
-+ return ret;
-+
-+ return state_save(backend_state->state);
-+}
-+
-+static int bootstate_backend_state_load(struct bootstate_backend *backend,
-+ struct bootstate *bootstate)
-+{
-+ return bootstate_backend_load(backend, bootstate);
-+}
-+
-+int bootstate_backend_state(struct bootstate *bootstate, const struct device_node *node)
-+{
-+ struct bootstate_backend_state *backend_state;
-+ struct bootstate_backend *backend;
-+ const struct device_node *state_node;
-+ struct state *state;
-+
-+ if (bootstate->backend)
-+ return -EBUSY;
-+
-+ state_node = of_parse_phandle(node, "backend", 0);
-+ if (!state_node)
-+ return -EINVAL;
-+
-+ state = state_by_node(state_node);
-+ if (!state)
-+ return -EPROBE_DEFER;
-+
-+ backend_state = xzalloc(sizeof(*backend_state));
-+ backend_state->state = state;
-+
-+ backend = &backend_state->backend;
-+ backend->load = bootstate_backend_state_load;
-+ backend->save = bootstate_backend_state_save;
-+ backend->name = "state";
-+
-+ bootstate->backend = backend;
-+
-+ return state_get_name(backend_state->state, &backend->path);
-+}
-+
-+/*
-+ * bootstate_new_from_node - create a new bootstate instance from a device_node
-+ *
-+ * @name The name of the new bootstate instance
-+ * @node The device_node describing the new bootstate instance
-+ */
-+struct bootstate *bootstate_new_from_node(const char *name, const struct device_node *node)
-+{
-+ struct bootstate *bootstate;
-+ int ret;
-+
-+ pr_debug("%s: node=%s, name=%s\n", __func__, node->full_name, name);
-+
-+ bootstate = bootstate_new(name);
-+ if (!bootstate)
-+ return ERR_PTR(-EINVAL);
-+
-+ ret = bootstate_from_node(bootstate, node, true);
-+ if (ret) {
-+ bootstate_release(bootstate);
-+ return ERR_PTR(ret);
-+ }
-+
-+ return bootstate;
-+}
-+
-+/*
-+ * bootstate_by_name - find a bootstate instance by name
-+ *
-+ * @name The name of the state instance
-+ */
-+struct bootstate *bootstate_by_name(const char *name)
-+{
-+ struct bootstate *bs;
-+
-+ list_for_each_entry(bs, &bootstate_list, list) {
-+ if (!strcmp(name, bs->name))
-+ return bs;
-+ }
-+
-+ return NULL;
-+}
-+
-+/*
-+ * bootstate_load - load a bootstate from the backing store
-+ *
-+ * @bootstate The state instance to load
-+ */
-+static int bootstate_load(struct bootstate *bootstate)
-+{
-+ int ret;
-+
-+ if (!bootstate->backend)
-+ return -ENOSYS;
-+
-+ ret = bootstate->backend->load(bootstate->backend, bootstate);
-+ if (ret)
-+ bootstate->dirty = 1;
-+ else
-+ bootstate->dirty = 0;
-+
-+ return ret;
-+}
-+
-+/*
-+ * bootstate_save - save a bootstate to the backing store
-+ *
-+ * @bootstate The bootstate instance to save
-+ */
-+static int bootstate_save(struct bootstate *bootstate)
-+{
-+ int ret;
-+
-+ if (!bootstate->dirty)
-+ return 0;
-+
-+ if (!bootstate->backend)
-+ return -ENOSYS;
-+
-+ ret = bootstate->backend->save(bootstate->backend, bootstate);
-+ if (ret)
-+ return ret;
-+
-+ bootstate->dirty = 0;
-+
-+ return 0;
-+}
-+
-+void bootstate_info(void)
-+{
-+ struct bootstate *bootstate;
-+
-+ printf("registered bootstate instances:\n");
-+
-+ list_for_each_entry(bootstate, &bootstate_list, list) {
-+ printf("%-20s ", bootstate->name);
-+ printf("(backend: %s, path: %s)\n",
-+ bootstate->backend->name, bootstate->backend->path);
-+ }
-+}
-+
-+#define __BF(arg) [__BOOTCHOOSER_FLAG_##arg##_SHIFT] = __stringify(arg)
-+
-+static const char * const bootstate_flags_str[] = {
-+ __BF(ATTEMPTS_KEEP),
-+ __BF(ATTEMPTS_DEC),
-+ __BF(ATTEMPTS_RESET),
-+ __BF(DEACTIVATE_ON_ZERO_ATTEMPTS),
-+ __BF(VERBOSE),
-+ __BF(DRYRUN),
-+ __BF(RETRY_WITH_DEC),
-+ __BF(WATCHDOG_ENABLE),
-+ __BF(WATCHDOG_TIMEOUT_FROM_STATE),
-+};
-+
-+#undef __BF
-+
-+#define pr(verbose, format, args...) \
-+ ({ \
-+ (verbose) ? pr_info((format), ##args) : 0; \
-+ })
-+
-+void _pr_flags(struct bootstate *bootstate, unsigned flags)
-+{
-+ int i;
-+
-+ pr_info("%s: flags=0x%08x\n", bootstate->name, flags);
-+
-+ for (i = 0; i < ARRAY_SIZE(bootstate_flags_str); i++) {
-+ if (flags & (1 << i))
-+ pr_info("%s: -> %s\n", bootstate->name,
-+ bootstate_flags_str[i]);
-+ }
-+}
-+
-+#define pr_flags(verbose, bootstate, flags) \
-+ ({ \
-+ (verbose) ? _pr_flags(bootstate, flags) : 0; \
-+ })
-+
-+/*
-+ * bootstate_get_target - create a new state instance from a device_node
-+ *
-+ * @bootstate the bootstate instance to work in
-+ * @flags supported flags:
-+ * BOOTCHOOSER_FLAG_VERBOSE
-+ * BOOTCHOOSER_FLAG_ATTEMPTS_KEEP
-+ * BOOTCHOOSER_FLAG_ATTEMPTS_DEC
-+ * BOOTCHOOSER_FLAG_ATTEMPTS_RESET
-+ * BOOTCHOOSER_FLAG_DEACTIVATE_ON_ZERO_ATTEMPTS
-+ * @target_out a string to the choosen boot target is returned via
-+ * this paramater
-+ */
-+int bootstate_get_target(struct bootstate *bootstate, unsigned flags, char **target_out)
-+{
-+ struct bootstate_target *target;
-+ int ret;
-+ bool found = false;
-+ bool v = flags & BOOTCHOOSER_FLAG_VERBOSE;
-+
-+ pr_flags(v, bootstate, flags);
-+
-+ ret = bootstate_load(bootstate);
-+ if (ret)
-+ return ret;
-+
-+ if (flags & BOOTCHOOSER_FLAG_ATTEMPTS_RESET) {
-+ list_for_each_entry(target, &bootstate->targets, list) {
-+ if (target->priority == 0)
-+ continue;
-+
-+ target->remaining_attempts = target->default_attempts;
-+ bootstate->dirty = true;
-+
-+ pr(v, "%s: target: name=%s setting rem to %d due to %s\n",
-+ bootstate->name, target->name, target->default_attempts,
-+ bootstate_flags_str[__BOOTCHOOSER_FLAG_ATTEMPTS_RESET_SHIFT]);
-+ }
-+ pr(v, "%s: --------\n", bootstate->name);
-+ }
-+
-+ list_for_each_entry(target, &bootstate->targets, list) {
-+ pr_target(bootstate, target);
-+
-+ if (found)
-+ continue;
-+
-+ if (target->priority == 0) {
-+ pr(v, "%s: name=%s prio=%d - trying next\n",
-+ bootstate->name, target->name, target->priority);
-+ continue;
-+ }
-+
-+ if (target->remaining_attempts == 0) {
-+ pr(v, "%s: name=%s remaining attempts == 0 - trying next\n",
-+ bootstate->name, target->name);
-+ continue;
-+ }
-+
-+ if (flags & BOOTCHOOSER_FLAG_ATTEMPTS_DEC) {
-+ bootstate->dirty = true;
-+ target->remaining_attempts--;
-+
-+ pr(v, "%s: name=%s decrementing remaining_attempts to %d due to %s\n",
-+ bootstate->name, target->name,
-+ target->remaining_attempts,
-+ bootstate_flags_str[__BOOTCHOOSER_FLAG_ATTEMPTS_DEC_SHIFT]);
-+
-+ if ((target->remaining_attempts == 0) &&
-+ (flags & BOOTCHOOSER_FLAG_DEACTIVATE_ON_ZERO_ATTEMPTS)) {
-+ target->priority = 0;
-+
-+ pr(v, "%s: name=%s deactivating target (setting priority = 0) due to %s\n",
-+ bootstate->name, target->name,
-+ bootstate_flags_str[__BOOTCHOOSER_FLAG_DEACTIVATE_ON_ZERO_ATTEMPTS_SHIFT]);
-+ }
-+ }
-+
-+ found = true;
-+ *target_out = strdup(target->name);
-+ pr_debug("%s: selected target '%s'\n", __func__, target->name);
-+ if (!v)
-+ goto out;
-+
-+ pr(v, "%s: --- other bootsources ---\n", bootstate->name);
-+ }
-+
-+ out:
-+ bootstate_save(bootstate);
-+
-+ if (!found)
-+ return -ENOENT;
-+
-+ return 0;
-+}
-+
-+int bootstate_bootchooser(char *name, unsigned flags, unsigned timeout)
-+{
-+ struct bootstate *bootstate;
-+ bool v = flags & BOOTCHOOSER_FLAG_VERBOSE;
-+ char *target;
-+ int ret;
-+
-+ if (!name)
-+ name = "bootstate";
-+
-+ bootstate = bootstate_by_name(name);
-+ if (!bootstate) {
-+ pr_err("Bootstate '%s' not found.\n", name);
-+ return -ENODEV;
-+ }
-+
-+ if (flags & BOOTCHOOSER_FLAG_WATCHDOG_ENABLE) {
-+ if (flags & BOOTCHOOSER_FLAG_WATCHDOG_TIMEOUT_FROM_STATE) {
-+ ret = bootstate_variable_read_u32(bootstate, "watchdog_timeout",
-+ &timeout);
-+ if (ret)
-+ return ret;
-+ }
-+
-+ if (timeout != 0) {
-+ pr(v, "%s: starting watchdog with timeout=%ds\n",
-+ __func__, timeout);
-+
-+ ret = watchdog_set_timeout(timeout);
-+ if (ret)
-+ return ret;
-+ }
-+ }
-+
-+ while (true) {
-+ char *cmd;
-+
-+ ret = bootstate_get_target(bootstate, flags, &target);
-+ if (ret)
-+ return ret;
-+
-+ cmd = asprintf("boot %s", target);
-+ free(target);
-+ pr_info("%srunning: %s...\n",
-+ flags & BOOTCHOOSER_FLAG_DRYRUN ? "not " : "", cmd);
-+ if (!(flags & BOOTCHOOSER_FLAG_DRYRUN))
-+ ret = run_command(cmd);
-+ free(cmd);
-+
-+ if (flags & BOOTCHOOSER_FLAG_RETRY_WITH_DEC) {
-+ flags |= BOOTCHOOSER_FLAG_ATTEMPTS_DEC;
-+ flags &= ~(BOOTCHOOSER_FLAG_ATTEMPTS_RESET |
-+ BOOTCHOOSER_FLAG_ATTEMPTS_KEEP);
-+ continue;
-+ }
-+
-+ return ret;
-+ }
-+
-+ return -ENOENT;
-+}
-diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
-index 7a5b14697efd..2459051e1db1 100644
---- a/drivers/misc/Kconfig
-+++ b/drivers/misc/Kconfig
-@@ -19,4 +19,9 @@ config STATE_DRV
- tristate "state driver"
- depends on STATE
-
-+config BOOTSTATE_DRV
-+ tristate "bootstate driver"
-+ depends on BOOTSTATE
-+ depends on STATE
-+
- endmenu
-diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
-index 487e4b8ba2e5..603e14ebb5de 100644
---- a/drivers/misc/Makefile
-+++ b/drivers/misc/Makefile
-@@ -5,3 +5,4 @@
- obj-$(CONFIG_JTAG) += jtag.o
- obj-$(CONFIG_SRAM) += sram.o
- obj-$(CONFIG_STATE_DRV) += state.o
-+obj-$(CONFIG_BOOTSTATE_DRV) += bootstate.o
-diff --git a/drivers/misc/bootstate.c b/drivers/misc/bootstate.c
-new file mode 100644
-index 000000000000..3ec9b8fda86b
---- /dev/null
-+++ b/drivers/misc/bootstate.c
-@@ -0,0 +1,79 @@
-+/*
-+ * Copyright (C) 2013 Sascha Hauer <s.hauer@pengutronix.de>
-+ * Copyright (C) 2015 Marc Kleine-Budde <mkl@pengutronix.de>
-+ *
-+ * This program is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-+ * GNU General Public License for more details.
-+ */
-+
-+#include <bootstate.h>
-+#include <driver.h>
-+#include <init.h>
-+#include <malloc.h>
-+#include <of.h>
-+#include <printk.h>
-+#include <string.h>
-+
-+#include <linux/err.h>
-+
-+static int bootstate_probe(struct device_d *dev)
-+{
-+ struct device_node *np = dev->device_node;
-+ struct bootstate *bootstate;
-+ const char *alias;
-+ const char *backend_type = NULL;
-+ int ret;
-+
-+ if (!np)
-+ return -EINVAL;
-+
-+ alias = of_alias_get(np);
-+ if (!alias)
-+ alias = "bootstate";
-+
-+ bootstate = bootstate_new_from_node(alias, np);
-+ if (IS_ERR(bootstate))
-+ return PTR_ERR(bootstate);
-+
-+ of_property_read_string(np, "backend-type", &backend_type);
-+ if (!strcmp(backend_type, "state")) {
-+ ret = bootstate_backend_state(bootstate, np);
-+ } else if (!strcmp(backend_type, "nv")) {
-+ ret = bootstate_backend_nv(bootstate);
-+ } else {
-+ dev_warn(dev, "invalid backend type: %s\n", backend_type);
-+ ret = -ENODEV;
-+ goto out_release;
-+ }
-+
-+ if (ret)
-+ goto out_release;
-+
-+ return 0;
-+
-+ out_release:
-+ bootstate_release(bootstate);
-+ return ret;
-+}
-+
-+static __maybe_unused struct of_device_id bootstate_ids[] = {
-+ {
-+ .compatible = "barebox,bootstate",
-+ }, {
-+ /* sentinel */
-+ }
-+};
-+
-+static struct driver_d bootstate_driver = {
-+ .name = "bootstate",
-+ .probe = bootstate_probe,
-+ .of_compatible = DRV_OF_COMPAT(bootstate_ids),
-+};
-+device_platform_driver(bootstate_driver);
-diff --git a/include/bootstate.h b/include/bootstate.h
-new file mode 100644
-index 000000000000..22631c902f60
---- /dev/null
-+++ b/include/bootstate.h
-@@ -0,0 +1,39 @@
-+#ifndef __BOOTSTATE_H
-+#define __BOOTSTATE_H
-+
-+#include <of.h>
-+
-+struct bootstate *bootstate_new_from_node(const char *name, const struct device_node *node);
-+struct bootstate *bootstate_find_by_name(const char *name);
-+struct bootstate *bootstate_by_name(const char *name);
-+void bootstate_release(struct bootstate *bootstate);
-+void bootstate_info(void);
-+int bootstate_backend_nv(struct bootstate *bootstate);
-+int bootstate_backend_state(struct bootstate *bootstate, const struct device_node *node);
-+
-+enum {
-+ __BOOTCHOOSER_FLAG_ATTEMPTS_KEEP_SHIFT,
-+ __BOOTCHOOSER_FLAG_ATTEMPTS_DEC_SHIFT,
-+ __BOOTCHOOSER_FLAG_ATTEMPTS_RESET_SHIFT,
-+ __BOOTCHOOSER_FLAG_DEACTIVATE_ON_ZERO_ATTEMPTS_SHIFT,
-+ __BOOTCHOOSER_FLAG_VERBOSE_SHIFT,
-+ __BOOTCHOOSER_FLAG_DRYRUN_SHIFT,
-+ __BOOTCHOOSER_FLAG_RETRY_WITH_DEC_SHIFT,
-+ __BOOTCHOOSER_FLAG_WATCHDOG_ENABLE_SHIFT,
-+ __BOOTCHOOSER_FLAG_WATCHDOG_TIMEOUT_FROM_STATE_SHIFT,
-+};
-+
-+#define BOOTCHOOSER_FLAG_ATTEMPTS_KEEP (1 << __BOOTCHOOSER_FLAG_ATTEMPTS_KEEP_SHIFT)
-+#define BOOTCHOOSER_FLAG_ATTEMPTS_DEC (1 << __BOOTCHOOSER_FLAG_ATTEMPTS_DEC_SHIFT)
-+#define BOOTCHOOSER_FLAG_ATTEMPTS_RESET (1 << __BOOTCHOOSER_FLAG_ATTEMPTS_RESET_SHIFT)
-+#define BOOTCHOOSER_FLAG_DEACTIVATE_ON_ZERO_ATTEMPTS (1 << __BOOTCHOOSER_FLAG_DEACTIVATE_ON_ZERO_ATTEMPTS_SHIFT)
-+#define BOOTCHOOSER_FLAG_VERBOSE (1 << __BOOTCHOOSER_FLAG_VERBOSE_SHIFT)
-+#define BOOTCHOOSER_FLAG_DRYRUN (1 << __BOOTCHOOSER_FLAG_DRYRUN_SHIFT)
-+#define BOOTCHOOSER_FLAG_RETRY_WITH_DEC (1 << __BOOTCHOOSER_FLAG_RETRY_WITH_DEC_SHIFT)
-+#define BOOTCHOOSER_FLAG_WATCHDOG_ENABLE (1 << __BOOTCHOOSER_FLAG_WATCHDOG_ENABLE_SHIFT)
-+#define BOOTCHOOSER_FLAG_WATCHDOG_TIMEOUT_FROM_STATE (1 << __BOOTCHOOSER_FLAG_WATCHDOG_TIMEOUT_FROM_STATE_SHIFT)
-+
-+int bootstate_get_target(struct bootstate *bootstate, unsigned flags, char **target_out);
-+int bootstate_bootchooser(char *name, unsigned flags, unsigned watchdog_timeout_s);
-+
-+#endif /* __BOOTSTATE_H */