|
@@ -0,0 +1,356 @@
|
|
|
|
+/* 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>
|
|
|
|
+
|
|
|
|
+#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
|
|
|
|
+ or CHCTR_ICINV,d0
|
|
|
|
+ movhu d0,(a0)
|
|
|
|
+ movhu (a0),d0
|
|
|
|
+
|
|
|
|
+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
|
|
|
|
+ or CHCTR_DCINV,d0
|
|
|
|
+ movhu d0,(a0)
|
|
|
|
+ movhu (a0),d0
|
|
|
|
+
|
|
|
|
+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_BYTES-1),d0
|
|
|
|
+ bne 1f
|
|
|
|
+ btst ~(L1_CACHE_BYTES-1),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_ADDRESS|L1_CACHE_TAG_ENTRY,d0 # round start
|
|
|
|
+ # addr down
|
|
|
|
+
|
|
|
|
+ mov L1_CACHE_BYTES-1,d2
|
|
|
|
+ add d2,d1
|
|
|
|
+ and L1_CACHE_TAG_ADDRESS|L1_CACHE_TAG_ENTRY,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
|