/* SPDX-License-Identifier: LGPL-2.1-only OR BSD-3-Clause */
/******************************************************************************
 *
 * This file is provided under a dual license.  When you use or
 * distribute this software, you may choose to be licensed under
 * version 2.1 of the GNU Lesser General Public License
 * ("LGPLv2.1 License") or BSD License.
 *
 * LGPLv2.1 License
 *
 * Copyright(C) 2019 MediaTek Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of version 2.1 of the GNU Lesser General Public
 * License as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html for more
 * details.
 *
 * BSD LICENSE
 *
 * Copyright(C) 2019 MediaTek Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  * Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *  * Neither the name of the copyright holder nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *****************************************************************************/

/*-----------------------------------------------------------------------------
 * Description:
 *---------------------------------------------------------------------------*/

/* driver-OS header files */
#include "x_bim.h"
#define __USE_XBIM_IRQS        // UGLY HACK! Make sure we don't include mach/irqs.h from kernel.

#include "x_linux.h"
#include "driver_os.h"
#include "isr_linux_drv.h"
#include "x_assert.h"
#include "x_printf.h"
#include "x_timeprofile.h"

#include <linux/version.h>
#include <linux/interrupt.h>

#ifndef SA_SHIRQ		/* depricated naming*/
#define SA_SHIRQ		IRQF_SHARED
#define SA_INTERRUPT		IRQF_DISABLED
#endif

//---------------------------------------------------------------------------

#if defined(CC_MT5396)||defined(CC_MT5368)||defined(CC_MT5389)
    #define MAX_VECTOR_ID       72                  // Max vector ID
#else
    #define MAX_VECTOR_ID       MAX_IRQ_VECTOR
#endif

#if defined(CC_MT5396)||defined(CC_MT5368)||defined(CC_MT5399)||defined(CC_MT5398)||defined(CC_MT5880)||defined(CC_MT5890)||defined(CC_MT5882)
    #define SPI_OFFSET          32
#else
    #define SPI_OFFSET          0
#endif

#define ISR_VECTOR_SIZE     (MAX_VECTOR_ID + 1) // Max number of registered vectors

typedef struct
{
    x_os_isr_fct    pf_isr;
    char            ucName[32];
} IRQ_ARRAY_T;

#if defined(CC_MT5396)||defined(CC_MT5368)||defined(CC_MT5389)||defined(CC_MT5399)||defined(CC_MT5398)||defined(CC_MT5880)||defined(CC_MT5860)||defined(CC_MT5881)||defined(CC_MT5890)||defined(CC_MT5882)
IRQ_ARRAY_T rIrqs[ISR_VECTOR_SIZE] =
{
    {NULL, "PDWNC"},
    {NULL, "SERIAL"},
    {NULL, "NAND"},
    {NULL, "T0"},
    {NULL, "T1"},
    {NULL, "T2"},
    {NULL, "SMARTCARD"},
    {NULL, "WDT"},
    {NULL, "ENET"},
    {NULL, "MJC"},
    {NULL, "PSCAN"},
    {NULL, "USB1"},
    {NULL, "IMGRZ"},
    {NULL, "VENC"},
    {NULL, "SPDIF_PSR"},
    {NULL, "USB0"},
    {NULL, "AUDIO"},
    {NULL, "RS232"},
    {NULL, "LED"},
    {NULL, "OSD"},
    {NULL, "VDOIN"},
    {NULL, "BLK2RS"},
    {NULL, "FLASH"},
    {NULL, "POST_PROC"},
    {NULL, "VDEC"},
    {NULL, "GFX"},
    {NULL, "DEMUX"},
    {NULL, "DEMOD"},
    {NULL, "FCI"},
    {NULL, "APWM"},
    {NULL, "MISC2"},
    {NULL, "MISC"},
    {NULL, "DRAMC"},
    {NULL, "EXT1"},
    {NULL, "POD"},
#if defined(CC_MT5389)||defined(CC_MT5396)
    {NULL, "PMU1"},
    {NULL, "PMU0"},
#elif defined(CC_MT5399)||defined(CC_MT5398)||defined(CC_MT5880)||defined(CC_MT5881)||defined(CC_MT5890)||defined(CC_MT5882)
    {NULL, "PMU1"},
    {NULL, "PMU0"},
#else
#error PMU IRQ array not set.
#endif
    {NULL, "DRAMC_CHB"},
    {NULL, "TVE"},
    {NULL, "PCIE"},
    {NULL, "APWM2"},
    {NULL, "SPI"},
    {NULL, "JPGDEC"},
    {NULL, "P"},
    {NULL, "AES"},
#if defined(CC_MT5399)||defined(CC_MT5890)
    {NULL, "TCON_EH"},
    {NULL, "TCON_TCH"},
#else
    {NULL, "TCON_TCH"},
    {NULL, "TCON_EH"},
#endif
    {NULL, "OD"},
    {NULL, "USB2"},
    {NULL, "USB3"},
    {NULL, "GDMA"},
    {NULL, "EPHY"},
    {NULL, "TCPIP"},
    {NULL, "GFX3D_GP"},
    {NULL, "GFX3D_PP"},
    {NULL, "GFX3D_PMU"},
    {NULL, "GFX3D_GPMMU"},
    {NULL, "GFX3D_PPMMU"},
    {NULL, "MMU_GFX"},
    {NULL, "MMU_GCPU"},
    {NULL, "MMU_IMGRZ"},
    {NULL, "MMU_JPG"},
    {NULL, "MMU_GDMA"},
    {NULL, "DDI"},
    {NULL, "LZHS"},
    {NULL, "L2C"},
    {NULL, "IMGRZ2"},
    {NULL, "MMU_IMGRZ2"},
    {NULL, "MSDC2"},
    {NULL, "PNG1"},
    {NULL, "PNG2"},
    {NULL, "PNG3"},
    {NULL, "MMU_PNG1"},
   #if defined(CC_MT5399)||defined(CC_MT5890)||defined(CC_MT5882)
    {NULL, "SMCARD1"},
    {NULL, "TDDC"},
    {NULL, "APWM3"},
    {NULL, "MMU_VDEC"},
    {NULL, "MMU_DDI"},
    {NULL, "MMU_DMX"},
    {NULL, "VECTOR_USB3_1"},
    {NULL, "RS232_1"},
    #ifdef CC_MT5399
    {NULL, "APOLLO"},
    #else // for CC_MT5890
    {NULL, "MMU_IMGRZ3"},
    {NULL, "IMGRZ3"},
    {NULL, "ZZ"},
    {NULL, "DRAMC_CHC"},
    {NULL, "VDEC_LAE0"},
    {NULL, "VDEC_LAE1"},
    {NULL, "TRNG"},
    {NULL, "P_B"},
    {NULL, "VDEC_CORE1"},
    {NULL, "ABIM0"},
    {NULL, "ABIM1"},
    {NULL, "ABIM2"},
    {NULL, "ABIM3"},
    {NULL, "CI_FMR"},
    {NULL, "CI_FMR2"},
    {NULL, "PTP_THERM"},
    {NULL, "PTP_FSM"},
    {NULL, "SC_TIMER"},
    {NULL, "HDMIRX_20"},
    {NULL, "UHD"},
    #endif
   #elif defined(CC_MT5398)||defined(CC_MT5880)||defined(CC_MT5860)
    {NULL, "SMCARD1"},
    {NULL, "TDDC"},
    {NULL, "APWM3"},
    {NULL, "ARM9_0"},
    {NULL, "ARM9_1"},
    {NULL, "ARM9_2"},
    {NULL, "ARM9_3"},
   #elif defined(CC_MT5881)
    {NULL, "SMCARD1"},
    {NULL, "TDDC"},
    {NULL, "LZMA"},
    {NULL, "NONE"},
    {NULL, "GFX3D_PP1"},
    {NULL, "GFX3D_PP1MMU"},
    {NULL, "SERDES"},
   #endif
};
#else
#error rIrqs  not defined.
#endif


#if (TIMEPROFILE_LOG == 1)
EXETIME_MEASURE ExeTime[40];
#endif
irqreturn_t _interrupt(int irq, void *dev_id)
{
    irqreturn_t ret;

    irq -= SPI_OFFSET;

    if (irq > MAX_VECTOR_ID)
    {
        return IRQ_NONE;
    }

    if (rIrqs[irq].pf_isr)
    {
#if (TIMEPROFILE_LOG == 1)
    ExeTimeRecordStart(&ExeTime[irq].RawTimeStart);
#endif
        rIrqs[irq].pf_isr((UINT16)irq);
#if (TIMEPROFILE_LOG == 1)
    ExeTimeRecordStop(&ExeTime[irq] );
#endif
        ret = IRQ_HANDLED;
    }
    else
    {
        ret = IRQ_NONE;
    }

    return ret;
}

/*----------------------------------------------------------------------------
 * Function: x_os_drv_reg_isr()
 *
 * Description:
 *      this API registers an ISR with its vector id. it performs
 *      1. check is vector table is established.
 *      2. locate vector table entry from vector id.
 *      3. save the new ISR if the table entry is not occupied or is
 *         occupied by the same vector, otherwise, search forward to
 *         find one until entire table is searched.
 *
 * Inputs:
 *      ui2_vec_id: an vector id to register an ISR.
 *      pf_isr: pointer to a ISR to set.
 *      ppf_old_isr: pointer to hold the current ISR setting.
 *
 * Outputs: -
 *
 * Returns:
 *      OSR_DRV_OK : routine was successful.
 *      OSR_DRV_NOT_INIT: invalid argument.
 *      OSR_DRV_FAIL: OS driver related error.
 *---------------------------------------------------------------------------*/
INT32
x_os_drv_reg_isr(UINT16             ui2_vec_id,
                 x_os_drv_isr_fct   pf_isr,
                 x_os_drv_isr_fct   *ppf_old_isr)
{
    int result;
    INT32 ret = OSR_DRV_OK;
    unsigned long flags;

    if (ui2_vec_id> MAX_VECTOR_ID)
    {
        return OSR_DRV_FAIL;
    }

    // disable IRQ.
    local_irq_save(flags);

#if 0//defined(CONFIG_SMP) && !defined(CONFIG_USE_OF)
    // For SMP, also need to disable specific IRQ to prevent it run from other cores.
    // We can't use x_crit_start(), otherwise it might deadlock when:
    //  1. core 2 running reg_isr with lock held
    //  2. core 1 got IRQ, in ISR it need to enter critical section, wait for core2.
    //  3. core 2 running free_irq, which need to wait for core 1 ISR to finish.
    {
    int irqen = BIM_IsIrqEnabled(ui2_vec_id);
	if (irqen)
        BIM_DisableIrq(ui2_vec_id);
#endif

    *ppf_old_isr = rIrqs[ui2_vec_id].pf_isr;
    rIrqs[ui2_vec_id].pf_isr = pf_isr;

    if (*ppf_old_isr)
    {
        free_irq(ui2_vec_id+SPI_OFFSET, &rIrqs[ui2_vec_id].ucName);
    }

    if (pf_isr != NULL)
    {
        unsigned long irq_flags;

        irq_flags = SA_SHIRQ;

#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0)
        if ((ui2_vec_id == VECTOR_SERIAL) ||
            (ui2_vec_id == VECTOR_GFX) ||
            (ui2_vec_id == VECTOR_PDWNC))
        {
            irq_flags |= IRQF_SAMPLE_RANDOM;
        }
#endif

        result = request_irq(ui2_vec_id+SPI_OFFSET, _interrupt, irq_flags, rIrqs[ui2_vec_id].ucName, &rIrqs[ui2_vec_id].ucName);
        if (result)
        {
            ret = OSR_DRV_FAIL;
            goto out;
        }
    }

out:

#if 0//defined(CONFIG_SMP) && !defined(CONFIG_USE_OF)
    if (irqen)
    	BIM_EnableIrq(ui2_vec_id);
#endif

    local_irq_restore(flags);

    return ret;
}

INT32
x_os_drv_reg_isr_ex(UINT16             ui2_vec_id,
                    x_os_drv_isr_fct   pf_isr,
                    x_os_drv_isr_fct   *ppf_old_isr)
{
    int result;

    if (ui2_vec_id > MAX_VECTOR_ID)
    {
        return OSR_DRV_FAIL;
    }

    *ppf_old_isr = rIrqs[ui2_vec_id].pf_isr;
    rIrqs[ui2_vec_id].pf_isr = pf_isr;

    if (*ppf_old_isr)
    {
        free_irq(ui2_vec_id+SPI_OFFSET, NULL);
    }

    if (pf_isr != NULL)
    {
        result = request_irq(ui2_vec_id+SPI_OFFSET, _interrupt, SA_INTERRUPT, rIrqs[ui2_vec_id].ucName, NULL);
        if (result)
        {
            return OSR_DRV_FAIL;
        }
    }

    return OSR_DRV_OK;
}

/*----------------------------------------------------------------------------
 * Function: os_drv_isr_init()
 *
 * Description:
 *      this API registers an ISR with its vector id. it performs
 *      1. allocate vector table
 *      2. initialize each table entry.
 *
 * Inputs:  -
 *
 * Outputs: -
 *
 * Returns:
 *      OSR_DRV_OK : routine was successful.
 *      OSR_DRV_FAIL: OS driver related error.
 *---------------------------------------------------------------------------*/
INT32
os_drv_isr_init(void)
{
    return OSR_DRV_OK;
}
