/**
 * @file hv_vos_irq.c
 * @brief vos irq source file.
 * @details This file provides the following functions: \n
 *          (1) Initialization and de-initialization functions \n
 *
 * @author HiView SoC Software Team
 * @version 1.0.0
 * @date 2022-08-25
 * @copyright Copyright(c),2022-8, Hiview Software. All rights reserved.
 * @par History:
 * <table>
 * <tr><th>Author                        <th>Date         <th>Change Description
 * <tr><td>HiView SoC Software Team      <td>2022-08-25   <td>init
 * </table>
*/

#include "hv_vos_Irq.h"
#include "hv_vos_Log.h"
#include "hv_vos_Conn.h"
#include "hv_vos_Assert.h"
#include "hv_vos_Memory.h"
#include "hv_chip_Config.h"
#include "hv_comm_Define.h"
#include "hv_vos_Task.h"
#include <mips/cpu.h>

#define HV_RISC_GIC_OFFSET          0
#define HV_VOS_IRQ_MAX_COUNT       (8 + 64)    /* 8 internal interrupt + 64 external interrupt */
#define HV_VOS_MISC_INT_MAX_COUNT   32  /* 32 external interrupt*/


typedef struct _VosIRQVector {
    HV_VOS_ISR_HANDLER isrHandler;
    void *isrArg;
} VosIRQVector;
typedef enum _VosMiscINTTrig {
    HV_VOS_MISC_INT_TRIG_COND_HIGH_LEVEL,
    HV_VOS_MISC_INT_TRIG_COND_LOW_LEVEL,
    HV_VOS_MISC_INT_TRIG_COND_POSITIVE_EDGE,
    HV_VOS_MISC_INT_TRIG_COND_NEGATIVE_EDGE,
    HV_VOS_MISC_INT_TRIG_COND_DOUBLE_EDGE,
} VosMiscINTTrig;

typedef enum _VosMiscINTBypass {
    HV_VOS_MISC_INT_NO_BYPASS,
    HV_VOS_MISC_INT_BYPASS,
} VosMiscINTBypass;

typedef struct _VosMiscINTDB {
    USHORT16                          int_num;
    VosMiscINTBypass        bypass;
    VosMiscINTTrig     trig_cond;
} VosMiscINTDB;

static struct VosIRQData {
    VosIRQVector irqVec[HV_VOS_IRQ_MAX_COUNT];
} s_stIRQData;

UINT32 g_uiSavedStatusRegister = 0;

void Hv_Vos_UnmaskGIC(void)
{
    mips_setsr(g_uiSavedStatusRegister);
    return;
}
void Hv_Vos_MaskGIC(void)
{
    __asm__ __volatile__( "di" );
    __asm__ __volatile__( "ehb" );

    g_uiSavedStatusRegister = mips_getsr() | 0x01;
    mips_setsr( ( g_uiSavedStatusRegister & ~( 0x0000ff00 ) ) );

    return;
}

void Hv_Vos_Int_Handler(void)
{
    VosIRQVector *vec;
    UCHAR8 ucIrqNum = 0;
    UINT32 uiIrq    = 0;
    UINT32 uiStatus = HV_R32(CPU_SYS_GIC0_IRQ_STATUS_L + HV_RISC_GIC_OFFSET);

    Hv_Vos_MaskGIC();

    while(uiStatus)
    {
        ucIrqNum = __builtin_ffs(uiStatus) - 1;
        uiStatus &= (~(1 << ucIrqNum));
        uiIrq = ucIrqNum + HV_CHIP_IRQ_BASE;
        vec = &s_stIRQData.irqVec[uiIrq];
        if (!vec->isrHandler)
        {
            HV_LOGW("Unhandled interrupt:%d\n", uiIrq);
        }
        else
        {
            (vec->isrHandler)(uiIrq, vec->isrArg);
        }
    }
    uiStatus = HV_R32(CPU_SYS_GIC0_IRQ_STATUS_H + HV_RISC_GIC_OFFSET);
    while(uiStatus)
    {
        ucIrqNum = __builtin_ffs(uiStatus) - 1;
        uiStatus &= (~(1 << ucIrqNum));
        ucIrqNum += 32;
        uiIrq = ucIrqNum + HV_CHIP_IRQ_BASE;
        vec = &s_stIRQData.irqVec[uiIrq];
        if (!vec->isrHandler)
        {
            HV_LOGW("Unhandled interrupt:%d\n", uiIrq);
        }
        else
        {
            (vec->isrHandler)(uiIrq, vec->isrArg);
        }
    }

    Hv_Vos_UnmaskGIC();

}

void _risc_interrup_handler(int irq)
{
    VosIRQVector *vec;
    HV_ASSERT(irq < HV_VOS_IRQ_MAX_COUNT);

    if (irq == HV_CHIP_IRQ_GLOBAL)
    {
        Hv_Vos_Int_Handler();
    }
    else
    {
        vec = &s_stIRQData.irqVec[irq];
        if (!vec->isrHandler)
        {
            HV_LOGW("Unhandled interrupt:%d\n", irq);
        }
        else
        {
            (vec->isrHandler)(irq, vec->isrArg);
        }
    }
}



#ifdef HV_CONFIG_CHIP_HT7315
#define HV_VOS_MISC_INT_ARRAY_COUNT   32
static const VosMiscINTDB misc_int_db[HV_VOS_MISC_INT_ARRAY_COUNT] =
{
    {0,  HV_VOS_MISC_INT_NO_BYPASS, HV_VOS_MISC_INT_TRIG_COND_HIGH_LEVEL},
    {1,  HV_VOS_MISC_INT_NO_BYPASS, HV_VOS_MISC_INT_TRIG_COND_HIGH_LEVEL},
    {2,  HV_VOS_MISC_INT_NO_BYPASS, HV_VOS_MISC_INT_TRIG_COND_HIGH_LEVEL},
    {3,  HV_VOS_MISC_INT_NO_BYPASS, HV_VOS_MISC_INT_TRIG_COND_HIGH_LEVEL},
    {4,  HV_VOS_MISC_INT_NO_BYPASS, HV_VOS_MISC_INT_TRIG_COND_HIGH_LEVEL},
    {5,  HV_VOS_MISC_INT_NO_BYPASS, HV_VOS_MISC_INT_TRIG_COND_HIGH_LEVEL},
    {6,  HV_VOS_MISC_INT_NO_BYPASS, HV_VOS_MISC_INT_TRIG_COND_HIGH_LEVEL},
    {7,  HV_VOS_MISC_INT_NO_BYPASS, HV_VOS_MISC_INT_TRIG_COND_HIGH_LEVEL},
    {8,  HV_VOS_MISC_INT_NO_BYPASS, HV_VOS_MISC_INT_TRIG_COND_HIGH_LEVEL},
    {9,  HV_VOS_MISC_INT_NO_BYPASS, HV_VOS_MISC_INT_TRIG_COND_HIGH_LEVEL},
    {10, HV_VOS_MISC_INT_NO_BYPASS, HV_VOS_MISC_INT_TRIG_COND_HIGH_LEVEL},
    {11, HV_VOS_MISC_INT_NO_BYPASS, HV_VOS_MISC_INT_TRIG_COND_HIGH_LEVEL},
    {12, HV_VOS_MISC_INT_NO_BYPASS, HV_VOS_MISC_INT_TRIG_COND_HIGH_LEVEL},
    {13, HV_VOS_MISC_INT_NO_BYPASS, HV_VOS_MISC_INT_TRIG_COND_HIGH_LEVEL},
    {14, HV_VOS_MISC_INT_NO_BYPASS, HV_VOS_MISC_INT_TRIG_COND_DOUBLE_EDGE},
    {15, HV_VOS_MISC_INT_NO_BYPASS, HV_VOS_MISC_INT_TRIG_COND_HIGH_LEVEL},
    {16, HV_VOS_MISC_INT_NO_BYPASS, HV_VOS_MISC_INT_TRIG_COND_HIGH_LEVEL},
    {17, HV_VOS_MISC_INT_NO_BYPASS, HV_VOS_MISC_INT_TRIG_COND_HIGH_LEVEL},
    {18, HV_VOS_MISC_INT_NO_BYPASS, HV_VOS_MISC_INT_TRIG_COND_HIGH_LEVEL},
    {19, HV_VOS_MISC_INT_NO_BYPASS, HV_VOS_MISC_INT_TRIG_COND_HIGH_LEVEL},
    {20, HV_VOS_MISC_INT_NO_BYPASS, HV_VOS_MISC_INT_TRIG_COND_HIGH_LEVEL},
    {21, HV_VOS_MISC_INT_NO_BYPASS, HV_VOS_MISC_INT_TRIG_COND_HIGH_LEVEL},
    {22, HV_VOS_MISC_INT_NO_BYPASS, HV_VOS_MISC_INT_TRIG_COND_HIGH_LEVEL},
    {23, HV_VOS_MISC_INT_NO_BYPASS, HV_VOS_MISC_INT_TRIG_COND_DOUBLE_EDGE},
    {24, HV_VOS_MISC_INT_NO_BYPASS, HV_VOS_MISC_INT_TRIG_COND_DOUBLE_EDGE},
    {25, HV_VOS_MISC_INT_NO_BYPASS, HV_VOS_MISC_INT_TRIG_COND_POSITIVE_EDGE},
    {26, HV_VOS_MISC_INT_NO_BYPASS, HV_VOS_MISC_INT_TRIG_COND_POSITIVE_EDGE},
    {27, HV_VOS_MISC_INT_NO_BYPASS, HV_VOS_MISC_INT_TRIG_COND_POSITIVE_EDGE},
    {28, HV_VOS_MISC_INT_NO_BYPASS, HV_VOS_MISC_INT_TRIG_COND_DOUBLE_EDGE},
    {29, HV_VOS_MISC_INT_NO_BYPASS, HV_VOS_MISC_INT_TRIG_COND_DOUBLE_EDGE},
    {30, HV_VOS_MISC_INT_NO_BYPASS, HV_VOS_MISC_INT_TRIG_COND_DOUBLE_EDGE},
    {31, HV_VOS_MISC_INT_NO_BYPASS, HV_VOS_MISC_INT_TRIG_COND_DOUBLE_EDGE}
};
#endif

void Hv_Vos_InterruptRecord(UINT32 dwIntFlag,UINT32 dwAddr)
{
    UINT32 dwLoop = 0;
    UINT32 dwValue = 0;
    for(dwLoop = 0;dwLoop < 32;dwLoop++)
    {
        if((dwIntFlag >> dwLoop) & 0x1)
        {
            dwValue = HV_R32(dwAddr + dwLoop * 4);
            dwValue++;
            HV_W32((dwAddr + dwLoop * 4),dwValue);
        }
    }
}

static void InitMiscint(void)
{
    USHORT16 i = 0;
    UINT32 miscIntBypassVal = 0;
    UINT32 miscIntTrigLevelHVal = 0;
    UINT32 miscIntTrigLevelLVal = 0;
    UINT32 miscIntTrigEdgePVal = 0;
    UINT32 miscIntTrigEdgeNVal = 0;
    UINT32 miscIntBypassReg = SYS_REG_SH5_TOP0_MISC_INT_0;
    UINT32 miscIntTrigLevelHReg = SYS_REG_SH5_TOP0_MISC_INT_2;
    UINT32 miscIntTrigLevelLReg = SYS_REG_SH5_TOP0_MISC_INT_3;
    UINT32 miscIntTrigEdgePReg = SYS_REG_SH5_TOP0_MISC_INT_4;
    UINT32 miscIntTrigEdgeNReg = SYS_REG_SH5_TOP0_MISC_INT_5;
    for(i = 0 ; i < HV_VOS_MISC_INT_ARRAY_COUNT ; i++)
    {
        if(misc_int_db[i].bypass == HV_VOS_MISC_INT_BYPASS)
        {
            miscIntBypassVal |= (1 << i);
        }
        if(misc_int_db[i].trig_cond == HV_VOS_MISC_INT_TRIG_COND_HIGH_LEVEL)
        {
            miscIntTrigLevelHVal |= (1 << i);
        }
        else if(misc_int_db[i].trig_cond == HV_VOS_MISC_INT_TRIG_COND_LOW_LEVEL)
        {
            miscIntTrigLevelLVal |= (1 << i);
        }
        else if(misc_int_db[i].trig_cond == HV_VOS_MISC_INT_TRIG_COND_POSITIVE_EDGE)
        {
            miscIntTrigEdgePVal |= (1 << i);
        }
        else if(misc_int_db[i].trig_cond == HV_VOS_MISC_INT_TRIG_COND_NEGATIVE_EDGE)
        {
            miscIntTrigEdgeNVal |= (1 << i);
        }
        else if(misc_int_db[i].trig_cond == HV_VOS_MISC_INT_TRIG_COND_DOUBLE_EDGE)
        {
            miscIntTrigEdgePVal |= (1 << i);
            miscIntTrigEdgeNVal |= (1 << i);
        }
    }
    HV_W32(miscIntBypassReg, miscIntBypassVal);
    HV_W32(miscIntTrigLevelHReg, miscIntTrigLevelHVal);
    HV_W32(miscIntTrigLevelLReg, miscIntTrigLevelLVal);
    HV_W32(miscIntTrigEdgePReg, miscIntTrigEdgePVal);
    HV_W32(miscIntTrigEdgeNReg, miscIntTrigEdgeNVal);
}

void Hv_Vos_WClrMiscInt(UINT32 irqNum)
{
    HV_ASSERT(irqNum < HV_VOS_MISC_INT_MAX_COUNT);
    UINT32 mask = 0;
    UINT32 miscIntWClrReg = 0;
    miscIntWClrReg = SYS_REG_SH5_TOP0_MISC_INT_1;
    mask = 1 << irqNum;
    Hv_Vos_TaskEnterCritical();
    HV_W32_MASK(miscIntWClrReg, mask, mask);
    HV_W32_MASK(miscIntWClrReg, mask, 0);
    Hv_Vos_TaskExitCritical();
}

static void EnableMiscInt(UINT32 irqNum)
{
    HV_ASSERT(irqNum < HV_VOS_MISC_INT_MAX_COUNT);
    UINT32 mask = 0;
    UINT32 miscIntEnableReg = 0;
    miscIntEnableReg = SYS_REG_SH5_TOP0_48;
    mask = (UINT32)1 << irqNum;
    HV_W32_MASK(miscIntEnableReg, mask, mask);
}

static void DisableMiscInt(UINT32 irqNum)
{
    HV_ASSERT(irqNum < HV_VOS_MISC_INT_MAX_COUNT);
    UINT32 mask = 0;
    UINT32 miscIntDisableReg = 0;
    miscIntDisableReg = SYS_REG_SH5_TOP0_48;
    mask = (UINT32)1 << irqNum;
    HV_W32_MASK(miscIntDisableReg, mask, 0);
}

void Hv_Vos_InitIrq(void)
{
    extern void *_ebase_ram;

    // disable all interrupts
    mips_bicsr(SR_IMASK);

    // set vector spacing as 32
    mips32_setintctl((mips32_getintctl() & ~INTCTL_VS ) | INTCTL_VS_32);

    // use general exception vector(0x180)
    mips_biscr(CR_IV);

    // disable BEV mode, enable IV mode, relocate to ebase as base IRQ address
    mips_bicsr(SR_BEV);

    // set ebase address
    mips32_setebase(&_ebase_ram);

    // enable interrupt
    mips_bissr(SR_IE);

    HV_W32(CPU_SYS_GIC0_IRQ_INTEN_L + HV_RISC_GIC_OFFSET, 0);

    HV_W32(CPU_SYS_GIC0_IRQ_INTEN_H + HV_RISC_GIC_OFFSET, 0);

    // enable global external interrupt
    Hv_Vos_UnmaskIrq(HV_CHIP_IRQ_GLOBAL);
    // init misc_int interrupt controller.
    InitMiscint();
}

void Hv_Vos_ConfigIrq(UINT32 irqNum, BOOL enable, UINT32 channel)
{
    HV_UNUSED(irqNum);
    HV_UNUSED(enable);
    HV_UNUSED(channel);
}

void Hv_Vos_ClearPendingIrq(INT32 IRQn)
{
    return;
}

void Hv_Vos_MaskIrq(UINT32 irqNum)
{
    if (irqNum < HV_CHIP_IRQ_BASE)
    {
        mips_bicsr(SR_IM0 << irqNum);
    }
    else 
    {
        UINT32 mask = 0;
        UINT32 gicEnableReg = 0;
        irqNum -= HV_CHIP_IRQ_BASE;
        if (irqNum < 32) 
        {
            gicEnableReg = CPU_SYS_GIC0_IRQ_INTEN_L + HV_RISC_GIC_OFFSET;
            mask = (UINT32)1 << irqNum;
        } 
        else 
        {
            gicEnableReg = CPU_SYS_GIC0_IRQ_INTEN_H + HV_RISC_GIC_OFFSET;
            mask = 1 << (irqNum - 32);
        }
        Hv_Vos_TaskEnterCritical();
        HV_W32_MASK(gicEnableReg, mask, 0);
        // Disable misc_int interrupt
        if (irqNum < 32)
        {
            DisableMiscInt(irqNum);
        }
        Hv_Vos_TaskExitCritical();
    }
}
void Hv_Vos_UnmaskIrq(UINT32 irqNum)
{
    if (irqNum < HV_CHIP_IRQ_BASE)
    {
        mips_bissr(SR_IM0 << irqNum);
    }
    else
    {
        UINT32 mask = 0;
        UINT32 gicEnableReg = 0;
        irqNum -= HV_CHIP_IRQ_BASE;
        if (irqNum < 32)
        {
            gicEnableReg = CPU_SYS_GIC0_IRQ_INTEN_L + HV_RISC_GIC_OFFSET;
            mask = (UINT32)1 << irqNum;
        }
        else
        {
            gicEnableReg = CPU_SYS_GIC0_IRQ_INTEN_H + HV_RISC_GIC_OFFSET;
            mask = 1 << (irqNum - 32);
        }
        Hv_Vos_TaskEnterCritical();
        HV_W32_MASK(gicEnableReg, mask, mask);
        // Enable misc_int interrupt
        if (irqNum < 32)
        {
            EnableMiscInt(irqNum);
        }
        Hv_Vos_TaskExitCritical();
    }
}

void Hv_Vos_CleanupIrq(void)
{
    mips_bicsr(SR_IE);
}

HV_VOS_IRQ_T Hv_Vos_GetIrqMask(void)
{
    return 0;
}

BOOL Hv_Vos_TriggerIrq(UINT32 irqNum)
{
    return HV_TRUE;
}

void Hv_Vos_DisableIrq(HV_VOS_IRQ_T *mask)
{
    mips_bicsr(SR_IE);
}

void Hv_Vos_RestoreIrq(const HV_VOS_IRQ_T *mask)
{
    mips_bissr(SR_IE);
}

void Hv_Vos_AttachIsr(UINT32 irqNum, HV_VOS_ISR_HANDLER handler, void *arg)
{
    HV_VOS_IRQ_T mask = 0;
    VosIRQVector *irqVec;
    HV_ASSERT(irqNum < HV_VOS_IRQ_MAX_COUNT);
    //HV_LOGW("Hv_Vos_AttachIsr %d\n", irqNum);
    irqVec = &s_stIRQData.irqVec[irqNum];
    HV_ASSERT(irqVec->isrArg == NULL && irqVec->isrHandler == NULL);
    Hv_Vos_DisableIrq(&mask);
    irqVec->isrHandler = handler;
    irqVec->isrArg = arg;
    Hv_Vos_RestoreIrq(&mask);

}

void Hv_Vos_DetachIsr(UINT32 irqNum, HV_VOS_ISR_HANDLER handler)
{
    HV_VOS_IRQ_T mask = 0;
    VosIRQVector *irqVec;

    HV_ASSERT(irqNum < HV_VOS_IRQ_MAX_COUNT);
    //HV_LOGI("Hv_Vos_DetachIsr %d\n", irqNum);
    irqVec = &s_stIRQData.irqVec[irqNum];
    HV_ASSERT(irqVec->isrHandler == handler);
    Hv_Vos_DisableIrq(&mask);
    irqVec->isrHandler = NULL;
    irqVec->isrArg = NULL;
    Hv_Vos_RestoreIrq(&mask);
}
void Hv_Vos_ISR_Detach(UINT32 irqNum, HV_VOS_ISR_HANDLER handler)
{
    HV_VOS_IRQ_T mask = 0;
    VosIRQVector *irqVec;

    HV_ASSERT(irqNum < HV_VOS_IRQ_MAX_COUNT);
    //HV_LOGI("Hv_Vos_DetachIsr %d\n", irqNum);
    irqVec = &s_stIRQData.irqVec[irqNum];
    HV_ASSERT(irqVec->isrHandler == handler);
    Hv_Vos_DisableIrq(&mask);
    irqVec->isrHandler = NULL;
    irqVec->isrArg = NULL;
    Hv_Vos_RestoreIrq(&mask);
}

void Hv_Vos_SetIrqPrio(UINT32 IRQn, UINT32 priority)
{
    // do sth here.
    return;
}
