root/arch/mips/include/asm/mach-cavium-octeon/kernel-entry-init.h
/*
 * This file is subject to the terms and conditions of the GNU General Public
 * License.  See the file "COPYING" in the main directory of this archive
 * for more details.
 *
 * Copyright (C) 2005-2008 Cavium Networks, Inc
 */
#ifndef __ASM_MACH_CAVIUM_OCTEON_KERNEL_ENTRY_H
#define __ASM_MACH_CAVIUM_OCTEON_KERNEL_ENTRY_H

#define CP0_CVMCTL_REG $9, 7
#define CP0_CVMMEMCTL_REG $11,7
#define CP0_PRID_REG $15, 0
#define CP0_DCACHE_ERR_REG $27, 1
#define CP0_PRID_OCTEON_PASS1 0x000d0000
#define CP0_PRID_OCTEON_CN30XX 0x000d0200

.macro  kernel_entry_setup
        # Registers set by bootloader:
        # (only 32 bits set by bootloader, all addresses are physical
        # addresses, and need to have the appropriate memory region set
        # by the kernel
        # a0 = argc
        # a1 = argv (kseg0 compat addr)
        # a2 = 1 if init core, zero otherwise
        # a3 = address of boot descriptor block
        .set push
        .set arch=octeon
        # Read the cavium mem control register
        dmfc0   v0, CP0_CVMMEMCTL_REG
        # Clear the lower 6 bits, the CVMSEG size
        dins    v0, $0, 0, 6
        ori     v0, CONFIG_CAVIUM_OCTEON_CVMSEG_SIZE
        dmtc0   v0, CP0_CVMMEMCTL_REG   # Write the cavium mem control register
        dmfc0   v0, CP0_CVMCTL_REG      # Read the cavium control register
        # Disable unaligned load/store support but leave HW fixup enabled
        # Needed for octeon specific memcpy
        or  v0, v0, 0x5001
        xor v0, v0, 0x1001
        # First clear off CvmCtl[IPPCI] bit and move the performance
        # counters interrupt to IRQ 6
        dli     v1, ~(7 << 7)
        and     v0, v0, v1
        ori     v0, v0, (6 << 7)

        mfc0    v1, CP0_PRID_REG
        and     t1, v1, 0xfff8
        xor     t1, t1, 0x9000          # 63-P1
        beqz    t1, 4f
        and     t1, v1, 0xfff8
        xor     t1, t1, 0x9008          # 63-P2
        beqz    t1, 4f
        and     t1, v1, 0xfff8
        xor     t1, t1, 0x9100          # 68-P1
        beqz    t1, 4f
        and     t1, v1, 0xff00
        xor     t1, t1, 0x9200          # 66-PX
        bnez    t1, 5f                  # Skip WAR for others.
        and     t1, v1, 0x00ff
        slti    t1, t1, 2               # 66-P1.2 and later good.
        beqz    t1, 5f

4:      # core-16057 work around
        or      v0, v0, 0x2000          # Set IPREF bit.

5:      # No core-16057 work around
        # Write the cavium control register
        dmtc0   v0, CP0_CVMCTL_REG
        sync
        # Flush dcache after config change
        cache   9, 0($0)
        # Zero all of CVMSEG to make sure parity is correct
        dli     v0, CONFIG_CAVIUM_OCTEON_CVMSEG_SIZE
        dsll    v0, 7
        beqz    v0, 2f
1:      dsubu   v0, 8
        sd      $0, -32768(v0)
        bnez    v0, 1b
2:
        mfc0    v0, CP0_PRID_REG
        bbit0   v0, 15, 1f
        # OCTEON II or better have bit 15 set.  Clear the error bits.
        and     t1, v0, 0xff00
        dli     v0, 0x9500
        bge     t1, v0, 1f  # OCTEON III has no DCACHE_ERR_REG COP0
        dli     v0, 0x27
        dmtc0   v0, CP0_DCACHE_ERR_REG
1:
        # Get my core id
        rdhwr   v0, $0
        # Jump the master to kernel_entry
        bne     a2, zero, octeon_main_processor
        nop

#ifdef CONFIG_SMP

        #
        # All cores other than the master need to wait here for SMP bootstrap
        # to begin
        #

octeon_spin_wait_boot:
#ifdef CONFIG_RELOCATABLE
        PTR_LA  t0, octeon_processor_relocated_kernel_entry
        LONG_L  t0, (t0)
        beq     zero, t0, 1f
        nop

        jr      t0
        nop
1:
#endif /* CONFIG_RELOCATABLE */

        # This is the variable where the next core to boot is stored
        PTR_LA  t0, octeon_processor_boot
        # Get the core id of the next to be booted
        LONG_L  t1, (t0)
        # Keep looping if it isn't me
        bne t1, v0, octeon_spin_wait_boot
        nop
        # Get my GP from the global variable
        PTR_LA  t0, octeon_processor_gp
        LONG_L  gp, (t0)
        # Get my SP from the global variable
        PTR_LA  t0, octeon_processor_sp
        LONG_L  sp, (t0)
        # Set the SP global variable to zero so the master knows we've started
        LONG_S  zero, (t0)
#ifdef __OCTEON__
        syncw
        syncw
#else
        sync
#endif
        # Jump to the normal Linux SMP entry point
        j   smp_bootstrap
        nop
#else /* CONFIG_SMP */

        #
        # Someone tried to boot SMP with a non SMP kernel. All extra cores
        # will halt here.
        #
octeon_wait_forever:
        wait
        b   octeon_wait_forever
        nop

#endif /* CONFIG_SMP */
octeon_main_processor:
        .set pop
.endm

/*
 * Do SMP slave processor setup necessary before we can safely execute C code.
 */
        .macro  smp_slave_setup
        .endm

#define USE_KEXEC_SMP_WAIT_FINAL
        .macro  kexec_smp_wait_final
        .set push
        .set noreorder
        synci           0($0)
        .set pop
        .endm

#endif /* __ASM_MACH_CAVIUM_OCTEON_KERNEL_ENTRY_H */