/**
 * \file    drv_mcu.c
 * \brief   MCU driver
 *
 * \par     Copyright (c) 2018 UWE Technologies Corp., Ltd. All Rights Reserved.
 * \author  James
 * \date    2018-03-05
 * \version 
 * 
 * \history 
 *  Date        Note
 *  2018-03-05  Original Creation
 * 
 */

#include "drivers.h"
#include "libc.h"


/**
 * array of function pointers which contains the IPIC
 * interrupt handlers
 */
v_func_ptr_t g_ext_interrupt_handlers[IPIC_CFG_INTR_VEC_NUM];

/**
 * array of function argument
 */
u32_t g_ext_interrupt_handler_args[IPIC_CFG_INTR_VEC_NUM];


/**
 * external interrupt enable bit mapping
 */
volatile u32_t g_ext_interrupt_en_map = 0;
/**
 * external interrupt index priority mapping
 *  4'h0 : pd_int is 0;
 *  4'h1 : dma_int is 1;
 *  4'h2 : i2c_int is  2;
 *  4'h3 : sec_i2c_int is 3;
 *  4'h4 : uart0_int is 4;
 *  4'h5 : uart1_int is 5;
 *  4'h6 : spi_int is 6;
 *  4'h7 : gpio_int is 7;
 */
volatile u32_t g_ext_interrupt_idx_pri_map = 0x76543210; //0x06543271;
volatile u32_t g_ext_interrupt_pri_idx_map = 0x76543210; //0x16543207;

#if SYS_DRIVER_M_TIMER_ENABLED
/**
 *  RISC-V Machine timer interrupt handler
 *  If user need continuous timer, the handler need to re-enable the mtimer
 */
v_func_v_ptr_t g_mtimer_interrupt_handler = NULL;
#endif /* SYS_DRIVER_M_TIMER_ENABLED */

/**
 *  RISC-V Machine software interrupt handler
 */
v_func_v_ptr_t g_msw_interrupt_handler = NULL;


int trap_int_mext_handler(unsigned long cause, unsigned long epc, unsigned long *regs);


/**
 * The external interrupts initialization
 *
 * \see
 */
static void drv_intr_ext_init(void)
{
    int i;

    sync_ext_isr_pending_set(FALSE);

    // disable external interrupt
    clear_csr(mie, MIP_MEIP);

    DRV_REG_SET(MCU_INT_INDEX_REG,g_ext_interrupt_pri_idx_map);

    // enable external interrupt vectors
    for (i = 0; i < IPIC_CFG_INTR_VEC_NUM; i++ )
    {
        write_csr(0xBF6, i);
        write_csr(0xBF7, 0);
        SAL_BIT_CLEAR(g_ext_interrupt_en_map,i);
        g_ext_interrupt_handlers[i] = drv_intr_mext_default_handler;
        g_ext_interrupt_handler_args[i] = SAL_FIELD_GET2(g_ext_interrupt_idx_pri_map,i<<2,3);
    }

    // enable external interrupt
    set_csr(mie, MIP_MEIP);
}


/**
 * The Non-maskable interrupt service
 *
 * \see
 */
int trap_handler(unsigned long cause, unsigned long epc, unsigned long *regs)
{
	if (0 == (0x80000000 & cause)) /* is exception */
	{
        drv_uart_putstr("Trap handler: cause=");
        drv_uart_puthex32(cause);
        drv_uart_putstr(" epc=");
        drv_uart_puthex32(epc);
        drv_uart_putchar(UART_LF);
        drv_chip_reset();
	}
	else { /* is interrupt */

	}
    return 0;
}

/**
 * The software interrupt service
 *
 * \see
 */
int trap_int_msw_handler(unsigned long cause, unsigned long epc, unsigned long *regs)
{
    // disable software interrupt
    DRV_MSW_INTR_DISABLE();

    //printf("M-Mode software interrupt\n");

    if (g_msw_interrupt_handler) 
    {
        g_msw_interrupt_handler();
    }

    return 0;
}


/**
 * The Timer interrupt service
 *
 * \see
 */
int trap_int_mtimer_handler(unsigned long cause, unsigned long epc, unsigned long *regs)
{
    // disable timer interrupt
    SCR_RTC_TIMER_DISABLE();

    //printf("M-Mode Timer interrupt\n");

#if SYS_DRIVER_M_TIMER_ENABLED
    if (g_mtimer_interrupt_handler) 
    {
        //If continuous timer, the handler need to re-enable the mtimer
        g_mtimer_interrupt_handler();
    }
#endif /* SYS_DRIVER_M_TIMER_ENABLED */

    return 0;
}


/**
 * The external interrupt service
 *
 * \see
 */
int trap_int_mext_handler(unsigned long cause, unsigned long epc, unsigned long *regs)
{
    // write start of interrupt
    write_csr(0xBF5, 0x1);
    // get priority
	unsigned long mext_idx;
    asm volatile ("csrr %0, " _TOSTR(0xBF0)   \
                      : "=r"(mext_idx));

    sync_ext_isr_pending_set(TRUE);

    g_ext_interrupt_handlers[mext_idx](g_ext_interrupt_handler_args[mext_idx]);

    sync_ext_isr_pending_set(FALSE);

    // write end of interrupt
    write_csr(0xBF4, 0x1);

    return 0;
}

// Define Vectored interrupts
// As described in "The RISC-V Instruction Set Manual. Volume II: Privileged Architecture"
// chapter 3.1.12 "Machine Trap-Vector Base-Address Register"
// and "SCR1 External Architecture Specification"
// IMPORTANT! Base address must be aligned by 0x80 for SCR1 cores.
asm(
    ".align 7;"
    ".global global_interrupt_vector;"
    "global_interrupt_vector:;"
    // software interrupts
    "j trap_entry;" // U-mode sw interrupt and generic trap hander
    "j trap_entry;" // S-mode
    "j trap_entry;" // reserved
    "j trap_entry;" // M-mode

    // timer interrupts
    "j trap_entry;"            // U-mode
    "j trap_entry;"            // S-mode
    "j trap_entry;"            // reserved
    "j trap_int_mtimer_entry;" // M-mode

    // external interrupts
    "j trap_entry;" // U-mode
    "j trap_entry;" // S-mode
    "j trap_entry;" // reserved
    "j trap_int_mext_entry;" // M-mode
    );

extern char global_interrupt_vector[];


void drv_intr_init(void)
{
#if SYS_FREERTOS_ENABLED
	// enable external interrupts
	drv_intr_ext_init();
#else
	unsigned long mtvec_val;

    // check and enable vectored mode
    write_csr(mtvec, (unsigned long)global_interrupt_vector | 0x1);
    mtvec_val = read_csr(mtvec);
    if ((mtvec_val & 0x3) != 0x1) {
        //printf("Vectored interrupts are not supported (current mode: %x)\n", mtvec_val & 0x3);
        //printf("Canceled.\n");
        return;
    }

    // enable external interrupts
    drv_intr_ext_init();

    // enable global inerrupts
    set_csr(mstatus, MSTATUS_MIE);
#endif
}

bool_t drv_intr_ext_en_get(int idx)
{
    if (idx >= IPIC_CFG_INTR_VEC_NUM)
        return FALSE;

    return SAL_IS_BIT_SET(g_ext_interrupt_en_map,idx) ? TRUE : FALSE;
}

void drv_intr_ext_en_set(int idx, bool_t state)
{
    if (idx >= IPIC_CFG_INTR_VEC_NUM)
        return;

    sync_enter_critical();

    u32_t pri = SAL_FIELD_GET2(g_ext_interrupt_idx_pri_map,idx<<2,3);

    if (state)
    {
    	write_csr(0xBF6, pri);
		write_csr(0xBF7, 0x12);
        SAL_BIT_SET(g_ext_interrupt_en_map,idx);
    }
    else {
    	write_csr(0xBF6, pri);
		write_csr(0xBF7, 0x0);
        SAL_BIT_CLEAR(g_ext_interrupt_en_map,idx);
    }
    sync_exit_critical();
}

void drv_intr_ext_set(int idx, bool_t state, int pri, v_func_ptr_t isr_func)
{
    if (idx >= IPIC_CFG_INTR_VEC_NUM)
        return;

    sync_enter_critical();

    u32_t old_idx = SAL_FIELD_GET2(g_ext_interrupt_pri_idx_map,pri<<2,3);
    u32_t cur_idx_old_pri = SAL_FIELD_GET2(g_ext_interrupt_idx_pri_map,idx<<2,3);
    u32_t old_arg = g_ext_interrupt_handler_args[cur_idx_old_pri];
    g_ext_interrupt_handler_args[cur_idx_old_pri] = g_ext_interrupt_handler_args[pri];
    g_ext_interrupt_handler_args[pri] = old_arg;
    // Update idx to priority reg
    SAL_FIELD_SET2(g_ext_interrupt_pri_idx_map,cur_idx_old_pri<<2,3,old_idx);
    SAL_FIELD_SET2(g_ext_interrupt_pri_idx_map,pri<<2,3,idx);
    DRV_REG_SET(MCU_INT_INDEX_REG,g_ext_interrupt_pri_idx_map);
    // Update priority for new idx to local memory
    SAL_FIELD_SET2(g_ext_interrupt_idx_pri_map,old_idx<<2,3,cur_idx_old_pri);
    SAL_FIELD_SET2(g_ext_interrupt_idx_pri_map,idx<<2,3,pri);

    if (state)
    {
    	write_csr(0xBF6, pri);
		write_csr(0xBF7, 0x12);
        SAL_BIT_SET(g_ext_interrupt_en_map,idx);
        g_ext_interrupt_handlers[cur_idx_old_pri] = g_ext_interrupt_handlers[pri];
        g_ext_interrupt_handlers[pri] = isr_func;
    }
    else {
    	write_csr(0xBF6, pri);
		write_csr(0xBF7, 0x0);
        SAL_BIT_CLEAR(g_ext_interrupt_en_map,idx);
        g_ext_interrupt_handlers[cur_idx_old_pri] = g_ext_interrupt_handlers[pri];
        g_ext_interrupt_handlers[pri] = drv_intr_mext_default_handler;
    }
    if (SAL_IS_BIT_SET(g_ext_interrupt_en_map, old_idx))
    {
        write_csr(0xBF6, cur_idx_old_pri);
		write_csr(0xBF7, 0x12);
    }
    else {
        write_csr(0xBF6, cur_idx_old_pri);
		write_csr(0xBF7, 0x0);
    }
    sync_exit_critical();
}

void drv_intr_mext_default_handler(u32_t pri)
{
#if 0
    u32_t idx = SAL_FIELD_GET2(g_ext_interrupt_pri_idx_map,pri<<2,3);

    drv_uart_putstr("mext default handler: ");
    drv_uart_puthex32(idx);
    drv_uart_putstr(" pri=");
    drv_uart_puthex32(arg);
    drv_uart_putchar(UART_LF);
#endif
}

void drv_intr_ext_handler_arg_set(int idx, bool_t state, int pri, v_func_ptr_t isr_func, u32_t arg)
{
    if (idx >= IPIC_CFG_INTR_VEC_NUM)
        return;

    sync_enter_critical();

    u32_t old_idx = SAL_FIELD_GET2(g_ext_interrupt_pri_idx_map,pri<<2,3);
    u32_t cur_idx_old_pri = SAL_FIELD_GET2(g_ext_interrupt_idx_pri_map,idx<<2,3);

    g_ext_interrupt_handler_args[cur_idx_old_pri] = g_ext_interrupt_handler_args[pri];
    g_ext_interrupt_handler_args[pri] = arg;
    // Update idx to priority reg
    SAL_FIELD_SET2(g_ext_interrupt_pri_idx_map,cur_idx_old_pri<<2,3,old_idx);
    SAL_FIELD_SET2(g_ext_interrupt_pri_idx_map,pri<<2,3,idx);
    DRV_REG_SET(MCU_INT_INDEX_REG,g_ext_interrupt_pri_idx_map);
    // Update priority for new idx to local memory
    SAL_FIELD_SET2(g_ext_interrupt_idx_pri_map,old_idx<<2,3,cur_idx_old_pri);
    SAL_FIELD_SET2(g_ext_interrupt_idx_pri_map,idx<<2,3,pri);

    if (state)
    {
    	write_csr(0xBF6, pri);
		write_csr(0xBF7, 0x12);
        SAL_BIT_SET(g_ext_interrupt_en_map,idx);
        g_ext_interrupt_handlers[cur_idx_old_pri] = g_ext_interrupt_handlers[pri];
        g_ext_interrupt_handlers[pri] = isr_func;
    }
    else {
    	write_csr(0xBF6, pri);
		write_csr(0xBF7, 0x0);
        SAL_BIT_CLEAR(g_ext_interrupt_en_map,idx);
        g_ext_interrupt_handlers[cur_idx_old_pri] = g_ext_interrupt_handlers[pri];
        g_ext_interrupt_handlers[pri] = drv_intr_mext_default_handler;
    }
    if (SAL_IS_BIT_SET(g_ext_interrupt_en_map, old_idx))
    {
        write_csr(0xBF6, cur_idx_old_pri);
		write_csr(0xBF7, 0x12);
    }
    else {
        write_csr(0xBF6, cur_idx_old_pri);
		write_csr(0xBF7, 0x0);
    }
    sync_exit_critical();
}
