diff --git a/MAINTAINERS b/MAINTAINERS
index 66f0b07263..29f2d7328c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -749,6 +749,12 @@ M:	Ezequiel Garcia <ezequiel at collabora.com>
 S:	Maintained
 F:	arch/mips/mach-jz47xx/
 
+MIPS Octeon
+M:	Aaron Williams <awilliams at marvell.com>
+S:	Maintained
+F:	arch/mips/mach-octeon/
+F:	arch/mips/include/asm/arch-octeon/
+
 MMC
 M:	Peng Fan <peng.fan at nxp.com>
 S:	Maintained
diff --git a/arch/Kconfig b/arch/Kconfig
index 91e049b322..1cd3e1dc0b 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -37,6 +37,7 @@ config MICROBLAZE
 
 config MIPS
 	bool "MIPS architecture"
+	select CREATE_ARCH_SYMLINK
 	select HAVE_ARCH_IOREMAP
 	select HAVE_PRIVATE_LIBGCC
 	select SUPPORT_OF_CONTROL
diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index 48e754cc46..3c7f3eb94f 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -106,6 +106,24 @@ config ARCH_JZ47XX
 	select OF_CONTROL
 	select DM
 
+config ARCH_OCTEON
+	bool "Support Marvell Octeon CN7xxx platforms"
+	select DISPLAY_CPUINFO
+	select DMA_ADDR_T_64BIT
+	select DM
+	select DM_SERIAL
+	select MIPS_CACHE_COHERENT
+	select MIPS_INIT_STACK_IN_SRAM
+	select MIPS_L2_CACHE
+	select MIPS_TUNE_OCTEON3
+	select ROM_EXCEPTION_VECTORS
+	select SUPPORTS_BIG_ENDIAN
+	select SUPPORTS_CPU_MIPS64_OCTEON
+	select PHYS_64BIT
+	select OF_CONTROL
+	select OF_LIVE
+	imply CMD_DM
+
 config MACH_PIC32
 	bool "Support Microchip PIC32"
 	select DM
@@ -160,6 +178,7 @@ source "arch/mips/mach-bmips/Kconfig"
 source "arch/mips/mach-jz47xx/Kconfig"
 source "arch/mips/mach-pic32/Kconfig"
 source "arch/mips/mach-mtmips/Kconfig"
+source "arch/mips/mach-octeon/Kconfig"
 
 if MIPS
 
@@ -233,6 +252,14 @@ config CPU_MIPS64_R6
 	  Choose this option to build a kernel for release 6 or later of the
 	  MIPS64 architecture.
 
+config CPU_MIPS64_OCTEON
+	bool "Marvell Octeon series of CPUs"
+	depends on SUPPORTS_CPU_MIPS64_OCTEON
+	select 64BIT
+	help
+	 Choose this option for Marvell Octeon CPUs.  These CPUs are between
+	 MIPS64 R5 and R6 with other extensions.
+
 endchoice
 
 menu "General setup"
@@ -261,7 +288,7 @@ config MIPS_CM_BASE
 config MIPS_CACHE_INDEX_BASE
 	hex "Index base address for cache initialisation"
 	default 0x80000000 if CPU_MIPS32
-	default 0xffffffff80000000 if CPU_MIPS64
+	default 0xFFFFFFFFC0000000 if ARCH_OCTEON
 	help
 	  This is the base address for a memory block, which is used for
 	  initialising the cache lines. This is also the base address of a memory
@@ -342,6 +369,14 @@ config SPL_LOADER_SUPPORT
 	help
 	  Enable this option if you want to use SPL loaders without DM enabled.
 
+config MIPS_CACHE_COHERENT
+	bool "Set if MIPS processor is cache coherent"
+	help
+	 Enable this if the MIPS architecture is cache coherent like the
+	 Marvell Octeon series of SoCs.  When this is set, cache flushes
+	 and invalidates only flush the write buffer since the hardware
+	 maintains cache coherency.
+
 endmenu
 
 menu "OS boot interface"
@@ -398,6 +433,9 @@ config SUPPORTS_CPU_MIPS64_R2
 config SUPPORTS_CPU_MIPS64_R6
 	bool
 
+config SUPPORTS_CPU_MIPS64_OCTEON
+	bool
+
 config CPU_MIPS32
 	bool
 	default y if CPU_MIPS32_R1 || CPU_MIPS32_R2 || CPU_MIPS32_R6
@@ -405,6 +443,7 @@ config CPU_MIPS32
 config CPU_MIPS64
 	bool
 	default y if CPU_MIPS64_R1 || CPU_MIPS64_R2 || CPU_MIPS64_R6
+	default y if CPU_MIPS64_OCTEON
 
 config MIPS_TUNE_4KC
 	bool
@@ -421,6 +460,9 @@ config MIPS_TUNE_34KC
 config MIPS_TUNE_74KC
 	bool
 
+config MIPS_TUNE_OCTEON3
+	bool
+
 config 32BIT
 	bool
 
@@ -453,6 +495,11 @@ config MIPS_SRAM_INIT
 	  before it can be used. If enabled, a function mips_sram_init() will
 	  be called just before setup_stack_gd.
 
+config DMA_ADDR_T_64BIT
+	bool
+	help
+	 Select this to enable 64-bit DMA addressing
+
 config SYS_DCACHE_SIZE
 	int
 	default 0
diff --git a/arch/mips/Makefile b/arch/mips/Makefile
index af3f227436..fa1ba7855a 100644
--- a/arch/mips/Makefile
+++ b/arch/mips/Makefile
@@ -1,6 +1,10 @@
 # SPDX-License-Identifier: GPL-2.0+
 
+ifneq ($(CONFIG_ARCH_OCTEON),y)
 head-y := arch/mips/cpu/start.o
+else
+head-y := arch/mips/mach-octeon/start.o
+endif
 
 ifeq ($(CONFIG_SPL_BUILD),y)
 ifneq ($(CONFIG_SPL_START_S_PATH),)
@@ -17,6 +21,7 @@ machine-$(CONFIG_ARCH_JZ47XX) += jz47xx
 machine-$(CONFIG_MACH_PIC32) += pic32
 machine-$(CONFIG_ARCH_MTMIPS) += mtmips
 machine-$(CONFIG_ARCH_MSCC) += mscc
+machine-${CONFIG_ARCH_OCTEON} += octeon
 
 machdirs := $(patsubst %,arch/mips/mach-%/,$(machine-y))
 libs-y += $(machdirs)
@@ -30,6 +35,7 @@ arch-$(CONFIG_CPU_MIPS32_R6) += -march=mips32r6 -Wa,-mips32r6
 arch-$(CONFIG_CPU_MIPS64_R1) += -march=mips64 -Wa,-mips64
 arch-$(CONFIG_CPU_MIPS64_R2) += -march=mips64r2 -Wa,-mips64r2
 arch-$(CONFIG_CPU_MIPS64_R6) += -march=mips64r6 -Wa,-mips64r6
+arch-${CONFIG_CPU_MIPS64_OCTEON} += -march=octeon3
 
 # Allow extra optimization for specific CPUs/SoCs
 tune-$(CONFIG_MIPS_TUNE_4KC) += -mtune=4kc
@@ -37,6 +43,7 @@ tune-$(CONFIG_MIPS_TUNE_14KC) += -mtune=14kc
 tune-$(CONFIG_MIPS_TUNE_24KC) += -mtune=24kc
 tune-$(CONFIG_MIPS_TUNE_34KC) += -mtune=34kc
 tune-$(CONFIG_MIPS_TUNE_74KC) += -mtune=74kc
+tune-${CONFIG_MIPS_TUNE_OCTEON3} += -mtune=octeon3
 
 # Include default header files
 cflags-y += -I$(srctree)/arch/mips/include/asm/mach-generic
diff --git a/arch/mips/cpu/Makefile b/arch/mips/cpu/Makefile
index 6df7bb4e48..732015d6f3 100644
--- a/arch/mips/cpu/Makefile
+++ b/arch/mips/cpu/Makefile
@@ -1,6 +1,8 @@
 # SPDX-License-Identifier: GPL-2.0+
 
-extra-y	= start.o
+ifneq ($(CONFIG_ARCH_OCTEON),y)
+extra-y = start.o
+endif
 
 obj-y += time.o
 obj-y += interrupts.o
diff --git a/arch/mips/include/asm/arch-octeon/cavm-reg.h b/arch/mips/include/asm/arch-octeon/cavm-reg.h
new file mode 100644
index 0000000000..b961e54956
--- /dev/null
+++ b/arch/mips/include/asm/arch-octeon/cavm-reg.h
@@ -0,0 +1,42 @@
+/* SPDX-License-Identifier:    GPL-2.0 */
+/*
+ * Copyright (C) 2020 Marvell International Ltd.
+ */
+
+#ifndef __CAVM_REG_H__
+
+/* Register offsets */
+#define CAVM_CIU_FUSE			((u64 *)0x80010100000001a0)
+#define CAVM_MIO_BOOT_REG_CFG0		((u64 *)0x8001180000000000)
+#define CAVM_RST_BOOT			((u64 *)0x8001180006001600)
+
+/* Register structs */
+
+/**
+ * Register (RSL) rst_boot
+ *
+ * RST Boot Register
+ */
+union cavm_rst_boot {
+	u64 u;
+	struct cavm_rst_boot_s {
+		u64 chipkill                         : 1;
+		u64 jtcsrdis                         : 1;
+		u64 ejtagdis                         : 1;
+		u64 romen                            : 1;
+		u64 ckill_ppdis                      : 1;
+		u64 jt_tstmode                       : 1;
+		u64 vrm_err                          : 1;
+		u64 reserved_37_56                   : 20;
+		u64 c_mul                            : 7;
+		u64 pnr_mul                          : 6;
+		u64 reserved_21_23                   : 3;
+		u64 lboot_oci                        : 3;
+		u64 lboot_ext                        : 6;
+		u64 lboot                            : 10;
+		u64 rboot                            : 1;
+		u64 rboot_pin                        : 1;
+	} s;
+};
+
+#endif /* __CAVM_REG_H__ */
diff --git a/arch/mips/include/asm/arch-octeon/clock.h b/arch/mips/include/asm/arch-octeon/clock.h
new file mode 100644
index 0000000000..a844a222c9
--- /dev/null
+++ b/arch/mips/include/asm/arch-octeon/clock.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier:    GPL-2.0 */
+/*
+ * Copyright (C) 2018, 2019 Marvell International Ltd.
+ *
+ * https://spdx.org/licenses
+ */
+
+#ifndef __CLOCK_H__
+
+/** System PLL reference clock */
+#define PLL_REF_CLK                     50000000        /* 50 MHz */
+#define NS_PER_REF_CLK_TICK             (1000000000 / PLL_REF_CLK)
+
+/**
+ * Returns the I/O clock speed in Hz
+ */
+u64 octeon_get_io_clock(void);
+
+/**
+ * Returns the core clock speed in Hz
+ */
+u64 octeon_get_core_clock(void);
+
+#endif /* __CLOCK_H__ */
diff --git a/arch/mips/mach-octeon/Kconfig b/arch/mips/mach-octeon/Kconfig
new file mode 100644
index 0000000000..67fcb6058c
--- /dev/null
+++ b/arch/mips/mach-octeon/Kconfig
@@ -0,0 +1,92 @@
+menu "Octeon platforms"
+	depends on ARCH_OCTEON
+
+config SYS_SOC
+	string
+	default "octeon"
+
+config OCTEON_CN7XXX
+	bool "Octeon CN7XXX SoC"
+
+config OCTEON_CN70XX
+	bool "Octeon CN70XX SoC"
+	select OCTEON_CN7XXX
+
+config OCTEON_CN73XX
+	bool "Octeon CN73XX SoC"
+	select OCTEON_CN7XXX
+
+config OCTEON_CN78XX
+	bool "Octeon CN78XX SoC"
+	select OCTEON_CN7XXX
+
+choice
+	prompt "Octeon MIPS family select"
+
+config SOC_OCTEON2
+	bool "Octeon II family"
+	help
+	 This selects the Octeon II SoC family
+
+config SOC_OCTEON3
+	bool "Octeon III family"
+	help
+	 This selects the Octeon III SoC family CN70xx, CN73XX, CN78xx
+	 and CNF75XX.
+
+endchoice
+
+config SYS_DCACHE_SIZE
+	default 32768
+
+config SYS_DCACHE_LINE_SIZE
+	default 128
+
+config SYS_ICACHE_SIZE
+	default	79872
+
+config SYS_ICACHE_LINE_SIZE
+	default 128
+
+config OCTEON_BIG_STACK_SIZE
+	hex
+	default 0x4000
+	help
+	 This enables a larger stack needed for Octeon 3 DRAM initialization.
+	 If this is disabled then a part of the L1 cache will be reserved for
+	 the stack, resulting in a smaller image.  If this  is true then
+	 a portion of the TEXT address space will be reserved for the stack.
+	 Note that this requires that U-Boot MUST be able to fit entirely
+	 within the L2 cache and cannot be executed from a parallel NOR flash.
+	 The default size is 16KiB.
+
+config OCTEON_COPY_FROM_FLASH_TO_L2
+	bool
+	default y
+	help
+	 Set this for U-Boot to attempt to copy itself from flash memory into
+	 the L2 cache.  This significantly improvess the boot performance.
+
+config OCTEON_L2_MEMCPY_IN_CACHE
+	bool
+	default y
+	help
+	 If this is set then the memcpy code that is used to copy U-Boot from
+	 the flash to the L2 cache is written to the L2 cache.  This
+	 significantly speeds up the memcpy operation.
+
+config OCTEON_L2_UBOOT_ADDR
+	hex
+	default 0xffffffff81000000
+	help
+	 This specifies the address where U-Boot will be copied into the L2
+	 cache.
+
+config OCTEON_L2_MEMCPY_ADDR
+	hex
+	default 0xffffffff81400000
+	help
+	 This specifies where U-Boot will place the memcpy routine used for
+	 copying U-Boot from flash to L2 cache.
+
+endmenu
diff --git a/arch/mips/mach-octeon/Makefile b/arch/mips/mach-octeon/Makefile
new file mode 100644
index 0000000000..a5fda682a7
--- /dev/null
+++ b/arch/mips/mach-octeon/Makefile
@@ -0,0 +1,10 @@
+# (C) Copyright 2019 Marvell, Inc.
+#
+# SPDX-License-Identifier:	GPL-2.0+
+#
+
+extra-y = start.o
+
+obj-y += clock.o
+obj-y += cpu.o
+obj-y += dram.o
diff --git a/arch/mips/mach-octeon/clock.c b/arch/mips/mach-octeon/clock.c
new file mode 100644
index 0000000000..6e32008641
--- /dev/null
+++ b/arch/mips/mach-octeon/clock.c
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018, 2019 Marvell International Ltd.
+ */
+
+#include <common.h>
+#include <asm/arch/clock.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+int octeon_get_timer_freq(void)
+{
+	return gd->cpu_clk;
+}
+
+/**
+ * Returns the I/O clock speed in Hz
+ */
+u64 octeon_get_io_clock(void)
+{
+	return gd->bus_clk;
+}
diff --git a/arch/mips/mach-octeon/cpu.c b/arch/mips/mach-octeon/cpu.c
new file mode 100644
index 0000000000..a1373c6d56
--- /dev/null
+++ b/arch/mips/mach-octeon/cpu.c
@@ -0,0 +1,55 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2020 Marvell International Ltd.
+ */
+
+#include <common.h>
+#include <linux/io.h>
+#include <asm/arch/clock.h>
+#include <asm/arch-octeon/cavm-reg.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static int get_clocks(void)
+{
+	const u64 ref_clock = PLL_REF_CLK;
+	union cavm_rst_boot rst_boot;
+
+	rst_boot.u = ioread64(CAVM_RST_BOOT);
+	gd->cpu_clk = ref_clock * rst_boot.s.c_mul;
+	gd->bus_clk = ref_clock * rst_boot.s.pnr_mul;
+
+	debug("%s: cpu: %lu, bus: %lu\n", __func__, gd->cpu_clk, gd->bus_clk);
+
+	return 0;
+}
+
+/* Early mach init code run from flash */
+int mach_cpu_init(void)
+{
+	/* Remap boot-bus 0x1fc0.0000 -> 0x1f40.0000 */
+	/* ToDo: Move this to an early running bus (bootbus) DM driver */
+	clrsetbits_be64(CAVM_MIO_BOOT_REG_CFG0, 0xffff, 0x1f40);
+
+	/* Get clocks and store them in GD */
+	get_clocks();
+
+	return 0;
+}
+
+/**
+ * Returns number of cores
+ *
+ * @return	number of CPU cores for the specified node
+ */
+static int cavm_octeon_num_cores(void)
+{
+	return fls64(ioread64(CAVM_CIU_FUSE) & 0xffffffffffff);
+}
+
+int print_cpuinfo(void)
+{
+	printf("SoC:   Octeon CN73xx (%d cores)\n", cavm_octeon_num_cores());
+
+	return 0;
+}
diff --git a/arch/mips/mach-octeon/dram.c b/arch/mips/mach-octeon/dram.c
new file mode 100644
index 0000000000..c16a73e8e6
--- /dev/null
+++ b/arch/mips/mach-octeon/dram.c
@@ -0,0 +1,27 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2020 Marvell International Ltd.
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <ram.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+int dram_init(void)
+{
+	/*
+	 * No DDR init yet -> run in L2 cache
+	 */
+	gd->ram_size = (2 << 20);
+	gd->bd->bi_dram[0].size = gd->ram_size;
+	gd->bd->bi_dram[1].size = 0;
+
+	return 0;
+}
+
+ulong board_get_usable_ram_top(ulong total_size)
+{
+	return gd->ram_top;
+}
diff --git a/arch/mips/mach-octeon/include/ioremap.h b/arch/mips/mach-octeon/include/ioremap.h
new file mode 100644
index 0000000000..59b75008a2
--- /dev/null
+++ b/arch/mips/mach-octeon/include/ioremap.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __ASM_MACH_OCTEON_IOREMAP_H
+#define __ASM_MACH_OCTEON_IOREMAP_H
+
+#include <linux/types.h>
+
+/*
+ * Allow physical addresses to be fixed up to help peripherals located
+ * outside the low 32-bit range -- generic pass-through version.
+ */
+static inline phys_addr_t fixup_bigphys_addr(phys_addr_t phys_addr,
+					     phys_addr_t size)
+{
+	return phys_addr;
+}
+
+static inline void __iomem *plat_ioremap(phys_addr_t offset, unsigned long size,
+					 unsigned long flags)
+{
+	return (void __iomem *)(XKPHYS | offset);
+}
+
+static inline int plat_iounmap(const volatile void __iomem *addr)
+{
+	return 0;
+}
+
+#define _page_cachable_default	_CACHE_CACHABLE_NONCOHERENT
+
+#endif /* __ASM_MACH_OCTEON_IOREMAP_H */
diff --git a/arch/mips/mach-octeon/start.S b/arch/mips/mach-octeon/start.S
new file mode 100644
index 0000000000..acb967201a
--- /dev/null
+++ b/arch/mips/mach-octeon/start.S
@@ -0,0 +1,1241 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ *  Startup Code for OCTEON 64-bit CPU-core
+ *
+ *  Copyright (c) 2003	Wolfgang Denk <wd at denx.de>
+ *  Copyright 2004, 2005, 2010 - 2015 Cavium Inc..
+ */
+
+#include <asm-offsets.h>
+#include <config.h>
+#include <asm/regdef.h>
+#include <asm/mipsregs.h>
+#include <asm/asm.h>
+
+#define BOOT_VECTOR_NUM_WORDS		8
+
+#define OCTEON_BOOT_MOVEABLE_MAGIC_OFFSET	0x70
+#define OCTEON_BOOT_VECTOR_MOVEABLE_OFFSET	0x78
+
+#define OCTEON_BOOT_MOVEABLE_MAGIC1_RAW	0xdb00110ad358eacd
+#define OCTEON_BOOT_MOVEABLE_MAGIC1	OCTEON_BOOT_MOVEABLE_MAGIC1_RAW
+
+#define OCTEON_CIU_SOFT_RST		0x8001070000000740
+
+#define	OCTEON_L2C_WPAR_PP0		0x8001180080840000
+#define OCTEON_MIO_BOOT_BASE		0x8001180000000000
+#define OCTEON_MIO_BOOT_REG_CFG0_OFF	0x0000
+#define OCTEON_MIO_BOOT_LOC_CFG0_OFF	0x0080
+#define OCTEON_MIO_BOOT_LOC_ADR_OFF	0x0090
+#define OCTEON_MIO_BOOT_LOC_DAT_OFF	0x0098
+#define	OCTEON_MIO_RST_BOOT		0x8001180000001600
+#define OCTEON_MIO_BOOT_REG_CFG0	0x8001180000000000
+#define	OCTEON_MIO_BOOT_REG_TIM0	0x8001180000000040
+#define OCTEON_MIO_BOOT_LOC_CFG0	0x8001180000000080
+#define OCTEON_MIO_BOOT_LOC_ADR		0x8001180000000090
+#define OCTEON_MIO_BOOT_LOC_DAT		0x8001180000000098
+#define	OCTEON_MIO_FUSE_DAT3		0x8001180000001418
+#define OCTEON_L2D_FUS3			0x80011800800007B8
+#define	OCTEON_LMC0_DDR_PLL_CTL		0x8001180088000258
+
+#define OCTEON_RST			0x8001180006000000
+#define OCTEON_RST_BOOT_OFFSET		0x1600
+#define OCTEON_RST_SOFT_RST_OFFSET	0x1680
+#define OCTEON_RST_COLD_DATAX_OFFSET(X)	(0x17C0 + (X) * 8)
+#define OCTEON_RST_BOOT			0x8001180006001600
+#define OCTEON_RST_SOFT_RST		0x8001180006001680
+#define OCTEON_RST_COLD_DATAX(X)	(0x80011800060017C0 + (X) * 8)
+
+#define OCTEON_OCX_COM_NODE		0x8001180011000000
+#define OCTEON_L2C_OCI_CTL		0x8001180080800020
+#define OCTEON_L2C_TAD_CTL		0x8001180080800018
+#define OCTEON_L2C_CTL			0x8001180080800000
+
+#define OCTEON_DBG_DATA			0x80011F00000001E8
+#define OCTEON_PCI_READ_CMD_E		0x80011F0000001188
+#define OCTEON_NPEI_DBG_DATA		0x80011F0000008510
+#define OCTEON_CIU_WDOG(X)		(0x8001070000000500 + (X) * 8)
+#define OCTEON_CIU_PP_POKE(X)		(0x8001070000000580 + (X) * 8)
+#define OCTEON_CIU3_WDOG(X)		(0x8001010000020000 + (X) * 8)
+#define OCTEON_CIU3_PP_POKE(X)		(0x8001010000030000 + (X) * 8)
+#define OCTEON_OCX_COM_LINKX_CTL(X)	(0x8001180011000020 + (X) * 8)
+#define OCTEON_SLI_CTL_STATUS		0x80011F0000028570
+#define OCTEON_GSERX_SCRATCH(X)		(0x8001180090000020 + (X) * 0x1000000)
+
+/** PRID for CN56XX */
+#define OCTEON_PRID_CN56XX		0x04
+/** PRID for CN52XX */
+#define OCTEON_PRID_CN52XX		0x07
+/** PRID for CN63XX */
+#define OCTEON_PRID_CN63XX		0x90
+/** PRID for CN68XX */
+#define OCTEON_PRID_CN68XX		0x91
+/** PRID for CN66XX */
+#define OCTEON_PRID_CN66XX		0x92
+/** PRID for CN61XX */
+#define OCTEON_PRID_CN61XX		0x93
+/** PRID for CNF71XX */
+#define OCTEON_PRID_CNF71XX		0x94
+/** PRID for CN78XX */
+#define OCTEON_PRID_CN78XX		0x95
+/** PRID for CN70XX */
+#define OCTEON_PRID_CN70XX		0x96
+/** PRID for CN73XX */
+#define OCTEON_PRID_CN73XX		0x97
+/** PRID for CNF75XX */
+#define OCTEON_PRID_CNF75XX		0x98
+
+/* func argument is used to create a  mark, must be unique */
+#define GETOFFSET(reg, func)	\
+	.balign	8;		\
+	bal	func ##_mark;	\
+	nop;			\
+	.dword	.;		\
+func ##_mark:			\
+	ld	reg, 0(ra);	\
+	dsubu	reg, ra, reg;
+
+#define JAL(func)		\
+	.balign	8;		\
+	bal	func ##_mark;	\
+	 nop;			\
+	.dword .;		\
+func ##_mark:			\
+	ld	t8, 0(ra);	\
+	dsubu	t8, ra, t8;	\
+	dla	t9, func;	\
+	daddu	t9, t9, t8;	\
+	jalr	t9;		\
+	 nop;
+
+	.set	arch=octeon3
+	.set	noreorder
+
+	.macro uhi_mips_exception
+	move	k0, t9		# preserve t9 in k0
+	move	k1, a0		# preserve a0 in k1
+	li	t9, 15		# UHI exception operation
+	li	a0, 0		# Use hard register context
+	sdbbp	1		# Invoke UHI operation
+	.endm
+
+	.macro setup_stack_gd
+	li	t0, -16
+	PTR_LI	t1, big_stack_start
+	and	sp, t1, t0		# force 16 byte alignment
+	PTR_SUBU \
+		sp, sp, GD_SIZE		# reserve space for gd
+	and	sp, sp, t0		# force 16 byte alignment
+	move	k0, sp			# save gd pointer
+#if CONFIG_VAL(SYS_MALLOC_F_LEN) && \
+    !CONFIG_IS_ENABLED(INIT_STACK_WITHOUT_MALLOC_F)
+	li	t2, CONFIG_VAL(SYS_MALLOC_F_LEN)
+	PTR_SUBU \
+		sp, sp, t2		# reserve space for early malloc
+	and	sp, sp, t0		# force 16 byte alignment
+#endif
+	move	fp, sp
+
+	/* Clear gd */
+	move	t0, k0
+1:
+	PTR_S	zero, 0(t0)
+	PTR_ADDIU t0, PTRSIZE
+	blt	t0, t1, 1b
+	 nop
+
+#if CONFIG_VAL(SYS_MALLOC_F_LEN) && \
+    !CONFIG_IS_ENABLED(INIT_STACK_WITHOUT_MALLOC_F)
+	PTR_S	sp, GD_MALLOC_BASE(k0)	# gd->malloc_base offset
+#endif
+	.endm
+
+/* Saved register usage:
+ * s0:	not used
+ * s1:	not used
+ * s2:	Address U-Boot loaded into in L2 cache
+ * s3:	Start address
+ * s4:	flags
+ *		1:	booting from RAM
+ *		2:	executing out of cache
+ *		4:	booting from flash
+ * s5:	u-boot size (data end - _start)
+ * s6:	offset in flash.
+ * s7:	_start physical address
+ * s8:
+ */
+
+ENTRY(_start)
+	/* U-Boot entry point */
+	b	reset
+
+	/* The above jump instruction/nop are considered part of the
+	 * bootloader_header_t structure but are not changed when the header is
+	 * updated.
+	 */
+
+	/* Leave room for bootloader_header_t header at start of binary.  This
+	 * header is used to identify the board the bootloader is for, what
+	 * address it is linked at, failsafe/normal, etc.  It also contains a
+	 * CRC of the entire image.
+	 */
+
+#if defined(CONFIG_ROM_EXCEPTION_VECTORS)
+	/*
+	 * Exception vector entry points. When running from ROM, an exception
+	 * cannot be handled. Halt execution and transfer control to debugger,
+	 * if one is attached.
+	 */
+	.org 0x200
+	/* TLB refill, 32 bit task */
+	uhi_mips_exception
+
+	.org 0x280
+	/* XTLB refill, 64 bit task */
+	uhi_mips_exception
+
+	.org 0x300
+	/* Cache error exception */
+	uhi_mips_exception
+
+	.org 0x380
+	/* General exception */
+	uhi_mips_exception
+
+	.org 0x400
+	/* Catch interrupt exceptions */
+	uhi_mips_exception
+
+	.org 0x480
+	/* EJTAG debug exception */
+1:	b	1b
+	 nop
+
+	.org 0x500
+#endif
+
+/* Reserve extra space so that when we use the boot bus local memory
+ * segment to remap the debug exception vector we don't overwrite
+ * anything useful
+ */
+
+/* Basic exception handler (dump registers) in all ASM.	 When using the TLB for
+ * mapping u-boot C code, we can't branch to that C code for exception handling
+ * (TLB is disabled for some exceptions.
+ */
+
+/* RESET/start here */
+	.balign	8
+reset:
+	nop
+	synci	0(zero)
+	mfc0	k0, CP0_STATUS
+	ori	k0, 0x00E0		/* enable 64 bit mode for CSR access */
+	mtc0	k0, CP0_STATUS
+
+	/* Save the address we're booting from, strip off low bits */
+	bal	1f
+	 nop
+1:
+	move	s3, ra
+	dins	s3, zero, 0, 12
+
+	/* Disable boot bus moveable regions */
+	PTR_LI	k0, OCTEON_MIO_BOOT_LOC_CFG0
+	sd	zero, 0(k0)
+	sd	zero, 8(k0)
+
+	/* Disable the watchdog timer
+	 * First we check if we're running on CN78XX, CN73XX or CNF75XX to see
+	 * if we use CIU3 or CIU.
+	 */
+	mfc0	t0, CP0_PRID
+	ext	t0, t0, 8, 8
+	/* Assume CIU */
+	PTR_LI	t1, OCTEON_CIU_WDOG(0)
+	PTR_LI	t2, OCTEON_CIU_PP_POKE(0)
+	blt	t0, OCTEON_PRID_CN78XX, wd_use_ciu
+	 nop
+	beq	t0, OCTEON_PRID_CN70XX, wd_use_ciu
+	 nop
+	/* Use CIU3 */
+	PTR_LI	t1, OCTEON_CIU3_WDOG(0)
+	PTR_LI	t2, OCTEON_CIU3_PP_POKE(0)
+wd_use_ciu:
+	sd	zero, 0(t2)		/* Pet the dog */
+	sd	zero, 0(t1)		/* Disable watchdog timer */
+
+	/* Errata: CN76XX has a node ID of 3. change it to zero here.
+	 * This needs to be done before we relocate to L2 as addresses change
+	 * For 76XX pass 1.X we need to zero out the OCX_COM_NODE[ID],
+	 * L2C_OCI_CTL[GKSEGNODE] and CP0 of Root.CvmMemCtl2[KSEGNODE].
+	 */
+	mfc0	a4, CP0_PRID
+	/* Check for 78xx pass 1.x processor ID */
+	andi	a4, 0xffff
+	blt	a4, (OCTEON_PRID_CN78XX << 8), 1f
+	 nop
+
+	/* Zero out alternate package for now */
+	dins	a4, zero, 6, 1
+	bge	a4, ((OCTEON_PRID_CN78XX << 8) | 0x08), 1f
+	 nop
+
+	/* 78xx or 76xx here, first check for bug #27141 */
+	PTR_LI	a5, OCTEON_SLI_CTL_STATUS
+	ld	a6, 0(a5)
+	andi	a7, a4, 0xff
+	andi	a6, a6, 0xff
+
+	beq	a6, a7, not_bug27141
+	 nop
+
+	/* core 0 proc_id rev_id field does not match SLI_CTL_STATUS rev_id */
+	/* We just hit bug #27141.  Need to reset the chip and try again */
+
+	PTR_LI	a4, OCTEON_RST_SOFT_RST
+	ori	a5, zero, 0x1	/* set the reset bit */
+
+reset_78xx_27141:
+	sync
+	synci	0(zero)
+	cache	9, 0(zero)
+	sd	a5, 0(a4)
+	wait
+	b	reset_78xx_27141
+	 nop
+
+not_bug27141:
+	/* 76XX pass 1.x has the node number set to 3 */
+	mfc0	a4, CP0_EBASE
+	ext	a4, a4, 0, 10
+	bne	a4, 0x180, 1f	/* Branch if not node 3 core 0 */
+	 nop
+
+	/* Clear OCX_COM_NODE[ID] */
+	PTR_LI	a5, OCTEON_OCX_COM_NODE
+	ld	a4, 0(a5)
+	dins	a4, zero, 0, 2
+	sd	a4, 0(a5)
+	ld	zero, 0(a5)
+
+	/* Clear L2C_OCI_CTL[GKSEGNODE] */
+	PTR_LI	a5, OCTEON_L2C_OCI_CTL
+	ld	a4, 0(a5)
+	dins	a4, zero, 4, 2
+	sd	a4, 0(a5)
+	ld	zero, 0(a5)
+
+	/* Clear CP0 Root.CvmMemCtl2[KSEGNODE] */
+	dmfc0	a4, CP0_CVMMEMCTL2
+	dins	a4, zero, 12, 2
+	dmtc0	a4, CP0_CVMMEMCTL2
+
+	/* Put the flash address in the start of the EBASE register to
+	 * enable our exception handler but only for core 0.
+	 */
+	mfc0	a4, CP0_EBASE
+	dext	a4, a4, 0, 10
+	bnez	a4, no_flash
+	/* OK in delay slot */
+	dext	a6, a6, 0, 16		/* Get the base address in flash */
+	sll	a6, a6, 16
+	mtc0	a6, CP0_EBASE	/* Enable exceptions */
+
+no_flash:
+	/* Zero out various registers */
+	mtc0	zero, CP0_DEPC
+	mtc0	zero, CP0_EPC
+	mtc0	zero, CP0_CAUSE
+	mfc0	a4, CP0_PRID
+	ext	a4, a4, 8, 8
+	mtc0	zero, CP0_DESAVE
+
+	/* The following are only available on Octeon 2 or later */
+	mtc0	zero, CP0_KSCRATCH1
+	mtc0	zero, CP0_KSCRATCH2
+	mtc0	zero, CP0_KSCRATCH3
+	mtc0	zero, CP0_USERLOCAL
+
+	/* Turn off ROMEN bit to disable ROM */
+	PTR_LI	a1, OCTEON_MIO_RST_BOOT
+	/* For OCTEON 3 we use RST_BOOT instead of MIO_RST_BOOT.
+	 * The difference is bits 24-26 are 6 instead of 0 for the address.
+	 */
+	/* For Octeon 2 and CN70XX we can ignore the watchdog */
+	blt	a4, OCTEON_PRID_CN78XX, watchdog_ok
+	 nop
+
+	PTR_LI	a1, OCTEON_RST_BOOT
+
+	beq	a4, OCTEON_PRID_CN70XX, watchdog_ok
+	 nop
+
+	ld	a2, 0(a1)
+	/* There is a bug where some registers don't get properly reset when
+	 * the watchdog timer causes a reset.  In this case we need to force
+	 * a reset.
+	 */
+	bbit0	a2, 11, watchdog_ok	/* Skip if watchdog not hit */
+	 dins	a2, zero, 2, 18	/* Don't clear LBOOT, LBOOT_EXT or LBOOT_OCI */
+	/* Clear bit indicating reset due to watchdog */
+	ori	a2, 1 << 11
+	sd	a2, 0(a1)
+
+	/* Disable watchdog */
+	PTR_LI	a1, OCTEON_CIU3_PP_POKE(0)
+	sd	zero, 0(a1)
+	PTR_LI	a1, OCTEON_CIU3_WDOG(0)
+	sd	zero, 0(a1)
+
+	/* Record this in the GSER0_SCRATCH register in bit 11 */
+	PTR_LI	a1, OCTEON_GSERX_SCRATCH(0)
+	ld	a2, 0(a1)
+	ori	a2, 1 << 11
+	sd	a2, 0(a1)
+
+	PTR_LI	a1, OCTEON_RST_SOFT_RST
+	li	a2, 1
+	sd	a2, 0(a1)
+	wait
+
+	/* We should never get here */
+
+watchdog_ok:
+	ld	a2, 0(a1)
+	/* Don't clear LBOOT/LBOOT_EXT or LBOOT_OCI */
+	dins	a2, zero, 2, 18
+	dins	a2, zero, 60, 1	/* Clear ROMEN bit */
+	sd	a2, 0(a1)
+
+	/* Start of Octeon setup */
+
+	/* Check what core we are - if core 0, branch to init tlb
+	 * loop in flash.  Otherwise, look up address of init tlb
+	 * loop that was saved in the boot vector block.
+	 */
+	mfc0	a0, CP0_EBASE
+	andi	a0, EBASE_CPUNUM		/* get core */
+	beqz	a0, InitTLBStart_local
+	 nop
+
+	break
+	/* We should never get here - non-zero cores now go directly to
+	 * tlb init from the boot stub in movable region.
+	 */
+
+	.globl InitTLBStart
+InitTLBStart:
+InitTLBStart_local:
+	/* If we don't have working memory yet configure a bunch of
+	 * scratch memory, and set the stack pointer to the top
+	 * of it.  This allows us to go to C code without having
+	 * memory set up
+	 *
+	 * Warning: do not change SCRATCH_STACK_LINES as this can impact the
+	 * transition from start.S to crti.asm. crti requires 590 bytes of
+	 * stack space.
+	 */
+	cache	1,0(zero)	/* Clear Dcache so cvmseg works right */
+#if CONFIG_OCTEON_BIG_STACK_SIZE
+	rdhwr	v0, $0
+	bnez	v0, 1f
+	 nop
+	PTR_LA	sp, big_stack_start - 16
+	b	stack_clear_done
+	 nop
+1:
+#endif
+#define SCRATCH_STACK_LINES 0x36   /* MAX is 0x36 */
+	dmfc0	v0, CP0_CVMMEMCTL
+	dins	v0, zero, 0, 9
+	/* setup SCRATCH_STACK_LINES scratch lines of scratch */
+	ori	v0, 0x100 | SCRATCH_STACK_LINES
+	dmtc0	v0, CP0_CVMMEMCTL
+	/* set stack to top of scratch memory */
+	li	sp, 0xffffffffffff8000 + (SCRATCH_STACK_LINES * 128)
+	/* Clear scratch for CN63XX pass 2.0 errata Core-15169*/
+	li	t0, 0xffffffffffff8000
+clear_scratch:
+	sd	zero, 0(t0)
+	addiu	t0, 8
+	bne	t0, sp, clear_scratch
+	 nop
+
+	/* This code run on all cores - core 0 from flash,
+	 * the rest from DRAM.	When booting from PCI, non-zero cores
+	 * come directly here from the boot vector - no earlier code in this
+	 * file is executed.
+	 */
+
+	/* Some generic initialization is done here as well, as we need this
+	 * done on all cores even when booting from PCI
+	 */
+stack_clear_done:
+	/* Clear watch registers. */
+	mtc0	zero, CP0_WATCHLO
+	mtc0	zero, CP0_WATCHHI
+
+	/* STATUS register */
+	mfc0	k0, CP0_STATUS
+	li	k1, ~ST0_IE
+	and	k0, k1
+	mtc0	k0, CP0_STATUS
+
+	/* CAUSE register */
+	mtc0	zero, CP0_CAUSE
+
+	/* Init Timer */
+	dmtc0	zero, CP0_COUNT
+	dmtc0	zero, CP0_COMPARE
+
+
+	mfc0	a5, CP0_STATUS
+	li	v0, 0xE0		/* enable 64 bit mode for CSR access */
+	or	v0, v0, a5
+	mtc0	v0, CP0_STATUS
+
+
+	dli	v0, 1 << 29  /* Enable large physical address support in TLB */
+	mtc0	v0, CP0_PAGEGRAIN
+
+InitTLB:
+	dmtc0	zero, CP0_ENTRYLO0
+	dmtc0	zero, CP0_ENTRYLO1
+	mtc0	zero, CP0_PAGEMASK
+	dmtc0	zero, CP0_CONTEXT
+	/* Use an offset into kseg0 so we won't conflict with Mips1 legacy
+	 * TLB clearing
+	 */
+	PTR_LI	v0, 0xFFFFFFFF90000000
+	mfc0	a0, CP0_CONFIG1
+	srl	a0, a0, 25
+	/* Check if config4 reg present */
+	mfc0	a1, CP0_CONFIG3
+	bbit0	a1, 31, 2f
+	 and	a0, a0, 0x3F		/* a0 now has the max mmu entry index */
+	mfc0	a1, CP0_CONFIG4
+	bbit0	a1, 14, 2f		/* check config4[MMUExtDef] */
+	 nop
+	/* append config4[MMUSizeExt] to most significant bit of
+	 * config1[MMUSize-1]
+	 */
+	ins	a0, a1, 6, 8
+	and	a0, a0, 0x3fff	/* a0 now includes max entries for cn6xxx */
+2:
+	dmtc0	zero, CP0_XCONTEXT
+	mtc0	zero, CP0_WIRED
+
+InitTLBloop:
+	dmtc0	v0, CP0_ENTRYHI
+	tlbp
+	mfc0	v1, CP0_INDEX
+	daddiu	v0, v0, 1<<13
+	bgez	v1, InitTLBloop
+
+	mtc0	a0, CP0_INDEX
+	tlbwi
+	bnez	a0, InitTLBloop
+	 daddiu	a0, -1
+
+	mthi	zero
+	mtlo	zero
+
+	/* Set up status register */
+	mfc0	v0, CP0_STATUS
+	/* Enable COP0 and COP2 access */
+	li	a4, (1 << 28) | (1 << 30)
+	or	v0, a4
+
+	/* Must leave BEV set here, as DRAM is not configured for core 0.
+	 * Also, BEV must be 1 later on when the exception base address is set.
+	 */
+
+	/* Mask all interrupts */
+	ins	v0, zero, 0, 16
+	/* Clear NMI (used to start cores other than core 0) */
+	ori	v0, 0xE4		/* enable 64 bit, disable interrupts */
+	mtc0	v0, CP0_STATUS
+
+	dli	v0,0xE000000F		/* enable all readhw locations */
+	mtc0	v0, CP0_HWRENA
+
+	dmfc0	v0, CP0_CVMCTL
+	ori	v0, 1<<14	/* enable fixup of unaligned mem access */
+	dmtc0	v0, CP0_CVMCTL
+
+	/* Setup scratch memory.  This is also done in
+	 * cvmx_user_app_init, and this code will be removed
+	 * from the bootloader in the near future.
+	 */
+
+	/* Set L2C_LAD_CTL[MAXLFB] = 0 on CN73XX */
+	mfc0	a4, CP0_PRID
+	ext	a4, a4, 8, 8
+	blt	a4, OCTEON_PRID_CN73XX, 72f
+	nop
+	PTR_LI	v0, OCTEON_L2C_TAD_CTL
+	ld	t1, 0(v0)
+	dins	t1, zero, 0, 4
+	sd	t1, 0(v0)
+	ld	zero, 0(v0)
+
+72:
+
+	/* clear these to avoid immediate interrupt in noperf mode */
+	dmtc0	zero, CP0_COMPARE	/* clear timer interrupt */
+	dmtc0	zero, CP0_COUNT		/* clear timer interrupt */
+	dmtc0	zero, CP0_PERF_CNT0	/* clear perfCnt0 */
+	dmtc0	zero, CP0_PERF_CNT1	/* clear perfCnt1 */
+	dmtc0	zero, CP0_PERF_CNT2
+	dmtc0	zero, CP0_PERF_CNT3
+
+	/* If we're running on a node other than 0 then we need to set KSEGNODE
+	 * to 0.  The nice thing with this code is that it also autodetects if
+	 * we're running on a processor that supports CVMMEMCTL2 or not since
+	 * only processors that have this will have a non-zero node ID.  Because
+	 * of this there's no need to check if we're running on a 78XX.
+	 */
+	mfc0    t1, CP0_EBASE
+	dext    t1, t1, 7, 3            /* Extract node number */
+	beqz    t1, is_node0            /* If non-zero then we're not node 0 */
+	 nop
+	dmfc0   t1, CP0_CVMMEMCTL2
+	dins    t1, zero, 12, 4
+	dmtc0   t1, CP0_CVMMEMCTL2
+is_node0:
+
+	/* Set up TLB mappings for u-boot code in flash. */
+
+	/* Use a bal to get the current PC into ra.  Since this bal is to
+	 * the address immediately following the delay slot, the ra is
+	 * the address of the label.  We then use this to get the actual
+	 * address that we are executing from.
+	 */
+	bal	__dummy
+	 nop
+
+__dummy:
+	/* Get the actual address that we are running at */
+	PTR_LA	a6, _start		/* Linked address of _start */
+	PTR_LA	a7, __dummy
+	dsubu	t0, a7, a6		/* offset of __dummy label from _start*/
+	dsubu	a7, ra, t0		/* a7 now has actual address of _start*/
+
+	/* Save actual _start address in s7.  This is where we
+	 * are executing from, as opposed to where the code is
+	 * linked.
+	 */
+	move	s7, a7
+	move	s4, zero
+
+	/* s7 has actual address of _start.  If this is
+	 * on the boot bus, it will be between 0xBFC000000 and 0xBFFFFFFF.
+	 * If it is on the boot bus, use 0xBFC00000 as the physical address
+	 * for the TLB mapping, as we will be adjusting the boot bus
+	 * to make this adjustment.
+	 * If we are running from DRAM (remote-boot), then we want to use the
+	 * real address in DRAM.
+	 */
+
+	/* Check to see if we are running from flash - we expect that to
+	 * be 0xffffffffb0000000-0xffffffffbfffffff
+	 * (0x10000000-0x1fffffff, unmapped/uncached)
+	 */
+	dli	t2, 0xffffffffb0000000
+	dsubu	t2, s7
+	slt	s4, s7, t2
+	bltz	t2, uboot_in_flash
+	 nop
+
+	/* If we're not core 0 then we don't care about cache */
+	mfc0	t2, CP0_EBASE
+	andi	t2, EBASE_CPUNUM
+	bnez	t2, uboot_in_ram
+	 nop
+
+	/* Find out if we're OCTEON I or OCTEON + which don't support running
+	 * out of cache.
+	 */
+	mfc0	t2, CP0_PRID
+	ext	t2, t2, 8, 8
+	li	s4, 1
+	blt	t2, 0x90, uboot_in_ram
+	 nop
+
+	/* U-Boot can be executing either in RAM or L2 cache.  Now we need to
+	 * check if DRAM is initialized.  The way we do that is to look at
+	 * the reset bit of the LMC0_DDR_PLL_CTL register (bit 7)
+	 */
+	PTR_LI	t2, OCTEON_LMC0_DDR_PLL_CTL
+	ld	t2, 0(t2)
+	bbit1	t2, 7, uboot_in_ram
+	 nop
+
+	/* We must be executing out of cache */
+	b	uboot_in_ram
+	 li	s4, 2
+
+uboot_in_flash:
+	/* Set s4 to 4 to indicate we're running in FLASH */
+	li	s4, 4
+
+#if defined(CONFIG_OCTEON_DISABLE_L2_CACHE_INDEX_ALIASING)
+	/* By default, L2C index aliasing is enabled.  In some cases it may
+	 * need to be disabled.  The L2C index aliasing can only be disabled
+	 * if U-Boot is running out of L2 cache and the L2 cache has not been
+	 * used to store anything.
+	 */
+	PTR_LI	t1, OCTEON_L2C_CTL
+	ld	t2, 0(t1)
+	ori	t2, 1
+	sd	t2, 0(t1)
+#endif
+
+	/* Use BFC00000 as physical address for TLB mappings when booting
+	 * from flash, as we will adjust the boot bus mappings to make this
+	 * mapping correct.
+	 */
+	dli	a7, 0xFFFFFFFFBFC00000
+	dsubu	s6, s7, a7  /* Save flash offset in s6 */
+
+#if defined(CONFIG_OCTEON_COPY_FROM_FLASH_TO_L2)
+	/* For OCTEON II we check to see if the L2 cache is big enough to hold
+	 * U-Boot.  If it is big enough then we copy ourself from flash to the
+	 * L2 cache in order to speed up execution.
+	 */
+
+	/* Check for OCTEON 2 */
+	mfc0	t1, CP0_PRID
+	ext	t1, t1, 8, 8
+	/* Get number of L2 cache sets */
+	beq	t1, OCTEON_PRID_CNF71XX, got_l2_sets	/* CNF71XX */
+	 li	t2, 1 << 9
+	beq	t1, OCTEON_PRID_CN78XX, got_l2_sets	/* CN78XX */
+	 li	t2, 1 << 13
+	beq	t1, OCTEON_PRID_CN70XX, got_l2_sets	/* CN70XX */
+	 li	t2, 1 << 10
+	beq	t1, OCTEON_PRID_CN73XX, got_l2_sets	/* CN73XX */
+	 li	t2, 1 << 11
+	beq	t1, OCTEON_PRID_CNF75XX, got_l2_sets	/* CNF75XX */
+	 li	t2, 1 << 11
+	b	l2_cache_too_small	/* Unknown OCTEON model */
+	 nop
+
+got_l2_sets:
+	/* Get number of associations */
+	PTR_LI	t0, OCTEON_MIO_FUSE_DAT3
+	ld	t0, 0(t0)
+	dext	t0, t0, 32, 3
+
+	beq	t1, OCTEON_PRID_CN70XX, process_70xx_l2sets
+	 nop
+	/* 0 = 16-way, 1 = 12-way, 2 = 8-way, 3 = 4-way, 4-7 reserved */
+	beqz	t0, got_l2_ways
+	 li	t3, 16
+	beq	t0, 1, got_l2_ways
+	 li	t3, 12
+	beq	t0, 2, got_l2_ways
+	 li	t3, 8
+	beq	t0, 3, got_l2_ways
+	 li	t3, 4
+	b	l2_cache_too_small
+	 nop
+
+process_70xx_l2sets:
+	/* For 70XX, the number of ways is defined as:
+	 * 0 - full cache (4-way) 512K
+	 * 1 - 3/4 ways (3-way) 384K
+	 * 2 - 1/2 ways (2-way) 256K
+	 * 3 - 1/4 ways (1-way) 128K
+	 * 4-7 illegal (aliased to 0-3)
+	 */
+	andi	t0, 3
+	beqz	t0, got_l2_ways
+	 li	t3, 4
+	beq	t0, 1, got_l2_ways
+	 li	t3, 3
+	beq	t0, 2, got_l2_ways
+	 li	t3, 2
+	li	t3, 1
+
+got_l2_ways:
+	dmul	a1, t2, t3		/* Calculate cache size */
+	dsll	a1, 7			/* Ways * Sets * cache line sz (128) */
+	daddiu	a1, a1, -128		/* Adjust cache size for copy code */
+
+	/* Calculate size of U-Boot image */
+	/*
+	 * "uboot_end - _start" is not correct, as the image also
+	 * includes the DTB appended to the end (OF_EMBED is deprecated).
+	 * Lets use a defined max for now here.
+	 */
+	PTR_LI	s5, CONFIG_BOARD_SIZE_LIMIT
+
+	daddu	t2, s5, s7	/* t2 = end address */
+	daddiu	t2, t2, 127
+	ins	t2, zero, 0, 7	/* Round up to cache line for memcpy */
+
+	slt	t1, a1, s5	/* See if we're bigger than the L2 cache */
+	bnez	t1, l2_cache_too_small
+	 nop
+	/* Address we plan to load at in the L2 cache */
+	PTR_LI	t9, CONFIG_OCTEON_L2_UBOOT_ADDR
+# ifdef CONFIG_OCTEON_L2_MEMCPY_IN_CACHE
+	/* Enable all ways for PP0.  Authentik ROM may have disabled these */
+	PTR_LI	a1, OCTEON_L2C_WPAR_PP0
+	sd	zero, 0(a1)
+
+	/* Address to place our memcpy code */
+	PTR_LI	a0, CONFIG_OCTEON_L2_MEMCPY_ADDR
+	/* The following code writes a simple memcpy routine into the cache
+	 * to copy ourself from flash into the L2 cache.  This makes the
+	 * memcpy routine a lot faster since each instruction can potentially
+	 * require four read cycles to flash over the boot bus.
+	 */
+	/* Zero cache line in the L2 cache */
+	zcb	(a0)
+	synci	0(zero)
+	dli	a1, 0xdd840000dd850008	/* ld a0, 0(t0);  ld a1, 8(t0) */
+	sd	a1, 0(a0)
+	dli	a1, 0xdd860010dd870018	/* ld a2, 16(t0); ld a3, 24(t0) */
+	sd	a1, 8(a0)
+	dli	a1, 0xfda40000fda50008	/* sd a0, 0(t1);  sd a1, 8(t1) */
+	sd	a1, 16(a0)
+	dli	a1, 0xfda60010fda70018	/* sd a2, 16(t1); sd a3, 24(t1) */
+	sd	a1, 24(a0)
+	dli	a1, 0x258c0020158efff6	/* addiu t0, 32; bne t0, t2, -40 */
+	sd	a1, 32(a0)
+	dli	a1, 0x25ad002003e00008	/* addiu t1, 32; jr ra */
+	sd	a1, 40(a0)
+	sd	zero, 48(a0)		/* nop; nop */
+
+	/* Synchronize the caches */
+	sync
+	synci	0(zero)
+
+	move	t0, s7
+	move	t1, t9
+
+	/* Do the memcpy operation in L2 cache to copy ourself from flash
+	 * to the L2 cache.
+	 */
+	jalr	a0
+	 nop
+
+# else
+	/* Copy ourself to the L2 cache from flash, 32 bytes at a time */
+	/* This code is now written to the L2 cache using the code above */
+1:
+	ld	a0, 0(t0)
+	ld	a1, 8(t0)
+	ld	a2, 16(t0)
+	ld	a3, 24(t0)
+	sd	a0, 0(t1)
+	sd	a1, 8(t1)
+	sd	a2, 16(t1)
+	sd	a3, 24(t1)
+	addiu	t0, 32
+	bne	t0, t2, 1b
+	addiu	t1, 32
+# endif	/* CONFIG_OCTEON_L2_MEMCPY_IN_CACHE */
+
+	/* Adjust the start address of U-Boot and the global pointer */
+	subu	t0, s7, t9	/* t0 = address difference */
+	move	s7, t9		/* Update physical address */
+	move	s2, t9
+	sync
+	synci	0(zero)
+
+	/* Now we branch to the L2 cache.  We first get our PC then adjust it
+	 */
+	bal	3f
+	 nop
+3:
+	/* Don't add any instructions here! */
+	subu	t9, ra, t0
+	/* Give ourself 16 bytes */
+	addiu	t9, 0x10
+
+	jal	t9		/* Branch to address in L2 cache */
+
+	 nop
+	nop
+	/* Add instructions after here */
+
+	move	a7, s7
+
+	b	uboot_in_ram
+	 ori	s4, 2		/* Running out of L2 cache */
+
+l2_cache_too_small:	/* We go here if we can't copy ourself to L2 */
+#endif /* CONFIG_OCTEON_COPY_FROM_FLASH_TO_L2 */
+
+	/* This code is only executed if booting from flash. */
+	/*  For flash boot (_not_ RAM boot), we do a workaround for
+	 * an LLM errata on CN38XX and CN58XX parts.
+	 */
+
+uboot_in_ram:
+	/* U-boot address is now in reg a7, and is 4 MByte aligned.
+	 * (boot bus addressing has been adjusted to make this happen for flash,
+	 * and for DRAM this alignment must be provided by the remote boot
+	 * utility.
+	 */
+	/* See if we're in KSEG0 range, if so set EBASE register to handle
+	 * exceptions.
+	 */
+	dli	a1, 0x20000000
+	bge	a7, a1, 1f
+	 nop
+	/* Convert our physical address to KSEG0 */
+	PTR_LI	a1, 0xffffffff80000000
+	or	a1, a1, a7
+	mtc0	a1, CP0_EBASE
+1:
+	/* U-boot now starts at 0xBFC00000.  Use a single 4 MByte TLB mapping
+	 * to map u-boot.
+	 */
+	move	a0, a6		/* Virtual addr in a0 */
+	dins	a0, zero, 0, 16	/* Zero out offset bits */
+	move	a1, a7		/* Physical addr in a1 */
+
+	/* Now we need to remove the MIPS address space bits.  For this we
+	 * need to determine if it is a 32 bit compatibility address or not.
+	 */
+
+	/* 'lowest' address in compatibility space */
+	PTR_LI	t0, 0xffffffff80000000
+	dsubu	t0, t0, a1
+	bltz	t0, compat_space
+	 nop
+
+	/* We have a xkphys address, so strip off top bit */
+	b	addr_fixup_done
+	 dins	a1, zero, 63, 1
+
+compat_space:
+	PTR_LI	a2, 0x1fffffff
+	and	a1, a1, a2  /* Mask phy addr to remove address space bits */
+
+addr_fixup_done:
+	/* Currenty the u-boot image size is limited to 4 MBytes.  In order to
+	 * support larger images the flash mapping will need to be changed to
+	 * be able to access more than that before C code is run.  Until that
+	 * is done, we just use a 4 MByte mapping for the secondary cores as
+	 * well.
+	 */
+	/* page size (only support 4 Meg binary size for now for core 0)
+	 * This limitation is due to the fact that the boot vector is
+	 * 0xBFC00000 which only makes 4MB available.  Later more flash
+	 * address space will be available after U-Boot has been copied to
+	 * RAM.	 For now assume that it is in flash.
+	 */
+	li	a2, 2*1024*1024
+
+	mfc0	a4, CP0_EBASE
+	andi	a4, EBASE_CPUNUM		/* get core */
+	beqz	a4, core_0_tlb
+	 nop
+
+	/* Now determine how big a mapping to use for secondary cores,
+	 * which need to map all of u-boot + heap in DRAM
+	 */
+	/* Here we look at the alignment of the the physical address,
+	 * and use the largest page size possible.  In some cases
+	 * this can result in an oversize mapping, but for secondary cores
+	 * this mapping is very short lived.
+	 */
+
+	/* Physical address in a1 */
+	li	a2, 1
+1:
+	sll	a2, 1
+	and	a5, a1, a2
+	beqz	a5, 1b
+	 nop
+
+	/* a2 now contains largest page size we can use */
+core_0_tlb:
+	JAL(single_tlb_setup)
+
+	/* Check if we're running from cache */
+	bbit1	s4, 1, uboot_in_cache
+	 nop
+
+	/* If we are already running from ram, we don't need to muck
+	 * with boot bus mappings.
+	 */
+	PTR_LI	t2, 0xffffffffb0000000
+	dsubu	t2, s7
+	/* See if our starting address is lower than the boot bus */
+	bgez	t2, uboot_in_ram2	/* If yes, booting from RAM */
+	 nop
+
+uboot_in_cache:
+#if CONFIG_OCTEON_BIG_STACK_SIZE
+	/* The large stack is only for core 0.  For all other cores we need to
+	 * use the L1 cache otherwise the other cores will stomp on top of each
+	 * other unless even more space is reserved for the stack space for
+	 * each core.  With potentially 96 cores this gets excessive.
+	 */
+	mfc0	v0, CP0_EBASE
+	andi	a0, EBASE_CPUNUM
+	bnez	a0, no_big_stack
+	 nop
+	PTR_LA	sp, big_stack_start
+	daddiu	sp, -16
+
+no_big_stack:
+#endif
+	/* We now have the TLB set up, so we need to remap the boot bus.
+	 * This is tricky, as we are running from flash, and will be changing
+	 * the addressing of the flash.
+	 */
+	/* Enable movable boot bus region 0, at address 0x10000000 */
+	PTR_LI	a4, OCTEON_MIO_BOOT_BASE
+	dli	a5, 0x81000000	/* EN + base address 0x11000000 */
+	sd	a5, OCTEON_MIO_BOOT_LOC_CFG0_OFF(a4)
+
+	/* Copy code to that remaps the boot bus to movable region */
+	sd	zero, OCTEON_MIO_BOOT_LOC_DAT_OFF(a4)
+
+	PTR_LA	a6, change_boot_mappings
+	GETOFFSET(a5, change_boot_mappings);
+	daddu	a5, a5, a6
+
+	/* The code is 16 bytes (2 DWORDS) */
+	ld	a7, 0(a5)
+	sd	a7, OCTEON_MIO_BOOT_LOC_DAT_OFF(a4)
+	ld	a7, 8(a5)
+	sd	a7, OCTEON_MIO_BOOT_LOC_DAT_OFF(a4)
+
+	/* Read from an RML register to ensure that the previous writes have
+	 * completed before we branch to the movable region.
+	 */
+	ld	zero, OCTEON_MIO_BOOT_LOC_CFG0_OFF(a4)
+
+	/* Compute value for boot bus configuration register */
+	/* Read region 0 config so we can _modify_ the base address field */
+	PTR_LI	a4, OCTEON_MIO_BOOT_REG_CFG0	/* region 0 config */
+	ld	a0, 0(a4)
+	dli	a4, 0xf0000000		/* Mask off bits we want to save */
+	and	a4, a4, a0
+	dli	a0, 0x0fff0000		/* Force size to max */
+	or	a4, a4, a0
+
+	move	a5, s6
+	/* Convert to 64k blocks, as used by boot bus config */
+	srl	a5, 16
+	li	a6, 0x1fc0	/* 'normal' boot bus base config value */
+	subu	a6, a6, a5	/* Subtract offset */
+	/* combine into register value to pass to boot bus routine */
+	or	a0, a4, a6
+
+	/* Branch there */
+	PTR_LA	a1, __mapped_continue_label
+	PTR_LI	a2, OCTEON_MIO_BOOT_REG_CFG0
+	/* If region 0 is not enabled we can skip it */
+	ld	a4, 0(a2)
+	bbit0	a4, 31, __mapped_continue_label
+	 nop
+	li	a4, 0x10000000
+	j	a4
+	 synci	0(zero)
+
+	/* We never get here, as we go directly to __mapped_continue_label */
+	break
+
+
+uboot_in_ram2:
+
+	/* Now jump to address in TLB mapped memory to continue execution */
+	PTR_LA	a4, __mapped_continue_label
+	synci	0(a4)
+	j	a4
+	 nop
+
+__mapped_continue_label:
+	/* Check if we are core 0, if we are not then we need
+	 * to vector to code in DRAM to do application setup, and
+	 * skip the rest of the bootloader.  Only core 0 runs the bootloader
+	 * and sets up the tables that the other cores will use for
+	 * configuration.
+	 */
+	mfc0	a0, CP0_EBASE
+	andi	a0, EBASE_CPUNUM   /* get core */
+	/* if (__all_cores_are_equal==0 && core==0),
+	 * then jump to execute BL on core 0; else 'go to next line'
+	 * (core_0_cont1 is executed ONLY when k0=a0=0(core0_ID))
+	 */
+	lw	t0, __all_cores_are_equal
+	beq	a0, t0, core_0_cont1
+	 nop
+
+	/* other cores look up addr from dram */
+        /* DRAM controller already set up by first core */
+        li      a1, (BOOT_VECTOR_NUM_WORDS * 4)
+        mul     a0, a0, a1
+
+        /* Now find out the boot vector base address from the moveable boot
+         * bus region.
+         */
+
+        /* Get the address of the boot bus moveable region */
+        PTR_LI     t8, OCTEON_MIO_BOOT_BASE
+        ld      t9, OCTEON_MIO_BOOT_LOC_CFG0_OFF(t8)
+        /* Make sure it's enabled */
+        bbit0   t9, 31, invalid_boot_vector
+         dext   t9, t9, 3, 24
+        dsll    t9, t9, 7
+        /* Make address XKPHYS */
+	li	t0, 1
+	dins	t9, t0, 63, 1
+
+        ld      t0, OCTEON_BOOT_MOVEABLE_MAGIC_OFFSET(t9)
+        dli     t1, OCTEON_BOOT_MOVEABLE_MAGIC1
+        bne     t0, t1, invalid_boot_vector
+         nop
+
+        /* Load base address of boot vector table */
+        ld      t0, OCTEON_BOOT_VECTOR_MOVEABLE_OFFSET(t9)
+        /* Add offset for core */
+        daddu   a1, t0, a0
+
+	mfc0	v0, CP0_STATUS
+	move	v1, v0
+	ins	v1, zero, 19, 1		/* Clear NMI bit */
+	mtc0	v1, CP0_STATUS
+
+        /* Get app start function address */
+        lw      t9, 8(a1)
+        beqz    t9, invalid_boot_vector
+         nop
+
+        j       t9
+         lw      k0, 12(a1)      /* Load global data (deprecated) */
+
+invalid_boot_vector:
+        wait
+        b       invalid_boot_vector
+         nop
+
+__all_cores_are_equal:
+	/* The following .word tell if 'all_cores_are_equal' or core0 is special
+	 * By default (for the first execution) the core0 should be special,
+	 * in order to behave like the old(existing not-modified) bootloader
+	 * and run the bootloader on core 0 to follow the existing design.
+	 * However after that we make 'all_cores_equal' which allows to run SE
+	 * applications on core0 like on any other core. NOTE that value written
+	 * to '__all_cores_are_equal' should not match any core ID.
+	 */
+	.word 	0
+
+core_0_cont1:
+	li	t0, 0xffffffff
+	sw	t0, __all_cores_are_equal
+	/* From here on, only core 0 runs, other cores have branched
+	 * away.
+	 */
+#ifdef CONFIG_MIPS_INIT_STACK_IN_SRAM
+	/* Set up initial stack and global data */
+	setup_stack_gd
+# ifdef CONFIG_DEBUG_UART
+	PTR_LA	t9, debug_uart_init
+	jalr	t9
+	 nop
+# endif
+#endif
+	move	a0, zero		# a0 <-- boot_flags = 0
+	PTR_LA	t9, board_init_f
+
+	jr	t9
+	 move	ra, zero
+	END(_start)
+
+	.balign	8
+	.globl	single_tlb_setup
+	.ent	single_tlb_setup
+	/* Sets up a single TLB entry.	Virtual/physical addresses
+	 * must be properly aligned.
+	 * a0  Virtual address
+	 * a1  Physical address
+	 * a2  page (_not_ mapping) size
+	 */
+single_tlb_setup:
+	/* Determine the number of TLB entries available, and
+	 * use the top one.
+	 */
+	mfc0	a3, CP0_CONFIG1
+	dext	a3, a3, 25, 6		/* a3 now has the max mmu entry index */
+	mfc0	a5, CP0_CONFIG3		/* Check if config4 reg present */
+	bbit0	a5, 31, single_tlb_setup_cont
+	 nop
+	mfc0	a5, CP0_CONFIG4
+	bbit0	a5, 14, single_tlb_setup_cont	/* check config4[MMUExtDef] */
+	 nop
+	/* append config4[MMUSizeExt] to most significant bit of
+	 * config1[MMUSize-1]
+	 */
+	dins	a3, a5, 6, 8
+	and	a3, a3, 0x3fff	/* a3 now includes max entries for cn6xxx */
+
+single_tlb_setup_cont:
+
+	/* Format physical address for entry low */
+	nop
+	dsrl	a1, a1, 12
+	dsll	a1, a1, 6
+	ori	a1, a1, 0x7	/* set DVG bits */
+
+	move	a4, a2
+	daddu	a5, a4, a4	/* mapping size */
+	dsll	a6, a4, 1
+	daddiu	a6, a6, -1	/* pagemask */
+	dsrl	a4, a4, 6	/* adjust for adding with entrylo */
+
+	/* Now set up mapping */
+	mtc0	a6, CP0_PAGEMASK
+	mtc0	a3, CP0_INDEX
+
+	dmtc0	a1, CP0_ENTRYLO0
+	daddu	a1, a1, a4
+
+	dmtc0	a1, CP0_ENTRYLO1
+	daddu	a1, a1, a4
+
+	dmtc0	a0, CP0_ENTRYHI
+	daddu	a0, a0, a5
+
+	ehb
+	tlbwi
+	jr  ra
+	 nop
+	.end   single_tlb_setup
+
+
+/**
+ * This code is moved to a movable boot bus region,
+ * and it is responsible for changing the flash mappings and
+ * jumping to run from the TLB mapped address.
+ *
+ * @param a0	New address for boot bus region 0
+ * @param a1	Address to branch to afterwards
+ * @param a2	Address of MIO_BOOT_REG_CFG0
+ */
+	.balign	8
+change_boot_mappings:
+	sd	a0, 0(a2)
+	sync
+	j a1	    /* Jump to new TLB mapped location */
+	 synci	0(zero)
+
+/* If we need a large stack, allocate it here. */
+#if CONFIG_OCTEON_BIG_STACK_SIZE
+	/* Allocate the stack here so it's in L2 cache or DRAM */
+	.balign	16
+big_stack_end:
+	.skip	CONFIG_OCTEON_BIG_STACK_SIZE, 0
+big_stack_start:
+	.dword	0
+#endif
