| /* MN10300 CPU cache invalidation routines, using automatic purge registers | 
 |  * | 
 |  * 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> | 
 | #include <asm/cacheflush.h> | 
 | #include "cache.inc" | 
 |  | 
 | #define mn10300_local_dcache_inv_range_intr_interval \ | 
 | 	+((1 << MN10300_DCACHE_INV_RANGE_INTR_LOG2_INTERVAL) - 1) | 
 |  | 
 | #if mn10300_local_dcache_inv_range_intr_interval > 0xff | 
 | #error MN10300_DCACHE_INV_RANGE_INTR_LOG2_INTERVAL must be 8 or less | 
 | #endif | 
 |  | 
 | 	.am33_2 | 
 |  | 
 | #ifndef CONFIG_SMP | 
 | 	.globl	mn10300_icache_inv | 
 | 	.globl	mn10300_icache_inv_page | 
 | 	.globl	mn10300_icache_inv_range | 
 | 	.globl	mn10300_icache_inv_range2 | 
 | 	.globl	mn10300_dcache_inv | 
 | 	.globl	mn10300_dcache_inv_page | 
 | 	.globl	mn10300_dcache_inv_range | 
 | 	.globl	mn10300_dcache_inv_range2 | 
 |  | 
 | mn10300_icache_inv		= mn10300_local_icache_inv | 
 | mn10300_icache_inv_page		= mn10300_local_icache_inv_page | 
 | mn10300_icache_inv_range	= mn10300_local_icache_inv_range | 
 | mn10300_icache_inv_range2	= mn10300_local_icache_inv_range2 | 
 | mn10300_dcache_inv		= mn10300_local_dcache_inv | 
 | mn10300_dcache_inv_page		= mn10300_local_dcache_inv_page | 
 | mn10300_dcache_inv_range	= mn10300_local_dcache_inv_range | 
 | mn10300_dcache_inv_range2	= mn10300_local_dcache_inv_range2 | 
 |  | 
 | #endif /* !CONFIG_SMP */ | 
 |  | 
 | ############################################################################### | 
 | # | 
 | # void mn10300_local_icache_inv(void) | 
 | # Invalidate the entire icache | 
 | # | 
 | ############################################################################### | 
 | 	ALIGN | 
 | 	.globl	mn10300_local_icache_inv | 
 |         .type	mn10300_local_icache_inv,@function | 
 | mn10300_local_icache_inv: | 
 | 	mov	CHCTR,a0 | 
 |  | 
 | 	movhu	(a0),d0 | 
 | 	btst	CHCTR_ICEN,d0 | 
 | 	beq	mn10300_local_icache_inv_end | 
 |  | 
 | 	invalidate_icache 1 | 
 |  | 
 | mn10300_local_icache_inv_end: | 
 | 	ret	[],0 | 
 | 	.size	mn10300_local_icache_inv,.-mn10300_local_icache_inv | 
 |  | 
 | ############################################################################### | 
 | # | 
 | # void mn10300_local_dcache_inv(void) | 
 | # Invalidate the entire dcache | 
 | # | 
 | ############################################################################### | 
 | 	ALIGN | 
 | 	.globl	mn10300_local_dcache_inv | 
 | 	.type	mn10300_local_dcache_inv,@function | 
 | mn10300_local_dcache_inv: | 
 | 	mov	CHCTR,a0 | 
 |  | 
 | 	movhu	(a0),d0 | 
 | 	btst	CHCTR_DCEN,d0 | 
 | 	beq	mn10300_local_dcache_inv_end | 
 |  | 
 | 	invalidate_dcache 1 | 
 | 	 | 
 | mn10300_local_dcache_inv_end: | 
 | 	ret	[],0 | 
 | 	.size	mn10300_local_dcache_inv,.-mn10300_local_dcache_inv | 
 |  | 
 | ############################################################################### | 
 | # | 
 | # void mn10300_local_dcache_inv_range(unsigned long start, unsigned long end) | 
 | # void mn10300_local_dcache_inv_range2(unsigned long start, unsigned long size) | 
 | # void mn10300_local_dcache_inv_page(unsigned long start) | 
 | # Invalidate a range of addresses on a page in the dcache | 
 | # | 
 | ############################################################################### | 
 | 	ALIGN | 
 | 	.globl	mn10300_local_dcache_inv_page | 
 | 	.globl	mn10300_local_dcache_inv_range | 
 | 	.globl	mn10300_local_dcache_inv_range2 | 
 | 	.type	mn10300_local_dcache_inv_page,@function | 
 | 	.type	mn10300_local_dcache_inv_range,@function | 
 | 	.type	mn10300_local_dcache_inv_range2,@function | 
 | mn10300_local_dcache_inv_page: | 
 | 	and	~(PAGE_SIZE-1),d0 | 
 | 	mov	PAGE_SIZE,d1 | 
 | mn10300_local_dcache_inv_range2: | 
 | 	add	d0,d1 | 
 | mn10300_local_dcache_inv_range: | 
 | 	# If we are in writeback mode we check the start and end alignments, | 
 | 	# and if they're not cacheline-aligned, we must flush any bits outside | 
 | 	# the range that share cachelines with stuff inside the range | 
 | #ifdef CONFIG_MN10300_CACHE_WBACK | 
 | 	btst	~L1_CACHE_TAG_MASK,d0 | 
 | 	bne	1f | 
 | 	btst	~L1_CACHE_TAG_MASK,d1 | 
 | 	beq	2f | 
 | 1: | 
 | 	bra	mn10300_local_dcache_flush_inv_range | 
 | 2: | 
 | #endif /* CONFIG_MN10300_CACHE_WBACK */ | 
 |  | 
 | 	movm	[d2,d3,a2],(sp) | 
 |  | 
 | 	mov	CHCTR,a0 | 
 | 	movhu	(a0),d2 | 
 | 	btst	CHCTR_DCEN,d2 | 
 | 	beq	mn10300_local_dcache_inv_range_end | 
 |  | 
 | 	# round the addresses out to be full cachelines, unless we're in | 
 | 	# writeback mode, in which case we would be in flush and invalidate by | 
 | 	# now | 
 | #ifndef CONFIG_MN10300_CACHE_WBACK | 
 | 	and	L1_CACHE_TAG_MASK,d0	# round start addr down | 
 |  | 
 | 	mov	L1_CACHE_BYTES-1,d2 | 
 | 	add	d2,d1 | 
 | 	and	L1_CACHE_TAG_MASK,d1	# round end addr up | 
 | #endif /* !CONFIG_MN10300_CACHE_WBACK */ | 
 |  | 
 | 	sub	d0,d1,d2		# calculate the total size | 
 | 	mov	d0,a2			# A2 = start address | 
 | 	mov	d1,a1			# A1 = end address | 
 |  | 
 | 	LOCAL_CLI_SAVE(d3) | 
 |  | 
 | 	mov	DCPGCR,a0		# make sure the purger isn't busy | 
 | 	setlb | 
 | 	mov	(a0),d0 | 
 | 	btst	DCPGCR_DCPGBSY,d0 | 
 | 	lne | 
 |  | 
 | 	# skip initial address alignment calculation if address is zero | 
 | 	mov	d2,d1 | 
 | 	cmp	0,a2 | 
 | 	beq	1f | 
 |  | 
 | dcivloop: | 
 | 	/* calculate alignsize | 
 | 	 * | 
 | 	 * alignsize = L1_CACHE_BYTES; | 
 | 	 * while (! start & alignsize) { | 
 | 	 *	alignsize <<=1; | 
 | 	 * } | 
 | 	 * d1 = alignsize; | 
 | 	 */ | 
 | 	mov	L1_CACHE_BYTES,d1 | 
 | 	lsr	1,d1 | 
 | 	setlb | 
 | 	add	d1,d1 | 
 | 	mov	d1,d0 | 
 | 	and	a2,d0 | 
 | 	leq | 
 |  | 
 | 1: | 
 | 	/* calculate invsize | 
 | 	 * | 
 | 	 * if (totalsize > alignsize) { | 
 | 	 *	invsize = alignsize; | 
 | 	 * } else { | 
 | 	 *	invsize = totalsize; | 
 | 	 *	tmp = 0x80000000; | 
 | 	 *	while (! invsize & tmp) { | 
 | 	 *		tmp >>= 1; | 
 | 	 *	} | 
 | 	 *	invsize = tmp; | 
 | 	 * } | 
 | 	 * d1 = invsize | 
 | 	 */ | 
 | 	cmp	d2,d1 | 
 | 	bns	2f | 
 | 	mov	d2,d1 | 
 |  | 
 | 	mov	0x80000000,d0		# start from 31bit=1 | 
 | 	setlb | 
 | 	lsr	1,d0 | 
 | 	mov	d0,e0 | 
 | 	and	d1,e0 | 
 | 	leq | 
 | 	mov	d0,d1 | 
 |  | 
 | 2: | 
 | 	/* set mask | 
 | 	 * | 
 | 	 * mask = ~(invsize-1); | 
 | 	 * DCPGMR = mask; | 
 | 	 */ | 
 | 	mov	d1,d0 | 
 | 	add	-1,d0 | 
 | 	not	d0 | 
 | 	mov	d0,(DCPGMR) | 
 |  | 
 | 	# invalidate area | 
 | 	mov	a2,d0 | 
 | 	or	DCPGCR_DCI,d0 | 
 | 	mov	d0,(a0)			# DCPGCR = (mask & start) | DCPGCR_DCI | 
 |  | 
 | 	setlb				# wait for the purge to complete | 
 | 	mov	(a0),d0 | 
 | 	btst	DCPGCR_DCPGBSY,d0 | 
 | 	lne | 
 |  | 
 | 	sub	d1,d2			# decrease size remaining | 
 | 	add	d1,a2			# increase next start address | 
 |  | 
 | 	/* check invalidating of end address | 
 | 	 * | 
 | 	 * a2 = a2 + invsize | 
 | 	 * if (a2 < end) { | 
 | 	 *     goto dcivloop; | 
 | 	 * } */ | 
 | 	cmp	a1,a2 | 
 | 	bns	dcivloop | 
 |  | 
 | 	LOCAL_IRQ_RESTORE(d3) | 
 |  | 
 | mn10300_local_dcache_inv_range_end: | 
 | 	ret	[d2,d3,a2],12 | 
 | 	.size	mn10300_local_dcache_inv_page,.-mn10300_local_dcache_inv_page | 
 | 	.size	mn10300_local_dcache_inv_range,.-mn10300_local_dcache_inv_range | 
 | 	.size	mn10300_local_dcache_inv_range2,.-mn10300_local_dcache_inv_range2 | 
 |  | 
 | ############################################################################### | 
 | # | 
 | # void mn10300_local_icache_inv_page(unsigned long start) | 
 | # void mn10300_local_icache_inv_range2(unsigned long start, unsigned long size) | 
 | # void mn10300_local_icache_inv_range(unsigned long start, unsigned long end) | 
 | # Invalidate a range of addresses on a page in the icache | 
 | # | 
 | ############################################################################### | 
 | 	ALIGN | 
 | 	.globl	mn10300_local_icache_inv_page | 
 | 	.globl	mn10300_local_icache_inv_range | 
 | 	.globl	mn10300_local_icache_inv_range2 | 
 | 	.type	mn10300_local_icache_inv_page,@function | 
 | 	.type	mn10300_local_icache_inv_range,@function | 
 | 	.type	mn10300_local_icache_inv_range2,@function | 
 | mn10300_local_icache_inv_page: | 
 | 	and	~(PAGE_SIZE-1),d0 | 
 | 	mov	PAGE_SIZE,d1 | 
 | mn10300_local_icache_inv_range2: | 
 | 	add	d0,d1 | 
 | mn10300_local_icache_inv_range: | 
 | 	movm	[d2,d3,a2],(sp) | 
 |  | 
 | 	mov	CHCTR,a0 | 
 | 	movhu	(a0),d2 | 
 | 	btst	CHCTR_ICEN,d2 | 
 | 	beq	mn10300_local_icache_inv_range_reg_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: | 
 |  | 
 | 	/* a1 = end */ | 
 | 	mov	d1,a1 | 
 |  | 
 | 	LOCAL_CLI_SAVE(d3) | 
 |  | 
 | 	mov	ICIVCR,a0 | 
 | 	/* wait for busy bit of area invalidation */ | 
 | 	setlb | 
 | 	mov	(a0),d1 | 
 | 	btst	ICIVCR_ICIVBSY,d1 | 
 | 	lne | 
 |  | 
 | 	/* set mask | 
 | 	 * | 
 | 	 * mask = ~(alignsize-1); | 
 | 	 * ICIVMR = mask; | 
 | 	 */ | 
 | 	mov	d2,d1 | 
 | 	add	-1,d1 | 
 | 	not	d1 | 
 | 	mov	d1,(ICIVMR) | 
 | 	/* a2 = mask & start */ | 
 | 	and	d1,d0,a2 | 
 |  | 
 | icivloop: | 
 | 	/* area invalidate | 
 | 	 * | 
 | 	 * ICIVCR = (mask & start) | ICIVCR_ICI | 
 | 	 */ | 
 | 	mov	a2,d0 | 
 | 	or	ICIVCR_ICI,d0 | 
 | 	mov	d0,(a0) | 
 |  | 
 | 	/* wait for busy bit of area invalidation */ | 
 | 	setlb | 
 | 	mov	(a0),d1 | 
 | 	btst	ICIVCR_ICIVBSY,d1 | 
 | 	lne | 
 |  | 
 | 	/* check invalidating of end address | 
 | 	 * | 
 | 	 * a2 = a2 + alignsize | 
 | 	 * if (a2 < end) { | 
 | 	 *     goto icivloop; | 
 | 	 * } */ | 
 | 	add	d2,a2 | 
 | 	cmp	a1,a2 | 
 | 	bns	icivloop | 
 |  | 
 | 	LOCAL_IRQ_RESTORE(d3) | 
 |  | 
 | mn10300_local_icache_inv_range_reg_end: | 
 | 	ret	[d2,d3,a2],12 | 
 | 	.size	mn10300_local_icache_inv_page,.-mn10300_local_icache_inv_page | 
 | 	.size	mn10300_local_icache_inv_range,.-mn10300_local_icache_inv_range | 
 | 	.size	mn10300_local_icache_inv_range2,.-mn10300_local_icache_inv_range2 |