/* * Copyright (c) 2021-2022, NVIDIA Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include #include #include #include #include #define GICFMU_IDLE_TIMEOUT_US U(2000000) /* Macro to write 32-bit FMU registers */ #define GIC_FMU_WRITE_32(base, reg, val) \ do { \ /* \ * This register receives the unlock key that is required for \ * writes to FMU registers to be successful. \ */ \ mmio_write_32(base + GICFMU_KEY, 0xBE); \ /* Perform the actual write */ \ mmio_write_32((base) + (reg), (val)); \ } while (false) /* Macro to write 64-bit FMU registers */ #define GIC_FMU_WRITE_64(base, reg, n, val) \ do { \ /* \ * This register receives the unlock key that is required for \ * writes to FMU registers to be successful. \ */ \ mmio_write_32(base + GICFMU_KEY, 0xBE); \ /* \ * APB bus is 32-bit wide; so split the 64-bit write into \ * two 32-bit writes \ */ \ mmio_write_32((base) + reg##_LO + (n * 64), (val)); \ mmio_write_32((base) + reg##_HI + (n * 64), (val)); \ } while (false) /* Helper function to wait until FMU is ready to accept the next command */ static void wait_until_fmu_is_idle(uintptr_t base) { uint32_t timeout_count = GICFMU_IDLE_TIMEOUT_US; uint64_t status; /* wait until status is 'busy' */ do { status = (gic_fmu_read_status(base) & BIT(0)); if (timeout_count-- == 0U) { ERROR("GIC600 AE FMU is not responding\n"); panic(); } udelay(1U); } while (status == U(0)); } #define GIC_FMU_WRITE_ON_IDLE_32(base, reg, val) \ do { \ /* Wait until FMU is ready */ \ wait_until_fmu_is_idle(base); \ /* Actual register write */ \ GIC_FMU_WRITE_32(base, reg, val); \ /* Wait until FMU is ready */ \ wait_until_fmu_is_idle(base); \ } while (false) #define GIC_FMU_WRITE_ON_IDLE_64(base, reg, n, val) \ do { \ /* Wait until FMU is ready */ \ wait_until_fmu_is_idle(base); \ /* Actual register write */ \ GIC_FMU_WRITE_64(base, reg, n, val); \ /* Wait until FMU is ready */ \ wait_until_fmu_is_idle(base); \ } while (false) /******************************************************************************* * GIC FMU functions for accessing the Fault Management Unit registers ******************************************************************************/ /* * Accessors to read the Error Record Feature Register bits corresponding * to an error record 'n' */ uint64_t gic_fmu_read_errfr(uintptr_t base, unsigned int n) { /* * APB bus is 32-bit wide; so split the 64-bit read into * two 32-bit reads */ uint64_t reg_val = (uint64_t)mmio_read_32(base + GICFMU_ERRFR_LO + n * 64U); reg_val |= ((uint64_t)mmio_read_32(base + GICFMU_ERRFR_HI + n * 64U) << 32); return reg_val; } /* * Accessors to read the Error Record Control Register bits corresponding * to an error record 'n' */ uint64_t gic_fmu_read_errctlr(uintptr_t base, unsigned int n) { /* * APB bus is 32-bit wide; so split the 64-bit read into * two 32-bit reads */ uint64_t reg_val = (uint64_t)mmio_read_32(base + GICFMU_ERRCTLR_LO + n * 64U); reg_val |= ((uint64_t)mmio_read_32(base + GICFMU_ERRCTLR_HI + n * 64U) << 32); return reg_val; } /* * Accessors to read the Error Record Primary Status Register bits * corresponding to an error record 'n' */ uint64_t gic_fmu_read_errstatus(uintptr_t base, unsigned int n) { /* * APB bus is 32-bit wide; so split the 64-bit read into * two 32-bit reads */ uint64_t reg_val = (uint64_t)mmio_read_32(base + GICFMU_ERRSTATUS_LO + n * 64U); reg_val |= ((uint64_t)mmio_read_32(base + GICFMU_ERRSTATUS_HI + n * 64U) << 32); return reg_val; } /* * Accessors to read the Error Group Status Register */ uint64_t gic_fmu_read_errgsr(uintptr_t base) { /* * APB bus is 32-bit wide; so split the 64-bit read into * two 32-bit reads */ uint64_t reg_val = (uint64_t)mmio_read_32(base + GICFMU_ERRGSR_LO); reg_val |= ((uint64_t)mmio_read_32(base + GICFMU_ERRGSR_HI) << 32); return reg_val; } /* * Accessors to read the Ping Control Register */ uint32_t gic_fmu_read_pingctlr(uintptr_t base) { return mmio_read_32(base + GICFMU_PINGCTLR); } /* * Accessors to read the Ping Now Register */ uint32_t gic_fmu_read_pingnow(uintptr_t base) { return mmio_read_32(base + GICFMU_PINGNOW); } /* * Accessors to read the Ping Mask Register */ uint64_t gic_fmu_read_pingmask(uintptr_t base) { /* * APB bus is 32-bit wide; so split the 64-bit read into * two 32-bit reads */ uint64_t reg_val = (uint64_t)mmio_read_32(base + GICFMU_PINGMASK_LO); reg_val |= ((uint64_t)mmio_read_32(base + GICFMU_PINGMASK_HI) << 32); return reg_val; } /* * Accessors to read the FMU Status Register */ uint32_t gic_fmu_read_status(uintptr_t base) { return mmio_read_32(base + GICFMU_STATUS); } /* * Accessors to read the Error Record ID Register */ uint32_t gic_fmu_read_erridr(uintptr_t base) { return mmio_read_32(base + GICFMU_ERRIDR); } /* * Accessors to write a 64 bit value to the Error Record Control Register */ void gic_fmu_write_errctlr(uintptr_t base, unsigned int n, uint64_t val) { GIC_FMU_WRITE_64(base, GICFMU_ERRCTLR, n, val); } /* * Accessors to write a 64 bit value to the Error Record Primary Status * Register */ void gic_fmu_write_errstatus(uintptr_t base, unsigned int n, uint64_t val) { /* Wait until FMU is ready before writing */ GIC_FMU_WRITE_ON_IDLE_64(base, GICFMU_ERRSTATUS, n, val); } /* * Accessors to write a 32 bit value to the Ping Control Register */ void gic_fmu_write_pingctlr(uintptr_t base, uint32_t val) { GIC_FMU_WRITE_32(base, GICFMU_PINGCTLR, val); } /* * Accessors to write a 32 bit value to the Ping Now Register */ void gic_fmu_write_pingnow(uintptr_t base, uint32_t val) { /* Wait until FMU is ready before writing */ GIC_FMU_WRITE_ON_IDLE_32(base, GICFMU_PINGNOW, val); } /* * Accessors to write a 32 bit value to the Safety Mechanism Enable Register */ void gic_fmu_write_smen(uintptr_t base, uint32_t val) { /* Wait until FMU is ready before writing */ GIC_FMU_WRITE_ON_IDLE_32(base, GICFMU_SMEN, val); } /* * Accessors to write a 32 bit value to the Safety Mechanism Inject Error * Register */ void gic_fmu_write_sminjerr(uintptr_t base, uint32_t val) { /* Wait until FMU is ready before writing */ GIC_FMU_WRITE_ON_IDLE_32(base, GICFMU_SMINJERR, val); } /* * Accessors to write a 64 bit value to the Ping Mask Register */ void gic_fmu_write_pingmask(uintptr_t base, uint64_t val) { GIC_FMU_WRITE_64(base, GICFMU_PINGMASK, 0, val); } /* * Helper function to disable all safety mechanisms for a given block */ void gic_fmu_disable_all_sm_blkid(uintptr_t base, unsigned int blkid) { uint32_t smen, max_smid = U(0); /* Sanity check block ID */ assert((blkid >= FMU_BLK_GICD) && (blkid <= FMU_BLK_PPI31)); /* Find the max safety mechanism ID for the block */ switch (blkid) { case FMU_BLK_GICD: max_smid = FMU_SMID_GICD_MAX; break; case FMU_BLK_SPICOL: max_smid = FMU_SMID_SPICOL_MAX; break; case FMU_BLK_WAKERQ: max_smid = FMU_SMID_WAKERQ_MAX; break; case FMU_BLK_ITS0...FMU_BLK_ITS7: max_smid = FMU_SMID_ITS_MAX; break; case FMU_BLK_PPI0...FMU_BLK_PPI31: max_smid = FMU_SMID_PPI_MAX; break; default: assert(false); break; } /* Disable all Safety Mechanisms for a given block id */ for (unsigned int i = 0U; i < max_smid; i++) { smen = (blkid << FMU_SMEN_BLK_SHIFT) | (i << FMU_SMEN_SMID_SHIFT); gic_fmu_write_smen(base, smen); } }