/* 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: lzhs.c,v $
 * $Revision: #1 $
 *
 *---------------------------------------------------------------------------*/

/** @file lzhs_dec.c
 *  Brief of file lzhs_dec.c.
 *  Details of file lzhs_dec.c (optional).
 */


//-----------------------------------------------------------------------------
// Include files
//-----------------------------------------------------------------------------

#include "x_typedef.h"
#include "x_assert.h"
#include "x_printf.h"
#include "x_bim.h"
#include "x_hal_arm.h"
#include "x_timer.h"
#include "x_ckgen.h"
#include "x_serial.h"
#include "x_ldr_env.h"
#include "x_debug.h"

#ifndef CC_MTK_LOADER
#include "x_os.h"
#endif /* CC_MTK_LOADER */

#ifdef CC_UBOOT
extern void HAL_Delay_us(UINT32 u4Micros);
#define LZHS_Delay_us(Micros)   HAL_Delay_us(Micros)
#else
extern void CHIP_Delay_us(UINT32 u4Micros);
#define LZHS_Delay_us(Micros)   CHIP_Delay_us(Micros)
#endif

//-----------------------------------------------------------------------------
// Constant definitions
//-----------------------------------------------------------------------------
//#define CHANGE_CLK

#define LZHS_HW_ALIGNMENT   8
#define LZHS_TIMEOUT        5000
#define PBI_A_SIZE			(0x2000000)		//32MB
//#define LZHS_DEBUG
//#define CHANGE_DECOMPRESS_CLK

//-----------------------------------------------------------------------------
// Type definitions
//-----------------------------------------------------------------------------
#ifdef CC_MTK_LOADER
typedef void (*PFN_IMAGE)(void);
#endif /* CC_MTK_LOADER */

//-----------------------------------------------------------------------------
// Macro definitions
//-----------------------------------------------------------------------------
#ifdef CC_MTK_PRELOADER
#define Printf(fmt...)
#endif /* CC_MTK_PRELOADER */

#ifdef CC_MTK_LOADER
#ifdef  LOG
#undef  LOG
#endif
#define LOG(level, fmt...)   Printf(fmt)
#endif

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

//-----------------------------------------------------------------------------
// Imported functions
//-----------------------------------------------------------------------------
extern BOOL BIM_VerifySignature(UINT32* pu4Signature, UINT32* pu4CheckSum);

//-----------------------------------------------------------------------------
// Static function forward declarations
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// Static variables
//-----------------------------------------------------------------------------
#ifndef CC_MTK_LOADER

static HANDLE_T _hLZHSSema;
static BOOL _fgIsrEn = FALSE;
static BOOL _fgLZHSRegIsr = FALSE;


//-----------------------------------------------------------------------------
/** _LZHS_Isr()
 */
//-----------------------------------------------------------------------------
static void _LZHS_Isr(UINT16 u2VctId)
{
    UINT32 u4Val;

    UNUSED(u2VctId);
    u4Val = BIM_READ32(REG_LZHS_CTRL);

    // this LZHS_INT status already cleared by HISR
    // if (u4Val & LZHS_INT)
    {
        VERIFY(x_sema_unlock(_hLZHSSema) == OSR_OK);
    }

    VERIFY(BIM_ClearIrq(VECTOR_LZHS));
}

//-----------------------------------------------------------------------------
/** _LZHS_Isr()
 */
//-----------------------------------------------------------------------------
static void _LZHS_SetISR()
{
    void (* pfnOldIsr)(UINT16);

    if (!_fgLZHSRegIsr)
    {
        VERIFY(BIM_ClearIrq(VECTOR_LZHS));
        VERIFY(x_sema_create(&_hLZHSSema, X_SEMA_TYPE_BINARY, X_SEMA_STATE_LOCK) == OSR_OK);
        VERIFY(x_reg_isr(VECTOR_LZHS, _LZHS_Isr, &pfnOldIsr) == OSR_OK);

        VERIFY(BIM_DisableIrq(VECTOR_LZHS));
        _fgLZHSRegIsr = TRUE;
    }
}

//-----------------------------------------------------------------------------
/** Brief of LZHS_SetIsrEnable.
 *  Details of LZHS_SetIsrEnable
 */
//-----------------------------------------------------------------------------
BOOL LZHS_SetIsrEnable(BOOL fgSet)
{
    BOOL fgOrg;

    fgOrg = _fgIsrEn;
    _fgIsrEn = fgSet;

    if (fgSet)
    {
        _LZHS_SetISR();

        VERIFY(BIM_ClearIrq(VECTOR_LZHS));
        VERIFY(BIM_EnableIrq(VECTOR_LZHS));
    }
    else
    {
        VERIFY(BIM_DisableIrq(VECTOR_LZHS));
        VERIFY(BIM_ClearIrq(VECTOR_LZHS));
    }

    return fgOrg;
}

#endif /* CC_MTK_LOADER */

INT32 VerifySignatureEnable = TRUE;
void LZHS_VerifySignatureSetting(INT32 i32Enable)
{
    VerifySignatureEnable = i32Enable;
}

//-----------------------------------------------------------------------------
/** Brief of LZHS_Dec.
 *  Details of LZHS_Dec
 *  @retval 0   Success
 *  @retval 1   Fail
 */
//-----------------------------------------------------------------------------
INT32 LZHS_Dec(UINT32 u4SrcAddr, UINT32 u4DestAddr, UINT32 u4Len, UINT8 *pu1CheckSum)
{
#ifdef CC_MTK_LOADER
    UINT32 u4Reg, u4Addr, i;
    UINT8 u1OrgChecksum;
#ifdef CHANGE_DECOMPRESS_CLK
    UINT32 u4OrgClkReg;
#endif
    BOOL fgFlash;
    UINT32 u4ALEFlag = 0;

#ifdef LZHS_DEBUG
    LOG(0,"[LZHS_Dec] u4SrcAddr 0x%x\n",u4SrcAddr);
    LOG(0,"[LZHS_Dec] u4DestAddr 0x%x\n",u4DestAddr);
    LOG(0,"[LZHS_Dec] u4Len 0x%x\n",u4Len);
    LOG(0,"[LZHS_Dec] *pu1CheckSum 0x%x\n",*pu1CheckSum);
#endif

    u4Len =u4Len+8; //workaround for jump instruction issue. padding 8byte to origer data

#if !defined(CC_SECURE_BOOT_V2) && !defined(CC_SECURE_BOOT_V3) && defined(__GCC__) // skip this check if it is nucleus build
    #if defined(CC_MTK_LOADER) && defined(CC_SECURE_BOOT_ALL)
    if (BIM_IS_SECURE_BOOT && VerifySignatureEnable)
    {
        if (BIM_VerifySignature(
            (UINT32*)((u4SrcAddr + *(UINT32*)(u4SrcAddr-0xc) + 0x10) & ~0xf),
            (UINT32*)(u4SrcAddr - 0x10)) != 0)
        {
            LOG(0,"secure boot fail\n");
            return 2;
        }
        else
        {
            LOG(0,"secure boot ok\n");
        }
    }
    #endif // defined(CC_MTK_LOADER) && defined(CC_SECURE_BOOT_ALL)
#endif // !defined(CC_SECURE_BOOT_V2) && !defined(CC_SECURE_BOOT_V3) && defined(__GCC__)

    LOG(0,"LZHS start\n");
    u1OrgChecksum = *pu1CheckSum;

#ifndef CC_MTK_PRELOADER
    LDR_SetDramTimeLog(16);

#ifndef CC_UBOOT
    //------------------------------------------------------------
    #if 0
    #if !defined(CC_MT5890)
    // Sync instruction and data caches
    HalInstructionMemoryBarrier();

    // Disable MMU and caches
    HalDisableCaches();
    HalDisableMMU();
    #else
    HalDisableDCache();
    HalDisableMMU();
    #endif
    #endif
    HalFlushInvalidateDCache();
    // Wait until com data flush.
    SerWaitTxBufClear();

    // Set to normal mode back.
    SerNormalMode();

    //------------------------------------------------------------
#endif /* CC_UBOOT */
#endif /* CC_MTK_PRELOADER */

    if (((u4DestAddr % LZHS_HW_ALIGNMENT) != 0) || ((u4Len % LZHS_HW_ALIGNMENT) != 0))
    {
        LOG(0,"LZHS decompress address and len must 16-bytes alignment!!\n");
        return 1;
    }

    //----------------------------------------
    // Change clock
#ifdef CHANGE_DECOMPRESS_CLK
    u4OrgClkReg = CKGEN_READ32(REG_CPU_CKCFG);
    u4Reg = u4OrgClkReg;
    u4Reg &= ~0xF;
    u4Reg |= 0xD;   // mem clk
    CKGEN_WRITE32(REG_CPU_CKCFG, u4Reg);
#endif

    //--------------------------------------
    // Enable LZHS and switch clock
    BIM_WRITE32(REG_LZHS_RAMCK, SRAM_CLK_SEL | LZHS_SRAM_EN);

    do {
        u4Reg = BIM_READ32(REG_LZHS_RAMCK);
    } while (u4Reg & LZHS_CHGOK);

    //--------------------------------------
    // Prepare to decode
    BIM_WRITE32(REG_LZHS_CTRL, SWRST | MCKEN);  // reset
    BIM_WRITE32(REG_LZHS_CTRL, MCKEN );

    if ((u4SrcAddr > PBI_A_BASE) && (u4SrcAddr < (PBI_A_BASE + PBI_A_SIZE)))
    {
        // src is flash
        fgFlash = TRUE;
        u4Addr = u4SrcAddr;

        BIM_WRITE32(REG_LZHS_CTRL, SRC_SEL | MCKEN);
    }
    else
    {
        // src is dram
        fgFlash = FALSE;
        u4Addr = u4SrcAddr >> 3;

        BIM_WRITE32(REG_LZHS_CTRL, MCKEN);
    }

    BIM_WRITE32(REG_LZHS_R_ADDR, u4Addr);
    BIM_WRITE32(REG_LZHS_W_ADDR, u4DestAddr>>3);
    BIM_WRITE32(REG_LZHS_W_END_ADDR, (u4DestAddr + u4Len)>>3);

    #if defined(CC_MT5399) || defined(CC_MT5890)
    // only 5399/5861/5890 ES1 MPS1 need ALE_EN
    if(IS_IC_5399() || IS_IC_5861() || IS_IC_5890_ES1() || IS_IC_5890_MPS1())
    {
        u4ALEFlag = ALE_EN;
    }
    #endif

    // Start doecode
    if (fgFlash)
    {
    	BIM_WRITE32(REG_LZHS_CTRL, SRC_SEL | MCKEN | u4ALEFlag);
        LZHS_Delay_us(1);
        BIM_WRITE32(REG_LZHS_CTRL, SRC_SEL | DEC_START | MCKEN | u4ALEFlag);
    }
    else
    {
        BIM_WRITE32(REG_LZHS_CTRL, MCKEN | u4ALEFlag);
        LZHS_Delay_us(1);
        BIM_WRITE32(REG_LZHS_CTRL, DEC_START | MCKEN | u4ALEFlag);
    }

    i = 0;
    while ((BIM_READ32(REG_LZHS_CTRL)&LZHS_INT) == 0x0)
    {
        i++;
        if (i > 0x40000000)
        {
            LOG(0,"decode is not finished within 5 seconds\n");
            return 1;
        }
    } // XXX forever loop.

    //--------------------------------------
    // Disable LZHS and switch clock
    BIM_WRITE32(REG_LZHS_RAMCK, 0x0);
    do {
        u4Reg = BIM_READ32(REG_LZHS_RAMCK);
    } while (u4Reg & LZHS_CHGOK);

    if (((BIM_READ32(REG_LZHS_CTRL)) & CHECKSUM) != u1OrgChecksum)
    {
        LOG(0,"decode checksum is not the same.\n");
        return 1;
    }
    //--------------------------------------
    // Restore clock
#ifdef CHANGE_DECOMPRESS_CLK
    CKGEN_WRITE32(REG_CPU_CKCFG, u4OrgClkReg);
#endif

#ifdef CC_MTK_PRELOADER
    /* Here is preloader */
    BIM_SetTimeLog(3);
#else /* CC_PRELOADER */
    /* Here is uboot loader. */
    LDR_SetDramTimeLog(17);
#endif /* CC_PRELOADER */

    // Disable IRQ only when decoding successfully
#if !defined(CC_MTK_PRELOADER)&&!defined(CC_UBOOT)
#ifdef LOADER_USB_UPGRADE
#ifndef CC_USB_DISABLE
    extern UINT32 _fgupgrading;
    if(!_fgupgrading)
#endif
#endif
#endif
    {
    BIM_WRITE32(REG_IRQEN, 0);
    BIM_WRITE32(REG_IRQCL, 0xffffffff);
    }
    LOG(0,"LZHS done\n");

#else /* CC_MTK_LOADER */
    UINT32 u4Reg, u4Addr;
    INT32 i4Ret;
    HAL_TIME_T time1, time2, timediff;
    BOOL fgFlash;
    UINT8 u1CheckSum;
#ifdef CHANGE_DECOMPRESS_CLK
    UINT32 u4OrgClkReg;
#endif
    UINT32 u4ALEFlag = 0;

    LOG(0,"LZHS hardware decode\n");
    if (((u4DestAddr % LZHS_HW_ALIGNMENT) != 0) || ((u4Len % LZHS_HW_ALIGNMENT) != 0))
    {
        LOG(0,"LZHS decompress address and len must 16-bytes alignment!!\n");
        return 1;
    }
    u4Len =u4Len+8; //workaround for jump instruction issue. padding 8byte to origer da

    // Flush DCache
    HalFlushInvalidateDCache();

    //----------------------------------------
    // Change clock
#ifdef CHANGE_DECOMPRESS_CLK
    u4OrgClkReg = CKGEN_READ32(REG_CPU_CKCFG);
    u4Reg = u4OrgClkReg;
    u4Reg &= ~0xF;
    u4Reg |= 0xD;   // mem clk
    CKGEN_WRITE32(REG_CPU_CKCFG, u4Reg);
#endif

    //--------------------------------------
    // Enable LZHS and switch clock
    BIM_WRITE32(REG_LZHS_RAMCK, SRAM_CLK_SEL | LZHS_SRAM_EN);
    do {
        u4Reg = BIM_READ32(REG_LZHS_RAMCK);
    } while (u4Reg & LZHS_CHGOK);

    //--------------------------------------
    // Decode
    BIM_WRITE32(REG_LZHS_CTRL, SWRST | MCKEN);  // reset
    BIM_WRITE32(REG_LZHS_CTRL, MCKEN);

    fgFlash = FALSE;
    if ((u4SrcAddr > PBI_A_BASE) && (u4SrcAddr < (PBI_A_BASE + PBI_A_SIZE)))
    {
        // src is flash
        fgFlash = TRUE;
        u4Addr = u4SrcAddr - PBI_A_BASE;
        BIM_WRITE32(REG_LZHS_CTRL, SRC_SEL | MCKEN); // Must before set address register
    }
    else
    {
        // src is dram
        fgFlash = FALSE;
        u4Addr = u4SrcAddr >> 3;
        BIM_WRITE32(REG_LZHS_CTRL, MCKEN );  // Must before set address register
    }

    BIM_WRITE32(REG_LZHS_R_ADDR, u4Addr);
    BIM_WRITE32(REG_LZHS_W_ADDR, u4DestAddr>>3);
    BIM_WRITE32(REG_LZHS_W_END_ADDR, (u4DestAddr + u4Len)>>3);

    HAL_GetTime(&time1);

    #if defined(CC_MT5399) || defined(CC_MT5890)
    // only 5399/5861/5890 ES1 MPS1 need ALE_EN
    if(IS_IC_5399() || IS_IC_5861() || IS_IC_5890_ES1() || IS_IC_5890_MPS1())
    {
        u4ALEFlag = ALE_EN;
    }
    #endif

	if (fgFlash)
	{
        BIM_WRITE32(REG_LZHS_CTRL, SRC_SEL | MCKEN | u4ALEFlag);
        LZHS_Delay_us(1);
        BIM_WRITE32(REG_LZHS_CTRL, SRC_SEL | DEC_START | MCKEN | u4ALEFlag);
    }
    else
    {
        BIM_WRITE32(REG_LZHS_CTRL, MCKEN | u4ALEFlag);
        LZHS_Delay_us(1);
        BIM_WRITE32(REG_LZHS_CTRL, DEC_START | MCKEN | u4ALEFlag);
    }

    i4Ret = 0;
    if (_fgIsrEn)
    {
        i4Ret = x_sema_lock_timeout(_hLZHSSema, LZHS_TIMEOUT);
    }
    else
    {
        while((BIM_READ32(REG_LZHS_CTRL)& LZHS_INT) == 0x0)
        {
            ;
        }
    }

    //----------------------------------------
#ifdef CHANGE_DECOMPRESS_CLK
    CKGEN_WRITE32(REG_CPU_CKCFG, u4OrgClkReg);
#endif

    HAL_GetTime(&time2);

    u1CheckSum = (UINT8)((BIM_READ32(REG_LZHS_CTRL)) & CHECKSUM);

    //--------------------------------------
    // Disable LZHS and switch clock
    BIM_WRITE32(REG_LZHS_RAMCK, 0);
    do {
        u4Reg = BIM_READ32(REG_LZHS_RAMCK);
    } while (u4Reg & LZHS_CHGOK);

    if (_fgIsrEn && (i4Ret == OSR_TIMEOUT))
    {
        LOG(0,"LZHS hardware decode fail !\n");
        return 1;
    }
    else
    {
        LOG(0,"LZHS hardware decode done, CheckSum: 0x%X\n", u1CheckSum);
        HAL_GetDeltaTime(&timediff, &time1, &time2);
        LOG(0," time: %d sec, %d usec\n", timediff.u4Seconds, timediff.u4Micros);
    }

    if (pu1CheckSum != NULL)
    {
        *pu1CheckSum = u1CheckSum;
    }
    else
    {
        LOG(0,"LZHS return checksum pointer is NULL\n");
    }

#endif /* CC_MTK_LOADER */
    return 0;
}

#if defined(CC_MT5880)||defined(CC_MT5881)
UINT32 LZMA_Dec(UINT32 u4DstAddr, UINT32 u4DstSize, UINT32 u4SrcAddr, UINT32 u4SrcSize, UINT8 *pu1CheckSum)
{
#ifdef CC_MTK_LOADER
    UINT32 u4Addr;
    #ifdef LZHS_DEBUG
    HAL_TIME_T time1, time2, timediff;
	#endif
    BOOL fgFlash;
    UINT8 u1CheckSum,u1OrgChecksum=0;
    PFN_IMAGE pfnImage;
	UNUSED(fgFlash);
    Printf("LZMA hardware decode\n");

#ifdef LZHS_DEBUG
    Printf("[LZMA_Dec] u4DstAddr 0x%x\n",u4DstAddr);
    Printf("[LZMA_Dec] u4DstSize 0x%x\n",u4DstSize);
    Printf("[LZMA_Dec] u4SrcAddr 0x%x\n",u4SrcAddr);
    Printf("[LZMA_Dec] u4SrcSize 0x%x\n",u4SrcSize);
    Printf("[LZMA_Dec] *pu1CheckSum 0x%x\n",*pu1CheckSum);
#endif

#if defined(CC_MTK_LOADER) && defined(CC_SECURE_BOOT_ALL)
		if (BIM_IS_SECURE_BOOT && VerifySignatureEnable)
		{
			if (BIM_VerifySignature(
				(UINT32*)((u4SrcAddr + *(UINT32*)(u4SrcAddr-0xc) + 0x10) & ~0xf),
				(UINT32*)(u4SrcAddr - 0x10)) != 0)
			{
				Printf("secure boot fail\n");
				return 2;
			}
			else
			{
				Printf("secure boot ok\n");
			}
		}
#endif
    if (((u4DstAddr % LZHS_HW_ALIGNMENT) != 0) || ((u4SrcAddr % LZHS_HW_ALIGNMENT) != 0))
    {
        Printf("LZMA u4DstAddr and u4SrcAddr must be 16-bytes alignment!!\n");
        return 1;
    }
	if(pu1CheckSum != NULL)
	{
    u1OrgChecksum = *pu1CheckSum;
	}
    // Flush DCache
    HalFlushInvalidateDCache();

    //--------------------------------------
    // Enable LZMA and switch clock
    BIM_WRITE32(LZMA_REG0, LZMA_CLK_EN);
    BIM_WRITE32(REG_LZHS_RAMCK, 0x10);

    fgFlash = FALSE;
    if ((u4SrcAddr > PBI_A_BASE) && (u4SrcAddr < (PBI_A_BASE + PBI_A_SIZE)))
    {
        // src is flash
        fgFlash = TRUE;
        u4Addr = u4SrcAddr - PBI_A_BASE;
        BIM_WRITE32(LZMA_REG0, LZMA_CLK_EN | (u4Addr & LZMA_SRC_ADR)); // Must before set address register
    }
    else
    {
        // src is dram
        fgFlash = FALSE;
        u4Addr = u4SrcAddr >> 4;
        BIM_WRITE32(LZMA_REG0, LZMA_SRC_SEL | LZMA_CLK_EN | (u4Addr & LZMA_SRC_ADR));  // Must before set address register
    }

    BIM_WRITE32(LZMA_INSIZE, u4SrcSize); // File size compressed by LZMA
    BIM_WRITE32(LZMA_OUTSIZE, u4DstSize); // Original file size

    #ifdef LZHS_DEBUG
    HAL_GetTime(&time1);
    #endif
    BIM_WRITE32(LZMA_REG2, LZMADEC_TRIG | ((u4DstAddr >> 4) & LZMA_DEST_ADR));

    while ((BIM_READ32(LZMA_REG2)& LZMA_RDY) == 0x0)
    {
        ;
    }
    #ifdef LZHS_DEBUG
    HAL_GetTime(&time2);
    #endif
    u1CheckSum = (UINT8)((BIM_READ32(LZMA_CHECKSUM)) & 0xff);

    // clear RDY
    BIM_WRITE32(LZMA_REG2, LZMA_RDY_CLR);
    while((BIM_READ32(LZMA_REG2)& LZMA_RDY)==LZMA_RDY)
    {
        ;
    }
    BIM_WRITE32(LZMA_REG2, 0);

    // disable LZMA and switch clock
    BIM_WRITE32(REG_LZHS_RAMCK, 0);
    BIM_WRITE32(LZMA_REG0, 0);

    Printf("LZMA hardware decode done, CheckSum: 0x%X\n", u1CheckSum);

    if(u1CheckSum != u1OrgChecksum)
    {
        Printf("LZMA hardware decode done, CheckSum failed");
        return 1;
    }
   #ifdef LZHS_DEBUG
    HAL_GetDeltaTime(&timediff, &time1, &time2);
    Printf(" time: %d sec, %d usec\n", timediff.u4Seconds, timediff.u4Micros);
   #endif
    if (pu1CheckSum != NULL)
    {
        *pu1CheckSum = u1CheckSum;
    }
    else
    {
        Printf("LZMA return checksum pointer is NULL\n");
    }
    #ifndef CC_MTK_PRELOADER
    {
    BIM_WRITE32(REG_IRQEN, 0);
    BIM_WRITE32(REG_IRQCL, 0xffffffff);
    }
	#endif
#ifdef CC_MTK_PRELOADER
		pfnImage = (PFN_IMAGE)u4DstAddr;
		pfnImage();
#else /* CC_MTK_PRELOADER */
		UNUSED(pfnImage);
#endif /* CC_MTK_PRELOADER */
#else
    UINT32 u4Addr;
    HAL_TIME_T time1, time2, timediff;
    BOOL fgFlash;
    UINT8 u1CheckSum,u1OrgChecksum =0;

    Printf("LZMA hardware decode\n");

#ifdef LZHS_DEBUG
    Printf("[LZMA_Dec] u4DstAddr 0x%x\n",u4DstAddr);
    Printf("[LZMA_Dec] u4DstSize 0x%x\n",u4DstSize);
    Printf("[LZMA_Dec] u4SrcAddr 0x%x\n",u4SrcAddr);
    Printf("[LZMA_Dec] u4SrcSize 0x%x\n",u4SrcSize);
    Printf("[LZMA_Dec] *pu1CheckSum 0x%x\n",*pu1CheckSum);
#endif

    if (((u4DstAddr % LZHS_HW_ALIGNMENT) != 0) || ((u4SrcAddr % LZHS_HW_ALIGNMENT) != 0))
    {
        Printf("LZMA u4DstAddr and u4SrcAddr must be 16-bytes alignment!!\n");
        return 1;
    }
	if(pu1CheckSum != NULL)
	{
    u1OrgChecksum = *pu1CheckSum;
	}
    // Flush DCache
    HalFlushInvalidateDCache();

    //--------------------------------------
    // Enable LZMA and switch clock
    BIM_WRITE32(LZMA_REG0, LZMA_CLK_EN);
    BIM_WRITE32(REG_LZHS_RAMCK, 0x10);

    fgFlash = FALSE;
    if ((u4SrcAddr > PBI_A_BASE) && (u4SrcAddr < (PBI_A_BASE + PBI_A_SIZE)))
    {
        // src is flash
        fgFlash = TRUE;
        u4Addr = u4SrcAddr - PBI_A_BASE;
        BIM_WRITE32(LZMA_REG0, LZMA_CLK_EN | (u4Addr & LZMA_SRC_ADR)); // Must before set address register
    }
    else
    {
        // src is dram
        fgFlash = FALSE;
        u4Addr = u4SrcAddr >> 4;
        BIM_WRITE32(LZMA_REG0, LZMA_SRC_SEL | LZMA_CLK_EN | (u4Addr & LZMA_SRC_ADR));  // Must before set address register
    }

    BIM_WRITE32(LZMA_INSIZE, u4SrcSize); // File size compressed by LZMA
    BIM_WRITE32(LZMA_OUTSIZE, u4DstSize); // Original file size

    HAL_GetTime(&time1);

    BIM_WRITE32(LZMA_REG2, LZMADEC_TRIG | ((u4DstAddr >> 4) & LZMA_DEST_ADR));

    while ((BIM_READ32(LZMA_REG2)& LZMA_RDY) == 0x0)
    {
        ;
    }

    HAL_GetTime(&time2);

    u1CheckSum = (UINT8)((BIM_READ32(LZMA_CHECKSUM)) & 0xff);

    // clear RDY
    BIM_WRITE32(LZMA_REG2, LZMA_RDY_CLR);
    while((BIM_READ32(LZMA_REG2)& LZMA_RDY)==LZMA_RDY)
    {
        ;
    }
    BIM_WRITE32(LZMA_REG2, 0);

    // disable LZMA and switch clock
    BIM_WRITE32(REG_LZHS_RAMCK, 0);
    BIM_WRITE32(LZMA_REG0, 0);

    Printf("LZMA hardware decode done, CheckSum: 0x%X\n", u1CheckSum);

    if(u1CheckSum != u1OrgChecksum)
    {
        Printf("LZMA hardware decode done, CheckSum failed");
        return 1;
    }

    HAL_GetDeltaTime(&timediff, &time1, &time2);
    Printf(" time: %d sec, %d usec\n", timediff.u4Seconds, timediff.u4Micros);

    if (pu1CheckSum != NULL)
    {
        *pu1CheckSum = u1CheckSum;
    }
    else
    {
        Printf("LZMA return checksum pointer is NULL\n");
    }

    UNUSED(fgFlash);

#endif
    return 0;
}
#endif //CC_MT5880

