/* 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.
 *
 *****************************************************************************/

/*-----------------------------------------------------------------------------
 *
 * $Author: brianpc.huang $
 * $Date: 2015/05/29 $
 * $RCSfile: ir_hw.c,v $
 * $Revision: #1 $
 *
 *---------------------------------------------------------------------------*/

/** @file ir_hw.c
 *  ir_hw.c provides IRRX/IRTX register access functions.
 */
//-----------------------------------------------------------------------------
// Include files
//-----------------------------------------------------------------------------
#include "ir_if.h"
#include "ir_debug.h"
#include "x_assert.h"
#include "x_bim.h"
#include "x_ckgen.h"
#include "x_pdwnc.h"
#include "x_debug.h"
#include "x_hal_arm.h"
#include "x_lint.h"
#include "x_printf.h"

#define IRRX_CLK_3MHZ_DIV_1    0   // IRRX clock set in 24(3)MHZ
#define IRRX_CLK_3MHZ_DIV_2	   1   // IRRX clock set in 24(3)MHZ/2
#define IRRX_CLK_3MHZ_DIV_4	   2   // IRRX clock set in 24(3)MHZ/4
#define IRRX_CLK_3MHZ_DIV_8	   3   // IRRX clock set in 24(3)MHZ/8
#define IRRX_CLK_3MHZ_DIV_16   4   // IRRX clock set in 24(3)MHZ/16
#define IRRX_CLK_3MHZ_DIV_32   5   // IRRX clock set in 24(3)MHZ/32
#define IRRX_CLK_3MHZ_DIV_64   6   // IRRX clock set in 24(3)MHZ/64
#define IRRX_CLK_3MHZ_DIV_128  7   // IRRX clock set in 24(3)MHZ/128
#define IRRX_CLK_3MHZ_DIV_256  8   // IRRX clock set in 24(3)MHZ/256
#define IRRX_CLK_3MHZ_DIV_511  9   // IRRX clock set in 24(3)MHZ/511

#define IRRX_CLR_BSY_IR     0x01
#define MAX_IRRX_DATA       (4)

//-----------------------------------------------------------------------------
// Static functions
//-----------------------------------------------------------------------------
static UINT32 u4IrPulseTimeUnit = 50; //use default value 50 us
#ifndef CC_MTK_LOADER
static PFN_IRRXCB_T _pfnIrRxCbFunc = NULL;
#endif

//-----------------------------------------------------------------------------
/*
** _IrRxHwBusyClr() to clear IRRX h/w buffer then it can receive next message.
*/
//-----------------------------------------------------------------------------
static void _IrRxHwBusyClr(void)
{
    vIO32WriteFldAlign(PDWNC_IRCLR, IRRX_CLR_BSY_IR, FLD_IRCLR);
}

void _IrDtvRxHwBusyClr(void)
{
    UINT32 u4Info;

    u4Info = u4IO32Read4B(PDWNC_IRCNT);
    if(u4Info != 0)
    {
        LOG(0, "[%s]IR ISR TIMEOUT(RST BUSY BIT)!!\n",__FUNCTION__);
        vIO32WriteFldAlign(PDWNC_IRCLR, IRRX_CLR_BSY_IR, FLD_IRCLR);
    }
}


#ifndef CC_MTK_LOADER
//-----------------------------------------------------------------------------
/*
 * _IrRxHwIsr() the interrupt of IRRX from PDWNC interrupt ISR.
 * @param u2VctId It must be PDWNC_INTNO_IRRX.
 */
//-----------------------------------------------------------------------------
static void _IrRxHwIsr(UINT16 u2VctId)
{
    UINT32 u4Info;
    UINT32 au4IrRxData[MAX_IRRX_DATA];
    UINT32 i, u4PulsePeriod;
    UNUSED(u2VctId);

    /* read IRRX IRQ data. */
    u4Info = u4IO32Read4B(PDWNC_IRCNT);
    au4IrRxData[0] = u4IO32Read4B(PDWNC_IRDATA0);
    au4IrRxData[1] = u4IO32Read4B(PDWNC_IRDATA1);

    LOG(1, "PWD:0x%08x RxIsr Info:0x%08x 0x%08x%08x\n",u4IO32Read4B(PDWNC_INTSTA), u4Info, au4IrRxData[1], au4IrRxData[0]);
    LOG(2, "IR period(us):\n");
    for(i = 0; i < 67; )
    {
        u4PulsePeriod = u4IO32Read4B(PDWNC_CHKDATA0 + i);
        LOG(2, "%4d, %4d, %4d, %4d,",  (u4PulsePeriod & 0xff) * u4IrPulseTimeUnit, ((u4PulsePeriod >> 8 )& 0xff) * u4IrPulseTimeUnit,((u4PulsePeriod >> 16 )& 0xff) * u4IrPulseTimeUnit, ((u4PulsePeriod >> 24 )& 0xff) * u4IrPulseTimeUnit);
        i+=4;
        u4PulsePeriod = u4IO32Read4B(PDWNC_CHKDATA0 + i);
        LOG(2, " %4d, %4d, %4d, %4d\n",  (u4PulsePeriod & 0xff) * u4IrPulseTimeUnit, ((u4PulsePeriod >> 8 )& 0xff) * u4IrPulseTimeUnit,((u4PulsePeriod >> 16 )& 0xff) * u4IrPulseTimeUnit, ((u4PulsePeriod >> 24 )& 0xff) * u4IrPulseTimeUnit);
        i+=4;
    }

    if ((u4Info != 0) && (_pfnIrRxCbFunc != NULL))
    {
        _pfnIrRxCbFunc(u4Info, (UINT8 *)au4IrRxData);
    }

    /* clear interrupt. */
    VERIFY(PDWNC_ClearInt(PDWNC_INTNO_IRRX));

    /* Do BIM clear IR busy bit, after interrupt clear */
    _IrRxHwBusyClr();
}
#endif

//-----------------------------------------------------------------------------
/*
 *  _IRHW_RxSetIsr() to enable/disable interrupt of IRRX
 *  IRRX interrupt is a sub-interrupt of PDWNC interrupt, the enable/disable \n
 *  function is to call PDWNC function to set the PDWNC interrupt enable \n
 *  register.
 *  @param fgSet a boolean value, true to enable ISR, false to disable.
 *  @retval 0 is operating successfully, otherwise failed.
 */
//-----------------------------------------------------------------------------
static INT32 _IRHW_RxSetIsr(BOOL fgSet)
{
#ifndef CC_MTK_LOADER
    INT32 i4Val;
    void (* pfnOldIsr)(UINT16);

    if (fgSet)
    {   /* enable irq. */

        i4Val = PDWNC_RegIsr(PDWNC_INTNO_IRRX, _IrRxHwIsr, &pfnOldIsr);
        if (i4Val != PDWNC_OK)
        {
            return IR_FAIL;
        }
        VERIFY(PDWNC_ClearInt(PDWNC_INTNO_IRRX));
    }

    else
    {   /* disable irq. */

        i4Val = PDWNC_RegIsr(PDWNC_INTNO_IRRX, NULL, &pfnOldIsr);
        if (i4Val != OSR_OK)
        {
            return IR_FAIL;
        }
    }
#endif /* CC_MTK_LOADER */
   return IR_SUCC;
}
//-----------------------------------------------------------------------------
/*
 * IRHW_RxStopIsr() to stop IRRX ISR
 *  @retval 0 is operating successfully, otherwise failed.
 */
//-----------------------------------------------------------------------------
INT32 IRHW_RxStopIsr(void)
{
    INT32 i4Ret;

    i4Ret = _IRHW_RxSetIsr((BOOL) FALSE);
    return i4Ret;
}
//-----------------------------------------------------------------------------
/*
 * IRHW_RxReStartIsr() to restart a stoped IRRX ISR
 *  @retval 0 is operating successfully, otherwise failed.
 */
//-----------------------------------------------------------------------------
INT32 IRHW_RxReStartIsr(void)
{
    INT32 i4Ret;
    vIO32WriteFldAlign(PDWNC_IRCLR, IRRX_CLR_BSY_IR, FLD_IRCLR);
    i4Ret = _IRHW_RxSetIsr((BOOL) TRUE);
    return i4Ret;
}

#ifndef CC_MTK_LOADER
//-----------------------------------------------------------------------------
/*
 * IRHW_RxSetCallback() to register interrupt callback routine.
 *  @param pfnCallback the new register callback routine
 *  @param ppfnOld pointer to the old callback routine
 *  @retval 0 is operating successfully, otherwise failed.
 */
//-----------------------------------------------------------------------------
INT32 IRHW_RxSetCallback(PFN_IRRXCB_T pfnCallback, PFN_IRRXCB_T * ppfnOld)
{
    if (ppfnOld != NULL)
    {
    *ppfnOld = _pfnIrRxCbFunc;
    }
    _pfnIrRxCbFunc = pfnCallback;
    return IR_SUCC;
}
#endif /* CC_MTK_LOADER */


//-----------------------------------------------------------------------------
/*
 *  IRHW_RxRdConf() to read IRRX configration
 *  @param pi4Config to read PDWNC_IRCFGH
 *  @param pi4SaPeriod to read PDWNC_IRCFGL
 *  @param pi4Threshold to read PDWNC_IRTHD
 */
//-----------------------------------------------------------------------------
void IRHW_RxRdConf(INT32 * pi4Config, INT32 * pi4SaPeriod,  INT32 * pi4Threshold)
{
    if ((pi4Config == NULL) || (pi4SaPeriod==NULL) || (pi4Threshold==NULL))
    {
        ASSERT(0);
        LINT_SUPPRESS_BRACE(527);
        return;
    }
    *pi4Config =    (INT32)u4IO32Read4B(PDWNC_IRCFGH);
    *pi4SaPeriod =  (INT32)u4IO32Read4B(PDWNC_IRCFGL);
    *pi4Threshold = (INT32)u4IO32Read4B(PDWNC_IRTHD);

}

//-----------------------------------------------------------------------------
/*
 * IRHW_RxWrConf() to set IRRX configration
 *  @param pi4Config to read PDWNC_IRCFGH
 *  @param pi4SaPeriod to read PDWNC_IRCFGL
 *  @param pi4Threshold to read PDWNC_IRTHD
 */
//-----------------------------------------------------------------------------
void IRHW_RxWrConf(INT32 i4Config, INT32 i4SaPeriod, INT32 i4Threshold)
{
    // IRRX Clock enable
    if(IO32ReadFldAlign(PDWNC_CLKPDN, FLD_IRRXPD))
    {
		vIO32WriteFldAlign(PDWNC_CLKPDN, 0, FLD_IRRXPD);
    }
    /* Set IRRX clock to 3Mhz / 64 */
    vIO32WriteFldAlign(PDWNC_IRCKSEL, IRRX_CLK_3MHZ_DIV_64, FLD_IRCLKSEL);
#if defined(CC_MT5893) || defined(CC_MT5895)
    /* Set IRRX Check HW clock to 3Mhz or 24Mhz / 1 */
    vIO32WriteFldAlign(PDWNC_IRCKSEL, IRRX_CLK_3MHZ_DIV_1, FLD_IR_CHK_CLKSEL);
#endif    
    /* Config IRRX registers */
    vIO32Write4B(PDWNC_IRCFGH, (UINT32)i4Config);
    vIO32Write4B(PDWNC_IRCFGL, (UINT32)i4SaPeriod);
    vIO32Write4B(PDWNC_IRTHD, (UINT32)i4Threshold);
    //for ir HW check pulse test
    if(IO32ReadFldAlign(PDWNC_PDMODE, FLD_DEEP_SLEEP))// 3Mhz
    {
        vIO32WriteFldAlign(PDWNC_IRCFGL, u4IrPulseTimeUnit * 3, FLD_CHK_SAPERIOD);
    }
    else
    {
        vIO32WriteFldAlign(PDWNC_IRCFGL, u4IrPulseTimeUnit * 24, FLD_CHK_SAPERIOD);
    }
    vIO32WriteFldAlign(PDWNC_IRCFGH, 1, FLD_CHK_EN);
}




//-----------------------------------------------------------------------------
/*
 * IRHW_Init() to set IRRX configration and start IRRX ISR
 *  @param pi4Config to read PDWNC_IRCFGH
 *  @param pi4SaPeriod to read PDWNC_IRCFGL
 *  @param pi4Threshold to read PDWNC_IRTHD
 *  @retval 0 is operating successfully, otherwise failed.
 */
//-----------------------------------------------------------------------------
INT32 IRHW_RxInit(INT32 i4Config, INT32 i4SaPeriod, INT32 i4Threshold)
{
    INT32 i4Ret;
    #ifndef CC_UBOOT
    UINT32 u4State;
    #endif /* CC_UBOOT */
    INT32 i;

    IRHW_RxWrConf(i4Config, i4SaPeriod, i4Threshold);
#ifndef CC_MTK_AUTO_TEST
    i4Ret = _IRHW_RxSetIsr((BOOL)TRUE);
#else
    i4Ret = _IRHW_RxSetIsr((BOOL)FALSE);
#endif /* CC_MTK_AUTO_TEST */
    LOG(0,"u4IrPulseTimeUnit is %ld us\n", u4IrPulseTimeUnit);
    #ifndef CC_UBOOT
    u4State = HalCriticalStart();
    #endif /* CC_UBOOT */

    // wait 1ms for Busy Clr operation finish.
    for (i=0; i<30000; i++) { }
    _IrRxHwBusyClr();
    // wait 1ms for Busy Clr operation finish.
    for (i=0; i<30000; i++) { }
    #ifndef CC_UBOOT
    HalCriticalEnd(u4State);
    #endif /* CC_UBOOT */
    return i4Ret;
}

