arch/xtensa: add common driver for E-Fuse on Espressif devices

Adds a common E-Fuse driver to be used by Espressif Xtensa devices.

Signed-off-by: Filipe Cavalcanti <filipe.cavalcanti@espressif.com>
This commit is contained in:
Filipe Cavalcanti 2025-07-16 12:05:51 -03:00 committed by Tiago Medicci Serrano
parent e0e00f2b29
commit 384f788d18
4 changed files with 499 additions and 0 deletions

View file

@ -41,6 +41,50 @@ config ESPRESSIF_ADC_2
endif # ESPRESSIF_ADC
config ESPRESSIF_EFUSE
bool "EFUSE support"
default n
select EFUSE
---help---
Enable efuse support.
config ESPRESSIF_EFUSE_VIRTUAL
bool "Virtual EFUSE support"
depends on ESPRESSIF_EFUSE
default n
---help---
Enable virtual efuse support to simulate eFuse operations in RAM, changes will be reverted each reboot
config ESPRESSIF_EFUSE_VIRTUAL_KEEP_IN_FLASH
bool "Keep E-Fuses in flash"
depends on ESPRESSIF_EFUSE_VIRTUAL
---help---
In addition to the "Virtual E-Fuses support" option, this option just adds
a feature to keep E-Fuses after reboots in flash memory.
During startup, the E-Fuses are copied from flash or,
in case if flash is empty, from real E-Fuse to RAM and then update flash.
This mode is useful when need to keep changes after reboot (testing secure_boot,
flash_encryption or using MCUBoot + encryption).
if ESPRESSIF_EFUSE_VIRTUAL_KEEP_IN_FLASH
config ESPRESSIF_EFUSE_VIRTUAL_KEEP_IN_FLASH_OFFSET
hex "E-Fuses offset in flash"
depends on ESPRESSIF_EFUSE_VIRTUAL_KEEP_IN_FLASH
default 0x250000
---help---
Offset in flash where the E-Fuses will be stored when using the "E-Fuses size to keep in flash" option.
config ESPRESSIF_EFUSE_VIRTUAL_KEEP_IN_FLASH_SIZE
hex "E-Fuses size to keep in flash"
depends on ESPRESSIF_EFUSE_VIRTUAL_KEEP_IN_FLASH
default 0x2000
---help---
Size of E-Fuse region to keep in flash.
endif # ESPRESSIF_EFUSE_VIRTUAL_KEEP_IN_FLASH
config ESPRESSIF_TEMP
bool "Internal Temperature Sensor"
default n

View file

@ -111,6 +111,11 @@ ifeq ($(CONFIG_ESPRESSIF_ADC),y)
CHIP_CSRCS += esp_adc.c
endif
ifeq ($(CONFIG_ESPRESSIF_EFUSE),y)
CHIP_CSRCS += esp_efuse.c
LDFLAGS += -u esp_efuse_startup_include_func
endif
ifeq ($(CONFIG_ESPRESSIF_WIRELESS),y)
include common$(DELIM)espressif$(DELIM)Wireless.mk
endif

View file

@ -0,0 +1,308 @@
/****************************************************************************
* arch/xtensa/src/common/espressif/esp_efuse.c
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <stdlib.h>
#include <debug.h>
#include <errno.h>
#include <assert.h>
#include <string.h>
#include <sys/param.h>
#include <nuttx/irq.h>
#include <nuttx/efuse/efuse.h>
#include "espressif/esp_efuse.h"
#include "esp_efuse.h"
#include "esp_efuse_utility.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define EFUSE_MAX_BLK_LEN 256 /* Max length of efuse block. */
/****************************************************************************
* Private Types
****************************************************************************/
struct esp_efuse_lowerhalf_s
{
const struct efuse_ops_s *ops; /* Lower half operations */
void *upper; /* Pointer to efuse_upperhalf_s */
};
/****************************************************************************
* Private Functions Prototypes
****************************************************************************/
/* "Lower half" driver methods */
static int esp_efuse_lowerhalf_read(struct efuse_lowerhalf_s *lower,
const efuse_desc_t *field[],
uint8_t *data, size_t bits_len);
static int esp_efuse_lowerhalf_write(struct efuse_lowerhalf_s *lower,
const efuse_desc_t *field[],
const uint8_t *data,
size_t bits_len);
static int esp_efuse_lowerhalf_ioctl(struct efuse_lowerhalf_s *lower,
int cmd, unsigned long arg);
/****************************************************************************
* Private Data
****************************************************************************/
/* "Lower half" driver methods */
static const struct efuse_ops_s g_esp_efuse_ops =
{
.read_field = esp_efuse_lowerhalf_read,
.write_field = esp_efuse_lowerhalf_write,
.ioctl = esp_efuse_lowerhalf_ioctl,
};
/* EFUSE lower-half */
static struct esp_efuse_lowerhalf_s g_esp_efuse_lowerhalf =
{
.ops = &g_esp_efuse_ops,
.upper = NULL,
};
/****************************************************************************
* Private functions
****************************************************************************/
/****************************************************************************
* Name: esp_efuse_lowerhalf_read
*
* Description:
* Read value from EFUSE, writing it into an array.
* The field[0]->bit_offset received from the upper half represents
* the bit offset taking into consideration that each block is 256 bits.
* This is necessary as we have multiple blocks of 256 bits.
*
* Example: To read data from USER_DATA (EFUSE_BLK3), from bit 16 onwards,
* then bit_offset should be 3*256 + 16.
*
* Input Parameters:
* lower - A pointer the publicly visible representation of
* the "lower-half" driver state structure
* field - A pointer to describing the fields of efuse
* data - A pointer to array that contains the data for reading
* bits_len - The number of bits required to read
*
* Returned Value:
* Zero (OK) on success; a negated errno value on failure.
*
****************************************************************************/
static int esp_efuse_lowerhalf_read(struct efuse_lowerhalf_s *lower,
const efuse_desc_t *field[],
uint8_t *data, size_t bits_len)
{
int ret = OK;
uint8_t blk_num = field[0]->bit_offset / EFUSE_MAX_BLK_LEN;
esp_efuse_desc_t recv =
{
.efuse_block = blk_num,
.bit_start = field[0]->bit_offset - blk_num * EFUSE_MAX_BLK_LEN,
.bit_count = field[0]->bit_count
};
const esp_efuse_desc_t *desc[] =
{
&recv,
NULL
};
minfo("read from blk_num: %d, bit_start: %d, bit_count: %d\n",
blk_num, recv.bit_start, recv.bit_count);
/* Read the requested field */
ret = esp_efuse_read_field_blob((const esp_efuse_desc_t**)&desc,
data,
bits_len);
return ret;
}
/****************************************************************************
* Name: esp_efuse_lowerhalf_write
*
* Description:
* Write array to EFUSE.
*
* The field[0]->bit_offset received from the upper half represents
* the bit offset taking into consideration that each block is 256 bits.
* This is necessary as we have multiple blocks of 256 bits.
*
* Example: To write data to USER_DATA (EFUSE_BLK3), from bit 16 onwards,
* then bit_offset should be 3*256 + 16.
*
* Input Parameters:
* lower - A pointer the publicly visible representation of
* the "lower-half" driver state structure
* field - A pointer to describing the fields of efuse
* data - A pointer to array that contains the data for writing
* bits_len - The number of bits required to write
*
* Returned Value:
* Zero (OK) on success; a negated errno value on failure.
*
****************************************************************************/
static int esp_efuse_lowerhalf_write(struct efuse_lowerhalf_s *lower,
const efuse_desc_t *field[],
const uint8_t *data,
size_t bits_len)
{
irqstate_t flags;
int ret = OK;
uint8_t blk_num = field[0]->bit_offset / EFUSE_MAX_BLK_LEN;
esp_efuse_desc_t recv =
{
.efuse_block = blk_num,
.bit_start = field[0]->bit_offset - blk_num * EFUSE_MAX_BLK_LEN,
.bit_count = field[0]->bit_count
};
const esp_efuse_desc_t *desc[] =
{
&recv,
NULL
};
minfo("write to blk_num: %d, bit_start: %d, bit_count: %d\n",
blk_num, recv.bit_start, recv.bit_count);
flags = enter_critical_section();
ret = esp_efuse_write_field_blob((const esp_efuse_desc_t**)&desc,
data,
bits_len);
leave_critical_section(flags);
if (ret != OK)
{
return ERROR;
}
return ret;
}
/****************************************************************************
* Name: esp_efuse_lowerhalf_ioctl
*
* Description:
* Any ioctl commands that are not recognized by the "upper-half"
* driver are forwarded to the lower half driver through this method.
*
* Input Parameters:
* lower - A pointer the publicly visible representation of the
* "lower-half" driver state structure.
* cmd - The ioctl command value
* arg - The optional argument that accompanies the ioctl command.
*
* Returned Value:
* Zero (OK) on success; a negated errno value on failure.
*
****************************************************************************/
static int esp_efuse_lowerhalf_ioctl(struct efuse_lowerhalf_s *lower,
int cmd, unsigned long arg)
{
int ret = OK;
switch (cmd)
{
/* We don't have proprietary EFUSE ioctls */
default:
{
minfo("Unrecognized cmd: %d\n", cmd);
ret = -ENOTTY;
}
break;
}
return ret;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: esp_efuse_initialize
*
* Description:
* Initialize the efuse driver. The efuse is initialized
* and registered as 'devpath'
*
* Input Parameters:
* devpath - The full path to the efuse device.
* This should be of the form /dev/efuse
*
* Returned Value:
* Zero (OK) on success; a negated errno value on failure.
*
****************************************************************************/
int esp_efuse_initialize(const char *devpath)
{
struct esp_efuse_lowerhalf_s *lower = NULL;
int ret = OK;
DEBUGASSERT(devpath != NULL);
lower = &g_esp_efuse_lowerhalf;
/* Register the efuse upper driver */
lower->upper = efuse_register(devpath,
(struct efuse_lowerhalf_s *)lower);
if (lower->upper == NULL)
{
/* The actual cause of the failure may have been a failure to allocate
* perhaps a failure to register the efuse driver (such as if the
* 'devpath' were not unique). We know here but we return EEXIST to
* indicate the failure (implying the non-unique devpath).
*/
ret = -EEXIST;
}
#ifdef CONFIG_ESPRESSIF_EFUSE_VIRTUAL
mwarn("Virtual E-Fuses are enabled\n");
esp_efuse_utility_update_virt_blocks();
#endif
return ret;
}

View file

@ -0,0 +1,142 @@
/****************************************************************************
* arch/xtensa/src/common/espressif/esp_efuse.h
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
#ifndef __ARCH_XTENSA_SRC_COMMON_ESPRESSIF_ESP_EFUSE_H
#define __ARCH_XTENSA_SRC_COMMON_ESPRESSIF_ESP_EFUSE_H
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/efuse/efuse.h>
#ifndef __ASSEMBLY__
#undef EXTERN
#if defined(__cplusplus)
#define EXTERN extern "C"
extern "C"
{
#else
#define EXTERN extern
#endif
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define ESP_EFUSE_BLK_LEN 256
/****************************************************************************
* Public Types
****************************************************************************/
/* E-Fuse block and bit definitions can be found on the Technical Reference
* Manual or on the ESP-IDF documentation.
*/
#ifdef CONFIG_ARCH_CHIP_ESP32
typedef enum
{
ESP_EFUSE_BLK0 = 0,
ESP_EFUSE_BLK1 = 1,
ESP_EFUSE_BLK_KEY0 = 1,
ESP_EFUSE_BLK_ENCRYPT_FLASH = 1,
ESP_EFUSE_BLK2 = 2,
ESP_EFUSE_BLK_KEY1 = 2,
ESP_EFUSE_BLK_SECURE_BOOT = 2,
ESP_EFUSE_BLK3 = 3,
ESP_EFUSE_BLK_KEY2 = 3,
ESP_EFUSE_BLK_KEY_MAX = 4,
ESP_EFUSE_BLK_MAX = 4,
} esp_efuse_blk_num_t;
#else
typedef enum
{
ESP_EFUSE_BLK0 = 0,
ESP_EFUSE_BLK1 = 1,
ESP_EFUSE_BLK2 = 2,
ESP_EFUSE_BLK_SYS_DATA_PART1 = 2,
ESP_EFUSE_BLK3 = 3,
ESP_EFUSE_BLK_USER_DATA = 3,
ESP_EFUSE_BLK4 = 4,
ESP_EFUSE_BLK_KEY0 = 4,
ESP_EFUSE_BLK5 = 5,
ESP_EFUSE_BLK_KEY1 = 5,
ESP_EFUSE_BLK6 = 6,
ESP_EFUSE_BLK_KEY2 = 6,
ESP_EFUSE_BLK7 = 7,
ESP_EFUSE_BLK_KEY3 = 7,
ESP_EFUSE_BLK8 = 8,
ESP_EFUSE_BLK_KEY4 = 8,
ESP_EFUSE_BLK9 = 9,
ESP_EFUSE_BLK_KEY5 = 9,
ESP_EFUSE_BLK_KEY_MAX = 10,
ESP_EFUSE_BLK10 = 10,
ESP_EFUSE_BLK_SYS_DATA_PART2 = 10,
ESP_EFUSE_BLK_MAX
} esp_efuse_blk_num_t;
#endif
/****************************************************************************
* Public Functions Prototypes
****************************************************************************/
/****************************************************************************
* Name: esp_efuse_initialize
*
* Description:
* Initialize the efuse driver. The efuse is initialized
* and registered as 'devpath'
*
* Input Parameters:
* devpath - The full path to the efuse device.
* This should be of the form /dev/efuse
*
* Returned Value:
* Zero (OK) on success; a negated errno value on failure.
*
****************************************************************************/
int esp_efuse_initialize(const char *devpath);
#undef EXTERN
#if defined(__cplusplus)
}
#endif
#endif /* __ASSEMBLY__ */
#endif /* __ARCH_XTENSA_SRC_COMMON_ESPRESSIF_ESP_EFUSE_H */