blob: 6b19153720ec8f54e51e8b5c42b65c4c825192e6 [file] [log] [blame]
/****************************************************************************
* arch/xtensa/src/esp32/esp32_spiflash.c
*
* 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 <nuttx/config.h>
#ifdef CONFIG_ESP32_SPIFLASH
#include <stdint.h>
#include <assert.h>
#include <debug.h>
#include <stdio.h>
#include <string.h>
#include <sys/param.h>
#include <sys/types.h>
#include <errno.h>
#include <nuttx/arch.h>
#include <nuttx/init.h>
#include <nuttx/kthread.h>
#include <nuttx/mutex.h>
#include <nuttx/mtd/mtd.h>
#include "sched/sched.h"
#include "xtensa.h"
#include "xtensa_attr.h"
#include "rom/esp32_spiflash.h"
#include "hardware/esp32_soc.h"
#include "hardware/esp32_spi.h"
#include "hardware/esp32_dport.h"
#include "hardware/esp32_efuse.h"
#include "esp32_spicache.h"
#ifdef CONFIG_ESP32_SPIRAM
#include "esp32_spiram.h"
#endif
#include "esp32_irq.h"
#include "esp32_spiflash.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define SPI_FLASH_WRITE_BUF_SIZE (32)
#define SPI_FLASH_READ_BUF_SIZE (64)
#define SPI_FLASH_WRITE_WORDS (SPI_FLASH_WRITE_BUF_SIZE / 4)
#define SPI_FLASH_READ_WORDS (SPI_FLASH_READ_BUF_SIZE / 4)
#define SPI_FLASH_MMU_PAGE_SIZE (0x10000)
#define SPI_FLASH_ENCRYPT_UNIT_SIZE (32)
#define SPI_FLASH_ENCRYPT_WORDS (32 / 4)
#define SPI_FLASH_ERASED_STATE (0xff)
#define SPI_FLASH_ENCRYPT_MIN_SIZE (16)
#define MTD2PRIV(_dev) ((struct esp32_spiflash_s *)_dev)
#define MTD_SIZE(_priv) ((_priv)->chip->chip_size)
#define MTD_BLKSIZE(_priv) ((_priv)->chip->page_size)
#define MTD_ERASESIZE(_priv) ((_priv)->chip->sector_size)
#define MTD_BLK2SIZE(_priv, _b) (MTD_BLKSIZE(_priv) * (_b))
#define MTD_SIZE2BLK(_priv, _s) ((_s) / MTD_BLKSIZE(_priv))
#define MMU_ADDR2PAGE(_addr) ((_addr) / SPI_FLASH_MMU_PAGE_SIZE)
#define MMU_ADDR2OFF(_addr) ((_addr) % SPI_FLASH_MMU_PAGE_SIZE)
#define MMU_BYTES2PAGES(_n) (((_n) + SPI_FLASH_MMU_PAGE_SIZE - 1) \
/ SPI_FLASH_MMU_PAGE_SIZE)
#define MMU_ALIGNUP_SIZE(_s) (((_s) + SPI_FLASH_MMU_PAGE_SIZE - 1) \
& ~(SPI_FLASH_MMU_PAGE_SIZE - 1))
#define MMU_ALIGNDOWN_SIZE(_s) ((_s) & ~(SPI_FLASH_MMU_PAGE_SIZE - 1))
/* Flash MMU table for PRO CPU */
#define PRO_MMU_TABLE ((volatile uint32_t *)DPORT_PRO_FLASH_MMU_TABLE_REG)
/* Flash MMU table for APP CPU */
#define APP_MMU_TABLE ((volatile uint32_t *)DPORT_APP_FLASH_MMU_TABLE_REG)
#define PRO_IRAM0_FIRST_PAGE ((SOC_IRAM_LOW - SOC_DRAM_HIGH) /\
(SPI_FLASH_MMU_PAGE_SIZE + IROM0_PAGES_START))
#ifdef CONFIG_ESP32_SPI_FLASH_SUPPORT_PSRAM_STACK
/* SPI flash work operation code */
enum spiflash_op_code_e
{
SPIFLASH_OP_CODE_WRITE = 0,
SPIFLASH_OP_CODE_READ,
SPIFLASH_OP_CODE_ERASE,
SPIFLASH_OP_CODE_SET_BANK,
SPIFLASH_OP_CODE_ENCRYPT_READ,
SPIFLASH_OP_CODE_ENCRYPT_WRITE
};
#endif
/****************************************************************************
* Private Types
****************************************************************************/
/* SPI Flash device hardware configuration */
struct esp32_spiflash_config_s
{
/* SPI register base address */
uint32_t reg_base;
};
/* SPI Flash device private data */
struct esp32_spiflash_s
{
struct mtd_dev_s mtd;
/* Port configuration */
struct esp32_spiflash_config_s *config;
/* SPI Flash data */
esp32_spiflash_chip_t *chip;
/* SPI Flash communication dummy number */
uint8_t *dummies;
};
/* SPI Flash map request data */
struct spiflash_map_req
{
/* Request mapping SPI Flash base address */
uint32_t src_addr;
/* Request mapping SPI Flash size */
uint32_t size;
/* Mapped memory pointer */
void *ptr;
/* Mapped started MMU page index */
uint32_t start_page;
/* Mapped MMU page count */
uint32_t page_cnt;
};
#ifdef CONFIG_ESP32_SPI_FLASH_SUPPORT_PSRAM_STACK
/* SPI flash work operation arguments */
struct spiflash_work_arg
{
enum spiflash_op_code_e op_code;
struct
{
struct esp32_spiflash_s *priv;
uint32_t addr;
uint8_t *buffer;
uint32_t size;
uint32_t paddr;
} op_arg;
volatile int ret;
sem_t sem;
};
#endif
/****************************************************************************
* ROM function prototypes
****************************************************************************/
extern void cache_flush(int cpu);
/****************************************************************************
* Private Functions Prototypes
****************************************************************************/
/* SPI helpers */
static inline void spi_set_reg(struct esp32_spiflash_s *priv,
int offset, uint32_t value);
static inline uint32_t spi_get_reg(struct esp32_spiflash_s *priv,
int offset);
static inline void spi_set_regbits(struct esp32_spiflash_s *priv,
int offset, uint32_t bits);
static inline void spi_reset_regbits(struct esp32_spiflash_s *priv,
int offset, uint32_t bits);
/* Misc. helpers */
static inline void IRAM_ATTR
esp32_spiflash_opstart(void);
static inline void IRAM_ATTR
esp32_spiflash_opdone(void);
static bool IRAM_ATTR spiflash_pagecached(uint32_t phypage);
static void IRAM_ATTR spiflash_flushmapped(size_t start, size_t size);
/* Flash helpers */
static void IRAM_ATTR esp32_set_read_opt(struct esp32_spiflash_s *priv);
static void IRAM_ATTR esp32_set_write_opt(struct esp32_spiflash_s *priv);
static int IRAM_ATTR esp32_read_status(struct esp32_spiflash_s *priv,
uint32_t *status);
static int IRAM_ATTR esp32_wait_idle(struct esp32_spiflash_s *priv);
static int IRAM_ATTR esp32_enable_write(struct esp32_spiflash_s *priv);
static int IRAM_ATTR esp32_erasesector(struct esp32_spiflash_s *priv,
uint32_t addr, uint32_t size);
static int IRAM_ATTR esp32_writedata(struct esp32_spiflash_s *priv,
uint32_t addr,
const uint8_t *buffer, uint32_t size);
static int IRAM_ATTR esp32_readdata(struct esp32_spiflash_s *priv,
uint32_t addr,
uint8_t *buffer, uint32_t size);
static int IRAM_ATTR esp32_readdata_encrypted(struct esp32_spiflash_s *priv,
uint32_t addr,
uint8_t *buffer,
uint32_t size);
static int IRAM_ATTR esp32_writedata_encrypted(struct esp32_spiflash_s *priv,
uint32_t addr,
const uint8_t *buffer,
uint32_t size);
static int esp32_writeblk_encrypted(struct esp32_spiflash_s *priv,
uint32_t offset,
const uint8_t *buffer,
uint32_t nbytes);
#ifdef CONFIG_ESP32_SPI_FLASH_SUPPORT_PSRAM_STACK
static void esp32_spiflash_work(void *p);
#endif
/* MTD driver methods */
static int esp32_erase(struct mtd_dev_s *dev, off_t startblock,
size_t nblocks);
static ssize_t esp32_read(struct mtd_dev_s *dev, off_t offset,
size_t nbytes, uint8_t *buffer);
static ssize_t esp32_read_decrypt(struct mtd_dev_s *dev,
off_t offset,
size_t nbytes,
uint8_t *buffer);
static ssize_t esp32_bread(struct mtd_dev_s *dev, off_t startblock,
size_t nblocks, uint8_t *buffer);
static ssize_t esp32_bread_decrypt(struct mtd_dev_s *dev,
off_t startblock,
size_t nblocks,
uint8_t *buffer);
#ifdef CONFIG_MTD_BYTE_WRITE
static ssize_t esp32_write(struct mtd_dev_s *dev, off_t offset,
size_t nbytes, const uint8_t *buffer);
#endif
static ssize_t esp32_bwrite(struct mtd_dev_s *dev, off_t startblock,
size_t nblocks, const uint8_t *buffer);
static ssize_t esp32_bwrite_encrypt(struct mtd_dev_s *dev,
off_t startblock,
size_t nblocks,
const uint8_t *buffer);
static int esp32_ioctl(struct mtd_dev_s *dev, int cmd,
unsigned long arg);
static int esp32_ioctl_encrypt(struct mtd_dev_s *dev, int cmd,
unsigned long arg);
#ifdef CONFIG_ESP32_SPIRAM
static int esp32_set_mmu_map(int vaddr, int paddr, int num);
#endif
/****************************************************************************
* Private Data
****************************************************************************/
static struct esp32_spiflash_config_s g_esp32_spiflash1_config =
{
.reg_base = REG_SPI_BASE(1)
};
static struct esp32_spiflash_s g_esp32_spiflash1 =
{
.mtd =
{
.erase = esp32_erase,
.bread = esp32_bread,
.bwrite = esp32_bwrite,
.read = esp32_read,
.ioctl = esp32_ioctl,
#ifdef CONFIG_MTD_BYTE_WRITE
.write = esp32_write,
#endif
.name = "esp32_mainflash"
},
.config = &g_esp32_spiflash1_config,
.chip = &g_rom_flashchip,
.dummies = g_rom_spiflash_dummy_len_plus
};
static struct esp32_spiflash_s g_esp32_spiflash1_encrypt =
{
.mtd =
{
.erase = esp32_erase,
.bread = esp32_bread_decrypt,
.bwrite = esp32_bwrite_encrypt,
.read = esp32_read_decrypt,
.ioctl = esp32_ioctl_encrypt,
#ifdef CONFIG_MTD_BYTE_WRITE
.write = NULL,
#endif
.name = "esp32_mainflash_encrypt"
},
.config = &g_esp32_spiflash1_config,
.chip = &g_rom_flashchip,
.dummies = g_rom_spiflash_dummy_len_plus
};
/* Ensure exclusive access to the driver */
static mutex_t g_lock = NXMUTEX_INITIALIZER;
#ifdef CONFIG_ESP32_SPI_FLASH_SUPPORT_PSRAM_STACK
static struct work_s g_work;
#endif
static volatile bool g_flash_op_can_start = false;
static volatile bool g_flash_op_complete = false;
static volatile bool g_sched_suspended[CONFIG_SMP_NCPUS];
#ifdef CONFIG_SMP
static sem_t g_disable_non_iram_isr_on_core[CONFIG_SMP_NCPUS];
#endif
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: spi_set_reg
*
* Description:
* Set the content of the SPI register at offset
*
* Input Parameters:
* priv - Private SPI device structure
* offset - Offset to the register of interest
* value - Value to be written
*
* Returned Value:
* None
*
****************************************************************************/
static inline void spi_set_reg(struct esp32_spiflash_s *priv,
int offset, uint32_t value)
{
putreg32(value, priv->config->reg_base + offset);
}
/****************************************************************************
* Name: spi_get_reg
*
* Description:
* Get the content of the SPI register at offset
*
* Input Parameters:
* priv - Private SPI device structure
* offset - Offset to the register of interest
*
* Returned Value:
* The content of the register
*
****************************************************************************/
static inline uint32_t spi_get_reg(struct esp32_spiflash_s *priv,
int offset)
{
return getreg32(priv->config->reg_base + offset);
}
/****************************************************************************
* Name: spi_set_regbits
*
* Description:
* Set the bits of the SPI register at offset
*
* Input Parameters:
* priv - Private SPI device structure
* offset - Offset to the register of interest
* bits - Bits to be set
*
* Returned Value:
* None
*
****************************************************************************/
static inline void IRAM_ATTR spi_set_regbits(struct esp32_spiflash_s *priv,
int offset, uint32_t bits)
{
uint32_t tmp = getreg32(priv->config->reg_base + offset);
putreg32(tmp | bits, priv->config->reg_base + offset);
}
/****************************************************************************
* Name: spi_reset_regbits
*
* Description:
* Clear the bits of the SPI register at offset
*
* Input Parameters:
* priv - Private SPI device structure
* offset - Offset to the register of interest
* bits - Bits to be cleared
*
* Returned Value:
* None
*
****************************************************************************/
static inline void spi_reset_regbits(struct esp32_spiflash_s *priv,
int offset, uint32_t bits)
{
uint32_t tmp = getreg32(priv->config->reg_base + offset);
putreg32(tmp & (~bits), priv->config->reg_base + offset);
}
/****************************************************************************
* Name: esp32_spiflash_opstart
*
* Description:
* Prepare for an SPIFLASH operartion.
*
****************************************************************************/
static void esp32_spiflash_opstart(void)
{
struct tcb_s *tcb = this_task();
int saved_priority = tcb->sched_priority;
int cpu;
#ifdef CONFIG_SMP
int other_cpu;
#endif
/* Temporary raise schedule priority */
nxsched_set_priority(tcb, SCHED_PRIORITY_MAX);
cpu = up_cpu_index();
#ifdef CONFIG_SMP
other_cpu = cpu == 1 ? 0 : 1;
#endif
DEBUGASSERT(cpu == 0 || cpu == 1);
#ifdef CONFIG_SMP
DEBUGASSERT(other_cpu == 0 || other_cpu == 1);
DEBUGASSERT(other_cpu != cpu);
if (OSINIT_OS_READY())
{
g_flash_op_can_start = false;
nxsem_post(&g_disable_non_iram_isr_on_core[other_cpu]);
while (!g_flash_op_can_start)
{
/* Busy loop and wait for spi_flash_op_block_task to disable cache
* on the other CPU
*/
}
}
#endif
g_sched_suspended[cpu] = true;
sched_lock();
nxsched_set_priority(tcb, saved_priority);
esp32_irq_noniram_disable();
spi_disable_cache(cpu);
#ifdef CONFIG_SMP
spi_disable_cache(other_cpu);
#endif
}
/****************************************************************************
* Name: esp32_spiflash_opdone
*
* Description:
* Undo all the steps of opstart.
*
****************************************************************************/
static void esp32_spiflash_opdone(void)
{
const int cpu = up_cpu_index();
#ifdef CONFIG_SMP
const int other_cpu = cpu ? 0 : 1;
#endif
DEBUGASSERT(cpu == 0 || cpu == 1);
#ifdef CONFIG_SMP
DEBUGASSERT(other_cpu == 0 || other_cpu == 1);
DEBUGASSERT(other_cpu != cpu);
#endif
spi_enable_cache(cpu);
#ifdef CONFIG_SMP
spi_enable_cache(other_cpu);
#endif
/* Signal to spi_flash_op_block_task that flash operation is complete */
g_flash_op_complete = true;
esp32_irq_noniram_enable();
sched_unlock();
g_sched_suspended[cpu] = false;
#ifdef CONFIG_SMP
while (g_sched_suspended[other_cpu])
{
/* Busy loop and wait for spi_flash_op_block_task to properly finish
* and resume scheduler
*/
}
#endif
}
/****************************************************************************
* Name: stack_is_psram
*
* Description:
* Check if current task's stack space is in PSRAM
*
* Returned Value:
* true if it is in PSRAM or false if not.
*
****************************************************************************/
#ifdef CONFIG_ESP32_SPI_FLASH_SUPPORT_PSRAM_STACK
static inline bool IRAM_ATTR stack_is_psram(void)
{
void *sp = (void *)up_getsp();
return esp32_ptr_extram(sp);
}
#endif
/****************************************************************************
* Name: spiflash_pagecached
*
* Description:
* Check if the given page is cached.
*
****************************************************************************/
static bool IRAM_ATTR spiflash_pagecached(uint32_t phypage)
{
int start[2];
int end[2];
int i;
int j;
/* Data ROM start and end pages */
start[0] = DROM0_PAGES_START;
end[0] = DROM0_PAGES_END;
/* Instruction RAM start and end pages */
start[1] = PRO_IRAM0_FIRST_PAGE;
end[1] = IROM0_PAGES_END;
for (i = 0; i < 2; i++)
{
for (j = start[i]; j < end[i]; j++)
{
if (PRO_MMU_TABLE[j] == phypage)
{
return true;
}
}
}
return false;
}
/****************************************************************************
* Name: spiflash_flushmapped
*
* Description:
* Writeback PSRAM data and invalidate the cache if the address is mapped.
*
****************************************************************************/
static void IRAM_ATTR spiflash_flushmapped(size_t start, size_t size)
{
uint32_t page_start;
uint32_t addr;
uint32_t page;
page_start = MMU_ALIGNDOWN_SIZE(start);
size += (start - page_start);
size = MMU_ALIGNUP_SIZE(size);
for (addr = page_start; addr < page_start + size;
addr += SPI_FLASH_MMU_PAGE_SIZE)
{
page = addr / SPI_FLASH_MMU_PAGE_SIZE;
if (page >= 256)
{
return;
}
if (spiflash_pagecached(page))
{
#ifdef CONFIG_ESP32_SPIRAM
esp_spiram_writeback_cache();
#endif
cache_flush(0);
#ifdef CONFIG_SMP
cache_flush(1);
#endif
}
}
}
/****************************************************************************
* Name: esp32_set_read_opt
*
* Description:
* Set SPI Flash to be direct read mode. Due to different SPI I/O mode
* including DIO, QIO and so on. Different command and communication
* timing sequence are needed.
*
* Input Parameters:
* None
*
* Returned Value:
* None
*
****************************************************************************/
static void IRAM_ATTR esp32_set_read_opt(struct esp32_spiflash_s *priv)
{
uint32_t regval;
uint32_t ctrl;
uint32_t mode;
uint32_t cmd;
uint32_t cycles = 0;
uint32_t addrbits = 0;
uint32_t dummy = 0;
ctrl = spi_get_reg(priv, SPI_CTRL_OFFSET);
mode = ctrl & (SPI_FREAD_QIO | SPI_FASTRD_MODE);
if (mode == (SPI_FREAD_QIO | SPI_FASTRD_MODE))
{
cycles = SPI1_R_QIO_DUMMY_CYCLELEN + priv->dummies[1];
dummy = 1;
addrbits = SPI1_R_QIO_ADDR_BITSLEN;
cmd = (0x7 << SPI_USR_COMMAND_BITLEN_S) | 0xeb;
}
else if (mode == SPI_FASTRD_MODE)
{
if (ctrl & SPI_FREAD_DIO)
{
if (priv->dummies[1] == 0)
{
addrbits = SPI1_R_DIO_ADDR_BITSLEN;
cmd = (0x7 << SPI_USR_COMMAND_BITLEN_S) | 0xbb;
}
else
{
cycles = priv->dummies[1] - 1;
dummy = 1;
addrbits = SPI1_R_DIO_ADDR_BITSLEN;
cmd = 0xbb;
}
}
else
{
if (ctrl & SPI_FREAD_QUAD)
{
cmd = (0x7 << SPI_USR_COMMAND_BITLEN_S) | 0x6b;
}
else if (ctrl & SPI_FREAD_DUAL)
{
cmd = (0x7 << SPI_USR_COMMAND_BITLEN_S) | 0x3b;
}
else
{
cmd = (0x7 << SPI_USR_COMMAND_BITLEN_S) | 0x0b;
}
cycles = SPI1_R_FAST_DUMMY_CYCLELEN + priv->dummies[1];
dummy = 1;
addrbits = SPI1_R_DIO_ADDR_BITSLEN;
}
}
else
{
if (priv->dummies[1] != 0)
{
cycles = priv->dummies[1] - 1;
dummy = 1;
}
addrbits = SPI1_R_SIO_ADDR_BITSLEN ;
cmd = (0x7 << SPI_USR_COMMAND_BITLEN_S) | 0x03;
}
regval = spi_get_reg(priv, SPI_USER_OFFSET);
regval &= ~SPI_USR_MOSI;
regval = SPI_USR_MISO | SPI_USR_ADDR;
if (dummy)
{
regval |= SPI_USR_DUMMY;
}
else
{
regval &= ~SPI_USR_DUMMY;
}
spi_set_regbits(priv, SPI_USER_OFFSET, regval);
regval = spi_get_reg(priv, SPI_USER1_OFFSET);
regval &= ~SPI_USR_DUMMY_CYCLELEN_M;
regval |= cycles << SPI_USR_DUMMY_CYCLELEN_S;
regval &= ~SPI_USR_ADDR_BITLEN_M;
regval |= addrbits << SPI_USR_ADDR_BITLEN_S;
spi_set_reg(priv, SPI_USER1_OFFSET, regval);
regval = spi_get_reg(priv, SPI_USER2_OFFSET);
regval &= ~SPI_USR_COMMAND_VALUE;
regval |= cmd;
spi_set_reg(priv, SPI_USER2_OFFSET, regval);
}
/****************************************************************************
* Name: esp32_set_write_opt
*
* Description:
* Set SPI Flash to be direct write mode. Due to different SPI I/O mode
* including DIO, QIO and so on. Different command and communication
* timing sequence are needed.
*
* Input Parameters:
* None
*
* Returned Value:
* None
*
****************************************************************************/
static void IRAM_ATTR esp32_set_write_opt(struct esp32_spiflash_s *priv)
{
uint32_t addrbits;
uint32_t regval;
spi_reset_regbits(priv, SPI_USER_OFFSET, SPI_USR_DUMMY);
addrbits = ESP_ROM_SPIFLASH_W_SIO_ADDR_BITSLEN;
regval = spi_get_reg(priv, SPI_USER1_OFFSET);
regval &= ~SPI_USR_ADDR_BITLEN_M;
regval |= addrbits << SPI_USR_ADDR_BITLEN_S;
spi_set_reg(priv, SPI_USER1_OFFSET, regval);
}
/****************************************************************************
* Name: esp32_read_status
*
* Description:
* Read SPI Flash status register value.
*
* Input Parameters:
* spi - ESP32 SPI Flash chip data
* status - status buffer pointer
*
* Returned Value:
* OK if success or a negative value if fail.
*
****************************************************************************/
static int IRAM_ATTR esp32_read_status(struct esp32_spiflash_s *priv,
uint32_t *status)
{
esp32_spiflash_chip_t *chip = priv->chip;
uint32_t regval;
uint32_t flags;
bool direct = (priv->dummies[1] == 0);
do
{
if (direct)
{
spi_set_reg(priv, SPI_RD_STATUS_OFFSET, 0);
spi_set_reg(priv, SPI_CMD_OFFSET, SPI_FLASH_RDSR);
while (spi_get_reg(priv, SPI_CMD_OFFSET) != 0)
{
;
}
regval = spi_get_reg(priv, SPI_RD_STATUS_OFFSET);
regval &= chip->status_mask;
flags = regval & ESP_ROM_SPIFLASH_BUSY_FLAG;
}
else
{
if (esp_rom_spiflash_read_user_cmd(&regval, 0x05))
{
return -EIO;
}
flags = regval & ESP_ROM_SPIFLASH_BUSY_FLAG;
}
}
while (flags == ESP_ROM_SPIFLASH_BUSY_FLAG);
*status = regval;
return OK;
}
/****************************************************************************
* Name: esp32_wait_idle
*
* Description:
* Wait for SPI Flash to be in an idle state.
*
* Input Parameters:
* spi - ESP32 SPI Flash chip data
*
* Returned Value:
* OK if success or a negative value if fail.
*
****************************************************************************/
static int IRAM_ATTR esp32_wait_idle(struct esp32_spiflash_s *priv)
{
uint32_t status;
while (spi_get_reg(priv, SPI_EXT2_OFFSET) & SPI_ST)
{
;
}
while (getreg32(SPI_EXT2_REG(0)) & SPI_ST)
{
;
}
if (esp32_read_status(priv, &status) != OK)
{
return -EIO;
}
return OK;
}
/****************************************************************************
* Name: esp32_enable_write
*
* Description:
* Drive SPI flash entering into write mode.
*
* Input Parameters:
* spi - ESP32 SPI Flash chip data
*
* Returned Value:
* OK if success or a negative value if fail.
*
****************************************************************************/
static int IRAM_ATTR esp32_enable_write(struct esp32_spiflash_s *priv)
{
uint32_t flags;
uint32_t regval;
if (esp32_wait_idle(priv) != OK)
{
return -EIO;
}
spi_set_reg(priv, SPI_RD_STATUS_OFFSET, 0);
spi_set_reg(priv, SPI_CMD_OFFSET, SPI_FLASH_WREN);
while (spi_get_reg(priv, SPI_CMD_OFFSET) != 0)
{
;
}
do
{
if (esp32_read_status(priv, &regval) != OK)
{
return -EIO;
}
flags = regval & ESP_ROM_SPIFLASH_WRENABLE_FLAG;
}
while (flags != ESP_ROM_SPIFLASH_WRENABLE_FLAG);
return OK;
}
/****************************************************************************
* Name: esp32_erasesector
*
* Description:
* Erase SPI Flash sector at designated address.
*
* Input Parameters:
* spi - ESP32 SPI Flash chip data
* addr - erasing address
*
* Returned Value:
* 0 if success or a negative value if fail.
*
****************************************************************************/
static int IRAM_ATTR esp32_erasesector(struct esp32_spiflash_s *priv,
uint32_t addr, uint32_t size)
{
uint32_t offset;
esp32_set_write_opt(priv);
if (esp32_wait_idle(priv) != OK)
{
return -EIO;
}
for (offset = 0; offset < size; offset += MTD_ERASESIZE(priv))
{
esp32_spiflash_opstart();
if (esp32_enable_write(priv) != OK)
{
esp32_spiflash_opdone();
return -EIO;
}
spi_set_reg(priv, SPI_ADDR_OFFSET, (addr + offset) & 0xffffff);
spi_set_reg(priv, SPI_CMD_OFFSET, SPI_FLASH_SE);
while (spi_get_reg(priv, SPI_CMD_OFFSET) != 0)
{
;
}
if (esp32_wait_idle(priv) != OK)
{
esp32_spiflash_opdone();
return -EIO;
}
esp32_spiflash_opdone();
}
esp32_spiflash_opstart();
spiflash_flushmapped(addr, size);
esp32_spiflash_opdone();
return 0;
}
/****************************************************************************
* Name: esp32_writeonce
*
* Description:
* Write max 32 byte data to SPI Flash at designated address.
*
* ESP32 can write max 32 byte once transmission by hardware.
*
* Input Parameters:
* spi - ESP32 SPI Flash chip data
* addr - target address
* buffer - data buffer pointer
* size - data number by bytes
*
* Returned Value:
* 0 if success or a negative value if fail.
*
****************************************************************************/
static int IRAM_ATTR esp32_writeonce(struct esp32_spiflash_s *priv,
uint32_t addr,
const uint32_t *buffer,
uint32_t size)
{
uint32_t regval;
uint32_t i;
if (size > SPI_FLASH_WRITE_BUF_SIZE)
{
return -EINVAL;
}
if (esp32_wait_idle(priv) != OK)
{
return -EIO;
}
if (esp32_enable_write(priv) != OK)
{
return -EIO;
}
regval = addr & 0xffffff;
regval |= size << ESP_ROM_SPIFLASH_BYTES_LEN;
spi_set_reg(priv, SPI_ADDR_OFFSET, regval);
for (i = 0; i < (size / 4); i++)
{
spi_set_reg(priv, SPI_W0_OFFSET + i * 4, buffer[i]);
}
if (size & 0x3)
{
memcpy(&regval, &buffer[i], size & 0x3);
spi_set_reg(priv, SPI_W0_OFFSET + i * 4, regval);
}
spi_set_reg(priv, SPI_RD_STATUS_OFFSET, 0);
spi_set_reg(priv, SPI_CMD_OFFSET, SPI_FLASH_PP);
while (spi_get_reg(priv, SPI_CMD_OFFSET) != 0)
{
;
}
if (esp32_wait_idle(priv) != OK)
{
return -EIO;
}
return OK;
}
/****************************************************************************
* Name: esp32_writedata
*
* Description:
* Write data to SPI Flash at designated address.
*
* Input Parameters:
* spi - ESP32 SPI Flash chip data
* addr - target address
* buffer - data buffer pointer
* size - data number
*
* Returned Value:
* 0 if success or a negative value if fail.
*
****************************************************************************/
static int IRAM_ATTR esp32_writedata(struct esp32_spiflash_s *priv,
uint32_t addr,
const uint8_t *buffer,
uint32_t size)
{
int ret;
uint32_t off = 0;
uint32_t bytes;
uint32_t tmp_buf[SPI_FLASH_WRITE_WORDS];
esp32_set_write_opt(priv);
while (size > 0)
{
bytes = MTD_BLKSIZE(priv) - addr % MTD_BLKSIZE(priv) ;
if (!bytes)
{
bytes = MIN(size, SPI_FLASH_WRITE_BUF_SIZE);
}
else
{
bytes = MIN(bytes, size);
bytes = MIN(bytes, SPI_FLASH_WRITE_BUF_SIZE);
}
memcpy(tmp_buf, &buffer[off], bytes);
esp32_spiflash_opstart();
ret = esp32_writeonce(priv, addr, tmp_buf, bytes);
esp32_spiflash_opdone();
if (ret)
{
return ret;
}
addr += bytes;
size -= bytes;
off += bytes;
}
esp32_spiflash_opstart();
spiflash_flushmapped(addr, size);
esp32_spiflash_opdone();
return OK;
}
/****************************************************************************
* Name: esp32_writedata_encrypted
*
* Description:
* Write plaintext data to SPI Flash at designated address by SPI Flash
* hardware encryption, and written data in SPI Flash is ciphertext.
*
* Input Parameters:
* spi - ESP32 SPI Flash chip data
* addr - target address
* buffer - data buffer pointer
* size - data number
*
* Returned Value:
* 0 if success or a negative value if fail.
*
****************************************************************************/
static int IRAM_ATTR esp32_writedata_encrypted(
struct esp32_spiflash_s *priv,
uint32_t addr,
const uint8_t *buffer,
uint32_t size)
{
int i;
int blocks;
int ret = OK;
uint32_t tmp_buf[SPI_FLASH_ENCRYPT_WORDS];
if (addr % SPI_FLASH_ENCRYPT_UNIT_SIZE)
{
ferr("ERROR: address=0x%x is not %d-byte align\n",
addr, SPI_FLASH_ENCRYPT_UNIT_SIZE);
return -EINVAL;
}
if (size % SPI_FLASH_ENCRYPT_UNIT_SIZE)
{
ferr("ERROR: size=%u is not %d-byte align\n",
size, SPI_FLASH_ENCRYPT_UNIT_SIZE);
return -EINVAL;
}
blocks = size / SPI_FLASH_ENCRYPT_UNIT_SIZE;
for (i = 0; i < blocks; i++)
{
memcpy(tmp_buf, buffer, SPI_FLASH_ENCRYPT_UNIT_SIZE);
esp32_spiflash_opstart();
esp_rom_spiflash_write_encrypted_enable();
ret = esp_rom_spiflash_prepare_encrypted_data(addr, tmp_buf);
if (ret)
{
ferr("ERROR: Failed to prepare encrypted data\n");
goto exit;
}
ret = esp32_writeonce(priv, addr, tmp_buf,
SPI_FLASH_ENCRYPT_UNIT_SIZE);
if (ret)
{
ferr("ERROR: Failed to write encrypted data @ 0x%x\n", addr);
goto exit;
}
esp_rom_spiflash_write_encrypted_disable();
esp32_spiflash_opdone();
addr += SPI_FLASH_ENCRYPT_UNIT_SIZE;
buffer += SPI_FLASH_ENCRYPT_UNIT_SIZE;
size -= SPI_FLASH_ENCRYPT_UNIT_SIZE;
}
esp32_spiflash_opstart();
spiflash_flushmapped(addr, size);
esp32_spiflash_opdone();
return 0;
exit:
esp_rom_spiflash_write_encrypted_disable();
esp32_spiflash_opdone();
return ret;
}
/****************************************************************************
* Name: esp32_readdata
*
* Description:
* Read max 64 byte data data from SPI Flash at designated address.
*
* ESP32 can read max 64 byte once transmission by hardware.
*
* Input Parameters:
* spi - ESP32 SPI Flash chip data
* addr - target address
* buffer - data buffer pointer
* size - data number by bytes
*
* Returned Value:
* OK if success or a negative value if fail.
*
****************************************************************************/
static int IRAM_ATTR esp32_readonce(struct esp32_spiflash_s *priv,
uint32_t addr,
uint32_t *buffer,
uint32_t size)
{
uint32_t regval;
uint32_t i;
if (size > SPI_FLASH_READ_BUF_SIZE)
{
return -EINVAL;
}
if (esp32_wait_idle(priv) != OK)
{
return -EIO;
}
regval = ((size << 3) - 1) << SPI_USR_MISO_DBITLEN_S;
spi_set_reg(priv, SPI_MISO_DLEN_OFFSET, regval);
regval = addr << 8;
spi_set_reg(priv, SPI_ADDR_OFFSET, regval);
spi_set_reg(priv, SPI_RD_STATUS_OFFSET, 0);
spi_set_reg(priv, SPI_CMD_OFFSET, SPI_USR);
while (spi_get_reg(priv, SPI_CMD_OFFSET) != 0)
{
;
}
for (i = 0; i < (size / 4); i++)
{
buffer[i] = spi_get_reg(priv, SPI_W0_OFFSET + i * 4);
}
if (size & 0x3)
{
regval = spi_get_reg(priv, SPI_W0_OFFSET + i * 4);
memcpy(&buffer[i], &regval, size & 0x3);
}
return OK;
}
/****************************************************************************
* Name: esp32_readdata
*
* Description:
* Read data from SPI Flash at designated address.
*
* Input Parameters:
* spi - ESP32 SPI Flash chip data
* addr - target address
* buffer - data buffer pointer
* size - data number
*
* Returned Value:
* OK if success or a negative value if fail.
*
****************************************************************************/
static int IRAM_ATTR esp32_readdata(struct esp32_spiflash_s *priv,
uint32_t addr,
uint8_t *buffer,
uint32_t size)
{
int ret;
uint32_t off = 0;
uint32_t bytes;
uint32_t tmp_buf[SPI_FLASH_READ_WORDS];
while (size > 0)
{
bytes = MIN(size, SPI_FLASH_READ_BUF_SIZE);
esp32_spiflash_opstart();
ret = esp32_readonce(priv, addr, tmp_buf, bytes);
esp32_spiflash_opdone();
if (ret)
{
return ret;
}
memcpy(&buffer[off], tmp_buf, bytes);
addr += bytes;
size -= bytes;
off += bytes;
}
return OK;
}
/****************************************************************************
* Name: esp32_mmap
*
* Description:
* Mapped SPI Flash address to ESP32's address bus, so that software
* can read SPI Flash data by reading data from memory access.
*
* If SPI Flash hardware encryption is enable, the read from mapped
* address is decrypted.
*
* Input Parameters:
* spi - ESP32 SPI Flash chip data
* req - SPI Flash mapping requesting parameters
*
* Returned Value:
* 0 if success or a negative value if fail.
*
****************************************************************************/
static int IRAM_ATTR esp32_mmap(struct esp32_spiflash_s *priv,
struct spiflash_map_req *req)
{
int ret;
int i;
int start_page;
int flash_page;
int page_cnt;
bool flush = false;
esp32_spiflash_opstart();
for (start_page = DROM0_PAGES_START;
start_page < DROM0_PAGES_END;
++start_page)
{
if (PRO_MMU_TABLE[start_page] == INVALID_MMU_VAL
#ifdef CONFIG_SMP
&& APP_MMU_TABLE[start_page] == INVALID_MMU_VAL
#endif
)
{
break;
}
}
flash_page = MMU_ADDR2PAGE(req->src_addr);
page_cnt = MMU_BYTES2PAGES(MMU_ADDR2OFF(req->src_addr) + req->size);
if (start_page + page_cnt < DROM0_PAGES_END)
{
for (i = 0; i < page_cnt; i++)
{
PRO_MMU_TABLE[start_page + i] = flash_page + i;
#ifdef CONFIG_SMP
APP_MMU_TABLE[start_page + i] = flash_page + i;
#endif
}
req->start_page = start_page;
req->page_cnt = page_cnt;
req->ptr = (void *)(VADDR0_START_ADDR +
start_page * SPI_FLASH_MMU_PAGE_SIZE +
MMU_ADDR2OFF(req->src_addr));
flush = true;
ret = 0;
}
else
{
ret = -ENOBUFS;
}
if (flush)
{
#ifdef CONFIG_ESP32_SPIRAM
esp_spiram_writeback_cache();
#endif
cache_flush(0);
#ifdef CONFIG_SMP
cache_flush(1);
#endif
}
esp32_spiflash_opdone();
return ret;
}
/****************************************************************************
* Name: esp32_ummap
*
* Description:
* Unmap SPI Flash address in ESP32's address bus, and free resource.
*
* Input Parameters:
* spi - ESP32 SPI Flash chip data
* req - SPI Flash mapping requesting parameters
*
* Returned Value:
* None.
*
****************************************************************************/
static void IRAM_ATTR esp32_ummap(struct esp32_spiflash_s *priv,
const struct spiflash_map_req *req)
{
int i;
esp32_spiflash_opstart();
for (i = req->start_page; i < req->start_page + req->page_cnt; ++i)
{
PRO_MMU_TABLE[i] = INVALID_MMU_VAL;
#ifdef CONFIG_SMP
APP_MMU_TABLE[i] = INVALID_MMU_VAL;
#endif
}
#ifdef CONFIG_ESP32_SPIRAM
esp_spiram_writeback_cache();
#endif
cache_flush(0);
#ifdef CONFIG_SMP
cache_flush(1);
#endif
esp32_spiflash_opdone();
}
/****************************************************************************
* Name: esp32_readdata_encrypted
*
* Description:
* Read decrypted data from SPI Flash at designated address when
* enable SPI Flash hardware encryption.
*
* Input Parameters:
* spi - ESP32 SPI Flash chip data
* addr - target address
* buffer - data buffer pointer
* size - data number
*
* Returned Value:
* OK if success or a negative value if fail.
*
****************************************************************************/
static int IRAM_ATTR esp32_readdata_encrypted(
struct esp32_spiflash_s *priv,
uint32_t addr,
uint8_t *buffer,
uint32_t size)
{
int ret;
struct spiflash_map_req req =
{
.src_addr = addr,
.size = size
};
ret = esp32_mmap(priv, &req);
if (ret)
{
return ret;
}
memcpy(buffer, req.ptr, size);
esp32_ummap(priv, &req);
return OK;
}
/****************************************************************************
* Name: esp32_writeblk_encrypted
*
* Description:
* Write plaintext block data to SPI Flash at designated address by SPI
* Flash hardware encryption, and written data in SPI Flash is ciphertext.
*
* Input Parameters:
* priv - ESP32 SPI Flash private data
* offset - target address
* buffer - data buffer pointer
* nbytes - data number
*
* Returned Value:
* 0 if success or a negative value if fail.
*
****************************************************************************/
static int esp32_writeblk_encrypted(struct esp32_spiflash_s *priv,
uint32_t offset,
const uint8_t *buffer,
uint32_t nbytes)
{
uint8_t *wbuf;
uint8_t *rbuf;
off_t addr;
ssize_t n;
uint8_t tmp_buf[SPI_FLASH_ENCRYPT_UNIT_SIZE];
size_t wbytes = 0;
int ret = 0;
while (nbytes > 0)
{
if ((offset % SPI_FLASH_ENCRYPT_UNIT_SIZE) != 0)
{
wbuf = tmp_buf;
rbuf = tmp_buf;
addr = offset - SPI_FLASH_ENCRYPT_MIN_SIZE;
n = SPI_FLASH_ENCRYPT_MIN_SIZE;
ret = esp32_readdata_encrypted(priv, addr, rbuf, n);
if (ret < 0)
{
ferr("esp32_readdata_encrypted failed ret=%d\n", ret);
break;
}
memcpy(wbuf + n, buffer, n);
}
else if ((nbytes % SPI_FLASH_ENCRYPT_UNIT_SIZE) != 0)
{
wbuf = tmp_buf;
if ((offset % SPI_FLASH_ENCRYPT_UNIT_SIZE) != 0)
{
rbuf = tmp_buf;
addr = offset - SPI_FLASH_ENCRYPT_MIN_SIZE;
}
else
{
rbuf = tmp_buf + SPI_FLASH_ENCRYPT_MIN_SIZE;
addr = offset;
}
n = SPI_FLASH_ENCRYPT_MIN_SIZE;
ret = esp32_readdata_encrypted(priv, addr, rbuf, n);
if (ret < 0)
{
ferr("esp32_readdata_encrypted failed ret=%d\n", ret);
break;
}
if ((offset % SPI_FLASH_ENCRYPT_UNIT_SIZE) != 0)
{
memcpy(wbuf + n, buffer, n);
}
else
{
memcpy(wbuf, buffer, n);
}
}
else
{
n = SPI_FLASH_ENCRYPT_UNIT_SIZE;
wbuf = (uint8_t *)buffer;
addr = offset;
}
ret = esp32_writedata_encrypted(priv, addr, wbuf,
SPI_FLASH_ENCRYPT_UNIT_SIZE);
if (ret < 0)
{
ferr("esp32_writedata_encrypted failed ret=%d\n", ret);
break;
}
offset += n;
nbytes -= n;
buffer += n;
wbytes += n;
}
return wbytes;
}
/****************************************************************************
* Name: esp32_spiflash_work
*
* Description:
* Do SPI Flash operation, cache result and send semaphore to wake up
* blocked task.
*
* Input Parameters:
* p - SPI Flash work arguments
*
****************************************************************************/
#ifdef CONFIG_ESP32_SPI_FLASH_SUPPORT_PSRAM_STACK
static void esp32_spiflash_work(void *p)
{
struct spiflash_work_arg *work_arg = (struct spiflash_work_arg *)p;
if (work_arg->op_code == SPIFLASH_OP_CODE_WRITE)
{
work_arg->ret = esp32_writedata(work_arg->op_arg.priv,
work_arg->op_arg.addr,
work_arg->op_arg.buffer,
work_arg->op_arg.size);
}
else if (work_arg->op_code == SPIFLASH_OP_CODE_READ)
{
esp32_set_read_opt(work_arg->op_arg.priv);
work_arg->ret = esp32_readdata(work_arg->op_arg.priv,
work_arg->op_arg.addr,
work_arg->op_arg.buffer,
work_arg->op_arg.size);
}
else if (work_arg->op_code == SPIFLASH_OP_CODE_ERASE)
{
work_arg->ret = esp32_erasesector(work_arg->op_arg.priv,
work_arg->op_arg.addr,
work_arg->op_arg.size);
}
#ifdef CONFIG_ESP32_SPIRAM
else if (work_arg->op_code == SPIFLASH_OP_CODE_SET_BANK)
{
work_arg->ret = esp32_set_mmu_map(work_arg->op_arg.addr,
work_arg->op_arg.paddr,
work_arg->op_arg.size);
}
#endif
else if (work_arg->op_code == SPIFLASH_OP_CODE_ENCRYPT_READ)
{
esp32_set_read_opt(work_arg->op_arg.priv);
work_arg->ret = esp32_readdata_encrypted(work_arg->op_arg.priv,
work_arg->op_arg.addr,
work_arg->op_arg.buffer,
work_arg->op_arg.size);
}
else if (work_arg->op_code == SPIFLASH_OP_CODE_ENCRYPT_WRITE)
{
work_arg->ret = esp32_writeblk_encrypted(work_arg->op_arg.priv,
work_arg->op_arg.addr,
work_arg->op_arg.buffer,
work_arg->op_arg.size);
}
else
{
ferr("ERROR: op_code=%d is not supported\n", work_arg->op_code);
}
nxsem_post(&work_arg->sem);
}
/****************************************************************************
* Name: esp32_async_op
*
* Description:
* Send operation code and arguments to workqueue so that workqueue do SPI
* Flash operation actually.
*
* Input Parameters:
* p - SPI Flash work arguments
*
* Returned Value:
* 0 if success or a negative value if fail.
*
****************************************************************************/
static int esp32_async_op(enum spiflash_op_code_e opcode,
struct esp32_spiflash_s *priv,
uint32_t addr,
const uint8_t *buffer,
uint32_t size,
uint32_t paddr)
{
int ret;
struct spiflash_work_arg work_arg =
{
.op_code = opcode,
.op_arg =
{
.priv = priv,
.addr = addr,
.buffer = (uint8_t *)buffer,
.size = size,
.paddr = paddr,
},
.sem = NXSEM_INITIALIZER(0, 0)
};
ret = work_queue(LPWORK, &g_work, esp32_spiflash_work, &work_arg, 0);
if (ret == 0)
{
nxsem_wait(&work_arg.sem);
ret = work_arg.ret;
}
return ret;
}
#endif
/****************************************************************************
* Name: esp32_erase
*
* Description:
* Erase SPI Flash designated sectors.
*
* Input Parameters:
* dev - ESP32 MTD device data
* startblock - start block number, it is not equal to SPI Flash's block
* nblocks - blocks number
*
* Returned Value:
* 0 if success or a negative value if fail.
*
****************************************************************************/
static int esp32_erase(struct mtd_dev_s *dev, off_t startblock,
size_t nblocks)
{
int ret;
struct esp32_spiflash_s *priv = MTD2PRIV(dev);
uint32_t addr = startblock * MTD_ERASESIZE(priv);
uint32_t size = nblocks * MTD_ERASESIZE(priv);
if ((addr >= MTD_SIZE(priv)) || (addr + size > MTD_SIZE(priv)))
{
return -EINVAL;
}
#ifdef CONFIG_ESP32_SPIFLASH_DEBUG
finfo("esp32_erase(%p, %d, %d)\n", dev, startblock, nblocks);
#endif
ret = nxmutex_lock(&g_lock);
if (ret < 0)
{
return ret;
}
#ifdef CONFIG_ESP32_SPI_FLASH_SUPPORT_PSRAM_STACK
if (stack_is_psram())
{
ret = esp32_async_op(SPIFLASH_OP_CODE_ERASE, priv, addr, NULL,
size, 0);
}
else
{
ret = esp32_erasesector(priv, addr, size);
}
#else
ret = esp32_erasesector(priv, addr, size);
#endif
nxmutex_unlock(&g_lock);
if (ret == OK)
{
ret = nblocks;
}
#ifdef CONFIG_ESP32_SPIFLASH_DEBUG
finfo("esp32_erase()=%d\n", ret);
#endif
return ret;
}
/****************************************************************************
* Name: esp32_read
*
* Description:
* Read data from SPI Flash at designated address.
*
* Input Parameters:
* dev - ESP32 MTD device data
* offset - target address offset
* nbytes - data number
* buffer - data buffer pointer
*
* Returned Value:
* Read data bytes if success or a negative value if fail.
*
****************************************************************************/
static ssize_t esp32_read(struct mtd_dev_s *dev, off_t offset,
size_t nbytes, uint8_t *buffer)
{
ssize_t ret;
struct esp32_spiflash_s *priv = MTD2PRIV(dev);
#ifdef CONFIG_ESP32_SPIFLASH_DEBUG
finfo("esp32_read(%p, 0x%x, %d, %p)\n", dev, offset, nbytes, buffer);
#endif
/* Acquire the mutex. */
ret = nxmutex_lock(&g_lock);
if (ret < 0)
{
return ret;
}
#ifdef CONFIG_ESP32_SPI_FLASH_SUPPORT_PSRAM_STACK
if (stack_is_psram())
{
ret = esp32_async_op(SPIFLASH_OP_CODE_READ, priv,
offset, buffer, nbytes, 0);
}
else
{
esp32_set_read_opt(priv);
ret = esp32_readdata(priv, offset, buffer, nbytes);
}
#else
esp32_set_read_opt(priv);
ret = esp32_readdata(priv, offset, buffer, nbytes);
#endif
nxmutex_unlock(&g_lock);
if (ret == OK)
{
ret = nbytes;
}
#ifdef CONFIG_ESP32_SPIFLASH_DEBUG
finfo("esp32_read()=%d\n", ret);
#endif
return ret;
}
/****************************************************************************
* Name: esp32_bread
*
* Description:
* Read data from designated blocks.
*
* Input Parameters:
* dev - ESP32 MTD device data
* startblock - start block number, it is not equal to SPI Flash's block
* nblocks - blocks number
* buffer - data buffer pointer
*
* Returned Value:
* Read block number if success or a negative value if fail.
*
****************************************************************************/
static ssize_t esp32_bread(struct mtd_dev_s *dev, off_t startblock,
size_t nblocks, uint8_t *buffer)
{
int ret;
struct esp32_spiflash_s *priv = MTD2PRIV(dev);
uint32_t addr = MTD_BLK2SIZE(priv, startblock);
uint32_t size = MTD_BLK2SIZE(priv, nblocks);
#ifdef CONFIG_ESP32_SPIFLASH_DEBUG
finfo("esp32_bread(%p, 0x%x, %d, %p)\n",
dev, startblock, nblocks, buffer);
#endif
ret = esp32_read(dev, addr, size, buffer);
if (ret == size)
{
ret = nblocks;
}
#ifdef CONFIG_ESP32_SPIFLASH_DEBUG
finfo("esp32_bread()=%d\n", ret);
#endif
return ret;
}
/****************************************************************************
* Name: esp32_read_decrypt
*
* Description:
* Read encrypted data and decrypt automatically from SPI Flash
* at designated address.
*
* Input Parameters:
* dev - ESP32 MTD device data
* offset - target address offset
* nbytes - data number
* buffer - data buffer pointer
*
* Returned Value:
* Read data bytes if success or a negative value if fail.
*
****************************************************************************/
static ssize_t esp32_read_decrypt(struct mtd_dev_s *dev,
off_t offset,
size_t nbytes,
uint8_t *buffer)
{
ssize_t ret;
uint8_t *tmpbuff = buffer;
struct esp32_spiflash_s *priv = MTD2PRIV(dev);
#ifdef CONFIG_ESP32_SPIFLASH_DEBUG
finfo("esp32_read_decrypt(%p, 0x%x, %d, %p)\n",
dev, offset, nbytes, buffer);
#endif
/* Acquire the mutex. */
ret = nxmutex_lock(&g_lock);
if (ret < 0)
{
return ret;
}
#ifdef CONFIG_ESP32_SPI_FLASH_SUPPORT_PSRAM_STACK
if (stack_is_psram())
{
ret = esp32_async_op(SPIFLASH_OP_CODE_ENCRYPT_READ, priv,
offset, buffer, nbytes, 0);
}
else
{
ret = esp32_readdata_encrypted(priv, offset, tmpbuff, nbytes);
}
#else
ret = esp32_readdata_encrypted(priv, offset, tmpbuff, nbytes);
#endif
nxmutex_unlock(&g_lock);
if (ret == OK)
{
ret = nbytes;
}
#ifdef CONFIG_ESP32_SPIFLASH_DEBUG
finfo("esp32_read_decrypt()=%d\n", ret);
#endif
return ret;
}
/****************************************************************************
* Name: esp32_bread_decrypt
*
* Description:
* Read encrypted data and decrypt automatically from designated blocks.
*
* Input Parameters:
* dev - ESP32 MTD device data
* startblock - start block number, it is not equal to SPI Flash's block
* nblocks - blocks number
* buffer - data buffer pointer
*
* Returned Value:
* Read block number if success or a negative value if fail.
*
****************************************************************************/
static ssize_t esp32_bread_decrypt(struct mtd_dev_s *dev,
off_t startblock,
size_t nblocks,
uint8_t *buffer)
{
int ret;
struct esp32_spiflash_s *priv = MTD2PRIV(dev);
uint32_t addr = MTD_BLK2SIZE(priv, startblock);
uint32_t size = MTD_BLK2SIZE(priv, nblocks);
#ifdef CONFIG_ESP32_SPIFLASH_DEBUG
finfo("esp32_bread_decrypt(%p, 0x%x, %d, %p)\n",
dev, startblock, nblocks, buffer);
#endif
ret = esp32_read_decrypt(dev, addr, size, buffer);
if (ret == size)
{
ret = nblocks;
}
#ifdef CONFIG_ESP32_SPIFLASH_DEBUG
finfo("esp32_bread_decrypt()=%d\n", ret);
#endif
return ret;
}
/****************************************************************************
* Name: esp32_write
*
* Description:
* write data to SPI Flash at designated address.
*
* Input Parameters:
* dev - ESP32 MTD device data
* offset - target address offset
* nbytes - data number
* buffer - data buffer pointer
*
* Returned Value:
* Written bytes if success or a negative value if fail.
*
****************************************************************************/
#ifdef CONFIG_MTD_BYTE_WRITE
static ssize_t esp32_write(struct mtd_dev_s *dev, off_t offset,
size_t nbytes, const uint8_t *buffer)
{
ssize_t ret;
struct esp32_spiflash_s *priv = MTD2PRIV(dev);
ASSERT(buffer);
if ((offset > MTD_SIZE(priv)) || ((offset + nbytes) > MTD_SIZE(priv)))
{
return -EINVAL;
}
#ifdef CONFIG_ESP32_SPIFLASH_DEBUG
finfo("esp32_write(%p, 0x%x, %d, %p)\n", dev, offset, nbytes, buffer);
#endif
/* Acquire the mutex. */
ret = nxmutex_lock(&g_lock);
if (ret < 0)
{
return ret;
}
#ifdef CONFIG_ESP32_SPI_FLASH_SUPPORT_PSRAM_STACK
if (stack_is_psram())
{
ret = esp32_async_op(SPIFLASH_OP_CODE_WRITE, priv,
offset, buffer, nbytes, 0);
}
else
{
ret = esp32_writedata(priv, offset, buffer, nbytes);
}
#else
ret = esp32_writedata(priv, offset, buffer, nbytes);
#endif
nxmutex_unlock(&g_lock);
if (ret == OK)
{
ret = nbytes;
}
#ifdef CONFIG_ESP32_SPIFLASH_DEBUG
finfo("esp32_write()=%d\n", ret);
#endif
return ret;
}
#endif
/****************************************************************************
* Name: esp32_bwrite
*
* Description:
* Write data to designated blocks.
*
* Input Parameters:
* dev - ESP32 MTD device data
* startblock - start MTD block number,
* it is not equal to SPI Flash's block
* nblocks - blocks number
* buffer - data buffer pointer
*
* Returned Value:
* Written block number if success or a negative value if fail.
*
****************************************************************************/
static ssize_t esp32_bwrite(struct mtd_dev_s *dev, off_t startblock,
size_t nblocks, const uint8_t *buffer)
{
ssize_t ret;
struct esp32_spiflash_s *priv = MTD2PRIV(dev);
uint32_t addr = MTD_BLK2SIZE(priv, startblock);
uint32_t size = MTD_BLK2SIZE(priv, nblocks);
#ifdef CONFIG_ESP32_SPIFLASH_DEBUG
finfo("esp32_bwrite(%p, 0x%x, %d, %p)\n",
dev, startblock, nblocks, buffer);
#endif
ret = esp32_write(dev, addr, size, buffer);
if (ret == size)
{
ret = nblocks;
}
#ifdef CONFIG_ESP32_SPIFLASH_DEBUG
finfo("esp32_bwrite()=%d\n", ret);
#endif
return ret;
}
/****************************************************************************
* Name: esp32_bwrite_encrypt
*
* Description:
* Write data to designated blocks by SPI Flash hardware encryption.
*
* Input Parameters:
* dev - ESP32 MTD device data
* startblock - start MTD block number,
* it is not equal to SPI Flash's block
* nblocks - blocks number
* buffer - data buffer pointer
*
* Returned Value:
* Written block number if success or a negative value if fail.
*
****************************************************************************/
static ssize_t esp32_bwrite_encrypt(struct mtd_dev_s *dev,
off_t startblock,
size_t nblocks,
const uint8_t *buffer)
{
ssize_t ret;
struct esp32_spiflash_s *priv = MTD2PRIV(dev);
uint32_t offset = MTD_BLK2SIZE(priv, startblock);
uint32_t nbytes = MTD_BLK2SIZE(priv, nblocks);
if ((offset % SPI_FLASH_ENCRYPT_MIN_SIZE) ||
(nbytes % SPI_FLASH_ENCRYPT_MIN_SIZE))
{
return -EINVAL;
}
#ifdef CONFIG_ESP32_SPIFLASH_DEBUG
finfo("esp32_bwrite_encrypt(%p, 0x%x, %d, %p)\n",
dev, startblock, nblocks, buffer);
#endif
ret = nxmutex_lock(&g_lock);
if (ret < 0)
{
return ret;
}
#ifdef CONFIG_ESP32_SPI_FLASH_SUPPORT_PSRAM_STACK
if (stack_is_psram())
{
ret = esp32_async_op(SPIFLASH_OP_CODE_ENCRYPT_WRITE, priv,
offset, buffer, nbytes, 0);
}
else
{
ret = esp32_writeblk_encrypted(priv, offset, buffer, nbytes);
}
#else
ret = esp32_writeblk_encrypted(priv, offset, buffer, nbytes);
#endif
if (ret == nbytes)
{
ret = nblocks;
}
nxmutex_unlock(&g_lock);
if (ret == OK)
{
ret = nblocks;
}
#ifdef CONFIG_ESP32_SPIFLASH_DEBUG
finfo("esp32_bwrite_encrypt()=%d\n", ret);
#endif
return ret;
}
/****************************************************************************
* Name: esp32_ioctl
*
* Description:
* Set/Get option to/from ESP32 SPI Flash MTD device data.
*
* Input Parameters:
* dev - ESP32 MTD device data
* cmd - operation command
* arg - operation argument
*
* Returned Value:
* 0 if success or a negative value if fail.
*
****************************************************************************/
static int esp32_ioctl(struct mtd_dev_s *dev, int cmd,
unsigned long arg)
{
int ret = -EINVAL;
struct esp32_spiflash_s *priv = MTD2PRIV(dev);
finfo("cmd: %d\n", cmd);
switch (cmd)
{
case MTDIOC_GEOMETRY:
{
struct mtd_geometry_s *geo = (struct mtd_geometry_s *)arg;
if (geo)
{
memset(geo, 0, sizeof(*geo));
geo->blocksize = MTD_BLKSIZE(priv);
geo->erasesize = MTD_ERASESIZE(priv);
geo->neraseblocks = MTD_SIZE(priv) / MTD_ERASESIZE(priv);
ret = OK;
finfo("blocksize: %d erasesize: %d neraseblocks: %d\n",
geo->blocksize, geo->erasesize, geo->neraseblocks);
}
}
break;
case BIOC_PARTINFO:
{
struct partition_info_s *info =
(struct partition_info_s *)arg;
if (info != NULL)
{
info->numsectors = MTD_SIZE(priv) / MTD_BLKSIZE(priv);
info->sectorsize = MTD_BLKSIZE(priv);
info->startsector = 0;
info->parent[0] = '\0';
ret = OK;
}
}
break;
case MTDIOC_ERASESTATE:
{
uint8_t *result = (uint8_t *)arg;
*result = SPI_FLASH_ERASED_STATE;
ret = OK;
}
break;
default:
ret = -ENOTTY;
break;
}
finfo("return %d\n", ret);
return ret;
}
/****************************************************************************
* Name: esp32_ioctl_encrypt
*
* Description:
* Set/Get option to/from ESP32 SPI Flash Hardware Encryption MTD
* device data.
*
* Input Parameters:
* dev - ESP32 MTD device data
* cmd - operation command
* arg - operation argument
*
* Returned Value:
* 0 if success or a negative value if fail.
*
****************************************************************************/
static int esp32_ioctl_encrypt(struct mtd_dev_s *dev, int cmd,
unsigned long arg)
{
int ret = -EINVAL;
struct esp32_spiflash_s *priv = MTD2PRIV(dev);
finfo("cmd: %d\n", cmd);
switch (cmd)
{
case MTDIOC_GEOMETRY:
{
struct mtd_geometry_s *geo = (struct mtd_geometry_s *)arg;
if (geo)
{
memset(geo, 0, sizeof(*geo));
geo->blocksize = SPI_FLASH_ENCRYPT_MIN_SIZE;
geo->erasesize = MTD_ERASESIZE(priv);
geo->neraseblocks = MTD_SIZE(priv) / geo->erasesize;
ret = OK;
finfo("blocksize: %d erasesize: %d neraseblocks: %d\n",
geo->blocksize, geo->erasesize, geo->neraseblocks);
}
}
break;
case BIOC_PARTINFO:
{
struct partition_info_s *info =
(struct partition_info_s *)arg;
if (info != NULL)
{
info->sectorsize = SPI_FLASH_ENCRYPT_MIN_SIZE;
info->numsectors = MTD_SIZE(priv) / info->sectorsize;
info->startsector = 0;
info->parent[0] = '\0';
ret = OK;
}
}
break;
case MTDIOC_ERASESTATE:
{
uint8_t *result = (uint8_t *)arg;
*result = SPI_FLASH_ERASED_STATE;
ret = OK;
}
break;
default:
ret = -ENOTTY;
break;
}
finfo("return %d\n", ret);
return ret;
}
#ifdef CONFIG_SMP
/****************************************************************************
* Name: spi_flash_op_block_task
*
* Description:
* Disable the non-IRAM interrupts on the other core (the one that isn't
* handling the SPI flash operation) and notify that the SPI flash
* operation can start. Wait on a busy loop until it's finished and then
* reenable the non-IRAM interrups.
*
* Input Parameters:
* argc - Not used.
* argv - Not used.
*
* Returned Value:
* Zero (OK) is returned on success. A negated errno value is returned to
* indicate the nature of any failure.
*
****************************************************************************/
static int spi_flash_op_block_task(int argc, char *argv[])
{
struct tcb_s *tcb = this_task();
int cpu = up_cpu_index();
for (; ; )
{
DEBUGASSERT((1 << cpu) & tcb->affinity);
/* Wait for a SPI flash operation to take place and this (the other
* core) being asked to disable its non-IRAM interrupts.
*/
nxsem_wait(&g_disable_non_iram_isr_on_core[cpu]);
sched_lock();
esp32_irq_noniram_disable();
/* g_flash_op_complete flag is cleared on *this* CPU, otherwise the
* other CPU may reset the flag back to false before this task has a
* chance to check it (if it's preempted by an ISR taking non-trivial
* amount of time).
*/
g_flash_op_complete = false;
g_flash_op_can_start = true;
while (!g_flash_op_complete)
{
/* Busy loop here and wait for the other CPU to finish the SPI
* flash operation.
*/
}
/* Flash operation is complete, re-enable cache */
spi_enable_cache(cpu);
/* Restore interrupts that aren't located in IRAM */
esp32_irq_noniram_enable();
sched_unlock();
}
return OK;
}
/****************************************************************************
* Name: spiflash_init_spi_flash_op_block_task
*
* Description:
* Starts a kernel thread that waits for a semaphore indicating that a SPI
* flash operation is going to take place in the other CPU. It disables
* non-IRAM interrupts, indicates to the other core that the SPI flash
* operation can start and waits for it to be finished in a busy loop.
*
* Input Parameters:
* cpu - The CPU core that will run the created task to wait on a busy
* loop while the SPI flash operation finishes
*
* Returned Value:
* 0 (OK) on success; A negated errno value on failure.
*
****************************************************************************/
int spiflash_init_spi_flash_op_block_task(int cpu)
{
int pid;
int ret = OK;
char *argv[2];
char arg1[32];
cpu_set_t cpuset;
snprintf(arg1, sizeof(arg1), "%p", &cpu);
argv[0] = arg1;
argv[1] = NULL;
pid = kthread_create("spiflash_op",
SCHED_PRIORITY_MAX,
CONFIG_ESP32_SPIFLASH_OP_TASK_STACKSIZE,
spi_flash_op_block_task,
argv);
if (pid > 0)
{
if (cpu < CONFIG_SMP_NCPUS)
{
CPU_ZERO(&cpuset);
CPU_SET(cpu, &cpuset);
ret = nxsched_set_affinity(pid, sizeof(cpuset), &cpuset);
if (ret < 0)
{
return ret;
}
}
}
else
{
return -EPERM;
}
return ret;
}
#endif /* CONFIG_SMP */
#ifdef CONFIG_ESP32_SPIRAM
/****************************************************************************
* Name: esp32_set_mmu_map
*
* Description:
* Set Ext-SRAM-Cache mmu mapping.
*
* Input Parameters:
* vaddr - Virtual address in CPU address space
* paddr - Physical address in Ext-SRAM
* num - Pages to be set
*
* Returned Value:
* 0 if success or a negative value if fail.
*
****************************************************************************/
static int esp32_set_mmu_map(int vaddr, int paddr, int num)
{
int ret;
ret = cache_sram_mmu_set(0, 0, vaddr, paddr, 32, num);
DEBUGASSERT(ret == 0);
ret = cache_sram_mmu_set(1, 0, vaddr, paddr, 32, num);
return ret;
}
#endif
/****************************************************************************
* Public Functions
****************************************************************************/
#ifdef CONFIG_ESP32_SPIRAM
/****************************************************************************
* Name: esp32_set_bank
*
* Description:
* Set Ext-SRAM-Cache mmu mapping.
*
* Input Parameters:
* virt_bank - Beginning of the virtual bank
* phys_bank - Beginning of the physical bank
* ct - Number of banks
*
* Returned Value:
* None.
*
****************************************************************************/
void esp32_set_bank(int virt_bank, int phys_bank, int ct)
{
int ret;
uint32_t vaddr = SOC_EXTRAM_DATA_LOW + CACHE_BLOCKSIZE * virt_bank;
uint32_t paddr = phys_bank * CACHE_BLOCKSIZE;
#ifdef CONFIG_ESP32_SPI_FLASH_SUPPORT_PSRAM_STACK
if (stack_is_psram())
{
ret = esp32_async_op(SPIFLASH_OP_CODE_SET_BANK, NULL, vaddr, NULL,
ct, paddr);
}
else
#endif
{
ret = esp32_set_mmu_map(vaddr, paddr, ct);
}
DEBUGASSERT(ret == 0);
UNUSED(ret);
}
#endif
/****************************************************************************
* Name: esp32_spiflash_init
*
* Description:
* Initialize ESP32 SPI flash driver.
*
* Returned Value:
* OK if success or a negative value if fail.
*
****************************************************************************/
int esp32_spiflash_init(void)
{
int cpu;
int ret = OK;
#ifdef CONFIG_SMP
sched_lock();
for (cpu = 0; cpu < CONFIG_SMP_NCPUS; cpu++)
{
nxsem_init(&g_disable_non_iram_isr_on_core[cpu], 0, 0);
ret = spiflash_init_spi_flash_op_block_task(cpu);
if (ret != OK)
{
return ret;
}
}
sched_unlock();
#else
UNUSED(cpu);
#endif
return ret;
}
/****************************************************************************
* Name: esp32_spiflash_alloc_mtdpart
*
* Description:
* Allocate an MTD partition from the ESP32 SPI Flash.
*
* Input Parameters:
* mtd_offset - MTD Partition offset from the base address in SPI Flash.
* mtd_size - Size for the MTD partition.
* encrypted - Flag indicating whether the newly allocated partition will
* have its content encrypted.
*
* Returned Value:
* ESP32 SPI Flash MTD data pointer if success or NULL if fail.
*
****************************************************************************/
struct mtd_dev_s *esp32_spiflash_alloc_mtdpart(uint32_t mtd_offset,
uint32_t mtd_size,
bool encrypted)
{
struct esp32_spiflash_s *priv;
esp32_spiflash_chip_t *chip;
struct mtd_dev_s *mtd_part;
uint32_t blocks;
uint32_t startblock;
uint32_t size;
if (encrypted)
{
priv = &g_esp32_spiflash1_encrypt;
}
else
{
priv = &g_esp32_spiflash1;
}
chip = priv->chip;
finfo("ESP32 SPI Flash information:\n");
finfo("\tID = 0x%x\n", chip->device_id);
finfo("\tStatus mask = %x\n", chip->status_mask);
finfo("\tChip size = %d KB\n", chip->chip_size / 1024);
finfo("\tPage size = %d B\n", chip->page_size);
finfo("\tSector size = %d KB\n", chip->sector_size / 1024);
finfo("\tBlock size = %d KB\n", chip->block_size / 1024);
ASSERT((mtd_offset + mtd_size) <= chip->chip_size);
ASSERT((mtd_offset % chip->sector_size) == 0);
ASSERT((mtd_size % chip->sector_size) == 0);
if (mtd_size == 0)
{
size = chip->chip_size - mtd_offset;
}
else
{
size = mtd_size;
}
finfo("\tMTD offset = 0x%x\n", mtd_offset);
finfo("\tMTD size = 0x%x\n", size);
startblock = MTD_SIZE2BLK(priv, mtd_offset);
blocks = MTD_SIZE2BLK(priv, size);
mtd_part = mtd_partition(&priv->mtd, startblock, blocks);
if (!mtd_part)
{
ferr("ERROR: create MTD partition");
return NULL;
}
return mtd_part;
}
/****************************************************************************
* Name: esp32_spiflash_get_mtd
*
* Description:
* Get ESP32 SPI Flash raw MTD.
*
* Input Parameters:
* None
*
* Returned Value:
* ESP32 SPI Flash raw MTD data pointer.
*
****************************************************************************/
struct mtd_dev_s *esp32_spiflash_get_mtd(void)
{
struct esp32_spiflash_s *priv = &g_esp32_spiflash1;
return &priv->mtd;
}
/****************************************************************************
* Name: esp32_spiflash_get_mtd
*
* Description:
* Get ESP32 SPI Flash encryption raw MTD.
*
* Input Parameters:
* None
*
* Returned Value:
* ESP32 SPI Flash encryption raw MTD data pointer.
*
****************************************************************************/
struct mtd_dev_s *esp32_spiflash_encrypt_get_mtd(void)
{
struct esp32_spiflash_s *priv = &g_esp32_spiflash1_encrypt;
return &priv->mtd;
}
/****************************************************************************
* Name: esp32_flash_encryption_enabled
*
* Description:
* Check if ESP32 enables SPI Flash encryption.
*
* Input Parameters:
* None
*
* Returned Value:
* True: SPI Flash encryption is enable, False if not.
*
****************************************************************************/
bool esp32_flash_encryption_enabled(void)
{
bool enabled = false;
uint32_t regval;
uint32_t flash_crypt_cnt;
regval = getreg32(EFUSE_BLK0_RDATA0_REG);
flash_crypt_cnt = (regval >> EFUSE_RD_FLASH_CRYPT_CNT_S) &
EFUSE_RD_FLASH_CRYPT_CNT_V;
while (flash_crypt_cnt)
{
if (flash_crypt_cnt & 1)
{
enabled = !enabled;
}
flash_crypt_cnt >>= 1;
}
return enabled;
}
#endif /* CONFIG_ESP32_SPIFLASH */