/*
 * (C) Copyright 2001
 * Bill Hunter, Wave 7 Optics, william.hunter@mediaone.net
 *  and
 * Erik Theisen, Wave 7 Optics, etheisen@mindspring.com
 *
 * See file CREDITS for list of people who contributed to this
 * project.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 * MA 02111-1307 USA
 */
//
//Description:
//    Routine to exercise memory for the bringing up of our boards.
//
#include <config.h>
#include <ppc4xx.h>

#define _LINUX_CONFIG_H 1       /* avoid reading Linux autoconf.h file  */

#include <ppc_asm.tmpl>
#include <ppc_defs.h>

#include <asm/cache.h>
#include <asm/mmu.h>

#include "error.h"

#define _ASMLANGUAGE

	.globl	test_sdram
	.globl	test_led
	.globl	log_stat
	.globl	log_warn
	.globl	log_err

//****************************************
//****************************************
//********    LED register test   ********
//****************************************
//****************************************
test_led:
	/* save the return info on stack */
	mflr	r0			// Get link register
	stwu	r1, -8(r1)		// Save back chain and move SP
	stw	r0, +12(r1)		// Save link register
	
#if defined(CONFIG_W7OLMG)
	addi    r3, 0, ERR_FF		// first test value is ffff
	addi	r4, r3, 0		// save copy of pattern
	bl	set_led			// store first test value
	bl	get_led			// read it back
	xor.	r4, r4, r3		// compare to original
	beq     LED2			// next test
	addi    r3, 0, ERR_LED		// error code = 1
	bl	log_err			// display error and halt
LED2:	addi    r3, 0, ERR_00		// 2nd test value is 0000
	addi	r4, r3, 0		// save copy of pattern
	bl	set_led			// store first test value
	bl	get_led			// read it back
	xor.	r4, r4, r3		// compare to original
	beq     LED3			// next test
	addi    r3, 0, ERR_LED		// error code = 1
	bl	log_err			// display error and halt
#endif
LED3:	/* restore stack and return */
	lwz	r0, +12(r1)		// Get saved link register
	mtlr	r0			// Restore link register
	addi	r1, r1, +8		// Remove frame from stack
	blr				// Return to calling function


//****************************************
//****************************************
//********     SDRAM TESTS        ********
//****************************************
//****************************************
test_sdram:	
	/* save the return info on stack */
	mflr	r0			// Get link register
	stwu	r1, -8(r1)		// Save back chain and move SP
	stw	r0, +12(r1)		// Save link register
			
//****************************************
//********  sdram data bus test   ********
//****************************************
	// set up led register for this test
Data_Buster:	
	addi    r3, 0, ERR_RAMG		// set led code to 1
	bl	log_stat		// store test value
	//now test the dram data bus
	xor	r7, r7, r7		// load r7 with 0
	xor	r6, r6, r6		// load r6 with base address of sdram
	addis	r5, 0, 0x5555		// load r4 with test value
	ori	r5, r5, 0x5555
	stw	r5,0(r6)		// sto the value
	lwz	r3,0(r6)		// read it back
	xor	r3,r3,r5		// compare it to original
	addis	r5, 0, 0xaaaa		// load r4 with test value
	ori	r5, r5, 0xaaaa
	stw	r5,0(r6)		// sto the value
	lwz	r4,0(r6)		// read it back
	xor	r4,r4,r5		// compare it to original
	or	r3,r3,r4		// or together both error terms
	// now that we have the error bits, we have to decide which part they are in
	bl	get_idx			// r5 is now index to error
	addi	r3, r3, ERR_RAMG
	cmpwi	r3, ERR_RAMG		// check for errors
	beq	db2			// skip if no errors
	bl	log_err			// log the error
db2:	b	Ghost_Buster		// on to next test
	

//****************************************************
//********  test for address ghosting in dram ********
//****************************************************
	
Ghost_Buster:
	addi	r3, 0, ERR_ADDG		// set the leds for address ghost test
	bl	set_led
	addi	r7, 0, 0x00ff		// r7 = one byte of set bits
	addi	r8, 0, 0		// r8 = 0 for testing
	addi	r9, 0, 1		// r9 = 1 this is shifted to get ghost addresses
	addi	r6, 0, 0		// r6 is main referance loop counter, 0 to 23
					// r5 is ghost loop counter, 0 to 22
					// r4 is referance address
					// r3 is ghost address
					
	// first fill memory with zeros
	addis	r4, 0, 0x0100		// cover 8M of longs
	mtctr	r4			// setup base address
	addi	r4, 0, 0		// r4 is address, start at 0
0:	stw	r8, 0(r4)		// Store zero value
	addi	r4, r4, 4		// Increment to next word
	bdnz	0b			// Round and round...

	// now do main test
outside:
	// Calculate the referance address
	//   the referance address is calculated by setting the (r6-1)bit of the base address
	//   when r6=0, the referance address is the base address.
	//   setting the bit is done with the following shift functions.
	slw	r4,r9,r6		// tmp = 1 << i
	srwi	r4,r4,1			// tmp = tmp >> 1
	
	// fill referance address with Fs
	stb     r7,0(r4)

        // ghost (inner) loop, now check all posible ghosted addresses
	addi	r5, 0, 0		// r5 is the ghosted loop counter,0 to 22
inside:
	// Calculate the ghost address
	//   the ghost address is calculated by toggling the r6th bit of the referance address
	slw     r3, r9, r5		// r3 = bit pattern, only r5th bit is set
	xor	r3, r4, r3		// r3 = ghost address = ref addr with r5th bit flipped
   
	// now check for ghosting
	lbz     r3,0(r3)		// get contents
	cmpwi   r3, 0			// compare read value to 0
	bne	Casper			// we found a ghost!

	// now close ghost ( inner ) loop
	addi    r5, r5, 1		// increment inner loop counter
	cmpwi   r5, 23			// check for last inner loop
	ble     inside			// do more inner loops

	// now close referance ( outer ) loop
	stb     r8, 0(r4)		// zero out the altered address loc.
	addi    r6, r6, 1		// increment outer loop counter
	cmpwi   r6, 24			// check for last inner loop
	ble     outside			// do more outer loops

	// were done, lets go home
	b	Bit_Buster	
Casper:	addi	r3, 0, ERR_ADDG		// get indexed error message
	b	log_err			// output led error code, halt everything
	
//****************************************************
//********      SDRAM data fill tests       **********
//****************************************************
Bit_Buster:	
	addis	r5, 0, 0x0400		// FIXME - hard coded @ 64MB 0x04000000

	// Test 55555555
	addi	r3, 0, ERR_R55G		// set up error code in case we fail
	bl	log_stat		// store test value
	addis	r4, 0, 0x5555
	ori	r4, r4, 0x5555
	bl	fill_test

	// Test aaaaaaaa 
	addi	r3, 0, ERR_RAAG		// set up error code in case we fail
	bl	log_stat		// store test value
	addis	r4, 0, 0xAAAA	
	ori	r4, r4, 0xAAAA
	bl	fill_test

	// Test 00000000 
	addi	r3, 0, ERR_R00G		// set up error code in case we fail
	bl	log_stat		// store test value
	addis	r4, 0, 0
	ori	r4, r4, 0
	bl	fill_test
	

//****************************************************
//********       finished these tests       **********
//****************************************************
	/* restore stack and return */
	lwz	r0, +12(r1)		// Get saved link register
	mtlr	r0			// Restore link register
	addi	r1, r1, +8		// Remove frame from stack
	blr				// Return to calling function



//****************************************************
//********             fill test              ********
//****************************************************
//	tests memory by filling with value, and reading back
//	r5 = Size of memory in bytes
//	r4 = Value to write
//	r3 = Error code
fill_test:
	mflr	r30
	bl	kick_dog		// Reset the watchdog
	// first fill memory with Value
	srawi	r7, r5, 2		// convert bytes to longs
	mtctr	r7			// setup counter
	addi	r6, 0, 0		// Make r6 = addr 0
0:	stw	r4, 0(r6)		// Store value
	addi	r6, r6, 4		// Increment to next word
	bdnz	0b			// Round and round...
	
	bl	kick_dog		// Reset the watchdog
	
	// now confirm Value is in memory
	mtctr	r7			// setup counter
	addi	r6, 0, 0		// Make r6 = addr 0
0:	lwz	r7, 0(r6)		// get value from memory
        xor.	r7, r7, r4		// Writen = Read ?
        bne	ft_err			// If bad, than halt
2:	addi	r6, r6, 4		// Increment to next word
	bdnz	0b			// Round and round...

	bl	kick_dog		// Reset the watchdog
	mtlr	r30
	blr				// return

ft_err:	addi	r7, r3, 0		// save current led code
	addi	r3, r7, 0		// get pattern in r3
	bl	get_idx			// get index from r3
	add	r3, r3, r4		// add index to old led code
	bl	log_err			// output led error code, halt everything
	
		
//****************************************************
//*******  get error index from r3 pattern    ********
//****************************************************
get_idx:				// r4 = (MSW(r3) !=0)*2 + (LSW(r3) !=0)
	andi.	r4, r3, 0xffff		// check for lower bits
	beq	gi2			// skip if no bits set
	andis.	r4, r3, 0xffff		// check for upper bits
	beq	gi3			// skip if no bits set
	addi	r4, 0, 3		// both upper and lower bits set
	blr
gi2:	andis.	r4, r3, 0xffff		// check for upper bits
	beq	gi4			// skip if no bits set
	addi	r4, 0, 2		// only upper bits set
	blr
gi3:	addi	r4, 0, 1		// only lower bits set
	blr
gi4:	addi	r4, 0, 0		// no bits set
	blr

//****************************************************
//********       set LED to R5 and hang       ********
//****************************************************
log_stat:	// output a led code and continue
set_led:	
	/* save the return info on stack */
	mflr	r0			// Get link register
	stwu	r1, -12(r1)		// Save back chain and move SP
	stw	r0, +16(r1)		// Save link register
	stw	r4, +8(r1)		// save R4

	addis	r4, 0, 0xfe00		// LED buffer is at 0xfe000000
	xori	r3,r3, 0xffff		// complement led code since active low
	sth	r3, 0(r4)		// store first test value
	xori	r3,r3, 0xffff		// complement led code since active low

	/* restore stack and return */
	lwz	r0, +16(r1)		// Get saved link register
	mtlr	r0			// Restore link register
	lwz	r4, +8(r1)		// restore r4
	addi	r1, r1, +12		// Remove frame from stack
	blr				// Return to calling function

get_led:	
	/* save the return info on stack */
	mflr	r0			// Get link register
	stwu	r1, -12(r1)		// Save back chain and move SP
	stw	r0, +16(r1)		// Save link register
	stw	r4, +8(r1)		// save R4

	addis	r4, 0, 0xfe00		// LED buffer is at 0xfe000000
	lhz	r3, 0(r4)		// store first test value
	xori	r3,r3, 0xffff		// complement led code since active low

	/* restore stack and return */
	lwz	r0, +16(r1)		// Get saved link register
	mtlr	r0			// Restore link register
	lwz	r4, +8(r1)		// restore r4
	addi	r1, r1, +12		// Remove frame from stack
	blr				// Return to calling function

log_err:	// output the error and hang the board ( for now )
	/* save the return info on stack */
	mflr	r0			// Get link register
	stwu	r1, -8(r1)		// Save back chain and move SP
	stw	r0, +12(r1)		// Save link register
	bl	set_led	
halt:	
	b	halt			// hang

	/* restore stack and return */
	lwz	r0, +12(r1)		// Get saved link register
	mtlr	r0			// Restore link register
	addi	r1, r1, +8		// Remove frame from stack
	blr				// Return to calling function

log_warn:	// output a warning, then continue with operations
	/* save the return info on stack */
	mflr	r0			// Get link register
	stwu	r1, -8(r1)		// Save back chain and move SP
	stw	r0, +12(r1)		// Save link register

	bl	set_led			// set the led pattern
	addis	r3, 0, 0xf		// r3 = 0xf4240 = 1e6
	ori	r3, r3, 0x4240
	xor	r4,r4,r4		// r4 = 0
	bl	udelay			// delay 1e6 usec = 1 sec
	
	/* restore stack and return */
	lwz	r0, +12(r1)		// Get saved link register
	mtlr	r0			// Restore link register
	addi	r1, r1, +8		// Remove frame from stack
	blr				// Return to calling function

//****************************************************
//********       void kick_dog(void)          ********
//********       Toggle the watchdog line     ********
//****************************************************
		.section ".text"
		.globl	kick_dog
		.align	2
		.type	kick_dog, @function
kick_dog:
	/* save the return info on stack */
	mflr	r0			// Get link register
	stwu	r1, -16(r1)		// Save back chain and move SP
	stw	r0, +20(r1)		// Save link register

	/* Save r3/r4 since we might be called from asm */
	stw	r4, +8(r1)
	stw	r3, +12(r1)
	
	addis	r4, 0, 0xfe00		// LED register is at 0xfe000000

	lhz	r3, 0(r4)		// Read register
	xori	r3, r3, 0x8000		// Toggle watchdog bit
	sth	r3, 0(r4)

	/* Restore r3/r4 */
	lwz	r3, +12(r1)
	lwz	r4, +8(r1)
	
	/* restore stack and return */
	lwz	r0, +20(r1)		// Get saved link register
	mtlr	r0			// Restore link register
	addi	r1, r1, +16		// Remove frame from stack
	blr				// Return to calling function
// end kick_dog()

	
