/* 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