/*
 * 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

#endif /* __ASM_MACH_CAVIUM_OCTEON_KERNEL_ENTRY_H */
