/*
 * File      : board.c
 * This file is part of RT-Thread RTOS
 * COPYRIGHT (C) 2012, RT-Thread Development Team
 *
 * The license and distribution terms for this file may be
 * found in the file LICENSE in this distribution or at
 * http://www.rt-thread.org/license/LICENSE
 *
 * Change Logs:
 * Date         Author      Notes
 * 2012-02-13   mojingxian  first version
 */

#include "board.h"
#include "rtconfig.h"
#include "rtdef.h"
#include "rthw.h"
#include "serial.h"

#include <signal.h>
#include <sys/platform.h>
#include <ccblkfn.h>
#include <sysreg.h>
#include <string.h>
#include <sys\exception.h>
#include <stdio.h>

#define IVG_CLR(index)     (index > 0 ? ((0xFFFFFFF0 << (index * 0x04)) | \
                           (0xFFFFFFF0 >> ((0x08 - index) * 0x04))):0xFFFFFFF0)
#define IVG_SET(index,ivg) ((((ivg) - 0x07) & 0x0F) << (index * 0x04))

#define UART0   ((struct uartport *)pUART_THR)
struct serial_int_rx uart0_int_rx;
struct serial_device uart0 =
{
    UART0,
    &uart0_int_rx,
    RT_NULL
};
struct rt_device uart0_device;

/**
 * This function is to set the EBIU(EXTERNAL BUS INTERFACE UNIT).
 */
static void rt_hw_ebiu_init(void)
{
    *pEBIU_AMBCTL0  = 0xffc2ffc2;

    *pEBIU_AMBCTL1  = 0xffc2ffc3;

    *pEBIU_AMGCTL   = 0x010f;
}

/**
 * This is the timer interrupt service routine.
 */
EX_INTERRUPT_HANDLER(rt_hw_timer_handler)
{
    /* enter interrupt */
    rt_interrupt_enter();

    rt_tick_increase();

    /* leave interrupt */
    rt_interrupt_leave();
}

/**
 * This function is called to initialize system tick source (typically a
 * timer generating interrupts every 1 to 100 mS).
 * We decided to use Core Timer as the tick interrupt source.
 */
void rt_hw_core_timer_init(void)
{
    *pTCNTL   = 1;     // Turn on timer, TMPWR
    *pTSCALE  = 0x00;
    *pTCOUNT  = CCLKSPEED / RT_TICK_PER_SECOND;
    *pTPERIOD = CCLKSPEED / RT_TICK_PER_SECOND;
    register_handler(ik_timer,rt_hw_timer_handler);
    *pTCNTL    = 0x07; // Start Timer and set Auto-reload
}

void rt_hw_interrupt_init(void)
{
    extern rt_uint32_t rt_interrupt_from_thread;
    extern rt_uint32_t rt_interrupt_to_thread;
    extern rt_uint32_t rt_thread_switch_interrupt_flag;
    extern rt_uint8_t  rt_interrupt_nest;
    extern void interrupt_thread_switch(void);

    register_handler(ik_ivg14,interrupt_thread_switch); //context_vdsp.S

    /* init interrupt nest, and context in thread sp */
    rt_interrupt_nest               = 0;
    rt_interrupt_from_thread        = 0;
    rt_interrupt_to_thread          = 0;
    rt_thread_switch_interrupt_flag = 0;
}

static void rt_hw_pll_init(void)
{
    unsigned long imask;

    sysreg_write(reg_SYSCFG, 0x32);

    *pSIC_IWR = 0x01;

    *pPLL_CTL = SET_MSEL(SPEED_MULTIPLE);

    // PLL Re-programming Sequence.
    // Core is idle'ed to allow the PPL to re-lock.
    imask = cli();
    idle();
    sti(imask);
    *pVR_CTL = 0x00FB;

    // PLL Re-programming Sequence.
    // Core is idle'ed to allow the PPL to re-lock.
    imask = cli();
    idle();
    sti(imask);

    *pPLL_DIV = BUS_DIVISOR;
}

/**
 * This function is called to initialize external sdram.
 */
static void rt_hw_exdram_init(void)
{
    // Initalize EBIU control registers to enable all banks
    *pEBIU_AMBCTL1 = 0xFFFFFF02;
    ssync();

    *pEBIU_AMGCTL = 0x00FF;
    ssync();

    // Check if already enabled
    if (SDRS != ((*pEBIU_SDSTAT) & SDRS))
    {
        return;
    }

    //SDRAM Refresh Rate Control Register
    *pEBIU_SDRRC = 0x01A0;

    //SDRAM Memory Bank Control Register
    *pEBIU_SDBCTL = 0x0025; //1.7   64 MB

    //SDRAM Memory Global Control Register
    *pEBIU_SDGCTL = 0x0091998D;//0x998D0491

    ssync();
}

short uart_set_bitrate(unsigned long bit_rate)
{
    unsigned short int divisor;

    switch (bit_rate)
    {
        case 1200:
        case 2400:
        case 4800:
        case 9600:
        case 19200:
        case 28800:
        case 38400:
        case 57600:
        case 115200:
        case 125000:
            divisor = (unsigned short int) ((float) SCLKSPEED / ((float) bit_rate * 16.0f) + 0.5f);

            *(pUART_LCR) |= DLAB;            // Enable access to DLL and DLH registers
            *(pUART_DLL) = divisor & 0xFF;
            *(pUART_DLH) = divisor >> 8;
            *(pUART_LCR) &= ~DLAB;           // clear DLAB bit

            break;

        default: // baud rate not supported
            break;
    }

    return 0;
}

void rt_hw_uart_init(void)
{
    // Apply UART configuration 8 bit data, No parity, 1 stop bit
    *pUART_LCR = 0x0000; // Reset value
    *pUART_LCR = WLS(8);

     // Ensure that Loopback mode is disabled by clearing LOOP_ENA bit
    *pUART_MCR = 0x0000; //Reset value

    uart_set_bitrate(19200);// Set communication baudrate 115200

    *pUART_IER = ERBFI;

    // Enable UART clock
    *pUART_GCTL = UCEN;
}

int uart_put_char(const char c)
{
    while (!(*pUART_LSR & THRE))
    {
        /* wait */
    }

    *pUART_THR = c;

    return c;
}

void rt_hw_console_output(const char *str)
{
    while (*str != '\0')
    {
        if (*str == '\n')
            uart_put_char('\r');
        uart_put_char(*str++);
    }
}

EX_INTERRUPT_HANDLER(uart_rx_isr)
{
    rt_interrupt_enter();

    rt_hw_serial_isr(&uart0_device);

    rt_interrupt_leave();
}

void rt_hw_isr_install(void)
{
    *pSIC_IWR   = 0xFFFFFFFF;
    *pSIC_IMASK = 0x00000000;

    *pSIC_IAR1 &= IVG_CLR(IAR1_DMA6_UARTRX_IVG);
    *pSIC_IAR1 |= IVG_SET(IAR1_DMA6_UARTRX_IVG,ik_ivg9);
    register_handler(ik_ivg9,uart_rx_isr);
    *pSIC_IMASK |= DMA6_UART_RX_INT_MASK;/*  ���ж�      */
}

void rt_hw_board_init(void)
{
    rt_hw_pll_init();

    rt_hw_ebiu_init();

    rt_hw_exdram_init();

    rt_hw_uart_init();
}