aboutsummaryrefslogtreecommitdiff
path: root/arch/cris/arch-v32/mm
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@woody.linux-foundation.org>2008-02-08 10:01:28 -0800
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2008-02-08 10:01:28 -0800
commit0cf975e16927fd70f34cee20d3856246c13bb4c8 (patch)
treebb955d50f28e5d98c198701798c8341d9763299a /arch/cris/arch-v32/mm
parent03054de1e0b90b33e9974107d84dabd2509f5898 (diff)
parentbc10ac3f2fe44e65f787d6197fd5d17304bf7d83 (diff)
Merge branch 'cris' of git://www.jni.nu/cris
* 'cris' of git://www.jni.nu/cris: (158 commits) CRIS v32: Remove hwregs/timer_defs.h, it is now architecture specific. CRIS v32: Change drivers/i2c.c locking. CRIS v32: Rewrite ARTPEC-3 gpio driver to avoid volatiles and general cleanup. CRIS: Add new timerfd syscall entries. MAINTAINERS: Add my information for the CRIS port. CRIS v32: Correct spelling of bandwidth in function name. CRIS v32: Clean up nandflash.c for ARTPEC-3 and ETRAX FS. CRIS v10: Cleanup of drivers/gpio.c CRIS v10: drivers/net/cris/eth_v10.c rename LED defines to CRIS_LED to avoid name clash. CRIS: Make io_pwm_set_period members unsigned in etraxgpio.h CRIS: Move ETRAX_AXISFLASHMAP to common Kconfig file. CRIS: Drop regs parameter from call to profile_tick in kernel/time.c CRIS v32: Fix minor formatting issue in mach-a3/io.c CRIS v32: Initialize GIO even if we're rambooting in kernel/head.S CRIS v32: Remove kernel/arbiter.c, it now exists in machine dependent directory. CRIS v32: Minor changes to avoid errors in asm-cris/arch-v32/hwregs/reg_rdwr.h CRIS v32: arch-v32/hwregs/intr_vect_defs.h moved to machine dependent directory. CRIS v32: Correct offset for TASK_pid in asm-cris/arch-v32/offset.h CRIS v32: Move register map header to machine dependent directory. CRIS v32: Let compiler know that memory is clobbered after a break op. ...
Diffstat (limited to 'arch/cris/arch-v32/mm')
-rw-r--r--arch/cris/arch-v32/mm/Makefile3
-rw-r--r--arch/cris/arch-v32/mm/init.c8
-rw-r--r--arch/cris/arch-v32/mm/intmem.c48
-rw-r--r--arch/cris/arch-v32/mm/l2cache.c29
-rw-r--r--arch/cris/arch-v32/mm/mmu.S93
-rw-r--r--arch/cris/arch-v32/mm/tlb.c56
6 files changed, 173 insertions, 64 deletions
diff --git a/arch/cris/arch-v32/mm/Makefile b/arch/cris/arch-v32/mm/Makefile
index 9146f88484b..0b801f2964a 100644
--- a/arch/cris/arch-v32/mm/Makefile
+++ b/arch/cris/arch-v32/mm/Makefile
@@ -1,3 +1,4 @@
# Makefile for the Linux/cris parts of the memory manager.
-obj-y := mmu.o init.o tlb.o intmem.o
+obj-y += mmu.o init.o tlb.o intmem.o
+obj-$(CONFIG_ETRAX_L2CACHE) += l2cache.o
diff --git a/arch/cris/arch-v32/mm/init.c b/arch/cris/arch-v32/mm/init.c
index a84ba7ff22d..5a9ac583464 100644
--- a/arch/cris/arch-v32/mm/init.c
+++ b/arch/cris/arch-v32/mm/init.c
@@ -65,7 +65,7 @@ cris_mmu_init(void)
REG_STATE(mmu, rw_mm_cfg, seg_d, page) |
REG_STATE(mmu, rw_mm_cfg, seg_c, linear) |
REG_STATE(mmu, rw_mm_cfg, seg_b, linear) |
-#ifndef CONFIG_ETRAXFS_SIM
+#ifndef CONFIG_ETRAX_VCS_SIM
REG_STATE(mmu, rw_mm_cfg, seg_a, page) |
#else
REG_STATE(mmu, rw_mm_cfg, seg_a, linear) |
@@ -84,13 +84,9 @@ cris_mmu_init(void)
mmu_kbase_hi = ( REG_FIELD(mmu, rw_mm_kbase_hi, base_f, 0x0) |
REG_FIELD(mmu, rw_mm_kbase_hi, base_e, 0x8) |
REG_FIELD(mmu, rw_mm_kbase_hi, base_d, 0x0) |
-#ifndef CONFIG_ETRAXFS_SIM
REG_FIELD(mmu, rw_mm_kbase_hi, base_c, 0x4) |
-#else
- REG_FIELD(mmu, rw_mm_kbase_hi, base_c, 0x0) |
-#endif
REG_FIELD(mmu, rw_mm_kbase_hi, base_b, 0xb) |
-#ifndef CONFIG_ETRAXFS_SIM
+#ifndef CONFIG_ETRAX_VCS_SIM
REG_FIELD(mmu, rw_mm_kbase_hi, base_a, 0x0) |
#else
REG_FIELD(mmu, rw_mm_kbase_hi, base_a, 0xa) |
diff --git a/arch/cris/arch-v32/mm/intmem.c b/arch/cris/arch-v32/mm/intmem.c
index 41ee7f7997f..9e8b69cdf19 100644
--- a/arch/cris/arch-v32/mm/intmem.c
+++ b/arch/cris/arch-v32/mm/intmem.c
@@ -7,11 +7,17 @@
#include <linux/list.h>
#include <linux/slab.h>
#include <asm/io.h>
-#include <asm/arch/memmap.h>
+#include <memmap.h>
#define STATUS_FREE 0
#define STATUS_ALLOCATED 1
+#ifdef CONFIG_ETRAX_L2CACHE
+#define RESERVED_SIZE 66*1024
+#else
+#define RESERVED_SIZE 0
+#endif
+
struct intmem_allocation {
struct list_head entry;
unsigned int size;
@@ -30,9 +36,10 @@ static void crisv32_intmem_init(void)
struct intmem_allocation* alloc =
(struct intmem_allocation*)kmalloc(sizeof *alloc, GFP_KERNEL);
INIT_LIST_HEAD(&intmem_allocations);
- intmem_virtual = ioremap(MEM_INTMEM_START, MEM_INTMEM_SIZE);
+ intmem_virtual = ioremap(MEM_INTMEM_START + RESERVED_SIZE,
+ MEM_INTMEM_SIZE - RESERVED_SIZE);
initiated = 1;
- alloc->size = MEM_INTMEM_SIZE;
+ alloc->size = MEM_INTMEM_SIZE - RESERVED_SIZE;
alloc->offset = 0;
alloc->status = STATUS_FREE;
list_add_tail(&alloc->entry, &intmem_allocations);
@@ -59,19 +66,23 @@ void* crisv32_intmem_alloc(unsigned size, unsigned align)
(struct intmem_allocation*)
kmalloc(sizeof *alloc, GFP_ATOMIC);
alloc->status = STATUS_FREE;
- alloc->size = allocation->size - size - alignment;
- alloc->offset = allocation->offset + size;
+ alloc->size = allocation->size - size -
+ alignment;
+ alloc->offset = allocation->offset + size +
+ alignment;
list_add(&alloc->entry, &allocation->entry);
if (alignment) {
- struct intmem_allocation* tmp;
- tmp = (struct intmem_allocation*)
- kmalloc(sizeof *tmp, GFP_ATOMIC);
+ struct intmem_allocation *tmp;
+ tmp = (struct intmem_allocation *)
+ kmalloc(sizeof *tmp,
+ GFP_ATOMIC);
tmp->offset = allocation->offset;
tmp->size = alignment;
tmp->status = STATUS_FREE;
allocation->offset += alignment;
- list_add_tail(&tmp->entry, &allocation->entry);
+ list_add_tail(&tmp->entry,
+ &allocation->entry);
}
}
allocation->status = STATUS_ALLOCATED;
@@ -96,22 +107,24 @@ void crisv32_intmem_free(void* addr)
list_for_each_entry_safe(allocation, tmp, &intmem_allocations, entry) {
if (allocation->offset == (int)(addr - intmem_virtual)) {
- struct intmem_allocation* prev =
+ struct intmem_allocation *prev =
list_entry(allocation->entry.prev,
struct intmem_allocation, entry);
- struct intmem_allocation* next =
+ struct intmem_allocation *next =
list_entry(allocation->entry.next,
struct intmem_allocation, entry);
allocation->status = STATUS_FREE;
/* Join with prev and/or next if also free */
- if (prev->status == STATUS_FREE) {
+ if ((prev != &intmem_allocations) &&
+ (prev->status == STATUS_FREE)) {
prev->size += allocation->size;
list_del(&allocation->entry);
kfree(allocation);
allocation = prev;
}
- if (next->status == STATUS_FREE) {
+ if ((next != &intmem_allocations) &&
+ (next->status == STATUS_FREE)) {
allocation->size += next->size;
list_del(&next->entry);
kfree(next);
@@ -125,15 +138,16 @@ void crisv32_intmem_free(void* addr)
void* crisv32_intmem_phys_to_virt(unsigned long addr)
{
- return (void*)(addr - MEM_INTMEM_START+
- (unsigned long)intmem_virtual);
+ return (void *)(addr - (MEM_INTMEM_START + RESERVED_SIZE) +
+ (unsigned long)intmem_virtual);
}
unsigned long crisv32_intmem_virt_to_phys(void* addr)
{
return (unsigned long)((unsigned long )addr -
- (unsigned long)intmem_virtual + MEM_INTMEM_START);
+ (unsigned long)intmem_virtual + MEM_INTMEM_START +
+ RESERVED_SIZE);
}
-
+module_init(crisv32_intmem_init);
diff --git a/arch/cris/arch-v32/mm/l2cache.c b/arch/cris/arch-v32/mm/l2cache.c
new file mode 100644
index 00000000000..332ff10dcc6
--- /dev/null
+++ b/arch/cris/arch-v32/mm/l2cache.c
@@ -0,0 +1,29 @@
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <memmap.h>
+#include <hwregs/reg_map.h>
+#include <hwregs/reg_rdwr.h>
+#include <hwregs/l2cache_defs.h>
+#include <asm/io.h>
+
+#define L2CACHE_SIZE 64
+
+int __init l2cache_init(void)
+{
+ reg_l2cache_rw_ctrl ctrl = {0};
+ reg_l2cache_rw_cfg cfg = {.en = regk_l2cache_yes};
+
+ ctrl.csize = L2CACHE_SIZE;
+ ctrl.cbase = L2CACHE_SIZE / 4 + (L2CACHE_SIZE % 4 ? 1 : 0);
+ REG_WR(l2cache, regi_l2cache, rw_ctrl, ctrl);
+
+ /* Flush the tag memory */
+ memset((void *)(MEM_INTMEM_START | MEM_NON_CACHEABLE), 0, 2*1024);
+
+ /* Enable the cache */
+ REG_WR(l2cache, regi_l2cache, rw_cfg, cfg);
+
+ return 0;
+}
+
diff --git a/arch/cris/arch-v32/mm/mmu.S b/arch/cris/arch-v32/mm/mmu.S
index 27b70e5006a..2238d154bde 100644
--- a/arch/cris/arch-v32/mm/mmu.S
+++ b/arch/cris/arch-v32/mm/mmu.S
@@ -1,3 +1,5 @@
+; WARNING : The refill handler has been modified, see below !!!
+
/*
* Copyright (C) 2003 Axis Communications AB
*
@@ -61,6 +63,14 @@
; Note that the code is optimized to minimize stalls (makes the code harder
; to read).
;
+; WARNING !!!
+; Modified by Mikael Asker 060725: added a workaround for strange TLB
+; behavior. If the same PTE is present in more than one set, the TLB
+; doesn't recognize it and we get stuck in a loop of refill exceptions.
+; The workaround detects such loops and exits them by flushing
+; the TLB contents. The problem and workaround were verified
+; in VCS by Mikael Starvik.
+;
; Each page is 8 KB. Each PMD holds 8192/4 PTEs (each PTE is 4 bytes) so each
; PMD holds 16 MB of virtual memory.
; Bits 0-12 : Offset within a page
@@ -68,6 +78,11 @@
; Bits 24-31 : PMD offset within the PGD
.macro MMU_REFILL_HANDLER handler, mmu
+ .data
+1: .dword 0 ; refill_count
+ ; == 0 <=> last_refill_cause is invalid
+2: .dword 0 ; last_refill_cause
+ .text
.globl \handler
\handler:
subq 4, $sp
@@ -76,42 +91,96 @@
subq 4, $sp
move \mmu, $srs ; Select MMU support register bank
move.d $acr, [$sp]
- subq 4, $sp
- move.d $r0, [$sp]
+ subq 12, $sp
+ move.d 1b, $acr ; Point to refill_count
+ movem $r2, [$sp]
+
+ test.d [$acr] ; refill_count == 0 ?
+ beq 5f ; yes, last_refill_cause is invalid
+ move.d $acr, $r1
+
+ ; last_refill_cause is valid, investigate cause
+ addq 4, $r1 ; Point to last_refill_cause
+ move $s3, $r0 ; Get rw_mm_cause
+ move.d [$r1], $r2 ; Get last_refill_cause
+ cmp.d $r0, $r2 ; rw_mm_cause == last_refill_cause ?
+ beq 6f ; yes, increment count
+ moveq 1, $r2
+
+ ; rw_mm_cause != last_refill_cause
+ move.d $r2, [$acr] ; refill_count = 1
+ move.d $r0, [$r1] ; last_refill_cause = rw_mm_cause
+
+3: ; Probably not in a loop, continue normal processing
#ifdef CONFIG_SMP
move $s7, $acr ; PGD
#else
move.d per_cpu__current_pgd, $acr ; PGD
#endif
; Look up PMD in PGD
- move $s3, $r0 ; rw_mm_cause
lsrq 24, $r0 ; Get PMD index into PGD (bit 24-31)
move.d [$acr], $acr ; PGD for the current process
addi $r0.d, $acr, $acr
move $s3, $r0 ; rw_mm_cause
move.d [$acr], $acr ; Get PMD
- beq 1f
+ beq 8f
; Look up PTE in PMD
lsrq PAGE_SHIFT, $r0
and.w PAGE_MASK, $acr ; Remove PMD flags
and.d 0x7ff, $r0 ; Get PTE index into PMD (bit 13-23)
addi $r0.d, $acr, $acr
move.d [$acr], $acr ; Get PTE
- beq 2f
- move.d [$sp+], $r0 ; Pop r0 in delayslot
+ beq 9f
+ movem [$sp], $r2 ; Restore r0-r2 in delay slot
+ addq 12, $sp
; Store in TLB
move $acr, $s5
- ; Return
+4: ; Return
move.d [$sp+], $acr
- move [$sp], $srs
+ move [$sp], $srs
addq 4, $sp
rete
rfe
-1: ; PMD missing, let the mm subsystem fix it up.
- move.d [$sp+], $r0 ; Pop r0
-2: ; PTE missing, let the mm subsystem fix it up.
+
+5: ; last_refill_cause is invalid
+ moveq 1, $r2
+ addq 4, $r1 ; Point to last_refill_cause
+ move.d $r2, [$acr] ; refill_count = 1
+ move $s3, $r0 ; Get rw_mm_cause
+ ba 3b ; Continue normal processing
+ move.d $r0,[$r1] ; last_refill_cause = rw_mm_cause
+
+6: ; rw_mm_cause == last_refill_cause
+ move.d [$acr], $r2 ; Get refill_count
+ cmpq 4, $r2 ; refill_count > 4 ?
+ bhi 7f ; yes
+ addq 1, $r2 ; refill_count++
+ ba 3b ; Continue normal processing
+ move.d $r2, [$acr]
+
+7: ; refill_count > 4, error
+ move.d $acr, $r0 ; Save pointer to refill_count
+ clear.d [$r0] ; refill_count = 0
+
+ ;; rewind the short stack
+ movem [$sp], $r2 ; Restore r0-r2
+ addq 12, $sp
+ move.d [$sp+], $acr
+ move [$sp], $srs
+ addq 4, $sp
+ ;; Keep it simple (slow), save all the regs.
+ SAVE_ALL
+ jsr __flush_tlb_all
+ nop
+ ba ret_from_intr ; Return
+ nop
+
+8: ; PMD missing, let the mm subsystem fix it up.
+ movem [$sp], $r2 ; Restore r0-r2
+9: ; PTE missing, let the mm subsystem fix it up.
+ addq 12, $sp
move.d [$sp+], $acr
- move [$sp], $srs
+ move [$sp], $srs
addq 4, $sp
SAVE_ALL
move \mmu, $srs
diff --git a/arch/cris/arch-v32/mm/tlb.c b/arch/cris/arch-v32/mm/tlb.c
index a076ef6e938..eda5ebcaea5 100644
--- a/arch/cris/arch-v32/mm/tlb.c
+++ b/arch/cris/arch-v32/mm/tlb.c
@@ -13,8 +13,8 @@
#include <asm/arch/hwregs/supp_reg.h>
#define UPDATE_TLB_SEL_IDX(val) \
-do { \
- unsigned long tlb_sel; \
+do { \
+ unsigned long tlb_sel; \
\
tlb_sel = REG_FIELD(mmu, rw_mm_tlb_sel, idx, val); \
SUPP_REG_WR(RW_MM_TLB_SEL, tlb_sel); \
@@ -30,8 +30,8 @@ do { \
* The TLB can host up to 256 different mm contexts at the same time. The running
* context is found in the PID register. Each TLB entry contains a page_id that
* has to match the PID register to give a hit. page_id_map keeps track of which
- * mm is assigned to which page_id, making sure it's known when to invalidate TLB
- * entries.
+ * mm's is assigned to which page_id's, making sure it's known when to
+ * invalidate TLB entries.
*
* The last page_id is never running, it is used as an invalid page_id so that
* it's possible to make TLB entries that will nerver match.
@@ -179,29 +179,29 @@ void
switch_mm(struct mm_struct *prev, struct mm_struct *next,
struct task_struct *tsk)
{
- int cpu = smp_processor_id();
-
- /* Make sure there is a MMU context. */
- spin_lock(&mmu_context_lock);
- get_mmu_context(next);
- cpu_set(cpu, next->cpu_vm_mask);
- spin_unlock(&mmu_context_lock);
-
- /*
- * Remember the pgd for the fault handlers. Keep a separate copy of it
- * because current and active_mm might be invalid at points where
- * there's still a need to derefer the pgd.
- */
- per_cpu(current_pgd, cpu) = next->pgd;
-
- /* Switch context in the MMU. */
- if (tsk && task_thread_info(tsk))
- {
- SPEC_REG_WR(SPEC_REG_PID, next->context.page_id | task_thread_info(tsk)->tls);
- }
- else
- {
- SPEC_REG_WR(SPEC_REG_PID, next->context.page_id);
- }
+ if (prev != next) {
+ int cpu = smp_processor_id();
+
+ /* Make sure there is a MMU context. */
+ spin_lock(&mmu_context_lock);
+ get_mmu_context(next);
+ cpu_set(cpu, next->cpu_vm_mask);
+ spin_unlock(&mmu_context_lock);
+
+ /*
+ * Remember the pgd for the fault handlers. Keep a seperate
+ * copy of it because current and active_mm might be invalid
+ * at points where * there's still a need to derefer the pgd.
+ */
+ per_cpu(current_pgd, cpu) = next->pgd;
+
+ /* Switch context in the MMU. */
+ if (tsk && task_thread_info(tsk)) {
+ SPEC_REG_WR(SPEC_REG_PID, next->context.page_id |
+ task_thread_info(tsk)->tls);
+ } else {
+ SPEC_REG_WR(SPEC_REG_PID, next->context.page_id);
+ }
+ }
}