/*******************************************************************************
 * Copyright (c) 2018-2021 Renesas Electronics Corporation. All rights reserved.
 *
 *     RENESAS ELECTRONICS CONFIDENTIAL AND PROPRIETARY
 *
 * This software is provided as reference/sample code under the license
 * agreement between Renesas Electronics Corporation and licensee (the
 * "License Agreement") and shall be treated as specified in the License
 * Agreement.
 * These instructions, statements, and software are the confidential
 * information of Renesas Electronics Corporation. They must be used and
 * modified solely for the purpose for which it was furnished by Renesas
 * Electronics Corporation. All or part of these instructions, statements and
 * software must not be reproduced nor disclosed to any third party in any
 * form, unless permitted by the License Agreement.
 *
 * THIS SOFTWARE IS PROVIDED BY RENESAS ELEOCTRONICS CORPORATION "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
 * SATISFACTORY QUALITY, ACCURACY, TITLE AND NON-INFRINGEMENT ARE DISCLAIMED.
 * IN NO EVENT SHALL RENESAS ELECTRONICS CORPORATION BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, PUNITIVE, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * 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.
 ******************************************************************************/

/*******************************************************************************
 * DESCRIPTION   : Power management driver
 ******************************************************************************/
/******************************************************************************
 * @file          cpu_on.c
 * - Version      : 0.01
 * @brief         Boot process of ARM CPU.
 * .
 *****************************************************************************/
/******************************************************************************
 * History : DD.MM.YYYY Version  Description
 *         : 03.07.2020 0.01     First Release
 *****************************************************************************/

#include <stdint.h>
#include <mem_io.h>
#include <cpu_on.h>
#include <rst_register.h>
#include <sysc_register.h>
#include <cpg.h>
#include <cpg_register.h>
#include <acc_prot_lifec.h>

#define BIT_CA53_SCU        ((uint32_t)1U << 21U)
#define BIT_CR7             ((uint32_t)1U << 13U)
#define REQ_RESUME          ((uint32_t)1U << 1U)
#define NOT_REQ_RESUME      ((uint32_t)0U << 1U)
#define SYSC_STATUS_PWRDOWN ((uint32_t)1U << 0U)
#define SYSC_STATUS_PWRUP   ((uint32_t)1U << 4U)
#define APMU_STATUS_PWRUP   ((uint32_t)3U << 0U)
#define CPG_SRSTCLR2_CR7    ((uint32_t)1U << 22U)

#define RST_UPPER_VALUE     ((uint32_t)0x5A5A0000U);

#define CA53_CORE_NUM           (0U)
#define CA53_CORE_NUM_MAX       (3U)
#define CA53_CORE_WAKEUP_COMP   (0U)
#define CR7_CORE_STANDBY        (0x00000003U)
#define CR7_CORE_RUNMODE        (0x00000000U)
#define PWRONCR_PWRUP_START     (0x00000001U)
#define PWRER_ERR               (0x00000001U)
#define PWRER_NOT_ERR           (0x00000000U)
#define PWR_RESUME_COMP         (0U)
#define SYSC_NOT_PWRDOWN_FLAG   (0x00000000U)

static void domain_power_on(uint32_t target);

void arm_cpu_set_address(uint32_t target, uint32_t boot_addr)
{
    if(RCAR_PWR_TARGET_CR7 == target)
    {
        /* CR7 Boot address set */
        lifec_set_master_grp(LIFEC_TGT_M_ICUMX, LIFEC_PUBLIC,
            LIFEC_GROUP3);
        mem_write32(RST_CR7BAR2, (uint32_t)(boot_addr | RST_CR7BAR2_VLD));
        mem_write32(RST_CR7BAR2, (uint32_t)(boot_addr
                                | RST_CR7BAR2_VLD | RST_CR7BAR_BAREN));
    }
    else
    {
        /* CA53 Boot address set */
        mem_write32(RST_CA53CPU0BARL, boot_addr);
        mem_write32(RST_CA53CPU0BARH, 0x00000000U);
    }
}
/* End of function arm_cpu_set_address(uint32_t target, uint32_t boot_addr) */

void arm_cpu_on(uint32_t target, uint32_t boot_addr)
{
    uint32_t res_data;

    if(RCAR_PWR_TARGET_CR7 == target)
    {
        /* CR7 power supply */
        domain_power_on(target);
        /* Wait until Power ON */
        do
        {
            res_data = mem_read32(SYSC_PWRSR7);
        }while(((uint32_t)(SYSC_STATUS_PWRUP) & res_data)
            != (uint32_t)(SYSC_STATUS_PWRUP));

        do
        {
            res_data = mem_read32(APMU_CR7PSTR);
        }while(((uint32_t)APMU_STATUS_PWRUP & res_data)
            != CR7_CORE_RUNMODE);
        /* CR7 reset */
        mem_write32(CPG_SRSTCLR2, CPG_SRSTCLR2_CR7);
    }
    else
    {
        /* CA53 SCU power supply */
        domain_power_on(target);
        /* CA53 Boot address set */
        arm_cpu_set_address(target, boot_addr);
        /* CA53 core0 power supply */
        mem_write32(APMU_CA53WUPCR, ((uint32_t)1U << CA53_CORE_NUM));
        /* Wait until CA53 core0 power on */
        do
        {
            res_data = mem_read32(APMU_CA53WUPCR);
        }while(((uint32_t)((uint32_t)1U << CA53_CORE_NUM) & res_data)
            != CA53_CORE_WAKEUP_COMP);
        /* CA53 core0 reset */
        res_data = mem_read32(RST_CA53RESCNT) | RST_UPPER_VALUE;
        mem_write32(RST_CA53RESCNT, (res_data &
        (~((uint32_t)1U << (CA53_CORE_NUM_MAX - CA53_CORE_NUM)))));
    }
}
/* End of function arm_cpu_on(uint32_t target, uint32_t boot_addr) */

static void domain_power_on(uint32_t target)
{
    const uint32_t reg_SYSCIER = SYSC_SYSCIER;
    const uint32_t reg_SYSCIMR = SYSC_SYSCIMR;
    const uint32_t reg_SYSCSR = SYSC_SYSCSR;
    const uint32_t reg_SYSCISR = SYSC_SYSCISR;
    const uint32_t reg_SYSCISCR = SYSC_SYSCISCR;
    uint32_t reg_PWRONCR;
    uint32_t reg_PWRSR;
    uint32_t reg_PWRER;
    uint32_t reg_SYSC_bit;

    if(RCAR_PWR_TARGET_CR7 == target)
    {
        reg_PWRONCR = SYSC_PWRONCR7;
        reg_PWRSR = SYSC_PWRSR7;
        reg_PWRER = SYSC_PWRER7;
        reg_SYSC_bit = (uint32_t)BIT_CR7;
    }
    else
    {
        reg_PWRONCR = SYSC_PWRONCR3;
        reg_PWRSR = SYSC_PWRSR3;
        reg_PWRER = SYSC_PWRER3;
        reg_SYSC_bit = (uint32_t)BIT_CA53_SCU;
    }

    if ((mem_read32(reg_PWRSR) & (uint32_t)(SYSC_STATUS_PWRDOWN))
        != SYSC_NOT_PWRDOWN_FLAG)
    {
        /* Enable status update interrupt */
        mem_write32(reg_SYSCIER,
            (mem_read32(reg_SYSCIER) | reg_SYSC_bit));
        mem_write32(reg_SYSCIMR,
            (mem_read32(reg_SYSCIMR) | reg_SYSC_bit));
        do
        {
            /* Wait until it is ready to accept the power resume request of the CPU */
            while ((mem_read32(reg_SYSCSR) &
                (uint32_t)(REQ_RESUME)) == NOT_REQ_RESUME) 
            {
                ;
            }
            /* Set the power resume of CPU */
            mem_write32(reg_PWRONCR, PWRONCR_PWRUP_START);
        } while ((mem_read32(reg_PWRER) & PWRER_ERR) != PWRER_NOT_ERR);

        /* Wait until power resume processing is completed */
        while ((mem_read32(reg_SYSCISR) & reg_SYSC_bit) == PWR_RESUME_COMP) 
        {
            ;
        }
        /* Clear the status of SYSCISR */
        mem_write32(reg_SYSCISCR, reg_SYSC_bit);
    }
}
/* End of function domain_power_on(uint32_t target) */
