diff --git a/arch/mips/cpu/start.S b/arch/mips/cpu/start.S
index 8f85ede9ad2b55f9fec68fd4f8f8138ea329afcc..cbc02fa9fe0ddce156be9ca808ac5be83ddde2aa 100644
--- a/arch/mips/cpu/start.S
+++ b/arch/mips/cpu/start.S
@@ -223,12 +223,9 @@ ENTRY(relocate_code)
 	PTR_LI	t0, CONFIG_SYS_MONITOR_BASE
 	PTR_SUB	s1, s2, t0		# s1 <-- relocation offset
 
-	PTR_LA	t3, in_ram
-	PTR_L	t2, -(3 * PTRSIZE)(t3)	# t2 <-- __image_copy_end
+	PTR_LA	t2, __image_copy_end
 	move	t1, a2
 
-	PTR_ADD	gp, s1			# adjust gp
-
 	/*
 	 * t0 = source address
 	 * t1 = target address
@@ -241,32 +238,14 @@ ENTRY(relocate_code)
 	blt	t0, t2, 1b
 	 PTR_ADDU t1, PTRSIZE
 
-	/* If caches were enabled, we would have to flush them here. */
-	PTR_SUB	a1, t1, s2		# a1 <-- size
-	PTR_LA	t9, flush_cache
-	jalr	t9
-	 move	a0, s2			# a0 <-- destination address
-
-	/* Jump to where we've relocated ourselves */
-	PTR_ADDIU t0, s2, in_ram - _start
-	jr	t0
-	 nop
-
-	PTR	__rel_dyn_end
-	PTR	__rel_dyn_start
-	PTR	__image_copy_end
-	PTR	_GLOBAL_OFFSET_TABLE_
-	PTR	num_got_entries
-
-in_ram:
 	/*
 	 * Now we want to update GOT.
 	 *
 	 * GOT[0] is reserved. GOT[1] is also reserved for the dynamic object
 	 * generated by GNU ld. Skip these reserved entries from relocation.
 	 */
-	PTR_L	t3, -(1 * PTRSIZE)(t0)	# t3 <-- num_got_entries
-	PTR_L	t8, -(2 * PTRSIZE)(t0)	# t8 <-- _GLOBAL_OFFSET_TABLE_
+	PTR_LA	t3, num_got_entries
+	PTR_LA	t8, _GLOBAL_OFFSET_TABLE_
 	PTR_ADD	t8, s1			# t8 now holds relocated _G_O_T_
 	PTR_ADDIU t8, t8, 2 * PTRSIZE	# skipping first two entries
 	PTR_LI	t2, 2
@@ -281,8 +260,8 @@ in_ram:
 	 PTR_ADDIU t8, PTRSIZE
 
 	/* Update dynamic relocations */
-	PTR_L	t1, -(4 * PTRSIZE)(t0)	# t1 <-- __rel_dyn_start
-	PTR_L	t2, -(5 * PTRSIZE)(t0)	# t2 <-- __rel_dyn_end
+	PTR_LA	t1, __rel_dyn_start
+	PTR_LA	t2, __rel_dyn_end
 
 	b	2f			# skip first reserved entry
 	 PTR_ADDIU t1, 2 * PTRSIZE
@@ -306,6 +285,20 @@ in_ram:
 	blt	t1, t2, 1b
 	 PTR_ADDIU t1, 2 * PTRSIZE	# each rel.dyn entry is 2*PTRSIZE bytes
 
+	/*
+	 * Flush caches to ensure our newly modified instructions are visible
+	 * to the instruction cache. We're still running with the old GOT, so
+	 * apply the reloc offset to the start address.
+	 */
+	PTR_LA	a0, __text_start
+	PTR_LA	a1, __text_end
+	PTR_SUB	a1, a1, a0
+	PTR_LA	t9, flush_cache
+	jalr	t9
+	 PTR_ADD	a0, s1
+
+	PTR_ADD	gp, s1			# adjust gp
+
 	/*
 	 * Clear BSS
 	 *
diff --git a/arch/mips/cpu/u-boot.lds b/arch/mips/cpu/u-boot.lds
index 7d71c11ae4c6cfa5ff1aa22df51a20d4ceb16bc0..0129c996118d621e913b097d433637e6503d25d0 100644
--- a/arch/mips/cpu/u-boot.lds
+++ b/arch/mips/cpu/u-boot.lds
@@ -19,7 +19,9 @@ SECTIONS
 
 	. = ALIGN(4);
 	.text : {
+		__text_start = .;
 		*(.text*)
+		__text_end = .;
 	}
 
 	. = ALIGN(4);