stm32mp1: add BSEC driver
The BSEC (Boot and Security and OTP control) is intended to control an OTP (one time programmable) fuse box, used for on-chip non-volatile storage for device configuration and security parameters. Change-Id: I38c44684c7b9c6a1f24ec0ae3fe99cec481d5a51 Signed-off-by: Yann Gautier <yann.gautier@st.com> Signed-off-by: Etienne Carriere <etienne.carriere@st.com> Signed-off-by: Lionel Debieve <lionel.debieve@st.com> Signed-off-by: Mathieu Belou <mathieu.belou@st.com> Signed-off-by: Nicolas Le Bayon <nicolas.le.bayon@st.com>
This commit is contained in:
parent
a42bad3763
commit
88ef0425da
|
@ -0,0 +1,913 @@
|
|||
/*
|
||||
* Copyright (c) 2017-2019, STMicroelectronics - All Rights Reserved
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include <libfdt.h>
|
||||
|
||||
#include <platform_def.h>
|
||||
|
||||
#include <arch_helpers.h>
|
||||
#include <common/debug.h>
|
||||
#include <drivers/st/bsec.h>
|
||||
#include <lib/mmio.h>
|
||||
#include <lib/spinlock.h>
|
||||
|
||||
#define BSEC_IP_VERSION_1_0 0x10
|
||||
#define BSEC_COMPAT "st,stm32mp15-bsec"
|
||||
|
||||
#define OTP_ACCESS_SIZE (round_up(OTP_MAX_SIZE, __WORD_BIT) / __WORD_BIT)
|
||||
|
||||
static uint32_t otp_nsec_access[OTP_ACCESS_SIZE] __unused;
|
||||
|
||||
static uint32_t bsec_power_safmem(bool power);
|
||||
|
||||
/* BSEC access protection */
|
||||
static spinlock_t bsec_spinlock;
|
||||
static uintptr_t bsec_base;
|
||||
|
||||
static void bsec_lock(void)
|
||||
{
|
||||
const uint32_t mask = SCTLR_M_BIT | SCTLR_C_BIT;
|
||||
|
||||
/* Lock is currently required only when MMU and cache are enabled */
|
||||
if ((read_sctlr() & mask) == mask) {
|
||||
spin_lock(&bsec_spinlock);
|
||||
}
|
||||
}
|
||||
|
||||
static void bsec_unlock(void)
|
||||
{
|
||||
const uint32_t mask = SCTLR_M_BIT | SCTLR_C_BIT;
|
||||
|
||||
/* Unlock is required only when MMU and cache are enabled */
|
||||
if ((read_sctlr() & mask) == mask) {
|
||||
spin_unlock(&bsec_spinlock);
|
||||
}
|
||||
}
|
||||
|
||||
static int bsec_get_dt_node(struct dt_node_info *info)
|
||||
{
|
||||
int node;
|
||||
|
||||
node = dt_get_node(info, -1, BSEC_COMPAT);
|
||||
if (node < 0) {
|
||||
return -FDT_ERR_NOTFOUND;
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
#if defined(IMAGE_BL32)
|
||||
static void enable_non_secure_access(uint32_t otp)
|
||||
{
|
||||
otp_nsec_access[otp / __WORD_BIT] |= BIT(otp % __WORD_BIT);
|
||||
|
||||
if (bsec_shadow_register(otp) != BSEC_OK) {
|
||||
panic();
|
||||
}
|
||||
}
|
||||
|
||||
static bool non_secure_can_access(uint32_t otp)
|
||||
{
|
||||
return (otp_nsec_access[otp / __WORD_BIT] &
|
||||
BIT(otp % __WORD_BIT)) != 0;
|
||||
}
|
||||
|
||||
static int bsec_dt_otp_nsec_access(void *fdt, int bsec_node)
|
||||
{
|
||||
int bsec_subnode;
|
||||
|
||||
fdt_for_each_subnode(bsec_subnode, fdt, bsec_node) {
|
||||
const fdt32_t *cuint;
|
||||
uint32_t reg;
|
||||
uint32_t i;
|
||||
uint32_t size;
|
||||
uint8_t status;
|
||||
|
||||
cuint = fdt_getprop(fdt, bsec_subnode, "reg", NULL);
|
||||
if (cuint == NULL) {
|
||||
panic();
|
||||
}
|
||||
|
||||
reg = fdt32_to_cpu(*cuint) / sizeof(uint32_t);
|
||||
if (reg < STM32MP1_UPPER_OTP_START) {
|
||||
continue;
|
||||
}
|
||||
|
||||
status = fdt_get_status(bsec_subnode);
|
||||
if ((status & DT_NON_SECURE) == 0U) {
|
||||
continue;
|
||||
}
|
||||
|
||||
size = fdt32_to_cpu(*(cuint + 1)) / sizeof(uint32_t);
|
||||
|
||||
if ((fdt32_to_cpu(*(cuint + 1)) % sizeof(uint32_t)) != 0) {
|
||||
size++;
|
||||
}
|
||||
|
||||
for (i = reg; i < (reg + size); i++) {
|
||||
enable_non_secure_access(i);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static uint32_t otp_bank_offset(uint32_t otp)
|
||||
{
|
||||
assert(otp <= STM32MP1_OTP_MAX_ID);
|
||||
|
||||
return ((otp & ~BSEC_OTP_MASK) >> BSEC_OTP_BANK_SHIFT) *
|
||||
sizeof(uint32_t);
|
||||
}
|
||||
|
||||
static uint32_t bsec_check_error(uint32_t otp)
|
||||
{
|
||||
uint32_t bit = BIT(otp & BSEC_OTP_MASK);
|
||||
uint32_t bank = otp_bank_offset(otp);
|
||||
|
||||
if ((mmio_read_32(bsec_base + BSEC_DISTURBED_OFF + bank) & bit) != 0U) {
|
||||
return BSEC_DISTURBED;
|
||||
}
|
||||
|
||||
if ((mmio_read_32(bsec_base + BSEC_ERROR_OFF + bank) & bit) != 0U) {
|
||||
return BSEC_ERROR;
|
||||
}
|
||||
|
||||
return BSEC_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* bsec_probe: initialize BSEC driver.
|
||||
* return value: BSEC_OK if no error.
|
||||
*/
|
||||
uint32_t bsec_probe(void)
|
||||
{
|
||||
void *fdt;
|
||||
int node;
|
||||
struct dt_node_info bsec_info;
|
||||
|
||||
if (fdt_get_address(&fdt) == 0) {
|
||||
panic();
|
||||
}
|
||||
|
||||
node = bsec_get_dt_node(&bsec_info);
|
||||
if (node < 0) {
|
||||
panic();
|
||||
}
|
||||
|
||||
bsec_base = bsec_info.base;
|
||||
|
||||
#if defined(IMAGE_BL32)
|
||||
bsec_dt_otp_nsec_access(fdt, node);
|
||||
#endif
|
||||
return BSEC_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* bsec_get_base: return BSEC base address.
|
||||
*/
|
||||
uint32_t bsec_get_base(void)
|
||||
{
|
||||
return bsec_base;
|
||||
}
|
||||
|
||||
/*
|
||||
* bsec_set_config: enable and configure BSEC.
|
||||
* cfg: pointer to param structure used to set register.
|
||||
* return value: BSEC_OK if no error.
|
||||
*/
|
||||
uint32_t bsec_set_config(struct bsec_config *cfg)
|
||||
{
|
||||
uint32_t value;
|
||||
int32_t result;
|
||||
|
||||
value = ((((uint32_t)cfg->freq << BSEC_CONF_FRQ_SHIFT) &
|
||||
BSEC_CONF_FRQ_MASK) |
|
||||
(((uint32_t)cfg->pulse_width << BSEC_CONF_PRG_WIDTH_SHIFT) &
|
||||
BSEC_CONF_PRG_WIDTH_MASK) |
|
||||
(((uint32_t)cfg->tread << BSEC_CONF_TREAD_SHIFT) &
|
||||
BSEC_CONF_TREAD_MASK));
|
||||
|
||||
bsec_lock();
|
||||
|
||||
mmio_write_32(bsec_base + BSEC_OTP_CONF_OFF, value);
|
||||
|
||||
bsec_unlock();
|
||||
|
||||
result = bsec_power_safmem((bool)cfg->power &
|
||||
BSEC_CONF_POWER_UP_MASK);
|
||||
if (result != BSEC_OK) {
|
||||
return result;
|
||||
}
|
||||
|
||||
value = ((((uint32_t)cfg->upper_otp_lock << UPPER_OTP_LOCK_SHIFT) &
|
||||
UPPER_OTP_LOCK_MASK) |
|
||||
(((uint32_t)cfg->den_lock << DENREG_LOCK_SHIFT) &
|
||||
DENREG_LOCK_MASK) |
|
||||
(((uint32_t)cfg->prog_lock << GPLOCK_LOCK_SHIFT) &
|
||||
GPLOCK_LOCK_MASK));
|
||||
|
||||
bsec_lock();
|
||||
|
||||
mmio_write_32(bsec_base + BSEC_OTP_LOCK_OFF, value);
|
||||
|
||||
bsec_unlock();
|
||||
|
||||
return BSEC_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* bsec_get_config: return config parameters set in BSEC registers.
|
||||
* cfg: config param return.
|
||||
* return value: BSEC_OK if no error.
|
||||
*/
|
||||
uint32_t bsec_get_config(struct bsec_config *cfg)
|
||||
{
|
||||
uint32_t value;
|
||||
|
||||
if (cfg == NULL) {
|
||||
return BSEC_INVALID_PARAM;
|
||||
}
|
||||
|
||||
value = mmio_read_32(bsec_base + BSEC_OTP_CONF_OFF);
|
||||
cfg->power = (uint8_t)((value & BSEC_CONF_POWER_UP_MASK) >>
|
||||
BSEC_CONF_POWER_UP_SHIFT);
|
||||
cfg->freq = (uint8_t)((value & BSEC_CONF_FRQ_MASK) >>
|
||||
BSEC_CONF_FRQ_SHIFT);
|
||||
cfg->pulse_width = (uint8_t)((value & BSEC_CONF_PRG_WIDTH_MASK) >>
|
||||
BSEC_CONF_PRG_WIDTH_SHIFT);
|
||||
cfg->tread = (uint8_t)((value & BSEC_CONF_TREAD_MASK) >>
|
||||
BSEC_CONF_TREAD_SHIFT);
|
||||
|
||||
value = mmio_read_32(bsec_base + BSEC_OTP_LOCK_OFF);
|
||||
cfg->upper_otp_lock = (uint8_t)((value & UPPER_OTP_LOCK_MASK) >>
|
||||
UPPER_OTP_LOCK_SHIFT);
|
||||
cfg->den_lock = (uint8_t)((value & DENREG_LOCK_MASK) >>
|
||||
DENREG_LOCK_SHIFT);
|
||||
cfg->prog_lock = (uint8_t)((value & GPLOCK_LOCK_MASK) >>
|
||||
GPLOCK_LOCK_SHIFT);
|
||||
|
||||
return BSEC_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* bsec_shadow_register: copy SAFMEM OTP to BSEC data.
|
||||
* otp: OTP number.
|
||||
* return value: BSEC_OK if no error.
|
||||
*/
|
||||
uint32_t bsec_shadow_register(uint32_t otp)
|
||||
{
|
||||
uint32_t result;
|
||||
bool power_up = false;
|
||||
|
||||
if (otp > STM32MP1_OTP_MAX_ID) {
|
||||
return BSEC_INVALID_PARAM;
|
||||
}
|
||||
|
||||
/* Check if shadowing of OTP is locked */
|
||||
if (bsec_read_sr_lock(otp)) {
|
||||
VERBOSE("BSEC: OTP %i is locked and will not be refreshed\n",
|
||||
otp);
|
||||
}
|
||||
|
||||
if ((bsec_get_status() & BSEC_MODE_PWR_MASK) == 0U) {
|
||||
result = bsec_power_safmem(true);
|
||||
|
||||
if (result != BSEC_OK) {
|
||||
return result;
|
||||
}
|
||||
|
||||
power_up = true;
|
||||
}
|
||||
|
||||
bsec_lock();
|
||||
|
||||
/* Set BSEC_OTP_CTRL_OFF and set ADDR with the OTP value */
|
||||
mmio_write_32(bsec_base + BSEC_OTP_CTRL_OFF, otp | BSEC_READ);
|
||||
|
||||
while ((bsec_get_status() & BSEC_MODE_BUSY_MASK) != 0U) {
|
||||
;
|
||||
}
|
||||
|
||||
result = bsec_check_error(otp);
|
||||
|
||||
bsec_unlock();
|
||||
|
||||
if (power_up) {
|
||||
if (bsec_power_safmem(false) != BSEC_OK) {
|
||||
panic();
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
uint32_t result;
|
||||
|
||||
if (otp > STM32MP1_OTP_MAX_ID) {
|
||||
return BSEC_INVALID_PARAM;
|
||||
}
|
||||
|
||||
bsec_lock();
|
||||
|
||||
*val = mmio_read_32(bsec_base + BSEC_OTP_DATA_OFF +
|
||||
(otp * sizeof(uint32_t)));
|
||||
|
||||
result = bsec_check_error(otp);
|
||||
|
||||
bsec_unlock();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* bsec_write_otp: write value in BSEC data register.
|
||||
* val: value to write.
|
||||
* otp: OTP number.
|
||||
* return value: BSEC_OK if no error.
|
||||
*/
|
||||
uint32_t bsec_write_otp(uint32_t val, uint32_t otp)
|
||||
{
|
||||
uint32_t result;
|
||||
|
||||
if (otp > STM32MP1_OTP_MAX_ID) {
|
||||
return BSEC_INVALID_PARAM;
|
||||
}
|
||||
|
||||
/* Check if programming of OTP is locked */
|
||||
if (bsec_read_sw_lock(otp)) {
|
||||
VERBOSE("BSEC: OTP %i is locked and write will be ignored\n",
|
||||
otp);
|
||||
}
|
||||
|
||||
bsec_lock();
|
||||
|
||||
mmio_write_32(bsec_base + BSEC_OTP_DATA_OFF +
|
||||
(otp * sizeof(uint32_t)), val);
|
||||
|
||||
result = bsec_check_error(otp);
|
||||
|
||||
bsec_unlock();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
bool power_up = false;
|
||||
|
||||
if (otp > STM32MP1_OTP_MAX_ID) {
|
||||
return BSEC_INVALID_PARAM;
|
||||
}
|
||||
|
||||
/* Check if programming of OTP is locked */
|
||||
if (bsec_read_sp_lock(otp)) {
|
||||
WARN("BSEC: OTP locked, prog will be ignored\n");
|
||||
}
|
||||
|
||||
if ((mmio_read_32(bsec_base + BSEC_OTP_LOCK_OFF) &
|
||||
BIT(BSEC_LOCK_PROGRAM)) != 0U) {
|
||||
WARN("BSEC: GPLOCK activated, prog will be ignored\n");
|
||||
}
|
||||
|
||||
if ((bsec_get_status() & BSEC_MODE_PWR_MASK) == 0U) {
|
||||
result = bsec_power_safmem(true);
|
||||
|
||||
if (result != BSEC_OK) {
|
||||
return result;
|
||||
}
|
||||
|
||||
power_up = true;
|
||||
}
|
||||
|
||||
bsec_lock();
|
||||
|
||||
/* Set value in write register */
|
||||
mmio_write_32(bsec_base + BSEC_OTP_WRDATA_OFF, val);
|
||||
|
||||
/* Set BSEC_OTP_CTRL_OFF and set ADDR with the OTP value */
|
||||
mmio_write_32(bsec_base + BSEC_OTP_CTRL_OFF, otp | BSEC_WRITE);
|
||||
|
||||
while ((bsec_get_status() & BSEC_MODE_BUSY_MASK) != 0U) {
|
||||
;
|
||||
}
|
||||
|
||||
if ((bsec_get_status() & BSEC_MODE_PROGFAIL_MASK) != 0U) {
|
||||
result = BSEC_PROG_FAIL;
|
||||
} else {
|
||||
result = bsec_check_error(otp);
|
||||
}
|
||||
|
||||
bsec_unlock();
|
||||
|
||||
if (power_up) {
|
||||
if (bsec_power_safmem(false) != BSEC_OK) {
|
||||
panic();
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* bsec_permanent_lock_otp: permanent lock of OTP in SAFMEM.
|
||||
* otp: OTP number.
|
||||
* return value: BSEC_OK if no error.
|
||||
*/
|
||||
uint32_t bsec_permanent_lock_otp(uint32_t otp)
|
||||
{
|
||||
uint32_t result;
|
||||
bool power_up = false;
|
||||
uint32_t data;
|
||||
uint32_t addr;
|
||||
|
||||
if (otp > STM32MP1_OTP_MAX_ID) {
|
||||
return BSEC_INVALID_PARAM;
|
||||
}
|
||||
|
||||
if ((bsec_get_status() & BSEC_MODE_PWR_MASK) == 0U) {
|
||||
result = bsec_power_safmem(true);
|
||||
|
||||
if (result != BSEC_OK) {
|
||||
return result;
|
||||
}
|
||||
|
||||
power_up = true;
|
||||
}
|
||||
|
||||
if (otp < STM32MP1_UPPER_OTP_START) {
|
||||
addr = otp >> ADDR_LOWER_OTP_PERLOCK_SHIFT;
|
||||
data = DATA_LOWER_OTP_PERLOCK_BIT <<
|
||||
((otp & DATA_LOWER_OTP_PERLOCK_MASK) << 1U);
|
||||
} else {
|
||||
addr = (otp >> ADDR_UPPER_OTP_PERLOCK_SHIFT) + 2U;
|
||||
data = DATA_UPPER_OTP_PERLOCK_BIT <<
|
||||
(otp & DATA_UPPER_OTP_PERLOCK_MASK);
|
||||
}
|
||||
|
||||
bsec_lock();
|
||||
|
||||
/* Set value in write register */
|
||||
mmio_write_32(bsec_base + BSEC_OTP_WRDATA_OFF, data);
|
||||
|
||||
/* Set BSEC_OTP_CTRL_OFF and set ADDR with the OTP value */
|
||||
mmio_write_32(bsec_base + BSEC_OTP_CTRL_OFF,
|
||||
addr | BSEC_WRITE | BSEC_LOCK);
|
||||
|
||||
while ((bsec_get_status() & BSEC_MODE_BUSY_MASK) != 0U) {
|
||||
;
|
||||
}
|
||||
|
||||
if ((bsec_get_status() & BSEC_MODE_PROGFAIL_MASK) != 0U) {
|
||||
result = BSEC_PROG_FAIL;
|
||||
} else {
|
||||
result = bsec_check_error(otp);
|
||||
}
|
||||
|
||||
bsec_unlock();
|
||||
|
||||
if (power_up) {
|
||||
if (bsec_power_safmem(false) != BSEC_OK) {
|
||||
panic();
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* bsec_write_debug_conf: write value in debug feature
|
||||
* to enable/disable debug service.
|
||||
* val: value to write.
|
||||
* return value: BSEC_OK if no error.
|
||||
*/
|
||||
uint32_t bsec_write_debug_conf(uint32_t val)
|
||||
{
|
||||
uint32_t result = BSEC_ERROR;
|
||||
uint32_t masked_val = val & BSEC_DEN_ALL_MSK;
|
||||
|
||||
bsec_lock();
|
||||
|
||||
mmio_write_32(bsec_base + BSEC_DEN_OFF, masked_val);
|
||||
|
||||
if ((mmio_read_32(bsec_base + BSEC_DEN_OFF) ^ masked_val) == 0U) {
|
||||
result = BSEC_OK;
|
||||
}
|
||||
|
||||
bsec_unlock();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* bsec_read_debug_conf: read debug configuration.
|
||||
*/
|
||||
uint32_t bsec_read_debug_conf(void)
|
||||
{
|
||||
return mmio_read_32(bsec_base + BSEC_DEN_OFF);
|
||||
}
|
||||
|
||||
/*
|
||||
* bsec_get_status: return status register value.
|
||||
*/
|
||||
uint32_t bsec_get_status(void)
|
||||
{
|
||||
return mmio_read_32(bsec_base + BSEC_OTP_STATUS_OFF);
|
||||
}
|
||||
|
||||
/*
|
||||
* bsec_get_hw_conf: return hardware configuration.
|
||||
*/
|
||||
uint32_t bsec_get_hw_conf(void)
|
||||
{
|
||||
return mmio_read_32(bsec_base + BSEC_IPHW_CFG_OFF);
|
||||
}
|
||||
|
||||
/*
|
||||
* bsec_get_version: return BSEC version.
|
||||
*/
|
||||
uint32_t bsec_get_version(void)
|
||||
{
|
||||
return mmio_read_32(bsec_base + BSEC_IPVR_OFF);
|
||||
}
|
||||
|
||||
/*
|
||||
* bsec_get_id: return BSEC ID.
|
||||
*/
|
||||
uint32_t bsec_get_id(void)
|
||||
{
|
||||
return mmio_read_32(bsec_base + BSEC_IP_ID_OFF);
|
||||
}
|
||||
|
||||
/*
|
||||
* bsec_get_magic_id: return BSEC magic number.
|
||||
*/
|
||||
uint32_t bsec_get_magic_id(void)
|
||||
{
|
||||
return mmio_read_32(bsec_base + BSEC_IP_MAGIC_ID_OFF);
|
||||
}
|
||||
|
||||
/*
|
||||
* bsec_write_sr_lock: write shadow-read lock.
|
||||
* otp: OTP number.
|
||||
* value: value to write in the register.
|
||||
* Must be always 1.
|
||||
* return: true if OTP is locked, else false.
|
||||
*/
|
||||
bool bsec_write_sr_lock(uint32_t otp, uint32_t value)
|
||||
{
|
||||
bool result = false;
|
||||
uint32_t bank = otp_bank_offset(otp);
|
||||
uint32_t bank_value;
|
||||
uint32_t otp_mask = BIT(otp & BSEC_OTP_MASK);
|
||||
|
||||
bsec_lock();
|
||||
|
||||
bank_value = mmio_read_32(bsec_base + BSEC_SRLOCK_OFF + bank);
|
||||
|
||||
if ((bank_value & otp_mask) == value) {
|
||||
/*
|
||||
* In case of write don't need to write,
|
||||
* the lock is already set.
|
||||
*/
|
||||
if (value != 0U) {
|
||||
result = true;
|
||||
}
|
||||
} else {
|
||||
if (value != 0U) {
|
||||
bank_value = bank_value | otp_mask;
|
||||
} else {
|
||||
bank_value = bank_value & ~otp_mask;
|
||||
}
|
||||
|
||||
/*
|
||||
* We can write 0 in all other OTP
|
||||
* if the lock is activated in one of other OTP.
|
||||
* Write 0 has no effect.
|
||||
*/
|
||||
mmio_write_32(bsec_base + BSEC_SRLOCK_OFF + bank, bank_value);
|
||||
result = true;
|
||||
}
|
||||
|
||||
bsec_unlock();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* bsec_read_sr_lock: read shadow-read lock.
|
||||
* otp: OTP number.
|
||||
* return: true if otp is locked, else false.
|
||||
*/
|
||||
bool bsec_read_sr_lock(uint32_t otp)
|
||||
{
|
||||
uint32_t bank = otp_bank_offset(otp);
|
||||
uint32_t otp_mask = BIT(otp & BSEC_OTP_MASK);
|
||||
uint32_t bank_value = mmio_read_32(bsec_base + BSEC_SRLOCK_OFF + bank);
|
||||
|
||||
return (bank_value & otp_mask) != 0U;
|
||||
}
|
||||
|
||||
/*
|
||||
* bsec_write_sw_lock: write shadow-write lock.
|
||||
* otp: OTP number.
|
||||
* value: Value to write in the register.
|
||||
* Must be always 1.
|
||||
* return: true if OTP is locked, else false.
|
||||
*/
|
||||
bool bsec_write_sw_lock(uint32_t otp, uint32_t value)
|
||||
{
|
||||
bool result = false;
|
||||
uint32_t bank = otp_bank_offset(otp);
|
||||
uint32_t otp_mask = BIT(otp & BSEC_OTP_MASK);
|
||||
uint32_t bank_value;
|
||||
|
||||
bsec_lock();
|
||||
|
||||
bank_value = mmio_read_32(bsec_base + BSEC_SWLOCK_OFF + bank);
|
||||
|
||||
if ((bank_value & otp_mask) == value) {
|
||||
/*
|
||||
* In case of write don't need to write,
|
||||
* the lock is already set.
|
||||
*/
|
||||
if (value != 0U) {
|
||||
result = true;
|
||||
}
|
||||
} else {
|
||||
if (value != 0U) {
|
||||
bank_value = bank_value | otp_mask;
|
||||
} else {
|
||||
bank_value = bank_value & ~otp_mask;
|
||||
}
|
||||
|
||||
/*
|
||||
* We can write 0 in all other OTP
|
||||
* if the lock is activated in one of other OTP.
|
||||
* Write 0 has no effect.
|
||||
*/
|
||||
mmio_write_32(bsec_base + BSEC_SWLOCK_OFF + bank, bank_value);
|
||||
result = true;
|
||||
}
|
||||
|
||||
bsec_unlock();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* bsec_read_sw_lock: read shadow-write lock.
|
||||
* otp: OTP number.
|
||||
* return: true if OTP is locked, else false.
|
||||
*/
|
||||
bool bsec_read_sw_lock(uint32_t otp)
|
||||
{
|
||||
uint32_t bank = otp_bank_offset(otp);
|
||||
uint32_t otp_mask = BIT(otp & BSEC_OTP_MASK);
|
||||
uint32_t bank_value = mmio_read_32(bsec_base + BSEC_SWLOCK_OFF + bank);
|
||||
|
||||
return (bank_value & otp_mask) != 0U;
|
||||
}
|
||||
|
||||
/*
|
||||
* bsec_write_sp_lock: write shadow-program lock.
|
||||
* otp: OTP number.
|
||||
* value: Value to write in the register.
|
||||
* Must be always 1.
|
||||
* return: true if OTP is locked, else false.
|
||||
*/
|
||||
bool bsec_write_sp_lock(uint32_t otp, uint32_t value)
|
||||
{
|
||||
bool result = false;
|
||||
uint32_t bank = otp_bank_offset(otp);
|
||||
uint32_t bank_value;
|
||||
uint32_t otp_mask = BIT(otp & BSEC_OTP_MASK);
|
||||
|
||||
bsec_lock();
|
||||
|
||||
bank_value = mmio_read_32(bsec_base + BSEC_SPLOCK_OFF + bank);
|
||||
|
||||
if ((bank_value & otp_mask) == value) {
|
||||
/*
|
||||
* In case of write don't need to write,
|
||||
* the lock is already set.
|
||||
*/
|
||||
if (value != 0U) {
|
||||
result = true;
|
||||
}
|
||||
} else {
|
||||
if (value != 0U) {
|
||||
bank_value = bank_value | otp_mask;
|
||||
} else {
|
||||
bank_value = bank_value & ~otp_mask;
|
||||
}
|
||||
|
||||
/*
|
||||
* We can write 0 in all other OTP
|
||||
* if the lock is activated in one of other OTP.
|
||||
* Write 0 has no effect.
|
||||
*/
|
||||
mmio_write_32(bsec_base + BSEC_SPLOCK_OFF + bank, bank_value);
|
||||
result = true;
|
||||
}
|
||||
|
||||
bsec_unlock();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* bsec_read_sp_lock: read shadow-program lock.
|
||||
* otp: OTP number.
|
||||
* return: true if OTP is locked, else false.
|
||||
*/
|
||||
bool bsec_read_sp_lock(uint32_t otp)
|
||||
{
|
||||
uint32_t bank = otp_bank_offset(otp);
|
||||
uint32_t otp_mask = BIT(otp & BSEC_OTP_MASK);
|
||||
uint32_t bank_value = mmio_read_32(bsec_base + BSEC_SPLOCK_OFF + bank);
|
||||
|
||||
return (bank_value & otp_mask) != 0U;
|
||||
}
|
||||
|
||||
/*
|
||||
* bsec_wr_lock: Read permanent lock status.
|
||||
* otp: OTP number.
|
||||
* return: true if OTP is locked, else false.
|
||||
*/
|
||||
bool bsec_wr_lock(uint32_t otp)
|
||||
{
|
||||
uint32_t bank = otp_bank_offset(otp);
|
||||
uint32_t lock_bit = BIT(otp & BSEC_OTP_MASK);
|
||||
|
||||
if ((mmio_read_32(bsec_base + BSEC_WRLOCK_OFF + bank) &
|
||||
lock_bit) != 0U) {
|
||||
/*
|
||||
* In case of write don't need to write,
|
||||
* the lock is already set.
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* bsec_otp_lock: Lock Upper OTP or Global programming or debug enable
|
||||
* service: Service to lock see header file.
|
||||
* value: Value to write must always set to 1 (only use for debug purpose).
|
||||
* return: BSEC_OK if succeed.
|
||||
*/
|
||||
uint32_t bsec_otp_lock(uint32_t service, uint32_t value)
|
||||
{
|
||||
uintptr_t reg = bsec_base + BSEC_OTP_LOCK_OFF;
|
||||
|
||||
switch (service) {
|
||||
case BSEC_LOCK_UPPER_OTP:
|
||||
mmio_write_32(reg, value << BSEC_LOCK_UPPER_OTP);
|
||||
break;
|
||||
case BSEC_LOCK_DEBUG:
|
||||
mmio_write_32(reg, value << BSEC_LOCK_DEBUG);
|
||||
break;
|
||||
case BSEC_LOCK_PROGRAM:
|
||||
mmio_write_32(reg, value << BSEC_LOCK_PROGRAM);
|
||||
break;
|
||||
default:
|
||||
return BSEC_INVALID_PARAM;
|
||||
}
|
||||
|
||||
return BSEC_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* bsec_power_safmem: Activate or deactivate SAFMEM power.
|
||||
* power: true to power up, false to power down.
|
||||
* return: BSEC_OK if succeed.
|
||||
*/
|
||||
static uint32_t bsec_power_safmem(bool power)
|
||||
{
|
||||
uint32_t register_val;
|
||||
uint32_t timeout = BSEC_TIMEOUT_VALUE;
|
||||
|
||||
bsec_lock();
|
||||
|
||||
register_val = mmio_read_32(bsec_base + BSEC_OTP_CONF_OFF);
|
||||
|
||||
if (power) {
|
||||
register_val |= BSEC_CONF_POWER_UP_MASK;
|
||||
} else {
|
||||
register_val &= ~BSEC_CONF_POWER_UP_MASK;
|
||||
}
|
||||
|
||||
mmio_write_32(bsec_base + BSEC_OTP_CONF_OFF, register_val);
|
||||
|
||||
/* Waiting loop */
|
||||
if (power) {
|
||||
while (((bsec_get_status() & BSEC_MODE_PWR_MASK) == 0U) &&
|
||||
(timeout != 0U)) {
|
||||
timeout--;
|
||||
}
|
||||
} else {
|
||||
while (((bsec_get_status() & BSEC_MODE_PWR_MASK) != 0U) &&
|
||||
(timeout != 0U)) {
|
||||
timeout--;
|
||||
}
|
||||
}
|
||||
|
||||
bsec_unlock();
|
||||
|
||||
if (timeout == 0U) {
|
||||
return BSEC_TIMEOUT;
|
||||
}
|
||||
|
||||
return BSEC_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* bsec_mode_is_closed_device: read OTP secure sub-mode.
|
||||
* return: false if open_device and true of closed_device.
|
||||
*/
|
||||
bool bsec_mode_is_closed_device(void)
|
||||
{
|
||||
uint32_t value;
|
||||
|
||||
if ((bsec_shadow_register(DATA0_OTP) != BSEC_OK) ||
|
||||
(bsec_read_otp(&value, DATA0_OTP) != BSEC_OK)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return (value & DATA0_OTP_SECURED) == DATA0_OTP_SECURED;
|
||||
}
|
||||
|
||||
/*
|
||||
* bsec_shadow_read_otp: Load OTP from SAFMEM and provide its value
|
||||
* otp_value: read value.
|
||||
* word: OTP number.
|
||||
* return value: BSEC_OK if no error.
|
||||
*/
|
||||
uint32_t bsec_shadow_read_otp(uint32_t *otp_value, uint32_t word)
|
||||
{
|
||||
uint32_t result;
|
||||
|
||||
result = bsec_shadow_register(word);
|
||||
if (result != BSEC_OK) {
|
||||
ERROR("BSEC: %u Shadowing Error %i\n", word, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
result = bsec_read_otp(otp_value, word);
|
||||
if (result != BSEC_OK) {
|
||||
ERROR("BSEC: %u Read Error %i\n", word, result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* bsec_check_nsec_access_rights: check non-secure access rights to target OTP.
|
||||
* otp: OTP number.
|
||||
* return: BSEC_OK if authorized access.
|
||||
*/
|
||||
uint32_t bsec_check_nsec_access_rights(uint32_t otp)
|
||||
{
|
||||
#if defined(IMAGE_BL32)
|
||||
if (otp > STM32MP1_OTP_MAX_ID) {
|
||||
return BSEC_INVALID_PARAM;
|
||||
}
|
||||
|
||||
if (otp >= STM32MP1_UPPER_OTP_START) {
|
||||
/* Check if BSEC is in OTP-SECURED closed_device state. */
|
||||
if (bsec_mode_is_closed_device()) {
|
||||
if (!non_secure_can_access(otp)) {
|
||||
return BSEC_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return BSEC_OK;
|
||||
}
|
||||
|
|
@ -0,0 +1,206 @@
|
|||
/*
|
||||
* Copyright (c) 2017-2019, STMicroelectronics - All Rights Reserved
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#ifndef BSEC_H
|
||||
#define BSEC_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <lib/utils_def.h>
|
||||
|
||||
/*
|
||||
* IP configuration
|
||||
*/
|
||||
#define BSEC_OTP_MASK GENMASK(4, 0)
|
||||
#define BSEC_OTP_BANK_SHIFT 5
|
||||
#define BSEC_TIMEOUT_VALUE 0xFFFF
|
||||
|
||||
#define ADDR_LOWER_OTP_PERLOCK_SHIFT 0x03
|
||||
#define DATA_LOWER_OTP_PERLOCK_BIT 0x03U /* 2 significants bits are used */
|
||||
#define DATA_LOWER_OTP_PERLOCK_MASK GENMASK(2, 0)
|
||||
#define ADDR_UPPER_OTP_PERLOCK_SHIFT 0x04
|
||||
#define DATA_UPPER_OTP_PERLOCK_BIT 0x01U /* 1 significants bits are used */
|
||||
#define DATA_UPPER_OTP_PERLOCK_MASK GENMASK(3, 0)
|
||||
|
||||
/*
|
||||
* Return status
|
||||
*/
|
||||
#define BSEC_OK 0U
|
||||
#define BSEC_ERROR 0xFFFFFFFFU
|
||||
#define BSEC_DISTURBED 0xFFFFFFFEU
|
||||
#define BSEC_INVALID_PARAM 0xFFFFFFFCU
|
||||
#define BSEC_PROG_FAIL 0xFFFFFFFBU
|
||||
#define BSEC_LOCK_FAIL 0xFFFFFFFAU
|
||||
#define BSEC_WRITE_FAIL 0xFFFFFFF9U
|
||||
#define BSEC_SHADOW_FAIL 0xFFFFFFF8U
|
||||
#define BSEC_TIMEOUT 0xFFFFFFF7U
|
||||
|
||||
/*
|
||||
* BSEC REGISTER OFFSET (base relative)
|
||||
*/
|
||||
#define BSEC_OTP_CONF_OFF 0x000U
|
||||
#define BSEC_OTP_CTRL_OFF 0x004U
|
||||
#define BSEC_OTP_WRDATA_OFF 0x008U
|
||||
#define BSEC_OTP_STATUS_OFF 0x00CU
|
||||
#define BSEC_OTP_LOCK_OFF 0x010U
|
||||
#define BSEC_DEN_OFF 0x014U
|
||||
#define BSEC_DISTURBED_OFF 0x01CU
|
||||
#define BSEC_DISTURBED1_OFF 0x020U
|
||||
#define BSEC_DISTURBED2_OFF 0x024U
|
||||
#define BSEC_ERROR_OFF 0x034U
|
||||
#define BSEC_ERROR1_OFF 0x038U
|
||||
#define BSEC_ERROR2_OFF 0x03CU
|
||||
#define BSEC_WRLOCK_OFF 0x04CU /* Safmem permanent lock */
|
||||
#define BSEC_WRLOCK1_OFF 0x050U
|
||||
#define BSEC_WRLOCK2_OFF 0x054U
|
||||
#define BSEC_SPLOCK_OFF 0x064U /* Program safmem sticky lock */
|
||||
#define BSEC_SPLOCK1_OFF 0x068U
|
||||
#define BSEC_SPLOCK2_OFF 0x06CU
|
||||
#define BSEC_SWLOCK_OFF 0x07CU /* Write in OTP sticky lock */
|
||||
#define BSEC_SWLOCK1_OFF 0x080U
|
||||
#define BSEC_SWLOCK2_OFF 0x084U
|
||||
#define BSEC_SRLOCK_OFF 0x094U /* Shadowing sticky lock */
|
||||
#define BSEC_SRLOCK1_OFF 0x098U
|
||||
#define BSEC_SRLOCK2_OFF 0x09CU
|
||||
#define BSEC_JTAG_IN_OFF 0x0ACU
|
||||
#define BSEC_JTAG_OUT_OFF 0x0B0U
|
||||
#define BSEC_SCRATCH_OFF 0x0B4U
|
||||
#define BSEC_OTP_DATA_OFF 0x200U
|
||||
#define BSEC_IPHW_CFG_OFF 0xFF0U
|
||||
#define BSEC_IPVR_OFF 0xFF4U
|
||||
#define BSEC_IP_ID_OFF 0xFF8U
|
||||
#define BSEC_IP_MAGIC_ID_OFF 0xFFCU
|
||||
|
||||
/*
|
||||
* BSEC_CONFIGURATION Register
|
||||
*/
|
||||
#define BSEC_CONF_POWER_UP_MASK BIT(0)
|
||||
#define BSEC_CONF_POWER_UP_SHIFT 0
|
||||
#define BSEC_CONF_FRQ_MASK GENMASK(2, 1)
|
||||
#define BSEC_CONF_FRQ_SHIFT 1
|
||||
#define BSEC_CONF_PRG_WIDTH_MASK GENMASK(6, 3)
|
||||
#define BSEC_CONF_PRG_WIDTH_SHIFT 3
|
||||
#define BSEC_CONF_TREAD_MASK GENMASK(8, 7)
|
||||
#define BSEC_CONF_TREAD_SHIFT 7
|
||||
|
||||
/*
|
||||
* BSEC_CONTROL Register
|
||||
*/
|
||||
#define BSEC_READ 0x000U
|
||||
#define BSEC_WRITE 0x100U
|
||||
#define BSEC_LOCK 0x200U
|
||||
|
||||
/*
|
||||
* BSEC_OTP_LOCK register
|
||||
*/
|
||||
#define UPPER_OTP_LOCK_MASK BIT(0)
|
||||
#define UPPER_OTP_LOCK_SHIFT 0
|
||||
#define DENREG_LOCK_MASK BIT(2)
|
||||
#define DENREG_LOCK_SHIFT 2
|
||||
#define GPLOCK_LOCK_MASK BIT(4)
|
||||
#define GPLOCK_LOCK_SHIFT 4
|
||||
|
||||
/*
|
||||
* BSEC_OTP_STATUS Register
|
||||
*/
|
||||
#define BSEC_MODE_STATUS_MASK GENMASK(2, 0)
|
||||
#define BSEC_MODE_BUSY_MASK BIT(3)
|
||||
#define BSEC_MODE_PROGFAIL_MASK BIT(4)
|
||||
#define BSEC_MODE_PWR_MASK BIT(5)
|
||||
#define BSEC_MODE_BIST1_LOCK_MASK BIT(6)
|
||||
#define BSEC_MODE_BIST2_LOCK_MASK BIT(7)
|
||||
|
||||
/* OTP MODE*/
|
||||
#define BSEC_MODE_OPEN1 0x00
|
||||
#define BSEC_MODE_SECURED 0x01
|
||||
#define BSEC_MODE_OPEN2 0x02
|
||||
#define BSEC_MODE_INVALID 0x04
|
||||
|
||||
/* BSEC_DENABLE Register */
|
||||
#define BSEC_HDPEN BIT(4)
|
||||
#define BSEC_SPIDEN BIT(5)
|
||||
#define BSEC_SPINDEN BIT(6)
|
||||
#define BSEC_DBGSWGEN BIT(10)
|
||||
#define BSEC_DEN_ALL_MSK GENMASK(10, 0)
|
||||
|
||||
/* BSEC_FENABLE Register */
|
||||
#define BSEC_FEN_ALL_MSK GENMASK(14, 0)
|
||||
|
||||
/*
|
||||
* OTP Lock services definition
|
||||
* Value must corresponding to the bit number in the register
|
||||
*/
|
||||
#define BSEC_LOCK_UPPER_OTP 0x00
|
||||
#define BSEC_LOCK_DEBUG 0x02
|
||||
#define BSEC_LOCK_PROGRAM 0x03
|
||||
|
||||
/* Values for struct bsec_config::freq */
|
||||
#define FREQ_10_20_MHZ 0x0
|
||||
#define FREQ_20_30_MHZ 0x1
|
||||
#define FREQ_30_45_MHZ 0x2
|
||||
#define FREQ_45_67_MHZ 0x3
|
||||
|
||||
/*
|
||||
* Device info structure, providing device-specific functions and a means of
|
||||
* adding driver-specific state
|
||||
*/
|
||||
struct bsec_config {
|
||||
uint8_t tread; /* SAFMEM Reading current level default 0 */
|
||||
uint8_t pulse_width; /* SAFMEM Programming pulse width default 1 */
|
||||
uint8_t freq; /* SAFMEM CLOCK see freq value define
|
||||
* default FREQ_45_67_MHZ
|
||||
*/
|
||||
uint8_t power; /* Power up SAFMEM. 1 power up, 0 power off */
|
||||
uint8_t prog_lock; /* Programming Sticky lock
|
||||
* 1 programming is locked until next reset
|
||||
*/
|
||||
uint8_t den_lock; /* Debug enable sticky lock
|
||||
* 1 debug enable is locked until next reset
|
||||
*/
|
||||
uint8_t upper_otp_lock; /* Shadowing of upper OTP sticky lock
|
||||
* 1 shadowing of upper OTP is locked
|
||||
* until next reset
|
||||
*/
|
||||
};
|
||||
|
||||
uint32_t bsec_probe(void);
|
||||
uint32_t bsec_get_base(void);
|
||||
|
||||
uint32_t bsec_set_config(struct bsec_config *cfg);
|
||||
uint32_t bsec_get_config(struct bsec_config *cfg);
|
||||
|
||||
uint32_t bsec_shadow_register(uint32_t otp);
|
||||
uint32_t bsec_read_otp(uint32_t *val, uint32_t otp);
|
||||
uint32_t bsec_write_otp(uint32_t val, uint32_t otp);
|
||||
uint32_t bsec_program_otp(uint32_t val, uint32_t otp);
|
||||
uint32_t bsec_permanent_lock_otp(uint32_t otp);
|
||||
|
||||
uint32_t bsec_write_debug_conf(uint32_t val);
|
||||
uint32_t bsec_read_debug_conf(void);
|
||||
uint32_t bsec_write_feature_conf(uint32_t val);
|
||||
uint32_t bsec_read_feature_conf(uint32_t *val);
|
||||
|
||||
uint32_t bsec_get_status(void);
|
||||
uint32_t bsec_get_hw_conf(void);
|
||||
uint32_t bsec_get_version(void);
|
||||
uint32_t bsec_get_id(void);
|
||||
uint32_t bsec_get_magic_id(void);
|
||||
|
||||
bool bsec_write_sr_lock(uint32_t otp, uint32_t value);
|
||||
bool bsec_read_sr_lock(uint32_t otp);
|
||||
bool bsec_write_sw_lock(uint32_t otp, uint32_t value);
|
||||
bool bsec_read_sw_lock(uint32_t otp);
|
||||
bool bsec_write_sp_lock(uint32_t otp, uint32_t value);
|
||||
bool bsec_read_sp_lock(uint32_t otp);
|
||||
bool bsec_wr_lock(uint32_t otp);
|
||||
uint32_t bsec_otp_lock(uint32_t service, uint32_t value);
|
||||
|
||||
bool bsec_mode_is_closed_device(void);
|
||||
uint32_t bsec_shadow_read_otp(uint32_t *otp_value, uint32_t word);
|
||||
uint32_t bsec_check_nsec_access_rights(uint32_t otp);
|
||||
|
||||
#endif /* BSEC_H */
|
|
@ -47,6 +47,7 @@ PLAT_BL_COMMON_SOURCES += ${LIBFDT_SRCS} \
|
|||
drivers/arm/tzc/tzc400.c \
|
||||
drivers/delay_timer/delay_timer.c \
|
||||
drivers/delay_timer/generic_delay_timer.c \
|
||||
drivers/st/bsec/bsec.c \
|
||||
drivers/st/clk/stm32mp1_clk.c \
|
||||
drivers/st/clk/stm32mp1_clkfunc.c \
|
||||
drivers/st/ddr/stm32mp1_ddr_helpers.c \
|
||||
|
|
|
@ -202,6 +202,21 @@ enum ddr_type {
|
|||
#define STM32MP1_EMMC_NORMAL_SPEED_MAX_FREQ 26000000 /*26 MHz*/
|
||||
#define STM32MP1_EMMC_HIGH_SPEED_MAX_FREQ 52000000 /*52 MHz*/
|
||||
|
||||
/*******************************************************************************
|
||||
* STM32MP1 BSEC / OTP
|
||||
******************************************************************************/
|
||||
#define STM32MP1_OTP_MAX_ID 0x5FU
|
||||
#define STM32MP1_UPPER_OTP_START 0x20U
|
||||
|
||||
#define OTP_MAX_SIZE (STM32MP1_OTP_MAX_ID + 1U)
|
||||
|
||||
/* OTP offsets */
|
||||
#define DATA0_OTP U(0)
|
||||
|
||||
/* OTP mask */
|
||||
/* DATA0 */
|
||||
#define DATA0_OTP_SECURED BIT(6)
|
||||
|
||||
/*******************************************************************************
|
||||
* STM32MP1 TAMP
|
||||
******************************************************************************/
|
||||
|
|
Loading…
Reference in New Issue