| /* |
| * 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_spi_flash.h> |
| #include <hal/spi_ll.h> |
| #include <hal/spi_flash_ll.h> |
| #include <hal/spi_flash_hal.h> |
| #include <soc/spi_struct.h> |
| #include <spi_flash_defs.h> |
| |
| #include <kernel.h> |
| #include <device.h> |
| #include <stddef.h> |
| #include <string.h> |
| #include <errno.h> |
| #include <drivers/flash.h> |
| #include <soc.h> |
| |
| #if defined(CONFIG_SOC_ESP32) |
| #include "soc/dport_reg.h" |
| #include "esp32/rom/cache.h" |
| #include "esp32/rom/spi_flash.h" |
| #include "esp32/spiram.h" |
| #elif defined(CONFIG_SOC_ESP32S2) |
| #include "soc/spi_mem_reg.h" |
| #include "esp32s2/rom/cache.h" |
| #include "esp32s2/rom/spi_flash.h" |
| #elif defined(CONFIG_SOC_ESP32C3) |
| #include "soc/spi_periph.h" |
| #include "soc/spi_mem_reg.h" |
| #include "soc/dport_access.h" |
| #include "esp32c3/dport_access.h" |
| #include "esp32c3/rom/cache.h" |
| #include "esp32c3/rom/spi_flash.h" |
| #endif |
| |
| #include "soc/mmu.h" |
| |
| #include <logging/log.h> |
| LOG_MODULE_REGISTER(flash_esp32, CONFIG_FLASH_LOG_LEVEL); |
| |
| struct flash_esp32_dev_config { |
| spi_dev_t *controller; |
| esp_rom_spiflash_chip_t *chip; |
| }; |
| |
| struct flash_esp32_dev_data { |
| struct k_sem sem; |
| }; |
| |
| static const struct flash_parameters flash_esp32_parameters = { |
| .write_block_size = FLASH_WRITE_BLK_SZ, |
| .erase_value = 0xff, |
| }; |
| |
| #if !defined(CONFIG_SOC_ESP32C3) |
| #define SPI1_EXTRA_DUMMIES (g_rom_spiflash_dummy_len_plus[1]) |
| #else |
| #define SPI1_EXTRA_DUMMIES ((uint8_t)((rom_spiflash_legacy_data->dummy_len_plus)[1])) |
| #define SPI_FREAD_QIO 0 |
| #define SPI_FREAD_DIO 0 |
| #endif |
| |
| #define MAX_BUFF_ALLOC_RETRIES 5 |
| #define MAX_READ_CHUNK 16384 |
| #define MAX_WRITE_CHUNK 8192 |
| #define ADDRESS_MASK_24BIT 0xFFFFFF |
| #define SPI_TIMEOUT_MSEC 500 |
| |
| #if defined(CONFIG_SOC_ESP32) |
| #define HOST_FLASH_CONTROLLER SPI0 |
| #define HOST_FLASH_RDSR SPI_FLASH_RDSR |
| #define HOST_FLASH_FASTRD SPI_FASTRD_MODE |
| #elif defined(CONFIG_SOC_ESP32S2) || defined(CONFIG_SOC_ESP32C3) |
| #define HOST_FLASH_CONTROLLER SPIMEM0 |
| #define HOST_FLASH_RDSR SPI_MEM_FLASH_RDSR |
| #define HOST_FLASH_FASTRD SPI_MEM_FASTRD_MODE |
| #endif |
| |
| #if defined(CONFIG_SOC_ESP32C3) |
| static esp_rom_spiflash_chip_t esp_flashchip_info; |
| #else |
| #define esp_flashchip_info g_rom_flashchip |
| #endif |
| |
| static inline void flash_esp32_sem_take(const struct device *dev) |
| { |
| struct flash_esp32_dev_data *data = dev->data; |
| |
| k_sem_take(&data->sem, K_FOREVER); |
| } |
| |
| static inline void flash_esp32_sem_give(const struct device *dev) |
| { |
| struct flash_esp32_dev_data *data = dev->data; |
| |
| k_sem_give(&data->sem); |
| } |
| |
| static inline int flash_esp32_wait_cmd_done(const spi_dev_t *hw) |
| { |
| int64_t timeout = k_uptime_get() + SPI_TIMEOUT_MSEC; |
| |
| while (!spi_flash_ll_cmd_is_done(hw)) { |
| if (k_uptime_get() > timeout) { |
| LOG_ERR("controller has timed out"); |
| return -ETIMEDOUT; |
| } |
| } |
| |
| return 0; |
| } |
| |
| static inline bool points_to_dram(const void *ptr) |
| { |
| return ((intptr_t)ptr >= SOC_DRAM_LOW && (intptr_t)ptr < SOC_DRAM_HIGH); |
| } |
| |
| int configure_read_mode(spi_dev_t *hw, |
| uint32_t cmd, |
| uint32_t addr_bitlen, |
| int dummy_len, |
| bool byte_cmd) |
| { |
| if (dummy_len) { |
| spi_flash_ll_set_dummy(hw, dummy_len); |
| } |
| |
| spi_flash_ll_set_addr_bitlen(hw, addr_bitlen); |
| |
| if (!byte_cmd) { |
| REG_SET_FIELD(PERIPHS_SPI_FLASH_USRREG2, SPI_USR_COMMAND_VALUE, cmd); |
| } else { |
| spi_flash_ll_set_command(hw, (uint8_t) cmd, 8); |
| } |
| |
| return 0; |
| } |
| |
| static bool IRAM_ATTR flash_esp32_mapped_in_cache(uint32_t phys_page, const void **out_ptr) |
| { |
| int start[2], end[2]; |
| |
| *out_ptr = NULL; |
| |
| /* SPI_FLASH_MMAP_DATA */ |
| start[0] = SOC_MMU_DROM0_PAGES_START; |
| end[0] = SOC_MMU_DROM0_PAGES_END; |
| |
| /* SPI_FLASH_MMAP_INST */ |
| start[1] = SOC_MMU_PRO_IRAM0_FIRST_USABLE_PAGE; |
| end[1] = SOC_MMU_IROM0_PAGES_END; |
| |
| DPORT_INTERRUPT_DISABLE(); |
| for (int j = 0; j < 2; j++) { |
| for (int i = start[j]; i < end[j]; i++) { |
| if (DPORT_SEQUENCE_REG_READ( |
| (uint32_t)&SOC_MMU_DPORT_PRO_FLASH_MMU_TABLE[i]) == |
| SOC_MMU_PAGE_IN_FLASH(phys_page)) { |
| #if !defined(CONFIG_SOC_ESP32) |
| if (j == 0) { /* SPI_FLASH_MMAP_DATA */ |
| *out_ptr = (const void *)(SOC_MMU_VADDR0_START_ADDR + |
| SPI_FLASH_MMU_PAGE_SIZE * (i - start[0])); |
| } else { |
| *out_ptr = (const void *)(SOC_MMU_VADDR1_FIRST_USABLE_ADDR + |
| SPI_FLASH_MMU_PAGE_SIZE * (i - start[1])); |
| } |
| #endif |
| DPORT_INTERRUPT_RESTORE(); |
| return true; |
| } |
| } |
| } |
| DPORT_INTERRUPT_RESTORE(); |
| |
| return false; |
| } |
| |
| /* Validates if flash address has corresponding cache mapping, if yes, flushes cache memories */ |
| static void IRAM_ATTR flash_esp32_flush_cache(size_t start_addr, size_t length) |
| { |
| /* align start_addr & length to full MMU pages */ |
| uint32_t page_start_addr = start_addr & ~(SPI_FLASH_MMU_PAGE_SIZE-1); |
| |
| length += (start_addr - page_start_addr); |
| length = (length + SPI_FLASH_MMU_PAGE_SIZE - 1) & ~(SPI_FLASH_MMU_PAGE_SIZE-1); |
| for (uint32_t addr = page_start_addr; |
| addr < page_start_addr + length; |
| addr += SPI_FLASH_MMU_PAGE_SIZE) { |
| |
| uint32_t page = addr / SPI_FLASH_MMU_PAGE_SIZE; |
| |
| if (page >= 256) { |
| return; |
| } |
| |
| const void *vaddr = NULL; |
| |
| if (flash_esp32_mapped_in_cache(page, &vaddr)) { |
| #if defined(CONFIG_SOC_ESP32) |
| #if CONFIG_ESP_SPIRAM |
| esp_spiram_writeback_cache(); |
| #endif |
| esp_rom_Cache_Flush(0); |
| #ifdef CONFIG_SMP |
| esp_rom_Cache_Flush(1); |
| #endif |
| return; |
| #else /* CONFIG_SOC_ESP32 */ |
| if (vaddr != NULL) { |
| esp_rom_Cache_Invalidate_Addr((uint32_t)vaddr, |
| SPI_FLASH_MMU_PAGE_SIZE); |
| } |
| #endif /* CONFIG_SOC_ESP32 */ |
| } |
| } |
| return; |
| } |
| |
| static int set_read_options(const struct device *dev) |
| { |
| const struct flash_esp32_dev_config *config = dev->config; |
| spi_dev_t *hw = config->controller; |
| uint32_t dummy_len = 0; |
| uint32_t addr_len; |
| uint8_t read_cmd; |
| bool byte_cmd = true; |
| uint32_t read_mode = READ_PERI_REG(PERIPHS_SPI_FLASH_CTRL); |
| |
| if ((read_mode & SPI_FREAD_QIO) && (read_mode & HOST_FLASH_FASTRD)) { |
| spi_ll_enable_mosi(hw, 0); |
| spi_ll_enable_miso(hw, 1); |
| dummy_len = 1 + SPI1_R_QIO_DUMMY_CYCLELEN + SPI1_EXTRA_DUMMIES; |
| addr_len = SPI1_R_QIO_ADDR_BITSLEN + 1; |
| read_cmd = CMD_FASTRD_QIO; |
| } else if (read_mode & HOST_FLASH_FASTRD) { |
| spi_ll_enable_mosi(hw, 0); |
| spi_ll_enable_miso(hw, 1); |
| if (read_mode & SPI_FREAD_DIO) { |
| read_cmd = CMD_FASTRD_DIO; |
| if (SPI1_EXTRA_DUMMIES == 0) { |
| spi_flash_ll_set_dummy(hw, 0); |
| addr_len = SPI1_R_DIO_ADDR_BITSLEN + 1; |
| } else { |
| byte_cmd = false; |
| dummy_len = SPI1_EXTRA_DUMMIES; |
| addr_len = SPI1_R_DIO_ADDR_BITSLEN + 1; |
| } |
| } else { |
| if ((read_mode & SPI_FREAD_QUAD)) { |
| read_cmd = CMD_FASTRD_QUAD; |
| } else if ((read_mode & SPI_FREAD_DUAL)) { |
| read_cmd = CMD_FASTRD_DUAL; |
| } else { |
| read_cmd = CMD_FASTRD; |
| } |
| dummy_len = 1 + SPI1_R_FAST_DUMMY_CYCLELEN + SPI1_EXTRA_DUMMIES; |
| addr_len = SPI1_R_FAST_ADDR_BITSLEN + 1; |
| } |
| } else { |
| spi_ll_enable_mosi(hw, 0); |
| if (SPI1_EXTRA_DUMMIES == 0) { |
| spi_flash_ll_set_dummy(hw, 0); |
| } else { |
| dummy_len = SPI1_EXTRA_DUMMIES; |
| } |
| spi_ll_enable_miso(hw, 1); |
| addr_len = SPI1_R_SIO_ADDR_BITSLEN + 1; |
| read_cmd = CMD_READ; |
| } |
| |
| return configure_read_mode(hw, read_cmd, addr_len, dummy_len, byte_cmd); |
| } |
| |
| static int read_once(const struct device *dev, void *buffer, uint32_t address, uint32_t read_len) |
| { |
| const struct flash_esp32_dev_config *config = dev->config; |
| spi_dev_t *hw = config->controller; |
| int bitlen = spi_flash_ll_get_addr_bitlen(hw); |
| |
| spi_flash_ll_set_usr_address(hw, address << (bitlen - 24), bitlen); |
| spi_flash_ll_set_miso_bitlen(hw, read_len * 8); |
| spi_flash_ll_user_start(hw); |
| |
| int rc = flash_esp32_wait_cmd_done(hw); |
| |
| if (rc != 0) { |
| return rc; |
| } |
| |
| spi_flash_ll_get_buffer_data(hw, buffer, read_len); |
| return 0; |
| } |
| |
| static int read_data(const struct device *dev, uint8_t *buffer, uint32_t address, uint32_t length) |
| { |
| int rc = 0; |
| |
| rc = set_read_options(dev); |
| |
| if (rc == -ENOTSUP) { |
| LOG_ERR("configure host io mode failed - unsupported"); |
| return rc; |
| } |
| |
| while (rc == 0 && length > 0) { |
| uint32_t read_len = MIN(length, SPI_FLASH_HAL_MAX_READ_BYTES); |
| |
| rc = read_once(dev, buffer, address, read_len); |
| |
| address += read_len; |
| length -= read_len; |
| buffer += read_len; |
| } |
| |
| return rc; |
| } |
| |
| static int flash_esp32_read(const struct device *dev, off_t address, void *buffer, size_t length) |
| { |
| const struct flash_esp32_dev_config *const cfg = dev->config; |
| const spi_flash_guard_funcs_t *guard = spi_flash_guard_get(); |
| uint32_t chip_size = cfg->chip->chip_size; |
| |
| #if defined(CONFIG_SOC_ESP32S2) || defined(CONFIG_SOC_ESP32C3) |
| WRITE_PERI_REG(PERIPHS_SPI_FLASH_CTRL, 0); |
| #endif |
| |
| if (length == 0) { |
| return 0; |
| } |
| |
| if (buffer == NULL || address > chip_size || address + length > chip_size) { |
| return -EINVAL; |
| } |
| |
| bool direct_read = points_to_dram(buffer); |
| uint8_t *temp_buff = NULL; |
| size_t read_chunk = MIN(MAX_READ_CHUNK, length); |
| size_t temp_chunk = MAX_READ_CHUNK; |
| int rc = 0; |
| |
| flash_esp32_sem_take(dev); |
| |
| if (!direct_read) { |
| |
| unsigned int retries = MAX_BUFF_ALLOC_RETRIES; |
| |
| while (temp_buff == NULL && retries--) { |
| read_chunk = MIN(read_chunk, temp_chunk); |
| temp_chunk >>= 1; |
| read_chunk = (read_chunk + 3) & ~3; |
| temp_buff = k_malloc(read_chunk); |
| } |
| |
| LOG_INF("allocate temp buffer: %p (%d)", temp_buff, read_chunk); |
| |
| if (temp_buff == NULL) { |
| rc = -ENOMEM; |
| goto out; |
| } |
| } |
| |
| uint8_t *buff = (uint8_t *)buffer; |
| |
| do { |
| guard->start(); |
| |
| uint8_t *read_buff = (temp_buff) ? temp_buff : buffer; |
| size_t read_len = MIN(read_chunk, length); |
| |
| rc = read_data(dev, read_buff, address, read_len); |
| |
| if (rc) { |
| guard->end(); |
| break; |
| } |
| |
| guard->end(); |
| |
| if (temp_buff) { |
| memcpy(buffer, temp_buff, read_len); |
| } |
| |
| address += read_len; |
| length -= read_len; |
| buff += read_len; |
| buffer = (void *)buff; |
| } while (rc == 0 && length > 0); |
| |
| k_free(temp_buff); |
| |
| out: |
| flash_esp32_sem_give(dev); |
| |
| return rc; |
| } |
| |
| static inline void set_write_options(const struct device *dev) |
| { |
| const struct flash_esp32_dev_config *config = dev->config; |
| spi_dev_t *hw = config->controller; |
| |
| spi_flash_ll_set_dummy(hw, 0); |
| /* only single line flash write is currently supported */ |
| spi_flash_ll_set_addr_bitlen(hw, (1 + ESP_ROM_SPIFLASH_W_SIO_ADDR_BITSLEN)); |
| } |
| |
| static int read_status(const struct device *dev, uint32_t *status) |
| { |
| const struct flash_esp32_dev_config *const cfg = dev->config; |
| uint32_t status_value = ESP_ROM_SPIFLASH_BUSY_FLAG; |
| |
| if (SPI1_EXTRA_DUMMIES == 0) { |
| while (ESP_ROM_SPIFLASH_BUSY_FLAG == |
| (status_value & ESP_ROM_SPIFLASH_BUSY_FLAG)) { |
| WRITE_PERI_REG(PERIPHS_SPI_FLASH_STATUS, 0); |
| WRITE_PERI_REG(PERIPHS_SPI_FLASH_CMD, HOST_FLASH_RDSR); |
| |
| int rc = flash_esp32_wait_cmd_done(cfg->controller); |
| |
| if (rc != 0) { |
| return rc; |
| } |
| |
| status_value = READ_PERI_REG(PERIPHS_SPI_FLASH_STATUS); |
| status_value &= cfg->chip->status_mask; |
| } |
| } else { |
| while (ESP_ROM_SPIFLASH_BUSY_FLAG == (status_value & ESP_ROM_SPIFLASH_BUSY_FLAG)) { |
| esp_rom_spiflash_read_user_cmd(&status_value, CMD_RDSR); |
| } |
| } |
| *status = status_value; |
| |
| return 0; |
| } |
| |
| static inline bool host_idle(spi_dev_t *hw) |
| { |
| #if defined(CONFIG_SOC_ESP32) |
| bool idle = spi_flash_ll_host_idle(hw); |
| |
| idle &= spi_flash_ll_host_idle(&HOST_FLASH_CONTROLLER); |
| #elif defined(CONFIG_SOC_ESP32S2) || defined(CONFIG_SOC_ESP32C3) |
| bool idle = spimem_flash_ll_host_idle((spi_mem_dev_t *)hw); |
| |
| idle &= spimem_flash_ll_host_idle(&HOST_FLASH_CONTROLLER); |
| #endif |
| |
| return idle; |
| } |
| |
| static int wait_idle(const struct device *dev) |
| { |
| const struct flash_esp32_dev_config *const cfg = dev->config; |
| uint32_t status; |
| int64_t timeout = k_uptime_get() + SPI_TIMEOUT_MSEC; |
| |
| /* wait for spi control ready */ |
| while (!host_idle(cfg->controller)) { |
| if (k_uptime_get() > timeout) { |
| return -ETIMEDOUT; |
| } |
| } |
| |
| /* wait for flash status ready */ |
| if (read_status(dev, &status) != 0) { |
| return -EINVAL; |
| } |
| return 0; |
| } |
| |
| static int write_protect(const struct device *dev, bool write_protect) |
| { |
| const struct flash_esp32_dev_config *const cfg = dev->config; |
| |
| wait_idle(dev); |
| |
| /* enable writing */ |
| spi_flash_ll_set_write_protect(cfg->controller, write_protect); |
| |
| int rc = flash_esp32_wait_cmd_done(cfg->controller); |
| |
| if (rc != 0) { |
| return rc; |
| } |
| #if !defined(CONFIG_SOC_ESP32C3) |
| uint32_t flash_status = 0; |
| |
| /* make sure the flash is ready for writing */ |
| while (ESP_ROM_SPIFLASH_WRENABLE_FLAG != (flash_status & ESP_ROM_SPIFLASH_WRENABLE_FLAG)) { |
| read_status(dev, &flash_status); |
| } |
| #endif |
| return 0; |
| } |
| |
| static int program_page(const struct device *dev, uint32_t spi_addr, |
| uint32_t *addr_source, int32_t byte_length) |
| { |
| const struct flash_esp32_dev_config *config = dev->config; |
| const uint32_t page_size = config->chip->page_size; |
| spi_dev_t *hw = config->controller; |
| |
| /* check 4byte alignment */ |
| if ((byte_length & 0x3) != 0) { |
| return -EINVAL; |
| } |
| |
| /* check if write in one page */ |
| if (page_size < ((spi_addr % page_size) + byte_length)) { |
| return -EINVAL; |
| } |
| |
| wait_idle(dev); |
| |
| uint32_t addr; |
| uint32_t prog_len; |
| |
| while (byte_length > 0) { |
| if (write_protect(dev, false) != 0) { |
| return -EINVAL; |
| } |
| |
| addr = spi_addr & ADDRESS_MASK_24BIT; |
| |
| if (byte_length >= ESP_ROM_SPIFLASH_BUFF_BYTE_WRITE_NUM) { |
| addr |= ESP_ROM_SPIFLASH_BUFF_BYTE_WRITE_NUM << ESP_ROM_SPIFLASH_BYTES_LEN; |
| prog_len = (uint32_t)ESP_ROM_SPIFLASH_BUFF_BYTE_WRITE_NUM; |
| spi_flash_ll_set_address(hw, addr); |
| spi_flash_ll_program_page(hw, addr_source, prog_len); |
| byte_length -= ESP_ROM_SPIFLASH_BUFF_BYTE_WRITE_NUM; |
| spi_addr += ESP_ROM_SPIFLASH_BUFF_BYTE_WRITE_NUM; |
| } else { |
| addr |= byte_length << ESP_ROM_SPIFLASH_BYTES_LEN; |
| prog_len = (uint32_t)byte_length; |
| spi_flash_ll_set_address(hw, addr); |
| spi_flash_ll_program_page(hw, addr_source, prog_len); |
| byte_length = 0; |
| } |
| |
| addr_source += (ESP_ROM_SPIFLASH_BUFF_BYTE_WRITE_NUM/4); |
| |
| int rc = flash_esp32_wait_cmd_done(hw); |
| |
| if (rc != 0) { |
| return rc; |
| } |
| |
| wait_idle(dev); |
| } |
| |
| return 0; |
| } |
| |
| static int flash_esp32_write_inner(const struct device *dev, |
| uint32_t address, |
| const uint32_t *buffer, |
| size_t length) |
| { |
| const struct flash_esp32_dev_config *config = dev->config; |
| const uint32_t page_size = config->chip->page_size; |
| const uint32_t chip_size = config->chip->chip_size; |
| uint32_t prog_len, prog_num; |
| |
| set_write_options(dev); |
| |
| /* check program size */ |
| if ((address + length) > chip_size) { |
| return -EINVAL; |
| } |
| |
| prog_len = page_size - (address % page_size); |
| if (length < prog_len) { |
| if (program_page(dev, address, (uint32_t *)buffer, length) != 0) { |
| return -EINVAL; |
| } |
| } else { |
| if (program_page(dev, address, (uint32_t *)buffer, prog_len) != 0) { |
| return -EINVAL; |
| } |
| |
| /* whole page program */ |
| prog_num = (length - prog_len) / page_size; |
| for (uint8_t i = 0; i < prog_num; ++i) { |
| if (program_page(dev, address + prog_len, |
| (uint32_t *)buffer + (prog_len >> 2), |
| page_size) != 0) { |
| return -EINVAL; |
| } |
| |
| prog_len += page_size; |
| } |
| |
| /* remain parts to program */ |
| if (program_page(dev, address + prog_len, |
| (uint32_t *)buffer + (prog_len >> 2), |
| length - prog_len) != 0) { |
| return -EINVAL; |
| } |
| } |
| |
| return 0; |
| } |
| |
| static int flash_esp32_write(const struct device *dev, |
| off_t address, |
| const void *buffer, |
| size_t length) |
| { |
| const struct flash_esp32_dev_config *config = dev->config; |
| const uint32_t chip_size = config->chip->chip_size; |
| const spi_flash_guard_funcs_t *guard = spi_flash_guard_get(); |
| int rc = 0; |
| |
| if (address + length > chip_size) { |
| return -EINVAL; |
| } |
| |
| if (length == 0) { |
| return 0; |
| } |
| |
| const uint8_t *srcc = (const uint8_t *) buffer; |
| /* |
| * Large operations are split into (up to) 3 parts: |
| * - Left padding: 4 bytes up to the first 4-byte aligned destination offset. |
| * - Middle part |
| * - Right padding: 4 bytes from the last 4-byte aligned offset covered. |
| */ |
| size_t left_off = address & ~3U; |
| size_t left_size = MIN(((address + 3) & ~3U) - address, length); |
| size_t mid_off = left_size; |
| size_t mid_size = (length - left_size) & ~3U; |
| size_t right_off = left_size + mid_size; |
| size_t right_size = length - mid_size - left_size; |
| |
| flash_esp32_sem_take(dev); |
| |
| if (left_size > 0) { |
| uint32_t t = 0xffffffff; |
| |
| memcpy(((uint8_t *) &t) + (address - left_off), srcc, left_size); |
| guard->start(); |
| rc = flash_esp32_write_inner(dev, left_off, &t, 4); |
| guard->end(); |
| if (rc != 0) { |
| goto out; |
| } |
| } |
| if (mid_size > 0) { |
| bool direct_write = esp_ptr_internal(srcc) |
| && esp_ptr_byte_accessible(srcc) |
| && ((uintptr_t) srcc + mid_off) % 4 == 0; |
| |
| while (mid_size > 0 && rc == 0) { |
| uint32_t write_buf[8]; |
| uint32_t write_size = MIN(mid_size, MAX_WRITE_CHUNK); |
| const uint8_t *write_src = srcc + mid_off; |
| |
| if (!direct_write) { |
| write_size = MIN(write_size, sizeof(write_buf)); |
| memcpy(write_buf, write_src, write_size); |
| write_src = (const uint8_t *)write_buf; |
| } |
| guard->start(); |
| rc = flash_esp32_write_inner(dev, address + mid_off, |
| (const uint32_t *) write_src, write_size); |
| guard->end(); |
| mid_size -= write_size; |
| mid_off += write_size; |
| } |
| if (rc != 0) { |
| goto out; |
| } |
| } |
| |
| if (right_size > 0) { |
| uint32_t t = 0xffffffff; |
| |
| memcpy(&t, srcc + right_off, right_size); |
| guard->start(); |
| rc = flash_esp32_write_inner(dev, address + right_off, &t, 4); |
| guard->end(); |
| } |
| |
| out: |
| guard->start(); |
| flash_esp32_flush_cache(address, length); |
| guard->end(); |
| flash_esp32_sem_give(dev); |
| |
| return rc; |
| } |
| |
| static int erase_sector(const struct device *dev, uint32_t start_addr) |
| { |
| const struct flash_esp32_dev_config *config = dev->config; |
| spi_dev_t *hw = config->controller; |
| int rc = write_protect(dev, false); |
| |
| if (rc == 0) { |
| rc = wait_idle(dev); |
| } |
| |
| if (rc == 0) { |
| spi_flash_ll_set_addr_bitlen(hw, 24); |
| spi_flash_ll_set_address(hw, start_addr & ADDRESS_MASK_24BIT); |
| spi_flash_ll_erase_sector(hw); |
| |
| rc = flash_esp32_wait_cmd_done(hw); |
| if (rc) { |
| return rc; |
| } |
| |
| rc = wait_idle(dev); |
| if (rc) { |
| LOG_ERR("waiting for host device idle state has failed"); |
| } |
| } |
| |
| return rc; |
| } |
| |
| static int flash_esp32_erase(const struct device *dev, off_t start, size_t len) |
| { |
| const struct flash_esp32_dev_config *config = dev->config; |
| uint32_t sector_size = config->chip->sector_size; |
| uint32_t chip_size = config->chip->chip_size; |
| const spi_flash_guard_funcs_t *guard = spi_flash_guard_get(); |
| int rc = 0; |
| |
| if (start % sector_size != 0) { |
| return -EINVAL; |
| } |
| if (len % sector_size != 0) { |
| return -EINVAL; |
| } |
| if (len + start > chip_size) { |
| return -EINVAL; |
| } |
| |
| flash_esp32_sem_take(dev); |
| |
| set_write_options(dev); |
| |
| while (len >= sector_size) { |
| guard->start(); |
| |
| rc = erase_sector(dev, start); |
| if (rc) { |
| guard->end(); |
| goto out; |
| } |
| |
| start += sector_size; |
| len -= sector_size; |
| |
| guard->end(); |
| } |
| |
| out: |
| guard->start(); |
| flash_esp32_flush_cache(start, len); |
| guard->end(); |
| |
| flash_esp32_sem_give(dev); |
| |
| return rc; |
| } |
| |
| #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) |
| { |
| struct flash_esp32_dev_data *const dev_data = dev->data; |
| |
| #if defined(CONFIG_SOC_ESP32C3) |
| spiflash_legacy_data_t *legacy_data = rom_spiflash_legacy_data; |
| |
| esp_flashchip_info.chip_size = legacy_data->chip.chip_size; |
| esp_flashchip_info.sector_size = legacy_data->chip.sector_size; |
| esp_flashchip_info.page_size = legacy_data->chip.page_size; |
| #endif |
| |
| k_sem_init(&dev_data->sem, 1, 1); |
| |
| return 0; |
| } |
| |
| static const struct flash_driver_api 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), |
| .chip = &esp_flashchip_info |
| }; |
| |
| 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); |