| /* |
| * Copyright (c) 2024, STMicroelectronics - All Rights Reserved |
| * |
| * SPDX-License-Identifier: BSD-3-Clause |
| */ |
| |
| #include <assert.h> |
| #include <limits.h> |
| |
| #include <arch_helpers.h> |
| #include <common/debug.h> |
| #include <drivers/st/bsec.h> |
| #include <drivers/st/bsec3_reg.h> |
| #include <drivers/st/stm32mp_reset.h> |
| #include <lib/mmio.h> |
| #include <lib/spinlock.h> |
| #include <libfdt.h> |
| |
| #include <platform_def.h> |
| |
| #define BSEC_IP_VERSION_1_0 U(0x10) |
| #define BSEC_IP_ID_3 U(0x100033) |
| |
| #define MAX_NB_TRIES U(3) |
| |
| /* |
| * IP configuration |
| */ |
| #define BSEC_OTP_MASK GENMASK_32(4, 0) |
| #define BSEC_OTP_BANK_SHIFT U(5) |
| #define BSEC_TIMEOUT_VALUE U(0x800000) /* ~7sec @1.2GHz */ |
| |
| /* Magic use to indicated valid SHADOW = 'B' 'S' 'E' 'C' */ |
| #define BSEC_MAGIC U(0x42534543) |
| |
| #define OTP_MAX_SIZE (STM32MP2_OTP_MAX_ID + U(1)) |
| |
| struct bsec_shadow { |
| uint32_t magic; |
| uint32_t state; |
| uint32_t value[OTP_MAX_SIZE]; |
| uint32_t status[OTP_MAX_SIZE]; |
| }; |
| |
| static uint32_t otp_bank(uint32_t otp) |
| { |
| if (otp > STM32MP2_OTP_MAX_ID) { |
| panic(); |
| } |
| |
| return (otp & ~BSEC_OTP_MASK) >> BSEC_OTP_BANK_SHIFT; |
| } |
| |
| static uint32_t otp_bit_mask(uint32_t otp) |
| { |
| return BIT(otp & BSEC_OTP_MASK); |
| } |
| |
| /* |
| * bsec_get_status: return status register value. |
| */ |
| static uint32_t bsec_get_status(void) |
| { |
| return mmio_read_32(BSEC_BASE + BSEC_OTPSR); |
| } |
| |
| /* |
| * bsec_get_version: return BSEC version. |
| */ |
| static uint32_t bsec_get_version(void) |
| { |
| return mmio_read_32(BSEC_BASE + BSEC_VERR) & BSEC_VERR_MASK; |
| } |
| |
| /* |
| * bsec_get_id: return BSEC ID. |
| */ |
| static uint32_t bsec_get_id(void) |
| { |
| return mmio_read_32(BSEC_BASE + BSEC_IPIDR); |
| } |
| |
| static bool is_fuse_shadowed(uint32_t otp) |
| { |
| uint32_t bank = otp_bank(otp); |
| uint32_t otp_mask = otp_bit_mask(otp); |
| uint32_t bank_value; |
| |
| bank_value = mmio_read_32(BSEC_BASE + BSEC_SFSR(bank)); |
| |
| if ((bank_value & otp_mask) != 0U) { |
| return true; |
| } |
| |
| return false; |
| } |
| |
| static void poll_otp_status_busy(void) |
| { |
| uint32_t timeout = BSEC_TIMEOUT_VALUE; |
| |
| while (((bsec_get_status() & BSEC_OTPSR_BUSY) != 0U) && (timeout != 0U)) { |
| timeout--; |
| } |
| |
| if ((bsec_get_status() & BSEC_OTPSR_BUSY) != 0U) { |
| ERROR("BSEC timeout\n"); |
| panic(); |
| } |
| } |
| |
| static uint32_t check_read_error(uint32_t otp) |
| { |
| uint32_t status = bsec_get_status(); |
| |
| if ((status & BSEC_OTPSR_SECF) != 0U) { |
| VERBOSE("BSEC read %u single error correction detected\n", otp); |
| } |
| |
| if ((status & BSEC_OTPSR_PPLF) != 0U) { |
| VERBOSE("BSEC read %u permanent programming lock detected.\n", otp); |
| } |
| |
| if ((status & BSEC_OTPSR_PPLMF) != 0U) { |
| ERROR("BSEC read %u error 0x%x\n", otp, status); |
| return BSEC_ERROR; |
| } |
| |
| if ((status & (BSEC_OTPSR_DISTURBF | BSEC_OTPSR_DEDF | BSEC_OTPSR_AMEF)) != 0U) { |
| ERROR("BSEC read %u error 0x%x with invalid FVR\n", otp, status); |
| return BSEC_RETRY; |
| } |
| |
| return BSEC_OK; |
| } |
| |
| static uint32_t check_program_error(uint32_t otp) |
| { |
| uint32_t status = bsec_get_status(); |
| |
| if ((status & BSEC_OTPSR_PROGFAIL) != 0U) { |
| ERROR("BSEC program %u error 0x%x\n", otp, status); |
| return BSEC_RETRY; |
| } |
| |
| return BSEC_OK; |
| } |
| |
| static void check_reset_error(void) |
| { |
| uint32_t status = bsec_get_status(); |
| |
| /* check initial status reporting */ |
| if ((status & BSEC_OTPSR_BUSY) != 0U) { |
| VERBOSE("BSEC reset and busy when OTPSR read\n"); |
| } |
| if ((status & BSEC_OTPSR_HIDEUP) != 0U) { |
| VERBOSE("BSEC upper fuse are not accessible (HIDEUP)\n"); |
| } |
| if ((status & BSEC_OTPSR_OTPSEC) != 0U) { |
| VERBOSE("BSEC reset single error correction detected\n"); |
| } |
| if ((status & BSEC_OTPSR_OTPNVIR) == 0U) { |
| VERBOSE("BSEC reset first fuse word 0 is detected zero\n"); |
| } |
| if ((status & BSEC_OTPSR_OTPERR) != 0U) { |
| ERROR("BSEC reset critical error 0x%x\n", status); |
| panic(); |
| } |
| if ((status & BSEC_OTPSR_FUSEOK) != BSEC_OTPSR_FUSEOK) { |
| ERROR("BSEC reset critical error 0x%x\n", status); |
| panic(); |
| } |
| } |
| |
| static bool is_bsec_write_locked(void) |
| { |
| return (mmio_read_32(BSEC_BASE + BSEC_LOCKR) & BSEC_LOCKR_GWLOCK_MASK) != 0U; |
| } |
| |
| /* |
| * bsec_probe: initialize BSEC driver. |
| * return value: BSEC_OK if no error. |
| */ |
| uint32_t bsec_probe(void) |
| { |
| uint32_t version = bsec_get_version(); |
| uint32_t id = bsec_get_id(); |
| |
| if ((version != BSEC_IP_VERSION_1_0) || (id != BSEC_IP_ID_3)) { |
| ERROR("%s: version = 0x%x, id = 0x%x\n", __func__, version, id); |
| panic(); |
| } |
| |
| check_reset_error(); |
| |
| return BSEC_OK; |
| } |
| |
| /* |
| * bsec_shadow_register: copy SAFMEM OTP to BSEC data. |
| * otp: OTP number. |
| * return value: BSEC_OK if no error. |
| */ |
| static uint32_t bsec_shadow_register(uint32_t otp) |
| { |
| uint32_t result; |
| uint32_t i; |
| bool value; |
| |
| result = bsec_read_sr_lock(otp, &value); |
| if (result != BSEC_OK) { |
| WARN("BSEC: %u Sticky-read bit read Error %u\n", otp, result); |
| } else if (value) { |
| VERBOSE("BSEC: OTP %u is locked and will not be refreshed\n", otp); |
| } |
| |
| for (i = 0U; i < MAX_NB_TRIES; i++) { |
| mmio_write_32(BSEC_BASE + BSEC_OTPCR, otp); |
| |
| poll_otp_status_busy(); |
| |
| result = check_read_error(otp); |
| if (result != BSEC_RETRY) { |
| break; |
| } |
| } |
| |
| return result; |
| } |
| |
| /* |
| * bsec_write_otp: write a value in shadow OTP. |
| * val: value to program. |
| * otp: OTP number. |
| * return value: BSEC_OK if no error. |
| */ |
| uint32_t bsec_write_otp(uint32_t val, uint32_t otp) |
| { |
| bool state; |
| uint32_t result; |
| |
| if (otp > STM32MP2_OTP_MAX_ID) { |
| panic(); |
| } |
| |
| if (!is_fuse_shadowed(otp)) { |
| return BSEC_ERROR; |
| } |
| |
| if (is_bsec_write_locked()) { |
| return BSEC_WRITE_LOCKED; |
| } |
| |
| result = bsec_read_sw_lock(otp, &state); |
| if (result != BSEC_OK) { |
| WARN("Shadow register is SW locked\n"); |
| return result; |
| } |
| |
| mmio_write_32(BSEC_BASE + BSEC_FVR(otp), val); |
| |
| return BSEC_OK; |
| } |
| |
| /* |
| * bsec_program_otp: program a bit in SAFMEM after the prog. |
| * The OTP data is not refreshed. |
| * val: value to program. |
| * otp: OTP number. |
| * return value: BSEC_OK if no error. |
| */ |
| uint32_t bsec_program_otp(uint32_t val, uint32_t otp) |
| { |
| uint32_t result; |
| uint32_t i; |
| bool value; |
| |
| if (otp > STM32MP2_OTP_MAX_ID) { |
| panic(); |
| } |
| |
| if (is_bsec_write_locked() == true) { |
| return BSEC_WRITE_LOCKED; |
| } |
| |
| result = bsec_read_sp_lock(otp, &value); |
| if (result != BSEC_OK) { |
| WARN("BSEC: %u Sticky-prog bit read Error %u\n", otp, result); |
| } else if (value) { |
| WARN("BSEC: OTP locked, prog will be ignored\n"); |
| return BSEC_WRITE_LOCKED; |
| } |
| |
| mmio_write_32(BSEC_BASE + BSEC_WDR, val); |
| |
| for (i = 0U; i < MAX_NB_TRIES; i++) { |
| mmio_write_32(BSEC_BASE + BSEC_OTPCR, otp | BSEC_OTPCR_PROG); |
| |
| poll_otp_status_busy(); |
| |
| result = check_program_error(otp); |
| if (result != BSEC_RETRY) { |
| break; |
| } |
| } |
| |
| return result; |
| } |
| |
| /* |
| * bsec_read_debug_conf: read debug configuration. |
| */ |
| uint32_t bsec_read_debug_conf(void) |
| { |
| return mmio_read_32(BSEC_BASE + BSEC_DENR); |
| } |
| |
| static uint32_t bsec_lock_register_set(uint32_t offset, uint32_t mask) |
| { |
| uint32_t value = mmio_read_32(BSEC_BASE + offset); |
| |
| /* The lock is already set */ |
| if ((value & mask) != 0U) { |
| return BSEC_OK; |
| } |
| |
| if (is_bsec_write_locked()) { |
| return BSEC_WRITE_LOCKED; |
| } |
| |
| value |= mask; |
| |
| mmio_write_32(BSEC_BASE + offset, value); |
| |
| return BSEC_OK; |
| } |
| |
| static bool bsec_lock_register_get(uint32_t offset, uint32_t mask) |
| { |
| uint32_t value = mmio_read_32(BSEC_BASE + offset); |
| |
| return (value & mask) != 0U; |
| } |
| |
| /* |
| * bsec_set_sr_lock: set shadow-read lock. |
| * otp: OTP number. |
| * return value: BSEC_OK if no error. |
| */ |
| uint32_t bsec_set_sr_lock(uint32_t otp) |
| { |
| uint32_t bank = otp_bank(otp); |
| uint32_t otp_mask = otp_bit_mask(otp); |
| |
| if (otp > STM32MP2_OTP_MAX_ID) { |
| panic(); |
| } |
| |
| return bsec_lock_register_set(BSEC_SRLOCK(bank), otp_mask); |
| } |
| |
| /* |
| * bsec_read_sr_lock: read shadow-read lock. |
| * otp: OTP number. |
| * value: read value (true or false). |
| * return value: BSEC_OK if no error. |
| */ |
| uint32_t bsec_read_sr_lock(uint32_t otp, bool *value) |
| { |
| uint32_t bank = otp_bank(otp); |
| uint32_t otp_mask = otp_bit_mask(otp); |
| |
| assert(value != NULL); |
| if (otp > STM32MP2_OTP_MAX_ID) { |
| panic(); |
| } |
| |
| *value = bsec_lock_register_get(BSEC_SRLOCK(bank), otp_mask); |
| |
| return BSEC_OK; |
| } |
| |
| /* |
| * bsec_set_sw_lock: set shadow-write lock. |
| * otp: OTP number. |
| * return value: BSEC_OK if no error. |
| */ |
| uint32_t bsec_set_sw_lock(uint32_t otp) |
| { |
| uint32_t bank = otp_bank(otp); |
| uint32_t otp_mask = otp_bit_mask(otp); |
| |
| if (otp > STM32MP2_OTP_MAX_ID) { |
| panic(); |
| } |
| |
| return bsec_lock_register_set(BSEC_SWLOCK(bank), otp_mask); |
| } |
| |
| /* |
| * bsec_read_sw_lock: read shadow-write lock. |
| * otp: OTP number. |
| * value: read value (true or false). |
| * return value: BSEC_OK if no error. |
| */ |
| uint32_t bsec_read_sw_lock(uint32_t otp, bool *value) |
| { |
| uint32_t bank = otp_bank(otp); |
| uint32_t otp_mask = otp_bit_mask(otp); |
| |
| assert(value != NULL); |
| if (otp > STM32MP2_OTP_MAX_ID) { |
| panic(); |
| } |
| |
| *value = bsec_lock_register_get(BSEC_SWLOCK(bank), otp_mask); |
| |
| return BSEC_OK; |
| } |
| |
| /* |
| * bsec_set_sp_lock: set shadow-program lock. |
| * otp: OTP number. |
| * return value: BSEC_OK if no error. |
| */ |
| uint32_t bsec_set_sp_lock(uint32_t otp) |
| { |
| uint32_t bank = otp_bank(otp); |
| uint32_t otp_mask = otp_bit_mask(otp); |
| |
| if (otp > STM32MP2_OTP_MAX_ID) { |
| panic(); |
| } |
| |
| return bsec_lock_register_set(BSEC_SPLOCK(bank), otp_mask); |
| } |
| |
| /* |
| * bsec_read_sp_lock: read shadow-program lock. |
| * otp: OTP number. |
| * value: read value (true or false). |
| * return value: BSEC_OK if no error. |
| */ |
| uint32_t bsec_read_sp_lock(uint32_t otp, bool *value) |
| { |
| uint32_t bank = otp_bank(otp); |
| uint32_t otp_mask = otp_bit_mask(otp); |
| |
| assert(value != NULL); |
| if (otp > STM32MP2_OTP_MAX_ID) { |
| panic(); |
| } |
| |
| *value = bsec_lock_register_get(BSEC_SPLOCK(bank), otp_mask); |
| |
| return BSEC_OK; |
| } |
| |
| /* |
| * bsec_get_secure_state: read state in BSEC status register. |
| * return: secure state |
| */ |
| uint32_t bsec_get_secure_state(void) |
| { |
| uint32_t state = BSEC_STATE_INVALID; |
| uint32_t status = bsec_get_status(); |
| uint32_t bsec_sr = mmio_read_32(BSEC_BASE + BSEC_SR); |
| |
| if ((status & BSEC_OTPSR_FUSEOK) == BSEC_OTPSR_FUSEOK) { |
| /* NVSTATE is only valid if FUSEOK */ |
| uint32_t nvstates = (bsec_sr & BSEC_SR_NVSTATE_MASK) >> BSEC_SR_NVSTATE_SHIFT; |
| |
| if (nvstates == BSEC_SR_NVSTATE_OPEN) { |
| state = BSEC_STATE_SEC_OPEN; |
| } else if (nvstates == BSEC_SR_NVSTATE_CLOSED) { |
| state = BSEC_STATE_SEC_CLOSED; |
| } else { |
| VERBOSE("%s nvstates = %u\n", __func__, nvstates); |
| } |
| } |
| |
| return state; |
| } |
| |
| /* |
| * bsec_shadow_read_otp: Load OTP from SAFMEM and provide its value |
| * val: read value. |
| * otp: OTP number. |
| * return value: BSEC_OK if no error. |
| */ |
| uint32_t bsec_shadow_read_otp(uint32_t *val, uint32_t otp) |
| { |
| assert(val != NULL); |
| if (otp > STM32MP2_OTP_MAX_ID) { |
| panic(); |
| } |
| |
| *val = 0U; |
| |
| if (is_bsec_write_locked()) { |
| return BSEC_WRITE_LOCKED; |
| } |
| |
| if (!is_fuse_shadowed(otp)) { |
| uint32_t result = bsec_shadow_register(otp); |
| |
| if (result != BSEC_OK) { |
| ERROR("BSEC: %u Shadowing Error %u\n", otp, result); |
| return result; |
| } |
| } |
| |
| *val = mmio_read_32(BSEC_BASE + BSEC_FVR(otp)); |
| |
| return BSEC_OK; |
| } |
| |
| /* |
| * bsec_read_otp: read an OTP data value. |
| * val: read value. |
| * otp: OTP number. |
| * return value: BSEC_OK if no error. |
| */ |
| uint32_t bsec_read_otp(uint32_t *val, uint32_t otp) |
| { |
| assert(val != NULL); |
| if (otp > STM32MP2_OTP_MAX_ID) { |
| panic(); |
| } |
| |
| return bsec_shadow_read_otp(val, otp); |
| } |