/*
 * Copyright (c) 2015-2021, Renesas Electronics Corporation
 * All rights reserved.
 */

/******************************************************************************
 * @file          ecc.c
 * - Version      : 0.01
 * @brief         Initial setting process of ECC.
 * .
 *****************************************************************************/
/******************************************************************************
 * History : DD.MM.YYYY Version  Description
 *         : 29.05.2020 0.01     First Release
 *****************************************************************************/

#include "debug.h"
#include "mmio.h"
#include "ecc.h"

/* AXI-Bus registers definitions */
#define AXI_BASE            (0xE6784000U)

/* System RAM ECC Register */
#define AXI_SFUSAAREACR0    (AXI_BASE+0x1100U)
#define AXI_SECCAREACR0     (AXI_BASE+0x1140U)

/* SDRAM ECC Register */
#define DFUSAAREACR0        (AXI_BASE + 0x1000U)
#define DECCAREACR0         (AXI_BASE + 0x1040U)

/* Error Detection Register */
#define EDCEN0              (0xFF840010U)
#define EDCEN1              (0xFF840014U)
#define EDCINTEN0           (0xFF840040U)
#define EDCINTEN1           (0xFF840044U)
#define EDCINTEN2           (0xFF840048U)
#define EDCINTEN3           (0xFF84004CU)
#define EDCINTEN4           (0xFF840190U)

/** EDC Enable Register 0 */
/* Bit[31]      : CCI           1:Enable    */
/* Bit[30]      : CR7           1:Enable    */
/* Bit[29]      : DMAC          1:Enable    */
/* Bit[28]      : GPU           1:Enable    */
/* Bit[27]      : IMP           1:Enable    */
/* Bit[26:25]       : Reserved              */
/* Bit[24]      : ETH           1:Enable    */
/* Bit[23:22]       : Reserved              */
/* Bit[21]      : IPMMUVI       1:Enable    */
/* Bit[20]      : IPMMURT       1:Enable    */
/* Bit[19]      : IPMMUIR       1:Enable    */
/* Bit[18]      : IPMMUDS0      1:Enable    */
/* Bit[17]      : IPMMUDS1      1:Enable    */
/* Bit[16]      : IPMMUPV       1:Enable    */
/* Bit[15]      : MEM           1:Enable    */
/* Bit[14:11]       : Reserved              */
/* Bit[10]      : ARMCNT        1:Enable    */
/* Bit[9]       : RST           1:Enable    */
/* Bit[8]       : INTAP         1:Enable    */
/* Bit[7]       : INTRT         1:Enable    */
/* Bit[6]       : BOOTROM       1:Enable    */
/* Bit[5]       : MFIS          1:Enable    */
/* Bit[4]       : Reserved                  */
/* Bit[3]       : LIFEC1        1:Enable    */
/* Bit[2]       : LIFEC0        1:Enable    */
/* Bit[1:0]     : Reserved                  */
#define EDCEN0_ALL_EN           (0xF93F87ECU)

/** EDC Enable Register 1 */
/* Bit[31:19]       : Reserved              */
/* Bit[18]      : IPMMUVC       1:Enable    */
/* Bit[17]      : FCPCS         1:Enable    */
/* Bit[16]      : INTC          1:Enable    */
/* Bit[15]      : SYSC          1:Enable    */
/* Bit[14]      : PFC           1:Enable    */
/* Bit[13]      : CPG           1:Enable    */
/* Bit[12]      : Reserved                  */
/* Bit[11]      : iV1E          1:Enable    */
/* Bit[10]      : FCPVD         1:Enable    */
/* Bit[9]       : IMR           1:Enable    */
/* Bit[8]       : ISP           1:Enable    */
/* Bit[7]       : VIN           1:Enable    */
/* Bit[6]       : Reserved                  */
/* Bit[5]       : FRAY          1:Enable    */
/* Bit[4]       : IPC           1:Enable    */
/* Bit[3]       : CAIP          1:Enable    */
/* Bit[2]       : SDHI          1:Enable    */
/* Bit[1]       : RTDMAC        1:Enable    */
/* Bit[0]       : STBE          1:Enable    */
#define EDCEN1_ALL_EN           (0x0007EFBFU)

/** EDC Interrupt Enable Register 0 */
/* Bit[31]      : IPMMUDS0      1:Enable    */
/* Bit[30]      : IPMMUDS1      1:Enable    */
/* Bit[29]      : IPMMUIR       1:Enable    */
/* Bit[28]      : IMP1          1:Enable    */
/* Bit[27]      : IMP0          1:Enable    */
/* Bit[26]      : IMP_SLAVE     1:Enable    */
/* Bit[25:19]       : Reserved              */
/* Bit[18]      : IPMMURT       1:Enable    */
/* Bit[17:15]       : Reserved              */
/* Bit[14]      : MFIS          1:Enable    */
/* Bit[13]      : INTAP         1:Enable    */
/* Bit[12]      : INTRT         1:Enable    */
/* Bit[11]      : BOOTROM       1:Enable    */
/* Bit[10]      : CR7           1:Enable    */
/* Bit[9]       : IPMMUPV       1:Enable    */
/* Bit[8]       : MEM3          1:Enable    */
/* Bit[7]       : MEM2          1:Enable    */
/* Bit[6]       : MEM1          1:Enable    */
/* Bit[5]       : MEM0          1:Enable    */
/* Bit[4]       : RGX_SLAVE     1:Enable    */
/* Bit[3]       : RGX3          1:Enable    */
/* Bit[2]       : RGX2          1:Enable    */
/* Bit[1]       : RGX1          1:Enable    */
/* Bit[0]       : RGX0          1:Enable    */
#define EDCINTEN0_ALL_EN        (0xFC047FFFU)

/** EDC Interrupt Enable Register 1 */
/* Bit[31]      : DRAM          1:Enable    */
/* Bit[30]      : DRAM_MLT      1:Enable    */
/* Bit[29]      : DRAM_SGL      1:Enable    */
/* Bit[28]      : TR2           1:Enable    */
/* Bit[27]      : TR1           1:Enable    */
/* Bit[26]      : TR0           1:Enable    */
/* Bit[25]      : Reserved                  */
/* Bit[24]      : RST           1:Enable    */
/* Bit[23]      : ARMCNT        1:Enable    */
/* Bit[22]      : APS0          1:Enable    */
/* Bit[21]      : LIFEC1        1:Enable    */
/* Bit[20]      : LIFEC0        1:Enable    */
/* Bit[19]      : ETHER         1:Enable    */
/* Bit[18]      : Reserved                  */
/* Bit[17]      : ETHER(X2X)    1:Enable    */
/* Bit[16]      : SYSD1         1:Enable    */
/* Bit[15]      : SYSD2         1:Enable    */
/* Bit[14]      : SYSD(X2X)     1:Enable    */
/* Bit[13]      : CR7(utlb)     1:Enable    */
/* Bit[12:0]        : Reserved              */
#define EDCINTEN1_ALL_EN        (0xFDFBE000U)

/** EDC Interrupt Enable Register 2 */
/* Bit[31]      : X2X128to64_RT 1:Enable    */
/* Bit[30]      : X2X_APVI2     1:Enable    */
/* Bit[29]      : X2X_SDS2      1:Enable    */
/* Bit[28]      : X2X_APVI1     1:Enable    */
/* Bit[27]      : ISP_Slave     1:Enable    */
/* Bit[26]      : X2X_APVI0     1:Enable    */
/* Bit[25]      : VIN0          1:Enable    */
/* Bit[24]      : ISP1          1:Enable    */
/* Bit[23]      : ISP0          1:Enable    */
/* Bit[22]      : IMR23         1:Enable    */
/* Bit[21]      : IMR01         1:Enable    */
/* Bit[20]      : FCPVD         1:Enable    */
/* Bit[19]      : iV1E          1:Enable    */
/* Bit[18]      : Reserved                  */
/* Bit[17]      : IPMMUVI       1:Enable    */
/* Bit[16]      : PFC           1:Enable    */
/* Bit[15]      : SYSC          1:Enable    */
/* Bit[14]      : INTC          1:Enable    */
/* Bit[13]      : FCPCS         1:Enable    */
/* Bit[12]      : CAIP          1:Enable    */
/* Bit[11]      : CPG           1:Enable    */
/* Bit[10]      : IMR0          1:Enable    */
/* Bit[9]       : IMR1          1:Enable    */
/* Bit[8]       : IMR2          1:Enable    */
/* Bit[7]       : IMR3          1:Enable    */
/* Bit[6]       : ISPC          1:Enable    */
/* Bit[5]       : VIN0          1:Enable    */
/* Bit[4]       : VIN1          1:Enable    */
/* Bit[3]       : VIN2          1:Enable    */
/* Bit[2]       : VIN3          1:Enable    */
/* Bit[1]       : Reserved                  */
/* Bit[0]       : X2X_iV1E      1:Enable    */
#define EDCINTEN2_ALL_EN        (0xFFFBFFFDU)

/** EDC Interrupt Enable Register 3 */
/* Bit[31]      : X2X_IPC       1:Enable    */
/* Bit[30]      : SYDM1         1:Enable    */
/* Bit[29]      : SYDM2         1:Enable    */
/* Bit[28]      : RTDM0         1:Enable    */
/* Bit[27]      : RTDM1         1:Enable    */
/* Bit[26]      : AHFRAY        1:Enable    */
/* Bit[25]      : X2X_RDS0      1:Enable    */
/* Bit[24]      : X2X_RDS1      1:Enable    */
/* Bit[23]      : X2X_CR7       1:Enable    */
/* Bit[22]      : AXMM_DUP      1:Enable    */
/* Bit[21]      : X2X_AXRT_128  1:Enable    */
/* Bit[20]      : X2X_AXRT_64   1:Enable    */
/* Bit[19]      : X2X_SDS1      1:Enable    */
/* Bit[18]      : AVB_SLAVE     1:Enable    */
/* Bit[17]      : CAIP          1:Enable    */
/* Bit[16]      : X2X_APVC0     1:Enable    */
/* Bit[15]      : IPMMUPV1      1:Enable    */
/* Bit[14]      : IPMMUPV2      1:Enable    */
/* Bit[13]      : IPMMUPV3      1:Enable    */
/* Bit[12]      : VIN1 (master) 1:Enable    */
/* Bit[11]      : VIN4          1:Enable    */
/* Bit[10]      : VIN5          1:Enable    */
/* Bit[9]       : VIN6          1:Enable    */
/* Bit[8]       : VIN7          1:Enable    */
/* Bit[7]       : X2X_AXVC_128  1:Enable    */
/* Bit[6]       : X2X_AXVC_256  1:Enable    */
/* Bit[5]       : FCPCI         1:Enable    */
/* Bit[4]       : FCPCS         1:Enable    */
/* Bit[3]       : VCPLF_CE      1:Enable    */
/* Bit[2]       : VCPLF_VLC     1:Enable    */
/* Bit[1]       : X2X_APSW1     1:Enable    */
/* Bit[0]       : X2X_APSE1     1:Enable    */
#define EDCINTEN3_ALL_EN        (0xFFFFFFFFU)

/** EDC Interrupt Enable Register 4 */
/* Bit[31:3]        : Reserved              */
/* Bit[2]       : IPMMUVC1      1:Enable    */
/* Bit[1]       : IPMMUVC0      1:Enable    */
/* Bit[0]       : X2X_APSW2     1:Enable    */
#define EDCINTEN4_ALL_EN        (0x00000007U)

#define DATA64_ECC8             (1U)
#define DATA8_ECC5              (0U)
#define ECC_ON                  (1U)
#define ECC_OFF                 (0U)
#define FUSA_SIZE_4KB           (0U)
#define FUSA_SIZE_8KB           (1U)
#define FUSA_SIZE_16KB          (2U)
#define FUSA_SIZE_32KB          (3U)
#define FUSA_SIZE_64KB          (4U)
#define FUSA_SIZE_128KB         (5U)
#define FUSA_SIZE_256KB         (6U)
#define DRAM_FUSA_SIZE_1MB      (0U)
#define DRAM_FUSA_SIZE_2MB      (1U)
#define DRAM_FUSA_SIZE_4MB      (2U)
#define DRAM_FUSA_SIZE_8MB      (3U)
#define DRAM_FUSA_SIZE_16MB     (4U)
#define DRAM_FUSA_SIZE_32MB     (5U)
#define DRAM_FUSA_SIZE_64MB     (6U)
#define DRAM_FUSA_SIZE_128MB    (7U)
#define DRAM_FUSA_SIZE_256MB    (8U)
#define DRAM_FUSA_SIZE_512MB    (9U)
#define AREA_NUM                (8U)
#define DRAM_AREA_NUM           (16U)

#define START_ADDRESS_MASK      (0xFFFFF000U)
#define DRAM_40BIT_ADDRESS      (0x400000U)
#define DRAM_START_ADDRESS      (0x40000000U)
#define NON_START_ADDRESS       (0x00000000U)

static inline void dsb(void)
{
    __asm__ __volatile__ ("dsb sy");

}
/* End of function dsb */

/************************************************************************/
/*  System RAM ECC enable                       */
/************************************************************************/
void ecc_init(void)
{
#if RCAR_SYSRAM_ECC_ENABLE == 1
    uint32_t        i;
    uint32_t    ecc_enable[AREA_NUM];

    /* Functional Safety settings */
    /* Area ECC mode                *
     * 64DATA_8ECC = 64bit Data/8bit ECC mode   *
     * 8DATA_5ECC = 8bit Data/5bit ECC mode     *
     *
     * Area FUSA size       *
     * FUSA_SIZE_4KB: 4KB       *
     * FUSA_SIZE_8KB: 8KB       *
     * FUSA_SIZE_16KB: 16KB     *
     * FUSA_SIZE_32KB: 32KB     *
     * FUSA_SIZE_64KB: 64KB     *
     * FUSA_SIZE_128KB: 128KB   *
     * FUSA_SIZE_256KB: 256KB   *
     * Others: Reserved     */
    const struct {
        uint32_t    data_address;
        uint32_t    ecc_address;
        uint32_t    area_ecc_mode;
        uint32_t    ecc_area_size;
    } ecc_setting_table[] = {
        {0xE6350000U,   0xE6358000U,    DATA8_ECC5, FUSA_SIZE_32KB}, /* area0 */
        {0x00000000U,   0x00000000U,    DATA8_ECC5, FUSA_SIZE_4KB}, /* area1 */
        {0x00000000U,   0x00000000U,    DATA8_ECC5, FUSA_SIZE_4KB}, /* area2 */
        {0x00000000U,   0x00000000U,    DATA8_ECC5, FUSA_SIZE_4KB}, /* area3 */
        {0x00000000U,   0x00000000U,    DATA8_ECC5, FUSA_SIZE_4KB}, /* area4 */
        {0x00000000U,   0x00000000U,    DATA8_ECC5, FUSA_SIZE_4KB}, /* area5 */
        {0x00000000U,   0x00000000U,    DATA8_ECC5, FUSA_SIZE_4KB}, /* area6 */
        {0x00000000U,   0x00000000U,    DATA8_ECC5, FUSA_SIZE_4KB} /* area7 */
    };

    /* Address Check */
    for (i = 0; i < AREA_NUM; i++)
    {
        /* FUSA Register Setting */
        if ((ecc_setting_table[i].data_address != NON_START_ADDRESS) ||
            (ecc_setting_table[i].ecc_address != NON_START_ADDRESS))
        {

            ecc_enable[i] = ECC_ON;
        }
        else
        {
            ecc_enable[i] = ECC_OFF;
        }
    }

    /* register param set */
    /* System RAM ECC enable */
    for (i = 0; i < AREA_NUM; i++)
    {
        /* FUSA Register Setting */
        if (ecc_enable[i] == ECC_ON)
        {
            /* ECC Register Setting */
            mmio_write_32(AXI_SECCAREACR0 + (i*4U),
                    (ecc_setting_table[i].area_ecc_mode << 31)
                    | ((ecc_setting_table[i].ecc_address & START_ADDRESS_MASK) >> 12));

            mmio_write_32(AXI_SFUSAAREACR0 + (i*4U),
                    (ECC_ON << 31)
                    | (ecc_setting_table[i].ecc_area_size << 24)
                    | ((ecc_setting_table[i].data_address & START_ADDRESS_MASK) >> 12));
        }
    }

    dsb();
    NOTICE("System RAM ECC enable(CR7)\n");
#endif /* RCAR_SYSRAM_ECC_ENABLE == 1 */
}
/* End of function ecc_init */

void dram_ecc_init(void)
{
#if RCAR_DRAM_ECC_ENABLE == 1

    uint32_t reg;
    uint32_t i;
    uint32_t dram_start_addr;
    uint32_t ecc_enable[DRAM_AREA_NUM];

    /* Functional Safety settings */
    /* Area ECC mode                *
     * 64DATA_8ECC = 64bit Data/8bit ECC mode   *
     * 8DATA_5ECC = 8bit Data/5bit ECC mode     *
     *
     * Area FUSA size               *
     * DRAM_FUSA_SIZE_1MB: 1MB          *
     * DRAM_FUSA_SIZE_2MB: 2MB          *
     * DRAM_FUSA_SIZE_4MB: 4MB          *
     * DRAM_FUSA_SIZE_8MB: 8MB          *
     * DRAM_FUSA_SIZE_16MB: 16MB            *
     * DRAM_FUSA_SIZE_32MB: 32MB            *
     * DRAM_FUSA_SIZE_64MB: 64MB            *
     * DRAM_FUSA_SIZE_128MB: 128MB          *
     * DRAM_FUSA_SIZE_256MB: 256MB          *
     * DRAM_FUSA_SIZE_512MB: 512MB          *
     * Others: Reserved     */
    const struct {
        uint32_t    data_address;
        uint32_t    ecc_address;
        uint32_t    area_ecc_mode;
        uint32_t    ecc_area_size;
    } dram_ecc_setting_table[] = {
        {0x40000000U,   0x40100000U,    DATA8_ECC5, DRAM_FUSA_SIZE_1MB}, /* area0 */
        {0x40200000U,   0x44200000U,    DATA8_ECC5, DRAM_FUSA_SIZE_64MB}, /* area1 */
        {0x48200000U,   0x4C200000U,    DATA8_ECC5, DRAM_FUSA_SIZE_64MB}, /* area2 */
        {0x50200000U,   0x50300000U,    DATA8_ECC5, DRAM_FUSA_SIZE_1MB}, /* area3 */
        {0x50400000U,   0x54400000U,    DATA8_ECC5, DRAM_FUSA_SIZE_64MB}, /* area4 */
        {0x00000000U,   0x00000000U,    DATA8_ECC5, DRAM_FUSA_SIZE_1MB}, /* area5 */
        {0x00000000U,   0x00000000U,    DATA8_ECC5, DRAM_FUSA_SIZE_1MB}, /* area6 */
        {0x00000000U,   0x00000000U,    DATA8_ECC5, DRAM_FUSA_SIZE_1MB}, /* area7 */
        {0x00000000U,   0x00000000U,    DATA8_ECC5, DRAM_FUSA_SIZE_1MB}, /* area8 */
        {0x00000000U,   0x00000000U,    DATA8_ECC5, DRAM_FUSA_SIZE_1MB}, /* area9 */
        {0x00000000U,   0x00000000U,    DATA8_ECC5, DRAM_FUSA_SIZE_1MB}, /* area10 */
        {0x00000000U,   0x00000000U,    DATA8_ECC5, DRAM_FUSA_SIZE_1MB}, /* area11 */
        {0x00000000U,   0x00000000U,    DATA8_ECC5, DRAM_FUSA_SIZE_1MB}, /* area12 */
        {0x00000000U,   0x00000000U,    DATA8_ECC5, DRAM_FUSA_SIZE_1MB}, /* area13 */
        {0x00000000U,   0x00000000U,    DATA8_ECC5, DRAM_FUSA_SIZE_1MB}, /* area14 */
        {0x00000000U,   0x00000000U,    DATA8_ECC5, DRAM_FUSA_SIZE_1MB} /* area15 */
    };

    /* Address Check */
    for (i = 0; i < DRAM_AREA_NUM; i++)
    {
        if ((DRAM_START_ADDRESS <= dram_ecc_setting_table[i].data_address) &&
            (DRAM_START_ADDRESS <= dram_ecc_setting_table[i].ecc_address))
        {
            ecc_enable[i] = ECC_ON;
        }
        else
        {
            ecc_enable[i] = ECC_OFF;
        }
    }

    /* Error Detection Circuit */
    reg = mmio_read_32(EDCEN0);
    reg = (reg | EDCEN0_ALL_EN);
    mmio_write_32(EDCEN0, reg);

    reg = mmio_read_32(EDCEN1);
    reg = (reg | EDCEN1_ALL_EN);
    mmio_write_32(EDCEN1, reg);

    reg = mmio_read_32(EDCINTEN0);
    reg = (reg | EDCINTEN0_ALL_EN);
    mmio_write_32(EDCINTEN0, reg);

    reg = mmio_read_32(EDCINTEN1);
    reg = (reg | EDCINTEN1_ALL_EN);
    mmio_write_32(EDCINTEN1, reg);

    reg = mmio_read_32(EDCINTEN2);
    reg = (reg | EDCINTEN2_ALL_EN);
    mmio_write_32(EDCINTEN2, reg);

    reg = mmio_read_32(EDCINTEN3);
    reg = (reg | EDCINTEN3_ALL_EN);
    mmio_write_32(EDCINTEN3, reg);

    reg = mmio_read_32(EDCINTEN4);
    reg = (reg | EDCINTEN4_ALL_EN);
    mmio_write_32(EDCINTEN4, reg);

    /* register param set */
    /* DRAM ECC enable */
    for (i = 0; i < DRAM_AREA_NUM; i++)
    {
        if (ecc_enable[i] == ECC_ON)
        {
            /* ECC Register Setting */
            /* ECC mode[31] + ECC Start Address[23:0] */
            dram_start_addr = (((dram_ecc_setting_table[i].ecc_address - DRAM_START_ADDRESS) >> 12)) | DRAM_40BIT_ADDRESS;
            mmio_write_32(DECCAREACR0 + (i*4U),
                    (dram_ecc_setting_table[i].area_ecc_mode << 31)
                    | dram_start_addr);

            /* FUSA Register Setting */
            /* FuSa Area enable[31] + FuSa Area size[27:24] + FuSa Start Address[23:0] */
            dram_start_addr = (((dram_ecc_setting_table[i].data_address - DRAM_START_ADDRESS) >> 12)) | DRAM_40BIT_ADDRESS;
            mmio_write_32(DFUSAAREACR0 + (i*4U),
                    (ECC_ON << 31)
                    | (dram_ecc_setting_table[i].ecc_area_size << 24)
                    | dram_start_addr);
        }
    }

    /* log output */
    NOTICE("DRAM ECC enable(CR7)\n");

#endif  /* RCAR_DRAM_ECC_ENABLE == 1 */
}
/* End of function dram_ecc_init */

