/*
 * Copyright (c) Hisilicon Technologies Co., Ltd. 2019-2020. All rights reserved.
 * Description: mailbox driver in liteos
 */
#include "drv_mbx.h"
#include "mbx_common.h"
#include "los_mbx.h"

#include "los_printf.h"
#include "los_base.h"
#include "los_memory.h"
#include "los_mux.h"

#define SC_GEN18 (void *)(td_uintptr_t)0x841448

static td_bool is_v2_chipset(td_void)
{
    if (MBX_READL(SC_GEN18) & (0x1 << 13)) { /* if SC_GEN18[13]==1 then V2(CS) chipset */
        return TD_TRUE;
    }

    return TD_FALSE;
}

td_s32 drv_mbx_open(td_u32 session_id)
{
    return mbx_open(session_id, MBX_RX_BUFF_SIZE, MBX_TX_BUFF_SIZE);
}

td_s32 drv_mbx_close(td_u32 handle)
{
    return mbx_close(handle);
}

td_s32 drv_mbx_register_irq_callback(td_u32 handle, session_callback func, const td_void * const data)
{
    return mbx_register_irq_callback(handle, func, data);
}

td_s32 drv_mbx_rx(td_u32 handle, td_u8 *msg, td_u32 msg_len, const td_u32 * const rx_len, td_u32 timeout)
{
    return mbx_rx(handle, msg, msg_len, (td_u32 *)rx_len, timeout);
}

td_s32 drv_mbx_tx(td_u32 handle, const td_u8 *msg, td_u32 msg_len, const td_u32 * const tx_len,
    td_u32 timeout)
{
    return mbx_tx(handle, msg, msg_len, (td_u32 *)tx_len, timeout);
}


td_void mbx_polling_rx(td_void)
{
#ifndef SUPPORT_MBX_INTERRUPT
    struct mailbox *mailbox = NULL;

    mailbox = get_mailbox_data();
    if (mailbox == NULL) {
        return;
    }
    mbx_rx_msg((td_void *)mailbox->acpu_vmcu0.rx_head_addr);
    mbx_rx_msg((td_void *)mailbox->tcpu_vmcu0.rx_head_addr);
#endif
    return;
}
static void init_acpu_reg(struct session * const session, const struct mailbox *mailbox)
{
    if (session == NULL || mailbox == NULL) {
        return;
    }
    if (mailbox->acpu_vmcu0.base_addr == NULL) {
        return;
    }
    session->tx_reg = (struct reg *)MBX_MALLOC(sizeof(struct reg));
    if (session->tx_reg == NULL) {
        return;
    }
    if (is_v2_chipset() == TD_TRUE) {
        session->tx_reg->argv_size = VMCU_TO_ACPU_ARGS_V2_NUM;
        session->tx_reg->argv = mailbox->acpu_vmcu0.base_addr + VMCU_TO_ACPU_ARGS_V2_OFFSET;
    } else {
        session->tx_reg->argv_size = VMCU_TO_ACPU_ARGS_NUM;
        session->tx_reg->argv = mailbox->acpu_vmcu0.base_addr + VMCU_TO_ACPU_ARGS_OFFSET;
    }
#ifdef CHIP_TYPE_RESERVED23
    session->tx_reg->argv_size = VMCU_TO_ACPU_ARGS_V2_NUM;
    session->tx_reg->argv = mailbox->acpu_vmcu0.base_addr + VMCU_TO_ACPU_ARGS_V2_OFFSET;
#endif
    session->tx_reg->version = mailbox->acpu_vmcu0.base_addr + MAILBOX_VERSION_OFFSET;
    session->tx_reg->head = mailbox->acpu_vmcu0.base_addr + VMCU_TO_ACPU_HEAD_OFFSET;
    session->tx_reg->trigger_rx = mailbox->acpu_vmcu0.base_addr + VMCU_TO_ACPU_SEND_OFFSET;
    session->tx_reg->pending = mailbox->acpu_vmcu0.base_addr + ACPU_INTR_FROM_VMCU_OFFSET;
    session->tx_reg->lock = (td_u32 *)&mailbox->tx_acpu_lock;

    session->rx_reg = (struct reg *)MBX_MALLOC(sizeof(struct reg));
    if (session->rx_reg == NULL) {
        return;
    }
    session->rx_reg->argv_size = ACPU_TO_VMCU_ARGS_NUM;
    session->rx_reg->version = mailbox->acpu_vmcu0.base_addr + MAILBOX_VERSION_OFFSET;
    session->rx_reg->head = mailbox->acpu_vmcu0.base_addr + ACPU_TO_VMCU_HEAD_OFFSET;
    session->rx_reg->argv = mailbox->acpu_vmcu0.base_addr + ACPU_TO_VMCU_ARGS_OFFSET;
    session->rx_reg->trigger_rx = mailbox->acpu_vmcu0.base_addr + ACPU_TO_VMCU_SEND_OFFSET;
    session->rx_reg->pending = mailbox->acpu_vmcu0.base_addr + VMCU_INTR_FROM_ACPU_OFFSET;

    return;
}

static void init_tcpu_reg(struct session * const session, const struct mailbox * const mailbox)
{
    if (session == NULL || mailbox == NULL) {
        return;
    }
    if (mailbox->tcpu_vmcu0.base_addr == NULL) {
        return;
    }
    session->tx_reg = (struct reg *)MBX_MALLOC(sizeof(struct reg));
    if (session->tx_reg == NULL) {
        return;
    }
    session->tx_reg->argv_size = VMCU_TO_TCPU_ARGS_NUM;
    session->tx_reg->version = mailbox->tcpu_vmcu0.base_addr + MAILBOX_VERSION_OFFSET;
    session->tx_reg->head = mailbox->tcpu_vmcu0.base_addr + VMCU_TO_TCPU_HEAD_OFFSET;
    session->tx_reg->argv = mailbox->tcpu_vmcu0.base_addr + VMCU_TO_TCPU_ARGS_OFFSET;
    session->tx_reg->trigger_rx = mailbox->tcpu_vmcu0.base_addr + VMCU_TO_TCPU_SEND_OFFSET;
    session->tx_reg->pending = mailbox->tcpu_vmcu0.base_addr + TCPU_INTR_FROM_VMCU_OFFSET;
    session->tx_reg->lock = (td_u32 *)&mailbox->tx_tcpu_lock;

    session->rx_reg = (struct reg *)MBX_MALLOC(sizeof(struct reg));
    if (session->rx_reg == NULL) {
        return;
    }
    session->rx_reg->argv_size = TCPU_TO_VMCU_ARGS_NUM;
    session->rx_reg->version = mailbox->tcpu_vmcu0.base_addr + MAILBOX_VERSION_OFFSET;
    session->rx_reg->head = mailbox->tcpu_vmcu0.base_addr + TCPU_TO_VMCU_HEAD_OFFSET;
    session->rx_reg->argv = mailbox->tcpu_vmcu0.base_addr + TCPU_TO_VMCU_ARGS_OFFSET;
    session->rx_reg->trigger_rx = mailbox->tcpu_vmcu0.base_addr + TCPU_TO_VMCU_SEND_OFFSET;
    session->rx_reg->pending = mailbox->tcpu_vmcu0.base_addr + VMCU_INTR_FROM_TCPU_OFFSET;

    return;
}

static void init_dmcu0_reg(struct session *session, const struct mailbox *mailbox)
{
    if (session == NULL || mailbox == NULL) {
        return;
    }
    if (mailbox->acpu_dmcu0.base_addr == NULL) {
        return;
    }
    session->tx_reg = (struct reg *)MBX_MALLOC(sizeof(struct reg));
    if (session->tx_reg == NULL) {
        return;
    }
    session->tx_reg->argv_size = DMCU_TO_ACPU_ARGS_V2_NUM;
    session->tx_reg->argv = mailbox->acpu_dmcu0.base_addr + DMCU_TO_ACPU_ARGS_V2_OFFSET;
    session->tx_reg->version = mailbox->acpu_dmcu0.base_addr + MAILBOX_VERSION_OFFSET;
    session->tx_reg->head = mailbox->acpu_dmcu0.base_addr + DMCU_TO_ACPU_HEAD_OFFSET;
    session->tx_reg->trigger_rx = mailbox->acpu_dmcu0.base_addr + DMCU_TO_ACPU_SEND_OFFSET;
    session->tx_reg->pending = mailbox->acpu_dmcu0.base_addr + ACPU_INTR_FROM_DMCU_OFFSET;
    session->tx_reg->lock = (td_u32 *)&mailbox->tx_dmcu0_lock;

    session->rx_reg = (struct reg *)MBX_MALLOC(sizeof(struct reg));
    if (session->rx_reg == NULL) {
        MBX_FREE(session->tx_reg);
        return;
    }
    session->rx_reg->argv_size = ACPU_TO_DMCU_ARGS_NUM;
    session->rx_reg->version = mailbox->acpu_dmcu0.base_addr + MAILBOX_VERSION_OFFSET;
    session->rx_reg->head = mailbox->acpu_dmcu0.base_addr + ACPU_TO_DMCU_HEAD_OFFSET;
    session->rx_reg->argv = mailbox->acpu_dmcu0.base_addr + ACPU_TO_DMCU_ARGS_OFFSET;
    session->rx_reg->trigger_rx = mailbox->acpu_dmcu0.base_addr + ACPU_TO_DMCU_SEND_OFFSET;
    session->rx_reg->pending = mailbox->acpu_dmcu0.base_addr + DMCU_INTR_FROM_ACPU_OFFSET;

    return;
}

td_void init_mailbox_reg(struct session *session, td_u32 session_id, const struct mailbox *mailbox)
{
    td_u32 local_side, remote_side;

    if (mailbox == NULL) {
        return;
    }

    local_side = mailbox->local_cpu;
    remote_side = SESSION_ID_SIDE0(session_id) == local_side ? \
        SESSION_ID_SIDE1(session_id) : SESSION_ID_SIDE0(session_id);

    if (local_side != SESSION_ID_SIDE0(session_id) && \
            local_side != SESSION_ID_SIDE1(session_id)) {
        return;
    }
    if (session == NULL) {
        return;
    }
    switch (local_side) {
        case VMCU0:
            if (remote_side == ACPU) {
                init_acpu_reg(session, mailbox);
            } else if (remote_side == TCPU) {
                init_tcpu_reg(session, mailbox);
            } else {
                session->tx_reg = NULL;
                session->rx_reg = NULL;
            }
            break;
        case DMCU0:
            if (remote_side == ACPU) {
                init_dmcu0_reg(session, mailbox);
            } else {
                session->tx_reg = NULL;
                session->rx_reg = NULL;
            }
            break;
        default:
            session->tx_reg = NULL;
            session->rx_reg = NULL;
            break;
    }

    return;
}

#ifdef SUPPORT_MBX_INTERRUPT
#ifdef CONFIG_DRIVERS_MBX_DMCU
static MBX_IRQ_RET mailbox_dmcu0_irq_handler(int irq, void *dev_id)
{
    int ret;
    struct mailbox *mailbox;

    mailbox = get_mailbox_data();
    if (mailbox == NULL || mailbox->acpu_dmcu0.rx_head_addr == NULL) {
        return MBX_IRQ_HANDLED;
    }
    ret = mbx_rx_msg((void *)mailbox->acpu_dmcu0.rx_head_addr);
    if (ret != SOC_MBX_SUCCESS) {
        MBX_WRITEL(0x00, mailbox->acpu_dmcu0.base_addr + DMCU_INTR_FROM_ACPU_OFFSET);
        MBX_ERR_PRINT("mbx_rx_msg dmcu0 error and ret:0x%x\n", ret);
    }

    return MBX_IRQ_HANDLED;
}
#else
static MBX_IRQ_RET mailbox_acpu_irq_handler(td_void *dev_id)
{
    td_s32 ret;
    struct mailbox *mailbox;

    mailbox = get_mailbox_data();
    if (mailbox == NULL || (mailbox->acpu_vmcu0.rx_head_addr == NULL) || mailbox->acpu_vmcu0.base_addr == NULL) {
        return MBX_IRQ_HANDLED;
    }
    ret = mbx_rx_msg((td_void *)mailbox->acpu_vmcu0.rx_head_addr);
    if (ret != SOC_MBX_SUCCESS) {
        MBX_WRITEL(0x00, mailbox->acpu_vmcu0.base_addr + VMCU_INTR_FROM_ACPU_OFFSET);
        MBX_ERR_PRINT("mbx_rx_msg error from acpu and ret:0x%x\n", ret);
    }

    return MBX_IRQ_HANDLED;
}

static MBX_IRQ_RET mailbox_tcpu_irq_handler(td_void *dev_id)
{
    td_s32 ret;
    struct mailbox *mailbox;

    mailbox = get_mailbox_data();
    if ((mailbox == NULL) || (mailbox->tcpu_vmcu0.rx_head_addr == NULL) || (mailbox->tcpu_vmcu0.base_addr == NULL)) {
        return MBX_IRQ_HANDLED;
    }
    ret = mbx_rx_msg((td_void *)mailbox->tcpu_vmcu0.rx_head_addr);
    if (ret != SOC_MBX_SUCCESS) {
        MBX_WRITEL(0x00, mailbox->tcpu_vmcu0.base_addr + VMCU_INTR_FROM_TCPU_OFFSET);
        MBX_ERR_PRINT("mbx_rx_msg error from tcpu and ret:0x%x\n", ret);
    }

    return MBX_IRQ_HANDLED;
}
#endif
#endif

td_s32 drv_mbx_init(enum cpu_id local_cpu)
{
    struct mailbox *mailbox;
#ifdef SUPPORT_MBX_INTERRUPT
    td_s32 ret;
#endif

    mailbox = get_mailbox_data();
    if ((mailbox == NULL) || (local_cpu >= CPU_MAX) || (mailbox->initalized != TD_FALSE)) {
        return SOC_ERR_MAILBOX_NOT_SUPPORT;
    }
    MBX_INIT_LIST_HEAD(&mailbox->list_head);
    mailbox->local_cpu = local_cpu;
    MBX_MUTEX_INIT(&mailbox->list_lock);
#ifdef CONFIG_DRIVERS_MBX_DMCU
    MBX_MUTEX_INIT(&mailbox->tx_dmcu0_lock);
    mailbox->acpu_dmcu0.base_addr = (td_u32 *)(MBX_ACPU_DMCU0_BASE_ADDR);
    mailbox->acpu_dmcu0.rx_head_addr = mailbox->acpu_dmcu0.base_addr + ACPU_TO_DMCU_HEAD_OFFSET;
    mailbox->initalized = TRUE;
#ifdef SUPPORT_MBX_INTERRUPT
    ret = (td_s32)LOS_HwiCreate(MBX_IRQ_ACPU2DMCU, 0x01, 0, (HWI_PROC_FUNC)mailbox_dmcu0_irq_handler, 0);
    if (ret != 0) {
        MBX_ERR_PRINT("Request MBX_IRQ_ACPU2DMCU IRQ failed\n");
        return SOC_ERR_MAILBOX_NOT_INIT;
    }
    LOS_HwiEnable(MBX_IRQ_ACPU2DMCU);
#endif
#else
    MBX_MUTEX_INIT(&mailbox->tx_acpu_lock);
    MBX_MUTEX_INIT(&mailbox->tx_tcpu_lock);
    mailbox->acpu_vmcu0.base_addr = (td_u32 *)MBX_VMCU0_BASE_ADDR;
    mailbox->acpu_vmcu0.rx_head_addr = mailbox->acpu_vmcu0.base_addr + ACPU_TO_VMCU_HEAD_OFFSET;
    mailbox->tcpu_vmcu0.base_addr = (td_u32 *)MBX_VMCU0_BASE_ADDR;
    mailbox->tcpu_vmcu0.rx_head_addr = mailbox->acpu_vmcu0.base_addr + TCPU_TO_VMCU_HEAD_OFFSET;
    mailbox->initalized = TRUE;
#ifdef SUPPORT_MBX_INTERRUPT
    ret = (td_s32)LOS_HwiCreate(MBX_IRQ_ACPU2VMCU, 0x01, 0, (HWI_PROC_FUNC)mailbox_acpu_irq_handler, 0);
    if (ret != 0) {
        MBX_ERR_PRINT("Request MBX_IRQ_ACPU2VMCU IRQ failed\n");
        return SOC_ERR_MAILBOX_NOT_INIT;
    }
    LOS_InterruptUnmask(MBX_IRQ_ACPU2VMCU);
    ret = (td_s32)LOS_HwiCreate(MBX_IRQ_TCPU2VMCU, 0x01, 0, (HWI_PROC_FUNC)mailbox_tcpu_irq_handler, 0);
    if (ret != 0) {
        MBX_ERR_PRINT("Request MBX_IRQ_TCPU2VMCU IRQ failed\n");
        return SOC_ERR_MAILBOX_NOT_INIT;
    }
    LOS_InterruptUnmask(MBX_IRQ_TCPU2VMCU);
#endif
#endif

    return SOC_MBX_SUCCESS;
}

td_s32 drv_mbx_deinit(td_void)
{
    struct mailbox *mailbox;

    mailbox = get_mailbox_data();
    if (mailbox == NULL) {
        return SOC_ERR_MAILBOX_NOT_SUPPORT;
    }
    mailbox->initalized = FALSE;
    mailbox->local_cpu = 0;
#ifdef SUPPORT_MBX_INTERRUPT
    LOS_HwiDelete(MBX_IRQ_ACPU2VMCU, 0);
    LOS_HwiDelete(MBX_IRQ_TCPU2VMCU, 0);
#endif

    return SOC_MBX_SUCCESS;
}