/* 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 [31mCONFIG_SMP[0m
.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 [31mCONFIG_MN10300_CACHE_WBACK[0m
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 [31mCONFIG_MN10300_CACHE_WBACK[0m
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