/* MN10300 CPU core caching routines, using direct tag flushing
 *
 * 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

	# read the addresses tagged in the cache's tag RAM and attempt to flush
	# those addresses specifically
	# - we rely on the hardware to filter out invalid tag entry addresses
	mov	DCACHE_TAG(0,0),a0		# dcache tag RAM access address
	mov	DCACHE_PURGE(0,0),a1		# dcache purge request address
	mov	L1_CACHE_NWAYS*L1_CACHE_NENTRIES,d1  # total number of entries

mn10300_local_dcache_flush_loop:
	mov	(a0),d0
	and	L1_CACHE_TAG_MASK,d0
	or	L1_CACHE_TAG_VALID,d0		# retain valid entries in the
						# cache
	mov	d0,(a1)				# conditional purge

	add	L1_CACHE_BYTES,a0
	add	L1_CACHE_BYTES,a1
	add	-1,d1
	bne	mn10300_local_dcache_flush_loop

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],(sp)

	movhu	(CHCTR),d2
	btst	CHCTR_DCEN,d2
	beq	mn10300_local_dcache_flush_range_end

	sub	d0,d1,a0
	cmp	MN10300_DCACHE_FLUSH_BORDER,a0
	ble	1f

	movm	(sp),[d2]
	bra	mn10300_local_dcache_flush
1:

	# round start addr down
	and	L1_CACHE_TAG_MASK,d0
	mov	d0,a1

	add	L1_CACHE_BYTES,d1			# round end addr up
	and	L1_CACHE_TAG_MASK,d1

	# write a request to flush all instances of an address from the cache
	mov	DCACHE_PURGE(0,0),a0
	mov	a1,d0
	and	L1_CACHE_TAG_ENTRY,d0
	add	d0,a0				# starting dcache purge control
						# reg address

	sub	a1,d1
	lsr	L1_CACHE_SHIFT,d1		# total number of entries to
						# examine

	or	L1_CACHE_TAG_VALID,a1		# retain valid entries in the
						# cache

mn10300_local_dcache_flush_range_loop:
	mov	a1,(L1_CACHE_WAYDISP*0,a0)	# conditionally purge this line
						# all ways

	add	L1_CACHE_BYTES,a0
	add	L1_CACHE_BYTES,a1
	and	~L1_CACHE_WAYDISP,a0		# make sure way stay on way 0
	add	-1,d1
	bne	mn10300_local_dcache_flush_range_loop

mn10300_local_dcache_flush_range_end:
	ret	[d2],4

	.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	L1_CACHE_NENTRIES,d1
	clr	a1

mn10300_local_dcache_flush_inv_loop:
	mov	(DCACHE_PURGE_WAY0(0),a1),d0	# unconditional purge
	mov	(DCACHE_PURGE_WAY1(0),a1),d0	# unconditional purge
	mov	(DCACHE_PURGE_WAY2(0),a1),d0	# unconditional purge
	mov	(DCACHE_PURGE_WAY3(0),a1),d0	# unconditional purge

	add	L1_CACHE_BYTES,a1
	add	-1,d1
	bne	mn10300_local_dcache_flush_inv_loop

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],(sp)

	movhu	(CHCTR),d2
	btst	CHCTR_DCEN,d2
	beq	mn10300_local_dcache_flush_inv_range_end

	sub	d0,d1,a0
	cmp	MN10300_DCACHE_FLUSH_INV_BORDER,a0
	ble	1f

	movm	(sp),[d2]
	bra	mn10300_local_dcache_flush_inv
1:

	and	L1_CACHE_TAG_MASK,d0		# round start addr down
	mov	d0,a1

	add	L1_CACHE_BYTES,d1		# round end addr up
	and	L1_CACHE_TAG_MASK,d1

	# write a request to flush and invalidate all instances of an address
	# from the cache
	mov	DCACHE_PURGE(0,0),a0
	mov	a1,d0
	and	L1_CACHE_TAG_ENTRY,d0
	add	d0,a0				# starting dcache purge control
						# reg address

	sub	a1,d1
	lsr	L1_CACHE_SHIFT,d1		# total number of entries to
						# examine

mn10300_local_dcache_flush_inv_range_loop:
	mov	a1,(L1_CACHE_WAYDISP*0,a0)	# conditionally purge this line
						# in all ways

	add	L1_CACHE_BYTES,a0
	add	L1_CACHE_BYTES,a1
	and	~L1_CACHE_WAYDISP,a0		# make sure way stay on way 0
	add	-1,d1
	bne	mn10300_local_dcache_flush_inv_range_loop

mn10300_local_dcache_flush_inv_range_end:
	ret	[d2],4
	.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
