/* 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$
 * $Date$
 * $RCSfile: board.c,v $
 * $Revision$
 *
 *---------------------------------------------------------------------------*/

/** @file board.c
 *  board.c provides functions of board initialization, board detection, and
 *
 */

#ifndef CC_MTK_LOADER

#include "x_bim.h"
#include "x_hal_arm.h"
#include "x_hal_5381.h"
#include "x_serial.h"
#include "x_assert.h"
#include "x_ckgen.h"
#include "x_printf.h"
#include "x_lint.h"
#include "x_pdwnc.h"
#include "x_pinmux.h"
#include "drvcust_if.h"

LINT_EXT_HEADER_BEGIN

#include "x_rand.h"
#include "x_os.h"
#include "x_util.h"
#include "nucleus.h"

LINT_EXT_HEADER_END

#include "c_model.h"

//-----------------------------------------------------------------------------
// Constant definitions
//-----------------------------------------------------------------------------
#define FPGA_SYSTEM_CLOCK			27000000	// 20M Hz system clock
#define TICKS_PER_SECOND			NU_PLUS_Ticks_Per_Second
#define SIGNAL_MSG_NUM              15
#define MIN_MEMORY_ALIGNMENT		8


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

typedef void (* PFN_VOID)(void);


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

EXTERN UINT32 g_u4BootRemap;

EXTERN UINT32 HeapStart;
EXTERN void* g_pvPageTable;

EXTERN UINT32 g_u4ArmLibHeapStart;
EXTERN UINT32 g_u4ArmLibHeapSize;
EXTERN UINT32 g_u4HeapStart;


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

EXTERN void Restore_System_Data(void);
EXTERN void INC_Initialize(void* first_available_memory, void(*pfnPrint)(const CHAR*));


// Exported functions for RVCT library
EXTERN void __rt_raise(INT32 signal, INT32 type);

EXTERN void _sys_exit(INT32 return_code);

EXTERN void __rt_entry(void);

EXTERN UINT32 __rt_heap_extend(UINT32 size, void* block);

// The return value of __rt_lib_init() is an __argv_argv struct actually. But
// here we don't use it
//
EXTERN void __rt_lib_init(UINT32 u4HeapBase, UINT32 u4HeapTop);
EXTERN void _BSP_Main(void);
// Note: Calling __rt_lib_init() causes semihosting violation if there is no
// main() existed!

// below two function is used in void _RvctLibInit(void)
EXTERN void _init_alloc(UINT32 u4MemBase, UINT32 u4MemTop);
EXTERN void _fp_init(void);

//-----------------------------------------------------------------------------
// Static variables
//-----------------------------------------------------------------------------

static UINT32 fgAbortReboot = 1;

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

//=====================================================================
// RVCT-dependent stuffs

// The following symbol has no effect except to cause a link-time error if
// a function that uses the semihosting SWI is included from the library.
// The linker error message is:
//
//	Error: L6200E: Symbol __semihosting_swi_guard multiply defined
//					(by use_semi.o and use_no_semi.o)
//
// To resolve the error, link with -verbose -errors out.txt, then search
// out.txt for __semihosting_swi_guard to identify functions using semihosting
// directly or indirectly.
//
#pragma import(__use_no_semihosting_swi)


//-----------------------------------------------------------------------------
// RVCT used functions
//-----------------------------------------------------------------------------

/*----------------------------------------------------------------------------
 * Function: __rt_raise
 *
 * Description:
 *      __rt_raise() can be called from numerous places within RVCT compiled
 *      code, thus must be re-implemented. Refer to signal.h in RCVT for
 *      signal definitions.
 *
 * Inputs:
 *      signal:
 *      type:
 *
 * Outputs: -
 *
 * Returns: -
 *---------------------------------------------------------------------------*/
void __rt_raise(INT32 signal, INT32 type)
{
    CHAR* msgs[SIGNAL_MSG_NUM] =
    {
        "Unknown",                              // 0
        "Abort",                                // 1
        "Arithmetic exception",                 // 2
        "Illegal instruction",                  // 3
        "Attention request from user",          // 4
        "Bad memory access",                    // 5
        "Termination request",                  // 6
        "Stack overflow",                       // 7
        "Run-time redirection error",           // 8
        "Run-time memory error",                // 9
        "User signal 1",                        // 10
        "User signal 2",                        // 11
        "Pure virtual function called",         // 12
        "Misc exception from C++ library",      // 13
        "new or new[] cannot allocate memory"   // 14
    };

    CHAR* pMsg;

    // Flush Log data.
    UTIL_LogFlush();

    pMsg = ((signal < 0) || (signal >= SIGNAL_MSG_NUM)) ? msgs[0] :
        msgs[signal];

    Printf("\nSignal - %s (%d), type: %d\n\n", pMsg, signal, type);

    BSP_HaltSystem();
}


/*----------------------------------------------------------------------------
 * Function: _sys_exit
 *
 * Description:
 *      This function is redefined to avoid semi-hosting
 *
 * Inputs:
 *      return_code:
 *
 * Outputs: -
 *
 * Returns: -
 *---------------------------------------------------------------------------*/
void _sys_exit(INT32 return_code)
{
	UNUSED(return_code);

    BSP_HaltSystem();
}


/*----------------------------------------------------------------------------
 * Function: __rt_entry
 *
 * Description:
 *      The starting point for a program using ARM C library. Called directly
 *      from __main().
 *
 * Inputs: -
 *
 * Outputs: -
 *
 * Returns: -
 *---------------------------------------------------------------------------*/
void __rt_entry(void)
{
// The return value of __rt_lib_init() is an __argv_argv struct actually. But
// here we don't use it
//
// EXTERN void __rt_lib_init(UINT32 u4HeapBase, UINT32 u4HeapTop);
// EXTERN void _BSP_Main(void);

// Note: Calling __rt_lib_init() causes semihosting violation if there is no
// main() existed!
/*
    // Initialize C library and constructors
    __rt_lib_init(g_u4ArmLibHeapStart,
        g_u4ArmLibHeapStart + g_u4ArmLibHeapSize);
*/
    /* Here is tv image. */
    BIM_SetTimeLog(5);

    _BSP_Main();
}


/*----------------------------------------------------------------------------
 * Function: __rt_heap_extend
 *
 * Description:
 *      Called in __main() while initializing heap memory
 *
 * Inputs:
 *      size:
 *      block:
 *
 * Outputs: -
 *
 * Returns:
 *
 *---------------------------------------------------------------------------*/
UINT32 __rt_heap_extend(UINT32 size, void* block)
{
    UNUSED(size);
    UNUSED(block);

    return 0;   // Refuse all requests for extra heap space
}

#if 1
void __rt_SIGRTMEM(INT32 signal, INT32 type)
{
    UNUSED(signal);
    UNUSED(type);
}

void __rt_SIGFPE_inner(INT32 signal, INT32 type)
{
    UNUSED(signal);
    UNUSED(type);
}
#else
void _ttywrch()
{
}
#endif


//-----------------------------------------------------------------------------
// Static functions
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
/** _RvctLibInit() Initialize RVCT library
 */
//-----------------------------------------------------------------------------
static void _RvctLibInit(void)
{
// EXTERN void _init_alloc(UINT32 u4MemBase, UINT32 u4MemTop);
// EXTERN void _fp_init(void);

    // Initialize heap area
    _init_alloc(g_u4ArmLibHeapStart, g_u4ArmLibHeapStart + g_u4ArmLibHeapSize);
    // Initialize floating-point library
    _fp_init();
}


//=====================================================================
// Board level initialization


//-----------------------------------------------------------------------------
/** _TimerInit() Initialize timer
 */
//-----------------------------------------------------------------------------
static void _TimerInit(void)
{
    UINT32 u4SysClock, u4TimerInterval, u4Val;

	// Reset timer 0 value
    BIM_WRITE32(REG_RW_TIMER0_LOW, 0);
    BIM_WRITE32(REG_RW_TIMER0_HIGH, 0);

    // Setup timer interrupt interval
    u4SysClock = BSP_GetSystemClock();
    u4TimerInterval = u4SysClock / TICKS_PER_SECOND;
    BIM_WRITE32(REG_RW_TIMER0_LLMT, (u4TimerInterval - 1));
    BIM_WRITE32(REG_RW_TIMER0_LOW, (u4TimerInterval - 1));
    BIM_WRITE32(REG_RW_TIMER0_HLMT, 0);

    // Enable timer 0 with auto-load
    u4Val = BIM_READ32(REG_RW_TIMER_CTRL);
    u4Val |= (TMR0_CNTDWN_EN | TMR0_AUTOLD_EN);
    BIM_WRITE32(REG_RW_TIMER_CTRL, u4Val);

    // Enable timer interrupt
    VERIFY(BIM_EnableIrq(VECTOR_T0));
}


//-----------------------------------------------------------------------------
/** _BoardInit Board-related initializations
 */
//-----------------------------------------------------------------------------
static void _BoardInit(void)
{
    // Initialize RVCT library
    _RvctLibInit();

	// Initialize timer used for OS
	_TimerInit();

	// Init serial port
	SerInit();

    // Init random number generator
    srand(BIM_READ32(REG_RW_TIMER0_LOW));

    BSP_CkgenInit();
}


/*----------------------------------------------------------------------------
 * Function: _InitMMU
 *
 * Description:
 *      Initialize MMU
 *
 * Inputs: -
 *
 * Outputs: -
 *
 * Returns: -
 *---------------------------------------------------------------------------*/
static void _InitMMU(void)
{
    //HalEnableCaches();
    VERIFY(HalInitMMU((UINT32)&g_pvPageTable));
//    VERIFY(HalInitMMU(BSP_GetMemSize() - 0x4000));
}


//-----------------------------------------------------------------------------
/** _PrintMessage() Print a message
 *  @param msg: The message
 */
//-----------------------------------------------------------------------------
static void _PrintMessage(const CHAR* msg)
{
    Printf(msg);
}


//-----------------------------------------------------------------------------
// Public functions
//-----------------------------------------------------------------------------

#pragma arm

//-----------------------------------------------------------------------------
/** _BSP_Main() The entry point of BSP main routine
 */
//-----------------------------------------------------------------------------
void _BSP_Main(void)
{
    vIO32Write4B(PDWNC_WDT0, 0);//PDWNC_WRITE32(REG_RW_WATCHDOG_TMR, 0);

    // Restore certain system data zeroed by __main()
    Restore_System_Data();

    // Initialize MMU, if comment this, no MMU and NO I-cache & D-cache.
    _InitMMU();

    // Board level initialization
    _BoardInit();

    // Call INC_Initialize() (in Nucleus library) to initialize OS, which will
    // then call to above Application_Initialize(). INC_Initialize() will
    // start scheduler, thus it will never return.
    INC_Initialize((void*)g_u4HeapStart, _PrintMessage);

    // Never goes here
    ASSERT(FALSE);
}

//-----------------------------------------------------------------------------
/** BSP_GetSystemClock() Get system clock
 *  @return The system clock
 */
//-----------------------------------------------------------------------------
UINT32 BSP_GetSystemClock(void)
{
    if (BSP_IsFPGA())
    {
        return FPGA_SYSTEM_CLOCK;
    }
    else
    {
        return GET_XTAL_CLK();
    }
}


//-----------------------------------------------------------------------------
/** BSP_HaltSystem() Halt the system
 */
//-----------------------------------------------------------------------------
void BSP_HaltSystem(void)
{
    UINT32 u4Loop;

    SerWaitTxBufClear();

    // Disable interrupts
    HalDisableIRQ();
    HalDisableFIQ();

    SerEnd();

#ifdef NDEBUG
    if (fgAbortReboot) { PDWNC_Reboot(); }
#else /* NDEBUG */
    PDWNC_WatchDogReboot();
#endif /* NDEBUG */

    u4Loop = 1;

    // Halt the system
    while (u4Loop)
    {
    }

    // Enable interrupts
    HalEnableIRQ();
    HalEnableFIQ();

    SerStart();
}


//-----------------------------------------------------------------------------
/** BSP_AllocateDmaMemory() Allocate memory that can be used as DMA buffer
 *  @param u4Size: The desired memory size
 *  @return The pointer of the allocated memory, or NULL if out of memory
 */
//-----------------------------------------------------------------------------
UPTR BSP_AllocDmaMemory(UPTR u4Size)
{
    UINT32 u4CacheLineSize, u4AllocSize;
    void* pMem;
    UPTR* p;

    u4CacheLineSize = HalGetDCacheLineSize();

    // Allocate 2 * cache_line bytes more, to guarantee that the entire DMA
    // buffer will not share cache lines with others
    u4AllocSize = u4Size + (u4CacheLineSize * 2) + sizeof(UPTR);

    pMem = x_mem_alloc(u4AllocSize);
    if (pMem == NULL)
    {
        return 0;
    }

    p = (UPTR*)((UPTR)pMem + u4CacheLineSize);
    *p = (UPTR)pMem;

    return ((UPTR)pMem + u4CacheLineSize + sizeof(UPTR));
}


//-----------------------------------------------------------------------------
/** BSP_FreeDmaMemory() Free the allocated DMA memory
 *  @param pUser: The memory pointer to be freed
 *  @retval TRUE: Succeed
 *  @retval FALSE: Fail
 */
//-----------------------------------------------------------------------------
BOOL BSP_FreeDmaMemory(UPTR uUserAddr)
{
    UINT32 u4CacheLineSize;
    UPTR* p;
    void* pMem, *pUser = (void*)uUserAddr;

    // Check if NULL
    if (pUser == NULL)
    {
        return TRUE;
    }

    // Check if the memory is allocated from BSP_AllocDmaMemory()
    u4CacheLineSize = HalGetDCacheLineSize();
    p = (UPTR*)((UPTR)pUser - sizeof(UPTR));
    pMem = (void*)((UPTR)p - u4CacheLineSize);
    if (*p != (UPTR)pMem)
    {
        return FALSE;
    }

    x_mem_free(pMem);

    return TRUE;
}


//-----------------------------------------------------------------------------
/** BSP_AllocateAlignedDmaMemory Allocate memory that can be used as DMA buffer
 *  @param u4Size: The desired memory size
 *  @return: The pointer of the allocated memory, or NULL if out of memory
 */
//-----------------------------------------------------------------------------
UPTR BSP_AllocAlignedDmaMemory(UPTR u4Size, UPTR u4Align)
{
    UINT32 u4CacheLineSize, u4AllocSize;
    void* pMem;
    UPTR* p;

	// Adjust alignment
	if (u4Align < MIN_MEMORY_ALIGNMENT)
	{
		u4Align = MIN_MEMORY_ALIGNMENT;
	}
	u4Align &= ~(MIN_MEMORY_ALIGNMENT - 1);

    u4CacheLineSize = HalGetDCacheLineSize();

    // Allocate 2 * cache_line bytes more, to guarantee that the entire DMA
    // buffer will not share cache lines with others
    u4AllocSize = u4Size + (u4CacheLineSize * 2) + u4Align;

    pMem = x_mem_alloc(u4AllocSize);
    if (pMem == NULL)
    {
        return 0;
    }

    p = (UPTR*)(((UPTR)pMem + u4CacheLineSize + u4Align) & ~(u4Align - 1));
    *(p - 1) = (UPTR)pMem;
    *(UPTR*)pMem = (UPTR)p;

    return (UPTR)p;
}


//-----------------------------------------------------------------------------
/** BSP_FreeAlignedDmaMemory() Free the allocated DMA memory
 *  @param pUser: The memory pointer to be freed
 *  @retval  TRUE: Succeed
 *  @retval  FALSE: Fail
 */
//-----------------------------------------------------------------------------
BOOL BSP_FreeAlignedDmaMemory(UPTR uUserAddr)
{
    UPTR* p;
    void* pMem, *pUser = (void*)uUserAddr;

    // Check if NULL
	if (pUser == NULL)
	{
		return TRUE;
	}

    // Check if the memory is allocated from BSP_AllocDmaMemory()
    p = (UPTR*)pUser;
    pMem = (void*)*(p - 1);
    if (*(UPTR*)pMem != (UPTR)p)
    {
    	return FALSE;
    }

    x_mem_free(pMem);

    return TRUE;
}

//-----------------------------------------------------------------------------
/** BSP_GetFbmMemAddr()
 *  @return the frame buffer start address
 */
//-----------------------------------------------------------------------------
UINT32 BSP_GetFbmMemAddr()
{
    return (BSP_GetMemSize() - DRVCUST_OptGet(eFbmMemSize));
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
UINT32 BSP_QuerySetAbortRebootFlag(UINT32 *pu4Flag)
{
    if (pu4Flag != NULL)
    {
        fgAbortReboot = *pu4Flag;
    }
    return fgAbortReboot;
}


#ifdef CC_SUPPORT_STR_CORE_OFF
struct timer_saves {
    UINT32 timer[3][2];
    UINT32 timerLimit[3][2];
    UINT32 timeControl;
};
static struct timer_saves timerInfo;

void Board_pm_suspend(void)
{
    int i;
    UINT32 state;

    state = HalCriticalStart();

    timerInfo.timeControl = BIM_READ32(REG_RW_TIMER_CTRL);

    // Temporary stop timer.
    BIM_WRITE32(REG_RW_TIMER_CTRL, 0);

    for (i=0; i<3; i++)
    {
        timerInfo.timer[i][0] = BIM_READ32(REG_RW_TIMER0_LOW + i*8);
        timerInfo.timer[i][1] = BIM_READ32(REG_RW_TIMER0_HIGH + i*8);
        timerInfo.timerLimit[i][0] = BIM_READ32(REG_RW_TIMER0_LLMT + i*8);
        timerInfo.timerLimit[i][1] = BIM_READ32(REG_RW_TIMER0_HLMT + i*8);
    }

    // Start again.
    BIM_WRITE32(REG_RW_TIMER_CTRL, timerInfo.timeControl);

    HalCriticalEnd(state);
}

void Board_pm_resume(void)
{
    int i;
    UINT32 state;

    state = HalCriticalStart();

    // Temporary stop timer.
    BIM_WRITE32(REG_RW_TIMER_CTRL, 0);

    for (i=0; i<3; i++)
    {
        BIM_WRITE32(REG_RW_TIMER0_LOW + i*8, timerInfo.timer[i][0]);
        BIM_WRITE32(REG_RW_TIMER0_HIGH + i*8, timerInfo.timer[i][1]);
        BIM_WRITE32(REG_RW_TIMER0_LLMT + i*8, timerInfo.timerLimit[i][0]);
        BIM_WRITE32(REG_RW_TIMER0_HLMT + i*8, timerInfo.timerLimit[i][1]);
    }

    // Set TIMER1 to MAX
    BIM_WRITE32(REG_RW_TIMER1_LOW , 0xFFFFFFFF);

    // Start again.
    BIM_WRITE32(REG_RW_TIMER_CTRL, timerInfo.timeControl);

    HalCriticalEnd(state);

    BSP_CkgenInit();
}
#endif

#endif /* CC_MTK_LOADER */
