/*
 * diag_other.c
 *
 * Matthew Millman
 */


#include <common.h>
#include <diag.h>
#include <mmc_spi.h>
#include <w1.h>

static u32 memtest_seed = 0;
static int last_percent;

void diag_print_percentage(char *prefix_message)
{
	last_percent = 0;
	diag_printf("  %-20s [                                                  ]\033[51D", prefix_message);
}


void diag_update_percentage(int percent)
{
	if (percent > 100)
		return;

	//printf("percent=%u\n", percent);
	if (percent - last_percent > 1)
	{
		diag_printf("=");
		last_percent = percent;
	}
	if (percent >= 100)
		diag_printf("\n");
}

void diag_memtest_srand(u32 seed)
{
	memtest_seed = seed;
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 *	memtest_rand()
 *	Return an unsigned 32-bit pseudorandom number uniformly distributed across 
 *	all 32-bit values
 *
 *	We use our own implementation of rand() rather than a C library 
 *	implementation for two reasons:
 *	- our own implementation is known to be of reasonable quality;
 *	- the pseudorandom bit patterns for memory testing are reliably 
 *	  reproducible on normal 32-bit CPUs.
 *
 *	Uses the quick-and-dirty linear congruential generator from
 *	"Numerical Recipes in C" 2nd edition, Press et al., CUP, UK, 1992.
 *	Chapter 7 p.284.
 *	This is as good as any 32-bit linear congruential generator.
 */
u32 diag_memtest_rand(void)
{
	memtest_seed = (1664525UL * memtest_seed) + 1013904223UL;

	return memtest_seed;
}

u16 diag_memtest_rand16(void)
{
/*	Return 16 bits from near the high-order end of our 32-bit rand() value.
	*	(Linear congruential generators tend to be poor in the low-order bits).
 */
	return (u16) (diag_memtest_rand() >> 14);
}
 
/*byteCount must be even*/
void diag_memtest_word_set(void * destPt, u32 byteCount, u16 wordValue)
{
	volatile u16 * destWordPt;

	destWordPt = destPt;
	while (byteCount > 1)		/*Don't write beyond byteCount*/
	{
		*destWordPt++ = wordValue;
		byteCount -= 2;
	}
}
/*byteCount must be even*/
void memtest_rand_set( void * destPt, u32 byteCount, int complement, void (*percent_callback)(int), int *quit)
{
	u16          randomWord;
	volatile u16 * destWordPt;	
	u32	bytes_generated;
	u32	original_bytecount;
	
	original_bytecount = byteCount;
	bytes_generated = 0;

	destWordPt = destPt;
	while (byteCount > 1)		/*Don't write beyond byteCount*/
	{
		randomWord = diag_memtest_rand16();
		*destWordPt++ = complement ? ~randomWord : randomWord;
		byteCount -= 2;
		bytes_generated += 2;
		
		if (percent_callback && !(bytes_generated % 0x20000))
		{
			u32 kib_generated = bytes_generated/1024;
			u32 kib_original  = original_bytecount/1024;
			//diag_printf("bytes_generated=%u byteCount=%u\n", bytes_generated, original_bytecount);
			(*percent_callback)((int)(kib_generated*100)/kib_original);
			
			if (quit && diag_test_quit())
			{
				if (quit)
					*quit = 1;
				return;
			}
		}
	}
}
 
void diag_memtest_init_make_data(int testType, u16 testValue)
{
	if ((testType == MEMORY_TEST_RANDOM) || 
		    (testType == MEMORY_TEST_RANDOM_COMPLEMENTED))
	{
/*		Set the pseudorandom number generator seed to the given value
 */
		diag_memtest_srand( testValue );

/*		Stir up the seed
 */
		diag_memtest_rand();
		diag_memtest_rand();
	}
}

void diag_memtest_make_data(void * destPt, u32 byteCount, int testType, u16 testValue, void (*percent_callback)(int), int *quit)
{
	switch (testType)
	{
		case MEMORY_TEST_CONSTANT:
			diag_memtest_word_set(destPt, byteCount, testValue);
			break;
		case MEMORY_TEST_RANDOM:
			memtest_rand_set(destPt, byteCount, 0, percent_callback, quit);
			break;
		case MEMORY_TEST_RANDOM_COMPLEMENTED:
			memtest_rand_set(destPt, byteCount, 1, percent_callback, quit);
			break;
	}
}

int diag_memtest_compare_data(void * readbackDataPt, void * expectedDataPt, 
				       u32 byteCount, void * deviceStartPt)
{
	u32   byteIndex;
	u8    * byteRPt;
	u8    * byteEPt;
	u8    byteReadBack;
	u8    byteExpected;
	int dataIdentical;

	dataIdentical = 1;

	byteRPt = readbackDataPt;
	byteEPt = expectedDataPt;
	for (byteIndex = 0; byteIndex < byteCount; byteIndex++ )
	{
		byteReadBack = *byteRPt++;
		byteExpected = *byteEPt++; 
		if (byteReadBack != byteExpected)
		{
			dataIdentical = 0;

			diag_printf( "\r\n   Compare error: address %X  read %Xh  expected %Xh ",
					 (int) (((u32) deviceStartPt) + byteIndex), (int) 8, 
					 (int) byteReadBack, (int) 2, 
					 (int) byteExpected, (int) 2 );
		}
	}

	return dataIdentical;
} 

void diag_rom_checksum_test(void)
{
	u8	* address;
	u16	checksum0;
	u16	checksum1;

	checksum0 = 0;
	checksum1 = 0;

	for ( address = (u8 *)CONFIG_UBOOT_ROM_BASE;
		     address <= (u8 *)(CONFIG_UBOOT_ROM_BASE + CONFIG_UBOOT_SIZE - 1); address++ )
	{
		if ( ( (u32)address & 0x01 ) == 0 )
		{
			checksum0 = (u16)( checksum0 + *address );
		}
		else
		{
			checksum1 = (u16)( checksum1 + *address );
		}
		if (address == (u8 *)(CONFIG_UBOOT_ROM_BASE + CONFIG_UBOOT_SIZE - 1))
		{
			break;
		}
	}
	serial_puts( "\nROM checksum0 0x" );
	diag_hex2byte_out( checksum0 );
	serial_puts( "\nROM checksum1 0x" );
	diag_hex2byte_out( checksum1 );
	serial_putc('\n');

}

#ifdef CONFIG_MMC_SPI
void diag_card_slot_test(void)
{
	sdcard_test_slot_v2();
}
#endif

#ifdef CONFIG_CMD_W1
void diag_probe_w1(void)
{
	unsigned char data[10];
	unsigned char key;
	int bus;

	/* Check we actually have registered onewire busses */
	if (w1_get_bus_num() == 0) {
		diag_printf(" No 1-wire busses registered.\n");
	}
	else {
		/* Loop through all busses */
		for (bus = 0; bus < w1_get_bus_num(); bus++) {
	
			/* Set the global bus */
			w1_set_bus(bus);
	
			/* Read the eeprom */
			if (w1_eeprom_read(data, 0, 10) < 0) {
				diag_printf(" Bus %d: no device detected\n", bus);
				continue;
			}
	
			/* Display the data */
			diag_printf(" Bus %d: board %d detected\n", bus, data[9]);
		}
	}

	/* Wait for any key to be pressed */
	diag_printf("\nPress any key to continue...");
	key = serial_getc();
	diag_printf("\n");
}
#endif /* CONFIG_CMD_W1 */
