From 2541ce2c1af87f74a9feb35a1cbfc20ff8d04e4b Mon Sep 17 00:00:00 2001 From: Nandor Han Date: Thu, 10 Jun 2021 16:56:43 +0300 Subject: reboot-mode: add support for reboot mode control A new driver uclass is created to handle the reboot mode control. The new uclass driver is updating an environment variable with the configured reboot mode. The mode is extracted from a map provided at initialization time. The map contains a list of modes and associated ids. Signed-off-by: Nandor Han Reviewed-by: Simon Glass --- drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/reboot-mode/Kconfig | 18 +++++ drivers/reboot-mode/Makefile | 7 ++ drivers/reboot-mode/reboot-mode-uclass.c | 134 +++++++++++++++++++++++++++++++ 5 files changed, 162 insertions(+) create mode 100644 drivers/reboot-mode/Kconfig create mode 100644 drivers/reboot-mode/Makefile create mode 100644 drivers/reboot-mode/reboot-mode-uclass.c (limited to 'drivers') diff --git a/drivers/Kconfig b/drivers/Kconfig index c9c812b752..417d6f88c2 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -92,6 +92,8 @@ source "drivers/qe/Kconfig" source "drivers/ram/Kconfig" +source "drivers/reboot-mode/Kconfig" + source "drivers/remoteproc/Kconfig" source "drivers/reset/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index 4081289104..82d3c98e06 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -95,6 +95,7 @@ obj-y += dfu/ obj-$(CONFIG_PCH) += pch/ obj-y += phy/allwinner/ obj-y += phy/marvell/ +obj-$(CONFIG_DM_REBOOT_MODE) += reboot-mode/ obj-y += phy/rockchip/ obj-y += phy/socionext/ obj-y += rtc/ diff --git a/drivers/reboot-mode/Kconfig b/drivers/reboot-mode/Kconfig new file mode 100644 index 0000000000..0edc3209d7 --- /dev/null +++ b/drivers/reboot-mode/Kconfig @@ -0,0 +1,18 @@ +# +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright (c), Vaisala Oyj +# + +menu "Reboot Mode Support" + +config DM_REBOOT_MODE + bool "Enable reboot mode using Driver Model" + depends on DM + default n + help + Enable support for reboot mode control. This will allow users to + adjust the boot process based on reboot mode parameter + passed to U-Boot. + +endmenu diff --git a/drivers/reboot-mode/Makefile b/drivers/reboot-mode/Makefile new file mode 100644 index 0000000000..2ab0fddac9 --- /dev/null +++ b/drivers/reboot-mode/Makefile @@ -0,0 +1,7 @@ +# +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright (c), Vaisala Oyj +# + +obj-$(CONFIG_DM_REBOOT_MODE) += reboot-mode-uclass.o diff --git a/drivers/reboot-mode/reboot-mode-uclass.c b/drivers/reboot-mode/reboot-mode-uclass.c new file mode 100644 index 0000000000..bb7a355fbf --- /dev/null +++ b/drivers/reboot-mode/reboot-mode-uclass.c @@ -0,0 +1,134 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c), Vaisala Oyj + */ + +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +int dm_reboot_mode_update(struct udevice *dev) +{ + struct reboot_mode_ops *ops = reboot_mode_get_ops(dev); + u32 rebootmode; + int ret, i; + + assert(ops); + + if (!ops->get) + return -ENOSYS; + + ret = ops->get(dev, &rebootmode); + if (ret < 0) { + dev_err(dev, "Failed to retrieve the reboot mode value\n"); + return ret; + } + + const struct reboot_mode_uclass_platdata *plat_data = + dev_get_uclass_plat(dev); + + for (i = 0; i < plat_data->count; i++) { + if (plat_data->modes[i].mode_id == rebootmode) { + ret = env_set(plat_data->env_variable, + plat_data->modes[i].mode_name); + if (ret) { + dev_err(dev, "Failed to set env: %s\n", + plat_data->env_variable); + return ret; + } + } + } + + if (ops->set) { + /* Clear the value */ + rebootmode = 0; + ret = ops->set(dev, rebootmode); + if (ret) { + dev_err(dev, "Failed to clear the reboot mode\n"); + return ret; + } + } + + return 0; +} + +int dm_reboot_mode_pre_probe(struct udevice *dev) +{ + struct reboot_mode_uclass_platdata *plat_data; + + plat_data = dev_get_uclass_plat(dev); + if (!plat_data) + return -EINVAL; + +#if CONFIG_IS_ENABLED(OF_CONTROL) + const int node = dev_of_offset(dev); + const char *mode_prefix = "mode-"; + const int mode_prefix_len = strlen(mode_prefix); + int property; + const u32 *propvalue; + const char *propname; + + plat_data->env_variable = fdt_getprop(gd->fdt_blob, + node, + "u-boot,env-variable", + NULL); + if (!plat_data->env_variable) + plat_data->env_variable = "reboot-mode"; + + plat_data->count = 0; + + fdt_for_each_property_offset(property, gd->fdt_blob, node) { + propvalue = fdt_getprop_by_offset(gd->fdt_blob, + property, &propname, NULL); + if (!propvalue) { + dev_err(dev, "Could not get the value for property %s\n", + propname); + return -EINVAL; + } + + if (!strncmp(propname, mode_prefix, mode_prefix_len)) + plat_data->count++; + } + + plat_data->modes = devm_kcalloc(dev, plat_data->count, + sizeof(struct reboot_mode_mode), 0); + + struct reboot_mode_mode *next = plat_data->modes; + + fdt_for_each_property_offset(property, gd->fdt_blob, node) { + propvalue = fdt_getprop_by_offset(gd->fdt_blob, + property, &propname, NULL); + if (!propvalue) { + dev_err(dev, "Could not get the value for property %s\n", + propname); + return -EINVAL; + } + + if (!strncmp(propname, mode_prefix, mode_prefix_len)) { + next->mode_name = &propname[mode_prefix_len]; + next->mode_id = fdt32_to_cpu(*propvalue); + + next++; + } + } +#else + if (!plat_data->env_variable) + plat_data->env_variable = "reboot-mode"; + +#endif + + return 0; +} + +UCLASS_DRIVER(reboot_mode) = { + .name = "reboot-mode", + .id = UCLASS_REBOOT_MODE, + .pre_probe = dm_reboot_mode_pre_probe, + .per_device_plat_auto = + sizeof(struct reboot_mode_uclass_platdata), +}; -- cgit v1.2.3