zephyr/drivers/flash/flash_esp32.c
Sylvio Alves 5902f00bd5 driver: flash: esp32: fix unaligned read
Fix flash read operation to account for all unaligned
scenarios, i.e, address, buffer and length. This is needed
when using flash APIs provided in ROM.

This also removes the unaligned flash write call as it
expects aligned values only.

Signed-off-by: Sylvio Alves <sylvio.alves@espressif.com>
2025-02-24 20:19:47 +00:00

268 lines
6.3 KiB
C

/*
* Copyright (c) 2021 Espressif Systems (Shanghai) Co., Ltd.
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT espressif_esp32_flash_controller
#define SOC_NV_FLASH_NODE DT_INST(0, soc_nv_flash)
#define FLASH_WRITE_BLK_SZ DT_PROP(SOC_NV_FLASH_NODE, write_block_size)
#define FLASH_ERASE_BLK_SZ DT_PROP(SOC_NV_FLASH_NODE, erase_block_size)
/*
* HAL includes go first to
* avoid BIT macro redefinition
*/
#include <esp_flash.h>
#include <spi_flash_mmap.h>
#include <soc/spi_struct.h>
#include <esp_flash_encrypt.h>
#include <esp_flash_internal.h>
#include <bootloader_flash_priv.h>
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <stddef.h>
#include <string.h>
#include <errno.h>
#include <zephyr/drivers/flash.h>
#include <soc.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(flash_esp32, CONFIG_FLASH_LOG_LEVEL);
#define FLASH_SEM_TIMEOUT (k_is_in_isr() ? K_NO_WAIT : K_FOREVER)
struct flash_esp32_dev_config {
spi_dev_t *controller;
};
struct flash_esp32_dev_data {
#ifdef CONFIG_MULTITHREADING
struct k_sem sem;
#endif
};
static const struct flash_parameters flash_esp32_parameters = {
.write_block_size = FLASH_WRITE_BLK_SZ,
.erase_value = 0xff,
};
#ifdef CONFIG_MULTITHREADING
static inline void flash_esp32_sem_take(const struct device *dev)
{
struct flash_esp32_dev_data *data = dev->data;
k_sem_take(&data->sem, FLASH_SEM_TIMEOUT);
}
static inline void flash_esp32_sem_give(const struct device *dev)
{
struct flash_esp32_dev_data *data = dev->data;
k_sem_give(&data->sem);
}
#else
#define flash_esp32_sem_take(dev) do {} while (0)
#define flash_esp32_sem_give(dev) do {} while (0)
#endif /* CONFIG_MULTITHREADING */
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include <zephyr/sys/util.h>
#include <stdint.h>
#include <string.h>
#ifdef CONFIG_MCUBOOT
#define READ_BUFFER_SIZE 32
static bool flash_esp32_is_aligned(off_t address, void *buffer, size_t length)
{
/* check if address, buffer pointer, and length are 4-byte aligned */
return ((address & 3) == 0) && (((uintptr_t)buffer & 3) == 0) && ((length & 3) == 0);
}
#endif
static int flash_esp32_read(const struct device *dev, off_t address, void *buffer, size_t length)
{
int ret = 0;
#ifdef CONFIG_MCUBOOT
uint8_t *dest_ptr = (uint8_t *)buffer;
size_t remaining = length;
size_t copy_size = 0;
size_t aligned_size = 0;
bool allow_decrypt = esp_flash_encryption_enabled();
if (flash_esp32_is_aligned(address, buffer, length)) {
ret = esp_rom_flash_read(address, buffer, length, allow_decrypt);
return (ret == ESP_OK) ? 0 : -EIO;
}
/* handle unaligned reading */
uint8_t __aligned(4) temp_buf[READ_BUFFER_SIZE + 8];
while (remaining > 0) {
size_t addr_offset = address & 3;
size_t buf_offset = (uintptr_t)dest_ptr & 3;
copy_size = (remaining > READ_BUFFER_SIZE) ? READ_BUFFER_SIZE : remaining;
if (addr_offset == 0 && buf_offset == 0 && copy_size >= 4) {
aligned_size = copy_size & ~3;
ret = esp_rom_flash_read(address, dest_ptr, aligned_size, allow_decrypt);
if (ret != ESP_OK) {
return -EIO;
}
address += aligned_size;
dest_ptr += aligned_size;
remaining -= aligned_size;
} else {
size_t start_addr = address - addr_offset;
aligned_size = (copy_size + addr_offset + 3) & ~3;
ret = esp_rom_flash_read(start_addr, temp_buf, aligned_size, allow_decrypt);
if (ret != ESP_OK) {
return -EIO;
}
memcpy(dest_ptr, temp_buf + addr_offset, copy_size);
address += copy_size;
dest_ptr += copy_size;
remaining -= copy_size;
}
}
#else
flash_esp32_sem_take(dev);
if (esp_flash_encryption_enabled()) {
ret = esp_flash_read_encrypted(NULL, address, buffer, length);
} else {
ret = esp_flash_read(NULL, buffer, address, length);
}
flash_esp32_sem_give(dev);
#endif
if (ret != 0) {
LOG_ERR("Flash read error: %d", ret);
return -EIO;
}
return 0;
}
static int flash_esp32_write(const struct device *dev,
off_t address,
const void *buffer,
size_t length)
{
int ret = 0;
#ifdef CONFIG_MCUBOOT
if (!flash_esp32_is_aligned(address, (void *)buffer, length)) {
LOG_ERR("Unaligned flash write is not supported");
return -EINVAL;
}
bool encrypt = esp_flash_encryption_enabled();
ret = esp_rom_flash_write(address, (void *)buffer, length, encrypt);
#else
flash_esp32_sem_take(dev);
if (esp_flash_encryption_enabled()) {
ret = esp_flash_write_encrypted(NULL, address, buffer, length);
} else {
ret = esp_flash_write(NULL, buffer, address, length);
}
flash_esp32_sem_give(dev);
#endif
if (ret != 0) {
LOG_ERR("Flash write error: %d", ret);
return -EIO;
}
return 0;
}
static int flash_esp32_erase(const struct device *dev, off_t start, size_t len)
{
int ret = 0;
#ifdef CONFIG_MCUBOOT
ret = esp_rom_flash_erase_range(start, len);
#else
flash_esp32_sem_take(dev);
ret = esp_flash_erase_region(NULL, start, len);
flash_esp32_sem_give(dev);
#endif
if (ret != 0) {
LOG_ERR("Flash erase error: %d", ret);
return -EIO;
}
return 0;
}
#if CONFIG_FLASH_PAGE_LAYOUT
static const struct flash_pages_layout flash_esp32_pages_layout = {
.pages_count = DT_REG_SIZE(SOC_NV_FLASH_NODE) / FLASH_ERASE_BLK_SZ,
.pages_size = DT_PROP(SOC_NV_FLASH_NODE, erase_block_size),
};
void flash_esp32_page_layout(const struct device *dev,
const struct flash_pages_layout **layout,
size_t *layout_size)
{
*layout = &flash_esp32_pages_layout;
*layout_size = 1;
}
#endif /* CONFIG_FLASH_PAGE_LAYOUT */
static const struct flash_parameters *
flash_esp32_get_parameters(const struct device *dev)
{
ARG_UNUSED(dev);
return &flash_esp32_parameters;
}
static int flash_esp32_init(const struct device *dev)
{
#ifdef CONFIG_MULTITHREADING
struct flash_esp32_dev_data *const dev_data = dev->data;
k_sem_init(&dev_data->sem, 1, 1);
#endif /* CONFIG_MULTITHREADING */
return 0;
}
static DEVICE_API(flash, flash_esp32_driver_api) = {
.read = flash_esp32_read,
.write = flash_esp32_write,
.erase = flash_esp32_erase,
.get_parameters = flash_esp32_get_parameters,
#ifdef CONFIG_FLASH_PAGE_LAYOUT
.page_layout = flash_esp32_page_layout,
#endif
};
static struct flash_esp32_dev_data flash_esp32_data;
static const struct flash_esp32_dev_config flash_esp32_config = {
.controller = (spi_dev_t *) DT_INST_REG_ADDR(0),
};
DEVICE_DT_INST_DEFINE(0, flash_esp32_init,
NULL,
&flash_esp32_data, &flash_esp32_config,
POST_KERNEL, CONFIG_FLASH_INIT_PRIORITY,
&flash_esp32_driver_api);