Merge branch '2020-12-01-next-imports' into next

- More IPQ40xx improvements
- Add string support to setexpr (and tests)
- ProxyDHCP support
- Assorted cleanups
diff --git a/Makefile b/Makefile
index 41446d8..c94cc39 100644
--- a/Makefile
+++ b/Makefile
@@ -1005,7 +1005,7 @@
 append = cat $(filter-out $< $(PHONY), $^) >> $@
 
 quiet_cmd_pad_cat = CAT     $@
-cmd_pad_cat = $(cmd_objcopy) && $(append) || rm -f $@
+cmd_pad_cat = $(cmd_objcopy) && $(append) || { rm -f $@; false; }
 
 quiet_cmd_lzma = LZMA    $@
 cmd_lzma = lzma -c -z -k -9 $< > $@
@@ -1312,7 +1312,7 @@
 shell_cmd = { $(call echo-cmd,$(1)) $(cmd_$(1)); }
 
 quiet_cmd_objcopy_uboot = OBJCOPY $@
-cmd_objcopy_uboot = $(cmd_objcopy) && $(call shell_cmd,static_rela,$<,$@,$(CONFIG_SYS_TEXT_BASE)) || rm -f $@
+cmd_objcopy_uboot = $(cmd_objcopy) && $(call shell_cmd,static_rela,$<,$@,$(CONFIG_SYS_TEXT_BASE)) || { rm -f $@; false; }
 
 u-boot-nodtb.bin: u-boot FORCE
 	$(call if_changed,objcopy_uboot)
@@ -1584,12 +1584,12 @@
 ifneq ($(CONFIG_ARCH_SOCFPGA),)
 quiet_cmd_gensplx4 = GENSPLX4 $@
 cmd_gensplx4 = cat	spl/u-boot-spl.sfp spl/u-boot-spl.sfp	\
-			spl/u-boot-spl.sfp spl/u-boot-spl.sfp > $@ || rm -f $@
+			spl/u-boot-spl.sfp spl/u-boot-spl.sfp > $@ || { rm -f $@; false; }
 spl/u-boot-splx4.sfp: spl/u-boot-spl.sfp FORCE
 	$(call if_changed,gensplx4)
 
 quiet_cmd_socboot = SOCBOOT $@
-cmd_socboot = cat	spl/u-boot-splx4.sfp u-boot.img > $@ || rm -f $@
+cmd_socboot = cat	spl/u-boot-splx4.sfp u-boot.img > $@ || { rm -f $@; false; }
 u-boot-with-spl.sfp: spl/u-boot-splx4.sfp u-boot.img FORCE
 	$(call if_changed,socboot)
 
@@ -1599,12 +1599,12 @@
 			spl/u-boot-spl.sfp spl/u-boot-spl.pad \
 			spl/u-boot-spl.sfp spl/u-boot-spl.pad \
 			spl/u-boot-spl.sfp spl/u-boot-spl.pad > $@ || \
-			rm -f $@ spl/u-boot-spl.pad
+			{ rm -f $@ spl/u-boot-spl.pad; false; }
 u-boot-spl-padx4.sfp: spl/u-boot-spl.sfp FORCE
 	$(call if_changed,gensplpadx4)
 
 quiet_cmd_socnandboot = SOCNANDBOOT $@
-cmd_socnandboot = cat	u-boot-spl-padx4.sfp u-boot.img > $@ || rm -f $@
+cmd_socnandboot = cat	u-boot-spl-padx4.sfp u-boot.img > $@ || { rm -f $@; false; }
 u-boot-with-nand-spl.sfp: u-boot-spl-padx4.sfp u-boot.img FORCE
 	$(call if_changed,socnandboot)
 
diff --git a/README b/README
index 7b73a1c..5ca11f2 100644
--- a/README
+++ b/README
@@ -1925,13 +1925,6 @@
 		try longer timeout such as
 		#define CONFIG_NFS_TIMEOUT 10000UL
 
-- Command Interpreter:
-		CONFIG_SYS_PROMPT_HUSH_PS2
-
-		This defines the secondary prompt string, which is
-		printed when the command interpreter needs more input
-		to complete a command. Usually "> ".
-
 	Note:
 
 		In the current implementation, the local variables
diff --git a/arch/arm/mach-ipq40xx/clock-ipq4019.c b/arch/arm/mach-ipq40xx/clock-ipq4019.c
index 31ae971..a3f8729 100644
--- a/arch/arm/mach-ipq40xx/clock-ipq4019.c
+++ b/arch/arm/mach-ipq40xx/clock-ipq4019.c
@@ -8,8 +8,8 @@
  *
  */
 
-#include <common.h>
 #include <clk-uclass.h>
+#include <common.h>
 #include <dm.h>
 #include <errno.h>
 
@@ -25,9 +25,8 @@
 	case GCC_BLSP1_UART1_APPS_CLK: /*UART1*/
 		/* This clock is already initialized by SBL1 */
 		return 0;
-		break;
 	default:
-		return 0;
+		return -EINVAL;
 	}
 }
 
@@ -35,7 +34,7 @@
 {
 	struct msm_clk_priv *priv = dev_get_priv(dev);
 
-	priv->base = devfdt_get_addr(dev);
+	priv->base = dev_read_addr(dev);
 	if (priv->base == FDT_ADDR_T_NONE)
 		return -EINVAL;
 
@@ -53,13 +52,19 @@
 	case GCC_BLSP1_QUP1_SPI_APPS_CLK: /*SPI1*/
 		/* This clock is already initialized by SBL1 */
 		return 0;
-		break;
 	case GCC_PRNG_AHB_CLK: /*PRNG*/
 		/* This clock is already initialized by SBL1 */
 		return 0;
-		break;
-	default:
+	case GCC_USB3_MASTER_CLK:
+	case GCC_USB3_SLEEP_CLK:
+	case GCC_USB3_MOCK_UTMI_CLK:
+	case GCC_USB2_MASTER_CLK:
+	case GCC_USB2_SLEEP_CLK:
+	case GCC_USB2_MOCK_UTMI_CLK:
+		/* These clocks is already initialized by SBL1 */
 		return 0;
+	default:
+		return -EINVAL;
 	}
 }
 
diff --git a/board/keymile/common/common.c b/board/keymile/common/common.c
index 03c7ce9..df507e2 100644
--- a/board/keymile/common/common.c
+++ b/board/keymile/common/common.c
@@ -56,7 +56,7 @@
 
 	/* try to read rootfssize (ram image) from environment */
 	p = env_get("rootfssize");
-	if (p != NULL)
+	if (p)
 		strict_strtoul(p, 16, &rootfssize);
 	pram = (rootfssize + CONFIG_KM_RESERVED_PRAM + CONFIG_KM_PHRAM +
 		CONFIG_KM_PNVRAM) / 0x400;
@@ -165,7 +165,7 @@
 	char *p;
 
 	p = get_local_var("IVM_BoardId");
-	if (p == NULL) {
+	if (!p) {
 		printf("can't get the IVM_Boardid\n");
 		return 1;
 	}
@@ -174,7 +174,7 @@
 	printf("set boardid=%s\n", buf);
 
 	p = get_local_var("IVM_HWKey");
-	if (p == NULL) {
+	if (!p) {
 		printf("can't get the IVM_HWKey\n");
 		return 1;
 	}
@@ -186,8 +186,8 @@
 	return 0;
 }
 
-U_BOOT_CMD(km_setboardid, 1, 0, do_setboardid, "setboardid", "read out bid and "
-				 "hwkey from IVM and set in environment");
+U_BOOT_CMD(km_setboardid, 1, 0, do_setboardid, "setboardid",
+	   "read out bid and hwkey from IVM and set in environment");
 
 /*
  * command km_checkbidhwk
@@ -218,14 +218,14 @@
 	 * already stored in the local hush variables
 	 */
 	p = get_local_var("IVM_BoardId");
-	if (p == NULL) {
+	if (!p) {
 		printf("can't get the IVM_Boardid\n");
 		return 1;
 	}
 	rc = strict_strtoul(p, 16, &ivmbid);
 
 	p = get_local_var("IVM_HWKey");
-	if (p == NULL) {
+	if (!p) {
 		printf("can't get the IVM_HWKey\n");
 		return 1;
 	}
@@ -238,10 +238,10 @@
 
 	/* now try to read values from environment if available */
 	p = env_get("boardid");
-	if (p != NULL)
+	if (p)
 		rc = strict_strtoul(p, 16, &envbid);
 	p = env_get("hwkey");
-	if (p != NULL)
+	if (p)
 		rc = strict_strtoul(p, 16, &envhwkey);
 
 	if (rc != 0) {
@@ -263,9 +263,8 @@
 
 			if (verbose) {
 				printf("IVM_BoardId: %ld, IVM_HWKey=%ld\n",
-					ivmbid, ivmhwkey);
-				printf("boardIdHwKeyList: %s\n",
-					bidhwklist);
+				       ivmbid, ivmhwkey);
+				printf("boardIdHwKeyList: %s\n", bidhwklist);
 			}
 			while (!found) {
 				/* loop over each bid/hwkey pair in the list */
@@ -291,13 +290,13 @@
 					while (*rest && !isxdigit(*rest))
 						rest++;
 				}
-				if ((!bid) || (!hwkey)) {
+				if (!bid || !hwkey) {
 					/* end of list */
 					break;
 				}
 				if (verbose) {
 					printf("trying bid=0x%lX, hwkey=%ld\n",
-						bid, hwkey);
+					       bid, hwkey);
 				}
 				/*
 				 * Compare the values of the found entry in the
@@ -305,7 +304,7 @@
 				 * in the inventory eeprom. If they are equal
 				 * set the values in environment variables.
 				 */
-				if ((bid == ivmbid) && (hwkey == ivmhwkey)) {
+				if (bid == ivmbid && hwkey == ivmhwkey) {
 					char buf[10];
 
 					found = 1;
@@ -321,12 +320,12 @@
 	}
 
 	/* compare now the values */
-	if ((ivmbid == envbid) && (ivmhwkey == envhwkey)) {
+	if (ivmbid == envbid && ivmhwkey == envhwkey) {
 		printf("boardid=0x%3lX, hwkey=%ld\n", envbid, envhwkey);
 		rc = 0; /* match */
 	} else {
 		printf("Error: env boardid=0x%3lX, hwkey=%ld\n", envbid,
-			envhwkey);
+		       envhwkey);
 		printf("       IVM bId=0x%3lX, hwKey=%ld\n", ivmbid, ivmhwkey);
 		rc = 1; /* don't match */
 	}
@@ -334,10 +333,8 @@
 }
 
 U_BOOT_CMD(km_checkbidhwk, 2, 0, do_checkboardidhwk,
-		"check boardid and hwkey",
-		"[v]\n  - check environment parameter "\
-		"\"boardIdListHex\" against stored boardid and hwkey "\
-		"from the IVM\n    v: verbose output"
+	   "check boardid and hwkey",
+	   "[v]\n  - check environment parameter \"boardIdListHex\" against stored boardid and hwkey from the IVM\n    v: verbose output"
 );
 
 /*
@@ -356,6 +353,7 @@
 #if defined(CONFIG_POST)
 	testpin = post_hotkeys_pressed();
 #endif
+
 	s = env_get("test_bank");
 	/* when test_bank is not set, act as if testpin is not asserted */
 	testboot = (testpin != 0) && (s);
@@ -370,6 +368,6 @@
 }
 
 U_BOOT_CMD(km_checktestboot, 2, 0, do_checktestboot,
-		"check if testpin is asserted",
-		"[v]\n  v - verbose output"
+	   "check if testpin is asserted",
+	   "[v]\n  v - verbose output"
 );
diff --git a/board/keymile/common/ivm.c b/board/keymile/common/ivm.c
index 60b89fe..e989bf6 100644
--- a/board/keymile/common/ivm.c
+++ b/board/keymile/common/ivm.c
@@ -46,28 +46,27 @@
 {
 	char tempbuf[256];
 
-	if (value != NULL) {
+	if (value) {
 		sprintf(tempbuf, "%s=%s", name, value);
 		return set_local_var(tempbuf, 0);
-	} else {
-		unset_local_var(name);
 	}
+	unset_local_var(name);
 	return 0;
 }
 
 static int ivm_get_value(unsigned char *buf, int len, char *name, int off,
-				int check)
+			 int check)
 {
 	unsigned short	val;
 	unsigned char	valbuf[30];
 
-	if ((buf[off + 0] != buf[off + 2]) &&
-	    (buf[off + 2] != buf[off + 4])) {
+	if (buf[off + 0] != buf[off + 2] &&
+	    buf[off + 2] != buf[off + 4]) {
 		printf("%s Error corrupted %s\n", __func__, name);
 		val = -1;
 	} else {
 		val = buf[off + 0] + (buf[off + 1] << 8);
-		if ((val == 0) && (check == 1))
+		if (val == 0 && check == 1)
 			val = -1;
 	}
 	sprintf((char *)valbuf, "%x", val);
@@ -98,9 +97,9 @@
 }
 
 static int ivm_findinventorystring(int type,
-					unsigned char *const string,
-					unsigned long maxlen,
-					unsigned char *buf)
+				   unsigned char *const string,
+				   unsigned long maxlen,
+				   unsigned char *buf)
 {
 	int xcode = 0;
 	unsigned long cr = 0;
@@ -133,12 +132,12 @@
 	 */
 	if (addr < INVENTORYDATASIZE) {
 		/* Copy the IVM string in the corresponding string */
-		for (; (buf[addr] != '\r')			&&
-			((buf[addr] != ';') ||  (!stop))	&&
-			(size < (maxlen - 1)			&&
-			(addr < INVENTORYDATASIZE)); addr++) {
+		for (; (buf[addr] != '\r')		&&
+		     ((buf[addr] != ';') ||  (!stop))	&&
+		     (size < (maxlen - 1)		&&
+		     (addr < INVENTORYDATASIZE)); addr++) {
 			size += sprintf((char *)string + size, "%c",
-						convert_char (buf[addr]));
+					convert_char (buf[addr]));
 		}
 
 		/*
@@ -176,12 +175,12 @@
 	unsigned long	crceeprom;
 
 	crc = ivm_calc_crc(buf, CONFIG_SYS_IVM_EEPROM_PAGE_LEN - 2);
-	crceeprom = (buf[CONFIG_SYS_IVM_EEPROM_PAGE_LEN - 1] + \
+	crceeprom = (buf[CONFIG_SYS_IVM_EEPROM_PAGE_LEN - 1] +
 			buf[CONFIG_SYS_IVM_EEPROM_PAGE_LEN - 2] * 256);
 	if (crc != crceeprom) {
 		if (block == 0)
-			printf("Error CRC Block: %d EEprom: calculated: \
-			%lx EEprom: %lx\n", block, crc, crceeprom);
+			printf("Error CRC Block: %d EEprom: calculated: %lx EEprom: %lx\n",
+			       block, crc, crceeprom);
 		return -1;
 	}
 	return 0;
@@ -189,7 +188,7 @@
 
 /* take care of the possible MAC address offset and the IVM content offset */
 static int process_mac(unsigned char *valbuf, unsigned char *buf,
-				int offset, bool unique)
+		       int offset, bool unique)
 {
 	unsigned char mac[6];
 	unsigned long val = (buf[4] << 16) + (buf[5] << 8) + buf[6];
@@ -197,9 +196,9 @@
 	/* use an intermediate buffer, to not change IVM content
 	 * MAC address is at offset 1
 	 */
-	memcpy(mac, buf+1, 6);
+	memcpy(mac, buf + 1, 6);
 
-	/* MAC adress can be set to locally administred, this is only allowed
+	/* MAC address can be set to locally administred, this is only allowed
 	 * for interfaces which have now connection to the outside. For these
 	 * addresses we need to set the second bit in the first byte.
 	 */
@@ -222,7 +221,7 @@
 	unsigned char	valbuf[MAC_STR_SZ];
 	unsigned long	count;
 
-	/* IVM_MAC Adress begins at offset 1 */
+	/* IVM_MAC Address begins at offset 1 */
 	sprintf((char *)valbuf, "%pM", buf + 1);
 	ivm_set_value("IVM_MacAddress", (char *)valbuf);
 	/* IVM_MacCount */
@@ -247,9 +246,9 @@
 		return -1;
 
 	ivm_get_value(buf, CONFIG_SYS_IVM_EEPROM_PAGE_LEN,
-			"IVM_BoardId", 0, 1);
+		      "IVM_BoardId", 0, 1);
 	val = ivm_get_value(buf, CONFIG_SYS_IVM_EEPROM_PAGE_LEN,
-			"IVM_HWKey", 6, 1);
+			    "IVM_HWKey", 6, 1);
 	if (val != 0xffff) {
 		sprintf((char *)valbuf, "%x", ((val / 100) % 10));
 		ivm_set_value("IVM_HWVariant", (char *)valbuf);
@@ -257,7 +256,7 @@
 		ivm_set_value("IVM_HWVersion", (char *)valbuf);
 	}
 	ivm_get_value(buf, CONFIG_SYS_IVM_EEPROM_PAGE_LEN,
-		"IVM_Functions", 12, 0);
+		      "IVM_Functions", 12, 0);
 
 	GET_STRING("IVM_Symbol", IVM_POS_SYMBOL_ONLY, 8)
 	GET_STRING("IVM_DeviceName", IVM_POS_SHORT_TEXT, 64)
@@ -269,7 +268,7 @@
 		while (i < len) {
 			if (tmp[i] == ';') {
 				ivm_set_value("IVM_ShortText",
-					(char *)&tmp[i + 1]);
+					      (char *)&tmp[i + 1]);
 				break;
 			}
 			i++;
@@ -292,7 +291,7 @@
 	if (ivm_check_crc(&buf[CONFIG_SYS_IVM_EEPROM_PAGE_LEN * 2], 2) != 0)
 		return 0;
 	ivm_analyze_block2(&buf[CONFIG_SYS_IVM_EEPROM_PAGE_LEN * 2],
-		CONFIG_SYS_IVM_EEPROM_PAGE_LEN);
+			   CONFIG_SYS_IVM_EEPROM_PAGE_LEN);
 
 	return 0;
 }
@@ -305,22 +304,23 @@
 	/* do we have the page 2 filled ? if not return */
 	if (ivm_check_crc(buf, 2))
 		return 0;
-	page2 = &buf[CONFIG_SYS_IVM_EEPROM_PAGE_LEN*2];
+	page2 = &buf[CONFIG_SYS_IVM_EEPROM_PAGE_LEN * 2];
 
-#ifndef CONFIG_KMTEGR1
-	/* if an offset is defined, add it */
-	process_mac(valbuf, page2, mac_address_offset, true);
-	env_set((char *)"ethaddr", (char *)valbuf);
-#else
-/* KMTEGR1 has a special setup. eth0 has no connection to the outside and
- * gets an locally administred MAC address, eth1 is the debug interface and
- * gets the official MAC address from the IVM
- */
-	process_mac(valbuf, page2, mac_address_offset, false);
-	env_set((char *)"ethaddr", (char *)valbuf);
-	process_mac(valbuf, page2, mac_address_offset, true);
-	env_set((char *)"eth1addr", (char *)valbuf);
-#endif
+	if (!IS_ENABLED(CONFIG_KMTEGR1)) {
+		/* if an offset is defined, add it */
+		process_mac(valbuf, page2, mac_address_offset, true);
+		env_set((char *)"ethaddr", (char *)valbuf);
+	} else {
+		/* KMTEGR1 has a special setup. eth0 has no connection to the
+		 * outside and gets an locally administred MAC address, eth1 is
+		 * the debug interface and gets the official MAC address from
+		 * the IVM
+		 */
+		process_mac(valbuf, page2, mac_address_offset, false);
+		env_set((char *)"ethaddr", (char *)valbuf);
+		process_mac(valbuf, page2, mac_address_offset, true);
+		env_set((char *)"eth1addr", (char *)valbuf);
+	}
 
 	return 0;
 }
diff --git a/board/keymile/km_arm/fpga_config.c b/board/keymile/km_arm/fpga_config.c
index abb5b7d..839b162 100644
--- a/board/keymile/km_arm/fpga_config.c
+++ b/board/keymile/km_arm/fpga_config.c
@@ -40,14 +40,14 @@
 	ret = i2c_read(BOCO_ADDR, reg, 1, &regval, 1);
 	if (ret) {
 		printf("%s: error reading the BOCO @%#x !!\n",
-			__func__, reg);
+		       __func__, reg);
 		return ret;
 	}
 	regval &= ~flags;
 	ret = i2c_write(BOCO_ADDR, reg, 1, &regval, 1);
 	if (ret) {
 		printf("%s: error writing the BOCO @%#x !!\n",
-			__func__, reg);
+		       __func__, reg);
 		return ret;
 	}
 
@@ -63,14 +63,14 @@
 	ret = i2c_read(BOCO_ADDR, reg, 1, &regval, 1);
 	if (ret) {
 		printf("%s: error reading the BOCO @%#x !!\n",
-			__func__, reg);
+		       __func__, reg);
 		return ret;
 	}
 	regval |= flags;
 	ret = i2c_write(BOCO_ADDR, reg, 1, &regval, 1);
 	if (ret) {
 		printf("%s: error writing the BOCO @%#x !!\n",
-			__func__, reg);
+		       __func__, reg);
 		return ret;
 	}
 
@@ -113,7 +113,8 @@
 	skip = 0;
 #ifndef CONFIG_KM_FPGA_FORCE_CONFIG
 	/* if the FPGA is already configured, we do not want to
-	 * reconfigure it */
+	 * reconfigure it
+	 */
 	skip = 0;
 	if (fpga_done()) {
 		printf("PCIe FPGA config: skipped\n");
@@ -179,7 +180,7 @@
 		ret = i2c_read(BOCO_ADDR, SPI_REG, 1, &spictrl, 1);
 		if (ret) {
 			printf("%s: error reading the BOCO spictrl !!\n",
-				__func__);
+			       __func__);
 			return ret;
 		}
 		if (timeout-- == 0) {
@@ -235,7 +236,8 @@
 #endif
 
 /* the FPGA was configured, we configure the BOCO2 so that the EEPROM
- * is available from the Bobcat SPI bus */
+ * is available from the Bobcat SPI bus
+ */
 int toggle_eeprom_spi_bus(void)
 {
 	int ret = 0;
diff --git a/board/keymile/km_arm/km_arm.c b/board/keymile/km_arm/km_arm.c
index 7d191ab..60187bd 100644
--- a/board/keymile/km_arm/km_arm.c
+++ b/board/keymile/km_arm/km_arm.c
@@ -53,9 +53,9 @@
 #define PHY_MARVELL_88E1118R_LED_CTRL_REG		0x0010
 
 #define PHY_MARVELL_88E1118R_LED_CTRL_RESERVED		0x1000
-#define PHY_MARVELL_88E1118R_LED_CTRL_LED0_1000MB	(0x7<<0)
-#define PHY_MARVELL_88E1118R_LED_CTRL_LED1_ACT		(0x3<<4)
-#define PHY_MARVELL_88E1118R_LED_CTRL_LED2_LINK		(0x0<<8)
+#define PHY_MARVELL_88E1118R_LED_CTRL_LED0_1000MB	(0x7 << 0)
+#define PHY_MARVELL_88E1118R_LED_CTRL_LED1_ACT		(0x3 << 4)
+#define PHY_MARVELL_88E1118R_LED_CTRL_LED2_LINK		(0x0 << 8)
 
 /* I/O pin to erase flash RGPP09 = MPP43 */
 #define KM_FLASH_ERASE_ENABLE	43
@@ -169,6 +169,7 @@
 {
 	uchar buf[32];
 	unsigned int bootcountaddr;
+
 	bootcountaddr = gd->ram_size - BOOTCOUNT_ADDR;
 	sprintf((char *)buf, "0x%x", bootcountaddr);
 	env_set("bootcountaddr", (char *)buf);
@@ -192,7 +193,7 @@
 
 	/* set the 2 bitbang i2c pins as output gpios */
 	tmp = readl(MVEBU_GPIO0_BASE + 4);
-	writel(tmp & (~KM_KIRKWOOD_SOFT_I2C_GPIOS) , MVEBU_GPIO0_BASE + 4);
+	writel(tmp & (~KM_KIRKWOOD_SOFT_I2C_GPIOS), MVEBU_GPIO0_BASE + 4);
 #endif
 	/* adjust SDRAM size for bank 0 */
 	mvebu_sdram_size_adjust(0);
@@ -292,11 +293,11 @@
 
 #define	PHY_LED_SEL_REG		0x18
 #define PHY_LED0_LINK		(0x5)
-#define PHY_LED1_ACT		(0x8<<4)
-#define PHY_LED2_INT		(0xe<<8)
+#define PHY_LED1_ACT		(0x8 << 4)
+#define PHY_LED2_INT		(0xe << 8)
 #define	PHY_SPEC_CTRL_REG	0x1c
-#define PHY_RGMII_CLK_STABLE	(0x1<<10)
-#define PHY_CLSA		(0x1<<1)
+#define PHY_RGMII_CLK_STABLE	(0x1 << 10)
+#define PHY_CLSA		(0x1 << 1)
 
 /* Configure and enable MV88E3018 PHY */
 void reset_phy(void)
@@ -407,8 +408,8 @@
 		return;
 
 	/* check for Marvell 88E1118R Gigabit PHY (PIGGY3) */
-	if ((oui == PHY_MARVELL_OUI) &&
-	    (model == PHY_MARVELL_88E1118R_MODEL)) {
+	if (oui == PHY_MARVELL_OUI &&
+	    model == PHY_MARVELL_88E1118R_MODEL) {
 		/* set page register to 3 */
 		if (miiphy_write(name, CONFIG_PHY_BASE_ADR,
 				 PHY_MARVELL_PAGE_REG,
@@ -438,7 +439,6 @@
 }
 #endif
 
-
 #if defined(CONFIG_HUSH_INIT_VAR)
 int hush_init_var(void)
 {
@@ -478,22 +478,23 @@
 
 int post_hotkeys_pressed(void)
 {
-#if defined(CONFIG_KM_COGE5UN)
-	return kw_gpio_get_value(KM_POST_EN_L);
-#else
-	return !kw_gpio_get_value(KM_POST_EN_L);
-#endif
+	if (IS_ENABLED(CONFIG_KM_COGE5UN))
+		return kw_gpio_get_value(KM_POST_EN_L);
+	else
+		return !kw_gpio_get_value(KM_POST_EN_L);
 }
 
 ulong post_word_load(void)
 {
-	void* addr = (void *) (gd->ram_size - BOOTCOUNT_ADDR + POST_WORD_OFF);
+	void *addr = (void *)(gd->ram_size - BOOTCOUNT_ADDR + POST_WORD_OFF);
+
 	return in_le32(addr);
 
 }
 void post_word_store(ulong value)
 {
-	void* addr = (void *) (gd->ram_size - BOOTCOUNT_ADDR + POST_WORD_OFF);
+	void *addr = (void *)(gd->ram_size - BOOTCOUNT_ADDR + POST_WORD_OFF);
+
 	out_le32(addr, value);
 }
 
@@ -502,14 +503,14 @@
 	*vstart = CONFIG_SYS_SDRAM_BASE;
 
 	/* we go up to relocation plus a 1 MB margin */
-	*size = CONFIG_SYS_TEXT_BASE - (1<<20);
+	*size = CONFIG_SYS_TEXT_BASE - (1 << 20);
 
 	return 0;
 }
 #endif
 
 #if defined(CONFIG_SYS_EEPROM_WREN)
-int eeprom_write_enable(unsigned dev_addr, int state)
+int eeprom_write_enable(unsigned int dev_addr, int state)
 {
 	kw_gpio_set_value(KM_KIRKWOOD_ENV_WP, !state);
 
diff --git a/cmd/Kconfig b/cmd/Kconfig
index 1595de9..98cf543 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -55,6 +55,15 @@
 	  This string is displayed in the command line to the left of the
 	  cursor.
 
+config SYS_PROMPT_HUSH_PS2
+	string "Hush shell secondary prompt"
+	depends on HUSH_PARSER
+	default "> "
+	help
+	  This defines the secondary prompt string, which is
+	  printed when the command interpreter needs more input
+	  to complete a command. Usually "> ".
+
 config SYS_XTRACE
 	string "Command execution tracer"
 	depends on CMDLINE
diff --git a/cmd/itest.c b/cmd/itest.c
index a0cf4be..9a441ce 100644
--- a/cmd/itest.c
+++ b/cmd/itest.c
@@ -197,10 +197,10 @@
 #endif
 		value = binary_test (argv[2], argv[1], argv[3], w);
 		break;
-	case -2:
+	case CMD_DATA_SIZE_STR:
 		value = binary_test (argv[2], argv[1], argv[3], 0);
 		break;
-	case -1:
+	case CMD_DATA_SIZE_ERR:
 	default:
 		puts("Invalid data width specifier\n");
 		value = 0;
diff --git a/cmd/mem.c b/cmd/mem.c
index 56e1d07..1d4f2ba 100644
--- a/cmd/mem.c
+++ b/cmd/mem.c
@@ -393,7 +393,7 @@
 		 * Defaults to long if no or incorrect specification.
 		 */
 		size = cmd_get_data_size(argv[0], 4);
-		if (size < 0 && size != -2 /* string */)
+		if (size < 0 && size != CMD_DATA_SIZE_STR)
 			return 1;
 
 		argc--;
diff --git a/cmd/pxe_utils.c b/cmd/pxe_utils.c
index 8716e78..235522f 100644
--- a/cmd/pxe_utils.c
+++ b/cmd/pxe_utils.c
@@ -451,11 +451,14 @@
 
 	/*
 	 * fdt usage is optional:
-	 * It handles the following scenarios. All scenarios are exclusive
+	 * It handles the following scenarios.
 	 *
-	 * Scenario 1: If fdt_addr_r specified and "fdt" label is defined in
-	 * pxe file, retrieve fdt blob from server. Pass fdt_addr_r to bootm,
-	 * and adjust argc appropriately.
+	 * Scenario 1: If fdt_addr_r specified and "fdt" or "fdtdir" label is
+	 * defined in pxe file, retrieve fdt blob from server. Pass fdt_addr_r to
+	 * bootm, and adjust argc appropriately.
+	 *
+	 * If retrieve fails and no exact fdt blob is specified in pxe file with
+	 * "fdt" label, try Scenario 2.
 	 *
 	 * Scenario 2: If there is an fdt_addr specified, pass it along to
 	 * bootm, and adjust argc appropriately.
@@ -521,9 +524,13 @@
 
 			free(fdtfilefree);
 			if (err < 0) {
-				printf("Skipping %s for failure retrieving fdt\n",
-				       label->name);
-				goto cleanup;
+				bootm_argv[3] = NULL;
+
+				if (label->fdt) {
+					printf("Skipping %s for failure retrieving FDT\n",
+					       label->name);
+					goto cleanup;
+				}
 			}
 		} else {
 			bootm_argv[3] = NULL;
diff --git a/cmd/setexpr.c b/cmd/setexpr.c
index 770dc24..e828be3 100644
--- a/cmd/setexpr.c
+++ b/cmd/setexpr.c
@@ -13,10 +13,27 @@
 #include <command.h>
 #include <env.h>
 #include <log.h>
+#include <malloc.h>
 #include <mapmem.h>
+#include <linux/sizes.h>
 
-static ulong get_arg(char *s, int w)
+/**
+ * struct expr_arg: Holds an argument to an expression
+ *
+ * @ival: Integer value (if width is not CMD_DATA_SIZE_STR)
+ * @sval: String value (if width is CMD_DATA_SIZE_STR)
+ */
+struct expr_arg {
+	union {
+		ulong ival;
+		char *sval;
+	};
+};
+
+static int get_arg(char *s, int w, struct expr_arg *argp)
 {
+	struct expr_arg arg;
+
 	/*
 	 * If the parameter starts with a '*' then assume it is a pointer to
 	 * the value we want.
@@ -25,6 +42,8 @@
 		ulong *p;
 		ulong addr;
 		ulong val;
+		int len;
+		char *str;
 
 		addr = simple_strtoul(&s[1], NULL, 16);
 		switch (w) {
@@ -32,31 +51,56 @@
 			p = map_sysmem(addr, sizeof(uchar));
 			val = (ulong)*(uchar *)p;
 			unmap_sysmem(p);
-			return val;
+			arg.ival = val;
+			break;
 		case 2:
 			p = map_sysmem(addr, sizeof(ushort));
 			val = (ulong)*(ushort *)p;
 			unmap_sysmem(p);
-			return val;
+			arg.ival = val;
+			break;
+		case CMD_DATA_SIZE_STR:
+			p = map_sysmem(addr, SZ_64K);
+
+			/* Maximum string length of 64KB plus terminator */
+			len = strnlen((char *)p, SZ_64K) + 1;
+			str = malloc(len);
+			if (!str) {
+				printf("Out of memory\n");
+				return -ENOMEM;
+			}
+			memcpy(str, p, len);
+			str[len - 1] = '\0';
+			unmap_sysmem(p);
+			arg.sval = str;
+			break;
 		case 4:
+			p = map_sysmem(addr, sizeof(u32));
+			val = *(u32 *)p;
+			unmap_sysmem(p);
+			arg.ival = val;
+			break;
 		default:
 			p = map_sysmem(addr, sizeof(ulong));
 			val = *p;
 			unmap_sysmem(p);
-			return val;
+			arg.ival = val;
+			break;
 		}
 	} else {
-		return simple_strtoul(s, NULL, 16);
+		if (w == CMD_DATA_SIZE_STR)
+			return -EINVAL;
+		arg.ival = simple_strtoul(s, NULL, 16);
 	}
+	*argp = arg;
+
+	return 0;
 }
 
 #ifdef CONFIG_REGEX
 
 #include <slre.h>
 
-#define SLRE_BUFSZ	16384
-#define SLRE_PATSZ	4096
-
 /*
  * memstr - Find the first substring in memory
  * @s1: The string to be searched
@@ -79,13 +123,24 @@
 	return NULL;
 }
 
-static char *substitute(char *string,	/* string buffer */
-			int *slen,	/* current string length */
-			int ssize,	/* string bufer size */
-			const char *old,/* old (replaced) string */
-			int olen,	/* length of old string */
-			const char *new,/* new (replacement) string */
-			int nlen)	/* length of new string */
+/**
+ * substitute() - Substitute part of one string with another
+ *
+ * This updates @string so that the first occurrence of @old is replaced with
+ * @new
+ *
+ * @string: String buffer containing string to update at the start
+ * @slen: Pointer to current string length, updated on success
+ * @ssize: Size of string buffer
+ * @old: Old string to find in the buffer (no terminator needed)
+ * @olen: Length of @old excluding terminator
+ * @new: New string to replace @old with
+ * @nlen: Length of @new excluding terminator
+ * @return pointer to immediately after the copied @new in @string, or NULL if
+ *	no replacement took place
+ */
+static char *substitute(char *string, int *slen, int ssize,
+			const char *old, int olen, const char *new, int nlen)
 {
 	char *p = memstr(string, *slen, old, olen);
 
@@ -114,7 +169,7 @@
 		memmove(p + nlen, p + olen, tail);
 	}
 
-	/* insert substitue */
+	/* insert substitute */
 	memcpy(p, new, nlen);
 
 	*slen += nlen - olen;
@@ -122,71 +177,32 @@
 	return p + nlen;
 }
 
-/*
- * Perform regex operations on a environment variable
- *
- * Returns 0 if OK, 1 in case of errors.
- */
-static int regex_sub(const char *name,
-	const char *r, const char *s, const char *t,
-	int global)
+int setexpr_regex_sub(char *data, uint data_size, char *nbuf, uint nbuf_size,
+		      const char *r, const char *s, bool global)
 {
 	struct slre slre;
-	char data[SLRE_BUFSZ];
 	char *datap = data;
-	const char *value;
 	int res, len, nlen, loop;
 
-	if (name == NULL)
-		return 1;
-
 	if (slre_compile(&slre, r) == 0) {
 		printf("Error compiling regex: %s\n", slre.err_str);
 		return 1;
 	}
 
-	if (t == NULL) {
-		value = env_get(name);
-
-		if (value == NULL) {
-			printf("## Error: variable \"%s\" not defined\n", name);
-			return 1;
-		}
-		t = value;
-	}
-
-	debug("REGEX on %s=%s\n", name, t);
-	debug("REGEX=\"%s\", SUBST=\"%s\", GLOBAL=%d\n",
-		r, s ? s : "<NULL>", global);
-
-	len = strlen(t);
-	if (len + 1 > SLRE_BUFSZ) {
-		printf("## error: subst buffer overflow: have %d, need %d\n",
-			SLRE_BUFSZ, len + 1);
-		return 1;
-	}
-
-	strcpy(data, t);
-
-	if (s == NULL)
-		nlen = 0;
-	else
-		nlen = strlen(s);
-
+	len = strlen(data);
 	for (loop = 0;; loop++) {
 		struct cap caps[slre.num_caps + 2];
-		char nbuf[SLRE_PATSZ];
 		const char *old;
 		char *np;
 		int i, olen;
 
 		(void) memset(caps, 0, sizeof(caps));
 
-		res = slre_match(&slre, datap, len, caps);
+		res = slre_match(&slre, datap, len - (datap - data), caps);
 
 		debug("Result: %d\n", res);
 
-		for (i = 0; i < slre.num_caps; i++) {
+		for (i = 0; i <= slre.num_caps; i++) {
 			if (caps[i].len > 0) {
 				debug("Substring %d: [%.*s]\n", i,
 					caps[i].len, caps[i].ptr);
@@ -195,7 +211,7 @@
 
 		if (res == 0) {
 			if (loop == 0) {
-				printf("%s: No match\n", t);
+				printf("%s: No match\n", data);
 				return 1;
 			} else {
 				break;
@@ -204,17 +220,16 @@
 
 		debug("## MATCH ## %s\n", data);
 
-		if (s == NULL) {
-			printf("%s=%s\n", name, t);
+		if (!s)
 			return 1;
-		}
 
 		old = caps[0].ptr;
 		olen = caps[0].len;
+		nlen = strlen(s);
 
-		if (nlen + 1 >= SLRE_PATSZ) {
+		if (nlen + 1 >= nbuf_size) {
 			printf("## error: pattern buffer overflow: have %d, need %d\n",
-				SLRE_BUFSZ, nlen + 1);
+			       nbuf_size, nlen + 1);
 			return 1;
 		}
 		strcpy(nbuf, s);
@@ -259,7 +274,7 @@
 					break;
 
 				np = substitute(np, &nlen,
-					SLRE_PATSZ,
+					nbuf_size - (np - nbuf),
 					backref, 2,
 					caps[i].ptr, caps[i].len);
 
@@ -269,9 +284,8 @@
 		}
 		debug("## SUBST(2) ## %s\n", nbuf);
 
-		datap = substitute(datap, &len, SLRE_BUFSZ,
-				old, olen,
-				nbuf, nlen);
+		datap = substitute(datap, &len, data_size - (datap - data),
+				   old, olen, nbuf, nlen);
 
 		if (datap == NULL)
 			return 1;
@@ -285,6 +299,62 @@
 	}
 	debug("## FINAL (now env_set()) :  %s\n", data);
 
+	return 0;
+}
+
+#define SLRE_BUFSZ	16384
+#define SLRE_PATSZ	4096
+
+/*
+ * Perform regex operations on a environment variable
+ *
+ * Returns 0 if OK, 1 in case of errors.
+ */
+static int regex_sub_var(const char *name, const char *r, const char *s,
+			 const char *t, int global)
+{
+	struct slre slre;
+	char data[SLRE_BUFSZ];
+	char nbuf[SLRE_PATSZ];
+	const char *value;
+	int len;
+	int ret;
+
+	if (!name)
+		return 1;
+
+	if (slre_compile(&slre, r) == 0) {
+		printf("Error compiling regex: %s\n", slre.err_str);
+		return 1;
+	}
+
+	if (!t) {
+		value = env_get(name);
+		if (!value) {
+			printf("## Error: variable \"%s\" not defined\n", name);
+			return 1;
+		}
+		t = value;
+	}
+
+	debug("REGEX on %s=%s\n", name, t);
+	debug("REGEX=\"%s\", SUBST=\"%s\", GLOBAL=%d\n", r, s ? s : "<NULL>",
+	      global);
+
+	len = strlen(t);
+	if (len + 1 > SLRE_BUFSZ) {
+		printf("## error: subst buffer overflow: have %d, need %d\n",
+		       SLRE_BUFSZ, len + 1);
+		return 1;
+	}
+
+	strcpy(data, t);
+
+	ret = setexpr_regex_sub(data, SLRE_BUFSZ, nbuf, SLRE_PATSZ, r, s,
+				global);
+	if (ret)
+		return 1;
+
 	printf("%s=%s\n", name, data);
 
 	return env_set(name, data);
@@ -294,8 +364,9 @@
 static int do_setexpr(struct cmd_tbl *cmdtp, int flag, int argc,
 		      char *const argv[])
 {
-	ulong a, b;
+	struct expr_arg aval, bval;
 	ulong value;
+	int ret = 0;
 	int w;
 
 	/*
@@ -312,12 +383,19 @@
 
 	w = cmd_get_data_size(argv[0], 4);
 
-	a = get_arg(argv[2], w);
+	if (get_arg(argv[2], w, &aval))
+		return CMD_RET_FAILURE;
 
 	/* plain assignment: "setexpr name value" */
 	if (argc == 3) {
-		env_set_hex(argv[1], a);
-		return 0;
+		if (w == CMD_DATA_SIZE_STR) {
+			ret = env_set(argv[1], aval.sval);
+			free(aval.sval);
+		} else {
+			ret = env_set_hex(argv[1], aval.ival);
+		}
+
+		return ret;
 	}
 
 	/* 5 or 6 args (6 args only with [g]sub) */
@@ -327,10 +405,10 @@
 	 * with 5 args, "t" will be NULL
 	 */
 	if (strcmp(argv[2], "gsub") == 0)
-		return regex_sub(argv[1], argv[3], argv[4], argv[5], 1);
+		return regex_sub_var(argv[1], argv[3], argv[4], argv[5], 1);
 
 	if (strcmp(argv[2], "sub") == 0)
-		return regex_sub(argv[1], argv[3], argv[4], argv[5], 0);
+		return regex_sub_var(argv[1], argv[3], argv[4], argv[5], 0);
 #endif
 
 	/* standard operators: "setexpr name val1 op val2" */
@@ -340,49 +418,89 @@
 	if (strlen(argv[3]) != 1)
 		return CMD_RET_USAGE;
 
-	b = get_arg(argv[4], w);
-
-	switch (argv[3][0]) {
-	case '|':
-		value = a | b;
-		break;
-	case '&':
-		value = a & b;
-		break;
-	case '+':
-		value = a + b;
-		break;
-	case '^':
-		value = a ^ b;
-		break;
-	case '-':
-		value = a - b;
-		break;
-	case '*':
-		value = a * b;
-		break;
-	case '/':
-		value = a / b;
-		break;
-	case '%':
-		value = a % b;
-		break;
-	default:
-		printf("invalid op\n");
-		return 1;
+	if (get_arg(argv[4], w, &bval)) {
+		if (w == CMD_DATA_SIZE_STR)
+			free(aval.sval);
+		return CMD_RET_FAILURE;
 	}
 
-	env_set_hex(argv[1], value);
+	if (w == CMD_DATA_SIZE_STR) {
+		int len;
+		char *str;
 
-	return 0;
+		switch (argv[3][0]) {
+		case '+':
+			len = strlen(aval.sval) + strlen(bval.sval) + 1;
+			str = malloc(len);
+			if (!str) {
+				printf("Out of memory\n");
+				ret = CMD_RET_FAILURE;
+			} else {
+				/* These were copied out and checked earlier */
+				strcpy(str, aval.sval);
+				strcat(str, bval.sval);
+				ret = env_set(argv[1], str);
+				if (ret)
+					printf("Could not set var\n");
+				free(str);
+			}
+			break;
+		default:
+			printf("invalid op\n");
+			ret = 1;
+		}
+	} else {
+		ulong a = aval.ival;
+		ulong b = bval.ival;
+
+		switch (argv[3][0]) {
+		case '|':
+			value = a | b;
+			break;
+		case '&':
+			value = a & b;
+			break;
+		case '+':
+			value = a + b;
+			break;
+		case '^':
+			value = a ^ b;
+			break;
+		case '-':
+			value = a - b;
+			break;
+		case '*':
+			value = a * b;
+			break;
+		case '/':
+			value = a / b;
+			break;
+		case '%':
+			value = a % b;
+			break;
+		default:
+			printf("invalid op\n");
+			return 1;
+		}
+
+		env_set_hex(argv[1], value);
+	}
+
+	if (w == CMD_DATA_SIZE_STR) {
+		free(aval.sval);
+		free(bval.sval);
+	}
+
+	return ret;
 }
 
 U_BOOT_CMD(
 	setexpr, 6, 0, do_setexpr,
 	"set environment variable as the result of eval expression",
-	"[.b, .w, .l] name [*]value1 <op> [*]value2\n"
+	"[.b, .w, .l, .s] name [*]value1 <op> [*]value2\n"
 	"    - set environment variable 'name' to the result of the evaluated\n"
 	"      expression specified by <op>.  <op> can be &, |, ^, +, -, *, /, %\n"
+	"      (for strings only + is supported)\n"
 	"      size argument is only meaningful if value1 and/or value2 are\n"
 	"      memory addresses (*)\n"
 	"setexpr[.b, .w, .l] name [*]value\n"
diff --git a/common/cli_hush.c b/common/cli_hush.c
index 66995c2..b7f0f0f 100644
--- a/common/cli_hush.c
+++ b/common/cli_hush.c
@@ -84,9 +84,6 @@
 #include <cli.h>
 #include <cli_hush.h>
 #include <command.h>        /* find_cmd */
-#ifndef CONFIG_SYS_PROMPT_HUSH_PS2
-#define CONFIG_SYS_PROMPT_HUSH_PS2	"> "
-#endif
 #endif
 #ifndef __U_BOOT__
 #include <ctype.h>     /* isalpha, isdigit */
diff --git a/common/command.c b/common/command.c
index 2c491e2..068cb55 100644
--- a/common/command.c
+++ b/common/command.c
@@ -475,13 +475,13 @@
 		case 'l':
 			return 4;
 		case 's':
-			return -2;
+			return CMD_DATA_SIZE_STR;
 		case 'q':
 			if (MEM_SUPPORT_64BIT_DATA)
 				return 8;
 			/* no break */
 		default:
-			return -1;
+			return CMD_DATA_SIZE_ERR;
 		}
 	}
 	return default_size;
diff --git a/common/spl/spl_fit.c b/common/spl/spl_fit.c
index 6418062..2fbee4f 100644
--- a/common/spl/spl_fit.c
+++ b/common/spl/spl_fit.c
@@ -558,6 +558,16 @@
 	if (spl_load_simple_fit_skip_processing())
 		return 0;
 
+	if (IS_ENABLED(CONFIG_SPL_FIT_SIGNATURE)) {
+		int conf_offset = fit_find_config_node(fit);
+
+		printf("## Checking hash(es) for config %s ... ",
+		       fit_get_name(fit, conf_offset, NULL));
+		if (fit_config_verify(fit, conf_offset))
+			return -EPERM;
+		puts("OK\n");
+	}
+
 	/* find the node holding the images information */
 	images = fdt_path_offset(fit, FIT_IMAGES_PATH);
 	if (images < 0) {
diff --git a/drivers/gpio/gpio-uclass.c b/drivers/gpio/gpio-uclass.c
index 0c01413..4785b6b 100644
--- a/drivers/gpio/gpio-uclass.c
+++ b/drivers/gpio/gpio-uclass.c
@@ -1100,9 +1100,8 @@
 {
 	int ret;
 
-	ret = fdtdec_parse_phandle_with_args(gd->fdt_blob, dev_of_offset(dev),
-					     list_name, "#gpio-cells", 0, -1,
-					     NULL);
+	ret = dev_read_phandle_with_args(dev, list_name, "#gpio-cells", 0, -1,
+					 NULL);
 	if (ret) {
 		debug("%s: Node '%s', property '%s', GPIO count failed: %d\n",
 		      __func__, dev->name, list_name, ret);
diff --git a/include/command.h b/include/command.h
index b9b5ec1..e229bf2 100644
--- a/include/command.h
+++ b/include/command.h
@@ -117,7 +117,31 @@
 	defined(CONFIG_CMD_PCI) || \
 	defined(CONFIG_CMD_SETEXPR)
 #define CMD_DATA_SIZE
-extern int cmd_get_data_size(char* arg, int default_size);
+#define CMD_DATA_SIZE_ERR	(-1)
+#define CMD_DATA_SIZE_STR	(-2)
+
+/**
+ * cmd_get_data_size() - Get the data-size specifier from a command
+ *
+ * This reads a '.x' size specifier appended to a command. For example 'md.b'
+ * is the 'md' command with a '.b' specifier, meaning that the command should
+ * use bytes.
+ *
+ * Valid characters are:
+ *
+ *	b - byte
+ *	w - word (16 bits)
+ *	l - long (32 bits)
+ *	q - quad (64 bits)
+ *	s - string
+ *
+ * @arg: Pointers to the command to check. If a valid specifier is present it
+ *	will be the last character of the string, following a '.'
+ * @default_size: Default size to return if there is no specifier
+ * @return data size in bytes (1, 2, 4, 8) or CMD_DATA_SIZE_ERR for an invalid
+ *	character, or CMD_DATA_SIZE_STR for a string
+ */
+int cmd_get_data_size(char *arg, int default_size);
 #endif
 
 #ifdef CONFIG_CMD_BOOTD
@@ -159,6 +183,23 @@
 			  char *const argv[]);
 #endif
 
+/**
+ * setexpr_regex_sub() - Replace a regex pattern with a string
+ *
+ * @data: Buffer containing the string to update
+ * @data_size: Size of buffer (must be large enough for the new string)
+ * @nbuf: Back-reference buffer
+ * @nbuf_size: Size of back-reference buffer (must be larger enough for @s plus
+ *	all back-reference expansions)
+ * @r: Regular expression to find
+ * @s: String to replace with
+ * @global: true to replace all matches in @data, false to replace just the
+ *	first
+ * @return 0 if OK, 1 on error
+ */
+int setexpr_regex_sub(char *data, uint data_size, char *nbuf, uint nbuf_size,
+		      const char *r, const char *s, bool global);
+
 /*
  * Error codes that commands return to cmd_process(). We use the standard 0
  * and 1 for success and failure, but add one more case - failure with a
diff --git a/include/configs/apalis-imx8.h b/include/configs/apalis-imx8.h
index db4e901..b474b2f 100644
--- a/include/configs/apalis-imx8.h
+++ b/include/configs/apalis-imx8.h
@@ -98,7 +98,6 @@
 #define PHYS_SDRAM_2_SIZE		SZ_2G		/* 2 GB */
 
 /* Monitor Command Prompt */
-#define CONFIG_SYS_PROMPT_HUSH_PS2	"> "
 #define CONFIG_SYS_CBSIZE		SZ_2K
 #define CONFIG_SYS_MAXARGS		64
 #define CONFIG_SYS_BARGSIZE		CONFIG_SYS_CBSIZE
diff --git a/include/configs/colibri-imx8x.h b/include/configs/colibri-imx8x.h
index 29a37ed..fc2c191 100644
--- a/include/configs/colibri-imx8x.h
+++ b/include/configs/colibri-imx8x.h
@@ -132,7 +132,6 @@
 #define PHYS_SDRAM_2_SIZE		0x00000000	/* 0 GB */
 
 /* Monitor Command Prompt */
-#define CONFIG_SYS_PROMPT_HUSH_PS2	"> "
 #define CONFIG_SYS_CBSIZE		SZ_2K
 #define CONFIG_SYS_MAXARGS		64
 #define CONFIG_SYS_BARGSIZE		CONFIG_SYS_CBSIZE
diff --git a/include/configs/imx8mm_beacon.h b/include/configs/imx8mm_beacon.h
index 3c95411..9a93dba 100644
--- a/include/configs/imx8mm_beacon.h
+++ b/include/configs/imx8mm_beacon.h
@@ -119,7 +119,6 @@
 #define CONFIG_MXC_UART_BASE		UART2_BASE_ADDR
 
 /* Monitor Command Prompt */
-#define CONFIG_SYS_PROMPT_HUSH_PS2	"> "
 #define CONFIG_SYS_CBSIZE		2048
 #define CONFIG_SYS_MAXARGS		64
 #define CONFIG_SYS_BARGSIZE		CONFIG_SYS_CBSIZE
diff --git a/include/configs/imx8mm_evk.h b/include/configs/imx8mm_evk.h
index 83521ad..92eb855 100644
--- a/include/configs/imx8mm_evk.h
+++ b/include/configs/imx8mm_evk.h
@@ -120,7 +120,6 @@
 #define CONFIG_MXC_UART_BASE		UART2_BASE_ADDR
 
 /* Monitor Command Prompt */
-#define CONFIG_SYS_PROMPT_HUSH_PS2	"> "
 #define CONFIG_SYS_CBSIZE		2048
 #define CONFIG_SYS_MAXARGS		64
 #define CONFIG_SYS_BARGSIZE		CONFIG_SYS_CBSIZE
diff --git a/include/configs/imx8mn_evk.h b/include/configs/imx8mn_evk.h
index a633308..cda8fc2 100644
--- a/include/configs/imx8mn_evk.h
+++ b/include/configs/imx8mn_evk.h
@@ -124,7 +124,6 @@
 #define CONFIG_MXC_UART_BASE		UART2_BASE_ADDR
 
 /* Monitor Command Prompt */
-#define CONFIG_SYS_PROMPT_HUSH_PS2	"> "
 #define CONFIG_SYS_CBSIZE		2048
 #define CONFIG_SYS_MAXARGS		64
 #define CONFIG_SYS_BARGSIZE		CONFIG_SYS_CBSIZE
diff --git a/include/configs/imx8mp_evk.h b/include/configs/imx8mp_evk.h
index 8253c6a..92091df 100644
--- a/include/configs/imx8mp_evk.h
+++ b/include/configs/imx8mp_evk.h
@@ -135,7 +135,6 @@
 #define CONFIG_MXC_UART_BASE		UART2_BASE_ADDR
 
 /* Monitor Command Prompt */
-#define CONFIG_SYS_PROMPT_HUSH_PS2	"> "
 #define CONFIG_SYS_CBSIZE		2048
 #define CONFIG_SYS_MAXARGS		64
 #define CONFIG_SYS_BARGSIZE CONFIG_SYS_CBSIZE
diff --git a/include/configs/imx8mq_evk.h b/include/configs/imx8mq_evk.h
index 3f9a3bc..96bfff7 100644
--- a/include/configs/imx8mq_evk.h
+++ b/include/configs/imx8mq_evk.h
@@ -175,7 +175,6 @@
 /* Monitor Command Prompt */
 #undef CONFIG_SYS_PROMPT
 #define CONFIG_SYS_PROMPT		"u-boot=> "
-#define CONFIG_SYS_PROMPT_HUSH_PS2	"> "
 #define CONFIG_SYS_CBSIZE		1024
 #define CONFIG_SYS_MAXARGS		64
 #define CONFIG_SYS_BARGSIZE		CONFIG_SYS_CBSIZE
diff --git a/include/configs/imx8mq_phanbell.h b/include/configs/imx8mq_phanbell.h
index e8b65a4..66c2c3a 100644
--- a/include/configs/imx8mq_phanbell.h
+++ b/include/configs/imx8mq_phanbell.h
@@ -169,7 +169,6 @@
 #define CONFIG_MXC_UART_BASE		UART1_BASE_ADDR
 
 /* Monitor Command Prompt */
-#define CONFIG_SYS_PROMPT_HUSH_PS2	"> "
 #define CONFIG_SYS_CBSIZE		1024
 #define CONFIG_SYS_MAXARGS		64
 #define CONFIG_SYS_BARGSIZE		CONFIG_SYS_CBSIZE
diff --git a/include/configs/km/keymile-common.h b/include/configs/km/keymile-common.h
index 851b13e..e084862 100644
--- a/include/configs/km/keymile-common.h
+++ b/include/configs/km/keymile-common.h
@@ -184,9 +184,9 @@
 	"cramfsloadfdt="						\
 		"cramfsload ${fdt_addr_r} "				\
 		"fdt_0x${IVM_BoardId}_0x${IVM_HWKey}.dtb\0"		\
-	"fdt_addr_r="__stringify(CONFIG_KM_FDT_ADDR) "\0"		\
+	"fdt_addr_r=" __stringify(CONFIG_KM_FDT_ADDR) "\0"		\
 	"init=/sbin/init-overlay.sh\0"					\
-	"load_addr_r="__stringify(CONFIG_KM_KERNEL_ADDR) "\0"		\
+	"load_addr_r=" __stringify(CONFIG_KM_KERNEL_ADDR) "\0"		\
 	"load=tftpboot ${load_addr_r} ${u-boot}\0"			\
 	"mtdids=" CONFIG_MTDIDS_DEFAULT "\0"					\
 	"mtdparts=" CONFIG_MTDPARTS_DEFAULT "\0"				\
diff --git a/include/configs/km/km_arm.h b/include/configs/km/km_arm.h
index 98e0ce1..29060fa 100644
--- a/include/configs/km/km_arm.h
+++ b/include/configs/km/km_arm.h
@@ -48,7 +48,7 @@
 		" boardid=0x${IVM_BoardId} hwkey=0x${IVM_HWKey}"
 
 #define CONFIG_KM_DEF_ENV_CPU						\
-	"u-boot="CONFIG_HOSTNAME "/u-boot.kwb\0"		\
+	"u-boot=" CONFIG_HOSTNAME "/u-boot.kwb\0"		\
 	CONFIG_KM_UPDATE_UBOOT						\
 	"set_fdthigh=setenv fdt_high ${kernelmem}\0"			\
 	"checkfdt="							\
diff --git a/include/configs/s5p4418_nanopi2.h b/include/configs/s5p4418_nanopi2.h
index 6dd1f3b..1e2180b 100644
--- a/include/configs/s5p4418_nanopi2.h
+++ b/include/configs/s5p4418_nanopi2.h
@@ -102,10 +102,6 @@
 /* Boot Argument Buffer Size */
 #define CONFIG_SYS_BARGSIZE		CONFIG_SYS_CBSIZE
 
-#ifdef CONFIG_HUSH_PARSER
-#define CONFIG_SYS_PROMPT_HUSH_PS2	"> "
-#endif
-
 /*-----------------------------------------------------------------------
  * Etc Command definition
  */
diff --git a/include/configs/verdin-imx8mm.h b/include/configs/verdin-imx8mm.h
index fd84054..4751bf5 100644
--- a/include/configs/verdin-imx8mm.h
+++ b/include/configs/verdin-imx8mm.h
@@ -98,7 +98,6 @@
 #define CONFIG_MXC_UART_BASE		UART1_BASE_ADDR
 
 /* Monitor Command Prompt */
-#define CONFIG_SYS_PROMPT_HUSH_PS2	"> "
 #define CONFIG_SYS_CBSIZE		SZ_2K
 #define CONFIG_SYS_MAXARGS		64
 #define CONFIG_SYS_BARGSIZE		CONFIG_SYS_CBSIZE
diff --git a/include/configs/xenguest_arm64.h b/include/configs/xenguest_arm64.h
index c44381e..d76ce13 100644
--- a/include/configs/xenguest_arm64.h
+++ b/include/configs/xenguest_arm64.h
@@ -27,7 +27,6 @@
 #define CONFIG_SYS_MALLOC_LEN         (32 * 1024 * 1024)
 
 /* Monitor Command Prompt */
-#define CONFIG_SYS_PROMPT_HUSH_PS2    "> "
 #define CONFIG_SYS_CBSIZE             1024
 #define CONFIG_SYS_MAXARGS            64
 #define CONFIG_SYS_BARGSIZE           CONFIG_SYS_CBSIZE
diff --git a/include/test/suites.h b/include/test/suites.h
index ab7b3bd..5c97846 100644
--- a/include/test/suites.h
+++ b/include/test/suites.h
@@ -38,6 +38,8 @@
 int do_ut_optee(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]);
 int do_ut_overlay(struct cmd_tbl *cmdtp, int flag, int argc,
 		  char *const argv[]);
+int do_ut_setexpr(struct cmd_tbl *cmdtp, int flag, int argc,
+		  char *const argv[]);
 int do_ut_str(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]);
 int do_ut_time(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]);
 int do_ut_unicode(struct cmd_tbl *cmdtp, int flag, int argc,
diff --git a/net/Kconfig b/net/Kconfig
index 1b3e420..c4b4dae 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -74,4 +74,18 @@
 	  before an ack response is required.
 	  The default TFTP implementation implies a window size of 1.
 
+config SERVERIP_FROM_PROXYDHCP
+	bool "Get serverip value from Proxy DHCP response"
+	help
+	  Allows bootfile config to be fetched from Proxy DHCP server
+		while IP is obtained from main DHCP server.
+
+config SERVERIP_FROM_PROXYDHCP_DELAY_MS
+	int "# of additional milliseconds to wait for ProxyDHCP response"
+	default 100
+	help
+	  Amount of additional time to wait for ProxyDHCP response after
+		receiving response from main DHCP server. Has no effect if
+		SERVERIP_FROM_PROXYDHCP is false.
+
 endif   # if NET
diff --git a/net/bootp.c b/net/bootp.c
index de3dab4..163af41e 100644
--- a/net/bootp.c
+++ b/net/bootp.c
@@ -146,10 +146,7 @@
 	return retval;
 }
 
-/*
- * Copy parameters of interest from BOOTP_REPLY/DHCP_OFFER packet
- */
-static void store_net_params(struct bootp_hdr *bp)
+static void store_bootp_params(struct bootp_hdr *bp)
 {
 #if !defined(CONFIG_BOOTP_SERVERIP)
 	struct in_addr tmp_ip;
@@ -183,6 +180,16 @@
 	if (*net_boot_file_name)
 		env_set("bootfile", net_boot_file_name);
 #endif
+}
+
+/*
+ * Copy parameters of interest from BOOTP_REPLY/DHCP_OFFER packet
+ */
+static void store_net_params(struct bootp_hdr *bp)
+{
+#if !defined(CONFIG_SERVERIP_FROM_PROXYDHCP)
+	store_bootp_params(bp);
+#endif
 	net_copy_ip(&net_ip, &bp->bp_yiaddr);
 }
 
@@ -1055,8 +1062,12 @@
 	debug("DHCPHandler: got DHCP packet: (src=%d, dst=%d, len=%d) state: "
 	      "%d\n", src, dest, len, dhcp_state);
 
-	if (net_read_ip(&bp->bp_yiaddr).s_addr == 0)
+	if (net_read_ip(&bp->bp_yiaddr).s_addr == 0) {
+#if defined(CONFIG_SERVERIP_FROM_PROXYDHCP)
+		store_bootp_params(bp);
+#endif
 		return;
+	}
 
 	switch (dhcp_state) {
 	case SELECTING:
@@ -1075,6 +1086,12 @@
 			dhcp_packet_process_options(bp);
 			efi_net_set_dhcp_ack(pkt, len);
 
+#if defined(CONFIG_SERVERIP_FROM_PROXYDHCP)
+			if (!net_server_ip.s_addr)
+				udelay(CONFIG_SERVERIP_FROM_PROXYDHCP_DELAY_MS *
+					1000);
+#endif	/* CONFIG_SERVERIP_FROM_PROXYDHCP */
+
 			debug("TRANSITIONING TO REQUESTING STATE\n");
 			dhcp_state = REQUESTING;
 
diff --git a/net/sntp.c b/net/sntp.c
index d5d5671..dac0f8c 100644
--- a/net/sntp.c
+++ b/net/sntp.c
@@ -57,18 +57,15 @@
 static void sntp_handler(uchar *pkt, unsigned dest, struct in_addr sip,
 			 unsigned src, unsigned len)
 {
-#ifdef CONFIG_TIMESTAMP
 	struct sntp_pkt_t *rpktp = (struct sntp_pkt_t *)pkt;
 	struct rtc_time tm;
 	ulong seconds;
-#endif
 
 	debug("%s\n", __func__);
 
 	if (dest != sntp_our_port)
 		return;
 
-#ifdef CONFIG_TIMESTAMP
 	/*
 	 * As the RTC's used in U-Boot support second resolution only
 	 * we simply ignore the sub-second field.
@@ -76,8 +73,7 @@
 	memcpy(&seconds, &rpktp->transmit_timestamp, sizeof(ulong));
 
 	rtc_to_tm(ntohl(seconds) - 2208988800UL + net_ntp_time_offset, &tm);
-#if defined(CONFIG_CMD_DATE)
-#  ifdef CONFIG_DM_RTC
+#ifdef CONFIG_DM_RTC
 	struct udevice *dev;
 	int ret;
 
@@ -86,14 +82,12 @@
 		printf("SNTP: cannot find RTC: err=%d\n", ret);
 	else
 		dm_rtc_set(dev, &tm);
-#  else
+#elif defined(CONFIG_CMD_DATE)
 	rtc_set(&tm);
-#  endif
 #endif
 	printf("Date: %4d-%02d-%02d Time: %2d:%02d:%02d\n",
 	       tm.tm_year, tm.tm_mon, tm.tm_mday,
 	       tm.tm_hour, tm.tm_min, tm.tm_sec);
-#endif
 
 	net_set_state(NETLOOP_SUCCESS);
 }
diff --git a/scripts/config_whitelist.txt b/scripts/config_whitelist.txt
index 8b4fcba..31acc36 100644
--- a/scripts/config_whitelist.txt
+++ b/scripts/config_whitelist.txt
@@ -3508,7 +3508,6 @@
 CONFIG_SYS_POST_WORD_ADDR
 CONFIG_SYS_PPC_DDR_WIMGE
 CONFIG_SYS_PQSPAR
-CONFIG_SYS_PROMPT_HUSH_PS2
 CONFIG_SYS_PSDPAR
 CONFIG_SYS_PSSR_VAL
 CONFIG_SYS_PTCPAR
diff --git a/test/cmd/Makefile b/test/cmd/Makefile
index 859dcda..b2f71af 100644
--- a/test/cmd/Makefile
+++ b/test/cmd/Makefile
@@ -4,3 +4,4 @@
 
 obj-y += mem.o
 obj-$(CONFIG_CMD_MEM_SEARCH) += mem_search.o
+obj-y += setexpr.o
diff --git a/test/cmd/setexpr.c b/test/cmd/setexpr.c
new file mode 100644
index 0000000..fd6d869
--- /dev/null
+++ b/test/cmd/setexpr.c
@@ -0,0 +1,384 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Tests for setexpr command
+ *
+ * Copyright 2020 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <common.h>
+#include <console.h>
+#include <mapmem.h>
+#include <dm/test.h>
+#include <test/suites.h>
+#include <test/ut.h>
+
+#define BUF_SIZE	0x100
+
+/* Declare a new mem test */
+#define SETEXPR_TEST(_name, _flags)	UNIT_TEST(_name, _flags, setexpr_test)
+
+/* Test 'setexpr' command with simply setting integers */
+static int setexpr_test_int(struct unit_test_state *uts)
+{
+	u8 *buf;
+
+	buf = map_sysmem(0, BUF_SIZE);
+	memset(buf, '\xff', BUF_SIZE);
+
+	/* byte */
+	buf[0x0] = 0x12;
+	ut_assertok(run_command("setexpr.b fred 0", 0));
+	ut_asserteq_str("0", env_get("fred"));
+	ut_assertok(run_command("setexpr.b fred *0", 0));
+	ut_asserteq_str("12", env_get("fred"));
+
+	/* 16-bit */
+	*(short *)buf = 0x2345;
+	ut_assertok(run_command("setexpr.w fred 0", 0));
+	ut_asserteq_str("0", env_get("fred"));
+	ut_assertok(run_command("setexpr.w fred *0", 0));
+	ut_asserteq_str("2345", env_get("fred"));
+
+	/* 32-bit */
+	*(u32 *)buf = 0x3456789a;
+	ut_assertok(run_command("setexpr.l fred 0", 0));
+	ut_asserteq_str("0", env_get("fred"));
+	ut_assertok(run_command("setexpr.l fred *0", 0));
+	ut_asserteq_str("3456789a", env_get("fred"));
+
+	/* 64-bit */
+	*(u64 *)buf = 0x456789abcdef0123;
+	ut_assertok(run_command("setexpr.q fred 0", 0));
+	ut_asserteq_str("0", env_get("fred"));
+	ut_assertok(run_command("setexpr.q fred *0", 0));
+	ut_asserteq_str("456789abcdef0123", env_get("fred"));
+
+	/* default */
+	ut_assertok(run_command("setexpr fred 0", 0));
+	ut_asserteq_str("0", env_get("fred"));
+	ut_assertok(run_command("setexpr fred *0", 0));
+	ut_asserteq_str("cdef0123", env_get("fred"));
+
+	unmap_sysmem(buf);
+
+	return 0;
+}
+SETEXPR_TEST(setexpr_test_int, UT_TESTF_CONSOLE_REC);
+
+/* Test 'setexpr' command with + operator */
+static int setexpr_test_plus(struct unit_test_state *uts)
+{
+	char *buf;
+
+	buf = map_sysmem(0, BUF_SIZE);
+	memset(buf, '\xff', BUF_SIZE);
+
+	/* byte */
+	buf[0x0] = 0x12;
+	buf[0x10] = 0x34;
+	ut_assertok(run_command("setexpr.b fred *0 + *10", 0));
+	ut_asserteq_str("46", env_get("fred"));
+
+	/* 16-bit */
+	*(short *)buf = 0x2345;
+	*(short *)(buf + 0x10) = 0xf012;
+	ut_assertok(run_command("setexpr.w fred *0 + *10", 0));
+	ut_asserteq_str("11357", env_get("fred"));
+
+	/* 32-bit */
+	*(u32 *)buf = 0x3456789a;
+	*(u32 *)(buf + 0x10) = 0xc3384235;
+	ut_assertok(run_command("setexpr.l fred *0 + *10", 0));
+	ut_asserteq_str("f78ebacf", env_get("fred"));
+
+	/* 64-bit */
+	*(u64 *)buf = 0x456789abcdef0123;
+	*(u64 *)(buf + 0x10) = 0x4987328372849283;
+	ut_assertok(run_command("setexpr.q fred *0 + *10", 0));
+	ut_asserteq_str("8eeebc2f407393a6", env_get("fred"));
+
+	/* default */
+	ut_assertok(run_command("setexpr fred *0 + *10", 0));
+	ut_asserteq_str("1407393a6", env_get("fred"));
+
+	unmap_sysmem(buf);
+
+	return 0;
+}
+SETEXPR_TEST(setexpr_test_plus, UT_TESTF_CONSOLE_REC);
+
+/* Test 'setexpr' command with other operators */
+static int setexpr_test_oper(struct unit_test_state *uts)
+{
+	char *buf;
+
+	buf = map_sysmem(0, BUF_SIZE);
+	memset(buf, '\xff', BUF_SIZE);
+
+	*(u32 *)buf = 0x1234;
+	*(u32 *)(buf + 0x10) = 0x560000;
+
+	/* Quote | to avoid confusing hush */
+	ut_assertok(run_command("setexpr fred *0 \"|\" *10", 0));
+	ut_asserteq_str("561234", env_get("fred"));
+
+	*(u32 *)buf = 0x561200;
+	*(u32 *)(buf + 0x10) = 0x1234;
+
+	/* Quote & to avoid confusing hush */
+	ut_assertok(run_command("setexpr.l fred *0 \"&\" *10", 0));
+	ut_asserteq_str("1200", env_get("fred"));
+
+	ut_assertok(run_command("setexpr.l fred *0 ^ *10", 0));
+	ut_asserteq_str("560034", env_get("fred"));
+
+	ut_assertok(run_command("setexpr.l fred *0 - *10", 0));
+	ut_asserteq_str("55ffcc", env_get("fred"));
+
+	ut_assertok(run_command("setexpr.l fred *0 * *10", 0));
+	ut_asserteq_str("61ebfa800", env_get("fred"));
+
+	ut_assertok(run_command("setexpr.l fred *0 / *10", 0));
+	ut_asserteq_str("4ba", env_get("fred"));
+
+	ut_assertok(run_command("setexpr.l fred *0 % *10", 0));
+	ut_asserteq_str("838", env_get("fred"));
+
+	unmap_sysmem(buf);
+
+	return 0;
+}
+SETEXPR_TEST(setexpr_test_oper, UT_TESTF_CONSOLE_REC);
+
+/* Test 'setexpr' command with regex */
+static int setexpr_test_regex(struct unit_test_state *uts)
+{
+	char *buf, *val;
+
+	buf = map_sysmem(0, BUF_SIZE);
+
+	/* Single substitution */
+	ut_assertok(run_command("setenv fred 'this is a test'", 0));
+	ut_assertok(run_command("setexpr fred sub is us", 0));
+	val = env_get("fred");
+	ut_asserteq_str("thus is a test", val);
+
+	/* Global substitution */
+	ut_assertok(run_command("setenv fred 'this is a test'", 0));
+	ut_assertok(run_command("setexpr fred gsub is us", 0));
+	val = env_get("fred");
+	ut_asserteq_str("thus us a test", val);
+
+	/* Global substitution */
+	ut_assertok(run_command("setenv fred 'this is a test'", 0));
+	ut_assertok(run_command("setenv mary 'this is a test'", 0));
+	ut_assertok(run_command("setexpr fred gsub is us \"${mary}\"", 0));
+	val = env_get("fred");
+	ut_asserteq_str("thus us a test", val);
+	val = env_get("mary");
+	ut_asserteq_str("this is a test", val);
+
+	unmap_sysmem(buf);
+
+	return 0;
+}
+SETEXPR_TEST(setexpr_test_regex, UT_TESTF_CONSOLE_REC);
+
+/* Test 'setexpr' command with regex replacement that expands the string */
+static int setexpr_test_regex_inc(struct unit_test_state *uts)
+{
+	char *buf, *val;
+
+	buf = map_sysmem(0, BUF_SIZE);
+
+	ut_assertok(run_command("setenv fred 'this is a test'", 0));
+	ut_assertok(run_command("setexpr fred gsub is much_longer_string", 0));
+	val = env_get("fred");
+	ut_asserteq_str("thmuch_longer_string much_longer_string a test", val);
+	unmap_sysmem(buf);
+
+	return 0;
+}
+SETEXPR_TEST(setexpr_test_regex_inc, UT_TESTF_CONSOLE_REC);
+
+/* Test setexpr_regex_sub() directly to check buffer usage */
+static int setexpr_test_sub(struct unit_test_state *uts)
+{
+	char *buf, *nbuf;
+	int i;
+
+	buf = map_sysmem(0, BUF_SIZE);
+	nbuf = map_sysmem(0x1000, BUF_SIZE);
+
+	/* Add a pattern so we can check the buffer limits */
+	memset(buf, '\xff', BUF_SIZE);
+	memset(nbuf, '\xff', BUF_SIZE);
+	for (i = BUF_SIZE; i < 0x1000; i++) {
+		buf[i] = i & 0xff;
+		nbuf[i] = i & 0xff;
+	}
+	strcpy(buf, "this is a test");
+
+	/*
+	 * This is a regression test, since a bug was found in the use of
+	 * memmove() in setexpr
+	 */
+	ut_assertok(setexpr_regex_sub(buf, BUF_SIZE, nbuf, BUF_SIZE, "is",
+				      "us it is longer", true));
+	ut_asserteq_str("thus it is longer us it is longer a test", buf);
+	for (i = BUF_SIZE; i < 0x1000; i++) {
+		ut_assertf(buf[i] == (char)i,
+			   "buf byte at %x should be %02x, got %02x)\n",
+			   i, i & 0xff, (u8)buf[i]);
+		ut_assertf(nbuf[i] == (char)i,
+			   "nbuf byte at %x should be %02x, got %02x)\n",
+			   i, i & 0xff, (u8)nbuf[i]);
+	}
+
+	unmap_sysmem(buf);
+
+	return 0;
+}
+SETEXPR_TEST(setexpr_test_sub, UT_TESTF_CONSOLE_REC);
+
+/* Test setexpr_regex_sub() with back references */
+static int setexpr_test_backref(struct unit_test_state *uts)
+{
+	char *buf, *nbuf;
+	int i;
+
+	buf = map_sysmem(0, BUF_SIZE);
+	nbuf = map_sysmem(0x1000, BUF_SIZE);
+
+	/* Add a pattern so we can check the buffer limits */
+	memset(buf, '\xff', BUF_SIZE);
+	memset(nbuf, '\xff', BUF_SIZE);
+	for (i = BUF_SIZE; i < 0x1000; i++) {
+		buf[i] = i & 0xff;
+		nbuf[i] = i & 0xff;
+	}
+	strcpy(buf, "this is surely a test is it? yes this is indeed a test");
+
+	/*
+	 * This is a regression test, since a bug was found in the use of
+	 * memmove() in setexpr
+	 */
+	ut_assertok(setexpr_regex_sub(buf, BUF_SIZE, nbuf, BUF_SIZE,
+				      "(this) (is) (surely|indeed)",
+				      "us \\1 \\2 \\3!", true));
+	ut_asserteq_str("us this is surely! a test is it? yes us this is indeed! a test",
+			buf);
+
+	/* The following checks fail at present due to a bug in setexpr */
+	return 0;
+	for (i = BUF_SIZE; i < 0x1000; i++) {
+		ut_assertf(buf[i] == (char)i,
+			   "buf byte at %x should be %02x, got %02x)\n",
+			   i, i & 0xff, (u8)buf[i]);
+		ut_assertf(nbuf[i] == (char)i,
+			   "nbuf byte at %x should be %02x, got %02x)\n",
+			   i, i & 0xff, (u8)nbuf[i]);
+	}
+
+	unmap_sysmem(buf);
+
+	return 0;
+}
+SETEXPR_TEST(setexpr_test_backref, UT_TESTF_CONSOLE_REC);
+
+/* Test 'setexpr' command with setting strings */
+static int setexpr_test_str(struct unit_test_state *uts)
+{
+	ulong start_mem;
+	char *buf;
+
+	buf = map_sysmem(0, BUF_SIZE);
+	memset(buf, '\xff', BUF_SIZE);
+
+	/*
+	 * Set 'fred' to the same length as we expect to get below, to avoid a
+	 * new allocation in 'setexpr'. That way we can check for memory leaks.
+	 */
+	ut_assertok(env_set("fred", "x"));
+	start_mem = ut_check_free();
+	strcpy(buf, "hello");
+	ut_asserteq(1, run_command("setexpr.s fred 0", 0));
+	ut_assertok(ut_check_delta(start_mem));
+
+	start_mem = ut_check_free();
+	ut_assertok(env_set("fred", "12345"));
+	ut_assertok(run_command("setexpr.s fred *0", 0));
+	ut_asserteq_str("hello", env_get("fred"));
+	ut_assertok(ut_check_delta(start_mem));
+
+	unmap_sysmem(buf);
+
+	return 0;
+}
+SETEXPR_TEST(setexpr_test_str, UT_TESTF_CONSOLE_REC);
+
+
+/* Test 'setexpr' command with concatenating strings */
+static int setexpr_test_str_oper(struct unit_test_state *uts)
+{
+	ulong start_mem;
+	char *buf;
+
+	buf = map_sysmem(0, BUF_SIZE);
+	memset(buf, '\xff', BUF_SIZE);
+	strcpy(buf, "hello");
+	strcpy(buf + 0x10, " there");
+
+	ut_assertok(console_record_reset_enable());
+	start_mem = ut_check_free();
+	ut_asserteq(1, run_command("setexpr.s fred *0 * *10", 0));
+	ut_assertok(ut_check_delta(start_mem));
+	ut_assert_nextline("invalid op");
+	ut_assert_console_end();
+
+	/*
+	 * Set 'fred' to the same length as we expect to get below, to avoid a
+	 * new allocation in 'setexpr'. That way we can check for memory leaks.
+	 */
+	ut_assertok(env_set("fred", "12345012345"));
+	start_mem = ut_check_free();
+	ut_assertok(run_command("setexpr.s fred *0 + *10", 0));
+	ut_asserteq_str("hello there", env_get("fred"));
+	ut_assertok(ut_check_delta(start_mem));
+
+	unmap_sysmem(buf);
+
+	return 0;
+}
+SETEXPR_TEST(setexpr_test_str_oper, UT_TESTF_CONSOLE_REC);
+
+/* Test 'setexpr' command with a string that is too long */
+static int setexpr_test_str_long(struct unit_test_state *uts)
+{
+	const int size = 128 << 10;  /* setexpr strings are a max of 64KB */
+	char *buf, *val;
+
+	buf = map_sysmem(0, size);
+	memset(buf, 'a', size);
+
+	/* String should be truncated to 64KB */
+	ut_assertok(run_command("setexpr.s fred *0", 0));
+	val = env_get("fred");
+	ut_asserteq(64 << 10, strlen(val));
+
+	unmap_sysmem(buf);
+
+	return 0;
+}
+SETEXPR_TEST(setexpr_test_str_long, UT_TESTF_CONSOLE_REC);
+
+int do_ut_setexpr(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+	struct unit_test *tests = ll_entry_start(struct unit_test,
+						 setexpr_test);
+	const int n_ents = ll_entry_count(struct unit_test, setexpr_test);
+
+	return cmd_ut_category("cmd_setexpr", "cmd_mem_", tests, n_ents, argc,
+			       argv);
+}
diff --git a/test/cmd_ut.c b/test/cmd_ut.c
index 8f0bc68..f79109e 100644
--- a/test/cmd_ut.c
+++ b/test/cmd_ut.c
@@ -75,6 +75,8 @@
 	U_BOOT_CMD_MKENT(log, CONFIG_SYS_MAXARGS, 1, do_ut_log, "", ""),
 #endif
 	U_BOOT_CMD_MKENT(mem, CONFIG_SYS_MAXARGS, 1, do_ut_mem, "", ""),
+	U_BOOT_CMD_MKENT(setexpr, CONFIG_SYS_MAXARGS, 1, do_ut_setexpr, "",
+			 ""),
 #ifdef CONFIG_UT_TIME
 	U_BOOT_CMD_MKENT(time, CONFIG_SYS_MAXARGS, 1, do_ut_time, "", ""),
 #endif
@@ -153,6 +155,7 @@
 #ifdef CONFIG_UT_OVERLAY
 	"ut overlay [test-name]\n"
 #endif
+	"ut setexpr [test-name] - test setexpr command\n"
 #ifdef CONFIG_SANDBOX
 	"ut str - Basic test of string functions\n"
 #endif