From 430e136dc1398546ad2b1db08692e9edac480cc8 Mon Sep 17 00:00:00 2001 From: Sean Anderson Date: Tue, 20 Apr 2021 10:50:54 -0400 Subject: [PATCH 1/9] dm: gpio: Fix gpio_get_list_count failing with livetree of_parse_phandle_with_args (called by dev_read_phandle_with_args) does not support getting the length of a phandle list by using the index -1. Instead, use dev_count_phandle_with_args which supports exactly this use-case. Fixes: 8558217153 ("gpio: Convert to use APIs which support live DT") Signed-off-by: Sean Anderson Reviewed-by: Simon Glass --- drivers/gpio/gpio-uclass.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpio/gpio-uclass.c b/drivers/gpio/gpio-uclass.c index e4e7f58c39..131099cc17 100644 --- a/drivers/gpio/gpio-uclass.c +++ b/drivers/gpio/gpio-uclass.c @@ -1215,9 +1215,9 @@ int gpio_get_list_count(struct udevice *dev, const char *list_name) { int ret; - ret = dev_read_phandle_with_args(dev, list_name, "#gpio-cells", 0, -1, - NULL); - if (ret) { + ret = dev_count_phandle_with_args(dev, list_name, "#gpio-cells", + -ENOENT); + if (ret < 0) { debug("%s: Node '%s', property '%s', GPIO count failed: %d\n", __func__, dev->name, list_name, ret); } From eed0a7a3e6087d038fccea18676e285d9807b644 Mon Sep 17 00:00:00 2001 From: Sean Anderson Date: Tue, 20 Apr 2021 10:50:55 -0400 Subject: [PATCH 2/9] sysinfo: Use global sysinfo IDs for existing sysinfo drivers Since 07c9e683a4 ("smbios: Allow a few values to come from sysinfo") there are common global sysinfo IDs. This patch moved existing IDs above SYSINFO_ID_USER. Signed-off-by: Sean Anderson Reviewed-by: Simon Glass --- drivers/sysinfo/gazerbeam.h | 8 +++++--- drivers/sysinfo/sandbox.h | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/sysinfo/gazerbeam.h b/drivers/sysinfo/gazerbeam.h index 171729d203..6bf3c0098d 100644 --- a/drivers/sysinfo/gazerbeam.h +++ b/drivers/sysinfo/gazerbeam.h @@ -5,10 +5,12 @@ * */ +#include + enum { - BOARD_MULTICHANNEL, - BOARD_VARIANT, - BOARD_HWVERSION, + BOARD_HWVERSION = SYSINFO_ID_BOARD_MODEL, + BOARD_MULTICHANNEL = SYSINFO_ID_USER, + BOARD_VARIANT }; enum { diff --git a/drivers/sysinfo/sandbox.h b/drivers/sysinfo/sandbox.h index 2cff494f56..d9c5804c26 100644 --- a/drivers/sysinfo/sandbox.h +++ b/drivers/sysinfo/sandbox.h @@ -5,7 +5,7 @@ */ enum { - BOOL_CALLED_DETECT, + BOOL_CALLED_DETECT = SYSINFO_ID_USER, INT_TEST1, INT_TEST2, STR_VACATIONSPOT, From 4d65c6bcd71ab2a03a5b7fff0ecf22d068597b25 Mon Sep 17 00:00:00 2001 From: Sean Anderson Date: Tue, 20 Apr 2021 10:50:56 -0400 Subject: [PATCH 3/9] sysinfo: Require that sysinfo_detect be called before other methods This has the uclass enforce calling detect() before other methods. This allows drivers to cache information in detect() and perform (cheaper) retrieval in the other accessors. This also modifies the only instance where this sequencing was not followed. Signed-off-by: Sean Anderson Reviewed-by: Simon Glass --- common/spl/spl_fit.c | 4 ++++ drivers/sysinfo/sysinfo-uclass.c | 29 ++++++++++++++++++++++++++++- include/sysinfo.h | 26 +++++++++++++++++--------- test/dm/sysinfo.c | 25 +++++++++++++++---------- 4 files changed, 64 insertions(+), 20 deletions(-) diff --git a/common/spl/spl_fit.c b/common/spl/spl_fit.c index 4288f571fc..caddf51196 100644 --- a/common/spl/spl_fit.c +++ b/common/spl/spl_fit.c @@ -110,6 +110,10 @@ static int spl_fit_get_image_name(const struct spl_fit_info *ctx, * no string in the property for this index. Check if the * sysinfo-level code can supply one. */ + rc = sysinfo_detect(sysinfo); + if (rc) + return rc; + rc = sysinfo_get_fit_loadable(sysinfo, index - i - 1, type, &str); if (rc && rc != -ENOENT) diff --git a/drivers/sysinfo/sysinfo-uclass.c b/drivers/sysinfo/sysinfo-uclass.c index 6df58fe160..4a660dfd15 100644 --- a/drivers/sysinfo/sysinfo-uclass.c +++ b/drivers/sysinfo/sysinfo-uclass.c @@ -8,6 +8,10 @@ #include #include +struct sysinfo_priv { + bool detected; +}; + int sysinfo_get(struct udevice **devp) { return uclass_first_device_err(UCLASS_SYSINFO, devp); @@ -15,19 +19,29 @@ int sysinfo_get(struct udevice **devp) int sysinfo_detect(struct udevice *dev) { + int ret; + struct sysinfo_priv *priv = dev_get_uclass_priv(dev); struct sysinfo_ops *ops = sysinfo_get_ops(dev); if (!ops->detect) return -ENOSYS; - return ops->detect(dev); + ret = ops->detect(dev); + if (!ret) + priv->detected = true; + + return ret; } int sysinfo_get_fit_loadable(struct udevice *dev, int index, const char *type, const char **strp) { + struct sysinfo_priv *priv = dev_get_uclass_priv(dev); struct sysinfo_ops *ops = sysinfo_get_ops(dev); + if (!priv->detected) + return -EPERM; + if (!ops->get_fit_loadable) return -ENOSYS; @@ -36,8 +50,12 @@ int sysinfo_get_fit_loadable(struct udevice *dev, int index, const char *type, int sysinfo_get_bool(struct udevice *dev, int id, bool *val) { + struct sysinfo_priv *priv = dev_get_uclass_priv(dev); struct sysinfo_ops *ops = sysinfo_get_ops(dev); + if (!priv->detected) + return -EPERM; + if (!ops->get_bool) return -ENOSYS; @@ -46,8 +64,12 @@ int sysinfo_get_bool(struct udevice *dev, int id, bool *val) int sysinfo_get_int(struct udevice *dev, int id, int *val) { + struct sysinfo_priv *priv = dev_get_uclass_priv(dev); struct sysinfo_ops *ops = sysinfo_get_ops(dev); + if (!priv->detected) + return -EPERM; + if (!ops->get_int) return -ENOSYS; @@ -56,8 +78,12 @@ int sysinfo_get_int(struct udevice *dev, int id, int *val) int sysinfo_get_str(struct udevice *dev, int id, size_t size, char *val) { + struct sysinfo_priv *priv = dev_get_uclass_priv(dev); struct sysinfo_ops *ops = sysinfo_get_ops(dev); + if (!priv->detected) + return -EPERM; + if (!ops->get_str) return -ENOSYS; @@ -68,4 +94,5 @@ UCLASS_DRIVER(sysinfo) = { .id = UCLASS_SYSINFO, .name = "sysinfo", .post_bind = dm_scan_fdt_dev, + .per_device_auto = sizeof(bool), }; diff --git a/include/sysinfo.h b/include/sysinfo.h index 8054d4d4a1..b140d742e9 100644 --- a/include/sysinfo.h +++ b/include/sysinfo.h @@ -60,7 +60,8 @@ struct sysinfo_ops { * This operation might take a long time (e.g. read from EEPROM, * check the presence of a device on a bus etc.), hence this is not * done in the probe() method, but later during operation in this - * dedicated method. + * dedicated method. This method will be called before any other + * methods. * * Return: 0 if OK, -ve on error. */ @@ -104,7 +105,7 @@ struct sysinfo_ops { * get_fit_loadable - Get the name of an image to load from FIT * This function can be used to provide the image names based on runtime * detection. A classic use-case would when DTBOs are used to describe - * additionnal daughter cards. + * additional daughter cards. * * @dev: The sysinfo instance to gather the data. * @index: Index of the image. Starts at 0 and gets incremented @@ -127,6 +128,9 @@ struct sysinfo_ops { * * @dev: The device containing the information * + * This function must be called before any other accessor function for this + * device. + * * Return: 0 if OK, -ve on error. */ int sysinfo_detect(struct udevice *dev); @@ -138,7 +142,8 @@ int sysinfo_detect(struct udevice *dev); * @id: A unique identifier for the bool value to be read. * @val: Pointer to a buffer that receives the value read. * - * Return: 0 if OK, -ve on error. + * Return: 0 if OK, -EPERM if called before sysinfo_detect(), else -ve on + * error. */ int sysinfo_get_bool(struct udevice *dev, int id, bool *val); @@ -149,7 +154,8 @@ int sysinfo_get_bool(struct udevice *dev, int id, bool *val); * @id: A unique identifier for the int value to be read. * @val: Pointer to a buffer that receives the value read. * - * Return: 0 if OK, -ve on error. + * Return: 0 if OK, -EPERM if called before sysinfo_detect(), else -ve on + * error. */ int sysinfo_get_int(struct udevice *dev, int id, int *val); @@ -161,7 +167,8 @@ int sysinfo_get_int(struct udevice *dev, int id, int *val); * @size: The size of the buffer to receive the string data. * @val: Pointer to a buffer that receives the value read. * - * Return: 0 if OK, -ve on error. + * Return: 0 if OK, -EPERM if called before sysinfo_detect(), else -ve on + * error. */ int sysinfo_get_str(struct udevice *dev, int id, size_t size, char *val); @@ -173,7 +180,8 @@ int sysinfo_get_str(struct udevice *dev, int id, size_t size, char *val); * function that returns the unique device. This is especially useful for use * in sysinfo files. * - * Return: 0 if OK, -ve on error. + * Return: 0 if OK, -EPERM if called before sysinfo_detect(), else -ve on + * error. */ int sysinfo_get(struct udevice **devp); @@ -181,7 +189,7 @@ int sysinfo_get(struct udevice **devp); * sysinfo_get_fit_loadable - Get the name of an image to load from FIT * This function can be used to provide the image names based on runtime * detection. A classic use-case would when DTBOs are used to describe - * additionnal daughter cards. + * additional daughter cards. * * @dev: The sysinfo instance to gather the data. * @index: Index of the image. Starts at 0 and gets incremented @@ -190,8 +198,8 @@ int sysinfo_get(struct udevice **devp); * @strp: A pointer to string. Untouched if the function fails * * - * Return: 0 if OK, -ENOENT if no loadable is available else -ve on - * error. + * Return: 0 if OK, -EPERM if called before sysinfo_detect(), -ENOENT if no + * loadable is available else -ve on error. */ int sysinfo_get_fit_loadable(struct udevice *dev, int index, const char *type, const char **strp); diff --git a/test/dm/sysinfo.c b/test/dm/sysinfo.c index 4aaa9e85bc..96b3a8ebab 100644 --- a/test/dm/sysinfo.c +++ b/test/dm/sysinfo.c @@ -17,40 +17,45 @@ static int dm_test_sysinfo(struct unit_test_state *uts) { struct udevice *sysinfo; - bool called_detect; + bool called_detect = false; char str[64]; int i; ut_assertok(sysinfo_get(&sysinfo)); ut_assert(sysinfo); - sysinfo_get_bool(sysinfo, BOOL_CALLED_DETECT, &called_detect); + ut_asserteq(-EPERM, sysinfo_get_bool(sysinfo, BOOL_CALLED_DETECT, + &called_detect)); ut_assert(!called_detect); sysinfo_detect(sysinfo); - sysinfo_get_bool(sysinfo, BOOL_CALLED_DETECT, &called_detect); + ut_assertok(sysinfo_get_bool(sysinfo, BOOL_CALLED_DETECT, + &called_detect)); ut_assert(called_detect); - sysinfo_get_str(sysinfo, STR_VACATIONSPOT, sizeof(str), str); + ut_assertok(sysinfo_get_str(sysinfo, STR_VACATIONSPOT, sizeof(str), + str)); ut_assertok(strcmp(str, "R'lyeh")); - sysinfo_get_int(sysinfo, INT_TEST1, &i); + ut_assertok(sysinfo_get_int(sysinfo, INT_TEST1, &i)); ut_asserteq(0, i); - sysinfo_get_int(sysinfo, INT_TEST2, &i); + ut_assertok(sysinfo_get_int(sysinfo, INT_TEST2, &i)); ut_asserteq(100, i); - sysinfo_get_str(sysinfo, STR_VACATIONSPOT, sizeof(str), str); + ut_assertok(sysinfo_get_str(sysinfo, STR_VACATIONSPOT, sizeof(str), + str)); ut_assertok(strcmp(str, "Carcosa")); - sysinfo_get_int(sysinfo, INT_TEST1, &i); + ut_assertok(sysinfo_get_int(sysinfo, INT_TEST1, &i)); ut_asserteq(1, i); - sysinfo_get_int(sysinfo, INT_TEST2, &i); + ut_assertok(sysinfo_get_int(sysinfo, INT_TEST2, &i)); ut_asserteq(99, i); - sysinfo_get_str(sysinfo, STR_VACATIONSPOT, sizeof(str), str); + ut_assertok(sysinfo_get_str(sysinfo, STR_VACATIONSPOT, sizeof(str), + str)); ut_assertok(strcmp(str, "Yuggoth")); return 0; From 54aa07fdfc01ac5abff342699df269f6f869fbe0 Mon Sep 17 00:00:00 2001 From: Sean Anderson Date: Tue, 20 Apr 2021 10:50:57 -0400 Subject: [PATCH 4/9] sysinfo: Add gpio-sysinfo driver This uses the newly-added dm_gpio_get_values_as_int_base3 function to implement a sysinfo device. The revision map is stored in the device tree. Signed-off-by: Sean Anderson Reviewed-by: Simon Glass --- .../sysinfo/gpio-sysinfo.txt | 37 +++++ drivers/sysinfo/Kconfig | 8 + drivers/sysinfo/Makefile | 1 + drivers/sysinfo/gpio.c | 141 ++++++++++++++++++ 4 files changed, 187 insertions(+) create mode 100644 doc/device-tree-bindings/sysinfo/gpio-sysinfo.txt create mode 100644 drivers/sysinfo/gpio.c diff --git a/doc/device-tree-bindings/sysinfo/gpio-sysinfo.txt b/doc/device-tree-bindings/sysinfo/gpio-sysinfo.txt new file mode 100644 index 0000000000..b5739d94e9 --- /dev/null +++ b/doc/device-tree-bindings/sysinfo/gpio-sysinfo.txt @@ -0,0 +1,37 @@ +GPIO-based Sysinfo device + +This binding describes several GPIOs which specify a board revision. Each GPIO +forms a digit in a ternary revision number. This revision is then mapped to a +name using the revisions and names properties. + +Each GPIO may be floating, pulled-up, or pulled-down, mapping to digits 2, 1, +and 0, respectively. The first GPIO forms the least-significant digit of the +revision. For example, consider the property + + gpios = <&gpio 0>, <&gpio 1>, <&gpio 2>; + +If GPIO 0 is pulled-up, GPIO 1 is pulled-down, and GPIO 2 is floating, then the +revision would be + + 0t201 = 2*9 + 0*3 + 1*3 = 19 + +If instead GPIO 0 is floating, GPIO 1 is pulled-up, and GPIO 2 is pulled-down, +then the revision would be + + 0t012 = 0*9 + 1*3 + 2*1 = 5 + +Required properties: +- compatible: should be "gpio-sysinfo". +- gpios: should be a list of gpios forming the revision number, + least-significant-digit first +- revisions: a list of known revisions; any revisions not present will have the + name "unknown" +- names: the name of each revision in revisions + +Example: +sysinfo { + compatible = "gpio-sysinfo"; + gpios = <&gpio_a 15>, <&gpio_a 16>, <&gpio_a 17>; + revisions = <19>, <5>; + names = "rev_a", "foo"; +}; diff --git a/drivers/sysinfo/Kconfig b/drivers/sysinfo/Kconfig index 85c1e81e41..381dcd8844 100644 --- a/drivers/sysinfo/Kconfig +++ b/drivers/sysinfo/Kconfig @@ -30,4 +30,12 @@ config SYSINFO_SMBIOS one which provides a way to specify this SMBIOS information in the devicetree, without needing any board-specific functionality. +config SYSINFO_GPIO + bool "Enable gpio sysinfo driver" + help + Support querying gpios to determine board revision. This uses gpios to + form a ternary number (when they are pulled-up, -down, or floating). + This ternary number is then mapped to a board revision name using + device tree properties. + endif diff --git a/drivers/sysinfo/Makefile b/drivers/sysinfo/Makefile index 6d04fcba1d..d9f708b7ea 100644 --- a/drivers/sysinfo/Makefile +++ b/drivers/sysinfo/Makefile @@ -4,5 +4,6 @@ # Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc obj-y += sysinfo-uclass.o obj-$(CONFIG_SYSINFO_GAZERBEAM) += gazerbeam.o +obj-$(CONFIG_SYSINFO_GPIO) += gpio.o obj-$(CONFIG_SYSINFO_SANDBOX) += sandbox.o obj-$(CONFIG_SYSINFO_SMBIOS) += smbios.o diff --git a/drivers/sysinfo/gpio.c b/drivers/sysinfo/gpio.c new file mode 100644 index 0000000000..1d7f050998 --- /dev/null +++ b/drivers/sysinfo/gpio.c @@ -0,0 +1,141 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2021 Sean Anderson + */ + +#include +#include +#include +#include +#include +#include + +/** + * struct sysinfo_gpio_priv - GPIO sysinfo private data + * @gpios: List of GPIOs used to detect the revision + * @gpio_num: The number of GPIOs in @gpios + * @revision: The revision as detected from the GPIOs. + */ +struct sysinfo_gpio_priv { + struct gpio_desc *gpios; + int gpio_num, revision; +}; + +static int sysinfo_gpio_detect(struct udevice *dev) +{ + int ret; + struct sysinfo_gpio_priv *priv = dev_get_priv(dev); + + ret = dm_gpio_get_values_as_int_base3(priv->gpios, priv->gpio_num); + if (ret < 0) + return ret; + + priv->revision = ret; + return 0; +} + +static int sysinfo_gpio_get_int(struct udevice *dev, int id, int *val) +{ + struct sysinfo_gpio_priv *priv = dev_get_priv(dev); + + switch (id) { + case SYSINFO_ID_BOARD_MODEL: + *val = priv->revision; + return 0; + default: + return -EINVAL; + }; +} + +static int sysinfo_gpio_get_str(struct udevice *dev, int id, size_t size, char *val) +{ + struct sysinfo_gpio_priv *priv = dev_get_priv(dev); + + switch (id) { + case SYSINFO_ID_BOARD_MODEL: { + const char *name = NULL; + int i, ret; + u32 revision; + + for (i = 0; i < priv->gpio_num; i++) { + ret = dev_read_u32_index(dev, "revisions", i, + &revision); + if (ret) { + if (ret != -EOVERFLOW) + return ret; + break; + } + + if (revision == priv->revision) { + ret = dev_read_string_index(dev, "names", i, + &name); + if (ret < 0) + return ret; + break; + } + } + if (!name) + name = "unknown"; + + strncpy(val, name, size); + val[size - 1] = '\0'; + return 0; + } default: + return -EINVAL; + }; +} + +static const struct sysinfo_ops sysinfo_gpio_ops = { + .detect = sysinfo_gpio_detect, + .get_int = sysinfo_gpio_get_int, + .get_str = sysinfo_gpio_get_str, +}; + +static int sysinfo_gpio_probe(struct udevice *dev) +{ + int ret; + struct sysinfo_gpio_priv *priv = dev_get_priv(dev); + + priv->gpio_num = gpio_get_list_count(dev, "gpios"); + if (priv->gpio_num < 0) { + dev_err(dev, "could not get gpios length (err = %d)\n", + priv->gpio_num); + return priv->gpio_num; + } + + priv->gpios = calloc(priv->gpio_num, sizeof(*priv->gpios)); + if (!priv->gpios) { + dev_err(dev, "could not allocate memory for %d gpios\n", + priv->gpio_num); + return -ENOMEM; + } + + ret = gpio_request_list_by_name(dev, "gpios", priv->gpios, + priv->gpio_num, GPIOD_IS_IN); + if (ret != priv->gpio_num) { + dev_err(dev, "could not get gpios (err = %d)\n", + priv->gpio_num); + return ret; + } + + if (!dev_read_bool(dev, "revisions") || !dev_read_bool(dev, "names")) { + dev_err(dev, "revisions or names properties missing\n"); + return -ENOENT; + } + + return 0; +} + +static const struct udevice_id sysinfo_gpio_ids[] = { + { .compatible = "gpio-sysinfo" }, + { /* sentinel */ } +}; + +U_BOOT_DRIVER(sysinfo_gpio) = { + .name = "sysinfo_gpio", + .id = UCLASS_SYSINFO, + .of_match = sysinfo_gpio_ids, + .ops = &sysinfo_gpio_ops, + .priv_auto = sizeof(struct sysinfo_gpio_priv), + .probe = sysinfo_gpio_probe, +}; From 1cbfed8d3e9254d7e2a9466498ef867f7cb3f4cd Mon Sep 17 00:00:00 2001 From: Sean Anderson Date: Tue, 20 Apr 2021 10:50:58 -0400 Subject: [PATCH 5/9] test: Add gpio-sysinfo test This adds a test for the gpio-sysinfo driver. Signed-off-by: Sean Anderson Reviewed-by: Simon Glass --- arch/sandbox/dts/test.dts | 7 +++ configs/sandbox64_defconfig | 1 + configs/sandbox_defconfig | 1 + configs/sandbox_flattree_defconfig | 1 + configs/sandbox_noinst_defconfig | 1 + configs/sandbox_spl_defconfig | 1 + test/dm/Makefile | 1 + test/dm/sysinfo-gpio.c | 69 ++++++++++++++++++++++++++++++ 8 files changed, 82 insertions(+) create mode 100644 test/dm/sysinfo-gpio.c diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index 4fde923e9a..6fd56c351c 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -1526,6 +1526,13 @@ compatible = "sandbox,sysinfo-sandbox"; }; + sysinfo-gpio { + compatible = "gpio-sysinfo"; + gpios = <&gpio_a 15>, <&gpio_a 16>, <&gpio_a 17>; + revisions = <19>, <5>; + names = "rev_a", "foo"; + }; + some_regmapped-bus { #address-cells = <0x1>; #size-cells = <0x1>; diff --git a/configs/sandbox64_defconfig b/configs/sandbox64_defconfig index 8a7e519847..80c5be2067 100644 --- a/configs/sandbox64_defconfig +++ b/configs/sandbox64_defconfig @@ -204,6 +204,7 @@ CONFIG_SPMI=y CONFIG_SPMI_SANDBOX=y CONFIG_SYSINFO=y CONFIG_SYSINFO_SANDBOX=y +CONFIG_SYSINFO_GPIO=y CONFIG_SYSRESET=y CONFIG_TIMER=y CONFIG_TIMER_EARLY=y diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index 00befc8b40..570af61cbd 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -245,6 +245,7 @@ CONFIG_SPMI=y CONFIG_SPMI_SANDBOX=y CONFIG_SYSINFO=y CONFIG_SYSINFO_SANDBOX=y +CONFIG_SYSINFO_GPIO=y CONFIG_SYSRESET=y CONFIG_TIMER=y CONFIG_TIMER_EARLY=y diff --git a/configs/sandbox_flattree_defconfig b/configs/sandbox_flattree_defconfig index 97ac46d21f..853c9440ea 100644 --- a/configs/sandbox_flattree_defconfig +++ b/configs/sandbox_flattree_defconfig @@ -178,6 +178,7 @@ CONFIG_SPMI=y CONFIG_SPMI_SANDBOX=y CONFIG_SYSINFO=y CONFIG_SYSINFO_SANDBOX=y +CONFIG_SYSINFO_GPIO=y CONFIG_SYSRESET=y CONFIG_TIMER=y CONFIG_TIMER_EARLY=y diff --git a/configs/sandbox_noinst_defconfig b/configs/sandbox_noinst_defconfig index 2e7b138558..c7fc98b556 100644 --- a/configs/sandbox_noinst_defconfig +++ b/configs/sandbox_noinst_defconfig @@ -197,6 +197,7 @@ CONFIG_SPMI=y CONFIG_SPMI_SANDBOX=y CONFIG_SYSINFO=y CONFIG_SYSINFO_SANDBOX=y +CONFIG_SYSINFO_GPIO=y CONFIG_SYSRESET=y CONFIG_SPL_SYSRESET=y CONFIG_TIMER=y diff --git a/configs/sandbox_spl_defconfig b/configs/sandbox_spl_defconfig index a71349432e..87223a54d8 100644 --- a/configs/sandbox_spl_defconfig +++ b/configs/sandbox_spl_defconfig @@ -199,6 +199,7 @@ CONFIG_SPMI=y CONFIG_SPMI_SANDBOX=y CONFIG_SYSINFO=y CONFIG_SYSINFO_SANDBOX=y +CONFIG_SYSINFO_GPIO=y CONFIG_SYSRESET=y CONFIG_SPL_SYSRESET=y CONFIG_TIMER=y diff --git a/test/dm/Makefile b/test/dm/Makefile index e7cb1eec96..c9644617a1 100644 --- a/test/dm/Makefile +++ b/test/dm/Makefile @@ -96,6 +96,7 @@ obj-$(CONFIG_SPMI) += spmi.o obj-y += syscon.o obj-$(CONFIG_RESET_SYSCON) += syscon-reset.o obj-$(CONFIG_SYSINFO) += sysinfo.o +obj-$(CONFIG_SYSINFO_GPIO) += sysinfo-gpio.o obj-$(CONFIG_TEE) += tee.o obj-$(CONFIG_TIMER) += timer.o obj-$(CONFIG_DM_USB) += usb.o diff --git a/test/dm/sysinfo-gpio.c b/test/dm/sysinfo-gpio.c new file mode 100644 index 0000000000..2e494b3f34 --- /dev/null +++ b/test/dm/sysinfo-gpio.c @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2021 Sean Anderson + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static int dm_test_sysinfo_gpio(struct unit_test_state *uts) +{ + char buf[64]; + int val; + struct udevice *sysinfo, *gpio; + + ut_assertok(uclass_get_device_by_name(UCLASS_SYSINFO, "sysinfo-gpio", + &sysinfo)); + ut_assertok(uclass_get_device_by_name(UCLASS_GPIO, "base-gpios", &gpio)); + + /* + * Set up pins: pull-up (1), pull-down (0) and floating (2). This should + * result in digits 2 0 1, i.e. 2 * 9 + 1 * 3 = 19 + */ + sandbox_gpio_set_flags(gpio, 15, GPIOD_EXT_PULL_UP); + sandbox_gpio_set_flags(gpio, 16, GPIOD_EXT_PULL_DOWN); + sandbox_gpio_set_flags(gpio, 17, 0); + ut_assertok(sysinfo_detect(sysinfo)); + ut_assertok(sysinfo_get_int(sysinfo, SYSINFO_ID_BOARD_MODEL, &val)); + ut_asserteq(19, val); + ut_assertok(sysinfo_get_str(sysinfo, SYSINFO_ID_BOARD_MODEL, sizeof(buf), + buf)); + ut_asserteq_str("rev_a", buf); + + /* + * Set up pins: floating (2), pull-up (1) and pull-down (0). This should + * result in digits 0 1 2, i.e. 1 * 3 + 2 = 5 + */ + sandbox_gpio_set_flags(gpio, 15, 0); + sandbox_gpio_set_flags(gpio, 16, GPIOD_EXT_PULL_UP); + sandbox_gpio_set_flags(gpio, 17, GPIOD_EXT_PULL_DOWN); + ut_assertok(sysinfo_detect(sysinfo)); + ut_assertok(sysinfo_get_int(sysinfo, SYSINFO_ID_BOARD_MODEL, &val)); + ut_asserteq(5, val); + ut_assertok(sysinfo_get_str(sysinfo, SYSINFO_ID_BOARD_MODEL, sizeof(buf), + buf)); + ut_asserteq_str("foo", buf); + + /* + * Set up pins: floating (2), pull-up (1) and pull-down (0). This should + * result in digits 1 2 0, i.e. 1 * 9 + 2 * 3 = 15 + */ + sandbox_gpio_set_flags(gpio, 15, GPIOD_EXT_PULL_DOWN); + sandbox_gpio_set_flags(gpio, 16, 0); + sandbox_gpio_set_flags(gpio, 17, GPIOD_EXT_PULL_UP); + ut_assertok(sysinfo_detect(sysinfo)); + ut_assertok(sysinfo_get_int(sysinfo, SYSINFO_ID_BOARD_MODEL, &val)); + ut_asserteq(15, val); + ut_assertok(sysinfo_get_str(sysinfo, SYSINFO_ID_BOARD_MODEL, sizeof(buf), + buf)); + ut_asserteq_str("unknown", buf); + + return 0; +} +DM_TEST(dm_test_sysinfo_gpio, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); From 95fd9772011f29fad2c40fbc3060b5dac042152c Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Wed, 21 Apr 2021 11:06:54 +0200 Subject: [PATCH 6/9] env: allow environment to be amended from control dtb It can be useful to use the same U-Boot binary for multiple purposes, say the normal one, one for developers that allow breaking into the U-Boot shell, and one for use during bootstrapping which runs a special-purpose bootcmd. Or one can have several board variants that can share almost all boot logic, but just needs a few tweaks in the variables used by the boot script. To that end, allow the control dtb to contain a /config/enviroment node (or whatever one puts in fdt_env_path variable), whose property/value pairs are used to update the run-time environment after it has been loaded from its persistent location. The indirection via fdt_env_path is for maximum flexibility - for example, should the user wish (or board logic dictate) that the values in the DTB should no longer be applied, one simply needs to delete the fdt_env_path variable; that can even be done automatically by including a fdt_env_path = ""; property in the DTB node. Reviewed-by: Simon Glass Signed-off-by: Rasmus Villemoes Acked-by: Joe Hershberger --- common/board_r.c | 2 ++ env/Kconfig | 18 ++++++++++++++++++ env/common.c | 30 ++++++++++++++++++++++++++++++ include/env.h | 15 +++++++++++++++ include/env_default.h | 3 +++ 5 files changed, 68 insertions(+) diff --git a/common/board_r.c b/common/board_r.c index c835ff8e26..3f82404772 100644 --- a/common/board_r.c +++ b/common/board_r.c @@ -459,6 +459,8 @@ static int initr_env(void) else env_set_default(NULL, 0); + env_import_fdt(); + if (IS_ENABLED(CONFIG_OF_CONTROL)) env_set_hex("fdtcontroladdr", (unsigned long)map_to_sysmem(gd->fdt_blob)); diff --git a/env/Kconfig b/env/Kconfig index 1b7906cf72..1411f9e815 100644 --- a/env/Kconfig +++ b/env/Kconfig @@ -670,6 +670,24 @@ config DELAY_ENVIRONMENT later by U-Boot code. With CONFIG_OF_CONTROL this is instead controlled by the value of /config/load-environment. +config ENV_IMPORT_FDT + bool "Amend environment by FDT properties" + depends on OF_CONTROL + help + If selected, after the environment has been loaded from its + persistent location, the "env_fdt_path" variable is looked + up and used as a path to a node in the control DTB. The + property/value pairs in that node is then used to update the + run-time environment. This can be useful to use the same + U-Boot binary with different board variants. + +config ENV_FDT_PATH + string "Default value for env_fdt_path variable" + depends on ENV_IMPORT_FDT + default "/config/environment" + help + The initial value of the env_fdt_path variable. + config ENV_APPEND bool "Always append the environment with new data" default n diff --git a/env/common.c b/env/common.c index 49bbb05eec..81e9e0b2aa 100644 --- a/env/common.c +++ b/env/common.c @@ -20,6 +20,7 @@ #include #include #include +#include DECLARE_GLOBAL_DATA_PTR; @@ -334,3 +335,32 @@ int env_complete(char *var, int maxv, char *cmdv[], int bufsz, char *buf, return found; } #endif + +#ifdef CONFIG_ENV_IMPORT_FDT +void env_import_fdt(void) +{ + const char *path; + struct ofprop prop; + ofnode node; + int res; + + path = env_get("env_fdt_path"); + if (!path || !path[0]) + return; + + node = ofnode_path(path); + if (!ofnode_valid(node)) { + printf("Warning: device tree node '%s' not found\n", path); + return; + } + + for (res = ofnode_get_first_property(node, &prop); + !res; + res = ofnode_get_next_property(&prop)) { + const char *name, *val; + + val = ofnode_get_property_by_prop(&prop, &name, NULL); + env_set(name, val); + } +} +#endif diff --git a/include/env.h b/include/env.h index b5731e4b9a..d5e2bcb530 100644 --- a/include/env.h +++ b/include/env.h @@ -375,4 +375,19 @@ int env_get_char(int index); * This is used for those unfortunate archs with crappy toolchains */ void env_reloc(void); + + +/** + * env_import_fdt() - Import environment values from device tree blob + * + * This uses the value of the environment variable "env_fdt_path" as a + * path to an fdt node, whose property/value pairs are added to the + * environment. + */ +#ifdef CONFIG_ENV_IMPORT_FDT +void env_import_fdt(void); +#else +static inline void env_import_fdt(void) {} +#endif + #endif diff --git a/include/env_default.h b/include/env_default.h index ea31a8eddf..1ddd64ba8f 100644 --- a/include/env_default.h +++ b/include/env_default.h @@ -103,6 +103,9 @@ const uchar default_environment[] = { #ifdef CONFIG_SYS_SOC "soc=" CONFIG_SYS_SOC "\0" #endif +#ifdef CONFIG_ENV_IMPORT_FDT + "env_fdt_path=" CONFIG_ENV_FDT_PATH "\0" +#endif #endif #if defined(CONFIG_BOOTCOUNT_BOOTLIMIT) && (CONFIG_BOOTCOUNT_BOOTLIMIT > 0) "bootlimit=" __stringify(CONFIG_BOOTCOUNT_BOOTLIMIT)"\0" From 8c72842af5f81826b344c26b1c28c3122edb909e Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Wed, 21 Apr 2021 11:06:55 +0200 Subject: [PATCH 7/9] sandbox: add test of CONFIG_ENV_IMPORT_FDT Check that a variable defined in /config/environment is found in the run-time environment, and that clearing fdt_env_path from within that node works. Reviewed-by: Simon Glass Signed-off-by: Rasmus Villemoes Acked-by: Joe Hershberger [trini: Conditionalize the test being linked in] Signed-off-by: Tom Rini --- arch/sandbox/dts/test.dts | 7 +++++++ configs/sandbox64_defconfig | 1 + configs/sandbox_defconfig | 1 + test/env/Makefile | 1 + test/env/fdt.c | 20 ++++++++++++++++++++ 5 files changed, 30 insertions(+) create mode 100644 test/env/fdt.c diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index 6fd56c351c..fe26ced31d 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -53,6 +53,13 @@ osd0 = "/osd"; }; + config { + environment { + from_fdt = "yes"; + fdt_env_path = ""; + }; + }; + audio: audio-codec { compatible = "sandbox,audio-codec"; #sound-dai-cells = <1>; diff --git a/configs/sandbox64_defconfig b/configs/sandbox64_defconfig index 80c5be2067..9a373bab6f 100644 --- a/configs/sandbox64_defconfig +++ b/configs/sandbox64_defconfig @@ -91,6 +91,7 @@ CONFIG_ENV_IS_NOWHERE=y CONFIG_ENV_IS_IN_EXT4=y CONFIG_ENV_EXT4_INTERFACE="host" CONFIG_ENV_EXT4_DEVICE_AND_PART="0:0" +CONFIG_ENV_IMPORT_FDT=y CONFIG_BOOTP_SEND_HOSTNAME=y CONFIG_NETCONSOLE=y CONFIG_IP_DEFRAG=y diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index 570af61cbd..bdbf714e2b 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -106,6 +106,7 @@ CONFIG_ENV_IS_NOWHERE=y CONFIG_ENV_IS_IN_EXT4=y CONFIG_ENV_EXT4_INTERFACE="host" CONFIG_ENV_EXT4_DEVICE_AND_PART="0:0" +CONFIG_ENV_IMPORT_FDT=y CONFIG_BOOTP_SEND_HOSTNAME=y CONFIG_NETCONSOLE=y CONFIG_IP_DEFRAG=y diff --git a/test/env/Makefile b/test/env/Makefile index 5c8eae31b0..9a98fd4796 100644 --- a/test/env/Makefile +++ b/test/env/Makefile @@ -5,3 +5,4 @@ obj-y += cmd_ut_env.o obj-y += attr.o obj-y += hashtable.o +obj-$(CONFIG_ENV_IMPORT_FDT) += fdt.o diff --git a/test/env/fdt.c b/test/env/fdt.c new file mode 100644 index 0000000000..30bfa88c35 --- /dev/null +++ b/test/env/fdt.c @@ -0,0 +1,20 @@ +#include +#include +#include +#include +#include + +static int env_test_fdt_import(struct unit_test_state *uts) +{ + const char *val; + + val = env_get("from_fdt"); + ut_assertnonnull(val); + ut_asserteq_str("yes", val); + + val = env_get("fdt_env_path"); + ut_assertnull(val); + + return 0; +} +ENV_TEST(env_test_fdt_import, 0); From 653cd92d66771cf1fbd1b7612b06a0874d316654 Mon Sep 17 00:00:00 2001 From: Farhan Ali Date: Fri, 26 Feb 2021 10:17:33 -0800 Subject: [PATCH 8/9] cmd: gpt: Add option to write GPT partitions to environment variable This change would enhance the existing 'gpt read' command to allow (optionally) writing of the read GPT partitions to an environment variable in the UBOOT partitions layout format. This would allow users to easily change the overall partition settings by editing said variable and then using the variable in the 'gpt write' and 'gpt verify' commands. Signed-off-by: Farhan Ali Cc: Simon Glass Cc: Heinrich Schuchardt Cc: Corneliu Doban Cc: Rayagonda Kokatanur Cc: Rasmus Villemoes Reviewed-by: Simon Glass --- cmd/gpt.c | 46 ++++++++++++++++++++++++++++++++++++++-------- doc/README.gpt | 17 +++++++++++++++++ 2 files changed, 55 insertions(+), 8 deletions(-) diff --git a/cmd/gpt.c b/cmd/gpt.c index 76a95ade6c..17f2b839d7 100644 --- a/cmd/gpt.c +++ b/cmd/gpt.c @@ -350,17 +350,46 @@ static int get_gpt_info(struct blk_desc *dev_desc) } /* a wrapper to test get_gpt_info */ -static int do_get_gpt_info(struct blk_desc *dev_desc) +static int do_get_gpt_info(struct blk_desc *dev_desc, char * const namestr) { - int ret; + int numparts; - ret = get_gpt_info(dev_desc); - if (ret > 0) { - print_gpt_info(); + numparts = get_gpt_info(dev_desc); + + if (numparts > 0) { + if (namestr) { + char disk_guid[UUID_STR_LEN + 1]; + char *partitions_list; + int partlistlen; + int ret = -1; + + ret = get_disk_guid(dev_desc, disk_guid); + if (ret < 0) + return ret; + + partlistlen = calc_parts_list_len(numparts); + partitions_list = malloc(partlistlen); + if (!partitions_list) { + del_gpt_info(); + return -ENOMEM; + } + memset(partitions_list, '\0', partlistlen); + + ret = create_gpt_partitions_list(numparts, disk_guid, + partitions_list); + if (ret < 0) + printf("Error: Could not create partition list string!\n"); + else + env_set(namestr, partitions_list); + + free(partitions_list); + } else { + print_gpt_info(); + } del_gpt_info(); return 0; } - return ret; + return numparts; } #endif @@ -982,7 +1011,7 @@ static int do_gpt(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) ret = do_disk_guid(blk_dev_desc, argv[4]); #ifdef CONFIG_CMD_GPT_RENAME } else if (strcmp(argv[1], "read") == 0) { - ret = do_get_gpt_info(blk_dev_desc); + ret = do_get_gpt_info(blk_dev_desc, (argc == 5) ? argv[4] : NULL); } else if ((strcmp(argv[1], "swap") == 0) || (strcmp(argv[1], "rename") == 0)) { ret = do_rename_gpt_parts(blk_dev_desc, argv[1], argv[4], argv[5]); @@ -1028,8 +1057,9 @@ U_BOOT_CMD(gpt, CONFIG_SYS_MAXARGS, 1, do_gpt, " gpt guid mmc 0 varname\n" #ifdef CONFIG_CMD_GPT_RENAME "gpt partition renaming commands:\n" - " gpt read \n" + " gpt read []\n" " - read GPT into a data structure for manipulation\n" + " - read GPT partitions into environment variable\n" " gpt swap \n" " - change all partitions named name1 to name2\n" " and vice-versa\n" diff --git a/doc/README.gpt b/doc/README.gpt index ac975f66b8..91e397d06f 100644 --- a/doc/README.gpt +++ b/doc/README.gpt @@ -237,6 +237,23 @@ doc/arch/index.rst: => gpt swap host 0 name othername [ . . . ] +Modifying GPT partition layout from U-Boot: +=========================================== + +The entire GPT partition layout can be exported to an environment +variable and then modified enmasse. Users can change the partition +numbers, offsets, names and sizes. The resulting variable can used to +reformat the device. Here is an example of reading the GPT partitions +into a variable and then modifying them: + +U-BOOT> gpt read mmc 0 current_partitions +U-BOOT> env edit current_partitions +edit: uuid_disk=[...];name=part1,start=0x4000,size=0x4000,uuid=[...]; +name=part2,start=0xc000,size=0xc000,uuid=[...];[ . . . ] + +U-BOOT> gpt write mmc 0 $current_partitions +U-BOOT> gpt verify mmc 0 $current_partitions + Partition type GUID: ==================== From f0b21ebd417cfe75f266b4dc16318bb3581783b9 Mon Sep 17 00:00:00 2001 From: Niko Mauno Date: Mon, 22 Feb 2021 19:18:51 +0000 Subject: [PATCH 9/9] fdt_support.c: Allow late kernel cmdline modification By declaring board-specific board_fdt_chosen_bootargs() the kernel command line arguments can be adjusted before injecting to flat dt chosen node. Signed-off-by: Niko Mauno --- common/fdt_support.c | 12 +++++++++++- include/fdt_support.h | 10 ++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/common/fdt_support.c b/common/fdt_support.c index e624bbdf40..7eb5ba3bb2 100644 --- a/common/fdt_support.c +++ b/common/fdt_support.c @@ -269,6 +269,15 @@ int fdt_initrd(void *fdt, ulong initrd_start, ulong initrd_end) return 0; } +/** + * board_fdt_chosen_bootargs - boards may override this function to use + * alternative kernel command line arguments + */ +__weak char *board_fdt_chosen_bootargs(void) +{ + return env_get("bootargs"); +} + int fdt_chosen(void *fdt) { int nodeoffset; @@ -286,7 +295,8 @@ int fdt_chosen(void *fdt) if (nodeoffset < 0) return nodeoffset; - str = env_get("bootargs"); + str = board_fdt_chosen_bootargs(); + if (str) { err = fdt_setprop(fdt, nodeoffset, "bootargs", str, strlen(str) + 1); diff --git a/include/fdt_support.h b/include/fdt_support.h index 46eb1dbbb2..e2a4689cd8 100644 --- a/include/fdt_support.h +++ b/include/fdt_support.h @@ -185,6 +185,16 @@ int fdt_find_or_add_subnode(void *fdt, int parentoffset, const char *name); */ int ft_board_setup(void *blob, struct bd_info *bd); +/** + * board_fdt_chosen_bootargs() - Arbitrarily amend fdt kernel command line + * + * This is used for late modification of kernel command line arguments just + * before they are added into the /chosen node in flat device tree. + * + * @return: pointer to kernel command line arguments in memory + */ +char *board_fdt_chosen_bootargs(void); + /* * The keystone2 SOC requires all 32 bit aliased addresses to be converted * to their 36 physical format. This has to happen after all fdt nodes