/* 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: dtvbm11 $
 * $Date: 2015/11/10 $
 * $RCSfile: sif_hw_v2.c,v $
 * $Revision: #1 $
 *
 *---------------------------------------------------------------------------*/

/** @file sif_hw_v2.c
 *  This file implement serial interface SIF function.
 */

//---------------------------------------------------------------------------
// Include files
//---------------------------------------------------------------------------
#include "sif_debug.h"
#include "sif_hw.h"
#include "sif_if.h"

#include "drvcust_if.h"
#include "x_assert.h"
#include "x_bim.h"
#include "x_os.h"
#include "x_pinmux.h"
#include "x_hal_arm.h"
#include "x_printf.h"

#include "x_pdwnc.h"
#ifndef  CC_DISCARD_SUPPORT_PCMCIA
#include "pcmcia_if.h"
#endif

//---------------------------------------------------------------------------
// Configurations
//---------------------------------------------------------------------------

/// For driver customization options
/// 1. open drain (default use open drain mode)
/// 2. scl stretch (default don't use clock stretch mode)

/// Default use interrupt mode for OSAI mode
#define CC_SIF_USE_INTERRUPT_MODE

/// Use OSAI function option
#if defined (CC_MTK_LOADER)
#define SIF_USE_OSAI            0
#ifdef CC_SIF_USE_INTERRUPT_MODE
#undef CC_SIF_USE_INTERRUPT_MODE
#endif // CC_SIF_USE_INTERRUPT_MODE
#else // defined (CC_MTK_LOADER)
#define SIF_USE_OSAI            1
#endif // defined (CC_MTK_LOADER)

/// Default use multiple data read/write
#define CC_SIF_MULTIPLE_DATA_BYTE_ACCESS

#define SIF_TIMEOUT_MAX  (2000)


//---------------------------------------------------------------------------
// Constant definitions
//---------------------------------------------------------------------------


//---------------------------------------------------------------------------
// Type definitions
//---------------------------------------------------------------------------


//---------------------------------------------------------------------------
// Macro definitions
//---------------------------------------------------------------------------
#ifndef CC_MTK_LOADER
#ifndef  CC_DISCARD_SUPPORT_PCMCIA
#ifndef PCMCIA_RawWrite32MSK
#define PCMCIA_RawWrite32MSK(offset, value, msk)  PCMCIA_RawWrite32((offset),((PCMCIA_RawRead32(offset) & ~(msk)) | (value)))
#endif
#endif
#endif


//---------------------------------------------------------------------------
// Global Variables
//---------------------------------------------------------------------------


//---------------------------------------------------------------------------
// Imported variables
//---------------------------------------------------------------------------


//---------------------------------------------------------------------------
// Imported functions
//---------------------------------------------------------------------------


//---------------------------------------------------------------------------
// Static function forward declarations
//---------------------------------------------------------------------------
extern SIF_STRUCT_T* _SIF_GetActiveChannel(void);
extern SIF_STRUCT_T* _SIF_SetActiveChannel(SIF_STRUCT_T* prStruct);


//---------------------------------------------------------------------------
// Static variables
//---------------------------------------------------------------------------
#if SIF_USE_OSAI
#ifdef CC_SIF_USE_INTERRUPT_MODE
static HANDLE_T _hSifIsrSema      = NULL_HANDLE;
#endif // CC_SIF_USE_INTERRUPT_MODE
#endif

static BOOL _fgSifInit            = FALSE;
static BOOL _fgSifInitStart       = FALSE;
static BOOL _fgSifInitWait        = FALSE;

static BOOL _fgEnablePdwncMaster  = FALSE;
#if !defined(CC_MT5389)
static BOOL _fgEnablePdwncMaster1  = FALSE;
#endif

#ifndef CC_MTK_LOADER
#ifndef  CC_DISCARD_SUPPORT_PCMCIA
static BOOL _fgEnable8295bMaster0 = FALSE;
static BOOL _fgEnable8295bMaster1 = FALSE;
#endif
#endif

//---------------------------------------------------------------------------
// Static functions
//---------------------------------------------------------------------------
#if SIF_USE_OSAI
#ifdef CC_SIF_USE_INTERRUPT_MODE
static VOID _SIF_CommonISR(UINT16 u2Vector)
{
    if(_SIF_GetActiveChannel()->rSifOps.pfn_ISRHandler != NULL)
    {
        _SIF_GetActiveChannel()->rSifOps.pfn_ISRHandler(u2Vector);
    }
    else
    {
        Printf("Un-hooked SIF ISR!\n");
    }
}
#endif // CC_SIF_USE_INTERRUPT_MODE
#endif // SIF_USE_OSAI

INT32 _SIF_ISR_TryLock(void)
{
#if SIF_USE_OSAI && defined(CC_SIF_USE_INTERRUPT_MODE)
    return x_sema_lock(_hSifIsrSema, X_SEMA_OPTION_NOWAIT);
#else
    return 0;
#endif
}

INT32 _SIF_ISR_Unlock(void)
{
#if SIF_USE_OSAI && defined(CC_SIF_USE_INTERRUPT_MODE)
    return x_sema_unlock(_hSifIsrSema);
#else
    return 0;
#endif
}

INT32 _SIF_ISR_Lock(void)
{
#if SIF_USE_OSAI && defined(CC_SIF_USE_INTERRUPT_MODE)
    return x_sema_lock_timeout(_hSifIsrSema, SIF_TIMEOUT_MAX);
#else
    return 0;
#endif
}

void _SIF_ISR_DeleteSema(void)
{
#if SIF_USE_OSAI && defined(CC_SIF_USE_INTERRUPT_MODE)
        VERIFY(x_sema_delete(_hSifIsrSema) == OSR_OK);
#endif
}

void _SIF_ISR_CreateSema(void)
{
#if SIF_USE_OSAI && defined(CC_SIF_USE_INTERRUPT_MODE)
        VERIFY(x_sema_create(&_hSifIsrSema, X_SEMA_TYPE_BINARY, X_SEMA_STATE_LOCK) == OSR_OK);
#endif
}

BOOL _SIF_ISR_IsPdwncMasterEnable(void)
{
    return _fgEnablePdwncMaster;
}
#if !defined(CC_MT5389)
BOOL _SIF_ISR_IsPdwncMaster1Enable(void)
{
    return _fgEnablePdwncMaster1;
}
#endif

#ifndef CC_MTK_LOADER
#ifndef  CC_DISCARD_SUPPORT_PCMCIA
BOOL _SIF_ISR_Is8295bMaster0Enable(void)
{
    return _fgEnable8295bMaster0;
}

BOOL _SIF_ISR_Is8295bMaster1Enable(void)
{
    return _fgEnable8295bMaster1;
}
#endif
#endif

//-------------------------------------------------------------------------
/** SIF_Init
 *  Initialize SIF module.
 *  @param  VOID
 *  @return  VOID
 */
//-------------------------------------------------------------------------
VOID _SIF_ISR_Init(VOID)
{
    UINT32 u4Reg;
    UINT32 u4Ret, u4Var;
#if SIF_USE_OSAI
#ifdef CC_SIF_USE_INTERRUPT_MODE
    x_os_isr_fct pfnOldIsr;
#endif // CC_SIF_USE_INTERRUPT_MODE
#endif // SIF_USE_OSAI

    u4Reg = HalCriticalStart();

    if (_fgSifInit)
    {
        HalCriticalEnd(u4Reg);
        return;
    }

    if (!_fgSifInitStart)
    {
        _fgSifInitStart = TRUE;
    }
    else
    {
        _fgSifInitWait = TRUE;
    }
    HalCriticalEnd(u4Reg);

    if (!_fgSifInitWait)
    {

        u4Ret = (UINT32) DRVCUST_InitQuery(eSifEnablePdwncMaster, &u4Var);
        if (u4Ret == 0)
        {
            _fgEnablePdwncMaster = u4Var;
        }        
#if !defined(CC_MT5389)
        u4Ret = (UINT32) DRVCUST_InitQuery(eSifEnablePdwncMaster1, &u4Var);
        if (u4Ret == 0)
        {
            _fgEnablePdwncMaster1 = u4Var;
        }
#endif
#ifndef CC_MTK_LOADER        
#ifndef  CC_DISCARD_SUPPORT_PCMCIA
		u4Ret = (UINT32) DRVCUST_InitQuery(eSifEnable8295bMaster0, &u4Var);
        if (u4Ret == 0)
        {
            _fgEnable8295bMaster0 = u4Var;
        }

		u4Ret = (UINT32) DRVCUST_InitQuery(eSifEnable8295bMaster1, &u4Var);
        if (u4Ret == 0)
        {
            _fgEnable8295bMaster1 = u4Var;
        }
#endif
#endif

#if SIF_USE_OSAI
#ifdef CC_SIF_USE_INTERRUPT_MODE
        // Create ISR semaphore
        _SIF_ISR_CreateSema();
        // Register SIF ISR
        VERIFY(x_reg_isr(VECTOR_SERIAL, _SIF_CommonISR, &pfnOldIsr) == OSR_OK);
        if (_fgEnablePdwncMaster)
        {
            VERIFY(PDWNC_RegIsr(PDWNC_INTNO_SIFM, _SIF_CommonISR, &pfnOldIsr) == PDWNC_OK);
        }		
#if !defined(CC_MT5389)
        if (_fgEnablePdwncMaster1)
        {
            VERIFY(PDWNC_RegIsr(PDWNC_INTNO_SIFM1, _SIF_CommonISR, &pfnOldIsr) == PDWNC_OK);
        } 
#endif
#ifndef CC_MTK_LOADER
#ifndef  CC_DISCARD_SUPPORT_PCMCIA
		if ((_fgEnable8295bMaster0)||(_fgEnable8295bMaster1))
		{
			PCMCIA_RegIsr(PCMCIA_ISR_SIF, _SIF_CommonISR, &pfnOldIsr);
		}
#endif
#endif
#endif // CC_SIF_USE_INTERRUPT_MODE
#endif // SIF_USE_OSAI

        // init local variable
        _fgSifInit = TRUE;

    }
    else
    {
        /* Wait until SIF_Init() finish */
        while (!_fgSifInit)
        {
            static UINT32 u4Count=0;
            x_thread_delay(10);
            u4Count++;
            ASSERT(u4Count < 1000);
        }
    }
}


