From patchwork Mon May 4 09:18:42 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nandor Han X-Patchwork-Id: 244963 List-Id: U-Boot discussion From: nandor.han at vaisala.com (Nandor Han) Date: Mon, 4 May 2020 12:18:42 +0300 Subject: [PATCH v2 1/3] reboot-mode: add support for reboot mode control In-Reply-To: References: Message-ID: 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 --- drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/reboot-mode/Kconfig | 18 ++++ drivers/reboot-mode/Makefile | 7 ++ drivers/reboot-mode/reboot-mode-uclass.c | 132 +++++++++++++++++++++++ include/dm/uclass-id.h | 1 + include/reboot-mode/reboot-mode.h | 56 ++++++++++ 7 files changed, 217 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 create mode 100644 include/reboot-mode/reboot-mode.h diff --git a/drivers/Kconfig b/drivers/Kconfig index e34a22708c..b88c27e95c 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -86,6 +86,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 4208750428..92bb7c67fb 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -91,6 +91,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 += rtc/ obj-y += scsi/ obj-y += sound/ 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..78b54836df --- /dev/null +++ b/drivers/reboot-mode/reboot-mode-uclass.c @@ -0,0 +1,132 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c), Vaisala Oyj + */ + +#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_platdata(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_platdata(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_platdata_auto_alloc_size = + sizeof(struct reboot_mode_uclass_platdata), +}; diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index 37ada51f9f..8350ccd176 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -87,6 +87,7 @@ enum uclass_id { UCLASS_PWM, /* Pulse-width modulator */ UCLASS_PWRSEQ, /* Power sequence 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__ */