diff --git a/arch/arm/cpu/armv7/cache_v7.c b/arch/arm/cpu/armv7/cache_v7.c
index 1b4e808..5f6d039 100644
--- a/arch/arm/cpu/armv7/cache_v7.c
+++ b/arch/arm/cpu/armv7/cache_v7.c
@@ -297,6 +297,12 @@ void arm_init_before_mmu(void)
 	v7_inval_tlb();
 }
 
+void mmu_page_table_flush(unsigned long start, unsigned long stop)
+{
+	flush_dcache_range(start, stop);
+	v7_inval_tlb();
+}
+
 /*
  * Flush range from all levels of d-cache/unified-cache used:
  * Affects the range [start, start + size - 1]
@@ -329,6 +335,11 @@ void arm_init_before_mmu(void)
 void  flush_cache(unsigned long start, unsigned long size)
 {
 }
+
+void mmu_page_table_flush(unsigned long start, unsigned long stop)
+{
+}
+
 #endif /* #ifndef CONFIG_SYS_DCACHE_OFF */
 
 #ifndef CONFIG_SYS_ICACHE_OFF
diff --git a/arch/arm/include/asm/system.h b/arch/arm/include/asm/system.h
index 2b28a26..9a05211 100644
--- a/arch/arm/include/asm/system.h
+++ b/arch/arm/include/asm/system.h
@@ -75,6 +75,36 @@ static inline void set_cr(unsigned int val)
 	isb();
 }
 
+/* options available for data cache on each page */
+enum dcache_option {
+	DCACHE_OFF = 0x12,
+	DCACHE_WRITETHROUGH = 0x1a,
+	DCACHE_WRITEBACK = 0x1e,
+};
+
+/* Size of an MMU section */
+enum {
+	MMU_SECTION_SHIFT	= 20,
+	MMU_SECTION_SIZE	= 1 << MMU_SECTION_SHIFT,
+};
+
+/**
+ * Change the cache settings for a region.
+ *
+ * \param start		start address of memory region to change
+ * \param size		size of memory region to change
+ * \param option	dcache option to select
+ */
+void mmu_set_region_dcache(u32 start, int size, enum dcache_option option);
+
+/**
+ * Register an update to the page tables, and flush the TLB
+ *
+ * \param start		start address of update in page table
+ * \param stop		stop address of update in page table
+ */
+void mmu_page_table_flush(unsigned long start, unsigned long stop);
+
 #endif /* __ASSEMBLY__ */
 
 #define arch_align_stack(x) (x)
diff --git a/arch/arm/lib/cache-cp15.c b/arch/arm/lib/cache-cp15.c
index 939de10..c9bee42 100644
--- a/arch/arm/lib/cache-cp15.c
+++ b/arch/arm/lib/cache-cp15.c
@@ -26,12 +26,6 @@
 
 #if !(defined(CONFIG_SYS_ICACHE_OFF) && defined(CONFIG_SYS_DCACHE_OFF))
 
-#if defined(CONFIG_SYS_ARM_CACHE_WRITETHROUGH)
-#define CACHE_SETUP	0x1a
-#else
-#define CACHE_SETUP	0x1e
-#endif
-
 DECLARE_GLOBAL_DATA_PTR;
 
 void __arm_init_before_mmu(void)
@@ -50,9 +44,40 @@ static void cp_delay (void)
 	asm volatile("" : : : "memory");
 }
 
-static inline void dram_bank_mmu_setup(int bank)
+void set_section_dcache(int section, enum dcache_option option)
 {
 	u32 *page_table = (u32 *)gd->tlb_addr;
+	u32 value;
+
+	value = (section << MMU_SECTION_SHIFT) | (3 << 10);
+	value |= option;
+	page_table[section] = value;
+}
+
+void __mmu_page_table_flush(unsigned long start, unsigned long stop)
+{
+	debug("%s: Warning: not implemented\n", __func__);
+}
+
+void mmu_page_table_flush(unsigned long start, unsigned long stop)
+	__attribute__((weak, alias("__mmu_page_table_flush")));
+
+void mmu_set_region_dcache(u32 start, int size, enum dcache_option option)
+{
+	u32 *page_table = (u32 *)gd->tlb_addr;
+	u32 upto, end;
+
+	end = ALIGN(start + size, MMU_SECTION_SIZE) >> MMU_SECTION_SHIFT;
+	start = start >> MMU_SECTION_SHIFT;
+	debug("mmu_set_region_dcache start=%x, size=%x, option=%d\n",
+	      start, size, option);
+	for (upto = start; upto < end; upto++)
+		set_section_dcache(upto, option);
+	mmu_page_table_flush((u32)&page_table[start], (u32)&page_table[end]);
+}
+
+static inline void dram_bank_mmu_setup(int bank)
+{
 	bd_t *bd = gd->bd;
 	int	i;
 
@@ -60,21 +85,24 @@ static inline void dram_bank_mmu_setup(int bank)
 	for (i = bd->bi_dram[bank].start >> 20;
 	     i < (bd->bi_dram[bank].start + bd->bi_dram[bank].size) >> 20;
 	     i++) {
-		page_table[i] = i << 20 | (3 << 10) | CACHE_SETUP;
+#if defined(CONFIG_SYS_ARM_CACHE_WRITETHROUGH)
+		set_section_dcache(i, DCACHE_WRITETHROUGH);
+#else
+		set_section_dcache(i, DCACHE_WRITEBACK);
+#endif
 	}
 }
 
 /* to activate the MMU we need to set up virtual memory: use 1M areas */
 static inline void mmu_setup(void)
 {
-	u32 *page_table = (u32 *)gd->tlb_addr;
 	int i;
 	u32 reg;
 
 	arm_init_before_mmu();
 	/* Set up an identity-mapping for all 4GB, rw for everyone */
 	for (i = 0; i < 4096; i++)
-		page_table[i] = i << 20 | (3 << 10) | 0x12;
+		set_section_dcache(i, DCACHE_OFF);
 
 	for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
 		dram_bank_mmu_setup(i);
@@ -82,7 +110,7 @@ static inline void mmu_setup(void)
 
 	/* Copy the page table address to cp15 */
 	asm volatile("mcr p15, 0, %0, c2, c0, 0"
-		     : : "r" (page_table) : "memory");
+		     : : "r" (gd->tlb_addr) : "memory");
 	/* Set the access control to all-supervisor */
 	asm volatile("mcr p15, 0, %0, c3, c0, 0"
 		     : : "r" (~0));
