/*
 * Copyright (c) 2013-2019 Huawei Technologies Co., Ltd. All rights reserved.
 * Copyright (c) 2020-2021 Huawei Device Co., Ltd. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this list of
 *    conditions and the following disclaimer.
 *
 * 2. 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.
 *
 * 3. 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.
 */

#include "los_init_pri.h"
#include "los_atomic.h"
#include "los_config.h"
#include "los_hw.h"
#include "los_printf.h"
#include "los_spinlock.h"
#include "los_typedef.h"

#ifdef LOS_INIT_DEBUG
#include "los_sys_pri.h"
#include "los_tick.h"
#endif

/**
 * Register kernel init level labels.
 */
OS_INIT_LEVEL_REG(kernel, 10, g_kernInitLevelList);

STATIC volatile UINT32 g_initCurrentLevel = OS_INVALID_VALUE;
STATIC volatile struct ModuleInitInfo *g_initCurrentModule = 0;
STATIC Atomic g_initCount = 0;
STATIC SPIN_LOCK_INIT(g_initLock);

/**
 * It is recommended that each startup framework encapsulate a layer of its own calling interface.
 */
STATIC VOID InitLevelCall(const CHAR *name, const UINT32 level, struct ModuleInitInfo *initLevelList[])
{
    struct ModuleInitInfo *module = NULL;
#ifdef LOS_INIT_DEBUG
    UINT64 startNsec, endNsec;
    UINT64 totalTime = 0;
    UINT64 singleTime;
    UINT32 ret = LOS_OK;
#endif

    if (ArchCurrCpuid() == 0) {
#ifdef LOS_INIT_DEBUG
        PRINTK("-------- %s Module Init... level = %u --------\n", name, level);
#endif
        g_initCurrentLevel = level;
        g_initCurrentModule = initLevelList[level];
    } else {
        while (g_initCurrentLevel < level) {
        }
    }

    do {
        LOS_SpinLock(&g_initLock);
        if (g_initCurrentModule >= initLevelList[level + 1]) {
            LOS_SpinUnlock(&g_initLock);
            break;
        }
        module = (struct ModuleInitInfo *)g_initCurrentModule;
        g_initCurrentModule++;
        LOS_SpinUnlock(&g_initLock);
        if (module->hook != NULL) {
#ifdef LOS_INIT_DEBUG
            ret = LOS_OK;
            startNsec = LOS_CurrNanosec();
            ret = (UINT32)module->hook();
            endNsec = LOS_CurrNanosec();
            singleTime = endNsec - startNsec;
            totalTime += singleTime;
            PRINTK("Starting %s module consumes %llu ns. Run on cpu %u\n", module->name, singleTime, ArchCurrCpuid());
#else
            module->hook();
#endif
        }
#ifdef LOS_INIT_DEBUG
        if (ret != LOS_OK) {
            PRINT_ERR("%s initialization failed at module %s, function addr at 0x%x, ret code is %u\n",
                      name, module->name, module->hook, ret);
        }
#endif
    } while (1);

    if (level >= LOS_INIT_LEVEL_KMOD_TASK) {
        LOS_AtomicInc(&g_initCount);
        while ((LOS_AtomicRead(&g_initCount) % LOSCFG_KERNEL_CORE_NUM) != 0) {
        }
    }

#ifdef LOS_INIT_DEBUG
    PRINTK("%s initialization at level %u consumes %lluns on cpu %u.\n", name, level, totalTime, ArchCurrCpuid());
#endif
}

VOID OsInitCall(const UINT32 level)
{
    if (level >= LOS_INIT_LEVEL_FINISH) {
        return;
    }

    InitLevelCall("Kernel", level, g_kernInitLevelList);
}
