diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index 0cee15a0ea..2347f4cbe4 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -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>; diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index 1655bb1e8a..6931b176ed 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -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 index 0000000000..bb209d2742 --- /dev/null +++ b/doc/device-tree-bindings/reboot-mode/reboot-mode-gpio.txt @@ -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>; + }; diff --git a/drivers/reboot-mode/Kconfig b/drivers/reboot-mode/Kconfig index 0edc3209d7..ff65e2031a 100644 --- a/drivers/reboot-mode/Kconfig +++ b/drivers/reboot-mode/Kconfig @@ -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 diff --git a/drivers/reboot-mode/Makefile b/drivers/reboot-mode/Makefile index 2ab0fddac9..04917be4f4 100644 --- a/drivers/reboot-mode/Makefile +++ b/drivers/reboot-mode/Makefile @@ -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 index 0000000000..305174736e --- /dev/null +++ b/drivers/reboot-mode/reboot-mode-gpio.c @@ -0,0 +1,128 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c), Vaisala Oyj + */ + +#include +#include +#include +#include +#include +#include +#include + +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 index 0000000000..16b1185c69 --- /dev/null +++ b/include/reboot-mode/reboot-mode-gpio.h @@ -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 + +/* + * 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_ */ diff --git a/test/dm/Makefile b/test/dm/Makefile index 9ef9171a1c..d5c42e7643 100644 --- a/test/dm/Makefile +++ b/test/dm/Makefile @@ -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 index 0000000000..66aa4793f7 --- /dev/null +++ b/test/dm/reboot-mode.c @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) 2018 Theobroma Systems Design und Consulting GmbH + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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);