reboot-mode: read the boot mode from GPIOs status

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>
This commit is contained in:
Nandor Han 2021-06-10 16:56:44 +03:00 committed by Tom Rini
parent 2541ce2c1a
commit f9db2f16cb
9 changed files with 243 additions and 0 deletions

View file

@ -59,6 +59,14 @@
};
};
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>;

View 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

View file

@ -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>;
};

View 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

View file

@ -5,3 +5,4 @@
#
obj-$(CONFIG_DM_REBOOT_MODE) += reboot-mode-uclass.o
obj-$(CONFIG_DM_REBOOT_MODE_GPIO) += reboot-mode-gpio.o

View file

@ -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,
};

View file

@ -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_ */

View 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

42
test/dm/reboot-mode.c Normal file
View file

@ -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);