Merge https://source.denx.de/u-boot/custodians/u-boot-riscv
diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts
index 4fde923..fe26ced 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>;
@@ -1526,6 +1533,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/cmd/gpt.c b/cmd/gpt.c
index 76a95ad..17f2b83 100644
--- a/cmd/gpt.c
+++ b/cmd/gpt.c
@@ -350,17 +350,46 @@
 }
 
 /* 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;
+
+	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);
 
-	ret = get_gpt_info(dev_desc);
-	if (ret > 0) {
-		print_gpt_info();
+			free(partitions_list);
+		} else {
+			print_gpt_info();
+		}
 		del_gpt_info();
 		return 0;
 	}
-	return ret;
+	return numparts;
 }
 #endif
 
@@ -982,7 +1011,7 @@
 		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 @@
 	" gpt guid mmc 0 varname\n"
 #ifdef CONFIG_CMD_GPT_RENAME
 	"gpt partition renaming commands:\n"
-	" gpt read <interface> <dev>\n"
+	" gpt read <interface> <dev> [<varname>]\n"
 	"    - read GPT into a data structure for manipulation\n"
+	"    - read GPT partitions into environment variable\n"
 	" gpt swap <interface> <dev> <name1> <name2>\n"
 	"    - change all partitions named name1 to name2\n"
 	"      and vice-versa\n"
diff --git a/common/board_r.c b/common/board_r.c
index c835ff8e..3f82404 100644
--- a/common/board_r.c
+++ b/common/board_r.c
@@ -459,6 +459,8 @@
 	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/common/fdt_support.c b/common/fdt_support.c
index e624bbd..7eb5ba3 100644
--- a/common/fdt_support.c
+++ b/common/fdt_support.c
@@ -269,6 +269,15 @@
 	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 @@
 	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/common/spl/spl_fit.c b/common/spl/spl_fit.c
index 4288f57..caddf51 100644
--- a/common/spl/spl_fit.c
+++ b/common/spl/spl_fit.c
@@ -110,6 +110,10 @@
 		 * 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/configs/sandbox64_defconfig b/configs/sandbox64_defconfig
index 8a7e519..9a373ba 100644
--- a/configs/sandbox64_defconfig
+++ b/configs/sandbox64_defconfig
@@ -91,6 +91,7 @@
 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
@@ -204,6 +205,7 @@
 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 00befc8b..bdbf714 100644
--- a/configs/sandbox_defconfig
+++ b/configs/sandbox_defconfig
@@ -106,6 +106,7 @@
 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
@@ -245,6 +246,7 @@
 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 97ac46d..853c944 100644
--- a/configs/sandbox_flattree_defconfig
+++ b/configs/sandbox_flattree_defconfig
@@ -178,6 +178,7 @@
 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 2e7b138..c7fc98b 100644
--- a/configs/sandbox_noinst_defconfig
+++ b/configs/sandbox_noinst_defconfig
@@ -197,6 +197,7 @@
 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 a713494..87223a5 100644
--- a/configs/sandbox_spl_defconfig
+++ b/configs/sandbox_spl_defconfig
@@ -199,6 +199,7 @@
 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/doc/README.gpt b/doc/README.gpt
index ac975f6..91e397d 100644
--- a/doc/README.gpt
+++ b/doc/README.gpt
@@ -237,6 +237,23 @@
 => 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:
 ====================
 
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 0000000..b5739d9
--- /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/gpio/gpio-uclass.c b/drivers/gpio/gpio-uclass.c
index e4e7f58..131099c 100644
--- a/drivers/gpio/gpio-uclass.c
+++ b/drivers/gpio/gpio-uclass.c
@@ -1215,9 +1215,9 @@
 {
 	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);
 	}
diff --git a/drivers/sysinfo/Kconfig b/drivers/sysinfo/Kconfig
index 85c1e81..381dcd8 100644
--- a/drivers/sysinfo/Kconfig
+++ b/drivers/sysinfo/Kconfig
@@ -30,4 +30,12 @@
 	  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 6d04fcb..d9f708b 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/gazerbeam.h b/drivers/sysinfo/gazerbeam.h
index 171729d..6bf3c00 100644
--- a/drivers/sysinfo/gazerbeam.h
+++ b/drivers/sysinfo/gazerbeam.h
@@ -5,10 +5,12 @@
  *
  */
 
+#include <sysinfo.h>
+
 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/gpio.c b/drivers/sysinfo/gpio.c
new file mode 100644
index 0000000..1d7f050
--- /dev/null
+++ b/drivers/sysinfo/gpio.c
@@ -0,0 +1,141 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2021 Sean Anderson <sean.anderson@seco.com>
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <log.h>
+#include <sysinfo.h>
+#include <asm/gpio.h>
+#include <dm/device_compat.h>
+
+/**
+ * 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,
+};
diff --git a/drivers/sysinfo/sandbox.h b/drivers/sysinfo/sandbox.h
index 2cff494..d9c5804 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,
diff --git a/drivers/sysinfo/sysinfo-uclass.c b/drivers/sysinfo/sysinfo-uclass.c
index 6df58fe..4a660df 100644
--- a/drivers/sysinfo/sysinfo-uclass.c
+++ b/drivers/sysinfo/sysinfo-uclass.c
@@ -8,6 +8,10 @@
 #include <dm.h>
 #include <sysinfo.h>
 
+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_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_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_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_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 @@
 	.id		= UCLASS_SYSINFO,
 	.name		= "sysinfo",
 	.post_bind	= dm_scan_fdt_dev,
+	.per_device_auto	= sizeof(bool),
 };
diff --git a/env/Kconfig b/env/Kconfig
index 1b7906c..1411f9e 100644
--- a/env/Kconfig
+++ b/env/Kconfig
@@ -670,6 +670,24 @@
 	  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 49bbb05..81e9e0b 100644
--- a/env/common.c
+++ b/env/common.c
@@ -20,6 +20,7 @@
 #include <errno.h>
 #include <malloc.h>
 #include <u-boot/crc.h>
+#include <dm/ofnode.h>
 
 DECLARE_GLOBAL_DATA_PTR;
 
@@ -334,3 +335,32 @@
 	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 b5731e4..d5e2bcb 100644
--- a/include/env.h
+++ b/include/env.h
@@ -375,4 +375,19 @@
  * 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 ea31a8e..1ddd64b 100644
--- a/include/env_default.h
+++ b/include/env_default.h
@@ -103,6 +103,9 @@
 #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"
diff --git a/include/fdt_support.h b/include/fdt_support.h
index 46eb1db..e2a4689 100644
--- a/include/fdt_support.h
+++ b/include/fdt_support.h
@@ -185,6 +185,16 @@
  */
 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
diff --git a/include/sysinfo.h b/include/sysinfo.h
index 8054d4d..b140d74 100644
--- a/include/sysinfo.h
+++ b/include/sysinfo.h
@@ -60,7 +60,8 @@
 	 * 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 @@
 	 * 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 @@
  *
  * @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 @@
  * @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 @@
  * @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 @@
  * @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 @@
  * 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 @@
  * 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 @@
  * @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/Makefile b/test/dm/Makefile
index e7cb1ee..c964461 100644
--- a/test/dm/Makefile
+++ b/test/dm/Makefile
@@ -96,6 +96,7 @@
 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 0000000..2e494b3
--- /dev/null
+++ b/test/dm/sysinfo-gpio.c
@@ -0,0 +1,69 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2021 Sean Anderson <sean.anderson@seco.com>
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <log.h>
+#include <sysinfo.h>
+#include <asm/gpio.h>
+#include <dm/test.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+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);
diff --git a/test/dm/sysinfo.c b/test/dm/sysinfo.c
index 4aaa9e8..96b3a8e 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;
diff --git a/test/env/Makefile b/test/env/Makefile
index 5c8eae3..9a98fd4 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 0000000..30bfa88
--- /dev/null
+++ b/test/env/fdt.c
@@ -0,0 +1,20 @@
+#include <common.h>
+#include <command.h>
+#include <env_attr.h>
+#include <test/env.h>
+#include <test/ut.h>
+
+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);