OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [uclinux/] [uClinux-2.0.x/] [arch/] [mips/] [kernel/] [r4xx0.S] - Rev 1765

Compare with Previous | Blame | View Log

/*
 * arch/mips/kernel/r4xx0.S
 *
 * Copyright (C) 1994, 1995 Waldorf Electronics
 * Written by Ralf Baechle and Andreas Busse
 *
 * This file contains most of the R4xx0 specific routines.  Due to the
 * similarities this should hopefully also be fine for the R10000.  For
 * now we especially support the R10000 by not invalidating entries out of
 * the TLB before calling the C handlers.
 *
 * This code is evil magic. Read appendix f (coprocessor 0 hazards) of
 * all R4xx0 manuals and think about that MIPS means "Microprocessor without
 * Interlocked Pipeline Stages" before you even think about changing this code!
 */
#include <linux/config.h>

#include <asm/asm.h>
#include <asm/bootinfo.h>
#include <asm/cachectl.h>
#include <asm/mipsconfig.h>
#include <asm/mipsregs.h>
#include <asm/page.h>
#include <asm/pgtable.h>
#include <asm/processor.h>
#include <asm/mipsregs.h>
#include <asm/segment.h>
#include <asm/stackframe.h>

#ifdef __SMP__
#error "Fix this for SMP"
#else
#define current current_set
#endif

MODE_ALIAS      =       0x0016                  # uncachable

                .text
                .set    mips3
                .set    noreorder

                .align  5
                NESTED(handle_tlbl, FR_SIZE, sp)
                .set    noat
                /*
                 * Check whether this is a refill or an invalid exception
                 *
                 * NOTE: Some MIPS manuals say that the R4x00 sets the
                 * BadVAddr only when EXL == 0. This is wrong - BadVAddr
                 * is being set for all Reload, Invalid and Modified
                 * exceptions.
                 */
                mfc0    k0,CP0_BADVADDR
                mfc0    k1,CP0_ENTRYHI
                ori     k0,0x1fff
                xori    k0,0x1fff
                andi    k1,0xff
                or      k0,k1
                mfc0    k1,CP0_ENTRYHI
                mtc0    k0,CP0_ENTRYHI
                nop                                     # for R4[04]00 pipeline
                nop
                nop
                tlbp
                nop                                     # for R4[04]00 pipeline
                nop
                mfc0    k0,CP0_INDEX
                bgez    k0,invalid_tlbl                 # bad addr in c0_badvaddr
                mtc0    k1,CP0_ENTRYHI                  # delay slot
                /*
                 * Damn... The next nop is required on my R4400PC V5.0, but
                 * I don't know why - at least there is no documented
                 * reason as for the others :-(
                 */
                nop

#ifdef CONF_DEBUG_TLB
                /*
                 * OK, this is a double fault. Let's see whether this is
                 * due to an invalid entry in the page_table.
                 */
                dmfc0   k0,CP0_BADVADDR
                srl     k0,12
                sll     k0,2
                lui     k1,%HI(TLBMAP)
                addu    k0,k1
                lw      k1,(k0)
                andi    k1,(_PAGE_PRESENT|_PAGE_ACCESSED)
                bnez    k1,reload_pgd_entries
                nop                                     # delay slot

                .set    noat
                SAVE_ALL
                .set    at
                PRINT("Double fault caused by invalid entries in pgd:\n")
                dmfc0   a1,CP0_BADVADDR
                PRINT("Double fault address     : %08lx\n")
                dmfc0   a1,CP0_EPC
                PRINT("c0_epc                   : %08lx\n")
                jal     show_regs
                move    a0,sp
                jal     dump_tlb_all
                nop
                dmfc0   a0,CP0_BADVADDR
                jal     dump_list_current
                nop
                .set    noat
                STI
                .set    at
                PANIC("Corrupted pagedir")
                .set    noat

reload_pgd_entries:
#endif /* CONF_DEBUG_TLB */

                /*
                 * Load missing pair of entries from the pgd and return.
                 */
                dmfc0   k1,CP0_CONTEXT
                dsra    k1,1
                lwu     k0,(k1)                 # Never causes nested exception
                lwu     k1,4(k1)
                dsrl    k0,6                    # Convert to EntryLo format
                dsrl    k1,6                    # Convert to EntryLo format
                dmtc0   k0,CP0_ENTRYLO0
                dmtc0   k1,CP0_ENTRYLO1
                nop                             # for R4[04]00 pipeline
                tlbwr
                nop                             # for R4[04]00 pipeline
                nop
                nop
                /*
                 * We don't know whether the original access was read or
                 * write, so return and see what happens...
                 */
                eret

                /*
                 * Handle invalid exception
                 *
                 * There are two possible causes for an invalid (tlbl)
                 * exception:
                 * 1) pages with present bit set but the valid bit clear
                 * 2) nonexistent pages
                 * Case one needs fast handling, therefore don't save
                 * registers yet.
                 *
                 * k0 contains c0_index.
                 */
invalid_tlbl:
#ifdef CONFIG_TLB_SHUTDOWN
                /*
                 * Remove entry so we don't need to care later
                 * For sake of the R4000 V2.2 pipeline the tlbwi insn
                 * has been moved down. Moving it around is juggling with
                 * explosives...
                 */
                lui     k1,0x0008
                or      k0,k1
                dsll    k0,13
                dmtc0   k0,CP0_ENTRYHI
                dmtc0   zero,CP0_ENTRYLO0
                dmtc0   zero,CP0_ENTRYLO1
#endif
                /*
                 * Test present bit in entry
                 */
                dmfc0   k0,CP0_BADVADDR
                srl     k0,12
                sll     k0,2
#ifdef CONFIG_TLB_SHUTDOWN
                tlbwi                                           # do not move!
#endif
                lui     k1,%HI(TLBMAP)
                addu    k0,k1
                lw      k1,(k0)
                andi    k1,(_PAGE_PRESENT|_PAGE_READ)
                xori    k1,(_PAGE_PRESENT|_PAGE_READ)
                bnez    k1,nopage_tlbl
                /*
                 * Present and read bits are set -> set valid and accessed bits
                 */
                lw      k1,(k0)                                 # delay slot
                ori     k1,(_PAGE_VALID|_PAGE_ACCESSED)
                sw      k1,(k0)
                eret

                /*
                 * Page doesn't exist. Lots of work which is less important
                 * for speed needs to be done, so hand it all over to the
                 * kernel memory management routines.
                 */
nopage_tlbl:    SAVE_ALL
                dmfc0   a2,CP0_BADVADDR
                STI
                .set    at
                /*
                 * a0 (struct pt_regs *) regs
                 * a1 (unsigned long)    0 for read access
                 * a2 (unsigned long)    faulting virtual address
                 */
                move    a0,sp
                jal     do_page_fault
                li      a1,0                            # delay slot
                j       ret_from_sys_call
                nop                                     # delay slot
                END(handle_tlbl)

                .text
                .align  5
                NESTED(handle_tlbs, FR_SIZE, sp)
                .set    noat
                /*
                 * It is impossible that is a nested reload exception.
                 * Therefore this must be a invalid exception.
                 * Two possible cases:
                 * 1) Page exists but not dirty.
                 * 2) Page doesn't exist yet. Hand over to the kernel.
                 *
                 * Test whether present bit in entry is set
                 */
                dmfc0   k0,CP0_BADVADDR
                srl     k0,12
                sll     k0,2
                lui     k1,%HI(TLBMAP)
                addu    k0,k1
                lw      k1,(k0)
                tlbp                                    # find faulting entry
                andi    k1,(_PAGE_PRESENT|_PAGE_WRITE)
                xori    k1,(_PAGE_PRESENT|_PAGE_WRITE)
                bnez    k1,nopage_tlbs
                /*
                 * Present and writable bits set: set accessed and dirty bits.
                 */
                lw      k1,(k0)                         # delay slot
                ori     k1,k1,(_PAGE_ACCESSED|_PAGE_MODIFIED| \
                               _PAGE_VALID|_PAGE_DIRTY)
                sw      k1,(k0)
                /*
                 * Now reload the entry into the TLB
                 */
                ori     k0,0x0004
                xori    k0,0x0004
                lw      k1,4(k0)
                lw      k0,(k0)
                srl     k1,6
                srl     k0,6
                dmtc0   k1,CP0_ENTRYLO1
                dmtc0   k0,CP0_ENTRYLO0
                nop                             # for R4[04]00 pipeline
                tlbwi
                nop                             # for R4[04]00 pipeline
                nop
                nop
                eret

                /*
                 * Page doesn't exist. Lots of work which is less important
                 * for speed needs to be done, so hand it all over to the
                 * kernel memory management routines.
                 */
nopage_tlbs:
nowrite_mod:
#ifdef CONFIG_TLB_SHUTDOWN
                /*
                 * Remove entry so we don't need to care later
                 */
                mfc0    k0,CP0_INDEX
#ifdef CONF_DEBUG_TLB
                bgez    k0,2f
                nop
                /*
                 * We got a tlbs exception but found no matching entry in
                 * the tlb.  This should never happen.  Paranoia makes us
                 * check it, though.
                 */
                SAVE_ALL
                jal     show_regs
                move    a0,sp
                .set    at
                mfc0    a1,CP0_BADVADDR
                PRINT("c0_badvaddr == %08lx\n")
                mfc0    a1,CP0_INDEX
                PRINT("c0_index    == %08x\n")
                mfc0    a1,CP0_ENTRYHI
                PRINT("c0_entryhi  == %08x\n")
                .set    noat
                STI
                .set    at
                PANIC("Tlbs or tlbm exception with no matching entry in tlb")
1:              j       1b
                nop
2:
#endif /* CONF_DEBUG_TLB */
                lui     k1,0x0008
                or      k0,k1
                dsll    k0,13
                dmtc0   k0,CP0_ENTRYHI
                dmtc0   zero,CP0_ENTRYLO0
                dmtc0   zero,CP0_ENTRYLO1
                nop                             # for R4[04]00 pipeline
                nop                             # R4000 V2.2 requires 4 NOPs
                nop
                nop
                tlbwi
#endif
                .set    noat
                SAVE_ALL
                dmfc0   a2,CP0_BADVADDR
                STI
                .set    at
                /*
                 * a0 (struct pt_regs *) regs
                 * a1 (unsigned long)    1 for write access
                 * a2 (unsigned long)    faulting virtual address
                 */
                move    a0,sp
                jal     do_page_fault
                li      a1,1                            # delay slot
                j       ret_from_sys_call
                nop                                     # delay slot
                END(handle_tlbs)

                .align  5
                NESTED(handle_mod, FR_SIZE, sp)
                .set    noat
                /*
                 * Two possible cases:
                 * 1) Page is writable but not dirty -> set dirty and return
                 * 2) Page is not writable -> call C handler
                 */
                dmfc0   k0,CP0_BADVADDR
                srl     k0,12
                sll     k0,2
                lui     k1,%HI(TLBMAP)
                addu    k0,k1
                lw      k1,(k0)
                tlbp                                    # find faulting entry
                andi    k1,_PAGE_WRITE
                beqz    k1,nowrite_mod
                /*
                 * Present and writable bits set: set accessed and dirty bits.
                 */
                lw      k1,(k0)                         # delay slot
                ori     k1,(_PAGE_ACCESSED|_PAGE_DIRTY)
                sw      k1,(k0)
                /*
                 * Now reload the entry into the tlb
                 */
                ori     k0,0x0004
                xori    k0,0x0004
                lw      k1,4(k0)
                lw      k0,(k0)
                srl     k1,6
                srl     k0,6
                dmtc0   k1,CP0_ENTRYLO1
                dmtc0   k0,CP0_ENTRYLO0
                nop                             # for R4[04]00 pipeline
                nop
                nop
                tlbwi
                nop                             # for R4[04]00 pipeline
                nop
                nop
                eret
                END(handle_mod)
                .set    at

/*
 * Until SAVE_ALL/RESTORE_ALL handle registers 64-bit wide we have to
 * disable interrupts here.
 */
                .set    noreorder
                LEAF(tlbflush)
                mfc0    t3,CP0_STATUS
                ori     t4,t3,1
                xori    t4,1
                mtc0    t4,CP0_STATUS
                li      t0,PM_4K
                mtc0    t0,CP0_PAGEMASK
                la      t0,boot_info
                lw      t0,OFFSET_BOOTINFO_TLB_ENTRIES(t0)
                dmtc0   zero,CP0_ENTRYLO0
                dmtc0   zero,CP0_ENTRYLO1
                mfc0    t2,CP0_WIRED
1:              subu    t0,1
                mtc0    t0,CP0_INDEX
                lui     t1,0x0008
                or      t1,t0,t1
                dsll    t1,13
                dmtc0   t1,CP0_ENTRYHI
                bne     t2,t0,1b
                tlbwi                                   # delay slot
                jr      ra
                mtc0    t3,CP0_STATUS                   # delay slot
                END(tlbflush)

                /*
                 * Code necessary to switch tasks on an Linux/MIPS machine.
                 */
                .align  5
                LEAF(resume)
                /*
                 * Current task's task_struct
                 */
                lui     t5,%hi(current)
                lw      t0,%lo(current)(t5)

                /*
                 * Save status register
                 */
                mfc0    t1,CP0_STATUS
                addu    t0,a1                           # Add tss offset
                sw      t1,TOFF_CP0_STATUS(t0)

                /*
                 * Disable interrupts
                 */
                ori     t2,t1,0x1f
                xori    t2,0x1e
                mtc0    t2,CP0_STATUS

                /*
                 * Save non-scratch registers
                 * All other registers have been saved on the kernel stack
                 */
                sw      s0,TOFF_REG16(t0)
                sw      s1,TOFF_REG17(t0)
                sw      s2,TOFF_REG18(t0)
                sw      s3,TOFF_REG19(t0)
                sw      s4,TOFF_REG20(t0)
                sw      s5,TOFF_REG21(t0)
                sw      s6,TOFF_REG22(t0)
                sw      s7,TOFF_REG23(t0)
                sw      gp,TOFF_REG28(t0)
                sw      sp,TOFF_REG29(t0)
                sw      fp,TOFF_REG30(t0)

                /*
                 * Save floating point state
                 */
                sll     t2,t1,2
                bgez    t2,2f
                sw      ra,TOFF_REG31(t0)               # delay slot
                sll     t2,t1,5
                bgez    t2,1f
                sdc1    $f0,(TOFF_FPU+0)(t0)            # delay slot
                /*
                 * Store the 16 odd double precision registers
                 */
                sdc1    $f1,(TOFF_FPU+8)(t0)
                sdc1    $f3,(TOFF_FPU+24)(t0)
                sdc1    $f5,(TOFF_FPU+40)(t0)
                sdc1    $f7,(TOFF_FPU+56)(t0)
                sdc1    $f9,(TOFF_FPU+72)(t0)
                sdc1    $f11,(TOFF_FPU+88)(t0)
                sdc1    $f13,(TOFF_FPU+104)(t0)
                sdc1    $f15,(TOFF_FPU+120)(t0)
                sdc1    $f17,(TOFF_FPU+136)(t0)
                sdc1    $f19,(TOFF_FPU+152)(t0)
                sdc1    $f21,(TOFF_FPU+168)(t0)
                sdc1    $f23,(TOFF_FPU+184)(t0)
                sdc1    $f25,(TOFF_FPU+200)(t0)
                sdc1    $f27,(TOFF_FPU+216)(t0)
                sdc1    $f29,(TOFF_FPU+232)(t0)
                sdc1    $f31,(TOFF_FPU+248)(t0)

                /*
                 * Store the 16 even double precision registers
                 */
1:              cfc1    t1,fcr31
                sdc1    $f2,(TOFF_FPU+16)(t0)
                sdc1    $f4,(TOFF_FPU+32)(t0)
                sdc1    $f6,(TOFF_FPU+48)(t0)
                sdc1    $f8,(TOFF_FPU+64)(t0)
                sdc1    $f10,(TOFF_FPU+80)(t0)
                sdc1    $f12,(TOFF_FPU+96)(t0)
                sdc1    $f14,(TOFF_FPU+112)(t0)
                sdc1    $f16,(TOFF_FPU+128)(t0)
                sdc1    $f18,(TOFF_FPU+144)(t0)
                sdc1    $f20,(TOFF_FPU+160)(t0)
                sdc1    $f22,(TOFF_FPU+176)(t0)
                sdc1    $f24,(TOFF_FPU+192)(t0)
                sdc1    $f26,(TOFF_FPU+208)(t0)
                sdc1    $f28,(TOFF_FPU+224)(t0)
                sdc1    $f30,(TOFF_FPU+240)(t0)
                sw      t1,(TOFF_FPU+256)(t0)

                /*
                 * Switch current task
                 */
2:              sw      a0,%lo(current)(t5)
                addu    a0,a1                   # Add tss offset

                /*
                 * Switch address space
                 */

                /*
                 * (Choose new ASID for process)
                 * This isn't really required, but would speed up
                 * context switching.
                 */

                /*
                 * Switch the root pointer
                 */
                lw      t0,TOFF_PG_DIR(a0)
                li      t1,TLB_ROOT
                mtc0    t1,CP0_ENTRYHI
                mtc0    zero,CP0_INDEX
                srl     t0,6
                ori     t0,MODE_ALIAS
                mtc0    t0,CP0_ENTRYLO0
                mtc0    zero,CP0_ENTRYLO1
                lw      a2,TOFF_CP0_STATUS(a0)

                /*
                 * Flush tlb
                 * (probably not needed, doesn't clobber a0-a3)
                 */
                jal     tlbflush
                tlbwi                                   # delay slot

                /*
                 * Restore fpu state:
                 *  - cp0 status register bits
                 *  - fp gp registers
                 *  - cp1 status/control register
                 */
                ori     t1,a2,1                         # pipeline magic
                xori    t1,1
                mtc0    t1,CP0_STATUS
                sll     t0,a2,2
                bgez    t0,2f
                sll     t0,a2,5                         # delay slot
                bgez    t0,1f
                ldc1    $f0,(TOFF_FPU+0)(a0)            # delay slot
                /*
                 * Restore the 16 odd double precision registers only
                 * when enabled in the cp0 status register.
                 */
                ldc1    $f1,(TOFF_FPU+8)(a0)
                ldc1    $f3,(TOFF_FPU+24)(a0)
                ldc1    $f5,(TOFF_FPU+40)(a0)
                ldc1    $f7,(TOFF_FPU+56)(a0)
                ldc1    $f9,(TOFF_FPU+72)(a0)
                ldc1    $f11,(TOFF_FPU+88)(a0)
                ldc1    $f13,(TOFF_FPU+104)(a0)
                ldc1    $f15,(TOFF_FPU+120)(a0)
                ldc1    $f17,(TOFF_FPU+136)(a0)
                ldc1    $f19,(TOFF_FPU+152)(a0)
                ldc1    $f21,(TOFF_FPU+168)(a0)
                ldc1    $f23,(TOFF_FPU+184)(a0)
                ldc1    $f25,(TOFF_FPU+200)(a0)
                ldc1    $f27,(TOFF_FPU+216)(a0)
                ldc1    $f29,(TOFF_FPU+232)(a0)
                ldc1    $f31,(TOFF_FPU+248)(a0)

                /*
                 * Restore the 16 even double precision registers
                 * when cp1 was enabled in the cp0 status register.
                 */
1:              lw      t0,(TOFF_FPU+256)(a0)
                ldc1    $f2,(TOFF_FPU+16)(a0)
                ldc1    $f4,(TOFF_FPU+32)(a0)
                ldc1    $f6,(TOFF_FPU+48)(a0)
                ldc1    $f8,(TOFF_FPU+64)(a0)
                ldc1    $f10,(TOFF_FPU+80)(a0)
                ldc1    $f12,(TOFF_FPU+96)(a0)
                ldc1    $f14,(TOFF_FPU+112)(a0)
                ldc1    $f16,(TOFF_FPU+128)(a0)
                ldc1    $f18,(TOFF_FPU+144)(a0)
                ldc1    $f20,(TOFF_FPU+160)(a0)
                ldc1    $f22,(TOFF_FPU+176)(a0)
                ldc1    $f24,(TOFF_FPU+192)(a0)
                ldc1    $f26,(TOFF_FPU+208)(a0)
                ldc1    $f28,(TOFF_FPU+224)(a0)
                ldc1    $f30,(TOFF_FPU+240)(a0)
                ctc1    t0,fcr31

                /*
                 * Restore non-scratch registers
                 */
2:              lw      s0,TOFF_REG16(a0)
                lw      s1,TOFF_REG17(a0)
                lw      s2,TOFF_REG18(a0)
                lw      s3,TOFF_REG19(a0)
                lw      s4,TOFF_REG20(a0)
                lw      s5,TOFF_REG21(a0)
                lw      s6,TOFF_REG22(a0)
                lw      s7,TOFF_REG23(a0)
                lw      gp,TOFF_REG28(a0)
                lw      sp,TOFF_REG29(a0)
                lw      fp,TOFF_REG30(a0)
                lw      ra,TOFF_REG31(a0)

                /*
                 * Restore status register
                 */
                lw      t0,TOFF_KSP(a0)
                sw      t0,kernelsp

                jr      ra
                mtc0    a2,CP0_STATUS                   # delay slot
                END(resume)

                /*
                 * Load a new root pointer into the tlb
                 */
                .set    noreorder
                LEAF(load_pgd)
                /*
                 * Switch the root pointer
                 */
                mfc0    t0,CP0_STATUS
                ori     t1,t0,1
                xori    t1,1
                mtc0    t1,CP0_STATUS
                srl     a0,6
                ori     a0,MODE_ALIAS
                li      t1,TLB_ROOT
                mtc0    t1,CP0_ENTRYHI
                mtc0    zero,CP0_INDEX
                mtc0    a0,CP0_ENTRYLO0
                mtc0    zero,CP0_ENTRYLO1
                mtc0    t0,CP0_STATUS
                j       tlbflush
                tlbwi                                   # delay slot
                END(load_pgd)

/*
 * Some bits in the config register
 */
#define CONFIG_DB       (1<<4)
#define CONFIG_IB       (1<<5)

/*
 * Flush instruction/data caches
 *
 * Parameters: a0 - starting address to flush
 *             a1 - size of area to be flushed
 *             a2 - which caches to be flushed
 *
 * FIXME:      - ignores parameters in a0/a1
 *             - doesn't know about second level caches
 */
                .set    noreorder
                LEAF(sys_cacheflush)
                andi    t1,a2,DCACHE
                beqz    t1,do_icache
                li      t0,KSEG0                        # delay slot

                /*
                 * Writeback data cache, even lines
                 */
                li      t1,CACHELINES-1
1:              cache   Index_Writeback_Inv_D,0(t0)
                cache   Index_Writeback_Inv_D,32(t0)
                cache   Index_Writeback_Inv_D,64(t0)
                cache   Index_Writeback_Inv_D,96(t0)
                cache   Index_Writeback_Inv_D,128(t0)
                cache   Index_Writeback_Inv_D,160(t0)
                cache   Index_Writeback_Inv_D,192(t0)
                cache   Index_Writeback_Inv_D,224(t0)
                cache   Index_Writeback_Inv_D,256(t0)
                cache   Index_Writeback_Inv_D,288(t0)
                cache   Index_Writeback_Inv_D,320(t0)
                cache   Index_Writeback_Inv_D,352(t0)
                cache   Index_Writeback_Inv_D,384(t0)
                cache   Index_Writeback_Inv_D,416(t0)
                cache   Index_Writeback_Inv_D,448(t0)
                cache   Index_Writeback_Inv_D,480(t0)
                addiu   t0,512
                bnez    t1,1b
                subu    t1,1

                /*
                 * Writeback data cache, odd lines
                 * Only needed for 16 byte line size
                 */
                mfc0    t1,CP0_CONFIG
                andi    t1,CONFIG_DB
                bnez    t1,do_icache
                li      t1,CACHELINES-1
1:              cache   Index_Writeback_Inv_D,16(t0)
                cache   Index_Writeback_Inv_D,48(t0)
                cache   Index_Writeback_Inv_D,80(t0)
                cache   Index_Writeback_Inv_D,112(t0)
                cache   Index_Writeback_Inv_D,144(t0)
                cache   Index_Writeback_Inv_D,176(t0)
                cache   Index_Writeback_Inv_D,208(t0)
                cache   Index_Writeback_Inv_D,240(t0)
                cache   Index_Writeback_Inv_D,272(t0)
                cache   Index_Writeback_Inv_D,304(t0)
                cache   Index_Writeback_Inv_D,336(t0)
                cache   Index_Writeback_Inv_D,368(t0)
                cache   Index_Writeback_Inv_D,400(t0)
                cache   Index_Writeback_Inv_D,432(t0)
                cache   Index_Writeback_Inv_D,464(t0)
                cache   Index_Writeback_Inv_D,496(t0)
                addiu   t0,512
                bnez    t1,1b
                subu    t1,1

do_icache:      andi    t1,a2,ICACHE
                beqz    t1,done

                /*
                 * Flush instruction cache, even lines
                 */
                lui     t0,0x8000
                li      t1,CACHELINES-1
1:              cache   Index_Invalidate_I,0(t0)        
                cache   Index_Invalidate_I,32(t0)
                cache   Index_Invalidate_I,64(t0)
                cache   Index_Invalidate_I,96(t0)
                cache   Index_Invalidate_I,128(t0)
                cache   Index_Invalidate_I,160(t0)
                cache   Index_Invalidate_I,192(t0)
                cache   Index_Invalidate_I,224(t0)
                cache   Index_Invalidate_I,256(t0)
                cache   Index_Invalidate_I,288(t0)
                cache   Index_Invalidate_I,320(t0)
                cache   Index_Invalidate_I,352(t0)
                cache   Index_Invalidate_I,384(t0)
                cache   Index_Invalidate_I,416(t0)
                cache   Index_Invalidate_I,448(t0)
                cache   Index_Invalidate_I,480(t0)
                addiu   t0,512
                bnez    t1,1b
                subu    t1,1

                /*
                 * Flush instruction cache, even lines
                 * Only needed for 16 byte line size
                 */
                mfc0    t1,CP0_CONFIG
                andi    t1,CONFIG_IB
                bnez    t1,done
                li      t1,CACHELINES-1
1:              cache   Index_Invalidate_I,16(t0)
                cache   Index_Invalidate_I,48(t0)
                cache   Index_Invalidate_I,80(t0)
                cache   Index_Invalidate_I,112(t0)
                cache   Index_Invalidate_I,144(t0)
                cache   Index_Invalidate_I,176(t0)
                cache   Index_Invalidate_I,208(t0)
                cache   Index_Invalidate_I,240(t0)
                cache   Index_Invalidate_I,272(t0)
                cache   Index_Invalidate_I,304(t0)
                cache   Index_Invalidate_I,336(t0)
                cache   Index_Invalidate_I,368(t0)
                cache   Index_Invalidate_I,400(t0)
                cache   Index_Invalidate_I,432(t0)
                cache   Index_Invalidate_I,464(t0)
                cache   Index_Invalidate_I,496(t0)
                addiu   t0,512
                bnez    t1,1b
                subu    t1,1

done:           j       ra
                nop
                END(sys_cacheflush)

/*
 * Update the TLB - or how instruction scheduling makes code unreadable ...
 *
 * MIPS doesn't need any external MMU info: the kernel page tables contain
 * all the necessary information.  We use this hook though to load the
 * TLB as early as possible with uptodate information avoiding unnecessary
 * exceptions.
 *
 * Parameters: a0 - struct vm_area_struct *vma  (ignored)
 *             a1 - unsigned long address
 *             a2 - pte_t pte
 */
                .set    noreorder
                LEAF(update_mmu_cache)
                /*
                 * Step 1: Wipe out old TLB information.  Not sure if
                 * we really need that step; call it paranoia ...
                 * In order to do that we need to disable interrupts.
                 */
                mfc0    t0,CP0_STATUS           # interrupts off
                ori     t1,t0,1
                xori    t1,1
                mtc0    t1,CP0_STATUS
                li      t3,TLBMAP               # then wait 3 cycles
                ori     t1,a1,0xfff             # mask off low 12 bits
                xori    t1,0xfff
                mfc0    t2,CP0_ENTRYHI          # copy ASID into address
                andi    t2,0xff
                or      t2,t1
                mtc0    t2,CP0_ENTRYHI
                srl     t4,a1,12                # wait again three cycles
                sll     t4,t4,PTRLOG
                dmtc0   zero,CP0_ENTRYLO0
                tlbp                            # now query the TLB
                addu    t3,t4                   # wait another three cycles
                ori     t3,0xffff
                xori    t3,0xffff
                mfc0    t1,CP0_INDEX
                bltz    t1,1f                   # No old entry?
                dmtc0   zero,CP0_ENTRYLO1
                or      t3,t1                   # wait one cycle
                tlbwi
                /*
                 * But there still might be a entry for the pgd ...
                 */
1:              mtc0    t3,CP0_ENTRYHI
                nop                             # wait 3 cycles
                nop
                nop
                tlbp                            # TLB lookup
                nop
                nop
                mfc0    t1,CP0_INDEX            # wait 3 cycles
                bltz    t1,1f                   # No old entry?
                nop             
                tlbwi                           # gotcha ...
                /*
                 * Step 2: Reload the TLB with new information.  We can skip
                 * this but this should speed the mess a bit by avoiding
                 * tlbl/tlbs exceptions. (To be done)
                 */
1:              jr      ra
                mtc0    t0,CP0_STATUS           # delay slot
                END(update_mmu_cache)

Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.