]> git.dujemihanovic.xyz Git - u-boot.git/commitdiff
reboot-mode: read the boot mode from GPIOs status
authorNandor Han <nandor.han@vaisala.com>
Thu, 10 Jun 2021 13:56:44 +0000 (16:56 +0300)
committerTom Rini <trini@konsulko.com>
Fri, 23 Jul 2021 14:16:39 +0000 (10:16 -0400)
A use case for controlling the boot mode is when the user wants
to control the device boot by pushing a button without needing to
go in user-space.

Add a new backed for reboot mode where GPIOs are used to control the
reboot-mode. The driver is able to scan a predefined list of GPIOs
and return the magic value. Having the modes associated with
the magic value generated based on the GPIO values, allows the
reboot mode uclass to select the proper mode.

Signed-off-by: Nandor Han <nandor.han@vaisala.com>
Reviewed-by: Simon Glass <sjg@chromium.org>
arch/sandbox/dts/test.dts
configs/sandbox_defconfig
doc/device-tree-bindings/reboot-mode/reboot-mode-gpio.txt [new file with mode: 0644]
drivers/reboot-mode/Kconfig
drivers/reboot-mode/Makefile
drivers/reboot-mode/reboot-mode-gpio.c [new file with mode: 0644]
include/reboot-mode/reboot-mode-gpio.h [new file with mode: 0644]
test/dm/Makefile
test/dm/reboot-mode.c [new file with mode: 0644]

index 0cee15a0ea2cc1e008664f36c072e98a16c1fd41..2347f4cbe4f1e3cdd4ad125934e9a27f3a3afeb9 100644 (file)
                };
        };
 
+       reboot-mode0 {
+               compatible = "reboot-mode-gpio";
+               gpios = <&gpio_c 0 GPIO_ACTIVE_HIGH>, <&gpio_c 1 GPIO_ACTIVE_HIGH>;
+               u-boot,env-variable = "bootstatus";
+               mode-test = <0x01>;
+               mode-download = <0x03>;
+       };
+
        audio: audio-codec {
                compatible = "sandbox,audio-codec";
                #sound-dai-cells = <1>;
index 1655bb1e8a2db385213640ec8ee8be682c0e7cee..6931b176ed9f2be270f1d220740e5340923e6453 100644 (file)
@@ -290,3 +290,5 @@ CONFIG_TEST_FDTDEC=y
 CONFIG_UNIT_TEST=y
 CONFIG_UT_TIME=y
 CONFIG_UT_DM=y
+CONFIG_DM_REBOOT_MODE=y
+CONFIG_DM_REBOOT_MODE_GPIO=y
diff --git a/doc/device-tree-bindings/reboot-mode/reboot-mode-gpio.txt b/doc/device-tree-bindings/reboot-mode/reboot-mode-gpio.txt
new file mode 100644 (file)
index 0000000..bb209d2
--- /dev/null
@@ -0,0 +1,20 @@
+GPIO Reboot Mode Configuration
+
+Required Properties:
+- compatible: must be "reboot-mode-gpio".
+- gpios: list of gpios that are used to calculate the reboot-mode magic value.
+        Every gpio represents a bit in the magic value in the same order
+        as defined in device tree.
+- modes: list of properties that define the modes and associated unique ids.
+
+Optional Properties:
+- u-boot,env-variable: used to save the reboot mode (default: reboot-mode).
+
+Example:
+       reboot-mode {
+               compatible = "reboot-mode-gpio";
+               gpios = <&gpio1 2 GPIO_ACTIVE_LOW>, <&gpio2 6 GPIO_ACTIVE_HIGH>;
+               u-boot,env-variable = "bootstatus";
+               mode-test = <0x00000001>;
+               mode-download = <0x00000002>;
+       };
index 0edc3209d71b3ead429c119e8303dff4a2624e5e..ff65e2031ae6b5d9213e61d50958c7416e16dc13 100644 (file)
@@ -15,4 +15,13 @@ config DM_REBOOT_MODE
                adjust the boot process based on reboot mode parameter
                passed to U-Boot.
 
+config DM_REBOOT_MODE_GPIO
+       bool "Use GPIOs as reboot mode backend"
+       depends on DM_REBOOT_MODE
+       default n
+       help
+               Use GPIOs to control the reboot mode. This will allow users to boot
+               a device in a specific mode by using a GPIO that can be controlled
+               outside U-Boot.
+
 endmenu
index 2ab0fddac9ea96dc2365b21177db43989e437fbb..04917be4f43f186f32996dd76855f02fec71fd9b 100644 (file)
@@ -5,3 +5,4 @@
 #
 
 obj-$(CONFIG_DM_REBOOT_MODE) += reboot-mode-uclass.o
+obj-$(CONFIG_DM_REBOOT_MODE_GPIO) += reboot-mode-gpio.o
diff --git a/drivers/reboot-mode/reboot-mode-gpio.c b/drivers/reboot-mode/reboot-mode-gpio.c
new file mode 100644 (file)
index 0000000..3051747
--- /dev/null
@@ -0,0 +1,128 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c), Vaisala Oyj
+ */
+
+#include <common.h>
+#include <asm/gpio.h>
+#include <dm.h>
+#include <dm/devres.h>
+#include <errno.h>
+#include <reboot-mode/reboot-mode-gpio.h>
+#include <reboot-mode/reboot-mode.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static int reboot_mode_get(struct udevice *dev, u32 *buf)
+{
+       int ret;
+       struct reboot_mode_gpio_platdata *plat_data;
+
+       if (!buf)
+               return -EINVAL;
+
+       plat_data = dev_get_plat(dev);
+       if (!plat_data)
+               return -EINVAL;
+
+       ret = dm_gpio_get_values_as_int(plat_data->gpio_desc,
+                                       plat_data->gpio_count);
+       if (ret < 0)
+               return ret;
+
+       *buf = ret;
+
+       return 0;
+}
+
+static int reboot_mode_probe(struct udevice *dev)
+{
+       struct reboot_mode_gpio_platdata *plat_data;
+
+       plat_data = dev_get_plat(dev);
+       if (!plat_data)
+               return -EINVAL;
+
+       int ret;
+
+#if CONFIG_IS_ENABLED(OF_CONTROL)
+       ret = gpio_get_list_count(dev, "gpios");
+       if (ret < 0)
+               return ret;
+
+       plat_data->gpio_count = ret;
+#endif
+
+       if (plat_data->gpio_count <= 0)
+               return -EINVAL;
+
+       plat_data->gpio_desc = devm_kcalloc(dev, plat_data->gpio_count,
+                                           sizeof(struct gpio_desc), 0);
+       if (!plat_data->gpio_desc)
+               return -ENOMEM;
+
+#if CONFIG_IS_ENABLED(OF_CONTROL)
+       ret = gpio_request_list_by_name(dev, "gpios", plat_data->gpio_desc,
+                                       plat_data->gpio_count, GPIOD_IS_IN);
+       if (ret < 0)
+               return ret;
+#else
+       for (int i = 0; i < plat_data->gpio_count; i++) {
+               struct reboot_mode_gpio_config *gpio =
+                       plat_data->gpios_config + i;
+               struct gpio_desc *desc = plat_data->gpio_desc + i;
+
+               ret = uclass_get_device_by_seq(UCLASS_GPIO,
+                                              gpio->gpio_dev_offset,
+                                              &desc->dev);
+               if (ret < 0)
+                       return ret;
+
+               desc->flags = gpio->flags;
+               desc->offset = gpio->gpio_offset;
+
+               ret = dm_gpio_request(desc, "");
+               if (ret < 0)
+                       return ret;
+
+               ret = dm_gpio_set_dir(desc);
+               if (ret < 0)
+                       return ret;
+       }
+#endif
+       return 0;
+}
+
+static int reboot_mode_remove(struct udevice *dev)
+{
+       struct reboot_mode_gpio_platdata *plat_data;
+
+       plat_data = dev_get_plat(dev);
+       if (!plat_data)
+               return -EINVAL;
+
+       return gpio_free_list(dev, plat_data->gpio_desc, plat_data->gpio_count);
+}
+
+#if CONFIG_IS_ENABLED(OF_CONTROL)
+static const struct udevice_id reboot_mode_ids[] = {
+       { .compatible = "reboot-mode-gpio", 0 },
+       { }
+};
+#endif
+
+static const struct reboot_mode_ops reboot_mode_gpio_ops = {
+       .get = reboot_mode_get,
+};
+
+U_BOOT_DRIVER(reboot_mode_gpio) = {
+       .name = "reboot-mode-gpio",
+       .id = UCLASS_REBOOT_MODE,
+       .probe = reboot_mode_probe,
+       .remove = reboot_mode_remove,
+#if CONFIG_IS_ENABLED(OF_CONTROL)
+       .of_match = reboot_mode_ids,
+#endif
+       .plat_auto = sizeof(struct reboot_mode_gpio_platdata),
+       .ops = &reboot_mode_gpio_ops,
+};
diff --git a/include/reboot-mode/reboot-mode-gpio.h b/include/reboot-mode/reboot-mode-gpio.h
new file mode 100644 (file)
index 0000000..16b1185
--- /dev/null
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (c) Vaisala Oyj.
+ */
+
+#ifndef REBOOT_MODE_REBOOT_MODE_GPIO_H_
+#define REBOOT_MODE_REBOOT_MODE_GPIO_H_
+
+#include <asm/gpio.h>
+
+/*
+ * In case of initializing the driver statically (using U_BOOT_DEVICE macro),
+ * we can use this struct to declare the pins used.
+ */
+
+#if !CONFIG_IS_ENABLED(OF_CONTROL)
+struct reboot_mode_gpio_config {
+       int gpio_dev_offset;
+       int gpio_offset;
+       int flags;
+};
+#endif
+
+struct reboot_mode_gpio_platdata {
+       struct gpio_desc *gpio_desc;
+#if !CONFIG_IS_ENABLED(OF_CONTROL)
+       struct reboot_mode_gpio_config *gpios_config;
+#endif
+       int gpio_count;
+};
+
+#endif /* REBOOT_MODE_REBOOT_MODE_GPIO_H_ */
index 9ef9171a1cbcdad6f1089566b3f929994ba28983..d5c42e7643ecb90ae3d9e7d5e16b09fe81733ede 100644 (file)
@@ -27,6 +27,7 @@ obj-$(CONFIG_AXI) += axi.o
 obj-$(CONFIG_BLK) += blk.o
 obj-$(CONFIG_BUTTON) += button.o
 obj-$(CONFIG_DM_BOOTCOUNT) += bootcount.o
+obj-$(CONFIG_DM_REBOOT_MODE) += reboot-mode.o
 obj-$(CONFIG_CLK) += clk.o clk_ccf.o
 obj-$(CONFIG_CPU) += cpu.o
 obj-$(CONFIG_CROS_EC) += cros_ec.o
diff --git a/test/dm/reboot-mode.c b/test/dm/reboot-mode.c
new file mode 100644 (file)
index 0000000..66aa479
--- /dev/null
@@ -0,0 +1,42 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) 2018 Theobroma Systems Design und Consulting GmbH
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <reboot-mode/reboot-mode.h>
+#include <env.h>
+#include <log.h>
+#include <asm/gpio.h>
+#include <asm/test.h>
+#include <dm/test.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+static int dm_test_reboot_mode_gpio(struct unit_test_state *uts)
+{
+       struct udevice *gpio_dev;
+       struct udevice *rm_dev;
+       int gpio0_offset = 0;
+       int gpio1_offset = 1;
+
+       uclass_get_device_by_name(UCLASS_GPIO, "pinmux-gpios", &gpio_dev);
+
+       /* Prepare the GPIOs for "download" mode */
+       sandbox_gpio_set_direction(gpio_dev, gpio0_offset, 0);
+       sandbox_gpio_set_direction(gpio_dev, gpio1_offset, 0);
+       sandbox_gpio_set_value(gpio_dev, gpio0_offset, 1);
+       sandbox_gpio_set_value(gpio_dev, gpio1_offset, 1);
+
+       ut_assertok(uclass_get_device_by_name(UCLASS_REBOOT_MODE,
+                                             "reboot-mode0", &rm_dev));
+       ut_assertok(dm_reboot_mode_update(rm_dev));
+
+       ut_asserteq_str("download", env_get("bootstatus"));
+
+       return 0;
+}
+
+DM_TEST(dm_test_reboot_mode_gpio,
+       UT_TESTF_PROBE_TEST | UT_TESTF_SCAN_FDT | UT_TESTF_FLAT_TREE);