diff --git a/bsps/aarch64/xilinx-zynqmp/ecc/cache.c b/bsps/aarch64/xilinx-zynqmp/ecc/cache.c new file mode 100644 index 0000000000..393983a5ed --- /dev/null +++ b/bsps/aarch64/xilinx-zynqmp/ecc/cache.c @@ -0,0 +1,129 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/** + * @file + * + * @ingroup RTEMSBSPsAArch64XilinxZynqMP + * + * @brief This source file contains the implementation of zynqmp_ecc_init(). + */ + +/* + * Copyright (C) 2023 On-Line Applications Research Corporation (OAR) + * Written by Kinsey Moore + * + * 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. + * + * 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 OWNER 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 +#include +#include +#include +#include + +static Cache_Error_RAM_ID get_l1_ramid(uint64_t ramid) +{ + switch (ramid) { + case 0x0: + return RAM_ID_L1I_TAG; + case 0x1: + return RAM_ID_L1I_DATA; + case 0x8: + return RAM_ID_L1D_TAG; + case 0x9: + return RAM_ID_L1D_DATA; + case 0xa: + return RAM_ID_L1D_DIRTY; + case 0x18: + return RAM_ID_TLB; + default: + return RAM_ID_UNKNOWN; + } +} + +static Cache_Error_RAM_ID get_l2_ramid(uint64_t ramid) +{ + switch (ramid) { + case 0x10: + return RAM_ID_L2_TAG; + case 0x11: + return RAM_ID_L2_DATA; + case 0x12: + return RAM_ID_SCU; + default: + return RAM_ID_UNKNOWN; + } +} + +static void cache_handler(void *arg) +{ + uint64_t l1val = _AArch64_Read_cpumerrsr_el1(); + _AArch64_Write_cpumerrsr_el1(l1val); + uint64_t l2val = _AArch64_Read_l2merrsr_el1(); + _AArch64_Write_l2merrsr_el1(l2val); + + (void) arg; + + if (l1val & AARCH64_CPUMERRSR_EL1_VALID) { + /* parse L1 data */ + Cache_Error_Event cerr = {0, }; + cerr.abort = l1val & AARCH64_CPUMERRSR_EL1_FATAL; + cerr.repeats = AARCH64_CPUMERRSR_EL1_REPEATERR_GET(l1val); + cerr.other_errors = AARCH64_CPUMERRSR_EL1_OTHERERR_GET(l1val); + cerr.ramid = get_l1_ramid(AARCH64_CPUMERRSR_EL1_RAMID_GET(l1val)); + cerr.segment = AARCH64_CPUMERRSR_EL1_CPUIDWAY_GET(l1val); + cerr.address = AARCH64_CPUMERRSR_EL1_ADDR_GET(l1val); + + zynqmp_invoke_ecc_handler(L1_CACHE, &cerr); + } + + if (l2val & AARCH64_L2MERRSR_EL1_VALID) { + /* parse L2 data */ + Cache_Error_Event cerr = {0, }; + cerr.abort = l2val & AARCH64_L2MERRSR_EL1_FATAL; + cerr.repeats = AARCH64_L2MERRSR_EL1_REPEATERR_GET(l2val); + cerr.other_errors = AARCH64_L2MERRSR_EL1_OTHERERR_GET(l2val); + cerr.ramid = get_l2_ramid(AARCH64_L2MERRSR_EL1_RAMID_GET(l2val)); + cerr.segment = AARCH64_L2MERRSR_EL1_CPUIDWAY_GET(l2val); + cerr.address = AARCH64_L2MERRSR_EL1_ADDR_GET(l2val); + + zynqmp_invoke_ecc_handler(L2_CACHE, &cerr); + } +} + +static rtems_interrupt_entry zynqmp_cache_ecc_entry; + +rtems_status_code zynqmp_configure_cache_ecc( void ) +{ + rtems_interrupt_entry_initialize( + &zynqmp_cache_ecc_entry, + cache_handler, + NULL, + "L1/L2 Cache Errors" + ); + + return rtems_interrupt_entry_install( + ZYNQMP_IRQ_CACHE, + RTEMS_INTERRUPT_SHARED, + &zynqmp_cache_ecc_entry + ); +} diff --git a/bsps/aarch64/xilinx-zynqmp/ecc/ddr.c b/bsps/aarch64/xilinx-zynqmp/ecc/ddr.c new file mode 100644 index 0000000000..abdbda5b69 --- /dev/null +++ b/bsps/aarch64/xilinx-zynqmp/ecc/ddr.c @@ -0,0 +1,906 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/** + * @file + * + * @ingroup RTEMSBSPsAArch64XilinxZynqMP + * + * @brief This source file contains the implementation of DDR ECC support. + */ + +/* + * Copyright (C) 2023 On-Line Applications Research Corporation (OAR) + * Written by Kinsey Moore + * + * 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. + * + * 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 OWNER 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 +#include +#include +#include +#include + +static uintptr_t ddrc_base = 0xFD070000; + +/* + * The upper value expressable by the bits in a field is sometimes used to + * indicate different things + */ +#define DDRC_ADDRMAP_4BIT_SPECIAL 15 +#define DDRC_ADDRMAP_5BIT_SPECIAL 31 + +#define DDRC_MSTR_OFFSET 0x0 +#define DDRC_MSTR_BURST_RDWR(val) BSP_FLD32(val, 16, 19) +#define DDRC_MSTR_BURST_RDWR_GET(reg) BSP_FLD32GET(reg, 16, 19) +#define DDRC_MSTR_BURST_RDWR_SET(reg, val) BSP_FLD32SET(reg, val, 16, 19) +#define DDRC_MSTR_BURST_RDWR_4 0x2 +#define DDRC_MSTR_BURST_RDWR_8 0x4 +#define DDRC_MSTR_BURST_RDWR_16 0x8 +#define DDRC_MSTR_DATA_BUS_WIDTH(val) BSP_FLD32(val, 12, 13) +#define DDRC_MSTR_DATA_BUS_WIDTH_GET(reg) BSP_FLD32GET(reg, 12, 13) +#define DDRC_MSTR_DATA_BUS_WIDTH_SET(reg, val) BSP_FLD32SET(reg, val, 12, 13) +#define DDRC_MSTR_DATA_BUS_WIDTH_FULL 0x0 +#define DDRC_MSTR_DATA_BUS_WIDTH_HALF 0x1 +#define DDRC_MSTR_DATA_BUS_WIDTH_QUARTER 0x2 +#define DDRC_MSTR_LPDDR4 BSP_BIT32(5) +#define DDRC_MSTR_DDR4 BSP_BIT32(4) +#define DDRC_MSTR_LPDDR3 BSP_BIT32(3) +#define DDRC_MSTR_DDR3 BSP_BIT32(0) + +/* Address map definitions, DDR4 variant with full bus width expected */ +#define DDRC_ADDRMAP0_OFFSET 0x200 +#define DDRC_ADDRMAP0_RANK_B0_BASE 6 +#define DDRC_ADDRMAP0_RANK_B0_TARGET_BIT(bw, lp3) 0 +#define DDRC_ADDRMAP0_RANK_B0_TARGET rank +#define DDRC_ADDRMAP0_RANK_B0_SPECIAL DDRC_ADDRMAP_5BIT_SPECIAL +#define DDRC_ADDRMAP0_RANK_B0(val) BSP_FLD32(val, 0, 4) +#define DDRC_ADDRMAP0_RANK_B0_GET(reg) BSP_FLD32GET(reg, 0, 4) +#define DDRC_ADDRMAP0_RANK_B0_SET(reg, val) BSP_FLD32SET(reg, val, 0, 4) + +#define DDRC_ADDRMAP1_OFFSET 0x204 +#define DDRC_ADDRMAP1_BANK_B2_BASE 4 +#define DDRC_ADDRMAP1_BANK_B2_TARGET_BIT(bw, lp3) 2 +#define DDRC_ADDRMAP1_BANK_B2_TARGET bank +#define DDRC_ADDRMAP1_BANK_B2_SPECIAL DDRC_ADDRMAP_5BIT_SPECIAL +#define DDRC_ADDRMAP1_BANK_B2(val) BSP_FLD32(val, 16, 20) +#define DDRC_ADDRMAP1_BANK_B2_GET(reg) BSP_FLD32GET(reg, 16, 20) +#define DDRC_ADDRMAP1_BANK_B2_SET(reg, val) BSP_FLD32SET(reg, val, 16, 20) +#define DDRC_ADDRMAP1_BANK_B1_BASE 3 +#define DDRC_ADDRMAP1_BANK_B1_TARGET_BIT(bw, lp3) 1 +#define DDRC_ADDRMAP1_BANK_B1_TARGET bank +#define DDRC_ADDRMAP1_BANK_B1_SPECIAL DDRC_ADDRMAP_5BIT_SPECIAL +#define DDRC_ADDRMAP1_BANK_B1(val) BSP_FLD32(val, 8, 12) +#define DDRC_ADDRMAP1_BANK_B1_GET(reg) BSP_FLD32GET(reg, 8, 12) +#define DDRC_ADDRMAP1_BANK_B1_SET(reg, val) BSP_FLD32SET(reg, val, 8, 12) +#define DDRC_ADDRMAP1_BANK_B0_BASE 2 +#define DDRC_ADDRMAP1_BANK_B0_TARGET_BIT(bw, lp3) 0 +#define DDRC_ADDRMAP1_BANK_B0_TARGET bank +#define DDRC_ADDRMAP1_BANK_B0_SPECIAL DDRC_ADDRMAP_5BIT_SPECIAL +#define DDRC_ADDRMAP1_BANK_B0(val) BSP_FLD32(val, 0, 4) +#define DDRC_ADDRMAP1_BANK_B0_GET(reg) BSP_FLD32GET(reg, 0, 4) +#define DDRC_ADDRMAP1_BANK_B0_SET(reg, val) BSP_FLD32SET(reg, val, 0, 4) + +#define DDRC_ADDRMAP2_OFFSET 0x208 +#define DDRC_ADDRMAP2_COL_B5_BASE 5 +#define DDRC_ADDRMAP2_COL_B5_TARGET_BIT(bw, lp3) \ + ((bw == DDRC_MSTR_DATA_BUS_WIDTH_FULL) ? \ + 5 : ((bw == DDRC_MSTR_DATA_BUS_WIDTH_HALF) ? 6 : 7)) +#define DDRC_ADDRMAP2_COL_B5_TARGET column +#define DDRC_ADDRMAP2_COL_B5_SPECIAL DDRC_ADDRMAP_4BIT_SPECIAL +#define DDRC_ADDRMAP2_COL_B5(val) BSP_FLD32(val, 24, 27) +#define DDRC_ADDRMAP2_COL_B5_GET(reg) BSP_FLD32GET(reg, 24, 27) +#define DDRC_ADDRMAP2_COL_B5_SET(reg, val) BSP_FLD32SET(reg, val, 24, 27) +#define DDRC_ADDRMAP2_COL_B4_BASE 4 +#define DDRC_ADDRMAP2_COL_B4_TARGET_BIT(bw, lp3) \ + ((bw == DDRC_MSTR_DATA_BUS_WIDTH_FULL) ? \ + 4 : ((bw == DDRC_MSTR_DATA_BUS_WIDTH_HALF) ? 5 : 6)) +#define DDRC_ADDRMAP2_COL_B4_TARGET column +#define DDRC_ADDRMAP2_COL_B4_SPECIAL DDRC_ADDRMAP_4BIT_SPECIAL +#define DDRC_ADDRMAP2_COL_B4(val) BSP_FLD32(val, 16, 19) +#define DDRC_ADDRMAP2_COL_B4_GET(reg) BSP_FLD32GET(reg, 16, 19) +#define DDRC_ADDRMAP2_COL_B4_SET(reg, val) BSP_FLD32SET(reg, val, 16, 19) +#define DDRC_ADDRMAP2_COL_B3_BASE 3 +#define DDRC_ADDRMAP2_COL_B3_TARGET_BIT(bw, lp3) \ + ((bw == DDRC_MSTR_DATA_BUS_WIDTH_FULL) ? \ + 3 : ((bw == DDRC_MSTR_DATA_BUS_WIDTH_HALF) ? 4 : 5 )) +#define DDRC_ADDRMAP2_COL_B3_TARGET column +#define DDRC_ADDRMAP2_COL_B3_SPECIAL DDRC_ADDRMAP_4BIT_SPECIAL +#define DDRC_ADDRMAP2_COL_B3(val) BSP_FLD32(val, 8, 11) +#define DDRC_ADDRMAP2_COL_B3_GET(reg) BSP_FLD32GET(reg, 8, 11) +#define DDRC_ADDRMAP2_COL_B3_SET(reg, val) BSP_FLD32SET(reg, val, 8, 11) +#define DDRC_ADDRMAP2_COL_B2_BASE 2 +#define DDRC_ADDRMAP2_COL_B2_TARGET_BIT(bw, lp3) \ + ((bw == DDRC_MSTR_DATA_BUS_WIDTH_FULL) ? \ + 2 : ((bw == DDRC_MSTR_DATA_BUS_WIDTH_HALF) ? 3 : 4)) +#define DDRC_ADDRMAP2_COL_B2_TARGET column +#define DDRC_ADDRMAP2_COL_B2_SPECIAL DDRC_ADDRMAP_4BIT_SPECIAL +#define DDRC_ADDRMAP2_COL_B2(val) BSP_FLD32(val, 0, 3) +#define DDRC_ADDRMAP2_COL_B2_GET(reg) BSP_FLD32GET(reg, 0, 3) +#define DDRC_ADDRMAP2_COL_B2_SET(reg, val) BSP_FLD32SET(reg, val, 0, 3) + +#define DDRC_ADDRMAP3_OFFSET 0x20c +#define DDRC_ADDRMAP3_COL_B9_BASE 9 +static uint32_t map3_col_b9_target_bit(uint32_t bw, bool lp3) +{ + if (bw == DDRC_MSTR_DATA_BUS_WIDTH_FULL) { + return 9; + } + + if (bw == DDRC_MSTR_DATA_BUS_WIDTH_QUARTER) { + return 13; + } + + if (lp3) { + return 10; + } + + return 11; +} +#define DDRC_ADDRMAP3_COL_B9_TARGET_BIT(bw, lp3) \ + map3_col_b9_target_bit(bw, lp3) +#define DDRC_ADDRMAP3_COL_B9_TARGET column +#define DDRC_ADDRMAP3_COL_B9_SPECIAL DDRC_ADDRMAP_4BIT_SPECIAL +#define DDRC_ADDRMAP3_COL_B9(val) BSP_FLD32(val, 24, 27) +#define DDRC_ADDRMAP3_COL_B9_GET(reg) BSP_FLD32GET(reg, 24, 27) +#define DDRC_ADDRMAP3_COL_B9_SET(reg, val) BSP_FLD32SET(reg, val, 24, 27) +#define DDRC_ADDRMAP3_COL_B8_BASE 8 +#define DDRC_ADDRMAP3_COL_B8_TARGET_BIT(bw, lp3) \ + ((bw == DDRC_MSTR_DATA_BUS_WIDTH_FULL) ? \ + 8 : ((bw == DDRC_MSTR_DATA_BUS_WIDTH_HALF) ? 9 : 11)) +#define DDRC_ADDRMAP3_COL_B8_TARGET column +#define DDRC_ADDRMAP3_COL_B8_SPECIAL DDRC_ADDRMAP_4BIT_SPECIAL +#define DDRC_ADDRMAP3_COL_B8(val) BSP_FLD32(val, 16, 19) +#define DDRC_ADDRMAP3_COL_B8_GET(reg) BSP_FLD32GET(reg, 16, 19) +#define DDRC_ADDRMAP3_COL_B8_SET(reg, val) BSP_FLD32SET(reg, val, 16, 19) +#define DDRC_ADDRMAP3_COL_B7_BASE 7 +#define DDRC_ADDRMAP3_COL_B7_TARGET_BIT(bw, lp3) \ + ((bw == DDRC_MSTR_DATA_BUS_WIDTH_FULL) ? \ + 7 : ((bw == DDRC_MSTR_DATA_BUS_WIDTH_HALF) ? 8 : 9)) +#define DDRC_ADDRMAP3_COL_B7_TARGET column +#define DDRC_ADDRMAP3_COL_B7_SPECIAL DDRC_ADDRMAP_4BIT_SPECIAL +#define DDRC_ADDRMAP3_COL_B7(val) BSP_FLD32(val, 8, 11) +#define DDRC_ADDRMAP3_COL_B7_GET(reg) BSP_FLD32GET(reg, 8, 11) +#define DDRC_ADDRMAP3_COL_B7_SET(reg, val) BSP_FLD32SET(reg, val, 8, 11) +#define DDRC_ADDRMAP3_COL_B6_BASE 6 +#define DDRC_ADDRMAP3_COL_B6_TARGET_BIT(bw, lp3) \ + ((bw == DDRC_MSTR_DATA_BUS_WIDTH_FULL) ? \ + 6 : ((bw == DDRC_MSTR_DATA_BUS_WIDTH_HALF) ? 7 : 8)) +#define DDRC_ADDRMAP3_COL_B6_TARGET column +#define DDRC_ADDRMAP3_COL_B6_SPECIAL DDRC_ADDRMAP_4BIT_SPECIAL +#define DDRC_ADDRMAP3_COL_B6(val) BSP_FLD32(val, 0, 3) +#define DDRC_ADDRMAP3_COL_B6_GET(reg) BSP_FLD32GET(reg, 0, 3) +#define DDRC_ADDRMAP3_COL_B6_SET(reg, val) BSP_FLD32SET(reg, val, 0, 3) + +#define DDRC_ADDRMAP4_OFFSET 0x210 +#define DDRC_ADDRMAP4_COL_B11_BASE 11 +#define DDRC_ADDRMAP4_COL_B11_TARGET_BIT(bw, lp3) \ + (lp3?11:13) +#define DDRC_ADDRMAP4_COL_B11_TARGET column +#define DDRC_ADDRMAP4_COL_B11_SPECIAL DDRC_ADDRMAP_4BIT_SPECIAL +#define DDRC_ADDRMAP4_COL_B11(val) BSP_FLD32(val, 8, 11) +#define DDRC_ADDRMAP4_COL_B11_GET(reg) BSP_FLD32GET(reg, 8, 11) +#define DDRC_ADDRMAP4_COL_B11_SET(reg, val) BSP_FLD32SET(reg, val, 8, 11) +#define DDRC_ADDRMAP4_COL_B10_BASE 10 +static uint32_t map4_col_b10_target_bit(uint32_t bw, bool lp3) +{ + if (bw == DDRC_MSTR_DATA_BUS_WIDTH_FULL) { + if (lp3) { + return 10; + } + return 11; + } + + /* QUARTER bus mode not valid */ + if (lp3) { + return 11; + } + + return 13; +} +#define DDRC_ADDRMAP4_COL_B10_TARGET_BIT(bw, lp3) \ + map4_col_b10_target_bit(bw, lp3) +#define DDRC_ADDRMAP4_COL_B10_TARGET column +#define DDRC_ADDRMAP4_COL_B10_SPECIAL DDRC_ADDRMAP_4BIT_SPECIAL +#define DDRC_ADDRMAP4_COL_B10(val) BSP_FLD32(val, 0, 3) +#define DDRC_ADDRMAP4_COL_B10_GET(reg) BSP_FLD32GET(reg, 0, 3) +#define DDRC_ADDRMAP4_COL_B10_SET(reg, val) BSP_FLD32SET(reg, val, 0, 3) + +#define DDRC_ADDRMAP5_OFFSET 0x214 +#define DDRC_ADDRMAP5_ROW_B11_BASE 17 +#define DDRC_ADDRMAP5_ROW_B11_TARGET_BIT(bw, lp3) 11 +#define DDRC_ADDRMAP5_ROW_B11_TARGET row +#define DDRC_ADDRMAP5_ROW_B11_SPECIAL DDRC_ADDRMAP_4BIT_SPECIAL +#define DDRC_ADDRMAP5_ROW_B11(val) BSP_FLD32(val, 24, 27) +#define DDRC_ADDRMAP5_ROW_B11_GET(reg) BSP_FLD32GET(reg, 24, 27) +#define DDRC_ADDRMAP5_ROW_B11_SET(reg, val) BSP_FLD32SET(reg, val, 24, 27) +/* This gets mapped into ADDRMAP[9,10,11] */ +#define DDRC_ADDRMAP5_ROW_B2_10(val) BSP_FLD32(val, 16, 19) +#define DDRC_ADDRMAP5_ROW_B2_10_GET(reg) BSP_FLD32GET(reg, 16, 19) +#define DDRC_ADDRMAP5_ROW_B2_10_SET(reg, val) BSP_FLD32SET(reg, val, 16, 19) +#define DDRC_ADDRMAP5_ROW_B1_BASE 7 +#define DDRC_ADDRMAP5_ROW_B1_TARGET_BIT(bw, lp3) 1 +#define DDRC_ADDRMAP5_ROW_B1_TARGET row +#define DDRC_ADDRMAP5_ROW_B1_SPECIAL DDRC_ADDRMAP_4BIT_SPECIAL +#define DDRC_ADDRMAP5_ROW_B1(val) BSP_FLD32(val, 8, 11) +#define DDRC_ADDRMAP5_ROW_B1_GET(reg) BSP_FLD32GET(reg, 8, 11) +#define DDRC_ADDRMAP5_ROW_B1_SET(reg, val) BSP_FLD32SET(reg, val, 8, 11) +#define DDRC_ADDRMAP5_ROW_B0_BASE 6 +#define DDRC_ADDRMAP5_ROW_B0_TARGET_BIT(bw, lp3) 0 +#define DDRC_ADDRMAP5_ROW_B0_TARGET row +#define DDRC_ADDRMAP5_ROW_B0_SPECIAL DDRC_ADDRMAP_4BIT_SPECIAL +#define DDRC_ADDRMAP5_ROW_B0(val) BSP_FLD32(val, 0, 3) +#define DDRC_ADDRMAP5_ROW_B0_GET(reg) BSP_FLD32GET(reg, 0, 3) +#define DDRC_ADDRMAP5_ROW_B0_SET(reg, val) BSP_FLD32SET(reg, val, 0, 3) + +#define DDRC_ADDRMAP6_OFFSET 0x218 +#define DDRC_ADDRMAP6_LPDDR3_6_12 BSP_BIT(bw, lp3)32(31) +#define DDRC_ADDRMAP6_ROW_B15_BASE 21 +#define DDRC_ADDRMAP6_ROW_B15_TARGET_BIT(bw, lp3) 15 +#define DDRC_ADDRMAP6_ROW_B15_TARGET row +#define DDRC_ADDRMAP6_ROW_B15_SPECIAL DDRC_ADDRMAP_4BIT_SPECIAL +#define DDRC_ADDRMAP6_ROW_B15(val) BSP_FLD32(val, 24, 27) +#define DDRC_ADDRMAP6_ROW_B15_GET(reg) BSP_FLD32GET(reg, 24, 27) +#define DDRC_ADDRMAP6_ROW_B15_SET(reg, val) BSP_FLD32SET(reg, val, 24, 27) +#define DDRC_ADDRMAP6_ROW_B14_BASE 20 +#define DDRC_ADDRMAP6_ROW_B14_TARGET_BIT(bw, lp3) 14 +#define DDRC_ADDRMAP6_ROW_B14_TARGET row +#define DDRC_ADDRMAP6_ROW_B14_SPECIAL DDRC_ADDRMAP_4BIT_SPECIAL +#define DDRC_ADDRMAP6_ROW_B14(val) BSP_FLD32(val, 16, 19) +#define DDRC_ADDRMAP6_ROW_B14_GET(reg) BSP_FLD32GET(reg, 16, 19) +#define DDRC_ADDRMAP6_ROW_B14_SET(reg, val) BSP_FLD32SET(reg, val, 16, 19) +#define DDRC_ADDRMAP6_ROW_B13_BASE 19 +#define DDRC_ADDRMAP6_ROW_B13_TARGET_BIT(bw, lp3) 13 +#define DDRC_ADDRMAP6_ROW_B13_TARGET row +#define DDRC_ADDRMAP6_ROW_B13_SPECIAL DDRC_ADDRMAP_4BIT_SPECIAL +#define DDRC_ADDRMAP6_ROW_B13(val) BSP_FLD32(val, 8, 11) +#define DDRC_ADDRMAP6_ROW_B13_GET(reg) BSP_FLD32GET(reg, 8, 11) +#define DDRC_ADDRMAP6_ROW_B13_SET(reg, val) BSP_FLD32SET(reg, val, 8, 11) +#define DDRC_ADDRMAP6_ROW_B12_BASE 18 +#define DDRC_ADDRMAP6_ROW_B12_TARGET_BIT(bw, lp3) 12 +#define DDRC_ADDRMAP6_ROW_B12_TARGET row +#define DDRC_ADDRMAP6_ROW_B12_SPECIAL DDRC_ADDRMAP_4BIT_SPECIAL +#define DDRC_ADDRMAP6_ROW_B12(val) BSP_FLD32(val, 0, 3) +#define DDRC_ADDRMAP6_ROW_B12_GET(reg) BSP_FLD32GET(reg, 0, 3) +#define DDRC_ADDRMAP6_ROW_B12_SET(reg, val) BSP_FLD32SET(reg, val, 0, 3) + +#define DDRC_ADDRMAP7_OFFSET 0x21c +#define DDRC_ADDRMAP7_ROW_B17_BASE 23 +#define DDRC_ADDRMAP7_ROW_B17_TARGET_BIT(bw, lp3) 17 +#define DDRC_ADDRMAP7_ROW_B17_TARGET row +#define DDRC_ADDRMAP7_ROW_B17_SPECIAL DDRC_ADDRMAP_4BIT_SPECIAL +#define DDRC_ADDRMAP7_ROW_B17(val) BSP_FLD32(val, 8, 11) +#define DDRC_ADDRMAP7_ROW_B17_GET(reg) BSP_FLD32GET(reg, 8, 11) +#define DDRC_ADDRMAP7_ROW_B17_SET(reg, val) BSP_FLD32SET(reg, val, 8, 11) +#define DDRC_ADDRMAP7_ROW_B16_BASE 22 +#define DDRC_ADDRMAP7_ROW_B16_TARGET_BIT(bw, lp3) 16 +#define DDRC_ADDRMAP7_ROW_B16_TARGET row +#define DDRC_ADDRMAP7_ROW_B16_SPECIAL DDRC_ADDRMAP_4BIT_SPECIAL +#define DDRC_ADDRMAP7_ROW_B16(val) BSP_FLD32(val, 0, 3) +#define DDRC_ADDRMAP7_ROW_B16_GET(reg) BSP_FLD32GET(reg, 0, 3) +#define DDRC_ADDRMAP7_ROW_B16_SET(reg, val) BSP_FLD32SET(reg, val, 0, 3) + +#define DDRC_ADDRMAP8_OFFSET 0x220 +#define DDRC_ADDRMAP8_BG_B1_BASE 3 +#define DDRC_ADDRMAP8_BG_B1_TARGET_BIT(bw, lp3) 1 +#define DDRC_ADDRMAP8_BG_B1_TARGET bank_group +#define DDRC_ADDRMAP8_BG_B1_SPECIAL DDRC_ADDRMAP_5BIT_SPECIAL +#define DDRC_ADDRMAP8_BG_B1(val) BSP_FLD32(val, 8, 12) +#define DDRC_ADDRMAP8_BG_B1_GET(reg) BSP_FLD32GET(reg, 8, 12) +#define DDRC_ADDRMAP8_BG_B1_SET(reg, val) BSP_FLD32SET(reg, val, 8, 12) +#define DDRC_ADDRMAP8_BG_B0_BASE 2 +#define DDRC_ADDRMAP8_BG_B0_TARGET_BIT(bw, lp3) 0 +#define DDRC_ADDRMAP8_BG_B0_TARGET bank_group +#define DDRC_ADDRMAP8_BG_B0_SPECIAL DDRC_ADDRMAP_5BIT_SPECIAL +#define DDRC_ADDRMAP8_BG_B0(val) BSP_FLD32(val, 0, 4) +#define DDRC_ADDRMAP8_BG_B0_GET(reg) BSP_FLD32GET(reg, 0, 4) +#define DDRC_ADDRMAP8_BG_B0_SET(reg, val) BSP_FLD32SET(reg, val, 0, 4) + +#define DDRC_ADDRMAP9_OFFSET 0x224 +#define DDRC_ADDRMAP9_ROW_B5_BASE 11 +#define DDRC_ADDRMAP9_ROW_B5_TARGET_BIT(bw, lp3) 5 +#define DDRC_ADDRMAP9_ROW_B5_TARGET row +#define DDRC_ADDRMAP9_ROW_B5_SPECIAL DDRC_ADDRMAP_4BIT_SPECIAL +#define DDRC_ADDRMAP9_ROW_B5(val) BSP_FLD32(val, 24, 27) +#define DDRC_ADDRMAP9_ROW_B5_GET(reg) BSP_FLD32GET(reg, 24, 27) +#define DDRC_ADDRMAP9_ROW_B5_SET(reg, val) BSP_FLD32SET(reg, val, 24, 27) +#define DDRC_ADDRMAP9_ROW_B4_BASE 10 +#define DDRC_ADDRMAP9_ROW_B4_TARGET_BIT(bw, lp3) 4 +#define DDRC_ADDRMAP9_ROW_B4_TARGET row +#define DDRC_ADDRMAP9_ROW_B4_SPECIAL DDRC_ADDRMAP_4BIT_SPECIAL +#define DDRC_ADDRMAP9_ROW_B4(val) BSP_FLD32(val, 16, 19) +#define DDRC_ADDRMAP9_ROW_B4_GET(reg) BSP_FLD32GET(reg, 16, 19) +#define DDRC_ADDRMAP9_ROW_B4_SET(reg, val) BSP_FLD32SET(reg, val, 16, 19) +#define DDRC_ADDRMAP9_ROW_B3_BASE 9 +#define DDRC_ADDRMAP9_ROW_B3_TARGET_BIT(bw, lp3) 3 +#define DDRC_ADDRMAP9_ROW_B3_TARGET row +#define DDRC_ADDRMAP9_ROW_B3_SPECIAL DDRC_ADDRMAP_4BIT_SPECIAL +#define DDRC_ADDRMAP9_ROW_B3(val) BSP_FLD32(val, 8, 11) +#define DDRC_ADDRMAP9_ROW_B3_GET(reg) BSP_FLD32GET(reg, 8, 11) +#define DDRC_ADDRMAP9_ROW_B3_SET(reg, val) BSP_FLD32SET(reg, val, 8, 11) +#define DDRC_ADDRMAP9_ROW_B2_BASE 8 +#define DDRC_ADDRMAP9_ROW_B2_TARGET_BIT(bw, lp3) 2 +#define DDRC_ADDRMAP9_ROW_B2_TARGET row +#define DDRC_ADDRMAP9_ROW_B2_SPECIAL DDRC_ADDRMAP_4BIT_SPECIAL +#define DDRC_ADDRMAP9_ROW_B2(val) BSP_FLD32(val, 0, 3) +#define DDRC_ADDRMAP9_ROW_B2_GET(reg) BSP_FLD32GET(reg, 0, 3) +#define DDRC_ADDRMAP9_ROW_B2_SET(reg, val) BSP_FLD32SET(reg, val, 0, 3) + +#define DDRC_ADDRMAP10_OFFSET 0x228 +#define DDRC_ADDRMAP10_ROW_B9_BASE 15 +#define DDRC_ADDRMAP10_ROW_B9_TARGET_BIT(bw, lp3) 9 +#define DDRC_ADDRMAP10_ROW_B9_TARGET row +#define DDRC_ADDRMAP10_ROW_B9_SPECIAL DDRC_ADDRMAP_4BIT_SPECIAL +#define DDRC_ADDRMAP10_ROW_B9(val) BSP_FLD32(val, 24, 27) +#define DDRC_ADDRMAP10_ROW_B9_GET(reg) BSP_FLD32GET(reg, 24, 27) +#define DDRC_ADDRMAP10_ROW_B9_SET(reg, val) BSP_FLD32SET(reg, val, 24, 27) +#define DDRC_ADDRMAP10_ROW_B8_BASE 14 +#define DDRC_ADDRMAP10_ROW_B8_TARGET_BIT(bw, lp3) 8 +#define DDRC_ADDRMAP10_ROW_B8_TARGET row +#define DDRC_ADDRMAP10_ROW_B8_SPECIAL DDRC_ADDRMAP_4BIT_SPECIAL +#define DDRC_ADDRMAP10_ROW_B8(val) BSP_FLD32(val, 16, 19) +#define DDRC_ADDRMAP10_ROW_B8_GET(reg) BSP_FLD32GET(reg, 16, 19) +#define DDRC_ADDRMAP10_ROW_B8_SET(reg, val) BSP_FLD32SET(reg, val, 16, 19) +#define DDRC_ADDRMAP10_ROW_B7_BASE 13 +#define DDRC_ADDRMAP10_ROW_B7_TARGET_BIT(bw, lp3) 7 +#define DDRC_ADDRMAP10_ROW_B7_TARGET row +#define DDRC_ADDRMAP10_ROW_B7_SPECIAL DDRC_ADDRMAP_4BIT_SPECIAL +#define DDRC_ADDRMAP10_ROW_B7(val) BSP_FLD32(val, 8, 11) +#define DDRC_ADDRMAP10_ROW_B7_GET(reg) BSP_FLD32GET(reg, 8, 11) +#define DDRC_ADDRMAP10_ROW_B7_SET(reg, val) BSP_FLD32SET(reg, val, 8, 11) +#define DDRC_ADDRMAP10_ROW_B6_BASE 12 +#define DDRC_ADDRMAP10_ROW_B6_TARGET_BIT(bw, lp3) 6 +#define DDRC_ADDRMAP10_ROW_B6_TARGET row +#define DDRC_ADDRMAP10_ROW_B6_SPECIAL DDRC_ADDRMAP_4BIT_SPECIAL +#define DDRC_ADDRMAP10_ROW_B6(val) BSP_FLD32(val, 0, 3) +#define DDRC_ADDRMAP10_ROW_B6_GET(reg) BSP_FLD32GET(reg, 0, 3) +#define DDRC_ADDRMAP10_ROW_B6_SET(reg, val) BSP_FLD32SET(reg, val, 0, 3) + +#define DDRC_ADDRMAP11_OFFSET 0x22c +#define DDRC_ADDRMAP11_ROW_B10_BASE 16 +#define DDRC_ADDRMAP11_ROW_B10_TARGET_BIT(bw, lp3) 10 +#define DDRC_ADDRMAP11_ROW_B10_TARGET row +#define DDRC_ADDRMAP11_ROW_B10_SPECIAL DDRC_ADDRMAP_4BIT_SPECIAL +#define DDRC_ADDRMAP11_ROW_B10(val) BSP_FLD32(val, 0, 3) +#define DDRC_ADDRMAP11_ROW_B10_GET(reg) BSP_FLD32GET(reg, 0, 3) +#define DDRC_ADDRMAP11_ROW_B10_SET(reg, val) BSP_FLD32SET(reg, val, 0, 3) + +#define DDRC_ECCPOISONADDR0_OFFSET 0xB8 +#define DDRC_ECCPOISONADDR0_RANK BSP_BIT32(24) +#define DDRC_ECCPOISONADDR0_COL(val) BSP_FLD32(val, 0, 11) +#define DDRC_ECCPOISONADDR0_COL_GET(reg) BSP_FLD32GET(reg, 0, 11) +#define DDRC_ECCPOISONADDR0_COL_SET(reg, val) BSP_FLD32SET(reg, val, 0, 11) + +#define DDRC_ECCPOISONADDR1_OFFSET 0xBC +#define DDRC_ECCPOISONADDR1_BG(val) BSP_FLD32(val, 28, 29) +#define DDRC_ECCPOISONADDR1_BG_GET(reg) BSP_FLD32GET(reg, 28, 29) +#define DDRC_ECCPOISONADDR1_BG_SET(reg, val) BSP_FLD32SET(reg, val, 28, 29) +#define DDRC_ECCPOISONADDR1_BANK(val) BSP_FLD32(val, 24, 26) +#define DDRC_ECCPOISONADDR1_BANK_GET(reg) BSP_FLD32GET(reg, 24, 26) +#define DDRC_ECCPOISONADDR1_BANK_SET(reg, val) BSP_FLD32SET(reg, val, 24, 26) +#define DDRC_ECCPOISONADDR1_ROW(val) BSP_FLD32(val, 0, 17) +#define DDRC_ECCPOISONADDR1_ROW_GET(reg) BSP_FLD32GET(reg, 0, 17) +#define DDRC_ECCPOISONADDR1_ROW_SET(reg, val) BSP_FLD32SET(reg, val, 0, 17) + +static void homogenize_row( + uint32_t *addrmap5, + uint32_t *addrmap9, + uint32_t *addrmap10, + uint32_t *addrmap11 +) +{ + uint32_t b2_10 = DDRC_ADDRMAP5_ROW_B2_10_GET(*addrmap5); + if (b2_10 == DDRC_ADDRMAP_4BIT_SPECIAL) { + /* ADDRMAP[9,10,11] already define row[2:10] correctly */ + return; + } + + /* Translate b2_10 to ADDRMAP[9,10,11] to simplify future code */ + *addrmap9 = DDRC_ADDRMAP9_ROW_B5_SET(*addrmap9, b2_10); + *addrmap9 = DDRC_ADDRMAP9_ROW_B4_SET(*addrmap9, b2_10); + *addrmap9 = DDRC_ADDRMAP9_ROW_B3_SET(*addrmap9, b2_10); + *addrmap9 = DDRC_ADDRMAP9_ROW_B2_SET(*addrmap9, b2_10); + + *addrmap10 = DDRC_ADDRMAP10_ROW_B9_SET(*addrmap10, b2_10); + *addrmap10 = DDRC_ADDRMAP10_ROW_B8_SET(*addrmap10, b2_10); + *addrmap10 = DDRC_ADDRMAP10_ROW_B7_SET(*addrmap10, b2_10); + *addrmap10 = DDRC_ADDRMAP10_ROW_B6_SET(*addrmap10, b2_10); + + *addrmap11 = DDRC_ADDRMAP11_ROW_B10_SET(*addrmap11, b2_10); +} + +#define DDRC_READ(offset) (*(uint32_t *)(ddrc_base + offset)) + +#define DDRC_MAP_BIT(value, source, target) ((value >> source) & 0x1) << target + +#define DDRC_CHECK_AND_UNMAP(bus_width, lpddr3, BIT_ID, info, addrmap) \ +({ \ + uint32_t mapbit = DDRC_ ## BIT_ID ## _GET(addrmap); \ + uint32_t target_bit = DDRC_ ## BIT_ID ## _TARGET_BIT(bus_width, lpddr3); \ + if (mapbit != DDRC_ ## BIT_ID ## _SPECIAL) { \ + mapbit += DDRC_ ## BIT_ID ## _BASE; \ + /* account for AXI -> HIF shift */ \ + mapbit += 3; \ + info->address |= \ + DDRC_MAP_BIT(info-> DDRC_ ## BIT_ID ## _TARGET, target_bit, mapbit); \ + } \ +}) + +/* + * Steps in mapping an address: + * system address -> DDRC -> AXI byte address: + * disjoint memory regions are mapped into a monolithic block representing + * total available RAM based on configured offsets + * AXI byte address -> XPI -> HIF word address: + * word-sized shift of 3 bits for 8 byte (word) alignment + * HIF word address -> DDRC -> SDRAM address: + * addresses are mapped into SDRAM terms by the flexible address mapper using + * the ADDRMAP* registers + */ +static int compose_address(DDR_Error_Info *info) +{ + uint32_t addrmap0 = DDRC_READ(DDRC_ADDRMAP0_OFFSET); + uint32_t addrmap1 = DDRC_READ(DDRC_ADDRMAP1_OFFSET); + uint32_t addrmap2 = DDRC_READ(DDRC_ADDRMAP2_OFFSET); + uint32_t addrmap3 = DDRC_READ(DDRC_ADDRMAP3_OFFSET); + uint32_t addrmap4 = DDRC_READ(DDRC_ADDRMAP4_OFFSET); + uint32_t addrmap5 = DDRC_READ(DDRC_ADDRMAP5_OFFSET); + uint32_t addrmap6 = DDRC_READ(DDRC_ADDRMAP6_OFFSET); + uint32_t addrmap7 = DDRC_READ(DDRC_ADDRMAP7_OFFSET); + uint32_t addrmap8 = DDRC_READ(DDRC_ADDRMAP8_OFFSET); + uint32_t addrmap9 = DDRC_READ(DDRC_ADDRMAP9_OFFSET); + uint32_t addrmap10 = DDRC_READ(DDRC_ADDRMAP10_OFFSET); + uint32_t addrmap11 = DDRC_READ(DDRC_ADDRMAP11_OFFSET); + uint32_t mstr = DDRC_READ(DDRC_MSTR_OFFSET); + uint32_t bus_width = DDRC_MSTR_DATA_BUS_WIDTH_GET(mstr); + bool lpddr3 = mstr & DDRC_MSTR_LPDDR3; + + homogenize_row(&addrmap5, &addrmap9, &addrmap10, &addrmap11); + + /* Clear items that will be written to */ + info->address = 0; + + DDRC_CHECK_AND_UNMAP(bus_width, lpddr3, ADDRMAP0_RANK_B0, info, addrmap0); + DDRC_CHECK_AND_UNMAP(bus_width, lpddr3, ADDRMAP1_BANK_B2, info, addrmap1); + DDRC_CHECK_AND_UNMAP(bus_width, lpddr3, ADDRMAP1_BANK_B1, info, addrmap1); + DDRC_CHECK_AND_UNMAP(bus_width, lpddr3, ADDRMAP1_BANK_B0, info, addrmap1); + DDRC_CHECK_AND_UNMAP(bus_width, lpddr3, ADDRMAP2_COL_B5, info, addrmap2); + DDRC_CHECK_AND_UNMAP(bus_width, lpddr3, ADDRMAP2_COL_B4, info, addrmap2); + DDRC_CHECK_AND_UNMAP(bus_width, lpddr3, ADDRMAP2_COL_B3, info, addrmap2); + DDRC_CHECK_AND_UNMAP(bus_width, lpddr3, ADDRMAP2_COL_B2, info, addrmap2); + DDRC_CHECK_AND_UNMAP(bus_width, lpddr3, ADDRMAP3_COL_B9, info, addrmap3); + DDRC_CHECK_AND_UNMAP(bus_width, lpddr3, ADDRMAP3_COL_B8, info, addrmap3); + DDRC_CHECK_AND_UNMAP(bus_width, lpddr3, ADDRMAP3_COL_B7, info, addrmap3); + DDRC_CHECK_AND_UNMAP(bus_width, lpddr3, ADDRMAP3_COL_B6, info, addrmap3); + DDRC_CHECK_AND_UNMAP(bus_width, lpddr3, ADDRMAP4_COL_B11, info, addrmap4); + DDRC_CHECK_AND_UNMAP(bus_width, lpddr3, ADDRMAP4_COL_B10, info, addrmap4); + DDRC_CHECK_AND_UNMAP(bus_width, lpddr3, ADDRMAP5_ROW_B11, info, addrmap5); + DDRC_CHECK_AND_UNMAP(bus_width, lpddr3, ADDRMAP5_ROW_B1, info, addrmap5); + DDRC_CHECK_AND_UNMAP(bus_width, lpddr3, ADDRMAP5_ROW_B0, info, addrmap5); + DDRC_CHECK_AND_UNMAP(bus_width, lpddr3, ADDRMAP6_ROW_B15, info, addrmap6); + DDRC_CHECK_AND_UNMAP(bus_width, lpddr3, ADDRMAP6_ROW_B14, info, addrmap6); + DDRC_CHECK_AND_UNMAP(bus_width, lpddr3, ADDRMAP6_ROW_B13, info, addrmap6); + DDRC_CHECK_AND_UNMAP(bus_width, lpddr3, ADDRMAP6_ROW_B12, info, addrmap6); + DDRC_CHECK_AND_UNMAP(bus_width, lpddr3, ADDRMAP7_ROW_B17, info, addrmap7); + DDRC_CHECK_AND_UNMAP(bus_width, lpddr3, ADDRMAP7_ROW_B16, info, addrmap7); + DDRC_CHECK_AND_UNMAP(bus_width, lpddr3, ADDRMAP8_BG_B1, info, addrmap8); + DDRC_CHECK_AND_UNMAP(bus_width, lpddr3, ADDRMAP8_BG_B0, info, addrmap8); + DDRC_CHECK_AND_UNMAP(bus_width, lpddr3, ADDRMAP9_ROW_B5, info, addrmap9); + DDRC_CHECK_AND_UNMAP(bus_width, lpddr3, ADDRMAP9_ROW_B4, info, addrmap9); + DDRC_CHECK_AND_UNMAP(bus_width, lpddr3, ADDRMAP9_ROW_B3, info, addrmap9); + DDRC_CHECK_AND_UNMAP(bus_width, lpddr3, ADDRMAP9_ROW_B2, info, addrmap9); + DDRC_CHECK_AND_UNMAP(bus_width, lpddr3, ADDRMAP10_ROW_B9, info, addrmap10); + DDRC_CHECK_AND_UNMAP(bus_width, lpddr3, ADDRMAP10_ROW_B8, info, addrmap10); + DDRC_CHECK_AND_UNMAP(bus_width, lpddr3, ADDRMAP10_ROW_B7, info, addrmap10); + DDRC_CHECK_AND_UNMAP(bus_width, lpddr3, ADDRMAP10_ROW_B6, info, addrmap10); + DDRC_CHECK_AND_UNMAP(bus_width, lpddr3, ADDRMAP11_ROW_B10, info, addrmap11); + + /* Column[0:1] are always statically mapped to HIF[0:1] */ + info->address |= (info->column & 0x3) << 3; + return 0; +} + +static uintptr_t ddr_qos_ctrl_base = 0xFD090000; +/* DDR QoS CTRL QoS IRQ Status */ +#define DDR_QIS_OFFSET 0x200 +#define DDR_QIE_OFFSET 0x208 +#define DDR_QID_OFFSET 0x20c +#define DDR_QI_UNCRERR BSP_BIT32(2) +#define DDR_QI_CORERR BSP_BIT32(1) + +#define DDRC_ECCSTAT_OFFSET 0x78 +#define DDRC_ECCSTAT_UNCR_ERR(val) BSP_FLD32(val, 16, 19) +#define DDRC_ECCSTAT_UNCR_ERR_GET(reg) BSP_FLD32GET(reg, 16, 19) +#define DDRC_ECCSTAT_UNCR_ERR_SET(reg, val) BSP_FLD32SET(reg, val, 16, 19) +#define DDRC_ECCSTAT_CORR_ERR(val) BSP_FLD32(val, 8, 11) +#define DDRC_ECCSTAT_CORR_ERR_GET(reg) BSP_FLD32GET(reg, 8, 11) +#define DDRC_ECCSTAT_CORR_ERR_SET(reg, val) BSP_FLD32SET(reg, val, 8, 11) +#define DDRC_ECCSTAT_CORR_BIT(val) BSP_FLD32(val, 0, 6) +#define DDRC_ECCSTAT_CORR_BIT_GET(reg) BSP_FLD32GET(reg, 0, 6) +#define DDRC_ECCSTAT_CORR_BIT_SET(reg, val) BSP_FLD32SET(reg, val, 0, 6) + +/* Correctable and uncorrectable error address registers share encodings */ +#define DDRC_ECCCADDR0_OFFSET 0x84 +#define DDRC_ECCUADDR0_OFFSET 0xA4 +#define DDRC_ECCXADDR0_RANK BSP_BIT32(24) +#define DDRC_ECCXADDR0_ROW(val) BSP_FLD32(val, 0, 17) +#define DDRC_ECCXADDR0_ROW_GET(reg) BSP_FLD32GET(reg, 0, 17) +#define DDRC_ECCXADDR0_ROW_SET(reg, val) BSP_FLD32SET(reg, val, 0, 17) + +#define DDRC_ECCCADDR1_OFFSET 0x88 +#define DDRC_ECCUADDR1_OFFSET 0xA8 +#define DDRC_ECCXADDR1_BG(val) BSP_FLD32(val, 24, 25) +#define DDRC_ECCXADDR1_BG_GET(reg) BSP_FLD32GET(reg, 24, 25) +#define DDRC_ECCXADDR1_BG_SET(reg, val) BSP_FLD32SET(reg, val, 24, 25) +#define DDRC_ECCXADDR1_BANK(val) BSP_FLD32(val, 16, 18) +#define DDRC_ECCXADDR1_BANK_GET(reg) BSP_FLD32GET(reg, 16, 18) +#define DDRC_ECCXADDR1_BANK_SET(reg, val) BSP_FLD32SET(reg, val, 16, 18) +#define DDRC_ECCXADDR1_COL(val) BSP_FLD32(val, 0, 11) +#define DDRC_ECCXADDR1_COL_GET(reg) BSP_FLD32GET(reg, 0, 11) +#define DDRC_ECCXADDR1_COL_SET(reg, val) BSP_FLD32SET(reg, val, 0, 11) + +static void extract_ddr_info( + DDR_Error_Info *info, + uint32_t addr0_val, + uint32_t addr1_val +) +{ + info->rank = (addr0_val & DDRC_ECCXADDR0_RANK) >> 24; + info->bank_group = DDRC_ECCXADDR1_BG_GET(addr1_val); + info->bank = DDRC_ECCXADDR1_BANK_GET(addr1_val); + info->row = DDRC_ECCXADDR0_ROW_GET(addr0_val); + info->column = DDRC_ECCXADDR1_COL_GET(addr1_val); + compose_address(info); +} + + +static void ddr_handler(void *arg) +{ + (void) arg; + + volatile uint32_t *qis = (uint32_t *)(ddr_qos_ctrl_base + DDR_QIS_OFFSET); + uint32_t qis_value = *qis; + DDR_Error_Info info; + volatile uint32_t *addr0 = (uint32_t *)(ddrc_base + DDRC_ECCCADDR0_OFFSET); + volatile uint32_t *addr1 = (uint32_t *)(ddrc_base + DDRC_ECCCADDR1_OFFSET); + + /* specific data is captured in DDRC.ECCSTAT[corrected_bit_num] */ + if ((qis_value & DDR_QI_CORERR) != 0) { + /* Clear status flag */ + *qis = DDR_QI_CORERR; + + info.type = DDR_CORRECTABLE; + extract_ddr_info(&info, *addr0, *addr1); + zynqmp_invoke_ecc_handler(DDR_RAM, &info); + } + if ((qis_value & DDR_QI_UNCRERR) != 0) { + /* Clear status flag */ + *qis = DDR_QI_UNCRERR; + + info.type = DDR_UNCORRECTABLE; + extract_ddr_info(&info, *addr0, *addr1); + zynqmp_invoke_ecc_handler(DDR_RAM, &info); + } +} + +static rtems_interrupt_entry zynqmp_ddr_ecc_entry; + +rtems_status_code zynqmp_configure_ddr_ecc( void ) +{ + volatile uint32_t *qie = (uint32_t *)(ddr_qos_ctrl_base + DDR_QIE_OFFSET); + rtems_status_code sc; + + rtems_interrupt_entry_initialize( + &zynqmp_ddr_ecc_entry, + ddr_handler, + NULL, + "DDR RAM ECC" + ); + + sc = rtems_interrupt_entry_install( + ZYNQMP_IRQ_DDR, + RTEMS_INTERRUPT_SHARED, + &zynqmp_ddr_ecc_entry + ); + + if (sc != RTEMS_SUCCESSFUL) { + return sc; + } + + /* enable interrupts for ECC in QOS_IRQ_ENABLE */ + *qie |= DDR_QI_UNCRERR | DDR_QI_CORERR; + return RTEMS_SUCCESSFUL; +} + +#if RUNNING_FROM_OCM_IS_NOT_CURRENTLY_POSSIBLE +/* + * Injecting DDR ECC faults requires RTEMS to run from OCM since the DDR will be + * partially disabled and re-enabled during the process. RTEMS is too large to + * run out of OCM in its current configuration and doing so would require + * operation without MMU along with other changes to reduce the memory footprint + * to below 256KB. It must be the whole RTEMS executable since stack accesses + * would also present a problem. + */ + +#define DDRC_PRINT_MAP(BIT_ID, addrmap) \ +({ \ + uint32_t mapbit = DDRC_ ## BIT_ID ## _GET(addrmap); \ + if (mapbit != DDRC_ ## BIT_ID ## _SPECIAL) { \ + mapbit += DDRC_ ## BIT_ID ## _BASE; \ + } \ +}) + +static void print_addr_maps( void ) +{ + uint32_t addrmap0 = DDRC_READ(DDRC_ADDRMAP0_OFFSET); + uint32_t addrmap1 = DDRC_READ(DDRC_ADDRMAP1_OFFSET); + uint32_t addrmap2 = DDRC_READ(DDRC_ADDRMAP2_OFFSET); + uint32_t addrmap3 = DDRC_READ(DDRC_ADDRMAP3_OFFSET); + uint32_t addrmap4 = DDRC_READ(DDRC_ADDRMAP4_OFFSET); + uint32_t addrmap5 = DDRC_READ(DDRC_ADDRMAP5_OFFSET); + uint32_t addrmap6 = DDRC_READ(DDRC_ADDRMAP6_OFFSET); + uint32_t addrmap7 = DDRC_READ(DDRC_ADDRMAP7_OFFSET); + uint32_t addrmap8 = DDRC_READ(DDRC_ADDRMAP8_OFFSET); + uint32_t addrmap9 = DDRC_READ(DDRC_ADDRMAP9_OFFSET); + uint32_t addrmap10 = DDRC_READ(DDRC_ADDRMAP10_OFFSET); + uint32_t addrmap11 = DDRC_READ(DDRC_ADDRMAP11_OFFSET); + + homogenize_row(&addrmap5, &addrmap9, &addrmap10, &addrmap11); + + DDRC_PRINT_MAP(ADDRMAP0_RANK_B0, addrmap0); + DDRC_PRINT_MAP(ADDRMAP1_BANK_B2, addrmap1); + DDRC_PRINT_MAP(ADDRMAP1_BANK_B1, addrmap1); + DDRC_PRINT_MAP(ADDRMAP1_BANK_B0, addrmap1); + DDRC_PRINT_MAP(ADDRMAP2_COL_B5, addrmap2); + DDRC_PRINT_MAP(ADDRMAP2_COL_B4, addrmap2); + DDRC_PRINT_MAP(ADDRMAP2_COL_B3, addrmap2); + DDRC_PRINT_MAP(ADDRMAP2_COL_B2, addrmap2); + DDRC_PRINT_MAP(ADDRMAP3_COL_B9, addrmap3); + DDRC_PRINT_MAP(ADDRMAP3_COL_B8, addrmap3); + DDRC_PRINT_MAP(ADDRMAP3_COL_B7, addrmap3); + DDRC_PRINT_MAP(ADDRMAP3_COL_B6, addrmap3); + DDRC_PRINT_MAP(ADDRMAP4_COL_B11, addrmap4); + DDRC_PRINT_MAP(ADDRMAP4_COL_B10, addrmap4); + DDRC_PRINT_MAP(ADDRMAP5_ROW_B11, addrmap5); + DDRC_PRINT_MAP(ADDRMAP5_ROW_B1, addrmap5); + DDRC_PRINT_MAP(ADDRMAP5_ROW_B0, addrmap5); + DDRC_PRINT_MAP(ADDRMAP6_ROW_B15, addrmap6); + DDRC_PRINT_MAP(ADDRMAP6_ROW_B14, addrmap6); + DDRC_PRINT_MAP(ADDRMAP6_ROW_B13, addrmap6); + DDRC_PRINT_MAP(ADDRMAP6_ROW_B12, addrmap6); + DDRC_PRINT_MAP(ADDRMAP7_ROW_B17, addrmap7); + DDRC_PRINT_MAP(ADDRMAP7_ROW_B16, addrmap7); + DDRC_PRINT_MAP(ADDRMAP8_BG_B1, addrmap8); + DDRC_PRINT_MAP(ADDRMAP8_BG_B0, addrmap8); + DDRC_PRINT_MAP(ADDRMAP9_ROW_B5, addrmap9); + DDRC_PRINT_MAP(ADDRMAP9_ROW_B4, addrmap9); + DDRC_PRINT_MAP(ADDRMAP9_ROW_B3, addrmap9); + DDRC_PRINT_MAP(ADDRMAP9_ROW_B2, addrmap9); + DDRC_PRINT_MAP(ADDRMAP10_ROW_B9, addrmap10); + DDRC_PRINT_MAP(ADDRMAP10_ROW_B8, addrmap10); + DDRC_PRINT_MAP(ADDRMAP10_ROW_B7, addrmap10); + DDRC_PRINT_MAP(ADDRMAP10_ROW_B6, addrmap10); + DDRC_PRINT_MAP(ADDRMAP11_ROW_B10, addrmap11); +} + +/* Ignore the bitmap if it's the flag value, otherwise map it */ +#define DDRC_CHECK_AND_MAP(bus_width, lpddr3, BIT_ID, info, addrmap) \ +({ \ + uint32_t mapbit = DDRC_ ## BIT_ID ## _GET(addrmap); \ + uint32_t target_bit = DDRC_ ## BIT_ID ## _TARGET_BIT(bus_width, lpddr3); \ + if (mapbit != DDRC_ ## BIT_ID ## _SPECIAL) { \ + mapbit += DDRC_ ## BIT_ID ## _BASE; \ + /* account for AXI -> HIF shift */ \ + mapbit += 3; \ + info-> DDRC_ ## BIT_ID ## _TARGET |= \ + DDRC_MAP_BIT(info->address, mapbit, target_bit); \ + } \ +}) + +static int decompose_address(DDR_Error_Info *info) +{ + uint32_t addrmap0 = DDRC_READ(DDRC_ADDRMAP0_OFFSET); + uint32_t addrmap1 = DDRC_READ(DDRC_ADDRMAP1_OFFSET); + uint32_t addrmap2 = DDRC_READ(DDRC_ADDRMAP2_OFFSET); + uint32_t addrmap3 = DDRC_READ(DDRC_ADDRMAP3_OFFSET); + uint32_t addrmap4 = DDRC_READ(DDRC_ADDRMAP4_OFFSET); + uint32_t addrmap5 = DDRC_READ(DDRC_ADDRMAP5_OFFSET); + uint32_t addrmap6 = DDRC_READ(DDRC_ADDRMAP6_OFFSET); + uint32_t addrmap7 = DDRC_READ(DDRC_ADDRMAP7_OFFSET); + uint32_t addrmap8 = DDRC_READ(DDRC_ADDRMAP8_OFFSET); + uint32_t addrmap9 = DDRC_READ(DDRC_ADDRMAP9_OFFSET); + uint32_t addrmap10 = DDRC_READ(DDRC_ADDRMAP10_OFFSET); + uint32_t addrmap11 = DDRC_READ(DDRC_ADDRMAP11_OFFSET); + uint32_t mstr = DDRC_READ(DDRC_MSTR_OFFSET); + uint32_t bus_width = DDRC_MSTR_DATA_BUS_WIDTH_GET(mstr); + bool lpddr3 = mstr & DDRC_MSTR_LPDDR3; + + homogenize_row(&addrmap5, &addrmap9, &addrmap10, &addrmap11); + + /* Clear items that will be written to */ + info->rank = 0; + info->bank_group = 0; + info->bank = 0; + info->row = 0; + info->column = 0; + + DDRC_CHECK_AND_MAP(bus_width, lpddr3, ADDRMAP0_RANK_B0, info, addrmap0); + DDRC_CHECK_AND_MAP(bus_width, lpddr3, ADDRMAP1_BANK_B2, info, addrmap1); + DDRC_CHECK_AND_MAP(bus_width, lpddr3, ADDRMAP1_BANK_B1, info, addrmap1); + DDRC_CHECK_AND_MAP(bus_width, lpddr3, ADDRMAP1_BANK_B0, info, addrmap1); + DDRC_CHECK_AND_MAP(bus_width, lpddr3, ADDRMAP2_COL_B5, info, addrmap2); + DDRC_CHECK_AND_MAP(bus_width, lpddr3, ADDRMAP2_COL_B4, info, addrmap2); + DDRC_CHECK_AND_MAP(bus_width, lpddr3, ADDRMAP2_COL_B3, info, addrmap2); + DDRC_CHECK_AND_MAP(bus_width, lpddr3, ADDRMAP2_COL_B2, info, addrmap2); + DDRC_CHECK_AND_MAP(bus_width, lpddr3, ADDRMAP3_COL_B9, info, addrmap3); + DDRC_CHECK_AND_MAP(bus_width, lpddr3, ADDRMAP3_COL_B8, info, addrmap3); + DDRC_CHECK_AND_MAP(bus_width, lpddr3, ADDRMAP3_COL_B7, info, addrmap3); + DDRC_CHECK_AND_MAP(bus_width, lpddr3, ADDRMAP3_COL_B6, info, addrmap3); + DDRC_CHECK_AND_MAP(bus_width, lpddr3, ADDRMAP4_COL_B11, info, addrmap4); + DDRC_CHECK_AND_MAP(bus_width, lpddr3, ADDRMAP4_COL_B10, info, addrmap4); + DDRC_CHECK_AND_MAP(bus_width, lpddr3, ADDRMAP5_ROW_B11, info, addrmap5); + DDRC_CHECK_AND_MAP(bus_width, lpddr3, ADDRMAP5_ROW_B1, info, addrmap5); + DDRC_CHECK_AND_MAP(bus_width, lpddr3, ADDRMAP5_ROW_B0, info, addrmap5); + DDRC_CHECK_AND_MAP(bus_width, lpddr3, ADDRMAP6_ROW_B15, info, addrmap6); + DDRC_CHECK_AND_MAP(bus_width, lpddr3, ADDRMAP6_ROW_B14, info, addrmap6); + DDRC_CHECK_AND_MAP(bus_width, lpddr3, ADDRMAP6_ROW_B13, info, addrmap6); + DDRC_CHECK_AND_MAP(bus_width, lpddr3, ADDRMAP6_ROW_B12, info, addrmap6); + DDRC_CHECK_AND_MAP(bus_width, lpddr3, ADDRMAP7_ROW_B17, info, addrmap7); + DDRC_CHECK_AND_MAP(bus_width, lpddr3, ADDRMAP7_ROW_B16, info, addrmap7); + DDRC_CHECK_AND_MAP(bus_width, lpddr3, ADDRMAP8_BG_B1, info, addrmap8); + DDRC_CHECK_AND_MAP(bus_width, lpddr3, ADDRMAP8_BG_B0, info, addrmap8); + DDRC_CHECK_AND_MAP(bus_width, lpddr3, ADDRMAP9_ROW_B5, info, addrmap9); + DDRC_CHECK_AND_MAP(bus_width, lpddr3, ADDRMAP9_ROW_B4, info, addrmap9); + DDRC_CHECK_AND_MAP(bus_width, lpddr3, ADDRMAP9_ROW_B3, info, addrmap9); + DDRC_CHECK_AND_MAP(bus_width, lpddr3, ADDRMAP9_ROW_B2, info, addrmap9); + DDRC_CHECK_AND_MAP(bus_width, lpddr3, ADDRMAP10_ROW_B9, info, addrmap10); + DDRC_CHECK_AND_MAP(bus_width, lpddr3, ADDRMAP10_ROW_B8, info, addrmap10); + DDRC_CHECK_AND_MAP(bus_width, lpddr3, ADDRMAP10_ROW_B7, info, addrmap10); + DDRC_CHECK_AND_MAP(bus_width, lpddr3, ADDRMAP10_ROW_B6, info, addrmap10); + DDRC_CHECK_AND_MAP(bus_width, lpddr3, ADDRMAP11_ROW_B10, info, addrmap11); + + /* Column[0:1] are always statically mapped to HIF[0:1] */ + info->column |= (info->address >> 3) & 0x3; + + /* select target address */ + uint32_t paddr0_val = DDRC_ECCPOISONADDR0_COL_SET(0, info->column); + if (info->rank) { + paddr0_val |= DDRC_ECCPOISONADDR0_RANK; + } + + uint32_t paddr1_val = DDRC_ECCPOISONADDR1_BG_SET(0, info->bank_group); + paddr1_val = DDRC_ECCPOISONADDR1_BANK_SET(paddr1_val, info->bank); + paddr1_val = DDRC_ECCPOISONADDR1_ROW_SET(paddr1_val, info->row); + return 0; +} + +/* DDR Controller (DDRC) ECC Config Register 1 */ +#define DDRC_ECCCFG1_OFFSET 0x74 +#define DDRC_ECCCFG1_POISON_SINGLE BSP_BIT32(1) +#define DDRC_ECCCFG1_POISON_EN BSP_BIT32(0) + +#define DDRC_SWCTL_OFFSET 0x74 +#define DDRC_SWSTAT_OFFSET 0x324 + +#define DDRC_DBGCAM_OFFSET 0x308 +#define DDRC_DBGCAM_WR_DATA_PIPELINE_EMPTY BSP_BIT32(28) +#define DDRC_DBGCAM_RD_DATA_PIPELINE_EMPTY BSP_BIT32(27) +#define DDRC_DBGCAM_DBG_WR_Q_EMPTY BSP_BIT32(26) +#define DDRC_DBGCAM_DBG_RD_Q_EMPTY BSP_BIT32(25) + +#define DDRC_DBG1_OFFSET 0x304 +#define DDRC_DBG1_DIS_DQ BSP_BIT32(0) + +static void set_ecccfg1(volatile uint32_t *ecccfg1, uint32_t ecccfg1_val) +{ + uint32_t dbgcam_mask = DDRC_DBGCAM_WR_DATA_PIPELINE_EMPTY | + DDRC_DBGCAM_RD_DATA_PIPELINE_EMPTY | DDRC_DBGCAM_DBG_WR_Q_EMPTY | + DDRC_DBGCAM_DBG_RD_Q_EMPTY; + volatile uint32_t *dbgcam = (uint32_t *)(ddrc_base + DDRC_DBGCAM_OFFSET); + volatile uint32_t *dbg1 = (uint32_t *)(ddrc_base + DDRC_DBG1_OFFSET); + + /* disable dequeueing */ + *dbg1 |= DDRC_DBG1_DIS_DQ; + /* poll for DDRC empty state */ + while((*dbgcam & dbgcam_mask) != dbgcam_mask); + *ecccfg1 = ecccfg1_val; + /* enable dequeueing */ + *dbg1 &= ~DDRC_DBG1_DIS_DQ; +} + +void zynqmp_ddr_inject_fault( bool correctable ) +{ + uint64_t poison_var; + uint64_t read_var; + volatile uint64_t *poison_addr = &poison_var; + volatile uint32_t *ecccfg1 = (uint32_t *)(ddrc_base + DDRC_ECCCFG1_OFFSET); + uint32_t ecccfg1_val = 0; + volatile uint32_t *swctl = (uint32_t *)(ddrc_base + DDRC_SWCTL_OFFSET); + volatile uint32_t *swstat = (uint32_t *)(ddrc_base + DDRC_SWSTAT_OFFSET); + volatile uint32_t *poisonaddr0; + uint32_t paddr0_val; + volatile uint32_t *poisonaddr1; + uint32_t paddr1_val; + DDR_Error_Info info; + poisonaddr0 = (uint32_t *)(ddrc_base + DDRC_ECCPOISONADDR0_OFFSET); + poisonaddr1 = (uint32_t *)(ddrc_base + DDRC_ECCPOISONADDR1_OFFSET); + + info.address = (uint64_t)(uintptr_t)poison_addr; + /* convert address to SDRAM address components */ + decompose_address(&info); + + /* select correctable/uncorrectable */ + ecccfg1_val &= ~DDRC_ECCCFG1_POISON_SINGLE; + if (correctable) { + ecccfg1_val |= DDRC_ECCCFG1_POISON_SINGLE; + } + + /* enable poisoning */ + ecccfg1_val |= DDRC_ECCCFG1_POISON_EN; + + uint32_t isr_cookie; + rtems_interrupt_local_disable(isr_cookie); + /* swctl must be unset to allow modification of ecccfg1 */ + *swctl = 0; + set_ecccfg1(ecccfg1, ecccfg1_val); + *swctl = 1; + rtems_interrupt_local_enable(isr_cookie); + + /* Wait for swctl propagation to swstat */ + while ((*swstat & 0x1) == 0); + + /* select target address */ + paddr0_val = DDRC_ECCPOISONADDR0_COL_SET(0, info.column); + if (info.rank) { + paddr0_val |= DDRC_ECCPOISONADDR0_RANK; + } + *poisonaddr0 = paddr0_val; + + paddr1_val = DDRC_ECCPOISONADDR1_BG_SET(0, info.bank_group); + paddr1_val = DDRC_ECCPOISONADDR1_BANK_SET(paddr1_val, info.bank); + paddr1_val = DDRC_ECCPOISONADDR1_ROW_SET(paddr1_val, info.row); + *poisonaddr1 = paddr1_val; + + /* write to poison address */ + *poison_addr = 0x5555555555555555UL; + + /* flush cache to force write */ + rtems_cache_flush_multiple_data_lines(poison_addr, sizeof(*poison_addr)); + + /* invalidate cache to force read */ + rtems_cache_invalidate_multiple_data_lines(poison_addr, sizeof(*poison_addr)); + + /* Force a data sync barrier */ + _AARCH64_Data_synchronization_barrier(); + + /* read from poison address to generate event */ + read_var = *poison_addr; + read_var++; + + volatile uint32_t *qis = (uint32_t *)(ddr_qos_ctrl_base + DDR_QIS_OFFSET); + + /* disable poisoning */ + *swctl = 0; + *ecccfg1 = 0; + *swctl = 1; +} +#endif diff --git a/bsps/aarch64/xilinx-zynqmp/ecc/ocm.c b/bsps/aarch64/xilinx-zynqmp/ecc/ocm.c new file mode 100644 index 0000000000..e097c6feb0 --- /dev/null +++ b/bsps/aarch64/xilinx-zynqmp/ecc/ocm.c @@ -0,0 +1,300 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/** + * @file + * + * @ingroup RTEMSBSPsAArch64XilinxZynqMP + * + * @brief This source file contains the implementation of OCM ECC support. + */ + +/* + * Copyright (C) 2023 On-Line Applications Research Corporation (OAR) + * Written by Kinsey Moore + * + * 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. + * + * 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 OWNER 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 +#include +#include +#include +#include + +static uintptr_t ocm_base = 0xFF960000; + +/* ECC Control */ +#define OCM_ECC_CTRL 0x14 +/* 0 -> single error, 1 -> continuous errors */ +#define OCM_ECC_CTRL_FI_MODE BSP_BIT32(2) +/* When bit is set, detection occurs without correction */ +#define OCM_ECC_CTRL_DET_ONLY BSP_BIT32(1) +/* Enable ECC, should only be modified at system boot */ +#define OCM_ECC_CTRL_ECC_ON_OFF BSP_BIT32(0) + +/* Interrupt Enable */ +#define OCM_IE 0xc +#define OCM_IE_UE_RMW BSP_BIT32(10) +#define OCM_IE_UE BSP_BIT32(7) +#define OCM_IE_CE BSP_BIT32(6) + +/* Error Response Control */ +#define OCM_ERR_CTRL 0x0 +#define OCM_ERR_CTRL_UE_RES BSP_BIT32(3) + +/* + * Fault Injection Data, four registers comprising 16 bytes of a data word + * + * Bits set to 1 toggle the corresponding bits of the next written word during a + * fault injection. + */ +#define OCM_FI_D0 0x4c +#define OCM_FI_D1 0x50 +#define OCM_FI_D2 0x54 +#define OCM_FI_D3 0x58 + +/* Fault Injection Syndrome */ +#define OCM_FI_SY 0x5c +#define OCM_FI_SY_DATA(val) BSP_FLD32(val, 0, 15) +#define OCM_FI_SY_DATA_GET(reg) BSP_FLD32GET(reg, 0, 15) +#define OCM_FI_SY_DATA_SET(reg, val) BSP_FLD32SET(reg, val, 0, 15) + +/* Fault Injection Counter */ +#define OCM_FI_CNTR 0x74 +#define OCM_FI_CNTR_COUNT(val) BSP_FLD32(val, 0, 23) +#define OCM_FI_CNTR_COUNT_GET(reg) BSP_FLD32GET(reg, 0, 23) +#define OCM_FI_CNTR_COUNT_SET(reg, val) BSP_FLD32SET(reg, val, 0, 23) + +/* Interrupt Status */ +#define OCM_IS 0x4 +#define OCM_IS_UE_RMW BSP_BIT32(10) +#define OCM_IS_UE BSP_BIT32(7) +#define OCM_IS_CE BSP_BIT32(6) + +/* Interrupt Mask */ +#define OCM_IM 0x8 +#define OCM_IM_UE_RMW BSP_BIT32(10) +#define OCM_IM_UE BSP_BIT32(7) +#define OCM_IM_CE BSP_BIT32(6) + +void zynqmp_ocm_inject_fault( void ) +{ + volatile uint32_t *fi_d0 = (uint32_t*)(ocm_base + OCM_FI_D0); + volatile uint32_t *fi_cnt = (uint32_t*)(ocm_base + OCM_FI_CNTR); + volatile uint64_t *ocm_top = (uint64_t*)0xFFFFFFF0U; + volatile uint32_t *ecc_ctrl = (uint32_t*)(ocm_base + OCM_ECC_CTRL); + uint64_t ocm_tmp = *ocm_top; + + /* Configure OCM to throw constant errors */ + *ecc_ctrl |= OCM_ECC_CTRL_FI_MODE; + + /* Inject a single bit error */ + *fi_d0 = 1; + + /* Configure the clock count after which errors will begin */ + *fi_cnt = 0; + + /* Insert a memory barrier to ensure that fault injection is active */ + _AARCH64_Data_synchronization_barrier(); + + /* trigger fault with a write of data that was already at the given address */ + *ocm_top = 0; + + /* Insert a memory barrier to prevent optimization */ + _AARCH64_Data_synchronization_barrier(); + + /* Perform read to force reporting of the error */ + *ocm_top; + + /* Disable constant fault mode */ + *ecc_ctrl &= ~(OCM_ECC_CTRL_FI_MODE); + + /* Insert a memory barrier to ensure the mode has changed */ + _AARCH64_Data_synchronization_barrier(); + + /* Reset to original value now that constant errors are disabled */ + *ocm_top = ocm_tmp; +} + +/* Correctable Error First Failing Address */ +#define OCM_CE_FFA 0x1c +#define OCM_CE_FFA_ADDR(val) BSP_FLD32(val, 0, 17) +#define OCM_CE_FFA_ADDR_GET(reg) BSP_FLD32GET(reg, 0, 17) +#define OCM_CE_FFA_ADDR_SET(reg, val) BSP_FLD32SET(reg, val, 0, 17) + +/* Correctable Error First Failing Data, four registers comprising 16 bytes */ +#define OCM_CE_FFD0 0x20 +#define OCM_CE_FFD1 0x24 +#define OCM_CE_FFD2 0x28 +#define OCM_CE_FFD3 0x2c + +/* Correctable Error First Failing ECC */ +#define OCM_CE_FFE 0x1c +#define OCM_CE_FFE_SYNDROME(val) BSP_FLD32(val, 0, 15) +#define OCM_CE_FFE_SYNDROME_GET(reg) BSP_FLD32GET(reg, 0, 15) +#define OCM_CE_FFE_SYNDROME_SET(reg, val) BSP_FLD32SET(reg, val, 0, 15) + +/* Uncorrectable Error First Failing Address */ +#define OCM_UE_FFA 0x34 +#define OCM_UE_FFA_ADDR(val) BSP_FLD32(val, 0, 17) +#define OCM_UE_FFA_ADDR_GET(reg) BSP_FLD32GET(reg, 0, 17) +#define OCM_UE_FFA_ADDR_SET(reg, val) BSP_FLD32SET(reg, val, 0, 17) + +/* Uncorrectable Error First Failing Data, four registers comprising 16 bytes */ +#define OCM_UE_FFD0 0x38 +#define OCM_UE_FFD1 0x3c +#define OCM_UE_FFD2 0x40 +#define OCM_UE_FFD3 0x44 + +/* Uncorrectable Error First Failing ECC */ +#define OCM_UE_FFE 0x48 +#define OCM_UE_FFE_SYNDROME(val) BSP_FLD32(val, 0, 15) +#define OCM_UE_FFE_SYNDROME_GET(reg) BSP_FLD32GET(reg, 0, 15) +#define OCM_UE_FFE_SYNDROME_SET(reg, val) BSP_FLD32SET(reg, val, 0, 15) + +/* Read/Modify/Write Uncorrectable Error First Failing Address */ +#define OCM_RMW_UE_FFA 0x70 +#define OCM_RMW_UE_FFA_ADDR(val) BSP_FLD32(val, 0, 17) +#define OCM_RMW_UE_FFA_ADDR_GET(reg) BSP_FLD32GET(reg, 0, 17) +#define OCM_RMW_UE_FFA_ADDR_SET(reg, val) BSP_FLD32SET(reg, val, 0, 17) + +static void ocm_handle_rmw( void ) +{ + volatile uint32_t *rmw_ffa = (uint32_t*)(ocm_base + OCM_RMW_UE_FFA); + OCM_Error_Info info; + + info.type = OCM_UNCORRECTABLE_RMW; + info.offset = OCM_RMW_UE_FFA_ADDR_GET(*rmw_ffa); + zynqmp_invoke_ecc_handler(OCM_RAM, &info); +} + +static void ocm_handle_ce( void ) +{ + volatile uint32_t *ce_ffa = (uint32_t*)(ocm_base + OCM_CE_FFA); + volatile uint32_t *ce_ffe = (uint32_t*)(ocm_base + OCM_CE_FFA); + volatile uint32_t *ce_ffd0 = (uint32_t*)(ocm_base + OCM_CE_FFD0); + volatile uint32_t *ce_ffd1 = (uint32_t*)(ocm_base + OCM_CE_FFD1); + volatile uint32_t *ce_ffd2 = (uint32_t*)(ocm_base + OCM_CE_FFD2); + volatile uint32_t *ce_ffd3 = (uint32_t*)(ocm_base + OCM_CE_FFD3); + OCM_Error_Info info; + + info.type = OCM_CORRECTABLE; + info.offset = OCM_CE_FFA_ADDR_GET(*ce_ffa); + info.data0 = *ce_ffd0; + info.data1 = *ce_ffd1; + info.data2 = *ce_ffd2; + info.data3 = *ce_ffd3; + info.syndrome = OCM_CE_FFE_SYNDROME_GET(*ce_ffe); + zynqmp_invoke_ecc_handler(OCM_RAM, &info); +} + +static void ocm_handle_ue( void ) +{ + volatile uint32_t *ue_ffa = (uint32_t*)(ocm_base + OCM_UE_FFA); + volatile uint32_t *ue_ffe = (uint32_t*)(ocm_base + OCM_UE_FFA); + volatile uint32_t *ue_ffd0 = (uint32_t*)(ocm_base + OCM_UE_FFD0); + volatile uint32_t *ue_ffd1 = (uint32_t*)(ocm_base + OCM_UE_FFD1); + volatile uint32_t *ue_ffd2 = (uint32_t*)(ocm_base + OCM_UE_FFD2); + volatile uint32_t *ue_ffd3 = (uint32_t*)(ocm_base + OCM_UE_FFD3); + OCM_Error_Info info; + + info.type = OCM_UNCORRECTABLE; + info.offset = OCM_UE_FFA_ADDR_GET(*ue_ffa); + info.data0 = *ue_ffd0; + info.data1 = *ue_ffd1; + info.data2 = *ue_ffd2; + info.data3 = *ue_ffd3; + info.syndrome = OCM_UE_FFE_SYNDROME_GET(*ue_ffe); + zynqmp_invoke_ecc_handler(OCM_RAM, &info); +} + +static void ocm_handler(void *arg) +{ + volatile uint32_t *ocm_is = (uint32_t*)(ocm_base + OCM_IS); + uint32_t ocm_is_value = *ocm_is; + (void) arg; + + /* Check and clear each error type after handling */ + if ((ocm_is_value & OCM_IS_UE_RMW) != 0) { + ocm_handle_rmw(); + *ocm_is = OCM_IS_UE_RMW; + } + + if ((ocm_is_value & OCM_IS_CE) != 0) { + ocm_handle_ce(); + *ocm_is = OCM_IS_CE; + } + + if ((ocm_is_value & OCM_IS_UE) != 0) { + ocm_handle_ue(); + *ocm_is = OCM_IS_UE; + } +} + +static rtems_interrupt_entry zynqmp_ocm_ecc_entry; + +rtems_status_code zynqmp_configure_ocm_ecc( void ) +{ + volatile uint32_t *err_ctrl = (uint32_t*)(ocm_base + OCM_ERR_CTRL); + volatile uint32_t *ecc_ctrl = (uint32_t*)(ocm_base + OCM_ECC_CTRL); + volatile uint32_t *int_enable = (uint32_t*)(ocm_base + OCM_IE); + rtems_status_code sc; + + rtems_interrupt_entry_initialize( + &zynqmp_ocm_ecc_entry, + ocm_handler, + NULL, + "OCM RAM ECC" + ); + + sc = rtems_interrupt_entry_install( + ZYNQMP_IRQ_OCM, + RTEMS_INTERRUPT_SHARED, + &zynqmp_ocm_ecc_entry + ); + + if (sc != RTEMS_SUCCESSFUL) { + return sc; + } + + if ((*ecc_ctrl & OCM_ECC_CTRL_ECC_ON_OFF) == 0) { + /* + * ECC is not enabled and should already have been by BOOT.bin. Enabling it + * now could corrupt existing data in the OCM. + */ + return RTEMS_NOT_CONFIGURED; + } + + /* + * OCM_ERR_CTRL.UE_RES forces generation of a synchronous external abort + * instead of using interrupts to signal the fault + */ + *err_ctrl &= ~(OCM_ERR_CTRL_UE_RES); + + /* Ensure ECC_CTRL is in the right state */ + *ecc_ctrl &= ~(OCM_ECC_CTRL_DET_ONLY); + + /* enable correctable and uncorrectable error interrupts */ + *int_enable = OCM_IE_CE | OCM_IE_UE | OCM_IE_UE_RMW; + + return RTEMS_SUCCESSFUL; +} diff --git a/bsps/aarch64/xilinx-zynqmp/include/bsp/ecc.h b/bsps/aarch64/xilinx-zynqmp/include/bsp/ecc.h new file mode 100644 index 0000000000..a718549b2a --- /dev/null +++ b/bsps/aarch64/xilinx-zynqmp/include/bsp/ecc.h @@ -0,0 +1,209 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/** + * @file + * + * @ingroup RTEMSBSPsAArch64XilinxZynqMP + * + * @brief This header file provides internal APIs for managing ECC events. + */ + +/* + * Copyright (C) 2024 On-Line Applications Research Corporation (OAR) + * Written by Kinsey Moore + * + * 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. + * + * 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 OWNER 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. + */ + +#ifndef LIBBSP_AARCH64_XILINX_ZYNQMP_BSP_ECC_H +#define LIBBSP_AARCH64_XILINX_ZYNQMP_BSP_ECC_H + +/** + * @addtogroup RTEMSBSPsAArch64 + * + * @{ + */ + +//#include + +#ifndef ASM + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * @brief Enumeration describing the possible types of ECC events + */ +typedef enum { + /* L1 Cache event information is delivered via Cache_Error_Event struct. */ + L1_CACHE, + /* L2 Cache event information is delivered via Cache_Error_Event struct. */ + L2_CACHE, + /* + * L1 and L2 cache are on a combined interrupt on ZynqMP. They are enabled as + * a single unit. The above individual L1 and L2 cache definitions will be + * used for reporting. Attempting to enable L1 or L2 individually will enable + * both. + */ + L1_L2_CACHE, + OCM_RAM, + DDR_RAM, +} ECC_Event_Type; + +/** + * @brief The specific locations where a cache error can originate + */ +typedef enum { + RAM_ID_L1I_TAG, + RAM_ID_L1I_DATA, + RAM_ID_L1D_TAG, + RAM_ID_L1D_DATA, + RAM_ID_L1D_DIRTY, + RAM_ID_TLB, + RAM_ID_L2_TAG, + RAM_ID_L2_DATA, + RAM_ID_SCU, + RAM_ID_UNKNOWN +} Cache_Error_RAM_ID; + +/** + * @brief Structure containing information about a Cache error + */ +typedef struct { + /* Indicates the RAM index address */ + uint64_t address; + /* Indicates the type of RAM where the error originated */ + Cache_Error_RAM_ID ramid; + /* + * Indicates the segment (way or bank) of the RAM where the error originated. + * Does not apply to L1D_DIRTY RAM ID. For SCU errors, this also indicates the + * associated CPU. + */ + uint8_t segment; + /* The number of times this specific error has occurred since last reset */ + uint8_t repeats; + /* The number of times other errors have occurred since last reset */ + uint8_t other_errors; + /* Whether any of the errors represented have caused a data abort */ + bool abort; +} Cache_Error_Event; + +/** + * @brief Typedef for ECC handlers + * + * Functions matching this prototype can be registered as the handler for ECC + * event callbacks. The data argument is a struct describing the event that + * occurred. + */ +typedef void (*zynqmp_ecc_handler)( ECC_Event_Type event, void *data ); + +/** + * @brief Enumeration describing the possible types of ECC events + * + * Note that the provided handler may be called from interrupt context. + * + * @param handler The handler to be called for all ECC error events + */ +void zynqmp_ecc_register_handler( zynqmp_ecc_handler handler ); + +/** + * @brief Enable ECC error reporting + * + * Enables ECC error reporting for the specified subsystem. + * + * @param event The ECC error event type to enable + */ +int zynqmp_ecc_enable( ECC_Event_Type event ); + +/** + * @brief Injects an ECC fault in the On-Chip Memory (OCM) + */ +void zynqmp_ocm_inject_fault( void ); + +/** + * @brief The types of OCM ECC errors + */ +typedef enum { + OCM_UNCORRECTABLE, + OCM_UNCORRECTABLE_RMW, + OCM_CORRECTABLE +} OCM_Error_Type; + +/** + * @brief Structure containing information about a OCM ECC error + */ +typedef struct { + /* Describes the type of error being reported */ + OCM_Error_Type type; + /* The offset into OCM where the error occurred */ + uint32_t offset; + /* The data relevant to the error. Does not apply to RMW errors */ + uint32_t data0; + uint32_t data1; + uint32_t data2; + uint32_t data3; + /* The ECC syndrome relevant to the error. Does not apply to RMW errors */ + uint16_t syndrome; +} OCM_Error_Info; + +/** + * @brief The types of DDR ECC errors + */ +typedef enum { + DDR_UNCORRECTABLE, + DDR_CORRECTABLE +} DDR_Error_Type; + +/** + * @brief Structure containing information about a DDR ECC error + */ +typedef struct { + /* Describes the type of error being reported */ + DDR_Error_Type type; + /* The DDR Rank where the error occurred */ + uint32_t rank; + /* The DDR Bank Group where the error occurred */ + uint32_t bank_group; + /* The DDR Bank where the error occurred */ + uint32_t bank; + /* The DDR Row where the error occurred */ + uint32_t row; + /* The DDR Column where the error occurred */ + uint32_t column; + /* + * When mapping from SDRAM addressing back to AXI addressing, this is will + * only be a close approximation of the source address since bits can be + * discarded when converting from AXI to SDRAM. + */ + uint64_t address; +} DDR_Error_Info; + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* ASM */ + +/** @} */ + +#endif /* LIBBSP_AARCH64_XILINX_ZYNQMP_BSP_ECC_H */ diff --git a/bsps/aarch64/xilinx-zynqmp/include/bsp/ecc_priv.h b/bsps/aarch64/xilinx-zynqmp/include/bsp/ecc_priv.h new file mode 100644 index 0000000000..f7bed638d3 --- /dev/null +++ b/bsps/aarch64/xilinx-zynqmp/include/bsp/ecc_priv.h @@ -0,0 +1,104 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/** + * @file + * + * @ingroup RTEMSBSPsAArch64XilinxZynqMP + * + * @brief This header file provides internal APIs for managing ECC events. + */ + +/* + * Copyright (C) 2024 On-Line Applications Research Corporation (OAR) + * Written by Kinsey Moore + * + * 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. + * + * 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 OWNER 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. + */ + +#ifndef LIBBSP_AARCH64_XILINX_ZYNQMP_BSP_ECC_PRIV_H +#define LIBBSP_AARCH64_XILINX_ZYNQMP_BSP_ECC_PRIV_H + +/** + * @addtogroup RTEMSBSPsAArch64 + * + * @{ + */ + +#include + +#ifndef ASM + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include + +/** + * @brief Initialize ECC reporting support + * + * This initializes the base ECC event reporting support for the platform. + */ +void zynqmp_ecc_init( void ); + +/** + * @brief Initialize BSP-specific ECC reporting + * + * Various BSPs may have different ECC capabilities. This allows those BSPs to + * initialize those facilities as necessary. + */ +void zynqmp_ecc_init_bsp( void ); + +/** + * @brief Configure Cache ECC reporting + */ +rtems_status_code zynqmp_configure_cache_ecc( void ); + +/** + * @brief Configure On-Chip Memory (OCM) ECC reporting + */ +rtems_status_code zynqmp_configure_ocm_ecc( void ); + +/** + * @brief Configure DDR Memory ECC reporting + */ +rtems_status_code zynqmp_configure_ddr_ecc( void ); + +/** + * @brief Invoke the ECC error handler + * + * @param event The ECC error event type to be raised + * @param data The details associated with the raised ECC error + */ +void zynqmp_invoke_ecc_handler( ECC_Event_Type event, void *data ); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* ASM */ + +/** @} */ + +#endif /* LIBBSP_AARCH64_XILINX_ZYNQMP_BSP_ECC_PRIV_H */ diff --git a/bsps/aarch64/xilinx-zynqmp/include/bsp/irq.h b/bsps/aarch64/xilinx-zynqmp/include/bsp/irq.h index dd5fb6acfc..9f13f8f81b 100644 --- a/bsps/aarch64/xilinx-zynqmp/include/bsp/irq.h +++ b/bsps/aarch64/xilinx-zynqmp/include/bsp/irq.h @@ -52,6 +52,7 @@ extern "C" { /* Interrupts vectors */ #define BSP_TIMER_VIRT_PPI 27 #define BSP_TIMER_PHYS_NS_PPI 30 +#define ZYNQMP_IRQ_OCM 42 #define ZYNQMP_IRQ_QSPI 47 #define ZYNQMP_IRQ_I2C_0 49 #define ZYNQMP_IRQ_I2C_1 50 @@ -61,6 +62,8 @@ extern "C" { #define ZYNQMP_IRQ_ETHERNET_1 91 #define ZYNQMP_IRQ_ETHERNET_2 93 #define ZYNQMP_IRQ_ETHERNET_3 95 +#define ZYNQMP_IRQ_DDR 144 +#define ZYNQMP_IRQ_CACHE 183 /** @} */ diff --git a/bsps/aarch64/xilinx-zynqmp/start/bspstart.c b/bsps/aarch64/xilinx-zynqmp/start/bspstart.c index d75e5a1620..0b161bf619 100644 --- a/bsps/aarch64/xilinx-zynqmp/start/bspstart.c +++ b/bsps/aarch64/xilinx-zynqmp/start/bspstart.c @@ -36,6 +36,7 @@ #include #include +#include #include #include @@ -56,4 +57,5 @@ void bsp_start( void ) bsp_section_nocacheheap_begin, (uintptr_t) bsp_section_nocacheheap_size ); + zynqmp_ecc_init(); } diff --git a/bsps/aarch64/xilinx-zynqmp/start/bspstartecc_cfc400x.c b/bsps/aarch64/xilinx-zynqmp/start/bspstartecc_cfc400x.c new file mode 100644 index 0000000000..c7831cbfa6 --- /dev/null +++ b/bsps/aarch64/xilinx-zynqmp/start/bspstartecc_cfc400x.c @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/** + * @file + * + * @ingroup RTEMSBSPsAArch64XilinxZynqMP + * + * @brief This source file contains the implementation of zynqmp_ecc_init_bsp(). + */ + +/* + * Copyright (C) 2023 On-Line Applications Research Corporation (OAR) + * Written by Kinsey Moore + * + * 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. + * + * 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 OWNER 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 +#include + +void zynqmp_ecc_init_bsp( void ) +{ + zynqmp_ecc_enable( DDR_RAM ); +} + diff --git a/bsps/aarch64/xilinx-zynqmp/start/bspstartecc_hw.c b/bsps/aarch64/xilinx-zynqmp/start/bspstartecc_hw.c new file mode 100644 index 0000000000..4eae50b6d4 --- /dev/null +++ b/bsps/aarch64/xilinx-zynqmp/start/bspstartecc_hw.c @@ -0,0 +1,92 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/** + * @file + * + * @ingroup RTEMSBSPsAArch64XilinxZynqMP + * + * @brief This source file contains the implementation of zynqmp_ecc_init(). + */ + +/* + * Copyright (C) 2023 On-Line Applications Research Corporation (OAR) + * Written by Kinsey Moore + * + * 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. + * + * 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 OWNER 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 +#include +#include +#include + +zynqmp_ecc_handler saved_handler = NULL; + +void zynqmp_ecc_register_handler( zynqmp_ecc_handler handler ) +{ + saved_handler = handler; +} + +void zynqmp_invoke_ecc_handler( ECC_Event_Type event, void *data ) +{ + if (saved_handler == NULL) { + bsp_fatal( BSP_FATAL_MEMORY_ECC_ERROR ); + } + + saved_handler(event, data); +} + +int zynqmp_ecc_enable( ECC_Event_Type event ) +{ + rtems_status_code sc; + + switch (event) { + case L1_CACHE: + case L2_CACHE: + case L1_L2_CACHE: + sc = zynqmp_configure_cache_ecc(); + break; + case OCM_RAM: + sc = zynqmp_configure_ocm_ecc(); + break; + case DDR_RAM: + sc = zynqmp_configure_ddr_ecc(); + break; + default: + return 1; + } + + if (sc != RTEMS_SUCCESSFUL) { + return 1; + } + return 0; +} + +void zynqmp_ecc_init( void ) +{ + /* Do something on hardware */ + zynqmp_ecc_enable( L1_L2_CACHE ); + zynqmp_ecc_enable( OCM_RAM ); + + /* Call BSP-specific init function */ + zynqmp_ecc_init_bsp(); +} diff --git a/bsps/aarch64/xilinx-zynqmp/start/bspstartecc_qemu.c b/bsps/aarch64/xilinx-zynqmp/start/bspstartecc_qemu.c new file mode 100644 index 0000000000..b01d67fe50 --- /dev/null +++ b/bsps/aarch64/xilinx-zynqmp/start/bspstartecc_qemu.c @@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/** + * @file + * + * @ingroup RTEMSBSPsAArch64XilinxZynqMP + * + * @brief This source file contains the implementation of zynqmp_ecc_init(). + */ + +/* + * Copyright (C) 2023 On-Line Applications Research Corporation (OAR) + * Written by Kinsey Moore + * + * 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. + * + * 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 OWNER 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 +#include + +void zynqmp_ecc_register_handler( zynqmp_ecc_handler handler ) +{ + (void) handler; + /* Do nothing on QEMU */ +} + +int zynqmp_ecc_enable( ECC_Event_Type event ) +{ + (void) event; + /* Do nothing on QEMU */ + return 0; +} +void zynqmp_ecc_init( void ) +{ + /* Do nothing on QEMU */ +} diff --git a/bsps/aarch64/xilinx-zynqmp/start/bspstartecc_zu3eg.c b/bsps/aarch64/xilinx-zynqmp/start/bspstartecc_zu3eg.c new file mode 100644 index 0000000000..ef798b6ad1 --- /dev/null +++ b/bsps/aarch64/xilinx-zynqmp/start/bspstartecc_zu3eg.c @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/** + * @file + * + * @ingroup RTEMSBSPsAArch64XilinxZynqMP + * + * @brief This source file contains the implementation of zynqmp_ecc_init_bsp(). + */ + +/* + * Copyright (C) 2023 On-Line Applications Research Corporation (OAR) + * Written by Kinsey Moore + * + * 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. + * + * 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 OWNER 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 +#include + +void zynqmp_ecc_init_bsp( void ) +{ + /* Do nothing special for ZU3EG BSPs */ +} diff --git a/bsps/aarch64/xilinx-zynqmp/start/bspstartmmu.c b/bsps/aarch64/xilinx-zynqmp/start/bspstartmmu.c index e727f9b1de..4ee0e1ebd9 100644 --- a/bsps/aarch64/xilinx-zynqmp/start/bspstartmmu.c +++ b/bsps/aarch64/xilinx-zynqmp/start/bspstartmmu.c @@ -50,6 +50,11 @@ zynqmp_mmu_config_table[] = { .begin = 0xfd000000U, .end = 0xffc00000U, .flags = AARCH64_MMU_DEVICE + /* Map OCM space */ + }, { + .begin = 0xfffc0000U, + .end = 0x100000000U, + .flags = AARCH64_MMU_DATA_RW }, { .begin = 0x80000000U, .end = 0x80100000U, diff --git a/bsps/include/bsp/fatal.h b/bsps/include/bsp/fatal.h index 98928e4756..c4cc1d0f18 100644 --- a/bsps/include/bsp/fatal.h +++ b/bsps/include/bsp/fatal.h @@ -74,6 +74,7 @@ typedef enum { BSP_FATAL_CONSOLE_REGISTER_DEV_2, BSP_FATAL_MMU_ADDRESS_INVALID, BSP_FATAL_HEAP_EXTEND_ERROR, + BSP_FATAL_MEMORY_ECC_ERROR, /* ARM fatal codes */ BSP_ARM_A9MPCORE_FATAL_CLOCK_IRQ_INSTALL = BSP_FATAL_CODE_BLOCK(1), diff --git a/spec/build/bsps/aarch64/xilinx-zynqmp/bspcfc400xlp64.yml b/spec/build/bsps/aarch64/xilinx-zynqmp/bspcfc400xlp64.yml index 1687ced857..a951a51d4c 100644 --- a/spec/build/bsps/aarch64/xilinx-zynqmp/bspcfc400xlp64.yml +++ b/spec/build/bsps/aarch64/xilinx-zynqmp/bspcfc400xlp64.yml @@ -17,5 +17,7 @@ links: uid: linkcmds_lp64 - role: build-dependency uid: objfdtcfc400x -source: [] +source: +- bsps/aarch64/xilinx-zynqmp/start/bspstartecc_hw.c +- bsps/aarch64/xilinx-zynqmp/start/bspstartecc_cfc400x.c type: build diff --git a/spec/build/bsps/aarch64/xilinx-zynqmp/bspqemuilp32.yml b/spec/build/bsps/aarch64/xilinx-zynqmp/bspqemuilp32.yml index 18968cd43d..322b7f5a54 100644 --- a/spec/build/bsps/aarch64/xilinx-zynqmp/bspqemuilp32.yml +++ b/spec/build/bsps/aarch64/xilinx-zynqmp/bspqemuilp32.yml @@ -19,5 +19,6 @@ links: uid: linkcmds_ilp32 - role: build-dependency uid: objfdtzynqmp -source: [] +source: +- bsps/aarch64/xilinx-zynqmp/start/bspstartecc_qemu.c type: build diff --git a/spec/build/bsps/aarch64/xilinx-zynqmp/bspqemulp64.yml b/spec/build/bsps/aarch64/xilinx-zynqmp/bspqemulp64.yml index 86177d7fcd..2bcbf320ee 100644 --- a/spec/build/bsps/aarch64/xilinx-zynqmp/bspqemulp64.yml +++ b/spec/build/bsps/aarch64/xilinx-zynqmp/bspqemulp64.yml @@ -19,5 +19,6 @@ links: uid: linkcmds_lp64 - role: build-dependency uid: objfdtzynqmp -source: [] +source: +- bsps/aarch64/xilinx-zynqmp/start/bspstartecc_qemu.c type: build diff --git a/spec/build/bsps/aarch64/xilinx-zynqmp/bspzu3egilp32.yml b/spec/build/bsps/aarch64/xilinx-zynqmp/bspzu3egilp32.yml index cd0bb2b854..6329164084 100644 --- a/spec/build/bsps/aarch64/xilinx-zynqmp/bspzu3egilp32.yml +++ b/spec/build/bsps/aarch64/xilinx-zynqmp/bspzu3egilp32.yml @@ -17,5 +17,7 @@ links: uid: linkcmds_ilp32 - role: build-dependency uid: objfdtzynqmp -source: [] +source: +- bsps/aarch64/xilinx-zynqmp/start/bspstartecc_hw.c +- bsps/aarch64/xilinx-zynqmp/start/bspstartecc_zu3eg.c type: build diff --git a/spec/build/bsps/aarch64/xilinx-zynqmp/bspzu3eglp64.yml b/spec/build/bsps/aarch64/xilinx-zynqmp/bspzu3eglp64.yml index 19211dae15..a2db0c1415 100644 --- a/spec/build/bsps/aarch64/xilinx-zynqmp/bspzu3eglp64.yml +++ b/spec/build/bsps/aarch64/xilinx-zynqmp/bspzu3eglp64.yml @@ -17,5 +17,7 @@ links: uid: linkcmds_lp64 - role: build-dependency uid: objfdtzynqmp -source: [] +source: +- bsps/aarch64/xilinx-zynqmp/start/bspstartecc_hw.c +- bsps/aarch64/xilinx-zynqmp/start/bspstartecc_zu3eg.c type: build diff --git a/spec/build/bsps/aarch64/xilinx-zynqmp/obj.yml b/spec/build/bsps/aarch64/xilinx-zynqmp/obj.yml index d390f9a111..aacbd45d20 100644 --- a/spec/build/bsps/aarch64/xilinx-zynqmp/obj.yml +++ b/spec/build/bsps/aarch64/xilinx-zynqmp/obj.yml @@ -20,6 +20,9 @@ source: - bsps/aarch64/shared/cache/cache.c - bsps/aarch64/shared/mmu/vmsav8-64.c - bsps/aarch64/xilinx-zynqmp/console/console.c +- bsps/aarch64/xilinx-zynqmp/ecc/cache.c +- bsps/aarch64/xilinx-zynqmp/ecc/ddr.c +- bsps/aarch64/xilinx-zynqmp/ecc/ocm.c - bsps/aarch64/xilinx-zynqmp/fdt/bsp_fdt.c - bsps/aarch64/xilinx-zynqmp/start/bspstart.c - bsps/aarch64/xilinx-zynqmp/start/bspstarthooks.c