mirror of
https://github.com/torvalds/linux.git
synced 2024-11-19 02:21:47 +00:00
c8c90860cd
Commit 81d11955bf
("ARM: 6405/1: Handle __flush_icache_all for
CONFIG_SMP_ON_UP") added a new function to struct cpu_cache_fns:
flush_icache_all(). It also implemented this for v6 and v7 but not
for v5 and backwards. Without the function pointer in place, we
will be calling wrong cache functions.
For example with ep93xx we get following:
Unable to handle kernel paging request at virtual address ee070f38
pgd = c0004000
[ee070f38] *pgd=00000000
Internal error: Oops: 80000005 [#1] PREEMPT
last sysfs file:
Modules linked in:
CPU: 0 Not tainted (2.6.36+ #1)
PC is at 0xee070f38
LR is at __dma_alloc+0x11c/0x2d0
pc : [<ee070f38>] lr : [<c0032c8c>] psr: 60000013
sp : c581bde0 ip : 00000000 fp : c0472000
r10: c0472000 r9 : 000000d0 r8 : 00020000
r7 : 0001ffff r6 : 00000000 r5 : c0472400 r4 : c5980000
r3 : c03ab7e0 r2 : 00000000 r1 : c59a0000 r0 : c5980000
Flags: nZCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment kernel
Control: c000717f Table: c0004000 DAC: 00000017
Process swapper (pid: 1, stack limit = 0xc581a270)
[<c0032c8c>] (__dma_alloc+0x11c/0x2d0)
[<c0032e5c>] (dma_alloc_writecombine+0x1c/0x24)
[<c0204148>] (ep93xx_pcm_preallocate_dma_buffer+0x44/0x60)
[<c02041c0>] (ep93xx_pcm_new+0x5c/0x88)
[<c01ff188>] (snd_soc_instantiate_cards+0x8a8/0xbc0)
[<c01ff59c>] (soc_probe+0xfc/0x134)
[<c01adafc>] (platform_drv_probe+0x18/0x1c)
[<c01acca4>] (driver_probe_device+0xb0/0x16c)
[<c01ac284>] (bus_for_each_drv+0x48/0x84)
[<c01ace90>] (device_attach+0x50/0x68)
[<c01ac0f8>] (bus_probe_device+0x24/0x44)
[<c01aad7c>] (device_add+0x2fc/0x44c)
[<c01adfa8>] (platform_device_add+0x104/0x15c)
[<c0015eb8>] (simone_init+0x60/0x94)
[<c0021410>] (do_one_initcall+0xd0/0x1a4)
__dma_alloc() calls (inlined) __dma_alloc_buffer() which ends up
calling dmac_flush_range(). Now since the entries in the
arm920_cache_fns are shifted by one, we jump into address 0xee070f38
which is actually next instruction after the arm920_cache_fns
structure.
So implement flush_icache_all() for the rest of the supported CPUs
using a generic 'invalidate I cache' instruction.
Signed-off-by: Mika Westerberg <mika.westerberg@iki.fi>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
213 lines
4.8 KiB
ArmAsm
213 lines
4.8 KiB
ArmAsm
/*
|
|
* linux/arch/arm/mm/cache-v4wt.S
|
|
*
|
|
* Copyright (C) 1997-2002 Russell king
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* ARMv4 write through cache operations support.
|
|
*
|
|
* We assume that the write buffer is not enabled.
|
|
*/
|
|
#include <linux/linkage.h>
|
|
#include <linux/init.h>
|
|
#include <asm/page.h>
|
|
#include "proc-macros.S"
|
|
|
|
/*
|
|
* The size of one data cache line.
|
|
*/
|
|
#define CACHE_DLINESIZE 32
|
|
|
|
/*
|
|
* The number of data cache segments.
|
|
*/
|
|
#define CACHE_DSEGMENTS 8
|
|
|
|
/*
|
|
* The number of lines in a cache segment.
|
|
*/
|
|
#define CACHE_DENTRIES 64
|
|
|
|
/*
|
|
* This is the size at which it becomes more efficient to
|
|
* clean the whole cache, rather than using the individual
|
|
* cache line maintainence instructions.
|
|
*
|
|
* *** This needs benchmarking
|
|
*/
|
|
#define CACHE_DLIMIT 16384
|
|
|
|
/*
|
|
* flush_icache_all()
|
|
*
|
|
* Unconditionally clean and invalidate the entire icache.
|
|
*/
|
|
ENTRY(v4wt_flush_icache_all)
|
|
mov r0, #0
|
|
mcr p15, 0, r0, c7, c5, 0 @ invalidate I cache
|
|
mov pc, lr
|
|
ENDPROC(v4wt_flush_icache_all)
|
|
|
|
/*
|
|
* flush_user_cache_all()
|
|
*
|
|
* Invalidate all cache entries in a particular address
|
|
* space.
|
|
*/
|
|
ENTRY(v4wt_flush_user_cache_all)
|
|
/* FALLTHROUGH */
|
|
/*
|
|
* flush_kern_cache_all()
|
|
*
|
|
* Clean and invalidate the entire cache.
|
|
*/
|
|
ENTRY(v4wt_flush_kern_cache_all)
|
|
mov r2, #VM_EXEC
|
|
mov ip, #0
|
|
__flush_whole_cache:
|
|
tst r2, #VM_EXEC
|
|
mcrne p15, 0, ip, c7, c5, 0 @ invalidate I cache
|
|
mcr p15, 0, ip, c7, c6, 0 @ invalidate D cache
|
|
mov pc, lr
|
|
|
|
/*
|
|
* flush_user_cache_range(start, end, flags)
|
|
*
|
|
* Clean and invalidate a range of cache entries in the specified
|
|
* address space.
|
|
*
|
|
* - start - start address (inclusive, page aligned)
|
|
* - end - end address (exclusive, page aligned)
|
|
* - flags - vma_area_struct flags describing address space
|
|
*/
|
|
ENTRY(v4wt_flush_user_cache_range)
|
|
sub r3, r1, r0 @ calculate total size
|
|
cmp r3, #CACHE_DLIMIT
|
|
bhs __flush_whole_cache
|
|
|
|
1: mcr p15, 0, r0, c7, c6, 1 @ invalidate D entry
|
|
tst r2, #VM_EXEC
|
|
mcrne p15, 0, r0, c7, c5, 1 @ invalidate I entry
|
|
add r0, r0, #CACHE_DLINESIZE
|
|
cmp r0, r1
|
|
blo 1b
|
|
mov pc, lr
|
|
|
|
/*
|
|
* coherent_kern_range(start, end)
|
|
*
|
|
* Ensure coherency between the Icache and the Dcache in the
|
|
* region described by start. If you have non-snooping
|
|
* Harvard caches, you need to implement this function.
|
|
*
|
|
* - start - virtual start address
|
|
* - end - virtual end address
|
|
*/
|
|
ENTRY(v4wt_coherent_kern_range)
|
|
/* FALLTRHOUGH */
|
|
|
|
/*
|
|
* coherent_user_range(start, end)
|
|
*
|
|
* Ensure coherency between the Icache and the Dcache in the
|
|
* region described by start. If you have non-snooping
|
|
* Harvard caches, you need to implement this function.
|
|
*
|
|
* - start - virtual start address
|
|
* - end - virtual end address
|
|
*/
|
|
ENTRY(v4wt_coherent_user_range)
|
|
bic r0, r0, #CACHE_DLINESIZE - 1
|
|
1: mcr p15, 0, r0, c7, c5, 1 @ invalidate I entry
|
|
add r0, r0, #CACHE_DLINESIZE
|
|
cmp r0, r1
|
|
blo 1b
|
|
mov pc, lr
|
|
|
|
/*
|
|
* flush_kern_dcache_area(void *addr, size_t size)
|
|
*
|
|
* Ensure no D cache aliasing occurs, either with itself or
|
|
* the I cache
|
|
*
|
|
* - addr - kernel address
|
|
* - size - region size
|
|
*/
|
|
ENTRY(v4wt_flush_kern_dcache_area)
|
|
mov r2, #0
|
|
mcr p15, 0, r2, c7, c5, 0 @ invalidate I cache
|
|
add r1, r0, r1
|
|
/* fallthrough */
|
|
|
|
/*
|
|
* dma_inv_range(start, end)
|
|
*
|
|
* Invalidate (discard) the specified virtual address range.
|
|
* May not write back any entries. If 'start' or 'end'
|
|
* are not cache line aligned, those lines must be written
|
|
* back.
|
|
*
|
|
* - start - virtual start address
|
|
* - end - virtual end address
|
|
*/
|
|
v4wt_dma_inv_range:
|
|
bic r0, r0, #CACHE_DLINESIZE - 1
|
|
1: mcr p15, 0, r0, c7, c6, 1 @ invalidate D entry
|
|
add r0, r0, #CACHE_DLINESIZE
|
|
cmp r0, r1
|
|
blo 1b
|
|
mov pc, lr
|
|
|
|
/*
|
|
* dma_flush_range(start, end)
|
|
*
|
|
* Clean and invalidate the specified virtual address range.
|
|
*
|
|
* - start - virtual start address
|
|
* - end - virtual end address
|
|
*/
|
|
.globl v4wt_dma_flush_range
|
|
.equ v4wt_dma_flush_range, v4wt_dma_inv_range
|
|
|
|
/*
|
|
* dma_unmap_area(start, size, dir)
|
|
* - start - kernel virtual start address
|
|
* - size - size of region
|
|
* - dir - DMA direction
|
|
*/
|
|
ENTRY(v4wt_dma_unmap_area)
|
|
add r1, r1, r0
|
|
teq r2, #DMA_TO_DEVICE
|
|
bne v4wt_dma_inv_range
|
|
/* FALLTHROUGH */
|
|
|
|
/*
|
|
* dma_map_area(start, size, dir)
|
|
* - start - kernel virtual start address
|
|
* - size - size of region
|
|
* - dir - DMA direction
|
|
*/
|
|
ENTRY(v4wt_dma_map_area)
|
|
mov pc, lr
|
|
ENDPROC(v4wt_dma_unmap_area)
|
|
ENDPROC(v4wt_dma_map_area)
|
|
|
|
__INITDATA
|
|
|
|
.type v4wt_cache_fns, #object
|
|
ENTRY(v4wt_cache_fns)
|
|
.long v4wt_flush_icache_all
|
|
.long v4wt_flush_kern_cache_all
|
|
.long v4wt_flush_user_cache_all
|
|
.long v4wt_flush_user_cache_range
|
|
.long v4wt_coherent_kern_range
|
|
.long v4wt_coherent_user_range
|
|
.long v4wt_flush_kern_dcache_area
|
|
.long v4wt_dma_map_area
|
|
.long v4wt_dma_unmap_area
|
|
.long v4wt_dma_flush_range
|
|
.size v4wt_cache_fns, . - v4wt_cache_fns
|