/*
 *
 *     Module Name: simpleSerial.c
 *         Version: 1.00
 *   Original Date: 02/24/03    
 *          Author: Li Zhen
 *        Language: Ansi C
 * Compile Options:
 * Compile defines:
 *       Libraries:
 *    Link Options:
 *
 *    Entry Points:  
 *
 *  Copyright (c) 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
 *
 * Description.
 * =======================================================================
 *  This file contains the code that sets up a simple serial driver to be 
 *  able to output some debugging informations in BSP initilization stage 
 *  before kernel is loaded.
 *  THE CODE IN THIS FILE MUST BE CUSTOMIZED TO SUPPORT YOUR APPLICATION
 *  HARDWARE.  The default implementation supports the NET+OS development
 *  boards, serial PORTA.
 *
 *
 *
 * Edit Date/Ver   Edit Description
 * ==============  =======================================================
 *
 *
 * 12/15/05 JZW    In function setupSimpleSerial there is a bug in the 
 *                 use of the pllMode valiable. This variable is only required
 *                 for certain ND values on the NS9750. The bug that this is working
 *                 around was fixed on the NS9360. added #ifdef NS9750 so that 
 *                 current pllMOde values continue on NS9750 but always go to 1
 *                 on NS9360 and later chips.
 */

/*
 * Do not attempt to trace registers before the C library is
 * setup.  The global variables used by the trace library will
 * be in an undefined state until after the C library has
 * initilized the data segement.
 */

/* Define SERIAL_TIME_POLLING will increase 
 * the size of bootloader. 
 */
/* #define  SERIAL_TIMER_POLLING */

#include <string.h>

#ifdef NA_REG_TRACE_ENABLED
    #ifdef _REG_DEF_H
        #error Trace code compiled into init code.
    #endif
    #undef NA_REG_TRACE_ENABLED
#endif

#include <gpio.h>
#include <ncc_init.h>
#include <gpio_def.h>
#include "reg_def.h"
#include "bsp_serial.h"
#include "sysClock.h" /* for NAgetXtalFreq */
#include "iohub_reg.h"
#include "uart_16750.h"
#include "bele.h"
#include <simpleserial.h>
#include <natimer.h>
#include <mctimer.h>


#if (BSP_SIMPLE_SERIAL_PORT == BSP_SIMPLE_SERIAL_ON_PORTA)
    #define NASS_ACTIVE_PORT        BSP_GPIO_MUX_SERIAL_A
    #define PORT        0
    #define IOHUB_PORT  (PORT + 2)
#elif (BSP_SIMPLE_SERIAL_PORT == BSP_SIMPLE_SERIAL_ON_PORTB)
    #define NASS_ACTIVE_PORT        BSP_GPIO_MUX_SERIAL_B
    #define PORT        1
    #define IOHUB_PORT  (PORT + 2)
#elif (BSP_SIMPLE_SERIAL_PORT == BSP_SIMPLE_SERIAL_ON_PORTC)
    #define NASS_ACTIVE_PORT        BSP_GPIO_MUX_SERIAL_C
    #define PORT        2
    #define IOHUB_PORT  (PORT + 2)
#elif (BSP_SIMPLE_SERIAL_PORT == BSP_SIMPLE_SERIAL_ON_PORTD)
    #define NASS_ACTIVE_PORT        BSP_GPIO_MUX_SERIAL_D
    #define PORT        3
    #define IOHUB_PORT  (PORT + 2)
#else
    #error BSP_SIMPLE_SERIAL_PORT is out of range
#endif

#ifdef SERIAL_TIMER_POLLING
#define SERIAL_FGET_CAN_USE_TIMER
#else
#define SERIAL_FGET_POLLING_SPEED    1000
#endif


/*
 * Data is always read in little endian formation when the receive FIFO is read
 * directly.  This macro swaps the word to the correct endianess when operating
 * in big endian mode.
 */
#ifdef _LITTLE_ENDIAN
    #define FIX_ENDIANNESS(word)        (word)
#else
    #define FIX_ENDIANNESS(word)        swap32(word)
#endif



/*  Function: setupSimpleSerial (void)
 *
 *  Description:
 *     This function will setup a Serial Port so BSP can send some info
 *     through the PORTA debugger port.
 *
 *  Parameters:
 *     none
 *  
 *  Return Value:
 *     none
 *
 */

void setupSimpleSerial(void)
{
    unsigned divisor;
    
    /* Make sure GPIO pins needed for serial are set up */

#if (BSP_SIMPLE_SERIAL_PORT == BSP_SIMPLE_SERIAL_ON_PORTA)
    switch (NASS_ACTIVE_PORT)
    {
        case BSP_GPIO_MUX_SERIAL_8_WIRE_UART:
        case BSP_GPIO_MUX_SERIAL_6_WIRE_UART:
        case BSP_GPIO_MUX_SERIAL_4_WIRE_UART:
            NAconfigureGPIOpin(1, NA_GPIO_SELECT_MUX_FUNC0, 0);
            NAconfigureGPIOpin(5, NA_GPIO_SELECT_MUX_FUNC0, 0);
            /* fall through is intended */
        default:
            NAconfigureGPIOpin(3, NA_GPIO_SELECT_MUX_FUNC0, 0);
            NAconfigureGPIOpin(7, NA_GPIO_SELECT_MUX_FUNC0, 0);
    }
#elif (BSP_SIMPLE_SERIAL_PORT == BSP_SIMPLE_SERIAL_ON_PORTB)
    switch (NASS_ACTIVE_PORT)
    {
        case BSP_GPIO_MUX_SERIAL_8_WIRE_UART_USE_ALTERNATE_PATH:
        case BSP_GPIO_MUX_SERIAL_6_WIRE_UART_USE_ALTERNATE_PATH:
        case BSP_GPIO_MUX_SERIAL_4_WIRE_UART_USE_ALTERNATE_PATH:
            NAconfigureGPIOpin(52, NA_GPIO_SELECT_MUX_FUNC0, 0);
            NAconfigureGPIOpin(56, NA_GPIO_SELECT_MUX_FUNC0, 0);
            /* fall through is intended */
        case BSP_GPIO_MUX_SERIAL_2_WIRE_UART_USE_ALTERNATE_PATH:
            NAconfigureGPIOpin(54, NA_GPIO_SELECT_MUX_FUNC0, 0);
            NAconfigureGPIOpin(58, NA_GPIO_SELECT_MUX_FUNC0, 0);
            break;
        case BSP_GPIO_MUX_SERIAL_8_WIRE_UART:
        case BSP_GPIO_MUX_SERIAL_6_WIRE_UART:
        case BSP_GPIO_MUX_SERIAL_4_WIRE_UART:
            NAconfigureGPIOpin(17, NA_GPIO_SELECT_MUX_FUNC1, 0);
            NAconfigureGPIOpin(21, NA_GPIO_SELECT_MUX_FUNC1, 0);
            /* fall through is intended */
        default:
            NAconfigureGPIOpin(19, NA_GPIO_SELECT_MUX_FUNC1, 0);
            NAconfigureGPIOpin(23, NA_GPIO_SELECT_MUX_FUNC1, 0);
    }
#elif (BSP_SIMPLE_SERIAL_PORT == BSP_SIMPLE_SERIAL_ON_PORTC)
    switch (NASS_ACTIVE_PORT)
    {
        case BSP_GPIO_MUX_SERIAL_8_WIRE_UART:
        case BSP_GPIO_MUX_SERIAL_6_WIRE_UART:
        case BSP_GPIO_MUX_SERIAL_4_WIRE_UART:
            NAconfigureGPIOpin(9, NA_GPIO_SELECT_MUX_FUNC0, 0);
            NAconfigureGPIOpin(13, NA_GPIO_SELECT_MUX_FUNC0, 0);
            /* fall through is intended */
        default:
            NAconfigureGPIOpin(11, NA_GPIO_SELECT_MUX_FUNC0, 0);
            NAconfigureGPIOpin(15, NA_GPIO_SELECT_MUX_FUNC0, 0);
    }
#else 
    switch (NASS_ACTIVE_PORT)
    {
        case BSP_GPIO_MUX_SERIAL_8_WIRE_UART_USE_ALTERNATE_PATH:
        case BSP_GPIO_MUX_SERIAL_6_WIRE_UART_USE_ALTERNATE_PATH:
        case BSP_GPIO_MUX_SERIAL_4_WIRE_UART_USE_ALTERNATE_PATH:
            NAconfigureGPIOpin(60, NA_GPIO_SELECT_MUX_FUNC0, 0);
            NAconfigureGPIOpin(64, NA_GPIO_SELECT_MUX_FUNC0, 0);
            /* fall through is intended */
        case BSP_GPIO_MUX_SERIAL_2_WIRE_UART_USE_ALTERNATE_PATH:
            NAconfigureGPIOpin(62, NA_GPIO_SELECT_MUX_FUNC0, 0);
            NAconfigureGPIOpin(66, NA_GPIO_SELECT_MUX_FUNC0, 0);
            break;
        case BSP_GPIO_MUX_SERIAL_8_WIRE_UART:
        case BSP_GPIO_MUX_SERIAL_6_WIRE_UART:
        case BSP_GPIO_MUX_SERIAL_4_WIRE_UART:
            NAconfigureGPIOpin(25, NA_GPIO_SELECT_MUX_FUNC1, 0);
            NAconfigureGPIOpin(29, NA_GPIO_SELECT_MUX_FUNC1, 0);
            /* fall through is intended */
        default:
            NAconfigureGPIOpin(27, NA_GPIO_SELECT_MUX_FUNC1, 0);
            NAconfigureGPIOpin(31, NA_GPIO_SELECT_MUX_FUNC1, 0);
    }
#endif
    /*
     * Disable interrupts from the port
     */
    *NA_IOHUB_DMA_RX_ICR_getAddress(IOHUB_PORT) = 0;
    *NA_IOHUB_DMA_TX_ICR_getAddress(IOHUB_PORT) = 0;
    *NA_16750_WRAPPER_IE_getAddress(PORT) = 0;
    *NA_16750_UART_IE_getAddress(PORT) = 0;
    
    /*
     * Reset the port and its fifos
     */
    *NA_16750_CONFIG_getAddress(PORT) = 0;
    narm_write_reg(NA_16750_CONFIG, NA_16750_CONFIG_getAddress(PORT), rxflush, TRUE);
    narm_write_reg(NA_16750_CONFIG, NA_16750_CONFIG_getAddress(PORT), rxflush, FALSE);
    narm_write_reg(NA_16750_CONFIG, NA_16750_CONFIG_getAddress(PORT), txflush, TRUE);
    narm_write_reg(NA_16750_CONFIG, NA_16750_CONFIG_getAddress(PORT), txflush, FALSE);
    *NA_IOHUB_DMA_RX_CONTROL_getAddress(IOHUB_PORT) = 0;
    *NA_IOHUB_DMA_TX_CONTROL_getAddress(IOHUB_PORT) = 0;

    /*
     * Set direct access mode
     */
    narm_write_reg(NA_IOHUB_DMA_RX_CONTROL, NA_IOHUB_DMA_RX_CONTROL_getAddress(IOHUB_PORT), direct, NA_IOHUB_DMA_RX_CONTROL_DIRECT_ACCESS_MODE);
    narm_write_reg(NA_IOHUB_DMA_TX_CONTROL, NA_IOHUB_DMA_TX_CONTROL_getAddress(IOHUB_PORT), direct, NA_IOHUB_DMA_TX_CONTROL_DIRECT_ACCESS_MODE);
     
    /*
     * Set baud rate divisor
     */
    divisor = NAgetSysOscFreq() / (16 * NAGetAppBspBaudRate());

    narm_write_reg(NA_16750_LINE_CONTROL, NA_16750_LINE_CONTROL_getAddress(PORT), dlab, NA_16750_LINE_CONTROL_ACCESS_BAUD_RATE_DIVISOR);
    narm_write_reg(NA_16750_BAUD_RATE_DIVISOR_LSB, NA_16750_BAUD_RATE_DIVISOR_LSB_getAddress(PORT), brdl, divisor & 0xff);
    narm_write_reg(NA_16750_BAUD_RATE_DIVISOR_MSB, NA_16750_BAUD_RATE_DIVISOR_MSB_getAddress(PORT), brdm, divisor >> 8);
    narm_write_reg(NA_16750_LINE_CONTROL, NA_16750_LINE_CONTROL_getAddress(PORT), dlab, NA_16750_LINE_CONTROL_ACCESS_BUFFERS);
    
    /*
     * Setup line control register:  break disabled, parity disabled, 1 stop bit, 8 data bits
     */
    narm_write_reg(NA_16750_LINE_CONTROL, NA_16750_LINE_CONTROL_getAddress(PORT), sb, FALSE);
    narm_write_reg(NA_16750_LINE_CONTROL, NA_16750_LINE_CONTROL_getAddress(PORT), pen, FALSE);
    narm_write_reg(NA_16750_LINE_CONTROL, NA_16750_LINE_CONTROL_getAddress(PORT), stb, NA_16750_LINE_CONTROL_1_STOP_BIT);
    narm_write_reg(NA_16750_LINE_CONTROL, NA_16750_LINE_CONTROL_getAddress(PORT), wls, NA_16750_LINE_CONTROL_8_DATA_BITS);
    
    /*
     * Enable internal UART FIFO.  This is different than the wrapper FIFOs.
     */
    narm_write_reg(NA_16750_UART_FIFO_CONTROL, NA_16750_UART_FIFO_CONTROL_getAddress(PORT), fifoen, TRUE);
    
    /*
     * Enable the transmitter holding register from the UART.  This interrupt will not be
     * seen by software.  It is handled by the hardware wrapper, and needed by the wrapper
     * in order to function.
     */
    narm_write_reg(NA_16750_UART_IE, NA_16750_UART_IE_getAddress(PORT), etbei, TRUE);
     
    /*
     * Start the wrapper by enabling the transmitter and receiver FIFOs
     */
    narm_write_reg(NA_16750_CONFIG, NA_16750_CONFIG_getAddress(PORT), rxen, TRUE);
    narm_write_reg(NA_16750_CONFIG, NA_16750_CONFIG_getAddress(PORT), txen, TRUE);

    /*
     * Enable the transmitter
     */
    narm_write_reg(NA_16750_CONFIG, NA_16750_CONFIG_getAddress(PORT), enableTransmitter, TRUE);

    /*
     * Enable DTR
     */
    narm_write_reg(NA_16750_MODEM_CONTROL, NA_16750_MODEM_CONTROL_getAddress(PORT), dtr, TRUE);

    /*
     * Enable transmitter interrupts from the UART which actually go to
     * the wrapper.  This does not generate interrupts that the CPU sees.
     */
    narm_write_reg(NA_16750_WRAPPER_IE, NA_16750_WRAPPER_IE_getAddress(PORT), tx_idle, TRUE);
    
}




/*  Function: bsp_fputc (char out)
 *
 *  Description:
 *     This function will print one byte infomation
 *     through the PORTA debugger port.
 *
 *  Parameters:
 *     out:     byte to be printed
 *  
 *  Return Value:
 *     none
 *
 */
void bsp_fputc (char out)
{
    if (narm_read_reg(NA_16750_CONFIG, NA_16750_CONFIG_getAddress(PORT), txen) == 0)
    {
        setupSimpleSerial();
    }
    
    /* Wait for room in the transmitter FIFO */
    while (narm_read_reg(NA_IOHUB_IFS, NA_IOHUB_IFS_getAddress(IOHUB_PORT), tx_fifo_full) != 0)
    {
        ;
    }

    /* Write the output byte */
    *(BYTE *)NA_IOHUB_DIRECT_MODE_TX_FIFO_getAddress(IOHUB_PORT) = out;

    if (out == 0x0a)  /* If line feed, add carriage return */
    {
        /* Wait for room in the transmitter FIFO */
        while (narm_read_reg(NA_IOHUB_IFS, NA_IOHUB_IFS_getAddress(IOHUB_PORT), tx_fifo_full)!= 0)
        {
            ;
        }

        /* Write the carriage return byte */
        *(BYTE *)NA_IOHUB_DIRECT_MODE_TX_FIFO_getAddress(IOHUB_PORT) = 0x0d;
    }
}



/*  Function: bsp_printf (char *outString)
 *
 *  Description:
 *     This function will print a null terminated string
 *     through the PORTA debugger port. This function is intended only 
 *     to be used in early BSP initialization stage before the kernel and 
 *     device driver are loaded. The string should be as short as possible
 *     due to limited stack.
 *
 *  Parameters:
 *     outString:   pointer to a null terminated string
 *  
 *  Return Value:
 *     none
 *
 */
void bsp_printf (char *outString)
{
    char *out = outString;

    while (*out != '\0')
    {
        bsp_fputc(*out);
        out++;
    }
}

/*  Function: bsp_fgetw (WORD32 *data)
 *
 *  Description:
 *     This function will print up to 4 bytes of data
 *     through the PORTA debugger port.
 *
 *  Parameters:
 *     data:        pointer to the location to store up to 4 bytes of data
 *  
 *  Return Value:
 *     size:        size of the data
 *
 */
char bsp_fgetw(int pollTime, WORD32 *data)
{
    char size = 0;
#ifdef SERIAL_FGET_CAN_USE_TIMER 
    static timerHandleType handle = TIMER_BAD_HANDLE;
    WORD64 value;
    timerConfigType config = {timerPeriod, {0}, (timerGatingType) 0, TRUE, NULL, NULL, FALSE};
#else
    unsigned long wait_time = pollTime * SERIAL_FGET_POLLING_SPEED; 
#endif

    if (narm_read_reg(NA_16750_CONFIG, NA_16750_CONFIG_getAddress(PORT), rxen) == 0)
    {
        setupSimpleSerial();
    }
    
#ifdef SERIAL_FGET_CAN_USE_TIMER 
    /*
     * We use one of the timers in ARM9 (NS9750 chip)
     * to more accurately setup a delay loop.
     */
    config.timeout.period.seconds = pollTime/1000;
    config.timeout.period.nanoseconds = (pollTime%1000) * 1000000;
    
    if (NATimerOpen(NAWAIT_TIMER, &handle, timerNaturalSize) != timerSuccess)
    {
        customizeErrorHandler(ERROR_TIMER, ERROR_TIMER_NAWAIT_TIMER_UNAVAIL);
    }
    if (NATimerConfigure(handle, &config) != timerSuccess)
    {
        customizeErrorHandler(ERROR_TIMER, ERROR_TIMER_NAWAIT_TIMER_CONFIG);
    }

    NATimerStart(handle);
#endif

    /* Poll for data until we receive something or time out */
    do
    {
        /*
         * If receive FIFO is empty, then try closing the current buffer to force any
         * pending characters into it.
         */
        if (narm_read_reg(NA_IOHUB_IFS, NA_IOHUB_IFS_getAddress(IOHUB_PORT), rx_fifo_empty) != 0)
        {
            narm_write_reg(NA_16750_CONFIG, NA_16750_CONFIG_getAddress(PORT), rxclose, TRUE);
            while(narm_read_reg(NA_16750_CONFIG, NA_16750_CONFIG_getAddress(PORT), rxclose) != 0)
            { 
                ;   /* wait for hardware to complete receive buffer close operation */
            }
        }

        if (narm_read_reg(NA_IOHUB_IFS, NA_IOHUB_IFS_getAddress(IOHUB_PORT), rx_fifo_empty) == 0)
        {   /* if something in FIFO */
            size = narm_read_reg(NA_IOHUB_DIRECT_MODE_RX_STATUS, NA_IOHUB_DIRECT_MODE_RX_STATUS_getAddress(IOHUB_PORT), byte);
            *data = FIX_ENDIANNESS(*NA_IOHUB_DIRECT_MODE_RX_FIFO_getAddress(IOHUB_PORT));
                     
        }            
        if (size)
            break; /* data has been read */

#ifdef SERIAL_FGET_CAN_USE_TIMER 
        NATimerRead(handle, &value);
    } while (value.s.lsw != 0);

    NATimerStop(handle);
    NATimerClose(handle);
#else
    } while(wait_time-- > 0);
#endif

    return (size);
}


 
/* This function shold never be called before main() */   
int bsp_fgetc(int pollTime)
{
    static int indx = 0;
    static char size = 0, data[sizeof(WORD32)];
    unsigned long temp;

    if (indx == size)
    {
        size = bsp_fgetw( pollTime, (WORD32 *)&temp );
        memcpy(data, &temp, sizeof(WORD32));
        indx = 0;
        if (size == 0)
        {
            /* Timeout */
            indx = 0;
            return -2;
        }
    }

    return (data[indx++] & 0xFF);
}


void bsp_flush(void)
{
    while (bsp_fgetc(1000) >= 0);
}

