/*
 *  Copyright (c) 1996-2007 Digi International Inc., All Rights Reserved
 *
 *  This software contains proprietary and confidential information of Digi
 *  International Inc.  By accepting transfer of this copy, Recipient agrees
 *  to retain this software in confidence, to prevent disclosure to others,
 *  and to make no use of this software other than that for which it was
 *  delivered.  This is an unpublished copyrighted work of Digi International
 *  Inc.  Except as permitted by federal law, 17 USC 117, copying is strictly
 *  prohibited.
 *
 *  Restricted Rights Legend
 *
 *  Use, duplication, or disclosure by the Government is subject to
 *  restrictions set forth in sub-paragraph (c)(1)(ii) of The Rights in
 *  Technical Data and Computer Software clause at DFARS 252.227-7031 or
 *  subparagraphs (c)(1) and (2) of the Commercial Computer Software -
 *  Restricted Rights at 48 CFR 52.227-19, as applicable.
 *
 *  Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343
 *
 *
 * memtest.c - Memory test routines.
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <Npttypes.h>
#include <ncc_init.h>
// #include "bsp.h"

#define NETOS_DEBUG     (1)

#define ITOA_BUFFER_SIZE        (20)


#if defined NETOS_MEMORY_TEST
void nccRunMemoryTest(unsigned long *start,unsigned long *end, int iterations);

static char *localItoa(int n)
{
    static char buffer[ITOA_BUFFER_SIZE];
    char *p = &buffer[ITOA_BUFFER_SIZE - 1];
    char *result = "0";
    BOOLEAN isNegative = FALSE;
    
    memset(buffer, 0, sizeof(buffer));
    
    if (n == 0)
    {
        goto localItoaExit;
    }
    if (n < 0)
    {
        isNegative = TRUE;
        n = -1 * n;
    }
    
    do
    {
        --p;
        *p = '0' + (n % 10);
        n = n / 10;
    } while ((n != 0) && (p != buffer));
    
    if ((isNegative) && (p != buffer))
    {
        --p;
        *p = '-';
    }
    
    result = p;
    
localItoaExit:
    return result;
}

#endif


static char *localPtoa(void *address)
{
    static char buffer[ITOA_BUFFER_SIZE];
    unsigned n = (unsigned) address;
    const char hexTable[] = "0123456789ABCDEF";
    memset(buffer, 0, sizeof(buffer));
    
    buffer[0] = '0';
    buffer[1] = 'x';
    buffer[2] = hexTable[(n & 0xf0000000) >> 28];
    buffer[3] = hexTable[(n & 0x0f000000) >> 24];
    buffer[4] = hexTable[(n & 0x00f00000) >> 20];
    buffer[5] = hexTable[(n & 0x000f0000) >> 16];
    buffer[6] = hexTable[(n & 0x0000f000) >> 12];
    buffer[7] = hexTable[(n & 0x00000f00) >> 8];
    buffer[8] = hexTable[(n & 0x000000f0) >> 4];
    buffer[9] = hexTable[(n & 0x0000000f)];
    
    return buffer;
}


/*ad*
 * This function is called to customize a memory test which is called
 * upon startup before we load the C library and the operating system.
 * This called from the routine nccInit() which is called from the
 * Reset_Handler() which has a small stack.  Return FALSE if the memory 
 * test fails.
 *
 * @external
 * @category BSP:BSP_CustomizationHooks
 * @since 6.1
 *
 * @note  The C library is not loaded and there is a very small stack
 * when this routine is called.
 *
 * @param start   Pointer to the starting address of memory to test
 * @param ramSize The number of bytes in RAM which we are going to test.
 *
 * @return PASS=0 FAIL=-1
 *
*ad*/
int customizeMemoryTest(unsigned long * start, unsigned long ramSize)
{

#if defined NETOS_MEMORY_TEST
    /* 
     * The memory test takes a starting and ending address as arguments,
     * this is a rebust memory test which test a long time to complete,
     * this is intended to help debug memory problems on new boards and
     * is not intended to be run on startup.
     */
    bsp_printf("Starting memory test.\n");
    nccRunMemoryTest((unsigned long *)start, 
                     (unsigned long *)(ramSize-1), 100);
#else
    unsigned long *end;

    end = (unsigned long *)((char *)start+ramSize);

#if defined NETOS_DEBUG
    bsp_printf("customizeMemoryTest: start\t[");
    bsp_printf(localPtoa(start));
    bsp_printf("]end\t[");
    bsp_printf(localPtoa(end));
    bsp_printf("]\n");
#endif

    /* Run the fast memory test */
    if (NAFastMemoryTest((WORD32)start, (WORD32)end) == TRUE)
    {
#if defined NETOS_DEBUG
        bsp_printf("ncc_init: ERROR - RAM test failure!\n");
#endif
        nccInitializationFailed(NCC_MEMORY_TEST_FAIL);
		return -1;
    }

#if defined NETOS_DEBUG
   bsp_printf("memory test done\n");
#endif

#endif

    return 0;
}


#if defined NETOS_MEMORY_TEST || defined NETOS_MEMORY_TEST_LOOP_FOREVER

#define LOOP_SIZE   4
#define BLOCK_SIZE  64 /* In words */

int InitArray(int val, unsigned long *start, unsigned long *end);
int CheckArray(int val, unsigned long *start, unsigned long *end); 
void FlushMemControllerBuffer(unsigned long volatile *ram, unsigned long pattern);
void MemTestFail(unsigned long *addr, unsigned long val, unsigned long read);
void nccInitializationFailed(int reason);
int NS9XXXRamTest(unsigned long *start, unsigned long *end);

/*
 * Call this routine to run a memory test, be careful to adjust the 
 * starting and ending address correctly, this memory test is defined in
 * the file memtest.c.
 */
 
void nccRunMemoryTest(unsigned long *start,unsigned long *end, int iterations)
{
    int i=0, num_failures=0;

#if defined NETOS_DEBUG
    bsp_printf("\nnccRunMemoryTest: starting [");
    bsp_printf(localPtoa(start));
    bsp_printf("] ending [");
    bsp_printf(localPtoa(end));
    bsp_printf("]\n");
#endif

    /*
     * You need to adjust the starting and ending address of the memory
     * test based on your board and code size.  This is used to 
     * help debug stability problems on NS9750 Dev boards, so if
     * you are seeing strange behavior enable this test by setting
     * run_ram_test=1 above.  
     * Note: The starting and ending addresses should start
     * on a four byte boundary
     */
    do
    {
#if defined NETOS_DEBUG
        bsp_printf("\nnccInit: memtest pass\t[");
        bsp_printf(localItoa(i));
        bsp_printf("]\tfailures\t[");
        bsp_printf(localItoa(num_failures));
        bsp_printf("]\n");
#endif

        if (NS9XXXRamTest(start, end) < 0)
        {
#if defined NETOS_DEBUG
            bsp_printf("\nnccInit: memory test failed\n");
#endif
            num_failures++;
            nccInitializationFailed(NCC_MEMORY_TEST_FAIL);                
        }
        i++;
    } while (--iterations > 0);

}


/*
 * The number of words which must be written to flush the
 * memory controller.
 */
#define MEMC_BUFFER_SIZE 64 /* In words */

/*
 * Rigorous memory test, return an error code on failure
 * and save the memory address and value of the failure.
 *
 * Note: start and end must be aligned on a 4 byte boundary.
 */
int NS9XXXRamTest(unsigned long *start, unsigned long *end)
{
    unsigned long  volatile *block_ptr, *ptr_long, read;
    unsigned long                     retval=0, i, j;

    /* These are the patterns used to write the "back ground color" */
    unsigned long            val_array[LOOP_SIZE] = { 0x00000000, 
                                                      0xFFFFFFFF, 
                                                      0x55555555, 
                                                      0xAAAAAAAA };

    /*
     * Loop here 4 times, setting all of RAM to the pattern
     * defined in val_array.  There is an inner loop
     * which 'walks' memory in 64 WORD blocks to perform a 
     * walking one and a walking zero on all of memory.
     */
    for (i = 0; i < LOOP_SIZE; i++)
    {
        /* Initialize all of memory to the background color */
        if (InitArray(val_array[i], start, end) != 0)
        {
#if defined NETOS_DEBUG
            bsp_printf("InitArray failed\n");
#endif
            return -1;
        }
    
        /* Verify that the memory was written correctly */
        if (CheckArray(val_array[i], start, end) != 0)
        {
            bsp_printf("CheckArray failed\n");
            return -1;
        }

        /*
         * Loop here and test all of memory in 64 WORD increments. 
         */
        for (block_ptr = start; block_ptr < end; 
             block_ptr = block_ptr + BLOCK_SIZE)

        {
            /* Walk a one across the first 32 words of this block */
            for (j = 0, ptr_long=block_ptr; j < (BLOCK_SIZE / 2); j++)
            {
                *ptr_long++ = (1 << j);
            }


            /* Walk a zero across the next 32 words of this block*/
            for (j = 0; j < (BLOCK_SIZE / 2); j++)
            {
                *ptr_long++ = ~(1 << j);
            }

            /*
             * Flush out the memory controller, by doing 64 WORD
             * writes to some memory location.
             */
            if ((unsigned long)block_ptr > BLOCK_SIZE)
            { 
                FlushMemControllerBuffer(block_ptr - BLOCK_SIZE, val_array[i]);
            }
            else
            {
                FlushMemControllerBuffer(block_ptr + BLOCK_SIZE, val_array[i]);
            }
            
            /* 
             * Verify the walking one was correctly written to memory 
             */
            for (j = 0, ptr_long=block_ptr; j < (BLOCK_SIZE / 2); j++)
            {
                if ((read = *ptr_long++) != (unsigned long) (1 << j))
                {
                    --ptr_long; /* Go back to the address */
                    MemTestFail((unsigned long *)ptr_long, (1 << j), read);
                    return -2;                    
                }
    
            }

            /*
             * Verify the walking zero is correct  
             */
            for (j = 0; j < (BLOCK_SIZE / 2); j++)
            {
                if ((read = *ptr_long++) != (unsigned long)~(1 << j))
                {
                    --ptr_long;  /* Go back to the address */
                    MemTestFail((unsigned long *)ptr_long, (unsigned long)~(1 << j), read);
                    return -3;                    
                }

            }

            /* Set this block back to the loop value */
            for (j = 0, ptr_long=block_ptr; j < BLOCK_SIZE; j++)
            {
                *ptr_long++ = val_array[i];
            }


        }
    } 

    return retval;
}

void FlushMemControllerBuffer(unsigned long volatile *ram, unsigned long pattern)
{
    int i;

    for (i = 0; i < MEMC_BUFFER_SIZE; i++)
    {
        ram[i]= pattern;
    }
}

int InitArray(int val, unsigned long *start, unsigned long *end)
{
    unsigned long volatile *ram=start;

    while (ram < end)
    {
        *ram++ = val;
    } 

    return 0;
}


int CheckArray(int val, unsigned long *start, unsigned long *end)
{
    unsigned long volatile *ram=start;
    unsigned long read;

    while (ram < end)
    {
        if ((read = *ram++) != (unsigned long) val)
        {
            --ram; /* Go back to the address which failed */
            MemTestFail((unsigned long *)ram, val, read);
            return -1;
        }
    } 

    return 0;
}

void MemTestFail(unsigned long *addr, unsigned long val, unsigned long read)
{
    bsp_printf("ERROR: address = [");
    bsp_printf(localPtoa(addr));
    bsp_printf("]\texpected value = [");
    bsp_printf(localItoa((int) val));
    bsp_printf("]\tread = [");
    bsp_printf(localItoa((int) read));
    bsp_printf("]\n");
}

#endif


