};
};
+ 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>;
CONFIG_UNIT_TEST=y
CONFIG_UT_TIME=y
CONFIG_UT_DM=y
+CONFIG_DM_REBOOT_MODE=y
+CONFIG_DM_REBOOT_MODE_GPIO=y
--- /dev/null
+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>;
+ };
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
#
obj-$(CONFIG_DM_REBOOT_MODE) += reboot-mode-uclass.o
+obj-$(CONFIG_DM_REBOOT_MODE_GPIO) += reboot-mode-gpio.o
--- /dev/null
+// 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,
+};
--- /dev/null
+/* 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_ */
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
--- /dev/null
+// 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);