351 lines
8.3 KiB
ArmAsm
351 lines
8.3 KiB
ArmAsm
/* 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
|