diff --git a/arch/arm/cpu/armv8/Kconfig b/arch/arm/cpu/armv8/Kconfig
index 3d19bbf..019b625 100644
--- a/arch/arm/cpu/armv8/Kconfig
+++ b/arch/arm/cpu/armv8/Kconfig
@@ -3,4 +3,22 @@ if ARM64
 config ARMV8_MULTIENTRY
         boolean "Enable multiple CPUs to enter into U-Boot"
 
+config SPIN_TABLE
+	bool "Support spin-table enable method"
+	depends on ARMV8_MULTIENTRY && OF_LIBFDT
+	help
+	  Say Y here to support "spin-table" enable method for booting Linux.
+
+	  To use this feature, you must do:
+	    - Specify enable-method = "spin-table" in each CPU node in the
+	      Device Tree you are using to boot the kernel
+	    - Let secondary CPUs in U-Boot (in a board specific manner)
+	      before the master CPU jumps to the kernel
+
+	  U-Boot automatically does:
+	    - Set "cpu-release-addr" property of each CPU node
+	      (overwrites it if already exists).
+	    - Reserve the code for the spin-table and the release address
+	      via a /memreserve/ region in the Device Tree.
+
 endif
diff --git a/arch/arm/cpu/armv8/Makefile b/arch/arm/cpu/armv8/Makefile
index 1c85aa9..2e3f421 100644
--- a/arch/arm/cpu/armv8/Makefile
+++ b/arch/arm/cpu/armv8/Makefile
@@ -15,6 +15,7 @@ obj-y	+= cache.o
 obj-y	+= tlb.o
 obj-y	+= transition.o
 obj-y	+= fwcall.o
+obj-$(CONFIG_SPIN_TABLE) += spin_table_v8.o
 
 obj-$(CONFIG_FSL_LAYERSCAPE) += fsl-layerscape/
 obj-$(CONFIG_ARCH_ZYNQMP) += zynqmp/
diff --git a/arch/arm/cpu/armv8/spin_table_v8.S b/arch/arm/cpu/armv8/spin_table_v8.S
new file mode 100644
index 0000000..2f1bd61
--- /dev/null
+++ b/arch/arm/cpu/armv8/spin_table_v8.S
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2016 Masahiro Yamada <yamada.masahiro@socionext.com>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <linux/linkage.h>
+
+ENTRY(spin_table_secondary_jump)
+.globl spin_table_reserve_begin
+spin_table_reserve_begin:
+0:	wfe
+	ldr	x0, spin_table_cpu_release_addr
+	cbz	x0, 0b
+	br	x0
+.globl spin_table_cpu_release_addr
+	.align	3
+spin_table_cpu_release_addr:
+	.quad	0
+.globl spin_table_reserve_end
+spin_table_reserve_end:
+ENDPROC(spin_table_secondary_jump)
diff --git a/arch/arm/cpu/armv8/start.S b/arch/arm/cpu/armv8/start.S
index 670e323..15ca864 100644
--- a/arch/arm/cpu/armv8/start.S
+++ b/arch/arm/cpu/armv8/start.S
@@ -94,7 +94,11 @@ reset:
 	/* Processor specific initialization */
 	bl	lowlevel_init
 
-#ifdef CONFIG_ARMV8_MULTIENTRY
+#if defined(CONFIG_SPIN_TABLE)
+	branch_if_master x0, x1, master_cpu
+	b	spin_table_secondary_jump
+	/* never return */
+#elif defined(CONFIG_ARMV8_MULTIENTRY)
 	branch_if_master x0, x1, master_cpu
 
 	/*
@@ -106,10 +110,8 @@ slave_cpu:
 	ldr	x0, [x1]
 	cbz	x0, slave_cpu
 	br	x0			/* branch to the given address */
-master_cpu:
-	/* On the master CPU */
 #endif /* CONFIG_ARMV8_MULTIENTRY */
-
+master_cpu:
 	bl	_main
 
 #ifdef CONFIG_SYS_RESET_SCTRL
diff --git a/arch/arm/lib/bootm-fdt.c b/arch/arm/lib/bootm-fdt.c
index 76b75d8..ec581ac 100644
--- a/arch/arm/lib/bootm-fdt.c
+++ b/arch/arm/lib/bootm-fdt.c
@@ -21,6 +21,7 @@
 #include <asm/armv7.h>
 #endif
 #include <asm/psci.h>
+#include <spin_table.h>
 
 DECLARE_GLOBAL_DATA_PTR;
 
@@ -51,5 +52,11 @@ int arch_fixup_fdt(void *blob)
 		return ret;
 #endif
 
+#ifdef CONFIG_SPIN_TABLE
+	ret = spin_table_update_dt(blob);
+	if (ret)
+		return ret;
+#endif
+
 	return 0;
 }
diff --git a/common/Makefile b/common/Makefile
index 1557a04..f4eb381 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -141,6 +141,7 @@ obj-$(CONFIG_ANDROID_BOOT_IMAGE) += image-android.o
 obj-$(CONFIG_$(SPL_)OF_LIBFDT) += image-fdt.o
 obj-$(CONFIG_$(SPL_)FIT) += image-fit.o
 obj-$(CONFIG_$(SPL_)FIT_SIGNATURE) += image-sig.o
+obj-$(CONFIG_$(SPL_)SPIN_TABLE) += spin_table.o
 obj-$(CONFIG_IO_TRACE) += iotrace.o
 obj-y += memsize.o
 obj-y += stdio.o
diff --git a/common/spin_table.c b/common/spin_table.c
new file mode 100644
index 0000000..046af8f
--- /dev/null
+++ b/common/spin_table.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2016 Masahiro Yamada <yamada.masahiro@socionext.com>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <libfdt.h>
+#include <spin_table.h>
+
+int spin_table_update_dt(void *fdt)
+{
+	int cpus_offset, offset;
+	const char *prop;
+	int ret;
+	unsigned long rsv_addr = (unsigned long)&spin_table_reserve_begin;
+	unsigned long rsv_size = &spin_table_reserve_end -
+						&spin_table_reserve_begin;
+
+	cpus_offset = fdt_path_offset(fdt, "/cpus");
+	if (cpus_offset < 0)
+		return -ENODEV;
+
+	for (offset = fdt_first_subnode(fdt, cpus_offset);
+	     offset >= 0;
+	     offset = fdt_next_subnode(fdt, offset)) {
+		prop = fdt_getprop(fdt, offset, "device_type", NULL);
+		if (!prop || strcmp(prop, "cpu"))
+			continue;
+
+		/*
+		 * In the first loop, we check if every CPU node specifies
+		 * spin-table.  Otherwise, just return successfully to not
+		 * disturb other methods, like psci.
+		 */
+		prop = fdt_getprop(fdt, offset, "enable-method", NULL);
+		if (!prop || strcmp(prop, "spin-table"))
+			return 0;
+	}
+
+	for (offset = fdt_first_subnode(fdt, cpus_offset);
+	     offset >= 0;
+	     offset = fdt_next_subnode(fdt, offset)) {
+		prop = fdt_getprop(fdt, offset, "device_type", NULL);
+		if (!prop || strcmp(prop, "cpu"))
+			continue;
+
+		ret = fdt_setprop_u64(fdt, offset, "cpu-release-addr",
+				(unsigned long)&spin_table_cpu_release_addr);
+		if (ret)
+			return -ENOSPC;
+	}
+
+	ret = fdt_add_mem_rsv(fdt, rsv_addr, rsv_size);
+	if (ret)
+		return -ENOSPC;
+
+	printf("   Reserved memory region for spin-table: addr=%lx size=%lx\n",
+	       rsv_addr, rsv_size);
+
+	return 0;
+}
diff --git a/include/spin_table.h b/include/spin_table.h
new file mode 100644
index 0000000..cf455a1
--- /dev/null
+++ b/include/spin_table.h
@@ -0,0 +1,14 @@
+/*
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#ifndef __SPIN_TABLE_H__
+#define __SPIN_TABLE_H__
+
+extern u64 spin_table_cpu_release_addr;
+extern char spin_table_reserve_begin;
+extern char spin_table_reserve_end;
+
+int spin_table_update_dt(void *fdt);
+
+#endif /* __SPIN_TABLE_H__ */
