/*
 *  mmuAsm.S
 *
 *  Copyright (C) 1999 Eric Valette (valette@crf.canon.fr)
 *
 *  This file contains the low-level support for various MMU
 *  features.
 *
 *  The license and distribution terms for this file may be
 *  found in the file LICENSE in this distribution or at
 *  http://www.rtems.org/license/LICENSE.
 *
 *  T. Straumann - 11/2001: added support for 7400 (no AltiVec yet)
 *  S.K. Feng    - 10/2003: added support for 7455 (no AltiVec yet)
 *
 */

#include <rtems/asm.h>
#include <rtems/score/cpu.h>
#include <libcpu/io.h>
#include <libcpu/bat.h>

/* Unfortunately, the CPU types defined in cpu.h are
 * an 'enum' type and hence not available :-(
 */
#define PPC_601   0x1
#define PPC_603   0x3
#define PPC_604   0x4
#define PPC_603e  0x6
#define PPC_603ev 0x7
#define PPC_750   0x8
#define PPC_604e  0x9
#define PPC_604r  0xA
#define PPC_7400  0xC
#define PPC_7455  0x8001
#define PPC_7457  0x8002
#define PPC_620   0x16
#define PPC_860   0x50
#define PPC_821   PPC_860
#define PPC_8260  0x81
#define PPC_8240  PPC_8260

/* ALTIVEC instructions (not recognized by off-the shelf gcc yet) */
#define DSSALL	.long	0x7e00066c		/* DSSALL altivec instruction opcode */

/* A couple of defines to make the code more readable */
#define CACHE_LINE_SIZE 32

#ifndef MSSCR0
#define MSSCR0   1014
#endif

#define DL1HWF	(1<<(31-8))
#define L2HWF	(1<<(31-20))

#FIXME Should really move this to C code

	.globl L1_caches_enables
	.type  L1_caches_enables, @function

L1_caches_enables:
	/*
	 * Enable caches and 604-specific features if necessary.
	 */
	mfspr	r9,PPC_PVR
	rlwinm	r9,r9,16,16,31
	cmpi	0,r9,PPC_601
	beq	4f			/* not needed for 601 */
	mfspr	r11,HID0
	andi.	r0,r11,HID0_DCE
	ori	r11,r11,HID0_ICE|HID0_DCE
	ori	r8,r11,HID0_ICFI
	bne	3f			/* don't invalidate the D-cache */
	ori	r8,r8,HID0_DCI		/* unless it wasn't enabled */
3:
	sync
	mtspr	HID0,r8			/* enable and invalidate caches */
	sync
	mtspr	HID0,r11		/* enable caches */
	sync
	isync
	cmpi	1,r9,PPC_604	/* check for 604 */
	cmpi	2,r9,PPC_604e	/* or 604e */
	cmpi	3,r9,PPC_604r	/* or mach5 */
	cror	6,6,10
	cror	6,6,14
	cmpi	2,r9,PPC_750	/* or 750 */
	cror	6,6,10
	cmpi	2,r9,PPC_7400	/* or 7400 */
	cror	6,6,10
	cmpli   0,r9,PPC_7455   /* or 7455 */
	beq     1f
	cmpli   0,r9,PPC_7457   /* or 7457 */
	bne		2f
1:
	/* 7455:link register stack,branch folding &
	 * TBEN : enable the time base and decrementer.
	 * EMCP bit is defined in HID1. However, it's not used
	 * in mvme5500 board because of GT64260 (e.g. it's connected
	 * pull-up).
	 */
	oris    r11,r11,(HID0_LRSTK|HID0_FOLD|HID0_TBEN)@h
	ori     r11,r11,(HID0_LRSTK|HID0_FOLD|HID0_TBEN)@l
2:	cror    2,2,10
	bne	3f
	ori	r11,r11,HID0_BTIC	/* enable branch tgt cache on 7400 , 7455 , 7457 */
3:	cror	2,2,6
	bne	4f
	/* on 7400 SIED is actually SGE (store gathering enable) */
	ori	r11,r11,HID0_SIED|HID0_BHTE /* for 604[e], enable */
	bne	2,5f
	ori	r11,r11,HID0_BTCD
5:	mtspr	HID0,r11		/* superscalar exec & br history tbl */
	sync		     /* for SGE bit */
	isync                /* P2-17 to 2-22 in MPC7450UM */
4:
	blr

	.globl get_L1CR
.type  get_L1CR, @function
get_L1CR:
	mfspr   r3,HID0
	blr

	.globl get_L2CR
	.type  get_L2CR, @function
get_L2CR:
	/* Make sure this is a > 750 chip */
	mfspr	r3,PPC_PVR
	rlwinm	r3,r3,16,16,31
	cmplwi	r3,PPC_750	/* it's a 750 */
	beq	1f
	cmplwi  r3,PPC_7400	/* it's a 7400 */
	beq	1f
	cmplwi  r3,PPC_7455	/* it's a 7455 */
	beq	1f
	cmplwi  r3,PPC_7457	/* it's a 7457 */
	beq	1f
	li	r3,-1
	blr

1:
	/* Return the L2CR contents */
	mfspr	r3,L2CR
	blr

	.globl set_L2CR
	.type  set_L2CR, @function
set_L2CR:
	/* Usage:
	 * When setting the L2CR register, you must do a few special things.
	 * If you are enabling the cache, you must perform a global invalidate.
	 * If you are disabling the cache, you must flush the cache contents first.
	 * This routine takes care of doing these things.  When first
	 * enabling the cache, make sure you pass in the L2CR you want, as well as
	 * passing in the global invalidate bit set.  A global invalidate will
	 * only be performed if the L2I bit is set in applyThis.  When enabling
	 * the cache, you should also set the L2E bit in applyThis.  If you
	 * want to modify the L2CR contents after the cache has been enabled,
	 * the recommended procedure is to first call __setL2CR(0) to disable
	 * the cache and then call it again with the new values for L2CR.  Examples:
	 *
	 *	_setL2CR(0)		-	disables the cache
	 *	_setL2CR(0xb9A14000)	-	enables my G3 MCP750 card:
	 *				-	L2E set to turn on the cache
	 *				-	L2SIZ set to 1MB
	 *				-	L2CLK set to %2
	 *				-	L2RAM set to pipelined syncronous late-write
	 *				-	L2I set to perform a global invalidation
	 *				-	L2OH set to 1 nS
	 *
	 * A similar call should work for your card.  You need to know the correct
	 * setting for your card and then place them in the fields I have outlined
	 * above.  Other fields support optional features, such as L2DO which caches
	 * only data, or L2TS which causes cache pushes from the L1 cache to go to
	 *the L2 cache instead of to main memory.
	 */

	/* Make sure this is a > 750 chip */
	mfspr	r0,PPC_PVR
	rlwinm	r0,r0,16,16,31
	cmplwi	r0,PPC_750
	beq	thisIs750
	cmplwi	r0,PPC_7400
	beq	thisIs750
	cmplwi	r0,PPC_7455
	beq	thisIs750
	cmplwi	r0,PPC_7457
	beq	thisIs750
	li	r3,-1
	blr

thisIs750:
	/* Get the current enable bit of the L2CR into r4 */
	mfspr	r4,L2CR
	rlwinm	r4,r4,0,0,0

	/* See if we want to perform a global inval this time. */
	rlwinm	r6,r3,0,10,10		/* r6 contains the new invalidate bit */
	rlwinm.	r5,r3,0,0,0		/* r5 contains the new enable bit */
	rlwinm	r3,r3,0,11,9		/* Turn off the invalidate bit */
	rlwinm	r3,r3,0,1,31		/* Turn off the enable bit */
	or	r3,r3,r4		/* Keep the enable bit the same as it was for now. */
	mfmsr	r7			/* shut off interrupts around critical flush/invalidate sections */
	rlwinm	r4,r7,0,17,15		/* Turn off EE bit - an external exception while we are flushing
								   the cache is fatal (comment this line and see!) */
	mtmsr	r4
	bne	dontDisableCache	/* Only disable the cache if L2CRApply has the enable bit off */

	cmplwi  r0,PPC_7400		/* 7400 ? */
	bne	disableCache		/* use traditional method */

	/* On the 7400, they recommend using the hardware flush feature */
	DSSALL						/* stop all data streams */
	sync
	/* we wouldn't have to flush L1, but for sake of consistency with the other code we do it anyway */
	mfspr	r4, MSSCR0
	oris	r4, r4, DL1HWF@h
	mtspr	MSSCR0, r4
	sync
	/* L1 flushed */
	mfspr	r4, L2CR
	ori	r4, r4, L2HWF
	mtspr	L2CR, r4
	sync
	/* L2 flushed */
	b	flushDone

disableCache:
	/* Disable the cache.  First, we turn off data relocation. */
	rlwinm	r4,r4,0,28,26		/* Turn off DR bit */
	cmplwi  r0,PPC_7455             /* 7455 ? */
	beq     1f
	cmplwi  r0,PPC_7457             /* 7457 ? */
	bne	not745x
1:
	/* 745x:L1 Load/Flush, L2, L3 :	 hardware flush */
	DSSALL
	mtmsr	r4
	sync
	isync
	mfspr	r4, MSSCR0
	rlwinm	r4,r4,0,29,0		/* Turn off the L2PFE bits */
	mtspr	MSSCR0, r4
	sync
	/* flush L1 first */
	lis	r4,0x0001
	mtctr	r4
	li	r4,0
	li      r0,0
loadFlush:
	lwzx	r0,r0,r4
	dcbf	r0,r4
	addi	r4,r4,CACHE_LINE_SIZE	/* Go to start of next cache line */
	bdnz	loadFlush
	sync
	/* Set the L2CR[L2IO & L2DO] bits to completely lock the L2 cache */
	mfspr   r0, L2CR
	lis     r4,L2CR_LOCK_745x@h
	ori     r4,r4,L2CR_LOCK_745x@l
	or      r4,r0,r4
	rlwinm  r4,r4,0,11,9           /* make sure the invalidate bit off */
	mtspr   L2CR, r4
	sync
	ori	r4, r4, L2HWF
	mtspr	L2CR, r4
	sync
	/* L2 flushed,L2IO & L2DO got cleared in the dontDisableCache:  */
	b	reenableDR

not745x:
	sync
	mtmsr	r4
	isync
	/*
		Now, read the first 2MB of memory to put new data in the cache.
		(Actually we only need the size of the L2 cache plus
		the size of the L1 cache, but 2MB will cover everything just to be safe).
	*/
	lis	r4,0x0001
	mtctr	r4
	li	r4,0
loadLoop:
	lwzx	r0,r0,r4
	addi	r4,r4,CACHE_LINE_SIZE	/* Go to start of next cache line */
	bdnz	loadLoop

	/* Now, flush the first 2MB of memory */
	lis		r4,0x0001
	mtctr	r4
	li		r4,0
	sync
flushLoop:
	dcbf	r0,r4
	addi	r4,r4,CACHE_LINE_SIZE	/* Go to start of next cache line */
	bdnz	flushLoop
reenableDR:
	rlwinm	r4,r7,0,17,15		/* still mask EE but reenable data relocation */
	sync
	mtmsr	r4
	isync

flushDone:

	/* Turn off the L2CR enable bit. */
	rlwinm	r3,r3,0,1,31

dontDisableCache:
	/* Set up the L2CR configuration bits */
	sync
	mtspr	L2CR,r3
	sync
	cmplwi	r6,0
	beq	noInval

	/* Perform a global invalidation */
	oris	r3,r3,0x0020
	sync
	mtspr	L2CR,r3
	sync
invalCompleteLoop:				/* Wait for the invalidation to complete */
	mfspr	r3,L2CR
	rlwinm.	r4,r3,0,31,31
	bne	invalCompleteLoop

	rlwinm	r3,r3,0,11,9;		/* Turn off the L2I bit */
	sync
	mtspr	L2CR,r3

noInval:
	sync
	/* re-enable interrupts, i.e. restore original MSR */
	mtmsr	r7					/* (no sync needed) */
	/* See if we need to enable the cache */
	cmplwi	r5,0
	beqlr

enableCache:
	/* Enable the cache */
	oris	r3,r3,0x8000
	mtspr	L2CR,r3
	sync
	blr


	.globl get_L3CR
	.type  get_L3CR, @function
get_L3CR:
	/* Make sure this is a 7455 chip */
	mfspr	r3,PPC_PVR
	rlwinm	r3,r3,16,16,31
	cmplwi  r3,PPC_7455	/* it's a 7455 */
	beq	1f
	cmplwi  r3,PPC_7457	/* it's a 7457 */
	beq	1f
	li	r3,-1
	blr

1:
	/* Return the L3CR contents */
	mfspr	r3,L3CR
	blr

	.globl set_L3CR
	.type  set_L3CR, @function
set_L3CR:
	/* Usage:
	 * When setting the L3CR register, you must do a few special things.
	 * If you are enabling the cache, you must perform a global invalidate.
	 * Then call cpu_enable_l3cr(l3cr).
	 * If you are disabling the cache, you must flush the cache contents first.
	 * This routine takes care of doing these things.  If you
	 * want to modify the L3CR contents after the cache has been enabled,
	 * the recommended procedure is to first call __setL3CR(0) to disable
	 * the cache and then call cpu_enable_l3cr with the new values for
	 * L3CR.
	 */

	/* Make sure this is a 7455 chip */
	mfspr	r0,PPC_PVR
	rlwinm	r0,r0,16,16,31
	cmplwi	r0,PPC_7455
	beq	thisIs7455
	cmplwi	r0,PPC_7457
	beq	thisIs7455
	li	r3,-1
	blr

thisIs7455:
	/* Get the current enable bit of the L3CR into r4 */
	mfspr	r4,L3CR
	rlwinm	r4,r4,0,0,0

	/* See if we want to perform a global inval this time. */
	rlwinm	r6,r3,0,10,10		/* r6 contains the new invalidate bit */
	rlwinm.	r5,r3,0,0,0		/* r5 contains the new enable bit */
	rlwinm	r3,r3,0,11,9		/* Turn off the invalidate bit */
	rlwinm	r3,r3,0,1,31		/* Turn off the enable bit */
	or	r3,r3,r4		/* Keep the enable bit the same as it was for now. */
	mfmsr	r7			/* shut off interrupts around critical flush/invalidate sections */
	rlwinm	r4,r7,0,17,15		/* Turn off EE bit - an external exception while we are flushing
								   the cache is fatal (comment this line and see!) */
	mtmsr	r4
	bne	dontDisableL3Cache	/* Only disable the cache if L3CRApply has the enable bit off */
	/* Before the L3 is disabled, it must be flused to prevent coherency problems */
	/* First, we turn off data relocation. */
	rlwinm	r4,r4,0,28,26		/* Turn off DR bit */
	DSSALL
	sync
	mtmsr	r4
	isync				/* make sure memory accesses have completed */
	/* 7455: L3 :	 hardware flush
	 * Set the L3CR[L3IO & L3DO] bits to completely lock the L3 cache */
	mfspr   r0, L3CR
	lis     r4, L3CR_LOCK_745x@h
	ori     r4,r4, L3CR_LOCK_745x@l
	or      r4,r0,r4
	rlwinm  r4,r4,0,11,9           /* make sure the invalidate bit off */
	mtspr   L3CR, r4
	sync
	ori	r4, r4, L3CR_L3HWF
	mtspr	L3CR, r4
	sync
	/* L3 flushed,L3IO & L3DO got cleared in the dontDisableL3Cache:  */
	rlwinm	r4,r7,0,17,15		/* still mask EE but reenable data relocation */
	sync
	mtmsr	r4
	isync

	/* Turn off the L3CR enable bit. */
	rlwinm	r3,r3,0,1,31

dontDisableL3Cache:
	/* Set up the L3CR configuration bits */
	sync
	mtspr	L3CR,r3
	sync
ifL3Inval:
	cmplwi	r6,0
	beq	noL3Inval

	/* Perform a global invalidation */
	oris	r3,r3,0x0020
	sync
	mtspr	L3CR,r3
	sync
invalCompleteL3:				/* Wait for the invalidation to complete */
	mfspr	r3,L3CR
	rlwinm.	r4,r3,0,31,31
	bne	invalCompleteL3

	rlwinm	r3,r3,0,11,9;		/* Turn off the L3I bit */
	sync
	mtspr	L3CR,r3
	sync

noL3Inval:
	/* re-enable interrupts, i.e. restore original MSR */
	mtmsr	r7					/* (no sync needed) */
	/* See if we need to enable the cache */
	cmplwi	r5,0
	beqlr

enableL3Cache:
	/* Enable the cache */
	oris	r3,r3,0x8000
	mtspr	L3CR,r3
	sync
	blr

/*
 * An undocumented "feature" of 604e requires that the v bit
 * be cleared before changing BAT values.
 *
 * Also, newer IBM firmware does not clear bat3 and 4 so
 * this makes sure it's done.
 *  -- Cort
 */
	.globl	CPU_clear_bats_early
	.type	CPU_clear_bats_early,@function
CPU_clear_bats_early:
	li		r3,0
	mfspr	r4,PPC_PVR
	rlwinm	r4,r4,16,16,31		/* r4 = 1 for 601, 4 for 604 */
	cmpwi	r4, 1
	sync
	isync
	beq	1f
	cmplwi	r4,0x8001			/* 7445, 7455 (0x8001), 7447, 7457 (0x8002)      */
	blt 2f						/* 7447a (0x8003) and 7448 (0x8004) have 16 bats */
	cmplwi	r4,0x8004
	bgt 2f
	mtspr	DBAT4U,r3
	mtspr	DBAT4L,r3
	mtspr	DBAT5U,r3
	mtspr	DBAT5L,r3
	mtspr	DBAT6U,r3
	mtspr	DBAT6L,r3
	mtspr	DBAT7U,r3
	mtspr	DBAT7L,r3
	mtspr	IBAT4U,r3
	mtspr	IBAT4L,r3
	mtspr	IBAT5U,r3
	mtspr	IBAT5L,r3
	mtspr	IBAT6U,r3
	mtspr	IBAT6L,r3
	mtspr	IBAT7U,r3
	mtspr	IBAT7L,r3
2:
	mtspr	DBAT0U,r3
	mtspr	DBAT0L,r3
	mtspr	DBAT1U,r3
	mtspr	DBAT1L,r3
	mtspr	DBAT2U,r3
	mtspr	DBAT2L,r3
	mtspr	DBAT3U,r3
	mtspr	DBAT3L,r3
1:
	mtspr	IBAT0U,r3
	mtspr	IBAT0L,r3
	mtspr	IBAT1U,r3
	mtspr	IBAT1L,r3
	mtspr	IBAT2U,r3
	mtspr	IBAT2L,r3
	mtspr	IBAT3U,r3
	mtspr	IBAT3L,r3
	sync
	isync
	blr

