From: Nandor Han Date: Thu, 10 Jun 2021 13:56:43 +0000 (+0300) Subject: reboot-mode: add support for reboot mode control X-Git-Url: http://git.dujemihanovic.xyz/html/static/%7B%7B%20.Permalink%20%7D%7D?a=commitdiff_plain;h=2541ce2c1af87f74a9feb35a1cbfc20ff8d04e4b;p=u-boot.git 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 --- 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), +}; diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index d800f679d5..9d474533ba 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -92,6 +92,7 @@ enum uclass_id { UCLASS_PWRSEQ, /* Power sequence device */ UCLASS_QFW, /* QEMU firmware config device */ UCLASS_RAM, /* RAM controller */ + UCLASS_REBOOT_MODE, /* Reboot mode */ UCLASS_REGULATOR, /* Regulator device */ UCLASS_REMOTEPROC, /* Remote Processor device */ UCLASS_RESET, /* Reset controller device */ diff --git a/include/reboot-mode/reboot-mode.h b/include/reboot-mode/reboot-mode.h new file mode 100644 index 0000000000..86b51f881c --- /dev/null +++ b/include/reboot-mode/reboot-mode.h @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c), Vaisala Oyj + */ + +#ifndef REBOOT_MODE_REBOOT_MODE_H__ +#define REBOOT_MODE_REBOOT_MODE_H__ + +#include +#include + +struct reboot_mode_mode { + const char *mode_name; + u32 mode_id; +}; + +struct reboot_mode_uclass_platdata { + struct reboot_mode_mode *modes; + u8 count; + const char *env_variable; +}; + +struct reboot_mode_ops { + /** + * get() - get the current reboot mode value + * + * Returns the current value from the reboot mode backing store. + * + * @dev: Device to read from + * @rebootmode: Address to save the current reboot mode value + */ + int (*get)(struct udevice *dev, u32 *rebootmode); + + /** + * set() - set a reboot mode value + * + * Sets the value in the reboot mode backing store. + * + * @dev: Device to read from + * @rebootmode: New reboot mode value to store + */ + int (*set)(struct udevice *dev, u32 rebootmode); +}; + +/* Access the operations for a reboot mode device */ +#define reboot_mode_get_ops(dev) ((struct reboot_mode_ops *)(dev)->driver->ops) + +/** + * dm_reboot_mode_update() - Update the reboot mode env variable. + * + * @dev: Device to read from + * @return 0 if OK, -ve on error + */ +int dm_reboot_mode_update(struct udevice *dev); + +#endif /* REBOOT_MODE_REBOOT_MODE_H__ */