sh: Preparation for uncached jumps through PMB.

Presently most of the 29-bit physical parts do P1/P2 segmentation
with a 1:1 cached/uncached mapping, jumping between the two to
control the caching behaviour. This provides the basic infrastructure
to maintain this behaviour on 32-bit physical parts that don't map
P1/P2 at all, using a shiny new linker section and corresponding
fixmap entry.

Signed-off-by: Stuart Menefy <stuart.menefy@st.com>
Signed-off-by: Paul Mundt <lethal@linux-sh.org>
diff --git a/arch/sh/kernel/cpu/init.c b/arch/sh/kernel/cpu/init.c
index fd1688e6..0f0c76a 100644
--- a/arch/sh/kernel/cpu/init.c
+++ b/arch/sh/kernel/cpu/init.c
@@ -64,11 +64,11 @@
  * Generic first-level cache init
  */
 #ifdef CONFIG_SUPERH32
-static void __init cache_init(void)
+static void __uses_jump_to_uncached cache_init(void)
 {
 	unsigned long ccr, flags;
 
-	jump_to_P2();
+	jump_to_uncached();
 	ccr = ctrl_inl(CCR);
 
 	/*
@@ -145,7 +145,7 @@
 #endif
 
 	ctrl_outl(flags, CCR);
-	back_to_P1();
+	back_to_cached();
 }
 #else
 #define cache_init()	do { } while (0)
diff --git a/arch/sh/kernel/cpu/sh3/probe.c b/arch/sh/kernel/cpu/sh3/probe.c
index bf579e0..22070e4 100644
--- a/arch/sh/kernel/cpu/sh3/probe.c
+++ b/arch/sh/kernel/cpu/sh3/probe.c
@@ -16,11 +16,11 @@
 #include <asm/cache.h>
 #include <asm/io.h>
 
-int __init detect_cpu_and_cache_system(void)
+int __uses_jump_to_uncached detect_cpu_and_cache_system(void)
 {
 	unsigned long addr0, addr1, data0, data1, data2, data3;
 
-	jump_to_P2();
+	jump_to_uncached();
 	/*
 	 * Check if the entry shadows or not.
 	 * When shadowed, it's 128-entry system.
@@ -48,7 +48,7 @@
 	ctrl_outl(data0&~SH_CACHE_VALID, addr0);
 	ctrl_outl(data2&~SH_CACHE_VALID, addr1);
 
-	back_to_P1();
+	back_to_cached();
 
 	boot_cpu_data.dcache.ways		= 4;
 	boot_cpu_data.dcache.entry_shift	= 4;
diff --git a/arch/sh/kernel/vmlinux_32.lds.S b/arch/sh/kernel/vmlinux_32.lds.S
index 0956fb3..50c69c1 100644
--- a/arch/sh/kernel/vmlinux_32.lds.S
+++ b/arch/sh/kernel/vmlinux_32.lds.S
@@ -43,6 +43,15 @@
 	NOTES
 	RO_DATA(PAGE_SIZE)
 
+	/*
+	 * Code which must be executed uncached and the associated data
+	 */
+	. = ALIGN(PAGE_SIZE);
+	__uncached_start = .;
+	.uncached.text : { *(.uncached.text) }
+	.uncached.data : { *(.uncached.data) }
+	__uncached_end = .;
+
 	. = ALIGN(THREAD_SIZE);
 	.data : {			/* Data */
 		*(.data.init_task)
diff --git a/arch/sh/mm/cache-debugfs.c b/arch/sh/mm/cache-debugfs.c
index de6d2c9..db6d950 100644
--- a/arch/sh/mm/cache-debugfs.c
+++ b/arch/sh/mm/cache-debugfs.c
@@ -22,7 +22,8 @@
 	CACHE_TYPE_UNIFIED,
 };
 
-static int cache_seq_show(struct seq_file *file, void *iter)
+static int __uses_jump_to_uncached cache_seq_show(struct seq_file *file,
+						  void *iter)
 {
 	unsigned int cache_type = (unsigned int)file->private;
 	struct cache_info *cache;
@@ -34,11 +35,11 @@
 	 * Go uncached immediately so we don't skew the results any
 	 * more than we already are..
 	 */
-	jump_to_P2();
+	jump_to_uncached();
 
 	ccr = ctrl_inl(CCR);
 	if ((ccr & CCR_CACHE_ENABLE) == 0) {
-		back_to_P1();
+		back_to_cached();
 
 		seq_printf(file, "disabled\n");
 		return 0;
@@ -104,7 +105,7 @@
 		addrstart += cache->way_incr;
 	}
 
-	back_to_P1();
+	back_to_cached();
 
 	return 0;
 }
diff --git a/arch/sh/mm/cache-sh4.c b/arch/sh/mm/cache-sh4.c
index 226b190..43d7ff6b 100644
--- a/arch/sh/mm/cache-sh4.c
+++ b/arch/sh/mm/cache-sh4.c
@@ -190,7 +190,7 @@
  * .. which happens to be the same behavior as flush_icache_range().
  * So, we simply flush out a line.
  */
-void flush_cache_sigtramp(unsigned long addr)
+void __uses_jump_to_uncached flush_cache_sigtramp(unsigned long addr)
 {
 	unsigned long v, index;
 	unsigned long flags;
@@ -205,13 +205,13 @@
 			(v & boot_cpu_data.icache.entry_mask);
 
 	local_irq_save(flags);
-	jump_to_P2();
+	jump_to_uncached();
 
 	for (i = 0; i < boot_cpu_data.icache.ways;
 	     i++, index += boot_cpu_data.icache.way_incr)
 		ctrl_outl(0, index);	/* Clear out Valid-bit */
 
-	back_to_P1();
+	back_to_cached();
 	wmb();
 	local_irq_restore(flags);
 }
@@ -256,12 +256,12 @@
 }
 
 /* TODO: Selective icache invalidation through IC address array.. */
-static inline void flush_icache_all(void)
+static inline void __uses_jump_to_uncached flush_icache_all(void)
 {
 	unsigned long flags, ccr;
 
 	local_irq_save(flags);
-	jump_to_P2();
+	jump_to_uncached();
 
 	/* Flush I-cache */
 	ccr = ctrl_inl(CCR);
@@ -269,11 +269,11 @@
 	ctrl_outl(ccr, CCR);
 
 	/*
-	 * back_to_P1() will take care of the barrier for us, don't add
+	 * back_to_cached() will take care of the barrier for us, don't add
 	 * another one!
 	 */
 
-	back_to_P1();
+	back_to_cached();
 	local_irq_restore(flags);
 }
 
diff --git a/arch/sh/mm/cache-sh7705.c b/arch/sh/mm/cache-sh7705.c
index 4896d73..22dacc7 100644
--- a/arch/sh/mm/cache-sh7705.c
+++ b/arch/sh/mm/cache-sh7705.c
@@ -71,7 +71,7 @@
 /*
  * Writeback&Invalidate the D-cache of the page
  */
-static void __flush_dcache_page(unsigned long phys)
+static void __uses_jump_to_uncached __flush_dcache_page(unsigned long phys)
 {
 	unsigned long ways, waysize, addrstart;
 	unsigned long flags;
@@ -92,7 +92,7 @@
 	 * possible.
 	 */
 	local_irq_save(flags);
-	jump_to_P2();
+	jump_to_uncached();
 
 	ways = current_cpu_data.dcache.ways;
 	waysize = current_cpu_data.dcache.sets;
@@ -118,7 +118,7 @@
 		addrstart += current_cpu_data.dcache.way_incr;
 	} while (--ways);
 
-	back_to_P1();
+	back_to_cached();
 	local_irq_restore(flags);
 }
 
@@ -132,15 +132,15 @@
 		__flush_dcache_page(PHYSADDR(page_address(page)));
 }
 
-void flush_cache_all(void)
+void __uses_jump_to_uncached flush_cache_all(void)
 {
 	unsigned long flags;
 
 	local_irq_save(flags);
-	jump_to_P2();
+	jump_to_uncached();
 
 	cache_wback_all();
-	back_to_P1();
+	back_to_cached();
 	local_irq_restore(flags);
 }
 
diff --git a/arch/sh/mm/init.c b/arch/sh/mm/init.c
index 79c3097..094225e 100644
--- a/arch/sh/mm/init.c
+++ b/arch/sh/mm/init.c
@@ -23,6 +23,7 @@
 
 DEFINE_PER_CPU(struct mmu_gather, mmu_gathers);
 pgd_t swapper_pg_dir[PTRS_PER_PGD];
+unsigned long cached_to_uncached = 0;
 
 void show_mem(void)
 {
@@ -99,7 +100,8 @@
 
 	set_pte(pte, pfn_pte(phys >> PAGE_SHIFT, prot));
 
-	flush_tlb_one(get_asid(), addr);
+	if (cached_to_uncached)
+		flush_tlb_one(get_asid(), addr);
 }
 
 /*
@@ -164,6 +166,18 @@
 	}
 
 	free_area_init_nodes(max_zone_pfns);
+
+	/* Set up the uncached fixmap */
+	set_fixmap_nocache(FIX_UNCACHED, __pa(&__uncached_start));
+
+#ifdef CONFIG_29BIT
+	/*
+	 * Handle trivial transitions between cached and uncached
+	 * segments, making use of the 1:1 mapping relationship in
+	 * 512MB lowmem.
+	 */
+	cached_to_uncached = P2SEG - P1SEG;
+#endif
 }
 
 static struct kcore_list kcore_mem, kcore_vmalloc;
diff --git a/arch/sh/mm/pmb.c b/arch/sh/mm/pmb.c
index ef6ab39..ab81c60 100644
--- a/arch/sh/mm/pmb.c
+++ b/arch/sh/mm/pmb.c
@@ -163,18 +163,18 @@
 	return 0;
 }
 
-int set_pmb_entry(struct pmb_entry *pmbe)
+int __uses_jump_to_uncached set_pmb_entry(struct pmb_entry *pmbe)
 {
 	int ret;
 
-	jump_to_P2();
+	jump_to_uncached();
 	ret = __set_pmb_entry(pmbe->vpn, pmbe->ppn, pmbe->flags, &pmbe->entry);
-	back_to_P1();
+	back_to_cached();
 
 	return ret;
 }
 
-void clear_pmb_entry(struct pmb_entry *pmbe)
+void __uses_jump_to_uncached clear_pmb_entry(struct pmb_entry *pmbe)
 {
 	unsigned int entry = pmbe->entry;
 	unsigned long addr;
@@ -188,7 +188,7 @@
 		     entry >= NR_PMB_ENTRIES))
 		return;
 
-	jump_to_P2();
+	jump_to_uncached();
 
 	/* Clear V-bit */
 	addr = mk_pmb_addr(entry);
@@ -197,7 +197,7 @@
 	addr = mk_pmb_data(entry);
 	ctrl_outl(ctrl_inl(addr) & ~PMB_V, addr);
 
-	back_to_P1();
+	back_to_cached();
 
 	clear_bit(entry, &pmb_map);
 }
@@ -302,7 +302,7 @@
 	pmbe->entry = PMB_NO_ENTRY;
 }
 
-static int __init pmb_init(void)
+static int __uses_jump_to_uncached pmb_init(void)
 {
 	unsigned int nr_entries = ARRAY_SIZE(pmb_init_map);
 	unsigned int entry, i;
@@ -312,7 +312,7 @@
 	pmb_cache = kmem_cache_create("pmb", sizeof(struct pmb_entry), 0,
 				      SLAB_PANIC, pmb_cache_ctor);
 
-	jump_to_P2();
+	jump_to_uncached();
 
 	/*
 	 * Ordering is important, P2 must be mapped in the PMB before we
@@ -335,7 +335,7 @@
 	i |= MMUCR_TI;
 	ctrl_outl(i, MMUCR);
 
-	back_to_P1();
+	back_to_cached();
 
 	return 0;
 }
diff --git a/arch/sh/mm/tlb-sh4.c b/arch/sh/mm/tlb-sh4.c
index 2d1dd60..f0c7b73 100644
--- a/arch/sh/mm/tlb-sh4.c
+++ b/arch/sh/mm/tlb-sh4.c
@@ -79,7 +79,8 @@
 	local_irq_restore(flags);
 }
 
-void local_flush_tlb_one(unsigned long asid, unsigned long page)
+void __uses_jump_to_uncached local_flush_tlb_one(unsigned long asid,
+						 unsigned long page)
 {
 	unsigned long addr, data;
 
@@ -91,7 +92,7 @@
 	 */
 	addr = MMU_UTLB_ADDRESS_ARRAY | MMU_PAGE_ASSOC_BIT;
 	data = page | asid; /* VALID bit is off */
-	jump_to_P2();
+	jump_to_uncached();
 	ctrl_outl(data, addr);
-	back_to_P1();
+	back_to_cached();
 }
diff --git a/include/asm-sh/fixmap.h b/include/asm-sh/fixmap.h
index 09463cd..721fcc4 100644
--- a/include/asm-sh/fixmap.h
+++ b/include/asm-sh/fixmap.h
@@ -49,6 +49,7 @@
 #define FIX_N_COLOURS 16
 	FIX_CMAP_BEGIN,
 	FIX_CMAP_END = FIX_CMAP_BEGIN + FIX_N_COLOURS,
+	FIX_UNCACHED,
 #ifdef CONFIG_HIGHMEM
 	FIX_KMAP_BEGIN,	/* reserved pte's for temporary kernel mappings */
 	FIX_KMAP_END = FIX_KMAP_BEGIN+(KM_TYPE_NR*NR_CPUS)-1,
diff --git a/include/asm-sh/sections.h b/include/asm-sh/sections.h
index bd9cbc9..8f8f4ad 100644
--- a/include/asm-sh/sections.h
+++ b/include/asm-sh/sections.h
@@ -4,6 +4,7 @@
 #include <asm-generic/sections.h>
 
 extern long __machvec_start, __machvec_end;
+extern char __uncached_start, __uncached_end;
 extern char _ebss[];
 
 #endif /* __ASM_SH_SECTIONS_H */
diff --git a/include/asm-sh/system.h b/include/asm-sh/system.h
index 969f3d4..9bda8d0 100644
--- a/include/asm-sh/system.h
+++ b/include/asm-sh/system.h
@@ -144,6 +144,8 @@
 #define instruction_size(insn)	(4)
 #endif
 
+extern unsigned long cached_to_uncached;
+
 /* XXX
  * disable hlt during certain critical i/o operations
  */
diff --git a/include/asm-sh/system_32.h b/include/asm-sh/system_32.h
index ad37e8d..e918bac 100644
--- a/include/asm-sh/system_32.h
+++ b/include/asm-sh/system_32.h
@@ -58,29 +58,31 @@
 	last = __last;						\
 } while (0)
 
+#define __uses_jump_to_uncached __attribute__ ((__section__ (".uncached.text")))
+
 /*
- * Jump to P2 area.
- * When handling TLB or caches, we need to do it from P2 area.
+ * Jump to uncached area.
+ * When handling TLB or caches, we need to do it from an uncached area.
  */
-#define jump_to_P2()			\
-do {					\
-	unsigned long __dummy;		\
-	__asm__ __volatile__(		\
-		"mov.l	1f, %0\n\t"	\
-		"or	%1, %0\n\t"	\
-		"jmp	@%0\n\t"	\
-		" nop\n\t"		\
-		".balign 4\n"		\
-		"1:	.long 2f\n"	\
-		"2:"			\
-		: "=&r" (__dummy)	\
-		: "r" (0x20000000));	\
+#define jump_to_uncached()			\
+do {						\
+	unsigned long __dummy;			\
+						\
+	__asm__ __volatile__(			\
+		"mova	1f, %0\n\t"		\
+		"add	%1, %0\n\t"		\
+		"jmp	@%0\n\t"		\
+		" nop\n\t"			\
+		".balign 4\n"			\
+		"1:"				\
+		: "=&z" (__dummy)		\
+		: "r" (cached_to_uncached));	\
 } while (0)
 
 /*
- * Back to P1 area.
+ * Back to cached area.
  */
-#define back_to_P1()					\
+#define back_to_cached()				\
 do {							\
 	unsigned long __dummy;				\
 	ctrl_barrier();					\
diff --git a/include/asm-sh/system_64.h b/include/asm-sh/system_64.h
index 0e466e9..943acf5 100644
--- a/include/asm-sh/system_64.h
+++ b/include/asm-sh/system_64.h
@@ -32,8 +32,9 @@
 			      &next->thread);			\
 } while (0)
 
-/* No segmentation.. */
-#define jump_to_P2()	do { } while (0)
-#define back_to_P1()	do { } while (0)
+#define __uses_jump_to_uncached
+
+#define jump_to_uncached()	do { } while (0)
+#define back_to_cached()	do { } while (0)
 
 #endif /* __ASM_SH_SYSTEM_64_H */