/*
 * 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) 2011-2012 by Broadcom Corporation
 *
 * Init for bmips 5000.
 * Used to init second core in dual core 5000's.
 */

#include <linux/init.h>

#include <asm/asm.h>
#include <asm/asmmacro.h>
#include <asm/cacheops.h>
#include <asm/regdef.h>
#include <asm/mipsregs.h>
#include <asm/stackframe.h>
#include <asm/addrspace.h>
#include <asm/hazards.h>
#include <asm/bmips.h>

#ifdef CONFIG_CPU_BMIPS5000


#define cacheop(kva, size, linesize, op) 	\
	.set noreorder			;	\
	addu		t1, kva, size	;	\
	subu		t2, linesize, 1	;	\
	not		t2		;	\
	and		t0, kva, t2	;	\
	addiu		t1, t1, -1	;	\
	and		t1, t2		;	\
9:	cache		op, 0(t0)	;	\
	bne		t0, t1, 9b	;	\
	 addu		t0, linesize	;	\
	.set reorder			;



#define	IS_SHIFT	22
#define	IL_SHIFT	19
#define	IA_SHIFT	16
#define	DS_SHIFT	13
#define	DL_SHIFT	10
#define	DA_SHIFT	 7
#define	IS_MASK		 7
#define	IL_MASK		 7
#define	IA_MASK		 7
#define	DS_MASK		 7
#define	DL_MASK		 7
#define	DA_MASK		 7
#define	ICE_MASK	0x80000000
#define	DCE_MASK	0x40000000

#define CP0_BRCM_CONFIG0	$22, 0
#define CP0_BRCM_MODE		$22, 1
#define	CP0_CONFIG_K0_MASK	7

#define CP0_ICACHE_TAG_LO	$28
#define CP0_ICACHE_DATA_LO	$28, 1
#define CP0_DCACHE_TAG_LO	$28, 2
#define CP0_D_SEC_CACHE_DATA_LO	$28, 3
#define CP0_ICACHE_TAG_HI	$29
#define CP0_ICACHE_DATA_HI	$29, 1
#define CP0_DCACHE_TAG_HI	$29, 2

#define CP0_BRCM_MODE_Luc_MASK		(1 << 11)
#define	CP0_BRCM_CONFIG0_CWF_MASK	(1 << 20)
#define	CP0_BRCM_CONFIG0_TSE_MASK	(1 << 19)
#define CP0_BRCM_MODE_SET_MASK		(1 << 7)
#define CP0_BRCM_MODE_ClkRATIO_MASK	(7 << 4)
#define CP0_BRCM_MODE_BrPRED_MASK 	(3 << 24)
#define CP0_BRCM_MODE_BrPRED_SHIFT	24
#define CP0_BRCM_MODE_BrHIST_MASK 	(0x1f << 20)
#define CP0_BRCM_MODE_BrHIST_SHIFT	20

/* ZSC L2 Cache Register Access Register Definitions */
#define BRCM_ZSC_ALL_REGS_SELECT		0x7 << 24

#define BRCM_ZSC_CONFIG_REG			0 << 3
#define BRCM_ZSC_REQ_BUFFER_REG			2 << 3
#define BRCM_ZSC_RBUS_ADDR_MAPPING_REG0		4 << 3
#define BRCM_ZSC_RBUS_ADDR_MAPPING_REG1		6 << 3
#define BRCM_ZSC_RBUS_ADDR_MAPPING_REG2		8 << 3

#define BRCM_ZSC_SCB0_ADDR_MAPPING_REG0		0xa << 3
#define BRCM_ZSC_SCB0_ADDR_MAPPING_REG1		0xc << 3

#define BRCM_ZSC_SCB1_ADDR_MAPPING_REG0		0xe << 3
#define BRCM_ZSC_SCB1_ADDR_MAPPING_REG1		0x10 << 3

#define BRCM_ZSC_CONFIG_LMB1En			1 << (15)
#define BRCM_ZSC_CONFIG_LMB0En			1 << (14)

/* branch predition values */

#define BRCM_BrPRED_ALL_TAKEN		(0x0)
#define BRCM_BrPRED_ALL_NOT_TAKEN	(0x1)
#define BRCM_BrPRED_BHT_ENABLE		(0x2)
#define BRCM_BrPRED_PREDICT_BACKWARD	(0x3)



.align 2
/*
 * Function: 	size_i_cache
 * Arguments: 	None
 * Returns:	v0 = i cache size, v1 = I cache line size
 * Description: compute the I-cache size and I-cache line size
 * Trashes:	v0, v1, a0, t0
 *
 *	pseudo code:
 *
 */

LEAF(size_i_cache)
	.set	noreorder

	mfc0	a0, CP0_CONFIG, 1
	move	t0, a0

	/*
	 * Determine sets per way: IS
	 *
	 * This field contains the number of sets (i.e., indices) per way of
	 * the instruction cache:
	 * i) 0x0: 64, ii) 0x1: 128, iii) 0x2: 256, iv) 0x3: 512, v) 0x4: 1k
	 * vi) 0x5 - 0x7: Reserved.
	 */

	srl	a0, a0, IS_SHIFT
	and	a0, a0, IS_MASK

	/* sets per way = (64<<IS) */

	li	v0, 0x40
	sllv	v0, v0, a0

	/*
	 * Determine line size
	 *
	 * This field contains the line size of the instruction cache:
	 * i) 0x0: No I-cache present, i) 0x3: 16 bytes, ii) 0x4: 32 bytes, iii)
	 * 0x5: 64 bytes, iv) the rest: Reserved.
	 */

	move	a0, t0

	srl	a0, a0, IL_SHIFT
	and	a0, a0, IL_MASK

	beqz	a0, no_i_cache
	nop

	/* line size = 2 ^ (IL+1) */

	addi	a0, a0, 1
	li	v1, 1
	sll	v1, v1, a0

	/* v0 now have sets per way, multiply it by line size now
	 * that will give the set size
	 */

	sll	v0, v0, a0

	/*
	 * Determine set associativity
	 *
	 * This field contains the set associativity of the instruction cache.
	 * i) 0x0: Direct mapped, ii) 0x1: 2-way, iii) 0x2: 3-way, iv) 0x3:
	 * 4-way, v) 0x4 - 0x7: Reserved.
	 */

	move	a0, t0

	srl	a0, a0, IA_SHIFT
	and	a0, a0, IA_MASK
	addi	a0, a0, 0x1

	/* v0 has the set size, multiply it by
	 * set associativiy, to get the cache size
	 */

	multu	v0, a0	/*multu is interlocked, so no need to insert nops */
	mflo	v0
	b	1f
	nop

no_i_cache:
	move	v0, zero
	move	v1, zero
1:
	jr	ra
	nop
	.set	reorder

END(size_i_cache)

/*
 * Function: 	size_d_cache
 * Arguments: 	None
 * Returns:	v0 = d cache size, v1 = d cache line size
 * Description: compute the D-cache size and D-cache line size.
 * Trashes:	v0, v1, a0, t0
 *
 */

LEAF(size_d_cache)
	.set	noreorder

	mfc0	a0, CP0_CONFIG, 1
	move	t0, a0

	/*
	 * Determine sets per way: IS
	 *
	 * This field contains the number of sets (i.e., indices) per way of
	 * the instruction cache:
	 * i) 0x0: 64, ii) 0x1: 128, iii) 0x2: 256, iv) 0x3: 512, v) 0x4: 1k
	 * vi) 0x5 - 0x7: Reserved.
	 */

	srl	a0, a0, DS_SHIFT
	and	a0, a0, DS_MASK

	/* sets per way = (64<<IS) */

	li	v0, 0x40
	sllv	v0, v0, a0

	/*
	 * Determine line size
	 *
	 * This field contains the line size of the instruction cache:
	 * i) 0x0: No I-cache present, i) 0x3: 16 bytes, ii) 0x4: 32 bytes, iii)
	 * 0x5: 64 bytes, iv) the rest: Reserved.
	 */
	move	a0, t0

	srl	a0, a0, DL_SHIFT
	and	a0, a0, DL_MASK

	beqz	a0, no_d_cache
	nop

	/* line size = 2 ^ (IL+1) */

	addi	a0, a0, 1
	li	v1, 1
	sll	v1, v1, a0

	/* v0 now have sets per way, multiply it by line size now
	 * that will give the set size
	 */

	sll	v0, v0, a0

	/* determine set associativity
	 *
	 * This field contains the set associativity of the instruction cache.
	 * i) 0x0: Direct mapped, ii) 0x1: 2-way, iii) 0x2: 3-way, iv) 0x3:
	 * 4-way, v) 0x4 - 0x7: Reserved.
	 */

	move	a0, t0

	srl	a0, a0, DA_SHIFT
	and	a0, a0, DA_MASK
	addi	a0, a0, 0x1

	/* v0 has the set size, multiply it by
	 * set associativiy, to get the cache size
	 */

	multu	v0, a0	/*multu is interlocked, so no need to insert nops */
	mflo	v0

	b	1f
	nop

no_d_cache:
	move	v0, zero
	move	v1, zero
1:
	jr	ra
	nop
	.set	reorder

END(size_d_cache)


/*
 * Function: enable_ID
 * Arguments: 	None
 * Returns:	None
 * Description: Enable I and D caches, initialize I and D-caches, also set
 *		hardware delay for d-cache (TP0).
 * Trashes:	t0
 *
 */
	.global	enable_ID
	.ent	enable_ID
	.set	noreorder
enable_ID:
	mfc0	t0, CP0_BRCM_CONFIG0
	or	t0, t0, (ICE_MASK | DCE_MASK)
	mtc0	t0, CP0_BRCM_CONFIG0
	jr	ra
	nop

	.end	enable_ID
	.set	reorder


/*
 * Function: l1_init
 * Arguments: 	None
 * Returns:	None
 * Description: Enable I and D caches, and initialize I and D-caches
 * Trashes:	a0, v0, v1, t0, t1, t2, t8
 *
 */
	.globl	l1_init
	.ent	l1_init
	.set	noreorder
l1_init:

	/* save return address */
	move	t8, ra


	/* initialize I and D cache Data and Tag registers.  */
	mtc0	zero, CP0_ICACHE_TAG_LO
	mtc0	zero, CP0_ICACHE_TAG_HI
	mtc0	zero, CP0_ICACHE_DATA_LO
	mtc0	zero, CP0_ICACHE_DATA_HI
	mtc0	zero, CP0_DCACHE_TAG_LO
	mtc0	zero, CP0_DCACHE_TAG_HI

	/* Enable Caches before Clearing. If the caches are disabled
	 * then the cache operations to clear the cache will be ignored
	 */

	jal	enable_ID
	nop

	jal	size_i_cache	/* v0 = i-cache size, v1 = i-cache line size */
	nop

	/* run uncached in kseg 1 */
	la	k0, 1f
	lui	k1, 0x2000
	or	k0, k1, k0
	jr	k0
	nop
1:

	/*
	 * set K0 cache mode
	 */

	mfc0	t0, CP0_CONFIG
	and	t0, t0, ~CP0_CONFIG_K0_MASK
	or	t0, t0, 3	/* Write Back mode */
	mtc0	t0, CP0_CONFIG

	/*
	 * Initialize instruction cache.
	 */

	li	a0, KSEG0
	cacheop(a0, v0, v1, Index_Store_Tag_I)

	/*
	 * Now we can run from I-$, kseg 0
	 */
	la	k0, 1f
	lui	k1, 0x2000
	or	k0, k1, k0
	xor	k0, k1, k0
	jr	k0
	nop
1:
	/*
	 * Initialize data cache.
	 */

	jal	size_d_cache	/* v0 = d-cache size, v1 = d-cache line size */
	nop


	li	a0, KSEG0
	cacheop(a0, v0, v1, Index_Store_Tag_D)

	jr	t8
	nop

	.end 	l1_init
	.set	reorder


/*
 * Function: 	set_other_config
 * Arguments:	none
 * Returns:	None
 * Description: initialize other remainder configuration to defaults.
 * Trashes:	t0, t1
 *
 *	pseudo code:
 *
 */
LEAF(set_other_config)
	.set noreorder

	/* enable Bus error for I-fetch */
	mfc0	t0, CP0_CACHEERR, 0
	li	t1, 0x4
	or	t0, t1
	mtc0	t0, CP0_CACHEERR, 0

	/* enable Bus error for Load */
	mfc0	t0, CP0_CACHEERR, 1
	li	t1, 0x4
	or	t0, t1
	mtc0	t0, CP0_CACHEERR, 1

	/* enable Bus Error for Store */
	mfc0	t0, CP0_CACHEERR, 2
	li	t1, 0x4
	or	t0, t1
	mtc0	t0, CP0_CACHEERR, 2

	jr	ra
	nop
	.set reorder
END(set_other_config)

/*
 * Function: 	set_branch_pred
 * Arguments:	none
 * Returns:	None
 * Description:
 * Trashes:	t0, t1
 *
 *	pseudo code:
 *
 */

LEAF(set_branch_pred)
	.set noreorder
	mfc0	t0, CP0_BRCM_MODE
	li	t1, ~(CP0_BRCM_MODE_BrPRED_MASK | CP0_BRCM_MODE_BrHIST_MASK )
	and	t0, t0, t1

	/* enable Branch prediction */
	li	t1, BRCM_BrPRED_BHT_ENABLE
	sll	t1, CP0_BRCM_MODE_BrPRED_SHIFT
	or	t0, t0, t1

	/* set history count to 8 */
	li	t1, 8
	sll	t1, CP0_BRCM_MODE_BrHIST_SHIFT
	or	t0, t0, t1

	mtc0	t0, CP0_BRCM_MODE
	jr	ra
	nop
	.set	reorder
END(set_branch_pred)


/*
 * Function: 	set_luc
 * Arguments:	set link uncached.
 * Returns:	None
 * Description:
 * Trashes:	t0, t1
 *
 */
LEAF(set_luc)
	.set noreorder
	mfc0	t0, CP0_BRCM_MODE
	li	t1, ~(CP0_BRCM_MODE_Luc_MASK)
	and	t0, t0, t1

	/* set Luc */
	ori	t0, t0, CP0_BRCM_MODE_Luc_MASK

	mtc0	t0, CP0_BRCM_MODE
	jr	ra
	nop
	.set	reorder
END(set_luc)

/*
 * Function: 	set_cwf_tse
 * Arguments:	set CWF and TSE bits
 * Returns:	None
 * Description:
 * Trashes:	t0, t1
 *
 */
LEAF(set_cwf_tse)
	.set noreorder
	mfc0	t0, CP0_BRCM_CONFIG0
	li	t1, (CP0_BRCM_CONFIG0_CWF_MASK | CP0_BRCM_CONFIG0_TSE_MASK)
	or	t0, t0, t1

	mtc0	t0, CP0_BRCM_CONFIG0
	jr	ra
	nop
	.set	reorder
END(set_cwf_tse)

/*
 * Function: 	set_clock_ratio
 * Arguments:	set clock ratio specified by a0
 * Returns:	None
 * Description:
 * Trashes:	v0, v1, a0, a1
 *
 *	pseudo code:
 *
 */
LEAF(set_clock_ratio)
	.set noreorder

	mfc0	t0, CP0_BRCM_MODE
	li	t1, ~(CP0_BRCM_MODE_SET_MASK | CP0_BRCM_MODE_ClkRATIO_MASK)
	and	t0, t0, t1
	li	t1, CP0_BRCM_MODE_SET_MASK
	or	t0, t0, t1
	or	t0, t0, a0
	mtc0	t0, CP0_BRCM_MODE
	jr	ra
	nop
	.set	reorder
END(set_clock_ratio)
/*
 * Function: set_zephyr
 * Arguments:	None
 * Returns:	None
 * Description: Set any zephyr bits
 * Trashes:	t0 & t1
 *
 */
LEAF(set_zephyr)
	.set	noreorder

	/* enable read/write of CP0 #22 sel. 8 */
	li	t0, 0x5a455048
	.word	0x4088b00f	/* mtc0    t0, $22, 15 */

	.word	0x4008b008	/* mfc0    t0, $22, 8 */
	li	t1, 0x09008000	/* turn off pref, jtb */
	or	t0, t0, t1
	.word	0x4088b008	/* mtc0    t0, $22, 8 */
	sync

	/* disable read/write of CP0 #22 sel 8 */
	li	t0, 0x0
	.word	0x4088b00f	/* mtc0    t0, $22, 15 */


	jr	ra
	nop
	.set reorder

END(set_zephyr)


/*
 * Function:	set_llmb
 * Arguments:	a0=0 disable llmb, a0=1 enables llmb
 * Returns:	None
 * Description:
 * Trashes:	t0, t1, t2
 *
 *      pseudo code:
 *
 */
LEAF(set_llmb)
	.set noreorder

	li	t2, 0x90000000 | BRCM_ZSC_ALL_REGS_SELECT | BRCM_ZSC_CONFIG_REG
	sync
	cache	0x7, 0x0(t2)
	sync
	mfc0	t0, CP0_D_SEC_CACHE_DATA_LO
	li	t1, ~(BRCM_ZSC_CONFIG_LMB1En | BRCM_ZSC_CONFIG_LMB0En)
	and	t0, t0, t1

	beqz	a0, svlmb
	nop

enable_lmb:
	li	t1, (BRCM_ZSC_CONFIG_LMB1En | BRCM_ZSC_CONFIG_LMB0En)
	or	t0, t0, t1

svlmb:
	mtc0	t0, CP0_D_SEC_CACHE_DATA_LO
	sync
	cache	0xb, 0x0(t2)
	sync

	jr	ra
	nop
	.set reorder

END(set_llmb)
/*
 * Function: 	core_init
 * Arguments:	none
 * Returns:	None
 * Description: initialize core related configuration
 * Trashes:	v0,v1,a0,a1,t8
 *
 *	pseudo code:
 *
 */
	.globl	core_init
	.ent	core_init
	.set	noreorder
core_init:
	move	t8, ra

	/* set Zephyr bits. */
	bal	set_zephyr
	nop

	/* set low latency memory bus */
	li	a0, 1
	bal	set_llmb
	nop

	/* set branch prediction (TP0 only) */
	bal	set_branch_pred
	nop

	/* set link uncached */
	bal	set_luc
	nop

	/* set CWF and TSE */
	bal	set_cwf_tse
	nop

	/*
	 *set clock ratio by setting 1 to 'set'
	 * and 0 to ClkRatio, (TP0 only)
	 */
	li	a0, 0
	bal	set_clock_ratio
	nop

	/* set other configuration to defaults */
	bal	set_other_config
	nop

	move	ra, t8
	jr	ra
	nop

	.set reorder
	.end	core_init

/*
 * Function: 	clear_jump_target_buffer
 * Arguments:	None
 * Returns:	None
 * Description:
 * Trashes:	t0, t1, t2
 *
 */
#define RESET_CALL_RETURN_STACK_THIS_THREAD		(0x06<<16)
#define RESET_JUMP_TARGET_BUFFER_THIS_THREAD		(0x04<<16)
#define JTB_CS_CNTL_MASK				(0xFF<<16)

	.globl	clear_jump_target_buffer
	.ent	clear_jump_target_buffer
	.set	noreorder
clear_jump_target_buffer:

	mfc0	t0, $22, 2
	nop
	nop

	li	t1, ~JTB_CS_CNTL_MASK
	and	t0, t0, t1
	li	t2, RESET_CALL_RETURN_STACK_THIS_THREAD
	or	t0, t0, t2
	mtc0	t0, $22, 2
	nop
	nop

	and	t0, t0, t1
	li	t2, RESET_JUMP_TARGET_BUFFER_THIS_THREAD
	or	t0, t0, t2
	mtc0	t0, $22, 2
	nop
	nop
	jr	ra
	nop

	.end	clear_jump_target_buffer
	.set	reorder
/*
 * Function: 	bmips_cache_init
 * Arguments: 	None
 * Returns:	None
 * Description: Enable I and D caches, and initialize I and D-caches
 * Trashes:	v0, v1, t0, t1, t2, t5, t7, t8
 *
 */
	.globl	bmips_5xxx_init
	.ent	bmips_5xxx_init
	.set	noreorder
bmips_5xxx_init:

	/* save return address and A0 */
	move	t7, ra
	move	t5, a0

	jal	l1_init
	nop

	jal	core_init
	nop

	jal	clear_jump_target_buffer
	nop

	mtc0	zero, CP0_CAUSE

	move 	a0, t5
	jr	t7
	nop

	.end 	bmips_5xxx_init
	.set	reorder


#endif