/*
 * (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 <asm/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 <watchdog.h>

#include "errors.h"

#define _ASMLANGUAGE

	.globl	test_sdram
	.globl	test_led
	.globl	log_stat
	.globl	log_warn
	.globl	log_err
	.globl  temp_uart_init
	.globl  post_puts
	.globl  disp_hex

/*****************************************************
*******   Text Strings for low level printing   ******
*******          In section got2               *******
*****************************************************/

/*
 * Define the text strings for errors and warnings.
 * Switch to .data section.
 */
	.section ".data"
err_str:	.asciz "*** POST ERROR   = "
warn_str:	.asciz "*** POST WARNING = "
end_str:  .asciz "\r\n"

/*
 * Enter the labels in Global Entry Table (GOT).
 * Switch to .got2 section.
 */
	START_GOT
	GOT_ENTRY(err_str)
	GOT_ENTRY(warn_str)
	GOT_ENTRY(end_str)
	END_GOT

/*
 * Switch  back to .text section.
 */
	.text

/****************************************
 ****************************************
 ********    LED register test   ********
 ****************************************
 ***************************************/
test_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 */

	WATCHDOG_RESET			/* Reset the watchdog */

	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 */
#if defined(CONFIG_W7OLMC)
	andi.   r4, r4, 0x00ff		/* lmc has 8 bits */
#else
	andi.   r4, r4, 0xffff		/* lmg has 16 bits */
#endif
	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 */
#if defined(CONFIG_W7OLMC)
	andi.   r4, r4, 0x00ff		/* lmc has 8 bits */
#else
	andi.   r4, r4, 0xffff		/* lmg has 16 bits */
#endif
	beq     LED3			/* next test */
	addi    r3, 0, ERR_LED		/* error code = 1 */
	bl	log_err			/* display error and halt */

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

/****************************************
 ****************************************
 ********     SDRAM TESTS        ********
 ****************************************
 ***************************************/
test_sdram:
	/* called with mem size in r3 */
	/* 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 */
	stmw	r30, +8(r1)		/* save R30,R31 */
					/* r30 is log2(mem size) */
					/* r31 is mem size */

	/* take log2 of total mem size */
	addi	r31, r3, 0		/* save total mem size */
	addi	r30, 0, 0		/* clear r30 */
l2_loop:
	srwi.	r31, r31, 1		/* shift right 1 */
	addi	r30, r30, 1		/* count shifts */
	bne	l2_loop			/* loop till done */
	addi	r30, r30, -1		/* correct for over count */
	addi	r31, r3, 0		/* save original size */

	/* now kick the dog and test the mem */
	WATCHDOG_RESET			/* Reset the watchdog */
	bl	Data_Buster		/* test crossed/shorted data lines */
	addi	r3, r30, 0		/* get log2(memsize) */
	addi	r4, r31, 0		/* get memsize */
	bl	Ghost_Buster		/* test crossed/shorted addr lines */
	addi	r3, r31, 0		/* get mem size */
	bl	Bit_Buster		/* check for bad internal bits */

	/* restore stack and return */
	lmw	r30, +8(r1)		/* Restore r30, r31 */
	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 */


/****************************************
 ********  sdram data bus test   ********
 ***************************************/
Data_Buster:
	/* save the return info on stack */
	mflr	r0			/* Get link register */
	stwu	r1, -24(r1)		/* Save back chain and move SP */
	stw	r0, +28(r1)		/* Save link register */
	stmw	r28, 8(r1)		/* save r28 - r31 on stack */
					/* r31 i/o register */
					/* r30 sdram base address */
					/* r29 5555 syndrom */
					/* r28 aaaa syndrom */

	/* set up led register for this test */
	addi    r3, 0, ERR_RAMG		/* set led code to 1 */
	bl	log_stat		/* store test value */
	/* now test the dram data bus */
	xor	r30, r30, r30		/* load r30 with base addr of sdram */
	addis	r31, 0, 0x5555		/* load r31 with test value */
	ori	r31, r31, 0x5555
	stw	r31,0(r30)		/* sto the value */
	lwz	r29,0(r30)		/* read it back */
	xor	r29,r31,r29		/* compare it to original */
	addis	r31, 0, 0xaaaa		/* load r31 with test value */
	ori	r31, r31, 0xaaaa
	stw	r31,0(r30)		/* sto the value */
	lwz	r28,0(r30)		/* read it back */
	xor	r28,r31,r28		/* compare it to original */
	or	r3,r28,r29		/* 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	db_done			/* skip if no errors */
	bl	log_err			/* log the error */

db_done:
	lmw	r28, 8(r1)		/* restore r28 - r31 from stack */
	lwz	r0, +28(r1)		/* Get saved link register */
	addi	r1, r1, +24		/* Remove frame from stack */
	mtlr	r0			/* Restore link register */
	blr				/* Return to calling function */


/****************************************************
 ********  test for address ghosting in dram ********
 ***************************************************/

Ghost_Buster:
	/* save the return info on stack */
	mflr	r0			/* Get link register */
	stwu	r1, -36(r1)		/* Save back chain and move SP */
	stw	r0, +40(r1)		/* Save link register */
	stmw	r25, 8(r1)		/* save r25 - r31 on stack */
					/* r31 = scratch register */
					/* r30 is main referance loop counter,
					   0 to 23 */
					/* r29 is ghost loop count, 0 to 22 */
					/* r28 is referance address */
					/* r27 is ghost address */
					/* r26 is log2 (mem size) =
					     number of byte addr bits */
					/* r25 is mem size */

	/* save the log2(mem size) and mem size */
	addi	r26, r3, 0		/* r26 is number of byte addr bits */
	addi	r25, r4, 0		/* r25 is mem size in bytes */

	/* set the leds for address ghost test */
	addi	r3, 0, ERR_ADDG
	bl	set_led

	/* first fill memory with zeros */
	srwi	r31, r25, 2		/* convert bytes to longs */
	mtctr	r31			/* setup byte counter */
	addi	r28, 0, 0		/* start at address at 0 */
	addi	r31, 0, 0		/* data value = 0 */
clr_loop:
	stw	r31, 0(r28)		/* Store zero value */
	addi	r28, r28, 4		/* Increment to next word */
	andi.	r27, r28, 0xffff	/* check for 2^16 loops */
	bne	clr_skip		/* if not there, then skip */
	WATCHDOG_RESET			/* kick the dog every now and then */
clr_skip:
	bdnz	clr_loop		/* Round and round... */

	/* now do main test */
	addi	r30, 0, 0		/* start referance counter at 0 */
outside:
	/*
	 * Calculate the referance address
	 *   the referance address is calculated by setting the (r30-1)
	 *   bit of the base address
	 * when r30=0, the referance address is the base address.
	 * thus the sequence 0,1,2,4,8,..,2^(n-1)
	 * setting the bit is done with the following shift functions.
	 */
	WATCHDOG_RESET			/* Reset the watchdog */

	addi	r31, 0, 1		/* r31 = 1 */
	slw	r28, r31, r30		/* set bit coresponding to loop cnt */
	srwi	r28, r28, 1		/* then shift it right one so  */
					/*   we start at location 0 */
	/* fill referance address with Fs */
	addi	r31, 0, 0x00ff		/* r31 = one byte of set bits */
	stb     r31,0(r28)		/* save ff in referance address */

	/* ghost (inner) loop, now check all posible ghosted addresses */
	addi	r29, 0, 0		/* start ghosted loop counter at 0 */
inside:
	/*
	 * Calculate the ghost address by flipping one
	 *  bit of referance address.  This gives the
	 *  sequence 1,2,4,8,...,2^(n-1)
	 */
	addi	r31, 0, 1		/* r31 = 1 */
	slw     r27, r31, r29		/* set  bit coresponding to loop cnt */
	xor	r27, r28, r27		/* ghost address = ref addr with
					     bit flipped*/

	/* now check for ghosting */
	lbz     r31,0(r27)		/* get content of ghost addr */
	cmpwi   r31, 0			/* compare read value to 0 */
	bne	Casper			/*   we found a ghost! */

	/* now close ghost ( inner ) loop */
	addi	r29, r29, 1		/* increment inner loop counter */
	cmpw	r29, r26		/* check for last inner loop */
	blt		inside		/* do more inner loops */

	/* now close referance ( outer ) loop */
	addi	r31, 0, 0		/* r31 = zero */
	stb	r31, 0(28)		/* zero out the altered address loc. */
	/*
	 * Increment and check for end, count is zero based.
	 * With the ble, this gives us one more loops than
	 * address bits for sequence 0,1,2,4,8,...2^(n-1)
	*/
	addi	r30, r30, 1		/* increment outer loop counter */
	cmpw	r30, r26		/* check for last inner loop */
	ble	outside			/* do more outer loops */

	/* were done, lets go home */
	b	gb_done
Casper:					/* we found a ghost !! */
	addi	r3, 0, ERR_ADDF		/* get indexed error message */
	bl	log_err			/* log error led error code */
gb_done: /*  pack your bags, and go home */
	lmw     r25, 8(r1)              /* restore r25 - r31 from stack */
	lwz     r0, +40(r1)             /* Get saved link register */
	addi    r1, r1, +36             /* Remove frame from stack */
	mtlr    r0                      /* Restore link register */
	blr                             /* Return to calling function */

/****************************************************
 ********      SDRAM data fill tests       **********
 ***************************************************/
Bit_Buster:
	/* called with mem size in r3 */
	/* 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 */
	stw	r4, +8(r1)		/* save R4 */
	stw	r5, +12(r1)		/* save r5 */

	addis	r5, r3, 0		/* save mem size */

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

	/* restore stack and return */
	lwz	r5, +12(r1)		/* restore r4 */
	lwz	r4, +8(r1)		/* restore r4 */
	lwz	r0, +20(r1)		/* Get saved link register */
	addi	r1, r1, +16		/* Remove frame from stack */
	mtlr	r0			/* Restore link register */
	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    r0                      /* Get link register */
	stwu    r1, -32(r1)             /* Save back chain and move SP */
	stw     r0, +36(r1)             /* Save link register */
	stmw    r27, 8(r1)              /* save r27 - r31 on stack */
					/* r31 - scratch register */
					/* r30 - memory address */
	mr	r27, r3
	mr	r28, r4
	mr	r29, r5

	WATCHDOG_RESET			/* Reset the watchdog */

	/* first fill memory with Value */
	srawi	r31, r29, 2		/* convert bytes to longs */
	mtctr	r31			/* setup counter */
	addi	r30, 0, 0		/* Make r30 = addr 0 */
ft_0:	stw	r28, 0(r30)		/* Store value */
	addi	r30, r30, 4		/* Increment to next word */
	andi.	r31, r30, 0xffff	/* check for 2^16 loops */
	bne	ft_0a			/* if not there, then skip */
	WATCHDOG_RESET			/* kick the dog every now and then */
ft_0a:	bdnz	ft_0			/* Round and round... */

	WATCHDOG_RESET			/* Reset the watchdog */

	/* Now confirm Value is in memory */
	srawi	r31, r29, 2		/* convert bytes to longs */
	mtctr	r31			/* setup counter */
	addi	r30, 0, 0		/* Make r30 = addr 0 */
ft_1:	lwz	r31, 0(r30)		/* get value from memory */
	xor.	r31, r31, r28		/* Writen = Read ? */
	bne	ft_err			/* If bad, than halt */
	addi	r30, r30, 4		/* Increment to next word */
	andi.	r31, r30, 0xffff	/* check for 2^16 loops*/
	bne	ft_1a			/* if not there, then skip */
	WATCHDOG_RESET			/* kick the dog every now and then */
ft_1a:	bdnz	ft_1			/* Round and round... */

	WATCHDOG_RESET			/* Reset the watchdog */

	b	fill_done		/* restore and return */

ft_err:	addi	r29, r27, 0		/* save current led code */
	addi	r27, r31, 0		/* get pattern in r27 */
	bl	get_idx			/* get index from r27 */
	add	r27, r27, r29		/* add index to old led code */
	bl	log_err			/* output led err code, halt CPU */

fill_done:
	lmw     r27, 8(r1)              /* restore r27 - r31 from stack */
	lwz     r0, +36(r1)             /* Get saved link register */
	addi    r1, r1, +32             /* Remove frame from stack */
	mtlr    r0                      /* Restore link register */
	blr                             /* Return to calling function */


/****************************************************
 *******  get error index from r3 pattern    ********
 ***************************************************/
get_idx:				/* r3 = (MSW(r3) !=0)*2 +
					    (LSW(r3) !=0) */
	/* 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 */

	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	r3, 0, 3		/* both upper and lower bits set */
	b	gi_done
gi2:	andis.	r4, r3, 0xffff		/* check for upper bits*/
	beq	gi4			/* skip if no bits set */
	addi	r3, 0, 2		/* only upper bits set */
	b	gi_done
gi3:	addi	r3, 0, 1		/* only lower bits set */
	b	gi_done
gi4:	addi	r3, 0, 0		/* no bits set */
gi_done:
	/* 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 */

/****************************************************
 ********       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 */
#if defined(CONFIG_W7OLMG)		/* only on gateway, invert outputs */
	xori	r3,r3, 0xffff		/* complement led code, active low */
	sth	r3, 0(r4)		/* store first test value */
	xori	r3,r3, 0xffff		/* complement led code, active low */
#else					/* if not gateway, then don't invert */
	sth	r3, 0(r4)		/* store first test value */
#endif

	/* 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 */
#if defined(CONFIG_W7OLMG)		/* only on gateway, invert inputs */
	xori	r3,r3, 0xffff		/* complement led code, active low */
#endif

	/* 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, -12(r1)		/* Save back chain and move SP */
	stw	r0, +16(r1)		/* Save link register */
	stw	r3, +8(r1)		/* save a copy of error code */
	bl	set_led			/* set the led pattern */
	GET_GOT				/* get GOT address in r14 */
	lwz	r3,GOT(err_str)		/* get address of string */
	bl	post_puts		/* output the warning string */
	lwz	r3, +8(r1)		/* get error code */
	addi	r4, 0, 2		/* set disp length to 2 nibbles */
	bl	disp_hex		/* output the error code */
	lwz	r3,GOT(end_str)		/* get address of string */
	bl	post_puts		/* output the warning string */
halt:
	b	halt			/* hang */

	/* restore stack and return */
	lwz	r0, +16(r1)		/* Get saved link register */
	mtlr	r0			/* Restore link register */
	addi	r1, r1, +12		/* 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, -16(r1)		/* Save back chain and move SP */
	stw	r0, +20(r1)		/* Save link register */
	stw	r3, +8(r1)		/* save a copy of error code */
	stw	r14, +12(r1)		/* save a copy of r14 (used by GOT) */

	bl	set_led			/* set the led pattern */
	GET_GOT				/* get GOT address in r14 */
	lwz	r3,GOT(warn_str)	/* get address of string */
	bl	post_puts		/* output the warning string */
	lwz	r3, +8(r1)		/* get error code */
	addi	r4, 0, 2		/* set disp length to 2 nibbles */
	bl	disp_hex		/* output the error code */
	lwz	r3,GOT(end_str)		/* get address of string */
	bl	post_puts		/* output the warning string */

	addis	r3, 0, 64		/* has a long delay */
	mtctr	r3
log_2:
	WATCHDOG_RESET			/* this keeps dog from barking, */
					/*   and takes time */
	bdnz	log_2			/* loop till time expires */

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

/*******************************************************************
 *	temp_uart_init
 *	Temporary UART initialization routine
 *	Sets up UART0 to run at 9600N81 off of the internal clock.
 *	R3-R4 are used.
 ******************************************************************/
temp_uart_init:
	/* 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 */

	addis   r3, 0, 0xef60
	ori     r3, r3, 0x0303          /* r3 = UART0_LCR */
	addi    r4, 0, 0x83             /* n81 format, divisor regs enabled */
	stb     r4, 0(r3)

	/* set baud rate to use internal clock,
	   baud = (200e6/16)/31/42 = 9600 */

	addis   r3, 0, 0xef60		/* Address of baud divisor reg */
	ori     r3, r3, 0x0300		/*   UART0_DLM */
	addi    r4, 0, +42		/* uart baud divisor LSB = 93 */
	stb     r4, 0(r3)               /* baud = (200 /16)/14/93 */

	addi    r3, r3, 0x0001		/* uart baud divisor addr */
	addi    r4, 0, 0
	stb     r4, 0(r3)               /* Divisor Latch MSB = 0 */

	addis   r3, 0, 0xef60
	ori     r3, r3, 0x0303          /* r3 = UART0_LCR */
	addi    r4, 0, 0x03             /* n81 format, tx/rx regs enabled */
	stb     r4, 0(r3)

	/* output a few line feeds */
	addi	r3, 0, '\n'		/* load line feed */
	bl	post_putc		/* output the char */
	addi	r3, 0, '\n'		/* load line feed */
	bl	post_putc		/* output the char */

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

/**********************************************************************
 **	post_putc
 **	outputs charactor in R3
 **	r3 returns the error code ( -1 if there is an error )
 *********************************************************************/

post_putc:

	/* save the return info on stack */
	mflr	r0			/* Get link register */
	stwu	r1, -20(r1)		/* Save back chain and move SP */
	stw	r0, +24(r1)		/* Save link register */
	stmw	r29, 8(r1)		/* save	r29 - r31 on stack
					   r31 - uart base address
					   r30 - delay counter
					   r29 - scratch reg */

     addis   r31, 0, 0xef60		/* Point to uart base */
     ori     r31, r31, 0x0300
     addis   r30, 0, 152		/* Load about 10,000,000 ticks. */
pputc_lp:
	lbz     r29, 5(r31)		/* Read Line Status Register */
	andi.   r29, r29, 0x20		/* Check THRE status */
	bne     thre_set		/* Branch if FIFO empty */
	addic.  r30, r30, -1		/* Decrement and check if empty. */
	bne     pputc_lp		/* Try, try again */
	addi    r3, 0, -1		/* Load error code for timeout */
	b       pputc_done		/* Bail out with error code set */
thre_set:
	stb     r3, 0(r31)		/* Store character to UART */
	addi	r3, 0, 0		/* clear error code */
pputc_done:
	lmw	r29, 8(r1)		/*restore r29 - r31 from stack */
	lwz	r0, +24(r1)		/* Get saved link register */
	addi	r1, r1, +20		/* Remove frame from stack */
	mtlr	r0			/* Restore link register */
	blr				/* Return to calling function */


/****************************************************************
    post_puts
    Accepts a null-terminated string pointed to by R3
    Outputs to the serial port until 0x00 is found.
    r3 returns the error code ( -1 if there is an error )
*****************************************************************/
post_puts:

	/* 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	r31, 8(r1)		/* save r31 - char pointer */

	addi	r31, r3, 0              /* move pointer to R31 */
pputs_nxt:
	lbz	r3, 0(r31)		/* Get next character */
	addic.  r3, r3, 0		/* Check for zero */
	beq	pputs_term		/* bail out if zero */
	bl	post_putc		/* output the char */
	addic.	r3, r3, 0		/* check for error */
	bne	pputs_err
	addi	r31, r31, 1		/* point to next char */
	b	pputs_nxt		/* loop till term */
pputs_err:
	addi	r3, 0, -1		/* set error code */
	b	pputs_end		/* were outa here */
pputs_term:
	addi	r3, 0, 1		/* set success code */
	/* restore stack and return */
pputs_end:
	lwz	r31, 8(r1)		/* restore r27 - r31 from stack */
	lwz	r0, +16(r1)		/* Get saved link register */
	addi	r1, r1, +12		/* Remove frame from stack */
	mtlr	r0			/* Restore link register */
	blr				/* Return to calling function */


/********************************************************************
 *****	disp_hex
 *****	Routine to display a hex value from a register.
 *****	R3 is value to display
 *****	R4 is number of nibbles to display ie 2 for byte 8 for (long)word
 *****	Returns -1 in R3 if there is an error ( ie serial port hangs )
 *****	Returns 0 in R3 if no error
 *******************************************************************/
disp_hex:
	/* 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 */
	stmw	r30, 8(r1)		/* save r30 - r31 on stack */
					/* r31 output char */
					/* r30 uart base address */
	addi	r30, 0, 8               /* Go through 8 nibbles. */
	addi	r31, r3, 0
pputh_nxt:
	rlwinm	r31, r31, 4, 0, 31	/* Rotate next nibble into position */
	andi.	r3, r31, 0x0f		/* Get nibble. */
	addi	r3, r3, 0x30		/* Add zero's ASCII code. */
	cmpwi	r3, 0x03a
	blt	pputh_out
	addi	r3, r3, 0x07            /* 0x27 for lower case. */
pputh_out:
	cmpw	r30, r4
	bgt	pputh_skip
	bl	post_putc
	addic.	r3, r3, 0		/* check for error */
	bne	pputh_err
pputh_skip:
	addic.	r30, r30, -1
	bne	pputh_nxt
	xor	r3, r3, r3		/* Clear error code */
	b	pputh_done
pputh_err:
	addi	r3, 0, -1		/* set error code */
pputh_done:
	/* restore stack and return */
	lmw	r30, 8(r1)		/*  restore r30 - r31 from stack */
	lwz	r0, +20(r1)		/* Get saved link register */
	addi	r1, r1, +16		/* Remove frame from stack */
	mtlr	r0			/* Restore link register */
	blr				/* Return to calling function */