Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:22:42

0001 /* SPDX-License-Identifier: BSD-2-Clause */
0002 
0003 /**
0004  * @file
0005  *
0006  * @ingroup RTEMSBSPsAArch64XilinxZynqMP
0007  *
0008  * @brief This source file contains the implementation of OCM ECC support.
0009  */
0010 
0011 /*
0012  * Copyright (C) 2023 On-Line Applications Research Corporation (OAR)
0013  * Written by Kinsey Moore <kinsey.moore@oarcorp.com>
0014  *
0015  * Redistribution and use in source and binary forms, with or without
0016  * modification, are permitted provided that the following conditions
0017  * are met:
0018  * 1. Redistributions of source code must retain the above copyright
0019  *    notice, this list of conditions and the following disclaimer.
0020  * 2. Redistributions in binary form must reproduce the above copyright
0021  *    notice, this list of conditions and the following disclaimer in the
0022  *    documentation and/or other materials provided with the distribution.
0023  *
0024  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0025  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0026  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0027  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
0028  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
0029  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
0030  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
0031  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
0032  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
0033  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
0034  * POSSIBILITY OF SUCH DAMAGE.
0035  */
0036 
0037 #include <bsp.h>
0038 #include <bsp/ecc_priv.h>
0039 #include <bsp/irq.h>
0040 #include <bsp/utility.h>
0041 #include <libcpu/mmu-vmsav8-64.h>
0042 
0043 static uintptr_t ocm_base = 0xFF960000;
0044 
0045 /* ECC Control */
0046 #define OCM_ECC_CTRL 0x14
0047 /* 0 -> single error, 1 -> continuous errors */
0048 #define OCM_ECC_CTRL_FI_MODE BSP_BIT32(2)
0049 /* When bit is set, detection occurs without correction */
0050 #define OCM_ECC_CTRL_DET_ONLY BSP_BIT32(1)
0051 /* Enable ECC, should only be modified at system boot */
0052 #define OCM_ECC_CTRL_ECC_ON_OFF BSP_BIT32(0)
0053 
0054 /* Interrupt Enable */
0055 #define OCM_IE 0xc
0056 #define OCM_IE_UE_RMW BSP_BIT32(10)
0057 #define OCM_IE_UE BSP_BIT32(7)
0058 #define OCM_IE_CE BSP_BIT32(6)
0059 
0060 /* Error Response Control */
0061 #define OCM_ERR_CTRL 0x0
0062 #define OCM_ERR_CTRL_UE_RES BSP_BIT32(3)
0063 
0064 /*
0065  * Fault Injection Data, four registers comprising 16 bytes of a data word
0066  *
0067  * Bits set to 1 toggle the corresponding bits of the next written word during a
0068  * fault injection.
0069  */
0070 #define OCM_FI_D0 0x4c
0071 #define OCM_FI_D1 0x50
0072 #define OCM_FI_D2 0x54
0073 #define OCM_FI_D3 0x58
0074 
0075 /* Fault Injection Syndrome */
0076 #define OCM_FI_SY 0x5c
0077 #define OCM_FI_SY_DATA(val) BSP_FLD32(val, 0, 15)
0078 #define OCM_FI_SY_DATA_GET(reg) BSP_FLD32GET(reg, 0, 15)
0079 #define OCM_FI_SY_DATA_SET(reg, val) BSP_FLD32SET(reg, val, 0, 15)
0080 
0081 /* Fault Injection Counter */
0082 #define OCM_FI_CNTR 0x74
0083 #define OCM_FI_CNTR_COUNT(val) BSP_FLD32(val, 0, 23)
0084 #define OCM_FI_CNTR_COUNT_GET(reg) BSP_FLD32GET(reg, 0, 23)
0085 #define OCM_FI_CNTR_COUNT_SET(reg, val) BSP_FLD32SET(reg, val, 0, 23)
0086 
0087 /* Interrupt Status */
0088 #define OCM_IS 0x4
0089 #define OCM_IS_UE_RMW BSP_BIT32(10)
0090 #define OCM_IS_UE BSP_BIT32(7)
0091 #define OCM_IS_CE BSP_BIT32(6)
0092 
0093 /* Interrupt Mask */
0094 #define OCM_IM 0x8
0095 #define OCM_IM_UE_RMW BSP_BIT32(10)
0096 #define OCM_IM_UE BSP_BIT32(7)
0097 #define OCM_IM_CE BSP_BIT32(6)
0098 
0099 void zynqmp_ocm_inject_fault( void )
0100 {
0101   volatile uint32_t *fi_d0 = (uint32_t*)(ocm_base + OCM_FI_D0);
0102   volatile uint32_t *fi_cnt = (uint32_t*)(ocm_base + OCM_FI_CNTR);
0103   volatile uint64_t *ocm_top = (uint64_t*)0xFFFFFFF0U;
0104   volatile uint32_t *ecc_ctrl = (uint32_t*)(ocm_base + OCM_ECC_CTRL);
0105   uint64_t ocm_tmp = *ocm_top;
0106 
0107   /* Configure OCM to throw constant errors */
0108   *ecc_ctrl |= OCM_ECC_CTRL_FI_MODE;
0109 
0110   /* Inject a single bit error */
0111   *fi_d0 = 1;
0112 
0113   /* Configure the clock count after which errors will begin */
0114   *fi_cnt = 0;
0115 
0116   /* Insert a memory barrier to ensure that fault injection is active */
0117   _AARCH64_Data_synchronization_barrier();
0118 
0119   /* trigger fault with a write of data that was already at the given address */
0120   *ocm_top = 0;
0121 
0122   /* Insert a memory barrier to prevent optimization */
0123   _AARCH64_Data_synchronization_barrier();
0124 
0125   /* Perform read to force reporting of the error */
0126   *ocm_top;
0127 
0128   /* Disable constant fault mode */
0129   *ecc_ctrl &= ~(OCM_ECC_CTRL_FI_MODE);
0130 
0131   /* Insert a memory barrier to ensure the mode has changed */
0132   _AARCH64_Data_synchronization_barrier();
0133 
0134   /* Reset to original value now that constant errors are disabled */
0135   *ocm_top = ocm_tmp;
0136 }
0137 
0138 /* Correctable Error First Failing Address */
0139 #define OCM_CE_FFA 0x1c
0140 #define OCM_CE_FFA_ADDR(val) BSP_FLD32(val, 0, 17)
0141 #define OCM_CE_FFA_ADDR_GET(reg) BSP_FLD32GET(reg, 0, 17)
0142 #define OCM_CE_FFA_ADDR_SET(reg, val) BSP_FLD32SET(reg, val, 0, 17)
0143 
0144 /* Correctable Error First Failing Data, four registers comprising 16 bytes */
0145 #define OCM_CE_FFD0 0x20
0146 #define OCM_CE_FFD1 0x24
0147 #define OCM_CE_FFD2 0x28
0148 #define OCM_CE_FFD3 0x2c
0149 
0150 /* Correctable Error First Failing ECC */
0151 #define OCM_CE_FFE 0x1c
0152 #define OCM_CE_FFE_SYNDROME(val) BSP_FLD32(val, 0, 15)
0153 #define OCM_CE_FFE_SYNDROME_GET(reg) BSP_FLD32GET(reg, 0, 15)
0154 #define OCM_CE_FFE_SYNDROME_SET(reg, val) BSP_FLD32SET(reg, val, 0, 15)
0155 
0156 /* Uncorrectable Error First Failing Address */
0157 #define OCM_UE_FFA 0x34
0158 #define OCM_UE_FFA_ADDR(val) BSP_FLD32(val, 0, 17)
0159 #define OCM_UE_FFA_ADDR_GET(reg) BSP_FLD32GET(reg, 0, 17)
0160 #define OCM_UE_FFA_ADDR_SET(reg, val) BSP_FLD32SET(reg, val, 0, 17)
0161 
0162 /* Uncorrectable Error First Failing Data, four registers comprising 16 bytes */
0163 #define OCM_UE_FFD0 0x38
0164 #define OCM_UE_FFD1 0x3c
0165 #define OCM_UE_FFD2 0x40
0166 #define OCM_UE_FFD3 0x44
0167 
0168 /* Uncorrectable Error First Failing ECC */
0169 #define OCM_UE_FFE 0x48
0170 #define OCM_UE_FFE_SYNDROME(val) BSP_FLD32(val, 0, 15)
0171 #define OCM_UE_FFE_SYNDROME_GET(reg) BSP_FLD32GET(reg, 0, 15)
0172 #define OCM_UE_FFE_SYNDROME_SET(reg, val) BSP_FLD32SET(reg, val, 0, 15)
0173 
0174 /* Read/Modify/Write Uncorrectable Error First Failing Address */
0175 #define OCM_RMW_UE_FFA 0x70
0176 #define OCM_RMW_UE_FFA_ADDR(val) BSP_FLD32(val, 0, 17)
0177 #define OCM_RMW_UE_FFA_ADDR_GET(reg) BSP_FLD32GET(reg, 0, 17)
0178 #define OCM_RMW_UE_FFA_ADDR_SET(reg, val) BSP_FLD32SET(reg, val, 0, 17)
0179 
0180 static void ocm_handle_rmw( void )
0181 {
0182   volatile uint32_t *rmw_ffa = (uint32_t*)(ocm_base + OCM_RMW_UE_FFA);
0183   OCM_Error_Info info;
0184 
0185   info.type = OCM_UNCORRECTABLE_RMW;
0186   info.offset = OCM_RMW_UE_FFA_ADDR_GET(*rmw_ffa);
0187   zynqmp_invoke_ecc_handler(OCM_RAM, &info);
0188 }
0189 
0190 static void ocm_handle_ce( void )
0191 {
0192   volatile uint32_t *ce_ffa = (uint32_t*)(ocm_base + OCM_CE_FFA);
0193   volatile uint32_t *ce_ffe = (uint32_t*)(ocm_base + OCM_CE_FFA);
0194   volatile uint32_t *ce_ffd0 = (uint32_t*)(ocm_base + OCM_CE_FFD0);
0195   volatile uint32_t *ce_ffd1 = (uint32_t*)(ocm_base + OCM_CE_FFD1);
0196   volatile uint32_t *ce_ffd2 = (uint32_t*)(ocm_base + OCM_CE_FFD2);
0197   volatile uint32_t *ce_ffd3 = (uint32_t*)(ocm_base + OCM_CE_FFD3);
0198   OCM_Error_Info info;
0199 
0200   info.type = OCM_CORRECTABLE;
0201   info.offset = OCM_CE_FFA_ADDR_GET(*ce_ffa);
0202   info.data0 = *ce_ffd0;
0203   info.data1 = *ce_ffd1;
0204   info.data2 = *ce_ffd2;
0205   info.data3 = *ce_ffd3;
0206   info.syndrome = OCM_CE_FFE_SYNDROME_GET(*ce_ffe);
0207   zynqmp_invoke_ecc_handler(OCM_RAM, &info);
0208 }
0209 
0210 static void ocm_handle_ue( void )
0211 {
0212   volatile uint32_t *ue_ffa = (uint32_t*)(ocm_base + OCM_UE_FFA);
0213   volatile uint32_t *ue_ffe = (uint32_t*)(ocm_base + OCM_UE_FFA);
0214   volatile uint32_t *ue_ffd0 = (uint32_t*)(ocm_base + OCM_UE_FFD0);
0215   volatile uint32_t *ue_ffd1 = (uint32_t*)(ocm_base + OCM_UE_FFD1);
0216   volatile uint32_t *ue_ffd2 = (uint32_t*)(ocm_base + OCM_UE_FFD2);
0217   volatile uint32_t *ue_ffd3 = (uint32_t*)(ocm_base + OCM_UE_FFD3);
0218   OCM_Error_Info info;
0219 
0220   info.type = OCM_UNCORRECTABLE;
0221   info.offset = OCM_UE_FFA_ADDR_GET(*ue_ffa);
0222   info.data0 = *ue_ffd0;
0223   info.data1 = *ue_ffd1;
0224   info.data2 = *ue_ffd2;
0225   info.data3 = *ue_ffd3;
0226   info.syndrome = OCM_UE_FFE_SYNDROME_GET(*ue_ffe);
0227   zynqmp_invoke_ecc_handler(OCM_RAM, &info);
0228 }
0229 
0230 static void ocm_handler(void *arg)
0231 {
0232   volatile uint32_t *ocm_is = (uint32_t*)(ocm_base + OCM_IS);
0233   uint32_t ocm_is_value = *ocm_is;
0234   (void) arg;
0235 
0236   /* Check and clear each error type after handling */
0237   if ((ocm_is_value & OCM_IS_UE_RMW) != 0) {
0238     ocm_handle_rmw();
0239     *ocm_is = OCM_IS_UE_RMW;
0240   }
0241 
0242   if ((ocm_is_value & OCM_IS_CE) != 0) {
0243     ocm_handle_ce();
0244     *ocm_is = OCM_IS_CE;
0245   }
0246 
0247   if ((ocm_is_value & OCM_IS_UE) != 0) {
0248     ocm_handle_ue();
0249     *ocm_is = OCM_IS_UE;
0250   }
0251 }
0252 
0253 static rtems_interrupt_entry zynqmp_ocm_ecc_entry;
0254 
0255 rtems_status_code zynqmp_configure_ocm_ecc( void )
0256 {
0257   volatile uint32_t *err_ctrl = (uint32_t*)(ocm_base + OCM_ERR_CTRL);
0258   volatile uint32_t *ecc_ctrl = (uint32_t*)(ocm_base + OCM_ECC_CTRL);
0259   volatile uint32_t *int_enable = (uint32_t*)(ocm_base + OCM_IE);
0260   rtems_status_code sc;
0261 
0262   rtems_interrupt_entry_initialize(
0263     &zynqmp_ocm_ecc_entry,
0264     ocm_handler,
0265     NULL,
0266     "OCM RAM ECC"
0267   );
0268 
0269   sc = rtems_interrupt_entry_install(
0270     ZYNQMP_IRQ_OCM,
0271     RTEMS_INTERRUPT_SHARED,
0272     &zynqmp_ocm_ecc_entry
0273   );
0274 
0275   if (sc != RTEMS_SUCCESSFUL) {
0276     return sc;
0277   }
0278 
0279   if ((*ecc_ctrl & OCM_ECC_CTRL_ECC_ON_OFF) == 0) {
0280     /*
0281      * ECC is not enabled and should already have been by BOOT.bin. Enabling it
0282      * now could corrupt existing data in the OCM.
0283      */
0284     return RTEMS_NOT_CONFIGURED;
0285   }
0286 
0287   /*
0288    * OCM_ERR_CTRL.UE_RES forces generation of a synchronous external abort
0289    * instead of using interrupts to signal the fault
0290    */
0291   *err_ctrl &= ~(OCM_ERR_CTRL_UE_RES);
0292 
0293   /* Ensure ECC_CTRL is in the right state */
0294   *ecc_ctrl &= ~(OCM_ECC_CTRL_DET_ONLY);
0295 
0296   /* enable correctable and uncorrectable error interrupts */
0297   *int_enable = OCM_IE_CE | OCM_IE_UE | OCM_IE_UE_RMW;
0298 
0299   return RTEMS_SUCCESSFUL;
0300 }