Merge tag 'mips-pull-2020-10-07' of https://gitlab.denx.de/u-boot/custodians/u-boot-mips

- mips: octeon: add support for DDR4 memory controller
- mips: octeon: add support for DWC3 USB
- mips: octeon: add support for booting Linux
diff --git a/MAINTAINERS b/MAINTAINERS
index 85babd1..ed5e354 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -959,6 +959,7 @@
 M:	Simon Glass <sjg@chromium.org>
 S:	Maintained
 F:	arch/sandbox/
+F:	doc/arch/sandbox.rst
 
 SH
 M:	Marek Vasut <marek.vasut+renesas@gmail.com>
diff --git a/arch/sandbox/cpu/sdl.c b/arch/sandbox/cpu/sdl.c
index 6416cab..7dc3dab 100644
--- a/arch/sandbox/cpu/sdl.c
+++ b/arch/sandbox/cpu/sdl.c
@@ -172,33 +172,7 @@
 }
 
 static const unsigned short sdl_to_keycode[SDL_NUM_SCANCODES] = {
-	[SDL_SCANCODE_A]	= KEY_A,
-	[SDL_SCANCODE_B]	= KEY_B,
-	[SDL_SCANCODE_C]	= KEY_C,
-	[SDL_SCANCODE_D]	= KEY_D,
-	[SDL_SCANCODE_E]	= KEY_E,
-	[SDL_SCANCODE_F]	= KEY_F,
-	[SDL_SCANCODE_G]	= KEY_G,
-	[SDL_SCANCODE_H]	= KEY_H,
-	[SDL_SCANCODE_I]	= KEY_I,
-	[SDL_SCANCODE_J]	= KEY_J,
-	[SDL_SCANCODE_K]	= KEY_K,
-	[SDL_SCANCODE_L]	= KEY_L,
-	[SDL_SCANCODE_M]	= KEY_M,
-	[SDL_SCANCODE_N]	= KEY_N,
-	[SDL_SCANCODE_O]	= KEY_O,
-	[SDL_SCANCODE_P]	= KEY_P,
-	[SDL_SCANCODE_Q]	= KEY_Q,
-	[SDL_SCANCODE_R]	= KEY_R,
-	[SDL_SCANCODE_S]	= KEY_S,
-	[SDL_SCANCODE_T]	= KEY_T,
-	[SDL_SCANCODE_U]	= KEY_U,
-	[SDL_SCANCODE_V]	= KEY_V,
-	[SDL_SCANCODE_W]	= KEY_W,
-	[SDL_SCANCODE_X]	= KEY_X,
-	[SDL_SCANCODE_Y]	= KEY_Y,
-	[SDL_SCANCODE_Z]	= KEY_Z,
-
+	[SDL_SCANCODE_ESCAPE]	= KEY_ESC,
 	[SDL_SCANCODE_1]	= KEY_1,
 	[SDL_SCANCODE_2]	= KEY_2,
 	[SDL_SCANCODE_3]	= KEY_3,
@@ -209,25 +183,53 @@
 	[SDL_SCANCODE_8]	= KEY_8,
 	[SDL_SCANCODE_9]	= KEY_9,
 	[SDL_SCANCODE_0]	= KEY_0,
-
-	[SDL_SCANCODE_RETURN]	= KEY_ENTER,
-	[SDL_SCANCODE_ESCAPE]	= KEY_ESC,
-	[SDL_SCANCODE_BACKSPACE]	= KEY_BACKSPACE,
-	[SDL_SCANCODE_TAB]	= KEY_TAB,
-	[SDL_SCANCODE_SPACE]	= KEY_SPACE,
-
 	[SDL_SCANCODE_MINUS]	= KEY_MINUS,
 	[SDL_SCANCODE_EQUALS]	= KEY_EQUAL,
-	[SDL_SCANCODE_BACKSLASH]	= KEY_BACKSLASH,
+	[SDL_SCANCODE_BACKSPACE]	= KEY_BACKSPACE,
+	[SDL_SCANCODE_TAB]	= KEY_TAB,
+	[SDL_SCANCODE_Q]	= KEY_Q,
+	[SDL_SCANCODE_W]	= KEY_W,
+	[SDL_SCANCODE_E]	= KEY_E,
+	[SDL_SCANCODE_R]	= KEY_R,
+	[SDL_SCANCODE_T]	= KEY_T,
+	[SDL_SCANCODE_Y]	= KEY_Y,
+	[SDL_SCANCODE_U]	= KEY_U,
+	[SDL_SCANCODE_I]	= KEY_I,
+	[SDL_SCANCODE_O]	= KEY_O,
+	[SDL_SCANCODE_P]	= KEY_P,
+	[SDL_SCANCODE_LEFTBRACKET]	= KEY_LEFTBRACE,
+	[SDL_SCANCODE_RIGHTBRACKET]	= KEY_RIGHTBRACE,
+	[SDL_SCANCODE_RETURN]	= KEY_ENTER,
+	[SDL_SCANCODE_LCTRL]	= KEY_LEFTCTRL,
+	[SDL_SCANCODE_A]	= KEY_A,
+	[SDL_SCANCODE_S]	= KEY_S,
+	[SDL_SCANCODE_D]	= KEY_D,
+	[SDL_SCANCODE_F]	= KEY_F,
+	[SDL_SCANCODE_G]	= KEY_G,
+	[SDL_SCANCODE_H]	= KEY_H,
+	[SDL_SCANCODE_J]	= KEY_J,
+	[SDL_SCANCODE_K]	= KEY_K,
+	[SDL_SCANCODE_L]	= KEY_L,
 	[SDL_SCANCODE_SEMICOLON]	= KEY_SEMICOLON,
 	[SDL_SCANCODE_APOSTROPHE]	= KEY_APOSTROPHE,
 	[SDL_SCANCODE_GRAVE]	= KEY_GRAVE,
+	[SDL_SCANCODE_LSHIFT]	= KEY_LEFTSHIFT,
+	[SDL_SCANCODE_BACKSLASH]	= KEY_BACKSLASH,
+	[SDL_SCANCODE_Z]	= KEY_Z,
+	[SDL_SCANCODE_X]	= KEY_X,
+	[SDL_SCANCODE_C]	= KEY_C,
+	[SDL_SCANCODE_V]	= KEY_V,
+	[SDL_SCANCODE_B]	= KEY_B,
+	[SDL_SCANCODE_N]	= KEY_N,
+	[SDL_SCANCODE_M]	= KEY_M,
 	[SDL_SCANCODE_COMMA]	= KEY_COMMA,
 	[SDL_SCANCODE_PERIOD]	= KEY_DOT,
 	[SDL_SCANCODE_SLASH]	= KEY_SLASH,
-
+	[SDL_SCANCODE_RSHIFT]	= KEY_RIGHTSHIFT,
+	[SDL_SCANCODE_KP_MULTIPLY] = KEY_KPASTERISK,
+	[SDL_SCANCODE_LALT]	= KEY_LEFTALT,
+	[SDL_SCANCODE_SPACE]	= KEY_SPACE,
 	[SDL_SCANCODE_CAPSLOCK]	= KEY_CAPSLOCK,
-
 	[SDL_SCANCODE_F1]	= KEY_F1,
 	[SDL_SCANCODE_F2]	= KEY_F2,
 	[SDL_SCANCODE_F3]	= KEY_F3,
@@ -238,45 +240,65 @@
 	[SDL_SCANCODE_F8]	= KEY_F8,
 	[SDL_SCANCODE_F9]	= KEY_F9,
 	[SDL_SCANCODE_F10]	= KEY_F10,
-	[SDL_SCANCODE_F11]	= KEY_F11,
-	[SDL_SCANCODE_F12]	= KEY_F12,
-
-	[SDL_SCANCODE_PRINTSCREEN]	= KEY_PRINT,
-	[SDL_SCANCODE_SCROLLLOCK]	= KEY_SCROLLLOCK,
-	[SDL_SCANCODE_PAUSE]	= KEY_PAUSE,
-	[SDL_SCANCODE_INSERT]	= KEY_INSERT,
-	[SDL_SCANCODE_HOME]	= KEY_HOME,
-	[SDL_SCANCODE_PAGEUP]	= KEY_PAGEUP,
-	[SDL_SCANCODE_DELETE]	= KEY_DELETE,
-	[SDL_SCANCODE_END]	= KEY_END,
-	[SDL_SCANCODE_PAGEDOWN]	= KEY_PAGEDOWN,
-	[SDL_SCANCODE_RIGHT]	= KEY_RIGHT,
-	[SDL_SCANCODE_LEFT]	= KEY_LEFT,
-	[SDL_SCANCODE_DOWN]	= KEY_DOWN,
-	[SDL_SCANCODE_UP]	= KEY_UP,
-
 	[SDL_SCANCODE_NUMLOCKCLEAR]	= KEY_NUMLOCK,
-	[SDL_SCANCODE_KP_DIVIDE]	= KEY_KPSLASH,
-	[SDL_SCANCODE_KP_MULTIPLY]	= KEY_KPASTERISK,
+	[SDL_SCANCODE_SCROLLLOCK]	= KEY_SCROLLLOCK,
+	[SDL_SCANCODE_KP_7]	= KEY_KP7,
+	[SDL_SCANCODE_KP_8]	= KEY_KP8,
+	[SDL_SCANCODE_KP_9]	= KEY_KP9,
 	[SDL_SCANCODE_KP_MINUS]	= KEY_KPMINUS,
+	[SDL_SCANCODE_KP_4]	= KEY_KP4,
+	[SDL_SCANCODE_KP_5]	= KEY_KP5,
+	[SDL_SCANCODE_KP_6]	= KEY_KP6,
 	[SDL_SCANCODE_KP_PLUS]	= KEY_KPPLUS,
-	[SDL_SCANCODE_KP_ENTER]	= KEY_KPENTER,
 	[SDL_SCANCODE_KP_1]	= KEY_KP1,
 	[SDL_SCANCODE_KP_2]	= KEY_KP2,
 	[SDL_SCANCODE_KP_3]	= KEY_KP3,
-	[SDL_SCANCODE_KP_4]	= KEY_KP4,
-	[SDL_SCANCODE_KP_5]	= KEY_KP5,
-	[SDL_SCANCODE_KP_6]	= KEY_KP6,
-	[SDL_SCANCODE_KP_7]	= KEY_KP7,
-	[SDL_SCANCODE_KP_8]	= KEY_KP8,
-	[SDL_SCANCODE_KP_9]	= KEY_KP9,
 	[SDL_SCANCODE_KP_0]	= KEY_KP0,
 	[SDL_SCANCODE_KP_PERIOD]	= KEY_KPDOT,
-
-	[SDL_SCANCODE_KP_EQUALS]	= KEY_KPEQUAL,
-	[SDL_SCANCODE_KP_COMMA]	= KEY_KPCOMMA,
-
+	/* key 84 does not exist linux_input.h */
+	[SDL_SCANCODE_LANG5]	=  KEY_ZENKAKUHANKAKU,
+	[SDL_SCANCODE_NONUSBACKSLASH]	= KEY_102ND,
+	[SDL_SCANCODE_F11]	= KEY_F11,
+	[SDL_SCANCODE_F12]	= KEY_F12,
+	[SDL_SCANCODE_INTERNATIONAL1]	= KEY_RO,
+	[SDL_SCANCODE_LANG3]	= KEY_KATAKANA,
+	[SDL_SCANCODE_LANG4]	= KEY_HIRAGANA,
+	[SDL_SCANCODE_INTERNATIONAL4] = KEY_HENKAN,
+	[SDL_SCANCODE_INTERNATIONAL2] = KEY_KATAKANAHIRAGANA,
+	[SDL_SCANCODE_INTERNATIONAL5] = KEY_MUHENKAN,
+	/* [SDL_SCANCODE_INTERNATIONAL5] -> [KEY_KPJPCOMMA] */
+	[SDL_SCANCODE_KP_ENTER]	= KEY_KPENTER,
+	[SDL_SCANCODE_RCTRL]	= KEY_RIGHTCTRL,
+	[SDL_SCANCODE_KP_DIVIDE] = KEY_KPSLASH,
 	[SDL_SCANCODE_SYSREQ]	= KEY_SYSRQ,
+	[SDL_SCANCODE_RALT]	= KEY_RIGHTALT,
+	/* KEY_LINEFEED */
+	[SDL_SCANCODE_HOME]	= KEY_HOME,
+	[SDL_SCANCODE_UP]	= KEY_UP,
+	[SDL_SCANCODE_PAGEUP]	= KEY_PAGEUP,
+	[SDL_SCANCODE_LEFT]	= KEY_LEFT,
+	[SDL_SCANCODE_RIGHT]	= KEY_RIGHT,
+	[SDL_SCANCODE_END]	= KEY_END,
+	[SDL_SCANCODE_DOWN]	= KEY_DOWN,
+	[SDL_SCANCODE_PAGEDOWN]	= KEY_PAGEDOWN,
+	[SDL_SCANCODE_INSERT]	= KEY_INSERT,
+	[SDL_SCANCODE_DELETE]	= KEY_DELETE,
+	/* KEY_MACRO */
+	[SDL_SCANCODE_MUTE]	= KEY_MUTE,
+	[SDL_SCANCODE_VOLUMEDOWN]	= KEY_VOLUMEDOWN,
+	[SDL_SCANCODE_VOLUMEUP]	= KEY_VOLUMEUP,
+	[SDL_SCANCODE_POWER]	= KEY_POWER,
+	[SDL_SCANCODE_KP_EQUALS]	= KEY_KPEQUAL,
+	[SDL_SCANCODE_KP_PLUSMINUS]	= KEY_KPPLUSMINUS,
+	[SDL_SCANCODE_PAUSE]	= KEY_PAUSE,
+	/* KEY_SCALE */
+	[SDL_SCANCODE_KP_COMMA] = KEY_KPCOMMA,
+	[SDL_SCANCODE_LANG1]	= KEY_HANGUEL,
+	[SDL_SCANCODE_LANG2]	= KEY_HANJA,
+	[SDL_SCANCODE_INTERNATIONAL3]	= KEY_YEN,
+	[SDL_SCANCODE_LGUI]	= KEY_LEFTMETA,
+	[SDL_SCANCODE_RGUI]	= KEY_RIGHTMETA,
+	[SDL_SCANCODE_APPLICATION] = KEY_COMPOSE,
 };
 
 int sandbox_sdl_scan_keys(int key[], int max_keys)
diff --git a/arch/sandbox/dts/cros-ec-keyboard.dtsi b/arch/sandbox/dts/cros-ec-keyboard.dtsi
index 9c7fb0a..d885a5e 100644
--- a/arch/sandbox/dts/cros-ec-keyboard.dtsi
+++ b/arch/sandbox/dts/cros-ec-keyboard.dtsi
@@ -1,12 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0-only
 /*
  * Keyboard dts fragment for devices that use cros-ec-keyboard
  *
  * Copyright (c) 2014 Google, Inc
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
-*/
+ * This file is taken from Linux
+ * arch/arm/boot/dts/cros-ec-keyboard.dtsi.
+ *
+ * A duplicate KEY_BACKSLASH key had to be removed.
+ */
 
 #include <dt-bindings/input/input.h>
 
@@ -22,6 +24,7 @@
 			MATRIX_KEY(0x00, 0x02, KEY_F1)
 			MATRIX_KEY(0x00, 0x03, KEY_B)
 			MATRIX_KEY(0x00, 0x04, KEY_F10)
+			MATRIX_KEY(0x00, 0x05, KEY_RO)
 			MATRIX_KEY(0x00, 0x06, KEY_N)
 			MATRIX_KEY(0x00, 0x08, KEY_EQUAL)
 			MATRIX_KEY(0x00, 0x0a, KEY_RIGHTALT)
@@ -34,6 +37,7 @@
 			MATRIX_KEY(0x01, 0x08, KEY_APOSTROPHE)
 			MATRIX_KEY(0x01, 0x09, KEY_F9)
 			MATRIX_KEY(0x01, 0x0b, KEY_BACKSPACE)
+			MATRIX_KEY(0x01, 0x0c, KEY_HENKAN)
 
 			MATRIX_KEY(0x02, 0x00, KEY_LEFTCTRL)
 			MATRIX_KEY(0x02, 0x01, KEY_TAB)
@@ -45,6 +49,7 @@
 			MATRIX_KEY(0x02, 0x07, KEY_102ND)
 			MATRIX_KEY(0x02, 0x08, KEY_LEFTBRACE)
 			MATRIX_KEY(0x02, 0x09, KEY_F8)
+			MATRIX_KEY(0x02, 0x0a, KEY_YEN)
 
 			MATRIX_KEY(0x03, 0x01, KEY_GRAVE)
 			MATRIX_KEY(0x03, 0x02, KEY_F2)
@@ -52,7 +57,9 @@
 			MATRIX_KEY(0x03, 0x04, KEY_F5)
 			MATRIX_KEY(0x03, 0x06, KEY_6)
 			MATRIX_KEY(0x03, 0x08, KEY_MINUS)
+			MATRIX_KEY(0x03, 0x09, KEY_F13)
 			MATRIX_KEY(0x03, 0x0b, KEY_BACKSLASH)
+			MATRIX_KEY(0x03, 0x0c, KEY_MUHENKAN)
 
 			MATRIX_KEY(0x04, 0x00, KEY_RIGHTCTRL)
 			MATRIX_KEY(0x04, 0x01, KEY_A)
@@ -63,7 +70,10 @@
 			MATRIX_KEY(0x04, 0x06, KEY_J)
 			MATRIX_KEY(0x04, 0x08, KEY_SEMICOLON)
 			MATRIX_KEY(0x04, 0x09, KEY_L)
-			MATRIX_KEY(0x04, 0x0a, KEY_BACKSLASH)
+			/*
+			 * Do not map any key twice
+			 * MATRIX_KEY(0x04, 0x0a, KEY_BACKSLASH)
+			 */
 			MATRIX_KEY(0x04, 0x0b, KEY_ENTER)
 
 			MATRIX_KEY(0x05, 0x01, KEY_Z)
diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts
index e6da47d..cc37280 100644
--- a/arch/sandbox/dts/test.dts
+++ b/arch/sandbox/dts/test.dts
@@ -131,6 +131,7 @@
 		str-value = "test string";
 		interrupts-extended = <&irq 3 0>;
 		acpi,name = "GHIJ";
+		phandle-value = <&gpio_c 10>, <0xFFFFFFFF 20>, <&gpio_a 30>;
 	};
 
 	junk {
diff --git a/arch/x86/cpu/intel_common/acpi.c b/arch/x86/cpu/intel_common/acpi.c
index a4d5fbd3..4496bbf 100644
--- a/arch/x86/cpu/intel_common/acpi.c
+++ b/arch/x86/cpu/intel_common/acpi.c
@@ -198,7 +198,7 @@
 	struct acpi_global_nvs *gnvs;
 	int ret;
 
-	ret = bloblist_ensure_size(BLOBLISTT_ACPI_GNVS, sizeof(*gnvs),
+	ret = bloblist_ensure_size(BLOBLISTT_ACPI_GNVS, sizeof(*gnvs), 0,
 				   (void **)&gnvs);
 	if (ret)
 		return log_msg_ret("bloblist", ret);
diff --git a/arch/x86/cpu/intel_common/intel_opregion.c b/arch/x86/cpu/intel_common/intel_opregion.c
index 4e6c64d..c95ae04 100644
--- a/arch/x86/cpu/intel_common/intel_opregion.c
+++ b/arch/x86/cpu/intel_common/intel_opregion.c
@@ -108,7 +108,7 @@
 		struct optionrom_vbt *ext_vbt;
 
 		ret = bloblist_ensure_size(BLOBLISTT_INTEL_VBT,
-					   vbt->hdr_vbt_size,
+					   vbt->hdr_vbt_size, 0,
 					   (void **)&ext_vbt);
 		if (ret) {
 			log_err("GMA: Unable to add Ext VBT to bloblist\n");
diff --git a/board/st/stm32mp1/stm32mp1.c b/board/st/stm32mp1/stm32mp1.c
index 3b677d3..03a19af 100644
--- a/board/st/stm32mp1/stm32mp1.c
+++ b/board/st/stm32mp1/stm32mp1.c
@@ -314,7 +314,7 @@
 	 * for each of them
 	 */
 	adc_count = ofnode_count_phandle_with_args(node, "st,adc_usb_pd",
-						   "#io-channel-cells");
+						   "#io-channel-cells", 0);
 	if (adc_count < 0) {
 		if (adc_count == -ENOENT)
 			return 0;
diff --git a/cmd/Kconfig b/cmd/Kconfig
index 999b6cf..b25d2b0 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -631,6 +631,15 @@
 	  Compute binary operations (xor, or, and) of byte arrays of arbitrary
 	  size from memory and store the result in memory or the environment.
 
+config CMD_BLOBLIST
+	bool "bloblist"
+	default y if BLOBLIST
+	help
+	  Show information about the bloblist, a collection of binary blobs
+	  held in memory that persist between SPL and U-Boot. In the case of
+	  x86 devices the bloblist can be used to hold ACPI tables so that they
+	  remain available in memory.
+
 config CMD_CRC32
 	bool "crc32"
 	default y
diff --git a/cmd/Makefile b/cmd/Makefile
index c7a08ed..015b837 100644
--- a/cmd/Makefile
+++ b/cmd/Makefile
@@ -24,6 +24,7 @@
 obj-$(CONFIG_CMD_BEDBUG) += bedbug.o
 obj-$(CONFIG_CMD_BIND) += bind.o
 obj-$(CONFIG_CMD_BINOP) += binop.o
+obj-$(CONFIG_CMD_BLOBLIST) += bloblist.o
 obj-$(CONFIG_CMD_BLOCK_CACHE) += blkcache.o
 obj-$(CONFIG_CMD_BMP) += bmp.o
 obj-$(CONFIG_CMD_BOOTCOUNT) += bootcount.o
diff --git a/cmd/bloblist.c b/cmd/bloblist.c
new file mode 100644
index 0000000..bb2e682
--- /dev/null
+++ b/cmd/bloblist.c
@@ -0,0 +1,37 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Command-line access to bloblist features
+ *
+ * Copyright 2020 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <common.h>
+#include <bloblist.h>
+#include <command.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static int do_bloblist_info(struct cmd_tbl *cmdtp, int flag, int argc,
+			    char *const argv[])
+{
+	bloblist_show_stats();
+
+	return 0;
+}
+
+static int do_bloblist_list(struct cmd_tbl *cmdtp, int flag, int argc,
+			    char *const argv[])
+{
+	bloblist_show_list();
+
+	return 0;
+}
+
+static char bloblist_help_text[] =
+	"info   - show information about the bloblist\n"
+	"bloblist list   - list blobs in the bloblist";
+
+U_BOOT_CMD_WITH_SUBCMDS(bloblist, "Bloblists", bloblist_help_text,
+	U_BOOT_SUBCMD_MKENT(info, 1, 1, do_bloblist_info),
+	U_BOOT_SUBCMD_MKENT(list, 1, 1, do_bloblist_list));
diff --git a/common/bloblist.c b/common/bloblist.c
index 9950195..33b5862 100644
--- a/common/bloblist.c
+++ b/common/bloblist.c
@@ -11,17 +11,47 @@
 #include <spl.h>
 #include <u-boot/crc.h>
 
+/*
+ * A bloblist is a single contiguous chunk of memory with a header
+ * (struct bloblist_hdr) and a number of blobs in it.
+ *
+ * Each blob starts on a BLOBLIST_ALIGN boundary relative to the start of the
+ * bloblist and consists of a struct bloblist_rec, some padding to the required
+ * alignment for the blog and then the actual data. The padding ensures that the
+ * start address of the data in each blob is aligned as required. Note that
+ * each blob's *data* is aligned to BLOBLIST_ALIGN regardless of the alignment
+ * of the bloblist itself or the blob header.
+ *
+ * So far, only BLOBLIST_ALIGN alignment is supported.
+ */
+
 DECLARE_GLOBAL_DATA_PTR;
 
-struct bloblist_rec *bloblist_first_blob(struct bloblist_hdr *hdr)
+static const char *const tag_name[] = {
+	[BLOBLISTT_NONE]		= "(none)",
+	[BLOBLISTT_EC_HOSTEVENT]	= "EC host event",
+	[BLOBLISTT_SPL_HANDOFF]		= "SPL hand-off",
+	[BLOBLISTT_VBOOT_CTX]		= "Chrome OS vboot context",
+	[BLOBLISTT_VBOOT_HANDOFF]	= "Chrome OS vboot hand-off",
+};
+
+const char *bloblist_tag_name(enum bloblist_tag_t tag)
+{
+	if (tag < 0 || tag >= BLOBLISTT_COUNT)
+		return "invalid";
+
+	return tag_name[tag];
+}
+
+static struct bloblist_rec *bloblist_first_blob(struct bloblist_hdr *hdr)
 {
 	if (hdr->alloced <= hdr->hdr_size)
 		return NULL;
 	return (struct bloblist_rec *)((void *)hdr + hdr->hdr_size);
 }
 
-struct bloblist_rec *bloblist_next_blob(struct bloblist_hdr *hdr,
-					struct bloblist_rec *rec)
+static struct bloblist_rec *bloblist_next_blob(struct bloblist_hdr *hdr,
+					       struct bloblist_rec *rec)
 {
 	ulong offset;
 
@@ -53,13 +83,25 @@
 	return NULL;
 }
 
-static int bloblist_addrec(uint tag, int size, struct bloblist_rec **recp)
+static int bloblist_addrec(uint tag, int size, int align,
+			   struct bloblist_rec **recp)
 {
 	struct bloblist_hdr *hdr = gd->bloblist;
 	struct bloblist_rec *rec;
-	int new_alloced;
+	int data_start, new_alloced;
+
+	if (!align)
+		align = BLOBLIST_ALIGN;
 
-	new_alloced = hdr->alloced + sizeof(*rec) + ALIGN(size, BLOBLIST_ALIGN);
+	/* Figure out where the new data will start */
+	data_start = map_to_sysmem(hdr) + hdr->alloced + sizeof(*rec);
+
+	/* Align the address and then calculate the offset from ->alloced */
+	data_start = ALIGN(data_start, align) - map_to_sysmem(hdr);
+
+	/* Calculate the new allocated total */
+	new_alloced = data_start + ALIGN(size, align);
+
 	if (new_alloced >= hdr->size) {
 		log(LOGC_BLOBLIST, LOGL_ERR,
 		    "Failed to allocate %x bytes size=%x, need size=%x\n",
@@ -67,21 +109,23 @@
 		return log_msg_ret("bloblist add", -ENOSPC);
 	}
 	rec = (void *)hdr + hdr->alloced;
-	hdr->alloced = new_alloced;
 
 	rec->tag = tag;
-	rec->hdr_size = sizeof(*rec);
+	rec->hdr_size = data_start - hdr->alloced;
 	rec->size = size;
 	rec->spare = 0;
 
 	/* Zero the record data */
-	memset(rec + 1, '\0', rec->size);
+	memset((void *)rec + rec->hdr_size, '\0', rec->size);
+
+	hdr->alloced = new_alloced;
 	*recp = rec;
 
 	return 0;
 }
 
-static int bloblist_ensurerec(uint tag, struct bloblist_rec **recp, int size)
+static int bloblist_ensurerec(uint tag, struct bloblist_rec **recp, int size,
+			      int align)
 {
 	struct bloblist_rec *rec;
 
@@ -94,7 +138,7 @@
 	} else {
 		int ret;
 
-		ret = bloblist_addrec(tag, size, &rec);
+		ret = bloblist_addrec(tag, size, align, &rec);
 		if (ret)
 			return ret;
 	}
@@ -116,22 +160,22 @@
 	return (void *)rec + rec->hdr_size;
 }
 
-void *bloblist_add(uint tag, int size)
+void *bloblist_add(uint tag, int size, int align)
 {
 	struct bloblist_rec *rec;
 
-	if (bloblist_addrec(tag, size, &rec))
+	if (bloblist_addrec(tag, size, align, &rec))
 		return NULL;
 
-	return rec + 1;
+	return (void *)rec + rec->hdr_size;
 }
 
-int bloblist_ensure_size(uint tag, int size, void **blobp)
+int bloblist_ensure_size(uint tag, int size, int align, void **blobp)
 {
 	struct bloblist_rec *rec;
 	int ret;
 
-	ret = bloblist_ensurerec(tag, &rec, size);
+	ret = bloblist_ensurerec(tag, &rec, size, align);
 	if (ret)
 		return ret;
 	*blobp = (void *)rec + rec->hdr_size;
@@ -143,7 +187,7 @@
 {
 	struct bloblist_rec *rec;
 
-	if (bloblist_ensurerec(tag, &rec, size))
+	if (bloblist_ensurerec(tag, &rec, size, 0))
 		return NULL;
 
 	return (void *)rec + rec->hdr_size;
@@ -154,7 +198,7 @@
 	struct bloblist_rec *rec;
 	int ret;
 
-	ret = bloblist_ensurerec(tag, &rec, *sizep);
+	ret = bloblist_ensurerec(tag, &rec, *sizep, 0);
 	if (ret == -ESPIPE)
 		*sizep = rec->size;
 	else if (ret)
@@ -233,6 +277,46 @@
 	return 0;
 }
 
+void bloblist_get_stats(ulong *basep, ulong *sizep, ulong *allocedp)
+{
+	struct bloblist_hdr *hdr = gd->bloblist;
+
+	*basep = map_to_sysmem(gd->bloblist);
+	*sizep = hdr->size;
+	*allocedp = hdr->alloced;
+}
+
+static void show_value(const char *prompt, ulong value)
+{
+	printf("%s:%*s %-5lx  ", prompt, 8 - (int)strlen(prompt), "", value);
+	print_size(value, "\n");
+}
+
+void bloblist_show_stats(void)
+{
+	ulong base, size, alloced;
+
+	bloblist_get_stats(&base, &size, &alloced);
+	printf("base:     %lx\n", base);
+	show_value("size", size);
+	show_value("alloced", alloced);
+	show_value("free", size - alloced);
+}
+
+void bloblist_show_list(void)
+{
+	struct bloblist_hdr *hdr = gd->bloblist;
+	struct bloblist_rec *rec;
+
+	printf("%-8s  %8s  Tag Name\n", "Address", "Size");
+	for (rec = bloblist_first_blob(hdr); rec;
+	     rec = bloblist_next_blob(hdr, rec)) {
+		printf("%08lx  %8x  %3d %s\n",
+		       (ulong)map_to_sysmem((void *)rec + rec->hdr_size),
+		       rec->size, rec->tag, bloblist_tag_name(rec->tag));
+	}
+}
+
 int bloblist_init(void)
 {
 	bool expected;
diff --git a/doc/arch/sandbox.rst b/doc/arch/sandbox.rst
index 360f224..4674c42 100644
--- a/doc/arch/sandbox.rst
+++ b/doc/arch/sandbox.rst
@@ -97,30 +97,39 @@
 --------------------
 
 Various options are available, mostly for test purposes. Use -h to see
-available options. Some of these are described below.
+available options. Some of these are described below:
 
-The terminal is normally in what is called 'raw-with-sigs' mode. This means
-that you can use arrow keys for command editing and history, but if you
-press Ctrl-C, U-Boot will exit instead of handling this as a keypress.
+* -t, --terminal <arg>
+  - The terminal is normally in what is called 'raw-with-sigs' mode. This means
+  that you can use arrow keys for command editing and history, but if you
+  press Ctrl-C, U-Boot will exit instead of handling this as a keypress.
+  Other options are 'raw' (so Ctrl-C is handled within U-Boot) and 'cooked'
+  (where the terminal is in cooked mode and cursor keys will not work, Ctrl-C
+  will exit).
 
-Other options are 'raw' (so Ctrl-C is handled within U-Boot) and 'cooked'
-(where the terminal is in cooked mode and cursor keys will not work, Ctrl-C
-will exit).
+* -l
+  - Show the LCD emulation window.
 
-As mentioned above, -l causes the LCD emulation window to be shown.
+* -d <device_tree>
+  - A device tree binary file can be provided with -d. If you edit the source
+  (it is stored at arch/sandbox/dts/sandbox.dts) you must rebuild U-Boot to
+  recreate the binary file.
 
-A device tree binary file can be provided with -d. If you edit the source
-(it is stored at arch/sandbox/dts/sandbox.dts) you must rebuild U-Boot to
-recreate the binary file.
+* -D
+  - To use the default device tree, use -D.
 
-To use the default device tree, use -D. To use the test device tree, use -T.
+* -T
+  - To use the test device tree, use -T.
 
-To execute commands directly, use the -c option. You can specify a single
-command, or multiple commands separated by a semicolon, as is normal in
-U-Boot. Be careful with quoting as the shell will normally process and
-swallow quotes. When -c is used, U-Boot exits after the command is complete,
-but you can force it to go to interactive mode instead with -i.
+* -c [<cmd>;]<cmd>
+  - To execute commands directly, use the -c option. You can specify a single
+  command, or multiple commands separated by a semicolon, as is normal in
+  U-Boot. Be careful with quoting as the shell will normally process and
+  swallow quotes. When -c is used, U-Boot exits after the command is complete,
+  but you can force it to go to interactive mode instead with -i.
 
+* -i
+  - Go to interactive mode after executing the commands specified by -c.
 
 Memory Emulation
 ----------------
diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c
index 4076535..31c5997 100644
--- a/drivers/clk/clk-uclass.c
+++ b/drivers/clk/clk-uclass.c
@@ -161,7 +161,7 @@
 	
 	bulk->count = 0;
 
-	count = dev_count_phandle_with_args(dev, "clocks", "#clock-cells");
+	count = dev_count_phandle_with_args(dev, "clocks", "#clock-cells", 0);
 	if (count < 1)
 		return count;
 
@@ -213,7 +213,7 @@
 	int ret;
 
 	num_parents = dev_count_phandle_with_args(dev, "assigned-clock-parents",
-						  "#clock-cells");
+						  "#clock-cells", 0);
 	if (num_parents < 0) {
 		debug("%s: could not read assigned-clock-parents for %p\n",
 		      __func__, dev);
diff --git a/drivers/core/of_access.c b/drivers/core/of_access.c
index bcf1644..0a12e9b 100644
--- a/drivers/core/of_access.c
+++ b/drivers/core/of_access.c
@@ -756,10 +756,11 @@
 }
 
 int of_count_phandle_with_args(const struct device_node *np,
-			       const char *list_name, const char *cells_name)
+			       const char *list_name, const char *cells_name,
+			       int cell_count)
 {
-	return __of_parse_phandle_with_args(np, list_name, cells_name, 0,
-					    -1, NULL);
+	return __of_parse_phandle_with_args(np, list_name, cells_name,
+					    cell_count, -1, NULL);
 }
 
 static void of_alias_add(struct alias_prop *ap, struct device_node *np,
diff --git a/drivers/core/ofnode.c b/drivers/core/ofnode.c
index 79fcdf5..7d1b895 100644
--- a/drivers/core/ofnode.c
+++ b/drivers/core/ofnode.c
@@ -432,15 +432,15 @@
 }
 
 int ofnode_count_phandle_with_args(ofnode node, const char *list_name,
-				   const char *cells_name)
+				   const char *cells_name, int cell_count)
 {
 	if (ofnode_is_np(node))
 		return of_count_phandle_with_args(ofnode_to_np(node),
-				list_name, cells_name);
+				list_name, cells_name, cell_count);
 	else
 		return fdtdec_parse_phandle_with_args(gd->fdt_blob,
 				ofnode_to_offset(node), list_name, cells_name,
-				0, -1, NULL);
+				cell_count, -1, NULL);
 }
 
 ofnode ofnode_path(const char *path)
diff --git a/drivers/core/read.c b/drivers/core/read.c
index 86f3f88..0761258 100644
--- a/drivers/core/read.c
+++ b/drivers/core/read.c
@@ -214,10 +214,11 @@
 }
 
 int dev_count_phandle_with_args(const struct udevice *dev,
-				const char *list_name, const char *cells_name)
+				const char *list_name, const char *cells_name,
+				int cell_count)
 {
 	return ofnode_count_phandle_with_args(dev_ofnode(dev), list_name,
-					      cells_name);
+					      cells_name, cell_count);
 }
 
 int dev_read_addr_cells(const struct udevice *dev)
diff --git a/drivers/net/designware.c b/drivers/net/designware.c
index 1c0e829..4c19abb 100644
--- a/drivers/net/designware.c
+++ b/drivers/net/designware.c
@@ -688,7 +688,8 @@
 	int i, clock_nb;
 
 	priv->clock_count = 0;
-	clock_nb = dev_count_phandle_with_args(dev, "clocks", "#clock-cells");
+	clock_nb = dev_count_phandle_with_args(dev, "clocks", "#clock-cells",
+					       0);
 	if (clock_nb > 0) {
 		priv->clocks = devm_kcalloc(dev, clock_nb, sizeof(struct clk),
 					    GFP_KERNEL);
diff --git a/drivers/phy/phy-uclass.c b/drivers/phy/phy-uclass.c
index 8f456f3..f344e94 100644
--- a/drivers/phy/phy-uclass.c
+++ b/drivers/phy/phy-uclass.c
@@ -214,7 +214,7 @@
 	if (!dev_read_prop(dev, "phys", NULL))
 		return 0;
 
-	count = dev_count_phandle_with_args(dev, "phys", "#phy-cells");
+	count = dev_count_phandle_with_args(dev, "phys", "#phy-cells", 0);
 	if (count < 1)
 		return count;
 
diff --git a/drivers/power/domain/power-domain-uclass.c b/drivers/power/domain/power-domain-uclass.c
index c2c7c3b..af829db 100644
--- a/drivers/power/domain/power-domain-uclass.c
+++ b/drivers/power/domain/power-domain-uclass.c
@@ -117,7 +117,7 @@
 	int i, count, ret = 0;
 
 	count = dev_count_phandle_with_args(dev, "power-domains",
-					    "#power-domain-cells");
+					    "#power-domain-cells", 0);
 	for (i = 0; i < count; i++) {
 		ret = power_domain_get_by_index(dev, &pd, i);
 		if (ret)
diff --git a/drivers/reset/reset-uclass.c b/drivers/reset/reset-uclass.c
index e7e407c..071c389 100644
--- a/drivers/reset/reset-uclass.c
+++ b/drivers/reset/reset-uclass.c
@@ -108,7 +108,8 @@
 
 	bulk->count = 0;
 
-	count = ofnode_count_phandle_with_args(node, "resets", "#reset-cells");
+	count = ofnode_count_phandle_with_args(node, "resets", "#reset-cells",
+					       0);
 	if (count < 1)
 		return count;
 
diff --git a/drivers/usb/host/ehci-generic.c b/drivers/usb/host/ehci-generic.c
index 304a343..c93a705 100644
--- a/drivers/usb/host/ehci-generic.c
+++ b/drivers/usb/host/ehci-generic.c
@@ -86,7 +86,7 @@
 	err = 0;
 	priv->clock_count = 0;
 	clock_nb = ofnode_count_phandle_with_args(dev_ofnode(dev), "clocks",
-						  "#clock-cells");
+						  "#clock-cells", 0);
 	if (clock_nb > 0) {
 		priv->clocks = devm_kcalloc(dev, clock_nb, sizeof(struct clk),
 					    GFP_KERNEL);
@@ -116,7 +116,7 @@
 
 	priv->reset_count = 0;
 	reset_nb = ofnode_count_phandle_with_args(dev_ofnode(dev), "resets",
-						  "#reset-cells");
+						  "#reset-cells", 0);
 	if (reset_nb > 0) {
 		priv->resets = devm_kcalloc(dev, reset_nb,
 					    sizeof(struct reset_ctl),
diff --git a/drivers/usb/host/ohci-da8xx.c b/drivers/usb/host/ohci-da8xx.c
index 22e7b56..aa1eba2 100644
--- a/drivers/usb/host/ohci-da8xx.c
+++ b/drivers/usb/host/ohci-da8xx.c
@@ -95,7 +95,8 @@
 
 	err = 0;
 	priv->clock_count = 0;
-	clock_nb = dev_count_phandle_with_args(dev, "clocks", "#clock-cells");
+	clock_nb = dev_count_phandle_with_args(dev, "clocks", "#clock-cells",
+					       0);
 
 	if (clock_nb < 0)
 		return clock_nb;
diff --git a/drivers/usb/host/ohci-generic.c b/drivers/usb/host/ohci-generic.c
index ed5e500..ac9b7e1 100644
--- a/drivers/usb/host/ohci-generic.c
+++ b/drivers/usb/host/ohci-generic.c
@@ -85,7 +85,8 @@
 
 	err = 0;
 	priv->clock_count = 0;
-	clock_nb = dev_count_phandle_with_args(dev, "clocks", "#clock-cells");
+	clock_nb = dev_count_phandle_with_args(dev, "clocks", "#clock-cells",
+					       0);
 	if (clock_nb > 0) {
 		priv->clocks = devm_kcalloc(dev, clock_nb, sizeof(struct clk),
 					    GFP_KERNEL);
@@ -111,7 +112,8 @@
 	}
 
 	priv->reset_count = 0;
-	reset_nb = dev_count_phandle_with_args(dev, "resets", "#reset-cells");
+	reset_nb = dev_count_phandle_with_args(dev, "resets", "#reset-cells",
+					       0);
 	if (reset_nb > 0) {
 		priv->resets = devm_kcalloc(dev, reset_nb,
 					    sizeof(struct reset_ctl),
diff --git a/include/bloblist.h b/include/bloblist.h
index 5784c22..2b4b669 100644
--- a/include/bloblist.h
+++ b/include/bloblist.h
@@ -19,6 +19,7 @@
 	BLOBLIST_ALIGN		= 16,
 };
 
+/* Supported tags - add new ones to tag_name in bloblist.c */
 enum bloblist_tag_t {
 	BLOBLISTT_NONE = 0,
 
@@ -35,6 +36,8 @@
 	BLOBLISTT_INTEL_VBT,		/* Intel Video-BIOS table */
 	BLOBLISTT_TPM2_TCG_LOG,		/* TPM v2 log space */
 	BLOBLISTT_TCPA_LOG,		/* TPM log space */
+
+	BLOBLISTT_COUNT
 };
 
 /**
@@ -65,7 +68,7 @@
  *	the bloblist can grow up to this size. This starts out as
  *	sizeof(bloblist_hdr) since we need at least that much space to store a
  *	valid bloblist
- * @spare: Space space
+ * @spare: Spare space (for future use)
  * @chksum: CRC32 for the entire bloblist allocated area. Since any of the
  *	blobs can be altered after being created, this checksum is only valid
  *	when the bloblist is finalised before jumping to the next stage of boot.
@@ -112,7 +115,7 @@
  * Searches the bloblist and returns the blob with the matching tag
  *
  * @tag:	Tag to search for (enum bloblist_tag_t)
- * @size:	Expected size of the blob
+ * @size:	Expected size of the blob, or 0 for any size
  * @return pointer to blob if found, or NULL if not found, or a blob was found
  *	but it is the wrong size
  */
@@ -129,10 +132,11 @@
  *
  * @tag:	Tag to add (enum bloblist_tag_t)
  * @size:	Size of the blob
+ * @align:	Alignment of the blob (in bytes), 0 for default
  * @return pointer to the newly added block, or NULL if there is not enough
  *	space for the blob
  */
-void *bloblist_add(uint tag, int size);
+void *bloblist_add(uint tag, int size, int align);
 
 /**
  * bloblist_ensure_size() - Find or add a blob
@@ -142,10 +146,11 @@
  * @tag:	Tag to add (enum bloblist_tag_t)
  * @size:	Size of the blob
  * @blobp:	Returns a pointer to blob on success
+ * @align:	Alignment of the blob (in bytes), 0 for default
  * @return 0 if OK, -ENOSPC if it is missing and could not be added due to lack
  *	of space, or -ESPIPE it exists but has the wrong size
  */
-int bloblist_ensure_size(uint tag, int size, void **blobp);
+int bloblist_ensure_size(uint tag, int size, int align, void **blobp);
 
 /**
  * bloblist_ensure() - Find or add a blob
@@ -207,6 +212,35 @@
 int bloblist_finish(void);
 
 /**
+ * bloblist_get_stats() - Get information about the bloblist
+ *
+ * This returns useful information about the bloblist
+ */
+void bloblist_get_stats(ulong *basep, ulong *sizep, ulong *allocedp);
+
+/**
+ * bloblist_show_stats() - Show information about the bloblist
+ *
+ * This shows useful information about the bloblist on the console
+ */
+void bloblist_show_stats(void);
+
+/**
+ * bloblist_show_list() - Show a list of blobs in the bloblist
+ *
+ * This shows a list of blobs, showing their address, size and tag.
+ */
+void bloblist_show_list(void);
+
+/**
+ * bloblist_tag_name() - Get the name for a tag
+ *
+ * @tag: Tag to check
+ * @return name of tag, or "invalid" if an invalid tag is provided
+ */
+const char *bloblist_tag_name(enum bloblist_tag_t tag);
+
+/**
  * bloblist_init() - Init the bloblist system with a single bloblist
  *
  * This uses CONFIG_BLOBLIST_ADDR and CONFIG_BLOBLIST_SIZE to set up a bloblist
diff --git a/include/dm/of_access.h b/include/dm/of_access.h
index 2fa65c9..cc382b1 100644
--- a/include/dm/of_access.h
+++ b/include/dm/of_access.h
@@ -450,6 +450,7 @@
  * @np:		pointer to a device tree node containing a list
  * @list_name:	property name that contains a list
  * @cells_name:	property name that specifies phandles' arguments count
+ * @cells_count: Cell count to use if @cells_name is NULL
  * @return number of phandle found, -ENOENT if
  *	@list_name does not exist, -EINVAL if a phandle was not found,
  *	@cells_name could not be found, the arguments were truncated or there
@@ -460,7 +461,8 @@
  *
  */
 int of_count_phandle_with_args(const struct device_node *np,
-			       const char *list_name, const char *cells_name);
+			       const char *list_name, const char *cells_name,
+			       int cells_count);
 
 /**
  * of_alias_scan() - Scan all properties of the 'aliases' node
diff --git a/include/dm/ofnode.h b/include/dm/ofnode.h
index 8df2fac..4b7af37 100644
--- a/include/dm/ofnode.h
+++ b/include/dm/ofnode.h
@@ -10,6 +10,7 @@
 /* TODO(sjg@chromium.org): Drop fdtdec.h include */
 #include <fdtdec.h>
 #include <dm/of.h>
+#include <log.h>
 
 /* Enable checks to protect against invalid calls */
 #undef OF_CHECKS
@@ -84,7 +85,7 @@
 };
 
 /**
- * _ofnode_to_np() - convert an ofnode to a live DT node pointer
+ * ofnode_to_np() - convert an ofnode to a live DT node pointer
  *
  * This cannot be called if the reference contains an offset.
  *
@@ -127,7 +128,7 @@
 	if (of_live_active())
 		return node.np != NULL;
 	else
-		return node.of_offset != -1;
+		return node.of_offset >= 0;
 }
 
 /**
@@ -182,8 +183,8 @@
 	 * live tree is in use.
 	 */
 	assert(!ofnode_valid(node) ||
-	       (of_live_active() ? _ofnode_to_np(node)
-				  : _ofnode_to_np(node)));
+	       (of_live_active() ? ofnode_to_np(node)
+				  : ofnode_to_np(node)));
 #endif
 	return of_live_active() && ofnode_valid(node);
 }
@@ -555,12 +556,13 @@
  * @node:	device tree node containing a list
  * @list_name:	property name that contains a list
  * @cells_name:	property name that specifies phandles' arguments count
+ * @cells_count: Cell count to use if @cells_name is NULL
  * @return number of phandle on success, -ENOENT if @list_name does not
  *      exist, -EINVAL if a phandle was not found, @cells_name could not
  *      be found.
  */
 int ofnode_count_phandle_with_args(ofnode node, const char *list_name,
-				   const char *cells_name);
+				   const char *cells_name, int cell_count);
 
 /**
  * ofnode_path() - find a node by full path
diff --git a/include/dm/read.h b/include/dm/read.h
index 67db94a..0585eb1 100644
--- a/include/dm/read.h
+++ b/include/dm/read.h
@@ -429,12 +429,14 @@
  * @dev:	device whose node containing a list
  * @list_name:	property name that contains a list
  * @cells_name:	property name that specifies phandles' arguments count
+ * @cells_count: Cell count to use if @cells_name is NULL
  * @Returns number of phandle found on success, on error returns appropriate
  * errno value.
  */
 
 int dev_count_phandle_with_args(const struct udevice *dev,
-				const char *list_name, const char *cells_name);
+				const char *list_name, const char *cells_name,
+				int cell_count);
 
 /**
  * dev_read_addr_cells() - Get the number of address cells for a device's node
@@ -880,10 +882,10 @@
 }
 
 static inline int dev_count_phandle_with_args(const struct udevice *dev,
-		const char *list_name, const char *cells_name)
+		const char *list_name, const char *cells_name, int cell_count)
 {
 	return ofnode_count_phandle_with_args(dev_ofnode(dev), list_name,
-					      cells_name);
+					      cells_name, cell_count);
 }
 
 static inline int dev_read_addr_cells(const struct udevice *dev)
diff --git a/include/test/ut.h b/include/test/ut.h
index 3295cd4..3f2ee75 100644
--- a/include/test/ut.h
+++ b/include/test/ut.h
@@ -224,6 +224,19 @@
 	}								\
 }
 
+/* Assert that two addresses (converted from pointers) are equal */
+#define ut_asserteq_addr(expr1, expr2) {				\
+	ulong _val1 = map_to_sysmem(expr1);				\
+	ulong _val2 = map_to_sysmem(expr2);				\
+									\
+	if (_val1 != _val2) {						\
+		ut_failf(uts, __FILE__, __LINE__, __func__,		\
+			 #expr1 " = " #expr2,				\
+			 "Expected %lx, got %lx", _val1, _val2);	\
+		return CMD_RET_FAILURE;					\
+	}								\
+}
+
 /* Assert that a pointer is NULL */
 #define ut_assertnull(expr) {					\
 	const void *_val = (expr);					\
diff --git a/lib/fdtdec.c b/lib/fdtdec.c
index b8fc5e2..ee1bd41 100644
--- a/lib/fdtdec.c
+++ b/lib/fdtdec.c
@@ -746,7 +746,7 @@
 			if (cells_name || cur_index == index) {
 				node = fdt_node_offset_by_phandle(blob,
 								  phandle);
-				if (!node) {
+				if (node < 0) {
 					debug("%s: could not find phandle\n",
 					      fdt_get_name(blob, src_node,
 							   NULL));
diff --git a/test/bloblist.c b/test/bloblist.c
index 4e537ee..0bb9e2d 100644
--- a/test/bloblist.c
+++ b/test/bloblist.c
@@ -7,6 +7,7 @@
 #include <bloblist.h>
 #include <log.h>
 #include <mapmem.h>
+#include <asm/state.h>
 #include <test/suites.h>
 #include <test/test.h>
 #include <test/ut.h>
@@ -24,10 +25,12 @@
 
 	TEST_SIZE		= 10,
 	TEST_SIZE2		= 20,
-	TEST_SIZE_LARGE		= 0xe0,
+	TEST_SIZE_LARGE		= 0x3e0,
 
 	TEST_ADDR		= CONFIG_BLOBLIST_ADDR,
-	TEST_BLOBLIST_SIZE	= 0x100,
+	TEST_BLOBLIST_SIZE	= 0x400,
+
+	ERASE_BYTE		= '\xff',
 };
 
 static struct bloblist_hdr *clear_bloblist(void)
@@ -40,7 +43,7 @@
 	 * to 0xff for testing purposes.
 	 */
 	hdr = map_sysmem(CONFIG_BLOBLIST_ADDR, TEST_BLOBLIST_SIZE);
-	memset(hdr, '\xff', TEST_BLOBLIST_SIZE);
+	memset(hdr, ERASE_BYTE, TEST_BLOBLIST_SIZE);
 	memset(hdr, '\0', sizeof(*hdr));
 
 	return hdr;
@@ -94,26 +97,27 @@
 	hdr = clear_bloblist();
 	ut_assertnull(bloblist_find(TEST_TAG, TEST_BLOBLIST_SIZE));
 	ut_assertok(bloblist_new(TEST_ADDR, TEST_BLOBLIST_SIZE, 0));
+	ut_asserteq(map_to_sysmem(hdr), TEST_ADDR);
 
 	/* Add a record and check that we can find it */
-	data = bloblist_add(TEST_TAG, TEST_SIZE);
+	data = bloblist_add(TEST_TAG, TEST_SIZE, 0);
 	rec = (void *)(hdr + 1);
-	ut_asserteq_ptr(rec + 1, data);
+	ut_asserteq_addr(rec + 1, data);
 	data = bloblist_find(TEST_TAG, TEST_SIZE);
-	ut_asserteq_ptr(rec + 1, data);
+	ut_asserteq_addr(rec + 1, data);
 
 	/* Check the data is zeroed */
 	ut_assertok(check_zero(data, TEST_SIZE));
 
 	/* Check the 'ensure' method */
-	ut_asserteq_ptr(data, bloblist_ensure(TEST_TAG, TEST_SIZE));
+	ut_asserteq_addr(data, bloblist_ensure(TEST_TAG, TEST_SIZE));
 	ut_assertnull(bloblist_ensure(TEST_TAG, TEST_SIZE2));
 	rec2 = (struct bloblist_rec *)(data + ALIGN(TEST_SIZE, BLOBLIST_ALIGN));
 	ut_assertok(check_zero(data, TEST_SIZE));
 
 	/* Check for a non-existent record */
-	ut_asserteq_ptr(data, bloblist_ensure(TEST_TAG, TEST_SIZE));
-	ut_asserteq_ptr(rec2 + 1, bloblist_ensure(TEST_TAG2, TEST_SIZE2));
+	ut_asserteq_addr(data, bloblist_ensure(TEST_TAG, TEST_SIZE));
+	ut_asserteq_addr(rec2 + 1, bloblist_ensure(TEST_TAG2, TEST_SIZE2));
 	ut_assertnull(bloblist_find(TEST_TAG_MISSING, 0));
 
 	return 0;
@@ -139,7 +143,7 @@
 	/* Check that we get the same thing again */
 	ut_assertok(bloblist_ensure_size_ret(TEST_TAG, &size, &data2));
 	ut_asserteq(TEST_SIZE, size);
-	ut_asserteq_ptr(data, data2);
+	ut_asserteq_addr(data, data2);
 
 	/* Check that the size remains the same */
 	size = TEST_SIZE2;
@@ -163,8 +167,8 @@
 	ut_assertok(bloblist_new(TEST_ADDR, TEST_BLOBLIST_SIZE, 0));
 	data = hdr + 1;
 	data += sizeof(struct bloblist_rec);
-	ut_asserteq_ptr(data, bloblist_ensure(TEST_TAG, TEST_SIZE));
-	ut_asserteq_ptr(data, bloblist_ensure(TEST_TAG, TEST_SIZE));
+	ut_asserteq_addr(data, bloblist_ensure(TEST_TAG, TEST_SIZE));
+	ut_asserteq_addr(data, bloblist_ensure(TEST_TAG, TEST_SIZE));
 
 	return 0;
 }
@@ -202,10 +206,10 @@
 	hdr->chksum--;
 
 	/* Make sure the checksum changes when we add blobs */
-	data = bloblist_add(TEST_TAG, TEST_SIZE);
+	data = bloblist_add(TEST_TAG, TEST_SIZE, 0);
 	ut_asserteq(-EIO, bloblist_check(TEST_ADDR, TEST_BLOBLIST_SIZE));
 
-	data2 = bloblist_add(TEST_TAG2, TEST_SIZE2);
+	data2 = bloblist_add(TEST_TAG2, TEST_SIZE2, 0);
 	ut_asserteq(-EIO, bloblist_check(TEST_ADDR, TEST_BLOBLIST_SIZE));
 	ut_assertok(bloblist_finish());
 
@@ -231,9 +235,123 @@
 
 	return 0;
 }
-
 BLOBLIST_TEST(bloblist_test_checksum, 0);
 
+/* Test the 'bloblist info' command */
+static int bloblist_test_cmd_info(struct unit_test_state *uts)
+{
+	struct sandbox_state *state = state_get_current();
+	struct bloblist_hdr *hdr;
+	char *data, *data2;
+
+	hdr = clear_bloblist();
+	ut_assertok(bloblist_new(TEST_ADDR, TEST_BLOBLIST_SIZE, 0));
+	data = bloblist_ensure(TEST_TAG, TEST_SIZE);
+	data2 = bloblist_ensure(TEST_TAG2, TEST_SIZE2);
+
+	console_record_reset_enable();
+	if (!state->show_test_output)
+		gd->flags |= GD_FLG_SILENT;
+	console_record_reset();
+	run_command("bloblist info", 0);
+	ut_assert_nextline("base:     %lx", (ulong)map_to_sysmem(hdr));
+	ut_assert_nextline("size:     400    1 KiB");
+	ut_assert_nextline("alloced:  70     112 Bytes");
+	ut_assert_nextline("free:     390    912 Bytes");
+	ut_assert_console_end();
+	gd->flags &= ~(GD_FLG_SILENT | GD_FLG_RECORD);
+
+	return 0;
+}
+BLOBLIST_TEST(bloblist_test_cmd_info, 0);
+
+/* Test the 'bloblist list' command */
+static int bloblist_test_cmd_list(struct unit_test_state *uts)
+{
+	struct sandbox_state *state = state_get_current();
+	struct bloblist_hdr *hdr;
+	char *data, *data2;
+
+	hdr = clear_bloblist();
+	ut_assertok(bloblist_new(TEST_ADDR, TEST_BLOBLIST_SIZE, 0));
+	data = bloblist_ensure(TEST_TAG, TEST_SIZE);
+	data2 = bloblist_ensure(TEST_TAG2, TEST_SIZE2);
+
+	console_record_reset_enable();
+	if (!state->show_test_output)
+		gd->flags |= GD_FLG_SILENT;
+	console_record_reset();
+	run_command("bloblist list", 0);
+	ut_assert_nextline("Address       Size  Tag Name");
+	ut_assert_nextline("%08lx  %8x    1 EC host event",
+			   (ulong)map_to_sysmem(data), TEST_SIZE);
+	ut_assert_nextline("%08lx  %8x    2 SPL hand-off",
+			   (ulong)map_to_sysmem(data2), TEST_SIZE2);
+	ut_assert_console_end();
+	gd->flags &= ~(GD_FLG_SILENT | GD_FLG_RECORD);
+
+	return 0;
+}
+BLOBLIST_TEST(bloblist_test_cmd_list, 0);
+
+/* Test alignment of bloblist blobs */
+static int bloblist_test_align(struct unit_test_state *uts)
+{
+	struct bloblist_hdr *hdr;
+	ulong addr;
+	char *data;
+	int i;
+
+	/* At the start there should be no records */
+	hdr = clear_bloblist();
+	ut_assertok(bloblist_new(TEST_ADDR, TEST_BLOBLIST_SIZE, 0));
+	ut_assertnull(bloblist_find(TEST_TAG, TEST_BLOBLIST_SIZE));
+
+	/* Check the default alignment */
+	for (i = 0; i < 3; i++) {
+		int size = i * 3;
+		ulong addr;
+		char *data;
+		int j;
+
+		data = bloblist_add(i, size, 0);
+		ut_assertnonnull(data);
+		addr = map_to_sysmem(data);
+		ut_asserteq(0, addr & (BLOBLIST_ALIGN - 1));
+
+		/* Only the bytes in the blob data should be zeroed */
+		for (j = 0; j < size; j++)
+			ut_asserteq(0, data[j]);
+		for (; j < BLOBLIST_ALIGN; j++)
+			ut_asserteq(ERASE_BYTE, data[j]);
+	}
+
+	/* Check larger alignment */
+	for (i = 0; i < 3; i++) {
+		int align = 32 << i;
+
+		data = bloblist_add(3 + i, i * 4, align);
+		ut_assertnonnull(data);
+		addr = map_to_sysmem(data);
+		ut_asserteq(0, addr & (align - 1));
+	}
+
+	/* Check alignment with an bloblist starting on a smaller alignment */
+	hdr = map_sysmem(TEST_ADDR + BLOBLIST_ALIGN, TEST_BLOBLIST_SIZE);
+	memset(hdr, ERASE_BYTE, TEST_BLOBLIST_SIZE);
+	memset(hdr, '\0', sizeof(*hdr));
+	ut_assertok(bloblist_new(TEST_ADDR + BLOBLIST_ALIGN, TEST_BLOBLIST_SIZE,
+				 0));
+
+	data = bloblist_add(1, 5, BLOBLIST_ALIGN * 2);
+	ut_assertnonnull(data);
+	addr = map_to_sysmem(data);
+	ut_asserteq(0, addr & (BLOBLIST_ALIGN * 2 - 1));
+
+	return 0;
+}
+BLOBLIST_TEST(bloblist_test_align, 0);
+
 int do_ut_bloblist(struct cmd_tbl *cmdtp, int flag, int argc,
 		   char *const argv[])
 {
diff --git a/test/dm/ofnode.c b/test/dm/ofnode.c
index 8bfb706..01ac3c2 100644
--- a/test/dm/ofnode.c
+++ b/test/dm/ofnode.c
@@ -19,6 +19,22 @@
 }
 DM_TEST(dm_test_ofnode_compatible, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
 
+static int dm_test_ofnode_get_by_phandle(struct unit_test_state *uts)
+{
+	/* test invalid phandle */
+	ut_assert(!ofnode_valid(ofnode_get_by_phandle(0)));
+	ut_assert(!ofnode_valid(ofnode_get_by_phandle(-1)));
+
+	/* test first valid phandle */
+	ut_assert(ofnode_valid(ofnode_get_by_phandle(1)));
+
+	/* test unknown phandle */
+	ut_assert(!ofnode_valid(ofnode_get_by_phandle(0x1000000)));
+
+	return 0;
+}
+DM_TEST(dm_test_ofnode_get_by_phandle, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
 static int dm_test_ofnode_by_prop_value(struct unit_test_state *uts)
 {
 	const char propname[] = "compatible";
@@ -87,6 +103,81 @@
 }
 DM_TEST(dm_test_ofnode_read, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
 
+static int dm_test_ofnode_phandle(struct unit_test_state *uts)
+{
+	struct ofnode_phandle_args args;
+	ofnode node;
+	int ret;
+	const char prop[] = "test-gpios";
+	const char cell[] = "#gpio-cells";
+	const char prop2[] = "phandle-value";
+
+	node = ofnode_path("/a-test");
+	ut_assert(ofnode_valid(node));
+
+	/* Test ofnode_count_phandle_with_args with cell name */
+	ret = ofnode_count_phandle_with_args(node, "missing", cell, 0);
+	ut_asserteq(-ENOENT, ret);
+	ret = ofnode_count_phandle_with_args(node, prop, "#invalid", 0);
+	ut_asserteq(-EINVAL, ret);
+	ret = ofnode_count_phandle_with_args(node, prop, cell, 0);
+	ut_asserteq(5, ret);
+
+	/* Test ofnode_parse_phandle_with_args with cell name */
+	ret = ofnode_parse_phandle_with_args(node, "missing", cell, 0, 0,
+					     &args);
+	ut_asserteq(-ENOENT, ret);
+	ret = ofnode_parse_phandle_with_args(node, prop, "#invalid", 0, 0,
+					     &args);
+	ut_asserteq(-EINVAL, ret);
+	ret = ofnode_parse_phandle_with_args(node, prop, cell, 0, 0, &args);
+	ut_assertok(ret);
+	ut_asserteq(1, args.args_count);
+	ut_asserteq(1, args.args[0]);
+	ret = ofnode_parse_phandle_with_args(node, prop, cell, 0, 1, &args);
+	ut_assertok(ret);
+	ut_asserteq(1, args.args_count);
+	ut_asserteq(4, args.args[0]);
+	ret = ofnode_parse_phandle_with_args(node, prop, cell, 0, 2, &args);
+	ut_assertok(ret);
+	ut_asserteq(5, args.args_count);
+	ut_asserteq(5, args.args[0]);
+	ut_asserteq(1, args.args[4]);
+	ret = ofnode_parse_phandle_with_args(node, prop, cell, 0, 3, &args);
+	ut_asserteq(-ENOENT, ret);
+	ret = ofnode_parse_phandle_with_args(node, prop, cell, 0, 4, &args);
+	ut_assertok(ret);
+	ut_asserteq(1, args.args_count);
+	ut_asserteq(12, args.args[0]);
+	ret = ofnode_parse_phandle_with_args(node, prop, cell, 0, 5, &args);
+	ut_asserteq(-ENOENT, ret);
+
+	/* Test ofnode_count_phandle_with_args with cell count */
+	ret = ofnode_count_phandle_with_args(node, "missing", NULL, 2);
+	ut_asserteq(-ENOENT, ret);
+	ret = ofnode_count_phandle_with_args(node, prop2, NULL, 1);
+	ut_asserteq(3, ret);
+
+	/* Test ofnode_parse_phandle_with_args with cell count */
+	ret = ofnode_parse_phandle_with_args(node, prop2, NULL, 1, 0, &args);
+	ut_assertok(ret);
+	ut_asserteq(1, ofnode_valid(args.node));
+	ut_asserteq(1, args.args_count);
+	ut_asserteq(10, args.args[0]);
+	ret = ofnode_parse_phandle_with_args(node, prop2, NULL, 1, 1, &args);
+	ut_asserteq(-EINVAL, ret);
+	ret = ofnode_parse_phandle_with_args(node, prop2, NULL, 1, 2, &args);
+	ut_assertok(ret);
+	ut_asserteq(1, ofnode_valid(args.node));
+	ut_asserteq(1, args.args_count);
+	ut_asserteq(30, args.args[0]);
+	ret = ofnode_parse_phandle_with_args(node, prop2, NULL, 1, 3, &args);
+	ut_asserteq(-ENOENT, ret);
+
+	return 0;
+}
+DM_TEST(dm_test_ofnode_phandle, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
 static int dm_test_ofnode_read_chosen(struct unit_test_state *uts)
 {
 	const char *str;
diff --git a/test/dm/test-fdt.c b/test/dm/test-fdt.c
index 26d57f4..cc12419 100644
--- a/test/dm/test-fdt.c
+++ b/test/dm/test-fdt.c
@@ -968,6 +968,71 @@
 }
 DM_TEST(dm_test_read_int_index, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
 
+static int dm_test_read_phandle(struct unit_test_state *uts)
+{
+	struct udevice *dev;
+	struct ofnode_phandle_args args;
+	int ret;
+	const char prop[] = "test-gpios";
+	const char cell[] = "#gpio-cells";
+	const char prop2[] = "phandle-value";
+
+	ut_assertok(uclass_first_device_err(UCLASS_TEST_FDT, &dev));
+	ut_asserteq_str("a-test", dev->name);
+
+	/* Test dev_count_phandle_with_args with cell name */
+	ret = dev_count_phandle_with_args(dev, "missing", cell, 0);
+	ut_asserteq(-ENOENT, ret);
+	ret = dev_count_phandle_with_args(dev, prop, "#invalid", 0);
+	ut_asserteq(-EINVAL, ret);
+	ut_asserteq(5, dev_count_phandle_with_args(dev, prop, cell, 0));
+
+	/* Test dev_read_phandle_with_args with cell name */
+	ret = dev_read_phandle_with_args(dev, "missing", cell, 0, 0, &args);
+	ut_asserteq(-ENOENT, ret);
+	ret = dev_read_phandle_with_args(dev, prop, "#invalid", 0, 0, &args);
+	ut_asserteq(-EINVAL, ret);
+	ut_assertok(dev_read_phandle_with_args(dev, prop, cell, 0, 0, &args));
+	ut_asserteq(1, args.args_count);
+	ut_asserteq(1, args.args[0]);
+	ut_assertok(dev_read_phandle_with_args(dev, prop, cell, 0, 1, &args));
+	ut_asserteq(1, args.args_count);
+	ut_asserteq(4, args.args[0]);
+	ut_assertok(dev_read_phandle_with_args(dev, prop, cell, 0, 2, &args));
+	ut_asserteq(5, args.args_count);
+	ut_asserteq(5, args.args[0]);
+	ut_asserteq(1, args.args[4]);
+	ret = dev_read_phandle_with_args(dev, prop, cell, 0, 3, &args);
+	ut_asserteq(-ENOENT, ret);
+	ut_assertok(dev_read_phandle_with_args(dev, prop, cell, 0, 4, &args));
+	ut_asserteq(1, args.args_count);
+	ut_asserteq(12, args.args[0]);
+	ret = dev_read_phandle_with_args(dev, prop, cell, 0, 5, &args);
+	ut_asserteq(-ENOENT, ret);
+
+	/* Test dev_count_phandle_with_args with cell count */
+	ret = dev_count_phandle_with_args(dev, "missing", NULL, 2);
+	ut_asserteq(-ENOENT, ret);
+	ut_asserteq(3, dev_count_phandle_with_args(dev, prop2, NULL, 1));
+
+	/* Test dev_read_phandle_with_args with cell count */
+	ut_assertok(dev_read_phandle_with_args(dev, prop2, NULL, 1, 0, &args));
+	ut_asserteq(1, ofnode_valid(args.node));
+	ut_asserteq(1, args.args_count);
+	ut_asserteq(10, args.args[0]);
+	ret = dev_read_phandle_with_args(dev, prop2, NULL, 1, 1, &args);
+	ut_asserteq(-EINVAL, ret);
+	ut_assertok(dev_read_phandle_with_args(dev, prop2, NULL, 1, 2, &args));
+	ut_asserteq(1, ofnode_valid(args.node));
+	ut_asserteq(1, args.args_count);
+	ut_asserteq(30, args.args[0]);
+	ret = dev_read_phandle_with_args(dev, prop2, NULL, 1, 3, &args);
+	ut_asserteq(-ENOENT, ret);
+
+	return 0;
+}
+DM_TEST(dm_test_read_phandle, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
 /* Test iteration through devices by drvdata */
 static int dm_test_uclass_drvdata(struct unit_test_state *uts)
 {