c6345ab1a3
In order to safely work around anomaly 05000491, we have to execute IFLUSH from L1 instruction sram. The trouble with multi-core systems is that all L1 sram is visible only to the active core. So we can't just place the functions into L1 and call it directly. We need to setup a jump table and place the entry point in external memory. This will call the right func based on the active core. In the process, convert from the manual relocation of a small bit of code into Core B's L1 to the more general framework we already have in place for loading arbitrary pieces of code into L1. Signed-off-by: Sonic Zhang <sonic.zhang@analog.com> Signed-off-by: Mike Frysinger <vapier@gentoo.org>
125 lines
2.8 KiB
ArmAsm
125 lines
2.8 KiB
ArmAsm
/*
|
|
* Blackfin cache control code
|
|
*
|
|
* Copyright 2004-2008 Analog Devices Inc.
|
|
*
|
|
* Licensed under the GPL-2 or later.
|
|
*/
|
|
|
|
#include <linux/linkage.h>
|
|
#include <asm/blackfin.h>
|
|
#include <asm/cache.h>
|
|
#include <asm/page.h>
|
|
|
|
/* 05000443 - IFLUSH cannot be last instruction in hardware loop */
|
|
#if ANOMALY_05000443
|
|
# define BROK_FLUSH_INST "IFLUSH"
|
|
#else
|
|
# define BROK_FLUSH_INST "no anomaly! yeah!"
|
|
#endif
|
|
|
|
/* Since all L1 caches work the same way, we use the same method for flushing
|
|
* them. Only the actual flush instruction differs. We write this in asm as
|
|
* GCC can be hard to coax into writing nice hardware loops.
|
|
*
|
|
* Also, we assume the following register setup:
|
|
* R0 = start address
|
|
* R1 = end address
|
|
*/
|
|
.macro do_flush flushins:req label
|
|
|
|
R2 = -L1_CACHE_BYTES;
|
|
|
|
/* start = (start & -L1_CACHE_BYTES) */
|
|
R0 = R0 & R2;
|
|
|
|
/* end = ((end - 1) & -L1_CACHE_BYTES) + L1_CACHE_BYTES; */
|
|
R1 += -1;
|
|
R1 = R1 & R2;
|
|
R1 += L1_CACHE_BYTES;
|
|
|
|
/* count = (end - start) >> L1_CACHE_SHIFT */
|
|
R2 = R1 - R0;
|
|
R2 >>= L1_CACHE_SHIFT;
|
|
P1 = R2;
|
|
|
|
.ifnb \label
|
|
\label :
|
|
.endif
|
|
P0 = R0;
|
|
|
|
LSETUP (1f, 2f) LC1 = P1;
|
|
1:
|
|
.ifeqs "\flushins", BROK_FLUSH_INST
|
|
\flushins [P0++];
|
|
nop;
|
|
nop;
|
|
2: nop;
|
|
.else
|
|
2: \flushins [P0++];
|
|
.endif
|
|
|
|
RTS;
|
|
.endm
|
|
|
|
#ifdef CONFIG_ICACHE_FLUSH_L1
|
|
.section .l1.text
|
|
#else
|
|
.text
|
|
#endif
|
|
|
|
/* Invalidate all instruction cache lines assocoiated with this memory area */
|
|
#ifdef CONFIG_SMP
|
|
# define _blackfin_icache_flush_range _blackfin_icache_flush_range_l1
|
|
#endif
|
|
ENTRY(_blackfin_icache_flush_range)
|
|
do_flush IFLUSH
|
|
ENDPROC(_blackfin_icache_flush_range)
|
|
|
|
#ifdef CONFIG_SMP
|
|
.text
|
|
# undef _blackfin_icache_flush_range
|
|
ENTRY(_blackfin_icache_flush_range)
|
|
p0.L = LO(DSPID);
|
|
p0.H = HI(DSPID);
|
|
r3 = [p0];
|
|
r3 = r3.b (z);
|
|
p2 = r3;
|
|
p0.L = _blackfin_iflush_l1_entry;
|
|
p0.H = _blackfin_iflush_l1_entry;
|
|
p0 = p0 + (p2 << 2);
|
|
p1 = [p0];
|
|
jump (p1);
|
|
ENDPROC(_blackfin_icache_flush_range)
|
|
#endif
|
|
|
|
#ifdef CONFIG_DCACHE_FLUSH_L1
|
|
.section .l1.text
|
|
#else
|
|
.text
|
|
#endif
|
|
|
|
/* Throw away all D-cached data in specified region without any obligation to
|
|
* write them back. Since the Blackfin ISA does not have an "invalidate"
|
|
* instruction, we use flush/invalidate. Perhaps as a speed optimization we
|
|
* could bang on the DTEST MMRs ...
|
|
*/
|
|
ENTRY(_blackfin_dcache_invalidate_range)
|
|
do_flush FLUSHINV
|
|
ENDPROC(_blackfin_dcache_invalidate_range)
|
|
|
|
/* Flush all data cache lines assocoiated with this memory area */
|
|
ENTRY(_blackfin_dcache_flush_range)
|
|
do_flush FLUSH, .Ldfr
|
|
ENDPROC(_blackfin_dcache_flush_range)
|
|
|
|
/* Our headers convert the page structure to an address, so just need to flush
|
|
* its contents like normal. We know the start address is page aligned (which
|
|
* greater than our cache alignment), as is the end address. So just jump into
|
|
* the middle of the dcache flush function.
|
|
*/
|
|
ENTRY(_blackfin_dflush_page)
|
|
P1 = 1 << (PAGE_SHIFT - L1_CACHE_SHIFT);
|
|
jump .Ldfr;
|
|
ENDPROC(_blackfin_dflush_page)
|