MN10300: AM34: Add cacheflushing by using the AM34 purge registers

The AM34 CPU core provides an automated way of purging the cache rather than
manually iterating over all the tags in the cache.  Make it possible to use
these.

Signed-off-by: Akira Takeuchi <takeuchi.akr@jp.panasonic.com>
Signed-off-by: Kiyoshi Owada <owada.kiyoshi@jp.panasonic.com>
Signed-off-by: David Howells <dhowells@redhat.com>
diff --git a/arch/mn10300/mm/cache-flush-by-reg.S b/arch/mn10300/mm/cache-flush-by-reg.S
new file mode 100644
index 0000000..1dcae02
--- /dev/null
+++ b/arch/mn10300/mm/cache-flush-by-reg.S
@@ -0,0 +1,308 @@
+/* MN10300 CPU core caching routines, using indirect regs on cache controller
+ *
+ * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#include <linux/sys.h>
+#include <linux/linkage.h>
+#include <asm/smp.h>
+#include <asm/page.h>
+#include <asm/cache.h>
+#include <asm/irqflags.h>
+
+	.am33_2
+
+#ifndef CONFIG_SMP
+	.globl mn10300_dcache_flush
+	.globl mn10300_dcache_flush_page
+	.globl mn10300_dcache_flush_range
+	.globl mn10300_dcache_flush_range2
+	.globl mn10300_dcache_flush_inv
+	.globl mn10300_dcache_flush_inv_page
+	.globl mn10300_dcache_flush_inv_range
+	.globl mn10300_dcache_flush_inv_range2
+
+mn10300_dcache_flush		= mn10300_local_dcache_flush
+mn10300_dcache_flush_page	= mn10300_local_dcache_flush_page
+mn10300_dcache_flush_range	= mn10300_local_dcache_flush_range
+mn10300_dcache_flush_range2	= mn10300_local_dcache_flush_range2
+mn10300_dcache_flush_inv	= mn10300_local_dcache_flush_inv
+mn10300_dcache_flush_inv_page	= mn10300_local_dcache_flush_inv_page
+mn10300_dcache_flush_inv_range	= mn10300_local_dcache_flush_inv_range
+mn10300_dcache_flush_inv_range2	= mn10300_local_dcache_flush_inv_range2
+
+#endif /* !CONFIG_SMP */
+
+###############################################################################
+#
+# void mn10300_local_dcache_flush(void)
+# Flush the entire data cache back to RAM
+#
+###############################################################################
+	ALIGN
+	.globl	mn10300_local_dcache_flush
+        .type	mn10300_local_dcache_flush,@function
+mn10300_local_dcache_flush:
+	movhu	(CHCTR),d0
+	btst	CHCTR_DCEN,d0
+	beq	mn10300_local_dcache_flush_end
+
+	mov	DCPGCR,a0
+
+	LOCAL_CLI_SAVE(d1)
+
+	# wait for busy bit of area purge
+	setlb
+	mov	(a0),d0
+	btst	DCPGCR_DCPGBSY,d0
+	lne
+
+	# set mask
+	clr	d0
+	mov	d0,(DCPGMR)
+
+	# area purge
+	#
+	# DCPGCR = DCPGCR_DCP
+	#
+	mov	DCPGCR_DCP,d0
+	mov	d0,(a0)
+
+	# wait for busy bit of area purge
+	setlb
+	mov	(a0),d0
+	btst	DCPGCR_DCPGBSY,d0
+	lne
+
+	LOCAL_IRQ_RESTORE(d1)
+
+mn10300_local_dcache_flush_end:
+	ret	[],0
+	.size	mn10300_local_dcache_flush,.-mn10300_local_dcache_flush
+
+###############################################################################
+#
+# void mn10300_local_dcache_flush_page(unsigned long start)
+# void mn10300_local_dcache_flush_range(unsigned long start, unsigned long end)
+# void mn10300_local_dcache_flush_range2(unsigned long start, unsigned long size)
+# Flush a range of addresses on a page in the dcache
+#
+###############################################################################
+	ALIGN
+	.globl	mn10300_local_dcache_flush_page
+	.globl	mn10300_local_dcache_flush_range
+	.globl	mn10300_local_dcache_flush_range2
+	.type	mn10300_local_dcache_flush_page,@function
+	.type	mn10300_local_dcache_flush_range,@function
+	.type	mn10300_local_dcache_flush_range2,@function
+mn10300_local_dcache_flush_page:
+	and	~(PAGE_SIZE-1),d0
+	mov	PAGE_SIZE,d1
+mn10300_local_dcache_flush_range2:
+	add	d0,d1
+mn10300_local_dcache_flush_range:
+	movm	[d2,d3,a2],(sp)
+
+	movhu	(CHCTR),d2
+	btst	CHCTR_DCEN,d2
+	beq	mn10300_local_dcache_flush_range_end
+
+	# calculate alignsize
+	#
+	# alignsize = L1_CACHE_BYTES;
+	# for (i = (end - start - 1) / L1_CACHE_BYTES ;  i > 0; i >>= 1)
+	#     alignsize <<= 1;
+	# d2 = alignsize;
+	#
+	mov	L1_CACHE_BYTES,d2
+	sub	d0,d1,d3
+	add	-1,d3
+	lsr	L1_CACHE_SHIFT,d3
+	beq	2f
+1:
+	add     d2,d2
+	lsr     1,d3
+	bne     1b
+2:
+	mov	d1,a1		# a1 = end
+
+	LOCAL_CLI_SAVE(d3)
+	mov	DCPGCR,a0
+
+	# wait for busy bit of area purge
+	setlb
+	mov	(a0),d1
+	btst	DCPGCR_DCPGBSY,d1
+	lne
+
+	# determine the mask
+	mov	d2,d1
+	add	-1,d1
+	not	d1		# d1 = mask = ~(alignsize-1)
+	mov	d1,(DCPGMR)
+
+	and	d1,d0,a2	# a2 = mask & start
+
+dcpgloop:
+	# area purge
+	mov	a2,d0
+	or	DCPGCR_DCP,d0
+	mov	d0,(a0)		# DCPGCR = (mask & start) | DCPGCR_DCP
+
+	# wait for busy bit of area purge
+	setlb
+	mov	(a0),d1
+	btst	DCPGCR_DCPGBSY,d1
+	lne
+
+	# check purge of end address
+	add	d2,a2		# a2 += alignsize
+	cmp	a1,a2		# if (a2 < end) goto dcpgloop
+	bns	dcpgloop
+
+	LOCAL_IRQ_RESTORE(d3)
+
+mn10300_local_dcache_flush_range_end:
+	ret	[d2,d3,a2],12
+
+	.size	mn10300_local_dcache_flush_page,.-mn10300_local_dcache_flush_page
+	.size	mn10300_local_dcache_flush_range,.-mn10300_local_dcache_flush_range
+	.size	mn10300_local_dcache_flush_range2,.-mn10300_local_dcache_flush_range2
+
+###############################################################################
+#
+# void mn10300_local_dcache_flush_inv(void)
+# Flush the entire data cache and invalidate all entries
+#
+###############################################################################
+	ALIGN
+	.globl	mn10300_local_dcache_flush_inv
+	.type	mn10300_local_dcache_flush_inv,@function
+mn10300_local_dcache_flush_inv:
+	movhu	(CHCTR),d0
+	btst	CHCTR_DCEN,d0
+	beq	mn10300_local_dcache_flush_inv_end
+
+	mov	DCPGCR,a0
+
+	LOCAL_CLI_SAVE(d1)
+
+	# wait for busy bit of area purge & invalidate
+	setlb
+	mov	(a0),d0
+	btst	DCPGCR_DCPGBSY,d0
+	lne
+
+	# set the mask to cover everything
+	clr	d0
+	mov	d0,(DCPGMR)
+
+	# area purge & invalidate
+	mov	DCPGCR_DCP|DCPGCR_DCI,d0
+	mov	d0,(a0)
+
+	# wait for busy bit of area purge & invalidate
+	setlb
+	mov	(a0),d0
+	btst	DCPGCR_DCPGBSY,d0
+	lne
+
+	LOCAL_IRQ_RESTORE(d1)
+
+mn10300_local_dcache_flush_inv_end:
+	ret	[],0
+	.size	mn10300_local_dcache_flush_inv,.-mn10300_local_dcache_flush_inv
+
+###############################################################################
+#
+# void mn10300_local_dcache_flush_inv_page(unsigned long start)
+# void mn10300_local_dcache_flush_inv_range(unsigned long start, unsigned long end)
+# void mn10300_local_dcache_flush_inv_range2(unsigned long start, unsigned long size)
+# Flush and invalidate a range of addresses on a page in the dcache
+#
+###############################################################################
+	ALIGN
+	.globl	mn10300_local_dcache_flush_inv_page
+	.globl	mn10300_local_dcache_flush_inv_range
+	.globl	mn10300_local_dcache_flush_inv_range2
+	.type	mn10300_local_dcache_flush_inv_page,@function
+	.type	mn10300_local_dcache_flush_inv_range,@function
+	.type	mn10300_local_dcache_flush_inv_range2,@function
+mn10300_local_dcache_flush_inv_page:
+	and	~(PAGE_SIZE-1),d0
+	mov	PAGE_SIZE,d1
+mn10300_local_dcache_flush_inv_range2:
+	add	d0,d1
+mn10300_local_dcache_flush_inv_range:
+	movm	[d2,d3,a2],(sp)
+
+	movhu	(CHCTR),d2
+	btst	CHCTR_DCEN,d2
+	beq	mn10300_local_dcache_flush_inv_range_end
+
+	# calculate alignsize
+	#
+	# alignsize = L1_CACHE_BYTES;
+	# for (i = (end - start - 1) / L1_CACHE_BYTES; i > 0; i >>= 1)
+	#     alignsize <<= 1;
+	# d2 = alignsize
+	#
+	mov	L1_CACHE_BYTES,d2
+	sub	d0,d1,d3
+	add	-1,d3
+	lsr	L1_CACHE_SHIFT,d3
+	beq	2f
+1:
+	add     d2,d2
+	lsr     1,d3
+	bne     1b
+2:
+	mov	d1,a1		# a1 = end
+
+	LOCAL_CLI_SAVE(d3)
+	mov	DCPGCR,a0
+
+	# wait for busy bit of area purge & invalidate
+	setlb
+	mov	(a0),d1
+	btst	DCPGCR_DCPGBSY,d1
+	lne
+
+	# set the mask
+	mov	d2,d1
+	add	-1,d1
+	not	d1		# d1 = mask = ~(alignsize-1)
+	mov	d1,(DCPGMR)
+
+	and	d1,d0,a2	# a2 = mask & start
+
+dcpgivloop:
+	# area purge & invalidate
+	mov	a2,d0
+	or	DCPGCR_DCP|DCPGCR_DCI,d0
+	mov	d0,(a0)		# DCPGCR = (mask & start)|DCPGCR_DCP|DCPGCR_DCI
+
+	# wait for busy bit of area purge & invalidate
+	setlb
+	mov	(a0),d1
+	btst	DCPGCR_DCPGBSY,d1
+	lne
+
+	# check purge & invalidate of end address
+	add	d2,a2		# a2 += alignsize
+	cmp	a1,a2		# if (a2 < end) goto dcpgivloop
+	bns	dcpgivloop
+
+	LOCAL_IRQ_RESTORE(d3)
+
+mn10300_local_dcache_flush_inv_range_end:
+	ret	[d2,d3,a2],12
+	.size	mn10300_local_dcache_flush_inv_page,.-mn10300_local_dcache_flush_inv_page
+	.size	mn10300_local_dcache_flush_inv_range,.-mn10300_local_dcache_flush_inv_range
+	.size	mn10300_local_dcache_flush_inv_range2,.-mn10300_local_dcache_flush_inv_range2